Review this page to become familiar with SELinux concepts.
Mandatory access control
Security Enhanced Linux (SELinux), is a mandatory access control (MAC) system for the Linux operating system. As a MAC system, it differs from Linux's familiar discretionary access control (DAC) system. In a DAC system, a concept of ownership exists, whereby an owner of a particular resource controls access permissions associated with it. This is generally coarse-grained and subject to unintended privilege escalation. A MAC system, however, consults a central authority for a decision on all access attempts.
SELinux has been implemented as part of the Linux Security Module (LSM) framework, which recognizes various kernel objects, and sensitive actions performed on them. At the point at which each of these actions would be performed, an LSM hook function is called to determine whether or not the action should be allowed based on the information for it stored in an opaque security object. SELinux provides an implementation for these hooks and management of these security objects, which combine with its own policy, to determine the access decisions.
Along with other Android security measures, Android's access control policy greatly limits the potential damage of compromised machines and accounts. Using tools like Android's discretionary and mandatory access controls gives you a structure to ensure your software runs only at the minimum privilege level. This mitigates the effects of attacks and reduces the likelihood of errant processes overwriting or even transmitting data.
In Android 4.3 and higher, SELinux provides a mandatory access control (MAC) umbrella over traditional discretionary access control (DAC) environments. For instance, software must typically run as the root user account to write to raw block devices. In a traditional DAC-based Linux environment, if the root user becomes compromised that user can write to every raw block device. However, SELinux can be used to label these devices so the process assigned the root privilege can write to only those specified in the associated policy. In this way, the process cannot overwrite data and system settings outside of the specific raw block device.
See Use Cases for more examples of threats and ways to address them with SELinux.
Enforcement levels
SELinux can be implemented in varying modes:
- Permissive - SELinux security policy isn't enforced, only logged.
- Enforcing - Security policy is enforced and logged. Failures appear as EPERM errors.
This choice is binary and determines whether your policy takes action or merely allows you to gather potential failures. Permissive is especially useful during implementation.
Types, attributes, and rules
Android relies on the Type Enforcement (TE) component of SELinux for its
policy. It means that all objects (such as, file, process or socket) have a
type associated with them. For instance, by default, an app
has the type untrusted_app
. For a process, its type is also
known as its domain. It's possible to annotate a type with one or
many attributes. Attributes are useful to refer to multiple types
at the same time.
Objects are mapped to classes
(for example, a file, a directory, a symbolic link, a socket) and the different kinds of access
for each class are represented by permissions.
For instance, the permission open
exists for the class
file
. While types and attributes are regularly updated as part of
the Android SELinux policy, permissions and classes are statically defined and
rarely updated as part of a new Linux release.
A policy rule comes in the form:
allow source target:class permissions;
where:
- Source - The type (or attribute) of the subject of the rule. Who is requesting the access?
- Target - The type (or attribute) of the object. To what is the access requested?
- Class - The kind of object (for example, file, socket) being accessed.
- Permissions - The operation (or set of operations) (efor example, read, write) being performed.
An example of a rule is:
allow untrusted_app app_data_file:file { read write };
This says that apps are allowed to read and write files labeled
app_data_file
. There exist other types for apps. For
instances, isolated_app
is used for app services with
isolatedProcess=true
in their manifest. Instead of repeating the
rule for both types, Android uses an attribute named appdomain
for all the types that covers 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 };
When a rule is written that specifies an attribute name, that name is automatically expanded to the list of domains or types associated with the attribute. Some notable attributes are:
domain
- attribute associated with all process types,file_type
- attribute associated with all file types.
Macros
For file access in particular, there are many kinds of permission to
consider. For instance, the read
permission isn't enough to open the
file or call stat
on it. To simplify the rule definition, Android
provides a set of macros to handle the most common cases. For example, in order
to include the missing permissions such as open
, the rule above
could be rewritten as:
allow appdomain app_data_file:file rw_file_perms;
See the global_macros
and te_macros
files for more example of useful macros. Macros should be used whenever possible
to help reduce the likelihood of failures due to denials on related
permissions.
Once a type is defined, it needs to be associated with the file or process it represents. See Implementing SELinux for more details on how this association is done. For further information on rules, see the SELinux Notebook.
Security context and categories
When debugging SELinux policies or labelling files (using
file_contexts
or when ing ls -Z
), you might come
across a security context (also known as a label). For
example:
u:r:untrusted_app:s0:c15,c256,c513,c768
. A security context has the format:
user:role:type:sensitivity[:categories]
. You can usually ignore the
user
, role
and sensitivity
fields of a
context (see Specificity). The type
field is explained in the previous section. categories
are part of
the Multi-Level Security (MLS)
support in SELinux. In Android 12 and higher, categories are used to:
- Isolate the app data from access by another app,
- Isolate the app data from one physical user to another.
Specificity
Android doesn't use all the features provided by SELinux. When reading external documentation, keep these points in mind:
- The majority of the policies in AOSP are defined using the Kernel Policy Language. There are some exceptions for using Common Intermediate Language (CIL).
- SELinux users aren't used. The only user defined is
u
. When necessary, physical users are represented using the categories field of a security context. - SELinux roles and Role-Based Access Control (RBAC) aren't used. Two default roles are defined and used:
r
for subjects andobject_r
for objects. - SELinux sensitivities aren't used. The default
s0
sensitivity is always set. - SELinux booleans aren't used. When the policy is built for a device, it doesn't depend on the state of the device. This simplifies the auditing and debugging of policies.