Android’s Sandbox
Android’s foundation of Linux brings with it a well-understood heritage of Unix-like process isolation and the principle of least privilege. Specifically, the concept that processes running as separate users cannot interfere with each other, such as sending signals or accessing one another’s memory space. Ergo, much of Android’s sandbox is predicated on a few key concepts: standard Linux process isolation, unique user IDs (UIDs) for most processes, and tightly restricted file system permissions.
Android shares Linux’s UID/group ID (GID) paradigm, but does not have the traditional passwd and group files for its source of user and group credentials. Instead, Android defines a map of names to unique identifiers known as Android IDs (AIDs). The initial AID mapping contains reserved, static entries for privileged and system-critical users, such as the system user/group. Android also reserves AID ranges used for provisioning app UIDs. Versions of Android after 4.1 added
additional AID ranges for multiple user profiles and isolated process users (e.g., for further sandboxing of Chrome). You can find definitions for AIDs in system/core/include/private/android_filesystem_config.h in the Android Open Source Project (AOSP) tree. The following shows an excerpt that was edited for brevity:
#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_SHELL 2000 /* adb and debug shell user */
#define AID_CACHE 2001 /* cache access */
#define AID_DIAG 2002 /* access to diagnostic resources */
/* The 3000 series are intended for use as supplemental group id’s only.
* They indicate special Android capabilities
that the kernel is aware of. */
#define AID_NET_BT_ADMIN 3001 /* bluetooth: create any socket */
#define AID_NET_BT 3002 /* bluetooth: create sco,
rfcomm or l2cap sockets */
#define AID_INET 3003 /* can create AF_INET and
AF_INET6 sockets */
#define AID_NET_RAW 3004 /* can create raw INET sockets */
…
#define AID_APP 10000 /* first app user */
#define AID_ISOLATED_START 99000 /* start of uids for fully
isolated sandboxed processes */
#define AID_ISOLATED_END 99999 /* end of uids for fully
isolated sandboxed processes */
#define AID_USER 100000 /* offset for uid ranges for each user */
In addition to AIDs, Android uses supplementary groups to enable processes to access shared or protected resources. For example, membership in the sdcard_rw group allows a process to both read and write the /sdcard directory, as its mount options restrict which groups can read and write. This is similar to how supplementary groups are used in many Linux distributions.
Aside from enforcing file system access, supplementary groups may also be used to grant processes additional rights. The AID_INET group, for instance, allows for users to open AF_INET and AF_INET6 sockets. In some cases, rights may also come in the form of a Linux capability. For example, membership in the AID_INET_ADMIN group grants the CAP_NET_ADMIN capability, allowing the user to configure network interfaces and routing tables. Other similar, network-related groups are cited later in the “Paranoid Networking” section.
In version 4.3 and later, Android increases its use of Linux capabilities. For example, Android 4.3 changed the /system/bin/run-as binary from being set-UID root to using Linux capabilities to access privileged resources. Here, this capability facilitates access to the packages.list file.
When applications execute, their UID, GID, and supplementary groups are assigned to the newly created process. Running under a unique UID and GID enables the operating system to enforce lower-level restrictions in the kernel, and for the runtime to control inter-app interaction. This is the crux of the Android sandbox.
The following snippet shows the output of the ps command on an HTC One V. Note the owning UID on the far left, each of which are unique for each app process:
app_16 4089 1451 304080 31724 … S com.htc.bgp
app_35 4119 1451 309712 30164 … S com.google.android.calendar
app_155 4145 1451 318276 39096 … S com.google.android.apps.plus
app_24 4159 1451 307736 32920 … S android.process.media
app_151 4247 1451 303172 28032 … S com.htc.lockscreen
app_49 4260 1451 303696 28132 … S com.htc.weather.bg
app_13 4277 1451 453248 68260 … S com.android.browser
Applications can also share UIDs, by way of a special directive in the application package. This is discussed further in the “Major Application Components” section.
Under the hood, the user and group names displayed for the process are actually provided by Android-specific implementations of the POSIX functions typically used for setting and fetching of these values. For instance, consider the getpwuid function (defined in stubs.cpp in the Bionic library):
345 passwd* getpwuid(uid_t uid) { // NOLINT: implementing bad function.
346 stubs_state_t* state = __stubs_state();
347 if (state == NULL) {
348 return NULL;
349 }
350
351 passwd* pw = android_id_to_passwd(state, uid);
352 if (pw != NULL) {
353 return pw;
354 }
355 return app_id_to_passwd(uid, state);
356 }
Like its brethren, getpwuid in turn calls additional Android-specific functions, such as android_id_to_passwd and app_id_to_passwd. These functions then populate a Unix password structure with the corresponding AID’s information. The android_id_to_passwd function calls android_iinfo_to_passwd to accomplish this:
static passwd* android_iinfo_to_passwd(stubs_state_t* state,
const android_id_info* iinfo) {
snprintf(state->dir_buffer_, sizeof(state->dir_buffer_), “/”);
snprintf(state->sh_buffer_, sizeof(state->sh_buffer_),
“/system/bin/sh”);
passwd* pw = &state->passwd_;
pw->pw_name = (char*) iinfo->name;
pw->pw_uid = iinfo->aid;
pw->pw_gid = iinfo->aid;
pw->pw_dir = state->dir_buffer_;
pw->pw_shell = state->sh_buffer_;
return pw;
}