Cómo escribir una política de SELinux

El Proyecto de código abierto de Android (AOSP) proporciona una política de base sólida para las apps y los servicios comunes en todos los dispositivos Android. Los colaboradores de AOSP definen mejor esta política con frecuencia. Se espera que la política principal represente entre el 90 y el 95% de la política final en el dispositivo, y las personalizaciones específicas del dispositivo representarán entre el 5% y el 10% restante. En este artículo, se enfoca en estas personalizaciones específicas del dispositivo, cómo escribir una política específica del dispositivo y algunas de las dificultades que se deben evitar en el proceso.

Activación del dispositivo

Cuando escribas la política específica del dispositivo, sigue estos pasos.

Ejecuta en modo permisivo

Cuando un dispositivo está en modo permisivo, las denegaciones se registran, pero no se aplican. El modo permisivo es importante por dos razones:

  • El modo permisivo garantiza que la incorporación de políticas no retrase otras tareas de inicio anticipado del dispositivo.
  • Una denegación forzosa puede enmascarar otras denegaciones. Por ejemplo, el acceso a archivos suele implicar una búsqueda de directorios, la apertura de archivos y, luego, la lectura de archivos. En el modo de aplicación forzosa, solo se produciría el rechazo de la búsqueda de directorios. El modo permisivo garantiza que se vean todas las denegaciones.

La forma más sencilla de poner un dispositivo en modo permisivo es con la línea de comandos del kernel. Se puede agregar al archivo BoardConfig.mk del dispositivo: platform/device/<vendor>/<target>/BoardConfig.mk. Después de modificar la línea de comandos, ejecuta make clean, luego make bootimage y, luego, escribe la nueva imagen de arranque.

Luego, confirma el modo permisivo con el siguiente comando:

adb shell getenforce

Dos semanas es un período razonable para estar en el modo permisivo global. Después de abordar la mayoría de las denegaciones, vuelve al modo de aplicación forzosa y resuelve los errores a medida que se presenten. Los dominios que aún generan rechazos o los servicios que aún están en desarrollo intensivo se pueden colocar temporalmente en el modo permisivo, pero deben volver al modo de aplicación lo antes posible.

Aplicar con anticipación

En el modo de aplicación, los rechazos se registran y aplican. La práctica recomendada es que el dispositivo esté en modo de aplicación de políticas lo antes posible. Esperar para crear y aplicar una política específica del dispositivo suele generar un producto con errores y una experiencia del usuario deficiente. Comienza con suficiente anticipación para participar en el dogfooding y garantizar una cobertura de prueba completa de la funcionalidad en el uso real. Comenzar temprano garantiza que las inquietudes de seguridad informen las decisiones de diseño. Por el contrario, otorgar permisos basados únicamente en las denegaciones observadas no es un enfoque seguro. Aprovecha este momento para realizar una auditoría de seguridad del dispositivo y registrar errores relacionados con comportamientos que no se deben permitir.

Quita o borra la política existente

Existen varios motivos para crear una política específica del dispositivo desde cero en un dispositivo nuevo, entre los que se incluyen los siguientes:

Cómo abordar las denegaciones de servicios principales

Las denegaciones que generan los servicios principales suelen abordarse mediante el etiquetado de archivos. Por ejemplo:

avc: denied { open } for pid=1003 comm=”mediaserver” path="/dev/kgsl-3d0”
dev="tmpfs" scontext=u:r:mediaserver:s0 tcontext=u:object_r:device:s0
tclass=chr_file permissive=1
avc: denied { read write } for pid=1003 name="kgsl-3d0" dev="tmpfs"
scontext=u:r:mediaserver:s0
tcontext=u:object_r:device:s0 tclass=chr_file permissive=1

se aborda por completo etiquetando correctamente /dev/kgsl-3d0. En este ejemplo, tcontext es device. Esto representa un contexto predeterminado en el que todo en /dev recibe la etiqueta “ device”, a menos que se asigne una etiqueta más específica. Si solo aceptas el resultado de audit2allow aquí, se generará una regla incorrecta y demasiado permisiva.

Para resolver este tipo de problema, asigna al archivo una etiqueta más específica, que en este caso es gpu_device. No se necesitan más permisos, ya que mediaserver ya tiene los permisos necesarios en la política principal para acceder a gpu_device.

Otros archivos específicos del dispositivo que deben etiquetarse con tipos predefinidos en la política principal:

En general, no es correcto otorgar permisos a las etiquetas predeterminadas. Muchas de estas reglas neverallow no permiten muchos de estos permisos, pero, incluso cuando no se prohíben de forma explícita, se recomienda proporcionar una etiqueta específica.

Etiqueta los servicios nuevos y las denegaciones de direcciones

Los servicios iniciados por init deben ejecutarse en sus propios dominios de SELinux. En el siguiente ejemplo, el servicio “foo” se coloca en su propio dominio de SELinux y se le otorgan permisos.

El servicio se inicia en el archivo init.device.rc de nuestro dispositivo de la siguiente manera:

service foo /system/bin/foo
    class core
  1. Crea un dominio nuevo "foo".

    Crea el archivo device/manufacturer/device-name/sepolicy/foo.te con el siguiente contenido:

    # foo service
    type foo, domain;
    type foo_exec, exec_type, file_type;
    
    init_daemon_domain(foo)
    

    Esta es la plantilla inicial del dominio foo de SELinux, a la que puedes agregar reglas según las operaciones específicas que realiza ese ejecutable.

  2. Etiqueta /system/bin/foo

    Agrega lo siguiente a device/manufacturer/device-name/sepolicy/file_contexts:

    /system/bin/foo   u:object_r:foo_exec:s0
    

    Esto garantiza que el ejecutable esté etiquetado de forma correcta para que SELinux ejecute el servicio en el dominio adecuado.

  3. Compila y escribe en la memoria flash las imágenes de arranque y del sistema.
  4. Define mejor las reglas de SELinux para el dominio.

    Usa denegaciones para determinar los permisos necesarios. La herramienta audit2allow proporciona buenos lineamientos, pero úsala solo para fundamentar la redacción de políticas. No solo copies el resultado.

Cómo volver al modo de aplicación forzosa

Está bien solucionar problemas en el modo permisivo, pero vuelve al modo de aplicación forzosa lo antes posible y trata de permanecer en él.

Errores comunes

Estas son algunas soluciones para los errores comunes que ocurren cuando se escriben políticas específicas del dispositivo.

Uso excesivo de la negación

La siguiente regla de ejemplo es como trabar la puerta de entrada, pero dejar las ventanas abiertas:

allow { domain -untrusted_app } scary_debug_device:chr_file rw_file_perms

El objetivo es claro: todos, excepto las apps de terceros, pueden tener acceso al dispositivo de depuración.

La regla tiene algunos defectos. La exclusión de untrusted_app es trivial, ya que todas las apps pueden ejecutar servicios de forma opcional en el dominio isolated_app. Del mismo modo, si se agregan dominios nuevos para apps de terceros al AOSP, también tendrán acceso a scary_debug_device. La regla es demasiado permisiva. La mayoría de los dominios no se beneficiarán de tener acceso a esta herramienta de depuración. La regla debería haberse escrito para permitir solo los dominios que requieren acceso.

Funciones de depuración en producción

Las funciones de depuración no deben estar presentes en las compilaciones de producción ni su política.

La alternativa más simple es permitir solo la función de depuración cuando SELinux está inhabilitado en compilaciones eng/userdebug, como adb root y adb shell setenforce 0.

Otra alternativa segura es encerrar los permisos de depuración en una sentencia userdebug_or_eng.

Explosión del tamaño de la política

En Characterizing SEAndroid Policies in the Wild, se describe una tendencia preocupante en el crecimiento de las personalizaciones de políticas de dispositivos. La política específica del dispositivo debe representar entre el 5 y el 10% de la política general que se ejecuta en un dispositivo. Es casi seguro que las personalizaciones en el rango superior al 20%contengan dominios con privilegios excesivos y una política inactiva.

Política innecesariamente grande:

  • Genera un doble impacto en la memoria, ya que la política se encuentra en el ramdisk y también se carga en la memoria del kernel.
  • Desperdicia espacio en el disco, ya que requiere una imagen de arranque más grande.
  • Afecta los tiempos de búsqueda de la política de tiempo de ejecución.

En el siguiente ejemplo, se muestran dos dispositivos en los que la política específica del fabricante comprendió el 50% y el 40% de la política en el dispositivo. La reescritura de la política proporcionó mejoras sustanciales de seguridad sin pérdida de funcionalidad, como se muestra a continuación. (Se incluyen los dispositivos AOSP Shamu y Flounder para comparar).

Figura 1: Comparación del tamaño de la política específica del dispositivo después de la auditoría de seguridad.

Figura 1. Comparación del tamaño de la política específica del dispositivo después de la auditoría de seguridad

En ambos casos, la política se redujo de forma significativa en tamaño y en cantidad de permisos. La disminución del tamaño de la política se debe casi por completo a la eliminación de permisos innecesarios, muchos de los cuales probablemente eran reglas generadas por audit2allow que se agregaron de forma indiscriminada a la política. Los dominios inactivos también fueron un problema para ambos dispositivos.

Otorga la capability dac_override

Una denegación de dac_override significa que el proceso infractor intenta acceder a un archivo con los permisos de usuario, grupo o mundo de Unix incorrectos. La solución adecuada casi nunca es otorgar el permiso dac_override. En su lugar, cambia los permisos de Unix en el archivo o proceso. Algunos dominios, como init, vold y installd, realmente necesitan la capacidad de anular los permisos de archivos de Unix para acceder a los archivos de otros procesos. Consulta el blog de Dan Walsh para obtener una explicación más detallada.