Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 34b764f9 authored by Rubin Xu's avatar Rubin Xu
Browse files

Support non-user-0 profile in ManagedProfilePasswordCache

Bug: 272704160
Test: com.android.server.locksettings
      com.android.cts.devicepolicy.QuietModeHostsideTest
      KeyGenParameterSpecTest
      Manual
Change-Id: I620cc4455ca0f7a8508f12b7550039200b42b8e8
parent 068e0087
Loading
Loading
Loading
Loading
+41 −3
Original line number Diff line number Diff line
@@ -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);
        }
    }
}
+4 −1
Original line number Diff line number Diff line
@@ -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>() {
+15 −4
Original line number Diff line number Diff line
@@ -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");
+10 −20
Original line number Diff line number Diff line
@@ -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();