diff options
200 files changed, 11740 insertions, 1704 deletions
diff --git a/doc/dbus.doxygen b/doc/dbus.doxygen index 06e53b9..8231aac 100644 --- a/doc/dbus.doxygen +++ b/doc/dbus.doxygen @@ -669,7 +669,7 @@ fi.w1.wpa_supplicant1.CreateInterface. <tr><td>Pairwise</td><td>as</td><td>Possible array elements: "ccmp-256", "gcmp-256", "ccmp", "gcmp", "tkip", "none"</td> <tr><td>Group</td><td>as</td><td>Possible array elements: "ccmp-256", "gcmp-256", "ccmp", "gcmp", "tkip", "wep104", "wep40"</td> <tr><td>GroupMgmt</td><td>as</td><td>Possible array elements: "aes-128-cmac", "bip-gmac-128", "bip-gmac-256", "bip-cmac-256"</td> - <tr><td>KeyMgmt</td><td>as</td><td>Possible array elements: "wpa-psk", "wpa-ft-psk", "wpa-psk-sha256", "wpa-eap", "wpa-ft-eap", "wpa-eap-sha256", "sae", "ieee8021x", "wpa-none", "wps", "none"</td> + <tr><td>KeyMgmt</td><td>as</td><td>Possible array elements: "wpa-psk", "wpa-ft-psk", "wpa-psk-sha256", "wpa-eap", "wpa-ft-eap", "wpa-eap-sha256", "sae", "owe", "ieee8021x", "wpa-none", "wps", "none"</td> <tr><td>Protocol</td><td>as</td><td>Possible array elements: "rsn", "wpa"</td> <tr><td>AuthAlg</td><td>as</td><td>Possible array elements: "open", "shared", "leap"</td> <tr><td>Scan</td><td>as</td><td>Possible array elements: "active", "passive", "ssid"</td> diff --git a/hostapd/Android.mk b/hostapd/Android.mk index 6c95617..b3af968 100644 --- a/hostapd/Android.mk +++ b/hostapd/Android.mk @@ -565,6 +565,16 @@ L_CFLAGS += -DCONFIG_DPP2 endif endif +ifdef CONFIG_PASN +L_CFLAGS += -DCONFIG_PASN +L_CFLAGS += -DCONFIG_PTKSA_CACHE +NEED_HMAC_SHA256_KDF=y +NEED_HMAC_SHA384_KDF=y +NEED_SHA256=y +NEED_SHA384=y +OBJS += src/common/ptksa_cache.c +endif + ifdef CONFIG_EAP_IKEV2 L_CFLAGS += -DEAP_SERVER_IKEV2 OBJS += src/eap_server/eap_server_ikev2.c src/eap_server/ikev2.c diff --git a/hostapd/Makefile b/hostapd/Makefile index 456fb18..ac085fd 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -595,6 +595,16 @@ CFLAGS += -DCONFIG_DPP2 endif endif +ifdef CONFIG_PASN +CFLAGS += -DCONFIG_PASN +CFLAGS += -DCONFIG_PTKSA_CACHE +NEED_HMAC_SHA256_KDF=y +NEED_HMAC_SHA384_KDF=y +NEED_SHA256=y +NEED_SHA384=y +OBJS += ../src/common/ptksa_cache.o +endif + ifdef CONFIG_EAP_IKEV2 CFLAGS += -DEAP_SERVER_IKEV2 OBJS += ../src/eap_server/eap_server_ikev2.o ../src/eap_server/ikev2.o diff --git a/hostapd/config_file.c b/hostapd/config_file.c index ce32f3c..64704fb 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -754,6 +754,10 @@ static int hostapd_config_parse_key_mgmt(int line, const char *value) else if (os_strcmp(start, "OSEN") == 0) val |= WPA_KEY_MGMT_OSEN; #endif /* CONFIG_HS20 */ +#ifdef CONFIG_PASN + else if (os_strcmp(start, "PASN") == 0) + val |= WPA_KEY_MGMT_PASN; +#endif /* CONFIG_PASN */ else { wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'", line, start); @@ -1216,6 +1220,32 @@ static u8 set_he_cap(int val, u8 mask) return (u8) (mask & (val << find_bit_offset(mask))); } + +static int hostapd_parse_he_srg_bitmap(u8 *bitmap, char *val) +{ + int bitpos; + char *pos, *end; + + os_memset(bitmap, 0, 8); + pos = val; + while (*pos != '\0') { + end = os_strchr(pos, ' '); + if (end) + *end = '\0'; + + bitpos = atoi(pos); + if (bitpos < 0 || bitpos > 64) + return -1; + + bitmap[bitpos / 8] |= BIT(bitpos % 8); + if (!end) + break; + pos = end + 1; + } + + return 0; +} + #endif /* CONFIG_IEEE80211AX */ @@ -3259,6 +3289,16 @@ static int hostapd_config_fill(struct hostapd_config *conf, } conf->rate_type = BEACON_RATE_VHT; conf->beacon_rate = val; + } else if (os_strncmp(pos, "he:", 3) == 0) { + val = atoi(pos + 3); + if (val < 0 || val > 11) { + wpa_printf(MSG_ERROR, + "Line %d: invalid beacon_rate HE-MCS %d", + line, val); + return 1; + } + conf->rate_type = BEACON_RATE_HE; + conf->beacon_rate = val; } else { val = atoi(pos); if (val < 10 || val > 10000) { @@ -3540,19 +3580,53 @@ static int hostapd_config_fill(struct hostapd_config *conf, conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_TIMER_IDX] = atoi(pos) & 0xff; } else if (os_strcmp(buf, "he_spr_sr_control") == 0) { - conf->spr.sr_control = atoi(pos) & 0xff; + conf->spr.sr_control = atoi(pos) & 0x1f; } else if (os_strcmp(buf, "he_spr_non_srg_obss_pd_max_offset") == 0) { conf->spr.non_srg_obss_pd_max_offset = atoi(pos); } else if (os_strcmp(buf, "he_spr_srg_obss_pd_min_offset") == 0) { conf->spr.srg_obss_pd_min_offset = atoi(pos); } else if (os_strcmp(buf, "he_spr_srg_obss_pd_max_offset") == 0) { conf->spr.srg_obss_pd_max_offset = atoi(pos); + } else if (os_strcmp(buf, "he_spr_srg_bss_colors") == 0) { + if (hostapd_parse_he_srg_bitmap( + conf->spr.srg_bss_color_bitmap, pos)) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid srg bss colors list '%s'", + line, pos); + return 1; + } + } else if (os_strcmp(buf, "he_spr_srg_partial_bssid") == 0) { + if (hostapd_parse_he_srg_bitmap( + conf->spr.srg_partial_bssid_bitmap, pos)) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid srg partial bssid list '%s'", + line, pos); + return 1; + } } else if (os_strcmp(buf, "he_oper_chwidth") == 0) { conf->he_oper_chwidth = atoi(pos); } else if (os_strcmp(buf, "he_oper_centr_freq_seg0_idx") == 0) { conf->he_oper_centr_freq_seg0_idx = atoi(pos); } else if (os_strcmp(buf, "he_oper_centr_freq_seg1_idx") == 0) { conf->he_oper_centr_freq_seg1_idx = atoi(pos); + } else if (os_strcmp(buf, "he_6ghz_max_mpdu") == 0) { + conf->he_6ghz_max_mpdu = atoi(pos); + } else if (os_strcmp(buf, "he_6ghz_max_ampdu_len_exp") == 0) { + conf->he_6ghz_max_ampdu_len_exp = atoi(pos); + } else if (os_strcmp(buf, "he_6ghz_rx_ant_pat") == 0) { + conf->he_6ghz_rx_ant_pat = atoi(pos); + } else if (os_strcmp(buf, "he_6ghz_tx_ant_pat") == 0) { + conf->he_6ghz_tx_ant_pat = atoi(pos); + } else if (os_strcmp(buf, "unsol_bcast_probe_resp_interval") == 0) { + int val = atoi(pos); + + if (val < 0 || val > 20) { + wpa_printf(MSG_ERROR, + "Line %d: invalid unsol_bcast_probe_resp_interval value", + line); + return 1; + } + bss->unsol_bcast_probe_resp_interval = val; #endif /* CONFIG_IEEE80211AX */ } else if (os_strcmp(buf, "max_listen_interval") == 0) { bss->max_listen_interval = atoi(pos); @@ -4359,11 +4433,17 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->dhcp_server_port = atoi(pos); } else if (os_strcmp(buf, "dhcp_relay_port") == 0) { bss->dhcp_relay_port = atoi(pos); + } else if (os_strcmp(buf, "fils_discovery_min_interval") == 0) { + bss->fils_discovery_min_int = atoi(pos); + } else if (os_strcmp(buf, "fils_discovery_max_interval") == 0) { + bss->fils_discovery_max_int = atoi(pos); #endif /* CONFIG_FILS */ } else if (os_strcmp(buf, "multicast_to_unicast") == 0) { bss->multicast_to_unicast = atoi(pos); } else if (os_strcmp(buf, "broadcast_deauth") == 0) { bss->broadcast_deauth = atoi(pos); + } else if (os_strcmp(buf, "notify_mgmt_frames") == 0) { + bss->notify_mgmt_frames = atoi(pos); #ifdef CONFIG_DPP } else if (os_strcmp(buf, "dpp_name") == 0) { os_free(bss->dpp_name); @@ -4450,6 +4530,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, conf->rssi_reject_assoc_rssi = atoi(pos); } else if (os_strcmp(buf, "rssi_reject_assoc_timeout") == 0) { conf->rssi_reject_assoc_timeout = atoi(pos); + } else if (os_strcmp(buf, "rssi_ignore_probe_request") == 0) { + conf->rssi_ignore_probe_request = atoi(pos); } else if (os_strcmp(buf, "pbss") == 0) { bss->pbss = atoi(pos); } else if (os_strcmp(buf, "transition_disable") == 0) { @@ -4569,6 +4651,25 @@ static int hostapd_config_fill(struct hostapd_config *conf, } bss->mka_psk_set |= MKA_PSK_SET_CKN; #endif /* CONFIG_MACSEC */ + } else if (os_strcmp(buf, "disable_11n") == 0) { + bss->disable_11n = !!atoi(pos); + } else if (os_strcmp(buf, "disable_11ac") == 0) { + bss->disable_11ac = !!atoi(pos); + } else if (os_strcmp(buf, "disable_11ax") == 0) { + bss->disable_11ax = !!atoi(pos); +#ifdef CONFIG_PASN +#ifdef CONFIG_TESTING_OPTIONS + } else if (os_strcmp(buf, "force_kdk_derivation") == 0) { + bss->force_kdk_derivation = atoi(pos); +#endif /* CONFIG_TESTING_OPTIONS */ + } else if (os_strcmp(buf, "pasn_groups") == 0) { + if (hostapd_parse_intlist(&bss->pasn_groups, pos)) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid pasn_groups value '%s'", + line, pos); + return 1; + } +#endif /* CONFIG_PASN */ } else { wpa_printf(MSG_ERROR, "Line %d: unknown configuration item '%s'", diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index e2ae0ad..05d6be6 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -37,6 +37,7 @@ #include "common/dpp.h" #endif /* CONFIG_DPP */ #include "common/wpa_ctrl.h" +#include "common/ptksa_cache.h" #include "crypto/tls.h" #include "drivers/driver.h" #include "eapol_auth/eapol_auth_sm.h" @@ -1222,6 +1223,52 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, return pos - buf; pos += ret; } + + if (hapd->conf->multi_ap) { + struct hostapd_ssid *ssid = &hapd->conf->multi_ap_backhaul_ssid; + + ret = os_snprintf(pos, end - pos, "multi_ap=%d\n", + hapd->conf->multi_ap); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + if (ssid->ssid_len) { + ret = os_snprintf(pos, end - pos, + "multi_ap_backhaul_ssid=%s\n", + wpa_ssid_txt(ssid->ssid, + ssid->ssid_len)); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + + if (hapd->conf->wps_state && hapd->conf->wpa && + ssid->wpa_passphrase) { + ret = os_snprintf(pos, end - pos, + "multi_ap_backhaul_wpa_passphrase=%s\n", + ssid->wpa_passphrase); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + + if (hapd->conf->wps_state && hapd->conf->wpa && + ssid->wpa_psk && + ssid->wpa_psk->group) { + char hex[PMK_LEN * 2 + 1]; + + wpa_snprintf_hex(hex, sizeof(hex), ssid->wpa_psk->psk, + PMK_LEN); + ret = os_snprintf(pos, end - pos, + "multi_ap_backhaul_wpa_psk=%s\n", + hex); + forced_memzero(hex, sizeof(hex)); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + } #endif /* CONFIG_WPS */ if (hapd->conf->wpa) { @@ -1347,21 +1394,29 @@ static void hostapd_disassoc_deny_mac(struct hostapd_data *hapd) static int hostapd_ctrl_iface_set_band(struct hostapd_data *hapd, - const char *band) + const char *bands) { union wpa_event_data event; - enum set_band setband; - - if (os_strcmp(band, "AUTO") == 0) - setband = WPA_SETBAND_AUTO; - else if (os_strcmp(band, "5G") == 0) - setband = WPA_SETBAND_5G; - else if (os_strcmp(band, "2G") == 0) - setband = WPA_SETBAND_2G; - else - return -1; + u32 setband_mask = WPA_SETBAND_AUTO; - if (hostapd_drv_set_band(hapd, setband) == 0) { + /* + * For example: + * SET setband 2G,6G + * SET setband 5G + * SET setband AUTO + */ + if (!os_strstr(bands, "AUTO")) { + if (os_strstr(bands, "5G")) + setband_mask |= WPA_SETBAND_5G; + if (os_strstr(bands, "6G")) + setband_mask |= WPA_SETBAND_6G; + if (os_strstr(bands, "2G")) + setband_mask |= WPA_SETBAND_2G; + if (setband_mask == WPA_SETBAND_AUTO) + return -1; + } + + if (hostapd_drv_set_band(hapd, setband_mask) == 0) { os_memset(&event, 0, sizeof(event)); event.channel_list_changed.initiator = REGDOM_SET_BY_USER; event.channel_list_changed.type = REGDOM_TYPE_UNKNOWN; @@ -2671,9 +2726,9 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface, for (i = 0; i < iface->num_bss; i++) { - /* Save CHAN_SWITCH VHT config */ - hostapd_chan_switch_vht_config( - iface->bss[i], settings.freq_params.vht_enabled); + /* Save CHAN_SWITCH VHT and HE config */ + hostapd_chan_switch_config(iface->bss[i], + &settings.freq_params); ret = hostapd_switch_channel(iface->bss[i], &settings); if (ret) { @@ -3309,6 +3364,23 @@ static int hostapd_ctrl_iface_get_capability(struct hostapd_data *hapd, } +#ifdef ANDROID +static int hostapd_ctrl_iface_driver_cmd(struct hostapd_data *hapd, char *cmd, + char *buf, size_t buflen) +{ + int ret; + + ret = hostapd_drv_driver_cmd(hapd, cmd, buf, buflen); + if (ret == 0) { + ret = os_snprintf(buf, buflen, "%s\n", "OK"); + if (os_snprintf_error(buflen, ret)) + ret = -1; + } + return ret; +} +#endif /* ANDROID */ + + static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, char *buf, char *reply, int reply_size, @@ -3626,16 +3698,17 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, eloop_terminate(); } else if (os_strncmp(buf, "ACCEPT_ACL ", 11) == 0) { if (os_strncmp(buf + 11, "ADD_MAC ", 8) == 0) { - if (!hostapd_ctrl_iface_acl_add_mac( + if (hostapd_ctrl_iface_acl_add_mac( + &hapd->conf->accept_mac, + &hapd->conf->num_accept_mac, buf + 19)) + reply_len = -1; + } else if (os_strncmp((buf + 11), "DEL_MAC ", 8) == 0) { + if (!hostapd_ctrl_iface_acl_del_mac( &hapd->conf->accept_mac, &hapd->conf->num_accept_mac, buf + 19)) hostapd_disassoc_accept_mac(hapd); else reply_len = -1; - } else if (os_strncmp((buf + 11), "DEL_MAC ", 8) == 0) { - hostapd_ctrl_iface_acl_del_mac( - &hapd->conf->accept_mac, - &hapd->conf->num_accept_mac, buf + 19); } else if (os_strcmp(buf + 11, "SHOW") == 0) { reply_len = hostapd_ctrl_iface_acl_show_mac( hapd->conf->accept_mac, @@ -3644,6 +3717,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, hostapd_ctrl_iface_acl_clear_list( &hapd->conf->accept_mac, &hapd->conf->num_accept_mac); + hostapd_disassoc_accept_mac(hapd); } } else if (os_strncmp(buf, "DENY_ACL ", 9) == 0) { if (os_strncmp(buf + 9, "ADD_MAC ", 8) == 0) { @@ -3651,10 +3725,13 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, &hapd->conf->deny_mac, &hapd->conf->num_deny_mac, buf + 17)) hostapd_disassoc_deny_mac(hapd); + else + reply_len = -1; } else if (os_strncmp(buf + 9, "DEL_MAC ", 8) == 0) { - hostapd_ctrl_iface_acl_del_mac( - &hapd->conf->deny_mac, - &hapd->conf->num_deny_mac, buf + 17); + if (hostapd_ctrl_iface_acl_del_mac( + &hapd->conf->deny_mac, + &hapd->conf->num_deny_mac, buf + 17)) + reply_len = -1; } else if (os_strcmp(buf + 9, "SHOW") == 0) { reply_len = hostapd_ctrl_iface_acl_show_mac( hapd->conf->deny_mac, @@ -3802,6 +3879,15 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, } else if (os_strncmp(buf, "GET_CAPABILITY ", 15) == 0) { reply_len = hostapd_ctrl_iface_get_capability( hapd, buf + 15, reply, reply_size); +#ifdef CONFIG_PASN + } else if (os_strcmp(buf, "PTKSA_CACHE_LIST") == 0) { + reply_len = ptksa_cache_list(hapd->ptksa, reply, reply_size); +#endif /* CONFIG_PASN */ +#ifdef ANDROID + } else if (os_strncmp(buf, "DRIVER ", 7) == 0) { + reply_len = hostapd_ctrl_iface_driver_cmd(hapd, buf + 7, reply, + reply_size); +#endif /* ANDROID */ } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; diff --git a/hostapd/defconfig b/hostapd/defconfig index e9f5de7..cbdd2a5 100644 --- a/hostapd/defconfig +++ b/hostapd/defconfig @@ -395,3 +395,10 @@ CONFIG_IPV6=y # build includes this to allow mixed mode WPA+WPA2 networks to be enabled, but # that functionality is subject to be removed in the future. #CONFIG_NO_TKIP=y + +# Pre-Association Security Negotiation (PASN) +# Experimental implementation based on IEEE P802.11z/D2.6 and the protocol +# design is still subject to change. As such, this should not yet be enabled in +# production use. +# This requires CONFIG_IEEE80211W=y to be enabled, too. +#CONFIG_PASN=y diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index bf89fbc..a3d28ef 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -279,6 +279,8 @@ fragm_threshold=-1 # beacon_rate=ht:<HT MCS> # VHT: # beacon_rate=vht:<VHT MCS> +# HE: +# beacon_rate=he:<HE MCS> # # For example, beacon_rate=10 for 1 Mbps or beacon_rate=60 for 6 Mbps (OFDM). #beacon_rate=10 @@ -571,6 +573,10 @@ wmm_ac_vo_acm=0 # Default: 1 (enabled) #broadcast_deauth=1 +# Get notifications for received Management frames on control interface +# Default: 0 (disabled) +#notify_mgmt_frames=0 + ##### IEEE 802.11n related configuration ###################################### # ieee80211n: Whether IEEE 802.11n (HT) is enabled @@ -580,6 +586,9 @@ wmm_ac_vo_acm=0 # Note: hw_mode=g (2.4 GHz) and hw_mode=a (5 GHz) is used to specify the band. #ieee80211n=1 +# disable_11n: Boolean (0/1) to disable HT for a specific BSS +#disable_11n=0 + # ht_capab: HT capabilities (list of flags) # LDPC coding capability: [LDPC] = supported # Supported channel width set: [HT40-] = both 20 MHz and 40 MHz with secondary @@ -632,6 +641,9 @@ wmm_ac_vo_acm=0 # Note: hw_mode=a is used to specify that 5 GHz band is used with VHT. #ieee80211ac=1 +# disable_11ac: Boolean (0/1) to disable VHT for a specific BSS +#disable_11ac=0 + # vht_capab: VHT capabilities (list of flags) # # vht_max_mpdu_len: [MAX-MPDU-7991] [MAX-MPDU-11454] @@ -786,6 +798,9 @@ wmm_ac_vo_acm=0 # 1 = enabled #ieee80211ax=1 +# disable_11ax: Boolean (0/1) to disable HE for a specific BSS +#disable_11ax=0 + #he_su_beamformer: HE single user beamformer support # 0 = not supported (default) # 1 = supported @@ -866,6 +881,65 @@ wmm_ac_vo_acm=0 #he_spr_non_srg_obss_pd_max_offset #he_spr_srg_obss_pd_min_offset #he_spr_srg_obss_pd_max_offset +# +# SPR SRG BSS Color +# This config represents SRG BSS Color Bitmap field of Spatial Reuse Parameter +# Set element that indicates the BSS color values used by members of the +# SRG of which the transmitting STA is a member. The value is in range of 0-63. +#he_spr_srg_bss_colors=1 2 10 63 +# +# SPR SRG Partial BSSID +# This config represents SRG Partial BSSID Bitmap field of Spatial Reuse +# Parameter Set element that indicates the Partial BSSID values used by members +# of the SRG of which the transmitting STA is a member. The value range +# corresponds to one of the 64 possible values of BSSID[39:44], where the lowest +# numbered bit corresponds to Partial BSSID value 0 and the highest numbered bit +# corresponds to Partial BSSID value 63. +#he_spr_srg_partial_bssid=0 1 3 63 +# +#he_6ghz_max_mpdu: Maximum MPDU Length of HE 6 GHz band capabilities. +# Indicates maximum MPDU length +# 0 = 3895 octets +# 1 = 7991 octets +# 2 = 11454 octets (default) +#he_6ghz_max_mpdu=2 +# +#he_6ghz_max_ampdu_len_exp: Maximum A-MPDU Length Exponent of HE 6 GHz band +# capabilities. Indicates the maximum length of A-MPDU pre-EOF padding that +# the STA can receive. This field is an integer in the range of 0 to 7. +# The length defined by this field is equal to +# 2 pow(13 + Maximum A-MPDU Length Exponent) -1 octets +# 0 = AMPDU length of 8k +# 1 = AMPDU length of 16k +# 2 = AMPDU length of 32k +# 3 = AMPDU length of 65k +# 4 = AMPDU length of 131k +# 5 = AMPDU length of 262k +# 6 = AMPDU length of 524k +# 7 = AMPDU length of 1048k (default) +#he_6ghz_max_ampdu_len_exp=7 +# +#he_6ghz_rx_ant_pat: Rx Antenna Pattern Consistency of HE 6 GHz capability. +# Indicates the possibility of Rx antenna pattern change +# 0 = Rx antenna pattern might change during the lifetime of an association +# 1 = Rx antenna pattern does not change during the lifetime of an association +# (default) +#he_6ghz_rx_ant_pat=1 +# +#he_6ghz_tx_ant_pat: Tx Antenna Pattern Consistency of HE 6 GHz capability. +# Indicates the possibility of Tx antenna pattern change +# 0 = Tx antenna pattern might change during the lifetime of an association +# 1 = Tx antenna pattern does not change during the lifetime of an association +# (default) +#he_6ghz_tx_ant_pat=1 + +# Unsolicited broadcast Probe Response transmission settings +# This is for the 6 GHz band only. If the interval is set to a non-zero value, +# the AP schedules unsolicited broadcast Probe Response frames to be +# transmitted for in-band discovery. Refer to +# IEEE P802.11ax/D8.0 26.17.2.3.2, AP behavior for fast passive scanning. +# Valid range: 0..20 TUs; default is 0 (disabled) +#unsol_bcast_probe_resp_interval=0 ##### IEEE 802.1X-2004 related configuration ################################## @@ -1712,7 +1786,8 @@ own_ip_addr=127.0.0.1 #group_mgmt_cipher=AES-128-CMAC # Beacon Protection (management frame protection for Beacon frames) -# This depends on management frame protection being enabled (ieee80211w != 0). +# This depends on management frame protection being enabled (ieee80211w != 0) +# and beacon protection support indication from the driver. # 0 = disabled (default) # 1 = enabled #beacon_prot=0 @@ -1729,6 +1804,9 @@ own_ip_addr=127.0.0.1 # ocv: Operating Channel Validation # This is a countermeasure against multi-channel man-in-the-middle attacks. +# Enabling this depends on the driver's support for OCV when the driver SME is +# used. If hostapd SME is used, this will be enabled just based on this +# configuration. # Enabling this automatically also enables ieee80211w, if not yet enabled. # 0 = disabled (default) # 1 = enabled @@ -1921,6 +1999,13 @@ own_ip_addr=127.0.0.1 # default: 30 TUs (= 30.72 milliseconds) #fils_hlp_wait_time=30 +# FILS Discovery frame transmission minimum and maximum interval settings. +# If fils_discovery_max_interval is non-zero, the AP enables FILS Discovery +# frame transmission. These values use TUs as the unit and have allowed range +# of 0-10000. fils_discovery_min_interval defaults to 20. +#fils_discovery_min_interval=20 +#fils_discovery_max_interval=0 + # Transition Disable indication # The AP can notify authenticated stations to disable transition mode in their # network profiles when the network has completed transition steps, i.e., once @@ -1938,6 +2023,14 @@ own_ip_addr=127.0.0.1 # (default: 0 = do not include Transition Disable KDE) #transition_disable=0x01 +# PASN ECDH groups +# PASN implementations are required to support group 19 (NIST P-256). If this +# parameter is not set, only group 19 is supported by default. This +# configuration parameter can be used to specify a limited set of allowed +# groups. The group values are listed in the IANA registry: +# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-10 +#pasn_groups=19 20 21 + ##### IEEE 802.11r configuration ############################################## # Mobility Domain identifier (dot11FTMobilityDomainID, MDID) @@ -2726,6 +2819,10 @@ own_ip_addr=127.0.0.1 # threshold (range: 0..255, default=30). #rssi_reject_assoc_timeout=30 +# Ignore Probe Request frames if RSSI is below given threshold (in dBm) +# Allowed range: -60 to -90 dBm; default = 0 (rejection disabled) +#rssi_ignore_probe_request=-75 + ##### Fast Session Transfer (FST) support ##################################### # # The options in this section are only available when the build configuration diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c index dac00e0..eaa628a 100644 --- a/hostapd/hostapd_cli.c +++ b/hostapd/hostapd_cli.c @@ -1475,6 +1475,20 @@ static int hostapd_cli_cmd_dpp_pkex_remove(struct wpa_ctrl *ctrl, int argc, #ifdef CONFIG_DPP2 +static int hostapd_cli_cmd_dpp_controller_start(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_CONTROLLER_START", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_controller_stop(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "DPP_CONTROLLER_STOP"); +} + + static int hostapd_cli_cmd_dpp_chirp(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -1527,6 +1541,14 @@ static int hostapd_cli_cmd_reload_wpa_psk(struct wpa_ctrl *ctrl, int argc, } +#ifdef ANDROID +static int hostapd_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DRIVER", 1, argc, argv); +} +#endif /* ANDROID */ + + struct hostapd_cli_cmd { const char *cmd; int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); @@ -1698,6 +1720,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = { { "dpp_pkex_remove", hostapd_cli_cmd_dpp_pkex_remove, NULL, "*|<id> = remove DPP pkex information" }, #ifdef CONFIG_DPP2 + { "dpp_controller_start", hostapd_cli_cmd_dpp_controller_start, NULL, + "[tcp_port=<port>] [role=..] = start DPP controller" }, + { "dpp_controller_stop", hostapd_cli_cmd_dpp_controller_stop, NULL, + "= stop DPP controller" }, { "dpp_chirp", hostapd_cli_cmd_dpp_chirp, NULL, "own=<BI ID> iter=<count> = start DPP chirp" }, { "dpp_stop_chirp", hostapd_cli_cmd_dpp_stop_chirp, NULL, @@ -1714,6 +1740,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = { "<addr> [req_mode=] <measurement request hexdump> = send a Beacon report request to a station" }, { "reload_wpa_psk", hostapd_cli_cmd_reload_wpa_psk, NULL, "= reload wpa_psk_file only" }, +#ifdef ANDROID + { "driver", hostapd_cli_cmd_driver, NULL, + "<driver sub command> [<hex formatted data>] = send driver command data" }, +#endif /* ANDROID */ { NULL, NULL, NULL, NULL } }; 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..a420701 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,21 @@ 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); } +#ifdef ANDROID +static inline int hostapd_drv_driver_cmd(struct hostapd_data *hapd, + char *cmd, char *buf, size_t buf_len) +{ + if (!hapd->driver->driver_cmd) + return -1; + return hapd->driver->driver_cmd(hapd->drv_priv, cmd, buf, buf_len); +} +#endif /* ANDROID */ + #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..9518545 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) */ @@ -2150,6 +2164,7 @@ struct ieee80211_he_capabilities { * and optional variable length PPE Thresholds field. */ u8 optional[37]; } STRUCT_PACKED; +#define IEEE80211_HE_CAPAB_MIN_LEN (6 + 11) struct ieee80211_he_operation { le32 he_oper_params; /* HE Operation Parameters[3] and @@ -2364,4 +2379,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..04d49ee 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"; } @@ -2903,6 +3226,11 @@ int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie) pos[1] >= sizeof(struct ieee80211_vht_capabilities)) { ie->vht_capabilities = pos + 2; + } else if (*pos == WLAN_EID_EXTENSION && + pos[1] >= 1 + IEEE80211_HE_CAPAB_MIN_LEN && + pos[2] == WLAN_EID_EXT_HE_CAPABILITIES) { + ie->he_capabilities = pos + 3; + ie->he_capab_len = pos[1] - 1; } else if (*pos == WLAN_EID_QOS && pos[1] >= 1) { ie->qosinfo = pos[2]; } else if (*pos == WLAN_EID_SUPPORTED_CHANNELS) { @@ -2942,3 +3270,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..0e9f252 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); @@ -553,6 +592,8 @@ struct wpa_eapol_ie_parse { size_t ext_supp_rates_len; const u8 *ht_capabilities; const u8 *vht_capabilities; + const u8 *he_capabilities; + size_t he_capab_len; const u8 *supp_channels; size_t supp_channels_len; const u8 *supp_oper_classes; @@ -591,4 +632,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..81bb937 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -428,7 +428,9 @@ static struct p2p_device * p2p_create_device(struct p2p_data *p2p, oldest = dev; } if (count + 1 > p2p->cfg->max_peers && oldest) { - p2p_dbg(p2p, "Remove oldest peer entry to make room for a new peer"); + p2p_dbg(p2p, + "Remove oldest peer entry to make room for a new peer " + MACSTR, MAC2STR(oldest->info.p2p_device_addr)); dl_list_del(&oldest->list); p2p_device_free(p2p, oldest); } @@ -453,6 +455,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 +667,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 +3516,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/p2p/p2p_pd.c b/src/p2p/p2p_pd.c index 3994ec0..05fd593 100644 --- a/src/p2p/p2p_pd.c +++ b/src/p2p/p2p_pd.c @@ -595,14 +595,12 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, goto out; } + dev = p2p_get_device(p2p, sa); if (!dev) { - dev = p2p_get_device(p2p, sa); - if (!dev) { - p2p_dbg(p2p, - "Provision Discovery device not found " - MACSTR, MAC2STR(sa)); - goto out; - } + p2p_dbg(p2p, + "Provision Discovery device not found " + MACSTR, MAC2STR(sa)); + goto out; } } else if (msg.wfd_subelems) { wpabuf_free(dev->info.wfd_subelems); 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/tdls.c b/src/rsn_supp/tdls.c index 7c4ef19..af69268 100644 --- a/src/rsn_supp/tdls.c +++ b/src/rsn_supp/tdls.c @@ -136,6 +136,8 @@ struct wpa_tdls_peer { struct ieee80211_ht_capabilities *ht_capabilities; struct ieee80211_vht_capabilities *vht_capabilities; + struct ieee80211_he_capabilities *he_capabilities; + size_t he_capab_len; u8 qos_info; @@ -703,6 +705,8 @@ static void wpa_tdls_peer_clear(struct wpa_sm *sm, struct wpa_tdls_peer *peer) peer->ht_capabilities = NULL; os_free(peer->vht_capabilities); peer->vht_capabilities = NULL; + os_free(peer->he_capabilities); + peer->he_capabilities = NULL; os_free(peer->ext_capab); peer->ext_capab = NULL; os_free(peer->supp_channels); @@ -1652,6 +1656,29 @@ static int copy_peer_vht_capab(const struct wpa_eapol_ie_parse *kde, } +static int copy_peer_he_capab(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + if (!kde->he_capabilities) { + wpa_printf(MSG_DEBUG, "TDLS: No HE capabilities received"); + return 0; + } + + os_free(peer->he_capabilities); + peer->he_capab_len = 0; + peer->he_capabilities = os_memdup(kde->he_capabilities, + kde->he_capab_len); + if (!peer->he_capabilities) + return -1; + + peer->he_capab_len = kde->he_capab_len; + wpa_hexdump(MSG_DEBUG, "TDLS: Peer HE capabilities", + peer->he_capabilities, peer->he_capab_len); + + return 0; +} + + static int copy_peer_ext_capab(const struct wpa_eapol_ie_parse *kde, struct wpa_tdls_peer *peer) { @@ -1761,6 +1788,8 @@ static int wpa_tdls_addset_peer(struct wpa_sm *sm, struct wpa_tdls_peer *peer, peer->supp_rates, peer->supp_rates_len, peer->ht_capabilities, peer->vht_capabilities, + peer->he_capabilities, + peer->he_capab_len, peer->qos_info, peer->wmm_capable, peer->ext_capab, peer->ext_capab_len, peer->supp_channels, @@ -1896,7 +1925,8 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, if (copy_peer_ht_capab(&kde, peer) < 0) goto error; - if (copy_peer_vht_capab(&kde, peer) < 0) + if (copy_peer_vht_capab(&kde, peer) < 0 || + copy_peer_he_capab(&kde, peer) < 0) goto error; if (copy_peer_ext_capab(&kde, peer) < 0) @@ -1925,7 +1955,8 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, "TDLS setup - send own request"); peer->initiator = 1; wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL, - NULL, 0, 0, NULL, 0, NULL, 0, NULL, 0); + NULL, NULL, 0, 0, 0, NULL, 0, NULL, 0, + NULL, 0); if (wpa_tdls_send_tpk_m1(sm, peer) == -2) { peer = NULL; goto error; @@ -2303,7 +2334,8 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, if (copy_peer_ht_capab(&kde, peer) < 0) goto error; - if (copy_peer_vht_capab(&kde, peer) < 0) + if (copy_peer_vht_capab(&kde, peer) < 0 || + copy_peer_he_capab(&kde, peer) < 0) goto error; if (copy_peer_ext_capab(&kde, peer) < 0) @@ -2690,7 +2722,8 @@ int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr) /* add the peer to the driver as a "setup in progress" peer */ if (wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL, - NULL, 0, 0, NULL, 0, NULL, 0, NULL, 0)) { + NULL, NULL, 0, 0, 0, NULL, 0, NULL, 0, + NULL, 0)) { wpa_tdls_disable_peer_link(sm, peer); return -1; } 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..f377acb 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -69,6 +69,8 @@ struct wpa_sm_ctx { size_t supp_rates_len, const struct ieee80211_ht_capabilities *ht_capab, const struct ieee80211_vht_capabilities *vht_capab, + const struct ieee80211_he_capabilities *he_capab, + size_t he_capab_len, u8 qosinfo, int wmm, const u8 *ext_capab, size_t ext_capab_len, const u8 *supp_channels, size_t supp_channels_len, @@ -87,6 +89,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 +134,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 +194,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 +375,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 +432,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 +482,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 +540,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..e7281bf 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 @@ -372,6 +398,8 @@ wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add, size_t supp_rates_len, const struct ieee80211_ht_capabilities *ht_capab, const struct ieee80211_vht_capabilities *vht_capab, + const struct ieee80211_he_capabilities *he_capab, + size_t he_capab_len, u8 qosinfo, int wmm, const u8 *ext_capab, size_t ext_capab_len, const u8 *supp_channels, size_t supp_channels_len, const u8 *supp_oper_classes, @@ -381,7 +409,9 @@ wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add, return sm->ctx->tdls_peer_addset(sm->ctx->ctx, addr, add, aid, capability, supp_rates, supp_rates_len, ht_capab, - vht_capab, qosinfo, wmm, + vht_capab, + he_capab, he_capab_len, + qosinfo, wmm, ext_capab, ext_capab_len, supp_channels, supp_channels_len, @@ -441,6 +471,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, diff --git a/tests/fuzzing/p2p/p2p.c b/tests/fuzzing/p2p/p2p.c index 8ffcbbd..fc83c35 100644 --- a/tests/fuzzing/p2p/p2p.c +++ b/tests/fuzzing/p2p/p2p.c @@ -115,7 +115,7 @@ static void test_send(void *eloop_data, void *user_ctx) os_memset(&rx_time, 0, sizeof(rx_time)); p2p_scan_res_handler(ctx->p2p, (u8 *) "\x02\x00\x00\x00\x01\x00", 2412, &rx_time, 0, ctx->data, ctx->data_len); - p2p_scan_res_handled(ctx->p2p); + p2p_scan_res_handled(ctx->p2p, 0); p2p_probe_req_rx(ctx->p2p, (u8 *) "\x02\x00\x00\x00\x01\x00", (u8 *) "\x02\x00\x00\x00\x00\x00", diff --git a/tests/fuzzing/wnm/Makefile b/tests/fuzzing/wnm/Makefile index 73eab53..60d27b3 100644 --- a/tests/fuzzing/wnm/Makefile +++ b/tests/fuzzing/wnm/Makefile @@ -20,7 +20,7 @@ LIBS += $(SRC)/utils/libutils.a ELIBS += $(SRC)/crypto/libcrypto.a ELIBS += $(SRC)/tls/libtls.a -OBJS += $(WPAS_SRC)/blacklist.o +OBJS += $(WPAS_SRC)/bssid_ignore.o OBJS += $(WPAS_SRC)/bss.o OBJS += $(WPAS_SRC)/config.o OBJS += $(WPAS_SRC)/config_file.o diff --git a/tests/fuzzing/wnm/wnm.c b/tests/fuzzing/wnm/wnm.c index 9c0d541..7afc648 100644 --- a/tests/fuzzing/wnm/wnm.c +++ b/tests/fuzzing/wnm/wnm.c @@ -16,7 +16,7 @@ #include "wpa_supplicant_i.h" #include "bss.h" #include "wnm_sta.h" -#include "config.h" +#include "../../../wpa_supplicant/config.h" #include "../fuzzer-common.h" diff --git a/tests/hwsim/build.sh b/tests/hwsim/build.sh index 2a3dd70..cb47001 100755 --- a/tests/hwsim/build.sh +++ b/tests/hwsim/build.sh @@ -26,13 +26,18 @@ while [ "$1" != "" ]; do esac done +JOBS=`nproc` +if [ -z "$ABC" ]; then + JOBS=8 +fi + echo "Building TNC testing tools" cd tnc -make QUIET=1 -j8 +make QUIET=1 -j$JOBS echo "Building wlantest" cd ../../../wlantest -make QUIET=1 -j8 > /dev/null +make QUIET=1 -j$JOBS > /dev/null echo "Building hs20-osu-client" cd ../hs20/client/ @@ -54,7 +59,7 @@ if [ $use_lcov -eq 1 ]; then fi fi -make QUIET=1 -j8 hostapd hostapd_cli hlr_auc_gw +make QUIET=1 -j$JOBS hostapd hostapd_cli hlr_auc_gw echo "Building wpa_supplicant" cd ../wpa_supplicant @@ -75,4 +80,4 @@ fi if [ -z $FIPSLD_CC ]; then export FIPSLD_CC=gcc fi -make QUIET=1 -j8 +make QUIET=1 -j$JOBS diff --git a/tests/hwsim/example-hostapd.config b/tests/hwsim/example-hostapd.config index 972d35c..8b466e8 100644 --- a/tests/hwsim/example-hostapd.config +++ b/tests/hwsim/example-hostapd.config @@ -112,3 +112,4 @@ CONFIG_OWE=y CONFIG_DPP=y CONFIG_DPP2=y CONFIG_WEP=y +CONFIG_PASN=y diff --git a/tests/hwsim/example-wpa_supplicant.config b/tests/hwsim/example-wpa_supplicant.config index 9e3cc67..a579289 100644 --- a/tests/hwsim/example-wpa_supplicant.config +++ b/tests/hwsim/example-wpa_supplicant.config @@ -112,6 +112,7 @@ CONFIG_AUTOSCAN_EXPONENTIAL=y CONFIG_AUTOSCAN_PERIODIC=y CONFIG_EXT_PASSWORD_TEST=y +CONFIG_EXT_PASSWORD_FILE=y CONFIG_EAP_UNAUTH_TLS=y @@ -155,3 +156,4 @@ CONFIG_OWE=y CONFIG_DPP=y CONFIG_DPP2=y CONFIG_WEP=y +CONFIG_PASN=y diff --git a/tests/hwsim/hostapd.py b/tests/hwsim/hostapd.py index 5717575..de9263b 100644 --- a/tests/hwsim/hostapd.py +++ b/tests/hwsim/hostapd.py @@ -535,6 +535,23 @@ class Hostapd: def send_file(self, src, dst): self.host.send_file(src, dst) + def get_ptksa(self, bssid, cipher): + res = self.request("PTKSA_CACHE_LIST") + lines = res.splitlines() + for l in lines: + if bssid not in l or cipher not in l: + continue + vals = dict() + [index, addr, cipher, expiration, tk, kdk] = l.split(' ', 5) + vals['index'] = index + vals['addr'] = addr + vals['cipher'] = cipher + vals['expiration'] = expiration + vals['tk'] = tk + vals['kdk'] = kdk + return vals + return None + def add_ap(apdev, params, wait_enabled=True, no_enable=False, timeout=30, global_ctrl_override=None, driver=False): if isinstance(apdev, dict): diff --git a/tests/hwsim/remotehost.py b/tests/hwsim/remotehost.py index dec1ad5..0799b95 100644 --- a/tests/hwsim/remotehost.py +++ b/tests/hwsim/remotehost.py @@ -9,6 +9,8 @@ import subprocess import threading import tempfile import os +import traceback +import select logger = logging.getLogger() @@ -99,7 +101,7 @@ class Host(): return status, buf.decode() # async execute - def execute_run(self, command, res, use_reaper=True): + def thread_run(self, command, res, use_reaper=True): if use_reaper: filename = gen_reaper_file("reaper") self.send_file(filename, filename) @@ -113,51 +115,132 @@ class Host(): cmd = _command else: cmd = ["ssh", self.user + "@" + self.host, ' '.join(_command)] - _cmd = self.name + " execute_run: " + ' '.join(cmd) + _cmd = self.name + " thread_run: " + ' '.join(cmd) logger.debug(_cmd) t = threading.Thread(target=execute_thread, name=filename, args=(cmd, res)) t.start() return t - def execute_stop(self, t): + def thread_stop(self, t): if t.name.find("reaper") == -1: raise Exception("use_reaper required") pid_file = t.name + ".pid" - if t.isAlive(): + if t.is_alive(): cmd = ["kill `cat " + pid_file + "`"] self.execute(cmd) # try again - self.wait_execute_complete(t, 5) - if t.isAlive(): + self.thread_wait(t, 5) + if t.is_alive(): cmd = ["kill `cat " + pid_file + "`"] self.execute(cmd) # try with -9 - self.wait_execute_complete(t, 5) - if t.isAlive(): + self.thread_wait(t, 5) + if t.is_alive(): cmd = ["kill -9 `cat " + pid_file + "`"] self.execute(cmd) - self.wait_execute_complete(t, 5) - if t.isAlive(): + self.thread_wait(t, 5) + if t.is_alive(): raise Exception("thread still alive") self.execute(["rm", pid_file]) self.execute(["rm", t.name]) + self.local_execute(["rm", t.name]) - def wait_execute_complete(self, t, wait=None): + def thread_wait(self, t, wait=None): if wait == None: wait_str = "infinite" else: wait_str = str(wait) + "s" - logger.debug(self.name + " wait_execute_complete(" + wait_str + "): ") - if t.isAlive(): + logger.debug(self.name + " thread_wait(" + wait_str + "): ") + if t.is_alive(): t.join(wait) + def pending(self, s, timeout=0): + [r, w, e] = select.select([s], [], [], timeout) + if r: + return True + return False + + def proc_run(self, command): + filename = gen_reaper_file("reaper") + self.send_file(filename, filename) + self.execute(["chmod", "755", filename]) + _command = [filename] + command + + if self.host: + cmd = ["ssh", self.user + "@" + self.host, ' '.join(_command)] + else: + cmd = _command + + _cmd = self.name + " proc_run: " + ' '.join(cmd) + logger.debug(_cmd) + err = tempfile.TemporaryFile() + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=err) + proc.reaper_file = filename + return proc + + def proc_wait_event(self, proc, events, timeout=10): + if not isinstance(events, list): + raise Exception("proc_wait_event() events not a list") + + logger.debug(self.name + " proc_wait_event: " + ' '.join(events) + " timeout: " + str(timeout)) + start = os.times()[4] + try: + while True: + while self.pending(proc.stdout): + line = proc.stdout.readline() + if not line: + return None + line = line.decode() + logger.debug(line.strip('\n')) + for event in events: + if event in line: + return line + now = os.times()[4] + remaining = start + timeout - now + if remaining <= 0: + break + if not self.pending(proc.stdout, timeout=remaining): + break + except: + logger.debug(traceback.format_exc()) + pass + return None + + def proc_stop(self, proc): + if not proc: + return + + self.execute(["kill `cat " + proc.reaper_file + ".pid`"]) + self.execute(["rm", proc.reaper_file + ".pid"]) + self.execute(["rm", proc.reaper_file]) + self.local_execute(["rm", proc.reaper_file]) + proc.kill() + + def proc_dump(self, proc): + if not proc: + return "" + return proc.stdout.read() + + def execute_and_wait_event(self, command, events, timeout=10): + proc = None + ev = None + + try: + proc = self.proc_run(command) + ev = self.proc_wait_event(proc, events, timeout) + except: + pass + + self.proc_stop(proc) + return ev + def add_log(self, log_file): self.logs.append(log_file) diff --git a/tests/hwsim/rfkill.py b/tests/hwsim/rfkill.py index f08cf50..72b2527 100755 --- a/tests/hwsim/rfkill.py +++ b/tests/hwsim/rfkill.py @@ -126,7 +126,7 @@ class RFKill(object): @classmethod def list(cls): res = [] - rfk = open('/dev/rfkill', 'rb') + rfk = open('/dev/rfkill', 'rb', buffering=0) fd = rfk.fileno() flgs = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, flgs | os.O_NONBLOCK) diff --git a/tests/hwsim/test_ap_acs.py b/tests/hwsim/test_ap_acs.py index eb245e7..08eaee5 100644 --- a/tests/hwsim/test_ap_acs.py +++ b/tests/hwsim/test_ap_acs.py @@ -622,3 +622,43 @@ def test_ap_acs_rx_during(dev, apdev): finally: for i in range(3): dev[i].request("SCAN_INTERVAL 5") + +def test_ap_acs_he_24g(dev, apdev): + """Automatic channel selection on 2.4 GHz with HE""" + clear_scan_cache(apdev[0]) + force_prev_ap_on_24g(apdev[0]) + + params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678") + params['channel'] = '0' + params['ieee80211ax'] = '1' + params['ht_capab'] = '[HT40+]' + hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False) + wait_acs(hapd) + + freq = hapd.get_status_field("freq") + if int(freq) < 2400: + raise Exception("Unexpected frequency") + + dev[0].connect("test-acs", psk="12345678", scan_freq=freq) + +def test_ap_acs_he_24g_overlap(dev, apdev): + """Automatic channel selection on 2.4 GHz with HE (overlap)""" + clear_scan_cache(apdev[0]) + force_prev_ap_on_24g(apdev[0]) + + params = {"ssid": "overlapping", + "channel": "6", "ieee80211n": "1"} + hostapd.add_ap(apdev[1], params) + + params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678") + params['channel'] = '0' + params['ieee80211ax'] = '1' + params['ht_capab'] = '[HT40+]' + hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False) + wait_acs(hapd) + + freq = hapd.get_status_field("freq") + if int(freq) < 2400: + raise Exception("Unexpected frequency") + + dev[0].connect("test-acs", psk="12345678", scan_freq=freq) diff --git a/tests/hwsim/test_ap_ciphers.py b/tests/hwsim/test_ap_ciphers.py index d9f827a..eea17c1 100644 --- a/tests/hwsim/test_ap_ciphers.py +++ b/tests/hwsim/test_ap_ciphers.py @@ -415,6 +415,35 @@ def test_ap_cipher_mixed_wpa_wpa2(dev, apdev): hwsim_utils.test_connectivity(dev[0], dev[1]) @remote_compatible +def test_ap_cipher_wpa_sae(dev, apdev): + """WPA-PSK/TKIP and SAE mixed AP - WPA IE and RSNXE coexistence""" + skip_with_fips(dev[0]) + skip_without_tkip(dev[0]) + ssid = "test-wpa-sae" + passphrase = "12345678" + params = {"ssid": ssid, + "wpa_passphrase": passphrase, + "wpa": "3", + "wpa_key_mgmt": "WPA-PSK SAE", + "rsn_pairwise": "CCMP", + "wpa_pairwise": "TKIP", + "sae_pwe": "1"} + hapd = hostapd.add_ap(apdev[0], params) + dev[0].flush_scan_cache() + + dev[0].connect(ssid, psk=passphrase, proto="WPA", + pairwise="TKIP", group="TKIP", scan_freq="2412") + status = dev[0].get_status() + if status['key_mgmt'] != 'WPA-PSK': + raise Exception("Incorrect key_mgmt reported") + if status['pairwise_cipher'] != 'TKIP': + raise Exception("Incorrect pairwise_cipher reported") + if status['group_cipher'] != 'TKIP': + raise Exception("Incorrect group_cipher reported") + hapd.wait_sta() + hwsim_utils.test_connectivity(dev[0], hapd) + +@remote_compatible def test_ap_cipher_bip(dev, apdev): """WPA2-PSK with BIP""" check_group_mgmt_cipher(dev[0], apdev[0], "AES-128-CMAC") diff --git a/tests/hwsim/test_ap_dynamic.py b/tests/hwsim/test_ap_dynamic.py index 96d35b9..d23cf0c 100644 --- a/tests/hwsim/test_ap_dynamic.py +++ b/tests/hwsim/test_ap_dynamic.py @@ -37,6 +37,44 @@ def test_ap_change_ssid(dev, apdev): dev[0].set_network_quoted(id, "ssid", "test-wpa2-psk-new") dev[0].connect_network(id) +def test_ap_change_ssid_wps(dev, apdev): + """Dynamic SSID change with hostapd and WPA2-PSK using WPS""" + params = hostapd.wpa2_params(ssid="test-wpa2-psk-start", + passphrase="12345678") + # Use a PSK and not the passphrase, because the PSK will have to be computed + # again if we use a passphrase. + del params["wpa_passphrase"] + params["wpa_psk"] = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" + + params.update({"wps_state": "2", "eap_server": "1"}) + bssid = apdev[0]['bssid'] + hapd = hostapd.add_ap(apdev[0], params) + + new_ssid = "test-wpa2-psk-new" + logger.info("Change SSID dynamically (WPS)") + res = hapd.request("SET ssid " + new_ssid) + if "OK" not in res: + raise Exception("SET command failed") + res = hapd.request("RELOAD") + if "OK" not in res: + raise Exception("RELOAD command failed") + + # Connect to the new ssid using wps: + hapd.request("WPS_PBC") + if "PBC Status: Active" not in hapd.request("WPS_GET_STATUS"): + raise Exception("PBC status not shown correctly") + + dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True) + dev[0].request("WPS_PBC") + dev[0].wait_connected(timeout=20) + status = dev[0].get_status() + if status['wpa_state'] != 'COMPLETED' or status['bssid'] != bssid: + raise Exception("Not fully connected") + if status['ssid'] != new_ssid: + raise Exception("Unexpected SSID %s != %s" % (status['ssid'], new_ssid)) + dev[0].request("DISCONNECT") + dev[0].wait_disconnected() + def multi_check(apdev, dev, check, scan_opt=True): id = [] num_bss = len(check) diff --git a/tests/hwsim/test_ap_eap.py b/tests/hwsim/test_ap_eap.py index 7395c79..88e03df 100644 --- a/tests/hwsim/test_ap_eap.py +++ b/tests/hwsim/test_ap_eap.py @@ -5922,6 +5922,55 @@ def test_ap_wpa2_eap_tls_13(dev, apdev): dev[0].request("RECONNECT") dev[0].wait_connected() +def test_ap_wpa2_eap_ttls_13(dev, apdev): + """EAP-TTLS and TLS 1.3""" + params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap") + hapd = hostapd.add_ap(apdev[0], params) + + tls = dev[0].request("GET tls_library") + if "run=OpenSSL 1.1.1" not in tls: + raise HwsimSkip("TLS v1.3 not supported") + id = eap_connect(dev[0], hapd, "TTLS", "pap user", + anonymous_identity="ttls", password="password", + ca_cert="auth_serv/ca.pem", + phase1="tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 tls_disable_tlsv1_3=0", + phase2="auth=PAP") + ver = dev[0].get_status_field("eap_tls_version") + if ver != "TLSv1.3": + raise Exception("Unexpected TLS version") + + eap_reauth(dev[0], "TTLS") + dev[0].request("DISCONNECT") + dev[0].wait_disconnected() + dev[0].request("PMKSA_FLUSH") + dev[0].request("RECONNECT") + dev[0].wait_connected() + +def test_ap_wpa2_eap_peap_13(dev, apdev): + """PEAP and TLS 1.3""" + check_eap_capa(dev[0], "MSCHAPV2") + params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap") + hapd = hostapd.add_ap(apdev[0], params) + + tls = dev[0].request("GET tls_library") + if "run=OpenSSL 1.1.1" not in tls: + raise HwsimSkip("TLS v1.3 not supported") + id = eap_connect(dev[0], hapd, "PEAP", "user", + anonymous_identity="peap", password="password", + ca_cert="auth_serv/ca.pem", + phase1="tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 tls_disable_tlsv1_3=0", + phase2="auth=MSCHAPV2") + ver = dev[0].get_status_field("eap_tls_version") + if ver != "TLSv1.3": + raise Exception("Unexpected TLS version") + + eap_reauth(dev[0], "PEAP") + dev[0].request("DISCONNECT") + dev[0].wait_disconnected() + dev[0].request("PMKSA_FLUSH") + dev[0].request("RECONNECT") + dev[0].wait_connected() + def test_ap_wpa2_eap_tls_13_ec(dev, apdev): """EAP-TLS and TLS 1.3 (EC certificates)""" params = {"ssid": "test-wpa2-eap", diff --git a/tests/hwsim/test_ap_ft.py b/tests/hwsim/test_ap_ft.py index f2ca6f1..1598c89 100644 --- a/tests/hwsim/test_ap_ft.py +++ b/tests/hwsim/test_ap_ft.py @@ -136,7 +136,8 @@ def run_roams(dev, apdev, hapd0, hapd1, ssid, passphrase, over_ds=False, group_mgmt=None, ocv=None, sae_password=None, sae_password_id=None, sae_and_psk=False, pmksa_caching=False, roam_with_reassoc=False, also_non_ft=False, only_one_way=False, - wait_before_roam=0, return_after_initial=False, ieee80211w="1"): + wait_before_roam=0, return_after_initial=False, ieee80211w="1", + sae_transition=False): logger.info("Connect to first AP") copts = {} @@ -161,7 +162,9 @@ def run_roams(dev, apdev, hapd0, hapd1, ssid, passphrase, over_ds=False, copts["identity"] = eap_identity copts["password"] = "abcdefghijklmnop0123456789abcdef" else: - if sae: + if sae_transition: + copts["key_mgmt"] = "FT-SAE FT-PSK" + elif sae: copts["key_mgmt"] = "SAE FT-SAE" if sae_and_psk else "FT-SAE" else: copts["key_mgmt"] = "FT-PSK" @@ -998,7 +1001,8 @@ def test_ap_ft_over_ds_pull_vlan(dev, apdev): def start_ft_sae(dev, apdev, wpa_ptk_rekey=None, sae_pwe=None, rsne_override=None, rsnxe_override=None, no_beacon_rsnxe2=False, ext_key_id=False, - skip_prune_assoc=False, ft_rsnxe_used=False): + skip_prune_assoc=False, ft_rsnxe_used=False, + sae_transition=False): if "SAE" not in dev.get_capability("auth_alg"): raise HwsimSkip("SAE not supported") ssid = "test-ft" @@ -1022,7 +1026,8 @@ def start_ft_sae(dev, apdev, wpa_ptk_rekey=None, sae_pwe=None, params['ft_rsnxe_used'] = '1' hapd0 = hostapd.add_ap(apdev[0], params) params = ft_params2(ssid=ssid, passphrase=passphrase) - params['wpa_key_mgmt'] = "FT-SAE" + if not sae_transition: + params['wpa_key_mgmt'] = "FT-SAE" if wpa_ptk_rekey: params['wpa_ptk_rekey'] = str(wpa_ptk_rekey) if sae_pwe is not None: @@ -1041,7 +1046,7 @@ def start_ft_sae(dev, apdev, wpa_ptk_rekey=None, sae_pwe=None, params['ft_rsnxe_used'] = '1' hapd1 = hostapd.add_ap(apdev[1], params) key_mgmt = hapd1.get_config()['key_mgmt'] - if key_mgmt.split(' ')[0] != "FT-SAE": + if key_mgmt.split(' ')[0] != "FT-SAE" and not sae_transition: raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt) dev.request("SET sae_groups ") @@ -1052,6 +1057,12 @@ def test_ap_ft_sae(dev, apdev): hapd0, hapd1 = start_ft_sae(dev[0], apdev) run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True) +def test_ap_ft_sae_transition(dev, apdev): + """WPA2-PSK-FT-SAE/PSK AP""" + hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_transition=True) + run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", + sae_transition=True) + def test_ap_ft_sae_h2e(dev, apdev): """WPA2-PSK-FT-SAE AP (H2E)""" try: @@ -1595,7 +1606,14 @@ def test_ap_ft_eap_pull_wildcard_multi_bss(dev, apdev, params): f.write("interface=%s\n" % ifname2) f.write("bssid=%s\n" % bssid2) f.write("ctrl_interface=/var/run/hostapd\n") + + fields = ["ssid", "wpa_passphrase", "nas_identifier", "wpa_key_mgmt", + "wpa", "rsn_pairwise", "auth_server_addr"] + for name in fields: + f.write("%s=%s\n" % (name, params[name])) for name, val in params.items(): + if name in fields: + continue f.write("%s=%s\n" % (name, val)) hapd2 = hostapd.add_bss(apdev[0], ifname2, bssconf) @@ -3334,9 +3352,11 @@ def test_ap_ft_r0_key_expiration(dev, apdev): passphrase = "12345678" params = ft_params1(ssid=ssid, passphrase=passphrase) + params.pop('r0_key_lifetime', None) params['ft_r0_key_lifetime'] = "2" hapd0 = hostapd.add_ap(apdev[0], params) params = ft_params2(ssid=ssid, passphrase=passphrase) + params.pop('r0_key_lifetime', None) params['ft_r0_key_lifetime'] = "2" hapd1 = hostapd.add_ap(apdev[1], params) diff --git a/tests/hwsim/test_ap_open.py b/tests/hwsim/test_ap_open.py index 62b4446..a3bea76 100644 --- a/tests/hwsim/test_ap_open.py +++ b/tests/hwsim/test_ap_open.py @@ -198,19 +198,15 @@ def test_ap_open_unexpected_assoc_event(dev, apdev): dev[0].request("DISCONNECT") dev[0].wait_disconnected(timeout=15) dev[0].dump_monitor() - # This will be accepted due to matching network + # This association will be ignored by wpa_supplicant since the current + # state is not to try to connect after that DISCONNECT command. dev[0].cmd_execute(['iw', 'dev', dev[0].ifname, 'connect', 'open', "2412", apdev[0]['bssid']]) - dev[0].wait_connected(timeout=15) - dev[0].dump_monitor() - - dev[0].request("REMOVE_NETWORK all") - dev[0].wait_disconnected(timeout=5) + ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.3) + dev[0].cmd_execute(['iw', 'dev', dev[0].ifname, 'disconnect']) dev[0].dump_monitor() - # This will result in disconnection due to no matching network - dev[0].cmd_execute(['iw', 'dev', dev[0].ifname, 'connect', 'open', "2412", - apdev[0]['bssid']]) - dev[0].wait_disconnected(timeout=15) + if ev is not None: + raise Exception("Unexpected connection") def test_ap_open_external_assoc(dev, apdev): """AP with open mode and external association""" @@ -301,18 +297,18 @@ def test_ap_open_out_of_memory(dev, apdev): # not fail hostapd.add_ap(apdev[1], {"ssid": "open"}) -def test_bssid_black_white_list(dev, apdev): - """BSSID black/white list""" +def test_bssid_ignore_accept(dev, apdev): + """BSSID ignore/accept list""" hapd = hostapd.add_ap(apdev[0], {"ssid": "open"}) hapd2 = hostapd.add_ap(apdev[1], {"ssid": "open"}) dev[0].connect("open", key_mgmt="NONE", scan_freq="2412", - bssid_whitelist=apdev[1]['bssid']) + bssid_accept=apdev[1]['bssid']) dev[1].connect("open", key_mgmt="NONE", scan_freq="2412", - bssid_blacklist=apdev[1]['bssid']) + bssid_ignore=apdev[1]['bssid']) dev[2].connect("open", key_mgmt="NONE", scan_freq="2412", - bssid_whitelist="00:00:00:00:00:00/00:00:00:00:00:00", - bssid_blacklist=apdev[1]['bssid']) + bssid_accept="00:00:00:00:00:00/00:00:00:00:00:00", + bssid_ignore=apdev[1]['bssid']) if dev[0].get_status_field('bssid') != apdev[1]['bssid']: raise Exception("dev[0] connected to unexpected AP") if dev[1].get_status_field('bssid') != apdev[0]['bssid']: @@ -324,11 +320,11 @@ def test_bssid_black_white_list(dev, apdev): dev[2].request("REMOVE_NETWORK all") dev[2].connect("open", key_mgmt="NONE", scan_freq="2412", - bssid_whitelist="00:00:00:00:00:00", wait_connect=False) + bssid_accept="00:00:00:00:00:00", wait_connect=False) dev[0].connect("open", key_mgmt="NONE", scan_freq="2412", - bssid_whitelist="11:22:33:44:55:66/ff:00:00:00:00:00 " + apdev[1]['bssid'] + " aa:bb:cc:dd:ee:ff") + bssid_accept="11:22:33:44:55:66/ff:00:00:00:00:00 " + apdev[1]['bssid'] + " aa:bb:cc:dd:ee:ff") dev[1].connect("open", key_mgmt="NONE", scan_freq="2412", - bssid_blacklist="11:22:33:44:55:66/ff:00:00:00:00:00 " + apdev[1]['bssid'] + " aa:bb:cc:dd:ee:ff") + bssid_ignore="11:22:33:44:55:66/ff:00:00:00:00:00 " + apdev[1]['bssid'] + " aa:bb:cc:dd:ee:ff") if dev[0].get_status_field('bssid') != apdev[1]['bssid']: raise Exception("dev[0] connected to unexpected AP") if dev[1].get_status_field('bssid') != apdev[0]['bssid']: @@ -586,17 +582,17 @@ def test_ap_open_select_network(dev, apdev): dev[0].select_network(id1) dev[0].wait_connected() - res = dev[0].request("BLACKLIST") + res = dev[0].request("BSSID_IGNORE") if bssid1 in res or bssid2 in res: - raise Exception("Unexpected blacklist entry") + raise Exception("Unexpected BSSID ignore list entry") hwsim_utils.test_connectivity(dev[0], hapd1) dev[0].select_network(id2) dev[0].wait_connected() hwsim_utils.test_connectivity(dev[0], hapd2) - res = dev[0].request("BLACKLIST") + res = dev[0].request("BSSID_IGNORE") if bssid1 in res or bssid2 in res: - raise Exception("Unexpected blacklist entry(2)") + raise Exception("Unexpected BSSID ignore list entry(2)") @remote_compatible def test_ap_open_disable_enable(dev, apdev): @@ -917,9 +913,9 @@ def test_ap_open_disable_select(dev, apdev): dev[0].request("DISABLE_NETWORK %d" % id) dev[0].wait_disconnected() - res = dev[0].request("BLACKLIST") + res = dev[0].request("BSSID_IGNORE") if hapd1.own_addr() in res or hapd2.own_addr() in res: - raise Exception("Unexpected blacklist entry added") + raise Exception("Unexpected BSSID ignore list entry added") dev[0].request("SELECT_NETWORK %d" % id) dev[0].wait_connected() diff --git a/tests/hwsim/test_ap_params.py b/tests/hwsim/test_ap_params.py index 7918057..0735127 100644 --- a/tests/hwsim/test_ap_params.py +++ b/tests/hwsim/test_ap_params.py @@ -254,6 +254,29 @@ def test_ap_acl_mgmt(dev, apdev): if filename.startswith('/tmp/'): os.unlink(filename) +def test_ap_acl_accept_changes(dev, apdev): + """MAC ACL accept list changes""" + ssid = "acl" + params = {} + params['ssid'] = ssid + params['macaddr_acl'] = "1" + hapd = hostapd.add_ap(apdev[0], params) + hapd.request("ACCEPT_ACL ADD_MAC " + dev[0].own_addr()) + hapd.request("ACCEPT_ACL ADD_MAC " + dev[1].own_addr()) + dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412") + dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412") + dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412") + dev[1].connect(ssid, key_mgmt="NONE", scan_freq="2412") + hapd.request("ACCEPT_ACL DEL_MAC " + dev[0].own_addr()) + dev[0].wait_disconnected() + dev[0].request("DISCONNECT") + ev = dev[1].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.1) + if ev is not None: + raise Exception("Unexpected disconnection") + hapd.request("ACCEPT_ACL CLEAR") + dev[1].wait_disconnected() + dev[1].request("DISCONNECT") + @remote_compatible def test_ap_wds_sta(dev, apdev): """WPA2-PSK AP with STA using 4addr mode""" @@ -843,3 +866,29 @@ def test_ap_wowlan_triggers(dev, apdev): dev[0].scan_for_bss(bssid, freq="2412") dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412") hwsim_utils.test_connectivity(dev[0], hapd) + +def test_ap_notify_mgmt_frames(dev, apdev): + """hostapd notify_mgmt_frames configuration enabled""" + ssid = "mgmt_frames" + params = {'ssid': ssid, 'notify_mgmt_frames': "1"} + hapd = hostapd.add_ap(apdev[0], params) + bssid = hapd.own_addr() + dev[0].scan_for_bss(bssid, freq="2412") + dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412") + ev = hapd.wait_event(["AP-MGMT-FRAME-RECEIVED"], timeout=5) + if ev is None: + raise Exception("AP-MGMT-FRAME-RECEIVED wait timed out") + if "buf=b0" not in ev: + raise Exception("Expected auth request in AP-MGMT-FRAME-RECEIVED") + +def test_ap_notify_mgmt_frames_disabled(dev, apdev): + """hostapd notify_mgmt_frames configuration disabled""" + ssid = "mgmt_frames" + params = {'ssid': ssid, 'notify_mgmt_frames': "0"} + hapd = hostapd.add_ap(apdev[0], params) + bssid = hapd.own_addr() + dev[0].scan_for_bss(bssid, freq="2412") + dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412") + ev = hapd.wait_event(["AP-MGMT-FRAME-RECEIVED"], timeout=0.1) + if ev is not None: + raise Exception("Unexpected AP-MGMT-FRAME-RECEIVED") diff --git a/tests/hwsim/test_ap_psk.py b/tests/hwsim/test_ap_psk.py index 9bc1414..db10377 100644 --- a/tests/hwsim/test_ap_psk.py +++ b/tests/hwsim/test_ap_psk.py @@ -685,6 +685,8 @@ def setup_psk_ext(dev, apdev, wpa_ptk_rekey=None): def ext_4way_hs(hapd, dev): bssid = hapd.own_addr() addr = dev.own_addr() + first = None + last = None while True: ev = hapd.wait_event(["EAPOL-TX", "AP-STA-CONNECTED"], timeout=15) if ev is None: @@ -692,6 +694,9 @@ def ext_4way_hs(hapd, dev): if "AP-STA-CONNECTED" in ev: dev.wait_connected(timeout=15) break + if not first: + first = ev.split(' ')[2] + last = ev.split(' ')[2] res = dev.request("EAPOL_RX " + bssid + " " + ev.split(' ')[2]) if "OK" not in res: raise Exception("EAPOL_RX to wpa_supplicant failed") @@ -703,12 +708,41 @@ def ext_4way_hs(hapd, dev): res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2]) if "OK" not in res: raise Exception("EAPOL_RX to hostapd failed") + return first, last def test_ap_wpa2_psk_ext(dev, apdev): """WPA2-PSK AP using external EAPOL I/O""" hapd = setup_psk_ext(dev[0], apdev[0]) ext_4way_hs(hapd, dev[0]) +def test_ap_wpa2_psk_unexpected(dev, apdev): + """WPA2-PSK and supplicant receiving unexpected EAPOL-Key frames""" + hapd = setup_psk_ext(dev[0], apdev[0]) + first, last = ext_4way_hs(hapd, dev[0]) + + # Not associated - Delay processing of received EAPOL frame (state=COMPLETED + # bssid=02:00:00:00:03:00) + other = "02:11:22:33:44:55" + res = dev[0].request("EAPOL_RX " + other + " " + first) + if "OK" not in res: + raise Exception("EAPOL_RX to wpa_supplicant failed") + + # WPA: EAPOL-Key Replay Counter did not increase - dropping packet + bssid = hapd.own_addr() + res = dev[0].request("EAPOL_RX " + bssid + " " + last) + if "OK" not in res: + raise Exception("EAPOL_RX to wpa_supplicant failed") + + # WPA: Invalid EAPOL-Key MIC - dropping packet + msg = last[0:18] + '01' + last[20:] + res = dev[0].request("EAPOL_RX " + bssid + " " + msg) + if "OK" not in res: + raise Exception("EAPOL_RX to wpa_supplicant failed") + + ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=12) + if ev is not None: + raise Exception("Unexpected disconnection") + def test_ap_wpa2_psk_ext_retry_msg_3(dev, apdev): """WPA2-PSK AP using external EAPOL I/O and retry for EAPOL-Key msg 3/4""" hapd = setup_psk_ext(dev[0], apdev[0]) diff --git a/tests/hwsim/test_ap_roam.py b/tests/hwsim/test_ap_roam.py index fca3b57..8488889 100644 --- a/tests/hwsim/test_ap_roam.py +++ b/tests/hwsim/test_ap_roam.py @@ -26,8 +26,8 @@ def test_ap_roam_open(dev, apdev): dev[0].roam(apdev[0]['bssid']) hwsim_utils.test_connectivity(dev[0], hapd0) -def test_ap_blacklist_all(dev, apdev, params): - """Ensure we clear the blacklist if all visible APs reject""" +def test_ap_ignore_bssid_all(dev, apdev, params): + """Ensure we clear the ignore BSSID list if all visible APs reject""" hapd0 = hostapd.add_ap(apdev[0], {"ssid": "test-open", "max_num_sta": "0"}) hapd1 = hostapd.add_ap(apdev[1], {"ssid": "test-open", "max_num_sta": "0"}) bss0 = hapd0.own_addr() @@ -43,13 +43,13 @@ def test_ap_blacklist_all(dev, apdev, params): wait_connect=False, bssid=bss1) if not dev[0].wait_event(["CTRL-EVENT-AUTH-REJECT"], timeout=10): raise Exception("AP 1 didn't reject us") - blacklist = get_blacklist(dev[0]) - logger.info("blacklist: " + str(blacklist)) + ignore_list = get_bssid_ignore_list(dev[0]) + logger.info("ignore list: " + str(ignore_list)) dev[0].request("REMOVE_NETWORK all") dev[0].dump_monitor() hapd0.set("max_num_sta", "1") - # All visible APs were blacklisted; we should clear the blacklist and find + # All visible APs were ignored; we should clear the ignore list and find # the AP that now accepts us. dev[0].scan_for_bss(bss0, freq=2412) dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2412", bssid=bss0) @@ -141,8 +141,8 @@ def test_ap_roam_wpa2_psk_pmf_mismatch(dev, apdev): raise Exception("Unexpected BSSID reported after failed roam attempt: " + bssid) hwsim_utils.test_connectivity(dev[0], hapd0) -def get_blacklist(dev): - return dev.request("BLACKLIST").splitlines() +def get_bssid_ignore_list(dev): + return dev.request("BSSID_IGNORE").splitlines() def test_ap_reconnect_auth_timeout(dev, apdev, params): """Reconnect to 2nd AP and authentication times out""" @@ -162,7 +162,7 @@ def test_ap_reconnect_auth_timeout(dev, apdev, params): hapd1 = hostapd.add_ap(apdev[1], params) bssid1 = hapd1.own_addr() - wpas.request("BLACKLIST " + bssid0) + wpas.request("BSSID_IGNORE " + bssid0) wpas.scan_for_bss(bssid1, freq=2412) wpas.request("DISCONNECT") @@ -179,11 +179,11 @@ def test_ap_reconnect_auth_timeout(dev, apdev, params): if not ev: raise Exception("CTRL-EVENT-SCAN-STARTED not seen") - b = get_blacklist(wpas) + b = get_bssid_ignore_list(wpas) if '00:00:00:00:00:00' in b: - raise Exception("Unexpected blacklist contents: " + str(b)) + raise Exception("Unexpected ignore list contents: " + str(b)) if bssid1 not in b: - raise Exception("Unexpected blacklist contents: " + str(b)) + raise Exception("Unexpected ignore list contents: " + str(b)) def test_ap_roam_with_reassoc_auth_timeout(dev, apdev, params): """Roam using reassoc between two APs and authentication times out""" @@ -216,9 +216,9 @@ def test_ap_roam_with_reassoc_auth_timeout(dev, apdev, params): if not ev: raise Exception("CTRL-EVENT-SCAN-STARTED not seen") - b = get_blacklist(wpas) + b = get_bssid_ignore_list(wpas) if bssid0 in b: - raise Exception("Unexpected blacklist contents: " + str(b)) + raise Exception("Unexpected ignore list contents: " + str(b)) def test_ap_roam_wpa2_psk_failed(dev, apdev, params): """Roam failure with WPA2-PSK AP due to wrong passphrase""" diff --git a/tests/hwsim/test_ap_tdls.py b/tests/hwsim/test_ap_tdls.py index a2bf6d4..8cdd002 100644 --- a/tests/hwsim/test_ap_tdls.py +++ b/tests/hwsim/test_ap_tdls.py @@ -639,6 +639,8 @@ def test_ap_sae_tdls(dev, apdev): """SAE AP and two stations using TDLS""" check_sae_capab(dev[0]) check_sae_capab(dev[1]) + dev[0].request("SET sae_groups ") + dev[1].request("SET sae_groups ") params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE' params["ieee80211w"] = "2" diff --git a/tests/hwsim/test_dfs.py b/tests/hwsim/test_dfs.py index 3efe6bb..c587653 100644 --- a/tests/hwsim/test_dfs.py +++ b/tests/hwsim/test_dfs.py @@ -652,3 +652,116 @@ def test_dfs_chan_switch(dev, apdev): hwsim_utils.test_connectivity(dev[0], hapd) finally: clear_regdom(hapd, dev) + +@long_duration_test +def test_dfs_no_available_channel(dev, apdev): + """DFS and no available channel after radar detection""" + try: + hapd = None + hapd = start_dfs_ap(apdev[0], chanlist="56") + + ev = hapd.wait_event(["AP-ENABLED"], timeout=70) + if not ev: + raise Exception("AP2 setup timed out") + + dfs_simulate_radar(hapd) + ev = wait_dfs_event(hapd, "DFS-RADAR-DETECTED", 5) + if "freq=5260 ht_enabled=1 chan_offset=0 chan_width=1" not in ev: + raise Exception("Unexpected DFS radar detection freq from AP") + + ev = wait_dfs_event(hapd, "DFS-NEW-CHANNEL", 5) + if "freq=5280 chan=56" not in ev: + raise Exception("Unexpected DFS new freq: " + ev) + ev = wait_dfs_event(hapd, "DFS-CAC-START", 5) + if "freq=5280" not in ev: + raise Exception("Unexpected channel: " + ev) + ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 70) + if "success=1" not in ev: + raise Exception("CAC failed") + if "freq=5280" not in ev: + raise Exception("Unexpected DFS freq result") + ev = hapd.wait_event(["AP-ENABLED"], timeout=5) + if not ev: + raise Exception("AP setup timed out") + + dfs_simulate_radar(hapd) + ev = wait_dfs_event(hapd, "DFS-RADAR-DETECTED", 5) + if "freq=5280 ht_enabled=1 chan_offset=0 chan_width=1" not in ev: + raise Exception("Unexpected DFS radar detection freq from AP [2]") + + ev = hapd.wait_event(["AP-DISABLED"], timeout=10) + if ev is None: + raise Exception("AP was not disabled") + finally: + clear_regdom(hapd, dev) + +def dfs_chan_switch_precac(dev, apdev, country): + """DFS channel switch pre CAC""" + try: + hapd = None + + # Toggle regulatory - clean all preCAC + hostapd.cmd_execute(apdev[0], ['iw', 'reg', 'set', 'US']) + + hapd = start_dfs_ap(apdev[0], country=country) + + ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 70) + if "success=1" not in ev: + raise Exception("CAC failed") + if "freq=5260" not in ev: + raise Exception("Unexpected DFS freq result") + ev = hapd.wait_event(["AP-ENABLED"], timeout=5) + if not ev: + raise Exception("AP setup timed out") + freq = hapd.get_status_field("freq") + if freq != "5260": + raise Exception("Unexpected frequency") + + # TODO add/connect station here + # Today skip this step while dev[0].connect() + # for some reason toggle regulatory to US + # and clean preCAC + + # Back to non DFS channel + if "OK" not in hapd.request("CHAN_SWITCH 5 5180 ht"): + raise Exception("CHAN_SWITCH 5180 failed") + + ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=5) + if not ev: + raise Exception("No CSA finished event - 5180") + freq = hapd.get_status_field("freq") + if freq != "5180": + raise Exception("Unexpected frequency") + + # Today cfg80211 first send AP-CSA-FINISHED and next + # DFS-PRE-CAC-EXPIRED + ev = hapd.wait_event(["DFS-PRE-CAC-EXPIRED"], timeout=3) + if not ev and country == 'US': + raise Exception("US - no CAC-EXPIRED event") + + # Back again to DFS channel (CAC passed) + if "OK" not in hapd.request("CHAN_SWITCH 5 5260 ht"): + raise Exception("CHAN_SWITCH 5260 failed") + + if country == 'US': + # For non EU we should start CAC again + ev = wait_dfs_event(hapd, "DFS-CAC-START", 5) + if not ev: + raise Exception("No DFS CAC start event") + else: + # For EU preCAC should be used + ev = wait_dfs_event(hapd, "AP-CSA-FINISHED", 5) + if not ev: + raise Exception("No CSA finished event - 5260") + finally: + clear_regdom(hapd, dev) + +@long_duration_test +def test_dfs_eu_chan_switch_precac(dev, apdev): + """DFS channel switch pre CAC - ETSI domain""" + dfs_chan_switch_precac(dev, apdev, 'PL') + +@long_duration_test +def test_dfs_us_chan_switch_precac(dev, apdev): + """DFS channel switch pre CAC - FCC domain""" + dfs_chan_switch_precac(dev, apdev, 'US') diff --git a/tests/hwsim/test_dpp.py b/tests/hwsim/test_dpp.py index ff03814..233c6a5 100644 --- a/tests/hwsim/test_dpp.py +++ b/tests/hwsim/test_dpp.py @@ -2821,6 +2821,7 @@ def run_dpp_proto_init(dev, test_dev, test, mutual=False, unicast=True, own = id1b dev[1].dpp_auth_init(uri=uri0, role=role, configurator=configurator, conf=conf, own=own) + return uri0, role, configurator, conf, own def test_dpp_proto_after_wrapped_data_auth_req(dev, apdev): """DPP protocol testing - attribute after Wrapped Data in Auth Req""" @@ -3249,7 +3250,7 @@ def test_dpp_proto_stop_at_auth_req(dev, apdev): def test_dpp_proto_stop_at_auth_resp(dev, apdev): """DPP protocol testing - stop when receiving Auth Resp""" - run_dpp_proto_init(dev, 1, 88) + uri0, role, configurator, conf, own = run_dpp_proto_init(dev, 1, 88) ev = dev[1].wait_event(["DPP-TX "], timeout=5) if ev is None: @@ -3263,6 +3264,18 @@ def test_dpp_proto_stop_at_auth_resp(dev, apdev): if ev is not None: raise Exception("Unexpected Auth Conf TX") + ev = dev[0].wait_event(["DPP-FAIL"], timeout=2) + if ev is None or "No Auth Confirm received" not in ev: + raise Exception("DPP-FAIL for missing Auth Confirm not reported") + time.sleep(0.1) + + # Try again without special testing behavior to confirm Responder is able + # to accept a new provisioning attempt. + dev[1].set("dpp_test", "0") + dev[1].dpp_auth_init(uri=uri0, role=role, configurator=configurator, + conf=conf, own=own) + wait_auth_success(dev[0], dev[1]) + def test_dpp_proto_stop_at_auth_conf(dev, apdev): """DPP protocol testing - stop when receiving Auth Conf""" run_dpp_proto_init(dev, 0, 89, init_enrollee=True) @@ -5378,6 +5391,14 @@ def test_dpp_chirp_configurator(dev, apdev): if "type=13" not in ev: raise Exception("Unexpected DPP frame received: " + ev) + ev = dev[1].wait_event(["DPP-TX"], timeout=10) + if ev is None: + raise Exception("Authentication Request TX not seen") + if "type=0" not in ev: + raise Exception("Unexpected DPP frame TX: " + ev) + if "dst=" + dev[0].own_addr() not in ev: + raise Exception("Unexpected Authentication Request destination: " + ev) + wait_auth_success(dev[0], dev[1], dev[1], dev[0]) def test_dpp_chirp_configurator_inits(dev, apdev): @@ -5967,6 +5988,9 @@ def test_dpp_enterprise_reject(dev, apdev, params): def test_dpp_enterprise_tcp(dev, apdev, params): """DPP over TCP for enterprise provisioning""" + if not openssl_imported: + raise HwsimSkip("OpenSSL python method not available") + try: run_dpp_enterprise_tcp(dev, apdev, params) finally: @@ -6054,6 +6078,9 @@ def run_dpp_enterprise_tcp_end(params, dev, wt): def test_dpp_enterprise_tcp2(dev, apdev, params): """DPP over TCP for enterprise provisioning (Controller initiating)""" + if not openssl_imported: + raise HwsimSkip("OpenSSL python method not available") + try: run_dpp_enterprise_tcp2(dev, apdev, params) finally: diff --git a/tests/hwsim/test_eap_proto.py b/tests/hwsim/test_eap_proto.py index 7494b42..3401749 100644 --- a/tests/hwsim/test_eap_proto.py +++ b/tests/hwsim/test_eap_proto.py @@ -5629,8 +5629,7 @@ def test_eap_proto_aka_errors(dev, apdev): tests = [(1, "=eap_aka_learn_ids"), (2, "=eap_aka_learn_ids"), (1, "eap_sim_parse_encr;eap_aka_process_challenge"), - (1, "wpabuf_dup;eap_aka_add_id_msg"), - (1, "wpabuf_resize;eap_aka_add_id_msg"), + (1, "wpabuf_alloc;eap_aka_add_id_msg"), (1, "eap_aka_getKey"), (1, "eap_aka_get_emsk"), (1, "eap_aka_get_session_id")] diff --git a/tests/hwsim/test_ext_password.py b/tests/hwsim/test_ext_password.py index dfaf3ea..789b673 100644 --- a/tests/hwsim/test_ext_password.py +++ b/tests/hwsim/test_ext_password.py @@ -7,6 +7,8 @@ from remotehost import remote_compatible import logging logger = logging.getLogger() +import os +import tempfile import hostapd from utils import skip_with_fips @@ -79,3 +81,32 @@ def test_ext_password_interworking(dev, apdev): dev[0].set_cred(id, "password", "ext:pw1") interworking_select(dev[0], bssid, freq="2412") interworking_connect(dev[0], bssid, "TTLS") + +def test_ext_password_file_psk(dev, apdev): + """External password (file) storage for PSK""" + params = hostapd.wpa2_params(ssid="ext-pw-psk", passphrase="12345678") + hostapd.add_ap(apdev[0], params) + fd, fn = tempfile.mkstemp() + with open(fn, "w") as f: + f.write("psk1=12345678\n") + os.close(fd) + dev[0].request("SET ext_password_backend file:%s" % fn) + dev[0].connect("ext-pw-psk", raw_psk="ext:psk1", scan_freq="2412") + for i in range(2): + dev[0].request("REMOVE_NETWORK all") + if i == 0: + dev[0].wait_disconnected() + dev[0].connect("ext-pw-psk", raw_psk="ext:psk2", scan_freq="2412", + wait_connect=False) + else: + dev[0].connect("ext-pw-psk", raw_psk="ext:psk1", scan_freq="2412", + wait_connect=False) + ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED", + "EXT PW: No PSK found from external storage"], + timeout=10) + if i == 0: + os.unlink(fn) + if ev is None: + raise Exception("No connection result reported") + if "CTRL-EVENT-CONNECTED" in ev: + raise Exception("Unexpected connection") diff --git a/tests/hwsim/test_fils.py b/tests/hwsim/test_fils.py index 8e4df65..4616ca3 100644 --- a/tests/hwsim/test_fils.py +++ b/tests/hwsim/test_fils.py @@ -2330,3 +2330,37 @@ def test_fils_auth_ptk_rekey_ap_ext_key_id(dev, apdev, params): hwsim_utils.test_connectivity(dev[0], hapd) finally: dev[0].set("extended_key_id", "0") + +def test_fils_discovery_frame(dev, apdev, params): + """FILS Discovery frame generation""" + check_fils_capa(dev[0]) + check_erp_capa(dev[0]) + + start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst")) + + bssid = apdev[0]['bssid'] + params = hostapd.wpa2_eap_params(ssid="fils") + params['wpa_key_mgmt'] = "FILS-SHA256" + params['auth_server_port'] = "18128" + params['erp_send_reauth_start'] = '1' + params['erp_domain'] = 'example.com' + params['fils_realm'] = 'example.com' + params['wpa_group_rekey'] = '1' + params['fils_discovery_min_interval'] = '20' + params['fils_discovery_max_interval'] = '20' + hapd = hostapd.add_ap(apdev[0]['ifname'], params, no_enable=True) + + if "OK" not in hapd.request("ENABLE"): + raise HwsimSkip("FILS Discovery frame transmission not supported") + + ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=5) + if ev is None: + raise Exception("AP startup timed out") + if "AP-ENABLED" not in ev: + raise Exception("AP startup failed") + + dev[0].request("ERP_FLUSH") + dev[0].connect("fils", key_mgmt="FILS-SHA256", + eap="PSK", identity="psk.user@example.com", + password_hex="0123456789abcdef0123456789abcdef", + erp="1", scan_freq="2412") diff --git a/tests/hwsim/test_he.py b/tests/hwsim/test_he.py index 28123e8..21c1b55 100644 --- a/tests/hwsim/test_he.py +++ b/tests/hwsim/test_he.py @@ -67,6 +67,12 @@ def test_he_params(dev, apdev): "he_spr_non_srg_obss_pd_max_offset": "0", "he_spr_srg_obss_pd_min_offset": "0", "he_spr_srg_obss_pd_max_offset": "0", + "he_spr_srg_bss_colors": "1 2 10 63", + "he_spr_srg_partial_bssid": "0 1 3 63", + "he_6ghz_max_ampdu_len_exp": "7", + "he_6ghz_rx_ant_pat": "1", + "he_6ghz_tx_ant_pat": "1", + "he_6ghz_max_mpdu": "2", "he_oper_chwidth": "0", "he_oper_centr_freq_seg0_idx": "1", "he_oper_centr_freq_seg1_idx": "0"} @@ -154,27 +160,19 @@ def test_he80(dev, apdev): dev[0].request("DISCONNECT") clear_regdom(hapd, dev) -def test_he_wifi_generation(dev, apdev): +def _test_he_wifi_generation(dev, apdev, conf, scan_freq): """HE and wifi_generation""" try: hapd = None params = {"ssid": "he", "country_code": "FI", - "hw_mode": "a", - "channel": "36", - "ht_capab": "[HT40+]", "ieee80211n": "1", - "ieee80211ac": "1", - "ieee80211ax": "1", - "vht_oper_chwidth": "1", - "vht_capab": "[MAX-MPDU-11454]", - "vht_oper_centr_freq_seg0_idx": "42", - "he_oper_chwidth": "1", - "he_oper_centr_freq_seg0_idx": "42"} + "ieee80211ax": "1"} + params.update(conf) hapd = hostapd.add_ap(apdev[0], params) bssid = apdev[0]['bssid'] - dev[0].connect("he", key_mgmt="NONE", scan_freq="5180") + dev[0].connect("he", key_mgmt="NONE", scan_freq=scan_freq) status = dev[0].get_status() if 'wifi_generation' not in status: # For now, assume this is because of missing kernel support @@ -185,7 +183,7 @@ def test_he_wifi_generation(dev, apdev): wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') wpas.interface_add("wlan5", drv_params="force_connect_cmd=1") - wpas.connect("he", key_mgmt="NONE", scan_freq="5180") + wpas.connect("he", key_mgmt="NONE", scan_freq=scan_freq) status = wpas.get_status() if 'wifi_generation' not in status: # For now, assume this is because of missing kernel support @@ -202,6 +200,27 @@ def test_he_wifi_generation(dev, apdev): dev[0].request("DISCONNECT") clear_regdom(hapd, dev) +def test_he_wifi_generation(dev, apdev): + conf = { + "vht_oper_chwidth": "1", + "hw_mode": "a", + "channel": "36", + "ht_capab": "[HT40+]", + "vht_oper_centr_freq_seg0_idx": "42", + "he_oper_chwidth": "1", + "he_oper_centr_freq_seg0_idx": "42", + "vht_capab": "[MAX-MPDU-11454]", + "ieee80211ac": "1", + } + _test_he_wifi_generation(dev, apdev, conf, "5180") + +def test_he_wifi_generation_24(dev, apdev): + conf = { + "hw_mode": "g", + "channel": "1", + } + _test_he_wifi_generation(dev, apdev, conf, "2412") + def he80_test(apdev, dev, channel, ht_capab): clear_scan_cache(apdev) try: @@ -992,7 +1011,7 @@ def test_he_tkip(dev, apdev): raise Exception("Unexpected STATUS ieee80211n value") if status["ieee80211ac"] != "0": raise Exception("Unexpected STATUS ieee80211ac value") - if status["ieee80211ax"] != "1": + if status["ieee80211ax"] != "0": raise Exception("Unexpected STATUS ieee80211ax value") if status["secondary_channel"] != "0": raise Exception("Unexpected STATUS secondary_channel value") diff --git a/tests/hwsim/test_multi_ap.py b/tests/hwsim/test_multi_ap.py index 4070d3e..ff761f3 100644 --- a/tests/hwsim/test_multi_ap.py +++ b/tests/hwsim/test_multi_ap.py @@ -5,6 +5,7 @@ # See README for more details. import hostapd +from wpasupplicant import WpaSupplicant from utils import * def test_multi_ap_association(dev, apdev): @@ -38,6 +39,42 @@ def run_multi_ap_association(dev, apdev, multi_ap, wait_connect=True): dev[0].connect("multi-ap", psk="12345678", scan_freq="2412", multi_ap_backhaul_sta="1", wait_connect=wait_connect) +def test_multi_ap_backhaul_roam_with_bridge(dev, apdev): + """Multi-AP backhaul BSS reassociation to another BSS with bridge""" + br_ifname = 'sta-br0' + ifname = 'wlan5' + try: + run_multi_ap_backhaul_roam_with_bridge(dev, apdev) + finally: + subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'down']) + subprocess.call(['brctl', 'delif', br_ifname, ifname]) + subprocess.call(['brctl', 'delbr', br_ifname]) + subprocess.call(['iw', ifname, 'set', '4addr', 'off']) + +def run_multi_ap_backhaul_roam_with_bridge(dev, apdev): + br_ifname = 'sta-br0' + ifname = 'wlan5' + wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') + subprocess.call(['brctl', 'addbr', br_ifname]) + subprocess.call(['brctl', 'setfd', br_ifname, '0']) + subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'up']) + subprocess.call(['iw', ifname, 'set', '4addr', 'on']) + subprocess.check_call(['brctl', 'addif', br_ifname, ifname]) + wpas.interface_add(ifname, br_ifname=br_ifname) + wpas.flush_scan_cache() + + params = hostapd.wpa2_params(ssid="multi-ap", passphrase="12345678") + params["multi_ap"] = "1" + hapd = hostapd.add_ap(apdev[0], params) + + wpas.connect("multi-ap", psk="12345678", scan_freq="2412", + multi_ap_backhaul_sta="1") + + hapd2 = hostapd.add_ap(apdev[1], params) + bssid2 = hapd2.own_addr() + wpas.scan_for_bss(bssid2, freq="2412", force_scan=True) + wpas.roam(bssid2) + def test_multi_ap_disabled_on_ap(dev, apdev): """Multi-AP association attempt when disabled on AP""" run_multi_ap_association(dev, apdev, 0, wait_connect=False) @@ -63,7 +100,8 @@ def test_multi_ap_fronthaul_on_ap(dev, apdev): if "CTRL-EVENT-DISCONNECTED" not in ev: raise Exception("Unexpected connection result") -def run_multi_ap_wps(dev, apdev, params, multi_ap_bssid=None): +def run_multi_ap_wps(dev, apdev, params, params_backhaul=None, add_apdev=False, + run_csa=False, allow_csa_fail=False): """Helper for running Multi-AP WPS tests dev[0] does multi_ap WPS, dev[1] does normal WPS. apdev[0] is the fronthaul @@ -72,8 +110,14 @@ def run_multi_ap_wps(dev, apdev, params, multi_ap_bssid=None): the WPS parameters. multi_ap_bssid must be given if it is not equal to the fronthaul BSSID.""" - if multi_ap_bssid is None: + wpas_apdev = None + + if params_backhaul: + hapd_backhaul = hostapd.add_ap(apdev[1], params_backhaul) + multi_ap_bssid = hapd_backhaul.own_addr() + else: multi_ap_bssid = apdev[0]['bssid'] + params.update({"wps_state": "2", "eap_server": "1"}) # WPS with multi-ap station dev[0] @@ -130,6 +174,42 @@ def run_multi_ap_wps(dev, apdev, params, multi_ap_bssid=None): if len(dev[1].list_networks()) != 1: raise Exception("Unexpected number of network blocks") + try: + # Add apdev to the same phy that dev[0] + if add_apdev: + wpas_apdev = {} + wpas_apdev['ifname'] = dev[0].ifname + "_ap" + status, buf = dev[0].cmd_execute(['iw', dev[0].ifname, + 'interface', 'add', + wpas_apdev['ifname'], + 'type', 'managed']) + if status != 0: + raise Exception("iw interface add failed") + wpas_hapd = hostapd.add_ap(wpas_apdev, params) + + if run_csa: + if 'OK' not in hapd.request("CHAN_SWITCH 5 2462 ht"): + raise Exception("chan switch request failed") + + ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=5) + if not ev: + raise Exception("chan switch failed") + + # now check station + ev = dev[0].wait_event(["CTRL-EVENT-CHANNEL-SWITCH", + "CTRL-EVENT-DISCONNECTED"], timeout=5) + if not ev: + raise Exception("sta - no chanswitch event") + if "CTRL-EVENT-CHANNEL-SWITCH" not in ev and not allow_csa_fail: + raise Exception("Received disconnection event instead of channel switch event") + + if add_apdev: + dev[0].cmd_execute(['iw', wpas_apdev['ifname'], 'del']) + except: + if wpas_apdev: + dev[0].cmd_execute(['iw', wpas_apdev['ifname'], 'del']) + raise + def test_multi_ap_wps_shared(dev, apdev): """WPS on shared fronthaul/backhaul AP""" ssid = "multi-ap-wps" @@ -140,6 +220,30 @@ def test_multi_ap_wps_shared(dev, apdev): "multi_ap_backhaul_wpa_passphrase": passphrase}) run_multi_ap_wps(dev, apdev, params) +def test_multi_ap_wps_shared_csa(dev, apdev): + """WPS on shared fronthaul/backhaul AP, run CSA""" + ssid = "multi-ap-wps-csa" + passphrase = "12345678" + params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase) + params.update({"multi_ap": "3", + "multi_ap_backhaul_ssid": '"%s"' % ssid, + "multi_ap_backhaul_wpa_passphrase": passphrase}) + run_multi_ap_wps(dev, apdev, params, run_csa=True) + +def test_multi_ap_wps_shared_apdev_csa(dev, apdev): + """WPS on shared fronthaul/backhaul AP add apdev on same phy and run CSA""" + ssid = "multi-ap-wps-apdev-csa" + passphrase = "12345678" + params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase) + params.update({"multi_ap": "3", + "multi_ap_backhaul_ssid": '"%s"' % ssid, + "multi_ap_backhaul_wpa_passphrase": passphrase}) + # This case is currently failing toc omplete CSA on the station interface. + # For the time being, ignore that to avoid always failing tests. Full + # validation can be enabled once the issue behind this is fixed. + run_multi_ap_wps(dev, apdev, params, add_apdev=True, run_csa=True, + allow_csa_fail=True) + def test_multi_ap_wps_shared_psk(dev, apdev): """WPS on shared fronthaul/backhaul AP using PSK""" ssid = "multi-ap-wps" @@ -163,9 +267,8 @@ def test_multi_ap_wps_split(dev, apdev): params_backhaul = hostapd.wpa2_params(ssid=backhaul_ssid, passphrase=backhaul_passphrase) params_backhaul.update({"multi_ap": "1"}) - hapd_backhaul = hostapd.add_ap(apdev[1], params_backhaul) - run_multi_ap_wps(dev, apdev, params, hapd_backhaul.own_addr()) + run_multi_ap_wps(dev, apdev, params, params_backhaul) def test_multi_ap_wps_split_psk(dev, apdev): """WPS on split fronthaul and backhaul AP""" @@ -178,9 +281,8 @@ def test_multi_ap_wps_split_psk(dev, apdev): "multi_ap_backhaul_wpa_psk": backhaul_psk}) params_backhaul = hostapd.wpa2_params(ssid=backhaul_ssid) params_backhaul.update({"multi_ap": "1", "wpa_psk": backhaul_psk}) - hapd_backhaul = hostapd.add_ap(apdev[1], params_backhaul) - run_multi_ap_wps(dev, apdev, params, hapd_backhaul.own_addr()) + run_multi_ap_wps(dev, apdev, params, params_backhaul) def test_multi_ap_wps_split_mixed(dev, apdev): """WPS on split fronthaul and backhaul AP with mixed-mode fronthaul""" @@ -195,9 +297,8 @@ def test_multi_ap_wps_split_mixed(dev, apdev): params_backhaul = hostapd.wpa2_params(ssid=backhaul_ssid, passphrase=backhaul_passphrase) params_backhaul.update({"multi_ap": "1"}) - hapd_backhaul = hostapd.add_ap(apdev[1], params_backhaul) - run_multi_ap_wps(dev, apdev, params, hapd_backhaul.own_addr()) + run_multi_ap_wps(dev, apdev, params, params_backhaul) def test_multi_ap_wps_split_open(dev, apdev): """WPS on split fronthaul and backhaul AP with open fronthaul""" @@ -209,9 +310,8 @@ def test_multi_ap_wps_split_open(dev, apdev): params_backhaul = hostapd.wpa2_params(ssid=backhaul_ssid, passphrase=backhaul_passphrase) params_backhaul.update({"multi_ap": "1"}) - hapd_backhaul = hostapd.add_ap(apdev[1], params_backhaul) - run_multi_ap_wps(dev, apdev, params, hapd_backhaul.own_addr()) + run_multi_ap_wps(dev, apdev, params, params_backhaul) def test_multi_ap_wps_fail_non_multi_ap(dev, apdev): """Multi-AP WPS on non-WPS AP fails""" diff --git a/tests/hwsim/test_p2p_channel.py b/tests/hwsim/test_p2p_channel.py index 87a1a24..d57234d 100644 --- a/tests/hwsim/test_p2p_channel.py +++ b/tests/hwsim/test_p2p_channel.py @@ -17,7 +17,7 @@ from tshark import run_tshark from wpasupplicant import WpaSupplicant from hwsim import HWSimRadio from p2p_utils import * -from utils import clear_regdom_dev +from utils import * def set_country(country, dev=None): subprocess.call(['iw', 'reg', 'set', country]) @@ -226,6 +226,7 @@ def test_p2p_channel_avoid2(dev): def test_p2p_channel_avoid3(dev): """P2P and avoid frequencies driver event on 5 GHz""" try: + dev[0].global_request("SET p2p_pref_chan 128:44") set_country("CN", dev[0]) form(dev[0], dev[1]) set_country("CN", dev[0]) @@ -251,6 +252,7 @@ def test_p2p_channel_avoid3(dev): finally: set_country("00") dev[0].request("DRIVER_EVENT AVOID_FREQUENCIES") + dev[0].global_request("SET p2p_pref_chan ") dev[1].flush_scan_cache() @remote_compatible @@ -603,7 +605,7 @@ def test_p2p_autogo_pref_chan_not_in_regulatory(dev, apdev): raise Exception("Unexpected number of network blocks: " + str(netw)) id = netw[0]['id'] - set_country("SE", dev[0]) + set_country("JP", dev[0]) res = autogo(dev[0], persistent=id) if res['freq'] == "5745": raise Exception("Unexpected channel selected(2): " + res['freq']) @@ -950,7 +952,8 @@ def _test_p2p_go_move_scm_peer_supports(dev, apdev): dev[0].remove_group() finally: dev[0].global_request("SET p2p_go_freq_change_policy 2") - set_country("00") + disable_hapd(hapd) + clear_regdom_dev(dev, 1) def test_p2p_go_move_scm_peer_does_not_support(dev, apdev): """No P2P GO move due to SCM operation (peer does not supports)""" @@ -996,6 +999,7 @@ def _test_p2p_go_move_scm_peer_does_not_support(dev, apdev): finally: dev[0].global_request("SET p2p_go_freq_change_policy 2") dev[1].request("DRIVER_EVENT AVOID_FREQUENCIES") + disable_hapd(hapd) clear_regdom_dev(dev, 2) def test_p2p_go_move_scm_multi(dev, apdev): diff --git a/tests/hwsim/test_p2p_discovery.py b/tests/hwsim/test_p2p_discovery.py index f4353e8..4bce4ec 100644 --- a/tests/hwsim/test_p2p_discovery.py +++ b/tests/hwsim/test_p2p_discovery.py @@ -7,9 +7,12 @@ from remotehost import remote_compatible import logging logger = logging.getLogger() +import binascii import os +import struct import time +import hostapd import hwsim_utils from wpasupplicant import WpaSupplicant from p2p_utils import * @@ -776,3 +779,42 @@ def test_discovery_device_name_change(dev): raise Exception("Unexpected device name(2): " + ev) wpas.p2p_stop_find() dev[0].p2p_stop_find() + +def test_p2p_group_cli_invalid(dev, apdev): + """P2P device discovery with invalid group client info""" + attr = struct.pack('<BHBB', 2, 2, 0x25, 0x09) + + attr += struct.pack('<BH', 3, 6) + "\x02\x02\x02\x02\x02\x00".encode() + + cli = bytes() + cli += "\x02\x02\x02\x02\x02\x03".encode() + cli += "\x02\x02\x02\x02\x02\x04".encode() + cli += struct.pack('>BH', 0, 0x3148) + dev_type = "\x00\x00\x00\x00\x00\x00\x00\x01".encode() + cli += dev_type + num_sec = 25 + cli += struct.pack('B', num_sec) + cli += num_sec * dev_type + name = "TEST".encode() + cli += struct.pack('>HH', 0x1011, len(name)) + name + desc = struct.pack('B', len(cli)) + cli + attr += struct.pack('<BH', 14, len(desc)) + desc + + p2p_ie = struct.pack('>BBL', 0xdd, 4 + len(attr), 0x506f9a09) + attr + ie = binascii.hexlify(p2p_ie).decode() + + params = {"ssid": "DIRECT-test", + "eap_server": "1", + "wps_state": "2", + "wpa_passphrase": "12345678", + "wpa": "2", + "wpa_key_mgmt": "WPA-PSK", + "rsn_pairwise": "CCMP", + "vendor_elements": ie} + hapd = hostapd.add_ap(apdev[0], params) + + for i in range(2): + dev[i].p2p_find(social=True) + ev = dev[i].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5) + if not ev: + raise Exception("P2P device not found") diff --git a/tests/hwsim/test_pasn.py b/tests/hwsim/test_pasn.py new file mode 100644 index 0000000..3ee8cf5 --- /dev/null +++ b/tests/hwsim/test_pasn.py @@ -0,0 +1,638 @@ +# Test cases for PASN +# Copyright (C) 2019 Intel Corporation +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. + +from remotehost import remote_compatible +import binascii +import os +import time +import logging +logger = logging.getLogger() +import socket +import struct +import subprocess + +import hwsim_utils +import hostapd +from wpasupplicant import WpaSupplicant +from utils import * +from hwsim import HWSimRadio +from test_erp import check_erp_capa, start_erp_as +from test_fils import check_fils_capa +from test_ap_ft import run_roams, ft_params1, ft_params2 + +def check_pasn_capab(dev): + if "PASN" not in dev.get_capability("auth_alg"): + raise HwsimSkip("PASN not supported") + +def pasn_ap_params(akmp="PASN", cipher="CCMP", group="19"): + params = {"ssid": "test-wpa2-pasn", + "wpa_passphrase": "12345678", + "wpa": "2", + "ieee80211w": "2", + "wpa_key_mgmt": "WPA-PSK " + akmp, + "rsn_pairwise": cipher, + "pasn_groups" : group} + + return params + +def start_pasn_ap(apdev, params): + try: + return hostapd.add_ap(apdev, params) + except Exception as e: + if "Failed to set hostapd parameter wpa_key_mgmt" in str(e) or \ + "Failed to set hostapd parameter force_kdk_derivation" in str(e): + raise HwsimSkip("PASN not supported") + raise + +def check_pasn_ptk(dev, hapd, cipher): + sta_ptksa = dev.get_ptksa(hapd.own_addr(), cipher) + ap_ptksa = hapd.get_ptksa(dev.own_addr(), cipher) + if not (sta_ptksa and ap_ptksa): + raise Exception("Could not get PTKSA entry") + + logger.info("sta: TK: %s KDK: %s" % (sta_ptksa['tk'], sta_ptksa['kdk'])) + logger.info("ap : TK: %s KDK: %s" % (ap_ptksa['tk'], ap_ptksa['kdk'])) + + if sta_ptksa['tk'] != ap_ptksa['tk'] or sta_ptksa['kdk'] != ap_ptksa['kdk']: + raise Exception("TK/KDK mismatch") + +def check_pasn_akmp_cipher(dev, hapd, akmp="PASN", cipher="CCMP", + group="19", status=0, fail=0, nid=""): + dev.flush_scan_cache() + dev.scan(type="ONLY", freq=2412) + + cmd = "PASN_START bssid=%s akmp=%s cipher=%s group=%s" % (hapd.own_addr(), akmp, cipher, group) + + resp = dev.request(cmd) + if nid != "": + cmd += " nid=%s" % nid + + if fail: + if "OK" in resp: + raise Exception("Unexpected success to start PASN authentication") + return + + if "OK" not in resp: + raise Exception("Failed to start PASN authentication") + + ev = dev.wait_event(["PASN-AUTH-STATUS"], 3) + if not ev: + raise Exception("PASN: PASN-AUTH-STATUS not seen") + + if hapd.own_addr() + " akmp=" + akmp + ", status=" + str(status) not in ev: + raise Exception("PASN: unexpected status") + + if status: + return + + check_pasn_ptk(dev, hapd, cipher) + +@remote_compatible +def test_pasn_ccmp(dev, apdev): + """PASN authentication with WPA2/CCMP AP""" + check_pasn_capab(dev[0]) + + params = pasn_ap_params("PASN", "CCMP", "19") + hapd = start_pasn_ap(apdev[0], params) + + check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP") + +@remote_compatible +def test_pasn_gcmp(dev, apdev): + """PASN authentication with WPA2/GCMP AP""" + check_pasn_capab(dev[0]) + + params = pasn_ap_params("PASN", "GCMP", "19") + hapd = start_pasn_ap(apdev[0], params) + + check_pasn_akmp_cipher(dev[0], hapd, "PASN", "GCMP") + +@remote_compatible +def test_pasn_ccmp_256(dev, apdev): + """PASN authentication with WPA2/CCMP256 AP""" + check_pasn_capab(dev[0]) + + params = pasn_ap_params("PASN", "CCMP-256", "19") + hapd = start_pasn_ap(apdev[0], params) + + check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP-256") + +@remote_compatible +def test_pasn_gcmp_256(dev, apdev): + """PASN authentication with WPA2/GCMP-256 AP""" + check_pasn_capab(dev[0]) + + params = pasn_ap_params("PASN", "GCMP-256", "19") + hapd = start_pasn_ap(apdev[0], params) + + check_pasn_akmp_cipher(dev[0], hapd, "PASN", "GCMP-256") + +@remote_compatible +def test_pasn_group_mismatch(dev, apdev): + """PASN authentication with WPA2/CCMP AP with group mismatch""" + check_pasn_capab(dev[0]) + + params = pasn_ap_params("PASN", "CCMP", "20") + hapd = start_pasn_ap(apdev[0], params) + + check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP", status=77) + +@remote_compatible +def test_pasn_channel_mismatch(dev, apdev): + """PASN authentication with WPA2/CCMP AP with channel mismatch""" + check_pasn_capab(dev[0]) + + params = pasn_ap_params("PASN", "CCMP") + params['channel'] = "6" + hapd = start_pasn_ap(apdev[0], params) + + check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP", fail=1) + +@remote_compatible +def test_pasn_while_connected_same_channel(dev, apdev): + """PASN authentication with WPA2/CCMP AP while connected same channel""" + check_pasn_capab(dev[0]) + + ssid = "test-wpa2-psk" + psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6' + params = hostapd.wpa2_params(ssid=ssid) + params['wpa_psk'] = psk + hapd = start_pasn_ap(apdev[0], params) + + dev[0].connect(ssid, raw_psk=psk, scan_freq="2412") + + params = pasn_ap_params("PASN", "CCMP") + hapd = start_pasn_ap(apdev[1], params) + + check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP") + +@remote_compatible +def test_pasn_while_connected_same_ap(dev, apdev): + """PASN authentication with WPA2/CCMP AP while connected to it""" + check_pasn_capab(dev[0]) + + params = hostapd.wpa2_params(ssid="test-wpa2-psk", + passphrase="12345678") + hapd = start_pasn_ap(apdev[0], params) + + dev[0].connect("test-wpa2-psk", psk="12345678", scan_freq="2412") + + check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP", fail=1) + +@remote_compatible +def test_pasn_while_connected_diff_channel(dev, apdev): + """PASN authentication with WPA2/CCMP AP while connected diff channel""" + check_pasn_capab(dev[0]) + + with HWSimRadio(n_channels=2) as (radio, iface): + wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') + wpas.interface_add(iface) + + if wpas.get_mcc() < 2: + raise HwsimSkip("PASN: New radio does not support MCC") + + params = hostapd.wpa2_params(ssid="test-wpa2-psk", + passphrase="12345678") + params['channel'] = "6" + hapd = start_pasn_ap(apdev[0], params) + wpas.connect("test-wpa2-psk", psk="12345678", scan_freq="2437") + + params = pasn_ap_params("PASN", "CCMP") + hapd2 = start_pasn_ap(apdev[1], params) + + check_pasn_akmp_cipher(wpas, hapd2, "PASN", "CCMP") + +@remote_compatible +def test_pasn_sae_pmksa_cache(dev, apdev): + """PASN authentication with SAE AP with PMKSA caching""" + check_pasn_capab(dev[0]) + check_sae_capab(dev[0]) + + params = hostapd.wpa2_params(ssid="test-sae", + passphrase="12345678") + params['wpa_key_mgmt'] = 'SAE PASN' + hapd = start_pasn_ap(apdev[0], params) + + dev[0].set("sae_groups", "19") + dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412") + + hapd.wait_sta() + hwsim_utils.test_connectivity(dev[0], hapd) + + dev[0].request("DISCONNECT") + dev[0].wait_disconnected() + + check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP") + +def check_pasn_fils_pmksa_cache(dev, apdev, params, key_mgmt): + check_fils_capa(dev[0]) + check_erp_capa(dev[0]) + check_pasn_capab(dev[0]) + + start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst")) + + bssid = apdev[0]['bssid'] + params = hostapd.wpa2_eap_params(ssid="fils") + params['wpa_key_mgmt'] = key_mgmt + " PASN" + params['auth_server_port'] = "18128" + params['erp_domain'] = 'example.com' + params['fils_realm'] = 'example.com' + hapd = start_pasn_ap(apdev[0], params) + + dev[0].scan_for_bss(bssid, freq=2412) + dev[0].request("ERP_FLUSH") + + id = dev[0].connect("fils", key_mgmt=key_mgmt, + eap="PSK", identity="psk.user@example.com", + password_hex="0123456789abcdef0123456789abcdef", + erp="1", scan_freq="2412") + pmksa = dev[0].get_pmksa(bssid) + if pmksa is None: + raise Exception("No PMKSA cache entry created") + + hapd.wait_sta() + hwsim_utils.test_connectivity(dev[0], hapd) + + dev[0].request("DISCONNECT") + dev[0].wait_disconnected() + + check_pasn_akmp_cipher(dev[0], hapd, key_mgmt, "CCMP") + +@remote_compatible +def test_pasn_fils_sha256_pmksa_cache(dev, apdev, params): + """PASN authentication with FILS-SHA256 with PMKSA caching""" + check_pasn_fils_pmksa_cache(dev, apdev, params, "FILS-SHA256") + +@remote_compatible +def test_pasn_fils_sha384_pmksa_cache(dev, apdev, params): + """PASN authentication with FILS-SHA384 with PMKSA caching""" + check_pasn_fils_pmksa_cache(dev, apdev, params, "FILS-SHA384") + +@remote_compatible +def test_pasn_sae_kdk(dev, apdev): + """Station authentication with SAE AP with KDK derivation during connection""" + check_pasn_capab(dev[0]) + check_sae_capab(dev[0]) + + try: + params = hostapd.wpa2_params(ssid="test-sae", + passphrase="12345678") + params['wpa_key_mgmt'] = 'SAE PASN' + params['force_kdk_derivation'] = "1" + hapd = start_pasn_ap(apdev[0], params) + + dev[0].set("force_kdk_derivation", "1") + dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", + scan_freq="2412") + + check_pasn_ptk(dev[0], hapd, "CCMP") + finally: + dev[0].set("force_kdk_derivation", "0") + + +def check_pasn_fils_kdk(dev, apdev, params, key_mgmt): + check_fils_capa(dev[0]) + check_erp_capa(dev[0]) + check_pasn_capab(dev[0]) + + start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst")) + + try: + bssid = apdev[0]['bssid'] + params = hostapd.wpa2_eap_params(ssid="fils") + params['wpa_key_mgmt'] = key_mgmt + params['auth_server_port'] = "18128" + params['erp_domain'] = 'example.com' + params['fils_realm'] = 'example.com' + params['disable_pmksa_caching'] = '1' + params['force_kdk_derivation'] = "1" + hapd = start_pasn_ap(apdev[0], params) + + dev[0].scan_for_bss(bssid, freq=2412) + dev[0].request("ERP_FLUSH") + dev[0].set("force_kdk_derivation", "1") + + id = dev[0].connect("fils", key_mgmt=key_mgmt, + eap="PSK", identity="psk.user@example.com", + password_hex="0123456789abcdef0123456789abcdef", + erp="1", scan_freq="2412") + + hapd.wait_sta() + hwsim_utils.test_connectivity(dev[0], hapd) + + check_pasn_ptk(dev[0], hapd, "CCMP") + + dev[0].request("DISCONNECT") + dev[0].wait_disconnected() + + dev[0].dump_monitor() + dev[0].select_network(id, freq=2412) + ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED", + "EVENT-ASSOC-REJECT", + "CTRL-EVENT-CONNECTED"], timeout=10) + if ev is None: + raise Exception("Connection using FILS/ERP timed out") + if "CTRL-EVENT-EAP-STARTED" in ev: + raise Exception("Unexpected EAP exchange") + if "EVENT-ASSOC-REJECT" in ev: + raise Exception("Association failed") + + hapd.wait_sta() + hwsim_utils.test_connectivity(dev[0], hapd) + + check_pasn_ptk(dev[0], hapd, "CCMP") + finally: + dev[0].set("force_kdk_derivation", "0") + +@remote_compatible +def test_pasn_fils_sha256_kdk(dev, apdev, params): + """Station authentication with FILS-SHA256 with KDK derivation during connection""" + check_pasn_fils_kdk(dev, apdev, params, "FILS-SHA256") + +@remote_compatible +def test_pasn_fils_sha384_kdk(dev, apdev, params): + """Station authentication with FILS-SHA384 with KDK derivation during connection""" + check_pasn_fils_kdk(dev, apdev, params, "FILS-SHA384") + +@remote_compatible +def test_pasn_sae(dev, apdev): + """PASN authentication with SAE AP with PMK derivation + PMKSA caching""" + check_pasn_capab(dev[0]) + check_sae_capab(dev[0]) + + params = hostapd.wpa2_params(ssid="test-pasn-sae", + passphrase="12345678") + params['wpa_key_mgmt'] = 'SAE PASN' + hapd = start_pasn_ap(apdev[0], params) + + dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412", + only_add_network=True) + + # first test with a valid PSK + check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP", nid="0") + + # And now with PMKSA caching + check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP") + + # And now with a wrong passphrase + if "FAIL" in dev[0].request("PMKSA_FLUSH"): + raise Exception("PMKSA_FLUSH failed") + + dev[0].set_network_quoted(0, "psk", "12345678787") + check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP", status=1, nid="0") + +@remote_compatible +def test_pasn_sae_while_connected_same_channel(dev, apdev): + """PASN SAE authentication while connected same channel""" + check_pasn_capab(dev[0]) + check_sae_capab(dev[0]) + + params = hostapd.wpa2_params(ssid="test-pasn-wpa2-psk", + passphrase="12345678") + hapd = hostapd.add_ap(apdev[0], params) + + dev[0].connect("test-pasn-wpa2-psk", psk="12345678", scan_freq="2412") + + params = hostapd.wpa2_params(ssid="test-pasn-sae", + passphrase="12345678") + + params['wpa_key_mgmt'] = 'SAE PASN' + hapd = start_pasn_ap(apdev[1], params) + + dev[0].connect("test-pasn-sae", psk="12345678", key_mgmt="SAE", + scan_freq="2412", only_add_network=True) + + check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP", nid="1") + +@remote_compatible +def test_pasn_sae_while_connected_diff_channel(dev, apdev): + """PASN SAE authentication while connected diff channel""" + check_pasn_capab(dev[0]) + check_sae_capab(dev[0]) + + with HWSimRadio(n_channels=2) as (radio, iface): + wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') + wpas.interface_add(iface) + + if wpas.get_mcc() < 2: + raise HwsimSkip("PASN: New radio does not support MCC") + + params = hostapd.wpa2_params(ssid="test-pasn-wpa2-psk", + passphrase="12345678") + params['channel'] = "6" + hapd = hostapd.add_ap(apdev[0], params) + + wpas.connect("test-pasn-wpa2-psk", psk="12345678", scan_freq="2437") + + params = hostapd.wpa2_params(ssid="test-pasn-sae", + passphrase="12345678") + + params['wpa_key_mgmt'] = 'SAE PASN' + hapd = start_pasn_ap(apdev[1], params) + + wpas.connect("test-pasn-sae", psk="12345678", key_mgmt="SAE", + scan_freq="2412", only_add_network=True) + + check_pasn_akmp_cipher(wpas, hapd, "SAE", "CCMP", nid="1") + +def pasn_fils_setup(wpas, apdev, params, key_mgmt): + check_fils_capa(wpas) + check_erp_capa(wpas) + + wpas.flush_scan_cache() + + start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst")) + + bssid = apdev[0]['bssid'] + params = hostapd.wpa2_eap_params(ssid="fils") + params['wpa_key_mgmt'] = key_mgmt + " PASN" + params['auth_server_port'] = "18128" + params['erp_domain'] = 'example.com' + params['fils_realm'] = 'example.com' + params['disable_pmksa_caching'] = '1' + hapd = hostapd.add_ap(apdev[0]['ifname'], params) + + id = wpas.connect("fils", key_mgmt=key_mgmt, + eap="PSK", identity="psk.user@example.com", + password_hex="0123456789abcdef0123456789abcdef", + erp="1", scan_freq="2412") + + wpas.request("DISCONNECT") + wpas.wait_disconnected() + wpas.dump_monitor() + + if "FAIL" in wpas.request("PMKSA_FLUSH"): + raise Exception("PMKSA_FLUSH failed") + + return hapd + +def check_pasn_fils(dev, apdev, params, key_mgmt): + check_pasn_capab(dev[0]) + + hapd = pasn_fils_setup(dev[0], apdev, params, key_mgmt); + check_pasn_akmp_cipher(dev[0], hapd, key_mgmt, "CCMP", nid="0") + +@remote_compatible +def test_pasn_fils_sha256(dev, apdev, params): + """PASN FILS authentication using SHA-256""" + check_pasn_fils(dev, apdev, params, "FILS-SHA256") + +@remote_compatible +def test_pasn_fils_sha384(dev, apdev, params): + """PASN FILS authentication using SHA-384""" + check_pasn_fils(dev, apdev, params, "FILS-SHA384") + +def check_pasn_fils_connected_same_channel(dev, apdev, params, key_mgmt): + check_pasn_capab(dev[0]) + + hapd = pasn_fils_setup(dev[0], apdev, params, key_mgmt); + + # Connect to another AP on the same channel + hapd1 = hostapd.add_ap(apdev[1], {"ssid": "open"}) + dev[0].connect("open", key_mgmt="NONE", scan_freq="2412", + bg_scan_period="0") + + hwsim_utils.test_connectivity(dev[0], hapd1) + + # And perform the PASN authentication with FILS + check_pasn_akmp_cipher(dev[0], hapd, key_mgmt, "CCMP", nid="0") + +@remote_compatible +def test_pasn_fils_sha256_connected_same_channel(dev, apdev, params): + """PASN FILS authentication using SHA-256 while connected same channel""" + check_pasn_fils_connected_same_channel(dev, apdev, params, "FILS-SHA256") + +@remote_compatible +def test_pasn_fils_sha384_connected_same_channel(dev, apdev, params): + """PASN FILS authentication using SHA-384 while connected same channel""" + check_pasn_fils_connected_same_channel(dev, apdev, params, "FILS-SHA384") + +def check_pasn_fils_connected_diff_channel(dev, apdev, params, key_mgmt): + check_pasn_capab(dev[0]) + + with HWSimRadio(n_channels=2) as (radio, iface): + wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') + wpas.interface_add(iface) + if wpas.get_mcc() < 2: + raise Exception("New radio does not support MCC") + + hapd = pasn_fils_setup(wpas, apdev, params, key_mgmt); + + # Connect to another AP on a different channel + hapd1 = hostapd.add_ap(apdev[1], {"ssid": "open", "channel" : "6"}) + wpas.connect("open", key_mgmt="NONE", scan_freq="2437", + bg_scan_period="0") + + hwsim_utils.test_connectivity(wpas, hapd1) + + # And perform the PASN authentication with FILS + check_pasn_akmp_cipher(wpas, hapd, key_mgmt, "CCMP", nid="0") + +@remote_compatible +def test_pasn_fils_sha256_connected_diff_channel(dev, apdev, params): + """PASN FILS authentication using SHA-256 while connected diff channel""" + check_pasn_fils_connected_diff_channel(dev, apdev, params, "FILS-SHA256") + +@remote_compatible +def test_pasn_fils_sha384_connected_diff_channel(dev, apdev, params): + """PASN FILS authentication using SHA-384 while connected diff channel""" + check_pasn_fils_connected_diff_channel(dev, apdev, params, "FILS-SHA384") + +def test_pasn_ft_psk(dev, apdev): + """PASN authentication with FT-PSK""" + check_pasn_capab(dev[0]) + + ssid = "test-pasn-ft-psk" + passphrase = "12345678" + + params = ft_params1(ssid=ssid, passphrase=passphrase) + params['wpa_key_mgmt'] += " PASN" + hapd0 = hostapd.add_ap(apdev[0], params) + params = ft_params2(ssid=ssid, passphrase=passphrase) + params['wpa_key_mgmt'] += " PASN" + hapd1 = hostapd.add_ap(apdev[1], params) + + run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase) + + if dev[0].get_status_field('bssid') == apdev[0]['bssid']: + pasn_hapd = hapd1 + else: + pasn_hapd = hapd0 + + check_pasn_akmp_cipher(dev[0], pasn_hapd, "FT-PSK", "CCMP") + + run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, only_one_way=1) + + if dev[0].get_status_field('bssid') == apdev[0]['bssid']: + pasn_hapd = hapd1 + else: + pasn_hapd = hapd0 + + check_pasn_akmp_cipher(dev[0], pasn_hapd, "FT-PSK", "CCMP") + +def test_pasn_ft_eap(dev, apdev): + """PASN authentication with FT-EAP""" + check_pasn_capab(dev[0]) + + ssid = "test-pasn-ft-psk" + passphrase = "12345678" + identity = "gpsk user" + + radius = hostapd.radius_params() + params = ft_params1(ssid=ssid, passphrase=passphrase) + params['wpa_key_mgmt'] = "FT-EAP PASN" + params["ieee8021x"] = "1" + params = dict(list(radius.items()) + list(params.items())) + hapd0 = hostapd.add_ap(apdev[0], params) + + params = ft_params2(ssid=ssid, passphrase=passphrase) + params['wpa_key_mgmt'] = "FT-EAP PASN" + params["ieee8021x"] = "1" + params = dict(list(radius.items()) + list(params.items())) + hapd1 = hostapd.add_ap(apdev[1], params) + + run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, eap=True, + eap_identity=identity) + + if dev[0].get_status_field('bssid') == apdev[0]['bssid']: + pasn_hapd = hapd1 + else: + pasn_hapd = hapd0 + + check_pasn_akmp_cipher(dev[0], pasn_hapd, "FT-EAP", "CCMP") + +def test_pasn_ft_eap_sha384(dev, apdev): + """PASN authentication with FT-EAP-SHA-384""" + check_pasn_capab(dev[0]) + + ssid = "test-pasn-ft-psk" + passphrase = "12345678" + identity = "gpsk user" + + radius = hostapd.radius_params() + params = ft_params1(ssid=ssid, passphrase=passphrase) + params["ieee80211w"] = "2" + params['wpa_key_mgmt'] = "FT-EAP-SHA384 PASN" + params["ieee8021x"] = "1" + params = dict(list(radius.items()) + list(params.items())) + hapd0 = hostapd.add_ap(apdev[0], params) + + params = ft_params2(ssid=ssid, passphrase=passphrase) + params["ieee80211w"] = "2" + params['wpa_key_mgmt'] = "FT-EAP-SHA384 PASN" + params["ieee8021x"] = "1" + params = dict(list(radius.items()) + list(params.items())) + hapd1 = hostapd.add_ap(apdev[1], params) + + run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, eap=True, + sha384=True) + + if dev[0].get_status_field('bssid') == apdev[0]['bssid']: + pasn_hapd = hapd1 + else: + pasn_hapd = hapd0 + + check_pasn_akmp_cipher(dev[0], pasn_hapd, "FT-EAP-SHA384", "CCMP") diff --git a/tests/hwsim/test_scan.py b/tests/hwsim/test_scan.py index 2e64cee..24a7903 100644 --- a/tests/hwsim/test_scan.py +++ b/tests/hwsim/test_scan.py @@ -654,6 +654,10 @@ def test_scan_reqs_with_non_scan_radio_work(dev, apdev): def test_scan_setband(dev, apdev): """Band selection for scan operations""" + wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') + wpas.interface_add("wlan5") + devs = [ dev[0], dev[1], dev[2], wpas ] + try: hapd = None hapd2 = None @@ -678,21 +682,26 @@ def test_scan_setband(dev, apdev): raise Exception("Failed to set setband") if "OK" not in dev[2].request("SET setband 2G"): raise Exception("Failed to set setband") + if "OK" not in wpas.request("SET setband 2G,5G"): + raise Exception("Failed to set setband") # Allow a retry to avoid reporting errors during heavy load for j in range(5): - for i in range(3): - dev[i].request("SCAN only_new=1") + for d in devs: + d.request("SCAN only_new=1") - for i in range(3): - ev = dev[i].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15) + for d in devs: + ev = d.wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15) if ev is None: raise Exception("Scan timed out") res0 = dev[0].request("SCAN_RESULTS") res1 = dev[1].request("SCAN_RESULTS") res2 = dev[2].request("SCAN_RESULTS") - if bssid in res0 and bssid2 in res0 and bssid in res1 and bssid2 in res2: + res3 = wpas.request("SCAN_RESULTS") + if bssid in res0 and bssid2 in res0 and \ + bssid in res1 and bssid2 in res2 and \ + bssid in res3 and bssid2 in res3: break res = dev[0].request("SCAN_RESULTS") @@ -710,15 +719,19 @@ def test_scan_setband(dev, apdev): raise Exception("Missing scan result(2)") if bssid in res: raise Exception("Unexpected scan result(2)") + + res = wpas.request("SCAN_RESULTS") + if bssid not in res or bssid2 not in res: + raise Exception("Missing scan result(3)") finally: if hapd: hapd.request("DISABLE") if hapd2: hapd2.request("DISABLE") subprocess.call(['iw', 'reg', 'set', '00']) - for i in range(3): - dev[i].request("SET setband AUTO") - dev[i].flush_scan_cache() + for d in devs: + d.request("SET setband AUTO") + d.flush_scan_cache() @remote_compatible def test_scan_hidden_many(dev, apdev): @@ -1137,7 +1150,7 @@ def test_scan_fail(dev, apdev): try: if "OK" not in dev[0].request("SET setband 2G"): raise Exception("SET setband failed") - with alloc_fail(dev[0], 1, "=wpa_setband_scan_freqs_list"): + with alloc_fail(dev[0], 1, "=wpa_add_scan_freqs_list"): # While the frequency list cannot be created due to memory # allocation failure, this scan is expected to be completed without # frequency filtering. @@ -1976,6 +1989,9 @@ def test_scan_ssid_list(dev, apdev): break finally: dev[0].request("VENDOR_ELEM_REMOVE 14 *") + hapd.disable() + dev[0].flush_scan_cache(freq=2432) + dev[0].flush_scan_cache() if not found: raise Exception("AP not found in scan results") @@ -2001,6 +2017,9 @@ def test_scan_short_ssid_list(dev, apdev): break finally: dev[0].request("VENDOR_ELEM_REMOVE 14 *") + hapd.disable() + dev[0].flush_scan_cache(freq=2432) + dev[0].flush_scan_cache() if not found: raise Exception("AP not found in scan results") diff --git a/tests/hwsim/test_sigma_dut.py b/tests/hwsim/test_sigma_dut.py index e2151cf..8bf06e0 100644 --- a/tests/hwsim/test_sigma_dut.py +++ b/tests/hwsim/test_sigma_dut.py @@ -2240,7 +2240,7 @@ def test_sigma_dut_ap_dpp_qr_enrollee_chirp(dev, apdev, params): dev[1].dpp_listen(2437) dev[0].dpp_auth_init(uri=uri, conf="sta-dpp", ssid="DPPNET01", configurator=conf_id) - dev[1].wait_connected() + dev[1].wait_connected(timeout=20) sigma_dut_cmd_check("ap_reset_default,program,DPP") finally: @@ -2334,7 +2334,7 @@ def run_sigma_dut_ap_dpp_qr(dev, apdev, params, ap_conf, sta_conf, extra=""): cmd = "DPP_AUTH_INIT peer=%d conf=%s %s configurator=%d" % (id0b, sta_conf, extra, conf_id) if "OK" not in dev[0].request(cmd): raise Exception("Failed to initiate DPP Authentication") - dev[1].wait_connected() + dev[1].wait_connected(timeout=20) sigma_dut_cmd_check("ap_reset_default") finally: @@ -2389,7 +2389,7 @@ def test_sigma_dut_ap_dpp_offchannel(dev, apdev, params): cmd = "DPP_AUTH_INIT peer=%d conf=sta-dpp ssid=%s configurator=%d" % (id0b, to_hex("DPPNET01"), conf_id) if "OK" not in dev[0].request(cmd): raise Exception("Failed to initiate DPP Authentication") - dev[1].wait_connected() + dev[1].wait_connected(timeout=20) sigma_dut_cmd_check("ap_reset_default") finally: @@ -2914,7 +2914,7 @@ def run_sigma_dut_ap_dpp_self_config(dev, apdev): res = sigma_dut_cmd(cmd) if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK" not in res: raise Exception("Unexpected result: " + res) - dev[0].wait_connected() + dev[0].wait_connected(timeout=20) dev[0].request("DISCONNECT") dev[0].wait_disconnected() sigma_dut_cmd_check("ap_reset_default") @@ -3391,7 +3391,7 @@ def test_sigma_dut_dpp_reconfig_enrollee(dev, apdev): if ev is None: raise Exception("DPP Config Response (reconfig) not transmitted") - dev[0].wait_connected() + dev[0].wait_connected(timeout=20) ev = dev[1].wait_event(["DPP-CONN-STATUS-RESULT"], timeout=20) if ev is None: raise Exception("No connection status reported") @@ -3413,7 +3413,7 @@ def test_sigma_dut_dpp_reconfig_enrollee(dev, apdev): if ev is None: raise Exception("DPP Config Response (reconfig) not transmitted [2]") - dev[0].wait_connected() + dev[0].wait_connected(timeout=20) finally: dev[0].set("dpp_config_processing", "0") stop_sigma_dut(sigma) @@ -5214,11 +5214,12 @@ def test_sigma_dut_client_privacy(dev, apdev, params): sigma_dut_cmd_check("sta_reset_default,interface," + ifname) finally: stop_sigma_dut(sigma) - dev[1].set("mac_addr", "0", allow_fail=True) - dev[1].set("rand_addr_lifetime", "60", allow_fail=True) - dev[1].set("preassoc_mac_addr", "0", allow_fail=True) - dev[1].set("gas_rand_mac_addr", "0", allow_fail=True) - dev[1].set("gas_rand_addr_lifetime", "60", allow_fail=True) + dev[0].set("mac_addr", "0", allow_fail=True) + dev[0].set("rand_addr_lifetime", "60", allow_fail=True) + dev[0].request("MAC_RAND_SCAN enable=0 all") + dev[0].set("preassoc_mac_addr", "0", allow_fail=True) + dev[0].set("gas_rand_mac_addr", "0", allow_fail=True) + dev[0].set("gas_rand_addr_lifetime", "60", allow_fail=True) out = run_tshark(os.path.join(logdir, "hwsim0.pcapng"), "wlan.addr == " + addr, diff --git a/tests/hwsim/test_wpas_ctrl.py b/tests/hwsim/test_wpas_ctrl.py index 04418f0..bb8ca1a 100644 --- a/tests/hwsim/test_wpas_ctrl.py +++ b/tests/hwsim/test_wpas_ctrl.py @@ -266,14 +266,14 @@ def test_wpas_ctrl_network(dev): "f2:99:88:77:66:55 02:11:22:33:44:55/ff:00:ff:00:ff:00 12:34:56:78:90:ab", "02:11:22:33:44:55/ff:ff:ff:00:00:00 02:ae:be:ce:53:77/00:00:00:00:00:ff"] for val in tests: - dev[0].set_network(id, "bssid_blacklist", val) - res = dev[0].get_network(id, "bssid_blacklist") + dev[0].set_network(id, "bssid_ignore", val) + res = dev[0].get_network(id, "bssid_ignore") if res != val: - raise Exception("Unexpected bssid_blacklist value: %s != %s" % (res, val)) - dev[0].set_network(id, "bssid_whitelist", val) - res = dev[0].get_network(id, "bssid_whitelist") + raise Exception("Unexpected bssid_ignore value: %s != %s" % (res, val)) + dev[0].set_network(id, "bssid_accept", val) + res = dev[0].get_network(id, "bssid_accept") if res != val: - raise Exception("Unexpected bssid_whitelist value: %s != %s" % (res, val)) + raise Exception("Unexpected bssid_accept value: %s != %s" % (res, val)) tests = ["foo", "00:11:22:33:44:5", @@ -281,8 +281,8 @@ def test_wpas_ctrl_network(dev): "00:11:22:33:44:55/", "00:11:22:33:44:55/66:77:88:99:aa:b"] for val in tests: - if "FAIL" not in dev[0].request("SET_NETWORK %d bssid_blacklist %s" % (id, val)): - raise Exception("Invalid bssid_blacklist value accepted") + if "FAIL" not in dev[0].request("SET_NETWORK %d bssid_ignore %s" % (id, val)): + raise Exception("Invalid bssid_ignore value accepted") @remote_compatible def test_wpas_ctrl_network_oom(dev): @@ -687,8 +687,8 @@ def test_wpas_ctrl_addr(dev): raise Exception("Unexpected success on invalid WPS_REG") if "FAIL" not in dev[0].request("IBSS_RSN 00:11:22:33:44"): raise Exception("Unexpected success on invalid IBSS_RSN") - if "FAIL" not in dev[0].request("BLACKLIST 00:11:22:33:44"): - raise Exception("Unexpected success on invalid BLACKLIST") + if "FAIL" not in dev[0].request("BSSID_IGNORE 00:11:22:33:44"): + raise Exception("Unexpected success on invalid BSSID_IGNORE") @remote_compatible def test_wpas_ctrl_wps_errors(dev): @@ -1078,43 +1078,43 @@ def test_wpas_ctrl_nfc_get_handover(dev): if "FAIL" in dev[0].request("NFC_GET_HANDOVER_SEL " + v): raise Exception("Unexpected NFC_GET_HANDOVER_SEL failure for " + v) -def get_blacklist(dev): - return dev.request("BLACKLIST").splitlines() +def get_bssid_ignore_list(dev): + return dev.request("BSSID_IGNORE").splitlines() @remote_compatible -def test_wpas_ctrl_blacklist(dev): - """wpa_supplicant ctrl_iface BLACKLIST""" - if "OK" not in dev[0].request("BLACKLIST clear"): - raise Exception("BLACKLIST clear failed") - b = get_blacklist(dev[0]) +def test_wpas_ctrl_bssid_ignore(dev): + """wpa_supplicant ctrl_iface BSSID_IGNORE""" + if "OK" not in dev[0].request("BSSID_IGNORE clear"): + raise Exception("BSSID_IGNORE clear failed") + b = get_bssid_ignore_list(dev[0]) if len(b) != 0: - raise Exception("Unexpected blacklist contents: " + str(b)) - if "OK" not in dev[0].request("BLACKLIST 00:11:22:33:44:55"): - raise Exception("BLACKLIST add failed") - b = get_blacklist(dev[0]) + raise Exception("Unexpected BSSID ignore list contents: " + str(b)) + if "OK" not in dev[0].request("BSSID_IGNORE 00:11:22:33:44:55"): + raise Exception("BSSID_IGNORE add failed") + b = get_bssid_ignore_list(dev[0]) if "00:11:22:33:44:55" not in b: - raise Exception("Unexpected blacklist contents: " + str(b)) - if "OK" not in dev[0].request("BLACKLIST 00:11:22:33:44:56"): - raise Exception("BLACKLIST add failed") - b = get_blacklist(dev[0]) + raise Exception("Unexpected BSSID ignore list contents: " + str(b)) + if "OK" not in dev[0].request("BSSID_IGNORE 00:11:22:33:44:56"): + raise Exception("BSSID_IGNORE add failed") + b = get_bssid_ignore_list(dev[0]) if "00:11:22:33:44:55" not in b or "00:11:22:33:44:56" not in b: - raise Exception("Unexpected blacklist contents: " + str(b)) - if "OK" not in dev[0].request("BLACKLIST 00:11:22:33:44:56"): - raise Exception("BLACKLIST add failed") - b = get_blacklist(dev[0]) + raise Exception("Unexpected BSSID ignore list contents: " + str(b)) + if "OK" not in dev[0].request("BSSID_IGNORE 00:11:22:33:44:56"): + raise Exception("BSSID_IGNORE add failed") + b = get_bssid_ignore_list(dev[0]) if "00:11:22:33:44:55" not in b or "00:11:22:33:44:56" not in b or len(b) != 2: - raise Exception("Unexpected blacklist contents: " + str(b)) + raise Exception("Unexpected BSSID ignore list contents: " + str(b)) - if "OK" not in dev[0].request("BLACKLIST clear"): - raise Exception("BLACKLIST clear failed") - if dev[0].request("BLACKLIST") != "": - raise Exception("Unexpected blacklist contents") + if "OK" not in dev[0].request("BSSID_IGNORE clear"): + raise Exception("BSSID_IGNORE clear failed") + if dev[0].request("BSSID_IGNORE") != "": + raise Exception("Unexpected BSSID ignore list contents") @remote_compatible -def test_wpas_ctrl_blacklist_oom(dev): - """wpa_supplicant ctrl_iface BLACKLIST and out-of-memory""" - with alloc_fail(dev[0], 1, "wpa_blacklist_add"): - if "FAIL" not in dev[0].request("BLACKLIST aa:bb:cc:dd:ee:ff"): +def test_wpas_ctrl_bssid_ignore_oom(dev): + """wpa_supplicant ctrl_iface BSSID_IGNORE and out-of-memory""" + with alloc_fail(dev[0], 1, "wpa_bssid_ignore_add"): + if "FAIL" not in dev[0].request("BSSID_IGNORE aa:bb:cc:dd:ee:ff"): raise Exception("Unexpected success with allocation failure") def test_wpas_ctrl_log_level(dev): diff --git a/tests/hwsim/test_wpas_mesh.py b/tests/hwsim/test_wpas_mesh.py index b7f9846..75bc021 100644 --- a/tests/hwsim/test_wpas_mesh.py +++ b/tests/hwsim/test_wpas_mesh.py @@ -80,8 +80,24 @@ def check_mesh_scan(dev, params, other_started=False, beacon_int=0): if '[MESH]' not in bss['flags']: raise Exception("BSS output did not include MESH flag") -def check_mesh_group_added(dev): - ev = dev.wait_event(["MESH-GROUP-STARTED"]) +def check_dfs_started(dev, timeout=10): + ev = dev.wait_event(["DFS-CAC-START"], timeout=timeout) + if ev is None: + raise Exception("Test exception: CAC did not start") + +def check_dfs_finished(dev, timeout=70): + ev = dev.wait_event(["DFS-CAC-COMPLETED"], timeout=timeout) + if ev is None: + raise Exception("Test exception: CAC did not finish") + +def check_mesh_radar_handling_finished(dev, timeout=75): + ev = dev.wait_event(["CTRL-EVENT-CHANNEL-SWITCH", "MESH-GROUP-STARTED"], + timeout=timeout) + if ev is None: + raise Exception("Test exception: Couldn't join mesh") + +def check_mesh_group_added(dev, timeout=10): + ev = dev.wait_event(["MESH-GROUP-STARTED"], timeout=timeout) if ev is None: raise Exception("Test exception: Couldn't join mesh") @@ -91,6 +107,10 @@ def check_mesh_group_removed(dev): if ev is None: raise Exception("Test exception: Couldn't leave mesh") +def check_regdom_change(dev, timeout=10): + ev = dev.wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=timeout) + if ev is None: + raise Exception("Test exception: No regdom change happened.") def check_mesh_peer_connected(dev, timeout=10): ev = dev.wait_event(["MESH-PEER-CONNECTED"], timeout=timeout) @@ -167,6 +187,39 @@ def test_wpas_mesh_group_remove(dev): check_mesh_group_removed(dev[0]) dev[0].mesh_group_remove() +def dfs_simulate_radar(dev): + logger.info("Trigger a simulated radar event") + phyname = dev.get_driver_status_field("phyname") + radar_file = '/sys/kernel/debug/ieee80211/' + phyname + '/hwsim/dfs_simulate_radar' + with open(radar_file, 'w') as f: + f.write('1') + +@long_duration_test +def test_mesh_peer_connected_dfs(dev): + """Mesh peer connected (DFS)""" + dev[0].set("country", "DE") + dev[1].set("country", "DE") + + check_regdom_change(dev[0]) + check_regdom_change(dev[1]) + + check_mesh_support(dev[0]) + add_open_mesh_network(dev[0], freq="5500", beacon_int=160) + add_open_mesh_network(dev[1], freq="5500", beacon_int=160) + check_dfs_started(dev[0]) + check_dfs_finished(dev[0]) + check_mesh_joined_connected(dev, timeout0=10) + + dfs_simulate_radar(dev[0]) + + check_mesh_radar_handling_finished(dev[0], timeout=75) + + dev[0].set("country", "00") + dev[1].set("country", "00") + + check_regdom_change(dev[0]) + check_regdom_change(dev[1]) + def test_wpas_mesh_peer_connected(dev): """wpa_supplicant MESH peer connected""" check_mesh_support(dev[0]) diff --git a/tests/hwsim/tshark.py b/tests/hwsim/tshark.py index d6a57f0..32cdf47 100644 --- a/tests/hwsim/tshark.py +++ b/tests/hwsim/tshark.py @@ -12,6 +12,8 @@ import subprocess import logging logger = logging.getLogger() +from utils import * + class UnknownFieldsException(Exception): def __init__(self, fields): Exception.__init__(self, "unknown tshark fields %s" % ','.join(fields)) @@ -41,6 +43,8 @@ def _run_tshark(filename, filter, display=None, wait=True): stderr=subprocess.PIPE) except Exception as e: logger.info("Could run run tshark check: " + str(e)) + if "No such file or directory: 'tshark'" in str(e): + raise HwsimSkip("No tshark available") cmd = None return None @@ -111,6 +115,8 @@ def run_tshark_json(filename, filter): stderr=subprocess.PIPE) except Exception as e: logger.info("Could run run tshark: " + str(e)) + if "No such file or directory: 'tshark'" in str(e): + raise HwsimSkip("No tshark available") return None output = cmd.communicate() out = output[0].decode() diff --git a/tests/hwsim/wlantest.py b/tests/hwsim/wlantest.py index 6d4343b..16765d2 100644 --- a/tests/hwsim/wlantest.py +++ b/tests/hwsim/wlantest.py @@ -29,7 +29,7 @@ class Wlantest: return cls.remote_host.execute(["killall", "-9", "wlantest"]) - cls.remote_host.wait_execute_complete(cls.exe_thread, 5) + cls.remote_host.thread_wait(cls.exe_thread, 5) cls.exe_thread = None cls.exe_res = [] @@ -64,7 +64,7 @@ class Wlantest: pcap_file, log_file) cls.remote_host.add_log(log_file) cls.remote_host.add_log(pcap_file) - cls.exe_thread = cls.remote_host.execute_run(cmd.split(), cls.exe_res) + cls.exe_thread = cls.remote_host.thread_run(cmd.split(), cls.exe_res) # Give wlantest a chance to start working time.sleep(1) diff --git a/tests/hwsim/wpasupplicant.py b/tests/hwsim/wpasupplicant.py index 92c8552..fed1d10 100644 --- a/tests/hwsim/wpasupplicant.py +++ b/tests/hwsim/wpasupplicant.py @@ -1095,8 +1095,8 @@ class WpaSupplicant: "disable_max_amsdu", "ampdu_factor", "ampdu_density", "disable_ht40", "disable_sgi", "disable_ldpc", "ht40_intolerant", "update_identifier", "mac_addr", - "erp", "bg_scan_period", "bssid_blacklist", - "bssid_whitelist", "mem_only_psk", "eap_workaround", + "erp", "bg_scan_period", "bssid_ignore", + "bssid_accept", "mem_only_psk", "eap_workaround", "engine", "fils_dh_group", "bssid_hint", "dpp_csign", "dpp_csign_expiry", "dpp_netaccesskey", "dpp_netaccesskey_expiry", "dpp_pfs", @@ -1628,3 +1628,21 @@ class WpaSupplicant: res = self.request("DPP_CONFIGURATOR_REMOVE %d" % conf_id) if "OK" not in res: raise Exception("DPP_CONFIGURATOR_REMOVE failed") + + def get_ptksa(self, bssid, cipher): + res = self.request("PTKSA_CACHE_LIST") + lines = res.splitlines() + for l in lines: + if bssid not in l or cipher not in l: + continue + + vals = dict() + [index, addr, cipher, expiration, tk, kdk] = l.split(' ', 5) + vals['index'] = index + vals['addr'] = addr + vals['cipher'] = cipher + vals['expiration'] = expiration + vals['tk'] = tk + vals['kdk'] = kdk + return vals + return None diff --git a/tests/remote/monitor.py b/tests/remote/monitor.py index 5bd801c..0f77d50 100644 --- a/tests/remote/monitor.py +++ b/tests/remote/monitor.py @@ -110,7 +110,7 @@ def run(host, setup_params): log = log_dir + tc_name + "_" + host.name + log_monitor + ".pcap" host.add_log(log) - thread = host.execute_run([tshark, "-w", log], monitor_res) + thread = host.thread_run([tshark, "-w", log], monitor_res) host.thread = thread @@ -122,7 +122,7 @@ def stop(host): if host.thread is None: return - host.execute_stop(host.thread) + host.thread_stop(host.thread) host.thread = None # Add monitor to existing interface diff --git a/tests/remote/run-tests.py b/tests/remote/run-tests.py index e26e348..67993a3 100755 --- a/tests/remote/run-tests.py +++ b/tests/remote/run-tests.py @@ -13,6 +13,7 @@ import time import traceback import getopt from datetime import datetime +from random import shuffle import logging logger = logging.getLogger() @@ -32,7 +33,7 @@ from hwsim_wrapper import run_hwsim_test def usage(): print("USAGE: " + sys.argv[0] + " -t devices") print("USAGE: " + sys.argv[0] + " -t check_devices") - print("USAGE: " + sys.argv[0] + " -d <dut_name> -t <all|sanity|tests_to_run> [-r <ref_name>] [-c <cfg_file.py>] [-m <all|monitor_name>] [-h hwsim_tests] [-f hwsim_modules][-R][-T][-P][-v]") + print("USAGE: " + sys.argv[0] + " -d <dut_name> -t <all|sanity|tests_to_run> [-r <ref_name>] [-c <cfg_file.py>] [-m <all|monitor_name>] [-h hwsim_tests] [-f hwsim_modules][-R][-T][-P][-S][-v]") print("USAGE: " + sys.argv[0]) def get_devices(devices, duts, refs, monitors): @@ -79,10 +80,11 @@ def main(): trace = False restart = False perf = False + shuffle_tests = False # parse input parameters try: - opts, args = getopt.getopt(sys.argv[1:], "d:f:r:t:l:k:c:m:h:vRPT", + opts, args = getopt.getopt(sys.argv[1:], "d:f:r:t:l:k:c:m:h:vRPTS", ["dut=", "modules=", "ref=", "tests=", "log-dir=", "cfg=", "key=", "monitor=", "hwsim="]) @@ -100,6 +102,8 @@ def main(): trace = True elif option == "-P": perf = True + elif option == "-S": + shuffle_tests = True elif option in ("-d", "--dut"): duts.append(argument) elif option in ("-r", "--ref"): @@ -283,6 +287,10 @@ def main(): continue tests_to_run.append(t) + if shuffle_tests: + shuffle(tests_to_run) + shuffle(hwsim_tests_to_run) + # lock devices try: get_devices(devices, duts, refs, monitors) diff --git a/tests/remote/rutils.py b/tests/remote/rutils.py index e80901b..6902991 100644 --- a/tests/remote/rutils.py +++ b/tests/remote/rutils.py @@ -342,12 +342,12 @@ def ping_run(host, ip, result, ifname=None, addr_type="ipv4", deadline="5", qos= flush_arp_cache(host) - thread = host.execute_run(ping, result) + thread = host.thread_run(ping, result) return thread def ping_wait(host, thread, timeout=None): - host.wait_execute_complete(thread, timeout) - if thread.isAlive(): + host.thread_wait(thread, timeout) + if thread.is_alive(): raise Exception("ping thread still alive") def flush_arp_cache(host): @@ -496,24 +496,24 @@ def iperf_run(server, client, server_ip, client_res, server_res, flush_arp_cache(server) flush_arp_cache(client) - server_thread = server.execute_run(iperf_server, server_res) + server_thread = server.thread_run(iperf_server, server_res) time.sleep(1) - client_thread = client.execute_run(iperf_client, client_res) + client_thread = client.thread_run(iperf_client, client_res) return server_thread, client_thread def iperf_wait(server, client, server_thread, client_thread, timeout=None, iperf="iperf"): - client.wait_execute_complete(client_thread, timeout) - if client_thread.isAlive(): + client.thread_wait(client_thread, timeout) + if client_thread.is_alive(): raise Exception("iperf client thread still alive") - server.wait_execute_complete(server_thread, 5) - if server_thread.isAlive(): + server.thread_wait(server_thread, 5) + if server_thread.is_alive(): server.execute(["killall", "-s", "INT", iperf]) time.sleep(1) - server.wait_execute_complete(server_thread, 5) - if server_thread.isAlive(): + server.thread_wait(server_thread, 5) + if server_thread.is_alive(): raise Exception("iperf server thread still alive") return diff --git a/wlantest/Makefile b/wlantest/Makefile index 6023751..0045020 100644 --- a/wlantest/Makefile +++ b/wlantest/Makefile @@ -25,6 +25,7 @@ CFLAGS += -DCONFIG_SAE CFLAGS += -DCONFIG_OWE CFLAGS += -DCONFIG_DPP CFLAGS += -DCONFIG_SHA384 +CFLAGS += -DCONFIG_PASN OBJS += ../src/common/ieee802_11_common.o OBJS += ../src/common/wpa_common.o diff --git a/wlantest/bss.c b/wlantest/bss.c index 3208e65..0e5b603 100644 --- a/wlantest/bss.c +++ b/wlantest/bss.c @@ -178,14 +178,18 @@ void bss_update(struct wlantest *wt, struct wlantest_bss *bss, elems->osen_len + 2); } - if (elems->rsn_ie == NULL) { + /* S1G does not include RSNE in beacon, so only clear it from + * Probe Response frames. Note this assumes short beacons were dropped + * due to missing SSID above. + */ + if (!elems->rsn_ie && (!elems->s1g_capab || beacon != 1)) { if (bss->rsnie[0]) { add_note(wt, MSG_INFO, "BSS " MACSTR " - RSN IE removed", MAC2STR(bss->bssid)); bss->rsnie[0] = 0; update = 1; } - } else { + } else if (elems->rsn_ie) { if (bss->rsnie[0] == 0 || os_memcmp(bss->rsnie, elems->rsn_ie - 2, elems->rsn_ie_len + 2) != 0) { @@ -289,8 +293,8 @@ void bss_update(struct wlantest *wt, struct wlantest_bss *bss, "pairwise=%s%s%s%s%s%s%s" "group=%s%s%s%s%s%s%s%s%s" "mgmt_group_cipher=%s%s%s%s%s" - "key_mgmt=%s%s%s%s%s%s%s%s%s" - "rsn_capab=%s%s%s%s%s%s%s", + "key_mgmt=%s%s%s%s%s%s%s%s%s%s%s%s%s%s" + "rsn_capab=%s%s%s%s%s%s%s%s%s%s", MAC2STR(bss->bssid), bss->proto == 0 ? "OPEN " : "", bss->proto & WPA_PROTO_WPA ? "WPA " : "", @@ -333,7 +337,14 @@ void bss_update(struct wlantest *wt, struct wlantest_bss *bss, "EAP-SHA256 " : "", bss->key_mgmt & WPA_KEY_MGMT_PSK_SHA256 ? "PSK-SHA256 " : "", + bss->key_mgmt & WPA_KEY_MGMT_OWE ? "OWE " : "", + bss->key_mgmt & WPA_KEY_MGMT_PASN ? "PASN " : "", bss->key_mgmt & WPA_KEY_MGMT_OSEN ? "OSEN " : "", + bss->key_mgmt & WPA_KEY_MGMT_DPP ? "DPP " : "", + bss->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B ? + "EAP-SUITE-B " : "", + bss->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 ? + "EAP-SUITE-B-192 " : "", bss->rsn_capab & WPA_CAPABILITY_PREAUTH ? "PREAUTH " : "", bss->rsn_capab & WPA_CAPABILITY_NO_PAIRWISE ? "NO_PAIRWISE " : "", @@ -341,6 +352,11 @@ void bss_update(struct wlantest *wt, struct wlantest_bss *bss, bss->rsn_capab & WPA_CAPABILITY_MFPC ? "MFPC " : "", bss->rsn_capab & WPA_CAPABILITY_PEERKEY_ENABLED ? "PEERKEY " : "", + bss->rsn_capab & WPA_CAPABILITY_SPP_A_MSDU_CAPABLE ? + "SPP-A-MSDU-CAPAB " : "", + bss->rsn_capab & WPA_CAPABILITY_SPP_A_MSDU_REQUIRED ? + "SPP-A-MSDU-REQUIRED " : "", + bss->rsn_capab & WPA_CAPABILITY_PBAC ? "PBAC " : "", bss->rsn_capab & WPA_CAPABILITY_OCVC ? "OCVC " : "", bss->rsn_capab & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST ? "ExtKeyID " : ""); diff --git a/wlantest/inject.c b/wlantest/inject.c index 54a0554..399f1a3 100644 --- a/wlantest/inject.c +++ b/wlantest/inject.c @@ -68,7 +68,8 @@ static int is_robust_mgmt(u8 *frame, size_t len) stype = WLAN_FC_GET_STYPE(fc); if (stype == WLAN_FC_STYPE_DEAUTH || stype == WLAN_FC_STYPE_DISASSOC) return 1; - if (stype == WLAN_FC_STYPE_ACTION) { + if (stype == WLAN_FC_STYPE_ACTION || + stype == WLAN_FC_STYPE_ACTION_NO_ACK) { if (len < 25) return 0; if (mgmt->u.action.category != WLAN_ACTION_PUBLIC) diff --git a/wlantest/rx_data.c b/wlantest/rx_data.c index b632013..aedf9e8 100644 --- a/wlantest/rx_data.c +++ b/wlantest/rx_data.c @@ -119,19 +119,6 @@ static void rx_data_process(struct wlantest *wt, const u8 *bssid, } -static void write_decrypted_note(struct wlantest *wt, const u8 *decrypted, - const u8 *tk, size_t tk_len, int keyid) -{ - char tk_hex[65]; - - if (!decrypted) - return; - - wpa_snprintf_hex(tk_hex, sizeof(tk_hex), tk, tk_len); - add_note(wt, MSG_EXCESSIVE, "TK[%d] %s", keyid, tk_hex); -} - - static u8 * try_ptk(int pairwise_cipher, struct wpa_ptk *ptk, const struct ieee80211_hdr *hdr, const u8 *data, size_t data_len, size_t *decrypted_len) diff --git a/wlantest/rx_eapol.c b/wlantest/rx_eapol.c index 1e5d667..eaf97c3 100644 --- a/wlantest/rx_eapol.c +++ b/wlantest/rx_eapol.c @@ -120,7 +120,7 @@ static int try_pmk(struct wlantest *wt, struct wlantest_bss *bss, sta->snonce, sta->anonce, sta->addr, bss->bssid, sta->pmk_r1_name, &ptk, ptk_name, sta->key_mgmt, - sta->pairwise_cipher) < 0 || + sta->pairwise_cipher, 0) < 0 || check_mic(ptk.kck, ptk.kck_len, sta->key_mgmt, ver, data, len) < 0) return -1; @@ -128,7 +128,7 @@ static int try_pmk(struct wlantest *wt, struct wlantest_bss *bss, "Pairwise key expansion", bss->bssid, sta->addr, sta->anonce, sta->snonce, &ptk, sta->key_mgmt, - sta->pairwise_cipher, NULL, 0) < 0 || + sta->pairwise_cipher, NULL, 0, 0) < 0 || check_mic(ptk.kck, ptk.kck_len, sta->key_mgmt, ver, data, len) < 0) { return -1; diff --git a/wlantest/rx_ip.c b/wlantest/rx_ip.c index fdf80b7..b0fdd20 100644 --- a/wlantest/rx_ip.c +++ b/wlantest/rx_ip.c @@ -120,63 +120,64 @@ void rx_data_ip(struct wlantest *wt, const u8 *bssid, const u8 *sta_addr, const u8 *dst, const u8 *src, const u8 *data, size_t len, const u8 *peer_addr) { - const struct ip *ip; + struct ip ip; const u8 *payload; size_t plen; uint16_t frag_off, ip_len; - ip = (const struct ip *) data; - if (len < sizeof(*ip)) + if (len < sizeof(ip)) return; - if (ip->ip_v != 4) { + os_memcpy(&ip, data, sizeof(ip)); + + if (ip.ip_v != 4) { if (hwsim_test_packet(data, len)) { add_note(wt, MSG_INFO, "hwsim_test package"); return; } add_note(wt, MSG_DEBUG, "Unexpected IP protocol version %u in " "IPv4 packet (bssid=" MACSTR " str=" MACSTR - " dst=" MACSTR ")", ip->ip_v, MAC2STR(bssid), + " dst=" MACSTR ")", ip.ip_v, MAC2STR(bssid), MAC2STR(src), MAC2STR(dst)); return; } - if (ip->ip_hl * 4 < sizeof(*ip)) { + if (ip.ip_hl * 4 < sizeof(ip)) { add_note(wt, MSG_DEBUG, "Unexpected IP header length %u in " "IPv4 packet (bssid=" MACSTR " str=" MACSTR - " dst=" MACSTR ")", ip->ip_hl, MAC2STR(bssid), + " dst=" MACSTR ")", ip.ip_hl, MAC2STR(bssid), MAC2STR(src), MAC2STR(dst)); return; } - if (ip->ip_hl * 4 > len) { + if (ip.ip_hl * 4 > len) { add_note(wt, MSG_DEBUG, "Truncated IP header (ihl=%u len=%u) " "in IPv4 packet (bssid=" MACSTR " str=" MACSTR - " dst=" MACSTR ")", ip->ip_hl, (unsigned) len, + " dst=" MACSTR ")", ip.ip_hl, (unsigned) len, MAC2STR(bssid), MAC2STR(src), MAC2STR(dst)); return; } - /* TODO: check header checksum in ip->ip_sum */ + /* TODO: check header checksum in ip.ip_sum */ - frag_off = be_to_host16(ip->ip_off); + frag_off = be_to_host16(ip.ip_off); if (frag_off & 0x1fff) { wpa_printf(MSG_EXCESSIVE, "IP fragment reassembly not yet " "supported"); return; } - ip_len = be_to_host16(ip->ip_len); + ip_len = be_to_host16(ip.ip_len); if (ip_len > len) return; if (ip_len < len) len = ip_len; - payload = data + 4 * ip->ip_hl; - plen = len - 4 * ip->ip_hl; + payload = data + 4 * ip.ip_hl; + plen = len - 4 * ip.ip_hl; - switch (ip->ip_p) { + switch (ip.ip_p) { #ifndef __APPLE__ case IPPROTO_ICMP: - rx_data_icmp(wt, bssid, sta_addr, ip->ip_dst.s_addr, - ip->ip_src.s_addr, payload, plen, peer_addr); + rx_data_icmp(wt, bssid, sta_addr, ip.ip_dst.s_addr, + ip.ip_src.s_addr, payload, plen, peer_addr); break; #endif /* __APPLE__ */ } diff --git a/wlantest/rx_mgmt.c b/wlantest/rx_mgmt.c index 0bc7eb2..f7690e0 100644 --- a/wlantest/rx_mgmt.c +++ b/wlantest/rx_mgmt.c @@ -51,6 +51,8 @@ static const char * mgmt_stype(u16 stype) return "DEAUTH"; case WLAN_FC_STYPE_ACTION: return "ACTION"; + case WLAN_FC_STYPE_ACTION_NO_ACK: + return "ACTION-NO-ACK"; } return "??"; } @@ -290,7 +292,7 @@ static void process_ft_auth(struct wlantest *wt, struct wlantest_bss *bss, wpa_pmk_r1_to_ptk(sta->pmk_r1, sta->pmk_r1_len, parse.fte_snonce, parse.fte_anonce, sta->addr, bss->bssid, sta->pmk_r1_name, &ptk, ptk_name, sta->key_mgmt, - sta->pairwise_cipher) < 0) + sta->pairwise_cipher, 0) < 0) return; add_note(wt, MSG_DEBUG, "Derived new PTK"); @@ -494,7 +496,7 @@ static int try_rmsk(struct wlantest *wt, struct wlantest_bss *bss, sta->snonce, sta->anonce, NULL, 0, &ptk, ick, &ick_len, sta->key_mgmt, sta->pairwise_cipher, - NULL, NULL) < 0) + NULL, NULL, 0) < 0) return -1; /* Check AES-SIV decryption with the derived key */ @@ -1779,7 +1781,8 @@ static void rx_mgmt_action_ft_response(struct wlantest *wt, wpa_pmk_r1_to_ptk(sta->pmk_r1, sta->pmk_r1_len, parse.fte_snonce, parse.fte_anonce, new_sta->addr, bss->bssid, sta->pmk_r1_name, &ptk, ptk_name, - new_sta->key_mgmt, new_sta->pairwise_cipher) < 0) + new_sta->key_mgmt, new_sta->pairwise_cipher, + 0) < 0) return; add_note(wt, MSG_DEBUG, "Derived new PTK"); @@ -1913,8 +1916,126 @@ static void rx_mgmt_action_sa_query(struct wlantest *wt, } +static void +rx_mgmt_location_measurement_report(struct wlantest *wt, + const struct ieee80211_mgmt *mgmt, + size_t len, bool no_ack) +{ + const u8 *pos = mgmt->u.action.u.public_action.variable; + const u8 *end = ((const u8 *) mgmt) + len; + + if (end - pos < 1) { + add_note(wt, MSG_INFO, + "Too short Location Measurement Report frame from " + MACSTR, MAC2STR(mgmt->sa)); + return; + } + + wpa_printf(MSG_DEBUG, "Location Measurement Report " MACSTR " --> " + MACSTR " (dialog token %u)", + MAC2STR(mgmt->sa), MAC2STR(mgmt->da), *pos); + pos++; + + if (!no_ack) + add_note(wt, MSG_INFO, + "Protected Fine Timing Measurement Report incorrectly as an Action frame from " + MACSTR, MAC2STR(mgmt->sa)); + + wpa_hexdump(MSG_MSGDUMP, "Location Measurement Report contents", + pos, end - pos); +} + + +static void rx_mgmt_action_no_bss_public(struct wlantest *wt, + const struct ieee80211_mgmt *mgmt, + size_t len, bool no_ack) +{ + switch (mgmt->u.action.u.public_action.action) { + case WLAN_PA_LOCATION_MEASUREMENT_REPORT: + rx_mgmt_location_measurement_report(wt, mgmt, len, no_ack); + break; + } +} + + +static void rx_mgmt_prot_ftm_request(struct wlantest *wt, + const struct ieee80211_mgmt *mgmt, + size_t len, bool no_ack) +{ + wpa_printf(MSG_DEBUG, "Protected Fine Timing Measurement Request " + MACSTR " --> " MACSTR, + MAC2STR(mgmt->sa), MAC2STR(mgmt->da)); + if (no_ack) + add_note(wt, MSG_INFO, + "Protected Fine Timing Measurement Request incorrectly as an Action No Ack frame from " + MACSTR, MAC2STR(mgmt->sa)); +} + + +static void rx_mgmt_prot_ftm(struct wlantest *wt, + const struct ieee80211_mgmt *mgmt, + size_t len, bool no_ack) +{ + wpa_printf(MSG_DEBUG, "Protected Fine Timing Measurement " + MACSTR " --> " MACSTR, + MAC2STR(mgmt->sa), MAC2STR(mgmt->da)); + if (no_ack) + add_note(wt, MSG_INFO, + "Protected Fine Timing Measurement incorrectly as an Action No Ack frame from " + MACSTR, MAC2STR(mgmt->sa)); +} + + +static void rx_mgmt_prot_ftm_report(struct wlantest *wt, + const struct ieee80211_mgmt *mgmt, + size_t len, bool no_ack) +{ + wpa_printf(MSG_DEBUG, "Protected Fine Timing Measurement Report " + MACSTR " --> " MACSTR, + MAC2STR(mgmt->sa), MAC2STR(mgmt->da)); + if (!no_ack) + add_note(wt, MSG_INFO, + "Protected Fine Timing Measurement Report incorrectly as an Action frame from " + MACSTR, MAC2STR(mgmt->sa)); +} + + +static void +rx_mgmt_action_no_bss_protected_ftm(struct wlantest *wt, + const struct ieee80211_mgmt *mgmt, + size_t len, bool no_ack) +{ + switch (mgmt->u.action.u.public_action.action) { + case WLAN_PROT_FTM_REQUEST: + rx_mgmt_prot_ftm_request(wt, mgmt, len, no_ack); + break; + case WLAN_PROT_FTM: + rx_mgmt_prot_ftm(wt, mgmt, len, no_ack); + break; + case WLAN_PROT_FTM_REPORT: + rx_mgmt_prot_ftm_report(wt, mgmt, len, no_ack); + break; + } +} + + +static void rx_mgmt_action_no_bss(struct wlantest *wt, + const struct ieee80211_mgmt *mgmt, size_t len, + bool no_ack) +{ + switch (mgmt->u.action.category) { + case WLAN_ACTION_PUBLIC: + rx_mgmt_action_no_bss_public(wt, mgmt, len, no_ack); + break; + case WLAN_ACTION_PROTECTED_FTM: + rx_mgmt_action_no_bss_protected_ftm(wt, mgmt, len, no_ack); + break; + } +} + + static void rx_mgmt_action(struct wlantest *wt, const u8 *data, size_t len, - int valid) + int valid, bool no_ack) { const struct ieee80211_mgmt *mgmt; struct wlantest_bss *bss; @@ -1929,6 +2050,24 @@ static void rx_mgmt_action(struct wlantest *wt, const u8 *data, size_t len, MAC2STR(mgmt->bssid), mgmt->u.action.category); return; /* Ignore group addressed Action frames for now */ } + + if (len < 24 + 2) { + add_note(wt, MSG_INFO, "Too short Action frame from " MACSTR, + MAC2STR(mgmt->sa)); + return; + } + + wpa_printf(MSG_DEBUG, "ACTION%s " MACSTR " -> " MACSTR + " BSSID=" MACSTR " (category=%u) (valid=%d)", + no_ack ? "-NO-ACK" : "", + MAC2STR(mgmt->sa), MAC2STR(mgmt->da), MAC2STR(mgmt->bssid), + mgmt->u.action.category, valid); + wpa_hexdump(MSG_MSGDUMP, "ACTION payload", data + 24, len - 24); + + if (is_broadcast_ether_addr(mgmt->bssid)) { + rx_mgmt_action_no_bss(wt, mgmt, len, no_ack); + return; + } bss = bss_get(wt, mgmt->bssid); if (bss == NULL) return; @@ -1939,18 +2078,6 @@ static void rx_mgmt_action(struct wlantest *wt, const u8 *data, size_t len, if (sta == NULL) return; - if (len < 24 + 1) { - add_note(wt, MSG_INFO, "Too short Action frame from " MACSTR, - MAC2STR(mgmt->sa)); - return; - } - - wpa_printf(MSG_DEBUG, "ACTION " MACSTR " -> " MACSTR - " (category=%u) (valid=%d)", - MAC2STR(mgmt->sa), MAC2STR(mgmt->da), - mgmt->u.action.category, valid); - wpa_hexdump(MSG_MSGDUMP, "ACTION payload", data + 24, len - 24); - if (mgmt->u.action.category != WLAN_ACTION_PUBLIC && sta->state < STATE3) { add_note(wt, MSG_INFO, "Action frame sent when STA is not in " @@ -2061,7 +2188,8 @@ static int check_bip(struct wlantest *wt, const u8 *data, size_t len) fc = le_to_host16(mgmt->frame_control); stype = WLAN_FC_GET_STYPE(fc); - if (stype == WLAN_FC_STYPE_ACTION) { + if (stype == WLAN_FC_STYPE_ACTION || + stype == WLAN_FC_STYPE_ACTION_NO_ACK) { if (len < 24 + 1) return 0; if (mgmt->u.action.category == WLAN_ACTION_PUBLIC) @@ -2138,6 +2266,56 @@ static int check_bip(struct wlantest *wt, const u8 *data, size_t len) } +static u8 * try_tk(struct wpa_ptk *ptk, const u8 *data, size_t len, + size_t *dlen) +{ + const struct ieee80211_hdr *hdr; + u8 *decrypted, *frame; + + hdr = (const struct ieee80211_hdr *) data; + decrypted = ccmp_decrypt(ptk->tk, hdr, data + 24, len - 24, dlen); + if (!decrypted) + return NULL; + + frame = os_malloc(24 + *dlen); + if (frame) { + os_memcpy(frame, data, 24); + os_memcpy(frame + 24, decrypted, *dlen); + *dlen += 24; + } + os_free(decrypted); + return frame; +} + + +static u8 * mgmt_ccmp_decrypt_tk(struct wlantest *wt, const u8 *data, + size_t len, size_t *dlen) +{ + struct wlantest_ptk *ptk; + u8 *decrypted; + int prev_level = wpa_debug_level; + int keyid; + + keyid = data[24 + 3] >> 6; + + wpa_debug_level = MSG_WARNING; + dl_list_for_each(ptk, &wt->ptk, struct wlantest_ptk, list) { + decrypted = try_tk(&ptk->ptk, data, len, dlen); + if (decrypted) { + wpa_debug_level = prev_level; + add_note(wt, MSG_DEBUG, + "Found TK match from the list of all known TKs"); + write_decrypted_note(wt, decrypted, ptk->ptk.tk, + ptk->ptk.tk_len, keyid); + return decrypted; + } + } + wpa_debug_level = prev_level; + + return NULL; +} + + static u8 * mgmt_ccmp_decrypt(struct wlantest *wt, const u8 *data, size_t len, size_t *dlen) { @@ -2147,19 +2325,11 @@ static u8 * mgmt_ccmp_decrypt(struct wlantest *wt, const u8 *data, size_t len, int keyid; u8 *decrypted, *frame = NULL; u8 pn[6], *rsc; + u16 fc; + u8 mask; hdr = (const struct ieee80211_hdr *) data; - bss = bss_get(wt, hdr->addr3); - if (bss == NULL) - return NULL; - if (os_memcmp(hdr->addr1, hdr->addr3, ETH_ALEN) == 0) - sta = sta_get(bss, hdr->addr2); - else - sta = sta_get(bss, hdr->addr1); - if (sta == NULL || !sta->ptk_set) { - add_note(wt, MSG_MSGDUMP, "No PTK known to decrypt the frame"); - return NULL; - } + fc = le_to_host16(hdr->frame_control); if (len < 24 + 4) return NULL; @@ -2171,7 +2341,11 @@ static u8 * mgmt_ccmp_decrypt(struct wlantest *wt, const u8 *data, size_t len, return NULL; } - if (data[24 + 2] != 0 || (data[24 + 3] & 0x1f) != 0) { + mask = 0x1f; + if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION || + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION_NO_ACK) + mask &= ~0x10; /* FTM */ + if (data[24 + 2] != 0 || (data[24 + 3] & mask) != 0) { add_note(wt, MSG_INFO, "CCMP mgmt frame from " MACSTR " used " "non-zero reserved bit", MAC2STR(hdr->addr2)); } @@ -2183,6 +2357,21 @@ static u8 * mgmt_ccmp_decrypt(struct wlantest *wt, const u8 *data, size_t len, MACSTR, keyid, MAC2STR(hdr->addr2)); } + bss = bss_get(wt, hdr->addr3); + if (bss == NULL) + return mgmt_ccmp_decrypt_tk(wt, data, len, dlen); + if (os_memcmp(hdr->addr1, hdr->addr3, ETH_ALEN) == 0) + sta = sta_get(bss, hdr->addr2); + else + sta = sta_get(bss, hdr->addr1); + if (sta == NULL || !sta->ptk_set) { + decrypted = mgmt_ccmp_decrypt_tk(wt, data, len, dlen); + if (!decrypted) + add_note(wt, MSG_MSGDUMP, + "No PTK known to decrypt the frame"); + return decrypted; + } + if (os_memcmp(hdr->addr1, hdr->addr3, ETH_ALEN) == 0) rsc = sta->rsc_tods[16]; else @@ -2236,7 +2425,8 @@ static int check_mgmt_ccmp(struct wlantest *wt, const u8 *data, size_t len) mgmt = (const struct ieee80211_mgmt *) data; fc = le_to_host16(mgmt->frame_control); - if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) { + if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION || + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION_NO_ACK) { if (len > 24 && mgmt->u.action.category == WLAN_ACTION_PUBLIC) return 0; /* Not a robust management frame */ @@ -2255,7 +2445,8 @@ static int check_mgmt_ccmp(struct wlantest *wt, const u8 *data, size_t len) if ((bss->rsn_capab & WPA_CAPABILITY_MFPC) && (sta->rsn_capab & WPA_CAPABILITY_MFPC) && (sta->state == STATE3 || - WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION)) { + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION || + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION_NO_ACK)) { add_note(wt, MSG_INFO, "Robust individually-addressed " "management frame sent without CCMP by " MACSTR, MAC2STR(mgmt->sa)); @@ -2285,7 +2476,8 @@ void rx_mgmt(struct wlantest *wt, const u8 *data, size_t len) if ((hdr->addr1[0] & 0x01) && (stype == WLAN_FC_STYPE_DEAUTH || stype == WLAN_FC_STYPE_DISASSOC || - stype == WLAN_FC_STYPE_ACTION)) { + stype == WLAN_FC_STYPE_ACTION || + stype == WLAN_FC_STYPE_ACTION_NO_ACK)) { if (check_bip(wt, data, len) < 0) valid = 0; } @@ -2305,7 +2497,8 @@ void rx_mgmt(struct wlantest *wt, const u8 *data, size_t len) !(hdr->addr1[0] & 0x01) && (stype == WLAN_FC_STYPE_DEAUTH || stype == WLAN_FC_STYPE_DISASSOC || - stype == WLAN_FC_STYPE_ACTION)) { + stype == WLAN_FC_STYPE_ACTION || + stype == WLAN_FC_STYPE_ACTION_NO_ACK)) { decrypted = mgmt_ccmp_decrypt(wt, data, len, &dlen); if (decrypted) { write_pcap_decrypted(wt, decrypted, dlen, NULL, 0); @@ -2319,7 +2512,8 @@ void rx_mgmt(struct wlantest *wt, const u8 *data, size_t len) !(hdr->addr1[0] & 0x01) && (stype == WLAN_FC_STYPE_DEAUTH || stype == WLAN_FC_STYPE_DISASSOC || - stype == WLAN_FC_STYPE_ACTION)) { + stype == WLAN_FC_STYPE_ACTION || + stype == WLAN_FC_STYPE_ACTION_NO_ACK)) { if (check_mgmt_ccmp(wt, data, len) < 0) valid = 0; } @@ -2353,7 +2547,10 @@ void rx_mgmt(struct wlantest *wt, const u8 *data, size_t len) rx_mgmt_disassoc(wt, data, len, valid); break; case WLAN_FC_STYPE_ACTION: - rx_mgmt_action(wt, data, len, valid); + rx_mgmt_action(wt, data, len, valid, false); + break; + case WLAN_FC_STYPE_ACTION_NO_ACK: + rx_mgmt_action(wt, data, len, valid, true); break; } diff --git a/wlantest/sta.c b/wlantest/sta.c index 62dae07..02ecb78 100644 --- a/wlantest/sta.c +++ b/wlantest/sta.c @@ -180,8 +180,8 @@ skip_rsn_wpa: wpa_printf(MSG_INFO, "STA " MACSTR " proto=%s%s%s%s" "pairwise=%s%s%s%s%s%s%s" - "key_mgmt=%s%s%s%s%s%s%s%s%s%s%s" - "rsn_capab=%s%s%s%s%s%s", + "key_mgmt=%s%s%s%s%s%s%s%s%s%s%s%s%s%s" + "rsn_capab=%s%s%s%s%s%s%s%s%s%s", MAC2STR(sta->addr), sta->proto == 0 ? "OPEN " : "", sta->proto & WPA_PROTO_WPA ? "WPA " : "", @@ -206,7 +206,10 @@ skip_rsn_wpa: "EAP-SHA256 " : "", sta->key_mgmt & WPA_KEY_MGMT_PSK_SHA256 ? "PSK-SHA256 " : "", + sta->key_mgmt & WPA_KEY_MGMT_OWE ? "OWE " : "", + sta->key_mgmt & WPA_KEY_MGMT_PASN ? "PASN " : "", sta->key_mgmt & WPA_KEY_MGMT_OSEN ? "OSEN " : "", + sta->key_mgmt & WPA_KEY_MGMT_DPP ? "DPP " : "", sta->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B ? "EAP-SUITE-B " : "", sta->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 ? @@ -218,5 +221,12 @@ skip_rsn_wpa: sta->rsn_capab & WPA_CAPABILITY_MFPC ? "MFPC " : "", sta->rsn_capab & WPA_CAPABILITY_PEERKEY_ENABLED ? "PEERKEY " : "", - sta->rsn_capab & WPA_CAPABILITY_OCVC ? "OCVC " : ""); + sta->rsn_capab & WPA_CAPABILITY_SPP_A_MSDU_CAPABLE ? + "SPP-A-MSDU-CAPAB " : "", + sta->rsn_capab & WPA_CAPABILITY_SPP_A_MSDU_REQUIRED ? + "SPP-A-MSDU-REQUIRED " : "", + sta->rsn_capab & WPA_CAPABILITY_PBAC ? "PBAC " : "", + sta->rsn_capab & WPA_CAPABILITY_OCVC ? "OCVC " : "", + sta->rsn_capab & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST ? + "ExtKeyID " : ""); } diff --git a/wlantest/wlantest.c b/wlantest/wlantest.c index 1b8d714..62c89e2 100644 --- a/wlantest/wlantest.c +++ b/wlantest/wlantest.c @@ -323,6 +323,19 @@ size_t notes_len(struct wlantest *wt, size_t hdrlen) } +void write_decrypted_note(struct wlantest *wt, const u8 *decrypted, + const u8 *tk, size_t tk_len, int keyid) +{ + char tk_hex[65]; + + if (!decrypted) + return; + + wpa_snprintf_hex(tk_hex, sizeof(tk_hex), tk, tk_len); + add_note(wt, MSG_EXCESSIVE, "TK[%d] %s", keyid, tk_hex); +} + + int wlantest_relog(struct wlantest *wt) { int ret = 0; diff --git a/wlantest/wlantest.h b/wlantest/wlantest.h index 0c266f4..658a3a0 100644 --- a/wlantest/wlantest.h +++ b/wlantest/wlantest.h @@ -232,6 +232,8 @@ void add_note(struct wlantest *wt, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4); void clear_notes(struct wlantest *wt); size_t notes_len(struct wlantest *wt, size_t hdrlen); +void write_decrypted_note(struct wlantest *wt, const u8 *decrypted, + const u8 *tk, size_t tk_len, int keyid); int add_wep(struct wlantest *wt, const char *key); int read_cap_file(struct wlantest *wt, const char *fname); diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk index ed7e358..fd74c16 100644 --- a/wpa_supplicant/Android.mk +++ b/wpa_supplicant/Android.mk @@ -32,6 +32,10 @@ ifeq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB),) L_CFLAGS += -DANDROID_LIB_STUB endif +ifneq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB_EVENT),) +L_CFLAGS += -DANDROID_LIB_EVENT +endif + # Disable roaming in wpa_supplicant ifdef CONFIG_NO_ROAMING L_CFLAGS += -DCONFIG_NO_ROAMING @@ -90,6 +94,7 @@ OBJS += notify.c OBJS += bss.c OBJS += eap_register.c OBJS += src/utils/common.c +OBJS += src/utils/config.c OBJS += src/utils/wpa_debug.c OBJS += src/utils/wpabuf.c OBJS += src/utils/bitfield.c @@ -367,6 +372,17 @@ L_CFLAGS += -DCONFIG_WIFI_DISPLAY OBJS += wifi_display.c endif +ifdef CONFIG_PASN +L_CFLAGS += -DCONFIG_PASN +L_CFLAGS += -DCONFIG_PTKSA_CACHE +NEED_HMAC_SHA256_KDF=y +NEED_HMAC_SHA384_KDF=y +NEED_SHA256=y +NEED_SHA384=y +OBJS += src/common/ptksa_cache.c +OBJS += pasn_supplicant.c +endif + ifdef CONFIG_HS20 OBJS += hs20_supplicant.c L_CFLAGS += -DCONFIG_HS20 @@ -1601,6 +1617,12 @@ L_CFLAGS += -DCONFIG_EXT_PASSWORD_TEST NEED_EXT_PASSWORD=y endif +ifdef CONFIG_EXT_PASSWORD_FILE +OBJS += src/utils/ext_password_file.c +L_CFLAGS += -DCONFIG_EXT_PASSWORD_FILE +NEED_EXT_PASSWORD=y +endif + ifdef NEED_EXT_PASSWORD OBJS += src/utils/ext_password.c L_CFLAGS += -DCONFIG_EXT_PASSWORD @@ -1631,7 +1653,7 @@ endif OBJS += src/drivers/driver_common.c -OBJS += wpa_supplicant.c events.c blacklist.c wpas_glue.c scan.c +OBJS += wpa_supplicant.c events.c bssid_ignore.c wpas_glue.c scan.c OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.c OBJS_t += src/radius/radius_client.c OBJS_t += src/radius/radius.c diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index 9adadf1..bc3aa1d 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -83,6 +83,7 @@ OBJS += notify.o OBJS += bss.o OBJS += eap_register.o OBJS += ../src/utils/common.o +OBJS += ../src/utils/config.o OBJS += ../src/utils/wpa_debug.o OBJS += ../src/utils/wpabuf.o OBJS += ../src/utils/bitfield.o @@ -390,6 +391,17 @@ CFLAGS += -DCONFIG_WIFI_DISPLAY OBJS += wifi_display.o endif +ifdef CONFIG_PASN +CFLAGS += -DCONFIG_PASN +CFLAGS += -DCONFIG_PTKSA_CACHE +NEED_HMAC_SHA256_KDF=y +NEED_HMAC_SHA384_KDF=y +NEED_SHA256=y +NEED_SHA384=y +OBJS += ../src/common/ptksa_cache.o +OBJS += pasn_supplicant.o +endif + ifdef CONFIG_HS20 OBJS += hs20_supplicant.o CFLAGS += -DCONFIG_HS20 @@ -1739,6 +1751,12 @@ CFLAGS += -DCONFIG_EXT_PASSWORD_TEST NEED_EXT_PASSWORD=y endif +ifdef CONFIG_EXT_PASSWORD_FILE +OBJS += ../src/utils/ext_password_file.o +CFLAGS += -DCONFIG_EXT_PASSWORD_FILE +NEED_EXT_PASSWORD=y +endif + ifdef NEED_EXT_PASSWORD OBJS += ../src/utils/ext_password.o CFLAGS += -DCONFIG_EXT_PASSWORD @@ -1781,7 +1799,7 @@ endif OBJS += ../src/drivers/driver_common.o OBJS_priv += ../src/drivers/driver_common.o -OBJS += wpa_supplicant.o events.o blacklist.o wpas_glue.o scan.o +OBJS += wpa_supplicant.o events.o bssid_ignore.o wpas_glue.o scan.o OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.o OBJS_t += ../src/radius/radius_client.o OBJS_t += ../src/radius/radius.o diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index ac88a7d..cfefa48 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -44,6 +44,7 @@ static void wpas_wps_ap_pin_timeout(void *eloop_data, void *user_ctx); #endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P static bool is_chanwidth160_supported(struct hostapd_hw_modes *mode, struct hostapd_config *conf) { @@ -63,6 +64,7 @@ static bool is_chanwidth160_supported(struct hostapd_hw_modes *mode, return true; return false; } +#endif /* CONFIG_P2P */ static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s, @@ -264,6 +266,16 @@ int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, "HT secondary channel offset %d for P2P group", conf->secondary_channel); + } else if (ssid->p2p_group && conf->secondary_channel && + conf->hw_mode != HOSTAPD_MODE_IEEE80211A) { + /* This ended up trying to configure invalid + * 2.4 GHz channels (e.g., HT40+ on channel 11) + * in some cases, so clear the secondary channel + * configuration now to avoid such cases that + * would lead to group formation failures. */ + wpa_printf(MSG_DEBUG, + "Disable HT secondary channel for P2P group on 2.4 GHz"); + conf->secondary_channel = 0; } #endif /* CONFIG_P2P */ diff --git a/wpa_supplicant/blacklist.h b/wpa_supplicant/blacklist.h deleted file mode 100644 index a1c60d5..0000000 --- a/wpa_supplicant/blacklist.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * wpa_supplicant - Temporary BSSID blacklist - * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#ifndef BLACKLIST_H -#define BLACKLIST_H - -struct wpa_blacklist { - struct wpa_blacklist *next; - u8 bssid[ETH_ALEN]; - int count; - /* Time of most recent blacklist event. */ - struct os_reltime blacklist_start; - /* - * Number of seconds after blacklist_start that the entry will be - * considered blacklisted. - */ - int timeout_secs; -}; - -struct wpa_blacklist * wpa_blacklist_get(struct wpa_supplicant *wpa_s, - const u8 *bssid); -int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid); -int wpa_blacklist_del(struct wpa_supplicant *wpa_s, const u8 *bssid); -int wpa_blacklist_is_blacklisted(struct wpa_supplicant *wpa_s, const u8 *bssid); -void wpa_blacklist_clear(struct wpa_supplicant *wpa_s); -void wpa_blacklist_update(struct wpa_supplicant *wpa_s); - -#endif /* BLACKLIST_H */ diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c index e9c2f82..e13783c 100644 --- a/wpa_supplicant/bss.c +++ b/wpa_supplicant/bss.c @@ -19,18 +19,6 @@ #include "scan.h" #include "bss.h" - -#define WPA_BSS_FREQ_CHANGED_FLAG BIT(0) -#define WPA_BSS_SIGNAL_CHANGED_FLAG BIT(1) -#define WPA_BSS_PRIVACY_CHANGED_FLAG BIT(2) -#define WPA_BSS_MODE_CHANGED_FLAG BIT(3) -#define WPA_BSS_WPAIE_CHANGED_FLAG BIT(4) -#define WPA_BSS_RSNIE_CHANGED_FLAG BIT(5) -#define WPA_BSS_WPS_CHANGED_FLAG BIT(6) -#define WPA_BSS_RATES_CHANGED_FLAG BIT(7) -#define WPA_BSS_IES_CHANGED_FLAG BIT(8) - - static void wpa_bss_set_hessid(struct wpa_bss *bss) { #ifdef CONFIG_INTERWORKING @@ -588,8 +576,8 @@ static u32 wpa_bss_compare_res(const struct wpa_bss *old, } -static void notify_bss_changes(struct wpa_supplicant *wpa_s, u32 changes, - const struct wpa_bss *bss) +void notify_bss_changes(struct wpa_supplicant *wpa_s, u32 changes, + const struct wpa_bss *bss) { if (changes & WPA_BSS_FREQ_CHANGED_FLAG) wpas_notify_bss_freq_changed(wpa_s, bss->id); @@ -1390,6 +1378,8 @@ const u8 * wpa_bss_get_fils_cache_id(const struct wpa_bss *bss) int wpa_bss_ext_capab(const struct wpa_bss *bss, unsigned int capab) { + if (!bss) + return 0; return ieee802_11_ext_capab(wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB), capab); } diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h index 7cb1745..4078b9b 100644 --- a/wpa_supplicant/bss.h +++ b/wpa_supplicant/bss.h @@ -20,10 +20,20 @@ struct wpa_scan_res; #define WPA_BSS_ANQP_FETCH_TRIED BIT(6) #define WPA_BSS_OWE_TRANSITION BIT(7) +#define WPA_BSS_FREQ_CHANGED_FLAG BIT(0) +#define WPA_BSS_SIGNAL_CHANGED_FLAG BIT(1) +#define WPA_BSS_PRIVACY_CHANGED_FLAG BIT(2) +#define WPA_BSS_MODE_CHANGED_FLAG BIT(3) +#define WPA_BSS_WPAIE_CHANGED_FLAG BIT(4) +#define WPA_BSS_RSNIE_CHANGED_FLAG BIT(5) +#define WPA_BSS_WPS_CHANGED_FLAG BIT(6) +#define WPA_BSS_RATES_CHANGED_FLAG BIT(7) +#define WPA_BSS_IES_CHANGED_FLAG BIT(8) + struct wpa_bss_anqp_elem { struct dl_list list; u16 infoid; - bool protected; /* received in a protected GAS response */ + bool protected_response; /* received in a protected GAS response */ struct wpabuf *payload; }; @@ -120,6 +130,8 @@ static inline const u8 * wpa_bss_ie_ptr(const struct wpa_bss *bss) return bss->ies; } +void notify_bss_changes(struct wpa_supplicant *wpa_s, u32 changes, + const struct wpa_bss *bss); void wpa_bss_update_start(struct wpa_supplicant *wpa_s); void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, struct wpa_scan_res *res, @@ -177,7 +189,7 @@ static inline int bss_is_pbss(struct wpa_bss *bss) static inline void wpa_bss_update_level(struct wpa_bss *bss, int new_level) { - if (bss != NULL && new_level < 0) + if (bss != NULL && new_level > -WPA_INVALID_NOISE && new_level < 0) bss->level = new_level; } diff --git a/wpa_supplicant/blacklist.c b/wpa_supplicant/bssid_ignore.c index 2f32644..e378577 100644 --- a/wpa_supplicant/blacklist.c +++ b/wpa_supplicant/bssid_ignore.c @@ -1,6 +1,6 @@ /* - * wpa_supplicant - Temporary BSSID blacklist - * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * wpa_supplicant - List of temporarily ignored BSSIDs + * Copyright (c) 2003-2021, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,32 +10,32 @@ #include "common.h" #include "wpa_supplicant_i.h" -#include "blacklist.h" +#include "bssid_ignore.h" /** - * wpa_blacklist_get - Get the blacklist entry for a BSSID + * wpa_bssid_ignore_get - Get the ignore list entry for a BSSID * @wpa_s: Pointer to wpa_supplicant data * @bssid: BSSID - * Returns: Matching blacklist entry for the BSSID or %NULL if not found + * Returns: Matching entry for the BSSID or %NULL if not found */ -struct wpa_blacklist * wpa_blacklist_get(struct wpa_supplicant *wpa_s, - const u8 *bssid) +struct wpa_bssid_ignore * wpa_bssid_ignore_get(struct wpa_supplicant *wpa_s, + const u8 *bssid) { - struct wpa_blacklist *e; + struct wpa_bssid_ignore *e; if (wpa_s == NULL || bssid == NULL) return NULL; if (wpa_s->current_ssid && wpa_s->current_ssid->was_recently_reconfigured) { - wpa_blacklist_clear(wpa_s); + wpa_bssid_ignore_clear(wpa_s); wpa_s->current_ssid->was_recently_reconfigured = false; return NULL; } - wpa_blacklist_update(wpa_s); + wpa_bssid_ignore_update(wpa_s); - e = wpa_s->blacklist; + e = wpa_s->bssid_ignore; while (e) { if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0) return e; @@ -47,33 +47,33 @@ struct wpa_blacklist * wpa_blacklist_get(struct wpa_supplicant *wpa_s, /** - * wpa_blacklist_add - Add an BSSID to the blacklist + * wpa_bssid_ignore_add - Add an BSSID to the ignore list * @wpa_s: Pointer to wpa_supplicant data - * @bssid: BSSID to be added to the blacklist - * Returns: Current blacklist count on success, -1 on failure + * @bssid: BSSID to be added to the ignore list + * Returns: Current ignore list count on success, -1 on failure * - * This function adds the specified BSSID to the blacklist or increases the - * blacklist count if the BSSID was already listed. It should be called when + * This function adds the specified BSSID to the ignore list or increases the + * ignore count if the BSSID was already listed. It should be called when * an association attempt fails either due to the selected BSS rejecting * association or due to timeout. * - * This blacklist is used to force %wpa_supplicant to go through all available + * This ignore list is used to force %wpa_supplicant to go through all available * BSSes before retrying to associate with an BSS that rejected or timed out * association. It does not prevent the listed BSS from being used; it only * changes the order in which they are tried. */ -int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid) +int wpa_bssid_ignore_add(struct wpa_supplicant *wpa_s, const u8 *bssid) { - struct wpa_blacklist *e; + struct wpa_bssid_ignore *e; struct os_reltime now; if (wpa_s == NULL || bssid == NULL) return -1; - e = wpa_blacklist_get(wpa_s, bssid); + e = wpa_bssid_ignore_get(wpa_s, bssid); os_get_reltime(&now); if (e) { - e->blacklist_start = now; + e->start = now; e->count++; if (e->count > 5) e->timeout_secs = 1800; @@ -86,7 +86,7 @@ int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid) else e->timeout_secs = 10; wpa_printf(MSG_INFO, "BSSID " MACSTR - " blacklist count incremented to %d, blacklisting for %d seconds", + " ignore list count incremented to %d, ignoring for %d seconds", MAC2STR(bssid), e->count, e->timeout_secs); return e->count; } @@ -97,11 +97,11 @@ int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid) os_memcpy(e->bssid, bssid, ETH_ALEN); e->count = 1; e->timeout_secs = 10; - e->blacklist_start = now; - e->next = wpa_s->blacklist; - wpa_s->blacklist = e; + e->start = now; + e->next = wpa_s->bssid_ignore; + wpa_s->bssid_ignore = e; wpa_printf(MSG_DEBUG, "Added BSSID " MACSTR - " into blacklist, blacklisting for %d seconds", + " into ignore list, ignoring for %d seconds", MAC2STR(bssid), e->timeout_secs); return e->count; @@ -109,28 +109,28 @@ int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid) /** - * wpa_blacklist_del - Remove an BSSID from the blacklist + * wpa_bssid_ignore_del - Remove an BSSID from the ignore list * @wpa_s: Pointer to wpa_supplicant data - * @bssid: BSSID to be removed from the blacklist + * @bssid: BSSID to be removed from the ignore list * Returns: 0 on success, -1 on failure */ -int wpa_blacklist_del(struct wpa_supplicant *wpa_s, const u8 *bssid) +int wpa_bssid_ignore_del(struct wpa_supplicant *wpa_s, const u8 *bssid) { - struct wpa_blacklist *e, *prev = NULL; + struct wpa_bssid_ignore *e, *prev = NULL; if (wpa_s == NULL || bssid == NULL) return -1; - e = wpa_s->blacklist; + e = wpa_s->bssid_ignore; while (e) { if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0) { if (prev == NULL) { - wpa_s->blacklist = e->next; + wpa_s->bssid_ignore = e->next; } else { prev->next = e->next; } - wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR " from " - "blacklist", MAC2STR(bssid)); + wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR + " from ignore list", MAC2STR(bssid)); os_free(e); return 0; } @@ -142,75 +142,75 @@ int wpa_blacklist_del(struct wpa_supplicant *wpa_s, const u8 *bssid) /** - * wpa_blacklist_is_blacklisted - Check the blacklist status of a BSS + * wpa_bssid_ignore_is_listed - Check whether a BSSID is ignored temporarily * @wpa_s: Pointer to wpa_supplicant data * @bssid: BSSID to be checked - * Returns: count if BSS is currently considered to be blacklisted, 0 otherwise + * Returns: count if BSS is currently considered to be ignored, 0 otherwise */ -int wpa_blacklist_is_blacklisted(struct wpa_supplicant *wpa_s, const u8 *bssid) +int wpa_bssid_ignore_is_listed(struct wpa_supplicant *wpa_s, const u8 *bssid) { - struct wpa_blacklist *e; + struct wpa_bssid_ignore *e; struct os_reltime now; - e = wpa_blacklist_get(wpa_s, bssid); + e = wpa_bssid_ignore_get(wpa_s, bssid); if (!e) return 0; os_get_reltime(&now); - if (os_reltime_expired(&now, &e->blacklist_start, e->timeout_secs)) + if (os_reltime_expired(&now, &e->start, e->timeout_secs)) return 0; return e->count; } /** - * wpa_blacklist_clear - Clear the blacklist of all entries + * wpa_bssid_ignore_clear - Clear the ignore list of all entries * @wpa_s: Pointer to wpa_supplicant data */ -void wpa_blacklist_clear(struct wpa_supplicant *wpa_s) +void wpa_bssid_ignore_clear(struct wpa_supplicant *wpa_s) { - struct wpa_blacklist *e, *prev; + struct wpa_bssid_ignore *e, *prev; - e = wpa_s->blacklist; - wpa_s->blacklist = NULL; + e = wpa_s->bssid_ignore; + wpa_s->bssid_ignore = NULL; while (e) { prev = e; e = e->next; - wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR " from " - "blacklist (clear)", MAC2STR(prev->bssid)); + wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR + " from ignore list (clear)", MAC2STR(prev->bssid)); os_free(prev); } } /** - * wpa_blacklist_update - Update the entries in the blacklist, + * wpa_bssid_ignore_update - Update the entries in the ignore list, * deleting entries that have been expired for over an hour. * @wpa_s: Pointer to wpa_supplicant data */ -void wpa_blacklist_update(struct wpa_supplicant *wpa_s) +void wpa_bssid_ignore_update(struct wpa_supplicant *wpa_s) { - struct wpa_blacklist *e, *prev = NULL; + struct wpa_bssid_ignore *e, *prev = NULL; struct os_reltime now; if (!wpa_s) return; - e = wpa_s->blacklist; + e = wpa_s->bssid_ignore; os_get_reltime(&now); while (e) { - if (os_reltime_expired(&now, &e->blacklist_start, + if (os_reltime_expired(&now, &e->start, e->timeout_secs + 3600)) { - struct wpa_blacklist *to_delete = e; + struct wpa_bssid_ignore *to_delete = e; if (prev) { prev->next = e->next; e = prev->next; } else { - wpa_s->blacklist = e->next; - e = wpa_s->blacklist; + wpa_s->bssid_ignore = e->next; + e = wpa_s->bssid_ignore; } wpa_printf(MSG_INFO, "Removed BSSID " MACSTR - " from blacklist (expired)", + " from ignore list (expired)", MAC2STR(to_delete->bssid)); os_free(to_delete); } else { diff --git a/wpa_supplicant/bssid_ignore.h b/wpa_supplicant/bssid_ignore.h new file mode 100644 index 0000000..721b0e1 --- /dev/null +++ b/wpa_supplicant/bssid_ignore.h @@ -0,0 +1,33 @@ +/* + * wpa_supplicant - List of temporarily ignored BSSIDs + * Copyright (c) 2003-2021, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef BSSID_IGNORE_H +#define BSSID_IGNORE_H + +struct wpa_bssid_ignore { + struct wpa_bssid_ignore *next; + u8 bssid[ETH_ALEN]; + int count; + /* Time of the most recent trigger to ignore this BSSID. */ + struct os_reltime start; + /* + * Number of seconds after start that the entey will be considered + * valid. + */ + int timeout_secs; +}; + +struct wpa_bssid_ignore * wpa_bssid_ignore_get(struct wpa_supplicant *wpa_s, + const u8 *bssid); +int wpa_bssid_ignore_add(struct wpa_supplicant *wpa_s, const u8 *bssid); +int wpa_bssid_ignore_del(struct wpa_supplicant *wpa_s, const u8 *bssid); +int wpa_bssid_ignore_is_listed(struct wpa_supplicant *wpa_s, const u8 *bssid); +void wpa_bssid_ignore_clear(struct wpa_supplicant *wpa_s); +void wpa_bssid_ignore_update(struct wpa_supplicant *wpa_s); + +#endif /* BSSID_IGNORE_H */ diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index c516154..ce5c80d 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -442,47 +442,99 @@ static char * wpa_config_write_bssid_hint(const struct parse_data *data, #endif /* NO_CONFIG_WRITE */ +static int wpa_config_parse_bssid_ignore(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + return wpa_config_parse_addr_list(data, line, value, + &ssid->bssid_ignore, + &ssid->num_bssid_ignore, + "bssid_ignore", 1, 1); +} + + +/* deprecated alias for bssid_ignore for backwards compatibility */ static int wpa_config_parse_bssid_blacklist(const struct parse_data *data, struct wpa_ssid *ssid, int line, const char *value) { return wpa_config_parse_addr_list(data, line, value, - &ssid->bssid_blacklist, - &ssid->num_bssid_blacklist, - "bssid_blacklist", 1, 1); + &ssid->bssid_ignore, + &ssid->num_bssid_ignore, + "bssid_ignore", 1, 1); } #ifndef NO_CONFIG_WRITE + +static char * wpa_config_write_bssid_ignore(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_addr_list(data, ssid->bssid_ignore, + ssid->num_bssid_ignore, + "bssid_ignore"); +} + + +/* deprecated alias for bssid_ignore for backwards compatibility */ static char * wpa_config_write_bssid_blacklist(const struct parse_data *data, struct wpa_ssid *ssid) { - return wpa_config_write_addr_list(data, ssid->bssid_blacklist, - ssid->num_bssid_blacklist, - "bssid_blacklist"); + return wpa_config_write_addr_list(data, ssid->bssid_ignore, + ssid->num_bssid_ignore, + "bssid_ignore"); } + #endif /* NO_CONFIG_WRITE */ +static int wpa_config_parse_bssid_accept(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + return wpa_config_parse_addr_list(data, line, value, + &ssid->bssid_accept, + &ssid->num_bssid_accept, + "bssid_accept", 1, 1); +} + + +/* deprecated alias for bssid_accept for backwards compatibility */ static int wpa_config_parse_bssid_whitelist(const struct parse_data *data, struct wpa_ssid *ssid, int line, const char *value) { return wpa_config_parse_addr_list(data, line, value, - &ssid->bssid_whitelist, - &ssid->num_bssid_whitelist, - "bssid_whitelist", 1, 1); + &ssid->bssid_accept, + &ssid->num_bssid_accept, + "bssid_accept", 1, 1); } #ifndef NO_CONFIG_WRITE + +static char * wpa_config_write_bssid_accept(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_addr_list(data, ssid->bssid_accept, + ssid->num_bssid_accept, + "bssid_accept"); +} + + +/* deprecated alias for bssid_accept for backwards compatibility */ static char * wpa_config_write_bssid_whitelist(const struct parse_data *data, struct wpa_ssid *ssid) { - return wpa_config_write_addr_list(data, ssid->bssid_whitelist, - ssid->num_bssid_whitelist, - "bssid_whitelist"); + return wpa_config_write_addr_list(data, ssid->bssid_accept, + ssid->num_bssid_accept, + "bssid_accept"); } + +#endif /* NO_CONFIG_WRITE */ + + +#ifndef NO_CONFIG_WRITE #endif /* NO_CONFIG_WRITE */ @@ -2362,8 +2414,10 @@ static const struct parse_data ssid_fields[] = { { INT_RANGE(scan_ssid, 0, 1) }, { FUNC(bssid) }, { FUNC(bssid_hint) }, - { FUNC(bssid_blacklist) }, - { FUNC(bssid_whitelist) }, + { FUNC(bssid_ignore) }, + { FUNC(bssid_accept) }, + { FUNC(bssid_blacklist) }, /* deprecated alias for bssid_ignore */ + { FUNC(bssid_whitelist) }, /* deprecated alias for bssid_accept */ { FUNC_KEY(psk) }, { INT(mem_only_psk) }, { STR_KEY(sae_password) }, @@ -2763,8 +2817,8 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid) os_free(ssid->freq_list); os_free(ssid->bgscan); os_free(ssid->p2p_client_list); - os_free(ssid->bssid_blacklist); - os_free(ssid->bssid_whitelist); + os_free(ssid->bssid_ignore); + os_free(ssid->bssid_accept); #ifdef CONFIG_HT_OVERRIDES os_free(ssid->ht_mcs); #endif /* CONFIG_HT_OVERRIDES */ @@ -2890,6 +2944,7 @@ void wpa_config_free(struct wpa_config *config) os_free(config->p2p_no_go_freq.range); os_free(config->autoscan); os_free(config->freq_list); + os_free(config->initial_freq_list); wpabuf_free(config->wps_nfc_dh_pubkey); wpabuf_free(config->wps_nfc_dh_privkey); wpabuf_free(config->wps_nfc_dev_pw); @@ -4308,6 +4363,7 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface, config->ap_isolate = DEFAULT_AP_ISOLATE; config->access_network_type = DEFAULT_ACCESS_NETWORK_TYPE; config->scan_cur_freq = DEFAULT_SCAN_CUR_FREQ; + config->scan_res_valid_for_connect = DEFAULT_SCAN_RES_VALID_FOR_CONNECT; config->wmm_ac_params[0] = ac_be; config->wmm_ac_params[1] = ac_bk; config->wmm_ac_params[2] = ac_vi; @@ -4547,6 +4603,26 @@ static int wpa_config_process_freq_list(const struct global_parse_data *data, } +static int +wpa_config_process_initial_freq_list(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *value) +{ + int *freqs; + + freqs = wpa_config_parse_int_array(value); + if (!freqs) + return -1; + if (freqs[0] == 0) { + os_free(freqs); + freqs = NULL; + } + os_free(config->initial_freq_list); + config->initial_freq_list = freqs; + return 0; +} + + #ifdef CONFIG_P2P static int wpa_global_config_parse_ipv4(const struct global_parse_data *data, struct wpa_config *config, int line, @@ -5082,7 +5158,9 @@ static const struct global_parse_data global_fields[] = { { FUNC(ap_vendor_elements), 0 }, { INT_RANGE(ignore_old_scan_res, 0, 1), 0 }, { FUNC(freq_list), 0 }, + { FUNC(initial_freq_list), 0}, { INT(scan_cur_freq), 0 }, + { INT(scan_res_valid_for_connect), 0}, { INT(sched_scan_interval), 0 }, { INT(sched_scan_start_delay), 0 }, { INT(tdls_external_control), 0}, @@ -5126,6 +5204,12 @@ static const struct global_parse_data global_fields[] = { { INT_RANGE(disable_btm, 0, 1), CFG_CHANGED_DISABLE_BTM }, { INT_RANGE(extended_key_id, 0, 1), 0 }, #endif /* CONFIG_WNM */ + { INT_RANGE(wowlan_disconnect_on_deinit, 0, 1), 0}, +#ifdef CONFIG_PASN +#ifdef CONFIG_TESTING_OPTIONS + { INT_RANGE(force_kdk_derivation, 0, 1), 0 }, +#endif /* CONFIG_TESTING_OPTIONS */ +#endif /* CONFIG_PASN */ }; #undef FUNC diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index 648573d..aac4a9d 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -45,6 +45,7 @@ #define DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD -75 #define DEFAULT_OCE_SUPPORT OCE_STA #define DEFAULT_EXTENDED_KEY_ID 0 +#define DEFAULT_SCAN_RES_VALID_FOR_CONNECT 5 #include "config_ssid.h" #include "wps/wps.h" @@ -917,6 +918,19 @@ struct wpa_config { int *freq_list; /** + * initial_freq_list - like freq_list but for initial scan + * + * This is an optional zero-terminated array of frequencies in + * megahertz (MHz) to allow for narrowing scanning range when + * the application is started. + * + * This can be used to speed up initial connection time if the + * channel is known ahead of time, without limiting the scanned + * frequencies during normal use. + */ + int *initial_freq_list; + + /** * scan_cur_freq - Whether to scan only the current channel * * If true, attempt to scan only the current channel if any other @@ -925,6 +939,15 @@ struct wpa_config { int scan_cur_freq; /** + * scan_res_valid_for_connect - Seconds scans are valid for association + * + * This configures the number of seconds old scan results are considered + * valid for association. When scan results are older than this value + * a new scan is triggered prior to the association. + */ + int scan_res_valid_for_connect; + + /** * changed_parameters - Bitmap of changed parameters since last update */ unsigned int changed_parameters; @@ -1541,9 +1564,31 @@ struct wpa_config { /** * p2p_device_random_mac_addr - P2P Device MAC address policy default * - * 0 = use permanent MAC address + * 0 = use permanent MAC address (the one set by default by the device + * driver). Notice that, if the device driver is configured to + * always use random MAC addresses, this flag breaks reinvoking a + * persistent group, so flags 1 or 2 should be used instead with + * such drivers if persistent groups are used. * 1 = use random MAC address on creating the interface if there is no - * persistent groups. + * persistent group. Besides, if a persistent group is created, + * p2p_device_persistent_mac_addr is set to the MAC address of the + * P2P Device interface, so that this address will be subsequently + * used to change the MAC address of the P2P Device interface. With + * no persistent group, the random MAC address is created by + * wpa_supplicant, changing the one set by the device driver. + * The device driver shall support SIOCGIFFLAGS/SIOCSIFFLAGS ioctl + * interface control operations. + * 2 = this flag should be used when the device driver uses random MAC + * addresses by default when a P2P Device interface is created. + * If p2p_device_persistent_mac_addr is set, use this MAC address + * on creating the P2P Device interface. If not set, use the + * default method adopted by the device driver (e.g., random MAC + * address). Besides, if a persistent group is created, + * p2p_device_persistent_mac_addr is set to the MAC address of the + * P2P Device interface, so that this address will be subsequently + * used in place of the default address set by the device driver. + * (This option does not need support of SIOCGIFFLAGS/SIOCSIFFLAGS + * ioctl interface control operations and uses NL80211_ATTR_MAC). * * By default, permanent MAC address is used. */ @@ -1586,6 +1631,25 @@ struct wpa_config { * 1 = use Extended Key ID when possible */ int extended_key_id; + + /** + * wowlan_disconnect_on_deinit - Trigger disconnect on wpa_supplicant + * interface deinit even if the driver has enabled WoWLAN. + * + * 0 = Do not disconnect + * 1 = Trigger disconnection + */ + int wowlan_disconnect_on_deinit; + +#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 */ +#endif /* CONFIG_PASN*/ }; diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index 246644a..a535e3f 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -23,105 +23,7 @@ #include "p2p/p2p.h" #include "eap_peer/eap_methods.h" #include "eap_peer/eap.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; - } -} - - -/** - * 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. - */ -static 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; -} +#include "utils/config.h" static int wpa_config_validate_network(struct wpa_ssid *ssid, int line) @@ -767,8 +669,8 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) INT(scan_ssid); write_bssid(f, ssid); write_bssid_hint(f, ssid); - write_str(f, "bssid_blacklist", ssid); - write_str(f, "bssid_whitelist", ssid); + write_str(f, "bssid_ignore", ssid); + write_str(f, "bssid_accept", ssid); write_psk(f, ssid); INT(mem_only_psk); STR(sae_password); @@ -1475,9 +1377,23 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) } fprintf(f, "\n"); } + if (config->initial_freq_list && config->initial_freq_list[0]) { + int i; + fprintf(f, "initial_freq_list="); + for (i = 0; config->initial_freq_list[i]; i++) { + fprintf(f, "%s%d", i > 0 ? " " : "", + config->initial_freq_list[i]); + } + fprintf(f, "\n"); + } if (config->scan_cur_freq != DEFAULT_SCAN_CUR_FREQ) fprintf(f, "scan_cur_freq=%d\n", config->scan_cur_freq); + if (config->scan_res_valid_for_connect != + DEFAULT_SCAN_RES_VALID_FOR_CONNECT) + fprintf(f, "scan_res_valid_for_connect=%d\n", + config->scan_res_valid_for_connect); + if (config->sched_scan_interval) fprintf(f, "sched_scan_interval=%u\n", config->sched_scan_interval); @@ -1611,6 +1527,9 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->extended_key_id != DEFAULT_EXTENDED_KEY_ID) fprintf(f, "extended_key_id=%d\n", config->extended_key_id); + if (config->wowlan_disconnect_on_deinit) + fprintf(f, "wowlan_disconnect_on_deinit=%d\n", + config->wowlan_disconnect_on_deinit); } #endif /* CONFIG_NO_CONFIG_WRITE */ diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h index b4fdc8a..3f7b314 100644 --- a/wpa_supplicant/config_ssid.h +++ b/wpa_supplicant/config_ssid.h @@ -153,16 +153,16 @@ struct wpa_ssid { u8 bssid[ETH_ALEN]; /** - * bssid_blacklist - List of inacceptable BSSIDs + * bssid_ignore - List of inacceptable BSSIDs */ - u8 *bssid_blacklist; - size_t num_bssid_blacklist; + u8 *bssid_ignore; + size_t num_bssid_ignore; /** - * bssid_blacklist - List of acceptable BSSIDs + * bssid_accept - List of acceptable BSSIDs */ - u8 *bssid_whitelist; - size_t num_bssid_whitelist; + u8 *bssid_accept; + size_t num_bssid_accept; /** * bssid_set - Whether BSSID is configured for this network diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 8306950..0d4c2bc 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -22,6 +22,7 @@ #ifdef CONFIG_DPP #include "common/dpp.h" #endif /* CONFIG_DPP */ +#include "common/ptksa_cache.h" #include "crypto/tls.h" #include "ap/hostapd.h" #include "eap_peer/eap.h" @@ -48,7 +49,7 @@ #include "scan.h" #include "ctrl_iface.h" #include "interworking.h" -#include "blacklist.h" +#include "bssid_ignore.h" #include "autoscan.h" #include "wnm_sta.h" #include "offchannel.h" @@ -299,20 +300,30 @@ static int wpas_ctrl_pno(struct wpa_supplicant *wpa_s, char *cmd) } -static int wpas_ctrl_set_band(struct wpa_supplicant *wpa_s, char *band) +static int wpas_ctrl_set_band(struct wpa_supplicant *wpa_s, char *bands) { union wpa_event_data event; + u32 setband_mask = WPA_SETBAND_AUTO; - if (os_strcmp(band, "AUTO") == 0) - wpa_s->setband = WPA_SETBAND_AUTO; - else if (os_strcmp(band, "5G") == 0) - wpa_s->setband = WPA_SETBAND_5G; - else if (os_strcmp(band, "2G") == 0) - wpa_s->setband = WPA_SETBAND_2G; - else - return -1; + /* + * For example: + * SET setband 2G,6G + * SET setband 5G + * SET setband AUTO + */ + if (!os_strstr(bands, "AUTO")) { + if (os_strstr(bands, "5G")) + setband_mask |= WPA_SETBAND_5G; + if (os_strstr(bands, "6G")) + setband_mask |= WPA_SETBAND_6G; + if (os_strstr(bands, "2G")) + setband_mask |= WPA_SETBAND_2G; + if (setband_mask == WPA_SETBAND_AUTO) + return -1; + } - if (wpa_drv_setband(wpa_s, wpa_s->setband) == 0) { + wpa_s->setband_mask = setband_mask; + if (wpa_drv_setband(wpa_s, wpa_s->setband_mask) == 0) { os_memset(&event, 0, sizeof(event)); event.channel_list_changed.initiator = REGDOM_SET_BY_USER; event.channel_list_changed.type = REGDOM_TYPE_UNKNOWN; @@ -2543,20 +2554,20 @@ static int wpa_supplicant_ctrl_iface_bssid(struct wpa_supplicant *wpa_s, } -static int wpa_supplicant_ctrl_iface_blacklist(struct wpa_supplicant *wpa_s, - char *cmd, char *buf, - size_t buflen) +static int wpa_supplicant_ctrl_iface_bssid_ignore(struct wpa_supplicant *wpa_s, + char *cmd, char *buf, + size_t buflen) { u8 bssid[ETH_ALEN]; - struct wpa_blacklist *e; + struct wpa_bssid_ignore *e; char *pos, *end; int ret; - /* cmd: "BLACKLIST [<BSSID>]" */ + /* cmd: "BSSID_IGNORE [<BSSID>]" */ if (*cmd == '\0') { pos = buf; end = buf + buflen; - e = wpa_s->blacklist; + e = wpa_s->bssid_ignore; while (e) { ret = os_snprintf(pos, end - pos, MACSTR "\n", MAC2STR(e->bssid)); @@ -2570,12 +2581,12 @@ static int wpa_supplicant_ctrl_iface_blacklist(struct wpa_supplicant *wpa_s, cmd++; if (os_strncmp(cmd, "clear", 5) == 0) { - wpa_blacklist_clear(wpa_s); + wpa_bssid_ignore_clear(wpa_s); os_memcpy(buf, "OK\n", 3); return 3; } - wpa_printf(MSG_DEBUG, "CTRL_IFACE: BLACKLIST bssid='%s'", cmd); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: BSSID_IGNORE bssid='%s'", cmd); if (hwaddr_aton(cmd, bssid)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: invalid BSSID '%s'", cmd); return -1; @@ -2585,10 +2596,10 @@ static int wpa_supplicant_ctrl_iface_blacklist(struct wpa_supplicant *wpa_s, * Add the BSSID twice, so its count will be 2, causing it to be * skipped when processing scan results. */ - ret = wpa_blacklist_add(wpa_s, bssid); + ret = wpa_bssid_ignore_add(wpa_s, bssid); if (ret < 0) return -1; - ret = wpa_blacklist_add(wpa_s, bssid); + ret = wpa_bssid_ignore_add(wpa_s, bssid); if (ret < 0) return -1; os_memcpy(buf, "OK\n", 3); @@ -4488,6 +4499,15 @@ static int ctrl_iface_get_capability_auth_alg(struct wpa_supplicant *wpa_s, #endif /* CONFIG_FILS_SK_PFS */ #endif /* CONFIG_FILS */ +#ifdef CONFIG_PASN + ret = os_snprintf(pos, end - pos, "%sPASN", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + +#endif /* CONFIG_PASN */ + return pos - buf; } @@ -4813,6 +4833,31 @@ static int wpa_supplicant_ctrl_iface_get_capability( } #endif /* CONFIG_SAE */ +#ifdef CONFIG_OCV + if (os_strcmp(field, "ocv") == 0) { + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) || + (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_OCV)) + res = os_snprintf(buf, buflen, "supported"); + else + res = os_snprintf(buf, buflen, "not supported"); + if (os_snprintf_error(buflen, res)) + return -1; + return res; + } +#endif /* CONFIG_OCV */ + + if (os_strcmp(field, "beacon_prot") == 0) { + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_BEACON_PROTECTION) || + (wpa_s->drv_flags2 & + WPA_DRIVER_FLAGS2_BEACON_PROTECTION_CLIENT)) + res = os_snprintf(buf, buflen, "supported"); + else + res = os_snprintf(buf, buflen, "not supported"); + if (os_snprintf_error(buflen, res)) + return -1; + return res; + } + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'", field); @@ -5285,7 +5330,7 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, os_snprintf(title, sizeof(title), "anqp[%u]", elem->infoid); pos = anqp_add_hex(pos, end, title, elem->payload); - if (elem->protected) { + if (elem->protected_response) { ret = os_snprintf(pos, end - pos, "protected-anqp-info[%u]=1\n", elem->infoid); @@ -8414,13 +8459,14 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) wpa_s->consecutive_conn_failures = 0; wpa_drv_radio_disable(wpa_s, 0); - wpa_blacklist_clear(wpa_s); + wpa_bssid_ignore_clear(wpa_s); wpa_supplicant_ctrl_iface_remove_network(wpa_s, "all"); wpa_supplicant_ctrl_iface_remove_cred(wpa_s, "all"); wpa_config_flush_blobs(wpa_s->conf); wpa_s->conf->auto_interworking = 0; wpa_s->conf->okc = 0; + ptksa_cache_flush(wpa_s->ptksa, NULL, WPA_CIPHER_NONE); wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL); rsn_preauth_deinit(wpa_s->wpa); @@ -8521,6 +8567,13 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) free_bss_tmp_disallowed(wpa_s); os_memset(&wpa_s->robust_av, 0, sizeof(struct robust_av_data)); + +#ifdef CONFIG_PASN + wpas_pasn_auth_stop(wpa_s); +#endif /* CONFIG_PASN */ + + if (wpa_s->mac_addr_changed && wpa_s->conf->mac_addr == 0) + wpas_restore_permanent_mac_addr(wpa_s); } @@ -9774,8 +9827,7 @@ static int wpas_ctrl_vendor_elem_add(struct wpa_supplicant *wpa_s, char *cmd) if (wpa_s->vendor_elem[frame] == NULL) { wpa_s->vendor_elem[frame] = buf; - wpas_vendor_elem_update(wpa_s); - return 0; + goto update_ies; } if (wpabuf_resize(&wpa_s->vendor_elem[frame], len) < 0) { @@ -9785,8 +9837,14 @@ static int wpas_ctrl_vendor_elem_add(struct wpa_supplicant *wpa_s, char *cmd) wpabuf_put_buf(wpa_s->vendor_elem[frame], buf); wpabuf_free(buf); + +update_ies: wpas_vendor_elem_update(wpa_s); + if (frame == VENDOR_ELEM_PROBE_REQ || + frame == VENDOR_ELEM_PROBE_REQ_P2P) + wpa_supplicant_set_default_scan_ies(wpa_s); + return 0; } @@ -10093,6 +10151,7 @@ static int wpas_ctrl_iface_pmksa(struct wpa_supplicant *wpa_s, static void wpas_ctrl_iface_pmksa_flush(struct wpa_supplicant *wpa_s) { + ptksa_cache_flush(wpa_s->ptksa, NULL, WPA_CIPHER_NONE); wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL); #ifdef CONFIG_AP wpas_ap_pmksa_cache_flush(wpa_s); @@ -10425,6 +10484,73 @@ static int wpas_ctrl_iface_configure_mscs(struct wpa_supplicant *wpa_s, } +#ifdef CONFIG_PASN +static int wpas_ctrl_iface_pasn_start(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *token, *context = NULL; + u8 bssid[ETH_ALEN]; + int akmp = -1, cipher = -1, got_bssid = 0; + u16 group = 0xFFFF; + int id = 0; + + /* + * Entry format: bssid=<BSSID> akmp=<AKMP> cipher=<CIPHER> group=<group> + */ + while ((token = str_token(cmd, " ", &context))) { + if (os_strncmp(token, "bssid=", 6) == 0) { + if (hwaddr_aton(token + 6, bssid)) + return -1; + got_bssid = 1; + } else if (os_strcmp(token, "akmp=PASN") == 0) { + akmp = WPA_KEY_MGMT_PASN; +#ifdef CONFIG_IEEE80211R + } else if (os_strcmp(token, "akmp=FT-PSK") == 0) { + akmp = WPA_KEY_MGMT_FT_PSK; + } else if (os_strcmp(token, "akmp=FT-EAP-SHA384") == 0) { + akmp = WPA_KEY_MGMT_FT_IEEE8021X_SHA384; + } else if (os_strcmp(token, "akmp=FT-EAP") == 0) { + akmp = WPA_KEY_MGMT_FT_IEEE8021X; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_SAE + } else if (os_strcmp(token, "akmp=SAE") == 0) { + akmp = WPA_KEY_MGMT_SAE; +#endif /* CONFIG_SAE */ +#ifdef CONFIG_FILS + } else if (os_strcmp(token, "akmp=FILS-SHA256") == 0) { + akmp = WPA_KEY_MGMT_FILS_SHA256; + } else if (os_strcmp(token, "akmp=FILS-SHA384") == 0) { + akmp = WPA_KEY_MGMT_FILS_SHA384; +#endif /* CONFIG_FILS */ + } else if (os_strcmp(token, "cipher=CCMP-256") == 0) { + cipher = WPA_CIPHER_CCMP_256; + } else if (os_strcmp(token, "cipher=GCMP-256") == 0) { + cipher = WPA_CIPHER_GCMP_256; |