summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Andrew Scull <ascull@google.com> 2016-12-02 16:08:09 +0000
committer Andrew Scull <ascull@google.com> 2017-01-12 16:01:59 +0000
commite4cefbf4fce458489b5f1bebc79dfaf566bcc5d5 (patch)
tree3637c2966073473fa74ebec02919cb17fb9b222b
parent73b49fc7e8194e338a71f96c796ef08f2c0f9534 (diff)
Don't save password metrics to disk.
On FBE devices, don't save the metrics to disk but compute them when the password is first entered and only store them in RAM. Merged-in: 5daf273b7e3272269c53eda20ce494d0e7a365b5 Bug: 32793550 Change-Id: Icee7f615167761177b224b342970a36c7d90f6ba
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java17
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl1
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java45
-rw-r--r--services/core/java/com/android/server/LockSettingsService.java60
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java147
5 files changed, 166 insertions, 104 deletions
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 530547348adc..78f0c92d22f0 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1981,7 +1981,8 @@ public class DevicePolicyManager {
* Determine whether the current password the user has set is sufficient to meet the policy
* requirements (e.g. quality, minimum length) that have been requested by the admins of this
* user and its participating profiles. Restrictions on profiles that have a separate challenge
- * are not taken into account.
+ * are not taken into account. If the user has a password, it must have been entered in order to
+ * perform this check.
* <p>
* The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
@@ -1994,6 +1995,7 @@ public class DevicePolicyManager {
* @return Returns true if the password meets the current requirements, else false.
* @throws SecurityException if the calling application does not own an active administrator
* that uses {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD}
+ * @throws IllegalStateException if the user has a password but has not entered it yet.
*/
public boolean isActivePasswordSufficient() {
if (mService != null) {
@@ -3428,6 +3430,19 @@ public class DevicePolicyManager {
/**
* @hide
*/
+ public void reportPasswordChanged(int userId) {
+ if (mService != null) {
+ try {
+ mService.reportPasswordChanged(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
public void reportFailedPasswordAttempt(int userHandle) {
if (mService != null) {
try {
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index ddec412e53c1..2a679eb29c55 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -116,6 +116,7 @@ interface IDevicePolicyManager {
void setActivePasswordState(int quality, int length, int letters, int uppercase, int lowercase,
int numbers, int symbols, int nonletter, int userHandle);
+ void reportPasswordChanged(int userId);
void reportFailedPasswordAttempt(int userHandle);
void reportSuccessfulPasswordAttempt(int userHandle);
void reportFailedFingerprintAttempt(int userHandle);
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 7f2f740f2ee2..d79a5a41a12d 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -565,9 +565,6 @@ public class LockPatternUtils {
setCredentialRequiredToDecrypt(false);
}
- getDevicePolicyManager().setActivePasswordState(
- DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0, userHandle);
-
onAfterChangingPassword(userHandle);
}
@@ -614,6 +611,7 @@ public class LockPatternUtils {
+ MIN_LOCK_PATTERN_SIZE + " dots long.");
}
+ setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
getLockSettings().setLockPattern(patternToString(pattern), savedPattern, userId);
DevicePolicyManager dpm = getDevicePolicyManager();
@@ -630,9 +628,6 @@ public class LockPatternUtils {
setBoolean(PATTERN_EVER_CHOSEN_KEY, true, userId);
- setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
- dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
- pattern.size(), 0, 0, 0, 0, 0, 0, userId);
onAfterChangingPassword(userId);
} catch (RemoteException re) {
Log.e(TAG, "Couldn't save lock pattern " + re);
@@ -835,9 +830,9 @@ public class LockPatternUtils {
+ "of length " + MIN_LOCK_PASSWORD_SIZE);
}
+ final int computedQuality = computePasswordQuality(password);
+ setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle);
getLockSettings().setLockPassword(password, savedPassword, userHandle);
- getLockSettings().setSeparateProfileChallengeEnabled(userHandle, true, null);
- int computedQuality = computePasswordQuality(password);
// Update the device encryption password.
if (userHandle == UserHandle.USER_SYSTEM
@@ -855,40 +850,6 @@ public class LockPatternUtils {
}
}
- setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle);
- if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
- int letters = 0;
- int uppercase = 0;
- int lowercase = 0;
- int numbers = 0;
- int symbols = 0;
- int nonletter = 0;
- for (int i = 0; i < password.length(); i++) {
- char c = password.charAt(i);
- if (c >= 'A' && c <= 'Z') {
- letters++;
- uppercase++;
- } else if (c >= 'a' && c <= 'z') {
- letters++;
- lowercase++;
- } else if (c >= '0' && c <= '9') {
- numbers++;
- nonletter++;
- } else {
- symbols++;
- nonletter++;
- }
- }
- dpm.setActivePasswordState(Math.max(quality, computedQuality),
- password.length(), letters, uppercase, lowercase,
- numbers, symbols, nonletter, userHandle);
- } else {
- // The password is not anything.
- dpm.setActivePasswordState(
- DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
- 0, 0, 0, 0, 0, 0, 0, userHandle);
- }
-
// Add the password to the password history. We assume all
// password hashes have the same length for simplicity of implementation.
String passwordHistory = getString(PASSWORD_HISTORY_KEY, userHandle);
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 42e75c6632a5..80088b789227 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -899,6 +899,7 @@ public class LockSettingsService extends ILockSettings.Stub {
synchronized (mSeparateChallengeLock) {
setLockPatternInternal(pattern, savedCredential, userId);
setSeparateProfileChallengeEnabled(userId, true, null);
+ notifyPasswordChanged(userId);
}
}
@@ -913,6 +914,7 @@ public class LockSettingsService extends ILockSettings.Stub {
setKeystorePassword(null, userId);
fixateNewestUserKeyAuth(userId);
onUserLockChanged(userId);
+ notifyActivePasswordMetricsAvailable(null, userId);
return;
}
@@ -962,6 +964,7 @@ public class LockSettingsService extends ILockSettings.Stub {
synchronized (mSeparateChallengeLock) {
setLockPasswordInternal(password, savedCredential, userId);
setSeparateProfileChallengeEnabled(userId, true, null);
+ notifyPasswordChanged(userId);
}
}
@@ -975,6 +978,7 @@ public class LockSettingsService extends ILockSettings.Stub {
setKeystorePassword(null, userId);
fixateNewestUserKeyAuth(userId);
onUserLockChanged(userId);
+ notifyActivePasswordMetricsAvailable(null, userId);
return;
}
@@ -1365,6 +1369,7 @@ public class LockSettingsService extends ILockSettings.Stub {
// migrate credential to GateKeeper
credentialUtil.setCredential(credential, null, userId);
if (!hasChallenge) {
+ notifyActivePasswordMetricsAvailable(credential, userId);
return VerifyCredentialResponse.OK;
}
// Fall through to get the auth token. Technically this should never happen,
@@ -1398,6 +1403,7 @@ public class LockSettingsService extends ILockSettings.Stub {
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
// credential has matched
+ notifyActivePasswordMetricsAvailable(credential, userId);
unlockKeystore(credential, userId);
Slog.i(TAG, "Unlocking user " + userId +
@@ -1421,6 +1427,60 @@ public class LockSettingsService extends ILockSettings.Stub {
return response;
}
+ private void notifyActivePasswordMetricsAvailable(final String password, int userId) {
+ final int quality = mLockPatternUtils.getKeyguardStoredPasswordQuality(userId);
+
+ // Asynchronous to avoid dead lock
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ int length = 0;
+ int letters = 0;
+ int uppercase = 0;
+ int lowercase = 0;
+ int numbers = 0;
+ int symbols = 0;
+ int nonletter = 0;
+ if (password != null) {
+ length = password.length();
+ for (int i = 0; i < length; i++) {
+ char c = password.charAt(i);
+ if (c >= 'A' && c <= 'Z') {
+ letters++;
+ uppercase++;
+ } else if (c >= 'a' && c <= 'z') {
+ letters++;
+ lowercase++;
+ } else if (c >= '0' && c <= '9') {
+ numbers++;
+ nonletter++;
+ } else {
+ symbols++;
+ nonletter++;
+ }
+ }
+ }
+ DevicePolicyManager dpm = (DevicePolicyManager)
+ mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ dpm.setActivePasswordState(quality, length, letters, uppercase, lowercase, numbers,
+ symbols, nonletter, userId);
+ }
+ });
+ }
+
+ /**
+ * Call after {@link #notifyActivePasswordMetricsAvailable} so metrics are updated before
+ * reporting the password changed.
+ */
+ private void notifyPasswordChanged(int userId) {
+ // Same handler as notifyActivePasswordMetricsAvailable to ensure correct ordering
+ mHandler.post(() -> {
+ DevicePolicyManager dpm = (DevicePolicyManager)
+ mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ dpm.reportPasswordChanged(userId);
+ });
+ }
+
@Override
public boolean checkVoldPassword(int userId) throws RemoteException {
if (!mFirstCallToVold) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 2aaf945dae49..80dfe66a8ddd 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2218,10 +2218,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
out.endTag(null, "failed-password-attempts");
}
- if (policy.mActivePasswordQuality != 0 || policy.mActivePasswordLength != 0
+ // Don't save metrics for FBE devices
+ if (!mInjector.storageManagerIsFileBasedEncryptionEnabled()
+ && (policy.mActivePasswordQuality != 0 || policy.mActivePasswordLength != 0
|| policy.mActivePasswordUpperCase != 0 || policy.mActivePasswordLowerCase != 0
|| policy.mActivePasswordLetters != 0 || policy.mActivePasswordNumeric != 0
- || policy.mActivePasswordSymbols != 0 || policy.mActivePasswordNonLetter != 0) {
+ || policy.mActivePasswordSymbols != 0
+ || policy.mActivePasswordNonLetter != 0)) {
out.startTag(null, "active-password");
out.attribute(null, "quality", Integer.toString(policy.mActivePasswordQuality));
out.attribute(null, "length", Integer.toString(policy.mActivePasswordLength));
@@ -2314,6 +2317,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
JournaledFile journal = makeJournaledFile(userHandle);
FileInputStream stream = null;
File file = journal.chooseForRead();
+ boolean needsRewrite = false;
try {
stream = new FileInputStream(file);
XmlPullParser parser = Xml.newPullParser();
@@ -2390,23 +2394,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
} else if ("password-owner".equals(tag)) {
policy.mPasswordOwner = Integer.parseInt(
parser.getAttributeValue(null, "value"));
- } else if ("active-password".equals(tag)) {
- policy.mActivePasswordQuality = Integer.parseInt(
- parser.getAttributeValue(null, "quality"));
- policy.mActivePasswordLength = Integer.parseInt(
- parser.getAttributeValue(null, "length"));
- policy.mActivePasswordUpperCase = Integer.parseInt(
- parser.getAttributeValue(null, "uppercase"));
- policy.mActivePasswordLowerCase = Integer.parseInt(
- parser.getAttributeValue(null, "lowercase"));
- policy.mActivePasswordLetters = Integer.parseInt(
- parser.getAttributeValue(null, "letters"));
- policy.mActivePasswordNumeric = Integer.parseInt(
- parser.getAttributeValue(null, "numeric"));
- policy.mActivePasswordSymbols = Integer.parseInt(
- parser.getAttributeValue(null, "symbols"));
- policy.mActivePasswordNonLetter = Integer.parseInt(
- parser.getAttributeValue(null, "nonletter"));
} else if (TAG_ACCEPTED_CA_CERTIFICATES.equals(tag)) {
policy.mAcceptedCaCertificates.add(parser.getAttributeValue(null, ATTR_NAME));
} else if (TAG_LOCK_TASK_COMPONENTS.equals(tag)) {
@@ -2423,6 +2410,28 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
policy.mAdminBroadcastPending = Boolean.toString(true).equals(pending);
} else if (TAG_INITIALIZATION_BUNDLE.equals(tag)) {
policy.mInitBundle = PersistableBundle.restoreFromXml(parser);
+ } else if ("active-password".equals(tag)) {
+ if (mInjector.storageManagerIsFileBasedEncryptionEnabled()) {
+ // Remove this from FBE devices
+ needsRewrite = true;
+ } else {
+ policy.mActivePasswordQuality = Integer.parseInt(
+ parser.getAttributeValue(null, "quality"));
+ policy.mActivePasswordLength = Integer.parseInt(
+ parser.getAttributeValue(null, "length"));
+ policy.mActivePasswordUpperCase = Integer.parseInt(
+ parser.getAttributeValue(null, "uppercase"));
+ policy.mActivePasswordLowerCase = Integer.parseInt(
+ parser.getAttributeValue(null, "lowercase"));
+ policy.mActivePasswordLetters = Integer.parseInt(
+ parser.getAttributeValue(null, "letters"));
+ policy.mActivePasswordNumeric = Integer.parseInt(
+ parser.getAttributeValue(null, "numeric"));
+ policy.mActivePasswordSymbols = Integer.parseInt(
+ parser.getAttributeValue(null, "symbols"));
+ policy.mActivePasswordNonLetter = Integer.parseInt(
+ parser.getAttributeValue(null, "nonletter"));
+ }
} else {
Slog.w(LOG_TAG, "Unknown tag: " + tag);
XmlUtils.skipCurrentTag(parser);
@@ -2442,34 +2451,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// Ignore
}
+ // Might need to upgrade the file by rewriting it
+ if (needsRewrite) {
+ saveSettingsLocked(userHandle);
+ }
+
// Generate a list of admins from the admin map
policy.mAdminList.addAll(policy.mAdminMap.values());
- // Validate that what we stored for the password quality matches
- // sufficiently what is currently set. Note that this is only
- // a sanity check in case the two get out of sync; this should
- // never normally happen.
- final long identity = mInjector.binderClearCallingIdentity();
- try {
- int actualPasswordQuality = mLockPatternUtils.getActivePasswordQuality(userHandle);
- if (actualPasswordQuality < policy.mActivePasswordQuality) {
- Slog.w(LOG_TAG, "Active password quality 0x"
- + Integer.toHexString(policy.mActivePasswordQuality)
- + " does not match actual quality 0x"
- + Integer.toHexString(actualPasswordQuality));
- policy.mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
- policy.mActivePasswordLength = 0;
- policy.mActivePasswordUpperCase = 0;
- policy.mActivePasswordLowerCase = 0;
- policy.mActivePasswordLetters = 0;
- policy.mActivePasswordNumeric = 0;
- policy.mActivePasswordSymbols = 0;
- policy.mActivePasswordNonLetter = 0;
- }
- } finally {
- mInjector.binderRestoreCallingIdentity(identity);
- }
-
validatePasswordOwnerLocked(policy);
updateMaximumTimeToLockLocked(userHandle);
updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle);
@@ -3663,6 +3652,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private boolean isActivePasswordSufficientForUserLocked(
DevicePolicyData policy, int userHandle, boolean parent) {
+ enforceUserUnlocked(userHandle, parent);
+
if (policy.mActivePasswordQuality < getPasswordQuality(null, userHandle, parent)
|| policy.mActivePasswordLength < getPasswordMinimumLength(
null, userHandle, parent)) {
@@ -4673,40 +4664,66 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return;
}
enforceFullCrossUsersPermission(userHandle);
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+
+ // If the managed profile doesn't have a separate password, set the metrics to default
+ if (isManagedProfile(userHandle) && !isSeparateProfileChallengeEnabled(userHandle)) {
+ quality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+ length = 0;
+ letters = 0;
+ uppercase = 0;
+ lowercase = 0;
+ numbers = 0;
+ symbols = 0;
+ nonletter = 0;
+ }
+
+ validateQualityConstant(quality);
+ DevicePolicyData policy = getUserData(userHandle);
+ synchronized (this) {
+ policy.mActivePasswordQuality = quality;
+ policy.mActivePasswordLength = length;
+ policy.mActivePasswordLetters = letters;
+ policy.mActivePasswordLowerCase = lowercase;
+ policy.mActivePasswordUpperCase = uppercase;
+ policy.mActivePasswordNumeric = numbers;
+ policy.mActivePasswordSymbols = symbols;
+ policy.mActivePasswordNonLetter = nonletter;
+ }
+ }
+
+ @Override
+ public void reportPasswordChanged(int userId) {
+ if (!mHasFeature) {
+ return;
+ }
+ enforceFullCrossUsersPermission(userId);
// Managed Profile password can only be changed when it has a separate challenge.
- if (!isSeparateProfileChallengeEnabled(userHandle)) {
- enforceNotManagedProfile(userHandle, "set the active password");
+ if (!isSeparateProfileChallengeEnabled(userId)) {
+ enforceNotManagedProfile(userId, "set the active password");
}
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
- validateQualityConstant(quality);
- DevicePolicyData policy = getUserData(userHandle);
+ DevicePolicyData policy = getUserData(userId);
long ident = mInjector.binderClearCallingIdentity();
try {
synchronized (this) {
- policy.mActivePasswordQuality = quality;
- policy.mActivePasswordLength = length;
- policy.mActivePasswordLetters = letters;
- policy.mActivePasswordLowerCase = lowercase;
- policy.mActivePasswordUpperCase = uppercase;
- policy.mActivePasswordNumeric = numbers;
- policy.mActivePasswordSymbols = symbols;
- policy.mActivePasswordNonLetter = nonletter;
policy.mFailedPasswordAttempts = 0;
- saveSettingsLocked(userHandle);
- updatePasswordExpirationsLocked(userHandle);
- setExpirationAlarmCheckLocked(mContext, userHandle, /* parent */ false);
+ saveSettingsLocked(userId);
+ updatePasswordExpirationsLocked(userId);
+ setExpirationAlarmCheckLocked(mContext, userId, /* parent */ false);
// Send a broadcast to each profile using this password as its primary unlock.
sendAdminCommandForLockscreenPoliciesLocked(
DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
- DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userHandle);
+ DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userId);
}
- removeCaApprovalsIfNeeded(userHandle);
+ removeCaApprovalsIfNeeded(userId);
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
@@ -6278,6 +6295,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
"User must be running and unlocked");
}
+ private void enforceUserUnlocked(int userId, boolean parent) {
+ if (parent) {
+ enforceUserUnlocked(getProfileParentId(userId));
+ } else {
+ enforceUserUnlocked(userId);
+ }
+ }
+
private void enforceManageUsers() {
final int callingUid = mInjector.binderGetCallingUid();
if (!(isCallerWithSystemUid() || callingUid == Process.ROOT_UID)) {