Loading core/java/com/android/internal/widget/LockscreenCredential.java +5 −5 Original line number Diff line number Diff line Loading @@ -134,12 +134,12 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { } /** * Creates a LockscreenCredential object representing a managed password for profile with * unified challenge. This credentiall will have type {@code CREDENTIAL_TYPE_PASSWORD} for now. * TODO: consider add a new credential type for this. This can then supersede the * isLockTiedToParent argument in various places in LSS. * Creates a LockscreenCredential object representing the system-generated, system-managed * password for a profile with unified challenge. This credential has type {@code * CREDENTIAL_TYPE_PASSWORD} for now. TODO: consider add a new credential type for this. This * can then supersede the isLockTiedToParent argument in various places in LSS. */ public static LockscreenCredential createManagedPassword(@NonNull byte[] password) { public static LockscreenCredential createUnifiedProfilePassword(@NonNull byte[] password) { return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD, Arrays.copyOf(password, password.length), /* hasInvalidChars= */ false); } Loading services/core/java/com/android/server/locksettings/LockSettingsService.java +39 −35 Original line number Diff line number Diff line Loading @@ -209,7 +209,7 @@ import javax.crypto.spec.GCMParameterSpec; * <li>Protect each user's data using their SP. For example, use the SP to encrypt/decrypt the * user's credential-encrypted (CE) key for file-based encryption (FBE).</li> * * <li>Generate, protect, and use profile passwords for managed profiles.</li> * <li>Generate, protect, and use unified profile passwords.</li> * * <li>Support unlocking the SP by alternative means: resume-on-reboot (reboot escrow) for easier * OTA updates, and escrow tokens when set up by the Device Policy Controller (DPC).</li> Loading Loading @@ -287,7 +287,7 @@ public class LockSettingsService extends ILockSettings.Stub { private final java.security.KeyStore mJavaKeyStore; private final RecoverableKeyStoreManager mRecoverableKeyStoreManager; private ManagedProfilePasswordCache mManagedProfilePasswordCache; private final UnifiedProfilePasswordCache mUnifiedProfilePasswordCache; private final RebootEscrowManager mRebootEscrowManager; Loading Loading @@ -404,7 +404,8 @@ public class LockSettingsService extends ILockSettings.Stub { for (int i = 0; i < newPasswordChars.length; i++) { newPassword[i] = (byte) newPasswordChars[i]; } LockscreenCredential credential = LockscreenCredential.createManagedPassword(newPassword); LockscreenCredential credential = LockscreenCredential.createUnifiedProfilePassword(newPassword); Arrays.fill(newPasswordChars, '\u0000'); Arrays.fill(newPassword, (byte) 0); Arrays.fill(randomLockSeed, (byte) 0); Loading @@ -424,7 +425,7 @@ public class LockSettingsService extends ILockSettings.Stub { if (!isCredentialSharableWithParent(profileUserId)) { return; } // Do not tie profile when work challenge is enabled // Do not tie profile when separate challenge is enabled if (getSeparateProfileChallengeEnabledInternal(profileUserId)) { return; } Loading @@ -432,11 +433,14 @@ public class LockSettingsService extends ILockSettings.Stub { if (mStorage.hasChildProfileLock(profileUserId)) { return; } final UserInfo parent = mUserManager.getProfileParent(profileUserId); if (parent == null) { return; } // If parent does not have a screen lock, simply clear credential from the profile, // to maintain the invariant that unified profile should always have the same secure state // as its parent. final int parentId = mUserManager.getProfileParent(profileUserId).id; if (!isUserSecure(parentId) && !profileUserPassword.isNone()) { if (!isUserSecure(parent.id) && !profileUserPassword.isNone()) { Slogf.i(TAG, "Clearing password for profile user %d to match parent", profileUserId); setLockCredentialInternal(LockscreenCredential.createNone(), profileUserPassword, profileUserId, /* isLockTiedToParent= */ true); Loading @@ -447,7 +451,7 @@ public class LockSettingsService extends ILockSettings.Stub { // This can only happen during an upgrade path where SID is yet to be // generated when the user unlocks for the first time. try { parentSid = getGateKeeperService().getSecureUserId(parentId); parentSid = getGateKeeperService().getSecureUserId(parent.id); if (parentSid == 0) { return; } Loading @@ -458,8 +462,8 @@ public class LockSettingsService extends ILockSettings.Stub { try (LockscreenCredential unifiedProfilePassword = generateRandomProfilePassword()) { setLockCredentialInternal(unifiedProfilePassword, profileUserPassword, profileUserId, /* isLockTiedToParent= */ true); tieProfileLockToParent(profileUserId, parentId, unifiedProfilePassword); mManagedProfilePasswordCache.storePassword(profileUserId, unifiedProfilePassword, tieProfileLockToParent(profileUserId, parent.id, unifiedProfilePassword); mUnifiedProfilePasswordCache.storePassword(profileUserId, unifiedProfilePassword, parentSid); } } Loading Loading @@ -617,9 +621,9 @@ public class LockSettingsService extends ILockSettings.Stub { } } public @NonNull ManagedProfilePasswordCache getManagedProfilePasswordCache( public @NonNull UnifiedProfilePasswordCache getUnifiedProfilePasswordCache( java.security.KeyStore ks) { return new ManagedProfilePasswordCache(ks); return new UnifiedProfilePasswordCache(ks); } public boolean isHeadlessSystemUserMode() { Loading Loading @@ -662,7 +666,7 @@ public class LockSettingsService extends ILockSettings.Stub { mGatekeeperPasswords = new LongSparseArray<>(); mSpManager = injector.getSyntheticPasswordManager(mStorage); mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache(mJavaKeyStore); mUnifiedProfilePasswordCache = injector.getUnifiedProfilePasswordCache(mJavaKeyStore); mBiometricDeferredQueue = new BiometricDeferredQueue(mSpManager, mHandler); mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(), Loading @@ -686,8 +690,8 @@ public class LockSettingsService extends ILockSettings.Stub { } /** * If the account is credential-encrypted, show notification requesting the user to unlock the * device. * If the user is a managed profile whose credential-encrypted storage is locked, show a * notification requesting the user to unlock the device. */ private void maybeShowEncryptionNotificationForUser(@UserIdInt int userId, String reason) { final UserInfo user = mUserManager.getUserInfo(userId); Loading Loading @@ -843,7 +847,7 @@ public class LockSettingsService extends ILockSettings.Stub { mHandler.post(new Runnable() { @Override public void run() { // Hide notification first, as tie managed profile lock takes time // Hide notification first, as tie profile lock takes time hideEncryptionNotification(new UserHandle(userId)); if (isCredentialSharableWithParent(userId)) { Loading Loading @@ -1455,13 +1459,13 @@ public class LockSettingsService extends ILockSettings.Stub { cipher.init(Cipher.DECRYPT_MODE, decryptionKey, new GCMParameterSpec(128, iv)); decryptionResult = cipher.doFinal(encryptedPassword); LockscreenCredential credential = LockscreenCredential.createManagedPassword( LockscreenCredential credential = LockscreenCredential.createUnifiedProfilePassword( decryptionResult); Arrays.fill(decryptionResult, (byte) 0); try { long parentSid = getGateKeeperService().getSecureUserId( mUserManager.getProfileParent(userId).id); mManagedProfilePasswordCache.storePassword(userId, credential, parentSid); mUnifiedProfilePasswordCache.storePassword(userId, credential, parentSid); } catch (RemoteException e) { Slogf.w(TAG, "Failed to talk to GateKeeper service", e); } Loading Loading @@ -1547,7 +1551,7 @@ public class LockSettingsService extends ILockSettings.Stub { // so it goes into the cache getDecryptedPasswordForTiedProfile(profile.id); } catch (GeneralSecurityException | IOException e) { Slog.d(TAG, "Cache work profile password failed", e); Slog.d(TAG, "Cache unified profile password failed", e); } } } Loading Loading @@ -1601,19 +1605,19 @@ public class LockSettingsService extends ILockSettings.Stub { } /** * Synchronize all profile's work challenge of the given user if it's unified: tie or clear them * Synchronize all profile's challenge of the given user if it's unified: tie or clear them * depending on the parent user's secure state. * * When clearing tied work challenges, a pre-computed password table for profiles are required, * since changing password for profiles requires existing password, and existing passwords can * only be computed before the parent user's password is cleared. * When clearing tied challenges, a pre-computed password table for profiles are required, since * changing password for profiles requires existing password, and existing passwords can only be * computed before the parent user's password is cleared. * * Strictly this is a recursive function, since setLockCredentialInternal ends up calling this * method again on profiles. However the recursion is guaranteed to terminate as this method * terminates when the user is a profile that shares lock credentials with parent. * (e.g. managed and clone profile). */ private void synchronizeUnifiedWorkChallengeForProfiles(int userId, private void synchronizeUnifiedChallengeForProfiles(int userId, Map<Integer, LockscreenCredential> profilePasswordMap) { if (isCredentialSharableWithParent(userId)) { return; Loading @@ -1632,7 +1636,7 @@ public class LockSettingsService extends ILockSettings.Stub { tieProfileLockIfNecessary(profileUserId, LockscreenCredential.createNone()); } else { // We use cached work profile password computed before clearing the parent's // We use cached profile password computed before clearing the parent's // credential, otherwise they get lost if (profilePasswordMap != null && profilePasswordMap.containsKey(profileUserId)) { Loading Loading @@ -1774,7 +1778,7 @@ public class LockSettingsService extends ILockSettings.Stub { notifyPasswordChanged(credential, userId); } if (isCredentialSharableWithParent(userId)) { // Make sure the profile doesn't get locked straight after setting work challenge. // Make sure the profile doesn't get locked straight after setting challenge. setDeviceUnlockedForUser(userId); } notifySeparateProfileChallengeChanged(userId); Loading Loading @@ -2365,7 +2369,7 @@ public class LockSettingsService extends ILockSettings.Stub { } try { // Unlock work profile, and work profile with unified lock must use password only // Unlock profile with unified lock return doVerifyCredential(getDecryptedPasswordForTiedProfile(userId), userId, null /* progressCallback */, flags); } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException Loading Loading @@ -2489,7 +2493,7 @@ public class LockSettingsService extends ILockSettings.Stub { mStrongAuth.removeUser(userId); AndroidKeyStoreMaintenance.onUserRemoved(userId); mManagedProfilePasswordCache.removePassword(userId); mUnifiedProfilePasswordCache.removePassword(userId); gateKeeperClearSecureUserId(userId); removeKeystoreProfileKey(userId); Loading Loading @@ -2979,7 +2983,7 @@ public class LockSettingsService extends ILockSettings.Stub { credential, sp, userId); final Map<Integer, LockscreenCredential> profilePasswords; if (!credential.isNone()) { // not needed by synchronizeUnifiedWorkChallengeForProfiles() // not needed by synchronizeUnifiedChallengeForProfiles() profilePasswords = null; if (!mSpManager.hasSidForUser(userId)) { Loading @@ -2990,8 +2994,8 @@ public class LockSettingsService extends ILockSettings.Stub { } } } else { // Cache all profile password if they use unified work challenge. This will later be // used to clear the profile's password in synchronizeUnifiedWorkChallengeForProfiles() // Cache all profile password if they use unified challenge. This will later be used to // clear the profile's password in synchronizeUnifiedChallengeForProfiles(). profilePasswords = getDecryptedPasswordsForAllTiedProfiles(userId); mSpManager.clearSidForUser(userId); Loading @@ -3007,10 +3011,10 @@ public class LockSettingsService extends ILockSettings.Stub { } setCurrentLskfBasedProtectorId(newProtectorId, userId); LockPatternUtils.invalidateCredentialTypeCache(); synchronizeUnifiedWorkChallengeForProfiles(userId, profilePasswords); synchronizeUnifiedChallengeForProfiles(userId, profilePasswords); setUserPasswordMetrics(credential, userId); mManagedProfilePasswordCache.removePassword(userId); mUnifiedProfilePasswordCache.removePassword(userId); if (savedCredentialType != CREDENTIAL_TYPE_NONE) { mSpManager.destroyAllWeakTokenBasedProtectors(userId); } Loading Loading @@ -3111,7 +3115,7 @@ public class LockSettingsService extends ILockSettings.Stub { try { currentCredential = getDecryptedPasswordForTiedProfile(userId); } catch (Exception e) { Slog.e(TAG, "Failed to get work profile credential", e); Slog.e(TAG, "Failed to get unified profile password", e); return null; } } Loading Loading @@ -3281,7 +3285,7 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public boolean tryUnlockWithCachedUnifiedChallenge(int userId) { checkPasswordReadPermission(); try (LockscreenCredential cred = mManagedProfilePasswordCache.retrievePassword(userId)) { try (LockscreenCredential cred = mUnifiedProfilePasswordCache.retrievePassword(userId)) { if (cred == null) { return false; } Loading @@ -3293,7 +3297,7 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public void removeCachedUnifiedChallenge(int userId) { checkWritePermission(); mManagedProfilePasswordCache.removePassword(userId); mUnifiedProfilePasswordCache.removePassword(userId); } static String timestampToString(long timestamp) { Loading services/core/java/com/android/server/locksettings/LockSettingsStorage.java +2 −2 Original line number Diff line number Diff line Loading @@ -501,10 +501,10 @@ class LockSettingsStorage { final UserInfo parentInfo = um.getProfileParent(userId); if (parentInfo == null) { // This user owns its lock settings files - safe to delete them // Delete files specific to non-profile users. deleteFile(getRebootEscrowFile(userId)); } else { // Managed profile // Delete files specific to profile users. removeChildProfileLock(userId); } Loading services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java→services/core/java/com/android/server/locksettings/UnifiedProfilePasswordCache.java +18 −16 Original line number Diff line number Diff line Loading @@ -43,30 +43,31 @@ import javax.crypto.SecretKey; import javax.crypto.spec.GCMParameterSpec; /** * Caches *unified* work challenge for managed profiles. The cached credential is encrypted using * a keystore key auth-bound to the parent user's lockscreen credential, similar to how unified * work challenge is normally secured. * * <p> The cache is filled whenever the managed profile's unified challenge is created or derived * (as part of the parent user's credential verification flow). It's removed when the profile is * deleted or a (separate) lockscreen credential is explicitly set on the profile. There is also * an ADB command to evict the cache "cmd lock_settings remove-cache --user X", to assist * development and testing. * <p> The encrypted credential is stored in-memory only so the cache does not persist across * reboots. * An in-memory cache for unified profile passwords. A "unified profile password" is the random * password that the system automatically generates and manages for each profile that uses a unified * challenge and where the parent user has a secure lock screen. * <p> * Each password in this cache is encrypted by a Keystore key that is auth-bound to the parent user. * This is very similar to how the password is protected on-disk, but the in-memory cache uses a * much longer timeout on the keys: 7 days instead of 30 seconds. This enables use cases like * unpausing work apps without requiring authentication as frequently. * <p> * Unified profile passwords are cached when they are created, or when they are decrypted as part of * the parent user's LSKF verification flow. They are removed when the profile is deleted or when a * separate challenge is explicitly set on the profile. There is also an ADB command to evict a * cached password, "locksettings remove-cache --user X", to assist development and testing. */ @VisibleForTesting // public visibility is needed for Mockito public class ManagedProfilePasswordCache { public class UnifiedProfilePasswordCache { private static final String TAG = "ManagedProfilePasswordCache"; private static final String TAG = "UnifiedProfilePasswordCache"; private static final int KEY_LENGTH = 256; private static final int CACHE_TIMEOUT_SECONDS = (int) TimeUnit.DAYS.toSeconds(7); private final SparseArray<byte[]> mEncryptedPasswords = new SparseArray<>(); private final KeyStore mKeyStore; public ManagedProfilePasswordCache(KeyStore keyStore) { public UnifiedProfilePasswordCache(KeyStore keyStore) { mKeyStore = keyStore; } Loading Loading @@ -151,7 +152,8 @@ public class ManagedProfilePasswordCache { Slog.d(TAG, "Cannot decrypt", e); return null; } LockscreenCredential result = LockscreenCredential.createManagedPassword(credential); LockscreenCredential result = LockscreenCredential.createUnifiedProfilePassword(credential); Arrays.fill(credential, (byte) 0); return result; } Loading services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java +2 −2 Original line number Diff line number Diff line Loading @@ -140,9 +140,9 @@ public class LockSettingsServiceTestable extends LockSettingsService { } @Override public ManagedProfilePasswordCache getManagedProfilePasswordCache( public UnifiedProfilePasswordCache getUnifiedProfilePasswordCache( java.security.KeyStore ks) { return mock(ManagedProfilePasswordCache.class); return mock(UnifiedProfilePasswordCache.class); } @Override Loading Loading
core/java/com/android/internal/widget/LockscreenCredential.java +5 −5 Original line number Diff line number Diff line Loading @@ -134,12 +134,12 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { } /** * Creates a LockscreenCredential object representing a managed password for profile with * unified challenge. This credentiall will have type {@code CREDENTIAL_TYPE_PASSWORD} for now. * TODO: consider add a new credential type for this. This can then supersede the * isLockTiedToParent argument in various places in LSS. * Creates a LockscreenCredential object representing the system-generated, system-managed * password for a profile with unified challenge. This credential has type {@code * CREDENTIAL_TYPE_PASSWORD} for now. TODO: consider add a new credential type for this. This * can then supersede the isLockTiedToParent argument in various places in LSS. */ public static LockscreenCredential createManagedPassword(@NonNull byte[] password) { public static LockscreenCredential createUnifiedProfilePassword(@NonNull byte[] password) { return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD, Arrays.copyOf(password, password.length), /* hasInvalidChars= */ false); } Loading
services/core/java/com/android/server/locksettings/LockSettingsService.java +39 −35 Original line number Diff line number Diff line Loading @@ -209,7 +209,7 @@ import javax.crypto.spec.GCMParameterSpec; * <li>Protect each user's data using their SP. For example, use the SP to encrypt/decrypt the * user's credential-encrypted (CE) key for file-based encryption (FBE).</li> * * <li>Generate, protect, and use profile passwords for managed profiles.</li> * <li>Generate, protect, and use unified profile passwords.</li> * * <li>Support unlocking the SP by alternative means: resume-on-reboot (reboot escrow) for easier * OTA updates, and escrow tokens when set up by the Device Policy Controller (DPC).</li> Loading Loading @@ -287,7 +287,7 @@ public class LockSettingsService extends ILockSettings.Stub { private final java.security.KeyStore mJavaKeyStore; private final RecoverableKeyStoreManager mRecoverableKeyStoreManager; private ManagedProfilePasswordCache mManagedProfilePasswordCache; private final UnifiedProfilePasswordCache mUnifiedProfilePasswordCache; private final RebootEscrowManager mRebootEscrowManager; Loading Loading @@ -404,7 +404,8 @@ public class LockSettingsService extends ILockSettings.Stub { for (int i = 0; i < newPasswordChars.length; i++) { newPassword[i] = (byte) newPasswordChars[i]; } LockscreenCredential credential = LockscreenCredential.createManagedPassword(newPassword); LockscreenCredential credential = LockscreenCredential.createUnifiedProfilePassword(newPassword); Arrays.fill(newPasswordChars, '\u0000'); Arrays.fill(newPassword, (byte) 0); Arrays.fill(randomLockSeed, (byte) 0); Loading @@ -424,7 +425,7 @@ public class LockSettingsService extends ILockSettings.Stub { if (!isCredentialSharableWithParent(profileUserId)) { return; } // Do not tie profile when work challenge is enabled // Do not tie profile when separate challenge is enabled if (getSeparateProfileChallengeEnabledInternal(profileUserId)) { return; } Loading @@ -432,11 +433,14 @@ public class LockSettingsService extends ILockSettings.Stub { if (mStorage.hasChildProfileLock(profileUserId)) { return; } final UserInfo parent = mUserManager.getProfileParent(profileUserId); if (parent == null) { return; } // If parent does not have a screen lock, simply clear credential from the profile, // to maintain the invariant that unified profile should always have the same secure state // as its parent. final int parentId = mUserManager.getProfileParent(profileUserId).id; if (!isUserSecure(parentId) && !profileUserPassword.isNone()) { if (!isUserSecure(parent.id) && !profileUserPassword.isNone()) { Slogf.i(TAG, "Clearing password for profile user %d to match parent", profileUserId); setLockCredentialInternal(LockscreenCredential.createNone(), profileUserPassword, profileUserId, /* isLockTiedToParent= */ true); Loading @@ -447,7 +451,7 @@ public class LockSettingsService extends ILockSettings.Stub { // This can only happen during an upgrade path where SID is yet to be // generated when the user unlocks for the first time. try { parentSid = getGateKeeperService().getSecureUserId(parentId); parentSid = getGateKeeperService().getSecureUserId(parent.id); if (parentSid == 0) { return; } Loading @@ -458,8 +462,8 @@ public class LockSettingsService extends ILockSettings.Stub { try (LockscreenCredential unifiedProfilePassword = generateRandomProfilePassword()) { setLockCredentialInternal(unifiedProfilePassword, profileUserPassword, profileUserId, /* isLockTiedToParent= */ true); tieProfileLockToParent(profileUserId, parentId, unifiedProfilePassword); mManagedProfilePasswordCache.storePassword(profileUserId, unifiedProfilePassword, tieProfileLockToParent(profileUserId, parent.id, unifiedProfilePassword); mUnifiedProfilePasswordCache.storePassword(profileUserId, unifiedProfilePassword, parentSid); } } Loading Loading @@ -617,9 +621,9 @@ public class LockSettingsService extends ILockSettings.Stub { } } public @NonNull ManagedProfilePasswordCache getManagedProfilePasswordCache( public @NonNull UnifiedProfilePasswordCache getUnifiedProfilePasswordCache( java.security.KeyStore ks) { return new ManagedProfilePasswordCache(ks); return new UnifiedProfilePasswordCache(ks); } public boolean isHeadlessSystemUserMode() { Loading Loading @@ -662,7 +666,7 @@ public class LockSettingsService extends ILockSettings.Stub { mGatekeeperPasswords = new LongSparseArray<>(); mSpManager = injector.getSyntheticPasswordManager(mStorage); mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache(mJavaKeyStore); mUnifiedProfilePasswordCache = injector.getUnifiedProfilePasswordCache(mJavaKeyStore); mBiometricDeferredQueue = new BiometricDeferredQueue(mSpManager, mHandler); mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(), Loading @@ -686,8 +690,8 @@ public class LockSettingsService extends ILockSettings.Stub { } /** * If the account is credential-encrypted, show notification requesting the user to unlock the * device. * If the user is a managed profile whose credential-encrypted storage is locked, show a * notification requesting the user to unlock the device. */ private void maybeShowEncryptionNotificationForUser(@UserIdInt int userId, String reason) { final UserInfo user = mUserManager.getUserInfo(userId); Loading Loading @@ -843,7 +847,7 @@ public class LockSettingsService extends ILockSettings.Stub { mHandler.post(new Runnable() { @Override public void run() { // Hide notification first, as tie managed profile lock takes time // Hide notification first, as tie profile lock takes time hideEncryptionNotification(new UserHandle(userId)); if (isCredentialSharableWithParent(userId)) { Loading Loading @@ -1455,13 +1459,13 @@ public class LockSettingsService extends ILockSettings.Stub { cipher.init(Cipher.DECRYPT_MODE, decryptionKey, new GCMParameterSpec(128, iv)); decryptionResult = cipher.doFinal(encryptedPassword); LockscreenCredential credential = LockscreenCredential.createManagedPassword( LockscreenCredential credential = LockscreenCredential.createUnifiedProfilePassword( decryptionResult); Arrays.fill(decryptionResult, (byte) 0); try { long parentSid = getGateKeeperService().getSecureUserId( mUserManager.getProfileParent(userId).id); mManagedProfilePasswordCache.storePassword(userId, credential, parentSid); mUnifiedProfilePasswordCache.storePassword(userId, credential, parentSid); } catch (RemoteException e) { Slogf.w(TAG, "Failed to talk to GateKeeper service", e); } Loading Loading @@ -1547,7 +1551,7 @@ public class LockSettingsService extends ILockSettings.Stub { // so it goes into the cache getDecryptedPasswordForTiedProfile(profile.id); } catch (GeneralSecurityException | IOException e) { Slog.d(TAG, "Cache work profile password failed", e); Slog.d(TAG, "Cache unified profile password failed", e); } } } Loading Loading @@ -1601,19 +1605,19 @@ public class LockSettingsService extends ILockSettings.Stub { } /** * Synchronize all profile's work challenge of the given user if it's unified: tie or clear them * Synchronize all profile's challenge of the given user if it's unified: tie or clear them * depending on the parent user's secure state. * * When clearing tied work challenges, a pre-computed password table for profiles are required, * since changing password for profiles requires existing password, and existing passwords can * only be computed before the parent user's password is cleared. * When clearing tied challenges, a pre-computed password table for profiles are required, since * changing password for profiles requires existing password, and existing passwords can only be * computed before the parent user's password is cleared. * * Strictly this is a recursive function, since setLockCredentialInternal ends up calling this * method again on profiles. However the recursion is guaranteed to terminate as this method * terminates when the user is a profile that shares lock credentials with parent. * (e.g. managed and clone profile). */ private void synchronizeUnifiedWorkChallengeForProfiles(int userId, private void synchronizeUnifiedChallengeForProfiles(int userId, Map<Integer, LockscreenCredential> profilePasswordMap) { if (isCredentialSharableWithParent(userId)) { return; Loading @@ -1632,7 +1636,7 @@ public class LockSettingsService extends ILockSettings.Stub { tieProfileLockIfNecessary(profileUserId, LockscreenCredential.createNone()); } else { // We use cached work profile password computed before clearing the parent's // We use cached profile password computed before clearing the parent's // credential, otherwise they get lost if (profilePasswordMap != null && profilePasswordMap.containsKey(profileUserId)) { Loading Loading @@ -1774,7 +1778,7 @@ public class LockSettingsService extends ILockSettings.Stub { notifyPasswordChanged(credential, userId); } if (isCredentialSharableWithParent(userId)) { // Make sure the profile doesn't get locked straight after setting work challenge. // Make sure the profile doesn't get locked straight after setting challenge. setDeviceUnlockedForUser(userId); } notifySeparateProfileChallengeChanged(userId); Loading Loading @@ -2365,7 +2369,7 @@ public class LockSettingsService extends ILockSettings.Stub { } try { // Unlock work profile, and work profile with unified lock must use password only // Unlock profile with unified lock return doVerifyCredential(getDecryptedPasswordForTiedProfile(userId), userId, null /* progressCallback */, flags); } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException Loading Loading @@ -2489,7 +2493,7 @@ public class LockSettingsService extends ILockSettings.Stub { mStrongAuth.removeUser(userId); AndroidKeyStoreMaintenance.onUserRemoved(userId); mManagedProfilePasswordCache.removePassword(userId); mUnifiedProfilePasswordCache.removePassword(userId); gateKeeperClearSecureUserId(userId); removeKeystoreProfileKey(userId); Loading Loading @@ -2979,7 +2983,7 @@ public class LockSettingsService extends ILockSettings.Stub { credential, sp, userId); final Map<Integer, LockscreenCredential> profilePasswords; if (!credential.isNone()) { // not needed by synchronizeUnifiedWorkChallengeForProfiles() // not needed by synchronizeUnifiedChallengeForProfiles() profilePasswords = null; if (!mSpManager.hasSidForUser(userId)) { Loading @@ -2990,8 +2994,8 @@ public class LockSettingsService extends ILockSettings.Stub { } } } else { // Cache all profile password if they use unified work challenge. This will later be // used to clear the profile's password in synchronizeUnifiedWorkChallengeForProfiles() // Cache all profile password if they use unified challenge. This will later be used to // clear the profile's password in synchronizeUnifiedChallengeForProfiles(). profilePasswords = getDecryptedPasswordsForAllTiedProfiles(userId); mSpManager.clearSidForUser(userId); Loading @@ -3007,10 +3011,10 @@ public class LockSettingsService extends ILockSettings.Stub { } setCurrentLskfBasedProtectorId(newProtectorId, userId); LockPatternUtils.invalidateCredentialTypeCache(); synchronizeUnifiedWorkChallengeForProfiles(userId, profilePasswords); synchronizeUnifiedChallengeForProfiles(userId, profilePasswords); setUserPasswordMetrics(credential, userId); mManagedProfilePasswordCache.removePassword(userId); mUnifiedProfilePasswordCache.removePassword(userId); if (savedCredentialType != CREDENTIAL_TYPE_NONE) { mSpManager.destroyAllWeakTokenBasedProtectors(userId); } Loading Loading @@ -3111,7 +3115,7 @@ public class LockSettingsService extends ILockSettings.Stub { try { currentCredential = getDecryptedPasswordForTiedProfile(userId); } catch (Exception e) { Slog.e(TAG, "Failed to get work profile credential", e); Slog.e(TAG, "Failed to get unified profile password", e); return null; } } Loading Loading @@ -3281,7 +3285,7 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public boolean tryUnlockWithCachedUnifiedChallenge(int userId) { checkPasswordReadPermission(); try (LockscreenCredential cred = mManagedProfilePasswordCache.retrievePassword(userId)) { try (LockscreenCredential cred = mUnifiedProfilePasswordCache.retrievePassword(userId)) { if (cred == null) { return false; } Loading @@ -3293,7 +3297,7 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public void removeCachedUnifiedChallenge(int userId) { checkWritePermission(); mManagedProfilePasswordCache.removePassword(userId); mUnifiedProfilePasswordCache.removePassword(userId); } static String timestampToString(long timestamp) { Loading
services/core/java/com/android/server/locksettings/LockSettingsStorage.java +2 −2 Original line number Diff line number Diff line Loading @@ -501,10 +501,10 @@ class LockSettingsStorage { final UserInfo parentInfo = um.getProfileParent(userId); if (parentInfo == null) { // This user owns its lock settings files - safe to delete them // Delete files specific to non-profile users. deleteFile(getRebootEscrowFile(userId)); } else { // Managed profile // Delete files specific to profile users. removeChildProfileLock(userId); } Loading
services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java→services/core/java/com/android/server/locksettings/UnifiedProfilePasswordCache.java +18 −16 Original line number Diff line number Diff line Loading @@ -43,30 +43,31 @@ import javax.crypto.SecretKey; import javax.crypto.spec.GCMParameterSpec; /** * Caches *unified* work challenge for managed profiles. The cached credential is encrypted using * a keystore key auth-bound to the parent user's lockscreen credential, similar to how unified * work challenge is normally secured. * * <p> The cache is filled whenever the managed profile's unified challenge is created or derived * (as part of the parent user's credential verification flow). It's removed when the profile is * deleted or a (separate) lockscreen credential is explicitly set on the profile. There is also * an ADB command to evict the cache "cmd lock_settings remove-cache --user X", to assist * development and testing. * <p> The encrypted credential is stored in-memory only so the cache does not persist across * reboots. * An in-memory cache for unified profile passwords. A "unified profile password" is the random * password that the system automatically generates and manages for each profile that uses a unified * challenge and where the parent user has a secure lock screen. * <p> * Each password in this cache is encrypted by a Keystore key that is auth-bound to the parent user. * This is very similar to how the password is protected on-disk, but the in-memory cache uses a * much longer timeout on the keys: 7 days instead of 30 seconds. This enables use cases like * unpausing work apps without requiring authentication as frequently. * <p> * Unified profile passwords are cached when they are created, or when they are decrypted as part of * the parent user's LSKF verification flow. They are removed when the profile is deleted or when a * separate challenge is explicitly set on the profile. There is also an ADB command to evict a * cached password, "locksettings remove-cache --user X", to assist development and testing. */ @VisibleForTesting // public visibility is needed for Mockito public class ManagedProfilePasswordCache { public class UnifiedProfilePasswordCache { private static final String TAG = "ManagedProfilePasswordCache"; private static final String TAG = "UnifiedProfilePasswordCache"; private static final int KEY_LENGTH = 256; private static final int CACHE_TIMEOUT_SECONDS = (int) TimeUnit.DAYS.toSeconds(7); private final SparseArray<byte[]> mEncryptedPasswords = new SparseArray<>(); private final KeyStore mKeyStore; public ManagedProfilePasswordCache(KeyStore keyStore) { public UnifiedProfilePasswordCache(KeyStore keyStore) { mKeyStore = keyStore; } Loading Loading @@ -151,7 +152,8 @@ public class ManagedProfilePasswordCache { Slog.d(TAG, "Cannot decrypt", e); return null; } LockscreenCredential result = LockscreenCredential.createManagedPassword(credential); LockscreenCredential result = LockscreenCredential.createUnifiedProfilePassword(credential); Arrays.fill(credential, (byte) 0); return result; } Loading
services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java +2 −2 Original line number Diff line number Diff line Loading @@ -140,9 +140,9 @@ public class LockSettingsServiceTestable extends LockSettingsService { } @Override public ManagedProfilePasswordCache getManagedProfilePasswordCache( public UnifiedProfilePasswordCache getUnifiedProfilePasswordCache( java.security.KeyStore ks) { return mock(ManagedProfilePasswordCache.class); return mock(UnifiedProfilePasswordCache.class); } @Override Loading