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