diff --git a/core/api/current.txt b/core/api/current.txt index 2472eac3d0557..9bce694432cbc 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -7121,6 +7121,7 @@ package android.app.admin { method @NonNull public android.os.Bundle getUserRestrictions(@NonNull android.content.ComponentName); method @Nullable public String getWifiMacAddress(@NonNull android.content.ComponentName); method public boolean grantKeyPairToApp(@Nullable android.content.ComponentName, @NonNull String, @NonNull String); + method public boolean grantKeyPairToWifiAuth(@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); @@ -7143,6 +7144,7 @@ package android.app.admin { method public boolean isDeviceIdAttestationSupported(); method public boolean isDeviceOwnerApp(String); method public boolean isEphemeralUser(@NonNull android.content.ComponentName); + method public boolean isKeyPairGrantedToWifiAuth(@NonNull String); method public boolean isLockTaskPermitted(String); method public boolean isLogoutEnabled(); method public boolean isManagedProfile(@NonNull android.content.ComponentName); @@ -7178,6 +7180,7 @@ package android.app.admin { method @Nullable public java.util.List retrievePreRebootSecurityLogs(@NonNull android.content.ComponentName); method @Nullable public java.util.List retrieveSecurityLogs(@NonNull android.content.ComponentName); method public boolean revokeKeyPairFromApp(@Nullable android.content.ComponentName, @NonNull String, @NonNull String); + method public boolean revokeKeyPairFromWifiAuth(@NonNull String); method public void setAccountManagementDisabled(@NonNull android.content.ComponentName, String, boolean); method public void setAffiliationIds(@NonNull android.content.ComponentName, @NonNull java.util.Set); method public void setAlwaysOnVpnPackage(@NonNull android.content.ComponentName, @Nullable String, boolean) throws android.content.pm.PackageManager.NameNotFoundException; diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 0635bd08e22bd..ccf41e5f30638 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -6437,6 +6437,74 @@ 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 allow using a KeyChain key + * pair for authentication to Wifi networks. The key can then be used in configurations passed + * to {@link android.net.wifi.WifiManager#addNetwork}. + * + * @param alias The alias of the key pair. + * @return {@code true} if the operation was set successfully, {@code false} otherwise. + * + * @throws SecurityException if the caller is not a device owner, a profile owner or + * delegated certificate chooser. + * @see #revokeKeyPairFromWifiAuth + */ + public boolean grantKeyPairToWifiAuth(@NonNull String alias) { + throwIfParentInstance("grantKeyPairToWifiAuth"); + try { + return mService.setKeyGrantToWifiAuth(mContext.getPackageName(), alias, true); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + 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 deny using a KeyChain key + * pair for authentication to Wifi networks. Configured networks using this key won't be able to + * authenticate. + * + * @param alias The alias of the key pair. + * @return {@code true} if the operation was set successfully, {@code false} otherwise. + * + * @throws SecurityException if the caller is not a device owner, a profile owner or + * delegated certificate chooser. + * @see #grantKeyPairToWifiAuth + */ + public boolean revokeKeyPairFromWifiAuth(@NonNull String alias) { + throwIfParentInstance("revokeKeyPairFromWifiAuth"); + try { + return mService.setKeyGrantToWifiAuth(mContext.getPackageName(), alias, false); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + 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 whether a KeyChain key + * pair can be used for authentication to Wifi networks. + * + * @param alias The alias of the key pair. + * @return {@code true} if the key pair can be used, {@code false} otherwise. + * + * @throws SecurityException if the caller is not a device owner, a profile owner or + * delegated certificate chooser. + * @see #grantKeyPairToWifiAuth + */ + public boolean isKeyPairGrantedToWifiAuth(@NonNull String alias) { + throwIfParentInstance("isKeyPairGrantedToWifiAuth"); + try { + return mService.isKeyPairGrantedToWifiAuth(mContext.getPackageName(), alias); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + return false; + } + /** * Returns {@code true} if the device supports attestation of device identifiers in addition * to key attestation. See diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index ac1592d2d2a10..25ca59963d4b9 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -479,6 +479,8 @@ interface IDevicePolicyManager { boolean setKeyGrantForApp(in ComponentName admin, String callerPackage, String alias, String packageName, boolean hasGrant); List getKeyPairGrants(in String callerPackage, in String alias); + boolean setKeyGrantToWifiAuth(String callerPackage, String alias, boolean hasGrant); + boolean isKeyPairGrantedToWifiAuth(String callerPackage, String alias); void setUserControlDisabledPackages(in ComponentName admin, in List packages); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index ef7afc8d1894d..cdd5a92bec7a7 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -143,4 +143,14 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { public boolean canAdminGrantSensorsPermissionsForUser(int userId) { return false; } + + @Override + public boolean setKeyGrantToWifiAuth(String callerPackage, String alias, boolean hasGrant) { + return false; + } + + @Override + public boolean isKeyPairGrantedToWifiAuth(String callerPackage, String alias) { + 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 7d2bafdb6483f..2949679620dec 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -5460,6 +5460,42 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { || isCallerDelegate(caller, DELEGATION_CERT_INSTALL); } + @Override + public boolean setKeyGrantToWifiAuth(String callerPackage, String alias, boolean hasGrant) { + Preconditions.checkStringNotEmpty(alias, "Alias to grant cannot be empty"); + + final CallerIdentity caller = getCallerIdentity(callerPackage); + Preconditions.checkCallAuthorization(canManageCertificates(caller)); + + return setKeyChainGrantInternal(alias, hasGrant, Process.WIFI_UID, caller.getUserHandle()); + } + + @Override + public boolean isKeyPairGrantedToWifiAuth(String callerPackage, String alias) { + Preconditions.checkStringNotEmpty(alias, "Alias to check cannot be empty"); + + 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); + + for (final int uid : granteeUids) { + if (uid == Process.WIFI_UID) { + return true; + } + } + return false; + } catch (RemoteException e) { + Log.e(LOG_TAG, "Querying grant to wifi auth. ", e); + return false; + } + }); + } + @Override public boolean setKeyGrantForApp(ComponentName who, String callerPackage, String alias, String packageName, boolean hasGrant) { @@ -5482,19 +5518,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { throw new IllegalStateException("Failure getting grantee uid", e); } + return setKeyChainGrantInternal(alias, hasGrant, granteeUid, caller.getUserHandle()); + } + + private boolean setKeyChainGrantInternal(String alias, boolean hasGrant, int granteeUid, + UserHandle userHandle) { final long id = mInjector.binderClearCallingIdentity(); try { - final KeyChainConnection keyChainConnection = - KeyChain.bindAsUser(mContext, caller.getUserHandle()); - try { + try (KeyChainConnection keyChainConnection = + KeyChain.bindAsUser(mContext, userHandle)) { IKeyChainService keyChain = keyChainConnection.getService(); keyChain.setGrant(granteeUid, alias, hasGrant); return true; } catch (RemoteException e) { Log.e(LOG_TAG, "Setting grant for package.", e); - return false; - } finally { - keyChainConnection.close(); + return false; } } catch (InterruptedException e) { Log.w(LOG_TAG, "Interrupted while setting key grant", e);