Valida SELinux

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

Después de aplicar una política nueva, asegúrate de que SELinux se ejecute en el modo correcto en el dispositivo con el comando getenforce.

Esto imprime el modo global de SELinux: Enforcing o Permissive. Para determinar el modo de SELinux de cada dominio, debes examinar los archivos correspondientes o ejecutar la versión más reciente de sepolicy-analyze con la marca adecuada (-p), que se encuentra en /platform/system/sepolicy/tools/.

Denegaciones de lectura

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

Los mensajes de error de SELinux tienen un límite de frecuencia después de que se completa el arranque para evitar saturar los registros. Para asegurarte de ver todos los mensajes relevantes, puedes inhabilitar esta opción con adb shell auditctl -r 0.

Con este resultado, los fabricantes pueden identificar fácilmente cuándo los usuarios o componentes del sistema incumplen la política de SELinux. Luego, los fabricantes pueden corregir este comportamiento inadecuado con 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 de aplicación y por qué. A continuación, se muestra 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

Interpreta este resultado de la siguiente manera:

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

Otro ejemplo:

adb shell su root dmesg | grep 'avc: '

Resultado:

<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 este rechazo:

  • Action: La acción intentada se destaca entre corchetes, read write o setenforce.
  • Actor: La entrada scontext (contexto de origen) representa al actor, en este caso, el daemon rmt_storage.
  • Object: La entrada tcontext (contexto de destino) representa el objeto sobre el que se realiza la acción, en este caso, kmem.
  • Resultado: La entrada tclass (clase objetivo) indica el tipo de objeto sobre el que se realiza la acción, en este caso, un chr_file (dispositivo de caracteres).

Cómo volcar las pilas del usuario y del kernel

En algunos casos, la información que contiene el registro de eventos no es suficiente para identificar el origen del rechazo. A menudo, es útil recopilar la cadena de llamadas, incluido el kernel y el espacio del usuario, para comprender mejor por qué se produjo el rechazo.

Los kernels recientes definen un punto de seguimiento llamado avc:selinux_audited. Usa simpleperf de Android para habilitar este punto de seguimiento y capturar la cadena de llamadas.

Configuración admitida

  • Se admiten el kernel de Linux >= 5.10, en particular las ramas del kernel común de Android mainline y android12-5.10. También se admite la rama android12-5.4. Puedes usar simpleperf para determinar si el punto de seguimiento está definido en tu dispositivo: adb root && adb shell simpleperf list | grep avc:selinux_audited. Para otras versiones del kernel, puedes seleccionar los cambios dd81662 y 30969bc.
  • Debería ser posible reproducir el evento que estás depurando. Los eventos de tiempo de arranque no son compatibles con simpleperf, pero es posible que puedas reiniciar el servicio para activar el evento.

Cómo capturar la cadena de llamadas

El primer paso es registrar el evento con simpleperf record:

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

Luego, se debe activar el evento que causó el rechazo. Después de eso, se debe detener la grabación. En este ejemplo, con Ctrl-c, se debería haber capturado la muestra:

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

Por último, se puede usar simpleperf report para inspeccionar el registro de seguimiento 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 del usuario. Te brinda una mejor vista del flujo de código, ya que inicia el registro desde el espacio del usuario hasta el kernel, donde se produce el rechazo. Para obtener más información sobre simpleperf, consulta la referencia de comandos ejecutables de Simpleperf.

Cambiar a permisivo

La aplicación de SELinux se puede inhabilitar con adb en compilaciones de userdebug o eng. Para ello, primero cambia ADB a root ejecutando adb root. Luego, para inhabilitar la aplicación de SELinux, ejecuta lo siguiente:

adb shell setenforce 0

O bien, en la línea de comandos del kernel (durante la activación inicial del dispositivo):

androidboot.selinux=permissive
androidboot.selinux=enforcing

O bien, a través de bootconfig en Android 12:

androidboot.selinux=permissive
androidboot.selinux=enforcing

Cómo usar audit2allow

La herramienta audit2allow toma los rechazos de dmesg y los convierte en instrucciones de política de SELinux correspondientes. Por lo tanto, puede acelerar en gran medida el desarrollo de SELinux.

Para usarlo, ejecuta lo siguiente:

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 para evitar permisos excesivos. Por ejemplo, si se ingresa audit2allow con la denegación de rmt_storage que se mostró anteriormente, se genera la siguiente instrucción sugerida de la política de SELinux:

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

Esto le otorgaría a rmt la capacidad de escribir en la memoria del kernel, lo que representa una brecha de seguridad evidente. A menudo, las instrucciones audit2allow son solo un punto de partida. Después de emplear estas declaraciones, es posible que debas cambiar el dominio de origen y la etiqueta del objetivo, así como incorporar las macros adecuadas, para llegar a una buena política. A veces, el rechazo que se examina no debería generar ningún cambio en la política, sino que se debería cambiar la app infractora.