Merge "Enforce profile password quality policy on unified device lock" into sc-dev

This commit is contained in:
TreeHugger Robot
2021-07-01 10:30:59 +00:00
committed by Android (Google) Code Review
3 changed files with 204 additions and 28 deletions

View File

@@ -104,8 +104,6 @@ class ActiveAdmin {
private static final String TAG_PASSWORD_HISTORY_LENGTH = "password-history-length";
private static final String TAG_MIN_PASSWORD_LENGTH = "min-password-length";
private static final String TAG_PASSWORD_QUALITY = "password-quality";
private static final String TAG_PASSWORD_QUALITY_APPLIES_TO_PARENT =
"password-quality-applies-parent";
private static final String TAG_POLICIES = "policies";
private static final String TAG_CROSS_PROFILE_WIDGET_PROVIDERS =
"cross-profile-widget-providers";
@@ -158,7 +156,6 @@ class ActiveAdmin {
@NonNull
PasswordPolicy mPasswordPolicy = new PasswordPolicy();
boolean mPasswordPolicyAppliesToParent = true;
@DevicePolicyManager.PasswordComplexity
int mPasswordComplexity = PASSWORD_COMPLEXITY_NONE;
@@ -363,9 +360,6 @@ class ActiveAdmin {
writeAttributeValueToXml(
out, TAG_MIN_PASSWORD_NONLETTER, mPasswordPolicy.nonLetter);
}
writeAttributeValueToXml(out, TAG_PASSWORD_QUALITY_APPLIES_TO_PARENT,
mPasswordPolicyAppliesToParent);
}
if (passwordHistoryLength != DEF_PASSWORD_HISTORY_LENGTH) {
writeAttributeValueToXml(
@@ -669,8 +663,6 @@ class ActiveAdmin {
mPasswordPolicy.symbols = parser.getAttributeInt(null, ATTR_VALUE);
} else if (TAG_MIN_PASSWORD_NONLETTER.equals(tag)) {
mPasswordPolicy.nonLetter = parser.getAttributeInt(null, ATTR_VALUE);
} else if (TAG_PASSWORD_QUALITY_APPLIES_TO_PARENT.equals(tag)) {
mPasswordPolicyAppliesToParent = parser.getAttributeBoolean(null, ATTR_VALUE);
} else if (TAG_MAX_TIME_TO_UNLOCK.equals(tag)) {
maximumTimeToUnlock = parser.getAttributeLong(null, ATTR_VALUE);
} else if (TAG_STRONG_AUTH_UNLOCK_TIMEOUT.equals(tag)) {
@@ -1036,9 +1028,6 @@ class ActiveAdmin {
pw.print("minimumPasswordNonLetter=");
pw.println(mPasswordPolicy.nonLetter);
pw.print("passwordPolicyAppliesToParent=");
pw.println(mPasswordPolicyAppliesToParent);
pw.print("maximumTimeToUnlock=");
pw.println(maximumTimeToUnlock);

View File

@@ -3827,10 +3827,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
isProfileOwner(caller) || isDeviceOwner(caller) || isSystemUid(caller)
|| isPasswordLimitingAdminTargetingP(caller));
final boolean qualityMayApplyToParent =
canSetPasswordQualityOnParent(who.getPackageName(), caller);
if (!qualityMayApplyToParent) {
Preconditions.checkCallAuthorization(!parent,
if (parent) {
Preconditions.checkCallAuthorization(
canSetPasswordQualityOnParent(who.getPackageName(), caller),
"Profile Owner may not apply password quality requirements device-wide");
}
@@ -3856,7 +3855,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (passwordPolicy.quality != quality) {
passwordPolicy.quality = quality;
ap.mPasswordComplexity = PASSWORD_COMPLEXITY_NONE;
ap.mPasswordPolicyAppliesToParent = qualityMayApplyToParent;
resetInactivePasswordRequirementsIfRPlus(userId, ap);
updatePasswordValidityCheckpointLocked(userId, parent);
updatePasswordQualityCacheForUserGroup(userId);
@@ -4588,8 +4586,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
ArrayList<PasswordMetrics> adminMetrics = new ArrayList<>();
final List<ActiveAdmin> admins;
synchronized (getLockObject()) {
final List<ActiveAdmin> admins;
if (deviceWideOnly) {
admins = getActiveAdminsForUserAndItsManagedProfilesLocked(userId,
/* shouldIncludeProfileAdmins */ (user) -> false);
@@ -4597,16 +4595,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
admins = getActiveAdminsForLockscreenPoliciesLocked(userId);
}
for (ActiveAdmin admin : admins) {
final boolean isAdminOfUser = userId == admin.getUserHandle().getIdentifier();
// Use the password metrics from the admin in one of three cases:
// (1) The admin is of the user we're getting the minimum metrics for. The admin
// always affects the user it's managing. This applies also to the parent
// ActiveAdmin instance: It'd have the same user handle.
// (2) The mPasswordPolicyAppliesToParent field is true: That indicates the
// call to setPasswordQuality was made by an admin that may affect the parent.
if (isAdminOfUser || admin.mPasswordPolicyAppliesToParent) {
adminMetrics.add(admin.mPasswordPolicy.getMinMetrics());
}
adminMetrics.add(admin.mPasswordPolicy.getMinMetrics());
}
}
return PasswordMetrics.merge(adminMetrics);
@@ -4821,7 +4810,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
admin.mPasswordComplexity = passwordComplexity;
// Reset the password policy.
admin.mPasswordPolicy = new PasswordPolicy();
admin.mPasswordPolicyAppliesToParent = true;
updatePasswordValidityCheckpointLocked(caller.getUserId(), calledOnParent);
updatePasswordQualityCacheForUserGroup(caller.getUserId());
saveSettingsLocked(caller.getUserId());

View File

@@ -5427,6 +5427,205 @@ public class DevicePolicyManagerTest extends DpmTestBase {
}
}
@Test
public void isActivePasswordSufficient_SeparateWorkChallenge_ProfileQualityRequirementMet()
throws Exception {
// Create work profile with empty separate challenge
final int managedProfileUserId = 15;
final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
addManagedProfileForPasswordTests(managedProfileUserId, managedProfileAdminUid,
/* separateChallenge */ true);
// Set profile password quality requirement. No password added yet so
// profile.isActivePasswordSufficient should return false
mContext.binder.callingUid = managedProfileAdminUid;
dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC);
assertThat(dpm.isActivePasswordSufficient()).isFalse();
assertThat(parentDpm.isActivePasswordSufficient()).isTrue();
// Set a work challenge and verify profile.isActivePasswordSufficient is now true
when(getServices().lockSettingsInternal.getUserPasswordMetrics(managedProfileUserId))
.thenReturn(computeForPasswordOrPin("abcdXYZ5".getBytes(), /* isPin */ false));
assertThat(dpm.isActivePasswordSufficient()).isTrue();
assertThat(parentDpm.isActivePasswordSufficient()).isTrue();
}
@Test
public void isActivePasswordSufficient_SeparateWorkChallenge_ProfileComplexityRequirementMet()
throws Exception {
// Create work profile with empty separate challenge
final int managedProfileUserId = 15;
final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
addManagedProfileForPasswordTests(managedProfileUserId, managedProfileAdminUid,
/* separateChallenge */ true);
// Set profile password complexity requirement. No password added yet so
// profile.isActivePasswordSufficient should return false
mContext.binder.callingUid = managedProfileAdminUid;
dpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_MEDIUM);
assertThat(dpm.isActivePasswordSufficient()).isFalse();
assertThat(parentDpm.isActivePasswordSufficient()).isTrue();
// Set a work challenge and verify profile.isActivePasswordSufficient is now true
when(getServices().lockSettingsInternal.getUserPasswordMetrics(managedProfileUserId))
.thenReturn(computeForPasswordOrPin("5156".getBytes(), /* isPin */ true));
assertThat(dpm.isActivePasswordSufficient()).isTrue();
assertThat(parentDpm.isActivePasswordSufficient()).isTrue();
}
@Test
public void isActivePasswordSufficient_SeparateWorkChallenge_ParentQualityRequirementMet()
throws Exception {
// Create work profile with empty separate challenge
final int managedProfileUserId = 15;
final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
addManagedProfileForPasswordTests(managedProfileUserId, managedProfileAdminUid,
/* separateChallenge */ true);
// Set parent password quality requirement. No password added yet so
// parent.isActivePasswordSufficient should return false
mContext.binder.callingUid = managedProfileAdminUid;
parentDpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX);
assertThat(dpm.isActivePasswordSufficient()).isTrue();
assertThat(parentDpm.isActivePasswordSufficient()).isFalse();
// Set a device lockscreen and verify parent.isActivePasswordSufficient is now true
when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM))
.thenReturn(computeForPasswordOrPin("acbdXYZ5".getBytes(), /* isPin */ false));
assertThat(dpm.isActivePasswordSufficient()).isTrue();
assertThat(parentDpm.isActivePasswordSufficient()).isTrue();
}
@Test
public void isActivePasswordSufficient_SeparateWorkChallenge_ParentComplexityRequirementMet()
throws Exception {
// Create work profile with empty separate challenge
final int managedProfileUserId = 15;
final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
addManagedProfileForPasswordTests(managedProfileUserId, managedProfileAdminUid,
/* separateChallenge */ true);
// Set parent password complexity requirement. No password added yet so
// parent.isActivePasswordSufficient should return false
mContext.binder.callingUid = managedProfileAdminUid;
parentDpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_LOW);
assertThat(dpm.isActivePasswordSufficient()).isTrue();
assertThat(parentDpm.isActivePasswordSufficient()).isFalse();
// Set a device lockscreen and verify parent.isActivePasswordSufficient is now true
when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM))
.thenReturn(computeForPasswordOrPin("1234".getBytes(), /* isPin */ true));
assertThat(dpm.isActivePasswordSufficient()).isTrue();
assertThat(parentDpm.isActivePasswordSufficient()).isTrue();
}
@Test
public void isActivePasswordSufficient_UnifiedWorkChallenge_ProfileQualityRequirementMet()
throws Exception {
// Create work profile with unified challenge
final int managedProfileUserId = 15;
final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
addManagedProfileForPasswordTests(managedProfileUserId, managedProfileAdminUid,
/* separateChallenge */ false);
// Set profile password quality requirement. No password added yet so
// {profile, parent}.isActivePasswordSufficient should return false
mContext.binder.callingUid = managedProfileAdminUid;
dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC);
assertThat(dpm.isActivePasswordSufficient()).isFalse();
assertThat(parentDpm.isActivePasswordSufficient()).isFalse();
// Set a device lockscreen and verify {profile, parent}.isActivePasswordSufficient is true
when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM))
.thenReturn(computeForPasswordOrPin("abcdXYZ5".getBytes(), /* isPin */ false));
assertThat(dpm.isActivePasswordSufficient()).isTrue();
assertThat(parentDpm.isActivePasswordSufficient()).isTrue();
}
@Test
public void isActivePasswordSufficient_UnifiedWorkChallenge_ProfileComplexityRequirementMet()
throws Exception {
// Create work profile with unified challenge
final int managedProfileUserId = 15;
final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
addManagedProfileForPasswordTests(managedProfileUserId, managedProfileAdminUid,
/* separateChallenge */ false);
// Set profile password complexity requirement. No password added yet so
// {profile, parent}.isActivePasswordSufficient should return false
mContext.binder.callingUid = managedProfileAdminUid;
dpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_HIGH);
assertThat(dpm.isActivePasswordSufficient()).isFalse();
assertThat(parentDpm.isActivePasswordSufficient()).isFalse();
// Set a device lockscreen and verify {profile, parent}.isActivePasswordSufficient is true
when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM))
.thenReturn(computeForPasswordOrPin("51567548".getBytes(), /* isPin */ true));
assertThat(dpm.isActivePasswordSufficient()).isTrue();
assertThat(parentDpm.isActivePasswordSufficient()).isTrue();
}
@Test
public void isActivePasswordSufficient_UnifiedWorkChallenge_ParentQualityRequirementMet()
throws Exception {
// Create work profile with unified challenge
final int managedProfileUserId = 15;
final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
addManagedProfileForPasswordTests(managedProfileUserId, managedProfileAdminUid,
/* separateChallenge */ false);
// Set parent password quality requirement. No password added yet so
// {profile, parent}.isActivePasswordSufficient should return false
mContext.binder.callingUid = managedProfileAdminUid;
parentDpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC);
assertThat(dpm.isActivePasswordSufficient()).isFalse();
assertThat(parentDpm.isActivePasswordSufficient()).isFalse();
// Set a device lockscreen and verify {profile, parent}.isActivePasswordSufficient is true
when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM))
.thenReturn(computeForPasswordOrPin("abcdXYZ5".getBytes(), /* isPin */ false));
assertThat(dpm.isActivePasswordSufficient()).isTrue();
assertThat(parentDpm.isActivePasswordSufficient()).isTrue();
}
@Test
public void isActivePasswordSufficient_UnifiedWorkChallenge_ParentComplexityRequirementMet()
throws Exception {
// Create work profile with unified challenge
final int managedProfileUserId = 15;
final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
addManagedProfileForPasswordTests(managedProfileUserId, managedProfileAdminUid,
/* separateChallenge */ false);
// Set parent password complexity requirement. No password added yet so
// {profile, parent}.isActivePasswordSufficient should return false
mContext.binder.callingUid = managedProfileAdminUid;
parentDpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_MEDIUM);
assertThat(dpm.isActivePasswordSufficient()).isFalse();
assertThat(parentDpm.isActivePasswordSufficient()).isFalse();
// Set a device lockscreen and verify {profile, parent}.isActivePasswordSufficient is true
when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM))
.thenReturn(computeForPasswordOrPin("5156".getBytes(), /* isPin */ true));
assertThat(dpm.isActivePasswordSufficient()).isTrue();
assertThat(parentDpm.isActivePasswordSufficient()).isTrue();
}
private void addManagedProfileForPasswordTests(int userId, int adminUid,
boolean separateChallenge) throws Exception {
addManagedProfile(admin1, adminUid, admin1);
when(getServices().userManager.getProfileParent(userId))
.thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
doReturn(separateChallenge).when(getServices().lockPatternUtils)
.isSeparateProfileChallengeEnabled(userId);
when(getServices().userManager.getCredentialOwnerProfile(userId))
.thenReturn(separateChallenge ? userId : UserHandle.USER_SYSTEM);
when(getServices().lockSettingsInternal.getUserPasswordMetrics(userId))
.thenReturn(new PasswordMetrics(CREDENTIAL_TYPE_NONE));
when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM))
.thenReturn(new PasswordMetrics(CREDENTIAL_TYPE_NONE));
}
@Test
public void testPasswordQualityAppliesToParentPreS() throws Exception {
final int managedProfileUserId = CALLER_USER_HANDLE;