Android uses SELinux (Security-Enhanced Linux) to enforce mandatory access control (MAC). Unlike traditional Linux permissions where root can do anything, SELinux adds a second layer - even the root user is restricted unless the policy explicitly allows an action. This dramatically limits what a compromised process can do.
DAC vs MAC
- ▸DAC (Discretionary Access Control) - traditional Linux permissions: owner/group/others with rwx bits. A root process bypasses all of these
- ▸MAC (Mandatory Access Control) - SELinux. Every process has a label (type), every file/socket has a label, and a policy defines exactly which types can access which other types. Even root is bound by MAC
SELinux Core Concepts
SELinux Label + Policy Check
Process: adbd File: /data/local/tmp
Label: u:r:adbd:s0 Label: u:object_r:shell_data_file:s0
│ │
└──────────────┬───────────────┘
▼
SELinux Policy Check
'allow adbd shell_data_file:file { read write }'
│
┌──────────┴──────────┐
│ ALLOWED │ DENIED + audit log
└─────────────────────┘How Policies are Written
SELinux policies in AOSP are written in .te (type enforcement) files inside system/sepolicy/ (for core AOSP) and device/vendor/ (for vendor-specific policies). Each rule is an 'allow' statement.
Policy Rule Syntax
allow <source_type> <target_type>:<class> { <permissions> };
Examples:
allow cameraserver camera_device:chr_file { read write ioctl };
# ↑ ↑ ↑ ↑
# process type file label file allowed
# class operations
allow system_server app_data_file:dir { search getattr };
neverallow untrusted_app kernel:process { ptrace };Enforcing vs Permissive Mode
- ▸Enforcing mode - denials actually block the action. Production devices always run in enforcing mode
- ▸Permissive mode - denials are only logged (auditd), nothing is blocked. Used during development to find policy gaps
- ▸adb shell getenforce - check current mode
- ▸adb shell setenforce 0 - switch to permissive (requires root, debug builds only)
Working with SELinux Denials
- ▸Find denials: adb shell dmesg | grep 'avc: denied'
- ▸Or: adb logcat | grep 'avc'
- ▸Use audit2allow tool to convert a denial message into a policy rule automatically
- ▸Never blindly allow everything - audit2allow rules should be reviewed carefully for security
Tip: When your new HAL or daemon is being denied, run in permissive mode first, collect all avc denials, then write the minimal policy that allows exactly what you need. Never write 'allow myprocess domain:file *;' - always be specific.