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

Commit 751e102e authored by Kevin Chyn's avatar Kevin Chyn Committed by android-build-merger
Browse files

Merge "Defer resetLockout and execute on a separate thread" into qt-r1-dev am: a73b5323

am: 97bd42ec

Change-Id: I4d15c34585ecbb17aaa629006b709dcf75376957
parents 82ec168c 97bd42ec
Loading
Loading
Loading
Loading
+82 −29
Original line number Original line Diff line number Diff line
@@ -32,6 +32,7 @@ import static com.android.internal.widget.LockPatternUtils.USER_FRP;
import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled;
import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled;
import static com.android.internal.widget.LockPatternUtils.userOwnsFrpCredential;
import static com.android.internal.widget.LockPatternUtils.userOwnsFrpCredential;


import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.annotation.UserIdInt;
@@ -169,6 +170,20 @@ public class LockSettingsService extends ILockSettings.Stub {
    private static final String SEPARATE_PROFILE_CHALLENGE_KEY = "lockscreen.profilechallenge";
    private static final String SEPARATE_PROFILE_CHALLENGE_KEY = "lockscreen.profilechallenge";
    private static final int SYNTHETIC_PASSWORD_ENABLED_BY_DEFAULT = 1;
    private static final int SYNTHETIC_PASSWORD_ENABLED_BY_DEFAULT = 1;


    // No challenge provided
    private static final int CHALLENGE_NONE = 0;
    // Challenge was provided from the external caller (non-LockSettingsService)
    private static final int CHALLENGE_FROM_CALLER = 1;
    // Challenge was generated from within LockSettingsService, for resetLockout. When challenge
    // type is set to internal, LSS will revokeChallenge after all profiles for that user are
    // unlocked.
    private static final int CHALLENGE_INTERNAL = 2;

    @IntDef({CHALLENGE_NONE,
            CHALLENGE_FROM_CALLER,
            CHALLENGE_INTERNAL})
    @interface ChallengeType {};

    // Order of holding lock: mSeparateChallengeLock -> mSpManager -> this
    // Order of holding lock: mSeparateChallengeLock -> mSpManager -> this
    // Do not call into ActivityManager while holding mSpManager lock.
    // Do not call into ActivityManager while holding mSpManager lock.
    private final Object mSeparateChallengeLock = new Object();
    private final Object mSeparateChallengeLock = new Object();
@@ -276,6 +291,15 @@ public class LockSettingsService extends ILockSettings.Stub {
        }
        }
    }
    }


    private class PendingResetLockout {
        final int mUserId;
        final byte[] mHAT;
        PendingResetLockout(int userId, byte[] hat) {
            mUserId = userId;
            mHAT = hat;
        }
    }

    /**
    /**
     * Tie managed profile to primary profile if it is in unified mode and not tied before.
     * Tie managed profile to primary profile if it is in unified mode and not tied before.
     *
     *
@@ -585,7 +609,7 @@ public class LockSettingsService extends ILockSettings.Stub {
                // be still locked, we ignore this case since the user will be prompted to unlock
                // be still locked, we ignore this case since the user will be prompted to unlock
                // the device after boot.
                // the device after boot.
                unlockChildProfile(userId, true /* ignoreUserNotAuthenticated */,
                unlockChildProfile(userId, true /* ignoreUserNotAuthenticated */,
                        false /* hasChallenge */, 0 /* challenge */);
                        CHALLENGE_NONE, 0 /* challenge */, null /* resetLockouts */);
            } catch (RemoteException e) {
            } catch (RemoteException e) {
                Slog.e(TAG, "Failed to unlock child profile");
                Slog.e(TAG, "Failed to unlock child profile");
            }
            }
@@ -1162,12 +1186,14 @@ public class LockSettingsService extends ILockSettings.Stub {
    }
    }


    private void unlockChildProfile(int profileHandle, boolean ignoreUserNotAuthenticated,
    private void unlockChildProfile(int profileHandle, boolean ignoreUserNotAuthenticated,
            boolean hasChallenge, long challenge)
            @ChallengeType int challengeType, long challenge,
            @Nullable ArrayList<PendingResetLockout> resetLockouts)
            throws RemoteException {
            throws RemoteException {
        try {
        try {
            doVerifyCredential(getDecryptedPasswordForTiedProfile(profileHandle),
            doVerifyCredential(getDecryptedPasswordForTiedProfile(profileHandle),
                    CREDENTIAL_TYPE_PASSWORD,
                    CREDENTIAL_TYPE_PASSWORD,
                    hasChallenge, challenge, profileHandle, null /* progressCallback */);
                    challengeType, challenge, profileHandle, null /* progressCallback */,
                    resetLockouts);
        } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
        } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
                | NoSuchAlgorithmException | NoSuchPaddingException
                | NoSuchAlgorithmException | NoSuchPaddingException
                | InvalidAlgorithmParameterException | IllegalBlockSizeException
                | InvalidAlgorithmParameterException | IllegalBlockSizeException
@@ -1183,7 +1209,7 @@ public class LockSettingsService extends ILockSettings.Stub {
    }
    }


    private void unlockUser(int userId, byte[] token, byte[] secret) {
    private void unlockUser(int userId, byte[] token, byte[] secret) {
        unlockUser(userId, token, secret, false /* hasChallenge */, 0 /* challenge */);
        unlockUser(userId, token, secret, CHALLENGE_NONE, 0 /* challenge */, null);
    }
    }


    /**
    /**
@@ -1195,7 +1221,8 @@ public class LockSettingsService extends ILockSettings.Stub {
     * {@link com.android.server.SystemServiceManager#unlockUser} </em>
     * {@link com.android.server.SystemServiceManager#unlockUser} </em>
     */
     */
    private void unlockUser(int userId, byte[] token, byte[] secret,
    private void unlockUser(int userId, byte[] token, byte[] secret,
            boolean hasChallenge, long challenge) {
            @ChallengeType int challengeType, long challenge,
            @Nullable ArrayList<PendingResetLockout> resetLockouts) {
        // TODO: make this method fully async so we can update UI with progress strings
        // TODO: make this method fully async so we can update UI with progress strings
        final boolean alreadyUnlocked = mUserManager.isUserUnlockingOrUnlocked(userId);
        final boolean alreadyUnlocked = mUserManager.isUserUnlockingOrUnlocked(userId);
        final CountDownLatch latch = new CountDownLatch(1);
        final CountDownLatch latch = new CountDownLatch(1);
@@ -1240,7 +1267,7 @@ public class LockSettingsService extends ILockSettings.Stub {
                    // Must pass the challenge on for resetLockout, so it's not over-written, which
                    // Must pass the challenge on for resetLockout, so it's not over-written, which
                    // causes LockSettingsService to revokeChallenge inappropriately.
                    // causes LockSettingsService to revokeChallenge inappropriately.
                    unlockChildProfile(profile.id, false /* ignoreUserNotAuthenticated */,
                    unlockChildProfile(profile.id, false /* ignoreUserNotAuthenticated */,
                            hasChallenge, challenge);
                            challengeType, challenge, resetLockouts);
                } catch (RemoteException e) {
                } catch (RemoteException e) {
                    Log.d(TAG, "Failed to unlock child profile", e);
                    Log.d(TAG, "Failed to unlock child profile", e);
                }
                }
@@ -1257,6 +1284,20 @@ public class LockSettingsService extends ILockSettings.Stub {
            }
            }


        }
        }

        if (resetLockouts != null && !resetLockouts.isEmpty()) {
            mHandler.post(() -> {
                final BiometricManager bm = mContext.getSystemService(BiometricManager.class);
                final PackageManager pm = mContext.getPackageManager();
                for (int i = 0; i < resetLockouts.size(); i++) {
                    bm.resetLockout(resetLockouts.get(i).mHAT);
                }
                if (challengeType == CHALLENGE_INTERNAL
                        && pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
                    mContext.getSystemService(FaceManager.class).revokeChallenge();
                }
            });
        }
    }
    }


    private boolean tiedManagedProfileReadyToUnlock(UserInfo userInfo) {
    private boolean tiedManagedProfileReadyToUnlock(UserInfo userInfo) {
@@ -1528,7 +1569,8 @@ public class LockSettingsService extends ILockSettings.Stub {
            setUserKeyProtection(userId, credential, convertResponse(gkResponse));
            setUserKeyProtection(userId, credential, convertResponse(gkResponse));
            fixateNewestUserKeyAuth(userId);
            fixateNewestUserKeyAuth(userId);
            // Refresh the auth token
            // Refresh the auth token
            doVerifyCredential(credential, credentialType, true, 0, userId, null /* progressCallback */);
            doVerifyCredential(credential, credentialType, CHALLENGE_FROM_CALLER, 0, userId,
                    null /* progressCallback */);
            synchronizeUnifiedWorkChallengeForProfiles(userId, null);
            synchronizeUnifiedWorkChallengeForProfiles(userId, null);
            sendCredentialsOnChangeIfRequired(
            sendCredentialsOnChangeIfRequired(
                    credentialType, credential, userId, isLockTiedToParent);
                    credentialType, credential, userId, isLockTiedToParent);
@@ -1753,24 +1795,32 @@ public class LockSettingsService extends ILockSettings.Stub {
    public VerifyCredentialResponse checkCredential(byte[] credential, int type, int userId,
    public VerifyCredentialResponse checkCredential(byte[] credential, int type, int userId,
            ICheckCredentialProgressCallback progressCallback) throws RemoteException {
            ICheckCredentialProgressCallback progressCallback) throws RemoteException {
        checkPasswordReadPermission(userId);
        checkPasswordReadPermission(userId);
        return doVerifyCredential(credential, type, false, 0, userId, progressCallback);
        return doVerifyCredential(credential, type, CHALLENGE_NONE, 0, userId, progressCallback);
    }
    }


    @Override
    @Override
    public VerifyCredentialResponse verifyCredential(byte[] credential, int type, long challenge,
    public VerifyCredentialResponse verifyCredential(byte[] credential, int type, long challenge,
            int userId) throws RemoteException {
            int userId) throws RemoteException {
        checkPasswordReadPermission(userId);
        checkPasswordReadPermission(userId);
        return doVerifyCredential(credential, type, true, challenge, userId,
        return doVerifyCredential(credential, type, CHALLENGE_FROM_CALLER, challenge, userId,
                null /* progressCallback */);
                null /* progressCallback */);
    }
    }


    private VerifyCredentialResponse doVerifyCredential(byte[] credential, int credentialType,
            @ChallengeType int challengeType, long challenge, int userId,
            ICheckCredentialProgressCallback progressCallback) throws RemoteException {
        return doVerifyCredential(credential, credentialType, challengeType, challenge, userId,
                progressCallback, null /* resetLockouts */);
    }

    /**
    /**
     * Verify user credential and unlock the user. Fix pattern bug by deprecating the old base zero
     * Verify user credential and unlock the user. Fix pattern bug by deprecating the old base zero
     * format.
     * format.
     */
     */
    private VerifyCredentialResponse doVerifyCredential(byte[] credential, int credentialType,
    private VerifyCredentialResponse doVerifyCredential(byte[] credential, int credentialType,
            boolean hasChallenge, long challenge, int userId,
            @ChallengeType int challengeType, long challenge, int userId,
            ICheckCredentialProgressCallback progressCallback) throws RemoteException {
            ICheckCredentialProgressCallback progressCallback,
            @Nullable ArrayList<PendingResetLockout> resetLockouts) throws RemoteException {
        if (credential == null || credential.length == 0) {
        if (credential == null || credential.length == 0) {
            throw new IllegalArgumentException("Credential can't be null or empty");
            throw new IllegalArgumentException("Credential can't be null or empty");
        }
        }
@@ -1780,8 +1830,8 @@ public class LockSettingsService extends ILockSettings.Stub {
            return VerifyCredentialResponse.ERROR;
            return VerifyCredentialResponse.ERROR;
        }
        }
        VerifyCredentialResponse response = null;
        VerifyCredentialResponse response = null;
        response = spBasedDoVerifyCredential(credential, credentialType, hasChallenge, challenge,
        response = spBasedDoVerifyCredential(credential, credentialType, challengeType, challenge,
                userId, progressCallback);
                userId, progressCallback, resetLockouts);
        // The user employs synthetic password based credential.
        // The user employs synthetic password based credential.
        if (response != null) {
        if (response != null) {
            if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
            if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
@@ -1813,7 +1863,7 @@ public class LockSettingsService extends ILockSettings.Stub {
        }
        }


        response = verifyCredential(userId, storedHash, credentialToVerify,
        response = verifyCredential(userId, storedHash, credentialToVerify,
                hasChallenge, challenge, progressCallback);
                challengeType, challenge, progressCallback);


        if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
        if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
            mStrongAuth.reportSuccessfulStrongAuthUnlock(userId);
            mStrongAuth.reportSuccessfulStrongAuthUnlock(userId);
@@ -1839,7 +1889,7 @@ public class LockSettingsService extends ILockSettings.Stub {
        final VerifyCredentialResponse parentResponse = doVerifyCredential(
        final VerifyCredentialResponse parentResponse = doVerifyCredential(
                credential,
                credential,
                type,
                type,
                true /* hasChallenge */,
                CHALLENGE_FROM_CALLER,
                challenge,
                challenge,
                parentProfileId,
                parentProfileId,
                null /* progressCallback */);
                null /* progressCallback */);
@@ -1852,7 +1902,7 @@ public class LockSettingsService extends ILockSettings.Stub {
            // Unlock work profile, and work profile with unified lock must use password only
            // Unlock work profile, and work profile with unified lock must use password only
            return doVerifyCredential(getDecryptedPasswordForTiedProfile(userId),
            return doVerifyCredential(getDecryptedPasswordForTiedProfile(userId),
                    CREDENTIAL_TYPE_PASSWORD,
                    CREDENTIAL_TYPE_PASSWORD,
                    true,
                    CHALLENGE_FROM_CALLER,
                    challenge,
                    challenge,
                    userId, null /* progressCallback */);
                    userId, null /* progressCallback */);
        } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
        } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
@@ -1870,7 +1920,7 @@ public class LockSettingsService extends ILockSettings.Stub {
     * hash to GK.
     * hash to GK.
     */
     */
    private VerifyCredentialResponse verifyCredential(int userId, CredentialHash storedHash,
    private VerifyCredentialResponse verifyCredential(int userId, CredentialHash storedHash,
            byte[] credential, boolean hasChallenge, long challenge,
            byte[] credential, @ChallengeType int challengeType, long challenge,
            ICheckCredentialProgressCallback progressCallback) throws RemoteException {
            ICheckCredentialProgressCallback progressCallback) throws RemoteException {
        if ((storedHash == null || storedHash.hash.length == 0)
        if ((storedHash == null || storedHash.hash.length == 0)
                    && (credential == null || credential.length == 0)) {
                    && (credential == null || credential.length == 0)) {
@@ -1913,7 +1963,7 @@ public class LockSettingsService extends ILockSettings.Stub {
                                : DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
                                : DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
                                /* TODO(roosa): keep the same password quality */,
                                /* TODO(roosa): keep the same password quality */,
                        userId, false, /* isLockTiedToParent= */ false);
                        userId, false, /* isLockTiedToParent= */ false);
                if (!hasChallenge) {
                if (challengeType == CHALLENGE_NONE) {
                    notifyActivePasswordMetricsAvailable(storedHash.type, credential, userId);
                    notifyActivePasswordMetricsAvailable(storedHash.type, credential, userId);
                    // Use credentials to create recoverable keystore snapshot.
                    // Use credentials to create recoverable keystore snapshot.
                    sendCredentialsOnUnlockIfRequired(storedHash.type, credential, userId);
                    sendCredentialsOnUnlockIfRequired(storedHash.type, credential, userId);
@@ -2501,12 +2551,13 @@ public class LockSettingsService extends ILockSettings.Stub {
    }
    }


    private VerifyCredentialResponse spBasedDoVerifyCredential(byte[] userCredential,
    private VerifyCredentialResponse spBasedDoVerifyCredential(byte[] userCredential,
            @CredentialType int credentialType, boolean hasChallenge, long challenge, int userId,
            @CredentialType int credentialType, @ChallengeType int challengeType, long challenge,
            ICheckCredentialProgressCallback progressCallback) throws RemoteException {
            int userId, ICheckCredentialProgressCallback progressCallback,
            @Nullable ArrayList<PendingResetLockout> resetLockouts) throws RemoteException {


        final boolean hasEnrolledBiometrics = mInjector.hasEnrolledBiometrics(userId);
        final boolean hasEnrolledBiometrics = mInjector.hasEnrolledBiometrics(userId);


        Slog.d(TAG, "spBasedDoVerifyCredential: user=" + userId + " hasChallenge=" + hasChallenge
        Slog.d(TAG, "spBasedDoVerifyCredential: user=" + userId + " challengeType=" + challengeType
                + " hasEnrolledBiometrics=" + hasEnrolledBiometrics);
                + " hasEnrolledBiometrics=" + hasEnrolledBiometrics);
        if (credentialType == CREDENTIAL_TYPE_NONE) {
        if (credentialType == CREDENTIAL_TYPE_NONE) {
            userCredential = null;
            userCredential = null;
@@ -2516,8 +2567,11 @@ public class LockSettingsService extends ILockSettings.Stub {
        // TODO: When lockout is handled under the HAL for all biometrics (fingerprint),
        // TODO: When lockout is handled under the HAL for all biometrics (fingerprint),
        // we need to generate challenge for each one, have it signed by GK and reset lockout
        // we need to generate challenge for each one, have it signed by GK and reset lockout
        // for each modality.
        // for each modality.
        if (!hasChallenge && pm.hasSystemFeature(PackageManager.FEATURE_FACE)
        if (challengeType == CHALLENGE_NONE && pm.hasSystemFeature(PackageManager.FEATURE_FACE)
                && hasEnrolledBiometrics) {
                && hasEnrolledBiometrics) {
            // If there are multiple profiles in the same account, ensure we only generate the
            // challenge once.
            challengeType = CHALLENGE_INTERNAL;
            challenge = mContext.getSystemService(FaceManager.class).generateChallenge();
            challenge = mContext.getSystemService(FaceManager.class).generateChallenge();
        }
        }


@@ -2559,19 +2613,18 @@ public class LockSettingsService extends ILockSettings.Stub {
        if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
        if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
            notifyActivePasswordMetricsAvailable(credentialType, userCredential, userId);
            notifyActivePasswordMetricsAvailable(credentialType, userCredential, userId);
            unlockKeystore(authResult.authToken.deriveKeyStorePassword(), userId);
            unlockKeystore(authResult.authToken.deriveKeyStorePassword(), userId);
            // Reset lockout only if user has enrolled templates
            if (hasEnrolledBiometrics) {
                BiometricManager bm = mContext.getSystemService(BiometricManager.class);
                bm.resetLockout(response.getPayload());


                if (!hasChallenge && pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
            // Do resetLockout / revokeChallenge when all profiles are unlocked
                    mContext.getSystemService(FaceManager.class).revokeChallenge();
            if (hasEnrolledBiometrics) {
                if (resetLockouts == null) {
                    resetLockouts = new ArrayList<>();
                }
                }
                resetLockouts.add(new PendingResetLockout(userId, response.getPayload()));
            }
            }


            final byte[] secret = authResult.authToken.deriveDiskEncryptionKey();
            final byte[] secret = authResult.authToken.deriveDiskEncryptionKey();
            Slog.i(TAG, "Unlocking user " + userId + " with secret only, length " + secret.length);
            Slog.i(TAG, "Unlocking user " + userId + " with secret only, length " + secret.length);
            unlockUser(userId, null, secret, hasChallenge, challenge);
            unlockUser(userId, null, secret, challengeType, challenge, resetLockouts);


            activateEscrowTokens(authResult.authToken, userId);
            activateEscrowTokens(authResult.authToken, userId);