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