Skip to main content

Android Security: An Overview Of Application Sandbox

The Problem:

Define a policy to control how various clients can access different resources.
A solution:
  1. Each resource has an owner and belongs to a group.
  2. Each client has an owner but can belongs to multiple groups.
  3. Each resource has a mode stating the access permissions allowed for its ownergroup members and others, respectively.
In the context of operating system, or Linux specifically, the resources can be files, sockets, etc; the clients are actually processes; and we have three access permissions:read, write and execute.
Yes, this is just Linux's UID/GID based access control model, and the rules are enforced by Linux kernel. What we will discuss in this article is how it works Android. By the end of the article, we should be able to answer following questions.
  1. How does Android set up the owner, groups and mode of a resource?
  2. How does Android set up the owner and groups of a process?
  3. What does it mean for users and apps? For example, is it possible for app1 access app2's data? Will a normal app be able to access device node directly?
The discussion here is based on the latest android master (Android N) but we'll mention some history in the hope it helps your understanding..

Android Users and Groups ID

Before we jumping in and answering above questions, let first take a look how the user and group are represented in Android. Yes, with an ID, obliviously. Here lists all the users and groups IDs for the system, their meaning and designated ranges for different purposes.
/* This is the master Users and Groups config for the platform.*/
#define AID_ROOT             0  /* traditional unix root user */
#define AID_SYSTEM        1000  /* system server */
#define AID_RADIO         1001  /* telephony subsystem, RIL */
#define AID_BLUETOOTH     1002  /* bluetooth subsystem */
#define AID_GRAPHICS      1003  /* graphics devices */
#define AID_INPUT         1004  /* input devices */
#define AID_AUDIO         1005  /* audio devices */
#define AID_CAMERA        1006  /* camera devices */
#define AID_LOG           1007  /* log devices */
#define AID_COMPASS       1008  /* compass device */
#define AID_MOUNT         1009  /* mountd socket */
#define AID_WIFI          1010  /* wifi subsystem */
#define ...
#define AID_WEBVIEW_ZYGOTE 1053 /* WebView zygote process */
/* The 3000 series are intended for use as supplemental group id's only*/
#define AID_NET_BT_ADMIN  3001  /* bluetooth: create any socket */
#define AID_NET_BT        3002  /* bluetooth: create sco, rfcomm or
#define AID_APP          10000  /* first app user */
#define AID_USER        100000  /* offset for uid ranges for each user */
Then, we will look at the first question - How and when to set up the owner, groups and mode of a resource? Roughly speaking, there are two categories. The first is to set it when the file system is created; the second is to set it during the system init.

File System Configuration

When creating the file systems, following information will be utilized to set the mode, uid and guid of corresponding directories and files. Since M, OEM are allowed to override those rules with customized configuration.
static const struct fs_path_config android_dirs[] = {
    { 00770, AID_SYSTEM, AID_CACHE,  0, "cache" },
    { 00500, AID_ROOT,   AID_ROOT,   0, "config" },
    { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app" },
    { 00771, AID_ROOT,   AID_ROOT,   0, "data/dalvik-cache" },
    { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/data" },
    { 01771, AID_SYSTEM, AID_MISC,   0, "data/misc" },
    { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media" },
    { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" },
    { 00755, AID_ROOT,   AID_SYSTEM, 0, "mnt" },
    { 00755, AID_ROOT,   AID_ROOT,   0, "root" },
    { 00755, AID_ROOT,   AID_SHELL,  0, "system/bin" },
    { 00755, AID_ROOT,   AID_SHELL,  0, "system/vendor" },
    { 00755, AID_ROOT,   AID_SHELL,  0, "vendor" },
    { 00777, AID_ROOT,   AID_ROOT,   0, "sdcard" },
    { 00755, AID_ROOT,   AID_ROOT,   0, 0 },
};

static const struct fs_path_config android_files[] = {
    { 00555, AID_ROOT,      AID_ROOT,      0, "system/etc/rc.*" },
    { 00644, AID_MEDIA_RW,  AID_MEDIA_RW,  0, "data/media/*" },
    { 00755, AID_ROOT,      AID_SHELL,     0, "system/vendor/bin/*" },
    { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/bin/*" },
    { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/xbin/*" },
    { 00750, AID_ROOT,      AID_SHELL,     0, "sbin/*" },
    { 00755, AID_ROOT,      AID_ROOT,      0, "bin/*" },
    { 00750, AID_ROOT,      AID_SHELL,     0, "init*" },
    { 00640, AID_ROOT,      AID_SHELL,     0, "fstab.*" },
    { 00644, AID_ROOT,      AID_ROOT,      0, 0 },
};

System Init and init.rc

The second place to set mode/uid/gid of a particular file or directory is the init.rcs, which will be read by init process - the first user space program will be executed after kernel is ready.
The full description of the init.rc and the boot process is outside of the scope of this article. As far as what is relevant to the discussing here, it boils down to use chown and chmod to set the owner and mode for a particular file and directory.
on post fs-data
    # We chown/chmod /data again so because mount is run as root + defaults
    chown system system /data
    chmod 0771 /data

Ueventd and Device Node

One thing we are of particular interest are the UID and GID of device node. Since device nodes are the interface to the system hardware resources, a failure to enforce permission control on device node indicates a big security vulnerability.
ueventd is responsible for taking are of assigning the correct mode, UID and GID to the device node. It starts very early and will parse uventd.*.rc and set up the mode/uid/gid of corresponding device node. This is the third place you can tweak the mode, uid and guid for a file but it is specific for the device node.
ueventd.rc
/dev/alarm                0664   system     radio
/dev/rtc0                 0640   system     system
/dev/tty0                 0660   root       system
/dev/graphics/*           0660   root       graphics
/dev/input/*              0660   root       input
/dev/eac                  0660   root       audio
/dev/cam                  0660   root       camera
...
To recap, we have covered three places where you can set the mode/uid/gid for files and directories, that is 1) when you creating the file system, 2) when the system start running and 3) a special handling of the device nodes using ueventd.
Now, it is time to look at another part of the story - how the uid/gid are set for processes. First, we will check the system processes. And, normal app processes.

UID/GID of System Process

At the late stage of the init process, the core system services, such as servicemanager, vold and surfaceflinger, will be started. The UID and GID of the system process are specified in its corresponding .rc file. For example forsurfaceflinger, it's configuration is in the surfaceflinger.rc. It might worth note that, before M, the system process and its settings are all put into a centralized file called init.rc.
service surfaceflinger /system/bin/surfaceflinger
    class core
    user system
    group graphics drmrpc readproc
    onrestart restart zygote
    writepid /sys/fs/cgroup/stune/foreground/tasks
As you can see, each process is assigned a user and multiple groups. For example, surfaceflinger's UID is system and it belongs to three groups: graphics, drmrpc and readproc.
To show the USER ID of a process, use ps
myDevice # ps
system    427   1     171224 23988 S /system/bin/surfaceflinger
To show the Group IDs of a process, we can check the process’ related proc file.
myDevice # cat /proc/427/status
Name:   surfaceflinger
State:  S (sleeping)
Tgid:   427
Pid:    427
PPid:   1
TracerPid:  0
Uid:    1000    1000    1000    1000
Gid:    1003    1003    1003    1003
FDSize: 256
Groups: 1026 3009
We can see that surfaceflinger belongs to 1003 and 1026 groups, which are graphics and drmrpc respectively. (*1003 is the gid, 1026 and 3009 are the supplementary group it belongs to. See the proc main page for detail)

UID/GID of Normal App Process

Normal app will be assigned AID above 10000, and the GUID will be the same as AID.
To show the UID, use ps:
USER      PID   PPID  VSIZE   RSS            PC   NAME
u0_a46    5833  1096  2283376 144908 7e739ecab4 S com.android.camera2
To check the GID, check its proc file:
myDevice:/ # cat /proc/5833/status
Name:   android.camera2
State:  S (sleeping)
Tgid:   5833
Pid:    5833
PPid:   1096
TracerPid:  0
Uid:    10046   10046   10046   10046
Gid:    10046   10046   10046   10046
FDSize: 128
Groups: 3003
Note that the GID is the same as UID, which are 10046. It is easy to find out how it is related to the name u0_a46.
You may have noticed that there is supplementary groups ID the camera2 process belongs, 3003 (i.e AID_INET). It is related with what permission this app has been granted.

Permission and GUID for Apps

If an application requests certain permission and is granted, the corresponding group ID will be added to the process of the application. Part of the mapping between the permission and group id is shown as below:
<permission name="android.permission.BLUETOOTH" >
    <group gid="net_bt" />
</permission>
<permission name="android.permission.WRITE_MEDIA_STORAGE" >
    <group gid="media_rw" />
    <group gid="sdcard_rw" />
</permission>
<permission name="android.permission.INTERNET" >
    <group gid="inet" />
</permission>
We will use above camera2 app as an example to show how the permission is related to the group it is assigned.
To show the permissions granted for camera2 application, use dumpsys package: adb shell dumpsys package com.android.camera2
  install permissions:
  // other permissions are removed for clarity
  android.permission.INTERNET: granted=true
  gids=[3003]
As we can see, since camera2 app is granted INTERNET permission, which maps to the inet group, it has the supplementary groups 3003.

Apps With Special UID

One particular interest is to assign an app a special UID so it will be allowed to access resource that otherwise won't be able to access. By special UID, we usually mean the UID defined for system, i.e those belong to the range of 1000 to 1999. Can that be achieved?
Yes, we can do that by declaring android:sharedUserId="android.uid.xxxx" in the AndroidManifest.xml. In addition, the application also should be signed with the platform key by adding LOCAL_CERTIFICATE := platform in the Android.mk.
One example is the NFC app. Instead of having a normal u0_axx UID, Nfc app has the User ID nfc.
    nfc       4414  1096  1579708 59440 SyS_epoll_ 7e739ecab4 S com.android.nfc
And that is AID_NFC, 1027.
arche:/ # cat /proc/4414/status
Name:   com.android.nfc
Tgid:   4414
Pid:    4414
PPid:   1096
Uid:    1027    1027    1027    1027
Gid:    1027    1027    1027    1027
Groups: 3001 3002 3003 9997 41027
And that means the nfc app can access following device node directly!
myDevice:/ # ls -l /dev/pn54x
crw-rw---- 1 nfc nfc 10,  73 1970-01-09 20:14 /dev/pn54x
Here is an example how that is achieved in nfc app.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.nfc"
    android:sharedUserId="android.uid.nfc">
Being able to access the device node directly means lots of trusts; that is the reason the application request system UID must also be signed with platform certification.
LOCAL_PACKAGE_NAME := Nfc
LOCAL_CERTIFICATE := platform
Now, it is time to have some exercises.

Exercises

1. Can app1 can access app2’s data?

Normally, they can't.
drwxr-x--x u0_a21   u0_a21            1970-01-01 04:12 com.android.calendar
drwxr-x--x u0_a22   u0_a22            1970-01-01 00:54 com.android.camera2
App’s uid/gid are unique and the mode is set to “rw” only for the owner. So, app1 can’t access the data of app2.
But it can be done by sharing same uid and signed with same certification, as we discussed in Apps with Special UID.

2. Whether a process can access a certain device node?

It depends.
case 1 : same UID
root@myBoard:/ # ll /dev/ion
crw-rw-rw- system   media     10,  62 1970-01-01 00:00 ion
surfacflinger can access /dev/ion because surfaceflinger’s user is system and so is the /dev/ion.
A recap that the mode/uid/gid of /dev/ion is set in the ueventd.device.rc/dev/ion 0666 system media
case 2 : same Group
root@myBoard:/ # ll /dev/video0
crw-rw----   root     camera    81,   0 1970-01-01 00:00 video0
Despite that UID of video0 and the mediaserver are different (root and media respectively), but since they belongs to the same group (camera), and also the permission for group member is “rw”, so mediaserver can read and write /dev/video0node.

UID and Binder call

So far, we limit our definition of resources to be files. However, it can be something else, such as the ability to trigger certain system action, or more general, to do a Binder call.
For each binder call, at the server side, you can get its calling PID and UID, which can be used determine whether the call will be served or denied. This is the most basic but fundamental practice in Android to ensure the IPC security.

Summary

UID/GID based security control is a type of Discretionary Access Control(DAC). It is the fundamental part of Android's sandbox and security model to ensure the data and system security, so it's important to understand how it works.
Since Android 4.3, SELinux, as an implementation of Mandatory Access Control(MAC), has been utilized to overcome the limitation of DAC and to further improve the security of Android. We can talk it about it someday as well.

EDIT: Well, SELinux is discussed here.

Popular posts from this blog

Android Security: A walk-through of SELinux

In  DAC , each process has an owner and belong to one or several groups, and each resource will be assigned different access permission for its owner and group members. It is useful and simple. The problem is once a program gain root privileged, it can do anything. It has only three permissions which you can control, which is very coarse. SELinux is to fix that. It is much fine-grained. It has lots of permissions defined for different type of resources. It is based on the principle of default denial. We need to write rules explicitly state what a process, or a type of process (called domain in SELinux), are allowed to do. That means even root processes are contained. A malicious process belongs to no domain actually end up can do nothing at all. This is a great enhancement to the DAC based security module, and hence the name Security-Enhanced Linux, aka SELinux.

Java Collections Framework Cheat Sheet

Java Collections Framework (JCF) implements the  Abstract Data Type   for Java platform. Every serious Java programmer should familiar himself on this topic and be able to choose the right class for a specific need.  A thorough introduction to JCF is not the target of this small article and to achieve  that goal you can start with  this excellent tutorial  .