Weryfikacja SELinux

Android zdecydowanie zachęca producentów OEM do dokładnego testowania implementacji SELinux. W miarę wdrażania SELinux producenci powinni najpierw zastosować nowe zasady w puli urządzeń testowych.

Po zastosowaniu nowych zasad sprawdź, czy SELinux działa na urządzeniu w odpowiednim trybie, wydając polecenie getenforce.

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

Odczytywanie odmów

Sprawdź, czy nie ma błędów, które są kierowane jako dzienniki zdarzeń do dmesg i logcat i są widoczne lokalnie na urządzeniu. Producenci powinni sprawdzić dane wyjściowe SELinux w dmesg na tych urządzeniach i dopracować ustawienia przed publicznym udostępnieniem w trybie Permissive i ewentualnym przejściem do trybu Enforcing. Komunikaty dziennika SELinux zawierają avc:, więc można je łatwo znaleźć za pomocą grep. Możesz rejestrować bieżące dzienniki odmów, uruchamiając cat /proc/kmsg, lub rejestrować dzienniki odmów z poprzedniego uruchomienia, uruchamiając cat /sys/fs/pstore/console-ramoops.

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

Dzięki tym danym wyjściowym producenci mogą łatwo określić, kiedy użytkownicy lub komponenty systemu naruszają zasady SELinux. Producenci mogą następnie naprawić to niewłaściwe działanie, wprowadzając zmiany w oprogramowaniu, zasadach SELinux lub w obu tych elementach.

Te komunikaty dziennika wskazują, które procesy nie powiodłyby się w trybie Enforcing 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

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

  • Powyższy element { connectto } reprezentuje wykonywaną czynność. Wraz z elementem tclass na końcu (unix_stream_socket) informuje on, co i w jaki sposób zostało wykonane co. W tym przypadku coś próbowało połączyć się z gniazdem strumienia uniksowego.
  • Element scontext (u:r:shell:s0) informuje, jaki kontekst zainicjował działanie. W tym przypadku jest to coś, co działa jako powłoka.
  • Element tcontext (u:r:netd:s0) informuje o kontekście celu działania. W tym przypadku jest to gniazdo strumienia uniksowego należące do netd.
  • Element 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 dość dobra wskazówka.

Inny przykład:

adb shell su root dmesg | grep 'avc: '

Dane 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 – próba działania jest wyróżniona w nawiasach, 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 wykonywane jest działanie, w tym przypadku kmem.
  • Wynik – wpis tclass (klasa docelowa) wskazuje typ obiektu, na którym wykonywane jest działanie, 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 źródła odmowy. Często przydatne jest zebranie łańcucha wywołań, w tym jądra i przestrzeni użytkownika, aby lepiej zrozumieć, dlaczego doszło do odmowy.

Najnowsze jądra definiują punkt śledzenia o nazwie avc:selinux_audited. Użyj Androida simpleperf, 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, w szczególności gałęzie Android Common Kernel mainline i android12-5.10. Obsługiwana jest też gałąź android12-5.4. Za pomocą simpleperf możesz sprawdzić, czy punkt śledzenia jest zdefiniowany na Twoim urządzeniu: adb root && adb shell simpleperf list | grep avc:selinux_audited. W przypadku innych wersji jądra możesz wybrać commity dd81662 i 30969bc.
  • Powinno być możliwe odtworzenie zdarzenia, które debugujesz. Zdarzenia podczas uruchamiania nie są obsługiwane przez simpleperf. Możesz jednak ponownie uruchomić usługę, aby wywołać zdarzenie.

Rejestrowanie łańcucha wywołań

Pierwszym krokiem jest zarejestrowanie zdarzenia za pomocą simpleperf record:

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

Następnie należy wywołać zdarzenie, które spowodowało odmowę. Potem należy zatrzymać nagrywanie. W tym przykładzie próbka powinna zostać zarejestrowana po naciśnięciu Ctrl-c:

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

Na koniec możesz użyć simpleperf report, aby sprawdzić zarejestrowany ś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. Dzięki temu uzyskasz lepszy wgląd w przepływ kodu, ponieważ śledzenie rozpoczyna się w przestrzeni użytkownika i prowadzi aż do jądra, w którym występuje odmowa. Więcej informacji o simpleperf znajdziesz w dokumentacji poleceń wykonywalnych Simpleperf

Przełączanie na tryb Permissive

Wymuszanie SELinux można wyłączyć za pomocą adb w kompilacjach userdebug lub eng. Aby to zrobić, najpierw przełącz ADB na root, uruchamiając adb root. Następnie, aby wyłączyć wymuszanie SELinux, uruchom:

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 audit2allow

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

Aby go użyć, uruchom:

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

Należy jednak dokładnie sprawdzić każde potencjalne dodanie pod kątem nadmiernych uprawnień. Na przykład przekazanie do audit2allow odmowy rmt_storage pokazanej wcześniej spowoduje wygenerowanie następującej sugerowanej instrukcji zasad SELinux:

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

Przyznałoby to rmt możliwość zapisywania pamięci jądra, co stanowi poważną lukę w zabezpieczeniach. Często audit2allow twierdzenia są tylko punktem początkowym. Po zastosowaniu tych instrukcji może być konieczne zmodyfikowanie domeny źródłowej i etykiety celu, a także uwzględnienie odpowiednich makr, aby uzyskać dobre zasady. Czasami sprawdzana odmowa nie powinna powodować żadnych zmian w zasadach. Zamiast tego należy zmienić aplikację, która spowodowała problem.