Android recomienda 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, 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 para cada dominio, debes examinar los archivos correspondientes
o ejecutar la versión más reciente de sepolicy-analyze con la
marca adecuada (-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 el resultado de SELinux a 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: 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 los registros de denegación 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 que se inunden los registros. Para asegurarte de ver todos los mensajes pertinentes, puedes inhabilitar esta opción 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 comportamiento inadecuado, ya sea mediante cambios en el software, la política de SELinux o ambos.
En particular, 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 eltclassal final (unix_stream_socket), te indica aproximadamente qué se hizo y a 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, es algo que se ejecuta como shell. - El
tcontext (u:r:netd:s0)te indica el contexto del destino de la acción. En este caso, es un unix_stream_socket propiedad denetd. - El
comm="ping"en la parte superior te da una pista adicional sobre lo que se estaba ejecutando en el momento en que se generó la denegación. En este caso, es una muy 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 esta denegación:
- Acción : La acción intentada se destaca entre corchetes,
read writeosetenforce. - 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 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, unchr_file(dispositivo de caracteres).
Volcar pilas de usuarios 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, 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 Android simpleperf para habilitar este punto de seguimiento y capturar la cadena de llamadas.
Configuración admitida
- Se admite el kernel de Linux >= 5.10, en particular, las ramas
mainline
y
android12-5.10
del kernel común de Android.
También se admite la rama android12-5.4. Puedes usar
simpleperfpara 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 se admiten 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ó la denegación. 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 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 de usuario. Te brinda una mejor vista del flujo de código, ya que inicia el seguimiento desde el espacio de usuario hasta el kernel, donde 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 de userdebug o eng. Para ello, primero cambia ADB a la raíz ejecutando adb root. Luego, para inhabilitar la aplicación de SELinux, ejecuta lo siguiente:
adb shell setenforce 0
O en la línea de comandos del kernel (durante el inicio temprano del dispositivo):
androidboot.selinux=permissiveandroidboot.selinux=enforcing
O a través de bootconfig en Android 12:
androidboot.selinux=permissiveandroidboot.selinux=enforcing
Usar audit2allow
La herramienta audit2allow toma las denegaciones 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 lo siguiente:
adb pull /sys/fs/selinux/policyadb logcat -b events -d | audit2allow -p policy
Sin embargo, se debe tener cuidado para examinar cada posible adición en busca de permisos excesivos. Por ejemplo, si se alimenta audit2allow con la denegación de rmt_storage que se mostró anteriormente, se obtiene la siguiente instrucción de política de SELinux sugerida:
#============= 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 memoria del kernel, un agujero de seguridad evidente. A menudo, las instrucciones audit2allow son solo un punto de partida. Después de emplear estas instrucciones, es posible que debas 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 generar ningún cambio en la política; en cambio, se debería cambiar la app ofensiva.