From b645679d1d40d05944527eee4b5fe5cae0c3fc18 Mon Sep 17 00:00:00 2001 From: Nate Myren Date: Thu, 9 Jan 2025 10:09:56 -0800 Subject: Do not allow non-system apps to provide unverified attributions Some apps (the shell, system server, etc) are exempt from the requirement that attribution tags be registered. However, in the proxy case, the tag provied by the proxy app is trusted if the proxied app is one of these exemptions. We should only trust these tags if the proxy app is a system app. This CL also adds a second restriction check when a restriction is removed, to verify that an op is free of all restrictions, before resuming a started op Bug: 375623125 Test: upcoming Flag: EXEMPT: See bug Merged-In: I6a7b0a24359097c0ea2f52cc69637d929a931b4f Change-Id: I6a7b0a24359097c0ea2f52cc69637d929a931b4f --- .../com/android/server/appop/AppOpsService.java | 88 ++++++++++++++++------ 1 file changed, 63 insertions(+), 25 deletions(-) diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index b891aeeb527d..2e48b24eb25c 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -699,7 +699,7 @@ public class AppOpsService extends IAppOpsService.Stub { } } - /** Returned from {@link #verifyAndGetBypass(int, String, String, String, boolean)}. */ + /** Returned from {@link #verifyAndGetBypass(int, String, String, int, String, boolean)}. */ private static final class PackageVerificationResult { final RestrictionBypass bypass; @@ -3336,7 +3336,7 @@ public class AppOpsService extends IAppOpsService.Stub { verifyAndGetBypass(uid, packageName, null); // When the caller is the system, it's possible that the packageName is the special // one (e.g., "root") which isn't actually existed. - if (resolveUid(packageName) == uid + if (resolveNonAppUid(packageName) == uid || (isPackageExisted(packageName) && !filterAppAccessUnlocked(packageName))) { return AppOpsManager.MODE_ALLOWED; } @@ -3460,7 +3460,7 @@ public class AppOpsService extends IAppOpsService.Stub { boolean shouldCollectMessage) { PackageVerificationResult pvr; try { - pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName); + pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyUid, proxyPackageName); boolean wasNull = attributionTag == null; if (!pvr.isAttributionTagValid) { attributionTag = null; @@ -3979,7 +3979,7 @@ public class AppOpsService extends IAppOpsService.Stub { int attributionChainId, boolean dryRun) { PackageVerificationResult pvr; try { - pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName); + pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyUid, proxyPackageName); if (!pvr.isAttributionTagValid) { attributionTag = null; } @@ -4442,7 +4442,11 @@ public class AppOpsService extends IAppOpsService.Stub { private boolean isCallerAndAttributionTrusted(@NonNull AttributionSource attributionSource) { if (attributionSource.getUid() != Binder.getCallingUid() && attributionSource.isTrusted(mContext)) { - return true; + // if there is a next attribution source, it must be trusted, as well. + if (attributionSource.getNext() == null + || attributionSource.getNext().isTrusted(mContext)) { + return true; + } } return mContext.checkPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS, Binder.getCallingPid(), Binder.getCallingUid(), null) @@ -4580,11 +4584,20 @@ public class AppOpsService extends IAppOpsService.Stub { } /** - * @see #verifyAndGetBypass(int, String, String, String) + * @see #verifyAndGetBypass(int, String, String, int, String, boolean) */ private @NonNull PackageVerificationResult verifyAndGetBypass(int uid, String packageName, @Nullable String attributionTag) { - return verifyAndGetBypass(uid, packageName, attributionTag, null); + return verifyAndGetBypass(uid, packageName, attributionTag, Process.INVALID_UID, null); + } + + /** + * @see #verifyAndGetBypass(int, String, String, int, String, boolean) + */ + private @NonNull PackageVerificationResult verifyAndGetBypass(int uid, String packageName, + @Nullable String attributionTag, int proxyUid, @Nullable String proxyPackageName) { + return verifyAndGetBypass(uid, packageName, attributionTag, proxyUid, proxyPackageName, + false); } /** @@ -4595,13 +4608,16 @@ public class AppOpsService extends IAppOpsService.Stub { * @param uid The uid the package belongs to * @param packageName The package the might belong to the uid * @param attributionTag attribution tag or {@code null} if no need to verify - * @param proxyPackageName The proxy package, from which the attribution tag is to be pulled + * @param proxyUid The proxy uid, from which the attribution tag is to be pulled + * @param proxyPackageName The proxy package, from which the attribution tag may be pulled + * @param suppressErrorLogs Whether to print to logcat about nonmatching parameters * * @return PackageVerificationResult containing {@link RestrictionBypass} and whether the * attribution tag is valid */ private @NonNull PackageVerificationResult verifyAndGetBypass(int uid, String packageName, - @Nullable String attributionTag, @Nullable String proxyPackageName) { + @Nullable String attributionTag, int proxyUid, @Nullable String proxyPackageName, + boolean suppressErrorLogs) { if (uid == Process.ROOT_UID) { // For backwards compatibility, don't check package name for root UID. return new PackageVerificationResult(null, @@ -4645,31 +4661,47 @@ public class AppOpsService extends IAppOpsService.Stub { int callingUid = Binder.getCallingUid(); - // Allow any attribution tag for resolvable uids - int pkgUid; + // Allow any attribution tag for resolvable, non-app uids + int nonAppUid; if (Objects.equals(packageName, "com.android.shell")) { // Special case for the shell which is a package but should be able // to bypass app attribution tag restrictions. - pkgUid = Process.SHELL_UID; + nonAppUid = Process.SHELL_UID; } else { - pkgUid = resolveUid(packageName); - } - if (pkgUid != Process.INVALID_UID) { - if (pkgUid != UserHandle.getAppId(uid)) { - Slog.e(TAG, "Bad call made by uid " + callingUid + ". " - + "Package \"" + packageName + "\" does not belong to uid " + uid + "."); - String otherUidMessage = DEBUG ? " but it is really " + pkgUid : " but it is not"; - throw new SecurityException("Specified package \"" + packageName + "\" under uid " - + UserHandle.getAppId(uid) + otherUidMessage); + nonAppUid = resolveNonAppUid(packageName); + } + if (nonAppUid != Process.INVALID_UID) { + if (nonAppUid != UserHandle.getAppId(uid)) { + if (!suppressErrorLogs) { + Slog.e(TAG, "Bad call made by uid " + callingUid + ". " + + "Package \"" + packageName + "\" does not belong to uid " + uid + + "."); + } + String otherUidMessage = + DEBUG ? " but it is really " + nonAppUid : " but it is not"; + throw new SecurityException("Specified package \"" + packageName + + "\" under uid " + UserHandle.getAppId(uid) + otherUidMessage); + } + // We only allow bypassing the attribution tag verification if the proxy is a + // system app (or is null), in order to prevent abusive apps clogging the appops + // system with unlimited attribution tags via proxy calls. + boolean proxyIsSystemAppOrNull = true; + if (proxyPackageName != null) { + int proxyAppId = UserHandle.getAppId(proxyUid); + if (proxyAppId >= Process.FIRST_APPLICATION_UID) { + proxyIsSystemAppOrNull = + mPackageManagerInternal.isSystemPackage(proxyPackageName); + } } return new PackageVerificationResult(RestrictionBypass.UNRESTRICTED, - /* isAttributionTagValid */ true); + /* isAttributionTagValid */ proxyIsSystemAppOrNull); } int userId = UserHandle.getUserId(uid); RestrictionBypass bypass = null; boolean isAttributionTagValid = false; + int pkgUid = nonAppUid; final long ident = Binder.clearCallingIdentity(); try { PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class); @@ -5536,7 +5568,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (nonpackageUid != -1) { packageName = null; } else { - packageUid = resolveUid(packageName); + packageUid = resolveNonAppUid(packageName); if (packageUid < 0) { packageUid = AppGlobals.getPackageManager().getPackageUid(packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); @@ -6720,7 +6752,13 @@ public class AppOpsService extends IAppOpsService.Stub { if (restricted && attrOp.isRunning()) { attrOp.pause(); } else if (attrOp.isPaused()) { - attrOp.resume(); + RestrictionBypass bypass = verifyAndGetBypass(uid, ops.packageName, attrOp.tag) + .bypass; + if (!isOpRestrictedLocked(uid, code, ops.packageName, attrOp.tag, + bypass, false)) { + // Only resume if there are no other restrictions remaining on this op + attrOp.resume(); + } } } } @@ -7165,7 +7203,7 @@ public class AppOpsService extends IAppOpsService.Stub { } } - private static int resolveUid(String packageName) { + private static int resolveNonAppUid(String packageName) { if (packageName == null) { return -1; } -- cgit v1.2.3-59-g8ed1b