Escrevendo a política do 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 em 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, como escrever uma política específica do dispositivo e algumas das armadilhas a serem evitadas ao longo do caminho.

Exibição do dispositivo

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

Executar no 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 no 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 , make bootimage e atualize a nova imagem de inicialização.

Depois disso, confirme o modo permissivo com:

adb shell getenforce

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

Aplicar antecipadamente

No modo de imposição, as negações são registradas e aplicadas. É uma prática recomendada colocar seu dispositivo no modo de imposição o mais cedo possível. Esperar para criar e aplicar uma política específica do dispositivo geralmente resulta em um produto com erros e uma experiência ruim para o usuário. Comece cedo o suficiente para participar do dogfooding e garantir a cobertura total do teste 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 arquivar bugs em relação a comportamentos que não devem ser permitidos.

Remover ou excluir a política existente

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

Lidar com negações de serviços principais

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 de nunca permitir, mas mesmo quando não explicitamente não permitidas, a prática recomendada é fornecer um rótulo específico.

Rotule novos serviços e resolva negações

Os serviços iniciados por init precisam ser 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.

O serviço é iniciado no 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 com base nas operações específicas realizadas por esse 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 apenas a usa para informar a redação de políticas. Não basta copiar a saída.

Voltar para o modo de imposição

Não há problema em solucionar problemas no modo permissivo, mas volte para o modo de imposiçã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 do dispositivo.

Uso excessivo de negação

A regra de exemplo 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 é trivial para 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á de ter acesso a essa 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 em compilações de produção nem 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 de tamanho de política

Caracterizando as políticas do SEAndroid na natureza 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 mais de 20% quase certamente contêm domínios privilegiados e políticas mortas.

Política desnecessariamente grande:

  • Dá um duplo golpe na memória, pois a política fica no ramdisk e também é carregada na memória do kernel.
  • Desperdiça espaço em disco exigindo 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 no dispositivo. Uma reescrita da política gerou 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 a auditoria de segurança.

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

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

Conceder o recurso dac_override

Uma negação dac_override significa que o processo ofensivo está tentando acessar um arquivo com as permissões incorretas de usuário/grupo/mundo do unix. A solução adequada é quase nunca conceder a permissão dac_override . Em vez disso , altere as permissões do unix no arquivo ou processo . Alguns domínios como init , vold e installd genuinamente 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 detalhada.