Implementar o SELinux

O SELinux é configurado para negar por padrão, o que significa que cada acesso para o qual ele tem um gancho no kernel precisa ser permitido explicitamente pela política. Isso significa que um arquivo de política é composto por uma grande quantidade de informações sobre regras, tipos, classes, permissões e muito mais. Uma consideração completa do SELinux está fora do escopo deste documento, mas entender como escrever regras de política agora é essencial ao criar novos dispositivos Android. Já há muitas informações disponíveis sobre o SELinux. Consulte a documentação de suporte para conferir recursos sugeridos.

Arquivos de chave

Para ativar o SELinux, integre o kernel do Android mais recente e incorpore os arquivos encontrados no diretório system/sepolicy. Quando compilados, esses arquivos compreendem a política de segurança do kernel SELinux e abrangem o sistema operacional Android upstream.

Em geral, não modifique os arquivos system/sepolicy diretamente. Em vez disso, adicione ou edite seus próprios arquivos de política específicos do dispositivo no diretório /device/manufacturer/device-name/sepolicy. No Android 8.0 e versões mais recentes, as alterações feitas nesses arquivos só afetam a política no diretório do fornecedor. Para mais detalhes sobre a separação de sepolicy pública no Android 8.0 e versões mais recentes, consulte Personalizar SEPolicy no Android 8.0 e versões mais recentes. Independentemente da versão do Android, você ainda vai modificar estes arquivos:

Arquivos de política

Os arquivos que terminam com *.te são arquivos de origem da política do SELinux, que definem domínios e rótulos. Talvez seja necessário criar novos arquivos de política em /device/manufacturer/device-name/sepolicy, mas tente atualizar os arquivos atuais sempre que possível.

Arquivos de contexto

Os arquivos de contexto são onde você especifica rótulos para seus objetos.

  • file_contexts atribui rótulos a arquivos e é usado por vários componentes do espaço do usuário. À medida que você cria novas políticas, crie ou atualize esse arquivo para atribuir novos rótulos aos arquivos. Para aplicar um novo file_contexts, reconstrua a imagem do sistema de arquivos ou execute restorecon no arquivo a ser remarcado. Em upgrades, as mudanças em file_contexts são aplicadas automaticamente ao sistema e às partições de userdata como parte do upgrade. As mudanças também podem ser aplicadas automaticamente no upgrade para outras partições adicionando chamadas restorecon_recursive ao arquivo init.board.rc depois que a partição for montada para leitura e gravação.
  • genfs_contexts atribui rótulos a sistemas de arquivos, como proc ou vfat, que não oferecem suporte a atributos estendidos. Essa configuração é carregada como parte da política do kernel, mas as mudanças podem não entrar em vigor para inodes no núcleo, exigindo uma reinicialização ou desmontagem e remontagem do sistema de arquivos para aplicar totalmente a mudança. Rótulos específicos também podem ser atribuídos a montagens específicas, como vfat usando a opção context=mount.
  • property_contexts atribui rótulos às propriedades do sistema Android para controlar quais processos podem defini-los. Essa configuração é lida pelo processo init durante a inicialização.
  • service_contexts atribui rótulos aos serviços de vinculação do Android para controlar quais processos podem adicionar (registrar) e encontrar (procurar) uma referência de vinculação para o serviço. Essa configuração é lida pelo processo servicemanager durante a inicialização.
  • seapp_contexts atribui rótulos a processos de apps e diretórios /data/data. Essa configuração é lida pelo processo zygote em cada inicialização do app e pelo installd durante a inicialização.
  • mac_permissions.xml atribui uma tag seinfo a apps com base na assinatura e, opcionalmente, no nome do pacote. A tag seinfo pode ser usada como uma chave no arquivo seapp_contexts para atribuir um rótulo específico a todos os apps com essa tag seinfo. Essa configuração é lida por system_server durante a inicialização.
  • keystore2_key_contexts atribui rótulos aos namespaces do Keystore 2. Esse namespace é aplicado pelo daemon keystore2. O keystore sempre forneceu namespaces baseados em UID/AID. Além disso, o Keystore 2 aplica namespaces definidos pela sepolicy. Confira aqui uma descrição detalhada do formato e das convenções desse arquivo.

Makefile BoardConfig.mk

Depois de editar ou adicionar arquivos de política e contexto, atualize o /device/manufacturer/device-name/BoardConfig.mk makefile para fazer referência ao subdiretório sepolicy e a cada novo arquivo de política. Para mais informações sobre as variáveis BOARD_SEPOLICY, consulte arquivo system/sepolicy/README.

BOARD_SEPOLICY_DIRS += \
        <root>/device/manufacturer/device-name/sepolicy

BOARD_SEPOLICY_UNION += \
        genfs_contexts \
        file_contexts \
        sepolicy.te

Após a recriação, o dispositivo é ativado com o SELinux. Agora é possível personalizar suas políticas do SELinux para acomodar suas próprias adições ao sistema operacional Android, conforme descrito em Personalização, ou verificar a configuração atual, conforme descrito em Validação.

Quando os novos arquivos de política e as atualizações do BoardConfig.mk estiverem em vigor, as novas configurações de política serão criadas automaticamente no arquivo de política do kernel final. Para mais informações sobre como o sepolicy é criado no dispositivo, consulte Como criar o sepolicy.

Implementação

Para começar a usar o SELinux:

  1. Ative o SELinux no kernel: CONFIG_SECURITY_SELINUX=y
  2. Mude o parâmetro kernel_cmdline ou bootconfig para que:
    BOARD_KERNEL_CMDLINE := androidboot.selinux=permissive
    ou
    BOARD_BOOTCONFIG := androidboot.selinux=permissive
    Isso é apenas para o desenvolvimento inicial da política do dispositivo. Depois de ter uma política de inicialização inicial, remova esse parâmetro para que o dispositivo seja aplicado ou falhe no CTS.
  3. Inicialize o sistema em modo permissivo e veja quais negações são encontradas na inicialização:
    No Ubuntu 14.04 ou mais recente:
    adb shell su -c dmesg | grep denied | audit2allow -p out/target/product/BOARD/root/sepolicy
    
    No Ubuntu 12.04:
    adb pull /sys/fs/selinux/policy
    adb logcat -b all | audit2allow -p policy
    
  4. Avalie a saída em busca de avisos semelhantes a init: Warning! Service name needs a SELinux domain defined; please fix!. Consulte Validação para instruções e ferramentas.
  5. Identifique dispositivos e outros novos arquivos que precisam ser rotulados.
  6. Use rótulos novos ou já existentes para seus objetos. Analise os arquivos *_contexts para saber como as coisas foram rotuladas anteriormente e use o conhecimento dos significados dos rótulos para atribuir um novo. O ideal é que seja um rótulo existente que se encaixe na política, mas às vezes é necessário um novo rótulo e regras para o acesso a ele. Adicione os rótulos aos arquivos de contexto apropriados.
  7. Identifique domínios/processos que precisam ter domínios de segurança próprios. Provavelmente, você precisará escrever uma política completamente nova para cada um. Todos os serviços gerados a partir de init, por exemplo, precisam ter o próprio. Os comandos a seguir ajudam a revelar aqueles que permanecem em execução (mas TODOS os serviços precisam desse tratamento):
    adb shell su -c ps -Z | grep init
    
    adb shell su -c dmesg | grep 'avc: '
    
  8. Analise init.device.rc para identificar os domínios que não têm um tipo. Forneça um domínio cedo no processo de desenvolvimento para evitar a adição de regras ao init ou confundir os acessos init com os que estão na própria política.
  9. Configure BOARD_CONFIG.mk para usar variáveis BOARD_SEPOLICY_*. Consulte o README em system/sepolicy para saber como configurar.
  10. Examine o arquivo init.device.rc e fstab.device e confira se cada uso de mount corresponde a um sistema de arquivos corretamente identificado ou se uma opção context= mount foi especificada.
  11. Analise cada negação e crie uma política SELinux para processar cada uma delas corretamente. Consulte os exemplos em Personalização.

Comece com as políticas do AOSP e depois crie suas próprias personalizações. Para mais informações sobre a estratégia de política e uma análise mais detalhada de algumas dessas etapas, consulte Como escrever uma política do SELinux.

Casos de uso

Confira exemplos específicos de exploits a serem considerados ao criar seu próprio software e as políticas SELinux associadas:

Links simbólicos:como eles aparecem como arquivos, geralmente são lidos como arquivos, o que pode levar a exploits. Por exemplo, alguns componentes privilegiados, como init, mudam as permissões de determinados arquivos, às vezes para serem excessivamente abertos.

Os invasores podem substituir esses arquivos por links simbólicos para o código que eles controlam, permitindo que o invasor substitua arquivos arbitrários. No entanto, se você souber que o app nunca atravessa um link simbólico, poderá proibir isso com o SELinux.

Arquivos do sistema:considere a classe de arquivos do sistema que deve ser modificada apenas pelo servidor do sistema. No entanto, como netd, init e vold são executados como raiz, eles podem acessar esses arquivos do sistema. Se o netd for comprometido, ele poderá comprometer esses arquivos e, possivelmente, o próprio servidor do sistema.

Com o SELinux, é possível identificar esses arquivos como arquivos de dados do servidor do sistema. Portanto, o único domínio com acesso de leitura/gravação é o servidor do sistema. Mesmo que o netd tenha sido comprometido, ele não poderia mudar de domínio para o domínio do servidor do sistema e acessar esses arquivos do sistema, embora seja executado como raiz.

Dados do app:outro exemplo é a classe de funções que precisa ser executada como raiz, mas não pode acessar os dados do app. Isso é muito útil, porque é possível fazer várias declarações, como a proibição de acesso à Internet de determinados domínios não relacionados aos dados do app.

setattr:para comandos como chmod e chown, é possível identificar o conjunto de arquivos em que o domínio associado pode realizar setattr. Qualquer coisa fora disso pode ser proibida dessas mudanças, mesmo por raiz. Assim, um app pode executar chmod e chown em relação aos rotulados app_data_files, mas não shell_data_files ou system_data_files.