Consulta questa pagina per acquisire familiarità con i concetti di SELinux.
Controllo dell'accesso obbligatorio
Security Enhanced Linux (SELinux) è un sistema di controllo dell'accesso obbligatorio (MAC) per il sistema operativo Linux. In quanto sistema MAC, si differenzia dal sistema di controllo di accesso discrezionale (DAC) di Linux. In un sistema DAC esiste un concetto di proprietà, in base al quale un proprietario di una determinata risorsa controlla le autorizzazioni di accesso associate. In genere è grossolana e soggetta a un'escalation involontaria dei privilegi. Un sistema MAC, tuttavia, consulta un'autorità centrale per una decisione su tutti i tentativi di accesso.
SELinux è stato implementato come parte del framework Linux Security Module (LSM), che riconosce vari oggetti del kernel e azioni sensibili eseguite su di essi. Nel momento in cui viene eseguita ciascuna di queste azioni, viene chiamata una funzione hook LSM per determinare se l'azione deve essere consentita in base alle informazioni memorizzate in un oggetto di sicurezza opaco. SELinux fornisce un'implementazione per questi hook e la gestione di questi oggetti di sicurezza, che si combinano con i propri criteri per determinare le decisioni di accesso.
Insieme ad altre misure di sicurezza di Android, la norma di controllo dell'accesso di Android limita notevolmente i potenziali danni di macchine e account compromessi. L'utilizzo di strumenti come i controlli di accesso discrezionali e obbligatori di Android ti offre una struttura per garantire che il software venga eseguito solo al livello di privilegio minimo. In questo modo si attenuano gli effetti degli attacchi e si riduce la probabilità che processi errati sovrascrivano o trasmettano dati.
In Android 4.3 e versioni successive, SELinux fornisce un controllo dell'accesso obbligatorio (MAC) che copre gli ambienti di controllo dell'accesso discrezionale (DAC) tradizionali. Ad esempio, il software deve in genere essere eseguito come account utente root per scrivere su dispositivi a blocchi non elaborati. In un ambiente Linux tradizionale basato su DAC, se l'utente root viene compromesso, può scrivere su ogni dispositivo a blocchi non elaborati. Tuttavia, SELinux può essere utilizzato per etichettare questi dispositivi in modo che il processo a cui è stato assegnato il privilegio di root possa scrivere solo su quelli specificati nel criterio associato. In questo modo, la procedura non può sovrascrivere i dati e le impostazioni di sistema al di fuori del dispositivo a blocchi non elaborati specifico.
Consulta Casi d'uso per altri esempi di minacce e modi per affrontarle con SELinux.
Livelli di applicazione
SELinux può essere implementato in varie modalità:
- Permissivo: il criterio di sicurezza SELinux non viene applicato, ma solo registrato.
- Applicazione: la policy di sicurezza viene applicata e registrata. Gli errori vengono visualizzati come errori EPERM.
Questa scelta è binaria e determina se il criterio esegue un'azione o ti consente semplicemente di raccogliere potenziali errori. Permissivo è particolarmente utile durante l'implementazione.
Tipi, attributi e regole
Android si basa sul componente Type Enforcement (TE) di SELinux per le sue
norme. Ciò significa che tutti gli oggetti (ad esempio file, processi o socket) hanno un
tipo associato. Ad esempio, per impostazione predefinita, un'app
ha il tipo untrusted_app
. Per un processo, il tipo è noto anche come dominio. È possibile annotare un tipo con uno o
più attributi. Gli attributi sono utili per fare riferimento a più tipi
contemporaneamente.
Gli oggetti vengono mappati alle classi
(ad esempio, un file, una directory, un link simbolico, un socket) e i diversi tipi di accesso
per ogni classe sono rappresentati dalle autorizzazioni.
Ad esempio, l'autorizzazione open
esiste per la classe
file
. Mentre i tipi e gli attributi vengono aggiornati regolarmente nell'ambito
delle norme SELinux di Android, le autorizzazioni e le classi sono definite in modo statico e
vengono aggiornate raramente nell'ambito di una nuova release di Linux.
Una regola del criterio ha il seguente formato:
allow source target:class permissions;
dove:
- Origine: il tipo (o l'attributo) del soggetto della regola. Chi sta richiedendo l'accesso?
- Target: il tipo (o l'attributo) dell'oggetto. A cosa si riferisce l'accesso richiesto?
- Classe: il tipo di oggetto (ad esempio file, socket) a cui si accede.
- Autorizzazioni: l'operazione (o insieme di operazioni) (ad esempio, lettura, scrittura) in corso.
Un esempio di regola è:
allow untrusted_app app_data_file:file { read write };
Ciò significa che le app sono autorizzate a leggere e scrivere file etichettati
app_data_file
. Esistono altri tipi per le app. Ad esempio, isolated_app
viene utilizzato per i servizi app con isolatedProcess=true
nel manifest. Anziché ripetere la regola per entrambi i tipi, Android utilizza un attributo denominato appdomain
per tutti i tipi che coprono le app:
# Associate the attribute appdomain with the type untrusted_app. typeattribute untrusted_app appdomain; # Associate the attribute appdomain with the type isolated_app. typeattribute isolated_app appdomain; allow appdomain app_data_file:file { read write };
Quando viene scritta una regola che specifica un nome di attributo, questo nome viene espanso automaticamente all'elenco di domini o tipi associati all'attributo. Alcuni attributi importanti sono:
domain
- attributo associato a tutti i tipi di processo,file_type
: attributo associato a tutti i tipi di file.
Macro
Per l'accesso ai file in particolare, esistono molti tipi di autorizzazioni da
prendere in considerazione. Ad esempio, l'autorizzazione read
non è sufficiente per aprire il file o chiamare stat
. Per semplificare la definizione delle regole, Android
fornisce un insieme di macro per gestire i casi più comuni. Ad esempio, per includere le autorizzazioni mancanti come open
, la regola precedente potrebbe essere riscritta come:
allow appdomain app_data_file:file rw_file_perms;
Per altri esempi di macro utili, consulta i file global_macros
e te_macros
. Le macro devono essere utilizzate ogni volta che è possibile
per ridurre la probabilità di errori dovuti a negazioni delle autorizzazioni correlate.
Una volta definito un tipo, deve essere associato al file o al processo che rappresenta. Per ulteriori dettagli su come viene eseguita questa associazione, consulta Implementazione di SELinux. Per ulteriori informazioni sulle regole, consulta il notebook SELinux.
Contesto e categorie di sicurezza
Quando esegui il debug dei criteri SELinux o etichetti i file (utilizzando
file_contexts
o quando esegui ls -Z
), potresti imbatterti
in un contesto di sicurezza (noto anche come etichetta). Ad
esempio:
u:r:untrusted_app:s0:c15,c256,c513,c768
. Un contesto di sicurezza ha il formato:
user:role:type:sensitivity[:categories]
. In genere puoi ignorare i campi
user
, role
e sensitivity
di un
contesto (vedi Specificità). Il campo type
è spiegato nella sezione precedente. categories
fanno parte del supporto Multi-Level Security (MLS) in SELinux. In Android 12 e versioni successive, le categorie vengono utilizzate per:
- Isolare i dati dell'app dall'accesso di un'altra app.
- Isolare i dati delle app da un utente fisico all'altro.
Specificità
Android non utilizza tutte le funzionalità fornite da SELinux. Quando leggi la documentazione esterna, tieni presente quanto segue:
- La maggior parte dei criteri in AOSP è definita utilizzando il linguaggio dei criteri del kernel. Esistono alcune eccezioni per l'utilizzo di Common Intermediate Language (CIL).
- Gli utenti SELinux non vengono utilizzati. L'unico utente definito è
u
. Se necessario, gli utenti fisici vengono rappresentati utilizzando il campo delle categorie di un contesto di sicurezza. - I ruoli SELinux e il controllo degli accessi basato sui ruoli (RBAC) non vengono utilizzati. Vengono definiti e utilizzati due ruoli predefiniti:
r
per i soggetti eobject_r
per gli oggetti. - Le sensibilità di SELinux non vengono utilizzate. La sensibilità
s0
predefinita è sempre impostata. - I booleani SELinux non vengono utilizzati. Quando il criterio viene creato per un dispositivo, non dipende dallo stato del dispositivo. Ciò semplifica il controllo e il debug delle norme.