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

Commit 5676ae25 authored by Victor Chang's avatar Victor Chang
Browse files

isDeviceOwnerProvisioningAllowed implementation to match enforceCanSetDeviceOwnerLocked

The main purpose is to fix the security flaw that
user can force isDeviceOwnerProvisioningAllowed to return true
by setting the device_provisioned without factory reset

Check UserSetupComplete instead, as it's cached by DPMS if it's ever set to true

Refactor common code of isDeviceOwnerProvisioningAllowed and enforceCanSetDeviceOwnerLocked
The functionality of enforceCanSetDeviceOwnerLocked should be exactly the same.
DPM Unit Test all pass

Bug:27403225
Change-Id: I32dae8e222e01e08664abb313ead3a92d4186658
parent 86ea8f00
Loading
Loading
Loading
Loading
+88 −54
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import com.google.android.collect.Sets;
import android.Manifest.permission;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accounts.AccountManager;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
@@ -151,6 +152,8 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.charset.StandardCharsets;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
@@ -280,6 +283,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
    private static final int PROFILE_KEYGUARD_FEATURES =
            PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER | PROFILE_KEYGUARD_FEATURES_PROFILE_ONLY;

    private static final int CODE_OK = 0;
    private static final int CODE_HAS_DEVICE_OWNER = 1;
    private static final int CODE_USER_HAS_PROFILE_OWNER = 2;
    private static final int CODE_USER_NOT_RUNNING = 3;
    private static final int CODE_USER_SETUP_COMPLETED = 4;
    private static final int CODE_NONSYSTEM_USER_EXISTS = 5;
    private static final int CODE_ACCOUNTS_NOT_EMPTY = 6;

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({ CODE_OK, CODE_HAS_DEVICE_OWNER, CODE_USER_HAS_PROFILE_OWNER, CODE_USER_NOT_RUNNING,
            CODE_USER_SETUP_COMPLETED })
    private @interface DeviceOwnerPreConditionCode {}

    private static final int DEVICE_ADMIN_DEACTIVATE_TIMEOUT = 10000;

    final Context mContext;
@@ -5933,52 +5949,38 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
    /**
     * The Device owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS
     * permission.
     * The device owner can only be set before the setup phase of the primary user has completed,
     * except for adb if no accounts or additional users are present on the device.
     */
    private void enforceCanSetDeviceOwnerLocked(int userId) {
        if (mOwners.hasDeviceOwner()) {
            throw new IllegalStateException("Trying to set the device owner, but device owner "
                    + "is already set.");
        }
        if (mOwners.hasProfileOwner(userId)) {
            throw new IllegalStateException("Trying to set the device owner, but the user already "
                    + "has a profile owner.");
        }
        if (!mUserManager.isUserRunning(new UserHandle(userId))) {
            throw new IllegalStateException("User not running: " + userId);
        int callingUid = mInjector.binderGetCallingUid();
        boolean isAdb = callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
        if (!isAdb) {
            mContext.enforceCallingOrSelfPermission(
                    android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
        }

        int callingUid = mInjector.binderGetCallingUid();
        if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
            if (!hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
        final int code = checkSetDeviceOwnerPreCondition(userId, isAdb);
        switch (code) {
            case CODE_OK:
                return;
            }
            // STOPSHIP Do proper check in split user mode
            if (!mInjector.userManagerIsSplitSystemUser()) {
                if (mUserManager.getUserCount() > 1) {
            case CODE_HAS_DEVICE_OWNER:
                throw new IllegalStateException(
                            "Not allowed to set the device owner because there "
                                    + "are already several users on the device");
                }
                if (AccountManager.get(mContext).getAccounts().length > 0) {
                        "Trying to set the device owner, but device owner is already set.");
            case CODE_USER_HAS_PROFILE_OWNER:
                throw new IllegalStateException("Trying to set the device owner, but the user "
                        + "already has a profile owner.");
            case CODE_USER_NOT_RUNNING:
                throw new IllegalStateException("User not running: " + userId);
            case CODE_USER_SETUP_COMPLETED:
                throw new IllegalStateException(
                            "Not allowed to set the device owner because there "
                        "Cannot set the device owner if the device is already set-up");
            case CODE_NONSYSTEM_USER_EXISTS:
                throw new IllegalStateException("Not allowed to set the device owner because there "
                        + "are already several users on the device");
            case CODE_ACCOUNTS_NOT_EMPTY:
                throw new IllegalStateException("Not allowed to set the device owner because there "
                        + "are already some accounts on the device");
                }
            }
            return;
        }
        // STOPSHIP check the caller UID with userId

        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
        // STOPSHIP Do proper check in split user mode
        if (!mInjector.userManagerIsSplitSystemUser()) {
            if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
                throw new IllegalStateException("Cannot set the device owner if the device is "
                        + "already set-up");
            }
            default:
                throw new IllegalStateException("Unknown @DeviceOwnerPreConditionCode " + code);
        }
    }

@@ -8035,6 +8037,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {

    @Override
    public boolean isProvisioningAllowed(String action) {
        if (!mHasFeature) {
            return false;
        }

        final int callingUserId = mInjector.userHandleGetCallingUserId();
        if (DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE.equals(action)) {
            if (!hasFeatureManagedUsers()) {
@@ -8099,23 +8105,51 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
        throw new IllegalArgumentException("Unknown provisioning action " + action);
    }

    private boolean isDeviceOwnerProvisioningAllowed(int callingUserId) {
        synchronized (this) {
    /*
     * The device owner can only be set before the setup phase of the primary user has completed,
     * except for adb command if no accounts or additional users are present on the device.
     */
    private synchronized @DeviceOwnerPreConditionCode int checkSetDeviceOwnerPreCondition(
            int deviceOwnerUserId, boolean isAdb) {
        if (mOwners.hasDeviceOwner()) {
                return false;
            return CODE_HAS_DEVICE_OWNER;
        }
        if (mOwners.hasProfileOwner(deviceOwnerUserId)) {
            return CODE_USER_HAS_PROFILE_OWNER;
        }
        if (getProfileOwner(callingUserId) != null) {
            return false;
        if (!mUserManager.isUserRunning(new UserHandle(deviceOwnerUserId))) {
            return CODE_USER_NOT_RUNNING;
        }
        if (mInjector.settingsGlobalGetInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0) {
            return false;
        if (isAdb) {
            // if shell command runs after user setup completed check device status. Otherwise, OK.
            if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
                if (!mInjector.userManagerIsSplitSystemUser()) {
                    if (mUserManager.getUserCount() > 1) {
                        return CODE_NONSYSTEM_USER_EXISTS;
                    }
        if (callingUserId != UserHandle.USER_SYSTEM) {
            // Device owner provisioning can only be initiated from system user.
            return false;
                    if (AccountManager.get(mContext).getAccounts().length > 0) {
                        return CODE_ACCOUNTS_NOT_EMPTY;
                    }
        return true;
                } else {
                    // STOPSHIP Do proper check in split user mode
                }
            }
            return CODE_OK;
        } else {
            if (!mInjector.userManagerIsSplitSystemUser()) {
                // In non-split user mode, only provision DO before setup wizard completes
                if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
                    return CODE_USER_SETUP_COMPLETED;
                }
            } else {
                // STOPSHIP Do proper check in split user mode
            }
            return CODE_OK;
        }
    }

    private boolean isDeviceOwnerProvisioningAllowed(int deviceOwnerUserId) {
        return CODE_OK == checkSetDeviceOwnerPreCondition(deviceOwnerUserId, /* isAdb */ false);
    }

    private boolean hasFeatureManagedUsers() {