diff options
-rw-r--r-- | core/java/com/android/internal/widget/LockscreenCredential.java | 10 | ||||
-rw-r--r-- | services/core/java/com/android/server/locksettings/LockSettingsService.java | 74 | ||||
-rw-r--r-- | services/core/java/com/android/server/locksettings/LockSettingsStorage.java | 4 | ||||
-rw-r--r-- | services/core/java/com/android/server/locksettings/UnifiedProfilePasswordCache.java (renamed from services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java) | 34 | ||||
-rw-r--r-- | services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java | 4 |
5 files changed, 66 insertions, 60 deletions
diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java index c88763ce6c97..18d5f6db6ac9 100644 --- a/core/java/com/android/internal/widget/LockscreenCredential.java +++ b/core/java/com/android/internal/widget/LockscreenCredential.java @@ -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); } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index c08b6d2301e8..0c2eee5aebbe 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -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> @@ -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; @@ -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); @@ -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; } @@ -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); @@ -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; } @@ -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); } } @@ -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() { @@ -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(), @@ -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); @@ -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)) { @@ -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); } @@ -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); } } } @@ -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; @@ -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)) { @@ -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); @@ -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 @@ -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); @@ -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)) { @@ -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); @@ -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); } @@ -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; } } @@ -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; } @@ -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) { diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java index 1e8b387fc189..6d123ccebc7c 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java @@ -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); } diff --git a/services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java b/services/core/java/com/android/server/locksettings/UnifiedProfilePasswordCache.java index 1298fe8f07a4..21caf76d30d0 100644 --- a/services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java +++ b/services/core/java/com/android/server/locksettings/UnifiedProfilePasswordCache.java @@ -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; } @@ -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; } 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 18961c0feef9..ee076c6bcf4b 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java @@ -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 |