From 85da5fd5f463d8d4927a49cae122a93bf1fc5c71 Mon Sep 17 00:00:00 2001 From: Stephanie Bak Date: Wed, 22 Dec 2021 06:06:02 +0000 Subject: [PATCH] DPM API to set Wi-Fi minimum security level required Adding API for the admin to set the mininum security level required for a Wi-Fi network in order to be elligible for a connection. Bug: 197558754 Test: atest com.android.server.devicepolicy.DevicePolicyManagerTest Change-Id: I90abb4e3ce9597cb4bff57a6f9e1e8d4a097ed3e --- core/api/current.txt | 6 + .../app/admin/DevicePolicyManager.java | 111 ++++++++++++++++++ .../app/admin/IDevicePolicyManager.aidl | 3 + .../server/devicepolicy/ActiveAdmin.java | 11 ++ .../DevicePolicyManagerService.java | 35 ++++++ .../devicepolicy/DevicePolicyManagerTest.java | 38 ++++++ 6 files changed, 204 insertions(+) diff --git a/core/api/current.txt b/core/api/current.txt index 1b5e926645304..2f5bebb01d742 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -7314,6 +7314,7 @@ package android.app.admin { method public int getMaximumFailedPasswordsForWipe(@Nullable android.content.ComponentName); method public long getMaximumTimeToLock(@Nullable android.content.ComponentName); method @NonNull public java.util.List getMeteredDataDisabledPackages(@NonNull android.content.ComponentName); + method public int getMinimumRequiredWifiSecurityLevel(); method @RequiresPermission(value=android.Manifest.permission.READ_NEARBY_STREAMING_POLICY, conditional=true) public int getNearbyAppStreamingPolicy(); method @RequiresPermission(value=android.Manifest.permission.READ_NEARBY_STREAMING_POLICY, conditional=true) public int getNearbyNotificationStreamingPolicy(); method @Deprecated @ColorInt public int getOrganizationColor(@NonNull android.content.ComponentName); @@ -7458,6 +7459,7 @@ package android.app.admin { method public void setMaximumFailedPasswordsForWipe(@NonNull android.content.ComponentName, int); method public void setMaximumTimeToLock(@NonNull android.content.ComponentName, long); method @NonNull public java.util.List setMeteredDataDisabledPackages(@NonNull android.content.ComponentName, @NonNull java.util.List); + method public void setMinimumRequiredWifiSecurityLevel(int); method public void setNearbyAppStreamingPolicy(int); method public void setNearbyNotificationStreamingPolicy(int); method public void setNetworkLoggingEnabled(@Nullable android.content.ComponentName, boolean); @@ -7676,6 +7678,10 @@ package android.app.admin { field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2 field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1 field public static final int SKIP_SETUP_WIZARD = 1; // 0x1 + field public static final int WIFI_SECURITY_ENTERPRISE_192 = 3; // 0x3 + field public static final int WIFI_SECURITY_ENTERPRISE_EAP = 2; // 0x2 + field public static final int WIFI_SECURITY_OPEN = 0; // 0x0 + field public static final int WIFI_SECURITY_PERSONAL = 1; // 0x1 field public static final int WIPE_EUICC = 4; // 0x4 field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1 field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2 diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index cefd25ac7ac53..b46771a32c810 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1577,6 +1577,78 @@ public class DevicePolicyManager { @SystemApi public static final int FLAG_SUPPORTED_MODES_DEVICE_OWNER = 1 << 2; + /** + * Constant for {@link #getMinimumRequiredWifiSecurityLevel()} and + * {@link #setMinimumRequiredWifiSecurityLevel(int)}: no minimum security level. + * + *

When returned from {@link #getMinimumRequiredWifiSecurityLevel()}, the constant + * represents the current minimum security level required. + * When passed to {@link #setMinimumRequiredWifiSecurityLevel(int)}, it sets the + * minimum security level a Wi-Fi network must meet. + * + * @see #WIFI_SECURITY_PERSONAL + * @see #WIFI_SECURITY_ENTERPRISE_EAP + * @see #WIFI_SECURITY_ENTERPRISE_192 + */ + public static final int WIFI_SECURITY_OPEN = 0; + + /** + * Constant for {@link #getMinimumRequiredWifiSecurityLevel()} and + * {@link #setMinimumRequiredWifiSecurityLevel(int)}: personal network such as WEP, WPA2-PSK. + * + *

When returned from {@link #getMinimumRequiredWifiSecurityLevel()}, the constant + * represents the current minimum security level required. + * When passed to {@link #setMinimumRequiredWifiSecurityLevel(int)}, it sets the + * minimum security level a Wi-Fi network must meet. + * + * @see #WIFI_SECURITY_OPEN + * @see #WIFI_SECURITY_ENTERPRISE_EAP + * @see #WIFI_SECURITY_ENTERPRISE_192 + */ + public static final int WIFI_SECURITY_PERSONAL = 1; + + /** + * Constant for {@link #getMinimumRequiredWifiSecurityLevel()} and + * {@link #setMinimumRequiredWifiSecurityLevel(int)}: enterprise EAP network. + * + *

When returned from {@link #getMinimumRequiredWifiSecurityLevel()}, the constant + * represents the current minimum security level required. + * When passed to {@link #setMinimumRequiredWifiSecurityLevel(int)}, it sets the + * minimum security level a Wi-Fi network must meet. + * + * @see #WIFI_SECURITY_OPEN + * @see #WIFI_SECURITY_PERSONAL + * @see #WIFI_SECURITY_ENTERPRISE_192 + */ + public static final int WIFI_SECURITY_ENTERPRISE_EAP = 2; + + /** + * Constant for {@link #getMinimumRequiredWifiSecurityLevel()} and + * {@link #setMinimumRequiredWifiSecurityLevel(int)}: enterprise 192 bit network. + * + *

When returned from {@link #getMinimumRequiredWifiSecurityLevel()}, the constant + * represents the current minimum security level required. + * When passed to {@link #setMinimumRequiredWifiSecurityLevel(int)}, it sets the + * minimum security level a Wi-Fi network must meet. + * + * @see #WIFI_SECURITY_OPEN + * @see #WIFI_SECURITY_PERSONAL + * @see #WIFI_SECURITY_ENTERPRISE_EAP + */ + public static final int WIFI_SECURITY_ENTERPRISE_192 = 3; + + /** + * Possible Wi-Fi minimum security levels + * + * @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"WIFI_SECURITY_"}, value = { + WIFI_SECURITY_OPEN, + WIFI_SECURITY_PERSONAL, + WIFI_SECURITY_ENTERPRISE_EAP, + WIFI_SECURITY_ENTERPRISE_192}) + public @interface WifiSecurity {} + /** * This MIME type is used for starting the device owner provisioning. * @@ -14559,6 +14631,45 @@ public class DevicePolicyManager { return ProvisioningIntentHelper.createProvisioningIntentFromNfcIntent(nfcIntent); } + /** + * Called by device owner or profile owner of an organization-owned managed profile to + * specify the minimum security level required for Wi-Fi networks. + * The device may not connect to networks that do not meet the minimum security level. + * If the current network does not meet the minimum security level set, it will be disconnected. + * + * + * @param level minimum security level + * @throws SecurityException if the caller is not a device owner or a profile owner on + * an organization-owned managed profile. + */ + public void setMinimumRequiredWifiSecurityLevel(@WifiSecurity int level) { + throwIfParentInstance("setMinimumRequiredWifiSecurityLevel"); + if (mService != null) { + try { + mService.setMinimumRequiredWifiSecurityLevel(level); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Returns the current Wi-Fi minimum security level. + * + * @see #setMinimumRequiredWifiSecurityLevel(int) + */ + public @WifiSecurity int getMinimumRequiredWifiSecurityLevel() { + throwIfParentInstance("getMinimumRequiredWifiSecurityLevel"); + if (mService == null) { + return WIFI_SECURITY_OPEN; + } + try { + return mService.getMinimumRequiredWifiSecurityLevel(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * For each {@link DevicePolicyDrawableResource} item in {@code drawables}, if * {@link DevicePolicyDrawableResource#getDrawableSource()} is not set or is set to diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index fae64d735b17f..67665a095cdb2 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -533,6 +533,9 @@ interface IDevicePolicyManager { boolean isUsbDataSignalingEnabledForUser(int userId); boolean canUsbDataSignalingBeDisabled(); + void setMinimumRequiredWifiSecurityLevel(int level); + int getMinimumRequiredWifiSecurityLevel(); + List listForegroundAffiliatedUsers(); void setDrawables(in List drawables); void resetDrawables(in int[] drawableIds); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java index df9ab5003122e..6a866e225ae00 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java @@ -145,6 +145,7 @@ class ActiveAdmin { private static final String TAG_PREFERENTIAL_NETWORK_SERVICE_ENABLED = "preferential-network-service-enabled"; private static final String TAG_USB_DATA_SIGNALING = "usb-data-signaling"; + private static final String TAG_WIFI_MIN_SECURITY = "wifi-min-security"; private static final String ATTR_VALUE = "value"; private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification"; private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications"; @@ -298,6 +299,8 @@ class ActiveAdmin { private static final boolean USB_DATA_SIGNALING_ENABLED_DEFAULT = true; boolean mUsbDataSignalingEnabled = USB_DATA_SIGNALING_ENABLED_DEFAULT; + int mWifiMinimumSecurityLevel = DevicePolicyManager.WIFI_SECURITY_OPEN; + ActiveAdmin(DeviceAdminInfo info, boolean isParent) { this.info = info; this.isParent = isParent; @@ -574,6 +577,9 @@ class ActiveAdmin { if (mUsbDataSignalingEnabled != USB_DATA_SIGNALING_ENABLED_DEFAULT) { writeAttributeValueToXml(out, TAG_USB_DATA_SIGNALING, mUsbDataSignalingEnabled); } + if (mWifiMinimumSecurityLevel != DevicePolicyManager.WIFI_SECURITY_OPEN) { + writeAttributeValueToXml(out, TAG_WIFI_MIN_SECURITY, mWifiMinimumSecurityLevel); + } } void writeTextToXml(TypedXmlSerializer out, String tag, String text) throws IOException { @@ -826,6 +832,8 @@ class ActiveAdmin { } else if (TAG_USB_DATA_SIGNALING.equals(tag)) { mUsbDataSignalingEnabled = parser.getAttributeBoolean(null, ATTR_VALUE, USB_DATA_SIGNALING_ENABLED_DEFAULT); + } else if (TAG_WIFI_MIN_SECURITY.equals(tag)) { + mWifiMinimumSecurityLevel = parser.getAttributeInt(null, ATTR_VALUE); } else { Slogf.w(LOG_TAG, "Unknown admin tag: %s", tag); XmlUtils.skipCurrentTag(parser); @@ -1184,5 +1192,8 @@ class ActiveAdmin { pw.print("mUsbDataSignaling="); pw.println(mUsbDataSignalingEnabled); + + pw.print("mWifiMinimumSecurityLevel="); + pw.println(mWifiMinimumSecurityLevel); } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 7c0d549b2fe0b..9044a64a9f1a8 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -17979,6 +17979,41 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { ); } + private void validateCurrentWifiMeetsAdminRequirements() { + mInjector.binderWithCleanCallingIdentity( + () -> mInjector.getWifiManager().validateCurrentWifiMeetsAdminRequirements()); + } + + @Override + public void setMinimumRequiredWifiSecurityLevel(int level) { + final CallerIdentity caller = getCallerIdentity(); + Preconditions.checkCallAuthorization( + isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller), + "Wi-Fi minimum security level can only be controlled by a device owner or " + + "a profile owner on an organization-owned device."); + + boolean valueChanged = false; + synchronized (getLockObject()) { + final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller); + if (admin.mWifiMinimumSecurityLevel != level) { + admin.mWifiMinimumSecurityLevel = level; + saveSettingsLocked(caller.getUserId()); + valueChanged = true; + } + } + if (valueChanged) validateCurrentWifiMeetsAdminRequirements(); + } + + @Override + public int getMinimumRequiredWifiSecurityLevel() { + synchronized (getLockObject()) { + final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked( + UserHandle.USER_SYSTEM); + return (admin == null) ? DevicePolicyManager.WIFI_SECURITY_OPEN + : admin.mWifiMinimumSecurityLevel; + } + } + @Override public void setDrawables(@NonNull List drawables) { Preconditions.checkCallAuthorization(hasCallingOrSelfPermission( diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 1228d625325fb..09c0137df3601 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -34,6 +34,10 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW; import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM; import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE; import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_SET_NO_ERROR; +import static android.app.admin.DevicePolicyManager.WIFI_SECURITY_ENTERPRISE_192; +import static android.app.admin.DevicePolicyManager.WIFI_SECURITY_ENTERPRISE_EAP; +import static android.app.admin.DevicePolicyManager.WIFI_SECURITY_OPEN; +import static android.app.admin.DevicePolicyManager.WIFI_SECURITY_PERSONAL; import static android.app.admin.DevicePolicyManager.WIPE_EUICC; import static android.app.admin.PasswordMetrics.computeForPasswordOrPin; import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE; @@ -7828,6 +7832,40 @@ public class DevicePolicyManagerTest extends DpmTestBase { () -> dpm.getOrganizationNameForUser(UserHandle.USER_SYSTEM)); } + @Test + public void testSetWifiMinimumSecurity_noDeviceOwnerOrPoOfOrgOwnedDevice() { + assertThrows(SecurityException.class, () -> dpm.setMinimumRequiredWifiSecurityLevel( + DevicePolicyManager.WIFI_SECURITY_PERSONAL)); + } + + @Test + public void testSetWifiMinimumSecurity_asDeviceOwner() throws Exception { + setDeviceOwner(); + + final Set allowedLevels = Set.of(WIFI_SECURITY_OPEN, WIFI_SECURITY_PERSONAL, + WIFI_SECURITY_ENTERPRISE_EAP, WIFI_SECURITY_ENTERPRISE_192); + for (int level : allowedLevels) { + dpm.setMinimumRequiredWifiSecurityLevel(level); + assertThat(dpm.getMinimumRequiredWifiSecurityLevel()).isEqualTo(level); + } + } + + @Test + public void testSetWifiMinimumSecurity_asPoOfOrgOwnedDevice() throws Exception { + final int managedProfileUserId = 15; + final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436); + addManagedProfile(admin1, managedProfileAdminUid, admin1); + configureProfileOwnerOfOrgOwnedDevice(admin1, managedProfileUserId); + mContext.binder.callingUid = managedProfileAdminUid; + + final Set allowedLevels = Set.of(WIFI_SECURITY_OPEN, WIFI_SECURITY_PERSONAL, + WIFI_SECURITY_ENTERPRISE_EAP, WIFI_SECURITY_ENTERPRISE_192); + for (int level : allowedLevels) { + dpm.setMinimumRequiredWifiSecurityLevel(level); + assertThat(dpm.getMinimumRequiredWifiSecurityLevel()).isEqualTo(level); + } + } + private void setupVpnAuthorization(String userVpnPackage, int userVpnUid) { final AppOpsManager.PackageOps vpnOp = new AppOpsManager.PackageOps(userVpnPackage, userVpnUid, List.of(new AppOpsManager.OpEntry(