Exploring SE for Android (2015)
Chapter 11. Labeling Properties
In this chapter, we will cover how to label properties via the property_contexts file.
Properties are a unique Android feature we learned about in Chapter 3, Android Is Weird. We want to label these to restrict setting of our properties to only the domains that should set them, preventing a classic DAC root attack from inadvertently changing the value. In this chapter, we will learn to:
· Create new properties
· Label new and existing properties
· Interpret and deal with property denials
· Enumerate special Android properties and their behaviors
Labeling via property_contexts
All properties are labeled using the property_contexts file, and its syntax is similar to file_contexts. However, instead of working on file paths, it works on property names or property keys (properties in Android are a key-value store). The property keys themselves are typically delimited with periods (.). This is analogous to file_contexts, except the slash (/) becomes a period. Some sample properties and their entries in property_contexts would look like the following:
ctl.ril-daemon u:object_r:ctl_rildaemon_prop:s0
ctl. u:object_r:ctl_default_prop:s0
Notice how all ctl. properties are labeled with the ctl_default_prop type, but ctl.ril-daemon has a different type label of ctl_rildaemon_prop. These are representative of how you can start generically and move to more specific values/types as necessary.
Additionally, anything not explicitly labeled defaults to default_prop through a "match all" expression in property_contexts:
# default property context
* u:object_r:default_prop:s0
Permissions on properties
One can view the current properties on the system, and create new ones with the command line utilities getprop and setprop, as shown in the following code snippet:
root@udoo:/ # getprop
...
[sys.usb.state]: [mtp,adb]
[wifi.interface]: [wlan0]
[wlan.driver.status]: [unloaded]
Recall from Chapter 3, Android Is Weird, that properties are mapped into everyone's address space, thus anyone can read them. However, not everyone can set (write) them. The DAC permission model for properties is hardcoded intosystem/core/init/property_service.c:
/* White list of permissions for setting property services. */
struct {
const char *prefix;
unsigned int uid;
unsigned int gid;
} property_perms[] = {
{ "net.rmnet0.", AID_RADIO, 0 },
{ "net.gprs.", AID_RADIO, 0 },
{ "net.ppp", AID_RADIO, 0 },
...
{ "persist.service.bdroid.", AID_BLUETOOTH, 0 },
{ "selinux." , AID_SYSTEM, 0 },
{ "persist.audio.device", AID_SYSTEM, 0 },
{ NULL, 0, 0 }
You must have the UID or GID in the property_perms array to set any property that the prefix matches with. For instance, in order to set the selinux. properties, you must be UID AID_SYSTEM (uid 1000) or root. Yes, root can always set a property, and this is a key benefit to applying SELinux to Android properties. Unfortunately, there is no way to getprop -Z to list the properties and their labels, like with ls -Z and files.
Relabeling existing properties
In order to become more comfortable with labeling properties, let's relabel the wifi.interface property. First, let's verify its context by causing a denial and viewing the denial log, as shown in the following code:
root@udoo:/ # setprop wifi.interface wlan0
avc: denied { set } for property=wifi.interface scontext=u:r:shell:s0 tcontext=u:object_r:default_prop:s0 tclass=property_service
An interesting action occurred when we executed the setprop command over the UDOO serial console. The AVC denial record was printed out. This is because the serial console includes anything printed from the kernel using printk(). What happens here is theinit process, which controls setprops as detailed in Chapter 3, Android Is Weird, writes a message to the kernel log. This log message shows up when we execute our setprop command. If you run this through adb shell, you'll see the message on the serial console, but not in the adb console. To do this, however, you must reboot your system because SELinux only prints denial records once while in permissive mode.
The command using adb shell is as follows:
$ adb shell setprop wifi.interface wlan0
The command using the serial console is as follows:
root@udoo:/ # avc: denied {set} for property=wifi.interface scontext=u:r:shell:s0 tcontext=u:object_r:default_prop
usb 2-1.3: device descriptor read/64, error -110
From the denial output, we can see that the property type label is default_prop. Let's change this to wifi_prop.
We start by editing property.te in the sepolicy directory to declare the new type to label these properties by appending the following line:
type wifi_prop, property_type;
With the type declared, the next step is to apply the label by modifying property_contexts by adding the following:
# wifi properties
wifi. u:object_r:wifi_prop:s0
Build the policy, as follows:
$ mmm external/sepolicy
Push the new property_contexts file:
$ adb push out/target/product/udoo/root/property_contexts /data/security/current
51 KB/s (2261 bytes in 0.042s)
Trigger a dynamic reload:
$ adb shell setprop selinux.reload_policy 1
# setprop wifi.interface wlan0
avc: denied { set } for property=wifi.interface scontext=u:r:shell:s0 tcontext=u:object_r:default_prop:s0 tclass=property_service
Ok, that didn't work! The property_contexts file must be in /data/security, not /data/security/current.
To discover this, search the libselinux/src/android.c file. There is no mention of property_contexts in this file; thus, it must be mentioned elsewhere. This leads us to search system/core, which contains the property service for uses of that file. The matches are on code in init.c to load the file from priority locations.
$ grep -rn property_contexts *
init/init.c:745: { SELABEL_OPT_PATH, "/data/security/property_contexts" },
init/init.c:746: { SELABEL_OPT_PATH, "/property_contexts" },
init/init.c:760: ERROR("SELinux: Could not load property_contexts: %s\n",
Let's push the property_contexts file to the proper location and try again:
$ adb push out/target/product/udoo/root/property_contexts /data/security
51 KB/s (2261 bytes in 0.042s)
$ adb shell setprop selinux.reload_policy 1
root@udoo:/ # setprop wifi.interface wlan0
avc: received policyload notice (seqno=3)
init: sys_prop: permission denied uid:0 name:wifi.interface
Wow! It failed yet again. This exercise was meant to point out how tricky this can be if you forget to do something. No informative denial messages were displayed, only an indicator that it was denied. This is because the sepolicy file that contains the type declaration for wifi_prop was never pushed. This causes check_mac_perms() in system/core/init/property_service.c to fail in the selinux_check_access() function because it cannot find the type to compute the access check against, even though the look up inproperty_contexts succeeded. There are no verbose error logs from this.
We can correct this by ensuring that the sepolicy is pushed as well:
$ adb push out/target/product/udoo/root/sepolicy /data/security/current/
550 KB/s (87385 bytes in 0.154s)
$ adb shell setprop selinux.reload_policy 1
root@udoo:/ # setprop wifi.interface wlan0
avc: received policyload notice (seqno=4)
avc: denied { set } for property=wifi.interface scontext=u:r:shell:s0 tcontext=u:object_r:wifi_prop:s0 tclass=property_service
Now we see a denial message, as expected, but the label of the target (or property) is u:object_r:wifi_prop:s0.
Now with the target property labeled, you can allow access to it. Note that this is a contrived example, and in the real world, you probably would not want to allow access from shell to most properties. The policy should align with your security goals and the property of least privilege.
We can add an allow rule in shell.te in the following way:
# wifi prop
allow shelldomain wifi_prop:property_service set;
Compile the policy, push it to the phone, and trigger a dynamic reload:
$ mmm external/sepolicy/
$ adb push out/target/product/udoo/root/sepolicy /data/security/current/
547 KB/s (87397 bytes in 0.155s)
$ adb shell setprop selinux.reload_policy 1
Now attempt to set the wifi.interface property and notice the lack of denial.
root@udoo:/ # setprop wifi.interface wlan0
avc: received policyload notice (seqno=5)
Creating and labeling new properties
All properties are dynamically created in the system using setprop calls or function calls that do the equivalent from C (bionic/libc/include/sys/system_properties.h) and Java (android.os.SystemProperties). Note that the System.getProperty() and System.setProperty()Java calls work on application private property stores and are not tied into the global one.
For DAC controls, you need to modify property_perms[] as noted earlier to have permissions for non-root users to create or set the property. Note that root can always set and create, unless constrained by SELinux policy.
Suppose we want to create the udoo.name and udoo.owner properties; we only want the root user and shell domain to access them. We could create them like this:
root@udoo:/ # setprop udoo.name udoo
avc: denied { set } for property=udoo.name scontext=u:r:shell:s0 tcontext=u:object_r:default_prop:s0 tclass=property_service
root@udoo:/ # setprop udoo.owner William
Notice the denial shows these as being default_prop type. To correct this, we would relabel these, exactly as we did in the preceding section, Relabeling existing properties.
Special properties
In Android, there are some special properties that have different behaviors. We enumerate the property names and meanings in the proceeding sections.
Control properties
Properties that start with ctl are reserved as control properties for controlling services through init:
· start: Starts a service (setprop ctl.start <servicename>)
· stop: Stops a service (setprop ctl.stop <servicename>)
· restart: Restarts a service (setprop ctl.restart <servicename>)
Persistent properties
Any property starting with the prefix persist persists across reboots and is restored. The data is saved to /data/property in files of the same name as the property.
root@udoo:/ # ls /data/property/
persist.gps.oacmode
persist.service.bdroid.bdaddr
persist.sys.profiler_ms
persist.sys.usb.config
SELinux properties
The selinux.reload_policy property is special. As we have seen, its use is for triggering a dynamic reload event.
Summary
In this chapter, we have examined how to create and label new and existing properties and some of the oddities that occur when doing so. We have also examined the hard coded DAC permission table for properties in property_service.c, as well as the hardcoded specialty properties like the ctl. family. In the next chapter, we look at how the tool chain builds and creates all the policy files we have been using.