From f49794b512745ca3b3a26221d36291440bc417e5 Mon Sep 17 00:00:00 2001 From: Andrew Scull Date: Fri, 13 Apr 2018 12:01:25 +0100 Subject: [PATCH] LSS: pass secret to AuthSecret HAL when no credential If there was once a credential, a secret will have been enrolled. When the credential is removed, that secret is still enrolled but still needs to be derived. This adds that derivation in the case that the secret is enrolled by the user doesn't have a credential. Bug: 77942316 Test: runtest frameworks-services -c com.android.server.locksettings.SyntheticPasswordTests Change-Id: I099a9537ab0739830a234b5f4f3721f4e8476571 --- .../locksettings/LockSettingsService.java | 33 +++++++++++++++++++ .../BaseLockSettingsServiceTests.java | 4 ++- .../LockSettingsStorageTests.java | 3 +- .../locksettings/MockLockSettingsContext.java | 8 ++++- .../locksettings/SyntheticPasswordTests.java | 32 ++++++++++++++++++ 5 files changed, 77 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 4b58d537e8449..fd4bd1d50cde2 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -584,10 +584,43 @@ public class LockSettingsService extends ILockSettings.Stub { if (mUserManager.getUserInfo(userId).isManagedProfile()) { tieManagedProfileLockIfNecessary(userId, null); } + + // If the user doesn't have a credential, try and derive their secret for the + // AuthSecret HAL. The secret will have been enrolled if the user previously set a + // credential and still needs to be passed to the HAL once that credential is + // removed. + if (mUserManager.getUserInfo(userId).isPrimary() && !isUserSecure(userId)) { + tryDeriveAuthTokenForUnsecuredPrimaryUser(userId); + } } }); } + private void tryDeriveAuthTokenForUnsecuredPrimaryUser(@UserIdInt int userId) { + synchronized (mSpManager) { + // Make sure the user has a synthetic password to derive + if (!isSyntheticPasswordBasedCredentialLocked(userId)) { + return; + } + + try { + final long handle = getSyntheticPasswordHandleLocked(userId); + final String noCredential = null; + AuthenticationResult result = + mSpManager.unwrapPasswordBasedSyntheticPassword( + getGateKeeperService(), handle, noCredential, userId, null); + if (result.authToken != null) { + Slog.i(TAG, "Retrieved auth token for user " + userId); + onAuthTokenKnownForUser(userId, result.authToken); + } else { + Slog.e(TAG, "Auth token not available for user " + userId); + } + } catch (RemoteException e) { + Slog.e(TAG, "Failure retrieving auth token", e); + } + } + } + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java index 96f81606a985c..2dc3510a82e5f 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java @@ -25,6 +25,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.app.IActivityManager; +import android.app.KeyguardManager; import android.app.NotificationManager; import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManagerInternal; @@ -102,7 +103,8 @@ public class BaseLockSettingsServiceTests extends AndroidTestCase { LocalServices.addService(DevicePolicyManagerInternal.class, mDevicePolicyManagerInternal); mContext = new MockLockSettingsContext(getContext(), mUserManager, mNotificationManager, - mDevicePolicyManager, mock(StorageManager.class), mock(TrustManager.class)); + mDevicePolicyManager, mock(StorageManager.class), mock(TrustManager.class), + mock(KeyguardManager.class)); mStorage = new LockSettingsStorageTestable(mContext, new File(getContext().getFilesDir(), "locksettings")); File storageDir = mStorage.mStorageDir; diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java index 237091de2640b..6e1f35784c7d3 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java @@ -20,6 +20,7 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import android.app.KeyguardManager; import android.app.NotificationManager; import android.app.admin.DevicePolicyManager; import android.app.trust.TrustManager; @@ -79,7 +80,7 @@ public class LockSettingsStorageTests extends AndroidTestCase { MockLockSettingsContext context = new MockLockSettingsContext(getContext(), mockUserManager, mock(NotificationManager.class), mock(DevicePolicyManager.class), - mock(StorageManager.class), mock(TrustManager.class)); + mock(StorageManager.class), mock(TrustManager.class), mock(KeyguardManager.class)); mStorage = new LockSettingsStorageTestable(context, new File(getContext().getFilesDir(), "locksettings")); mStorage.setDatabaseOnCreateCallback(new LockSettingsStorage.Callback() { diff --git a/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java b/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java index 3ad30f38595b3..b332532643172 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java @@ -16,6 +16,7 @@ package com.android.server.locksettings; +import android.app.KeyguardManager; import android.app.NotificationManager; import android.app.admin.DevicePolicyManager; import android.app.trust.TrustManager; @@ -32,16 +33,19 @@ public class MockLockSettingsContext extends ContextWrapper { private DevicePolicyManager mDevicePolicyManager; private StorageManager mStorageManager; private TrustManager mTrustManager; + private KeyguardManager mKeyguardManager; public MockLockSettingsContext(Context base, UserManager userManager, NotificationManager notificationManager, DevicePolicyManager devicePolicyManager, - StorageManager storageManager, TrustManager trustManager) { + StorageManager storageManager, TrustManager trustManager, + KeyguardManager keyguardManager) { super(base); mUserManager = userManager; mNotificationManager = notificationManager; mDevicePolicyManager = devicePolicyManager; mStorageManager = storageManager; mTrustManager = trustManager; + mKeyguardManager = keyguardManager; } @Override @@ -56,6 +60,8 @@ public class MockLockSettingsContext extends ContextWrapper { return mStorageManager; } else if (TRUST_SERVICE.equals(name)) { return mTrustManager; + } else if (KEYGUARD_SERVICE.equals(name)) { + return mKeyguardManager; } else { throw new RuntimeException("System service not mocked: " + name); } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java index e9f9800f2198c..142b950c395b6 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java @@ -217,6 +217,38 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class)); } + public void testNoSyntheticPasswordOrCredentialDoesNotPassAuthSecret() throws RemoteException { + // Setting null doesn't create a synthetic password + initializeCredentialUnderSP(null, PRIMARY_USER_ID); + + reset(mAuthSecretService); + mService.onUnlockUser(PRIMARY_USER_ID); + mService.mHandler.runWithScissors(() -> {}, 0 /*now*/); // Flush runnables on handler + verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class)); + } + + public void testSyntheticPasswordAndCredentialDoesNotPassAuthSecret() throws RemoteException { + final String PASSWORD = "passwordForASyntheticPassword"; + initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID); + + reset(mAuthSecretService); + mService.onUnlockUser(PRIMARY_USER_ID); + mService.mHandler.runWithScissors(() -> {}, 0 /*now*/); // Flush runnables on handler + verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class)); + } + + public void testSyntheticPasswordButNoCredentialPassesAuthSecret() throws RemoteException { + final String PASSWORD = "getASyntheticPassword"; + initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID); + mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, PASSWORD, + PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID); + + reset(mAuthSecretService); + mService.onUnlockUser(PRIMARY_USER_ID); + mService.mHandler.runWithScissors(() -> {}, 0 /*now*/); // Flush runnables on handler + verify(mAuthSecretService).primaryUserCredential(any(ArrayList.class)); + } + public void testManagedProfileUnifiedChallengeMigration() throws RemoteException { final String UnifiedPassword = "testManagedProfileUnifiedChallengeMigration-pwd"; disableSyntheticPassword();