Android consiglia vivamente agli OEM di testare a fondo le implementazioni di SELinux. Quando i produttori implementano SELinux, devono prima applicare la nuova policy a un pool di test di dispositivi.
Dopo aver applicato una nuova policy, assicurati che SELinux sia in esecuzione nella modalità corretta sul dispositivo eseguendo il comando getenforce.
Questo comando stampa la modalità SELinux globale: Enforcing o Permissive. Per
determinare la modalità SELinux per ogni dominio, devi esaminare i file corrispondenti
o eseguire l'ultima versione di sepolicy-analyze con il
flag appropriato (-p), presente in
/platform/system/sepolicy/tools/.
Leggere i rifiuti
Controlla la presenza di errori, che vengono indirizzati come log degli eventi a dmesg e logcat e sono visualizzabili localmente sul dispositivo. I produttori devono esaminare l'output di SELinux in dmesg su questi dispositivi e perfezionare le impostazioni prima della release pubblica in modalità permissiva e del passaggio finale alla modalità di applicazione. I messaggi di log di SELinux contengono avc: e possono quindi essere facilmente trovati con grep. È possibile acquisire i log di rifiuto in corso eseguendo cat /proc/kmsg o acquisire i log di rifiuto dall'avvio precedente eseguendo cat /sys/fs/pstore/console-ramoops.
I messaggi di errore di SELinux sono limitati in base alla frequenza dopo il completamento dell'avvio per evitare di sovraccaricare i log. Per assicurarti di visualizzare tutti i messaggi pertinenti, puoi disattivare questa funzionalità eseguendo adb shell auditctl -r 0.
Con questo output, i produttori possono identificare facilmente quando gli utenti o i componenti di sistema violano la policy SELinux. I produttori possono quindi correggere queste prestazioni scadenti modificando il software, la policy SELinux o entrambi.
In particolare, questi messaggi di log indicano quali processi non andrebbero a buon fine 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 come segue:
- Il
{ connectto }sopra rappresenta l'azione intrapresa. Insieme atclassalla fine (unix_stream_socket), ti indica approssimativamente cosa è stato fatto a cosa. In questo caso, qualcosa stava tentando di connettersi a un socket di flusso Unix. -
scontext (u:r:shell:s0)indica il contesto che ha avviato l'azione. In questo caso, si tratta di qualcosa in esecuzione come shell. -
tcontext (u:r:netd:s0)indica il contesto della destinazione dell'azione. In questo caso, si tratta di un socket di flusso Unix di proprietà dinetd. -
comm="ping"nella parte superiore fornisce un suggerimento aggiuntivo su cosa era in esecuzione al momento della generazione del rifiuto. In questo caso, è un suggerimento piuttosto utile.
Un altro esempio:
adb shell su root dmesg | grep 'avc: '
Output:
<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 questo rifiuto:
- Azione : l'azione tentata è evidenziata tra parentesi,
read writeosetenforce. - Attore : la voce
scontext(contesto di origine) rappresenta l'attore, in questo caso il daemonrmt_storage. - Oggetto : la voce
tcontext(contesto di destinazione) rappresenta l'oggetto su cui viene eseguita l'azione, in questo caso kmem. - Risultato : la voce
tclass(classe di destinazione) indica il tipo di oggetto su cui viene eseguita l'azione, in questo caso unchr_file(dispositivo di caratteri).
Eseguire il dump degli stack utente e kernel
In alcuni casi, le informazioni contenute nel log degli eventi non sono sufficienti per individuare l'origine del rifiuto. Spesso è utile raccogliere la catena di chiamate, inclusi kernel e spazio utente, per comprendere meglio il motivo del rifiuto.
I kernel recenti definiscono un punto di traccia denominato avc:selinux_audited. Utilizza Android simpleperf per attivare questo punto di traccia e acquisire la catena di chiamate.
Configurazione supportata
- Sono supportati il kernel Linux >= 5.10, in particolare i rami
mainline
e
android12-5.10
del kernel comune di Android.
È supportato anche il ramo android12-5.4. Puoi utilizzare
simpleperfper determinare se il punto di traccia è 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 stai eseguendo il debug. Gli eventi di avvio non sono supportati con simpleperf, ma potresti comunque essere in grado di riavviare il servizio per attivare l'evento.
Acquisire la catena di chiamate
Il primo passo è registrare l'evento utilizzando simpleperf record:
adb shell -t "cd /data/local/tmp && su root simpleperf record -a -g -e avc:selinux_audited"
Quindi, deve essere attivato l'evento che ha causato il rifiuto. Dopodiché, la registrazione deve essere interrotta. In questo esempio, utilizzando Ctrl-c, il campione dovrebbe essere stato acquisito:
^Csimpleperf I cmd_record.cpp:751] Samples recorded: 1. Samples lost: 0.
Infine, è possibile utilizzare simpleperf report per esaminare lo stacktrace acquisito.
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 sopra è una catena di chiamate unificata del kernel e dello spazio utente. Ti offre una visione migliore del flusso di codice avviando la traccia dallo spazio utente fino al kernel in cui si verifica il rifiuto. Per ulteriori informazioni su simpleperf, consulta il
riferimento ai comandi eseguibili di Simpleperf
Passare alla modalità permissiva
L'applicazione di SELinux può essere disattivata con adb nelle build userdebug o eng. Per farlo, devi prima passare ad ADB come root eseguendo adb root. Quindi, per disattivare l'applicazione di SELinux, esegui:
adb shell setenforce 0
Oppure nella riga di comando del kernel (durante l'avvio iniziale del dispositivo):
androidboot.selinux=permissiveandroidboot.selinux=enforcing
Oppure tramite bootconfig in Android 12:
androidboot.selinux=permissiveandroidboot.selinux=enforcing
Utilizzare audit2allow
Lo strumento audit2allow prende i rifiuti dmesg e li converte nelle istruzioni della policy SELinux corrispondenti. Pertanto, può accelerare notevolmente lo sviluppo di SELinux.
Per utilizzarlo, esegui:
adb pull /sys/fs/selinux/policyadb logcat -b events -d | audit2allow -p policy
Tuttavia, è necessario prestare attenzione a esaminare ogni potenziale aggiunta per le autorizzazioni eccessive. Ad esempio, se fornisci a audit2allow il rifiuto rmt_storage mostrato in precedenza, viene generata la seguente istruzione della policy SELinux suggerita:
#============= shell ==============
allow shell kernel:security setenforce;
#============= rmt ==============
allow rmt kmem_device:chr_file { read write };
In questo modo, rmt avrebbe la possibilità di scrivere la memoria del kernel, una grave falla di sicurezza. Spesso le istruzioni audit2allow sono solo un punto di partenza. Dopo aver utilizzato queste istruzioni, potrebbe essere necessario modificare il dominio di origine e l'etichetta della destinazione, nonché incorporare le macro appropriate, per ottenere una policy valida. A volte il rifiuto esaminato non deve comportare alcuna modifica della policy; al contrario, l'app che causa il problema deve essere modificata.