Validando o SELinux

O Android encoraja fortemente os OEMs a testarem suas implementações do SELinux completamente. À 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 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 .

Com essa saída, os fabricantes podem identificar prontamente quando os usuários ou componentes do sistema estão violando a política do SELinux. Os fabricantes podem então reparar esse mau comportamento, seja por 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 a tclass no final ( unix_stream_socket ), ela informa aproximadamente o que estava sendo feito com o 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, isso é algo em execução como o shell.
  • O tcontext (u:r:netd:s0) informa o contexto do destino 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 principais elementos dessa negação:

  • Ação - a ação tentada é destacada entre colchetes, read write ou setenforce .
  • Ator - A scontext (contexto de origem) representa o ator, neste caso o daemon rmt_storage .
  • Object - A tcontext (contexto de destino) representa o objeto que está sendo executado, neste caso kmem.
  • Resultado - A entrada tclass (classe de destino) indica o tipo de objeto que está sendo executado, 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. Muitas vezes, é útil reunir a cadeia de chamadas, incluindo kernel e espaço de usuário, para entender melhor por que ocorreu a negação.

Os kernels recentes definem um ponto de rastreamento chamado avc:selinux_audited . Use o Android simpleperf para habilitar este tracepoint e capturar o callchain.

Configuração compatível

  • Kernel do Linux >= 5.10, em particular os ramos do Android Common Kernel mainline e android12-5.10 são suportados. A ramificação android12-5.4 também é suportada. Você pode usar o 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. Os 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.

Finalmente, simpleperf report pode ser usado para inspecionar o rastreamento de pilha 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 acontece. Para obter mais informações sobre simpleperf , consulte a referência de comandos executáveis ​​Simpleperf

Mudando para permissivo

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

adb shell setenforce 0

Ou na linha de comando do kernel (durante o início 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 as negações do dmesg e as converte em declarações de política SELinux correspondentes. Como tal, pode acelerar muito 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 em potencial para 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 ao rmt a capacidade de escrever na memória do kernel, uma falha de segurança gritante. Muitas vezes, as instruçõ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 apropriadas, 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 ofensivo deve ser alterado.