Binder
is a kernel device driver that uses Linux’s shared memory feature to
achieve efficient, secure IPC. System services are published as Binder
interfaces and the AIDL (Android Interface Definition Language) is used
not just to define system interfaces, but to allow developers to create
their own Binder clients and servers. The terminology can be confusing,
but servers generally subclass android.os.Binder and implement the
onTransact() method, whereas clients receive a Binder interface as an
android.os.IBinder reference and call its transact() method. Both
transact() and onTransact() use instances of android.os.Parcel to
exchange data efficiently (the native implementation of this Parcel
formats data as it is expected by the kernel mode Binder device).
Android’s support for Binder includes the interface Parcelable.
Parcelable objects can be moved between processes through a Binder.
Under
the covers, a Binder reference is a descriptor maintained by the Binder
device (which is a kernel mode device driver). Binder IPC can be used
to pass and return primitive types, Parcelable objects, file
descriptors (which also allows memory maps), and Binders. Having a
reference to a Binder interface allows calls to its interface—that is,
you can call transact() and have a corresponding call to onTransact()
occur on the server side—but this does not guarantee that the service
exposing the interface will do what the caller requests. For example,
any program can get a reference to the Zygote system service’s Binder
and call the method on it to launch an application as some other user,
but Zygote will ignore such requests from unauthorized processes.
Binder security has two key ways it can enforce security: by checking the caller’s identity and via Binder reference security.
Security by Caller Permission or Identity Checking
When a Binder interface is
called, the identity of the caller is securely provided by the kernel.
Android associates the calling application’s identity with the thread
on which the request is handled (the application’s UID and its
process’s current PID are provided). This allows the recipient to use
their Context’s checkCallingPermission(String permission) or
checkCallingPermissionOrSelf (String permission) method to validate the
caller’s rights. Applications commonly want to enforce permissions they
don’t have on callers; therefore, checkCallingPermissionOrSelf(String
permission) allows the application to still call itself even if it
lacks the normally needed permission. Binder services are free to make
other binder calls, but these calls always occur with the service’s own
identity (UID and PID) and not the identity of the caller.
Binder services also have
access to the caller’s identity using the getCallingUid() and
getCallingPid() static methods of the Binder class. These methods
return the UID and process identifier (PID) of the process that made
the Binder call. The identity information is securely communicated to
the implementer of a Binder interface by the kernel. This is similar to
how Unix domain sockets can tell you the identity of the caller, or
most IPC mechanisms in Win32.
A Binder interface can be
implemented a number of ways. The simplest is to use the AIDL compiler
to create a Stub class, which you then subclass. Inside the
implementations of the methods, the caller is automatically associated
with the current thread, so calling Binder.getCallingUid() identifies
the caller. Developers who direct requests to handlers or implement
their own onTransact() (and forego AIDL) must realize that the identity
of the caller is bound to the thread the call was received on and
therefore must be determined before switching to a new thread to handle
a request. A call to Binder.clearCallingIdentity() will also stop
getCallingUid() and getCallingPid() from identifying the caller.
Context’s checkPermission(String permission, int pid, int uid) method
is useful for performing permission checks, even after the caller’s
identity has been cleared by using stored UID and PID values.
Binder Reference Security
Binder references can be
moved across a Binder interface. The Parcel.writeStrongBinder() and
Parcel.readStrongBinder() methods allow this and provide some security
assurances. When reading a Binder reference from a Parcel with
readStrongBinder(), the receiver is assured (by the kernel’s Binder
driver) that the writer of that Binder had a reference to the received
Binder reference. This prevents callers from tricking servers by
sending guesses of the numerical value used in the server’s process to
represent a Binder the caller doesn’t have.
Getting a reference to a
Binder isn’t always possible. (By Binder, I mean a reference to a
Binder interface. In Java, this is represented by an android.os.Binder
object.) Because servers can tell if callers had a particular Binder,
not giving out references to a Binder can effectively be used as a
security boundary. Although Zygote might not protect its Binder
interfaces from exposure, many Binder objects are kept private. To use
reference security, processes need to carefully limit the revealing of
Binder objects. Once a process receives a Binder, it can do whatever it
likes with it, passing it to others or calling its transact() method.
Binders are globally
unique, which means if you create one, nobody else can create one that
appears equal to it. A Binder doesn’t need to expose an interface—it
might just serve as a unique value. A Binder can be passed between
cooperating processes. A service could provide callers a Binder that
acts as a key, knowing that only those who receive the key (or had it
sent to them) can later send it back. This acts like an unguessable,
easily generated password. The Activity Manager uses the reference
nature of Binders to control the management of Surfaces and Activities.