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 con él. Por lo general, 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 momento en 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 o no la acción en función de la información que se almacena 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) que abarca 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 bloques sin procesar. En un entorno tradicional de Linux basado en DAC, si el usuario raíz se ve comprometido, puede escribir en todos los dispositivos de bloques sin procesar. Sin embargo, se puede usar SELinux para etiquetar estos dispositivos de modo que el proceso al que se le asignó el privilegio de raíz pueda escribir solo en los especificados en la política asociada. De esta manera, el proceso no puede reemplazar los datos y la configuración del sistema fuera del dispositivo de bloques sin procesar específico.
Consulta Casos de uso para ver más ejemplos de amenazas y formas de abordarlas con SELinux.
Niveles de aplicación
SELinux se puede implementar en diferentes modos:
- Permisivo: La política de seguridad de SELinux no se aplica, solo se registra.
- Aplicación: La política de seguridad se aplica y se registra. Los errores aparecen como errores EPERM.
Esta opción es binaria y determina si tu política toma medidas o solo te permite recopilar posibles fallas. El permiso permisivo es especialmente útil durante la implementación.
Tipos, atributos y reglas
Android se basa en el componente de aplicación de tipo (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 o un socket), y los diferentes tipos de acceso para cada clase se representan con permisos.
Por ejemplo, el permiso open
existe para la clase file
. Si bien los tipos y los atributos se actualizan periódicamente 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 nueva versión de Linux.
Una regla de política tiene el siguiente formato:
allow source target:class permissions;
en el que:
- Fuente: Es el tipo (o atributo) del sujeto de la regla. ¿Quién solicita el acceso?
- Destino: Es el tipo (o atributo) del objeto. ¿A qué se solicita el acceso?
- Clase: Es el tipo de objeto (por ejemplo, archivo o socket) al que se accede.
- Permisos: Es la operación (o el conjunto de operaciones) (por ejemplo, lectura o escritura) que se está realizando.
Un ejemplo de una 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 de 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 las 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. Estos son algunos de los atributos destacados:
domain
: Es el atributo asociado con todos los tipos de procesos.file_type
: Es el atributo asociado con todos los tipos de archivos.
Macros
En el caso del acceso a archivos en particular, hay muchos tipos de permisos que se deben tener en cuenta. Por ejemplo, el permiso read
no es suficiente para abrir el archivo ni 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 ver más ejemplos de macros útiles. Las macros deben usarse siempre que sea posible para ayudar a reducir la probabilidad de fallas debido a rechazos de permisos relacionados.
Una vez que se define un tipo, se debe asociar con el archivo o 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 notebook de SELinux.
Contexto y categorías de seguridad
Cuando depuras políticas de SELinux o etiquetas de archivos (con file_contexts
o cuando usas ls -Z
), es posible que encuentres 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
forman 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 la 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 los siguientes 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 los usuarios de SELinux. El único usuario definido es
u
. Cuando sea necesario, los usuarios físicos se representan con el campo de categorías de un contexto de seguridad. - No se usan los roles ni el control de acceso basado en roles (RBAC) de SELinux. Se definen y usan dos roles predeterminados:
r
para los sujetos yobject_r
para los objetos. - No se usan las sensibilidades de SELinux. La sensibilidad predeterminada de
s0
siempre se establece. - No se usan los valores 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.