Fix LSS unit tests and make behaviour consistent under synthetic password
1. Fix LSS unit tests: new credential initialization steps when synthetic
password is used.
2. Fix LSS behaviour under SP: If credential matches but type doesn't, treat
this as failure.
3. Fix LSS behaviour under SP: when changing credential, if old credential is
provided but is incorrect, fail instead of performing an untrusted enroll.
Bug: 63064202
Test: runtest frameworks-services -p com.android.server.locksettings
Change-Id: I762d3f4cc8fa5e4270b851721e0208c7a0f0152a
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 e9674f0..13cf9df 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -27,6 +27,7 @@
import android.app.IActivityManager;
import android.app.NotificationManager;
import android.app.admin.DevicePolicyManager;
+import android.app.trust.TrustManager;
import android.content.ComponentName;
import android.content.pm.UserInfo;
import android.os.FileUtils;
@@ -38,6 +39,7 @@
import android.security.KeyStore;
import android.test.AndroidTestCase;
+import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.LockPatternUtils;
import org.mockito.invocation.InvocationOnMock;
@@ -67,7 +69,7 @@
LockSettingsStorageTestable mStorage;
LockPatternUtils mLockPatternUtils;
- MockGateKeeperService mGateKeeperService;
+ FakeGateKeeperService mGateKeeperService;
NotificationManager mNotificationManager;
UserManager mUserManager;
FakeStorageManager mStorageManager;
@@ -80,8 +82,7 @@
protected void setUp() throws Exception {
super.setUp();
- mLockPatternUtils = mock(LockPatternUtils.class);
- mGateKeeperService = new MockGateKeeperService();
+ mGateKeeperService = new FakeGateKeeperService();
mNotificationManager = mock(NotificationManager.class);
mUserManager = mock(UserManager.class);
mStorageManager = new FakeStorageManager();
@@ -89,7 +90,7 @@
mDevicePolicyManager = mock(DevicePolicyManager.class);
mContext = new MockLockSettingsContext(getContext(), mUserManager, mNotificationManager,
- mDevicePolicyManager, mock(StorageManager.class));
+ mDevicePolicyManager, mock(StorageManager.class), mock(TrustManager.class));
mStorage = new LockSettingsStorageTestable(mContext,
new File(getContext().getFilesDir(), "locksettings"));
File storageDir = mStorage.mStorageDir;
@@ -99,6 +100,12 @@
storageDir.mkdirs();
}
+ mLockPatternUtils = new LockPatternUtils(mContext) {
+ @Override
+ public ILockSettings getLockSettings() {
+ return mService;
+ }
+ };
mSpManager = new MockSyntheticPasswordManager(mStorage, mGateKeeperService, mUserManager);
mService = new LockSettingsServiceTestable(mContext, mLockPatternUtils, mStorage,
mGateKeeperService, mKeyStore, setUpStorageManagerMock(), mActivityManager,
@@ -122,8 +129,6 @@
}
});
- when(mLockPatternUtils.getLockSettings()).thenReturn(mService);
-
// Adding a fake Device Owner app which will enable escrow token support in LSS.
when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()).thenReturn(
new ComponentName("com.dummy.package", ".FakeDeviceOwner"));
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/MockGateKeeperService.java b/services/tests/servicestests/src/com/android/server/locksettings/FakeGateKeeperService.java
similarity index 90%
rename from services/tests/servicestests/src/com/android/server/locksettings/MockGateKeeperService.java
rename to services/tests/servicestests/src/com/android/server/locksettings/FakeGateKeeperService.java
index b89c1d1..094b7af 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/MockGateKeeperService.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/FakeGateKeeperService.java
@@ -28,7 +28,7 @@
import java.util.Arrays;
import java.util.Random;
-public class MockGateKeeperService implements IGateKeeperService {
+public class FakeGateKeeperService implements IGateKeeperService {
static class VerifyHandle {
public byte[] password;
public long sid;
@@ -92,7 +92,6 @@
@Override
public GateKeeperResponse enroll(int uid, byte[] currentPasswordHandle, byte[] currentPassword,
byte[] desiredPassword) throws android.os.RemoteException {
-
if (currentPasswordHandle != null) {
VerifyHandle handle = new VerifyHandle(currentPasswordHandle);
if (Arrays.equals(currentPassword, handle.password)) {
@@ -101,17 +100,18 @@
refreshSid(uid, handle.sid, false);
handleMap.put(uid, newHandle.toBytes());
return GateKeeperResponse.createOkResponse(newHandle.toBytes(), false);
- } else {
+ } else if (currentPassword != null) {
+ // current password is provided but does not match handle, this is an error case.
return null;
}
- } else {
- // Untrusted enroll
- long newSid = new Random().nextLong();
- VerifyHandle newHandle = new VerifyHandle(desiredPassword, newSid);
- refreshSid(uid, newSid, true);
- handleMap.put(uid, newHandle.toBytes());
- return GateKeeperResponse.createOkResponse(newHandle.toBytes(), false);
+ // Fall through: password handle is provided, but no password
}
+ // Untrusted/new enrollment: generate a new SID
+ long newSid = new Random().nextLong();
+ VerifyHandle newHandle = new VerifyHandle(desiredPassword, newSid);
+ refreshSid(uid, newSid, true);
+ handleMap.put(uid, newHandle.toBytes());
+ return GateKeeperResponse.createOkResponse(newHandle.toBytes(), false);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index 3a4aa2d..0df834f 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -103,12 +103,10 @@
public int binderGetCallingUid() {
return Process.SYSTEM_UID;
}
-
-
}
protected LockSettingsServiceTestable(Context context, LockPatternUtils lockPatternUtils,
- LockSettingsStorage storage, MockGateKeeperService gatekeeper, KeyStore keystore,
+ LockSettingsStorage storage, FakeGateKeeperService gatekeeper, KeyStore keystore,
IStorageManager storageManager, IActivityManager mActivityManager,
SyntheticPasswordManager spManager) {
super(new MockInjector(context, storage, keystore, mActivityManager, lockPatternUtils,
@@ -137,4 +135,5 @@
}
return new String(storedData);
}
+
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
index cb32492..e12f6d3 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
@@ -26,12 +26,13 @@
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.service.gatekeeper.GateKeeperResponse;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.server.locksettings.LockSettingsStorage.CredentialHash;
-import com.android.server.locksettings.MockGateKeeperService.VerifyHandle;
+import com.android.server.locksettings.FakeGateKeeperService.VerifyHandle;
/**
* runtest frameworks-services -c com.android.server.locksettings.LockSettingsServiceTests
@@ -80,13 +81,6 @@
} catch (RemoteException expected) {
assertTrue(expected.getMessage().equals(FAILED_MESSAGE));
}
- try {
- mService.setLockCredential("newpwd", CREDENTIAL_TYPE_PASSWORD, null,
- PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
- fail("Did not fail when enrolling using incorrect credential");
- } catch (RemoteException expected) {
- assertTrue(expected.getMessage().equals(FAILED_MESSAGE));
- }
assertVerifyCredentials(PRIMARY_USER_ID, "password", CREDENTIAL_TYPE_PASSWORD, sid);
}
@@ -101,9 +95,10 @@
}
public void testManagedProfileUnifiedChallenge() throws RemoteException {
- final String UnifiedPassword = "testManagedProfileUnifiedChallenge-pwd";
- mService.setLockCredential(UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
- PASSWORD_QUALITY_COMPLEX, PRIMARY_USER_ID);
+ final String firstUnifiedPassword = "testManagedProfileUnifiedChallenge-pwd-1";
+ final String secondUnifiedPassword = "testManagedProfileUnifiedChallenge-pwd-2";
+ mService.setLockCredential(firstUnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ null, PASSWORD_QUALITY_COMPLEX, PRIMARY_USER_ID);
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID);
@@ -121,7 +116,7 @@
mGateKeeperService.clearAuthToken(TURNED_OFF_PROFILE_USER_ID);
// verify credential
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ firstUnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
.getResponseCode());
// Verify that we have a new auth token for the profile
@@ -137,15 +132,15 @@
*/
mStorageManager.setIgnoreBadUnlock(true);
// Change primary password and verify that profile SID remains
- mService.setLockCredential("pwd", LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
- UnifiedPassword, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
+ mService.setLockCredential(secondUnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ firstUnifiedPassword, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
mStorageManager.setIgnoreBadUnlock(false);
assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
assertNull(mGateKeeperService.getAuthToken(TURNED_OFF_PROFILE_USER_ID));
// Clear unified challenge
- mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, UnifiedPassword,
- PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
+ mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE,
+ secondUnifiedPassword, PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
assertEquals(0, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
assertEquals(0, mGateKeeperService.getSecureUserId(TURNED_OFF_PROFILE_USER_ID));
@@ -241,14 +236,21 @@
type, challenge, userId).getResponseCode());
}
- private void initializeStorageWithCredential(int userId, String credential, int type, long sid) {
+ private void initializeStorageWithCredential(int userId, String credential, int type, long sid)
+ throws RemoteException {
byte[] oldHash = new VerifyHandle(credential.getBytes(), sid).toBytes();
- if (type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD) {
- mStorage.writeCredentialHash(CredentialHash.create(oldHash,
- LockPatternUtils.CREDENTIAL_TYPE_PASSWORD), userId);
+ if (mService.shouldMigrateToSyntheticPasswordLocked(userId)) {
+ mService.initializeSyntheticPasswordLocked(oldHash, credential, type,
+ type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD ? PASSWORD_QUALITY_ALPHABETIC
+ : PASSWORD_QUALITY_SOMETHING, userId);
} else {
- mStorage.writeCredentialHash(CredentialHash.create(oldHash,
- LockPatternUtils.CREDENTIAL_TYPE_PATTERN), userId);
+ if (type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD) {
+ mStorage.writeCredentialHash(CredentialHash.create(oldHash,
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD), userId);
+ } else {
+ mStorage.writeCredentialHash(CredentialHash.create(oldHash,
+ LockPatternUtils.CREDENTIAL_TYPE_PATTERN), userId);
+ }
}
}
}
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 a0578c9..4c77f62 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
@@ -22,6 +22,7 @@
import android.app.NotificationManager;
import android.app.admin.DevicePolicyManager;
+import android.app.trust.TrustManager;
import android.content.pm.UserInfo;
import android.database.sqlite.SQLiteDatabase;
import android.os.FileUtils;
@@ -74,7 +75,7 @@
MockLockSettingsContext context = new MockLockSettingsContext(getContext(), mockUserManager,
mock(NotificationManager.class), mock(DevicePolicyManager.class),
- mock(StorageManager.class));
+ mock(StorageManager.class), mock(TrustManager.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 8da33a8..3ad30f3 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java
@@ -18,8 +18,10 @@
import android.app.NotificationManager;
import android.app.admin.DevicePolicyManager;
+import android.app.trust.TrustManager;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
import android.os.UserManager;
import android.os.storage.StorageManager;
@@ -29,15 +31,17 @@
private NotificationManager mNotificationManager;
private DevicePolicyManager mDevicePolicyManager;
private StorageManager mStorageManager;
+ private TrustManager mTrustManager;
public MockLockSettingsContext(Context base, UserManager userManager,
NotificationManager notificationManager, DevicePolicyManager devicePolicyManager,
- StorageManager storageManager) {
+ StorageManager storageManager, TrustManager trustManager) {
super(base);
mUserManager = userManager;
mNotificationManager = notificationManager;
mDevicePolicyManager = devicePolicyManager;
mStorageManager = storageManager;
+ mTrustManager = trustManager;
}
@Override
@@ -50,6 +54,8 @@
return mDevicePolicyManager;
} else if (STORAGE_SERVICE.equals(name)) {
return mStorageManager;
+ } else if (TRUST_SERVICE.equals(name)) {
+ return mTrustManager;
} else {
throw new RuntimeException("System service not mocked: " + name);
}
@@ -60,4 +66,8 @@
// Skip permission checks for unit tests.
}
+ @Override
+ public int checkCallingOrSelfPermission(String permission) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
index d7468c2..cf03593 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
@@ -32,11 +32,11 @@
public class MockSyntheticPasswordManager extends SyntheticPasswordManager {
- private MockGateKeeperService mGateKeeper;
+ private FakeGateKeeperService mGateKeeper;
private IWeaver mWeaverService;
public MockSyntheticPasswordManager(LockSettingsStorage storage,
- MockGateKeeperService gatekeeper, UserManager userManager) {
+ FakeGateKeeperService gatekeeper, UserManager userManager) {
super(storage, userManager);
mGateKeeper = gatekeeper;
}
@@ -88,7 +88,7 @@
@Override
protected long sidFromPasswordHandle(byte[] handle) {
- return new MockGateKeeperService.VerifyHandle(handle).sid;
+ return new FakeGateKeeperService.VerifyHandle(handle).sid;
}
@Override
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 ba4ff33..fd77de3 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -72,11 +72,11 @@
assertNull(result.authToken);
}
- private void disableSyntheticPassword(int userId) throws RemoteException {
+ private void disableSyntheticPassword() throws RemoteException {
mService.setLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 0, UserHandle.USER_SYSTEM);
}
- private void enableSyntheticPassword(int userId) throws RemoteException {
+ private void enableSyntheticPassword() throws RemoteException {
mService.setLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 1, UserHandle.USER_SYSTEM);
}
@@ -87,12 +87,12 @@
public void testPasswordMigration() throws RemoteException {
final String PASSWORD = "testPasswordMigration-password";
- disableSyntheticPassword(PRIMARY_USER_ID);
+ disableSyntheticPassword();
mService.setLockCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
final byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
- enableSyntheticPassword(PRIMARY_USER_ID);
+ enableSyntheticPassword();
// Performs migration
assertEquals(VerifyCredentialResponse.RESPONSE_OK,
mService.verifyCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode());
@@ -106,7 +106,7 @@
}
private void initializeCredentialUnderSP(String password, int userId) throws RemoteException {
- enableSyntheticPassword(userId);
+ enableSyntheticPassword();
int quality = password != null ? PASSWORD_QUALITY_ALPHABETIC
: PASSWORD_QUALITY_UNSPECIFIED;
int type = password != null ? LockPatternUtils.CREDENTIAL_TYPE_PASSWORD
@@ -198,8 +198,7 @@
public void testManagedProfileUnifiedChallengeMigration() throws RemoteException {
final String UnifiedPassword = "testManagedProfileUnifiedChallengeMigration-pwd";
- disableSyntheticPassword(PRIMARY_USER_ID);
- disableSyntheticPassword(MANAGED_PROFILE_USER_ID);
+ disableSyntheticPassword();
mService.setLockCredential(UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
@@ -212,8 +211,7 @@
assertTrue(profileSid != primarySid);
// do migration
- enableSyntheticPassword(PRIMARY_USER_ID);
- enableSyntheticPassword(MANAGED_PROFILE_USER_ID);
+ enableSyntheticPassword();
assertEquals(VerifyCredentialResponse.RESPONSE_OK,
mService.verifyCredential(UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode());
@@ -231,6 +229,7 @@
public void testManagedProfileSeparateChallengeMigration() throws RemoteException {
final String primaryPassword = "testManagedProfileSeparateChallengeMigration-primary";
final String profilePassword = "testManagedProfileSeparateChallengeMigration-profile";
+ disableSyntheticPassword();
mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
mService.setLockCredential(profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
@@ -244,8 +243,7 @@
assertTrue(profileSid != primarySid);
// do migration
- enableSyntheticPassword(PRIMARY_USER_ID);
- enableSyntheticPassword(MANAGED_PROFILE_USER_ID);
+ enableSyntheticPassword();
assertEquals(VerifyCredentialResponse.RESPONSE_OK,
mService.verifyCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode());
assertEquals(VerifyCredentialResponse.RESPONSE_OK,
@@ -335,7 +333,7 @@
public void testEscrowTokenActivatedImmediatelyIfNoUserPasswordNeedsMigration() throws RemoteException {
final String TOKEN = "some-high-entropy-secure-token";
- enableSyntheticPassword(PRIMARY_USER_ID);
+ enableSyntheticPassword();
long handle = mService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID);
assertTrue(mService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
@@ -355,10 +353,10 @@
final String TOKEN = "some-high-entropy-secure-token";
final String PASSWORD = "password";
// Set up pre-SP user password
- disableSyntheticPassword(PRIMARY_USER_ID);
+ disableSyntheticPassword();
mService.setLockCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
- enableSyntheticPassword(PRIMARY_USER_ID);
+ enableSyntheticPassword();
long handle = mService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID);
// Token not activated immediately since user password exists