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.
Prüfen Sie nach dem Anwenden einer neuen Richtlinie, ob SELinux auf dem Gerät im richtigen Modus ausgeführt wird. Geben Sie dazu den Befehl getenforce
ein.
Dadurch wird der globale SELinux-Modus ausgegeben: entweder „Erzwingen“ oder „Laissez-faire“. Um den SELinux-Modus für jede Domain zu ermitteln, müssen Sie die entsprechenden Dateien prüfen oder die neueste Version von sepolicy-analyze
mit dem entsprechenden Flag (-p
) ausführen, das in
/platform/system/sepolicy/tools/
enthalten ist.
Ablehnungen lesen
Prüfen Sie auf Fehler, die als Ereignisprotokolle an dmesg
und logcat
weitergeleitet und lokal auf dem Gerät angezeigt werden. Hersteller sollten die SELinux-Ausgabe an dmesg
auf diesen Geräten prüfen und die Einstellungen vor der Veröffentlichung im permissiven Modus optimieren und gegebenenfalls in den Erzwingungsmodus wechseln. SELinux-Logmeldungen enthalten avc:
und können daher mit grep
leicht gefunden werden. Sie können die laufenden Deaktivierungsprotokolle mit cat /proc/kmsg
erfassen oder die Deaktivierungsprotokolle vom vorherigen Start mit cat /sys/fs/pstore/console-ramoops
.
SELinux-Fehlermeldungen werden nach dem Starten mit einer Ratenbegrenzung versehen, um eine Überlastung der Protokolle zu vermeiden. Wenn Sie alle relevanten Nachrichten sehen möchten, können Sie diese Funktion deaktivieren, indem Sie adb shell auditctl -r 0
eingeben.
Anhand dieser Ausgabe können Hersteller leicht erkennen, ob Systemnutzer oder ‑komponenten gegen die SELinux-Richtlinie verstoßen. Hersteller können dieses Fehlverhalten dann beheben, entweder durch Änderungen an der Software, an der SELinux-Richtlinie oder an beiden.
Insbesondere geben diese Protokollmeldungen an, welche Prozesse im Erzwingungsmodus fehlschlagen würden und warum. Hier 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 so:
- Das
{ connectto }
oben steht für die ausgeführte Aktion. Zusammen mit demtclass
am Ende (unix_stream_socket
) erfahren Sie ungefähr, was mit was gemacht wurde. In diesem Fall wurde versucht, eine Verbindung zu einem Unix-Stream-Socket herzustellen. - Die
scontext (u:r:shell:s0)
gibt Aufschluss darüber, in welchem Kontext die Aktion ausgelöst wurde. In diesem Fall wird es als Shell ausgeführt. -
tcontext (u:r:netd:s0)
gibt den Kontext des Ziels der Aktion an. In diesem Fall ist das ein unix_stream_socket, dessen Eigentümernetd
ist. - Das Symbol
comm="ping"
oben gibt einen zusätzlichen Hinweis darauf, was zum Zeitpunkt der Ablehnung ausgeführt wurde. In diesem Fall ist es ein ziemlich guter Hinweis.
Ein weiteres 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 wichtigsten Elemente dieser Ablehnung:
- Aktion: Die versuchte Aktion ist in Klammern hervorgehoben:
read write
odersetenforce
. - Akteur: Der Eintrag
scontext
(Quellkontext) steht für den Akteur, in diesem Fall denrmt_storage
-Daemon. - Objekt: Der Eintrag
tcontext
(Zielkontext) steht für das Objekt, auf das eine Aktion angewendet wird, in diesem Fall kmem. - Ergebnis: Der Eintrag
tclass
(Zielklasse) gibt den Typ des Objekts an, auf das eine Aktion angewendet wird. In diesem Fall ist es einchr_file
(Zeichengerät).
Nutzer- und Kernel-Stacks dumpen
In einigen Fällen reichen die Informationen im Ereignisprotokoll nicht aus, um die Ursache der Ablehnung zu ermitteln. Es ist oft hilfreich, die Aufrufabfolge zu erfassen, einschließlich Kernel und Userspace, um besser nachvollziehen zu können, warum die Zugriffsverweigerung aufgetreten ist.
Neuere Kernel definieren einen Tracepoint mit dem Namen avc:selinux_audited
. Verwenden Sie Androidsimpleperf
, um diesen Tracepoint zu aktivieren und die Aufrufabfolge aufzuzeichnen.
Unterstützte Konfiguration
- Linux-Kernel >= 5.10, insbesondere die Android Common Kernel-Branches mainline und android12-5.10 werden unterstützt.
Der Branch android12-5.4 wird ebenfalls unterstützt. Mit
simpleperf
können Sie prüfen, ob der Tracepoint auf Ihrem Gerät definiert ist:adb root && adb shell simpleperf list | grep avc:selinux_audited
. Bei anderen Kernelversionen können Sie die Commits dd81662 und 30969bc auswählen. - Das Ereignis, das Sie beheben möchten, sollte reproduzierbar sein. Ereignisse zur Bootzeit werden von simpleperf nicht unterstützt. Sie können den Dienst jedoch neu starten, um das Ereignis auszulösen.
Aufrufabfolge erfassen
Der erste Schritt besteht darin, das Ereignis mit simpleperf record
aufzuzeichnen:
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 zur Ablehnung geführt hat. Danach sollte die Aufnahme beendet werden. In diesem Beispiel sollte das Sample mit Ctrl-c
aufgenommen worden sein:
^Csimpleperf I cmd_record.cpp:751] Samples recorded: 1. Samples lost: 0.
Mit simpleperf report
können Sie den erfassten Stacktrace prüfen.
Beispiele:
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 Aufrufabfolge ist eine einheitliche Aufrufabfolge für Kernel und Userspace. Sie erhalten einen besseren Überblick über den Codefluss, da die Verfolgung vom Userspace bis hinunter zum Kernel gestartet wird, wo die Deaktivierung erfolgt. Weitere Informationen zu simpleperf
finden Sie in der Referenz zu ausführbaren Simpleperf-Befehlen.
Zu „Permissiv“ wechseln
Die SELinux-Erzwigung kann mit adb bei Userdebug- oder eng-Builds deaktiviert werden. Führen Sie dazu zuerst adb root
aus, um ADB zu Root zu wechseln. Führen Sie dann diesen Befehl aus, um die SELinux-Erzwigung zu deaktivieren:
adb shell setenforce 0
Oder in der Kernel-Befehlszeile (während der frühen Geräteeinrichtung):
androidboot.selinux=permissive
androidboot.selinux=enforcing
Oder über die Boot-Konfiguration in Android 12:
androidboot.selinux=permissive
androidboot.selinux=enforcing
audit2allow verwenden
Das audit2allow
-Tool wandelt dmesg
-Ablehnungen in entsprechende SELinux-Richtlinienausdrücke um. So kann die SELinux-Entwicklung erheblich beschleunigt werden.
Führen Sie zum Verwenden Folgendes aus:
adb pull /sys/fs/selinux/policy
adb logcat -b events -d | audit2allow -p policy
Dennoch müssen Sie jede potenzielle Ergänzung auf übermäßige Berechtigungen prüfen. Wenn Sie audit2allow
beispielsweise die oben gezeigte rmt_storage
-Deaktivierung zuführen, wird die folgende vorgeschlagene SELinux-Richtlinienbeschreibung zurückgegeben:
#============= shell ============== allow shell kernel:security setenforce; #============= rmt ============== allow rmt kmem_device:chr_file { read write };
Dadurch hätte rmt
die Möglichkeit, in den Kernelspeicher zu schreiben, was eine eklatante Sicherheitslücke wäre. Häufig sind die audit2allow
-Anweisungen nur ein Ausgangspunkt. Nachdem Sie diese Anweisungen verwendet haben, müssen Sie möglicherweise die Quelldomain und das Label des Ziels ändern und die richtigen Makros einbinden, um eine gute Richtlinie zu erhalten. Manchmal sollte die Ablehnung, die geprüft wird, gar nicht zu Richtlinienänderungen führen. Stattdessen sollte die betreffende App geändert werden.