Gravar política do SELinux

O Android Open Source Project (AOSP) oferece uma política de base sólida para os apps e serviços comuns em todos os dispositivos Android. Os colaboradores do AOSP refinam essa política regularmente. Espera-se que a política principal compõe 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, em como escrever políticas específicas do dispositivo e em algumas armadilhas a serem evitadas.

Inicializaçã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 inicialização da política não atrase outras tarefas iniciais de ativação do dispositivo.
  • Uma negação aplicada pode mascarar outras. Por exemplo, o acesso a arquivos normalmente envolve uma pesquisa de diretório, a abertura do arquivo e a leitura do arquivo. No modo de aplicação, apenas a negação da pesquisa de diretório ocorreria. 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, depois 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 razoável para o modo permissivo global. Depois de resolver a maioria das negações, volte para o modo de aplicação e resolva os bugs conforme eles aparecem. Os domínios que ainda estiverem produzindo negações ou serviços ainda em desenvolvimento intenso podem ser colocados temporariamente em modo permissivo, mas revertê-los ao modo de aplicação assim que possível.

Aplicar antecipadamente

No modo de aplicação, as negações são registradas e aplicadas. É uma prática recomendada colocar o dispositivo no modo de aplicação o mais cedo possível. Esperar para criar e aplicar a política específica do dispositivo geralmente resulta em um produto com bugs e uma experiência ruim do usuário. Comece cedo o suficiente para participar do dogfooding e garantir a cobertura completa de testes da funcionalidade no uso 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 não segura. Use esse tempo para realizar uma auditoria de segurança do dispositivo e registrar bugs em comportamentos que não devem ser permitidos.

Remover ou excluir uma política

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

Resolver negações de serviços principais

As recusas geradas pelos serviços principais geralmente são resolvidas com o rotulagem de arquivos. 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

é totalmente resolvido ao rotular corretamente /dev/kgsl-3d0. Neste exemplo, tcontext é device. Isso representa um contexto padrão em que tudo em /dev recebe o rótulo device, a menos que um rótulo mais específico seja atribuído. Simplesmente aceitar a saída de audit2allow 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 outra permissão é necessária, porque o mediaserver já tem as permissões necessárias na política principal para acessar o gpu_device.

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

Em geral, conceder permissões para rótulos padrão é errado. Muitas dessas permissões não são permitidas por regras dependendo da necessidade. No entanto, mesmo quando isso não é permitido explicitamente, a prática recomendada é fornecer um rótulo específico.

Rotular novos serviços e resolver negações

Os serviços iniciados pelo init precisam ser executados nos próprios domínios do SELinux. O exemplo a seguir coloca o serviço "foo" no próprio domínio do SELinux e concede permissões a ele.

O serviço é iniciado no arquivo init.device.rc do dispositivo 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)
    

    Esse é o modelo inicial do domínio foo SELinux, ao qual você pode adicionar regras com base nas operações específicas realizadas por esse executável.

  2. Rótulo /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 seja devidamente rotulado para que o SELinux execute o serviço no domínio adequado.

  3. Crie e faça o flash das imagens de inicialização e do sistema.
  4. Refinar as regras do SELinux para o domínio.

    Use as negações para determinar as permissões necessárias. A ferramenta audit2allow oferece boas diretrizes, mas use-a apenas para informar a redação da política. Não copie apenas a saída.

Voltar para o modo aplicado

Não há problema em solucionar problemas no modo permissivo, mas volte para o modo de aplicação o quanto antes e tente permanecer nele.

Erros comuns

Confira algumas soluções para erros comuns que acontecem ao escrever políticas específicas do dispositivo.

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 apps de terceiros, podem ter acesso ao dispositivo de depuração.

A regra tem algumas falhas. A exclusão de untrusted_app é simples de contornar, porque todos os apps podem executar serviços no domínio isolated_app. Da mesma forma, se novos domínios de apps de terceiros forem adicionados ao AOSP, eles também terão acesso a scary_debug_device. A regra é muito permissiva. A maioria dos domínios não se beneficia de ter acesso a essa ferramenta de depuração. A regra precisa ter sido escrita para permitir apenas os domínios que exigem acesso.

Depurar recursos em produção

Os recursos de depuração não podem estar presentes em builds de produção nem a política deles.

A alternativa mais simples é permitir o recurso de depuração somente quando o SELinux estiver desativado em builds eng/userdebug, como adb root e adb shell setenforce 0.

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

Aumento do tamanho das políticas

Caracterização de políticas do SEAndroid no mundo real descreve uma tendência preocupante no crescimento das personalizações de políticas do dispositivo. A política específica do dispositivo precisa representar de 5% a 10% da política geral em execução em um dispositivo. As personalizações na faixa de 20%ou mais provavelmente contêm domínios privilegiados e políticas inválidas.

Política grande demais:

  • Tem um impacto duplo na memória, já que a política fica no ramdisk e também é carregada na memória do kernel.
  • Desperdiça espaço em disco ao exigir uma imagem de inicialização maior.
  • Afeta os tempos de pesquisa de políticas de ambiente de execução.

O exemplo a seguir mostra dois dispositivos em que a política específica do fabricante compreende 50% e 40% da política no dispositivo. Uma reescrita da política resultou em melhorias de segurança substanciais sem perda de funcionalidade, como 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 em tamanho e número de permissões. A redução do tamanho da política se deve quase totalmente à remoção de permissões desnecessárias. Muitas delas foram prováveis regras geradas por audit2allow que foram adicionadas indiscriminadamente à política. Os domínios inativos também foram um problema para os dois dispositivos.

Conceder o capability dac_override

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