| /* |
| * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. |
| * |
| * Permission to use, copy, modify, and/or distribute this software for |
| * any purpose with or without fee is hereby granted, provided that the |
| * above copyright notice and this permission notice appear in all |
| * copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
| * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| */ |
| /* |
| * DOC: contains scan cache filter logic |
| */ |
| |
| #include <wlan_scan_utils_api.h> |
| #include "wlan_scan_main.h" |
| #include "wlan_scan_cache_db_i.h" |
| |
| /** |
| * scm_is_open_security() - Check if scan entry support open security |
| * @filter: scan filter |
| * @db_entry: db entry |
| * @security: matched security. |
| * |
| * Return: true if open security else false |
| */ |
| static bool scm_is_open_security(struct scan_filter *filter, |
| struct scan_cache_entry *db_entry, |
| struct security_info *security) |
| { |
| bool match = false; |
| int i; |
| |
| if (db_entry->cap_info.wlan_caps.privacy) |
| return false; |
| |
| /* Check MC cipher and Auth type requested. */ |
| for (i = 0; i < filter->num_of_mc_enc_type; i++) { |
| if (WLAN_ENCRYPT_TYPE_NONE == |
| filter->mc_enc_type[i]) { |
| security->mc_enc = |
| filter->mc_enc_type[i]; |
| match = true; |
| break; |
| } |
| } |
| if (!match && filter->num_of_mc_enc_type) |
| return match; |
| |
| match = false; |
| /* Check Auth list. It should contain AuthOpen. */ |
| for (i = 0; i < filter->num_of_auth; i++) { |
| if ((WLAN_AUTH_TYPE_OPEN_SYSTEM == |
| filter->auth_type[i]) || |
| (WLAN_AUTH_TYPE_AUTOSWITCH == |
| filter->auth_type[i])) { |
| security->auth_type = |
| WLAN_AUTH_TYPE_OPEN_SYSTEM; |
| match = true; |
| break; |
| } |
| } |
| |
| return match; |
| } |
| |
| /** |
| * scm_is_cipher_match() - Check if cipher match the cipher list |
| * @cipher_list: cipher list to match |
| * @num_cipher: number of cipher in cipher list |
| * @cipher_to_match: cipher to found in cipher list |
| * |
| * Return: true if open security else false |
| */ |
| static bool scm_is_cipher_match( |
| uint32_t *cipher_list, |
| uint16_t num_cipher, uint32_t cipher_to_match) |
| { |
| int i; |
| bool match = false; |
| |
| for (i = 0; i < num_cipher ; i++) { |
| match = (cipher_list[i] == cipher_to_match); |
| if (match) |
| break; |
| } |
| |
| return match; |
| } |
| |
| /** |
| * scm_get_cipher_suite_type() - get cypher suite type from enc type |
| * @enc: enc type |
| * |
| * Return: cypher suite type |
| */ |
| static uint8_t scm_get_cipher_suite_type(enum wlan_enc_type enc) |
| { |
| uint8_t cipher_type; |
| |
| switch (enc) { |
| case WLAN_ENCRYPT_TYPE_WEP40: |
| case WLAN_ENCRYPT_TYPE_WEP40_STATICKEY: |
| cipher_type = WLAN_CSE_WEP40; |
| break; |
| case WLAN_ENCRYPT_TYPE_WEP104: |
| case WLAN_ENCRYPT_TYPE_WEP104_STATICKEY: |
| cipher_type = WLAN_CSE_WEP104; |
| break; |
| case WLAN_ENCRYPT_TYPE_TKIP: |
| cipher_type = WLAN_CSE_TKIP; |
| break; |
| case WLAN_ENCRYPT_TYPE_AES: |
| cipher_type = WLAN_CSE_CCMP; |
| break; |
| case WLAN_ENCRYPT_TYPE_AES_GCMP: |
| cipher_type = WLAN_CSE_GCMP_128; |
| break; |
| case WLAN_ENCRYPT_TYPE_AES_GCMP_256: |
| cipher_type = WLAN_CSE_GCMP_256; |
| break; |
| case WLAN_ENCRYPT_TYPE_NONE: |
| cipher_type = WLAN_CSE_NONE; |
| break; |
| case WLAN_ENCRYPT_TYPE_WPI: |
| cipher_type = WLAN_WAI_CERT_OR_SMS4; |
| break; |
| default: |
| cipher_type = WLAN_CSE_RESERVED; |
| break; |
| } |
| |
| return cipher_type; |
| } |
| |
| /** |
| * scm_is_wep_security() - Check if scan entry support WEP security |
| * @filter: scan filter |
| * @db_entry: db entry |
| * @security: matched security. |
| * |
| * Return: true if WEP security else false |
| */ |
| static bool scm_is_wep_security(struct scan_filter *filter, |
| struct scan_cache_entry *db_entry, |
| struct security_info *security) |
| { |
| int i; |
| QDF_STATUS status; |
| bool match = false; |
| enum wlan_auth_type neg_auth = WLAN_AUTH_TYPE_OPEN_SYSTEM; |
| enum wlan_enc_type neg_mccipher = WLAN_ENCRYPT_TYPE_NONE; |
| |
| if (!security) |
| return false; |
| |
| /* If privacy bit is not set, consider no match */ |
| if (!db_entry->cap_info.wlan_caps.privacy) |
| return false; |
| |
| for (i = 0; i < filter->num_of_mc_enc_type; i++) { |
| switch (filter->mc_enc_type[i]) { |
| case WLAN_ENCRYPT_TYPE_WEP40_STATICKEY: |
| case WLAN_ENCRYPT_TYPE_WEP104_STATICKEY: |
| case WLAN_ENCRYPT_TYPE_WEP40: |
| case WLAN_ENCRYPT_TYPE_WEP104: |
| /* |
| * Multicast list may contain WEP40/WEP104. |
| * Check whether it matches UC. |
| */ |
| if (security->uc_enc == |
| filter->mc_enc_type[i]) { |
| match = true; |
| neg_mccipher = |
| filter->mc_enc_type[i]; |
| } |
| break; |
| default: |
| match = false; |
| break; |
| } |
| if (match) |
| break; |
| } |
| |
| if (!match) |
| return match; |
| |
| for (i = 0; i < filter->num_of_auth; i++) { |
| switch (filter->auth_type[i]) { |
| case WLAN_AUTH_TYPE_OPEN_SYSTEM: |
| case WLAN_AUTH_TYPE_SHARED_KEY: |
| case WLAN_AUTH_TYPE_AUTOSWITCH: |
| match = true; |
| neg_auth = filter->auth_type[i]; |
| break; |
| default: |
| match = false; |
| } |
| if (match) |
| break; |
| } |
| |
| if (!match) |
| return match; |
| |
| /* |
| * In case of WPA / WPA2, check whether it supports WEP as well. |
| * Prepare the encryption type for WPA/WPA2 functions |
| */ |
| if (security->uc_enc == WLAN_ENCRYPT_TYPE_WEP40_STATICKEY) |
| security->uc_enc = WLAN_ENCRYPT_TYPE_WEP40; |
| else if (security->uc_enc == WLAN_ENCRYPT_TYPE_WEP104) |
| security->uc_enc = WLAN_ENCRYPT_TYPE_WEP104; |
| |
| /* else we can use the encryption type directly */ |
| if (util_scan_entry_wpa(db_entry)) { |
| struct wlan_wpa_ie wpa = {0}; |
| uint8_t cipher_type; |
| |
| cipher_type = |
| scm_get_cipher_suite_type(security->uc_enc); |
| status = wlan_parse_wpa_ie(util_scan_entry_wpa(db_entry), &wpa); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| scm_err("failed to parse WPA IE, status %d", status); |
| scm_hex_dump(QDF_TRACE_LEVEL_DEBUG, |
| util_scan_entry_wpa(db_entry), |
| util_scan_get_wpa_len(db_entry)); |
| return false; |
| } |
| |
| match = scm_is_cipher_match(&wpa.mc_cipher, |
| 1, WLAN_WPA_SEL(cipher_type)); |
| } |
| if (!match && util_scan_entry_rsn(db_entry)) { |
| struct wlan_rsn_ie rsn = {0}; |
| uint8_t cipher_type; |
| |
| cipher_type = |
| scm_get_cipher_suite_type(security->uc_enc); |
| status = wlan_parse_rsn_ie(util_scan_entry_rsn(db_entry), &rsn); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| scm_err("failed to parse RSN IE, status %d", status); |
| scm_hex_dump(QDF_TRACE_LEVEL_DEBUG, |
| util_scan_entry_rsn(db_entry), |
| util_scan_get_rsn_len(db_entry)); |
| return false; |
| } |
| match = scm_is_cipher_match(&rsn.gp_cipher_suite, |
| 1, WLAN_RSN_SEL(cipher_type)); |
| } |
| |
| |
| if (match) { |
| security->auth_type = neg_auth; |
| security->mc_enc = neg_mccipher; |
| } |
| |
| return match; |
| } |
| |
| /** |
| * scm_check_pmf_match() - Check PMF security of entry match filter |
| * @filter: scan filter |
| * @db_entry: ap entry |
| * @rsn: rsn IE of the scan entry |
| * |
| * Return: true if PMF security match else false |
| */ |
| static bool |
| scm_check_pmf_match(struct scan_filter *filter, |
| struct scan_cache_entry *db_entry, |
| struct wlan_rsn_ie *rsn) |
| { |
| enum wlan_pmf_cap ap_pmf_cap = WLAN_PMF_DISABLED; |
| bool match = true; |
| |
| if (rsn->cap & RSN_CAP_MFP_CAPABLE) |
| ap_pmf_cap = WLAN_PMF_CAPABLE; |
| if (rsn->cap & RSN_CAP_MFP_REQUIRED) |
| ap_pmf_cap = WLAN_PMF_REQUIRED; |
| |
| if ((filter->pmf_cap == WLAN_PMF_REQUIRED) && |
| (ap_pmf_cap == WLAN_PMF_DISABLED)) |
| match = false; |
| else if ((filter->pmf_cap == WLAN_PMF_DISABLED) && |
| (ap_pmf_cap == WLAN_PMF_REQUIRED)) |
| match = false; |
| |
| if (!match) |
| scm_debug("%pM : PMF cap didn't match (filter %d AP %d)", |
| db_entry->bssid.bytes, filter->pmf_cap, |
| ap_pmf_cap); |
| |
| return match; |
| } |
| |
| /** |
| * scm_is_rsn_mcast_cipher_match() - match the rsn mcast cipher type with AP's |
| * mcast cipher |
| * @rsn: AP's RSNE |
| * @filter: scan filter |
| * @neg_mccipher: negotiated mc cipher if matched. |
| * |
| * Return: true if mc cipher is negotiated |
| */ |
| static bool |
| scm_is_rsn_mcast_cipher_match(struct wlan_rsn_ie *rsn, |
| struct scan_filter *filter, enum wlan_enc_type *neg_mccipher) |
| { |
| int i; |
| bool match; |
| uint8_t cipher_type; |
| |
| if (!rsn || !neg_mccipher || !filter) |
| return false; |
| |
| for (i = 0; i < filter->num_of_mc_enc_type; i++) { |
| |
| if (filter->mc_enc_type[i] == WLAN_ENCRYPT_TYPE_ANY) { |
| /* Try the more secured ones first. */ |
| /* Check GCMP_256 first */ |
| cipher_type = WLAN_CSE_GCMP_256; |
| match = scm_is_cipher_match(&rsn->gp_cipher_suite, 1, |
| WLAN_RSN_SEL(cipher_type)); |
| if (match) { |
| *neg_mccipher = WLAN_ENCRYPT_TYPE_AES_GCMP_256; |
| return true; |
| } |
| /* Check GCMP */ |
| cipher_type = WLAN_CSE_GCMP_128; |
| match = scm_is_cipher_match(&rsn->gp_cipher_suite, 1, |
| WLAN_RSN_SEL(cipher_type)); |
| if (match) { |
| *neg_mccipher = WLAN_ENCRYPT_TYPE_AES_GCMP; |
| return true; |
| } |
| /* Check AES */ |
| cipher_type = WLAN_CSE_CCMP; |
| match = scm_is_cipher_match(&rsn->gp_cipher_suite, 1, |
| WLAN_RSN_SEL(cipher_type)); |
| if (match) { |
| *neg_mccipher = WLAN_ENCRYPT_TYPE_AES; |
| return true; |
| } |
| /* Check TKIP */ |
| cipher_type = WLAN_CSE_TKIP; |
| match = scm_is_cipher_match(&rsn->gp_cipher_suite, 1, |
| WLAN_RSN_SEL(cipher_type)); |
| if (match) { |
| *neg_mccipher = WLAN_ENCRYPT_TYPE_TKIP; |
| return true; |
| } |
| } else { |
| cipher_type = |
| scm_get_cipher_suite_type(filter->mc_enc_type[i]); |
| match = scm_is_cipher_match(&rsn->gp_cipher_suite, 1, |
| WLAN_RSN_SEL(cipher_type)); |
| if (match) { |
| *neg_mccipher = filter->mc_enc_type[i]; |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * scm_is_rsn_security() - Check if scan entry support RSN security |
| * @filter: scan filter |
| * @db_entry: db entry |
| * @security: matched security. |
| * |
| * Return: true if RSN security else false |
| */ |
| static bool scm_is_rsn_security(struct scan_filter *filter, |
| struct scan_cache_entry *db_entry, |
| struct security_info *security) |
| { |
| int i; |
| uint8_t cipher_type; |
| bool match_any_akm, match = false; |
| enum wlan_auth_type neg_auth = WLAN_NUM_OF_SUPPORT_AUTH_TYPE; |
| enum wlan_enc_type neg_mccipher = WLAN_ENCRYPT_TYPE_NONE; |
| struct wlan_rsn_ie rsn = {0}; |
| QDF_STATUS status; |
| |
| if (!security) |
| return false; |
| if (!util_scan_entry_rsn(db_entry)) { |
| scm_debug("%pM : doesn't have RSN IE", db_entry->bssid.bytes); |
| return false; |
| } |
| status = wlan_parse_rsn_ie(util_scan_entry_rsn(db_entry), &rsn); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| scm_err("failed to parse RSN IE, status %d", status); |
| scm_hex_dump(QDF_TRACE_LEVEL_DEBUG, |
| util_scan_entry_rsn(db_entry), |
| util_scan_get_rsn_len(db_entry)); |
| return false; |
| } |
| |
| cipher_type = |
| scm_get_cipher_suite_type(security->uc_enc); |
| match = scm_is_cipher_match(rsn.pwise_cipher_suites, |
| rsn.pwise_cipher_count, WLAN_RSN_SEL(cipher_type)); |
| if (!match) { |
| scm_debug("%pM : pairwise cipher didn't match", |
| db_entry->bssid.bytes); |
| return false; |
| } |
| |
| match = scm_is_rsn_mcast_cipher_match(&rsn, filter, &neg_mccipher); |
| if (!match) { |
| scm_debug("%pM : mcast cipher didn't match", |
| db_entry->bssid.bytes); |
| return false; |
| } |
| |
| /* Initializing with false as it has true value already */ |
| match = false; |
| for (i = 0; i < filter->num_of_auth; i++) { |
| |
| if (filter->auth_type[i] == WLAN_AUTH_TYPE_ANY) |
| match_any_akm = true; |
| else |
| match_any_akm = false; |
| /* |
| * Ciphers are supported, Match authentication algorithm and |
| * pick first matching authtype. |
| */ |
| if (scm_is_cipher_match(rsn.akm_suites, |
| rsn.akm_suite_count, |
| WLAN_RSN_SEL(WLAN_AKM_FILS_FT_SHA384))) { |
| if (match_any_akm || (WLAN_AUTH_TYPE_FT_FILS_SHA384 == |
| filter->auth_type[i])) { |
| neg_auth = WLAN_AUTH_TYPE_FT_FILS_SHA384; |
| match = true; |
| break; |
| } |
| } |
| if (scm_is_cipher_match(rsn.akm_suites, |
| rsn.akm_suite_count, |
| WLAN_RSN_SEL(WLAN_AKM_FILS_FT_SHA256))) { |
| if (match_any_akm || (WLAN_AUTH_TYPE_FT_FILS_SHA256 == |
| filter->auth_type[i])) { |
| neg_auth = WLAN_AUTH_TYPE_FT_FILS_SHA256; |
| match = true; |
| break; |
| } |
| } |
| if (scm_is_cipher_match(rsn.akm_suites, |
| rsn.akm_suite_count, |
| WLAN_RSN_SEL(WLAN_AKM_FILS_SHA384))) { |
| if (match_any_akm || (WLAN_AUTH_TYPE_FILS_SHA384 == |
| filter->auth_type[i])) { |
| neg_auth = WLAN_AUTH_TYPE_FILS_SHA384; |
| match = true; |
| break; |
| } |
| } |
| if (scm_is_cipher_match(rsn.akm_suites, |
| rsn.akm_suite_count, |
| WLAN_RSN_SEL(WLAN_AKM_FILS_SHA256))) { |
| if (match_any_akm || (WLAN_AUTH_TYPE_FILS_SHA256 == |
| filter->auth_type[i])) { |
| neg_auth = WLAN_AUTH_TYPE_FILS_SHA256; |
| match = true; |
| break; |
| } |
| } |
| |
| if (scm_is_cipher_match(rsn.akm_suites, |
| rsn.akm_suite_count, |
| WLAN_RSN_SEL(WLAN_AKM_SAE))) { |
| if (match_any_akm || (WLAN_AUTH_TYPE_SAE == |
| filter->auth_type[i])) { |
| neg_auth = WLAN_AUTH_TYPE_SAE; |
| match = true; |
| break; |
| } |
| } |
| |
| if (scm_is_cipher_match(rsn.akm_suites, |
| rsn.akm_suite_count, WLAN_RSN_DPP_AKM)) { |
| if (match_any_akm || (WLAN_AUTH_TYPE_DPP_RSN == |
| filter->auth_type[i])) { |
| neg_auth = WLAN_AUTH_TYPE_DPP_RSN; |
| match = true; |
| break; |
| } |
| } |
| if (scm_is_cipher_match(rsn.akm_suites, |
| rsn.akm_suite_count, |
| WLAN_RSN_OSEN_AKM)) { |
| if (match_any_akm || |
| WLAN_AUTH_TYPE_OSEN == filter->auth_type[i]) { |
| neg_auth = WLAN_AUTH_TYPE_OSEN; |
| match = true; |
| break; |
| } |
| } |
| if (scm_is_cipher_match(rsn.akm_suites, |
| rsn.akm_suite_count, |
| WLAN_RSN_SEL(WLAN_AKM_OWE))) { |
| if (match_any_akm || (WLAN_AUTH_TYPE_OWE == |
| filter->auth_type[i])) { |
| neg_auth = WLAN_AUTH_TYPE_OWE; |
| match = true; |
| break; |
| } |
| } |
| if (scm_is_cipher_match(rsn.akm_suites, |
| rsn.akm_suite_count, |
| WLAN_RSN_SEL(WLAN_AKM_FT_IEEE8021X))) { |
| if (match_any_akm || (WLAN_AUTH_TYPE_FT_RSN == |
| filter->auth_type[i])) { |
| neg_auth = WLAN_AUTH_TYPE_FT_RSN; |
| match = true; |
| break; |
| } |
| } |
| |
| if (scm_is_cipher_match(rsn.akm_suites, |
| rsn.akm_suite_count, |
| WLAN_RSN_SEL(WLAN_AKM_FT_PSK))) { |
| if (match_any_akm || (WLAN_AUTH_TYPE_FT_RSN_PSK == |
| filter->auth_type[i])) { |
| neg_auth = WLAN_AUTH_TYPE_FT_RSN_PSK; |
| match = true; |
| break; |
| } |
| } |
| /* ESE only supports 802.1X. No PSK. */ |
| if (scm_is_cipher_match(rsn.akm_suites, |
| rsn.akm_suite_count, |
| WLAN_RSN_CCKM_AKM)) { |
| if (match_any_akm || (WLAN_AUTH_TYPE_CCKM_RSN == |
| filter->auth_type[i])) { |
| neg_auth = WLAN_AUTH_TYPE_CCKM_RSN; |
| match = true; |
| break; |
| } |
| } |
| /* RSN */ |
| if (scm_is_cipher_match(rsn.akm_suites, |
| rsn.akm_suite_count, |
| WLAN_RSN_SEL(WLAN_AKM_IEEE8021X))) { |
| if (match_any_akm || (WLAN_AUTH_TYPE_RSN == |
| filter->auth_type[i])) { |
| neg_auth = WLAN_AUTH_TYPE_RSN; |
| match = true; |
| break; |
| } |
| } |
| /* TKIP */ |
| if (scm_is_cipher_match(rsn.akm_suites, |
| rsn.akm_suite_count, |
| WLAN_RSN_SEL(WLAN_AKM_PSK))) { |
| if (match_any_akm || (WLAN_AUTH_TYPE_RSN_PSK == |
| filter->auth_type[i])) { |
| neg_auth = WLAN_AUTH_TYPE_RSN_PSK; |
| match = true; |
| break; |
| } |
| } |
| /* SHA256 */ |
| if (scm_is_cipher_match(rsn.akm_suites, |
| rsn.akm_suite_count, |
| WLAN_RSN_SEL(WLAN_AKM_SHA256_PSK))) { |
| if (match_any_akm || (WLAN_AUTH_TYPE_RSN_PSK_SHA256 == |
| filter->auth_type[i])) { |
| neg_auth = |
| WLAN_AUTH_TYPE_RSN_PSK_SHA256; |
| match = true; |
| break; |
| } |
| } |
| /* 8021X SHA256 */ |
| if (scm_is_cipher_match(rsn.akm_suites, |
| rsn.akm_suite_count, |
| WLAN_RSN_SEL(WLAN_AKM_SHA256_IEEE8021X))) { |
| if (match_any_akm || (WLAN_AUTH_TYPE_RSN_8021X_SHA256 == |
| filter->auth_type[i])) { |
| neg_auth = |
| WLAN_AUTH_TYPE_RSN_8021X_SHA256; |
| match = true; |
| break; |
| } |
| } |
| if (scm_is_cipher_match(rsn.akm_suites, |
| rsn.akm_suite_count, |
| WLAN_RSN_SEL(WLAN_AKM_SUITEB_EAP_SHA256))) { |
| if (match_any_akm || |
| (WLAN_AUTH_TYPE_SUITEB_EAP_SHA256 == |
| filter->auth_type[i])) { |
| neg_auth = WLAN_AUTH_TYPE_SUITEB_EAP_SHA256; |
| match = true; |
| break; |
| } |
| } |
| if (scm_is_cipher_match(rsn.akm_suites, |
| rsn.akm_suite_count, |
| WLAN_RSN_SEL(WLAN_AKM_SUITEB_EAP_SHA384))) { |
| if (match_any_akm || |
| (WLAN_AUTH_TYPE_SUITEB_EAP_SHA384 == |
| filter->auth_type[i])) { |
| neg_auth = WLAN_AUTH_TYPE_SUITEB_EAP_SHA384; |
| match = true; |
| break; |
| } |
| } |
| |
| if (scm_is_cipher_match(rsn.akm_suites, rsn.akm_suite_count, |
| WLAN_RSN_SEL(WLAN_AKM_FT_SAE))) { |
| if (match_any_akm || (WLAN_AUTH_TYPE_FT_SAE == |
| filter->auth_type[i])) { |
| neg_auth = WLAN_AUTH_TYPE_FT_SAE; |
| match = true; |
| break; |
| } |
| } |
| |
| if (scm_is_cipher_match(rsn.akm_suites, rsn.akm_suite_count, |
| WLAN_RSN_SEL( |
| WLAN_AKM_FT_SUITEB_EAP_SHA384))) { |
| if (match_any_akm || |
| (WLAN_AUTH_TYPE_FT_SUITEB_EAP_SHA384 == |
| filter->auth_type[i])) { |
| neg_auth = WLAN_AUTH_TYPE_FT_SUITEB_EAP_SHA384; |
| match = true; |
| break; |
| } |
| } |
| } |
| |
| if (!match) { |
| scm_debug("%pM : akm suites didn't match", |
| db_entry->bssid.bytes); |
| return false; |
| } |
| |
| if (!filter->ignore_pmf_cap) |
| match = scm_check_pmf_match(filter, db_entry, &rsn); |
| |
| if (match) { |
| security->auth_type = neg_auth; |
| security->mc_enc = neg_mccipher; |
| } |
| |
| return match; |
| } |
| |
| /** |
| * scm_is_wpa_mcast_cipher_match() - match the wpa mcast cipher type with AP's |
| * mcast cipher |
| * @wpa: AP's WPA IE |
| * @filter: scan filter |
| * @neg_mccipher: negotiated mc cipher if matched. |
| * |
| * Return: true if mc cipher is negotiated |
| */ |
| static bool |
| scm_is_wpa_mcast_cipher_match(struct wlan_wpa_ie *wpa, |
| struct scan_filter *filter, enum wlan_enc_type *neg_mccipher) |
| { |
| int i; |
| bool match; |
| uint8_t cipher_type; |
| |
| if (!wpa || !neg_mccipher || !filter) |
| return false; |
| |
| for (i = 0; i < filter->num_of_mc_enc_type; i++) { |
| |
| if (filter->mc_enc_type[i] == WLAN_ENCRYPT_TYPE_ANY) { |
| /* Try the more secured ones first. */ |
| |
| /* Check AES */ |
| cipher_type = WLAN_CSE_CCMP; |
| match = scm_is_cipher_match(&wpa->mc_cipher, 1, |
| WLAN_WPA_SEL(cipher_type)); |
| if (match) { |
| *neg_mccipher = WLAN_ENCRYPT_TYPE_AES; |
| return true; |
| } |
| /* Check TKIP */ |
| cipher_type = WLAN_CSE_TKIP; |
| match = scm_is_cipher_match(&wpa->mc_cipher, 1, |
| WLAN_WPA_SEL(cipher_type)); |
| if (match) { |
| *neg_mccipher = WLAN_ENCRYPT_TYPE_TKIP; |
| return true; |
| } |
| } else { |
| cipher_type = |
| scm_get_cipher_suite_type(filter->mc_enc_type[i]); |
| match = scm_is_cipher_match(&wpa->mc_cipher, 1, |
| WLAN_WPA_SEL(cipher_type)); |
| if (match) { |
| *neg_mccipher = filter->mc_enc_type[i]; |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * scm_is_wpa_security() - Check if scan entry support WPA security |
| * @filter: scan filter |
| * @db_entry: db entry |
| * @security: matched security. |
| * |
| * Return: true if WPA security else false |
| */ |
| static bool scm_is_wpa_security(struct scan_filter *filter, |
| struct scan_cache_entry *db_entry, |
| struct security_info *security) |
| { |
| int i; |
| QDF_STATUS status; |
| uint8_t cipher_type; |
| bool match_any_akm, match = false; |
| enum wlan_auth_type neg_auth = WLAN_NUM_OF_SUPPORT_AUTH_TYPE; |
| enum wlan_enc_type neg_mccipher = WLAN_ENCRYPT_TYPE_NONE; |
| struct wlan_wpa_ie wpa = {0}; |
| |
| if (!security) |
| return false; |
| if (!util_scan_entry_wpa(db_entry)) { |
| scm_debug("%pM : AP doesn't have WPA IE", |
| db_entry->bssid.bytes); |
| return false; |
| } |
| |
| status = wlan_parse_wpa_ie(util_scan_entry_wpa(db_entry), &wpa); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| scm_err("failed to parse WPA IE, status %d", status); |
| scm_hex_dump(QDF_TRACE_LEVEL_DEBUG, |
| util_scan_entry_wpa(db_entry), |
| util_scan_get_wpa_len(db_entry)); |
| return false; |
| } |
| |
| cipher_type = |
| scm_get_cipher_suite_type(security->uc_enc); |
| match = scm_is_cipher_match(wpa.uc_ciphers, |
| wpa.uc_cipher_count, WLAN_WPA_SEL(cipher_type)); |
| if (!match) { |
| scm_debug("%pM : unicase cipher didn't match", |
| db_entry->bssid.bytes); |
| return false; |
| } |
| |
| match = scm_is_wpa_mcast_cipher_match(&wpa, filter, &neg_mccipher); |
| if (!match) { |
| scm_debug("%pM : mcast cipher didn't match", |
| db_entry->bssid.bytes); |
| return false; |
| } |
| |
| /* Initializing with false as it has true value already */ |
| match = false; |
| for (i = 0; i < filter->num_of_auth; i++) { |
| |
| if (filter->auth_type[i] == WLAN_AUTH_TYPE_ANY) |
| match_any_akm = true; |
| else |
| match_any_akm = false; |
| /* |
| * Ciphers are supported, Match authentication algorithm and |
| * pick first matching authtype. |
| */ |
| /**/ |
| if (scm_is_cipher_match(wpa.auth_suites, |
| wpa.auth_suite_count, |
| WLAN_WPA_SEL(WLAN_AKM_IEEE8021X))) { |
| if (match_any_akm || (WLAN_AUTH_TYPE_WPA == |
| filter->auth_type[i])) { |
| neg_auth = WLAN_AUTH_TYPE_WPA; |
| match = true; |
| break; |
| } |
| } |
| if (scm_is_cipher_match(wpa.auth_suites, |
| wpa.auth_suite_count, |
| WLAN_WPA_SEL(WLAN_AKM_PSK))) { |
| if (match_any_akm || (WLAN_AUTH_TYPE_WPA_PSK == |
| filter->auth_type[i])) { |
| neg_auth = WLAN_AUTH_TYPE_WPA_PSK; |
| match = true; |
| break; |
| } |
| } |
| if (scm_is_cipher_match(wpa.auth_suites, |
| wpa.auth_suite_count, |
| WLAN_WPA_CCKM_AKM)) { |
| if (match_any_akm || (WLAN_AUTH_TYPE_CCKM_WPA == |
| filter->auth_type[i])) { |
| neg_auth = WLAN_AUTH_TYPE_CCKM_WPA; |
| match = true; |
| break; |
| } |
| } |
| } |
| |
| if (!match) |
| scm_debug("%pM : akm didn't match", db_entry->bssid.bytes); |
| |
| if (match) { |
| security->auth_type = neg_auth; |
| security->mc_enc = neg_mccipher; |
| } |
| |
| return match; |
| } |
| |
| /** |
| * scm_is_wapi_security() - Check if scan entry support WAPI security |
| * @filter: scan filter |
| * @db_entry: db entry |
| * @security: matched security. |
| * |
| * Return: true if WAPI security else false |
| */ |
| static bool scm_is_wapi_security(struct scan_filter *filter, |
| struct scan_cache_entry *db_entry, |
| struct security_info *security) |
| { |
| int i; |
| uint8_t cipher_type; |
| bool match = false; |
| enum wlan_auth_type neg_auth = WLAN_NUM_OF_SUPPORT_AUTH_TYPE; |
| enum wlan_enc_type neg_mccipher = WLAN_ENCRYPT_TYPE_NONE; |
| struct wlan_wapi_ie wapi = {0}; |
| |
| if (!security) |
| return false; |
| if (!util_scan_entry_wapi(db_entry)) { |
| scm_debug("%pM : mcast cipher didn't match", |
| db_entry->bssid.bytes); |
| return false; |
| } |
| |
| wlan_parse_wapi_ie( |
| util_scan_entry_wapi(db_entry), &wapi); |
| |
| cipher_type = |
| scm_get_cipher_suite_type(security->uc_enc); |
| match = scm_is_cipher_match(wapi.uc_cipher_suites, |
| wapi.uc_cipher_count, WLAN_WAPI_SEL(cipher_type)); |
| if (!match) { |
| scm_debug("%pM : unicast cipher didn't match", |
| db_entry->bssid.bytes); |
| return false; |
| } |
| |
| for (i = 0; i < filter->num_of_mc_enc_type; i++) { |
| cipher_type = |
| scm_get_cipher_suite_type( |
| filter->mc_enc_type[i]); |
| match = scm_is_cipher_match(&wapi.mc_cipher_suite, |
| 1, WLAN_WAPI_SEL(cipher_type)); |
| if (match) |
| break; |
| } |
| if (!match) { |
| scm_debug("%pM : mcast cipher didn't match", |
| db_entry->bssid.bytes); |
| return false; |
| } |
| neg_mccipher = filter->mc_enc_type[i]; |
| |
| if (scm_is_cipher_match(wapi.akm_suites, |
| wapi.akm_suite_count, |
| WLAN_WAPI_SEL(WLAN_WAI_CERT_OR_SMS4))) { |
| neg_auth = |
| WLAN_AUTH_TYPE_WAPI_WAI_CERTIFICATE; |
| } else if (scm_is_cipher_match(wapi.akm_suites, |
| wapi.akm_suite_count, WLAN_WAPI_SEL(WLAN_WAI_PSK))) { |
| neg_auth = WLAN_AUTH_TYPE_WAPI_WAI_PSK; |
| } else { |
| scm_debug("%pM : akm is not supported", |
| db_entry->bssid.bytes); |
| return false; |
| } |
| |
| match = false; |
| for (i = 0; i < filter->num_of_auth; i++) { |
| if (filter->auth_type[i] == neg_auth) { |
| match = true; |
| break; |
| } |
| } |
| |
| if (!match) |
| scm_debug("%pM : akm suite didn't match", |
| db_entry->bssid.bytes); |
| if (match) { |
| security->auth_type = neg_auth; |
| security->mc_enc = neg_mccipher; |
| } |
| |
| return match; |
| } |
| |
| /** |
| * scm_is_def_security() - Check if any security in filter match |
| * @filter: scan filter |
| * @db_entry: db entry |
| * @security: matched security. |
| * |
| * Return: true if any security else false |
| */ |
| static bool scm_is_def_security(struct scan_filter *filter, |
| struct scan_cache_entry *db_entry, |
| struct security_info *security) |
| { |
| |
| /* It is allowed to match anything. Try the more secured ones first. */ |
| /* Check GCMP_256 first */ |
| security->uc_enc = WLAN_ENCRYPT_TYPE_AES_GCMP_256; |
| if (scm_is_rsn_security(filter, db_entry, security)) |
| return true; |
| |
| /* Check GCMP */ |
| security->uc_enc = WLAN_ENCRYPT_TYPE_AES_GCMP; |
| if (scm_is_rsn_security(filter, db_entry, security)) |
| return true; |
| |
| /* Check AES */ |
| security->uc_enc = WLAN_ENCRYPT_TYPE_AES; |
| if (scm_is_rsn_security(filter, db_entry, security)) |
| return true; |
| if (scm_is_wpa_security(filter, db_entry, security)) |
| return true; |
| |
| /* Check TKIP */ |
| security->uc_enc = WLAN_ENCRYPT_TYPE_TKIP; |
| if (scm_is_rsn_security(filter, db_entry, security)) |
| return true; |
| if (scm_is_wpa_security(filter, db_entry, security)) |
| return true; |
| |
| /* Check AES */ |
| security->uc_enc = WLAN_ENCRYPT_TYPE_AES; |
| if (scm_is_wpa_security(filter, db_entry, security)) |
| return true; |
| |
| /* Check TKIP */ |
| security->uc_enc = WLAN_ENCRYPT_TYPE_TKIP; |
| if (scm_is_wpa_security(filter, db_entry, security)) |
| return true; |
| |
| /* Check WAPI */ |
| security->uc_enc = WLAN_ENCRYPT_TYPE_WPI; |
| if (scm_is_wapi_security(filter, db_entry, security)) |
| return true; |
| |
| security->uc_enc = WLAN_ENCRYPT_TYPE_WEP104; |
| if (scm_is_wep_security(filter, db_entry, security)) |
| return true; |
| security->uc_enc = WLAN_ENCRYPT_TYPE_WEP40; |
| if (scm_is_wep_security(filter, db_entry, security)) |
| return true; |
| security->uc_enc = WLAN_ENCRYPT_TYPE_WEP104_STATICKEY; |
| if (scm_is_wep_security(filter, db_entry, security)) |
| return true; |
| security->uc_enc = WLAN_ENCRYPT_TYPE_WEP40_STATICKEY; |
| if (scm_is_wep_security(filter, db_entry, security)) |
| return true; |
| |
| /* It must be open and no enc */ |
| if (db_entry->cap_info.wlan_caps.privacy) |
| return false; |
| |
| security->auth_type = WLAN_AUTH_TYPE_OPEN_SYSTEM; |
| security->mc_enc = WLAN_ENCRYPT_TYPE_NONE; |
| security->uc_enc = WLAN_ENCRYPT_TYPE_NONE; |
| |
| return true; |
| } |
| |
| /** |
| * scm_is_fils_config_match() - Check if FILS config matches |
| * @filter: scan filter |
| * @db_entry: db entry |
| * |
| * Return: true if FILS config matches else false |
| */ |
| static bool scm_is_fils_config_match(struct scan_filter *filter, |
| struct scan_cache_entry *db_entry) |
| { |
| int i; |
| struct fils_indication_ie *indication_ie; |
| uint8_t *data; |
| |
| if (!filter->fils_scan_filter.realm_check) |
| return true; |
| |
| if (!db_entry->ie_list.fils_indication) |
| return false; |
| |
| |
| indication_ie = |
| (struct fils_indication_ie *) db_entry->ie_list.fils_indication; |
| |
| data = indication_ie->variable_data; |
| if (indication_ie->is_cache_id_present) |
| data += CACHE_IDENTIFIER_LEN; |
| |
| if (indication_ie->is_hessid_present) |
| data += HESSID_LEN; |
| |
| for (i = 1; i <= indication_ie->realm_identifiers_cnt; i++) { |
| if (!qdf_mem_cmp(filter->fils_scan_filter.fils_realm, |
| data, REAM_HASH_LEN)) |
| return true; |
| /* Max realm count reached */ |
| if (indication_ie->realm_identifiers_cnt == i) |
| break; |
| else |
| data = data + REAM_HASH_LEN; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * scm_is_security_match() - Check if security in filter match |
| * @filter: scan filter |
| * @db_entry: db entry |
| * @security: matched security. |
| * |
| * Return: true if security match else false |
| */ |
| static bool scm_is_security_match(struct scan_filter *filter, |
| struct scan_cache_entry *db_entry, |
| struct security_info *security) |
| { |
| int i; |
| bool match = false; |
| struct security_info local_security = {0}; |
| |
| if (!filter->num_of_enc_type) |
| return true; |
| |
| for (i = 0; (i < filter->num_of_enc_type) && |
| !match; i++) { |
| |
| local_security.uc_enc = |
| filter->enc_type[i]; |
| |
| switch (filter->enc_type[i]) { |
| case WLAN_ENCRYPT_TYPE_NONE: |
| match = scm_is_open_security(filter, |
| db_entry, &local_security); |
| break; |
| case WLAN_ENCRYPT_TYPE_WEP40_STATICKEY: |
| case WLAN_ENCRYPT_TYPE_WEP104_STATICKEY: |
| case WLAN_ENCRYPT_TYPE_WEP40: |
| case WLAN_ENCRYPT_TYPE_WEP104: |
| match = scm_is_wep_security(filter, |
| db_entry, &local_security); |
| break; |
| case WLAN_ENCRYPT_TYPE_TKIP: |
| case WLAN_ENCRYPT_TYPE_AES: |
| case WLAN_ENCRYPT_TYPE_AES_GCMP: |
| case WLAN_ENCRYPT_TYPE_AES_GCMP_256: |
| /* First check if there is a RSN match */ |
| match = scm_is_rsn_security(filter, |
| db_entry, &local_security); |
| /* If not RSN, then check WPA match */ |
| if (!match) |
| match = scm_is_wpa_security(filter, |
| db_entry, &local_security); |
| break; |
| case WLAN_ENCRYPT_TYPE_WPI:/* WAPI */ |
| match = scm_is_wapi_security(filter, |
| db_entry, &local_security); |
| break; |
| case WLAN_ENCRYPT_TYPE_ANY: |
| default: |
| match = scm_is_def_security(filter, |
| db_entry, &local_security); |
| break; |
| } |
| } |
| |
| if (match && security) |
| qdf_mem_copy(security, |
| &local_security, sizeof(*security)); |
| |
| return match; |
| } |
| |
| bool scm_filter_match(struct wlan_objmgr_psoc *psoc, |
| struct scan_cache_entry *db_entry, |
| struct scan_filter *filter, |
| struct security_info *security) |
| { |
| int i; |
| bool match = false; |
| struct roam_filter_params *roam_params; |
| struct scan_default_params *def_param; |
| struct wlan_country_ie *cc_ie; |
| |
| def_param = wlan_scan_psoc_get_def_params(psoc); |
| if (!def_param) |
| return false; |
| |
| roam_params = &def_param->roam_params; |
| |
| if (filter->p2p_results && !db_entry->is_p2p) |
| return false; |
| |
| for (i = 0; i < roam_params->num_bssid_avoid_list; i++) { |
| if (qdf_is_macaddr_equal(&roam_params->bssid_avoid_list[i], |
| &db_entry->bssid)) { |
| scm_debug("%pM : Ignore as its blacklisted", |
| db_entry->bssid.bytes); |
| return false; |
| } |
| } |
| |
| match = false; |
| if (db_entry->ssid.length) { |
| for (i = 0; i < filter->num_of_ssid; i++) { |
| if (util_is_ssid_match(&filter->ssid_list[i], |
| &db_entry->ssid)) { |
| match = true; |
| break; |
| } |
| } |
| } |
| /* |
| * In OWE transition mode, ssid is hidden. And supplicant does not issue |
| * scan with specific ssid prior to connect as in other hidden ssid |
| * cases. Add explicit check to allow OWE when ssid is hidden. |
| */ |
| if (!match && util_scan_entry_is_hidden_ap(db_entry)) { |
| for (i = 0; i < filter->num_of_auth; i++) { |
| if (filter->auth_type[i] == WLAN_AUTH_TYPE_OWE) { |
| match = true; |
| break; |
| } |
| } |
| } |
| if (!match && filter->num_of_ssid) |
| return false; |
| |
| match = false; |
| /* TO do Fill p2p MAC*/ |
| for (i = 0; i < filter->num_of_bssid; i++) { |
| if (util_is_bssid_match(&filter->bssid_list[i], |
| &db_entry->bssid)) { |
| match = true; |
| break; |
| } |
| /* TODO match p2p mac */ |
| } |
| if (!match && filter->num_of_bssid) |
| return false; |
| |
| match = false; |
| for (i = 0; i < filter->num_of_channels; i++) { |
| if (!filter->channel_list[i] || ( |
| (filter->channel_list[i] == |
| db_entry->channel.chan_idx))) { |
| match = true; |
| break; |
| } |
| } |
| |
| if (!match && filter->num_of_channels) |
| return false; |
| |
| if (filter->rrm_measurement_filter) |
| return true; |
| |
| /* TODO match phyMode */ |
| |
| if (!filter->ignore_auth_enc_type && |
| !scm_is_security_match(filter, |
| db_entry, security)) { |
| scm_debug("%pM : Ignore as security profile didn't match", |
| db_entry->bssid.bytes); |
| return false; |
| } |
| |
| if (!util_is_bss_type_match(filter->bss_type, |
| db_entry->cap_info)) { |
| scm_debug("%pM : Ignore as bss type didn't match cap_info %x bss_type %d", |
| db_entry->bssid.bytes, db_entry->cap_info.value, |
| filter->bss_type); |
| return false; |
| } |
| |
| /* TODO match rate set */ |
| |
| if (filter->only_wmm_ap && |
| !db_entry->ie_list.wmeinfo && |
| !db_entry->ie_list.wmeparam) { |
| scm_debug("%pM : Ignore as required wmeinfo and wme params not present", |
| db_entry->bssid.bytes); |
| return false; |
| } |
| |
| /* Match realm */ |
| if (!scm_is_fils_config_match(filter, db_entry)) { |
| scm_debug("%pM :Ignore as fils config didn't match", |
| db_entry->bssid.bytes); |
| return false; |
| } |
| |
| cc_ie = util_scan_entry_country(db_entry); |
| if (!util_country_code_match(filter->country, cc_ie)) { |
| scm_debug("%pM : Ignore as country %.*s didn't match", |
| db_entry->bssid.bytes, 2, filter->country); |
| return false; |
| } |
| |
| if (!util_mdie_match(filter->mobility_domain, |
| (struct rsn_mdie *)db_entry->ie_list.mdie)) { |
| scm_debug("%pM : Ignore as mdie didn't match", |
| db_entry->bssid.bytes); |
| return false; |
| } |
| |
| return true; |
| } |