Scrivere la politica di 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 di base rappresenterà circa il 90–95% della policy finale sul dispositivo, mentre le personalizzazioni specifiche del dispositivo costituiranno il restante 5–10%. Questo articolo è incentrato su queste personalizzazioni specifiche del dispositivo, su come scrivere criteri specifici del dispositivo e su alcune delle insidie ​​da evitare lungo il percorso.

Visualizzazione del dispositivo

Durante la scrittura di criteri specifici del 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 richiamo dei criteri non ritardi altre attività di richiamo del dispositivo in anticipo.
  • Una negazione forzata può mascherare altre negazioni. Ad esempio, l'accesso ai file in genere comporta una ricerca nella directory, l'apertura del file, quindi la lettura del file. In modalità di applicazione, si verificherebbe solo la negazione 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 è usare 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, eseguire make clean , quindi make bootimage e flashare la nuova immagine di avvio.

Successivamente, conferma la modalità permissiva con:

adb shell getenforce

Due settimane sono una quantità ragionevole di tempo per essere in modalità permissiva globale. Dopo aver affrontato la maggior parte delle smentite, torna alla modalità di applicazione e risolvi i bug man mano che si presentano. I domini che continuano a produrre rifiuti o servizi ancora in fase di sviluppo intenso possono essere temporaneamente messi in modalità permissiva, ma riportarli alla modalità di applicazione il prima possibile.

Applicare in anticipo

In modalità di applicazione, i rifiuti vengono registrati e applicati. È consigliabile portare il dispositivo in modalità di esecuzione il prima possibile. L'attesa per la creazione e l'applicazione di criteri specifici del dispositivo spesso si traduce in un prodotto difettoso e in un'esperienza utente negativa. Inizia abbastanza presto per partecipare al dogfooding e assicurati la copertura completa del test della funzionalità nell'utilizzo nel mondo reale. L'avvio anticipato garantisce che i problemi di sicurezza informino le decisioni di progettazione. Al contrario, la concessione di autorizzazioni basate esclusivamente sui rifiuti osservati è un approccio non sicuro. Utilizzare questo tempo per eseguire un controllo di sicurezza del dispositivo e segnalare i bug rispetto a comportamenti che non dovrebbero essere consentiti.

Rimuovere o eliminare la politica esistente

Esistono diversi buoni motivi per creare da zero criteri specifici del dispositivo su un nuovo dispositivo, tra cui:

Rifiutare i servizi di base

I rifiuti generati dai servizi principali vengono in genere affrontati 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. La semplice accettazione dell'output di audit2allow qui comporterebbe una regola errata ed eccessivamente permissiva.

Per risolvere questo tipo di problema, assegna 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 di base per accedere a gpu_device.

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

In generale, la concessione delle autorizzazioni alle etichette predefinite è sbagliata. Molte di queste autorizzazioni non sono consentite dalle regole Neverallow , ma anche quando non sono esplicitamente disabilitate, la procedura consigliata consiste nel fornire un'etichetta specifica.

Etichetta nuovi servizi e indirizzi negati

I servizi avviati 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 nell'init del nostro dispositivo 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 i seguenti contenuti:

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

    Questo è il template iniziale per il dominio foo SELinux, a cui puoi aggiungere regole basate sulle operazioni specifiche eseguite da quell'eseguibile.

  2. Etichetta /system/bin/foo

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

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

    Questo assicura che l'eseguibile sia etichettato correttamente in modo che SELinux esegua il servizio nel dominio appropriato.

  3. Crea e flasha le immagini di avvio e di sistema.
  4. Affina le regole di SELinux per il dominio.

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

Torna alla modalità di applicazione

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

Errori comuni

Ecco alcune soluzioni per gli errori comuni che si verificano durante la scrittura di criteri specifici del dispositivo.

Uso eccessivo della negazione

La seguente regola di esempio è come bloccare 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 potrebbero avere accesso al dispositivo di debug.

La regola è viziata in alcuni modi. L'esclusione di untrusted_app è banale da aggirare 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 regola è 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.

Funzioni di debug in produzione

Le funzionalità di debug non dovrebbero essere presenti nelle build di produzione né dovrebbero essere le relative 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 le autorizzazioni di debug in un'istruzione userdebug_or_eng .

Esplosione delle dimensioni delle politiche

La caratterizzazione di SEAndroid Policies in the Wild descrive una tendenza preoccupante nella crescita delle personalizzazioni delle policy dei dispositivi. Il criterio specifico del dispositivo dovrebbe rappresentare il 5-10% del criterio complessivo in esecuzione su un dispositivo. Le personalizzazioni nell'intervallo 20%+ contengono quasi sicuramente domini privilegiati e criteri morti.

Polizza inutilmente grande:

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

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

Figura 1: confronto delle dimensioni dei criteri specifici del dispositivo dopo il controllo di sicurezza.

Figura 1 . Confronto delle dimensioni dei criteri specifici del dispositivo dopo il controllo di sicurezza.

In entrambi i casi, la polizza è stata drasticamente ridotta sia nelle dimensioni che nel numero di autorizzazioni. La diminuzione delle dimensioni della policy è quasi interamente dovuta alla rimozione di autorizzazioni non necessarie, molte delle quali erano probabilmente regole generate da audit2allow che sono state aggiunte indiscriminatamente alla policy. Anche i domini morti erano un problema per entrambi i dispositivi.

Concessione della capacità dac_override

Un dac_override denial 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 . Cambia invece le autorizzazioni Unix sul file o sul processo . Alcuni domini come init , vold e installd hanno davvero bisogno della possibilità di ignorare i permessi dei file unix per accedere ai file di altri processi. Vedi il blog di Dan Walsh per una spiegazione più approfondita.