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

Commit 9f62a263 authored by Zimuzo's avatar Zimuzo
Browse files

Add privileged APIs to lock device and reset device password

In 534d732e9f274ad3f3e0637b9da963f889309afb, we are restricting privileged apps from silently becoming
Device Admins. Privileged apps can now call the following existing Device Admin APIs provided they have the correct permissions:
1. DevicePolicyManager#resetPassword -> Guarded by android.permission.RESET_PASSWORD
2. DevicePolicyManager#lockNow -> Guarded by android.permission.LOCK_DEVICE

The following existing Device Admin APIs already have alternatives hence no change required:
3. DevicePolicyManager#wipeData -> Send ACTION_FACTORY_RESET broadcast.
Guarded by android.permission.MASTER_CLEAR
4. DevicePolicyManager#setKeyguardDisabledFeatures -> Write '0' to LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS setting
Guarded by WRITE_SECURE_SETTINGS

Bug: 111153365
Bug: 112601004
Test: Manually tested with dev privileged app
Change-Id: Ia4e1ce9b81756e7f84ed0aa22d97e0b968cd8d89
parent 66b0919d
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -91,6 +91,7 @@ package android {
    field public static final java.lang.String KILL_UID = "android.permission.KILL_UID";
    field public static final java.lang.String LOCAL_MAC_ADDRESS = "android.permission.LOCAL_MAC_ADDRESS";
    field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
    field public static final java.lang.String LOCK_DEVICE = "android.permission.LOCK_DEVICE";
    field public static final java.lang.String LOOP_RADIO = "android.permission.LOOP_RADIO";
    field public static final java.lang.String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
    field public static final java.lang.String MANAGE_APP_OPS_RESTRICTIONS = "android.permission.MANAGE_APP_OPS_RESTRICTIONS";
@@ -155,6 +156,7 @@ package android {
    field public static final java.lang.String REGISTER_CONNECTION_MANAGER = "android.permission.REGISTER_CONNECTION_MANAGER";
    field public static final java.lang.String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION";
    field public static final java.lang.String REMOVE_DRM_CERTIFICATES = "android.permission.REMOVE_DRM_CERTIFICATES";
    field public static final java.lang.String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
    field public static final java.lang.String RESTRICTED_VR_ACCESS = "android.permission.RESTRICTED_VR_ACCESS";
    field public static final java.lang.String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
    field public static final java.lang.String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS";
+12 −0
Original line number Diff line number Diff line
@@ -3054,6 +3054,18 @@
    <permission android:name="android.permission.MANAGE_DEVICE_ADMINS"
        android:protectionLevel="signature|privileged" />

    <!-- @SystemApi Allows an app to reset the device password.
         <p>Not for use by third-party applications.
         @hide -->
    <permission android:name="android.permission.RESET_PASSWORD"
        android:protectionLevel="signature|privileged" />

    <!-- @SystemApi Allows an app to lock the device.
         <p>Not for use by third-party applications.
         @hide -->
    <permission android:name="android.permission.LOCK_DEVICE"
        android:protectionLevel="signature|privileged" />

    <!-- @SystemApi Allows low-level access to setting the orientation (actually
         rotation) of the screen.
         <p>Not for use by third-party applications.
+82 −32
Original line number Diff line number Diff line
@@ -2595,12 +2595,32 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
    ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy)
            throws SecurityException {
        return getActiveAdminOrCheckPermissionForCallerLocked(who,
                reqPolicy, /* permission= */ null);
    }
    /**
     * Finds an active admin for the caller then checks {@code permission} if admin check failed.
     *
     * @return an active admin or {@code null} if there is no active admin but
     * {@code permission} is granted
     * @throws SecurityException if caller neither has an active admin nor {@code permission}
     */
    @Nullable
    ActiveAdmin getActiveAdminOrCheckPermissionForCallerLocked(
            ComponentName who,
            int reqPolicy,
            @Nullable String permission) throws SecurityException {
        ensureLocked();
        final int callingUid = mInjector.binderGetCallingUid();
        ActiveAdmin result = getActiveAdminWithPolicyForUidLocked(who, reqPolicy, callingUid);
        if (result != null) {
            return result;
        } else if (permission != null
                && (mContext.checkCallingPermission(permission)
                        == PackageManager.PERMISSION_GRANTED)) {
            return null;
        }
        if (who != null) {
@@ -2620,20 +2640,39 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
                    + admin.info.getTagForPolicy(reqPolicy));
        } else {
            throw new SecurityException("No active admin owned by uid "
                    + mInjector.binderGetCallingUid() + " for policy #" + reqPolicy);
                    + callingUid + " for policy #" + reqPolicy);
        }
    }
    ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy, boolean parent)
            throws SecurityException {
        return getActiveAdminOrCheckPermissionForCallerLocked(
                who, reqPolicy, parent, /* permission= */ null);
    }
    /**
     * Finds an active admin for the caller then checks {@code permission} if admin check failed.
     *
     * @return an active admin or {@code null} if there is no active admin but
     * {@code permission} is granted
     * @throws SecurityException if caller neither has an active admin nor {@code permission}
     */
    @Nullable
    ActiveAdmin getActiveAdminOrCheckPermissionForCallerLocked(
            ComponentName who,
            int reqPolicy,
            boolean parent,
            @Nullable String permission) throws SecurityException {
        ensureLocked();
        if (parent) {
            enforceManagedProfile(mInjector.userHandleGetCallingUserId(),
                    "call APIs on the parent profile");
        }
        ActiveAdmin admin = getActiveAdminForCallerLocked(who, reqPolicy);
        ActiveAdmin admin = getActiveAdminOrCheckPermissionForCallerLocked(
                who, reqPolicy, permission);
        return parent ? admin.getParentActiveAdmin() : admin;
    }
    /**
     * Find the admin for the component and userId bit of the uid, then check
     * the admin's uid matches the uid.
@@ -4739,10 +4778,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
                preN = getTargetSdk(admin.info.getPackageName(),
                        userHandle) <= android.os.Build.VERSION_CODES.M;
            } else {
                // Otherwise, make sure the caller has any active admin with the right policy.
                admin = getActiveAdminForCallerLocked(null,
                        DeviceAdminInfo.USES_POLICY_RESET_PASSWORD);
                preN = getTargetSdk(admin.info.getPackageName(),
                // Otherwise, make sure the caller has any active admin with the right policy or
                // the required permission.
                admin = getActiveAdminOrCheckPermissionForCallerLocked(
                        null,
                        DeviceAdminInfo.USES_POLICY_RESET_PASSWORD,
                        android.Manifest.permission.RESET_PASSWORD);
                // Cannot be preN if admin is null because an exception would have been
                // thrown before getting here
                preN = admin == null ? false : getTargetSdk(admin.info.getPackageName(),
                        userHandle) <= android.os.Build.VERSION_CODES.M;
                // As of N, password resetting to empty/null is not allowed anymore.
@@ -4758,9 +4802,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
                // As of N, password cannot be changed by the admin if it is already set.
                if (isLockScreenSecureUnchecked(userHandle)) {
                    if (!preN) {
                        throw new SecurityException("Admin cannot change current password");
                        throw new SecurityException("Cannot change current password");
                    } else {
                        Slog.e(LOG_TAG, "Admin cannot change current password");
                        Slog.e(LOG_TAG, "Cannot change current password");
                        return false;
                    }
                }
@@ -5131,16 +5175,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        final int callingUserId = mInjector.userHandleGetCallingUserId();
        synchronized (getLockObject()) {
            // This API can only be called by an active device admin,
            // so try to retrieve it to check that the caller is one.
            final ActiveAdmin admin = getActiveAdminForCallerLocked(
                    null, DeviceAdminInfo.USES_POLICY_FORCE_LOCK, parent);
            // Make sure the caller has any active admin with the right policy or
            // the required permission.
            final ActiveAdmin admin = getActiveAdminOrCheckPermissionForCallerLocked(
                    null,
                    DeviceAdminInfo.USES_POLICY_FORCE_LOCK,
                    parent,
                    android.Manifest.permission.LOCK_DEVICE);
            final long ident = mInjector.binderClearCallingIdentity();
            try {
                final ComponentName adminComponent = admin.info.getComponent();
                // Evict key
                final ComponentName adminComponent = admin == null ?
                        null : admin.info.getComponent();
                if (adminComponent != null) {
                    // For Profile Owners only, callers with only permission not allowed.
                    if ((flags & DevicePolicyManager.FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY) != 0) {
                        // Evict key
                        enforceManagedProfile(
                                callingUserId, "set FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY");
                        if (!isProfileOwner(adminComponent, callingUserId)) {
@@ -5157,6 +5206,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
                        }
                        mUserManager.evictCredentialEncryptionKey(callingUserId);
                    }
                }
                // Lock all users unless this is a managed profile with a separate challenge
                final int userToLock = (parent || !isSeparateProfileChallengeEnabled(callingUserId)
@@ -5174,7 +5224,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
                    mInjector.getTrustManager().setDeviceLockedForUser(userToLock, true);
                }
                if (SecurityLog.isLoggingEnabled()) {
                if (SecurityLog.isLoggingEnabled() && adminComponent != null) {
                    final int affectedUserId =
                            parent ? getProfileParentId(callingUserId) : callingUserId;
                    SecurityLog.writeEvent(SecurityLog.TAG_REMOTE_LOCK,