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

Commit daf6dfa3 authored by Rhed Jao's avatar Rhed Jao Committed by Android (Google) Code Review
Browse files

Merge changes I51699d15,I8e22c1c9

* changes:
  Add a verification flow for the user to exit repair mode
  Create a verify flag to support verification for entering repair mode
parents 5afee216 5ebbd7d3
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -116,6 +116,26 @@ public class KeyguardManager {
    public static final String ACTION_CONFIRM_REMOTE_DEVICE_CREDENTIAL =
            "android.app.action.CONFIRM_REMOTE_DEVICE_CREDENTIAL";

    /**
     * Intent used to prompt user for device credential for entering repair
     * mode. If the credential is verified successfully, then the information
     * needed to verify the credential again will be written to a location that
     * is available to repair mode. This makes it possible for repair mode to
     * require that the same credential be provided to exit repair mode.
     * @hide
     */
    public static final String ACTION_PREPARE_REPAIR_MODE_DEVICE_CREDENTIAL =
            "android.app.action.PREPARE_REPAIR_MODE_DEVICE_CREDENTIAL";

    /**
     * Intent used to prompt user for device credential that is written by
     * {@link #ACTION_PREPARE_REPAIR_MODE_DEVICE_CREDENTIAL} for exiting
     * repair mode.
     * @hide
     */
    public static final String ACTION_CONFIRM_REPAIR_MODE_DEVICE_CREDENTIAL =
            "android.app.action.CONFIRM_REPAIR_MODE_DEVICE_CREDENTIAL";

    /**
     * A CharSequence dialog title to show to the user when used with a
     * {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}.
+96 −10
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
@@ -160,9 +161,17 @@ public class LockPatternUtils {
     */
    public static final int VERIFY_FLAG_REQUEST_GK_PW_HANDLE = 1 << 0;

    /**
     * Flag provided to {@link #verifyCredential(LockscreenCredential, int, int)} . If set, the
     * method writes the password data to the repair mode file after the credential is verified
     * successfully.
     */
    public static final int VERIFY_FLAG_WRITE_REPAIR_MODE_PW = 1 << 1;

    @Retention(RetentionPolicy.SOURCE)
    @IntDef(flag = true, value = {
            VERIFY_FLAG_REQUEST_GK_PW_HANDLE
            VERIFY_FLAG_REQUEST_GK_PW_HANDLE,
            VERIFY_FLAG_WRITE_REPAIR_MODE_PW
    })
    public @interface VerifyFlag {}

@@ -171,6 +180,11 @@ public class LockPatternUtils {
     */
    public static final int USER_FRP = UserHandle.USER_NULL + 1;

    /**
     * Special user id for triggering the exiting repair mode verification flow.
     */
    public static final int USER_REPAIR_MODE = UserHandle.USER_NULL + 2;

    public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type";
    @Deprecated
    public final static String PASSWORD_TYPE_ALTERNATE_KEY = "lockscreen.password_type_alternate";
@@ -200,6 +214,8 @@ public class LockPatternUtils {
    public static final String CURRENT_LSKF_BASED_PROTECTOR_ID_KEY = "sp-handle";
    public static final String PASSWORD_HISTORY_DELIMITER = ",";

    private static final String GSI_RUNNING_PROP = "ro.gsid.image_running";

    /**
     * drives the pin auto confirmation feature availability in code logic.
     */
@@ -388,7 +404,7 @@ public class LockPatternUtils {

    @UnsupportedAppUsage
    public void reportFailedPasswordAttempt(int userId) {
        if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
        if (isSpecialUserId(mContext, userId, /* checkDeviceSupported= */ true)) {
            return;
        }
        getDevicePolicyManager().reportFailedPasswordAttempt(userId);
@@ -397,7 +413,7 @@ public class LockPatternUtils {

    @UnsupportedAppUsage
    public void reportSuccessfulPasswordAttempt(int userId) {
        if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
        if (isSpecialUserId(mContext, userId, /* checkDeviceSupported= */ true)) {
            return;
        }
        getDevicePolicyManager().reportSuccessfulPasswordAttempt(userId);
@@ -405,21 +421,21 @@ public class LockPatternUtils {
    }

    public void reportPasswordLockout(int timeoutMs, int userId) {
        if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
        if (isSpecialUserId(mContext, userId, /* checkDeviceSupported= */ true)) {
            return;
        }
        getTrustManager().reportUnlockLockout(timeoutMs, userId);
    }

    public int getCurrentFailedPasswordAttempts(int userId) {
        if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
        if (isSpecialUserId(mContext, userId, /* checkDeviceSupported= */ true)) {
            return 0;
        }
        return getDevicePolicyManager().getCurrentFailedPasswordAttempts(userId);
    }

    public int getMaximumFailedPasswordsForWipe(int userId) {
        if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
        if (isSpecialUserId(mContext, userId, /* checkDeviceSupported= */ true)) {
            return 0;
        }
        return getDevicePolicyManager().getMaximumFailedPasswordsForWipe(
@@ -739,6 +755,17 @@ public class LockPatternUtils {
        }
    }

    /** Returns the credential type corresponding to the given PIN or password quality. */
    public static int pinOrPasswordQualityToCredentialType(int quality) {
        if (isQualityAlphabeticPassword(quality)) {
            return CREDENTIAL_TYPE_PASSWORD;
        }
        if (isQualityNumericPin(quality)) {
            return CREDENTIAL_TYPE_PIN;
        }
        throw new IllegalArgumentException("Quality is neither Pin nor password: " + quality);
    }

    /**
     * Save a new lockscreen credential.
     *
@@ -973,7 +1000,7 @@ public class LockPatternUtils {
                }
                @Override
                public boolean shouldBypassCache(Integer userHandle) {
                    return userHandle == USER_FRP;
                    return isSpecialUserId(userHandle);
                }
            };

@@ -1080,9 +1107,10 @@ public class LockPatternUtils {
    @UnsupportedAppUsage
    public long setLockoutAttemptDeadline(int userId, int timeoutMs) {
        final long deadline = SystemClock.elapsedRealtime() + timeoutMs;
        if (userId == USER_FRP) {
            // For secure password storage (that is required for FRP), the underlying storage also
            // enforces the deadline. Since we cannot store settings for the FRP user, don't.
        if (isSpecialUserId(userId)) {
            // For secure password storage (that is required for special users such as FRP), the
            // underlying storage also enforces the deadline. Since we cannot store settings
            // for special users, don't.
            return deadline;
        }
        mLockoutDeadlines.put(userId, deadline);
@@ -1821,6 +1849,64 @@ public class LockPatternUtils {
                com.android.internal.R.bool.config_enableCredentialFactoryResetProtection);
    }

    /**
     * Return {@code true} if repair mode is supported by the device.
     */
    public static boolean isRepairModeSupported(Context context) {
        return context.getResources().getBoolean(
                com.android.internal.R.bool.config_repairModeSupported);
    }

    /**
     * Return {@code true} if repair mode is active on the device.
     */
    public static boolean isRepairModeActive(Context context) {
        return Settings.Global.getInt(context.getContentResolver(),
                Settings.Global.REPAIR_MODE_ACTIVE, /* def= */ 0) > 0;
    }

    /**
     * Return {@code true} if repair mode is supported by the device and the user has been granted
     * admin privileges.
     */
    public static boolean canUserEnterRepairMode(Context context, UserInfo info) {
        return info != null && info.isAdmin() && isRepairModeSupported(context);
    }

    /**
     * Return {@code true} if GSI is running on the device.
     */
    public static boolean isGsiRunning() {
        return SystemProperties.getInt(GSI_RUNNING_PROP, 0) > 0;
    }

    /**
     * Return {@code true} if the given user id is a special user such as {@link #USER_FRP}.
     */
    public static boolean isSpecialUserId(int userId) {
        return isSpecialUserId(/* context= */ null, userId, /* checkDeviceSupported= */ false);
    }

    /**
     * Return {@code true} if the given user id is a special user for the verification flow.
     *
     * @param checkDeviceSupported {@code true} to check the specified user is supported
     *                             by the device.
     */
    private static boolean isSpecialUserId(@Nullable Context context, int userId,
            boolean checkDeviceSupported) {
        switch (userId) {
            case USER_FRP:
                if (checkDeviceSupported) return frpCredentialEnabled(context);
                return true;

            case USER_REPAIR_MODE:
                if (checkDeviceSupported) return isRepairModeSupported(context);
                return true;
        }
        return false;
    }

    /**
     * Attempt to rederive the unified work challenge for the specified profile user and unlock the
     * user. If successful, this would allow the user to leave quiet mode automatically without
+13 −0
Original line number Diff line number Diff line
@@ -36,6 +36,11 @@ public class LockPatternUtilsTest {
        assertTrue(LockPatternUtils.USER_FRP < 0);
    }

    @Test
    public void testUserRepairMode_isNotRegularUser() {
        assertTrue(LockPatternUtils.USER_REPAIR_MODE < 0);
    }

    @Test
    public void testUserFrp_isNotAReservedSpecialUser() throws Exception {
        assertNotEquals(UserHandle.USER_NULL, LockPatternUtils.USER_FRP);
@@ -43,4 +48,12 @@ public class LockPatternUtilsTest {
        assertNotEquals(UserHandle.USER_CURRENT, LockPatternUtils.USER_FRP);
        assertNotEquals(UserHandle.USER_CURRENT_OR_SELF, LockPatternUtils.USER_FRP);
    }

    @Test
    public void testUserRepairMode_isNotAReservedSpecialUser() throws Exception {
        assertNotEquals(UserHandle.USER_NULL, LockPatternUtils.USER_REPAIR_MODE);
        assertNotEquals(UserHandle.USER_ALL, LockPatternUtils.USER_REPAIR_MODE);
        assertNotEquals(UserHandle.USER_CURRENT, LockPatternUtils.USER_REPAIR_MODE);
        assertNotEquals(UserHandle.USER_CURRENT_OR_SELF, LockPatternUtils.USER_REPAIR_MODE);
    }
}
+33 −34
Original line number Diff line number Diff line
@@ -31,7 +31,6 @@ import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_SYSTEM;

import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
import static com.android.internal.widget.LockPatternUtils.CURRENT_LSKF_BASED_PROTECTOR_ID_KEY;
@@ -40,8 +39,12 @@ import static com.android.internal.widget.LockPatternUtils.PIN_LENGTH_UNAVAILABL
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
import static com.android.internal.widget.LockPatternUtils.USER_FRP;
import static com.android.internal.widget.LockPatternUtils.USER_REPAIR_MODE;
import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE;
import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW;
import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled;
import static com.android.internal.widget.LockPatternUtils.isSpecialUserId;
import static com.android.internal.widget.LockPatternUtils.pinOrPasswordQualityToCredentialType;
import static com.android.internal.widget.LockPatternUtils.userOwnsFrpCredential;
import static com.android.server.locksettings.SyntheticPasswordManager.TOKEN_TYPE_STRONG;
import static com.android.server.locksettings.SyntheticPasswordManager.TOKEN_TYPE_WEAK;
@@ -278,8 +281,6 @@ public class LockSettingsService extends ILockSettings.Stub {
    protected IGateKeeperService mGateKeeperService;
    protected IAuthSecret mAuthSecretService;

    private static final String GSI_RUNNING_PROP = "ro.gsid.image_running";

    /**
     * The UIDs that are used for system credential storage in keystore.
     */
@@ -311,6 +312,7 @@ public class LockSettingsService extends ILockSettings.Stub {
            if (phase == PHASE_ACTIVITY_MANAGER_READY) {
                mLockSettingsService.migrateOldDataAfterSystemReady();
                mLockSettingsService.loadEscrowData();
                mLockSettingsService.deleteRepairModePersistentDataIfNeeded();
            }
        }

@@ -544,7 +546,7 @@ public class LockSettingsService extends ILockSettings.Stub {
        }

        public boolean isGsiRunning() {
            return SystemProperties.getInt(GSI_RUNNING_PROP, 0) > 0;
            return LockPatternUtils.isGsiRunning();
        }

        public FingerprintManager getFingerprintManager() {
@@ -952,6 +954,16 @@ public class LockSettingsService extends ILockSettings.Stub {
        return success;
    }

    @VisibleForTesting
    void deleteRepairModePersistentDataIfNeeded() {
        if (!LockPatternUtils.isRepairModeSupported(mContext)
                || LockPatternUtils.isRepairModeActive(mContext)
                || mInjector.isGsiRunning()) {
            return;
        }
        mStorage.deleteRepairModePersistentData();
    }

    // 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() {
@@ -1282,8 +1294,8 @@ public class LockSettingsService extends ILockSettings.Stub {
     * {@link #CREDENTIAL_TYPE_PASSWORD}
     */
    private int getCredentialTypeInternal(int userId) {
        if (userId == USER_FRP) {
            return getFrpCredentialType();
        if (isSpecialUserId(userId)) {
            return mSpManager.getSpecialUserCredentialType(userId);
        }
        synchronized (mSpManager) {
            final long protectorId = getCurrentLskfBasedProtectorId(userId);
@@ -1299,29 +1311,6 @@ public class LockSettingsService extends ILockSettings.Stub {
        }
    }

    private int getFrpCredentialType() {
        PersistentData data = mStorage.readPersistentDataBlock();
        if (data.type != PersistentData.TYPE_SP_GATEKEEPER &&
                data.type != PersistentData.TYPE_SP_WEAVER) {
            return CREDENTIAL_TYPE_NONE;
        }
        int credentialType = SyntheticPasswordManager.getFrpCredentialType(data.payload);
        if (credentialType != CREDENTIAL_TYPE_PASSWORD_OR_PIN) {
            return credentialType;
        }
        return pinOrPasswordQualityToCredentialType(data.qualityForUi);
    }

    private static int pinOrPasswordQualityToCredentialType(int quality) {
        if (LockPatternUtils.isQualityAlphabeticPassword(quality)) {
            return CREDENTIAL_TYPE_PASSWORD;
        }
        if (LockPatternUtils.isQualityNumericPin(quality)) {
            return CREDENTIAL_TYPE_PIN;
        }
        throw new IllegalArgumentException("Quality is neither Pin nor password: " + quality);
    }

    private boolean isUserSecure(int userId) {
        return getCredentialTypeInternal(userId) != CREDENTIAL_TYPE_NONE;
    }
@@ -1563,8 +1552,8 @@ public class LockSettingsService extends ILockSettings.Stub {
     * unlock operation.
     */
    private void sendCredentialsOnUnlockIfRequired(LockscreenCredential credential, int userId) {
        // Don't send credentials during the factory reset protection flow.
        if (userId == USER_FRP) {
        // Don't send credentials during the special user flow.
        if (isSpecialUserId(userId)) {
            return;
        }

@@ -2184,15 +2173,19 @@ public class LockSettingsService extends ILockSettings.Stub {
            Slog.e(TAG, "FRP credential can only be verified prior to provisioning.");
            return VerifyCredentialResponse.ERROR;
        }
        if (userId == USER_REPAIR_MODE && !LockPatternUtils.isRepairModeActive(mContext)) {
            Slog.e(TAG, "Repair mode is not active on the device.");
            return VerifyCredentialResponse.ERROR;
        }
        Slogf.i(TAG, "Verifying lockscreen credential for user %d", userId);

        final AuthenticationResult authResult;
        VerifyCredentialResponse response;

        synchronized (mSpManager) {
            if (userId == USER_FRP) {
                return mSpManager.verifyFrpCredential(getGateKeeperService(), credential,
                        progressCallback);
            if (isSpecialUserId(userId)) {
                return mSpManager.verifySpecialUserCredential(userId, getGateKeeperService(),
                        credential, progressCallback);
            }

            long protectorId = getCurrentLskfBasedProtectorId(userId);
@@ -2201,6 +2194,12 @@ public class LockSettingsService extends ILockSettings.Stub {
            response = authResult.gkResponse;

            if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
                if ((flags & VERIFY_FLAG_WRITE_REPAIR_MODE_PW) != 0) {
                    if (!mSpManager.writeRepairModeCredentialLocked(protectorId, userId)) {
                        Slog.e(TAG, "Failed to write repair mode credential");
                        return VerifyCredentialResponse.ERROR;
                    }
                }
                // credential has matched
                mBiometricDeferredQueue.addPendingLockoutResetForUser(userId,
                        authResult.syntheticPassword.deriveGkPassword());
+54 −3
Original line number Diff line number Diff line
@@ -19,7 +19,7 @@ package com.android.server.locksettings;
import static android.content.Context.USER_SERVICE;

import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
import static com.android.internal.widget.LockPatternUtils.USER_FRP;
import static com.android.internal.widget.LockPatternUtils.isSpecialUserId;

import android.annotation.Nullable;
import android.app.admin.DevicePolicyManager;
@@ -90,6 +90,9 @@ class LockSettingsStorage {

    private static final String SYNTHETIC_PASSWORD_DIRECTORY = "spblob/";

    private static final String REPAIR_MODE_DIRECTORY = "repair-mode/";
    private static final String REPAIR_MODE_PERSISTENT_FILE = "pst";

    private static final Object DEFAULT = new Object();

    private static final String[] SETTINGS_TO_BACKUP = new String[] {
@@ -390,6 +393,29 @@ class LockSettingsStorage {
        }
    }

    @VisibleForTesting
    File getRepairModePersistentDataFile() {
        final File directory = new File(Environment.getMetadataDirectory(), REPAIR_MODE_DIRECTORY);
        return new File(directory, REPAIR_MODE_PERSISTENT_FILE);
    }

    public PersistentData readRepairModePersistentData() {
        final byte[] data = readFile(getRepairModePersistentDataFile());
        if (data == null) {
            return PersistentData.NONE;
        }
        return PersistentData.fromBytes(data);
    }

    public void writeRepairModePersistentData(int persistentType, int userId, byte[] payload) {
        writeFile(getRepairModePersistentDataFile(),
                PersistentData.toBytes(persistentType, userId, /* qualityForUi= */0, payload));
    }

    public void deleteRepairModePersistentData() {
        deleteFile(getRepairModePersistentDataFile());
    }

    /**
     * Writes the synthetic password state file for the given user ID, protector ID, and state name.
     * If the file already exists, then it is atomically replaced.
@@ -510,7 +536,8 @@ class LockSettingsStorage {
    }

    public void setString(String key, String value, int userId) {
        Preconditions.checkArgument(userId != USER_FRP, "cannot store lock settings for FRP user");
        Preconditions.checkArgument(!isSpecialUserId(userId),
                "cannot store lock settings for special user: %d", userId);

        writeKeyValue(key, value, userId);
        if (ArrayUtils.contains(SETTINGS_TO_BACKUP, key)) {
@@ -535,7 +562,7 @@ class LockSettingsStorage {
    }

    public String getString(String key, String defaultValue, int userId) {
        if (userId == USER_FRP) {
        if (isSpecialUserId(userId)) {
            return null;
        }
        return readKeyValue(key, defaultValue, userId);
@@ -583,6 +610,17 @@ class LockSettingsStorage {
        }
    }

    /**
     * Provides a concrete data structure to represent the minimal information from
     * a user's LSKF-based SP protector that is needed to verify the user's LSKF,
     * in combination with the corresponding Gatekeeper enrollment or Weaver slot.
     * It can be stored in {@link com.android.server.PersistentDataBlockService} for
     * FRP to live across factory resets not initiated via the Settings UI.
     * Written to {@link #REPAIR_MODE_PERSISTENT_FILE} to support verification for
     * exiting repair mode, since the device runs with an empty data partition in
     * repair mode and the same credential be provided to exit repair mode is
     * required.
     */
    public static class PersistentData {
        static final byte VERSION_1 = 1;
        static final int VERSION_1_HEADER_SIZE = 1 + 1 + 4 + 4;
@@ -685,6 +723,19 @@ class LockSettingsStorage {
            }
            pw.decreaseIndent();
        }
        // Dump repair mode file states
        final File repairModeFile = getRepairModePersistentDataFile();
        if (repairModeFile.exists()) {
            pw.println(TextUtils.formatSimple("Repair Mode [%s]:", repairModeFile.getParent()));
            pw.increaseIndent();
            pw.println(TextUtils.formatSimple("%6d %s %s", repairModeFile.length(),
                    LockSettingsService.timestampToString(repairModeFile.lastModified()),
                    repairModeFile.getName()));
            final PersistentData data = readRepairModePersistentData();
            pw.println(TextUtils.formatSimple("type: %d, user id: %d, payload size: %d",
                    data.type, data.userId, data.payload != null ? data.payload.length : 0));
            pw.decreaseIndent();
        }
    }

    static class DatabaseHelper extends SQLiteOpenHelper {
Loading