Walidacja SELinux

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

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

Spowoduje to wydrukowanie globalnego trybu SELinux: Enforcing lub Permissive. Aby określić tryb SELinux dla każdej domeny, należy sprawdzić odpowiednie pliki lub uruchomić najnowszą wersję sepolicy-analyze z odpowiednią flagą ( -p ), obecną w /platform/system/sepolicy/tools/ .

Zaprzeczenia czytania

Sprawdź błędy, 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 do dmesg na tych urządzeniach i udoskonalić ustawienia przed publicznym wydaniem w trybie zezwalającym i ewentualnym przejściem w tryb wymuszający. Komunikaty dziennika SELinux zawierają avc: i dlatego można je łatwo znaleźć za pomocą grep . Możliwe jest przechwycenie trwających dzienników odmów, uruchamiając cat /proc/kmsg lub przechwycenie dzienników odmów z poprzedniego rozruchu, uruchamiając cat /sys/fs/pstore/console-ramoops .

Komunikaty o błędach SELinux mają ograniczoną szybkość po zakończeniu rozruchu, aby uniknąć zapełnienia dzienników. Aby upewnić się, ż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 lub komponenty systemu naruszają politykę SELinux. Producenci mogą następnie naprawić to złe zachowanie, zmieniając oprogramowanie, politykę SELinux lub jedno i drugie.

W szczególności te komunikaty dziennika wskazują, które procesy zakończą się niepowodzeniem 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 to wyjście w następujący sposób:

  • { connectto } powyżej reprezentuje podejmowane działanie. Wraz z tclass na końcu ( unix_stream_socket ), mówi ci z grubsza, co było robione z czym. W tym przypadku coś próbowało połączyć się z gniazdem unix stream.
  • 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" u góry daje dodatkową wskazówkę na temat tego, co było uruchamiane w momencie wygenerowania odmowy. W tym przypadku to całkiem dobra 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 tego zaprzeczenia:

  • Akcja - próba akcji jest podświetlona w nawiasach, read write lub setenforce .
  • Aktorscontext (kontekst źródłowy) reprezentuje aktora, w tym przypadku demona rmt_storage .
  • Obiekttcontext (kontekst docelowy) reprezentuje obiekt, na którym wykonywane są działania, 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).

Zrzucanie stosów użytkownika i jądra

W niektórych przypadkach informacje zawarte w dzienniku zdarzeń nie wystarczają 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 wystąpiła odmowa.

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

Obsługiwana konfiguracja

  • Jądro systemu Linux >= 5.10, w szczególności obsługiwane są główne gałęzie systemu Android Common Kernel i system Android 12-5.10 . Obsługiwana jest również gałąź Androida 12-5.4 . Możesz użyć simpleperf , aby określić, 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ć 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 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 powinno zostać wywołane zdarzenie, które spowodowało odmowę. Po tym nagrywanie powinno zostać zatrzymane. W tym przykładzie przy użyciu Ctrl-c próbka powinna zostać przechwycona:

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

Wreszcie, simpleperf report może być 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, w którym następuje odmowa. Aby uzyskać więcej informacji na temat simpleperf , zobacz informacje o poleceniach wykonywalnych Simpleperf

Przełączanie na permisywne

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 przez bootconfig w Androidzie 12:

androidboot.selinux=permissive
androidboot.selinux=enforcing

Korzystanie z audit2allow

Narzędzie audit2allow pobiera odmowy dmesg i konwertuje je na odpowiednie deklaracje polityki SELinux. W związku z tym może znacznie przyspieszyć rozwój SELinuksa.

Aby go użyć, uruchom:

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

Niemniej jednak należy zachować ostrożność, aby zbadać każdy potencjalny dodatek pod kątem przekroczenia uprawnień. Na przykład podawanie audit2allow odmowy rmt_storage pokazanej 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 };

Dałoby to rmt możliwość zapisywania 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ć konieczna zmiana domeny źródłowej i etykiety celu, a także włączenie odpowiednich makr, aby uzyskać dobrą politykę. Czasami badana odmowa nie powinna w ogóle powodować żadnych zmian w polityce; raczej należy zmienić aplikację naruszającą prawo.