Support non-user-0 profile in ManagedProfilePasswordCache

Bug: 272704160
Test: com.android.server.locksettings
      com.android.cts.devicepolicy.QuietModeHostsideTest
      KeyGenParameterSpecTest
      Manual
Change-Id: I620cc4455ca0f7a8508f12b7550039200b42b8e8
This commit is contained in:
Rubin Xu
2023-04-12 15:22:59 +01:00
parent 068e0087b5
commit 34b764f9e3
4 changed files with 70 additions and 28 deletions

View File

@@ -320,6 +320,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
private final boolean mCriticalToDeviceEncryption;
private final int mMaxUsageCount;
private final String mAttestKeyAlias;
private final long mBoundToSecureUserId;
/*
* ***NOTE***: All new fields MUST also be added to the following:
* ParcelableKeyGenParameterSpec class.
@@ -362,7 +364,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
boolean unlockedDeviceRequired,
boolean criticalToDeviceEncryption,
int maxUsageCount,
String attestKeyAlias) {
String attestKeyAlias,
long boundToSecureUserId) {
if (TextUtils.isEmpty(keyStoreAlias)) {
throw new IllegalArgumentException("keyStoreAlias must not be empty");
}
@@ -422,6 +425,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
mCriticalToDeviceEncryption = criticalToDeviceEncryption;
mMaxUsageCount = maxUsageCount;
mAttestKeyAlias = attestKeyAlias;
mBoundToSecureUserId = boundToSecureUserId;
}
/**
@@ -842,10 +846,20 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
}
/**
* Return the secure user id that this key should be bound to.
*
* Normally an authentication-bound key is tied to the secure user id of the current user
* (either the root SID from GateKeeper for auth-bound keys with a timeout, or the authenticator
* id of the current biometric set for keys requiring explicit biometric authorization).
* If this parameter is set (this method returning non-zero value), the key should be tied to
* the specified secure user id, overriding the logic above.
*
* This is only applicable when {@link #isUserAuthenticationRequired} is {@code true}
*
* @hide
*/
public long getBoundToSpecificSecureUserId() {
return GateKeeper.INVALID_SECURE_USER_ID;
return mBoundToSecureUserId;
}
/**
@@ -920,6 +934,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
private boolean mCriticalToDeviceEncryption = false;
private int mMaxUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT;
private String mAttestKeyAlias = null;
private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID;
/**
* Creates a new instance of the {@code Builder}.
@@ -990,6 +1005,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
mCriticalToDeviceEncryption = sourceSpec.isCriticalToDeviceEncryption();
mMaxUsageCount = sourceSpec.getMaxUsageCount();
mAttestKeyAlias = sourceSpec.getAttestKeyAlias();
mBoundToSecureUserId = sourceSpec.getBoundToSpecificSecureUserId();
}
/**
@@ -1724,6 +1740,27 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
return this;
}
/**
* Set the secure user id that this key should be bound to.
*
* Normally an authentication-bound key is tied to the secure user id of the current user
* (either the root SID from GateKeeper for auth-bound keys with a timeout, or the
* authenticator id of the current biometric set for keys requiring explicit biometric
* authorization). If this parameter is set (this method returning non-zero value), the key
* should be tied to the specified secure user id, overriding the logic above.
*
* This is only applicable when {@link #setUserAuthenticationRequired} is set to
* {@code true}
*
* @see KeyGenParameterSpec#getBoundToSpecificSecureUserId()
* @hide
*/
@NonNull
public Builder setBoundToSpecificSecureUserId(long secureUserId) {
mBoundToSecureUserId = secureUserId;
return this;
}
/**
* Builds an instance of {@code KeyGenParameterSpec}.
*/
@@ -1762,7 +1799,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
mUnlockedDeviceRequired,
mCriticalToDeviceEncryption,
mMaxUsageCount,
mAttestKeyAlias);
mAttestKeyAlias,
mBoundToSecureUserId);
}
}
}

View File

@@ -111,6 +111,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable {
out.writeBoolean(mSpec.isCriticalToDeviceEncryption());
out.writeInt(mSpec.getMaxUsageCount());
out.writeString(mSpec.getAttestKeyAlias());
out.writeLong(mSpec.getBoundToSpecificSecureUserId());
}
private static Date readDateOrNull(Parcel in) {
@@ -172,6 +173,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable {
final boolean criticalToDeviceEncryption = in.readBoolean();
final int maxUsageCount = in.readInt();
final String attestKeyAlias = in.readString();
final long boundToSecureUserId = in.readLong();
// The KeyGenParameterSpec is intentionally not constructed using a Builder here:
// The intention is for this class to break if new parameters are added to the
// KeyGenParameterSpec constructor (whereas using a builder would silently drop them).
@@ -208,7 +210,8 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable {
unlockedDeviceRequired,
criticalToDeviceEncryption,
maxUsageCount,
attestKeyAlias);
attestKeyAlias,
boundToSecureUserId);
}
public static final @android.annotation.NonNull Creator<ParcelableKeyGenParameterSpec> CREATOR = new Creator<ParcelableKeyGenParameterSpec>() {

View File

@@ -400,11 +400,13 @@ public class LockSettingsService extends ILockSettings.Stub {
profileUserId, /* isLockTiedToParent= */ true);
return;
}
final long parentSid;
// Do not tie when the parent has no SID (but does have a screen lock).
// This can only happen during an upgrade path where SID is yet to be
// generated when the user unlocks for the first time.
try {
if (getGateKeeperService().getSecureUserId(parentId) == 0) {
parentSid = getGateKeeperService().getSecureUserId(parentId);
if (parentSid == 0) {
return;
}
} catch (RemoteException e) {
@@ -415,7 +417,8 @@ public class LockSettingsService extends ILockSettings.Stub {
setLockCredentialInternal(unifiedProfilePassword, profileUserPassword, profileUserId,
/* isLockTiedToParent= */ true);
tieProfileLockToParent(profileUserId, parentId, unifiedProfilePassword);
mManagedProfilePasswordCache.storePassword(profileUserId, unifiedProfilePassword);
mManagedProfilePasswordCache.storePassword(profileUserId, unifiedProfilePassword,
parentSid);
}
}
@@ -574,7 +577,7 @@ public class LockSettingsService extends ILockSettings.Stub {
public @NonNull ManagedProfilePasswordCache getManagedProfilePasswordCache(
java.security.KeyStore ks) {
return new ManagedProfilePasswordCache(ks, getUserManager());
return new ManagedProfilePasswordCache(ks);
}
public boolean isHeadlessSystemUserMode() {
@@ -1325,7 +1328,13 @@ public class LockSettingsService extends ILockSettings.Stub {
LockscreenCredential credential = LockscreenCredential.createManagedPassword(
decryptionResult);
Arrays.fill(decryptionResult, (byte) 0);
mManagedProfilePasswordCache.storePassword(userId, credential);
try {
long parentSid = getGateKeeperService().getSecureUserId(
mUserManager.getProfileParent(userId).id);
mManagedProfilePasswordCache.storePassword(userId, credential, parentSid);
} catch (RemoteException e) {
Slogf.w(TAG, "Failed to talk to GateKeeper service", e);
}
return credential;
}
@@ -2208,6 +2217,8 @@ public class LockSettingsService extends ILockSettings.Stub {
public VerifyCredentialResponse verifyTiedProfileChallenge(LockscreenCredential credential,
int userId, @LockPatternUtils.VerifyFlag int flags) {
checkPasswordReadPermission();
Slogf.i(TAG, "Verifying tied profile challenge for user %d", userId);
if (!isProfileWithUnifiedLock(userId)) {
throw new IllegalArgumentException(
"User id must be managed/clone profile with unified lock");

View File

@@ -17,9 +17,7 @@
package com.android.server.locksettings;
import android.annotation.Nullable;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.os.UserManager;
import android.security.GateKeeper;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.security.keystore.UserNotAuthenticatedException;
@@ -45,12 +43,9 @@ import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
/**
* Caches *unified* work challenge for user 0's managed profiles. Only user 0's profile is supported
* at the moment because the cached credential is encrypted using a keystore key auth-bound to
* user 0: this is to match how unified work challenge is similarly auth-bound to its parent user's
* lockscreen credential normally. It's possible to extend this class to support managed profiles
* for secondary users, that will require generating auth-bound keys to their corresponding parent
* user though (which {@link KeyGenParameterSpec} does not support right now).
* Caches *unified* work challenge for managed profiles. The cached credential is encrypted using
* a keystore key auth-bound to the parent user's lockscreen credential, similar to how unified
* work challenge is normally secured.
*
* <p> The cache is filled whenever the managed profile's unified challenge is created or derived
* (as part of the parent user's credential verification flow). It's removed when the profile is
@@ -70,28 +65,23 @@ public class ManagedProfilePasswordCache {
private final SparseArray<byte[]> mEncryptedPasswords = new SparseArray<>();
private final KeyStore mKeyStore;
private final UserManager mUserManager;
public ManagedProfilePasswordCache(KeyStore keyStore, UserManager userManager) {
public ManagedProfilePasswordCache(KeyStore keyStore) {
mKeyStore = keyStore;
mUserManager = userManager;
}
/**
* Encrypt and store the password in the cache. Does NOT overwrite existing password cache
* if one for the given user already exists.
*
* Should only be called on a profile userId.
*/
public void storePassword(int userId, LockscreenCredential password) {
public void storePassword(int userId, LockscreenCredential password, long parentSid) {
if (parentSid == GateKeeper.INVALID_SECURE_USER_ID) return;
synchronized (mEncryptedPasswords) {
if (mEncryptedPasswords.contains(userId)) {
return;
}
UserInfo parent = mUserManager.getProfileParent(userId);
if (parent == null || parent.id != UserHandle.USER_SYSTEM) {
// Since the cached password is encrypted using a keystore key auth-bound to user 0,
// only support caching password for user 0's profile.
return;
}
String keyName = getEncryptionKeyName(userId);
KeyGenerator generator;
SecretKey key;
@@ -104,8 +94,8 @@ public class ManagedProfilePasswordCache {
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setNamespace(SyntheticPasswordCrypto.keyNamespace())
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
// Generate auth-bound key to user 0 (since we the caller is user 0)
.setUserAuthenticationRequired(true)
.setBoundToSpecificSecureUserId(parentSid)
.setUserAuthenticationValidityDurationSeconds(CACHE_TIMEOUT_SECONDS)
.build());
key = generator.generateKey();