Android IoT, Automotive, & Smart TV Customizations

From Permissive to Enforcing: Hardening Android Automotive AOSP with Custom SELinux Domains

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction: The Imperative of SELinux in Android Automotive

Android Automotive OS (AAOS) is rapidly becoming the foundational software for in-vehicle infotainment and critical vehicle functions. While Android provides a robust security model, relying solely on default configurations or permissive SELinux policies in a production automotive environment is a significant risk. Security-Enhanced Linux (SELinux) is a mandatory access control (MAC) system that provides fine-grained control over what processes can access which resources, offering a crucial layer of defense against vulnerabilities and malicious attacks. This article delves into the advanced process of hardening Android Automotive AOSP by transitioning custom services and applications from permissive to enforcing SELinux domains, ensuring a robust and secure in-car experience.

The journey from a permissive SELinux state, where denials are merely logged, to an enforcing state, where they are actively blocked, is essential for any production-grade automotive system. It involves understanding existing policies, identifying custom service behaviors, and meticulously crafting new rules.

Understanding SELinux in Android Automotive

SELinux operates on the principle of Type Enforcement, where every process and file is labeled with a specific security context (e.g., u:object_r:system_file:s0 for system files, u:r:untrusted_app:s0 for untrusted applications). The SELinux policy, compiled into a binary file (sepolicy), defines the rules that govern interactions between these contexts. In Android, this policy is enforced by the kernel.

Key components of Android’s SELinux policy include:

  • .te files: Type Enforcement files, defining types, domains, and rules.
  • file_contexts: Maps file paths to their SELinux contexts.
  • seapp_contexts: Defines SELinux contexts for Android applications based on their UIDs and signatures.
  • genfs_contexts: Labels file systems that don’t support extended attributes.

A permissive domain logs all access denials (AVC denials) but does not prevent the access. While useful for development and debugging, it leaves the system vulnerable. An enforcing domain actively blocks unauthorized access, making it critical for production.

Phase 1: Identifying Permissive Domains and AVC Denials

Before moving to enforcing, it’s crucial to understand the access patterns of your custom services and applications. Start by identifying if any custom domains are currently running in permissive mode. While AOSP generally targets full enforcement, custom additions might inadvertently introduce permissive modes or trigger unexpected denials.

Checking Current SELinux Status

On your Android Automotive device, use adb shell to check the global SELinux status:

adb shell getenforce

If it returns Enforcing, the system is globally enforced. However, individual domains might still be permissive. To list all permissive domains:

adb shell su 0 grep 'permissive' /sys/fs/selinux/policy/policy_config

Collecting AVC Denials

When your custom service runs in permissive mode, any access it attempts that would normally be denied by an enforcing policy is logged as an AVC denial. You can capture these logs using dmesg or logcat:

adb shell dmesg | grep 'avc: 'adb shell logcat | grep 'avc: '

A typical AVC denial log might look like this:

avc: denied { read } for pid=1234 comm="mydiagservice" name="some_file" dev="dm-0" ino=5678 scontext=u:r:mydiag_service:s0 tcontext=u:object_r:system_file:s0 tclass=file permissive=1

This log indicates that a process running in the mydiag_service context attempted to read a file labeled system_file and was denied. The permissive=1 flag confirms it was logged but not blocked.

Phase 2: Defining Custom SELinux Domains and Policies

Once you’ve identified the denials, the next step is to define or refine your SELinux policy. For a new custom service (e.g., a vehicle diagnostics service), you’ll likely create a new domain.

Step 2.1: Create a New Type Enforcement (.te) File

Navigate to your device’s sepolicy directory in the AOSP source (e.g., device/<manufacturer>/<board>/sepolicy/ or system/sepolicy/private). Create a new .te file for your service, for example, mydiagnosticservice.te:

# mydiagnosticservice.te# Declare the domain type for our diagnostic service.type mydiag_service, domain;# Allow the service to start and runinit_daemon_domain(mydiag_service)allow mydiag_service self:capability { sys_nice chown };# Allow it to read system propertiesallow mydiag_service system_prop:property_service { get };# Allow it to read/write to its own data directoryallow mydiag_service mydiag_service_data_file:file { read write open create getattr setattr };# Define custom types for resources it interacts withtype mydiag_device, dev_type, fs_type;type mydiag_socket, sock_file;

Step 2.2: Define File Contexts

Next, you need to label the executable, libraries, and data files associated with your service. Create a mydiagnosticservice_file_contexts file in the same directory:

# mydiagnosticservice_file_contexts/vendor/bin/mydiagservice        u:object_r:mydiag_service_exec:s0/data/mydiagservice(/.*)?      u:object_r:mydiag_service_data_file:s0/dev/mydiag_driver          u:object_r:mydiag_device:s0

This labels the executable binary as mydiag_service_exec and its data directory as mydiag_service_data_file. Remember to add rules in mydiagnosticservice.te to allow mydiag_service to execute mydiag_service_exec and access mydiag_service_data_file.

Step 2.3: Integrate into AOSP Build

To ensure your new policy files are included, you’ll need to update the BoardConfig.mk or BoardConfig-<device>.mk for your device. For instance:

# BoardConfig.mk or device.mk referencing your new policy filesBOARD_SEPOLICY_DIRS +=     device/<manufacturer>/<board>/sepolicySELINUX_IGNORE_NEVERALLOW_POLICY := true # During initial development, set to true to ignore 'neverallow' violations. Remove for final builds.

Also, ensure your service is started with the correct context. If it’s an init service, modify its .rc file:

# mydiagservice.rcservice mydiagservice /vendor/bin/mydiagservice    class hal    user system    group system    seclabel u:r:mydiag_service:s0    oneshot

Phase 3: Building, Deploying, and Enforcing

Step 3.1: Build AOSP with New Policies

After defining your policies, rebuild AOSP:

source build/envsetup.shlunch <your_device_target>make -j$(nproc)

Step 3.2: Flash the New Image

Flash the updated system image to your Android Automotive device. This will include your new SELinux policy.

adb reboot bootloaderfastboot flash all

Step 3.3: Verify Enforcement and Iterate

Once the device boots, check the global SELinux status:

adb shell getenforce

It should ideally be Enforcing. Now, run your custom service and monitor for new AVC denials. Even if you defined initial rules, it’s highly probable that your service will encounter new denials when it attempts unanticipated operations. This is an iterative process:

  1. Run service, trigger all expected functionalities.
  2. Collect new AVC denials using dmesg | grep 'avc: denied'.
  3. Analyze denials and add necessary rules to mydiagnosticservice.te.
  4. Rebuild and re-flash.
  5. Repeat until no more critical denials occur during normal operation.

Tools like audit2allow can help generate initial rules from denials, but always review them critically:

adb shell dmesg | grep 'avc: denied' > audit.logaudit2allow -i audit.log

The output will suggest rules to add to your .te file. Be cautious; only grant the minimum necessary permissions (Principle of Least Privilege).

Best Practices for Hardening

  • Principle of Least Privilege: Always grant the absolute minimum permissions required for a service to function. Avoid broad permissions like allow <domain> *:*:*.
  • Leverage Existing Types: Reuse existing AOSP types (e.g., system_server_prop, logd) whenever possible instead of creating new ones for common resources.
  • Automated Testing: Integrate SELinux policy testing into your CI/CD pipeline. Automated tests should cover various use cases to expose policy gaps.
  • Regular Review: Periodically review your SELinux policies, especially after software updates or new feature introductions, to ensure they remain relevant and secure.
  • Avoid `neverallow` Violations: The neverallow rules in AOSP are critical for platform security. While SELINUX_IGNORE_NEVERALLOW_POLICY can be used during development, ensure your final build adheres to all neverallow rules.
  • Use `dontaudit` Sparingly: While `dontaudit` rules hide specific denials from logs, they should be used with extreme caution and only for non-critical, known-good behaviors that generate excessive noise. They can hide potential security issues.

Conclusion

Hardening Android Automotive AOSP with custom SELinux domains is a complex but indispensable process for ensuring the security and integrity of modern in-vehicle systems. By diligently moving services from permissive to enforcing modes, meticulously defining granular access controls, and adhering to best practices, developers can significantly reduce the attack surface and enhance the overall resilience of their automotive platforms. This deep dive into SELinux policy definition provides a foundational understanding and practical steps to achieve a more secure Android Automotive experience, crucial for protecting both vehicle functionality and user data.

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