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.net.IpConfiguration; 24 import android.net.MacAddress; 25 import android.net.StaticIpConfiguration; 26 import android.net.wifi.WifiConfiguration; 27 import android.net.wifi.WifiEnterpriseConfig; 28 import android.net.wifi.WifiNetworkSpecifier; 29 import android.net.wifi.WifiScanner; 30 import android.os.PatternMatcher; 31 import android.text.TextUtils; 32 import android.util.Log; 33 import android.util.Pair; 34 35 import com.android.internal.annotations.VisibleForTesting; 36 import com.android.server.wifi.util.NativeUtil; 37 38 import java.nio.charset.StandardCharsets; 39 import java.security.cert.X509Certificate; 40 import java.util.Arrays; 41 import java.util.BitSet; 42 import java.util.Comparator; 43 import java.util.Objects; 44 45 /** 46 * WifiConfiguration utility for any {@link android.net.wifi.WifiConfiguration} related operations. 47 * Currently contains: 48 * > Helper method to check if the WifiConfiguration object is visible to the provided users. 49 * > Helper methods to identify the encryption of a WifiConfiguration object. 50 */ 51 public class WifiConfigurationUtil { 52 private static final String TAG = "WifiConfigurationUtil"; 53 54 /** 55 * Constants used for validating external config objects. 56 */ 57 private static final int ENCLOSING_QUOTES_LEN = 2; 58 private static final int SSID_UTF_8_MIN_LEN = 1 + ENCLOSING_QUOTES_LEN; 59 private static final int SSID_UTF_8_MAX_LEN = 32 + ENCLOSING_QUOTES_LEN; 60 private static final int SSID_HEX_MIN_LEN = 2; 61 private static final int SSID_HEX_MAX_LEN = 64; 62 private static final int PSK_ASCII_MIN_LEN = 8 + ENCLOSING_QUOTES_LEN; 63 private static final int SAE_ASCII_MIN_LEN = 1 + ENCLOSING_QUOTES_LEN; 64 private static final int PSK_SAE_ASCII_MAX_LEN = 63 + ENCLOSING_QUOTES_LEN; 65 private static final int PSK_SAE_HEX_LEN = 64; 66 67 @VisibleForTesting 68 public static final String PASSWORD_MASK = "*"; 69 private static final String MATCH_EMPTY_SSID_PATTERN_PATH = ""; 70 private static final Pair<MacAddress, MacAddress> MATCH_NONE_BSSID_PATTERN = 71 new Pair<>(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS); 72 private static final Pair<MacAddress, MacAddress> MATCH_ALL_BSSID_PATTERN = 73 new Pair<>(ALL_ZEROS_MAC_ADDRESS, ALL_ZEROS_MAC_ADDRESS); 74 75 /** 76 * Checks if the provided |wepKeys| array contains any non-null value; 77 */ hasAnyValidWepKey(String[] wepKeys)78 public static boolean hasAnyValidWepKey(String[] wepKeys) { 79 for (int i = 0; i < wepKeys.length; i++) { 80 if (wepKeys[i] != null) { 81 return true; 82 } 83 } 84 return false; 85 } 86 87 /** 88 * Helper method to check if the provided |config| corresponds to a PSK network or not. 89 */ isConfigForPskNetwork(WifiConfiguration config)90 public static boolean isConfigForPskNetwork(WifiConfiguration config) { 91 return config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK); 92 } 93 94 /** 95 * Helper method to check if the provided |config| corresponds to a WAPI PSK network or not. 96 */ isConfigForWapiPskNetwork(WifiConfiguration config)97 public static boolean isConfigForWapiPskNetwork(WifiConfiguration config) { 98 return config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WAPI_PSK); 99 } 100 101 /** 102 * Helper method to check if the provided |config| corresponds to a WAPI CERT network or not. 103 */ isConfigForWapiCertNetwork(WifiConfiguration config)104 public static boolean isConfigForWapiCertNetwork(WifiConfiguration config) { 105 return config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WAPI_CERT); 106 } 107 108 /** 109 * Helper method to check if the provided |config| corresponds to an SAE network or not. 110 */ isConfigForSaeNetwork(WifiConfiguration config)111 public static boolean isConfigForSaeNetwork(WifiConfiguration config) { 112 return config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SAE); 113 } 114 115 /** 116 * Helper method to check if the provided |config| corresponds to an OWE network or not. 117 */ isConfigForOweNetwork(WifiConfiguration config)118 public static boolean isConfigForOweNetwork(WifiConfiguration config) { 119 return config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE); 120 } 121 122 /** 123 * Helper method to check if the provided |config| corresponds to a EAP network or not. 124 */ isConfigForEapNetwork(WifiConfiguration config)125 public static boolean isConfigForEapNetwork(WifiConfiguration config) { 126 return (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP) 127 || config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)); 128 } 129 130 /** 131 * Helper method to check if the provided |config| corresponds to a EAP Suite-B network or not. 132 */ isConfigForEapSuiteBNetwork(WifiConfiguration config)133 public static boolean isConfigForEapSuiteBNetwork(WifiConfiguration config) { 134 return config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SUITE_B_192); 135 } 136 137 /** 138 * Helper method to check if the provided |config| corresponds to a WEP network or not. 139 */ isConfigForWepNetwork(WifiConfiguration config)140 public static boolean isConfigForWepNetwork(WifiConfiguration config) { 141 return (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE) 142 && hasAnyValidWepKey(config.wepKeys)); 143 } 144 145 /** 146 * Helper method to check if the provided |config| corresponds to an open or enhanced 147 * open network, or not. 148 */ isConfigForOpenNetwork(WifiConfiguration config)149 public static boolean isConfigForOpenNetwork(WifiConfiguration config) { 150 return (!(isConfigForWepNetwork(config) || isConfigForPskNetwork(config) 151 || isConfigForEapNetwork(config) || isConfigForSaeNetwork(config) 152 || isConfigForEapSuiteBNetwork(config))); 153 } 154 155 /** 156 * Compare existing and new WifiConfiguration objects after a network update and return if 157 * IP parameters have changed or not. 158 * 159 * @param existingConfig Existing WifiConfiguration object corresponding to the network. 160 * @param newConfig New WifiConfiguration object corresponding to the network. 161 * @return true if IP parameters have changed, false otherwise. 162 */ hasIpChanged(WifiConfiguration existingConfig, WifiConfiguration newConfig)163 public static boolean hasIpChanged(WifiConfiguration existingConfig, 164 WifiConfiguration newConfig) { 165 if (existingConfig.getIpAssignment() != newConfig.getIpAssignment()) { 166 return true; 167 } 168 if (newConfig.getIpAssignment() == IpConfiguration.IpAssignment.STATIC) { 169 return !Objects.equals(existingConfig.getStaticIpConfiguration(), 170 newConfig.getStaticIpConfiguration()); 171 } 172 return false; 173 } 174 175 /** 176 * Compare existing and new WifiConfiguration objects after a network update and return if 177 * proxy parameters have changed or not. 178 * 179 * @param existingConfig Existing WifiConfiguration object corresponding to the network. 180 * @param newConfig New WifiConfiguration object corresponding to the network. 181 * @return true if proxy parameters have changed, false if no existing config and proxy settings 182 * are NONE, false otherwise. 183 */ hasProxyChanged(WifiConfiguration existingConfig, WifiConfiguration newConfig)184 public static boolean hasProxyChanged(WifiConfiguration existingConfig, 185 WifiConfiguration newConfig) { 186 if (existingConfig == null) { 187 return newConfig.getProxySettings() != IpConfiguration.ProxySettings.NONE; 188 } 189 if (newConfig.getProxySettings() != existingConfig.getProxySettings()) { 190 return true; 191 } 192 return !Objects.equals(existingConfig.getHttpProxy(), newConfig.getHttpProxy()); 193 } 194 195 /** 196 * Compare existing and new WifiConfiguration objects after a network update and return if 197 * MAC randomization setting has changed or not. 198 * @param existingConfig Existing WifiConfiguration object corresponding to the network. 199 * @param newConfig New WifiConfiguration object corresponding to the network. 200 * @return true if MAC randomization setting setting changed or the existing confiuration is 201 * null and the newConfig is setting macRandomizationSetting to the default value. 202 */ hasMacRandomizationSettingsChanged(WifiConfiguration existingConfig, WifiConfiguration newConfig)203 public static boolean hasMacRandomizationSettingsChanged(WifiConfiguration existingConfig, 204 WifiConfiguration newConfig) { 205 if (existingConfig == null) { 206 return newConfig.macRandomizationSetting != WifiConfiguration.RANDOMIZATION_PERSISTENT; 207 } 208 return newConfig.macRandomizationSetting != existingConfig.macRandomizationSetting; 209 } 210 211 /** 212 * Compare existing and new WifiEnterpriseConfig objects after a network update and return if 213 * credential parameters have changed or not. 214 * 215 * @param existingEnterpriseConfig Existing WifiConfiguration object corresponding to the 216 * network. 217 * @param newEnterpriseConfig New WifiConfiguration object corresponding to the network. 218 * @return true if credentials have changed, false otherwise. 219 */ 220 @VisibleForTesting hasEnterpriseConfigChanged(WifiEnterpriseConfig existingEnterpriseConfig, WifiEnterpriseConfig newEnterpriseConfig)221 public static boolean hasEnterpriseConfigChanged(WifiEnterpriseConfig existingEnterpriseConfig, 222 WifiEnterpriseConfig newEnterpriseConfig) { 223 if (existingEnterpriseConfig != null && newEnterpriseConfig != null) { 224 if (existingEnterpriseConfig.getEapMethod() != newEnterpriseConfig.getEapMethod()) { 225 return true; 226 } 227 if (existingEnterpriseConfig.isAuthenticationSimBased()) { 228 // No other credential changes for SIM based methods. 229 // The SIM card is the credential. 230 return false; 231 } 232 if (existingEnterpriseConfig.getPhase2Method() 233 != newEnterpriseConfig.getPhase2Method()) { 234 return true; 235 } 236 if (!TextUtils.equals(existingEnterpriseConfig.getIdentity(), 237 newEnterpriseConfig.getIdentity())) { 238 return true; 239 } 240 if (!TextUtils.equals(existingEnterpriseConfig.getAnonymousIdentity(), 241 newEnterpriseConfig.getAnonymousIdentity())) { 242 return true; 243 } 244 if (!TextUtils.equals(existingEnterpriseConfig.getPassword(), 245 newEnterpriseConfig.getPassword())) { 246 return true; 247 } 248 X509Certificate[] existingCaCerts = existingEnterpriseConfig.getCaCertificates(); 249 X509Certificate[] newCaCerts = newEnterpriseConfig.getCaCertificates(); 250 if (!Arrays.equals(existingCaCerts, newCaCerts)) { 251 return true; 252 } 253 if (!Arrays.equals(newEnterpriseConfig.getCaCertificateAliases(), 254 existingEnterpriseConfig.getCaCertificateAliases())) { 255 return true; 256 } 257 if (!TextUtils.equals(newEnterpriseConfig.getClientCertificateAlias(), 258 existingEnterpriseConfig.getClientCertificateAlias())) { 259 return true; 260 } 261 if (!TextUtils.equals(newEnterpriseConfig.getAltSubjectMatch(), 262 existingEnterpriseConfig.getAltSubjectMatch())) { 263 return true; 264 } 265 if (newEnterpriseConfig.getOcsp() != existingEnterpriseConfig.getOcsp()) { 266 return true; 267 } 268 } else { 269 // One of the configs may have an enterpriseConfig 270 if (existingEnterpriseConfig != null || newEnterpriseConfig != null) { 271 return true; 272 } 273 } 274 return false; 275 } 276 277 /** 278 * Compare existing and new WifiConfiguration objects after a network update and return if 279 * credential parameters have changed or not. 280 * 281 * @param existingConfig Existing WifiConfiguration object corresponding to the network. 282 * @param newConfig New WifiConfiguration object corresponding to the network. 283 * @return true if credentials have changed, false otherwise. 284 */ hasCredentialChanged(WifiConfiguration existingConfig, WifiConfiguration newConfig)285 public static boolean hasCredentialChanged(WifiConfiguration existingConfig, 286 WifiConfiguration newConfig) { 287 if (!Objects.equals(existingConfig.allowedKeyManagement, 288 newConfig.allowedKeyManagement)) { 289 return true; 290 } 291 if (!Objects.equals(existingConfig.allowedProtocols, newConfig.allowedProtocols)) { 292 return true; 293 } 294 if (!Objects.equals(existingConfig.allowedAuthAlgorithms, 295 newConfig.allowedAuthAlgorithms)) { 296 return true; 297 } 298 if (!Objects.equals(existingConfig.allowedPairwiseCiphers, 299 newConfig.allowedPairwiseCiphers)) { 300 return true; 301 } 302 if (!Objects.equals(existingConfig.allowedGroupCiphers, 303 newConfig.allowedGroupCiphers)) { 304 return true; 305 } 306 if (!Objects.equals(existingConfig.allowedGroupManagementCiphers, 307 newConfig.allowedGroupManagementCiphers)) { 308 return true; 309 } 310 if (!Objects.equals(existingConfig.allowedSuiteBCiphers, 311 newConfig.allowedSuiteBCiphers)) { 312 return true; 313 } 314 if (!Objects.equals(existingConfig.preSharedKey, newConfig.preSharedKey)) { 315 return true; 316 } 317 if (!Arrays.equals(existingConfig.wepKeys, newConfig.wepKeys)) { 318 return true; 319 } 320 if (existingConfig.wepTxKeyIndex != newConfig.wepTxKeyIndex) { 321 return true; 322 } 323 if (existingConfig.hiddenSSID != newConfig.hiddenSSID) { 324 return true; 325 } 326 if (existingConfig.requirePmf != newConfig.requirePmf) { 327 return true; 328 } 329 if (existingConfig.carrierId != newConfig.carrierId) { 330 return true; 331 } 332 if (hasEnterpriseConfigChanged(existingConfig.enterpriseConfig, 333 newConfig.enterpriseConfig)) { 334 return true; 335 } 336 return false; 337 } 338 validateSsid(String ssid, boolean isAdd)339 private static boolean validateSsid(String ssid, boolean isAdd) { 340 if (isAdd) { 341 if (ssid == null) { 342 Log.e(TAG, "validateSsid : null string"); 343 return false; 344 } 345 } else { 346 if (ssid == null) { 347 // This is an update, so the SSID can be null if that is not being changed. 348 return true; 349 } 350 } 351 if (ssid.isEmpty()) { 352 Log.e(TAG, "validateSsid failed: empty string"); 353 return false; 354 } 355 if (ssid.startsWith("\"")) { 356 // UTF-8 SSID string 357 byte[] ssidBytes = ssid.getBytes(StandardCharsets.UTF_8); 358 if (ssidBytes.length < SSID_UTF_8_MIN_LEN) { 359 Log.e(TAG, "validateSsid failed: utf-8 ssid string size too small: " 360 + ssidBytes.length); 361 return false; 362 } 363 if (ssidBytes.length > SSID_UTF_8_MAX_LEN) { 364 Log.e(TAG, "validateSsid failed: utf-8 ssid string size too large: " 365 + ssidBytes.length); 366 return false; 367 } 368 } else { 369 // HEX SSID string 370 if (ssid.length() < SSID_HEX_MIN_LEN) { 371 Log.e(TAG, "validateSsid failed: hex string size too small: " + ssid.length()); 372 return false; 373 } 374 if (ssid.length() > SSID_HEX_MAX_LEN) { 375 Log.e(TAG, "validateSsid failed: hex string size too large: " + ssid.length()); 376 return false; 377 } 378 } 379 try { 380 NativeUtil.decodeSsid(ssid); 381 } catch (IllegalArgumentException e) { 382 Log.e(TAG, "validateSsid failed: malformed string: " + ssid); 383 return false; 384 } 385 return true; 386 } 387 validateBssid(MacAddress bssid)388 private static boolean validateBssid(MacAddress bssid) { 389 if (bssid == null) return true; 390 if (bssid.getAddressType() != MacAddress.TYPE_UNICAST) { 391 Log.e(TAG, "validateBssid failed: invalid bssid"); 392 return false; 393 } 394 return true; 395 } 396 validateBssid(String bssid)397 private static boolean validateBssid(String bssid) { 398 if (bssid == null) return true; 399 if (bssid.isEmpty()) { 400 Log.e(TAG, "validateBssid failed: empty string"); 401 return false; 402 } 403 MacAddress bssidMacAddress; 404 try { 405 bssidMacAddress = MacAddress.fromString(bssid); 406 } catch (IllegalArgumentException e) { 407 Log.e(TAG, "validateBssid failed: malformed string: " + bssid); 408 return false; 409 } 410 if (!validateBssid(bssidMacAddress)) { 411 return false; 412 } 413 return true; 414 } 415 validatePassword(String password, boolean isAdd, boolean isSae)416 private static boolean validatePassword(String password, boolean isAdd, boolean isSae) { 417 if (isAdd) { 418 if (password == null) { 419 Log.e(TAG, "validatePassword: null string"); 420 return false; 421 } 422 } else { 423 if (password == null) { 424 // This is an update, so the psk can be null if that is not being changed. 425 return true; 426 } else if (password.equals(PASSWORD_MASK)) { 427 // This is an update, so the app might have returned back the masked password, let 428 // it thru. WifiConfigManager will handle it. 429 return true; 430 } 431 } 432 if (password.isEmpty()) { 433 Log.e(TAG, "validatePassword failed: empty string"); 434 return false; 435 } 436 if (password.startsWith("\"")) { 437 // ASCII PSK string 438 byte[] passwordBytes = password.getBytes(StandardCharsets.US_ASCII); 439 int targetMinLength; 440 441 if (isSae) { 442 targetMinLength = SAE_ASCII_MIN_LEN; 443 } else { 444 targetMinLength = PSK_ASCII_MIN_LEN; 445 } 446 if (passwordBytes.length < targetMinLength) { 447 Log.e(TAG, "validatePassword failed: ASCII string size too small: " 448 + passwordBytes.length); 449 return false; 450 } 451 if (passwordBytes.length > PSK_SAE_ASCII_MAX_LEN) { 452 Log.e(TAG, "validatePassword failed: ASCII string size too large: " 453 + passwordBytes.length); 454 return false; 455 } 456 } else { 457 // HEX PSK string 458 if (password.length() != PSK_SAE_HEX_LEN) { 459 Log.e(TAG, "validatePassword failed: hex string size mismatch: " 460 + password.length()); 461 return false; 462 } 463 } 464 try { 465 NativeUtil.hexOrQuotedStringToBytes(password); 466 } catch (IllegalArgumentException e) { 467 Log.e(TAG, "validatePassword failed: malformed string: " + password); 468 return false; 469 } 470 return true; 471 } 472 validateBitSet(BitSet bitSet, int validValuesLength)473 private static boolean validateBitSet(BitSet bitSet, int validValuesLength) { 474 if (bitSet == null) return false; 475 BitSet clonedBitset = (BitSet) bitSet.clone(); 476 clonedBitset.clear(0, validValuesLength); 477 return clonedBitset.isEmpty(); 478 } 479 validateBitSets(WifiConfiguration config)480 private static boolean validateBitSets(WifiConfiguration config) { 481 // 1. Check |allowedKeyManagement|. 482 if (!validateBitSet(config.allowedKeyManagement, 483 WifiConfiguration.KeyMgmt.strings.length)) { 484 Log.e(TAG, "validateBitsets failed: invalid allowedKeyManagement bitset " 485 + config.allowedKeyManagement); 486 return false; 487 } 488 // 2. Check |allowedProtocols|. 489 if (!validateBitSet(config.allowedProtocols, 490 WifiConfiguration.Protocol.strings.length)) { 491 Log.e(TAG, "validateBitsets failed: invalid allowedProtocols bitset " 492 + config.allowedProtocols); 493 return false; 494 } 495 // 3. Check |allowedAuthAlgorithms|. 496 if (!validateBitSet(config.allowedAuthAlgorithms, 497 WifiConfiguration.AuthAlgorithm.strings.length)) { 498 Log.e(TAG, "validateBitsets failed: invalid allowedAuthAlgorithms bitset " 499 + config.allowedAuthAlgorithms); 500 return false; 501 } 502 // 4. Check |allowedGroupCiphers|. 503 if (!validateBitSet(config.allowedGroupCiphers, 504 WifiConfiguration.GroupCipher.strings.length)) { 505 Log.e(TAG, "validateBitsets failed: invalid allowedGroupCiphers bitset " 506 + config.allowedGroupCiphers); 507 return false; 508 } 509 // 5. Check |allowedPairwiseCiphers|. 510 if (!validateBitSet(config.allowedPairwiseCiphers, 511 WifiConfiguration.PairwiseCipher.strings.length)) { 512 Log.e(TAG, "validateBitsets failed: invalid allowedPairwiseCiphers bitset " 513 + config.allowedPairwiseCiphers); 514 return false; 515 } 516 return true; 517 } 518 validateKeyMgmt(BitSet keyMgmnt)519 private static boolean validateKeyMgmt(BitSet keyMgmnt) { 520 if (keyMgmnt.cardinality() > 1) { 521 if (keyMgmnt.cardinality() > 3) { 522 Log.e(TAG, "validateKeyMgmt failed: cardinality > 3"); 523 return false; 524 } 525 if (!keyMgmnt.get(WifiConfiguration.KeyMgmt.WPA_EAP)) { 526 Log.e(TAG, "validateKeyMgmt failed: not WPA_EAP"); 527 return false; 528 } 529 if (!keyMgmnt.get(WifiConfiguration.KeyMgmt.IEEE8021X) 530 && !keyMgmnt.get(WifiConfiguration.KeyMgmt.WPA_PSK)) { 531 Log.e(TAG, "validateKeyMgmt failed: not PSK or 8021X"); 532 return false; 533 } 534 if (keyMgmnt.cardinality() == 3 535 && !keyMgmnt.get(WifiConfiguration.KeyMgmt.SUITE_B_192)) { 536 Log.e(TAG, "validateKeyMgmt failed: not SUITE_B_192"); 537 return false; 538 } 539 } 540 return true; 541 } 542 validateIpConfiguration(IpConfiguration ipConfig)543 private static boolean validateIpConfiguration(IpConfiguration ipConfig) { 544 if (ipConfig == null) { 545 Log.e(TAG, "validateIpConfiguration failed: null IpConfiguration"); 546 return false; 547 } 548 if (ipConfig.getIpAssignment() == IpConfiguration.IpAssignment.STATIC) { 549 StaticIpConfiguration staticIpConfig = ipConfig.getStaticIpConfiguration(); 550 if (staticIpConfig == null) { 551 Log.e(TAG, "validateIpConfiguration failed: null StaticIpConfiguration"); 552 return false; 553 } 554 if (staticIpConfig.getIpAddress() == null) { 555 Log.e(TAG, "validateIpConfiguration failed: null static ip Address"); 556 return false; 557 } 558 } 559 return true; 560 } 561 562 /** 563 * Enums to specify if the provided config is being validated for add or update. 564 */ 565 public static final boolean VALIDATE_FOR_ADD = true; 566 public static final boolean VALIDATE_FOR_UPDATE = false; 567 568 /** 569 * Validate the configuration received from an external application. 570 * 571 * This method checks for the following parameters: 572 * 1. {@link WifiConfiguration#SSID} 573 * 2. {@link WifiConfiguration#BSSID} 574 * 3. {@link WifiConfiguration#preSharedKey} 575 * 4. {@link WifiConfiguration#allowedKeyManagement} 576 * 5. {@link WifiConfiguration#allowedProtocols} 577 * 6. {@link WifiConfiguration#allowedAuthAlgorithms} 578 * 7. {@link WifiConfiguration#allowedGroupCiphers} 579 * 8. {@link WifiConfiguration#allowedPairwiseCiphers} 580 * 9. {@link WifiConfiguration#getIpConfiguration()} 581 * 582 * @param config {@link WifiConfiguration} received from an external application. 583 * @param isAdd {@link #VALIDATE_FOR_ADD} to indicate a network config received for an add, 584 * {@link #VALIDATE_FOR_UPDATE} for a network config received for an update. 585 * These 2 cases need to be handled differently because the config received for an 586 * update could contain only the fields that are being changed. 587 * @return true if the parameters are valid, false otherwise. 588 */ validate(WifiConfiguration config, boolean isAdd)589 public static boolean validate(WifiConfiguration config, boolean isAdd) { 590 if (!validateSsid(config.SSID, isAdd)) { 591 return false; 592 } 593 if (!validateBssid(config.BSSID)) { 594 return false; 595 } 596 if (!validateBitSets(config)) { 597 return false; 598 } 599 if (!validateKeyMgmt(config.allowedKeyManagement)) { 600 return false; 601 } 602 if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK) 603 && !validatePassword(config.preSharedKey, isAdd, false)) { 604 return false; 605 } 606 if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE)) { 607 // PMF mandatory for OWE networks 608 if (!config.requirePmf) { 609 Log.e(TAG, "PMF must be enabled for OWE networks"); 610 return false; 611 } 612 } 613 if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SAE)) { 614 // PMF mandatory for WPA3-Personal networks 615 if (!config.requirePmf) { 616 Log.e(TAG, "PMF must be enabled for SAE networks"); 617 return false; 618 } 619 if (!validatePassword(config.preSharedKey, isAdd, true)) { 620 return false; 621 } 622 } 623 if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SUITE_B_192)) { 624 // PMF mandatory for WPA3-Enterprise networks 625 if (!config.requirePmf) { 626 Log.e(TAG, "PMF must be enabled for Suite-B 192-bit networks"); 627 return false; 628 } 629 } 630 // b/153435438: Added to deal with badly formed WifiConfiguration from apps. 631 if (config.preSharedKey != null && !config.needsPreSharedKey()) { 632 Log.e(TAG, "preSharedKey set with an invalid KeyMgmt, resetting KeyMgmt to WPA_PSK"); 633 config.allowedKeyManagement.clear(); 634 config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); 635 } 636 if (!validateIpConfiguration(config.getIpConfiguration())) { 637 return false; 638 } 639 // TBD: Validate some enterprise params as well in the future here. 640 return true; 641 } 642 validateBssidPattern( Pair<MacAddress, MacAddress> bssidPatternMatcher)643 private static boolean validateBssidPattern( 644 Pair<MacAddress, MacAddress> bssidPatternMatcher) { 645 if (bssidPatternMatcher == null) return true; 646 MacAddress baseAddress = bssidPatternMatcher.first; 647 MacAddress mask = bssidPatternMatcher.second; 648 if (baseAddress.getAddressType() != MacAddress.TYPE_UNICAST) { 649 Log.e(TAG, "validateBssidPatternMatcher failed : invalid base address: " + baseAddress); 650 return false; 651 } 652 if (mask.equals(ALL_ZEROS_MAC_ADDRESS) 653 && !baseAddress.equals(ALL_ZEROS_MAC_ADDRESS)) { 654 Log.e(TAG, "validateBssidPatternMatcher failed : invalid mask/base: " + mask + "/" 655 + baseAddress); 656 return false; 657 } 658 // TBD: Can we do any more sanity checks? 659 return true; 660 } 661 662 // TODO(b/113878056): Some of this is duplicated in {@link WifiNetworkConfigBuilder}. 663 // Merge them somehow?. isValidNetworkSpecifier(WifiNetworkSpecifier specifier)664 private static boolean isValidNetworkSpecifier(WifiNetworkSpecifier specifier) { 665 PatternMatcher ssidPatternMatcher = specifier.ssidPatternMatcher; 666 Pair<MacAddress, MacAddress> bssidPatternMatcher = specifier.bssidPatternMatcher; 667 if (ssidPatternMatcher == null || bssidPatternMatcher == null) { 668 return false; 669 } 670 if (ssidPatternMatcher.getPath() == null || bssidPatternMatcher.first == null 671 || bssidPatternMatcher.second == null) { 672 return false; 673 } 674 return true; 675 } 676 isMatchNoneNetworkSpecifier(WifiNetworkSpecifier specifier)677 private static boolean isMatchNoneNetworkSpecifier(WifiNetworkSpecifier specifier) { 678 PatternMatcher ssidPatternMatcher = specifier.ssidPatternMatcher; 679 Pair<MacAddress, MacAddress> bssidPatternMatcher = specifier.bssidPatternMatcher; 680 if (ssidPatternMatcher.getType() != PatternMatcher.PATTERN_PREFIX 681 && ssidPatternMatcher.getPath().equals(MATCH_EMPTY_SSID_PATTERN_PATH)) { 682 return true; 683 } 684 if (bssidPatternMatcher.equals(MATCH_NONE_BSSID_PATTERN)) { 685 return true; 686 } 687 return false; 688 } 689 isMatchAllNetworkSpecifier(WifiNetworkSpecifier specifier)690 private static boolean isMatchAllNetworkSpecifier(WifiNetworkSpecifier specifier) { 691 PatternMatcher ssidPatternMatcher = specifier.ssidPatternMatcher; 692 Pair<MacAddress, MacAddress> bssidPatternMatcher = specifier.bssidPatternMatcher; 693 if (ssidPatternMatcher.match(MATCH_EMPTY_SSID_PATTERN_PATH) 694 && bssidPatternMatcher.equals(MATCH_ALL_BSSID_PATTERN)) { 695 return true; 696 } 697 return false; 698 } 699 700 /** 701 * Validate the configuration received from an external application inside 702 * {@link WifiNetworkSpecifier}. 703 * 704 * This method checks for the following parameters: 705 * 1. {@link WifiNetworkSpecifier#ssidPatternMatcher} 706 * 2. {@link WifiNetworkSpecifier#bssidPatternMatcher} 707 * 3. {@link WifiConfiguration#SSID} 708 * 4. {@link WifiConfiguration#BSSID} 709 * 5. {@link WifiConfiguration#preSharedKey} 710 * 6. {@link WifiConfiguration#allowedKeyManagement} 711 * 7. {@link WifiConfiguration#allowedProtocols} 712 * 8. {@link WifiConfiguration#allowedAuthAlgorithms} 713 * 9. {@link WifiConfiguration#allowedGroupCiphers} 714 * 10. {@link WifiConfiguration#allowedPairwiseCiphers} 715 * 11. {@link WifiConfiguration#getIpConfiguration()} 716 * 717 * @param specifier Instance of {@link WifiNetworkSpecifier}. 718 * @return true if the parameters are valid, false otherwise. 719 */ validateNetworkSpecifier(WifiNetworkSpecifier specifier)720 public static boolean validateNetworkSpecifier(WifiNetworkSpecifier specifier) { 721 if (!isValidNetworkSpecifier(specifier)) { 722 Log.e(TAG, "validateNetworkSpecifier failed : invalid network specifier"); 723 return false; 724 } 725 if (isMatchNoneNetworkSpecifier(specifier)) { 726 Log.e(TAG, "validateNetworkSpecifier failed : match-none specifier"); 727 return false; 728 } 729 if (isMatchAllNetworkSpecifier(specifier)) { 730 Log.e(TAG, "validateNetworkSpecifier failed : match-all specifier"); 731 return false; 732 } 733 WifiConfiguration config = specifier.wifiConfiguration; 734 if (specifier.ssidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL) { 735 // For literal SSID matches, the value should satisfy SSID requirements. 736 // WifiConfiguration.SSID needs quotes around ASCII SSID. 737 if (!validateSsid(addEnclosingQuotes(specifier.ssidPatternMatcher.getPath()), true)) { 738 return false; 739 } 740 } else { 741 if (config.hiddenSSID) { 742 Log.e(TAG, "validateNetworkSpecifier failed : ssid pattern not supported " 743 + "for hidden networks"); 744 return false; 745 } 746 } 747 if (Objects.equals(specifier.bssidPatternMatcher.second, MacAddress.BROADCAST_ADDRESS)) { 748 // For literal BSSID matches, the value should satisfy MAC address requirements. 749 if (!validateBssid(specifier.bssidPatternMatcher.first)) { 750 return false; 751 } 752 } else { 753 if (!validateBssidPattern(specifier.bssidPatternMatcher)) { 754 return false; 755 } 756 } 757 if (!validateBitSets(config)) { 758 return false; 759 } 760 if (!validateKeyMgmt(config.allowedKeyManagement)) { 761 return false; 762 } 763 if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK) 764 && !validatePassword(config.preSharedKey, true, false)) { 765 return false; 766 } 767 if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE)) { 768 // PMF mandatory for OWE networks 769 if (!config.requirePmf) { 770 return false; 771 } 772 } 773 if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SAE)) { 774 // PMF mandatory for WPA3-Personal networks 775 if (!config.requirePmf) { 776 return false; 777 } 778 if (!validatePassword(config.preSharedKey, true, true)) { 779 return false; 780 } 781 } 782 if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SUITE_B_192)) { 783 // PMF mandatory for WPA3-Enterprise networks 784 if (!config.requirePmf) { 785 return false; 786 } 787 } 788 // TBD: Validate some enterprise params as well in the future here. 789 return true; 790 } 791 792 /** 793 * Check if the provided two networks are the same. 794 * Note: This does not check if network selection BSSID's are the same. 795 * 796 * @param config Configuration corresponding to a network. 797 * @param config1 Configuration corresponding to another network. 798 * 799 * @return true if |config| and |config1| are the same network. 800 * false otherwise. 801 */ isSameNetwork(WifiConfiguration config, WifiConfiguration config1)802 public static boolean isSameNetwork(WifiConfiguration config, WifiConfiguration config1) { 803 if (config == null && config1 == null) { 804 return true; 805 } 806 if (config == null || config1 == null) { 807 return false; 808 } 809 if (config.networkId != config1.networkId) { 810 return false; 811 } 812 if (!Objects.equals(config.SSID, config1.SSID)) { 813 return false; 814 } 815 if (WifiConfigurationUtil.hasCredentialChanged(config, config1)) { 816 return false; 817 } 818 return true; 819 } 820 821 /** 822 * Create a PnoNetwork object from the provided WifiConfiguration. 823 * 824 * @param config Configuration corresponding to the network. 825 * @return PnoNetwork object corresponding to the network. 826 */ createPnoNetwork( WifiConfiguration config)827 public static WifiScanner.PnoSettings.PnoNetwork createPnoNetwork( 828 WifiConfiguration config) { 829 WifiScanner.PnoSettings.PnoNetwork pnoNetwork = 830 new WifiScanner.PnoSettings.PnoNetwork(config.SSID); 831 if (config.hiddenSSID) { 832 pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_DIRECTED_SCAN; 833 } 834 pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_A_BAND; 835 pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_G_BAND; 836 if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) { 837 pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_PSK; 838 } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP) 839 || config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)) { 840 pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_EAPOL; 841 } else { 842 pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_OPEN; 843 } 844 return pnoNetwork; 845 } 846 847 848 /** 849 * General WifiConfiguration list sorting algorithm: 850 * 1, Place the fully enabled networks first. 851 * 2. Next place all the temporarily disabled networks. 852 * 3. Place the permanently disabled networks last (Permanently disabled networks are removed 853 * before WifiConfigManager uses this comparator today!). 854 * 855 * Among the networks with the same status, sort them in the order determined by the return of 856 * {@link #compareNetworksWithSameStatus(WifiConfiguration, WifiConfiguration)} method 857 * implementation. 858 */ 859 public abstract static class WifiConfigurationComparator implements 860 Comparator<WifiConfiguration> { 861 private static final int ENABLED_NETWORK_SCORE = 3; 862 private static final int TEMPORARY_DISABLED_NETWORK_SCORE = 2; 863 private static final int PERMANENTLY_DISABLED_NETWORK_SCORE = 1; 864 865 @Override compare(WifiConfiguration a, WifiConfiguration b)866 public int compare(WifiConfiguration a, WifiConfiguration b) { 867 int configAScore = getNetworkStatusScore(a); 868 int configBScore = getNetworkStatusScore(b); 869 if (configAScore == configBScore) { 870 return compareNetworksWithSameStatus(a, b); 871 } else { 872 return Integer.compare(configBScore, configAScore); 873 } 874 } 875 876 // This needs to be implemented by the connected/disconnected PNO list comparator. compareNetworksWithSameStatus(WifiConfiguration a, WifiConfiguration b)877 abstract int compareNetworksWithSameStatus(WifiConfiguration a, WifiConfiguration b); 878 879 /** 880 * Returns an integer representing a score for each configuration. The scores are assigned 881 * based on the status of the configuration. The scores are assigned according to the order: 882 * Fully enabled network > Temporarily disabled network > Permanently disabled network. 883 */ getNetworkStatusScore(WifiConfiguration config)884 private int getNetworkStatusScore(WifiConfiguration config) { 885 if (config.getNetworkSelectionStatus().isNetworkEnabled()) { 886 return ENABLED_NETWORK_SCORE; 887 } else if (config.getNetworkSelectionStatus().isNetworkTemporaryDisabled()) { 888 return TEMPORARY_DISABLED_NETWORK_SCORE; 889 } else { 890 return PERMANENTLY_DISABLED_NETWORK_SCORE; 891 } 892 } 893 } 894 } 895