Android recomienda encarecidamente a los OEMs 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 prueba de dispositivos.
Después de aplicar una política nueva, ejecuta el comando getenforce
para asegurarte de que SELinux se ejecute en el modo correcto en el dispositivo.
Esto imprime el modo SELinux global: Enforcing o Permissive. Para determinar el modo SELinux para cada dominio, debes examinar los archivos correspondientes o ejecutar la versión más reciente de sepolicy-analyze
con la marca correspondiente (-p
), presente en
/platform/system/sepolicy/tools/
.
Leer denegaciones
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 los resultados de SELinux para dmesg
en estos dispositivos, definir mejor la configuración antes del lanzamiento público en el modo permisivo y, luego, cambiar al modo de aplicación forzosa. Los mensajes de registro de SELinux contienen avc:
, por lo que se pueden encontrar fácilmente con grep
. Para capturar los registros de denegación en curso, ejecuta cat /proc/kmsg
o, para capturar los registros de denegación del inicio anterior, ejecuta cat /sys/fs/pstore/console-ramoops
.
Los mensajes de error de SELinux tienen un límite de frecuencia una vez que se completa el inicio para evitar saturar los registros. Para asegurarte de ver todos los mensajes relevantes, puedes inhabilitar la función con adb shell auditctl -r 0
.
Con este resultado, los fabricantes pueden identificar fácilmente cuándo los usuarios o los componentes del sistema incumplen la política de SELinux. Luego, los fabricantes pueden reparar este comportamiento incorrecto, ya sea mediante cambios en el software, la política de SELinux o ambos.
Específicamente, estos mensajes de registro indican qué procesos fallarán en el modo de aplicación forzosa 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 eltclass
al final (unix_stream_socket
), te indica de forma aproximada qué se estaba haciendo con qué. En este caso, algo intentaba conectarse a un socket de flujo de Unix. -
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. -
tcontext (u:r:netd:s0)
te indica el contexto del objetivo de la acción. En este caso, es un unix_stream_socket propiedad denetd
. - 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ó la denegación. En este caso, es una buena pista.
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:
- Acción: La acción intentada se destaca entre corchetes,
read write
osetenforce
. - Actor: La entrada
scontext
(contexto de origen) representa al actor, en este caso el daemonrmt_storage
. - Objeto: 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 de destino) indica el tipo de objeto sobre el que se actúa, en este caso, unchr_file
(dispositivo de caracteres).
Cómo volcar pilas de usuarios y kernels
En algunos casos, la información contenida en 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 de usuario, para comprender mejor por qué se produjo la denegación.
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 kernels 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 elegir las confirmaciones dd81662 y 30969bc. - Debería ser posible reproducir el evento que estás depurando. Los eventos de tiempo de inicio no son compatibles con simpleperf. Sin embargo, es posible que puedas reiniciar el servicio para activar el evento.
Captura 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, la grabación debería detenerse. En este ejemplo, con el uso de 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 seguimiento de 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 del usuario. Te brinda una mejor vista del flujo de código, ya que inicia el seguimiento desde el espacio del usuario hasta el kernel en el que se produce la denegación. 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 userdebug o eng. Para ello, primero ejecuta adb root
para cambiar ADB al directorio raíz. Luego, para inhabilitar la aplicación forzosa de SELinux, ejecuta lo siguiente:
adb shell setenforce 0
También puedes hacerlo en la línea de comandos del kernel (durante la primera activación del dispositivo):
androidboot.selinux=permissive
androidboot.selinux=enforcing
O a través de bootconfig en Android 12:
androidboot.selinux=permissive
androidboot.selinux=enforcing
Usa audit2allow
La herramienta audit2allow
toma las negaciones de dmesg
y las convierte en las instrucciones de política de SELinux correspondientes. Por lo tanto, puede acelerar en gran medida el desarrollo de SELinux.
Para usarlo, ejecuta el siguiente comando:
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 detectar permisos por incumplimiento. Por ejemplo, si se le proporciona a audit2allow
la denegación rmt_storage
que se mostró antes, se obtiene 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 a rmt
la capacidad de escribir memoria del kernel, un punto de seguridad evidente. A menudo, las sentencias audit2allow
son solo un punto de partida. Después de usar estas instrucciones, es posible que debas cambiar el dominio de origen y la etiqueta del objetivo, así como incorporar macros adecuadas para llegar a una política adecuada. A veces, la denegación que se examina no debería generar ningún cambio en la política, sino que se debe cambiar la app infractora.