Introduction to SELinux in Android Automotive
Android Automotive OS (AAOS) extends Android’s robust security model to the vehicle domain, where safety and security are paramount. At the heart of this security architecture lies SELinux (Security-Enhanced Linux), a mandatory access control (MAC) system that fine-tunes what processes can access which resources. While AOSP provides a strong baseline policy, custom hardware, proprietary services, and unique automotive functionalities often necessitate the definition and integration of advanced, custom SELinux policy modules. This article delves into the expert-level process of crafting, compiling, and loading such modules to enhance the security posture of your Android Automotive system.
Understanding and extending SELinux is critical for mitigating attack surfaces, enforcing least privilege, and ensuring system integrity in a complex automotive environment where multiple ECUs, network interfaces, and user-facing applications interact.
Prerequisites and Environment Setup
Before diving into policy creation, ensure you have a fully functional Android Open Source Project (AOSP) build environment capable of compiling Android Automotive. This typically involves:
- A Linux-based workstation (Ubuntu recommended) with sufficient RAM and storage.
- AOSP source code synced for your target Android Automotive version (e.g., Android 11, 12, or newer).
- Knowledge of basic AOSP build commands (
source build/envsetup.sh,lunch,mormka). - Familiarity with foundational SELinux concepts: types, domains, permissions, and rules.
We’ll assume you’re working within an AOSP tree, capable of building a complete Android Automotive image for a reference board or an emulated device.
Understanding Android Automotive SELinux Policy Structure
Android’s SELinux policy is highly modular. The core policy is defined in system/sepolicy, while device-specific and vendor policies reside in directories like device/<vendor>/<board>/sepolicy. Custom policy modules are typically added within your device’s sepolicy directory structure.
Key SELinux file types you’ll encounter:
.te(Type Enforcement) files: Define types, domains, and their associated rules. This is where most of your custom policy logic will reside..if(Interface) files: Define macros that encapsulate common rule sets, making policies more readable and reusable..fc(File Context) files: Map file paths to specific SELinux types, determining the default context for files and directories.file_contexts.bin: The compiled binary version of all.fcfiles.sepolicy: The compiled binary version of all.tefiles.
Crafting a Custom SELinux Policy Module
Let’s consider a scenario where you’ve developed a new proprietary automotive service, my_automotive_service, which needs to run as a unique domain, read from a specific hardware device (/dev/my_sensor), and write logs to /data/vendor/my_service_logs/. The service will be started by init.
1. Define the Domain and Service Type (.te file)
Create a new directory for your custom policy, for example, device/<vendor>/<board>/sepolicy/my_service. Inside, create my_service.te:
# my_service.te
type my_automotive_service, domain;
type my_automotive_service_exec, exec_type, file_type, system_file_type;
init_daemon_domain(my_automotive_service)
# Allow the service to read its own executable
allow my_automotive_service my_automotive_service_exec:file { read execute map open getattr };
# Allow accessing /dev/my_sensor
type my_sensor_device, dev_type;
allow my_automotive_service my_sensor_device:chr_file { read write open getattr };
# Allow writing to its log directory
type my_service_log_file, file_type, data_file_type;
type my_service_log_dir, file_type, data_file_type;
allow my_automotive_service my_service_log_dir:dir { create search add_name write remove_name rmdir };
allow my_automotive_service my_service_log_file:file { create read write open getattr append unlink };
# Allow standard interactions (e.g., binder, network sockets, logging)
allow my_automotive_service self:{ capability { dac_override net_raw } };
allow my_automotive_service domain:{ dir { read open } };
allow my_automotive_service ashmem_device:chr_file { execute read write map open };
allow my_automotive_service ion_device:chr_file { ioctl read write open };
allow my_automotive_service binder_device:chr_file { ioctl read write open };
allow my_automotive_service logd:binder call;
allow my_automotive_service system_server:binder call;
2. Define File Contexts (.fc file)
Create file_contexts in the same directory (or add to your main device file_contexts). This maps file paths to the SELinux types:
# file_contexts
/vendor/bin/my_automotive_service u:object_r:my_automotive_service_exec:s0
/dev/my_sensor u:object_r:my_sensor_device:s0
/data/vendor/my_service_logs(/.*)? u:object_r:my_service_log_dir:s0
/data/vendor/my_service_logs/.* u:object_r:my_service_log_file:s0
3. Initialize the Service (.rc file)
Ensure your service’s init script correctly specifies its SELinux domain. In your my_automotive_service.rc file (e.g., device/<vendor>/<board>/my_automotive_service.rc):
# my_automotive_service.rc
service my_automotive_service /vendor/bin/my_automotive_service
class core
user system
group system
seclabel u:r:my_automotive_service:s0
capabilities NET_RAW DAC_OVERRIDE
onrestart restart zygote
Integrating and Compiling into AOSP
To integrate your custom policy into the AOSP build, you need to inform the build system about its existence. This is typically done in your device’s BoardConfig.mk or Android.bp files.
1. Modify BoardConfig.mk
Navigate to your device’s BoardConfig.mk (e.g., device/<vendor>/<board>/BoardConfig.mk). You’ll usually find existing `BOARD_SEPOLICY_DIRS` or similar variables. Add your new policy directory:
# BoardConfig.mk
# ... existing configurations ...
BOARD_SEPOLICY_DIRS +=
device/<vendor>/<board>/sepolicy/my_service
# For pre-Android 10/11, you might also need to specify:
BOARD_VENDOR_SEPOLICY_DIRS +=
device/<vendor>/<board>/sepolicy/my_service
The AOSP build system will automatically pick up all .te, .if, and .fc files from these directories and compile them into the final sepolicy and file_contexts.bin images.
2. Compile the AOSP Image
With the BoardConfig.mk updated, rebuild your Android Automotive image:
source build/envsetup.sh
lunch <your_device_target>
mka sepolicy
mka -j$(nproc) # Or simply 'mka' to build everything
The mka sepolicy command specifically rebuilds the SELinux policy, which is useful for faster iteration during policy development.
Deploying and Debugging the Policy Module
1. Flashing the New Policy
Once compiled, the updated sepolicy and file_contexts.bin are part of your system image. Flash the updated image to your Android Automotive device or emulator:
adb reboot bootloader
fastboot flashall
Alternatively, if you only changed policy and not other partitions, you might be able to selectively flash the `vendor` or `system` partition depending on your device’s configuration and Android version.
2. Verifying and Debugging
After the device boots, verify the SELinux status:
adb shell getenforce # Should return 'Enforcing'
adb shell dmesg | grep audit # Look for AVC denials
adb shell logcat | grep audit # Also check logcat for denials
If your service isn’t working as expected, or if you see AVC denial messages, it indicates a missing or incorrect SELinux rule. An AVC denial looks like this:
type=1400 audit(1678886400.0:123): avc: denied { read } for pid=1234 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 →