Escrevendo Política SELinux

O Android Open Source Project (AOSP) fornece uma política de base sólida para os aplicativos e serviços que são comuns a todos os dispositivos Android. Os colaboradores do AOSP refinam regularmente esta política. Espera-se que a política principal represente cerca de 90 a 95% da política final no dispositivo, com personalizações específicas do dispositivo representando os 5 a 10% restantes. Este artigo se concentra nessas personalizações específicas do dispositivo, em como escrever políticas específicas do dispositivo e em algumas das armadilhas a serem evitadas ao longo do caminho.

Apresentação do dispositivo

Ao escrever uma política específica do dispositivo, siga estas etapas.

Execute em modo permissivo

Quando um dispositivo está no modo permissivo , as negações são registradas, mas não aplicadas. O modo permissivo é importante por dois motivos:

  • O modo permissivo garante que a ativação da política não atrase outras tarefas iniciais de ativação do dispositivo.
  • Uma negação forçada pode mascarar outras negações. Por exemplo, o acesso a arquivos normalmente envolve uma pesquisa de diretório, abertura de arquivo e leitura de arquivo. No modo de imposição, ocorreria apenas a negação da pesquisa de diretório. O modo permissivo garante que todas as negações sejam vistas.

A maneira mais simples de colocar um dispositivo em modo permissivo é usando a linha de comando do kernel . Isso pode ser adicionado ao arquivo BoardConfig.mk do dispositivo: platform/device/<vendor>/<target>/BoardConfig.mk . Depois de modificar a linha de comando, execute make clean , então make bootimage e atualize a nova imagem de inicialização.

Depois disso, confirme o modo permissivo com:

adb shell getenforce

Duas semanas é um período de tempo razoável para estar no modo permissivo global. Depois de resolver a maioria das negações, volte para o modo de aplicação e resolva os bugs à medida que eles surgirem. Domínios que ainda produzem negações ou serviços ainda em desenvolvimento intenso podem ser temporariamente colocados no modo permissivo, mas mova-os de volta para o modo de aplicação o mais rápido possível.

Aplicar antecipadamente

No modo de aplicação, as negações são registradas e aplicadas. É uma prática recomendada colocar seu dispositivo no modo de aplicação o mais cedo possível. Esperar para criar e aplicar políticas específicas do dispositivo geralmente resulta em um produto com bugs e em uma experiência ruim para o usuário. Comece com antecedência suficiente para participar do dogfooding e garanta a cobertura completa dos testes de funcionalidade no uso no mundo real. Começar cedo garante que as preocupações de segurança informem as decisões de design. Por outro lado, conceder permissões com base apenas em negações observadas é uma abordagem insegura. Use esse tempo para realizar uma auditoria de segurança do dispositivo e registrar bugs contra comportamentos que não deveriam ser permitidos.

Remover ou excluir a política existente

Existem vários bons motivos para criar uma política específica do dispositivo do zero em um novo dispositivo, que incluem:

Abordar negações de serviços essenciais

As negações geradas pelos serviços principais são normalmente tratadas pela rotulagem de arquivos. Por exemplo:

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

é completamente resolvido rotulando adequadamente /dev/kgsl-3d0 . Neste exemplo, tcontext é device . Isso representa um contexto padrão onde tudo em /dev recebe o rótulo “ dispositivo ”, a menos que um rótulo mais específico seja atribuído. Simplesmente aceitar a saída de audit2allow aqui resultaria em uma regra incorreta e excessivamente permissiva.

Para resolver esse tipo de problema, dê ao arquivo um rótulo mais específico, que neste caso é gpu_device . Nenhuma permissão adicional é necessária, pois o mediaserver já possui as permissões necessárias na política principal para acessar o gpu_device.

Outros arquivos específicos do dispositivo que devem ser rotulados com tipos predefinidos na política principal:

Em geral, conceder permissões a rótulos padrão é errado. Muitas dessas permissões não são permitidas pelas regras neverallow , mas mesmo quando não são explicitamente proibidas, a prática recomendada é fornecer um rótulo específico.

Rotular novos serviços e lidar com negações

Os serviços iniciados pelo Init são necessários para serem executados em seus próprios domínios SELinux. O exemplo a seguir coloca o serviço “foo” em seu próprio domínio SELinux e concede permissões a ele.

O serviço é iniciado no init. device .rc arquivo init. device .rc como:

service foo /system/bin/foo
    class core
  1. Crie um novo domínio "foo"

    Crie o arquivo device/ manufacturer / device-name /sepolicy/foo.te com o seguinte conteúdo:

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

    Este é o modelo inicial para o domínio foo SELinux, ao qual você pode adicionar regras baseadas nas operações específicas executadas por aquele executável.

  2. Etiqueta /system/bin/foo

    Adicione o seguinte a device/ manufacturer / device-name /sepolicy/file_contexts :

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

    Isso garante que o executável esteja devidamente rotulado para que o SELinux execute o serviço no domínio apropriado.

  3. Crie e atualize as imagens de inicialização e do sistema.
  4. Refine as regras do SELinux para o domínio.

    Use negações para determinar as permissões necessárias. A ferramenta audit2allow fornece boas diretrizes, mas só a utiliza para informar a redação de políticas. Não basta copiar a saída.

Voltar para o modo de aplicação

Não há problema em solucionar problemas no modo permissivo, mas volte para o modo de aplicação o mais cedo possível e tente permanecer lá.

Erros comuns

Aqui estão algumas soluções para erros comuns que acontecem ao escrever políticas específicas para dispositivos.

Uso excessivo de negação

O exemplo de regra a seguir é como trancar a porta da frente, mas deixar as janelas abertas:

allow { domain -untrusted_app } scary_debug_device:chr_file rw_file_perms

A intenção é clara: todos, exceto aplicativos de terceiros, podem ter acesso ao dispositivo de depuração.

A regra é falha em alguns aspectos. A exclusão de untrusted_app é fácil de contornar porque todos os aplicativos podem, opcionalmente, executar serviços no domínio isolated_app . Da mesma forma, se novos domínios para aplicativos de terceiros forem adicionados ao AOSP, eles também terão acesso a scary_debug_device . A regra é excessivamente permissiva. A maioria dos domínios não se beneficiará com o acesso a esta ferramenta de depuração. A regra deveria ter sido escrita para permitir apenas os domínios que requerem acesso.

Recursos de depuração em produção

Os recursos de depuração não devem estar presentes nas compilações de produção nem em sua política.

A alternativa mais simples é permitir apenas o recurso de depuração quando o SELinux estiver desabilitado em compilações eng/userdebug, como adb root e adb shell setenforce 0 .

Outra alternativa segura é incluir permissões de depuração em uma instrução userdebug_or_eng .

Explosão do tamanho da política

Caracterizar as políticas SEAndroid in the Wild descreve uma tendência preocupante no crescimento das personalizações de políticas de dispositivos. A política específica do dispositivo deve representar de 5 a 10% da política geral executada em um dispositivo. As personalizações na faixa de 20% ou mais quase certamente contêm domínios sobreprivilegiados e políticas inativas.

Política desnecessariamente grande:

  • Leva um golpe duplo na memória, pois a política fica no disco RAM e também é carregada na memória do kernel.
  • Desperdiça espaço em disco ao necessitar de uma imagem de inicialização maior.
  • Afeta os tempos de pesquisa da política de tempo de execução.

O exemplo a seguir mostra dois dispositivos em que a política específica do fabricante compreendia 50% e 40% da política do dispositivo. Uma reescrita da política resultou em melhorias substanciais de segurança sem perda de funcionalidade, conforme mostrado abaixo. (Os dispositivos AOSP Shamu e Flounder estão incluídos para comparação.)

Figura 1: Comparação do tamanho da política específica do dispositivo após auditoria de segurança.

Figura 1 . Comparação do tamanho da política específica do dispositivo após auditoria de segurança.

Em ambos os casos, a política foi drasticamente reduzida tanto em tamanho como em número de permissões. A diminuição no tamanho da política deve-se quase inteiramente à remoção de permissões desnecessárias, muitas das quais eram prováveis ​​regras geradas por audit2allow que foram adicionadas indiscriminadamente à política. Domínios mortos também foram um problema para ambos os dispositivos.

Concedendo o recurso dac_override

Uma negação dac_override significa que o processo infrator está tentando acessar um arquivo com permissões de usuário/grupo/mundo Unix incorretas. A solução adequada quase nunca é conceder a permissão dac_override . Em vez disso , altere as permissões Unix no arquivo ou processo . Alguns domínios como init , vold e installd realmente precisam da capacidade de substituir permissões de arquivo unix para acessar arquivos de outros processos. Veja o blog de Dan Walsh para uma explicação mais aprofundada.