• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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