Advanced OS Customizations & Bootloaders

Reverse Engineering Android SELinux AVC Denials: Crafting Precise Policy Rules

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction to SELinux on Android

Security-Enhanced Linux (SELinux) is a mandatory access control (MAC) system implemented in the Linux kernel. On Android, SELinux is critical for enforcing isolation between applications, protecting system resources, and mitigating the impact of security vulnerabilities. Unlike discretionary access control (DAC), which allows users to control permissions for their own files, SELinux policies define comprehensive rules that govern all interactions between processes and resources, regardless of traditional Unix permissions. When an operation violates an SELinux policy rule, the kernel denies the action and logs an Access Vector Cache (AVC) denial message.

Understanding and resolving these AVC denials is a fundamental skill for anyone developing custom Android ROMs, porting new devices, or deeply customizing system services. Blanket permissive policies (`setenforce 0`) undermine the security benefits and are not suitable for production environments. The goal is to craft precise, minimal policy rules that allow necessary operations without weakening the overall security posture.

Identifying and Analyzing AVC Denials

The first step in debugging an SELinux issue is to locate the AVC denial messages. These are typically found in the kernel’s message buffer or Android’s logcat.

Accessing Kernel Logs (dmesg)

You can retrieve kernel messages directly from a rooted Android device or an ADB shell:

adb shell
su
dmesg | grep audit

Or, if you prefer to pull the logs to your computer:

adb shell dmesg > dmesg.log
grep audit dmesg.log

Accessing Android Logcat

Logcat provides a more comprehensive view of system events, including SELinux denials, especially if they are propagated through Android’s logging system:

adb logcat | grep "avc: denied"

A typical AVC denial message will look something like this:

audit: avc: denied { read } for pid=1234 comm="my_service" name="some_file" dev="tmpfs" ino=5678 scontext=u:r:my_service:s0 tcontext=u:object_r:system_file:s0 tclass=file permissive=0

Let’s break down the key components of this message:

  • avc: denied { read }: The specific permission that was denied (e.g., `read`, `write`, `execute`, `open`, `getattr`).
  • pid=1234 comm="my_service": The process ID and command name of the process attempting the action.
  • name="some_file": The name of the file or resource being accessed.
  • scontext=u:r:my_service:s0: The Security Context of the Subject (the process). This tells you the domain of the process.
  • tcontext=u:object_r:system_file:s0: The Security Context of the Target (the resource). This tells you the type of the resource.
  • tclass=file: The Target Class (e.g., `file`, `dir`, `socket`, `service_manager`, `binder`).
  • permissive=0: Indicates SELinux is in enforcing mode. If `permissive=1`, the action would have been allowed, but an alert would still be logged.

Crafting Precise Policy Rules

The goal is to write a rule that permits the specific `read` action for `my_service` on `system_file` of `file` class, and nothing more.

Step 1: Initial Policy Generation (audit2allow)

The `audit2allow` tool is a useful starting point for generating basic SELinux policy rules from denial messages. While convenient, its output often generates overly broad rules, so it should be used with caution and its suggestions always reviewed and refined.

You can feed the denial message directly into `audit2allow`:

echo 'audit: avc: denied { read } for pid=1234 comm="my_service" name="some_file" dev="tmpfs" ino=5678 scontext=u:r:my_service:s0 tcontext=u:object_r:system_file:s0 tclass=file permissive=0' | audit2allow

This might produce an output like:

#============= my_service =============
allow my_service system_file:file { read };

This is a good starting point, but let’s consider how to make it more precise.

Step 2: Understanding SELinux Policy Language

SELinux policies are written in a specific language. Key concepts include:

  • Type Enforcement (TE): The core of SELinux, defining rules based on types.
  • Types: Labels assigned to subjects (processes) and objects (files, sockets, etc.). For example, `my_service` is a subject type, `system_file` is an object type.
  • Classes: Represent categories of objects, like `file`, `dir`, `socket`.
  • Permissions: Specific actions within a class, like `read`, `write`, `execute`.
  • Attributes: Groups of types that can be referenced collectively in rules.

The basic rule structure is:

allow SOURCE_TYPE TARGET_TYPE:CLASS { PERMISSIONS };

Where:

  • SOURCE_TYPE is the `scontext` type (e.g., `my_service`).
  • TARGET_TYPE is the `tcontext` type (e.g., `system_file`).
  • CLASS is the `tclass` (e.g., `file`).
  • PERMISSIONS are the denied actions (e.g., `read`).

Step 3: Refining the Policy Rule

The `audit2allow` output `allow my_service system_file:file { read };` is often too broad. If `system_file` refers to many files, `my_service` will be able to read all of them. We want to grant access only to `some_file` if possible.

First, identify the exact path of `some_file`. If it’s a specific configuration file in `/data/misc/`, for example, you might want to create a new type for it.

Creating a New File Type

Let’s say `some_file` is located at `/data/misc/my_app/config.json`. We would first define a type for this specific file or directory in a `.te` file (e.g., `my_service.te`):

type my_app_config_file, file_type, data_file_type;

# Or, if it's a directory
type my_app_config_dir, file_type, data_file_type, core_data_file_type;

Then, in a `file_contexts` file (e.g., `my_service_file_contexts` or `device/manufacturer/device-name/sepolicy/file_contexts`), we map the path to this new type:

/data/misc/my_app/config.json u:object_r:my_app_config_file:s0
/data/misc/my_app(/.*)? u:object_r:my_app_config_dir:s0

Now, your policy rule becomes much more precise:

allow my_service my_app_config_file:file { read };

This grants `my_service` read access *only* to files labeled `my_app_config_file`, not all `system_file` types.

Considering Neverallow Rules

Android’s SELinux policy extensively uses `neverallow` rules, which explicitly forbid certain operations. If your precise rule still results in denials or doesn’t work, it might be conflicting with a `neverallow` rule. These rules prevent common security holes. You can check for `neverallow` rules using `sepolicy-analyze` on your compiled policy (`sepolicy`).

sepolicy-analyze neverallow system/etc/selinux/plat_sepolicy.cil

Circumventing `neverallow` rules is generally discouraged as it often indicates a fundamental security design flaw in your approach. Re-evaluate if the operation is truly necessary or if there’s a more secure alternative.

Applying and Testing the New Policy

Once you’ve crafted your `.te` and `file_contexts` rules, you need to compile them into your device’s SELinux policy. This typically involves placing your `.te` and `file_contexts` files in the appropriate `device/manufacturer/device-name/sepolicy/` directory within your AOSP build tree and recompiling the system image.

# Example build commands from AOSP root
source build/envsetup.sh
TARGET_PRODUCT=your_device_name lunch
m -j$(nproc)

After flashing the new system image, re-run the problematic operation and check `logcat` or `dmesg` again for any new AVC denials. This iterative process of identify-analyze-craft-test is crucial for developing robust SELinux policies.

Conclusion

Mastering SELinux policy development on Android is an essential skill for system integrators and advanced developers. By systematically reverse engineering AVC denials, leveraging tools like `audit2allow` judiciously, and understanding the nuances of the SELinux policy language, you can craft precise, secure, and maintainable policies. Always strive for the principle of least privilege, granting only the necessary permissions and refining broad rules into highly specific ones to maintain the integrity and security of the Android platform.

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