Validating Security-Enhanced Linux in Android

In this document

Introduction

As part of the Android security model, Android uses Security-Enhanced Linux (SELinux) to enforce Mandatory Access Control (MAC) over all processes, even processes running with root/superuser privileges (a.k.a. Linux capabilities). SELinux enhances Android security, and contributions to it have been made by a number of companies and organizations; all Android code and contributors are publicly available for review on android.googlesource.com. With SELinux, Android can better protect and confine system services, control access to application data and system logs, reduce the effects of malicious software, and protect users from potential flaws in code on mobile devices.

Android includes SELinux in enforcing mode and a corresponding security policy that works by default across the Android Open Source Project. In enforcing mode, illegitimate actions are prevented and all potential violations are logged by the kernel to dmesg. Android device manufacturers should gather information about errors so they may refine their software and SELinux policies before enforcing them.

Background

SELinux can operate in one of two global modes: permissive mode, in which permission denials are logged but not enforced, and enforcing mode, in which permission denials are both logged and enforced. SELinux also supports a per-domain permissive mode in which specific domains (processes) can be made permissive while placing the rest of the system in global enforcing mode. A domain is simply a label identifying a process or set of processes in the security policy, where all processes labeled with the same domain are treated identically by the security policy. Per-domain permissive mode enables incremental application of SELinux to an ever-increasing portion of the system. Per-domain permissive mode also enables policy development for new services while keeping the rest of the system enforcing.

In Android 4.3, SELinux was fully permissive. In Android 4.4, SELinux was made enforcing for the domains for several root processes: installd, netd, vold and zygote. All other processes, including other services and all apps, remain in permissive mode to allow further evaluation and prevent failures in Android 4.4. Still, an errant application could trigger an action in a root process that is not allowed, thereby causing the process or the application to crash.

For this reason, device manufacturers should retain the default settings provided by Android and limit enforcing mode to system services only until they've resolved issues reported in dmesg. That said, device manufacturers may need to augment their SELinux implementation to account for their additions and other changes to the operating system. See the Customization section for instructions.

Mandatory access control

In conjunction 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.

Starting in Android 4.3, 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 the Use Cases section for more examples of threats and ways to address them with SELinux.

Implementation

Android's SELinux implementation is in enforcing mode - rather than the non-functional disabled mode or the notification-only permissive mode - to act as a reference and facilitate testing and development. Although enforcing mode is set globally, please remember this can be overridden on a per-domain basis as is in the case of the application domain.

SELinux for Android is accompanied by everything you need to enable SELinux now. You merely need to integrate the latest Android kernel and then incorporate the files found in the external/sepolicy directory:
https://android.googlesource.com/kernel/common/
https://android.googlesource.com/platform/external/sepolicy/

Those files when compiled comprise the SELinux kernel security policy and cover the upstream Android operating system. You should not need to modify the external/sepolicy files directly. Instead, add your own device-specific policy files within the /device/manufacturer/device-name/sepolicy directory.

Then just update your BoardConfig.mk makefile - located in the directory containing the sepolicy subdirectory - to reference the sepolicy subdirectory and any policy file once created, as shown below. The BOARD_SEPOLICY variables and their meaning is documented in the external/sepolicy/README file.

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

BOARD_SEPOLICY_UNION += \
        genfs_contexts \
        file_contexts \
        sepolicy.te

After rebuilding your device, it is enabled with SELinux. You can now either customize your SELinux policies to accommodate your own additions to the Android operating system as described in the Customization section or verify your existing setup as covered in the Validation section.

Customization

Once you've integrated this base level of functionality and thoroughly analyzed the results, you may add your own policy settings to cover your customizations to the Android operating system. Of course, these policies must still meet the Android Compatibility program requirements and not remove the default SELinux settings.

Manufacturers should not remove existing security settings. Otherwise, they risk breaking the Android SELinux implementation and the applications it governs. This includes third-party applications that will likely need to be improved to be compliant and operational. Applications must require no modification to continue functioning on SELinux-enabled devices.

See the Kernel Security Features section of the Android Compatibility Definition document for specific requirements:
http://source.android.com/compatibility/index.html

SELinux uses a whitelist approach, meaning all access must be explicitly allowed in policy in order to be granted. Since Android's default SELinux policy already supports the Android Open Source Project, OEMs are not required to modify SELinux settings in any way. If they do customize SELinux settings, they should take great care not to break existing applications. Here is how we recommend proceeding:

  1. Use the latest Android kernel.
  2. Adopt the principle of least privilege.
  3. Address only your own additions to Android. The default policy works with the Android Open Source Project codebase automatically.
  4. Compartmentalize software components into modules that conduct singular tasks.
  5. Create SELinux policies that isolate those tasks from unrelated functions.
  6. Put those policies in *.te files (the extension for SELinux policy source files) within the /device/manufacturer/device-name/sepolicy directory and use BOARD_SEPOLICY variables to include them in your build.
  7. Make new domains permissive initially. In Android 4.4 and earlier, this is done using a permissive declaration. In later versions of Android, per-domain permissive mode is specified using the permissive_or_unconfined() macro.
  8. Analyze results and refine your domain definitions.
  9. Remove the permissive declaration when no further denials appear in userdebug builds.

Once integrated, OEM Android development should include a step to ensure SELinux compatibility going forward. In an ideal software development process, SELinux policy changes only when the software model changes and not the actual implementation.

As device manufacturers begin to customize SELinux, they should first audit their additions to Android. If they've added a component that conducts a new function, the manufacturers will need to ensure the component meets the security policy applied by Android, as well as any associated policy crafted by the OEM, before turning on enforcing mode.

To prevent unnecessary issues, it is better to be overbroad and over-compatible than too restrictive and incompatible, which results in broken device functions. Conversely, if a manufacturer's changes will benefit others, it should supply the modifications to the default SELinux policy as a patch. If the patch is applied to the default security policy, the manufacturer will no longer need to make this change with each new Android release.

Use Cases

Here are specific examples of exploits to consider when crafting your own software and associated SELinux policies:

Symlinks - Because symlinks appear as files, they are often read just as that. This can lead to exploits. For instance, some privileged components such as init change the permissions of certain files, sometimes to be excessively open.

Attackers might then replace those files with symlinks to code they control, allowing the attacker to overwrite arbitrary files. But if you know your application will never traverse a symlink, you can prohibit it from doing so with SELinux.

System files - Consider the class of system files that should only be modified by the system server. Still, since netd, init, and vold run as root, they can access those system files. So if netd became compromised, it could compromise those files and potentially the system server itself.

With SELinux, you can identify those files as system server data files. Therefore, the only domain that has read/write access to them is system server. Even if netd became compromised, it could not switch domains to the system server domain and access those system files although it runs as root.

App data - Another example is the class of functions that must run as root but should not get to access app data. This is incredibly useful as wide-ranging assertions can be made, such as certain domains unrelated to application data being prohibited from accessing the internet.

setattr - For commands such as chmod and chown, you could identify the set of files where the associated domain can conduct setattr. Anything outside of that could be prohibited from these changes, even by root. So an application might run chmod and chown against those labeled app_data_files but not shell_data_files or system_data_files.

This section serves to guide you once you’ve decided to customize the SELinux policy settings. See the Customization section for steps. We recommend device manufacturers start with the default Android SELinux policy and make the minimum possible set of changes to address their additions to Android. Existing Android SELinux policy files are found in the root of the external/sepolicy directory.

Android upgraded its SELinux policy version to allow the SELinux mode to be set to permissive on a per-domain basis. For example, if you run all of your applications in a single domain, you could set that domain to be permissive and then have all other functions and their domains set to enforcing. Domains are associated with applications by the key used to sign each application. The mapping of app certificates to domains is specified via the mac_permissions.xml and seapp_contexts configuration files.

Here are the files you must create or edit in order to customize SELinux:

  • New SELinux policy source (*.te) files - Located in the <root>/device/manufacturer/device-name/sepolicy directory These files define domains and their labels. The new policy files get concatenated with the existing policy files during compilation into a single SELinux kernel policy file.

    Important:Do not alter the app.te file provided by the Android Open Source Project. Doing so risks breaking all third-party applications.

  • Updated BoardConfig.mk makefile - Located in the <device-name> directory containing the sepolicy subdirectory. It must be updated to reference the sepolicy subdirectory once created if it wasn’t in initial implementation.

  • Updated file_contexts - Located in the sepolicy subdirectory. It labels files and is managed in the userspace. As you create new policies, update this file to reference them. In order to apply new file_contexts, you must run restorecon on the file to be relabeled.

The remaining files in the sepolicy directory are either auto-generated or should remain static. The policy rules come in the form: allow domains types:classes permissions;, where:

  • Domain - A label for the process or set of processes.

  • Type - A label for the object (e.g. file, socket) or set of objects.

  • Class - The kind of object (e.g. file, socket) being accessed.

  • Permission - The operation (e.g. read, write) being performed.

  • And so an example use of this would follow the structure:
    allow appdomain app_data_file:file rw_file_perms;

    This says an application is allowed to read and write files labeled app_data_file. Note that this rule relies upon macros defined in the global_macros file, and other helpful macros can also be found in the te_macros file. Macros are provided for common groupings of classes, permissions and rules, and should be used whenever possible to help reduce the likelihood of failures due to denials on related permissions. During compilation, those overrides are concatenated to the existing SELinux settings and into a single security policy. These overrides add to the base security policy rather than subtract from existing settings.

    Once the new policy files and BoardConfig.mk updates are in place, the new policy settings are automatically built into the final kernel policy file.

    Validation

    Android strongly encourages OEMs to test their SELinux implementations thoroughly. As manufacturers implement SELinux, they should initially release their own policies in permissive mode. If possible, apply the new policy to a test pool of devices first.

    Once applied, make sure SELinux is running in the correct mode on the device by issuing the command: getenforce

    This will print the global SELinux mode: either Disabled, Enforcing, or Permissive. Please note, this command shows only the global SELinux mode. To determine the SELinux mode for each domain, you must examine the corresponding files.

    Then check for errors. Errors are routed as event logs to dmesg and viewable locally on the device. Manufacturers should examine the SELinux output to dmesg on these devices and refine settings prior to public release in permissive mode and eventual switch to enforcing mode. It is possible to capture the ongoing denial logs by running cat /proc/kmsg or to capture denial logs from the previous boot by running cat /proc/last_kmsg.

    With this output, manufacturers can readily identify when system users or components are in violation of SELinux policy. Manufacturers can then repair this bad behavior, either by changes to the software, SELinux policy, or both.

    Specifically, these log messages indicate what processes would fail under enforcing mode and why. Here is an example:

    denied  { connectto } for  pid=2671 comm="ping" path="/dev/socket/dnsproxyd"
    scontext=u:r:shell:s0 tcontext=u:r:netd:s0 tclass=unix_stream_socket
    

    Interpret this output like so:

    • The { connectto } above represents the action being taken. Together with the tclass at the end (unix_stream_socket) it tells you roughly what was being done to what. In this case, something was trying to connect to a unix stream socket.
    • The scontext (u:r:shell:s0) tells you what context initiated the action. In this case this is something running as the shell.
    • The tcontext (u:r:netd:s0) tells you the context of the action’s target. In this case, that’s a unix_stream_socket owned by netd.
    • The comm="ping" at the top gives you an additional hint about what was being run at the time the denial was generated. In this case, it’s a pretty good hint.

    Android is taking this information, analyzing it and refining its default security policy so that it works on a wide range of Android devices with little customization. With this policy, OEMs must only accommodate their own changes to the Android operating system.

    Then run the SELinux-enabled devices through the Android Compatibility Test Suite (CTS).

    As said, any new policies must still meet the Android Compatibility program requirements.

    Finally, if possible, turn on enforcing mode internally (on devices of employees) to raise the visibility of failures. Identify any user issues and resolve them.

    Help

    Device manufacturers are strongly encouraged to work with their Android account managers to analyze SELinux results and improve policy settings. Over time, Android intends to support common manufacturer additions in its default SELinux policy. For more information, contact security@android.com.