SELinux concepts

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 have 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 and object_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.