diff options
6 files changed, 54 insertions, 3 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index ef397b72e612..714f59ec13be 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -6968,6 +6968,7 @@ package android.app.admin { method public boolean grantKeyPairToApp(@Nullable android.content.ComponentName, @NonNull String, @NonNull String); method public boolean hasCaCertInstalled(@Nullable android.content.ComponentName, byte[]); method public boolean hasGrantedPolicy(@NonNull android.content.ComponentName, int); + method public boolean hasKeyPair(@NonNull String); method public boolean hasLockdownAdminConfiguredNetworks(@NonNull android.content.ComponentName); method public boolean installCaCert(@Nullable android.content.ComponentName, byte[]); method public boolean installExistingPackage(@NonNull android.content.ComponentName, String); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 247d6f0de831..26784f2c247e 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -5370,6 +5370,27 @@ public class DevicePolicyManager { } } + // STOPSHIP(b/174298501): clarify the expected return value following generateKeyPair call. + /** + * Called by a device or profile owner, or delegated certificate installer, to query whether a + * certificate and private key are installed under a given alias. + * + * @param alias The alias under which the key pair is installed. + * @return {@code true} if a key pair with this alias exists, {@code false} otherwise. + * @throws SecurityException if the caller is not a device or profile owner or a delegated + * certificate installer. + * @see #setDelegatedScopes + * @see #DELEGATION_CERT_INSTALL + */ + public boolean hasKeyPair(@NonNull String alias) { + throwIfParentInstance("hasKeyPair"); + try { + return mService.hasKeyPair(mContext.getPackageName(), alias); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Called by a device or profile owner, or delegated certificate installer, to generate a * new private/public key pair. If the device supports key generation via secure hardware, diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 37d34511902d..8be3cdc1296a 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -185,6 +185,7 @@ interface IDevicePolicyManager { in byte[] certBuffer, in byte[] certChainBuffer, String alias, boolean requestAccess, boolean isUserSelectable); boolean removeKeyPair(in ComponentName who, in String callerPackage, String alias); + boolean hasKeyPair(in String callerPackage, in String alias); boolean generateKeyPair(in ComponentName who, in String callerPackage, in String algorithm, in ParcelableKeyGenParameterSpec keySpec, in int idAttestationFlags, out KeymasterCertificateChain attestationChain); diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl index 97da3cc6f80f..1ae6a631dbcb 100644 --- a/keystore/java/android/security/IKeyChainService.aidl +++ b/keystore/java/android/security/IKeyChainService.aidl @@ -46,6 +46,7 @@ interface IKeyChainService { boolean installKeyPair( in byte[] privateKey, in byte[] userCert, in byte[] certChain, String alias, int uid); boolean removeKeyPair(String alias); + boolean containsKeyPair(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..05b1e4253bc0 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -101,4 +101,9 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { public boolean canProfileOwnerResetPasswordWhenLocked(int userId) { return false; } + + public boolean hasKeyPair(String callerPackage, String alias) { + // STOPSHIP: implement delegation code in ArcDevicePolicyManagerWrapperService & nuke this. + return false; + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 2f477afecf12..38381d2d74f8 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -5101,6 +5101,30 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override + public boolean hasKeyPair(String callerPackage, String alias) { + final CallerIdentity caller = getCallerIdentity(callerPackage); + Preconditions.checkCallAuthorization(canManageCertificates(caller)); + + return mInjector.binderWithCleanCallingIdentity(() -> { + try (KeyChainConnection keyChainConnection = + KeyChain.bindAsUser(mContext, caller.getUserHandle())) { + return keyChainConnection.getService().containsKeyPair(alias); + } catch (RemoteException e) { + Log.e(LOG_TAG, "Querying keypair", e); + } catch (InterruptedException e) { + Log.w(LOG_TAG, "Interrupted while querying keypair", e); + Thread.currentThread().interrupt(); + } + return false; + }); + } + + private boolean canManageCertificates(CallerIdentity caller) { + return isProfileOwner(caller) || isDeviceOwner(caller) + || isCallerDelegate(caller, DELEGATION_CERT_INSTALL); + } + + @Override public boolean setKeyGrantForApp(ComponentName who, String callerPackage, String alias, String packageName, boolean hasGrant) { Preconditions.checkStringNotEmpty(alias, "Alias to grant cannot be empty"); @@ -5178,9 +5202,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { */ if (hasProfileOwner(caller.getUserId())) { // Make sure that the caller is the profile owner or delegate. - Preconditions.checkCallAuthorization( - isDeviceOwner(caller) || isProfileOwner(caller) || isCallerDelegate( - caller, DELEGATION_CERT_INSTALL)); + Preconditions.checkCallAuthorization(canManageCertificates(caller)); // Verify that the managed profile is on an organization-owned device and as such // the profile owner can access Device IDs. if (isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId())) { |