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

Commit 3255823d authored by Konstantin Lopyrev's avatar Konstantin Lopyrev
Browse files

Fix 2673731: Adding support for password history to Device Admin.

Change-Id: If3240048813e32b2bae79fe5cb8a73aea20ec56c
parent e46c1e81
Loading
Loading
Loading
Loading
+28 −0
Original line number Diff line number Diff line
@@ -31730,6 +31730,19 @@
<parameter name="admin" type="android.content.ComponentName">
</parameter>
</method>
<method name="getPasswordHistoryLength"
 return="int"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="admin" type="android.content.ComponentName">
</parameter>
</method>
<method name="getPasswordMaximumLength"
 return="int"
 abstract="false"
@@ -31862,6 +31875,21 @@
<parameter name="timeMs" type="long">
</parameter>
</method>
<method name="setPasswordHistoryLength"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="admin" type="android.content.ComponentName">
</parameter>
<parameter name="length" type="int">
</parameter>
</method>
<method name="setPasswordMinimumLength"
 return="void"
 abstract="false"
+112 −63
Original line number Diff line number Diff line
@@ -93,15 +93,14 @@ public class DevicePolicyManager {
    public static final String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION";

    /**
     * Activity action: have the user enter a new password.  This activity
     * should be launched after using {@link #setPasswordQuality(ComponentName, int)}
     * or {@link #setPasswordMinimumLength(ComponentName, int)} to have the
     * user enter a new password that meets the current requirements.  You can
     * use {@link #isActivePasswordSufficient()} to determine whether you need
     * to have the user select a new password in order to meet the current
     * constraints.  Upon being resumed from this activity,
     * you can check the new password characteristics to see if they are
     * sufficient.
     * Activity action: have the user enter a new password. This activity should
     * be launched after using {@link #setPasswordQuality(ComponentName, int)},
     * or {@link #setPasswordMinimumLength(ComponentName, int)} to have the user
     * enter a new password that meets the current requirements. You can use
     * {@link #isActivePasswordSufficient()} to determine whether you need to
     * have the user select a new password in order to meet the current
     * constraints. Upon being resumed from this activity, you can check the new
     * password characteristics to see if they are sufficient.
     */
    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
    public static final String ACTION_SET_NEW_PASSWORD
@@ -304,6 +303,56 @@ public class DevicePolicyManager {
        return 0;
    }

  /**
   * Called by an application that is administering the device to set the length
   * of the password history. After setting this, the user will not be able to
   * enter a new password that is the same as any password in the history. Note
   * that the current password will remain until the user has set a new one, so
   * the change does not take place immediately. To prompt the user for a new
   * password, use {@link #ACTION_SET_NEW_PASSWORD} after setting this value.
   * This constraint is only imposed if the administrator has also requested
   * either {@link #PASSWORD_QUALITY_NUMERIC},
   * {@link #PASSWORD_QUALITY_ALPHABETIC}, or
   * {@link #PASSWORD_QUALITY_ALPHANUMERIC} with {@link #setPasswordQuality}.
   *
   * <p>
   * The calling device admin must have requested
   * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this
   * method; if it has not, a security exception will be thrown.
   *
   * @param admin Which {@link DeviceAdminReceiver} this request is associated
   *        with.
   * @param length The new desired length of password history. A value of 0
   *        means there is no restriction.
   */
    public void setPasswordHistoryLength(ComponentName admin, int length) {
        if (mService != null) {
            try {
                mService.setPasswordHistoryLength(admin, length);
            } catch (RemoteException e) {
                Log.w(TAG, "Failed talking with device policy service", e);
            }
        }
    }

    /**
     * Retrieve the current password history length for all admins
     * or a particular one.
     * @param admin The name of the admin component to check, or null to aggregate
     * all admins.
     * @return The length of the password history
     */
    public int getPasswordHistoryLength(ComponentName admin) {
        if (mService != null) {
            try {
                return mService.getPasswordHistoryLength(admin);
            } catch (RemoteException e) {
                Log.w(TAG, "Failed talking with device policy service", e);
            }
        }
        return 0;
    }

    /**
     * Return the maximum password length that the device supports for a
     * particular password quality.
+3 −0
Original line number Diff line number Diff line
@@ -31,6 +31,9 @@ interface IDevicePolicyManager {
    void setPasswordMinimumLength(in ComponentName who, int length);
    int getPasswordMinimumLength(in ComponentName who);
    
    void setPasswordHistoryLength(in ComponentName who, int length);
    int getPasswordHistoryLength(in ComponentName who);

    boolean isActivePasswordSufficient();
    int getCurrentFailedPasswordAttempts();
    
+30 −13
Original line number Diff line number Diff line
@@ -86,12 +86,6 @@ public class LockPatternUtils {
     */
    public static final int MIN_PATTERN_REGISTER_FAIL = 3;

    /**
     * The number of previous password hashes to store. This is used to prevent
     * the user from setting the same password as any of the stored ones.
     */
    public static final int MAX_PASSWORD_HISTORY_LENGTH = 5;

    private final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently";
    private final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline";
    private final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen";
@@ -146,6 +140,10 @@ public class LockPatternUtils {
        return getDevicePolicyManager().getPasswordQuality(null);
    }

    public int getRequestedPasswordHistoryLength() {
        return getDevicePolicyManager().getPasswordHistoryLength(null);
    }

    /**
     * Returns the actual password mode, as set by keyguard after updating the password.
     *
@@ -219,7 +217,21 @@ public class LockPatternUtils {
    public boolean checkPasswordHistory(String password) {
        String passwordHashString = new String(passwordToHash(password));
        String passwordHistory = getString(PASSWORD_HISTORY_KEY);
        return passwordHistory != null && passwordHistory.contains(passwordHashString);
        if (passwordHistory == null) {
            return false;
        }
        // Password History may be too long...
        int passwordHashLength = passwordHashString.length();
        int passwordHistoryLength = getRequestedPasswordHistoryLength();
        if(passwordHistoryLength == 0) {
            return false;
        }
        int neededPasswordHistoryLength = passwordHashLength * passwordHistoryLength
                + passwordHistoryLength - 1;
        if (passwordHistory.length() > neededPasswordHistoryLength) {
            passwordHistory = passwordHistory.substring(0, neededPasswordHistoryLength);
        }
        return passwordHistory.contains(passwordHashString);
    }

    /**
@@ -413,12 +425,17 @@ public class LockPatternUtils {
                if (passwordHistory == null) {
                    passwordHistory = new String();
                }
                int passwordHistoryLength = getRequestedPasswordHistoryLength();
                if (passwordHistoryLength == 0) {
                    passwordHistory = "";
                } else {
                    passwordHistory = new String(hash) + "," + passwordHistory;
                // Cut it to contain MAX_PASSWORD_HISTORY_LENGTH hashes
                // and MAX_PASSWORD_HISTORY_LENGTH -1 commas.
                    // Cut it to contain passwordHistoryLength hashes
                    // and passwordHistoryLength -1 commas.
                    passwordHistory = passwordHistory.substring(0, Math.min(hash.length
                        * MAX_PASSWORD_HISTORY_LENGTH + MAX_PASSWORD_HISTORY_LENGTH - 1,
                        passwordHistory.length()));
                            * passwordHistoryLength + passwordHistoryLength - 1, passwordHistory
                            .length()));
                }
                setString(PASSWORD_HISTORY_KEY, passwordHistory);
            } else {
                dpm.setActivePasswordState(
+128 −83
Original line number Diff line number Diff line
@@ -89,6 +89,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {

        int passwordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
        int minimumPasswordLength = 0;
        int passwordHistoryLength = 0;
        long maximumTimeToUnlock = 0;
        int maximumFailedPasswordsForWipe = 0;

@@ -110,7 +111,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                if (minimumPasswordLength > 0) {
                    out.startTag(null, "min-password-length");
                    out.attribute(null, "value", Integer.toString(minimumPasswordLength));
                    out.endTag(null, "mn-password-length");
                    out.endTag(null, "min-password-length");
                }
                if(passwordHistoryLength > 0) {
                    out.startTag(null, "password-history-length");
                    out.attribute(null, "value", Integer.toString(passwordHistoryLength));
                    out.endTag(null, "password-history-length");
                }
            }
            if (maximumTimeToUnlock != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
@@ -143,6 +149,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                } else if ("min-password-length".equals(tag)) {
                    minimumPasswordLength = Integer.parseInt(
                            parser.getAttributeValue(null, "value"));
                } else if ("password-history-length".equals(tag)) {
                    passwordHistoryLength = Integer.parseInt(
                            parser.getAttributeValue(null, "value"));
                } else if ("max-time-to-unlock".equals(tag)) {
                    maximumTimeToUnlock = Long.parseLong(
                            parser.getAttributeValue(null, "value"));
@@ -166,9 +175,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                }
            }
            pw.print(prefix); pw.print("passwordQuality=0x");
                    pw.print(Integer.toHexString(passwordQuality));
                    pw.print(" minimumPasswordLength=");
                    pw.println(Integer.toHexString(passwordQuality));
            pw.print(prefix); pw.print("minimumPasswordLength=");
                    pw.println(minimumPasswordLength);
            pw.print(prefix); pw.print("passwordHistoryLength=");
                    pw.println(passwordHistoryLength);
            pw.print(prefix); pw.print("maximumTimeToUnlock=");
                    pw.println(maximumTimeToUnlock);
            pw.print(prefix); pw.print("maximumFailedPasswordsForWipe=");
@@ -667,6 +678,40 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
        }
    }

    public void setPasswordHistoryLength(ComponentName who, int length) {
        synchronized (this) {
            if (who == null) {
                throw new NullPointerException("ComponentName is null");
            }
            ActiveAdmin ap = getActiveAdminForCallerLocked(who,
                    DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
            if (ap.passwordHistoryLength != length) {
                ap.passwordHistoryLength = length;
                saveSettingsLocked();
            }
        }
    }

    public int getPasswordHistoryLength(ComponentName who) {
        synchronized (this) {
            int length = 0;

            if (who != null) {
                ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
                return admin != null ? admin.passwordHistoryLength : length;
            }

            final int N = mAdminList.size();
            for (int i = 0; i < N; i++) {
                ActiveAdmin admin = mAdminList.get(i);
                if (length < admin.passwordHistoryLength) {
                    length = admin.passwordHistoryLength;
                }
            }
            return length;
        }
    }

    public boolean isActivePasswordSufficient() {
        synchronized (this) {
            // This API can only be called by an active device admin,