summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author TreeHugger Robot <treehugger-gerrit@google.com> 2021-01-12 18:35:49 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2021-01-12 18:35:49 +0000
commit7c6e169d826a68106002abd8276b32781c87ab05 (patch)
tree5a5add0cf4012d3fcfa00ffac4c76d768d017704
parentb078a29814842c75c32210667d384e0517097fc5 (diff)
parent7da93284ccb37b7b2ce662e81aaf944f430fb10f (diff)
Merge "DPM: Prevent mixing password quality and complexity"
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java15
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java27
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java29
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);
}