Validando SELinux

O Android incentiva fortemente os OEMs a testarem minuciosamente suas implementações do SELinux. À medida que os fabricantes implementam o SELinux, eles devem primeiro aplicar a nova política a um conjunto de dispositivos de teste.

Após aplicar uma nova política, certifique-se de que o SELinux esteja sendo executado no modo correto no dispositivo emitindo o comando getenforce .

Isso imprime o modo SELinux global: Enforcing ou Permissive. Para determinar o modo SELinux para cada domínio, você deve examinar os arquivos correspondentes ou executar a versão mais recente do sepolicy-analyze com o sinalizador apropriado ( -p ), presente em /platform/system/sepolicy/tools/ .

Lendo negações

Verifique se há erros, que são roteados como logs de eventos para dmesg e logcat e podem ser visualizados localmente no dispositivo. Os fabricantes devem 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 eventual mudança para o modo de aplicação. As mensagens de log do SELinux contêm avc: e portanto podem ser facilmente encontradas com grep . É possível capturar os logs de negação em andamento executando cat /proc/kmsg ou capturar logs de negação da inicialização anterior executando cat /sys/fs/pstore/console-ramoops .

As mensagens de erro do SELinux têm taxa limitada após a conclusão da inicialização para evitar sobrecarregar os logs. Para ter certeza de ver todas as mensagens relevantes, você pode desabilitar isso executando adb shell auditctl -r 0 .

Com esta saída, os fabricantes podem identificar prontamente quando os usuários ou componentes do sistema violam a política do SELinux. Os fabricantes podem então reparar esse mau comportamento, seja por meio de alterações no software, na política do SELinux ou em ambos.

Especificamente, essas mensagens de log indicam quais processos falhariam no modo de imposição e por quê. Aqui está 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 esta saída assim:

  • O { connectto } acima representa a ação que está sendo executada. Juntamente com o tclass no final ( unix_stream_socket ), ele informa aproximadamente o que estava sendo feito e o quê. Neste 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. Neste caso, isso é algo rodando como 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 superior fornece uma dica adicional sobre o que estava sendo executado no momento em que a negação foi gerada. Neste caso, é uma boa dica.

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 elementos-chave desta negação:

  • Ação - a tentativa de ação é destacada entre colchetes, read write ou setenforce .
  • Ator - A entrada scontext (contexto de origem) representa o ator, neste caso o daemon rmt_storage .
  • Objeto - A entrada tcontext (contexto de destino) representa o objeto que está sendo acionado, neste caso kmem.
  • Resultado - A entrada tclass (classe de destino) indica o tipo de objeto sobre o qual está sendo atuado, neste caso um chr_file (dispositivo de caractere).

Despejando pilhas de usuários e kernel

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

Kernels recentes definem um tracepoint chamado avc:selinux_audited . Use o Android simpleperf para ativar esse tracepoint e capturar o callchain.

Configuração suportada

  • Kernel Linux >= 5.10, em particular as ramificações principais do Android Common Kernel e android12-5.10 são suportadas. A ramificação android12-5.4 também é suportada. Você pode usar simpleperf para determinar se o tracepoint está definido em 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. Eventos de tempo de inicialização não são suportados usando simpleperf; no entanto, você ainda poderá reiniciar o serviço para acionar o evento.

Capturando a cadeia de chamadas

O primeiro passo é registrar o evento usando simpleperf record :

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

Então, o evento que causou a negação deverá 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.

Finalmente, simpleperf report pode ser usado para inspecionar o stacktrace 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 é uma cadeia de chamadas unificada do kernel e do espaço do usuário. Ele oferece uma visão melhor do fluxo do código, iniciando o rastreamento do espaço do usuário até o kernel onde ocorre a negação. Para obter mais informações sobre simpleperf , consulte a referência de comandos executáveis ​​do Simpleperf

Mudando para permissivo

A aplicação do SELinux pode ser desabilitada via ADB em userdebug ou eng builds. Para fazer isso, primeiro mude o ADB para root executando adb root . Então, para desabilitar a aplicação do SELinux, execute:

adb shell setenforce 0

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

androidboot.selinux=permissive
androidboot.selinux=enforcing

Ou através do bootconfig no Android 12:

androidboot.selinux=permissive
androidboot.selinux=enforcing

Usando audit2allow

A ferramenta audit2allow pega negações dmesg e as converte em declarações de política SELinux correspondentes. Como tal, pode acelerar bastante o desenvolvimento do SELinux.

Para usá-lo, execute:

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

No entanto, deve-se ter cuidado ao examinar cada possível acréscimo de permissões excessivas. Por exemplo, alimentar audit2allow 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 rmt a capacidade de gravar na memória do kernel, uma falha de segurança gritante. Freqüentemente, as declarações audit2allow são apenas um ponto de partida. Depois de empregar essas declarações, talvez seja necessário alterar o domínio de origem e o rótulo do destino, bem como incorporar macros adequadas, para chegar a uma boa política. Às vezes, a negação que está sendo examinada não deveria resultar em nenhuma mudança política; em vez disso, o aplicativo ofensivo deve ser alterado.