diff options
| author | 2021-01-12 18:35:49 +0000 | |
|---|---|---|
| committer | 2021-01-12 18:35:49 +0000 | |
| commit | 7c6e169d826a68106002abd8276b32781c87ab05 (patch) | |
| tree | 5a5add0cf4012d3fcfa00ffac4c76d768d017704 | |
| parent | b078a29814842c75c32210667d384e0517097fc5 (diff) | |
| parent | 7da93284ccb37b7b2ce662e81aaf944f430fb10f (diff) | |
Merge "DPM: Prevent mixing password quality and complexity"
3 files changed, 71 insertions, 0 deletions
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 97879b80ea06..1789b635a6fc 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -3020,6 +3020,10 @@ public class DevicePolicyManager { * * <p><strong>Note:</strong> Specifying password requirements using this method clears the * password complexity requirements set using {@link #setRequiredPasswordComplexity(int)}. + * If this method is called on the {@link DevicePolicyManager} instance returned by + * {@link #getParentProfileInstance(ComponentName)}, then password complexity requirements + * set on the primary {@link DevicePolicyManager} must be cleared first by calling + * {@link #setRequiredPasswordComplexity} with {@link #PASSWORD_COMPLEXITY_NONE) first. * * @deprecated Prefer using {@link #setRequiredPasswordComplexity(int)}, to require a password * that satisfies a complexity level defined by the platform, rather than specifying custom @@ -3039,6 +3043,9 @@ public class DevicePolicyManager { * calling app is targeting {@link android.os.Build.VERSION_CODES#S} and above, * and is calling the method the {@link DevicePolicyManager} instance returned by * {@link #getParentProfileInstance(ComponentName)}. + * @throws IllegalStateException if the caller is trying to set password quality on the parent + * {@link DevicePolicyManager} instance while password complexity was set on the + * primary {@link DevicePolicyManager} instance. */ @Deprecated public void setPasswordQuality(@NonNull ComponentName admin, int quality) { @@ -4055,10 +4062,18 @@ public class DevicePolicyManager { * <p><strong>Note:</strong> Specifying password requirements using this method clears any * password requirements set using the obsolete {@link #setPasswordQuality(ComponentName, int)} * and any of its associated methods. + * Additionally, if there are password requirements set using the obsolete + * {@link #setPasswordQuality(ComponentName, int)} on the parent {@code DevicePolicyManager} + * instance, they must be cleared by calling {@link #setPasswordQuality(ComponentName, int)} + * with {@link #PASSWORD_QUALITY_UNSPECIFIED} on that instance prior to setting complexity + * requirement for the managed profile. * * @throws SecurityException if the calling application is not a device owner or a profile * owner. * @throws IllegalArgumentException if the complexity level is not one of the four above. + * @throws IllegalStateException if the caller is trying to set password complexity while there + * are password requirements specified using {@link #setPasswordQuality(ComponentName, int)} + * on the parent {@code DevicePolicyManager} instance. */ public void setRequiredPasswordComplexity(@PasswordComplexity int passwordComplexity) { if (mService == null) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 4654fca250a3..c7d6d5dca973 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -3432,6 +3432,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { synchronized (getLockObject()) { ActiveAdmin ap = getActiveAdminForCallerLocked( who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent); + + // If setPasswordQuality is called on the parent, ensure that + // the primary admin does not have password complexity state (this is an + // unsupported state). + if (parent) { + final ActiveAdmin primaryAdmin = getActiveAdminForCallerLocked( + who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, false); + final boolean hasComplexitySet = + primaryAdmin.mPasswordComplexity != PASSWORD_COMPLEXITY_NONE; + Preconditions.checkState(!hasComplexitySet, + "Cannot set password quality when complexity is set on the primary admin." + + " Set the primary admin's complexity to NONE first."); + } mInjector.binderWithCleanCallingIdentity(() -> { final PasswordPolicy passwordPolicy = ap.mPasswordPolicy; if (passwordPolicy.quality != quality) { @@ -4397,6 +4410,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final ActiveAdmin admin = getParentOfAdminIfRequired( getProfileOwnerOrDeviceOwnerLocked(caller), calledOnParent); if (admin.mPasswordComplexity != passwordComplexity) { + // We require the caller to explicitly clear any password quality requirements set + // on the parent DPM instance, to avoid the case where password requirements are + // specified in the form of quality on the parent but complexity on the profile + // itself. + if (!calledOnParent) { + final boolean hasQualityRequirementsOnParent = admin.hasParentActiveAdmin() + && admin.getParentActiveAdmin().mPasswordPolicy.quality + != PASSWORD_QUALITY_UNSPECIFIED; + Preconditions.checkState(!hasQualityRequirementsOnParent, + "Password quality is set on the parent when attempting to set password" + + "complexity. Clear the quality by setting the password quality " + + "on the parent to PASSWORD_QUALITY_UNSPECIFIED first"); + } + mInjector.binderWithCleanCallingIdentity(() -> { admin.mPasswordComplexity = passwordComplexity; // Reset the password policy. diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 39fa20e4153f..a455ba90ccfe 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -6918,6 +6918,35 @@ public class DevicePolicyManagerTest extends DpmTestBase { DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); } + @Test + public void testSetRequiredPasswordComplexityFailsWithQualityOnParent() throws Exception { + final int managedProfileUserId = CALLER_USER_HANDLE; + final int managedProfileAdminUid = + UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID); + mContext.binder.callingUid = managedProfileAdminUid; + addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R); + + parentDpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); + + assertThrows(IllegalStateException.class, + () -> dpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_HIGH)); + } + + @Test + public void testSetQualityOnParentFailsWithComplexityOnProfile() throws Exception { + final int managedProfileUserId = CALLER_USER_HANDLE; + final int managedProfileAdminUid = + UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID); + mContext.binder.callingUid = managedProfileAdminUid; + addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R); + + dpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_HIGH); + + assertThrows(IllegalStateException.class, + () -> parentDpm.setPasswordQuality(admin1, + DevicePolicyManager.PASSWORD_QUALITY_COMPLEX)); + } + private void setUserUnlocked(int userHandle, boolean unlocked) { when(getServices().userManager.isUserUnlocked(eq(userHandle))).thenReturn(unlocked); } |