Validando SELinux

Android recomienda encarecidamente a los OEM que prueben exhaustivamente sus implementaciones de SELinux. A medida que los fabricantes implementen SELinux, primero deben aplicar la nueva política a un grupo de dispositivos de prueba.

Después de aplicar una nueva política, asegúrese de que SELinux se esté ejecutando en el modo correcto en el dispositivo emitiendo el comando getenforce .

Esto imprime el modo SELinux global: ya sea Enforcing o Permissive. Para determinar el modo SELinux para cada dominio, debe examinar los archivos correspondientes o ejecutar la última versión de sepolicy-analyze con el indicador apropiado ( -p ), presente en /platform/system/sepolicy/tools/ .

Negaciones de lectura

Compruebe si hay errores, que se enrutan como registros de eventos a dmesg y logcat y se pueden ver localmente en el dispositivo. Los fabricantes deben examinar la salida de SELinux para dmesg en estos dispositivos y refinar la configuración antes del lanzamiento público en modo permisivo y eventualmente cambiar al modo obligatorio. Los mensajes de registro de SELinux contienen avc: y, por lo tanto, se pueden encontrar fácilmente con grep . Es posible capturar los registros de denegación en curso ejecutando cat /proc/kmsg o capturar registros de denegación del inicio anterior ejecutando cat /sys/fs/pstore/console-ramoops .

Los mensajes de error de SELinux tienen una velocidad limitada una vez completado el arranque para evitar saturar los registros. Para asegurarse de ver todos los mensajes relevantes, puede desactivarlo ejecutando adb shell auditctl -r 0 .

Con este resultado, los fabricantes pueden identificar fácilmente cuándo los usuarios o componentes del sistema infringen la política de SELinux. Luego, los fabricantes pueden reparar este mal comportamiento, ya sea mediante cambios en el software, la política de SELinux o ambos.

Específicamente, estos mensajes de registro indican qué procesos fallarían en el modo obligatorio y por qué. Aquí hay un ejemplo:

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 este resultado así:

  • El { connectto } de arriba representa la acción que se está tomando. Junto con la tclass al final ( unix_stream_socket ), le indica aproximadamente qué se estaba haciendo y qué. En este caso, algo estaba intentando conectarse a un socket de flujo Unix.
  • El scontext (u:r:shell:s0) le indica qué contexto inició la acción. En este caso, se trata de algo que se ejecuta como shell.
  • El tcontext (u:r:netd:s0) le indica el contexto del objetivo de la acción. En este caso, se trata de un unix_stream_socket propiedad de netd .
  • El comm="ping" en la parte superior le brinda una pista adicional sobre lo que se estaba ejecutando en el momento en que se generó la denegación. En este caso, es una pista bastante buena.

Otro ejemplo:

adb shell su root dmesg | grep 'avc: '

Producción:

<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

Estos son los elementos clave de esta negación:

  • Acción : la acción intentada está resaltada entre paréntesis, read write o setenforce .
  • Actor : la entrada scontext (contexto de origen) representa al actor, en este caso el demonio rmt_storage .
  • Objeto : la entrada tcontext (contexto de destino) representa el objeto sobre el que se actúa, en este caso kmem.
  • Resultado : la entrada tclass (clase de destino) indica el tipo de objeto sobre el que se actúa, en este caso un chr_file (dispositivo de carácter).

Vaciado de pilas de usuario y kernel

En algunos casos, la información contenida en el registro de eventos no es suficiente para identificar el origen de la denegación. A menudo resulta útil recopilar la cadena de llamadas, incluidos el kernel y el espacio de usuario, para comprender mejor por qué se produjo la denegación.

Los núcleos recientes definen un punto de seguimiento llamado avc:selinux_audited . Utilice Android simpleperf para habilitar este punto de seguimiento y capturar la cadena de llamadas.

Configuración admitida

  • Kernel de Linux >= 5.10, en particular, se admiten las ramas principales del kernel común de Android y android12-5.10 . También se admite la rama android12-5.4 . Puede utilizar simpleperf para determinar si el punto de seguimiento está definido en su dispositivo: adb root && adb shell simpleperf list | grep avc:selinux_audited . Para otras versiones del kernel, puede seleccionar las confirmaciones dd81662 y 30969bc .
  • Debería ser posible reproducir el evento que está depurando. Los eventos de tiempo de arranque no son compatibles con simpleperf; sin embargo, es posible que aún puedas reiniciar el servicio para activar el evento.

Capturando la cadena de llamadas

El primer paso es registrar el evento usando simpleperf record :

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

Entonces, se debe desencadenar el evento que provocó la negación. Después de eso, se debe detener la grabación. En este ejemplo, al usar Ctrl-c , la muestra debería haberse capturado:

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

Finalmente, se puede utilizar simpleperf report para inspeccionar el seguimiento de la pila capturado. Por ejemplo:

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

La cadena de llamadas anterior es una cadena de llamadas unificada del kernel y del espacio de usuario. Le brinda una mejor vista del flujo de código al iniciar el seguimiento desde el espacio de usuario hasta el kernel donde ocurre la denegación. Para obtener más información sobre simpleperf , consulte la referencia de comandos ejecutables de Simpleperf.

Cambiar a permisivo

La aplicación de SELinux se puede deshabilitar a través de ADB en userdebug o eng builds. Para hacerlo, primero cambie ADB a root ejecutando adb root . Luego, para deshabilitar la aplicación de SELinux, ejecute:

adb shell setenforce 0

O en la línea de comando del kernel (durante el inicio temprano del dispositivo):

androidboot.selinux=permissive
androidboot.selinux=enforcing

O mediante bootconfig en Android 12:

androidboot.selinux=permissive
androidboot.selinux=enforcing

Usando audit2allow

La herramienta audit2allow toma las denegaciones dmesg y las convierte en las correspondientes declaraciones de política de SELinux. Como tal, puede acelerar enormemente el desarrollo de SELinux.

Para usarlo, ejecute:

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

Sin embargo, se debe tener cuidado al examinar cada posible adición en busca de permisos excesivos. Por ejemplo, alimentar audit2allow con la denegación rmt_storage mostrada anteriormente da como resultado la siguiente declaración de política de SELinux sugerida:

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

Esto otorgaría rmt la capacidad de escribir en la memoria del kernel, un flagrante agujero de seguridad. A menudo, las declaraciones audit2allow son sólo un punto de partida. Después de emplear estas declaraciones, es posible que necesite cambiar el dominio de origen y la etiqueta del destino, así como incorporar macros adecuadas, para llegar a una buena política. A veces, la denegación que se examina no debería dar lugar a ningún cambio de política; más bien se debe cambiar la aplicación infractora.