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

Commit 26fccb7d authored by Pavel Grafov's avatar Pavel Grafov
Browse files

Throw if admin sets password constraints that have no effect.

For admin apps targeting R+, throw when the app sets password requirement
that is not taken into account at  given quality, e.g. when quality is set
to UNSPECIFIED, it doesn't make sense to require certain password length.
If the intent is to require a password of certain length having at least
NUMERIC quality, the admin should first call setPasswordQuality() and only
then call setPasswordMinimumLength().

Conversely when an admin targeting R+ lowers password quality, those
requiremnts that stop making sense, are reset to default values.

+ fix the behaviour of getPasswordMinimumLength to match the docs: only
  admins with password quality >= NUMERIC should be taken into account.

Test: com.android.cts.devicepolicy..MixedDeviceOwnerTest#testResetPasswordWithToken
Test: com.android.cts.devicepolicy.DeviceAdminHostSideTestApi23#testRunDeviceOwnerPasswordTest
Test: com.android.cts.devicepolicy.MixedDeviceOwnerTestApi#testPasswordRequirementsApi
Test: com.android.cts.devicepolicy.MixedDeviceOwnerTestApi25#testPasswordRequirementsApi
Bug: 123562444
Change-Id: Id134a7918718e3b0a220caaf6c672df4238a062c
parent e75b4a7d
Loading
Loading
Loading
Loading
+51 −9
Original line number Diff line number Diff line
@@ -2669,7 +2669,10 @@ public class DevicePolicyManager {
     * only imposed if the administrator has also requested either {@link #PASSWORD_QUALITY_NUMERIC}
     * , {@link #PASSWORD_QUALITY_NUMERIC_COMPLEX}, {@link #PASSWORD_QUALITY_ALPHABETIC},
     * {@link #PASSWORD_QUALITY_ALPHANUMERIC}, or {@link #PASSWORD_QUALITY_COMPLEX} with
     * {@link #setPasswordQuality}.
     * {@link #setPasswordQuality}. If an app targeting SDK level
     * {@link android.os.Build.VERSION_CODES#R} and above enforces this constraint without settings
     * password quality to one of these values first, this method will throw
     * {@link IllegalStateException}.
     * <p>
     * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
     * password is always treated as empty.
@@ -2687,6 +2690,9 @@ public class DevicePolicyManager {
     *     restriction.
     * @throws SecurityException if {@code admin} is not an active administrator or {@code admin}
     *     does not use {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD}
     * @throws IllegalStateException if the calling app is targeting SDK level
     *     {@link android.os.Build.VERSION_CODES#R} and above and didn't set a sufficient password
     *     quality requirement prior to calling this method.
     */
    public void setPasswordMinimumLength(@NonNull ComponentName admin, int length) {
        if (mService != null) {
@@ -2738,7 +2744,10 @@ public class DevicePolicyManager {
     * place immediately. To prompt the user for a new password, use
     * {@link #ACTION_SET_NEW_PASSWORD} or {@link #ACTION_SET_NEW_PARENT_PROFILE_PASSWORD} after
     * setting this value. This constraint is only imposed if the administrator has also requested
     * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The default value is 0.
     * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. If an app targeting
     * SDK level {@link android.os.Build.VERSION_CODES#R} and above enforces this constraint without
     * settings password quality to {@link #PASSWORD_QUALITY_COMPLEX} first, this method will throw
     * {@link IllegalStateException}. The default value is 0.
     * <p>
     * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
     * password is always treated as empty.
@@ -2756,6 +2765,9 @@ public class DevicePolicyManager {
     *            A value of 0 means there is no restriction.
     * @throws SecurityException if {@code admin} is not an active administrator or {@code admin}
     *             does not use {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD}
     * @throws IllegalStateException if the calling app is targeting SDK level
     *     {@link android.os.Build.VERSION_CODES#R} and above and didn't set a sufficient password
     *     quality requirement prior to calling this method.
     */
    public void setPasswordMinimumUpperCase(@NonNull ComponentName admin, int length) {
        if (mService != null) {
@@ -2814,7 +2826,10 @@ public class DevicePolicyManager {
     * place immediately. To prompt the user for a new password, use
     * {@link #ACTION_SET_NEW_PASSWORD} or {@link #ACTION_SET_NEW_PARENT_PROFILE_PASSWORD} after
     * setting this value. This constraint is only imposed if the administrator has also requested
     * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The default value is 0.
     * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. If an app targeting
     * SDK level {@link android.os.Build.VERSION_CODES#R} and above enforces this constraint without
     * settings password quality to {@link #PASSWORD_QUALITY_COMPLEX} first, this method will throw
     * {@link IllegalStateException}. The default value is 0.
     * <p>
     * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
     * password is always treated as empty.
@@ -2832,6 +2847,9 @@ public class DevicePolicyManager {
     *            A value of 0 means there is no restriction.
     * @throws SecurityException if {@code admin} is not an active administrator or {@code admin}
     *             does not use {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD}
     * @throws IllegalStateException if the calling app is targeting SDK level
     *     {@link android.os.Build.VERSION_CODES#R} and above and didn't set a sufficient password
     *     quality requirement prior to calling this method.
     */
    public void setPasswordMinimumLowerCase(@NonNull ComponentName admin, int length) {
        if (mService != null) {
@@ -2890,7 +2908,10 @@ public class DevicePolicyManager {
     * immediately. To prompt the user for a new password, use {@link #ACTION_SET_NEW_PASSWORD} or
     * {@link #ACTION_SET_NEW_PARENT_PROFILE_PASSWORD} after setting this value. This constraint is
     * only imposed if the administrator has also requested {@link #PASSWORD_QUALITY_COMPLEX} with
     * {@link #setPasswordQuality}. The default value is 1.
     * {@link #setPasswordQuality}. If an app targeting SDK level
     * {@link android.os.Build.VERSION_CODES#R} and above enforces this constraint without settings
     * password quality to {@link #PASSWORD_QUALITY_COMPLEX} first, this method will throw
     * {@link IllegalStateException}. The default value is 1.
     * <p>
     * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
     * password is always treated as empty.
@@ -2908,6 +2929,9 @@ public class DevicePolicyManager {
     *            0 means there is no restriction.
     * @throws SecurityException if {@code admin} is not an active administrator or {@code admin}
     *             does not use {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD}
     * @throws IllegalStateException if the calling app is targeting SDK level
     *     {@link android.os.Build.VERSION_CODES#R} and above and didn't set a sufficient password
     *     quality requirement prior to calling this method.
     */
    public void setPasswordMinimumLetters(@NonNull ComponentName admin, int length) {
        if (mService != null) {
@@ -2965,7 +2989,10 @@ public class DevicePolicyManager {
     * place immediately. To prompt the user for a new password, use
     * {@link #ACTION_SET_NEW_PASSWORD} or {@link #ACTION_SET_NEW_PARENT_PROFILE_PASSWORD} after
     * setting this value. This constraint is only imposed if the administrator has also requested
     * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The default value is 1.
     * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. If an app targeting
     * SDK level {@link android.os.Build.VERSION_CODES#R} and above enforces this constraint without
     * settings password quality to {@link #PASSWORD_QUALITY_COMPLEX} first, this method will throw
     * {@link IllegalStateException}. The default value is 1.
     * <p>
     * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
     * password is always treated as empty.
@@ -2983,6 +3010,9 @@ public class DevicePolicyManager {
     *            value of 0 means there is no restriction.
     * @throws SecurityException if {@code admin} is not an active administrator or {@code admin}
     *             does not use {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD}
     * @throws IllegalStateException if the calling app is targeting SDK level
     *     {@link android.os.Build.VERSION_CODES#R} and above and didn't set a sufficient password
     *     quality requirement prior to calling this method.
     */
    public void setPasswordMinimumNumeric(@NonNull ComponentName admin, int length) {
        if (mService != null) {
@@ -3040,7 +3070,10 @@ public class DevicePolicyManager {
     * immediately. To prompt the user for a new password, use {@link #ACTION_SET_NEW_PASSWORD} or
     * {@link #ACTION_SET_NEW_PARENT_PROFILE_PASSWORD} after setting this value. This constraint is
     * only imposed if the administrator has also requested {@link #PASSWORD_QUALITY_COMPLEX} with
     * {@link #setPasswordQuality}. The default value is 1.
     * {@link #setPasswordQuality}. If an app targeting SDK level
     * {@link android.os.Build.VERSION_CODES#R} and above enforces this constraint without settings
     * password quality to {@link #PASSWORD_QUALITY_COMPLEX} first, this method will throw
     * {@link IllegalStateException}. The default value is 1.
     * <p>
     * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
     * password is always treated as empty.
@@ -3058,6 +3091,9 @@ public class DevicePolicyManager {
     *            0 means there is no restriction.
     * @throws SecurityException if {@code admin} is not an active administrator or {@code admin}
     *             does not use {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD}
     * @throws IllegalStateException if the calling app is targeting SDK level
     *     {@link android.os.Build.VERSION_CODES#R} and above and didn't set a sufficient password
     *     quality requirement prior to calling this method.
     */
    public void setPasswordMinimumSymbols(@NonNull ComponentName admin, int length) {
        if (mService != null) {
@@ -3114,7 +3150,10 @@ public class DevicePolicyManager {
     * one, so the change does not take place immediately. To prompt the user for a new password,
     * use {@link #ACTION_SET_NEW_PASSWORD} or {@link #ACTION_SET_NEW_PARENT_PROFILE_PASSWORD} after
     * setting this value. This constraint is only imposed if the administrator has also requested
     * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The default value is 0.
     * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. If an app targeting
     * SDK level {@link android.os.Build.VERSION_CODES#R} and above enforces this constraint without
     * settings password quality to {@link #PASSWORD_QUALITY_COMPLEX} first, this method will throw
     * {@link IllegalStateException}. The default value is 0.
     * <p>
     * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
     * password is always treated as empty.
@@ -3132,6 +3171,9 @@ public class DevicePolicyManager {
     *            0 means there is no restriction.
     * @throws SecurityException if {@code admin} is not an active administrator or {@code admin}
     *             does not use {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD}
     * @throws IllegalStateException if the calling app is targeting SDK level
     *     {@link android.os.Build.VERSION_CODES#R} and above and didn't set a sufficient password
     *     quality requirement prior to calling this method.
     */
    public void setPasswordMinimumNonLetter(@NonNull ComponentName admin, int length) {
        if (mService != null) {
+42 −1
Original line number Diff line number Diff line
@@ -4132,6 +4132,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
                final PasswordMetrics metrics = ap.minimumPasswordMetrics;
                if (metrics.quality != quality) {
                    metrics.quality = quality;
                    resetInactivePasswordRequirementsIfRPlus(userId, ap);
                    updatePasswordValidityCheckpointLocked(userId, parent);
                    updatePasswordQualityCacheForUserGroup(userId);
                    saveSettingsLocked(userId);
@@ -4149,6 +4150,27 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
                .write();
    }
    /**
     * For admins targeting R+ reset various password constraints to default values when quality is
     * set to a value that makes those constraints that have no effect.
     */
    private void resetInactivePasswordRequirementsIfRPlus(int userId, ActiveAdmin admin) {
        if (getTargetSdk(admin.info.getPackageName(), userId) > Build.VERSION_CODES.Q) {
            final PasswordMetrics metrics = admin.minimumPasswordMetrics;
            if (metrics.quality < PASSWORD_QUALITY_NUMERIC) {
                metrics.length = ActiveAdmin.DEF_MINIMUM_PASSWORD_LENGTH;
            }
            if (metrics.quality < PASSWORD_QUALITY_COMPLEX) {
                metrics.letters = ActiveAdmin.DEF_MINIMUM_PASSWORD_LETTERS;
                metrics.upperCase = ActiveAdmin.DEF_MINIMUM_PASSWORD_UPPER_CASE;
                metrics.lowerCase = ActiveAdmin.DEF_MINIMUM_PASSWORD_LOWER_CASE;
                metrics.numeric = ActiveAdmin.DEF_MINIMUM_PASSWORD_NUMERIC;
                metrics.symbols = ActiveAdmin.DEF_MINIMUM_PASSWORD_SYMBOLS;
                metrics.nonLetter = ActiveAdmin.DEF_MINIMUM_PASSWORD_NON_LETTER;
            }
        }
    }
    /**
     * Updates a flag that tells us whether the user's password currently satisfies the
     * requirements set by all of the user's active admins. The flag is updated both in memory
@@ -4280,6 +4302,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
            ActiveAdmin ap = getActiveAdminForCallerLocked(
                    who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
            final PasswordMetrics metrics = ap.minimumPasswordMetrics;
            ensureMinimumQuality(userId, ap, PASSWORD_QUALITY_NUMERIC, "setPasswordMinimumLength");
            if (metrics.length != length) {
                metrics.length = length;
                updatePasswordValidityCheckpointLocked(userId, parent);
@@ -4294,10 +4317,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
                .write();
    }
    private void ensureMinimumQuality(
            int userId, ActiveAdmin admin, int minimumQuality, String operation) {
        if (admin.minimumPasswordMetrics.quality < minimumQuality
                && getTargetSdk(admin.info.getPackageName(), userId) > Build.VERSION_CODES.Q) {
            throw new IllegalStateException(String.format(
                    "password quality should be at least %d for %s", minimumQuality, operation));
        }
    }
    @Override
    public int getPasswordMinimumLength(ComponentName who, int userHandle, boolean parent) {
        return getStrictestPasswordRequirement(who, userHandle, parent,
                admin -> admin.minimumPasswordMetrics.length, PASSWORD_QUALITY_UNSPECIFIED);
                admin -> admin.minimumPasswordMetrics.length, PASSWORD_QUALITY_NUMERIC);
    }
    @Override
@@ -4524,6 +4556,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        synchronized (getLockObject()) {
            final ActiveAdmin ap = getActiveAdminForCallerLocked(
                    who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
            ensureMinimumQuality(
                    userId, ap, PASSWORD_QUALITY_COMPLEX, "setPasswordMinimumUpperCase");
            final PasswordMetrics metrics = ap.minimumPasswordMetrics;
            if (metrics.upperCase != length) {
                metrics.upperCase = length;
@@ -4552,6 +4586,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        synchronized (getLockObject()) {
            ActiveAdmin ap = getActiveAdminForCallerLocked(
                    who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
            ensureMinimumQuality(
                    userId, ap, PASSWORD_QUALITY_COMPLEX, "setPasswordMinimumLowerCase");
            final PasswordMetrics metrics = ap.minimumPasswordMetrics;
            if (metrics.lowerCase != length) {
                metrics.lowerCase = length;
@@ -4583,6 +4619,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        synchronized (getLockObject()) {
            ActiveAdmin ap = getActiveAdminForCallerLocked(
                    who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
            ensureMinimumQuality(userId, ap, PASSWORD_QUALITY_COMPLEX, "setPasswordMinimumLetters");
            final PasswordMetrics metrics = ap.minimumPasswordMetrics;
            if (metrics.letters != length) {
                metrics.letters = length;
@@ -4614,6 +4651,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        synchronized (getLockObject()) {
            ActiveAdmin ap = getActiveAdminForCallerLocked(
                    who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
            ensureMinimumQuality(userId, ap, PASSWORD_QUALITY_COMPLEX, "setPasswordMinimumNumeric");
            final PasswordMetrics metrics = ap.minimumPasswordMetrics;
            if (metrics.numeric != length) {
                metrics.numeric = length;
@@ -4645,6 +4683,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        synchronized (getLockObject()) {
            ActiveAdmin ap = getActiveAdminForCallerLocked(
                    who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
            ensureMinimumQuality(userId, ap, PASSWORD_QUALITY_COMPLEX, "setPasswordMinimumSymbols");
            final PasswordMetrics metrics = ap.minimumPasswordMetrics;
            if (metrics.symbols != length) {
                ap.minimumPasswordMetrics.symbols = length;
@@ -4676,6 +4715,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        synchronized (getLockObject()) {
            ActiveAdmin ap = getActiveAdminForCallerLocked(
                    who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
            ensureMinimumQuality(
                    userId, ap, PASSWORD_QUALITY_COMPLEX, "setPasswordMinimumNonLetter");
            final PasswordMetrics metrics = ap.minimumPasswordMetrics;
            if (metrics.nonLetter != length) {
                ap.minimumPasswordMetrics.nonLetter = length;