Weryfikowanie SELinux

Android zdecydowanie zachęca producentów OEM do dokładnego testowania implementacji SELinux. Gdy producenci wdrażają SELinux, powinni najpierw zastosować nową zasadę w puli testowej urządzeń.

Po zastosowaniu nowej zasady sprawdź, czy SELinux działa w odpowiednim trybie na urządzeniu. Aby to zrobić, uruchom polecenie getenforce.

Powoduje to wydrukowanie globalnego trybu SELinux: wymuszania lub mniej rygorystycznego. Aby określić tryb SELinux w przypadku każdej domeny, musisz sprawdzić odpowiednie pliki lub uruchomić najnowszą wersję sepolicy-analyze z odpowiednią flagą (-p) dostępną w /platform/system/sepolicy/tools/.

Odczytywanie odpowiedzi odmów

Sprawdź, czy nie występują błędy, które są kierowane jako dzienniki zdarzeń do dmesglogcat oraz są widoczne lokalnie na urządzeniu. Producenci powinni sprawdzić dane wyjściowe SELinux dmesg na tych urządzeniach i doprecyzować ustawienia przed ich opublikowaniem w trybie mniej rygorystycznym, a następnie przełączyć na tryb egzekwowania. Wiadomości z dziennika SELinux zawierają avc:, więc można je łatwo znaleźć za pomocą grep. Można przechwytywać bieżące logi odmowy, uruchamiając cat /proc/kmsg lub przechwytując logi odmowy z poprzedniego uruchomienia przez uruchomienie polecenia cat /sys/fs/pstore/console-ramoops.

Częstotliwość komunikatów o błędach SELinux jest ograniczona po zakończeniu uruchamiania, aby zapobiec zniekształceniu logów. 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ą z łatwością 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.

Komunikaty te wskazują w szczególności, których procesów nie da się wymuszać 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

Zinterpretuj te dane wyjściowe w następujący sposób:

  • { connectto } powyżej wskazuje podjęte 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, w jakim kontekście zainicjowano 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 kanał unix_stream_socket należący do organizacji netd.
  • comm="ping" u góry zawiera dodatkowy podpowiedź dotyczącą tego, co było wykonywane 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, który wykonał czynność – wpis scontext (kontekst źródła) reprezentuje podmiot, w tym przypadku demona rmt_storage.
  • Obiekt – wpis tcontext (kontekst docelowy) reprezentuje obiekt, na którym wykonano działanie, w tym przypadku kmem.
  • Wynik – wpis tclass (klasa docelowa) wskazuje typ obiektu, którego dotyczy działanie, w tym przypadku chr_file (urządzenie znaków).

Wydumpowanie stosów użytkownika i jądra

W niektórych przypadkach informacji zawartych w logu zdarzeń jest za mało, aby można było wskazać źródło odmowy. Często przydatne jest zebranie łańcucha wywołań, w tym jądra i przestrzeni użytkownika, w celu lepszego zrozumienia przyczyn odmowy.

Ostatnie jądra tworzą punkt śledzenia o nazwie avc:selinux_audited. Użyj Androida simpleperf, aby włączyć ten punkt śledzenia i przechwycić łańcuch wywołań.

Obsługiwana konfiguracja

  • Jądro Linuksa >= 5.10, w szczególności gałęzie mainlineandroid12-5.10 jądra wspólnego Androida. Obsługiwana jest też gałąź android12-5.4. Możesz użyć simpleperf, aby określić, 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ć commity dd8166230969bc.
  • Możliwe jest odtworzenie debugowanego zdarzenia. 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. Po tym czasie nagrywanie powinno zostać zatrzymane. W tym przykładzie przy użyciu funkcji Ctrl-c próbka powinna zostać przechwycona:

^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 to lepszy wgląd w przepływ kodu, ponieważ umożliwia uruchomienie śledzenia od przestrzeni użytkownika aż do jądra systemu, w którym następuje odmowa. Więcej informacji o simpleperf znajdziesz w pliku pomocy z poleceniami Simpleperf.

Przełącz na mniej rygorystyczny

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

adb shell setenforce 0

W wierszu poleceń jądra (podczas wcześniejszego wywołania urządzenia):

androidboot.selinux=permissive
androidboot.selinux=enforcing

Na Androidzie 12 możesz to zrobić za pomocą konfiguracji rozruchowej:

androidboot.selinux=permissive
androidboot.selinux=enforcing

Używaj audyt2allow

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

Aby go użyć, uruchom polecenie:

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

Pamiętaj jednak, aby sprawdzić każdy przypadek dodania uprawnień pod kątem przekraczania limitu. Na przykład przesłanie audit2allow odmowy rmt_storage powoduje wyświetlenie następującego sugerowanego stwierdzenia zasady SELinux:

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

Przyznaje to usłudze rmt możliwość zapisu w pamięci jądra, co stanowi ogromną lukę w zabezpieczeniach. Często instrukcje audit2allow stanowią tylko punkt wyjścia. Po zastosowaniu tych stwierdzeń może być konieczna zmiana domeny źródłowej i etykiety elementu docelowego, a także dodanie odpowiednich makr. Czasami sprawdzenie odmowy nie powinno w ogóle spowodować zmiany zasad – należy zmienić aplikację naruszającą zasady.