AOSP Framework & Internals
4 min read

Writing Allow Rules

Learn about Writing Allow Rules.

Decoding the SELinux Block

You build a new hardware integration, compile the OS, and flash it to a device. The feature immediately crashes. Checking the logs reveals an avc: denied message. The kernel blocks your process from accessing a resource because SELinux operates on a strict default deny philosophy.

Resolving these denials requires explicit authorization. An allow rule serves as the fundamental building block of Type Enforcement policy. It maps a subject to an object and defines exactly what operations are permitted. You must provide these explicit instructions to the security server to authorize any action.

The syntax requires four specific components. You define the subject context, the object context, the object class, and the requested permissions.

allow hal_audio_default audio_data_file:file { read write open getattr };

This rule permits the hal_audio_default domain to perform read, write, open, and attribute-checking operations on files labeled as audio_data_file. The most common mistake engineers make is requesting write access when only read is truly needed. Security reviewers will reject policies that violate the principle of least privilege. A compromised process cannot suddenly open network sockets if its policy only allows reading local audio files.

Categorizing System Resources

Treating a network socket exactly like a text file creates massive security loopholes. The kernel needs precise boundaries to enforce meaningful rules. Without these boundaries, a single read permission could accidentally grant access to sensitive hardware interfaces.

Android SELinux policy uses object classes to group resources into logical buckets. Each class defines a unique set of permissions that make sense for that specific resource type. Standard files use the file class and accept permissions like read or write. Directories use the dir class and require permissions like search.

You must map your target resource to its correct class when writing a rule. Inter-process communication relies on the binder class with call or transfer permissions. Hardware integrations frequently interact with character device nodes using the chr_file class and ioctl permissions.

You are about to see a visualization of a process request flowing through the SELinux policy check. This clarifies why every component of the rule syntax is mandatory. Notice how the policy check acts as a strict gatekeeper between the subject domain and the target object.

Selecting the correct class and permission prevents overly broad rules. Security reviewers reject policies that request generic access when only a specific operation is required.

Condensing Policy with Macros

Writing individual permissions for every single file access creates massive policy files. A simple file read operation usually requires read, open, and getattr permissions simultaneously. Repeating these triplets across hundreds of files introduces formatting errors and makes the policy impossible to maintain.

The platform solves this verbosity by providing predefined macros. These macros live in the te_macros file and bundle common permission sets together. They abstract away the repetitive syntax of standard rules.

Instead of manually writing out every required permission, you call a macro with your domain and target type.

r_dir_file(hal_audio_default, audio_data_file)

This single line expands into all the necessary rules for searching a directory and reading the files within it. Similarly, setting up a Binder connection requires complex bi-directional permissions.

binder_call(client_domain, server_domain)

This macro generates the call and transfer rules for both the client and the server automatically. A frequent mistake is assuming this macro also grants access to the underlying service data, when it only authorizes the IPC connection itself. Using macros reduces boilerplate code and guarantees you do not miss hidden required permissions.

Tip: Always search for an existing macro before writing a raw allow rule. Macros enforce platform best practices automatically.

The Path Forward

Writing SELinux policy requires translating abstract system behaviors into concrete rules. You define the subject, specify the object class, and grant precise permissions. Macros keep this policy maintainable as the system grows.

Resolving denials is a routine part of platform engineering. You write explicit allow rules to fix immediate crashes. But how do you know which rules to write without guessing? The answer lies hidden in the audit logs.