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