Exploring SE for Android (2015)

Chapter 10. Placing Applications in Domains

In Chapter 3Android Is Weird, we introduced the zygote and that all applications, APKs in Android speak, emanate from the zygote just like services emanate from the init process. As such, they need to be labeled, as we did in the previous chapter. Recall that labeling is the same as placing a process in a domain of that label. Applications need to be labeled as well.

Note

APK is the file extension and format for installable application packages on Android. It's analogous to the desktop package formats like RPM (Redhat based) or DEB (Debian based).

In this chapter, we will learn to:

·        Properly label application private data directories and their runtime contexts

·        Further examine zygote and methods to secure it

·        Discover how a finished mac_permssions.xml file assigns seinfo value

·        Create a new custom domain

The case to secure the zygote

Android applications with elevated permissions and capabilities are spawned from the zygote. An example of this is the system server, a large process comprised of native and non-native code hosting a variety of services. The system server houses the activity manager, package manager, GPS feeds and so on. The system server also runs with a highly sensitive UID of system (1000). Also, many OEMs package what are known as system apps, which are standalone applications running with the system UID.

The zygote also spawns applications that do not need elevated permissions. All third-party applications represent this. Third party applications run as their own UID, separate from sensitive UIDs, such as system. Additionally, applications get spawned into various UIDs such as media, nfc, and so on. OEMs tend to define additional UIDs.

It's important to note that to get into a special UID, like system, you must be signed with the proper key. Android has four major keys used to sign applications: media, platform, shared, and testkey. They are located in build/target/product/security, along with a README.

According to the README, the key usage is as follows:

·        testkey: A generic key for packages that do not otherwise specify a key.

·        platform: A test key for packages that are part of the core platform.

·        shared: A test key for things that are shared in the home/contacts process.

·        media: A test key for packages that are part of the media/download system.

In order to request system UID for your application, you must be signed with the platform key. Possession of the private key is required to execute in these more privileged environments.

As you can see, we have applications executing at a variety of permission levels, and trust levels. We cannot trust third party applications since they are created by unknown entities, and we can trust things signed with our private keys. However, before SELinux, application permissions were still bound by the same DAC permission limitations as those identified in Chapter 1Linux Access Controls. Because of these properties, it makes the zygote a prime target for attack, as well as fortification with SELinux.

Fortifying the zygote

Now that we have identified a problem with zygote, the next step is understanding how to get applications into appropriate domains. We need either SELinux policy or code changes to place new processes into a domain. In Chapter 9, Adding Services to Domains, we covered dynamic domain transitions with init-based services and the end of the chapter mentions the importance of the exec() syscall in the "Limitations on App Labeling" section. This is the trigger on which dynamic domain transitions occur. If there is no exec in the path, we would have to rely on code changes. However, one also has to consider the signing key in this security model, and there is no way in pure SELinux policy language to express the key the process was signed with.

Rather than exploring the whole zygote, we can dissect the following patches that introduce application labeling into Android. Additionally, we can discover how the introduced design meets the requirements of respecting the signing key, working within the design of SELinux and the zygote.

Plumbing the zygote socket

In Chapter 3Android Is Weird, we learned that the zygote listens for requests to spawn a new application from a socket. The first patch to examine is https://android-review.googlesource.com/#/c/31066/. This patch modifies three files in the base frameworks of Android. The first file is Process.java in the method startViaZygote(). This method is the main entry point for other methods with respect to building string arguments and passing them to the zygote with zygoteSendArgsAndGetResult(). The patch introduces a new argument called seinfo. Later on, we will see how this gets used. It appears that this patch is plumbing this new seinfo argument over the socket. Note that this code is called external to the zygote process.

The next file to look at in this patch is ZygoteConnection.java. This code executes from within the context. The patch starts off by declaring a string member variable peerContext in the ZygoteConnection class. In the constructor, this peerContext member is set to the value obtained from a call to SELinux.getPeerContext(mSocket.getFileDescriptor()).

Since the LocalSocket mSocket is a Unix domain socket under the hood, you can obtain the connected client's credentials. In this case, the call to getPeerContext() gets the client's security context, or in more formal terms, the process label. After the initialization, further down in method runOnce(), we see it being used in calls to applyUidSecurityPolicy and other apply*SecurityPolicy routines. The protected method runOnce() is called to read one start command from the socket and arguments. Eventually, after theapply*SecurityPolicy checks, it calls forkandSpecialize(). Each security policy check has been modified to use SELinux on top of the existing DAC security controls. If we review applyUidSecurityPolicy, we see they make the call:

boolean allowed = SELinux.checkSELinuxAccess(peerSecurityContext, peerSecurityContext, "zygote", "specifyids");

This is an example of a userspace leveraging mandatory access controls in what is known as an object manager. Additionally, a security check has been added for the mysterious seinfo string in the applyseInfoSecurityPolicy() method. All the security checks here for SELinux specify the target class zygote. So if we look into sepolicy access_vectors, we see the added class zygote. This is a custom class for Android and defines all the vectors checked in the security checks.

The last file we'll consider from this patch is ActivityManagerService.java. The ActivityManager is responsible for starting applications and managing their lifecycles. It's a consumer of the Process.start API and needs to specify seinfo. This patch is simple, and for now, just sends null. Later, we will see the patch enabling its use.

The next patch, https://android-review.googlesource.com/#/c/31063/, executes within the context of the Android Dalvik VM and is coded in the VM zygote process space. The forkAndSpecialize() we saw in ZygoteConnection ends up in this native routine. It enters using static pid_t forkAndSpecializeCommon(const u4* args, bool isSystemServer). This routine is responsible for creating the new process that becomes the application.

It begins with housekeeping code moving from Java to C and sets up the niceName and seinfo values as C-style strings. Eventually, the code calls fork() and the child process starts doing things, like executing setgid and setuid. The uid and gid values are specified to the zygote connection with the Process.start method. We also see a new call to setSELinuxContext(). As an aside, the order of these events is important here. If you set the SELinux context of the new process too early, the process would need additional capabilities in the new context to do things like setuid and setgid. However, those permissions are best left to the zygote domain, so the application domain we entered can be as minimal as possible.

Continuing, setSELinuxContext eventually calls selinux_android_setcontext(). Note that the HAVE_SELINUX conditional compilation macros were removed after this commit, but prior to the 4.3 release. Also note that selinux_android_setcontext() is defined in libselinux, so our journey will take us there. Here we see the mysterious seinfo is still being passed along.

The next patch to evaluate is https://android-review.googlesource.com/#/c/39601/. This patch actually passes a more meaningful seinfo value from the Java layer. Rather than being set to null, this patch introduces some parsing logic from an XML file, and passes this along to the Process.start method.

This patch modifies two major components: PackageManager and installd. PackageManager runs inside the system_server, and performs application installation. It maintains the state of all installed packages in the system. The second component, a service known asinstalld, is a very privileged root service that creates all the applications' private directories on disk. Rather than giving system server, and therefore PackageManager, the capability to create these directories, only installd has these permissions. Using this approach, even the system server cannot read data in your private data directories unless you make it world readable.

This patch is larger than the others, so we are only going to inspect the parts directly relevant to our discussion. We'll start by looking at PackageManagerService.java. This class is the package manager, proper for Android. In the constructor for PackageManagerService(), we see the addition of mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();.

Based on the naming, we can conjecture that this method is looking for some type of policy configuration file, and if found, returns true, setting the mFoundPolicyFile member variable. We also see some calls to createDataDirs and mInstaller.* calls. These we can ignore, since those calls are headed to installd.

The next major portion adds the following:

if (mFoundPolicyFile) {

  SELinuxMMAC.assignSeinfoValue(pkg);

}

It's important to note that this code was added into the scanPackageLI() method. This method is called every time a package needs to be scanned for installation. So at a high level, if some policy file is found during service startup, then a seinfo value is assigned to the package.

The next file to look at is ApplicationInfo.java, a container class for maintaining meta information about a package. As we can see, the seinfo value is specified here for storage purposes. Additionally, there is some code for serializing and deserializing the class via the Android specific Parcel implementation.

At this point, we should have a closer look at the SELinuxMMAC.java code to confirm our understanding of what's going on. The class starts by declaring two locations for policy files.

// Locations of potential install policy files.

private static final File[] INSTALL_POLICY_FILE = {

  new File(Environment.getDataDirectory(), "system/mac_permissions.xml"),

  new File(Environment.getRootDirectory(), "etc/security/mac_permissions.xml"),

  null };

According to this, policy files can exist in two locations- /data/system/mac_permissions.xml and /system/etc/security/mac_permissions.xml. Eventually, we see the call from PackageManagerService initialization to the method defined in the class readInstallPolicy(), which eventually reduces to a call of:

private static boolean readInstallPolicy(File[] policyFiles) {

  FileReader policyFile = null;

  int i = 0;

  while (policyFile == null && policyFiles != null && policyFiles[i] != null) {

    try {

      policyFile = new FileReader(policyFiles[i]);

      break;

    } catch (FileNotFoundException e) {

      Slog.d(TAG,"Couldn't find install policy " + policyFiles[i].getPath());

    }

  i++;

  }

...

With policyFiles set to INSTALL_POLICY_FILE, this code uses the array to find a file at the specified locations. It is priority based, with the /data location taking precedence over /system. The rest of the code in this method looks like parsing logic and fills up two hash tables that were defined in the class declaration:

// Signature seinfo values read from policy.

private static final HashMap<Signature, String> sSigSeinfo =

new HashMap<Signature, String>();

// Package name seinfo values read from policy.

private static final HashMap<String, String> sPackageSeinfo =

new HashMap<String, String>();

The sSigSeinfo maps Signatures, or signing keys, to seinfo strings. The other map, sPackageSeinfo maps a package name to a string.

At this point, we can read some formatted XML from the mac_permissions.xml file and create internal mappings from signing key to seinfo and package name to seinfo.

The other call from PackageManagerService into this class came from void assignSeinfoValue(PackageParser.Package pkg).

Let's investigate what this method can do. It starts by checking if the application is system UID or a system installed app. In other words, it checks whether the application is a third-party application:

if (((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) ||

((pkg.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)) {

This code has subsequently been dropped by Google, and was initially a requirement for merge. We can, however, continue our evaluation. The code loops over all the signatures in the package, and checks against the hash table. If it is signed with something in that map, it uses the associated seinfo value. The other case is that it matches by package name. In either case, the package's ApplictionInfo class seinfo value is updated to reflect this and be used elsewhere by installd and zygote application spawn:

// We just want one of the signatures to match.

for (Signature s : pkg.mSignatures) {

  if (s == null)

    continue;

  if (sSigSeinfo.containsKey(s)) {

    String seinfo = pkg.applicationInfo.seinfo = sSigSeinfo.get(s);

    if (DEBUG_POLICY_INSTALL)

      Slog.i(TAG, "package (" + pkg.packageName +

        ") labeled with seinfo=" + seinfo);

    return;

    }

  }

  // Check for seinfo labeled by package.

  if (sPackageSeinfo.containsKey(pkg.packageName)) {

    String seinfo = pkg.applicationInfo.seinfo = sPackageSeinfo.get(pkg.packageName);

    if (DEBUG_POLICY_INSTALL)

      Slog.i(TAG, "package (" + pkg.packageName +

        ") labeled with seinfo=" + seinfo);

      return;

    }

  }

}

As an aside, what is merged into mainline AOSP and what is maintained in the NSA Bitbucket repositories is a bit different. The NSA has additional controls in these policy files that can cause an application installation to abort. Google and the NSA are "forked" over this issue, so to speak. In the NSA versions of SELinuxMMAC.java, you can specify that applications matching a specific signature or package name are allowed to have certain sets of Android-level permissions. For instance, you can block all applications from being installed that request CAMERA permissions or block applications signed with certain keys. This also highlights how important it can be to find patches within large code bases and quickly come up to speed on how projects evolve, which can often seem daunting.

The last file in this patch for us to consider is ActivityManagerService.java. This patch replaces the null with app.info.seinfo. After all that work and all that plumbing, we finally have the mystical seinfo value fully parsed, associated per application package, and sent along to the zygote for use in selinux_android_setcontext().

Now it would benefit us to sit back and think about some of the properties we wanted to achieve in labeling applications. One of them is to somehow couple a security context with the application signing key, and this is precisely the main benefit of seinfo. This is a highly sensitive and trusted string associated value of a signing key. The actual contents of the string are arbitrary and dictated in mac_permissions.xml, which is the next stop on our adventure.

The mac_permissions.xml file

The mac_permissions.xml file has a very confusing name. Expanded, the name is MAC permissions. However, its major mainline functionality is to map a signing key to a seinfo string. Secondarily, it can also be used to configure a non-mainstream install-time permission-checking feature, known as install time MMAC. MMAC controls are part of the NSA's work to implement mandatory access controls in the middleware layer. MMAC stands for "Middleware Mandatory Access Controls". Google has not merged any of the MMAC features. However, since we used the NSA Bitbucket repositories, our code base contains these features.

The mac_permissions.xml is an XML file, and should adhere to the following rules, where italicized portions are only supported on NSA branches:

·        A signature is a hex encoded X.509 certificate and is required for each signer tag.

·        A <signer signature="" > element may have multiple child elements:

·        allow-permission: It produces a set of maximal allowed permissions (whitelist)

·        deny-permission: It produces a blacklist of permissions to deny

·        allow-all: It is a wildcard tag that will allow every permission requested

·        package: It is a complex tag which defines allow, deny, and wildcard sub-elements for a specific package name protected by the signature

·        Zero or more global <package name=""> tags are allowed. These tags allow a policy to be set outside any signature for specific package names.

·        A <default> tag is allowed that can contain install policy for all apps not signed with a previously listed cert and not having a per package global policy.

·        Unknown tags at any level are skipped.

·        Zero or more signer tags are allowed.

·        Zero or more package tags are allowed per signer tag.

·        A <package name=""> tag may not contain another <package name=""> tag. If found, it's skipped.

·        When multiple sub-elements appear for a tag, the following logic is used to ultimately determine the type of enforcement:

·        A blacklist is used if at least one deny-permission tag is found.

·        A whitelist is used, if not a blacklist, and at least one allow-permission tag is found.

·        A wildcard (accept all permissions) policy is used if not a blacklist and not a whitelist, and at least one allow-all tag is present.

·        If a <package name=""> sub-element is found, then that sub-element's policy is used according to the earlier logic and overrides any signature global policy type.

·        In order for a policy stanza to be enforced, at least one of the preceding situations must apply. Meaning, empty signer, default or package tags will not be accepted.

·        Each signer/default/package (global or attached to a signer) tag is allowed to contain one <seinfo value=""/> tag. This tag represents additional info that each app can use in setting an SELinux security context on the eventual process.

·        Strict enforcing of any XML stanza is not enforced in most cases. This mainly applies to duplicate tags, which are allowed. In the event that a tag already exists, the original tag is replaced.

·        There are also no checks on the validity of permission names. Although valid Android permissions are expected, nothing prevents unknowns.

·        Following are the enforcement decisions:

·        All signatures used to sign an app are checked for policy according to signer tags. However, only one of the signature policies has to pass.

·        In the event that none of the signature policies pass, or none even match, then a global package policy is sought. If found, this policy mediates the install.

·        The default tag is consulted last, if needed.

·        A local package policy always overrides any parent policy.

·        If none of the cases apply, then the app is denied.

The following examples ignore the Install MMAC support and focus on the mainline usage of seinfo mapping. The following is an example of stanza mapping all things signed with the platform key to seinfo value platform:

<!-- Platform dev key in AOSP -->

<signer signature="@PLATFORM" >

  <seinfo value="platform" />

</signer>

Here is an example mapping all things signed with the release key to the release domain with the exception of the browser. The browser gets assigned a seinfo value of browser, as follows:

<!-- release dev key in AOSP -->

<signer signature="@RELEASE" >

  <seinfo value="release" />

  <package name="com.android.browser" >

  <seinfo value="browser" />

  </package>

</signer>

...

Anything with an unknown key, gets mapped to the default tag:

...

<!-- All other keys -->

<default>

  <seinfo value="default" />

</default>

The signing tags are of interest, the @PLATFORM and @RELEASE are special processing strings used during build. Another mapping file maps these to actual key values. The file that is processed and placed onto the device has all key references replaced with hex encoded public keys rather than these placeholders. It also has all whitespace and comments stripped to reduce size. Let's take a look by pulling the built file from the device and formatting it.

$ adb pull /system/etc/security/mac_permissions.xml

$ xmllint --format mac_permissions.xml

Now, scroll to the top of the formatted output; you should see the following:

<?xml version="1.0" encoding="iso-8859-1"?>

<!-- AUTOGENERATED FILE DO NOT MODIFY -->

<policy>

  <signer signature="308204ae30820396a003020102020900d2cba57296ebebe2300d06092a864886f70d0101050500308196310b300906035504061302555331133...

dec513c8443956b7b0182bcf1f1d">

    <allow-all/>

    <seinfo value="platform"/>

  </signer>

Notice that signature=@PLATFORM is now a hex string. This hex string is a valid X509 certificate.

keys.conf

The actual magic doing the mapping from signature=@PLATFORM in mac_permissions.xml is keys.conf. This configuration file allows you to map a pem encoded x509 to an arbitrary string. The convention is to start them with @, but this is not enforced. The format of the file is based on the Python config parser and contains sections. The section names are the tags in the mac_permissions.xml file you wish to replace with key values. The platform example is:

[@PLATFORM]

ALL : $DEFAULT_SYSTEM_DEV_CERTIFICATE/platform.x509.pem

In Android, when you build, you can have three levels of builds: engineering, userdebug, or user. In the keys.conf file, you can associate a key to be used for all levels with the section attribute ALL, or you can assign different keys per level. This is helpful when building release or user builds with very special release keys. We see an example of this in the @RELEASE section:

[@RELEASE]

ENG       : $DEFAULT_SYSTEM_DEV_CERTIFICATE/testkey.x509.pem

USER      : $DEFAULT_SYSTEM_DEV_CERTIFICATE/testkey.x509.pem

USERDEBUG : $DEFAULT_SYSTEM_DEV_CERTIFICATE/testkey.x509.pem

The file also allows the use of environment variables through the traditional $ special character. The default location for the pem files is build/target/product/security. However, you should never use these keys for a user release build. These keys are the AOSP test keys and are public! By doing so, anyone can use the system key to sign their app and gain system privilege. The keys.conf file is only used during the build and is not located on the system.

seapp_contexts

So far, we have looked at how a finished mac_permssions.xml file assigns the seinfo value. Now we should address how the labeling is actually configured and utilizes this value. The labeling of applications is managed in another configuration file, seapp_contexts. Likemac_permissions.xml, it is loaded to the device. However, the default location is /seapp_contexts. The format of seapp_contexts is the key=value pair mappings per line, adhering to the following rules:

·        Input selectors:

·        isSystemServer (boolean)

·        user (string)

·        seinfo (string)

·        name (string)

·        sebool (string)

·        Input selector rules:

·        isSystemServer=true can only be used once.

·        An unspecified isSystemServer defaults to false.

·        An unspecified string selector will match any value.

·        A user string selector that ends in * will perform a prefix match.

·        user=_app will match any regular app UID.

·        user=_isolated will match any isolated service UID.

·        All specified input selectors in an entry must match (logical AND).

·        Matching is case-insensitive.

·        Precedence rules in order:

§  isSystemServer=true before isSystemServer=false

§  Specified user= string before unspecified user= string

§  Fixed the user= string before the user= prefix (ending in *)

§  Longer user= prefix before shorter user= prefix

§  Specified seinfo= string before unspecified seinfo= string.

§  Specified name= string before unspecified name= string.

§  Specified sebool= string before unspecified sebool= string.

·        Outputs:

·        domain (string): It specifies the process domain for the application.

·        type (string): It specifies the disk label for the applications' private data directory.

·        levelFrom (string; one of none, all, app, or user): It gives the MLS specifier.

·        level (string): It shows the hardcoded MLS value.

·        Output rules:

·        Only entries that specify domain= will be used for app process labeling.

·        Only entries that specify type= will be used for app directory labeling.

·        levelFrom=user is only supported for _app or _isolated UIDs.

·        levelFrom=app or levelFrom=all is only supported for _app UIDs.

·        level may be used to specify a fixed level for any UID.

During application spawn, this file is used by the selinux_android_setcontext() and selinux_android_setfilecon2() functions to look up the proper application domain or filesystem context, respectively. The source for these can be found inexternal/libselinux/src/android.c and are recommended reads. For example, this entry places all applications with UID bluetooth in the bluetooth domain with a data directory label of bluetooth_data_file:

user=bluetooth domain=bluetooth type=bluetooth_data_file

This example places all third party or "default" applications into a process domain of untrusted_app and a data directory of app_data_file. It additionally uses MLS categories of levelFrom=app to help provide additional MLS-based separations.

user=_app domain=untrusted_app type=app_data_file levelFrom=app

Currently, this feature is experimental as this breaks some known application compatibility issues. At the time of this writing, this was a hot item of focus for both Google and NSA engineers. Since it is experimental, let's validate its functionality and then disable it.

We have not installed any third party applications yet, so we'll need to do so in order to experiment. FDroid is a useful place to find third party applications, so let's download something from there and install it. We can use the 0xbenchmark application located athttps://f-droid.org/repository/browse/?fdid=org.zeroxlab.zeroxbenchmark with an APK at https://f-droid.org/repo/org.zeroxlab.zeroxbenchmark_9.apk, as follows:

$ wget https://f-droid.org/repo/org.zeroxlab.zeroxbenchmark_9.apk

$ adb install org.zeroxlab.zeroxbenchmark_9.apk

567 KB/s (1193455 bytes in 2.052s)

pkg: /data/local/tmp/org.zeroxlab.zeroxbenchmark_9.apk

Success

Tip

Check logcat for the install time seinfo value:

$ adb logcat | grep SELinux

I/SELinuxMMAC( 2557): package (org.zeroxlab.zeroxbenchmark) installed with seinfo=default

From your UDOO, launch the 0xbenchmark APK. We should see it running with its label in ps:

$ adb shell ps -Z | grep untrusted

u:r:untrusted_app:s0:c40,c256 u0_a40 17890 2285 org.zeroxlab.zeroxbenchmark

Notice the level portion of the context string s0:c40,c256. These categories were created with the level=app setting from seapp_contexts.

To disable it, we could simply remove the key-value pair for level from the entry in seapp_contexts, or we could leverage the sebool conditional assignment. Let's use the Boolean approach. Modify the sepolicy seapp_contexts file so the existing untrusted_app entry is modified, and a new one is added. Change user=_app domain=untrusted_app type=app_data_file to user=_app sebool=app_level domain=untrusted_app type=app_data_file levelFrom=app.

Build that with mmm external/sepolicy, as follows:

Error:

out/host/linux-x86/bin/checkseapp -p out/target/product/udoo/obj/ETC/sepolicy_intermediates/sepolicy -o out/target/product/udoo/obj/ETC/seapp_contexts_intermediates/seapp_contexts out/target/product/udoo/obj/ETC/seapp_contexts_intermediates/seapp_contexts.tmp

Error: Could not find selinux boolean "app_level" on line: 42 in file: out/target/product/udoo/obj/ETC/seapp_contexts_intermediates/seapp_contexts

Error: Could not validate

Well, there was a build error complaining about not finding the selinux Boolean on line 42 of seapp_contexts. Let's attempt to correct the issue by declaring the Boolean. In app.te, add: bool app_level false;. Now push the newly built seapp_contexts and sepolicy file to the device and trigger a dynamic reload:

$ adb push $OUT/root/sepolicy /data/security/current/

$ adb push $OUT/root/seapp_contexts /data/security/current/

$ adb shell setprop selinux.reload_policy 1

We can verify that the Boolean exists by:

$ adb shell getsebool -a | grep app_level

app_level --> off

Due to design limitations, we need to uninstall and reinstall the application:

$ adb uninstall org.zeroxlab.zeroxbenchmark

Re-install and check the context of the process after launching it:

$ adb shell ps -Z | grep untrusted

u:r:untrusted_app:s0:c40,c256 u0_a40 17890 2285 org.zeroxlab.zeroxbenchmark

Great! It failed. After some debugging, we discovered the source of the issue is that the path /data/security is not world searchable, causing a DAC permissions failure.

Note

We found this by printing off the result and error codes in android.c where we saw the fopen on seapp_contexts_file[] array (files in priority order) while checking the result of fp = fopen(seapp_contexts_file[i++], "r") in selinux_android_seapp_context_reload() and using selinux_log() to dump the data to logcat.

$ adb shell ls -la /data | grep security

drwx------ system system 1970-01-04 00:22 security

Remember the set selinux context occurs after the UID switch, so we need to make it searchable for others. We can fix the permissions on the UDOO init.rc script by changing device/fsl/imx6/etc/init.rc. Specifically, change the line mkdir /data/security 0700 system system to mkdir /data/security 0711 system system. Build and flash the bootimage, and try the context test again.

$ adb uninstall org.zeroxlab.zeroxbenchmark

$ adb install ~/org.zeroxlab.zeroxbenchmark_9.apk

<launch apk>

$ adb shell ps -Z | grep org.zeroxlab.zeroxbenchmark

u:r:untrusted_app:s0 u0_a40 3324 2285 org.zeroxlab.zeroxbenchmark

So far, we've demonstrated how to use the sebool option on seapp_contexts to disable the MLS categories. It's important to note that when changing categories or types on APKs, it is required to remove and install the APK, or you will orphan the process from its data directory because it won't have access permissions under most circumstances.

Next, let's take this APK, uninstall it, and assign it a unique domain by changing its seinfo string. Typically, you use this feature to take a set of applications signed with a common key and get them into a custom domain to do custom things. For example, if you're an OEM, you may need to allow custom permissions to third party applications that are not signed with an OEM controlled key. Start by uninstalling the APK:

$ adb uninstall org.zeroxlab.zeroxbenchmark

Create a new entry in mac_permissions.xml by adding:

<signer signature="@BENCHMARK" >

<allow-all />

<seinfo value="benchmark" />

</signer>

Now we need to get a pem file for keys.conf. So unpackage the APK and extract the public certificate:

$ mkdir tmp

$ cd tmp

$ unzip ~/org.zeroxlab.zeroxbenchmark_9.apk

$ cd META-INF/

$ $ openssl pkcs7 -inform DER -in *.RSA -out CERT.pem -outform PEM  -print_certs

We'll have to strip any cruft from the generated CERT.pem file. If you open it up, you should see these lines at the top:

subject=/C=UK/ST=ORG/L=ORG/O=fdroid.org/OU=FDroid/CN=FDroid

issuer=/C=UK/ST=ORG/L=ORG/O=fdroid.org/OU=FDroid/CN=FDroid

-----BEGIN CERTIFICATE-----

MIIDPDCCAiSgAwIBAgIEUVJuojANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJV

SzEMMAoGA1UECBMDT1JHMQwwCgYDVQQHEwNPUkcxEzARBgNVBAoTCmZkcm9pZC5v

...

They need to be removed, so remove only the subject and issuer lines. The file should start with BEGIN CERTIFICATE and end with END CERTIFICATE scissor lines.

Let's move this to a new folder in our workspace called certs and move the certificate into this folder with a better name:

$ mkdir UDOO_SOURCE_ROOT/certs

$ mv CERT.pem UDOO_SOURCE_ROOT/certs/benchmark.x509.pem

We can set up our keys.conf by adding:

[@BENCHMARK]

ALL : certs/benchmark.x509.pem

Don't forget to update seapp_contexts in order to use the new mapping:

user=_app seinfo=benchmark domain=benchmark_app type=benchmark_app_data_file

Now declare the new types to be used. The domain type should be declared in a file called benchmark_app.te in sepolicy:

# Declare the new type

type benchmark_app, domain;

# This macro adds it to the untrusted app domain set and gives it some allow rules

# for basic functionality as well as object access to the type in argument 2.

untrustedapp_domain(benchmark_app, benchmark_app_data_file)

Also, add the benchmark_app_data_file in file.te:

type benchmark_app_data_file, file_type, data_file_type, app_public_data_type;

Tip

You may not always want all of these attributes, especially if you're doing something security critical. Make sure you look at each attribute and macro and see its usage. You don't want to open up an unintended hole by having an overly permissive domain.

Rebuild the policy, push the required pieces, and trigger a reload.

$ mmm external/sepolicy/

$ adb push $OUT/system/etc/security/mac_permissions.xml /data/security/current/

$ adb push $OUT/root/sepolicy /data/security/current/

$ adb push $OUT/root/seapp_contexts /data/security/current/

$ adb shell setprop selinux.reload_policy 1

Start a shell and grep logcat to see the seinfo value the benchmark APK is installed as. Then install the APK:

$ adb install ~/org.zeroxlab.zeroxbenchmark_9.apk

$ adb logcat | grep -i SELinux

On the logcat output, you should see:

I/SELinuxMMAC( 2564): package (org.zeroxlab.zeroxbenchmark) installed with seinfo=default

It should have been seinfo=benchmark! What could have happened?

The problem is in frameworks/base/services/java/com/android/server/pm/SELinuxMMAC.java. It looks in /data/security/mac_permissions.xml; so we can just push mac_permissions.xml. This is another bug in the dynamic policy reload and has to do with historical changes in this loading procedure. The culprit is within the frameworks/base/services/java/com/android/server/pm/SELinuxMMAC.java file:

private static final File[] INSTALL_POLICY_FILE = {

new File(Environment.getDataDirectory(), "security/mac_permissions.xml"),

new File(Environment.getRootDirectory(), "etc/security/mac_permissions.xml"),

null};

To get around this, remount system and push it to the default location.

$ adb remount

$ adb push $OUT/system/etc/security/mac_permissions.xml /system/etc/security/

This does not require a setprop selinux.reload_policy 1. Uninstall and reinstall the benchmark APK, and check the logs:

I/SELinuxMMAC( 2564): package (org.zeroxlab.zeroxbenchmark) installed with seinfo=default

OK. It still didn't work. When we examined the code, the mac_permissions.xml file was loaded during package manager service start. This file won't get reloaded without a reboot, so let's uninstall the benchmark APK, and reboot the UDOO. After it's been booted andadb is enabled, trigger a dynamic reload, install the APK, and check logcat. It should have:

I/SELinuxMMAC( 2559): package (org.zeroxlab.zeroxbenchmark) installed with seinfo=benchmark

Now let's verify the process domain by launching the APK, checking ps, and verifying its application private directory:

<launch apk>

$ adb shell ps -Z | grep org.zeroxlab.zeroxbenchmark

u:r:benchmark_app:s0 u0_a45 3493 2285 org.zeroxlab.zeroxbenchmark

$ adb shell ls -Z /data/data | grep org.zeroxlab.zeroxbenchmark

drwxr-x--x u0_a45 u0_a45 u:object_r:benchmark_app_data_file:s0 org.zeroxlab.zeroxbenchmark

This time, all the types check out. We successfully created a new custom domain.

Summary

In this chapter, we investigated how to properly label application private data directories as well as their runtime contexts via the configuration files and SELinux policy. We also looked into the subsystems and code to make all of this work as well as some basic things that may go wrong along the way. In the next chapter, we will expand on how the policy and configuration files get built by peering into the SE for Android build system.