summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Svet Ganov <svetoslavganov@google.com> 2019-04-09 23:55:05 -0700
committer Svetoslav Ganov <svetoslavganov@google.com> 2019-04-10 15:38:11 +0000
commit38a0631dda87a6519198d2ae38139dcafe1c660d (patch)
tree776455c9de365323de2f106221de59159b9d8925
parentacbdf910366f9cd2e890dd4913e465ffcca06e36 (diff)
Handle restricted permissions for shared UID components - framework
We set the app op for a restricted permission to allow if the app requesting the permission is whitelisted and to deny if the app requesting the permission is not whitelisted. However, there is another case where an app in a shared user can access a component in another app in the same shared user due to being in the same shared user and not by having the permission that guards the component form the rest of the world. We need to handle this. The way we do this is by setting app ops corresponding to non requested restricted permissions to allow as this would allow the shared uid access case and be okay for other apps as they would not have the permission and would fail on the permission checks before reaching the app op check. Test: atest CtsAppSecurityHostTestCases:android.appsecurity.cts.PermissionsHostTest Test: atest CtsPermissionTestCases Test: atest CtsPermission2TestCases Test: atest CtsRoleTestCases Test: shell query commands now work bug:130045570 Change-Id: I654f057e909340fafd708b2bca182eb105d0cd30
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java180
1 files changed, 130 insertions, 50 deletions
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 3011808d281c..67f30dc2e9fc 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -22,25 +22,28 @@ import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.content.Context;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManagerInternal.PackageListObserver;
import android.content.pm.PackageParser;
import android.content.pm.PermissionInfo;
+import android.os.Process;
import android.os.UserHandle;
-import android.os.UserManagerInternal;
import android.permission.PermissionControllerManager;
import android.permission.PermissionManagerInternal;
+import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
-import com.android.internal.util.function.QuadConsumer;
-import com.android.internal.util.function.TriConsumer;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
@@ -51,11 +54,17 @@ import java.util.concurrent.CountDownLatch;
* and app ops - and vise versa.
*/
public final class PermissionPolicyService extends SystemService {
+ private static final String PLATFORM_PACKAGE = "android";
private static final String LOG_TAG = PermissionPolicyService.class.getSimpleName();
+ // No need to lock as this is populated on boot when the OS is
+ // single threaded and is never mutated until a reboot.
+ private static final ArraySet<String> sAllRestrictedPermissions = new ArraySet<>();
+
public PermissionPolicyService(@NonNull Context context) {
super(context);
+ cacheAllRestrictedPermissions(context);
}
@Override
@@ -89,6 +98,20 @@ public final class PermissionPolicyService extends SystemService {
startWatchingRuntimePermissionChanges(getContext(), userId);
}
+ private static void cacheAllRestrictedPermissions(@NonNull Context context) {
+ try {
+ final PackageInfo packageInfo = context.getPackageManager()
+ .getPackageInfo(PLATFORM_PACKAGE, PackageManager.GET_PERMISSIONS);
+ for (PermissionInfo permissionInfo : packageInfo.permissions) {
+ if (permissionInfo.isRestricted()) {
+ sAllRestrictedPermissions.add(permissionInfo.name);
+ }
+ }
+ } catch (NameNotFoundException impossible) {
+ /* cannot happen */
+ }
+ }
+
private static void grantOrUpgradeDefaultRuntimePermissionsInNeeded(@NonNull Context context,
@UserIdInt int userId) {
final PackageManagerInternal packageManagerInternal = LocalServices.getService(
@@ -123,16 +146,6 @@ public final class PermissionPolicyService extends SystemService {
}
}
- private static void onRestrictedPermissionEnabledChange(@NonNull Context context) {
- final PermissionManagerInternal permissionManagerInternal = LocalServices
- .getService(PermissionManagerInternal.class);
- final UserManagerInternal userManagerInternal = LocalServices.getService(
- UserManagerInternal.class);
- for (int userId : userManagerInternal.getUserIds()) {
- synchronizePermissionsAndAppOpsForUser(context, userId);
- }
- }
-
private static void startWatchingRuntimePermissionChanges(@NonNull Context context,
int userId) {
final PermissionManagerInternal permissionManagerInternal = LocalServices.getService(
@@ -149,40 +162,66 @@ public final class PermissionPolicyService extends SystemService {
@NonNull String packageName, @UserIdInt int userId) {
final PackageManagerInternal packageManagerInternal = LocalServices.getService(
PackageManagerInternal.class);
- final PackageParser.Package pkg = packageManagerInternal
- .getPackage(packageName);
- if (pkg != null) {
- PermissionToOpSynchronizer.syncPackage(context, pkg, userId);
+ final PackageParser.Package pkg = packageManagerInternal.getPackage(packageName);
+ if (pkg == null) {
+ return;
+ }
+ final PermissionToOpSynchroniser synchroniser = new PermissionToOpSynchroniser(context);
+ synchroniser.addPackage(context, pkg, userId);
+ final String[] sharedPkgNames = packageManagerInternal.getPackagesForSharedUserId(
+ pkg.mSharedUserId, userId);
+ if (sharedPkgNames != null) {
+ for (String sharedPkgName : sharedPkgNames) {
+ final PackageParser.Package sharedPkg = packageManagerInternal
+ .getPackage(sharedPkgName);
+ if (sharedPkg != null) {
+ synchroniser.addPackage(context, sharedPkg, userId);
+ }
+ }
}
+ synchroniser.syncPackages();
}
private static void synchronizePermissionsAndAppOpsForUser(@NonNull Context context,
@UserIdInt int userId) {
final PackageManagerInternal packageManagerInternal = LocalServices.getService(
PackageManagerInternal.class);
- final PermissionToOpSynchronizer synchronizer = new PermissionToOpSynchronizer(context);
+ final PermissionToOpSynchroniser synchronizer = new PermissionToOpSynchroniser(context);
packageManagerInternal.forEachPackage((pkg) ->
synchronizer.addPackage(context, pkg, userId));
synchronizer.syncPackages();
}
- private static class PermissionToOpSynchronizer {
+ /**
+ * Synchronizes permission to app ops. You *must* always sync all packages
+ * in a shared UID at the same time to ensure proper synchronization.
+ */
+ private static class PermissionToOpSynchroniser {
private final @NonNull Context mContext;
+ private final @NonNull SparseIntArray mUids = new SparseIntArray();
private final @NonNull SparseArray<String> mPackageNames = new SparseArray<>();
private final @NonNull SparseIntArray mAllowedUidOps = new SparseIntArray();
private final @NonNull SparseIntArray mDefaultUidOps = new SparseIntArray();
- PermissionToOpSynchronizer(@NonNull Context context) {
+ PermissionToOpSynchroniser(@NonNull Context context) {
mContext = context;
}
- private void addPackage(@NonNull Context context,
- @NonNull PackageParser.Package pkg, @UserIdInt int userId) {
- addPackage(context, pkg, userId, this::addAllowedEntry, this::addIgnoredEntry);
- }
-
void syncPackages() {
+ // TRICKY: we set the app op for a restricted permission to allow if the app
+ // requesting the permission is whitelisted and to deny if the app requesting
+ // the permission is not whitelisted. However, there is another case where an
+ // app in a shared user can access a component in another app in the same shared
+ // user due to being in the same shared user and not by having the permission
+ // that guards the component form the rest of the world. We need to handle this.
+ // The way we do this is by setting app ops corresponding to non requested
+ // restricted permissions to allow as this would allow the shared uid access
+ // case and be okay for other apps as they would not have the permission and
+ // would fail on the permission checks before reaching the app op check.
+ final SparseArray<List<String>> unrequestedRestrictedPermissionsForUid =
+ new SparseArray<>();
+
final AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class);
final int allowedCount = mAllowedUidOps.size();
for (int i = 0; i < allowedCount; i++) {
@@ -190,31 +229,84 @@ public final class PermissionPolicyService extends SystemService {
final int uid = mAllowedUidOps.valueAt(i);
final String packageName = mPackageNames.valueAt(i);
setUidModeAllowed(appOpsManager, opCode, uid, packageName);
+
+ // Keep track this permission was requested by the UID.
+ List<String> unrequestedRestrictedPermissions =
+ unrequestedRestrictedPermissionsForUid.get(uid);
+ if (unrequestedRestrictedPermissions == null) {
+ unrequestedRestrictedPermissions = new ArrayList<>(sAllRestrictedPermissions);
+ unrequestedRestrictedPermissionsForUid.put(uid,
+ unrequestedRestrictedPermissions);
+ }
+ unrequestedRestrictedPermissions.remove(AppOpsManager.opToPermission(opCode));
+
+ mUids.delete(uid);
}
final int defaultCount = mDefaultUidOps.size();
for (int i = 0; i < defaultCount; i++) {
final int opCode = mDefaultUidOps.keyAt(i);
final int uid = mDefaultUidOps.valueAt(i);
setUidModeDefault(appOpsManager, opCode, uid);
+
+ // Keep track this permission was requested by the UID.
+ List<String> unrequestedRestrictedPermissions =
+ unrequestedRestrictedPermissionsForUid.get(uid);
+ if (unrequestedRestrictedPermissions == null) {
+ unrequestedRestrictedPermissions = new ArrayList<>(sAllRestrictedPermissions);
+ unrequestedRestrictedPermissionsForUid.put(uid,
+ unrequestedRestrictedPermissions);
+ }
+ unrequestedRestrictedPermissions.remove(AppOpsManager.opToPermission(opCode));
+
+ mUids.delete(uid);
}
- }
- static void syncPackage(@NonNull Context context, @NonNull PackageParser.Package pkg,
- @UserIdInt int userId) {
- addPackage(context, pkg, userId, PermissionToOpSynchronizer::setUidModeAllowed,
- PermissionToOpSynchronizer::setUidModeDefault);
+ // Give root access
+ mUids.put(Process.ROOT_UID, Process.ROOT_UID);
+
+ // Add records for UIDs that don't use any restricted permissions.
+ final int uidCount = mUids.size();
+ for (int i = 0; i < uidCount; i++) {
+ final int uid = mUids.keyAt(i);
+ unrequestedRestrictedPermissionsForUid.put(uid,
+ new ArrayList<>(sAllRestrictedPermissions));
+ }
+
+ // Flip ops for all unrequested restricted permission for the UIDs.
+ final int unrequestedUidCount = unrequestedRestrictedPermissionsForUid.size();
+ for (int i = 0; i < unrequestedUidCount; i++) {
+ final List<String> unrequestedRestrictedPermissions =
+ unrequestedRestrictedPermissionsForUid.valueAt(i);
+ if (unrequestedRestrictedPermissions != null) {
+ final int uid = unrequestedRestrictedPermissionsForUid.keyAt(i);
+ final String[] packageNames = (uid != Process.ROOT_UID)
+ ? mContext.getPackageManager().getPackagesForUid(uid)
+ : new String[] {"root"};
+ if (packageNames == null) {
+ continue;
+ }
+ final int permissionCount = unrequestedRestrictedPermissions.size();
+ for (int j = 0; j < permissionCount; j++) {
+ final String permission = unrequestedRestrictedPermissions.get(j);
+ for (String packageName : packageNames) {
+ setUidModeAllowed(appOpsManager,
+ AppOpsManager.permissionToOpCode(permission), uid,
+ packageName);
+ }
+ }
+ }
+ }
}
- private static void addPackage(@NonNull Context context,
- @NonNull PackageParser.Package pkg, @UserIdInt int userId,
- @NonNull QuadConsumer<AppOpsManager, Integer, Integer, String> allowedConsumer,
- @NonNull TriConsumer<AppOpsManager, Integer, Integer> defaultConsumer) {
+ private void addPackage(@NonNull Context context,
+ @NonNull PackageParser.Package pkg, @UserIdInt int userId) {
final PackageManager packageManager = context.getPackageManager();
- final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
final int uid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.applicationInfo.uid));
final UserHandle userHandle = UserHandle.of(userId);
+ mUids.put(uid, uid);
+
final int permissionCount = pkg.requestedPermissions.size();
for (int i = 0; i < permissionCount; i++) {
final String permission = pkg.requestedPermissions.get(i);
@@ -241,9 +333,10 @@ public final class PermissionPolicyService extends SystemService {
if (permissionInfo.isHardRestricted()) {
if (applyRestriction) {
- defaultConsumer.accept(appOpsManager, opCode, uid);
+ mDefaultUidOps.put(opCode, uid);
} else {
- allowedConsumer.accept(appOpsManager, opCode, uid, pkg.packageName);
+ mPackageNames.put(opCode, pkg.packageName);
+ mAllowedUidOps.put(opCode, uid);
}
} else if (permissionInfo.isSoftRestricted()) {
//TODO: Implement soft restrictions like storage here.
@@ -251,19 +344,6 @@ public final class PermissionPolicyService extends SystemService {
}
}
- @SuppressWarnings("unused")
- private void addAllowedEntry(@NonNull AppOpsManager appOpsManager, int opCode,
- int uid, @NonNull String packageName) {
- mPackageNames.put(opCode, packageName);
- mAllowedUidOps.put(opCode, uid);
- }
-
- @SuppressWarnings("unused")
- private void addIgnoredEntry(@NonNull AppOpsManager appOpsManager,
- int opCode, int uid) {
- mDefaultUidOps.put(opCode, uid);
- }
-
private static void setUidModeAllowed(@NonNull AppOpsManager appOpsManager,
int opCode, int uid, @NonNull String packageName) {
final int currentMode = appOpsManager.unsafeCheckOpRaw(AppOpsManager