The Problem:
Define a policy
to control how various clients
can access
different resources
.
A solution:
- Each
resource
has an owner
and belongs to a group
.
- Each
client
has an owner
but can belongs to multiple groups
.
- Each
resource
has a mode
stating the access permissions
allowed for its owner
, group
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.
- How does Android set up the owner, groups and mode of a resource?
- How does Android set up the owner and groups of a process?
- 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..
policy
to control how various clients
can access
different resources
.resource
has an owner
and belongs to a group
.client
has an owner
but can belongs to multiple groups
.resource
has a mode
stating the access permissions
allowed for its owner
, group
members and others, respectively.resources
can be files, sockets, etc; the clients
are actually processes; and we have three access permissions
:read, write and execute.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.
/* 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 */
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 },
};
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.rc
s, 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
init.rc
s, which will be read by init
process - the first user space program will be executed after kernel is ready.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.
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
...
ueventd
.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)
.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
user
and multiple groups
. For example, surfaceflinger's UID is system and it belongs to three groups: graphics, drmrpc and readproc.ps
myDevice # ps
system 427 1 171224 23988 S /system/bin/surfaceflinger
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
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.
ps
:USER PID PPID VSIZE RSS PC NAME
u0_a46 5833 1096 2283376 144908 7e739ecab4 S com.android.camera2
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
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.
<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>
adb shell dumpsys package com.android.camera2
install permissions:
// other permissions are removed for clarity
android.permission.INTERNET: granted=true
gids=[3003]
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.
AndroidManifest.xml
. In addition, the application also should be signed with the platform key by adding LOCAL_CERTIFICATE := platform
in the Android.mk. nfc 4414 1096 1579708 59440 SyS_epoll_ 7e739ecab4 S com.android.nfc
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
myDevice:/ # ls -l /dev/pn54x
crw-rw---- 1 nfc nfc 10, 73 1970-01-09 20:14 /dev/pn54x
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.nfc"
android:sharedUserId="android.uid.nfc">
LOCAL_PACKAGE_NAME := Nfc
LOCAL_CERTIFICATE := platform
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.
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
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/video0
node.
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./dev/ion
is set in the ueventd.device.rc
. /dev/ion 0666 system media
root@myBoard:/ # ll /dev/video0
crw-rw---- root camera 81, 0 1970-01-01 00:00 video0
mediaserver
can read and write /dev/video0
node.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.
EDIT: Well, SELinux is discussed here.