Keep managed profile keystores in sync with owner
Fixes setting a keyguard password for keystore in a multi-user setup
while we're at it.
Bug: 16233206.
Change-Id: I7941707ca66ac25bd122fd22e5e0f639e7af697e
diff --git a/core/java/android/security/IKeystoreService.java b/core/java/android/security/IKeystoreService.java
index f8bf45b..7e9aba0 100644
--- a/core/java/android/security/IKeystoreService.java
+++ b/core/java/android/security/IKeystoreService.java
@@ -478,6 +478,59 @@
}
return _result;
}
+
+ public int reset_uid(int uid) throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ int _result;
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeInt(uid);
+ mRemote.transact(Stub.TRANSACTION_reset_uid, _data, _reply, 0);
+ _reply.readException();
+ _result = _reply.readInt();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ return _result;
+ }
+
+ public int sync_uid(int srcUid, int dstUid) throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ int _result;
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeInt(srcUid);
+ _data.writeInt(dstUid);
+ mRemote.transact(Stub.TRANSACTION_sync_uid, _data, _reply, 0);
+ _reply.readException();
+ _result = _reply.readInt();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ return _result;
+ }
+
+ public int password_uid(String password, int uid) throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ int _result;
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeString(password);
+ _data.writeInt(uid);
+ mRemote.transact(Stub.TRANSACTION_password_uid, _data, _reply, 0);
+ _reply.readException();
+ _result = _reply.readInt();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ return _result;
+ }
}
private static final String DESCRIPTOR = "android.security.keystore";
@@ -505,6 +558,9 @@
static final int TRANSACTION_duplicate = IBinder.FIRST_CALL_TRANSACTION + 20;
static final int TRANSACTION_is_hardware_backed = IBinder.FIRST_CALL_TRANSACTION + 21;
static final int TRANSACTION_clear_uid = IBinder.FIRST_CALL_TRANSACTION + 22;
+ static final int TRANSACTION_reset_uid = IBinder.FIRST_CALL_TRANSACTION + 23;
+ static final int TRANSACTION_sync_uid = IBinder.FIRST_CALL_TRANSACTION + 24;
+ static final int TRANSACTION_password_uid = IBinder.FIRST_CALL_TRANSACTION + 25;
/**
* Cast an IBinder object into an IKeystoreService interface, generating
@@ -597,4 +653,10 @@
public int is_hardware_backed(String string) throws RemoteException;
public int clear_uid(long uid) throws RemoteException;
+
+ public int reset_uid(int uid) throws RemoteException;
+
+ public int sync_uid(int sourceUid, int targetUid) throws RemoteException;
+
+ public int password_uid(String password, int uid) throws RemoteException;
}
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 6ac49ee..0db8c77 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -331,6 +331,36 @@
}
}
+ public boolean resetUid(int uid) {
+ try {
+ mError = mBinder.reset_uid(uid);
+ return mError == NO_ERROR;
+ } catch (RemoteException e) {
+ Log.w(TAG, "Cannot connect to keystore", e);
+ return false;
+ }
+ }
+
+ public boolean syncUid(int sourceUid, int targetUid) {
+ try {
+ mError = mBinder.sync_uid(sourceUid, targetUid);
+ return mError == NO_ERROR;
+ } catch (RemoteException e) {
+ Log.w(TAG, "Cannot connect to keystore", e);
+ return false;
+ }
+ }
+
+ public boolean passwordUid(String password, int uid) {
+ try {
+ mError = mBinder.password_uid(password, uid);
+ return mError == NO_ERROR;
+ } catch (RemoteException e) {
+ Log.w(TAG, "Cannot connect to keystore", e);
+ return false;
+ }
+ }
+
public int getLastError() {
return mError;
}
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 86ce961..92f5170 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -16,9 +16,12 @@
package com.android.server;
+import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
@@ -31,6 +34,7 @@
import android.os.Binder;
import android.os.Environment;
import android.os.IBinder;
+import android.os.Process;
import android.os.RemoteException;
import android.os.storage.IMountService;
import android.os.ServiceManager;
@@ -41,11 +45,14 @@
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.provider.Settings.SettingNotFoundException;
+import android.security.KeyChain;
+import android.security.KeyChain.KeyChainConnection;
import android.security.KeyStore;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.ILockSettingsObserver;
import com.android.internal.widget.LockPatternUtils;
@@ -99,8 +106,30 @@
mLockPatternUtils = new LockPatternUtils(context);
mFirstCallToVold = true;
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_USER_ADDED);
+ mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
}
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // Update keystore settings for profiles which use the same password as their parent
+ if (Intent.ACTION_USER_STARTED.equals(intent.getAction())) {
+ final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
+ final UserInfo parentInfo = um.getProfileParent(userHandle);
+ if (parentInfo != null) {
+ final KeyStore ks = KeyStore.getInstance();
+ final int profileUid = UserHandle.getUid(userHandle, Process.SYSTEM_UID);
+ final int parentUid = UserHandle.getUid(parentInfo.id, Process.SYSTEM_UID);
+ ks.syncUid(parentUid, profileUid);
+ }
+ }
+ }
+ };
+
public void systemReady() {
migrateOldData();
}
@@ -275,6 +304,17 @@
}
}
+ private int getUserParentOrSelfId(int userId) {
+ if (userId != 0) {
+ final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
+ final UserInfo pi = um.getProfileParent(userId);
+ if (pi != null) {
+ return pi.id;
+ }
+ }
+ return userId;
+ }
+
private String getLockPatternFilename(int userId) {
String dataSystemDirectory =
android.os.Environment.getDataDirectory().getAbsolutePath() +
@@ -283,6 +323,7 @@
// Leave it in the same place for user 0
return dataSystemDirectory + LOCK_PATTERN_FILE;
} else {
+ userId = getUserParentOrSelfId(userId);
return new File(Environment.getUserSystemDirectory(userId), LOCK_PATTERN_FILE)
.getAbsolutePath();
}
@@ -296,7 +337,8 @@
// Leave it in the same place for user 0
return dataSystemDirectory + LOCK_PASSWORD_FILE;
} else {
- return new File(Environment.getUserSystemDirectory(userId), LOCK_PASSWORD_FILE)
+ userId = getUserParentOrSelfId(userId);
+ return new File(Environment.getUserSystemDirectory(userId), LOCK_PASSWORD_FILE)
.getAbsolutePath();
}
}
@@ -315,16 +357,27 @@
return new File(getLockPatternFilename(userId)).length() > 0;
}
- private void maybeUpdateKeystore(String password, int userId) {
- if (userId == UserHandle.USER_OWNER) {
- final KeyStore keyStore = KeyStore.getInstance();
- // Conditionally reset the keystore if empty. If non-empty, we are just
- // switching key guard type
- if (TextUtils.isEmpty(password) && keyStore.isEmpty()) {
- keyStore.reset();
+ private void maybeUpdateKeystore(String password, int userHandle) {
+ final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
+ final KeyStore ks = KeyStore.getInstance();
+
+ final List<UserInfo> profiles = um.getProfiles(userHandle);
+ boolean shouldReset = TextUtils.isEmpty(password);
+
+ // For historical reasons, don't wipe a non-empty keystore if we have a single user with a
+ // single profile.
+ if (userHandle == UserHandle.USER_OWNER && profiles.size() == 1) {
+ if (!ks.isEmpty()) {
+ shouldReset = false;
+ }
+ }
+
+ for (UserInfo pi : profiles) {
+ final int profileUid = UserHandle.getUid(pi.id, Process.SYSTEM_UID);
+ if (shouldReset) {
+ ks.resetUid(profileUid);
} else {
- // Update the keystore password
- keyStore.password(password);
+ ks.passwordUid(password, profileUid);
}
}
}