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