Merge "API to approve CA certificates" into nyc-dev
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index c9b1d0c..656c7ff 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2655,6 +2655,43 @@
}
/**
+ * 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 8fa4c3a..6ee56aa 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -144,6 +144,8 @@
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,
in byte[] certChainBuffer, 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 ac6fc58..6fe5c16 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.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 @@
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 @@
final ArrayList<ActiveAdmin> mAdminList = new ArrayList<>();
final ArrayList<ComponentName> mRemovingAdmins = new ArrayList<>();
+ final ArraySet<String> mAcceptedCaCertificates = new ArraySet<>();
+
// This is the list of component allowed to start lock task mode.
List<String> mLockTaskPackages = new ArrayList<>();
@@ -483,7 +490,8 @@
}
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();
@@ -2221,6 +2229,12 @@
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<policy.mLockTaskPackages.size(); i++) {
String component = policy.mLockTaskPackages.get(i);
out.startTag(null, TAG_LOCK_TASK_COMPONENTS);
@@ -2387,6 +2401,8 @@
parser.getAttributeValue(null, "symbols"));
policy.mActivePasswordNonLetter = Integer.parseInt(
parser.getAttributeValue(null, "nonletter"));
+ } else if (TAG_ACCEPTED_CA_CERTIFICATES.equals(tag)) {
+ policy.mAcceptedCaCertificates.add(parser.getAttributeValue(null, ATTR_NAME));
} else if (TAG_LOCK_TASK_COMPONENTS.equals(tag)) {
policy.mLockTaskPackages.add(parser.getAttributeValue(null, "name"));
} else if (TAG_STATUS_BAR.equals(tag)) {
@@ -2638,17 +2654,17 @@
}
}
- private class MonitoringCertNotificationTask extends AsyncTask<Intent, Void, Void> {
+ private class MonitoringCertNotificationTask extends AsyncTask<Integer, Void, Void> {
@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;
}
@@ -2658,25 +2674,27 @@
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<String> 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();
- }
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- } catch (RuntimeException | AssertionError e) {
- Log.e(LOG_TAG, "Could not connect to KeyChain service", e);
+ pendingCertificates = getInstalledCaCertificates(userHandle);
+ } catch (RemoteException | RuntimeException e) {
+ Log.e(LOG_TAG, "Could not retrieve certificates from KeyChain service", e);
+ return;
}
- if (!hasCert) {
+
+ synchronized (DevicePolicyManagerService.this) {
+ final DevicePolicyData policy = getUserData(userHandle.getIdentifier());
+
+ // Remove deleted certificates. Flush xml if necessary.
+ if (policy.mAcceptedCaCertificates.retainAll(pendingCertificates)) {
+ saveSettingsLocked(userHandle.getIdentifier());
+ }
+ // Trim to approved certificates.
+ pendingCertificates.removeAll(policy.mAcceptedCaCertificates);
+ }
+
+ if (pendingCertificates.isEmpty()) {
mInjector.getNotificationManager().cancelAsUser(
null, MONITORING_CERT_NOTIFICATION_ID, userHandle);
return;
@@ -2707,7 +2725,8 @@
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;
@@ -2726,6 +2745,29 @@
mInjector.getNotificationManager().notifyAsUser(
null, MONITORING_CERT_NOTIFICATION_ID, noti, userHandle);
}
+
+ private List<String> getInstalledCaCertificates(UserHandle userHandle)
+ throws RemoteException, RuntimeException {
+ KeyChainConnection conn = null;
+ try {
+ conn = KeyChain.bindAsUser(mContext, userHandle);
+ List<ParcelableString> aliases = conn.getService().getUserCaAliases().getList();
+ List<String> 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();
+ }
+ }
+ }
}
/**
@@ -4076,6 +4118,29 @@
}
@Override
+ public boolean approveCaCert(String alias, int userId, boolean approval) {
+ enforceManageUsers();
+ synchronized (this) {
+ Set<String> 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);