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

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

Merge "Fix UnlockedDeviceRequired with weak unlock methods" into main

parents a0536547 cd7f4fad
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -100,12 +100,14 @@ public class Authorization {
     *
     * @param userId - the user's Android user ID
     * @param unlockingSids - list of biometric SIDs with which the device may be unlocked again
     * @param weakUnlockEnabled - true if non-strong biometric or trust agent unlock is enabled
     * @return 0 if successful or a {@code ResponseCode}.
     */
    public static int onDeviceLocked(int userId, @NonNull long[] unlockingSids) {
    public static int onDeviceLocked(int userId, @NonNull long[] unlockingSids,
            boolean weakUnlockEnabled) {
        StrictMode.noteDiskWrite();
        try {
            getService().onDeviceLocked(userId, unlockingSids);
            getService().onDeviceLocked(userId, unlockingSids, weakUnlockEnabled);
            return 0;
        } catch (RemoteException | NullPointerException e) {
            Log.w(TAG, "Can not connect to keystore", e);
+70 −8
Original line number Diff line number Diff line
@@ -44,6 +44,9 @@ import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -157,6 +160,8 @@ public class TrustManagerService extends SystemService {
    private final LockPatternUtils mLockPatternUtils;
    private final UserManager mUserManager;
    private final ActivityManager mActivityManager;
    private FingerprintManager mFingerprintManager;
    private FaceManager mFaceManager;
    private VirtualDeviceManagerInternal mVirtualDeviceManager;

    private enum TrustState {
@@ -294,6 +299,8 @@ public class TrustManagerService extends SystemService {
            mPackageMonitor.register(mContext, mHandler.getLooper(), UserHandle.ALL, true);
            mReceiver.register(mContext);
            mLockPatternUtils.registerStrongAuthTracker(mStrongAuthTracker);
            mFingerprintManager = mContext.getSystemService(FingerprintManager.class);
            mFaceManager = mContext.getSystemService(FaceManager.class);
        } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
            mTrustAgentsCanRun = true;
            refreshAgentList(UserHandle.USER_ALL);
@@ -895,7 +902,19 @@ public class TrustManagerService extends SystemService {

    private void notifyKeystoreOfDeviceLockState(int userId, boolean isLocked) {
        if (isLocked) {
            Authorization.onDeviceLocked(userId, getBiometricSids(userId));
            if (android.security.Flags.fixUnlockedDeviceRequiredKeysV2()) {
                // A profile with unified challenge is unlockable not by its own biometrics and
                // trust agents, but rather by those of the parent user.  Therefore, when protecting
                // the profile's UnlockedDeviceRequired keys, we must use the parent's list of
                // biometric SIDs and weak unlock methods, not the profile's.
                int authUserId = mLockPatternUtils.isProfileWithUnifiedChallenge(userId)
                        ? resolveProfileParent(userId) : userId;

                Authorization.onDeviceLocked(userId, getBiometricSids(authUserId),
                        isWeakUnlockMethodEnabled(authUserId));
            } else {
                Authorization.onDeviceLocked(userId, getBiometricSids(userId), false);
            }
        } else {
            // Notify Keystore that the device is now unlocked for the user.  Note that for unlocks
            // with LSKF, this is redundant with the call from LockSettingsService which provides
@@ -1442,16 +1461,59 @@ public class TrustManagerService extends SystemService {
        if (biometricManager == null) {
            return new long[0];
        }
        if (android.security.Flags.fixUnlockedDeviceRequiredKeysV2()
                && mLockPatternUtils.isProfileWithUnifiedChallenge(userId)) {
            // Profiles with unified challenge have their own set of biometrics, but the device
            // unlock happens via the parent user.  In this case Keystore needs to be given the list
            // of biometric SIDs from the parent user, not the profile.
            userId = resolveProfileParent(userId);
        }
        return biometricManager.getAuthenticatorIds(userId);
    }

    // Returns whether the device can become unlocked for the specified user via one of that user's
    // non-strong biometrics or trust agents.  This assumes that the device is currently locked, or
    // is becoming locked, for the user.
    private boolean isWeakUnlockMethodEnabled(int userId) {

        // Check whether the system currently allows the use of non-strong biometrics for the user,
        // *and* the user actually has a non-strong biometric enrolled.
        //
        // The biometrics framework ostensibly supports multiple sensors per modality.  However,
        // that feature is unused and untested.  So, we simply consider one sensor per modality.
        //
        // Also, currently we just consider fingerprint and face, matching Keyguard.  If Keyguard
        // starts supporting other biometric modalities, this will need to be updated.
        if (mStrongAuthTracker.isBiometricAllowedForUser(/* isStrongBiometric= */ false, userId)) {
            DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager();
            int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, userId);

            if (mFingerprintManager != null
                    && (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT) == 0
                    && mFingerprintManager.hasEnrolledTemplates(userId)
                    && isWeakOrConvenienceSensor(
                            mFingerprintManager.getSensorProperties().get(0))) {
                Slog.i(TAG, "User is unlockable by non-strong fingerprint auth");
                return true;
            }

            if (mFaceManager != null
                    && (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_FACE) == 0
                    && mFaceManager.hasEnrolledTemplates(userId)
                    && isWeakOrConvenienceSensor(mFaceManager.getSensorProperties().get(0))) {
                Slog.i(TAG, "User is unlockable by non-strong face auth");
                return true;
            }
        }

        // Check whether it's possible for the device to be actively unlocked by a trust agent.
        if (getUserTrustStateInner(userId) == TrustState.TRUSTABLE
                || (isAutomotive() && isTrustUsuallyManagedInternal(userId))) {
            Slog.i(TAG, "User is unlockable by trust agent");
            return true;
        }

        return false;
    }

    private static boolean isWeakOrConvenienceSensor(SensorProperties sensor) {
        return sensor.getSensorStrength() == SensorProperties.STRENGTH_WEAK
                || sensor.getSensorStrength() == SensorProperties.STRENGTH_CONVENIENCE;
    }

    // User lifecycle

    @Override
+201 −5
Original line number Diff line number Diff line
@@ -47,6 +47,10 @@ import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorProperties;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
@@ -69,6 +73,7 @@ import android.view.WindowManagerGlobal;
import androidx.test.core.app.ApplicationProvider;

import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker;
import com.android.modules.utils.testing.ExtendedMockitoRule;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -105,6 +110,8 @@ public class TrustManagerServiceTest {

    private static final String URI_SCHEME_PACKAGE = "package";
    private static final int TEST_USER_ID = 50;
    private static final UserInfo TEST_USER =
            new UserInfo(TEST_USER_ID, "user", UserInfo.FLAG_FULL);
    private static final int PARENT_USER_ID = 60;
    private static final int PROFILE_USER_ID = 70;
    private static final long[] PARENT_BIOMETRIC_SIDS = new long[] { 600L, 601L };
@@ -117,6 +124,8 @@ public class TrustManagerServiceTest {
    private @Mock ActivityManager mActivityManager;
    private @Mock BiometricManager mBiometricManager;
    private @Mock DevicePolicyManager mDevicePolicyManager;
    private @Mock FaceManager mFaceManager;
    private @Mock FingerprintManager mFingerprintManager;
    private @Mock IKeystoreAuthorization mKeystoreAuthorization;
    private @Mock LockPatternUtils mLockPatternUtils;
    private @Mock PackageManager mPackageManager;
@@ -133,6 +142,9 @@ public class TrustManagerServiceTest {
        when(mActivityManager.isUserRunning(TEST_USER_ID)).thenReturn(true);
        doReturn(mock(IActivityManager.class)).when(() -> ActivityManager.getService());

        when(mFaceManager.getSensorProperties()).thenReturn(List.of());
        when(mFingerprintManager.getSensorProperties()).thenReturn(List.of());

        doReturn(mKeystoreAuthorization).when(() -> Authorization.getService());

        when(mLockPatternUtils.getDevicePolicyManager()).thenReturn(mDevicePolicyManager);
@@ -161,13 +173,16 @@ public class TrustManagerServiceTest {
        when(mPackageManager.checkPermission(any(), any())).thenReturn(
                PackageManager.PERMISSION_GRANTED);

        when(mUserManager.getAliveUsers()).thenReturn(
                List.of(new UserInfo(TEST_USER_ID, "user", UserInfo.FLAG_FULL)));
        when(mUserManager.getAliveUsers()).thenReturn(List.of(TEST_USER));
        when(mUserManager.getEnabledProfileIds(TEST_USER_ID)).thenReturn(new int[0]);
        when(mUserManager.getUserInfo(TEST_USER_ID)).thenReturn(TEST_USER);

        when(mWindowManager.isKeyguardLocked()).thenReturn(true);

        mMockContext.addMockSystemService(ActivityManager.class, mActivityManager);
        mMockContext.addMockSystemService(BiometricManager.class, mBiometricManager);
        mMockContext.addMockSystemService(FaceManager.class, mFaceManager);
        mMockContext.addMockSystemService(FingerprintManager.class, mFingerprintManager);
        mMockContext.setMockPackageManager(mPackageManager);
        mMockContext.addMockSystemService(UserManager.class, mUserManager);
        doReturn(mWindowManager).when(() -> WindowManagerGlobal.getWindowManagerService());
@@ -362,9 +377,9 @@ public class TrustManagerServiceTest {
        when(mWindowManager.isKeyguardLocked()).thenReturn(true);
        mTrustManager.reportKeyguardShowingChanged();
        verify(mKeystoreAuthorization)
                .onDeviceLocked(eq(PARENT_USER_ID), eq(PARENT_BIOMETRIC_SIDS));
                .onDeviceLocked(eq(PARENT_USER_ID), eq(PARENT_BIOMETRIC_SIDS), eq(false));
        verify(mKeystoreAuthorization)
                .onDeviceLocked(eq(PROFILE_USER_ID), eq(PARENT_BIOMETRIC_SIDS));
                .onDeviceLocked(eq(PROFILE_USER_ID), eq(PARENT_BIOMETRIC_SIDS), eq(false));
    }

    // Tests that when the device is locked for a managed profile with a *separate* challenge, the
@@ -381,7 +396,188 @@ public class TrustManagerServiceTest {

        mTrustManager.setDeviceLockedForUser(PROFILE_USER_ID, true);
        verify(mKeystoreAuthorization)
                .onDeviceLocked(eq(PROFILE_USER_ID), eq(PROFILE_BIOMETRIC_SIDS));
                .onDeviceLocked(eq(PROFILE_USER_ID), eq(PROFILE_BIOMETRIC_SIDS), eq(false));
    }

    @Test
    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
    public void testKeystoreWeakUnlockEnabled_whenWeakFingerprintIsSetupAndAllowed()
            throws Exception {
        setupStrongAuthTrackerToAllowEverything();
        setupFingerprint(SensorProperties.STRENGTH_WEAK);
        verifyWeakUnlockEnabled();
    }

    @Test
    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
    public void testKeystoreWeakUnlockEnabled_whenWeakFaceIsSetupAndAllowed() throws Exception {
        setupStrongAuthTrackerToAllowEverything();
        setupFace(SensorProperties.STRENGTH_WEAK);
        verifyWeakUnlockEnabled();
    }

    @Test
    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
    public void testKeystoreWeakUnlockEnabled_whenConvenienceFingerprintIsSetupAndAllowed()
            throws Exception {
        setupStrongAuthTrackerToAllowEverything();
        setupFingerprint(SensorProperties.STRENGTH_CONVENIENCE);
        verifyWeakUnlockEnabled();
    }

    @Test
    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
    public void testKeystoreWeakUnlockEnabled_whenConvenienceFaceIsSetupAndAllowed()
            throws Exception {
        setupStrongAuthTrackerToAllowEverything();
        setupFace(SensorProperties.STRENGTH_CONVENIENCE);
        verifyWeakUnlockEnabled();
    }

    @Test
    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
    public void testKeystoreWeakUnlockDisabled_whenStrongAuthRequired() throws Exception {
        setupStrongAuthTracker(StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN, true);
        setupFace(SensorProperties.STRENGTH_WEAK);
        verifyWeakUnlockDisabled();
    }

    @Test
    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
    public void testKeystoreWeakUnlockDisabled_whenNonStrongBiometricNotAllowed() throws Exception {
        setupStrongAuthTracker(StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED,
                /* isNonStrongBiometricAllowed= */ false);
        setupFace(SensorProperties.STRENGTH_WEAK);
        verifyWeakUnlockDisabled();
    }

    @Test
    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
    public void testKeystoreWeakUnlockDisabled_whenWeakFingerprintSensorIsPresentButNotEnrolled()
            throws Exception {
        setupStrongAuthTrackerToAllowEverything();
        setupFingerprint(SensorProperties.STRENGTH_WEAK, /* enrolled= */ false);
        verifyWeakUnlockDisabled();
    }

    @Test
    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
    public void testKeystoreWeakUnlockDisabled_whenWeakFaceSensorIsPresentButNotEnrolled()
            throws Exception {
        setupStrongAuthTrackerToAllowEverything();
        setupFace(SensorProperties.STRENGTH_WEAK, /* enrolled= */ false);
        verifyWeakUnlockDisabled();
    }

    @Test
    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
    public void
            testKeystoreWeakUnlockDisabled_whenWeakFingerprintIsSetupButForbiddenByDevicePolicy()
            throws Exception {
        setupStrongAuthTrackerToAllowEverything();
        setupFingerprint(SensorProperties.STRENGTH_WEAK);
        when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, TEST_USER_ID))
                .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
        verifyWeakUnlockDisabled();
    }

    @Test
    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
    public void testKeystoreWeakUnlockDisabled_whenWeakFaceIsSetupButForbiddenByDevicePolicy()
            throws Exception {
        setupStrongAuthTrackerToAllowEverything();
        setupFace(SensorProperties.STRENGTH_WEAK);
        when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, TEST_USER_ID))
                .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FACE);
        verifyWeakUnlockDisabled();
    }

    @Test
    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
    public void testKeystoreWeakUnlockDisabled_whenOnlyStrongFingerprintIsSetup() throws Exception {
        setupStrongAuthTrackerToAllowEverything();
        setupFingerprint(SensorProperties.STRENGTH_STRONG);
        verifyWeakUnlockDisabled();
    }

    @Test
    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
    public void testKeystoreWeakUnlockDisabled_whenOnlyStrongFaceIsSetup() throws Exception {
        setupStrongAuthTrackerToAllowEverything();
        setupFace(SensorProperties.STRENGTH_STRONG);
        verifyWeakUnlockDisabled();
    }

    @Test
    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
    public void testKeystoreWeakUnlockDisabled_whenNoBiometricsAreSetup() throws Exception {
        setupStrongAuthTrackerToAllowEverything();
        verifyWeakUnlockDisabled();
    }

    private void setupStrongAuthTrackerToAllowEverything() throws Exception {
        setupStrongAuthTracker(StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED, true);
    }

    private void setupStrongAuthTracker(int strongAuthFlags, boolean isNonStrongBiometricAllowed)
            throws Exception {
        bootService();
        mService.onUserSwitching(null, new SystemService.TargetUser(TEST_USER));

        ArgumentCaptor<StrongAuthTracker> strongAuthTracker =
                ArgumentCaptor.forClass(StrongAuthTracker.class);
        verify(mLockPatternUtils).registerStrongAuthTracker(strongAuthTracker.capture());
        strongAuthTracker.getValue().getStub().onStrongAuthRequiredChanged(
                strongAuthFlags, TEST_USER_ID);
        strongAuthTracker.getValue().getStub().onIsNonStrongBiometricAllowedChanged(
                isNonStrongBiometricAllowed, TEST_USER_ID);
        mService.waitForIdle();
    }

    private void setupFingerprint(int strength) {
        setupFingerprint(strength, /* enrolled= */ true);
    }

    private void setupFingerprint(int strength, boolean enrolled) {
        int sensorId = 100;
        List<SensorProperties.ComponentInfo> componentInfo = List.of();
        SensorProperties sensor = new SensorProperties(sensorId, strength, componentInfo);
        when(mFingerprintManager.getSensorProperties()).thenReturn(List.of(sensor));
        when(mFingerprintManager.hasEnrolledTemplates(TEST_USER_ID)).thenReturn(enrolled);
    }

    private void setupFace(int strength) {
        setupFace(strength, /* enrolled= */ true);
    }

    private void setupFace(int strength, boolean enrolled) {
        int sensorId = 100;
        List<SensorProperties.ComponentInfo> componentInfo = List.of();
        FaceSensorProperties sensor = new FaceSensorProperties(
                sensorId, strength, componentInfo, FaceSensorProperties.TYPE_RGB);
        when(mFaceManager.getSensorProperties()).thenReturn(List.of(sensor));
        when(mFaceManager.hasEnrolledTemplates(TEST_USER_ID)).thenReturn(enrolled);
    }

    private void verifyWeakUnlockEnabled() throws Exception {
        verifyWeakUnlockValue(true);
    }

    private void verifyWeakUnlockDisabled() throws Exception {
        verifyWeakUnlockValue(false);
    }

    // Simulates a device unlock and a device lock, then verifies that the expected
    // weakUnlockEnabled flag was passed to Keystore's onDeviceLocked method.
    private void verifyWeakUnlockValue(boolean expectedWeakUnlockEnabled) throws Exception {
        when(mWindowManager.isKeyguardLocked()).thenReturn(false);
        mTrustManager.reportKeyguardShowingChanged();
        verify(mKeystoreAuthorization).onDeviceUnlocked(TEST_USER_ID, null);

        when(mWindowManager.isKeyguardLocked()).thenReturn(true);
        mTrustManager.reportKeyguardShowingChanged();
        verify(mKeystoreAuthorization).onDeviceLocked(eq(TEST_USER_ID), any(),
                eq(expectedWeakUnlockEnabled));
    }

    private void setupMocksForProfile(boolean unifiedChallenge) {