1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wifi; 18 19 import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID; 20 import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_EAP; 21 import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE; 22 import static android.net.wifi.WifiManager.ALL_ZEROS_MAC_ADDRESS; 23 24 import static com.android.server.wifi.util.NativeUtil.addEnclosingQuotes; 25 26 import android.annotation.SuppressLint; 27 import android.net.IpConfiguration; 28 import android.net.MacAddress; 29 import android.net.StaticIpConfiguration; 30 import android.net.wifi.SecurityParams; 31 import android.net.wifi.WifiConfiguration; 32 import android.net.wifi.WifiEnterpriseConfig; 33 import android.net.wifi.WifiInfo; 34 import android.net.wifi.WifiManager; 35 import android.net.wifi.WifiNetworkSpecifier; 36 import android.net.wifi.WifiScanner; 37 import android.os.PatternMatcher; 38 import android.text.TextUtils; 39 import android.util.Log; 40 import android.util.Pair; 41 42 import com.android.internal.annotations.VisibleForTesting; 43 import com.android.modules.utils.build.SdkLevel; 44 import com.android.server.wifi.util.NativeUtil; 45 46 import java.nio.charset.StandardCharsets; 47 import java.security.cert.X509Certificate; 48 import java.util.ArrayList; 49 import java.util.Arrays; 50 import java.util.BitSet; 51 import java.util.Comparator; 52 import java.util.List; 53 import java.util.Objects; 54 55 /** 56 * WifiConfiguration utility for any {@link android.net.wifi.WifiConfiguration} related operations. 57 * Currently contains: 58 * > Helper method to check if the WifiConfiguration object is visible to the provided users. 59 * > Helper methods to identify the encryption of a WifiConfiguration object. 60 */ 61 public class WifiConfigurationUtil { 62 private static final String TAG = "WifiConfigurationUtil"; 63 64 /** 65 * Constants used for validating external config objects. 66 */ 67 private static final int ENCLOSING_QUOTES_LEN = 2; 68 private static final int SSID_UTF_8_MIN_LEN = 1 + ENCLOSING_QUOTES_LEN; 69 private static final int SSID_UTF_8_MAX_LEN = 32 + ENCLOSING_QUOTES_LEN; 70 private static final int SSID_HEX_MIN_LEN = 2; 71 private static final int SSID_HEX_MAX_LEN = 64; 72 private static final int PSK_ASCII_MIN_LEN = 8 + ENCLOSING_QUOTES_LEN; 73 private static final int SAE_ASCII_MIN_LEN = 1 + ENCLOSING_QUOTES_LEN; 74 private static final int PSK_SAE_ASCII_MAX_LEN = 63 + ENCLOSING_QUOTES_LEN; 75 private static final int PSK_SAE_HEX_LEN = 64; 76 private static final int WEP104_KEY_BYTES_LEN = 13; 77 private static final int WEP40_KEY_BYTES_LEN = 5; 78 79 @VisibleForTesting 80 public static final String PASSWORD_MASK = "*"; 81 private static final String MATCH_EMPTY_SSID_PATTERN_PATH = ""; 82 private static final Pair<MacAddress, MacAddress> MATCH_NONE_BSSID_PATTERN = 83 new Pair<>(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS); 84 private static final Pair<MacAddress, MacAddress> MATCH_ALL_BSSID_PATTERN = 85 new Pair<>(ALL_ZEROS_MAC_ADDRESS, ALL_ZEROS_MAC_ADDRESS); 86 87 private static final int NETWORK_ID_SECURITY_MASK = 0xff; 88 private static final int NETWORK_ID_SECURITY_OFFSET = 23; 89 90 /** 91 * Checks if the provided |wepKeys| array contains any non-null value; 92 */ hasAnyValidWepKey(String[] wepKeys)93 public static boolean hasAnyValidWepKey(String[] wepKeys) { 94 for (int i = 0; i < wepKeys.length; i++) { 95 if (wepKeys[i] != null) { 96 return true; 97 } 98 } 99 return false; 100 } 101 102 /** 103 * Helper method to check if the provided |config| corresponds to a PSK network or not. 104 */ isConfigForPskNetwork(WifiConfiguration config)105 public static boolean isConfigForPskNetwork(WifiConfiguration config) { 106 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK); 107 } 108 109 /** 110 * Helper method to check if the provided |config| corresponds to a WAPI PSK network or not. 111 */ isConfigForWapiPskNetwork(WifiConfiguration config)112 public static boolean isConfigForWapiPskNetwork(WifiConfiguration config) { 113 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_WAPI_PSK); 114 } 115 116 /** 117 * Helper method to check if the provided |config| corresponds to a WAPI CERT network or not. 118 */ isConfigForWapiCertNetwork(WifiConfiguration config)119 public static boolean isConfigForWapiCertNetwork(WifiConfiguration config) { 120 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_WAPI_CERT); 121 } 122 123 /** 124 * Helper method to check if the provided |config| corresponds to an SAE network or not. 125 */ isConfigForSaeNetwork(WifiConfiguration config)126 public static boolean isConfigForSaeNetwork(WifiConfiguration config) { 127 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE); 128 } 129 130 /** 131 * Helper method to check if the provided |config| corresponds to an OWE network or not. 132 */ isConfigForOweNetwork(WifiConfiguration config)133 public static boolean isConfigForOweNetwork(WifiConfiguration config) { 134 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_OWE); 135 } 136 137 /** 138 * Helper method to check if the provided |config| corresponds to a EAP network or not. 139 */ isConfigForEapNetwork(WifiConfiguration config)140 public static boolean isConfigForEapNetwork(WifiConfiguration config) { 141 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP); 142 } 143 144 /** 145 * Helper method to check if the provided |config| corresponds to 146 * a WPA3 Enterprise network or not. 147 */ isConfigForWpa3EnterpriseNetwork(WifiConfiguration config)148 public static boolean isConfigForWpa3EnterpriseNetwork(WifiConfiguration config) { 149 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE); 150 } 151 152 /** 153 * Helper method to check if the provided |config| corresponds to a EAP Suite-B network or not. 154 */ isConfigForWpa3Enterprise192BitNetwork(WifiConfiguration config)155 public static boolean isConfigForWpa3Enterprise192BitNetwork(WifiConfiguration config) { 156 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT); 157 } 158 159 /** 160 * Helper method to check if the provided |config| corresponds to a WEP network or not. 161 */ isConfigForWepNetwork(WifiConfiguration config)162 public static boolean isConfigForWepNetwork(WifiConfiguration config) { 163 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_WEP); 164 } 165 166 /** 167 * Helper method to check if the provided |config| corresponds to an open or enhanced 168 * open network, or not. 169 */ isConfigForOpenNetwork(WifiConfiguration config)170 public static boolean isConfigForOpenNetwork(WifiConfiguration config) { 171 return (!(isConfigForWepNetwork(config) || isConfigForPskNetwork(config) 172 || isConfigForWapiPskNetwork(config) || isConfigForWapiCertNetwork(config) 173 || isConfigForEapNetwork(config) || isConfigForSaeNetwork(config) 174 || isConfigForWpa3Enterprise192BitNetwork(config))); 175 } 176 177 /** 178 * Compare existing and new WifiConfiguration objects after a network update and return if 179 * IP parameters have changed or not. 180 * 181 * @param existingConfig Existing WifiConfiguration object corresponding to the network. 182 * @param newConfig New WifiConfiguration object corresponding to the network. 183 * @return true if IP parameters have changed, false otherwise. 184 */ hasIpChanged(WifiConfiguration existingConfig, WifiConfiguration newConfig)185 public static boolean hasIpChanged(WifiConfiguration existingConfig, 186 WifiConfiguration newConfig) { 187 if (existingConfig.getIpAssignment() != newConfig.getIpAssignment()) { 188 return true; 189 } 190 if (newConfig.getIpAssignment() == IpConfiguration.IpAssignment.STATIC) { 191 return !Objects.equals(existingConfig.getStaticIpConfiguration(), 192 newConfig.getStaticIpConfiguration()); 193 } 194 return false; 195 } 196 197 /** 198 * Compare existing and new WifiConfiguration objects after a network update and return if 199 * proxy parameters have changed or not. 200 * 201 * @param existingConfig Existing WifiConfiguration object corresponding to the network. 202 * @param newConfig New WifiConfiguration object corresponding to the network. 203 * @return true if proxy parameters have changed, false if no existing config and proxy settings 204 * are NONE, false otherwise. 205 */ hasProxyChanged(WifiConfiguration existingConfig, WifiConfiguration newConfig)206 public static boolean hasProxyChanged(WifiConfiguration existingConfig, 207 WifiConfiguration newConfig) { 208 if (existingConfig == null) { 209 return newConfig.getProxySettings() != IpConfiguration.ProxySettings.NONE; 210 } 211 if (newConfig.getProxySettings() != existingConfig.getProxySettings()) { 212 return true; 213 } 214 return !Objects.equals(existingConfig.getHttpProxy(), newConfig.getHttpProxy()); 215 } 216 217 /** 218 * Compare existing and new WifiConfiguration objects after a network update and return if 219 * MAC randomization setting has changed or not. 220 * @param existingConfig Existing WifiConfiguration object corresponding to the network. 221 * @param newConfig New WifiConfiguration object corresponding to the network. 222 * @return true if MAC randomization setting setting changed or the existing confiuration is 223 * null and the newConfig is setting macRandomizationSetting to the default value. 224 */ hasMacRandomizationSettingsChanged(WifiConfiguration existingConfig, WifiConfiguration newConfig)225 public static boolean hasMacRandomizationSettingsChanged(WifiConfiguration existingConfig, 226 WifiConfiguration newConfig) { 227 if (existingConfig == null) { 228 return newConfig.macRandomizationSetting != WifiConfiguration.RANDOMIZATION_AUTO; 229 } 230 return newConfig.macRandomizationSetting != existingConfig.macRandomizationSetting; 231 } 232 233 /** 234 * Compare existing and new WifiEnterpriseConfig objects after a network update and return if 235 * credential parameters have changed or not. 236 * 237 * @param existingEnterpriseConfig Existing WifiConfiguration object corresponding to the 238 * network. 239 * @param newEnterpriseConfig New WifiConfiguration object corresponding to the network. 240 * @return true if credentials have changed, false otherwise. 241 */ 242 @VisibleForTesting hasEnterpriseConfigChanged(WifiEnterpriseConfig existingEnterpriseConfig, WifiEnterpriseConfig newEnterpriseConfig)243 public static boolean hasEnterpriseConfigChanged(WifiEnterpriseConfig existingEnterpriseConfig, 244 WifiEnterpriseConfig newEnterpriseConfig) { 245 if (existingEnterpriseConfig != null && newEnterpriseConfig != null) { 246 if (existingEnterpriseConfig.getEapMethod() != newEnterpriseConfig.getEapMethod()) { 247 return true; 248 } 249 if (existingEnterpriseConfig.isAuthenticationSimBased()) { 250 // No other credential changes for SIM based methods. 251 // The SIM card is the credential. 252 return false; 253 } 254 if (existingEnterpriseConfig.getPhase2Method() 255 != newEnterpriseConfig.getPhase2Method()) { 256 return true; 257 } 258 if (!TextUtils.equals(existingEnterpriseConfig.getIdentity(), 259 newEnterpriseConfig.getIdentity())) { 260 return true; 261 } 262 if (!TextUtils.equals(existingEnterpriseConfig.getAnonymousIdentity(), 263 newEnterpriseConfig.getAnonymousIdentity())) { 264 return true; 265 } 266 if (!TextUtils.equals(existingEnterpriseConfig.getPassword(), 267 newEnterpriseConfig.getPassword())) { 268 return true; 269 } 270 X509Certificate[] existingCaCerts = existingEnterpriseConfig.getCaCertificates(); 271 X509Certificate[] newCaCerts = newEnterpriseConfig.getCaCertificates(); 272 if (!Arrays.equals(existingCaCerts, newCaCerts)) { 273 return true; 274 } 275 if (!Arrays.equals(newEnterpriseConfig.getCaCertificateAliases(), 276 existingEnterpriseConfig.getCaCertificateAliases())) { 277 return true; 278 } 279 if (!TextUtils.equals(newEnterpriseConfig.getClientCertificateAlias(), 280 existingEnterpriseConfig.getClientCertificateAlias())) { 281 return true; 282 } 283 if (!TextUtils.equals(newEnterpriseConfig.getAltSubjectMatch(), 284 existingEnterpriseConfig.getAltSubjectMatch())) { 285 return true; 286 } 287 if (!TextUtils.equals(newEnterpriseConfig.getWapiCertSuite(), 288 existingEnterpriseConfig.getWapiCertSuite())) { 289 return true; 290 } 291 if (newEnterpriseConfig.getOcsp() != existingEnterpriseConfig.getOcsp()) { 292 return true; 293 } 294 } else { 295 // One of the configs may have an enterpriseConfig 296 if (existingEnterpriseConfig != null || newEnterpriseConfig != null) { 297 return true; 298 } 299 } 300 return false; 301 } 302 303 /** 304 * Compare existing and new WifiConfiguration objects after a network update and return if 305 * credential parameters have changed or not. 306 * 307 * @param existingConfig Existing WifiConfiguration object corresponding to the network. 308 * @param newConfig New WifiConfiguration object corresponding to the network. 309 * @return true if credentials have changed, false otherwise. 310 */ hasCredentialChanged(WifiConfiguration existingConfig, WifiConfiguration newConfig)311 public static boolean hasCredentialChanged(WifiConfiguration existingConfig, 312 WifiConfiguration newConfig) { 313 if (!Objects.equals(existingConfig.allowedKeyManagement, 314 newConfig.allowedKeyManagement)) { 315 return true; 316 } 317 if (!Objects.equals(existingConfig.allowedProtocols, newConfig.allowedProtocols)) { 318 return true; 319 } 320 if (!Objects.equals(existingConfig.allowedAuthAlgorithms, 321 newConfig.allowedAuthAlgorithms)) { 322 return true; 323 } 324 if (!Objects.equals(existingConfig.allowedPairwiseCiphers, 325 newConfig.allowedPairwiseCiphers)) { 326 return true; 327 } 328 if (!Objects.equals(existingConfig.allowedGroupCiphers, 329 newConfig.allowedGroupCiphers)) { 330 return true; 331 } 332 if (!Objects.equals(existingConfig.allowedGroupManagementCiphers, 333 newConfig.allowedGroupManagementCiphers)) { 334 return true; 335 } 336 if (!Objects.equals(existingConfig.allowedSuiteBCiphers, 337 newConfig.allowedSuiteBCiphers)) { 338 return true; 339 } 340 if (!existingConfig.getSecurityParamsList().equals(newConfig.getSecurityParamsList())) { 341 return true; 342 } 343 if (!Objects.equals(existingConfig.preSharedKey, newConfig.preSharedKey)) { 344 return true; 345 } 346 if (!Arrays.equals(existingConfig.wepKeys, newConfig.wepKeys)) { 347 return true; 348 } 349 if (existingConfig.wepTxKeyIndex != newConfig.wepTxKeyIndex) { 350 return true; 351 } 352 if (existingConfig.hiddenSSID != newConfig.hiddenSSID) { 353 return true; 354 } 355 if (existingConfig.requirePmf != newConfig.requirePmf) { 356 return true; 357 } 358 if (existingConfig.carrierId != newConfig.carrierId) { 359 return true; 360 } 361 if (hasEnterpriseConfigChanged(existingConfig.enterpriseConfig, 362 newConfig.enterpriseConfig)) { 363 return true; 364 } 365 return false; 366 } 367 validateSsid(String ssid, boolean isAdd)368 private static boolean validateSsid(String ssid, boolean isAdd) { 369 if (isAdd) { 370 if (ssid == null) { 371 Log.e(TAG, "validateSsid : null string"); 372 return false; 373 } 374 } else { 375 if (ssid == null) { 376 // This is an update, so the SSID can be null if that is not being changed. 377 return true; 378 } 379 } 380 if (ssid.isEmpty()) { 381 Log.e(TAG, "validateSsid failed: empty string"); 382 return false; 383 } 384 if (ssid.startsWith("\"")) { 385 // UTF-8 SSID string 386 byte[] ssidBytes = ssid.getBytes(StandardCharsets.UTF_8); 387 if (ssidBytes.length < SSID_UTF_8_MIN_LEN) { 388 Log.e(TAG, "validateSsid failed: utf-8 ssid string size too small: " 389 + ssidBytes.length); 390 return false; 391 } 392 if (ssidBytes.length > SSID_UTF_8_MAX_LEN) { 393 Log.e(TAG, "validateSsid failed: utf-8 ssid string size too large: " 394 + ssidBytes.length); 395 return false; 396 } 397 } else { 398 // HEX SSID string 399 if (ssid.length() < SSID_HEX_MIN_LEN) { 400 Log.e(TAG, "validateSsid failed: hex string size too small: " + ssid.length()); 401 return false; 402 } 403 if (ssid.length() > SSID_HEX_MAX_LEN) { 404 Log.e(TAG, "validateSsid failed: hex string size too large: " + ssid.length()); 405 return false; 406 } 407 } 408 try { 409 NativeUtil.decodeSsid(ssid); 410 } catch (IllegalArgumentException e) { 411 Log.e(TAG, "validateSsid failed: malformed string: " + ssid); 412 return false; 413 } 414 return true; 415 } 416 validateBssid(MacAddress bssid)417 private static boolean validateBssid(MacAddress bssid) { 418 if (bssid == null) return true; 419 if (bssid.getAddressType() != MacAddress.TYPE_UNICAST) { 420 Log.e(TAG, "validateBssid failed: invalid bssid"); 421 return false; 422 } 423 return true; 424 } 425 validateBssid(String bssid)426 private static boolean validateBssid(String bssid) { 427 if (bssid == null) return true; 428 if (bssid.isEmpty()) { 429 Log.e(TAG, "validateBssid failed: empty string"); 430 return false; 431 } 432 // Allow reset of bssid with "any". 433 if (bssid.equals(ClientModeImpl.SUPPLICANT_BSSID_ANY)) return true; 434 MacAddress bssidMacAddress; 435 try { 436 bssidMacAddress = MacAddress.fromString(bssid); 437 } catch (IllegalArgumentException e) { 438 Log.e(TAG, "validateBssid failed: malformed string: " + bssid); 439 return false; 440 } 441 if (!validateBssid(bssidMacAddress)) { 442 return false; 443 } 444 return true; 445 } 446 validatePassword(String password, boolean isAdd, boolean isSae)447 private static boolean validatePassword(String password, boolean isAdd, boolean isSae) { 448 if (isAdd) { 449 if (password == null) { 450 Log.e(TAG, "validatePassword: null string"); 451 return false; 452 } 453 } else { 454 if (password == null) { 455 // This is an update, so the psk can be null if that is not being changed. 456 return true; 457 } else if (password.equals(PASSWORD_MASK)) { 458 // This is an update, so the app might have returned back the masked password, let 459 // it thru. WifiConfigManager will handle it. 460 return true; 461 } 462 } 463 if (password.isEmpty()) { 464 Log.e(TAG, "validatePassword failed: empty string"); 465 return false; 466 } 467 if (password.startsWith("\"")) { 468 // ASCII PSK string 469 byte[] passwordBytes = password.getBytes(StandardCharsets.US_ASCII); 470 int targetMinLength; 471 472 if (isSae) { 473 targetMinLength = SAE_ASCII_MIN_LEN; 474 } else { 475 targetMinLength = PSK_ASCII_MIN_LEN; 476 } 477 if (passwordBytes.length < targetMinLength) { 478 Log.e(TAG, "validatePassword failed: ASCII string size too small: " 479 + passwordBytes.length); 480 return false; 481 } 482 if (passwordBytes.length > PSK_SAE_ASCII_MAX_LEN) { 483 Log.e(TAG, "validatePassword failed: ASCII string size too large: " 484 + passwordBytes.length); 485 return false; 486 } 487 } else { 488 // HEX PSK string 489 if (password.length() != PSK_SAE_HEX_LEN) { 490 Log.e(TAG, "validatePassword failed: hex string size mismatch: " 491 + password.length()); 492 return false; 493 } 494 } 495 try { 496 NativeUtil.hexOrQuotedStringToBytes(password); 497 } catch (IllegalArgumentException e) { 498 Log.e(TAG, "validatePassword failed: malformed string: " + password); 499 return false; 500 } 501 return true; 502 } 503 validateWepKeys(String[] wepKeys, int wepTxKeyIndex, boolean isAdd)504 private static boolean validateWepKeys(String[] wepKeys, int wepTxKeyIndex, boolean isAdd) { 505 if (isAdd) { 506 if (wepKeys == null) { 507 Log.e(TAG, "validateWepKeys: null string"); 508 return false; 509 } 510 } else { 511 if (wepKeys == null) { 512 // This is an update, so the psk can be null if that is not being changed. 513 return true; 514 } else { 515 boolean allMaskedKeys = true; 516 for (int i = 0; i < wepKeys.length; i++) { 517 if (wepKeys[i] != null && !TextUtils.equals(wepKeys[i], PASSWORD_MASK)) { 518 allMaskedKeys = false; 519 } 520 } 521 if (allMaskedKeys) { 522 // This is an update, so the app might have returned back the masked password, 523 // let it thru. WifiConfigManager will handle it. 524 return true; 525 } 526 } 527 } 528 for (int i = 0; i < wepKeys.length; i++) { 529 if (wepKeys[i] != null) { 530 try { 531 ArrayList<Byte> wepKeyBytes = 532 NativeUtil.hexOrQuotedStringToBytes(wepKeys[i]); 533 if (wepKeyBytes.size() != WEP40_KEY_BYTES_LEN 534 && wepKeyBytes.size() != WEP104_KEY_BYTES_LEN) { 535 Log.e(TAG, "validateWepKeys: invalid wep key length " 536 + wepKeys[i].length() + " at index " + i); 537 return false; 538 } 539 } catch (IllegalArgumentException e) { 540 Log.e(TAG, "validateWepKeys: invalid wep key at index " + i, e); 541 return false; 542 } 543 } 544 } 545 if (wepTxKeyIndex >= wepKeys.length) { 546 Log.e(TAG, "validateWepKeys: invalid wep tx key index " + wepTxKeyIndex 547 + " wepKeys len: " + wepKeys.length); 548 return false; 549 } 550 return true; 551 } 552 validateBitSet(BitSet bitSet, int validValuesLength)553 private static boolean validateBitSet(BitSet bitSet, int validValuesLength) { 554 if (bitSet == null) return false; 555 BitSet clonedBitset = (BitSet) bitSet.clone(); 556 clonedBitset.clear(0, validValuesLength); 557 return clonedBitset.isEmpty(); 558 } 559 validateBitSets(WifiConfiguration config)560 private static boolean validateBitSets(WifiConfiguration config) { 561 // 1. Check |allowedKeyManagement|. 562 if (!validateBitSet(config.allowedKeyManagement, 563 WifiConfiguration.KeyMgmt.strings.length)) { 564 Log.e(TAG, "validateBitsets failed: invalid allowedKeyManagement bitset " 565 + config.allowedKeyManagement); 566 return false; 567 } 568 // 2. Check |allowedProtocols|. 569 if (!validateBitSet(config.allowedProtocols, 570 WifiConfiguration.Protocol.strings.length)) { 571 Log.e(TAG, "validateBitsets failed: invalid allowedProtocols bitset " 572 + config.allowedProtocols); 573 return false; 574 } 575 // 3. Check |allowedAuthAlgorithms|. 576 if (!validateBitSet(config.allowedAuthAlgorithms, 577 WifiConfiguration.AuthAlgorithm.strings.length)) { 578 Log.e(TAG, "validateBitsets failed: invalid allowedAuthAlgorithms bitset " 579 + config.allowedAuthAlgorithms); 580 return false; 581 } 582 // 4. Check |allowedGroupCiphers|. 583 if (!validateBitSet(config.allowedGroupCiphers, 584 WifiConfiguration.GroupCipher.strings.length)) { 585 Log.e(TAG, "validateBitsets failed: invalid allowedGroupCiphers bitset " 586 + config.allowedGroupCiphers); 587 return false; 588 } 589 // 5. Check |allowedPairwiseCiphers|. 590 if (!validateBitSet(config.allowedPairwiseCiphers, 591 WifiConfiguration.PairwiseCipher.strings.length)) { 592 Log.e(TAG, "validateBitsets failed: invalid allowedPairwiseCiphers bitset " 593 + config.allowedPairwiseCiphers); 594 return false; 595 } 596 return true; 597 } 598 validateKeyMgmt(BitSet keyMgmnt)599 private static boolean validateKeyMgmt(BitSet keyMgmnt) { 600 if (keyMgmnt.cardinality() > 1) { 601 if (keyMgmnt.cardinality() > 3) { 602 Log.e(TAG, "validateKeyMgmt failed: cardinality > 3"); 603 return false; 604 } 605 if (!keyMgmnt.get(WifiConfiguration.KeyMgmt.WPA_EAP)) { 606 Log.e(TAG, "validateKeyMgmt failed: not WPA_EAP"); 607 return false; 608 } 609 if (!keyMgmnt.get(WifiConfiguration.KeyMgmt.IEEE8021X) 610 && !keyMgmnt.get(WifiConfiguration.KeyMgmt.WPA_PSK)) { 611 Log.e(TAG, "validateKeyMgmt failed: not PSK or 8021X"); 612 return false; 613 } 614 // SUITE-B keymgmt must be WPA_EAP + IEEE8021X + SUITE_B_192. 615 if (keyMgmnt.cardinality() == 3 616 && !(keyMgmnt.get(WifiConfiguration.KeyMgmt.WPA_EAP) 617 && keyMgmnt.get(WifiConfiguration.KeyMgmt.IEEE8021X) 618 && keyMgmnt.get(WifiConfiguration.KeyMgmt.SUITE_B_192))) { 619 Log.e(TAG, "validateKeyMgmt failed: not SUITE_B_192"); 620 return false; 621 } 622 } 623 return true; 624 } 625 validateIpConfiguration(IpConfiguration ipConfig)626 private static boolean validateIpConfiguration(IpConfiguration ipConfig) { 627 if (ipConfig == null) { 628 Log.e(TAG, "validateIpConfiguration failed: null IpConfiguration"); 629 return false; 630 } 631 if (ipConfig.getIpAssignment() == IpConfiguration.IpAssignment.STATIC) { 632 StaticIpConfiguration staticIpConfig = ipConfig.getStaticIpConfiguration(); 633 if (staticIpConfig == null) { 634 Log.e(TAG, "validateIpConfiguration failed: null StaticIpConfiguration"); 635 return false; 636 } 637 if (staticIpConfig.getIpAddress() == null) { 638 Log.e(TAG, "validateIpConfiguration failed: null static ip Address"); 639 return false; 640 } 641 } 642 return true; 643 } 644 645 /** 646 * Enums to specify if the provided config is being validated for add or update. 647 */ 648 public static final boolean VALIDATE_FOR_ADD = true; 649 public static final boolean VALIDATE_FOR_UPDATE = false; 650 651 /** 652 * Validate the configuration received from an external application. 653 * 654 * This method checks for the following parameters: 655 * 1. {@link WifiConfiguration#SSID} 656 * 2. {@link WifiConfiguration#BSSID} 657 * 3. {@link WifiConfiguration#preSharedKey} 658 * 4. {@link WifiConfiguration#allowedKeyManagement} 659 * 5. {@link WifiConfiguration#allowedProtocols} 660 * 6. {@link WifiConfiguration#allowedAuthAlgorithms} 661 * 7. {@link WifiConfiguration#allowedGroupCiphers} 662 * 8. {@link WifiConfiguration#allowedPairwiseCiphers} 663 * 9. {@link WifiConfiguration#getIpConfiguration()} 664 * 665 * @param config {@link WifiConfiguration} received from an external application. 666 * @param isAdd {@link #VALIDATE_FOR_ADD} to indicate a network config received for an add, 667 * {@link #VALIDATE_FOR_UPDATE} for a network config received for an update. 668 * These 2 cases need to be handled differently because the config received for an 669 * update could contain only the fields that are being changed. 670 * @return true if the parameters are valid, false otherwise. 671 */ validate(WifiConfiguration config, boolean isAdd)672 public static boolean validate(WifiConfiguration config, boolean isAdd) { 673 if (!validateSsid(config.SSID, isAdd)) { 674 return false; 675 } 676 if (!validateBssid(config.BSSID)) { 677 return false; 678 } 679 if (!validateBitSets(config)) { 680 return false; 681 } 682 if (!validateKeyMgmt(config.allowedKeyManagement)) { 683 return false; 684 } 685 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_WEP) 686 && config.wepKeys != null 687 && !validateWepKeys(config.wepKeys, config.wepTxKeyIndex, isAdd)) { 688 return false; 689 } 690 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK) 691 && !validatePassword(config.preSharedKey, isAdd, false)) { 692 return false; 693 } 694 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE) 695 && !validatePassword(config.preSharedKey, isAdd, true)) { 696 return false; 697 } 698 699 if (!validateEnterpriseConfig(config, isAdd)) { 700 return false; 701 } 702 703 // b/153435438: Added to deal with badly formed WifiConfiguration from apps. 704 if (config.preSharedKey != null && !config.needsPreSharedKey()) { 705 Log.e(TAG, "preSharedKey set with an invalid KeyMgmt, resetting KeyMgmt to WPA_PSK"); 706 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK); 707 } 708 if (!validateIpConfiguration(config.getIpConfiguration())) { 709 return false; 710 } 711 // TBD: Validate some enterprise params as well in the future here. 712 return true; 713 } 714 validateBssidPattern( Pair<MacAddress, MacAddress> bssidPatternMatcher)715 private static boolean validateBssidPattern( 716 Pair<MacAddress, MacAddress> bssidPatternMatcher) { 717 if (bssidPatternMatcher == null) return true; 718 MacAddress baseAddress = bssidPatternMatcher.first; 719 MacAddress mask = bssidPatternMatcher.second; 720 if (baseAddress.getAddressType() != MacAddress.TYPE_UNICAST) { 721 Log.e(TAG, "validateBssidPatternMatcher failed : invalid base address: " + baseAddress); 722 return false; 723 } 724 if (mask.equals(ALL_ZEROS_MAC_ADDRESS) 725 && !baseAddress.equals(ALL_ZEROS_MAC_ADDRESS)) { 726 Log.e(TAG, "validateBssidPatternMatcher failed : invalid mask/base: " + mask + "/" 727 + baseAddress); 728 return false; 729 } 730 return true; 731 } 732 733 // TODO(b/113878056): Some of this is duplicated in {@link WifiNetworkConfigBuilder}. 734 // Merge them somehow?. isValidNetworkSpecifier(WifiNetworkSpecifier specifier)735 private static boolean isValidNetworkSpecifier(WifiNetworkSpecifier specifier) { 736 PatternMatcher ssidPatternMatcher = specifier.ssidPatternMatcher; 737 Pair<MacAddress, MacAddress> bssidPatternMatcher = specifier.bssidPatternMatcher; 738 if (ssidPatternMatcher == null || bssidPatternMatcher == null) { 739 return false; 740 } 741 if (ssidPatternMatcher.getPath() == null || bssidPatternMatcher.first == null 742 || bssidPatternMatcher.second == null) { 743 return false; 744 } 745 return true; 746 } 747 isMatchNoneNetworkSpecifier(WifiNetworkSpecifier specifier)748 private static boolean isMatchNoneNetworkSpecifier(WifiNetworkSpecifier specifier) { 749 PatternMatcher ssidPatternMatcher = specifier.ssidPatternMatcher; 750 Pair<MacAddress, MacAddress> bssidPatternMatcher = specifier.bssidPatternMatcher; 751 if (ssidPatternMatcher.getType() != PatternMatcher.PATTERN_PREFIX 752 && ssidPatternMatcher.getPath().equals(MATCH_EMPTY_SSID_PATTERN_PATH)) { 753 return true; 754 } 755 if (bssidPatternMatcher.equals(MATCH_NONE_BSSID_PATTERN)) { 756 return true; 757 } 758 return false; 759 } 760 isMatchAllNetworkSpecifier(WifiNetworkSpecifier specifier)761 private static boolean isMatchAllNetworkSpecifier(WifiNetworkSpecifier specifier) { 762 PatternMatcher ssidPatternMatcher = specifier.ssidPatternMatcher; 763 Pair<MacAddress, MacAddress> bssidPatternMatcher = specifier.bssidPatternMatcher; 764 if (ssidPatternMatcher.match(MATCH_EMPTY_SSID_PATTERN_PATH) 765 && bssidPatternMatcher.equals(MATCH_ALL_BSSID_PATTERN)) { 766 return true; 767 } 768 return false; 769 } 770 771 // TODO: b/177434707 calls inside same module are safe 772 @SuppressLint("NewApi") getBand(WifiNetworkSpecifier s)773 private static int getBand(WifiNetworkSpecifier s) { 774 return s.getBand(); 775 } 776 777 /** 778 * Validate the configuration received from an external application inside 779 * {@link WifiNetworkSpecifier}. 780 * 781 * This method checks for the following parameters: 782 * 1. {@link WifiNetworkSpecifier#ssidPatternMatcher} 783 * 2. {@link WifiNetworkSpecifier#bssidPatternMatcher} 784 * 3. {@link WifiNetworkSpecifier#getBand()} 785 * 4. {@link WifiConfiguration#SSID} 786 * 5. {@link WifiConfiguration#BSSID} 787 * 6. {@link WifiConfiguration#preSharedKey} 788 * 7. {@link WifiConfiguration#allowedKeyManagement} 789 * 8. {@link WifiConfiguration#allowedProtocols} 790 * 9. {@link WifiConfiguration#allowedAuthAlgorithms} 791 * 10. {@link WifiConfiguration#allowedGroupCiphers} 792 * 11. {@link WifiConfiguration#allowedPairwiseCiphers} 793 * 12. {@link WifiConfiguration#getIpConfiguration()} 794 * 795 * @param specifier Instance of {@link WifiNetworkSpecifier}. 796 * @return true if the parameters are valid, false otherwise. 797 */ validateNetworkSpecifier(WifiNetworkSpecifier specifier)798 public static boolean validateNetworkSpecifier(WifiNetworkSpecifier specifier) { 799 if (!isValidNetworkSpecifier(specifier)) { 800 Log.e(TAG, "validateNetworkSpecifier failed : invalid network specifier"); 801 return false; 802 } 803 if (isMatchNoneNetworkSpecifier(specifier)) { 804 Log.e(TAG, "validateNetworkSpecifier failed : match-none specifier"); 805 return false; 806 } 807 if (isMatchAllNetworkSpecifier(specifier)) { 808 Log.e(TAG, "validateNetworkSpecifier failed : match-all specifier"); 809 return false; 810 } 811 if (!WifiNetworkSpecifier.validateBand(getBand(specifier))) { 812 return false; 813 } 814 WifiConfiguration config = specifier.wifiConfiguration; 815 if (specifier.ssidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL) { 816 // For literal SSID matches, the value should satisfy SSID requirements. 817 // WifiConfiguration.SSID needs quotes around ASCII SSID. 818 if (!validateSsid(addEnclosingQuotes(specifier.ssidPatternMatcher.getPath()), true)) { 819 return false; 820 } 821 } else { 822 if (config.hiddenSSID) { 823 Log.e(TAG, "validateNetworkSpecifier failed : ssid pattern not supported " 824 + "for hidden networks"); 825 return false; 826 } 827 } 828 if (Objects.equals(specifier.bssidPatternMatcher.second, MacAddress.BROADCAST_ADDRESS)) { 829 // For literal BSSID matches, the value should satisfy MAC address requirements. 830 if (!validateBssid(specifier.bssidPatternMatcher.first)) { 831 return false; 832 } 833 } else { 834 if (!validateBssidPattern(specifier.bssidPatternMatcher)) { 835 return false; 836 } 837 } 838 if (!validateBitSets(config)) { 839 return false; 840 } 841 if (!validateKeyMgmt(config.allowedKeyManagement)) { 842 return false; 843 } 844 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK) 845 && !validatePassword(config.preSharedKey, true, false)) { 846 return false; 847 } 848 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE) 849 && !validatePassword(config.preSharedKey, true, true)) { 850 return false; 851 } 852 // TBD: Validate some enterprise params as well in the future here. 853 return true; 854 } 855 856 /** 857 * Check if the provided two networks are the same. 858 * Note: This does not check if network selection BSSID's are the same. 859 * 860 * @param config Configuration corresponding to a network. 861 * @param config1 Configuration corresponding to another network. 862 * 863 * @return true if |config| and |config1| are the same network. 864 * false otherwise. 865 */ isSameNetwork(WifiConfiguration config, WifiConfiguration config1)866 public static boolean isSameNetwork(WifiConfiguration config, WifiConfiguration config1) { 867 if (config == null && config1 == null) { 868 return true; 869 } 870 if (config == null || config1 == null) { 871 return false; 872 } 873 if (config.networkId != config1.networkId) { 874 return false; 875 } 876 if (!Objects.equals(config.SSID, config1.SSID)) { 877 return false; 878 } 879 if (!Objects.equals(config.getNetworkSelectionStatus().getCandidateSecurityParams(), 880 config1.getNetworkSelectionStatus().getCandidateSecurityParams())) { 881 return false; 882 } 883 if (WifiConfigurationUtil.hasCredentialChanged(config, config1)) { 884 return false; 885 } 886 return true; 887 } 888 889 /** 890 * Create a PnoNetwork object from the provided WifiConfiguration. 891 * 892 * @param config Configuration corresponding to the network. 893 * @return PnoNetwork object corresponding to the network. 894 */ createPnoNetwork( WifiConfiguration config)895 public static WifiScanner.PnoSettings.PnoNetwork createPnoNetwork( 896 WifiConfiguration config) { 897 WifiScanner.PnoSettings.PnoNetwork pnoNetwork = 898 new WifiScanner.PnoSettings.PnoNetwork(config.SSID); 899 if (config.hiddenSSID) { 900 pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_DIRECTED_SCAN; 901 } 902 pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_A_BAND; 903 pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_G_BAND; 904 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK)) { 905 pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_PSK; 906 } else if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP)) { 907 pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_EAPOL; 908 } else { 909 pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_OPEN; 910 } 911 return pnoNetwork; 912 } 913 addOpenUpgradableSecurityTypeIfNecessary(WifiConfiguration config)914 private static void addOpenUpgradableSecurityTypeIfNecessary(WifiConfiguration config) { 915 if (!config.isSecurityType(WifiConfiguration.SECURITY_TYPE_OPEN)) return; 916 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_OWE)) return; 917 918 Log.d(TAG, "Add upgradable OWE configuration."); 919 SecurityParams oweParams = SecurityParams.createSecurityParamsBySecurityType( 920 WifiConfiguration.SECURITY_TYPE_OWE); 921 oweParams.setIsAddedByAutoUpgrade(true); 922 config.addSecurityParams(oweParams); 923 } 924 addPskUpgradableSecurityTypeIfNecessary(WifiConfiguration config)925 private static void addPskUpgradableSecurityTypeIfNecessary(WifiConfiguration config) { 926 if (!config.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK)) return; 927 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE)) return; 928 929 Log.d(TAG, "Add upgradable SAE configuration."); 930 SecurityParams saeParams = SecurityParams.createSecurityParamsBySecurityType( 931 WifiConfiguration.SECURITY_TYPE_SAE); 932 saeParams.setIsAddedByAutoUpgrade(true); 933 config.addSecurityParams(saeParams); 934 } 935 addEapUpgradableSecurityTypeIfNecessary(WifiConfiguration config)936 private static void addEapUpgradableSecurityTypeIfNecessary(WifiConfiguration config) { 937 if (!config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP)) return; 938 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE)) return; 939 940 Log.d(TAG, "Add upgradable Enterprise configuration."); 941 SecurityParams wpa3EnterpriseParams = SecurityParams.createSecurityParamsBySecurityType( 942 WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE); 943 wpa3EnterpriseParams.setIsAddedByAutoUpgrade(true); 944 config.addSecurityParams(wpa3EnterpriseParams); 945 } 946 947 /** 948 * Add upgradable securit type to the given wifi configuration. 949 * 950 * @param config the wifi configuration to be checked. 951 */ addUpgradableSecurityTypeIfNecessary(WifiConfiguration config)952 public static boolean addUpgradableSecurityTypeIfNecessary(WifiConfiguration config) { 953 try { 954 addOpenUpgradableSecurityTypeIfNecessary(config); 955 addPskUpgradableSecurityTypeIfNecessary(config); 956 addEapUpgradableSecurityTypeIfNecessary(config); 957 } catch (IllegalArgumentException e) { 958 Log.e(TAG, "Failed to add upgradable security type"); 959 return false; 960 } 961 return true; 962 } 963 964 /** 965 * For a upgradable type which is added by the auto-upgrade mechenism, it is only 966 * matched when corresponding auto-upgrade features are enabled. 967 */ shouldOmitAutoUpgradeParams(SecurityParams params)968 private static boolean shouldOmitAutoUpgradeParams(SecurityParams params) { 969 if (!params.isAddedByAutoUpgrade()) return false; 970 971 WifiGlobals wifiGlobals = WifiInjector.getInstance().getWifiGlobals(); 972 973 if (params.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE)) { 974 return !wifiGlobals.isWpa3SaeUpgradeEnabled(); 975 } 976 if (params.isSecurityType(WifiConfiguration.SECURITY_TYPE_OWE)) { 977 return !wifiGlobals.isOweUpgradeEnabled(); 978 } 979 return false; 980 } 981 isSecurityParamsSupported(SecurityParams params)982 private static boolean isSecurityParamsSupported(SecurityParams params) { 983 final long wifiFeatures = WifiInjector.getInstance() 984 .getActiveModeWarden().getPrimaryClientModeManager() 985 .getSupportedFeatures(); 986 switch (params.getSecurityType()) { 987 case WifiConfiguration.SECURITY_TYPE_SAE: 988 return 0 != (wifiFeatures & WifiManager.WIFI_FEATURE_WPA3_SAE); 989 case WifiConfiguration.SECURITY_TYPE_OWE: 990 return 0 != (wifiFeatures & WifiManager.WIFI_FEATURE_OWE); 991 } 992 return true; 993 } 994 995 /** 996 * Check the security params is valid or not. 997 * @param params the requesting security params. 998 * @return true if it's valid; otherwise false. 999 */ isSecurityParamsValid(SecurityParams params)1000 public static boolean isSecurityParamsValid(SecurityParams params) { 1001 if (!params.isEnabled()) return false; 1002 if (!isSecurityParamsSupported(params)) return false; 1003 return true; 1004 } 1005 1006 /** 1007 * General WifiConfiguration list sorting algorithm: 1008 * 1, Place the fully enabled networks first. 1009 * 2. Next place all the temporarily disabled networks. 1010 * 3. Place the permanently disabled networks last (Permanently disabled networks are removed 1011 * before WifiConfigManager uses this comparator today!). 1012 * 1013 * Among the networks with the same status, sort them in the order determined by the return of 1014 * {@link #compareNetworksWithSameStatus(WifiConfiguration, WifiConfiguration)} method 1015 * implementation. 1016 */ 1017 public abstract static class WifiConfigurationComparator implements 1018 Comparator<WifiConfiguration> { 1019 private static final int ENABLED_NETWORK_SCORE = 3; 1020 private static final int TEMPORARY_DISABLED_NETWORK_SCORE = 2; 1021 private static final int PERMANENTLY_DISABLED_NETWORK_SCORE = 1; 1022 1023 @Override compare(WifiConfiguration a, WifiConfiguration b)1024 public int compare(WifiConfiguration a, WifiConfiguration b) { 1025 int configAScore = getNetworkStatusScore(a); 1026 int configBScore = getNetworkStatusScore(b); 1027 if (configAScore == configBScore) { 1028 return compareNetworksWithSameStatus(a, b); 1029 } else { 1030 return Integer.compare(configBScore, configAScore); 1031 } 1032 } 1033 1034 // This needs to be implemented by the connected/disconnected PNO list comparator. compareNetworksWithSameStatus(WifiConfiguration a, WifiConfiguration b)1035 abstract int compareNetworksWithSameStatus(WifiConfiguration a, WifiConfiguration b); 1036 1037 /** 1038 * Returns an integer representing a score for each configuration. The scores are assigned 1039 * based on the status of the configuration. The scores are assigned according to the order: 1040 * Fully enabled network > Temporarily disabled network > Permanently disabled network. 1041 */ getNetworkStatusScore(WifiConfiguration config)1042 private int getNetworkStatusScore(WifiConfiguration config) { 1043 if (config.getNetworkSelectionStatus().isNetworkEnabled()) { 1044 return ENABLED_NETWORK_SCORE; 1045 } else if (config.getNetworkSelectionStatus().isNetworkTemporaryDisabled()) { 1046 return TEMPORARY_DISABLED_NETWORK_SCORE; 1047 } else { 1048 return PERMANENTLY_DISABLED_NETWORK_SCORE; 1049 } 1050 } 1051 } 1052 1053 /** 1054 * Convert multi-type configurations to a list of configurations with a single security type, 1055 * where a configuration with multiple security configurations will be converted to multiple 1056 * Wi-Fi configurations with a single security type.. 1057 * 1058 * @param configs the list of multi-type configurations. 1059 * @return a list of Wi-Fi configurations with a single security type, 1060 * that may contain multiple configurations with the same network ID. 1061 */ convertMultiTypeConfigsToLegacyConfigs( List<WifiConfiguration> configs)1062 public static List<WifiConfiguration> convertMultiTypeConfigsToLegacyConfigs( 1063 List<WifiConfiguration> configs) { 1064 List<WifiConfiguration> legacyConfigs = new ArrayList<>(); 1065 for (WifiConfiguration config : configs) { 1066 boolean wpa2EnterpriseAdded = false; 1067 WifiConfiguration wpa3EnterpriseConfig = null; 1068 for (SecurityParams params: config.getSecurityParamsList()) { 1069 if (!params.isEnabled()) continue; 1070 if (shouldOmitAutoUpgradeParams(params)) continue; 1071 WifiConfiguration legacyConfig = new WifiConfiguration(config); 1072 legacyConfig.setSecurityParams(params); 1073 legacyConfig.networkId = addSecurityTypeToNetworkId( 1074 legacyConfig.networkId, 1075 params.getSecurityType()); 1076 int securityType = params.getSecurityType(); 1077 if (securityType == SECURITY_TYPE_EAP) { 1078 wpa2EnterpriseAdded = true; 1079 } else if (securityType == SECURITY_TYPE_EAP_WPA3_ENTERPRISE) { 1080 wpa3EnterpriseConfig = legacyConfig; 1081 continue; 1082 } 1083 legacyConfigs.add(legacyConfig); 1084 } 1085 if (wpa3EnterpriseConfig != null && (SdkLevel.isAtLeastS() || !wpa2EnterpriseAdded)) { 1086 // R Wifi settings maps WPA3-Enterprise to the same security type as 1087 // WPA2-Enterprise, which causes a DuplicateKeyException. For R, we should only 1088 // return the WPA2-Enterprise config if we have both. 1089 legacyConfigs.add(wpa3EnterpriseConfig); 1090 } 1091 } 1092 return legacyConfigs; 1093 } 1094 1095 /** 1096 * Converts WifiInfo.SecurityType to WifiConfiguration.SecurityType 1097 */ 1098 public static convertWifiInfoSecurityTypeToWifiConfiguration( @ifiInfo.SecurityType int securityType)1099 @WifiConfiguration.SecurityType int convertWifiInfoSecurityTypeToWifiConfiguration( 1100 @WifiInfo.SecurityType int securityType) { 1101 switch (securityType) { 1102 case WifiInfo.SECURITY_TYPE_OPEN: 1103 return WifiConfiguration.SECURITY_TYPE_OPEN; 1104 case WifiInfo.SECURITY_TYPE_WEP: 1105 return WifiConfiguration.SECURITY_TYPE_WEP; 1106 case WifiInfo.SECURITY_TYPE_PSK: 1107 return WifiConfiguration.SECURITY_TYPE_PSK; 1108 case WifiInfo.SECURITY_TYPE_EAP: 1109 return WifiConfiguration.SECURITY_TYPE_EAP; 1110 case WifiInfo.SECURITY_TYPE_SAE: 1111 return WifiConfiguration.SECURITY_TYPE_SAE; 1112 case WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT: 1113 return WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT; 1114 case WifiInfo.SECURITY_TYPE_OWE: 1115 return WifiConfiguration.SECURITY_TYPE_OWE; 1116 case WifiInfo.SECURITY_TYPE_WAPI_PSK: 1117 return WifiConfiguration.SECURITY_TYPE_WAPI_PSK; 1118 case WifiInfo.SECURITY_TYPE_WAPI_CERT: 1119 return WifiConfiguration.SECURITY_TYPE_WAPI_CERT; 1120 case WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE: 1121 return WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE; 1122 case WifiInfo.SECURITY_TYPE_OSEN: 1123 return WifiConfiguration.SECURITY_TYPE_OSEN; 1124 case WifiInfo.SECURITY_TYPE_PASSPOINT_R1_R2: 1125 return WifiConfiguration.SECURITY_TYPE_PASSPOINT_R1_R2; 1126 case WifiInfo.SECURITY_TYPE_PASSPOINT_R3: 1127 return WifiConfiguration.SECURITY_TYPE_PASSPOINT_R3; 1128 default: 1129 return -1; 1130 } 1131 } 1132 1133 /** 1134 * Adds a WifiConfiguration.SecurityType value to a network ID to differentiate the network IDs 1135 * of legacy single-type configurations derived from the same multi-type configuration. 1136 * 1137 * This method only works for SDK levels less than S, since those callers may expect a unique 1138 * network ID for each single-type configuration. For SDK level S and above, this method returns 1139 * the network ID as-is. 1140 * 1141 * @param netId network id to add the security type to 1142 * @param securityType WifiConfiguration security type to encode 1143 * @return network id with security type encoded in it 1144 */ addSecurityTypeToNetworkId( int netId, @WifiConfiguration.SecurityType int securityType)1145 public static int addSecurityTypeToNetworkId( 1146 int netId, @WifiConfiguration.SecurityType int securityType) { 1147 if (netId == INVALID_NETWORK_ID || SdkLevel.isAtLeastS()) { 1148 return netId; 1149 } 1150 return removeSecurityTypeFromNetworkId(netId) 1151 | ((securityType & NETWORK_ID_SECURITY_MASK) << NETWORK_ID_SECURITY_OFFSET); 1152 } 1153 1154 /** 1155 * Removes the security type value of a network ID to have it match with internal network IDs. 1156 * 1157 * This method only works for SDK levels less than S. It should be used on network ID passed in 1158 * from external callers, since those callers are be exposed to network IDs with an embedded 1159 * security type value. For SDK levels S and above, this method returns the network ID as-is. 1160 * 1161 * @param netId network id to remove the security type from 1162 * @return network id with the security type removed 1163 */ removeSecurityTypeFromNetworkId(int netId)1164 public static int removeSecurityTypeFromNetworkId(int netId) { 1165 if (netId == INVALID_NETWORK_ID || SdkLevel.isAtLeastS()) { 1166 return netId; 1167 } 1168 return netId & ~(NETWORK_ID_SECURITY_MASK << NETWORK_ID_SECURITY_OFFSET); 1169 } 1170 validateEnterpriseConfig(WifiConfiguration config, boolean isAdd)1171 private static boolean validateEnterpriseConfig(WifiConfiguration config, boolean isAdd) { 1172 if ((config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP) 1173 || config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE)) 1174 && !config.isEnterprise()) { 1175 return false; 1176 } 1177 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT) 1178 && (!config.isEnterprise() 1179 || config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.TLS)) { 1180 return false; 1181 } 1182 if (config.isEnterprise()) { 1183 if (config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.PEAP 1184 || config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TTLS) { 1185 1186 int phase2Method = config.enterpriseConfig.getPhase2Method(); 1187 if (phase2Method == WifiEnterpriseConfig.Phase2.MSCHAP 1188 || phase2Method == WifiEnterpriseConfig.Phase2.MSCHAPV2 1189 || phase2Method == WifiEnterpriseConfig.Phase2.PAP 1190 || phase2Method == WifiEnterpriseConfig.Phase2.GTC) { 1191 // Check the password on add only. When updating, the password may not be 1192 // available and it appears as "(Unchanged)" in Settings 1193 if ((isAdd && TextUtils.isEmpty(config.enterpriseConfig.getPassword())) 1194 || TextUtils.isEmpty(config.enterpriseConfig.getIdentity())) { 1195 Log.e(TAG, "Enterprise network without an identity or a password set"); 1196 return false; 1197 } 1198 } 1199 } 1200 } 1201 return true; 1202 } 1203 } 1204