Use isCredentialSharedWithParent in LockSettingsService

Created property isCredentialSharedWithParent in UserTypeDetails which
is true for managed and clone profiles. This is used in
LockSettingsService for sharing the locking credentials with its parent
user.

Bug: 214355283
Test: atest android.multiuser.cts.UserManagerTest
Test: atest LockSettingsServiceTests

Change-Id: I6ceaf498aadce7e713b20a4d24e4a29e812eb0ca
This commit is contained in:
sailendrabathi
2022-01-18 05:55:41 +00:00
parent 00e99e742a
commit 7a799535e1
9 changed files with 232 additions and 107 deletions

View File

@@ -9526,6 +9526,7 @@ package android.os {
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean hasUserRestrictionForUser(@NonNull String, @NonNull android.os.UserHandle);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isAdminUser();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isCloneProfile();
method public boolean isCredentialSharedWithParent();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isGuestUser();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isManagedProfile(int);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isMediaSharedWithParent();

View File

@@ -111,6 +111,7 @@ interface IUserManager {
boolean isManagedProfile(int userId);
boolean isCloneProfile(int userId);
boolean isMediaSharedWithParent(int userId);
boolean isCredentialSharedWithParent(int userId);
boolean isDemoUser(int userId);
boolean isPreCreated(int userId);
UserInfo createProfileForUserEvenWhenDisallowedWithThrow(in String name, in String userType, int flags,

View File

@@ -4762,6 +4762,28 @@ public class UserManager {
}
}
/**
* Returns {@code true} if the user shares lock settings credential with its parent user
*
* This API only works for {@link UserManager#isProfile() profiles}
* and will always return false for any other user type.
*
* @hide
*/
@SystemApi
@UserHandleAware(
requiresAnyOfPermissionsIfNotCallerProfileGroup = {
Manifest.permission.MANAGE_USERS,
Manifest.permission.INTERACT_ACROSS_USERS})
@SuppressAutoDoc
public boolean isCredentialSharedWithParent() {
try {
return mService.isCredentialSharedWithParent(mUserId);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}
/**
* Removes a user and all associated data.
* @param userId the integer handle of the user.

View File

@@ -34,6 +34,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.Build;
import android.os.Handler;
@@ -65,6 +66,7 @@ import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
/**
@@ -194,6 +196,8 @@ public class LockPatternUtils {
private final SparseLongArray mLockoutDeadlines = new SparseLongArray();
private Boolean mHasSecureLockScreen;
private HashMap<UserHandle, UserManager> mUserManagerCache = new HashMap<>();
/**
* Use {@link TrustManager#isTrustUsuallyManaged(int)}.
*
@@ -265,6 +269,22 @@ public class LockPatternUtils {
return mUserManager;
}
private UserManager getUserManager(int userId) {
UserHandle userHandle = UserHandle.of(userId);
if (mUserManagerCache.containsKey(userHandle)) {
return mUserManagerCache.get(userHandle);
}
try {
Context userContext = mContext.createPackageContextAsUser("system", 0, userHandle);
UserManager userManager = userContext.getSystemService(UserManager.class);
mUserManagerCache.put(userHandle, userManager);
return userManager;
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException("Failed to create context for user " + userHandle, e);
}
}
private TrustManager getTrustManager() {
TrustManager trust = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
if (trust == null) {
@@ -812,16 +832,17 @@ public class LockPatternUtils {
/**
* Enables/disables the Separate Profile Challenge for this {@code userHandle}. This is a no-op
* for user handles that do not belong to a managed profile.
* for user handles that do not belong to a profile that shares credential with parent.
* (managed profile and clone profile share lock credential with parent).
*
* @param userHandle Managed profile user id
* @param enabled True if separate challenge is enabled
* @param profilePassword Managed profile previous password. Null when {@code enabled} is
* @param profilePassword Managed/Clone profile previous password. Null when {@code enabled} is
* true
*/
public void setSeparateProfileChallengeEnabled(int userHandle, boolean enabled,
LockscreenCredential profilePassword) {
if (!isManagedProfile(userHandle)) {
if (!isCredentialSharedWithParent(userHandle)) {
return;
}
try {
@@ -837,7 +858,7 @@ public class LockPatternUtils {
* Returns true if {@code userHandle} is a managed profile with separate challenge.
*/
public boolean isSeparateProfileChallengeEnabled(int userHandle) {
return isManagedProfile(userHandle) && hasSeparateChallenge(userHandle);
return isCredentialSharedWithParent(userHandle) && hasSeparateChallenge(userHandle);
}
/**
@@ -862,6 +883,10 @@ public class LockPatternUtils {
return info != null && info.isManagedProfile();
}
private boolean isCredentialSharedWithParent(int userHandle) {
return getUserManager(userHandle).isCredentialSharedWithParent();
}
/**
* Deserialize a pattern.
* @param bytes The pattern serialized with {@link #patternToByteArray}

View File

@@ -170,6 +170,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
@@ -235,7 +236,7 @@ public class LockSettingsService extends ILockSettings.Stub {
private final Random mRandom;
private final NotificationManager mNotificationManager;
private final UserManager mUserManager;
protected final UserManager mUserManager;
private final IStorageManager mStorageManager;
private final IActivityManager mActivityManager;
private final SyntheticPasswordManager mSpManager;
@@ -267,6 +268,8 @@ public class LockSettingsService extends ILockSettings.Stub {
private static final int[] SYSTEM_CREDENTIAL_UIDS = {
Process.VPN_UID, Process.ROOT_UID, Process.SYSTEM_UID};
private HashMap<UserHandle, UserManager> mUserManagerCache = new HashMap<>();
// This class manages life cycle events for encrypted users on File Based Encryption (FBE)
// devices. The most basic of these is to show/hide notifications about missing features until
// the user unlocks the account and credential-encrypted storage is available.
@@ -356,35 +359,36 @@ public class LockSettingsService extends ILockSettings.Stub {
}
/**
* Tie managed profile to primary profile if it is in unified mode and not tied before.
* Tie profile to primary profile if it is in unified mode and not tied before.
* Only for profiles which share credential with parent. (e.g. managed and clone profiles)
*
* @param managedUserId Managed profile user Id
* @param managedUserPassword Managed profile original password (when it has separated lock).
* @param profileUserId profile user Id
* @param profileUserPassword profile original password (when it has separated lock).
*/
public void tieManagedProfileLockIfNecessary(int managedUserId,
LockscreenCredential managedUserPassword) {
if (DEBUG) Slog.v(TAG, "Check child profile lock for user: " + managedUserId);
// Only for managed profile
if (!mUserManager.getUserInfo(managedUserId).isManagedProfile()) {
public void tieProfileLockIfNecessary(int profileUserId,
LockscreenCredential profileUserPassword) {
if (DEBUG) Slog.v(TAG, "Check child profile lock for user: " + profileUserId);
// Only for profiles that shares credential with parent
if (!isCredentialSharedWithParent(profileUserId)) {
return;
}
// Do not tie managed profile when work challenge is enabled
if (getSeparateProfileChallengeEnabledInternal(managedUserId)) {
// Do not tie profile when work challenge is enabled
if (getSeparateProfileChallengeEnabledInternal(profileUserId)) {
return;
}
// Do not tie managed profile to parent when it's done already
if (mStorage.hasChildProfileLock(managedUserId)) {
// Do not tie profile to parent when it's done already
if (mStorage.hasChildProfileLock(profileUserId)) {
return;
}
// If parent does not have a screen lock, simply clear credential from the managed profile,
// If parent does not have a screen lock, simply clear credential from the profile,
// to maintain the invariant that unified profile should always have the same secure state
// as its parent.
final int parentId = mUserManager.getProfileParent(managedUserId).id;
if (!isUserSecure(parentId) && !managedUserPassword.isNone()) {
final int parentId = mUserManager.getProfileParent(profileUserId).id;
if (!isUserSecure(parentId) && !profileUserPassword.isNone()) {
if (DEBUG) Slog.v(TAG, "Parent does not have a screen lock but profile has one");
setLockCredentialInternal(LockscreenCredential.createNone(), managedUserPassword,
managedUserId, /* isLockTiedToParent= */ true);
setLockCredentialInternal(LockscreenCredential.createNone(), profileUserPassword,
profileUserId, /* isLockTiedToParent= */ true);
return;
}
// Do not tie when the parent has no SID (but does have a screen lock).
@@ -398,12 +402,12 @@ public class LockSettingsService extends ILockSettings.Stub {
Slog.e(TAG, "Failed to talk to GateKeeper service", e);
return;
}
if (DEBUG) Slog.v(TAG, "Tie managed profile to parent now!");
if (DEBUG) Slog.v(TAG, "Tie profile to parent now!");
try (LockscreenCredential unifiedProfilePassword = generateRandomProfilePassword()) {
setLockCredentialInternal(unifiedProfilePassword, managedUserPassword, managedUserId,
setLockCredentialInternal(unifiedProfilePassword, profileUserPassword, profileUserId,
/* isLockTiedToParent= */ true);
tieProfileLockToParent(managedUserId, unifiedProfilePassword);
mManagedProfilePasswordCache.storePassword(managedUserId, unifiedProfilePassword);
tieProfileLockToParent(profileUserId, unifiedProfilePassword);
mManagedProfilePasswordCache.storePassword(profileUserId, unifiedProfilePassword);
}
}
@@ -765,9 +769,9 @@ public class LockSettingsService extends ILockSettings.Stub {
private void ensureProfileKeystoreUnlocked(int userId) {
final KeyStore ks = KeyStore.getInstance();
if (ks.state(userId) == KeyStore.State.LOCKED
&& mUserManager.getUserInfo(userId).isManagedProfile()
&& isCredentialSharedWithParent(userId)
&& hasUnifiedChallenge(userId)) {
Slog.i(TAG, "Managed profile got unlocked, will unlock its keystore");
Slog.i(TAG, "Profile got unlocked, will unlock its keystore");
// If boot took too long and the password in vold got expired, parent keystore will
// be still locked, we ignore this case since the user will be prompted to unlock
// the device after boot.
@@ -786,8 +790,8 @@ public class LockSettingsService extends ILockSettings.Stub {
// Hide notification first, as tie managed profile lock takes time
hideEncryptionNotification(new UserHandle(userId));
if (mUserManager.getUserInfo(userId).isManagedProfile()) {
tieManagedProfileLockIfNecessary(userId, LockscreenCredential.createNone());
if (isCredentialSharedWithParent(userId)) {
tieProfileLockIfNecessary(userId, LockscreenCredential.createNone());
}
// If the user doesn't have a credential, try and derive their secret for the
@@ -1053,7 +1057,8 @@ public class LockSettingsService extends ILockSettings.Stub {
final int userCount = users.size();
for (int i = 0; i < userCount; i++) {
UserInfo user = users.get(i);
if (user.isManagedProfile() && !getSeparateProfileChallengeEnabledInternal(user.id)) {
if (isCredentialSharedWithParent(user.id)
&& !getSeparateProfileChallengeEnabledInternal(user.id)) {
success &= SyntheticPasswordCrypto.migrateLockSettingsKey(
PROFILE_KEY_NAME_ENCRYPT + user.id);
success &= SyntheticPasswordCrypto.migrateLockSettingsKey(
@@ -1177,24 +1182,24 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public void setSeparateProfileChallengeEnabled(int userId, boolean enabled,
LockscreenCredential managedUserPassword) {
LockscreenCredential profileUserPassword) {
checkWritePermission(userId);
if (!mHasSecureLockScreen
&& managedUserPassword != null
&& managedUserPassword.getType() != CREDENTIAL_TYPE_NONE) {
&& profileUserPassword != null
&& profileUserPassword.getType() != CREDENTIAL_TYPE_NONE) {
throw new UnsupportedOperationException(
"This operation requires secure lock screen feature.");
}
synchronized (mSeparateChallengeLock) {
setSeparateProfileChallengeEnabledLocked(userId, enabled, managedUserPassword != null
? managedUserPassword : LockscreenCredential.createNone());
setSeparateProfileChallengeEnabledLocked(userId, enabled, profileUserPassword != null
? profileUserPassword : LockscreenCredential.createNone());
}
notifySeparateProfileChallengeChanged(userId);
}
@GuardedBy("mSeparateChallengeLock")
private void setSeparateProfileChallengeEnabledLocked(@UserIdInt int userId,
boolean enabled, LockscreenCredential managedUserPassword) {
boolean enabled, LockscreenCredential profileUserPassword) {
final boolean old = getBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, false, userId);
setBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, enabled, userId);
try {
@@ -1202,7 +1207,7 @@ public class LockSettingsService extends ILockSettings.Stub {
mStorage.removeChildProfileLock(userId);
removeKeystoreProfileKey(userId);
} else {
tieManagedProfileLockIfNecessary(userId, managedUserPassword);
tieProfileLockIfNecessary(userId, profileUserPassword);
}
} catch (IllegalStateException e) {
setBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, old, userId);
@@ -1398,8 +1403,8 @@ public class LockSettingsService extends ILockSettings.Stub {
}
/**
* Unlock the user (both storage and user state) and its associated managed profiles
* synchronously.
* Unlock the user (both storage and user state) and its associated profiles
* that share lock credential (e.g. managed and clone profiles) synchronously.
*
* <em>Be very careful about the risk of deadlock here: ActivityManager.unlockUser()
* can end up calling into other system services to process user unlock request (via
@@ -1441,7 +1446,7 @@ public class LockSettingsService extends ILockSettings.Stub {
Thread.currentThread().interrupt();
}
if (mUserManager.getUserInfo(userId).isManagedProfile()) {
if (isCredentialSharedWithParent(userId)) {
if (!hasUnifiedChallenge(userId)) {
mBiometricDeferredQueue.processPendingLockoutResets();
}
@@ -1450,11 +1455,11 @@ public class LockSettingsService extends ILockSettings.Stub {
for (UserInfo profile : mUserManager.getProfiles(userId)) {
if (profile.id == userId) continue;
if (!profile.isManagedProfile()) continue;
if (!isCredentialSharedWithParent(profile.id)) continue;
if (hasUnifiedChallenge(profile.id)) {
if (mUserManager.isUserRunning(profile.id)) {
// Unlock managed profile with unified lock
// Unlock profile with unified lock
unlockChildProfile(profile.id, false /* ignoreUserNotAuthenticated */);
} else {
try {
@@ -1487,7 +1492,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
private Map<Integer, LockscreenCredential> getDecryptedPasswordsForAllTiedProfiles(int userId) {
if (mUserManager.getUserInfo(userId).isManagedProfile()) {
if (isCredentialSharedWithParent(userId)) {
return null;
}
Map<Integer, LockscreenCredential> result = new ArrayMap<>();
@@ -1495,21 +1500,21 @@ public class LockSettingsService extends ILockSettings.Stub {
final int size = profiles.size();
for (int i = 0; i < size; i++) {
final UserInfo profile = profiles.get(i);
if (!profile.isManagedProfile()) {
if (!isCredentialSharedWithParent(profile.id)) {
continue;
}
final int managedUserId = profile.id;
if (getSeparateProfileChallengeEnabledInternal(managedUserId)) {
final int profileUserId = profile.id;
if (getSeparateProfileChallengeEnabledInternal(profileUserId)) {
continue;
}
try {
result.put(managedUserId, getDecryptedPasswordForTiedProfile(managedUserId));
result.put(profileUserId, getDecryptedPasswordForTiedProfile(profileUserId));
} catch (KeyStoreException | UnrecoverableKeyException | NoSuchAlgorithmException
| NoSuchPaddingException | InvalidKeyException
| InvalidAlgorithmParameterException | IllegalBlockSizeException
| BadPaddingException | CertificateException | IOException e) {
Slog.e(TAG, "getDecryptedPasswordsForAllTiedProfiles failed for user " +
managedUserId, e);
profileUserId, e);
}
}
return result;
@@ -1525,11 +1530,12 @@ public class LockSettingsService extends ILockSettings.Stub {
*
* 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.
* terminates when the user is a profile that shares lock credentials with parent.
* (e.g. managed and clone profile).
*/
private void synchronizeUnifiedWorkChallengeForProfiles(int userId,
Map<Integer, LockscreenCredential> profilePasswordMap) {
if (mUserManager.getUserInfo(userId).isManagedProfile()) {
if (isCredentialSharedWithParent(userId)) {
return;
}
final boolean isSecure = isUserSecure(userId);
@@ -1537,25 +1543,25 @@ public class LockSettingsService extends ILockSettings.Stub {
final int size = profiles.size();
for (int i = 0; i < size; i++) {
final UserInfo profile = profiles.get(i);
if (profile.isManagedProfile()) {
final int managedUserId = profile.id;
if (getSeparateProfileChallengeEnabledInternal(managedUserId)) {
final int profileUserId = profile.id;
if (isCredentialSharedWithParent(profileUserId)) {
if (getSeparateProfileChallengeEnabledInternal(profileUserId)) {
continue;
}
if (isSecure) {
tieManagedProfileLockIfNecessary(managedUserId,
tieProfileLockIfNecessary(profileUserId,
LockscreenCredential.createNone());
} else {
// We use cached work profile password computed before clearing the parent's
// credential, otherwise they get lost
if (profilePasswordMap != null
&& profilePasswordMap.containsKey(managedUserId)) {
&& profilePasswordMap.containsKey(profileUserId)) {
setLockCredentialInternal(LockscreenCredential.createNone(),
profilePasswordMap.get(managedUserId),
managedUserId,
profilePasswordMap.get(profileUserId),
profileUserId,
/* isLockTiedToParent= */ true);
mStorage.removeChildProfileLock(managedUserId);
removeKeystoreProfileKey(managedUserId);
mStorage.removeChildProfileLock(profileUserId);
removeKeystoreProfileKey(profileUserId);
} else {
Slog.wtf(TAG, "Attempt to clear tied challenge, but no password supplied.");
}
@@ -1564,13 +1570,13 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
private boolean isManagedProfileWithUnifiedLock(int userId) {
return mUserManager.getUserInfo(userId).isManagedProfile()
private boolean isProfileWithUnifiedLock(int userId) {
return isCredentialSharedWithParent(userId)
&& !getSeparateProfileChallengeEnabledInternal(userId);
}
private boolean isManagedProfileWithSeparatedLock(int userId) {
return mUserManager.getUserInfo(userId).isManagedProfile()
private boolean isProfileWithSeparatedLock(int userId) {
return isCredentialSharedWithParent(userId)
&& getSeparateProfileChallengeEnabledInternal(userId);
}
@@ -1587,7 +1593,7 @@ public class LockSettingsService extends ILockSettings.Stub {
// A profile with a unified lock screen stores a randomly generated credential, so skip it.
// Its parent will send credentials for the profile, as it stores the unified lock
// credential.
if (isManagedProfileWithUnifiedLock(userId)) {
if (isProfileWithUnifiedLock(userId)) {
return;
}
@@ -1631,7 +1637,7 @@ public class LockSettingsService extends ILockSettings.Stub {
for (UserInfo profile : mUserManager.getProfiles(userId)) {
if (profile.id == userId
|| (profile.profileGroupId == userId
&& isManagedProfileWithUnifiedLock(profile.id))) {
&& isProfileWithUnifiedLock(profile.id))) {
profiles.add(profile.id);
}
}
@@ -1666,7 +1672,7 @@ public class LockSettingsService extends ILockSettings.Stub {
// accept only the parent user credential on its public API interfaces, swap it
// with the profile's random credential at that API boundary (i.e. here) and make
// sure LSS internally does not special case profile with unififed challenge: b/80170828
if (!savedCredential.isNone() && isManagedProfileWithUnifiedLock(userId)) {
if (!savedCredential.isNone() && isProfileWithUnifiedLock(userId)) {
// Verify the parent credential again, to make sure we have a fresh enough
// auth token such that getDecryptedPasswordForTiedProfile() inside
// setLockCredentialInternal() can function correctly.
@@ -1684,7 +1690,7 @@ public class LockSettingsService extends ILockSettings.Stub {
setSeparateProfileChallengeEnabledLocked(userId, true, /* unused */ null);
notifyPasswordChanged(userId);
}
if (mUserManager.getUserInfo(userId).isManagedProfile()) {
if (isCredentialSharedWithParent(userId)) {
// Make sure the profile doesn't get locked straight after setting work challenge.
setDeviceUnlockedForUser(userId);
}
@@ -1698,7 +1704,8 @@ public class LockSettingsService extends ILockSettings.Stub {
}
/**
* @param savedCredential if the user is a managed profile with unified challenge and
* @param savedCredential if the user is a profile with
* {@link UserManager#isCredentialSharedWithParent()} with unified challenge and
* savedCredential is empty, LSS will try to re-derive the profile password internally.
* TODO (b/80170828): Fix this so profile password is always passed in.
* @param isLockTiedToParent is {@code true} if {@code userId} is a profile and its new
@@ -1732,8 +1739,8 @@ public class LockSettingsService extends ILockSettings.Stub {
}
CredentialHash currentHandle = mStorage.readCredentialHash(userId);
if (isManagedProfileWithUnifiedLock(userId)) {
// get credential from keystore when managed profile has unified lock
if (isProfileWithUnifiedLock(userId)) {
// get credential from keystore when managed/clone profile has unified lock
if (savedCredential.isNone()) {
try {
//TODO: remove as part of b/80170828
@@ -1907,6 +1914,26 @@ public class LockSettingsService extends ILockSettings.Stub {
return value != 0;
}
private UserManager getUserManagerFromCache(int userId) {
UserHandle userHandle = UserHandle.of(userId);
if (mUserManagerCache.containsKey(userHandle)) {
return mUserManagerCache.get(userHandle);
}
try {
Context userContext = mContext.createPackageContextAsUser("system", 0, userHandle);
UserManager userManager = userContext.getSystemService(UserManager.class);
mUserManagerCache.put(userHandle, userManager);
return userManager;
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException("Failed to create context for user " + userHandle, e);
}
}
protected boolean isCredentialSharedWithParent(int userId) {
return getUserManagerFromCache(userId).isCredentialSharedWithParent();
}
private VerifyCredentialResponse convertResponse(GateKeeperResponse gateKeeperResponse) {
return VerifyCredentialResponse.fromGateKeeperResponse(gateKeeperResponse);
}
@@ -2220,23 +2247,17 @@ public class LockSettingsService extends ILockSettings.Stub {
public void resetKeyStore(int userId) {
checkWritePermission(userId);
if (DEBUG) Slog.v(TAG, "Reset keystore for user: " + userId);
int managedUserId = -1;
LockscreenCredential managedUserDecryptedPassword = null;
List<Integer> profileUserIds = new ArrayList<>();
List<LockscreenCredential> profileUserDecryptedPasswords = new ArrayList<>();
final List<UserInfo> profiles = mUserManager.getProfiles(userId);
for (UserInfo pi : profiles) {
// Unlock managed profile with unified lock
if (pi.isManagedProfile()
// Unlock profile which shares credential with parent with unified lock
if (isCredentialSharedWithParent(pi.id)
&& !getSeparateProfileChallengeEnabledInternal(pi.id)
&& mStorage.hasChildProfileLock(pi.id)) {
try {
if (managedUserId == -1) {
managedUserDecryptedPassword = getDecryptedPasswordForTiedProfile(pi.id);
managedUserId = pi.id;
} else {
// Should not happen
Slog.e(TAG, "More than one managed profile, uid1:" + managedUserId
+ ", uid2:" + pi.id);
}
profileUserDecryptedPasswords.add(getDecryptedPasswordForTiedProfile(pi.id));
profileUserIds.add(pi.id);
} catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
| NoSuchAlgorithmException | NoSuchPaddingException
| InvalidAlgorithmParameterException | IllegalBlockSizeException
@@ -2258,14 +2279,18 @@ public class LockSettingsService extends ILockSettings.Stub {
KeyProperties.NAMESPACE_WIFI);
}
} finally {
if (managedUserId != -1 && managedUserDecryptedPassword != null) {
if (DEBUG) Slog.v(TAG, "Restore tied profile lock");
tieProfileLockToParent(managedUserId, managedUserDecryptedPassword);
for (int i = 0; i < profileUserIds.size(); ++i) {
int piUserId = profileUserIds.get(i);
LockscreenCredential piUserDecryptedPassword = profileUserDecryptedPasswords.get(i);
if (piUserId != -1 && piUserDecryptedPassword != null) {
if (DEBUG) Slog.v(TAG, "Restore tied profile lock");
tieProfileLockToParent(piUserId, piUserDecryptedPassword);
}
if (piUserDecryptedPassword != null) {
piUserDecryptedPassword.zeroize();
}
}
}
if (managedUserDecryptedPassword != null) {
managedUserDecryptedPassword.zeroize();
}
}
@Override
@@ -2387,8 +2412,9 @@ public class LockSettingsService extends ILockSettings.Stub {
public VerifyCredentialResponse verifyTiedProfileChallenge(LockscreenCredential credential,
int userId, @LockPatternUtils.VerifyFlag int flags) {
checkPasswordReadPermission();
if (!isManagedProfileWithUnifiedLock(userId)) {
throw new IllegalArgumentException("User id must be managed profile with unified lock");
if (!isProfileWithUnifiedLock(userId)) {
throw new IllegalArgumentException(
"User id must be managed/clone profile with unified lock");
}
final int parentProfileId = mUserManager.getProfileParent(userId).id;
// Unlock parent by using parent's challenge
@@ -2466,7 +2492,7 @@ public class LockSettingsService extends ILockSettings.Stub {
+ response.getGatekeeperHAT().length);
unlockUser(userId, response.getGatekeeperHAT(), secretFromCredential(credential));
if (isManagedProfileWithSeparatedLock(userId)) {
if (isProfileWithSeparatedLock(userId)) {
setDeviceUnlockedForUser(userId);
}
if (shouldReEnroll) {
@@ -2619,7 +2645,7 @@ public class LockSettingsService extends ILockSettings.Stub {
mManagedProfilePasswordCache.removePassword(userId);
gateKeeperClearSecureUserId(userId);
if (unknownUser || mUserManager.getUserInfo(userId).isManagedProfile()) {
if (unknownUser || isCredentialSharedWithParent(userId)) {
removeKeystoreProfileKey(userId);
}
// Clean up storage last, this is to ensure that cleanupDataForReusedUserIdIfNecessary()
@@ -3129,7 +3155,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
activateEscrowTokens(authToken, userId);
if (isManagedProfileWithSeparatedLock(userId)) {
if (isProfileWithSeparatedLock(userId)) {
setDeviceUnlockedForUser(userId);
}
mStrongAuth.reportSuccessfulStrongAuthUnlock(userId);
@@ -3286,7 +3312,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
/**
* @param savedCredential if the user is a managed profile with unified challenge and
* @param savedCredential if the user is a profile with unified challenge and
* savedCredential is empty, LSS will try to re-derive the profile password internally.
* TODO (b/80170828): Fix this so profile password is always passed in.
*/
@@ -3294,8 +3320,8 @@ public class LockSettingsService extends ILockSettings.Stub {
private boolean spBasedSetLockCredentialInternalLocked(LockscreenCredential credential,
LockscreenCredential savedCredential, int userId, boolean isLockTiedToParent) {
if (DEBUG) Slog.d(TAG, "spBasedSetLockCredentialInternalLocked: user=" + userId);
if (savedCredential.isNone() && isManagedProfileWithUnifiedLock(userId)) {
// get credential from keystore when managed profile has unified lock
if (savedCredential.isNone() && isProfileWithUnifiedLock(userId)) {
// get credential from keystore when profile has unified lock
try {
//TODO: remove as part of b/80170828
savedCredential = getDecryptedPasswordForTiedProfile(userId);
@@ -3339,13 +3365,14 @@ public class LockSettingsService extends ILockSettings.Stub {
* Returns a fixed pseudorandom byte string derived from the user's synthetic password.
* This is used to salt the password history hash to protect the hash against offline
* bruteforcing, since rederiving this value requires a successful authentication.
* If user is a managed profile with unified challenge, currentCredential is ignored.
* If user is a profile with {@link UserManager#isCredentialSharedWithParent()} true and with
* unified challenge, currentCredential is ignored.
*/
@Override
public byte[] getHashFactor(LockscreenCredential currentCredential, int userId) {
checkPasswordReadPermission();
try {
if (isManagedProfileWithUnifiedLock(userId)) {
if (isProfileWithUnifiedLock(userId)) {
try {
currentCredential = getDecryptedPasswordForTiedProfile(userId);
} catch (Exception e) {
@@ -3815,10 +3842,11 @@ public class LockSettingsService extends ILockSettings.Stub {
public PasswordMetrics getUserPasswordMetrics(int userHandle) {
final long identity = Binder.clearCallingIdentity();
try {
if (isManagedProfileWithUnifiedLock(userHandle)) {
// A managed profile with unified challenge is supposed to be protected by the
// parent lockscreen, so asking for its password metrics is not really useful,
// as this method would just return the metrics of the random profile password
if (isProfileWithUnifiedLock(userHandle)) {
// A managed/clone profile with unified challenge is supposed to be protected by
// the parent lockscreen, so asking for its password metrics is not really
// useful, as this method would just return the metrics of the random profile
// password
Slog.w(TAG, "Querying password metrics for unified challenge profile: "
+ userHandle);
}

View File

@@ -1555,6 +1555,17 @@ public class UserManagerService extends IUserManager.Stub {
}
}
@Override
public boolean isCredentialSharedWithParent(@UserIdInt int userId) {
checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,
"isCredentialSharedWithParent");
synchronized (mUsersLock) {
UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
return userTypeDetails != null && userTypeDetails.isProfile()
&& userTypeDetails.isCredentialSharedWithParent();
}
}
@Override
public boolean isUserUnlockingOrUnlocked(@UserIdInt int userId) {
checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,

View File

@@ -156,6 +156,13 @@ public final class UserTypeDetails {
*/
private final boolean mIsMediaSharedWithParent;
/**
* Denotes if the user shares encryption credentials with its parent user.
*
* <p> Default value is false
*/
private final boolean mIsCredentialSharedWithParent;
private UserTypeDetails(@NonNull String name, boolean enabled, int maxAllowed,
@UserInfoFlag int baseType, @UserInfoFlag int defaultUserInfoPropertyFlags, int label,
int maxAllowedPerParent,
@@ -166,7 +173,8 @@ public final class UserTypeDetails {
@Nullable Bundle defaultSystemSettings,
@Nullable Bundle defaultSecureSettings,
@Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters,
boolean isMediaSharedWithParent) {
boolean isMediaSharedWithParent,
boolean isCredentialSharedWithParent) {
this.mName = name;
this.mEnabled = enabled;
this.mMaxAllowed = maxAllowed;
@@ -186,6 +194,7 @@ public final class UserTypeDetails {
this.mBadgeColors = badgeColors;
this.mDarkThemeBadgeColors = darkThemeBadgeColors;
this.mIsMediaSharedWithParent = isMediaSharedWithParent;
this.mIsCredentialSharedWithParent = isCredentialSharedWithParent;
}
/**
@@ -310,6 +319,14 @@ public final class UserTypeDetails {
return mIsMediaSharedWithParent;
}
/**
* Returns true if the user has shared encryption credential with parent user or
* false otherwise.
*/
public boolean isCredentialSharedWithParent() {
return mIsCredentialSharedWithParent;
}
/** Returns a {@link Bundle} representing the default user restrictions. */
@NonNull Bundle getDefaultRestrictions() {
return BundleUtils.clone(mDefaultRestrictions);
@@ -402,6 +419,7 @@ public final class UserTypeDetails {
private @DrawableRes int mBadgePlain = Resources.ID_NULL;
private @DrawableRes int mBadgeNoBackground = Resources.ID_NULL;
private boolean mIsMediaSharedWithParent = false;
private boolean mIsCredentialSharedWithParent = false;
public Builder setName(String name) {
mName = name;
@@ -501,6 +519,15 @@ public final class UserTypeDetails {
return this;
}
/**
* Sets shared media property for the user.
* @param isCredentialSharedWithParent the value to be set, true or false
*/
public Builder setIsCredentialSharedWithParent(boolean isCredentialSharedWithParent) {
mIsCredentialSharedWithParent = isCredentialSharedWithParent;
return this;
}
@UserInfoFlag int getBaseType() {
return mBaseType;
}
@@ -543,7 +570,8 @@ public final class UserTypeDetails {
mDefaultSystemSettings,
mDefaultSecureSettings,
mDefaultCrossProfileIntentFilters,
mIsMediaSharedWithParent);
mIsMediaSharedWithParent,
mIsCredentialSharedWithParent);
}
private boolean hasBadge() {

View File

@@ -121,7 +121,8 @@ public final class UserTypeFactory {
.setMaxAllowedPerParent(1)
.setLabel(0)
.setDefaultRestrictions(null)
.setIsMediaSharedWithParent(true);
.setIsMediaSharedWithParent(true)
.setIsCredentialSharedWithParent(true);
}
/**
@@ -152,7 +153,8 @@ public final class UserTypeFactory {
com.android.internal.R.color.profile_badge_3_dark)
.setDefaultRestrictions(getDefaultManagedProfileRestrictions())
.setDefaultSecureSettings(getDefaultManagedProfileSecureSettings())
.setDefaultCrossProfileIntentFilters(getDefaultManagedCrossProfileIntentFilter());
.setDefaultCrossProfileIntentFilters(getDefaultManagedCrossProfileIntentFilter())
.setIsCredentialSharedWithParent(true);
}
/**

View File

@@ -22,6 +22,7 @@ import android.app.IActivityManager;
import android.app.admin.DeviceStateCache;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.UserInfo;
import android.hardware.authsecret.V1_0.IAuthSecret;
import android.os.Handler;
import android.os.Parcel;
@@ -214,4 +215,10 @@ public class LockSettingsServiceTestable extends LockSettingsService {
void setKeystorePassword(byte[] password, int userHandle) {
}
@Override
protected boolean isCredentialSharedWithParent(int userId) {
UserInfo userInfo = mUserManager.getUserInfo(userId);
return userInfo.isCloneProfile() || userInfo.isManagedProfile();
}
}