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

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

Merge changes If05d11a0,Ie67f0141 into android15-tests-dev

* changes:
  Polishing for migration to disable Weaver
  Support disabling Weaver on unsecured users
parents ddc65536 7504b7b0
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -1529,6 +1529,11 @@
        factory reset. -->
    <bool name="config_enableCredentialFactoryResetProtection">true</bool>

    <!-- If true, then work around broken Weaver HALs that don't work reliably before the device has
         fully booted. Setting this to true weakens a security feature; it should be done only when
         necessary, though it is still better than not using Weaver at all. -->
    <bool name="config_disableWeaverOnUnsecuredUsers">false</bool>

    <!-- Control the behavior when the user long presses the home button.
            0 - Nothing
            1 - Launch all apps intent
+1 −0
Original line number Diff line number Diff line
@@ -3958,6 +3958,7 @@
  <java-symbol type="string" name="foreground_service_multiple_separator" />

  <java-symbol type="bool" name="config_enableCredentialFactoryResetProtection" />
  <java-symbol type="bool" name="config_disableWeaverOnUnsecuredUsers" />

  <!-- ETWS primary messages -->
  <java-symbol type="string" name="etws_primary_default_message_earthquake" />
+92 −6
Original line number Diff line number Diff line
@@ -99,6 +99,7 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
@@ -126,6 +127,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -253,6 +255,8 @@ public class LockSettingsService extends ILockSettings.Stub {
    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 String MIGRATED_WEAVER_DISABLED_ON_UNSECURED_USERS =
            "migrated_weaver_disabled_on_unsecured_users";

    // Duration that LockSettingsService will store the gatekeeper password for. This allows
    // multiple biometric enrollments without prompting the user to enter their password via
@@ -309,6 +313,10 @@ public class LockSettingsService extends ILockSettings.Stub {
    @GuardedBy("mUserCreationAndRemovalLock")
    private boolean mThirdPartyAppsStarted;

    // This list contains the (protectorId, userId) of any protectors that were by replaced by a
    // migration and should be destroyed once rollback to the old build is no longer possible.
    private ArrayList<Pair<Long, Integer>> mProtectorsToDestroyOnBootCompleted = new ArrayList<>();

    // Current password metrics for all secured users on the device. Updated when user unlocks the
    // device or changes password. Removed if user is stopped with its CE key evicted.
    @GuardedBy("this")
@@ -363,6 +371,10 @@ public class LockSettingsService extends ILockSettings.Stub {
                mLockSettingsService.migrateOldDataAfterSystemReady();
                mLockSettingsService.deleteRepairModePersistentDataIfNeeded();
            } else if (phase == PHASE_BOOT_COMPLETED) {
                // In the case of an upgrade, PHASE_BOOT_COMPLETED means that a rollback to the old
                // build can no longer occur.  This is the time to destroy any migrated protectors.
                mLockSettingsService.destroyMigratedProtectors();

                mLockSettingsService.loadEscrowData();
            }
        }
@@ -1076,6 +1088,11 @@ public class LockSettingsService extends ILockSettings.Stub {
        mStorage.deleteRepairModePersistentData();
    }

    private boolean isWeaverDisabledOnUnsecuredUsers() {
        return mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_disableWeaverOnUnsecuredUsers);
    }

    // This is called when Weaver is guaranteed to be available (if the device supports Weaver).
    // It does any synthetic password related work that was delayed from earlier in the boot.
    private void onThirdPartyAppsStarted() {
@@ -1114,13 +1131,20 @@ public class LockSettingsService extends ILockSettings.Stub {
            //
            // - Upgrading from Android 14, where unsecured users didn't have Keystore super keys.
            //
            // - Upgrading from a build with config_disableWeaverOnUnsecuredUsers=false to one with
            //   config_disableWeaverOnUnsecuredUsers=true.  (We don't bother to proactively add
            //   Weaver for the reverse update to false, as it's too late to help in that case.)
            //
            // 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.
            // a synthetic password with all keys initialized and protected by it, and honoring
            // config_disableWeaverOnUnsecuredUsers=true when applicable.
            //
            // 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 (!getBoolean(MIGRATED_SP_FULL, false, 0)) {
            if (!getBoolean(MIGRATED_SP_FULL, false, 0)
                    || (isWeaverDisabledOnUnsecuredUsers()
                        && !getBoolean(MIGRATED_WEAVER_DISABLED_ON_UNSECURED_USERS, false, 0))) {
                for (UserInfo user : mUserManager.getAliveUsers()) {
                    removeStateForReusedUserIdIfNecessary(user.id, user.serialNumber);
                    synchronized (mSpManager) {
@@ -1128,6 +1152,9 @@ public class LockSettingsService extends ILockSettings.Stub {
                    }
                }
                setBoolean(MIGRATED_SP_FULL, true, 0);
                if (isWeaverDisabledOnUnsecuredUsers()) {
                    setBoolean(MIGRATED_WEAVER_DISABLED_ON_UNSECURED_USERS, true, 0);
                }
            }

            mThirdPartyAppsStarted = true;
@@ -1151,13 +1178,61 @@ public class LockSettingsService extends ILockSettings.Stub {
                getGateKeeperService(), protectorId, LockscreenCredential.createNone(), userId,
                null);
        SyntheticPassword sp = result.syntheticPassword;
        if (isWeaverDisabledOnUnsecuredUsers()) {
            Slog.i(TAG, "config_disableWeaverOnUnsecuredUsers=true");

            // If config_disableWeaverOnUnsecuredUsers=true, then the Weaver HAL may be buggy and
            // need multiple retries before it works here to unwrap the SP, if the SP was already
            // protected by Weaver.  Note that the problematic HAL can also deadlock if called with
            // the ActivityManagerService lock held, but that should not be a problem here since
            // that lock isn't held here, unlike unlockUserKeyIfUnsecured() where it is.
            for (int i = 0; i < 12 && sp == null; i++) {
                Slog.e(TAG, "Failed to unwrap synthetic password. Waiting 5 seconds to retry.");
                SystemClock.sleep(5000);
                result = mSpManager.unlockLskfBasedProtector(getGateKeeperService(), protectorId,
                            LockscreenCredential.createNone(), userId, null);
                sp = result.syntheticPassword;
            }
            if (sp == null) {
                throw new IllegalStateException(
                        "Failed to unwrap synthetic password for unsecured user");
            }
            // If the SP is protected by Weaver, then remove the Weaver protection in order to make
            // config_disableWeaverOnUnsecuredUsers=true take effect.
            if (result.usedWeaver) {
                Slog.i(TAG, "Removing Weaver protection from the synthetic password");
                // Create a new protector, which will not use Weaver.
                long newProtectorId = mSpManager.createLskfBasedProtector(
                        getGateKeeperService(), LockscreenCredential.createNone(), sp, userId);

                // Out of paranoia, make sure the new protector really works.
                result = mSpManager.unlockLskfBasedProtector(getGateKeeperService(),
                            newProtectorId, LockscreenCredential.createNone(), userId, null);
                sp = result.syntheticPassword;
                if (sp == null) {
                    throw new IllegalStateException("New SP protector does not work");
                }

                // Replace the protector.  Wait until PHASE_BOOT_COMPLETED to destroy the old
                // protector, since the Weaver slot erasure and freeing cannot be rolled back.
                setCurrentLskfBasedProtectorId(newProtectorId, userId);
                mProtectorsToDestroyOnBootCompleted.add(new Pair(protectorId, userId));
            } else {
                Slog.i(TAG, "Synthetic password is already not protected by Weaver");
            }
        } else 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) {

        // Call setCeStorageProtection(), to re-encrypt the CE key with the SP if it's currently
        // encrypted by an empty secret.  Skip this if it was definitely already done as part of the
        // upgrade to Android 14, since while setCeStorageProtection() is idempotent it does log
        // some error messages when called again.  Do not skip this if
        // config_disableWeaverOnUnsecuredUsers=true, since in that case we'd like to recover from
        // the case where an earlier upgrade to Android 14 incorrectly skipped this step.
        if (getString(MIGRATED_SP_CE_ONLY, null, 0) == null
                || isWeaverDisabledOnUnsecuredUsers()) {
            Slogf.i(TAG, "Encrypting CE key of user %d with synthetic password", userId);
            setCeStorageProtection(userId, sp);
        }
@@ -1165,6 +1240,17 @@ public class LockSettingsService extends ILockSettings.Stub {
        initKeystoreSuperKeys(userId, sp, /* allowExisting= */ true);
    }

    private void destroyMigratedProtectors() {
        if (!mProtectorsToDestroyOnBootCompleted.isEmpty()) {
            synchronized (mSpManager) {
                for (Pair<Long, Integer> pair : mProtectorsToDestroyOnBootCompleted) {
                    mSpManager.destroyLskfBasedProtector(pair.first, pair.second);
                }
            }
        }
        mProtectorsToDestroyOnBootCompleted = null; // The list is no longer needed.
    }

    /**
     * Returns the lowest password quality that still presents the same UI for entering it.
     *
+15 −1
Original line number Diff line number Diff line
@@ -195,6 +195,8 @@ class SyntheticPasswordManager {
        // ERROR: password / token fails verification
        // RETRY: password / token verification is throttled at the moment.
        @Nullable public VerifyCredentialResponse gkResponse;
        // For unlockLskfBasedProtector() this is set to true if the protector uses Weaver.
        public boolean usedWeaver;
    }

    /**
@@ -532,6 +534,11 @@ class SyntheticPasswordManager {
                Settings.Global.DEVICE_PROVISIONED, 0) != 0;
    }

    private boolean isWeaverDisabledOnUnsecuredUsers() {
        return mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_disableWeaverOnUnsecuredUsers);
    }

    @VisibleForTesting
    protected android.hardware.weaver.V1_0.IWeaver getWeaverHidlService() throws RemoteException {
        try {
@@ -1011,7 +1018,13 @@ class SyntheticPasswordManager {

        Slogf.i(TAG, "Creating LSKF-based protector %016x for user %d", protectorId, userId);

        final IWeaver weaver = getWeaverService();
        final IWeaver weaver;
        if (credential.isNone() && isWeaverDisabledOnUnsecuredUsers()) {
            weaver = null;
            Slog.w(TAG, "Not using Weaver for unsecured user (disabled by config)");
        } else {
            weaver = getWeaverService();
        }
        if (weaver != null) {
            // Weaver is available, so make the protector use it to verify the LSKF.  Do this even
            // if the LSKF is empty, as that gives us support for securely deleting the protector.
@@ -1404,6 +1417,7 @@ class SyntheticPasswordManager {
        int weaverSlot = loadWeaverSlot(protectorId, userId);
        if (weaverSlot != INVALID_WEAVER_SLOT) {
            // Protector uses Weaver to verify the LSKF
            result.usedWeaver = true;
            final IWeaver weaver = getWeaverService();
            if (weaver == null) {
                Slog.e(TAG, "Protector uses Weaver, but Weaver is unavailable");
+44 −0
Original line number Diff line number Diff line
package com.android.server.locksettings;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;

import android.platform.test.annotations.Presubmit;

@@ -56,4 +60,44 @@ public class WeaverBasedSyntheticPasswordTests extends SyntheticPasswordTests {
        mService.initializeSyntheticPassword(userId); // This should allocate a Weaver slot.
        assertEquals(Sets.newHashSet(0), mPasswordSlotManager.getUsedSlots());
    }

    private int getNumUsedWeaverSlots() {
        return mPasswordSlotManager.getUsedSlots().size();
    }

    @Test
    public void testDisableWeaverOnUnsecuredUsers_false() {
        final int userId = PRIMARY_USER_ID;
        when(mResources.getBoolean(eq(
                        com.android.internal.R.bool.config_disableWeaverOnUnsecuredUsers)))
                .thenReturn(false);
        assertEquals(0, getNumUsedWeaverSlots());
        mService.initializeSyntheticPassword(userId);
        assertEquals(1, getNumUsedWeaverSlots());
        assertTrue(mService.setLockCredential(newPassword("password"), nonePassword(), userId));
        assertEquals(1, getNumUsedWeaverSlots());
        assertTrue(mService.setLockCredential(nonePassword(), newPassword("password"), userId));
        assertEquals(1, getNumUsedWeaverSlots());
    }

    @Test
    public void testDisableWeaverOnUnsecuredUsers_true() {
        final int userId = PRIMARY_USER_ID;
        when(mResources.getBoolean(eq(
                        com.android.internal.R.bool.config_disableWeaverOnUnsecuredUsers)))
                .thenReturn(true);
        assertEquals(0, getNumUsedWeaverSlots());
        mService.initializeSyntheticPassword(userId);
        assertEquals(0, getNumUsedWeaverSlots());
        assertTrue(mService.setLockCredential(newPassword("password"), nonePassword(), userId));
        assertEquals(1, getNumUsedWeaverSlots());
        assertTrue(mService.setLockCredential(nonePassword(), newPassword("password"), userId));
        assertEquals(0, getNumUsedWeaverSlots());
    }

    @Test
    public void testDisableWeaverOnUnsecuredUsers_defaultsToFalse() {
        assertFalse(mResources.getBoolean(
                    com.android.internal.R.bool.config_disableWeaverOnUnsecuredUsers));
    }
}