User-centric - programs run on behalf of users, thus can access any of the user’s files!
Running a UNIX program:
fork
+ exec
ld.so
dynamic library loading + sharing → memory savingComposition on shell:
pipe
s + fork
+ exec
$ echo $PATH | tr : "\n" | wc -l
files
directories
/proc/*
/dev/*
→ Shell composition of programs can process on all system resources!
→ UNIX APIs for file manipulation manipulate all resources (polymorphism)!
setuid
BitsWe can use setuid
bits to coordinate services that serve
multiple users.
/etc/passwd
)setuid
bit programs update filesdaemons
) consume and act on filesroot
root
permission is all or nothingroot
required for many roles
(e.g. chown
)Examples:
root
required for login
? Only
require 1. access to password file, and 2. ability to
setuid
to user./etc/
owned by
root
; why are the files in /dev/
all owned by
root
(e.g. /dev/mem
)Traditional services:
→ Each user (and all of their programs) can interact with the service
Should everyone’s programs be able to interact with core services?
Discretionary Access Control (DAC): Users restrict access to their own files/data, and can indiscriminately pass that privilege.
Mandatory Access Control (MAC): Users restricted in sharing files/data by system-wide policies that restrict sharing relationships.
All of a user’s data is accessible to every program they execute. Assume a program will be attacked and compromised. How can we minimize its negative effects?
Principle of least privilege: Only give the access required to accomplish a program’s goals.
Examples:
App-store model is central
Apps cannot access each other’s resources!
Default: share nothing.
App-centric - programs have separate, limited access to files
Sensitive resources are not just files
Must limit App access to each of these, based on App’s goals/requirements.
By-default deny App access to resources, mediated IPC
Idea: Apps are not trusted. Isolate them from each other!
They are run by the user, but
Example: Banking App running alongside flashlight app
Services oversee key abstraction/resources
Resources include
Only Apps with permissions given by the user should be able to communicate with these services.
UNIX programs:
fork
+ exec
ld.so
dynamic library loadingJava VM program execution:
fork
classpath
)imports
)An application’s computation is always within a JVM. This means:
JVM-first execution abstractions
The Java VM has a huge number of large packages
Zygote: single process that is the parent of all Apps and Services.
fork
s the new App/Servicesetuid
to the App’s uid
(zygote is
root
)Apps have uid
/gid
s, not users
Apps have “home directories” for private storage
No facilities for filesystem sharing between applications
Apps explicitly specify their resource requirements
Apps have a permission manifest: what resources they can access.
<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>
All of the system’s potential permissions.
READ_CONTACTS
permissionApp and Service permissions are tracked and queried:
PermissionManager
+ PackageManager
return AppGlobals.getPackageManager()
.checkUidPermission(permission, uid);
Applications have access only to a small part of the filesystem
uid
and gid
are a unique, per-app
values app_N
. I.e. app_6
/data/app/<app_name>/
binary and “ELF-like”
data/data/data/<app_name>/
storageIf Apps cannot access each other’s data, how do we have a cohesive, single experience as user?
Our human experience is clicking buttons, and seeing actions. The system need only provide the illusion of a single, shared device.
How?
Illusion of a single, shared device:
Apps communicate to services that provide shared resources
Composition of Apps: communication through intents
/* 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, ... */
#define AID_APP 10000 /* first app user */
#define AID_USER 100000 /* offset for uid ranges for each user */
0
/1000
- root
+ System,
1001...1052
- Devices1053
- Zygote, 10000+
- AppsBinder: domain-socket analog for IPC
names
names
can mimic function calls→ cross App/Service “function invocations”
IPC Connections can be passed to Apps
ServiceManager
nameserver: “here you go, have this
connection”uid/gid
of Apps using connection can be queried
AID_INPUT
to be able
to input?Package/PermissionManager
:
“are they allowed?”Who is making a request to a service? Do they have permissions?
pid
and euid
Binder.getCallingPid()
&
Binder.getCallingUid()
ActivityManager.checkComponentPermission(permission, uid, owningUid, exported)
android.os.ServiceManager
includes:
public static void addService(String name, IBinder service) { /* ... */ }
public static IBinder getService(String name) { /* ... */ }
We can see that at its core, Android has a nameserver to map names to IPC channels.
surfaceflinger
to display
contents)Coordination:
UNIX focuses on pipelines for composition
stdin
, output on stdout
pipe
s for compositionread
/write
callsAndroid:
Intents: simple action you’d like to perform, identified by a name
List of default intents. These include:
ActivityManager
is nameserver for intents
// java code to send an email!
public void composeEmail(String[] addresses, String subject, Uri attachment) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("*/*");
intent.putExtra(Intent.EXTRA_EMAIL, addresses);
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
intent.putExtra(Intent.EXTRA_STREAM, attachment);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
System starts out by triggering the
PRE_BOOT_COMPLETED
intent
Homescreen App displays homescreen (or “update” might run)
Homescreen triggers BOOT_COMPLETED
intent
Homescreen App detects click → triggers App intent
If App is running, pass it the message, otherwise start it!
App might trigger many intents
INPUT_METHOD_SERVICE
to read from onscreen
keyboardACTION_SEND
to send emailActivityManager
implements this intent
system
SystemServer
processFor functionality:
void startActivity (Intent intent)
intent
the
InputManager
multiplexes user input between
InputMethod
s.
stdin
? Up to the system
to decide if stdin
is a pipe, or a terminal.
Polymorphic!InputMethod
s
(link) are things like on-screen keyboards that are Services
activated by intents from the InputManager
.
INPUT_METHOD_SERVICE
intent0
”; we know it
will give us input.App →
ActivityManager
(INPUT_METHOD_SERVICE
) → InputMethod
uid
/pid
of clientActivityManager
checks if the client has permissions to
access resource/trigger intentroot
// frameworks/base/core/java/android/app/ActivityManager.java
public class ActivityManager {
public static int checkComponentPermission(String permission, int uid,
int owningUid, boolean exported) {
final int appId = UserHandle.getAppId(uid);
if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
return PackageManager.PERMISSION_GRANTED;
}
if (UserHandle.isIsolated(uid)) {
return PackageManager.PERMISSION_DENIED;
}
if (owningUid >= 0 && UserHandle.isSameApp(uid, owningUid)) {
return PackageManager.PERMISSION_GRANTED;
}
if (!exported) {
return PackageManager.PERMISSION_DENIED;
}
if (permission == null) {
return PackageManager.PERMISSION_GRANTED;
}
try {
return AppGlobals.getPackageManager()
.checkUidPermission(permission, uid);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
uid
) for each App/Serviceuid
/pid
of calling
AppPackage/PermissionManager
lets us query App
permissions→ foundation for per-App access control/privileges
SystemManager
nameserver maps unique
name
to a Service
Apps use IPC to request resources from Services
Zygote as the parent of each App
fork
setuid
to restrict App’s privilegesIntent
sApps and Services are activated by intents.
Polymorphic
PRE_BOOT_COMPLETED
intent.INPUT_METHOD_SERVICE
intent.Drives App/Service activation
Communication driven by the ActivityManager
UNIX:
open(Stringpath) → intdescriptor
uid
/gid
descriptortable(intdescriptor) → char[]file
Android:
open(Stringpath) → intdescriptor
uid
/gid
descriptortable(intdescriptor) → char[]file
and
ActivityManager(StringIntent) → BinderChannel
PackageManager
BinderChannel(fn, args…) Service
POSIX | Android |
---|---|
User-centric | App-centric, per-App permissions |
Security through FS | Security through permission manifests |
Shared resources on FS | Shared resources via Services |
Everything’s a file | Everything’s App (JVM) execution |
Shell + pipe composition |
Composition via App IPC through Intent s |
Similar set of system calls to normal UNIX system…
…augmented with pervasive use of IPC…
…to coordinate between Apps and Services…
…while checking per-App permissions…
…all driven by the special trust relationship of untrusted Apps.