Consulte esta página para se familiarizar com os conceitos do SELinux.
Controle de acesso obrigatório
O Security Enhanced Linux (SELinux) é um sistema de controle de acesso obrigatório (MAC, na sigla em inglês) para o sistema operacional Linux. Como um sistema MAC, ele é diferente do sistema de controle de acesso discricionário (DAC) conhecido do Linux. Em um sistema DAC, existe um conceito de propriedade, em que o proprietário de um recurso específico controla as permissões de acesso associadas a ele. Isso geralmente é grosseiro e está sujeito a escalonamento de privilégios não intencional. No entanto, um sistema MAC consulta uma autoridade central para tomar uma decisão sobre todas as tentativas de acesso.
O SELinux foi implementado como parte do framework do módulo de segurança do Linux (LSM, na sigla em inglês), que reconhece vários objetos do kernel e ações sensíveis realizadas neles. No ponto em que cada uma dessas ações seria realizada, uma função de gancho de LSM é chamada para determinar se a ação precisa ser permitida com base nas informações armazenadas em um objeto de segurança opaco. O SELinux fornece uma implementação para esses hooks e gerenciamento desses objetos de segurança, que se combinam com a própria política para determinar as decisões de acesso.
Junto com outras medidas de segurança do Android, a política de controle de acesso limita bastante os possíveis danos de máquinas e contas comprometidas. O uso de ferramentas como os controles de acesso discricionário e obrigatório do Android oferece uma estrutura para garantir que o software seja executado apenas no nível mínimo de privilégio. Isso mitiga os efeitos dos ataques e reduz a probabilidade de processos errôneos sobrescreverem ou até mesmo transmitirem dados.
No Android 4.3 e versões mais recentes, o SELinux oferece um controle de acesso obrigatório (MAC) que abrange os ambientes tradicionais de controle de acesso discricionário (DAC, na sigla em inglês). Por exemplo, o software normalmente precisa ser executado como a conta de usuário raiz para gravar em dispositivos de bloco brutos. Em um ambiente Linux tradicional baseado em DAC, se o usuário raiz for comprometido, ele poderá gravar em todos os dispositivos de bloco bruto. No entanto, o SELinux pode ser usado para rotular esses dispositivos para que o processo atribuído ao privilégio raiz possa gravar apenas aqueles especificados na política associada. Dessa forma, o processo não pode substituir dados e configurações do sistema fora do dispositivo de bloco bruto específico.
Consulte Casos de uso para mais exemplos de ameaças e maneiras de lidar com elas no SELinux.
Níveis de restrição
O SELinux pode ser implementado em vários modos:
- Permissive: a política de segurança do SELinux não é aplicada, apenas registrada.
- Aplicação: a política de segurança é aplicada e registrada. As falhas aparecem como erros EPERM.
Essa escolha é binária e determina se a política toma medidas ou apenas permite que você colete possíveis falhas. A permissão é especialmente útil durante a implementação.
Tipos, atributos e regras
O Android depende do componente de aplicação de tipo (TE) do SELinux para a
política. Isso significa que todos os objetos (como arquivo, processo ou soquete) têm um
tipo associado a eles. Por exemplo, por padrão, um app
tem o tipo untrusted_app
. Para um processo, o tipo também é
conhecido como domínio. É possível anotar um tipo com um ou
vários atributos. Os atributos são úteis para se referir a vários tipos
ao mesmo tempo.
Os objetos são mapeados para classes, como um arquivo, um diretório, um link simbólico ou um soquete, e os diferentes tipos de acesso
para cada classe são representados por permissões.
Por exemplo, a permissão open
existe para a classe
file
. Embora os tipos e atributos sejam atualizados regularmente como parte da
política do SELinux do Android, as permissões e classes são definidas de forma estática e
raramente atualizadas como parte de uma nova versão do Linux.
Uma regra de política tem o seguinte formato:
allow source target:class permissions;
em que:
- Origem: o tipo (ou atributo) do sujeito da regra. Quem está solicitando o acesso?
- Destino: o tipo (ou atributo) do objeto. A que o acesso está sendo solicitado?
- Classe: o tipo de objeto (por exemplo, arquivo, soquete) que está sendo acessado.
- Permissões: a operação (ou conjunto de operações) (por exemplo, leitura, gravação) que está sendo realizada.
Um exemplo de regra é:
allow untrusted_app app_data_file:file { read write };
Isso indica que os apps podem ler e gravar arquivos marcados como
app_data_file
. Há outros tipos de apps. Por
exemplo, isolated_app
é usado para serviços de app com
isolatedProcess=true
no manifesto. Em vez de repetir a
regra para os dois tipos, o Android usa um atributo chamado appdomain
para todos os tipos que abrangem apps:
# 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 uma regra é gravada e especifica um nome de atributo, esse nome é expandido automaticamente para a lista de domínios ou tipos associados ao atributo. Alguns atributos importantes são:
domain
: atributo associado a todos os tipos de processo.file_type
: atributo associado a todos os tipos de arquivo.
Macros
Para o acesso a arquivos em particular, há muitos tipos de permissão a
considerar. Por exemplo, a permissão read
não é suficiente para abrir o
arquivo ou chamar stat
nele. Para simplificar a definição de regras, o Android
oferece um conjunto de macros para lidar com os casos mais comuns. Por exemplo, para
incluir as permissões ausentes, como open
, a regra acima
pode ser reescrita como:
allow appdomain app_data_file:file rw_file_perms;
Consulte os arquivos global_macros
e te_macros
para mais exemplos de macros úteis. As macros devem ser usadas sempre que possível
para reduzir a probabilidade de falhas devido a negações de
permissões relacionadas.
Depois que um tipo é definido, ele precisa ser associado ao arquivo ou processo que representa. Consulte Como implementar o SELinux para mais detalhes sobre como essa associação é feita. Para mais informações sobre regras, consulte o notebook do SELinux (em inglês).
Contexto e categorias de segurança
Ao depurar políticas do SELinux ou rotular arquivos (usando
file_contexts
ou ls -Z
), você pode encontrar
um contexto de segurança (também conhecido como rótulo). Por
exemplo:
u:r:untrusted_app:s0:c15,c256,c513,c768
. Um contexto de segurança tem o formato:
user:role:type:sensitivity[:categories]
. Geralmente, é possível ignorar os campos
user
, role
e sensitivity
de um
contexto (consulte Especificidade). O campo type
é explicado na seção anterior. categories
fazem parte do
suporte a segurança multinível (MLS)
no SELinux. No Android 12 e versões mais recentes, as categorias são usadas para:
- Isolar os dados do app do acesso por outro app,
- Isolar os dados do app de um usuário físico para outro.
Especificidade
O Android não usa todos os recursos fornecidos pelo SELinux. Ao ler documentação externa, considere estes pontos:
- A maioria das políticas do AOSP são definidas usando a linguagem de política do kernel. Há algumas exceções para o uso da linguagem intermediária comum (CIL).
- Os usuários do SELinux não são usados. O único usuário definido é
u
. Quando necessário, os usuários físicos são representados usando o campo de categorias de um contexto de segurança. - Os papéis do SELinux e o controle de acesso baseado em função (RBAC) não são usados. Duas funções padrão são definidas e usadas:
r
para sujeitos eobject_r
para objetos. - As suscetibilidades do SELinux não são usadas. A sensibilidade padrão de
s0
sempre é definida. - Os booleans do SELinux não são usados. Quando a política é criada para um dispositivo, ela não depende do estado do dispositivo. Isso simplifica a auditoria e a depuração de políticas.