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

Commit ef0cac74 authored by Winson's avatar Winson
Browse files

Add DomainVerificationEnforcer

Abstracts the permission/caller enforcement code from
DomainVerificationService.

There's 3 types of callers the service cares about:
1. Able to change internal state, which for now is only system and shell
    - Not currently used, but will be for debug/shell commands
2. Approved verifier, which is system, shell, or the verification agent
3. Approved user state selector, which requires the permission

Exempt-From-Owner-Approval: Already approved by owners on main branch

Bug: 163565712

Test: atest DomainVerificationEnforcerTest

Change-Id: I7c00e4ebd843537e0b20e60c82a1c4abf2904596
parent 5429dfc3
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -1833,6 +1833,17 @@ public class PackageManagerService extends IPackageManager.Stub
            }
        }
        @Override
        public int getCallingUid() {
            return Binder.getCallingUid();
        }
        @UserIdInt
        @Override
        public int getCallingUserId() {
            return UserHandle.getCallingUserId();
        }
        @Override
        public void schedule(int code, @Nullable Object object) {
            Message message = mHandler.obtainMessage(DOMAIN_VERIFICATION);
@@ -1856,6 +1867,18 @@ public class PackageManagerService extends IPackageManager.Stub
            final int callingUserId = UserHandle.getUserId(callingUid);
            return callingUid == getPackageUid(packageName, 0, callingUserId);
        }
        @Nullable
        @Override
        public PackageSetting getPackageSettingLocked(@NonNull String pkgName) {
            return PackageManagerService.this.getPackageSetting(pkgName);
        }
        @Nullable
        @Override
        public AndroidPackage getPackageLocked(@NonNull String pkgName) {
            return PackageManagerService.this.getPackage(pkgName);
        }
    }
    /**
+121 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.pm.domain.verify;

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

import com.android.server.pm.domain.verify.proxy.DomainVerificationProxy;

public class DomainVerificationEnforcer {

    @NonNull
    private final Context mContext;

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

    /**
     * Enforced when mutating any state from shell or internally in the system process.
     */
    public void assertInternal(int callingUid) {
        switch (callingUid) {
            case Process.ROOT_UID:
            case Process.SHELL_UID:
            case Process.SYSTEM_UID:
                break;
            default:
                throw new SecurityException(
                        "Caller " + callingUid + " is not allowed to change internal state");
        }
    }

    /**
     * Enforced when retrieving state for a package. The system, the verifier, and anyone approved
     * to mutate user selections are allowed through.
     */
    public void assertApprovedQuerent(int callingUid, @NonNull DomainVerificationProxy proxy) {
        switch (callingUid) {
            case Process.ROOT_UID:
            case Process.SHELL_UID:
            case Process.SYSTEM_UID:
                break;
            default:
                if (!proxy.isCallerVerifier(callingUid)) {
                    mContext.enforcePermission(
                            android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION,
                            Binder.getCallingPid(), callingUid,
                            "Caller " + callingUid
                                    + " is not allowed to query domain verification state");
                }
                break;
        }
    }

    /**
     * Enforced when mutating domain verification state inside an exposed API method.
     */
    public void assertApprovedVerifier(int callingUid, @NonNull DomainVerificationProxy proxy)
            throws SecurityException {
        boolean isAllowed;
        switch (callingUid) {
            case Process.ROOT_UID:
            case Process.SHELL_UID:
            case Process.SYSTEM_UID:
                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");
                isAllowed = proxy.isCallerVerifier(callingUid);
                break;
        }

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

    /**
     * Enforced when mutating user selection state inside an exposed API method.
     */
    public void assertApprovedUserSelector(int callingUid, @UserIdInt int callingUserId,
            @UserIdInt int targetUserId) throws SecurityException {
        if (callingUserId != targetUserId) {
            mContext.enforcePermission(
                    Manifest.permission.INTERACT_ACROSS_USERS,
                    Binder.getCallingPid(), callingUid,
                    "Caller is not allowed to edit other users");
        }

        mContext.enforcePermission(
                android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION,
                Binder.getCallingPid(), callingUid,
                "Caller is not allowed to edit user selections");
    }
}
+57 −6
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@ import android.content.pm.domain.verify.DomainVerificationSet;
import android.content.pm.domain.verify.DomainVerificationState;
import android.content.pm.domain.verify.DomainVerificationUserSelection;
import android.content.pm.domain.verify.IDomainVerificationManager;
import android.os.Binder;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Singleton;
@@ -41,6 +43,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.server.SystemConfig;
import com.android.server.SystemService;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.PackageSetting;
import com.android.server.pm.domain.verify.models.DomainVerificationPkgState;
import com.android.server.pm.domain.verify.models.DomainVerificationStateMap;
@@ -89,6 +92,9 @@ public class DomainVerificationService extends SystemService
    @NonNull
    private final DomainVerificationCollector mCollector;

    @NonNull
    private final DomainVerificationEnforcer mEnforcer;

    @NonNull
    private final IDomainVerificationManager.Stub mStub = new DomainVerificationManagerStub(this);

@@ -102,6 +108,7 @@ public class DomainVerificationService extends SystemService
        mSystemConfig = systemConfig;
        mSettings = new DomainVerificationSettings();
        mCollector = new DomainVerificationCollector(platformCompat, systemConfig);
        mEnforcer = new DomainVerificationEnforcer(context);
    }

    @Override
@@ -117,6 +124,7 @@ public class DomainVerificationService extends SystemService
    @NonNull
    @Override
    public List<String> getValidVerificationPackageNames() {
        mEnforcer.assertApprovedVerifier(mConnection.get().getCallingUid(), mProxy);
        return null;
    }

@@ -124,12 +132,14 @@ public class DomainVerificationService extends SystemService
    @Override
    public DomainVerificationSet getDomainVerificationSet(@NonNull String packageName)
            throws NameNotFoundException {
        mEnforcer.assertApprovedQuerent(mConnection.get().getCallingUid(), mProxy);
        return null;
    }

    @Override
    public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
            int state) throws InvalidDomainSetException, NameNotFoundException {
        mEnforcer.assertApprovedVerifier(mConnection.get().getCallingUid(), mProxy);
        //TODO(b/163565712): Implement method
        mConnection.get().scheduleWriteSettings();
    }
@@ -137,12 +147,15 @@ public class DomainVerificationService extends SystemService
    @Override
    public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName,
            boolean allowed) throws NameNotFoundException {
        //TODO(b/163565712): Implement method
        mConnection.get().scheduleWriteSettings();
        setDomainVerificationLinkHandlingAllowed(packageName, allowed,
                mConnection.get().getCallingUserId());
    }

    public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName,
            boolean allowed, @UserIdInt int userId) throws NameNotFoundException {
        Connection connection = mConnection.get();
        mEnforcer.assertApprovedUserSelector(connection.getCallingUid(),
                connection.getCallingUserId(), userId);
        //TODO(b/163565712): Implement method
        mConnection.get().scheduleWriteSettings();
    }
@@ -151,13 +164,16 @@ public class DomainVerificationService extends SystemService
    public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
            @NonNull Set<String> domains, boolean enabled)
            throws InvalidDomainSetException, NameNotFoundException {
        //TODO(b/163565712): Implement method
        mConnection.get().scheduleWriteSettings();
        setDomainVerificationUserSelection(domainSetId, domains, enabled,
                mConnection.get().getCallingUserId());
    }

    public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
            @NonNull Set<String> domains, boolean enabled, @UserIdInt int userId)
            throws InvalidDomainSetException, NameNotFoundException {
        Connection connection = mConnection.get();
        mEnforcer.assertApprovedUserSelector(connection.getCallingUid(),
                connection.getCallingUserId(), userId);
        //TODO(b/163565712): Implement method
        mConnection.get().scheduleWriteSettings();
    }
@@ -166,12 +182,16 @@ public class DomainVerificationService extends SystemService
    @Override
    public DomainVerificationUserSelection getDomainVerificationUserSelection(
            @NonNull String packageName) throws NameNotFoundException {
        return null;
        return getDomainVerificationUserSelection(packageName,
                mConnection.get().getCallingUserId());
    }

    @Nullable
    public DomainVerificationUserSelection getDomainVerificationUserSelection(
            @NonNull String packageName, @UserIdInt int userId) throws NameNotFoundException {
        Connection connection = mConnection.get();
        mEnforcer.assertApprovedUserSelector(connection.getCallingUid(),
                connection.getCallingUserId(), userId);
        return null;
    }

@@ -449,7 +469,38 @@ public class DomainVerificationService extends SystemService
         */
        void scheduleWriteSettings();

        /** @see DomainVerificationProxy.Connection#schedule(int, java.lang.Object) */
        /**
         * Delegate to {@link Binder#getCallingUid()} to allow mocking in tests.
         */
        int getCallingUid();

        /**
         * Delegate to {@link UserHandle#getCallingUserId()} to allow mocking in tests.
         */
        @UserIdInt
        int getCallingUserId();

        /**
         * @see DomainVerificationProxy.Connection#schedule(int, java.lang.Object)
         */
        void schedule(int code, @Nullable Object object);

        boolean isCallerPackage(int callingUid, @NonNull String packageName);

        /**
         * This can only be called when the internal {@link #mLock} is held. Otherwise it's possible
         * to deadlock with {@link PackageManagerService}.
         */
        @GuardedBy("mLock")
        @Nullable
        PackageSetting getPackageSettingLocked(@NonNull String pkgName);

        /**
         * This can only be called when the internal {@link #mLock} is held. Otherwise it's possible
         * to deadlock with {@link PackageManagerService}.
         */
        @GuardedBy("mLock")
        @Nullable
        AndroidPackage getPackageLocked(@NonNull String pkgName);
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ android_test {
        "junit",
        "services.core",
        "servicestests-utils",
        "testng",
        "truth-prebuilt",
    ],
    platform_apis: true,
+446 −0

File added.

Preview size limit exceeded, changes collapsed.