Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6a2770b9 authored by Eric Biggers's avatar Eric Biggers
Browse files

locksettings: clean up references to managed profiles

Clean up some code (comments, method names, class names) in
LockSettingsService that referred to managed profiles (or work profiles,
which are the same thing as managed profiles), when they really meant
any profile with unified challenge.  Originally only managed profiles
supported unified challenge, so this code was fine originally.  But now
"clone profiles" support unified challenge too.  There's also a plan to
add another profile type that supports unified challenge.

Bug: 214355283
Test: atest com.android.server.locksettings
Flag: exempt, mechanical refactoring
Change-Id: Ica472482983de13a19fcd9da966273e8d2e974ad
parent ada6ce94
Loading
Loading
Loading
Loading
+5 −5
Original line number Diff line number Diff line
@@ -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);
    }
+32 −31
Original line number Diff line number Diff line
@@ -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;

@@ -405,7 +405,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);
@@ -425,7 +426,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;
        }
@@ -463,7 +464,7 @@ public class LockSettingsService extends ILockSettings.Stub {
            setLockCredentialInternal(unifiedProfilePassword, profileUserPassword, profileUserId,
                    /* isLockTiedToParent= */ true);
            tieProfileLockToParent(profileUserId, parent.id, unifiedProfilePassword);
            mManagedProfilePasswordCache.storePassword(profileUserId, unifiedProfilePassword,
            mUnifiedProfilePasswordCache.storePassword(profileUserId, unifiedProfilePassword,
                    parentSid);
        }
    }
@@ -621,9 +622,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() {
@@ -666,7 +667,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(),
@@ -690,8 +691,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);
@@ -847,7 +848,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)) {
@@ -1459,13 +1460,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);
        }
@@ -1551,7 +1552,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);
                    }
                }
            }
@@ -1605,19 +1606,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;
@@ -1636,7 +1637,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)) {
@@ -1778,7 +1779,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);
@@ -2369,7 +2370,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
@@ -2493,7 +2494,7 @@ public class LockSettingsService extends ILockSettings.Stub {
        mStrongAuth.removeUser(userId);

        AndroidKeyStoreMaintenance.onUserRemoved(userId);
        mManagedProfilePasswordCache.removePassword(userId);
        mUnifiedProfilePasswordCache.removePassword(userId);

        gateKeeperClearSecureUserId(userId);
        removeKeystoreProfileKey(userId);
@@ -2983,7 +2984,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)) {
@@ -2994,8 +2995,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);
@@ -3011,10 +3012,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);
        }
@@ -3115,7 +3116,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;
                }
            }
@@ -3285,7 +3286,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;
            }
@@ -3297,7 +3298,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) {
+2 −2
Original line number Diff line number Diff line
@@ -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);
        }

+18 −16
Original line number Diff line number Diff line
@@ -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;
        }
+2 −2
Original line number Diff line number Diff line
@@ -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