Validar o SELinux

O Android incentiva fortemente os OEMs a testar as implementações do SELinux de forma completa. À medida que os fabricantes implementam o SELinux, eles devem aplicar a nova a um pool de testes de dispositivos primeiro.

Depois de aplicar uma nova política, verifique se o SELinux está sendo executado no modo correto no dispositivo emitindo o comando getenforce.

Isso imprime o modo SELinux global: aplicador ou permissivo. Para determinar o modo SELinux de cada domínio, é preciso examinar o modo arquivos ou execute a versão mais recente do sepolicy-analyze com os sinalização apropriada (-p), presente em /platform/system/sepolicy/tools/.

Ler negações

Verifique se há erros, que são roteados como logs de eventos para dmesg e logcat e são visíveis localmente no dispositivo. Os fabricantes precisam examinar a saída do SELinux para dmesg nesses dispositivos e refinar as configurações antes do lançamento público no modo permissivo e, eventualmente, mudar para o modo de aplicação. As mensagens de registro do SELinux contêm avc: e, portanto, podem ser encontradas facilmente com grep. É possível capturar de negação de serviço executando cat /proc/kmsg ou para capturar registros de negação da inicialização anterior executando cat /sys/fs/pstore/console-ramoops.

As mensagens de erro do SELinux têm limitação de taxa após a conclusão da inicialização para evitar sobrecarga os registros. Para garantir que você veja todas as mensagens relevantes, desative esta opção executando adb shell auditctl -r 0.

Com essa saída, os fabricantes podem identificar prontamente quando os usuários do sistema ou estão violando a política do SELinux. Os fabricantes podem corrigir esse comportamento incorreto, fazendo mudanças no software, na política do SELinux ou em ambos.

Especificamente, essas mensagens de registro indicam quais processos falhariam na o modo de aplicação e por quê. Confira um exemplo:

avc: denied  { connectto } for  pid=2671 comm="ping" path="/dev/socket/dnsproxyd"
scontext=u:r:shell:s0 tcontext=u:r:netd:s0 tclass=unix_stream_socket

Interprete essa saída da seguinte maneira:

  • O { connectto } acima representa a ação que está sendo realizada. Junto com o tclass no final (unix_stream_socket), ele informa aproximadamente o que foi feito em relação a qual. Nesse caso, algo estava tentando se conectar a um soquete de fluxo Unix.
  • O scontext (u:r:shell:s0) informa qual contexto iniciou a ação. Nesse caso, é algo executado como o shell.
  • O tcontext (u:r:netd:s0) informa o contexto do alvo da ação. Nesse caso, é um unix_stream_socket de propriedade de netd.
  • O comm="ping" na parte de cima dá uma dica adicional sobre o que estava sendo no momento em que a negação foi gerada. Nesse caso, é uma dica muito boa.

Outro exemplo:

adb shell su root dmesg | grep 'avc: '

Saída:

<5> type=1400 audit: avc:  denied  { read write } for  pid=177
comm="rmt_storage" name="mem" dev="tmpfs" ino=6004 scontext=u:r:rmt:s0
tcontext=u:object_r:kmem_device:s0 tclass=chr_file

Aqui estão os principais elementos dessa negação:

  • Ação: a ação tentada é destacada entre colchetes, read write ou setenforce.
  • Ator: a entrada scontext (contexto de origem) representa o ator, neste caso o daemon rmt_storage.
  • Object: a entrada tcontext (contexto de destino) representa objeto sendo usado, neste caso kmem.
  • Resultado: a entrada tclass (classe de destino) indica o tipo de objeto em que a ação é realizada. Neste caso, um chr_file (dispositivo de caractere).

Fazer o despejo de pilhas de usuário e kernel

Em alguns casos, as informações contidas no registro de eventos não são suficientes para identificar a origem da recusa. Muitas vezes, é útil reunir a cadeia de chamadas, incluindo o kernel e o espaço do usuário, para entender melhor por que a negação ocorreu.

Os kernels recentes definem um tracepoint chamado avc:selinux_audited. Usar o Android simpleperf para ativar esse tracepoint e capturar a cadeia de chamadas.

Configuração com suporte

  • O kernel do Linux 5.10 ou mais recente, em particular as ramificações do Kernel comum do Android mainline e android12-5.10 são aceitos. A biblioteca android12-5.4 a ramificação também é aceita. Use simpleperf para determinar se o tracepoint está definido no seu dispositivo: adb root && adb shell simpleperf list | grep avc:selinux_audited. Para outras versões do kernel, você pode escolher os commits dd81662 e 30969bc.
  • Deve ser possível reproduzir o evento que você está depurando. Os eventos de tempo de inicialização não são compatíveis com o simpleperf. No entanto, ainda é possível reiniciar o serviço para acionar o evento.

Capturar a cadeia de chamada

A primeira etapa é registrar o evento usando simpleperf record:

adb shell -t "cd /data/local/tmp && su root simpleperf record -a -g -e avc:selinux_audited"

Em seguida, o evento que causou a negação precisa ser acionado. Depois disso, a gravação deve ser interrompida. Neste exemplo, usando Ctrl-c, a amostra deveria ter sido capturada:

^Csimpleperf I cmd_record.cpp:751] Samples recorded: 1. Samples lost: 0.

Por fim, o simpleperf report pode ser usado para inspecionar o stack trace capturado. Por exemplo:

adb shell -t "cd /data/local/tmp && su root simpleperf report -g --full-callgraph"
[...]
Children  Self     Command  Pid   Tid   Shared Object                                   Symbol
100.00%   0.00%    dmesg    3318  3318  /apex/com.android.runtime/lib64/bionic/libc.so  __libc_init
       |
       -- __libc_init
          |
           -- main
              toybox_main
              toy_exec_which
              dmesg_main
              klogctl
              entry_SYSCALL_64_after_hwframe
              do_syscall_64
              __x64_sys_syslog
              do_syslog
              selinux_syslog
              slow_avc_audit
              common_lsm_audit
              avc_audit_post_callback
              avc_audit_post_callback

A cadeia de chamadas acima é um kernel unificado e uma cadeia de chamadas do espaço do usuário. Ele oferece uma experiência do fluxo do código iniciando o rastro desde o espaço do usuário até o kernel, onde a negação acontece. Para mais informações sobre simpleperf, consulte a Referência de comandos executáveis do Simpleperf

Mudar para permissiva

A aplicação do SELinux pode ser desativada com o adb em builds userdebug ou eng. Para fazer isso, primeiro mude o ADB para raiz executando adb root. Em seguida, para desativar a aplicação do SELinux, execute:

adb shell setenforce 0

Ou na linha de comando do kernel (durante a inicialização inicial do dispositivo):

androidboot.selinux=permissive
androidboot.selinux=enforcing

Ou pelo bootconfig do Android 12:

androidboot.selinux=permissive
androidboot.selinux=enforcing

Usar audit2allow

A ferramenta audit2allow recebe dmesg negações e os converte em instruções de política do SELinux correspondentes. Por isso, ele pode acelerar bastante o desenvolvimento do SELinux.

Para usá-la, execute:

adb pull /sys/fs/selinux/policy
adb logcat -b events -d | audit2allow -p policy

No entanto, é preciso ter cuidado para examinar cada possível adição de permissões excessivas. Por exemplo, alimentar audit2allow com a negação rmt_storage mostrada anteriormente resulta na seguinte declaração de política SELinux sugerida:

#============= shell ==============
allow shell kernel:security setenforce;
#============= rmt ==============
allow rmt kmem_device:chr_file { read write };

Isso concederia a rmt a capacidade de gravar memória do kernel, uma uma brecha de segurança evidente. Muitas vezes, as instruções audit2allow são apenas uma ponto de partida. Depois de empregar essas instruções, talvez seja necessário alterar o domínio de origem e o rótulo do destino, além de incorporar para chegar a uma boa política. Às vezes, a recusa em análise não resulta em nenhuma mudança na política. Em vez disso, o app infrator precisa ser alterado.