diff options
| -rw-r--r-- | services/core/java/com/android/server/policy/PermissionPolicyService.java | 180 |
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 |