Validating SELinux

Android strongly encourages OEMs to test their SELinux implementations thoroughly. As manufacturers implement SELinux, they should apply the new policy to a test pool of devices first.

Once applied, make sure SELinux is running in the correct mode on the device by issuing the command:getenforce

This will print the global SELinux mode: either Enforcing or Permissive. Please note, this command shows only the global SELinux mode. To determine the SELinux mode for each domain, you must examine the corresponding files or run the latest version of sepolicy-analyze with the appropriate (-p) flag, present in /platform/system/sepolicy/tools/.

Reading denials

Then check for errors. Errors are routed as event logs to dmesg and logcat and are viewable locally on the device. Manufacturers should examine the SELinux output to dmesg on these devices and refine settings prior to public release in permissive mode and eventual switch to enforcing mode. SELinux log messages contain "avc:" and so may easily be found with grep. It is possible to capture the ongoing denial logs by running cat /proc/kmsg or to capture denial logs from the previous boot by running cat /proc/last_kmsg.

With this output, manufacturers can readily identify when system users or components are in violation of SELinux policy. Manufacturers can then repair this bad behavior, either by changes to the software, SELinux policy, or both.

Specifically, these log messages indicate what processes would fail under enforcing mode and why. Here is an example:

avc: denied  { connectto } for  pid=2671 comm="ping" path="/dev/socket/dnsproxyd"
scontext=u:r:shell:s0 tcontext=u:r:netd:s0 tclass=unix_stream_socket

Interpret this output like so:

  • The { connectto } above represents the action being taken. Together with the tclass at the end (unix_stream_socket), it tells you roughly what was being done to what. In this case, something was trying to connect to a unix stream socket.
  • The scontext (u:r:shell:s0) tells you what context initiated the action. In this case this is something running as the shell.
  • The tcontext (u:r:netd:s0) tells you the context of the action’s target. In this case, that’s a unix_stream_socket owned by netd.
  • The comm="ping" at the top gives you an additional hint about what was being run at the time the denial was generated. In this case, it’s a pretty good hint.

And here is another example:

adb shell su root dmesg | grep 'avc: '

Output:

<5> type=1400 audit: avc:  denied  { read write } for  pid=177
comm="rmt_storage" name="mem" dev="tmpfs" ino=6004 scontext=u:r:rmt:s0
tcontext=u:object_r:kmem_device:s0 tclass=chr_file

Here are the key elements from this denial:

  • Action - the attempted action is highlighted in brackets, read write or setenforce.
  • Actor - The scontext (source context) entry represents the actor, in this case the rmt_storage daemon.
  • Object - The tcontext (target context) entry represents the object being acted upon, in this case kmem.
  • Result - The tclass (target class) entry indicates the type of object being acted upon, in this case a chr_file (character device).

Switching to permissive

Important: Permissive mode is not supported on production devices. CTS tests confirm enforcing mode is enabled.

SELinux enforcement can be disabled via ADB on userdebug or eng builds. To do so, first switch ADB to root by running adb root. Then, to disable SELinux enforcement, run:

adb shell setenforce 0

Or at the kernel command line (during early device bring-up):

androidboot.selinux=permissive
androidboot.selinux=enforcing

Using audit2allow

The selinux/policycoreutils/audit2allow tool takes dmesg denials and converts them into corresponding SELinux policy statements. As such, it can greatly speed SELinux development. audit2allow is shipped as part of the Android source tree and is compiled automatically when you build Android from source.

To use it, run:

adb pull /sys/fs/selinux/policy
adb logcat -b all -d | audit2allow -p policy

Note: Running these commands does not change bugreport.txt because all logs are already there, including the ones from before the last reboot. On devices running an OTA or development flash, old and new violations are mixed until another reboot. To address this, reboot the device again or filter LAST_KMSG and LAST_LOGCAT from your bugreport.

Nevertheless, care must be taken to examine each potential addition for overreaching permissions. For example, feeding audit2allow the rmt_storage denial shown earlier results in the following suggested SELinux policy statement:

#============= shell ==============
allow shell kernel:security setenforce;
#============= rmt ==============
allow rmt kmem_device:chr_file { read write };

This would grant rmt the ability to write kernel memory, a glaring security hole. Often the audit2allow statements are only a starting point. After employing these statements, you may need to change the source domain and the label of the target, as well as incorporate proper macros, to arrive at a good policy. Sometimes the denial being examined should not result in any policy changes at all; rather the offending application should be changed.