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

Commit e7a3e9fa authored by Rhed Jao's avatar Rhed Jao
Browse files

Create a verify flag to support verification for entering repair mode

Add a flag provided to verifyCredential API to support verification
for entering repair mode. If the flag is set, the API writes the
password data to the repair mode file after the user credential is
verified successfully. The file is deleted once the device boots
into normal mode.

Bug: 277561275
Test: atest com.android.server.locksettings
Test: adb shell dumpsys lock_settings
Test: am start -a android.app.action.PREPARE_REPAIR_MODE_DEVICE_CREDENTIAL
      The file is created in /metadata/repair-mode after the credential is
      verified.
Change-Id: I8e22c1c9045c1c67de72fae6e9ed581bb4f04b96
parent e978f449
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -116,6 +116,17 @@ 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";

    /**
     * A CharSequence dialog title to show to the user when used with a
     * {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}.
+43 −1
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 {}

@@ -200,6 +209,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.
     */
@@ -1821,6 +1832,37 @@ 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;
    }

    /**
     * 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
+19 −3
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR
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.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.userOwnsFrpCredential;
import static com.android.server.locksettings.SyntheticPasswordManager.TOKEN_TYPE_STRONG;
@@ -278,8 +279,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 +310,7 @@ public class LockSettingsService extends ILockSettings.Stub {
            if (phase == PHASE_ACTIVITY_MANAGER_READY) {
                mLockSettingsService.migrateOldDataAfterSystemReady();
                mLockSettingsService.loadEscrowData();
                mLockSettingsService.deleteRepairModePersistentDataIfNeeded();
            }
        }

@@ -544,7 +544,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 +952,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() {
@@ -2201,6 +2211,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());
+50 −0
Original line number Diff line number Diff line
@@ -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.
@@ -583,6 +609,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 +722,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 {
+51 −0
Original line number Diff line number Diff line
@@ -1114,6 +1114,57 @@ class SyntheticPasswordManager {
        }
    }

    /**
     * Writes the user's synthetic password data to the repair mode file.
     *
     * @param protectorId current LSKF based protectorId
     * @param userId user id of the user
     */
    public boolean writeRepairModeCredentialLocked(long protectorId, int userId) {
        if (!shouldWriteRepairModeCredential(userId)) {
            return false;
        }
        final byte[] data = loadState(PASSWORD_DATA_NAME, protectorId, userId);
        if (data == null) {
            Slogf.w(TAG, "Password data not found for user %d", userId);
            return false;
        }
        final PasswordData pwd = PasswordData.fromBytes(data);
        if (isNoneCredential(pwd)) {
            Slogf.w(TAG, "User %d has NONE credential", userId);
            return false;
        }
        Slogf.d(TAG, "Writing repair mode credential tied to user %d", userId);
        final int weaverSlot = loadWeaverSlot(protectorId, userId);
        if (weaverSlot != INVALID_WEAVER_SLOT) {
            // write weaver password
            mStorage.writeRepairModePersistentData(
                    PersistentData.TYPE_SP_WEAVER, weaverSlot, pwd.toBytes());
        } else {
            // write gatekeeper password
            mStorage.writeRepairModePersistentData(
                    PersistentData.TYPE_SP_GATEKEEPER, userId, pwd.toBytes());
        }
        return true;
    }

    private boolean shouldWriteRepairModeCredential(int userId) {
        final UserInfo userInfo = mUserManager.getUserInfo(userId);
        if (!LockPatternUtils.canUserEnterRepairMode(mContext, userInfo)) {
            Slogf.w(TAG, "User %d can't enter repair mode", userId);
            return false;
        }
        if (LockPatternUtils.isRepairModeActive(mContext)) {
            Slog.w(TAG, "Can't write repair mode credential while repair mode is already active");
            return false;
        }
        if (LockPatternUtils.isGsiRunning()) {
            Slog.w(TAG, "Can't write repair mode credential while GSI is running");
            return false;
        }
        return true;
    }

    private ArrayMap<Integer, ArrayMap<Long, TokenData>> tokenMap = new ArrayMap<>();

    /**
Loading