Proces inicjalizacji ma prawie nieograniczone uprawnienia i używa skryptów wejściowych z obu partycji (systemowej i dostawcy), aby zainicjować system podczas procesu uruchamiania. Ten dostęp powoduje ogromną lukę w podziale systemu Treble/producent, ponieważ skrypty producenta mogą zlecać initowi dostęp do plików, właściwości itp., które nie są częścią stabilnego interfejsu binarnego aplikacji systemowej producenta (ABI).
Inicjacja dostawcy ma na celu zamykanie tej luki za pomocą osobnej domeny vendor_init
z systemem SELinux (zaawansowane zabezpieczenia systemu Linux) do wykonywania poleceń znajdujących się w /vendor
z uprawnieniami dostawcy.
Mechanizm
Dostawca inicjuje podproces inicjowania na wczesnym etapie rozruchu za pomocą kontekstu SELinux u:r:vendor_init:s0
. Ten kontekst SELinux ma znacznie mniej uprawnień niż domyślny kontekst init, a jego dostęp jest ograniczony do plików, właściwości itp., które są specyficzne dla dostawcy lub są częścią stabilnego ABI dostawcy systemu.
Funkcja init sprawdza każdy wczytywany skrypt, aby sprawdzić, czy jego ścieżka zaczyna się od /vendor
. Jeśli tak, oznacza go tagiem wskazującym, że jego polecenia muszą być wykonywane w kontekście inicjalizacji dostawcy. Każda wbudowana funkcja init jest opatrzona adnotacją typu boolean, która określa, czy polecenie musi być wykonane w podprocesie init dostawcy:
- Większość poleceń, które uzyskują dostęp do systemu plików, jest oznaczona jako polecenia do wykonania w podprocesie init dostawcy, dlatego podlegają polityce SEPolicy dostawcy.
- Większość poleceń, które wpływają na stan wewnętrznego procesu inicjowania (np. uruchamianie i zatrzymywanie usług), jest wykonywana w ramach normalnego procesu inicjowania. Te polecenia są wykonywane, gdy skrypt dostawcy wywołuje je w celu obsługi uprawnień niebędących SELinux.
Główna pętla przetwarzania init zawiera sprawdzanie, czy polecenie jest oznaczone do uruchomienia w podprocesie dostawcy i pochodzi ze skryptu dostawcy. Jeśli tak, jest ono wysyłane za pomocą komunikacji między procesami (IPC) do podprocesu init dostawcy, który wykonuje polecenie i przesyła wynik z powrotem do init.
Używanie funkcji vendor init
Inicjowanie dostawcy jest domyślnie włączone, a jego ograniczenia mają zastosowanie do wszystkich skryptów inicjujących znajdujących się na partycji /vendor
. Inicjacja dostawcy powinna być przejrzysta dla dostawców, których skrypty nie mają dostępu do plików, właściwości itp. tylko dla systemu.
Jeśli jednak polecenia w skrypcie dostawcy naruszają ograniczenia inicjowania dostawcy, nie powiodą się. Nieudane polecenia mają w logu jądra (widocznym za pomocą polecenia dmesg) wiersz z init, który wskazuje na błąd. Kontrola SELinux towarzyszy każdemu poleceniu, które nie zostało wykonane z powodu zasad SELinux. Przykład błędu obejmującego kontrolę SELinux:
type=1400 audit(1511821362.996:9): avc: denied { search } for pid=540 comm="init" name="nfc" dev="sda45" ino=1310721 scontext=u:r:vendor_init:s0 tcontext=u:object_r:nfc_data_file:s0 tclass=dir permissive=0 init: Command 'write /data/nfc/bad_file_access 1234' action=boot (/vendor/etc/init/hw/init.walleye.rc:422) took 2ms and failed: Unable to write to file '/data/nfc/bad_file_access': open() failed: Permission denied
Jeśli polecenie się nie powiedzie, masz 2 opcje:
- Jeśli polecenie nie działa z powodu zamierzonego ograniczenia (np. gdy polecenie uzyskuje dostęp do pliku lub właściwości systemowej), należy je ponownie zaimplementować w sposób zgodny z Treble, korzystając tylko ze stabilnych interfejsów. Reguły typu „Nigdy nie zezwalaj” uniemożliwiają dodawanie uprawnień dostępu do plików systemowych, które nie są częścią stabilnego interfejsu ABI dostawcy systemu.
- Jeśli etykieta SELinux jest nowa i nie ma jeszcze przyznanych uprawnień w systemie
vendor_init.te
ani nie jest wykluczona w regułach neverallow, nowa etykieta może otrzymać uprawnienia wvendor_init.te
.
W przypadku urządzeń uruchamianych przed Androidem 9 reguły neverallows można pominąć, dodając atrybut data_between_core_and_vendor_violators
type do pliku vendor_init.te
konkretnego urządzenia.
Lokalizacje kodu
Większość logiki dla IPC inicjalizacji dostawcy znajduje się w pliku system/core/init/subcontext.cpp.
Tabela poleceń znajduje się w klasie BuiltinFunctionMap
w pliku system/core/init/builtins.cpp i zawiera adnotacje wskazujące, czy polecenie musi zostać uruchomione w podprocesie inicjującym dostawcę.
SEPolicy dla vendor_init jest podzielony na katalogi prywatne (system/sepolicy/private/vendor_init.te) i publiczne (system/sepolicy/public/vendor_init.te) w katalogu system/sepolicy.