Loading core/java/com/android/internal/widget/LockPatternUtils.java +47 −40 Original line number Diff line number Diff line Loading @@ -569,11 +569,12 @@ public class LockPatternUtils { /** * Clear any lock pattern or password. */ public void clearLock(int userHandle) { public void clearLock(String savedCredential, int userHandle) { setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle); try{ getLockSettings().setLockCredential(null, CREDENTIAL_TYPE_NONE, null, userHandle); getLockSettings().setLockCredential(null, CREDENTIAL_TYPE_NONE, savedCredential, userHandle); } catch (RemoteException e) { // well, we tried... } Loading Loading @@ -758,7 +759,6 @@ public class LockPatternUtils { public void saveLockPassword(String password, String savedPassword, int quality, int userHandle) { try { DevicePolicyManager dpm = getDevicePolicyManager(); if (password == null || password.length() < MIN_LOCK_PASSWORD_SIZE) { throw new IllegalArgumentException("password must not be null and at least " + "of length " + MIN_LOCK_PASSWORD_SIZE); Loading @@ -769,21 +769,32 @@ public class LockPatternUtils { getLockSettings().setLockCredential(password, CREDENTIAL_TYPE_PASSWORD, savedPassword, userHandle); addEncryptionPassword(password, computedQuality, userHandle); updatePasswordHistory(password, userHandle); } catch (RemoteException re) { // Cant do much Log.e(TAG, "Unable to save lock password " + re); } } private void addEncryptionPassword(String password, int quality, int userHandle) { // Update the device encryption password. if (userHandle == UserHandle.USER_SYSTEM && LockPatternUtils.isDeviceEncryptionEnabled()) { if (!shouldEncryptWithCredentials(true)) { clearEncryptionPassword(); } else { boolean numeric = computedQuality == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; boolean numericComplex = computedQuality boolean numeric = quality == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; boolean numericComplex = quality == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX; int type = numeric || numericComplex ? StorageManager.CRYPT_TYPE_PIN : StorageManager.CRYPT_TYPE_PASSWORD; updateEncryptionPassword(type, password); } } } private void updatePasswordHistory(String password, int userHandle) { // Add the password to the password history. We assume all // password hashes have the same length for simplicity of implementation. Loading @@ -805,10 +816,6 @@ public class LockPatternUtils { } setString(PASSWORD_HISTORY_KEY, passwordHistory, userHandle); onAfterChangingPassword(userHandle); } catch (RemoteException re) { // Cant do much Log.e(TAG, "Unable to save lock password " + re); } } /** Loading services/core/java/com/android/server/LockSettingsService.java +132 −78 Original line number Diff line number Diff line Loading @@ -68,6 +68,7 @@ import android.security.keystore.KeyProtection; import android.service.gatekeeper.GateKeeperResponse; import android.service.gatekeeper.IGateKeeperService; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; Loading @@ -94,8 +95,10 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; Loading Loading @@ -231,7 +234,7 @@ public class LockSettingsService extends ILockSettings.Stub { } // Do not tie it to parent when parent does not have a screen lock final int parentId = mUserManager.getProfileParent(managedUserId).id; if (!mStorage.hasPassword(parentId) && !mStorage.hasPattern(parentId)) { if (!isUserSecure(parentId)) { if (DEBUG) Slog.v(TAG, "Parent does not have a screen lock"); return; } Loading Loading @@ -378,7 +381,7 @@ public class LockSettingsService extends ILockSettings.Stub { } final UserHandle userHandle = user.getUserHandle(); final boolean isSecure = mStorage.hasPassword(userId) || mStorage.hasPattern(userId); final boolean isSecure = isUserSecure(userId); if (isSecure && !mUserManager.isUserUnlockingOrUnlocked(userHandle)) { UserInfo parent = mUserManager.getProfileParent(userId); if (parent != null && Loading Loading @@ -453,27 +456,20 @@ public class LockSettingsService extends ILockSettings.Stub { } public void onUnlockUser(final int userId) { // Hide notification first, as tie managed profile lock takes time hideEncryptionNotification(new UserHandle(userId)); if (mUserManager.getUserInfo(userId).isManagedProfile()) { // As tieManagedProfileLockIfNecessary() may try to unlock user, we should not do it // in onUnlockUser() synchronously, otherwise it may cause a deadlock // Perform tasks which require locks in LSS on a handler, as we are callbacks from // ActivityManager.unlockUser() mHandler.post(new Runnable() { @Override public void run() { tieManagedProfileLockIfNecessary(userId, null); } }); } // Hide notification first, as tie managed profile lock takes time hideEncryptionNotification(new UserHandle(userId)); // Now we have unlocked the parent user we should show notifications // about any profiles that exist. List<UserInfo> profiles = mUserManager.getProfiles(userId); for (int i = 0; i < profiles.size(); i++) { UserInfo profile = profiles.get(i); final boolean isSecure = mStorage.hasPassword(profile.id) || mStorage.hasPattern(profile.id); final boolean isSecure = isUserSecure(profile.id); if (isSecure && profile.isManagedProfile()) { UserHandle userHandle = profile.getUserHandle(); if (!mUserManager.isUserUnlockingOrUnlocked(userHandle) && Loading @@ -482,6 +478,12 @@ public class LockSettingsService extends ILockSettings.Stub { } } } if (mUserManager.getUserInfo(userId).isManagedProfile()) { tieManagedProfileLockIfNecessary(userId, null); } } }); } private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { Loading Loading @@ -809,6 +811,10 @@ public class LockSettingsService extends ILockSettings.Stub { return mStorage.hasPattern(userId); } private boolean isUserSecure(int userId) { return mStorage.hasCredential(userId); } private void setKeystorePassword(String password, int userHandle) { final KeyStore ks = KeyStore.getInstance(); ks.onUserPasswordChanged(userHandle, password); Loading Loading @@ -914,21 +920,52 @@ public class LockSettingsService extends ILockSettings.Stub { } } private byte[] getCurrentHandle(int userId) { CredentialHash credential = mStorage.readCredentialHash(userId); // sanity check if (credential.type != LockPatternUtils.CREDENTIAL_TYPE_NONE && credential.hash == null) { Slog.e(TAG, "Stored handle type [" + credential.type + "] but no handle available"); private Map<Integer, String> getDecryptedPasswordsForAllTiedProfiles(int userId) { if (mUserManager.getUserInfo(userId).isManagedProfile()) { return null; } return credential.hash; Map<Integer, String> result = new ArrayMap<Integer, String>(); final List<UserInfo> profiles = mUserManager.getProfiles(userId); final int size = profiles.size(); for (int i = 0; i < size; i++) { final UserInfo profile = profiles.get(i); if (!profile.isManagedProfile()) { continue; } final int managedUserId = profile.id; if (mLockPatternUtils.isSeparateProfileChallengeEnabled(managedUserId)) { continue; } try { result.put(userId, getDecryptedPasswordForTiedProfile(userId)); } catch (KeyStoreException | UnrecoverableKeyException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException | CertificateException | IOException e) { // ignore } } return result; } private void onUserLockChanged(int userId) throws RemoteException { /** * Synchronize all profile's work challenge of the given user if it's unified: tie or clear them * depending on the parent user's secure state. * * When clearing tied work challenges, a pre-computed password table for profiles are required, * since changing password for profiles requires existing password, and existing passwords can * only be computed before the parent user's password is cleared. * * Strictly this is a recursive function, since setLockCredentialInternal ends up calling this * method again on profiles. However the recursion is guaranteed to terminate as this method * terminates when the user is a managed profile. */ private void synchronizeUnifiedWorkChallengeForProfiles(int userId, Map<Integer, String> profilePasswordMap) throws RemoteException { if (mUserManager.getUserInfo(userId).isManagedProfile()) { return; } final boolean isSecure = mStorage.hasPassword(userId) || mStorage.hasPattern(userId); final boolean isSecure = isUserSecure(userId); final List<UserInfo> profiles = mUserManager.getProfiles(userId); final int size = profiles.size(); for (int i = 0; i < size; i++) { Loading @@ -941,11 +978,17 @@ public class LockSettingsService extends ILockSettings.Stub { if (isSecure) { tieManagedProfileLockIfNecessary(managedUserId, null); } else { clearUserKeyProtection(managedUserId); getGateKeeperService().clearSecureUserId(managedUserId); mStorage.writeCredentialHash(CredentialHash.createEmptyHash(), managedUserId); setKeystorePassword(null, managedUserId); fixateNewestUserKeyAuth(managedUserId); // We use cached work profile password computed before clearing the parent's // credential, otherwise they get lost if (profilePasswordMap != null && profilePasswordMap.containsKey(managedUserId)) { setLockCredentialInternal(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, profilePasswordMap.get(managedUserId), managedUserId); } else { Slog.wtf(TAG, "clear tied profile challenges, but no password supplied."); // Supplying null here would lead to untrusted credential change setLockCredentialInternal(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, null, managedUserId); } mStorage.removeChildProfileLock(managedUserId); removeKeystoreProfileKey(managedUserId); } Loading Loading @@ -978,7 +1021,6 @@ public class LockSettingsService extends ILockSettings.Stub { private void setLockCredentialInternal(String credential, int credentialType, String savedCredential, int userId) throws RemoteException { byte[] currentHandle = getCurrentHandle(userId); if (credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) { if (credential != null) { Slog.wtf(TAG, "CredentialType is none, but credential is non-null."); Loading @@ -988,15 +1030,18 @@ public class LockSettingsService extends ILockSettings.Stub { mStorage.writeCredentialHash(CredentialHash.createEmptyHash(), userId); setKeystorePassword(null, userId); fixateNewestUserKeyAuth(userId); onUserLockChanged(userId); synchronizeUnifiedWorkChallengeForProfiles(userId, null); notifyActivePasswordMetricsAvailable(null, userId); return; } if (credential == null) { throw new RemoteException("Null credential with mismatched credential type"); } CredentialHash currentHandle = mStorage.readCredentialHash(userId); if (isManagedProfileWithUnifiedLock(userId)) { // get credential from keystore when managed profile has unified lock if (savedCredential == null) { try { savedCredential = getDecryptedPasswordForTiedProfile(userId); } catch (FileNotFoundException e) { Loading @@ -1007,8 +1052,9 @@ public class LockSettingsService extends ILockSettings.Stub { | BadPaddingException | CertificateException | IOException e) { Slog.e(TAG, "Failed to decrypt child profile key", e); } } } else { if (currentHandle == null) { if (currentHandle.hash == null) { if (savedCredential != null) { Slog.w(TAG, "Saved credential provided, but none stored"); } Loading @@ -1016,17 +1062,19 @@ public class LockSettingsService extends ILockSettings.Stub { } } byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, credential, byte[] enrolledHandle = enrollCredential(currentHandle.hash, savedCredential, credential, userId); if (enrolledHandle != null) { CredentialHash willStore = CredentialHash.create(enrolledHandle, credentialType); mStorage.writeCredentialHash(willStore, userId); // Refresh the auth token setUserKeyProtection(userId, credential, doVerifyCredential(credential, credentialType, true, 0, userId, null /* progressCallback */)); // push new secret and auth token to vold GateKeeperResponse gkResponse = getGateKeeperService() .verifyChallenge(userId, 0, willStore.hash, credential.getBytes()); setUserKeyProtection(userId, credential, convertResponse(gkResponse)); fixateNewestUserKeyAuth(userId); onUserLockChanged(userId); // Refresh the auth token doVerifyCredential(credential, credentialType, true, 0, userId, null /* progressCallback */); synchronizeUnifiedWorkChallengeForProfiles(userId, null); } else { throw new RemoteException("Failed to enroll " + (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD ? "password" Loading @@ -1034,6 +1082,26 @@ public class LockSettingsService extends ILockSettings.Stub { } } private VerifyCredentialResponse convertResponse(GateKeeperResponse gateKeeperResponse) { VerifyCredentialResponse response; int responseCode = gateKeeperResponse.getResponseCode(); if (responseCode == GateKeeperResponse.RESPONSE_RETRY) { response = new VerifyCredentialResponse(gateKeeperResponse.getTimeout()); } else if (responseCode == GateKeeperResponse.RESPONSE_OK) { byte[] token = gateKeeperResponse.getPayload(); if (token == null) { // something's wrong if there's no payload with a challenge Slog.e(TAG, "verifyChallenge response had no associated payload"); response = VerifyCredentialResponse.ERROR; } else { response = new VerifyCredentialResponse(token); } } else { response = VerifyCredentialResponse.ERROR; } return response; } @VisibleForTesting protected void tieProfileLockToParent(int userId, String password) { if (DEBUG) Slog.v(TAG, "tieProfileLockToParent for user: " + userId); Loading Loading @@ -1123,6 +1191,7 @@ public class LockSettingsService extends ILockSettings.Stub { private void setUserKeyProtection(int userId, String credential, VerifyCredentialResponse vcr) throws RemoteException { if (DEBUG) Slog.d(TAG, "setUserKeyProtection: user=" + userId); if (vcr == null) { throw new RemoteException("Null response verifying a credential we just set"); } Loading @@ -1138,6 +1207,7 @@ public class LockSettingsService extends ILockSettings.Stub { } private void clearUserKeyProtection(int userId) throws RemoteException { if (DEBUG) Slog.d(TAG, "clearUserKeyProtection user=" + userId); addUserKeyAuth(userId, null, null); } Loading Loading @@ -1171,6 +1241,7 @@ public class LockSettingsService extends ILockSettings.Stub { private void fixateNewestUserKeyAuth(int userId) throws RemoteException { if (DEBUG) Slog.d(TAG, "fixateNewestUserKeyAuth: user=" + userId); final IStorageManager storageManager = mInjector.getStorageManager(); final long callingId = Binder.clearCallingIdentity(); try { Loading Loading @@ -1370,27 +1441,10 @@ public class LockSettingsService extends ILockSettings.Stub { return VerifyCredentialResponse.ERROR; } } VerifyCredentialResponse response; boolean shouldReEnroll = false; GateKeeperResponse gateKeeperResponse = getGateKeeperService() .verifyChallenge(userId, challenge, storedHash.hash, credential.getBytes()); int responseCode = gateKeeperResponse.getResponseCode(); if (responseCode == GateKeeperResponse.RESPONSE_RETRY) { response = new VerifyCredentialResponse(gateKeeperResponse.getTimeout()); } else if (responseCode == GateKeeperResponse.RESPONSE_OK) { byte[] token = gateKeeperResponse.getPayload(); if (token == null) { // something's wrong if there's no payload with a challenge Slog.e(TAG, "verifyChallenge response had no associated payload"); response = VerifyCredentialResponse.ERROR; } else { shouldReEnroll = gateKeeperResponse.getShouldReEnroll(); response = new VerifyCredentialResponse(token); } } else { response = VerifyCredentialResponse.ERROR; } VerifyCredentialResponse response = convertResponse(gateKeeperResponse); boolean shouldReEnroll = gateKeeperResponse.getShouldReEnroll(); if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { Loading services/core/java/com/android/server/LockSettingsShellCommand.java +1 −1 Original line number Diff line number Diff line Loading @@ -116,7 +116,7 @@ class LockSettingsShellCommand extends ShellCommand { } private void runClear() throws RemoteException { mLockPatternUtils.clearLock(mCurrentUserId); mLockPatternUtils.clearLock(mOld, mCurrentUserId); getOutPrintWriter().println("Lock credential cleared"); } Loading services/core/java/com/android/server/LockSettingsStorage.java +5 −0 Original line number Diff line number Diff line Loading @@ -290,6 +290,10 @@ class LockSettingsStorage { hasFile(getLegacyLockPatternFilename(userId)); } public boolean hasCredential(int userId) { return hasPassword(userId) || hasPattern(userId); } private boolean hasFile(String name) { byte[] contents = readFile(name); return contents != null && contents.length > 0; Loading Loading @@ -650,4 +654,5 @@ class LockSettingsStorage { } } } } services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +1 −1 Original line number Diff line number Diff line Loading @@ -4297,7 +4297,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!TextUtils.isEmpty(password)) { mLockPatternUtils.saveLockPassword(password, null, quality, userHandle); } else { mLockPatternUtils.clearLock(userHandle); mLockPatternUtils.clearLock(null, userHandle); } boolean requireEntry = (flags & DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) != 0; if (requireEntry) { Loading Loading
core/java/com/android/internal/widget/LockPatternUtils.java +47 −40 Original line number Diff line number Diff line Loading @@ -569,11 +569,12 @@ public class LockPatternUtils { /** * Clear any lock pattern or password. */ public void clearLock(int userHandle) { public void clearLock(String savedCredential, int userHandle) { setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle); try{ getLockSettings().setLockCredential(null, CREDENTIAL_TYPE_NONE, null, userHandle); getLockSettings().setLockCredential(null, CREDENTIAL_TYPE_NONE, savedCredential, userHandle); } catch (RemoteException e) { // well, we tried... } Loading Loading @@ -758,7 +759,6 @@ public class LockPatternUtils { public void saveLockPassword(String password, String savedPassword, int quality, int userHandle) { try { DevicePolicyManager dpm = getDevicePolicyManager(); if (password == null || password.length() < MIN_LOCK_PASSWORD_SIZE) { throw new IllegalArgumentException("password must not be null and at least " + "of length " + MIN_LOCK_PASSWORD_SIZE); Loading @@ -769,21 +769,32 @@ public class LockPatternUtils { getLockSettings().setLockCredential(password, CREDENTIAL_TYPE_PASSWORD, savedPassword, userHandle); addEncryptionPassword(password, computedQuality, userHandle); updatePasswordHistory(password, userHandle); } catch (RemoteException re) { // Cant do much Log.e(TAG, "Unable to save lock password " + re); } } private void addEncryptionPassword(String password, int quality, int userHandle) { // Update the device encryption password. if (userHandle == UserHandle.USER_SYSTEM && LockPatternUtils.isDeviceEncryptionEnabled()) { if (!shouldEncryptWithCredentials(true)) { clearEncryptionPassword(); } else { boolean numeric = computedQuality == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; boolean numericComplex = computedQuality boolean numeric = quality == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; boolean numericComplex = quality == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX; int type = numeric || numericComplex ? StorageManager.CRYPT_TYPE_PIN : StorageManager.CRYPT_TYPE_PASSWORD; updateEncryptionPassword(type, password); } } } private void updatePasswordHistory(String password, int userHandle) { // Add the password to the password history. We assume all // password hashes have the same length for simplicity of implementation. Loading @@ -805,10 +816,6 @@ public class LockPatternUtils { } setString(PASSWORD_HISTORY_KEY, passwordHistory, userHandle); onAfterChangingPassword(userHandle); } catch (RemoteException re) { // Cant do much Log.e(TAG, "Unable to save lock password " + re); } } /** Loading
services/core/java/com/android/server/LockSettingsService.java +132 −78 Original line number Diff line number Diff line Loading @@ -68,6 +68,7 @@ import android.security.keystore.KeyProtection; import android.service.gatekeeper.GateKeeperResponse; import android.service.gatekeeper.IGateKeeperService; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; Loading @@ -94,8 +95,10 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; Loading Loading @@ -231,7 +234,7 @@ public class LockSettingsService extends ILockSettings.Stub { } // Do not tie it to parent when parent does not have a screen lock final int parentId = mUserManager.getProfileParent(managedUserId).id; if (!mStorage.hasPassword(parentId) && !mStorage.hasPattern(parentId)) { if (!isUserSecure(parentId)) { if (DEBUG) Slog.v(TAG, "Parent does not have a screen lock"); return; } Loading Loading @@ -378,7 +381,7 @@ public class LockSettingsService extends ILockSettings.Stub { } final UserHandle userHandle = user.getUserHandle(); final boolean isSecure = mStorage.hasPassword(userId) || mStorage.hasPattern(userId); final boolean isSecure = isUserSecure(userId); if (isSecure && !mUserManager.isUserUnlockingOrUnlocked(userHandle)) { UserInfo parent = mUserManager.getProfileParent(userId); if (parent != null && Loading Loading @@ -453,27 +456,20 @@ public class LockSettingsService extends ILockSettings.Stub { } public void onUnlockUser(final int userId) { // Hide notification first, as tie managed profile lock takes time hideEncryptionNotification(new UserHandle(userId)); if (mUserManager.getUserInfo(userId).isManagedProfile()) { // As tieManagedProfileLockIfNecessary() may try to unlock user, we should not do it // in onUnlockUser() synchronously, otherwise it may cause a deadlock // Perform tasks which require locks in LSS on a handler, as we are callbacks from // ActivityManager.unlockUser() mHandler.post(new Runnable() { @Override public void run() { tieManagedProfileLockIfNecessary(userId, null); } }); } // Hide notification first, as tie managed profile lock takes time hideEncryptionNotification(new UserHandle(userId)); // Now we have unlocked the parent user we should show notifications // about any profiles that exist. List<UserInfo> profiles = mUserManager.getProfiles(userId); for (int i = 0; i < profiles.size(); i++) { UserInfo profile = profiles.get(i); final boolean isSecure = mStorage.hasPassword(profile.id) || mStorage.hasPattern(profile.id); final boolean isSecure = isUserSecure(profile.id); if (isSecure && profile.isManagedProfile()) { UserHandle userHandle = profile.getUserHandle(); if (!mUserManager.isUserUnlockingOrUnlocked(userHandle) && Loading @@ -482,6 +478,12 @@ public class LockSettingsService extends ILockSettings.Stub { } } } if (mUserManager.getUserInfo(userId).isManagedProfile()) { tieManagedProfileLockIfNecessary(userId, null); } } }); } private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { Loading Loading @@ -809,6 +811,10 @@ public class LockSettingsService extends ILockSettings.Stub { return mStorage.hasPattern(userId); } private boolean isUserSecure(int userId) { return mStorage.hasCredential(userId); } private void setKeystorePassword(String password, int userHandle) { final KeyStore ks = KeyStore.getInstance(); ks.onUserPasswordChanged(userHandle, password); Loading Loading @@ -914,21 +920,52 @@ public class LockSettingsService extends ILockSettings.Stub { } } private byte[] getCurrentHandle(int userId) { CredentialHash credential = mStorage.readCredentialHash(userId); // sanity check if (credential.type != LockPatternUtils.CREDENTIAL_TYPE_NONE && credential.hash == null) { Slog.e(TAG, "Stored handle type [" + credential.type + "] but no handle available"); private Map<Integer, String> getDecryptedPasswordsForAllTiedProfiles(int userId) { if (mUserManager.getUserInfo(userId).isManagedProfile()) { return null; } return credential.hash; Map<Integer, String> result = new ArrayMap<Integer, String>(); final List<UserInfo> profiles = mUserManager.getProfiles(userId); final int size = profiles.size(); for (int i = 0; i < size; i++) { final UserInfo profile = profiles.get(i); if (!profile.isManagedProfile()) { continue; } final int managedUserId = profile.id; if (mLockPatternUtils.isSeparateProfileChallengeEnabled(managedUserId)) { continue; } try { result.put(userId, getDecryptedPasswordForTiedProfile(userId)); } catch (KeyStoreException | UnrecoverableKeyException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException | CertificateException | IOException e) { // ignore } } return result; } private void onUserLockChanged(int userId) throws RemoteException { /** * Synchronize all profile's work challenge of the given user if it's unified: tie or clear them * depending on the parent user's secure state. * * When clearing tied work challenges, a pre-computed password table for profiles are required, * since changing password for profiles requires existing password, and existing passwords can * only be computed before the parent user's password is cleared. * * Strictly this is a recursive function, since setLockCredentialInternal ends up calling this * method again on profiles. However the recursion is guaranteed to terminate as this method * terminates when the user is a managed profile. */ private void synchronizeUnifiedWorkChallengeForProfiles(int userId, Map<Integer, String> profilePasswordMap) throws RemoteException { if (mUserManager.getUserInfo(userId).isManagedProfile()) { return; } final boolean isSecure = mStorage.hasPassword(userId) || mStorage.hasPattern(userId); final boolean isSecure = isUserSecure(userId); final List<UserInfo> profiles = mUserManager.getProfiles(userId); final int size = profiles.size(); for (int i = 0; i < size; i++) { Loading @@ -941,11 +978,17 @@ public class LockSettingsService extends ILockSettings.Stub { if (isSecure) { tieManagedProfileLockIfNecessary(managedUserId, null); } else { clearUserKeyProtection(managedUserId); getGateKeeperService().clearSecureUserId(managedUserId); mStorage.writeCredentialHash(CredentialHash.createEmptyHash(), managedUserId); setKeystorePassword(null, managedUserId); fixateNewestUserKeyAuth(managedUserId); // We use cached work profile password computed before clearing the parent's // credential, otherwise they get lost if (profilePasswordMap != null && profilePasswordMap.containsKey(managedUserId)) { setLockCredentialInternal(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, profilePasswordMap.get(managedUserId), managedUserId); } else { Slog.wtf(TAG, "clear tied profile challenges, but no password supplied."); // Supplying null here would lead to untrusted credential change setLockCredentialInternal(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, null, managedUserId); } mStorage.removeChildProfileLock(managedUserId); removeKeystoreProfileKey(managedUserId); } Loading Loading @@ -978,7 +1021,6 @@ public class LockSettingsService extends ILockSettings.Stub { private void setLockCredentialInternal(String credential, int credentialType, String savedCredential, int userId) throws RemoteException { byte[] currentHandle = getCurrentHandle(userId); if (credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) { if (credential != null) { Slog.wtf(TAG, "CredentialType is none, but credential is non-null."); Loading @@ -988,15 +1030,18 @@ public class LockSettingsService extends ILockSettings.Stub { mStorage.writeCredentialHash(CredentialHash.createEmptyHash(), userId); setKeystorePassword(null, userId); fixateNewestUserKeyAuth(userId); onUserLockChanged(userId); synchronizeUnifiedWorkChallengeForProfiles(userId, null); notifyActivePasswordMetricsAvailable(null, userId); return; } if (credential == null) { throw new RemoteException("Null credential with mismatched credential type"); } CredentialHash currentHandle = mStorage.readCredentialHash(userId); if (isManagedProfileWithUnifiedLock(userId)) { // get credential from keystore when managed profile has unified lock if (savedCredential == null) { try { savedCredential = getDecryptedPasswordForTiedProfile(userId); } catch (FileNotFoundException e) { Loading @@ -1007,8 +1052,9 @@ public class LockSettingsService extends ILockSettings.Stub { | BadPaddingException | CertificateException | IOException e) { Slog.e(TAG, "Failed to decrypt child profile key", e); } } } else { if (currentHandle == null) { if (currentHandle.hash == null) { if (savedCredential != null) { Slog.w(TAG, "Saved credential provided, but none stored"); } Loading @@ -1016,17 +1062,19 @@ public class LockSettingsService extends ILockSettings.Stub { } } byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, credential, byte[] enrolledHandle = enrollCredential(currentHandle.hash, savedCredential, credential, userId); if (enrolledHandle != null) { CredentialHash willStore = CredentialHash.create(enrolledHandle, credentialType); mStorage.writeCredentialHash(willStore, userId); // Refresh the auth token setUserKeyProtection(userId, credential, doVerifyCredential(credential, credentialType, true, 0, userId, null /* progressCallback */)); // push new secret and auth token to vold GateKeeperResponse gkResponse = getGateKeeperService() .verifyChallenge(userId, 0, willStore.hash, credential.getBytes()); setUserKeyProtection(userId, credential, convertResponse(gkResponse)); fixateNewestUserKeyAuth(userId); onUserLockChanged(userId); // Refresh the auth token doVerifyCredential(credential, credentialType, true, 0, userId, null /* progressCallback */); synchronizeUnifiedWorkChallengeForProfiles(userId, null); } else { throw new RemoteException("Failed to enroll " + (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD ? "password" Loading @@ -1034,6 +1082,26 @@ public class LockSettingsService extends ILockSettings.Stub { } } private VerifyCredentialResponse convertResponse(GateKeeperResponse gateKeeperResponse) { VerifyCredentialResponse response; int responseCode = gateKeeperResponse.getResponseCode(); if (responseCode == GateKeeperResponse.RESPONSE_RETRY) { response = new VerifyCredentialResponse(gateKeeperResponse.getTimeout()); } else if (responseCode == GateKeeperResponse.RESPONSE_OK) { byte[] token = gateKeeperResponse.getPayload(); if (token == null) { // something's wrong if there's no payload with a challenge Slog.e(TAG, "verifyChallenge response had no associated payload"); response = VerifyCredentialResponse.ERROR; } else { response = new VerifyCredentialResponse(token); } } else { response = VerifyCredentialResponse.ERROR; } return response; } @VisibleForTesting protected void tieProfileLockToParent(int userId, String password) { if (DEBUG) Slog.v(TAG, "tieProfileLockToParent for user: " + userId); Loading Loading @@ -1123,6 +1191,7 @@ public class LockSettingsService extends ILockSettings.Stub { private void setUserKeyProtection(int userId, String credential, VerifyCredentialResponse vcr) throws RemoteException { if (DEBUG) Slog.d(TAG, "setUserKeyProtection: user=" + userId); if (vcr == null) { throw new RemoteException("Null response verifying a credential we just set"); } Loading @@ -1138,6 +1207,7 @@ public class LockSettingsService extends ILockSettings.Stub { } private void clearUserKeyProtection(int userId) throws RemoteException { if (DEBUG) Slog.d(TAG, "clearUserKeyProtection user=" + userId); addUserKeyAuth(userId, null, null); } Loading Loading @@ -1171,6 +1241,7 @@ public class LockSettingsService extends ILockSettings.Stub { private void fixateNewestUserKeyAuth(int userId) throws RemoteException { if (DEBUG) Slog.d(TAG, "fixateNewestUserKeyAuth: user=" + userId); final IStorageManager storageManager = mInjector.getStorageManager(); final long callingId = Binder.clearCallingIdentity(); try { Loading Loading @@ -1370,27 +1441,10 @@ public class LockSettingsService extends ILockSettings.Stub { return VerifyCredentialResponse.ERROR; } } VerifyCredentialResponse response; boolean shouldReEnroll = false; GateKeeperResponse gateKeeperResponse = getGateKeeperService() .verifyChallenge(userId, challenge, storedHash.hash, credential.getBytes()); int responseCode = gateKeeperResponse.getResponseCode(); if (responseCode == GateKeeperResponse.RESPONSE_RETRY) { response = new VerifyCredentialResponse(gateKeeperResponse.getTimeout()); } else if (responseCode == GateKeeperResponse.RESPONSE_OK) { byte[] token = gateKeeperResponse.getPayload(); if (token == null) { // something's wrong if there's no payload with a challenge Slog.e(TAG, "verifyChallenge response had no associated payload"); response = VerifyCredentialResponse.ERROR; } else { shouldReEnroll = gateKeeperResponse.getShouldReEnroll(); response = new VerifyCredentialResponse(token); } } else { response = VerifyCredentialResponse.ERROR; } VerifyCredentialResponse response = convertResponse(gateKeeperResponse); boolean shouldReEnroll = gateKeeperResponse.getShouldReEnroll(); if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { Loading
services/core/java/com/android/server/LockSettingsShellCommand.java +1 −1 Original line number Diff line number Diff line Loading @@ -116,7 +116,7 @@ class LockSettingsShellCommand extends ShellCommand { } private void runClear() throws RemoteException { mLockPatternUtils.clearLock(mCurrentUserId); mLockPatternUtils.clearLock(mOld, mCurrentUserId); getOutPrintWriter().println("Lock credential cleared"); } Loading
services/core/java/com/android/server/LockSettingsStorage.java +5 −0 Original line number Diff line number Diff line Loading @@ -290,6 +290,10 @@ class LockSettingsStorage { hasFile(getLegacyLockPatternFilename(userId)); } public boolean hasCredential(int userId) { return hasPassword(userId) || hasPattern(userId); } private boolean hasFile(String name) { byte[] contents = readFile(name); return contents != null && contents.length > 0; Loading Loading @@ -650,4 +654,5 @@ class LockSettingsStorage { } } } }
services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +1 −1 Original line number Diff line number Diff line Loading @@ -4297,7 +4297,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!TextUtils.isEmpty(password)) { mLockPatternUtils.saveLockPassword(password, null, quality, userHandle); } else { mLockPatternUtils.clearLock(userHandle); mLockPatternUtils.clearLock(null, userHandle); } boolean requireEntry = (flags & DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) != 0; if (requireEntry) { Loading