Merge "Fix insider attack resistance on headless devices"

This commit is contained in:
Paul Crowley
2023-02-03 01:53:53 +00:00
committed by Android (Google) Code Review
6 changed files with 294 additions and 56 deletions

View File

@@ -29,6 +29,7 @@ import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRY
import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_SYSTEM;
import static android.provider.DeviceConfig.NAMESPACE_AUTO_PIN_CONFIRMATION;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
@@ -70,6 +71,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.database.sqlite.SQLiteDatabase;
import android.hardware.authsecret.IAuthSecret;
@@ -219,6 +221,8 @@ public class LockSettingsService extends ILockSettings.Stub {
private static final String PROFILE_KEY_NAME_ENCRYPT = "profile_key_name_encrypt_";
private static final String PROFILE_KEY_NAME_DECRYPT = "profile_key_name_decrypt_";
private static final int HEADLESS_VENDOR_AUTH_SECRET_LENGTH = 32;
// Order of holding lock: mSeparateChallengeLock -> mSpManager -> this
// Do not call into ActivityManager while holding mSpManager lock.
private final Object mSeparateChallengeLock = new Object();
@@ -267,6 +271,13 @@ public class LockSettingsService extends ILockSettings.Stub {
@VisibleForTesting
protected boolean mHasSecureLockScreen;
@VisibleForTesting
protected final Object mHeadlessAuthSecretLock = new Object();
@VisibleForTesting
@GuardedBy("mHeadlessAuthSecretLock")
protected byte[] mAuthSecret;
protected IGateKeeperService mGateKeeperService;
protected IAuthSecret mAuthSecretService;
@@ -563,6 +574,15 @@ public class LockSettingsService extends ILockSettings.Stub {
java.security.KeyStore ks) {
return new ManagedProfilePasswordCache(ks, getUserManager());
}
public boolean isHeadlessSystemUserMode() {
return UserManager.isHeadlessSystemUserMode();
}
public boolean isMainUserPermanentAdmin() {
return Resources.getSystem()
.getBoolean(com.android.internal.R.bool.config_isMainUserPermanentAdmin);
}
}
public LockSettingsService(Context context) {
@@ -1697,7 +1717,7 @@ public class LockSettingsService extends ILockSettings.Stub {
throw new IllegalStateException("password change failed");
}
onSyntheticPasswordKnown(userId, sp);
onSyntheticPasswordUnlocked(userId, sp);
setLockCredentialWithSpLocked(credential, sp, userId);
sendCredentialsOnChangeIfRequired(credential, userId, isLockTiedToParent);
return true;
@@ -2009,7 +2029,7 @@ public class LockSettingsService extends ILockSettings.Stub {
Slogf.wtf(TAG, "Failed to unwrap synthetic password for unsecured user %d", userId);
return;
}
onSyntheticPasswordKnown(userId, result.syntheticPassword);
onSyntheticPasswordUnlocked(userId, result.syntheticPassword);
unlockUserKey(userId, result.syntheticPassword);
}
}
@@ -2602,43 +2622,112 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
private void onSyntheticPasswordKnown(@UserIdInt int userId, SyntheticPassword sp) {
private void onSyntheticPasswordCreated(@UserIdInt int userId, SyntheticPassword sp) {
onSyntheticPasswordKnown(userId, sp, true);
}
private void onSyntheticPasswordUnlocked(@UserIdInt int userId, SyntheticPassword sp) {
onSyntheticPasswordKnown(userId, sp, false);
}
private void onSyntheticPasswordKnown(
@UserIdInt int userId, SyntheticPassword sp, boolean justCreated) {
if (mInjector.isGsiRunning()) {
Slog.w(TAG, "Running in GSI; skipping calls to AuthSecret and RebootEscrow");
return;
}
mRebootEscrowManager.callToRebootEscrowIfNeeded(userId, sp.getVersion(),
sp.getSyntheticPassword());
callToAuthSecretIfNeeded(userId, sp);
mRebootEscrowManager.callToRebootEscrowIfNeeded(
userId, sp.getVersion(), sp.getSyntheticPassword());
callToAuthSecretIfNeeded(userId, sp, justCreated);
}
private void callToAuthSecretIfNeeded(@UserIdInt int userId, SyntheticPassword sp) {
// If the given user is the primary user, pass the auth secret to the HAL. Only the system
// user can be primary. Check for the system user ID before calling getUserInfo(), as other
// users may still be under construction.
/**
* Handles generation, storage, and sending of the vendor auth secret. Here we try to retrieve
* the auth secret to send it to the auth secret HAL, generate a fresh secret if need be, store
* it encrypted on disk so that the given user can unlock it in future, and stash it in memory
* so that when future users are created they can also unlock it.
*
* <p>Called whenever the SP of a user is available, except in GSI.
*/
private void callToAuthSecretIfNeeded(
@UserIdInt int userId, SyntheticPassword sp, boolean justCreated) {
if (mAuthSecretService == null) {
// If there's no IAuthSecret service, we don't need to maintain a auth secret
return;
}
if (userId == UserHandle.USER_SYSTEM &&
mUserManager.getUserInfo(userId).isPrimary()) {
final byte[] secret = sp.deriveVendorAuthSecret();
try {
mAuthSecretService.setPrimaryUserCredential(secret);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to pass primary user secret to AuthSecret HAL", e);
// User may be partially created, so use the internal user manager interface
final UserManagerInternal userManagerInternal = mInjector.getUserManagerInternal();
final UserInfo userInfo = userManagerInternal.getUserInfo(userId);
if (userInfo == null) {
// User may be partially deleted, skip this.
return;
}
final byte[] authSecret;
if (!mInjector.isHeadlessSystemUserMode()) {
// On non-headless systems, the auth secret is derived from user 0's
// SP, and only user 0 passes it to the HAL.
if (userId != USER_SYSTEM) {
return;
}
authSecret = sp.deriveVendorAuthSecret();
} else if (!mInjector.isMainUserPermanentAdmin() || !userInfo.isFull()) {
// Only full users can receive or pass on the auth secret.
// If there is no main permanent admin user, we don't try to create or send
// an auth secret, since there may sometimes be no full users.
return;
} else if (justCreated) {
if (userInfo.isMain()) {
// The first user is just being created, so we create a new auth secret
// at the same time.
Slog.i(TAG, "Generating new vendor auth secret and storing for user: " + userId);
authSecret = SecureRandomUtils.randomBytes(HEADLESS_VENDOR_AUTH_SECRET_LENGTH);
// Store it in memory, for when new users are created.
synchronized (mHeadlessAuthSecretLock) {
mAuthSecret = authSecret;
}
} else {
// A new user is being created. Another user should already have logged in at
// this point, and therefore the auth secret should be stored in memory.
synchronized (mHeadlessAuthSecretLock) {
authSecret = mAuthSecret;
}
if (authSecret == null) {
Slog.e(TAG, "Creating non-main user " + userId
+ " but vendor auth secret is not in memory");
return;
}
}
// Store the auth secret encrypted using the user's SP (which was just created).
mSpManager.writeVendorAuthSecret(authSecret, sp, userId);
} else {
// The user already exists, so the auth secret should be stored encrypted
// with that user's SP.
authSecret = mSpManager.readVendorAuthSecret(sp, userId);
if (authSecret == null) {
Slog.e(TAG, "Unable to read vendor auth secret for user: " + userId);
return;
}
// Store it in memory, for when new users are created.
synchronized (mHeadlessAuthSecretLock) {
mAuthSecret = authSecret;
}
}
Slog.i(TAG, "Sending vendor auth secret to IAuthSecret HAL as user: " + userId);
try {
mAuthSecretService.setPrimaryUserCredential(authSecret);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to send vendor auth secret to IAuthSecret HAL", e);
}
}
/**
* Creates the synthetic password (SP) for the given user, protects it with an empty LSKF, and
* protects the user's CE key with a key derived from the SP.
* <p>
* This is called just once in the lifetime of the user: at user creation time (possibly delayed
* until the time when Weaver is guaranteed to be available), or when upgrading from Android 13
* or earlier where users with no LSKF didn't necessarily have an SP.
*
* <p>This is called just once in the lifetime of the user: at user creation time (possibly
* delayed until the time when Weaver is guaranteed to be available), or when upgrading from
* Android 13 or earlier where users with no LSKF didn't necessarily have an SP.
*/
@VisibleForTesting
SyntheticPassword initializeSyntheticPassword(int userId) {
@@ -2653,7 +2742,7 @@ public class LockSettingsService extends ILockSettings.Stub {
LockscreenCredential.createNone(), sp, userId);
setCurrentLskfBasedProtectorId(protectorId, userId);
setUserKeyProtection(userId, sp.deriveFileBasedEncryptionKey());
onSyntheticPasswordKnown(userId, sp);
onSyntheticPasswordCreated(userId, sp);
return sp;
}
}
@@ -2720,7 +2809,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
mStrongAuth.reportSuccessfulStrongAuthUnlock(userId);
onSyntheticPasswordKnown(userId, sp);
onSyntheticPasswordUnlocked(userId, sp);
}
private void setDeviceUnlockedForUser(int userId) {
@@ -3008,7 +3097,7 @@ public class LockSettingsService extends ILockSettings.Stub {
+ "verification.");
return false;
}
onSyntheticPasswordKnown(userId, result.syntheticPassword);
onSyntheticPasswordUnlocked(userId, result.syntheticPassword);
setLockCredentialWithSpLocked(credential, result.syntheticPassword, userId);
return true;
}

View File

@@ -21,6 +21,7 @@ import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChang
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.admin.PasswordMetrics;
import android.content.Context;
import android.content.pm.UserInfo;
@@ -93,6 +94,9 @@ import java.util.Set;
* while the LSKF is nonempty.
* SP_E0_NAME, SP_P1_NAME: Information needed to create and use escrow token-based protectors.
* Deleted when escrow token support is disabled for the user.
* VENDOR_AUTH_SECRET_NAME: A copy of the secret passed using the IAuthSecret interface,
* encrypted using a secret derived from the SP using
* PERSONALIZATION_AUTHSECRET_ENCRYPTION_KEY.
*
* For each protector, stored under the corresponding protector ID:
* SP_BLOB_NAME: The encrypted SP secret (the SP itself or the P0 value). Always exists.
@@ -120,6 +124,7 @@ class SyntheticPasswordManager {
private static final String PASSWORD_DATA_NAME = "pwd";
private static final String WEAVER_SLOT_NAME = "weaver";
private static final String PASSWORD_METRICS_NAME = "metrics";
private static final String VENDOR_AUTH_SECRET_NAME = "vendor_auth_secret";
// used for files associated with the SP itself, not with a particular protector
public static final long NULL_PROTECTOR_ID = 0L;
@@ -158,6 +163,8 @@ class SyntheticPasswordManager {
private static final byte[] PERSONALIZATION_SP_GK_AUTH = "sp-gk-authentication".getBytes();
private static final byte[] PERSONALIZATION_FBE_KEY = "fbe-key".getBytes();
private static final byte[] PERSONALIZATION_AUTHSECRET_KEY = "authsecret-hal".getBytes();
private static final byte[] PERSONALIZATION_AUTHSECRET_ENCRYPTION_KEY =
"vendor-authsecret-encryption-key".getBytes();
private static final byte[] PERSONALIZATION_SP_SPLIT = "sp-split".getBytes();
private static final byte[] PERSONALIZATION_PASSWORD_HASH = "pw-hash".getBytes();
private static final byte[] PERSONALIZATION_E0 = "e0-encryption".getBytes();
@@ -249,6 +256,10 @@ class SyntheticPasswordManager {
return deriveSubkey(PERSONALIZATION_PASSWORD_METRICS);
}
public byte[] deriveVendorAuthSecretEncryptionKey() {
return deriveSubkey(PERSONALIZATION_AUTHSECRET_ENCRYPTION_KEY);
}
/**
* Assigns escrow data to this synthetic password. This is a prerequisite to call
* {@link SyntheticPassword#recreateFromEscrow}.
@@ -1737,4 +1748,25 @@ class SyntheticPasswordManager {
mListeners.finishBroadcast();
}
}
public void writeVendorAuthSecret(
@NonNull final byte[] vendorAuthSecret,
@NonNull final SyntheticPassword sp,
@UserIdInt final int userId) {
final byte[] encrypted =
SyntheticPasswordCrypto.encrypt(
sp.deriveVendorAuthSecretEncryptionKey(), new byte[0], vendorAuthSecret);
saveState(VENDOR_AUTH_SECRET_NAME, encrypted, NULL_PROTECTOR_ID, userId);
syncState(userId);
}
public @Nullable byte[] readVendorAuthSecret(
@NonNull final SyntheticPassword sp, @UserIdInt final int userId) {
final byte[] encrypted = loadState(VENDOR_AUTH_SECRET_NAME, NULL_PROTECTOR_ID, userId);
if (encrypted == null) {
return null;
}
return SyntheticPasswordCrypto.decrypt(
sp.deriveVendorAuthSecretEncryptionKey(), new byte[0], encrypted);
}
}

View File

@@ -49,8 +49,8 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.provider.Settings;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.security.KeyStore;
import androidx.test.InstrumentationRegistry;
@@ -83,16 +83,15 @@ public abstract class BaseLockSettingsServiceTests {
protected static final int MANAGED_PROFILE_USER_ID = 12;
protected static final int TURNED_OFF_PROFILE_USER_ID = 17;
protected static final int SECONDARY_USER_ID = 20;
protected static final int TERTIARY_USER_ID = 21;
private static final UserInfo PRIMARY_USER_INFO = new UserInfo(PRIMARY_USER_ID, null, null,
UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY
| UserInfo.FLAG_MAIN);
private static final UserInfo SECONDARY_USER_INFO = new UserInfo(SECONDARY_USER_ID, null, null,
UserInfo.FLAG_INITIALIZED);
protected UserInfo mPrimaryUserInfo;
protected UserInfo mSecondaryUserInfo;
protected UserInfo mTertiaryUserInfo;
private ArrayList<UserInfo> mPrimaryUserProfiles = new ArrayList<>();
LockSettingsService mService;
LockSettingsServiceTestable mService;
LockSettingsInternal mLocalService;
MockLockSettingsContext mContext;
@@ -117,6 +116,7 @@ public abstract class BaseLockSettingsServiceTests {
FingerprintManager mFingerprintManager;
FaceManager mFaceManager;
PackageManager mPackageManager;
LockSettingsServiceTestable.MockInjector mInjector;
@Rule
public FakeSettingsProviderRule mSettingsRule = FakeSettingsProvider.rule();
@@ -162,22 +162,61 @@ public abstract class BaseLockSettingsServiceTests {
mSpManager = new MockSyntheticPasswordManager(mContext, mStorage, mGateKeeperService,
mUserManager, mPasswordSlotManager);
mAuthSecretService = mock(IAuthSecret.class);
mService = new LockSettingsServiceTestable(mContext, mStorage,
mGateKeeperService, mKeyStore, setUpStorageManagerMock(), mActivityManager,
mSpManager, mAuthSecretService, mGsiService, mRecoverableKeyStoreManager,
mUserManagerInternal, mDeviceStateCache);
mInjector =
new LockSettingsServiceTestable.MockInjector(
mContext,
mStorage,
mKeyStore,
mActivityManager,
setUpStorageManagerMock(),
mSpManager,
mGsiService,
mRecoverableKeyStoreManager,
mUserManagerInternal,
mDeviceStateCache);
mService =
new LockSettingsServiceTestable(mInjector, mGateKeeperService, mAuthSecretService);
mService.mHasSecureLockScreen = true;
when(mUserManager.getUserInfo(eq(PRIMARY_USER_ID))).thenReturn(PRIMARY_USER_INFO);
mPrimaryUserProfiles.add(PRIMARY_USER_INFO);
mPrimaryUserInfo =
new UserInfo(
PRIMARY_USER_ID,
null,
null,
UserInfo.FLAG_INITIALIZED
| UserInfo.FLAG_ADMIN
| UserInfo.FLAG_PRIMARY
| UserInfo.FLAG_MAIN
| UserInfo.FLAG_FULL);
mSecondaryUserInfo =
new UserInfo(
SECONDARY_USER_ID,
null,
null,
UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_FULL);
mTertiaryUserInfo =
new UserInfo(
TERTIARY_USER_ID,
null,
null,
UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_FULL);
when(mUserManager.getUserInfo(eq(PRIMARY_USER_ID))).thenReturn(mPrimaryUserInfo);
when(mUserManagerInternal.getUserInfo(eq(PRIMARY_USER_ID))).thenReturn(mPrimaryUserInfo);
mPrimaryUserProfiles.add(mPrimaryUserInfo);
installChildProfile(MANAGED_PROFILE_USER_ID);
installAndTurnOffChildProfile(TURNED_OFF_PROFILE_USER_ID);
for (UserInfo profile : mPrimaryUserProfiles) {
when(mUserManager.getProfiles(eq(profile.id))).thenReturn(mPrimaryUserProfiles);
}
when(mUserManager.getUserInfo(eq(SECONDARY_USER_ID))).thenReturn(SECONDARY_USER_INFO);
when(mUserManager.getUserInfo(eq(SECONDARY_USER_ID))).thenReturn(mSecondaryUserInfo);
when(mUserManagerInternal.getUserInfo(eq(SECONDARY_USER_ID)))
.thenReturn(mSecondaryUserInfo);
when(mUserManager.getUserInfo(eq(TERTIARY_USER_ID))).thenReturn(mTertiaryUserInfo);
when(mUserManagerInternal.getUserInfo(eq(TERTIARY_USER_ID))).thenReturn(mTertiaryUserInfo);
final ArrayList<UserInfo> allUsers = new ArrayList<>(mPrimaryUserProfiles);
allUsers.add(SECONDARY_USER_INFO);
allUsers.add(mSecondaryUserInfo);
allUsers.add(mTertiaryUserInfo);
when(mUserManager.getUsers()).thenReturn(allUsers);
when(mActivityManager.unlockUser2(anyInt(), any())).thenAnswer(
@@ -227,9 +266,10 @@ public abstract class BaseLockSettingsServiceTests {
userInfo.profileGroupId = PRIMARY_USER_ID;
mPrimaryUserProfiles.add(userInfo);
when(mUserManager.getUserInfo(eq(profileId))).thenReturn(userInfo);
when(mUserManager.getProfileParent(eq(profileId))).thenReturn(PRIMARY_USER_INFO);
when(mUserManager.getProfileParent(eq(profileId))).thenReturn(mPrimaryUserInfo);
when(mUserManager.isUserRunning(eq(profileId))).thenReturn(true);
when(mUserManager.isUserUnlocked(eq(profileId))).thenReturn(true);
when(mUserManagerInternal.getUserInfo(eq(profileId))).thenReturn(userInfo);
// TODO(b/258213147): Remove
when(mUserManagerInternal.isUserManaged(eq(profileId))).thenReturn(true);
when(mDeviceStateCache.isUserOrganizationManaged(eq(profileId)))

View File

@@ -30,6 +30,7 @@ import android.os.RemoteException;
import android.os.storage.IStorageManager;
import android.security.KeyStore;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.service.gatekeeper.IGateKeeperService;
import com.android.internal.widget.LockscreenCredential;
import com.android.server.ServiceThread;
@@ -40,7 +41,7 @@ import java.io.FileNotFoundException;
public class LockSettingsServiceTestable extends LockSettingsService {
private static class MockInjector extends LockSettingsService.Injector {
public static class MockInjector extends LockSettingsService.Injector {
private LockSettingsStorage mLockSettingsStorage;
private KeyStore mKeyStore;
@@ -52,6 +53,9 @@ public class LockSettingsServiceTestable extends LockSettingsService {
private UserManagerInternal mUserManagerInternal;
private DeviceStateCache mDeviceStateCache;
public boolean mIsHeadlessSystemUserMode = false;
public boolean mIsMainUserPermanentAdmin = false;
public MockInjector(Context context, LockSettingsStorage storage, KeyStore keyStore,
IActivityManager activityManager,
IStorageManager storageManager, SyntheticPasswordManager spManager,
@@ -140,19 +144,22 @@ public class LockSettingsServiceTestable extends LockSettingsService {
return mock(ManagedProfilePasswordCache.class);
}
@Override
public boolean isHeadlessSystemUserMode() {
return mIsHeadlessSystemUserMode;
}
@Override
public boolean isMainUserPermanentAdmin() {
return mIsMainUserPermanentAdmin;
}
}
public MockInjector mInjector;
protected LockSettingsServiceTestable(Context context,
LockSettingsStorage storage, FakeGateKeeperService gatekeeper, KeyStore keystore,
IStorageManager storageManager, IActivityManager mActivityManager,
SyntheticPasswordManager spManager, IAuthSecret authSecretService,
FakeGsiService gsiService, RecoverableKeyStoreManager recoverableKeyStoreManager,
UserManagerInternal userManagerInternal, DeviceStateCache deviceStateCache) {
super(new MockInjector(context, storage, keystore, mActivityManager,
storageManager, spManager, gsiService, recoverableKeyStoreManager,
userManagerInternal, deviceStateCache));
protected LockSettingsServiceTestable(
LockSettingsService.Injector injector,
IGateKeeperService gatekeeper,
IAuthSecret authSecretService) {
super(injector);
mGateKeeperService = gatekeeper;
mAuthSecretService = authSecretService;
}
@@ -199,4 +206,10 @@ public class LockSettingsServiceTestable extends LockSettingsService {
UserInfo userInfo = mUserManager.getUserInfo(userId);
return userInfo.isCloneProfile() || userInfo.isManagedProfile();
}
void clearAuthSecret() {
synchronized (mHeadlessAuthSecretLock) {
mAuthSecret = null;
}
}
}

View File

@@ -16,6 +16,10 @@
package com.android.server.locksettings;
import static android.content.pm.UserInfo.FLAG_FULL;
import static android.content.pm.UserInfo.FLAG_MAIN;
import static android.content.pm.UserInfo.FLAG_PRIMARY;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN;
@@ -27,9 +31,9 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -247,6 +251,15 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
@Test
public void testUnlockUserKeyIfUnsecuredPassesPrimaryUserAuthSecret() throws RemoteException {
initSpAndSetCredential(PRIMARY_USER_ID, newPassword(null));
reset(mAuthSecretService);
mLocalService.unlockUserKeyIfUnsecured(PRIMARY_USER_ID);
verify(mAuthSecretService).setPrimaryUserCredential(any(byte[].class));
}
@Test
public void testUnlockUserKeyIfUnsecuredPassesPrimaryUserAuthSecretIfPasswordIsCleared()
throws RemoteException {
LockscreenCredential password = newPassword("password");
initSpAndSetCredential(PRIMARY_USER_ID, password);
mService.setLockCredential(nonePassword(), password, PRIMARY_USER_ID);
@@ -256,6 +269,56 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
verify(mAuthSecretService).setPrimaryUserCredential(any(byte[].class));
}
private void setupHeadlessTest() {
mInjector.mIsHeadlessSystemUserMode = true;
mInjector.mIsMainUserPermanentAdmin = true;
mPrimaryUserInfo.flags &= ~(FLAG_FULL | FLAG_PRIMARY);
mSecondaryUserInfo.flags |= FLAG_MAIN;
mService.initializeSyntheticPassword(PRIMARY_USER_ID);
mService.initializeSyntheticPassword(SECONDARY_USER_ID);
mService.initializeSyntheticPassword(TERTIARY_USER_ID);
reset(mAuthSecretService);
}
@Test
public void testHeadlessSystemUserDoesNotPassAuthSecret() throws RemoteException {
setupHeadlessTest();
mLocalService.unlockUserKeyIfUnsecured(PRIMARY_USER_ID);
verify(mAuthSecretService, never()).setPrimaryUserCredential(any(byte[].class));
}
@Test
public void testHeadlessSecondaryUserPassesAuthSecret() throws RemoteException {
setupHeadlessTest();
mLocalService.unlockUserKeyIfUnsecured(SECONDARY_USER_ID);
verify(mAuthSecretService).setPrimaryUserCredential(any(byte[].class));
}
@Test
public void testHeadlessTertiaryUserPassesSameAuthSecret() throws RemoteException {
setupHeadlessTest();
mLocalService.unlockUserKeyIfUnsecured(SECONDARY_USER_ID);
var captor = ArgumentCaptor.forClass(byte[].class);
verify(mAuthSecretService).setPrimaryUserCredential(captor.capture());
var value = captor.getValue();
reset(mAuthSecretService);
mLocalService.unlockUserKeyIfUnsecured(TERTIARY_USER_ID);
verify(mAuthSecretService).setPrimaryUserCredential(eq(value));
}
@Test
public void testHeadlessTertiaryUserPassesSameAuthSecretAfterReset() throws RemoteException {
setupHeadlessTest();
mLocalService.unlockUserKeyIfUnsecured(SECONDARY_USER_ID);
var captor = ArgumentCaptor.forClass(byte[].class);
verify(mAuthSecretService).setPrimaryUserCredential(captor.capture());
var value = captor.getValue();
mService.clearAuthSecret();
reset(mAuthSecretService);
mLocalService.unlockUserKeyIfUnsecured(TERTIARY_USER_ID);
verify(mAuthSecretService).setPrimaryUserCredential(eq(value));
}
@Test
public void testTokenBasedResetPassword() throws RemoteException {
LockscreenCredential password = newPassword("password");

View File

@@ -8,6 +8,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.locksettings.LockSettingsStorage.PersistentData;
import com.google.android.collect.Sets;
import org.junit.Before;
@@ -29,7 +30,7 @@ public class WeaverBasedSyntheticPasswordTests extends SyntheticPasswordTests {
// sequentially, starting at slot 0.
@Test
public void testFrpWeaverSlotNotReused() {
final int userId = 10;
final int userId = SECONDARY_USER_ID;
final int frpWeaverSlot = 0;
setDeviceProvisioned(false);
@@ -45,7 +46,7 @@ public class WeaverBasedSyntheticPasswordTests extends SyntheticPasswordTests {
// it's here as a control for testFrpWeaverSlotNotReused().
@Test
public void testFrpWeaverSlotReused() {
final int userId = 10;
final int userId = SECONDARY_USER_ID;
final int frpWeaverSlot = 0;
setDeviceProvisioned(true);