diff options
Diffstat (limited to 'src')
90 files changed, 6397 insertions, 847 deletions
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 04535a1..419bf7c 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -131,6 +131,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) bss->fils_hlp_wait_time = 30; bss->dhcp_server_port = DHCP_SERVER_PORT; bss->dhcp_relay_port = DHCP_SERVER_PORT; + bss->fils_discovery_min_int = 20; #endif /* CONFIG_FILS */ bss->broadcast_deauth = 1; @@ -268,6 +269,10 @@ struct hostapd_config * hostapd_config_defaults(void) conf->he_op.he_bss_color_disabled = 1; conf->he_op.he_bss_color_partial = 0; conf->he_op.he_bss_color = 1; + conf->he_6ghz_max_mpdu = 2; + conf->he_6ghz_max_ampdu_len_exp = 7; + conf->he_6ghz_rx_ant_pat = 1; + conf->he_6ghz_tx_ant_pat = 1; #endif /* CONFIG_IEEE80211AX */ /* The third octet of the country string uses an ASCII space character @@ -955,6 +960,10 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) } #endif /* CONFIG_AIRTIME_POLICY */ +#ifdef CONFIG_PASN + os_free(conf->pasn_groups); +#endif /* CONFIG_PASN */ + os_free(conf); } @@ -1150,10 +1159,54 @@ static bool hostapd_sae_pk_password_without_pk(struct hostapd_bss_config *bss) #endif /* CONFIG_SAE_PK */ +static bool hostapd_config_check_bss_6g(struct hostapd_bss_config *bss) +{ + if (bss->wpa != WPA_PROTO_RSN) { + wpa_printf(MSG_ERROR, + "Pre-RSNA security methods are not allowed in 6 GHz"); + return false; + } + + if (bss->ieee80211w != MGMT_FRAME_PROTECTION_REQUIRED) { + wpa_printf(MSG_ERROR, + "Management frame protection is required in 6 GHz"); + return false; + } + + if (bss->wpa_key_mgmt & (WPA_KEY_MGMT_PSK | + WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_PSK_SHA256)) { + wpa_printf(MSG_ERROR, "Invalid AKM suite for 6 GHz"); + return false; + } + + if (bss->rsn_pairwise & (WPA_CIPHER_WEP40 | + WPA_CIPHER_WEP104 | + WPA_CIPHER_TKIP)) { + wpa_printf(MSG_ERROR, + "Invalid pairwise cipher suite for 6 GHz"); + return false; + } + + if (bss->wpa_group & (WPA_CIPHER_WEP40 | + WPA_CIPHER_WEP104 | + WPA_CIPHER_TKIP)) { + wpa_printf(MSG_ERROR, "Invalid group cipher suite for 6 GHz"); + return false; + } + + return true; +} + + static int hostapd_config_check_bss(struct hostapd_bss_config *bss, struct hostapd_config *conf, int full_config) { + if (full_config && is_6ghz_op_class(conf->op_class) && + !hostapd_config_check_bss_6g(bss)) + return -1; + if (full_config && bss->ieee802_1x && !bss->eap_server && !bss->radius->auth_servers) { wpa_printf(MSG_ERROR, "Invalid IEEE 802.1X configuration (no " @@ -1229,7 +1282,7 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, if (full_config && conf->ieee80211n && conf->hw_mode == HOSTAPD_MODE_IEEE80211B) { - bss->disable_11n = 1; + bss->disable_11n = true; wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) in 11b mode is not " "allowed, disabling HT capabilities"); } @@ -1237,7 +1290,7 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, #ifdef CONFIG_WEP if (full_config && conf->ieee80211n && bss->ssid.security_policy == SECURITY_STATIC_WEP) { - bss->disable_11n = 1; + bss->disable_11n = true; wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WEP is not " "allowed, disabling HT capabilities"); } @@ -1248,7 +1301,7 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256))) { - bss->disable_11n = 1; + bss->disable_11n = true; wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WPA/WPA2 " "requires CCMP/GCMP to be enabled, disabling HT " "capabilities"); @@ -1258,7 +1311,7 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, #ifdef CONFIG_WEP if (full_config && conf->ieee80211ac && bss->ssid.security_policy == SECURITY_STATIC_WEP) { - bss->disable_11ac = 1; + bss->disable_11ac = true; wpa_printf(MSG_ERROR, "VHT (IEEE 802.11ac) with WEP is not allowed, disabling VHT capabilities"); } @@ -1269,12 +1322,33 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256))) { - bss->disable_11ac = 1; + bss->disable_11ac = true; wpa_printf(MSG_ERROR, "VHT (IEEE 802.11ac) with WPA/WPA2 requires CCMP/GCMP to be enabled, disabling VHT capabilities"); } #endif /* CONFIG_IEEE80211AC */ +#ifdef CONFIG_IEEE80211AX +#ifdef CONFIG_WEP + if (full_config && conf->ieee80211ax && + bss->ssid.security_policy == SECURITY_STATIC_WEP) { + bss->disable_11ax = true; + wpa_printf(MSG_ERROR, + "HE (IEEE 802.11ax) with WEP is not allowed, disabling HE capabilities"); + } +#endif /* CONFIG_WEP */ + + if (full_config && conf->ieee80211ax && bss->wpa && + !(bss->wpa_pairwise & WPA_CIPHER_CCMP) && + !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | + WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256))) + { + bss->disable_11ax = true; + wpa_printf(MSG_ERROR, + "HE (IEEE 802.11ax) with WPA/WPA2 requires CCMP/GCMP to be enabled, disabling HE capabilities"); + } +#endif /* CONFIG_IEEE80211AX */ + #ifdef CONFIG_WPS if (full_config && bss->wps_state && bss->ignore_broadcast_ssid) { wpa_printf(MSG_INFO, "WPS: ignore_broadcast_ssid " diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index bada04c..88200c6 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -531,8 +531,9 @@ struct hostapd_bss_config { #define TDLS_PROHIBIT BIT(0) #define TDLS_PROHIBIT_CHAN_SWITCH BIT(1) int tdls; - int disable_11n; - int disable_11ac; + bool disable_11n; + bool disable_11ac; + bool disable_11ax; /* IEEE 802.11v */ int time_advertisement; @@ -729,12 +730,16 @@ struct hostapd_bss_config { unsigned int fils_hlp_wait_time; u16 dhcp_server_port; u16 dhcp_relay_port; + u32 fils_discovery_min_int; + u32 fils_discovery_max_int; #endif /* CONFIG_FILS */ int multicast_to_unicast; int broadcast_deauth; + int notify_mgmt_frames; + #ifdef CONFIG_DPP char *dpp_name; char *dpp_mud_url; @@ -861,6 +866,20 @@ struct hostapd_bss_config { */ u8 mka_psk_set; #endif /* CONFIG_MACSEC */ + +#ifdef CONFIG_PASN +#ifdef CONFIG_TESTING_OPTIONS + /* + * Normally, KDK should be derived if and only if both sides support + * secure LTF. Allow forcing KDK derivation for testing purposes. + */ + int force_kdk_derivation; +#endif /* CONFIG_TESTING_OPTIONS */ + + int *pasn_groups; +#endif /* CONFIG_PASN */ + + unsigned int unsol_bcast_probe_resp_interval; }; /** @@ -893,8 +912,8 @@ struct spatial_reuse { u8 non_srg_obss_pd_max_offset; u8 srg_obss_pd_min_offset; u8 srg_obss_pd_max_offset; - u8 srg_obss_color_bitmap; - u8 srg_obss_color_partial_bitmap; + u8 srg_bss_color_bitmap[8]; + u8 srg_partial_bssid_bitmap[8]; }; /** @@ -1029,6 +1048,10 @@ struct hostapd_config { u8 he_oper_chwidth; u8 he_oper_centr_freq_seg0_idx; u8 he_oper_centr_freq_seg1_idx; + u8 he_6ghz_max_mpdu; + u8 he_6ghz_max_ampdu_len_exp; + u8 he_6ghz_rx_ant_pat; + u8 he_6ghz_tx_ant_pat; #endif /* CONFIG_IEEE80211AX */ /* VHT enable/disable config from CHAN_SWITCH */ @@ -1036,8 +1059,14 @@ struct hostapd_config { #define CH_SWITCH_VHT_DISABLED BIT(1) unsigned int ch_switch_vht_config; + /* HE enable/disable config from CHAN_SWITCH */ +#define CH_SWITCH_HE_ENABLED BIT(0) +#define CH_SWITCH_HE_DISABLED BIT(1) + unsigned int ch_switch_he_config; + int rssi_reject_assoc_rssi; int rssi_reject_assoc_timeout; + int rssi_ignore_probe_request; #ifdef CONFIG_AIRTIME_POLICY enum { diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c index f157659..d1642d7 100644 --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -988,3 +988,11 @@ int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer, return hapd->driver->update_dh_ie(hapd->drv_priv, peer, reason_code, ie, ielen); } + + +int hostapd_drv_dpp_listen(struct hostapd_data *hapd, bool enable) +{ + if (!hapd->driver || !hapd->driver->dpp_listen || !hapd->drv_priv) + return 0; + return hapd->driver->dpp_listen(hapd->drv_priv, enable); +} diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index cc7ea07..582ab61 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -134,6 +134,7 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface, int hostapd_drv_do_acs(struct hostapd_data *hapd); int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer, u16 reason_code, const u8 *ie, size_t ielen); +int hostapd_drv_dpp_listen(struct hostapd_data *hapd, bool enable); #include "drivers/driver.h" @@ -385,11 +386,11 @@ hostapd_drv_send_external_auth_status(struct hostapd_data *hapd, } static inline int -hostapd_drv_set_band(struct hostapd_data *hapd, enum set_band band) +hostapd_drv_set_band(struct hostapd_data *hapd, u32 band_mask) { if (!hapd->driver || !hapd->drv_priv || !hapd->driver->set_band) return -1; - return hapd->driver->set_band(hapd->drv_priv, band); + return hapd->driver->set_band(hapd->drv_priv, band_mask); } #endif /* AP_DRV_OPS */ diff --git a/src/ap/beacon.c b/src/ap/beacon.c index 6d5bb71..7d9e8b9 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -266,7 +266,7 @@ static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid, } -static const u8 * hostapd_wpa_ie(struct hostapd_data *hapd, u8 eid) +const u8 * hostapd_wpa_ie(struct hostapd_data *hapd, u8 eid) { const u8 *ies; size_t ies_len; @@ -458,7 +458,7 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, } #ifdef CONFIG_IEEE80211AX - if (hapd->iconf->ieee80211ax) { + if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) { buflen += 3 + sizeof(struct ieee80211_he_capabilities) + 3 + sizeof(struct ieee80211_he_operation) + 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) + @@ -563,15 +563,21 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, } #endif /* CONFIG_IEEE80211AC */ +#ifdef CONFIG_IEEE80211AX + if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax && + is_6ghz_op_class(hapd->iconf->op_class)) + pos = hostapd_eid_txpower_envelope(hapd, pos); +#endif /* CONFIG_IEEE80211AX */ + if ((hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) || - hapd->iconf->ieee80211ax) + (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax)) pos = hostapd_eid_wb_chsw_wrapper(hapd, pos); pos = hostapd_eid_fils_indic(hapd, pos, 0); pos = hostapd_get_rsnxe(hapd, pos, epos - pos); #ifdef CONFIG_IEEE80211AX - if (hapd->iconf->ieee80211ax) { + if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) { pos = hostapd_eid_he_capab(hapd, pos, IEEE80211_MODE_AP); pos = hostapd_eid_he_operation(hapd, pos); pos = hostapd_eid_spatial_reuse(hapd, pos); @@ -818,6 +824,10 @@ void handle_probe_req(struct hostapd_data *hapd, size_t csa_offs_len; struct radius_sta rad_info; + if (hapd->iconf->rssi_ignore_probe_request && ssi_signal && + ssi_signal < hapd->iconf->rssi_ignore_probe_request) + return; + if (len < IEEE80211_HDRLEN) return; ie = ((const u8 *) mgmt) + IEEE80211_HDRLEN; @@ -1114,6 +1124,23 @@ static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd, #endif /* NEED_AP_MLME */ +#ifdef CONFIG_IEEE80211AX +/* Unsolicited broadcast Probe Response transmission, 6 GHz only */ +static u8 * hostapd_unsol_bcast_probe_resp(struct hostapd_data *hapd, + struct wpa_driver_ap_params *params) +{ + if (!is_6ghz_op_class(hapd->iconf->op_class)) + return NULL; + + params->unsol_bcast_probe_resp_interval = + hapd->conf->unsol_bcast_probe_resp_interval; + + return hostapd_gen_probe_resp(hapd, NULL, 0, + ¶ms->unsol_bcast_probe_resp_tmpl_len); +} +#endif /* CONFIG_IEEE80211AX */ + + void sta_track_del(struct hostapd_sta_info *info) { #ifdef CONFIG_TAXONOMY @@ -1124,6 +1151,243 @@ void sta_track_del(struct hostapd_sta_info *info) } +#ifdef CONFIG_FILS + +static u16 hostapd_fils_discovery_cap(struct hostapd_data *hapd) +{ + u16 cap_info, phy_index = 0; + u8 chwidth = FD_CAP_BSS_CHWIDTH_20, mcs_nss_size = 4; + struct hostapd_hw_modes *mode = hapd->iface->current_mode; + + cap_info = FD_CAP_ESS; + if (hapd->conf->wpa) + cap_info |= FD_CAP_PRIVACY; + + if (is_6ghz_op_class(hapd->iconf->op_class)) { + phy_index = FD_CAP_PHY_INDEX_HE; + + switch (hapd->iconf->op_class) { + case 135: + mcs_nss_size += 4; + /* fallthrough */ + case 134: + mcs_nss_size += 4; + chwidth = FD_CAP_BSS_CHWIDTH_160_80_80; + break; + case 133: + chwidth = FD_CAP_BSS_CHWIDTH_80; + break; + case 132: + chwidth = FD_CAP_BSS_CHWIDTH_40; + break; + } + } else { + switch (hostapd_get_oper_chwidth(hapd->iconf)) { + case CHANWIDTH_80P80MHZ: + mcs_nss_size += 4; + /* fallthrough */ + case CHANWIDTH_160MHZ: + mcs_nss_size += 4; + chwidth = FD_CAP_BSS_CHWIDTH_160_80_80; + break; + case CHANWIDTH_80MHZ: + chwidth = FD_CAP_BSS_CHWIDTH_80; + break; + case CHANWIDTH_USE_HT: + if (hapd->iconf->secondary_channel) + chwidth = FD_CAP_BSS_CHWIDTH_40; + else + chwidth = FD_CAP_BSS_CHWIDTH_20; + break; + } + +#ifdef CONFIG_IEEE80211AX + if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) + phy_index = FD_CAP_PHY_INDEX_HE; +#endif /* CONFIG_IEEE80211AX */ +#ifdef CONFIG_IEEE80211AC + if (!phy_index && + hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) + phy_index = FD_CAP_PHY_INDEX_VHT; +#endif /* CONFIG_IEEE80211AC */ + if (!phy_index && + hapd->iconf->ieee80211n && !hapd->conf->disable_11n) + phy_index = FD_CAP_PHY_INDEX_HT; + } + + cap_info |= phy_index << FD_CAP_PHY_INDEX_SHIFT; + cap_info |= chwidth << FD_CAP_BSS_CHWIDTH_SHIFT; + + if (mode) { + u16 *mcs = (u16 *) mode->he_capab[IEEE80211_MODE_AP].mcs; + int i; + u16 nss = 0; + + for (i = 0; i < HE_NSS_MAX_STREAMS; i++) { + u16 nss_mask = 0x3 << (i * 2); + + if (mcs_nss_size == 4 && + (((mcs[0] & nss_mask) == nss_mask) || + ((mcs[1] & nss_mask) == nss_mask))) + continue; + + if (mcs_nss_size == 8 && + (((mcs[2] & nss_mask) == nss_mask) || + ((mcs[3] & nss_mask) == nss_mask))) + continue; + + if (mcs_nss_size == 12 && + (((mcs[4] & nss_mask) == nss_mask) || + ((mcs[5] & nss_mask) == nss_mask))) + continue; + + nss++; + } + + if (nss > 4) + cap_info |= FD_CAP_NSS_5_8 << FD_CAP_NSS_SHIFT; + else if (nss) + cap_info |= (nss - 1) << FD_CAP_NSS_SHIFT; + } + + return cap_info; +} + + +static u8 * hostapd_gen_fils_discovery(struct hostapd_data *hapd, size_t *len) +{ + struct ieee80211_mgmt *head; + const u8 *mobility_domain; + u8 *pos, *length_pos, buf[200]; + u16 ctl = 0; + u8 fd_rsn_info[5]; + size_t total_len, buf_len; + + total_len = 24 + 2 + 12; + + /* FILS Discovery Frame Control */ + ctl = (sizeof(hapd->conf->ssid.short_ssid) - 1) | + FD_FRAME_CTL_SHORT_SSID_PRESENT | + FD_FRAME_CTL_LENGTH_PRESENT | + FD_FRAME_CTL_CAP_PRESENT; + total_len += 4 + 1 + 2; + + /* Check for optional subfields and calculate length */ + if (wpa_auth_write_fd_rsn_info(hapd->wpa_auth, fd_rsn_info)) { + ctl |= FD_FRAME_CTL_RSN_INFO_PRESENT; + total_len += sizeof(fd_rsn_info); + } + + mobility_domain = hostapd_wpa_ie(hapd, WLAN_EID_MOBILITY_DOMAIN); + if (mobility_domain) { + ctl |= FD_FRAME_CTL_MD_PRESENT; + total_len += 3; + } + + pos = hostapd_eid_fils_indic(hapd, buf, 0); + buf_len = pos - buf; + total_len += buf_len; + + head = os_zalloc(total_len); + if (!head) + return NULL; + + head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memset(head->da, 0xff, ETH_ALEN); + os_memcpy(head->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN); + + head->u.action.category = WLAN_ACTION_PUBLIC; + head->u.action.u.public_action.action = WLAN_PA_FILS_DISCOVERY; + + pos = &head->u.action.u.public_action.variable[0]; + + /* FILS Discovery Information field */ + + /* FILS Discovery Frame Control */ + WPA_PUT_LE16(pos, ctl); + pos += 2; + + /* Hardware or low-level driver will fill in the Timestamp value */ + pos += 8; + + /* Beacon Interval */ + WPA_PUT_LE16(pos, hapd->iconf->beacon_int); + pos += 2; + + /* Short SSID */ + WPA_PUT_LE32(pos, hapd->conf->ssid.short_ssid); + pos += sizeof(hapd->conf->ssid.short_ssid); + + /* Store position of FILS discovery information element Length field */ + length_pos = pos++; + + /* FD Capability */ + WPA_PUT_LE16(pos, hostapd_fils_discovery_cap(hapd)); + pos += 2; + + /* Operating Class - not present */ + + /* Primary Channel - not present */ + + /* AP Configuration Sequence Number - not present */ + + /* Access Network Options - not present */ + + /* FD RSN Information */ + if (ctl & FD_FRAME_CTL_RSN_INFO_PRESENT) { + os_memcpy(pos, fd_rsn_info, sizeof(fd_rsn_info)); + pos += sizeof(fd_rsn_info); + } + + /* Channel Center Frequency Segment 1 - not present */ + + /* Mobility Domain */ + if (ctl & FD_FRAME_CTL_MD_PRESENT) { + os_memcpy(pos, &mobility_domain[2], 3); + pos += 3; + } + + /* Fill in the Length field value */ + *length_pos = pos - (length_pos + 1); + + /* FILS Indication element */ + if (buf_len) { + os_memcpy(pos, buf, buf_len); + pos += buf_len; + } + + *len = pos - (u8 *) head; + wpa_hexdump(MSG_DEBUG, "FILS Discovery frame template", + head, pos - (u8 *) head); + return (u8 *) head; +} + + +/* Configure FILS Discovery frame transmission parameters */ +static u8 * hostapd_fils_discovery(struct hostapd_data *hapd, + struct wpa_driver_ap_params *params) +{ + params->fd_max_int = hapd->conf->fils_discovery_max_int; + if (is_6ghz_op_class(hapd->iconf->op_class) && + params->fd_max_int > FD_MAX_INTERVAL_6GHZ) + params->fd_max_int = FD_MAX_INTERVAL_6GHZ; + + params->fd_min_int = hapd->conf->fils_discovery_min_int; + if (params->fd_min_int > params->fd_max_int) + params->fd_min_int = params->fd_max_int; + + if (params->fd_max_int) + return hostapd_gen_fils_discovery(hapd, + ¶ms->fd_frame_tmpl_len); + + return NULL; +} + +#endif /* CONFIG_FILS */ + + int ieee802_11_build_ap_params(struct hostapd_data *hapd, struct wpa_driver_ap_params *params) { @@ -1163,7 +1427,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, #endif /* CONFIG_IEEE80211AC */ #ifdef CONFIG_IEEE80211AX - if (hapd->iconf->ieee80211ax) { + if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) { tail_len += 3 + sizeof(struct ieee80211_he_capabilities) + 3 + sizeof(struct ieee80211_he_operation) + 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) + @@ -1280,22 +1544,29 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, #endif /* CONFIG_FST */ #ifdef CONFIG_IEEE80211AC - if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { + if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac && + !is_6ghz_op_class(hapd->iconf->op_class)) { tailpos = hostapd_eid_vht_capabilities(hapd, tailpos, 0); tailpos = hostapd_eid_vht_operation(hapd, tailpos); tailpos = hostapd_eid_txpower_envelope(hapd, tailpos); } #endif /* CONFIG_IEEE80211AC */ +#ifdef CONFIG_IEEE80211AX + if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax && + is_6ghz_op_class(hapd->iconf->op_class)) + tailpos = hostapd_eid_txpower_envelope(hapd, tailpos); +#endif /* CONFIG_IEEE80211AX */ + if ((hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) || - hapd->iconf->ieee80211ax) + (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax)) tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos); tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0); tailpos = hostapd_get_rsnxe(hapd, tailpos, tailend - tailpos); #ifdef CONFIG_IEEE80211AX - if (hapd->iconf->ieee80211ax) { + if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) { tailpos = hostapd_eid_he_capab(hapd, tailpos, IEEE80211_MODE_AP); tailpos = hostapd_eid_he_operation(hapd, tailpos); @@ -1461,6 +1732,14 @@ void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params) params->head = NULL; os_free(params->proberesp); params->proberesp = NULL; +#ifdef CONFIG_FILS + os_free(params->fd_frame_tmpl); + params->fd_frame_tmpl = NULL; +#endif /* CONFIG_FILS */ +#ifdef CONFIG_IEEE80211AX + os_free(params->unsol_bcast_probe_resp_tmpl); + params->unsol_bcast_probe_resp_tmpl = NULL; +#endif /* CONFIG_IEEE80211AX */ } @@ -1493,11 +1772,17 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd) params.assocresp_ies = assocresp; params.reenable = hapd->reenable_beacon; #ifdef CONFIG_IEEE80211AX - params.he_spr = !!hapd->iface->conf->spr.sr_control; + params.he_spr_ctrl = hapd->iface->conf->spr.sr_control; + params.he_spr_non_srg_obss_pd_max_offset = + hapd->iface->conf->spr.non_srg_obss_pd_max_offset; params.he_spr_srg_obss_pd_min_offset = hapd->iface->conf->spr.srg_obss_pd_min_offset; params.he_spr_srg_obss_pd_max_offset = hapd->iface->conf->spr.srg_obss_pd_max_offset; + os_memcpy(params.he_spr_bss_color_bitmap, + hapd->iface->conf->spr.srg_bss_color_bitmap, 8); + os_memcpy(params.he_spr_partial_bssid_bitmap, + hapd->iface->conf->spr.srg_partial_bssid_bitmap, 8); params.he_bss_color_disabled = hapd->iface->conf->he_op.he_bss_color_disabled; params.he_bss_color_partial = @@ -1505,12 +1790,18 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd) params.he_bss_color = hapd->iface->conf->he_op.he_bss_color; params.twt_responder = hostapd_get_he_twt_responder(hapd, IEEE80211_MODE_AP); + params.unsol_bcast_probe_resp_tmpl = + hostapd_unsol_bcast_probe_resp(hapd, ¶ms); #endif /* CONFIG_IEEE80211AX */ hapd->reenable_beacon = 0; #ifdef CONFIG_SAE params.sae_pwe = hapd->conf->sae_pwe; #endif /* CONFIG_SAE */ +#ifdef CONFIG_FILS + params.fd_frame_tmpl = hostapd_fils_discovery(hapd, ¶ms); +#endif /* CONFIG_FILS */ + if (cmode && hostapd_set_freq_params(&freq, iconf->hw_mode, iface->freq, iconf->channel, iconf->enable_edmg, diff --git a/src/ap/beacon.h b/src/ap/beacon.h index a26e308..c320825 100644 --- a/src/ap/beacon.h +++ b/src/ap/beacon.h @@ -30,4 +30,6 @@ sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr, void sta_track_claim_taxonomy_info(struct hostapd_iface *iface, const u8 *addr, struct wpabuf **probe_ie_taxonomy); +const u8 * hostapd_wpa_ie(struct hostapd_data *hapd, u8 eid); + #endif /* BEACON_H */ diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c index ef53a82..28e40ba 100644 --- a/src/ap/ctrl_iface_ap.c +++ b/src/ap/ctrl_iface_ap.c @@ -748,7 +748,8 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, iface->conf->ieee80211n && !hapd->conf->disable_11n, iface->conf->ieee80211ac && !hapd->conf->disable_11ac, - iface->conf->ieee80211ax, + iface->conf->ieee80211ax && + !hapd->conf->disable_11ax, iface->conf->beacon_int, hapd->conf->dtim_period); if (os_snprintf_error(buflen - len, ret)) @@ -756,7 +757,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, len += ret; #ifdef CONFIG_IEEE80211AX - if (iface->conf->ieee80211ax) { + if (iface->conf->ieee80211ax && !hapd->conf->disable_11ax) { ret = os_snprintf(buf + len, buflen - len, "he_oper_chwidth=%d\n" "he_oper_centr_freq_seg0_idx=%d\n" @@ -908,6 +909,7 @@ int hostapd_parse_csa_settings(const char *pos, SET_CSA_SETTING(sec_channel_offset); settings->freq_params.ht_enabled = !!os_strstr(pos, " ht"); settings->freq_params.vht_enabled = !!os_strstr(pos, " vht"); + settings->freq_params.he_enabled = !!os_strstr(pos, " he"); settings->block_tx = !!os_strstr(pos, " blocktx"); #undef SET_CSA_SETTING diff --git a/src/ap/dfs.c b/src/ap/dfs.c index f04a00a..b990fb3 100644 --- a/src/ap/dfs.c +++ b/src/ap/dfs.c @@ -81,17 +81,17 @@ static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans) * We will also choose this first channel as the control one. */ int allowed_40[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, - 184, 192 }; + 165, 173, 184, 192 }; /* * VHT80, valid channels based on center frequency: - * 42, 58, 106, 122, 138, 155 + * 42, 58, 106, 122, 138, 155, 171 */ - int allowed_80[] = { 36, 52, 100, 116, 132, 149 }; + int allowed_80[] = { 36, 52, 100, 116, 132, 149, 165 }; /* * VHT160 valid channels based on center frequency: - * 50, 114 + * 50, 114, 163 */ - int allowed_160[] = { 36, 100 }; + int allowed_160[] = { 36, 100, 149 }; int *allowed = allowed_40; unsigned int i, allowed_no = 0; @@ -1032,6 +1032,7 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) int err = 1; struct hostapd_hw_modes *cmode = iface->current_mode; u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf); + int ieee80211_mode = IEEE80211_MODE_AP; wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)", __func__, iface->cac_started ? "yes" : "no", @@ -1069,8 +1070,16 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) &oper_centr_freq_seg0_idx, &oper_centr_freq_seg1_idx, &skip_radar); - if (!channel) - return err; + if (!channel) { + /* + * Toggle interface state to enter DFS state + * until NOP is finished. + */ + hostapd_disable_iface(iface); + hostapd_enable_iface(iface); + return 0; + } + if (!skip_radar) { iface->freq = channel->freq; iface->conf->channel = channel->chan; @@ -1099,6 +1108,10 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) os_memset(&csa_settings, 0, sizeof(csa_settings)); csa_settings.cs_count = 5; csa_settings.block_tx = 1; +#ifdef CONFIG_MESH + if (iface->mconf) + ieee80211_mode = IEEE80211_MODE_MESH; +#endif /* CONFIG_MESH */ err = hostapd_set_freq_params(&csa_settings.freq_params, iface->conf->hw_mode, channel->freq, @@ -1113,7 +1126,7 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) oper_centr_freq_seg0_idx, oper_centr_freq_seg1_idx, cmode->vht_capab, - &cmode->he_capab[IEEE80211_MODE_AP]); + &cmode->he_capab[ieee80211_mode]); if (err) { wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params"); @@ -1335,12 +1348,16 @@ int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width, if (!(chan->flag & HOSTAPD_CHAN_RADAR)) continue; + if ((chan->flag & HOSTAPD_CHAN_DFS_MASK) == + HOSTAPD_CHAN_DFS_AVAILABLE) + continue; + if (center_freq - chan->freq < half_width && chan->freq - center_freq < half_width) res++; } - wpa_printf(MSG_DEBUG, "DFS: (%d, %d): in range: %s", + wpa_printf(MSG_DEBUG, "DFS CAC required: (%d, %d): in range: %s", center_freq - half_width, center_freq + half_width, res ? "yes" : "no"); diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c index 6772a87..e1e5a3a 100644 --- a/src/ap/dpp_hostapd.c +++ b/src/ap/dpp_hostapd.c @@ -23,6 +23,8 @@ static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx); +static void hostapd_dpp_auth_conf_wait_timeout(void *eloop_ctx, + void *timeout_ctx); static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator); static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx); static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd); @@ -246,6 +248,8 @@ void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst, eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL); eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_auth_conf_wait_timeout, + hapd, NULL); eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL); #ifdef CONFIG_DPP2 @@ -277,6 +281,17 @@ void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst, } } + if (auth->waiting_auth_conf && + auth->auth_resp_status == DPP_STATUS_OK) { + /* Make sure we do not get stuck waiting for Auth Confirm + * indefinitely after successfully transmitted Auth Response to + * allow new authentication exchanges to be started. */ + eloop_cancel_timeout(hostapd_dpp_auth_conf_wait_timeout, hapd, + NULL); + eloop_register_timeout(1, 0, hostapd_dpp_auth_conf_wait_timeout, + hapd, NULL); + } + if (!is_broadcast_ether_addr(dst) && auth->waiting_auth_resp && ok) { /* Allow timeout handling to stop iteration if no response is * received from a peer that has ACKed a request. */ @@ -377,6 +392,25 @@ static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx) } +static void hostapd_dpp_auth_conf_wait_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct dpp_authentication *auth = hapd->dpp_auth; + + if (!auth || !auth->waiting_auth_conf) + return; + + wpa_printf(MSG_DEBUG, + "DPP: Terminate authentication exchange due to Auth Confirm timeout"); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "No Auth Confirm received"); + hostapd_drv_send_action_cancel_wait(hapd); + dpp_auth_deinit(auth); + hapd->dpp_auth = NULL; +} + + static void hostapd_dpp_set_testing_options(struct hostapd_data *hapd, struct dpp_authentication *auth) { @@ -461,7 +495,9 @@ static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd) freq = auth->freq[auth->freq_idx++]; auth->curr_freq = freq; - if (is_zero_ether_addr(auth->peer_bi->mac_addr)) + if (!is_zero_ether_addr(auth->peer_mac_addr)) + dst = auth->peer_mac_addr; + else if (is_zero_ether_addr(auth->peer_bi->mac_addr)) dst = broadcast; else dst = auth->peer_bi->mac_addr; @@ -594,6 +630,8 @@ int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd) eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL); eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_auth_conf_wait_timeout, + hapd, NULL); eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL); #ifdef CONFIG_DPP2 @@ -661,12 +699,14 @@ int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd) return -1; } + hostapd_drv_dpp_listen(hapd, true); return 0; } void hostapd_dpp_listen_stop(struct hostapd_data *hapd) { + hostapd_drv_dpp_listen(hapd, false); /* TODO: Stop listen operation on non-operating channel */ } @@ -1261,8 +1301,9 @@ hostapd_dpp_rx_presence_announcement(struct hostapd_data *hapd, const u8 *src, auth->neg_freq = freq; - if (!is_zero_ether_addr(peer_bi->mac_addr)) - os_memcpy(auth->peer_mac_addr, peer_bi->mac_addr, ETH_ALEN); + /* The source address of the Presence Announcement frame overrides any + * MAC address information from the bootstrapping information. */ + os_memcpy(auth->peer_mac_addr, src, ETH_ALEN); hapd->dpp_auth = auth; if (hostapd_dpp_auth_init_next(hapd) < 0) { @@ -1962,6 +2003,7 @@ void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok) wpa_printf(MSG_DEBUG, "DPP: Configuration exchange completed (ok=%d)", ok); eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_auth_conf_wait_timeout, hapd, NULL); eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL); #ifdef CONFIG_DPP2 eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout, @@ -2207,6 +2249,7 @@ void hostapd_dpp_deinit(struct hostapd_data *hapd) if (!hapd->dpp_init_done) return; eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_auth_conf_wait_timeout, hapd, NULL); eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL); eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL); #ifdef CONFIG_DPP2 diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index 9af5445..53082f5 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -870,9 +870,10 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, - "driver %s channel switch: freq=%d, ht=%d, vht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d", + "driver %s channel switch: freq=%d, ht=%d, vht_ch=0x%x, he_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d", finished ? "had" : "starting", - freq, ht, hapd->iconf->ch_switch_vht_config, offset, + freq, ht, hapd->iconf->ch_switch_vht_config, + hapd->iconf->ch_switch_he_config, offset, width, channel_width_to_string(width), cf1, cf2); if (!hapd->iface->current_mode) { @@ -944,8 +945,17 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, else if (hapd->iconf->ch_switch_vht_config & CH_SWITCH_VHT_DISABLED) hapd->iconf->ieee80211ac = 0; + } else if (hapd->iconf->ch_switch_he_config) { + /* CHAN_SWITCH HE config */ + if (hapd->iconf->ch_switch_he_config & + CH_SWITCH_HE_ENABLED) + hapd->iconf->ieee80211ax = 1; + else if (hapd->iconf->ch_switch_he_config & + CH_SWITCH_HE_DISABLED) + hapd->iconf->ieee80211ax = 0; } hapd->iconf->ch_switch_vht_config = 0; + hapd->iconf->ch_switch_he_config = 0; hapd->iconf->secondary_channel = offset; hostapd_set_oper_chwidth(hapd->iconf, chwidth); diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index b37f49f..e257174 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -354,7 +354,7 @@ static int hostapd_broadcast_wep_set(struct hostapd_data *hapd) #endif /* CONFIG_WEP */ -static void hostapd_free_hapd_data(struct hostapd_data *hapd) +void hostapd_free_hapd_data(struct hostapd_data *hapd) { os_free(hapd->probereq_cb); hapd->probereq_cb = NULL; @@ -434,6 +434,8 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) #ifdef CONFIG_MESH wpabuf_free(hapd->mesh_pending_auth); hapd->mesh_pending_auth = NULL; + /* handling setup failure is already done */ + hapd->setup_complete_cb = NULL; #endif /* CONFIG_MESH */ hostapd_clean_rrm(hapd); @@ -496,7 +498,7 @@ static void sta_track_deinit(struct hostapd_iface *iface) } -static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) +void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) { wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); #ifdef NEED_AP_MLME @@ -624,7 +626,7 @@ static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason) } -static void hostapd_bss_deinit_no_free(struct hostapd_data *hapd) +void hostapd_bss_deinit_no_free(struct hostapd_data *hapd) { hostapd_free_stas(hapd); hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING); @@ -1701,6 +1703,9 @@ static int setup_interface2(struct hostapd_iface *iface) ret = hostapd_check_edmg_capab(iface); if (ret < 0) goto fail; + ret = hostapd_check_he_6ghz_capab(iface); + if (ret < 0) + goto fail; ret = hostapd_check_ht_capab(iface); if (ret < 0) goto fail; @@ -2156,6 +2161,13 @@ dfs_offload: if (hapd->setup_complete_cb) hapd->setup_complete_cb(hapd->setup_complete_cb_ctx); +#ifdef CONFIG_MESH + if (delay_apply_cfg && !iface->mconf) { + wpa_printf(MSG_ERROR, "Error while completing mesh init"); + goto fail; + } +#endif /* CONFIG_MESH */ + wpa_printf(MSG_DEBUG, "%s: Setup of interface done.", iface->bss[0]->conf->iface); if (iface->interfaces && iface->interfaces->terminate_on_error > 0) @@ -2299,7 +2311,7 @@ int hostapd_setup_interface(struct hostapd_iface *iface) ret = setup_interface(iface); if (ret) { wpa_printf(MSG_ERROR, "%s: Unable to setup interface.", - iface->bss[0]->conf->iface); + iface->conf ? iface->conf->bss[0]->iface : "N/A"); return -1; } @@ -2681,6 +2693,12 @@ int hostapd_enable_iface(struct hostapd_iface *hapd_iface) { size_t j; + if (!hapd_iface) + return -1; + + if (hapd_iface->enable_iface_cb) + return hapd_iface->enable_iface_cb(hapd_iface); + if (hapd_iface->bss[0]->drv_priv != NULL) { wpa_printf(MSG_ERROR, "Interface %s already enabled", hapd_iface->conf->bss[0]->iface); @@ -2742,6 +2760,9 @@ int hostapd_disable_iface(struct hostapd_iface *hapd_iface) if (hapd_iface == NULL) return -1; + if (hapd_iface->disable_iface_cb) + return hapd_iface->disable_iface_cb(hapd_iface); + if (hapd_iface->bss[0]->drv_priv == NULL) { wpa_printf(MSG_INFO, "Interface %s already disabled", hapd_iface->conf->bss[0]->iface); @@ -3539,15 +3560,23 @@ void hostapd_cleanup_cs_params(struct hostapd_data *hapd) } -void hostapd_chan_switch_vht_config(struct hostapd_data *hapd, int vht_enabled) +void hostapd_chan_switch_config(struct hostapd_data *hapd, + struct hostapd_freq_params *freq_params) { - if (vht_enabled) + if (freq_params->he_enabled) + hapd->iconf->ch_switch_he_config |= CH_SWITCH_HE_ENABLED; + else + hapd->iconf->ch_switch_he_config |= CH_SWITCH_HE_DISABLED; + + if (freq_params->vht_enabled) hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_ENABLED; else hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_DISABLED; hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "CHAN_SWITCH VHT CONFIG 0x%x", + HOSTAPD_LEVEL_INFO, + "CHAN_SWITCH HE config 0x%x VHT config 0x%x", + hapd->iconf->ch_switch_he_config, hapd->iconf->ch_switch_vht_config); } diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index b70d13f..0736f45 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -370,6 +370,8 @@ struct hostapd_data { int dhcp_sock; /* UDP socket used with the DHCP server */ + struct ptksa_cache *ptksa; + #ifdef CONFIG_DPP int dpp_init_done; struct dpp_authentication *dpp_auth; @@ -589,6 +591,9 @@ struct hostapd_iface { /* Previous WMM element information */ struct hostapd_wmm_ac_params prev_wmm[WMM_AC_NUM]; + + int (*enable_iface_cb)(struct hostapd_iface *iface); + int (*disable_iface_cb)(struct hostapd_iface *iface); }; /* hostapd.c */ @@ -617,13 +622,17 @@ void hostapd_interface_deinit_free(struct hostapd_iface *iface); int hostapd_enable_iface(struct hostapd_iface *hapd_iface); int hostapd_reload_iface(struct hostapd_iface *hapd_iface); int hostapd_disable_iface(struct hostapd_iface *hapd_iface); +void hostapd_bss_deinit_no_free(struct hostapd_data *hapd); +void hostapd_free_hapd_data(struct hostapd_data *hapd); +void hostapd_cleanup_iface_partial(struct hostapd_iface *iface); int hostapd_add_iface(struct hapd_interfaces *ifaces, char *buf); int hostapd_remove_iface(struct hapd_interfaces *ifaces, char *buf); void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator); void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s); const char * hostapd_state_text(enum hostapd_iface_state s); int hostapd_csa_in_progress(struct hostapd_iface *iface); -void hostapd_chan_switch_vht_config(struct hostapd_data *hapd, int vht_enabled); +void hostapd_chan_switch_config(struct hostapd_data *hapd, + struct hostapd_freq_params *freq_params); int hostapd_switch_channel(struct hostapd_data *hapd, struct csa_settings *settings); void diff --git a/src/ap/hs20.c b/src/ap/hs20.c index 543fa33..05e9b9d 100644 --- a/src/ap/hs20.c +++ b/src/ap/hs20.c @@ -86,7 +86,9 @@ u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid) capab |= WPA_CAPABILITY_MFPR; } #ifdef CONFIG_OCV - if (hapd->conf->ocv) + if (hapd->conf->ocv && + (hapd->iface->drv_flags2 & + (WPA_DRIVER_FLAGS2_AP_SME | WPA_DRIVER_FLAGS2_OCV))) capab |= WPA_CAPABILITY_OCVC; #endif /* CONFIG_OCV */ WPA_PUT_LE16(eid, capab); diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index f6e6903..7849be1 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -313,7 +313,7 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface) { struct wpa_scan_results *scan_res; int oper40; - int res; + int res = 0; /* Check list of neighboring BSSes (from scan) to see whether 40 MHz is * allowed per IEEE Std 802.11-2012, 10.15.3.2 */ @@ -349,7 +349,24 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface) } } - res = ieee80211n_allowed_ht40_channel_pair(iface); +#ifdef CONFIG_IEEE80211AX + if (iface->conf->secondary_channel && + iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && + iface->conf->ieee80211ax) { + struct he_capabilities *he_cap; + + he_cap = &iface->current_mode->he_capab[IEEE80211_MODE_AP]; + if (!(he_cap->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] & + HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G)) { + wpa_printf(MSG_DEBUG, + "HE: 40 MHz channel width is not supported in 2.4 GHz; clear secondary channel configuration"); + iface->conf->secondary_channel = 0; + } + } +#endif /* CONFIG_IEEE80211AX */ + + if (iface->conf->secondary_channel) + res = ieee80211n_allowed_ht40_channel_pair(iface); if (!res) { iface->conf->secondary_channel = 0; hostapd_set_oper_centr_freq_seg0_idx(iface->conf, 0); @@ -732,6 +749,51 @@ int hostapd_check_edmg_capab(struct hostapd_iface *iface) } +int hostapd_check_he_6ghz_capab(struct hostapd_iface *iface) +{ +#ifdef CONFIG_IEEE80211AX + struct he_capabilities *he_cap; + u16 hw; + + if (!iface->current_mode || !is_6ghz_freq(iface->freq)) + return 0; + + he_cap = &iface->current_mode->he_capab[IEEE80211_MODE_AP]; + hw = he_cap->he_6ghz_capa; + if (iface->conf->he_6ghz_max_mpdu > + ((hw & HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_MASK) >> + HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_SHIFT)) { + wpa_printf(MSG_ERROR, + "The driver does not support the configured HE 6 GHz Max MPDU length"); + return -1; + } + + if (iface->conf->he_6ghz_max_ampdu_len_exp > + ((hw & HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_MASK) >> + HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_SHIFT)) { + wpa_printf(MSG_ERROR, + "The driver does not support the configured HE 6 GHz Max AMPDU Length Exponent"); + return -1; + } + + if (iface->conf->he_6ghz_rx_ant_pat && + !(hw & HE_6GHZ_BAND_CAP_RX_ANTPAT_CONS)) { + wpa_printf(MSG_ERROR, + "The driver does not support the configured HE 6 GHz Rx Antenna Pattern"); + return -1; + } + + if (iface->conf->he_6ghz_tx_ant_pat && + !(hw & HE_6GHZ_BAND_CAP_TX_ANTPAT_CONS)) { + wpa_printf(MSG_ERROR, + "The driver does not support the configured HE 6 GHz Tx Antenna Pattern"); + return -1; + } +#endif /* CONFIG_IEEE80211AX */ + return 0; +} + + static int hostapd_is_usable_chan(struct hostapd_iface *iface, int frequency, int primary) { @@ -949,9 +1011,9 @@ static void hostapd_notify_bad_chans(struct hostapd_iface *iface) hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_WARNING, - "Configured channel (%d) or frequency (%d) not found from the channel list of the current mode (%d) %s", + "Configured channel (%d) or frequency (%d) (secondary_channel=%d) not found from the channel list of the current mode (%d) %s", iface->conf->channel, - iface->freq, + iface->freq, iface->conf->secondary_channel, iface->current_mode->mode, hostapd_hw_mode_txt(iface->current_mode->mode)); hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, @@ -1029,12 +1091,13 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface) iface->current_mode = NULL; for (i = 0; i < iface->num_hw_features; i++) { struct hostapd_hw_modes *mode = &iface->hw_features[i]; + int chan; + if (mode->mode == iface->conf->hw_mode) { if (iface->freq > 0 && - !hw_get_chan(mode->mode, iface->freq, - iface->hw_features, - iface->num_hw_features)) + !hw_mode_get_channel(mode, iface->freq, &chan)) continue; + iface->current_mode = mode; break; } diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h index dd24f95..ad0ddf7 100644 --- a/src/ap/hw_features.h +++ b/src/ap/hw_features.h @@ -22,6 +22,7 @@ int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan); int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq); int hostapd_check_ht_capab(struct hostapd_iface *iface); int hostapd_check_edmg_capab(struct hostapd_iface *iface); +int hostapd_check_he_6ghz_capab(struct hostapd_iface *iface); int hostapd_prepare_rates(struct hostapd_iface *iface, struct hostapd_hw_modes *mode); void hostapd_stop_setup_timers(struct hostapd_iface *iface); @@ -85,6 +86,11 @@ static inline int hostapd_hw_skip_mode(struct hostapd_iface *iface, return 0; } +static inline int hostapd_check_he_6ghz_capab(struct hostapd_iface *iface) +{ + return 0; +} + #endif /* NEED_AP_MLME */ #endif /* HW_FEATURES_H */ diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index de715a0..40d4a33 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -25,6 +25,7 @@ #include "common/ocv.h" #include "common/wpa_common.h" #include "common/wpa_ctrl.h" +#include "common/ptksa_cache.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "p2p/p2p.h" @@ -65,6 +66,23 @@ prepare_auth_resp_fils(struct hostapd_data *hapd, const u8 *msk, size_t msk_len, int *is_pub); #endif /* CONFIG_FILS */ + +#ifdef CONFIG_PASN + +static int handle_auth_pasn_resp(struct hostapd_data *hapd, + struct sta_info *sta, + struct rsn_pmksa_cache_entry *pmksa, + u16 status); +#ifdef CONFIG_FILS + +static void pasn_fils_auth_resp(struct hostapd_data *hapd, + struct sta_info *sta, u16 status, + struct wpabuf *erp_resp, + const u8 *msk, size_t msk_len); + +#endif /* CONFIG_FILS */ +#endif /* CONFIG_PASN */ + static void handle_auth(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int rssi, int from_queue); @@ -469,6 +487,49 @@ static void sae_set_state(struct sta_info *sta, enum sae_state state, } +static const char * sae_get_password(struct hostapd_data *hapd, + struct sta_info *sta, + const char *rx_id, + struct sae_password_entry **pw_entry, + struct sae_pt **s_pt, + const struct sae_pk **s_pk) +{ + const char *password = NULL; + struct sae_password_entry *pw; + struct sae_pt *pt = NULL; + const struct sae_pk *pk = NULL; + + for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) { + if (!is_broadcast_ether_addr(pw->peer_addr) && + os_memcmp(pw->peer_addr, sta->addr, ETH_ALEN) != 0) + continue; + if ((rx_id && !pw->identifier) || (!rx_id && pw->identifier)) + continue; + if (rx_id && pw->identifier && + os_strcmp(rx_id, pw->identifier) != 0) + continue; + password = pw->password; + pt = pw->pt; + if (!(hapd->conf->mesh & MESH_ENABLED)) + pk = pw->pk; + break; + } + if (!password) { + password = hapd->conf->ssid.wpa_passphrase; + pt = hapd->conf->ssid.pt; + } + + if (pw_entry) + *pw_entry = pw; + if (s_pt) + *s_pt = pt; + if (s_pk) + *s_pk = pk; + + return password; +} + + static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, struct sta_info *sta, int update, int status_code) @@ -498,25 +559,7 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, status_code == WLAN_STATUS_SAE_PK) use_pt = 1; - for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) { - if (!is_broadcast_ether_addr(pw->peer_addr) && - os_memcmp(pw->peer_addr, sta->addr, ETH_ALEN) != 0) - continue; - if ((rx_id && !pw->identifier) || (!rx_id && pw->identifier)) - continue; - if (rx_id && pw->identifier && - os_strcmp(rx_id, pw->identifier) != 0) - continue; - password = pw->password; - pt = pw->pt; - if (!(hapd->conf->mesh & MESH_ENABLED)) - pk = pw->pk; - break; - } - if (!password) { - password = hapd->conf->ssid.wpa_passphrase; - pt = hapd->conf->ssid.pt; - } + password = sae_get_password(hapd, sta, rx_id, &pw, &pt, &pk); if (!password || (use_pt && !pt)) { wpa_printf(MSG_DEBUG, "SAE: No password available"); return NULL; @@ -1234,6 +1277,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, int default_groups[] = { 19, 0 }; const u8 *pos, *end; int sta_removed = 0; + bool success_status; if (!groups) groups = default_groups; @@ -1519,9 +1563,12 @@ reply: } remove_sta: + if (auth_transaction == 1) + success_status = sae_status_success(hapd, status_code); + else + success_status = status_code == WLAN_STATUS_SUCCESS; if (!sta_removed && sta->added_unassoc && - (resp != WLAN_STATUS_SUCCESS || - status_code != WLAN_STATUS_SUCCESS)) { + (resp != WLAN_STATUS_SUCCESS || !success_status)) { hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; } @@ -2185,23 +2232,35 @@ void ieee802_11_finish_fils_auth(struct hostapd_data *hapd, struct wpabuf *erp_resp, const u8 *msk, size_t msk_len) { - struct wpabuf *data; - int pub = 0; u16 resp; + u32 flags = sta->flags; - sta->flags &= ~WLAN_STA_PENDING_FILS_ERP; + sta->flags &= ~(WLAN_STA_PENDING_FILS_ERP | + WLAN_STA_PENDING_PASN_FILS_ERP); - if (!sta->fils_pending_cb) - return; resp = success ? WLAN_STATUS_SUCCESS : WLAN_STATUS_UNSPECIFIED_FAILURE; - data = prepare_auth_resp_fils(hapd, sta, &resp, NULL, erp_resp, - msk, msk_len, &pub); - if (!data) { - wpa_printf(MSG_DEBUG, - "%s: prepare_auth_resp_fils() returned failure", - __func__); + + if (flags & WLAN_STA_PENDING_FILS_ERP) { + struct wpabuf *data; + int pub = 0; + + if (!sta->fils_pending_cb) + return; + + data = prepare_auth_resp_fils(hapd, sta, &resp, NULL, erp_resp, + msk, msk_len, &pub); + if (!data) { + wpa_printf(MSG_DEBUG, + "%s: prepare_auth_resp_fils() failure", + __func__); + } + sta->fils_pending_cb(hapd, sta, resp, data, pub); +#ifdef CONFIG_PASN + } else if (flags & WLAN_STA_PENDING_PASN_FILS_ERP) { + pasn_fils_auth_resp(hapd, sta, resp, erp_resp, + msk, msk_len); +#endif /* CONFIG_PASN */ } - sta->fils_pending_cb(hapd, sta, resp, data, pub); } #endif /* CONFIG_FILS */ @@ -2296,6 +2355,1065 @@ ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta, } +#ifdef CONFIG_PASN +#ifdef CONFIG_SAE + +static int pasn_wd_handle_sae_commit(struct hostapd_data *hapd, + struct sta_info *sta, + struct wpabuf *wd) +{ + struct pasn_data *pasn = sta->pasn; + const char *password = NULL; + const u8 *data; + size_t buf_len; + u16 res, alg, seq, status; + int groups[] = { pasn->group, 0 }; + int ret; + + if (!wd) + return -1; + + data = wpabuf_head_u8(wd); + buf_len = wpabuf_len(wd); + + if (buf_len < 6) { + wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%lu", + buf_len); + return -1; + } + + alg = WPA_GET_LE16(data); + seq = WPA_GET_LE16(data + 2); + status = WPA_GET_LE16(data + 4); + + wpa_printf(MSG_DEBUG, "PASN: SAE commit: alg=%u, seq=%u, status=%u", + alg, seq, status); + + /* TODO: SAE H2E */ + if (alg != WLAN_AUTH_SAE || seq != 1 || status != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE commit"); + return -1; + } + + sae_clear_data(&pasn->sae); + pasn->sae.state = SAE_NOTHING; + + ret = sae_set_group(&pasn->sae, pasn->group); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to set SAE group"); + return -1; + } + + password = sae_get_password(hapd, sta, NULL, NULL, NULL, NULL); + if (!password) { + wpa_printf(MSG_DEBUG, "PASN: No SAE password found"); + return -1; + } + + ret = sae_prepare_commit(hapd->own_addr, sta->addr, + (const u8 *) password, os_strlen(password), 0, + &pasn->sae); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit"); + return -1; + } + + res = sae_parse_commit(&pasn->sae, data + 6, buf_len - 6, NULL, 0, + groups, 0); + if (res != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "PASN: Failed parsing SAE commit"); + return -1; + } + + /* Process the commit message and derive the PMK */ + ret = sae_process_commit(&pasn->sae); + if (ret) { + wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit"); + return -1; + } + + pasn->sae.state = SAE_COMMITTED; + + return 0; +} + + +static int pasn_wd_handle_sae_confirm(struct hostapd_data *hapd, + struct sta_info *sta, + struct wpabuf *wd) +{ + struct pasn_data *pasn = sta->pasn; + const u8 *data; + size_t buf_len; + u16 res, alg, seq, status; + + if (!wd) + return -1; + + data = wpabuf_head_u8(wd); + buf_len = wpabuf_len(wd); + + if (buf_len < 6) { + wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%lu", + buf_len); + return -1; + } + + alg = WPA_GET_LE16(data); + seq = WPA_GET_LE16(data + 2); + status = WPA_GET_LE16(data + 4); + + wpa_printf(MSG_DEBUG, "PASN: SAE confirm: alg=%u, seq=%u, status=%u", + alg, seq, status); + + if (alg != WLAN_AUTH_SAE || seq != 2 || status != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE confirm"); + return -1; + } + + res = sae_check_confirm(&pasn->sae, data + 6, buf_len - 6); + if (res != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "PASN: SAE failed checking confirm"); + return -1; + } + + pasn->sae.state = SAE_ACCEPTED; + + /* + * TODO: Based on on IEEE P802.11az/D2.6, the PMKSA derived with + * PASN/SAE should only be allowed with future PASN only. For now do not + * restrict this only for PASN. + */ + wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr, + pasn->sae.pmk, pasn->sae.pmkid); + return 0; +} + + +static struct wpabuf * pasn_get_sae_wd(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct pasn_data *pasn = sta->pasn; + struct wpabuf *buf = NULL; + u8 *len_ptr; + size_t len; + + /* Need to add the entire Authentication frame body */ + buf = wpabuf_alloc(8 + SAE_COMMIT_MAX_LEN + 8 + SAE_CONFIRM_MAX_LEN); + if (!buf) { + wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer"); + return NULL; + } + + /* Need to add the entire authentication frame body for the commit */ + len_ptr = wpabuf_put(buf, 2); + wpabuf_put_le16(buf, WLAN_AUTH_SAE); + wpabuf_put_le16(buf, 1); + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + + /* Write the actual commit and update the length accordingly */ + sae_write_commit(&pasn->sae, buf, NULL, 0); + len = wpabuf_len(buf); + WPA_PUT_LE16(len_ptr, len - 2); + + /* Need to add the entire Authentication frame body for the confirm */ + len_ptr = wpabuf_put(buf, 2); + wpabuf_put_le16(buf, WLAN_AUTH_SAE); + wpabuf_put_le16(buf, 2); + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + + sae_write_confirm(&pasn->sae, buf); + WPA_PUT_LE16(len_ptr, wpabuf_len(buf) - len - 2); + + pasn->sae.state = SAE_CONFIRMED; + + return buf; +} + +#endif /* CONFIG_SAE */ + + +#ifdef CONFIG_FILS + +static struct wpabuf * pasn_get_fils_wd(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct pasn_data *pasn = sta->pasn; + struct pasn_fils_data *fils = &pasn->fils; + struct wpabuf *buf = NULL; + + if (!fils->erp_resp) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Missing erp_resp"); + return NULL; + } + + buf = wpabuf_alloc(1500); + if (!buf) + return NULL; + + /* Add the authentication algorithm */ + wpabuf_put_le16(buf, WLAN_AUTH_FILS_SK); + + /* Authentication Transaction seq# */ + wpabuf_put_le16(buf, 2); + + /* Status Code */ + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + + /* Own RSNE */ + wpa_pasn_add_rsne(buf, NULL, pasn->akmp, pasn->cipher); + + /* FILS Nonce */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN); + wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE); + wpabuf_put_data(buf, fils->anonce, FILS_NONCE_LEN); + + /* FILS Session */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); + wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION); + wpabuf_put_data(buf, fils->session, FILS_SESSION_LEN); + + /* Wrapped Data */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 1 + wpabuf_len(fils->erp_resp)); + wpabuf_put_u8(buf, WLAN_EID_EXT_WRAPPED_DATA); + wpabuf_put_buf(buf, fils->erp_resp); + + return buf; +} + + +static void pasn_fils_auth_resp(struct hostapd_data *hapd, + struct sta_info *sta, u16 status, + struct wpabuf *erp_resp, + const u8 *msk, size_t msk_len) +{ + struct pasn_data *pasn = sta->pasn; + struct pasn_fils_data *fils = &pasn->fils; + u8 pmk[PMK_LEN_MAX]; + size_t pmk_len; + int ret; + + wpa_printf(MSG_DEBUG, "PASN: FILS: Handle AS response - status=%u", + status); + + if (status != WLAN_STATUS_SUCCESS) + goto fail; + + if (!pasn->secret) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Missing secret"); + goto fail; + } + + if (random_get_bytes(fils->anonce, FILS_NONCE_LEN) < 0) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to get ANonce"); + goto fail; + } + + wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS ANonce", + fils->anonce, FILS_NONCE_LEN); + + ret = fils_rmsk_to_pmk(pasn->akmp, msk, msk_len, fils->nonce, + fils->anonce, NULL, 0, pmk, &pmk_len); + if (ret) { + wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK"); + goto fail; + } + + ret = pasn_pmk_to_ptk(pmk, pmk_len, sta->addr, hapd->own_addr, + wpabuf_head(pasn->secret), + wpabuf_len(pasn->secret), + &sta->pasn->ptk, sta->pasn->akmp, + sta->pasn->cipher, WPA_KDK_MAX_LEN); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to derive PTK"); + goto fail; + } + + wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived"); + + wpabuf_free(pasn->secret); + pasn->secret = NULL; + + fils->erp_resp = erp_resp; + ret = handle_auth_pasn_resp(hapd, sta, NULL, WLAN_STATUS_SUCCESS); + fils->erp_resp = NULL; + + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to send response"); + goto fail; + } + + fils->state = PASN_FILS_STATE_COMPLETE; + return; +fail: + ap_free_sta(hapd, sta); +} + + +static int pasn_wd_handle_fils(struct hostapd_data *hapd, struct sta_info *sta, + struct wpabuf *wd) +{ + struct pasn_data *pasn = sta->pasn; + struct pasn_fils_data *fils = &pasn->fils; + struct ieee802_11_elems elems; + struct wpa_ie_data rsne_data; + struct wpabuf *fils_wd; + const u8 *data; + size_t buf_len; + u16 alg, seq, status; + int ret; + + if (fils->state != PASN_FILS_STATE_NONE) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Not expecting wrapped data"); + return -1; + } + + if (!wd) { + wpa_printf(MSG_DEBUG, "PASN: FILS: No wrapped data"); + return -1; + } + + data = wpabuf_head_u8(wd); + buf_len = wpabuf_len(wd); + + if (buf_len < 6) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Buffer too short. len=%lu", + buf_len); + return -1; + } + + alg = WPA_GET_LE16(data); + seq = WPA_GET_LE16(data + 2); + status = WPA_GET_LE16(data + 4); + + wpa_printf(MSG_DEBUG, "PASN: FILS: alg=%u, seq=%u, status=%u", + alg, seq, status); + + if (alg != WLAN_AUTH_FILS_SK || seq != 1 || + status != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, + "PASN: FILS: Dropping peer authentication"); + return -1; + } + + data += 6; + buf_len -= 6; + + if (ieee802_11_parse_elems(data, buf_len, &elems, 1) == ParseFailed) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Could not parse elements"); + return -1; + } + + if (!elems.rsn_ie || !elems.fils_nonce || !elems.fils_nonce || + !elems.wrapped_data) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Missing IEs"); + return -1; + } + + ret = wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2, + &rsne_data); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Failed parsing RNSE"); + return -1; + } + + ret = wpa_pasn_validate_rsne(&rsne_data); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Failed validating RSNE"); + return -1; + } + + if (rsne_data.num_pmkid) { + wpa_printf(MSG_DEBUG, + "PASN: FILS: Not expecting PMKID in RSNE"); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "PASN: FILS: Nonce", elems.fils_nonce, + FILS_NONCE_LEN); + os_memcpy(fils->nonce, elems.fils_nonce, FILS_NONCE_LEN); + + wpa_hexdump(MSG_DEBUG, "PASN: FILS: Session", elems.fils_session, + FILS_SESSION_LEN); + os_memcpy(fils->session, elems.fils_session, FILS_SESSION_LEN); + +#ifdef CONFIG_NO_RADIUS + wpa_printf(MSG_DEBUG, "PASN: FILS: RADIUS is not configured. Fail"); + return -1; +#endif /* CONFIG_NO_RADIUS */ + + fils_wd = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION, + WLAN_EID_EXT_WRAPPED_DATA); + + if (!fils_wd) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Missing wrapped data"); + return -1; + } + + if (!sta->eapol_sm) + sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta); + + wpa_printf(MSG_DEBUG, + "PASN: FILS: Forward EAP-Initiate/Re-auth to AS"); + + ieee802_1x_encapsulate_radius(hapd, sta, wpabuf_head(fils_wd), + wpabuf_len(fils_wd)); + + sta->flags |= WLAN_STA_PENDING_PASN_FILS_ERP; + + fils->state = PASN_FILS_STATE_PENDING_AS; + + /* + * Calculate pending PMKID here so that we do not need to maintain a + * copy of the EAP-Initiate/Reautt message. + */ + fils_pmkid_erp(pasn->akmp, wpabuf_head(fils_wd), wpabuf_len(fils_wd), + fils->erp_pmkid); + + wpabuf_free(fils_wd); + return 0; +} + +#endif /* CONFIG_FILS */ + + +static struct wpabuf * pasn_get_wrapped_data(struct hostapd_data *hapd, + struct sta_info *sta) +{ + switch (sta->pasn->akmp) { + case WPA_KEY_MGMT_PASN: + /* no wrapped data */ + return NULL; + case WPA_KEY_MGMT_SAE: +#ifdef CONFIG_SAE + return pasn_get_sae_wd(hapd, sta); +#else /* CONFIG_SAE */ + wpa_printf(MSG_ERROR, + "PASN: SAE: Cannot derive wrapped data"); + return NULL; +#endif /* CONFIG_SAE */ + case WPA_KEY_MGMT_FILS_SHA256: + case WPA_KEY_MGMT_FILS_SHA384: +#ifdef CONFIG_FILS + return pasn_get_fils_wd(hapd, sta); +#endif /* CONFIG_FILS */ + /* fall through */ + case WPA_KEY_MGMT_FT_PSK: + case WPA_KEY_MGMT_FT_IEEE8021X: + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: + default: + wpa_printf(MSG_ERROR, + "PASN: TODO: Wrapped data for akmp=0x%x", + sta->pasn->akmp); + return NULL; + } +} + + +static int +pasn_derive_keys(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *cached_pmk, size_t cached_pmk_len, + struct wpa_pasn_params_data *pasn_data, + struct wpabuf *wrapped_data, + struct wpabuf *secret) +{ + static const u8 pasn_default_pmk[] = {'P', 'M', 'K', 'z'}; + u8 pmk[PMK_LEN_MAX]; + u8 pmk_len; + int ret; + + os_memset(pmk, 0, sizeof(pmk)); + pmk_len = 0; + + if (!cached_pmk || !cached_pmk_len) + wpa_printf(MSG_DEBUG, "PASN: No valid PMKSA entry"); + + if (sta->pasn->akmp == WPA_KEY_MGMT_PASN) { + wpa_printf(MSG_DEBUG, "PASN: Using default PMK"); + + pmk_len = WPA_PASN_PMK_LEN; + os_memcpy(pmk, pasn_default_pmk, sizeof(pasn_default_pmk)); + } else if (cached_pmk && cached_pmk_len) { + wpa_printf(MSG_DEBUG, "PASN: Using PMKSA entry"); + + pmk_len = cached_pmk_len; + os_memcpy(pmk, cached_pmk, cached_pmk_len); + } else { + switch (sta->pasn->akmp) { +#ifdef CONFIG_SAE + case WPA_KEY_MGMT_SAE: + if (sta->pasn->sae.state == SAE_COMMITTED) { + pmk_len = PMK_LEN; + os_memcpy(pmk, sta->pasn->sae.pmk, PMK_LEN); + break; + } +#endif /* CONFIG_SAE */ + /* fall through */ + default: + /* TODO: Derive PMK based on wrapped data */ + wpa_printf(MSG_DEBUG, + "PASN: Missing PMK derivation"); + return -1; + } + } + + ret = pasn_pmk_to_ptk(pmk, pmk_len, sta->addr, hapd->own_addr, + wpabuf_head(secret), wpabuf_len(secret), + &sta->pasn->ptk, sta->pasn->akmp, + sta->pasn->cipher, WPA_KDK_MAX_LEN); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK"); + return -1; + } + + wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived"); + return 0; +} + + +static int handle_auth_pasn_resp(struct hostapd_data *hapd, + struct sta_info *sta, + struct rsn_pmksa_cache_entry *pmksa, + u16 status) +{ + struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL; + u8 mic[WPA_PASN_MAX_MIC_LEN]; + u8 mic_len, frame_len, data_len; + u8 *ptr; + const u8 *frame, *data, *rsn_ie, *rsnxe_ie; + u8 *data_buf = NULL; + size_t rsn_ie_len; + int ret; + + wpa_printf(MSG_DEBUG, "PASN: Building frame 2: status=%u", status); + + buf = wpabuf_alloc(1500); + if (!buf) + goto fail; + + wpa_pasn_build_auth_header(buf, hapd->own_addr, hapd->own_addr, + sta->addr, 2, status); + + if (status != WLAN_STATUS_SUCCESS) + goto done; + + if (wpa_pasn_add_rsne(buf, pmksa ? pmksa->pmkid : NULL, + sta->pasn->akmp, sta->pasn->cipher) < 0) + goto fail; + + /* No need to derive PMK if PMKSA is given */ + if (!pmksa) + wrapped_data_buf = pasn_get_wrapped_data(hapd, sta); + else + sta->pasn->wrapped_data_format = WPA_PASN_WRAPPED_DATA_NO; + + /* Get public key */ + pubkey = crypto_ecdh_get_pubkey(sta->pasn->ecdh, 0); + pubkey = wpabuf_zeropad(pubkey, + crypto_ecdh_prime_len(sta->pasn->ecdh)); + if (!pubkey) { + wpa_printf(MSG_DEBUG, "PASN: Failed to get pubkey"); + goto fail; + } + + wpa_pasn_add_parameter_ie(buf, sta->pasn->group, + sta->pasn->wrapped_data_format, + pubkey, NULL, 0); + + if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0) + goto fail; + + wpabuf_free(wrapped_data_buf); + wrapped_data_buf = NULL; + wpabuf_free(pubkey); + pubkey = NULL; + + /* Add RSNXE if needed */ + rsnxe_ie = hostapd_wpa_ie(hapd, WLAN_EID_RSNX); + if (rsnxe_ie) + wpabuf_put_data(buf, rsnxe_ie, 2 + rsnxe_ie[1]); + + /* Add the mic */ + mic_len = pasn_mic_len(sta->pasn->akmp, sta->pasn->cipher); + wpabuf_put_u8(buf, WLAN_EID_MIC); + wpabuf_put_u8(buf, mic_len); + ptr = wpabuf_put(buf, mic_len); + + os_memset(ptr, 0, mic_len); + + frame = wpabuf_head_u8(buf) + IEEE80211_HDRLEN; + frame_len = wpabuf_len(buf) - IEEE80211_HDRLEN; + + rsn_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &rsn_ie_len); + if (!rsn_ie || !rsn_ie_len) + goto fail; + + /* + * Note: wpa_auth_get_wpa_ie() might return not only the RSNE but also + * MDE, etc. Thus, do not use the returned length but instead use the + * length specified in the IE header. + */ + data_len = rsn_ie[1] + 2; + if (rsnxe_ie) { + data_buf = os_zalloc(rsn_ie[1] + 2 + rsnxe_ie[1] + 2); + if (!data_buf) + goto fail; + + os_memcpy(data_buf, rsn_ie, rsn_ie[1] + 2); + os_memcpy(data_buf + rsn_ie[1] + 2, rsnxe_ie, rsnxe_ie[1] + 2); + data_len += rsnxe_ie[1] + 2; + data = data_buf; + } else { + data = rsn_ie; + } + + ret = pasn_mic(sta->pasn->ptk.kck, sta->pasn->akmp, sta->pasn->cipher, + hapd->own_addr, sta->addr, data, data_len, + frame, frame_len, mic); + os_free(data_buf); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Frame 3: Failed MIC calculation"); + goto fail; + } + + os_memcpy(ptr, mic, mic_len); + +done: + wpa_printf(MSG_DEBUG, + "PASN: Building frame 2: success; resp STA=" MACSTR, + MAC2STR(sta->addr)); + + ret = hostapd_drv_send_mlme(hapd, wpabuf_head(buf), wpabuf_len(buf), 0, + NULL, 0, 0); + if (ret) + wpa_printf(MSG_INFO, "send_auth_reply: Send failed"); + + wpabuf_free(buf); + return ret; +fail: + wpabuf_free(wrapped_data_buf); + wpabuf_free(pubkey); + wpabuf_free(buf); + return -1; +} + + +static void handle_auth_pasn_1(struct hostapd_data *hapd, struct sta_info *sta, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + struct ieee802_11_elems elems; + struct wpa_ie_data rsn_data; + struct wpa_pasn_params_data pasn_params; + struct rsn_pmksa_cache_entry *pmksa = NULL; + const u8 *cached_pmk = NULL; + size_t cached_pmk_len = 0; +#ifdef CONFIG_IEEE80211R_AP + u8 pmk_r1[PMK_LEN_MAX]; + size_t pmk_r1_len; +#endif /* CONFIG_IEEE80211R_AP */ + struct wpabuf *wrapped_data = NULL, *secret = NULL; + const int *groups = hapd->conf->pasn_groups; + static const int default_groups[] = { 19, 0 }; + u16 status = WLAN_STATUS_SUCCESS; + int ret; + bool derive_keys; + u32 i; + + if (!groups) + groups = default_groups; + + if (ieee802_11_parse_elems(mgmt->u.auth.variable, + len - offsetof(struct ieee80211_mgmt, + u.auth.variable), + &elems, 0) == ParseFailed) { + wpa_printf(MSG_DEBUG, + "PASN: Failed parsing Authentication frame"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + + ret = wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2, + &rsn_data); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed parsing RNSE"); + status = WLAN_STATUS_INVALID_RSNIE; + goto send_resp; + } + + ret = wpa_pasn_validate_rsne(&rsn_data); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE"); + status = WLAN_STATUS_INVALID_RSNIE; + goto send_resp; + } + + if (!(rsn_data.key_mgmt & hapd->conf->wpa_key_mgmt) || + !(rsn_data.pairwise_cipher & hapd->conf->rsn_pairwise)) { + wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher"); + status = WLAN_STATUS_INVALID_RSNIE; + goto send_resp; + } + + sta->pasn->akmp = rsn_data.key_mgmt; + sta->pasn->cipher = rsn_data.pairwise_cipher; + + if (!elems.pasn_params || !elems.pasn_params_len) { + wpa_printf(MSG_DEBUG, + "PASN: No PASN Parameters element found"); + status = WLAN_STATUS_INVALID_PARAMETERS; + goto send_resp; + } + + ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3, + elems.pasn_params_len + 3, + false, &pasn_params); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: Failed validation of PASN Parameters IE"); + status = WLAN_STATUS_INVALID_PARAMETERS; + goto send_resp; + } + + for (i = 0; groups[i] > 0 && groups[i] != pasn_params.group; i++) + ; + + if (!pasn_params.group || groups[i] != pasn_params.group) { + wpa_printf(MSG_DEBUG, "PASN: Requested group=%hu not allowed", + pasn_params.group); + status = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + goto send_resp; + } + + if (!pasn_params.pubkey || !pasn_params.pubkey_len) { + wpa_printf(MSG_DEBUG, "PASN: Invalid public key"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + + sta->pasn->ecdh = crypto_ecdh_init(pasn_params.group); + if (!sta->pasn->ecdh) { + wpa_printf(MSG_DEBUG, "PASN: Failed to init ECDH"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + + sta->pasn->group = pasn_params.group; + + secret = crypto_ecdh_set_peerkey(sta->pasn->ecdh, 0, + pasn_params.pubkey, + pasn_params.pubkey_len); + if (!secret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to derive shared secret"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + + derive_keys = true; + if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) { + wrapped_data = ieee802_11_defrag(&elems, + WLAN_EID_EXTENSION, + WLAN_EID_EXT_WRAPPED_DATA); + if (!wrapped_data) { + wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + +#ifdef CONFIG_SAE + if (sta->pasn->akmp == WPA_KEY_MGMT_SAE) { + ret = pasn_wd_handle_sae_commit(hapd, sta, + wrapped_data); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: Failed processing SAE commit"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + } +#endif /* CONFIG_SAE */ +#ifdef CONFIG_FILS + if (sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 || + sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) { + ret = pasn_wd_handle_fils(hapd, sta, wrapped_data); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: Failed processing FILS wrapped data"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + + wpa_printf(MSG_DEBUG, + "PASN: FILS: Pending AS response"); + + /* + * With PASN/FILS, keys can be derived only after a + * response from the AS is processed. + */ + derive_keys = false; + } +#endif /* CONFIG_FILS */ + } + + sta->pasn->wrapped_data_format = pasn_params.wrapped_data_format; + + ret = pasn_auth_frame_hash(sta->pasn->akmp, sta->pasn->cipher, + ((const u8 *) mgmt) + IEEE80211_HDRLEN, + len - IEEE80211_HDRLEN, sta->pasn->hash); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + + if (!derive_keys) { + wpa_printf(MSG_DEBUG, "PASN: Storing secret"); + sta->pasn->secret = secret; + wpabuf_free(wrapped_data); + return; + } + + if (rsn_data.num_pmkid) { + if (wpa_key_mgmt_ft(sta->pasn->akmp)) { +#ifdef CONFIG_IEEE80211R_AP + wpa_printf(MSG_DEBUG, "PASN: FT: Fetch PMK-R1"); + + ret = wpa_ft_fetch_pmk_r1(hapd->wpa_auth, sta->addr, + rsn_data.pmkid, + pmk_r1, &pmk_r1_len, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: FT: Failed getting PMK-R1"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + cached_pmk = pmk_r1; + cached_pmk_len = pmk_r1_len; +#else /* CONFIG_IEEE80211R_AP */ + wpa_printf(MSG_DEBUG, "PASN: FT: Not supported"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; +#endif /* CONFIG_IEEE80211R_AP */ + } else { + wpa_printf(MSG_DEBUG, "PASN: Try to find PMKSA entry"); + + pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr, + rsn_data.pmkid); + if (pmksa) { + cached_pmk = pmksa->pmk; + cached_pmk_len = pmksa->pmk_len; + } + } + } else { + wpa_printf(MSG_DEBUG, "PASN: No PMKID specified"); + } + + ret = pasn_derive_keys(hapd, sta, cached_pmk, cached_pmk_len, + &pasn_params, wrapped_data, secret); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to derive keys"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + + ret = pasn_auth_frame_hash(sta->pasn->akmp, sta->pasn->cipher, + ((const u8 *) mgmt) + IEEE80211_HDRLEN, + len - IEEE80211_HDRLEN, sta->pasn->hash); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + } + +send_resp: + ret = handle_auth_pasn_resp(hapd, sta, pmksa, status); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to send response"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + } else { + wpa_printf(MSG_DEBUG, + "PASN: Success handling transaction == 1"); + } + + wpabuf_free(secret); + wpabuf_free(wrapped_data); + + if (status != WLAN_STATUS_SUCCESS) + ap_free_sta(hapd, sta); +} + + +static void handle_auth_pasn_3(struct hostapd_data *hapd, struct sta_info *sta, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + struct ieee802_11_elems elems; + struct wpa_pasn_params_data pasn_params; + struct wpabuf *wrapped_data = NULL; + u8 mic[WPA_PASN_MAX_MIC_LEN], out_mic[WPA_PASN_MAX_MIC_LEN]; + u8 mic_len; + int ret; + + if (ieee802_11_parse_elems(mgmt->u.auth.variable, + len - offsetof(struct ieee80211_mgmt, + u.auth.variable), + &elems, 0) == ParseFailed) { + wpa_printf(MSG_DEBUG, + "PASN: Failed parsing Authentication frame"); + goto fail; + } + + /* Check that the MIC IE exists. Save it and zero out the memory. */ + mic_len = pasn_mic_len(sta->pasn->akmp, sta->pasn->cipher); + if (!elems.mic || elems.mic_len != mic_len) { + wpa_printf(MSG_DEBUG, + "PASN: Invalid MIC. Expecting len=%u", mic_len); + goto fail; + } else { + os_memcpy(mic, elems.mic, mic_len); + /* TODO: Clean this up.. Should not modify received frame + * buffer. */ + os_memset((u8 *) elems.mic, 0, mic_len); + } + + if (!elems.pasn_params || !elems.pasn_params_len) { + wpa_printf(MSG_DEBUG, + "PASN: No PASN Parameters element found"); + goto fail; + } + + ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3, + elems.pasn_params_len + 3, + false, &pasn_params); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: Failed validation of PASN Parameters IE"); + goto fail; + } + + if (pasn_params.pubkey || pasn_params.pubkey_len) { + wpa_printf(MSG_DEBUG, + "PASN: Public key should not be included"); + goto fail; + } + + /* Verify the MIC */ + ret = pasn_mic(sta->pasn->ptk.kck, sta->pasn->akmp, sta->pasn->cipher, + sta->addr, hapd->own_addr, + sta->pasn->hash, mic_len * 2, + (u8 *) &mgmt->u.auth, + len - offsetof(struct ieee80211_mgmt, u.auth), + out_mic); + + wpa_hexdump_key(MSG_DEBUG, "PASN: Frame MIC", mic, mic_len); + if (ret || os_memcmp(mic, out_mic, mic_len) != 0) { + wpa_printf(MSG_DEBUG, "PASN: Failed MIC verification"); + goto fail; + } + + if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) { + wrapped_data = ieee802_11_defrag(&elems, + WLAN_EID_EXTENSION, + WLAN_EID_EXT_WRAPPED_DATA); + + if (!wrapped_data) { + wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data"); + goto fail; + } + +#ifdef CONFIG_SAE + if (sta->pasn->akmp == WPA_KEY_MGMT_SAE) { + ret = pasn_wd_handle_sae_confirm(hapd, sta, + wrapped_data); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: Failed processing SAE confirm"); + wpabuf_free(wrapped_data); + goto fail; + } + } +#endif /* CONFIG_SAE */ +#ifdef CONFIG_FILS + if (sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 || + sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) { + if (wrapped_data) { + wpa_printf(MSG_DEBUG, + "PASN: FILS: Ignore wrapped data"); + } + } +#endif /* CONFIG_FILS */ + wpabuf_free(wrapped_data); + } + + wpa_printf(MSG_INFO, + "PASN: Success handling transaction == 3. Store PTK"); + + ptksa_cache_add(hapd->ptksa, sta->addr, sta->pasn->cipher, 43200, + &sta->pasn->ptk); +fail: + ap_free_sta(hapd, sta); +} + + +static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta, + const struct ieee80211_mgmt *mgmt, size_t len, + u16 trans_seq, u16 status) +{ + if (hapd->conf->wpa != WPA_PROTO_RSN) { + wpa_printf(MSG_INFO, "PASN: RSN is not configured"); + return; + } + + wpa_printf(MSG_INFO, "PASN authentication: sta=" MACSTR, + MAC2STR(sta->addr)); + + if (trans_seq == 1) { + if (sta->pasn) { + wpa_printf(MSG_DEBUG, + "PASN: Not expecting transaction == 1"); + return; + } + + if (status != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, + "PASN: Failure status in transaction == 1"); + return; + } + + sta->pasn = os_zalloc(sizeof(*sta->pasn)); + if (!sta->pasn) { + wpa_printf(MSG_DEBUG, + "PASN: Failed to allocate PASN context"); + return; + } + + handle_auth_pasn_1(hapd, sta, mgmt, len); + } else if (trans_seq == 3) { + if (!sta->pasn) { + wpa_printf(MSG_DEBUG, + "PASN: Not expecting transaction == 3"); + return; + } + + if (status != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, + "PASN: Failure status in transaction == 3"); + ap_free_sta_pasn(hapd, sta); + return; + } + + handle_auth_pasn_3(hapd, sta, mgmt, len); + } else { + wpa_printf(MSG_DEBUG, + "PASN: Invalid transaction %u - ignore", trans_seq); + } +} + +#endif /* CONFIG_PASN */ + + static void handle_auth(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int rssi, int from_queue) @@ -2382,6 +3500,11 @@ static void handle_auth(struct hostapd_data *hapd, hapd->conf->fils_dh_group && auth_alg == WLAN_AUTH_FILS_SK_PFS) || #endif /* CONFIG_FILS */ +#ifdef CONFIG_PASN + (hapd->conf->wpa && + (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PASN) && + auth_alg == WLAN_AUTH_PASN) || +#endif /* CONFIG_PASN */ ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) && auth_alg == WLAN_AUTH_SHARED_KEY))) { wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)", @@ -2391,6 +3514,9 @@ static void handle_auth(struct hostapd_data *hapd, } if (!(auth_transaction == 1 || auth_alg == WLAN_AUTH_SAE || +#ifdef CONFIG_PASN + (auth_alg == WLAN_AUTH_PASN && auth_transaction == 3) || +#endif /* CONFIG_PASN */ (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) { wpa_printf(MSG_INFO, "Unknown authentication transaction number (%d)", auth_transaction); @@ -2512,6 +3638,15 @@ static void handle_auth(struct hostapd_data *hapd, return; } #endif /* CONFIG_MESH */ +#ifdef CONFIG_PASN + if (auth_alg == WLAN_AUTH_PASN && + (sta->flags & WLAN_STA_ASSOC)) { + wpa_printf(MSG_DEBUG, + "PASN: auth: Existing station: " MACSTR, + MAC2STR(sta->addr)); + return; + } +#endif /* CONFIG_PASN */ } else { #ifdef CONFIG_MESH if (hapd->conf->mesh & MESH_ENABLED) { @@ -2572,11 +3707,14 @@ static void handle_auth(struct hostapd_data *hapd, * to allow the original connection work until the attempt can complete * (re)association, so that unprotected Authentication frame cannot be * used to bypass PMF protection. + * + * PASN authentication does not require adding/removing station to the + * driver so skip this flow in case of PASN authentication. */ if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) && (!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta)) && !(hapd->conf->mesh & MESH_ENABLED) && - !(sta->added_unassoc)) { + !(sta->added_unassoc) && auth_alg != WLAN_AUTH_PASN) { if (ap_sta_re_add(hapd, sta) < 0) { resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; goto fail; @@ -2663,6 +3801,12 @@ static void handle_auth(struct hostapd_data *hapd, handle_auth_fils_finish); return; #endif /* CONFIG_FILS */ +#ifdef CONFIG_PASN + case WLAN_AUTH_PASN: + handle_auth_pasn(hapd, sta, mgmt, len, auth_transaction, + status_code); + return; +#endif /* CONFIG_PASN */ } fail: @@ -3254,7 +4398,7 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_IEEE80211AC */ #ifdef CONFIG_IEEE80211AX - if (hapd->iconf->ieee80211ax) { + if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) { resp = copy_sta_he_capab(hapd, sta, IEEE80211_MODE_AP, elems.he_capabilities, elems.he_capabilities_len); @@ -3870,7 +5014,7 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, #endif /* CONFIG_IEEE80211AC */ #ifdef CONFIG_IEEE80211AX - if (hapd->iconf->ieee80211ax) { + if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) { p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP); p = hostapd_eid_he_operation(hapd, p); p = hostapd_eid_spatial_reuse(hapd, p); @@ -4884,6 +6028,31 @@ static int handle_action(struct hostapd_data *hapd, /** + * notify_mgmt_frame - Notify of Management frames on the control interface + * @hapd: hostapd BSS data structure (the BSS to which the Management frame was + * sent to) + * @buf: Management frame data (starting from the IEEE 802.11 header) + * @len: Length of frame data in octets + * + * Notify the control interface of any received Management frame. + */ +static void notify_mgmt_frame(struct hostapd_data *hapd, const u8 *buf, + size_t len) +{ + + int hex_len = len * 2 + 1; + char *hex = os_malloc(hex_len); + + if (hex) { + wpa_snprintf_hex(hex, hex_len, buf, len); + wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, + AP_MGMT_FRAME_RECEIVED "buf=%s", hex); + os_free(hex); + } +} + + +/** * ieee802_11_mgmt - process incoming IEEE 802.11 management frames * @hapd: hostapd BSS data structure (the BSS to which the management frame was * sent to) @@ -4973,6 +6142,9 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, if (hapd->iconf->track_sta_max_num) sta_track_add(hapd->iface, mgmt->sa, ssi_signal); + if (hapd->conf->notify_mgmt_frames) + notify_mgmt_frame(hapd, buf, len); + switch (stype) { case WLAN_FC_STYPE_AUTH: wpa_printf(MSG_DEBUG, "mgmt::auth"); @@ -5020,6 +6192,7 @@ static void handle_auth_cb(struct hostapd_data *hapd, { u16 auth_alg, auth_transaction, status_code; struct sta_info *sta; + bool success_status; sta = ap_get_sta(hapd, mgmt->da); if (!sta) { @@ -5029,6 +6202,15 @@ static void handle_auth_cb(struct hostapd_data *hapd, return; } + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { + wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)", + (unsigned long) len); + auth_alg = 0; + auth_transaction = 0; + status_code = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + auth_alg = le_to_host16(mgmt->u.auth.auth_alg); auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); status_code = le_to_host16(mgmt->u.auth.status_code); @@ -5040,12 +6222,6 @@ static void handle_auth_cb(struct hostapd_data *hapd, goto fail; } - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { - wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)", - (unsigned long) len); - goto fail; - } - if (status_code == WLAN_STATUS_SUCCESS && ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) || (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) { @@ -5058,7 +6234,12 @@ static void handle_auth_cb(struct hostapd_data *hapd, } fail: - if (status_code != WLAN_STATUS_SUCCESS && sta->added_unassoc) { + success_status = status_code == WLAN_STATUS_SUCCESS; +#ifdef CONFIG_SAE + if (auth_alg == WLAN_AUTH_SAE && auth_transaction == 1) + success_status = sae_status_success(hapd, status_code); +#endif /* CONFIG_SAE */ + if (!success_status && sta->added_unassoc) { hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; } @@ -5588,6 +6769,118 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, } +u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid) +{ + struct hostapd_iface *iface = hapd->iface; + struct hostapd_config *iconf = iface->conf; + struct hostapd_hw_modes *mode = iface->current_mode; + struct hostapd_channel_data *chan; + int dfs, i; + u8 channel, tx_pwr_count, local_pwr_constraint; + int max_tx_power; + u8 tx_pwr; + + if (!mode) + return eid; + + if (ieee80211_freq_to_chan(iface->freq, &channel) == NUM_HOSTAPD_MODES) + return eid; + + for (i = 0; i < mode->num_channels; i++) { + if (mode->channels[i].freq == iface->freq) + break; + } + if (i == mode->num_channels) + return eid; + + switch (hostapd_get_oper_chwidth(iconf)) { + case CHANWIDTH_USE_HT: + if (iconf->secondary_channel == 0) { + /* Max Transmit Power count = 0 (20 MHz) */ + tx_pwr_count = 0; + } else { + /* Max Transmit Power count = 1 (20, 40 MHz) */ + tx_pwr_count = 1; + } + break; + case CHANWIDTH_80MHZ: + /* Max Transmit Power count = 2 (20, 40, and 80 MHz) */ + tx_pwr_count = 2; + break; + case CHANWIDTH_80P80MHZ: + case CHANWIDTH_160MHZ: + /* Max Transmit Power count = 3 (20, 40, 80, 160/80+80 MHz) */ + tx_pwr_count = 3; + break; + default: + return eid; + } + + /* + * Below local_pwr_constraint logic is referred from + * hostapd_eid_pwr_constraint. + * + * Check if DFS is required by regulatory. + */ + dfs = hostapd_is_dfs_required(hapd->iface); + if (dfs < 0) + dfs = 0; + + /* + * In order to meet regulations when TPC is not implemented using + * a transmit power that is below the legal maximum (including any + * mitigation factor) should help. In this case, indicate 3 dB below + * maximum allowed transmit power. + */ + if (hapd->iconf->local_pwr_constraint == -1) + local_pwr_constraint = (dfs == 0) ? 0 : 3; + else + local_pwr_constraint = hapd->iconf->local_pwr_constraint; + + /* + * A STA that is not an AP shall use a transmit power less than or + * equal to the local maximum transmit power level for the channel. + * The local maximum transmit power can be calculated from the formula: + * local max TX pwr = max TX pwr - local pwr constraint + * Where max TX pwr is maximum transmit power level specified for + * channel in Country element and local pwr constraint is specified + * for channel in this Power Constraint element. + */ + chan = &mode->channels[i]; + max_tx_power = chan->max_tx_power - local_pwr_constraint; + + /* + * Local Maximum Transmit power is encoded as two's complement + * with a 0.5 dB step. + */ + max_tx_power *= 2; /* in 0.5 dB steps */ + if (max_tx_power > 127) { + /* 63.5 has special meaning of 63.5 dBm or higher */ + max_tx_power = 127; + } + if (max_tx_power < -128) + max_tx_power = -128; + if (max_tx_power < 0) + tx_pwr = 0x80 + max_tx_power + 128; + else + tx_pwr = max_tx_power; + + *eid++ = WLAN_EID_TRANSMIT_POWER_ENVELOPE; + *eid++ = 2 + tx_pwr_count; + + /* + * Max Transmit Power count and + * Max Transmit Power units = 0 (EIRP) + */ + *eid++ = tx_pwr_count; + + for (i = 0; i <= tx_pwr_count; i++) + *eid++ = tx_pwr; + + return eid; +} + + u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid) { u8 bw, chan1, chan2 = 0; diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c index 85b7140..c27bb1f 100644 --- a/src/ap/ieee802_11_he.c +++ b/src/ap/ieee802_11_he.c @@ -304,6 +304,11 @@ u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid) if (spr->sr_ctrl & SPATIAL_REUSE_SRG_INFORMATION_PRESENT) { *spr_param++ = hapd->iface->conf->spr.srg_obss_pd_min_offset; *spr_param++ = hapd->iface->conf->spr.srg_obss_pd_max_offset; + os_memcpy(spr_param, + hapd->iface->conf->spr.srg_bss_color_bitmap, 8); + spr_param += 8; + os_memcpy(spr_param, + hapd->iface->conf->spr.srg_partial_bssid_bitmap, 8); pos += 18; } @@ -313,6 +318,7 @@ u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid) u8 * hostapd_eid_he_6ghz_band_cap(struct hostapd_data *hapd, u8 *eid) { + struct hostapd_config *conf = hapd->iface->conf; struct hostapd_hw_modes *mode = hapd->iface->current_mode; struct he_capabilities *he_cap; struct ieee80211_he_6ghz_band_cap *cap; @@ -324,8 +330,18 @@ u8 * hostapd_eid_he_6ghz_band_cap(struct hostapd_data *hapd, u8 *eid) return eid; he_cap = &mode->he_capab[IEEE80211_MODE_AP]; - capab = he_cap->he_6ghz_capa; + capab = he_cap->he_6ghz_capa & HE_6GHZ_BAND_CAP_MIN_MPDU_START; + capab |= (conf->he_6ghz_max_ampdu_len_exp << + HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_SHIFT) & + HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_MASK; + capab |= (conf->he_6ghz_max_mpdu << + HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_SHIFT) & + HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_MASK; capab |= HE_6GHZ_BAND_CAP_SMPS_DISABLED; + if (conf->he_6ghz_rx_ant_pat) + capab |= HE_6GHZ_BAND_CAP_RX_ANTPAT_CONS; + if (conf->he_6ghz_tx_ant_pat) + capab |= HE_6GHZ_BAND_CAP_TX_ANTPAT_CONS; pos = eid; *pos++ = WLAN_EID_EXTENSION; @@ -419,6 +435,7 @@ u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta, size_t he_capab_len) { if (!he_capab || !hapd->iconf->ieee80211ax || + hapd->conf->disable_11ax || !check_valid_he_mcs(hapd, he_capab, opmode) || ieee80211_invalid_he_cap_size(he_capab, he_capab_len) || he_capab_len > sizeof(struct ieee80211_he_capabilities)) { @@ -448,6 +465,7 @@ u16 copy_sta_he_6ghz_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *he_6ghz_capab) { if (!he_6ghz_capab || !hapd->iconf->ieee80211ax || + hapd->conf->disable_11ax || !is_6ghz_op_class(hapd->iconf->op_class)) { sta->flags &= ~WLAN_STA_6GHZ; os_free(sta->he_6ghz_capab); diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c index 17003d5..a429b5d 100644 --- a/src/ap/ieee802_11_shared.c +++ b/src/ap/ieee802_11_shared.c @@ -425,7 +425,9 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx) * Identifiers Used Exclusively */ } #endif /* CONFIG_SAE */ - if (hapd->conf->beacon_prot) + if (hapd->conf->beacon_prot && + (hapd->iface->drv_flags & + WPA_DRIVER_FLAGS_BEACON_PROTECTION)) *pos |= 0x10; /* Bit 84 - Beacon Protection Enabled */ break; case 11: /* Bits 88-95 */ @@ -494,7 +496,8 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) hostapd_sae_pw_id_in_use(hapd->conf)) len = 11; #endif /* CONFIG_SAE */ - if (len < 11 && hapd->conf->beacon_prot) + if (len < 11 && hapd->conf->beacon_prot && + (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_BEACON_PROTECTION)) len = 11; #ifdef CONFIG_SAE_PK if (len < 12 && hapd->conf->wpa && @@ -1097,29 +1100,45 @@ u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len) { u8 *pos = eid; bool sae_pk = false; + u16 capab = 0; + size_t flen; + + if (!(hapd->conf->wpa & WPA_PROTO_RSN)) + return eid; #ifdef CONFIG_SAE_PK sae_pk = hostapd_sae_pk_in_use(hapd->conf); #endif /* CONFIG_SAE_PK */ - if (!(hapd->conf->wpa & WPA_PROTO_RSN) || - !wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) || - (hapd->conf->sae_pwe != 1 && hapd->conf->sae_pwe != 2 && - !hostapd_sae_pw_id_in_use(hapd->conf) && !sae_pk) || - hapd->conf->sae_pwe == 3 || - len < 3) - return pos; - - *pos++ = WLAN_EID_RSNX; - *pos++ = 1; - /* bits 0-3 = 0 since only one octet of Extended RSN Capabilities is - * used for now */ - *pos = BIT(WLAN_RSNX_CAPAB_SAE_H2E); + if (wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) && + (hapd->conf->sae_pwe == 1 || hapd->conf->sae_pwe == 2 || + hostapd_sae_pw_id_in_use(hapd->conf) || sae_pk) && + hapd->conf->sae_pwe != 3) { + capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E); #ifdef CONFIG_SAE_PK - if (sae_pk) - *pos |= BIT(WLAN_RSNX_CAPAB_SAE_PK); + if (sae_pk) + capab |= BIT(WLAN_RSNX_CAPAB_SAE_PK); #endif /* CONFIG_SAE_PK */ - pos++; + } + + if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF) + capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF); + if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT) + capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT); + if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG) + capab |= BIT(WLAN_RSNX_CAPAB_PROT_RANGE_NEG); + + flen = (capab & 0xff00) ? 2 : 1; + if (len < 2 + flen || !capab) + return eid; /* no supported extended RSN capabilities */ + capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */ + + *pos++ = WLAN_EID_RSNX; + *pos++ = flen; + *pos++ = capab & 0x00ff; + capab >>= 8; + if (capab) + *pos++ = capab; return pos; } diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c index c925bf1..d037022 100644 --- a/src/ap/ieee802_11_vht.c +++ b/src/ap/ieee802_11_vht.c @@ -167,118 +167,6 @@ static int check_valid_vht_mcs(struct hostapd_hw_modes *mode, } -u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid) -{ - struct hostapd_iface *iface = hapd->iface; - struct hostapd_config *iconf = iface->conf; - struct hostapd_hw_modes *mode = iface->current_mode; - struct hostapd_channel_data *chan; - int dfs, i; - u8 channel, tx_pwr_count, local_pwr_constraint; - int max_tx_power; - u8 tx_pwr; - - if (!mode) - return eid; - - if (ieee80211_freq_to_chan(iface->freq, &channel) == NUM_HOSTAPD_MODES) - return eid; - - for (i = 0; i < mode->num_channels; i++) { - if (mode->channels[i].freq == iface->freq) - break; - } - if (i == mode->num_channels) - return eid; - - switch (iface->conf->vht_oper_chwidth) { - case CHANWIDTH_USE_HT: - if (iconf->secondary_channel == 0) { - /* Max Transmit Power count = 0 (20 MHz) */ - tx_pwr_count = 0; - } else { - /* Max Transmit Power count = 1 (20, 40 MHz) */ - tx_pwr_count = 1; - } - break; - case CHANWIDTH_80MHZ: - /* Max Transmit Power count = 2 (20, 40, and 80 MHz) */ - tx_pwr_count = 2; - break; - case CHANWIDTH_80P80MHZ: - case CHANWIDTH_160MHZ: - /* Max Transmit Power count = 3 (20, 40, 80, 160/80+80 MHz) */ - tx_pwr_count = 3; - break; - default: - return eid; - } - - /* - * Below local_pwr_constraint logic is referred from - * hostapd_eid_pwr_constraint. - * - * Check if DFS is required by regulatory. - */ - dfs = hostapd_is_dfs_required(hapd->iface); - if (dfs < 0) - dfs = 0; - - /* - * In order to meet regulations when TPC is not implemented using - * a transmit power that is below the legal maximum (including any - * mitigation factor) should help. In this case, indicate 3 dB below - * maximum allowed transmit power. - */ - if (hapd->iconf->local_pwr_constraint == -1) - local_pwr_constraint = (dfs == 0) ? 0 : 3; - else - local_pwr_constraint = hapd->iconf->local_pwr_constraint; - - /* - * A STA that is not an AP shall use a transmit power less than or - * equal to the local maximum transmit power level for the channel. - * The local maximum transmit power can be calculated from the formula: - * local max TX pwr = max TX pwr - local pwr constraint - * Where max TX pwr is maximum transmit power level specified for - * channel in Country element and local pwr constraint is specified - * for channel in this Power Constraint element. - */ - chan = &mode->channels[i]; - max_tx_power = chan->max_tx_power - local_pwr_constraint; - - /* - * Local Maximum Transmit power is encoded as two's complement - * with a 0.5 dB step. - */ - max_tx_power *= 2; /* in 0.5 dB steps */ - if (max_tx_power > 127) { - /* 63.5 has special meaning of 63.5 dBm or higher */ - max_tx_power = 127; - } - if (max_tx_power < -128) - max_tx_power = -128; - if (max_tx_power < 0) - tx_pwr = 0x80 + max_tx_power + 128; - else - tx_pwr = max_tx_power; - - *eid++ = WLAN_EID_VHT_TRANSMIT_POWER_ENVELOPE; - *eid++ = 2 + tx_pwr_count; - - /* - * Max Transmit Power count and - * Max Transmit Power units = 0 (EIRP) - */ - *eid++ = tx_pwr_count; - - for (i = 0; i <= tx_pwr_count; i++) - *eid++ = tx_pwr; - - return eid; -} - - u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *vht_capab) { diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index ee095f6..753c883 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -2067,7 +2067,8 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, #ifdef CONFIG_FILS #ifdef NEED_AP_MLME - if (sta->flags & WLAN_STA_PENDING_FILS_ERP) { + if (sta->flags & + (WLAN_STA_PENDING_FILS_ERP | WLAN_STA_PENDING_PASN_FILS_ERP)) { /* TODO: Add a PMKSA entry on success? */ ieee802_11_finish_fils_auth( hapd, sta, hdr->code == RADIUS_CODE_ACCESS_ACCEPT, diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c index 01bf886..2bbe318 100644 --- a/src/ap/neighbor_db.c +++ b/src/ap/neighbor_db.c @@ -220,7 +220,7 @@ void hostapd_neighbor_set_own_report(struct hostapd_data *hapd) u16 capab = hostapd_own_capab_info(hapd); int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n; int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac; - int he = hapd->iconf->ieee80211ax; + int he = hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax; struct wpa_ssid_value ssid; u8 channel, op_class; u8 center_freq1_idx = 0, center_freq2_idx = 0; diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 67b5e98..ccd1ed9 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -156,6 +156,37 @@ void ap_sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta) } +#ifdef CONFIG_PASN + +void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta) +{ + if (sta->pasn) { + wpa_printf(MSG_DEBUG, "PASN: Free PASN context: " MACSTR, + MAC2STR(sta->addr)); + + if (sta->pasn->ecdh) + crypto_ecdh_deinit(sta->pasn->ecdh); + + wpabuf_free(sta->pasn->secret); + sta->pasn->secret = NULL; + +#ifdef CONFIG_SAE + sae_clear_data(&sta->pasn->sae); +#endif /* CONFIG_SAE */ + +#ifdef CONFIG_FILS + /* In practice this pointer should be NULL */ + wpabuf_free(sta->pasn->fils.erp_resp); + sta->pasn->fils.erp_resp = NULL; +#endif /* CONFIG_FILS */ + + bin_clear_free(sta->pasn, sizeof(*sta->pasn)); + sta->pasn = NULL; + } +} + +#endif /* CONFIG_PASN */ + void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) { int set_beacon = 0; @@ -371,6 +402,10 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta); #endif /* CONFIG_WNM_AP */ +#ifdef CONFIG_PASN + ap_free_sta_pasn(hapd, sta); +#endif /* CONFIG_PASN */ + os_free(sta->ifname_wds); #ifdef CONFIG_TESTING_OPTIONS diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index ef48561..efa48e7 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -14,6 +14,8 @@ #include "vlan.h" #include "common/wpa_common.h" #include "common/ieee802_11_defs.h" +#include "common/sae.h" +#include "crypto/sha384.h" /* STA flags */ #define WLAN_STA_AUTH BIT(0) @@ -39,6 +41,7 @@ #define WLAN_STA_MULTI_AP BIT(23) #define WLAN_STA_HE BIT(24) #define WLAN_STA_6GHZ BIT(25) +#define WLAN_STA_PENDING_PASN_FILS_ERP BIT(26) #define WLAN_STA_PENDING_DISASSOC_CB BIT(29) #define WLAN_STA_PENDING_DEAUTH_CB BIT(30) #define WLAN_STA_NONERP BIT(31) @@ -63,6 +66,42 @@ struct pending_eapol_rx { struct os_reltime rx_time; }; +enum pasn_fils_state { + PASN_FILS_STATE_NONE = 0, + PASN_FILS_STATE_PENDING_AS, + PASN_FILS_STATE_COMPLETE +}; + +struct pasn_fils_data { + u8 state; + u8 nonce[FILS_NONCE_LEN]; + u8 anonce[FILS_NONCE_LEN]; + u8 session[FILS_SESSION_LEN]; + u8 erp_pmkid[PMKID_LEN]; + + struct wpabuf *erp_resp; +}; + +struct pasn_data { + int akmp; + int cipher; + u16 group; + u8 trans_seq; + u8 wrapped_data_format; + + u8 hash[SHA384_MAC_LEN]; + struct wpa_ptk ptk; + struct crypto_ecdh *ecdh; + + struct wpabuf *secret; +#ifdef CONFIG_SAE + struct sae_data sae; +#endif /* CONFIG_SAE */ +#ifdef CONFIG_FILS + struct pasn_fils_data fils; +#endif /* CONFIG_FILS */ +}; + struct sta_info { struct sta_info *next; /* next entry in sta list */ struct sta_info *hnext; /* next entry in hash table list */ @@ -286,6 +325,10 @@ struct sta_info { unsigned int airtime_weight; struct os_reltime backlogged_until; #endif /* CONFIG_AIRTIME_POLICY */ + +#ifdef CONFIG_PASN + struct pasn_data *pasn; +#endif /* CONFIG_PASN */ }; @@ -363,4 +406,6 @@ int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd, struct sta_info *sta); int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta); +void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta); + #endif /* STA_INFO_H */ diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c index be81797..d32967e 100644 --- a/src/ap/wnm_ap.c +++ b/src/ap/wnm_ap.c @@ -169,7 +169,9 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, pos += igtk_elem_len; wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d", (int) igtk_elem_len); - if (hapd->conf->beacon_prot) { + if (hapd->conf->beacon_prot && + (hapd->iface->drv_flags & + WPA_DRIVER_FLAGS_BEACON_PROTECTION)) { res = wpa_wnmsleep_bigtk_subelem(sta->wpa_sm, pos); if (res < 0) goto fail; @@ -537,7 +539,8 @@ static void wnm_beacon_protection_failure(struct hostapd_data *hapd, { struct sta_info *sta; - if (!hapd->conf->beacon_prot) + if (!hapd->conf->beacon_prot || + !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_BEACON_PROTECTION)) return; sta = ap_get_sta(hapd, addr); diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 82a9746..7c53797 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -224,6 +224,23 @@ int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth, } +void wpa_auth_store_ptksa(struct wpa_authenticator *wpa_auth, + const u8 *addr, int cipher, + u32 life_time, const struct wpa_ptk *ptk) +{ + if (wpa_auth->cb->store_ptksa) + wpa_auth->cb->store_ptksa(wpa_auth->cb_ctx, addr, cipher, + life_time, ptk); +} + + +void wpa_auth_remove_ptksa(struct wpa_authenticator *wpa_auth, + const u8 *addr, int cipher) +{ + if (wpa_auth->cb->clear_ptksa) + wpa_auth->cb->clear_ptksa(wpa_auth->cb_ctx, addr, cipher); +} + void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr, logger_level level, const char *txt) { @@ -1739,6 +1756,9 @@ void wpa_remove_ptk(struct wpa_state_machine *sm) { sm->PTK_valid = false; os_memset(&sm->PTK, 0, sizeof(sm->PTK)); + + wpa_auth_remove_ptksa(sm->wpa_auth, sm->addr, sm->pairwise); + if (wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, 0, KEY_FLAG_PAIRWISE)) wpa_printf(MSG_DEBUG, @@ -2258,9 +2278,17 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, struct wpa_ptk *ptk, int force_sha256) { const u8 *z = NULL; - size_t z_len = 0; + size_t z_len = 0, kdk_len; int akmp; + if (sm->wpa_auth->conf.force_kdk_derivation || + (sm->wpa_auth->conf.secure_ltf && + sm->rsnxe && sm->rsnxe_len >= 4 && + sm->rsnxe[3] & BIT(WLAN_RSNX_CAPAB_SECURE_LTF - 8))) + kdk_len = WPA_KDK_MAX_LEN; + else + kdk_len = 0; + #ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { if (sm->ft_completed) { @@ -2272,7 +2300,8 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, sm->pmk_r1_name, ptk, ptk_name, sm->wpa_key_mgmt, - sm->pairwise); + sm->pairwise, + kdk_len); } return wpa_auth_derive_ptk_ft(sm, ptk); } @@ -2290,7 +2319,7 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, akmp |= WPA_KEY_MGMT_PSK_SHA256; return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion", sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce, - ptk, akmp, sm->pairwise, z, z_len); + ptk, akmp, sm->pairwise, z, z_len, kdk_len); } @@ -2305,13 +2334,21 @@ int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk, size_t ick_len; int res; u8 fils_ft[FILS_FT_MAX_LEN]; - size_t fils_ft_len = 0; + size_t fils_ft_len = 0, kdk_len; + + if (sm->wpa_auth->conf.force_kdk_derivation || + (sm->wpa_auth->conf.secure_ltf && + sm->rsnxe && sm->rsnxe_len >= 4 && + sm->rsnxe[3] & BIT(WLAN_RSNX_CAPAB_SECURE_LTF - 8))) + kdk_len = WPA_KDK_MAX_LEN; + else + kdk_len = 0; res = fils_pmk_to_ptk(pmk, pmk_len, sm->addr, sm->wpa_auth->addr, snonce, anonce, dhss, dhss_len, &sm->PTK, ick, &ick_len, sm->wpa_key_mgmt, sm->pairwise, - fils_ft, &fils_ft_len); + fils_ft, &fils_ft_len, kdk_len); if (res < 0) return res; sm->PTK_valid = true; @@ -2819,6 +2856,9 @@ int fils_set_tk(struct wpa_state_machine *sm) } sm->tk_already_set = true; + wpa_auth_store_ptksa(sm->wpa_auth, sm->addr, sm->pairwise, + dot11RSNAConfigPMKLifetime, &sm->PTK); + return 0; } @@ -3353,6 +3393,8 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) wpa_ie_len > wpa_ie[1] + 2U && wpa_ie[0] == WLAN_EID_RSN) { /* WPA-only STA, remove RSN IE and possible MDIE */ wpa_ie = wpa_ie + wpa_ie[1] + 2; + if (wpa_ie[0] == WLAN_EID_RSNX) + wpa_ie = wpa_ie + wpa_ie[1] + 2; if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN) wpa_ie = wpa_ie + wpa_ie[1] + 2; wpa_ie_len = wpa_ie[1] + 2; @@ -3607,6 +3649,8 @@ SM_STATE(WPA_PTK, PTKINITDONE) sm->pairwise_set = true; wpa_auth_set_ptk_rekey_timer(sm); + wpa_auth_store_ptksa(sm->wpa_auth, sm->addr, sm->pairwise, + dot11RSNAConfigPMKLifetime, &sm->PTK); if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) || sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP || @@ -5355,6 +5399,8 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm, wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) { /* WPA-only STA, remove RSN IE and possible MDIE */ wpa_ie = wpa_ie + wpa_ie[1] + 2; + if (wpa_ie[0] == WLAN_EID_RSNX) + wpa_ie = wpa_ie + wpa_ie[1] + 2; if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN) wpa_ie = wpa_ie + wpa_ie[1] + 2; wpa_ie_len = wpa_ie[1] + 2; diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 5f9df9c..eaa2caf 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -257,11 +257,22 @@ struct wpa_auth_config { #endif /* CONFIG_FILS */ int sae_pwe; bool sae_pk; + + unsigned int secure_ltf:1; + unsigned int secure_rtt:1; + unsigned int prot_range_neg:1; + int owe_ptk_workaround; u8 transition_disable; #ifdef CONFIG_DPP2 int dpp_pfs; #endif /* CONFIG_DPP2 */ + + /* + * If set Key Derivation Key should be derived as part of PMK to + * PTK derivation regardless of advertised capabilities. + */ + bool force_kdk_derivation; }; typedef enum { @@ -306,6 +317,9 @@ struct wpa_auth_callbacks { int (*get_sta_tx_params)(void *ctx, const u8 *addr, int ap_max_chanwidth, int ap_seg1_idx, int *bandwidth, int *seg1_idx); + void (*store_ptksa)(void *ctx, const u8 *addr, int cipher, + u32 life_time, const struct wpa_ptk *ptk); + void (*clear_ptksa)(void *ctx, const u8 *addr, int cipher); #ifdef CONFIG_IEEE80211R_AP struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr); int (*add_sta_ft)(void *ctx, const u8 *sta_addr); @@ -459,6 +473,14 @@ void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr); void wpa_ft_deinit(struct wpa_authenticator *wpa_auth); void wpa_ft_sta_deinit(struct wpa_state_machine *sm); +int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth, + const u8 *spa, const u8 *pmk_r1_name, + u8 *pmk_r1, size_t *pmk_r1_len, int *pairwise, + struct vlan_description *vlan, + const u8 **identity, size_t *identity_len, + const u8 **radius_cui, size_t *radius_cui_len, + int *session_timeout); + #endif /* CONFIG_IEEE80211R_AP */ void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm); @@ -515,6 +537,8 @@ u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm, u8 * wpa_auth_write_assoc_resp_fils(struct wpa_state_machine *sm, u8 *pos, size_t max_len, const u8 *req_ies, size_t req_ies_len); +bool wpa_auth_write_fd_rsn_info(struct wpa_authenticator *wpa_auth, + u8 *fd_rsn_info); void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg); void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z); void wpa_auth_set_transition_disable(struct wpa_authenticator *wpa_auth, diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c index 5aa363e..32b7456 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -1473,13 +1473,13 @@ static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth, } -static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth, - const u8 *spa, const u8 *pmk_r1_name, - u8 *pmk_r1, size_t *pmk_r1_len, int *pairwise, - struct vlan_description *vlan, - const u8 **identity, size_t *identity_len, - const u8 **radius_cui, size_t *radius_cui_len, - int *session_timeout) +int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth, + const u8 *spa, const u8 *pmk_r1_name, + u8 *pmk_r1, size_t *pmk_r1_len, int *pairwise, + struct vlan_description *vlan, + const u8 **identity, size_t *identity_len, + const u8 **radius_cui, size_t *radius_cui_len, + int *session_timeout) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r1_sa *r1; @@ -2147,7 +2147,8 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk) return wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce, sm->addr, sm->wpa_auth->addr, sm->pmk_r1_name, - ptk, ptk_name, sm->wpa_key_mgmt, sm->pairwise); + ptk, ptk_name, sm->wpa_key_mgmt, sm->pairwise, + 0); } @@ -3065,7 +3066,7 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm, const u8 *identity, *radius_cui; size_t identity_len = 0, radius_cui_len = 0; int use_sha384; - size_t pmk_r1_len; + size_t pmk_r1_len, kdk_len; *resp_ies = NULL; *resp_ies_len = 0; @@ -3195,10 +3196,18 @@ pmk_r1_derived: wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce", sm->ANonce, WPA_NONCE_LEN); + if (sm->wpa_auth->conf.force_kdk_derivation || + (sm->wpa_auth->conf.secure_ltf && + sm->rsnxe && sm->rsnxe_len >= 4 && + sm->rsnxe[3] & BIT(WLAN_RSNX_CAPAB_SECURE_LTF - 8))) + kdk_len = WPA_KDK_MAX_LEN; + else + kdk_len = 0; + if (wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce, sm->addr, sm->wpa_auth->addr, pmk_r1_name, &sm->PTK, ptk_name, sm->wpa_key_mgmt, - pairwise) < 0) + pairwise, kdk_len) < 0) return WLAN_STATUS_UNSPECIFIED_FAILURE; sm->pairwise = pairwise; diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index c01654f..c3b2e81 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -14,6 +14,7 @@ #include "common/ieee802_11_defs.h" #include "common/sae.h" #include "common/wpa_ctrl.h" +#include "common/ptksa_cache.h" #include "crypto/sha1.h" #include "eapol_auth/eapol_auth_sm.h" #include "eapol_auth/eapol_auth_sm_i.h" @@ -208,6 +209,11 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, #ifdef CONFIG_DPP2 wconf->dpp_pfs = conf->dpp_pfs; #endif /* CONFIG_DPP2 */ +#ifdef CONFIG_PASN +#ifdef CONFIG_TESTING_OPTIONS + wconf->force_kdk_derivation = conf->force_kdk_derivation; +#endif /* CONFIG_TESTING_OPTIONS */ +#endif /* CONFIG_PASN */ } @@ -912,6 +918,27 @@ static int hostapd_channel_info(void *ctx, struct wpa_channel_info *ci) } +#ifdef CONFIG_PASN + +static void hostapd_store_ptksa(void *ctx, const u8 *addr,int cipher, + u32 life_time, const struct wpa_ptk *ptk) +{ + struct hostapd_data *hapd = ctx; + + ptksa_cache_add(hapd->ptksa, addr, cipher, life_time, ptk); +} + + +static void hostapd_clear_ptksa(void *ctx, const u8 *addr, int cipher) +{ + struct hostapd_data *hapd = ctx; + + ptksa_cache_flush(hapd->ptksa, addr, cipher); +} + +#endif /* CONFIG_PASN */ + + static int hostapd_wpa_auth_update_vlan(void *ctx, const u8 *addr, int vlan_id) { #ifndef CONFIG_NO_VLAN @@ -1437,6 +1464,11 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) .send_oui = hostapd_wpa_auth_send_oui, .channel_info = hostapd_channel_info, .update_vlan = hostapd_wpa_auth_update_vlan, +#ifdef CONFIG_PASN + .store_ptksa = hostapd_store_ptksa, + .clear_ptksa = hostapd_clear_ptksa, +#endif /* CONFIG_PASN */ + #ifdef CONFIG_OCV .get_sta_tx_params = hostapd_get_sta_tx_params, #endif /* CONFIG_OCV */ @@ -1480,6 +1512,22 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) else _conf.extended_key_id = 0; + if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_BEACON_PROTECTION)) + _conf.beacon_prot = 0; + +#ifdef CONFIG_OCV + if (!(hapd->iface->drv_flags2 & + (WPA_DRIVER_FLAGS2_AP_SME | WPA_DRIVER_FLAGS2_OCV))) + _conf.ocv = 0; +#endif /* CONFIG_OCV */ + + _conf.secure_ltf = + !!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF); + _conf.secure_rtt = + !!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT); + _conf.prot_range_neg = + !!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG); + hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd); if (hapd->wpa_auth == NULL) { wpa_printf(MSG_ERROR, "WPA initialization failed."); @@ -1505,6 +1553,12 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) return -1; } + hapd->ptksa = ptksa_cache_init(); + if (!hapd->ptksa) { + wpa_printf(MSG_ERROR, "Failed to allocate PTKSA cache"); + return -1; + } + #ifdef CONFIG_IEEE80211R_AP if (!hostapd_drv_none(hapd) && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) { @@ -1544,6 +1598,9 @@ void hostapd_reconfig_wpa(struct hostapd_data *hapd) void hostapd_deinit_wpa(struct hostapd_data *hapd) { ieee80211_tkip_countermeasures_deinit(hapd); + ptksa_cache_deinit(hapd->ptksa); + hapd->ptksa = NULL; + rsn_preauth_iface_deinit(hapd); if (hapd->wpa_auth) { wpa_deinit(hapd->wpa_auth); diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c index 3704fc0..d471031 100644 --- a/src/ap/wpa_auth_ie.c +++ b/src/ap/wpa_auth_ie.c @@ -88,13 +88,42 @@ static int wpa_write_wpa_ie(struct wpa_auth_config *conf, u8 *buf, size_t len) } +static u16 wpa_own_rsn_capab(struct wpa_auth_config *conf) +{ + u16 capab = 0; + + if (conf->rsn_preauth) + capab |= WPA_CAPABILITY_PREAUTH; + if (conf->wmm_enabled) { + /* 4 PTKSA replay counters when using WMM */ + capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2); + } + if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + capab |= WPA_CAPABILITY_MFPC; + if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) + capab |= WPA_CAPABILITY_MFPR; + } +#ifdef CONFIG_OCV + if (conf->ocv) + capab |= WPA_CAPABILITY_OCVC; +#endif /* CONFIG_OCV */ +#ifdef CONFIG_RSN_TESTING + if (rsn_testing) + capab |= BIT(8) | BIT(15); +#endif /* CONFIG_RSN_TESTING */ + if (conf->extended_key_id) + capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST; + + return capab; +} + + int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, const u8 *pmkid) { struct rsn_ie_hdr *hdr; int num_suites, res; u8 *pos, *count; - u16 capab; u32 suite; hdr = (struct rsn_ie_hdr *) buf; @@ -260,6 +289,13 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, num_suites++; } #endif /* CONFIG_HS20 */ +#ifdef CONFIG_PASN + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PASN) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PASN); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_PASN */ #ifdef CONFIG_RSN_TESTING if (rsn_testing) { @@ -277,29 +313,7 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, WPA_PUT_LE16(count, num_suites); /* RSN Capabilities */ - capab = 0; - if (conf->rsn_preauth) - capab |= WPA_CAPABILITY_PREAUTH; - if (conf->wmm_enabled) { - /* 4 PTKSA replay counters when using WMM */ - capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2); - } - if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { - capab |= WPA_CAPABILITY_MFPC; - if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) - capab |= WPA_CAPABILITY_MFPR; - } -#ifdef CONFIG_OCV - if (conf->ocv) - capab |= WPA_CAPABILITY_OCVC; -#endif /* CONFIG_OCV */ -#ifdef CONFIG_RSN_TESTING - if (rsn_testing) - capab |= BIT(8) | BIT(15); -#endif /* CONFIG_RSN_TESTING */ - if (conf->extended_key_id) - capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST; - WPA_PUT_LE16(pos, capab); + WPA_PUT_LE16(pos, wpa_own_rsn_capab(conf)); pos += 2; if (pmkid) { @@ -377,23 +391,38 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len) { u8 *pos = buf; + u16 capab = 0; + size_t flen; - if (conf->sae_pwe != 1 && conf->sae_pwe != 2 && !conf->sae_pk) - return 0; /* no supported extended RSN capabilities */ + if (wpa_key_mgmt_sae(conf->wpa_key_mgmt) && + (conf->sae_pwe == 1 || conf->sae_pwe == 2 || conf->sae_pk)) { + capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E); +#ifdef CONFIG_SAE_PK + if (conf->sae_pk) + capab |= BIT(WLAN_RSNX_CAPAB_SAE_PK); +#endif /* CONFIG_SAE_PK */ + } + + if (conf->secure_ltf) + capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF); + if (conf->secure_rtt) + capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT); + if (conf->prot_range_neg) + capab |= BIT(WLAN_RSNX_CAPAB_PROT_RANGE_NEG); - if (len < 3) + flen = (capab & 0xff00) ? 2 : 1; + if (!capab) + return 0; /* no supported extended RSN capabilities */ + if (len < 2 + flen) return -1; + capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */ *pos++ = WLAN_EID_RSNX; - *pos++ = 1; - /* bits 0-3 = 0 since only one octet of Extended RSN Capabilities is - * used for now */ - *pos = BIT(WLAN_RSNX_CAPAB_SAE_H2E); -#ifdef CONFIG_SAE_PK - if (conf->sae_pk) - *pos |= BIT(WLAN_RSNX_CAPAB_SAE_PK); -#endif /* CONFIG_SAE_PK */ - pos++; + *pos++ = flen; + *pos++ = capab & 0x00ff; + capab >>= 8; + if (capab) + *pos++ = capab; return pos - buf; } @@ -1096,6 +1125,7 @@ u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm, #ifdef CONFIG_FILS + u8 * wpa_auth_write_assoc_resp_fils(struct wpa_state_machine *sm, u8 *pos, size_t max_len, const u8 *req_ies, size_t req_ies_len) @@ -1112,4 +1142,91 @@ u8 * wpa_auth_write_assoc_resp_fils(struct wpa_state_machine *sm, return pos; return pos + res; } + + +bool wpa_auth_write_fd_rsn_info(struct wpa_authenticator *wpa_auth, + u8 *fd_rsn_info) +{ + struct wpa_auth_config *conf; + u32 selectors = 0; + u8 *pos = fd_rsn_info; + int i, res; + u32 cipher, suite, selector, mask; + u8 tmp[10 * RSN_SELECTOR_LEN]; + + if (!wpa_auth) + return false; + conf = &wpa_auth->conf; + + if (!(conf->wpa & WPA_PROTO_RSN)) + return false; + + /* RSN Capability (B0..B15) */ + WPA_PUT_LE16(pos, wpa_own_rsn_capab(conf)); + pos += 2; + + /* Group Data Cipher Suite Selector (B16..B21) */ + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, conf->wpa_group); + if (suite == RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED) + cipher = 63; /* No cipher suite selected */ + if ((suite >> 8) == 0x000fac && ((suite & 0xff) <= 13)) + cipher = suite & 0xff; + else + cipher = 62; /* vendor specific */ + selectors |= cipher; + + /* Group Management Cipher Suite Selector (B22..B27) */ + cipher = 63; /* Default to no cipher suite selected */ + if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + switch (conf->group_mgmt_cipher) { + case WPA_CIPHER_AES_128_CMAC: + cipher = RSN_CIPHER_SUITE_AES_128_CMAC & 0xff; + break; + case WPA_CIPHER_BIP_GMAC_128: + cipher = RSN_CIPHER_SUITE_BIP_GMAC_128 & 0xff; + break; + case WPA_CIPHER_BIP_GMAC_256: + cipher = RSN_CIPHER_SUITE_BIP_GMAC_256 & 0xff; + break; + case WPA_CIPHER_BIP_CMAC_256: + cipher = RSN_CIPHER_SUITE_BIP_CMAC_256 & 0xff; + break; + } + } + selectors |= cipher << 6; + + /* Pairwise Cipher Suite Selector (B28..B33) */ + cipher = 63; /* Default to no cipher suite selected */ + res = rsn_cipher_put_suites(tmp, conf->rsn_pairwise); + if (res == 1 && tmp[0] == 0x00 && tmp[1] == 0x0f && tmp[2] == 0xac && + tmp[3] <= 13) + cipher = tmp[3]; + selectors |= cipher << 12; + + /* AKM Suite Selector (B34..B39) */ + selector = 0; /* default to AKM from RSNE in Beacon/Probe Response */ + mask = WPA_KEY_MGMT_FILS_SHA256 | WPA_KEY_MGMT_FILS_SHA384 | + WPA_KEY_MGMT_FT_FILS_SHA384; + if ((conf->wpa_key_mgmt & mask) && (conf->wpa_key_mgmt & ~mask) == 0) { + suite = conf->wpa_key_mgmt & mask; + if (suite == WPA_KEY_MGMT_FILS_SHA256) + selector = 1; /* 00-0f-ac:14 */ + else if (suite == WPA_KEY_MGMT_FILS_SHA384) + selector = 2; /* 00-0f-ac:15 */ + else if (suite == (WPA_KEY_MGMT_FILS_SHA256 | + WPA_KEY_MGMT_FILS_SHA384)) + selector = 3; /* 00-0f-ac:14 or 00-0f-ac:15 */ + else if (suite == WPA_KEY_MGMT_FT_FILS_SHA384) + selector = 4; /* 00-0f-ac:17 */ + } + selectors |= selector << 18; + + for (i = 0; i < 3; i++) { + *pos++ = selectors & 0xff; + selectors >>= 8; + } + + return true; +} + #endif /* CONFIG_FILS */ diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c index dc8aa8f..e97dbf9 100644 --- a/src/ap/wps_hostapd.c +++ b/src/ap/wps_hostapd.c @@ -1376,6 +1376,48 @@ static void hostapd_wps_nfc_clear(struct wps_context *wps) } +static int hostapd_wps_update_multi_ap(struct hostapd_data *hapd, + struct wps_registrar *reg) +{ + struct hostapd_bss_config *conf = hapd->conf; + u8 *multi_ap_backhaul_network_key = NULL; + size_t multi_ap_backhaul_network_key_len = 0; + int ret; + + if (!(conf->multi_ap & FRONTHAUL_BSS) || + !conf->multi_ap_backhaul_ssid.ssid_len) + return 0; + + if (conf->multi_ap_backhaul_ssid.wpa_passphrase) { + multi_ap_backhaul_network_key = + (u8 *) os_strdup( + conf->multi_ap_backhaul_ssid.wpa_passphrase); + if (!multi_ap_backhaul_network_key) + return -1; + multi_ap_backhaul_network_key_len = + os_strlen(conf->multi_ap_backhaul_ssid.wpa_passphrase); + } else if (conf->multi_ap_backhaul_ssid.wpa_psk) { + multi_ap_backhaul_network_key = os_malloc(2 * PMK_LEN + 1); + if (!multi_ap_backhaul_network_key) + return -1; + wpa_snprintf_hex((char *) multi_ap_backhaul_network_key, + 2 * PMK_LEN + 1, + conf->multi_ap_backhaul_ssid.wpa_psk->psk, + PMK_LEN); + multi_ap_backhaul_network_key_len = 2 * PMK_LEN; + } + + ret = wps_registrar_update_multi_ap( + reg, conf->multi_ap_backhaul_ssid.ssid, + conf->multi_ap_backhaul_ssid.ssid_len, + multi_ap_backhaul_network_key, + multi_ap_backhaul_network_key_len); + os_free(multi_ap_backhaul_network_key); + + return ret; +} + + void hostapd_deinit_wps(struct hostapd_data *hapd) { eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL); @@ -1398,22 +1440,65 @@ void hostapd_deinit_wps(struct hostapd_data *hapd) void hostapd_update_wps(struct hostapd_data *hapd) { - if (hapd->wps == NULL) + struct wps_context *wps = hapd->wps; + struct hostapd_bss_config *conf = hapd->conf; + + if (!wps) return; #ifdef CONFIG_WPS_UPNP - hapd->wps->friendly_name = hapd->conf->friendly_name; - hapd->wps->manufacturer_url = hapd->conf->manufacturer_url; - hapd->wps->model_description = hapd->conf->model_description; - hapd->wps->model_url = hapd->conf->model_url; - hapd->wps->upc = hapd->conf->upc; + wps->friendly_name = conf->friendly_name; + wps->manufacturer_url = conf->manufacturer_url; + wps->model_description = conf->model_description; + wps->model_url = conf->model_url; + wps->upc = conf->upc; #endif /* CONFIG_WPS_UPNP */ - hostapd_wps_set_vendor_ext(hapd, hapd->wps); - hostapd_wps_set_application_ext(hapd, hapd->wps); + os_memcpy(wps->ssid, conf->ssid.ssid, conf->ssid.ssid_len); + wps->ssid_len = conf->ssid.ssid_len; - if (hapd->conf->wps_state) - wps_registrar_update_ie(hapd->wps->registrar); + /* Clear WPS settings, then fill them again */ + os_free(wps->network_key); + wps->network_key = NULL; + wps->network_key_len = 0; + wps->psk_set = 0; + if (conf->ssid.wpa_psk_file) { + /* Use per-device PSKs */ + } else if (conf->ssid.wpa_passphrase) { + wps->network_key = (u8 *) os_strdup(conf->ssid.wpa_passphrase); + if (!wps->network_key) + return; + wps->network_key_len = os_strlen(conf->ssid.wpa_passphrase); + } else if (conf->ssid.wpa_psk) { + wps->network_key = os_malloc(2 * PMK_LEN + 1); + if (!wps->network_key) + return; + wpa_snprintf_hex((char *) wps->network_key, 2 * PMK_LEN + 1, + conf->ssid.wpa_psk->psk, PMK_LEN); + wps->network_key_len = 2 * PMK_LEN; +#ifdef CONFIG_WEP + } else if (conf->ssid.wep.keys_set && conf->ssid.wep.key[0]) { + wps->network_key = os_malloc(conf->ssid.wep.len[0]); + if (!wps->network_key) + return; + os_memcpy(wps->network_key, conf->ssid.wep.key[0], + conf->ssid.wep.len[0]); + wps->network_key_len = conf->ssid.wep.len[0]; +#endif /* CONFIG_WEP */ + } + + if (conf->ssid.wpa_psk) { + os_memcpy(wps->psk, conf->ssid.wpa_psk->psk, PMK_LEN); + wps->psk_set = 1; + } + + hostapd_wps_update_multi_ap(hapd, wps->registrar); + + hostapd_wps_set_vendor_ext(hapd, wps); + hostapd_wps_set_application_ext(hapd, wps); + + if (conf->wps_state) + wps_registrar_update_ie(wps->registrar); else hostapd_deinit_wps(hapd); } diff --git a/src/common/Makefile b/src/common/Makefile index 59ba6c5..e2c5f03 100644 --- a/src/common/Makefile +++ b/src/common/Makefile @@ -3,12 +3,14 @@ CFLAGS += -DCONFIG_HS20 CFLAGS += -DCONFIG_SAE CFLAGS += -DCONFIG_SUITE CFLAGS += -DCONFIG_SUITEB +CFLAGS += -DCONFIG_PTKSA_CACHE LIB_OBJS= \ gas.o \ hw_features_common.o \ ieee802_11_common.o \ sae.o \ + ptksa_cache.o \ wpa_common.o include ../lib.rules diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c index 00308d4..50ce192 100644 --- a/src/common/common_module_tests.c +++ b/src/common/common_module_tests.c @@ -637,6 +637,178 @@ static int sae_pk_tests(void) } +#ifdef CONFIG_PASN + +static int pasn_test_pasn_auth(void) +{ + /* Test vector taken from IEEE P802.11az/D2.6, J.12 */ + const u8 pmk[] = { + 0xde, 0xf4, 0x3e, 0x55, 0x67, 0xe0, 0x1c, 0xa6, + 0x64, 0x92, 0x65, 0xf1, 0x9a, 0x29, 0x0e, 0xef, + 0xf8, 0xbd, 0x88, 0x8f, 0x6c, 0x1d, 0x9c, 0xc9, + 0xd1, 0x0f, 0x04, 0xbd, 0x37, 0x8f, 0x3c, 0xad + }; + + const u8 spa_addr[] = { + 0x00, 0x90, 0x4c, 0x01, 0xc1, 0x07 + }; + const u8 bssid[] = { + 0xc0, 0xff, 0xd4, 0xa8, 0xdb, 0xc1 + }; + const u8 dhss[] = { + 0xf8, 0x7b, 0x20, 0x8e, 0x7e, 0xd2, 0xb7, 0x37, + 0xaf, 0xdb, 0xc2, 0xe1, 0x3e, 0xae, 0x78, 0xda, + 0x30, 0x01, 0x23, 0xd4, 0xd8, 0x4b, 0xa8, 0xb0, + 0xea, 0xfe, 0x90, 0xc4, 0x8c, 0xdf, 0x1f, 0x93 + }; + const u8 kck[] = { + 0x7b, 0xb8, 0x21, 0xac, 0x0a, 0xa5, 0x90, 0x9d, + 0xd6, 0x54, 0xa5, 0x60, 0x65, 0xad, 0x7c, 0x77, + 0xeb, 0x88, 0x9c, 0xbe, 0x29, 0x05, 0xbb, 0xf0, + 0x5a, 0xbb, 0x1e, 0xea, 0xc8, 0x8b, 0xa3, 0x06 + }; + const u8 tk[] = { + 0x67, 0x3e, 0xab, 0x46, 0xb8, 0x32, 0xd5, 0xa8, + 0x0c, 0xbc, 0x02, 0x43, 0x01, 0x6e, 0x20, 0x7e + }; + const u8 kdk[] = { + 0x2d, 0x0f, 0x0e, 0x82, 0xc7, 0x0d, 0xd2, 0x6b, + 0x79, 0x06, 0x1a, 0x46, 0x81, 0xe8, 0xdb, 0xb2, + 0xea, 0x83, 0xbe, 0xa3, 0x99, 0x84, 0x4b, 0xd5, + 0x89, 0x4e, 0xb3, 0x20, 0xf6, 0x9d, 0x7d, 0xd6 + }; + struct wpa_ptk ptk; + int ret; + + ret = pasn_pmk_to_ptk(pmk, sizeof(pmk), + spa_addr, bssid, + dhss, sizeof(dhss), + &ptk, WPA_KEY_MGMT_PASN, WPA_CIPHER_CCMP, + WPA_KDK_MAX_LEN); + + if (ret) + return ret; + + if (ptk.kck_len != sizeof(kck) || + os_memcmp(kck, ptk.kck, sizeof(kck)) != 0) { + wpa_printf(MSG_ERROR, "PASN: Mismatched KCK"); + return -1; + } + + if (ptk.tk_len != sizeof(tk) || + os_memcmp(tk, ptk.tk, sizeof(tk)) != 0) { + wpa_printf(MSG_ERROR, "PASN: Mismatched TK"); + return -1; + } + + if (ptk.kdk_len != sizeof(kdk) || + os_memcmp(kdk, ptk.kdk, sizeof(kdk)) != 0) { + wpa_printf(MSG_ERROR, "PASN: Mismatched KDK"); + return -1; + } + + return 0; +} + + +static int pasn_test_no_pasn_auth(void) +{ + /* Test vector taken from IEEE P802.11az/D2.6, J.13 */ + const u8 pmk[] = { + 0xde, 0xf4, 0x3e, 0x55, 0x67, 0xe0, 0x1c, 0xa6, + 0x64, 0x92, 0x65, 0xf1, 0x9a, 0x29, 0x0e, 0xef, + 0xf8, 0xbd, 0x88, 0x8f, 0x6c, 0x1d, 0x9c, 0xc9, + 0xd1, 0x0f, 0x04, 0xbd, 0x37, 0x8f, 0x3c, 0xad + }; + const u8 aa[] = { + 0xc0, 0xff, 0xd4, 0xa8, 0xdb, 0xc1 + }; + const u8 spa[] = { + 0x00, 0x90, 0x4c, 0x01, 0xc1, 0x07 + }; + const u8 anonce[] = { + 0xbe, 0x7a, 0x1c, 0xa2, 0x84, 0x34, 0x7b, 0x5b, + 0xd6, 0x7d, 0xbd, 0x2d, 0xfd, 0xb4, 0xd9, 0x9f, + 0x1a, 0xfa, 0xe0, 0xb8, 0x8b, 0xa1, 0x8e, 0x00, + 0x87, 0x18, 0x41, 0x7e, 0x4b, 0x27, 0xef, 0x5f + }; + const u8 snonce[] = { + 0x40, 0x4b, 0x01, 0x2f, 0xfb, 0x43, 0xed, 0x0f, + 0xb4, 0x3e, 0xa1, 0xf2, 0x87, 0xc9, 0x1f, 0x25, + 0x06, 0xd2, 0x1b, 0x4a, 0x92, 0xd7, 0x4b, 0x5e, + 0xa5, 0x0c, 0x94, 0x33, 0x50, 0xce, 0x86, 0x71 + }; + const u8 kck[] = { + 0xcd, 0x7b, 0x9e, 0x75, 0x55, 0x36, 0x2d, 0xf0, + 0xb6, 0x35, 0x68, 0x48, 0x4a, 0x81, 0x12, 0xf5 + }; + const u8 kek[] = { + 0x99, 0xca, 0xd3, 0x58, 0x8d, 0xa0, 0xf1, 0xe6, + 0x3f, 0xd1, 0x90, 0x19, 0x10, 0x39, 0xbb, 0x4b + }; + const u8 tk[] = { + 0x9e, 0x2e, 0x93, 0x77, 0xe7, 0x53, 0x2e, 0x73, + 0x7a, 0x1b, 0xc2, 0x50, 0xfe, 0x19, 0x4a, 0x03 + }; + const u8 kdk[] = { + 0x6c, 0x7f, 0xb9, 0x7c, 0xeb, 0x55, 0xb0, 0x1a, + 0xcf, 0xf0, 0x0f, 0x07, 0x09, 0x42, 0xbd, 0xf5, + 0x29, 0x1f, 0xeb, 0x4b, 0xee, 0x38, 0xe0, 0x36, + 0x5b, 0x25, 0xa2, 0x50, 0xbb, 0x2a, 0xc9, 0xff + }; + struct wpa_ptk ptk; + int ret; + + ret = wpa_pmk_to_ptk(pmk, sizeof(pmk), + "Pairwise key expansion", + spa, aa, snonce, anonce, + &ptk, WPA_KEY_MGMT_SAE, WPA_CIPHER_CCMP, + NULL, 0, WPA_KDK_MAX_LEN); + + if (ret) + return ret; + + if (ptk.kck_len != sizeof(kck) || + os_memcmp(kck, ptk.kck, sizeof(kck)) != 0) { + wpa_printf(MSG_ERROR, "KDK no PASN auth: Mismatched KCK"); + return -1; + } + + if (ptk.kek_len != sizeof(kek) || + os_memcmp(kek, ptk.kek, sizeof(kek)) != 0) { + wpa_printf(MSG_ERROR, "KDK no PASN auth: Mismatched KEK"); + return -1; + } + + if (ptk.tk_len != sizeof(tk) || + os_memcmp(tk, ptk.tk, sizeof(tk)) != 0) { + wpa_printf(MSG_ERROR, "KDK no PASN auth: Mismatched TK"); + return -1; + } + + if (ptk.kdk_len != sizeof(kdk) || + os_memcmp(kdk, ptk.kdk, sizeof(kdk)) != 0) { + wpa_printf(MSG_ERROR, "KDK no PASN auth: Mismatched KDK"); + return -1; + } + + return 0; +} + +#endif /* CONFIG_PASN */ + + +static int pasn_tests(void) +{ +#ifdef CONFIG_PASN + if (pasn_test_pasn_auth() || + pasn_test_no_pasn_auth()) + return -1; +#endif /* CONFIG_PASN */ + return 0; +} + + int common_module_tests(void) { int ret = 0; @@ -647,6 +819,7 @@ int common_module_tests(void) gas_tests() < 0 || sae_tests() < 0 || sae_pk_tests() < 0 || + pasn_tests() < 0 || rsn_ie_parse_tests() < 0) ret = -1; diff --git a/src/common/defs.h b/src/common/defs.h index bbe3120..f43bdb5 100644 --- a/src/common/defs.h +++ b/src/common/defs.h @@ -49,6 +49,8 @@ #define WPA_KEY_MGMT_OWE BIT(22) #define WPA_KEY_MGMT_DPP BIT(23) #define WPA_KEY_MGMT_FT_IEEE8021X_SHA384 BIT(24) +#define WPA_KEY_MGMT_PASN BIT(25) + #define WPA_KEY_MGMT_FT (WPA_KEY_MGMT_FT_PSK | \ WPA_KEY_MGMT_FT_IEEE8021X | \ @@ -388,9 +390,10 @@ enum mesh_plink_state { }; enum set_band { - WPA_SETBAND_AUTO, - WPA_SETBAND_5G, - WPA_SETBAND_2G + WPA_SETBAND_AUTO = 0, + WPA_SETBAND_5G = BIT(0), + WPA_SETBAND_2G = BIT(1), + WPA_SETBAND_6G = BIT(2), }; enum wpa_radio_work_band { @@ -402,7 +405,8 @@ enum wpa_radio_work_band { enum beacon_rate_type { BEACON_RATE_LEGACY, BEACON_RATE_HT, - BEACON_RATE_VHT + BEACON_RATE_VHT, + BEACON_RATE_HE }; enum eap_proxy_sim_state { diff --git a/src/common/dpp.h b/src/common/dpp.h index 2fd331b..65ee905 100644 --- a/src/common/dpp.h +++ b/src/common/dpp.h @@ -348,6 +348,7 @@ struct dpp_authentication { struct wpabuf *cacert; struct wpabuf *certbag; void *cert_resp_ctx; + void *gas_server_ctx; #ifdef CONFIG_TESTING_OPTIONS char *config_obj_override; char *discovery_override; diff --git a/src/common/dpp_auth.c b/src/common/dpp_auth.c index f79cfef..0cabd64 100644 --- a/src/common/dpp_auth.c +++ b/src/common/dpp_auth.c @@ -251,6 +251,7 @@ static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth, u8 *attr_start, *attr_end, *pos; auth->waiting_auth_conf = 1; + auth->auth_resp_status = status; auth->auth_resp_tries = 0; /* Build DPP Authentication Response frame attributes */ diff --git a/src/common/dpp_tcp.c b/src/common/dpp_tcp.c index 7e330d6..609c243 100644 --- a/src/common/dpp_tcp.c +++ b/src/common/dpp_tcp.c @@ -1279,7 +1279,7 @@ static int dpp_rx_gas_resp(struct dpp_connection *conn, const u8 *msg, const u8 *pos, *end, *next, *adv_proto; u16 status, slen, comeback_delay; - if (len < 5 + 2 + (comeback ? 1 : 0)) + if (len < (size_t) (5 + 2 + (comeback ? 1 : 0))) return -1; wpa_printf(MSG_DEBUG, diff --git a/src/common/gas_server.c b/src/common/gas_server.c index c000aeb..5f44ffe 100644 --- a/src/common/gas_server.c +++ b/src/common/gas_server.c @@ -489,6 +489,21 @@ int gas_server_set_resp(struct gas_server *gas, void *resp_ctx, } +bool gas_server_response_sent(struct gas_server *gas, void *resp_ctx) +{ + struct gas_server_response *tmp; + + dl_list_for_each(tmp, &gas->responses, struct gas_server_response, + list) { + if (tmp == resp_ctx) + return tmp->resp && + tmp->offset == wpabuf_len(tmp->resp); + } + + return false; +} + + struct gas_server * gas_server_init(void *ctx, void (*tx)(void *ctx, int freq, const u8 *da, diff --git a/src/common/gas_server.h b/src/common/gas_server.h index 2611dde..db00f87 100644 --- a/src/common/gas_server.h +++ b/src/common/gas_server.h @@ -36,6 +36,7 @@ void gas_server_tx_status(struct gas_server *gas, const u8 *dst, const u8 *data, size_t data_len, int ack); int gas_server_set_resp(struct gas_server *gas, void *resp_ctx, struct wpabuf *resp); +bool gas_server_response_sent(struct gas_server *gas, void *resp_ctx); #else /* CONFIG_GAS_SERVER */ diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c index 511e68f..b8b886f 100644 --- a/src/common/hw_features_common.c +++ b/src/common/hw_features_common.c @@ -41,10 +41,30 @@ struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode, struct hostapd_channel_data * +hw_mode_get_channel(struct hostapd_hw_modes *mode, int freq, int *chan) +{ + int i; + + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *ch = &mode->channels[i]; + + if (ch->freq == freq) { + if (chan) + *chan = ch->chan; + return ch; + } + } + + return NULL; +} + + +struct hostapd_channel_data * hw_get_channel_freq(enum hostapd_hw_mode mode, int freq, int *chan, struct hostapd_hw_modes *hw_features, int num_hw_features) { - int i, j; + struct hostapd_channel_data *chan_data; + int i; if (chan) *chan = 0; @@ -52,21 +72,15 @@ hw_get_channel_freq(enum hostapd_hw_mode mode, int freq, int *chan, if (!hw_features) return NULL; - for (j = 0; j < num_hw_features; j++) { - struct hostapd_hw_modes *curr_mode = &hw_features[j]; + for (i = 0; i < num_hw_features; i++) { + struct hostapd_hw_modes *curr_mode = &hw_features[i]; if (curr_mode->mode != mode) continue; - for (i = 0; i < curr_mode->num_channels; i++) { - struct hostapd_channel_data *ch = - &curr_mode->channels[i]; - - if (ch->freq == freq) { - if (chan) - *chan = ch->chan; - return ch; - } - } + + chan_data = hw_mode_get_channel(curr_mode, freq, chan); + if (chan_data) + return chan_data; } return NULL; @@ -580,6 +594,8 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data, center_segment0 = 138; else if (channel <= 161) center_segment0 = 155; + else if (channel <= 177) + center_segment0 = 171; data->center_freq1 = 5000 + center_segment0 * 5; } else { /* diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h index e57a8d6..ddde36b 100644 --- a/src/common/hw_features_common.h +++ b/src/common/hw_features_common.h @@ -15,6 +15,9 @@ struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode, int chan, int *freq); struct hostapd_channel_data * +hw_mode_get_channel(struct hostapd_hw_modes *mode, int freq, int *chan); + +struct hostapd_channel_data * hw_get_channel_freq(enum hostapd_hw_mode mode, int freq, int *chan, struct hostapd_hw_modes *hw_features, int num_hw_features); diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index 8bdeeb5..ec17456 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -303,6 +303,10 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen, break; elems->he_6ghz_band_cap = pos; break; + case WLAN_EID_EXT_PASN_PARAMS: + elems->pasn_params = pos; + elems->pasn_params_len = elen; + break; default: if (show_errors) { wpa_printf(MSG_MSGDUMP, @@ -566,6 +570,11 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->dils = pos; elems->dils_len = elen; break; + case WLAN_EID_S1G_CAPABILITIES: + if (elen < 15) + break; + elems->s1g_capab = pos; + break; case WLAN_EID_FRAGMENT: ieee802_11_parse_fragment(&elems->frag_ies, pos, elen); break; @@ -870,7 +879,7 @@ enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel) /** * ieee80211_freq_to_channel_ext - Convert frequency into channel info - * for HT40 and VHT. DFS channels are not covered. + * for HT40, VHT, and HE. DFS channels are not covered. * @freq: Frequency (MHz) to convert * @sec_channel: 0 = non-HT40, 1 = sec. channel above, -1 = sec. channel below * @chanwidth: VHT/EDMG channel width (CHANWIDTH_*) @@ -981,8 +990,8 @@ enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, return HOSTAPD_MODE_IEEE80211A; } - /* 5 GHz, channels 149..169 */ - if (freq >= 5745 && freq <= 5845) { + /* 5 GHz, channels 149..177 */ + if (freq >= 5745 && freq <= 5885) { if ((freq - 5000) % 5) return NUM_HOSTAPD_MODES; @@ -1412,22 +1421,22 @@ static int ieee80211_chan_to_freq_global(u8 op_class, u8 chan) return -1; return 5000 + 5 * chan; case 124: /* channels 149,153,157,161 */ - case 126: /* channels 149,157; 40 MHz */ - case 127: /* channels 153,161; 40 MHz */ if (chan < 149 || chan > 161) return -1; return 5000 + 5 * chan; - case 125: /* channels 149,153,157,161,165,169 */ - if (chan < 149 || chan > 169) + case 125: /* channels 149,153,157,161,165,169,173,177 */ + case 126: /* channels 149,157,165,173; 40 MHz */ + case 127: /* channels 153,161,169,177; 40 MHz */ + if (chan < 149 || chan > 177) return -1; return 5000 + 5 * chan; - case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */ - case 130: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */ - if (chan < 36 || chan > 161) + case 128: /* center freqs 42, 58, 106, 122, 138, 155, 171; 80 MHz */ + case 130: /* center freqs 42, 58, 106, 122, 138, 155, 171; 80 MHz */ + if (chan < 36 || chan > 177) return -1; return 5000 + 5 * chan; - case 129: /* center freqs 50, 114; 160 MHz */ - if (chan < 36 || chan > 128) + case 129: /* center freqs 50, 114, 163; 160 MHz */ + if (chan < 36 || chan > 177) return -1; return 5000 + 5 * chan; case 131: /* UHB channels, 20 MHz: 1, 5, 9.. */ @@ -1871,20 +1880,20 @@ const struct oper_class_map global_op_class[] = { { HOSTAPD_MODE_IEEE80211A, 122, 100, 132, 8, BW40PLUS, NO_P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 123, 104, 136, 8, BW40MINUS, NO_P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20, P2P_SUPP }, - { HOSTAPD_MODE_IEEE80211A, 125, 149, 169, 4, BW20, P2P_SUPP }, - { HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS, P2P_SUPP }, - { HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 125, 149, 177, 4, BW20, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 126, 149, 173, 8, BW40PLUS, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 127, 153, 177, 8, BW40MINUS, P2P_SUPP }, /* - * IEEE P802.11ac/D7.0 Table E-4 actually talks about channel center - * frequency index 42, 58, 106, 122, 138, 155 with channel spacing of - * 80 MHz, but currently use the following definition for simplicity + * IEEE P802.11ax/D8.0 Table E-4 actually talks about channel center + * frequency index 42, 58, 106, 122, 138, 155, 171 with channel spacing + * of 80 MHz, but currently use the following definition for simplicity * (these center frequencies are not actual channels, which makes - * wpas_p2p_allow_channel() fail). wpas_p2p_verify_80mhz() should take + * wpas_p2p_verify_channel() fail). wpas_p2p_verify_80mhz() should take * care of removing invalid channels. */ - { HOSTAPD_MODE_IEEE80211A, 128, 36, 161, 4, BW80, P2P_SUPP }, - { HOSTAPD_MODE_IEEE80211A, 129, 50, 114, 16, BW160, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 128, 36, 177, 4, BW80, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 129, 36, 177, 4, BW160, P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 131, 1, 233, 4, BW20, NO_P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 132, 1, 233, 8, BW40, NO_P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 133, 1, 233, 16, BW80, NO_P2P_SUPP }, @@ -1906,7 +1915,7 @@ const struct oper_class_map global_op_class[] = { * the OneHundredAndThirty Delimiter value used in the Supported * Operating Classes element to indicate the end of the Operating * Classes field. */ - { HOSTAPD_MODE_IEEE80211A, 130, 36, 161, 4, BW80P80, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 130, 36, 177, 4, BW80P80, P2P_SUPP }, { -1, 0, 0, 0, 0, BW20, NO_P2P_SUPP } }; @@ -2240,44 +2249,44 @@ int center_idx_to_bw_6ghz(u8 idx) } -int is_6ghz_freq(int freq) +bool is_6ghz_freq(int freq) { if (freq < 5935 || freq > 7115) - return 0; + return false; if (freq == 5935) - return 1; + return true; if (center_idx_to_bw_6ghz((freq - 5950) / 5) < 0) - return 0; + return false; - return 1; + return true; } -int is_6ghz_op_class(u8 op_class) +bool is_6ghz_op_class(u8 op_class) { return op_class >= 131 && op_class <= 136; } -int is_6ghz_psc_frequency(int freq) +bool is_6ghz_psc_frequency(int freq) { int i; if (!is_6ghz_freq(freq) || freq == 5935) - return 0; + return false; if ((((freq - 5950) / 5) & 0x3) != 0x1) - return 0; + return false; i = (freq - 5950 + 55) % 80; if (i == 0) i = (freq - 5950 + 55) / 80; if (i >= 1 && i <= 15) - return 1; + return true; - return 0; + return false; } @@ -2488,16 +2497,16 @@ int op_class_to_bandwidth(u8 op_class) case 123: /* channels 104-136; 40 MHz */ return 40; case 124: /* channels 149,153,157,161 */ - case 125: /* channels 149,153,157,161,165,169 */ + case 125: /* channels 149,153,157,161,165,169,173,177 */ return 20; - case 126: /* channels 149,157; 40 MHz */ - case 127: /* channels 153,161; 40 MHz */ + case 126: /* channels 149,157,161,165,169,173; 40 MHz */ + case 127: /* channels 153..177; 40 MHz */ return 40; - case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */ + case 128: /* center freqs 42, 58, 106, 122, 138, 155, 171; 80 MHz */ return 80; - case 129: /* center freqs 50, 114; 160 MHz */ + case 129: /* center freqs 50, 114, 163; 160 MHz */ return 160; - case 130: /* center freqs 42, 58, 106, 122, 138, 155; 80+80 MHz */ + case 130: /* center freqs 42, 58, 106, 122, 138, 155, 171; 80+80 MHz */ return 80; case 131: /* UHB channels, 20 MHz: 1, 5, 9.. */ return 20; @@ -2549,16 +2558,16 @@ int op_class_to_ch_width(u8 op_class) case 123: /* channels 104-136; 40 MHz */ return CHANWIDTH_USE_HT; case 124: /* channels 149,153,157,161 */ - case 125: /* channels 149,153,157,161,165,169 */ + case 125: /* channels 149,153,157,161,165,169,171 */ return CHANWIDTH_USE_HT; - case 126: /* channels 149,157; 40 MHz */ - case 127: /* channels 153,161; 40 MHz */ + case 126: /* channels 149,157,165, 173; 40 MHz */ + case 127: /* channels 153,161,169,177; 40 MHz */ return CHANWIDTH_USE_HT; - case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */ + case 128: /* center freqs 42, 58, 106, 122, 138, 155, 171; 80 MHz */ return CHANWIDTH_80MHZ; - case 129: /* center freqs 50, 114; 160 MHz */ + case 129: /* center freqs 50, 114, 163; 160 MHz */ return CHANWIDTH_160MHZ; - case 130: /* center freqs 42, 58, 106, 122, 138, 155; 80+80 MHz */ + case 130: /* center freqs 42, 58, 106, 122, 138, 155, 171; 80+80 MHz */ return CHANWIDTH_80P80MHZ; case 131: /* UHB channels, 20 MHz: 1, 5, 9.. */ return CHANWIDTH_USE_HT; diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h index 0ae0fa4..8a16f16 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -115,6 +115,8 @@ struct ieee802_11_elems { const u8 *short_ssid_list; const u8 *he_6ghz_band_cap; const u8 *sae_pk; + const u8 *s1g_capab; + const u8 *pasn_params; u8 ssid_len; u8 supp_rates_len; @@ -168,6 +170,7 @@ struct ieee802_11_elems { u8 he_operation_len; u8 short_ssid_list_len; u8 sae_pk_len; + u8 pasn_params_len; struct mb_ies_info mb_ies; struct frag_ies_info frag_ies; @@ -258,9 +261,9 @@ u8 country_to_global_op_class(const char *country, u8 op_class); const struct oper_class_map * get_oper_class(const char *country, u8 op_class); int oper_class_bw_to_int(const struct oper_class_map *map); int center_idx_to_bw_6ghz(u8 idx); -int is_6ghz_freq(int freq); -int is_6ghz_op_class(u8 op_class); -int is_6ghz_psc_frequency(int freq); +bool is_6ghz_freq(int freq); +bool is_6ghz_op_class(u8 op_class); +bool is_6ghz_psc_frequency(int freq); int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep, size_t nei_rep_len); diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 86d71c1..038c4ec 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -50,6 +50,7 @@ #define WLAN_FC_STYPE_AUTH 11 #define WLAN_FC_STYPE_DEAUTH 12 #define WLAN_FC_STYPE_ACTION 13 +#define WLAN_FC_STYPE_ACTION_NO_ACK 14 /* control */ #define WLAN_FC_STYPE_PSPOLL 10 @@ -84,6 +85,7 @@ #define WLAN_AUTH_FILS_SK 4 #define WLAN_AUTH_FILS_SK_PFS 5 #define WLAN_AUTH_FILS_PK 6 +#define WLAN_AUTH_PASN 7 #define WLAN_AUTH_LEAP 128 #define WLAN_AUTH_CHALLENGE_LEN 128 @@ -432,7 +434,7 @@ #define WLAN_EID_VHT_OPERATION 192 #define WLAN_EID_VHT_EXTENDED_BSS_LOAD 193 #define WLAN_EID_VHT_WIDE_BW_CHSWITCH 194 -#define WLAN_EID_VHT_TRANSMIT_POWER_ENVELOPE 195 +#define WLAN_EID_TRANSMIT_POWER_ENVELOPE 195 #define WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER 196 #define WLAN_EID_VHT_AID 197 #define WLAN_EID_VHT_QUIET_CHANNEL 198 @@ -443,7 +445,10 @@ #define WLAN_EID_DEVICE_LOCATION 204 #define WLAN_EID_WHITE_SPACE_MAP 205 #define WLAN_EID_FTM_PARAMETERS 206 +#define WLAN_EID_S1G_BCN_COMPAT 213 +#define WLAN_EID_S1G_CAPABILITIES 217 #define WLAN_EID_VENDOR_SPECIFIC 221 +#define WLAN_EID_S1G_OPERATION 232 #define WLAN_EID_CAG_NUMBER 237 #define WLAN_EID_AP_CSN 239 #define WLAN_EID_FILS_INDICATION 240 @@ -482,6 +487,7 @@ #define WLAN_EID_EXT_TCLAS_MASK 89 #define WLAN_EID_EXT_REJECTED_GROUPS 92 #define WLAN_EID_EXT_ANTI_CLOGGING_TOKEN 93 +#define WLAN_EID_EXT_PASN_PARAMS 100 /* Extended Capabilities field */ #define WLAN_EXT_CAPAB_20_40_COEX 0 @@ -525,7 +531,6 @@ #define WLAN_EXT_CAPAB_TDLS_PROHIBITED 38 #define WLAN_EXT_CAPAB_TDLS_CHANNEL_SWITCH_PROHIBITED 39 #define WLAN_EXT_CAPAB_REJECT_UNADMITTED_FRAME 40 -#define WLAN_EXT_CAPAB_ /* 41-43 - Service Interval Granularity */ #define WLAN_EXT_CAPAB_IDENTIFIER_LOCATION 44 #define WLAN_EXT_CAPAB_U_APSD_COEX 45 @@ -546,7 +551,6 @@ #define WLAN_EXT_CAPAB_PROT_QLOAD_REPORT 60 #define WLAN_EXT_CAPAB_TDLS_WIDER_BW 61 #define WLAN_EXT_CAPAB_OPMODE_NOTIF 62 -#define WLAN_EXT_CAPAB_ /* 63-64 - Max Number of MSDUs In A-MSDU */ #define WLAN_EXT_CAPAB_CHANNEL_SCHEDULE_MGMT 65 #define WLAN_EXT_CAPAB_GEODB_INBAND_ENABLING_SIGNAL 66 @@ -572,6 +576,9 @@ #define WLAN_RSNX_CAPAB_PROTECTED_TWT 4 #define WLAN_RSNX_CAPAB_SAE_H2E 5 #define WLAN_RSNX_CAPAB_SAE_PK 6 +#define WLAN_RSNX_CAPAB_SECURE_LTF 8 +#define WLAN_RSNX_CAPAB_SECURE_RTT 9 +#define WLAN_RSNX_CAPAB_PROT_RANGE_NEG 10 /* Action frame categories (IEEE Std 802.11-2016, 9.4.1.11, Table 9-76) */ #define WLAN_ACTION_SPECTRUM_MGMT 0 @@ -597,6 +604,7 @@ #define WLAN_ACTION_UNPROTECTED_DMG 20 #define WLAN_ACTION_VHT 21 #define WLAN_ACTION_FILS 26 +#define WLAN_ACTION_PROTECTED_FTM 34 #define WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED 126 #define WLAN_ACTION_VENDOR_SPECIFIC 127 /* Note: 128-255 used to report errors by setting category | 0x80 */ @@ -637,6 +645,7 @@ #define WLAN_PA_FTM_REQUEST 32 #define WLAN_PA_FTM 33 #define WLAN_PA_FILS_DISCOVERY 34 +#define WLAN_PA_LOCATION_MEASUREMENT_REPORT 47 /* Protected Dual of Public Action frames (IEEE Std 802.11-2016, 9.6.11, * Table 9-332) */ @@ -694,6 +703,11 @@ #define WLAN_RRM_NEIGHBOR_REPORT_REQUEST 4 #define WLAN_RRM_NEIGHBOR_REPORT_RESPONSE 5 +/* Protected Fine Timing Frame Action Field value */ +#define WLAN_PROT_FTM_REQUEST 1 +#define WLAN_PROT_FTM 2 +#define WLAN_PROT_FTM_REPORT 3 + /* Radio Measurement capabilities (from RM Enabled Capabilities element) * IEEE Std 802.11-2016, 9.4.2.45, Table 9-157 */ /* byte 1 (out of 5) */ @@ -2364,4 +2378,51 @@ enum mscs_description_subelem { MCSC_SUBELEM_STATUS = 1, }; +/* + * IEEE Std 802.11ai-2016, 9.6.8.36 FILS Discovery frame format, + * Figure 9-687b - FILS Discovery Frame Control subfield format + */ +#define FD_FRAME_CTL_CAP_PRESENT ((u16) BIT(5)) +#define FD_FRAME_CTL_SHORT_SSID_PRESENT ((u16) BIT(6)) +#define FD_FRAME_CTL_AP_CSN_PRESENT ((u16) BIT(7)) +#define FD_FRAME_CTL_ANO_PRESENT ((u16) BIT(8)) +#define FD_FRAME_CTL_FREQ_SEG1_PRESENT ((u16) BIT(9)) +#define FD_FRAME_CTL_PRI_CHAN_PRESENT ((u16) BIT(10)) +#define FD_FRAME_CTL_RSN_INFO_PRESENT ((u16) BIT(11)) +#define FD_FRAME_CTL_LENGTH_PRESENT ((u16) BIT(12)) +#define FD_FRAME_CTL_MD_PRESENT ((u16) BIT(13)) + +/* + * IEEE Std 802.11ai-2016, 9.6.8.36 FILS Discovery frame format, + * Figure 9-687c - FD Capability subfield format + */ +#define FD_CAP_ESS BIT(0) +#define FD_CAP_PRIVACY BIT(1) +#define FD_CAP_MULTI_BSSID_PRESENT BIT(9) + +#define FD_CAP_BSS_CHWIDTH_20 0 +#define FD_CAP_BSS_CHWIDTH_40 1 +#define FD_CAP_BSS_CHWIDTH_80 2 +#define FD_CAP_BSS_CHWIDTH_160_80_80 3 +#define FD_CAP_BSS_CHWIDTH_SHIFT 2 + +#define FD_CAP_NSS_1 0 +#define FD_CAP_NSS_2 1 +#define FD_CAP_NSS_3 2 +#define FD_CAP_NSS_4 3 +#define FD_CAP_NSS_5_8 4 +#define FD_CAP_NSS_SHIFT 5 + +#define FD_CAP_PHY_INDEX_HR_DSSS 0 +#define FD_CAP_PHY_INDEX_ERP_OFDM 1 +#define FD_CAP_PHY_INDEX_HT 2 +#define FD_CAP_PHY_INDEX_VHT 3 +#define FD_CAP_PHY_INDEX_HE 4 /* P802.11ax */ +#define FD_CAP_PHY_INDEX_SHIFT 10 + +/* + * IEEE P802.11ax/D8.0 26.17.2.3.2, AP behavior for fast passive scanning + */ +#define FD_MAX_INTERVAL_6GHZ 20 /* TUs */ + #endif /* IEEE802_11_DEFS_H */ diff --git a/src/common/ptksa_cache.c b/src/common/ptksa_cache.c new file mode 100644 index 0000000..6a053d6 --- /dev/null +++ b/src/common/ptksa_cache.c @@ -0,0 +1,321 @@ +/* + * RSN PTKSA cache implementation + * + * Copyright (C) 2019 Intel Corporation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include "utils/common.h" +#include "eloop.h" +#include "common/ptksa_cache.h" + +#define PTKSA_CACHE_MAX_ENTRIES 16 + +struct ptksa_cache { + struct dl_list ptksa; + unsigned int n_ptksa; +}; + +static void ptksa_cache_set_expiration(struct ptksa_cache *ptksa); + + +static void ptksa_cache_free_entry(struct ptksa_cache *ptksa, + struct ptksa_cache_entry *entry) +{ + ptksa->n_ptksa--; + + dl_list_del(&entry->list); + bin_clear_free(entry, sizeof(*entry)); +} + + +static void ptksa_cache_expire(void *eloop_ctx, void *timeout_ctx) +{ + struct ptksa_cache *ptksa = eloop_ctx; + struct ptksa_cache_entry *e, *next; + struct os_reltime now; + + if (!ptksa) + return; + + os_get_reltime(&now); + + dl_list_for_each_safe(e, next, &ptksa->ptksa, + struct ptksa_cache_entry, list) { + if (e->expiration > now.sec) + continue; + + wpa_printf(MSG_DEBUG, "Expired PTKSA cache entry for " MACSTR, + MAC2STR(e->addr)); + + ptksa_cache_free_entry(ptksa, e); + } + + ptksa_cache_set_expiration(ptksa); +} + + +static void ptksa_cache_set_expiration(struct ptksa_cache *ptksa) +{ + struct ptksa_cache_entry *e; + int sec; + struct os_reltime now; + + eloop_cancel_timeout(ptksa_cache_expire, ptksa, NULL); + + if (!ptksa || !ptksa->n_ptksa) + return; + + e = dl_list_first(&ptksa->ptksa, struct ptksa_cache_entry, list); + if (!e) + return; + + os_get_reltime(&now); + sec = e->expiration - now.sec; + if (sec < 0) + sec = 0; + + eloop_register_timeout(sec + 1, 0, ptksa_cache_expire, ptksa, NULL); +} + + +/* + * ptksa_cache_init - Initialize PTKSA cache + * + * Returns: Pointer to PTKSA cache data or %NULL on failure + */ +struct ptksa_cache * ptksa_cache_init(void) +{ + struct ptksa_cache *ptksa = os_zalloc(sizeof(struct ptksa_cache)); + + wpa_printf(MSG_DEBUG, "PTKSA: Initializing"); + + if (ptksa) + dl_list_init(&ptksa->ptksa); + + return ptksa; +} + + +/* + * ptksa_cache_deinit - Free all entries in PTKSA cache + * @ptksa: Pointer to PTKSA cache data from ptksa_cache_init() + */ +void ptksa_cache_deinit(struct ptksa_cache *ptksa) +{ + struct ptksa_cache_entry *e, *next; + + if (!ptksa) + return; + + wpa_printf(MSG_DEBUG, "PTKSA: Deinit. n_ptksa=%u", ptksa->n_ptksa); + + dl_list_for_each_safe(e, next, &ptksa->ptksa, + struct ptksa_cache_entry, list) + ptksa_cache_free_entry(ptksa, e); + + eloop_cancel_timeout(ptksa_cache_expire, ptksa, NULL); + os_free(ptksa); +} + + +/* + * ptksa_cache_get - Fetch a PTKSA cache entry + * @ptksa: Pointer to PTKSA cache data from ptksa_cache_init() + * @addr: Peer address or %NULL to match any + * @cipher: Specific cipher suite to search for or WPA_CIPHER_NONE for any + * Returns: Pointer to PTKSA cache entry or %NULL if no match was found + */ +struct ptksa_cache_entry * ptksa_cache_get(struct ptksa_cache *ptksa, + const u8 *addr, u32 cipher) +{ + struct ptksa_cache_entry *e; + + if (!ptksa) + return NULL; + + dl_list_for_each(e, &ptksa->ptksa, struct ptksa_cache_entry, list) { + if ((!addr || os_memcmp(e->addr, addr, ETH_ALEN) == 0) && + (cipher == WPA_CIPHER_NONE || cipher == e->cipher)) + return e; + } + + return NULL; +} + + +/* + * ptksa_cache_list - Dump text list of entries in PTKSA cache + * @ptksa: Pointer to PTKSA cache data from ptksa_cache_init() + * @buf: Buffer for the list + * @len: Length of the buffer + * Returns: Number of bytes written to buffer + * + * This function is used to generate a text format representation of the + * current PTKSA cache contents for the ctrl_iface PTKSA command. + */ +int ptksa_cache_list(struct ptksa_cache *ptksa, char *buf, size_t len) +{ + struct ptksa_cache_entry *e; + int i = 0, ret; + char *pos = buf; + struct os_reltime now; + + if (!ptksa) + return 0; + + os_get_reltime(&now); + + ret = os_snprintf(pos, buf + len - pos, + "Index / ADDR / Cipher / expiration (secs) / TK / KDK\n"); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + + dl_list_for_each(e, &ptksa->ptksa, struct ptksa_cache_entry, list) { + ret = os_snprintf(pos, buf + len - pos, "%u " MACSTR, + i, MAC2STR(e->addr)); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, buf + len - pos, " %s %lu ", + wpa_cipher_txt(e->cipher), + e->expiration - now.sec); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + + ret = wpa_snprintf_hex(pos, buf + len - pos, e->ptk.tk, + e->ptk.tk_len); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, buf + len - pos, " "); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + + ret = wpa_snprintf_hex(pos, buf + len - pos, e->ptk.kdk, + e->ptk.kdk_len); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, buf + len - pos, "\n"); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + + i++; + } + + return pos - buf; +} + + +/* + * ptksa_cache_flush - Flush PTKSA cache entries + * + * @ptksa: Pointer to PTKSA cache data from ptksa_cache_init() + * @addr: Peer address or %NULL to match any + * @cipher: Specific cipher suite to search for or WPA_CIPHER_NONE for any + */ +void ptksa_cache_flush(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher) +{ + struct ptksa_cache_entry *e, *next; + bool removed = false; + + if (!ptksa) + return; + + dl_list_for_each_safe(e, next, &ptksa->ptksa, struct ptksa_cache_entry, + list) { + if ((!addr || os_memcmp(e->addr, addr, ETH_ALEN) == 0) && + (cipher == WPA_CIPHER_NONE || cipher == e->cipher)) { + wpa_printf(MSG_DEBUG, + "Flush PTKSA cache entry for " MACSTR, + MAC2STR(e->addr)); + + ptksa_cache_free_entry(ptksa, e); + removed = true; + } + } + + if (removed) + ptksa_cache_set_expiration(ptksa); +} + + +/* + * ptksa_cache_add - Add a PTKSA cache entry + * @ptksa: Pointer to PTKSA cache data from ptksa_cache_init() + * @addr: Peer address + * @cipher: The cipher used + * @life_time: The PTK life time in seconds + * @ptk: The PTK + * Returns: Pointer to the added PTKSA cache entry or %NULL on error + * + * This function creates a PTKSA entry and adds it to the PTKSA cache. + * If an old entry is already in the cache for the same peer and cipher + * this entry will be replaced with the new entry. + */ +struct ptksa_cache_entry * ptksa_cache_add(struct ptksa_cache *ptksa, + const u8 *addr, u32 cipher, + u32 life_time, + const struct wpa_ptk *ptk) +{ + struct ptksa_cache_entry *entry, *tmp; + struct os_reltime now; + + if (!ptksa || !ptk || !addr || !life_time || cipher == WPA_CIPHER_NONE) + return NULL; + + /* remove a previous entry if present */ + ptksa_cache_flush(ptksa, addr, cipher); + + /* no place to add another entry */ + if (ptksa->n_ptksa >= PTKSA_CACHE_MAX_ENTRIES) + return NULL; + + entry = os_zalloc(sizeof(*entry)); + if (!entry) + return NULL; + + dl_list_init(&entry->list); + os_memcpy(entry->addr, addr, ETH_ALEN); + entry->cipher = cipher; + + os_memcpy(&entry->ptk, ptk, sizeof(entry->ptk)); + + os_get_reltime(&now); + entry->expiration = now.sec + life_time; + + dl_list_for_each(tmp, &ptksa->ptksa, struct ptksa_cache_entry, list) { + if (tmp->expiration > entry->expiration) + break; + } + + /* + * If the list was empty add to the head; otherwise if the expiration is + * later then all other entries, add it to the end of the list; + * otherwise add it before the relevant entry. + */ + if (!tmp) + dl_list_add(&ptksa->ptksa, &entry->list); + else if (tmp->expiration < entry->expiration) + dl_list_add(&tmp->list, &entry->list); + else + dl_list_add_tail(&tmp->list, &entry->list); + + ptksa->n_ptksa++; + wpa_printf(MSG_DEBUG, + "Added PTKSA cache entry addr=" MACSTR " cipher=%u", + MAC2STR(addr), cipher); + + return entry; +} diff --git a/src/common/ptksa_cache.h b/src/common/ptksa_cache.h new file mode 100644 index 0000000..28ef291 --- /dev/null +++ b/src/common/ptksa_cache.h @@ -0,0 +1,79 @@ +/* + * RSN PTKSA cache interface + * + * Copyright (C) 2019 Intel Corporation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef PTKSA_CACHE_H +#define PTKSA_CACHE_H + +#include "wpa_common.h" +#include "defs.h" +#include "list.h" + +/** + * struct ptksa_cache_entry - PTKSA cache entry + */ +struct ptksa_cache_entry { + struct dl_list list; + struct wpa_ptk ptk; + os_time_t expiration; + u32 cipher; + u8 addr[ETH_ALEN]; +}; + +#ifdef CONFIG_PTKSA_CACHE + +struct ptksa_cache; + +struct ptksa_cache * ptksa_cache_init(void); +void ptksa_cache_deinit(struct ptksa_cache *ptksa); +struct ptksa_cache_entry * ptksa_cache_get(struct ptksa_cache *ptksa, + const u8 *addr, u32 cipher); +int ptksa_cache_list(struct ptksa_cache *ptksa, char *buf, size_t len); +struct ptksa_cache_entry * ptksa_cache_add(struct ptksa_cache *ptksa, + const u8 *addr, u32 cipher, + u32 life_time, + const struct wpa_ptk *ptk); +void ptksa_cache_flush(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher); + +#else /* CONFIG_PTKSA_CACHE */ + +static inline struct ptksa_cache * ptksa_cache_init(void) +{ + return (struct ptksa_cache *) 1; +} + +static inline void ptksa_cache_deinit(struct ptksa_cache *ptksa) +{ +} + +static inline struct ptksa_cache_entry * +ptksa_cache_get(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher) +{ + return NULL; +} + +static inline int ptksa_cache_list(struct ptksa_cache *ptksa, + char *buf, size_t len) +{ + return -1; +} + +static inline struct ptksa_cache_entry * +ptksa_cache_add(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher, + u32 life_time, const struct wpa_ptk *ptk) +{ + return NULL; +} + +static inline void ptksa_cache_flush(struct ptksa_cache *ptksa, + const u8 *addr, u32 cipher) +{ +} + +#endif /* CONFIG_PTKSA_CACHE */ +#endif /* PTKSA_CACHE_H */ diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h index d4c1da7..f7e5571 100644 --- a/src/common/qca-vendor.h +++ b/src/common/qca-vendor.h @@ -1458,6 +1458,12 @@ enum qca_wlan_vendor_acs_hw_mode { * concurrent network sessions on different Wi-Fi bands. This feature * capability is attributed to the hardware's capability to support * the same (e.g., DBS). + * @QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT: Flag indicating whether the + * responses for the respective TWT operations are asynchronous (separate + * event message) from the driver. If not specified, the responses are + * synchronous (in vendor command reply) to the request. Each TWT + * operation is specifically mentioned (against its respective + * documentation) to support either of these or both modes. * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits */ enum qca_wlan_vendor_features { @@ -1475,6 +1481,7 @@ enum qca_wlan_vendor_features { QCA_WLAN_VENDOR_FEATURE_THERMAL_CONFIG = 11, QCA_WLAN_VENDOR_FEATURE_ADAPTIVE_11R = 12, QCA_WLAN_VENDOR_FEATURE_CONCURRENT_BAND_SESSIONS = 13, + QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT = 14, NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */ }; @@ -2300,6 +2307,10 @@ enum qca_wlan_vendor_attr_config { * frame. The updated NSS value after the connection shall not be * greater than the one negotiated during the connection. Any such * higher value configuration shall be returned with a failure. + * Only symmetric NSS configuration (such as 2X2 or 1X1) can be done + * using this attribute. QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS and + * QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS attributes shall be used to + * configure the asymmetric NSS configuration (such as 1X2). */ QCA_WLAN_VENDOR_ATTR_CONFIG_NSS = 70, /* 8-bit unsigned value to trigger Optimized Power Management: @@ -2348,6 +2359,46 @@ enum qca_wlan_vendor_attr_config { */ QCA_WLAN_VENDOR_ATTR_CONFIG_ANI_LEVEL = 76, + /* 8-bit unsigned value. This attribute is used to dynamically configure + * the number of spatial streams used for transmitting the data. When + * configured in the disconnected state, the configured value will + * be considered for the following connection attempt. + * If the NSS is updated after the connection, the updated NSS value + * is notified to the peer using the Operating Mode Notification/Spatial + * Multiplexing Power Save frame. + * The TX NSS value configured after the connection shall not be greater + * than the value negotiated during the connection. Any such higher + * value configuration shall be treated as invalid configuration by + * the driver. This attribute shall be configured along with + * QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS attribute to define the symmetric + * configuration (such as 2X2 or 1X1) or the asymmetric + * configuration (such as 1X2). + * If QCA_WLAN_VENDOR_ATTR_CONFIG_NSS attribute is also provided along + * with this QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS attribute the driver + * will update the TX NSS based on QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS = 77, + + /* 8-bit unsigned value. This attribute is used to dynamically configure + * the number of spatial streams used for receiving the data. When + * configured in the disconnected state, the configured value will + * be considered for the following connection attempt. + * If the NSS is updated after the connection, the updated NSS value + * is notified to the peer using the Operating Mode Notification/Spatial + * Multiplexing Power Save frame. + * The RX NSS value configured after the connection shall not be greater + * than the value negotiated during the connection. Any such higher + * value configuration shall be treated as invalid configuration by + * the driver. This attribute shall be configured along with + * QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS attribute to define the symmetric + * configuration (such as 2X2 or 1X1) or the asymmetric + * configuration (such as 1X2). + * If QCA_WLAN_VENDOR_ATTR_CONFIG_NSS attribute is also provided along + * with this QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS attribute the driver + * will update the RX NSS based on QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS = 78, + /* keep last */ QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_CONFIG_MAX = @@ -4192,6 +4243,139 @@ enum qca_vendor_roam_triggers { QCA_ROAM_TRIGGER_REASON_EXTERNAL_SCAN = 1 << 12, }; +/* + * enum qca_vendor_roam_fail_reasons: Defines the various roam + * fail reasons. This enum value is used in + * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_FAIL_REASON attribute. + * + * @QCA_ROAM_FAIL_REASON_SCAN_NOT_ALLOWED: Roam module in the firmware is not + * able to trigger the scan. + * @QCA_ROAM_FAIL_REASON_NO_AP_FOUND: No roamable APs found during roam scan. + * @QCA_ROAM_FAIL_REASON_NO_CAND_AP_FOUND: No candidate APs found during roam + * scan. + * @QCA_ROAM_FAIL_REASON_HOST: Roam fail due to disconnect issued from host. + * @QCA_ROAM_FAIL_REASON_AUTH_SEND: Unable to send Authentication frame. + * @QCA_ROAM_FAIL_REASON_AUTH_RECV: Received Authentication frame with error + * status code. + * @QCA_ROAM_FAIL_REASON_NO_AUTH_RESP: Authentication frame not received. + * @QCA_ROAM_FAIL_REASON_REASSOC_SEND: Unable to send Reassociation Request + * frame. + * @QCA_ROAM_FAIL_REASON_REASSOC_RECV: Received Reassociation Response frame + * with error status code. + * @QCA_ROAM_FAIL_REASON_NO_REASSOC_RESP: Reassociation Response frame not + * received. + * @QCA_ROAM_FAIL_REASON_SCAN_FAIL: Scan module not able to start scan. + * @QCA_ROAM_FAIL_REASON_AUTH_NO_ACK: No ACK is received for Authentication + * frame. + * @QCA_ROAM_FAIL_REASON_AUTH_INTERNAL_DROP: Authentication frame is dropped + * internally before transmission. + * @QCA_ROAM_FAIL_REASON_REASSOC_NO_ACK: No ACK is received for Reassociation + * Request frame. + * @QCA_ROAM_FAIL_REASON_REASSOC_INTERNAL_DROP: Reassociation Request frame is + * dropped internally. + * @QCA_ROAM_FAIL_REASON_EAPOL_M1_TIMEOUT: EAPOL-Key M1 is not received and + * times out. + * @QCA_ROAM_FAIL_REASON_EAPOL_M2_SEND: Unable to send EAPOL-Key M2 frame. + * @QCA_ROAM_FAIL_REASON_EAPOL_M2_INTERNAL_DROP: EAPOL-Key M2 frame dropped + * internally. + * @QCA_ROAM_FAIL_REASON_EAPOL_M2_NO_ACK: No ACK is received for EAPOL-Key + * M2 frame. + * @QCA_ROAM_FAIL_REASON_EAPOL_M3_TIMEOUT: EAPOL-Key M3 frame is not received. + * @QCA_ROAM_FAIL_REASON_EAPOL_M4_SEND: Unable to send EAPOL-Key M4 frame. + * @QCA_ROAM_FAIL_REASON_EAPOL_M4_INTERNAL_DROP: EAPOL-Key M4 frame dropped + * internally. + * @QCA_ROAM_FAIL_REASON_EAPOL_M4_NO_ACK: No ACK is received for EAPOL-Key M4 + * frame. + * @QCA_ROAM_FAIL_REASON_NO_SCAN_FOR_FINAL_BEACON_MISS: Roam scan is not + * started for final beacon miss case. + * @QCA_ROAM_FAIL_REASON_DISCONNECT: Deauthentication or Disassociation frame + * received from the AP during roaming handoff. + * @QCA_ROAM_FAIL_REASON_RESUME_ABORT: Firmware roams to the AP when the Apps + * or host is suspended and gives the indication of the last roamed AP only + * when the Apps is resumed. If the Apps is resumed while the roaming is in + * progress, this ongoing roaming is aborted and the last roamed AP is + * indicated to host. + * @QCA_ROAM_FAIL_REASON_SAE_INVALID_PMKID: WPA3-SAE invalid PMKID. + * @QCA_ROAM_FAIL_REASON_SAE_PREAUTH_TIMEOUT: WPA3-SAE pre-authentication times + * out. + * @QCA_ROAM_FAIL_REASON_SAE_PREAUTH_FAIL: WPA3-SAE pre-authentication fails. + */ +enum qca_vendor_roam_fail_reasons { + QCA_ROAM_FAIL_REASON_NONE = 0, + QCA_ROAM_FAIL_REASON_SCAN_NOT_ALLOWED = 1, + QCA_ROAM_FAIL_REASON_NO_AP_FOUND = 2, + QCA_ROAM_FAIL_REASON_NO_CAND_AP_FOUND = 3, + QCA_ROAM_FAIL_REASON_HOST = 4, + QCA_ROAM_FAIL_REASON_AUTH_SEND = 5, + QCA_ROAM_FAIL_REASON_AUTH_RECV = 6, + QCA_ROAM_FAIL_REASON_NO_AUTH_RESP = 7, + QCA_ROAM_FAIL_REASON_REASSOC_SEND = 8, + QCA_ROAM_FAIL_REASON_REASSOC_RECV = 9, + QCA_ROAM_FAIL_REASON_NO_REASSOC_RESP = 10, + QCA_ROAM_FAIL_REASON_SCAN_FAIL = 11, + QCA_ROAM_FAIL_REASON_AUTH_NO_ACK = 12, + QCA_ROAM_FAIL_REASON_AUTH_INTERNAL_DROP = 13, + QCA_ROAM_FAIL_REASON_REASSOC_NO_ACK = 14, + QCA_ROAM_FAIL_REASON_REASSOC_INTERNAL_DROP = 15, + QCA_ROAM_FAIL_REASON_EAPOL_M1_TIMEOUT = 16, + QCA_ROAM_FAIL_REASON_EAPOL_M2_SEND = 17, + QCA_ROAM_FAIL_REASON_EAPOL_M2_INTERNAL_DROP = 18, + QCA_ROAM_FAIL_REASON_EAPOL_M2_NO_ACK = 19, + QCA_ROAM_FAIL_REASON_EAPOL_M3_TIMEOUT = 20, + QCA_ROAM_FAIL_REASON_EAPOL_M4_SEND = 21, + QCA_ROAM_FAIL_REASON_EAPOL_M4_INTERNAL_DROP = 22, + QCA_ROAM_FAIL_REASON_EAPOL_M4_NO_ACK = 23, + QCA_ROAM_FAIL_REASON_NO_SCAN_FOR_FINAL_BEACON_MISS = 24, + QCA_ROAM_FAIL_REASON_DISCONNECT = 25, + QCA_ROAM_FAIL_REASON_RESUME_ABORT = 26, + QCA_ROAM_FAIL_REASON_SAE_INVALID_PMKID = 27, + QCA_ROAM_FAIL_REASON_SAE_PREAUTH_TIMEOUT = 28, + QCA_ROAM_FAIL_REASON_SAE_PREAUTH_FAIL = 29, +}; + +/* + * enum qca_vendor_roam_invoke_fail_reasons: Defines the various roam + * invoke fail reasons. This enum value is used in + * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_INVOKE_FAIL_REASON attribute. + * + * @QCA_ROAM_INVOKE_STATUS_IFACE_INVALID: Invalid interface ID is passed + * in roam invoke command. + * @QCA_ROAM_INVOKE_STATUS_OFFLOAD_DISABLE: Roam offload in firmware is not + * enabled. + * @QCA_ROAM_INVOKE_STATUS_AP_SSID_LENGTH_INVALID: Connected AP profile SSID + * length is invalid. + * @QCA_ROAM_INVOKE_STATUS_ROAM_DISALLOW: Firmware internal roaming is already + * in progress. + * @QCA_ROAM_INVOKE_STATUS_NON_ROAMABLE_AP: Host sends the Beacon/Probe Response + * of the AP in the roam invoke command to firmware. This reason is sent by the + * firmware when the given AP is configured to be ignored or SSID/security + * does not match. + * @QCA_ROAM_INVOKE_STATUS_ROAM_INTERNAL_FAIL: Roam handoff failed because of + * firmware internal reasons. + * @QCA_ROAM_INVOKE_STATUS_DISALLOW: Roam invoke trigger is not enabled. + * @QCA_ROAM_INVOKE_STATUS_SCAN_FAIL: Scan start fail for roam invoke. + * @QCA_ROAM_INVOKE_STATUS_START_ROAM_FAIL: Roam handoff start fail. + * @QCA_ROAM_INVOKE_STATUS_INVALID_PARAMS: Roam invoke parameters are invalid. + * @QCA_ROAM_INVOKE_STATUS_NO_CAND_AP: No candidate AP found to roam to. + * @QCA_ROAM_INVOKE_STATUS_ROAM_FAIL: Roam handoff failed. + */ +enum qca_vendor_roam_invoke_fail_reasons { + QCA_ROAM_INVOKE_STATUS_NONE = 0, + QCA_ROAM_INVOKE_STATUS_IFACE_INVALID = 1, + QCA_ROAM_INVOKE_STATUS_OFFLOAD_DISABLE = 2, + QCA_ROAM_INVOKE_STATUS_AP_SSID_LENGTH_INVALID = 3, + QCA_ROAM_INVOKE_STATUS_ROAM_DISALLOW = 4, + QCA_ROAM_INVOKE_STATUS_NON_ROAMABLE_AP = 5, + QCA_ROAM_INVOKE_STATUS_ROAM_INTERNAL_FAIL = 6, + QCA_ROAM_INVOKE_STATUS_DISALLOW = 7, + QCA_ROAM_INVOKE_STATUS_SCAN_FAIL = 8, + QCA_ROAM_INVOKE_STATUS_START_ROAM_FAIL = 9, + QCA_ROAM_INVOKE_STATUS_INVALID_PARAMS = 10, + QCA_ROAM_INVOKE_STATUS_NO_CAND_AP = 11, + QCA_ROAM_INVOKE_STATUS_ROAM_FAIL = 12, + +}; + /** * enum qca_vendor_attr_roam_candidate_selection_criteria: * @@ -7774,6 +7958,37 @@ enum qca_wlan_vendor_attr_wifi_test_config { */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_FULL_BW_UL_MU_MIMO = 45, + /* 16-bit unsigned value to configure the driver with a specific BSS + * max idle period to advertise in the BSS Max Idle Period element + * (IEEE Std 802.11-2016, 9.4.2.79) in (Re)Association Request frames. + * This attribute is used to configure the testbed device. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BSS_MAX_IDLE_PERIOD = 46, + + /* 8-bit unsigned value to configure the driver to use only RU 242 tone + * for data transmission. + * 0 - Default behavior, 1 - Configure RU 242 tone for data Tx. + * This attribute is used to configure the testbed device. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_RU_242_TONE_TX = 47, + + /* 8-bit unsigned value to configure the driver to disable data and + * management response frame transmission to test the BSS max idle + * feature. + * 0 - Default behavior, 1 - Disable data and management response Tx. + * This attribute is used to configure the testbed device. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_DISABLE_DATA_MGMT_RSP_TX = 48, + + /* 8-bit unsigned value to configure the driver/firmware to enable or + * disable Punctured Preamble Rx subfield in the HE PHY capabilities + * information field. + * 0 - Disable Punctured Preamble Rx subfield + * 1 - Enable Punctured Preamble Rx subfield + * This attribute is used to configure the testbed device. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_PUNCTURED_PREAMBLE_RX = 49, + /* keep last */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX = @@ -7783,32 +7998,69 @@ enum qca_wlan_vendor_attr_wifi_test_config { /** * enum qca_wlan_twt_operation - Operation of the config TWT request * Values for %QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_OPERATION. + * The response for the respective operations can be either synchronous or + * asynchronous (wherever specified). If synchronous, the response to this + * operation is obtained in the corresponding vendor command reply to the user + * space. For the asynchronous case the response is obtained as an event with + * the same operation type. + * + * Drivers shall support either of these modes but not both simultaneously. + * This support for asynchronous mode is advertised through the flag + * QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT. If this flag is not advertised, + * the driver shall support synchronous mode. * * @QCA_WLAN_TWT_SET: Setup a TWT session. Required parameters are configured * through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum - * qca_wlan_vendor_attr_twt_setup. + * qca_wlan_vendor_attr_twt_setup. Depending upon the + * @QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT capability, this is either a + * synchronous or asynchronous operation. * * @QCA_WLAN_TWT_GET: Get the configured TWT parameters. Required parameters are * obtained through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum - * qca_wlan_vendor_attr_twt_setup. + * qca_wlan_vendor_attr_twt_setup. This is a synchronous operation. * * @QCA_WLAN_TWT_TERMINATE: Terminate the TWT session. Required parameters are * obtained through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum * qca_wlan_vendor_attr_twt_setup. Valid only after the TWT session is setup. + * This terminate can either get triggered by the user space or can as well be + * a notification from the firmware if it initiates a terminate. + * Depending upon the @QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT capability, + * the request from user space can either be a synchronous or asynchronous + * operation. * * @QCA_WLAN_TWT_SUSPEND: Suspend the TWT session. Required parameters are * obtained through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum * qca_wlan_vendor_attr_twt_setup. Valid only after the TWT session is setup. + * Depending upon the @QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT capability, + * this is either a synchronous or asynchronous operation. * * @QCA_WLAN_TWT_RESUME: Resume the TWT session. Required parameters are * configured through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum * qca_wlan_vendor_attr_twt_resume. Valid only after the TWT session is setup. + * This can as well be a notification from the firmware on a QCA_WLAN_TWT_NUDGE + * request. Depending upon the @QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT + * capability, this is either a synchronous or asynchronous operation. * * @QCA_WLAN_TWT_NUDGE: Suspend and resume the TWT session. TWT nudge is a * combination of suspend and resume in a single request. Required parameters * are configured through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the * enum qca_wlan_vendor_attr_twt_nudge. Valid only after the TWT session is - * setup. + * setup. Depending upon the @QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT + * capability, this is either a synchronous or asynchronous operation. + * + * @QCA_WLAN_TWT_GET_STATS: Get the TWT session traffic statistics information. + * Refers the enum qca_wlan_vendor_attr_twt_stats. Valid only after the TWT + * session is setup. It's a synchronous operation. + * + * @QCA_WLAN_TWT_CLEAR_STATS: Clear TWT session traffic statistics information. + * Valid only after the TWT session is setup. It's a synchronous operation. + * + * @QCA_WLAN_TWT_GET_CAPABILITIES: Get TWT capabilities of this device and its + * peer. Refers the enum qca_wlan_vendor_attr_twt_capability. It's a synchronous + * operation. + * + * @QCA_WLAN_TWT_SETUP_READY_NOTIFY: Notify userspace that the firmare is + * ready for a new TWT session setup after it issued a TWT teardown. */ enum qca_wlan_twt_operation { QCA_WLAN_TWT_SET = 0, @@ -7817,6 +8069,10 @@ enum qca_wlan_twt_operation { QCA_WLAN_TWT_SUSPEND = 3, QCA_WLAN_TWT_RESUME = 4, QCA_WLAN_TWT_NUDGE = 5, + QCA_WLAN_TWT_GET_STATS = 6, + QCA_WLAN_TWT_CLEAR_STATS = 7, + QCA_WLAN_TWT_GET_CAPABILITIES = 8, + QCA_WLAN_TWT_SETUP_READY_NOTIFY = 9, }; /** @@ -7830,8 +8086,8 @@ enum qca_wlan_twt_operation { * * @QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS: Nested attribute representing the * parameters configured for TWT. These parameters are represented by - * enum qca_wlan_vendor_attr_twt_setup or enum qca_wlan_vendor_attr_twt_resume - * based on the operation. + * enum qca_wlan_vendor_attr_twt_setup, enum qca_wlan_vendor_attr_twt_resume, + * or enum qca_wlan_vendor_attr_twt_stats based on the operation. */ enum qca_wlan_vendor_attr_config_twt { QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_INVALID = 0, @@ -7992,6 +8248,19 @@ enum qca_wlan_vendor_attr_nan_params { }; /** + * qca_wlan_twt_setup_state: Represents the TWT session states. + * + * QCA_WLAN_TWT_SETUP_STATE_NOT_ESTABLISHED: TWT session not established. + * QCA_WLAN_TWT_SETUP_STATE_ACTIVE: TWT session is active. + * QCA_WLAN_TWT_SETUP_STATE_SUSPEND: TWT session is in suspended state. + */ +enum qca_wlan_twt_setup_state { + QCA_WLAN_TWT_SETUP_STATE_NOT_ESTABLISHED = 0, + QCA_WLAN_TWT_SETUP_STATE_ACTIVE = 1, + QCA_WLAN_TWT_SETUP_STATE_SUSPEND = 2, +}; + +/** * enum qca_wlan_vendor_attr_twt_setup: Represents attributes for * TWT (Target Wake Time) setup request. These attributes are sent as part of * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP and @@ -8041,6 +8310,14 @@ enum qca_wlan_vendor_attr_nan_params { * 2. TWT GET Request and Response * 3. TWT TERMINATE Request and Response * 4. TWT SUSPEND Request and Response + * Flow ID values from 0 to 254 represent a single TWT session + * Flow ID value of 255 represents all TWT sessions for the following + * 1. TWT TERMINATE Request and Response + * 2. TWT SUSPEND Request and Response + * 4. TWT CLEAR STATISTICS request + * 5. TWT GET STATISTICS request and response + * If an invalid dialog ID is provided, status + * QCA_WLAN_VENDOR_TWT_STATUS_SESSION_NOT_EXIST will be returned. * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_EXP: Required (u8) * This attribute (exp) is used along with the mantissa to derive the @@ -8128,6 +8405,23 @@ enum qca_wlan_vendor_attr_nan_params { * 4. TWT SUSPEND Request * In STA mode, this is an optional parameter in request and response for * the above four TWT operations. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MIN_WAKE_INTVL: Optional (u32) + * Minimum tolerance limit of wake interval parameter in microseconds. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX_WAKE_INTVL: Optional (u32) + * Maximum tolerance limit of wake interval parameter in microseconds. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MIN_WAKE_DURATION: Optional (u32) + * Minimum tolerance limit of wake duration parameter in microseconds. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX_WAKE_DURATION: Optional (u32) + * Maximum tolerance limit of wake duration parameter in microseconds. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_STATE: Optional (u32) + * TWT state for the given dialog id. The values for this are represented + * by enum qca_wlan_twt_setup_state. + * This is obtained through TWT GET operation. */ enum qca_wlan_vendor_attr_twt_setup { QCA_WLAN_VENDOR_ATTR_TWT_SETUP_INVALID = 0, @@ -8149,6 +8443,11 @@ enum qca_wlan_vendor_attr_twt_setup { QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TWT_INFO_ENABLED = 14, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAC_ADDR = 15, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MIN_WAKE_INTVL = 16, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX_WAKE_INTVL = 17, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MIN_WAKE_DURATION = 18, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX_WAKE_DURATION = 19, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_STATE = 20, /* keep last */ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_AFTER_LAST, @@ -8178,6 +8477,16 @@ enum qca_wlan_vendor_attr_twt_setup { * unknown reason * @QCA_WLAN_VENDOR_TWT_STATUS_ALREADY_SUSPENDED: TWT session already in * suspend state + * @QCA_WLAN_VENDOR_TWT_STATUS_IE_INVALID: FW has dropped the frame due to + * invalid IE in the received TWT frame + * @QCA_WLAN_VENDOR_TWT_STATUS_PARAMS_NOT_IN_RANGE: Parameters received from + * the responder are not in the specified range + * @QCA_WLAN_VENDOR_TWT_STATUS_PEER_INITIATED_TERMINATE: FW terminated the TWT + * session due to request from the responder. Used on the TWT_TERMINATE + * notification from the firmware. + * @QCA_WLAN_VENDOR_TWT_STATUS_ROAM_INITIATED_TERMINATE: FW terminated the TWT + * session due to roaming. Used on the TWT_TERMINATE notification from the + * firmware. */ enum qca_wlan_vendor_twt_status { QCA_WLAN_VENDOR_TWT_STATUS_OK = 0, @@ -8194,6 +8503,10 @@ enum qca_wlan_vendor_twt_status { QCA_WLAN_VENDOR_TWT_STATUS_DENIED = 11, QCA_WLAN_VENDOR_TWT_STATUS_UNKNOWN_ERROR = 12, QCA_WLAN_VENDOR_TWT_STATUS_ALREADY_SUSPENDED = 13, + QCA_WLAN_VENDOR_TWT_STATUS_IE_INVALID = 14, + QCA_WLAN_VENDOR_TWT_STATUS_PARAMS_NOT_IN_RANGE = 15, + QCA_WLAN_VENDOR_TWT_STATUS_PEER_INITIATED_TERMINATE = 16, + QCA_WLAN_VENDOR_TWT_STATUS_ROAM_INITIATED_TERMINATE = 17, }; /** @@ -8220,6 +8533,10 @@ enum qca_wlan_vendor_twt_status { * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_FLOW_ID: Required (u8). * Flow ID is the unique identifier for each TWT session. This attribute * represents the respective TWT session to resume. + * Flow ID values from 0 to 254 represent a single TWT session + * Flow ID value of 255 represents all TWT sessions. + * If an invalid dialog id is provided, status + * QCA_WLAN_VENDOR_TWT_STATUS_SESSION_NOT_EXIST will be returned. * * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_MAC_ADDR: 6-byte MAC address * Represents the MAC address of the peer to which TWT Resume is @@ -8250,6 +8567,11 @@ enum qca_wlan_vendor_attr_twt_resume { * @QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_FLOW_ID: Required (u8) * Flow ID is the unique identifier for each TWT session. This attribute * represents the respective TWT session to suspend and resume. + * Flow ID values from 0 to 254 represent a single TWT session + * Flow ID value of 255 represents all TWT sessions in TWT NUDGE request + * and response. + * If an invalid dialog id is provided, status + * QCA_WLAN_VENDOR_TWT_STATUS_SESSION_NOT_EXIST will be returned. * * @QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_WAKE_TIME: Required (u32) * This attribute is used as the SP offset which is the offset from @@ -8265,6 +8587,10 @@ enum qca_wlan_vendor_attr_twt_resume { * being sent. This is used in AP mode to represent the respective * client and is a required parameter. In STA mode, this is an optional * parameter. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_WAKE_TIME_TSF: Optional (u64) + * This field contains absolute TSF value of the time at which the TWT + * session will be resumed. */ enum qca_wlan_vendor_attr_twt_nudge { QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_INVALID = 0, @@ -8272,6 +8598,7 @@ enum qca_wlan_vendor_attr_twt_nudge { QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_WAKE_TIME = 2, QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_NEXT_TWT_SIZE = 3, QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_MAC_ADDR = 4, + QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_WAKE_TIME_TSF = 5, /* keep last */ QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_AFTER_LAST, @@ -8280,6 +8607,162 @@ enum qca_wlan_vendor_attr_twt_nudge { }; /** + * enum qca_wlan_vendor_attr_twt_stats: Represents attributes for + * TWT (Target Wake Time) get statistics and clear statistics request. + * These attributes are sent as part of + * %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_FLOW_ID: Required (u8) + * Flow ID is the unique identifier for each TWT session. This attribute + * represents the respective TWT session for get and clear TWT statistics. + * Flow ID values from 0 to 254 represent a single TWT session + * Flow ID value of 255 represents all TWT sessions in + * 1) TWT GET STATISTICS request and response + * 2) TWT CLEAR STATISTICS request + * + * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_MAC_ADDR: 6-byte MAC address + * Represents the MAC address of the peer for which TWT Statistics + * is required. + * In AP mode this is used to represent the respective + * client and is a required parameter for + * 1) TWT GET STATISTICS request and response + * 2) TWT CLEAR STATISTICS request and response + * In STA mode, this is an optional parameter. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_SESSION_WAKE_DURATION: Required (u32) + * This is the duration of the service period in microseconds. + * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVG_WAKE_DURATION: Required (u32) + * Average of the actual wake duration observed so far. Unit is microseconds. + * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS: Required (u32) + * The number of TWT service periods elapsed so far. + * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_MIN_WAKE_DURATION: Required (u32) + * This is the minimum value of the wake duration observed across + * QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS. Unit is + * microseconds. + * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_MAX_WAKE_DURATION: Required (u32) + * This is the maximum value of wake duration observed across + * QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS. Unit is + * microseconds. + * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_TX_MPDU: Required (u32) + * Average number of MPDUs transmitted successfully across + * QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS. + * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_RX_MPDU: Required (u32) + * Average number of MPDUs received successfully across + * QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS. + * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_TX_PACKET_SIZE: Required (u32) + * Average number of bytes transmitted successfully across + * QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS. + * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_RX_PACKET_SIZE: Required (u32) + * Average number of bytes received successfully across + * QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS. + * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_STATUS: Required (u32) + * Status of the TWT GET STATISTICS request. + * This contains status values in enum qca_wlan_vendor_twt_status + * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware. + */ +enum qca_wlan_vendor_attr_twt_stats { + QCA_WLAN_VENDOR_ATTR_TWT_STATS_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TWT_STATS_FLOW_ID = 1, + QCA_WLAN_VENDOR_ATTR_TWT_STATS_MAC_ADDR = 2, + QCA_WLAN_VENDOR_ATTR_TWT_STATS_SESSION_WAKE_DURATION = 3, + QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVG_WAKE_DURATION = 4, + QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS = 5, + QCA_WLAN_VENDOR_ATTR_TWT_STATS_MIN_WAKE_DURATION = 6, + QCA_WLAN_VENDOR_ATTR_TWT_STATS_MAX_WAKE_DURATION = 7, + QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_TX_MPDU = 8, + QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_RX_MPDU = 9, + QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_TX_PACKET_SIZE = 10, + QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_RX_PACKET_SIZE = 11, + QCA_WLAN_VENDOR_ATTR_TWT_STATS_STATUS = 12, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_TWT_STATS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TWT_STATS_MAX = + QCA_WLAN_VENDOR_ATTR_TWT_STATS_AFTER_LAST - 1, +}; + +/** + * qca_wlan_twt_get_capa - Represents the bitmap of TWT capabilities + * supported by the device and the peer. + * Values for %QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_GET_CAPABILITIES + * + * @QCA_WLAN_TWT_CAPA_REQUESTOR: TWT requestor support is advertised by + * TWT non-scheduling STA. This capability is advertised in the HE + * Capability/Extended Capabilities information element in the + * Association Request frame by the device. + * + * @QCA_WLAN_TWT_CAPA_RESPONDER: TWT responder support is advertised by + * the TWT scheduling AP. This capability is advertised in the Extended + * Capabilities/HE Capabilities information element. + * + * @QCA_WLAN_TWT_CAPA_BROADCAST: On the requestor side, this indicates support + * for the broadcast TWT functionality. On the responder side, this indicates + * support for the role of broadcast TWT scheduling functionality. This + * capability is advertised in the HE Capabilities information element. + * + * @QCA_WLAN_TWT_CAPA_TWT_FLEXIBLE: The device supports flexible TWT schedule. + * This capability is advertised in the HE Capabilities information element. + * + * @QCA_WLAN_TWT_CAPA_REQUIRED: The TWT Required is advertised by AP to indicate + * that it mandates the associated HE STAs to support TWT. This capability is + * advertised by AP in the HE Operation Parameters field of the HE Operation + * information element. + */ +enum qca_wlan_twt_capa { + QCA_WLAN_TWT_CAPA_REQUESTOR = BIT(0), + QCA_WLAN_TWT_CAPA_RESPONDER = BIT(1), + QCA_WLAN_TWT_CAPA_BROADCAST = BIT(2), + QCA_WLAN_TWT_CAPA_FLEXIBLE = BIT(3), + QCA_WLAN_TWT_CAPA_REQUIRED = BIT(4), +}; + +/** + * enum qca_wlan_vendor_attr_twt_capability - Represents attributes for TWT + * get capabilities request type. Used by QCA_WLAN_TWT_GET_CAPABILITIES TWT + * operation. + * @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAC_ADDR: 6-byte MAC address + * Represents the MAC address of the peer for which the TWT capabilities + * are being queried. This is used in AP mode to represent the respective + * client. In STA mode, this is an optional parameter. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_SELF: (u16). + * Self TWT capabilities. Carries a bitmap of TWT capabilities specified in + * enum qca_wlan_twt_capa. + * @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_PEER: (u16). + * Peer TWT capabilities. Carries a bitmap of TWT capabilities specified in + * enum qca_wlan_twt_capa. + */ +enum qca_wlan_vendor_attr_twt_capability { + QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAC_ADDR = 1, + QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_SELF = 2, + QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_PEER = 3, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAX = + QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_AFTER_LAST - 1, +}; + +/** * enum qca_wlan_vendor_twt_setup_resp_type - Represents the response type by * the TWT responder * @@ -9623,6 +10106,40 @@ enum qca_vendor_wlan_sta_guard_interval { * include this attribute in response to the * QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO command if there is no connection * failure observed in the last attempted connection. + * + * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_LATEST_TX_RATE: u32, latest TX rate (Kbps) + * used by the station in its last TX frame while communicating to the AP in the + * connected state. When queried in the disconnected state, this represents the + * rate used by the STA in the last TX frame to the AP when it was connected. + * This attribute is used for STA mode only. + * + * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_LATEST_RIX: u32, used in STA mode only. + * This represents the rate index used by the STA for the last TX frame to the + * AP. When queried in the disconnected state, this gives the last RIX used by + * the STA in the last TX frame to the AP when it was connected. + * + * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TSF_OUT_OF_SYNC_COUNT: u32, used in STA + * mode only. This represents the number of times the STA TSF goes out of sync + * from the AP after the connection. If queried in the disconnected state, this + * gives the count of TSF out of sync for the last connection. + * + * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_TRIGGER_REASON: u32, used in STA + * mode only. This represents the roam trigger reason for the last roaming + * attempted by the firmware. This can be queried either in connected state or + * disconnected state. Each bit of this attribute represents the different + * roam trigger reason code which are defined in enum qca_vendor_roam_triggers. + * + * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_FAIL_REASON: u32, used in STA mode + * only. This represents the roam fail reason for the last failed roaming + * attempt by the firmware. Different roam failure reason codes are specified + * in enum qca_vendor_roam_fail_reasons. This can be queried either in + * connected state or disconnected state. + * + * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_INVOKE_FAIL_REASON: u32, used in + * STA mode only. This represents the roam invoke fail reason for the last + * failed roam invoke. Different roam invoke failure reason codes + * are specified in enum qca_vendor_roam_invoke_fail_reasons. This can be + * queried either in connected state or disconnected state. */ enum qca_wlan_vendor_attr_get_sta_info { QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_INVALID = 0, @@ -9669,6 +10186,12 @@ enum qca_wlan_vendor_attr_get_sta_info { QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_MIC_ERROR_COUNT = 41, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_REPLAY_COUNT = 42, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CONNECT_FAIL_REASON_CODE = 43, + QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_LATEST_TX_RATE = 44, + QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_LATEST_RIX = 45, + QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TSF_OUT_OF_SYNC_COUNT = 46, + QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_TRIGGER_REASON = 47, + QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_FAIL_REASON = 48, + QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_INVOKE_FAIL_REASON = 49, /* keep last */ QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_AFTER_LAST, diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index 82a5a17..2b8c7f6 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -333,6 +333,7 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver, * @ptk: Buffer for pairwise transient key * @akmp: Negotiated AKM * @cipher: Negotiated pairwise cipher + * @kdk_len: The length in octets that should be derived for KDK * Returns: 0 on success, -1 on failure * * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy @@ -348,12 +349,13 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, const u8 *addr1, const u8 *addr2, const u8 *nonce1, const u8 *nonce2, struct wpa_ptk *ptk, int akmp, int cipher, - const u8 *z, size_t z_len) + const u8 *z, size_t z_len, size_t kdk_len) { #define MAX_Z_LEN 66 /* with NIST P-521 */ u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN + MAX_Z_LEN]; size_t data_len = 2 * ETH_ALEN + 2 * WPA_NONCE_LEN; - u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN]; + u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN + + WPA_KDK_MAX_LEN]; size_t ptk_len; #ifdef CONFIG_OWE int owe_ptk_workaround = 0; @@ -395,16 +397,24 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, data_len += z_len; } + if (kdk_len > WPA_KDK_MAX_LEN) { + wpa_printf(MSG_ERROR, + "WPA: KDK len=%zu exceeds max supported len", + kdk_len); + return -1; + } + ptk->kck_len = wpa_kck_len(akmp, pmk_len); ptk->kek_len = wpa_kek_len(akmp, pmk_len); ptk->tk_len = wpa_cipher_key_len(cipher); + ptk->kdk_len = kdk_len; if (ptk->tk_len == 0) { wpa_printf(MSG_ERROR, "WPA: Unsupported cipher (0x%x) used in PTK derivation", cipher); return -1; } - ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len; + ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len + ptk->kdk_len; if (wpa_key_mgmt_sha384(akmp)) { #if defined(CONFIG_SUITEB192) || defined(CONFIG_FILS) @@ -488,6 +498,12 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len); wpa_hexdump_key(MSG_DEBUG, "WPA: TK", ptk->tk, ptk->tk_len); + if (kdk_len) { + os_memcpy(ptk->kdk, tmp + ptk->kck_len + ptk->kek_len + + ptk->tk_len, ptk->kdk_len); + wpa_hexdump_key(MSG_DEBUG, "WPA: KDK", ptk->kdk, ptk->kdk_len); + } + ptk->kek2_len = 0; ptk->kck2_len = 0; @@ -576,15 +592,16 @@ int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa, const u8 *snonce, const u8 *anonce, const u8 *dhss, size_t dhss_len, struct wpa_ptk *ptk, u8 *ick, size_t *ick_len, int akmp, int cipher, - u8 *fils_ft, size_t *fils_ft_len) + u8 *fils_ft, size_t *fils_ft_len, size_t kdk_len) { u8 *data, *pos; size_t data_len; u8 tmp[FILS_ICK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN + - FILS_FT_MAX_LEN]; + FILS_FT_MAX_LEN + WPA_KDK_MAX_LEN]; size_t key_data_len; const char *label = "FILS PTK Derivation"; int ret = -1; + size_t offset; /* * FILS-Key-Data = PRF-X(PMK, "FILS PTK Derivation", @@ -595,6 +612,9 @@ int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa, * If doing FT initial mobility domain association: * FILS-FT = L(FILS-Key-Data, ICK_bits + KEK_bits + TK_bits, * FILS-FT_bits) + * When a KDK is derived: + * KDK = L(FILS-Key-Data, ICK_bits + KEK_bits + TK_bits + FILS-FT_bits, + * KDK_bits) */ data_len = 2 * ETH_ALEN + 2 * FILS_NONCE_LEN + dhss_len; data = os_malloc(data_len); @@ -623,6 +643,19 @@ int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa, goto err; key_data_len = *ick_len + ptk->kek_len + ptk->tk_len; + if (kdk_len) { + if (kdk_len > WPA_KDK_MAX_LEN) { + wpa_printf(MSG_ERROR, "FILS: KDK len=%zu too big", + kdk_len); + goto err; + } + + ptk->kdk_len = kdk_len; + key_data_len += kdk_len; + } else { + ptk->kdk_len = 0; + } + if (fils_ft && fils_ft_len) { if (akmp == WPA_KEY_MGMT_FT_FILS_SHA256) { *fils_ft_len = 32; @@ -657,19 +690,27 @@ int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa, wpa_hexdump_key(MSG_DEBUG, "FILS: FILS-Key-Data", tmp, key_data_len); os_memcpy(ick, tmp, *ick_len); + offset = *ick_len; wpa_hexdump_key(MSG_DEBUG, "FILS: ICK", ick, *ick_len); - os_memcpy(ptk->kek, tmp + *ick_len, ptk->kek_len); + os_memcpy(ptk->kek, tmp + offset, ptk->kek_len); wpa_hexdump_key(MSG_DEBUG, "FILS: KEK", ptk->kek, ptk->kek_len); + offset += ptk->kek_len; - os_memcpy(ptk->tk, tmp + *ick_len + ptk->kek_len, ptk->tk_len); + os_memcpy(ptk->tk, tmp + offset, ptk->tk_len); wpa_hexdump_key(MSG_DEBUG, "FILS: TK", ptk->tk, ptk->tk_len); + offset += ptk->tk_len; if (fils_ft && fils_ft_len) { - os_memcpy(fils_ft, tmp + *ick_len + ptk->kek_len + ptk->tk_len, - *fils_ft_len); + os_memcpy(fils_ft, tmp + offset, *fils_ft_len); wpa_hexdump_key(MSG_DEBUG, "FILS: FILS-FT", fils_ft, *fils_ft_len); + offset += *fils_ft_len; + } + + if (ptk->kdk_len) { + os_memcpy(ptk->kdk, tmp + offset, ptk->kdk_len); + wpa_hexdump_key(MSG_DEBUG, "FILS: KDK", ptk->kdk, ptk->kdk_len); } ptk->kek2_len = 0; @@ -1127,6 +1168,266 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_PASN + +/* + * pasn_use_sha384 - Should SHA384 be used or SHA256 + * + * @akmp: Authentication and key management protocol + * @cipher: The cipher suite + * + * According to IEEE P802.11az/D2.7, 12.12.7, the hash algorithm to use is the + * hash algorithm defined for the Base AKM (see Table 9-151 (AKM suite + * selectors)). When there is no Base AKM, the hash algorithm is selected based + * on the pairwise cipher suite provided in the RSNE by the AP in the second + * PASN frame. SHA-256 is used as the hash algorithm, except for the ciphers + * 00-0F-AC:9 and 00-0F-AC:10 for which SHA-384 is used. + */ +static bool pasn_use_sha384(int akmp, int cipher) +{ + return (akmp == WPA_KEY_MGMT_PASN && (cipher == WPA_CIPHER_CCMP_256 || + cipher == WPA_CIPHER_GCMP_256)) || + wpa_key_mgmt_sha384(akmp); +} + + +/** + * pasn_pmk_to_ptk - Calculate PASN PTK from PMK, addresses, etc. + * @pmk: Pairwise master key + * @pmk_len: Length of PMK + * @spa: Suppplicant address + * @bssid: AP BSSID + * @dhss: Is the shared secret (DHss) derived from the PASN ephemeral key + * exchange encoded as an octet string + * @dhss_len: The length of dhss in octets + * @ptk: Buffer for pairwise transient key + * @akmp: Negotiated AKM + * @cipher: Negotiated pairwise cipher + * @kdk_len: the length in octets that should be derived for HTLK. Can be zero. + * Returns: 0 on success, -1 on failure + */ +int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len, + const u8 *spa, const u8 *bssid, + const u8 *dhss, size_t dhss_len, + struct wpa_ptk *ptk, int akmp, int cipher, + size_t kdk_len) +{ + u8 tmp[WPA_KCK_MAX_LEN + WPA_TK_MAX_LEN + WPA_KDK_MAX_LEN]; + u8 *data; + size_t data_len, ptk_len; + int ret = -1; + const char *label = "PASN PTK Derivation"; + + if (!pmk || !pmk_len) { + wpa_printf(MSG_ERROR, "PASN: No PMK set for PTK derivation"); + return -1; + } + + if (!dhss || !dhss_len) { + wpa_printf(MSG_ERROR, "PASN: No DHss set for PTK derivation"); + return -1; + } + + /* + * PASN-PTK = KDF(PMK, “PASN PTK Derivation”, SPA || BSSID || DHss) + * + * KCK = L(PASN-PTK, 0, 256) + * TK = L(PASN-PTK, 256, TK_bits) + * KDK = L(PASN-PTK, 256 + TK_bits, kdk_len * 8) + */ + data_len = 2 * ETH_ALEN + dhss_len; + data = os_zalloc(data_len); + if (!data) + return -1; + + os_memcpy(data, spa, ETH_ALEN); + os_memcpy(data + ETH_ALEN, bssid, ETH_ALEN); + os_memcpy(data + 2 * ETH_ALEN, dhss, dhss_len); + + ptk->kck_len = WPA_PASN_KCK_LEN; + ptk->tk_len = wpa_cipher_key_len(cipher); + ptk->kdk_len = kdk_len; + ptk->kek_len = 0; + ptk->kek2_len = 0; + ptk->kck2_len = 0; + + if (ptk->tk_len == 0) { + wpa_printf(MSG_ERROR, + "PASN: Unsupported cipher (0x%x) used in PTK derivation", + cipher); + goto err; + } + + ptk_len = ptk->kck_len + ptk->tk_len + ptk->kdk_len; + if (ptk_len > sizeof(tmp)) + goto err; + + if (pasn_use_sha384(akmp, cipher)) { + wpa_printf(MSG_DEBUG, "PASN: PTK derivation using SHA384"); + + if (sha384_prf(pmk, pmk_len, label, data, data_len, tmp, + ptk_len) < 0) + goto err; + } else { + wpa_printf(MSG_DEBUG, "PASN: PTK derivation using SHA256"); + + if (sha256_prf(pmk, pmk_len, label, data, data_len, tmp, + ptk_len) < 0) + goto err; + } + + wpa_printf(MSG_DEBUG, + "PASN: PTK derivation: SPA=" MACSTR " BSSID=" MACSTR, + MAC2STR(spa), MAC2STR(bssid)); + + wpa_hexdump_key(MSG_DEBUG, "PASN: DHss", dhss, dhss_len); + wpa_hexdump_key(MSG_DEBUG, "PASN: PMK", pmk, pmk_len); + wpa_hexdump_key(MSG_DEBUG, "PASN: PASN-PTK", tmp, ptk_len); + + os_memcpy(ptk->kck, tmp, WPA_PASN_KCK_LEN); + wpa_hexdump_key(MSG_DEBUG, "PASN: KCK:", ptk->kck, WPA_PASN_KCK_LEN); + + os_memcpy(ptk->tk, tmp + WPA_PASN_KCK_LEN, ptk->tk_len); + wpa_hexdump_key(MSG_DEBUG, "PASN: TK:", ptk->tk, ptk->tk_len); + + if (kdk_len) { + os_memcpy(ptk->kdk, tmp + WPA_PASN_KCK_LEN + ptk->tk_len, + ptk->kdk_len); + wpa_hexdump_key(MSG_DEBUG, "PASN: KDK:", + ptk->kdk, ptk->kdk_len); + } + + forced_memzero(tmp, sizeof(tmp)); + ret = 0; +err: + bin_clear_free(data, data_len); + return ret; +} + + +/* + * pasn_mic_len - Returns the MIC length for PASN authentication + */ +u8 pasn_mic_len(int akmp, int cipher) +{ + if (pasn_use_sha384(akmp, cipher)) + return 24; + + return 16; +} + + +/** + * pasn_mic - Calculate PASN MIC + * @kck: The key confirmation key for the PASN PTKSA + * @akmp: Negotiated AKM + * @cipher: Negotiated pairwise cipher + * @addr1: For the 2nd PASN frame supplicant address; for the 3rd frame the + * BSSID + * @addr2: For the 2nd PASN frame the BSSID; for the 3rd frame the supplicant + * address + * @data: For calculating the MIC for the 2nd PASN frame, this should hold the + * Beacon frame RSNE + RSNXE. For calculating the MIC for the 3rd PASN + * frame, this should hold the hash of the body of the PASN 1st frame. + * @data_len: The length of data + * @frame: The body of the PASN frame including the MIC element with the octets + * in the MIC field of the MIC element set to 0. + * @frame_len: The length of frame + * @mic: Buffer to hold the MIC on success. Should be big enough to handle the + * maximal MIC length + * Returns: 0 on success, -1 on failure + */ +int pasn_mic(const u8 *kck, int akmp, int cipher, + const u8 *addr1, const u8 *addr2, + const u8 *data, size_t data_len, + const u8 *frame, size_t frame_len, u8 *mic) +{ + u8 *buf; + u8 hash[SHA384_MAC_LEN]; + size_t buf_len = 2 * ETH_ALEN + data_len + frame_len; + int ret = -1; + + if (!kck) { + wpa_printf(MSG_ERROR, "PASN: No KCK for MIC calculation"); + return -1; + } + + if (!data || !data_len) { + wpa_printf(MSG_ERROR, "PASN: invalid data for MIC calculation"); + return -1; + } + + if (!frame || !frame_len) { + wpa_printf(MSG_ERROR, "PASN: invalid data for MIC calculation"); + return -1; + } + + buf = os_zalloc(buf_len); + if (!buf) + return -1; + + os_memcpy(buf, addr1, ETH_ALEN); + os_memcpy(buf + ETH_ALEN, addr2, ETH_ALEN); + + wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: data", data, data_len); + os_memcpy(buf + 2 * ETH_ALEN, data, data_len); + + wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: frame", frame, frame_len); + os_memcpy(buf + 2 * ETH_ALEN + data_len, frame, frame_len); + + wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: KCK", kck, WPA_PASN_KCK_LEN); + wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: buf", buf, buf_len); + + if (pasn_use_sha384(akmp, cipher)) { + wpa_printf(MSG_DEBUG, "PASN: MIC using HMAC-SHA384"); + + if (hmac_sha384(kck, WPA_PASN_KCK_LEN, buf, buf_len, hash)) + goto err; + + os_memcpy(mic, hash, 24); + wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: mic: ", mic, 24); + } else { + wpa_printf(MSG_DEBUG, "PASN: MIC using HMAC-SHA256"); + + if (hmac_sha256(kck, WPA_PASN_KCK_LEN, buf, buf_len, hash)) + goto err; + + os_memcpy(mic, hash, 16); + wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: mic: ", mic, 16); + } + + ret = 0; +err: + bin_clear_free(buf, buf_len); + return ret; +} + + +/** + * pasn_auth_frame_hash - Computes a hash of an Authentication frame body + * @akmp: Negotiated AKM + * @cipher: Negotiated pairwise cipher + * @data: Pointer to the Authentication frame body + * @len: Length of the Authentication frame body + * @hash: On return would hold the computed hash. Should be big enough to handle + * SHA384. + * Returns: 0 on success, -1 on failure + */ +int pasn_auth_frame_hash(int akmp, int cipher, const u8 *data, size_t len, + u8 *hash) +{ + if (pasn_use_sha384(akmp, cipher)) { + wpa_printf(MSG_DEBUG, "PASN: Frame hash using SHA-384"); + return sha384_vector(1, &data, &len, hash); + } else { + wpa_printf(MSG_DEBUG, "PASN: Frame hash using SHA-256"); + return sha256_vector(1, &data, &len, hash); + } +} + +#endif /* CONFIG_PASN */ + + static int rsn_selector_to_bitfield(const u8 *s) { if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NONE) @@ -1203,6 +1504,10 @@ static int rsn_key_mgmt_to_bitfield(const u8 *s) #endif /* CONFIG_DPP */ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OSEN) return WPA_KEY_MGMT_OSEN; +#ifdef CONFIG_PASN + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PASN) + return WPA_KEY_MGMT_PASN; +#endif /* CONFIG_PASN */ return 0; } @@ -1216,7 +1521,8 @@ int wpa_cipher_valid_group(int cipher) int wpa_cipher_valid_mgmt_group(int cipher) { - return cipher == WPA_CIPHER_AES_128_CMAC || + return cipher == WPA_CIPHER_GTK_NOT_USED || + cipher == WPA_CIPHER_AES_128_CMAC || cipher == WPA_CIPHER_BIP_GMAC_128 || cipher == WPA_CIPHER_BIP_GMAC_256 || cipher == WPA_CIPHER_BIP_CMAC_256; @@ -1733,16 +2039,25 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len, const u8 *snonce, const u8 *anonce, const u8 *sta_addr, const u8 *bssid, const u8 *pmk_r1_name, - struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher) + struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher, + size_t kdk_len) { u8 buf[2 * WPA_NONCE_LEN + 2 * ETH_ALEN]; u8 *pos, hash[32]; const u8 *addr[6]; size_t len[6]; - u8 tmp[2 * WPA_KCK_MAX_LEN + 2 * WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN]; + u8 tmp[2 * WPA_KCK_MAX_LEN + 2 * WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN + + WPA_KDK_MAX_LEN]; size_t ptk_len, offset; int use_sha384 = wpa_key_mgmt_sha384(akmp); + if (kdk_len > WPA_KDK_MAX_LEN) { + wpa_printf(MSG_ERROR, + "FT: KDK len=%zu exceeds max supported len", + kdk_len); + return -1; + } + /* * PTK = KDF-PTKLen(PMK-R1, "FT-PTK", SNonce || ANonce || * BSSID || STA-ADDR) @@ -1769,8 +2084,9 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len, ptk->kek_len = wpa_kek_len(akmp, PMK_LEN); ptk->kek2_len = wpa_kek2_len(akmp); ptk->tk_len = wpa_cipher_key_len(cipher); + ptk->kdk_len = kdk_len; ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len + - ptk->kck2_len + ptk->kek2_len; + ptk->kck2_len + ptk->kek2_len + ptk->kdk_len; #ifdef CONFIG_SHA384 if (use_sha384) { @@ -1829,6 +2145,8 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len, os_memcpy(ptk->kck2, tmp + offset, ptk->kck2_len); offset += ptk->kck2_len; os_memcpy(ptk->kek2, tmp + offset, ptk->kek2_len); + offset += ptk->kek2_len; + os_memcpy(ptk->kdk, tmp + offset, ptk->kdk_len); wpa_hexdump_key(MSG_DEBUG, "FT: KCK", ptk->kck, ptk->kck_len); wpa_hexdump_key(MSG_DEBUG, "FT: KEK", ptk->kek, ptk->kek_len); @@ -1838,6 +2156,9 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len, if (ptk->kek2_len) wpa_hexdump_key(MSG_DEBUG, "FT: KEK2", ptk->kek2, ptk->kek2_len); + if (ptk->kdk_len) + wpa_hexdump_key(MSG_DEBUG, "FT: KDK", ptk->kdk, ptk->kdk_len); + wpa_hexdump_key(MSG_DEBUG, "FT: TK", ptk->tk, ptk->tk_len); wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); @@ -2071,6 +2392,8 @@ const char * wpa_key_mgmt_txt(int key_mgmt, int proto) return "OWE"; case WPA_KEY_MGMT_DPP: return "DPP"; + case WPA_KEY_MGMT_PASN: + return "PASN"; default: return "UNKNOWN"; } @@ -2942,3 +3265,451 @@ int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie) return ret; } + + +#ifdef CONFIG_PASN + +/* + * wpa_pasn_build_auth_header - Add the MAC header and initialize Authentication + * frame for PASN + * + * @buf: Buffer in which the header will be added + * @bssid: The BSSID of the AP + * @src: Source address + * @dst: Destination address + * @trans_seq: Authentication transaction sequence number + * @status: Authentication status + */ +void wpa_pasn_build_auth_header(struct wpabuf *buf, const u8 *bssid, + const u8 *src, const u8 *dst, + u8 trans_seq, u16 status) +{ + struct ieee80211_mgmt *auth; + + wpa_printf(MSG_DEBUG, "PASN: Add authentication header. trans_seq=%u", + trans_seq); + + auth = wpabuf_put(buf, offsetof(struct ieee80211_mgmt, + u.auth.variable)); + + auth->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_AUTH << 4)); + + os_memcpy(auth->da, dst, ETH_ALEN); + os_memcpy(auth->sa, src, ETH_ALEN); + os_memcpy(auth->bssid, bssid, ETH_ALEN); + auth->seq_ctrl = 0; + + auth->u.auth.auth_alg = host_to_le16(WLAN_AUTH_PASN); + auth->u.auth.auth_transaction = host_to_le16(trans_seq); + auth->u.auth.status_code = host_to_le16(status); +} + + +/* + * wpa_pasn_add_rsne - Add an RSNE for PASN authentication + * @buf: Buffer in which the IE will be added + * @pmkid: Optional PMKID. Can be NULL. + * @akmp: Authentication and key management protocol + * @cipher: The cipher suite + */ +int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid, int akmp, int cipher) +{ + struct rsn_ie_hdr *hdr; + u32 suite; + u16 capab; + u8 *pos; + u8 rsne_len; + + wpa_printf(MSG_DEBUG, "PASN: Add RSNE"); + + rsne_len = sizeof(*hdr) + RSN_SELECTOR_LEN + + 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + + 2 + RSN_SELECTOR_LEN + 2 + (pmkid ? PMKID_LEN : 0); + + if (wpabuf_tailroom(buf) < rsne_len) + return -1; + hdr = wpabuf_put(buf, rsne_len); + hdr->elem_id = WLAN_EID_RSN; + hdr->len = rsne_len - 2; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + pos = (u8 *) (hdr + 1); + + /* Group addressed data is not allowed */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); + pos += RSN_SELECTOR_LEN; + + /* Add the pairwise cipher */ + WPA_PUT_LE16(pos, 1); + pos += 2; + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, cipher); + RSN_SELECTOR_PUT(pos, suite); + pos += RSN_SELECTOR_LEN; + + /* Add the AKM suite */ + WPA_PUT_LE16(pos, 1); + pos += 2; + + switch (akmp) { + case WPA_KEY_MGMT_PASN: + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PASN); + break; +#ifdef CONFIG_SAE + case WPA_KEY_MGMT_SAE: + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE); + break; +#endif /* CONFIG_SAE */ +#ifdef CONFIG_FILS + case WPA_KEY_MGMT_FILS_SHA256: + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256); + break; + case WPA_KEY_MGMT_FILS_SHA384: + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384); + break; +#endif /* CONFIG_FILS */ +#ifdef CONFIG_IEEE80211R + case WPA_KEY_MGMT_FT_PSK: + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); + break; + case WPA_KEY_MGMT_FT_IEEE8021X: + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); + break; + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384); + break; +#endif /* CONFIG_IEEE80211R */ + default: + wpa_printf(MSG_ERROR, "PASN: Invalid AKMP=0x%x", akmp); + return -1; + } + pos += RSN_SELECTOR_LEN; + + /* RSN Capabilities: PASN mandates both MFP capable and required */ + capab = WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR; + WPA_PUT_LE16(pos, capab); + pos += 2; + + if (pmkid) { + wpa_printf(MSG_DEBUG, "PASN: Adding PMKID"); + + WPA_PUT_LE16(pos, 1); + pos += 2; + os_memcpy(pos, pmkid, PMKID_LEN); + pos += PMKID_LEN; + } else { + WPA_PUT_LE16(pos, 0); + pos += 2; + } + + /* Group addressed management is not allowed */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); + + return 0; +} + + +/* + * wpa_pasn_add_parameter_ie - Add PASN Parameters IE for PASN authentication + * @buf: Buffer in which the IE will be added + * @pasn_group: Finite Cyclic Group ID for PASN authentication + * @wrapped_data_format: Format of the data in the Wrapped Data IE + * @pubkey: A buffer holding the local public key. Can be NULL + * @comeback: A buffer holding the comeback token. Can be NULL + * @after: If comeback is set, defined the comeback time in seconds. -1 to not + * include the Comeback After field (frames from non-AP STA). + */ +void wpa_pasn_add_parameter_ie(struct wpabuf *buf, u16 pasn_group, + u8 wrapped_data_format, + struct wpabuf *pubkey, + struct wpabuf *comeback, int after) +{ + struct pasn_parameter_ie *params; + + wpa_printf(MSG_DEBUG, "PASN: Add PASN Parameters element"); + + params = wpabuf_put(buf, sizeof(*params)); + + params->id = WLAN_EID_EXTENSION; + params->len = sizeof(*params) - 2; + params->id_ext = WLAN_EID_EXT_PASN_PARAMS; + params->control = 0; + params->wrapped_data_format = wrapped_data_format; + + if (comeback) { + wpa_printf(MSG_DEBUG, "PASN: Adding comeback data"); + + /* + * 2 octets for the 'after' field + 1 octet for the length + + * actual cookie data + */ + if (after >= 0) + params->len += 2; + params->len += 1 + wpabuf_len(comeback); + params->control |= WPA_PASN_CTRL_COMEBACK_INFO_PRESENT; + + if (after >= 0) + wpabuf_put_le16(buf, after); + wpabuf_put_u8(buf, wpabuf_len(comeback)); + wpabuf_put_buf(buf, comeback); + } + + if (pubkey) { + wpa_printf(MSG_DEBUG, + "PASN: Adding public key and group ID %u", + pasn_group); + + /* + * 2 octets for the finite cyclic group + 2 octets public key + * length + the actual key + */ + params->len += 2 + 1 + wpabuf_len(pubkey); + params->control |= WPA_PASN_CTRL_GROUP_AND_KEY_PRESENT; + + wpabuf_put_le16(buf, pasn_group); + wpabuf_put_u8(buf, wpabuf_len(pubkey)); + wpabuf_put_buf(buf, pubkey); + } +} + +/* + * wpa_pasn_add_wrapped_data - Add a Wrapped Data IE to PASN Authentication + * frame. If needed, the Wrapped Data IE would be fragmented. + * + * @buf: Buffer in which the IE will be added + * @wrapped_data_buf: Buffer holding the wrapped data + */ +int wpa_pasn_add_wrapped_data(struct wpabuf *buf, + struct wpabuf *wrapped_data_buf) +{ + const u8 *data; + size_t data_len; + u8 len; + + if (!wrapped_data_buf) + return 0; + + wpa_printf(MSG_DEBUG, "PASN: Add wrapped data"); + + data = wpabuf_head_u8(wrapped_data_buf); + data_len = wpabuf_len(wrapped_data_buf); + + /* nothing to add */ + if (!data_len) + return 0; + + if (data_len <= 254) + len = 1 + data_len; + else + len = 255; + + if (wpabuf_tailroom(buf) < 3 + data_len) + return -1; + + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, len); + wpabuf_put_u8(buf, WLAN_EID_EXT_WRAPPED_DATA); + wpabuf_put_data(buf, data, len - 1); + + data += len - 1; + data_len -= len - 1; + + while (data_len) { + if (wpabuf_tailroom(buf) < 1 + data_len) + return -1; + wpabuf_put_u8(buf, WLAN_EID_FRAGMENT); + len = data_len > 255 ? 255 : data_len; + wpabuf_put_u8(buf, len); + wpabuf_put_data(buf, data, len); + data += len; + data_len -= len; + } + + return 0; +} + + +/* + * wpa_pasn_validate_rsne - Validate PSAN specific data of RSNE + * @data: Parsed representation of an RSNE + * Returns -1 for invalid data; otherwise 0 + */ +int wpa_pasn_validate_rsne(const struct wpa_ie_data *data) +{ + u16 capab = WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR; + + if (data->proto != WPA_PROTO_RSN) + return -1; + + if ((data->capabilities & capab) != capab) { + wpa_printf(MSG_DEBUG, "PASN: Invalid RSNE capabilities"); + return -1; + } + + if (!data->has_group || data->group_cipher != WPA_CIPHER_GTK_NOT_USED) { + wpa_printf(MSG_DEBUG, "PASN: Invalid group data cipher"); + return -1; + } + + if (!data->has_pairwise || !data->pairwise_cipher || + (data->pairwise_cipher & (data->pairwise_cipher - 1))) { + wpa_printf(MSG_DEBUG, "PASN: No valid pairwise suite"); + return -1; + } + + switch (data->key_mgmt) { +#ifdef CONFIG_SAE + case WPA_KEY_MGMT_SAE: + /* fall through */ +#endif /* CONFIG_SAE */ +#ifdef CONFIG_FILS + case WPA_KEY_MGMT_FILS_SHA256: + case WPA_KEY_MGMT_FILS_SHA384: + /* fall through */ +#endif /* CONFIG_FILS */ +#ifdef CONFIG_IEEE80211R + case WPA_KEY_MGMT_FT_PSK: + case WPA_KEY_MGMT_FT_IEEE8021X: + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: + /* fall through */ +#endif /* CONFIG_IEEE80211R */ + case WPA_KEY_MGMT_PASN: + break; + default: + wpa_printf(MSG_ERROR, "PASN: invalid key_mgmt: 0x%0x", + data->key_mgmt); + return -1; + } + + if (data->mgmt_group_cipher != WPA_CIPHER_GTK_NOT_USED) { + wpa_printf(MSG_DEBUG, "PASN: Invalid group mgmt cipher"); + return -1; + } + + if (data->num_pmkid > 1) { + wpa_printf(MSG_DEBUG, "PASN: Invalid number of PMKIDs"); + return -1; + } + + return 0; +} + + +/* + * wpa_pasn_parse_parameter_ie - Validates PASN Parameters IE + * @data: Pointer to the PASN Parameters IE (starting with the EID). + * @len: Length of the data in the PASN Parameters IE + * @from_ap: Whether this was received from an AP + * @pasn_params: On successful return would hold the parsed PASN parameters. + * Returns: -1 for invalid data; otherwise 0 + * + * Note: On successful return, the pointers in &pasn_params point to the data in + * the IE and are not locally allocated (so they should not be freed etc.). + */ +int wpa_pasn_parse_parameter_ie(const u8 *data, u8 len, bool from_ap, + struct wpa_pasn_params_data *pasn_params) +{ + struct pasn_parameter_ie *params = (struct pasn_parameter_ie *) data; + const u8 *pos = (const u8 *) (params + 1); + + if (!pasn_params) { + wpa_printf(MSG_DEBUG, "PASN: Invalid params"); + return -1; + } + + if (!params || ((size_t) (params->len + 2) < sizeof(*params)) || + len < sizeof(*params) || params->len + 2 != len) { + wpa_printf(MSG_DEBUG, + "PASN: Invalid parameters IE. len=(%u, %u)", + params ? params->len : 0, len); + return -1; + } + + os_memset(pasn_params, 0, sizeof(*pasn_params)); + + switch (params->wrapped_data_format) { + case WPA_PASN_WRAPPED_DATA_NO: + case WPA_PASN_WRAPPED_DATA_SAE: + case WPA_PASN_WRAPPED_DATA_FILS_SK: + case WPA_PASN_WRAPPED_DATA_FT: + break; + default: + wpa_printf(MSG_DEBUG, "PASN: Invalid wrapped data format"); + return -1; + } + + pasn_params->wrapped_data_format = params->wrapped_data_format; + + len -= sizeof(*params); + + if (params->control & WPA_PASN_CTRL_COMEBACK_INFO_PRESENT) { + if (from_ap) { + if (len < 2) { + wpa_printf(MSG_DEBUG, + "PASN: Invalid Parameters IE: Truncated Comeback After"); + return -1; + } + pasn_params->after = WPA_GET_LE16(pos); + pos += 2; + len -= 2; + } + + if (len < 1 || len < 1 + *pos) { + wpa_printf(MSG_DEBUG, + "PASN: Invalid Parameters IE: comeback len"); + return -1; + } + + pasn_params->comeback_len = *pos++; + len--; + pasn_params->comeback = pos; + len -= pasn_params->comeback_len; + pos += pasn_params->comeback_len; + } + + if (params->control & WPA_PASN_CTRL_GROUP_AND_KEY_PRESENT) { + if (len < 3 || len < 3 + pos[2]) { + wpa_printf(MSG_DEBUG, + "PASN: Invalid Parameters IE: group and key"); + return -1; + } + + pasn_params->group = WPA_GET_LE16(pos); + pos += 2; + len -= 2; + pasn_params->pubkey_len = *pos++; + len--; + pasn_params->pubkey = pos; + len -= pasn_params->pubkey_len; + pos += pasn_params->pubkey_len; + } + + if (len) { + wpa_printf(MSG_DEBUG, + "PASN: Invalid Parameters IE. Bytes left=%u", len); + return -1; + } + + return 0; +} + + +void wpa_pasn_add_rsnxe(struct wpabuf *buf, u16 capab) +{ + size_t flen; + + flen = (capab & 0xff00) ? 2 : 1; + if (!capab) + return; /* no supported extended RSN capabilities */ + if (wpabuf_tailroom(buf) < 2 + flen) + return; + capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */ + + wpabuf_put_u8(buf, WLAN_EID_RSNX); + wpabuf_put_u8(buf, flen); + wpabuf_put_u8(buf, capab & 0x00ff); + capab >>= 8; + if (capab) + wpabuf_put_u8(buf, capab); +} + +#endif /* CONFIG_PASN */ diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index 065dc71..c31e1a0 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -19,6 +19,9 @@ #define WPA_KEY_RSC_LEN 8 #define WPA_GMK_LEN 32 #define WPA_GTK_MAX_LEN 32 +#define WPA_PASN_PMK_LEN 32 +#define WPA_PASN_MAX_MIC_LEN 24 +#define WPA_MAX_RSNXE_LEN 4 #define OWE_DH_GROUP 19 @@ -78,6 +81,9 @@ WPA_CIPHER_BIP_CMAC_256) #define RSN_AUTH_KEY_MGMT_FT_FILS_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 16) #define RSN_AUTH_KEY_MGMT_FT_FILS_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 17) #define RSN_AUTH_KEY_MGMT_OWE RSN_SELECTOR(0x00, 0x0f, 0xac, 18) + +#define RSN_AUTH_KEY_MGMT_PASN RSN_SELECTOR(0x00, 0x0f, 0xac, 21) + #define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00) #define RSN_AUTH_KEY_MGMT_OSEN RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x01) #define RSN_AUTH_KEY_MGMT_DPP RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x02) @@ -211,8 +217,11 @@ struct wpa_eapol_key { #define WPA_KCK_MAX_LEN 32 #define WPA_KEK_MAX_LEN 64 #define WPA_TK_MAX_LEN 32 +#define WPA_KDK_MAX_LEN 32 #define FILS_ICK_MAX_LEN 48 #define FILS_FT_MAX_LEN 48 +#define WPA_PASN_KCK_LEN 32 +#define WPA_PASN_MIC_MAX_LEN 24 /** * struct wpa_ptk - WPA Pairwise Transient Key @@ -224,11 +233,13 @@ struct wpa_ptk { u8 tk[WPA_TK_MAX_LEN]; /* Temporal Key (TK) */ u8 kck2[WPA_KCK_MAX_LEN]; /* FT reasoc Key Confirmation Key (KCK2) */ u8 kek2[WPA_KEK_MAX_LEN]; /* FT reassoc Key Encryption Key (KEK2) */ + u8 kdk[WPA_KDK_MAX_LEN]; /* Key Derivation Key */ size_t kck_len; size_t kek_len; size_t tk_len; size_t kck2_len; size_t kek2_len; + size_t kdk_len; int installed; /* 1 if key has already been installed to driver */ }; @@ -378,7 +389,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, const u8 *addr1, const u8 *addr2, const u8 *nonce1, const u8 *nonce2, struct wpa_ptk *ptk, int akmp, int cipher, - const u8 *z, size_t z_len); + const u8 *z, size_t z_len, size_t kdk_len); int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len, const u8 *snonce, const u8 *anonce, const u8 *dh_ss, size_t dh_ss_len, u8 *pmk, size_t *pmk_len); @@ -388,7 +399,7 @@ int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa, const u8 *snonce, const u8 *anonce, const u8 *dhss, size_t dhss_len, struct wpa_ptk *ptk, u8 *ick, size_t *ick_len, int akmp, int cipher, - u8 *fils_ft, size_t *fils_ft_len); + u8 *fils_ft, size_t *fils_ft_len, size_t kdk_len); int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce, const u8 *anonce, const u8 *sta_addr, const u8 *bssid, const u8 *g_sta, size_t g_sta_len, @@ -419,7 +430,8 @@ int wpa_derive_pmk_r1(const u8 *pmk_r0, size_t pmk_r0_len, int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len, const u8 *snonce, const u8 *anonce, const u8 *sta_addr, const u8 *bssid, const u8 *pmk_r1_name, - struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher); + struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher, + size_t kdk_len); #endif /* CONFIG_IEEE80211R */ struct wpa_ie_data { @@ -507,6 +519,33 @@ struct wpa_ft_ies { size_t rsnxe_len; }; +/* IEEE P802.11az/D2.6 - 9.4.2.303 PASN Parameters element */ +#define WPA_PASN_CTRL_COMEBACK_INFO_PRESENT BIT(0) +#define WPA_PASN_CTRL_GROUP_AND_KEY_PRESENT BIT(1) + +#define WPA_PASN_WRAPPED_DATA_NO 0 +#define WPA_PASN_WRAPPED_DATA_FT 1 +#define WPA_PASN_WRAPPED_DATA_FILS_SK 2 +#define WPA_PASN_WRAPPED_DATA_SAE 3 + +struct pasn_parameter_ie { + u8 id; + u8 len; + u8 id_ext; + u8 control; /* WPA_PASN_CTRL_* */ + u8 wrapped_data_format; /* WPA_PASN_WRAPPED_DATA_* */ +} STRUCT_PACKED; + +struct wpa_pasn_params_data { + u8 wrapped_data_format; + u16 after; + u8 comeback_len; + const u8 *comeback; + u16 group; + u8 pubkey_len; + const u8 *pubkey; +}; + int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse, int use_sha384); @@ -591,4 +630,41 @@ int wpa_use_cmac(int akmp); int wpa_use_aes_key_wrap(int akmp); int fils_domain_name_hash(const char *domain, u8 *hash); +int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len, + const u8 *spa, const u8 *bssid, + const u8 *dhss, size_t dhss_len, + struct wpa_ptk *ptk, int akmp, int cipher, + size_t kdk_len); + +u8 pasn_mic_len(int akmp, int cipher); + +int pasn_mic(const u8 *kck, int akmp, int cipher, + const u8 *addr1, const u8 *addr2, + const u8 *data, size_t data_len, + const u8 *frame, size_t frame_len, u8 *mic); + +int pasn_auth_frame_hash(int akmp, int cipher, const u8 *data, size_t len, + u8 *hash); + +void wpa_pasn_build_auth_header(struct wpabuf *buf, const u8 *bssid, + const u8 *src, const u8 *dst, + u8 trans_seq, u16 status); + +int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid, + int akmp, int cipher); + +void wpa_pasn_add_parameter_ie(struct wpabuf *buf, u16 pasn_group, + u8 wrapped_data_format, + struct wpabuf *pubkey, + struct wpabuf *comeback, int after); + +int wpa_pasn_add_wrapped_data(struct wpabuf *buf, + struct wpabuf *wrapped_data_buf); + +int wpa_pasn_validate_rsne(const struct wpa_ie_data *data); +int wpa_pasn_parse_parameter_ie(const u8 *data, u8 len, bool from_ap, + struct wpa_pasn_params_data *pasn_params); + +void wpa_pasn_add_rsnxe(struct wpabuf *buf, u16 capab); + #endif /* WPA_COMMON_H */ diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index acc2d6c..126a789 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -271,7 +271,7 @@ extern "C" { #define P2P_EVENT_P2PS_PROVISION_DONE "P2PS-PROV-DONE " #define INTERWORKING_AP "INTERWORKING-AP " -#define INTERWORKING_BLACKLISTED "INTERWORKING-BLACKLISTED " +#define INTERWORKING_EXCLUDED "INTERWORKING-BLACKLISTED " #define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH " #define INTERWORKING_ALREADY_CONNECTED "INTERWORKING-ALREADY-CONNECTED " #define INTERWORKING_SELECTED "INTERWORKING-SELECTED " @@ -404,10 +404,16 @@ extern "C" { * frame=<saqueryreq/saqueryresp> error=<error string> */ #define OCV_FAILURE "OCV-FAILURE " +/* Event triggered for received management frame */ +#define AP_MGMT_FRAME_RECEIVED "AP-MGMT-FRAME-RECEIVED " + #ifndef BIT #define BIT(x) (1U << (x)) #endif +/* PASN authentication status */ +#define PASN_AUTH_STATUS "PASN-AUTH-STATUS " + /* BSS command information masks */ #define WPA_BSS_MASK_ALL 0xFFFDFFFF diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index ef872c5..345a35e 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -1045,6 +1045,8 @@ void * tls_init(const struct tls_config *conf) SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2); SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3); + SSL_CTX_set_mode(ssl, SSL_MODE_AUTO_RETRY); + #ifdef SSL_MODE_NO_AUTO_CHAIN /* Number of deployed use cases assume the default OpenSSL behavior of * auto chaining the local certificate is in use. BoringSSL removed this @@ -4543,10 +4545,18 @@ struct wpabuf * tls_connection_decrypt(void *tls_ctx, return NULL; res = SSL_read(conn->ssl, wpabuf_mhead(buf), wpabuf_size(buf)); if (res < 0) { - tls_show_errors(MSG_INFO, __func__, - "Decryption failed - SSL_read"); - wpabuf_free(buf); - return NULL; + int err = SSL_get_error(conn->ssl, res); + + if (err == SSL_ERROR_WANT_READ) { + wpa_printf(MSG_DEBUG, + "SSL: SSL_connect - want more data"); + res = 0; + } else { + tls_show_errors(MSG_INFO, __func__, + "Decryption failed - SSL_read"); + wpabuf_free(buf); + return NULL; + } } wpabuf_put(buf, res); diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c index b8a7665..cf482bf 100644 --- a/src/crypto/tls_wolfssl.c +++ b/src/crypto/tls_wolfssl.c @@ -469,7 +469,7 @@ static int tls_connection_client_cert(struct tls_connection *conn, if (client_cert_blob) { if (wolfSSL_use_certificate_chain_buffer_format( conn->ssl, client_cert_blob, blob_len, - SSL_FILETYPE_ASN1) < 0) { + SSL_FILETYPE_ASN1) != SSL_SUCCESS) { wpa_printf(MSG_INFO, "SSL: use client cert DER blob failed"); return -1; @@ -479,13 +479,13 @@ static int tls_connection_client_cert(struct tls_connection *conn, } if (client_cert) { - if (wolfSSL_use_certificate_chain_file(conn->ssl, - client_cert) < 0) { + if (wolfSSL_use_certificate_chain_file( + conn->ssl, client_cert) != SSL_SUCCESS) { wpa_printf(MSG_INFO, "SSL: use client cert PEM file failed"); if (wolfSSL_use_certificate_chain_file_format( conn->ssl, client_cert, - SSL_FILETYPE_ASN1) < 0) { + SSL_FILETYPE_ASN1) != SSL_SUCCESS) { wpa_printf(MSG_INFO, "SSL: use client cert DER file failed"); return -1; @@ -534,7 +534,7 @@ static int tls_connection_private_key(void *tls_ctx, if (private_key_blob) { if (wolfSSL_use_PrivateKey_buffer(conn->ssl, private_key_blob, blob_len, - SSL_FILETYPE_ASN1) < 0) { + SSL_FILETYPE_ASN1) <= 0) { wpa_printf(MSG_INFO, "SSL: use private DER blob failed"); } else { @@ -545,11 +545,11 @@ static int tls_connection_private_key(void *tls_ctx, if (!ok && private_key) { if (wolfSSL_use_PrivateKey_file(conn->ssl, private_key, - SSL_FILETYPE_PEM) < 0) { + SSL_FILETYPE_PEM) <= 0) { wpa_printf(MSG_INFO, "SSL: use private key PEM file failed"); if (wolfSSL_use_PrivateKey_file(conn->ssl, private_key, - SSL_FILETYPE_ASN1) < 0) + SSL_FILETYPE_ASN1) <= 0) { wpa_printf(MSG_INFO, "SSL: use private key DER file failed"); diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 722e428..5b2c71c 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -1264,14 +1264,14 @@ struct wpa_driver_ap_params { * * This parameter can be used to set a specific Beacon frame data rate * for the BSS. The interpretation of this value depends on the - * rate_type (legacy: in 100 kbps units, HT: HT-MCS, VHT: VHT-MCS). If - * beacon_rate == 0 and rate_type == 0 (BEACON_RATE_LEGACY), the default - * Beacon frame data rate is used. + * rate_type (legacy: in 100 kbps units, HT: HT-MCS, VHT: VHT-MCS, + * HE: HE-MCS). If beacon_rate == 0 and rate_type == 0 + * (BEACON_RATE_LEGACY), the default Beacon frame data rate is used. */ unsigned int beacon_rate; /** - * beacon_rate_type: Beacon data rate type (legacy/HT/VHT) + * beacon_rate_type: Beacon data rate type (legacy/HT/VHT/HE) */ enum beacon_rate_type rate_type; @@ -1483,19 +1483,36 @@ struct wpa_driver_ap_params { const struct wpabuf *civic; /** - * he_spr - Whether Spatial Reuse is enabled + * he_spr_ctrl - Spatial Reuse control field of SPR element */ - int he_spr; + u8 he_spr_ctrl; + + /** + * he_spr_non_srg_obss_pd_max_offset - Non-SRG Maximum TX power offset + */ + u8 he_spr_non_srg_obss_pd_max_offset; /** * he_spr_srg_obss_pd_min_offset - Minimum TX power offset */ - int he_spr_srg_obss_pd_min_offset; + u8 he_spr_srg_obss_pd_min_offset; /** * he_spr_srg_obss_pd_max_offset - Maximum TX power offset */ - int he_spr_srg_obss_pd_max_offset; + u8 he_spr_srg_obss_pd_max_offset; + + /** + * he_spr_bss_color_bitmap - BSS color values used by members of the + * SRG. + */ + u8 he_spr_bss_color_bitmap[8]; + + /** + * he_spr_partial_bssid_bitmap - Partial BSSID values used by members + * of the SRG. + */ + u8 he_spr_partial_bssid_bitmap[8]; /** * he_bss_color - Whether the BSS Color is disabled @@ -1524,6 +1541,41 @@ struct wpa_driver_ap_params { * 2 = both hunting-and-pecking loop and hash-to-element enabled */ int sae_pwe; + + /** + * FILS Discovery frame minimum interval in TUs + */ + u32 fd_min_int; + + /** + * FILS Discovery frame maximum interval in TUs (0 = FD frame disabled) + */ + u32 fd_max_int; + + /** + * FILS Discovery frame template data + */ + u8 *fd_frame_tmpl; + + /** + * FILS Discovery frame template length + */ + size_t fd_frame_tmpl_len; + + /** + * Unsolicited broadcast Probe Response interval in TUs + */ + unsigned int unsol_bcast_probe_resp_interval; + + /** + * Unsolicited broadcast Probe Response template data + */ + u8 *unsol_bcast_probe_resp_tmpl; + + /** + * Unsolicited broadcast Probe Response template length + */ + size_t unsol_bcast_probe_resp_tmpl_len; }; struct wpa_driver_mesh_bss_params { @@ -1559,6 +1611,7 @@ struct wpa_driver_mesh_join_params { #define WPA_DRIVER_MESH_FLAG_SAE_AUTH 0x00000004 #define WPA_DRIVER_MESH_FLAG_AMPE 0x00000008 unsigned int flags; + bool handle_dfs; }; struct wpa_driver_set_key_params { @@ -1949,6 +2002,23 @@ struct wpa_driver_capa { #define WPA_DRIVER_FLAGS2_CONTROL_PORT_RX 0x0000000000000001ULL /** Driver supports TX status reports for EAPOL frames through control port */ #define WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS 0x0000000000000002ULL +/** Driver supports secure LTF */ +#define WPA_DRIVER_FLAGS2_SEC_LTF 0x0000000000000004ULL +/** Driver supports secure RTT measurement exchange */ +#define WPA_DRIVER_FLAGS2_SEC_RTT 0x0000000000000008ULL +/** + * Driver supports protection of range negotiation and measurement management + * frames + */ +#define WPA_DRIVER_FLAGS2_PROT_RANGE_NEG 0x0000000000000010ULL +/** Driver supports Beacon frame TX rate configuration (HE rates) */ +#define WPA_DRIVER_FLAGS2_BEACON_RATE_HE 0x0000000000000020ULL +/** Driver supports Beacon protection only in client mode */ +#define WPA_DRIVER_FLAGS2_BEACON_PROTECTION_CLIENT 0x0000000000000040ULL +/** Driver supports Operating Channel Validation */ +#define WPA_DRIVER_FLAGS2_OCV 0x0000000000000080ULL +/** Driver expects user space implementation of SME in AP mode */ +#define WPA_DRIVER_FLAGS2_AP_SME 0x0000000000000100ULL u64 flags2; #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \ @@ -4276,12 +4346,12 @@ struct wpa_driver_ops { int (*do_acs)(void *priv, struct drv_acs_params *params); /** - * set_band - Notify driver of band selection + * set_band - Notify driver of band(s) selection * @priv: Private driver interface data - * @band: The selected band(s) + * @band_mask: The selected band(s) bit mask (from enum set_band) * Returns 0 on success, -1 on failure */ - int (*set_band)(void *priv, enum set_band band); + int (*set_band)(void *priv, u32 band_mask); /** * get_pref_freq_list - Get preferred frequency list for an interface diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c index a29d2c8..00d970a 100644 --- a/src/drivers/driver_bsd.c +++ b/src/drivers/driver_bsd.c @@ -16,7 +16,9 @@ #include "common/ieee802_11_defs.h" #include "common/wpa_common.h" +#include <ifaddrs.h> #include <net/if.h> +#include <net/if_dl.h> #include <net/if_media.h> #ifdef __NetBSD__ @@ -615,6 +617,108 @@ bsd_set_opt_ie(void *priv, const u8 *ie, size_t ie_len) return 0; } +#ifdef SO_RERROR +static void +bsd_route_overflow(int sock, void *ctx, struct bsd_driver_global *global) +{ + char event_buf[2048]; /* max size of a single route(4) msg */ + int n; + struct ifaddrs *ifaddrs, *ifa; + struct bsd_driver_data *drv; + struct sockaddr_dl *sdl; + union wpa_event_data event; + + /* We need to match the system state, so drain the route + * socket to avoid stale messages. */ + do { + n = read(sock, event_buf, sizeof(event_buf)); + } while (n != -1 || errno == ENOBUFS); + + if (getifaddrs(&ifaddrs) == -1) { + wpa_printf(MSG_ERROR, "%s getifaddrs() failed: %s", + __func__, strerror(errno)); + return; + } + + /* add or update existing interfaces */ + for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL || + ifa->ifa_addr->sa_family != AF_LINK) + continue; + sdl = (struct sockaddr_dl *) (void *) ifa->ifa_addr; + drv = bsd_get_drvname(global, ifa->ifa_name); + if (drv != NULL && + (drv->ifindex != sdl->sdl_index || drv->if_removed)) { + wpa_printf(MSG_DEBUG, + "RTM_IFANNOUNCE: Interface '%s' added", + drv->ifname); + drv->ifindex = sdl->sdl_index; + drv->if_removed = 0; + event.interface_status.ievent = EVENT_INTERFACE_ADDED; + os_strlcpy(event.interface_status.ifname, ifa->ifa_name, + sizeof(event.interface_status.ifname)); + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, + &event); + } + if (!drv && + (drv = bsd_get_drvindex(global, sdl->sdl_index)) != NULL) { + /* Driver name is invalid */ + wpa_printf(MSG_DEBUG, + "RTM_IFANNOUNCE: Interface '%s' removed", + drv->ifname); + drv->if_removed = 1; + event.interface_status.ievent = EVENT_INTERFACE_REMOVED; + os_strlcpy(event.interface_status.ifname, drv->ifname, + sizeof(event.interface_status.ifname)); + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, + &event); + } + } + + /* punt missing interfaces and update flags */ + dl_list_for_each(drv, &global->ifaces, struct bsd_driver_data, list) { + for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL || + ifa->ifa_addr->sa_family != AF_LINK) + continue; + sdl = (struct sockaddr_dl *) (void *) ifa->ifa_addr; + if (os_strcmp(drv->ifname, ifa->ifa_name) == 0) + break; + } + if (ifa == NULL && !drv->if_removed) { + wpa_printf(MSG_DEBUG, + "RTM_IFANNOUNCE: Interface '%s' removed", + drv->ifname); + drv->if_removed = 1; + event.interface_status.ievent = EVENT_INTERFACE_REMOVED; + os_strlcpy(event.interface_status.ifname, drv->ifname, + sizeof(event.interface_status.ifname)); + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, + &event); + } + if (!ifa) + continue; + + if ((ifa->ifa_flags & IFF_UP) == 0 && + (drv->flags & IFF_UP) != 0) { + wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN", + drv->ifname); + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, + NULL); + } else if ((ifa->ifa_flags & IFF_UP) != 0 && + (drv->flags & IFF_UP) == 0) { + wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' UP", + drv->ifname); + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, + NULL); + } + drv->flags = ifa->ifa_flags; + } + + freeifaddrs(ifaddrs); +} +#endif /* SO_RERROR */ + static void bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx) { @@ -635,6 +739,10 @@ bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx) if (errno != EINTR && errno != EAGAIN) wpa_printf(MSG_ERROR, "%s read() failed: %s", __func__, strerror(errno)); +#ifdef SO_RERROR + if (errno == ENOBUFS) + bsd_route_overflow(sock, ctx, sock_ctx); +#endif /* SO_RERROR */ return; } @@ -1568,14 +1676,15 @@ bsd_global_init(void *ctx) global->ctx = ctx; dl_list_init(&global->ifaces); - global->sock = socket(PF_INET, SOCK_DGRAM, 0); + global->sock = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (global->sock < 0) { wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s", strerror(errno)); goto fail1; } - global->route = socket(PF_ROUTE, SOCK_RAW, 0); + global->route = socket(PF_ROUTE, + SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); if (global->route < 0) { wpa_printf(MSG_ERROR, "socket[PF_ROUTE,SOCK_RAW]: %s", strerror(errno)); diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 94bf982..8342eb8 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -467,8 +467,8 @@ static int send_and_recv(struct nl80211_global *global, } out: nl_cb_put(cb); - if (!valid_handler && valid_data == (void *) -1) - nl80211_nlmsg_clear(msg); + /* Always clear the message as it can potentially contain keys */ + nl80211_nlmsg_clear(msg); nlmsg_free(msg); return err; } @@ -675,6 +675,7 @@ struct wiphy_idx_data { int wiphy_idx; enum nl80211_iftype nlmode; u8 *macaddr; + u8 use_4addr; }; @@ -697,6 +698,9 @@ static int netdev_info_handler(struct nl_msg *msg, void *arg) os_memcpy(info->macaddr, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + if (tb[NL80211_ATTR_4ADDR]) + info->use_4addr = nla_get_u8(tb[NL80211_ATTR_4ADDR]); + return NL_SKIP; } @@ -752,6 +756,21 @@ static int nl80211_get_macaddr(struct i802_bss *bss) } +static int nl80211_get_4addr(struct i802_bss *bss) +{ + struct nl_msg *msg; + struct wiphy_idx_data data = { + .use_4addr = 0, + }; + + if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)) || + send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data, + NULL, NULL)) + return -1; + return data.use_4addr; +} + + static int nl80211_register_beacons(struct wpa_driver_nl80211_data *drv, struct nl80211_wiphy_data *w) { @@ -2326,6 +2345,14 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) (u8 *) "\x03\x00", 2, false); } +#ifdef CONFIG_PASN + /* register for PASN Authentication frames */ + if ((drv->capa.flags & WPA_DRIVER_FLAGS_SME) && + nl80211_register_frame(bss, bss->nl_mgmt, type, + (u8 *) "\x07\x00", 2, false)) + ret = -1; +#endif /* CONFIG_PASN */ + #ifdef CONFIG_INTERWORKING /* QoS Map Configure */ if (nl80211_register_action_frame(bss, (u8 *) "\x01\x04", 2) < 0) @@ -2975,6 +3002,7 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) os_free(drv->filter_ssids); os_free(drv->auth_ie); + os_free(drv->auth_data); if (drv->in_interface_list) dl_list_del(&drv->list); @@ -2986,6 +3014,9 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) os_free(drv->iface_ext_capa[i].ext_capa_mask); } os_free(drv->first_bss); +#ifdef CONFIG_DRIVER_NL80211_QCA + os_free(drv->pending_roam_data); +#endif /* CONFIG_DRIVER_NL80211_QCA */ os_free(drv); } @@ -3133,7 +3164,7 @@ static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv, nlmsg_free(msg); return -1; } - ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1, NULL, NULL); + ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL); if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Key management set key failed: ret=%d (%s)", @@ -3173,7 +3204,7 @@ static int nl80211_set_pmk(struct wpa_driver_nl80211_data *drv, return -ENOBUFS; } - ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1, NULL, NULL); + ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL); if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Set PMK failed: ret=%d (%s)", ret, strerror(-ret)); @@ -3339,8 +3370,7 @@ static int wpa_driver_nl80211_set_key(struct i802_bss *bss, goto fail; } - ret = send_and_recv_msgs(drv, msg, NULL, key ? (void *) -1 : NULL, - NULL, NULL); + ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL); if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE) ret = 0; if (ret) @@ -3625,6 +3655,16 @@ static void nl80211_copy_auth_params(struct wpa_driver_nl80211_data *drv, } } + os_free(drv->auth_data); + drv->auth_data = NULL; + drv->auth_data_len = 0; + if (params->auth_data) { + drv->auth_data = os_memdup(params->auth_data, + params->auth_data_len); + if (drv->auth_data) + drv->auth_data_len = params->auth_data_len; + } + for (i = 0; i < 4; i++) { if (params->wep_key[i] && params->wep_key_len[i] && params->wep_key_len[i] <= 16) { @@ -3878,6 +3918,8 @@ int wpa_driver_nl80211_authenticate_retry(struct wpa_driver_nl80211_data *drv) params.ie = drv->auth_ie; params.ie_len = drv->auth_ie_len; + params.auth_data = drv->auth_data; + params.auth_data_len = drv->auth_data_len; for (i = 0; i < 4; i++) { if (drv->auth_wep_key_len[i]) { @@ -3959,15 +4001,26 @@ static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data, encrypt = 0; } - if (freq == 0 && drv->nlmode == NL80211_IFTYPE_STATION && - (drv->capa.flags & WPA_DRIVER_FLAGS_SAE) && - !(drv->capa.flags & WPA_DRIVER_FLAGS_SME) && + if (is_sta_interface(drv->nlmode) && WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) { - freq = nl80211_get_assoc_freq(drv); - wpa_printf(MSG_DEBUG, - "nl80211: send_mlme - Use assoc_freq=%u for external auth", - freq); + if (freq == 0 && + (drv->capa.flags & WPA_DRIVER_FLAGS_SAE) && + !(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) { + freq = nl80211_get_assoc_freq(drv); + wpa_printf(MSG_DEBUG, + "nl80211: send_mlme - Use assoc_freq=%u for external auth", + freq); + } + + /* Allow off channel for PASN authentication */ + if (data_len >= IEEE80211_HDRLEN + 2 && + WPA_GET_LE16(data + IEEE80211_HDRLEN) == WLAN_AUTH_PASN && + !offchanok) { + wpa_printf(MSG_DEBUG, + "nl80211: send_mlme: allow off channel for PASN"); + offchanok = 1; + } } if (freq == 0 && drv->nlmode == NL80211_IFTYPE_ADHOC) { @@ -4163,11 +4216,12 @@ static int nl80211_set_mesh_config(void *priv, #endif /* CONFIG_MESH */ -static int nl80211_put_beacon_rate(struct nl_msg *msg, const u64 flags, +static int nl80211_put_beacon_rate(struct nl_msg *msg, u64 flags, u64 flags2, struct wpa_driver_ap_params *params) { struct nlattr *bands, *band; struct nl80211_txrate_vht vht_rate; + struct nl80211_txrate_he he_rate; if (!params->freq || (params->beacon_rate == 0 && @@ -4184,7 +4238,10 @@ static int nl80211_put_beacon_rate(struct nl_msg *msg, const u64 flags, band = nla_nest_start(msg, NL80211_BAND_2GHZ); break; case HOSTAPD_MODE_IEEE80211A: - band = nla_nest_start(msg, NL80211_BAND_5GHZ); + if (is_6ghz_freq(params->freq->freq)) + band = nla_nest_start(msg, NL80211_BAND_6GHZ); + else + band = nla_nest_start(msg, NL80211_BAND_5GHZ); break; case HOSTAPD_MODE_IEEE80211AD: band = nla_nest_start(msg, NL80211_BAND_60GHZ); @@ -4197,6 +4254,7 @@ static int nl80211_put_beacon_rate(struct nl_msg *msg, const u64 flags, return -1; os_memset(&vht_rate, 0, sizeof(vht_rate)); + os_memset(&he_rate, 0, sizeof(he_rate)); switch (params->rate_type) { case BEACON_RATE_LEGACY: @@ -4249,6 +4307,22 @@ static int nl80211_put_beacon_rate(struct nl_msg *msg, const u64 flags, wpa_printf(MSG_DEBUG, " * beacon_rate = VHT-MCS %u", params->beacon_rate); break; + case BEACON_RATE_HE: + if (!(flags2 & WPA_DRIVER_FLAGS2_BEACON_RATE_HE)) { + wpa_printf(MSG_INFO, + "nl80211: Driver does not support setting Beacon frame rate (HE)"); + return -1; + } + he_rate.mcs[0] = BIT(params->beacon_rate); + if (nla_put(msg, NL80211_TXRATE_LEGACY, 0, NULL) || + nla_put(msg, NL80211_TXRATE_HT, 0, NULL) || + nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate), + &vht_rate) || + nla_put(msg, NL80211_TXRATE_HE, sizeof(he_rate), &he_rate)) + return -1; + wpa_printf(MSG_DEBUG, " * beacon_rate = HE-MCS %u", + params->beacon_rate); + break; } nla_nest_end(msg, band); @@ -4328,6 +4402,69 @@ static int nl80211_put_sae_pwe(struct nl_msg *msg, int pwe) #endif /* CONFIG_SAE */ +#ifdef CONFIG_FILS +static int nl80211_fils_discovery(struct i802_bss *bss, struct nl_msg *msg, + struct wpa_driver_ap_params *params) +{ + struct nlattr *attr; + + if (!bss->drv->fils_discovery) { + wpa_printf(MSG_ERROR, + "nl80211: Driver does not support FILS Discovery frame transmission for %s", + bss->ifname); + return -1; + } + + attr = nla_nest_start(msg, NL80211_ATTR_FILS_DISCOVERY); + if (!attr || + nla_put_u32(msg, NL80211_FILS_DISCOVERY_ATTR_INT_MIN, + params->fd_min_int) || + nla_put_u32(msg, NL80211_FILS_DISCOVERY_ATTR_INT_MAX, + params->fd_max_int) || + (params->fd_frame_tmpl && + nla_put(msg, NL80211_FILS_DISCOVERY_ATTR_TMPL, + params->fd_frame_tmpl_len, params->fd_frame_tmpl))) + return -1; + + nla_nest_end(msg, attr); + return 0; +} +#endif /* CONFIG_FILS */ + + +#ifdef CONFIG_IEEE80211AX +static int nl80211_unsol_bcast_probe_resp(struct i802_bss *bss, + struct nl_msg *msg, + struct wpa_driver_ap_params *params) +{ + struct nlattr *attr; + + if (!bss->drv->unsol_bcast_probe_resp) { + wpa_printf(MSG_ERROR, + "nl80211: Driver does not support unsolicited broadcast Probe Response frame transmission for %s", + bss->ifname); + return -1; + } + + wpa_printf(MSG_DEBUG, + "nl80211: Unsolicited broadcast Probe Response frame interval: %u", + params->unsol_bcast_probe_resp_interval); + attr = nla_nest_start(msg, NL80211_ATTR_UNSOL_BCAST_PROBE_RESP); + if (!attr || + nla_put_u32(msg, NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT, + params->unsol_bcast_probe_resp_interval) || + (params->unsol_bcast_probe_resp_tmpl && + nla_put(msg, NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL, + params->unsol_bcast_probe_resp_tmpl_len, + params->unsol_bcast_probe_resp_tmpl))) + return -1; + + nla_nest_end(msg, attr); + return 0; +} +#endif /* CONFIG_IEEE80211AX */ + + static int wpa_driver_nl80211_set_ap(void *priv, struct wpa_driver_ap_params *params) { @@ -4371,7 +4508,8 @@ static int wpa_driver_nl80211_set_ap(void *priv, nla_put(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len, params->tail) || nl80211_put_beacon_int(msg, params->beacon_int) || - nl80211_put_beacon_rate(msg, drv->capa.flags, params) || + nl80211_put_beacon_rate(msg, drv->capa.flags, drv->capa.flags2, + params) || nl80211_put_dtim_period(msg, params->dtim_period) || nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) goto fail; @@ -4549,17 +4687,34 @@ static int wpa_driver_nl80211_set_ap(void *priv, } #ifdef CONFIG_IEEE80211AX - if (params->he_spr) { + if (params->he_spr_ctrl) { struct nlattr *spr; spr = nla_nest_start(msg, NL80211_ATTR_HE_OBSS_PD); - wpa_printf(MSG_DEBUG, "nl80211: he_spr=%d", params->he_spr); + wpa_printf(MSG_DEBUG, "nl80211: he_spr_ctrl=0x%x", + params->he_spr_ctrl); if (!spr || - nla_put_u8(msg, NL80211_HE_OBSS_PD_ATTR_MIN_OFFSET, - params->he_spr_srg_obss_pd_min_offset) || - nla_put_u8(msg, NL80211_HE_OBSS_PD_ATTR_MAX_OFFSET, - params->he_spr_srg_obss_pd_max_offset)) + nla_put_u8(msg, NL80211_HE_OBSS_PD_ATTR_SR_CTRL, + params->he_spr_ctrl) || + ((params->he_spr_ctrl & + SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT) && + nla_put_u8(msg, NL80211_HE_OBSS_PD_ATTR_NON_SRG_MAX_OFFSET, + params->he_spr_non_srg_obss_pd_max_offset))) + goto fail; + + if ((params->he_spr_ctrl & + SPATIAL_REUSE_SRG_INFORMATION_PRESENT) && + (nla_put_u8(msg, NL80211_HE_OBSS_PD_ATTR_MIN_OFFSET, + params->he_spr_srg_obss_pd_min_offset) || + nla_put_u8(msg, NL80211_HE_OBSS_PD_ATTR_MAX_OFFSET, + params->he_spr_srg_obss_pd_max_offset) || + nla_put(msg, NL80211_HE_OBSS_PD_ATTR_BSS_COLOR_BITMAP, + sizeof(params->he_spr_bss_color_bitmap), + params->he_spr_bss_color_bitmap) || + nla_put(msg, NL80211_HE_OBSS_PD_ATTR_PARTIAL_BSSID_BITMAP, + sizeof(params->he_spr_partial_bssid_bitmap), + params->he_spr_partial_bssid_bitmap))) goto fail; nla_nest_end(msg, spr); @@ -4586,6 +4741,10 @@ static int wpa_driver_nl80211_set_ap(void *priv, if (nla_put_flag(msg, NL80211_ATTR_TWT_RESPONDER)) goto fail; } + + if (params->unsol_bcast_probe_resp_interval && + nl80211_unsol_bcast_probe_resp(bss, msg, params) < 0) + goto fail; #endif /* CONFIG_IEEE80211AX */ #ifdef CONFIG_SAE @@ -4595,6 +4754,11 @@ static int wpa_driver_nl80211_set_ap(void *priv, goto fail; #endif /* CONFIG_SAE */ +#ifdef CONFIG_FILS + if (params->fd_max_int && nl80211_fils_discovery(bss, msg, params) < 0) + goto fail; +#endif /* CONFIG_FILS */ + ret = send_and_recv_msgs_owner(drv, msg, get_connect_handle(bss), 1, NULL, NULL, NULL, NULL); if (ret) { @@ -4895,6 +5059,16 @@ static int wpa_driver_nl80211_sta_add(void *priv, goto fail; } + if (params->he_6ghz_capab) { + wpa_hexdump(MSG_DEBUG, " * he_6ghz_capab", + params->he_6ghz_capab, + sizeof(*params->he_6ghz_capab)); + if (nla_put(msg, NL80211_ATTR_HE_6GHZ_CAPABILITY, + sizeof(*params->he_6ghz_capab), + params->he_6ghz_capab)) + goto fail; + } + if (params->ext_capab) { wpa_hexdump(MSG_DEBUG, " * ext_capab", params->ext_capab, params->ext_capab_len); @@ -5226,6 +5400,10 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, if (nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER)) goto fail; + if ((addr && iftype == NL80211_IFTYPE_P2P_DEVICE) && + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) + goto fail; + ret = send_and_recv_msgs(drv, msg, handler, arg, NULL, NULL); msg = NULL; if (ret) { @@ -6181,12 +6359,15 @@ skip_auth_type: goto fail; ret = send_and_recv_msgs_owner(drv, msg, nl_connect, 1, NULL, - (void *) -1, NULL, NULL); + NULL, NULL, NULL); msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d " "(%s)", ret, strerror(-ret)); } else { +#ifdef CONFIG_DRIVER_NL80211_QCA + drv->roam_indication_done = false; +#endif /* CONFIG_DRIVER_NL80211_QCA */ wpa_printf(MSG_DEBUG, "nl80211: Connect request send successfully"); } @@ -7950,6 +8131,28 @@ static int wpa_driver_nl80211_send_action(struct i802_bss *bss, os_memset(bss->rand_addr, 0, ETH_ALEN); } +#ifdef CONFIG_MESH + if (is_mesh_interface(drv->nlmode)) { + struct hostapd_hw_modes *modes; + u16 num_modes, flags; + u8 dfs_domain; + int i; + + modes = nl80211_get_hw_feature_data(bss, &num_modes, + &flags, &dfs_domain); + if (dfs_domain != HOSTAPD_DFS_REGION_ETSI && + ieee80211_is_dfs(bss->freq, modes, num_modes)) + offchanok = 0; + if (modes) { + for (i = 0; i < num_modes; i++) { + os_free(modes[i].channels); + os_free(modes[i].rates); + } + os_free(modes); + } + } +#endif /* CONFIG_MESH */ + if (is_ap_interface(drv->nlmode) && (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) || (int) freq == bss->freq || drv->device_ap_sme || @@ -8506,7 +8709,7 @@ static int nl80211_pmkid(struct i802_bss *bss, int cmd, return -ENOBUFS; } - return send_and_recv_msgs(bss->drv, msg, NULL, (void *) -1, NULL, NULL); + return send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL); } @@ -8776,7 +8979,7 @@ static void nl80211_set_rekey_info(void *priv, const u8 *kek, size_t kek_len, nla_nest_end(msg, replay_nested); - ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1, NULL, NULL); + ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL); if (ret == -EOPNOTSUPP) { wpa_printf(MSG_DEBUG, "nl80211: Driver does not support rekey offload"); @@ -10225,6 +10428,9 @@ static int nl80211_join_mesh(struct i802_bss *bss, wpa_printf(MSG_DEBUG, " * flags=%08X", params->flags); + if (params->handle_dfs && nla_put_flag(msg, NL80211_ATTR_HANDLE_DFS)) + goto fail; + container = nla_nest_start(msg, NL80211_ATTR_MESH_SETUP); if (!container) goto fail; @@ -10770,38 +10976,49 @@ static int wpa_driver_do_acs(void *priv, struct drv_acs_params *params) } -static int nl80211_set_band(void *priv, enum set_band band) +static int nl80211_set_band(void *priv, u32 band_mask) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; struct nlattr *data; int ret; - enum qca_set_band qca_band; + enum qca_set_band qca_band_value; + u32 qca_band_mask = QCA_SETBAND_AUTO; - if (!drv->setband_vendor_cmd_avail) + if (!drv->setband_vendor_cmd_avail || + (band_mask > (WPA_SETBAND_2G | WPA_SETBAND_5G | WPA_SETBAND_6G))) return -1; - switch (band) { - case WPA_SETBAND_AUTO: - qca_band = QCA_SETBAND_AUTO; - break; - case WPA_SETBAND_5G: - qca_band = QCA_SETBAND_5G; - break; - case WPA_SETBAND_2G: - qca_band = QCA_SETBAND_2G; - break; - default: - return -1; - } + if (band_mask & WPA_SETBAND_5G) + qca_band_mask |= QCA_SETBAND_5G; + if (band_mask & WPA_SETBAND_2G) + qca_band_mask |= QCA_SETBAND_2G; + if (band_mask & WPA_SETBAND_6G) + qca_band_mask |= QCA_SETBAND_6G; + + /* + * QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE is a legacy interface hence make + * it suite to its values (AUTO/5G/2G) for backwards compatibility. + */ + qca_band_value = ((qca_band_mask & QCA_SETBAND_5G) && + (qca_band_mask & QCA_SETBAND_2G)) ? + QCA_SETBAND_AUTO : + qca_band_mask & ~QCA_SETBAND_6G; + + wpa_printf(MSG_DEBUG, + "nl80211: QCA_BAND_MASK = 0x%x, QCA_BAND_VALUE = %d", + qca_band_mask, qca_band_value); if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, QCA_NL80211_VENDOR_SUBCMD_SETBAND) || !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) || - nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE, qca_band)) { + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE, + qca_band_value) || + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SETBAND_MASK, + qca_band_mask)) { nlmsg_free(msg); return -ENOBUFS; } @@ -11432,6 +11649,10 @@ static int nl80211_configure_data_frame_filters(void *priv, u32 filter_flags) char path[128]; int ret; + /* P2P-Device has no netdev that can (or should) be configured here */ + if (nl80211_get_ifmode(bss) == NL80211_IFTYPE_P2P_DEVICE) + return 0; + wpa_printf(MSG_DEBUG, "nl80211: Data frame filter flags=0x%x", filter_flags); @@ -11657,6 +11878,11 @@ static int nl80211_set_4addr_mode(void *priv, const char *bridge_ifname, ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL); msg = NULL; + if (ret && val && nl80211_get_4addr(bss) == 1) { + wpa_printf(MSG_DEBUG, + "nl80211: 4addr mode was already enabled"); + ret = 0; + } if (!ret) { if (bridge_ifname[0] && val && i802_check_bridge(drv, bss, bridge_ifname, bss->ifname) < 0) diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h index 4009545..940d82c 100644 --- a/src/drivers/driver_nl80211.h +++ b/src/drivers/driver_nl80211.h @@ -175,6 +175,8 @@ struct wpa_driver_nl80211_data { unsigned int multicast_registrations:1; unsigned int no_rrm:1; unsigned int get_sta_info_vendor_cmd_avail:1; + unsigned int fils_discovery:1; + unsigned int unsol_bcast_probe_resp:1; u64 vendor_scan_cookie; u64 remain_on_chan_cookie; @@ -209,6 +211,8 @@ struct wpa_driver_nl80211_data { int auth_alg; u8 *auth_ie; size_t auth_ie_len; + u8 *auth_data; + size_t auth_data_len; u8 auth_wep_key[4][16]; size_t auth_wep_key_len[4]; int auth_wep_tx_keyidx; @@ -221,6 +225,12 @@ struct wpa_driver_nl80211_data { * (NL80211_CMD_VENDOR). 0 if no pending scan request. */ int last_scan_cmd; +#ifdef CONFIG_DRIVER_NL80211_QCA + bool roam_indication_done; + u8 *pending_roam_data; + size_t pending_roam_data_len; + struct os_reltime pending_roam_ind_time; +#endif /* CONFIG_DRIVER_NL80211_QCA */ }; struct nl_msg; @@ -289,6 +299,10 @@ int android_pno_start(struct i802_bss *bss, int android_pno_stop(struct i802_bss *bss); extern int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf, size_t buf_len); +extern int wpa_driver_nl80211_driver_event(struct wpa_driver_nl80211_data *drv, + u32 vendor_id, u32 subcmd, + u8 *data, size_t len); + #ifdef ANDROID_P2P int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration); diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c index 6c2ab51..1b57c0e 100644 --- a/src/drivers/driver_nl80211_capa.c +++ b/src/drivers/driver_nl80211_capa.c @@ -559,6 +559,10 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info, capa->flags |= WPA_DRIVER_FLAGS_BEACON_RATE_VHT; if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_BEACON_RATE_HE)) + capa->flags2 |= WPA_DRIVER_FLAGS2_BEACON_RATE_HE; + + if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_SET_SCAN_DWELL)) capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL; @@ -644,6 +648,22 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info, if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS)) info->drv->multicast_registrations = 1; + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_FILS_DISCOVERY)) + info->drv->fils_discovery = 1; + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP)) + info->drv->unsol_bcast_probe_resp = 1; + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT)) + capa->flags2 |= WPA_DRIVER_FLAGS2_BEACON_PROTECTION_CLIENT; + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION)) + capa->flags2 |= WPA_DRIVER_FLAGS2_OCV; } @@ -1370,6 +1390,7 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) if (!info.device_ap_sme) { drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS; + drv->capa.flags2 |= WPA_DRIVER_FLAGS2_AP_SME; /* * No AP SME is currently assumed to also indicate no AP MLME diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c index f75f7b3..c971001 100644 --- a/src/drivers/driver_nl80211_event.c +++ b/src/drivers/driver_nl80211_event.c @@ -571,6 +571,13 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, event.assoc_info.fils_pmkid = nla_data(fils_pmkid); wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); + + /* Avoid a race condition by stopping to ignore any following + * disconnection events now that the driver has indicated it is + * connected since that connection could have been triggered by a roam + * operation that happened in parallel with the disconnection request. + */ + drv->ignore_next_local_disconnect = 0; } @@ -1376,7 +1383,6 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1]; enum nl80211_cqm_rssi_threshold_event event; union wpa_event_data ed; - struct wpa_signal_info sig; int res; if (tb[NL80211_ATTR_CQM] == NULL || @@ -1443,19 +1449,27 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, return; } - res = nl80211_get_link_signal(drv, &sig); + /* + * nl80211_get_link_signal() and nl80211_get_link_noise() set default + * values in case querying the driver fails. + */ + res = nl80211_get_link_signal(drv, &ed.signal_change); if (res == 0) { - ed.signal_change.current_signal = sig.current_signal; - ed.signal_change.current_txrate = sig.current_txrate; wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm txrate: %d", - sig.current_signal, sig.current_txrate); + ed.signal_change.current_signal, + ed.signal_change.current_txrate); + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Querying the driver for signal info failed"); } - res = nl80211_get_link_noise(drv, &sig); + res = nl80211_get_link_noise(drv, &ed.signal_change); if (res == 0) { - ed.signal_change.current_noise = sig.current_noise; wpa_printf(MSG_DEBUG, "nl80211: Noise: %d dBm", - sig.current_noise); + ed.signal_change.current_noise); + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Querying the driver for noise info failed"); } wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed); @@ -1930,7 +1944,7 @@ try_2_4_or_5: return 2407 + 5 * chan; if (chan == 14) return 2484; - if (chan >= 36 && chan <= 169) + if (chan >= 36 && chan <= 177) return 5000 + 5 * chan; return 0; @@ -2058,6 +2072,27 @@ static void qca_nl80211_key_mgmt_auth(struct wpa_driver_nl80211_data *drv, } +static void +qca_nl80211_key_mgmt_auth_handler(struct wpa_driver_nl80211_data *drv, + const u8 *data, size_t len) +{ + if (!drv->roam_indication_done) { + wpa_printf(MSG_DEBUG, + "nl80211: Pending roam indication, delay processing roam+auth vendor event"); + os_get_reltime(&drv->pending_roam_ind_time); + + os_free(drv->pending_roam_data); + drv->pending_roam_data = os_memdup(data, len); + if (!drv->pending_roam_data) + return; + drv->pending_roam_data_len = len; + return; + } + drv->roam_indication_done = false; + qca_nl80211_key_mgmt_auth(drv, data, len); +} + + static void qca_nl80211_dfs_offload_radar_event( struct wpa_driver_nl80211_data *drv, u32 subcmd, u8 *msg, int length) { @@ -2315,7 +2350,7 @@ static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv, qca_nl80211_avoid_freq(drv, data, len); break; case QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH: - qca_nl80211_key_mgmt_auth(drv, data, len); + qca_nl80211_key_mgmt_auth_handler(drv, data, len); break; case QCA_NL80211_VENDOR_SUBCMD_DO_ACS: qca_nl80211_acs_select_ch(drv, data, len); @@ -2380,6 +2415,12 @@ static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv, return; } +#ifdef ANDROID +#ifdef ANDROID_LIB_EVENT + wpa_driver_nl80211_driver_event(drv, vendor_id, subcmd, data, len); +#endif /* ANDROID_LIB_EVENT */ +#endif /* ANDROID */ + switch (vendor_id) { case OUI_QCA: nl80211_vendor_event_qca(drv, subcmd, data, len); @@ -2707,17 +2748,36 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s", cmd, nl80211_command_to_string(cmd), bss->ifname); +#ifdef CONFIG_DRIVER_NL80211_QCA if (cmd == NL80211_CMD_ROAM && (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) { + if (drv->pending_roam_data) { + struct os_reltime now, age; + + os_get_reltime(&now); + os_reltime_sub(&now, &drv->pending_roam_ind_time, &age); + if (age.sec == 0 && age.usec < 100000) { + wpa_printf(MSG_DEBUG, + "nl80211: Process pending roam+auth vendor event"); + qca_nl80211_key_mgmt_auth( + drv, drv->pending_roam_data, + drv->pending_roam_data_len); + } + os_free(drv->pending_roam_data); + drv->pending_roam_data = NULL; + return; + } /* * Device will use roam+auth vendor event to indicate * roaming, so ignore the regular roam event. */ + drv->roam_indication_done = true; wpa_printf(MSG_DEBUG, "nl80211: Ignore roam event (cmd=%d), device will use vendor event roam+auth", cmd); return; } +#endif /* CONFIG_DRIVER_NL80211_QCA */ if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED && (cmd == NL80211_CMD_NEW_SCAN_RESULTS || diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h index 3e0d4a0..ac78da9 100644 --- a/src/drivers/nl80211_copy.h +++ b/src/drivers/nl80211_copy.h @@ -1178,6 +1178,10 @@ * includes the contents of the frame. %NL80211_ATTR_ACK flag is included * if the recipient acknowledged the frame. * + * @NL80211_CMD_SET_SAR_SPECS: SAR power limitation configuration is + * passed using %NL80211_ATTR_SAR_SPEC. %NL80211_ATTR_WIPHY is used to + * specify the wiphy index to be applied to. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1408,6 +1412,8 @@ enum nl80211_commands { NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS, + NL80211_CMD_SET_SAR_SPECS, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1957,8 +1963,15 @@ enum nl80211_commands { * @NL80211_ATTR_PROBE_RESP: Probe Response template data. Contains the entire * probe-response frame. The DA field in the 802.11 header is zero-ed out, * to be filled by the FW. - * @NL80211_ATTR_DISABLE_HT: Force HT capable interfaces to disable - * this feature. Currently, only supported in mac80211 drivers. + * @NL80211_ATTR_DISABLE_HT: Force HT capable interfaces to disable + * this feature during association. This is a flag attribute. + * Currently only supported in mac80211 drivers. + * @NL80211_ATTR_DISABLE_VHT: Force VHT capable interfaces to disable + * this feature during association. This is a flag attribute. + * Currently only supported in mac80211 drivers. + * @NL80211_ATTR_DISABLE_HE: Force HE capable interfaces to disable + * this feature during association. This is a flag attribute. + * Currently only supported in mac80211 drivers. * @NL80211_ATTR_HT_CAPABILITY_MASK: Specify which bits of the * ATTR_HT_CAPABILITY to which attention should be paid. * Currently, only mac80211 NICs support this feature. @@ -2079,7 +2092,8 @@ enum nl80211_commands { * until the channel switch event. * @NL80211_ATTR_CH_SWITCH_BLOCK_TX: flag attribute specifying that transmission * must be blocked on the current channel (before the channel switch - * operation). + * operation). Also included in the channel switch started event if quiet + * was requested by the AP. * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information * for the time while performing a channel switch. * @NL80211_ATTR_CNTDWN_OFFS_BEACON: An array of offsets (u16) to the channel @@ -2534,6 +2548,15 @@ enum nl80211_commands { * This is a u8 attribute that encapsulates one of the values from * &enum nl80211_sae_pwe_mechanism. * + * @NL80211_ATTR_SAR_SPEC: SAR power limitation specification when + * used with %NL80211_CMD_SET_SAR_SPECS. The message contains fields + * of %nl80211_sar_attrs which specifies the sar type and related + * sar specs. Sar specs contains array of %nl80211_sar_specs_attrs. + * + * @NL80211_ATTR_RECONNECT_REQUESTED: flag attribute, used with deauth and + * disassoc events to indicate that an immediate reconnect to the AP + * is desired. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3025,6 +3048,12 @@ enum nl80211_attrs { NL80211_ATTR_SAE_PWE, + NL80211_ATTR_RECONNECT_REQUESTED, + + NL80211_ATTR_SAR_SPEC, + + NL80211_ATTR_DISABLE_HE, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -7156,4 +7185,96 @@ enum nl80211_sae_pwe_mechanism { NL80211_SAE_PWE_HASH_TO_ELEMENT, NL80211_SAE_PWE_BOTH, }; + +/** + * enum nl80211_sar_type - type of SAR specs + * + * @NL80211_SAR_TYPE_POWER: power limitation specified in 0.25dBm unit + * + */ +enum nl80211_sar_type { + NL80211_SAR_TYPE_POWER, + + /* add new type here */ + + /* Keep last */ + NUM_NL80211_SAR_TYPE, +}; + +/** + * enum nl80211_sar_attrs - Attributes for SAR spec + * + * @NL80211_SAR_ATTR_TYPE: the SAR type as defined in &enum nl80211_sar_type. + * + * @NL80211_SAR_ATTR_SPECS: Nested array of SAR power + * limit specifications. Each specification contains a set + * of %nl80211_sar_specs_attrs. + * + * For SET operation, it contains array of %NL80211_SAR_ATTR_SPECS_POWER + * and %NL80211_SAR_ATTR_SPECS_RANGE_INDEX. + * + * For sar_capa dump, it contains array of + * %NL80211_SAR_ATTR_SPECS_START_FREQ + * and %NL80211_SAR_ATTR_SPECS_END_FREQ. + * + * @__NL80211_SAR_ATTR_LAST: Internal + * @NL80211_SAR_ATTR_MAX: highest sar attribute + * + * These attributes are used with %NL80211_CMD_SET_SAR_SPEC + */ +enum nl80211_sar_attrs { + __NL80211_SAR_ATTR_INVALID, + + NL80211_SAR_ATTR_TYPE, + NL80211_SAR_ATTR_SPECS, + + __NL80211_SAR_ATTR_LAST, + NL80211_SAR_ATTR_MAX = __NL80211_SAR_ATTR_LAST - 1, +}; + +/** + * enum nl80211_sar_specs_attrs - Attributes for SAR power limit specs + * + * @NL80211_SAR_ATTR_SPECS_POWER: Required (s32)value to specify the actual + * power limit value in units of 0.25 dBm if type is + * NL80211_SAR_TYPE_POWER. (i.e., a value of 44 represents 11 dBm). + * 0 means userspace doesn't have SAR limitation on this associated range. + * + * @NL80211_SAR_ATTR_SPECS_RANGE_INDEX: Required (u32) value to specify the + * index of exported freq range table and the associated power limitation + * is applied to this range. + * + * Userspace isn't required to set all the ranges advertised by WLAN driver, + * and userspace can skip some certain ranges. These skipped ranges don't + * have SAR limitations, and they are same as setting the + * %NL80211_SAR_ATTR_SPECS_POWER to any unreasonable high value because any + * value higher than regulatory allowed value just means SAR power + * limitation is removed, but it's required to set at least one range. + * It's not allowed to set duplicated range in one SET operation. + * + * Every SET operation overwrites previous SET operation. + * + * @NL80211_SAR_ATTR_SPECS_START_FREQ: Required (u32) value to specify the start + * frequency of this range edge when registering SAR capability to wiphy. + * It's not a channel center frequency. The unit is kHz. + * + * @NL80211_SAR_ATTR_SPECS_END_FREQ: Required (u32) value to specify the end + * frequency of this range edge when registering SAR capability to wiphy. + * It's not a channel center frequency. The unit is kHz. + * + * @__NL80211_SAR_ATTR_SPECS_LAST: Internal + * @NL80211_SAR_ATTR_SPECS_MAX: highest sar specs attribute + */ +enum nl80211_sar_specs_attrs { + __NL80211_SAR_ATTR_SPECS_INVALID, + + NL80211_SAR_ATTR_SPECS_POWER, + NL80211_SAR_ATTR_SPECS_RANGE_INDEX, + NL80211_SAR_ATTR_SPECS_START_FREQ, + NL80211_SAR_ATTR_SPECS_END_FREQ, + + __NL80211_SAR_ATTR_SPECS_LAST, + NL80211_SAR_ATTR_SPECS_MAX = __NL80211_SAR_ATTR_SPECS_LAST - 1, +}; + #endif /* __LINUX_NL80211_H */ diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c index e57461a..8c475f1 100644 --- a/src/eap_peer/eap_aka.c +++ b/src/eap_peer/eap_aka.c @@ -442,19 +442,28 @@ static int eap_aka_learn_ids(struct eap_sm *sm, struct eap_aka_data *data, static int eap_aka_add_id_msg(struct eap_aka_data *data, - const struct wpabuf *msg) + const struct wpabuf *msg1, + const struct wpabuf *msg2) { - if (msg == NULL) + size_t len; + + if (!msg1) return -1; + len = wpabuf_len(msg1); + if (msg2) + len += wpabuf_len(msg2); - if (data->id_msgs == NULL) { - data->id_msgs = wpabuf_dup(msg); - return data->id_msgs == NULL ? -1 : 0; + if (!data->id_msgs) { + data->id_msgs = wpabuf_alloc(len); + if (!data->id_msgs) + return -1; + } else if (wpabuf_resize(&data->id_msgs, len) < 0) { + return -1; } - if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0) - return -1; - wpabuf_put_buf(data->id_msgs, msg); + wpabuf_put_buf(data->id_msgs, msg1); + if (msg2) + wpabuf_put_buf(data->id_msgs, msg2); return 0; } @@ -799,8 +808,13 @@ static struct wpabuf * eap_aka_process_identity(struct eap_sm *sm, buf = eap_aka_response_identity(sm, data, id, attr->id_req); if (data->prev_id != id) { - eap_aka_add_id_msg(data, reqData); - eap_aka_add_id_msg(data, buf); + if (eap_aka_add_id_msg(data, reqData, buf) < 0) { + wpa_printf(MSG_INFO, + "EAP-AKA: Failed to store ID messages"); + wpabuf_free(buf); + return eap_aka_client_error( + data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } data->prev_id = id; } diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c index 7c37043..12e30df 100644 --- a/src/eap_peer/eap_peap.c +++ b/src/eap_peer/eap_peap.c @@ -803,6 +803,10 @@ static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data, res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted); if (res) return res; + if (wpabuf_len(in_decrypted) == 0) { + wpabuf_free(in_decrypted); + return 1; + } continue_req: wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP", @@ -1081,7 +1085,11 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, } if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { - char *label; + const char *label; + const u8 eap_tls13_context[1] = { EAP_TYPE_PEAP }; + const u8 *context = NULL; + size_t context_len = 0; + wpa_printf(MSG_DEBUG, "EAP-PEAP: TLS done, proceed to Phase 2"); eap_peap_free_key(data); @@ -1091,16 +1099,25 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, * PEAPv1 implementations seem to be using the old * label, "client EAP encryption", instead. Use the old * label by default, but allow it to be configured with - * phase1 parameter peaplabel=1. */ - if (data->force_new_label) + * phase1 parameter peaplabel=1. + * + * When using TLS 1.3, draft-ietf-emu-tls-eap-types + * defines a new set of label and context parameters. + */ + if (data->ssl.tls_v13) { + label = "EXPORTER_EAP_TLS_Key_Material"; + context = eap_tls13_context; + context_len = sizeof(eap_tls13_context); + } else if (data->force_new_label) { label = "client PEAP encryption"; - else + } else { label = "client EAP encryption"; + } wpa_printf(MSG_DEBUG, "EAP-PEAP: using label '%s' in " "key derivation", label); data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, label, - NULL, 0, + context, context_len, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (data->key_data) { diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c index ad079a7..0d479f1 100644 --- a/src/eap_peer/eap_tls.c +++ b/src/eap_peer/eap_tls.c @@ -302,15 +302,11 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, return NULL; } - if (res == 2) { - /* Application data included in the handshake message (used by - * EAP-TLS 1.3 to indicate conclusion of the exchange). */ - wpa_hexdump_buf(MSG_DEBUG, "EAP-TLS: Received Application Data", - resp); - wpa_hexdump_buf(MSG_DEBUG, "EAP-TLS: Remaining tls_out data", - data->ssl.tls_out); + /* draft-ietf-emu-eap-tls13-13 Section 2.5 */ + if (res == 2 && data->ssl.tls_v13 && wpabuf_len(resp) == 1 && + *wpabuf_head_u8(resp) == 0) { + wpa_printf(MSG_DEBUG, "EAP-TLS: ACKing Commitment Message"); eap_peer_tls_reset_output(&data->ssl); - /* Send an ACK to allow the server to complete exchange */ res = 1; } diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c index ab10678..c1837db 100644 --- a/src/eap_peer/eap_tls_common.c +++ b/src/eap_peer/eap_tls_common.c @@ -413,9 +413,9 @@ u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, struct tls_random keys; u8 *out; - if (eap_type == EAP_TYPE_TLS && data->tls_v13) { + if (data->tls_v13) { u8 *id, *method_id; - const u8 context[] = { EAP_TYPE_TLS }; + const u8 context[] = { eap_type }; /* Session-Id = <EAP-Type> || Method-Id * Method-Id = TLS-Exporter("EXPORTER_EAP_TLS_Method-Id", diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c index 642d179..c401915 100644 --- a/src/eap_peer/eap_ttls.c +++ b/src/eap_peer/eap_ttls.c @@ -268,10 +268,22 @@ static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code, static int eap_ttls_v0_derive_key(struct eap_sm *sm, struct eap_ttls_data *data) { + const char *label; + const u8 eap_tls13_context[1] = { EAP_TYPE_TTLS }; + const u8 *context = NULL; + size_t context_len = 0; + + if (data->ssl.tls_v13) { + label = "EXPORTER_EAP_TLS_Key_Material"; + context = eap_tls13_context; + context_len = sizeof(eap_tls13_context); + } else { + label = "ttls keying material"; + } + eap_ttls_free_key(data); - data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, - "ttls keying material", - NULL, 0, + data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, label, + context, context_len, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (!data->key_data) { @@ -1441,6 +1453,7 @@ static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data, if ((in_data == NULL || wpabuf_len(in_data) == 0) && data->phase2_start) { +start: return eap_ttls_phase2_start(sm, data, ret, identifier, out_data); } @@ -1455,6 +1468,20 @@ static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data, retval = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted); if (retval) goto done; + if (wpabuf_len(in_decrypted) == 0) { + wpabuf_free(in_decrypted); + goto start; + } + + /* draft-ietf-emu-eap-tls13-13 Section 2.5 */ + if (data->ssl.tls_v13 && wpabuf_len(in_decrypted) == 1 && + *wpabuf_head_u8(in_decrypted) == 0) { + wpa_printf(MSG_DEBUG, + "EAP-TTLS: ACKing EAP-TLS Commitment Message"); + eap_peer_tls_reset_output(&data->ssl); + wpabuf_free(in_decrypted); + return 1; + } continue_req: data->phase2_start = 0; diff --git a/src/eap_server/eap_server_peap.c b/src/eap_server/eap_server_peap.c index f234f6f..f526e8b 100644 --- a/src/eap_server/eap_server_peap.c +++ b/src/eap_server/eap_server_peap.c @@ -325,13 +325,27 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) u8 *tk; u8 isk[32], imck[60]; int res; + const char *label; + const u8 eap_tls13_context[1] = { EAP_TYPE_PEAP }; + const u8 *context = NULL; + size_t context_len = 0; + + if (data->ssl.tls_v13) { + label = "EXPORTER_EAP_TLS_Key_Material"; + context = eap_tls13_context; + context_len = sizeof(eap_tls13_context); + } else { + /* TODO: PEAPv1 - different label in some cases */ + label = "client EAP encryption"; + } /* * Tunnel key (TK) is the first 60 octets of the key generated by * phase 1 of PEAP (based on TLS). */ - tk = eap_server_tls_derive_key(sm, &data->ssl, "client EAP encryption", - NULL, 0, EAP_TLS_KEY_LEN); + tk = eap_server_tls_derive_key(sm, &data->ssl, label, + context, context_len, + EAP_TLS_KEY_LEN); if (tk == NULL) return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60); @@ -498,7 +512,25 @@ static struct wpabuf * eap_peap_build_phase2_term(struct eap_sm *sm, encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf); os_free(hdr); - return encr_req; + if (!data->ssl.tls_v13 || + !tls_connection_resumed(sm->cfg->ssl_ctx, data->ssl.conn)) { + wpabuf_free(data->ssl.tls_out); + data->ssl.tls_out_pos = 0; + return encr_req; + } + + if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(encr_req)) < 0) { + wpa_printf(MSG_INFO, + "EAP-PEAP: Failed to resize output buffer"); + wpabuf_free(encr_req); + return NULL; + } + wpabuf_put_buf(data->ssl.tls_out, encr_req); + wpa_hexdump_buf(MSG_DEBUG, + "EAP-PEAP: Data appended to the message", encr_req); + os_free(encr_req); + + return data->ssl.tls_out; } @@ -547,8 +579,6 @@ static struct wpabuf * eap_peap_buildReq(struct eap_sm *sm, void *priv, u8 id) data->ssl.tls_out = eap_peap_build_phase2_tlv(sm, data, id); break; case SUCCESS_REQ: - wpabuf_free(data->ssl.tls_out); - data->ssl.tls_out_pos = 0; data->ssl.tls_out = eap_peap_build_phase2_term(sm, data, id, 1); break; @@ -1300,6 +1330,10 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_peap_data *data = priv; u8 *eapKeyData; + const char *label; + const u8 eap_tls13_context[1] = { EAP_TYPE_PEAP }; + const u8 *context = NULL; + size_t context_len = 0; if (data->state != SUCCESS) return NULL; @@ -1332,9 +1366,17 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) return eapKeyData; } - /* TODO: PEAPv1 - different label in some cases */ + if (data->ssl.tls_v13) { + label = "EXPORTER_EAP_TLS_Key_Material"; + context = eap_tls13_context; + context_len = sizeof(eap_tls13_context); + } else { + /* TODO: PEAPv1 - different label in some cases */ + label = "client EAP encryption"; + } + eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, - "client EAP encryption", NULL, 0, + label, context, context_len, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (eapKeyData) { os_memset(eapKeyData + EAP_TLS_KEY_LEN, 0, EAP_EMSK_LEN); @@ -1353,6 +1395,10 @@ static u8 * eap_peap_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_peap_data *data = priv; u8 *eapKeyData, *emsk; + const char *label; + const u8 eap_tls13_context[1] = { EAP_TYPE_PEAP }; + const u8 *context = NULL; + size_t context_len = 0; if (data->state != SUCCESS) return NULL; @@ -1362,9 +1408,17 @@ static u8 * eap_peap_get_emsk(struct eap_sm *sm, void *priv, size_t *len) return NULL; } - /* TODO: PEAPv1 - different label in some cases */ + if (data->ssl.tls_v13) { + label = "EXPORTER_EAP_TLS_Key_Material"; + context = eap_tls13_context; + context_len = sizeof(eap_tls13_context); + } else { + /* TODO: PEAPv1 - different label in some cases */ + label = "client EAP encryption"; + } + eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, - "client EAP encryption", NULL, 0, + label, context, context_len, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (eapKeyData) { emsk = os_memdup(eapKeyData + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c index 769fd1f..00a496f 100644 --- a/src/eap_server/eap_server_tls.c +++ b/src/eap_server/eap_server_tls.c @@ -266,39 +266,6 @@ static void eap_tls_process_msg(struct eap_sm *sm, void *priv, eap_tls_state(data, FAILURE); return; } - - if (data->ssl.tls_v13 && - tls_connection_established(sm->cfg->ssl_ctx, data->ssl.conn)) { - struct wpabuf *plain, *encr; - - wpa_printf(MSG_DEBUG, - "EAP-TLS: Send empty application data to indicate end of exchange"); - /* FIX: This should be an empty application data based on - * draft-ietf-emu-eap-tls13-05, but OpenSSL does not allow zero - * length payload (SSL_write() documentation explicitly - * describes this as not allowed), so work around that for now - * by sending out a payload of one octet. Hopefully the draft - * specification will change to allow this so that no crypto - * library changes are needed. */ - plain = wpabuf_alloc(1); - if (!plain) - return; - wpabuf_put_u8(plain, 0); - encr = eap_server_tls_encrypt(sm, &data->ssl, plain); - wpabuf_free(plain); - if (!encr) - return; - if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(encr)) < 0) { - wpa_printf(MSG_INFO, - "EAP-TLS: Failed to resize output buffer"); - wpabuf_free(encr); - return; - } - wpabuf_put_buf(data->ssl.tls_out, encr); - wpa_hexdump_buf(MSG_DEBUG, - "EAP-TLS: Data appended to the message", encr); - wpabuf_free(encr); - } } diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c index b38f1e0..a9b53b1 100644 --- a/src/eap_server/eap_server_tls_common.c +++ b/src/eap_server/eap_server_tls_common.c @@ -146,10 +146,10 @@ u8 * eap_server_tls_derive_session_id(struct eap_sm *sm, { struct tls_random keys; u8 *out; - const u8 context[] = { EAP_TYPE_TLS }; - if (eap_type == EAP_TYPE_TLS && data->tls_v13) { + if (data->tls_v13) { u8 *id, *method_id; + const u8 context[] = { eap_type }; /* Session-Id = <EAP-Type> || Method-Id * Method-Id = TLS-Exporter("EXPORTER_EAP_TLS_Method-Id", @@ -366,6 +366,56 @@ int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data) sm->serial_num = tls_connection_peer_serial_num( sm->cfg->ssl_ctx, data->conn); + /* + * https://tools.ietf.org/html/draft-ietf-emu-eap-tls13#section-2.5 + * + * We need to signal the other end that TLS negotiation is done. We + * can't send a zero-length application data message, so we send + * application data which is one byte of zero. + * + * Note this is only done for when there is no application data to be + * sent. So this is done always for EAP-TLS but notibly not for PEAP + * even on resumption. + */ + if (data->tls_v13 && + tls_connection_established(sm->cfg->ssl_ctx, data->conn)) { + struct wpabuf *plain, *encr; + + switch (sm->currentMethod) { + case EAP_TYPE_PEAP: + break; + default: + if (!tls_connection_resumed(sm->cfg->ssl_ctx, + data->conn)) + break; + /* fallthrough */ + case EAP_TYPE_TLS: + wpa_printf(MSG_DEBUG, + "EAP-TLS: Send Commitment Message"); + + plain = wpabuf_alloc(1); + if (!plain) + return -1; + wpabuf_put_u8(plain, 0); + encr = eap_server_tls_encrypt(sm, data, plain); + wpabuf_free(plain); + if (!encr) + return -1; + if (wpabuf_resize(&data->tls_out, wpabuf_len(encr)) < 0) + { + wpa_printf(MSG_INFO, + "EAP-TLS: Failed to resize output buffer"); + wpabuf_free(encr); + return -1; + } + wpabuf_put_buf(data->tls_out, encr); + wpa_hexdump_buf(MSG_DEBUG, + "EAP-TLS: Data appended to the message", + encr); + wpabuf_free(encr); + } + } + return 0; } diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c index 2f0c041..b893522 100644 --- a/src/eap_server/eap_server_ttls.c +++ b/src/eap_server/eap_server_ttls.c @@ -1271,13 +1271,25 @@ static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_ttls_data *data = priv; u8 *eapKeyData; + const char *label; + const u8 eap_tls13_context[1] = { EAP_TYPE_TTLS }; + const u8 *context = NULL; + size_t context_len = 0; if (data->state != SUCCESS) return NULL; + if (data->ssl.tls_v13) { + label = "EXPORTER_EAP_TLS_Key_Material"; + context = eap_tls13_context; + context_len = sizeof(eap_tls13_context); + } else { + label = "ttls keying material"; + } + eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, - "ttls keying material", NULL, 0, - EAP_TLS_KEY_LEN); + label, context, context_len, + EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (eapKeyData) { *len = EAP_TLS_KEY_LEN; wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key", @@ -1313,12 +1325,24 @@ static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_ttls_data *data = priv; u8 *eapKeyData, *emsk; + const char *label; + const u8 eap_tls13_context[1] = { EAP_TYPE_TTLS }; + const u8 *context = NULL; + size_t context_len = 0; if (data->state != SUCCESS) return NULL; + if (data->ssl.tls_v13) { + label = "EXPORTER_EAP_TLS_Key_Material"; + context = eap_tls13_context; + context_len = sizeof(eap_tls13_context); + } else { + label = "ttls keying material"; + } + eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, - "ttls keying material", NULL, 0, + label, context, context_len, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (eapKeyData) { emsk = os_malloc(EAP_EMSK_LEN); diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index 6b92a6d..9524aef 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -453,6 +453,8 @@ static void p2p_copy_client_info(struct p2p_device *dev, dev->info.config_methods = cli->config_methods; os_memcpy(dev->info.pri_dev_type, cli->pri_dev_type, 8); dev->info.wps_sec_dev_type_list_len = 8 * cli->num_sec_dev_types; + if (dev->info.wps_sec_dev_type_list_len > WPS_SEC_DEV_TYPE_MAX_LEN) + dev->info.wps_sec_dev_type_list_len = WPS_SEC_DEV_TYPE_MAX_LEN; os_memcpy(dev->info.wps_sec_dev_type_list, cli->sec_dev_types, dev->info.wps_sec_dev_type_list_len); } @@ -663,6 +665,8 @@ static void p2p_update_peer_vendor_elems(struct p2p_device *dev, const u8 *ies, if (wpabuf_resize(&dev->info.vendor_elems, 2 + len) < 0) break; wpabuf_put_data(dev->info.vendor_elems, pos - 2, 2 + len); + if (wpabuf_size(dev->info.vendor_elems) > 2000) + break; } } @@ -3510,12 +3514,17 @@ int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq, } -void p2p_scan_res_handled(struct p2p_data *p2p) +void p2p_scan_res_handled(struct p2p_data *p2p, unsigned int delay) { if (!p2p->p2p_scan_running) { p2p_dbg(p2p, "p2p_scan was not running, but scan results received"); } p2p->p2p_scan_running = 0; + + /* Use this delay only when p2p_find doesn't set it */ + if (!p2p->search_delay) + p2p->search_delay = delay; + eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); if (p2p_run_after_scan(p2p)) diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index ed8beab..762bd40 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -1625,6 +1625,7 @@ int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq, /** * p2p_scan_res_handled - Indicate end of scan results * @p2p: P2P module context from p2p_init() + * @delay: Search delay for next scan in ms * * This function is called to indicate that all P2P scan results from a scan * have been reported with zero or more calls to p2p_scan_res_handler(). This @@ -1632,7 +1633,7 @@ int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq, * struct p2p_config::p2p_scan() call if none of the p2p_scan_res_handler() * calls stopped iteration. */ -void p2p_scan_res_handled(struct p2p_data *p2p); +void p2p_scan_res_handled(struct p2p_data *p2p, unsigned int delay); enum p2p_send_action_result { P2P_SEND_ACTION_SUCCESS /* Frame was send and acknowledged */, diff --git a/src/radius/radius.h b/src/radius/radius.h index 630c0f9..fb81481 100644 --- a/src/radius/radius.h +++ b/src/radius/radius.h @@ -225,6 +225,9 @@ struct radius_msg; /* Default size to be allocated for attribute array */ #define RADIUS_DEFAULT_ATTR_COUNT 16 +/* Maximum message length for incoming RADIUS messages, as stated in RFC 2865 + * Section 3 ("Packet Format").*/ +#define RADIUS_MAX_MSG_LEN 4096 /* MAC address ASCII format for IEEE 802.1X use * (draft-congdon-radius-8021x-20.txt) */ diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c index 2b7a604..bfcb944 100644 --- a/src/radius/radius_client.c +++ b/src/radius/radius_client.c @@ -816,7 +816,9 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) struct hostapd_radius_servers *conf = radius->conf; RadiusType msg_type = (RadiusType) sock_ctx; int len, roundtrip; - unsigned char buf[3000]; + unsigned char buf[RADIUS_MAX_MSG_LEN]; + struct msghdr msghdr = {0}; + struct iovec iov; struct radius_msg *msg; struct radius_hdr *hdr; struct radius_rx_handler *handlers; @@ -836,15 +838,22 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) rconf = conf->auth_server; } - len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT); + iov.iov_base = buf; + iov.iov_len = RADIUS_MAX_MSG_LEN; + msghdr.msg_iov = &iov; + msghdr.msg_iovlen = 1; + msghdr.msg_flags = 0; + len = recvmsg(sock, &msghdr, MSG_DONTWAIT); if (len < 0) { - wpa_printf(MSG_INFO, "recv[RADIUS]: %s", strerror(errno)); + wpa_printf(MSG_INFO, "recvmsg[RADIUS]: %s", strerror(errno)); return; } + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS " "server", len); - if (len == sizeof(buf)) { + + if (msghdr.msg_flags & MSG_TRUNC) { wpa_printf(MSG_INFO, "RADIUS: Possibly too long UDP frame for our buffer - dropping it"); return; } diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c index 971fe91..e02c215 100644 --- a/src/radius/radius_server.c +++ b/src/radius/radius_server.c @@ -35,11 +35,6 @@ */ #define RADIUS_MAX_SESSION 1000 -/** - * RADIUS_MAX_MSG_LEN - Maximum message length for incoming RADIUS messages - */ -#define RADIUS_MAX_MSG_LEN 3000 - static const struct eapol_callbacks radius_server_eapol_cb; struct radius_client; diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index e07527b..9a5ba7b 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -579,7 +579,7 @@ static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, struct wpa_ptk *ptk) { const u8 *z = NULL; - size_t z_len = 0; + size_t z_len = 0, kdk_len; int akmp; #ifdef CONFIG_IEEE80211R @@ -603,10 +603,19 @@ static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr, akmp |= WPA_KEY_MGMT_PSK_SHA256; } #endif /* CONFIG_OWE */ + + if (sm->force_kdk_derivation || + (sm->secure_ltf && sm->ap_rsnxe && sm->ap_rsnxe_len >= 4 && + sm->ap_rsnxe[3] & BIT(WLAN_RSNX_CAPAB_SECURE_LTF - 8))) + kdk_len = WPA_KDK_MAX_LEN; + else + kdk_len = 0; + return wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion", sm->own_addr, sm->bssid, sm->snonce, key->key_nonce, ptk, akmp, - sm->pairwise_cipher, z, z_len); + sm->pairwise_cipher, z, z_len, + kdk_len); } @@ -948,6 +957,9 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, return -1; } + wpa_sm_store_ptk(sm, sm->bssid, sm->pairwise_cipher, + sm->dot11RSNAConfigPMKLifetime, &sm->ptk); + /* TK is not needed anymore in supplicant */ os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN); sm->ptk.tk_len = 0; @@ -1306,7 +1318,8 @@ static int ieee80211w_set_keys(struct wpa_sm *sm, { size_t len; - if (!wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher)) + if (!wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher) || + sm->mgmt_group_cipher == WPA_CIPHER_GTK_NOT_USED) return 0; if (ie->igtk) { @@ -1559,11 +1572,12 @@ static int wpa_supplicant_validate_ie(struct wpa_sm *sm, return -1; } - if ((sm->ap_rsnxe && !ie->rsnxe) || - (!sm->ap_rsnxe && ie->rsnxe) || - (sm->ap_rsnxe && ie->rsnxe && - (sm->ap_rsnxe_len != ie->rsnxe_len || - os_memcmp(sm->ap_rsnxe, ie->rsnxe, sm->ap_rsnxe_len) != 0))) { + if (sm->proto == WPA_PROTO_RSN && + ((sm->ap_rsnxe && !ie->rsnxe) || + (!sm->ap_rsnxe && ie->rsnxe) || + (sm->ap_rsnxe && ie->rsnxe && + (sm->ap_rsnxe_len != ie->rsnxe_len || + os_memcmp(sm->ap_rsnxe, ie->rsnxe, sm->ap_rsnxe_len) != 0)))) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: RSNXE mismatch between Beacon/ProbeResp and EAPOL-Key msg 3/4"); wpa_hexdump(MSG_INFO, "RSNXE in Beacon/ProbeResp", @@ -1665,6 +1679,7 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, } if (ie.igtk && + sm->mgmt_group_cipher != WPA_CIPHER_GTK_NOT_USED && wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher) && ie.igtk_len != WPA_IGTK_KDE_PREFIX_LEN + (unsigned int) wpa_cipher_key_len(sm->mgmt_group_cipher)) { @@ -3182,6 +3197,7 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) sm->p2p = config->p2p; sm->wpa_rsc_relaxation = config->wpa_rsc_relaxation; sm->owe_ptk_workaround = config->owe_ptk_workaround; + sm->force_kdk_derivation = config->force_kdk_derivation; #ifdef CONFIG_FILS if (config->fils_cache_id) { sm->fils_cache_id_set = 1; @@ -3204,6 +3220,7 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) sm->wpa_rsc_relaxation = 0; sm->owe_ptk_workaround = 0; sm->beacon_prot = 0; + sm->force_kdk_derivation = false; } } @@ -3781,6 +3798,16 @@ int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid, } +struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_get(struct wpa_sm *sm, + const u8 *aa, + const u8 *pmkid, + const void *network_ctx, + int akmp) +{ + return pmksa_cache_get(sm->pmksa, aa, pmkid, network_ctx, akmp); +} + + void wpa_sm_drop_sa(struct wpa_sm *sm) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK"); @@ -3801,6 +3828,11 @@ void wpa_sm_drop_sa(struct wpa_sm *sm) sm->pmk_r0_len = 0; os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1)); sm->pmk_r1_len = 0; +#ifdef CONFIG_PASN + os_free(sm->pasn_r1kh); + sm->pasn_r1kh = NULL; + sm->n_pasn_r1kh = 0; +#endif /* CONFIG_PASN */ #endif /* CONFIG_IEEE80211R */ } @@ -4111,7 +4143,7 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data, const u8 *g_sta = NULL; size_t g_sta_len = 0; const u8 *g_ap = NULL; - size_t g_ap_len = 0; + size_t g_ap_len = 0, kdk_len; struct wpabuf *pub = NULL; os_memcpy(sm->bssid, bssid, ETH_ALEN); @@ -4339,13 +4371,21 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data, goto fail; } + if (sm->force_kdk_derivation || + (sm->secure_ltf && sm->ap_rsnxe && sm->ap_rsnxe_len >= 4 && + sm->ap_rsnxe[3] & BIT(WLAN_RSNX_CAPAB_SECURE_LTF - 8))) + kdk_len = WPA_KDK_MAX_LEN; + else + kdk_len = 0; + if (fils_pmk_to_ptk(sm->pmk, sm->pmk_len, sm->own_addr, sm->bssid, sm->fils_nonce, sm->fils_anonce, dh_ss ? wpabuf_head(dh_ss) : NULL, dh_ss ? wpabuf_len(dh_ss) : 0, &sm->ptk, ick, &ick_len, sm->key_mgmt, sm->pairwise_cipher, - sm->fils_ft, &sm->fils_ft_len) < 0) { + sm->fils_ft, &sm->fils_ft_len, + kdk_len) < 0) { wpa_printf(MSG_DEBUG, "FILS: Failed to derive PTK"); goto fail; } @@ -4900,6 +4940,9 @@ int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len) goto fail; } + wpa_sm_store_ptk(sm, sm->bssid, sm->pairwise_cipher, + sm->dot11RSNAConfigPMKLifetime, &sm->ptk); + /* TODO: TK could be cleared after auth frame exchange now that driver * takes care of association frame encryption/decryption. */ /* TK is not needed anymore in supplicant */ @@ -5172,3 +5215,14 @@ void wpa_sm_set_dpp_z(struct wpa_sm *sm, const struct wpabuf *z) } } #endif /* CONFIG_DPP2 */ + + +#ifdef CONFIG_PASN +void wpa_pasn_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, + const u8 *pmkid, const u8 *bssid, int key_mgmt) +{ + sm->cur_pmksa = pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0, + bssid, sm->own_addr, NULL, + key_mgmt, 0); +} +#endif /* CONFIG_PASN */ diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index 2142772..8e4533e 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -87,6 +87,8 @@ struct wpa_sm_ctx { const u8 *pkt, size_t pkt_len); int (*channel_info)(void *ctx, struct wpa_channel_info *ci); void (*transition_disable)(void *ctx, u8 bitmap); + void (*store_ptk)(void *ctx, u8 *addr, int cipher, + u32 life_time, const struct wpa_ptk *ptk); }; @@ -130,6 +132,7 @@ struct rsn_supp_config { int owe_ptk_workaround; const u8 *fils_cache_id; int beacon_prot; + bool force_kdk_derivation; }; #ifndef CONFIG_NO_WPA @@ -189,6 +192,11 @@ void wpa_sm_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid, const void *network_ctx); void wpa_sm_drop_sa(struct wpa_sm *sm); +struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_get(struct wpa_sm *sm, + const u8 *aa, + const u8 *pmkid, + const void *network_ctx, + int akmp); int wpa_sm_has_ptk(struct wpa_sm *sm); int wpa_sm_has_ptk_installed(struct wpa_sm *sm); @@ -365,6 +373,13 @@ static inline void wpa_sm_drop_sa(struct wpa_sm *sm) { } +static inline struct rsn_pmksa_cache_entry * +wpa_sm_pmksa_cache_get(struct wpa_sm *sm, const u8 *aa, const u8 *pmkid, + const void *network_ctx, int akmp) +{ + return NULL; +} + static inline int wpa_sm_has_ptk(struct wpa_sm *sm) { return 0; @@ -415,6 +430,13 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap, const u8 *mdie); +#ifdef CONFIG_PASN + +int wpa_pasn_ft_derive_pmk_r1(struct wpa_sm *sm, int akmp, const u8 *r1kh_id, + u8 *pmk_r1, size_t *pmk_r1_len, u8 *pmk_r1_name); + +#endif /* CONFIG_PASN */ + #else /* CONFIG_IEEE80211R */ static inline int @@ -458,6 +480,16 @@ wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len, return -1; } +#ifdef CONFIG_PASN + +int wpa_pasn_ft_derive_pmk_r1(struct wpa_sm *sm, int akmp, const u8 *r1kh_id, + u8 *pmk_r1, size_t *pmk_r1_len, u8 *pmk_r1_name) +{ + return -1; +} + +#endif /* CONFIG_PASN */ + #endif /* CONFIG_IEEE80211R */ @@ -506,5 +538,7 @@ int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid, void wpa_sm_set_reset_fils_completed(struct wpa_sm *sm, int set); void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id); void wpa_sm_set_dpp_z(struct wpa_sm *sm, const struct wpabuf *z); +void wpa_pasn_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, + const u8 *pmkid, const u8 *bssid, int key_mgmt); #endif /* WPA_H */ diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c index bf73376..c26f1a5 100644 --- a/src/rsn_supp/wpa_ft.c +++ b/src/rsn_supp/wpa_ft.c @@ -24,6 +24,15 @@ #ifdef CONFIG_IEEE80211R +#ifdef CONFIG_PASN +static void wpa_ft_pasn_store_r1kh(struct wpa_sm *sm, const u8 *bssid); +#else /* CONFIG_PASN */ +static void wpa_ft_pasn_store_r1kh(struct wpa_sm *sm, const u8 *bssid) +{ +} +#endif /* CONFIG_PASN */ + + int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, struct wpa_ptk *ptk) { @@ -31,7 +40,7 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, const u8 *anonce = key->key_nonce; int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt); const u8 *mpmk; - size_t mpmk_len; + size_t mpmk_len, kdk_len; if (sm->xxkey_len > 0) { mpmk = sm->xxkey; @@ -56,9 +65,20 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, sm->r1kh_id, sm->own_addr, sm->pmk_r1, sm->pmk_r1_name) < 0) return -1; + + wpa_ft_pasn_store_r1kh(sm, src_addr); + + if (sm->force_kdk_derivation || + (sm->secure_ltf && sm->ap_rsnxe && sm->ap_rsnxe_len >= 4 && + sm->ap_rsnxe[3] & BIT(WLAN_RSNX_CAPAB_SECURE_LTF - 8))) + kdk_len = WPA_KDK_MAX_LEN; + else + kdk_len = 0; + return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce, anonce, sm->own_addr, sm->bssid, sm->pmk_r1_name, ptk, - ptk_name, sm->key_mgmt, sm->pairwise_cipher); + ptk_name, sm->key_mgmt, sm->pairwise_cipher, + kdk_len); } @@ -448,6 +468,8 @@ static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid) return -1; } + wpa_sm_store_ptk(sm, sm->bssid, sm->pairwise_cipher, + sm->dot11RSNAConfigPMKLifetime, &sm->ptk); return 0; } @@ -524,7 +546,7 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, int ret; const u8 *bssid; const u8 *kck; - size_t kck_len; + size_t kck_len, kdk_len; int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt); const u8 *anonce, *snonce; @@ -646,10 +668,21 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, sm->pmk_r1_len = sm->pmk_r0_len; bssid = target_ap; + + wpa_ft_pasn_store_r1kh(sm, bssid); + + if (sm->force_kdk_derivation || + (sm->secure_ltf && sm->ap_rsnxe && sm->ap_rsnxe_len >= 4 && + sm->ap_rsnxe[3] & BIT(WLAN_RSNX_CAPAB_SECURE_LTF - 8))) + kdk_len = WPA_KDK_MAX_LEN; + else + kdk_len = 0; + if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce, anonce, sm->own_addr, bssid, sm->pmk_r1_name, &sm->ptk, ptk_name, sm->key_mgmt, - sm->pairwise_cipher) < 0) + sm->pairwise_cipher, + kdk_len) < 0) return -1; if (wpa_key_mgmt_fils(sm->key_mgmt)) { @@ -1238,4 +1271,88 @@ int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap, return 0; } + +#ifdef CONFIG_PASN + +static struct pasn_ft_r1kh * wpa_ft_pasn_get_r1kh(struct wpa_sm *sm, + const u8 *bssid) +{ + size_t i; + + for (i = 0; i < sm->n_pasn_r1kh; i++) + if (os_memcmp(sm->pasn_r1kh[i].bssid, bssid, ETH_ALEN) == 0) + return &sm->pasn_r1kh[i]; + + return NULL; +} + + +static void wpa_ft_pasn_store_r1kh(struct wpa_sm *sm, const u8 *bssid) +{ + struct pasn_ft_r1kh *tmp = wpa_ft_pasn_get_r1kh(sm, bssid); + + if (tmp) + return; + + tmp = os_realloc_array(sm->pasn_r1kh, sm->n_pasn_r1kh + 1, + sizeof(*tmp)); + if (!tmp) { + wpa_printf(MSG_DEBUG, "PASN: FT: Failed to store R1KH"); + return; + } + + sm->pasn_r1kh = tmp; + tmp = &sm->pasn_r1kh[sm->n_pasn_r1kh]; + + wpa_printf(MSG_DEBUG, "PASN: FT: Store R1KH for " MACSTR, + MAC2STR(bssid)); + + os_memcpy(tmp->bssid, bssid, ETH_ALEN); + os_memcpy(tmp->r1kh_id, sm->r1kh_id, FT_R1KH_ID_LEN); + + sm->n_pasn_r1kh++; +} + + +int wpa_pasn_ft_derive_pmk_r1(struct wpa_sm *sm, int akmp, const u8 *bssid, + u8 *pmk_r1, size_t *pmk_r1_len, u8 *pmk_r1_name) +{ + struct pasn_ft_r1kh *r1kh_entry; + + if (sm->key_mgmt != (unsigned int) akmp) { + wpa_printf(MSG_DEBUG, + "PASN: FT: Key management mismatch: %u != %u", + sm->key_mgmt, akmp); + return -1; + } + + r1kh_entry = wpa_ft_pasn_get_r1kh(sm, bssid); + if (!r1kh_entry) { + wpa_printf(MSG_DEBUG, + "PASN: FT: Cannot find R1KH-ID for " MACSTR, + MAC2STR(bssid)); + return -1; + } + + /* + * Note: PMK R0 etc. were already derived and are maintained by the + * state machine, and as the same key hierarchy is used, there is no + * need to derive them again, so only derive PMK R1 etc. + */ + if (wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_len, sm->pmk_r0_name, + r1kh_entry->r1kh_id, sm->own_addr, pmk_r1, + pmk_r1_name) < 0) + return -1; + + *pmk_r1_len = sm->pmk_r0_len; + + wpa_hexdump_key(MSG_DEBUG, "PASN: FT: PMK-R1", pmk_r1, sm->pmk_r0_len); + wpa_hexdump(MSG_DEBUG, "PASN: FT: PMKR1Name", pmk_r1_name, + WPA_PMK_NAME_LEN); + + return 0; +} + +#endif /* CONFIG_PASN */ + #endif /* CONFIG_IEEE80211R */ diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 96b07fc..1dc9639 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -14,6 +14,11 @@ struct wpa_tdls_peer; struct wpa_eapol_key; +struct pasn_ft_r1kh { + u8 bssid[ETH_ALEN]; + u8 r1kh_id[FT_R1KH_ID_LEN]; +}; + /** * struct wpa_sm - Internal WPA state machine data */ @@ -73,6 +78,12 @@ struct wpa_sm { * to be used */ int keyidx_active; /* Key ID for the active TK */ + /* + * If set Key Derivation Key should be derived as part of PMK to + * PTK derivation regardless of advertised capabilities. + */ + bool force_kdk_derivation; + u8 own_addr[ETH_ALEN]; const char *ifname; const char *bridge_ifname; @@ -95,7 +106,11 @@ struct wpa_sm { int mfp; /* 0 = disabled, 1 = optional, 2 = mandatory */ int ocv; /* Operating Channel Validation */ int sae_pwe; /* SAE PWE generation options */ - int sae_pk; /* whether SAE-PK is used */ + + unsigned int sae_pk:1; /* whether SAE-PK is used */ + unsigned int secure_ltf:1; + unsigned int secure_rtt:1; + unsigned int prot_range_neg:1; u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */ size_t assoc_wpa_ie_len; @@ -146,6 +161,17 @@ struct wpa_sm { u8 mdie_ft_capab; /* FT Capability and Policy from target AP MDIE */ u8 *assoc_resp_ies; /* MDIE and FTIE from (Re)Association Response */ size_t assoc_resp_ies_len; +#ifdef CONFIG_PASN + /* + * Currently, the WPA state machine stores the PMK-R1, PMK-R1-Name and + * R1KH-ID only for the current association. As PMK-R1 is required to + * perform PASN authentication with FT, store the R1KH-ID for previous + * associations, which would later be used to derive the PMK-R1 as part + * of the PASN authentication flow. + */ + struct pasn_ft_r1kh *pasn_r1kh; + unsigned int n_pasn_r1kh; +#endif /* CONFIG_PASN */ #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_P2P @@ -441,6 +467,14 @@ static inline void wpa_sm_transition_disable(struct wpa_sm *sm, u8 bitmap) sm->ctx->transition_disable(sm->ctx->ctx, bitmap); } +static inline void wpa_sm_store_ptk(struct wpa_sm *sm, + u8 *addr, int cipher, + u32 life_time, struct wpa_ptk *ptk) +{ + if (sm->ctx->store_ptk) + sm->ctx->store_ptk(sm->ctx->ctx, addr, cipher, life_time, + ptk); +} int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk, int ver, const u8 *dest, u16 proto, diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c index 20fdd69..3ba722f 100644 --- a/src/rsn_supp/wpa_ie.c +++ b/src/rsn_supp/wpa_ie.c @@ -354,25 +354,38 @@ int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len) int wpa_gen_rsnxe(struct wpa_sm *sm, u8 *rsnxe, size_t rsnxe_len) { u8 *pos = rsnxe; + u16 capab = 0; + size_t flen; - if (!wpa_key_mgmt_sae(sm->key_mgmt)) - return 0; /* SAE not in use */ - if (sm->sae_pwe != 1 && sm->sae_pwe != 2 && !sm->sae_pk) - return 0; /* no supported extended RSN capabilities */ + if (wpa_key_mgmt_sae(sm->key_mgmt) && + (sm->sae_pwe == 1 || sm->sae_pwe == 2 || sm->sae_pk)) { + capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E); +#ifdef CONFIG_SAE_PK + if (sm->sae_pk) + capab |= BIT(WLAN_RSNX_CAPAB_SAE_PK); +#endif /* CONFIG_SAE_PK */ + } - if (rsnxe_len < 3) + if (sm->secure_ltf) + capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF); + if (sm->secure_rtt) + capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT); + if (sm->prot_range_neg) + capab |= BIT(WLAN_RSNX_CAPAB_PROT_RANGE_NEG); + + flen = (capab & 0xff00) ? 2 : 1; + if (!capab) + return 0; /* no supported extended RSN capabilities */ + if (rsnxe_len < 2 + flen) return -1; + capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */ *pos++ = WLAN_EID_RSNX; - *pos++ = 1; - /* bits 0-3 = 0 since only one octet of Extended RSN Capabilities is - * used for now */ - *pos = BIT(WLAN_RSNX_CAPAB_SAE_H2E); -#ifdef CONFIG_SAE_PK - if (sm->sae_pk) - *pos |= BIT(WLAN_RSNX_CAPAB_SAE_PK); -#endif /* CONFIG_SAE_PK */ - pos++; + *pos++ = flen; + *pos++ = capab & 0x00ff; + capab >>= 8; + if (capab) + *pos++ = capab; return pos - rsnxe; } diff --git a/src/utils/Makefile b/src/utils/Makefile index e8ad997..d995b81 100644 --- a/src/utils/Makefile +++ b/src/utils/Makefile @@ -6,6 +6,7 @@ LIB_OBJS= \ base64.o \ bitfield.o \ common.o \ + config.o \ crc32.o \ ip_addr.o \ json.o \ diff --git a/src/utils/config.c b/src/utils/config.c new file mode 100644 index 0000000..22aa221 --- /dev/null +++ b/src/utils/config.c @@ -0,0 +1,97 @@ +/* + * Configuration parsing + * Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "utils/config.h" +#include "common.h" + + +static int newline_terminated(const char *buf, size_t buflen) +{ + size_t len = os_strlen(buf); + if (len == 0) + return 0; + if (len == buflen - 1 && buf[buflen - 1] != '\r' && + buf[len - 1] != '\n') + return 0; + return 1; +} + + +static void skip_line_end(FILE *stream) +{ + char buf[100]; + while (fgets(buf, sizeof(buf), stream)) { + buf[sizeof(buf) - 1] = '\0'; + if (newline_terminated(buf, sizeof(buf))) + return; + } +} + + +char * wpa_config_get_line(char *s, int size, FILE *stream, int *line, + char **_pos) +{ + char *pos, *end, *sstart; + + while (fgets(s, size, stream)) { + (*line)++; + s[size - 1] = '\0'; + if (!newline_terminated(s, size)) { + /* + * The line was truncated - skip rest of it to avoid + * confusing error messages. + */ + wpa_printf(MSG_INFO, "Long line in configuration file " + "truncated"); + skip_line_end(stream); + } + pos = s; + + /* Skip white space from the beginning of line. */ + while (*pos == ' ' || *pos == '\t' || *pos == '\r') + pos++; + + /* Skip comment lines and empty lines */ + if (*pos == '#' || *pos == '\n' || *pos == '\0') + continue; + + /* + * Remove # comments unless they are within a double quoted + * string. + */ + sstart = os_strchr(pos, '"'); + if (sstart) + sstart = os_strrchr(sstart + 1, '"'); + if (!sstart) + sstart = pos; + end = os_strchr(sstart, '#'); + if (end) + *end-- = '\0'; + else + end = pos + os_strlen(pos) - 1; + + /* Remove trailing white space. */ + while (end > pos && + (*end == '\n' || *end == ' ' || *end == '\t' || + *end == '\r')) + *end-- = '\0'; + + if (*pos == '\0') + continue; + + if (_pos) + *_pos = pos; + return pos; + } + + if (_pos) + *_pos = NULL; + return NULL; +} diff --git a/src/utils/config.h b/src/utils/config.h new file mode 100644 index 0000000..074a88a --- /dev/null +++ b/src/utils/config.h @@ -0,0 +1,29 @@ +/* + * Configuration parsing + * Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef UTILS_CONFIG_H +#define UTILS_CONFIG_H + +/** + * wpa_config_get_line - Read the next configuration file line + * @s: Buffer for the line + * @size: The buffer length + * @stream: File stream to read from + * @line: Pointer to a variable storing the file line number + * @_pos: Buffer for the pointer to the beginning of data on the text line or + * %NULL if not needed (returned value used instead) + * Returns: Pointer to the beginning of data on the text line or %NULL if no + * more text lines are available. + * + * This function reads the next non-empty line from the configuration file and + * removes comments. The returned string is guaranteed to be null-terminated. + */ +char * wpa_config_get_line(char *s, int size, FILE *stream, int *line, + char **_pos); + +#endif /* UTILS_CONFIG_H */ diff --git a/src/utils/ext_password.c b/src/utils/ext_password.c index 5615bd7..cbf92de 100644 --- a/src/utils/ext_password.c +++ b/src/utils/ext_password.c @@ -20,6 +20,9 @@ static const struct ext_password_backend *backends[] = { #ifdef CONFIG_EXT_PASSWORD_TEST &ext_password_test, #endif /* CONFIG_EXT_PASSWORD_TEST */ +#ifdef CONFIG_EXT_PASSWORD_FILE + &ext_password_file, +#endif /* CONFIG_EXT_PASSWORD_FILE */ NULL }; diff --git a/src/utils/ext_password_file.c b/src/utils/ext_password_file.c new file mode 100644 index 0000000..4bb0095 --- /dev/null +++ b/src/utils/ext_password_file.c @@ -0,0 +1,136 @@ +/* + * External backend for file-backed passwords + * Copyright (c) 2021, Patrick Steinhardt <ps@pks.im> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "utils/common.h" +#include "utils/config.h" +#include "ext_password_i.h" + + +/** + * Data structure for the file-backed password backend. + */ +struct ext_password_file_data { + char *path; /* path of the password file */ +}; + + +/** + * ext_password_file_init - Initialize file-backed password backend + * @params: Parameters passed by the user. + * Returns: Pointer to the initialized backend. + * + * This function initializes a new file-backed password backend. The user is + * expected to initialize this backend with the parameters being the path of + * the file that contains the passwords. + */ +static void * ext_password_file_init(const char *params) +{ + struct ext_password_file_data *data; + + if (!params) { + wpa_printf(MSG_ERROR, "EXT PW FILE: no path given"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (!data) + return NULL; + + data->path = os_strdup(params); + if (!data->path) { + os_free(data); + return NULL; + } + + return data; +} + + +/** + * ext_password_file_deinit - Deinitialize file-backed password backend + * @ctx: The file-backed password backend + * + * This function frees all data associated with the file-backed password + * backend. + */ +static void ext_password_file_deinit(void *ctx) +{ + struct ext_password_file_data *data = ctx; + + str_clear_free(data->path); + os_free(data); +} + +/** + * ext_password_file_get - Retrieve password from the file-backed password backend + * @ctx: The file-backed password backend + * @name: Name of the password to retrieve + * Returns: Buffer containing the password if one was found or %NULL. + * + * This function tries to find a password identified by name in the password + * file. The password is expected to be stored in `NAME=PASSWORD` format. + * Comments and empty lines in the file are ignored. Invalid lines will cause + * an error message, but will not cause the function to fail. + */ +static struct wpabuf * ext_password_file_get(void *ctx, const char *name) +{ + struct ext_password_file_data *data = ctx; + struct wpabuf *password = NULL; + char buf[512], *pos; + int line = 0; + FILE *f; + + f = fopen(data->path, "r"); + if (!f) { + wpa_printf(MSG_ERROR, + "EXT PW FILE: could not open file '%s': %s", + data->path, strerror(errno)); + return NULL; + } + + wpa_printf(MSG_DEBUG, "EXT PW FILE: get(%s)", name); + + while (wpa_config_get_line(buf, sizeof(buf), f, &line, &pos)) { + char *sep = os_strchr(pos, '='); + + if (!sep) { + wpa_printf(MSG_ERROR, "Invalid password line %d.", + line); + continue; + } + + if (!sep[1]) { + wpa_printf(MSG_ERROR, "No password for line %d.", line); + continue; + + } + + if (os_strncmp(name, pos, sep - pos) != 0) + continue; + + password = wpabuf_alloc_copy(sep + 1, os_strlen(sep + 1)); + goto done; + } + + wpa_printf(MSG_ERROR, "Password for '%s' was not found.", name); + +done: + forced_memzero(buf, sizeof(buf)); + fclose(f); + return password; +} + + +const struct ext_password_backend ext_password_file = { + .name = "file", + .init = ext_password_file_init, + .deinit = ext_password_file_deinit, + .get = ext_password_file_get, +}; diff --git a/src/utils/ext_password_i.h b/src/utils/ext_password_i.h index 948eaf5..872ccd1 100644 --- a/src/utils/ext_password_i.h +++ b/src/utils/ext_password_i.h @@ -26,4 +26,8 @@ struct wpabuf * ext_password_alloc(size_t len); extern const struct ext_password_backend ext_password_test; #endif /* CONFIG_EXT_PASSWORD_TEST */ +#ifdef CONFIG_EXT_PASSWORD_FILE +extern const struct ext_password_backend ext_password_file; +#endif /* CONFIG_EXT_PASSWORD_FILE */ + #endif /* EXT_PASSWORD_I_H */ diff --git a/src/utils/list.h b/src/utils/list.h index 5298c26..aa62c08 100644 --- a/src/utils/list.h +++ b/src/utils/list.h @@ -76,8 +76,8 @@ static inline unsigned int dl_list_len(const struct dl_list *list) dl_list_entry((list)->prev, type, member)) #define dl_list_for_each(item, list, type, member) \ - for (item = dl_list_first((list), type, member); \ - item && item != dl_list_entry((list), type, member); \ + for (item = dl_list_entry((list)->next, type, member); \ + &item->member != (list); \ item = dl_list_entry(item->member.next, type, member)) #define dl_list_for_each_safe(item, n, list, type, member) \ diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c index 6f0c177..1de3720 100644 --- a/src/utils/os_unix.c +++ b/src/utils/os_unix.c @@ -39,7 +39,7 @@ static struct dl_list alloc_list = DL_LIST_HEAD_INIT(alloc_list); struct os_alloc_trace { unsigned int magic; - struct dl_list list; + struct dl_list list __attribute__((aligned(16))); size_t len; WPA_TRACE_INFO } __attribute__((aligned(16))); diff --git a/src/utils/platform.h b/src/utils/platform.h index 813987e..b2ad856 100644 --- a/src/utils/platform.h +++ b/src/utils/platform.h @@ -1,21 +1,18 @@ +/* + * Platform definitions for Radiotap parser + * Copyright (c) 2021, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + #ifndef PLATFORM_H #define PLATFORM_H #include "includes.h" #include "common.h" -#define le16_to_cpu le_to_host16 -#define le32_to_cpu le_to_host32 - -#define get_unaligned(p) \ -({ \ - struct packed_dummy_struct { \ - typeof(*(p)) __val; \ - } __attribute__((packed)) *__ptr = (void *) (p); \ - \ - __ptr->__val; \ -}) -#define get_unaligned_le16(p) le16_to_cpu(get_unaligned((le16 *)(p))) -#define get_unaligned_le32(p) le32_to_cpu(get_unaligned((le32 *)(p))) +#define get_unaligned_le16(p) WPA_GET_LE16((void *) (p)) +#define get_unaligned_le32(p) WPA_GET_LE32((void *) (p)) #endif /* PLATFORM_H */ diff --git a/src/utils/radiotap.c b/src/utils/radiotap.c index 71996eb..6dfe298 100644 --- a/src/utils/radiotap.c +++ b/src/utils/radiotap.c @@ -8,10 +8,8 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See COPYING for more details. + * Alternatively, this software may be distributed under the terms of ISC + * license, see COPYING for more details. */ #include "platform.h" #include "radiotap_iter.h" @@ -39,6 +37,8 @@ static const struct radiotap_align_size rtap_namespace_sizes[] = { [IEEE80211_RADIOTAP_DATA_RETRIES] = { .align = 1, .size = 1, }, [IEEE80211_RADIOTAP_MCS] = { .align = 1, .size = 3, }, [IEEE80211_RADIOTAP_AMPDU_STATUS] = { .align = 4, .size = 8, }, + [IEEE80211_RADIOTAP_VHT] = { .align = 2, .size = 12, }, + [IEEE80211_RADIOTAP_TIMESTAMP] = { .align = 8, .size = 12, }, /* * add more here as they are defined in radiotap.h */ @@ -111,7 +111,7 @@ int ieee80211_radiotap_iterator_init( iterator->_arg = (uint8_t *)radiotap_header + sizeof(*radiotap_header); iterator->_next_ns_data = NULL; iterator->_reset_on_ext = 0; - iterator->_next_bitmap = &radiotap_header->it_present; + iterator->_next_bitmap = (le32 *) (((u8 *) radiotap_header) + offsetof(struct ieee80211_radiotap_header, it_present)); iterator->_next_bitmap++; iterator->_vns = vns; iterator->current_namespace = &radiotap_ns; @@ -222,7 +222,7 @@ static int find_override(struct ieee80211_radiotap_iterator *iterator, * present fields. @this_arg can be changed by the caller (eg, * incremented to move inside a compound argument like * IEEE80211_RADIOTAP_CHANNEL). The args pointed to are in - * little-endian format whatever the endianess of your CPU. + * little-endian format whatever the endianness of your CPU. * * Alignment Gotcha: * You must take care when dereferencing iterator.this_arg diff --git a/src/utils/radiotap.h b/src/utils/radiotap.h index 460af23..488d5a3 100644 --- a/src/utils/radiotap.h +++ b/src/utils/radiotap.h @@ -1,190 +1,51 @@ -/*- - * Copyright (c) 2003, 2004 David Young. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of David Young may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID - * YOUNG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY - * OF SUCH DAMAGE. - */ - /* - * Modifications to fit into the linux IEEE 802.11 stack, - * Mike Kershaw (dragorn@kismetwireless.net) + * Copyright (c) 2017 Intel Deutschland GmbH + * + * 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. */ +#ifndef __RADIOTAP_H +#define __RADIOTAP_H -#ifndef IEEE80211RADIOTAP_H -#define IEEE80211RADIOTAP_H - -#include <stdint.h> - -/* Base version of the radiotap packet header data */ -#define PKTHDR_RADIOTAP_VERSION 0 - -/* A generic radio capture format is desirable. There is one for - * Linux, but it is neither rigidly defined (there were not even - * units given for some fields) nor easily extensible. - * - * I suggest the following extensible radio capture format. It is - * based on a bitmap indicating which fields are present. - * - * I am trying to describe precisely what the application programmer - * should expect in the following, and for that reason I tell the - * units and origin of each measurement (where it applies), or else I - * use sufficiently weaselly language ("is a monotonically nondecreasing - * function of...") that I cannot set false expectations for lawyerly - * readers. - */ - -/* The radio capture header precedes the 802.11 header. - * All data in the header is little endian on all platforms. +/** + * struct ieee82011_radiotap_header - base radiotap header */ struct ieee80211_radiotap_header { - uint8_t it_version; /* Version 0. Only increases - * for drastic changes, - * introduction of compatible - * new fields does not count. - */ + /** + * @it_version: radiotap version, always 0 + */ + uint8_t it_version; + + /** + * @it_pad: padding (or alignment) + */ uint8_t it_pad; - le16 it_len; /* length of the whole - * header in bytes, including - * it_version, it_pad, - * it_len, and data fields. - */ - le32 it_present; /* A bitmap telling which - * fields are present. Set bit 31 - * (0x80000000) to extend the - * bitmap by another 32 bits. - * Additional extensions are made - * by setting bit 31. - */ -}; -/* Name Data type Units - * ---- --------- ----- - * - * IEEE80211_RADIOTAP_TSFT __le64 microseconds - * - * Value in microseconds of the MAC's 64-bit 802.11 Time - * Synchronization Function timer when the first bit of the - * MPDU arrived at the MAC. For received frames, only. - * - * IEEE80211_RADIOTAP_CHANNEL 2 x uint16_t MHz, bitmap - * - * Tx/Rx frequency in MHz, followed by flags (see below). - * - * IEEE80211_RADIOTAP_FHSS uint16_t see below - * - * For frequency-hopping radios, the hop set (first byte) - * and pattern (second byte). - * - * IEEE80211_RADIOTAP_RATE u8 500kb/s - * - * Tx/Rx data rate - * - * IEEE80211_RADIOTAP_DBM_ANTSIGNAL s8 decibels from - * one milliwatt (dBm) - * - * RF signal power at the antenna, decibel difference from - * one milliwatt. - * - * IEEE80211_RADIOTAP_DBM_ANTNOISE s8 decibels from - * one milliwatt (dBm) - * - * RF noise power at the antenna, decibel difference from one - * milliwatt. - * - * IEEE80211_RADIOTAP_DB_ANTSIGNAL u8 decibel (dB) - * - * RF signal power at the antenna, decibel difference from an - * arbitrary, fixed reference. - * - * IEEE80211_RADIOTAP_DB_ANTNOISE u8 decibel (dB) - * - * RF noise power at the antenna, decibel difference from an - * arbitrary, fixed reference point. - * - * IEEE80211_RADIOTAP_LOCK_QUALITY uint16_t unitless - * - * Quality of Barker code lock. Unitless. Monotonically - * nondecreasing with "better" lock strength. Called "Signal - * Quality" in datasheets. (Is there a standard way to measure - * this?) - * - * IEEE80211_RADIOTAP_TX_ATTENUATION uint16_t unitless - * - * Transmit power expressed as unitless distance from max - * power set at factory calibration. 0 is max power. - * Monotonically nondecreasing with lower power levels. - * - * IEEE80211_RADIOTAP_DB_TX_ATTENUATION uint16_t decibels (dB) - * - * Transmit power expressed as decibel distance from max power - * set at factory calibration. 0 is max power. Monotonically - * nondecreasing with lower power levels. - * - * IEEE80211_RADIOTAP_DBM_TX_POWER s8 decibels from - * one milliwatt (dBm) - * - * Transmit power expressed as dBm (decibels from a 1 milliwatt - * reference). This is the absolute power level measured at - * the antenna port. - * - * IEEE80211_RADIOTAP_FLAGS u8 bitmap - * - * Properties of transmitted and received frames. See flags - * defined below. - * - * IEEE80211_RADIOTAP_ANTENNA u8 antenna index - * - * Unitless indication of the Rx/Tx antenna for this packet. - * The first antenna is antenna 0. - * - * IEEE80211_RADIOTAP_RX_FLAGS uint16_t bitmap - * - * Properties of received frames. See flags defined below. - * - * IEEE80211_RADIOTAP_TX_FLAGS uint16_t bitmap - * - * Properties of transmitted frames. See flags defined below. - * - * IEEE80211_RADIOTAP_RTS_RETRIES u8 data - * - * Number of rts retries a transmitted frame used. - * - * IEEE80211_RADIOTAP_DATA_RETRIES u8 data - * - * Number of unicast retries a transmitted frame used. - * - * IEEE80211_RADIOTAP_MCS u8, u8, u8 unitless - * - * Contains a bitmap of known fields/flags, the flags, and - * the MCS index. - * - * IEEE80211_RADIOTAP_AMPDU_STATUS u32, u16, u8, u8 unitlesss - * - * Contains the AMPDU information for the subframe. - */ -enum ieee80211_radiotap_type { + /** + * @it_len: overall radiotap header length + */ + le16 it_len; + + /** + * @it_present: (first) present word + */ + le32 it_present; +} STRUCT_PACKED; + +/* version is always 0 */ +#define PKTHDR_RADIOTAP_VERSION 0 + +/* see the radiotap website for the descriptions */ +enum ieee80211_radiotap_presence { IEEE80211_RADIOTAP_TSFT = 0, IEEE80211_RADIOTAP_FLAGS = 1, IEEE80211_RADIOTAP_RATE = 2, @@ -203,9 +64,11 @@ enum ieee80211_radiotap_type { IEEE80211_RADIOTAP_TX_FLAGS = 15, IEEE80211_RADIOTAP_RTS_RETRIES = 16, IEEE80211_RADIOTAP_DATA_RETRIES = 17, - + /* 18 is XChannel, but it's not defined yet */ IEEE80211_RADIOTAP_MCS = 19, IEEE80211_RADIOTAP_AMPDU_STATUS = 20, + IEEE80211_RADIOTAP_VHT = 21, + IEEE80211_RADIOTAP_TIMESTAMP = 22, /* valid in every it_present bitmap, even vendor namespaces */ IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29, @@ -213,79 +76,125 @@ enum ieee80211_radiotap_type { IEEE80211_RADIOTAP_EXT = 31 }; -/* Channel flags. */ -#define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */ -#define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */ -#define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */ -#define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */ -#define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */ -#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ -#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ -#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ +/* for IEEE80211_RADIOTAP_FLAGS */ +enum ieee80211_radiotap_flags { + IEEE80211_RADIOTAP_F_CFP = 0x01, + IEEE80211_RADIOTAP_F_SHORTPRE = 0x02, + IEEE80211_RADIOTAP_F_WEP = 0x04, + IEEE80211_RADIOTAP_F_FRAG = 0x08, + IEEE80211_RADIOTAP_F_FCS = 0x10, + IEEE80211_RADIOTAP_F_DATAPAD = 0x20, + IEEE80211_RADIOTAP_F_BADFCS = 0x40, +}; + +/* for IEEE80211_RADIOTAP_CHANNEL */ +enum ieee80211_radiotap_channel_flags { + IEEE80211_CHAN_CCK = 0x0020, + IEEE80211_CHAN_OFDM = 0x0040, + IEEE80211_CHAN_2GHZ = 0x0080, + IEEE80211_CHAN_5GHZ = 0x0100, + IEEE80211_CHAN_DYN = 0x0400, + IEEE80211_CHAN_HALF = 0x4000, + IEEE80211_CHAN_QUARTER = 0x8000, +}; + +/* for IEEE80211_RADIOTAP_RX_FLAGS */ +enum ieee80211_radiotap_rx_flags { + IEEE80211_RADIOTAP_F_RX_BADPLCP = 0x0002, +}; + +/* for IEEE80211_RADIOTAP_TX_FLAGS */ +enum ieee80211_radiotap_tx_flags { + IEEE80211_RADIOTAP_F_TX_FAIL = 0x0001, + IEEE80211_RADIOTAP_F_TX_CTS = 0x0002, + IEEE80211_RADIOTAP_F_TX_RTS = 0x0004, + IEEE80211_RADIOTAP_F_TX_NOACK = 0x0008, +}; + +/* for IEEE80211_RADIOTAP_MCS "have" flags */ +enum ieee80211_radiotap_mcs_have { + IEEE80211_RADIOTAP_MCS_HAVE_BW = 0x01, + IEEE80211_RADIOTAP_MCS_HAVE_MCS = 0x02, + IEEE80211_RADIOTAP_MCS_HAVE_GI = 0x04, + IEEE80211_RADIOTAP_MCS_HAVE_FMT = 0x08, + IEEE80211_RADIOTAP_MCS_HAVE_FEC = 0x10, + IEEE80211_RADIOTAP_MCS_HAVE_STBC = 0x20, +}; -/* For IEEE80211_RADIOTAP_FLAGS */ -#define IEEE80211_RADIOTAP_F_CFP 0x01 /* sent/received - * during CFP - */ -#define IEEE80211_RADIOTAP_F_SHORTPRE 0x02 /* sent/received - * with short - * preamble - */ -#define IEEE80211_RADIOTAP_F_WEP 0x04 /* sent/received - * with WEP encryption - */ -#define IEEE80211_RADIOTAP_F_FRAG 0x08 /* sent/received - * with fragmentation - */ -#define IEEE80211_RADIOTAP_F_FCS 0x10 /* frame includes FCS */ -#define IEEE80211_RADIOTAP_F_DATAPAD 0x20 /* frame has padding between - * 802.11 header and payload - * (to 32-bit boundary) - */ -#define IEEE80211_RADIOTAP_F_BADFCS 0x40 /* frame failed FCS check */ +enum ieee80211_radiotap_mcs_flags { + IEEE80211_RADIOTAP_MCS_BW_MASK = 0x03, + IEEE80211_RADIOTAP_MCS_BW_20 = 0, + IEEE80211_RADIOTAP_MCS_BW_40 = 1, + IEEE80211_RADIOTAP_MCS_BW_20L = 2, + IEEE80211_RADIOTAP_MCS_BW_20U = 3, + + IEEE80211_RADIOTAP_MCS_SGI = 0x04, + IEEE80211_RADIOTAP_MCS_FMT_GF = 0x08, + IEEE80211_RADIOTAP_MCS_FEC_LDPC = 0x10, + IEEE80211_RADIOTAP_MCS_STBC_MASK = 0x60, + IEEE80211_RADIOTAP_MCS_STBC_1 = 1, + IEEE80211_RADIOTAP_MCS_STBC_2 = 2, + IEEE80211_RADIOTAP_MCS_STBC_3 = 3, + IEEE80211_RADIOTAP_MCS_STBC_SHIFT = 5, +}; -/* For IEEE80211_RADIOTAP_RX_FLAGS */ -#define IEEE80211_RADIOTAP_F_RX_BADPLCP 0x0002 /* bad PLCP */ +/* for IEEE80211_RADIOTAP_AMPDU_STATUS */ +enum ieee80211_radiotap_ampdu_flags { + IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN = 0x0001, + IEEE80211_RADIOTAP_AMPDU_IS_ZEROLEN = 0x0002, + IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN = 0x0004, + IEEE80211_RADIOTAP_AMPDU_IS_LAST = 0x0008, + IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR = 0x0010, + IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN = 0x0020, +}; -/* For IEEE80211_RADIOTAP_TX_FLAGS */ -#define IEEE80211_RADIOTAP_F_TX_FAIL 0x0001 /* failed due to excessive - * retries */ -#define IEEE80211_RADIOTAP_F_TX_CTS 0x0002 /* used cts 'protection' */ -#define IEEE80211_RADIOTAP_F_TX_RTS 0x0004 /* used rts/cts handshake */ -#define IEEE80211_RADIOTAP_F_TX_NOACK 0x0008 /* don't expect an ACK */ +/* for IEEE80211_RADIOTAP_VHT */ +enum ieee80211_radiotap_vht_known { + IEEE80211_RADIOTAP_VHT_KNOWN_STBC = 0x0001, + IEEE80211_RADIOTAP_VHT_KNOWN_TXOP_PS_NA = 0x0002, + IEEE80211_RADIOTAP_VHT_KNOWN_GI = 0x0004, + IEEE80211_RADIOTAP_VHT_KNOWN_SGI_NSYM_DIS = 0x0008, + IEEE80211_RADIOTAP_VHT_KNOWN_LDPC_EXTRA_OFDM_SYM = 0x0010, + IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED = 0x0020, + IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH = 0x0040, + IEEE80211_RADIOTAP_VHT_KNOWN_GROUP_ID = 0x0080, + IEEE80211_RADIOTAP_VHT_KNOWN_PARTIAL_AID = 0x0100, +}; -/* For IEEE80211_RADIOTAP_AMPDU_STATUS */ -#define IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN 0x0001 -#define IEEE80211_RADIOTAP_AMPDU_IS_ZEROLEN 0x0002 -#define IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN 0x0004 -#define IEEE80211_RADIOTAP_AMPDU_IS_LAST 0x0008 -#define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR 0x0010 -#define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN 0x0020 +enum ieee80211_radiotap_vht_flags { + IEEE80211_RADIOTAP_VHT_FLAG_STBC = 0x01, + IEEE80211_RADIOTAP_VHT_FLAG_TXOP_PS_NA = 0x02, + IEEE80211_RADIOTAP_VHT_FLAG_SGI = 0x04, + IEEE80211_RADIOTAP_VHT_FLAG_SGI_NSYM_M10_9 = 0x08, + IEEE80211_RADIOTAP_VHT_FLAG_LDPC_EXTRA_OFDM_SYM = 0x10, + IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED = 0x20, +}; -/* For IEEE80211_RADIOTAP_MCS */ -#define IEEE80211_RADIOTAP_MCS_HAVE_BW 0x01 -#define IEEE80211_RADIOTAP_MCS_HAVE_MCS 0x02 -#define IEEE80211_RADIOTAP_MCS_HAVE_GI 0x04 -#define IEEE80211_RADIOTAP_MCS_HAVE_FMT 0x08 -#define IEEE80211_RADIOTAP_MCS_HAVE_FEC 0x10 -#define IEEE80211_RADIOTAP_MCS_HAVE_STBC 0x20 -#define IEEE80211_RADIOTAP_MCS_HAVE_NESS 0x40 -#define IEEE80211_RADIOTAP_MCS_NESS_BIT1 0x80 +enum ieee80211_radiotap_vht_coding { + IEEE80211_RADIOTAP_CODING_LDPC_USER0 = 0x01, + IEEE80211_RADIOTAP_CODING_LDPC_USER1 = 0x02, + IEEE80211_RADIOTAP_CODING_LDPC_USER2 = 0x04, + IEEE80211_RADIOTAP_CODING_LDPC_USER3 = 0x08, +}; +/* for IEEE80211_RADIOTAP_TIMESTAMP */ +enum ieee80211_radiotap_timestamp_unit_spos { + IEEE80211_RADIOTAP_TIMESTAMP_UNIT_MASK = 0x000F, + IEEE80211_RADIOTAP_TIMESTAMP_UNIT_MS = 0x0000, + IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US = 0x0001, + IEEE80211_RADIOTAP_TIMESTAMP_UNIT_NS = 0x0003, + IEEE80211_RADIOTAP_TIMESTAMP_SPOS_MASK = 0x00F0, + IEEE80211_RADIOTAP_TIMESTAMP_SPOS_BEGIN_MDPU = 0x0000, + IEEE80211_RADIOTAP_TIMESTAMP_SPOS_PLCP_SIG_ACQ = 0x0010, + IEEE80211_RADIOTAP_TIMESTAMP_SPOS_EO_PPDU = 0x0020, + IEEE80211_RADIOTAP_TIMESTAMP_SPOS_EO_MPDU = 0x0030, + IEEE80211_RADIOTAP_TIMESTAMP_SPOS_UNKNOWN = 0x00F0, +}; -#define IEEE80211_RADIOTAP_MCS_BW_MASK 0x03 -#define IEEE80211_RADIOTAP_MCS_BW_20 0 -#define IEEE80211_RADIOTAP_MCS_BW_40 1 -#define IEEE80211_RADIOTAP_MCS_BW_20L 2 -#define IEEE80211_RADIOTAP_MCS_BW_20U 3 -#define IEEE80211_RADIOTAP_MCS_SGI 0x04 -#define IEEE80211_RADIOTAP_MCS_FMT_GF 0x08 -#define IEEE80211_RADIOTAP_MCS_FEC_LDPC 0x10 -#define IEEE80211_RADIOTAP_MCS_STBC_MASK 0x60 -#define IEEE80211_RADIOTAP_MCS_STBC_SHIFT 5 -#define IEEE80211_RADIOTAP_MCS_STBC_1 1 -#define IEEE80211_RADIOTAP_MCS_STBC_2 2 -#define IEEE80211_RADIOTAP_MCS_STBC_3 3 -#define IEEE80211_RADIOTAP_MCS_NESS_BIT0 0x80 +enum ieee80211_radiotap_timestamp_flags { + IEEE80211_RADIOTAP_TIMESTAMP_FLAG_64BIT = 0x00, + IEEE80211_RADIOTAP_TIMESTAMP_FLAG_32BIT = 0x01, + IEEE80211_RADIOTAP_TIMESTAMP_FLAG_ACCURACY = 0x02, +}; -#endif /* IEEE80211_RADIOTAP_H */ +#endif /* __RADIOTAP_H */ diff --git a/src/wps/wps.h b/src/wps/wps.h index 93888b0..6a12255 100644 --- a/src/wps/wps.h +++ b/src/wps/wps.h @@ -873,6 +873,11 @@ int wps_registrar_add_nfc_password_token(struct wps_registrar *reg, const u8 *oob_dev_pw, size_t oob_dev_pw_len); void wps_registrar_flush(struct wps_registrar *reg); +int wps_registrar_update_multi_ap(struct wps_registrar *reg, + const u8 *multi_ap_backhaul_ssid, + size_t multi_ap_backhaul_ssid_len, + const u8 *multi_ap_backhaul_network_key, + size_t multi_ap_backhaul_network_key_len); int wps_build_credential_wrap(struct wpabuf *msg, const struct wps_credential *cred); diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index 9e1ee36..0db9367 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -3669,6 +3669,35 @@ int wps_registrar_config_ap(struct wps_registrar *reg, } +int wps_registrar_update_multi_ap(struct wps_registrar *reg, + const u8 *multi_ap_backhaul_ssid, + size_t multi_ap_backhaul_ssid_len, + const u8 *multi_ap_backhaul_network_key, + size_t multi_ap_backhaul_network_key_len) +{ + if (multi_ap_backhaul_ssid) { + os_memcpy(reg->multi_ap_backhaul_ssid, + multi_ap_backhaul_ssid, multi_ap_backhaul_ssid_len); + reg->multi_ap_backhaul_ssid_len = multi_ap_backhaul_ssid_len; + } + + os_free(reg->multi_ap_backhaul_network_key); + reg->multi_ap_backhaul_network_key = NULL; + reg->multi_ap_backhaul_network_key_len = 0; + if (multi_ap_backhaul_network_key) { + reg->multi_ap_backhaul_network_key = + os_memdup(multi_ap_backhaul_network_key, + multi_ap_backhaul_network_key_len); + if (!reg->multi_ap_backhaul_network_key) + return -1; + reg->multi_ap_backhaul_network_key_len = + multi_ap_backhaul_network_key_len; + } + + return 0; +} + + #ifdef CONFIG_WPS_NFC int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg, |