summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRebecca Silberstein <silberst@google.com>2017-07-19 13:26:33 -0700
committerRebecca Silberstein <silberst@google.com>2017-07-26 14:54:09 -0700
commit0cafbe0c8e1bb5b9900908a44df232251c2042ea (patch)
tree4d0ee052fdf216a2fb9fd2f9fa61f65d3368a07d
parent425676edcb8853bd5aff996a0be6974b06246e46 (diff)
WifiApConfigStore: add initial apconfig validation
Add initial validation for values in the supplied SoftAp config. The initial checks will not fully validate the supplied settings, but they will do a high level sanity check. Initial items verified: 1 - ssid string set 2 - ssid string not empty 3 - ssid string does not exceed max length 4 - open networks do not have a password 5 - password protected networks do have a password 6 - if applicable, passwords within min/max range Future extensions would include additional parameters and validating the bytes in the ssid and password fields. Bug: 37280779 Test: frameworks/opt/net/wifi/tests/wifitests/runtests.sh Change-Id: I1a9338ce92c60c88ff3992d024a00ab1c6c4ec5e
-rw-r--r--service/java/com/android/server/wifi/WifiApConfigStore.java119
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java160
2 files changed, 279 insertions, 0 deletions
diff --git a/service/java/com/android/server/wifi/WifiApConfigStore.java b/service/java/com/android/server/wifi/WifiApConfigStore.java
index 9c90bcf47..98a59329b 100644
--- a/service/java/com/android/server/wifi/WifiApConfigStore.java
+++ b/service/java/com/android/server/wifi/WifiApConfigStore.java
@@ -16,13 +16,16 @@
package com.android.server.wifi;
+import android.annotation.NonNull;
import android.content.Context;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.os.Environment;
+import android.text.TextUtils;
import android.util.Log;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -31,6 +34,7 @@ import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Random;
import java.util.UUID;
@@ -50,6 +54,15 @@ public class WifiApConfigStore {
private static final int RAND_SSID_INT_MIN = 1000;
private static final int RAND_SSID_INT_MAX = 9999;
+ @VisibleForTesting
+ static final int SSID_MIN_LEN = 1;
+ @VisibleForTesting
+ static final int SSID_MAX_LEN = 32;
+ @VisibleForTesting
+ static final int PSK_MIN_LEN = 8;
+ @VisibleForTesting
+ static final int PSK_MAX_LEN = 63;
+
private WifiConfiguration mWifiApConfig = null;
private ArrayList<Integer> mAllowed2GChannel = null;
@@ -224,4 +237,110 @@ public class WifiApConfigStore {
config.preSharedKey = randomUUID.substring(0, 8) + randomUUID.substring(9, 13);
return config;
}
+
+ /**
+ * Verify provided SSID for existence, length and conversion to bytes
+ *
+ * @param ssid String ssid name
+ * @return boolean indicating ssid met requirements
+ */
+ private static boolean validateApConfigSsid(String ssid) {
+ if (TextUtils.isEmpty(ssid)) {
+ Log.d(TAG, "SSID for softap configuration must be set.");
+ return false;
+ }
+
+ if (ssid.length() < SSID_MIN_LEN || ssid.length() > SSID_MAX_LEN) {
+ Log.d(TAG, "SSID for softap configuration string size must be at least "
+ + SSID_MIN_LEN + " and not more than " + SSID_MAX_LEN);
+ return false;
+ }
+
+ try {
+ ssid.getBytes(StandardCharsets.UTF_8);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "softap config SSID verification failed: malformed string " + ssid);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Verify provided preSharedKey in ap config for WPA2_PSK network meets requirements.
+ */
+ private static boolean validateApConfigPreSharedKey(String preSharedKey) {
+ if (preSharedKey.length() < PSK_MIN_LEN || preSharedKey.length() > PSK_MAX_LEN) {
+ Log.d(TAG, "softap network password string size must be at least " + PSK_MIN_LEN
+ + " and no more than " + PSK_MAX_LEN);
+ return false;
+ }
+
+ try {
+ preSharedKey.getBytes(StandardCharsets.UTF_8);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "softap network password verification failed: malformed string");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Validate a WifiConfiguration is properly configured for use by SoftApManager.
+ *
+ * This method checks the length of the SSID and for sanity between security settings (if it
+ * requires a password, was one provided?).
+ *
+ * @param apConfig {@link WifiConfiguration} to use for softap mode
+ * @return boolean true if the provided config meets the minimum set of details, false
+ * otherwise.
+ */
+ static boolean validateApWifiConfiguration(@NonNull WifiConfiguration apConfig) {
+ // first check the SSID
+ if (!validateApConfigSsid(apConfig.SSID)) {
+ // failed SSID verificiation checks
+ return false;
+ }
+
+ // now check security settings: settings app allows open and WPA2 PSK
+ if (apConfig.allowedKeyManagement == null) {
+ Log.d(TAG, "softap config key management bitset was null");
+ return false;
+ }
+
+ String preSharedKey = apConfig.preSharedKey;
+ boolean hasPreSharedKey = !TextUtils.isEmpty(preSharedKey);
+ int authType;
+
+ try {
+ authType = apConfig.getAuthType();
+ } catch (IllegalStateException e) {
+ Log.d(TAG, "Unable to get AuthType for softap config: " + e.getMessage());
+ return false;
+ }
+
+ if (authType == KeyMgmt.NONE) {
+ // open networks should not have a password
+ if (hasPreSharedKey) {
+ Log.d(TAG, "open softap network should not have a password");
+ return false;
+ }
+ } else if (authType == KeyMgmt.WPA2_PSK) {
+ // this is a config that should have a password - check that first
+ if (!hasPreSharedKey) {
+ Log.d(TAG, "softap network password must be set");
+ return false;
+ }
+
+ if (!validateApConfigPreSharedKey(preSharedKey)) {
+ // failed preSharedKey checks
+ return false;
+ }
+ } else {
+ // this is not a supported security type
+ Log.d(TAG, "softap configs must either be open or WPA2 PSK networks");
+ return false;
+ }
+
+ return true;
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java
index 2d3b06695..02064d87e 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java
@@ -17,6 +17,7 @@
package com.android.server.wifi;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -36,6 +37,7 @@ import org.mockito.MockitoAnnotations;
import java.io.File;
import java.lang.reflect.Method;
+import java.util.Random;
/**
* Unit tests for {@link com.android.server.wifi.WifiApConfigStore}.
@@ -50,12 +52,15 @@ public class WifiApConfigStoreTest {
private static final String TEST_DEFAULT_AP_SSID = "TestAP";
private static final String TEST_CONFIGURED_AP_SSID = "ConfiguredAP";
private static final String TEST_DEFAULT_HOTSPOT_SSID = "TestShare";
+ private static final String TEST_DEFAULT_HOTSPOT_PSK = "TestPassword";
private static final int RAND_SSID_INT_MIN = 1000;
private static final int RAND_SSID_INT_MAX = 9999;
+ private static final String TEST_CHAR_SET_AS_STRING = "abcdefghijklmnopqrstuvwxyz0123456789";
@Mock Context mContext;
@Mock BackupManagerProxy mBackupManagerProxy;
File mApConfigFile;
+ Random mRandom;
@Before
public void setUp() throws Exception {
@@ -73,6 +78,8 @@ public class WifiApConfigStoreTest {
resources.setString(R.string.wifi_localhotspot_configure_ssid_default,
TEST_DEFAULT_HOTSPOT_SSID);
when(mContext.getResources()).thenReturn(resources);
+
+ mRandom = new Random();
}
@After
@@ -195,6 +202,17 @@ public class WifiApConfigStoreTest {
}
/**
+ * Verify a proper WifiConfiguration is generate by getDefaultApConfiguration().
+ */
+ @Test
+ public void getDefaultApConfigurationIsValid() {
+ WifiApConfigStore store = new WifiApConfigStore(
+ mContext, mBackupManagerProxy, mApConfigFile.getPath());
+ WifiConfiguration config = store.getApConfiguration();
+ assertTrue(WifiApConfigStore.validateApWifiConfiguration(config));
+ }
+
+ /**
* Verify a proper local only hotspot config is generated when called properly with the valid
* context.
*/
@@ -204,5 +222,147 @@ public class WifiApConfigStoreTest {
verifyDefaultApConfig(config, TEST_DEFAULT_HOTSPOT_SSID);
// The LOHS config should also have a specific network id set - check that as well.
assertEquals(WifiConfiguration.LOCAL_ONLY_NETWORK_ID, config.networkId);
+
+ // verify that the config passes the validateApWifiConfiguration check
+ assertTrue(WifiApConfigStore.validateApWifiConfiguration(config));
+ }
+
+ /**
+ * Helper method to generate random SSIDs.
+ *
+ * Note: this method has limited use as a random SSID generator. The characters used in this
+ * method do no not cover all valid inputs.
+ * @param length number of characters to generate for the name
+ * @return String generated string of random characters
+ */
+ private String generateRandomString(int length) {
+
+ StringBuilder stringBuilder = new StringBuilder(length);
+ int index = -1;
+ while (stringBuilder.length() < length) {
+ index = mRandom.nextInt(TEST_CHAR_SET_AS_STRING.length());
+ stringBuilder.append(TEST_CHAR_SET_AS_STRING.charAt(index));
+ }
+ return stringBuilder.toString();
+ }
+
+ /**
+ * Verify the SSID checks in validateApWifiConfiguration.
+ *
+ * Cases to check and verify they trigger failed verification:
+ * null WifiConfiguration.SSID
+ * empty WifiConfiguration.SSID
+ * invalid WifiConfiguaration.SSID length
+ *
+ * Additionally check a valid SSID with a random (within valid ranges) length.
+ */
+ @Test
+ public void testSsidVerificationInValidateApWifiConfigurationCheck() {
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = null;
+ assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+ config.SSID = "";
+ assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+ // check a string that is too large
+ config.SSID = generateRandomString(WifiApConfigStore.SSID_MAX_LEN + 1);
+ assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+
+ // now check a valid SSID with a random length
+ config.SSID = generateRandomString(mRandom.nextInt(WifiApConfigStore.SSID_MAX_LEN + 1));
+ assertTrue(WifiApConfigStore.validateApWifiConfiguration(config));
+ }
+
+ /**
+ * Verify the Open network checks in validateApWifiConfiguration.
+ *
+ * If the configured network is open, it should not have a password set.
+ *
+ * Additionally verify a valid open network passes verification.
+ */
+ @Test
+ public void testOpenNetworkConfigInValidateApWifiConfigurationCheck() {
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = TEST_DEFAULT_HOTSPOT_SSID;
+
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ config.preSharedKey = TEST_DEFAULT_HOTSPOT_PSK;
+ assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+
+ // open networks should not have a password set
+ config.preSharedKey = null;
+ assertTrue(WifiApConfigStore.validateApWifiConfiguration(config));
+ config.preSharedKey = "";
+ assertTrue(WifiApConfigStore.validateApWifiConfiguration(config));
+ }
+
+ /**
+ * Verify the WPA2_PSK network checks in validateApWifiConfiguration.
+ *
+ * If the configured network is configured with a preSharedKey, verify that the passwork is set
+ * and it meets length requirements.
+ */
+ @Test
+ public void testWpa2PskNetworkConfigInValidateApWifiConfigurationCheck() {
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = TEST_DEFAULT_HOTSPOT_SSID;
+
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA2_PSK);
+ config.preSharedKey = null;
+ assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+ config.preSharedKey = "";
+ assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+
+ // test too short
+ config.preSharedKey =
+ generateRandomString(WifiApConfigStore.PSK_MIN_LEN - 1);
+ assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+
+ // test too long
+ config.preSharedKey =
+ generateRandomString(WifiApConfigStore.PSK_MAX_LEN + 1);
+ assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+
+ // explicitly test min length
+ config.preSharedKey =
+ generateRandomString(WifiApConfigStore.PSK_MIN_LEN);
+ assertTrue(WifiApConfigStore.validateApWifiConfiguration(config));
+
+ // explicitly test max length
+ config.preSharedKey =
+ generateRandomString(WifiApConfigStore.PSK_MAX_LEN);
+ assertTrue(WifiApConfigStore.validateApWifiConfiguration(config));
+
+ // test random (valid length)
+ int maxLen = WifiApConfigStore.PSK_MAX_LEN;
+ int minLen = WifiApConfigStore.PSK_MIN_LEN;
+ config.preSharedKey =
+ generateRandomString(mRandom.nextInt(maxLen - minLen) + minLen);
+ assertTrue(WifiApConfigStore.validateApWifiConfiguration(config));
+ }
+
+ /**
+ * Verify an invalid AuthType setting (that would trigger an IllegalStateException)
+ * returns false when triggered in the validateApWifiConfiguration.
+ */
+ @Test
+ public void testInvalidAuthTypeInValidateApWifiConfigurationCheck() {
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = TEST_DEFAULT_HOTSPOT_SSID;
+
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA2_PSK);
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+ }
+
+ /**
+ * Verify an unsupported authType returns false for validateApWifiConfigurationCheck.
+ */
+ @Test
+ public void testUnsupportedAuthTypeInValidateApWifiConfigurationCheck() {
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = TEST_DEFAULT_HOTSPOT_SSID;
+
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+ assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
}
}