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

Commit e4dc1592 authored by Eric Biggers's avatar Eric Biggers Committed by Gerrit Code Review
Browse files

Merge "LockSettingsService: fix UnlockedDeviceRequired to work without LSKF" into main

parents e597e6d5 9fece1ec
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -1936,7 +1936,8 @@ public class LockPatternUtils {
     * If the user is not secured, ie doesn't have an LSKF, then decrypt the user's synthetic
     * password and use it to unlock various cryptographic keys associated with the user.  This
     * primarily includes unlocking the user's credential-encrypted (CE) storage.  It also includes
     * deriving or decrypting the vendor auth secret and sending it to the AuthSecret HAL.
     * unlocking the user's Keystore super keys, and deriving or decrypting the vendor auth secret
     * and sending it to the AuthSecret HAL in order to unlock Secure Element firmware updates.
     * <p>
     * These tasks would normally be done when the LSKF is verified.  This method is where these
     * tasks are done when the user doesn't have an LSKF.  It's called when the user is started.
+47 −1
Original line number Diff line number Diff line
@@ -51,7 +51,7 @@ public class AndroidKeyStoreMaintenance {
     * @return 0 if successful or a {@code ResponseCode}
     * @hide
     */
    public static int onUserAdded(@NonNull int userId) {
    public static int onUserAdded(int userId) {
        StrictMode.noteDiskWrite();
        try {
            getService().onUserAdded(userId);
@@ -65,6 +65,30 @@ public class AndroidKeyStoreMaintenance {
        }
    }

    /**
     * Tells Keystore to create a user's super keys and store them encrypted by the given secret.
     *
     * @param userId - Android user id of the user
     * @param password - a secret derived from the user's synthetic password
     * @param allowExisting - true if the keys already existing should not be considered an error
     * @return 0 if successful or a {@code ResponseCode}
     * @hide
     */
    public static int initUserSuperKeys(int userId, @NonNull byte[] password,
            boolean allowExisting) {
        StrictMode.noteDiskWrite();
        try {
            getService().initUserSuperKeys(userId, password, allowExisting);
            return 0;
        } catch (ServiceSpecificException e) {
            Log.e(TAG, "initUserSuperKeys failed", e);
            return e.errorCode;
        } catch (Exception e) {
            Log.e(TAG, "Can not connect to keystore", e);
            return SYSTEM_ERROR;
        }
    }

    /**
     * Informs Keystore 2.0 about removing a user
     *
@@ -109,6 +133,28 @@ public class AndroidKeyStoreMaintenance {
        }
    }

    /**
     * Tells Keystore that a user's LSKF is being removed, ie the user's lock screen is changing to
     * Swipe or None.  Keystore uses this notification to delete the user's auth-bound keys.
     *
     * @param userId - Android user id of the user
     * @return 0 if successful or a {@code ResponseCode}
     * @hide
     */
    public static int onUserLskfRemoved(int userId) {
        StrictMode.noteDiskWrite();
        try {
            getService().onUserLskfRemoved(userId);
            return 0;
        } catch (ServiceSpecificException e) {
            Log.e(TAG, "onUserLskfRemoved failed", e);
            return e.errorCode;
        } catch (Exception e) {
            Log.e(TAG, "Can not connect to keystore", e);
            return SYSTEM_ERROR;
        }
    }

    /**
     * Informs Keystore 2.0 that an app was uninstalled and the corresponding namespace is to
     * be cleared.
+127 −26
Original line number Diff line number Diff line
@@ -243,6 +243,10 @@ public class LockSettingsService extends ILockSettings.Stub {
    private static final String MIGRATED_FRP2 = "migrated_frp2";
    private static final String MIGRATED_KEYSTORE_NS = "migrated_keystore_namespace";
    private static final String MIGRATED_SP_CE_ONLY = "migrated_all_users_to_sp_and_bound_ce";
    private static final String MIGRATED_SP_FULL = "migrated_all_users_to_sp_and_bound_keys";

    private static final boolean FIX_UNLOCKED_DEVICE_REQUIRED_KEYS =
            android.security.Flags.fixUnlockedDeviceRequiredKeys();

    // Duration that LockSettingsService will store the gatekeeper password for. This allows
    // multiple biometric enrollments without prompting the user to enter their password via
@@ -853,9 +857,11 @@ public class LockSettingsService extends ILockSettings.Stub {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) {
                if (!FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
                    // Notify keystore that a new user was added.
                    final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
                    AndroidKeyStoreMaintenance.onUserAdded(userHandle);
                }
            } else if (Intent.ACTION_USER_STARTING.equals(intent.getAction())) {
                final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
                mStorage.prefetchUser(userHandle);
@@ -1019,16 +1025,38 @@ public class LockSettingsService extends ILockSettings.Stub {
            }
            mEarlyCreatedUsers = null; // no longer needed

            // Also do a one-time migration of all users to SP-based credentials with the CE key
            // encrypted by the SP.  This is needed for the system user on the first boot of a
            // device, as the system user is special and never goes through the user creation flow
            // that other users do.  It is also needed for existing users on a device upgraded from
            // Android 13 or earlier, where users with no LSKF didn't necessarily have an SP, and if
            // they did have an SP then their CE key wasn't encrypted by it.
            // Do a one-time migration for any unsecured users: create the user's synthetic password
            // if not already done, encrypt the user's CE key with the synthetic password if not
            // already done, and create the user's Keystore super keys if not already done.
            //
            // This is needed for the following cases:
            //
            // - Finalizing the creation of the system user on the first boot of a device, as the
            //   system user is special and doesn't go through the normal user creation flow.
            //
            // If this gets interrupted (e.g. by the device powering off), there shouldn't be a
            // problem since this will run again on the next boot, and setCeStorageProtection() is
            // okay with the CE key being already protected by the given secret.
            // - Upgrading from Android 13 or earlier, where unsecured users didn't necessarily have
            //   a synthetic password, and if they did have a synthetic password their CE key wasn't
            //   encrypted by it.  Also, unsecured users didn't have Keystore super keys.
            //
            // - Upgrading from Android 14, where unsecured users didn't have Keystore super keys.
            //
            // The end result is that all users, regardless of whether they are secured or not, have
            // a synthetic password with all keys initialized and protected by it.
            //
            // Note: if this migration gets interrupted (e.g. by the device powering off), there
            // shouldn't be a problem since this will run again on the next boot, and
            // setCeStorageProtection() and initKeystoreSuperKeys(..., true) are idempotent.
            if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
                if (!getBoolean(MIGRATED_SP_FULL, false, 0)) {
                    for (UserInfo user : mUserManager.getAliveUsers()) {
                        removeStateForReusedUserIdIfNecessary(user.id, user.serialNumber);
                        synchronized (mSpManager) {
                            migrateUserToSpWithBoundKeysLocked(user.id);
                        }
                    }
                    setBoolean(MIGRATED_SP_FULL, true, 0);
                }
            } else {
                if (getString(MIGRATED_SP_CE_ONLY, null, 0) == null) {
                    for (UserInfo user : mUserManager.getAliveUsers()) {
                        removeStateForReusedUserIdIfNecessary(user.id, user.serialNumber);
@@ -1039,6 +1067,13 @@ public class LockSettingsService extends ILockSettings.Stub {
                    setString(MIGRATED_SP_CE_ONLY, "true", 0);
                }

                if (getBoolean(MIGRATED_SP_FULL, false, 0)) {
                    // The FIX_UNLOCKED_DEVICE_REQUIRED_KEYS flag was enabled but then got disabled.
                    // Ensure the full migration runs again the next time the flag is enabled...
                    setBoolean(MIGRATED_SP_FULL, false, 0);
                }
            }

            mThirdPartyAppsStarted = true;
        }
    }
@@ -1067,6 +1102,37 @@ public class LockSettingsService extends ILockSettings.Stub {
        }
    }

    @GuardedBy("mSpManager")
    private void migrateUserToSpWithBoundKeysLocked(@UserIdInt int userId) {
        if (isUserSecure(userId)) {
            Slogf.d(TAG, "User %d is secured; no migration needed", userId);
            return;
        }
        long protectorId = getCurrentLskfBasedProtectorId(userId);
        if (protectorId == SyntheticPasswordManager.NULL_PROTECTOR_ID) {
            Slogf.i(TAG, "Migrating unsecured user %d to SP-based credential", userId);
            initializeSyntheticPassword(userId);
            return;
        }
        Slogf.i(TAG, "Existing unsecured user %d has a synthetic password", userId);
        AuthenticationResult result = mSpManager.unlockLskfBasedProtector(
                getGateKeeperService(), protectorId, LockscreenCredential.createNone(), userId,
                null);
        SyntheticPassword sp = result.syntheticPassword;
        if (sp == null) {
            Slogf.wtf(TAG, "Failed to unwrap synthetic password for unsecured user %d", userId);
            return;
        }
        // While setCeStorageProtection() is idempotent, it does log some error messages when called
        // again.  Skip it if we know it was already handled by an earlier upgrade to Android 14.
        if (getString(MIGRATED_SP_CE_ONLY, null, 0) == null) {
            Slogf.i(TAG, "Encrypting CE key of user %d with synthetic password", userId);
            setCeStorageProtection(userId, sp);
        }
        Slogf.i(TAG, "Initializing Keystore super keys for user %d", userId);
        initKeystoreSuperKeys(userId, sp, /* allowExisting= */ true);
    }

    /**
     * Returns the lowest password quality that still presents the same UI for entering it.
     *
@@ -1348,6 +1414,20 @@ public class LockSettingsService extends ILockSettings.Stub {
        AndroidKeyStoreMaintenance.onUserPasswordChanged(userHandle, password);
    }

    @VisibleForTesting /** Note: this method is overridden in unit tests */
    void initKeystoreSuperKeys(@UserIdInt int userId, SyntheticPassword sp, boolean allowExisting) {
        final byte[] password = sp.deriveKeyStorePassword();
        try {
            int res = AndroidKeyStoreMaintenance.initUserSuperKeys(userId, password, allowExisting);
            if (res != 0) {
                throw new IllegalStateException("Failed to initialize Keystore super keys for user "
                        + userId);
            }
        } finally {
            Arrays.fill(password, (byte) 0);
        }
    }

    private void unlockKeystore(int userId, SyntheticPassword sp) {
        Authorization.onLockScreenEvent(false, userId, sp.deriveKeyStorePassword(), null);
    }
@@ -2071,6 +2151,9 @@ public class LockSettingsService extends ILockSettings.Stub {
                return;
            }
            onSyntheticPasswordUnlocked(userId, result.syntheticPassword);
            if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
                unlockKeystore(userId, result.syntheticPassword);
            }
            unlockCeStorage(userId, result.syntheticPassword);
        }
    }
@@ -2350,6 +2433,16 @@ public class LockSettingsService extends ILockSettings.Stub {
    }

    private void createNewUser(@UserIdInt int userId, int userSerialNumber) {

        // Delete all Keystore keys for userId, just in case any were left around from a removed
        // user with the same userId.  This should be unnecessary, but we've been doing this for a
        // long time, so for now we keep doing it just in case it's ever important.  Don't wait
        // until initKeystoreSuperKeys() to do this; that can be delayed if the user is being
        // created during early boot, and maybe something will use Keystore before then.
        if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
            AndroidKeyStoreMaintenance.onUserAdded(userId);
        }

        synchronized (mUserCreationAndRemovalLock) {
            // During early boot, don't actually create the synthetic password yet, but rather
            // automatically delay it to later.  We do this because protecting the synthetic
@@ -2756,7 +2849,7 @@ public class LockSettingsService extends ILockSettings.Stub {

    /**
     * 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.
     * protects the user's CE storage key and Keystore super keys with keys 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
@@ -2775,6 +2868,9 @@ public class LockSettingsService extends ILockSettings.Stub {
                    LockscreenCredential.createNone(), sp, userId);
            setCurrentLskfBasedProtectorId(protectorId, userId);
            setCeStorageProtection(userId, sp);
            if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
                initKeystoreSuperKeys(userId, sp, /* allowExisting= */ false);
            }
            onSyntheticPasswordCreated(userId, sp);
            Slogf.i(TAG, "Successfully initialized synthetic password for user %d", userId);
            return sp;
@@ -2867,11 +2963,10 @@ public class LockSettingsService extends ILockSettings.Stub {
    /**
     * Changes the user's LSKF by creating an LSKF-based protector that uses the new LSKF (which may
     * be empty) and replacing the old LSKF-based protector with it.  The SP itself is not changed.
     *
     * Also maintains the invariants described in {@link SyntheticPasswordManager} by
     * setting/clearing the protection (by the SP) on the user's auth-bound Keystore keys when the
     * LSKF is added/removed, respectively.  If an LSKF is being added, then the Gatekeeper auth
     * token is also refreshed.
     * <p>
     * Also maintains the invariants described in {@link SyntheticPasswordManager} by enrolling /
     * deleting the synthetic password into Gatekeeper as the LSKF is set / cleared, and asking
     * Keystore to delete the user's auth-bound keys when the LSKF is cleared.
     */
    @GuardedBy("mSpManager")
    private long setLockCredentialWithSpLocked(LockscreenCredential credential,
@@ -2890,8 +2985,10 @@ public class LockSettingsService extends ILockSettings.Stub {
            if (!mSpManager.hasSidForUser(userId)) {
                mSpManager.newSidForUser(getGateKeeperService(), sp, userId);
                mSpManager.verifyChallenge(getGateKeeperService(), sp, 0L, userId);
                if (!FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
                    setKeystorePassword(sp.deriveKeyStorePassword(), userId);
                }
            }
        } else {
            // Cache all profile password if they use unified work challenge. This will later be
            // used to clear the profile's password in synchronizeUnifiedWorkChallengeForProfiles()
@@ -2901,7 +2998,11 @@ public class LockSettingsService extends ILockSettings.Stub {
            gateKeeperClearSecureUserId(userId);
            unlockCeStorage(userId, sp);
            unlockKeystore(userId, sp);
            if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
                AndroidKeyStoreMaintenance.onUserLskfRemoved(userId);
            } else {
                setKeystorePassword(null, userId);
            }
            removeBiometricsForUser(userId);
        }
        setCurrentLskfBasedProtectorId(newProtectorId, userId);
+9 −4
Original line number Diff line number Diff line
@@ -90,10 +90,15 @@ import java.util.Set;
 *
 *  - The user's credential-encrypted storage is always protected by the SP.
 *
 *  - The user's auth-bound Keystore keys are protected by the SP, but only while an LSKF is set.
 *    This works by setting the user's Keystore and Gatekeeper passwords to SP-derived secrets, but
 *    only while an LSKF is set.  When the LSKF is removed, these passwords are cleared,
 *    invalidating the user's auth-bound keys.
 *  - The user's Keystore superencryption keys are always protected by the SP.  These in turn
 *    protect the Keystore keys that require user authentication, an unlocked device, or both.
 *
 *  - A secret derived from the synthetic password is enrolled in Gatekeeper for the user, but only
 *    while the user has a (nonempty) LSKF.  This enrollment has an associated ID called the Secure
 *    user ID or SID.  This use of Gatekeeper, which is separate from the use of GateKeeper that may
 *    be used in the LSKF-based protector, makes it so that unlocking the synthetic password
 *    generates a HardwareAuthToken (but only when the user has LSKF).  That HardwareAuthToken can
 *    be provided to KeyMint to authorize the use of the user's authentication-bound Keystore keys.
 *
 * Files stored on disk for each user:
 *   For the SP itself, stored under NULL_PROTECTOR_ID:
+5 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import android.service.gatekeeper.IGateKeeperService;

import com.android.internal.widget.LockscreenCredential;
import com.android.server.ServiceThread;
import com.android.server.locksettings.SyntheticPasswordManager.SyntheticPassword;
import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
import com.android.server.pm.UserManagerInternal;

@@ -202,6 +203,10 @@ public class LockSettingsServiceTestable extends LockSettingsService {

    }

    @Override
    void initKeystoreSuperKeys(int userId, SyntheticPassword sp, boolean allowExisting) {
    }

    @Override
    protected boolean isCredentialSharableWithParent(int userId) {
        UserInfo userInfo = mUserManager.getUserInfo(userId);