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

Commit 5daf273b authored by Andrew Scull's avatar Andrew Scull
Browse files

Don't save the password metrics to disk.

Only store the metrics in RAM, computing them at first log in.

Test: com.android.cts.devicepolicy.DeviceAdminHostSideTestApi24
Bug: 32793550
Change-Id: Iaf9516c193f054331e3e2c68cb3f627bd543b408
parent 9ed3a080
Loading
Loading
Loading
Loading
+15 −1
Original line number Diff line number Diff line
@@ -2239,7 +2239,7 @@ public class DevicePolicyManager {
     * Determine whether the current password the user has set is sufficient to meet the policy
     * requirements (e.g. quality, minimum length) that have been requested by the admins of this
     * user and its participating profiles. Restrictions on profiles that have a separate challenge
     * are not taken into account.
     * are not taken into account. The user must be unlocked in order to perform the check.
     * <p>
     * The calling device admin must have requested
     * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
@@ -2252,6 +2252,7 @@ public class DevicePolicyManager {
     * @return Returns true if the password meets the current requirements, else false.
     * @throws SecurityException if the calling application does not own an active administrator
     *             that uses {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD}
     * @throws InvalidStateException if the user is not unlocked.
     */
    public boolean isActivePasswordSufficient() {
        if (mService != null) {
@@ -3759,6 +3760,19 @@ public class DevicePolicyManager {
        }
    }

    /**
     * @hide
     */
    public void reportPasswordChanged(@UserIdInt int userId) {
        if (mService != null) {
            try {
                mService.reportPasswordChanged(userId);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
    }

    /**
     * @hide
     */
+1 −0
Original line number Diff line number Diff line
@@ -123,6 +123,7 @@ interface IDevicePolicyManager {
    boolean hasGrantedPolicy(in ComponentName policyReceiver, int usesPolicy, int userHandle);

    void setActivePasswordState(in PasswordMetrics metrics, int userHandle);
    void reportPasswordChanged(int userId);
    void reportFailedPasswordAttempt(int userHandle);
    void reportSuccessfulPasswordAttempt(int userHandle);
    void reportFailedFingerprintAttempt(int userHandle);
+3 −18
Original line number Diff line number Diff line
@@ -590,8 +590,6 @@ public class LockPatternUtils {
            setCredentialRequiredToDecrypt(false);
        }

        getDevicePolicyManager().setActivePasswordState(new PasswordMetrics(), userHandle);

        onAfterChangingPassword(userHandle);
    }

@@ -644,6 +642,7 @@ public class LockPatternUtils {
                        + MIN_LOCK_PATTERN_SIZE + " dots long.");
            }

            setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
            getLockSettings().setLockPattern(patternToString(pattern), savedPattern, userId);
            DevicePolicyManager dpm = getDevicePolicyManager();

@@ -659,10 +658,6 @@ public class LockPatternUtils {
            }

            setBoolean(PATTERN_EVER_CHOSEN_KEY, true, userId);

            setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
            dpm.setActivePasswordState(new PasswordMetrics(
                    DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, pattern.size()), userId);
            onAfterChangingPassword(userId);
        } catch (RemoteException re) {
            Log.e(TAG, "Couldn't save lock pattern " + re);
@@ -775,10 +770,9 @@ public class LockPatternUtils {
                        + "of length " + MIN_LOCK_PASSWORD_SIZE);
            }

            final int computedQuality = PasswordMetrics.computeForPassword(password).quality;
            setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle);
            getLockSettings().setLockPassword(password, savedPassword, userHandle);
            getLockSettings().setSeparateProfileChallengeEnabled(userHandle, true, null);
            final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password);
            final int computedQuality = metrics.quality;

            // Update the device encryption password.
            if (userHandle == UserHandle.USER_SYSTEM
@@ -796,15 +790,6 @@ public class LockPatternUtils {
                }
            }

            setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle);
            if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
                metrics.quality = Math.max(quality, metrics.quality);
                dpm.setActivePasswordState(metrics, userHandle);
            } else {
                // The password is not anything.
                dpm.setActivePasswordState(new PasswordMetrics(), userHandle);
            }

            // Add the password to the password history. We assume all
            // password hashes have the same length for simplicity of implementation.
            String passwordHistory = getString(PASSWORD_HISTORY_KEY, userHandle);
+38 −0
Original line number Diff line number Diff line
@@ -16,12 +16,14 @@

package com.android.server;

import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
import android.app.admin.PasswordMetrics;
import android.app.backup.BackupManager;
import android.app.trust.IStrongAuthTracker;
import android.app.trust.TrustManager;
@@ -931,6 +933,7 @@ public class LockSettingsService extends ILockSettings.Stub {
        synchronized (mSeparateChallengeLock) {
            setLockPatternInternal(pattern, savedCredential, userId);
            setSeparateProfileChallengeEnabled(userId, true, null);
            notifyPasswordChanged(userId);
        }
    }

@@ -945,6 +948,7 @@ public class LockSettingsService extends ILockSettings.Stub {
            setKeystorePassword(null, userId);
            fixateNewestUserKeyAuth(userId);
            onUserLockChanged(userId);
            notifyActivePasswordMetricsAvailable(null, userId);
            return;
        }

@@ -994,6 +998,7 @@ public class LockSettingsService extends ILockSettings.Stub {
        synchronized (mSeparateChallengeLock) {
            setLockPasswordInternal(password, savedCredential, userId);
            setSeparateProfileChallengeEnabled(userId, true, null);
            notifyPasswordChanged(userId);
        }
    }

@@ -1007,6 +1012,7 @@ public class LockSettingsService extends ILockSettings.Stub {
            setKeystorePassword(null, userId);
            fixateNewestUserKeyAuth(userId);
            onUserLockChanged(userId);
            notifyActivePasswordMetricsAvailable(null, userId);
            return;
        }

@@ -1420,6 +1426,7 @@ public class LockSettingsService extends ILockSettings.Stub {
                // migrate credential to GateKeeper
                credentialUtil.setCredential(credential, null, userId);
                if (!hasChallenge) {
                    notifyActivePasswordMetricsAvailable(credential, userId);
                    return VerifyCredentialResponse.OK;
                }
                // Fall through to get the auth token. Technically this should never happen,
@@ -1459,6 +1466,7 @@ public class LockSettingsService extends ILockSettings.Stub {
            if (progressCallback != null) {
                progressCallback.onCredentialVerified();
            }
            notifyActivePasswordMetricsAvailable(credential, userId);
            unlockKeystore(credential, userId);

            Slog.i(TAG, "Unlocking user " + userId +
@@ -1482,6 +1490,36 @@ public class LockSettingsService extends ILockSettings.Stub {
        return response;
    }

    private void notifyActivePasswordMetricsAvailable(String password, @UserIdInt int userId) {
        final PasswordMetrics metrics;
        if (password == null) {
            metrics = new PasswordMetrics();
        } else {
            metrics = PasswordMetrics.computeForPassword(password);
            metrics.quality = mLockPatternUtils.getKeyguardStoredPasswordQuality(userId);
        }

        // Asynchronous to avoid dead lock
        mHandler.post(() -> {
            DevicePolicyManager dpm = (DevicePolicyManager)
                    mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
            dpm.setActivePasswordState(metrics, userId);
        });
    }

    /**
     * Call after {@link #notifyActivePasswordMetricsAvailable} so metrics are updated before
     * reporting the password changed.
     */
    private void notifyPasswordChanged(@UserIdInt int userId) {
        // Same handler as notifyActivePasswordMetricsAvailable to ensure correct ordering
        mHandler.post(() -> {
            DevicePolicyManager dpm = (DevicePolicyManager)
                    mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
            dpm.reportPasswordChanged(userId);
        });
    }

    @Override
    public boolean checkVoldPassword(int userId) throws RemoteException {
        if (!mFirstCallToVold) {
+47 −53
Original line number Diff line number Diff line
@@ -253,7 +253,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
    private static final String ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED =
            "device-provisioning-config-applied";
    private static final String ATTR_DEVICE_PAIRED = "device-paired";

    private static final String ATTR_DELEGATED_CERT_INSTALLER = "delegated-cert-installer";
    private static final String ATTR_APPLICATION_RESTRICTIONS_MANAGER
            = "application-restrictions-manager";
@@ -2342,20 +2341,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                out.endTag(null, "failed-password-attempts");
            }

            final PasswordMetrics metrics = policy.mActivePasswordMetrics;
            if (!metrics.isDefault()) {
                out.startTag(null, "active-password");
                out.attribute(null, "quality", Integer.toString(metrics.quality));
                out.attribute(null, "length", Integer.toString(metrics.length));
                out.attribute(null, "uppercase", Integer.toString(metrics.upperCase));
                out.attribute(null, "lowercase", Integer.toString(metrics.lowerCase));
                out.attribute(null, "letters", Integer.toString(metrics.letters));
                out.attribute(null, "numeric", Integer.toString(metrics.numeric));
                out.attribute(null, "symbols", Integer.toString(metrics.symbols));
                out.attribute(null, "nonletter", Integer.toString(metrics.nonLetter));
                out.endTag(null, "active-password");
            }

            for (int i = 0; i < policy.mAcceptedCaCertificates.size(); i++) {
                out.startTag(null, TAG_ACCEPTED_CA_CERTIFICATES);
                out.attribute(null, ATTR_NAME, policy.mAcceptedCaCertificates.valueAt(i));
@@ -2456,6 +2441,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
        JournaledFile journal = makeJournaledFile(userHandle);
        FileInputStream stream = null;
        File file = journal.chooseForRead();
        boolean needsRewrite = false;
        try {
            stream = new FileInputStream(file);
            XmlPullParser parser = Xml.newPullParser();
@@ -2542,16 +2528,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                } else if ("password-owner".equals(tag)) {
                    policy.mPasswordOwner = Integer.parseInt(
                            parser.getAttributeValue(null, "value"));
                } else if ("active-password".equals(tag)) {
                    final PasswordMetrics m = policy.mActivePasswordMetrics;
                    m.quality = Integer.parseInt(parser.getAttributeValue(null, "quality"));
                    m.length = Integer.parseInt(parser.getAttributeValue(null, "length"));
                    m.upperCase = Integer.parseInt(parser.getAttributeValue(null, "uppercase"));
                    m.lowerCase = Integer.parseInt(parser.getAttributeValue(null, "lowercase"));
                    m.letters = Integer.parseInt(parser.getAttributeValue(null, "letters"));
                    m.numeric = Integer.parseInt(parser.getAttributeValue(null, "numeric"));
                    m.symbols = Integer.parseInt(parser.getAttributeValue(null, "symbols"));
                    m.nonLetter = Integer.parseInt(parser.getAttributeValue(null, "nonletter"));
                } else if (TAG_ACCEPTED_CA_CERTIFICATES.equals(tag)) {
                    policy.mAcceptedCaCertificates.add(parser.getAttributeValue(null, ATTR_NAME));
                } else if (TAG_LOCK_TASK_COMPONENTS.equals(tag)) {
@@ -2577,6 +2553,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                    policy.mAdminBroadcastPending = Boolean.toString(true).equals(pending);
                } else if (TAG_INITIALIZATION_BUNDLE.equals(tag)) {
                    policy.mInitBundle = PersistableBundle.restoreFromXml(parser);
                } else if ("active-password".equals(tag)) {
                    needsRewrite = true;
                } else {
                    Slog.w(LOG_TAG, "Unknown tag: " + tag);
                    XmlUtils.skipCurrentTag(parser);
@@ -2596,27 +2574,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
            // Ignore
        }

        // Might need to upgrade the file by rewriting it
        if (needsRewrite) {
            saveSettingsLocked(userHandle);
        }

        // Generate a list of admins from the admin map
        policy.mAdminList.addAll(policy.mAdminMap.values());

        // Validate that what we stored for the password quality matches
        // sufficiently what is currently set.  Note that this is only
        // a sanity check in case the two get out of sync; this should
        // never normally happen.
        final long identity = mInjector.binderClearCallingIdentity();
        try {
            int actualPasswordQuality = mLockPatternUtils.getActivePasswordQuality(userHandle);
            if (actualPasswordQuality < policy.mActivePasswordMetrics.quality) {
                Slog.w(LOG_TAG, "Active password quality 0x"
                        + Integer.toHexString(policy.mActivePasswordMetrics.quality)
                        + " does not match actual quality 0x"
                        + Integer.toHexString(actualPasswordQuality));
                policy.mActivePasswordMetrics = new PasswordMetrics();
            }
        } finally {
            mInjector.binderRestoreCallingIdentity(identity);
        }

        validatePasswordOwnerLocked(policy);
        updateMaximumTimeToLockLocked(userHandle);
        updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle);
@@ -3850,6 +3815,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {

    private boolean isActivePasswordSufficientForUserLocked(
            DevicePolicyData policy, int userHandle, boolean parent) {
        enforceUserUnlocked(userHandle, parent);

        final int requiredPasswordQuality = getPasswordQuality(null, userHandle, parent);
        if (policy.mActivePasswordMetrics.quality < requiredPasswordQuality) {
            return false;
@@ -4921,33 +4888,52 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
            return;
        }
        enforceFullCrossUsersPermission(userHandle);
        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.BIND_DEVICE_ADMIN, null);

        // If the managed profile doesn't have a separate password, set the metrics to default
        if (isManagedProfile(userHandle) && !isSeparateProfileChallengeEnabled(userHandle)) {
            metrics = new PasswordMetrics();
        }

        validateQualityConstant(metrics.quality);
        DevicePolicyData policy = getUserData(userHandle);
        synchronized (this) {
            policy.mActivePasswordMetrics = metrics;
        }
    }

    @Override
    public void reportPasswordChanged(@UserIdInt int userId) {
        if (!mHasFeature) {
            return;
        }
        enforceFullCrossUsersPermission(userId);

        // Managed Profile password can only be changed when it has a separate challenge.
        if (!isSeparateProfileChallengeEnabled(userHandle)) {
            enforceNotManagedProfile(userHandle, "set the active password");
        if (!isSeparateProfileChallengeEnabled(userId)) {
            enforceNotManagedProfile(userId, "set the active password");
        }

        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.BIND_DEVICE_ADMIN, null);
        validateQualityConstant(metrics.quality);

        DevicePolicyData policy = getUserData(userHandle);
        DevicePolicyData policy = getUserData(userId);

        long ident = mInjector.binderClearCallingIdentity();
        try {
            synchronized (this) {
                policy.mActivePasswordMetrics = metrics;
                policy.mFailedPasswordAttempts = 0;
                saveSettingsLocked(userHandle);
                updatePasswordExpirationsLocked(userHandle);
                setExpirationAlarmCheckLocked(mContext, userHandle, /* parent */ false);
                saveSettingsLocked(userId);
                updatePasswordExpirationsLocked(userId);
                setExpirationAlarmCheckLocked(mContext, userId, /* parent */ false);

                // Send a broadcast to each profile using this password as its primary unlock.
                sendAdminCommandForLockscreenPoliciesLocked(
                        DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
                        DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userHandle);
                        DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userId);
            }
            removeCaApprovalsIfNeeded(userHandle);
            removeCaApprovalsIfNeeded(userId);
        } finally {
            mInjector.binderRestoreCallingIdentity(ident);
        }
@@ -6574,6 +6560,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                "User must be running and unlocked");
    }

    private void enforceUserUnlocked(@UserIdInt int userId, boolean parent) {
        if (parent) {
            enforceUserUnlocked(getProfileParentId(userId));
        } else {
            enforceUserUnlocked(userId);
        }
    }

    private void enforceManageUsers() {
        final int callingUid = mInjector.binderGetCallingUid();
        if (!(isCallerWithSystemUid() || callingUid == Process.ROOT_UID)) {