From 2f7e1e487c8ef486a16ad2398ffee413b53da04e Mon Sep 17 00:00:00 2001 From: Robin Lee Date: Mon, 21 Mar 2016 10:50:01 +0000 Subject: [PATCH] API to approve CA certificates Bug: 18224038 Change-Id: Id928872cd70dac5a5ecfdcd52150fe6dea544e3b --- .../android/app/admin/DevicePolicyManager.java | 37 +++++++ .../android/app/admin/IDevicePolicyManager.aidl | 2 + .../devicepolicy/DevicePolicyManagerService.java | 109 ++++++++++++++++----- 3 files changed, 126 insertions(+), 22 deletions(-) diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index e7427bfabe01..b06350411944 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2654,6 +2654,43 @@ public class DevicePolicyManager { } /** + * Mark a CA certificate as approved by the device user. This means that they have been notified + * of the installation, were made aware of the risks, viewed the certificate and still wanted to + * keep the certificate on the device. + * + * Calling with {@param approval} as {@code true} will cancel any ongoing warnings related to + * this certificate. + * + * @hide + */ + public boolean approveCaCert(String alias, int userHandle, boolean approval) { + if (mService != null) { + try { + return mService.approveCaCert(alias, userHandle, approval); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return false; + } + + /** + * Check whether a CA certificate has been approved by the device user. + * + * @hide + */ + public boolean isCaCertApproved(String alias, int userHandle) { + if (mService != null) { + try { + return mService.isCaCertApproved(alias, userHandle); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return false; + } + + /** * Installs the given certificate as a user CA. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index aed220dd5705..b6174f1b6ee2 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -144,6 +144,8 @@ interface IDevicePolicyManager { boolean installCaCert(in ComponentName admin, in byte[] certBuffer); void uninstallCaCerts(in ComponentName admin, in String[] aliases); void enforceCanManageCaCerts(in ComponentName admin); + boolean approveCaCert(in String alias, int userHandle, boolean approval); + boolean isCaCertApproved(in String alias, int userHandle); boolean installKeyPair(in ComponentName who, in byte[] privKeyBuffer, in byte[] certBuffer, String alias, boolean requestAccess); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index d8b856b9b753..9203e9982c04 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -136,6 +136,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.JournaledFile; +import com.android.internal.util.ParcelableString; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.internal.widget.LockPatternUtils; @@ -184,12 +185,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private static final String DEVICE_POLICIES_XML = "device_policies.xml"; + private static final String TAG_ACCEPTED_CA_CERTIFICATES = "accepted-ca-certificate"; + private static final String TAG_LOCK_TASK_COMPONENTS = "lock-task-component"; private static final String TAG_STATUS_BAR = "statusbar"; private static final String ATTR_DISABLED = "disabled"; + private static final String ATTR_NAME = "name"; + private static final String DO_NOT_ASK_CREDENTIALS_ON_BOOT_XML = "do-not-ask-credentials-on-boot"; @@ -420,6 +425,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final ArrayList mAdminList = new ArrayList<>(); final ArrayList mRemovingAdmins = new ArrayList<>(); + final ArraySet mAcceptedCaCertificates = new ArraySet<>(); + // This is the list of component allowed to start lock task mode. List mLockTaskPackages = new ArrayList<>(); @@ -483,7 +490,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } if (Intent.ACTION_BOOT_COMPLETED.equals(action) || KeyChain.ACTION_STORAGE_CHANGED.equals(action)) { - new MonitoringCertNotificationTask().execute(intent); + int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_ALL); + new MonitoringCertNotificationTask().execute(userId); } if (Intent.ACTION_USER_ADDED.equals(action)) { disableSecurityLoggingIfNotCompliant(); @@ -2215,6 +2223,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { out.endTag(null, "active-password"); } + for (int i = 0; i < policy.mAcceptedCaCertificates.size(); i++) { + out.startTag(null, TAG_ACCEPTED_CA_CERTIFICATES); + out.attribute(null, ATTR_NAME, policy.mAcceptedCaCertificates.valueAt(i)); + out.endTag(null, TAG_ACCEPTED_CA_CERTIFICATES); + } + for (int i=0; i { + private class MonitoringCertNotificationTask extends AsyncTask { @Override - protected Void doInBackground(Intent... params) { - int userHandle = params[0].getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_ALL); + protected Void doInBackground(Integer... params) { + int userHandle = params[0]; if (userHandle == UserHandle.USER_ALL) { for (UserInfo userInfo : mUserManager.getUsers()) { manageNotification(userInfo.getUserHandle()); } } else { - manageNotification(new UserHandle(userHandle)); + manageNotification(UserHandle.of(userHandle)); } return null; } @@ -2652,25 +2668,27 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return; } - // Call out to KeyChain to check for user-added CAs - boolean hasCert = false; + // Call out to KeyChain to check for CAs which are waiting for approval. + final List pendingCertificates; try { - KeyChainConnection kcs = KeyChain.bindAsUser(mContext, userHandle); - try { - if (!kcs.getService().getUserCaAliases().getList().isEmpty()) { - hasCert = true; - } - } catch (RemoteException e) { - Log.e(LOG_TAG, "Could not connect to KeyChain service", e); - } finally { - kcs.close(); + pendingCertificates = getInstalledCaCertificates(userHandle); + } catch (RemoteException | RuntimeException e) { + Log.e(LOG_TAG, "Could not retrieve certificates from KeyChain service", e); + return; + } + + synchronized (DevicePolicyManagerService.this) { + final DevicePolicyData policy = getUserData(userHandle.getIdentifier()); + + // Remove deleted certificates. Flush xml if necessary. + if (policy.mAcceptedCaCertificates.retainAll(pendingCertificates)) { + saveSettingsLocked(userHandle.getIdentifier()); } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } catch (RuntimeException | AssertionError e) { - Log.e(LOG_TAG, "Could not connect to KeyChain service", e); + // Trim to approved certificates. + pendingCertificates.removeAll(policy.mAcceptedCaCertificates); } - if (!hasCert) { + + if (pendingCertificates.isEmpty()) { mInjector.getNotificationManager().cancelAsUser( null, MONITORING_CERT_NOTIFICATION_ID, userHandle); return; @@ -2701,7 +2719,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final Context userContext; try { - userContext = mContext.createPackageContextAsUser("android", 0, userHandle); + final String packageName = mContext.getPackageName(); + userContext = mContext.createPackageContextAsUser(packageName, 0, userHandle); } catch (PackageManager.NameNotFoundException e) { Log.e(LOG_TAG, "Create context as " + userHandle + " failed", e); return; @@ -2720,6 +2739,29 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mInjector.getNotificationManager().notifyAsUser( null, MONITORING_CERT_NOTIFICATION_ID, noti, userHandle); } + + private List getInstalledCaCertificates(UserHandle userHandle) + throws RemoteException, RuntimeException { + KeyChainConnection conn = null; + try { + conn = KeyChain.bindAsUser(mContext, userHandle); + List aliases = conn.getService().getUserCaAliases().getList(); + List result = new ArrayList<>(aliases.size()); + for (int i = 0; i < aliases.size(); i++) { + result.add(aliases.get(i).string); + } + return result; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return null; + } catch (AssertionError e) { + throw new RuntimeException(e); + } finally { + if (conn != null) { + conn.close(); + } + } + } } /** @@ -4070,6 +4112,29 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @Override + public boolean approveCaCert(String alias, int userId, boolean approval) { + enforceManageUsers(); + synchronized (this) { + Set certs = getUserData(userId).mAcceptedCaCertificates; + boolean changed = (approval ? certs.add(alias) : certs.remove(alias)); + if (!changed) { + return false; + } + saveSettingsLocked(userId); + } + new MonitoringCertNotificationTask().execute(userId); + return true; + } + + @Override + public boolean isCaCertApproved(String alias, int userId) { + enforceManageUsers(); + synchronized (this) { + return getUserData(userId).mAcceptedCaCertificates.contains(alias); + } + } + + @Override public boolean installCaCert(ComponentName admin, byte[] certBuffer) throws RemoteException { enforceCanManageCaCerts(admin); -- 2.11.0