blob: 11eb0c4fbf4a80b874530581a6f3a96e81829ef5 [file] [log] [blame]
Bartosz Fabianowski82893b52016-02-12 10:30:55 +01001/*
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
17package com.android.server.wifi;
18
Quang Luonge25b96e2021-07-01 16:15:32 -070019import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
Quang Luongbe6a6a32021-07-02 16:44:25 -070020import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_EAP;
21import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE;
David Suaf241752019-12-04 11:34:40 -080022import static android.net.wifi.WifiManager.ALL_ZEROS_MAC_ADDRESS;
23
Roshan Piusd3c28bf2018-09-27 11:36:37 -070024import static com.android.server.wifi.util.NativeUtil.addEnclosingQuotes;
25
Chalard Jean2c6d14d2021-05-12 22:22:06 +090026import android.annotation.SuppressLint;
Roshan Piusd01173d2016-07-18 10:32:35 -070027import android.net.IpConfiguration;
Roshan Piusd3c28bf2018-09-27 11:36:37 -070028import android.net.MacAddress;
Roshan Pius2dbf8912017-05-31 14:42:19 -070029import android.net.StaticIpConfiguration;
Jimmy Chen94fda172020-12-29 13:44:24 +080030import android.net.wifi.SecurityParams;
Bartosz Fabianowski82893b52016-02-12 10:30:55 +010031import android.net.wifi.WifiConfiguration;
Roshan Piusd01173d2016-07-18 10:32:35 -070032import android.net.wifi.WifiEnterpriseConfig;
Quang Luonge25b96e2021-07-01 16:15:32 -070033import android.net.wifi.WifiInfo;
Jimmy Chen94fda172020-12-29 13:44:24 +080034import android.net.wifi.WifiManager;
Roshan Piusd3c28bf2018-09-27 11:36:37 -070035import android.net.wifi.WifiNetworkSpecifier;
Roshan Pius8e3836f2016-07-25 11:33:09 -070036import android.net.wifi.WifiScanner;
Roshan Piusd3c28bf2018-09-27 11:36:37 -070037import android.os.PatternMatcher;
Peter Qiu5513f8e2017-07-27 14:41:36 -070038import android.text.TextUtils;
Roshan Pius2dbf8912017-05-31 14:42:19 -070039import android.util.Log;
Roshan Piusd3c28bf2018-09-27 11:36:37 -070040import android.util.Pair;
Bartosz Fabianowski82893b52016-02-12 10:30:55 +010041
Roshan Piusd01173d2016-07-18 10:32:35 -070042import com.android.internal.annotations.VisibleForTesting;
Quang Luongbe6a6a32021-07-02 16:44:25 -070043import com.android.modules.utils.build.SdkLevel;
Roshan Pius2dbf8912017-05-31 14:42:19 -070044import com.android.server.wifi.util.NativeUtil;
Roshan Piusd01173d2016-07-18 10:32:35 -070045
Roshan Pius8cc621a2018-04-27 10:28:27 -070046import java.nio.charset.StandardCharsets;
Roshan Piusd01173d2016-07-18 10:32:35 -070047import java.security.cert.X509Certificate;
Roshan Piuse8e4deb2020-09-29 14:33:21 -070048import java.util.ArrayList;
Roshan Piusd01173d2016-07-18 10:32:35 -070049import java.util.Arrays;
Roshan Pius2dbf8912017-05-31 14:42:19 -070050import java.util.BitSet;
Roshan Pius8e3836f2016-07-25 11:33:09 -070051import java.util.Comparator;
Jimmy Chen513cdb42021-04-23 18:36:20 +080052import java.util.List;
Roshan Piusd01173d2016-07-18 10:32:35 -070053import java.util.Objects;
Bartosz Fabianowski82893b52016-02-12 10:30:55 +010054
55/**
Roshan Pius7ef62b42016-06-28 12:59:42 -070056 * WifiConfiguration utility for any {@link android.net.wifi.WifiConfiguration} related operations.
57 * Currently contains:
58 * > Helper method to check if the WifiConfiguration object is visible to the provided users.
59 * > Helper methods to identify the encryption of a WifiConfiguration object.
Bartosz Fabianowski82893b52016-02-12 10:30:55 +010060 */
61public class WifiConfigurationUtil {
Roshan Pius2dbf8912017-05-31 14:42:19 -070062 private static final String TAG = "WifiConfigurationUtil";
63
64 /**
65 * Constants used for validating external config objects.
66 */
Roshan Piusd3c28bf2018-09-27 11:36:37 -070067 private static final int ENCLOSING_QUOTES_LEN = 2;
68 private static final int SSID_UTF_8_MIN_LEN = 1 + ENCLOSING_QUOTES_LEN;
Hu Wang3a7cbd52021-06-25 18:16:01 +080069 private static final int SSID_UTF_8_MAX_LEN = // wifigbk++
70 WifiGbk.MAX_SSID_UTF_LENGTH + ENCLOSING_QUOTES_LEN;
Roshan Pius2dbf8912017-05-31 14:42:19 -070071 private static final int SSID_HEX_MIN_LEN = 2;
72 private static final int SSID_HEX_MAX_LEN = 64;
Roshan Piusd3c28bf2018-09-27 11:36:37 -070073 private static final int PSK_ASCII_MIN_LEN = 8 + ENCLOSING_QUOTES_LEN;
Hai Shalom563fa312019-03-01 10:44:46 -080074 private static final int SAE_ASCII_MIN_LEN = 1 + ENCLOSING_QUOTES_LEN;
75 private static final int PSK_SAE_ASCII_MAX_LEN = 63 + ENCLOSING_QUOTES_LEN;
76 private static final int PSK_SAE_HEX_LEN = 64;
Roshan Piuse8e4deb2020-09-29 14:33:21 -070077 private static final int WEP104_KEY_BYTES_LEN = 13;
78 private static final int WEP40_KEY_BYTES_LEN = 5;
David Suaf241752019-12-04 11:34:40 -080079
Roshan Piusd266a342017-06-06 14:51:19 -070080 @VisibleForTesting
81 public static final String PASSWORD_MASK = "*";
Roshan Piusd3c28bf2018-09-27 11:36:37 -070082 private static final String MATCH_EMPTY_SSID_PATTERN_PATH = "";
83 private static final Pair<MacAddress, MacAddress> MATCH_NONE_BSSID_PATTERN =
David Suaf241752019-12-04 11:34:40 -080084 new Pair<>(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS);
Roshan Piusd3c28bf2018-09-27 11:36:37 -070085 private static final Pair<MacAddress, MacAddress> MATCH_ALL_BSSID_PATTERN =
David Suaf241752019-12-04 11:34:40 -080086 new Pair<>(ALL_ZEROS_MAC_ADDRESS, ALL_ZEROS_MAC_ADDRESS);
Roshan Pius2dbf8912017-05-31 14:42:19 -070087
Quang Luonge25b96e2021-07-01 16:15:32 -070088 private static final int NETWORK_ID_SECURITY_MASK = 0xff;
89 private static final int NETWORK_ID_SECURITY_OFFSET = 23;
90
Bartosz Fabianowski82893b52016-02-12 10:30:55 +010091 /**
Roshan Pius7ef62b42016-06-28 12:59:42 -070092 * Checks if the provided |wepKeys| array contains any non-null value;
93 */
94 public static boolean hasAnyValidWepKey(String[] wepKeys) {
95 for (int i = 0; i < wepKeys.length; i++) {
96 if (wepKeys[i] != null) {
97 return true;
98 }
99 }
100 return false;
101 }
102
103 /**
104 * Helper method to check if the provided |config| corresponds to a PSK network or not.
105 */
106 public static boolean isConfigForPskNetwork(WifiConfiguration config) {
Jimmy Chen1fea6572020-11-13 18:45:45 +0800107 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK);
Roshan Pius7ef62b42016-06-28 12:59:42 -0700108 }
109
110 /**
Jimmy Chen877256b2019-09-16 15:57:57 +0800111 * Helper method to check if the provided |config| corresponds to a WAPI PSK network or not.
112 */
113 public static boolean isConfigForWapiPskNetwork(WifiConfiguration config) {
Jimmy Chen1fea6572020-11-13 18:45:45 +0800114 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_WAPI_PSK);
Jimmy Chen877256b2019-09-16 15:57:57 +0800115 }
116
117 /**
118 * Helper method to check if the provided |config| corresponds to a WAPI CERT network or not.
119 */
120 public static boolean isConfigForWapiCertNetwork(WifiConfiguration config) {
Jimmy Chen1fea6572020-11-13 18:45:45 +0800121 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_WAPI_CERT);
Jimmy Chen877256b2019-09-16 15:57:57 +0800122 }
123
124 /**
Hai Shalom7da843c2018-10-15 16:14:04 -0700125 * Helper method to check if the provided |config| corresponds to an SAE network or not.
126 */
127 public static boolean isConfigForSaeNetwork(WifiConfiguration config) {
Jimmy Chen1fea6572020-11-13 18:45:45 +0800128 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE);
Hai Shalom7da843c2018-10-15 16:14:04 -0700129 }
130
131 /**
132 * Helper method to check if the provided |config| corresponds to an OWE network or not.
133 */
134 public static boolean isConfigForOweNetwork(WifiConfiguration config) {
Jimmy Chen1fea6572020-11-13 18:45:45 +0800135 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_OWE);
Hai Shalom7da843c2018-10-15 16:14:04 -0700136 }
137
138 /**
Roshan Pius7ef62b42016-06-28 12:59:42 -0700139 * Helper method to check if the provided |config| corresponds to a EAP network or not.
140 */
141 public static boolean isConfigForEapNetwork(WifiConfiguration config) {
Jimmy Chen1fea6572020-11-13 18:45:45 +0800142 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP);
Roshan Pius7ef62b42016-06-28 12:59:42 -0700143 }
144
145 /**
Jimmy Chen3a7ca7b2021-01-11 14:38:27 +0800146 * Helper method to check if the provided |config| corresponds to
147 * a WPA3 Enterprise network or not.
148 */
149 public static boolean isConfigForWpa3EnterpriseNetwork(WifiConfiguration config) {
150 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE);
151 }
152
153 /**
Hai Shalom7da843c2018-10-15 16:14:04 -0700154 * Helper method to check if the provided |config| corresponds to a EAP Suite-B network or not.
155 */
Jimmy Chen1fea6572020-11-13 18:45:45 +0800156 public static boolean isConfigForWpa3Enterprise192BitNetwork(WifiConfiguration config) {
157 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT);
Hai Shalom7da843c2018-10-15 16:14:04 -0700158 }
159
160 /**
Roshan Pius7ef62b42016-06-28 12:59:42 -0700161 * Helper method to check if the provided |config| corresponds to a WEP network or not.
162 */
163 public static boolean isConfigForWepNetwork(WifiConfiguration config) {
Jimmy Chen1fea6572020-11-13 18:45:45 +0800164 return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_WEP);
Roshan Pius7ef62b42016-06-28 12:59:42 -0700165 }
166
167 /**
Hai Shalom7da843c2018-10-15 16:14:04 -0700168 * Helper method to check if the provided |config| corresponds to an open or enhanced
169 * open network, or not.
Roshan Pius7ef62b42016-06-28 12:59:42 -0700170 */
171 public static boolean isConfigForOpenNetwork(WifiConfiguration config) {
Hai Shalom7da843c2018-10-15 16:14:04 -0700172 return (!(isConfigForWepNetwork(config) || isConfigForPskNetwork(config)
Jimmy Chene92a37b2021-01-15 18:24:51 +0800173 || isConfigForWapiPskNetwork(config) || isConfigForWapiCertNetwork(config)
Hai Shalom7da843c2018-10-15 16:14:04 -0700174 || isConfigForEapNetwork(config) || isConfigForSaeNetwork(config)
Jimmy Chen1fea6572020-11-13 18:45:45 +0800175 || isConfigForWpa3Enterprise192BitNetwork(config)));
Roshan Pius7ef62b42016-06-28 12:59:42 -0700176 }
177
Roshan Piusd01173d2016-07-18 10:32:35 -0700178 /**
179 * Compare existing and new WifiConfiguration objects after a network update and return if
180 * IP parameters have changed or not.
181 *
182 * @param existingConfig Existing WifiConfiguration object corresponding to the network.
183 * @param newConfig New WifiConfiguration object corresponding to the network.
184 * @return true if IP parameters have changed, false otherwise.
185 */
186 public static boolean hasIpChanged(WifiConfiguration existingConfig,
187 WifiConfiguration newConfig) {
188 if (existingConfig.getIpAssignment() != newConfig.getIpAssignment()) {
189 return true;
190 }
191 if (newConfig.getIpAssignment() == IpConfiguration.IpAssignment.STATIC) {
192 return !Objects.equals(existingConfig.getStaticIpConfiguration(),
193 newConfig.getStaticIpConfiguration());
194 }
195 return false;
196 }
197
198 /**
199 * Compare existing and new WifiConfiguration objects after a network update and return if
200 * proxy parameters have changed or not.
201 *
202 * @param existingConfig Existing WifiConfiguration object corresponding to the network.
203 * @param newConfig New WifiConfiguration object corresponding to the network.
Glen Kuhne7b70acb2016-08-31 16:23:22 -0700204 * @return true if proxy parameters have changed, false if no existing config and proxy settings
205 * are NONE, false otherwise.
Roshan Piusd01173d2016-07-18 10:32:35 -0700206 */
207 public static boolean hasProxyChanged(WifiConfiguration existingConfig,
208 WifiConfiguration newConfig) {
Glen Kuhne7b70acb2016-08-31 16:23:22 -0700209 if (existingConfig == null) {
210 return newConfig.getProxySettings() != IpConfiguration.ProxySettings.NONE;
211 }
212 if (newConfig.getProxySettings() != existingConfig.getProxySettings()) {
Roshan Piusd01173d2016-07-18 10:32:35 -0700213 return true;
214 }
Glen Kuhne7b70acb2016-08-31 16:23:22 -0700215 return !Objects.equals(existingConfig.getHttpProxy(), newConfig.getHttpProxy());
Roshan Piusd01173d2016-07-18 10:32:35 -0700216 }
217
218 /**
xshucaa6fc82018-11-14 13:31:33 -0800219 * Compare existing and new WifiConfiguration objects after a network update and return if
220 * MAC randomization setting has changed or not.
221 * @param existingConfig Existing WifiConfiguration object corresponding to the network.
222 * @param newConfig New WifiConfiguration object corresponding to the network.
223 * @return true if MAC randomization setting setting changed or the existing confiuration is
224 * null and the newConfig is setting macRandomizationSetting to the default value.
225 */
226 public static boolean hasMacRandomizationSettingsChanged(WifiConfiguration existingConfig,
227 WifiConfiguration newConfig) {
228 if (existingConfig == null) {
xshub76c5fe2020-08-02 21:30:56 -0700229 return newConfig.macRandomizationSetting != WifiConfiguration.RANDOMIZATION_AUTO;
xshucaa6fc82018-11-14 13:31:33 -0800230 }
231 return newConfig.macRandomizationSetting != existingConfig.macRandomizationSetting;
232 }
233
234 /**
Roshan Piusd01173d2016-07-18 10:32:35 -0700235 * Compare existing and new WifiEnterpriseConfig objects after a network update and return if
236 * credential parameters have changed or not.
237 *
238 * @param existingEnterpriseConfig Existing WifiConfiguration object corresponding to the
239 * network.
240 * @param newEnterpriseConfig New WifiConfiguration object corresponding to the network.
241 * @return true if credentials have changed, false otherwise.
242 */
243 @VisibleForTesting
244 public static boolean hasEnterpriseConfigChanged(WifiEnterpriseConfig existingEnterpriseConfig,
245 WifiEnterpriseConfig newEnterpriseConfig) {
246 if (existingEnterpriseConfig != null && newEnterpriseConfig != null) {
247 if (existingEnterpriseConfig.getEapMethod() != newEnterpriseConfig.getEapMethod()) {
248 return true;
249 }
Hai Shalomdc712ad2020-08-26 15:04:21 -0700250 if (existingEnterpriseConfig.isAuthenticationSimBased()) {
251 // No other credential changes for SIM based methods.
252 // The SIM card is the credential.
253 return false;
254 }
Roshan Piusd01173d2016-07-18 10:32:35 -0700255 if (existingEnterpriseConfig.getPhase2Method()
256 != newEnterpriseConfig.getPhase2Method()) {
257 return true;
258 }
Peter Qiu5513f8e2017-07-27 14:41:36 -0700259 if (!TextUtils.equals(existingEnterpriseConfig.getIdentity(),
Daisuke Niwafa03c9c2018-06-27 14:14:57 +0900260 newEnterpriseConfig.getIdentity())) {
261 return true;
262 }
Hai Shalomdc712ad2020-08-26 15:04:21 -0700263 if (!TextUtils.equals(existingEnterpriseConfig.getAnonymousIdentity(),
Daisuke Niwafa03c9c2018-06-27 14:14:57 +0900264 newEnterpriseConfig.getAnonymousIdentity())) {
Peter Qiu5513f8e2017-07-27 14:41:36 -0700265 return true;
266 }
zhouzhijie8c407492018-01-03 20:02:23 +0800267 if (!TextUtils.equals(existingEnterpriseConfig.getPassword(),
268 newEnterpriseConfig.getPassword())) {
269 return true;
270 }
Roshan Piusd01173d2016-07-18 10:32:35 -0700271 X509Certificate[] existingCaCerts = existingEnterpriseConfig.getCaCertificates();
272 X509Certificate[] newCaCerts = newEnterpriseConfig.getCaCertificates();
273 if (!Arrays.equals(existingCaCerts, newCaCerts)) {
274 return true;
275 }
Hai Shalomdc712ad2020-08-26 15:04:21 -0700276 if (!Arrays.equals(newEnterpriseConfig.getCaCertificateAliases(),
277 existingEnterpriseConfig.getCaCertificateAliases())) {
278 return true;
279 }
280 if (!TextUtils.equals(newEnterpriseConfig.getClientCertificateAlias(),
281 existingEnterpriseConfig.getClientCertificateAlias())) {
282 return true;
283 }
284 if (!TextUtils.equals(newEnterpriseConfig.getAltSubjectMatch(),
285 existingEnterpriseConfig.getAltSubjectMatch())) {
286 return true;
287 }
Jimmy Chene92a37b2021-01-15 18:24:51 +0800288 if (!TextUtils.equals(newEnterpriseConfig.getWapiCertSuite(),
289 existingEnterpriseConfig.getWapiCertSuite())) {
290 return true;
291 }
Hai Shalomdc712ad2020-08-26 15:04:21 -0700292 if (newEnterpriseConfig.getOcsp() != existingEnterpriseConfig.getOcsp()) {
293 return true;
294 }
Roshan Piusd01173d2016-07-18 10:32:35 -0700295 } else {
296 // One of the configs may have an enterpriseConfig
297 if (existingEnterpriseConfig != null || newEnterpriseConfig != null) {
298 return true;
299 }
300 }
301 return false;
302 }
303
304 /**
305 * Compare existing and new WifiConfiguration objects after a network update and return if
306 * credential parameters have changed or not.
307 *
308 * @param existingConfig Existing WifiConfiguration object corresponding to the network.
309 * @param newConfig New WifiConfiguration object corresponding to the network.
310 * @return true if credentials have changed, false otherwise.
311 */
312 public static boolean hasCredentialChanged(WifiConfiguration existingConfig,
313 WifiConfiguration newConfig) {
314 if (!Objects.equals(existingConfig.allowedKeyManagement,
315 newConfig.allowedKeyManagement)) {
316 return true;
317 }
318 if (!Objects.equals(existingConfig.allowedProtocols, newConfig.allowedProtocols)) {
319 return true;
320 }
321 if (!Objects.equals(existingConfig.allowedAuthAlgorithms,
322 newConfig.allowedAuthAlgorithms)) {
323 return true;
324 }
325 if (!Objects.equals(existingConfig.allowedPairwiseCiphers,
326 newConfig.allowedPairwiseCiphers)) {
327 return true;
328 }
329 if (!Objects.equals(existingConfig.allowedGroupCiphers,
330 newConfig.allowedGroupCiphers)) {
331 return true;
332 }
Hai Shalom9b39eda2019-01-10 12:43:56 -0800333 if (!Objects.equals(existingConfig.allowedGroupManagementCiphers,
334 newConfig.allowedGroupManagementCiphers)) {
Hai Shalom7da843c2018-10-15 16:14:04 -0700335 return true;
336 }
337 if (!Objects.equals(existingConfig.allowedSuiteBCiphers,
338 newConfig.allowedSuiteBCiphers)) {
339 return true;
340 }
Jimmy Chen085292e2021-03-08 16:48:18 +0800341 if (!existingConfig.getSecurityParamsList().equals(newConfig.getSecurityParamsList())) {
342 return true;
343 }
Roshan Piusd01173d2016-07-18 10:32:35 -0700344 if (!Objects.equals(existingConfig.preSharedKey, newConfig.preSharedKey)) {
345 return true;
346 }
347 if (!Arrays.equals(existingConfig.wepKeys, newConfig.wepKeys)) {
348 return true;
349 }
350 if (existingConfig.wepTxKeyIndex != newConfig.wepTxKeyIndex) {
351 return true;
352 }
353 if (existingConfig.hiddenSSID != newConfig.hiddenSSID) {
354 return true;
355 }
David Sued4c11e2020-01-27 17:46:50 -0800356 if (existingConfig.requirePmf != newConfig.requirePmf) {
Hai Shalom7da843c2018-10-15 16:14:04 -0700357 return true;
358 }
Steven Liu0bbb2fb2020-08-11 10:08:35 -0700359 if (existingConfig.carrierId != newConfig.carrierId) {
360 return true;
361 }
Roshan Piusd01173d2016-07-18 10:32:35 -0700362 if (hasEnterpriseConfigChanged(existingConfig.enterpriseConfig,
363 newConfig.enterpriseConfig)) {
364 return true;
365 }
366 return false;
367 }
368
Roshan Pius31195d32017-06-23 15:45:36 -0700369 private static boolean validateSsid(String ssid, boolean isAdd) {
370 if (isAdd) {
371 if (ssid == null) {
372 Log.e(TAG, "validateSsid : null string");
373 return false;
374 }
375 } else {
376 if (ssid == null) {
377 // This is an update, so the SSID can be null if that is not being changed.
378 return true;
379 }
Roshan Pius2dbf8912017-05-31 14:42:19 -0700380 }
381 if (ssid.isEmpty()) {
382 Log.e(TAG, "validateSsid failed: empty string");
383 return false;
384 }
385 if (ssid.startsWith("\"")) {
Roshan Pius8cc621a2018-04-27 10:28:27 -0700386 // UTF-8 SSID string
387 byte[] ssidBytes = ssid.getBytes(StandardCharsets.UTF_8);
388 if (ssidBytes.length < SSID_UTF_8_MIN_LEN) {
389 Log.e(TAG, "validateSsid failed: utf-8 ssid string size too small: "
390 + ssidBytes.length);
Roshan Pius2dbf8912017-05-31 14:42:19 -0700391 return false;
392 }
Roshan Pius8cc621a2018-04-27 10:28:27 -0700393 if (ssidBytes.length > SSID_UTF_8_MAX_LEN) {
394 Log.e(TAG, "validateSsid failed: utf-8 ssid string size too large: "
395 + ssidBytes.length);
Roshan Pius2dbf8912017-05-31 14:42:19 -0700396 return false;
397 }
398 } else {
399 // HEX SSID string
400 if (ssid.length() < SSID_HEX_MIN_LEN) {
401 Log.e(TAG, "validateSsid failed: hex string size too small: " + ssid.length());
402 return false;
403 }
404 if (ssid.length() > SSID_HEX_MAX_LEN) {
405 Log.e(TAG, "validateSsid failed: hex string size too large: " + ssid.length());
406 return false;
407 }
408 }
409 try {
410 NativeUtil.decodeSsid(ssid);
411 } catch (IllegalArgumentException e) {
412 Log.e(TAG, "validateSsid failed: malformed string: " + ssid);
413 return false;
414 }
415 return true;
416 }
417
Roshan Piusd3c28bf2018-09-27 11:36:37 -0700418 private static boolean validateBssid(MacAddress bssid) {
419 if (bssid == null) return true;
420 if (bssid.getAddressType() != MacAddress.TYPE_UNICAST) {
421 Log.e(TAG, "validateBssid failed: invalid bssid");
422 return false;
423 }
424 return true;
425 }
426
427 private static boolean validateBssid(String bssid) {
428 if (bssid == null) return true;
429 if (bssid.isEmpty()) {
430 Log.e(TAG, "validateBssid failed: empty string");
431 return false;
432 }
Roshan Pius72e957c2020-06-24 16:45:22 +0000433 // Allow reset of bssid with "any".
434 if (bssid.equals(ClientModeImpl.SUPPLICANT_BSSID_ANY)) return true;
Roshan Piusd3c28bf2018-09-27 11:36:37 -0700435 MacAddress bssidMacAddress;
436 try {
437 bssidMacAddress = MacAddress.fromString(bssid);
438 } catch (IllegalArgumentException e) {
439 Log.e(TAG, "validateBssid failed: malformed string: " + bssid);
440 return false;
441 }
442 if (!validateBssid(bssidMacAddress)) {
443 return false;
444 }
445 return true;
446 }
447
Oscar Shuc2e87562023-07-07 02:21:41 +0000448 private static boolean validatePassword(String password, boolean isAdd, boolean isSae,
449 boolean isWapi) {
Roshan Pius31195d32017-06-23 15:45:36 -0700450 if (isAdd) {
Hai Shalom563fa312019-03-01 10:44:46 -0800451 if (password == null) {
452 Log.e(TAG, "validatePassword: null string");
Roshan Pius31195d32017-06-23 15:45:36 -0700453 return false;
454 }
455 } else {
Hai Shalom563fa312019-03-01 10:44:46 -0800456 if (password == null) {
Roshan Pius31195d32017-06-23 15:45:36 -0700457 // This is an update, so the psk can be null if that is not being changed.
458 return true;
Hai Shalom563fa312019-03-01 10:44:46 -0800459 } else if (password.equals(PASSWORD_MASK)) {
Roshan Pius31195d32017-06-23 15:45:36 -0700460 // This is an update, so the app might have returned back the masked password, let
461 // it thru. WifiConfigManager will handle it.
462 return true;
463 }
Roshan Pius2dbf8912017-05-31 14:42:19 -0700464 }
Hai Shalom563fa312019-03-01 10:44:46 -0800465 if (password.isEmpty()) {
466 Log.e(TAG, "validatePassword failed: empty string");
Roshan Pius2dbf8912017-05-31 14:42:19 -0700467 return false;
468 }
Hai Shalom563fa312019-03-01 10:44:46 -0800469 if (password.startsWith("\"")) {
Roshan Pius2dbf8912017-05-31 14:42:19 -0700470 // ASCII PSK string
Hai Shalom563fa312019-03-01 10:44:46 -0800471 byte[] passwordBytes = password.getBytes(StandardCharsets.US_ASCII);
472 int targetMinLength;
473
474 if (isSae) {
475 targetMinLength = SAE_ASCII_MIN_LEN;
476 } else {
477 targetMinLength = PSK_ASCII_MIN_LEN;
478 }
479 if (passwordBytes.length < targetMinLength) {
480 Log.e(TAG, "validatePassword failed: ASCII string size too small: "
481 + passwordBytes.length);
Roshan Pius2dbf8912017-05-31 14:42:19 -0700482 return false;
483 }
Hai Shalom563fa312019-03-01 10:44:46 -0800484 if (passwordBytes.length > PSK_SAE_ASCII_MAX_LEN) {
485 Log.e(TAG, "validatePassword failed: ASCII string size too large: "
486 + passwordBytes.length);
Roshan Pius2dbf8912017-05-31 14:42:19 -0700487 return false;
488 }
489 } else {
490 // HEX PSK string
Oscar Shuc2e87562023-07-07 02:21:41 +0000491 if (isWapi) {
492 // Protect system against malicious actors injecting arbitrarily large passwords.
493 if (password.length() > 100) {
494 Log.e(TAG, "validatePassword failed: WAPI hex string too long: "
495 + password.length());
496 return false;
497 }
498 } else if (password.length() != PSK_SAE_HEX_LEN) {
Hai Shalom563fa312019-03-01 10:44:46 -0800499 Log.e(TAG, "validatePassword failed: hex string size mismatch: "
500 + password.length());
Roshan Pius2dbf8912017-05-31 14:42:19 -0700501 return false;
502 }
503 }
504 try {
Hai Shalom563fa312019-03-01 10:44:46 -0800505 NativeUtil.hexOrQuotedStringToBytes(password);
Roshan Pius2dbf8912017-05-31 14:42:19 -0700506 } catch (IllegalArgumentException e) {
Hai Shalom563fa312019-03-01 10:44:46 -0800507 Log.e(TAG, "validatePassword failed: malformed string: " + password);
Roshan Pius2dbf8912017-05-31 14:42:19 -0700508 return false;
509 }
510 return true;
511 }
512
Roshan Piuse8e4deb2020-09-29 14:33:21 -0700513 private static boolean validateWepKeys(String[] wepKeys, int wepTxKeyIndex, boolean isAdd) {
514 if (isAdd) {
515 if (wepKeys == null) {
516 Log.e(TAG, "validateWepKeys: null string");
517 return false;
518 }
519 } else {
520 if (wepKeys == null) {
521 // This is an update, so the psk can be null if that is not being changed.
522 return true;
523 } else {
524 boolean allMaskedKeys = true;
525 for (int i = 0; i < wepKeys.length; i++) {
526 if (wepKeys[i] != null && !TextUtils.equals(wepKeys[i], PASSWORD_MASK)) {
527 allMaskedKeys = false;
528 }
529 }
530 if (allMaskedKeys) {
531 // This is an update, so the app might have returned back the masked password,
532 // let it thru. WifiConfigManager will handle it.
533 return true;
534 }
535 }
536 }
537 for (int i = 0; i < wepKeys.length; i++) {
538 if (wepKeys[i] != null) {
539 try {
540 ArrayList<Byte> wepKeyBytes =
541 NativeUtil.hexOrQuotedStringToBytes(wepKeys[i]);
542 if (wepKeyBytes.size() != WEP40_KEY_BYTES_LEN
543 && wepKeyBytes.size() != WEP104_KEY_BYTES_LEN) {
544 Log.e(TAG, "validateWepKeys: invalid wep key length "
545 + wepKeys[i].length() + " at index " + i);
546 return false;
547 }
548 } catch (IllegalArgumentException e) {
549 Log.e(TAG, "validateWepKeys: invalid wep key at index " + i, e);
550 return false;
551 }
552 }
553 }
554 if (wepTxKeyIndex >= wepKeys.length) {
555 Log.e(TAG, "validateWepKeys: invalid wep tx key index " + wepTxKeyIndex
556 + " wepKeys len: " + wepKeys.length);
557 return false;
558 }
559 return true;
560 }
561
Roshan Pius8b4cb222018-03-14 11:14:24 -0700562 private static boolean validateBitSet(BitSet bitSet, int validValuesLength) {
563 if (bitSet == null) return false;
564 BitSet clonedBitset = (BitSet) bitSet.clone();
565 clonedBitset.clear(0, validValuesLength);
566 return clonedBitset.isEmpty();
567 }
568
569 private static boolean validateBitSets(WifiConfiguration config) {
570 // 1. Check |allowedKeyManagement|.
571 if (!validateBitSet(config.allowedKeyManagement,
572 WifiConfiguration.KeyMgmt.strings.length)) {
573 Log.e(TAG, "validateBitsets failed: invalid allowedKeyManagement bitset "
574 + config.allowedKeyManagement);
Roshan Pius2dbf8912017-05-31 14:42:19 -0700575 return false;
576 }
Roshan Pius8b4cb222018-03-14 11:14:24 -0700577 // 2. Check |allowedProtocols|.
578 if (!validateBitSet(config.allowedProtocols,
579 WifiConfiguration.Protocol.strings.length)) {
580 Log.e(TAG, "validateBitsets failed: invalid allowedProtocols bitset "
581 + config.allowedProtocols);
582 return false;
583 }
584 // 3. Check |allowedAuthAlgorithms|.
585 if (!validateBitSet(config.allowedAuthAlgorithms,
586 WifiConfiguration.AuthAlgorithm.strings.length)) {
587 Log.e(TAG, "validateBitsets failed: invalid allowedAuthAlgorithms bitset "
588 + config.allowedAuthAlgorithms);
589 return false;
590 }
591 // 4. Check |allowedGroupCiphers|.
592 if (!validateBitSet(config.allowedGroupCiphers,
593 WifiConfiguration.GroupCipher.strings.length)) {
594 Log.e(TAG, "validateBitsets failed: invalid allowedGroupCiphers bitset "
595 + config.allowedGroupCiphers);
596 return false;
597 }
598 // 5. Check |allowedPairwiseCiphers|.
599 if (!validateBitSet(config.allowedPairwiseCiphers,
600 WifiConfiguration.PairwiseCipher.strings.length)) {
601 Log.e(TAG, "validateBitsets failed: invalid allowedPairwiseCiphers bitset "
602 + config.allowedPairwiseCiphers);
603 return false;
604 }
605 return true;
606 }
607
608 private static boolean validateKeyMgmt(BitSet keyMgmnt) {
Roshan Pius2dbf8912017-05-31 14:42:19 -0700609 if (keyMgmnt.cardinality() > 1) {
Sunil Ravi93243ca2020-03-03 16:45:00 -0800610 if (keyMgmnt.cardinality() > 3) {
611 Log.e(TAG, "validateKeyMgmt failed: cardinality > 3");
Roshan Pius2dbf8912017-05-31 14:42:19 -0700612 return false;
613 }
614 if (!keyMgmnt.get(WifiConfiguration.KeyMgmt.WPA_EAP)) {
615 Log.e(TAG, "validateKeyMgmt failed: not WPA_EAP");
616 return false;
617 }
618 if (!keyMgmnt.get(WifiConfiguration.KeyMgmt.IEEE8021X)
619 && !keyMgmnt.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
620 Log.e(TAG, "validateKeyMgmt failed: not PSK or 8021X");
621 return false;
622 }
Jimmy Chen3c76ccb2021-06-15 01:11:43 +0800623 // SUITE-B keymgmt must be WPA_EAP + IEEE8021X + SUITE_B_192.
Sunil Ravi93243ca2020-03-03 16:45:00 -0800624 if (keyMgmnt.cardinality() == 3
Jimmy Chen3c76ccb2021-06-15 01:11:43 +0800625 && !(keyMgmnt.get(WifiConfiguration.KeyMgmt.WPA_EAP)
626 && keyMgmnt.get(WifiConfiguration.KeyMgmt.IEEE8021X)
627 && keyMgmnt.get(WifiConfiguration.KeyMgmt.SUITE_B_192))) {
Sunil Ravi93243ca2020-03-03 16:45:00 -0800628 Log.e(TAG, "validateKeyMgmt failed: not SUITE_B_192");
chenpaul727eb782019-02-13 18:03:21 +0800629 return false;
630 }
Roshan Pius2dbf8912017-05-31 14:42:19 -0700631 }
632 return true;
633 }
634
635 private static boolean validateIpConfiguration(IpConfiguration ipConfig) {
636 if (ipConfig == null) {
637 Log.e(TAG, "validateIpConfiguration failed: null IpConfiguration");
638 return false;
639 }
640 if (ipConfig.getIpAssignment() == IpConfiguration.IpAssignment.STATIC) {
641 StaticIpConfiguration staticIpConfig = ipConfig.getStaticIpConfiguration();
642 if (staticIpConfig == null) {
643 Log.e(TAG, "validateIpConfiguration failed: null StaticIpConfiguration");
644 return false;
645 }
Roshan Piusfd140082019-11-14 06:23:54 -0800646 if (staticIpConfig.getIpAddress() == null) {
Roshan Pius2dbf8912017-05-31 14:42:19 -0700647 Log.e(TAG, "validateIpConfiguration failed: null static ip Address");
648 return false;
649 }
650 }
651 return true;
652 }
653
654 /**
Roshan Pius31195d32017-06-23 15:45:36 -0700655 * Enums to specify if the provided config is being validated for add or update.
656 */
657 public static final boolean VALIDATE_FOR_ADD = true;
658 public static final boolean VALIDATE_FOR_UPDATE = false;
659
660 /**
Roshan Pius2dbf8912017-05-31 14:42:19 -0700661 * Validate the configuration received from an external application.
662 *
663 * This method checks for the following parameters:
664 * 1. {@link WifiConfiguration#SSID}
Roshan Piusd3c28bf2018-09-27 11:36:37 -0700665 * 2. {@link WifiConfiguration#BSSID}
666 * 3. {@link WifiConfiguration#preSharedKey}
667 * 4. {@link WifiConfiguration#allowedKeyManagement}
668 * 5. {@link WifiConfiguration#allowedProtocols}
669 * 6. {@link WifiConfiguration#allowedAuthAlgorithms}
670 * 7. {@link WifiConfiguration#allowedGroupCiphers}
671 * 8. {@link WifiConfiguration#allowedPairwiseCiphers}
672 * 9. {@link WifiConfiguration#getIpConfiguration()}
Roshan Pius31195d32017-06-23 15:45:36 -0700673 *
Roshan Pius2dbf8912017-05-31 14:42:19 -0700674 * @param config {@link WifiConfiguration} received from an external application.
Roshan Pius31195d32017-06-23 15:45:36 -0700675 * @param isAdd {@link #VALIDATE_FOR_ADD} to indicate a network config received for an add,
676 * {@link #VALIDATE_FOR_UPDATE} for a network config received for an update.
677 * These 2 cases need to be handled differently because the config received for an
678 * update could contain only the fields that are being changed.
Roshan Pius2dbf8912017-05-31 14:42:19 -0700679 * @return true if the parameters are valid, false otherwise.
680 */
Roshan Pius31195d32017-06-23 15:45:36 -0700681 public static boolean validate(WifiConfiguration config, boolean isAdd) {
682 if (!validateSsid(config.SSID, isAdd)) {
Roshan Pius2dbf8912017-05-31 14:42:19 -0700683 return false;
684 }
Roshan Piusd3c28bf2018-09-27 11:36:37 -0700685 if (!validateBssid(config.BSSID)) {
686 return false;
687 }
Roshan Pius8b4cb222018-03-14 11:14:24 -0700688 if (!validateBitSets(config)) {
689 return false;
690 }
Roshan Pius2dbf8912017-05-31 14:42:19 -0700691 if (!validateKeyMgmt(config.allowedKeyManagement)) {
692 return false;
693 }
Jimmy Chen1fea6572020-11-13 18:45:45 +0800694 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_WEP)
Roshan Piuse8e4deb2020-09-29 14:33:21 -0700695 && config.wepKeys != null
696 && !validateWepKeys(config.wepKeys, config.wepTxKeyIndex, isAdd)) {
697 return false;
698 }
Jimmy Chen1fea6572020-11-13 18:45:45 +0800699 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK)
Oscar Shuc2e87562023-07-07 02:21:41 +0000700 && !validatePassword(config.preSharedKey, isAdd, false, false)) {
Roshan Pius2dbf8912017-05-31 14:42:19 -0700701 return false;
702 }
Jimmy Chen1fea6572020-11-13 18:45:45 +0800703 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE)
Oscar Shuc2e87562023-07-07 02:21:41 +0000704 && !validatePassword(config.preSharedKey, isAdd, true, false)) {
Jimmy Chen1fea6572020-11-13 18:45:45 +0800705 return false;
Hai Shalom563fa312019-03-01 10:44:46 -0800706 }
Nate(Qiang) Jiang52c3cf92023-04-13 00:16:16 +0000707 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_WAPI_PSK)
Oscar Shuc2e87562023-07-07 02:21:41 +0000708 && !validatePassword(config.preSharedKey, isAdd, false, true)) {
Nate(Qiang) Jiang52c3cf92023-04-13 00:16:16 +0000709 return false;
710 }
Hai Shalomfa5409b2021-08-04 16:36:47 -0700711 if (!validateEnterpriseConfig(config, isAdd)) {
Hai Shalom518a3b02021-05-25 19:35:17 -0700712 return false;
713 }
714
Roshan Piuscd88cc62020-04-09 16:41:30 -0700715 // b/153435438: Added to deal with badly formed WifiConfiguration from apps.
716 if (config.preSharedKey != null && !config.needsPreSharedKey()) {
717 Log.e(TAG, "preSharedKey set with an invalid KeyMgmt, resetting KeyMgmt to WPA_PSK");
Jimmy Chend3a43472020-12-08 15:37:31 +0800718 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
Roshan Piuscd88cc62020-04-09 16:41:30 -0700719 }
Roshan Pius2dbf8912017-05-31 14:42:19 -0700720 if (!validateIpConfiguration(config.getIpConfiguration())) {
721 return false;
722 }
723 // TBD: Validate some enterprise params as well in the future here.
724 return true;
725 }
726
Roshan Piusd3c28bf2018-09-27 11:36:37 -0700727 private static boolean validateBssidPattern(
728 Pair<MacAddress, MacAddress> bssidPatternMatcher) {
729 if (bssidPatternMatcher == null) return true;
730 MacAddress baseAddress = bssidPatternMatcher.first;
731 MacAddress mask = bssidPatternMatcher.second;
732 if (baseAddress.getAddressType() != MacAddress.TYPE_UNICAST) {
733 Log.e(TAG, "validateBssidPatternMatcher failed : invalid base address: " + baseAddress);
734 return false;
735 }
Roshan Piusd7586612019-10-21 13:37:08 -0700736 if (mask.equals(ALL_ZEROS_MAC_ADDRESS)
737 && !baseAddress.equals(ALL_ZEROS_MAC_ADDRESS)) {
Roshan Piusd3c28bf2018-09-27 11:36:37 -0700738 Log.e(TAG, "validateBssidPatternMatcher failed : invalid mask/base: " + mask + "/"
739 + baseAddress);
740 return false;
741 }
Roshan Piusd3c28bf2018-09-27 11:36:37 -0700742 return true;
743 }
744
745 // TODO(b/113878056): Some of this is duplicated in {@link WifiNetworkConfigBuilder}.
746 // Merge them somehow?.
747 private static boolean isValidNetworkSpecifier(WifiNetworkSpecifier specifier) {
748 PatternMatcher ssidPatternMatcher = specifier.ssidPatternMatcher;
749 Pair<MacAddress, MacAddress> bssidPatternMatcher = specifier.bssidPatternMatcher;
750 if (ssidPatternMatcher == null || bssidPatternMatcher == null) {
751 return false;
752 }
753 if (ssidPatternMatcher.getPath() == null || bssidPatternMatcher.first == null
754 || bssidPatternMatcher.second == null) {
755 return false;
756 }
757 return true;
758 }
759
760 private static boolean isMatchNoneNetworkSpecifier(WifiNetworkSpecifier specifier) {
761 PatternMatcher ssidPatternMatcher = specifier.ssidPatternMatcher;
762 Pair<MacAddress, MacAddress> bssidPatternMatcher = specifier.bssidPatternMatcher;
763 if (ssidPatternMatcher.getType() != PatternMatcher.PATTERN_PREFIX
764 && ssidPatternMatcher.getPath().equals(MATCH_EMPTY_SSID_PATTERN_PATH)) {
765 return true;
766 }
767 if (bssidPatternMatcher.equals(MATCH_NONE_BSSID_PATTERN)) {
768 return true;
769 }
770 return false;
771 }
772
773 private static boolean isMatchAllNetworkSpecifier(WifiNetworkSpecifier specifier) {
774 PatternMatcher ssidPatternMatcher = specifier.ssidPatternMatcher;
775 Pair<MacAddress, MacAddress> bssidPatternMatcher = specifier.bssidPatternMatcher;
776 if (ssidPatternMatcher.match(MATCH_EMPTY_SSID_PATTERN_PATH)
777 && bssidPatternMatcher.equals(MATCH_ALL_BSSID_PATTERN)) {
778 return true;
779 }
780 return false;
781 }
782
Chalard Jean2c6d14d2021-05-12 22:22:06 +0900783 // TODO: b/177434707 calls inside same module are safe
784 @SuppressLint("NewApi")
785 private static int getBand(WifiNetworkSpecifier s) {
786 return s.getBand();
787 }
788
Roshan Piusd3c28bf2018-09-27 11:36:37 -0700789 /**
790 * Validate the configuration received from an external application inside
791 * {@link WifiNetworkSpecifier}.
792 *
793 * This method checks for the following parameters:
794 * 1. {@link WifiNetworkSpecifier#ssidPatternMatcher}
795 * 2. {@link WifiNetworkSpecifier#bssidPatternMatcher}
Chalard Jean2c6d14d2021-05-12 22:22:06 +0900796 * 3. {@link WifiNetworkSpecifier#getBand()}
797 * 4. {@link WifiConfiguration#SSID}
798 * 5. {@link WifiConfiguration#BSSID}
799 * 6. {@link WifiConfiguration#preSharedKey}
800 * 7. {@link WifiConfiguration#allowedKeyManagement}
801 * 8. {@link WifiConfiguration#allowedProtocols}
802 * 9. {@link WifiConfiguration#allowedAuthAlgorithms}
803 * 10. {@link WifiConfiguration#allowedGroupCiphers}
804 * 11. {@link WifiConfiguration#allowedPairwiseCiphers}
805 * 12. {@link WifiConfiguration#getIpConfiguration()}
Roshan Piusd3c28bf2018-09-27 11:36:37 -0700806 *
807 * @param specifier Instance of {@link WifiNetworkSpecifier}.
808 * @return true if the parameters are valid, false otherwise.
809 */
810 public static boolean validateNetworkSpecifier(WifiNetworkSpecifier specifier) {
811 if (!isValidNetworkSpecifier(specifier)) {
812 Log.e(TAG, "validateNetworkSpecifier failed : invalid network specifier");
813 return false;
814 }
815 if (isMatchNoneNetworkSpecifier(specifier)) {
816 Log.e(TAG, "validateNetworkSpecifier failed : match-none specifier");
817 return false;
818 }
819 if (isMatchAllNetworkSpecifier(specifier)) {
820 Log.e(TAG, "validateNetworkSpecifier failed : match-all specifier");
821 return false;
822 }
Chalard Jean2c6d14d2021-05-12 22:22:06 +0900823 if (!WifiNetworkSpecifier.validateBand(getBand(specifier))) {
824 return false;
825 }
Roshan Piusd3c28bf2018-09-27 11:36:37 -0700826 WifiConfiguration config = specifier.wifiConfiguration;
827 if (specifier.ssidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL) {
828 // For literal SSID matches, the value should satisfy SSID requirements.
829 // WifiConfiguration.SSID needs quotes around ASCII SSID.
830 if (!validateSsid(addEnclosingQuotes(specifier.ssidPatternMatcher.getPath()), true)) {
831 return false;
832 }
833 } else {
834 if (config.hiddenSSID) {
835 Log.e(TAG, "validateNetworkSpecifier failed : ssid pattern not supported "
836 + "for hidden networks");
837 return false;
838 }
839 }
840 if (Objects.equals(specifier.bssidPatternMatcher.second, MacAddress.BROADCAST_ADDRESS)) {
841 // For literal BSSID matches, the value should satisfy MAC address requirements.
842 if (!validateBssid(specifier.bssidPatternMatcher.first)) {
843 return false;
844 }
845 } else {
846 if (!validateBssidPattern(specifier.bssidPatternMatcher)) {
847 return false;
848 }
849 }
850 if (!validateBitSets(config)) {
851 return false;
852 }
853 if (!validateKeyMgmt(config.allowedKeyManagement)) {
854 return false;
855 }
Jimmy Chen1fea6572020-11-13 18:45:45 +0800856 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK)
Oscar Shuc2e87562023-07-07 02:21:41 +0000857 && !validatePassword(config.preSharedKey, true, false, false)) {
Roshan Piusd3c28bf2018-09-27 11:36:37 -0700858 return false;
859 }
Jimmy Chen1fea6572020-11-13 18:45:45 +0800860 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE)
Oscar Shuc2e87562023-07-07 02:21:41 +0000861 && !validatePassword(config.preSharedKey, true, true, false)) {
Jimmy Chen1fea6572020-11-13 18:45:45 +0800862 return false;
Hai Shalom563fa312019-03-01 10:44:46 -0800863 }
Roshan Piusd3c28bf2018-09-27 11:36:37 -0700864 // TBD: Validate some enterprise params as well in the future here.
865 return true;
866 }
867
Roshan Pius8e3836f2016-07-25 11:33:09 -0700868 /**
Ningyuan Wang721420a2017-05-08 13:34:52 -0700869 * Check if the provided two networks are the same.
Roshan Piusec327b72017-09-14 11:13:29 -0700870 * Note: This does not check if network selection BSSID's are the same.
Ningyuan Wang721420a2017-05-08 13:34:52 -0700871 *
Roshan Piusec327b72017-09-14 11:13:29 -0700872 * @param config Configuration corresponding to a network.
873 * @param config1 Configuration corresponding to another network.
Ningyuan Wang721420a2017-05-08 13:34:52 -0700874 *
875 * @return true if |config| and |config1| are the same network.
876 * false otherwise.
877 */
878 public static boolean isSameNetwork(WifiConfiguration config, WifiConfiguration config1) {
879 if (config == null && config1 == null) {
880 return true;
881 }
882 if (config == null || config1 == null) {
883 return false;
884 }
885 if (config.networkId != config1.networkId) {
886 return false;
887 }
888 if (!Objects.equals(config.SSID, config1.SSID)) {
889 return false;
890 }
Jimmy Chen01df6332021-08-05 15:40:53 +0800891 if (!Objects.equals(config.getNetworkSelectionStatus().getCandidateSecurityParams(),
892 config1.getNetworkSelectionStatus().getCandidateSecurityParams())) {
893 return false;
894 }
Ningyuan Wang721420a2017-05-08 13:34:52 -0700895 if (WifiConfigurationUtil.hasCredentialChanged(config, config1)) {
896 return false;
897 }
898 return true;
899 }
900
901 /**
Roshan Pius8e3836f2016-07-25 11:33:09 -0700902 * Create a PnoNetwork object from the provided WifiConfiguration.
903 *
904 * @param config Configuration corresponding to the network.
Roshan Pius8e3836f2016-07-25 11:33:09 -0700905 * @return PnoNetwork object corresponding to the network.
906 */
907 public static WifiScanner.PnoSettings.PnoNetwork createPnoNetwork(
Roshan Pius90814592018-03-21 15:52:15 -0700908 WifiConfiguration config) {
Roshan Pius8e3836f2016-07-25 11:33:09 -0700909 WifiScanner.PnoSettings.PnoNetwork pnoNetwork =
910 new WifiScanner.PnoSettings.PnoNetwork(config.SSID);
Roshan Pius8e3836f2016-07-25 11:33:09 -0700911 if (config.hiddenSSID) {
912 pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_DIRECTED_SCAN;
913 }
914 pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_A_BAND;
915 pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_G_BAND;
Jimmy Chen1fea6572020-11-13 18:45:45 +0800916 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK)) {
Roshan Pius8e3836f2016-07-25 11:33:09 -0700917 pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_PSK;
Jimmy Chen1fea6572020-11-13 18:45:45 +0800918 } else if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP)) {
Roshan Pius8e3836f2016-07-25 11:33:09 -0700919 pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_EAPOL;
920 } else {
921 pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_OPEN;
922 }
923 return pnoNetwork;
924 }
925
Jimmy Chen94fda172020-12-29 13:44:24 +0800926 private static void addOpenUpgradableSecurityTypeIfNecessary(WifiConfiguration config) {
927 if (!config.isSecurityType(WifiConfiguration.SECURITY_TYPE_OPEN)) return;
928 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_OWE)) return;
929
930 Log.d(TAG, "Add upgradable OWE configuration.");
931 SecurityParams oweParams = SecurityParams.createSecurityParamsBySecurityType(
932 WifiConfiguration.SECURITY_TYPE_OWE);
933 oweParams.setIsAddedByAutoUpgrade(true);
934 config.addSecurityParams(oweParams);
935 }
936
937 private static void addPskUpgradableSecurityTypeIfNecessary(WifiConfiguration config) {
938 if (!config.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK)) return;
939 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE)) return;
940
941 Log.d(TAG, "Add upgradable SAE configuration.");
942 SecurityParams saeParams = SecurityParams.createSecurityParamsBySecurityType(
943 WifiConfiguration.SECURITY_TYPE_SAE);
944 saeParams.setIsAddedByAutoUpgrade(true);
945 config.addSecurityParams(saeParams);
946 }
947
Jimmy Chen3a7ca7b2021-01-11 14:38:27 +0800948 private static void addEapUpgradableSecurityTypeIfNecessary(WifiConfiguration config) {
949 if (!config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP)) return;
950 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE)) return;
951
952 Log.d(TAG, "Add upgradable Enterprise configuration.");
953 SecurityParams wpa3EnterpriseParams = SecurityParams.createSecurityParamsBySecurityType(
954 WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE);
955 wpa3EnterpriseParams.setIsAddedByAutoUpgrade(true);
956 config.addSecurityParams(wpa3EnterpriseParams);
957 }
958
Jimmy Chen94fda172020-12-29 13:44:24 +0800959 /**
960 * Add upgradable securit type to the given wifi configuration.
961 *
962 * @param config the wifi configuration to be checked.
963 */
Hai Shalom518a3b02021-05-25 19:35:17 -0700964 public static boolean addUpgradableSecurityTypeIfNecessary(WifiConfiguration config) {
965 try {
966 addOpenUpgradableSecurityTypeIfNecessary(config);
967 addPskUpgradableSecurityTypeIfNecessary(config);
968 addEapUpgradableSecurityTypeIfNecessary(config);
969 } catch (IllegalArgumentException e) {
970 Log.e(TAG, "Failed to add upgradable security type");
971 return false;
972 }
973 return true;
Jimmy Chen94fda172020-12-29 13:44:24 +0800974 }
975
976 /**
977 * For a upgradable type which is added by the auto-upgrade mechenism, it is only
978 * matched when corresponding auto-upgrade features are enabled.
979 */
980 private static boolean shouldOmitAutoUpgradeParams(SecurityParams params) {
981 if (!params.isAddedByAutoUpgrade()) return false;
982
983 WifiGlobals wifiGlobals = WifiInjector.getInstance().getWifiGlobals();
984
985 if (params.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE)) {
986 return !wifiGlobals.isWpa3SaeUpgradeEnabled();
987 }
988 if (params.isSecurityType(WifiConfiguration.SECURITY_TYPE_OWE)) {
989 return !wifiGlobals.isOweUpgradeEnabled();
990 }
991 return false;
992 }
993
994 private static boolean isSecurityParamsSupported(SecurityParams params) {
995 final long wifiFeatures = WifiInjector.getInstance()
996 .getActiveModeWarden().getPrimaryClientModeManager()
997 .getSupportedFeatures();
998 switch (params.getSecurityType()) {
999 case WifiConfiguration.SECURITY_TYPE_SAE:
1000 return 0 != (wifiFeatures & WifiManager.WIFI_FEATURE_WPA3_SAE);
1001 case WifiConfiguration.SECURITY_TYPE_OWE:
1002 return 0 != (wifiFeatures & WifiManager.WIFI_FEATURE_OWE);
1003 }
1004 return true;
1005 }
1006
1007 /**
1008 * Check the security params is valid or not.
1009 * @param params the requesting security params.
1010 * @return true if it's valid; otherwise false.
1011 */
1012 public static boolean isSecurityParamsValid(SecurityParams params) {
1013 if (!params.isEnabled()) return false;
1014 if (!isSecurityParamsSupported(params)) return false;
Jimmy Chen94fda172020-12-29 13:44:24 +08001015 return true;
1016 }
Ningyuan Wang721420a2017-05-08 13:34:52 -07001017
Roshan Pius8e3836f2016-07-25 11:33:09 -07001018 /**
1019 * General WifiConfiguration list sorting algorithm:
1020 * 1, Place the fully enabled networks first.
1021 * 2. Next place all the temporarily disabled networks.
1022 * 3. Place the permanently disabled networks last (Permanently disabled networks are removed
Roshan Pius29091b82016-08-29 16:25:17 -07001023 * before WifiConfigManager uses this comparator today!).
Roshan Pius8e3836f2016-07-25 11:33:09 -07001024 *
1025 * Among the networks with the same status, sort them in the order determined by the return of
1026 * {@link #compareNetworksWithSameStatus(WifiConfiguration, WifiConfiguration)} method
1027 * implementation.
1028 */
1029 public abstract static class WifiConfigurationComparator implements
1030 Comparator<WifiConfiguration> {
1031 private static final int ENABLED_NETWORK_SCORE = 3;
1032 private static final int TEMPORARY_DISABLED_NETWORK_SCORE = 2;
1033 private static final int PERMANENTLY_DISABLED_NETWORK_SCORE = 1;
1034
1035 @Override
1036 public int compare(WifiConfiguration a, WifiConfiguration b) {
1037 int configAScore = getNetworkStatusScore(a);
1038 int configBScore = getNetworkStatusScore(b);
1039 if (configAScore == configBScore) {
1040 return compareNetworksWithSameStatus(a, b);
1041 } else {
1042 return Integer.compare(configBScore, configAScore);
1043 }
1044 }
1045
1046 // This needs to be implemented by the connected/disconnected PNO list comparator.
1047 abstract int compareNetworksWithSameStatus(WifiConfiguration a, WifiConfiguration b);
1048
1049 /**
1050 * Returns an integer representing a score for each configuration. The scores are assigned
1051 * based on the status of the configuration. The scores are assigned according to the order:
1052 * Fully enabled network > Temporarily disabled network > Permanently disabled network.
1053 */
1054 private int getNetworkStatusScore(WifiConfiguration config) {
1055 if (config.getNetworkSelectionStatus().isNetworkEnabled()) {
1056 return ENABLED_NETWORK_SCORE;
1057 } else if (config.getNetworkSelectionStatus().isNetworkTemporaryDisabled()) {
1058 return TEMPORARY_DISABLED_NETWORK_SCORE;
1059 } else {
1060 return PERMANENTLY_DISABLED_NETWORK_SCORE;
1061 }
1062 }
1063 }
Jimmy Chen513cdb42021-04-23 18:36:20 +08001064
1065 /**
1066 * Convert multi-type configurations to a list of configurations with a single security type,
1067 * where a configuration with multiple security configurations will be converted to multiple
1068 * Wi-Fi configurations with a single security type..
1069 *
1070 * @param configs the list of multi-type configurations.
1071 * @return a list of Wi-Fi configurations with a single security type,
1072 * that may contain multiple configurations with the same network ID.
1073 */
1074 public static List<WifiConfiguration> convertMultiTypeConfigsToLegacyConfigs(
1075 List<WifiConfiguration> configs) {
1076 List<WifiConfiguration> legacyConfigs = new ArrayList<>();
1077 for (WifiConfiguration config : configs) {
Quang Luongbe6a6a32021-07-02 16:44:25 -07001078 boolean wpa2EnterpriseAdded = false;
1079 WifiConfiguration wpa3EnterpriseConfig = null;
Jimmy Chen513cdb42021-04-23 18:36:20 +08001080 for (SecurityParams params: config.getSecurityParamsList()) {
1081 if (!params.isEnabled()) continue;
1082 if (shouldOmitAutoUpgradeParams(params)) continue;
1083 WifiConfiguration legacyConfig = new WifiConfiguration(config);
1084 legacyConfig.setSecurityParams(params);
Quang Luonge25b96e2021-07-01 16:15:32 -07001085 legacyConfig.networkId = addSecurityTypeToNetworkId(
1086 legacyConfig.networkId,
1087 params.getSecurityType());
Quang Luongbe6a6a32021-07-02 16:44:25 -07001088 int securityType = params.getSecurityType();
1089 if (securityType == SECURITY_TYPE_EAP) {
1090 wpa2EnterpriseAdded = true;
1091 } else if (securityType == SECURITY_TYPE_EAP_WPA3_ENTERPRISE) {
1092 wpa3EnterpriseConfig = legacyConfig;
1093 continue;
1094 }
Jimmy Chen513cdb42021-04-23 18:36:20 +08001095 legacyConfigs.add(legacyConfig);
1096 }
Quang Luongbe6a6a32021-07-02 16:44:25 -07001097 if (wpa3EnterpriseConfig != null && (SdkLevel.isAtLeastS() || !wpa2EnterpriseAdded)) {
1098 // R Wifi settings maps WPA3-Enterprise to the same security type as
1099 // WPA2-Enterprise, which causes a DuplicateKeyException. For R, we should only
1100 // return the WPA2-Enterprise config if we have both.
1101 legacyConfigs.add(wpa3EnterpriseConfig);
1102 }
Jimmy Chen513cdb42021-04-23 18:36:20 +08001103 }
1104 return legacyConfigs;
1105 }
1106
Quang Luonge25b96e2021-07-01 16:15:32 -07001107 /**
1108 * Converts WifiInfo.SecurityType to WifiConfiguration.SecurityType
1109 */
1110 public static
1111 @WifiConfiguration.SecurityType int convertWifiInfoSecurityTypeToWifiConfiguration(
1112 @WifiInfo.SecurityType int securityType) {
1113 switch (securityType) {
1114 case WifiInfo.SECURITY_TYPE_OPEN:
1115 return WifiConfiguration.SECURITY_TYPE_OPEN;
1116 case WifiInfo.SECURITY_TYPE_WEP:
1117 return WifiConfiguration.SECURITY_TYPE_WEP;
1118 case WifiInfo.SECURITY_TYPE_PSK:
1119 return WifiConfiguration.SECURITY_TYPE_PSK;
1120 case WifiInfo.SECURITY_TYPE_EAP:
1121 return WifiConfiguration.SECURITY_TYPE_EAP;
1122 case WifiInfo.SECURITY_TYPE_SAE:
1123 return WifiConfiguration.SECURITY_TYPE_SAE;
1124 case WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT:
1125 return WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT;
1126 case WifiInfo.SECURITY_TYPE_OWE:
1127 return WifiConfiguration.SECURITY_TYPE_OWE;
1128 case WifiInfo.SECURITY_TYPE_WAPI_PSK:
1129 return WifiConfiguration.SECURITY_TYPE_WAPI_PSK;
1130 case WifiInfo.SECURITY_TYPE_WAPI_CERT:
1131 return WifiConfiguration.SECURITY_TYPE_WAPI_CERT;
1132 case WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE:
1133 return WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE;
1134 case WifiInfo.SECURITY_TYPE_OSEN:
1135 return WifiConfiguration.SECURITY_TYPE_OSEN;
1136 case WifiInfo.SECURITY_TYPE_PASSPOINT_R1_R2:
1137 return WifiConfiguration.SECURITY_TYPE_PASSPOINT_R1_R2;
1138 case WifiInfo.SECURITY_TYPE_PASSPOINT_R3:
1139 return WifiConfiguration.SECURITY_TYPE_PASSPOINT_R3;
1140 default:
1141 return -1;
1142 }
1143 }
1144
1145 /**
1146 * Adds a WifiConfiguration.SecurityType value to a network ID to differentiate the network IDs
1147 * of legacy single-type configurations derived from the same multi-type configuration.
1148 *
1149 * This method only works for SDK levels less than S, since those callers may expect a unique
1150 * network ID for each single-type configuration. For SDK level S and above, this method returns
1151 * the network ID as-is.
1152 *
1153 * @param netId network id to add the security type to
1154 * @param securityType WifiConfiguration security type to encode
1155 * @return network id with security type encoded in it
1156 */
1157 public static int addSecurityTypeToNetworkId(
1158 int netId, @WifiConfiguration.SecurityType int securityType) {
1159 if (netId == INVALID_NETWORK_ID || SdkLevel.isAtLeastS()) {
1160 return netId;
1161 }
1162 return removeSecurityTypeFromNetworkId(netId)
1163 | ((securityType & NETWORK_ID_SECURITY_MASK) << NETWORK_ID_SECURITY_OFFSET);
1164 }
1165
1166 /**
1167 * Removes the security type value of a network ID to have it match with internal network IDs.
1168 *
1169 * This method only works for SDK levels less than S. It should be used on network ID passed in
1170 * from external callers, since those callers are be exposed to network IDs with an embedded
1171 * security type value. For SDK levels S and above, this method returns the network ID as-is.
1172 *
1173 * @param netId network id to remove the security type from
1174 * @return network id with the security type removed
1175 */
1176 public static int removeSecurityTypeFromNetworkId(int netId) {
1177 if (netId == INVALID_NETWORK_ID || SdkLevel.isAtLeastS()) {
1178 return netId;
1179 }
1180 return netId & ~(NETWORK_ID_SECURITY_MASK << NETWORK_ID_SECURITY_OFFSET);
1181 }
1182
Hai Shalomfa5409b2021-08-04 16:36:47 -07001183 private static boolean validateEnterpriseConfig(WifiConfiguration config, boolean isAdd) {
Hai Shalome105e182021-06-17 17:19:18 -07001184 if ((config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP)
1185 || config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE))
1186 && !config.isEnterprise()) {
1187 return false;
1188 }
1189 if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT)
1190 && (!config.isEnterprise()
1191 || config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.TLS)) {
1192 return false;
1193 }
Hai Shalome105e182021-06-17 17:19:18 -07001194 if (config.isEnterprise()) {
1195 if (config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.PEAP
1196 || config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TTLS) {
1197
1198 int phase2Method = config.enterpriseConfig.getPhase2Method();
1199 if (phase2Method == WifiEnterpriseConfig.Phase2.MSCHAP
1200 || phase2Method == WifiEnterpriseConfig.Phase2.MSCHAPV2
1201 || phase2Method == WifiEnterpriseConfig.Phase2.PAP
1202 || phase2Method == WifiEnterpriseConfig.Phase2.GTC) {
Hai Shalomfa5409b2021-08-04 16:36:47 -07001203 // Check the password on add only. When updating, the password may not be
1204 // available and it appears as "(Unchanged)" in Settings
1205 if ((isAdd && TextUtils.isEmpty(config.enterpriseConfig.getPassword()))
Hai Shalome105e182021-06-17 17:19:18 -07001206 || TextUtils.isEmpty(config.enterpriseConfig.getIdentity())) {
1207 Log.e(TAG, "Enterprise network without an identity or a password set");
1208 return false;
1209 }
1210 }
1211 }
1212 }
1213 return true;
1214 }
Bartosz Fabianowski82893b52016-02-12 10:30:55 +01001215}