Convalida di SELinux

Android incoraggia vivamente gli OEM a testare a fondo le loro implementazioni SELinux. Poiché i produttori implementano SELinux, dovrebbero prima applicare la nuova politica a un pool di test di dispositivi.

Dopo aver applicato una nuova politica, assicurati che SELinux sia in esecuzione nella modalità corretta sul dispositivo eseguendo il comando getenforce .

Questo stampa la modalità SELinux globale: o Enforcing o Permissive. Per determinare la modalità SELinux per ogni 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/ .

Negazioni di lettura

Verifica la presenza di errori, che vengono instradati come registri eventi a dmesg e logcat e sono visualizzabili localmente sul dispositivo. I produttori dovrebbero esaminare l'output di SELinux per dmesg su questi dispositivi e perfezionare le impostazioni prima del rilascio pubblico in modalità permissiva e l'eventuale passaggio alla modalità di applicazione. I messaggi di log di SELinux contengono avc: e quindi possono 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 .

Con questo output, i produttori possono identificare prontamente 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 non riuscirebbero 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 alla tclass alla fine ( unix_stream_socket ), ti dice più o meno 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 si tratta di qualcosa in esecuzione come shell.
  • Il tcontext (u:r:netd:s0) ti dice il contesto della destinazione dell'azione. In questo caso, si tratta di un unix_stream_socket di proprietà di netd .
  • Il comm="ping" in alto fornisce un ulteriore suggerimento su cosa era in esecuzione al momento in cui è stata generata la negazione. In questo caso, è un buon suggerimento.

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 viene evidenziata tra parentesi, read write o setenforce .
  • Attore - La scontext (contesto di origine) rappresenta l'attore, in questo caso il demone rmt_storage .
  • Oggetto - La tcontext (contesto di destinazione) rappresenta l'oggetto su cui si agisce, in questo caso kmem.
  • Risultato - La tclass (classe target) indica il tipo di oggetto su cui si agisce, in questo caso un chr_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 rifiuto. Spesso è utile raccogliere la catena di chiamate, inclusi kernel e userspace, per capire meglio perché si è verificata la negazione.

I kernel recenti definiscono un tracepoint chiamato avc:selinux_audited . Usa Android simpleperf per abilitare questo tracepoint e acquisire la callchain.

Configurazione supportata

  • Kernel Linux >= 5.10, in particolare sono supportati i rami principali del kernel comune di Android 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 cherry pick commit dd81662 e 30969bc .
  • Dovrebbe essere possibile riprodurre l'evento di cui si esegue il debug. Gli eventi all'avvio non sono supportati utilizzando 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 rifiuto. Dopodiché, la registrazione dovrebbe 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, il simpleperf report può essere utilizzato per ispezionare 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 la negazione. Per ulteriori informazioni su simpleperf , vedere il riferimento ai comandi eseguibili Simpleperf

Passaggio a permissivo

L'applicazione di SELinux può essere disabilitata tramite ADB su userdebug o build eng. Per fare ciò, prima cambia ADB su 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

Oppure tramite bootconfig in Android 12:

androidboot.selinux=permissive
androidboot.selinux=enforcing

Utilizzo di audit2allow

Lo strumento audit2allow prende le negazioni di dmesg e le converte nelle corrispondenti dichiarazioni di policy di SELinux. In quanto tale, può accelerare notevolmente lo sviluppo di SELinux.

Per usarlo, esegui:

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

Tuttavia, è necessario prestare attenzione nell'esaminare ogni potenziale aggiunta per il superamento delle autorizzazioni. Ad esempio, alimentando audit2allow rmt_storage denial mostrato in precedenza risulta nella 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 capacità di scrivere la memoria del kernel, un'evidente falla nella sicurezza. Spesso le dichiarazioni di 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, nonché incorporare le macro appropriate, per arrivare a una buona politica. A volte il rifiuto in esame non dovrebbe comportare alcun cambiamento di politica; piuttosto l'applicazione incriminata dovrebbe essere modificata.