diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-12-20 11:12:40 +0100 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-12-20 13:36:34 +0100 |
commit | 2b2563da953978f63e3e707f758fd600dcd19a32 (patch) | |
tree | a207b096c10788192b56025e8187f14d1b5a978d /freebsd/contrib/wpa/wpa_supplicant/wpa_supplicant.c | |
parent | freebsd/if_cpsw: Port. (diff) | |
download | rtems-libbsd-2b2563da953978f63e3e707f758fd600dcd19a32.tar.bz2 |
Update to FreeBSD head 2018-12-20
Git mirror commit 19a6ceb89dbacf74697d493e48c388767126d418.
It includes an update of wpa_supplicant to version 2.7.
It includes an update of the OpenSSL baseline to version 1.1.1a.
Update #3472.
Diffstat (limited to 'freebsd/contrib/wpa/wpa_supplicant/wpa_supplicant.c')
-rw-r--r-- | freebsd/contrib/wpa/wpa_supplicant/wpa_supplicant.c | 1764 |
1 files changed, 1041 insertions, 723 deletions
diff --git a/freebsd/contrib/wpa/wpa_supplicant/wpa_supplicant.c b/freebsd/contrib/wpa/wpa_supplicant/wpa_supplicant.c index 7346ae0b..5e777d70 100644 --- a/freebsd/contrib/wpa/wpa_supplicant/wpa_supplicant.c +++ b/freebsd/contrib/wpa/wpa_supplicant/wpa_supplicant.c @@ -2,7 +2,7 @@ /* * WPA Supplicant - * Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -40,6 +40,7 @@ #include "common/wpa_ctrl.h" #include "common/ieee802_11_defs.h" #include "common/hw_features_common.h" +#include "common/gas_server.h" #include "p2p/p2p.h" #include "fst/fst.h" #include "blacklist.h" @@ -61,10 +62,15 @@ #include "wnm_sta.h" #include "wpas_kay.h" #include "mesh.h" +#include "dpp_supplicant.h" +#ifdef CONFIG_MESH +#include "ap/ap_config.h" +#include "ap/hostapd.h" +#endif /* CONFIG_MESH */ const char *const wpa_supplicant_version = "wpa_supplicant v" VERSION_STR "\n" -"Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> and contributors"; +"Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi> and contributors"; const char *const wpa_supplicant_license = "This software may be distributed under the terms of the BSD license.\n" @@ -114,6 +120,13 @@ const char *const wpa_supplicant_full_license5 = "\n"; #endif /* CONFIG_NO_STDOUT_DEBUG */ + +static void wpa_bss_tmp_disallow_timeout(void *eloop_ctx, void *timeout_ctx); +#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL) +static void wpas_update_fils_connect_params(struct wpa_supplicant *wpa_s); +#endif /* CONFIG_FILS && IEEE8021X_EAPOL */ + + /* Configure default/group WEP keys for static WEP */ int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { @@ -232,10 +245,30 @@ void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s, wpa_dbg(wpa_s, MSG_DEBUG, "Setting authentication timeout: %d sec " "%d usec", sec, usec); eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); + wpa_s->last_auth_timeout_sec = sec; eloop_register_timeout(sec, usec, wpa_supplicant_timeout, wpa_s, NULL); } +/* + * wpas_auth_timeout_restart - Restart and change timeout for authentication + * @wpa_s: Pointer to wpa_supplicant data + * @sec_diff: difference in seconds applied to original timeout value + */ +void wpas_auth_timeout_restart(struct wpa_supplicant *wpa_s, int sec_diff) +{ + int new_sec = wpa_s->last_auth_timeout_sec + sec_diff; + + if (eloop_is_timeout_registered(wpa_supplicant_timeout, wpa_s, NULL)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Authentication timeout restart: %d sec", new_sec); + eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); + eloop_register_timeout(new_sec, 0, wpa_supplicant_timeout, + wpa_s, NULL); + } +} + + /** * wpa_supplicant_cancel_auth_timeout - Cancel authentication timeout * @wpa_s: Pointer to wpa_supplicant data @@ -249,6 +282,9 @@ void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s) wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling authentication timeout"); eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); wpa_blacklist_del(wpa_s, wpa_s->bssid); + os_free(wpa_s->last_con_fail_realm); + wpa_s->last_con_fail_realm = NULL; + wpa_s->last_con_fail_realm_len = 0; } @@ -331,7 +367,12 @@ void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s) eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf); - ieee802_1x_alloc_kay_sm(wpa_s, ssid); +#ifdef CONFIG_MACSEC + if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE && ssid->mka_psk_set) + ieee802_1x_create_preshared_mka(wpa_s, ssid); + else + ieee802_1x_alloc_kay_sm(wpa_s, ssid); +#endif /* CONFIG_MACSEC */ #endif /* IEEE8021X_EAPOL */ } @@ -411,12 +452,26 @@ static void free_bss_tmp_disallowed(struct wpa_supplicant *wpa_s) dl_list_for_each_safe(bss, prev, &wpa_s->bss_tmp_disallowed, struct wpa_bss_tmp_disallowed, list) { + eloop_cancel_timeout(wpa_bss_tmp_disallow_timeout, wpa_s, bss); dl_list_del(&bss->list); os_free(bss); } } +void wpas_flush_fils_hlp_req(struct wpa_supplicant *wpa_s) +{ + struct fils_hlp_req *req; + + while ((req = dl_list_first(&wpa_s->fils_hlp_req, struct fils_hlp_req, + list)) != NULL) { + dl_list_del(&req->list); + wpabuf_free(req->pkt); + os_free(req); + } +} + + static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) { int i; @@ -436,6 +491,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) #ifdef CONFIG_TESTING_OPTIONS l2_packet_deinit(wpa_s->l2_test); wpa_s->l2_test = NULL; + os_free(wpa_s->get_pref_freq_list_override); + wpa_s->get_pref_freq_list_override = NULL; + wpabuf_free(wpa_s->last_assoc_req_wpa_ie); + wpa_s->last_assoc_req_wpa_ie = NULL; #endif /* CONFIG_TESTING_OPTIONS */ if (wpa_s->conf != NULL) { @@ -450,6 +509,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) os_free(wpa_s->confanother); wpa_s->confanother = NULL; + os_free(wpa_s->last_con_fail_realm); + wpa_s->last_con_fail_realm = NULL; + wpa_s->last_con_fail_realm_len = 0; + wpa_sm_set_eapol(wpa_s->wpa, NULL); eapol_sm_deinit(wpa_s->eapol); wpa_s->eapol = NULL; @@ -508,6 +571,8 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) os_free(wpa_s->manual_scan_freqs); wpa_s->manual_scan_freqs = NULL; + os_free(wpa_s->select_network_scan_freqs); + wpa_s->select_network_scan_freqs = NULL; os_free(wpa_s->manual_sched_scan_freqs); wpa_s->manual_sched_scan_freqs = NULL; @@ -526,6 +591,8 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) radio_remove_works(wpa_s, "gas-query", 0); gas_query_deinit(wpa_s->gas); wpa_s->gas = NULL; + gas_server_deinit(wpa_s->gas_server); + wpa_s->gas_server = NULL; free_hw_features(wpa_s); @@ -582,6 +649,32 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpabuf_free(wpa_s->lci); wpa_s->lci = NULL; + wpas_clear_beacon_rep_data(wpa_s); + +#ifdef CONFIG_PMKSA_CACHE_EXTERNAL +#ifdef CONFIG_MESH + { + struct external_pmksa_cache *entry; + + while ((entry = dl_list_last(&wpa_s->mesh_external_pmksa_cache, + struct external_pmksa_cache, + list)) != NULL) { + dl_list_del(&entry->list); + os_free(entry->pmksa_cache); + os_free(entry); + } + } +#endif /* CONFIG_MESH */ +#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ + + wpas_flush_fils_hlp_req(wpa_s); + + wpabuf_free(wpa_s->ric_ies); + wpa_s->ric_ies = NULL; + +#ifdef CONFIG_DPP + wpas_dpp_deinit(wpa_s); +#endif /* CONFIG_DPP */ } @@ -795,12 +888,24 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, if (state == WPA_COMPLETED && wpa_s->new_connection) { struct wpa_ssid *ssid = wpa_s->current_ssid; + int fils_hlp_sent = 0; + +#ifdef CONFIG_SME + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && + wpa_auth_alg_fils(wpa_s->sme.auth_alg)) + fils_hlp_sent = 1; +#endif /* CONFIG_SME */ + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && + wpa_auth_alg_fils(wpa_s->auth_alg)) + fils_hlp_sent = 1; + #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to " - MACSTR " completed [id=%d id_str=%s]", + MACSTR " completed [id=%d id_str=%s%s]", MAC2STR(wpa_s->bssid), ssid ? ssid->id : -1, - ssid && ssid->id_str ? ssid->id_str : ""); + ssid && ssid->id_str ? ssid->id_str : "", + fils_hlp_sent ? " FILS_HLP_SENT" : ""); #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ wpas_clear_temp_disabled(wpa_s, ssid, 1); wpa_blacklist_clear(wpa_s); @@ -815,6 +920,11 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, wpas_p2p_completed(wpa_s); sme_sched_obss_scan(wpa_s, 1); + +#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL) + if (!fils_hlp_sent && ssid && ssid->eap.erp) + wpas_update_fils_connect_params(wpa_s); +#endif /* CONFIG_FILS && IEEE8021X_EAPOL */ } else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING || state == WPA_ASSOCIATED) { wpa_s->new_connection = 1; @@ -929,7 +1039,13 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) "file '%s' - exiting", wpa_s->confname); return -1; } - wpa_config_read(wpa_s->confanother, conf); + if (wpa_s->confanother && + !wpa_config_read(wpa_s->confanother, conf)) { + wpa_msg(wpa_s, MSG_ERROR, + "Failed to parse the configuration file '%s' - exiting", + wpa_s->confanother); + return -1; + } conf->changed_parameters = (unsigned int) -1; @@ -955,7 +1071,9 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) * TODO: should notify EAPOL SM about changes in opensc_engine_path, * pkcs11_engine_path, pkcs11_module_path, openssl_ciphers. */ - if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) { + if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || + wpa_s->key_mgmt == WPA_KEY_MGMT_OWE || + wpa_s->key_mgmt == WPA_KEY_MGMT_DPP) { /* * Clear forced success to clear EAP state for next * authentication. @@ -1100,14 +1218,20 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0"); proto = WPA_PROTO_WPA; #ifdef CONFIG_HS20 - } else if (bss_osen && (ssid->proto & WPA_PROTO_OSEN)) { + } else if (bss_osen && (ssid->proto & WPA_PROTO_OSEN) && + wpa_parse_wpa_ie(bss_osen, 2 + bss_osen[1], &ie) == 0 && + (ie.group_cipher & ssid->group_cipher) && + (ie.pairwise_cipher & ssid->pairwise_cipher) && + (ie.key_mgmt & ssid->key_mgmt)) { wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using OSEN"); - /* TODO: parse OSEN element */ - os_memset(&ie, 0, sizeof(ie)); - ie.group_cipher = WPA_CIPHER_CCMP; - ie.pairwise_cipher = WPA_CIPHER_CCMP; - ie.key_mgmt = WPA_KEY_MGMT_OSEN; proto = WPA_PROTO_OSEN; + } else if (bss_rsn && (ssid->proto & WPA_PROTO_OSEN) && + wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie) == 0 && + (ie.group_cipher & ssid->group_cipher) && + (ie.pairwise_cipher & ssid->pairwise_cipher) && + (ie.key_mgmt & ssid->key_mgmt)) { + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using OSEN (within RSN)"); + proto = WPA_PROTO_RSN; #endif /* CONFIG_HS20 */ } else if (bss) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select WPA/RSN"); @@ -1159,10 +1283,35 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, ie.pairwise_cipher = ssid->pairwise_cipher; ie.key_mgmt = ssid->key_mgmt; #ifdef CONFIG_IEEE80211W - ie.mgmt_group_cipher = - ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION ? - WPA_CIPHER_AES_128_CMAC : 0; + ie.mgmt_group_cipher = 0; + if (ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + if (ssid->group_mgmt_cipher & + WPA_CIPHER_BIP_GMAC_256) + ie.mgmt_group_cipher = + WPA_CIPHER_BIP_GMAC_256; + else if (ssid->group_mgmt_cipher & + WPA_CIPHER_BIP_CMAC_256) + ie.mgmt_group_cipher = + WPA_CIPHER_BIP_CMAC_256; + else if (ssid->group_mgmt_cipher & + WPA_CIPHER_BIP_GMAC_128) + ie.mgmt_group_cipher = + WPA_CIPHER_BIP_GMAC_128; + else + ie.mgmt_group_cipher = + WPA_CIPHER_AES_128_CMAC; + } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OWE + if ((ssid->key_mgmt & WPA_KEY_MGMT_OWE) && + !ssid->owe_only && + !bss_wpa && !bss_rsn && !bss_osen) { + wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); + wpa_s->wpa_proto = 0; + *wpa_ie_len = 0; + return 0; + } +#endif /* CONFIG_OWE */ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Set cipher suites " "based on configuration"); } else @@ -1235,10 +1384,46 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X with Suite B"); #endif /* CONFIG_SUITEB */ +#ifdef CONFIG_FILS +#ifdef CONFIG_IEEE80211R + } else if (sel & WPA_KEY_MGMT_FT_FILS_SHA384) { + wpa_s->key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT-FILS-SHA384"); + } else if (sel & WPA_KEY_MGMT_FT_FILS_SHA256) { + wpa_s->key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT-FILS-SHA256"); +#endif /* CONFIG_IEEE80211R */ + } else if (sel & WPA_KEY_MGMT_FILS_SHA384) { + wpa_s->key_mgmt = WPA_KEY_MGMT_FILS_SHA384; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FILS-SHA384"); + } else if (sel & WPA_KEY_MGMT_FILS_SHA256) { + wpa_s->key_mgmt = WPA_KEY_MGMT_FILS_SHA256; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FILS-SHA256"); +#endif /* CONFIG_FILS */ #ifdef CONFIG_IEEE80211R +#ifdef CONFIG_SHA384 + } else if (sel & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) { + wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: using KEY_MGMT FT/802.1X-SHA384"); + if (pmksa_cache_get_current(wpa_s->wpa)) { + /* PMKSA caching with FT is not fully functional, so + * disable the case for now. */ + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: Disable PMKSA caching for FT/802.1X connection"); + pmksa_cache_clear_current(wpa_s->wpa); + } +#endif /* CONFIG_SHA384 */ } else if (sel & WPA_KEY_MGMT_FT_IEEE8021X) { wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X"); + if (pmksa_cache_get_current(wpa_s->wpa)) { + /* PMKSA caching with FT is not fully functional, so + * disable the case for now. */ + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: Disable PMKSA caching for FT/802.1X connection"); + pmksa_cache_clear_current(wpa_s->wpa); + } } else if (sel & WPA_KEY_MGMT_FT_PSK) { wpa_s->key_mgmt = WPA_KEY_MGMT_FT_PSK; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/PSK"); @@ -1275,6 +1460,16 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_s->key_mgmt = WPA_KEY_MGMT_OSEN; wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using KEY_MGMT OSEN"); #endif /* CONFIG_HS20 */ +#ifdef CONFIG_OWE + } else if (sel & WPA_KEY_MGMT_OWE) { + wpa_s->key_mgmt = WPA_KEY_MGMT_OWE; + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT OWE"); +#endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP + } else if (sel & WPA_KEY_MGMT_DPP) { + wpa_s->key_mgmt = WPA_KEY_MGMT_DPP; + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT DPP"); +#endif /* CONFIG_DPP */ } else { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select " "authenticated key management type"); @@ -1288,6 +1483,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, #ifdef CONFIG_IEEE80211W sel = ie.mgmt_group_cipher; + if (ssid->group_mgmt_cipher) + sel &= ssid->group_mgmt_cipher; if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION || !(ie.capabilities & WPA_CAPABILITY_MFPC)) sel = 0; @@ -1324,15 +1521,27 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) { int psk_set = 0; + int sae_only; + + sae_only = (ssid->key_mgmt & (WPA_KEY_MGMT_PSK | + WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_PSK_SHA256)) == 0; - if (ssid->psk_set) { + if (ssid->psk_set && !sae_only) { + wpa_hexdump_key(MSG_MSGDUMP, "PSK (set in config)", + ssid->psk, PMK_LEN); wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL, NULL); psk_set = 1; } + + if (wpa_key_mgmt_sae(ssid->key_mgmt) && + (ssid->sae_password || ssid->passphrase)) + psk_set = 1; + #ifndef CONFIG_NO_PBKDF2 if (bss && ssid->bssid_set && ssid->ssid_len == 0 && - ssid->passphrase) { + ssid->passphrase && !sae_only) { u8 psk[PMK_LEN]; pbkdf2_sha1(ssid->passphrase, bss->ssid, bss->ssid_len, 4096, psk, PMK_LEN); @@ -1344,7 +1553,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_NO_PBKDF2 */ #ifdef CONFIG_EXT_PASSWORD - if (ssid->ext_psk) { + if (ssid->ext_psk && !sae_only) { struct wpabuf *pw = ext_password_get(wpa_s->ext_pw, ssid->ext_psk); char pw_str[64 + 1]; @@ -1390,6 +1599,9 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, ext_password_free(pw); return -1; } + wpa_hexdump_key(MSG_MSGDUMP, + "PSK (from external PSK)", + psk, PMK_LEN); wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL, NULL); psk_set = 1; @@ -1410,8 +1622,15 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, if (!psk_set) { wpa_msg(wpa_s, MSG_INFO, "No PSK available for association"); + wpas_auth_failed(wpa_s, "NO_PSK_AVAILABLE"); return -1; } +#ifdef CONFIG_OWE + } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_OWE) { + /* OWE Diffie-Hellman exchange in (Re)Association + * Request/Response frames set the PMK, so do not override it + * here. */ +#endif /* CONFIG_OWE */ } else wpa_sm_set_pmk_from_pmksa(wpa_s->wpa); @@ -1427,6 +1646,10 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) case 0: /* Bits 0-7 */ break; case 1: /* Bits 8-15 */ + if (wpa_s->conf->coloc_intf_reporting) { + /* Bit 13 - Collocated Interference Reporting */ + *pos |= 0x20; + } break; case 2: /* Bits 16-23 */ #ifdef CONFIG_WNM @@ -1445,7 +1668,7 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) break; case 4: /* Bits 32-39 */ #ifdef CONFIG_INTERWORKING - if (wpa_s->drv_flags / WPA_DRIVER_FLAGS_QOS_MAPPING) + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_QOS_MAPPING) *pos |= 0x01; /* Bit 32 - QoS Map */ #endif /* CONFIG_INTERWORKING */ break; @@ -1468,6 +1691,12 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) if (wpa_s->conf->ftm_initiator) *pos |= 0x80; /* Bit 71 - FTM initiator */ break; + case 9: /* Bits 72-79 */ +#ifdef CONFIG_FILS + if (!wpa_s->disable_fils) + *pos |= 0x01; +#endif /* CONFIG_FILS */ + break; } } @@ -1475,11 +1704,8 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen) { u8 *pos = buf; - u8 len = 6, i; + u8 len = 10, i; - if (len < 9 && - (wpa_s->conf->ftm_initiator || wpa_s->conf->ftm_responder)) - len = 9; if (len < wpa_s->extended_capa_len) len = wpa_s->extended_capa_len; if (buflen < (size_t) len + 2) { @@ -1667,6 +1893,9 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, wmm_ac_clear_saved_tspecs(wpa_s); wpa_s->reassoc_same_bss = 0; wpa_s->reassoc_same_ess = 0; +#ifdef CONFIG_TESTING_OPTIONS + wpa_s->testing_resend_assoc = 0; +#endif /* CONFIG_TESTING_OPTIONS */ if (wpa_s->last_ssid == ssid) { wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS"); @@ -1675,11 +1904,13 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, wmm_ac_save_tspecs(wpa_s); wpa_s->reassoc_same_bss = 1; } - } else if (rand_style > 0) { + } + + if (rand_style > 0 && !wpa_s->reassoc_same_ess) { if (wpas_update_random_addr(wpa_s, rand_style) < 0) return; wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid); - } else if (wpa_s->mac_addr_changed) { + } else if (rand_style == 0 && wpa_s->mac_addr_changed) { if (wpa_drv_set_mac_addr(wpa_s, NULL) < 0) { wpa_msg(wpa_s, MSG_INFO, "Could not restore permanent MAC address"); @@ -1698,6 +1929,13 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, #ifdef CONFIG_IBSS_RSN ibss_rsn_deinit(wpa_s->ibss_rsn); wpa_s->ibss_rsn = NULL; +#else /* CONFIG_IBSS_RSN */ + if (ssid->mode == WPAS_MODE_IBSS && + !(ssid->key_mgmt & (WPA_KEY_MGMT_NONE | WPA_KEY_MGMT_WPA_NONE))) { + wpa_msg(wpa_s, MSG_INFO, + "IBSS RSN not supported in the build"); + return; + } #endif /* CONFIG_IBSS_RSN */ if (ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_P2P_GO || @@ -1739,6 +1977,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_STARTED "ssid=\"%s\" id=%d", wpa_ssid_txt(ssid->ssid, ssid->ssid_len), ssid->id); + wpas_notify_mesh_group_started(wpa_s, ssid); #else /* CONFIG_MESH */ wpa_msg(wpa_s, MSG_ERROR, "mesh mode support not included in the build"); @@ -1746,6 +1985,20 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, return; } + /* + * Set WPA state machine configuration to match the selected network now + * so that the information is available before wpas_start_assoc_cb() + * gets called. This is needed at least for RSN pre-authentication where + * candidate APs are added to a list based on scan result processing + * before completion of the first association. + */ + wpa_supplicant_rsn_supp_set_config(wpa_s, ssid); + +#ifdef CONFIG_DPP + if (wpas_dpp_check_connect(wpa_s, ssid, bss) != 0) + return; +#endif /* CONFIG_DPP */ + #ifdef CONFIG_TDLS if (bss) wpa_tdls_ap_ies(wpa_s->wpa, (const u8 *) (bss + 1), @@ -1768,6 +2021,13 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, return; } +#ifdef CONFIG_SME + if (ssid->mode == WPAS_MODE_IBSS || ssid->mode == WPAS_MODE_MESH) { + /* Clear possibly set auth_alg, if any, from last attempt. */ + wpa_s->sme.auth_alg = WPA_AUTH_ALG_OPEN; + } +#endif /* CONFIG_SME */ + wpas_abort_ongoing_scan(wpa_s); cwork = os_zalloc(sizeof(*cwork)); @@ -1799,11 +2059,6 @@ static int drv_supports_vht(struct wpa_supplicant *wpa_s, u8 channel; int i; -#ifdef CONFIG_HT_OVERRIDES - if (ssid->disable_ht) - return 0; -#endif /* CONFIG_HT_OVERRIDES */ - hw_mode = ieee80211_freq_to_chan(ssid->frequency, &channel); if (hw_mode == NUM_HOSTAPD_MODES) return 0; @@ -2002,6 +2257,13 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, vht_freq = *freq; +#ifdef CONFIG_VHT_OVERRIDES + if (ssid->disable_vht) { + freq->vht_enabled = 0; + return; + } +#endif /* CONFIG_VHT_OVERRIDES */ + vht_freq.vht_enabled = vht_supported(mode); if (!vht_freq.vht_enabled) return; @@ -2086,147 +2348,170 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, } -static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) +#ifdef CONFIG_FILS +static size_t wpas_add_fils_hlp_req(struct wpa_supplicant *wpa_s, u8 *ie_buf, + size_t ie_buf_len) { - struct wpa_connect_work *cwork = work->ctx; - struct wpa_bss *bss = cwork->bss; - struct wpa_ssid *ssid = cwork->ssid; - struct wpa_supplicant *wpa_s = work->wpa_s; - u8 wpa_ie[200]; - size_t wpa_ie_len; - int use_crypt, ret, i, bssid_changed; - int algs = WPA_AUTH_ALG_OPEN; - unsigned int cipher_pairwise, cipher_group; - struct wpa_driver_associate_params params; - int wep_keys_set = 0; - int assoc_failed = 0; - struct wpa_ssid *old_ssid; - u8 prev_bssid[ETH_ALEN]; -#ifdef CONFIG_HT_OVERRIDES - struct ieee80211_ht_capabilities htcaps; - struct ieee80211_ht_capabilities htcaps_mask; -#endif /* CONFIG_HT_OVERRIDES */ -#ifdef CONFIG_VHT_OVERRIDES - struct ieee80211_vht_capabilities vhtcaps; - struct ieee80211_vht_capabilities vhtcaps_mask; -#endif /* CONFIG_VHT_OVERRIDES */ -#ifdef CONFIG_MBO - const u8 *mbo = NULL; -#endif /* CONFIG_MBO */ + struct fils_hlp_req *req; + size_t rem_len, hdr_len, hlp_len, len, ie_len = 0; + const u8 *pos; + u8 *buf = ie_buf; - if (deinit) { - if (work->started) { - wpa_s->connect_work = NULL; + dl_list_for_each(req, &wpa_s->fils_hlp_req, struct fils_hlp_req, + list) { + rem_len = ie_buf_len - ie_len; + pos = wpabuf_head(req->pkt); + hdr_len = 1 + 2 * ETH_ALEN + 6; + hlp_len = wpabuf_len(req->pkt); - /* cancel possible auth. timeout */ - eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, - NULL); + if (rem_len < 2 + hdr_len + hlp_len) { + wpa_printf(MSG_ERROR, + "FILS: Cannot fit HLP - rem_len=%lu to_fill=%lu", + (unsigned long) rem_len, + (unsigned long) (2 + hdr_len + hlp_len)); + break; + } + + len = (hdr_len + hlp_len) > 255 ? 255 : hdr_len + hlp_len; + /* Element ID */ + *buf++ = WLAN_EID_EXTENSION; + /* Length */ + *buf++ = len; + /* Element ID Extension */ + *buf++ = WLAN_EID_EXT_FILS_HLP_CONTAINER; + /* Destination MAC address */ + os_memcpy(buf, req->dst, ETH_ALEN); + buf += ETH_ALEN; + /* Source MAC address */ + os_memcpy(buf, wpa_s->own_addr, ETH_ALEN); + buf += ETH_ALEN; + /* LLC/SNAP Header */ + os_memcpy(buf, "\xaa\xaa\x03\x00\x00\x00", 6); + buf += 6; + /* HLP Packet */ + os_memcpy(buf, pos, len - hdr_len); + buf += len - hdr_len; + pos += len - hdr_len; + + hlp_len -= len - hdr_len; + ie_len += 2 + len; + rem_len -= 2 + len; + + while (hlp_len) { + len = (hlp_len > 255) ? 255 : hlp_len; + if (rem_len < 2 + len) + break; + *buf++ = WLAN_EID_FRAGMENT; + *buf++ = len; + os_memcpy(buf, pos, len); + buf += len; + pos += len; + + hlp_len -= len; + ie_len += 2 + len; + rem_len -= 2 + len; } - wpas_connect_work_free(cwork); - return; } - wpa_s->connect_work = work; + return ie_len; +} - if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid) || - wpas_network_disabled(wpa_s, ssid)) { - wpa_dbg(wpa_s, MSG_DEBUG, "BSS/SSID entry for association not valid anymore - drop connection attempt"); - wpas_connect_work_done(wpa_s); - return; - } - os_memcpy(prev_bssid, wpa_s->bssid, ETH_ALEN); - os_memset(¶ms, 0, sizeof(params)); - wpa_s->reassociate = 0; - wpa_s->eap_expected_failure = 0; - if (bss && - (!wpas_driver_bss_selection(wpa_s) || wpas_wps_searching(wpa_s))) { -#ifdef CONFIG_IEEE80211R - const u8 *ie, *md = NULL; -#endif /* CONFIG_IEEE80211R */ - wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR - " (SSID='%s' freq=%d MHz)", MAC2STR(bss->bssid), - wpa_ssid_txt(bss->ssid, bss->ssid_len), bss->freq); - bssid_changed = !is_zero_ether_addr(wpa_s->bssid); - os_memset(wpa_s->bssid, 0, ETH_ALEN); - os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN); - if (bssid_changed) - wpas_notify_bssid_changed(wpa_s); -#ifdef CONFIG_IEEE80211R - ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN); - if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN) - md = ie + 2; - wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0); - if (md) { - /* Prepare for the next transition */ - wpa_ft_prepare_auth_request(wpa_s->wpa, ie); - } -#endif /* CONFIG_IEEE80211R */ -#ifdef CONFIG_WPS - } else if ((ssid->ssid == NULL || ssid->ssid_len == 0) && - wpa_s->conf->ap_scan == 2 && - (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { - /* Use ap_scan==1 style network selection to find the network - */ - wpas_connect_work_done(wpa_s); - wpa_s->scan_req = MANUAL_SCAN_REQ; - wpa_s->reassociate = 1; - wpa_supplicant_req_scan(wpa_s, 0, 0); - return; -#endif /* CONFIG_WPS */ - } else { - wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'", - wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); - if (bss) - os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN); - else - os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); - } - if (!wpa_s->pno) - wpa_supplicant_cancel_sched_scan(wpa_s); +int wpa_is_fils_supported(struct wpa_supplicant *wpa_s) +{ + return (((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_FILS)) || + (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD))); +} - wpa_supplicant_cancel_scan(wpa_s); - /* Starting new association, so clear the possibly used WPA IE from the - * previous association. */ - wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); +int wpa_is_fils_sk_pfs_supported(struct wpa_supplicant *wpa_s) +{ +#ifdef CONFIG_FILS_SK_PFS + return (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_FILS); +#else /* CONFIG_FILS_SK_PFS */ + return 0; +#endif /* CONFIG_FILS_SK_PFS */ +} -#ifdef IEEE8021X_EAPOL - if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) { - if (ssid->leap) { - if (ssid->non_leap == 0) - algs = WPA_AUTH_ALG_LEAP; - else - algs |= WPA_AUTH_ALG_LEAP; - } +#endif /* CONFIG_FILS */ + + +static u8 * wpas_populate_assoc_ies( + struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, struct wpa_ssid *ssid, + struct wpa_driver_associate_params *params, + enum wpa_drv_update_connect_params_mask *mask) +{ + u8 *wpa_ie; + size_t max_wpa_ie_len = 500; + size_t wpa_ie_len; + int algs = WPA_AUTH_ALG_OPEN; +#ifdef CONFIG_MBO + const u8 *mbo_ie; +#endif +#ifdef CONFIG_FILS + const u8 *realm, *username, *rrk; + size_t realm_len, username_len, rrk_len; + u16 next_seq_num; + struct fils_hlp_req *req; + + dl_list_for_each(req, &wpa_s->fils_hlp_req, struct fils_hlp_req, + list) { + max_wpa_ie_len += 3 + 2 * ETH_ALEN + 6 + wpabuf_len(req->pkt) + + 2 + 2 * wpabuf_len(req->pkt) / 255; } -#endif /* IEEE8021X_EAPOL */ - wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs); - if (ssid->auth_alg) { - algs = ssid->auth_alg; - wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: " - "0x%x", algs); +#endif /* CONFIG_FILS */ + + wpa_ie = os_malloc(max_wpa_ie_len); + if (!wpa_ie) { + wpa_printf(MSG_ERROR, + "Failed to allocate connect IE buffer for %lu bytes", + (unsigned long) max_wpa_ie_len); + return NULL; } if (bss && (wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) || wpa_bss_get_ie(bss, WLAN_EID_RSN)) && wpa_key_mgmt_wpa(ssid->key_mgmt)) { int try_opportunistic; + const u8 *cache_id = NULL; + try_opportunistic = (ssid->proactive_key_caching < 0 ? wpa_s->conf->okc : ssid->proactive_key_caching) && (ssid->proto & WPA_PROTO_RSN); +#ifdef CONFIG_FILS + if (wpa_key_mgmt_fils(ssid->key_mgmt)) + cache_id = wpa_bss_get_fils_cache_id(bss); +#endif /* CONFIG_FILS */ if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, - ssid, try_opportunistic) == 0) + ssid, try_opportunistic, + cache_id, 0) == 0) eapol_sm_notify_pmkid_attempt(wpa_s->eapol); - wpa_ie_len = sizeof(wpa_ie); + wpa_ie_len = max_wpa_ie_len; if (wpa_supplicant_set_suites(wpa_s, bss, ssid, wpa_ie, &wpa_ie_len)) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA " "key management and encryption suites"); - wpas_connect_work_done(wpa_s); - return; + os_free(wpa_ie); + return NULL; } +#ifdef CONFIG_HS20 + } else if (bss && wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE) && + (ssid->key_mgmt & WPA_KEY_MGMT_OSEN)) { + /* No PMKSA caching, but otherwise similar to RSN/WPA */ + wpa_ie_len = max_wpa_ie_len; + if (wpa_supplicant_set_suites(wpa_s, bss, ssid, + wpa_ie, &wpa_ie_len)) { + wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA " + "key management and encryption suites"); + os_free(wpa_ie); + return NULL; + } +#endif /* CONFIG_HS20 */ } else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && bss && wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) { /* @@ -2238,20 +2523,20 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) wpa_ie_len = 0; wpa_s->wpa_proto = 0; } else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) { - wpa_ie_len = sizeof(wpa_ie); + wpa_ie_len = max_wpa_ie_len; if (wpa_supplicant_set_suites(wpa_s, NULL, ssid, wpa_ie, &wpa_ie_len)) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA " "key management and encryption suites (no " "scan results)"); - wpas_connect_work_done(wpa_s); - return; + os_free(wpa_ie); + return NULL; } #ifdef CONFIG_WPS } else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { struct wpabuf *wps_ie; wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid)); - if (wps_ie && wpabuf_len(wps_ie) <= sizeof(wpa_ie)) { + if (wps_ie && wpabuf_len(wps_ie) <= max_wpa_ie_len) { wpa_ie_len = wpabuf_len(wps_ie); os_memcpy(wpa_ie, wpabuf_head(wps_ie), wpa_ie_len); } else @@ -2259,9 +2544,9 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) wpabuf_free(wps_ie); wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); if (!bss || (bss->caps & IEEE80211_CAP_PRIVACY)) - params.wps = WPS_MODE_PRIVACY; + params->wps = WPS_MODE_PRIVACY; else - params.wps = WPS_MODE_OPEN; + params->wps = WPS_MODE_OPEN; wpa_s->wpa_proto = 0; #endif /* CONFIG_WPS */ } else { @@ -2270,13 +2555,61 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) wpa_s->wpa_proto = 0; } +#ifdef IEEE8021X_EAPOL + if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + if (ssid->leap) { + if (ssid->non_leap == 0) + algs = WPA_AUTH_ALG_LEAP; + else + algs |= WPA_AUTH_ALG_LEAP; + } + } + +#ifdef CONFIG_FILS + /* Clear FILS association */ + wpa_sm_set_reset_fils_completed(wpa_s->wpa, 0); + + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD) && + ssid->eap.erp && wpa_key_mgmt_fils(wpa_s->key_mgmt) && + eapol_sm_get_erp_info(wpa_s->eapol, &ssid->eap, &username, + &username_len, &realm, &realm_len, + &next_seq_num, &rrk, &rrk_len) == 0 && + (!wpa_s->last_con_fail_realm || + wpa_s->last_con_fail_realm_len != realm_len || + os_memcmp(wpa_s->last_con_fail_realm, realm, realm_len) != 0)) { + algs = WPA_AUTH_ALG_FILS; + params->fils_erp_username = username; + params->fils_erp_username_len = username_len; + params->fils_erp_realm = realm; + params->fils_erp_realm_len = realm_len; + params->fils_erp_next_seq_num = next_seq_num; + params->fils_erp_rrk = rrk; + params->fils_erp_rrk_len = rrk_len; + + if (mask) + *mask |= WPA_DRV_UPDATE_FILS_ERP_INFO; + } +#endif /* CONFIG_FILS */ +#endif /* IEEE8021X_EAPOL */ +#ifdef CONFIG_SAE + if (wpa_s->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE)) + algs = WPA_AUTH_ALG_SAE; +#endif /* CONFIG_SAE */ + + wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs); + if (ssid->auth_alg) { + algs = ssid->auth_alg; + wpa_dbg(wpa_s, MSG_DEBUG, + "Overriding auth_alg selection: 0x%x", algs); + } + #ifdef CONFIG_P2P if (wpa_s->global->p2p) { u8 *pos; size_t len; int res; pos = wpa_ie + wpa_ie_len; - len = sizeof(wpa_ie) - wpa_ie_len; + len = max_wpa_ie_len - wpa_ie_len; res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len, ssid->p2p_group); if (res >= 0) @@ -2301,21 +2634,12 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info)); #endif /* CONFIG_P2P */ -#ifdef CONFIG_MBO if (bss) { - mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE); - if (mbo) { - int len; - - len = wpas_mbo_supp_op_class_ie(wpa_s, bss->freq, - wpa_ie + wpa_ie_len, - sizeof(wpa_ie) - - wpa_ie_len); - if (len > 0) - wpa_ie_len += len; - } + wpa_ie_len += wpas_supp_op_class_ie(wpa_s, bss->freq, + wpa_ie + wpa_ie_len, + max_wpa_ie_len - + wpa_ie_len); } -#endif /* CONFIG_MBO */ /* * Workaround: Add Extended Capabilities element only if the AP @@ -2335,7 +2659,8 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) int ext_capab_len; ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab, sizeof(ext_capab)); - if (ext_capab_len > 0) { + if (ext_capab_len > 0 && + wpa_ie_len + ext_capab_len <= max_wpa_ie_len) { u8 *pos = wpa_ie; if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN) pos += 2 + pos[1]; @@ -2350,13 +2675,14 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) if (is_hs20_network(wpa_s, ssid, bss)) { struct wpabuf *hs20; - hs20 = wpabuf_alloc(20); + hs20 = wpabuf_alloc(20 + MAX_ROAMING_CONS_OI_LEN); if (hs20) { int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid); size_t len; wpas_hs20_add_indication(hs20, pps_mo_id); - len = sizeof(wpa_ie) - wpa_ie_len; + wpas_hs20_add_roam_cons_sel(hs20, ssid); + len = max_wpa_ie_len - wpa_ie_len; if (wpabuf_len(hs20) <= len) { os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(hs20), wpabuf_len(hs20)); @@ -2373,7 +2699,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]; size_t len; - len = sizeof(wpa_ie) - wpa_ie_len; + len = max_wpa_ie_len - wpa_ie_len; if (wpabuf_len(buf) <= len) { os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(buf), wpabuf_len(buf)); @@ -2385,7 +2711,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) if (wpa_s->fst_ies) { int fst_ies_len = wpabuf_len(wpa_s->fst_ies); - if (wpa_ie_len + fst_ies_len <= sizeof(wpa_ie)) { + if (wpa_ie_len + fst_ies_len <= max_wpa_ie_len) { os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(wpa_s->fst_ies), fst_ies_len); wpa_ie_len += fst_ies_len; @@ -2394,20 +2720,249 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) #endif /* CONFIG_FST */ #ifdef CONFIG_MBO - if (mbo) { + mbo_ie = bss ? wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE) : NULL; + if (mbo_ie) { int len; len = wpas_mbo_ie(wpa_s, wpa_ie + wpa_ie_len, - sizeof(wpa_ie) - wpa_ie_len); + max_wpa_ie_len - wpa_ie_len, + !!mbo_attr_from_mbo_ie(mbo_ie, + OCE_ATTR_ID_CAPA_IND)); if (len >= 0) wpa_ie_len += len; } #endif /* CONFIG_MBO */ +#ifdef CONFIG_FILS + if (algs == WPA_AUTH_ALG_FILS) { + size_t len; + + len = wpas_add_fils_hlp_req(wpa_s, wpa_ie + wpa_ie_len, + max_wpa_ie_len - wpa_ie_len); + wpa_ie_len += len; + } +#endif /* CONFIG_FILS */ + +#ifdef CONFIG_OWE +#ifdef CONFIG_TESTING_OPTIONS + if (get_ie_ext(wpa_ie, wpa_ie_len, WLAN_EID_EXT_OWE_DH_PARAM)) { + wpa_printf(MSG_INFO, "TESTING: Override OWE DH element"); + } else +#endif /* CONFIG_TESTING_OPTIONS */ + if (algs == WPA_AUTH_ALG_OPEN && + ssid->key_mgmt == WPA_KEY_MGMT_OWE) { + struct wpabuf *owe_ie; + u16 group; + + if (ssid->owe_group) { + group = ssid->owe_group; + } else if (wpa_s->assoc_status_code == + WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) { + if (wpa_s->last_owe_group == 19) + group = 20; + else if (wpa_s->last_owe_group == 20) + group = 21; + else + group = OWE_DH_GROUP; + } else { + group = OWE_DH_GROUP; + } + + wpa_s->last_owe_group = group; + wpa_printf(MSG_DEBUG, "OWE: Try to use group %u", group); + owe_ie = owe_build_assoc_req(wpa_s->wpa, group); + if (owe_ie && + wpabuf_len(owe_ie) <= max_wpa_ie_len - wpa_ie_len) { + os_memcpy(wpa_ie + wpa_ie_len, + wpabuf_head(owe_ie), wpabuf_len(owe_ie)); + wpa_ie_len += wpabuf_len(owe_ie); + wpabuf_free(owe_ie); + } + } +#endif /* CONFIG_OWE */ + +#ifdef CONFIG_IEEE80211R + /* + * Add MDIE under these conditions: the network profile allows FT, + * the AP supports FT, and the mobility domain ID matches. + */ + if (bss && wpa_key_mgmt_ft(wpa_sm_get_key_mgmt(wpa_s->wpa))) { + const u8 *mdie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN); + + if (mdie && mdie[1] >= MOBILITY_DOMAIN_ID_LEN) { + size_t len = 0; + const u8 *md = mdie + 2; + const u8 *wpa_md = wpa_sm_get_ft_md(wpa_s->wpa); + + if (os_memcmp(md, wpa_md, + MOBILITY_DOMAIN_ID_LEN) == 0) { + /* Add mobility domain IE */ + len = wpa_ft_add_mdie( + wpa_s->wpa, wpa_ie + wpa_ie_len, + max_wpa_ie_len - wpa_ie_len, mdie); + wpa_ie_len += len; + } +#ifdef CONFIG_SME + if (len > 0 && wpa_s->sme.ft_used && + wpa_sm_has_ptk(wpa_s->wpa)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "SME: Trying to use FT over-the-air"); + algs |= WPA_AUTH_ALG_FT; + } +#endif /* CONFIG_SME */ + } + } +#endif /* CONFIG_IEEE80211R */ + + params->wpa_ie = wpa_ie; + params->wpa_ie_len = wpa_ie_len; + params->auth_alg = algs; + if (mask) + *mask |= WPA_DRV_UPDATE_ASSOC_IES | WPA_DRV_UPDATE_AUTH_TYPE; + + return wpa_ie; +} + + +#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL) +static void wpas_update_fils_connect_params(struct wpa_supplicant *wpa_s) +{ + struct wpa_driver_associate_params params; + enum wpa_drv_update_connect_params_mask mask = 0; + u8 *wpa_ie; + + if (wpa_s->auth_alg != WPA_AUTH_ALG_OPEN) + return; /* nothing to do */ + + os_memset(¶ms, 0, sizeof(params)); + wpa_ie = wpas_populate_assoc_ies(wpa_s, wpa_s->current_bss, + wpa_s->current_ssid, ¶ms, &mask); + if (!wpa_ie) + return; + + if (params.auth_alg != WPA_AUTH_ALG_FILS) { + os_free(wpa_ie); + return; + } + + wpa_s->auth_alg = params.auth_alg; + wpa_drv_update_connect_params(wpa_s, ¶ms, mask); + os_free(wpa_ie); +} +#endif /* CONFIG_FILS && IEEE8021X_EAPOL */ + + +static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) +{ + struct wpa_connect_work *cwork = work->ctx; + struct wpa_bss *bss = cwork->bss; + struct wpa_ssid *ssid = cwork->ssid; + struct wpa_supplicant *wpa_s = work->wpa_s; + u8 *wpa_ie; + int use_crypt, ret, i, bssid_changed; + unsigned int cipher_pairwise, cipher_group, cipher_group_mgmt; + struct wpa_driver_associate_params params; + int wep_keys_set = 0; + int assoc_failed = 0; + struct wpa_ssid *old_ssid; + u8 prev_bssid[ETH_ALEN]; +#ifdef CONFIG_HT_OVERRIDES + struct ieee80211_ht_capabilities htcaps; + struct ieee80211_ht_capabilities htcaps_mask; +#endif /* CONFIG_HT_OVERRIDES */ +#ifdef CONFIG_VHT_OVERRIDES + struct ieee80211_vht_capabilities vhtcaps; + struct ieee80211_vht_capabilities vhtcaps_mask; +#endif /* CONFIG_VHT_OVERRIDES */ + + if (deinit) { + if (work->started) { + wpa_s->connect_work = NULL; + + /* cancel possible auth. timeout */ + eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, + NULL); + } + wpas_connect_work_free(cwork); + return; + } + + wpa_s->connect_work = work; + + if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid) || + wpas_network_disabled(wpa_s, ssid)) { + wpa_dbg(wpa_s, MSG_DEBUG, "BSS/SSID entry for association not valid anymore - drop connection attempt"); + wpas_connect_work_done(wpa_s); + return; + } + + os_memcpy(prev_bssid, wpa_s->bssid, ETH_ALEN); + os_memset(¶ms, 0, sizeof(params)); + wpa_s->reassociate = 0; + wpa_s->eap_expected_failure = 0; + if (bss && + (!wpas_driver_bss_selection(wpa_s) || wpas_wps_searching(wpa_s))) { +#ifdef CONFIG_IEEE80211R + const u8 *ie, *md = NULL; +#endif /* CONFIG_IEEE80211R */ + wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR + " (SSID='%s' freq=%d MHz)", MAC2STR(bss->bssid), + wpa_ssid_txt(bss->ssid, bss->ssid_len), bss->freq); + bssid_changed = !is_zero_ether_addr(wpa_s->bssid); + os_memset(wpa_s->bssid, 0, ETH_ALEN); + os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN); + if (bssid_changed) + wpas_notify_bssid_changed(wpa_s); +#ifdef CONFIG_IEEE80211R + ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN); + if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN) + md = ie + 2; + wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0); + if (md) { + /* Prepare for the next transition */ + wpa_ft_prepare_auth_request(wpa_s->wpa, ie); + } +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_WPS + } else if ((ssid->ssid == NULL || ssid->ssid_len == 0) && + wpa_s->conf->ap_scan == 2 && + (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { + /* Use ap_scan==1 style network selection to find the network + */ + wpas_connect_work_done(wpa_s); + wpa_s->scan_req = MANUAL_SCAN_REQ; + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); + return; +#endif /* CONFIG_WPS */ + } else { + wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'", + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + if (bss) + os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN); + else + os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); + } + if (!wpa_s->pno) + wpa_supplicant_cancel_sched_scan(wpa_s); + + wpa_supplicant_cancel_scan(wpa_s); + + /* Starting new association, so clear the possibly used WPA IE from the + * previous association. */ + wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); + + wpa_ie = wpas_populate_assoc_ies(wpa_s, bss, ssid, ¶ms, NULL); + if (!wpa_ie) { + wpas_connect_work_done(wpa_s); + return; + } + wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL); use_crypt = 1; cipher_pairwise = wpa_s->pairwise_cipher; cipher_group = wpa_s->group_cipher; + cipher_group_mgmt = wpa_s->mgmt_group_cipher; if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) @@ -2445,12 +3000,14 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) if (bss) { params.ssid = bss->ssid; params.ssid_len = bss->ssid_len; - if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) { + if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set || + wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) { wpa_printf(MSG_DEBUG, "Limit connection to BSSID " MACSTR " freq=%u MHz based on scan results " - "(bssid_set=%d)", + "(bssid_set=%d wps=%d)", MAC2STR(bss->bssid), bss->freq, - ssid->bssid_set); + ssid->bssid_set, + wpa_s->key_mgmt == WPA_KEY_MGMT_WPS); params.bssid = bss->bssid; params.freq.freq = bss->freq; } @@ -2458,6 +3015,9 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) params.freq_hint = bss->freq; params.pbss = bss_is_pbss(bss); } else { + if (ssid->bssid_hint_set) + params.bssid_hint = ssid->bssid_hint; + params.ssid = ssid->ssid; params.ssid_len = ssid->ssid_len; params.pbss = (ssid->pbss != 2) ? ssid->pbss : 0; @@ -2482,13 +3042,12 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) params.beacon_int = wpa_s->conf->beacon_int; } - params.wpa_ie = wpa_ie; - params.wpa_ie_len = wpa_ie_len; params.pairwise_suite = cipher_pairwise; params.group_suite = cipher_group; + params.mgmt_group_suite = cipher_group_mgmt; params.key_mgmt_suite = wpa_s->key_mgmt; params.wpa_proto = wpa_s->wpa_proto; - params.auth_alg = algs; + wpa_s->auth_alg = params.auth_alg; params.mode = ssid->mode; params.bg_scan_period = ssid->bg_scan_period; for (i = 0; i < NUM_WEP_KEYS; i++) { @@ -2538,6 +3097,11 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) "MFP: require MFP"); params.mgmt_frame_protection = MGMT_FRAME_PROTECTION_REQUIRED; +#ifdef CONFIG_OWE + } else if (!rsn && (ssid->key_mgmt & WPA_KEY_MGMT_OWE) && + !ssid->owe_only) { + params.mgmt_frame_protection = NO_MGMT_FRAME_PROTECTION; +#endif /* CONFIG_OWE */ } } #endif /* CONFIG_IEEE80211W */ @@ -2580,6 +3144,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) if (wpas_p2p_handle_frequency_conflicts( wpa_s, params.freq.freq, ssid) < 0) { wpas_connect_work_done(wpa_s); + os_free(wpa_ie); return; } } @@ -2591,6 +3156,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) params.prev_bssid = prev_bssid; ret = wpa_drv_associate(wpa_s, ¶ms); + os_free(wpa_ie); if (ret < 0) { wpa_msg(wpa_s, MSG_INFO, "Association request to the driver " "failed"); @@ -2732,8 +3298,13 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, #ifdef CONFIG_MESH if (wpa_s->ifmsh) { + struct mesh_conf *mconf; + + mconf = wpa_s->ifmsh->mconf; wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_REMOVED "%s", wpa_s->ifname); + wpas_notify_mesh_group_removed(wpa_s, mconf->meshid, + mconf->meshid_len, reason_code); wpa_supplicant_leave_mesh(wpa_s); } #endif /* CONFIG_MESH */ @@ -2758,6 +3329,7 @@ static void wpa_supplicant_enable_one_network(struct wpa_supplicant *wpa_s, return; ssid->disabled = 0; + ssid->owe_transition_bss_select_count = 0; wpas_clear_temp_disabled(wpa_s, ssid, 1); wpas_notify_network_enabled_changed(wpa_s, ssid); @@ -2923,13 +3495,19 @@ void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s, wpas_notify_network_enabled_changed( wpa_s, other_ssid); } - if (wpa_s->current_ssid) + if (wpa_s->current_ssid) { + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); + } } else if (ssid->disabled != 2) { - if (ssid == wpa_s->current_ssid) + if (ssid == wpa_s->current_ssid) { + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); + } was_disabled = ssid->disabled; @@ -3015,6 +3593,9 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, wpa_s->disconnected = 0; wpa_s->reassociate = 1; + wpa_s->last_owe_group = 0; + if (ssid) + ssid->owe_transition_bss_select_count = 0; if (wpa_s->connect_without_scan || wpa_supplicant_fast_associate(wpa_s) != 1) { @@ -3232,6 +3813,41 @@ int wpa_supplicant_set_debug_params(struct wpa_global *global, int debug_level, } +#ifdef CONFIG_OWE +static int owe_trans_ssid_match(struct wpa_supplicant *wpa_s, const u8 *bssid, + const u8 *entry_ssid, size_t entry_ssid_len) +{ + const u8 *owe, *pos, *end; + u8 ssid_len; + struct wpa_bss *bss; + + /* Check network profile SSID aganst the SSID in the + * OWE Transition Mode element. */ + + bss = wpa_bss_get_bssid_latest(wpa_s, bssid); + if (!bss) + return 0; + + owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE); + if (!owe) + return 0; + + pos = owe + 6; + end = owe + 2 + owe[1]; + + if (end - pos < ETH_ALEN + 1) + return 0; + pos += ETH_ALEN; + ssid_len = *pos++; + if (end - pos < ssid_len || ssid_len > SSID_MAX_LEN) + return 0; + + return entry_ssid_len == ssid_len && + os_memcmp(pos, entry_ssid, ssid_len) == 0; +} +#endif /* CONFIG_OWE */ + + /** * wpa_supplicant_get_ssid - Get a pointer to the current network structure * @wpa_s: Pointer to wpa_supplicant data @@ -3280,6 +3896,15 @@ struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s) return entry; #endif /* CONFIG_WPS */ +#ifdef CONFIG_OWE + if (!wpas_network_disabled(wpa_s, entry) && + owe_trans_ssid_match(wpa_s, bssid, entry->ssid, + entry->ssid_len) && + (!entry->bssid_set || + os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) + return entry; +#endif /* CONFIG_OWE */ + if (!wpas_network_disabled(wpa_s, entry) && entry->bssid_set && entry->ssid_len == 0 && os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0) @@ -3387,16 +4012,6 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, } #endif /* CONFIG_TESTING_OPTIONS */ -#ifdef CONFIG_PEERKEY - if (wpa_s->wpa_state > WPA_ASSOCIATED && wpa_s->current_ssid && - wpa_s->current_ssid->peerkey && - !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) && - wpa_sm_rx_eapol_peerkey(wpa_s->wpa, src_addr, buf, len) == 1) { - wpa_dbg(wpa_s, MSG_DEBUG, "RSN: Processed PeerKey EAPOL-Key"); - return; - } -#endif /* CONFIG_PEERKEY */ - if (wpa_s->wpa_state < WPA_ASSOCIATED || (wpa_s->last_eapol_matches_bssid && #ifdef CONFIG_AP @@ -3507,6 +4122,8 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, os_memcpy(wpa_s->last_eapol_src, src_addr, ETH_ALEN); if (!wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) && + wpa_s->key_mgmt != WPA_KEY_MGMT_OWE && + wpa_s->key_mgmt != WPA_KEY_MGMT_DPP && eapol_sm_rx_eapol(wpa_s->eapol, src_addr, buf, len) > 0) return; wpa_drv_poll(wpa_s); @@ -3536,6 +4153,11 @@ int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s) wpa_supplicant_rx_eapol, wpa_s, 0); if (wpa_s->l2 == NULL) return -1; + + if (l2_packet_set_packet_filter(wpa_s->l2, + L2_PACKET_FILTER_PKTTYPE)) + wpa_dbg(wpa_s, MSG_DEBUG, + "Failed to attach pkt_type filter"); } else { const u8 *addr = wpa_drv_get_mac_addr(wpa_s); if (addr) @@ -3675,6 +4297,7 @@ wpa_supplicant_alloc(struct wpa_supplicant *parent) wpa_s->sched_scanning = 0; dl_list_init(&wpa_s->bss_tmp_disallowed); + dl_list_init(&wpa_s->fils_hlp_req); return wpa_s; } @@ -3702,8 +4325,11 @@ static int wpa_set_htcap_mcs(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_DEBUG, "set_htcap, ht_mcs -:%s:-", ht_mcs); for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) { + long v; + errno = 0; - long v = strtol(tmp, &end, 16); + v = strtol(tmp, &end, 16); + if (errno == 0) { wpa_msg(wpa_s, MSG_DEBUG, "htcap value[%i]: %ld end: %p tmp: %p", @@ -3813,18 +4439,10 @@ static int wpa_set_disable_ht40(struct wpa_supplicant *wpa_s, struct ieee80211_ht_capabilities *htcaps_mask, int disabled) { - /* Masking these out disables HT40 */ - le16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET | - HT_CAP_INFO_SHORT_GI40MHZ); - wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ht40: %d", disabled); - if (disabled) - htcaps->ht_capabilities_info &= ~msk; - else - htcaps->ht_capabilities_info |= msk; - - htcaps_mask->ht_capabilities_info |= msk; + set_disable_ht40(htcaps, disabled); + set_disable_ht40(htcaps_mask, 0); return 0; } @@ -4100,10 +4718,14 @@ static int wpas_fst_send_action_cb(void *ctx, const u8 *da, struct wpabuf *data) { struct wpa_supplicant *wpa_s = ctx; - WPA_ASSERT(os_memcmp(wpa_s->bssid, da, ETH_ALEN) == 0); + if (os_memcmp(wpa_s->bssid, da, ETH_ALEN) != 0) { + wpa_printf(MSG_INFO, "FST:%s:bssid=" MACSTR " != da=" MACSTR, + __func__, MAC2STR(wpa_s->bssid), MAC2STR(da)); + return -1; + } return wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, - wpa_s->own_addr, wpa_s->bssid, - wpabuf_head(data), wpabuf_len(data), + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(data), wpabuf_len(data), 0); } @@ -4291,7 +4913,7 @@ static void radio_work_free(struct wpa_radio_work *work) if (work->started) { work->wpa_s->radio->num_active_works--; wpa_dbg(work->wpa_s, MSG_DEBUG, - "radio_work_free('%s'@%p: num_active_works --> %u", + "radio_work_free('%s'@%p): num_active_works --> %u", work->type, work, work->wpa_s->radio->num_active_works); } @@ -4301,6 +4923,20 @@ static void radio_work_free(struct wpa_radio_work *work) } +static int radio_work_is_connect(struct wpa_radio_work *work) +{ + return os_strcmp(work->type, "sme-connect") == 0 || + os_strcmp(work->type, "connect") == 0; +} + + +static int radio_work_is_scan(struct wpa_radio_work *work) +{ + return os_strcmp(work->type, "scan") == 0 || + os_strcmp(work->type, "p2p-scan") == 0; +} + + static struct wpa_radio_work * radio_work_get_next_work(struct wpa_radio *radio) { struct wpa_radio_work *active_work = NULL; @@ -4330,8 +4966,7 @@ static struct wpa_radio_work * radio_work_get_next_work(struct wpa_radio *radio) return NULL; } - if (os_strcmp(active_work->type, "sme-connect") == 0 || - os_strcmp(active_work->type, "connect") == 0) { + if (radio_work_is_connect(active_work)) { /* * If the active work is either connect or sme-connect, * do not parallelize them with other radio works. @@ -4350,10 +4985,20 @@ static struct wpa_radio_work * radio_work_get_next_work(struct wpa_radio *radio) * If connect or sme-connect are enqueued, parallelize only * those operations ahead of them in the queue. */ - if (os_strcmp(tmp->type, "connect") == 0 || - os_strcmp(tmp->type, "sme-connect") == 0) + if (radio_work_is_connect(tmp)) break; + /* Serialize parallel scan and p2p_scan operations on the same + * interface since the driver_nl80211 mechanism for tracking + * scan cookies does not yet have support for this. */ + if (active_work->wpa_s == tmp->wpa_s && + radio_work_is_scan(active_work) && + radio_work_is_scan(tmp)) { + wpa_dbg(active_work->wpa_s, MSG_DEBUG, + "Do not start work '%s' when another work '%s' is already scheduled", + tmp->type, active_work->type); + continue; + } /* * Check that the radio works are distinct and * on different bands. @@ -4475,6 +5120,22 @@ void radio_remove_works(struct wpa_supplicant *wpa_s, } +void radio_remove_pending_work(struct wpa_supplicant *wpa_s, void *ctx) +{ + struct wpa_radio_work *work; + struct wpa_radio *radio = wpa_s->radio; + + dl_list_for_each(work, &radio->work, struct wpa_radio_work, list) { + if (work->ctx != ctx) + continue; + wpa_dbg(wpa_s, MSG_DEBUG, "Free pending radio work '%s'@%p%s", + work->type, work, work->started ? " (started)" : ""); + radio_work_free(work); + break; + } +} + + static void radio_remove_interface(struct wpa_supplicant *wpa_s) { struct wpa_radio *radio = wpa_s->radio; @@ -4627,7 +5288,7 @@ radio_work_pending(struct wpa_supplicant *wpa_s, const char *type) static int wpas_init_driver(struct wpa_supplicant *wpa_s, - struct wpa_interface *iface) + const struct wpa_interface *iface) { const char *ifname, *driver, *rn; @@ -4675,11 +5336,47 @@ next_driver: } +#ifdef CONFIG_GAS_SERVER + +static void wpas_gas_server_tx_status(struct wpa_supplicant *wpa_s, + unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, + const u8 *data, size_t data_len, + enum offchannel_send_action_result result) +{ + wpa_printf(MSG_DEBUG, "GAS: TX status: freq=%u dst=" MACSTR + " result=%s", + freq, MAC2STR(dst), + result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" : + (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" : + "FAILED")); + gas_server_tx_status(wpa_s->gas_server, dst, data, data_len, + result == OFFCHANNEL_SEND_ACTION_SUCCESS); +} + + +static void wpas_gas_server_tx(void *ctx, int freq, const u8 *da, + struct wpabuf *buf, unsigned int wait_time) +{ + struct wpa_supplicant *wpa_s = ctx; + const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + if (wait_time > wpa_s->max_remain_on_chan) + wait_time = wpa_s->max_remain_on_chan; + + offchannel_send_action(wpa_s, freq, da, wpa_s->own_addr, broadcast, + wpabuf_head(buf), wpabuf_len(buf), + wait_time, wpas_gas_server_tx_status, 0); +} + +#endif /* CONFIG_GAS_SERVER */ + static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, - struct wpa_interface *iface) + const struct wpa_interface *iface) { struct wpa_driver_capa capa; int capa_res; + u8 dfs_domain; wpa_printf(MSG_DEBUG, "Initializing interface '%s' conf '%s' driver " "'%s' ctrl_interface '%s' bridge '%s'", iface->ifname, @@ -4709,7 +5406,13 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, return -1; } wpa_s->confanother = os_rel2abs_path(iface->confanother); - wpa_config_read(wpa_s->confanother, wpa_s->conf); + if (wpa_s->confanother && + !wpa_config_read(wpa_s->confanother, wpa_s->conf)) { + wpa_printf(MSG_ERROR, + "Failed to read or parse configuration '%s'.", + wpa_s->confanother); + return -1; + } /* * Override ctrl_interface and driver_param if set on command @@ -4807,7 +5510,8 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, wpa_s->hw.modes = wpa_drv_get_hw_feature_data(wpa_s, &wpa_s->hw.num_modes, - &wpa_s->hw.flags); + &wpa_s->hw.flags, + &dfs_domain); if (wpa_s->hw.modes) { u16 i; @@ -4869,8 +5573,6 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, */ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) wpa_s->p2p_mgmt = iface->p2p_mgmt; - else - iface->p2p_mgmt = 1; if (wpa_s->num_multichan_concurrent == 0) wpa_s->num_multichan_concurrent = 1; @@ -4879,10 +5581,7 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, return -1; #ifdef CONFIG_TDLS - if ((!iface->p2p_mgmt || - !(wpa_s->drv_flags & - WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) && - wpa_tdls_init(wpa_s->wpa)) + if (!iface->p2p_mgmt && wpa_tdls_init(wpa_s->wpa)) return -1; #endif /* CONFIG_TDLS */ @@ -4917,6 +5616,19 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, if (wpas_wps_init(wpa_s)) return -1; +#ifdef CONFIG_GAS_SERVER + wpa_s->gas_server = gas_server_init(wpa_s, wpas_gas_server_tx); + if (!wpa_s->gas_server) { + wpa_printf(MSG_ERROR, "Failed to initialize GAS server"); + return -1; + } +#endif /* CONFIG_GAS_SERVER */ + +#ifdef CONFIG_DPP + if (wpas_dpp_init(wpa_s) < 0) + return -1; +#endif /* CONFIG_DPP */ + if (wpa_supplicant_init_eapol(wpa_s) < 0) return -1; wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol); @@ -4941,7 +5653,9 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, return -1; } - if (iface->p2p_mgmt && wpas_p2p_init(wpa_s->global, wpa_s) < 0) { + if ((!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) || + wpa_s->p2p_mgmt) && + wpas_p2p_init(wpa_s->global, wpa_s) < 0) { wpa_msg(wpa_s, MSG_ERROR, "Failed to init P2P"); return -1; } @@ -4949,6 +5663,12 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, if (wpa_bss_init(wpa_s) < 0) return -1; +#ifdef CONFIG_PMKSA_CACHE_EXTERNAL +#ifdef CONFIG_MESH + dl_list_init(&wpa_s->mesh_external_pmksa_cache); +#endif /* CONFIG_MESH */ +#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ + /* * Set Wake-on-WLAN triggers, if configured. * Note: We don't restore/remove the triggers on shutdown (it doesn't @@ -4960,8 +5680,8 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, #ifdef CONFIG_EAP_PROXY { size_t len; - wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, wpa_s->imsi, - &len); + wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, -1, + wpa_s->imsi, &len); if (wpa_s->mnc_len > 0) { wpa_s->imsi[len] = '\0'; wpa_printf(MSG_DEBUG, "eap_proxy: IMSI %s (MNC length %d)", @@ -4986,6 +5706,17 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, hs20_init(wpa_s); #endif /* CONFIG_HS20 */ #ifdef CONFIG_MBO + if (wpa_s->conf->oce) { + if ((wpa_s->conf->oce & OCE_STA) && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA)) + wpa_s->enable_oce = OCE_STA; + if ((wpa_s->conf->oce & OCE_STA_CFON) && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA_CFON)) { + /* TODO: Need to add STA-CFON support */ + wpa_printf(MSG_ERROR, + "OCE STA-CFON feature is not yet supported"); + } + } wpas_mbo_update_non_pref_chan(wpa_s, wpa_s->conf->non_pref_chan); #endif /* CONFIG_MBO */ @@ -5250,6 +5981,7 @@ int wpa_supplicant_remove_iface(struct wpa_global *global, #ifdef CONFIG_MESH unsigned int mesh_if_created = wpa_s->mesh_if_created; char *ifname = NULL; + struct wpa_supplicant *parent = wpa_s->parent; #endif /* CONFIG_MESH */ /* Remove interface from the global list of interfaces */ @@ -5285,7 +6017,7 @@ int wpa_supplicant_remove_iface(struct wpa_global *global, #ifdef CONFIG_MESH if (mesh_if_created) { - wpa_drv_if_remove(global->ifaces, WPA_IF_MESH, ifname); + wpa_drv_if_remove(parent, WPA_IF_MESH, ifname); os_free(ifname); } #endif /* CONFIG_MESH */ @@ -5649,6 +6381,16 @@ void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s) if (wpa_s->conf->changed_parameters & CFG_CHANGED_SCHED_SCAN_PLANS) wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans); + if (wpa_s->conf->changed_parameters & CFG_CHANGED_WOWLAN_TRIGGERS) { + struct wpa_driver_capa capa; + int res = wpa_drv_get_capa(wpa_s, &capa); + + if (res == 0 && wpas_set_wowlan_triggers(wpa_s, &capa) < 0) + wpa_printf(MSG_ERROR, + "Failed to update wowlan_triggers to '%s'", + wpa_s->conf->wowlan_triggers); + } + #ifdef CONFIG_WPS wpas_wps_update_config(wpa_s); #endif /* CONFIG_WPS */ @@ -5808,6 +6550,35 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid) } +#ifdef CONFIG_FILS +void fils_connection_failure(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + const u8 *realm, *username, *rrk; + size_t realm_len, username_len, rrk_len; + u16 next_seq_num; + + if (!ssid || !ssid->eap.erp || !wpa_key_mgmt_fils(ssid->key_mgmt) || + eapol_sm_get_erp_info(wpa_s->eapol, &ssid->eap, + &username, &username_len, + &realm, &realm_len, &next_seq_num, + &rrk, &rrk_len) != 0 || + !realm) + return; + + wpa_hexdump_ascii(MSG_DEBUG, + "FILS: Store last connection failure realm", + realm, realm_len); + os_free(wpa_s->last_con_fail_realm); + wpa_s->last_con_fail_realm = os_malloc(realm_len); + if (wpa_s->last_con_fail_realm) { + wpa_s->last_con_fail_realm_len = realm_len; + os_memcpy(wpa_s->last_con_fail_realm, realm, realm_len); + } +} +#endif /* CONFIG_FILS */ + + int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s) { return wpa_s->conf->ap_scan == 2 || @@ -5878,6 +6649,7 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, case WPA_CTRL_REQ_SIM: str_clear_free(eap->external_sim_resp); eap->external_sim_resp = os_strdup(value); + eap->pending_req_sim = 0; break; case WPA_CTRL_REQ_PSK_PASSPHRASE: if (wpa_config_set(ssid, "psk", value, 0) < 0) @@ -5946,6 +6718,7 @@ int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set && (!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk && + !(wpa_key_mgmt_sae(ssid->key_mgmt) && ssid->sae_password) && !ssid->mem_only_psk) return 1; @@ -6130,6 +6903,7 @@ void wpas_request_connection(struct wpa_supplicant *wpa_s) wpa_s->extra_blacklist_count = 0; wpa_s->disconnected = 0; wpa_s->reassociate = 1; + wpa_s->last_owe_group = 0; if (wpa_supplicant_fast_associate(wpa_s) != 1) wpa_supplicant_req_scan(wpa_s, 0, 0); @@ -6256,489 +7030,6 @@ int get_shared_radio_freqs(struct wpa_supplicant *wpa_s, } -static void wpas_rrm_neighbor_rep_timeout_handler(void *data, void *user_ctx) -{ - struct rrm_data *rrm = data; - - if (!rrm->notify_neighbor_rep) { - wpa_printf(MSG_ERROR, - "RRM: Unexpected neighbor report timeout"); - return; - } - - wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report - NONE"); - rrm->notify_neighbor_rep(rrm->neighbor_rep_cb_ctx, NULL); - - rrm->notify_neighbor_rep = NULL; - rrm->neighbor_rep_cb_ctx = NULL; -} - - -/* - * wpas_rrm_reset - Clear and reset all RRM data in wpa_supplicant - * @wpa_s: Pointer to wpa_supplicant - */ -void wpas_rrm_reset(struct wpa_supplicant *wpa_s) -{ - wpa_s->rrm.rrm_used = 0; - - eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm, - NULL); - if (wpa_s->rrm.notify_neighbor_rep) - wpas_rrm_neighbor_rep_timeout_handler(&wpa_s->rrm, NULL); - wpa_s->rrm.next_neighbor_rep_token = 1; -} - - -/* - * wpas_rrm_process_neighbor_rep - Handle incoming neighbor report - * @wpa_s: Pointer to wpa_supplicant - * @report: Neighbor report buffer, prefixed by a 1-byte dialog token - * @report_len: Length of neighbor report buffer - */ -void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s, - const u8 *report, size_t report_len) -{ - struct wpabuf *neighbor_rep; - - wpa_hexdump(MSG_DEBUG, "RRM: New Neighbor Report", report, report_len); - if (report_len < 1) - return; - - if (report[0] != wpa_s->rrm.next_neighbor_rep_token - 1) { - wpa_printf(MSG_DEBUG, - "RRM: Discarding neighbor report with token %d (expected %d)", - report[0], wpa_s->rrm.next_neighbor_rep_token - 1); - return; - } - - eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm, - NULL); - - if (!wpa_s->rrm.notify_neighbor_rep) { - wpa_printf(MSG_ERROR, "RRM: Unexpected neighbor report"); - return; - } - - /* skipping the first byte, which is only an id (dialog token) */ - neighbor_rep = wpabuf_alloc(report_len - 1); - if (neighbor_rep == NULL) - return; - wpabuf_put_data(neighbor_rep, report + 1, report_len - 1); - wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report (token = %d)", - report[0]); - wpa_s->rrm.notify_neighbor_rep(wpa_s->rrm.neighbor_rep_cb_ctx, - neighbor_rep); - wpa_s->rrm.notify_neighbor_rep = NULL; - wpa_s->rrm.neighbor_rep_cb_ctx = NULL; -} - - -#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS) -/* Workaround different, undefined for Windows, error codes used here */ -#define ENOTCONN -1 -#define EOPNOTSUPP -1 -#define ECANCELED -1 -#endif - -/* Measurement Request element + Location Subject + Maximum Age subelement */ -#define MEASURE_REQUEST_LCI_LEN (3 + 1 + 4) -/* Measurement Request element + Location Civic Request */ -#define MEASURE_REQUEST_CIVIC_LEN (3 + 5) - - -/** - * wpas_rrm_send_neighbor_rep_request - Request a neighbor report from our AP - * @wpa_s: Pointer to wpa_supplicant - * @ssid: if not null, this is sent in the request. Otherwise, no SSID IE - * is sent in the request. - * @lci: if set, neighbor request will include LCI request - * @civic: if set, neighbor request will include civic location request - * @cb: Callback function to be called once the requested report arrives, or - * timed out after RRM_NEIGHBOR_REPORT_TIMEOUT seconds. - * In the former case, 'neighbor_rep' is a newly allocated wpabuf, and it's - * the requester's responsibility to free it. - * In the latter case NULL will be sent in 'neighbor_rep'. - * @cb_ctx: Context value to send the callback function - * Returns: 0 in case of success, negative error code otherwise - * - * In case there is a previous request which has not been answered yet, the - * new request fails. The caller may retry after RRM_NEIGHBOR_REPORT_TIMEOUT. - * Request must contain a callback function. - */ -int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, - const struct wpa_ssid_value *ssid, - int lci, int civic, - void (*cb)(void *ctx, - struct wpabuf *neighbor_rep), - void *cb_ctx) -{ - struct wpabuf *buf; - const u8 *rrm_ie; - - if (wpa_s->wpa_state != WPA_COMPLETED || wpa_s->current_ssid == NULL) { - wpa_printf(MSG_DEBUG, "RRM: No connection, no RRM."); - return -ENOTCONN; - } - - if (!wpa_s->rrm.rrm_used) { - wpa_printf(MSG_DEBUG, "RRM: No RRM in current connection."); - return -EOPNOTSUPP; - } - - rrm_ie = wpa_bss_get_ie(wpa_s->current_bss, - WLAN_EID_RRM_ENABLED_CAPABILITIES); - if (!rrm_ie || !(wpa_s->current_bss->caps & IEEE80211_CAP_RRM) || - !(rrm_ie[2] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) { - wpa_printf(MSG_DEBUG, - "RRM: No network support for Neighbor Report."); - return -EOPNOTSUPP; - } - - if (!cb) { - wpa_printf(MSG_DEBUG, - "RRM: Neighbor Report request must provide a callback."); - return -EINVAL; - } - - /* Refuse if there's a live request */ - if (wpa_s->rrm.notify_neighbor_rep) { - wpa_printf(MSG_DEBUG, - "RRM: Currently handling previous Neighbor Report."); - return -EBUSY; - } - - /* 3 = action category + action code + dialog token */ - buf = wpabuf_alloc(3 + (ssid ? 2 + ssid->ssid_len : 0) + - (lci ? 2 + MEASURE_REQUEST_LCI_LEN : 0) + - (civic ? 2 + MEASURE_REQUEST_CIVIC_LEN : 0)); - if (buf == NULL) { - wpa_printf(MSG_DEBUG, - "RRM: Failed to allocate Neighbor Report Request"); - return -ENOMEM; - } - - wpa_printf(MSG_DEBUG, "RRM: Neighbor report request (for %s), token=%d", - (ssid ? wpa_ssid_txt(ssid->ssid, ssid->ssid_len) : ""), - wpa_s->rrm.next_neighbor_rep_token); - - wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); - wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_REQUEST); - wpabuf_put_u8(buf, wpa_s->rrm.next_neighbor_rep_token); - if (ssid) { - wpabuf_put_u8(buf, WLAN_EID_SSID); - wpabuf_put_u8(buf, ssid->ssid_len); - wpabuf_put_data(buf, ssid->ssid, ssid->ssid_len); - } - - if (lci) { - /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */ - wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); - wpabuf_put_u8(buf, MEASURE_REQUEST_LCI_LEN); - - /* - * Measurement token; nonzero number that is unique among the - * Measurement Request elements in a particular frame. - */ - wpabuf_put_u8(buf, 1); /* Measurement Token */ - - /* - * Parallel, Enable, Request, and Report bits are 0, Duration is - * reserved. - */ - wpabuf_put_u8(buf, 0); /* Measurement Request Mode */ - wpabuf_put_u8(buf, MEASURE_TYPE_LCI); /* Measurement Type */ - - /* IEEE P802.11-REVmc/D5.0 9.4.2.21.10 - LCI request */ - /* Location Subject */ - wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE); - - /* Optional Subelements */ - /* - * IEEE P802.11-REVmc/D5.0 Figure 9-170 - * The Maximum Age subelement is required, otherwise the AP can - * send only data that was determined after receiving the - * request. Setting it here to unlimited age. - */ - wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE); - wpabuf_put_u8(buf, 2); - wpabuf_put_le16(buf, 0xffff); - } - - if (civic) { - /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */ - wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); - wpabuf_put_u8(buf, MEASURE_REQUEST_CIVIC_LEN); - - /* - * Measurement token; nonzero number that is unique among the - * Measurement Request elements in a particular frame. - */ - wpabuf_put_u8(buf, 2); /* Measurement Token */ - - /* - * Parallel, Enable, Request, and Report bits are 0, Duration is - * reserved. - */ - wpabuf_put_u8(buf, 0); /* Measurement Request Mode */ - /* Measurement Type */ - wpabuf_put_u8(buf, MEASURE_TYPE_LOCATION_CIVIC); - - /* IEEE P802.11-REVmc/D5.0 9.4.2.21.14: - * Location Civic request */ - /* Location Subject */ - wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE); - wpabuf_put_u8(buf, 0); /* Civic Location Type: IETF RFC 4776 */ - /* Location Service Interval Units: Seconds */ - wpabuf_put_u8(buf, 0); - /* Location Service Interval: 0 - Only one report is requested - */ - wpabuf_put_le16(buf, 0); - /* No optional subelements */ - } - - wpa_s->rrm.next_neighbor_rep_token++; - - if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, - wpa_s->own_addr, wpa_s->bssid, - wpabuf_head(buf), wpabuf_len(buf), 0) < 0) { - wpa_printf(MSG_DEBUG, - "RRM: Failed to send Neighbor Report Request"); - wpabuf_free(buf); - return -ECANCELED; - } - - wpa_s->rrm.neighbor_rep_cb_ctx = cb_ctx; - wpa_s->rrm.notify_neighbor_rep = cb; - eloop_register_timeout(RRM_NEIGHBOR_REPORT_TIMEOUT, 0, - wpas_rrm_neighbor_rep_timeout_handler, - &wpa_s->rrm, NULL); - - wpabuf_free(buf); - return 0; -} - - -static struct wpabuf * wpas_rrm_build_lci_report(struct wpa_supplicant *wpa_s, - const u8 *request, size_t len, - struct wpabuf *report) -{ - u8 token, type, subject; - u16 max_age = 0; - struct os_reltime t, diff; - unsigned long diff_l; - u8 *ptoken; - const u8 *subelem; - - if (!wpa_s->lci || len < 3 + 4) - return report; - - token = *request++; - /* Measurement request mode isn't used */ - request++; - type = *request++; - subject = *request++; - - wpa_printf(MSG_DEBUG, - "Measurement request token %u type %u location subject %u", - token, type, subject); - - if (type != MEASURE_TYPE_LCI || subject != LOCATION_SUBJECT_REMOTE) { - wpa_printf(MSG_INFO, - "Not building LCI report - bad type or location subject"); - return report; - } - - /* Subelements are formatted exactly like elements */ - subelem = get_ie(request, len, LCI_REQ_SUBELEM_MAX_AGE); - if (subelem && subelem[1] == 2) - max_age = WPA_GET_LE16(subelem + 2); - - if (os_get_reltime(&t)) - return report; - - os_reltime_sub(&t, &wpa_s->lci_time, &diff); - /* LCI age is calculated in 10th of a second units. */ - diff_l = diff.sec * 10 + diff.usec / 100000; - - if (max_age != 0xffff && max_age < diff_l) - return report; - - if (wpabuf_resize(&report, 2 + wpabuf_len(wpa_s->lci))) - return report; - - wpabuf_put_u8(report, WLAN_EID_MEASURE_REPORT); - wpabuf_put_u8(report, wpabuf_len(wpa_s->lci)); - /* We'll override user's measurement token */ - ptoken = wpabuf_put(report, 0); - wpabuf_put_buf(report, wpa_s->lci); - *ptoken = token; - - return report; -} - - -void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s, - const u8 *src, - const u8 *frame, size_t len) -{ - struct wpabuf *buf, *report; - u8 token; - const u8 *ie, *end; - - if (wpa_s->wpa_state != WPA_COMPLETED) { - wpa_printf(MSG_INFO, - "RRM: Ignoring radio measurement request: Not associated"); - return; - } - - if (!wpa_s->rrm.rrm_used) { - wpa_printf(MSG_INFO, - "RRM: Ignoring radio measurement request: Not RRM network"); - return; - } - - if (len < 3) { - wpa_printf(MSG_INFO, - "RRM: Ignoring too short radio measurement request"); - return; - } - - end = frame + len; - - token = *frame++; - - /* Ignore number of repetitions because it's not used in LCI request */ - frame += 2; - - report = NULL; - while ((ie = get_ie(frame, end - frame, WLAN_EID_MEASURE_REQUEST)) && - ie[1] >= 3) { - u8 msmt_type; - - msmt_type = ie[4]; - wpa_printf(MSG_DEBUG, "RRM request %d", msmt_type); - - switch (msmt_type) { - case MEASURE_TYPE_LCI: - report = wpas_rrm_build_lci_report(wpa_s, ie + 2, ie[1], - report); - break; - default: - wpa_printf(MSG_INFO, - "RRM: Unsupported radio measurement request %d", - msmt_type); - break; - } - - frame = ie + ie[1] + 2; - } - - if (!report) - return; - - buf = wpabuf_alloc(3 + wpabuf_len(report)); - if (!buf) { - wpabuf_free(report); - return; - } - - wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); - wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REPORT); - wpabuf_put_u8(buf, token); - - wpabuf_put_buf(buf, report); - wpabuf_free(report); - - if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, src, - wpa_s->own_addr, wpa_s->bssid, - wpabuf_head(buf), wpabuf_len(buf), 0)) { - wpa_printf(MSG_ERROR, - "RRM: Radio measurement report failed: Sending Action frame failed"); - } - wpabuf_free(buf); -} - - -void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s, - const u8 *src, - const u8 *frame, size_t len, - int rssi) -{ - struct wpabuf *buf; - const struct rrm_link_measurement_request *req; - struct rrm_link_measurement_report report; - - if (wpa_s->wpa_state != WPA_COMPLETED) { - wpa_printf(MSG_INFO, - "RRM: Ignoring link measurement request. Not associated"); - return; - } - - if (!wpa_s->rrm.rrm_used) { - wpa_printf(MSG_INFO, - "RRM: Ignoring link measurement request. Not RRM network"); - return; - } - - if (!(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION)) { - wpa_printf(MSG_INFO, - "RRM: Measurement report failed. TX power insertion not supported"); - return; - } - - req = (const struct rrm_link_measurement_request *) frame; - if (len < sizeof(*req)) { - wpa_printf(MSG_INFO, - "RRM: Link measurement report failed. Request too short"); - return; - } - - os_memset(&report, 0, sizeof(report)); - report.tpc.eid = WLAN_EID_TPC_REPORT; - report.tpc.len = 2; - report.rsni = 255; /* 255 indicates that RSNI is not available */ - report.dialog_token = req->dialog_token; - - /* - * It's possible to estimate RCPI based on RSSI in dBm. This - * calculation will not reflect the correct value for high rates, - * but it's good enough for Action frames which are transmitted - * with up to 24 Mbps rates. - */ - if (!rssi) - report.rcpi = 255; /* not available */ - else if (rssi < -110) - report.rcpi = 0; - else if (rssi > 0) - report.rcpi = 220; - else - report.rcpi = (rssi + 110) * 2; - - /* action_category + action_code */ - buf = wpabuf_alloc(2 + sizeof(report)); - if (buf == NULL) { - wpa_printf(MSG_ERROR, - "RRM: Link measurement report failed. Buffer allocation failed"); - return; - } - - wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); - wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REPORT); - wpabuf_put_data(buf, &report, sizeof(report)); - wpa_hexdump(MSG_DEBUG, "RRM: Link measurement report:", - wpabuf_head(buf), wpabuf_len(buf)); - - if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, src, - wpa_s->own_addr, wpa_s->bssid, - wpabuf_head(buf), wpabuf_len(buf), 0)) { - wpa_printf(MSG_ERROR, - "RRM: Link measurement report failed. Send action failed"); - } - wpabuf_free(buf); -} - - struct wpa_supplicant * wpas_vendor_elem(struct wpa_supplicant *wpa_s, enum wpa_vendor_elem_frame frame) { @@ -6852,18 +7143,56 @@ wpa_bss_tmp_disallowed * wpas_get_disallowed_bss(struct wpa_supplicant *wpa_s, } +static int wpa_set_driver_tmp_disallow_list(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss_tmp_disallowed *tmp; + unsigned int num_bssid = 0; + u8 *bssids; + int ret; + + bssids = os_malloc(dl_list_len(&wpa_s->bss_tmp_disallowed) * ETH_ALEN); + if (!bssids) + return -1; + dl_list_for_each(tmp, &wpa_s->bss_tmp_disallowed, + struct wpa_bss_tmp_disallowed, list) { + os_memcpy(&bssids[num_bssid * ETH_ALEN], tmp->bssid, + ETH_ALEN); + num_bssid++; + } + ret = wpa_drv_set_bssid_blacklist(wpa_s, num_bssid, bssids); + os_free(bssids); + return ret; +} + + +static void wpa_bss_tmp_disallow_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wpa_bss_tmp_disallowed *tmp, *bss = timeout_ctx; + + /* Make sure the bss is not already freed */ + dl_list_for_each(tmp, &wpa_s->bss_tmp_disallowed, + struct wpa_bss_tmp_disallowed, list) { + if (bss == tmp) { + dl_list_del(&tmp->list); + os_free(tmp); + wpa_set_driver_tmp_disallow_list(wpa_s); + break; + } + } +} + + void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid, unsigned int sec) { struct wpa_bss_tmp_disallowed *bss; - struct os_reltime until; - - os_get_reltime(&until); - until.sec += sec; bss = wpas_get_disallowed_bss(wpa_s, bssid); if (bss) { - bss->disallowed_until = until; + eloop_cancel_timeout(wpa_bss_tmp_disallow_timeout, wpa_s, bss); + eloop_register_timeout(sec, 0, wpa_bss_tmp_disallow_timeout, + wpa_s, bss); return; } @@ -6874,27 +7203,20 @@ void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid, return; } - bss->disallowed_until = until; os_memcpy(bss->bssid, bssid, ETH_ALEN); dl_list_add(&wpa_s->bss_tmp_disallowed, &bss->list); + wpa_set_driver_tmp_disallow_list(wpa_s); + eloop_register_timeout(sec, 0, wpa_bss_tmp_disallow_timeout, + wpa_s, bss); } int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid) { struct wpa_bss_tmp_disallowed *bss = NULL, *tmp, *prev; - struct os_reltime now, age; - - os_get_reltime(&now); dl_list_for_each_safe(tmp, prev, &wpa_s->bss_tmp_disallowed, struct wpa_bss_tmp_disallowed, list) { - if (!os_reltime_before(&now, &tmp->disallowed_until)) { - /* This BSS is not disallowed anymore */ - dl_list_del(&tmp->list); - os_free(tmp); - continue; - } if (os_memcmp(bssid, tmp->bssid, ETH_ALEN) == 0) { bss = tmp; break; @@ -6903,9 +7225,5 @@ int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid) if (!bss) return 0; - os_reltime_sub(&bss->disallowed_until, &now, &age); - wpa_printf(MSG_DEBUG, - "BSS " MACSTR " disabled for %ld.%0ld seconds", - MAC2STR(bss->bssid), age.sec, age.usec); return 1; } |