Scrivere la politica SELinux

L'Android Open Source Project (AOSP) fornisce una solida policy di base per le applicazioni e i servizi comuni a tutti i dispositivi Android. I contributori di AOSP perfezionano regolarmente questa politica. Si prevede che la policy principale costituirà circa il 90-95% della policy finale sul dispositivo, mentre le personalizzazioni specifiche del dispositivo costituiranno il restante 5-10%. Questo articolo si concentra su queste personalizzazioni specifiche del dispositivo, su come scrivere policy specifiche per il dispositivo e su alcune delle insidie ​​da evitare lungo il percorso.

Visualizzazione del dispositivo

Durante la scrittura della policy specifica per il dispositivo, attenersi alla seguente procedura.

Esegui in modalità permissiva

Quando un dispositivo è in modalità permissiva , i rifiuti vengono registrati ma non applicati. La modalità permissiva è importante per due motivi:

  • La modalità permissiva garantisce che il ripristino dei criteri non ritardi altre attività di ripristino anticipato del dispositivo.
  • Un rifiuto forzato può mascherare altri dinieghi. Ad esempio, l'accesso ai file comporta in genere una ricerca nella directory, l'apertura del file e quindi la lettura del file. Nella modalità di applicazione, si verificherebbe solo il rifiuto della ricerca nella directory. La modalità permissiva garantisce che tutti i rifiuti vengano visualizzati.

Il modo più semplice per mettere un dispositivo in modalità permissiva è utilizzare la riga di comando del kernel . Questo può essere aggiunto al file BoardConfig.mk del dispositivo: platform/device/<vendor>/<target>/BoardConfig.mk . Dopo aver modificato la riga di comando, esegui make clean , quindi make bootimage e esegui il flashing della nuova immagine di avvio.

Successivamente, conferma la modalità permissiva con:

adb shell getenforce

Due settimane sono un periodo di tempo ragionevole per essere in modalità permissiva globale. Dopo aver risolto la maggior parte dei rifiuti, torna alla modalità di applicazione e risolvi i bug non appena si presentano. I domini che ancora producono rifiuti o servizi ancora in fase di sviluppo intenso possono essere temporaneamente messi in modalità permissiva, ma riportali in modalità di applicazione il prima possibile.

Applicare in anticipo

Nella modalità di applicazione, i rifiuti vengono registrati e applicati. È consigliabile mettere il dispositivo in modalità di applicazione delle norme il prima possibile. Aspettare di creare e applicare policy specifiche per il dispositivo spesso si traduce in un prodotto difettoso e in un'esperienza utente negativa. Inizia abbastanza presto per partecipare alla sperimentazione e garantire una copertura completa dei test delle funzionalità nell'utilizzo nel mondo reale. Iniziare in anticipo garantisce che i problemi di sicurezza informino le decisioni di progettazione. Al contrario, concedere autorizzazioni basate esclusivamente sui rifiuti osservati è un approccio non sicuro. Utilizzare questo tempo per eseguire un controllo di sicurezza del dispositivo e segnalare bug rispetto a comportamenti che non dovrebbero essere consentiti.

Rimuovere o eliminare la policy esistente

Esistono numerosi buoni motivi per creare da zero policy specifiche per il dispositivo su un nuovo dispositivo, tra cui:

Affrontare le negazioni dei servizi principali

I rifiuti generati dai servizi principali vengono generalmente risolti mediante l'etichettatura dei file. Per esempio:

avc: denied { open } for pid=1003 comm=”mediaserver” path="/dev/kgsl-3d0”
dev="tmpfs" scontext=u:r:mediaserver:s0 tcontext=u:object_r:device:s0
tclass=chr_file permissive=1
avc: denied { read write } for pid=1003 name="kgsl-3d0" dev="tmpfs"
scontext=u:r:mediaserver:s0
tcontext=u:object_r:device:s0 tclass=chr_file permissive=1

viene completamente risolto etichettando correttamente /dev/kgsl-3d0 . In questo esempio, tcontext è device . Questo rappresenta un contesto predefinito in cui tutto in /dev riceve l'etichetta " dispositivo " a meno che non venga assegnata un'etichetta più specifica. Accettare semplicemente l'output di audit2allow in questo caso comporterebbe una regola errata ed eccessivamente permissiva.

Per risolvere questo tipo di problema, dai al file un'etichetta più specifica, che in questo caso è gpu_device . Non sono necessarie ulteriori autorizzazioni poiché il mediaserver dispone già delle autorizzazioni necessarie nella policy principale per accedere a gpu_device.

Altri file specifici del dispositivo che dovrebbero essere etichettati con i tipi predefiniti nella policy principale:

In generale, concedere autorizzazioni alle etichette predefinite è sbagliato. Molte di queste autorizzazioni non sono consentite dalle regole neverallow , ma anche quando non sono esplicitamente vietate, la procedura migliore consiste nel fornire un'etichetta specifica.

Etichettare i nuovi servizi e indirizzare i rifiuti

I servizi lanciati da Init devono essere eseguiti nei propri domini SELinux. L'esempio seguente inserisce il servizio "foo" nel proprio dominio SELinux e gli concede le autorizzazioni.

Il servizio viene lanciato init. device .rc file init. device .rc come:

service foo /system/bin/foo
    class core
  1. Crea un nuovo dominio "foo"

    Crea il file device/ manufacturer / device-name /sepolicy/foo.te con il seguente contenuto:

    # foo service
    type foo, domain;
    type foo_exec, exec_type, file_type;
    
    init_daemon_domain(foo)
    

    Questo è il modello iniziale per il dominio foo SELinux, al quale è possibile aggiungere regole basate sulle operazioni specifiche eseguite da quell'eseguibile.

  2. Etichetta /system/bin/foo

    Aggiungere quanto segue a device/ manufacturer / device-name /sepolicy/file_contexts :

    /system/bin/foo   u:object_r:foo_exec:s0
    

    Ciò garantisce che l'eseguibile sia etichettato correttamente in modo che SELinux esegua il servizio nel dominio corretto.

  3. Costruisci e esegui il flashing delle immagini di avvio e di sistema.
  4. Perfezionare le regole SELinux per il dominio.

    Utilizzare i rifiuti per determinare le autorizzazioni necessarie. Lo strumento audit2allow fornisce buone linee guida, ma utilizzarlo solo per informare la stesura delle politiche. Non limitarti a copiare l'output.

Torna alla modalità di applicazione

È possibile risolvere i problemi in modalità permissiva, ma torna alla modalità di applicazione il prima possibile e cerca di rimanere lì.

Errori comuni

Di seguito sono riportate alcune soluzioni agli errori comuni che si verificano durante la scrittura di policy specifiche per il dispositivo.

Uso eccessivo della negazione

La seguente regola di esempio è come chiudere la porta d'ingresso ma lasciare le finestre aperte:

allow { domain -untrusted_app } scary_debug_device:chr_file rw_file_perms

L'intento è chiaro: tutti tranne le app di terze parti possono avere accesso al dispositivo di debug.

La regola è viziata in alcuni modi. È semplice aggirare l'esclusione di untrusted_app perché tutte le app possono facoltativamente eseguire servizi nel dominio isolated_app . Allo stesso modo, se nuovi domini per app di terze parti vengono aggiunti ad AOSP, avranno accesso anche a scary_debug_device . La norma è eccessivamente permissiva. La maggior parte dei domini non trarrà vantaggio dall'accesso a questo strumento di debug. La regola avrebbe dovuto essere scritta per consentire solo i domini che richiedono l'accesso.

Funzionalità di debug in produzione

Le funzionalità di debug non dovrebbero essere presenti nelle build di produzione né dovrebbero esserlo le loro policy.

L'alternativa più semplice è consentire la funzionalità di debug solo quando SELinux è disabilitato su build eng/userdebug, come adb root e adb shell setenforce 0 .

Un'altra alternativa sicura è racchiudere i permessi di debug in un'istruzione userdebug_or_eng .

Esplosione delle dimensioni delle politiche

Characterizing SEAndroid Policies in the Wild descrive una tendenza preoccupante nella crescita delle personalizzazioni delle policy dei dispositivi. La policy specifica per il dispositivo dovrebbe rappresentare il 5-10% della policy complessiva in esecuzione su un dispositivo. Le personalizzazioni nell'intervallo superiore al 20% quasi certamente contengono domini eccessivamente privilegiati e policy inattive.

Politica inutilmente ampia:

  • Subisce un doppio impatto sulla memoria poiché la policy si trova nel ramdisk e viene anche caricata nella memoria del kernel.
  • Spreca spazio su disco rendendo necessaria un'immagine di avvio più grande.
  • Influisce sui tempi di ricerca dei criteri di runtime.

L'esempio seguente mostra due dispositivi in ​​cui la policy specifica del produttore comprendeva il 50% e il 40% della policy sul dispositivo. Una riscrittura della policy ha prodotto miglioramenti sostanziali della sicurezza senza perdita di funzionalità, come mostrato di seguito. (I dispositivi AOSP Shamu e Flounder sono inclusi per confronto.)

Figura 1: Confronto delle dimensioni delle policy specifiche del dispositivo dopo il controllo di sicurezza.

Figura 1 . Confronto delle dimensioni delle policy specifiche del dispositivo dopo il controllo di sicurezza.

In entrambi i casi, la policy è stata drasticamente ridotta sia in termini di dimensioni che di numero di autorizzazioni. La riduzione delle dimensioni della policy è quasi interamente dovuta alla rimozione di permessi non necessari, molti dei quali erano probabilmente regole generate da audit2allow aggiunte indiscriminatamente alla policy. Anche i domini morti rappresentavano un problema per entrambi i dispositivi.

Concessione della funzionalità dac_override

Un rifiuto dac_override significa che il processo incriminato sta tentando di accedere a un file con le autorizzazioni utente/gruppo/mondo unix errate. La soluzione corretta non è quasi mai quella di concedere l'autorizzazione dac_override . Modifica invece le autorizzazioni UNIX sul file o sul processo . Alcuni domini come init , vold e installd necessitano davvero della possibilità di sovrascrivere le autorizzazioni dei file Unix per accedere ai file di altri processi. Vedi il blog di Dan Walsh per una spiegazione più approfondita.