Implementa SELinux

SELinux está configurado para denegar el acceso de forma predeterminada, lo que significa que cada acceso para el que tiene un hook en el kernel debe permitirse de forma explícita por la política. Esto significa que un archivo de política se compone de una gran cantidad de información sobre reglas, tipos, clases, permisos y mucho más. En este documento, no se aborda el tema de SELinux en su totalidad, pero es fundamental comprender cómo escribir reglas de políticas cuando se configuran dispositivos Android nuevos. Ya hay mucha información disponible sobre SELinux. Consulta la documentación de asistencia para ver los recursos sugeridos.

Archivos de claves

Para habilitar SELinux, integra el kernel de Android más reciente y, luego, incorpora los archivos que se encuentran en el directorio system/sepolicy. Cuando se compilan, esos archivos comprenden la política de seguridad del kernel de SELinux y abarcan el sistema operativo Android upstream.

En general, no debes modificar los archivos system/sepolicy directamente. En su lugar, agrega o edita tus propios archivos de políticas específicos del dispositivo en el directorio /device/manufacturer/device-name/sepolicy. En Android 8.0 y versiones posteriores, los cambios que realices en estos archivos solo deben afectar la política en tu directorio de proveedores. Para obtener más detalles sobre la separación de la política de SELinux pública en Android 8.0 y versiones posteriores, consulta Cómo personalizar la política de SELinux en Android 8.0 y versiones posteriores. Independientemente de la versión de Android, seguirás modificando estos archivos:

Archivos de políticas

Los archivos que terminan con *.te son archivos fuente de políticas de SELinux, que definen dominios y sus etiquetas. Es posible que debas crear archivos de políticas nuevos en /device/manufacturer/device-name/sepolicy, pero debes intentar actualizar los archivos existentes siempre que sea posible.

Archivos de contexto

En los archivos de contexto, se especifican las etiquetas de los objetos.

  • file_contexts asigna etiquetas a los archivos y la usan varios componentes del espacio de usuario. A medida que crees políticas nuevas, crea o actualiza este archivo para asignar etiquetas nuevas a los archivos. Para aplicar un nuevo file_contexts, vuelve a compilar la imagen del sistema de archivos o ejecuta restorecon en el archivo al que se le cambiará la etiqueta. En las actualizaciones, los cambios en file_contexts se aplican automáticamente a las particiones del sistema y de datos del usuario como parte de la actualización. Los cambios también se pueden aplicar automáticamente en la actualización a otras particiones agregando llamadas restorecon_recursive a tu archivo init.board.rc después de que la partición se haya montado con acceso de lectura y escritura.
  • genfs_contexts asigna etiquetas a los sistemas de archivos, como proc o vfat, que no admiten atributos extendidos. Esta configuración se carga como parte de la política del kernel, pero es posible que los cambios no se apliquen a los inodos integrados en el kernel, lo que requiere un reinicio o desmontar y volver a montar el sistema de archivos para aplicar el cambio por completo. También se pueden asignar etiquetas específicas a puntos de montaje específicos, como vfat con la opción context=mount.
  • property_contexts asigna etiquetas a las propiedades del sistema Android para controlar qué procesos pueden establecerlas. El proceso de init lee esta configuración durante el inicio.
  • service_contexts asigna etiquetas a los servicios de Binder de Android para controlar qué procesos pueden agregar (registrar) y encontrar (buscar) una referencia de Binder para el servicio. El proceso de servicemanager lee esta configuración durante el inicio.
  • seapp_contexts asigna etiquetas a los procesos de la app y a los directorios de /data/data. El proceso zygote lee esta configuración en cada inicio de la app, y installd la lee durante el inicio.
  • mac_permissions.xml asigna una etiqueta seinfo a las apps según su firma y, de manera opcional, su nombre de paquete. Luego, la etiqueta seinfo se puede usar como clave en el archivo seapp_contexts para asignar una etiqueta específica a todas las apps con esa etiqueta seinfo. system_server lee esta configuración durante el inicio.
  • keystore2_key_contexts asigna etiquetas a los espacios de nombres de Keystore 2. El daemon keystore2 aplica estos espacios de nombres. Keystore siempre proporcionó espacios de nombres basados en UID/AID. Keystore 2 también aplica los espacios de nombres definidos por sepolicy. Aquí encontrarás una descripción detallada del formato y las convenciones de este archivo.

Archivo make BoardConfig.mk

Después de editar o agregar archivos de políticas y contexto, actualiza tu archivo de compilación /device/manufacturer/device-name/BoardConfig.mk para hacer referencia al subdirectorio sepolicy y a cada archivo de política nuevo. Para obtener más información sobre las variables BOARD_SEPOLICY, consulta el archivo system/sepolicy/README.

BOARD_SEPOLICY_DIRS += \
        <root>/device/manufacturer/device-name/sepolicy

BOARD_SEPOLICY_UNION += \
        genfs_contexts \
        file_contexts \
        sepolicy.te

Después de recompilar, tu dispositivo tendrá habilitado SELinux. Ahora puedes personalizar tus políticas de SELinux para adaptarlas a tus propias incorporaciones al sistema operativo Android, como se describe en Personalización, o bien verificar tu configuración existente, como se explica en Validación.

Cuando los nuevos archivos de políticas y las actualizaciones de BoardConfig.mk estén listos, la nueva configuración de políticas se compilará automáticamente en el archivo de políticas final del kernel. Para obtener más información sobre cómo se compila sepolicy en el dispositivo, consulta Cómo compilar sepolicy.

Implementación

Para comenzar a usar SELinux, haz lo siguiente:

  1. Habilita SELinux en el kernel: CONFIG_SECURITY_SELINUX=y
  2. Cambia el parámetro kernel_cmdline o bootconfig de modo que se cumpla lo siguiente:
    BOARD_KERNEL_CMDLINE := androidboot.selinux=permissive
    o
    BOARD_BOOTCONFIG := androidboot.selinux=permissive
    Esto es solo para el desarrollo inicial de la política del dispositivo. Después de que tengas una política de arranque inicial, quita este parámetro para que el dispositivo aplique la política o falle la prueba de CTS.
  3. Inicia el sistema en modo permisivo y observa qué denegaciones se encuentran durante el inicio:
    En Ubuntu 14.04 o versiones posteriores:
    adb shell su -c dmesg | grep denied | audit2allow -p out/target/product/BOARD/root/sepolicy
    
    En Ubuntu 12.04:
    adb pull /sys/fs/selinux/policy
    adb logcat -b all | audit2allow -p policy
    
  4. Evalúa el resultado en busca de advertencias similares a init: Warning! Service name needs a SELinux domain defined; please fix!. Consulta Validación para obtener instrucciones y herramientas.
  5. Identificar dispositivos y otros archivos nuevos que necesitan etiquetado
  6. Usa etiquetas existentes o nuevas para tus objetos. Consulta los archivos *_contexts para ver cómo se etiquetaban las cosas anteriormente y usa tu conocimiento sobre el significado de las etiquetas para asignar una nueva. Lo ideal es que sea una etiqueta existente que se ajuste a la política, pero, a veces, se necesita una etiqueta nueva y se requieren reglas para acceder a ella. Agrega tus etiquetas a los archivos de contexto correspondientes.
  7. Identifica los dominios o procesos que deberían tener sus propios dominios de seguridad. Es probable que debas escribir una política completamente nueva para cada uno. Todos los servicios derivados de init, por ejemplo, deben tener su propio registro. Los siguientes comandos ayudan a revelar los que permanecen en ejecución (pero TODOS los servicios necesitan ese tratamiento):
    adb shell su -c ps -Z | grep init
    
    adb shell su -c dmesg | grep 'avc: '
    
  8. Revisa init.device.rc para identificar los dominios que no tienen un tipo de dominio. Proporciónales un dominio al principio de tu proceso de desarrollo para evitar agregar reglas a init o confundir los accesos de init con los que se encuentran en su propia política.
  9. Configura BOARD_CONFIG.mk para usar variables de BOARD_SEPOLICY_*. Consulta el archivo README en system/sepolicy para obtener detalles sobre cómo configurar esto.
  10. Examina los archivos init.device.rc y fstab.device y asegúrate de que cada uso de mount corresponda a un sistema de archivos etiquetado correctamente o que se especifique una opción context= mount.
  11. Revisa cada rechazo y crea una política de SELinux para controlarlos correctamente. Consulta los ejemplos en Personalización.

Debes comenzar con las políticas del AOSP y, luego, crear tus propias personalizaciones. Para obtener más información sobre la estrategia de políticas y un análisis más detallado de algunos de estos pasos, consulta Cómo escribir una política de SELinux.

Casos de uso

A continuación, se incluyen ejemplos específicos de vulnerabilidades que debes tener en cuenta cuando crees tu propio software y las políticas de SELinux asociadas:

Vínculos simbólicos: Debido a que los vínculos simbólicos aparecen como archivos, a menudo se leen como archivos, lo que puede generar vulnerabilidades. Por ejemplo, algunos componentes privilegiados, como init, cambian los permisos de ciertos archivos, a veces para que sean excesivamente abiertos.

Luego, los atacantes podrían reemplazar esos archivos por vínculos simbólicos a código que controlan, lo que les permitiría sobrescribir archivos arbitrarios. Sin embargo, si sabes que tu app nunca atraviesa un symlink, puedes prohibirle que lo haga con SELinux.

Archivos del sistema: Considera la clase de archivos del sistema que solo el servidor del sistema debería modificar. Sin embargo, como netd, init y vold se ejecutan como raíz, pueden acceder a esos archivos del sistema. Por lo tanto, si netd se viera comprometido, podría comprometer esos archivos y, potencialmente, el servidor del sistema.

Con SELinux, puedes identificar esos archivos como archivos de datos del servidor del sistema. Por lo tanto, el único dominio que tiene acceso de lectura y escritura a ellos es el servidor del sistema. Incluso si netd se viera comprometido, no podría cambiar de dominio al dominio del servidor del sistema ni acceder a esos archivos del sistema, aunque se ejecute como raíz.

Datos de la app: Otro ejemplo es la clase de funciones que deben ejecutarse como raíz, pero no deberían acceder a los datos de la app. Esto es muy útil, ya que se pueden hacer afirmaciones de amplio alcance, como que ciertos dominios no relacionados con los datos de la app tienen prohibido acceder a Internet.

setattr: Para comandos como chmod y chown, puedes identificar el conjunto de archivos en los que el dominio asociado puede realizar setattr. Cualquier otra cosa podría estar prohibida por estos cambios, incluso por el administrador raíz. Por lo tanto, una app podría ejecutar chmod y chown en los datos etiquetados como app_data_files, pero no en los datos etiquetados como shell_data_files o system_data_files.