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