Sunday, April 26, 2015

Dissecting Android Permissions

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 :


<uses-permission android:name="string"
       android:maxSdkVersion="integer" />


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.


Package+Installer.jpg
( 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


#define AID_APP          10000  /* first app user */


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.




ps -t -p 3339



Looking inside the source code of PackageMangerService


Within the constructor, PackageManagerService instantiates an object (mSettings) of Settings class


1300        mSettings = new Settings(context);


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.


1125                        mSettings.writeLPr();

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    }




No comments:

Post a Comment