Weryfikacja SELinux

Android zdecydowanie zachęca producentów OEM do dokładnego testowania implementacji SELinux. Producenci wdrażający SELinux powinni najpierw zastosować nowe zasady na puli urządzeń testowych.

Po zastosowaniu nowych zasad upewnij się, że SELinux działa na urządzeniu w odpowiednim trybie, wydając polecenie getenforce.

Wyświetla globalny tryb SELinux: Enforcing lub Permissive. Aby określić tryb SELinux dla każdej domeny, musisz sprawdzić odpowiednie pliki lub uruchomić najnowszą wersję narzędzia sepolicy-analyze z odpowiednią flagą (-p), która znajduje się w  /platform/system/sepolicy/tools/.

Odczytywanie odmów

Sprawdź błędy, które są kierowane jako logi zdarzeń do dmesglogcat i można je wyświetlać lokalnie na urządzeniu. Producenci powinni sprawdzić dane wyjściowe SELinux na tych urządzeniach i dopracować ustawienia przed udostępnieniem ich publicznie w trybie zezwalającym, a następnie przełączyć się na tryb egzekwowania.dmesg Wiadomości dziennika SELinux zawierają avc:, więc można je łatwo znaleźć za pomocą grep. Możesz rejestrować bieżące logi odmowy, uruchamiając polecenie cat /proc/kmsg, lub rejestrować logi odmowy z poprzedniego uruchomienia, uruchamiając polecenie cat /sys/fs/pstore/console-ramoops.

Komunikaty o błędach SELinux są ograniczane po zakończeniu rozruchu, aby uniknąć przepełnienia dzienników. Aby mieć pewność, że widzisz wszystkie istotne wiadomości, możesz wyłączyć tę funkcję, uruchamiając adb shell auditctl -r 0.

Dzięki temu producenci mogą łatwo określić, kiedy użytkownicy systemu lub komponenty naruszają zasady SELinux. Producenci mogą wtedy naprawić to nieprawidłowe działanie, wprowadzając zmiany w oprogramowaniu, zasadach SELinux lub w obu tych elementach.

Te komunikaty w dzienniku wskazują, które procesy nie powiodłyby się w trybie egzekwowania i dlaczego. Oto przykład:

avc: denied  { connectto } for  pid=2671 comm="ping" path="/dev/socket/dnsproxyd"
scontext=u:r:shell:s0 tcontext=u:r:netd:s0 tclass=unix_stream_socket

Zinterpretuj te dane wyjściowe w ten sposób:

  • Symbol { connectto } powyżej oznacza podejmowane działanie. Wraz z tclass na końcu (unix_stream_socket) informuje, co mniej więcej zostało zrobione i w jakim celu. W tym przypadku coś próbowało połączyć się z gniazdem strumieniowym systemu Unix.
  • Ikona scontext (u:r:shell:s0) informuje, w jakim kontekście zainicjowano działanie. W tym przypadku jest to coś, co działa jako powłoka.
  • Symbol tcontext (u:r:netd:s0) informuje o kontekście celu działania. W tym przypadku jest to gniazdo unix_stream_socket należące do użytkownika netd.
  • Symbol comm="ping" u góry zawiera dodatkową wskazówkę dotyczącą tego, co było uruchomione w momencie wygenerowania odmowy. W tym przypadku jest to całkiem dobra wskazówka.

Inny przykład:

adb shell su root dmesg | grep 'avc: '

Urządzenie wyjściowe:

<5> type=1400 audit: avc:  denied  { read write } for  pid=177
comm="rmt_storage" name="mem" dev="tmpfs" ino=6004 scontext=u:r:rmt:s0
tcontext=u:object_r:kmem_device:s0 tclass=chr_file

Oto najważniejsze elementy tej odmowy:

  • Działanie – podjęte działanie jest wyróżnione w nawiasach, np. read write lub setenforce.
  • Użytkownik – wpis scontext (kontekst źródłowy) reprezentuje użytkownika, w tym przypadku demona rmt_storage.
  • Obiekt – wpis tcontext (kontekst docelowy) reprezentuje obiekt, na którym wykonywana jest operacja, w tym przypadku kmem.
  • Wynik – wpis tclass (klasa docelowa) wskazuje typ obiektu, na którym wykonywana jest operacja, w tym przypadku chr_file (urządzenie znakowe).

Zrzucanie stosów użytkownika i jądra

W niektórych przypadkach informacje zawarte w dzienniku zdarzeń nie wystarczają do określenia przyczyny odmowy. Aby lepiej zrozumieć, dlaczego odmowa nastąpiła, warto zebrać łańcuch wywołań, w tym jądro i przestrzeń użytkownika.

W najnowszych wersjach jądra zdefiniowano punkt śledzenia o nazwie avc:selinux_audited. Użyj Androidasimpleperf, aby włączyć ten punkt śledzenia i zarejestrować łańcuch wywołań.

Obsługiwana konfiguracja

  • Obsługiwane są jądra Linuksa w wersji 5.10 lub nowszej, w szczególności gałęzie mainlineandroid12-5.10 wspólnego jądra Androida. Obsługiwana jest też gałąź android12-5.4. Za pomocą simpleperf możesz sprawdzić, czy punkt śledzenia jest zdefiniowany na urządzeniu:adb root && adb shell simpleperf list | grep avc:selinux_audited. W przypadku innych wersji jądra możesz wybrać poszczególne commity: dd8166230969bc.
  • Powinno być możliwe odtworzenie wydarzenia, które debugujesz. Zdarzenia czasu rozruchu nie są obsługiwane w przypadku simpleperf, ale możesz ponownie uruchomić usługę, aby wywołać zdarzenie.

Przechwytywanie łańcucha wywołań

Pierwszym krokiem jest zarejestrowanie zdarzenia za pomocą funkcji simpleperf record:

adb shell -t "cd /data/local/tmp && su root simpleperf record -a -g -e avc:selinux_audited"

Następnie powinno zostać wywołane zdarzenie, które spowodowało odmowę. Następnie nagrywanie powinno zostać zatrzymane. W tym przykładzie próbka powinna zostać zarejestrowana dzięki użyciu funkcji Ctrl-c:

^Csimpleperf I cmd_record.cpp:751] Samples recorded: 1. Samples lost: 0.

Na koniec możesz użyć simpleperf report, aby sprawdzić przechwycony ślad stosu. Na przykład:

adb shell -t "cd /data/local/tmp && su root simpleperf report -g --full-callgraph"
[...]
Children  Self     Command  Pid   Tid   Shared Object                                   Symbol
100.00%   0.00%    dmesg    3318  3318  /apex/com.android.runtime/lib64/bionic/libc.so  __libc_init
       |
       -- __libc_init
          |
           -- main
              toybox_main
              toy_exec_which
              dmesg_main
              klogctl
              entry_SYSCALL_64_after_hwframe
              do_syscall_64
              __x64_sys_syslog
              do_syslog
              selinux_syslog
              slow_avc_audit
              common_lsm_audit
              avc_audit_post_callback
              avc_audit_post_callback

Powyższy łańcuch wywołań to ujednolicony łańcuch wywołań jądra i przestrzeni użytkownika. Zapewnia lepszy wgląd w przepływ kodu, ponieważ śledzenie rozpoczyna się w przestrzeni użytkownika i obejmuje całą ścieżkę aż do jądra, w którym następuje odmowa. Więcej informacji o simpleperf znajdziesz w dokumentacji poleceń wykonywalnych Simpleperf.

Przełącz na licencję zezwalającą

Wymuszanie SELinux można wyłączyć za pomocą adb w przypadku kompilacji userdebug lub eng. Aby to zrobić, najpierw przełącz ADB na tryb root, wpisując adb root. Następnie, aby wyłączyć egzekwowanie SELinux, uruchom to polecenie:

adb shell setenforce 0

lub w wierszu poleceń jądra (podczas wczesnego uruchamiania urządzenia):

androidboot.selinux=permissive
androidboot.selinux=enforcing

lub za pomocą bootconfig w Androidzie 12:

androidboot.selinux=permissive
androidboot.selinux=enforcing

Używanie narzędzia audit2allow

Narzędzie audit2allow przyjmuje dmesg odmowy i przekształca je w odpowiednie instrukcje zasad SELinux. Dzięki temu może znacznie przyspieszyć rozwój SELinux.

Aby go użyć, wpisz:

adb pull /sys/fs/selinux/policy
adb logcat -b events -d | audit2allow -p policy

Należy jednak dokładnie sprawdzać każde potencjalne rozszerzenie pod kątem nadmiernych uprawnień. Na przykład podanie audit2allow the rmt_storage denial pokazanego wcześniej spowoduje wyświetlenie tej sugerowanej instrukcji zasad SELinux:

#============= shell ==============
allow shell kernel:security setenforce;
#============= rmt ==============
allow rmt kmem_device:chr_file { read write };

Dzięki temu rmt zyskuje możliwość zapisywania pamięci jądra, co stanowi poważną lukę w zabezpieczeniach. Często audit2allow oświadczenia są tylko punktem wyjścia. Po zastosowaniu tych stwierdzeń może być konieczne zmodyfikowanie domeny źródłowej i etykiety miejsca docelowego oraz włączenie odpowiednich makr, aby uzyskać prawidłowe zasady. Czasami odrzucenie, które jest sprawdzane, nie powinno w ogóle skutkować zmianami zasad. Zamiast tego należy zmienić aplikację, która narusza zasady.