Conceptos de SELinux

Revisa esta página para familiarizarte con los conceptos de SELinux.

Control de acceso obligatorio

Security Enhanced Linux (SELinux) es un sistema de control de acceso obligatorio (MAC) para el sistema operativo Linux. Como sistema MAC, difiere del sistema de control de acceso discrecional (DAC) familiar de Linux. En un sistema DAC, existe un concepto de propiedad, en el que el propietario de un recurso en particular controla los permisos de acceso asociados a él. Por lo general, esto es de grano grueso y está sujeto a una elevación de privilegios no deseada. Sin embargo, un sistema MAC consulta a una autoridad central para tomar una decisión sobre todos los intentos de acceso.

SELinux se implementó como parte del framework del módulo de seguridad de Linux (LSM), que reconoce varios objetos del kernel y las acciones sensibles que se realizan en ellos. En el punto en el que se realizaría cada una de estas acciones, se llama a una función de hook de LSM para determinar si se debe permitir la acción en función de la información almacenada en un objeto de seguridad opaco. SELinux proporciona una implementación para estos hooks y la administración de estos objetos de seguridad, que se combinan con su propia política para determinar las decisiones de acceso.

Junto con otras medidas de seguridad de Android, la política de control de acceso de Android limita en gran medida el daño potencial de las máquinas y las cuentas vulneradas. El uso de herramientas como los controles de acceso discrecional y obligatorio de Android te brinda una estructura para garantizar que tu software se ejecute solo en el nivel de privilegio mínimo. Esto mitiga los efectos de los ataques y reduce la probabilidad de que los procesos erróneos reemplacen o incluso transmitan datos.

En Android 4.3 y versiones posteriores, SELinux proporciona un control de acceso obligatorio (MAC) sobre los entornos tradicionales de control de acceso discrecional (DAC). Por ejemplo, el software suele ejecutarse como la cuenta de usuario raíz para escribir en dispositivos de bloque sin procesar. En un entorno tradicional de Linux basado en DAC, si el usuario raíz se ve comprometido, ese usuario puede escribir en todos los dispositivos de bloque sin procesar. Sin embargo, SELinux se puede usar para etiquetar estos dispositivos, de modo que el proceso asignado al privilegio raíz pueda escribir solo en los especificados en la política asociada. De esta manera, el proceso no puede reemplazar los datos ni la configuración del sistema fuera del dispositivo de bloque sin procesar específico.

Consulta Casos de uso para obtener más ejemplos de amenazas y formas de abordarlas con SELinux.

Niveles de aplicación

SELinux se puede implementar en varios modos:

  • Permissive : No se aplica la política de seguridad de SELinux, solo se registra.
  • Enforcing : Se aplica y se registra la política de seguridad. Las fallas aparecen como errores EPERM.

Esta opción es binaria y determina si tu política toma medidas o simplemente te permite recopilar posibles fallas. El modo Permissive es especialmente útil durante la implementación.

Tipos, atributos y reglas

Android se basa en el componente Type Enforcement (TE) de SELinux para su política. Esto significa que todos los objetos (como archivos, procesos o sockets) tienen un tipo asociado. Por ejemplo, de forma predeterminada, una app tiene el tipo untrusted_app. En el caso de un proceso, su tipo también se conoce como su dominio. Es posible anotar un tipo con uno o varios atributos. Los atributos son útiles para hacer referencia a varios tipos al mismo tiempo.

Los objetos se asignan a clases (por ejemplo, un archivo, un directorio, un vínculo simbólico, un socket), y los diferentes tipos de acceso para cada clase se representan mediante permisos. Por ejemplo, el permiso open existe para la clase file. Si bien los tipos y los atributos se actualizan con regularidad como parte de la política de SELinux de Android, los permisos y las clases se definen de forma estática y rara vez se actualizan como parte de una versión nueva de Linux.

Una regla de política tiene el siguiente formato: allow source target:class permissions; donde:

  • Source : Es el tipo (o atributo) del sujeto de la regla. ¿Quién solicita el acceso?
  • Target : Es el tipo (o atributo) del objeto. ¿A qué se solicita el acceso solicitado?
  • Clase : Es el tipo de objeto (por ejemplo, archivo, socket) al que se accede.
  • Permissions : Es la operación (o conjunto de operaciones) (por ejemplo, leer, escribir) que se realiza.

Un ejemplo de regla es el siguiente:

allow untrusted_app app_data_file:file { read write };

Esto indica que las apps pueden leer y escribir archivos etiquetados como app_data_file. Existen otros tipos para las apps. Por ejemplo, isolated_app se usa para los servicios de apps con isolatedProcess=true en su manifiesto. En lugar de repetir la regla para ambos tipos, Android usa un atributo llamado appdomain para todos los tipos que abarcan apps:

# Associate the attribute appdomain with the type untrusted_app.
typeattribute untrusted_app appdomain;

# Associate the attribute appdomain with the type isolated_app.
typeattribute isolated_app appdomain;

allow appdomain app_data_file:file { read write };

Cuando se escribe una regla que especifica un nombre de atributo, ese nombre se expande automáticamente a la lista de dominios o tipos asociados con el atributo. Algunos atributos notables son los siguientes:

  • domain: Atributo asociado con todos los tipos de procesos
  • file_type: Atributo asociado con todos los tipos de archivos

Macros

En particular, para el acceso a archivos, hay muchos tipos de permisos que se deben tener en cuenta. Por ejemplo, el permiso read no es suficiente para abrir el archivo o llamar a stat en él. Para simplificar la definición de reglas, Android proporciona un conjunto de macros para controlar los casos más comunes. Por ejemplo, para incluir los permisos faltantes, como open, la regla anterior se podría reescribir de la siguiente manera:

allow appdomain app_data_file:file rw_file_perms;

Consulta los archivos global_macros y te_macros para obtener más ejemplos de macros útiles. Se deben usar macros siempre que sea posible para ayudar a reducir la probabilidad de fallas debido a denegaciones en permisos relacionados.

Una vez que se define un tipo, debe asociarse con el archivo o el proceso que representa. Consulta Cómo implementar SELinux para obtener más detalles sobre cómo se realiza esta asociación. Para obtener más información sobre las reglas, consulta el cuaderno de SELinux.

Contexto y categorías de seguridad

Cuando depuras políticas de SELinux o etiquetas archivos (con file_contexts o ls -Z), es posible que te encuentres con un contexto de seguridad (también conocido como etiqueta). Por ejemplo: u:r:untrusted_app:s0:c15,c256,c513,c768. Un contexto de seguridad tiene el siguiente formato: user:role:type:sensitivity[:categories]. Por lo general, puedes ignorar los campos user, role y sensitivity de un contexto (consulta Especificidad). El campo type se explica en la sección anterior. categories forma parte de la compatibilidad con la seguridad de varios niveles (MLS) en SELinux. En Android 12 y versiones posteriores, las categorías se usan para lo siguiente:

  • Aislar los datos de la app del acceso de otra app
  • Aislar los datos de app de un usuario físico a otro

Especificidad

Android no usa todas las funciones que proporciona SELinux. Cuando leas documentación externa, ten en cuenta estos puntos:

  • La mayoría de las políticas de AOSP se definen con el lenguaje de políticas del kernel. Existen algunas excepciones para usar el lenguaje intermedio común (CIL).
  • No se usan usuarios de SELinux. El único usuario definido es u. Cuando es necesario, los usuarios físicos se representan con el campo de categorías de un contexto de seguridad.
  • No se usan roles de SELinux ni el control de acceso basado en roles (RBAC). Se definen y usan dos roles predeterminados: r para los sujetos y object_r para los objetos.
  • No se usan sensibilidades de SELinux. Siempre se establece la sensibilidad s0 predeterminada.
  • No se usan booleanos de SELinux. Cuando se compila la política para un dispositivo, no depende del estado del dispositivo. Esto simplifica la auditoría y la depuración de las políticas.