Android Upgrades, Custom ROMs (LineageOS), & Kernels

From Permissive to Enforcing: Building a Secure SELinux Policy for a Custom Android Daemon

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction

In the evolving landscape of Android security, SELinux (Security-Enhanced Linux) stands as a critical pillar, enforcing Mandatory Access Control (MAC) over all processes and files. While Android provides robust default SELinux policies, integrating custom daemons or services into a custom ROM (like LineageOS) or an AOSP build often presents a unique challenge: making them function securely under SELinux’s watchful eye. This guide delves into the intricate process of transitioning a custom Android daemon from a permissive SELinux state to a fully enforcing one, ensuring your custom components enhance, rather than compromise, device security. We will focus on understanding SELinux contexts and crafting precise policy rules.

Understanding SELinux in Android

SELinux Fundamentals

SELinux operates on the principle of least privilege, defining exactly what subjects (processes) can do to what objects (files, sockets, other processes). This is achieved through security contexts, which are labels attached to every subject and object in the system. A security context typically comprises four parts: user:role:type:level. In Android, the common format is u:object_r:type:s0 for objects and u:r:type:s0 for processes. Policies are a set of rules that dictate interactions between these contexts. When a process attempts an action (e.g., reading a file) that isn’t explicitly allowed by the policy, SELinux generates an “Access Vector Cache” (AVC) denial.

Android’s SELinux Architecture

Android’s SELinux policy is compiled into a binary format and loaded during boot. It primarily resides in the system/sepolicy directory within the AOSP source tree. This directory contains various .te (type enforcement) files defining domains, types, and rules, along with file_contexts for labeling files and directories. A crucial aspect is the distinction between permissive and enforcing modes:

  • Permissive Mode (setenforce 0): SELinux logs denials but does not block them. This is invaluable for development, allowing you to identify all necessary permissions without breaking functionality.
  • Enforcing Mode (setenforce 1): SELinux logs denials and actively blocks unauthorized actions, enforcing the security policy. This is the desired operational mode for a secure system.

Identifying Your Daemon’s Requirements

The first step in crafting a robust SELinux policy is to understand every resource your custom daemon needs to access. This is best done by running the daemon in permissive mode and logging all SELinux denials. Let’s assume you have a custom daemon, my_daemon, located at /system/bin/my_daemon, which needs to read a configuration file at /data/local/tmp/config.txt and interact with a custom character device /dev/my_device.

To begin, boot your Android device into a state where SELinux is permissive (if not already, you can often achieve this temporarily via adb shell setenforce 0 if you have root access, or by modifying kernel boot arguments if building from source). Then, execute your daemon and monitor the kernel logs:

adb shell setenforce 0 adb shell /system/bin/my_daemon & adb shell dmesg -c > /sdcard/boot_log.txt adb shell sleep 10 # Let the daemon run and generate denials adb shell dmesg | grep 'avc: denied'

The dmesg -c command clears the kernel ring buffer before starting your daemon, ensuring you only capture relevant denials. Analyze the output carefully. Each avc: denied entry provides critical information: the process (comm), the attempted action (e.g., read, write, open, getattr), the target object (path), its current security context (tcontext), and the class of the object (tclass).

Step-by-Step Policy Creation

We’ll create our policy files within the AOSP source tree, typically in a directory like device/<vendor>/<device>/sepolicy or system/sepolicy. For custom components, it’s often best practice to define a new policy directory and add it to BOARD_SEPOLICY_DIRS in your device’s BoardConfig.mk.

Step 1: Define the Domain and Executable Type

First, we need to create a new SELinux type for our daemon’s process (its domain) and its executable file. Create a file, e.g., my_daemon.te:

# my_daemon.te type my_daemon, domain; # This defines the process domain type my_daemon_exec, exec_type, file_type, system_file_type; # This defines the executable file type # Optional: Inherit from common daemon attributes if applicable # For example, if it's a regular system daemon, you might allow it to run as unconfineddomain # or inherit from appdomain attributes if it's user-facing. # In this example, we keep it minimal.

Step 2: Assign File Contexts

Next, we need to tell SELinux how to label our daemon’s executable, the custom device node, and the configuration file. Edit the appropriate file_contexts file in your policy directory (or create a new my_daemon_file_contexts and include it in your build system). We also need to define types for these new contexts.

First, define new types for the device and config file. Create my_device.te and my_daemon_config_file.te:

# my_device.te type my_device_type, dev_type, file_type; # my_daemon_config_file.te type my_daemon_config_file, file_type, data_file_type;

Then, update file_contexts:

# my_daemon_file_contexts /system/bin/my_daemon     u:object_r:my_daemon_exec:s0 /dev/my_device            u:object_r:my_device_type:s0 /data/local/tmp/config.txt    u:object_r:my_daemon_config_file:s0

Ensure your build system picks up these new .te files and the updated file_contexts. This typically involves adding them to BOARD_SEPOLICY_DIRS and BOARD_SEPOLICY_UNION_DIRS in your BoardConfig.mk.

Step 3: Grant Permissions

Now, based on the denials observed in permissive mode, we add the necessary allow rules to my_daemon.te. Let’s assume our dmesg output showed denials similar to these:

  • avc: denied { read write } for pid=... comm=

    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