Attempt to unlock users with null token.
When starting a locked user, try unlocking their storace will a null
token, which will typically succeed if there is an insecure
lockscreen (no PIN or pattern).
For users with a secure lockscreen, pass through a stub token for
now to indicate that it came from a user challenge. Eventually we'll
hook that up to gatekeeperd.
Without this, we were only unlocking users with secure lockscreens.
Bug: 25943941
Change-Id: Ia0324d50f43f55dfe0b8366793ddc5d25d885922
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 6aa5263..033a4b8 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -624,7 +624,13 @@
byte[] hash = credentialUtil.toHash(credential, userId);
if (Arrays.equals(hash, storedHash.hash)) {
unlockKeystore(credentialUtil.adjustForKeystore(credential), userId);
- unlockUser(userId, null);
+
+ // TODO: pass through a meaningful token from gatekeeper to
+ // unlock credential keys; for now pass through a stub value to
+ // indicate that we came from a user challenge.
+ final byte[] token = String.valueOf(userId).getBytes();
+ unlockUser(userId, token);
+
// migrate credential to GateKeeper
credentialUtil.setCredential(credential, null, userId);
if (!hasChallenge) {
@@ -677,7 +683,13 @@
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
// credential has matched
unlockKeystore(credential, userId);
- unlockUser(userId, null);
+
+ // TODO: pass through a meaningful token from gatekeeper to
+ // unlock credential keys; for now pass through a stub value to
+ // indicate that we came from a user challenge.
+ final byte[] token = String.valueOf(userId).getBytes();
+ unlockUser(userId, token);
+
UserInfo info = UserManager.get(mContext).getUserInfo(userId);
if (LockPatternUtils.isSeparateWorkChallengeEnabled() && info.isManagedProfile()) {
TrustManager trustManager =
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index bd43a71..807c0d6 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -100,6 +100,7 @@
import com.android.internal.util.HexDump;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
+import com.android.internal.widget.LockPatternUtils;
import com.android.server.NativeDaemonConnector.Command;
import com.android.server.NativeDaemonConnector.SensitiveArg;
import com.android.server.pm.PackageManagerService;
@@ -435,6 +436,7 @@
private PackageManagerService mPms;
private final Callbacks mCallbacks;
+ private final LockPatternUtils mLockPatternUtils;
// Two connectors - mConnector & mCryptConnector
private final CountDownLatch mConnectedSignal = new CountDownLatch(2);
@@ -1429,6 +1431,7 @@
mContext = context;
mCallbacks = new Callbacks(FgThread.get().getLooper());
+ mLockPatternUtils = new LockPatternUtils(mContext);
// XXX: This will go away soon in favor of IMountServiceObserver
mPms = (PackageManagerService) ServiceManager.getService("package");
@@ -2721,6 +2724,12 @@
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
waitForReady();
+ // When a user has secure lock screen, require a challenge token to
+ // actually unlock. This check is mostly in place for emulation mode.
+ if (mLockPatternUtils.isSecure(userId) && ArrayUtils.isEmpty(token)) {
+ throw new IllegalStateException("Token required to unlock secure user " + userId);
+ }
+
final String encodedToken;
if (ArrayUtils.isEmpty(token)) {
encodedToken = "!";
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 195465c..f6f82da 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -82,8 +82,6 @@
import java.util.List;
import java.util.Set;
-import libcore.util.EmptyArray;
-
/**
* Helper class for {@link ActivityManagerService} responsible for multi-user functionality.
*/
@@ -223,7 +221,7 @@
AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
}
- maybeFinishUserUnlock(uss);
+ maybeUnlockUser(userId);
}
}
@@ -232,7 +230,7 @@
* {@link UserState#STATE_RUNNING}, which only occurs if the user storage is
* actually unlocked.
*/
- void maybeFinishUserUnlock(UserState uss) {
+ void finishUserUnlock(UserState uss) {
final int userId = uss.mHandle.getIdentifier();
synchronized (mService) {
// Bail if we ended up with a stale user
@@ -530,9 +528,12 @@
return userManager;
}
+ private IMountService getMountService() {
+ return IMountService.Stub.asInterface(ServiceManager.getService("mount"));
+ }
+
private boolean isUserKeyUnlocked(int userId) {
- final IMountService mountService = IMountService.Stub
- .asInterface(ServiceManager.getService("mount"));
+ final IMountService mountService = getMountService();
if (mountService != null) {
try {
return mountService.isUserKeyUnlocked(userId);
@@ -743,6 +744,17 @@
}
}
+ /**
+ * Attempt to unlock user without a credential token. This typically
+ * succeeds when the device doesn't have credential-encrypted storage, or
+ * when the the credential-encrypted storage isn't tied to a user-provided
+ * PIN or pattern.
+ */
+ boolean maybeUnlockUser(final int userId) {
+ // Try unlocking storage using empty token
+ return unlockUserCleared(userId, null);
+ }
+
boolean unlockUserCleared(final int userId, byte[] token) {
synchronized (mService) {
// Bail if already running unlocked
@@ -750,19 +762,20 @@
if (uss.state == UserState.STATE_RUNNING) return true;
}
- final UserInfo userInfo = getUserInfo(userId);
- final IMountService mountService = IMountService.Stub
- .asInterface(ServiceManager.getService("mount"));
- try {
- mountService.unlockUserKey(userId, userInfo.serialNumber, token);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to unlock: " + e.getMessage());
- return false;
+ if (!isUserKeyUnlocked(userId)) {
+ final UserInfo userInfo = getUserInfo(userId);
+ final IMountService mountService = getMountService();
+ try {
+ mountService.unlockUserKey(userId, userInfo.serialNumber, token);
+ } catch (RemoteException | RuntimeException e) {
+ Slog.w(TAG, "Failed to unlock: " + e.getMessage());
+ return false;
+ }
}
synchronized (mService) {
final UserState uss = mStartedUsers.get(userId);
- maybeFinishUserUnlock(uss);
+ finishUserUnlock(uss);
}
return true;