Weryfikacja SELinux

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

Po zastosowaniu nowej zasady sprawdź, czy SELinux działa w prawidłowym trybie na urządzeniu, wykonując polecenie getenforce.

Wyświetla globalny tryb SELinux: Enforcing (Wymuszający) lub Permissive (Zezwalający). 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/.

Odmowa odczytu

Sprawdź, czy nie występują błędy, które są kierowane jako dzienniki zdarzeń do dmesg i logcat oraz są widoczne lokalnie na urządzeniu. Producenci powinni sprawdzić dane wyjściowe SELinux (dmesg) na tych urządzeniach i dostosować ustawienia przed publiczną wersją w trybie zezwalającym, a potem przejść do trybu wymuszającego. Wiadomości z dziennika SELinux zawierają avc:, więc można je łatwo znaleźć za pomocą grep. Aby przechwycić bieżące logi odmowy, uruchom cat /proc/kmsg. Aby przechwycić logi odmowy z poprzedniego uruchomienia, uruchom cat /sys/fs/pstore/console-ramoops.

Aby uniknąć przeciążania dzienników, po zakończeniu uruchamiania system ogranicza częstotliwość wyświetlania komunikatów o błędach SELinux. Aby mieć pewność, że widzisz wszystkie istotne wiadomości, możesz wyłączyć tę funkcję, adb shell auditctl -r 0.

Dzięki tym wynikom producenci mogą łatwo określić, kiedy użytkownicy lub komponenty systemu naruszają zasady SELinux. Producenci mogą naprawić to złe zachowanie, wprowadzając zmiany w oprogramowaniu lub zasadach SELinux albo w obu tych elementach.

Te komunikaty wskazują, które procesy nie będą działać w trybie wymuszania 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:

  • { connectto } powyżej przedstawia podejmowane działanie. Wraz z tclass na końcu (unix_stream_socket) informuje, co zostało zrobione i dla kogo. W tym przypadku coś próbowało nawiązać połączenie z gniazdem strumieniowym Unixa.
  • scontext (u:r:shell:s0) informuje, jaki kontekst zainicjował działanie. W tym przypadku jest to coś, co działa jako powłoka.
  • tcontext (u:r:netd:s0) zawiera kontekst celu działania. W tym przypadku jest to unix_stream_socket należący do netd.
  • comm="ping" u góry strony zawiera dodatkowy podpowiedź dotyczącą tego, co było uruchamiane w momencie wygenerowania odmowy. W tym przypadku jest to całkiem dobry wskazówek.

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 – próba wykonania działania jest wyróżniona w nawiasach:read write lub setenforce.
  • Użytkownik – wpis scontext (kontekst źródła) 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 są działania, w tym przypadku chr_file (urządzenie znakowe).

Wydumpowanie stosów użytkownika i jądra

W niektórych przypadkach informacji zawartych w rejestrze zdarzeń może nie wystarczyć do określenia źródła odmowy. Aby lepiej zrozumieć, dlaczego doszło do odrzucenia, warto często zebrać łańcuch wywołań, w tym dotyczące jądra i przestrzeni użytkownika.

Najnowsze jądra definiują punkt śledzenia o nazwie avc:selinux_audited. Aby włączyć ten punkt śledzenia i utworzyć łańcuch wywołań, użyj na urządzeniu z Androidem opcji simpleperf.

Obsługiwana konfiguracja

  • Jądro Linuksa >= 5.10, w szczególności gałęzie jądra wspólnego Androida mainlineandroid12-5.10 są obsługiwane. Obsługiwana jest też gałąź android12-5.4. Aby sprawdzić, czy punkt trasy jest zdefiniowany na urządzeniu, użyj polecenia simpleperf:adb root && adb shell simpleperf list | grep avc:selinux_audited. W przypadku innych wersji jądra możesz wybrać commity dd8166230969bc.
  • Należy mieć możliwość odtworzenia zdarzenia, które debugujesz. Zdarzenia uruchamiania nie są obsługiwane przez simpleperf, ale nadal możesz ponownie uruchomić usługę, aby wywołać zdarzenie.

Rejestrowanie łańcucha wywołań

Najpierw należy zarejestrować zdarzenie 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 odrzucenie. Następnie nagrywanie powinno zostać zatrzymane. W tym przykładzie próbka zostałaby zarejestrowana za pomocą 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ć 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 zjednoczony łańcuch wywołań jądra i przestrzeni użytkownika. Zapewnia on lepszy wgląd w przepływ kodu, ponieważ rozpoczyna śledzenie od przestrzeni użytkownika aż do jądra, gdzie następuje odmowa. Więcej informacji o simpleperf znajdziesz w dokumentacji dotyczącej poleceń wykonalnych Simpleperf.

Przełącz na tryb dozwolający

Wymuszanie SELinux można wyłączyć za pomocą adb w wersjach userdebug lub eng. Aby to zrobić, najpierw przełącz ADB na root, wykonując polecenie adb root. Aby wyłączyć egzekwowanie SELinux, uruchom:

adb shell setenforce 0

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

androidboot.selinux=permissive
androidboot.selinux=enforcing

Możesz też użyć bootconfig w Androidzie 12:

androidboot.selinux=permissive
androidboot.selinux=enforcing

Używanie audit2allow

Narzędzie audit2allow przekształca odmowy dmesg w odpowiednie oświadczenia o zasadach SELinux. Dzięki temu może znacznie przyspieszyć rozwój SELinux.

Aby go użyć, uruchom:

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

Należy jednak zachować ostrożność i sprawdzać każde potencjalne dodanie pod kątem nadmiernych uprawnień. Na przykład podanie audit2allow zaprzeczenia rmt_storage, które zostało pokazane wcześniej, powoduje wyświetlenie następującego zalecanego oświadczenia o zasadach SELinux:

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

Dałoby to rmt możliwość zapisywania w pamięci jądra, co jest rażącą luką w zabezpieczeniach. Często instrukcje audit2allow są tylko punktem wyjścia. Po zastosowaniu tych instrukcji może być konieczne zmodyfikowanie domeny źródłowej i etykiety docelowej oraz włączenie odpowiednich makr, aby uzyskać dobre zasady. Czasami odrzucenie nie powinno powodować żadnych zmian w zasadach. W takim przypadku należy zmienić aplikację, która narusza zasady.