diff options
| author | 2020-11-30 12:51:27 +0000 | |
|---|---|---|
| committer | 2020-12-03 16:50:52 +0000 | |
| commit | 148e31d70c88bd372cf6fd3dc9f19ee89f195f5d (patch) | |
| tree | 4ac1fb9f9c7849fa38a93272b35d0669bca46c56 | |
| parent | e4b1ddde990a8a9a47f4483e31616873ed0eb8d5 (diff) | |
Add DPM.getKeyPairGrants()
The method returns names of the packages that have access
to the key grouped by UID since grants are stored on per-uid
basis. This is expressed as Set<Set<String>>
Since Binder currently doesn't support sets or lists of lists,
this data structure is flattened into a list of strings, with
null values used as a delimiter.
Bug: 160457441
Test: atest com.android.cts.devicepolicy.MixedManagedProfileOwnerTest#testKeyManagement
Test: atest com.android.cts.devicepolicy.MixedDeviceOwnerTest#testDelegatedCertInstallerDirectly
Test: atest android.admin.cts.DevicePolicyManagerTest
Change-Id: Ieee1048b145fb8400eccbf32c054afc64b5d90c0
6 files changed, 93 insertions, 1 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 5af08e33d9e1..54691a5bb60d 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<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 5eb1922a163c..41865b2dd481 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 @@ -5656,6 +5655,51 @@ public class DevicePolicyManager { /** * 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 8be3cdc1296a..9e1b75095862 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<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 add52fa5b436..a9d4094d5245 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 ce61d50df1d9..22976c30a749 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<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 98bb52963725..d4ca00c8abef 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<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: |