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

Commit cab33970 authored by Winson Chiu's avatar Winson Chiu Committed by Android (Google) Code Review
Browse files

Merge "Fix DomainVerificationEnforcer for app visibility" into sc-dev

parents e9716144 6821adee
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -1749,6 +1749,11 @@ public class PackageManagerService extends IPackageManager.Stub
        public AndroidPackage getPackage(@NonNull String packageName) {
            return getPackageLocked(packageName);
        }
        @Override
        public boolean filterAppAccess(String packageName, int callingUid, int userId) {
            return mPmInternal.filterAppAccess(packageName, callingUid, userId);
        }
    }
    /**
@@ -16157,8 +16162,7 @@ public class PackageManagerService extends IPackageManager.Stub
    @Deprecated
    @Override
    public boolean updateIntentVerificationStatus(String packageName, int status, int userId) {
        mDomainVerificationManager.setLegacyUserState(packageName, userId, status);
        return true;
        return mDomainVerificationManager.setLegacyUserState(packageName, userId, status);
    }
    @Deprecated
+0 −1
Original line number Diff line number Diff line
@@ -2712,7 +2712,6 @@ public final class Settings implements Watchable, Snappable {
        writeSigningKeySetLPr(serializer, pkg.keySetData);
        writeUpgradeKeySetsLPr(serializer, pkg.keySetData);
        writeKeySetAliasesLPr(serializer, pkg.keySetData);
        mDomainVerificationManager.writeLegacySettings(serializer, pkg.name);
        writeMimeGroupLPr(serializer, pkg.mimeGroups);

        serializer.endTag(null, "package");
+78 −11
Original line number Diff line number Diff line
@@ -18,8 +18,10 @@ package com.android.server.pm.verify.domain;

import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Process;

@@ -30,10 +32,17 @@ public class DomainVerificationEnforcer {
    @NonNull
    private final Context mContext;

    @NonNull
    private Callback mCallback;

    public DomainVerificationEnforcer(@NonNull Context context) {
        mContext = context;
    }

    public void setCallback(@NonNull Callback callback) {
        mCallback = callback;
    }

    /**
     * Enforced when mutating any state from shell or internally in the system process.
     */
@@ -67,6 +76,11 @@ public class DomainVerificationEnforcer {
                            "Caller " + callingUid
                                    + " is not allowed to query domain verification state");
                }

                mContext.enforcePermission(android.Manifest.permission.QUERY_ALL_PACKAGES,
                        Binder.getCallingPid(), callingUid,
                        "Caller " + callingUid + " does not hold "
                                + android.Manifest.permission.QUERY_ALL_PACKAGES);
                break;
        }
    }
@@ -84,28 +98,42 @@ public class DomainVerificationEnforcer {
                isAllowed = true;
                break;
            default:
                // TODO(b/159952358): Remove permission check? The component package should
                //  have been checked when the verifier component was first scanned in PMS.
                mContext.enforcePermission(
                        android.Manifest.permission.DOMAIN_VERIFICATION_AGENT,
                        Binder.getCallingPid(), callingUid,
                        "Caller " + callingUid + " does not hold DOMAIN_VERIFICATION_AGENT");
                final int callingPid = Binder.getCallingPid();
                boolean isLegacyVerificationAgent = false;
                if (mContext.checkPermission(
                        android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, callingPid,
                        callingUid) != PackageManager.PERMISSION_GRANTED) {
                    isLegacyVerificationAgent = mContext.checkPermission(
                            android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT,
                            callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
                    if (!isLegacyVerificationAgent) {
                        throw new SecurityException("Caller " + callingUid + " does not hold "
                                + android.Manifest.permission.DOMAIN_VERIFICATION_AGENT);
                    }
                }

                // If the caller isn't a legacy verifier, it needs the QUERY_ALL permission
                if (!isLegacyVerificationAgent) {
                    mContext.enforcePermission(android.Manifest.permission.QUERY_ALL_PACKAGES,
                            callingPid, callingUid, "Caller " + callingUid + " does not hold "
                                    + android.Manifest.permission.QUERY_ALL_PACKAGES);
                }

                isAllowed = proxy.isCallerVerifier(callingUid);
                break;
        }

        if (!isAllowed) {
            throw new SecurityException("Caller " + callingUid
                    + " is not the approved domain verification agent, isVerifier = "
                    + proxy.isCallerVerifier(callingUid));
                    + " is not the approved domain verification agent");
        }
    }

    /**
     * Enforced when mutating user selection state inside an exposed API method.
     */
    public void assertApprovedUserSelector(int callingUid, @UserIdInt int callingUserId,
            @UserIdInt int targetUserId) throws SecurityException {
    public boolean assertApprovedUserSelector(int callingUid, @UserIdInt int callingUserId,
            @Nullable String packageName, @UserIdInt int targetUserId) throws SecurityException {
        if (callingUserId != targetUserId) {
            mContext.enforcePermission(
                    Manifest.permission.INTERACT_ACROSS_USERS,
@@ -117,12 +145,51 @@ public class DomainVerificationEnforcer {
                android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION,
                Binder.getCallingPid(), callingUid,
                "Caller is not allowed to edit user selections");

        if (packageName == null) {
            return true;
        }

        return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
    }

    public void callerIsLegacyUserSelector(int callingUid) {
    public boolean callerIsLegacyUserSelector(int callingUid, @UserIdInt int callingUserId,
            @NonNull String packageName, @UserIdInt int targetUserId) {
        mContext.enforcePermission(
                android.Manifest.permission.SET_PREFERRED_APPLICATIONS,
                Binder.getCallingPid(), callingUid,
                "Caller is not allowed to edit user state");

        if (callingUserId != targetUserId) {
            if (mContext.checkPermission(
                    Manifest.permission.INTERACT_ACROSS_USERS,
                    Binder.getCallingPid(), callingUid) != PackageManager.PERMISSION_GRANTED) {
                // Legacy API did not enforce this, so for backwards compatibility, fail silently
                return false;
            }
        }

        return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
    }

    public boolean callerIsLegacyUserQuerent(int callingUid, @UserIdInt int callingUserId,
            @NonNull String packageName, @UserIdInt int targetUserId) {
        if (callingUserId != targetUserId) {
            // The legacy API enforces the _FULL variant, so maintain that here
            mContext.enforcePermission(
                    Manifest.permission.INTERACT_ACROSS_USERS_FULL,
                    Binder.getCallingPid(), callingUid,
                    "Caller is not allowed to edit other users");
        }

        return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
    }

    public interface Callback {
        /**
         * @return true if access to the given package should be filtered and the method failed as
         * if the package was not installed
         */
        boolean filterAppAccess(@NonNull String packageName, int callingUid, @UserIdInt int userId);
    }
}
+5 −8
Original line number Diff line number Diff line
@@ -174,8 +174,10 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan
     * Set aside a legacy user selection that will be restored to a pending
     * {@link DomainVerificationPkgState} once it's added through
     * {@link #addPackage(PackageSetting)}.
     *
     * @return true if state changed successfully
     */
    void setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state);
    boolean setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state);

    /**
     * Until the legacy APIs are entirely removed, returns the legacy state from the previously
@@ -183,12 +185,6 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan
     */
    int getLegacyState(@NonNull String packageName, @UserIdInt int userId);

    /**
     * Serialize a legacy setting that wasn't attached yet.
     * TODO: Does this even matter? Should consider for removal.
     */
    void writeLegacySettings(TypedXmlSerializer serializer, String name);

    /**
     * Print the verification state and user selection state of a package.
     *
@@ -235,7 +231,8 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan
            throws IllegalArgumentException, NameNotFoundException;


    interface Connection extends Function<String, PackageSetting> {
    interface Connection extends DomainVerificationEnforcer.Callback,
            Function<String, PackageSetting> {

        /**
         * Notify that a settings change has been made and that eventually
+46 −19
Original line number Diff line number Diff line
@@ -47,12 +47,12 @@ import com.android.server.SystemConfig;
import com.android.server.SystemService;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.PackageSetting;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
import com.android.server.pm.verify.domain.models.DomainVerificationUserState;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyUnavailable;
import com.android.server.pm.parsing.pkg.AndroidPackage;

import org.xmlpull.v1.XmlPullParserException;

@@ -92,9 +92,9 @@ public class DomainVerificationService extends SystemService
     * immediately attached once its available.
     * <p>
     * Generally this should be not accessed directly. Prefer calling {@link
     * #getAndValidateAttachedLocked(UUID, Set, boolean)}.
     * #getAndValidateAttachedLocked(UUID, Set, boolean, int, Integer)}.
     *
     * @see #getAndValidateAttachedLocked(UUID, Set, boolean)
     * @see #getAndValidateAttachedLocked(UUID, Set, boolean, int, Integer)
     **/
    @GuardedBy("mLock")
    @NonNull
@@ -160,6 +160,7 @@ public class DomainVerificationService extends SystemService
    @Override
    public void setConnection(@NonNull Connection connection) {
        mConnection = connection;
        mEnforcer.setCallback(mConnection);
    }

    @NonNull
@@ -285,7 +286,7 @@ public class DomainVerificationService extends SystemService
        mEnforcer.assertApprovedVerifier(callingUid, mProxy);
        synchronized (mLock) {
            DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains,
                    true /* forAutoVerify */);
                    true /* forAutoVerify */, callingUid, null /* userId */);
            ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
            for (String domain : domains) {
                Integer previousState = stateMap.get(domain);
@@ -389,8 +390,10 @@ public class DomainVerificationService extends SystemService

    public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName,
            boolean allowed, @UserIdInt int userId) throws NameNotFoundException {
        mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
                mConnection.getCallingUserId(), userId);
        if (!mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
                mConnection.getCallingUserId(), packageName, userId)) {
            throw DomainVerificationUtils.throwPackageUnavailable(packageName);
        }
        synchronized (mLock) {
            DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
            if (pkgState == null) {
@@ -455,11 +458,18 @@ public class DomainVerificationService extends SystemService
    public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
            @NonNull Set<String> domains, boolean enabled, @UserIdInt int userId)
            throws InvalidDomainSetException, NameNotFoundException {
        mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
                mConnection.getCallingUserId(), userId);
        synchronized (mLock) {
            final int callingUid = mConnection.getCallingUid();
            // Pass null for package name here and do the app visibility enforcement inside
            // getAndValidateAttachedLocked instead, since this has to fail with the same invalid
            // ID reason if the target app is invisible
            if (!mEnforcer.assertApprovedUserSelector(callingUid, mConnection.getCallingUserId(),
                    null /* packageName */, userId)) {
                throw new InvalidDomainSetException(domainSetId, null,
                        InvalidDomainSetException.REASON_ID_INVALID);
            }
            DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains,
                    false /* forAutoVerify */);
                    false /* forAutoVerify */, callingUid, userId);
            DomainVerificationUserState userState = pkgState.getOrCreateUserSelectionState(userId);
            if (enabled) {
                userState.addHosts(domains);
@@ -556,8 +566,10 @@ public class DomainVerificationService extends SystemService
    @Override
    public DomainVerificationUserSelection getDomainVerificationUserSelection(
            @NonNull String packageName, @UserIdInt int userId) throws NameNotFoundException {
        mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
                mConnection.getCallingUserId(), userId);
        if (!mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
                mConnection.getCallingUserId(), packageName, userId)) {
            throw DomainVerificationUtils.throwPackageUnavailable(packageName);
        }
        synchronized (mLock) {
            DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
            if (pkgState == null) {
@@ -844,20 +856,24 @@ public class DomainVerificationService extends SystemService
    }

    @Override
    public void setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state) {
        mEnforcer.callerIsLegacyUserSelector(mConnection.getCallingUid());
    public boolean setLegacyUserState(@NonNull String packageName, @UserIdInt int userId,
            int state) {
        if (!mEnforcer.callerIsLegacyUserSelector(mConnection.getCallingUid(),
                mConnection.getCallingUserId(), packageName, userId)) {
            return false;
        }
        mLegacySettings.add(packageName, userId, state);
        mConnection.scheduleWriteSettings();
        return true;
    }

    @Override
    public int getLegacyState(@NonNull String packageName, @UserIdInt int userId) {
        return mLegacySettings.getUserState(packageName, userId);
        if (!mEnforcer.callerIsLegacyUserQuerent(mConnection.getCallingUid(),
                mConnection.getCallingUserId(), packageName, userId)) {
            return PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
        }

    @Override
    public void writeLegacySettings(TypedXmlSerializer serializer, String name) {

        return mLegacySettings.getUserState(packageName, userId);
    }

    @Override
@@ -935,10 +951,14 @@ public class DomainVerificationService extends SystemService
     * Validates parameters provided by an external caller. Checks that an ID is still live and that
     * any provided domains are valid. Should be called at the beginning of each API that takes in a
     * {@link UUID} domain set ID.
     *
     * @param userIdForFilter which user to filter app access to, or null if the caller has already
     *                        validated package visibility
     */
    @GuardedBy("mLock")
    private DomainVerificationPkgState getAndValidateAttachedLocked(@NonNull UUID domainSetId,
            @NonNull Set<String> domains, boolean forAutoVerify)
            @NonNull Set<String> domains, boolean forAutoVerify, int callingUid,
            @Nullable Integer userIdForFilter)
            throws InvalidDomainSetException, NameNotFoundException {
        if (domainSetId == null) {
            throw new InvalidDomainSetException(null, null,
@@ -952,6 +972,13 @@ public class DomainVerificationService extends SystemService
        }

        String pkgName = pkgState.getPackageName();

        if (userIdForFilter != null
                && mConnection.filterAppAccess(pkgName, callingUid, userIdForFilter)) {
            throw new InvalidDomainSetException(domainSetId, null,
                    InvalidDomainSetException.REASON_ID_INVALID);
        }

        PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName);
        if (pkgSetting == null || pkgSetting.getPkg() == null) {
            throw DomainVerificationUtils.throwPackageUnavailable(pkgName);
Loading