diff --git a/core/api/current.txt b/core/api/current.txt index 5af08e33d9e15..54691a5bb60d3 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -6919,6 +6919,7 @@ package android.app.admin { method public int getGlobalPrivateDnsMode(@NonNull android.content.ComponentName); method @NonNull public java.util.List getInstalledCaCerts(@Nullable android.content.ComponentName); method @Nullable public java.util.List getKeepUninstalledPackages(@Nullable android.content.ComponentName); + method @NonNull public java.util.Set> 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 5eb1922a163c6..41865b2dd4817 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -5618,7 +5618,6 @@ public class DevicePolicyManager { 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 @@ -5654,6 +5653,51 @@ public class DevicePolicyManager { return false; } + /** + * 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> getKeyPairGrants(@NonNull String alias) { + throwIfParentInstance("getKeyPairGrants"); + try { + // Set of sets is flattened into a null-separated list. + final List flattened = + mService.getKeyPairGrants(mContext.getPackageName(), alias); + final Set> result = new HashSet<>(); + Set 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 diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 8be3cdc1296aa..9e1b750958628 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -471,6 +471,7 @@ interface IDevicePolicyManager { 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 getKeyPairGrants(in String callerPackage, in String alias); void setUserControlDisabledPackages(in ComponentName admin, in List packages); diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl index add52fa5b4363..a9d4094d5245e 100644 --- a/keystore/java/android/security/IKeyChainService.aidl +++ b/keystore/java/android/security/IKeyChainService.aidl @@ -49,6 +49,7 @@ interface IKeyChainService { 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 ce61d50df1d91..22976c30a7499 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 android.util.Slog; import com.android.server.SystemService; +import java.util.List; + /** * Defines the required interface for IDevicePolicyManager implemenation. * @@ -101,4 +103,9 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { public boolean canProfileOwnerResetPasswordWhenLocked(int userId) { return false; } + + public List 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 98bb52963725a..d4ca00c8abef5 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -5169,6 +5169,44 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } + @Override + public List 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 result = new ArrayList<>(); + final int[] granteeUids = keyChainConnection.getService().getGrants(alias); + final PackageManager pm = mInjector.getPackageManager(caller.getUserId()); + + // TODO: Return Set> 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: