Android incoraggia fortemente gli OEM a testare a fondo le loro implementazioni di SELinux. Poiché i produttori implementano SELinux, dovrebbero prima applicare la nuova policy a un pool di test di dispositivi.
Dopo aver applicato una nuova politica, assicurati che SELinux sia in esecuzione nella modalità corretta sul dispositivo emettendo il comando getenforce
.
Questo stampa la modalità globale di SELinux: Enforcing o Permissive. Per determinare la modalità SELinux per ciascun dominio, è necessario esaminare i file corrispondenti o eseguire l'ultima versione di sepolicy-analyze
con l'apposito flag ( -p
), presente in /platform/system/sepolicy/tools/
.
Leggere le smentite
Verifica la presenza di errori, che vengono indirizzati come registri eventi a dmesg
e logcat
e sono visualizzabili localmente sul dispositivo. I produttori dovrebbero esaminare l'output di SELinux in dmesg
su questi dispositivi e perfezionare le impostazioni prima del rilascio pubblico in modalità permissiva e passare eventualmente alla modalità di applicazione. I messaggi di registro di SELinux contengono avc:
e quindi possono essere facilmente trovati con grep
. È possibile acquisire i log di negazione in corso eseguendo cat /proc/kmsg
o acquisire i log di negazione dall'avvio precedente eseguendo cat /sys/fs/pstore/console-ramoops
.
I messaggi di errore di SELinux sono limitati dopo il completamento dell'avvio per evitare di sovraccaricare i log. Per assicurarti di visualizzare tutti i messaggi pertinenti, puoi disabilitarlo eseguendo adb shell auditctl -r 0
.
Con questo output, i produttori possono facilmente identificare quando gli utenti oi componenti del sistema violano la politica di SELinux. I produttori possono quindi riparare questo cattivo comportamento, modificando il software, la politica di SELinux o entrambi.
In particolare, questi messaggi di registro indicano quali processi fallirebbero in modalità di applicazione e perché. Ecco un esempio:
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
Interpreta questo output in questo modo:
- Il
{ connectto }
sopra rappresenta l'azione intrapresa. Insieme allatclass
alla fine (unix_stream_socket
), ti dice approssimativamente cosa è stato fatto a cosa. In questo caso, qualcosa stava tentando di connettersi a un socket di flusso unix. - Lo
scontext (u:r:shell:s0)
ti dice quale contesto ha avviato l'azione. In questo caso questo è qualcosa che funziona come shell. - Il
tcontext (u:r:netd:s0)
indica il contesto dell'obiettivo dell'azione. In questo caso, è un unix_stream_socket di proprietà dinetd
. - Il
comm="ping"
in alto fornisce un ulteriore suggerimento su ciò che era in esecuzione nel momento in cui è stato generato il diniego. In questo caso, è un suggerimento piuttosto buono.
Un altro esempio:
adb shell su root dmesg | grep 'avc: '
Produzione:
<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
Ecco gli elementi chiave di questa smentita:
- Azione : l'azione tentata è evidenziata tra parentesi,
read write
osetenforce
. - Actor - La
scontext
(contesto di origine) rappresenta l'attore, in questo caso il demonermt_storage
. - Object - La
tcontext
(contesto di destinazione) rappresenta l'oggetto su cui si agisce, in questo caso kmem. - Risultato : la
tclass
(classe di destinazione) indica il tipo di oggetto su cui si agisce, in questo caso unchr_file
(dispositivo di caratteri).
Dumping degli stack utente e kernel
In alcuni casi, le informazioni contenute nel registro eventi non sono sufficienti per individuare l'origine del diniego. Spesso è utile raccogliere la catena di chiamate, inclusi kernel e userspace, per capire meglio perché si è verificato il diniego.
I kernel recenti definiscono un tracepoint denominato avc:selinux_audited
. Utilizza Android simpleperf
per abilitare questo tracepoint e acquisire la callchain.
Configurazione supportata
- Kernel Linux >= 5.10, in particolare sono supportati i rami principali Android Common Kernel e android12-5.10 . È supportato anche il ramo android12-5.4 . Puoi usare
simpleperf
per determinare se il tracepoint è definito sul tuo dispositivo:adb root && adb shell simpleperf list | grep avc:selinux_audited
. Per altre versioni del kernel, puoi scegliere i commit dd81662 e 30969bc . - Dovrebbe essere possibile riprodurre l'evento di cui si sta eseguendo il debug. Gli eventi all'avvio non sono supportati usando simpleperf; tuttavia potresti essere ancora in grado di riavviare il servizio per attivare l'evento.
Catturare la catena di chiamate
Il primo passo è registrare l'evento usando simpleperf record
:
adb shell -t "cd /data/local/tmp && su root simpleperf record -a -g -e avc:selinux_audited"
Quindi, dovrebbe essere attivato l'evento che ha causato il diniego. Successivamente, la registrazione dovrebbe essere interrotta. In questo esempio, utilizzando Ctrl-c
, il campione avrebbe dovuto essere catturato:
^Csimpleperf I cmd_record.cpp:751] Samples recorded: 1. Samples lost: 0.
Infine, il simpleperf report
può essere utilizzato per ispezionare lo stacktrace catturato. Ad esempio:
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
La catena di chiamate di cui sopra è un kernel unificato e una catena di chiamate dello spazio utente. Ti offre una visione migliore del flusso di codice avviando la traccia dallo spazio utente fino al kernel dove avviene il rifiuto. Per ulteriori informazioni su simpleperf
, vedere il riferimento ai comandi Simpleperf Executable
Passaggio a permissivo
L'applicazione di SELinux può essere disabilitata tramite ADB su build userdebug o eng. Per fare ciò, prima passa da ADB a root eseguendo adb root
. Quindi, per disabilitare l'applicazione di SELinux, eseguire:
adb shell setenforce 0
O dalla riga di comando del kernel (durante il primo avvio del dispositivo):
androidboot.selinux=permissive
androidboot.selinux=enforcing
O tramite bootconfig in Android 12:
androidboot.selinux=permissive
androidboot.selinux=enforcing
Utilizzo di audit2allow
Lo strumento audit2allow
accetta i rifiuti dmesg
e li converte nelle corrispondenti dichiarazioni di policy SELinux. In quanto tale, può velocizzare notevolmente lo sviluppo di SELinux.
Per usarlo, eseguire:
adb pull /sys/fs/selinux/policy
adb logcat -b events -d | audit2allow -p policy
Tuttavia, è necessario prestare attenzione per esaminare ogni potenziale aggiunta per il superamento delle autorizzazioni. Ad esempio, alimentando audit2allow
il rifiuto rmt_storage
mostrato in precedenza si ottiene la seguente dichiarazione di politica SELinux suggerita:
#============= shell ============== allow shell kernel:security setenforce; #============= rmt ============== allow rmt kmem_device:chr_file { read write };
Ciò garantirebbe a rmt
la possibilità di scrivere la memoria del kernel, un evidente buco di sicurezza. Spesso le dichiarazioni audit2allow
sono solo un punto di partenza. Dopo aver utilizzato queste istruzioni, potrebbe essere necessario modificare il dominio di origine e l'etichetta di destinazione, oltre a incorporare le macro appropriate, per arrivare a una buona politica. A volte la negazione in esame non dovrebbe comportare alcun cambiamento di politica; piuttosto l'applicazione incriminata dovrebbe essere cambiata.