From 308f30b8e633bc808a1df675e76fca0c4ee58484 Mon Sep 17 00:00:00 2001 From: Rubin Xu Date: Wed, 12 Apr 2017 18:02:44 +0100 Subject: [PATCH] Do not check user escrow state if synthetic password is not enabled yet 1. when adding escrow tokens, do not throw SecurityException if the user hasn't migrated to synthetic password yet. 2. during migration, activate any pending escrow tokens. 3. Disable escrow before attempting to activate escrow tokens, this currently includes whenever the user authenticates, and when synthetic password migration happens. Also, add some comments in SyntheticPasswordManager and remove an invalid call to destroy escrow data. Test: runtest frameworks-services -c com.android.server.SyntheticPasswordTests Bug: 36776133 Change-Id: If4b9d385fb43e09d90b6dde6d25b4fcc0c6b7ddc Merged-In: If4b9d385fb43e09d90b6dde6d25b4fcc0c6b7ddc --- .../android/server/LockSettingsService.java | 14 ++++++++----- .../server/SyntheticPasswordManager.java | 18 ++++++++++++++--- .../server/SyntheticPasswordTests.java | 20 +++++++++++++++++++ 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java index e26630bc4aee0..3a24091ec1c69 100644 --- a/services/core/java/com/android/server/LockSettingsService.java +++ b/services/core/java/com/android/server/LockSettingsService.java @@ -1562,8 +1562,9 @@ public class LockSettingsService extends ILockSettings.Stub { // migration to synthetic password. synchronized (mSpManager) { if (shouldMigrateToSyntheticPasswordLocked(userId)) { - initializeSyntheticPasswordLocked(storedHash.hash, credential, - storedHash.type, userId); + AuthenticationToken auth = initializeSyntheticPasswordLocked( + storedHash.hash, credential, storedHash.type, userId); + activateEscrowTokens(auth, userId); } } } @@ -2071,9 +2072,11 @@ public class LockSettingsService extends ILockSettings.Stub { pwdHandle, null, userId).authToken; } } - disableEscrowTokenOnNonManagedDevicesIfNeeded(userId); - if (!mSpManager.hasEscrowData(userId)) { - throw new SecurityException("Escrow token is disabled on the current user"); + if (isSyntheticPasswordBasedCredentialLocked(userId)) { + disableEscrowTokenOnNonManagedDevicesIfNeeded(userId); + if (!mSpManager.hasEscrowData(userId)) { + throw new SecurityException("Escrow token is disabled on the current user"); + } } long handle = mSpManager.createTokenBasedSyntheticPassword(token, userId); if (auth != null) { @@ -2085,6 +2088,7 @@ public class LockSettingsService extends ILockSettings.Stub { private void activateEscrowTokens(AuthenticationToken auth, int userId) throws RemoteException { if (DEBUG) Slog.d(TAG, "activateEscrowTokens: user=" + userId); + disableEscrowTokenOnNonManagedDevicesIfNeeded(userId); synchronized (mSpManager) { for (long handle : mSpManager.getPendingTokensForUser(userId)) { Slog.i(TAG, String.format("activateEscrowTokens: %x %d ", handle, userId)); diff --git a/services/core/java/com/android/server/SyntheticPasswordManager.java b/services/core/java/com/android/server/SyntheticPasswordManager.java index 2517613c03e03..d23584fab3723 100644 --- a/services/core/java/com/android/server/SyntheticPasswordManager.java +++ b/services/core/java/com/android/server/SyntheticPasswordManager.java @@ -48,6 +48,20 @@ import java.util.Set; * The SP has an associated password handle, which binds to the SID for that user. The password * handle is persisted by SyntheticPasswordManager internally. * If the user credential is null, it's treated as if the credential is DEFAULT_PASSWORD + * + * Information persisted on disk: + * for each user (stored under DEFAULT_HANDLE): + * SP_HANDLE_NAME: GateKeeper password handle of synthetic password. Only available if user + * credential exists, cleared when user clears their credential. + * SP_E0_NAME, SP_P1_NAME: Secret to derive synthetic password when combined with escrow + * tokens. Destroyed when escrow support is turned off for the given user. + * + * for each SP blob under the user (stored under the corresponding handle): + * SP_BLOB_NAME: The encrypted synthetic password. Always exists. + * PASSWORD_DATA_NAME: Metadata about user credential. Only exists for password based SP. + * SECDISCARDABLE_NAME: Part of the necessary ingredient to decrypt SP_BLOB_NAME for the + * purpose of secure deletion. + * */ public class SyntheticPasswordManager { private static final String SP_BLOB_NAME = "spblob"; @@ -221,7 +235,7 @@ public class SyntheticPasswordManager { * If the existing credential hash is non-null, the existing SID mill be migrated so * the synthetic password in the authentication token will produce the same SID * (the corresponding synthetic password handle is persisted by SyntheticPasswordManager - * in a per-user data storage. + * in a per-user data storage.) * * If the existing credential hash is null, it means the given user should have no SID so * SyntheticPasswordManager will nuke any SP handle previously persisted. In this case, @@ -578,8 +592,6 @@ public class SyntheticPasswordManager { private void destroySyntheticPassword(long handle, int userId) { destroyState(SP_BLOB_NAME, true, handle, userId); - destroyState(SP_E0_NAME, true, handle, userId); - destroyState(SP_P1_NAME, true, handle, userId); destroySPBlobKey(getHandleName(handle)); } diff --git a/services/tests/servicestests/src/com/android/server/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/SyntheticPasswordTests.java index 6e5ade12ce2c7..3ec71e4311557 100644 --- a/services/tests/servicestests/src/com/android/server/SyntheticPasswordTests.java +++ b/services/tests/servicestests/src/com/android/server/SyntheticPasswordTests.java @@ -320,6 +320,26 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { assertTrue(hasSyntheticPassword(PRIMARY_USER_ID)); } + public void testEscrowTokenActivatedLaterWithUserPasswordNeedsMigration() throws RemoteException { + final String TOKEN = "some-high-entropy-secure-token"; + final String PASSWORD = "password"; + // Set up pre-SP user password + disableSyntheticPassword(PRIMARY_USER_ID); + mService.setLockCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, + PRIMARY_USER_ID); + enableSyntheticPassword(PRIMARY_USER_ID); + + long handle = mService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID); + // Token not activated immediately since user password exists + assertFalse(mService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); + // Activate token (password gets migrated to SP at the same time) + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, + PRIMARY_USER_ID).getResponseCode()); + // Verify token is activated + assertTrue(mService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); + } + // b/34600579 //TODO: add non-migration work profile case, and unify/un-unify transition. //TODO: test token after user resets password