What are Android Permissions ?
Android uses a fine-grained permission to control the application access to resources. APIs for accessing sensitive resources are actions are protected by permissions, the application developer must declare what permissions the application needs to operate correctly within the application manifest file “manifest.xml”. The syntax for that is :
where android:name is the name of the required permission, and android:maxSdkversion is the highest API level at which the application needs to be granted this permission. Ths attribute is useful if the permission declared is no longer needed after a certain API level.
The list of all permissions defined by the base Android operating system can be found here:
In addition to those, the application developer can also define new permissions to control what other applications can interact with his application.
Example:
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
|
This indicates that the application asks for being granted the “android.permission.WRITE_EXTERNAL_STORAGE” when installed on API level 18 or lower.
At the time of application installation, the user will presented with a list of all permissions requested by the application (as declared in the manifest file) and it is up to him to proceed with the installation process, therefore granting it all permissions it needs, or cancel the installation process.
How permissions are maintained in Android ?
In Android, the PackageManger is responsible for managing the packages installation and permissions. You can use the package manager to view all installed packages on your Android system:
pm list packages
|
or list all permissions (including those that are defined by 3rd party application developers):
pm list permissions
|
Or you can also use it to install an application
pm install <APK_filename>
|
or to grant/ revoke permission to a package ?
pm grant <PACKAGE> <PERMISSION>
pm revoke <PACKAGE> <PERMISSION>
|
When you attempt to install an application, The PackageInstallerActivity invokes the PackageManager that is starts the installation process. The package manager communicates with the PackageManagerService to get the app installed via the installd daemon. The PackageManager runs within the “system_server” process and the installd runs as a native service. Both of install daemon (installd) and system_server are started at the boot time.
( Figure Courtesy of http://kpbird.blogspot.com/2012/10/in-depth-android-package-manager-and.html )
How does the installation process happen ?
A major difference between Android and Linux is that Android isolates each application from other applications to prevent malicious applications from snooping or crashing other installed application. This approach is known as “Sandboxing”, Android achieves this by considering each application as a different user.
At installation time, the PackageManager copies the APK file and any native libraries within the app to “/data/app/<package name>” directory (or “/system/app/<package name>” for system applications). Then, it decides a unique identifiers (UID) for the new application. The application UIDs are chosen in the range between 10,000 and 99,999 . As defined in ANDROID_FILESYSTEM_CONFIG
In addition to the UID, the application is also assigned a username to the maps to the UID (For example application with UID 10032 is assigned the user name u0_a32 ) and a data directory at “/data/data/<package name>”. Permissions for the data directory are set appropriately such that it is owned by the new created user for this application.
Although that by default Android assigns to each application a unique UID, two application can request to be running under the same UID by setting the same value of android:sharedUserId in their manifest files, this is only possible if both application are going to be signed by the same developer certificate. The two applications that share the same user id will share the same data directory and , if desired, run in the same process. The role of code signing in Android Security model is explained in more detail in this article.
For example, the application “nesl.ucla.edu.cameraap1” has been assigned the UID : 10057 and the user name “u0_a57”. The data directory for that app /data/data/nesl.ucla.edu.cameraapp1” is therefore owned by “u0_a57” user.
Also during the installation, the PackageManager adds new entries for the new installed package in “/data/system/packages.xml” and “/data/system/packages.list” files.
The “packages.xml” also maintains information about installed packages, including the packagename, app directory (location of apk, and native libraries), their user ID, version number (as extracted from the manifest file), the certificate key by which the APK was signed and the list of permissions that have been granted to the application. Permissions are stored under the <perms> tag. Adding or removing items from this section may grant or revoke permission to the application. However, modification will not be reflected until the system_server (hosting the PackageManagerService) is restarted since the application permissions are cached as we will see later.
<package name="nesl.ucla.edu.cameraapp1" codePath="/data/app/nesl.ucla.edu.cameraapp1-2" nativeLibraryPath="/data/app/nesl.ucla.edu.cameraapp1-2/lib" flags="572998" ft="14cca4e4b58" it="14cca4cf54b" ut="14cca4e4ea5" version="1" userId="10057">
<sigs count="1">
<cert index="6" />
</sigs>
<perms>
<item name="android.permission.CAMERA" />
</perms>
<proper-signing-keyset identifier="48" />
<signing-keyset identifier="48" />
</package>
|
while the “packages.list” file lists all installed packages their user identifiers (UID), the location of their data directory, and the identifier of any groups (GIDs) they are member of.
nesl.ucla.edu.wifistatuslog 10058 1 /data/data/nesl.ucla.edu.wifistatuslog default 3003
nesl.ucla.edu.cameraapp1 10057 1 /data/data/nesl.ucla.edu.cameraapp1 default none
|
Note that granting some permissions to an application results in adding the applications UID into a members group. For example the package “nesl.ucla.edu.wifistatuslog” (u0_a58) is a member of 3003 group (inet) because it has been granted the “android.permission.INTERNET” permission. The mapping between permissions and group IDs can be found at:
“/etc/permissions/platform.xml” while the group identifier (GID) values for the groups are defined in ANDROID_FILESYSTEM_CONFIG.. Those kinds of permissions are enforced by at the low-level byl kernel itself.
Where does permission checking happen?
When the android device boots, the first process started by the linux kernel is init (PID: 1) process that executes shell scripts defined inside /init.rc.
This goal of init.rc is to initialize the system environment by executing a series of commands.
The init.rc defines the user under each each service is going to run (for example: Zygote will run under the root user, while keystore service will be started under the keystore user).
One of the early processes started by init is the Zygote. Zygote is a daemon whose goal is to start additional services and to load libraries. It acts as a loader for each Dalvik process by creating a copy of itself (e.g. forking it self). The zygote is started by executing the “system/bin/app_process” command.
Then it is the Zygote’s rule to start the “system_server” process. Therefore, the Zygote acts as the parent process oft he “system_server” and for any later running process.
ps | grep Zygote
|
Here, we notice that the Zygote is running the “root” user with PID = 3085 and PPID = 1
We also note that other running processes including the system_server have the Zygote’s PID (3085) assigned as their PPID.
The the system_server will start threads the android critical services that include the PackageManagerService.
ps -t -p 3339
|
Looking inside the source code of PackageMangerService
Within the constructor, PackageManagerService instantiates an object (mSettings) of Settings class
The object mSettings will be used deserialize package configurations from “packages.xml” file by calling:
mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),
1393 mSdkVersion, mOnlyCore); |
Also: updates to the package settings are serialized to the “packages.xml” file using the mSettings instance.
The packageManagerService exposes two public methods: checkPermission, and checkUidPermission function that is given a package name or application UID have been granted the permission identified by permName, it will return either PackageManager.PERMISSION_GRANTED or PackageManager.PERMISSION_DENIED.
public int checkPermission(String permName, String pkgName) {
2373 synchronized (mPackages) { 2374 PackageParser.Package p = mPackages.get(pkgName); 2375 if (p != null && p.mExtras != null) { 2376 PackageSetting ps = (PackageSetting)p.mExtras; 2377 if (ps.sharedUser != null) { 2378 if (ps.sharedUser.grantedPermissions.contains(permName)) { 2379 return PackageManager.PERMISSION_GRANTED; 2380 } 2381 } else if (ps.grantedPermissions.contains(permName)) { 2382 return PackageManager.PERMISSION_GRANTED; 2383 } 2384 } 2385 } 2386 return PackageManager.PERMISSION_DENIED; 2387 } |
@Override
2390 public int checkUidPermission(String permName, int uid) { 2391 synchronized (mPackages) { 2392 Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid)); 2393 if (obj != null) { 2394 GrantedPermissions gp = (GrantedPermissions)obj; 2395 if (gp.grantedPermissions.contains(permName)) { 2396 return PackageManager.PERMISSION_GRANTED; 2397 } 2398 } else { 2399 ArraySet<String> perms = mSystemPermissions.get(uid); 2400 if (perms != null && perms.contains(permName)) { 2401 return PackageManager.PERMISSION_GRANTED; 2402 } 2403 } 2404 } 2405 return PackageManager.PERMISSION_DENIED; 2406 } |
Case Analysis : LocationManager
Within the LocationManagerService, the function getAllowedResolutionLevel is used to return finest resolution level allowed for the calling application. It returns RESOLUTION_LEVEL_NONE if the application is not granted location access at all.
private int getAllowedResolutionLevel(int pid, int uid) {
1006 if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, 1007 pid, uid) == PackageManager.PERMISSION_GRANTED) { 1008 return RESOLUTION_LEVEL_FINE; 1009 } else if (mContext.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION, 1010 pid, uid) == PackageManager.PERMISSION_GRANTED) { 1011 return RESOLUTION_LEVEL_COARSE; 1012 } else { 1013 return RESOLUTION_LEVEL_NONE; 1014 } 1015 } |