Customizing SELinux
Once you've integrated this base level of functionality and thoroughly analyzed the results, you may add your own policy settings to cover your customizations to the Android operating system. Of course, these policies must still meet the Android Compatibility program requirements and not remove the default SELinux settings.
Manufacturers should not remove existing security settings. Otherwise, they risk breaking the Android SELinux implementation and the applications it governs. This includes third-party applications that will likely need to be improved to be compliant and operational. Applications must require no modification to continue functioning on SELinux-enabled devices.
When embarking upon customizing SELinux, manufacturers should remember to:
- Write SELinux policy for all new daemons
- Use predefined domains whenever appropriate
- Assign a domain to any process spawned as an
init
service - Become familiar with the macros before writing policy
- Submit changes to core policy to AOSP
And not to:
- Create incompatible policy
- Allow end user policy customization
- Allow MDM policy customizations
- Scare users with policy violations
- Add backdoors
See the Kernel Security Features section of the Android Compatibility Definition document for specific requirements.
SELinux uses a whitelist approach, meaning all access must be explicitly allowed in policy in order to be granted. Since Android's default SELinux policy already supports the Android Open Source Project, OEMs are not required to modify SELinux settings in any way. If they do customize SELinux settings, they should take great care not to break existing applications. Here is how we recommend proceeding:
- Use the latest Android kernel.
- Adopt the principle of least privilege.
- Address only your own additions to Android. The default policy works with the Android Open Source Project codebase automatically.
- Compartmentalize software components into modules that conduct singular tasks.
- Create SELinux policies that isolate those tasks from unrelated functions.
- Put those policies in *.te files (the extension for SELinux policy source files) within the
/device/manufacturer/device-name/sepolicy
directory and useBOARD_SEPOLICY
variables to include them in your build. - Make new domains permissive initially. This is done by using a permissive declaration in the domain's .te file.
- Analyze results and refine your domain definitions.
- Remove the permissive declaration when no further denials appear in userdebug builds.
Once integrated, OEM Android development should include a step to ensure SELinux compatibility going forward. In an ideal software development process, SELinux policy changes only when the software model changes and not the actual implementation.
As device manufacturers begin to customize SELinux, they should first audit their additions to Android. If they've added a component that conducts a new function, the manufacturers will need to ensure the component meets the security policy applied by Android, as well as any associated policy crafted by the OEM, before turning on enforcing mode.
To prevent unnecessary issues, it is better to be overbroad and over-compatible than too restrictive and incompatible, which results in broken device functions. Conversely, if a manufacturer's changes will benefit others, it should supply the modifications to the default SELinux policy as a patch. If the patch is applied to the default security policy, the manufacturer will no longer need to make this change with each new Android release.
Example policy statements
First, note SELinux is based upon the M4 computer language and therefore supports a variety of macros to save time.
In the following example, all domains are granted access to read from or write to /dev/null
and read from /dev/zero
.
# Allow read / write access to /dev/null
allow domain null_device:chr_file { getattr open read ioctl lock append write};
# Allow read-only access to /dev/zero
allow domain zero_device:chr_file { getattr open read ioctl lock };
This same statement can be written with SELinux *_file_perms
macros (shorthand):
# Allow read / write access to /dev/null
allow domain null_device:chr_file rw_file_perms;
# Allow read-only access to /dev/zero
allow domain zero_device:chr_file r_file_perms;
Example policy
Here is a complete example policy for DHCP, which we examine below:
type dhcp, domain;
permissive dhcp;
type dhcp_exec, exec_type, file_type;
type dhcp_data_file, file_type, data_file_type;
init_daemon_domain(dhcp)
net_domain(dhcp)
allow dhcp self:capability { setgid setuid net_admin net_raw net_bind_service
};
allow dhcp self:packet_socket create_socket_perms;
allow dhcp self:netlink_route_socket { create_socket_perms nlmsg_write };
allow dhcp shell_exec:file rx_file_perms;
allow dhcp system_file:file rx_file_perms;
# For /proc/sys/net/ipv4/conf/*/promote_secondaries
allow dhcp proc_net:file write;
allow dhcp system_prop:property_service set ;
unix_socket_connect(dhcp, property, init)
type_transition dhcp system_data_file:{ dir file } dhcp_data_file;
allow dhcp dhcp_data_file:dir create_dir_perms;
allow dhcp dhcp_data_file:file create_file_perms;
allow dhcp netd:fd use;
allow dhcp netd:fifo_file rw_file_perms;
allow dhcp netd:{ dgram_socket_class_set unix_stream_socket } { read write };
allow dhcp netd:{ netlink_kobject_uevent_socket netlink_route_socket
netlink_nflog_socket } { read write };
Let’s dissect the example:
In the first line, the type declaration, the DHCP daemon inherits from the base security policy (domain
). From the previous statement examples, we know DHCP can read from and write to /dev/null.
In the second line, DHCP is identified as a permissive domain.
In the init_daemon_domain(dhcp)
line, the policy states DHCP is spawned from init
and is allowed to communicate with it.
In the net_domain(dhcp)
line, the policy allows DHCP to use common network functionality from the net
domain such as reading and writing TCP packets, communicating over sockets, and conducting DNS requests.
In the line allow dhcp proc_net:file write;
, the policy states DHCP can write to specific files in /proc
. This line demonstrates SELinux’s fine-grained file labeling. It uses the proc_net
label to limit write access to only the files under /proc/sys/net
.
The final block of the example starting with allow dhcp netd:fd use;
depicts how applications may be allowed to interact with one another. The policy says DHCP and netd may communicate with one another via file descriptors, FIFO files, datagram sockets, and UNIX stream sockets. DHCP may only read to and write from the datagram sockets and UNIX stream sockets and not create or open them.
Available controls
Class | Permission |
file | ioctl read write create getattr setattr lock relabelfrom relabelto append |
directory | add_name remove_name reparent search rmdir open audit_access execmod |
socket | ioctl read write create getattr setattr lock relabelfrom relabelto append bind |
filesystem | mount remount unmount getattr relabelfrom relabelto transition associate |
process | fork transition sigchld sigkill sigstop signull signal ptrace getsched setsched |
security | compute_av compute_create compute_member check_context load_policy |
capability | chown dac_override dac_read_search fowner fsetid kill setgid setuid setpcap |
MORE | AND MORE |
neverallow rules
SELinux neverallow
rules prohibit behavior that should never occur. With compatibility testing, SELinux neverallow
rules are now enforced across partner devices.
The following guidelines are intended to help manufacturers avoid errors related to neverallow
rules during customization. The rule numbers used here correspond to Android 5.1 and are subject to change by release.
Rule 48: neverallow { domain -debuggerd -vold -dumpstate -system_server } self:capability sys_ptrace;
See the man page for ptrace
. The sys_ptrace
capability grants the ability to ptrace
any process, which allows a great deal of control over other processes and should belong only to designated system components, outlined in the rule. The need for this capability often indicates the presence of something that is not meant for user-facing builds or functionality that isn’t needed. Remove the unnecessary component.
Rule 76: neverallow { domain -appdomain -dumpstate -shell -system_server -zygote } { file_type -system_file -exec_type }:file execute;
This rule is intended to prevent the execution of arbitrary code on the system. Specifically, it asserts that only code on /system
gets executed, which allows security guarantees thanks to mechanisms such as verified boot. Often, the best solution when encountering a problem with this neverallow
rule is to move the offending code to the /system
partition.