Validierung von SELinux

Android empfiehlt OEMs dringend, ihre SELinux-Implementierungen gründlich zu testen. Wenn Hersteller SELinux implementieren, sollten sie die neue Richtlinie zuerst auf einen Testpool von Geräten anwenden.

Stellen Sie nach dem Anwenden einer neuen Richtlinie sicher, dass SELinux im richtigen Modus auf dem Gerät ausgeführt wird, indem Sie den Befehl getenforce .

Dies gibt den globalen SELinux-Modus aus: entweder Enforcing oder Permissive. Um den SELinux-Modus für jede Domäne zu bestimmen, müssen Sie die entsprechenden Dateien untersuchen oder die neueste Version von sepolicy-analyze mit dem entsprechenden Flag ( -p ) ausführen, das sich in /platform/system/sepolicy/tools/ .

Leugnungen lesen

Suchen Sie nach Fehlern, die als Ereignisprotokolle an dmesg und logcat werden und lokal auf dem Gerät angezeigt werden können. Hersteller sollten die SELinux-Ausgabe an dmesg auf diesen Geräten untersuchen und die Einstellungen vor der Veröffentlichung im Permissive-Modus und eventuellem Wechsel in den Enforcing-Modus verfeinern. SELinux-Protokollmeldungen enthalten avc: und können daher leicht mit grep gefunden werden. Es ist möglich, die laufenden Verweigerungsprotokolle durch Ausführen von cat /proc/kmsg oder Verweigerungsprotokolle vom vorherigen Booten durch Ausführen von cat /sys/fs/pstore/console-ramoops zu erfassen.

Mit dieser Ausgabe können Hersteller leicht erkennen, wenn Systembenutzer oder -komponenten gegen die SELinux-Richtlinie verstoßen. Hersteller können dieses schlechte Verhalten dann entweder durch Änderungen an der Software, der SELinux-Richtlinie oder beidem beheben.

Diese Protokollmeldungen geben insbesondere an, welche Prozesse im Erzwingungsmodus fehlschlagen würden und warum. Hier ist ein Beispiel:

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

Interpretieren Sie diese Ausgabe wie folgt:

  • Das { connectto } stellt die ausgeführte Aktion dar. Zusammen mit der tclass am Ende ( unix_stream_socket ) sagt es Ihnen ungefähr, was mit was gemacht wurde. In diesem Fall hat etwas versucht, eine Verbindung zu einem Unix-Stream-Socket herzustellen.
  • Der scontext (u:r:shell:s0) sagt Ihnen, welcher Kontext die Aktion initiiert hat. In diesem Fall läuft dies als Shell.
  • Der tcontext (u:r:netd:s0) teilt Ihnen den Kontext des Aktionsziels mit. In diesem Fall ist das ein unix_stream_socket im Besitz von netd .
  • Das comm="ping" oben gibt Ihnen einen zusätzlichen Hinweis darauf, was zum Zeitpunkt der Ablehnung ausgeführt wurde. In diesem Fall ist es ein ziemlich guter Hinweis.

Ein anderes Beispiel:

adb shell su root dmesg | grep 'avc: '

Ausgabe:

<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

Hier sind die Schlüsselelemente dieser Ablehnung:

  • Aktion – die versuchte Aktion wird in Klammern hervorgehoben, read write oder setenforce .
  • Akteur – Der scontext (Quellkontext) stellt den Akteur dar, in diesem Fall den Daemon rmt_storage .
  • Objekt – Der tcontext (Zielkontext) stellt das Objekt dar, auf das eingewirkt wird, in diesem Fall kmem.
  • Ergebnis – Der tclass (Zielklasse) gibt den Objekttyp an, auf den eingewirkt wird, in diesem Fall eine chr_file (Zeichengerät).

Sichern von Benutzer- und Kernel-Stacks

In einigen Fällen reichen die im Ereignisprotokoll enthaltenen Informationen nicht aus, um den Ursprung der Ablehnung zu bestimmen. Es ist oft nützlich, die Anrufkette einschließlich Kernel und Benutzerbereich zu sammeln, um besser zu verstehen, warum die Ablehnung aufgetreten ist.

Neuere Kernel definieren einen Tracepoint namens avc:selinux_audited . Verwenden Sie Android simpleperf , um diesen Ablaufverfolgungspunkt zu aktivieren und die Anrufkette zu erfassen.

Unterstützte Konfiguration

  • Linux Kernel >= 5.10, insbesondere Android Common Kernel Branches Mainline und Android12-5.10 werden unterstützt. Der Android12-5.4- Zweig wird ebenfalls unterstützt. Sie können simpleperf verwenden, um festzustellen, ob der Ablaufverfolgungspunkt auf Ihrem Gerät definiert ist: adb root && adb shell simpleperf list | grep avc:selinux_audited . Für andere Kernel-Versionen können Sie die Commits dd81662 und 30969bc aussuchen .
  • Es sollte möglich sein, das Ereignis, das Sie debuggen, zu reproduzieren. Startzeitereignisse werden bei Verwendung von simpleperf nicht unterstützt; Sie können den Dienst jedoch möglicherweise immer noch neu starten, um das Ereignis auszulösen.

Erfassen der Anrufkette

Der erste Schritt besteht darin, das Ereignis mit simpleperf record :

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

Dann sollte das Ereignis ausgelöst werden, das die Verweigerung verursacht hat. Danach sollte die Aufnahme gestoppt werden. In diesem Beispiel sollte das Beispiel mit Ctrl-c erfasst werden:

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

Schließlich kann der simpleperf report verwendet werden, um den erfassten Stacktrace zu untersuchen. Zum Beispiel:

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

Die obige Aufrufkette ist eine einheitliche Kernel- und Userspace-Aufrufkette. Es gibt Ihnen einen besseren Überblick über den Codefluss, indem es den Trace vom Userspace bis hinunter zum Kernel startet, wo die Verweigerung stattfindet. Weitere Informationen zu simpleperf finden Sie in der Referenz zu ausführbaren Simpleperf-Befehlen

Umschalten auf permissiv

Die SELinux-Erzwingung kann über ADB bei Userdebug- oder Eng-Builds deaktiviert werden. Schalten Sie dazu zuerst ADB auf root um, indem Sie adb root . Führen Sie dann Folgendes aus, um die SELinux-Erzwingung zu deaktivieren:

adb shell setenforce 0

Oder in der Kernel-Befehlszeile (während des frühen Gerätestarts):

androidboot.selinux=permissive
androidboot.selinux=enforcing

Oder über bootconfig in Android 12:

androidboot.selinux=permissive
androidboot.selinux=enforcing

Verwenden von audit2allow

Das audit2allow Tool nimmt dmesg Verweigerungen und wandelt sie in entsprechende SELinux-Richtlinienanweisungen um. Als solches kann es die SELinux-Entwicklung erheblich beschleunigen.

Um es zu verwenden, führen Sie Folgendes aus:

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

Dennoch muss darauf geachtet werden, jede potenzielle Ergänzung auf übergreifende Berechtigungen zu prüfen. Wenn Sie beispielsweise audit2allow der zuvor gezeigten rmt_storage Verweigerung füttern, führt dies zu der folgenden vorgeschlagenen SELinux-Richtlinienanweisung:

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

Dies würde rmt die Möglichkeit geben, Kernel-Speicher zu schreiben, eine eklatante Sicherheitslücke. Oft sind die audit2allow Anweisungen nur ein Ausgangspunkt. Nachdem Sie diese Anweisungen verwendet haben, müssen Sie möglicherweise die Quelldomäne und die Bezeichnung des Ziels ändern sowie geeignete Makros einbauen, um zu einer guten Richtlinie zu gelangen. Manchmal sollte die untersuchte Verweigerung überhaupt nicht zu irgendwelchen Richtlinienänderungen führen; vielmehr sollte die fehlerhafte Anwendung geändert werden.