diff options
| author | 2020-08-19 22:33:02 +0000 | |
|---|---|---|
| committer | 2020-08-19 22:33:02 +0000 | |
| commit | 106cfaacade1bfc37654f4cc0ed3f2e3f19fb9ee (patch) | |
| tree | 9f5b3ca0e4469ec170817ba1176800b2619696e2 | |
| parent | 4708dea711327120c94bda782e007a5c964b0a31 (diff) | |
| parent | be2f80392c268c77e9a1f499291bfbc5872e0e52 (diff) | |
Merge "Return handle to gatekeeper password instead of actual password"
7 files changed, 130 insertions, 53 deletions
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index d5d635de81d8..654b46164dcf 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -49,7 +49,8 @@ interface ILockSettings { in ICheckCredentialProgressCallback progressCallback); VerifyCredentialResponse verifyCredential(in LockscreenCredential credential, int userId, int flags); VerifyCredentialResponse verifyTiedProfileChallenge(in LockscreenCredential credential, int userId, int flags); - VerifyCredentialResponse verifyGatekeeperPassword(in byte[] gatekeeperPassword, long challenge, int userId); + VerifyCredentialResponse verifyGatekeeperPasswordHandle(long gatekeeperPasswordHandle, long challenge, int userId); + void removeGatekeeperPasswordHandle(long gatekeeperPasswordHandle); boolean checkVoldPassword(int userId); int getCredentialType(int userId); byte[] getHashFactor(in LockscreenCredential currentCredential, int userId); diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index f7370d6a22f9..960c11f4c55c 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -130,14 +130,15 @@ public class LockPatternUtils { public @interface CredentialType {} /** - * Flag provided to {@link #verifyCredential(LockscreenCredential, long, int, int)} . If set, - * the method will return the Gatekeeper Password in the {@link VerifyCredentialResponse}. + * Flag provided to {@link #verifyCredential(LockscreenCredential, int, int)} . If set, the + * method will return a handle to the Gatekeeper Password in the + * {@link VerifyCredentialResponse}. */ - public static final int VERIFY_FLAG_RETURN_GK_PW = 1 << 0; + public static final int VERIFY_FLAG_REQUEST_GK_PW_HANDLE = 1 << 0; @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, value = { - VERIFY_FLAG_RETURN_GK_PW + VERIFY_FLAG_REQUEST_GK_PW_HANDLE }) public @interface VerifyFlag {} @@ -409,16 +410,16 @@ public class LockPatternUtils { } /** - * With the Gatekeeper Password returned via {@link #verifyCredential(LockscreenCredential, - * int, int)}, request Gatekeeper to create a HardwareAuthToken wrapping the given - * challenge. + * With the Gatekeeper Password Handle returned via {@link #verifyCredential( + * LockscreenCredential, int, int)}, request Gatekeeper to create a HardwareAuthToken wrapping + * the given challenge. */ @NonNull - public VerifyCredentialResponse verifyGatekeeperPassword(@NonNull byte[] gatekeeperPassword, + public VerifyCredentialResponse verifyGatekeeperPasswordHandle(long gatekeeperPasswordHandle, long challenge, int userId) { try { - final VerifyCredentialResponse response = getLockSettings().verifyGatekeeperPassword( - gatekeeperPassword, challenge, userId); + final VerifyCredentialResponse response = getLockSettings() + .verifyGatekeeperPasswordHandle(gatekeeperPasswordHandle, challenge, userId); if (response == null) { return VerifyCredentialResponse.ERROR; } @@ -429,6 +430,14 @@ public class LockPatternUtils { } } + public void removeGatekeeperPasswordHandle(long gatekeeperPasswordHandle) { + try { + getLockSettings().removeGatekeeperPasswordHandle(gatekeeperPasswordHandle); + } catch (RemoteException e) { + Log.e(TAG, "failed to remove gatekeeper password handle", e); + } + } + /** * Check to see if a credential matches the saved one. * diff --git a/core/java/com/android/internal/widget/VerifyCredentialResponse.java b/core/java/com/android/internal/widget/VerifyCredentialResponse.java index e09eb4228219..ab146341cbaa 100644 --- a/core/java/com/android/internal/widget/VerifyCredentialResponse.java +++ b/core/java/com/android/internal/widget/VerifyCredentialResponse.java @@ -49,7 +49,7 @@ public final class VerifyCredentialResponse implements Parcelable { private final @ResponseCode int mResponseCode; private final int mTimeout; @Nullable private final byte[] mGatekeeperHAT; - @Nullable private final byte[] mGatekeeperPw; + private final long mGatekeeperPasswordHandle; public static final Parcelable.Creator<VerifyCredentialResponse> CREATOR = new Parcelable.Creator<VerifyCredentialResponse>() { @@ -58,10 +58,10 @@ public final class VerifyCredentialResponse implements Parcelable { final @ResponseCode int responseCode = source.readInt(); final int timeout = source.readInt(); final byte[] gatekeeperHAT = source.createByteArray(); - final byte[] gatekeeperPassword = source.createByteArray(); + long gatekeeperPasswordHandle = source.readLong(); return new VerifyCredentialResponse(responseCode, timeout, gatekeeperHAT, - gatekeeperPassword); + gatekeeperPasswordHandle); } @Override @@ -72,7 +72,7 @@ public final class VerifyCredentialResponse implements Parcelable { public static class Builder { @Nullable private byte[] mGatekeeperHAT; - @Nullable private byte[] mGatekeeperPassword; + private long mGatekeeperPasswordHandle; /** * @param gatekeeperHAT Gatekeeper HardwareAuthToken, minted upon successful authentication. @@ -82,8 +82,8 @@ public final class VerifyCredentialResponse implements Parcelable { return this; } - public Builder setGatekeeperPassword(byte[] gatekeeperPassword) { - mGatekeeperPassword = gatekeeperPassword; + public Builder setGatekeeperPasswordHandle(long gatekeeperPasswordHandle) { + mGatekeeperPasswordHandle = gatekeeperPasswordHandle; return this; } @@ -96,7 +96,7 @@ public final class VerifyCredentialResponse implements Parcelable { return new VerifyCredentialResponse(RESPONSE_OK, 0 /* timeout */, mGatekeeperHAT, - mGatekeeperPassword); + mGatekeeperPasswordHandle); } } @@ -110,7 +110,7 @@ public final class VerifyCredentialResponse implements Parcelable { return new VerifyCredentialResponse(RESPONSE_RETRY, timeout, null /* gatekeeperHAT */, - null /* gatekeeperPassword */); + 0L /* gatekeeperPasswordHandle */); } /** @@ -121,20 +121,20 @@ public final class VerifyCredentialResponse implements Parcelable { return new VerifyCredentialResponse(RESPONSE_ERROR, 0 /* timeout */, null /* gatekeeperHAT */, - null /* gatekeeperPassword */); + 0L /* gatekeeperPasswordHandle */); } private VerifyCredentialResponse(@ResponseCode int responseCode, int timeout, - @Nullable byte[] gatekeeperHAT, @Nullable byte[] gatekeeperPassword) { + @Nullable byte[] gatekeeperHAT, long gatekeeperPasswordHandle) { mResponseCode = responseCode; mTimeout = timeout; mGatekeeperHAT = gatekeeperHAT; - mGatekeeperPw = gatekeeperPassword; + mGatekeeperPasswordHandle = gatekeeperPasswordHandle; } public VerifyCredentialResponse stripPayload() { return new VerifyCredentialResponse(mResponseCode, mTimeout, - null /* gatekeeperHAT */, null /* gatekeeperPassword */); + null /* gatekeeperHAT */, 0L /* gatekeeperPasswordHandle */); } @Override @@ -142,7 +142,7 @@ public final class VerifyCredentialResponse implements Parcelable { dest.writeInt(mResponseCode); dest.writeInt(mTimeout); dest.writeByteArray(mGatekeeperHAT); - dest.writeByteArray(mGatekeeperPw); + dest.writeLong(mGatekeeperPasswordHandle); } @Override @@ -155,9 +155,12 @@ public final class VerifyCredentialResponse implements Parcelable { return mGatekeeperHAT; } - @Nullable - public byte[] getGatekeeperPw() { - return mGatekeeperPw; + public long getGatekeeperPasswordHandle() { + return mGatekeeperPasswordHandle; + } + + public boolean containsGatekeeperPasswordHandle() { + return mGatekeeperPasswordHandle != 0L; } public int getTimeout() { @@ -176,7 +179,7 @@ public final class VerifyCredentialResponse implements Parcelable { public String toString() { return "Response: " + mResponseCode + ", GK HAT: " + (mGatekeeperHAT != null) - + ", GK PW: " + (mGatekeeperPw != null); + + ", GK PW: " + (mGatekeeperPasswordHandle != 0L); } public static VerifyCredentialResponse fromGateKeeperResponse( diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java index d4e6506bd9a0..0892612d1825 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java @@ -111,7 +111,7 @@ public class AuthCredentialPasswordView extends AuthCredentialView // VerifyCredentialResponse so that we can request a Gatekeeper HAT with the // Gatekeeper Password and operationId. mPendingLockCheck = LockPatternChecker.verifyCredential(mLockPatternUtils, - password, mEffectiveUserId, LockPatternUtils.VERIFY_FLAG_RETURN_GK_PW, + password, mEffectiveUserId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, this::onCredentialVerified); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java index ad89ae82f637..ab8162f9464d 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java @@ -75,7 +75,7 @@ public class AuthCredentialPatternView extends AuthCredentialView { mLockPatternUtils, credential, mEffectiveUserId, - LockPatternUtils.VERIFY_FLAG_RETURN_GK_PW, + LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, this::onPatternVerified); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java index a8f6f85201f6..b44ff294b792 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java @@ -292,10 +292,12 @@ public abstract class AuthCredentialView extends LinearLayout { // The response passed into this method contains the Gatekeeper Password. We still // have to request Gatekeeper to create a Hardware Auth Token with the // Gatekeeper Password and Challenge (keystore operationId in this case) - final VerifyCredentialResponse gkResponse = mLockPatternUtils.verifyGatekeeperPassword( - response.getGatekeeperPw(), mOperationId, mEffectiveUserId); + final long pwHandle = response.getGatekeeperPasswordHandle(); + final VerifyCredentialResponse gkResponse = mLockPatternUtils + .verifyGatekeeperPasswordHandle(pwHandle, mOperationId, mEffectiveUserId); mCallback.onCredentialMatched(gkResponse.getGatekeeperHAT()); + mLockPatternUtils.removeGatekeeperPasswordHandle(pwHandle); } else { if (timeoutMs > 0) { mHandler.removeCallbacks(mClearErrorRunnable); diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 0044d8936841..93352dca7ce9 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -33,7 +33,7 @@ import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HA import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE; import static com.android.internal.widget.LockPatternUtils.USER_FRP; -import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_RETURN_GK_PW; +import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE; import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled; import static com.android.internal.widget.LockPatternUtils.userOwnsFrpCredential; @@ -104,6 +104,7 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.EventLog; +import android.util.LongSparseArray; import android.util.Slog; import android.util.SparseArray; @@ -123,7 +124,6 @@ import com.android.internal.widget.VerifyCredentialResponse; import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.SystemService; -import com.android.server.SystemService.TargetUser; import com.android.server.locksettings.LockSettingsStorage.CredentialHash; import com.android.server.locksettings.LockSettingsStorage.PersistentData; import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult; @@ -155,6 +155,7 @@ import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; +import java.util.Random; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -186,6 +187,14 @@ public class LockSettingsService extends ILockSettings.Stub { private static final String SYNTHETIC_PASSWORD_UPDATE_TIME_KEY = "sp-handle-ts"; private static final String USER_SERIAL_NUMBER_KEY = "serial-number"; + // Duration that LockSettingsService will store the gatekeeper password for. This allows + // multiple biometric enrollments without prompting the user to enter their password via + // ConfirmLockPassword/ConfirmLockPattern multiple times. This needs to be at least the duration + // from the start of the first biometric sensor's enrollment to the start of the last biometric + // sensor's enrollment. If biometric enrollment requests a password handle that has expired, the + // user's credential must be presented again, e.g. via ConfirmLockPattern/ConfirmLockPassword. + private static final int GK_PW_HANDLE_STORE_DURATION_MS = 10 * 60 * 1000; // 10 minutes + // Order of holding lock: mSeparateChallengeLock -> mSpManager -> this // Do not call into ActivityManager while holding mSpManager lock. private final Object mSeparateChallengeLock = new Object(); @@ -202,6 +211,8 @@ public class LockSettingsService extends ILockSettings.Stub { private final LockSettingsStrongAuth mStrongAuth; private final SynchronizedStrongAuthTracker mStrongAuthTracker; private final BiometricDeferredQueue mBiometricDeferredQueue; + private final LongSparseArray<byte[]> mGatekeeperPasswords; + private final Random mRandom; private final NotificationManager mNotificationManager; private final UserManager mUserManager; @@ -559,6 +570,8 @@ public class LockSettingsService extends ILockSettings.Stub { mStorageManager = injector.getStorageManager(); mStrongAuthTracker = injector.getStrongAuthTracker(); mStrongAuthTracker.register(mStrongAuth); + mGatekeeperPasswords = new LongSparseArray<>(); + mRandom = new SecureRandom(); mSpManager = injector.getSyntheticPasswordManager(mStorage); mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache(); @@ -1017,7 +1030,7 @@ public class LockSettingsService extends ILockSettings.Stub { mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsWrite"); } - private final void checkPasswordReadPermission(int userId) { + private final void checkPasswordReadPermission() { mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsRead"); } @@ -1923,7 +1936,7 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public VerifyCredentialResponse checkCredential(LockscreenCredential credential, int userId, ICheckCredentialProgressCallback progressCallback) { - checkPasswordReadPermission(userId); + checkPasswordReadPermission(); try { return doVerifyCredential(credential, userId, progressCallback, 0 /* flags */); } finally { @@ -1935,7 +1948,7 @@ public class LockSettingsService extends ILockSettings.Stub { @Nullable public VerifyCredentialResponse verifyCredential(LockscreenCredential credential, int userId, int flags) { - checkPasswordReadPermission(userId); + checkPasswordReadPermission(); try { return doVerifyCredential(credential, userId, null /* progressCallback */, flags); } finally { @@ -1944,18 +1957,36 @@ public class LockSettingsService extends ILockSettings.Stub { } @Override - public VerifyCredentialResponse verifyGatekeeperPassword(byte[] gatekeeperPassword, + public VerifyCredentialResponse verifyGatekeeperPasswordHandle(long gatekeeperPasswordHandle, long challenge, int userId) { - checkPasswordReadPermission(userId); + checkPasswordReadPermission(); + + final VerifyCredentialResponse response; + final byte[] gatekeeperPassword; + + synchronized (mGatekeeperPasswords) { + gatekeeperPassword = mGatekeeperPasswords.get(gatekeeperPasswordHandle); + } - VerifyCredentialResponse response; synchronized (mSpManager) { - response = mSpManager.verifyChallengeInternal(getGateKeeperService(), - gatekeeperPassword, challenge, userId); + if (gatekeeperPassword == null) { + response = VerifyCredentialResponse.ERROR; + } else { + response = mSpManager.verifyChallengeInternal(getGateKeeperService(), + gatekeeperPassword, challenge, userId); + } } return response; } + @Override + public void removeGatekeeperPasswordHandle(long gatekeeperPasswordHandle) { + checkPasswordReadPermission(); + synchronized (mGatekeeperPasswords) { + mGatekeeperPasswords.remove(gatekeeperPasswordHandle); + } + } + /* * Verify user credential and unlock the user. Fix pattern bug by deprecating the old base zero * format. @@ -2012,7 +2043,7 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public VerifyCredentialResponse verifyTiedProfileChallenge(LockscreenCredential credential, int userId, @LockPatternUtils.VerifyFlag int flags) { - checkPasswordReadPermission(userId); + checkPasswordReadPermission(); if (!isManagedProfileWithUnifiedLock(userId)) { throw new IllegalArgumentException("User id must be managed profile with unified lock"); } @@ -2179,7 +2210,7 @@ public class LockSettingsService extends ILockSettings.Stub { } mFirstCallToVold = false; - checkPasswordReadPermission(userId); + checkPasswordReadPermission(); // There's no guarantee that this will safely connect, but if it fails // we will simply show the lock screen when we shouldn't, so relatively @@ -2269,13 +2300,13 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public void registerStrongAuthTracker(IStrongAuthTracker tracker) { - checkPasswordReadPermission(UserHandle.USER_ALL); + checkPasswordReadPermission(); mStrongAuth.registerStrongAuthTracker(tracker); } @Override public void unregisterStrongAuthTracker(IStrongAuthTracker tracker) { - checkPasswordReadPermission(UserHandle.USER_ALL); + checkPasswordReadPermission(); mStrongAuth.unregisterStrongAuthTracker(tracker); } @@ -2305,7 +2336,7 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public int getStrongAuthForUser(int userId) { - checkPasswordReadPermission(userId); + checkPasswordReadPermission(); return mStrongAuthTracker.getStrongAuthForUser(userId); } @@ -2648,7 +2679,7 @@ public class LockSettingsService extends ILockSettings.Stub { final AuthenticationResult authResult; VerifyCredentialResponse response; - final boolean returnGkPw = (flags & VERIFY_FLAG_RETURN_GK_PW) != 0; + final boolean requestGkPw = (flags & VERIFY_FLAG_REQUEST_GK_PW_HANDLE) != 0; synchronized (mSpManager) { if (!isSyntheticPasswordBasedCredentialLocked(userId)) { @@ -2690,14 +2721,43 @@ public class LockSettingsService extends ILockSettings.Stub { } } - if (response.isMatched() && returnGkPw) { - return new VerifyCredentialResponse.Builder() - .setGatekeeperPassword(authResult.authToken.deriveGkPassword()).build(); + if (response.isMatched() && requestGkPw) { + final long handle = storeGatekeeperPasswordTemporarily( + authResult.authToken.deriveGkPassword()); + return new VerifyCredentialResponse.Builder().setGatekeeperPasswordHandle(handle) + .build(); } else { return response; } } + /** + * Stores the gatekeeper password temporarily. + * @param gatekeeperPassword unlocked upon successful Synthetic Password + * @return non-zero handle to the gatekeeper password, which can be used for a set amount of + * time. + */ + private long storeGatekeeperPasswordTemporarily(byte[] gatekeeperPassword) { + long handle = 0L; + + synchronized (mGatekeeperPasswords) { + while (handle == 0L || mGatekeeperPasswords.get(handle) != null) { + handle = mRandom.nextLong(); + } + mGatekeeperPasswords.put(handle, gatekeeperPassword); + } + + final long finalHandle = handle; + mHandler.postDelayed(() -> { + synchronized (mGatekeeperPasswords) { + Slog.d(TAG, "Removing handle: " + finalHandle); + mGatekeeperPasswords.remove(finalHandle); + } + }, GK_PW_HANDLE_STORE_DURATION_MS); + + return handle; + } + private void onCredentialVerified(AuthenticationToken authToken, PasswordMetrics metrics, int userId) { @@ -2935,7 +2995,7 @@ public class LockSettingsService extends ILockSettings.Stub { */ @Override public byte[] getHashFactor(LockscreenCredential currentCredential, int userId) { - checkPasswordReadPermission(userId); + checkPasswordReadPermission(); try { if (isManagedProfileWithUnifiedLock(userId)) { try { @@ -3013,7 +3073,7 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public boolean hasPendingEscrowToken(int userId) { - checkPasswordReadPermission(userId); + checkPasswordReadPermission(); synchronized (mSpManager) { return !mSpManager.getPendingTokensForUser(userId).isEmpty(); } @@ -3199,6 +3259,8 @@ public class LockSettingsService extends ILockSettings.Stub { mRebootEscrowManager.dump(pw); pw.println(); pw.decreaseIndent(); + + pw.println("PasswordHandleCount: " + mGatekeeperPasswords.size()); } /** |