Validando o SELinux

O Android encoraja fortemente os OEMs a testar suas implementações do SELinux completamente. À medida que os fabricantes implementam o SELinux, eles devem aplicar a nova política a um pool de teste 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: 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 ( -p ) apropriado, presente em /platform/system/sepolicy/tools/ .

Recusas de leitura

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 imposição. As mensagens de log do SELinux contêm avc: e 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 são limitadas por taxa após a conclusão da inicialização para evitar sobrecarregar os logs. Para ter certeza de ver todas as mensagens relevantes, você pode desativar isso executando adb shell auditctl -r 0 .

Com essa saída, os fabricantes podem identificar prontamente quando os usuários ou componentes do sistema violam a política do SELinux. Os fabricantes podem 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 para quê. 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 que está sendo executado como o shell.
  • O tcontext (u:r:netd:s0) informa o contexto do alvo da ação. Nesse caso, é um unix_stream_socket pertencente a 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. 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 elementos-chave dessa negação:

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

Despejando pilhas de usuário e kernel

Em alguns casos, as informações contidas no log de eventos não são suficientes para identificar a origem da negação. Geralmente é ú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.

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

Configuração suportada

  • Kernel do Linux >= 5.10, em particular Android Common Kernel branches mainline e android12-5.10 são suportados. 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 é gravar 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 deve 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, 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 de código, iniciando o rastreamento do espaço do usuário até o kernel onde a negação ocorre. Para obter mais informações sobre simpleperf , consulte a referência de comandos executáveis ​​Simpleperf

Mudando para permissivo

A imposição do SELinux pode ser desativada via ADB em userdebug ou compilações eng. Para fazer isso, primeiro mude ADB para root executando adb root . Em seguida, para desativar a imposiçã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 tomar cuidado para examinar cada adição potencial para 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 ao rmt a capacidade de gravar na memória do kernel, uma falha de segurança flagrante. Frequentemente, as declarações audit2allow são apenas um ponto de partida. Depois de empregar essas instruções, pode ser 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 deve resultar em nenhuma mudança de política; em vez disso, o aplicativo incorreto deve ser alterado.