Walidacja SELinuksa

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

Po zastosowaniu nowej polityki upewnij się, że SELinux działa na urządzeniu we właściwym trybie, wydając polecenie getenforce .

Spowoduje to wydrukowanie globalnego trybu SELinux: wymuszającego lub zezwalającego. Aby określić tryb SELinux dla każdej domeny, musisz sprawdzić odpowiednie pliki lub uruchomić najnowszą wersję sepolicy-analyze z odpowiednią flagą ( -p ), znajdującą się w /platform/system/sepolicy/tools/ .

Czytanie zaprzeczeń

Sprawdź błędy, które są kierowane jako dzienniki zdarzeń do dmesg i logcat i można je przeglądać lokalnie na urządzeniu. Producenci powinni sprawdzić dane wyjściowe SELinux wysyłane do dmesg na tych urządzeniach i dopracować ustawienia przed publicznym udostępnieniem w trybie zezwalającym i ewentualnym przejściem na tryb wymuszający. Komunikaty dziennika SELinux zawierają avc: i dlatego można je łatwo znaleźć za pomocą grep . Możliwe jest przechwycenie bieżących dzienników odmów poprzez uruchomienie cat /proc/kmsg lub przechwycenie dzienników odmów z poprzedniego rozruchu poprzez uruchomienie cat /sys/fs/pstore/console-ramoops .

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

Dzięki tym wynikom producenci mogą łatwo zidentyfikować, kiedy użytkownicy systemu lub komponenty naruszają zasady SELinux. Producenci mogą następnie naprawić to złe zachowanie, zmieniając oprogramowanie, zasady SELinux lub jedno i drugie.

W szczególności te komunikaty dziennika wskazują, które procesy zawiodą 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 ten wynik w następujący sposób:

  • Powyższy element { connectto } reprezentuje podejmowane działanie. Razem z tclass na końcu ( unix_stream_socket ) informuje z grubsza, co zostało zrobione i z czym. W tym przypadku coś próbowało połączyć się z gniazdem strumieniowym Unix.
  • scontext (u:r:shell:s0) informuje, jaki kontekst zainicjował akcję. W tym przypadku jest to coś działającego jako powłoka.
  • tcontext (u:r:netd:s0) informuje o kontekście celu akcji. W tym przypadku jest to unix_stream_socket należący do netd .
  • comm="ping" na górze daje dodatkową wskazówkę na temat tego, co było wykonywane w momencie wygenerowania odmowy. W tym przypadku jest to całkiem niezła wskazówka.

Inny przykład:

adb shell su root dmesg | grep 'avc: '

Wyjście:

<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 kluczowe elementy tej odmowy:

  • Akcja - próba akcji jest podświetlona w nawiasach, read write lub setenforce .
  • Aktor — wpis scontext (kontekst źródłowy) reprezentuje aktora, 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 plik chr_file (urządzenie znakowe).

Zrzucanie stosów użytkowników i jądra

W niektórych przypadkach informacje zawarte w dzienniku zdarzeń nie są wystarczające do ustalenia ź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 nastąpiła odmowa.

Najnowsze jądra definiują punkt śledzenia o nazwie avc:selinux_audited . Użyj simpleperf Android, aby włączyć ten punkt śledzenia i przechwycić łańcuch połączeń.

Obsługiwana konfiguracja

  • Obsługiwane jest jądro Linuksa >= 5.10, w szczególności obsługiwane są główne gałęzie Android Common Kernel i Android 12-5.10 . Obsługiwana jest także gałąź Android12-5.4 . Możesz użyć simpleperf , aby określić, czy na Twoim urządzeniu zdefiniowano punkt śledzenia: adb root && adb shell simpleperf list | grep avc:selinux_audited . W przypadku innych wersji jądra możesz wybrać zatwierdzenia dd81662 i 30969bc .
  • Powinno być możliwe odtworzenie debugowanego zdarzenia. Zdarzenia czasu rozruchu nie są obsługiwane przy użyciu Simpleperf; jednak nadal możesz być w stanie ponownie uruchomić usługę, aby wywołać zdarzenie.

Przechwytywanie łańcucha połączeń

Pierwszym krokiem jest nagranie zdarzenia przy użyciu 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 należy zatrzymać nagrywanie. W tym przykładzie, używając Ctrl-c , próbka powinna zostać przechwycona:

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

Wreszcie simpleperf report może zostać użyty do sprawdzenia przechwyconego śladu 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. Daje lepszy wgląd w przepływ kodu, rozpoczynając śledzenie od przestrzeni użytkownika aż do jądra, gdzie następuje odmowa. Więcej informacji na temat simpleperf można znaleźć w dokumentacji dotyczącej poleceń wykonywalnych Simpleperf

Przełączam na permisywne

Wymuszanie SELinux można wyłączyć za pomocą ADB w przypadku debugowania użytkownika lub kompilacji 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 poprzez bootconfig w Androidzie 12:

androidboot.selinux=permissive
androidboot.selinux=enforcing

Korzystanie z audytu2allow

Narzędzie audit2allow pobiera odmowy dmesg i konwertuje je na odpowiednie instrukcje polityki SELinux. Jako taki może znacznie przyspieszyć rozwój SELinuksa.

Aby z niego skorzystać, uruchom:

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

Niemniej jednak należy zachować ostrożność i sprawdzić każdy potencjalny dodatek pod kątem przekroczenia uprawnień. Na przykład, karmienie audit2allow odmowę rmt_storage pokazaną wcześniej, skutkuje następującym sugerowanym oświadczeniem polityki SELinux:

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

Zapewniłoby to rmt możliwość zapisywania pamięci jądra, co stanowi rażącą lukę w zabezpieczeniach. Często instrukcje audit2allow stanowią jedynie punkt wyjścia. Po zastosowaniu tych instrukcji może zaistnieć potrzeba zmiany domeny źródłowej i etykiety celu, a także włączenia odpowiednich makr, aby uzyskać dobrą politykę. Czasami rozpatrywana odmowa nie powinna w ogóle skutkować żadnymi zmianami polityki; należy raczej zmienić aplikację powodującą naruszenie.