Advanced Permissions

Adding basic permissions to your app to allow it to, say, access the Internet, is fairly easy. However, the full permissions system has many capabilities beyond simply asking the user to let you do something. This chapter explores other uses of permissions, from securing your own components to using signature-level permissions (your own or Android’s).

Prerequisites

Understanding this chapter requires that you have read the core chapters, particularly the chapter on permissions and the chapter on signing your app.

One of the sample apps uses RxJava and RxAndroid, which are introduced elsewhere in the book.

Securing Yourself

Principally, at least initially, permissions are there to allow the user to secure their device. They have to agree to allow you to do certain things, such as reading contacts, that they might not appreciate.

The other side of the coin, of course, is to secure your own application. If your application is mostly activities, security may be just an “outbound” thing, where you request the right to use resources of other applications. If, on the other hand, you put content providers or services in your application, you will want to implement “inbound” security to control which applications can do what with the data.

Note that the issue here is less about whether other applications might “mess up” your data, but rather about privacy of the user’s information or use of services that might incur expense. That is where the stock permissions for built-in Android applications are focused – can you read or modify contacts, can you send SMS, etc. If your application does not store information that might be considered private, security is less an issue. If, on the other hand, your application stores private data, such as medical information, security is much more important.

The first step to securing your own application using permissions is to declare said permissions, once again in the AndroidManifest.xml file. In this case, instead of uses-permission, you add permission elements. Once again, you can have zero or more permission elements, all as direct children of the root manifest element.

Declaring a permission is slightly more complicated than using a permission. There are three pieces of information you need to supply:


<permission
  android:name="vnd.tlagency.sekrits.SEE_SEKRITS"
  android:label="@string/see_sekrits_label"
  android:description="@string/see_sekrits_description" />

This does not enforce the permission. Rather, it indicates that it is a possible permission; your application must still flag security violations as they occur.

Enforcing Permissions via the Manifest

There are two ways for your application to enforce permissions, dictating where and under what circumstances they are required. The easier one is to indicate in the manifest where permissions are required.

Activities, services, and receivers can all declare an attribute named android:permission, whose value is the name of the permission that is required to access those items:


<activity
  android:name=".SekritApp"
  android:label="Top Sekrit"
  android:permission="vnd.tlagency.sekrits.SEE_SEKRITS">
  <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category
      android:name="android.intent.category.LAUNCHER" 
/>
  </intent-filter>
</activity>

Only applications that have requested your indicated permission will be able to access the secured component. In this case, “access” means:

  1. Activities cannot be started without the permission
  2. Services cannot be started, stopped, or bound to an activity without the permission
  3. Intent receivers ignore messages sent via sendBroadcast() unless the sender has the permission

Enforcing Permissions Elsewhere

In your code, you have two additional ways to enforce permissions.

Your services can check permissions on a per-call basis via checkCallingPermission(). This returns PERMISSION_GRANTED or PERMISSION_DENIED depending on whether the caller has the permission you specified. For example, if your service implements separate read and write methods, you could require separate read versus write permissions in code by checking those methods for the permissions you need from Java.

Also, you can include a permission when you call sendBroadcast(). This means that eligible broadcast receivers must hold that permission; those without the permission are ineligible to receive it. We will examine sendBroadcast() in greater detail elsewhere in this book.

Requiring Standard System Permissions

While normally you require your own custom permissions using the techniques described above, there is nothing stopping you from reusing a standard system permission, if it would fit your needs.

For example, suppose that you are writing YATC (Yet Another Twitter Client). You decide that in addition to YATC having its own UI, you will design YATC to be a “Twitter engine” for use by third party apps:

You could, and perhaps should, implement your own custom permission. However, since any app can get to Twitter just by having the INTERNET permission, one could argue that a third-party app should just need that same INTERNET permission to use your API (rather than integrating JTwitter or another third-party JAR).

Signature Permissions

Each permission in Android is assigned a protection level, via an android:protectionLevel attribute on the <permission> element. By default, permissions are at a normal level, but they can also be flagged as dangerous, signatureOrSystem, or signature. In the latter two cases, “signature” means that the app requesting the permission and the app requiring the permission should have be signed by the same signing key. In the case of signatureOrSystem — only used by the firmware – the app requesting the permission either needs to be signed by the firmware’s signing key or reside on the system partition (e.g., come pre-installed with the device).

Firmware-Only Permissions

Most of Android’s permissions mentioned in this book are ones that any SDK application can hold, if they ask for them and the user grants them. INTERNET, READ_CONTACTS, ACCESS_FINE_LOCATION, and kin all are normal permissions.

BRICK is not.

There was a permission in Android, named BRICK, that, in theory, allows an application to render a phone inoperable (a.k.a., “brick” the phone). While there is no brickMe() method in the Android SDK tied to this permission, presumably there might be something deep in the firmware that was protected by this permission. Though, since Android 6.0 removed the BRICK permission from the SDK, it is clearly not something Google expects us to use.

The BRICK permission could not be held by ordinary Android SDK applications. You could request it all you want, and it will not be granted.

However, applications that are signed with the same signing key that signed the firmware could hold the BRICK permission.

That is because the system’s own manifest used to have the following <permission> element:


<permission android:name="android.permission.BRICK"
        android:label="@string/permlab_brick"
        android:description="@string/permdesc_brick"
        android:protectionLevel="signature" />

Your Own Signature Permissions

You too can require signature-level permissions. That will restrict the holders of that permission to be other apps signed by your signing key. This is particularly useful for inter-process communication between apps in a suite — by using signature permissions, you ensure that only your apps will be able to participate in those communications.

One nice thing about these sorts of signature-level permissions is that the user is not bothered with them. It is assumed that the user will agree to the communication between the apps signed by the same signing key. Hence, the user will not see signature-level permissions at install or upgrade time.

Since in some cases, you may not be sure which app will be installed first, it is best to have all apps in the suite include the same <permission> element, in addition to the corresponding <uses-permission> element. That way, no matter which app is installed first, it can declare the permission that all will share.

Though, that has its own problems, as you will see in the next section.

The Custom Permission Vulnerability

(NOTE: Some of the material in this section originally appeared in material hosted in the CWAC-Security project repository. In addition, the author would like to thank Mark Carter and “Justin Case” for their contributions in this topic area).

Unfortunately, custom permissions have some undocumented limitations that make them intrinsically risky. Specifically, custom permissions can be defined by anyone, at any time, and “first one in wins”, which opens up the possibility of unexpected behavior.

Here, we will walk through some scenarios and show where the problems arise, plus discuss how to mitigate them as best we can.

Scenarios

All of the following scenarios focus on three major app profiles.

App A is an app that defines a custom permission in its manifest, such as:


<permission
  android:name="com.commonsware.cwac.security.demo.OMG"
  android:description="@string/perm_desc"
  android:label="@string/perm_label"
  android:protectionLevel="normal"/>

App A also defends a component using the android:permission attribute, referencing the custom permission:


<provider
  android:name="FileProvider"
  android:authorities="com.commonsware.cwac.security.demo.files"
  android:exported="true"
  android:grantUriPermissions="false"
  android:permission="com.commonsware.cwac.security.demo.OMG">
  <grant-uri-permission android:path="/test.pdf"/>
</provider>

App B has a <uses-permission> element to declare to the user that it wishes to access components defended by that permission:


<uses-permission android:name="com.commonsware.cwac.security.demo.OMG"/>

App C has the same <uses-permission> element. The difference is that App B also has the <permission> element, just as App A does, albeit with different descriptive information (e.g., android:description) and, at times, a different protection level.

All three apps are signed with different signing keys, because in the real world they would be from different developers.

So, to recap:

With all that in mind, let’s walk through some possible scenarios, focusing on two questions:

  1. What is the user told, when the app is installed through normal methods (i.e., not via adb), regarding this permission?
  2. What access, if any, does App B or App C have to the ContentProvider from App A?

The Application SDK Case (A, Then C)

Suppose the reason why App A has defined a custom permission is because it wants third-party apps to have the ability to access its secured components… but only with user approval. By defining a custom permission, and having third-party apps request that permission, the user should be informed about the requested permission and can make an informed decision.

Conversely, if an app tries to access a secured component but has not requested the permission, the access attempt should fail.

App C has requested the custom permission via the <uses-permission> element. If the permission — defined by App A — has an android:protectionLevel of normal or dangerous, the user will be informed about the requested permission at install time. If the user continues with the installation, App C can access the secured component.

If, however, the android:protectionLevel is signature, the user is not informed about the requested permission at install time, as the system can determine on its own whether or not the permission should be granted. In this case, App A and App C are signed with different signing keys, so Android silently ignores the permission request. If the user continues with installation, then App C tries to access App A’s secured component, App C crashes with a SecurityException.

In other words, this all works as expected.

The Application SDK Problem Case (C, Then A)

However, in many cases, there is nothing forcing the user to install App A before App C. This is particularly true for publicly-distributed apps on common markets, like the Play Store.

When the user installs App C, the user is not informed about the request for the custom permission, presumably because that permission has not yet been defined. If the user later installs App A, App C is not retroactively granted the permission, and so App C’s attempts to use the secured component fail.

This works as expected, though it puts a bit of a damper on custom permissions. One way to work around this would be for the user to uninstall App C, then install it again (with App A already installed). This returns us to the original scenario from the preceding section. However, if the user has data in App C, losing that data may be a problem (as in a “let’s give App C, or perhaps App A, one-star ratings on the Play Store” sort of problem).

The Peer Apps Case, Part One (A, Then B)

Suppose now we augment our SDK-consuming app (formerly App C) to declare the same permission that App A does, in an attempt to allow the two apps to be installed in either order. That is what App B is: the same app as App C, but where it has the same <permission> element as does App A in its manifest.

This scenario is particularly important where both apps could be of roughly equal importance to the user. In cases where App C is some sort of plugin for App A, it is not unreasonable for the author of App A to require App A to be installed first. But, if Twitter and Facebook wanted to access components of each others’ apps, it would be unreasonable for either of those firms to mandate that their app must be installed first. After all, if Twitter wants to be installed first, and Facebook wants to be installed first, one will be disappointed.

If the user installs App A (the app defending a component with the custom permission) before App B, the user will be notified at install time about App B’s request for this permission. Notably, the information shown on the installation security screen will contain App A’s description of the permission. And, if the user goes ahead and installs App B, App B can indeed access App A’s secured component, since it was granted permission by the user.

Once again, everything is working as expected. Going back to the two questions:

  1. The user is informed when App B or App C requests the permission defined by App A.
  2. App B and App C can hold that permission if and only if they meet the requirements of the protection level

The Peer Apps Case, Part Two (B, Then A)

What happens if we reverse the order of installation? After all, if App A and App B are peers, from the standpoint of the user, there is roughly a 50% chance that the user will install App B before App A.

Here is where things go off the rails.

The user is not informed about App B’s request for the custom permission.

The user will be informed about any platform permissions that the app requests via other <uses-permission> elements. If there are none, the user is told that App B requests no permissions… despite the fact that it does.

When the user installs App A, the same thing occurs. Of course, since App A does not have a <uses-permission> element, this is not all that surprising.

However, at this point, even though the user was not informed, App B holds the custom permission and can access the secured component.

This is bad enough when both parties are ethical. App B could be a piece of malware, though, designed to copy the data from App A, ideally without the user’s knowledge. And, if App B is installed before App A, that would happen.

So, going to the two questions:

  1. The user is not informed about App B’s request for the permission…
  2. …but App B gets it anyway and can access the secured component

The Downgraded-Level Malware Case (B, Then A, Again)

You might think that the preceding problem would only be for normal or dangerous protection levels. If App A defines a permission as requiring a matching signature, and App A marks a component as being defended by that permission, Android must require the signature match, right?

Wrong.

The behavior is identical to the preceding case. Android does not use the defender’s protection level. It uses the definer’s protection level, meaning the protection level of whoever was installed first and had the <permission> element.

So, if App A has the custom permission defined as signature, and App B has the custom permission defined as normal, if App B is installed first, the behavior is as shown in the preceding section:

  1. The user is not informed about App B’s request for the permission…
  2. …but App B gets it anyway and can access the secured component, despite the signatures not matching

The Peer Apps Case With a Side Order of C

What happens if we add App C back into the mix? Specifically, what if App B is installed first, then App A, then App C?

When App C eventually gets installed, the user is prompted for the custom permission that App C requests via <uses-permission>. However, the description that the user sees is from App B, the one that first defined the custom <permission>. Moreover, the protection level is whatever App B defined it to be. So if App B downgraded the protection level from App A’s intended signature to be normal, App C can hold that permission and access the secured App A component, even if it is signed by another signing key.

Not surprisingly, the same results occur if you install App B, then App C, then App A.

Behavior Analysis

The behavior exhibited in these scenarios is consistent with two presumed implementation “features” of Android’s permission system:

  1. First one in wins. In other words, the first app (or framework, in the case of the OS’s platform permissions) that defines a <permission> for a given android:name gets to determine what the description is and what the protection level is.
  2. The user is only prompted to confirm a permission if the app being installed has a <uses-permission> element, the permission was already defined by some other app, and the protection level is not signature.

Risk Assessment

The “first one in wins” rule is a blessing and a curse. It is a curse, insofar as it opens up the possibility for malware to hold a custom permission without the user’s awareness of that, and even to downgrade a signature-level permission to normal. However, it is a blessing, in that the malware would have to be installed first; if it is installed second, either its request to hold the permission will be seen by the user (normal or dangerous) or the request to hold the permission will be rejected (signature).

This makes it somewhat unlikely for a piece of malware to try to sneakily make off with data. Eventually, if enough users start to ask publicly why App B needs access to App A’s data (for cases where App A was installed first and the user knows about the permission request), somebody in authority may eventually realize that this is a malware attack. Of course, “eventually” may be a rather long time.

However, there are some situations where Android’s custom permission behavior presents risk even greater than that. If the attacker has a means of being sure that their app was installed first, they can hold any permission from any third-party app they want to that was known at install time.

For example:

Android 5.0’s “Fix”

Android 5.0 now prevents two apps from defining the same <permission> (“same” based on android:name) unless they are signed by the same signing key. First one in wins; the second app installation will fail.

On the plus side:

However, it does pose significant limitations on legitimate public uses of custom <permission> elements. Only the defender should have the <permission> element now. Some client of the defender’s app (C) should not have the <permission> element and should simply rely upon the fact that the defender should be installed first. If the client were to define the <permission>, then either the client or the defender cannot be installed, which is pointless.

This has usability issues:

And, of course, this fix is only for Android 5.0 and above.

Mitigation Using PermissionUtils

The “first one in wins” rule also leads us to a mitigation strategy: On first run of our app, see if any other app has defined permissions that we have defined. If that has happened, then we are at risk, and take appropriate steps. If, however, no other app has defined our custom permissions, then the Android permission system should work for us, and we can proceed as normal.

The CWAC-Security library provides some helper code, in the form of the PermissionUtils class, to detect other apps defining the same custom permissions that you define.

The idea is that you call checkCustomPermissions() — a static method on PermissionUtils — on the first run of your app. It will return details about what other apps have already defined custom permissions that your app defines. If checkCustomPermissions() returns nothing, you know that everything is fine, and you can move ahead. Otherwise, you can:

Custom Dangerous Permissions, and Android 6.0

Android 6.0 introduced the concept of runtime permissions, where dangerous permissions need to be requested at runtime in addition to being requested in the manifest. This is covered back in the introductory chapter on permissions.

However, what happens if you define a custom dangerous permission?

The good news is that it works. However, you will want to test it, in part to see what it looks like to users, so you can get the phrasing of your permission-related string resources correct.

The Permissions/CustomDangerous sample project contains two application modules:

The <permission> element is unremarkable, other than the protectionLevel being set to dangerous:

  <permission
    android:name="com.commonsware.android.perm.custdanger.SOMETHING"
    android:description="@string/perm_desc"
    android:label="@string/perm_label"
    android:protectionLevel="dangerous" />
(from Permissions/CustomDangerous/app/src/main/AndroidManifest.xml)

The label and description come from string resources:

  <string name="perm_label">Custom Dangerous Permission</string>
  <string name="perm_desc">This is a description. No, really.</string>
(from Permissions/CustomDangerous/app/src/main/res/values/strings.xml)

The client module uses the same AbstractPermissionActivity seen elsewhere in this book to request that com.commonsware.android.perm.custdanger.SOMETHING permission at runtime.

If you install the app application, then install and run the client application, what you see is the description, not the label, appear in the runtime permission dialog:

Custom Dangerous Permission, As Shown In Runtime Permission Dialog, on Android 6.0.1
Figure 724: Custom Dangerous Permission, As Shown In Runtime Permission Dialog, on Android 6.0.1

If you go into Settings > Apps > Custom Dangerous Client > Permissions, the custom dangerous permission does not show up immediately, due to a poorly-designed UI:

App Permissions in Settings, Showing Nothing Useful
Figure 725: App Permissions in Settings, Showing Nothing Useful

Instead, the user needs to tap on the “Additional permissions” row to have that be replaced by the custom dangerous permission:

App Permissions in Settings, Showing Custom Dangerous Permission
Figure 726: App Permissions in Settings, Showing Custom Dangerous Permission

Here, the label is what shows up, not the description.

Hence, you will want to tailor your phrasing of these string resources to make sense in their respective use cases.

Finding the Available Permissions

On the one hand, developers should try to stick to documented permissions.

On the other hand, documentation is sometimes lacking. This is particularly true for permissions other than those defined by the OS itself, ones that come from other apps that change more frequently, including the Play Services SDK and framework.

You might find that you need to determine what permissions have been defined on a given device. Perhaps that need is at runtime — if you request a permission that does not exist, you cannot actually get it, and that may lead to problems in the future. Perhaps that need is just during development itself, to inspect some device and determine what it does and does not have in terms of permissions.

PackageManager offers methods to allow you to examine the device’s permissions and permission groups. The Permissions/PermissionReporter sample app uses these methods to build up a tabbed UI listing the defined permissions, broken down by protection level.

getAllPermissionGroups() on PackageManager will return a list of PermissionGroupInfo objects. This method takes an int value; 0 generally will be fine for your use cases.

On its own, PermissionGroupInfo is not especially useful. However, you can turn around and call queryPermissionsByGroup() on PackageManager, passing in the name from the PermissionGroupInfo, to get all of the permissions in that group. This method also takes an int value as the second parameter, where once again 0 will be fine.

queryPermissionsByGroup() returns a List of PermissionInfo objects. PermissionInfo has a few interesting values:

Note that to get the actual text of the description, there is a loadDescription() method on PermissionInfo that will do all the work to find the actual string for the description, based upon the app that defined the permission and the current locale.

To get the details of all the permissions defined on a device, we will have to call queryPermissionsByGroup() for each permission group. Each of those calls will involve IPC, and so this might be slow enough to warrant its own thread.

With that in mind, MainActivity in the PermissionReporter sample app has a PermissionSource that collects information about the permissions on the system. That information is aggregated in a PermissionRoster, which the data emitted by this Observable:

  private static class PermissionSource implements ObservableOnSubscribe<PermissionRoster> {
    private Context ctxt;

    private PermissionSource(Context ctxt) {
      this.ctxt=ctxt.getApplicationContext();
    }

    @Override
    public void subscribe(ObservableEmitter<PermissionRoster> emitter)
      throws Exception {
      PackageManager pm=ctxt.getPackageManager();
      final PermissionRoster result=new PermissionRoster();

      addPermissionsFromGroup(pm, null, result);

      for (PermissionGroupInfo group :
        pm.getAllPermissionGroups(0)) {
        addPermissionsFromGroup(pm, group.name, result);
      }

      emitter.onNext(result);
      emitter.onComplete();
    }

    private void addPermissionsFromGroup(PackageManager pm,
                                         String groupName,
                                         PermissionRoster result)
      throws PackageManager.NameNotFoundException {
      for (PermissionInfo info :
        pm.queryPermissionsByGroup(groupName, 0)) {
        int coreBits=
          info.protectionLevel &
            PermissionInfo.PROTECTION_MASK_BASE;

        switch (coreBits) {
          case PermissionInfo.PROTECTION_NORMAL:
            result.add(PermissionType.NORMAL, info);
            break;

          case PermissionInfo.PROTECTION_DANGEROUS:
            result.add(PermissionType.DANGEROUS, info);
            break;

          case PermissionInfo.PROTECTION_SIGNATURE:
            result.add(PermissionType.SIGNATURE, info);
            break;

          default:
            result.add(PermissionType.OTHER, info);
            break;
        }
      }
    }
  }
(from Permissions/PermissionReporter/app/src/main/java/com/commonsware/android/permreporter/MainActivity.java)

The subscribe() method loops over the permission groups:

      for (PermissionGroupInfo group :
        pm.getAllPermissionGroups(0)) {
        addPermissionsFromGroup(pm, group.name, result);
      }
(from Permissions/PermissionReporter/app/src/main/java/com/commonsware/android/permreporter/MainActivity.java)

As it turns out, not all permissions are part of a group. To find out the details of these un-grouped permissions, you need to call queryPermissionsByGroup() with a null permission group name.

For each permission group (plus the magic null group), we call a private addPermissionsFromGroup() method (shown above) to collect the details of the permissions in that group.

The protectionLevel field on a PermissionInfo contains a number of different sorts of flags. The PROTECTION_MASK_BASE is a bitmask that restricts the bits we are looking at to the ones for basic protections. We then divide the permissions into four groups based on protection level:

Those PermissionInfo objects are then poured into the PermissionRoster object:

package com.commonsware.android.permreporter;

import android.content.pm.PermissionInfo;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
import java.util.HashMap;

class PermissionRoster {
  private HashMap<PermissionType, ArrayList<PermissionInfo>> roster=
      new HashMap<PermissionType, ArrayList<PermissionInfo>>();

  void add(PermissionType type, PermissionInfo info) {
    ArrayList<PermissionInfo> list=roster.get(type);

    if (list==null) {
      list=new ArrayList<>();
      roster.put(type, list);
    }

    list.add(info);
  }

  ArrayList<PermissionInfo> getListForType(PermissionType type) {
    return(roster.get(type));
  }
}
(from Permissions/PermissionReporter/app/src/main/java/com/commonsware/android/permreporter/PermissionRoster.java)

PermissionType is an enum defined by this project for the four groups and includes some Java shenanigans for being able to convert back and forth between integer values and the enum values:

package com.commonsware.android.permreporter;

import android.util.SparseArray;

enum PermissionType {
  NORMAL(0),
  DANGEROUS(1),
  SIGNATURE(2),
  OTHER(3);

  private static final SparseArray<PermissionType> BY_VALUE=
      new SparseArray<PermissionType>(4);

  static {
    for (PermissionType type : PermissionType.values()) {
      BY_VALUE.put(type.value, type);
    }
  }

  private final int value;

  PermissionType(int value) {
    this.value=value;
  }

  static PermissionType forValue(int value) {
    return(BY_VALUE.get(value));
  }
}
(from Permissions/PermissionReporter/app/src/main/java/com/commonsware/android/permreporter/PermissionType.java)