Loading core/java/android/app/KeyguardManager.java +11 −0 Original line number Diff line number Diff line Loading @@ -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}. Loading core/java/com/android/internal/widget/LockPatternUtils.java +43 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 {} Loading Loading @@ -201,6 +210,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. */ Loading Loading @@ -1846,6 +1857,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 Loading services/core/java/com/android/server/locksettings/LockSettingsService.java +19 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. */ Loading Loading @@ -311,6 +310,7 @@ public class LockSettingsService extends ILockSettings.Stub { if (phase == PHASE_ACTIVITY_MANAGER_READY) { mLockSettingsService.migrateOldDataAfterSystemReady(); mLockSettingsService.loadEscrowData(); mLockSettingsService.deleteRepairModePersistentDataIfNeeded(); } } Loading Loading @@ -541,7 +541,7 @@ public class LockSettingsService extends ILockSettings.Stub { } public boolean isGsiRunning() { return SystemProperties.getInt(GSI_RUNNING_PROP, 0) > 0; return LockPatternUtils.isGsiRunning(); } public FingerprintManager getFingerprintManager() { Loading Loading @@ -949,6 +949,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() { Loading Loading @@ -2202,6 +2212,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()); Loading services/core/java/com/android/server/locksettings/LockSettingsStorage.java +50 −0 Original line number Diff line number Diff line Loading @@ -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[] { Loading Loading @@ -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. Loading Loading @@ -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; Loading Loading @@ -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 { Loading services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +51 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
core/java/android/app/KeyguardManager.java +11 −0 Original line number Diff line number Diff line Loading @@ -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}. Loading
core/java/com/android/internal/widget/LockPatternUtils.java +43 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 {} Loading Loading @@ -201,6 +210,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. */ Loading Loading @@ -1846,6 +1857,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 Loading
services/core/java/com/android/server/locksettings/LockSettingsService.java +19 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. */ Loading Loading @@ -311,6 +310,7 @@ public class LockSettingsService extends ILockSettings.Stub { if (phase == PHASE_ACTIVITY_MANAGER_READY) { mLockSettingsService.migrateOldDataAfterSystemReady(); mLockSettingsService.loadEscrowData(); mLockSettingsService.deleteRepairModePersistentDataIfNeeded(); } } Loading Loading @@ -541,7 +541,7 @@ public class LockSettingsService extends ILockSettings.Stub { } public boolean isGsiRunning() { return SystemProperties.getInt(GSI_RUNNING_PROP, 0) > 0; return LockPatternUtils.isGsiRunning(); } public FingerprintManager getFingerprintManager() { Loading Loading @@ -949,6 +949,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() { Loading Loading @@ -2202,6 +2212,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()); Loading
services/core/java/com/android/server/locksettings/LockSettingsStorage.java +50 −0 Original line number Diff line number Diff line Loading @@ -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[] { Loading Loading @@ -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. Loading Loading @@ -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; Loading Loading @@ -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 { Loading
services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +51 −0 Original line number Diff line number Diff line Loading @@ -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