Android System Securing, Hardening, & Privacy

Process Isolation on Android: Implementing Custom SELinux Domains for High-Security Apps

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction to SELinux on Android

Android’s security model is built on several layers, with Linux’s Discretionary Access Control (DAC) forming a fundamental base. However, DAC alone isn’t sufficient for enterprise-grade or high-security applications due to its ‘owner-can-do-anything’ limitations. This is where Security-Enhanced Linux (SELinux) comes into play, providing Mandatory Access Control (MAC) at the kernel level. On Android, SELinux enforces strict policies on all processes, files, and resources, significantly hardening the system against privilege escalation and malicious attacks.

By default, Android assigns applications to generic SELinux domains like untrusted_app or platform_app. While these provide a baseline level of isolation, they often grant more permissions than a truly high-security application requires or are too restrictive for specific, privileged tasks. For critical applications handling sensitive data or performing system-level operations, a generic domain introduces unnecessary attack surface.

The Imperative for Custom SELinux Domains

The principle of least privilege dictates that an entity should only have access to the resources absolutely necessary to perform its function. For high-security Android applications, this means moving beyond the default SELinux contexts. Custom SELinux domains allow developers and system integrators to define extremely granular security policies tailored precisely to an application’s specific needs, thereby minimizing its attack surface significantly.

Consider an application that manages cryptographic keys in a hardware-backed keystore, or a system service that controls a specific peripheral. Assigning such components to a broad domain like untrusted_app would expose them to a larger set of permissions and potential interactions than needed, increasing risk. Conversely, a custom domain can restrict the application to only access its own data files, communicate with specific system services, and utilize only the necessary kernel capabilities.

Use Cases for Custom Domains:

  • Secure Enclaves: Isolating processes that handle cryptographic operations or sensitive data.
  • Critical System Services: Providing minimal privileges to custom daemon processes.
  • Specialized Device Access: Granting precise access to unique hardware components.
  • Proprietary Application Protection: Preventing unauthorized access or modification of app internals.

Anatomy of an Android SELinux Policy Module

To implement a custom SELinux domain, you need to understand the core components of an SELinux policy module within the Android Open Source Project (AOSP) build environment:

  1. Type Enforcement (.te) files: These define new types (domains for processes, or types for files/objects) and specify the rules (permissions) governing interactions between these types.
  2. File Contexts (file_contexts): These map file paths and properties (like directory names or regular expressions) to specific SELinux types. This is crucial for labeling an application’s data directory and executable files with its custom type.
  3. SEApp Contexts (seapp_contexts): This file associates Android application packages (identified by their package name, UID, or signing key) with a specific SELinux domain type.

Step-by-Step: Implementing a Custom Domain

This guide assumes you have access to the AOSP source code and a build environment to compile a custom Android image.

Prerequisites:

  • AOSP source tree synchronized.
  • Android build environment configured.
  • Basic understanding of SELinux concepts.

1. Define the Custom Domain Type (.te file)

Create a new .te file, for example, my_secure_app.te, in your device’s SELinux policy directory (e.g., device/<vendor>/<device>/sepolicy/private/ or system/sepolicy/private/ for system services). This file will declare your new domain and initial rules.

# my_secure_app.te (Example: device/vendor/device/sepolicy/private/my_secure_app.te) # Declares the new type for our secure application type my_secure_app; # Designates this type as a domain for processes domain_type(my_secure_app); # Inherit common app domain permissions (start with broad, then refine) app_domain(my_secure_app) # Allow our app to run executables marked with its own type allow my_secure_app my_secure_app_exec:file { execute_no_trans open read getattr }; # Allow our app to manage its own process context allow my_secure_app self:process { fork transition }; # Example: Allow logging to logd. Refine as needed. allow my_secure_app logd:unix_stream_socket connectto; # Allow binding to loopback for internal IPC (adjust if no network needed) allow my_secure_app self:tcp_socket create_socket_perms; allow my_secure_app self:udp_socket create_socket_perms; allow my_secure_app self:rawip_socket create_socket_perms; allow my_secure_app node:tcp_socket { node_bind }; allow my_secure_app port:tcp_socket { name_bind };

2. Define File Contexts (file_contexts)

Next, define the SELinux type for your application’s data directory and any specific files it manages. You’ll modify your device’s file_contexts file (e.g., device/<vendor>/<device>/sepolicy/private/file_contexts) or create a new entry.

# file_contexts (Example: device/vendor/device/sepolicy/private/file_contexts) # Add a type for the application's data directory and files /data/data/com.example.mysecureapp(/.*)? u:object_r:my_secure_app_data_file:s0 # Add a type for its executable (if it's a native binary in system partition) # /system/bin/my_secure_app u:object_r:my_secure_app_exec:s0

Then, define my_secure_app_data_file and my_secure_app_exec in your my_secure_app.te or a separate .te file:

# my_secure_app.te (continued) type my_secure_app_data_file, file_type, data_file_type; type my_secure_app_exec, exec_type, file_type; # Allow the app to manage its own data directory allow my_secure_app my_secure_app_data_file:dir { create search add_name remove_name rmdir write read open getattr }; allow my_secure_app my_secure_app_data_file:file { create read write rename link unlink append getattr setattr lock open };

3. Assign the Domain to the Application (seapp_contexts)

This step links your Android application’s package name to the newly defined SELinux domain. Edit your device’s seapp_contexts file (e.g., device/<vendor>/<device>/sepolicy/private/seapp_contexts).

# seapp_contexts (Example: device/vendor/device/sepolicy/private/seapp_contexts) # Assign 'com.example.mysecureapp' to 'my_secure_app' domain user=_app domain=my_secure_app app_data_file=my_secure_app_data_file seinfo=platform name=com.example.mysecureapp
  • user=_app: Matches application UIDs.
  • domain=my_secure_app: Specifies the process domain for the app.
  • app_data_file=my_secure_app_data_file: Specifies the file type for the app’s /data/data directory.
  • seinfo=platform: Used for applications signed with the platform key. Adjust if your app uses a different signing key (e.g., seinfo=presigned for pre-installed apps).
  • name=com.example.mysecureapp: The package name of your application.

4. Compile and Integrate the Policy

For the new policies to take effect, they must be compiled into the SELinux policy image and flashed to your device. Ensure your BoardConfig.mk includes your private sepolicy directory:

# device/<vendor>/<device>/BoardConfig.mk BOARD_SEPOLICY_DIRS += device/<vendor>/<device>/sepolicy/private

Now, rebuild the AOSP. The SELinux policy is typically part of the boot.img or vendor_boot.img, or a dedicated vendor.img partition.

# Clean previous build artifacts (optional, but recommended for SELinux changes) make clean # Update API files make update-api # Build only the SELinux policy (for quick iterations, then build boot.img) make selinux_policy # Full AOSP build (will include the new policy) source build/envsetup.sh lunch <target> # e.g., aosp_arm64-userdebug make -j$(nproc)

After the build completes, flash the updated images (e.g., boot.img, vendor.img) to your device.

5. Writing Specific Rules (Principle of Least Privilege)

The initial rules in my_secure_app.te are a starting point. The real work involves reducing permissions to the bare minimum. Deploy your app, run its functionality, and monitor for SELinux denials. Denials will appear in the kernel log.

adb logcat -b all | grep

Android Mobile Specs & Compare Directory

Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!

Compare Devices Specs →
Google AdSense Inline Placement - Content Footer banner