Merge "Add DPM.getKeyPairGrants()"
diff --git a/core/api/current.txt b/core/api/current.txt
index 4d944c3..a2f4f7e 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -6920,6 +6920,7 @@
method public int getGlobalPrivateDnsMode(@NonNull android.content.ComponentName);
method @NonNull public java.util.List<byte[]> getInstalledCaCerts(@Nullable android.content.ComponentName);
method @Nullable public java.util.List<java.lang.String> getKeepUninstalledPackages(@Nullable android.content.ComponentName);
+ method @NonNull public java.util.Set<java.util.Set<java.lang.String>> getKeyPairGrants(@NonNull String);
method public int getKeyguardDisabledFeatures(@Nullable android.content.ComponentName);
method public int getLockTaskFeatures(@NonNull android.content.ComponentName);
method @NonNull public String[] getLockTaskPackages(@NonNull android.content.ComponentName);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4095acc..251252e 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -5758,7 +5758,6 @@
return null;
}
-
/**
* Called by a device or profile owner, or delegated certificate chooser (an app that has been
* delegated the {@link #DELEGATION_CERT_SELECTION} privilege), to grant an application access
@@ -5796,6 +5795,51 @@
/**
* Called by a device or profile owner, or delegated certificate chooser (an app that has been
+ * delegated the {@link #DELEGATION_CERT_SELECTION} privilege), to query which apps have access
+ * to a given KeyChain key.
+ *
+ * Key are granted on a per-UID basis, so if several apps share the same UID, granting access to
+ * one of them automatically grants it to others. This method returns a set of sets of package
+ * names, where each internal set contains all packages sharing the same UID. Grantee packages
+ * that don't share UID with other packages are represented by singleton sets.
+ *
+ * @param alias The alias of the key to grant access to.
+ * @return package names of apps that have access to a given key, grouped by UIDs
+ *
+ * @throws SecurityException if the caller is not a device owner, a profile owner or
+ * delegated certificate chooser.
+ * @throws IllegalArgumentException if {@code alias} doesn't correspond to an existing key.
+ *
+ * @see #grantKeyPairToApp(ComponentName, String, String)
+ */
+ public @NonNull Set<Set<String>> getKeyPairGrants(@NonNull String alias) {
+ throwIfParentInstance("getKeyPairGrants");
+ try {
+ // Set of sets is flattened into a null-separated list.
+ final List<String> flattened =
+ mService.getKeyPairGrants(mContext.getPackageName(), alias);
+ final Set<Set<String>> result = new HashSet<>();
+ Set<String> pkgsForOneUid = new HashSet<>();
+ for (final String pkg : flattened) {
+ if (pkg == null) {
+ result.add(pkgsForOneUid);
+ pkgsForOneUid = new HashSet<>();
+ } else {
+ pkgsForOneUid.add(pkg);
+ }
+ }
+ if (!pkgsForOneUid.isEmpty()) {
+ result.add(pkgsForOneUid);
+ }
+ return result;
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return null;
+ }
+
+ /**
+ * Called by a device or profile owner, or delegated certificate chooser (an app that has been
* delegated the {@link #DELEGATION_CERT_SELECTION} privilege), to revoke an application's
* grant to a KeyChain key pair.
* Calls by the application to {@link android.security.KeyChain#getPrivateKey}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index e21fee2..bcc90f7 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -471,6 +471,7 @@
boolean startViewCalendarEventInManagedProfile(String packageName, long eventId, long start, long end, boolean allDay, int flags);
boolean setKeyGrantForApp(in ComponentName admin, String callerPackage, String alias, String packageName, boolean hasGrant);
+ List<String> getKeyPairGrants(in String callerPackage, in String alias);
void setUserControlDisabledPackages(in ComponentName admin, in List<String> packages);
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index add52fa..a9d4094 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -49,6 +49,7 @@
in byte[] privateKey, in byte[] userCert, in byte[] certChain, String alias, int uid);
boolean removeKeyPair(String alias);
boolean containsKeyPair(String alias);
+ int[] getGrants(String alias);
// APIs used by Settings
boolean deleteCaCertificate(String alias);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index ce61d50..22976c30 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -22,6 +22,8 @@
import com.android.server.SystemService;
+import java.util.List;
+
/**
* Defines the required interface for IDevicePolicyManager implemenation.
*
@@ -101,4 +103,9 @@
public boolean canProfileOwnerResetPasswordWhenLocked(int userId) {
return false;
}
+
+ public List<String> getKeyPairGrants(String callerPackage, String alias) {
+ // STOPSHIP: implement delegation code in ArcDevicePolicyManagerWrapperService & nuke this.
+ return null;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c7dbf95..065d5ff 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -5192,6 +5192,44 @@
return false;
}
+ @Override
+ public List<String> getKeyPairGrants(String callerPackage, String alias) {
+ final CallerIdentity caller = getCallerIdentity(callerPackage);
+ Preconditions.checkCallAuthorization(canManageCertificates(caller));
+
+ return mInjector.binderWithCleanCallingIdentity(() -> {
+ try (KeyChainConnection keyChainConnection =
+ KeyChain.bindAsUser(mContext, caller.getUserHandle())) {
+ final List<String> result = new ArrayList<>();
+ final int[] granteeUids = keyChainConnection.getService().getGrants(alias);
+ final PackageManager pm = mInjector.getPackageManager(caller.getUserId());
+
+ // TODO: Return Set<Set<String>> when AIDL supports it: b/136048684
+ // Public API returns a set of sets, where each internal set contains all package
+ // names corresponding to the same UID. For now a set of sets is marshalled as a
+ // null-separated list.
+ for (final int uid : granteeUids) {
+ final String[] packages = pm.getPackagesForUid(uid);
+ if (packages == null) {
+ Slog.wtf(LOG_TAG, "No packages found for uid " + uid);
+ continue;
+ }
+ if (!result.isEmpty()) {
+ result.add(null);
+ }
+ result.addAll(Arrays.asList(packages));
+ }
+ return result;
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Querying keypair grants", e);
+ } catch (InterruptedException e) {
+ Log.w(LOG_TAG, "Interrupted while querying keypair grants", e);
+ Thread.currentThread().interrupt();
+ }
+ return Collections.emptyList();
+ });
+ }
+
/**
* Enforce one the following conditions are met:
* (1) The device has a Device Owner, and one of the following holds: