Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a4523e22 authored by Nate Myren's avatar Nate Myren Committed by Android Build Coastguard Worker
Browse files

Prohibit untrusted proxys from specifying proxied attribution tags

Only trusted proxies should be allowed to specify tags. Also prevents
startOperationDryRun from editing the know attribution tags, as the dry
run should change no state.

Bug: 445917646
Test: atest AttributionTest
Flag: EXEMPT CVE_FIX
(cherry picked from commit 110db0acb84cbd21f9da9391ab242d141ebf390c)
Cherrypick-From: https://googleplex-android-review.googlesource.com/q/commit:94d32dce4639ba16b321667719678d2a6b42d9c3
Merged-In: I14ab1389384fd28009edd9cceceaacdb97fb96e5
Change-Id: I14ab1389384fd28009edd9cceceaacdb97fb96e5
parent b51a58ec
Loading
Loading
Loading
Loading
+91 −21
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import static android.app.AppOpsManager.OP_CAMERA_SANDBOXED;
import static android.app.AppOpsManager.OP_FLAGS_ALL;
import static android.app.AppOpsManager.OP_FLAG_SELF;
import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
import static android.app.AppOpsManager.OP_FLAG_UNTRUSTED_PROXIED;
import static android.app.AppOpsManager.OP_NONE;
import static android.app.AppOpsManager.OP_PLAY_AUDIO;
import static android.app.AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO;
@@ -3189,7 +3190,7 @@ public class AppOpsService extends IAppOpsService.Stub {
    public int checkPackage(int uid, String packageName) {
        Objects.requireNonNull(packageName);
        try {
            verifyAndGetBypass(uid, packageName, null, Process.INVALID_UID, null, true);
            verifyAndGetBypass(uid, packageName, null, Process.INVALID_UID, null, true, true);
            // 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 (resolveNonAppUid(packageName) == uid
@@ -3407,8 +3408,10 @@ public class AppOpsService extends IAppOpsService.Stub {
            @OpFlags int flags, boolean shouldCollectAsyncNotedOp, @Nullable String message,
            boolean shouldCollectMessage, int notedCount) {
        PackageVerificationResult pvr;
        boolean proxyTrusted = (flags & OP_FLAG_UNTRUSTED_PROXIED) == 0;
        try {
            pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyUid, proxyPackageName);
            pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyUid, proxyPackageName,
                    proxyTrusted);
            if (!pvr.isAttributionTagValid) {
                attributionTag = null;
            }
@@ -4072,8 +4075,10 @@ public class AppOpsService extends IAppOpsService.Stub {
            boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
            int attributionChainId) {
        PackageVerificationResult pvr;
        boolean proxyTrusted = (flags & OP_FLAG_UNTRUSTED_PROXIED) == 0;
        try {
            pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyUid, proxyPackageName);
            pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyUid, proxyPackageName,
                    proxyTrusted);
            if (!pvr.isAttributionTagValid) {
                attributionTag = null;
            }
@@ -4206,8 +4211,10 @@ public class AppOpsService extends IAppOpsService.Stub {
            int proxyUid, String proxyPackageName, @OpFlags int flags,
            boolean startIfModeDefault) {
        PackageVerificationResult pvr;
        boolean proxyTrusted = (flags & OP_FLAG_UNTRUSTED_PROXIED) == 0;
        try {
            pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyUid, proxyPackageName);
            pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyUid, proxyPackageName,
                    proxyTrusted);
            if (!pvr.isAttributionTagValid) {
                attributionTag = null;
            }
@@ -4223,7 +4230,9 @@ public class AppOpsService extends IAppOpsService.Stub {

        boolean isRestricted = false;
        synchronized (this) {
            final Ops ops = getOpsLocked(uid, packageName, attributionTag,
            // Edit is true (so we create the Ops object if needed), but attribution tag is given as
            // null, so we don't cache any information about it.
            final Ops ops = getOpsLocked(uid, packageName, null,
                    pvr.isAttributionTagValid, pvr.bypass, /* edit */ true);
            if (ops == null) {
                if (DEBUG) {
@@ -4402,8 +4411,11 @@ public class AppOpsService extends IAppOpsService.Stub {
            int virtualDeviceId) {
        PackageVerificationResult pvr;
        try {
            // assume the proxy is trusted, since we aren't sure. We'll search for the attribution
            // tag with trusted flags, and if we don't find it, search for a null tag with
            // untrusted flags
            pvr = verifyAndGetBypass(proxiedUid, proxiedPackageName, attributionTag,
                    proxyUid, proxyPackageName);
                    proxyUid, proxyPackageName, /* isProxyTrusted */ true);
            if (!pvr.isAttributionTagValid) {
                attributionTag = null;
            }
@@ -4413,8 +4425,9 @@ public class AppOpsService extends IAppOpsService.Stub {
        }

        synchronized (this) {
            boolean hasProxy = proxyUid != Process.INVALID_UID;
            Op op = getOpLocked(code, proxiedUid, proxiedPackageName, attributionTag,
                    pvr.isAttributionTagValid, pvr.bypass, /* edit */ true);
                    pvr.isAttributionTagValid, pvr.bypass, /* edit */ false);
            if (op == null) {
                Slog.e(TAG, "Operation not found: uid=" + proxiedUid + " pkg=" + proxiedPackageName
                        + "("
@@ -4422,9 +4435,8 @@ public class AppOpsService extends IAppOpsService.Stub {
                return;
            }
            final AttributedOp attributedOp =
                    op.mDeviceAttributedOps.getOrDefault(
                            getPersistentDeviceIdForOp(virtualDeviceId, code),
                            new ArrayMap<>()).get(attributionTag);
                    getAttributedOpWithClientId(op, clientId, attributionTag, virtualDeviceId,
                            hasProxy);
            if (attributedOp == null) {
                Slog.e(TAG, "Attribution not found: uid=" + proxiedUid
                        + " pkg=" + proxiedPackageName + "("
@@ -4442,6 +4454,54 @@ public class AppOpsService extends IAppOpsService.Stub {
        }
    }

    private AttributedOp getAttributedOpWithClientId(Op op, IBinder clientId,
            String attributionTag, int virtualDeviceId, boolean hasProxy) {
        AttributedOp attributedOp =
                op.mDeviceAttributedOps.getOrDefault(
                        getPersistentDeviceIdForOp(virtualDeviceId, op.op),
                        new ArrayMap<>()).get(attributionTag);
        if (!hasProxy) {
            return attributedOp;
        }
        boolean hasTrustedInProgressEvent = attributedOp != null
                && attributedOp.hasInProgressEvent((event -> event.getClientId() == clientId
                        && (event.getFlags() & OP_FLAG_UNTRUSTED_PROXIED) == 0));
        if (hasTrustedInProgressEvent) {
            return attributedOp;
        }

        // We failed to find a trusted in progress event that matches the clientId. Check if the
        // tag is valid in the package, and look for an untrusted access matching that tag, if so
        boolean tagValid = false;
        try {
            tagValid = verifyAndGetBypass(op.uid, op.packageName, attributionTag)
                    .isAttributionTagValid;
        } catch (SecurityException e) {
            // assume tag is invalid
        }
        if (tagValid) {
            boolean hasUntrustedInProgressEvent = attributedOp != null
                    && attributedOp.hasInProgressEvent((event -> event.getClientId() == clientId
                    && (event.getFlags() & OP_FLAG_UNTRUSTED_PROXIED) != 0));
            if (hasUntrustedInProgressEvent) {
                return attributedOp;
            }
        }

        // The tag was not valid, or we failed to find an untrusted event. Look for an untrusted
        // event with the null attribution tag
        attributedOp = op.mDeviceAttributedOps.getOrDefault(
                        getPersistentDeviceIdForOp(virtualDeviceId, op.op),
                        new ArrayMap<>()).get(null);
        boolean hasUntrustedNullEvent = attributedOp != null
                && attributedOp.hasInProgressEvent((event -> event.getClientId() == clientId
                        && (event.getFlags() & OP_FLAG_UNTRUSTED_PROXIED) != 0));
        if (hasUntrustedNullEvent) {
            return attributedOp;
        }
        return null;
    }

    void scheduleOpActiveChangedIfNeededLocked(int code, int uid, @NonNull
            String packageName, @Nullable String attributionTag, int virtualDeviceId,
            boolean active, @AttributionFlags int attributionFlags, int attributionChainId) {
@@ -4881,20 +4941,22 @@ public class AppOpsService extends IAppOpsService.Stub {
    }

    /**
     * @see #verifyAndGetBypass(int, String, String, int, String, boolean)
     * @see #verifyAndGetBypass(int, String, String, int, String, boolean, boolean)
     */
    private @NonNull PackageVerificationResult verifyAndGetBypass(int uid, String packageName,
            @Nullable String attributionTag) {
        return verifyAndGetBypass(uid, packageName, attributionTag, Process.INVALID_UID, null);
        return verifyAndGetBypass(uid, packageName, attributionTag, Process.INVALID_UID, null,
                true);
    }

    /**
     * @see #verifyAndGetBypass(int, String, String, int, String, boolean)
     * @see #verifyAndGetBypass(int, String, String, int, String, boolean, boolean)
     */
    private @NonNull PackageVerificationResult verifyAndGetBypass(int uid, String packageName,
            @Nullable String attributionTag, int proxyUid, @Nullable String proxyPackageName) {
            @Nullable String attributionTag, int proxyUid, @Nullable String proxyPackageName,
            boolean isProxyTrusted) {
        return verifyAndGetBypass(uid, packageName, attributionTag, proxyUid, proxyPackageName,
                false);
                isProxyTrusted, false);
    }

    /**
@@ -4907,6 +4969,8 @@ public class AppOpsService extends IAppOpsService.Stub {
     * @param attributionTag attribution tag or {@code null} if no need to verify
     * @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 isProxyTrusted Whether or not the proxy package is trusted. If it isn't, then the
     *                       proxy attribution tag will not be used
     * @param suppressErrorLogs Whether to print to logcat about nonmatching parameters
     *
     * @return PackageVerificationResult containing {@link RestrictionBypass} and whether the
@@ -4914,7 +4978,7 @@ public class AppOpsService extends IAppOpsService.Stub {
     */
    private @NonNull PackageVerificationResult verifyAndGetBypass(int uid, String packageName,
            @Nullable String attributionTag, int proxyUid, @Nullable String proxyPackageName,
            boolean suppressErrorLogs) {
            boolean isProxyTrusted, boolean suppressErrorLogs) {
        if (uid == Process.ROOT_UID) {
            // For backwards compatibility, don't check package name for root UID, unless someone
            // is claiming to be a proxy for root, which should never happen in normal usage.
@@ -4922,7 +4986,8 @@ public class AppOpsService extends IAppOpsService.Stub {
            // system app (or is null), in order to prevent abusive apps clogging the appops
            // system with unlimited attribution tags via proxy calls.
            return new PackageVerificationResult(null,
                    /* isAttributionTagValid */ isPackageNullOrSystem(proxyPackageName, proxyUid));
                    /* isAttributionTagValid */ isProxyTrusted
                    && isPackageNullOrSystem(proxyPackageName, proxyUid));
        }
        if (Process.isSdkSandboxUid(uid)) {
            // SDK sandbox processes run in their own UID range, but their associated
@@ -4986,7 +5051,8 @@ public class AppOpsService extends IAppOpsService.Stub {
            // system app (or is null), in order to prevent abusive apps clogging the appops
            // system with unlimited attribution tags via proxy calls.
            return new PackageVerificationResult(RestrictionBypass.UNRESTRICTED,
                    /* isAttributionTagValid */ isPackageNullOrSystem(proxyPackageName, proxyUid));
                    /* isAttributionTagValid */ isProxyTrusted
                    && isPackageNullOrSystem(proxyPackageName, proxyUid));
        }

        int userId = UserHandle.getUserId(uid);
@@ -5007,8 +5073,9 @@ public class AppOpsService extends IAppOpsService.Stub {
            if (!isAttributionTagValid) {
                AndroidPackage proxyPkg = proxyPackageName != null
                        ? pmInt.getPackage(proxyPackageName) : null;
                // Re-check in proxy.
                isAttributionTagValid = isAttributionInPackage(proxyPkg, attributionTag);
                // Re-check in proxy, if trusted.
                isAttributionTagValid =
                        isProxyTrusted && isAttributionInPackage(proxyPkg, attributionTag);
                String msg;
                if (pkg != null && isAttributionTagValid) {
                    msg = "attributionTag " + attributionTag + " declared in manifest of the proxy"
@@ -5035,7 +5102,6 @@ public class AppOpsService extends IAppOpsService.Stub {
            throw new SecurityException("Specified package \"" + packageName + "\" under uid " + uid
                    + otherUidMessage);
        }

        return new PackageVerificationResult(bypass, isAttributionTagValid);
    }

@@ -5050,6 +5116,10 @@ public class AppOpsService extends IAppOpsService.Stub {
        if (appId > 0 && appId < Process.FIRST_APPLICATION_UID) {
            return true;
        }
        if (mPackageManagerInternal.getPackageUid(packageName, PackageManager.MATCH_ALL,
                UserHandle.getUserId(uid)) != uid) {
            return false;
        }
        return mPackageManagerInternal.isSystemPackage(packageName);
    }

+14 −0
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.function.Consumer;
import java.util.function.Predicate;

final class AttributedOp {
    private final @NonNull AppOpsService mAppOpsService;
@@ -620,6 +621,19 @@ final class AttributedOp {
        return mPausedInProgressEvents != null && !mPausedInProgressEvents.isEmpty();
    }

    public boolean hasInProgressEvent(Predicate<InProgressStartOpEvent> predicate) {
        ArrayMap<IBinder, InProgressStartOpEvent> events =
                isPaused() ? mPausedInProgressEvents : mInProgressEvents;
        if (events == null || events.isEmpty()) {
            return false;
        }
        for (int i = 0; i < events.size(); i++) {
            if (predicate.test(events.valueAt(i))) {
                return true;
            }
        }
        return false;
    }
    boolean hasAnyTime() {
        return (mAccessEvents != null && mAccessEvents.size() > 0)
                || (mRejectEvents != null && mRejectEvents.size() > 0);