Merge "Do AIDL initialization after daemonizing the process" into udc-dev
diff --git a/hostapd/Android.bp b/hostapd/Android.bp
index 0c5d96b..e4b3718 100644
--- a/hostapd/Android.bp
+++ b/hostapd/Android.bp
@@ -330,6 +330,8 @@
"src/utils/json.c",
"src/ap/wmm.c",
"src/ap/ap_list.c",
+ "src/ap/comeback_token.c",
+ "src/pasn/pasn_responder.c",
"src/ap/ieee802_11.c",
"src/ap/hw_features.c",
"src/ap/dfs.c",
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index 14a7464..7a1b612 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -1056,6 +1056,8 @@
ifdef NEED_AP_MLME
OBJS += src/ap/wmm.c
OBJS += src/ap/ap_list.c
+OBJS += src/ap/comeback_token.c
+OBJS += src/pasn/pasn_responder.c
OBJS += src/ap/ieee802_11.c
OBJS += src/ap/hw_features.c
OBJS += src/ap/dfs.c
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 88a2e18..a2adc85 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -1186,6 +1186,8 @@
ifdef NEED_AP_MLME
OBJS += ../src/ap/wmm.o
OBJS += ../src/ap/ap_list.o
+OBJS += ../src/ap/comeback_token.o
+OBJS += ../src/pasn/pasn_responder.o
OBJS += ../src/ap/ieee802_11.o
OBJS += ../src/ap/hw_features.o
OBJS += ../src/ap/dfs.o
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 24fd297..d41a71a 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -2418,6 +2418,9 @@
bss->ap_max_inactivity = atoi(pos);
} else if (os_strcmp(buf, "skip_inactivity_poll") == 0) {
bss->skip_inactivity_poll = atoi(pos);
+ } else if (os_strcmp(buf, "config_id") == 0) {
+ os_free(bss->config_id);
+ bss->config_id = os_strdup(pos);
} else if (os_strcmp(buf, "country_code") == 0) {
if (pos[0] < 'A' || pos[0] > 'Z' ||
pos[1] < 'A' || pos[1] > 'Z') {
@@ -2582,6 +2585,8 @@
bss->eap_teap_separate_result = atoi(pos);
} else if (os_strcmp(buf, "eap_teap_id") == 0) {
bss->eap_teap_id = atoi(pos);
+ } else if (os_strcmp(buf, "eap_teap_method_sequence") == 0) {
+ bss->eap_teap_method_sequence = atoi(pos);
#endif /* EAP_SERVER_TEAP */
#ifdef EAP_SERVER_SIM
} else if (os_strcmp(buf, "eap_sim_db") == 0) {
@@ -3476,6 +3481,8 @@
#ifdef CONFIG_IEEE80211AX
} else if (os_strcmp(buf, "ieee80211ax") == 0) {
conf->ieee80211ax = atoi(pos);
+ } else if (os_strcmp(buf, "require_he") == 0) {
+ conf->require_he = atoi(pos);
} else if (os_strcmp(buf, "he_su_beamformer") == 0) {
conf->he_phy_capab.he_su_beamformer = atoi(pos);
} else if (os_strcmp(buf, "he_su_beamformee") == 0) {
@@ -3633,6 +3640,15 @@
return 1;
}
bss->unsol_bcast_probe_resp_interval = val;
+ } else if (os_strcmp(buf, "mbssid") == 0) {
+ int mbssid = atoi(pos);
+ if (mbssid < 0 || mbssid > ENHANCED_MBSSID_ENABLED) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid mbssid (%d): '%s'.",
+ line, mbssid, pos);
+ return 1;
+ }
+ conf->mbssid = mbssid;
#endif /* CONFIG_IEEE80211AX */
} else if (os_strcmp(buf, "max_listen_interval") == 0) {
bss->max_listen_interval = atoi(pos);
@@ -4270,6 +4286,8 @@
conf->enable_eapol_large_timeout = atoi(pos);
} else if (os_strcmp(buf, "eap_skip_prot_success") == 0) {
bss->eap_skip_prot_success = atoi(pos);
+ } else if (os_strcmp(buf, "delay_eapol_tx") == 0) {
+ conf->delay_eapol_tx = atoi(pos);
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_SAE
} else if (os_strcmp(buf, "sae_password") == 0) {
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index 7fb3fd3..94f31ee 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -983,6 +983,14 @@
return pos - buf;
pos += ret;
+ if ((hapd->conf->config_id)) {
+ ret = os_snprintf(pos, end - pos, "config_id=%s\n",
+ hapd->conf->config_id);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
#ifdef CONFIG_WPS
ret = os_snprintf(pos, end - pos, "wps_state=%s\n",
hapd->conf->wps_state == 0 ? "disabled" :
@@ -1380,6 +1388,16 @@
}
+static int hostapd_ctrl_iface_reload_bss(struct hostapd_data *bss)
+{
+ if (hostapd_reload_bss_only(bss) < 0) {
+ wpa_printf(MSG_ERROR, "Reloading of BSS failed");
+ return -1;
+ }
+ return 0;
+}
+
+
static int hostapd_ctrl_iface_disable(struct hostapd_iface *iface)
{
if (hostapd_disable_iface(iface) < 0) {
@@ -3199,6 +3217,8 @@
} else if (os_strncmp(buf, "RELOG", 5) == 0) {
if (wpa_debug_reopen_file() < 0)
reply_len = -1;
+ } else if (os_strcmp(buf, "CLOSE_LOG") == 0) {
+ wpa_debug_stop_log();
} else if (os_strncmp(buf, "NOTE ", 5) == 0) {
wpa_printf(MSG_INFO, "NOTE: %s", buf + 5);
} else if (os_strcmp(buf, "STATUS") == 0) {
@@ -3370,6 +3390,9 @@
} else if (os_strcmp(buf, "RELOAD_WPA_PSK") == 0) {
if (hostapd_ctrl_iface_reload_wpa_psk(hapd))
reply_len = -1;
+ } else if (os_strcmp(buf, "RELOAD_BSS") == 0) {
+ if (hostapd_ctrl_iface_reload_bss(hapd))
+ reply_len = -1;
} else if (os_strncmp(buf, "RELOAD", 6) == 0) {
if (hostapd_ctrl_iface_reload(hapd->iface))
reply_len = -1;
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index ea67aa1..c5e74a6 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -812,6 +812,9 @@
# 1 = enabled
#ieee80211ax=1
+# Require stations to support HE PHY (reject association if they do not)
+#require_he=1
+
# disable_11ax: Boolean (0/1) to disable HE for a specific BSS
#disable_11ax=0
@@ -1412,6 +1415,12 @@
# 5 = require both user and machine identity
#eap_teap_id=0
+# EAP-TEAP tunneled EAP method behavior
+# 0 = minimize roundtrips by merging start of the next EAP method with the
+# crypto-binding of the previous one.
+# 1 = complete crypto-binding before starting the next EAP method
+#eap_teap_method_sequence=0
+
# EAP-SIM and EAP-AKA protected success/failure indication using AT_RESULT_IND
# (default: 0 = disabled).
#eap_sim_aka_result_ind=1
@@ -2799,7 +2808,12 @@
# If the RADIUS server indicates that the station is not allowed to connect to
# the BSS/ESS, the AP can allow the station some time to download a
# notification page (URL included in the message). This parameter sets that
-# timeout in seconds.
+# timeout in seconds. If the RADIUS server provides no URL, this value is
+# reduced to two seconds with an additional trigger for immediate
+# deauthentication when the STA acknowledges reception of the deauthentication
+# imminent indication. Note that setting this value to 0 will prevent delivery
+# of the notification to the STA, so a value of at least 1 should be used here
+# for normal use cases.
#hs20_deauth_req_timeout=60
# Operator Friendly Name
@@ -3075,6 +3089,11 @@
# Include only ECSA IE without CSA IE where possible
# (channel switch operating class is needed)
#ecsa_ie_only=0
+#
+# Delay EAPOL-Key messages 1/4 and 3/4 by not sending the frame until the last
+# attempt (wpa_pairwise_update_count). This will trigger a timeout on all
+# previous attempts and thus delays the frame. (testing only)
+#delay_eapol_tx=0
##### Multiple BSSID support ##################################################
#
@@ -3118,3 +3137,63 @@
#bss=wlan0_1
#bssid=00:13:10:95:fe:0b
# ...
+#
+# Multiple BSSID Advertisement in IEEE 802.11ax
+# IEEE Std 802.11ax-2021 added a feature where instead of multiple interfaces
+# on a common radio transmitting individual Beacon frames, those interfaces can
+# form a set with a common Beacon frame transmitted for all. The interface
+# which is brought up first is called the transmitting profile of the MBSSID
+# set which transmits the Beacon frames. The remaining interfaces are called
+# the non-transmitting profiles and these are advertised inside the Multiple
+# BSSID element in the Beacon and Probe Response frames from the first
+# interface.
+#
+# The transmitting interface is visible to all stations in the vicinity, however
+# the stations that do not support parsing of the Multiple BSSID element will
+# not be able to connect to the non-transmitting interfaces.
+#
+# Enhanced Multiple BSSID Advertisements (EMA)
+# When enabled, the non-transmitting interfaces are split into multiple
+# Beacon frames. The number of Beacon frames required to cover all the
+# non-transmitting profiles is called the profile periodicity.
+#
+# Refer to IEEE Std 802.11-2020 for details regarding the procedure and
+# required MAC address assignment.
+#
+# Following configuration is per radio.
+# 0 = Disabled (default)
+# 1 = Multiple BSSID advertisement enabled.
+# 2 = Enhanced multiple BSSID advertisement enabled.
+#mbssid=0
+#
+# The transmitting interface should be added with the 'interface' option while
+# the non-transmitting interfaces should be added using the 'bss' option.
+# Security configuration should be added separately per interface, if required.
+#
+# Example:
+#mbssid=2
+#interface=wlan2
+#ctrl_interface=/var/run/hostapd
+#wpa_passphrase=0123456789
+#ieee80211w=2
+#sae_pwe=1
+#auth_algs=1
+#wpa=2
+#wpa_pairwise=CCMP
+#ssid=<SSID-0>
+#bridge=br-lan
+#wpa_key_mgmt=SAE
+#bssid=00:03:7f:12:84:84
+#
+#bss=wlan2-1
+#ctrl_interface=/var/run/hostapd
+#wpa_passphrase=0123456789
+#ieee80211w=2
+#sae_pwe=1
+#auth_algs=1
+#wpa=2
+#wpa_pairwise=CCMP
+#ssid=<SSID-1>
+#bridge=br-lan
+#wpa_key_mgmt=SAE
+#bssid=00:03:7f:12:84:85
diff --git a/hostapd/hostapd.eap_user b/hostapd/hostapd.eap_user
index 00edc95..61ef937 100644
--- a/hostapd/hostapd.eap_user
+++ b/hostapd/hostapd.eap_user
@@ -52,8 +52,8 @@
# Arbitrary RADIUS attributes can be added into Access-Accept packets similarly
# to the way radius_auth_req_attr is used for Access-Request packet in
# hostapd.conf. For EAP server, this is configured separately for each user
-# entry with radius_accept_attr=<value> line(s) following the main user entry
-# line.
+# entry with radius_accept_attr=<attr_id>[:<syntax:value>] line(s) following
+# the main user entry line.
# Phase 1 users
"user" MD5 "password"
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index 6376b78..ec40dda 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -252,6 +252,13 @@
}
+static int hostapd_cli_cmd_close_log(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "CLOSE_LOG");
+}
+
+
static int hostapd_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
if (argc > 0 && os_strcmp(argv[0], "driver") == 0)
@@ -1208,6 +1215,13 @@
}
+static int hostapd_cli_cmd_reload_bss(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "RELOAD_BSS");
+}
+
+
static int hostapd_cli_cmd_disable(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
@@ -1572,6 +1586,8 @@
"= get MIB variables (dot1x, dot11, radius)" },
{ "relog", hostapd_cli_cmd_relog, NULL,
"= reload/truncate debug log output file" },
+ { "close_log", hostapd_cli_cmd_close_log, NULL,
+ "= disable debug log output file" },
{ "status", hostapd_cli_cmd_status, NULL,
"= show interface status info" },
{ "sta", hostapd_cli_cmd_sta, hostapd_complete_stations,
@@ -1670,6 +1686,8 @@
"= enable hostapd on current interface" },
{ "reload", hostapd_cli_cmd_reload, NULL,
"= reload configuration for current interface" },
+ { "reload_bss", hostapd_cli_cmd_reload_bss, NULL,
+ "= reload configuration for current BSS" },
{ "disable", hostapd_cli_cmd_disable, NULL,
"= disable hostapd on current interface" },
{ "update_beacon", hostapd_cli_cmd_update_beacon, NULL,
diff --git a/hostapd/main.c b/hostapd/main.c
index 772ba74..18b2dd9 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -243,6 +243,9 @@
wpa_printf(MSG_ERROR, "set_wowlan failed");
}
os_free(triggs);
+
+ iface->mbssid_max_interfaces = capa.mbssid_max_interfaces;
+ iface->ema_max_periodicity = capa.ema_max_periodicity;
}
return 0;
diff --git a/hs20/client/est.c b/hs20/client/est.c
index c3f27e1..5c6e2f6 100644
--- a/hs20/client/est.c
+++ b/hs20/client/est.c
@@ -218,7 +218,9 @@
} d;
} AttrOrOID;
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
DEFINE_STACK_OF(AttrOrOID)
+#endif
typedef struct {
int type;
diff --git a/hs20/client/osu_client.c b/hs20/client/osu_client.c
index 01e7b75..2ca85f9 100644
--- a/hs20/client/osu_client.c
+++ b/hs20/client/osu_client.c
@@ -1231,12 +1231,13 @@
homeoi, required);
if (required) {
- if (set_cred(ctx->ifname, id, "required_roaming_consortium",
- homeoi) < 0)
- wpa_printf(MSG_INFO, "Failed to set cred required_roaming_consortium");
+ if (set_cred_quoted(ctx->ifname, id, "required_home_ois",
+ homeoi) < 0)
+ wpa_printf(MSG_INFO,
+ "Failed to set cred required_home_ois");
} else {
- if (set_cred(ctx->ifname, id, "roaming_consortium", homeoi) < 0)
- wpa_printf(MSG_INFO, "Failed to set cred roaming_consortium");
+ if (set_cred_quoted(ctx->ifname, id, "home_ois", homeoi) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred home_ois");
}
xml_node_get_text_free(ctx->xml, homeoi);
diff --git a/src/Makefile b/src/Makefile
index 6eb7f2a..d15cf32 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,4 +1,4 @@
-SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p pae radius rsn_supp tls utils wps
+SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p pae pasn radius rsn_supp tls utils wps
SUBDIRS += fst
all:
diff --git a/src/ap/acs.c b/src/ap/acs.c
index 8ee2e04..8cb5813 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -241,6 +241,57 @@
* [1] http://en.wikipedia.org/wiki/Near_and_far_field
*/
+enum bw_type {
+ ACS_BW40,
+ ACS_BW80,
+ ACS_BW160,
+};
+
+struct bw_item {
+ int first;
+ int last;
+ int center_chan;
+};
+
+static const struct bw_item bw_40[] = {
+ { 5180, 5200, 38 }, { 5220, 5240, 46 }, { 5260, 5280, 54 },
+ { 5300, 5320, 62 }, { 5500, 5520, 102 }, { 5540, 5560, 110 },
+ { 5580, 5600, 110 }, { 5620, 5640, 126}, { 5660, 5680, 134 },
+ { 5700, 5720, 142 }, { 5745, 5765, 151 }, { 5785, 5805, 159 },
+ { 5825, 5845, 167 }, { 5865, 5885, 175 },
+ { 5955, 5975, 3 }, { 5995, 6015, 11 }, { 6035, 6055, 19 },
+ { 6075, 6095, 27 }, { 6115, 6135, 35 }, { 6155, 6175, 43 },
+ { 6195, 6215, 51 }, { 6235, 6255, 59 }, { 6275, 6295, 67 },
+ { 6315, 6335, 75 }, { 6355, 6375, 83 }, { 6395, 6415, 91 },
+ { 6435, 6455, 99 }, { 6475, 6495, 107 }, { 6515, 6535, 115 },
+ { 6555, 6575, 123 }, { 6595, 6615, 131 }, { 6635, 6655, 139 },
+ { 6675, 6695, 147 }, { 6715, 6735, 155 }, { 6755, 6775, 163 },
+ { 6795, 6815, 171 }, { 6835, 6855, 179 }, { 6875, 6895, 187 },
+ { 6915, 6935, 195 }, { 6955, 6975, 203 }, { 6995, 7015, 211 },
+ { 7035, 7055, 219 }, { 7075, 7095, 227}, { -1, -1, -1 }
+};
+static const struct bw_item bw_80[] = {
+ { 5180, 5240, 42 }, { 5260, 5320, 58 }, { 5500, 5560, 106 },
+ { 5580, 5640, 122 }, { 5660, 5720, 138 }, { 5745, 5805, 155 },
+ { 5825, 5885, 171},
+ { 5955, 6015, 7 }, { 6035, 6095, 23 }, { 6115, 6175, 39 },
+ { 6195, 6255, 55 }, { 6275, 6335, 71 }, { 6355, 6415, 87 },
+ { 6435, 6495, 103 }, { 6515, 6575, 119 }, { 6595, 6655, 135 },
+ { 6675, 6735, 151 }, { 6755, 6815, 167 }, { 6835, 6895, 183 },
+ { 6915, 6975, 199 }, { 6995, 7055, 215 }, { -1, -1, -1 }
+};
+static const struct bw_item bw_160[] = {
+ { 5180, 5320, 50 }, { 5500, 5640, 114 }, { 5745, 5885, 163 },
+ { 5955, 6095, 15 }, { 6115, 6255, 47 }, { 6275, 6415, 79 },
+ { 6435, 6575, 111 }, { 6595, 6735, 143 },
+ { 6755, 6895, 175 }, { 6915, 7055, 207 }, { -1, -1, -1 }
+};
+static const struct bw_item *bw_desc[] = {
+ [ACS_BW40] = bw_40,
+ [ACS_BW80] = bw_80,
+ [ACS_BW160] = bw_160,
+};
+
static int acs_request_scan(struct hostapd_iface *iface);
static int acs_survey_is_sufficient(struct freq_survey *survey);
@@ -370,48 +421,31 @@
}
-static int acs_usable_bw40_chan(const struct hostapd_channel_data *chan)
+static bool acs_usable_bw_chan(const struct hostapd_channel_data *chan,
+ enum bw_type bw)
{
- const int allowed[] = { 5180, 5220, 5260, 5300, 5500, 5540, 5580, 5620,
- 5660, 5745, 5785, 4920, 4960, 5955, 5995, 6035,
- 6075, 6115, 6155, 6195, 6235, 6275, 6315, 6355,
- 6395, 6435, 6475, 6515, 6555, 6595, 6635, 6675,
- 6715, 6755, 6795, 6835, 6875, 6915, 6955, 6995,
- 7035, 7075 };
- unsigned int i;
+ unsigned int i = 0;
- for (i = 0; i < ARRAY_SIZE(allowed); i++)
- if (chan->freq == allowed[i])
- return 1;
+ while (bw_desc[bw][i].first != -1) {
+ if (chan->freq == bw_desc[bw][i].first)
+ return true;
+ i++;
+ }
- return 0;
+ return false;
}
-static int acs_usable_bw80_chan(const struct hostapd_channel_data *chan)
+static int acs_get_bw_center_chan(int freq, enum bw_type bw)
{
- const int allowed[] = { 5180, 5260, 5500, 5580, 5660, 5745, 5955, 6035,
- 6115, 6195, 6275, 6355, 6435, 6515, 6595, 6675,
- 6755, 6835, 6915, 6995 };
- unsigned int i;
+ unsigned int i = 0;
- for (i = 0; i < ARRAY_SIZE(allowed); i++)
- if (chan->freq == allowed[i])
- return 1;
-
- return 0;
-}
-
-
-static int acs_usable_bw160_chan(const struct hostapd_channel_data *chan)
-{
- const int allowed[] = { 5180, 5500, 5955, 6115, 6275, 6435, 6595, 6755,
- 6915 };
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(allowed); i++)
- if (chan->freq == allowed[i])
- return 1;
+ while (bw_desc[bw][i].first != -1) {
+ if (freq >= bw_desc[bw][i].first &&
+ freq <= bw_desc[bw][i].last)
+ return bw_desc[bw][i].center_chan;
+ i++;
+ }
return 0;
}
@@ -607,6 +641,26 @@
}
+static enum hostapd_hw_mode
+acs_find_mode(struct hostapd_iface *iface, int freq)
+{
+ int i;
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *chan;
+
+ for (i = 0; i < iface->num_hw_features; i++) {
+ mode = &iface->hw_features[i];
+ if (!hostapd_hw_skip_mode(iface, mode)) {
+ chan = acs_find_chan_mode(mode, freq);
+ if (chan)
+ return mode->mode;
+ }
+ }
+
+ return HOSTAPD_MODE_IEEE80211ANY;
+}
+
+
static struct hostapd_channel_data *
acs_find_chan(struct hostapd_iface *iface, int freq)
{
@@ -665,7 +719,7 @@
struct hostapd_channel_data **ideal_chan,
long double *ideal_factor)
{
- struct hostapd_channel_data *chan, *adj_chan = NULL;
+ struct hostapd_channel_data *chan, *adj_chan = NULL, *best;
long double factor;
int i, j;
unsigned int k;
@@ -673,8 +727,9 @@
for (i = 0; i < mode->num_channels; i++) {
double total_weight;
struct acs_bias *bias, tmp_bias;
+ bool update_best = true;
- chan = &mode->channels[i];
+ best = chan = &mode->channels[i];
/* Since in the current ACS implementation the first channel is
* always a primary channel, skip channels not available as
@@ -713,7 +768,7 @@
((iface->conf->ieee80211n &&
iface->conf->secondary_channel) ||
is_6ghz_freq(chan->freq)) &&
- !acs_usable_bw40_chan(chan)) {
+ !acs_usable_bw_chan(chan, ACS_BW40)) {
wpa_printf(MSG_DEBUG,
"ACS: Channel %d: not allowed as primary channel for 40 MHz bandwidth",
chan->chan);
@@ -724,7 +779,7 @@
(iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
if (hostapd_get_oper_chwidth(iface->conf) ==
CONF_OPER_CHWIDTH_80MHZ &&
- !acs_usable_bw80_chan(chan)) {
+ !acs_usable_bw_chan(chan, ACS_BW80)) {
wpa_printf(MSG_DEBUG,
"ACS: Channel %d: not allowed as primary channel for 80 MHz bandwidth",
chan->chan);
@@ -733,7 +788,7 @@
if (hostapd_get_oper_chwidth(iface->conf) ==
CONF_OPER_CHWIDTH_160MHZ &&
- !acs_usable_bw160_chan(chan)) {
+ !acs_usable_bw_chan(chan, ACS_BW160)) {
wpa_printf(MSG_DEBUG,
"ACS: Channel %d: not allowed as primary channel for 160 MHz bandwidth",
chan->chan);
@@ -761,7 +816,15 @@
if (acs_usable_chan(adj_chan)) {
factor += adj_chan->interference_factor;
total_weight += 1;
+ } else {
+ update_best = false;
}
+
+ /* find the best channel in this segment */
+ if (update_best &&
+ adj_chan->interference_factor <
+ best->interference_factor)
+ best = adj_chan;
}
if (j != n_chans) {
@@ -770,6 +833,18 @@
continue;
}
+ /* If the AP is in the 5 GHz or 6 GHz band, lets prefer a less
+ * crowded primary channel if one was found in the segment */
+ if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+ chan != best) {
+ wpa_printf(MSG_DEBUG,
+ "ACS: promoting channel %d over %d (less interference %Lg/%Lg)",
+ best->chan, chan->chan,
+ chan->interference_factor,
+ best->interference_factor);
+ chan = best;
+ }
+
/* 2.4 GHz has overlapping 20 MHz channels. Include adjacent
* channel interference factor. */
if (is_24ghz_mode(mode->mode)) {
@@ -923,21 +998,50 @@
}
+static void acs_adjust_secondary(struct hostapd_iface *iface)
+{
+ unsigned int i;
+
+ /* When working with bandwidth over 20 MHz on the 5 GHz or 6 GHz band,
+ * ACS can return a secondary channel which is not the first channel of
+ * the segment and we need to adjust. */
+ if (!iface->conf->secondary_channel ||
+ acs_find_mode(iface, iface->freq) != HOSTAPD_MODE_IEEE80211A)
+ return;
+
+ wpa_printf(MSG_DEBUG, "ACS: Adjusting HT/VHT/HE secondary frequency");
+
+ for (i = 0; bw_desc[ACS_BW40][i].first != -1; i++) {
+ if (iface->freq == bw_desc[ACS_BW40][i].first)
+ iface->conf->secondary_channel = 1;
+ else if (iface->freq == bw_desc[ACS_BW40][i].last)
+ iface->conf->secondary_channel = -1;
+ }
+}
+
+
static void acs_adjust_center_freq(struct hostapd_iface *iface)
{
- int offset;
+ int center;
wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
switch (hostapd_get_oper_chwidth(iface->conf)) {
case CONF_OPER_CHWIDTH_USE_HT:
- offset = 2 * iface->conf->secondary_channel;
+ if (iface->conf->secondary_channel &&
+ iface->freq >= 2400 && iface->freq < 2500)
+ center = iface->conf->channel +
+ 2 * iface->conf->secondary_channel;
+ else if (iface->conf->secondary_channel)
+ center = acs_get_bw_center_chan(iface->freq, ACS_BW40);
+ else
+ center = iface->conf->channel;
break;
case CONF_OPER_CHWIDTH_80MHZ:
- offset = 6;
+ center = acs_get_bw_center_chan(iface->freq, ACS_BW80);
break;
case CONF_OPER_CHWIDTH_160MHZ:
- offset = 14;
+ center = acs_get_bw_center_chan(iface->freq, ACS_BW160);
break;
default:
/* TODO: How can this be calculated? Adjust
@@ -947,8 +1051,7 @@
return;
}
- hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
- iface->conf->channel + offset);
+ hostapd_set_oper_centr_freq_seg0_idx(iface->conf, center);
}
@@ -1004,8 +1107,10 @@
iface->conf->channel = ideal_chan->chan;
iface->freq = ideal_chan->freq;
- if (iface->conf->ieee80211ac || iface->conf->ieee80211ax)
+ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+ acs_adjust_secondary(iface);
acs_adjust_center_freq(iface);
+ }
err = hostapd_select_hw_mode(iface);
if (err) {
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index b73aae2..5dc8a8f 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -481,10 +481,11 @@
struct hostapd_ssid *ssid = &conf->ssid;
struct sae_password_entry *pw;
- if ((conf->sae_pwe == 0 && !hostapd_sae_pw_id_in_use(conf) &&
+ if ((conf->sae_pwe == SAE_PWE_HUNT_AND_PECK &&
+ !hostapd_sae_pw_id_in_use(conf) &&
!wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt) &&
!hostapd_sae_pk_in_use(conf)) ||
- conf->sae_pwe == 3 ||
+ conf->sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK ||
!wpa_key_mgmt_sae(conf->wpa_key_mgmt))
return 0; /* PT not needed */
@@ -798,6 +799,7 @@
os_free(conf->radius_req_attr_sqlite);
os_free(conf->rsn_preauth_interfaces);
os_free(conf->ctrl_interface);
+ os_free(conf->config_id);
os_free(conf->ca_cert);
os_free(conf->server_cert);
os_free(conf->server_cert2);
@@ -1211,6 +1213,14 @@
return false;
}
+#ifdef CONFIG_SAE
+ if (wpa_key_mgmt_sae(bss->wpa_key_mgmt) &&
+ bss->sae_pwe == SAE_PWE_HUNT_AND_PECK) {
+ wpa_printf(MSG_INFO, "SAE: Enabling SAE H2E on 6 GHz");
+ bss->sae_pwe = SAE_PWE_BOTH;
+ }
+#endif /* CONFIG_SAE */
+
return true;
}
@@ -1452,6 +1462,12 @@
}
#endif /* CONFIG_IEEE80211BE */
+ if (full_config && bss->ignore_broadcast_ssid && conf->mbssid) {
+ wpa_printf(MSG_ERROR,
+ "Hidden SSID is not suppored when MBSSID is enabled");
+ return -1;
+ }
+
return 0;
}
@@ -1535,6 +1551,12 @@
}
#endif /* CONFIG_IEEE80211BE */
+ if (full_config && conf->mbssid && !conf->ieee80211ax) {
+ wpa_printf(MSG_ERROR,
+ "Cannot enable multiple BSSID support without ieee80211ax");
+ return -1;
+ }
+
for (i = 0; i < conf->num_bss; i++) {
if (hostapd_config_check_bss(conf->bss[i], conf, full_config))
return -1;
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 268829a..6dbb223 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -443,6 +443,7 @@
int eap_teap_pac_no_inner;
int eap_teap_separate_result;
int eap_teap_id;
+ int eap_teap_method_sequence;
int eap_sim_aka_result_ind;
int eap_sim_id;
char *imsi_privacy_key;
@@ -670,7 +671,7 @@
unsigned int sae_sync;
int sae_require_mfp;
int sae_confirm_immediate;
- int sae_pwe;
+ enum sae_pwe sae_pwe;
int *sae_groups;
struct sae_password_entry *sae_passwords;
@@ -916,6 +917,8 @@
u8 ext_capa[EXT_CAPA_MAX_LEN];
u8 rnr;
+ char *config_id;
+ bool xrates_supported;
};
/**
@@ -1076,6 +1079,7 @@
int ecsa_ie_only;
unsigned int skip_send_eapol;
unsigned int enable_eapol_large_timeout;
+ bool delay_eapol_tx;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_ACS
@@ -1105,6 +1109,7 @@
u8 he_6ghz_rx_ant_pat;
u8 he_6ghz_tx_ant_pat;
u8 he_6ghz_reg_pwr_type;
+ bool require_he;
#endif /* CONFIG_IEEE80211AX */
/* VHT enable/disable config from CHAN_SWITCH */
@@ -1144,6 +1149,12 @@
#define CH_SWITCH_EHT_ENABLED BIT(0)
#define CH_SWITCH_EHT_DISABLED BIT(1)
unsigned int ch_switch_eht_config;
+
+ enum mbssid {
+ MBSSID_DISABLED = 0,
+ MBSSID_ENABLED = 1,
+ ENHANCED_MBSSID_ENABLED = 2,
+ } mbssid;
};
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index b9fd920..1ffc37f 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -92,7 +92,7 @@
goto fail;
pos = buf;
- pos = hostapd_eid_ext_capab(hapd, pos);
+ pos = hostapd_eid_ext_capab(hapd, pos, false);
if (add_buf_data(&assocresp, buf, pos - buf) < 0)
goto fail;
pos = hostapd_eid_interworking(hapd, pos);
@@ -724,6 +724,7 @@
params.key_len = key_len;
params.vlan_id = vlan_id;
params.key_flag = key_flag;
+ params.link_id = -1;
return hapd->driver->set_key(hapd->drv_priv, ¶ms);
}
@@ -905,8 +906,10 @@
chan->chan)))
continue;
if (is_6ghz_freq(chan->freq) &&
- hapd->iface->conf->acs_exclude_6ghz_non_psc &&
- !is_6ghz_psc_frequency(chan->freq))
+ ((hapd->iface->conf->acs_exclude_6ghz_non_psc &&
+ !is_6ghz_psc_frequency(chan->freq)) ||
+ (!hapd->iface->conf->ieee80211ax &&
+ !hapd->iface->conf->ieee80211be)))
continue;
if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
!(hapd->iface->conf->acs_exclude_dfs &&
diff --git a/src/ap/ap_mlme.c b/src/ap/ap_mlme.c
index db8a267..309e69a 100644
--- a/src/ap/ap_mlme.c
+++ b/src/ap/ap_mlme.c
@@ -29,9 +29,9 @@
return "SHARED_KEY";
case WLAN_AUTH_FT:
return "FT";
+ default:
+ return "unknown";
}
-
- return "unknown";
}
#endif /* CONFIG_NO_HOSTAPD_LOGGER */
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index fd9c96f..4ab2a4a 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -211,6 +211,7 @@
cfg->eap_teap_pac_no_inner = hapd->conf->eap_teap_pac_no_inner;
cfg->eap_teap_separate_result = hapd->conf->eap_teap_separate_result;
cfg->eap_teap_id = hapd->conf->eap_teap_id;
+ cfg->eap_teap_method_sequence = hapd->conf->eap_teap_method_sequence;
cfg->eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind;
cfg->eap_sim_id = hapd->conf->eap_sim_id;
cfg->imsi_privacy_key = hapd->imsi_privacy_key;
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 8676570..dbc6b06 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -462,15 +462,86 @@
}
+static int
+ieee802_11_build_ap_params_mbssid(struct hostapd_data *hapd,
+ struct wpa_driver_ap_params *params)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ struct hostapd_data *tx_bss;
+ size_t len;
+ u8 elem_count = 0, *elem = NULL, **elem_offset = NULL, *end;
+
+ if (!iface->mbssid_max_interfaces ||
+ iface->num_bss > iface->mbssid_max_interfaces ||
+ (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED &&
+ !iface->ema_max_periodicity))
+ goto fail;
+
+ tx_bss = hostapd_mbssid_get_tx_bss(hapd);
+ len = hostapd_eid_mbssid_len(tx_bss, WLAN_FC_STYPE_BEACON, &elem_count,
+ NULL, 0);
+ if (!len || (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED &&
+ elem_count > iface->ema_max_periodicity))
+ goto fail;
+
+ elem = os_zalloc(len);
+ if (!elem)
+ goto fail;
+
+ elem_offset = os_zalloc(elem_count * sizeof(u8 *));
+ if (!elem_offset)
+ goto fail;
+
+ end = hostapd_eid_mbssid(tx_bss, elem, elem + len, WLAN_FC_STYPE_BEACON,
+ elem_count, elem_offset, NULL, 0);
+
+ params->mbssid_tx_iface = tx_bss->conf->iface;
+ params->mbssid_index = hostapd_mbssid_get_bss_index(hapd);
+ params->mbssid_elem = elem;
+ params->mbssid_elem_len = end - elem;
+ params->mbssid_elem_count = elem_count;
+ params->mbssid_elem_offset = elem_offset;
+ if (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED)
+ params->ema = true;
+
+ return 0;
+
+fail:
+ os_free(elem);
+ wpa_printf(MSG_ERROR, "MBSSID: Configuration failed");
+ return -1;
+}
+
+
+static u8 * hostapd_eid_mbssid_config(struct hostapd_data *hapd, u8 *eid,
+ u8 mbssid_elem_count)
+{
+ struct hostapd_iface *iface = hapd->iface;
+
+ if (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED) {
+ *eid++ = WLAN_EID_EXTENSION;
+ *eid++ = 3;
+ *eid++ = WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION;
+ *eid++ = iface->num_bss;
+ *eid++ = mbssid_elem_count;
+ }
+
+ return eid;
+}
+
+
static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
const struct ieee80211_mgmt *req,
int is_p2p, size_t *resp_len,
- bool bcast_probe_resp)
+ bool bcast_probe_resp, const u8 *known_bss,
+ u8 known_bss_len)
{
struct ieee80211_mgmt *resp;
u8 *pos, *epos, *csa_pos;
size_t buflen;
+ hapd = hostapd_mbssid_get_tx_bss(hapd);
+
#define MAX_PROBERESP_LEN 768
buflen = MAX_PROBERESP_LEN;
#ifdef CONFIG_WPS
@@ -517,6 +588,8 @@
}
#endif /* CONFIG_IEEE80211BE */
+ buflen += hostapd_eid_mbssid_len(hapd, WLAN_FC_STYPE_PROBE_RESP, NULL,
+ known_bss, known_bss_len);
buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP);
buflen += hostapd_mbo_ie_len(hapd);
buflen += hostapd_eid_owe_trans_len(hapd);
@@ -576,6 +649,8 @@
pos = hostapd_get_rsne(hapd, pos, epos - pos);
pos = hostapd_eid_bss_load(hapd, pos, epos - pos);
+ pos = hostapd_eid_mbssid(hapd, pos, epos, WLAN_FC_STYPE_PROBE_RESP, 0,
+ NULL, known_bss, known_bss_len);
pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos);
pos = hostapd_get_mde(hapd, pos, epos - pos);
@@ -589,7 +664,11 @@
pos = hostapd_eid_ht_capabilities(hapd, pos);
pos = hostapd_eid_ht_operation(hapd, pos);
- pos = hostapd_eid_ext_capab(hapd, pos);
+ /* Probe Response frames always include all non-TX profiles except
+ * when a list of known BSSes is included in the Probe Request frame. */
+ pos = hostapd_eid_ext_capab(hapd, pos,
+ hapd->iconf->mbssid >= MBSSID_ENABLED &&
+ !known_bss_len);
pos = hostapd_eid_time_adv(hapd, pos);
pos = hostapd_eid_time_zone(hapd, pos);
@@ -1141,11 +1220,18 @@
}
#endif /* CONFIG_TESTING_OPTIONS */
+ /* Do not send Probe Response frame from a non-transmitting multiple
+ * BSSID profile unless the Probe Request frame is directed at that
+ * particular BSS. */
+ if (hapd != hostapd_mbssid_get_tx_bss(hapd) && res != EXACT_SSID_MATCH)
+ return;
+
wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, RX_PROBE_REQUEST "sa=" MACSTR
" signal=%d", MAC2STR(mgmt->sa), ssi_signal);
resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL,
- &resp_len, false);
+ &resp_len, false, elems.mbssid_known_bss,
+ elems.mbssid_known_bss_len);
if (resp == NULL)
return;
@@ -1167,7 +1253,8 @@
hapd->cs_c_off_ecsa_proberesp;
}
- ret = hostapd_drv_send_mlme(hapd, resp, resp_len, noack,
+ ret = hostapd_drv_send_mlme(hostapd_mbssid_get_tx_bss(hapd), resp,
+ resp_len, noack,
csa_offs_len ? csa_offs : NULL,
csa_offs_len, 0);
@@ -1214,7 +1301,7 @@
"this");
/* Generate a Probe Response template for the non-P2P case */
- return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len, false);
+ return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len, false, NULL, 0);
}
#endif /* NEED_AP_MLME */
@@ -1233,7 +1320,7 @@
return hostapd_gen_probe_resp(hapd, NULL, 0,
¶ms->unsol_bcast_probe_resp_tmpl_len,
- true);
+ true, NULL, 0);
}
#endif /* CONFIG_IEEE80211AX */
@@ -1514,7 +1601,12 @@
#ifdef NEED_AP_MLME
u16 capab_info;
u8 *pos, *tailpos, *tailend, *csa_pos;
+ bool complete = false;
+#endif /* NEED_AP_MLME */
+ os_memset(params, 0, sizeof(*params));
+
+#ifdef NEED_AP_MLME
#define BEACON_HEAD_BUF_SIZE 256
#define BEACON_TAIL_BUF_SIZE 512
head = os_zalloc(BEACON_HEAD_BUF_SIZE);
@@ -1566,6 +1658,9 @@
}
#endif /* CONFIG_IEEE80211BE */
+ if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
+ hapd == hostapd_mbssid_get_tx_bss(hapd))
+ tail_len += 5; /* Multiple BSSID Configuration element */
tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON);
tail_len += hostapd_mbo_ie_len(hapd);
tail_len += hostapd_eid_owe_trans_len(hapd);
@@ -1652,7 +1747,20 @@
tailpos = hostapd_eid_ht_capabilities(hapd, tailpos);
tailpos = hostapd_eid_ht_operation(hapd, tailpos);
- tailpos = hostapd_eid_ext_capab(hapd, tailpos);
+ if (hapd->iconf->mbssid && hapd->iconf->num_bss > 1) {
+ if (ieee802_11_build_ap_params_mbssid(hapd, params)) {
+ os_free(head);
+ os_free(tail);
+ wpa_printf(MSG_ERROR,
+ "MBSSID: Failed to set beacon data");
+ return -1;
+ }
+ complete = hapd->iconf->mbssid == MBSSID_ENABLED ||
+ (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
+ params->mbssid_elem_count == 1);
+ }
+
+ tailpos = hostapd_eid_ext_capab(hapd, tailpos, complete);
/*
* TODO: Time Advertisement element should only be included in some
@@ -1692,6 +1800,8 @@
tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON);
tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
tailpos = hostapd_get_rsnxe(hapd, tailpos, tailend - tailpos);
+ tailpos = hostapd_eid_mbssid_config(hapd, tailpos,
+ params->mbssid_elem_count);
#ifdef CONFIG_IEEE80211AX
if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
@@ -1774,7 +1884,6 @@
resp = hostapd_probe_resp_offloads(hapd, &resp_len);
#endif /* NEED_AP_MLME */
- os_memset(params, 0, sizeof(*params));
params->head = (u8 *) head;
params->head_len = head_len;
params->tail = tail;
@@ -1877,6 +1986,10 @@
params->head = NULL;
os_free(params->proberesp);
params->proberesp = NULL;
+ os_free(params->mbssid_elem);
+ params->mbssid_elem = NULL;
+ os_free(params->mbssid_elem_offset);
+ params->mbssid_elem_offset = NULL;
#ifdef CONFIG_FILS
os_free(params->fd_frame_tmpl);
params->fd_frame_tmpl = NULL;
diff --git a/src/ap/comeback_token.c b/src/ap/comeback_token.c
new file mode 100644
index 0000000..8d9f21b
--- /dev/null
+++ b/src/ap/comeback_token.c
@@ -0,0 +1,139 @@
+/*
+ * hostapd / Comeback token mechanism for SAE
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "hostapd.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "comeback_token.h"
+
+
+#if defined(CONFIG_SAE) || defined(CONFIG_PASN)
+
+static int comeback_token_hash(const u8 *comeback_key, const u8 *addr, u8 *idx)
+{
+ u8 hash[SHA256_MAC_LEN];
+
+ if (hmac_sha256(comeback_key, COMEBACK_KEY_SIZE,
+ addr, ETH_ALEN, hash) < 0)
+ return -1;
+ *idx = hash[0];
+ return 0;
+}
+
+
+int check_comeback_token(const u8 *comeback_key,
+ u16 *comeback_pending_idx, const u8 *addr,
+ const u8 *token, size_t token_len)
+{
+ u8 mac[SHA256_MAC_LEN];
+ const u8 *addrs[2];
+ size_t len[2];
+ u16 token_idx;
+ u8 idx;
+
+ if (token_len != SHA256_MAC_LEN ||
+ comeback_token_hash(comeback_key, addr, &idx) < 0)
+ return -1;
+ token_idx = comeback_pending_idx[idx];
+ if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) {
+ wpa_printf(MSG_DEBUG,
+ "Comeback: Invalid anti-clogging token from "
+ MACSTR " - token_idx 0x%04x, expected 0x%04x",
+ MAC2STR(addr), WPA_GET_BE16(token), token_idx);
+ return -1;
+ }
+
+ addrs[0] = addr;
+ len[0] = ETH_ALEN;
+ addrs[1] = token;
+ len[1] = 2;
+ if (hmac_sha256_vector(comeback_key, COMEBACK_KEY_SIZE,
+ 2, addrs, len, mac) < 0 ||
+ os_memcmp_const(token + 2, &mac[2], SHA256_MAC_LEN - 2) != 0)
+ return -1;
+
+ comeback_pending_idx[idx] = 0; /* invalidate used token */
+
+ return 0;
+}
+
+
+struct wpabuf *
+auth_build_token_req(struct os_reltime *last_comeback_key_update,
+ u8 *comeback_key, u16 comeback_idx,
+ u16 *comeback_pending_idx, size_t idx_len,
+ int group, const u8 *addr, int h2e)
+{
+ struct wpabuf *buf;
+ u8 *token;
+ struct os_reltime now;
+ u8 idx[2];
+ const u8 *addrs[2];
+ size_t len[2];
+ u8 p_idx;
+ u16 token_idx;
+
+ os_get_reltime(&now);
+ if (!os_reltime_initialized(last_comeback_key_update) ||
+ os_reltime_expired(&now, last_comeback_key_update, 60) ||
+ comeback_idx == 0xffff) {
+ if (random_get_bytes(comeback_key, COMEBACK_KEY_SIZE) < 0)
+ return NULL;
+ wpa_hexdump(MSG_DEBUG, "Comeback: Updated token key",
+ comeback_key, COMEBACK_KEY_SIZE);
+ *last_comeback_key_update = now;
+ comeback_idx = 0;
+ os_memset(comeback_pending_idx, 0, idx_len);
+ }
+
+ buf = wpabuf_alloc(sizeof(le16) + 3 + SHA256_MAC_LEN);
+ if (buf == NULL)
+ return NULL;
+
+ if (group)
+ wpabuf_put_le16(buf, group); /* Finite Cyclic Group */
+
+ if (h2e) {
+ /* Encapsulate Anti-clogging Token field in a container IE */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(buf, 1 + SHA256_MAC_LEN);
+ wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN);
+ }
+
+ if (comeback_token_hash(comeback_key, addr, &p_idx) < 0) {
+ wpabuf_free(buf);
+ return NULL;
+ }
+
+ token_idx = comeback_pending_idx[p_idx];
+ if (!token_idx) {
+ comeback_idx++;
+ token_idx = comeback_idx;
+ comeback_pending_idx[p_idx] = token_idx;
+ }
+ WPA_PUT_BE16(idx, token_idx);
+ token = wpabuf_put(buf, SHA256_MAC_LEN);
+ addrs[0] = addr;
+ len[0] = ETH_ALEN;
+ addrs[1] = idx;
+ len[1] = sizeof(idx);
+ if (hmac_sha256_vector(comeback_key, COMEBACK_KEY_SIZE,
+ 2, addrs, len, token) < 0) {
+ wpabuf_free(buf);
+ return NULL;
+ }
+ WPA_PUT_BE16(token, token_idx);
+
+ return buf;
+}
+
+#endif /* defined(CONFIG_SAE) || defined(CONFIG_PASN) */
diff --git a/src/ap/comeback_token.h b/src/ap/comeback_token.h
new file mode 100644
index 0000000..d5de9e6
--- /dev/null
+++ b/src/ap/comeback_token.h
@@ -0,0 +1,21 @@
+/*
+ * hostapd / Comeback token mechanism for SAE
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef COMEBACK_TOKEN_H
+#define COMEBACK_TOKEN_H
+
+int check_comeback_token(const u8 *comeback_key,
+ u16 *comeback_pending_idx, const u8 *addr,
+ const u8 *token, size_t token_len);
+struct wpabuf *
+auth_build_token_req(struct os_reltime *last_comeback_key_update,
+ u8 *comeback_key, u16 comeback_idx,
+ u16 *comeback_pending_idx, size_t idx_len,
+ int group, const u8 *addr, int h2e);
+
+#endif /* COMEBACK_TOKEN_H */
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index ac8f6fa..168e5f5 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -99,7 +99,7 @@
len += ret;
ret = os_snprintf(buf + len, buflen - len, "rx_rate_info=%lu",
- data.current_rx_rate);
+ data.current_rx_rate / 100);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
@@ -131,7 +131,7 @@
len += ret;
ret = os_snprintf(buf + len, buflen - len, "tx_rate_info=%lu",
- data.current_tx_rate);
+ data.current_tx_rate / 100);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
@@ -206,9 +206,9 @@
return "REMOVE";
case STA_DISASSOC_FROM_CLI:
return "DISASSOC_FROM_CLI";
+ default:
+ return "?";
}
-
- return "?";
}
@@ -218,6 +218,7 @@
{
int len, res, ret, i;
const char *keyid;
+ const u8 *dpp_pkhash;
if (!sta)
return 0;
@@ -386,6 +387,18 @@
len += ret;
}
+ dpp_pkhash = ap_sta_wpa_get_dpp_pkhash(hapd, sta);
+ if (dpp_pkhash) {
+ ret = os_snprintf(buf + len, buflen - len, "dpp_pkhash=");
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ len += wpa_snprintf_hex(buf + len, buflen - len, dpp_pkhash,
+ SHA256_MAC_LEN);
+ ret = os_snprintf(buf + len, buflen - len, "\n");
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+
return len;
}
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 7f31f28..e8c5ec9 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -445,6 +445,8 @@
mode = iface->current_mode;
for (i = 0; i < n_chans; i++) {
+ if (start_chan_idx + i >= mode->num_channels)
+ break;
channel = &mode->channels[start_chan_idx + i];
if (channel->flag & HOSTAPD_CHAN_RADAR)
res++;
@@ -797,6 +799,8 @@
mode = iface->current_mode;
for (i = 0; i < n_chans; i++) {
+ if (start_chan_idx + i >= mode->num_channels)
+ break;
channel = &mode->channels[start_chan_idx + i];
if (!(channel->flag & HOSTAPD_CHAN_RADAR))
continue;
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index 32ddb3b..70dd18e 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -1378,8 +1378,7 @@
static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator)
{
wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
- wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=%d",
- initiator);
+ dpp_notify_auth_success(hapd->dpp_auth, initiator);
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
wpa_printf(MSG_INFO,
@@ -2052,6 +2051,7 @@
os_time_t expire;
int expiration;
enum dpp_status_error res;
+ u8 pkhash[SHA256_MAC_LEN];
os_memset(&intro, 0, sizeof(intro));
@@ -2088,7 +2088,7 @@
wpabuf_len(hapd->conf->dpp_netaccesskey),
wpabuf_head(hapd->conf->dpp_csign),
wpabuf_len(hapd->conf->dpp_csign),
- connector, connector_len, &expire);
+ connector, connector_len, &expire, pkhash);
if (res == 255) {
wpa_printf(MSG_INFO,
"DPP: Network Introduction protocol resulted in internal failure (peer "
@@ -2133,9 +2133,9 @@
else
expiration = 0;
- if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+ if (wpa_auth_pmksa_add3(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
intro.pmkid, expiration,
- WPA_KEY_MGMT_DPP) < 0) {
+ WPA_KEY_MGMT_DPP, pkhash) < 0) {
wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
goto done;
}
@@ -2793,6 +2793,7 @@
os_time_t expire;
int expiration;
enum dpp_status_error res;
+ u8 pkhash[SHA256_MAC_LEN];
os_memset(&intro, 0, sizeof(intro));
@@ -2870,7 +2871,7 @@
wpabuf_len(hapd->conf->dpp_netaccesskey),
wpabuf_head(hapd->conf->dpp_csign),
wpabuf_len(hapd->conf->dpp_csign),
- connector, connector_len, &expire);
+ connector, connector_len, &expire, pkhash);
if (res == 255) {
wpa_printf(MSG_INFO,
"DPP: Network Introduction protocol resulted in internal failure (peer "
@@ -2904,9 +2905,9 @@
else
expiration = 0;
- if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+ if (wpa_auth_pmksa_add3(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
intro.pmkid, expiration,
- WPA_KEY_MGMT_DPP) < 0) {
+ WPA_KEY_MGMT_DPP, pkhash) < 0) {
wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
goto done;
}
@@ -3089,8 +3090,7 @@
* from TX status handler, but since there was no such handler
* call yet, simply send out the event message and proceed with
* exchange. */
- wpa_msg(hapd->msg_ctx, MSG_INFO,
- DPP_EVENT_AUTH_SUCCESS "init=1");
+ dpp_notify_auth_success(hapd->dpp_auth, 1);
hapd->dpp_auth_ok_on_ack = 0;
}
@@ -3376,7 +3376,7 @@
{
struct hostapd_data *hapd = ctx;
- gas_serv_req_dpp_processing(hapd, addr, dialog_token, prot, buf);
+ gas_serv_req_dpp_processing(hapd, addr, dialog_token, prot, buf, 0);
}
#endif /* CONFIG_DPP2 */
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index b1cb31e..4c33e86 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -451,7 +451,7 @@
}
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_SAE
- if (hapd->conf->sae_pwe == 2 &&
+ if (hapd->conf->sae_pwe == SAE_PWE_BOTH &&
sta->auth_alg == WLAN_AUTH_SAE &&
sta->sae && !sta->sae->h2e &&
ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
@@ -862,14 +862,16 @@
int finished)
{
#ifdef NEED_AP_MLME
- int channel, chwidth, is_dfs;
+ int channel, chwidth, is_dfs0, is_dfs;
u8 seg0_idx = 0, seg1_idx = 0;
size_t i;
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
- "driver %s channel switch: freq=%d, ht=%d, vht_ch=0x%x, he_ch=0x%x, eht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
+ "driver %s channel switch: iface->freq=%d, freq=%d, ht=%d, vht_ch=0x%x, "
+ "he_ch=0x%x, eht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
finished ? "had" : "starting",
+ hapd->iface->freq,
freq, ht, hapd->iconf->ch_switch_vht_config,
hapd->iconf->ch_switch_he_config,
hapd->iconf->ch_switch_eht_config, offset,
@@ -882,6 +884,8 @@
return;
}
+ /* Check if any of configured channels require DFS */
+ is_dfs0 = hostapd_is_dfs_required(hapd->iface);
hapd->iface->freq = freq;
channel = hostapd_hw_get_channel(hapd, freq);
@@ -997,11 +1001,11 @@
hapd->iface->num_hw_features);
wpa_msg(hapd->msg_ctx, MSG_INFO,
- "%sfreq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d dfs=%d",
+ "%sfreq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d is_dfs0=%d dfs=%d",
finished ? WPA_EVENT_CHANNEL_SWITCH :
WPA_EVENT_CHANNEL_SWITCH_STARTED,
freq, ht, offset, channel_width_to_string(width),
- cf1, cf2, is_dfs);
+ cf1, cf2, is_dfs0, is_dfs);
if (!finished)
return;
@@ -1013,6 +1017,14 @@
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
"freq=%d dfs=%d", freq, is_dfs);
} else if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) {
+ /* Complete AP configuration for the first bring up. */
+ if (is_dfs0 > 0 &&
+ hostapd_is_dfs_required(hapd->iface) <= 0 &&
+ hapd->iface->state != HAPD_IFACE_ENABLED) {
+ /* Fake a CAC start bit to skip setting channel */
+ hapd->iface->cac_started = 1;
+ hostapd_setup_interface_complete(hapd->iface, 0);
+ }
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
"freq=%d dfs=%d", freq, is_dfs);
} else if (is_dfs &&
diff --git a/src/ap/fils_hlp.c b/src/ap/fils_hlp.c
index 0310aab..d64fb8c 100644
--- a/src/ap/fils_hlp.c
+++ b/src/ap/fils_hlp.c
@@ -530,9 +530,9 @@
switch (iph->ip_p) {
case 17:
return fils_process_hlp_udp(hapd, sta, dst, pos, len);
+ default:
+ return 0;
}
-
- return 0;
}
@@ -567,9 +567,9 @@
case ETH_P_IP:
return fils_process_hlp_ip(hapd, sta, pos, pkt + 2,
end - pkt - 2);
+ default:
+ return 0;
}
-
- return 0;
}
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
index 90f1577..4642e49 100644
--- a/src/ap/gas_serv.c
+++ b/src/ap/gas_serv.c
@@ -1524,7 +1524,7 @@
#ifdef CONFIG_DPP
void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
const u8 *sa, u8 dialog_token,
- int prot, struct wpabuf *buf)
+ int prot, struct wpabuf *buf, int freq)
{
struct wpabuf *tx_buf;
@@ -1582,7 +1582,7 @@
return;
if (prot)
convert_to_protected_dual(tx_buf);
- hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+ hostapd_drv_send_action(hapd, freq ? freq : hapd->iface->freq, 0, sa,
wpabuf_head(tx_buf),
wpabuf_len(tx_buf));
wpabuf_free(tx_buf);
@@ -1593,7 +1593,7 @@
static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
const u8 *sa,
const u8 *data, size_t len, int prot,
- int std_addr3)
+ int std_addr3, int freq)
{
const u8 *pos = data;
const u8 *end = data + len;
@@ -1688,7 +1688,8 @@
data, len);
if (!msg)
return;
- gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg);
+ gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg,
+ freq);
return;
}
#endif /* CONFIG_DPP */
@@ -1871,7 +1872,7 @@
switch (data[0]) {
case WLAN_PA_GAS_INITIAL_REQ:
gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot,
- std_addr3);
+ std_addr3, freq);
break;
case WLAN_PA_GAS_COMEBACK_REQ:
gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot,
diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h
index 1528af4..7646a98 100644
--- a/src/ap/gas_serv.h
+++ b/src/ap/gas_serv.h
@@ -90,6 +90,6 @@
void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
const u8 *sa, u8 dialog_token,
- int prot, struct wpabuf *buf);
+ int prot, struct wpabuf *buf, int freq);
#endif /* GAS_SERV_H */
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 3681df6..58492e5 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -91,6 +91,29 @@
}
+struct hostapd_data * hostapd_mbssid_get_tx_bss(struct hostapd_data *hapd)
+{
+ if (hapd->iconf->mbssid)
+ return hapd->iface->bss[0];
+
+ return hapd;
+}
+
+
+int hostapd_mbssid_get_bss_index(struct hostapd_data *hapd)
+{
+ if (hapd->iconf->mbssid) {
+ size_t i;
+
+ for (i = 1; i < hapd->iface->num_bss; i++)
+ if (hapd->iface->bss[i] == hapd)
+ return i;
+ }
+
+ return 0;
+}
+
+
void hostapd_reconfig_encryption(struct hostapd_data *hapd)
{
if (hapd->wpa_auth)
@@ -172,27 +195,34 @@
}
-static void hostapd_clear_old(struct hostapd_iface *iface)
+static void hostapd_clear_old_bss(struct hostapd_data *bss)
{
- size_t j;
+ wpa_printf(MSG_DEBUG, "BSS %s changed - clear old state",
+ bss->conf->iface);
/*
* Deauthenticate all stations since the new configuration may not
* allow them to use the BSS anymore.
*/
- for (j = 0; j < iface->num_bss; j++) {
- hostapd_flush_old_stations(iface->bss[j],
- WLAN_REASON_PREV_AUTH_NOT_VALID);
+ hostapd_flush_old_stations(bss, WLAN_REASON_PREV_AUTH_NOT_VALID);
#ifdef CONFIG_WEP
- hostapd_broadcast_wep_clear(iface->bss[j]);
+ hostapd_broadcast_wep_clear(bss);
#endif /* CONFIG_WEP */
#ifndef CONFIG_NO_RADIUS
- /* TODO: update dynamic data based on changed configuration
- * items (e.g., open/close sockets, etc.) */
- radius_client_flush(iface->bss[j]->radius, 0);
+ /* TODO: update dynamic data based on changed configuration
+ * items (e.g., open/close sockets, etc.) */
+ radius_client_flush(bss->radius, 0);
#endif /* CONFIG_NO_RADIUS */
- }
+}
+
+
+static void hostapd_clear_old(struct hostapd_iface *iface)
+{
+ size_t j;
+
+ for (j = 0; j < iface->num_bss; j++)
+ hostapd_clear_old_bss(iface->bss[j]);
}
@@ -236,13 +266,13 @@
if (newconf == NULL)
return -1;
- hostapd_clear_old(iface);
-
oldconf = hapd->iconf;
if (hostapd_iface_conf_changed(newconf, oldconf)) {
char *fname;
int res;
+ hostapd_clear_old(iface);
+
wpa_printf(MSG_DEBUG,
"Configuration changes include interface/BSS modification - force full disable+enable sequence");
fname = os_strdup(iface->config_fname);
@@ -272,6 +302,10 @@
for (j = 0; j < iface->num_bss; j++) {
hapd = iface->bss[j];
+ if (!hapd->conf->config_id || !newconf->bss[j]->config_id ||
+ os_strcmp(hapd->conf->config_id,
+ newconf->bss[j]->config_id) != 0)
+ hostapd_clear_old_bss(hapd);
hapd->iconf = newconf;
hapd->iconf->channel = oldconf->channel;
hapd->iconf->acs = oldconf->acs;
@@ -525,6 +559,7 @@
iface->current_rates = NULL;
os_free(iface->basic_rates);
iface->basic_rates = NULL;
+ iface->cac_started = 0;
ap_list_deinit(iface);
sta_track_deinit(iface);
airtime_policy_update_deinit(iface);
@@ -1104,18 +1139,55 @@
#endif /* CONFIG_NO_RADIUS */
+static int hostapd_start_beacon(struct hostapd_data *hapd,
+ bool flush_old_stations)
+{
+ struct hostapd_bss_config *conf = hapd->conf;
+
+ if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
+ return -1;
+
+ if (flush_old_stations && !conf->start_disabled &&
+ conf->broadcast_deauth) {
+ u8 addr[ETH_ALEN];
+
+ /* Should any previously associated STA not have noticed that
+ * the AP had stopped and restarted, send one more
+ * deauthentication notification now that the AP is ready to
+ * operate. */
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+ "Deauthenticate all stations at BSS start");
+ os_memset(addr, 0xff, ETH_ALEN);
+ hostapd_drv_sta_deauth(hapd, addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ }
+
+ if (hapd->driver && hapd->driver->set_operstate)
+ hapd->driver->set_operstate(hapd->drv_priv, 1);
+
+ return 0;
+}
+
+
/**
* hostapd_setup_bss - Per-BSS setup (initialization)
* @hapd: Pointer to BSS data
* @first: Whether this BSS is the first BSS of an interface; -1 = not first,
* but interface may exist
+ * @start_beacon: Whether Beacon frame template should be configured and
+ * transmission of Beaconf rames started at this time. This is used when
+ * MBSSID element is enabled where the information regarding all BSSes
+ * should be retrieved before configuring the Beacon frame template. The
+ * calling functions are responsible for configuring the Beacon frame
+ * explicitly if this is set to false.
*
* This function is used to initialize all per-BSS data structures and
* resources. This gets called in a loop for each BSS when an interface is
* initialized. Most of the modules that are initialized here will be
* deinitialized in hostapd_cleanup().
*/
-static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
+static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
+ bool start_beacon)
{
struct hostapd_bss_config *conf = hapd->conf;
u8 ssid[SSID_MAX_LEN + 1];
@@ -1388,29 +1460,11 @@
return -1;
}
- if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
- return -1;
-
- if (flush_old_stations && !conf->start_disabled &&
- conf->broadcast_deauth) {
- u8 addr[ETH_ALEN];
-
- /* Should any previously associated STA not have noticed that
- * the AP had stopped and restarted, send one more
- * deauthentication notification now that the AP is ready to
- * operate. */
- wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
- "Deauthenticate all stations at BSS start");
- os_memset(addr, 0xff, ETH_ALEN);
- hostapd_drv_sta_deauth(hapd, addr,
- WLAN_REASON_PREV_AUTH_NOT_VALID);
- }
-
if (hapd->wpa_auth && wpa_init_keys(hapd->wpa_auth) < 0)
return -1;
- if (hapd->driver && hapd->driver->set_operstate)
- hapd->driver->set_operstate(hapd->drv_priv, 1);
+ if (start_beacon)
+ return hostapd_start_beacon(hapd, flush_old_stations);
return 0;
}
@@ -2135,7 +2189,7 @@
hapd = iface->bss[j];
if (j)
os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN);
- if (hostapd_setup_bss(hapd, j == 0)) {
+ if (hostapd_setup_bss(hapd, j == 0, !iface->conf->mbssid)) {
for (;;) {
hapd = iface->bss[j];
hostapd_bss_deinit_no_free(hapd);
@@ -2149,6 +2203,24 @@
if (is_zero_ether_addr(hapd->conf->bssid))
prev_addr = hapd->own_addr;
}
+
+ if (hapd->iconf->mbssid) {
+ for (j = 0; hapd->iconf->mbssid && j < iface->num_bss; j++) {
+ hapd = iface->bss[j];
+ if (hostapd_start_beacon(hapd, true)) {
+ for (;;) {
+ hapd = iface->bss[j];
+ hostapd_bss_deinit_no_free(hapd);
+ hostapd_free_hapd_data(hapd);
+ if (j == 0)
+ break;
+ j--;
+ }
+ goto fail;
+ }
+ }
+ }
+
hapd = iface->bss[0];
hostapd_tx_queue_params(iface);
@@ -2802,6 +2874,21 @@
}
+int hostapd_reload_bss_only(struct hostapd_data *bss)
+{
+
+ wpa_printf(MSG_DEBUG, "Reload BSS %s", bss->conf->iface);
+ hostapd_set_security_params(bss->conf, 1);
+ if (hostapd_config_check(bss->iconf, 1) < 0) {
+ wpa_printf(MSG_ERROR, "Updated BSS configuration is invalid");
+ return -1;
+ }
+ hostapd_clear_old_bss(bss);
+ hostapd_reload_bss(bss);
+ return 0;
+}
+
+
int hostapd_disable_iface(struct hostapd_iface *hapd_iface)
{
size_t j;
@@ -3030,7 +3117,7 @@
if (start_ctrl_iface_bss(hapd) < 0 ||
(hapd_iface->state == HAPD_IFACE_ENABLED &&
- hostapd_setup_bss(hapd, -1))) {
+ hostapd_setup_bss(hapd, -1, true))) {
hostapd_cleanup(hapd);
hapd_iface->bss[hapd_iface->num_bss - 1] = NULL;
hapd_iface->conf->num_bss--;
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index e121362..a88f9b6 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -352,11 +352,15 @@
#endif /* CONFIG_SQLITE */
#ifdef CONFIG_SAE
+
+#define COMEBACK_KEY_SIZE 8
+#define COMEBACK_PENDING_IDX_SIZE 256
+
/** Key used for generating SAE anti-clogging tokens */
- u8 comeback_key[8];
+ u8 comeback_key[COMEBACK_KEY_SIZE];
struct os_reltime last_comeback_key_update;
u16 comeback_idx;
- u16 comeback_pending_idx[256];
+ u16 comeback_pending_idx[COMEBACK_PENDING_IDX_SIZE];
int dot11RSNASAERetransPeriod; /* msec */
struct dl_list sae_commit_queue; /* struct hostapd_sae_commit_queue */
#endif /* CONFIG_SAE */
@@ -640,6 +644,11 @@
/* Previous WMM element information */
struct hostapd_wmm_ac_params prev_wmm[WMM_AC_NUM];
+ /* Maximum number of interfaces supported for MBSSID advertisement */
+ unsigned int mbssid_max_interfaces;
+ /* Maximum profile periodicity for enhanced MBSSID advertisement */
+ unsigned int ema_max_periodicity;
+
int (*enable_iface_cb)(struct hostapd_iface *iface);
int (*disable_iface_cb)(struct hostapd_iface *iface);
};
@@ -669,6 +678,7 @@
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_reload_bss_only(struct hostapd_data *bss);
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);
@@ -740,5 +750,7 @@
#endif /* CONFIG_FST */
int hostapd_set_acl(struct hostapd_data *hapd);
+struct hostapd_data * hostapd_mbssid_get_tx_bss(struct hostapd_data *hapd);
+int hostapd_mbssid_get_bss_index(struct hostapd_data *hapd);
#endif /* HOSTAPD_H */
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index a65a296..e53f0dc 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -55,6 +55,8 @@
#include "fils_hlp.h"
#include "dpp_hostapd.h"
#include "gas_query_ap.h"
+#include "comeback_token.h"
+#include "pasn/pasn_common.h"
#ifdef CONFIG_FILS
@@ -68,11 +70,6 @@
#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,
@@ -118,9 +115,13 @@
num++;
if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
num++;
- h2e_required = (hapd->conf->sae_pwe == 1 ||
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax && hapd->iconf->require_he)
+ num++;
+#endif /* CONFIG_IEEE80211AX */
+ h2e_required = (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
- hapd->conf->sae_pwe != 3 &&
+ hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt);
if (h2e_required)
num++;
@@ -150,6 +151,13 @@
*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
}
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax && hapd->iconf->require_he && count < 8) {
+ count++;
+ *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY;
+ }
+#endif /* CONFIG_IEEE80211AX */
+
if (h2e_required && count < 8) {
count++;
*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
@@ -165,6 +173,7 @@
int i, num, count;
int h2e_required;
+ hapd->conf->xrates_supported = false;
if (hapd->iface->current_rates == NULL)
return eid;
@@ -173,9 +182,13 @@
num++;
if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
num++;
- h2e_required = (hapd->conf->sae_pwe == 1 ||
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax && hapd->iconf->require_he)
+ num++;
+#endif /* CONFIG_IEEE80211AX */
+ h2e_required = (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
- hapd->conf->sae_pwe != 3 &&
+ hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt);
if (h2e_required)
num++;
@@ -208,12 +221,21 @@
*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
}
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax && hapd->iconf->require_he) {
+ count++;
+ if (count > 8)
+ *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY;
+ }
+#endif /* CONFIG_IEEE80211AX */
+
if (h2e_required) {
count++;
if (count > 8)
*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
}
+ hapd->conf->xrates_supported = true;
return pos;
}
@@ -561,7 +583,7 @@
#endif /* CONFIG_SAE_PK */
}
- if (rx_id && hapd->conf->sae_pwe != 3)
+ if (rx_id && hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
use_pt = 1;
else if (status_code == WLAN_STATUS_SUCCESS)
use_pt = 0;
@@ -737,124 +759,6 @@
return 0;
}
-
-static int comeback_token_hash(struct hostapd_data *hapd, const u8 *addr,
- u8 *idx)
-{
- u8 hash[SHA256_MAC_LEN];
-
- if (hmac_sha256(hapd->comeback_key, sizeof(hapd->comeback_key),
- addr, ETH_ALEN, hash) < 0)
- return -1;
- *idx = hash[0];
- return 0;
-}
-
-
-static int check_comeback_token(struct hostapd_data *hapd, const u8 *addr,
- const u8 *token, size_t token_len)
-{
- u8 mac[SHA256_MAC_LEN];
- const u8 *addrs[2];
- size_t len[2];
- u16 token_idx;
- u8 idx;
-
- if (token_len != SHA256_MAC_LEN ||
- comeback_token_hash(hapd, addr, &idx) < 0)
- return -1;
- token_idx = hapd->comeback_pending_idx[idx];
- if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) {
- wpa_printf(MSG_DEBUG,
- "Comeback: Invalid anti-clogging token from "
- MACSTR " - token_idx 0x%04x, expected 0x%04x",
- MAC2STR(addr), WPA_GET_BE16(token), token_idx);
- return -1;
- }
-
- addrs[0] = addr;
- len[0] = ETH_ALEN;
- addrs[1] = token;
- len[1] = 2;
- if (hmac_sha256_vector(hapd->comeback_key, sizeof(hapd->comeback_key),
- 2, addrs, len, mac) < 0 ||
- os_memcmp_const(token + 2, &mac[2], SHA256_MAC_LEN - 2) != 0)
- return -1;
-
- hapd->comeback_pending_idx[idx] = 0; /* invalidate used token */
-
- return 0;
-}
-
-
-static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd,
- int group, const u8 *addr, int h2e)
-{
- struct wpabuf *buf;
- u8 *token;
- struct os_reltime now;
- u8 idx[2];
- const u8 *addrs[2];
- size_t len[2];
- u8 p_idx;
- u16 token_idx;
-
- os_get_reltime(&now);
- if (!os_reltime_initialized(&hapd->last_comeback_key_update) ||
- os_reltime_expired(&now, &hapd->last_comeback_key_update, 60) ||
- hapd->comeback_idx == 0xffff) {
- if (random_get_bytes(hapd->comeback_key,
- sizeof(hapd->comeback_key)) < 0)
- return NULL;
- wpa_hexdump(MSG_DEBUG, "Comeback: Updated token key",
- hapd->comeback_key, sizeof(hapd->comeback_key));
- hapd->last_comeback_key_update = now;
- hapd->comeback_idx = 0;
- os_memset(hapd->comeback_pending_idx, 0,
- sizeof(hapd->comeback_pending_idx));
- }
-
- buf = wpabuf_alloc(sizeof(le16) + 3 + SHA256_MAC_LEN);
- if (buf == NULL)
- return NULL;
-
- if (group)
- wpabuf_put_le16(buf, group); /* Finite Cyclic Group */
-
- if (h2e) {
- /* Encapsulate Anti-clogging Token field in a container IE */
- wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
- wpabuf_put_u8(buf, 1 + SHA256_MAC_LEN);
- wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN);
- }
-
- if (comeback_token_hash(hapd, addr, &p_idx) < 0) {
- wpabuf_free(buf);
- return NULL;
- }
-
- token_idx = hapd->comeback_pending_idx[p_idx];
- if (!token_idx) {
- hapd->comeback_idx++;
- token_idx = hapd->comeback_idx;
- hapd->comeback_pending_idx[p_idx] = token_idx;
- }
- WPA_PUT_BE16(idx, token_idx);
- token = wpabuf_put(buf, SHA256_MAC_LEN);
- addrs[0] = addr;
- len[0] = ETH_ALEN;
- addrs[1] = idx;
- len[1] = sizeof(idx);
- if (hmac_sha256_vector(hapd->comeback_key, sizeof(hapd->comeback_key),
- 2, addrs, len, token) < 0) {
- wpabuf_free(buf);
- return NULL;
- }
- WPA_PUT_BE16(token, token_idx);
-
- return buf;
-}
-
#endif /* defined(CONFIG_SAE) || defined(CONFIG_PASN) */
@@ -1220,31 +1124,32 @@
static int sae_status_success(struct hostapd_data *hapd, u16 status_code)
{
- int sae_pwe = hapd->conf->sae_pwe;
+ enum sae_pwe sae_pwe = hapd->conf->sae_pwe;
int id_in_use;
bool sae_pk = false;
id_in_use = hostapd_sae_pw_id_in_use(hapd->conf);
- if (id_in_use == 2 && sae_pwe != 3)
- sae_pwe = 1;
- else if (id_in_use == 1 && sae_pwe == 0)
- sae_pwe = 2;
+ if (id_in_use == 2 && sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+ sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
+ else if (id_in_use == 1 && sae_pwe == SAE_PWE_HUNT_AND_PECK)
+ sae_pwe = SAE_PWE_BOTH;
#ifdef CONFIG_SAE_PK
sae_pk = hostapd_sae_pk_in_use(hapd->conf);
- if (sae_pwe == 0 && sae_pk)
- sae_pwe = 2;
+ if (sae_pwe == SAE_PWE_HUNT_AND_PECK && sae_pk)
+ sae_pwe = SAE_PWE_BOTH;
#endif /* CONFIG_SAE_PK */
- if (sae_pwe == 0 &&
+ if (sae_pwe == SAE_PWE_HUNT_AND_PECK &&
(hapd->conf->wpa_key_mgmt &
(WPA_KEY_MGMT_SAE_EXT_KEY | WPA_KEY_MGMT_FT_SAE_EXT_KEY)))
- sae_pwe = 2;
+ sae_pwe = SAE_PWE_BOTH;
- return ((sae_pwe == 0 || sae_pwe == 3) &&
+ return ((sae_pwe == SAE_PWE_HUNT_AND_PECK ||
+ sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK) &&
status_code == WLAN_STATUS_SUCCESS) ||
- (sae_pwe == 1 &&
+ (sae_pwe == SAE_PWE_HASH_TO_ELEMENT &&
(status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
(sae_pk && status_code == WLAN_STATUS_SAE_PK))) ||
- (sae_pwe == 2 &&
+ (sae_pwe == SAE_PWE_BOTH &&
(status_code == WLAN_STATUS_SUCCESS ||
status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
(sae_pk && status_code == WLAN_STATUS_SAE_PK)));
@@ -1468,7 +1373,8 @@
mgmt->u.auth.variable, &token,
&token_len, groups, status_code ==
WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
- status_code == WLAN_STATUS_SAE_PK);
+ status_code == WLAN_STATUS_SAE_PK,
+ NULL);
if (resp == SAE_SILENTLY_DISCARD) {
wpa_printf(MSG_DEBUG,
"SAE: Drop commit message from " MACSTR " due to reflection attack",
@@ -1487,7 +1393,9 @@
}
if (token &&
- check_comeback_token(hapd, sta->addr, token, token_len)
+ check_comeback_token(hapd->comeback_key,
+ hapd->comeback_pending_idx, sta->addr,
+ token, token_len)
< 0) {
wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "
"incorrect token from " MACSTR,
@@ -1515,8 +1423,14 @@
if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
status_code == WLAN_STATUS_SAE_PK)
h2e = 1;
- data = auth_build_token_req(hapd, sta->sae->group,
- sta->addr, h2e);
+ data = auth_build_token_req(
+ &hapd->last_comeback_key_update,
+ hapd->comeback_key,
+ hapd->comeback_idx,
+ hapd->comeback_pending_idx,
+ sizeof(hapd->comeback_pending_idx),
+ sta->sae->group,
+ sta->addr, h2e);
resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
if (hapd->conf->mesh & MESH_ENABLED)
sae_set_state(sta, SAE_NOTHING,
@@ -1560,8 +1474,9 @@
return;
}
- if (sae_check_confirm(sta->sae, var, var_len) < 0) {
- resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ if (sae_check_confirm(sta->sae, var, var_len,
+ NULL) < 0) {
+ resp = WLAN_STATUS_CHALLENGE_FAIL;
goto reply;
}
sta->sae->rc = peer_send_confirm;
@@ -2110,10 +2025,8 @@
if (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm))) {
/* FTE[R1KH-ID,R0KH-ID] when using FILS+FT */
int res;
- int use_sha384 = wpa_key_mgmt_sha384(
- wpa_auth_sta_key_mgmt(sta->wpa_sm));
- res = wpa_auth_write_fte(hapd->wpa_auth, use_sha384,
+ res = wpa_auth_write_fte(hapd->wpa_auth, sta->wpa_sm,
wpabuf_put(data, 0),
wpabuf_tailroom(data));
if (res < 0) {
@@ -2390,243 +2303,15 @@
#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;
- const u8 *data;
- size_t buf_len;
- u16 res, alg, seq, status;
- int groups[] = { pasn->group, 0 };
- struct sae_pt *pt = NULL;
- 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=%zu",
- 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);
-
- if (alg != WLAN_AUTH_SAE || seq != 1 ||
- status != WLAN_STATUS_SAE_HASH_TO_ELEMENT) {
- 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, &pt, NULL);
- if (!password || !pt) {
- wpa_printf(MSG_DEBUG, "PASN: No SAE PT found");
- return -1;
- }
-
- ret = sae_prepare_commit_pt(&pasn->sae, pt, hapd->own_addr, sta->addr,
- NULL, NULL);
- 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=%zu",
- 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.pmk_len,
- pasn->sae.pmkid, pasn->sae.akmp);
- 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_SAE_HASH_TO_ELEMENT);
-
- /* 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;
+ struct pasn_fils *fils = &pasn->fils;
u8 pmk[PMK_LEN_MAX];
size_t pmk_len;
int ret;
@@ -2682,7 +2367,8 @@
pasn->secret = NULL;
fils->erp_resp = erp_resp;
- ret = handle_auth_pasn_resp(hapd, sta, NULL, WLAN_STATUS_SUCCESS);
+ ret = handle_auth_pasn_resp(sta->pasn, hapd->own_addr, sta->addr, NULL,
+ WLAN_STATUS_SUCCESS);
fils->erp_resp = NULL;
if (ret) {
@@ -2705,7 +2391,7 @@
return -1;
#else /* CONFIG_NO_RADIUS */
struct pasn_data *pasn = sta->pasn;
- struct pasn_fils_data *fils = &pasn->fils;
+ struct pasn_fils *fils = &pasn->fils;
struct ieee802_11_elems elems;
struct wpa_ie_data rsne_data;
struct wpabuf *fils_wd;
@@ -2756,7 +2442,7 @@
}
if (!elems.rsn_ie || !elems.fils_nonce || !elems.fils_nonce ||
- !elems.wrapped_data) {
+ !elems.wrapped_data || !elems.fils_session) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Missing IEs");
return -1;
}
@@ -2764,7 +2450,7 @@
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");
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Failed parsing RSNE");
return -1;
}
@@ -2824,36 +2510,45 @@
#endif /* CONFIG_FILS */
-static struct wpabuf * pasn_get_wrapped_data(struct hostapd_data *hapd,
- struct sta_info *sta)
+static int hapd_pasn_send_mlme(void *ctx, const u8 *data, size_t data_len,
+ int noack, unsigned int freq, unsigned int wait)
{
- 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;
- }
+ struct hostapd_data *hapd = ctx;
+
+ return hostapd_drv_send_mlme(hapd, data, data_len, 0, NULL, 0, 0);
+}
+
+
+static void hapd_initialize_pasn(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ struct pasn_data *pasn = sta->pasn;
+
+ pasn->cb_ctx = hapd;
+ pasn->send_mgmt = hapd_pasn_send_mlme;
+ pasn->pasn_groups = hapd->conf->pasn_groups;
+ pasn->wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
+ pasn->rsn_pairwise = hapd->conf->rsn_pairwise;
+ pasn->derive_kdk = hapd->iface->drv_flags2 &
+ WPA_DRIVER_FLAGS2_SEC_LTF_AP;
+#ifdef CONFIG_TESTING_OPTIONS
+ pasn->corrupt_mic = hapd->conf->pasn_corrupt_mic;
+ if (hapd->conf->force_kdk_derivation)
+ pasn->derive_kdk = true;
+#endif /* CONFIG_TESTING_OPTIONS */
+ pasn->use_anti_clogging = use_anti_clogging(hapd);
+ pasn->password = sae_get_password(hapd, sta, NULL, NULL, &pasn->pt,
+ NULL);
+ pasn->rsn_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &pasn->rsn_ie_len);
+ pasn->rsnxe_ie = hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
+ pasn->disable_pmksa_caching = hapd->conf->disable_pmksa_caching;
+ pasn->pmksa = wpa_auth_get_pmksa_cache(hapd->wpa_auth);
+
+ pasn->comeback_after = hapd->conf->pasn_comeback_after;
+ pasn->comeback_idx = hapd->comeback_idx;
+ pasn->comeback_key = hapd->comeback_key;
+ pasn->comeback_pending_idx = hapd->comeback_pending_idx;
+ os_memcpy(pasn->bssid, hapd->own_addr, ETH_ALEN);
}
@@ -2889,582 +2584,16 @@
}
-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 void hapd_pasn_update_params(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len)
{
- 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, sta->pasn->kdk_len);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK");
- return -1;
- }
-
- if (sta->pasn->secure_ltf) {
- ret = wpa_ltf_keyseed(&sta->pasn->ptk, sta->pasn->akmp,
- sta->pasn->cipher);
- if (ret) {
- wpa_printf(MSG_DEBUG,
- "PASN: Failed to derive LTF keyseed");
- return -1;
- }
- }
-
- wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived");
- return 0;
-}
-
-
-static void handle_auth_pasn_comeback(struct hostapd_data *hapd,
- struct sta_info *sta, u16 group)
-{
- struct wpabuf *buf, *comeback;
- int ret;
-
- wpa_printf(MSG_DEBUG,
- "PASN: Building comeback frame 2. Comeback after=%u",
- hapd->conf->pasn_comeback_after);
-
- buf = wpabuf_alloc(1500);
- if (!buf)
- return;
-
- wpa_pasn_build_auth_header(buf, hapd->own_addr, hapd->own_addr,
- sta->addr, 2,
- WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY);
-
- /*
- * Do not include the group as a part of the token since it is not going
- * to be used.
- */
- comeback = auth_build_token_req(hapd, 0, sta->addr, 0);
- if (!comeback) {
- wpa_printf(MSG_DEBUG,
- "PASN: Failed sending auth with comeback");
- wpabuf_free(buf);
- return;
- }
-
- wpa_pasn_add_parameter_ie(buf, group,
- WPA_PASN_WRAPPED_DATA_NO,
- NULL, 0, comeback,
- hapd->conf->pasn_comeback_after);
- wpabuf_free(comeback);
-
- wpa_printf(MSG_DEBUG,
- "PASN: comeback: 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, "PASN: Failed to send comeback frame 2");
-
- wpabuf_free(buf);
-}
-
-
-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;
- u8 *ptr;
- const u8 *frame, *data, *rsn_ie, *rsnxe_ie;
- u8 *data_buf = NULL;
- size_t rsn_ie_len, frame_len, data_len;
- int ret;
- const u8 *pmkid = NULL;
-
- 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 (pmksa) {
- pmkid = pmksa->pmkid;
-#ifdef CONFIG_SAE
- } else if (sta->pasn->akmp == WPA_KEY_MGMT_SAE) {
- wpa_printf(MSG_DEBUG, "PASN: Use SAE PMKID");
- pmkid = sta->pasn->sae.pmkid;
-#endif /* CONFIG_SAE */
-#ifdef CONFIG_FILS
- } else if (sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
- sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
- wpa_printf(MSG_DEBUG, "PASN: Use FILS ERP PMKID");
- pmkid = sta->pasn->fils.erp_pmkid;
-#endif /* CONFIG_FILS */
- }
-
- if (wpa_pasn_add_rsne(buf, pmkid,
- 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, true, 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;
- }
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (hapd->conf->pasn_corrupt_mic) {
- wpa_printf(MSG_DEBUG, "PASN: frame 2: Corrupt MIC");
- mic[0] = ~mic[0];
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- 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 pasn_data *pasn = sta->pasn;
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, inc_y;
- bool derive_keys;
- u32 i;
- bool derive_kdk;
-
- 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;
-
- derive_kdk = (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP) &&
- ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
- WLAN_RSNX_CAPAB_SECURE_LTF);
-#ifdef CONFIG_TESTING_OPTIONS
- if (!derive_kdk)
- derive_kdk = hapd->conf->force_kdk_derivation;
-#endif /* CONFIG_TESTING_OPTIONS */
- if (derive_kdk)
- sta->pasn->kdk_len = WPA_KDK_MAX_LEN;
- else
- sta->pasn->kdk_len = 0;
- wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", sta->pasn->kdk_len);
-
- if ((hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP) &&
- ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
- WLAN_RSNX_CAPAB_SECURE_LTF))
- sta->pasn->secure_ltf = true;
- else
- sta->pasn->secure_ltf = false;
-
- 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;
- }
-
- if (pasn_params.comeback) {
- wpa_printf(MSG_DEBUG, "PASN: Checking peer comeback token");
-
- ret = check_comeback_token(hapd, sta->addr,
- pasn_params.comeback,
- pasn_params.comeback_len);
-
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Invalid comeback token");
- status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto send_resp;
- }
- } else if (use_anti_clogging(hapd)) {
- wpa_printf(MSG_DEBUG, "PASN: Respond with comeback");
- handle_auth_pasn_comeback(hapd, sta, pasn_params.group);
- ap_free_sta(hapd, sta);
- return;
- }
-
- 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;
-
- if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_UNCOMPRESSED) {
- inc_y = 1;
- } else if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_0 ||
- pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_1) {
- inc_y = 0;
- } else {
- wpa_printf(MSG_DEBUG,
- "PASN: Invalid first octet in pubkey=0x%x",
- pasn_params.pubkey[0]);
- status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto send_resp;
- }
-
- secret = crypto_ecdh_set_peerkey(sta->pasn->ecdh, inc_y,
- pasn_params.pubkey + 1,
- pasn_params.pubkey_len - 1);
- 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,
@@ -3472,100 +2601,62 @@
&elems, 0) == ParseFailed) {
wpa_printf(MSG_DEBUG,
"PASN: Failed parsing Authentication frame");
- goto fail;
+ return;
}
- /* 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) {
+ if (!elems.rsn_ie ||
+ wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+ &rsn_data)) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed parsing RSNE");
+ return;
+ }
+
+ if (!(rsn_data.key_mgmt & pasn->wpa_key_mgmt) ||
+ !(rsn_data.pairwise_cipher & pasn->rsn_pairwise)) {
+ wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher");
+ return;
+ }
+
+ pasn->akmp = rsn_data.key_mgmt;
+ pasn->cipher = rsn_data.pairwise_cipher;
+
+ if (wpa_key_mgmt_ft(pasn->akmp) && rsn_data.num_pmkid) {
+#ifdef CONFIG_IEEE80211R_AP
+ pasn->pmk_r1_len = 0;
+ wpa_ft_fetch_pmk_r1(hapd->wpa_auth, sta->addr,
+ rsn_data.pmkid,
+ pasn->pmk_r1, &pasn->pmk_r1_len, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL);
+#endif /* CONFIG_IEEE80211R_AP */
+ }
+#ifdef CONFIG_FILS
+ if (pasn->akmp != WPA_KEY_MGMT_FILS_SHA256 &&
+ pasn->akmp != WPA_KEY_MGMT_FILS_SHA384)
+ return;
+ if (!elems.pasn_params ||
+ wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
+ elems.pasn_params_len + 3,
+ false, &pasn_params)) {
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);
+ "PASN: Failed validation of PASN Parameters element");
+ return;
}
-
- 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,
+ 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;
+ return;
}
-
-#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);
+ if (pasn_wd_handle_fils(hapd, sta, wrapped_data))
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed processing FILS wrapped data");
+ else
+ pasn->fils_wd_valid = true;
}
-
- wpa_printf(MSG_INFO,
- "PASN: Success handling transaction == 3. Store PTK");
-
- ptksa_cache_add(hapd->ptksa, hapd->own_addr, sta->addr,
- sta->pasn->cipher, 43200, &sta->pasn->ptk, NULL, NULL);
- pasn_set_keys_from_cache(hapd, hapd->own_addr, sta->addr,
- sta->pasn->cipher, sta->pasn->akmp);
-fail:
- ap_free_sta(hapd, sta);
+ wpabuf_free(wrapped_data);
+#endif /* CONFIG_FILS */
}
@@ -3601,7 +2692,12 @@
return;
}
- handle_auth_pasn_1(hapd, sta, mgmt, len);
+ hapd_initialize_pasn(hapd, sta);
+
+ hapd_pasn_update_params(hapd, sta, mgmt, len);
+ if (handle_auth_pasn_1(sta->pasn, hapd->own_addr,
+ sta->addr, mgmt, len) < 0)
+ ap_free_sta(hapd, sta);
} else if (trans_seq == 3) {
if (!sta->pasn) {
wpa_printf(MSG_DEBUG,
@@ -3616,7 +2712,18 @@
return;
}
- handle_auth_pasn_3(hapd, sta, mgmt, len);
+ if (handle_auth_pasn_3(sta->pasn, hapd->own_addr,
+ sta->addr, mgmt, len) == 0) {
+ ptksa_cache_add(hapd->ptksa, hapd->own_addr, sta->addr,
+ sta->pasn->cipher, 43200,
+ &sta->pasn->ptk, NULL, NULL,
+ sta->pasn->akmp);
+
+ pasn_set_keys_from_cache(hapd, hapd->own_addr,
+ sta->addr, sta->pasn->cipher,
+ sta->pasn->akmp);
+ }
+ ap_free_sta(hapd, sta);
} else {
wpa_printf(MSG_DEBUG,
"PASN: Invalid transaction %u - ignore", trans_seq);
@@ -4036,6 +3143,23 @@
}
+static u8 hostapd_max_bssid_indicator(struct hostapd_data *hapd)
+{
+ size_t num_bss_nontx;
+ u8 max_bssid_ind = 0;
+
+ if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1)
+ return 0;
+
+ num_bss_nontx = hapd->iface->num_bss - 1;
+ while (num_bss_nontx > 0) {
+ max_bssid_ind++;
+ num_bss_nontx >>= 1;
+ }
+ return max_bssid_ind;
+}
+
+
int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
{
int i, j = 32, aid;
@@ -4061,7 +3185,7 @@
}
if (j == 32)
return -1;
- aid = i * 32 + j + 1;
+ aid = i * 32 + j + (1 << hostapd_max_bssid_indicator(hapd));
if (aid > 2007)
return -1;
@@ -4605,6 +3729,15 @@
elems.he_capabilities_len);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
+
+ if (hapd->iconf->require_he && !(sta->flags & WLAN_STA_HE)) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Station does not support mandatory HE PHY - reject association");
+ return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
+ }
+
if (is_6ghz_op_class(hapd->iconf->op_class)) {
if (!(sta->flags & WLAN_STA_HE)) {
hostapd_logger(hapd, sta->addr,
@@ -4770,7 +3903,7 @@
return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
}
- if (hapd->conf->sae_pwe == 2 &&
+ if (hapd->conf->sae_pwe == SAE_PWE_BOTH &&
sta->auth_alg == WLAN_AUTH_SAE &&
sta->sae && !sta->sae->h2e &&
ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
@@ -5251,7 +4384,7 @@
}
#endif /* CONFIG_IEEE80211AX */
- p = hostapd_eid_ext_capab(hapd, p);
+ p = hostapd_eid_ext_capab(hapd, p, false);
p = hostapd_eid_bss_max_idle_period(hapd, p);
if (sta && sta->qos_map_enabled)
p = hostapd_eid_qos_map_set(hapd, p);
@@ -6759,6 +5892,19 @@
return;
}
+#ifdef CONFIG_HS20
+ if (ok && len >= IEEE80211_HDRLEN + 2 &&
+ mgmt->u.action.category == WLAN_ACTION_WNM &&
+ mgmt->u.action.u.vs_public_action.action == WNM_NOTIFICATION_REQ &&
+ sta->hs20_deauth_on_ack) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Deauthenticate STA " MACSTR
+ " on acknowledging the WNM-Notification",
+ MAC2STR(sta->addr));
+ ap_sta_session_timeout(hapd, sta, 0);
+ return;
+ }
+#endif /* CONFIG_HS20 */
+
if (len < 24 + 5 + sizeof(*report))
return;
report = (const struct rrm_measurement_report_element *)
@@ -7524,6 +6670,14 @@
reporting_hapd->conf->ssid.short_ssid)
bss_param |= RNR_BSS_PARAM_SAME_SSID;
+ if (iface->conf->mbssid != MBSSID_DISABLED &&
+ iface->num_bss > 1) {
+ bss_param |= RNR_BSS_PARAM_MULTIPLE_BSSID;
+ if (i == 0)
+ bss_param |=
+ RNR_BSS_PARAM_TRANSMITTED_BSSID;
+ }
+
if (is_6ghz_op_class(hapd->iconf->op_class) &&
bss->conf->unsol_bcast_probe_resp_interval)
bss_param |=
@@ -7612,4 +6766,253 @@
return eid;
}
+
+static bool mbssid_known_bss(unsigned int i, const u8 *known_bss,
+ size_t known_bss_len)
+{
+ if (!known_bss || known_bss_len <= i / 8)
+ return false;
+ known_bss = &known_bss[i / 8];
+ return *known_bss & (u8) (BIT(i % 8));
+}
+
+
+static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd,
+ u32 frame_type, size_t *bss_index,
+ const u8 *known_bss,
+ size_t known_bss_len)
+{
+ struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
+ size_t len = 3, i;
+
+ for (i = *bss_index; i < hapd->iface->num_bss; i++) {
+ struct hostapd_data *bss = hapd->iface->bss[i];
+ const u8 *auth, *rsn = NULL, *rsnx = NULL;
+ size_t nontx_profile_len, auth_len;
+ u8 ie_count = 0;
+
+ if (!bss || !bss->conf || !bss->started ||
+ mbssid_known_bss(i, known_bss, known_bss_len))
+ continue;
+
+ /*
+ * Sublement ID: 1 octet
+ * Length: 1 octet
+ * Nontransmitted capabilities: 4 octets
+ * SSID element: 2 + variable
+ * Multiple BSSID Index Element: 3 octets (+2 octets in beacons)
+ * Fixed length = 1 + 1 + 4 + 2 + 3 = 11
+ */
+ nontx_profile_len = 11 + bss->conf->ssid.ssid_len;
+
+ if (frame_type == WLAN_FC_STYPE_BEACON)
+ nontx_profile_len += 2;
+
+ auth = wpa_auth_get_wpa_ie(bss->wpa_auth, &auth_len);
+ if (auth) {
+ rsn = get_ie(auth, auth_len, WLAN_EID_RSN);
+ if (rsn)
+ nontx_profile_len += 2 + rsn[1];
+
+ rsnx = get_ie(auth, auth_len, WLAN_EID_RSNX);
+ if (rsnx)
+ nontx_profile_len += 2 + rsnx[1];
+ }
+ if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN))
+ ie_count++;
+ if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
+ ie_count++;
+ if (bss->conf->xrates_supported)
+ nontx_profile_len += 8;
+ else if (hapd->conf->xrates_supported)
+ ie_count++;
+ if (ie_count)
+ nontx_profile_len += 4 + ie_count;
+
+ if (len + nontx_profile_len > 255)
+ break;
+
+ len += nontx_profile_len;
+ }
+
+ *bss_index = i;
+ return len;
+}
+
+
+size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
+ u8 *elem_count, const u8 *known_bss,
+ size_t known_bss_len)
+{
+ size_t len = 0, bss_index = 1;
+
+ if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
+ (frame_type != WLAN_FC_STYPE_BEACON &&
+ frame_type != WLAN_FC_STYPE_PROBE_RESP))
+ return 0;
+
+ if (frame_type == WLAN_FC_STYPE_BEACON) {
+ if (!elem_count) {
+ wpa_printf(MSG_INFO,
+ "MBSSID: Insufficient data for Beacon frames");
+ return 0;
+ }
+ *elem_count = 0;
+ }
+
+ while (bss_index < hapd->iface->num_bss) {
+ len += hostapd_eid_mbssid_elem_len(hapd, frame_type,
+ &bss_index, known_bss,
+ known_bss_len);
+
+ if (frame_type == WLAN_FC_STYPE_BEACON)
+ *elem_count += 1;
+ }
+ return len;
+}
+
+
+static u8 * hostapd_eid_mbssid_elem(struct hostapd_data *hapd, u8 *eid, u8 *end,
+ u32 frame_type, u8 max_bssid_indicator,
+ size_t *bss_index, u8 elem_count,
+ const u8 *known_bss, size_t known_bss_len)
+{
+ struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
+ size_t i;
+ u8 *eid_len_offset, *max_bssid_indicator_offset;
+
+ *eid++ = WLAN_EID_MULTIPLE_BSSID;
+ eid_len_offset = eid++;
+ max_bssid_indicator_offset = eid++;
+
+ for (i = *bss_index; i < hapd->iface->num_bss; i++) {
+ struct hostapd_data *bss = hapd->iface->bss[i];
+ struct hostapd_bss_config *conf;
+ u8 *eid_len_pos, *nontx_bss_start = eid;
+ const u8 *auth, *rsn = NULL, *rsnx = NULL;
+ u8 ie_count = 0, non_inherit_ie[3];
+ size_t auth_len = 0;
+ u16 capab_info;
+
+ if (!bss || !bss->conf || !bss->started ||
+ mbssid_known_bss(i, known_bss, known_bss_len))
+ continue;
+ conf = bss->conf;
+
+ *eid++ = WLAN_MBSSID_SUBELEMENT_NONTRANSMITTED_BSSID_PROFILE;
+ eid_len_pos = eid++;
+
+ capab_info = hostapd_own_capab_info(bss);
+ *eid++ = WLAN_EID_NONTRANSMITTED_BSSID_CAPA;
+ *eid++ = sizeof(capab_info);
+ WPA_PUT_LE16(eid, capab_info);
+ eid += sizeof(capab_info);
+
+ *eid++ = WLAN_EID_SSID;
+ *eid++ = conf->ssid.ssid_len;
+ os_memcpy(eid, conf->ssid.ssid, conf->ssid.ssid_len);
+ eid += conf->ssid.ssid_len;
+
+ *eid++ = WLAN_EID_MULTIPLE_BSSID_INDEX;
+ if (frame_type == WLAN_FC_STYPE_BEACON) {
+ *eid++ = 3;
+ *eid++ = i; /* BSSID Index */
+ if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
+ (conf->dtim_period % elem_count))
+ conf->dtim_period = elem_count;
+ *eid++ = conf->dtim_period;
+ *eid++ = 0xFF; /* DTIM Count */
+ } else {
+ /* Probe Request frame does not include DTIM Period and
+ * DTIM Count fields. */
+ *eid++ = 1;
+ *eid++ = i; /* BSSID Index */
+ }
+
+ auth = wpa_auth_get_wpa_ie(bss->wpa_auth, &auth_len);
+ if (auth) {
+ rsn = get_ie(auth, auth_len, WLAN_EID_RSN);
+ if (rsn) {
+ os_memcpy(eid, rsn, 2 + rsn[1]);
+ eid += 2 + rsn[1];
+ }
+
+ rsnx = get_ie(auth, auth_len, WLAN_EID_RSNX);
+ if (rsnx) {
+ os_memcpy(eid, rsnx, 2 + rsnx[1]);
+ eid += 2 + rsnx[1];
+ }
+ }
+ if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN))
+ non_inherit_ie[ie_count++] = WLAN_EID_RSN;
+ if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
+ non_inherit_ie[ie_count++] = WLAN_EID_RSNX;
+ if (hapd->conf->xrates_supported &&
+ !bss->conf->xrates_supported)
+ non_inherit_ie[ie_count++] = WLAN_EID_EXT_SUPP_RATES;
+ if (ie_count) {
+ *eid++ = WLAN_EID_EXTENSION;
+ *eid++ = 2 + ie_count;
+ *eid++ = WLAN_EID_EXT_NON_INHERITANCE;
+ *eid++ = ie_count;
+ os_memcpy(eid, non_inherit_ie, ie_count);
+ eid += ie_count;
+ }
+
+ *eid_len_pos = (eid - eid_len_pos) - 1;
+
+ if (((eid - eid_len_offset) - 1) > 255) {
+ eid = nontx_bss_start;
+ break;
+ }
+ }
+
+ *bss_index = i;
+ *max_bssid_indicator_offset = max_bssid_indicator;
+ if (*max_bssid_indicator_offset < 1)
+ *max_bssid_indicator_offset = 1;
+ *eid_len_offset = (eid - eid_len_offset) - 1;
+ return eid;
+}
+
+
+u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
+ unsigned int frame_stype, u8 elem_count,
+ u8 **elem_offset,
+ const u8 *known_bss, size_t known_bss_len)
+{
+ size_t bss_index = 1;
+ u8 elem_index = 0;
+
+ if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
+ (frame_stype != WLAN_FC_STYPE_BEACON &&
+ frame_stype != WLAN_FC_STYPE_PROBE_RESP))
+ return eid;
+
+ if (frame_stype == WLAN_FC_STYPE_BEACON && !elem_offset) {
+ wpa_printf(MSG_INFO,
+ "MBSSID: Insufficient data for Beacon frames");
+ return eid;
+ }
+
+ while (bss_index < hapd->iface->num_bss) {
+ if (frame_stype == WLAN_FC_STYPE_BEACON) {
+ if (elem_index == elem_count) {
+ wpa_printf(MSG_WARNING,
+ "MBSSID: Larger number of elements than there is room in the provided array");
+ break;
+ }
+
+ elem_offset[elem_index] = eid;
+ elem_index = elem_index + 1;
+ }
+ eid = hostapd_eid_mbssid_elem(hapd, eid, end, frame_stype,
+ hostapd_max_bssid_indicator(hapd),
+ &bss_index, elem_count,
+ known_bss, known_bss_len);
+ }
+
+ return eid;
+}
+
#endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index fa1f47b..5f443fc 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -45,7 +45,8 @@
#endif /* NEED_AP_MLME */
u16 hostapd_own_capab_info(struct hostapd_data *hapd);
void ap_ht2040_timeout(void *eloop_data, void *user_data);
-u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid,
+ bool mbssid_complete);
u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid);
@@ -214,5 +215,12 @@
enum ieee80211_op_mode opmode,
const u8 *he_capab, size_t he_capab_len,
const u8 *eht_capab, size_t eht_capab_len);
+size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
+ u8 *elem_count, const u8 *known_bss,
+ size_t known_bss_len);
+u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
+ unsigned int frame_stype, u8 elem_count,
+ u8 **elem_offset,
+ const u8 *known_bss, size_t known_bss_len);
#endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
index ec36a9e..caaadce 100644
--- a/src/ap/ieee802_11_eht.c
+++ b/src/ap/ieee802_11_eht.c
@@ -42,11 +42,29 @@
static u8 ieee80211_eht_mcs_set_size(enum hostapd_hw_mode mode, u8 opclass,
- const u8 *he_phy_cap,
+ u8 he_oper_chwidth, const u8 *he_phy_cap,
const u8 *eht_phy_cap)
{
u8 sz = EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
bool band24, band5, band6;
+ u8 he_phy_cap_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK;
+
+ switch (he_oper_chwidth) {
+ case CONF_OPER_CHWIDTH_80P80MHZ:
+ he_phy_cap_chwidth |=
+ HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
+ /* fall through */
+ case CONF_OPER_CHWIDTH_160MHZ:
+ he_phy_cap_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+ /* fall through */
+ case CONF_OPER_CHWIDTH_80MHZ:
+ case CONF_OPER_CHWIDTH_USE_HT:
+ he_phy_cap_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
+ HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
+ break;
+ }
+
+ he_phy_cap_chwidth &= he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX];
band24 = mode == HOSTAPD_MODE_IEEE80211B ||
mode == HOSTAPD_MODE_IEEE80211G ||
@@ -56,19 +74,18 @@
band6 = is_6ghz_op_class(opclass);
if (band24 &&
- (he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
- HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G) == 0)
+ (he_phy_cap_chwidth & HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G) == 0)
return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY;
if (band5 &&
- (he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+ (he_phy_cap_chwidth &
(HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)) == 0)
return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY;
if (band5 &&
- (he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+ (he_phy_cap_chwidth &
(HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)))
sz += EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
@@ -98,6 +115,7 @@
return 0;
len += ieee80211_eht_mcs_set_size(mode->mode, hapd->iconf->op_class,
+ hapd->iconf->he_oper_chwidth,
mode->he_capab[opmode].phy_cap,
eht_cap->phy_cap);
len += ieee80211_eht_ppet_size(WPA_GET_LE16(&eht_cap->ppet[0]),
@@ -152,6 +170,7 @@
mcs_nss_len = ieee80211_eht_mcs_set_size(mode->mode,
hapd->iconf->op_class,
+ hapd->iconf->he_oper_chwidth,
mode->he_capab[opmode].phy_cap,
eht_cap->phy_cap);
if (mcs_nss_len) {
@@ -178,17 +197,24 @@
struct ieee80211_eht_operation *oper;
u8 *pos = eid, seg0 = 0, seg1 = 0;
enum oper_chan_width chwidth;
+ size_t elen = 1 + 4 + 3;
if (!hapd->iface->current_mode)
return eid;
*pos++ = WLAN_EID_EXTENSION;
- *pos++ = 5;
+ *pos++ = 1 + elen;
*pos++ = WLAN_EID_EXT_EHT_OPERATION;
oper = (struct ieee80211_eht_operation *) pos;
oper->oper_params = EHT_OPER_INFO_PRESENT;
+ /* TODO: Fill in appropriate EHT-MCS max Nss information */
+ oper->basic_eht_mcs_nss_set[0] = 0x11;
+ oper->basic_eht_mcs_nss_set[1] = 0x00;
+ oper->basic_eht_mcs_nss_set[2] = 0x00;
+ oper->basic_eht_mcs_nss_set[3] = 0x00;
+
if (is_6ghz_op_class(conf->op_class))
chwidth = op_class_to_ch_width(conf->op_class);
else
@@ -227,7 +253,7 @@
oper->oper_info.ccfs0 = seg0 ? seg0 : hapd->iconf->channel;
oper->oper_info.ccfs1 = seg1;
- return pos + 4;
+ return pos + elen;
}
@@ -275,6 +301,7 @@
sta_mcs = capab->optional;
if (ieee80211_eht_mcs_set_size(mode->mode, hapd->iconf->op_class,
+ hapd->iconf->he_oper_chwidth,
mode->he_capab[opmode].phy_cap,
mode->eht_capab[opmode].phy_cap) ==
EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY)
@@ -300,7 +327,7 @@
static bool ieee80211_invalid_eht_cap_size(enum hostapd_hw_mode mode,
- u8 opclass,
+ u8 opclass, u8 he_oper_chwidth,
const u8 *he_cap, const u8 *eht_cap,
size_t len)
{
@@ -317,8 +344,8 @@
if (len < cap_len)
return true;
- cap_len += ieee80211_eht_mcs_set_size(mode, opclass, he_phy_cap,
- cap->phy_cap);
+ cap_len += ieee80211_eht_mcs_set_size(mode, opclass, he_oper_chwidth,
+ he_phy_cap, cap->phy_cap);
if (len < cap_len)
return true;
@@ -342,6 +369,7 @@
!he_capab || he_capab_len < IEEE80211_HE_CAPAB_MIN_LEN ||
!eht_capab ||
ieee80211_invalid_eht_cap_size(mode, hapd->iconf->op_class,
+ hapd->iconf->he_oper_chwidth,
he_capab, eht_capab,
eht_capab_len) ||
!check_valid_eht_mcs(hapd, eht_capab, opmode)) {
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index eaeaec5..31dfb62 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -340,7 +340,8 @@
}
-static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
+static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx,
+ bool mbssid_complete)
{
*pos = 0x00;
@@ -364,6 +365,8 @@
*pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
if (hapd->conf->bss_transition)
*pos |= 0x08; /* Bit 19 - BSS Transition */
+ if (hapd->iconf->mbssid)
+ *pos |= 0x40; /* Bit 22 - Multiple BSSID */
break;
case 3: /* Bits 24-31 */
#ifdef CONFIG_WNM_AP
@@ -435,6 +438,11 @@
(hapd->iface->drv_flags &
WPA_DRIVER_FLAGS_BEACON_PROTECTION))
*pos |= 0x10; /* Bit 84 - Beacon Protection Enabled */
+ if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED)
+ *pos |= 0x08; /* Bit 83 - Enhanced multiple BSSID */
+ if (mbssid_complete)
+ *pos |= 0x01; /* Bit 80 - Complete List of NonTxBSSID
+ * Profiles */
break;
case 11: /* Bits 88-95 */
#ifdef CONFIG_SAE_PK
@@ -448,7 +456,8 @@
}
-u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
+u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid,
+ bool mbssid_complete)
{
u8 *pos = eid;
u8 len = EXT_CAPA_MAX_LEN, i;
@@ -459,7 +468,7 @@
*pos++ = WLAN_EID_EXT_CAPAB;
*pos++ = len;
for (i = 0; i < len; i++, pos++) {
- hostapd_ext_capab_byte(hapd, pos, i);
+ hostapd_ext_capab_byte(hapd, pos, i, mbssid_complete);
if (i < hapd->iface->extended_capa_len) {
*pos &= ~hapd->iface->extended_capa_mask[i];
@@ -470,6 +479,13 @@
*pos &= ~hapd->conf->ext_capa_mask[i];
*pos |= hapd->conf->ext_capa[i];
}
+
+ /* Clear bits 83 and 22 if EMA and MBSSID are not enabled
+ * otherwise association fails with some clients */
+ if (i == 10 && hapd->iconf->mbssid < ENHANCED_MBSSID_ENABLED)
+ *pos &= ~0x08;
+ if (i == 2 && !hapd->iconf->mbssid)
+ *pos &= ~0x40;
}
while (len > 0 && eid[1 + len] == 0) {
@@ -1062,10 +1078,11 @@
#endif /* CONFIG_SAE_PK */
if (wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
- (hapd->conf->sae_pwe == 1 || hapd->conf->sae_pwe == 2 ||
+ (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ hapd->conf->sae_pwe == SAE_PWE_BOTH ||
hostapd_sae_pw_id_in_use(hapd->conf) || sae_pk ||
wpa_key_mgmt_sae_ext_key(hapd->conf->wpa_key_mgmt)) &&
- hapd->conf->sae_pwe != 3) {
+ hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK) {
capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
#ifdef CONFIG_SAE_PK
if (sae_pk)
@@ -1078,7 +1095,7 @@
if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_AP)
capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP)
- capab |= BIT(WLAN_RSNX_CAPAB_PROT_RANGE_NEG);
+ capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
flen = (capab & 0xff00) ? 2 : 1;
if (len < 2 + flen || !capab)
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index d90792c..46a47d0 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -1709,23 +1709,35 @@
static void ieee802_1x_hs20_deauth_req(struct hostapd_data *hapd,
- struct sta_info *sta, u8 *pos,
+ struct sta_info *sta, const u8 *pos,
size_t len)
{
+ size_t url_len;
+ unsigned int timeout;
+
if (len < 3)
return; /* Malformed information */
+ url_len = len - 3;
sta->hs20_deauth_requested = 1;
+ sta->hs20_deauth_on_ack = url_len == 0;
wpa_printf(MSG_DEBUG,
- "HS 2.0: Deauthentication request - Code %u Re-auth Delay %u",
- *pos, WPA_GET_LE16(pos + 1));
+ "HS 2.0: Deauthentication request - Code %u Re-auth Delay %u URL length %zu",
+ *pos, WPA_GET_LE16(pos + 1), url_len);
wpabuf_free(sta->hs20_deauth_req);
sta->hs20_deauth_req = wpabuf_alloc(len + 1);
if (sta->hs20_deauth_req) {
wpabuf_put_data(sta->hs20_deauth_req, pos, 3);
- wpabuf_put_u8(sta->hs20_deauth_req, len - 3);
- wpabuf_put_data(sta->hs20_deauth_req, pos + 3, len - 3);
+ wpabuf_put_u8(sta->hs20_deauth_req, url_len);
+ wpabuf_put_data(sta->hs20_deauth_req, pos + 3, url_len);
}
- ap_sta_session_timeout(hapd, sta, hapd->conf->hs20_deauth_req_timeout);
+ timeout = hapd->conf->hs20_deauth_req_timeout;
+ /* If there is no URL, no need to provide time to fetch it. Use a short
+ * timeout here to allow maximum time for completing 4-way handshake and
+ * WNM-Notification delivery. Acknowledgement of the frame will result
+ * in cutting this wait further. */
+ if (!url_len && timeout > 2)
+ timeout = 2;
+ ap_sta_session_timeout(hapd, sta, timeout);
}
@@ -1813,6 +1825,7 @@
buf = NULL;
sta->remediation = 0;
sta->hs20_deauth_requested = 0;
+ sta->hs20_deauth_on_ack = 0;
for (;;) {
if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index b67b852..32d291d 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -40,6 +40,7 @@
{
os_free(entry->vlan_desc);
os_free(entry->identity);
+ os_free(entry->dpp_pkhash);
wpabuf_free(entry->cui);
#ifndef CONFIG_NO_RADIUS
radius_free_class(&entry->radius_class);
diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
index 2ef2174..e3cee4a 100644
--- a/src/ap/pmksa_cache_auth.h
+++ b/src/ap/pmksa_cache_auth.h
@@ -23,6 +23,8 @@
int akmp; /* WPA_KEY_MGMT_* */
u8 spa[ETH_ALEN];
+ u8 *dpp_pkhash; /* SHA256_MAC_LEN octet hash value of DPP Connector
+ * public key */
u8 *identity;
size_t identity_len;
struct wpabuf *cui;
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index c541926..63f514c 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -1260,6 +1260,13 @@
}
+const u8 * ap_sta_wpa_get_dpp_pkhash(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ return wpa_auth_get_dpp_pkhash(sta->wpa_sm);
+}
+
+
void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
int authorized)
{
@@ -1298,10 +1305,13 @@
sta->addr, authorized, dev_addr);
if (authorized) {
+ const u8 *dpp_pkhash;
const char *keyid;
+ char dpp_pkhash_buf[100];
char keyid_buf[100];
char ip_addr[100];
+ dpp_pkhash_buf[0] = '\0';
keyid_buf[0] = '\0';
ip_addr[0] = '\0';
#ifdef CONFIG_P2P
@@ -1319,14 +1329,27 @@
" keyid=%s", keyid);
}
- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s",
- buf, ip_addr, keyid_buf);
+ dpp_pkhash = ap_sta_wpa_get_dpp_pkhash(hapd, sta);
+ if (dpp_pkhash) {
+ const char *prefix = " dpp_pkhash=";
+ size_t plen = os_strlen(prefix);
+
+ os_strlcpy(dpp_pkhash_buf, prefix,
+ sizeof(dpp_pkhash_buf));
+ wpa_snprintf_hex(&dpp_pkhash_buf[plen],
+ sizeof(dpp_pkhash_buf) - plen,
+ dpp_pkhash, SHA256_MAC_LEN);
+ }
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s",
+ buf, ip_addr, keyid_buf, dpp_pkhash_buf);
if (hapd->msg_ctx_parent &&
hapd->msg_ctx_parent != hapd->msg_ctx)
wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
- AP_STA_CONNECTED "%s%s%s",
- buf, ip_addr, keyid_buf);
+ AP_STA_CONNECTED "%s%s%s%s",
+ buf, ip_addr, keyid_buf,
+ dpp_pkhash_buf);
} else {
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index d2a8344..b59b758 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -16,6 +16,7 @@
#include "common/ieee802_11_defs.h"
#include "common/sae.h"
#include "crypto/sha384.h"
+#include "pasn/pasn_common.h"
/* STA flags */
#define WLAN_STA_AUTH BIT(0)
@@ -68,44 +69,6 @@
enum frame_encryption encrypted;
};
-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;
- bool secure_ltf;
- u8 trans_seq;
- u8 wrapped_data_format;
- size_t kdk_len;
-
- 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 */
@@ -155,6 +118,7 @@
unsigned int qos_map_enabled:1;
unsigned int remediation:1;
unsigned int hs20_deauth_requested:1;
+ unsigned int hs20_deauth_on_ack:1;
unsigned int session_timeout_set:1;
unsigned int radius_das_match:1;
unsigned int ecsa_supported:1;
@@ -390,6 +354,8 @@
int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta);
const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd,
struct sta_info *sta);
+const u8 * ap_sta_wpa_get_dpp_pkhash(struct hostapd_data *hapd,
+ struct sta_info *sta);
void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *addr, u16 reason);
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 64a1800..7aff64f 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -23,6 +23,7 @@
#include "crypto/sha1.h"
#include "crypto/sha256.h"
#include "crypto/sha384.h"
+#include "crypto/sha512.h"
#include "crypto/random.h"
#include "eapol_auth/eapol_auth_sm.h"
#include "drivers/driver.h"
@@ -1652,22 +1653,25 @@
if (pad_len)
*pos++ = 0xdd;
- wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
+ wpa_hexdump_key(MSG_DEBUG,
+ "Plaintext EAPOL-Key Key Data (+ padding)",
buf, key_data_len);
if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
wpa_use_aes_key_wrap(sm->wpa_key_mgmt) ||
version == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
- wpa_printf(MSG_DEBUG,
- "WPA: Encrypt Key Data using AES-WRAP (KEK length %zu)",
- sm->PTK.kek_len);
+ wpa_hexdump_key(MSG_DEBUG, "RSN: AES-WRAP using KEK",
+ sm->PTK.kek, sm->PTK.kek_len);
if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len,
(key_data_len - 8) / 8, buf, key_data)) {
os_free(hdr);
bin_clear_free(buf, key_data_len);
return;
}
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Encrypted Key Data from AES-WRAP",
+ key_data, key_data_len);
WPA_PUT_BE16(key_mic + mic_len, key_data_len);
-#ifndef CONFIG_NO_RC4
+#if !defined(CONFIG_NO_RC4) && !defined(CONFIG_FIPS)
} else if (sm->PTK.kek_len == 16) {
u8 ek[32];
@@ -1681,7 +1685,7 @@
os_memcpy(key_data, buf, key_data_len);
rc4_skip(ek, 32, 256, key_data, key_data_len);
WPA_PUT_BE16(key_mic + mic_len, key_data_len);
-#endif /* CONFIG_NO_RC4 */
+#endif /* !(CONFIG_NO_RC4 || CONFIG_FIPS) */
} else {
os_free(hdr);
bin_clear_free(buf, key_data_len);
@@ -1716,6 +1720,7 @@
}
wpa_auth_set_eapol(wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx, 1);
+ wpa_hexdump(MSG_DEBUG, "Send EAPOL-Key msg", hdr, len);
wpa_auth_send_eapol(wpa_auth, sm->addr, (u8 *) hdr, len,
sm->pairwise_set);
os_free(hdr);
@@ -1735,10 +1740,25 @@
if (!sm)
return;
+ ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ /* When delay_eapol_tx is true, delay the EAPOL-Key transmission by
+ * sending it only on the last attempt after all timeouts for the prior
+ * skipped attemps. */
+ if (wpa_auth->conf.delay_eapol_tx &&
+ ctr != wpa_auth->conf.wpa_pairwise_update_count) {
+ wpa_msg(sm->wpa_auth->conf.msg_ctx, MSG_INFO,
+ "DELAY-EAPOL-TX-%d", ctr);
+ goto skip_tx;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
__wpa_send_eapol(wpa_auth, sm, key_info, key_rsc, nonce, kde, kde_len,
keyidx, encr, 0);
+#ifdef CONFIG_TESTING_OPTIONS
+skip_tx:
+#endif /* CONFIG_TESTING_OPTIONS */
- ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr;
if (ctr == 1 && wpa_auth->conf.tx_status)
timeout_ms = pairwise ? eapol_key_timeout_first :
eapol_key_timeout_first_group;
@@ -2192,13 +2212,20 @@
os_memcpy(sm->PMK, psk, psk_len);
sm->pmk_len = psk_len;
#ifdef CONFIG_IEEE80211R_AP
- os_memcpy(sm->xxkey, psk, PMK_LEN);
sm->xxkey_len = PMK_LEN;
+#ifdef CONFIG_SAE
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ (psk_len == SHA512_MAC_LEN || psk_len == SHA384_MAC_LEN ||
+ psk_len == SHA256_MAC_LEN))
+ sm->xxkey_len = psk_len;
+#endif /* CONFIG_SAE */
+ os_memcpy(sm->xxkey, psk, sm->xxkey_len);
#endif /* CONFIG_IEEE80211R_AP */
}
#ifdef CONFIG_SAE
if (wpa_auth_uses_sae(sm) && sm->pmksa) {
- wpa_printf(MSG_DEBUG, "SAE: PMK from PMKSA cache");
+ wpa_printf(MSG_DEBUG, "SAE: PMK from PMKSA cache (len=%zu)",
+ sm->pmksa->pmk_len);
os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
sm->pmk_len = sm->pmksa->pmk_len;
#ifdef CONFIG_IEEE80211R_AP
@@ -2460,7 +2487,6 @@
struct wpa_authenticator *wpa_auth = sm->wpa_auth;
struct wpa_auth_config *conf = &wpa_auth->conf;
u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
- int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
if (wpa_derive_pmk_r0(fils_ft, fils_ft_len,
conf->ssid, conf->ssid_len,
@@ -2468,7 +2494,7 @@
conf->r0_key_holder,
conf->r0_key_holder_len,
sm->addr, pmk_r0, pmk_r0_name,
- use_sha384) < 0)
+ sm->wpa_key_mgmt) < 0)
return -1;
wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name);
@@ -2476,7 +2502,7 @@
res = wpa_derive_pmk_r1_name(pmk_r0_name, conf->r1_key_holder,
sm->addr, sm->pmk_r1_name,
- use_sha384);
+ fils_ft_len);
forced_memzero(pmk_r0, PMK_LEN_MAX);
if (res < 0)
return -1;
@@ -3694,9 +3720,8 @@
2 + sm->assoc_resp_ftie[1]);
res = 2 + sm->assoc_resp_ftie[1];
} else {
- int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
-
- res = wpa_write_ftie(conf, use_sha384,
+ res = wpa_write_ftie(conf, sm->wpa_key_mgmt,
+ sm->xxkey_len,
conf->r0_key_holder,
conf->r0_key_holder_len,
NULL, NULL, pos,
@@ -4814,11 +4839,13 @@
"wpa=%d\n"
"AKMSuiteSelector=" RSN_SUITE "\n"
"hostapdWPAPTKState=%d\n"
- "hostapdWPAPTKGroupState=%d\n",
+ "hostapdWPAPTKGroupState=%d\n"
+ "hostapdMFPR=%d\n",
sm->wpa,
RSN_SUITE_ARG(wpa_akm_to_suite(sm->wpa_key_mgmt)),
sm->wpa_ptk_state,
- sm->wpa_ptk_group_state);
+ sm->wpa_ptk_group_state,
+ sm->mfpr);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
@@ -4855,6 +4882,14 @@
}
+const u8 * wpa_auth_get_dpp_pkhash(struct wpa_state_machine *sm)
+{
+ if (!sm || !sm->pmksa)
+ return NULL;
+ return sm->pmksa->dpp_pkhash;
+}
+
+
int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm)
{
if (!sm)
@@ -5017,6 +5052,29 @@
}
+int wpa_auth_pmksa_add3(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ int session_timeout, int akmp, const u8 *dpp_pkhash)
+{
+ struct rsn_pmksa_cache_entry *entry;
+
+ if (wpa_auth->conf.disable_pmksa_caching)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK (3)", pmk, PMK_LEN);
+ entry = pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
+ NULL, 0, wpa_auth->addr, addr, session_timeout,
+ NULL, akmp);
+ if (!entry)
+ return -1;
+
+ if (dpp_pkhash)
+ entry->dpp_pkhash = os_memdup(dpp_pkhash, SHA256_MAC_LEN);
+
+ return 0;
+}
+
+
void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr)
{
@@ -5102,6 +5160,15 @@
#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+struct rsn_pmksa_cache *
+wpa_auth_get_pmksa_cache(struct wpa_authenticator *wpa_auth)
+{
+ if (!wpa_auth || !wpa_auth->pmksa)
+ return NULL;
+ return wpa_auth->pmksa;
+}
+
+
struct rsn_pmksa_cache_entry *
wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
const u8 *pmkid)
@@ -5462,13 +5529,14 @@
#ifdef CONFIG_IEEE80211R_AP
-int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384,
+int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
u8 *buf, size_t len)
{
struct wpa_auth_config *conf = &wpa_auth->conf;
- return wpa_write_ftie(conf, use_sha384, conf->r0_key_holder,
- conf->r0_key_holder_len,
+ return wpa_write_ftie(conf, sm->wpa_key_mgmt, sm->xxkey_len,
+ conf->r0_key_holder, conf->r0_key_holder_len,
NULL, NULL, buf, len, NULL, 0, 0);
}
#endif /* CONFIG_IEEE80211R_AP */
@@ -5682,9 +5750,8 @@
2 + sm->assoc_resp_ftie[1]);
res = 2 + sm->assoc_resp_ftie[1];
} else {
- int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
-
- res = wpa_write_ftie(conf, use_sha384,
+ res = wpa_write_ftie(conf, sm->wpa_key_mgmt,
+ sm->xxkey_len,
conf->r0_key_holder,
conf->r0_key_holder_len,
NULL, NULL, pos,
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index a18f7cb..3b32fe3 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -242,6 +242,7 @@
int ft_rsnxe_used;
unsigned int skip_send_eapol:1;
unsigned int enable_eapol_large_timeout:1;
+ bool delay_eapol_tx;
#endif /* CONFIG_TESTING_OPTIONS */
unsigned int oci_freq_override_eapol_m3;
unsigned int oci_freq_override_eapol_g1;
@@ -257,7 +258,7 @@
unsigned int fils_cache_id_set:1;
u8 fils_cache_id[FILS_CACHE_ID_LEN];
#endif /* CONFIG_FILS */
- int sae_pwe;
+ enum sae_pwe sae_pwe;
bool sae_pk;
unsigned int secure_ltf:1;
@@ -412,6 +413,7 @@
int wpa_auth_pairwise_set(struct wpa_state_machine *sm);
int wpa_auth_get_pairwise(struct wpa_state_machine *sm);
const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len);
+const u8 * wpa_auth_get_dpp_pkhash(struct wpa_state_machine *sm);
int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm);
int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm);
int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm);
@@ -437,6 +439,9 @@
int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *pmk, size_t pmk_len, const u8 *pmkid,
int session_timeout, int akmp);
+int wpa_auth_pmksa_add3(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ int session_timeout, int akmp, const u8 *dpp_pkhash);
void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr);
int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
@@ -450,6 +455,8 @@
const u8 *pmkid, int expiration);
int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth,
struct rsn_pmksa_cache_entry *entry);
+struct rsn_pmksa_cache *
+wpa_auth_get_pmksa_cache(struct wpa_authenticator *wpa_auth);
struct rsn_pmksa_cache_entry *
wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
const u8 *pmkid);
@@ -537,7 +544,8 @@
int get_sta_tx_parameters(struct wpa_state_machine *sm, int ap_max_chanwidth,
int ap_seg1_idx, int *bandwidth, int *seg1_idx);
-int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384,
+int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
u8 *buf, size_t len);
void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
u8 *fils_anonce, u8 *fils_snonce,
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 1b1324b..88d63bb 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -20,6 +20,7 @@
#include "crypto/aes_siv.h"
#include "crypto/aes_wrap.h"
#include "crypto/sha384.h"
+#include "crypto/sha512.h"
#include "crypto/random.h"
#include "ap_config.h"
#include "ieee802_11.h"
@@ -805,15 +806,29 @@
}
-int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384,
+int wpa_write_ftie(struct wpa_auth_config *conf, int key_mgmt, size_t key_len,
const u8 *r0kh_id, size_t r0kh_id_len,
const u8 *anonce, const u8 *snonce,
u8 *buf, size_t len, const u8 *subelem,
size_t subelem_len, int rsnxe_used)
{
u8 *pos = buf, *ielen;
- size_t hdrlen = use_sha384 ? sizeof(struct rsn_ftie_sha384) :
- sizeof(struct rsn_ftie);
+ size_t hdrlen;
+ u16 mic_control = rsnxe_used ? FTE_MIC_CTRL_RSNXE_USED : 0;
+
+ if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ key_len == SHA256_MAC_LEN)
+ hdrlen = sizeof(struct rsn_ftie);
+ else if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ key_len == SHA384_MAC_LEN)
+ hdrlen = sizeof(struct rsn_ftie_sha384);
+ else if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ key_len == SHA512_MAC_LEN)
+ hdrlen = sizeof(struct rsn_ftie_sha512);
+ else if (wpa_key_mgmt_sha384(key_mgmt))
+ hdrlen = sizeof(struct rsn_ftie_sha384);
+ else
+ hdrlen = sizeof(struct rsn_ftie);
if (len < 2 + hdrlen + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len +
subelem_len)
@@ -822,12 +837,27 @@
*pos++ = WLAN_EID_FAST_BSS_TRANSITION;
ielen = pos++;
- if (use_sha384) {
+ if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ key_len == SHA512_MAC_LEN) {
+ struct rsn_ftie_sha512 *hdr = (struct rsn_ftie_sha512 *) pos;
+
+ os_memset(hdr, 0, sizeof(*hdr));
+ pos += sizeof(*hdr);
+ mic_control |= FTE_MIC_LEN_32 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+ WPA_PUT_LE16(hdr->mic_control, mic_control);
+ if (anonce)
+ os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
+ if (snonce)
+ os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
+ } else if ((key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ key_len == SHA384_MAC_LEN) ||
+ wpa_key_mgmt_sha384(key_mgmt)) {
struct rsn_ftie_sha384 *hdr = (struct rsn_ftie_sha384 *) pos;
os_memset(hdr, 0, sizeof(*hdr));
pos += sizeof(*hdr);
- WPA_PUT_LE16(hdr->mic_control, !!rsnxe_used);
+ mic_control |= FTE_MIC_LEN_24 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+ WPA_PUT_LE16(hdr->mic_control, mic_control);
if (anonce)
os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
if (snonce)
@@ -837,7 +867,8 @@
os_memset(hdr, 0, sizeof(*hdr));
pos += sizeof(*hdr);
- WPA_PUT_LE16(hdr->mic_control, !!rsnxe_used);
+ mic_control |= FTE_MIC_LEN_16 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+ WPA_PUT_LE16(hdr->mic_control, mic_control);
if (anonce)
os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
if (snonce)
@@ -2081,9 +2112,7 @@
int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk)
{
u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
- size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ?
- SHA384_MAC_LEN : PMK_LEN;
- size_t pmk_r1_len = pmk_r0_len;
+ size_t pmk_r0_len, pmk_r1_len;
u8 pmk_r1[PMK_LEN_MAX];
u8 ptk_name[WPA_PMK_NAME_LEN];
const u8 *mdid = sm->wpa_auth->conf.mobility_domain;
@@ -2101,6 +2130,17 @@
const u8 *mpmk;
size_t mpmk_len;
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ (sm->xxkey_len == SHA256_MAC_LEN ||
+ sm->xxkey_len == SHA384_MAC_LEN ||
+ sm->xxkey_len == SHA512_MAC_LEN))
+ pmk_r0_len = sm->xxkey_len;
+ else if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt))
+ pmk_r0_len = SHA384_MAC_LEN;
+ else
+ pmk_r0_len = PMK_LEN;
+ pmk_r1_len = pmk_r0_len;
+
if (sm->xxkey_len > 0) {
mpmk = sm->xxkey;
mpmk_len = sm->xxkey_len;
@@ -2127,7 +2167,7 @@
if (wpa_derive_pmk_r0(mpmk, mpmk_len, ssid, ssid_len, mdid,
r0kh, r0kh_len, sm->addr,
pmk_r0, pmk_r0_name,
- wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) < 0)
+ sm->wpa_key_mgmt) < 0)
return -1;
if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len,
@@ -2521,12 +2561,11 @@
u8 *anonce, *snonce;
const u8 *kck;
size_t kck_len;
- int use_sha384;
+ size_t key_len;
if (sm == NULL)
return pos;
- use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
conf = &sm->wpa_auth->conf;
if (!wpa_key_mgmt_ft(sm->wpa_key_mgmt))
@@ -2697,7 +2736,8 @@
snonce = NULL;
}
rsnxe_used = (auth_alg == WLAN_AUTH_FT) &&
- (conf->sae_pwe == 1 || conf->sae_pwe == 2);
+ (conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ conf->sae_pwe == SAE_PWE_BOTH);
#ifdef CONFIG_TESTING_OPTIONS
if (sm->wpa_auth->conf.ft_rsnxe_used) {
rsnxe_used = sm->wpa_auth->conf.ft_rsnxe_used == 1;
@@ -2705,7 +2745,20 @@
rsnxe_used);
}
#endif /* CONFIG_TESTING_OPTIONS */
- res = wpa_write_ftie(conf, use_sha384, r0kh_id, r0kh_id_len,
+ key_len = sm->xxkey_len;
+ if (!key_len)
+ key_len = sm->pmk_r1_len;
+ if (!key_len && sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ sm->wpa_auth->cb->get_psk) {
+ size_t psk_len;
+
+ if (sm->wpa_auth->cb->get_psk(sm->wpa_auth->cb_ctx,
+ sm->addr, sm->p2p_dev_addr,
+ NULL, &psk_len, NULL))
+ key_len = psk_len;
+ }
+ res = wpa_write_ftie(conf, sm->wpa_key_mgmt, key_len,
+ r0kh_id, r0kh_id_len,
anonce, snonce, pos, end - pos,
subelem, subelem_len, rsnxe_used);
os_free(subelem);
@@ -2715,7 +2768,16 @@
ftie_len = res;
pos += res;
- if (use_sha384) {
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ key_len == SHA512_MAC_LEN) {
+ struct rsn_ftie_sha512 *_ftie =
+ (struct rsn_ftie_sha512 *) (ftie + 2);
+
+ fte_mic = _ftie->mic;
+ elem_count = &_ftie->mic_control[1];
+ } else if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ key_len == SHA384_MAC_LEN) ||
+ wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
struct rsn_ftie_sha384 *_ftie =
(struct rsn_ftie_sha384 *) (ftie + 2);
@@ -2731,8 +2793,8 @@
*elem_count = 3; /* Information element count */
ric_start = pos;
- if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse, use_sha384) == 0
- && parse.ric) {
+ if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse,
+ sm->wpa_key_mgmt) == 0 && parse.ric) {
pos = wpa_ft_process_ric(sm, pos, end, parse.ric,
parse.ric_len);
if (auth_alg == WLAN_AUTH_FT)
@@ -2772,7 +2834,8 @@
kck_len = sm->PTK.kck_len;
}
if (auth_alg == WLAN_AUTH_FT &&
- wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 6,
+ wpa_ft_mic(sm->wpa_key_mgmt, kck, kck_len,
+ sm->addr, sm->wpa_auth->addr, 6,
mdie, mdie_len, ftie, ftie_len,
rsnie, rsnie_len,
ric_start, ric_start ? pos - ric_start : 0,
@@ -2913,7 +2976,8 @@
if (wpa_derive_pmk_r0(pmk, PMK_LEN, ssid, ssid_len, mdid, r0kh,
r0kh_len, sm->addr,
- pmk_r0, pmk_r0_name, 0) < 0 ||
+ pmk_r0, pmk_r0_name,
+ WPA_KEY_MGMT_FT_PSK) < 0 ||
wpa_derive_pmk_r1(pmk_r0, PMK_LEN, pmk_r0_name, r1kh,
sm->addr, pmk_r1, pmk_r1_name) < 0 ||
os_memcmp_const(pmk_r1_name, req_pmk_r1_name,
@@ -3013,7 +3077,8 @@
const u8 **identity, size_t *identity_len,
const u8 **radius_cui,
size_t *radius_cui_len,
- int *out_session_timeout)
+ int *out_session_timeout,
+ size_t *pmk_r1_len)
{
struct wpa_auth_config *conf = &wpa_auth->conf;
const struct wpa_ft_pmk_r0_sa *r0;
@@ -3072,6 +3137,8 @@
*out_session_timeout = session_timeout;
+ *pmk_r1_len = r0->pmk_r0_len;
+
return 0;
}
@@ -3092,8 +3159,7 @@
struct vlan_description vlan;
const u8 *identity, *radius_cui;
size_t identity_len = 0, radius_cui_len = 0;
- int use_sha384;
- size_t pmk_r1_len, kdk_len;
+ size_t pmk_r1_len, kdk_len, len;
*resp_ies = NULL;
*resp_ies_len = 0;
@@ -3104,12 +3170,10 @@
wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs",
ies, ies_len);
- if (wpa_ft_parse_ies(ies, ies_len, &parse, -1)) {
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, 0)) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
- use_sha384 = wpa_key_mgmt_sha384(parse.key_mgmt);
- pmk_r1_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
mdie = (struct rsn_mdie *) parse.mdie;
if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
@@ -3120,26 +3184,9 @@
return WLAN_STATUS_INVALID_MDIE;
}
- if (use_sha384) {
- struct rsn_ftie_sha384 *ftie;
-
- ftie = (struct rsn_ftie_sha384 *) parse.ftie;
- if (!ftie || parse.ftie_len < sizeof(*ftie)) {
- wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
- return WLAN_STATUS_INVALID_FTIE;
- }
-
- os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
- } else {
- struct rsn_ftie *ftie;
-
- ftie = (struct rsn_ftie *) parse.ftie;
- if (!ftie || parse.ftie_len < sizeof(*ftie)) {
- wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
- return WLAN_STATUS_INVALID_FTIE;
- }
-
- os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+ if (!parse.ftie || parse.ftie_len < sizeof(struct rsn_ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
}
if (parse.r0kh_id == NULL) {
@@ -3162,49 +3209,73 @@
wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name",
parse.rsn_pmkid, WPA_PMK_NAME_LEN);
- if (wpa_derive_pmk_r1_name(parse.rsn_pmkid,
- sm->wpa_auth->conf.r1_key_holder, sm->addr,
- pmk_r1_name, use_sha384) < 0)
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
if (conf->ft_psk_generate_local &&
wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
+ if (wpa_derive_pmk_r1_name(parse.rsn_pmkid,
+ sm->wpa_auth->conf.r1_key_holder,
+ sm->addr, pmk_r1_name, PMK_LEN) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
if (wpa_ft_psk_pmk_r1(sm, pmk_r1_name, pmk_r1, &pairwise,
&vlan, &identity, &identity_len,
&radius_cui, &radius_cui_len,
&session_timeout) < 0)
return WLAN_STATUS_INVALID_PMKID;
+ pmk_r1_len = PMK_LEN;
wpa_printf(MSG_DEBUG,
"FT: Generated PMK-R1 for FT-PSK locally");
- } else if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name,
- pmk_r1, &pmk_r1_len, &pairwise, &vlan,
- &identity, &identity_len, &radius_cui,
- &radius_cui_len, &session_timeout) < 0) {
- wpa_printf(MSG_DEBUG,
- "FT: No PMK-R1 available in local cache for the requested PMKR1Name");
- if (wpa_ft_local_derive_pmk_r1(sm->wpa_auth, sm,
- parse.r0kh_id, parse.r0kh_id_len,
- parse.rsn_pmkid,
- pmk_r1_name, pmk_r1, &pairwise,
- &vlan, &identity, &identity_len,
- &radius_cui, &radius_cui_len,
- &session_timeout) == 0) {
+ goto pmk_r1_derived;
+ }
+
+ /* Need to test all possible hash algorithms for FT-SAE-EXT-KEY since
+ * the key length is not yet known. For other AKMs, only the length
+ * identified by the AKM is used. */
+ for (len = SHA256_MAC_LEN; len <= SHA512_MAC_LEN; len += 16) {
+ if (parse.key_mgmt != WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ ((wpa_key_mgmt_sha384(parse.key_mgmt) &&
+ len != SHA384_MAC_LEN) ||
+ (!wpa_key_mgmt_sha384(parse.key_mgmt) &&
+ len != SHA256_MAC_LEN)))
+ continue;
+ if (wpa_derive_pmk_r1_name(parse.rsn_pmkid,
+ sm->wpa_auth->conf.r1_key_holder,
+ sm->addr, pmk_r1_name, len) < 0)
+ continue;
+
+ if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name,
+ pmk_r1, &pmk_r1_len, &pairwise, &vlan,
+ &identity, &identity_len, &radius_cui,
+ &radius_cui_len,
+ &session_timeout) == 0) {
wpa_printf(MSG_DEBUG,
- "FT: Generated PMK-R1 based on local PMK-R0");
+ "FT: Found PMKR1Name (using SHA%zu) from local cache",
+ pmk_r1_len * 8);
goto pmk_r1_derived;
}
-
- if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
- wpa_printf(MSG_DEBUG,
- "FT: Did not have matching PMK-R1 and either unknown or blocked R0KH-ID or NAK from R0KH");
- return WLAN_STATUS_INVALID_PMKID;
- }
-
- return -1; /* Status pending */
- } else {
- wpa_printf(MSG_DEBUG, "FT: Found PMKR1Name from local cache");
}
+ wpa_printf(MSG_DEBUG,
+ "FT: No PMK-R1 available in local cache for the requested PMKR1Name");
+ if (wpa_ft_local_derive_pmk_r1(sm->wpa_auth, sm,
+ parse.r0kh_id, parse.r0kh_id_len,
+ parse.rsn_pmkid,
+ pmk_r1_name, pmk_r1, &pairwise,
+ &vlan, &identity, &identity_len,
+ &radius_cui, &radius_cui_len,
+ &session_timeout, &pmk_r1_len) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Generated PMK-R1 based on local PMK-R0");
+ goto pmk_r1_derived;
+ }
+
+ if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Did not have matching PMK-R1 and either unknown or blocked R0KH-ID or NAK from R0KH");
+ return WLAN_STATUS_INVALID_PMKID;
+ }
+
+ return -1; /* Status pending */
+
pmk_r1_derived:
wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, pmk_r1_len);
sm->pmk_r1_name_valid = 1;
@@ -3218,6 +3289,40 @@
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
+ /* Now that we know the correct PMK-R1 length and as such, the length
+ * of the MIC field, fetch the SNonce. */
+ if (pmk_r1_len == SHA512_MAC_LEN) {
+ const struct rsn_ftie_sha512 *ftie;
+
+ ftie = (const struct rsn_ftie_sha512 *) parse.ftie;
+ if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+ } else if (pmk_r1_len == SHA384_MAC_LEN) {
+ const struct rsn_ftie_sha384 *ftie;
+
+ ftie = (const struct rsn_ftie_sha384 *) parse.ftie;
+ if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+ } else {
+ const struct rsn_ftie *ftie;
+
+ ftie = (const struct rsn_ftie *) parse.ftie;
+ if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+ }
+
wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
sm->SNonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce",
@@ -3232,14 +3337,14 @@
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,
+ &sm->PTK, ptk_name, parse.key_mgmt,
pairwise, kdk_len) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
#ifdef CONFIG_PASN
if (sm->wpa_auth->conf.secure_ltf &&
ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
- wpa_ltf_keyseed(&sm->PTK, sm->wpa_key_mgmt, pairwise)) {
+ wpa_ltf_keyseed(&sm->PTK, parse.key_mgmt, pairwise)) {
wpa_printf(MSG_DEBUG, "FT: Failed to derive LTF keyseed");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
@@ -3282,7 +3387,8 @@
goto fail;
pos += ret;
- ret = wpa_write_ftie(conf, use_sha384, parse.r0kh_id, parse.r0kh_id_len,
+ ret = wpa_write_ftie(conf, parse.key_mgmt, pmk_r1_len,
+ parse.r0kh_id, parse.r0kh_id_len,
sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0,
0);
if (ret < 0)
@@ -3349,25 +3455,20 @@
struct wpa_ft_ies parse;
struct rsn_mdie *mdie;
u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
- size_t mic_len = 16;
+ size_t mic_len;
unsigned int count;
const u8 *kck;
size_t kck_len;
- int use_sha384;
- const u8 *anonce, *snonce, *fte_mic;
- u8 fte_elem_count;
- int rsnxe_used;
struct wpa_auth_config *conf;
if (sm == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
conf = &sm->wpa_auth->conf;
- use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len);
- if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) {
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, sm->wpa_key_mgmt) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
@@ -3397,55 +3498,42 @@
return WLAN_STATUS_INVALID_MDIE;
}
- if (use_sha384) {
- struct rsn_ftie_sha384 *ftie;
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ sm->pmk_r1_len == SHA512_MAC_LEN)
+ mic_len = 32;
+ else if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ sm->pmk_r1_len == SHA384_MAC_LEN) ||
+ wpa_key_mgmt_sha384(sm->wpa_key_mgmt))
+ mic_len = 24;
+ else
+ mic_len = 16;
- ftie = (struct rsn_ftie_sha384 *) parse.ftie;
- if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
- wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
- return WLAN_STATUS_INVALID_FTIE;
- }
-
- anonce = ftie->anonce;
- snonce = ftie->snonce;
- rsnxe_used = ftie->mic_control[0] & 0x01;
- fte_elem_count = ftie->mic_control[1];
- fte_mic = ftie->mic;
- } else {
- struct rsn_ftie *ftie;
-
- ftie = (struct rsn_ftie *) parse.ftie;
- if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
- wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
- return WLAN_STATUS_INVALID_FTIE;
- }
-
- anonce = ftie->anonce;
- snonce = ftie->snonce;
- rsnxe_used = ftie->mic_control[0] & 0x01;
- fte_elem_count = ftie->mic_control[1];
- fte_mic = ftie->mic;
+ if (!parse.ftie || !parse.fte_anonce || !parse.fte_snonce ||
+ parse.fte_mic_len != mic_len) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Invalid FTE (fte_mic_len=%zu mic_len=%zu)",
+ parse.fte_mic_len, mic_len);
+ return WLAN_STATUS_INVALID_FTIE;
}
- if (os_memcmp(snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
+ if (os_memcmp(parse.fte_snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
- snonce, WPA_NONCE_LEN);
+ parse.fte_snonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
sm->SNonce, WPA_NONCE_LEN);
return WLAN_STATUS_INVALID_FTIE;
}
- if (os_memcmp(anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
+ if (os_memcmp(parse.fte_anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
- anonce, WPA_NONCE_LEN);
+ parse.fte_anonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
sm->ANonce, WPA_NONCE_LEN);
return WLAN_STATUS_INVALID_FTIE;
}
-
if (parse.r0kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
return WLAN_STATUS_INVALID_FTIE;
@@ -3492,10 +3580,10 @@
count += ieee802_11_ie_count(parse.ric, parse.ric_len);
if (parse.rsnxe)
count++;
- if (fte_elem_count != count) {
+ if (parse.fte_elem_count != count) {
wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
"Control: received %u expected %u",
- fte_elem_count, count);
+ parse.fte_elem_count, count);
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
@@ -3506,7 +3594,8 @@
kck = sm->PTK.kck;
kck_len = sm->PTK.kck_len;
}
- if (wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 5,
+ if (wpa_ft_mic(sm->wpa_key_mgmt, kck, kck_len,
+ sm->addr, sm->wpa_auth->addr, 5,
parse.mdie - 2, parse.mdie_len + 2,
parse.ftie - 2, parse.ftie_len + 2,
parse.rsn - 2, parse.rsn_len + 2,
@@ -3518,12 +3607,12 @@
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
- if (os_memcmp_const(mic, fte_mic, mic_len) != 0) {
+ if (os_memcmp_const(mic, parse.fte_mic, mic_len) != 0) {
wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR,
MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr));
wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC",
- fte_mic, mic_len);
+ parse.fte_mic, mic_len);
wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, mic_len);
wpa_hexdump(MSG_MSGDUMP, "FT: MDIE",
parse.mdie - 2, parse.mdie_len + 2);
@@ -3537,7 +3626,9 @@
return WLAN_STATUS_INVALID_FTIE;
}
- if (rsnxe_used && (conf->sae_pwe == 1 || conf->sae_pwe == 2) &&
+ if (parse.fte_rsnxe_used &&
+ (conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ conf->sae_pwe == SAE_PWE_BOTH) &&
!parse.rsnxe) {
wpa_printf(MSG_INFO,
"FT: FTE indicated that STA uses RSNXE, but RSNXE was not included");
@@ -4105,7 +4196,8 @@
pmk_r1_len = PMK_LEN;
if (wpa_ft_rrb_get_tlv(plain, plain_len, FT_RRB_PMK_R1, &f_pmk_r1_len,
&f_pmk_r1) == 0 &&
- (f_pmk_r1_len == PMK_LEN || f_pmk_r1_len == SHA384_MAC_LEN))
+ (f_pmk_r1_len == PMK_LEN || f_pmk_r1_len == SHA384_MAC_LEN ||
+ f_pmk_r1_len == SHA512_MAC_LEN))
pmk_r1_len = f_pmk_r1_len;
RRB_GET(FT_RRB_PMK_R1, pmk_r1, msgtype, pmk_r1_len);
wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f_pmk_r1, pmk_r1_len);
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index a510952..9f6699b 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -117,6 +117,7 @@
#ifdef CONFIG_TESTING_OPTIONS
wconf->corrupt_gtk_rekey_mic_probability =
iconf->corrupt_gtk_rekey_mic_probability;
+ wconf->delay_eapol_tx = iconf->delay_eapol_tx;
if (conf->own_ie_override &&
wpabuf_len(conf->own_ie_override) <= MAX_OWN_IE_OVERRIDE) {
wconf->own_ie_override_len = wpabuf_len(conf->own_ie_override);
@@ -198,10 +199,10 @@
#endif /* CONFIG_FILS */
wconf->sae_pwe = conf->sae_pwe;
sae_pw_id = hostapd_sae_pw_id_in_use(conf);
- if (sae_pw_id == 2 && wconf->sae_pwe != 3)
- wconf->sae_pwe = 1;
- else if (sae_pw_id == 1 && wconf->sae_pwe == 0)
- wconf->sae_pwe = 2;
+ if (sae_pw_id == 2 && wconf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+ wconf->sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
+ else if (sae_pw_id == 1 && wconf->sae_pwe == SAE_PWE_HUNT_AND_PECK)
+ wconf->sae_pwe = SAE_PWE_BOTH;
#ifdef CONFIG_SAE_PK
wconf->sae_pk = hostapd_sae_pk_in_use(conf);
#endif /* CONFIG_SAE_PK */
@@ -937,7 +938,7 @@
struct hostapd_data *hapd = ctx;
ptksa_cache_add(hapd->ptksa, hapd->own_addr, addr, cipher, life_time,
- ptk, NULL, NULL);
+ ptk, NULL, NULL, 0);
}
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 5bd699c..7ed3f2b 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -86,6 +86,7 @@
unsigned int pending_deinit:1;
unsigned int started:1;
unsigned int mgmt_frame_prot:1;
+ unsigned int mfpr:1;
unsigned int rx_eapol_key_secure:1;
unsigned int update_snonce:1;
unsigned int alt_snonce_valid:1;
@@ -298,7 +299,7 @@
#ifdef CONFIG_IEEE80211R_AP
int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len);
-int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384,
+int wpa_write_ftie(struct wpa_auth_config *conf, int key_mgmt, size_t key_len,
const u8 *r0kh_id, size_t r0kh_id_len,
const u8 *anonce, const u8 *snonce,
u8 *buf, size_t len, const u8 *subelem,
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 1c8affa..43ccec9 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -405,7 +405,8 @@
size_t flen;
if (wpa_key_mgmt_sae(conf->wpa_key_mgmt) &&
- (conf->sae_pwe == 1 || conf->sae_pwe == 2 || conf->sae_pk ||
+ (conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ conf->sae_pwe == SAE_PWE_BOTH || conf->sae_pk ||
wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt))) {
capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
#ifdef CONFIG_SAE_PK
@@ -419,7 +420,7 @@
if (conf->secure_rtt)
capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
if (conf->prot_range_neg)
- capab |= BIT(WLAN_RSNX_CAPAB_PROT_RANGE_NEG);
+ capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
flen = (capab & 0xff00) ? 2 : 1;
if (!capab)
@@ -883,6 +884,7 @@
sm->mgmt_frame_prot = 0;
else
sm->mgmt_frame_prot = 1;
+ sm->mfpr = !!(data.capabilities & WPA_CAPABILITY_MFPR);
if (sm->mgmt_frame_prot && (ciphers & WPA_CIPHER_TKIP)) {
wpa_printf(MSG_DEBUG,
diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c
index 8aba713..a95ae36 100644
--- a/src/common/common_module_tests.c
+++ b/src/common/common_module_tests.c
@@ -428,7 +428,7 @@
}
if (sae_parse_commit(&sae, peer_commit, sizeof(peer_commit), NULL, NULL,
- NULL, 0) != 0 ||
+ NULL, 0, NULL) != 0 ||
sae_process_commit(&sae) < 0)
goto fail;
diff --git a/src/common/defs.h b/src/common/defs.h
index 3e658cb..c0c6dbe 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -137,7 +137,8 @@
static inline int wpa_key_mgmt_sha256(int akm)
{
- return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 |
+ return !!(akm & (WPA_KEY_MGMT_FT_IEEE8021X |
+ WPA_KEY_MGMT_PSK_SHA256 |
WPA_KEY_MGMT_IEEE8021X_SHA256 |
WPA_KEY_MGMT_SAE |
WPA_KEY_MGMT_FT_SAE |
@@ -520,4 +521,12 @@
#define MAX_NUM_MLD_LINKS 15
+enum sae_pwe {
+ SAE_PWE_HUNT_AND_PECK = 0,
+ SAE_PWE_HASH_TO_ELEMENT = 1,
+ SAE_PWE_BOTH = 2,
+ SAE_PWE_FORCE_HUNT_AND_PECK = 3,
+ SAE_PWE_NOT_SET = 4,
+};
+
#endif /* DEFS_H */
diff --git a/src/common/dpp.c b/src/common/dpp.c
index 559bdcd..d2fc00f 100644
--- a/src/common/dpp.c
+++ b/src/common/dpp.c
@@ -4146,7 +4146,7 @@
const u8 *net_access_key, size_t net_access_key_len,
const u8 *csign_key, size_t csign_key_len,
const u8 *peer_connector, size_t peer_connector_len,
- os_time_t *expiry)
+ os_time_t *expiry, u8 *peer_key_hash)
{
struct json_token *root = NULL, *netkey, *token;
struct json_token *own_root = NULL;
@@ -4269,6 +4269,9 @@
}
#endif /* CONFIG_DPP3 */
+ if (peer_key_hash)
+ dpp_get_pubkey_hash(intro->peer_key, peer_key_hash);
+
ret = DPP_STATUS_OK;
fail:
if (ret != DPP_STATUS_OK)
@@ -5039,6 +5042,24 @@
}
+void dpp_notify_auth_success(struct dpp_authentication *auth, int initiator)
+{
+ u8 hash[SHA256_MAC_LEN];
+ char hex[SHA256_MAC_LEN * 2 + 1];
+
+ if (auth->peer_protocol_key) {
+ dpp_get_pubkey_hash(auth->peer_protocol_key, hash);
+ wpa_snprintf_hex(hex, sizeof(hex), hash, sizeof(hash));
+ } else {
+ hex[0] = '\0';
+ }
+ wpa_msg(auth->msg_ctx, MSG_INFO,
+ DPP_EVENT_AUTH_SUCCESS "init=%d pkhash=%s own=%d peer=%d",
+ initiator, hex, auth->own_bi ? (int) auth->own_bi->id : -1,
+ auth->peer_bi ? (int) auth->peer_bi->id : -1);
+}
+
+
#ifdef CONFIG_DPP2
struct wpabuf * dpp_build_presence_announcement(struct dpp_bootstrap_info *bi)
diff --git a/src/common/dpp.h b/src/common/dpp.h
index 3094be8..86f8478 100644
--- a/src/common/dpp.h
+++ b/src/common/dpp.h
@@ -674,7 +674,7 @@
const u8 *net_access_key, size_t net_access_key_len,
const u8 *csign_key, size_t csign_key_len,
const u8 *peer_connector, size_t peer_connector_len,
- os_time_t *expiry);
+ os_time_t *expiry, u8 *peer_key_hash);
void dpp_peer_intro_deinit(struct dpp_introduction *intro);
int dpp_get_connector_version(const char *connector);
struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
@@ -825,6 +825,7 @@
struct dpp_global * dpp_global_init(struct dpp_global_config *config);
void dpp_global_clear(struct dpp_global *dpp);
void dpp_global_deinit(struct dpp_global *dpp);
+void dpp_notify_auth_success(struct dpp_authentication *auth, int initiator);
/* dpp_reconfig.c */
@@ -857,6 +858,7 @@
size_t pp_key_len);
int dpp_update_reconfig_id(struct dpp_reconfig_id *id);
void dpp_free_reconfig_id(struct dpp_reconfig_id *id);
+int dpp_get_pubkey_hash(struct crypto_ec_key *key, u8 *hash);
#endif /* CONFIG_DPP */
#endif /* DPP_H */
diff --git a/src/common/dpp_crypto.c b/src/common/dpp_crypto.c
index 09d4d8c..f17f95a 100644
--- a/src/common/dpp_crypto.c
+++ b/src/common/dpp_crypto.c
@@ -246,6 +246,27 @@
}
+int dpp_get_pubkey_hash(struct crypto_ec_key *key, u8 *hash)
+{
+ struct wpabuf *uncomp;
+ const u8 *addr[1];
+ size_t len[1];
+ int res;
+
+ if (!key)
+ return -1;
+
+ uncomp = crypto_ec_key_get_pubkey_point(key, 1);
+ if (!uncomp)
+ return -1;
+ addr[0] = wpabuf_head(uncomp);
+ len[0] = wpabuf_len(uncomp);
+ res = sha256_vector(1, addr, len, hash);
+ wpabuf_free(uncomp);
+ return res;
+}
+
+
struct crypto_ec_key * dpp_gen_keypair(const struct dpp_curve_params *curve)
{
struct crypto_ec_key *key;
diff --git a/src/common/dpp_tcp.c b/src/common/dpp_tcp.c
index ff18a99..d226a8a 100644
--- a/src/common/dpp_tcp.c
+++ b/src/common/dpp_tcp.c
@@ -371,8 +371,7 @@
return;
wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
- wpa_msg(conn->msg_ctx, MSG_INFO,
- DPP_EVENT_AUTH_SUCCESS "init=%d", initiator);
+ dpp_notify_auth_success(auth, initiator);
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
wpa_printf(MSG_INFO,
@@ -628,6 +627,17 @@
if (!ctrl)
return -1;
+ if (type == DPP_PA_PRESENCE_ANNOUNCEMENT ||
+ type == DPP_PA_RECONFIG_ANNOUNCEMENT) {
+ conn = dpp_relay_match_ctrl(ctrl, src, freq, type);
+ if (conn &&
+ (!conn->auth || conn->auth->waiting_auth_resp)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Use existing TCP connection to Controller since no Auth Resp seen on it yet");
+ return dpp_relay_tx(conn, hdr, buf, len);
+ }
+ }
+
wpa_printf(MSG_DEBUG,
"DPP: Authentication Request for a configured Controller");
conn = dpp_relay_new_conn(ctrl, src, freq);
@@ -945,12 +955,6 @@
struct dpp_authentication *auth;
struct dpp_global *dpp = conn->ctrl->global;
- if (conn->auth) {
- wpa_printf(MSG_DEBUG,
- "DPP: Ignore Presence Announcement during ongoing Authentication");
- return -1;
- }
-
wpa_printf(MSG_DEBUG, "DPP: Presence Announcement");
r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
@@ -969,6 +973,12 @@
return -1;
}
+ if (conn->auth) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore Presence Announcement during ongoing Authentication");
+ return 0;
+ }
+
auth = dpp_auth_init(dpp, conn->msg_ctx, peer_bi, NULL,
DPP_CAPAB_CONFIGURATOR, -1, NULL, 0);
if (!auth)
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index d6fd792..3c3667f 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -199,11 +199,76 @@
}
+static int ieee802_11_parse_mle(const u8 *pos, size_t elen, size_t **total_len,
+ struct ieee802_11_elems *elems,
+ int show_errors)
+{
+ u8 mle_type = pos[0] & MULTI_LINK_CONTROL_TYPE_MASK;
+
+ switch (mle_type) {
+ case MULTI_LINK_CONTROL_TYPE_BASIC:
+ elems->basic_mle = pos;
+ elems->basic_mle_len = elen;
+ *total_len = &elems->basic_mle_len;
+ break;
+ case MULTI_LINK_CONTROL_TYPE_PROBE_REQ:
+ elems->probe_req_mle = pos;
+ elems->probe_req_mle_len = elen;
+ *total_len = &elems->probe_req_mle_len;
+ break;
+ case MULTI_LINK_CONTROL_TYPE_RECONF:
+ elems->reconf_mle = pos;
+ elems->reconf_mle_len = elen;
+ *total_len = &elems->reconf_mle_len;
+ break;
+ case MULTI_LINK_CONTROL_TYPE_TDLS:
+ elems->tdls_mle = pos;
+ elems->tdls_mle_len = elen;
+ *total_len = &elems->tdls_mle_len;
+ break;
+ case MULTI_LINK_CONTROL_TYPE_PRIOR_ACCESS:
+ elems->prior_access_mle = pos;
+ elems->prior_access_mle_len = elen;
+ *total_len = &elems->prior_access_mle_len;
+ break;
+ default:
+ if (show_errors) {
+ wpa_printf(MSG_MSGDUMP,
+ "Unknown Multi-Link element type %u",
+ mle_type);
+ }
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static size_t ieee802_11_fragments_length(struct ieee802_11_elems *elems,
+ const u8 *start, size_t len)
+{
+ const struct element *elem;
+ size_t frags_len = 0;
+
+ for_each_element(elem, start, len) {
+ if (elem->id != WLAN_EID_FRAGMENT)
+ break;
+
+ frags_len += elem->datalen + 2;
+ elems->num_frag_elems++;
+ }
+
+ return frags_len;
+}
+
+
static int ieee802_11_parse_extension(const u8 *pos, size_t elen,
struct ieee802_11_elems *elems,
+ const u8 *start, size_t len,
int show_errors)
{
u8 ext_id;
+ size_t *total_len = NULL;
if (elen < 1) {
if (show_errors) {
@@ -216,8 +281,6 @@
ext_id = *pos++;
elen--;
- elems->frag_ies.last_eid_ext = 0;
-
switch (ext_id) {
case WLAN_EID_EXT_ASSOC_DELAY_INFO:
if (elen != 1)
@@ -244,6 +307,7 @@
break;
elems->fils_hlp = pos;
elems->fils_hlp_len = elen;
+ total_len = &elems->fils_hlp_len;
break;
case WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN:
if (elen < 1)
@@ -260,6 +324,7 @@
case WLAN_EID_EXT_WRAPPED_DATA:
elems->wrapped_data = pos;
elems->wrapped_data_len = elen;
+ total_len = &elems->wrapped_data_len;
break;
case WLAN_EID_EXT_FILS_PUBLIC_KEY:
if (elen < 1)
@@ -323,6 +388,17 @@
elems->eht_operation = pos;
elems->eht_operation_len = elen;
break;
+ case WLAN_EID_EXT_MULTI_LINK:
+ if (elen < 2)
+ break;
+ if (ieee802_11_parse_mle(pos, elen, &total_len, elems,
+ show_errors))
+ return -1;
+ break;
+ case WLAN_EID_EXT_KNOWN_BSSID:
+ elems->mbssid_known_bss = pos;
+ elems->mbssid_known_bss_len = elen;
+ break;
default:
if (show_errors) {
wpa_printf(MSG_MSGDUMP,
@@ -332,39 +408,14 @@
return -1;
}
- if (elen == 254)
- elems->frag_ies.last_eid_ext = ext_id;
+ if (elen == 254 && total_len)
+ *total_len += ieee802_11_fragments_length(
+ elems, pos + elen, (start + len) - (pos + elen));
return 0;
}
-static void ieee802_11_parse_fragment(struct frag_ies_info *frag_ies,
- const u8 *pos, u8 elen)
-{
- if (frag_ies->n_frags >= MAX_NUM_FRAG_IES_SUPPORTED) {
- wpa_printf(MSG_MSGDUMP, "Too many element fragments - skip");
- return;
- }
-
- /*
- * Note: while EID == 0 is a valid ID (SSID IE), it should not be
- * fragmented.
- */
- if (!frag_ies->last_eid) {
- wpa_printf(MSG_MSGDUMP,
- "Fragment without a valid last element - skip");
- return;
- }
-
- frag_ies->frags[frag_ies->n_frags].ie = pos;
- frag_ies->frags[frag_ies->n_frags].ie_len = elen;
- frag_ies->frags[frag_ies->n_frags].eid = frag_ies->last_eid;
- frag_ies->frags[frag_ies->n_frags].eid_ext = frag_ies->last_eid_ext;
- frag_ies->n_frags++;
-}
-
-
/**
* ieee802_11_parse_elems - Parse information elements in management frames
* @start: Pointer to the start of IEs
@@ -389,6 +440,12 @@
u8 id = elem->id, elen = elem->datalen;
const u8 *pos = elem->data;
+ if (id == WLAN_EID_FRAGMENT && elems->num_frag_elems > 0) {
+ elems->num_frag_elems--;
+ continue;
+ }
+ elems->num_frag_elems = 0;
+
switch (id) {
case WLAN_EID_SSID:
if (elen > SSID_MAX_LEN) {
@@ -592,11 +649,13 @@
elems->s1g_capab = pos;
break;
case WLAN_EID_FRAGMENT:
- ieee802_11_parse_fragment(&elems->frag_ies, pos, elen);
+ wpa_printf(MSG_MSGDUMP,
+ "Fragment without a valid last element - skip");
+
break;
case WLAN_EID_EXTENSION:
- if (ieee802_11_parse_extension(pos, elen, elems,
- show_errors))
+ if (ieee802_11_parse_extension(pos, elen, elems, start,
+ len, show_errors))
unknown++;
break;
default:
@@ -608,12 +667,6 @@
id, elen);
break;
}
-
- if (id != WLAN_EID_FRAGMENT && elen == 255)
- elems->frag_ies.last_eid = id;
-
- if (id == WLAN_EID_EXTENSION && !elems->frag_ies.last_eid_ext)
- elems->frag_ies.last_eid = 0;
}
if (!for_each_element_completed(elem, start, len)) {
@@ -1273,8 +1326,9 @@
if (chan < 25 || chan > 29)
return -1;
return 56160 + 2160 * (chan - 24);
+ default:
+ return -1;
}
- return -1;
}
@@ -1323,8 +1377,9 @@
if (chan != 25)
return -1;
return 56160 + 2160 * (chan - 24);
+ default:
+ return -1;
}
- return -1;
}
@@ -1379,8 +1434,9 @@
if (chan != 25)
return -1;
return 56160 + 2160 * (chan - 24);
+ default:
+ return -1;
}
- return -1;
}
@@ -1405,8 +1461,9 @@
if (chan < 149 || chan > 165)
return -1;
return 5000 + 5 * chan;
+ default:
+ return -1;
}
- return -1;
}
@@ -1492,8 +1549,9 @@
if (chan < 25 || chan > 29)
return -1;
return 56160 + 2160 * (chan - 24);
+ default:
+ return -1;
}
- return -1;
}
/**
@@ -1833,6 +1891,9 @@
S2S(DENIED_HE_NOT_SUPPORTED)
S2S(SAE_HASH_TO_ELEMENT)
S2S(SAE_PK)
+ S2S(INVALID_PUBLIC_KEY)
+ S2S(PASN_BASE_AKMP_FAILED)
+ S2S(OCI_MISMATCH)
}
return "UNKNOWN";
#undef S2S
@@ -1922,11 +1983,14 @@
/*
* IEEE Std 802.11ax-2021, 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
+ * frequency index for operation classes 128, 129, 130, 132, 133, 134,
+ * and 135, but currently use the lowest 20 MHz channel for simplicity
* (these center frequencies are not actual channels, which makes
- * wpas_p2p_verify_channel() fail). wpas_p2p_verify_80mhz() should take
- * care of removing invalid channels.
+ * wpas_p2p_verify_channel() fail).
+ * Specially for the operation class 136, it is also defined to use the
+ * channel center frequency index value, but it happens to be a 20 MHz
+ * channel and the channel number in the channel set would match the
+ * value in for the frequency center.
*/
{ HOSTAPD_MODE_IEEE80211A, 128, 36, 177, 4, BW80, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 129, 36, 177, 4, BW160, P2P_SUPP },
@@ -2624,9 +2688,9 @@
return 6480;
case 183: /* 60 GHz band, EDMG CB4, channel 25..29 */
return 8640;
+ default:
+ return 20;
}
-
- return 20;
}
@@ -2688,41 +2752,47 @@
return CONF_OPER_CHWIDTH_6480MHZ;
case 183: /* 60 GHz band, EDMG CB4, channel 25..29 */
return CONF_OPER_CHWIDTH_8640MHZ;
+ default:
+ return CONF_OPER_CHWIDTH_USE_HT;
}
- return CONF_OPER_CHWIDTH_USE_HT;
}
-struct wpabuf * ieee802_11_defrag_data(struct ieee802_11_elems *elems,
- u8 eid, u8 eid_ext,
- const u8 *data, u8 len)
-{
- struct frag_ies_info *frag_ies = &elems->frag_ies;
- struct wpabuf *buf;
- unsigned int i;
- if (!elems || !data || !len)
+struct wpabuf * ieee802_11_defrag_data(const u8 *data, size_t len,
+ bool ext_elem)
+{
+ struct wpabuf *buf;
+ const u8 *pos, *end = data + len;
+ size_t min_defrag_len = ext_elem ? 255 : 256;
+
+ if (!data || !len)
return NULL;
- buf = wpabuf_alloc_copy(data, len);
+ if (len < min_defrag_len)
+ return wpabuf_alloc_copy(data, len);
+
+ buf = wpabuf_alloc_copy(data, min_defrag_len - 1);
if (!buf)
return NULL;
- for (i = 0; i < frag_ies->n_frags; i++) {
+ pos = &data[min_defrag_len - 1];
+ len -= min_defrag_len - 1;
+ while (len > 2 && pos[0] == WLAN_EID_FRAGMENT && pos[1]) {
int ret;
+ size_t elen = 2 + pos[1];
- if (frag_ies->frags[i].eid != eid ||
- frag_ies->frags[i].eid_ext != eid_ext)
- continue;
-
- ret = wpabuf_resize(&buf, frag_ies->frags[i].ie_len);
+ if (elen > (size_t) (end - pos) || elen > len)
+ break;
+ ret = wpabuf_resize(&buf, pos[1]);
if (ret < 0) {
wpabuf_free(buf);
return NULL;
}
/* Copy only the fragment data (without the EID and length) */
- wpabuf_put_data(buf, frag_ies->frags[i].ie,
- frag_ies->frags[i].ie_len);
+ wpabuf_put_data(buf, &pos[2], pos[1]);
+ pos += elen;
+ len -= elen;
}
return buf;
@@ -2733,7 +2803,7 @@
u8 eid, u8 eid_ext)
{
const u8 *data;
- u8 len;
+ size_t len;
/*
* TODO: Defragmentation mechanism can be supported for all IEs. For now
@@ -2763,7 +2833,7 @@
return NULL;
}
- return ieee802_11_defrag_data(elems, eid, eid_ext, data, len);
+ return ieee802_11_defrag_data(data, len, true);
}
/* Parse HT capabilities to get maximum number of supported spatial streams */
@@ -3125,3 +3195,40 @@
return &buf[mld_addr_pos];
}
+
+
+struct wpabuf * ieee802_11_defrag_mle(struct ieee802_11_elems *elems, u8 type)
+{
+ const u8 *data;
+ size_t len;
+
+ switch (type) {
+ case MULTI_LINK_CONTROL_TYPE_BASIC:
+ data = elems->basic_mle;
+ len = elems->basic_mle_len;
+ break;
+ case MULTI_LINK_CONTROL_TYPE_PROBE_REQ:
+ data = elems->probe_req_mle;
+ len = elems->probe_req_mle_len;
+ break;
+ case MULTI_LINK_CONTROL_TYPE_RECONF:
+ data = elems->reconf_mle;
+ len = elems->reconf_mle_len;
+ break;
+ case MULTI_LINK_CONTROL_TYPE_TDLS:
+ data = elems->tdls_mle;
+ len = elems->tdls_mle_len;
+ break;
+ case MULTI_LINK_CONTROL_TYPE_PRIOR_ACCESS:
+ data = elems->prior_access_mle;
+ len = elems->prior_access_mle_len;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "Defragmentation not supported for Multi-Link element type=%u",
+ type);
+ return NULL;
+ }
+
+ return ieee802_11_defrag_data(data, len, true);
+}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index ff31e8d..9a6915d 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -21,7 +21,6 @@
struct hostapd_hw_modes;
#define MAX_NOF_MB_IES_SUPPORTED 5
-#define MAX_NUM_FRAG_IES_SUPPORTED 3
struct mb_ies_info {
struct {
@@ -31,21 +30,6 @@
u8 nof_ies;
};
-struct frag_ies_info {
- struct {
- u8 eid;
- u8 eid_ext;
- const u8 *ie;
- u8 ie_len;
- } frags[MAX_NUM_FRAG_IES_SUPPORTED];
-
- u8 n_frags;
-
- /* the last parsed element ID and element extension ID */
- u8 last_eid;
- u8 last_eid_ext;
-};
-
/* Parsed Information Elements */
struct ieee802_11_elems {
const u8 *ssid;
@@ -119,6 +103,12 @@
const u8 *pasn_params;
const u8 *eht_capabilities;
const u8 *eht_operation;
+ const u8 *basic_mle;
+ const u8 *probe_req_mle;
+ const u8 *reconf_mle;
+ const u8 *tdls_mle;
+ const u8 *prior_access_mle;
+ const u8 *mbssid_known_bss;
u8 ssid_len;
u8 supp_rates_len;
@@ -157,10 +147,10 @@
u8 dils_len;
u8 fils_req_params_len;
u8 fils_key_confirm_len;
- u8 fils_hlp_len;
+ size_t fils_hlp_len;
u8 fils_ip_addr_assign_len;
u8 key_delivery_len;
- u8 wrapped_data_len;
+ size_t wrapped_data_len;
u8 fils_pk_len;
u8 owe_dh_len;
u8 power_capab_len;
@@ -175,9 +165,20 @@
u8 pasn_params_len;
u8 eht_capabilities_len;
u8 eht_operation_len;
+ size_t basic_mle_len;
+ size_t probe_req_mle_len;
+ size_t reconf_mle_len;
+ size_t tdls_mle_len;
+ size_t prior_access_mle_len;
+ u8 mbssid_known_bss_len;
struct mb_ies_info mb_ies;
- struct frag_ies_info frag_ies;
+
+ /*
+ * The number of fragment elements to be skipped after a known
+ * fragmented element.
+ */
+ unsigned int num_frag_elems;
};
typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
@@ -338,11 +339,11 @@
int ieee802_edmg_is_allowed(struct ieee80211_edmg_config allowed,
struct ieee80211_edmg_config requested);
-struct wpabuf * ieee802_11_defrag_data(struct ieee802_11_elems *elems,
- u8 eid, u8 eid_ext,
- const u8 *data, u8 len);
+struct wpabuf * ieee802_11_defrag_data(const u8 *data, size_t len,
+ bool ext_elem);
struct wpabuf * ieee802_11_defrag(struct ieee802_11_elems *elems,
u8 eid, u8 eid_ext);
+struct wpabuf * ieee802_11_defrag_mle(struct ieee802_11_elems *elems, u8 type);
const u8 * get_ml_ie(const u8 *ies, size_t len, u8 type);
const u8 * get_basic_mle_mld_addr(const u8 *buf, size_t len);
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 5c423eb..e5e4e83 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -209,6 +209,9 @@
#define WLAN_STATUS_DENIED_HE_NOT_SUPPORTED 124
#define WLAN_STATUS_SAE_HASH_TO_ELEMENT 126
#define WLAN_STATUS_SAE_PK 127
+#define WLAN_STATUS_INVALID_PUBLIC_KEY 136
+#define WLAN_STATUS_PASN_BASE_AKMP_FAILED 137
+#define WLAN_STATUS_OCI_MISMATCH 138
/* Reason codes (IEEE Std 802.11-2016, 9.4.1.7, Table 9-45) */
#define WLAN_REASON_UNSPECIFIED 1
@@ -481,6 +484,9 @@
#define WLAN_EID_EXT_SPATIAL_REUSE 39
#define WLAN_EID_EXT_COLOR_CHANGE_ANNOUNCEMENT 42
#define WLAN_EID_EXT_OCV_OCI 54
+#define WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION 55
+#define WLAN_EID_EXT_NON_INHERITANCE 56
+#define WLAN_EID_EXT_KNOWN_BSSID 57
#define WLAN_EID_EXT_SHORT_SSID_LIST 58
#define WLAN_EID_EXT_HE_6GHZ_BAND_CAP 59
#define WLAN_EID_EXT_EDMG_CAPABILITIES 61
@@ -586,7 +592,11 @@
#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
+#define WLAN_RSNX_CAPAB_URNM_MFPR_X20 10
+#define WLAN_RSNX_CAPAB_URNM_MFPR 15
+
+/* Multiple BSSID element subelements */
+#define WLAN_MBSSID_SUBELEMENT_NONTRANSMITTED_BSSID_PROFILE 0
/* Action frame categories (IEEE Std 802.11-2016, 9.4.1.11, Table 9-76) */
#define WLAN_ACTION_SPECTRUM_MGMT 0
@@ -1289,9 +1299,12 @@
#define HT_OPER_PARAM_PCO_PHASE ((u16) BIT(11))
/* B36..B39 - Reserved */
+#define BSS_MEMBERSHIP_SELECTOR_HE_PHY 122
+#define BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY 123
+#define BSS_MEMBERSHIP_SELECTOR_EPD 124
+#define BSS_MEMBERSHIP_SELECTOR_GLK 125
#define BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126
#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
-#define BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY 123
/* VHT Defines */
#define VHT_CAP_MAX_MPDU_LENGTH_7991 ((u32) BIT(0))
@@ -2444,13 +2457,16 @@
#define RNR_BSS_PARAM_CO_LOCATED BIT(6)
#define RNR_20_MHZ_PSD_MAX_TXPOWER 255 /* dBm */
-/* IEEE P802.11be/D1.5, 9.4.2.311 - EHT Operation element */
+/* IEEE P802.11be/D2.3, 9.4.2.311 - EHT Operation element */
#define EHT_OPERATION_IE_MIN_LEN 1
/* Figure 9-1002b: EHT Operation Parameters field subfields */
#define EHT_OPER_INFO_PRESENT BIT(0)
#define EHT_OPER_DISABLED_SUBCHAN_BITMAP_PRESENT BIT(1)
+#define EHT_OPER_DEFAULT_PE_DURATION BIT(2)
+#define EHT_OPER_GROUP_ADDR_BU_INDICATION_LIMIT BIT(3)
+#define EHT_OPER_GROUP_ADDR_BU_INDICATION_EXPONENT (BIT(4) | BIT(5))
/* Control subfield: Channel Width subfield; see Table 9-401b */
#define EHT_OPER_CHANNEL_WIDTH_MASK 0x7
@@ -2473,6 +2489,7 @@
/* Figure 9-1002a: EHT Operation element format */
struct ieee80211_eht_operation {
u8 oper_params; /* EHT Operation Parameters: EHT_OPER_* bits */
+ u8 basic_eht_mcs_nss_set[4];
struct ieee80211_eht_oper_info oper_info; /* 0 or 3 or 5 octets */
} STRUCT_PACKED;
@@ -2550,10 +2567,13 @@
u8 optional[EHT_MCS_NSS_CAPAB_LEN + EHT_PPE_THRESH_CAPAB_LEN];
} STRUCT_PACKED;
+#define IEEE80211_EHT_CAPAB_MIN_LEN (2 + 9)
+
/* IEEE P802.11be/D2.1, 9.4.2.312 - Multi-Link element */
/* Figure 9-1002f: Multi-Link Control field */
#define MULTI_LINK_CONTROL_TYPE_MASK 0x07
+#define MULTI_LINK_CONTROL_LEN 2
/* Table 9-401c: Mult-Link element Type subfield encoding */
#define MULTI_LINK_CONTROL_TYPE_BASIC 0
@@ -2562,14 +2582,125 @@
#define MULTI_LINK_CONTROL_TYPE_TDLS 3
#define MULTI_LINK_CONTROL_TYPE_PRIOR_ACCESS 4
-/* Figure 9-1002g: Presence Bitmap subfield of the Basic Multi-Link element */
-#define BASIC_MULTI_LINK_CTRL0_PRES_LINK_ID 0x10
-#define BASIC_MULTI_LINK_CTRL0_PRES_BSS_PARAM_CH_COUNT 0x20
-#define BASIC_MULTI_LINK_CTRL0_PRES_MSD_INFO 0x40
-#define BASIC_MULTI_LINK_CTRL0_PRES_EML_CAPA 0x80
+/*
+ * IEEE P802.11be/D2.2, Table 9-401c: Optional subelement IDs for Link Info
+ * field of the Multi-Link element
+ */
+#define MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE 0
+#define MULTI_LINK_SUB_ELEM_ID_VENDOR 221
+#define MULTI_LINK_SUB_ELEM_ID_FRAGMENT 254
-#define BASIC_MULTI_LINK_CTRL1_PRES_MLD_CAPA 0x01
-#define BASIC_MULTI_LINK_CTRL1_PRES_AP_MLD_ID 0x02
+/* IEEE P802.11be/D2.2, 9.4.2.312.2 - Basic Multi-Link element */
+
+/* Figure 9-1002g: Presence Bitmap subfield of the Basic Multi-Link element */
+#define BASIC_MULTI_LINK_CTRL_PRES_LINK_ID 0x0010
+#define BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT 0x0020
+#define BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO 0x0040
+#define BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA 0x0080
+#define BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA 0x0100
+#define BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID 0x0200
+
+/*
+ * STA Control field definitions of Per-STA Profile subelement in Basic
+ * Multi-Link element as described in Figure 9-1002n: STA Control field format.
+ */
+#define BASIC_MLE_STA_CTRL_LINK_ID_MASK 0x000F
+#define BASIC_MLE_STA_CTRL_COMPLETE_PROFILE 0x0010
+#define BASIC_MLE_STA_CTRL_PRES_STA_MAC 0x0020
+#define BASIC_MLE_STA_CTRL_PRES_BEACON_INT 0x0040
+#define BASIC_MLE_STA_CTRL_PRES_TSF_OFFSET 0x0080
+#define BASIC_MLE_STA_CTRL_PRES_DTIM_INFO 0x0100
+#define BASIC_MLE_STA_CTRL_PRES_NSTR_LINK_PAIR 0x0200
+#define BASIC_MLE_STA_CTRL_NSTR_BITMAP 0x0400
+#define BASIC_MLE_STA_CTRL_PRES_BSS_PARAM_COUNT 0x0800
+
+#define BASIC_MLE_STA_PROF_STA_MAC_IDX 3
+
+/* IEEE P802.11be/D2.2, 9.4.2.312.2.3 - Common Info field of the Basic
+ * Multi-Link element */
+struct eht_ml_basic_common_info {
+ u8 len;
+ u8 mld_addr[ETH_ALEN];
+
+ /*
+ * Followed by optional fields based on the multi link basic presence
+ * bitmap
+ *
+ * Link ID Info: 1 octet
+ * BSS Parameters Change Count: 1 octet
+ * Medium Synchronization Delay Information: 2 octets
+ * EML Capabilities: 2 octets
+ * MLD Capabilities and Operations: 2 octets
+ * AP MLD ID: 1 octet
+ */
+ u8 variable[];
+} STRUCT_PACKED;
+
+#define EHT_ML_LINK_ID_MSK 0x0f
+
+#define EHT_ML_MEDIUM_SYNC_DELAY_DURATION 0x00ff
+#define EHT_ML_MEDIUM_SYNC_DELAY_OFDM_ED_TH 0x0f00
+#define EHT_ML_MEDIUM_SYNC_DELAY_MAX_TXOP 0xf000
+
+#define EHT_ML_EML_CAPA_EMLSR_SUPP 0x0001
+#define EHT_ML_EML_CAPA_EMLSR_PADDING_DELAY_MASK 0x000e
+#define EHT_ML_EML_CAPA_EMLSR_TRANS_DELAY_MASK 0x0070
+#define EHT_ML_EML_CAPA_EMLMR_SUPP 0x0080
+#define EHT_ML_EML_CAPA_EMLMR_DELAY_MASK 0x0700
+#define EHT_ML_EML_CAPA_TRANSITION_TIMEOUT_MASK 0x7800
+
+#define EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK 0x000f
+#define EHT_ML_MLD_CAPA_SRS_SUPP 0x0010
+#define EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_ALL_TO_ALL 0x0020
+#define EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_ALL_TO_ONE 0x0040
+#define EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_NEG_SUPP_MSK 0x0060
+#define EHT_ML_MLD_CAPA_FREQ_SEP_FOR_STR_MASK 0x0f80
+#define EHT_ML_MLD_CAPA_AAR_SUPP 0x1000
+
+/* IEEE P802.11be/D2.0, 9.4.2.312.2.4 - Per-STA Profile subelement format */
+struct ieee80211_eht_per_sta_profile {
+ le16 sta_control;
+
+ /* Followed by STA Info and STA Profile fields */
+ u8 variable[];
+} STRUCT_PACKED;
+
+/* IEEE P802.11be/D2.0, 9.4.2.312.3 - Probe Request Multi-Link element */
+
+#define EHT_ML_PRES_BM_PROBE_REQ_AP_MLD_ID 0x0001
+
+struct eht_ml_probe_req_common_info {
+ u8 len;
+
+ /*
+ * Followed by optional fields based on the multi link basic presence
+ * bitmap
+ *
+ * AP MLD ID: 1 octet
+ */
+ u8 variable[];
+} STRUCT_PACKED;
+
+/* IEEE P802.11be/D2.0, 9.4.2.312.4 - Reconfiguration Multi-Link element */
+
+#define EHT_ML_PRES_BM_RECONFIGURE_MLD_ADDRESS 0x0001
+
+/* IEEE P802.11be/D2.0, 9.4.2.312.1 - Multi-Link element / General */
+
+struct ieee80211_eht_ml {
+ le16 ml_control;
+
+ /* Followed by Common Info and Link Info fields */
+ u8 variable[];
+} STRUCT_PACKED;
+
+/* Table 9-401c - Optional subelement IDs for Link Info field of the
+ * Multi-Link element */
+enum ieee80211_eht_ml_sub_elem {
+ EHT_ML_SUB_ELEM_PER_STA_PROFILE = 0,
+ EHT_ML_SUB_ELEM_VENDOR = 221,
+ EHT_ML_SUB_ELEM_FRAGMENT = 254,
+};
/* IEEE P802.11ay/D4.0, 9.4.2.251 - EDMG Operation element */
#define EDMG_BSS_OPERATING_CHANNELS_OFFSET 6
@@ -2717,4 +2848,15 @@
#define IS_CROSS_AKM_ROAM_KEY_MGMT(key_mgmt) \
((key_mgmt & WPA_KEY_MGMT_CROSS_AKM_ROAM) == WPA_KEY_MGMT_CROSS_AKM_ROAM)
#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
+
+struct ieee80211_neighbor_ap_info {
+ u8 tbtt_info_hdr;
+ u8 tbtt_info_len;
+ u8 op_class;
+ u8 channel;
+
+ /* Followed by the rest of the TBTT Information field contents */
+ u8 data[0];
+} STRUCT_PACKED;
+
#endif /* IEEE802_11_DEFS_H */
diff --git a/src/common/ptksa_cache.c b/src/common/ptksa_cache.c
index aacc425..3b5c0b8 100644
--- a/src/common/ptksa_cache.c
+++ b/src/common/ptksa_cache.c
@@ -264,6 +264,7 @@
* @ptk: The PTK
* @life_time_expiry_cb: Callback for alternative expiration handling
* @ctx: Context pointer to save into e->ctx for the callback
+ * @akmp: The key management mechanism that was used to derive 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.
@@ -277,7 +278,7 @@
const struct wpa_ptk *ptk,
void (*life_time_expiry_cb)
(struct ptksa_cache_entry *e),
- void *ctx)
+ void *ctx, u32 akmp)
{
struct ptksa_cache_entry *entry, *tmp, *tmp2 = NULL;
struct os_reltime now;
@@ -302,6 +303,7 @@
entry->cipher = cipher;
entry->cb = life_time_expiry_cb;
entry->ctx = ctx;
+ entry->akmp = akmp;
if (own_addr)
os_memcpy(entry->own_addr, own_addr, ETH_ALEN);
diff --git a/src/common/ptksa_cache.h b/src/common/ptksa_cache.h
index a643a26..6182215 100644
--- a/src/common/ptksa_cache.h
+++ b/src/common/ptksa_cache.h
@@ -26,6 +26,7 @@
u8 own_addr[ETH_ALEN];
void (*cb)(struct ptksa_cache_entry *e);
void *ctx;
+ u32 akmp;
};
#ifdef CONFIG_PTKSA_CACHE
@@ -44,7 +45,7 @@
const struct wpa_ptk *ptk,
void (*cb)
(struct ptksa_cache_entry *e),
- void *ctx);
+ void *ctx, u32 akmp);
void ptksa_cache_flush(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher);
#else /* CONFIG_PTKSA_CACHE */
@@ -73,7 +74,7 @@
static inline struct ptksa_cache_entry *
ptksa_cache_add(struct ptksa_cache *ptksa, const u8 *own_addr, const u8 *addr,
u32 cipher, u32 life_time, const struct wpa_ptk *ptk,
- void (*cb)(struct ptksa_cache_entry *e), void *ctx)
+ void (*cb)(struct ptksa_cache_entry *e), void *ctx, u32 akmp)
{
return NULL;
}
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index 215ba91..f972061 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -852,6 +852,48 @@
*
* The attributes used with this command are defined in
* enum qca_wlan_vendor_attr_sr.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_MLO_PEER_PRIM_NETDEV_EVENT: Subcommand used to
+ * notify application layer about the primary netdev of an MLO connection.
+ * In some implementations, MLO has multiple netdevs out of which one
+ * netdev is designated as primary to provide a unified interface to the
+ * bridge. In those implementations this event is sent on every MLO peer
+ * connection.
+ *
+ * The attributes used with this event are defined in
+ * enum qca_wlan_vendor_attr_mlo_peer_prim_netdev_event.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_AFC_EVENT: This vendor command is used by the
+ * driver to notify different AFC events to userspace. The attributes used
+ * with this command are defined in enum qca_wlan_vendor_attr_afc_event.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_AFC_RESPONSE: This vendor command is used by
+ * userspace to deliver AFC response data to driver. The attributes used
+ * with this command are defined in enum qca_wlan_vendor_attr_afc_response.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DOZED_AP: Subcommand to configure AP interface to
+ * operate in doze mode.
+ *
+ * Userspace uses this command to configure the AP interface to enter or
+ * exit from doze mode. The driver sends this event after it enters or
+ * exits the doze mode with the updated AP doze mode settings.
+ *
+ * The attributes used with this subcommand are defined in
+ * enum qca_wlan_vendor_attr_dozed_ap.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_MONITOR_MODE: This vendor subcommand is used
+ * to get the status of local packet capture of monitor mode. The monitor
+ * mode can be started using QCA_NL80211_VENDOR_SUBCMD_SET_MONITOR_MODE
+ * subcommand.
+ *
+ * The attributes used with this command are defined in enum
+ * qca_wlan_vendor_attr_get_monitor_mode.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_ROAM_STATS: This vendor command is used to
+ * get roam information from the driver to user space. It provides the
+ * latest several instances of roam information cached in the driver.
+ * The command is only used for STA mode. The attributes used with this
+ * command are defined in enum qca_wlan_vendor_attr_roam_cached_stats.
*/
enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -1056,6 +1098,12 @@
QCA_NL80211_VENDOR_SUBCMD_SCS_RULE_CONFIG = 218,
QCA_NL80211_VENDOR_SUBCMD_GET_SAR_CAPABILITY = 219,
QCA_NL80211_VENDOR_SUBCMD_SR = 220,
+ QCA_NL80211_VENDOR_SUBCMD_MLO_PEER_PRIM_NETDEV_EVENT = 221,
+ QCA_NL80211_VENDOR_SUBCMD_AFC_EVENT = 222,
+ QCA_NL80211_VENDOR_SUBCMD_AFC_RESPONSE = 223,
+ QCA_NL80211_VENDOR_SUBCMD_DOZED_AP = 224,
+ QCA_NL80211_VENDOR_SUBCMD_GET_MONITOR_MODE = 225,
+ QCA_NL80211_VENDOR_SUBCMD_ROAM_STATS = 226,
};
/* Compatibility defines for previously used subcmd names.
@@ -2656,6 +2704,14 @@
* Set the value to QCA_WLAN_AC_BK if the QoS upgrade needs to be
* disabled, as BK is of the lowest priority and an upgrade to it does
* not result in any changes for the frames.
+ *
+ * If only UDP frames of BE or BK access category needs to be upgraded
+ * without changing the access category of VO or VI UDP frames, refer to
+ * attribute QCA_WLAN_VENDOR_ATTR_CONFIG_UDP_QOS_UPGRADE_FOR_BE_BK.
+ *
+ * This attribute is not recommended to be used as it blindly forces all
+ * UDP packets to a higher access category which could impact the
+ * traffic pattern of all apps using UDP and can cause unknown behavior.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_UDP_QOS_UPGRADE = 72,
@@ -2781,6 +2837,74 @@
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_DBAM = 83,
+ /* 8-bit unsigned value. This attribute takes the QoS/access category
+ * value represented by the enum qca_wlan_ac_type and expects the driver
+ * to upgrade the UDP frames of BE or BK access category to this access
+ * category. This attribute will not modify UDP frames of VO or VI
+ * access category. The value of QCA_WLAN_AC_ALL is invalid for this
+ * attribute.
+ *
+ * This will override the DSCP value configured in the frame with the
+ * intention to only upgrade the access category. That said, it is not
+ * intended to downgrade the access category for the frames.
+ * Set the value to QCA_WLAN_AC_BK if the QoS upgrade needs to be
+ * disabled, as BK is of the lowest priority and an upgrade to it does
+ * not result in any changes for the frames.
+ *
+ * This attribute behavior is similar to
+ * QCA_WLAN_VENDOR_ATTR_CONFIG_UDP_QOS_UPGRADE with the difference that
+ * only UDP frames of BE or BK access category are upgraded and not
+ * UDP frames of VI or VO access category.
+ *
+ * This attribute is not recommended to be used as it blindly forces all
+ * UDP packets of BE or BK access category to a higher access category
+ * which could impact the traffic pattern of all apps using UDP and can
+ * cause unknown behavior.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_UDP_QOS_UPGRADE_FOR_BE_BK = 84,
+
+ /* 8-bit unsigned value to configure the driver to enable/disable the
+ * periodic sounding for Tx beamformer functionality. The default
+ * behavior uses algorithm to do sounding based on packet stats.
+ *
+ * 0 - Default behavior.
+ * 1 - Enable the periodic sounding for Tx beamformer.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_BEAMFORMER_PERIODIC_SOUNDING = 85,
+
+ /* 8-bit unsigned value, whenever wifi calling (wfc) begins or ends,
+ * userspace sends this information to the driver/firmware to configure
+ * wfc state. The driver/firmware uses this information to
+ * optimize power savings, rate adaption, roaming, etc.
+ *
+ * 1 - wfc is on.
+ * 0 - wfc is off.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_WFC_STATE = 86,
+
+ /* 8-bit unsigned value to configure the driver to enable/disable the
+ * EHT EML capability in management frame EHT capabilities.
+ * 1 - Enable, 0 - Disable.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_EHT_EML_CAPABILITY = 87,
+
+ /* 8-bit unsigned value to configure the driver with EHT MLO max
+ * simultaneous links to be used for MLO connection.
+ * The range of the value is 0 to 14.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_EHT_MLO_MAX_SIMULTANEOUS_LINKS = 88,
+
+ /* 8-bit unsigned value to configure the driver with EHT MLO maximum
+ * number of links to be used for MLO connection.
+ * The range of the value is 1 to 16.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_EHT_MLO_MAX_NUM_LINKS = 89,
+
+ /* 8-bit unsigned value to configure the driver with EHT MLO mode.
+ * Uses enum qca_wlan_eht_mlo_mode values.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_EHT_MLO_MODE = 90,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_CONFIG_MAX =
@@ -5058,6 +5182,42 @@
* If the current RX link speed is above the threshold, roaming is not
* needed. If this attribute is not configured, or if it is set to 0, the
* driver will not consider the RX link speed in the roaming decision.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_HO_DELAY_FOR_RX: u16 value in milliseconds.
+ * Optional parameter. This configuration delays hand-off by the
+ * specified duration to receive pending RX frames from the current BSS.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_FULL_SCAN_NO_REUSE_PARTIAL_SCAN_FREQ: Unsigned 8-bit
+ * value.
+ * During the roam scan, if there are no desired APs found in the partial
+ * frequency list, an immediate full scan on all the supported frequencies
+ * is initiated as a fallback. This flag controls the frequency list
+ * creation for the full scan on the following lines.
+ * 1 - Full scan to exclude the frequencies that were already scanned by
+ * the previous partial scan.
+ * 0 - Full scan to include all the supported frequencies irrespective of
+ * the ones part of the earlier partial scan.
+ * If this flag is not specified, a full scan shall include all the
+ * supported frequencies irrespective of the ones part of an earlier
+ * partial scan.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_FULL_SCAN_6GHZ_ONLY_ON_PRIOR_DISCOVERY: Unsigned 8-bit
+ * value.
+ * During the roam scan, if there are no desired APs found in the partial
+ * frequency list, an immediate full scan on all the supported frequencies
+ * is initiated as a fallback. This full scan would add the 2.4/5/6 GHz
+ * frequencies, including all PSC frequencies by default. This attribute
+ * controls the inclusion of the 6 GHz PSC frequencies for the full scan
+ * as following.
+ * 1 - Full scan to include the supported 6 GHz PSC frequencies only on the
+ * prior discovery of any 6 GHz frequency support in the environment.
+ * This discovery can happen through a prior RNR, 11k neighbor
+ * request, 11v BTM request, host scan, etc.
+ * 0 - Default behavior. Full scan to include all the supported 6 GHz
+ * PSC frequencies regardless of whether 6 GHz BSSs have been
+ * discovered.
+ * The default behavior if this flag is not specified is to include all
+ * the supported 6 GHz PSC frequencies in the roam full scan.
*/
enum qca_vendor_attr_roam_control {
QCA_ATTR_ROAM_CONTROL_ENABLE = 1,
@@ -5084,6 +5244,9 @@
QCA_ATTR_ROAM_CONTROL_SCAN_6G_PSC_DWELL_TIME = 22,
QCA_ATTR_ROAM_CONTROL_SCAN_6G_NON_PSC_DWELL_TIME = 23,
QCA_ATTR_ROAM_CONTROL_LINKSPEED_THRESHOLD = 24,
+ QCA_ATTR_ROAM_CONTROL_HO_DELAY_FOR_RX = 25,
+ QCA_ATTR_ROAM_CONTROL_FULL_SCAN_NO_REUSE_PARTIAL_SCAN_FREQ = 26,
+ QCA_ATTR_ROAM_CONTROL_FULL_SCAN_6GHZ_ONLY_ON_PRIOR_DISCOVERY = 27,
/* keep last */
QCA_ATTR_ROAM_CONTROL_AFTER_LAST,
@@ -5987,6 +6150,22 @@
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS_2 = 11,
/*
+ * Segment 0 in MHz (u32).
+ *
+ * For 20/40/80 MHz bandwidth, this indicates the channel center
+ * frequency index for the 20/40/80 MHz operating channel.
+ * For 160 MHz bandwidth, this indicates the channel center
+ * frequency of the primary 80 MHz channel.
+ * For 320 MHz bandwidth, indicates the channel center frequency
+ * of the primary 160 MHz channel.
+ *
+ * To maintain backward compatibility,
+ * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0
+ * is also maintained.
+ */
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_SEG_0 = 12,
+ /* Legacy alias for the Segment 0 attribute.
+ *
* VHT segment 0 in MHz (u32) and the attribute is mandatory.
* Note: Event QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS includes
* QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0
@@ -6004,9 +6183,25 @@
* is still used if either of the driver or user space application
* doesn't support the 6 GHz band.
*/
- QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0 = 12,
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0 =
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_SEG_0,
/*
+ * Segment 1 in MHz (u32).
+ *
+ * For 20/40/80 MHz bandwidth, this is set to 0.
+ * For 160 MHz bandwidth, indicates the channel center frequency of the
+ * 160 MHz channel.
+ * For 320 MHz bandwidth, indicates the channel center frequency of the
+ * 320 MHz channel.
+ *
+ * To maintain backward compatibility,
+ * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1
+ * is also maintained.
+ */
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_SEG_1 = 13,
+ /* Legacy alias for the Segment 1 attribute.
+ *
* VHT segment 1 in MHz (u32) and the attribute is mandatory.
* Note: Event QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS includes
* QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1
@@ -6024,7 +6219,8 @@
* is still used if either of the driver or user space application
* doesn't support the 6 GHz band.
*/
- QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1 = 13,
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1 =
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_SEG_1,
/*
* 16-bit attribute of bits indicating the AP power modes supported by
@@ -6041,6 +6237,33 @@
* qca_wlan_vendor_external_acs_event_chan_power_info_attr.
*/
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_POWER_INFO_ATTR = 15,
+ /*
+ * This indicates the overlapping 320 MHz center frequency in MHz
+ * (u32), if the given primary channel supports more than one
+ * 320 MHz channel bonding.
+ *
+ * Example:
+ * For 6 GHz, channel frequency 6115 MHz (channel number 33) segment 0
+ * center frequency (primary 160 MHz) is 6185 MHz and there can be two
+ * possible segment 2 frequencies for this (320 MHz center
+ * frequencies):
+ *
+ * 1) Center frequency 6105 MHz (channel 31): 320 MHz channel bonding
+ * from frequency 5945 MHz - 6265 MHz
+ * 2) Center frequency 6265 MHz (channel 63): 320 MHz channel bonding
+ * from frequency 6105 MHz - 6425 MHz
+ *
+ * In this case,
+ * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_SEG_0 will
+ * return 6185 MHz.
+ * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_SEG_1 will
+ * return 6105 MHz.
+ * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_OVERLAP_SEG_1
+ * will return 6265 MHz.
+ */
+ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_OVERLAP_SEG_1
+ = 16,
+
/* keep last */
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_LAST,
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX =
@@ -6293,6 +6516,11 @@
* VHT seg1 channel frequency in MHz
* Note: If user-space application has no support of the 6 GHz band, this
* attribute is optional.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_PUNCTURE_BITMAP: Required (u16)
+ * Puncture Bitmap for selected primary channel. Optional if no support
+ * for EHT (IEEE 802.11be). Encoding for this attribute follows the
+ * convention used in the Disabled Subchannel Bitmap field of the EHT Operation
+ * element.
*/
enum qca_wlan_vendor_attr_external_acs_channels {
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_INVALID = 0,
@@ -6328,6 +6556,7 @@
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_SECONDARY = 11,
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG0 = 12,
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG1 = 13,
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_PUNCTURE_BITMAP = 14,
/* keep last */
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LAST,
@@ -7110,6 +7339,28 @@
QCA_WLAN_HANG_BUS_FAILURE = 26,
/* tasklet/credit latency found */
QCA_WLAN_HANG_TASKLET_CREDIT_LATENCY_DETECT = 27,
+ /* MSDU buffers received in REO error ring, exceeding certain
+ * threshold
+ */
+ QCA_WLAN_HANG_RX_MSDU_BUF_RCVD_IN_ERR_RING = 28,
+ /* Vdev SM is out of sync and connect req received
+ * when already connected
+ */
+ QCA_WLAN_HANG_VDEV_SM_OUT_OF_SYNC = 29,
+ /* Stats request timeout */
+ QCA_WLAN_HANG_STATS_REQ_TIMEOUT = 30,
+ /* Leak in TX descriptor for a packet */
+ QCA_WLAN_HANG_TX_DESC_LEAK = 31,
+ /* Scheduler watchdog timeout */
+ QCA_WLAN_HANG_SCHED_TIMEOUT = 32,
+ /* Failed to send self peer deletion cmd to firmware */
+ QCA_WLAN_HANG_SELF_PEER_DEL_FAIL = 33,
+ /* Received del self sta without del bss */
+ QCA_WLAN_HANG_DEL_SELF_STA_FAIL = 34,
+ /* Recovery needed when sending flush completion to userspace */
+ QCA_WLAN_HANG_FLUSH_LOGS = 35,
+ /* Host wakeup because of page fault */
+ QCA_WLAN_HANG_HOST_WAKEUP_REASON_PAGE_FAULT = 36,
};
/**
@@ -8240,6 +8491,43 @@
};
/**
+ * enum eht_mcs_config - EHT MCS support configuration
+ *
+ * Configures the EHT Tx/Rx MCS map in EHT Capability element.
+ * These values are used in the driver to configure the EHT MCS map to advertise
+ * Tx/Rx MCS map in EHT capability and these values are applied for all the
+ * streams supported by the device.
+ * @EHT_MCS0_7: EHT MCS 0 to 7 support
+ * @EHT_MCS0_9: EHT MCS 0 to 9 support
+ * @EHT_MCS0_11: EHT MCS 0 to 11 support
+ * @EHT_MCS0_13: EHT MCS 0 to 13 support
+ */
+enum eht_mcs_config {
+ EHT_MCS0_7 = 0,
+ EHT_MCS0_9 = 1,
+ EHT_MCS0_11 = 2,
+ EHT_MCS0_13 = 3,
+};
+
+/**
+ * enum qca_wlan_eht_mlo_mode: EHT MLO mode configuration.
+ * @QCA_WLAN_EHT_MODE_INVALID: Invalid.
+ * @QCA_WLAN_EHT_MLSR: Multi-link single radio mode
+ * @QCA_WLAN_EHT_EMLSR: Enhanced multi-link single radio mode.
+ * @QCA_WLAN_EHT_NON_STR_MLMR: Non simultaneous transmit and receive
+ * multi-link multi radio mode.
+ * @QCA_WLAN_EHT_STR_MLMR: Simultaneous transmit and receive
+ * multi-link multi radio mode.
+ */
+enum qca_wlan_eht_mlo_mode {
+ QCA_WLAN_EHT_MODE_INVALID = 0,
+ QCA_WLAN_EHT_MLSR = 1,
+ QCA_WLAN_EHT_EMLSR = 2,
+ QCA_WLAN_EHT_NON_STR_MLMR = 3,
+ QCA_WLAN_EHT_STR_MLMR = 4,
+};
+
+/**
* enum qca_wlan_vendor_attr_he_omi_tx: Represents attributes for
* HE operating mode control transmit request. These attributes are
* sent as part of QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OMI_TX and
@@ -8831,6 +9119,64 @@
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BEAMFORMER_PERIODIC_SOUNDING = 59,
+ /* 8-bit unsigned value to configure beamformee SS EHT capability
+ * to indicate the maximum number of spatial streams that the STA
+ * can receive in an EHT sounding NDP for <= 80 MHz bandwidth.
+ * The range of the value is 3 to 7.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_BEAMFORMEE_SS_80MHZ = 60,
+
+ /* 8-bit unsigned value to configure beamformee SS EHT capability
+ * to indicate the maximum number of spatial streams that the STA
+ * can receive in an EHT sounding NDP for 160 MHz bandwidth.
+ * The range of the value is 3 to 7.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_BEAMFORMEE_SS_160MHZ = 61,
+
+ /* 8-bit unsigned value to configure beamformee SS EHT capability
+ * to indicate the maximum number of spatial streams that the STA
+ * can receive in an EHT sounding NDP for 320 MHz bandwidth.
+ * The range of the value is 3 to 7.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_BEAMFORMEE_SS_320MHZ = 62,
+
+ /* 8-bit unsigned value to configure the driver to exclude station
+ * profile in Probe Request frame Multi-Link element.
+ * 0 - Default behavior, sends the Probe Request frame with station
+ * profile data included in the Multi-Link element.
+ * 1 - Exclude station profile in Probe Request frame Multi-Link
+ * element.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EXCLUDE_STA_PROF_IN_PROBE_REQ = 63,
+
+ /* 8-bit unsigned value to configure EHT testbed defaults.
+ * This attribute is used to configure the testbed device.
+ * 1 - Set the device EHT capabilities to testbed defaults.
+ * 0 - Reset the device EHT capabilities to supported config.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SET_EHT_TESTBED_DEFAULTS = 64,
+
+ /* 8-bit unsigned value to indicate the EHT MCS support.
+ * Uses enum eht_mcs_config values.
+ * This attribute is used to configure the testbed device to
+ * allow the advertised hardware capabilities to be downgraded
+ * for testing purposes.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_MCS = 65,
+
+ /* 8-bit unsigned value to configure EHT TB Sounding Feedback
+ * Rate Limit capability.
+ * This attribute is used to configure the testbed device.
+ * 0 - Indicates no maximum supported data rate limitation.
+ * 1 - Indicates the maximum supported data rate is the lower of
+ * the 1500 Mb/s and the maximum supported data rate.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_TB_SOUNDING_FB_RL = 66,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX =
@@ -11797,11 +12143,15 @@
*
* @QCA_WLAN_CONCURRENT_AP_POLICY_LOSSLESS_AUDIO_STREAMING: Select interface
* concurrencies to meet lossless audio streaming requirements.
+ *
+ * @QCA_WLAN_CONCURRENT_AP_POLICY_XR: Select interface concurrencies to meet
+ * XR (eXtended Reality) requirements.
*/
enum qca_wlan_concurrent_ap_policy_config {
QCA_WLAN_CONCURRENT_AP_POLICY_UNSPECIFIED = 0,
QCA_WLAN_CONCURRENT_AP_POLICY_GAMING_AUDIO = 1,
QCA_WLAN_CONCURRENT_AP_POLICY_LOSSLESS_AUDIO_STREAMING = 2,
+ QCA_WLAN_CONCURRENT_AP_POLICY_XR = 3,
};
/**
@@ -12069,6 +12419,394 @@
};
/**
+ * enum qca_wlan_roam_stats_invoke_reason - Roam invoke reason. These values
+ * are used by the attribute
+ * %QCA_WLAN_VENDOR_ATTR_ROAM_STATS_USER_TRIGGER_INVOKE_REASON.
+ *
+ * @QCA_WLAN_ROAM_STATS_INVOKE_REASON_UNDEFINED: Default value when target
+ * invoke roam.
+ * @QCA_WLAN_ROAM_STATS_INVOKE_REASON_NUD_FAILURE: Neighbor unreachable
+ * detection failed when the roam trigger.
+ * @QCA_WLAN_ROAM_STATS_INVOKE_REASON_USER_SPACE: Invoke from user space.
+ */
+
+enum qca_wlan_roam_stats_invoke_reason {
+ QCA_WLAN_ROAM_STATS_INVOKE_REASON_UNDEFINED = 0,
+ QCA_WLAN_ROAM_STATS_INVOKE_REASON_NUD_FAILURE = 1,
+ QCA_WLAN_ROAM_STATS_INVOKE_REASON_USER_SPACE = 2,
+};
+
+/**
+ * enum qca_wlan_roam_stats_tx_failures_reason - Roam TX failures reason. These
+ * values are used by the attribute
+ * %QCA_WLAN_VENDOR_ATTR_ROAM_STATS_TX_FAILURES_REASON.
+ *
+ * @QCA_WLAN_ROAM_STATS_KICKOUT_REASON_UNSPECIFIED: Default value when
+ * roam by kickout.
+ * @QCA_WLAN_ROAM_STATS_KICKOUT_REASON_XRETRY: Excessive retry when roam
+ * trigger by kickout.
+ * @QCA_WLAN_ROAM_STATS_KICKOUT_REASON_INACTIVITY: Station inactivity when
+ * roam trigger by kickout.
+ * @QCA_WLAN_ROAM_STATS_KICKOUT_REASON_IBSS_DISCONNECT: IBSS disconnect when
+ * roam trigger by kickout.
+ * @QCA_WLAN_ROAM_STATS_KICKOUT_REASON_TDLS_DISCONNECT: TDLS peer has
+ * disappeared, and all TX is failing when roam trigger by kickout.
+ * @QCA_WLAN_ROAM_STATS_KICKOUT_REASON_SA_QUERY_TIMEOUT: SA query process
+ * timeout when roam trigger by kickout.
+ * @QCA_WLAN_ROAM_STATS_KICKOUT_REASON_ROAMING_EVENT: Directly connected
+ * peer has roamed to a repeater.
+ */
+enum qca_wlan_roam_stats_tx_failures_reason {
+ QCA_WLAN_ROAM_STATS_KICKOUT_REASON_UNSPECIFIED = 0,
+ QCA_WLAN_ROAM_STATS_KICKOUT_REASON_XRETRY = 1,
+ QCA_WLAN_ROAM_STATS_KICKOUT_REASON_INACTIVITY = 2,
+ QCA_WLAN_ROAM_STATS_KICKOUT_REASON_IBSS_DISCONNECT = 3,
+ QCA_WLAN_ROAM_STATS_KICKOUT_REASON_TDLS_DISCONNECT = 4,
+ QCA_WLAN_ROAM_STATS_KICKOUT_REASON_SA_QUERY_TIMEOUT = 5,
+ QCA_WLAN_ROAM_STATS_KICKOUT_REASON_ROAMING_EVENT = 6,
+};
+
+/**
+ * enum qca_wlan_roam_stats_abort_reason - Roam abort reason. These values
+ * are used by the attribute %QCA_WLAN_VENDOR_ATTR_ROAM_STATS_ABORT_REASON.
+ *
+ * @QCA_WLAN_ROAM_STATS_ABORT_UNSPECIFIED: Target did not specify the
+ * detailed reason for roam scan being aborted.
+ * @QCA_WLAN_ROAM_STATS_ABORT_LOWRSSI_DATA_RSSI_HIGH: Roam scan is not
+ * started due to high data RSSI during LOW-RSSI roaming.
+ * @QCA_WLAN_ROAM_STATS_ABORT_LOWRSSI_LINK_SPEED_GOOD: Roam scan is not
+ * started due to good link speed during LOW-RSSI roaming.
+ * @QCA_WLAN_ROAM_STATS_ABORT_BG_DATA_RSSI_HIGH: Roam scan is not started
+ * due to high data RSSI during background roaming.
+ * @QCA_WLAN_ROAM_STATS_ABORT_BG_RSSI_ABOVE_THRESHOLD: Roam scan is not
+ * started due to high beacon RSSI during background roaming
+ */
+enum qca_wlan_roam_stats_abort_reason {
+ QCA_WLAN_ROAM_STATS_ABORT_UNSPECIFIED = 0,
+ QCA_WLAN_ROAM_STATS_ABORT_LOWRSSI_DATA_RSSI_HIGH = 1,
+ QCA_WLAN_ROAM_STATS_ABORT_LOWRSSI_LINK_SPEED_GOOD = 2,
+ QCA_WLAN_ROAM_STATS_ABORT_BG_DATA_RSSI_HIGH = 3,
+ QCA_WLAN_ROAM_STATS_ABORT_BG_RSSI_ABOVE_THRESHOLD = 4,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_roam_stats_scan_chan_info - Attributes used inside
+ * the %QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_CHAN_INFO nested attribute.
+ */
+enum qca_wlan_vendor_attr_roam_stats_scan_chan_info {
+ /* 32-bit unsigned value to indicate center frequency of the primary
+ * channel in MHz for each roam scan channel.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_CHANNEL_FREQ = 1,
+ /* 8-bit unsigned value to indicate channel scan type for each
+ * roam scan channel. 0-passive, 1-active.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_DWELL_TYPE = 2,
+ /* 32-bit unsigned value to indicate maximum scan time in milliseconds
+ * for each roam scan channel.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_MAX_DWELL_TIME = 3,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_INFO_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_INFO_FRAME_MAX =
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_INFO_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_roam_stats_frame_subtype - Roam frame subtypes. These values
+ * are used by the attribute %QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_SUBTYPE.
+ *
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_PREAUTH: Pre-authentication frame
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_REASSOC: Reassociation frame
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M1: EAPOL-Key M1 frame
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M2: EAPOL-Key M2 frame
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M3: EAPOL-Key M3 frame
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M4: EAPOL-Key M4 frame
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_GTK_M1: EAPOL-Key GTK M1 frame
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_GTK_M2: EAPOL-Key GTK M2 frame
+ */
+enum qca_wlan_roam_stats_frame_subtype {
+ QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_PREAUTH = 1,
+ QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_REASSOC = 2,
+ QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M1 = 3,
+ QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M2 = 4,
+ QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M3 = 5,
+ QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M4 = 6,
+ QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_GTK_M1 = 7,
+ QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_GTK_M2 = 8,
+};
+
+/**
+ * enum roam_frame_status - Specifies the valid values the vendor roam frame
+ * attribute QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_STATUS can take.
+ *
+ * @QCA_WLAN_ROAM_FRAME_STATUS_SUCCESS: It indicates the roam frame was
+ * sent or received successfully.
+ * @QCA_WLAN_ROAM_FRAME_STATUS_FAIL: It indicates the roam frame sending or
+ * receiving failed.
+ */
+enum qca_wlan_roam_stats_frame_status {
+ QCA_WLAN_ROAM_STATS_FRAME_STATUS_SUCCESS = 0,
+ QCA_WLAN_ROAM_STATS_FRAME_STATUS_FAIL = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_roam_stats_frame_info - Attributes used within the
+ * %QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_INFO nested attribute.
+ */
+enum qca_wlan_vendor_attr_roam_stats_frame_info {
+ /* 8-bit unsigned value to indicate the frame subtype during
+ * roaming, one of the values in qca_wlan_roam_stats_frame_subtype.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_SUBTYPE = 1,
+ /* 8-bit unsigned value to indicate the frame is successful or failed
+ * during roaming, one of the values in
+ * qca_wlan_roam_stats_frame_status.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_STATUS = 2,
+ /* 64-bit unsigned value to indicate the timestamp for frame of
+ * preauthentication/reassociation/EAPOL-M1/EAPOL-M2/EAPOL-M3/EAPOL-M4
+ * when sent or received during roaming, timestamp in milliseconds
+ * from system boot.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_TIMESTAMP = 3,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_INFO_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_INFO_MAX =
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_INFO_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_roam_stats_info - Used by the attribute
+ * QCA_WLAN_VENDOR_ATTR_ROAM_STATS_INFO.
+ */
+enum qca_wlan_vendor_attr_roam_stats_info {
+ /* 64-bit unsigned value to indicate the timestamp when roam was
+ * triggered by the firmware, timestamp in milliseconds from system
+ * boot.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_ROAM_TRIGGER_TIMESTAMP = 1,
+ /* 32-bit unsigned value to indicate the roam trigger reason for the
+ * last roaming attempted by the firmware. This can be queried either
+ * in a connected state or disconnected state. The values of this
+ * attribute represent the roam trigger reason codes, which
+ * are defined in enum qca_roam_reason.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_TRIGGER_REASON = 2,
+ /* 8-bit unsigned value to indicate percentage of packets for which
+ * the RX rate is lower than the RX rate threshold in total RX packets,
+ * used for roaming trigger by per.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_PER_RXRATE_THRESHOLD_PERCENT = 3,
+ /* 8-bit unsigned value to indicate percentage of packets for which
+ * the TX rate is lower than TX rate threshold in total TX packets,
+ * used for roaming trigger by per.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_PER_TXRATE_THRESHOLD_PERCENT = 4,
+ /* 32-bit unsigned value to indicate final beacon miss count for
+ * trigger reason of beacon miss.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FINAL_BMISS_CNT = 5,
+ /* 32-bit unsigned value to indicate consecutive beacon miss count
+ * for trigger reason of beacon miss.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_CONSECUTIVE_BMISS_CNT = 6,
+ /* 8-bit unsigned value to indicate QOS-NULL TX status for trigger
+ * reason of beacon miss, 0 - success, 1 - fail.
+ * If QOS-NULL TX status is successful, beacon miss final count and
+ * consecutive beacon miss count will be reset to zero, and roam will
+ * not be triggered. If QOS-NULL TX status is failed, beacon miss final
+ * count and consecutive beacon miss count continue to calculate until
+ * roaming trigger by beacon miss.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BMISS_QOS_NULL_SUCCESS = 7,
+ /* 8-bit unsigned value to indicate connected AP RSSI in dBm
+ * for trigger reason of poor RSSI.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_POOR_RSSI_CURRENT_RSSI = 8,
+ /* 8-bit unsigned value to indicate RSSI threshold value in dBm
+ * for trigger reason of poor RSSI.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_POOR_RSSI_ROAM_RSSI_THRESHOLD = 9,
+ /* 8-bit unsigned value to indicate RX link speed status
+ * for trigger reason of poor RSSI, 0 - good link speed,
+ * 1 - bad link speed.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_POOR_RSSI_RX_LINKSPEED_STATUS = 10,
+ /* 8-bit unsigned value to indicate connected AP RSSI in dBm
+ * for trigger reason of better RSSI.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BETTER_RSSI_CURRENT_RSSI = 11,
+ /* 8-bit unsigned value to indicate RSSI threshold value in dBm
+ * for trigger reason of better RSSI.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BETTER_RSSI_HIGH_RSSI_THRESHOLD = 12,
+ /* 32-bit unsigned value to indicate RX throughput in bytes per second
+ * for trigger reason of congestion.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_CONGESTION_RX_TPUT = 13,
+ /* 32-bit unsigned value to indicate TX throughput in bytes per second
+ * for trigger reason of congestion.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_CONGESTION_TX_TPUT = 14,
+ /* 8-bit unsigned value to indicate roamable AP count
+ * for trigger reason of congestion.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_CONGESTION_ROAMABLE_CNT = 15,
+ /* 8-bit unsigned value to indicate invoke reason, one of the values
+ * defined in qca_wlan_roam_stats_invoke_reason.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_USER_TRIGGER_INVOKE_REASON = 16,
+ /* 8-bit unsigned value to indicate request mode for trigger reason
+ * of BTM, values are defined in IEEE Std 802.11-2020, 9.6.13.9.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BTM_REQUEST_MODE = 17,
+ /* 32-bit unsigned value to indicate disassociate time in milliseconds
+ * for trigger reason of BTM.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BTM_DISASSOC_IMMINENT_TIME = 18,
+ /* 32-bit unsigned value to indicate preferred candidate list valid
+ * interval in milliseconds for trigger reason of BTM.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BTM_VALID_INTERNAL = 19,
+ /* 8-bit unsigned value to indicate the number of preferred
+ * candidates for trigger reason of BTM.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BTM_CANDIDATE_LIST_CNT = 20,
+ /* 8-bit unsigned value to indicate response status for trigger
+ * reason of BTM, values are defined in IEEE Std 802.11-2020,
+ * Table 9-428 (BTM status code definitions).
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BTM_RESPONSE_STATUS_CODE = 21,
+ /* 32-bit unsigned value to indicate BSS termination timeout value
+ * in milliseconds for trigger reason of BTM.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BTM_BSS_TERMINATION_TIMEOUT = 22,
+ /* 32-bit unsigned value to indicate MBO associate retry timeout
+ * value in milliseconds for trigger reason of BTM.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BTM_MBO_ASSOC_RETRY_TIMEOUT = 23,
+ /* 8-bit unsigned value to indicate dialog token number
+ * for trigger reason of BTM.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BTM_REQ_DIALOG_TOKEN = 24,
+ /* 8-bit unsigned value to indicate percentage of connected AP
+ * channel congestion utilization for trigger reason of BSS load.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BSS_CU_LOAD = 25,
+ /* 8-bit unsigned value to indicate disconnection type
+ * for trigger reason of disconnection. 1 - Deauthentication,
+ * 2 - Disassociation.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_DISCONNECTION_TYPE = 26,
+ /* 16-bit unsigned value to indicate deauthentication or disassociation
+ * reason for trigger reason of disconnection, values are defined
+ * in IEEE Std 802.11-2020, Table 9-49 (Reason codes).
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_DISCONNECTION_REASON = 27,
+ /* 32-bit unsigned value to indicate milliseconds of roam scan
+ * periodicity when needing to roam to find a better AP for trigger
+ * reason of periodic timer.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_PERIODIC_TIMER_MS = 28,
+ /* 8-bit unsigned value to indicate connected AP RSSI in dBm for
+ * trigger reason of background scan.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BACKGROUND_SCAN_CURRENT_RSSI = 29,
+ /* 8-bit unsigned value to indicate data RSSI in dBm for trigger reason
+ * of background scan.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BACKGROUND_SCAN_DATA_RSSI = 30,
+ /* 8-bit unsigned value to indicate data RSSI threshold in dBm
+ * for trigger reason of background scan.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BACKGROUND_SCAN_DATA_RSSI_THRESH = 31,
+ /* 32-bit unsigned value to indicate consecutive TX failure threshold
+ * for trigger reason of TX failures.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_TX_FAILURES_THRESHOLD = 32,
+ /* 8-bit unsigned value to indicate TX failure reason for trigger
+ * reason of TX failures, one of the values defined in
+ * qca_wlan_roam_stats_tx_failures_reason.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_TX_FAILURES_REASON = 33,
+ /* 8-bit unsigned value to indicate detail abort reason. One of the
+ * values in enum qca_wlan_roam_stats_abort_reason.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_ABORT_REASON = 34,
+ /* 8-bit unsigned value to indicate data RSSI in dBm when aborting the
+ * roam scan.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_DATA_RSSI = 35,
+ /* 8-bit unsigned value to indicate data RSSI threshold in dBm when
+ * aborting the roam scan.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_DATA_RSSI_THRESHOLD = 36,
+ /* 8-bit unsigned value to indicate data RSSI threshold in RX link
+ * speed status when aborting the roam scan.
+ * 0 - good link speed, 1 - bad link speed
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_DATA_RX_LINKSPEED_STATUS = 37,
+ /* 8-bit unsigned value to indicate roaming scan type.
+ * 0 - Partial roam scan, 1 - Full roam scan
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_TYPE = 38,
+ /* 8-bit unsigned value to indicate roaming result, used in STA mode
+ * only.
+ * 0-Roaming is successful, 1-Roaming is failed
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_ROAM_STATUS = 39,
+ /* 8-bit unsigned value to indicate 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_ROAM_STATS_FAIL_REASON = 40,
+ /* Nested attribute. Indicate roam scan info for each channel, the
+ * attributes defined in enum
+ * qca_wlan_vendor_attr_roam_stats_scan_chan_info are used inside
+ * this attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_CHAN_INFO = 41,
+ /* 32-bit unsigned value to indicate total scan time during roam scan
+ * all channels, time in milliseconds.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_TOTAL_SCAN_TIME = 42,
+ /* Nested attribute. This attribute shall be used by the driver to
+ * send roam information of each subtype. The attributes defined in
+ * enum qca_wlan_vendor_attr_roam_stats_frame_info are used inside
+ * this attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_INFO = 43,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_MAX =
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_roam_cached_stats - Vendor subcmd attributes to
+ * report cached roam info from the driver to user space, enum values are used
+ * for netlink attributes sent with the
+ * %QCA_NL80211_VENDOR_SUBCMD_ROAM_STATS sub command.
+ */
+enum qca_wlan_vendor_attr_roam_cached_stats {
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_INVALID = 0,
+ /* Nested attribute, this attribute contains nested array roam info
+ * statistics defined in enum qca_wlan_vendor_attr_roam_stats_info.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_INFO = 1,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ROAM_CACHED_STATS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ROAM_CACHED_STATS_MAX =
+ QCA_WLAN_VENDOR_ATTR_ROAM_CACHED_STATS_AFTER_LAST - 1,
+};
+
+/**
* enum qca_wlan_vendor_attr_supported_radio_cfg - Attributes for
* radio configurations present in each radio combination.
*
@@ -13190,6 +13928,17 @@
* QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_NEXT_HEADER attribute with the
* filter mask specified by the
* QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_FILTER_MASK attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_SERVICE_CLASS_ID: Optional u16
+ * attribute.
+ * Represents the service class id of the configured SCS rule.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_DST_MAC_ADDR: Optional 6 bytes
+ * MAC address.
+ * Represents the destination MAC address in the rule.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_NETDEV_IF_INDEX: Optional u32 attribute
+ * Represents the netdevice interface index in the rule.
*/
enum qca_wlan_vendor_attr_scs_rule_config {
QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_INVALID = 0,
@@ -13211,6 +13960,9 @@
QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_NEXT_HEADER = 16,
QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_FILTER_MASK = 17,
QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_FILTER_VALUE = 18,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_SERVICE_CLASS_ID = 19,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_DST_MAC_ADDR = 20,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_NETDEV_IF_INDEX = 21,
/* Keep last */
QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_AFTER_LAST,
@@ -13561,4 +14313,531 @@
QCA_WLAN_VENDOR_ATTR_SR_AFTER_LAST - 1,
};
+/**
+ * enum qca_wlan_vendor_attr_mlo_peer_prim_netdev_event - Defines the attributes
+ * used in the QCA_NL80211_VENDOR_SUBCMD_MLO_PEER_PRIM_NETDEV_EVENT subcommand.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_MACADDR: 6 byte MAC address
+ * used by the peer on the link that corresponds to the link used for sending
+ * the event notification.
+ * @QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_MLD_MAC_ADDR: 6 byte
+ * MLD MAC address of the peer.
+ * @QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_PRIM_IFINDEX: u32 attribute,
+ * used to pass ifindex of the primary netdev.
+ */
+enum qca_wlan_vendor_attr_mlo_peer_prim_netdev_event {
+ QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_MACADDR = 1,
+ QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_MLD_MAC_ADDR = 2,
+ QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_PRIM_IFINDEX = 3,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_MAX =
+ QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_afc_freq_psd_info: This enum is used with
+ * nested attributes QCA_WLAN_VENDOR_ATTR_AFC_RESP_FREQ_PSD_INFO and
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_FREQ_RANGE_LIST to update the frequency range
+ * and PSD information.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_START: Required and type is
+ * u32. This attribute is used to indicate the start of the queried frequency
+ * range in MHz.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_END: Required and type is u32.
+ * This attribute is used to indicate the end of the queried frequency range
+ * in MHz.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_PSD: Required and type is u32.
+ * This attribute will contain the PSD information for a single range as
+ * specified by the QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_START and
+ * QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_END attributes.
+ *
+ * The PSD power info (dBm/MHz) from user space should be multiplied
+ * by a factor of 100 when sending to the driver to preserve granularity
+ * up to 2 decimal places.
+ * Example:
+ * PSD power value: 10.21 dBm/MHz
+ * Value to be updated in QCA_WLAN_VENDOR_ATTR_AFC_PSD_INFO: 1021.
+ *
+ * Note: QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_PSD attribute will be used only
+ * with nested attribute QCA_WLAN_VENDOR_ATTR_AFC_RESP_FREQ_PSD_INFO and with
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_FREQ_RANGE_LIST when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE.
+ *
+ * The following set of attributes will be used to exchange frequency and
+ * corresponding PSD information for AFC between the user space and the driver.
+ */
+enum qca_wlan_vendor_attr_afc_freq_psd_info {
+ QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_START = 1,
+ QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_END = 2,
+ QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_PSD = 3,
+
+ QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_MAX =
+ QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_afc_chan_eirp_info: This enum is used with
+ * nested attribute QCA_WLAN_VENDOR_ATTR_AFC_CHAN_LIST_INFO to update the
+ * channel list and corresponding EIRP information.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM: Required and type is u8.
+ * This attribute is used to indicate queried channel from
+ * the operating class indicated in QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_EIRP: Optional and type is u32.
+ * This attribute is used to configure the EIRP power info corresponding
+ * to the channel number indicated in QCA_WLAN_VENDOR_ATTR_AFC_CHAN_NUM.
+ * The EIRP power info(dBm) from user space should be multiplied
+ * by a factor of 100 when sending to Driver to preserve granularity up to
+ * 2 decimal places.
+ * Example:
+ * EIRP power value: 34.23 dBm
+ * Value to be updated in QCA_WLAN_VENDOR_ATTR_AFC_EIRP_INFO: 3423.
+ *
+ * Note: QCA_WLAN_VENDOR_ATTR_AFC_EIRP_INFO attribute will only be used with
+ * nested attribute QCA_WLAN_VENDOR_ATTR_AFC_RESP_OPCLASS_CHAN_EIRP_INFO and
+ * with QCA_WLAN_VENDOR_ATTR_AFC_EVENT_OPCLASS_CHAN_LIST when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE:
+ *
+ * The following set of attributes will be used to exchange Channel and
+ * corresponding EIRP information for AFC between the user space and Driver.
+ */
+enum qca_wlan_vendor_attr_afc_chan_eirp_info {
+ QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM = 1,
+ QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_EIRP = 2,
+
+ QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_MAX =
+ QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_afc_opclass_info: This enum is used with nested
+ * attributes QCA_WLAN_VENDOR_ATTR_AFC_RESP_OPCLASS_CHAN_EIRP_INFO and
+ * QCA_WLAN_VENDOR_ATTR_AFC_REQ_OPCLASS_CHAN_INFO to update the operating class,
+ * channel, and EIRP related information.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_OPCLASS: Required and type is u8.
+ * This attribute is used to indicate the operating class, as listed under
+ * IEEE Std 802.11-2020 Annex E Table E-4, for the queried channel list.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_CHAN_LIST: Array of nested attributes
+ * for updating the channel number and EIRP power information.
+ * It uses the attributes defined in
+ * enum qca_wlan_vendor_attr_afc_chan_eirp_info.
+ *
+ * Operating class information packing format for
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_OPCLASS_CHAN_INFO when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE_EXPIRY.
+ *
+ * m - Total number of operating classes.
+ * n, j - Number of queried channels for the corresponding operating class.
+ *
+ * QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_OPCLASS[0]
+ * QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_CHAN_LIST[0]
+ * QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM[0]
+ * .....
+ * QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM[n - 1]
+ * ....
+ * QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_OPCLASS[m]
+ * QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_CHAN_LIST[m]
+ * QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM[0]
+ * ....
+ * QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM[j - 1]
+ *
+ * Operating class information packing format for
+ * QCA_WLAN_VENDOR_ATTR_AFC_RESP_OPCLASS_CHAN_EIRP_INFO and
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_OPCLASS_CHAN_INFO when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE.
+ *
+ * m - Total number of operating classes.
+ * n, j - Number of channels for the corresponding operating class.
+ *
+ * QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_OPCLASS[0]
+ * QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_CHAN_LIST[0]
+ * QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM[0]
+ * QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_EIRP[0]
+ * .....
+ * QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM[n - 1]
+ * QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_EIRP[n - 1]
+ * ....
+ * QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_OPCLASS[m]
+ * QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_CHAN_LIST[m]
+ * QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM[0]
+ * QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_EIRP[0]
+ * ....
+ * QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM[j - 1]
+ * QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_EIRP[j - 1]
+ *
+ * The following set of attributes will be used to exchange operating class
+ * information for AFC between the user space and the driver.
+ */
+enum qca_wlan_vendor_attr_afc_opclass_info {
+ QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_OPCLASS = 1,
+ QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_CHAN_LIST = 2,
+
+ QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_MAX =
+ QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_afc_event_type: Defines values for AFC event type.
+ * Attribute used by QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE attribute.
+ *
+ * @QCA_WLAN_VENDOR_AFC_EVENT_TYPE_EXPIRY: AFC expiry event sent from the
+ * driver to userspace in order to query the new AFC power values.
+ *
+ * @QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE: Power update
+ * complete event will be sent from the driver to userspace to indicate
+ * processing of the AFC response.
+ *
+ * @QCA_WLAN_VENDOR_AFC_EVENT_TYPE_PAYLOAD_RESET: AFC payload reset event
+ * will be sent from the driver to userspace to indicate last received
+ * AFC response data has been cleared on the AP due to invalid data
+ * in the QCA_NL80211_VENDOR_SUBCMD_AFC_RESPONSE.
+ *
+ * The following enum defines the different event types that will be
+ * used by the driver to help trigger corresponding AFC functionality in user
+ * space.
+ */
+enum qca_wlan_vendor_afc_event_type {
+ QCA_WLAN_VENDOR_AFC_EVENT_TYPE_EXPIRY = 0,
+ QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE = 1,
+ QCA_WLAN_VENDOR_AFC_EVENT_TYPE_PAYLOAD_RESET = 2,
+};
+
+/**
+ * enum qca_wlan_vendor_afc_ap_deployment_type: Defines values for AP
+ * deployment type.
+ * Attribute used by QCA_WLAN_VENDOR_ATTR_AFC_EVENT_AP_DEPLOYMENT attribute.
+ *
+ * @QCA_WLAN_VENDOR_AFC_AP_DEPLOYMENT_TYPE_UNKNOWN: Unknown AP deployment.
+ *
+ * @QCA_WLAN_VENDOR_AFC_AP_DEPLOYMENT_TYPE_INDOOR: Indoor AP deployment.
+ *
+ * @QCA_WLAN_VENDOR_AFC_AP_DEPLOYMENT_TYPE_OUTDOOR: Outdoor AP deployment.
+ *
+ * The following enum defines different deployment modes that the AP might
+ * come up in. This information will be essential to retrieve deployment-type
+ * specific SP power values for AFC operation.
+ */
+enum qca_wlan_vendor_afc_ap_deployment_type {
+ QCA_WLAN_VENDOR_AFC_AP_DEPLOYMENT_TYPE_UNKNOWN = 0,
+ QCA_WLAN_VENDOR_AFC_AP_DEPLOYMENT_TYPE_INDOOR = 1,
+ QCA_WLAN_VENDOR_AFC_AP_DEPLOYMENT_TYPE_OUTDOOR = 2,
+};
+
+/**
+ * enum qca_wlan_vendor_afc_evt_status_code: Defines values AP will use to
+ * indicate AFC response status.
+ * Enum used by QCA_WLAN_VENDOR_ATTR_AFC_EVENT_STATUS_CODE attribute.
+ *
+ * @QCA_WLAN_VENDOR_AFC_EVT_STATUS_CODE_SUCCESS: Success
+ *
+ * @QCA_WLAN_VENDOR_AFC_EVT_STATUS_CODE_TIMEOUT: Indicates AFC indication
+ * command was not received within the expected time of the AFC expiry event
+ * being triggered.
+ *
+ * @QCA_WLAN_VENDOR_AFC_EVT_STATUS_CODE_PARSING_ERROR: Indicates AFC data
+ * parsing error by the driver.
+ *
+ * @QCA_WLAN_VENDOR_AFC_EVT_STATUS_CODE_LOCAL_ERROR: Indicates any other local
+ * error.
+ *
+ * The following enum defines the status codes that the driver will use to
+ * indicate whether the AFC data is valid or not.
+ */
+enum qca_wlan_vendor_afc_evt_status_code {
+ QCA_WLAN_VENDOR_AFC_EVT_STATUS_CODE_SUCCESS = 0,
+ QCA_WLAN_VENDOR_AFC_EVT_STATUS_CODE_TIMEOUT = 1,
+ QCA_WLAN_VENDOR_AFC_EVT_STATUS_CODE_PARSING_ERROR = 2,
+ QCA_WLAN_VENDOR_AFC_EVT_STATUS_CODE_LOCAL_ERROR = 3,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_afc_event: Defines attributes to be used with
+ * vendor event QCA_NL80211_VENDOR_SUBCMD_AFC_EVENT. These attributes will
+ * support sending only a single request to the user space at a time.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE: Required u8 attribute.
+ * Used with event to notify the type of AFC event received.
+ * Valid values are defined in enum qca_wlan_vendor_afc_event_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_AP_DEPLOYMENT: u8 attribute. Required when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is QCA_WLAN_VENDOR_AFC_EVENT_TYPE_EXPIRY,
+ * otherwise unused.
+ *
+ * This attribute is used to indicate the AP deployment type in the AFC request.
+ * Valid values are defined in enum qca_wlan_vendor_afc_ap_deployment_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_REQ_ID: Required u32 attribute.
+ * Unique request identifier generated by the AFC client for every
+ * AFC expiry event trigger. See also QCA_WLAN_VENDOR_ATTR_AFC_RESP_REQ_ID.
+ * The user space application is responsible for ensuring no duplicate values
+ * are in-flight with the server, e.g., by delaying a request, should the same
+ * value be received from different radios in parallel.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_AFC_WFA_VERSION: u32 attribute. Optional.
+ * It is used when the QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_EXPIRY, otherwise unused.
+ *
+ * This attribute indicates the AFC spec version information. This will
+ * indicate the AFC version AFC client must use to query the AFC data.
+ * Bits 15:0 - Minor version
+ * Bits 31:16 - Major version
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_MIN_DES_POWER: u16 attribute. Required when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is QCA_WLAN_VENDOR_AFC_EVENT_TYPE_EXPIRY,
+ * otherwise unused.
+ * This attribute indicates the minimum desired power (in dBm) for
+ * the queried spectrum.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_STATUS_CODE: u8 attribute. Required when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE, otherwise unused.
+ *
+ * Valid values are defined in enum qca_wlan_vendor_afc_evt_status_code.
+ * This attribute is used to indicate if there were any errors parsing the
+ * AFC response.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_SERVER_RESP_CODE: s32 attribute. Required
+ * when QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE, otherwise unused.
+ *
+ * This attribute indicates the AFC response code. The AFC response codes are
+ * in the following categories:
+ * -1: General Failure.
+ * 0: Success.
+ * 100 - 199: General errors related to protocol.
+ * 300 - 399: Error events specific to message exchange
+ * for the Available Spectrum Inquiry.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_EXP_DATE: u32 attribute. Required when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE, otherwise unused.
+ *
+ * This attribute indicates the date until which the current response is
+ * valid for in UTC format.
+ * Date format: bits 7:0 - DD (Day 1-31)
+ * bits 15:8 - MM (Month 1-12)
+ * bits 31:16 - YYYY (Year)
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_EXP_TIME: u32 attribute. Required when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE, otherwise unused.
+ *
+ * This attribute indicates the time until which the current response is
+ * valid for in UTC format.
+ * Time format: bits 7:0 - SS (Seconds 0-59)
+ * bits 15:8 - MM (Minutes 0-59)
+ * bits 23:16 - HH (Hours 0-23)
+ * bits 31:24 - Reserved
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_FREQ_RANGE_LIST: Array of nested attributes
+ * for updating the list of frequency ranges to be queried.
+ * Required when QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_EXPIRY or
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE, otherwise unused.
+ * It uses the attributes defined in
+ * enum qca_wlan_vendor_attr_afc_freq_psd_info.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_OPCLASS_CHAN_LIST: Array of nested attributes
+ * for updating the list of operating classes and corresponding channels to be
+ * queried.
+ * Required when QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_EXPIRY or
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE, otherwise unused.
+ * It uses the attributes defined in enum qca_wlan_vendor_attr_afc_opclass_info.
+ */
+enum qca_wlan_vendor_attr_afc_event {
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE = 1,
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_AP_DEPLOYMENT = 2,
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_REQ_ID = 3,
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_AFC_WFA_VERSION = 4,
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_MIN_DES_POWER = 5,
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_STATUS_CODE = 6,
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_SERVER_RESP_CODE = 7,
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_EXP_DATE = 8,
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_EXP_TIME = 9,
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_FREQ_RANGE_LIST = 10,
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_OPCLASS_CHAN_LIST = 11,
+
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_MAX =
+ QCA_WLAN_VENDOR_ATTR_AFC_EVENT_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_afc_response: Defines attributes to be used
+ * with vendor command QCA_NL80211_VENDOR_SUBCMD_AFC_RESPONSE. These attributes
+ * will support sending only a single AFC response to the driver at a time.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_RESP_DATA: Type is NLA_STRING. Required attribute.
+ * This attribute will be used to send a single Spectrum Inquiry response object
+ * from the 'availableSpectrumInquiryResponses' array object from the response
+ * JSON.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_RESP_TIME_TO_LIVE: Required u32 attribute.
+ *
+ * This attribute indicates the period (in seconds) for which the response
+ * data received is valid for.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_RESP_REQ_ID: Required u32 attribute.
+ *
+ * This attribute indicates the request ID for which the corresponding
+ * response is being sent for. See also QCA_WLAN_VENDOR_ATTR_AFC_EVENT_REQ_ID.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_RESP_EXP_DATE: Required u32 attribute.
+ *
+ * This attribute indicates the date until which the current response is
+ * valid for in UTC format.
+ * Date format: bits 7:0 - DD (Day 1-31)
+ * bits 15:8 - MM (Month 1-12)
+ * bits 31:16 - YYYY (Year)
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_RESP_EXP_TIME: Required u32 attribute.
+ *
+ * This attribute indicates the time until which the current response is
+ * valid for in UTC format.
+ * Time format: bits 7:0 - SS (Seconds 0-59)
+ * bits 15:8 - MM (Minutes 0-59)
+ * bits 23:16 - HH (Hours 0-23)
+ * bits 31:24 - Reserved
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_RESP_AFC_SERVER_RESP_CODE: Required s32 attribute.
+ *
+ * This attribute indicates the AFC response code. The AFC response codes are
+ * in the following categories:
+ * -1: General Failure.
+ * 0: Success.
+ * 100 - 199: General errors related to protocol.
+ * 300 - 399: Error events specific to message exchange
+ * for the Available Spectrum Inquiry.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_RESP_FREQ_PSD_INFO: Array of nested attributes
+ * for PSD info of all the queried frequency ranges. It uses the attributes
+ * defined in enum qca_wlan_vendor_attr_afc_freq_psd_info. Required attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_RESP_OPCLASS_CHAN_EIRP_INFO: Array of nested
+ * attributes for EIRP info of all queried operating class/channels. It uses
+ * the attributes defined in enum qca_wlan_vendor_attr_afc_opclass_info and
+ * enum qca_wlan_vendor_attr_afc_chan_eirp_info. Required attribute.
+ *
+ */
+enum qca_wlan_vendor_attr_afc_response {
+ QCA_WLAN_VENDOR_ATTR_AFC_RESP_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_AFC_RESP_DATA = 1,
+ QCA_WLAN_VENDOR_ATTR_AFC_RESP_TIME_TO_LIVE = 2,
+ QCA_WLAN_VENDOR_ATTR_AFC_RESP_REQ_ID = 3,
+ QCA_WLAN_VENDOR_ATTR_AFC_RESP_EXP_DATE = 4,
+ QCA_WLAN_VENDOR_ATTR_AFC_RESP_EXP_TIME = 5,
+ QCA_WLAN_VENDOR_ATTR_AFC_RESP_AFC_SERVER_RESP_CODE = 6,
+ QCA_WLAN_VENDOR_ATTR_AFC_RESP_FREQ_PSD_INFO = 7,
+ QCA_WLAN_VENDOR_ATTR_AFC_RESP_OPCLASS_CHAN_EIRP_INFO = 8,
+
+ QCA_WLAN_VENDOR_ATTR_AFC_RESP_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_AFC_RESP_MAX =
+ QCA_WLAN_VENDOR_ATTR_AFC_RESP_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_dozed_ap_state - Doze states for AP interface
+ *
+ * @QCA_WLAN_DOZED_AP_DISABLE: Disable doze state on the AP interface.
+ *
+ * @QCA_WLAN_DOZED_AP_ENABLE: Enable doze state on the AP interface. AP starts
+ * beaconing at higher beacon interval with Rx disabled.
+ */
+enum qca_wlan_dozed_ap_state {
+ QCA_WLAN_DOZED_AP_DISABLE = 0,
+ QCA_WLAN_DOZED_AP_ENABLE = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_dozed_ap - Used by the vendor command
+ * @QCA_NL80211_VENDOR_SUBCMD_DOZED_AP to configure or receive dozed AP mode
+ * configuration.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DOZED_AP_STATE: u8 attribute.
+ * Configures the doze state for an AP interface. Possible values are defined
+ * in enum qca_wlan_dozed_ap_state. @QCA_NL80211_VENDOR_SUBCMD_DOZED_AP event
+ * gets triggered asynchronously to provide updated AP interface configuration.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DOZED_AP_COOKIE: Unsigned 64-bit cookie provided by
+ * the driver in the response to specific @QCA_NL80211_VENDOR_SUBCMD_DOZED_AP
+ * command, which is used later to maintain synchronization between commands
+ * and asynchronous events.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DOZED_AP_NEXT_TSF: u64 attribute.
+ * Used in event to indicate the next TBTT TSF timer value after applying the
+ * doze mode configuration. Next TBTT TSF is the time at which the AP sends
+ * the first beacon after entering or exiting dozed mode.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DOZED_AP_BI_MULTIPLIER: u16 attribute.
+ * Used with event to inform the periodicity of beacon transmission that would
+ * be skipped at all TBTTs in between.
+ */
+enum qca_wlan_vendor_attr_dozed_ap {
+ QCA_WLAN_VENDOR_ATTR_DOZED_AP_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_DOZED_AP_STATE = 1,
+ QCA_WLAN_VENDOR_ATTR_DOZED_AP_COOKIE = 2,
+ QCA_WLAN_VENDOR_ATTR_DOZED_AP_NEXT_TSF = 3,
+ QCA_WLAN_VENDOR_ATTR_DOZED_AP_BI_MULTIPLIER = 4,
+
+ /* Keep last */
+ QCA_WLAN_VENDOR_ATTR_DOZED_AP_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_DOZED_AP_MAX =
+ QCA_WLAN_VENDOR_ATTR_DOZED_AP_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_monitor_mode_status - Represents the status codes
+ * used with QCA_NL80211_VENDOR_SUBCMD_GET_MONITOR_MODE.
+ * @QCA_WLAN_VENDOR_MONITOR_MODE_NO_CAPTURE_RUNNING: Used to indicate no
+ * capture running status.
+ * @QCA_WLAN_VENDOR_MONITOR_MODE_CAPTURE_RUNNING: Used to indicate
+ * capture running status.
+ **/
+
+enum qca_wlan_vendor_monitor_mode_status {
+ QCA_WLAN_VENDOR_MONITOR_MODE_NO_CAPTURE_RUNNING = 0,
+ QCA_WLAN_VENDOR_MONITOR_MODE_CAPTURE_RUNNING = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_get_monitor_mode - Used by the
+ * vendor command QCA_NL80211_VENDOR_SUBCMD_GET_MONITOR_MODE to report
+ * information regarding the local packet capture over the monitor mode.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_MONITOR_MODE_STATUS: u32 attribute. This attribute
+ * represents the status of the start capture commands. The values used with
+ * this attribute are defined in enum qca_wlan_vendor_monitor_mode_status. This
+ * is returned by the driver in the response to the command.
+ */
+
+enum qca_wlan_vendor_attr_get_monitor_mode {
+ QCA_WLAN_VENDOR_ATTR_GET_MONITOR_MODE_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_GET_MONITOR_MODE_STATUS = 1,
+
+ /* Keep last */
+ QCA_WLAN_VENDOR_ATTR_GET_MONITOR_MODE_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_GET_MONITOR_MODE_MAX =
+ QCA_WLAN_VENDOR_ATTR_GET_MONITOR_MODE_AFTER_LAST - 1,
+};
+
#endif /* QCA_VENDOR_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index 33c7e5f..d4a196f 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -603,9 +603,9 @@
case 30:
*z = 7;
return 0;
+ default:
+ return -1;
}
-
- return -1;
}
@@ -1606,7 +1606,9 @@
* (commit-scalar + peer-commit-scalar) mod r part as a bit string by
* zero padding it from left to the length of the order (in full
* octets). */
- crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->order_len);
+ if (crypto_bignum_to_bin(tmp, val, sizeof(val),
+ sae->tmp->order_len) < 0)
+ goto fail;
wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN);
#ifdef CONFIG_SAE_PK
@@ -1967,8 +1969,10 @@
crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0);
sae->tmp->peer_commit_element_ecc =
crypto_ec_point_from_bin(sae->tmp->ec, *pos);
- if (sae->tmp->peer_commit_element_ecc == NULL)
+ if (!sae->tmp->peer_commit_element_ecc) {
+ wpa_printf(MSG_DEBUG, "SAE: Peer element is not a valid point");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
if (!crypto_ec_point_is_on_curve(sae->tmp->ec,
sae->tmp->peer_commit_element_ecc)) {
@@ -2153,7 +2157,7 @@
u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
const u8 **token, size_t *token_len, int *allowed_groups,
- int h2e)
+ int h2e, int *ie_offset)
{
const u8 *pos = data, *end = data + len;
u16 res;
@@ -2179,6 +2183,9 @@
if (res != WLAN_STATUS_SUCCESS)
return res;
+ if (ie_offset)
+ *ie_offset = pos - data;
+
/* Optional Password Identifier element */
res = sae_parse_password_identifier(sae, &pos, end);
if (res != WLAN_STATUS_SUCCESS)
@@ -2372,7 +2379,8 @@
}
-int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len)
+int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len,
+ int *ie_offset)
{
u8 verifier[SAE_MAX_HASH_LEN];
size_t hash_len;
@@ -2428,6 +2436,10 @@
return -1;
#endif /* CONFIG_SAE_PK */
+ /* 2 bytes are for send-confirm, then the hash, followed by IEs */
+ if (ie_offset)
+ *ie_offset = 2 + hash_len;
+
return 0;
}
diff --git a/src/common/sae.h b/src/common/sae.h
index 6a6b0c8..c446da3 100644
--- a/src/common/sae.h
+++ b/src/common/sae.h
@@ -136,9 +136,10 @@
const struct wpabuf *token, const char *identifier);
u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
const u8 **token, size_t *token_len, int *allowed_groups,
- int h2e);
+ int h2e, int *ie_offset);
int sae_write_confirm(struct sae_data *sae, struct wpabuf *buf);
-int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len);
+int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len,
+ int *ie_offset);
u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group);
const char * sae_state_txt(enum sae_state state);
size_t sae_ecc_prime_len_2_hash_len(size_t prime_len);
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index da707e6..15ebcab 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -882,7 +882,7 @@
#ifdef CONFIG_IEEE80211R
-int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
+int wpa_ft_mic(int key_mgmt, const u8 *kck, size_t kck_len, const u8 *sta_addr,
const u8 *ap_addr, u8 transaction_seqnum,
const u8 *mdie, size_t mdie_len,
const u8 *ftie, size_t ftie_len,
@@ -894,8 +894,9 @@
const u8 *addr[10];
size_t len[10];
size_t i, num_elem = 0;
- u8 zero_mic[24];
+ u8 zero_mic[32];
size_t mic_len, fte_fixed_len;
+ int res;
if (kck_len == 16) {
mic_len = 16;
@@ -903,6 +904,10 @@
} else if (kck_len == 24) {
mic_len = 24;
#endif /* CONFIG_SHA384 */
+#ifdef CONFIG_SHA512
+ } else if (kck_len == 32) {
+ mic_len = 32;
+#endif /* CONFIG_SHA512 */
} else {
wpa_printf(MSG_WARNING, "FT: Unsupported KCK length %u",
(unsigned int) kck_len);
@@ -967,6 +972,17 @@
for (i = 0; i < num_elem; i++)
wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", addr[i], len[i]);
+ res = -1;
+#ifdef CONFIG_SHA512
+ if (kck_len == 32) {
+ u8 hash[SHA512_MAC_LEN];
+
+ if (hmac_sha512_vector(kck, kck_len, num_elem, addr, len, hash))
+ return -1;
+ os_memcpy(mic, hash, 32);
+ res = 0;
+ }
+#endif /* CONFIG_SHA384 */
#ifdef CONFIG_SHA384
if (kck_len == 24) {
u8 hash[SHA384_MAC_LEN];
@@ -974,26 +990,34 @@
if (hmac_sha384_vector(kck, kck_len, num_elem, addr, len, hash))
return -1;
os_memcpy(mic, hash, 24);
+ res = 0;
}
#endif /* CONFIG_SHA384 */
- if (kck_len == 16 &&
- omac1_aes_128_vector(kck, num_elem, addr, len, mic))
- return -1;
+ if (kck_len == 16 && key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
+ u8 hash[SHA256_MAC_LEN];
- return 0;
+ if (hmac_sha256_vector(kck, kck_len, num_elem, addr, len, hash))
+ return -1;
+ os_memcpy(mic, hash, 16);
+ res = 0;
+ }
+ if (kck_len == 16 && key_mgmt != WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ omac1_aes_128_vector(kck, num_elem, addr, len, mic) == 0)
+ res = 0;
+
+ return res;
}
static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
- struct wpa_ft_ies *parse, int use_sha384)
+ struct wpa_ft_ies *parse, const u8 *opt)
{
const u8 *end, *pos;
parse->ftie = ie;
parse->ftie_len = ie_len;
- pos = ie + (use_sha384 ? sizeof(struct rsn_ftie_sha384) :
- sizeof(struct rsn_ftie));
+ pos = opt;
end = ie + ie_len;
wpa_hexdump(MSG_DEBUG, "FT: Parse FTE subelements", pos, end - pos);
@@ -1004,7 +1028,7 @@
len = *pos++;
if (len > end - pos) {
wpa_printf(MSG_DEBUG, "FT: Truncated subelement");
- break;
+ return -1;
}
switch (id) {
@@ -1016,8 +1040,11 @@
return -1;
}
parse->r1kh_id = pos;
+ wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID",
+ parse->r1kh_id, FT_R1KH_ID_LEN);
break;
case FTIE_SUBELEM_GTK:
+ wpa_printf(MSG_DEBUG, "FT: GTK");
parse->gtk = pos;
parse->gtk_len = len;
break;
@@ -1030,8 +1057,11 @@
}
parse->r0kh_id = pos;
parse->r0kh_id_len = len;
+ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID",
+ parse->r0kh_id, parse->r0kh_id_len);
break;
case FTIE_SUBELEM_IGTK:
+ wpa_printf(MSG_DEBUG, "FT: IGTK");
parse->igtk = pos;
parse->igtk_len = len;
break;
@@ -1039,9 +1069,12 @@
case FTIE_SUBELEM_OCI:
parse->oci = pos;
parse->oci_len = len;
+ wpa_hexdump(MSG_DEBUG, "FT: OCI",
+ parse->oci, parse->oci_len);
break;
#endif /* CONFIG_OCV */
case FTIE_SUBELEM_BIGTK:
+ wpa_printf(MSG_DEBUG, "FT: BIGTK");
parse->bigtk = pos;
parse->bigtk_len = len;
break;
@@ -1057,20 +1090,73 @@
}
-int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
- struct wpa_ft_ies *parse, int use_sha384)
+static int wpa_ft_parse_fte(int key_mgmt, const u8 *ie, size_t len,
+ struct wpa_ft_ies *parse)
+{
+ size_t mic_len;
+ u8 mic_len_info;
+ const u8 *pos = ie;
+ const u8 *end = pos + len;
+
+ wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC Control", pos, 2);
+ parse->fte_rsnxe_used = pos[0] & FTE_MIC_CTRL_RSNXE_USED;
+ mic_len_info = (pos[0] & FTE_MIC_CTRL_MIC_LEN_MASK) >>
+ FTE_MIC_CTRL_MIC_LEN_SHIFT;
+ parse->fte_elem_count = pos[1];
+ pos += 2;
+
+ if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
+ switch (mic_len_info) {
+ case FTE_MIC_LEN_16:
+ mic_len = 16;
+ break;
+ case FTE_MIC_LEN_24:
+ mic_len = 24;
+ break;
+ case FTE_MIC_LEN_32:
+ mic_len = 32;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "FT: Unknown MIC Length subfield value %u",
+ mic_len_info);
+ return -1;
+ }
+ } else {
+ mic_len = wpa_key_mgmt_sha384(key_mgmt) ? 24 : 16;
+ }
+ if (mic_len > (size_t) (end - pos)) {
+ wpa_printf(MSG_DEBUG, "FT: No room for %zu octet MIC in FTE",
+ mic_len);
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC", pos, mic_len);
+ parse->fte_mic = pos;
+ parse->fte_mic_len = mic_len;
+ pos += mic_len;
+
+ if (2 * WPA_NONCE_LEN > end - pos)
+ return -1;
+ parse->fte_anonce = pos;
+ wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce",
+ parse->fte_anonce, WPA_NONCE_LEN);
+ pos += WPA_NONCE_LEN;
+ parse->fte_snonce = pos;
+ wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce",
+ parse->fte_snonce, WPA_NONCE_LEN);
+ pos += WPA_NONCE_LEN;
+
+ return wpa_ft_parse_ftie(ie, len, parse, pos);
+}
+
+
+int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse,
+ int key_mgmt)
{
const u8 *end, *pos;
struct wpa_ie_data data;
int ret;
- const struct rsn_ftie *ftie;
int prot_ie_count = 0;
- int update_use_sha384 = 0;
-
- if (use_sha384 < 0) {
- use_sha384 = 0;
- update_use_sha384 = 1;
- }
os_memset(parse, 0, sizeof(*parse));
if (ies == NULL)
@@ -1104,11 +1190,8 @@
parse->rsn_pmkid = data.pmkid;
parse->key_mgmt = data.key_mgmt;
parse->pairwise_cipher = data.pairwise_cipher;
- if (update_use_sha384) {
- use_sha384 =
- wpa_key_mgmt_sha384(parse->key_mgmt);
- update_use_sha384 = 0;
- }
+ if (!key_mgmt)
+ key_mgmt = parse->key_mgmt;
break;
case WLAN_EID_RSNX:
wpa_hexdump(MSG_DEBUG, "FT: RSNXE", pos, len);
@@ -1126,47 +1209,19 @@
break;
case WLAN_EID_FAST_BSS_TRANSITION:
wpa_hexdump(MSG_DEBUG, "FT: FTE", pos, len);
- if (use_sha384) {
- const struct rsn_ftie_sha384 *ftie_sha384;
+ /* The first two octets (MIC Control field) is in the
+ * same offset for all cases, but the second field (MIC)
+ * has variable length with three different values.
+ * In particular the FT-SAE-EXT-KEY is inconvinient to
+ * parse, so try to handle this in pieces instead of
+ * using the struct rsn_ftie* definitions. */
- if (len < sizeof(*ftie_sha384))
- return -1;
- ftie_sha384 =
- (const struct rsn_ftie_sha384 *) pos;
- wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC Control",
- ftie_sha384->mic_control, 2);
- wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC",
- ftie_sha384->mic,
- sizeof(ftie_sha384->mic));
- parse->fte_anonce = ftie_sha384->anonce;
- wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce",
- ftie_sha384->anonce,
- WPA_NONCE_LEN);
- parse->fte_snonce = ftie_sha384->snonce;
- wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce",
- ftie_sha384->snonce,
- WPA_NONCE_LEN);
- prot_ie_count = ftie_sha384->mic_control[1];
- if (wpa_ft_parse_ftie(pos, len, parse, 1) < 0)
- return -1;
- break;
- }
-
- if (len < sizeof(*ftie))
+ if (len < 2)
return -1;
- ftie = (const struct rsn_ftie *) pos;
- wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC Control",
- ftie->mic_control, 2);
- wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC",
- ftie->mic, sizeof(ftie->mic));
- parse->fte_anonce = ftie->anonce;
- wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce",
- ftie->anonce, WPA_NONCE_LEN);
- parse->fte_snonce = ftie->snonce;
- wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce",
- ftie->snonce, WPA_NONCE_LEN);
- prot_ie_count = ftie->mic_control[1];
- if (wpa_ft_parse_ftie(pos, len, parse, 0) < 0)
+ prot_ie_count = pos[1]; /* Element Count field in
+ * MIC Control */
+
+ if (wpa_ft_parse_fte(key_mgmt, pos, len, parse) < 0)
return -1;
break;
case WLAN_EID_TIMEOUT_INTERVAL:
@@ -1249,7 +1304,7 @@
* 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)
+bool pasn_use_sha384(int akmp, int cipher)
{
return (akmp == WPA_KEY_MGMT_PASN && (cipher == WPA_CIPHER_CCMP_256 ||
cipher == WPA_CIPHER_GCMP_256)) ||
@@ -1988,30 +2043,40 @@
const u8 *ssid, size_t ssid_len,
const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name,
- int use_sha384)
+ int key_mgmt)
{
u8 buf[1 + SSID_MAX_LEN + MOBILITY_DOMAIN_ID_LEN + 1 +
FT_R0KH_ID_MAX_LEN + ETH_ALEN];
- u8 *pos, r0_key_data[64], hash[48];
+ u8 *pos, r0_key_data[64 + 16], hash[64];
const u8 *addr[2];
size_t len[2];
- size_t q = use_sha384 ? 48 : 32;
- size_t r0_key_data_len = q + 16;
+ size_t q, r0_key_data_len;
+ int res;
+
+ if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ (xxkey_len == SHA256_MAC_LEN || xxkey_len == SHA384_MAC_LEN ||
+ xxkey_len == SHA512_MAC_LEN))
+ q = xxkey_len;
+ else if (wpa_key_mgmt_sha384(key_mgmt))
+ q = SHA384_MAC_LEN;
+ else
+ q = SHA256_MAC_LEN;
+ r0_key_data_len = q + 16;
/*
- * R0-Key-Data = KDF-384(XXKey, "FT-R0",
+ * R0-Key-Data = KDF-Hash-Length(XXKey, "FT-R0",
* SSIDlength || SSID || MDID || R0KHlength ||
* R0KH-ID || S0KH-ID)
* XXKey is either the second 256 bits of MSK or PSK; or the first
- * 384 bits of MSK for FT-EAP-SHA384.
+ * 384 bits of MSK for FT-EAP-SHA384; or PMK from SAE.
* PMK-R0 = L(R0-Key-Data, 0, Q)
* PMK-R0Name-Salt = L(R0-Key-Data, Q, 128)
- * Q = 384 for FT-EAP-SHA384; otherwise, 256
+ * Q = 384 for FT-EAP-SHA384; the length of the digest generated by H()
+ * for FT-SAE-EXT-KEY; or otherwise, 256
*/
if (ssid_len > SSID_MAX_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN)
return -1;
- wpa_printf(MSG_DEBUG, "FT: Derive PMK-R0 using KDF-%s",
- use_sha384 ? "SHA384" : "SHA256");
+ wpa_printf(MSG_DEBUG, "FT: Derive PMK-R0 using KDF-SHA%zu", q * 8);
wpa_hexdump_key(MSG_DEBUG, "FT: XXKey", xxkey, xxkey_len);
wpa_hexdump_ascii(MSG_DEBUG, "FT: SSID", ssid, ssid_len);
wpa_hexdump(MSG_DEBUG, "FT: MDID", mdid, MOBILITY_DOMAIN_ID_LEN);
@@ -2029,30 +2094,43 @@
os_memcpy(pos, s0kh_id, ETH_ALEN);
pos += ETH_ALEN;
+ res = -1;
+#ifdef CONFIG_SHA512
+ if (q == SHA512_MAC_LEN) {
+ if (xxkey_len != SHA512_MAC_LEN) {
+ wpa_printf(MSG_ERROR,
+ "FT: Unexpected XXKey length %d (expected %d)",
+ (int) xxkey_len, SHA512_MAC_LEN);
+ return -1;
+ }
+ res = sha512_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
+ r0_key_data, r0_key_data_len);
+ }
+#endif /* CONFIG_SHA512 */
#ifdef CONFIG_SHA384
- if (use_sha384) {
+ if (q == SHA384_MAC_LEN) {
if (xxkey_len != SHA384_MAC_LEN) {
wpa_printf(MSG_ERROR,
"FT: Unexpected XXKey length %d (expected %d)",
(int) xxkey_len, SHA384_MAC_LEN);
return -1;
}
- if (sha384_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
- r0_key_data, r0_key_data_len) < 0)
- return -1;
+ res = sha384_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
+ r0_key_data, r0_key_data_len);
}
#endif /* CONFIG_SHA384 */
- if (!use_sha384) {
+ if (q == SHA256_MAC_LEN) {
if (xxkey_len != PMK_LEN) {
wpa_printf(MSG_ERROR,
"FT: Unexpected XXKey length %d (expected %d)",
(int) xxkey_len, PMK_LEN);
return -1;
}
- if (sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
- r0_key_data, r0_key_data_len) < 0)
- return -1;
+ res = sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
+ r0_key_data, r0_key_data_len);
}
+ if (res < 0)
+ return res;
os_memcpy(pmk_r0, r0_key_data, q);
wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, q);
wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0Name-Salt", &r0_key_data[q], 16);
@@ -2065,12 +2143,23 @@
addr[1] = &r0_key_data[q];
len[1] = 16;
+ res = -1;
+#ifdef CONFIG_SHA512
+ if (q == SHA512_MAC_LEN)
+ res = sha512_vector(2, addr, len, hash);
+#endif /* CONFIG_SHA512 */
#ifdef CONFIG_SHA384
- if (use_sha384 && sha384_vector(2, addr, len, hash) < 0)
- return -1;
+ if (q == SHA384_MAC_LEN)
+ res = sha384_vector(2, addr, len, hash);
#endif /* CONFIG_SHA384 */
- if (!use_sha384 && sha256_vector(2, addr, len, hash) < 0)
- return -1;
+ if (q == SHA256_MAC_LEN)
+ res = sha256_vector(2, addr, len, hash);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Failed to derive PMKR0Name (PMK-R0 len %zu)",
+ q);
+ return res;
+ }
os_memcpy(pmk_r0_name, hash, WPA_PMK_NAME_LEN);
wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN);
forced_memzero(r0_key_data, sizeof(r0_key_data));
@@ -2084,11 +2173,14 @@
* IEEE Std 802.11r-2008 - 8.5.1.5.4
*/
int wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
- const u8 *s1kh_id, u8 *pmk_r1_name, int use_sha384)
+ const u8 *s1kh_id, u8 *pmk_r1_name,
+ size_t pmk_r1_len)
{
- u8 hash[48];
+ u8 hash[64];
const u8 *addr[4];
size_t len[4];
+ int res;
+ const char *title;
/*
* PMKR1Name = Truncate-128(Hash("FT-R1N" || PMKR0Name ||
@@ -2103,14 +2195,31 @@
addr[3] = s1kh_id;
len[3] = ETH_ALEN;
+ res = -1;
+#ifdef CONFIG_SHA512
+ if (pmk_r1_len == SHA512_MAC_LEN) {
+ title = "FT: PMKR1Name (using SHA512)";
+ res = sha512_vector(4, addr, len, hash);
+ }
+#endif /* CONFIG_SHA512 */
#ifdef CONFIG_SHA384
- if (use_sha384 && sha384_vector(4, addr, len, hash) < 0)
- return -1;
+ if (pmk_r1_len == SHA384_MAC_LEN) {
+ title = "FT: PMKR1Name (using SHA384)";
+ res = sha384_vector(4, addr, len, hash);
+ }
#endif /* CONFIG_SHA384 */
- if (!use_sha384 && sha256_vector(4, addr, len, hash) < 0)
- return -1;
+ if (pmk_r1_len == SHA256_MAC_LEN) {
+ title = "FT: PMKR1Name (using SHA256)";
+ res = sha256_vector(4, addr, len, hash);
+ }
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Failed to derive PMKR1Name (PMK-R1 len %zu)",
+ pmk_r1_len);
+ return res;
+ }
os_memcpy(pmk_r1_name, hash, WPA_PMK_NAME_LEN);
- wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
+ wpa_hexdump(MSG_DEBUG, title, pmk_r1_name, WPA_PMK_NAME_LEN);
return 0;
}
@@ -2127,10 +2236,11 @@
{
u8 buf[FT_R1KH_ID_LEN + ETH_ALEN];
u8 *pos;
+ int res;
- /* PMK-R1 = KDF-256(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID) */
- wpa_printf(MSG_DEBUG, "FT: Derive PMK-R1 using KDF-%s",
- pmk_r0_len == SHA384_MAC_LEN ? "SHA384" : "SHA256");
+ /* PMK-R1 = KDF-Hash(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID) */
+ wpa_printf(MSG_DEBUG, "FT: Derive PMK-R1 using KDF-SHA%zu",
+ pmk_r0_len * 8);
wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, pmk_r0_len);
wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", r1kh_id, FT_R1KH_ID_LEN);
wpa_printf(MSG_DEBUG, "FT: S1KH-ID: " MACSTR, MAC2STR(s1kh_id));
@@ -2140,26 +2250,28 @@
os_memcpy(pos, s1kh_id, ETH_ALEN);
pos += ETH_ALEN;
+ res = -1;
+#ifdef CONFIG_SHA512
+ if (pmk_r0_len == SHA512_MAC_LEN)
+ res = sha512_prf(pmk_r0, pmk_r0_len, "FT-R1",
+ buf, pos - buf, pmk_r1, pmk_r0_len);
+#endif /* CONFIG_SHA512 */
#ifdef CONFIG_SHA384
- if (pmk_r0_len == SHA384_MAC_LEN &&
- sha384_prf(pmk_r0, pmk_r0_len, "FT-R1",
- buf, pos - buf, pmk_r1, pmk_r0_len) < 0)
- return -1;
+ if (pmk_r0_len == SHA384_MAC_LEN)
+ res = sha384_prf(pmk_r0, pmk_r0_len, "FT-R1",
+ buf, pos - buf, pmk_r1, pmk_r0_len);
#endif /* CONFIG_SHA384 */
- if (pmk_r0_len == PMK_LEN &&
- sha256_prf(pmk_r0, pmk_r0_len, "FT-R1",
- buf, pos - buf, pmk_r1, pmk_r0_len) < 0)
- return -1;
- if (pmk_r0_len != SHA384_MAC_LEN && pmk_r0_len != PMK_LEN) {
- wpa_printf(MSG_ERROR, "FT: Unexpected PMK-R0 length %d",
- (int) pmk_r0_len);
- return -1;
+ if (pmk_r0_len == SHA256_MAC_LEN)
+ res = sha256_prf(pmk_r0, pmk_r0_len, "FT-R1",
+ buf, pos - buf, pmk_r1, pmk_r0_len);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "FT: Failed to derive PMK-R1");
+ return res;
}
wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r0_len);
return wpa_derive_pmk_r1_name(pmk_r0_name, r1kh_id, s1kh_id,
- pmk_r1_name,
- pmk_r0_len == SHA384_MAC_LEN);
+ pmk_r1_name, pmk_r0_len);
}
@@ -2182,7 +2294,8 @@
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);
+ size_t key_len;
+ int res;
if (kdk_len > WPA_KDK_MAX_LEN) {
wpa_printf(MSG_ERROR,
@@ -2191,12 +2304,20 @@
return -1;
}
+ if (akmp == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ (pmk_r1_len == SHA256_MAC_LEN || pmk_r1_len == SHA384_MAC_LEN ||
+ pmk_r1_len == SHA512_MAC_LEN))
+ key_len = pmk_r1_len;
+ else if (wpa_key_mgmt_sha384(akmp))
+ key_len = SHA384_MAC_LEN;
+ else
+ key_len = SHA256_MAC_LEN;
+
/*
* PTK = KDF-PTKLen(PMK-R1, "FT-PTK", SNonce || ANonce ||
* BSSID || STA-ADDR)
*/
- wpa_printf(MSG_DEBUG, "FT: Derive PTK using KDF-%s",
- use_sha384 ? "SHA384" : "SHA256");
+ wpa_printf(MSG_DEBUG, "FT: Derive PTK using KDF-SHA%zu", key_len * 8);
wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r1_len);
wpa_hexdump(MSG_DEBUG, "FT: SNonce", snonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: ANonce", anonce, WPA_NONCE_LEN);
@@ -2212,39 +2333,52 @@
os_memcpy(pos, sta_addr, ETH_ALEN);
pos += ETH_ALEN;
- ptk->kck_len = wpa_kck_len(akmp, PMK_LEN);
+ ptk->kck_len = wpa_kck_len(akmp, key_len);
ptk->kck2_len = wpa_kck2_len(akmp);
- ptk->kek_len = wpa_kek_len(akmp, PMK_LEN);
+ ptk->kek_len = wpa_kek_len(akmp, key_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->kdk_len;
+ res = -1;
+#ifdef CONFIG_SHA512
+ if (key_len == SHA512_MAC_LEN) {
+ if (pmk_r1_len != SHA512_MAC_LEN) {
+ wpa_printf(MSG_ERROR,
+ "FT: Unexpected PMK-R1 length %d (expected %d)",
+ (int) pmk_r1_len, SHA512_MAC_LEN);
+ return -1;
+ }
+ res = sha512_prf(pmk_r1, pmk_r1_len, "FT-PTK",
+ buf, pos - buf, tmp, ptk_len);
+ }
+#endif /* CONFIG_SHA512 */
#ifdef CONFIG_SHA384
- if (use_sha384) {
+ if (key_len == SHA384_MAC_LEN) {
if (pmk_r1_len != SHA384_MAC_LEN) {
wpa_printf(MSG_ERROR,
"FT: Unexpected PMK-R1 length %d (expected %d)",
(int) pmk_r1_len, SHA384_MAC_LEN);
return -1;
}
- if (sha384_prf(pmk_r1, pmk_r1_len, "FT-PTK",
- buf, pos - buf, tmp, ptk_len) < 0)
- return -1;
+ res = sha384_prf(pmk_r1, pmk_r1_len, "FT-PTK",
+ buf, pos - buf, tmp, ptk_len);
}
#endif /* CONFIG_SHA384 */
- if (!use_sha384) {
+ if (key_len == SHA256_MAC_LEN) {
if (pmk_r1_len != PMK_LEN) {
wpa_printf(MSG_ERROR,
"FT: Unexpected PMK-R1 length %d (expected %d)",
(int) pmk_r1_len, PMK_LEN);
return -1;
}
- if (sha256_prf(pmk_r1, pmk_r1_len, "FT-PTK",
- buf, pos - buf, tmp, ptk_len) < 0)
- return -1;
+ res = sha256_prf(pmk_r1, pmk_r1_len, "FT-PTK",
+ buf, pos - buf, tmp, ptk_len);
}
+ if (res < 0)
+ return -1;
wpa_hexdump_key(MSG_DEBUG, "FT: PTK", tmp, ptk_len);
/*
@@ -2313,7 +2447,7 @@
* @akmp: Negotiated key management protocol
*
* IEEE Std 802.11-2016 - 12.7.1.3 Pairwise key hierarchy
- * AKM: 00-0F-AC:5, 00-0F-AC:6, 00-0F-AC:14, 00-0F-AC:16
+ * AKM: 00-0F-AC:3, 00-0F-AC:5, 00-0F-AC:6, 00-0F-AC:14, 00-0F-AC:16
* PMKID = Truncate-128(HMAC-SHA-256(PMK, "PMK Name" || AA || SPA))
* AKM: 00-0F-AC:11
* See rsn_pmkid_suite_b()
@@ -2736,9 +2870,9 @@
return 16;
case WPA_CIPHER_TKIP:
return 32;
+ default:
+ return 0;
}
-
- return 0;
}
@@ -2751,9 +2885,9 @@
case WPA_CIPHER_GCMP:
case WPA_CIPHER_TKIP:
return 6;
+ default:
+ return 0;
}
-
- return 0;
}
@@ -2778,8 +2912,9 @@
return WPA_ALG_BIP_GMAC_256;
case WPA_CIPHER_BIP_CMAC_256:
return WPA_ALG_BIP_CMAC_256;
+ default:
+ return WPA_ALG_NONE;
}
- return WPA_ALG_NONE;
}
@@ -3951,4 +4086,27 @@
wpabuf_put_u8(buf, capab);
}
+
+/*
+ * wpa_pasn_add_extra_ies - Add protocol specific IEs in Authentication
+ * frame for PASN.
+ *
+ * @buf: Buffer in which the elements will be added
+ * @extra_ies: Protocol specific elements to add
+ * @len: Length of the elements
+ * Returns: 0 on success, -1 on failure
+ */
+
+int wpa_pasn_add_extra_ies(struct wpabuf *buf, const u8 *extra_ies, size_t len)
+{
+ if (!len || !extra_ies || !buf)
+ return 0;
+
+ if (wpabuf_tailroom(buf) < sizeof(len))
+ return -1;
+
+ wpabuf_put_data(buf, extra_ies, len);
+ return 0;
+}
+
#endif /* CONFIG_PASN */
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 71b423c..05b1a8a 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -186,6 +186,18 @@
#define FT_R1KH_ID_LEN 6
#define WPA_PMK_NAME_LEN 16
+/* FTE - MIC Control - RSNXE Used */
+#define FTE_MIC_CTRL_RSNXE_USED BIT(0)
+#define FTE_MIC_CTRL_MIC_LEN_MASK (BIT(1) | BIT(2) | BIT(3))
+#define FTE_MIC_CTRL_MIC_LEN_SHIFT 1
+
+/* FTE - MIC Length subfield values */
+enum ft_mic_len_subfield {
+ FTE_MIC_LEN_16 = 0,
+ FTE_MIC_LEN_24 = 1,
+ FTE_MIC_LEN_32 = 2,
+};
+
/* IEEE 802.11, 8.5.2 EAPOL-Key frames */
#define WPA_KEY_INFO_TYPE_MASK ((u16) (BIT(0) | BIT(1) | BIT(2)))
@@ -401,6 +413,14 @@
/* followed by optional parameters */
} STRUCT_PACKED;
+struct rsn_ftie_sha512 {
+ u8 mic_control[2];
+ u8 mic[32];
+ u8 anonce[WPA_NONCE_LEN];
+ u8 snonce[WPA_NONCE_LEN];
+ /* followed by optional parameters */
+} STRUCT_PACKED;
+
#define FTIE_SUBELEM_R1KH_ID 1
#define FTIE_SUBELEM_GTK 2
#define FTIE_SUBELEM_R0KH_ID 3
@@ -455,7 +475,7 @@
size_t *key_auth_len);
#ifdef CONFIG_IEEE80211R
-int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
+int wpa_ft_mic(int key_mgmt, const u8 *kck, size_t kck_len, const u8 *sta_addr,
const u8 *ap_addr, u8 transaction_seqnum,
const u8 *mdie, size_t mdie_len,
const u8 *ftie, size_t ftie_len,
@@ -467,9 +487,10 @@
const u8 *ssid, size_t ssid_len,
const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name,
- int use_sha384);
+ int key_mgmt);
int wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
- const u8 *s1kh_id, u8 *pmk_r1_name, int use_sha384);
+ const u8 *s1kh_id, u8 *pmk_r1_name,
+ size_t pmk_r1_len);
int wpa_derive_pmk_r1(const u8 *pmk_r0, size_t pmk_r0_len,
const u8 *pmk_r0_name,
const u8 *r1kh_id, const u8 *s1kh_id,
@@ -544,6 +565,10 @@
size_t r0kh_id_len;
const u8 *fte_anonce;
const u8 *fte_snonce;
+ bool fte_rsnxe_used;
+ unsigned int fte_elem_count;
+ const u8 *fte_mic;
+ size_t fte_mic_len;
const u8 *rsn;
size_t rsn_len;
u16 rsn_capab;
@@ -599,7 +624,7 @@
#define WPA_PASN_PUBKEY_UNCOMPRESSED 0x04
int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse,
- int use_sha384);
+ int key_mgmt);
struct wpa_eapol_ie_parse {
const u8 *wpa_ie;
@@ -697,6 +722,7 @@
int wpa_use_aes_key_wrap(int akmp);
int fils_domain_name_hash(const char *domain, u8 *hash);
+bool pasn_use_sha384(int akmp, int cipher);
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,
@@ -735,5 +761,6 @@
struct wpa_pasn_params_data *pasn_params);
void wpa_pasn_add_rsnxe(struct wpabuf *buf, u16 capab);
+int wpa_pasn_add_extra_ies(struct wpabuf *buf, const u8 *extra_ies, size_t len);
#endif /* WPA_COMMON_H */
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index ba54da5..4ab2a1b 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -466,6 +466,8 @@
#define WPA_BSS_MASK_UPDATE_IDX BIT(22)
#define WPA_BSS_MASK_BEACON_IE BIT(23)
#define WPA_BSS_MASK_FILS_INDICATION BIT(24)
+#define WPA_BSS_MASK_RNR BIT(25)
+#define WPA_BSS_MASK_ML BIT(26)
/* VENDOR_ELEM_* frame id values */
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 91df607..ff0869c 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -1068,7 +1068,8 @@
/**
* crypto_ec_key_get_subject_public_key - Get SubjectPublicKeyInfo ASN.1 for an EC key
* @key: EC key from crypto_ec_key_parse/set_pub/priv() or crypto_ec_key_gen()
- * Returns: Buffer with DER encoding of ASN.1 SubjectPublicKeyInfo or %NULL on failure
+ * Returns: Buffer with DER encoding of ASN.1 SubjectPublicKeyInfo using
+ * compressed point format, or %NULL on failure
*/
struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key);
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index f058e06..22f6ab4 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -182,7 +182,6 @@
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
-static OSSL_PROVIDER *openssl_default_provider = NULL;
static OSSL_PROVIDER *openssl_legacy_provider = NULL;
#endif /* OpenSSL version >= 3.0 */
@@ -192,9 +191,7 @@
if (openssl_legacy_provider)
return;
- openssl_legacy_provider = OSSL_PROVIDER_load(NULL, "legacy");
- if (openssl_legacy_provider && !openssl_default_provider)
- openssl_default_provider = OSSL_PROVIDER_load(NULL, "default");
+ openssl_legacy_provider = OSSL_PROVIDER_try_load(NULL, "legacy", 1);
#endif /* OpenSSL version >= 3.0 */
}
@@ -206,10 +203,6 @@
OSSL_PROVIDER_unload(openssl_legacy_provider);
openssl_legacy_provider = NULL;
}
- if (openssl_default_provider) {
- OSSL_PROVIDER_unload(openssl_default_provider);
- openssl_default_provider = NULL;
- }
#endif /* OpenSSL version >= 3.0 */
}
@@ -320,12 +313,12 @@
#ifndef CONFIG_FIPS
+
int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
{
openssl_load_legacy_provider();
return openssl_digest_vector(EVP_md4(), num_elem, addr, len, mac);
}
-#endif /* CONFIG_FIPS */
int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
@@ -404,11 +397,11 @@
#endif /* CONFIG_NO_RC4 */
-#ifndef CONFIG_FIPS
int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
{
return openssl_digest_vector(EVP_md5(), num_elem, addr, len, mac);
}
+
#endif /* CONFIG_FIPS */
@@ -454,9 +447,9 @@
return EVP_aes_192_ecb();
case 32:
return EVP_aes_256_ecb();
+ default:
+ return NULL;
}
-
- return NULL;
}
@@ -2156,9 +2149,7 @@
int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
struct crypto_bignum *r)
{
- /* Note: BN_rshift() does not modify the first argument even though it
- * has not been marked const. */
- return BN_rshift((BIGNUM *) a, (BIGNUM *) r, n) == 1 ? 0 : -1;
+ return BN_rshift((BIGNUM *) r, (const BIGNUM *) a, n) == 1 ? 0 : -1;
}
@@ -2458,14 +2449,16 @@
EC_POINT_get_affine_coordinates(e->group, (EC_POINT *) point,
x_bn, y_bn, e->bnctx)) {
if (x) {
- crypto_bignum_to_bin((struct crypto_bignum *) x_bn,
- x, len, len);
+ ret = crypto_bignum_to_bin(
+ (struct crypto_bignum *) x_bn, x, len, len);
}
- if (y) {
- crypto_bignum_to_bin((struct crypto_bignum *) y_bn,
- y, len, len);
+ if (ret >= 0 && y) {
+ ret = crypto_bignum_to_bin(
+ (struct crypto_bignum *) y_bn, y, len, len);
}
- ret = 0;
+
+ if (ret > 0)
+ ret = 0;
}
BN_clear_free(x_bn);
@@ -4090,10 +4083,12 @@
case NID_brainpoolP512r1:
return 30;
#endif /* NID_brainpoolP512r1 */
+ default:
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: Unsupported curve (nid=%d) in EC key",
+ nid);
+ return -1;
}
- wpa_printf(MSG_ERROR, "OpenSSL: Unsupported curve (nid=%d) in EC key",
- nid);
- return -1;
}
@@ -4854,10 +4849,8 @@
pos += suite_id_len;
os_memcpy(pos, label, label_len);
pos += label_len;
- if (info && info_len) {
+ if (info && info_len)
os_memcpy(pos, info, info_len);
- pos += info_len;
- }
pos = out;
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
@@ -5020,8 +5013,8 @@
EVP_PKEY_CTX *pctx = NULL;
struct crypto_ec_key *sk_e;
int res = -1;
- u8 dhss[HPKE_MAX_SHARED_SECRET_LEN + 16];
- size_t dhss_len;
+ u8 *dhss = NULL;
+ size_t dhss_len = 0;
struct wpabuf *enc_buf = NULL, *pk_rm = NULL;
/* skE, pkE = GenerateKeyPair() */
@@ -5038,10 +5031,13 @@
if (!pctx ||
EVP_PKEY_derive_init(pctx) != 1 ||
EVP_PKEY_derive_set_peer(pctx, (EVP_PKEY *) pk_r) != 1 ||
+ EVP_PKEY_derive(pctx, NULL, &dhss_len) != 1 ||
+ !(dhss = os_malloc(dhss_len)) ||
EVP_PKEY_derive(pctx, dhss, &dhss_len) != 1 ||
dhss_len > HPKE_MAX_SHARED_SECRET_LEN) {
- wpa_printf(MSG_INFO, "OpenSSL: EVP_PKEY_derive failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
+ wpa_printf(MSG_INFO,
+ "OpenSSL: hpke_encap: EVP_PKEY_derive failed (dhss_len=%zu): %s",
+ dhss_len, ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
@@ -5063,7 +5059,7 @@
wpabuf_head(pk_rm),
wpabuf_len(pk_rm), shared_secret);
fail:
- forced_memzero(dhss, sizeof(dhss));
+ bin_clear_free(dhss, dhss_len);
crypto_ec_key_deinit(sk_e);
EVP_PKEY_CTX_free(pctx);
wpabuf_free(enc_buf);
@@ -5184,8 +5180,8 @@
size_t len;
int res = -1;
struct crypto_ec_key *pk_e = NULL;
- u8 dhss[HPKE_MAX_SHARED_SECRET_LEN + 16];
- size_t dhss_len;
+ u8 *dhss = NULL;
+ size_t dhss_len = 0;
/* pkE = DeserializePublicKey(enc) */
if (enc_ct_len < ctx->n_pk)
@@ -5198,15 +5194,17 @@
if (!pk_e)
return -1; /* invalid public key point */
/* dh = DH(skR, pkE) */
- dhss_len = sizeof(dhss);
pctx = EVP_PKEY_CTX_new((EVP_PKEY *) sk_r, NULL);
if (!pctx ||
EVP_PKEY_derive_init(pctx) != 1 ||
EVP_PKEY_derive_set_peer(pctx, (EVP_PKEY *) pk_e) != 1 ||
+ EVP_PKEY_derive(pctx, NULL, &dhss_len) != 1 ||
+ !(dhss = os_malloc(dhss_len)) ||
EVP_PKEY_derive(pctx, dhss, &dhss_len) != 1 ||
dhss_len > HPKE_MAX_SHARED_SECRET_LEN) {
- wpa_printf(MSG_INFO, "OpenSSL: EVP_PKEY_derive failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
+ wpa_printf(MSG_INFO,
+ "OpenSSL: hpke_decap: EVP_PKEY_derive failed (dhss_len=%zu): %s",
+ dhss_len, ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
@@ -5221,7 +5219,7 @@
wpabuf_head(pk_rm),
wpabuf_len(pk_rm), shared_secret);
fail:
- forced_memzero(dhss, sizeof(dhss));
+ bin_clear_free(dhss, dhss_len);
crypto_ec_key_deinit(pk_e);
EVP_PKEY_CTX_free(pctx);
wpabuf_free(pk_rm);
diff --git a/src/crypto/sha256.c b/src/crypto/sha256.c
index 17af964..1ad1068 100644
--- a/src/crypto/sha256.c
+++ b/src/crypto/sha256.c
@@ -30,6 +30,7 @@
unsigned char tk[32];
const u8 *_addr[11];
size_t _len[11], i;
+ int ret;
if (num_elem > 10) {
/*
@@ -70,8 +71,9 @@
_addr[i + 1] = addr[i];
_len[i + 1] = len[i];
}
- if (sha256_vector(1 + num_elem, _addr, _len, mac) < 0)
- return -1;
+ ret = sha256_vector(1 + num_elem, _addr, _len, mac);
+ if (ret < 0)
+ goto fail;
os_memset(k_pad, 0, sizeof(k_pad));
os_memcpy(k_pad, key, key_len);
@@ -84,7 +86,14 @@
_len[0] = 64;
_addr[1] = mac;
_len[1] = SHA256_MAC_LEN;
- return sha256_vector(2, _addr, _len, mac);
+
+ ret = sha256_vector(2, _addr, _len, mac);
+
+fail:
+ forced_memzero(k_pad, sizeof(k_pad));
+ forced_memzero(tk, sizeof(tk));
+
+ return ret;
}
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index e215762..c201dcd 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -353,7 +353,9 @@
* tls_connection_set_verify - Set certificate verification options
* @tls_ctx: TLS context data from tls_init()
* @conn: Connection context data from tls_connection_init()
- * @verify_peer: 1 = verify peer certificate
+ * @verify_peer: 0 = do not verify peer certificate, 1 = verify peer
+ * certificate (require it to be provided), 2 = verify peer certificate if
+ * provided
* @flags: Connection flags (TLS_CONN_*)
* @session_ctx: Session caching context or %NULL to use default
* @session_ctx_len: Length of @session_ctx in bytes.
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index ab82e3d..e0e10fd 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -5416,6 +5416,12 @@
__func__, ERR_error_string(err, NULL));
}
+ if (tls_set_conn_flags(conn, params->flags,
+ params->openssl_ciphers) < 0) {
+ wpa_printf(MSG_ERROR, "TLS: Failed to set connection flags");
+ return -1;
+ }
+
if (engine_id) {
wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine %s",
engine_id);
@@ -5520,12 +5526,6 @@
#endif /* OPENSSL_IS_BORINGSSL */
}
- if (tls_set_conn_flags(conn, params->flags,
- params->openssl_ciphers) < 0) {
- wpa_printf(MSG_ERROR, "TLS: Failed to set connection flags");
- return -1;
- }
-
#ifdef OPENSSL_IS_BORINGSSL
if (params->flags & TLS_CONN_REQUEST_OCSP) {
SSL_enable_ocsp_stapling(conn->ssl);
@@ -5598,8 +5598,9 @@
return "DH";
case EVP_PKEY_EC:
return "EC";
+ default:
+ return "?";
}
- return "?";
}
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 71d799d..4ddfe07 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -212,6 +212,7 @@
#define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
#define HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN BIT(1)
+#define HOSTAPD_MODE_FLAG_HE_INFO_KNOWN BIT(2)
enum ieee80211_op_mode {
@@ -720,6 +721,21 @@
* auth_data_len - Length of auth_data buffer in octets
*/
size_t auth_data_len;
+
+ /**
+ * mld - Establish an MLD connection
+ */
+ bool mld;
+
+ /**
+ * mld_link_id - The link ID of the MLD AP to which we are associating
+ */
+ u8 mld_link_id;
+
+ /**
+ * The MLD AP address
+ */
+ const u8 *ap_mld_addr;
};
/**
@@ -883,6 +899,38 @@
size_t fils_kek_len;
};
+struct wpa_driver_mld_params {
+ /**
+ * mld_addr - AP's MLD address
+ */
+ const u8 *mld_addr;
+
+ /**
+ * valid_links - The valid links including the association link
+ */
+ u16 valid_links;
+
+ /**
+ * assoc_link_id - The link on which the association is performed
+ */
+ u8 assoc_link_id;
+
+ /**
+ * mld_links - Link information
+ *
+ * Should include information on all the requested links for association
+ * including the link on which the association should take place.
+ * For the association link, the ies and ies_len should be NULL and
+ * 0 respectively.
+ */
+ struct {
+ int freq;
+ const u8 *bssid;
+ const u8 *ies;
+ size_t ies_len;
+ } mld_links[MAX_NUM_MLD_LINKS];
+};
+
/**
* struct wpa_driver_associate_params - Association parameters
* Data for struct wpa_driver_ops::associate().
@@ -1250,13 +1298,24 @@
* 1 = hash-to-element only
* 2 = both hunting-and-pecking loop and hash-to-element enabled
*/
- int sae_pwe;
+ enum sae_pwe sae_pwe;
+
#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
/**
* td_policy - Transition Disable Policy
*/
u32 td_policy;
#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
+
+ /**
+ * disable_eht - Disable EHT for this connection
+ */
+ int disable_eht;
+
+ /*
+ * mld_params - MLD association parameters
+ */
+ struct wpa_driver_mld_params mld_params;
};
enum hide_ssid {
@@ -1600,7 +1659,7 @@
* 1 = hash-to-element only
* 2 = both hunting-and-pecking loop and hash-to-element enabled
*/
- int sae_pwe;
+ enum sae_pwe sae_pwe;
/**
* FILS Discovery frame minimum interval in TUs
@@ -1636,6 +1695,42 @@
* Unsolicited broadcast Probe Response template length
*/
size_t unsol_bcast_probe_resp_tmpl_len;
+
+ /**
+ * mbssid_tx_iface - Transmitting interface of the MBSSID set
+ */
+ const char *mbssid_tx_iface;
+
+ /**
+ * mbssid_index - The index of this BSS in the MBSSID set
+ */
+ unsigned int mbssid_index;
+
+ /**
+ * mbssid_elem - Buffer containing all MBSSID elements
+ */
+ u8 *mbssid_elem;
+
+ /**
+ * mbssid_elem_len - Total length of all MBSSID elements
+ */
+ size_t mbssid_elem_len;
+
+ /**
+ * mbssid_elem_count - The number of MBSSID elements
+ */
+ u8 mbssid_elem_count;
+
+ /**
+ * mbssid_elem_offset - Offsets to elements in mbssid_elem.
+ * Kernel will use these offsets to generate multiple BSSID beacons.
+ */
+ u8 **mbssid_elem_offset;
+
+ /**
+ * ema - Enhanced MBSSID advertisements support.
+ */
+ bool ema;
};
struct wpa_driver_mesh_bss_params {
@@ -1789,6 +1884,12 @@
* %KEY_FLAG_RX_TX
* RX/TX key. */
enum key_flag key_flag;
+
+ /**
+ * link_id - MLO Link ID
+ *
+ * Set to a valid Link ID (0-14) when applicable, otherwise -1. */
+ int link_id;
};
enum wpa_driver_if_type {
@@ -2096,6 +2197,8 @@
* frames in STA mode
*/
#define WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA 0x0000000000002000ULL
+/** Driver supports MLO in station/AP mode */
+#define WPA_DRIVER_FLAGS2_MLO 0x0000000000004000ULL
u64 flags2;
#define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@@ -2211,6 +2314,11 @@
/* Maximum number of supported AKM suites in commands */
unsigned int max_num_akms;
+
+ /* Maximum number of interfaces supported for MBSSID advertisement */
+ unsigned int mbssid_max_interfaces;
+ /* Maximum profile periodicity for enhanced MBSSID advertisement */
+ unsigned int ema_max_periodicity;
};
@@ -2226,14 +2334,19 @@
#define STA_DRV_DATA_RX_SHORT_GI BIT(7)
#define STA_DRV_DATA_LAST_ACK_RSSI BIT(8)
#define STA_DRV_DATA_CONN_TIME BIT(9)
+#define STA_DRV_DATA_TX_HE_MCS BIT(10)
+#define STA_DRV_DATA_RX_HE_MCS BIT(11)
+#define STA_DRV_DATA_TX_HE_NSS BIT(12)
+#define STA_DRV_DATA_RX_HE_NSS BIT(13)
struct hostap_sta_driver_data {
unsigned long rx_packets, tx_packets;
unsigned long long rx_bytes, tx_bytes;
unsigned long long rx_airtime, tx_airtime;
+ unsigned long long beacons_count;
int bytes_64bit; /* whether 64-bit byte counters are supported */
- unsigned long current_tx_rate;
- unsigned long current_rx_rate;
+ unsigned long current_tx_rate; /* in kbps */
+ unsigned long current_rx_rate; /* in kbps */
unsigned long inactive_msec;
unsigned long connected_sec;
unsigned long flags; /* bitfield of STA_DRV_DATA_* */
@@ -2243,13 +2356,25 @@
s8 last_ack_rssi;
unsigned long backlog_packets;
unsigned long backlog_bytes;
- s8 signal;
+ unsigned long fcs_error_count;
+ unsigned long beacon_loss_count;
+ unsigned long expected_throughput;
+ unsigned long rx_drop_misc;
+ unsigned long rx_mpdus;
+ int signal; /* dBm; or -WPA_INVALID_NOISE */
+ u8 rx_hemcs;
+ u8 tx_hemcs;
u8 rx_vhtmcs;
u8 tx_vhtmcs;
u8 rx_mcs;
u8 tx_mcs;
+ u8 rx_he_nss;
+ u8 tx_he_nss;
u8 rx_vht_nss;
u8 tx_vht_nss;
+ s8 avg_signal; /* dBm */
+ s8 avg_beacon_signal; /* dBm */
+ s8 avg_ack_signal; /* dBm */
};
struct hostapd_sta_add_params {
@@ -2376,11 +2501,8 @@
* @frequency: control frequency
* @above_threshold: true if the above threshold was crossed
* (relevant for a CQM event)
- * @current_signal: in dBm
- * @avg_signal: in dBm
- * @avg_beacon_signal: in dBm
+ * @data: STA information
* @current_noise: %WPA_INVALID_NOISE if not supported
- * @current_txrate: current TX rate
* @chanwidth: channel width
* @center_frq1: center frequency for the first segment
* @center_frq2: center frequency for the second segment (if relevant)
@@ -2388,12 +2510,8 @@
struct wpa_signal_info {
u32 frequency;
int above_threshold;
- int current_signal;
- int avg_signal;
- int avg_beacon_signal;
+ struct hostap_sta_driver_data data;
int current_noise;
- int current_txrate;
- int current_rxrate;
enum chan_width chanwidth;
int center_frq1;
int center_frq2;
@@ -2746,7 +2864,9 @@
};
struct driver_sta_mlo_info {
- u16 valid_links; /* bitmap of valid link IDs */
+ u16 req_links; /* bitmap of requested link IDs */
+ u16 valid_links; /* bitmap of accepted link IDs */
+ u8 assoc_link_id;
u8 ap_mld_addr[ETH_ALEN];
struct {
u8 addr[ETH_ALEN];
@@ -2975,9 +3095,9 @@
* poll - Poll driver for association information
* @priv: private driver interface data
*
- * This is an option callback that can be used when the driver does not
- * provide event mechanism for association events. This is called when
- * receiving WPA EAPOL-Key messages that require association
+ * This is an optional callback that can be used when the driver does
+ * not provide event mechanism for association events. This is called
+ * when receiving WPA/RSN EAPOL-Key messages that require association
* information. The driver interface is supposed to generate associnfo
* event before returning from this callback function. In addition, the
* driver interface should generate an association event after having
@@ -6362,6 +6482,13 @@
*/
struct pasn_auth pasn_auth;
+ /**
+ * struct port_authorized - Data for EVENT_PORT_AUTHORIZED
+ */
+ struct port_authorized {
+ const u8 *td_bitmap;
+ size_t td_bitmap_len;
+ } port_authorized;
};
/**
@@ -6453,6 +6580,8 @@
int ht_supported(const struct hostapd_hw_modes *mode);
int vht_supported(const struct hostapd_hw_modes *mode);
+bool he_supported(const struct hostapd_hw_modes *hw_mode,
+ enum ieee80211_op_mode op_mode);
struct wowlan_triggers *
wpa_get_wowlan_triggers(const char *wowlan_triggers,
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index 0ac0aa6..bbd1a7c 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -185,6 +185,21 @@
}
+bool he_supported(const struct hostapd_hw_modes *hw_mode,
+ enum ieee80211_op_mode op_mode)
+{
+ if (!(hw_mode->flags & HOSTAPD_MODE_FLAG_HE_INFO_KNOWN)) {
+ /*
+ * The driver did not indicate whether it supports HE. Assume
+ * it does to avoid connection issues.
+ */
+ return true;
+ }
+
+ return hw_mode->he_capab[op_mode].he_supported;
+}
+
+
static int wpa_check_wowlan_trigger(const char *start, const char *trigger,
int capa_trigger, u8 *param_trigger)
{
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 0568894..c7e9bfe 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -223,8 +223,9 @@
return CHAN_WIDTH_160;
case NL80211_CHAN_WIDTH_320:
return CHAN_WIDTH_320;
+ default:
+ return CHAN_WIDTH_UNKNOWN;
}
- return CHAN_WIDTH_UNKNOWN;
}
@@ -274,13 +275,17 @@
if (drv->associated)
os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
drv->associated = 0;
- drv->sta_mlo_info.valid_links = 0;
+ os_memset(&drv->sta_mlo_info, 0, sizeof(drv->sta_mlo_info));
os_memset(drv->bssid, 0, ETH_ALEN);
drv->first_bss->freq = 0;
#ifdef CONFIG_DRIVER_NL80211_QCA
os_free(drv->pending_roam_data);
drv->pending_roam_data = NULL;
#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+ drv->auth_mld = false;
+ drv->auth_mld_link_id = -1;
+ os_memset(drv->auth_ap_mld_addr, 0, ETH_ALEN);
}
@@ -1024,6 +1029,51 @@
}
+static int get_mlo_info(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *link_attr, *link_data[NL80211_ATTR_MAX + 1];
+ static struct nla_policy link_policy[NL80211_ATTR_MAX + 1] = {
+ [NL80211_ATTR_MLO_LINK_ID] = { .type = NLA_U8 },
+ [NL80211_ATTR_MAC] = { .minlen = ETH_ALEN, .maxlen = ETH_ALEN },
+ };
+ struct driver_sta_mlo_info *info = arg;
+ int rem;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (!tb[NL80211_ATTR_MLO_LINKS])
+ return NL_SKIP;
+
+ info->valid_links = 0;
+ nla_for_each_nested(link_attr, tb[NL80211_ATTR_MLO_LINKS], rem) {
+ u8 link_id;
+
+ if (nla_parse_nested(link_data, NL80211_ATTR_MAX,
+ link_attr, link_policy) != 0)
+ continue;
+
+ if (!link_data[NL80211_ATTR_MLO_LINK_ID] ||
+ !link_data[NL80211_ATTR_MAC])
+ continue;
+
+ link_id = nla_get_u8(link_data[NL80211_ATTR_MLO_LINK_ID]);
+ if (link_id >= MAX_NUM_MLD_LINKS)
+ continue;
+ info->valid_links |= BIT(link_id);
+ os_memcpy(info->links[link_id].addr,
+ nla_data(link_data[NL80211_ATTR_MAC]), ETH_ALEN);
+ if (link_data[NL80211_ATTR_WIPHY_FREQ])
+ info->links[link_id].freq =
+ nla_get_u32(link_data[NL80211_ATTR_WIPHY_FREQ]);
+ }
+
+ return NL_SKIP;
+}
+
+
static int nl80211_get_sta_mlo_info(void *priv,
struct driver_sta_mlo_info *mlo_info)
{
@@ -1033,6 +1083,15 @@
if (!drv->associated)
return -1;
+ if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+ struct nl_msg *msg;
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
+ if (send_and_recv_msgs(drv, msg, get_mlo_info,
+ &drv->sta_mlo_info, NULL, NULL))
+ return -1;
+ }
+
os_memcpy(mlo_info, &drv->sta_mlo_info, sizeof(*mlo_info));
return 0;
}
@@ -1192,6 +1251,7 @@
MACSTR " to " MACSTR,
ifindex, bss->ifname,
MAC2STR(bss->addr), MAC2STR(addr));
+ os_memcpy(bss->prev_addr, bss->addr, ETH_ALEN);
os_memcpy(bss->addr, addr, ETH_ALEN);
if (notify)
wpa_supplicant_event(drv->ctx,
@@ -1508,7 +1568,7 @@
}
if (!drv->sta_mlo_info.valid_links ||
- drv->mlo_assoc_link_id == link_id) {
+ drv->sta_mlo_info.assoc_link_id == link_id) {
ctx->assoc_freq = freq;
wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
ctx->assoc_freq);
@@ -1536,7 +1596,7 @@
}
if (!drv->sta_mlo_info.valid_links ||
- drv->mlo_assoc_link_id == link_id) {
+ drv->sta_mlo_info.assoc_link_id == link_id) {
os_memcpy(ctx->assoc_bssid, bssid, ETH_ALEN);
wpa_printf(MSG_DEBUG, "nl80211: Associated with "
MACSTR, MAC2STR(bssid));
@@ -1642,102 +1702,6 @@
return drv->assoc_freq;
}
-
-static int get_link_signal(struct nl_msg *msg, void *arg)
-{
- struct nlattr *tb[NL80211_ATTR_MAX + 1];
- struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
- struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
- static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = {
- [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
- [NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 },
- [NL80211_STA_INFO_BEACON_SIGNAL_AVG] = { .type = NLA_U8 },
- };
- struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
- static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
- [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
- [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
- [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
- [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
- };
- struct wpa_signal_info *sig_change = arg;
-
- nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
- genlmsg_attrlen(gnlh, 0), NULL);
- if (!tb[NL80211_ATTR_STA_INFO] ||
- nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
- tb[NL80211_ATTR_STA_INFO], policy))
- return NL_SKIP;
- if (!sinfo[NL80211_STA_INFO_SIGNAL])
- return NL_SKIP;
-
- sig_change->current_signal =
- (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
-
- if (sinfo[NL80211_STA_INFO_SIGNAL_AVG])
- sig_change->avg_signal =
- (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL_AVG]);
- else
- sig_change->avg_signal = 0;
-
- if (sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG])
- sig_change->avg_beacon_signal =
- (s8)
- nla_get_u8(sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG]);
- else
- sig_change->avg_beacon_signal = 0;
-
- if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
- if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
- sinfo[NL80211_STA_INFO_TX_BITRATE],
- rate_policy)) {
- sig_change->current_txrate = 0;
- } else {
- if (rinfo[NL80211_RATE_INFO_BITRATE]) {
- sig_change->current_txrate =
- nla_get_u16(rinfo[
- NL80211_RATE_INFO_BITRATE]) * 100;
- }
- }
- }
-
- if (sinfo[NL80211_STA_INFO_RX_BITRATE]) {
- if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
- sinfo[NL80211_STA_INFO_RX_BITRATE],
- rate_policy)) {
- sig_change->current_rxrate = 0;
- } else {
- if (rinfo[NL80211_RATE_INFO_BITRATE]) {
- sig_change->current_rxrate =
- nla_get_u16(rinfo[
- NL80211_RATE_INFO_BITRATE]) * 100;
- }
- }
- }
-
- return NL_SKIP;
-}
-
-
-int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
- const u8 *bssid, struct wpa_signal_info *sig)
-{
- struct nl_msg *msg;
-
- sig->current_signal = -WPA_INVALID_NOISE;
- sig->current_txrate = 0;
- sig->current_rxrate = 0;
-
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_STATION)) ||
- nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) {
- nlmsg_free(msg);
- return -ENOBUFS;
- }
-
- return send_and_recv_msgs(drv, msg, get_link_signal, sig, NULL, NULL);
-}
-
-
static int get_link_noise(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -3211,9 +3175,9 @@
return RSN_CIPHER_SUITE_WEP40;
case WPA_CIPHER_GTK_NOT_USED:
return RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED;
+ default:
+ return 0;
}
-
- return 0;
}
@@ -3466,6 +3430,7 @@
size_t key_len = params->key_len;
int vlan_id = params->vlan_id;
enum key_flag key_flag = params->key_flag;
+ int link_id = params->link_id;
/* Ignore for P2P Device */
if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE)
@@ -3473,9 +3438,10 @@
ifindex = if_nametoindex(ifname);
wpa_printf(MSG_DEBUG, "%s: ifindex=%d (%s) alg=%d addr=%p key_idx=%d "
- "set_tx=%d seq_len=%lu key_len=%lu key_flag=0x%x",
+ "set_tx=%d seq_len=%lu key_len=%lu key_flag=0x%x link_id=%d",
__func__, ifindex, ifname, alg, addr, key_idx, set_tx,
- (unsigned long) seq_len, (unsigned long) key_len, key_flag);
+ (unsigned long) seq_len, (unsigned long) key_len, key_flag,
+ link_id);
if (check_key_flag(key_flag)) {
wpa_printf(MSG_DEBUG, "%s: invalid key_flag", __func__);
@@ -3609,6 +3575,12 @@
goto fail;
}
+ if (link_id != -1) {
+ wpa_printf(MSG_DEBUG, "nl80211: Link ID %d", link_id);
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
+ goto fail;
+ }
+
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE)
ret = 0;
@@ -3671,6 +3643,13 @@
goto fail;
}
+ if (link_id != -1) {
+ wpa_printf(MSG_DEBUG, "nl80211: set_key default - Link ID %d",
+ link_id);
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
+ goto fail;
+ }
+
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret)
wpa_printf(MSG_DEBUG,
@@ -3894,6 +3873,15 @@
}
}
+ if (params->mld && params->ap_mld_addr) {
+ drv->auth_mld = params->mld;
+ drv->auth_mld_link_id = params->mld_link_id;
+ os_memcpy(drv->auth_ap_mld_addr, params->ap_mld_addr, ETH_ALEN);
+ } else {
+ drv->auth_mld = false;
+ drv->auth_mld_link_id = -1;
+ }
+
os_free(drv->auth_data);
drv->auth_data = NULL;
drv->auth_data_len = 0;
@@ -3998,6 +3986,7 @@
os_memset(&p, 0, sizeof(p));
p.ifname = bss->ifname;
p.alg = WPA_ALG_WEP;
+ p.link_id = -1;
for (i = 0; i < 4; i++) {
if (!params->wep_key[i])
continue;
@@ -4056,6 +4045,17 @@
goto fail;
}
+ if (params->mld && params->ap_mld_addr) {
+ wpa_printf(MSG_DEBUG, " * MLD: link_id=%u, MLD addr=" MACSTR,
+ params->mld_link_id, MAC2STR(params->ap_mld_addr));
+
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+ params->mld_link_id) ||
+ nla_put(msg, NL80211_ATTR_MLD_ADDR, ETH_ALEN,
+ params->ap_mld_addr))
+ goto fail;
+ }
+
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
msg = NULL;
if (ret) {
@@ -4159,6 +4159,10 @@
params.ie_len = drv->auth_ie_len;
params.auth_data = drv->auth_data;
params.auth_data_len = drv->auth_data_len;
+ params.mld = drv->auth_mld;
+ params.mld_link_id = drv->auth_mld_link_id;
+ if (drv->auth_mld)
+ params.ap_mld_addr = drv->auth_ap_mld_addr;
for (i = 0; i < 4; i++) {
if (drv->auth_wep_key_len[i]) {
@@ -4646,18 +4650,18 @@
#ifdef CONFIG_SAE
-static int nl80211_put_sae_pwe(struct nl_msg *msg, int pwe)
+static int nl80211_put_sae_pwe(struct nl_msg *msg, enum sae_pwe pwe)
{
u8 sae_pwe;
wpa_printf(MSG_DEBUG, "nl802111: sae_pwe=%d", pwe);
- if (pwe == 0)
+ if (pwe == SAE_PWE_HUNT_AND_PECK)
sae_pwe = NL80211_SAE_PWE_HUNT_AND_PECK;
- else if (pwe == 1)
+ else if (pwe == SAE_PWE_HASH_TO_ELEMENT)
sae_pwe = NL80211_SAE_PWE_HASH_TO_ELEMENT;
- else if (pwe == 2)
+ else if (pwe == SAE_PWE_BOTH)
sae_pwe = NL80211_SAE_PWE_BOTH;
- else if (pwe == 3)
+ else if (pwe == SAE_PWE_FORCE_HUNT_AND_PECK)
return 0; /* special test mode */
else
return -1;
@@ -4700,6 +4704,7 @@
#ifdef CONFIG_IEEE80211AX
+
static int nl80211_unsol_bcast_probe_resp(struct i802_bss *bss,
struct nl_msg *msg,
struct wpa_driver_ap_params *params)
@@ -4729,6 +4734,60 @@
nla_nest_end(msg, attr);
return 0;
}
+
+
+static int nl80211_mbssid(struct nl_msg *msg,
+ struct wpa_driver_ap_params *params)
+{
+ struct nlattr *config, *elems;
+ int ifidx;
+
+ if (!params->mbssid_tx_iface)
+ return 0;
+
+ config = nla_nest_start(msg, NL80211_ATTR_MBSSID_CONFIG);
+ if (!config ||
+ nla_put_u8(msg, NL80211_MBSSID_CONFIG_ATTR_INDEX,
+ params->mbssid_index))
+ return -1;
+
+ if (params->mbssid_tx_iface) {
+ ifidx = if_nametoindex(params->mbssid_tx_iface);
+ if (ifidx <= 0 ||
+ nla_put_u32(msg, NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX,
+ ifidx))
+ return -1;
+ }
+
+ if (params->ema && nla_put_flag(msg, NL80211_MBSSID_CONFIG_ATTR_EMA))
+ return -1;
+
+ nla_nest_end(msg, config);
+
+ if (params->mbssid_elem_count && params->mbssid_elem_len &&
+ params->mbssid_elem_offset && *params->mbssid_elem_offset) {
+ u8 i, **offs = params->mbssid_elem_offset;
+
+ elems = nla_nest_start(msg, NL80211_ATTR_MBSSID_ELEMS);
+ if (!elems)
+ return -1;
+
+ for (i = 0; i < params->mbssid_elem_count - 1; i++) {
+ if (nla_put(msg, i + 1, offs[i + 1] - offs[i], offs[i]))
+ return -1;
+ }
+
+ if (nla_put(msg, i + 1,
+ *offs + params->mbssid_elem_len - offs[i],
+ offs[i]))
+ return -1;
+
+ nla_nest_end(msg, elems);
+ }
+
+ return 0;
+}
+
#endif /* CONFIG_IEEE80211AX */
@@ -5027,6 +5086,9 @@
if (params->unsol_bcast_probe_resp_interval &&
nl80211_unsol_bcast_probe_resp(bss, msg, params) < 0)
goto fail;
+
+ if (nl80211_mbssid(msg, params) < 0)
+ goto fail;
#endif /* CONFIG_IEEE80211AX */
#ifdef CONFIG_SAE
@@ -6187,6 +6249,12 @@
}
#endif /* CONFIG_HE_OVERRIDES */
+ if (params->disable_eht) {
+ wpa_printf(MSG_DEBUG, " * EHT disabled");
+ if (nla_put_flag(msg, NL80211_ATTR_DISABLE_EHT))
+ return -1;
+ }
+
return 0;
}
@@ -6341,10 +6409,85 @@
struct wpa_driver_associate_params *params,
struct nl_msg *msg)
{
+ if (params->mld_params.mld_addr && params->mld_params.valid_links > 0) {
+ struct wpa_driver_mld_params *mld_params = ¶ms->mld_params;
+ struct nlattr *links, *attr;
+ int i;
+ u8 link_id;
+
+ wpa_printf(MSG_DEBUG, " * MLD: MLD addr=" MACSTR,
+ MAC2STR(mld_params->mld_addr));
+
+ if (nla_put(msg, NL80211_ATTR_MLD_ADDR, ETH_ALEN,
+ mld_params->mld_addr) ||
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+ mld_params->assoc_link_id))
+ return -1;
+
+ links = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS);
+ if (!links)
+ return -1;
+
+ attr = nla_nest_start(msg, 0);
+ if (!attr)
+ return -1;
+
+ /* First add the association link ID */
+ link_id = mld_params->assoc_link_id;
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
+ mld_params->mld_links[link_id].bssid) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
+ mld_params->mld_links[link_id].freq))
+ return -1;
+
+ os_memcpy(drv->sta_mlo_info.links[link_id].bssid,
+ mld_params->mld_links[link_id].bssid, ETH_ALEN);
+
+ nla_nest_end(msg, attr);
+
+ for (i = 1, link_id = 0; link_id < MAX_NUM_MLD_LINKS;
+ link_id++) {
+ if (!(mld_params->valid_links & BIT(link_id)) ||
+ link_id == mld_params->assoc_link_id)
+ continue;
+
+ attr = nla_nest_start(msg, i);
+ if (!attr)
+ return -1;
+
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+ link_id) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
+ mld_params->mld_links[link_id].bssid) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
+ mld_params->mld_links[link_id].freq) ||
+ (mld_params->mld_links[link_id].ies &&
+ mld_params->mld_links[i].ies_len &&
+ nla_put(msg, NL80211_ATTR_IE,
+ mld_params->mld_links[link_id].ies_len,
+ mld_params->mld_links[link_id].ies)))
+ return -1;
+
+ os_memcpy(drv->sta_mlo_info.links[link_id].bssid,
+ mld_params->mld_links[link_id].bssid,
+ ETH_ALEN);
+ nla_nest_end(msg, attr);
+ i++;
+ }
+
+ nla_nest_end(msg, links);
+
+ os_memcpy(drv->sta_mlo_info.ap_mld_addr,
+ params->mld_params.mld_addr, ETH_ALEN);
+ drv->sta_mlo_info.assoc_link_id = mld_params->assoc_link_id;
+ drv->sta_mlo_info.req_links = mld_params->valid_links;
+ }
+
if (nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER))
return -1;
- if (params->bssid) {
+ if (params->bssid && !params->mld_params.mld_addr) {
wpa_printf(MSG_DEBUG, " * bssid=" MACSTR,
MAC2STR(params->bssid));
if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
@@ -6359,7 +6502,7 @@
return -1;
}
- if (params->freq.freq) {
+ if (params->freq.freq && !params->mld_params.mld_addr) {
wpa_printf(MSG_DEBUG, " * freq=%d", params->freq.freq);
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
params->freq.freq))
@@ -7353,16 +7496,26 @@
[NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
[NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
[NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
[NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
[NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_TX_RETRIES] = { .type = NLA_U32 },
[NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 },
+ [NL80211_STA_INFO_CONNECTED_TIME] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_BEACON_LOSS] = { .type = NLA_U32 },
[NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 },
[NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 },
- [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
- [NL80211_STA_INFO_ACK_SIGNAL] = { .type = NLA_U8 },
+ [NL80211_STA_INFO_EXPECTED_THROUGHPUT] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_RX_DROP_MISC] = { .type = NLA_U64 },
+ [NL80211_STA_INFO_BEACON_RX] = { .type = NLA_U64 },
+ [NL80211_STA_INFO_BEACON_SIGNAL_AVG] = { .type = NLA_U8},
[NL80211_STA_INFO_RX_DURATION] = { .type = NLA_U64 },
+ [NL80211_STA_INFO_ACK_SIGNAL] = { .type = NLA_U8 },
+ [NL80211_STA_INFO_ACK_SIGNAL_AVG] = { .type = NLA_S8 },
+ [NL80211_STA_INFO_RX_MPDUS] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_FCS_ERROR_COUNT] = { .type = NLA_U32 },
[NL80211_STA_INFO_TX_DURATION] = { .type = NLA_U64 },
- [NL80211_STA_INFO_CONNECTED_TIME] = { .type = NLA_U32 },
};
struct nlattr *rate[NL80211_RATE_INFO_MAX + 1];
static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
@@ -7372,6 +7525,8 @@
[NL80211_RATE_INFO_VHT_MCS] = { .type = NLA_U8 },
[NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
[NL80211_RATE_INFO_VHT_NSS] = { .type = NLA_U8 },
+ [NL80211_RATE_INFO_HE_MCS] = { .type = NLA_U8 },
+ [NL80211_RATE_INFO_HE_NSS] = { .type = NLA_U8 },
};
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
@@ -7414,34 +7569,62 @@
nla_get_u64(stats[NL80211_STA_INFO_TX_BYTES64]);
data->bytes_64bit = 1;
}
+ if (stats[NL80211_STA_INFO_SIGNAL])
+ data->signal = (s8) nla_get_u8(stats[NL80211_STA_INFO_SIGNAL]);
if (stats[NL80211_STA_INFO_RX_PACKETS])
data->rx_packets =
nla_get_u32(stats[NL80211_STA_INFO_RX_PACKETS]);
if (stats[NL80211_STA_INFO_TX_PACKETS])
data->tx_packets =
nla_get_u32(stats[NL80211_STA_INFO_TX_PACKETS]);
- if (stats[NL80211_STA_INFO_RX_DURATION])
- data->rx_airtime =
- nla_get_u64(stats[NL80211_STA_INFO_RX_DURATION]);
- if (stats[NL80211_STA_INFO_TX_DURATION])
- data->tx_airtime =
- nla_get_u64(stats[NL80211_STA_INFO_TX_DURATION]);
+ if (stats[NL80211_STA_INFO_TX_RETRIES])
+ data->tx_retry_count =
+ nla_get_u32(stats[NL80211_STA_INFO_TX_RETRIES]);
if (stats[NL80211_STA_INFO_TX_FAILED])
data->tx_retry_failed =
nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]);
- if (stats[NL80211_STA_INFO_SIGNAL])
- data->signal = nla_get_u8(stats[NL80211_STA_INFO_SIGNAL]);
- if (stats[NL80211_STA_INFO_ACK_SIGNAL]) {
- data->last_ack_rssi =
- nla_get_u8(stats[NL80211_STA_INFO_ACK_SIGNAL]);
- data->flags |= STA_DRV_DATA_LAST_ACK_RSSI;
- }
-
+ if (stats[NL80211_STA_INFO_SIGNAL_AVG])
+ data->avg_signal =
+ (s8) nla_get_u8(stats[NL80211_STA_INFO_SIGNAL_AVG]);
if (stats[NL80211_STA_INFO_CONNECTED_TIME]) {
data->connected_sec =
nla_get_u32(stats[NL80211_STA_INFO_CONNECTED_TIME]);
data->flags |= STA_DRV_DATA_CONN_TIME;
}
+ if (stats[NL80211_STA_INFO_BEACON_LOSS])
+ data->beacon_loss_count =
+ nla_get_u32(stats[NL80211_STA_INFO_BEACON_LOSS]);
+ if (stats[NL80211_STA_INFO_EXPECTED_THROUGHPUT])
+ data->expected_throughput =
+ nla_get_u32(stats[NL80211_STA_INFO_EXPECTED_THROUGHPUT]);
+ if (stats[NL80211_STA_INFO_RX_DROP_MISC])
+ data->rx_drop_misc =
+ nla_get_u64(stats[NL80211_STA_INFO_RX_DROP_MISC]);
+ if (stats[NL80211_STA_INFO_BEACON_RX])
+ data->beacons_count =
+ nla_get_u64(stats[NL80211_STA_INFO_BEACON_RX]);
+ if (stats[NL80211_STA_INFO_BEACON_SIGNAL_AVG])
+ data->avg_beacon_signal =
+ (s8) nla_get_u8(stats[NL80211_STA_INFO_BEACON_SIGNAL_AVG]);
+ if (stats[NL80211_STA_INFO_RX_DURATION])
+ data->rx_airtime =
+ nla_get_u64(stats[NL80211_STA_INFO_RX_DURATION]);
+ if (stats[NL80211_STA_INFO_ACK_SIGNAL]) {
+ data->last_ack_rssi =
+ nla_get_u8(stats[NL80211_STA_INFO_ACK_SIGNAL]);
+ data->flags |= STA_DRV_DATA_LAST_ACK_RSSI;
+ }
+ if (stats[NL80211_STA_INFO_ACK_SIGNAL_AVG])
+ data->avg_ack_signal =
+ nla_get_s8(stats[NL80211_STA_INFO_ACK_SIGNAL_AVG]);
+ if (stats[NL80211_STA_INFO_RX_MPDUS])
+ data->rx_mpdus = nla_get_u32(stats[NL80211_STA_INFO_RX_MPDUS]);
+ if (stats[NL80211_STA_INFO_FCS_ERROR_COUNT])
+ data->fcs_error_count =
+ nla_get_u32(stats[NL80211_STA_INFO_FCS_ERROR_COUNT]);
+ if (stats[NL80211_STA_INFO_TX_DURATION])
+ data->tx_airtime =
+ nla_get_u64(stats[NL80211_STA_INFO_TX_DURATION]);
if (stats[NL80211_STA_INFO_TX_BITRATE] &&
nla_parse_nested(rate, NL80211_RATE_INFO_MAX,
@@ -7454,6 +7637,10 @@
data->current_tx_rate =
nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]);
+ /* Convert from 100 kbps to kbps; it's a more convenient unit.
+ * It's also safe up until ~1Tbps. */
+ data->current_tx_rate = data->current_tx_rate * 100;
+
if (rate[NL80211_RATE_INFO_MCS]) {
data->tx_mcs = nla_get_u8(rate[NL80211_RATE_INFO_MCS]);
data->flags |= STA_DRV_DATA_TX_MCS;
@@ -7470,6 +7657,16 @@
nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]);
data->flags |= STA_DRV_DATA_TX_VHT_NSS;
}
+ if (rate[NL80211_RATE_INFO_HE_MCS]) {
+ data->tx_hemcs =
+ nla_get_u8(rate[NL80211_RATE_INFO_HE_MCS]);
+ data->flags |= STA_DRV_DATA_TX_HE_MCS;
+ }
+ if (rate[NL80211_RATE_INFO_HE_NSS]) {
+ data->tx_he_nss =
+ nla_get_u8(rate[NL80211_RATE_INFO_HE_NSS]);
+ data->flags |= STA_DRV_DATA_TX_HE_NSS;
+ }
}
if (stats[NL80211_STA_INFO_RX_BITRATE] &&
@@ -7483,9 +7680,12 @@
data->current_rx_rate =
nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]);
+ /* Convert from 100 kbps to kbps; it's a more convenient unit.
+ * It's also safe up until ~1Tbps. */
+ data->current_rx_rate = data->current_rx_rate * 100;
+
if (rate[NL80211_RATE_INFO_MCS]) {
- data->rx_mcs =
- nla_get_u8(rate[NL80211_RATE_INFO_MCS]);
+ data->rx_mcs = nla_get_u8(rate[NL80211_RATE_INFO_MCS]);
data->flags |= STA_DRV_DATA_RX_MCS;
}
if (rate[NL80211_RATE_INFO_VHT_MCS]) {
@@ -7500,6 +7700,16 @@
nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]);
data->flags |= STA_DRV_DATA_RX_VHT_NSS;
}
+ if (rate[NL80211_RATE_INFO_HE_MCS]) {
+ data->rx_hemcs =
+ nla_get_u8(rate[NL80211_RATE_INFO_HE_MCS]);
+ data->flags |= STA_DRV_DATA_RX_HE_MCS;
+ }
+ if (rate[NL80211_RATE_INFO_HE_NSS]) {
+ data->rx_he_nss =
+ nla_get_u8(rate[NL80211_RATE_INFO_HE_NSS]);
+ data->flags |= STA_DRV_DATA_RX_HE_NSS;
+ }
}
if (stats[NL80211_STA_INFO_TID_STATS])
@@ -7508,6 +7718,26 @@
return NL_SKIP;
}
+
+int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
+ const u8 *bssid,
+ struct hostap_sta_driver_data *data)
+{
+ struct nl_msg *msg;
+
+ data->signal = -WPA_INVALID_NOISE;
+ data->current_tx_rate = 0;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_STATION)) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ return send_and_recv_msgs(drv, msg, get_sta_handler, data, NULL, NULL);
+}
+
+
static int i802_read_sta_data(struct i802_bss *bss,
struct hostap_sta_driver_data *data,
const u8 *addr)
@@ -8248,7 +8478,7 @@
if (!addr &&
(type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP ||
type == WPA_IF_P2P_GO || type == WPA_IF_MESH ||
- type == WPA_IF_STATION)) {
+ type == WPA_IF_STATION || type == WPA_IF_AP_BSS)) {
/* Enforce unique address */
u8 new_addr[ETH_ALEN];
@@ -8924,12 +9154,12 @@
int res;
os_memset(si, 0, sizeof(*si));
- res = nl80211_get_link_signal(drv, drv->bssid, si);
+ res = nl80211_get_link_signal(drv, drv->bssid, &si->data);
if (res) {
if (drv->nlmode != NL80211_IFTYPE_ADHOC &&
drv->nlmode != NL80211_IFTYPE_MESH_POINT)
return res;
- si->current_signal = 0;
+ si->data.signal = 0;
}
res = nl80211_get_channel_width(drv, si);
@@ -9078,7 +9308,7 @@
res = nl80211_get_link_signal(drv,
drv->sta_mlo_info.links[i].bssid,
- &mlo_si->links[i]);
+ &mlo_si->links[i].data);
if (res != 0)
return res;
@@ -9246,10 +9476,14 @@
static int nl80211_pmkid(struct i802_bss *bss, int cmd,
- struct wpa_pmkid_params *params)
+ struct wpa_pmkid_params *params, bool skip_pmk)
{
struct nl_msg *msg;
- const size_t PMK_MAX_LEN = 48; /* current cfg80211 limit */
+
+ if (cmd == NL80211_CMD_SET_PMKSA)
+ wpa_printf(MSG_DEBUG,
+ "nl80211: NL80211_CMD_SET_PMKSA with skip_pmk=%s pmk_len=%zu",
+ skip_pmk ? "true" : "false", params->pmk_len);
if (!(msg = nl80211_bss_msg(bss, 0, cmd)) ||
(params->pmkid &&
@@ -9268,7 +9502,7 @@
nla_put_u8(msg, NL80211_ATTR_PMK_REAUTH_THRESHOLD,
params->pmk_reauth_threshold)) ||
(cmd != NL80211_CMD_DEL_PMKSA &&
- params->pmk_len && params->pmk_len <= PMK_MAX_LEN &&
+ params->pmk_len && !skip_pmk &&
nla_put(msg, NL80211_ATTR_PMK, params->pmk_len, params->pmk))) {
nl80211_nlmsg_clear(msg);
nlmsg_free(msg);
@@ -9282,6 +9516,9 @@
static int nl80211_add_pmkid(void *priv, struct wpa_pmkid_params *params)
{
struct i802_bss *bss = priv;
+ const size_t PMK_MAX_LEN = 64; /* current cfg80211 limit */
+ const size_t LEGACY_PMK_MAX_LEN = 48; /* old cfg80211 limit */
+ bool skip_pmk = params->pmk_len > PMK_MAX_LEN;
int ret;
if (params->bssid)
@@ -9294,7 +9531,15 @@
wpa_ssid_txt(params->ssid, params->ssid_len));
}
- ret = nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, params);
+ ret = nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, params, skip_pmk);
+ /*
+ * Try again by skipping PMK if the first attempt failed with ERANGE
+ * error, PMK was not skipped, and PMK length is greater than the
+ * legacy kernel maximum allowed limit.
+ */
+ if (ret == -ERANGE && !skip_pmk &&
+ params->pmk_len > LEGACY_PMK_MAX_LEN)
+ ret = nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, params, true);
if (ret < 0) {
wpa_printf(MSG_DEBUG,
"nl80211: NL80211_CMD_SET_PMKSA failed: %d (%s)",
@@ -9320,7 +9565,7 @@
wpa_ssid_txt(params->ssid, params->ssid_len));
}
- ret = nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, params);
+ ret = nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, params, true);
if (ret < 0) {
wpa_printf(MSG_DEBUG,
"nl80211: NL80211_CMD_DEL_PMKSA failed: %d (%s)",
@@ -10236,7 +10481,9 @@
"capa.max_conc_chan_5_0=%u\n"
"capa.max_sched_scan_plans=%u\n"
"capa.max_sched_scan_plan_interval=%u\n"
- "capa.max_sched_scan_plan_iterations=%u\n",
+ "capa.max_sched_scan_plan_iterations=%u\n"
+ "capa.mbssid_max_interfaces=%u\n"
+ "capa.ema_max_periodicity=%u\n",
drv->capa.key_mgmt,
drv->capa.enc,
drv->capa.auth,
@@ -10258,7 +10505,9 @@
drv->capa.max_conc_chan_5_0,
drv->capa.max_sched_scan_plans,
drv->capa.max_sched_scan_plan_interval,
- drv->capa.max_sched_scan_plan_iterations);
+ drv->capa.max_sched_scan_plan_iterations,
+ drv->capa.mbssid_max_interfaces,
+ drv->capa.ema_max_periodicity);
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
@@ -11054,6 +11303,7 @@
wpa_printf(MSG_DEBUG, "nl80211: set_mac_addr for %s to " MACSTR,
bss->ifname, MAC2STR(addr));
drv->addr_changed = new_addr;
+ os_memcpy(bss->prev_addr, bss->addr, ETH_ALEN);
os_memcpy(bss->addr, addr, ETH_ALEN);
if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1) < 0)
@@ -12443,9 +12693,10 @@
ETH_ALEN, params->peer[i].peer_addr))
goto fail;
- if (params->peer[i].status == 0)
- nla_put_flag(msg,
- QCA_WLAN_VENDOR_ATTR_PASN_PEER_STATUS_SUCCESS);
+ if (params->peer[i].status == 0 &&
+ nla_put_flag(msg,
+ QCA_WLAN_VENDOR_ATTR_PASN_PEER_STATUS_SUCCESS))
+ goto fail;
wpa_printf(MSG_DEBUG,
"nl80211: Own address[%u]: " MACSTR
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index be5783b..694fb1b 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -68,6 +68,7 @@
unsigned int use_nl_connect:1;
u8 addr[ETH_ALEN];
+ u8 prev_addr[ETH_ALEN];
int freq;
int bandwidth;
@@ -128,7 +129,6 @@
u8 bssid[ETH_ALEN];
u8 prev_bssid[ETH_ALEN];
int associated;
- int mlo_assoc_link_id;
struct driver_sta_mlo_info sta_mlo_info;
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
@@ -225,6 +225,9 @@
int auth_wep_tx_keyidx;
int auth_local_state_change;
int auth_p2p;
+ bool auth_mld;
+ u8 auth_mld_link_id;
+ u8 auth_ap_mld_addr[ETH_ALEN];
/*
* Tells whether the last scan issued from wpa_supplicant was a normal
@@ -272,7 +275,8 @@
int is_sta_interface(enum nl80211_iftype nlmode);
int wpa_driver_nl80211_authenticate_retry(struct wpa_driver_nl80211_data *drv);
int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
- const u8 *bssid, struct wpa_signal_info *sig);
+ const u8 *bssid,
+ struct hostap_sta_driver_data *data);
int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
struct wpa_signal_info *sig_change);
int nl80211_get_wiphy_index(struct i802_bss *bss);
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index a211d2a..2f3b85a 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -874,6 +874,30 @@
}
+static void wiphy_info_mbssid(struct wpa_driver_capa *cap, struct nlattr *attr)
+{
+ struct nlattr *config[NL80211_MBSSID_CONFIG_ATTR_MAX + 1];
+
+ if (nla_parse_nested(config, NL80211_MBSSID_CONFIG_ATTR_MAX, attr,
+ NULL))
+ return;
+
+ if (!config[NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES])
+ return;
+
+ cap->mbssid_max_interfaces =
+ nla_get_u8(config[NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES]);
+
+ if (config[NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY])
+ cap->ema_max_periodicity =
+ nla_get_u8(config[NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY]);
+
+ wpa_printf(MSG_DEBUG,
+ "mbssid: max interfaces %u, max profile periodicity %u",
+ cap->mbssid_max_interfaces, cap->ema_max_periodicity);
+}
+
+
static int wiphy_info_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -1118,6 +1142,12 @@
capa->max_num_akms =
nla_get_u16(tb[NL80211_ATTR_MAX_NUM_AKM_SUITES]);
+ if (tb[NL80211_ATTR_MBSSID_CONFIG])
+ wiphy_info_mbssid(capa, tb[NL80211_ATTR_MBSSID_CONFIG]);
+
+ if (tb[NL80211_ATTR_MLO_SUPPORT])
+ capa->flags2 |= WPA_DRIVER_FLAGS2_MLO;
+
return NL_SKIP;
}
@@ -2003,7 +2033,8 @@
os_memset(mode, 0, sizeof(*mode));
mode->mode = NUM_HOSTAPD_MODES;
mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN |
- HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN;
+ HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN |
+ HOSTAPD_MODE_FLAG_HE_INFO_KNOWN;
/*
* Unsupported VHT MCS stream is defined as value 3, so the VHT
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 50fe438..46e4efb 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -481,7 +481,16 @@
if (link_id >= MAX_NUM_MLD_LINKS)
continue;
- mlo->valid_links |= BIT(link_id);
+ if (tb[NL80211_ATTR_STATUS_CODE]) {
+ /* Set requested links only when status indicated */
+ mlo->req_links |= BIT(link_id);
+ if (nla_get_u16(tb[NL80211_ATTR_STATUS_CODE]) ==
+ WLAN_STATUS_SUCCESS)
+ mlo->valid_links |= BIT(link_id);
+ } else {
+ mlo->valid_links |= BIT(link_id);
+ }
+
os_memcpy(mlo->links[link_id].addr,
nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
os_memcpy(mlo->links[link_id].bssid,
@@ -494,9 +503,149 @@
}
+struct links_info {
+ /* bitmap of link IDs in Per-STA profile subelements */
+ u16 non_assoc_links;
+ u8 addr[MAX_NUM_MLD_LINKS][ETH_ALEN];
+};
+
+
+static void nl80211_get_basic_mle_links_info(const u8 *mle, size_t mle_len,
+ struct links_info *info)
+{
+ size_t rem_len;
+ const u8 *pos;
+
+ if (mle_len < MULTI_LINK_CONTROL_LEN + 1 ||
+ mle_len - MULTI_LINK_CONTROL_LEN < mle[MULTI_LINK_CONTROL_LEN])
+ return;
+
+ /* Skip Common Info */
+ pos = mle + MULTI_LINK_CONTROL_LEN + mle[MULTI_LINK_CONTROL_LEN];
+ rem_len = mle_len -
+ (MULTI_LINK_CONTROL_LEN + mle[MULTI_LINK_CONTROL_LEN]);
+
+ /* Parse Subelements */
+ while (rem_len > 2) {
+ size_t ie_len = 2 + pos[1];
+
+ if (rem_len < ie_len)
+ break;
+
+ if (pos[0] == MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE) {
+ u8 link_id;
+ const u8 *sta_profile;
+ u16 sta_ctrl;
+
+ if (pos[1] < BASIC_MLE_STA_PROF_STA_MAC_IDX + ETH_ALEN)
+ goto next_subelem;
+
+ sta_profile = &pos[2];
+ sta_ctrl = WPA_GET_LE16(sta_profile);
+ link_id = sta_ctrl & BASIC_MLE_STA_CTRL_LINK_ID_MASK;
+ if (link_id >= MAX_NUM_MLD_LINKS)
+ goto next_subelem;
+
+ if (!(sta_ctrl & BASIC_MLE_STA_CTRL_PRES_STA_MAC))
+ goto next_subelem;
+
+ info->non_assoc_links |= BIT(link_id);
+ os_memcpy(info->addr[link_id],
+ &sta_profile[BASIC_MLE_STA_PROF_STA_MAC_IDX],
+ ETH_ALEN);
+ }
+next_subelem:
+ pos += ie_len;
+ rem_len -= ie_len;
+ }
+}
+
+
+static int nl80211_update_rejected_links_info(struct driver_sta_mlo_info *mlo,
+ struct nlattr *req_ie,
+ struct nlattr *resp_ie)
+{
+ int i;
+ struct wpabuf *mle;
+ struct ieee802_11_elems req_elems, resp_elems;
+ struct links_info req_info, resp_info;
+
+ if (!req_ie || !resp_ie) {
+ wpa_printf(MSG_INFO,
+ "nl80211: MLO: (Re)Association Request/Response frame elements not available");
+ return -1;
+ }
+
+ if (ieee802_11_parse_elems(nla_data(req_ie), nla_len(req_ie),
+ &req_elems, 0) == ParseFailed ||
+ ieee802_11_parse_elems(nla_data(resp_ie), nla_len(resp_ie),
+ &resp_elems, 0) == ParseFailed) {
+ wpa_printf(MSG_INFO,
+ "nl80211: MLO: Failed to parse (Re)Association Request/Response elements");
+ return -1;
+ }
+
+ mle = ieee802_11_defrag_mle(&req_elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+ if (!mle) {
+ wpa_printf(MSG_INFO,
+ "nl80211: MLO: Basic Multi-Link element not found in Association Request");
+ return -1;
+ }
+ os_memset(&req_info, 0, sizeof(req_info));
+ nl80211_get_basic_mle_links_info(wpabuf_head(mle), wpabuf_len(mle),
+ &req_info);
+ wpabuf_free(mle);
+
+ mle = ieee802_11_defrag_mle(&resp_elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+ if (!mle) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: MLO: Basic Multi-Link element not found in Association Response");
+ return -1;
+ }
+ os_memset(&resp_info, 0, sizeof(resp_info));
+ nl80211_get_basic_mle_links_info(wpabuf_head(mle), wpabuf_len(mle),
+ &resp_info);
+ wpabuf_free(mle);
+
+ if (req_info.non_assoc_links != resp_info.non_assoc_links) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: MLO: Association Request and Response links bitmaps not equal (0x%x != 0x%x)",
+ req_info.non_assoc_links,
+ resp_info.non_assoc_links);
+ return -1;
+ }
+
+ mlo->req_links = BIT(mlo->assoc_link_id) | req_info.non_assoc_links;
+ if ((mlo->req_links & mlo->valid_links) != mlo->valid_links) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: MLO: Accepted links are not a subset of requested links (req_links=0x%x valid_links=0x%x non_assoc_links=0x%x assoc_link_id=0x%x)",
+ mlo->req_links, mlo->valid_links,
+ req_info.non_assoc_links, BIT(mlo->assoc_link_id));
+ return -1;
+ }
+
+ /* Get MLO links info for rejected links */
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (!((mlo->req_links & ~mlo->valid_links) & BIT(i)))
+ continue;
+
+ os_memcpy(mlo->links[i].bssid, resp_info.addr[i], ETH_ALEN);
+ os_memcpy(mlo->links[i].addr, req_info.addr[i], ETH_ALEN);
+ }
+
+ return 0;
+}
+
+
static int nl80211_get_assoc_link_id(const u8 *data, u8 len)
{
- if (!(data[0] & BASIC_MULTI_LINK_CTRL0_PRES_LINK_ID))
+ u16 control;
+
+ if (len < 2)
+ return -1;
+
+ control = WPA_GET_LE16(data);
+ if (!(control & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID))
return -1;
#define BASIC_ML_IE_COMMON_INFO_LINK_ID_IDX \
@@ -514,10 +663,12 @@
bool qca_roam_auth,
struct nlattr *addr,
struct nlattr *mlo_links,
+ struct nlattr *req_ie,
struct nlattr *resp_ie)
{
const u8 *ml_ie;
struct driver_sta_mlo_info *mlo = &drv->sta_mlo_info;
+ int res;
if (!addr || !mlo_links || !resp_ie)
return;
@@ -527,11 +678,14 @@
if (!ml_ie)
return;
- drv->mlo_assoc_link_id = nl80211_get_assoc_link_id(&ml_ie[3],
- ml_ie[1] - 1);
- if (drv->mlo_assoc_link_id < 0 ||
- drv->mlo_assoc_link_id >= MAX_NUM_MLD_LINKS)
+ res = nl80211_get_assoc_link_id(&ml_ie[3], ml_ie[1] - 1);
+ if (res < 0 || res >= MAX_NUM_MLD_LINKS) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Could not find a valid association Link ID (res=%d)",
+ res);
return;
+ }
+ drv->sta_mlo_info.assoc_link_id = res;
os_memcpy(mlo->ap_mld_addr, nla_data(addr), ETH_ALEN);
wpa_printf(MSG_DEBUG, "nl80211: AP MLD MAC Address " MACSTR,
@@ -544,14 +698,15 @@
nl80211_parse_qca_vendor_mlo_link_info(mlo, mlo_links);
#endif /* CONFIG_DRIVER_NL80211_QCA */
- if (!(mlo->valid_links & BIT(drv->mlo_assoc_link_id))) {
- wpa_printf(MSG_ERROR, "nl80211: Invalid MLO assoc link ID %d",
- drv->mlo_assoc_link_id);
+ if (!(mlo->valid_links & BIT(mlo->assoc_link_id)) ||
+ (!mlo->req_links &&
+ nl80211_update_rejected_links_info(mlo, req_ie, resp_ie))) {
+ wpa_printf(MSG_INFO, "nl80211: Invalid MLO connection info");
mlo->valid_links = 0;
return;
}
- os_memcpy(drv->bssid, mlo->links[drv->mlo_assoc_link_id].bssid,
+ os_memcpy(drv->bssid, mlo->links[drv->sta_mlo_info.assoc_link_id].bssid,
ETH_ALEN);
os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
}
@@ -664,8 +819,9 @@
}
drv->associated = 1;
- drv->sta_mlo_info.valid_links = 0;
- nl80211_parse_mlo_info(drv, qca_roam_auth, addr, mlo_links, resp_ie);
+ os_memset(&drv->sta_mlo_info, 0, sizeof(drv->sta_mlo_info));
+ nl80211_parse_mlo_info(drv, qca_roam_auth, addr, mlo_links, req_ie,
+ resp_ie);
if (!drv->sta_mlo_info.valid_links && addr) {
os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN);
os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
@@ -916,7 +1072,7 @@
EVENT_LINK_CH_SWITCH_STARTED, &data);
}
- if (link_id != drv->mlo_assoc_link_id)
+ if (link_id != drv->sta_mlo_info.assoc_link_id)
return;
}
@@ -1089,8 +1245,11 @@
* ignore_next_local_deauth as well, to avoid next local
* deauth event be wrongly ignored.
*/
- if (!os_memcmp(mgmt->sa, drv->first_bss->addr,
- ETH_ALEN)) {
+ if (os_memcmp(mgmt->sa, drv->first_bss->addr,
+ ETH_ALEN) == 0 ||
+ (!is_zero_ether_addr(drv->first_bss->prev_addr) &&
+ os_memcmp(mgmt->sa, drv->first_bss->prev_addr,
+ ETH_ALEN) == 0)) {
wpa_printf(MSG_DEBUG,
"nl80211: Received a locally generated deauth event. Clear ignore_next_local_deauth flag");
drv->ignore_next_local_deauth = 0;
@@ -1300,7 +1459,10 @@
os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 &&
(is_zero_ether_addr(bss->rand_addr) ||
os_memcmp(bss->rand_addr, data + 4, ETH_ALEN) != 0) &&
- os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) {
+ os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0 &&
+ (is_zero_ether_addr(drv->first_bss->prev_addr) ||
+ os_memcmp(bss->prev_addr, data + 4 + ETH_ALEN,
+ ETH_ALEN) != 0)) {
wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event "
"for foreign address", bss->ifname);
return;
@@ -1522,7 +1684,7 @@
struct nlattr *nl;
int rem;
struct scan_info *info;
-#define MAX_REPORT_FREQS 100
+#define MAX_REPORT_FREQS 110
int freqs[MAX_REPORT_FREQS];
int num_freqs = 0;
@@ -1554,7 +1716,7 @@
}
}
if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) {
- char msg[500], *pos, *end;
+ char msg[MAX_REPORT_FREQS * 5], *pos, *end;
int res;
pos = msg;
@@ -1677,11 +1839,11 @@
* nl80211_get_link_signal() and nl80211_get_link_noise() set default
* values in case querying the driver fails.
*/
- res = nl80211_get_link_signal(drv, drv->bssid, &ed.signal_change);
+ res = nl80211_get_link_signal(drv, drv->bssid, &ed.signal_change.data);
if (res == 0) {
- wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm txrate: %d",
- ed.signal_change.current_signal,
- ed.signal_change.current_txrate);
+ wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm txrate: %lu",
+ ed.signal_change.data.signal,
+ ed.signal_change.data.current_tx_rate);
} else {
wpa_printf(MSG_DEBUG,
"nl80211: Querying the driver for signal info failed");
@@ -2293,6 +2455,14 @@
tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMK],
tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID],
tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MLO_LINKS]);
+
+#ifdef ANDROID
+#ifdef ANDROID_LIB_EVENT
+ wpa_driver_nl80211_driver_event(
+ drv, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH,
+ data, len);
+#endif /* ANDROID_LIB_EVENT */
+#endif /* ANDROID */
}
@@ -2614,11 +2784,11 @@
nl_src = cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_SRC_ADDR];
nl_peer = cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAC_ADDR];
if (nl_src)
- os_memcpy(event.pasn_auth.peer[idx].own_addr, nl_src,
- ETH_ALEN);
+ os_memcpy(event.pasn_auth.peer[idx].own_addr,
+ nla_data(nl_src), ETH_ALEN);
if (nl_peer)
- os_memcpy(event.pasn_auth.peer[idx].peer_addr, nl_peer,
- ETH_ALEN);
+ os_memcpy(event.pasn_auth.peer[idx].peer_addr,
+ nla_data(nl_peer), ETH_ALEN);
if (cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_LTF_KEYSEED_REQUIRED])
event.pasn_auth.peer[idx].ltf_keyseed_required = true;
idx++;
@@ -2802,7 +2972,13 @@
#ifdef ANDROID
#ifdef ANDROID_LIB_EVENT
- wpa_driver_nl80211_driver_event(drv, vendor_id, subcmd, data, len);
+ /* Postpone QCA roam+auth event indication to the point when both that
+ * and the NL80211_CMD_ROAM event have been received (see calls to
+ * qca_nl80211_key_mgmt_auth() and drv->pending_roam_data). */
+ if (!(vendor_id == OUI_QCA &&
+ subcmd == QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH))
+ wpa_driver_nl80211_driver_event(drv, vendor_id, subcmd, data,
+ len);
#endif /* ANDROID_LIB_EVENT */
#endif /* ANDROID */
@@ -2980,6 +3156,9 @@
struct nlattr **tb)
{
const u8 *addr;
+ union wpa_event_data event;
+
+ os_memset(&event, 0, sizeof(event));
if (!tb[NL80211_ATTR_MAC] ||
nla_len(tb[NL80211_ATTR_MAC]) != ETH_ALEN) {
@@ -2997,7 +3176,15 @@
return;
}
- wpa_supplicant_event(drv->ctx, EVENT_PORT_AUTHORIZED, NULL);
+ if (tb[NL80211_ATTR_TD_BITMAP]) {
+ event.port_authorized.td_bitmap_len =
+ nla_len(tb[NL80211_ATTR_TD_BITMAP]);
+ if (event.port_authorized.td_bitmap_len > 0)
+ event.port_authorized.td_bitmap =
+ nla_data(tb[NL80211_ATTR_TD_BITMAP]);
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_PORT_AUTHORIZED, &event);
}
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index d54dd3a..7a7890c 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -911,8 +911,21 @@
"nl80211: Local state (associated with " MACSTR
") does not match with BSS state",
MAC2STR(drv->bssid));
- clear_state_mismatch(drv, r->bssid);
- clear_state_mismatch(drv, drv->bssid);
+
+ if (os_memcmp(drv->sta_mlo_info.ap_mld_addr, drv->bssid,
+ ETH_ALEN) != 0) {
+ clear_state_mismatch(drv, r->bssid);
+
+ if (!is_zero_ether_addr(drv->sta_mlo_info.ap_mld_addr))
+ clear_state_mismatch(
+ drv, drv->sta_mlo_info.ap_mld_addr);
+ else
+ clear_state_mismatch(drv, drv->bssid);
+
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: BSSID is the MLD address");
+ }
}
}
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index 0f0ad1f..cf201fe 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -2424,7 +2424,7 @@
struct iwreq iwr;
os_memset(si, 0, sizeof(*si));
- si->current_signal = -WPA_INVALID_NOISE;
+ si->data.signal = -WPA_INVALID_NOISE;
si->current_noise = WPA_INVALID_NOISE;
si->chanwidth = CHAN_WIDTH_UNKNOWN;
@@ -2440,7 +2440,7 @@
return -1;
}
- si->current_signal = stats.qual.level -
+ si->data.signal = stats.qual.level -
((stats.qual.updated & IW_QUAL_DBM) ? 0x100 : 0);
si->current_noise = stats.qual.noise -
((stats.qual.updated & IW_QUAL_DBM) ? 0x100 : 0);
diff --git a/src/drivers/netlink.c b/src/drivers/netlink.c
index 0e960f4..7780479 100644
--- a/src/drivers/netlink.c
+++ b/src/drivers/netlink.c
@@ -147,8 +147,9 @@
return "kernel-control";
case 1:
return "userspace-control";
+ default:
+ return "?";
}
- return "?";
}
@@ -161,8 +162,9 @@
return "IF_OPER_DORMANT";
case IF_OPER_UP:
return "IF_OPER_UP";
+ default:
+ return "?";
}
- return "?";
}
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index ffb7c57..c14a91b 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -377,14 +377,22 @@
* the non-transmitting interfaces are deleted as well.
*
* @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified
- * by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC.
+ * by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC. %NL80211_ATTR_MAC
+ * represents peer's MLD address for MLO pairwise key. For MLO group key,
+ * the link is identified by %NL80211_ATTR_MLO_LINK_ID.
* @NL80211_CMD_SET_KEY: Set key attributes %NL80211_ATTR_KEY_DEFAULT,
* %NL80211_ATTR_KEY_DEFAULT_MGMT, or %NL80211_ATTR_KEY_THRESHOLD.
+ * For MLO connection, the link to set default key is identified by
+ * %NL80211_ATTR_MLO_LINK_ID.
* @NL80211_CMD_NEW_KEY: add a key with given %NL80211_ATTR_KEY_DATA,
* %NL80211_ATTR_KEY_IDX, %NL80211_ATTR_MAC, %NL80211_ATTR_KEY_CIPHER,
- * and %NL80211_ATTR_KEY_SEQ attributes.
+ * and %NL80211_ATTR_KEY_SEQ attributes. %NL80211_ATTR_MAC represents
+ * peer's MLD address for MLO pairwise key. The link to add MLO
+ * group key is identified by %NL80211_ATTR_MLO_LINK_ID.
* @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX
- * or %NL80211_ATTR_MAC.
+ * or %NL80211_ATTR_MAC. %NL80211_ATTR_MAC represents peer's MLD address
+ * for MLO pairwise key. The link to delete group key is identified by
+ * %NL80211_ATTR_MLO_LINK_ID.
*
* @NL80211_CMD_GET_BEACON: (not used)
* @NL80211_CMD_SET_BEACON: change the beacon on an access point interface
@@ -2741,6 +2749,8 @@
* When used with %NL80211_CMD_FRAME_TX_STATUS, indicates the ack RX
* timestamp. When used with %NL80211_CMD_FRAME RX notification, indicates
* the incoming frame RX timestamp.
+ * @NL80211_ATTR_TD_BITMAP: Transition Disable bitmap, for subsequent
+ * (re)associations.
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3268,6 +3278,7 @@
NL80211_ATTR_TX_HW_TIMESTAMP,
NL80211_ATTR_RX_HW_TIMESTAMP,
+ NL80211_ATTR_TD_BITMAP,
/* add attributes here, update the policy in nl80211.c */
@@ -4951,6 +4962,7 @@
* using the nesting index as the antenna number.
* @NL80211_BSS_FREQUENCY_OFFSET: frequency offset in KHz
* @NL80211_BSS_MLO_LINK_ID: MLO link ID of the BSS (u8).
+ * @NL80211_BSS_MLD_ADDR: MLD address of this BSS if connected to it.
* @__NL80211_BSS_AFTER_LAST: internal
* @NL80211_BSS_MAX: highest BSS attribute
*/
@@ -4977,6 +4989,7 @@
NL80211_BSS_CHAIN_SIGNAL,
NL80211_BSS_FREQUENCY_OFFSET,
NL80211_BSS_MLO_LINK_ID,
+ NL80211_BSS_MLD_ADDR,
/* keep last */
__NL80211_BSS_AFTER_LAST,
@@ -6273,6 +6286,14 @@
* @NL80211_EXT_FEATURE_RADAR_BACKGROUND: Device supports background radar/CAC
* detection.
*
+ * @NL80211_EXT_FEATURE_POWERED_ADDR_CHANGE: Device can perform a MAC address
+ * change without having to bring the underlying network device down
+ * first. For example, in station mode this can be used to vary the
+ * origin MAC address prior to a connection to a new AP for privacy
+ * or other reasons. Note that certain driver specific restrictions
+ * might apply, e.g. no scans in progress, no offchannel operations
+ * in progress, and no active connections.
+ *
* @NUM_NL80211_EXT_FEATURES: number of extended features.
* @MAX_NL80211_EXT_FEATURES: highest extended feature index.
*/
@@ -6340,6 +6361,7 @@
NL80211_EXT_FEATURE_BSS_COLOR,
NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD,
NL80211_EXT_FEATURE_RADAR_BACKGROUND,
+ NL80211_EXT_FEATURE_POWERED_ADDR_CHANGE,
/* add new features before the definition below */
NUM_NL80211_EXT_FEATURES,
diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c
index 0e3a7b2..ff8ad8d 100644
--- a/src/eap_common/eap_pwd_common.c
+++ b/src/eap_common/eap_pwd_common.c
@@ -356,9 +356,19 @@
return -1;
}
eap_pwd_h_update(hash, (const u8 *) ciphersuite, sizeof(u32));
- crypto_bignum_to_bin(peer_scalar, cruft, order_len, order_len);
+ if (crypto_bignum_to_bin(peer_scalar, cruft, order_len,
+ order_len) < 0) {
+ os_free(cruft);
+ return -1;
+ }
+
eap_pwd_h_update(hash, cruft, order_len);
- crypto_bignum_to_bin(server_scalar, cruft, order_len, order_len);
+ if (crypto_bignum_to_bin(server_scalar, cruft, order_len,
+ order_len) < 0) {
+ os_free(cruft);
+ return -1;
+ }
+
eap_pwd_h_update(hash, cruft, order_len);
eap_pwd_h_final(hash, &session_id[1]);
@@ -368,7 +378,12 @@
os_free(cruft);
return -1;
}
- crypto_bignum_to_bin(k, cruft, prime_len, prime_len);
+
+ if (crypto_bignum_to_bin(k, cruft, prime_len, prime_len) < 0) {
+ os_free(cruft);
+ return -1;
+ }
+
eap_pwd_h_update(hash, cruft, prime_len);
os_free(cruft);
eap_pwd_h_update(hash, confirm_peer, SHA256_MAC_LEN);
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index 15664df..ce44a4f 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -267,6 +267,7 @@
sm->reauthInit = false;
sm->erp_seq = (u32) -1;
sm->use_machine_cred = 0;
+ sm->eap_fast_mschapv2 = false;
}
diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c
index b12cfee..d7677ca 100644
--- a/src/eap_peer/eap_fast.c
+++ b/src/eap_peer/eap_fast.c
@@ -354,6 +354,7 @@
sm->auth_challenge = data->key_block_p->server_challenge;
sm->peer_challenge = data->key_block_p->client_challenge;
}
+ sm->eap_fast_mschapv2 = true;
sm->init_phase2 = 1;
data->phase2_priv = data->phase2_method->init(sm);
sm->init_phase2 = 0;
@@ -693,18 +694,7 @@
if (key_len > isk_len)
key_len = isk_len;
- if (key_len == 32 &&
- data->phase2_method->vendor == EAP_VENDOR_IETF &&
- data->phase2_method->method == EAP_TYPE_MSCHAPV2) {
- /*
- * EAP-FAST uses reverse order for MS-MPPE keys when deriving
- * MSK from EAP-MSCHAPv2. Swap the keys here to get the correct
- * ISK for EAP-FAST cryptobinding.
- */
- os_memcpy(isk, key + 16, 16);
- os_memcpy(isk + 16, key, 16);
- } else
- os_memcpy(isk, key, key_len);
+ os_memcpy(isk, key, key_len);
os_free(key);
return 0;
diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h
index 652b67e..3fe5f77 100644
--- a/src/eap_peer/eap_i.h
+++ b/src/eap_peer/eap_i.h
@@ -365,6 +365,11 @@
/* Optional challenges generated in Phase 1 (EAP-FAST) */
u8 *peer_challenge, *auth_challenge;
+ /* Whether to use the EAP-FAST-MSCHAPv2 instantiation of EAP-MSCHAPv2.
+ * That variant is otherwise identical, but it generates the MSK using
+ * MS-MPPE keys in reverse order. */
+ bool eap_fast_mschapv2;
+
int num_rounds;
int num_rounds_short;
int force_disabled;
diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c
index 8ad4d18..e84ae16 100644
--- a/src/eap_peer/eap_mschapv2.c
+++ b/src/eap_peer/eap_mschapv2.c
@@ -108,6 +108,12 @@
if (data == NULL)
return NULL;
+ wpa_printf(MSG_DEBUG, "EAP-%sMSCHAPv2 init%s%s",
+ sm->eap_fast_mschapv2 ? "FAST-" : "",
+ sm->peer_challenge && sm->auth_challenge ?
+ " with preset challenges" : "",
+ sm->init_phase2 ? " for Phase 2" : "");
+
if (sm->peer_challenge) {
data->peer_challenge = os_memdup(sm->peer_challenge,
MSCHAPV2_CHAL_LEN);
@@ -844,6 +850,7 @@
struct eap_mschapv2_data *data = priv;
u8 *key;
int key_len;
+ bool first_is_send;
if (!data->master_key_valid || !data->success)
return NULL;
@@ -854,12 +861,25 @@
if (key == NULL)
return NULL;
- /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e.,
- * peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */
- if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1,
- 0) < 0 ||
+ /*
+ * [MS-CHAP], 3.1.5.1 (Master Session Key (MSK) Derivation
+ * MSK = MasterReceiveKey + MasterSendKey + 32 bytes zeros (padding)
+ * On a Peer:
+ * MS-MPPE-Recv-Key = MasterSendKey
+ * MS-MPPE-Send-Key = MasterReceiveKey
+ *
+ * RFC 5422, 3.2.3 (Authenticating Using EAP-FAST-MSCHAPv2)
+ * MSK = MasterSendKey + MasterReceiveKey
+ * (i.e., reverse order and no padding)
+ *
+ * On Peer, EAP-MSCHAPv2 starts with Send key and EAP-FAST-MSCHAPv2
+ * starts with Receive key.
+ */
+ first_is_send = !sm->eap_fast_mschapv2;
+ if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN,
+ first_is_send, 0) < 0 ||
get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
- MSCHAPV2_KEY_LEN, 0, 0) < 0) {
+ MSCHAPV2_KEY_LEN, !first_is_send, 0) < 0) {
os_free(key);
return NULL;
}
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
index 605feb2..97f4dd2 100644
--- a/src/eap_peer/eap_pwd.c
+++ b/src/eap_peer/eap_pwd.c
@@ -666,7 +666,10 @@
* sufficiently smaller than the prime or order might need pre-pending
* with zeros.
*/
- crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len);
+ if (crypto_bignum_to_bin(data->my_scalar, scalar, order_len,
+ order_len) < 0)
+ goto fin;
+
if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element,
element + prime_len) != 0) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail");
@@ -742,7 +745,9 @@
* zero the memory each time because this is mod prime math and some
* value may start with a few zeros and the previous one did not.
*/
- crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len);
+ if (crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len) < 0)
+ goto fin;
+
eap_pwd_h_update(hash, cruft, prime_len);
/* server element: x, y */
@@ -755,7 +760,10 @@
eap_pwd_h_update(hash, cruft, prime_len * 2);
/* server scalar */
- crypto_bignum_to_bin(data->server_scalar, cruft, order_len, order_len);
+ if (crypto_bignum_to_bin(data->server_scalar, cruft, order_len,
+ order_len) < 0)
+ goto fin;
+
eap_pwd_h_update(hash, cruft, order_len);
/* my element: x, y */
@@ -768,7 +776,10 @@
eap_pwd_h_update(hash, cruft, prime_len * 2);
/* my scalar */
- crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len);
+ if (crypto_bignum_to_bin(data->my_scalar, cruft, order_len,
+ order_len) < 0)
+ goto fin;
+
eap_pwd_h_update(hash, cruft, order_len);
/* the ciphersuite */
@@ -796,7 +807,9 @@
goto fin;
/* k */
- crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len);
+ if (crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len) < 0)
+ goto fin;
+
eap_pwd_h_update(hash, cruft, prime_len);
/* my element */
@@ -809,7 +822,10 @@
eap_pwd_h_update(hash, cruft, prime_len * 2);
/* my scalar */
- crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len);
+ if (crypto_bignum_to_bin(data->my_scalar, cruft, order_len,
+ order_len) < 0)
+ goto fin;
+
eap_pwd_h_update(hash, cruft, order_len);
/* server element: x, y */
@@ -822,7 +838,10 @@
eap_pwd_h_update(hash, cruft, prime_len * 2);
/* server scalar */
- crypto_bignum_to_bin(data->server_scalar, cruft, order_len, order_len);
+ if (crypto_bignum_to_bin(data->server_scalar, cruft, order_len,
+ order_len) < 0)
+ goto fin;
+
eap_pwd_h_update(hash, cruft, order_len);
/* the ciphersuite */
diff --git a/src/eap_peer/eap_teap.c b/src/eap_peer/eap_teap.c
index bc7f6f4..ced7b16 100644
--- a/src/eap_peer/eap_teap.c
+++ b/src/eap_peer/eap_teap.c
@@ -319,6 +319,13 @@
if (!data->phase2_method)
return -1;
+ /* While RFC 7170 does not describe this, EAP-TEAP has been deployed
+ * with implementations that use the EAP-FAST-MSCHAPv2, instead of the
+ * EAP-MSCHAPv2, way of deriving the MSK for IMSK. Use that design here
+ * to interoperate.
+ */
+ sm->eap_fast_mschapv2 = true;
+
sm->init_phase2 = 1;
data->phase2_priv = data->phase2_method->init(sm);
sm->init_phase2 = 0;
@@ -1298,6 +1305,33 @@
goto done;
}
+ if (tlv.crypto_binding) {
+ if (tlv.iresult != TEAP_STATUS_SUCCESS &&
+ tlv.result != TEAP_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Unexpected Crypto-Binding TLV without Result TLV or Intermediate-Result TLV indicating success");
+ failed = 1;
+ error = TEAP_ERROR_UNEXPECTED_TLVS_EXCHANGED;
+ goto done;
+ }
+
+ tmp = eap_teap_process_crypto_binding(sm, data, ret,
+ tlv.crypto_binding,
+ tlv.crypto_binding_len);
+ if (!tmp) {
+ failed = 1;
+ error = TEAP_ERROR_TUNNEL_COMPROMISE_ERROR;
+ } else {
+ resp = wpabuf_concat(resp, tmp);
+ if (tlv.result == TEAP_STATUS_SUCCESS && !failed)
+ data->result_success_done = 1;
+ if (tlv.iresult == TEAP_STATUS_SUCCESS && !failed) {
+ data->inner_method_done = 0;
+ data->iresult_verified = 1;
+ }
+ }
+ }
+
if (tlv.identity_type == TEAP_IDENTITY_TYPE_MACHINE) {
struct eap_peer_config *config = eap_get_config(sm);
@@ -1353,33 +1387,6 @@
}
}
- if (tlv.crypto_binding) {
- if (tlv.iresult != TEAP_STATUS_SUCCESS &&
- tlv.result != TEAP_STATUS_SUCCESS) {
- wpa_printf(MSG_DEBUG,
- "EAP-TEAP: Unexpected Crypto-Binding TLV without Result TLV or Intermediate-Result TLV indicating success");
- failed = 1;
- error = TEAP_ERROR_UNEXPECTED_TLVS_EXCHANGED;
- goto done;
- }
-
- tmp = eap_teap_process_crypto_binding(sm, data, ret,
- tlv.crypto_binding,
- tlv.crypto_binding_len);
- if (!tmp) {
- failed = 1;
- error = TEAP_ERROR_TUNNEL_COMPROMISE_ERROR;
- } else {
- resp = wpabuf_concat(resp, tmp);
- if (tlv.result == TEAP_STATUS_SUCCESS && !failed)
- data->result_success_done = 1;
- if (tlv.iresult == TEAP_STATUS_SUCCESS && !failed) {
- data->inner_method_done = 0;
- data->iresult_verified = 1;
- }
- }
- }
-
if (data->result_success_done && data->session_ticket_used &&
eap_teap_derive_msk(data) == 0) {
/* Assume the server might accept authentication without going
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index 2894cfb..3696e1d 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -209,6 +209,7 @@
EAP_TEAP_ID_REQUEST_MACHINE_ACCEPT_USER = 4,
EAP_TEAP_ID_REQUIRE_USER_AND_MACHINE = 5,
} eap_teap_id;
+ int eap_teap_method_sequence;
/**
* eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication
diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h
index 28bb564..1c59bb0 100644
--- a/src/eap_server/eap_i.h
+++ b/src/eap_server/eap_i.h
@@ -176,9 +176,15 @@
METHOD_PENDING_NONE, METHOD_PENDING_WAIT, METHOD_PENDING_CONT
} method_pending;
+ /* Optional challenges generated in Phase 1 (EAP-FAST) */
u8 *auth_challenge;
u8 *peer_challenge;
+ /* Whether to use the EAP-FAST-MSCHAPv2 instantiation of EAP-MSCHAPv2.
+ * That variant is otherwise identical, but it generates the MSK using
+ * MS-MPPE keys in reverse order. */
+ bool eap_fast_mschapv2;
+
struct wpabuf *assoc_wps_ie;
struct wpabuf *assoc_p2p_ie;
diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c
index 55d48d9..0596334 100644
--- a/src/eap_server/eap_server_fast.c
+++ b/src/eap_server/eap_server_fast.c
@@ -357,18 +357,7 @@
if (key_len > isk_len)
key_len = isk_len;
- if (key_len == 32 &&
- data->phase2_method->vendor == EAP_VENDOR_IETF &&
- data->phase2_method->method == EAP_TYPE_MSCHAPV2) {
- /*
- * EAP-FAST uses reverse order for MS-MPPE keys when deriving
- * MSK from EAP-MSCHAPv2. Swap the keys here to get the correct
- * ISK for EAP-FAST cryptobinding.
- */
- os_memcpy(isk, key + 16, 16);
- os_memcpy(isk + 16, key, 16);
- } else
- os_memcpy(isk, key, key_len);
+ os_memcpy(isk, key, key_len);
os_free(key);
return 0;
@@ -961,6 +950,7 @@
sm->auth_challenge = data->key_block_p->server_challenge;
sm->peer_challenge = data->key_block_p->client_challenge;
}
+ sm->eap_fast_mschapv2 = true;
sm->init_phase2 = 1;
data->phase2_priv = data->phase2_method->init(sm);
sm->init_phase2 = 0;
diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c
index 9b3eb26..3dc6a39 100644
--- a/src/eap_server/eap_server_mschapv2.c
+++ b/src/eap_server/eap_server_mschapv2.c
@@ -64,6 +64,12 @@
return NULL;
data->state = CHALLENGE;
+ wpa_printf(MSG_DEBUG, "EAP-%sMSCHAPv2 init%s%s",
+ sm->eap_fast_mschapv2 ? "FAST-" : "",
+ sm->peer_challenge && sm->auth_challenge ?
+ " with preset challenges" : "",
+ sm->init_phase2 ? " for Phase 2" : "");
+
if (sm->auth_challenge) {
os_memcpy(data->auth_challenge, sm->auth_challenge,
CHALLENGE_LEN);
@@ -542,6 +548,7 @@
{
struct eap_mschapv2_data *data = priv;
u8 *key;
+ bool first_is_send;
if (data->state != SUCCESS || !data->master_key_valid)
return NULL;
@@ -550,11 +557,26 @@
key = os_malloc(*len);
if (key == NULL)
return NULL;
+ /*
+ * [MS-CHAP], 3.1.5.1 (Master Session Key (MSK) Derivation
+ * MSK = MasterReceiveKey + MasterSendKey + 32 bytes zeros (padding)
+ * On an Authenticator:
+ * MS-MPPE-Recv-Key = MasterReceiveKey
+ * MS-MPPE-Send-Key = MasterSendKey
+ *
+ * RFC 5422, 3.2.3 (Authenticating Using EAP-FAST-MSCHAPv2)
+ * MSK = MasterSendKey + MasterReceiveKey
+ * (i.e., reverse order and no padding)
+ *
+ * On Peer, EAP-MSCHAPv2 starts with Send key and EAP-FAST-MSCHAPv2
+ * starts with Receive key.
+ */
+ first_is_send = sm->eap_fast_mschapv2;
/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */
- if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0,
- 1) < 0 ||
+ if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN,
+ first_is_send, 1) < 0 ||
get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
- MSCHAPV2_KEY_LEN, 1, 1) < 0) {
+ MSCHAPV2_KEY_LEN, !first_is_send, 1) < 0) {
os_free(key);
return NULL;
}
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
index 81cddca..afafaef 100644
--- a/src/eap_server/eap_server_pwd.c
+++ b/src/eap_server/eap_server_pwd.c
@@ -293,7 +293,10 @@
/* We send the element as (x,y) followed by the scalar */
element = wpabuf_put(data->outbuf, 2 * prime_len);
scalar = wpabuf_put(data->outbuf, order_len);
- crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len);
+ if (crypto_bignum_to_bin(data->my_scalar, scalar, order_len,
+ order_len) < 0)
+ goto fin;
+
if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element,
element + prime_len) < 0) {
wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment "
@@ -349,7 +352,9 @@
*
* First is k
*/
- crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len);
+ if (crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len) < 0)
+ goto fin;
+
eap_pwd_h_update(hash, cruft, prime_len);
/* server element: x, y */
@@ -362,7 +367,10 @@
eap_pwd_h_update(hash, cruft, prime_len * 2);
/* server scalar */
- crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len);
+ if (crypto_bignum_to_bin(data->my_scalar, cruft, order_len,
+ order_len) < 0)
+ goto fin;
+
eap_pwd_h_update(hash, cruft, order_len);
/* peer element: x, y */
@@ -375,7 +383,10 @@
eap_pwd_h_update(hash, cruft, prime_len * 2);
/* peer scalar */
- crypto_bignum_to_bin(data->peer_scalar, cruft, order_len, order_len);
+ if (crypto_bignum_to_bin(data->peer_scalar, cruft, order_len,
+ order_len) < 0)
+ goto fin;
+
eap_pwd_h_update(hash, cruft, order_len);
/* ciphersuite */
@@ -785,7 +796,9 @@
goto fin;
/* k */
- crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len);
+ if (crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len) < 0)
+ goto fin;
+
eap_pwd_h_update(hash, cruft, prime_len);
/* peer element: x, y */
@@ -798,7 +811,10 @@
eap_pwd_h_update(hash, cruft, prime_len * 2);
/* peer scalar */
- crypto_bignum_to_bin(data->peer_scalar, cruft, order_len, order_len);
+ if (crypto_bignum_to_bin(data->peer_scalar, cruft, order_len,
+ order_len) < 0)
+ goto fin;
+
eap_pwd_h_update(hash, cruft, order_len);
/* server element: x, y */
@@ -811,7 +827,10 @@
eap_pwd_h_update(hash, cruft, prime_len * 2);
/* server scalar */
- crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len);
+ if (crypto_bignum_to_bin(data->my_scalar, cruft, order_len,
+ order_len) < 0)
+ goto fin;
+
eap_pwd_h_update(hash, cruft, order_len);
/* ciphersuite */
diff --git a/src/eap_server/eap_server_teap.c b/src/eap_server/eap_server_teap.c
index 691b44a..e32c6e4 100644
--- a/src/eap_server/eap_server_teap.c
+++ b/src/eap_server/eap_server_teap.c
@@ -74,11 +74,15 @@
enum teap_error_codes error_code;
enum teap_identity_types cur_id_type;
+
+ bool check_crypto_binding;
};
static int eap_teap_process_phase2_start(struct eap_sm *sm,
struct eap_teap_data *data);
+static int eap_teap_phase2_init(struct eap_sm *sm, struct eap_teap_data *data,
+ int vendor, enum eap_type eap_type);
static const char * eap_teap_state_txt(int state)
@@ -704,6 +708,8 @@
wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: MSK Compound MAC",
cb->msk_compound_mac, sizeof(cb->msk_compound_mac));
+ data->check_crypto_binding = true;
+
return buf;
}
@@ -889,6 +895,7 @@
struct eap_teap_data *data = priv;
struct wpabuf *req = NULL;
int piggyback = 0;
+ bool move_to_method = true;
if (data->ssl.state == FRAG_ACK) {
return eap_server_tls_build_ack(id, EAP_TYPE_TEAP,
@@ -940,6 +947,21 @@
break;
case CRYPTO_BINDING:
req = eap_teap_build_crypto_binding(sm, data);
+ if (req && sm->cfg->eap_teap_auth == 0 &&
+ data->inner_eap_not_done &&
+ !data->phase2_method &&
+ sm->cfg->eap_teap_method_sequence == 0) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Continue with inner EAP authentication for second credential (optimized)");
+ eap_teap_state(data, PHASE2_ID);
+ if (eap_teap_phase2_init(sm, data, EAP_VENDOR_IETF,
+ EAP_TYPE_IDENTITY) < 0) {
+ eap_teap_state(data, FAILURE);
+ wpabuf_free(req);
+ return NULL;
+ }
+ move_to_method = false;
+ }
if (data->phase2_method) {
/*
* Include the start of the next EAP method in the
@@ -950,7 +972,8 @@
eap = eap_teap_build_phase2_req(sm, data, id);
req = wpabuf_concat(req, eap);
- eap_teap_state(data, PHASE2_METHOD);
+ if (move_to_method)
+ eap_teap_state(data, PHASE2_METHOD);
}
break;
case REQUEST_PAC:
@@ -1008,6 +1031,13 @@
if (!data->phase2_method)
return -1;
+ /* While RFC 7170 does not describe this, EAP-TEAP has been deployed
+ * with implementations that use the EAP-FAST-MSCHAPv2, instead of the
+ * EAP-MSCHAPv2, way of deriving the MSK for IMSK. Use that design here
+ * to interoperate.
+ */
+ sm->eap_fast_mschapv2 = true;
+
sm->init_phase2 = 1;
data->phase2_priv = data->phase2_method->init(sm);
sm->init_phase2 = 0;
@@ -1503,7 +1533,8 @@
struct wpabuf *in_data)
{
struct eap_teap_tlv_parse tlv;
- int check_crypto_binding = data->state == CRYPTO_BINDING;
+ bool check_crypto_binding = data->state == CRYPTO_BINDING ||
+ data->check_crypto_binding;
if (eap_teap_parse_tlvs(in_data, &tlv) < 0) {
wpa_printf(MSG_DEBUG,
@@ -1586,6 +1617,7 @@
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Valid Crypto-Binding TLV received");
+ data->check_crypto_binding = false;
if (data->final_result) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Authentication completed successfully");
@@ -1664,7 +1696,8 @@
"EAP-TEAP: Continue with basic password authentication for second credential");
eap_teap_state(data, PHASE2_BASIC_AUTH);
} else if (check_crypto_binding && data->state == CRYPTO_BINDING &&
- sm->cfg->eap_teap_auth == 0 && data->inner_eap_not_done) {
+ sm->cfg->eap_teap_auth == 0 && data->inner_eap_not_done &&
+ sm->cfg->eap_teap_method_sequence == 1) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Continue with inner EAP authentication for second credential");
eap_teap_state(data, PHASE2_ID);
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index b7fde1b..e1f72ac 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -441,6 +441,7 @@
return NULL;
dl_list_add(&p2p->devices, &dev->list);
os_memcpy(dev->info.p2p_device_addr, addr, ETH_ALEN);
+ dev->support_6ghz = false;
return dev;
}
@@ -611,6 +612,8 @@
dev->info.group_capab = msg->capability[1];
}
+ p2p_update_peer_6ghz_capab(dev, msg);
+
if (msg->ext_listen_timing) {
dev->ext_listen_period = WPA_GET_LE16(msg->ext_listen_timing);
dev->ext_listen_interval =
@@ -634,6 +637,15 @@
}
+void p2p_update_peer_6ghz_capab(struct p2p_device *dev,
+ const struct p2p_message *msg)
+{
+ if (msg->capability &&
+ (msg->capability[0] & P2P_DEV_CAPAB_6GHZ_BAND_CAPABLE))
+ dev->support_6ghz = true;
+}
+
+
static void p2p_update_peer_vendor_elems(struct p2p_device *dev, const u8 *ies,
size_t ies_len)
{
@@ -2085,6 +2097,7 @@
}
}
+ p2p_update_peer_6ghz_capab(dev, &msg);
os_get_reltime(&dev->last_seen);
p2p_parse_free(&msg);
return; /* already known */
@@ -5606,7 +5619,7 @@
if (!dev)
return false;
- return !!(dev->info.dev_capab & P2P_DEV_CAPAB_6GHZ_BAND_CAPABLE);
+ return dev->support_6ghz;
}
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index e3d704b..afd8969 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -904,6 +904,9 @@
p2p_add_dev_info(p2p, sa, dev, &msg);
}
+ if (dev)
+ p2p_update_peer_6ghz_capab(dev, &msg);
+
if (p2p->go_neg_peer && p2p->go_neg_peer == dev)
eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
@@ -1230,6 +1233,7 @@
return;
}
dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
+ p2p_update_peer_6ghz_capab(dev, &msg);
if (msg.dialog_token != dev->dialog_token) {
p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)",
@@ -1525,6 +1529,8 @@
return;
}
+ p2p_update_peer_6ghz_capab(dev, &msg);
+
if (dev->go_state == REMOTE_GO && msg.group_id) {
/* Store SSID for Provisioning step */
p2p->ssid_len = msg.group_id_len - ETH_ALEN;
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index d8dc6f7..235467e 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -150,6 +150,7 @@
struct wpabuf *go_neg_conf;
int sd_pending_bcast_queries;
+ bool support_6ghz;
};
struct p2p_sd_query {
@@ -875,6 +876,8 @@
struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p,
const u8 *addr,
struct p2p_message *msg);
+void p2p_update_peer_6ghz_capab(struct p2p_device *dev,
+ const struct p2p_message *msg);
void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
struct p2p_device *dev, struct p2p_message *msg);
int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
index 1a78e14..f75cee8 100644
--- a/src/p2p/p2p_pd.c
+++ b/src/p2p/p2p_pd.c
@@ -365,7 +365,7 @@
(conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER))) {
bool is_6ghz_capab;
- is_6ghz_capab = is_p2p_6ghz_capable(p2p) &&
+ is_6ghz_capab = is_p2p_6ghz_capable(p2p) && dev &&
p2p_is_peer_6ghz_capab(
p2p, dev->info.p2p_device_addr);
p2p_buf_add_channel_list(buf, p2p->cfg->country,
@@ -619,6 +619,8 @@
dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
}
+ p2p_update_peer_6ghz_capab(dev, &msg);
+
if (!msg.adv_id) {
allowed_config_methods |= WPS_CONFIG_PUSHBUTTON;
if (!(msg.wps_config_methods & allowed_config_methods)) {
@@ -1367,6 +1369,8 @@
dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
}
+ p2p_update_peer_6ghz_capab(dev, &msg);
+
if (dev->dialog_token != msg.dialog_token) {
p2p_dbg(p2p, "Ignore Provision Discovery Response with unexpected Dialog Token %u (expected %u)",
msg.dialog_token, dev->dialog_token);
diff --git a/src/pasn/Makefile b/src/pasn/Makefile
new file mode 100644
index 0000000..a5b2c6b
--- /dev/null
+++ b/src/pasn/Makefile
@@ -0,0 +1,16 @@
+CFLAGS += -DCONFIG_SAE
+CFLAGS += -DCONFIG_FILS
+CFLAGS += -DIEEE8021X_EAPOL
+CFLAGS += -DCONFIG_IEEE80211R
+CFLAGS += -DCONFIG_TESTING_OPTIONS
+CFLAGS += -DCONFIG_SAE_PK
+CFLAGS += -DCONFIG_SHA256
+CFLAGS += -DCONFIG_SHA384
+CFLAGS += -DCONFIG_SHA512
+CFLAGS += -DCONFIG_PASN
+
+LIB_OBJS= \
+ pasn_initiator.o \
+ pasn_responder.o
+
+include ../lib.rules
diff --git a/src/pasn/pasn_common.h b/src/pasn/pasn_common.h
new file mode 100644
index 0000000..9c2f397
--- /dev/null
+++ b/src/pasn/pasn_common.h
@@ -0,0 +1,184 @@
+/*
+ * PASN info for initiator and responder
+ *
+ * Copyright (C) 2019, Intel Corporation
+ * Copyright (c) 2022, Jouni Malinen <j@w1.fi>
+ * Copyright (C) 2022, Qualcomm Innovation Center, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PASN_COMMON_H
+#define PASN_COMMON_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef CONFIG_PASN
+
+enum pasn_fils_state {
+ PASN_FILS_STATE_NONE = 0,
+ PASN_FILS_STATE_PENDING_AS,
+ PASN_FILS_STATE_COMPLETE
+};
+
+struct pasn_fils {
+ u8 state;
+ u8 nonce[FILS_NONCE_LEN];
+ u8 anonce[FILS_NONCE_LEN];
+ u8 session[FILS_SESSION_LEN];
+ u8 erp_pmkid[PMKID_LEN];
+ bool completed;
+ struct wpabuf *erp_resp;
+};
+
+struct pasn_data {
+ int akmp;
+ int cipher;
+ u16 group;
+ bool secure_ltf;
+ int freq;
+ size_t kdk_len;
+
+ u8 trans_seq;
+ u8 status;
+
+ u8 own_addr[ETH_ALEN];
+ u8 peer_addr[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ size_t pmk_len;
+ u8 pmk[PMK_LEN_MAX];
+ bool using_pmksa;
+
+ u8 hash[SHA384_MAC_LEN];
+
+ struct wpabuf *beacon_rsne_rsnxe;
+ struct wpa_ptk ptk;
+ struct crypto_ecdh *ecdh;
+
+ struct wpabuf *comeback;
+ u16 comeback_after;
+
+#ifdef CONFIG_SAE
+ struct sae_data sae;
+ struct sae_pt *pt;
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_FILS
+ bool fils_eapol;
+ bool fils_wd_valid;
+ struct pasn_fils fils;
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_IEEE80211R
+ u8 pmk_r1[PMK_LEN_MAX];
+ size_t pmk_r1_len;
+ u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+#endif /* CONFIG_IEEE80211R */
+ /* Note that this pointers to RSN PMKSA cache are actually defined
+ * differently for the PASN initiator (using RSN Supplicant
+ * implementation) and PASN responser (using RSN Authenticator
+ * implementation). Functions cannot be mixed between those cases. */
+ struct rsn_pmksa_cache *pmksa;
+ struct rsn_pmksa_cache_entry *pmksa_entry;
+ struct eapol_sm *eapol;
+ int fast_reauth;
+#ifdef CONFIG_TESTING_OPTIONS
+ int corrupt_mic;
+#endif /* CONFIG_TESTING_OPTIONS */
+ void *cb_ctx;
+ u16 rsnxe_capab;
+ int network_id;
+
+ u8 wrapped_data_format;
+ struct wpabuf *secret;
+
+ /* Reponder */
+ int wpa_key_mgmt;
+ int rsn_pairwise;
+ bool derive_kdk;
+ const char *password;
+ int disable_pmksa_caching;
+ int *pasn_groups;
+ struct wpabuf *wrapped_data;
+ int use_anti_clogging;
+ const u8 *rsn_ie;
+ const u8 *rsnxe_ie;
+ size_t rsn_ie_len;
+
+ u8 *comeback_key;
+ struct os_reltime last_comeback_key_update;
+ u16 comeback_idx;
+ u16 *comeback_pending_idx;
+
+ bool custom_pmkid_valid;
+ u8 custom_pmkid[PMKID_LEN];
+
+ /**
+ * Extra elements to add into Authentication frames. These can be used,
+ * e.g., for Wi-Fi Aware use cases.
+ */
+ const u8 *extra_ies;
+ size_t extra_ies_len;
+
+ /**
+ * send_mgmt - Function handler to transmit a Management frame
+ * @ctx: Callback context from cb_ctx
+ * @frame_buf : Frame to transmit
+ * @frame_len: Length of frame to transmit
+ * @freq: Frequency in MHz for the channel on which to transmit
+ * @wait_dur: How many milliseconds to wait for a response frame
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*send_mgmt)(void *ctx, const u8 *data, size_t data_len, int noack,
+ unsigned int freq, unsigned int wait);
+ /**
+ * validate_custom_pmkid - Handler to validate vendor specific PMKID
+ * @ctx: Callback context from cb_ctx
+ * @addr : MAC address of the peer
+ * @pmkid: Custom PMKID
+ * Returns: 0 on success (valid PMKID), -1 on failure
+ */
+ int (*validate_custom_pmkid)(void *ctx, const u8 *addr,
+ const u8 *pmkid);
+};
+
+/* Initiator */
+
+void wpa_pasn_reset(struct pasn_data *pasn);
+int wpas_pasn_start(struct pasn_data *pasn, const u8 *own_addr,
+ const u8 *peer_addr, const u8 *bssid,
+ int akmp, int cipher, u16 group,
+ int freq, const u8 *beacon_rsne, u8 beacon_rsne_len,
+ const u8 *beacon_rsnxe, u8 beacon_rsnxe_len,
+ const struct wpabuf *comeback);
+int wpa_pasn_verify(struct pasn_data *pasn, const u8 *own_addr,
+ const u8 *peer_addr, const u8 *bssid,
+ int akmp, int cipher, u16 group,
+ int freq, const u8 *beacon_rsne, u8 beacon_rsne_len,
+ const u8 *beacon_rsnxe, u8 beacon_rsnxe_len,
+ const struct wpabuf *comeback);
+int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len,
+ struct wpa_pasn_params_data *pasn_params);
+int wpa_pasn_auth_tx_status(struct pasn_data *pasn,
+ const u8 *data, size_t data_len, u8 acked);
+
+/* Responder */
+int handle_auth_pasn_1(struct pasn_data *pasn,
+ const u8 *own_addr, const u8 *peer_addr,
+ const struct ieee80211_mgmt *mgmt, size_t len);
+int handle_auth_pasn_3(struct pasn_data *pasn, const u8 *own_addr,
+ const u8 *peer_addr,
+ const struct ieee80211_mgmt *mgmt, size_t len);
+int handle_auth_pasn_resp(struct pasn_data *pasn, const u8 *own_addr,
+ const u8 *peer_addr,
+ struct rsn_pmksa_cache_entry *pmksa, u16 status);
+
+#endif /* CONFIG_PASN */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* PASN_COMMON_H */
diff --git a/src/pasn/pasn_initiator.c b/src/pasn/pasn_initiator.c
new file mode 100644
index 0000000..1f9a508
--- /dev/null
+++ b/src/pasn/pasn_initiator.c
@@ -0,0 +1,1393 @@
+/*
+ * PASN initiator processing
+ *
+ * Copyright (C) 2019, Intel Corporation
+ * Copyright (C) 2022, Qualcomm Innovation Center, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/wpa_common.h"
+#include "common/sae.h"
+#include "common/ieee802_11_common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/dragonfly.h"
+#include "crypto/sha384.h"
+#include "crypto/crypto.h"
+#include "crypto/random.h"
+#include "eap_common/eap_defs.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
+#include "rsn_supp/pmksa_cache.h"
+#include "pasn_common.h"
+
+
+#ifdef CONFIG_SAE
+
+static struct wpabuf * wpas_pasn_wd_sae_commit(struct pasn_data *pasn)
+{
+ struct wpabuf *buf = NULL;
+ int ret;
+
+ ret = sae_set_group(&pasn->sae, pasn->group);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to set SAE group");
+ return NULL;
+ }
+
+ ret = sae_prepare_commit_pt(&pasn->sae, pasn->pt,
+ pasn->own_addr, pasn->peer_addr,
+ NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit");
+ return NULL;
+ }
+
+ /* Need to add the entire Authentication frame body */
+ buf = wpabuf_alloc(6 + SAE_COMMIT_MAX_LEN);
+ if (!buf) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
+ return NULL;
+ }
+
+ wpabuf_put_le16(buf, WLAN_AUTH_SAE);
+ wpabuf_put_le16(buf, 1);
+ wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT);
+
+ sae_write_commit(&pasn->sae, buf, NULL, 0);
+ pasn->sae.state = SAE_COMMITTED;
+
+ return buf;
+}
+
+
+static int wpas_pasn_wd_sae_rx(struct pasn_data *pasn, struct wpabuf *wd)
+{
+ const u8 *data;
+ size_t buf_len;
+ u16 len, 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);
+
+ /* first handle the commit message */
+ if (buf_len < 2) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short (commit)");
+ return -1;
+ }
+
+ len = WPA_GET_LE16(data);
+ if (len < 6 || buf_len - 2 < len) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short for commit");
+ return -1;
+ }
+
+ buf_len -= 2;
+ data += 2;
+
+ 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);
+
+ if (alg != WLAN_AUTH_SAE || seq != 1 ||
+ status != WLAN_STATUS_SAE_HASH_TO_ELEMENT) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE: dropping peer commit");
+ return -1;
+ }
+
+ res = sae_parse_commit(&pasn->sae, data + 6, len - 6, NULL, 0, groups,
+ 1, NULL);
+ if (res != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE failed parsing 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;
+ }
+
+ buf_len -= len;
+ data += len;
+
+ /* Handle the confirm message */
+ if (buf_len < 2) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short (confirm)");
+ return -1;
+ }
+
+ len = WPA_GET_LE16(data);
+ if (len < 6 || buf_len - 2 < len) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short for confirm");
+ return -1;
+ }
+
+ buf_len -= 2;
+ data += 2;
+
+ 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, len - 6, NULL);
+ if (res != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE failed checking confirm");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "PASN: SAE completed successfully");
+ pasn->sae.state = SAE_ACCEPTED;
+
+ return 0;
+}
+
+
+static struct wpabuf * wpas_pasn_wd_sae_confirm(struct pasn_data *pasn)
+{
+ struct wpabuf *buf = NULL;
+
+ /* Need to add the entire authentication frame body */
+ buf = wpabuf_alloc(6 + SAE_CONFIRM_MAX_LEN);
+ if (!buf) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
+ return NULL;
+ }
+
+ 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);
+ pasn->sae.state = SAE_CONFIRMED;
+
+ return buf;
+}
+
+#endif /* CONFIG_SAE */
+
+
+#ifdef CONFIG_FILS
+
+static struct wpabuf * wpas_pasn_fils_build_auth(struct pasn_data *pasn)
+{
+ struct wpabuf *buf = NULL;
+ struct wpabuf *erp_msg;
+ int ret;
+
+ erp_msg = eapol_sm_build_erp_reauth_start(pasn->eapol);
+ if (!erp_msg) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: FILS: ERP EAP-Initiate/Re-auth unavailable");
+ return NULL;
+ }
+
+ if (random_get_bytes(pasn->fils.nonce, FILS_NONCE_LEN) < 0 ||
+ random_get_bytes(pasn->fils.session, FILS_SESSION_LEN) < 0)
+ goto fail;
+
+ wpa_hexdump(MSG_DEBUG, "PASN: FILS: Nonce", pasn->fils.nonce,
+ FILS_NONCE_LEN);
+
+ wpa_hexdump(MSG_DEBUG, "PASN: FILS: Session", pasn->fils.session,
+ FILS_SESSION_LEN);
+
+ buf = wpabuf_alloc(1500);
+ if (!buf)
+ goto fail;
+
+ /* Add the authentication algorithm */
+ wpabuf_put_le16(buf, WLAN_AUTH_FILS_SK);
+
+ /* Authentication Transaction seq# */
+ wpabuf_put_le16(buf, 1);
+
+ /* 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, pasn->fils.nonce, 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, pasn->fils.session, FILS_SESSION_LEN);
+
+ /* Wrapped Data (ERP) */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(buf, 1 + wpabuf_len(erp_msg));
+ wpabuf_put_u8(buf, WLAN_EID_EXT_WRAPPED_DATA);
+ wpabuf_put_buf(buf, erp_msg);
+
+ /*
+ * Calculate pending PMKID here so that we do not need to maintain a
+ * copy of the EAP-Initiate/Reauth message.
+ */
+ ret = fils_pmkid_erp(pasn->akmp, wpabuf_head(erp_msg),
+ wpabuf_len(erp_msg),
+ pasn->fils.erp_pmkid);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to get ERP PMKID");
+ goto fail;
+ }
+
+ wpabuf_free(erp_msg);
+ erp_msg = NULL;
+
+ wpa_hexdump_buf(MSG_DEBUG, "PASN: FILS: Authentication frame", buf);
+ return buf;
+fail:
+ wpabuf_free(erp_msg);
+ wpabuf_free(buf);
+ return NULL;
+}
+
+
+static struct wpabuf * wpas_pasn_wd_fils_auth(struct pasn_data *pasn)
+{
+ wpa_printf(MSG_DEBUG, "PASN: FILS: wrapped data - completed=%u",
+ pasn->fils.completed);
+
+ /* Nothing to add as we are done */
+ if (pasn->fils.completed)
+ return NULL;
+
+ if (!pasn->fils_eapol) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: FILS: Missing Indication IE or PFS");
+ return NULL;
+ }
+
+ return wpas_pasn_fils_build_auth(pasn);
+}
+
+
+static int wpas_pasn_wd_fils_rx(struct pasn_data *pasn, struct wpabuf *wd)
+{
+ struct ieee802_11_elems elems;
+ struct wpa_ie_data rsne_data;
+ u8 rmsk[ERP_MAX_KEY_LEN];
+ size_t rmsk_len;
+ u8 anonce[FILS_NONCE_LEN];
+ const u8 *data;
+ size_t buf_len;
+ struct wpabuf *fils_wd = NULL;
+ u16 alg, seq, status;
+ int ret;
+
+ if (!wd)
+ return -1;
+
+ data = wpabuf_head(wd);
+ buf_len = wpabuf_len(wd);
+
+ wpa_hexdump(MSG_DEBUG, "PASN: FILS: Authentication frame len=%zu",
+ data, buf_len);
+
+ /* first handle the header */
+ if (buf_len < 6) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Buffer too short");
+ 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: commit: alg=%u, seq=%u, status=%u",
+ alg, seq, status);
+
+ if (alg != WLAN_AUTH_FILS_SK || seq != 2 ||
+ 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(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+ &rsne_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Failed parsing RSNE");
+ 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: ANonce", elems.fils_nonce,
+ FILS_NONCE_LEN);
+ os_memcpy(anonce, elems.fils_nonce, FILS_NONCE_LEN);
+
+ wpa_hexdump(MSG_DEBUG, "PASN: FILS: FILS Session", elems.fils_session,
+ FILS_SESSION_LEN);
+
+ if (os_memcmp(pasn->fils.session, elems.fils_session,
+ FILS_SESSION_LEN)) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Session mismatch");
+ return -1;
+ }
+
+ fils_wd = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION,
+ WLAN_EID_EXT_WRAPPED_DATA);
+
+ if (!fils_wd) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: FILS: Failed getting wrapped data");
+ return -1;
+ }
+
+ eapol_sm_process_erp_finish(pasn->eapol, wpabuf_head(fils_wd),
+ wpabuf_len(fils_wd));
+
+ wpabuf_free(fils_wd);
+ fils_wd = NULL;
+
+ if (eapol_sm_failed(pasn->eapol)) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: ERP finish failed");
+ return -1;
+ }
+
+ rmsk_len = ERP_MAX_KEY_LEN;
+ ret = eapol_sm_get_key(pasn->eapol, rmsk, rmsk_len);
+
+ if (ret == PMK_LEN) {
+ rmsk_len = PMK_LEN;
+ ret = eapol_sm_get_key(pasn->eapol, rmsk, rmsk_len);
+ }
+
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Failed getting RMSK");
+ return -1;
+ }
+
+ ret = fils_rmsk_to_pmk(pasn->akmp, rmsk, rmsk_len,
+ pasn->fils.nonce, anonce, NULL, 0,
+ pasn->pmk, &pasn->pmk_len);
+
+ forced_memzero(rmsk, sizeof(rmsk));
+
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to derive PMK");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "PASN: FILS: PMKID", pasn->fils.erp_pmkid,
+ PMKID_LEN);
+
+ wpa_printf(MSG_DEBUG, "PASN: FILS: ERP processing succeeded");
+
+ pasn->pmksa_entry = pmksa_cache_add(pasn->pmksa, pasn->pmk,
+ pasn->pmk_len, pasn->fils.erp_pmkid,
+ NULL, 0, pasn->peer_addr,
+ pasn->own_addr, NULL,
+ pasn->akmp, 0);
+
+ pasn->fils.completed = true;
+ return 0;
+}
+
+#endif /* CONFIG_FILS */
+
+
+static struct wpabuf * wpas_pasn_get_wrapped_data(struct pasn_data *pasn)
+{
+ if (pasn->using_pmksa)
+ return NULL;
+
+ switch (pasn->akmp) {
+ case WPA_KEY_MGMT_PASN:
+ /* no wrapped data */
+ return NULL;
+ case WPA_KEY_MGMT_SAE:
+#ifdef CONFIG_SAE
+ if (pasn->trans_seq == 0)
+ return wpas_pasn_wd_sae_commit(pasn);
+ if (pasn->trans_seq == 2)
+ return wpas_pasn_wd_sae_confirm(pasn);
+#endif /* CONFIG_SAE */
+ wpa_printf(MSG_ERROR,
+ "PASN: SAE: Cannot derive wrapped data");
+ return NULL;
+ case WPA_KEY_MGMT_FILS_SHA256:
+ case WPA_KEY_MGMT_FILS_SHA384:
+#ifdef CONFIG_FILS
+ return wpas_pasn_wd_fils_auth(pasn);
+#endif /* CONFIG_FILS */
+ case WPA_KEY_MGMT_FT_PSK:
+ case WPA_KEY_MGMT_FT_IEEE8021X:
+ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+ /*
+ * Wrapped data with these AKMs is optional and is only needed
+ * for further validation of FT security parameters. For now do
+ * not use them.
+ */
+ return NULL;
+ default:
+ wpa_printf(MSG_ERROR,
+ "PASN: TODO: Wrapped data for akmp=0x%x",
+ pasn->akmp);
+ return NULL;
+ }
+}
+
+
+static u8 wpas_pasn_get_wrapped_data_format(struct pasn_data *pasn)
+{
+ if (pasn->using_pmksa)
+ return WPA_PASN_WRAPPED_DATA_NO;
+
+ /* Note: Valid AKMP is expected to already be validated */
+ switch (pasn->akmp) {
+ case WPA_KEY_MGMT_SAE:
+ return WPA_PASN_WRAPPED_DATA_SAE;
+ case WPA_KEY_MGMT_FILS_SHA256:
+ case WPA_KEY_MGMT_FILS_SHA384:
+ return WPA_PASN_WRAPPED_DATA_FILS_SK;
+ case WPA_KEY_MGMT_FT_PSK:
+ case WPA_KEY_MGMT_FT_IEEE8021X:
+ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+ /*
+ * Wrapped data with these AKMs is optional and is only needed
+ * for further validation of FT security parameters. For now do
+ * not use them.
+ */
+ return WPA_PASN_WRAPPED_DATA_NO;
+ case WPA_KEY_MGMT_PASN:
+ default:
+ return WPA_PASN_WRAPPED_DATA_NO;
+ }
+}
+
+
+static struct wpabuf * wpas_pasn_build_auth_1(struct pasn_data *pasn,
+ const struct wpabuf *comeback,
+ bool verify)
+{
+ struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL;
+ const u8 *pmkid;
+ u8 wrapped_data;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "PASN: Building frame 1");
+
+ if (pasn->trans_seq)
+ return NULL;
+
+ buf = wpabuf_alloc(1500);
+ if (!buf)
+ goto fail;
+
+ /* Get public key */
+ pubkey = crypto_ecdh_get_pubkey(pasn->ecdh, 0);
+ pubkey = wpabuf_zeropad(pubkey, crypto_ecdh_prime_len(pasn->ecdh));
+ if (!pubkey) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to get pubkey");
+ goto fail;
+ }
+
+ wrapped_data = wpas_pasn_get_wrapped_data_format(pasn);
+
+ wpa_pasn_build_auth_header(buf, pasn->bssid,
+ pasn->own_addr, pasn->peer_addr,
+ pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
+
+ pmkid = NULL;
+ if (wpa_key_mgmt_ft(pasn->akmp)) {
+#ifdef CONFIG_IEEE80211R
+ pmkid = pasn->pmk_r1_name;
+#else /* CONFIG_IEEE80211R */
+ goto fail;
+#endif /* CONFIG_IEEE80211R */
+ } else if (wrapped_data != WPA_PASN_WRAPPED_DATA_NO) {
+ struct rsn_pmksa_cache_entry *pmksa;
+
+ pmksa = pmksa_cache_get(pasn->pmksa, pasn->peer_addr,
+ pasn->own_addr, NULL, NULL, pasn->akmp);
+ if (pmksa && pasn->custom_pmkid_valid)
+ pmkid = pasn->custom_pmkid;
+ else if (pmksa)
+ pmkid = pmksa->pmkid;
+
+ /*
+ * Note: Even when PMKSA is available, also add wrapped data as
+ * it is possible that the PMKID is no longer valid at the AP.
+ */
+ if (!verify)
+ wrapped_data_buf = wpas_pasn_get_wrapped_data(pasn);
+ }
+
+ if (wpa_pasn_add_rsne(buf, pmkid, pasn->akmp, pasn->cipher) < 0)
+ goto fail;
+
+ if (!wrapped_data_buf)
+ wrapped_data = WPA_PASN_WRAPPED_DATA_NO;
+
+ wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data,
+ pubkey, true, comeback, -1);
+
+ if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0)
+ goto fail;
+
+ wpa_pasn_add_rsnxe(buf, pasn->rsnxe_capab);
+
+ wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len);
+
+ ret = pasn_auth_frame_hash(pasn->akmp, pasn->cipher,
+ wpabuf_head_u8(buf) + IEEE80211_HDRLEN,
+ wpabuf_len(buf) - IEEE80211_HDRLEN,
+ pasn->hash);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash");
+ goto fail;
+ }
+
+ pasn->trans_seq++;
+
+ wpabuf_free(wrapped_data_buf);
+ wpabuf_free(pubkey);
+
+ wpa_printf(MSG_DEBUG, "PASN: Frame 1: Success");
+ return buf;
+fail:
+ pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ wpabuf_free(wrapped_data_buf);
+ wpabuf_free(pubkey);
+ wpabuf_free(buf);
+ return NULL;
+}
+
+
+static struct wpabuf * wpas_pasn_build_auth_3(struct pasn_data *pasn)
+{
+ struct wpabuf *buf, *wrapped_data_buf = NULL;
+ u8 mic[WPA_PASN_MAX_MIC_LEN];
+ u8 mic_len, data_len;
+ const u8 *data;
+ u8 *ptr;
+ u8 wrapped_data;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "PASN: Building frame 3");
+
+ if (pasn->trans_seq != 2)
+ return NULL;
+
+ buf = wpabuf_alloc(1500);
+ if (!buf)
+ goto fail;
+
+ wrapped_data = wpas_pasn_get_wrapped_data_format(pasn);
+
+ wpa_pasn_build_auth_header(buf, pasn->bssid,
+ pasn->own_addr, pasn->peer_addr,
+ pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
+
+ wrapped_data_buf = wpas_pasn_get_wrapped_data(pasn);
+
+ if (!wrapped_data_buf)
+ wrapped_data = WPA_PASN_WRAPPED_DATA_NO;
+
+ wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data,
+ NULL, false, NULL, -1);
+
+ if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0)
+ goto fail;
+ wpabuf_free(wrapped_data_buf);
+ wrapped_data_buf = NULL;
+
+ /* Add the MIC */
+ mic_len = pasn_mic_len(pasn->akmp, 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);
+
+ data = wpabuf_head_u8(buf) + IEEE80211_HDRLEN;
+ data_len = wpabuf_len(buf) - IEEE80211_HDRLEN;
+
+ ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
+ pasn->own_addr, pasn->peer_addr,
+ pasn->hash, mic_len * 2, data, data_len, mic);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: frame 3: Failed MIC calculation");
+ goto fail;
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (pasn->corrupt_mic) {
+ wpa_printf(MSG_DEBUG, "PASN: frame 3: Corrupt MIC");
+ mic[0] = ~mic[0];
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ os_memcpy(ptr, mic, mic_len);
+
+ pasn->trans_seq++;
+
+ wpa_printf(MSG_DEBUG, "PASN: frame 3: Success");
+ return buf;
+fail:
+ pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ wpabuf_free(wrapped_data_buf);
+ wpabuf_free(buf);
+ return NULL;
+}
+
+
+void wpa_pasn_reset(struct pasn_data *pasn)
+{
+ wpa_printf(MSG_DEBUG, "PASN: Reset");
+
+ crypto_ecdh_deinit(pasn->ecdh);
+ pasn->ecdh = NULL;
+
+
+ pasn->akmp = 0;
+ pasn->cipher = 0;
+ pasn->group = 0;
+ pasn->trans_seq = 0;
+ pasn->pmk_len = 0;
+ pasn->using_pmksa = false;
+
+ forced_memzero(pasn->pmk, sizeof(pasn->pmk));
+ forced_memzero(&pasn->ptk, sizeof(pasn->ptk));
+ forced_memzero(&pasn->hash, sizeof(pasn->hash));
+
+ wpabuf_free(pasn->beacon_rsne_rsnxe);
+ pasn->beacon_rsne_rsnxe = NULL;
+
+ wpabuf_free(pasn->comeback);
+ pasn->comeback = NULL;
+ pasn->comeback_after = 0;
+
+#ifdef CONFIG_SAE
+ sae_clear_data(&pasn->sae);
+ if (pasn->pt) {
+ sae_deinit_pt(pasn->pt);
+ pasn->pt = NULL;
+ }
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_FILS
+ pasn->fils_eapol = false;
+ os_memset(&pasn->fils, 0, sizeof(pasn->fils));
+#endif /* CONFIG_FILS*/
+
+#ifdef CONFIG_IEEE80211R
+ forced_memzero(pasn->pmk_r1, sizeof(pasn->pmk_r1));
+ pasn->pmk_r1_len = 0;
+ os_memset(pasn->pmk_r1_name, 0, sizeof(pasn->pmk_r1_name));
+#endif /* CONFIG_IEEE80211R */
+ pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ pasn->pmksa_entry = NULL;
+#ifdef CONFIG_TESTING_OPTIONS
+ pasn->corrupt_mic = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+ pasn->network_id = 0;
+ pasn->derive_kdk = false;
+ pasn->rsn_ie = NULL;
+ pasn->rsn_ie_len = 0;
+ pasn->rsnxe_ie = NULL;
+ pasn->custom_pmkid_valid = false;
+}
+
+
+static int wpas_pasn_set_pmk(struct pasn_data *pasn,
+ struct wpa_ie_data *rsn_data,
+ struct wpa_pasn_params_data *pasn_data,
+ struct wpabuf *wrapped_data)
+{
+ static const u8 pasn_default_pmk[] = {'P', 'M', 'K', 'z'};
+
+ os_memset(pasn->pmk, 0, sizeof(pasn->pmk));
+ pasn->pmk_len = 0;
+
+ if (pasn->akmp == WPA_KEY_MGMT_PASN) {
+ wpa_printf(MSG_DEBUG, "PASN: Using default PMK");
+
+ pasn->pmk_len = WPA_PASN_PMK_LEN;
+ os_memcpy(pasn->pmk, pasn_default_pmk,
+ sizeof(pasn_default_pmk));
+ return 0;
+ }
+
+ if (wpa_key_mgmt_ft(pasn->akmp)) {
+#ifdef CONFIG_IEEE80211R
+ wpa_printf(MSG_DEBUG, "PASN: FT: Using PMK-R1");
+ pasn->pmk_len = pasn->pmk_r1_len;
+ os_memcpy(pasn->pmk, pasn->pmk_r1, pasn->pmk_r1_len);
+ pasn->using_pmksa = true;
+ return 0;
+#else /* CONFIG_IEEE80211R */
+ wpa_printf(MSG_DEBUG, "PASN: FT: Not supported");
+ return -1;
+#endif /* CONFIG_IEEE80211R */
+ }
+
+ if (rsn_data->num_pmkid) {
+ int ret;
+ struct rsn_pmksa_cache_entry *pmksa;
+ const u8 *pmkid = NULL;
+
+ if (pasn->custom_pmkid_valid) {
+ ret = pasn->validate_custom_pmkid(pasn->cb_ctx,
+ pasn->peer_addr,
+ rsn_data->pmkid);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed custom PMKID validation");
+ return -1;
+ }
+ } else {
+ pmkid = rsn_data->pmkid;
+ }
+
+ pmksa = pmksa_cache_get(pasn->pmksa, pasn->peer_addr,
+ pasn->own_addr,
+ pmkid, NULL, pasn->akmp);
+ if (pmksa) {
+ wpa_printf(MSG_DEBUG, "PASN: Using PMKSA");
+
+ pasn->pmk_len = pmksa->pmk_len;
+ os_memcpy(pasn->pmk, pmksa->pmk, pmksa->pmk_len);
+ pasn->using_pmksa = true;
+
+ return 0;
+ }
+ }
+
+#ifdef CONFIG_SAE
+ if (pasn->akmp == WPA_KEY_MGMT_SAE) {
+ int ret;
+
+ ret = wpas_pasn_wd_sae_rx(pasn, wrapped_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed processing SAE wrapped data");
+ pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "PASN: Success deriving PMK with SAE");
+ pasn->pmk_len = PMK_LEN;
+ os_memcpy(pasn->pmk, pasn->sae.pmk, PMK_LEN);
+
+ pasn->pmksa_entry = pmksa_cache_add(pasn->pmksa, pasn->pmk,
+ pasn->pmk_len,
+ pasn->sae.pmkid,
+ NULL, 0, pasn->peer_addr,
+ pasn->own_addr, NULL,
+ pasn->akmp, 0);
+ return 0;
+ }
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_FILS
+ if (pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
+ pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
+ int ret;
+
+ ret = wpas_pasn_wd_fils_rx(pasn, wrapped_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed processing FILS wrapped data");
+ pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ return -1;
+ }
+
+ return 0;
+ }
+#endif /* CONFIG_FILS */
+
+ /* TODO: Derive PMK based on wrapped data */
+ wpa_printf(MSG_DEBUG, "PASN: Missing implementation to derive PMK");
+ pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ return -1;
+}
+
+
+static int wpas_pasn_send_auth_1(struct pasn_data *pasn, const u8 *own_addr,
+ const u8 *peer_addr, const u8 *bssid, int akmp,
+ int cipher, u16 group, int freq,
+ const u8 *beacon_rsne, u8 beacon_rsne_len,
+ const u8 *beacon_rsnxe, u8 beacon_rsnxe_len,
+ const struct wpabuf *comeback, bool verify)
+{
+ struct wpabuf *frame;
+ int ret;
+
+ pasn->ecdh = crypto_ecdh_init(group);
+ if (!pasn->ecdh) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to init ECDH");
+ goto fail;
+ }
+
+ if (beacon_rsne && beacon_rsne_len) {
+ pasn->beacon_rsne_rsnxe = wpabuf_alloc(beacon_rsne_len +
+ beacon_rsnxe_len);
+ if (!pasn->beacon_rsne_rsnxe) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed storing beacon RSNE/RSNXE");
+ goto fail;
+ }
+
+ wpabuf_put_data(pasn->beacon_rsne_rsnxe, beacon_rsne,
+ beacon_rsne_len);
+ if (beacon_rsnxe && beacon_rsnxe_len)
+ wpabuf_put_data(pasn->beacon_rsne_rsnxe, beacon_rsnxe,
+ beacon_rsnxe_len);
+ }
+
+ pasn->akmp = akmp;
+ pasn->cipher = cipher;
+ pasn->group = group;
+ pasn->freq = freq;
+
+ os_memcpy(pasn->own_addr, own_addr, ETH_ALEN);
+ os_memcpy(pasn->peer_addr, peer_addr, ETH_ALEN);
+ os_memcpy(pasn->bssid, bssid, ETH_ALEN);
+
+ wpa_printf(MSG_DEBUG,
+ "PASN: Init%s: " MACSTR " akmp=0x%x, cipher=0x%x, group=%u",
+ verify ? " (verify)" : "",
+ MAC2STR(pasn->peer_addr), pasn->akmp, pasn->cipher,
+ pasn->group);
+
+ frame = wpas_pasn_build_auth_1(pasn, comeback, verify);
+ if (!frame) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed building 1st auth frame");
+ goto fail;
+ }
+
+ ret = pasn->send_mgmt(pasn->cb_ctx,
+ wpabuf_head(frame), wpabuf_len(frame), 0,
+ pasn->freq, 1000);
+
+ wpabuf_free(frame);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed sending 1st auth frame");
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ return -1;
+}
+
+
+int wpas_pasn_start(struct pasn_data *pasn, const u8 *own_addr,
+ const u8 *peer_addr, const u8 *bssid,
+ int akmp, int cipher, u16 group,
+ int freq, const u8 *beacon_rsne, u8 beacon_rsne_len,
+ const u8 *beacon_rsnxe, u8 beacon_rsnxe_len,
+ const struct wpabuf *comeback)
+{
+ /* TODO: Currently support only ECC groups */
+ if (!dragonfly_suitable_group(group, 1)) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Reject unsuitable group %u", group);
+ return -1;
+ }
+
+ switch (akmp) {
+ case WPA_KEY_MGMT_PASN:
+ break;
+#ifdef CONFIG_SAE
+ case WPA_KEY_MGMT_SAE:
+
+ if (beacon_rsnxe &&
+ !ieee802_11_rsnx_capab(beacon_rsnxe,
+ WLAN_RSNX_CAPAB_SAE_H2E)) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: AP does not support SAE H2E");
+ return -1;
+ }
+
+ pasn->sae.state = SAE_NOTHING;
+ pasn->sae.send_confirm = 0;
+ break;
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+ case WPA_KEY_MGMT_FILS_SHA256:
+ case WPA_KEY_MGMT_FILS_SHA384:
+ break;
+#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:
+ break;
+#endif /* CONFIG_IEEE80211R */
+ default:
+ wpa_printf(MSG_ERROR, "PASN: Unsupported AKMP=0x%x", akmp);
+ return -1;
+ }
+
+ return wpas_pasn_send_auth_1(pasn, own_addr, peer_addr, bssid, akmp,
+ cipher, group,
+ freq, beacon_rsne, beacon_rsne_len,
+ beacon_rsnxe, beacon_rsnxe_len, comeback,
+ false);
+}
+
+/*
+ * Wi-Fi Aware uses PASN handshake to authenticate peer devices.
+ * Devices can simply verify each other for subsequent sessions using
+ * pairing verification procedure.
+ *
+ * In pairing verification, Wi-Fi aware devices use PASN authentication
+ * frames with a custom PMKID and Wi-Fi Aware R4 specific verification IEs.
+ * It does not use wrapped data in the Authentication frames. This function
+ * provides support to construct PASN Authentication frames for pairing
+ * verification.
+ */
+int wpa_pasn_verify(struct pasn_data *pasn, const u8 *own_addr,
+ const u8 *peer_addr, const u8 *bssid,
+ int akmp, int cipher, u16 group,
+ int freq, const u8 *beacon_rsne, u8 beacon_rsne_len,
+ const u8 *beacon_rsnxe, u8 beacon_rsnxe_len,
+ const struct wpabuf *comeback)
+{
+ return wpas_pasn_send_auth_1(pasn, own_addr, peer_addr, bssid, akmp,
+ cipher, group, freq, beacon_rsne,
+ beacon_rsne_len, beacon_rsnxe,
+ beacon_rsnxe_len, comeback, true);
+}
+
+
+static bool is_pasn_auth_frame(struct pasn_data *pasn,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, bool rx)
+{
+ u16 fc;
+
+ if (!mgmt || len < offsetof(struct ieee80211_mgmt, u.auth.variable))
+ return false;
+
+ /* Not an Authentication frame; do nothing */
+ fc = le_to_host16(mgmt->frame_control);
+ if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT ||
+ WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_AUTH)
+ return false;
+
+ /* Not our frame; do nothing */
+ if (os_memcmp(mgmt->bssid, pasn->bssid, ETH_ALEN) != 0)
+ return false;
+
+ if (rx && (os_memcmp(mgmt->da, pasn->own_addr, ETH_ALEN) != 0 ||
+ os_memcmp(mgmt->sa, pasn->peer_addr, ETH_ALEN) != 0))
+ return false;
+
+ if (!rx && (os_memcmp(mgmt->sa, pasn->own_addr, ETH_ALEN) != 0 ||
+ os_memcmp(mgmt->da, pasn->peer_addr, ETH_ALEN) != 0))
+ return false;
+
+ /* Not PASN; do nothing */
+ if (mgmt->u.auth.auth_alg != host_to_le16(WLAN_AUTH_PASN))
+ return false;
+
+ return true;
+}
+
+
+int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len,
+ struct wpa_pasn_params_data *pasn_params)
+
+{
+ struct ieee802_11_elems elems;
+ struct wpa_ie_data rsn_data;
+ const struct ieee80211_mgmt *mgmt =
+ (const struct ieee80211_mgmt *) data;
+ struct wpabuf *wrapped_data = NULL, *secret = NULL, *frame = NULL;
+ u8 mic[WPA_PASN_MAX_MIC_LEN], out_mic[WPA_PASN_MAX_MIC_LEN];
+ u8 mic_len;
+ u16 status;
+ int ret, inc_y;
+ u8 *copy = NULL;
+ size_t mic_offset, copy_len;
+
+ if (!is_pasn_auth_frame(pasn, mgmt, len, true))
+ return -2;
+
+ if (mgmt->u.auth.auth_transaction !=
+ host_to_le16(pasn->trans_seq + 1)) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: RX: Invalid transaction sequence: (%u != %u)",
+ le_to_host16(mgmt->u.auth.auth_transaction),
+ pasn->trans_seq + 1);
+ return -3;
+ }
+
+ status = le_to_host16(mgmt->u.auth.status_code);
+
+ if (status != WLAN_STATUS_SUCCESS &&
+ status != WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Authentication rejected - status=%u", status);
+ goto fail;
+ }
+
+ 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(pasn->akmp, pasn->cipher);
+ if (status == WLAN_STATUS_SUCCESS) {
+ if (!elems.mic || elems.mic_len != mic_len) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Invalid MIC. Expecting len=%u",
+ mic_len);
+ goto fail;
+ }
+ os_memcpy(mic, elems.mic, mic_len);
+ }
+
+ if (!elems.pasn_params || !elems.pasn_params_len) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Missing PASN Parameters IE");
+ goto fail;
+ }
+
+ if (!pasn_params) {
+ wpa_printf(MSG_DEBUG, "PASN: pasn_params == NULL");
+ goto fail;
+ }
+
+ ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
+ elems.pasn_params_len + 3,
+ true, pasn_params);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed validation PASN of Parameters IE");
+ goto fail;
+ }
+
+ if (status == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Authentication temporarily rejected");
+
+ if (pasn_params->comeback && pasn_params->comeback_len) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Comeback token available. After=%u",
+ pasn_params->after);
+
+ if (!pasn_params->after)
+ return 1;
+
+ pasn->comeback = wpabuf_alloc_copy(
+ pasn_params->comeback,
+ pasn_params->comeback_len);
+ if (pasn->comeback)
+ pasn->comeback_after = pasn_params->after;
+ }
+
+ pasn->status = status;
+ goto fail;
+ }
+
+ if (!elems.rsn_ie) {
+ wpa_printf(MSG_DEBUG, "PASN: Missing RSNE");
+ goto fail;
+ }
+
+ ret = wpa_parse_wpa_ie(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+ &rsn_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed parsing RSNE");
+ goto fail;
+ }
+
+ ret = wpa_pasn_validate_rsne(&rsn_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE");
+ goto fail;
+ }
+
+ if (pasn->akmp != rsn_data.key_mgmt ||
+ pasn->cipher != rsn_data.pairwise_cipher) {
+ wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher");
+ goto fail;
+ }
+
+ if (pasn->group != pasn_params->group) {
+ wpa_printf(MSG_DEBUG, "PASN: Mismatch in group");
+ goto fail;
+ }
+
+ if (!pasn_params->pubkey || !pasn_params->pubkey_len) {
+ wpa_printf(MSG_DEBUG, "PASN: Invalid public key");
+ goto fail;
+ }
+
+ if (pasn_params->pubkey[0] == WPA_PASN_PUBKEY_UNCOMPRESSED) {
+ inc_y = 1;
+ } else if (pasn_params->pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_0 ||
+ pasn_params->pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_1) {
+ inc_y = 0;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Invalid first octet in pubkey=0x%x",
+ pasn_params->pubkey[0]);
+ goto fail;
+ }
+
+ secret = crypto_ecdh_set_peerkey(pasn->ecdh, inc_y,
+ pasn_params->pubkey + 1,
+ pasn_params->pubkey_len - 1);
+
+ if (!secret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to derive shared secret");
+ 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;
+ }
+ }
+
+ ret = wpas_pasn_set_pmk(pasn, &rsn_data, pasn_params, wrapped_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to set PMK");
+ goto fail;
+ }
+
+ ret = pasn_pmk_to_ptk(pasn->pmk, pasn->pmk_len,
+ pasn->own_addr, pasn->peer_addr,
+ wpabuf_head(secret), wpabuf_len(secret),
+ &pasn->ptk, pasn->akmp, pasn->cipher,
+ pasn->kdk_len);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK");
+ goto fail;
+ }
+
+ if (pasn->secure_ltf) {
+ ret = wpa_ltf_keyseed(&pasn->ptk, pasn->akmp, pasn->cipher);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed to derive LTF keyseed");
+ goto fail;
+ }
+ }
+
+ wpabuf_free(wrapped_data);
+ wrapped_data = NULL;
+ wpabuf_free(secret);
+ secret = NULL;
+
+ /* Use a copy of the message since we need to clear the MIC field */
+ if (!elems.mic)
+ goto fail;
+ mic_offset = elems.mic - (const u8 *) &mgmt->u.auth;
+ copy_len = len - offsetof(struct ieee80211_mgmt, u.auth);
+ if (mic_offset + mic_len > copy_len)
+ goto fail;
+ copy = os_memdup(&mgmt->u.auth, copy_len);
+ if (!copy)
+ goto fail;
+ os_memset(copy + mic_offset, 0, mic_len);
+
+ if (pasn->beacon_rsne_rsnxe) {
+ /* Verify the MIC */
+ ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
+ pasn->peer_addr, pasn->own_addr,
+ wpabuf_head(pasn->beacon_rsne_rsnxe),
+ wpabuf_len(pasn->beacon_rsne_rsnxe),
+ copy, copy_len, out_mic);
+ } else {
+ u8 *rsne_rsnxe;
+ size_t rsne_rsnxe_len = 0;
+
+ /*
+ * Note: When Beacon rsne_rsnxe is not initialized, it is likely
+ * that this is for Wi-Fi Aware using PASN handshake for which
+ * Beacon RSNE/RSNXE are same as RSNE/RSNXE in the
+ * Authentication frame
+ */
+ if (elems.rsn_ie && elems.rsn_ie_len)
+ rsne_rsnxe_len += elems.rsn_ie_len + 2;
+ if (elems.rsnxe && elems.rsnxe_len)
+ rsne_rsnxe_len += elems.rsnxe_len + 2;
+
+ rsne_rsnxe = os_zalloc(rsne_rsnxe_len);
+ if (!rsne_rsnxe)
+ goto fail;
+
+ if (elems.rsn_ie && elems.rsn_ie_len)
+ os_memcpy(rsne_rsnxe, elems.rsn_ie - 2,
+ elems.rsn_ie_len + 2);
+ if (elems.rsnxe && elems.rsnxe_len)
+ os_memcpy(rsne_rsnxe + elems.rsn_ie_len + 2,
+ elems.rsnxe - 2, elems.rsnxe_len + 2);
+
+ wpa_hexdump_key(MSG_DEBUG, "PASN: RSN + RSNXE buf",
+ rsne_rsnxe, rsne_rsnxe_len);
+
+ /* Verify the MIC */
+ ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
+ pasn->peer_addr, pasn->own_addr,
+ rsne_rsnxe,
+ rsne_rsnxe_len,
+ copy, copy_len, out_mic);
+
+ os_free(rsne_rsnxe);
+ }
+ os_free(copy);
+ copy = NULL;
+
+ 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;
+ }
+
+ pasn->trans_seq++;
+
+ wpa_printf(MSG_DEBUG, "PASN: Success verifying Authentication frame");
+
+ frame = wpas_pasn_build_auth_3(pasn);
+ if (!frame) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed building 3rd auth frame");
+ goto fail;
+ }
+
+ ret = pasn->send_mgmt(pasn->cb_ctx,
+ wpabuf_head(frame), wpabuf_len(frame), 0,
+ pasn->freq, 100);
+ wpabuf_free(frame);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed sending 3st auth frame");
+ goto fail;
+ }
+
+ wpa_printf(MSG_DEBUG, "PASN: Success sending last frame. Store PTK");
+
+ pasn->status = WLAN_STATUS_SUCCESS;
+
+ return 0;
+fail:
+ wpa_printf(MSG_DEBUG, "PASN: Failed RX processing - terminating");
+ wpabuf_free(wrapped_data);
+ wpabuf_free(secret);
+ os_free(copy);
+
+ /*
+ * TODO: In case of an error the standard allows to silently drop
+ * the frame and terminate the authentication exchange. However, better
+ * reply to the AP with an error status.
+ */
+ if (status == WLAN_STATUS_SUCCESS)
+ pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ else
+ pasn->status = status;
+
+ return -1;
+}
+
+
+int wpa_pasn_auth_tx_status(struct pasn_data *pasn,
+ const u8 *data, size_t data_len, u8 acked)
+
+{
+ const struct ieee80211_mgmt *mgmt =
+ (const struct ieee80211_mgmt *) data;
+
+ wpa_printf(MSG_DEBUG, "PASN: auth_tx_status: acked=%u", acked);
+
+ if (!is_pasn_auth_frame(pasn, mgmt, data_len, false))
+ return -1;
+
+ if (mgmt->u.auth.auth_transaction != host_to_le16(pasn->trans_seq)) {
+ wpa_printf(MSG_ERROR,
+ "PASN: Invalid transaction sequence: (%u != %u)",
+ pasn->trans_seq,
+ le_to_host16(mgmt->u.auth.auth_transaction));
+ return 0;
+ }
+
+ wpa_printf(MSG_ERROR,
+ "PASN: auth with trans_seq=%u, acked=%u", pasn->trans_seq,
+ acked);
+
+ /*
+ * Even if the frame was not acked, do not treat this is an error, and
+ * try to complete the flow, relying on the PASN timeout callback to
+ * clean up.
+ */
+ if (pasn->trans_seq == 3) {
+ wpa_printf(MSG_DEBUG, "PASN: auth complete with: " MACSTR,
+ MAC2STR(pasn->peer_addr));
+ /*
+ * Either frame was not ACKed or it was ACKed but the trans_seq
+ * != 1, i.e., not expecting an RX frame, so we are done.
+ */
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/src/pasn/pasn_responder.c b/src/pasn/pasn_responder.c
new file mode 100644
index 0000000..3b1912d
--- /dev/null
+++ b/src/pasn/pasn_responder.c
@@ -0,0 +1,1016 @@
+/*
+ * PASN responder processing
+ *
+ * Copyright (C) 2019, Intel Corporation
+ * Copyright (C) 2022, Qualcomm Innovation Center, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/wpa_common.h"
+#include "common/sae.h"
+#include "common/ieee802_11_common.h"
+#include "common/ieee802_11_defs.h"
+#include "crypto/sha384.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "crypto/crypto.h"
+#include "ap/hostapd.h"
+#include "ap/comeback_token.h"
+#include "ap/ieee802_1x.h"
+#include "ap/pmksa_cache_auth.h"
+#include "pasn_common.h"
+
+#ifdef CONFIG_PASN
+#ifdef CONFIG_SAE
+
+static int pasn_wd_handle_sae_commit(struct pasn_data *pasn,
+ const u8 *own_addr, const u8 *peer_addr,
+ struct wpabuf *wd)
+{
+ 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=%zu",
+ 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);
+
+ if (alg != WLAN_AUTH_SAE || seq != 1 ||
+ status != WLAN_STATUS_SAE_HASH_TO_ELEMENT) {
+ 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;
+ }
+
+ if (!pasn->password || !pasn->pt) {
+ wpa_printf(MSG_DEBUG, "PASN: No SAE PT found");
+ return -1;
+ }
+
+ ret = sae_prepare_commit_pt(&pasn->sae, pasn->pt, own_addr, peer_addr,
+ NULL, NULL);
+ 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, NULL);
+ 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 pasn_data *pasn,
+ const u8 *peer_addr, struct wpabuf *wd)
+{
+ 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=%zu",
+ 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, NULL);
+ 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.
+ */
+ if (pasn->disable_pmksa_caching)
+ return 0;
+
+ wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from SAE",
+ pasn->sae.pmk, pasn->sae.pmk_len);
+ if (!pasn->sae.akmp)
+ pasn->sae.akmp = WPA_KEY_MGMT_SAE;
+
+ pmksa_cache_auth_add(pasn->pmksa, pasn->sae.pmk, pasn->sae.pmk_len,
+ pasn->sae.pmkid, NULL, 0, pasn->own_addr,
+ peer_addr, 0, NULL, pasn->sae.akmp);
+ return 0;
+}
+
+
+static struct wpabuf * pasn_get_sae_wd(struct pasn_data *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_SAE_HASH_TO_ELEMENT);
+
+ /* 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 pasn_data *pasn)
+{
+ struct pasn_fils *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;
+}
+
+#endif /* CONFIG_FILS */
+
+static struct wpabuf * pasn_get_wrapped_data(struct pasn_data *pasn)
+{
+ switch (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(pasn);
+#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(pasn);
+#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",
+ pasn->akmp);
+ return NULL;
+ }
+}
+
+
+static int
+pasn_derive_keys(struct pasn_data *pasn,
+ const u8 *own_addr, const u8 *peer_addr,
+ 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 (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 (pasn->akmp) {
+#ifdef CONFIG_SAE
+ case WPA_KEY_MGMT_SAE:
+ if (pasn->sae.state == SAE_COMMITTED) {
+ pmk_len = PMK_LEN;
+ os_memcpy(pmk, 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, peer_addr, own_addr,
+ wpabuf_head(secret), wpabuf_len(secret),
+ &pasn->ptk, pasn->akmp,
+ pasn->cipher, pasn->kdk_len);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK");
+ return -1;
+ }
+
+ if (pasn->secure_ltf) {
+ ret = wpa_ltf_keyseed(&pasn->ptk, pasn->akmp,
+ pasn->cipher);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed to derive LTF keyseed");
+ return -1;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived");
+ return 0;
+}
+
+
+static void handle_auth_pasn_comeback(struct pasn_data *pasn,
+ const u8 *own_addr, const u8 *peer_addr,
+ u16 group)
+{
+ struct wpabuf *buf, *comeback;
+ int ret;
+
+ wpa_printf(MSG_DEBUG,
+ "PASN: Building comeback frame 2. Comeback after=%u",
+ pasn->comeback_after);
+
+ buf = wpabuf_alloc(1500);
+ if (!buf)
+ return;
+
+ wpa_pasn_build_auth_header(buf, pasn->bssid, own_addr, peer_addr, 2,
+ WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY);
+
+ /*
+ * Do not include the group as a part of the token since it is not going
+ * to be used.
+ */
+ comeback = auth_build_token_req(&pasn->last_comeback_key_update,
+ pasn->comeback_key, pasn->comeback_idx,
+ pasn->comeback_pending_idx,
+ sizeof(u16) * COMEBACK_PENDING_IDX_SIZE,
+ 0, peer_addr, 0);
+ if (!comeback) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed sending auth with comeback");
+ wpabuf_free(buf);
+ return;
+ }
+
+ wpa_pasn_add_parameter_ie(buf, group,
+ WPA_PASN_WRAPPED_DATA_NO,
+ NULL, 0, comeback,
+ pasn->comeback_after);
+ wpabuf_free(comeback);
+
+ wpa_printf(MSG_DEBUG,
+ "PASN: comeback: STA=" MACSTR, MAC2STR(peer_addr));
+
+ ret = pasn->send_mgmt(pasn->cb_ctx, wpabuf_head_u8(buf),
+ wpabuf_len(buf), 0, 0, 0);
+ if (ret)
+ wpa_printf(MSG_INFO, "PASN: Failed to send comeback frame 2");
+
+ wpabuf_free(buf);
+}
+
+
+int handle_auth_pasn_resp(struct pasn_data *pasn, const u8 *own_addr,
+ const u8 *peer_addr,
+ struct rsn_pmksa_cache_entry *pmksa, u16 status)
+{
+ struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL;
+ struct wpabuf *rsn_buf = NULL;
+ u8 mic[WPA_PASN_MAX_MIC_LEN];
+ u8 mic_len;
+ u8 *ptr;
+ const u8 *frame, *data, *rsn_ie, *rsnxe_ie;
+ u8 *data_buf = NULL;
+ size_t frame_len, data_len;
+ int ret;
+ const u8 *pmkid = NULL;
+
+ 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, pasn->bssid, own_addr, peer_addr, 2,
+ status);
+
+ if (status != WLAN_STATUS_SUCCESS)
+ goto done;
+
+ if (pmksa && pasn->custom_pmkid_valid)
+ pmkid = pasn->custom_pmkid;
+ else if (pmksa) {
+ pmkid = pmksa->pmkid;
+#ifdef CONFIG_SAE
+ } else if (pasn->akmp == WPA_KEY_MGMT_SAE) {
+ wpa_printf(MSG_DEBUG, "PASN: Use SAE PMKID");
+ pmkid = pasn->sae.pmkid;
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+ } else if (pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
+ pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
+ wpa_printf(MSG_DEBUG, "PASN: Use FILS ERP PMKID");
+ pmkid = pasn->fils.erp_pmkid;
+#endif /* CONFIG_FILS */
+ }
+
+ if (wpa_pasn_add_rsne(buf, pmkid,
+ pasn->akmp, pasn->cipher) < 0)
+ goto fail;
+
+ /* No need to derive PMK if PMKSA is given */
+ if (!pmksa)
+ wrapped_data_buf = pasn_get_wrapped_data(pasn);
+ else
+ pasn->wrapped_data_format = WPA_PASN_WRAPPED_DATA_NO;
+
+ /* Get public key */
+ pubkey = crypto_ecdh_get_pubkey(pasn->ecdh, 0);
+ pubkey = wpabuf_zeropad(pubkey,
+ crypto_ecdh_prime_len(pasn->ecdh));
+ if (!pubkey) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to get pubkey");
+ goto fail;
+ }
+
+ wpa_pasn_add_parameter_ie(buf, pasn->group,
+ pasn->wrapped_data_format,
+ pubkey, true, 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 = pasn->rsnxe_ie;
+ if (rsnxe_ie)
+ wpabuf_put_data(buf, rsnxe_ie, 2 + rsnxe_ie[1]);
+
+ wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len);
+
+ /* Add the mic */
+ mic_len = pasn_mic_len(pasn->akmp, 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;
+
+ if (pasn->rsn_ie && pasn->rsn_ie_len) {
+ rsn_ie = pasn->rsn_ie;
+ } else {
+ /*
+ * Note: when pasn->rsn_ie is NULL, it is likely that Beacon
+ * frame RSNE is not initialized. This is possible in case of
+ * PASN authentication used for Wi-Fi Aware for which Beacon
+ * frame RSNE and RSNXE are same as RSNE and RSNXE in the
+ * Authentication frame.
+ */
+ rsn_buf = wpabuf_alloc(500);
+ if (!rsn_buf)
+ goto fail;
+
+ if (wpa_pasn_add_rsne(rsn_buf, pmkid,
+ pasn->akmp, pasn->cipher) < 0)
+ goto fail;
+
+ rsn_ie = wpabuf_head_u8(rsn_buf);
+ }
+
+ /*
+ * 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(pasn->ptk.kck, pasn->akmp, pasn->cipher,
+ own_addr, peer_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;
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (pasn->corrupt_mic) {
+ wpa_printf(MSG_DEBUG, "PASN: frame 2: Corrupt MIC");
+ mic[0] = ~mic[0];
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ os_memcpy(ptr, mic, mic_len);
+
+done:
+ wpa_printf(MSG_DEBUG,
+ "PASN: Building frame 2: success; resp STA=" MACSTR,
+ MAC2STR(peer_addr));
+
+ ret = pasn->send_mgmt(pasn->cb_ctx, wpabuf_head_u8(buf),
+ wpabuf_len(buf), 0, 0, 0);
+ if (ret)
+ wpa_printf(MSG_INFO, "send_auth_reply: Send failed");
+
+ wpabuf_free(rsn_buf);
+ wpabuf_free(buf);
+ return ret;
+fail:
+ wpabuf_free(wrapped_data_buf);
+ wpabuf_free(pubkey);
+ wpabuf_free(rsn_buf);
+ wpabuf_free(buf);
+ return -1;
+}
+
+
+int handle_auth_pasn_1(struct pasn_data *pasn,
+ const u8 *own_addr, const u8 *peer_addr,
+ 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;
+ struct wpabuf *wrapped_data = NULL, *secret = NULL;
+ const int *groups = pasn->pasn_groups;
+ static const int default_groups[] = { 19, 0 };
+ u16 status = WLAN_STATUS_SUCCESS;
+ int ret, inc_y;
+ 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;
+ }
+
+ if (!elems.rsn_ie) {
+ wpa_printf(MSG_DEBUG, "PASN: No RSNE");
+ status = WLAN_STATUS_INVALID_RSNIE;
+ 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 RSNE");
+ 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 & pasn->wpa_key_mgmt) ||
+ !(rsn_data.pairwise_cipher & pasn->rsn_pairwise)) {
+ wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher");
+ status = WLAN_STATUS_INVALID_RSNIE;
+ goto send_resp;
+ }
+
+ pasn->akmp = rsn_data.key_mgmt;
+ pasn->cipher = rsn_data.pairwise_cipher;
+
+ if (pasn->derive_kdk &&
+ ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
+ WLAN_RSNX_CAPAB_SECURE_LTF))
+ pasn->secure_ltf = true;
+
+ if (pasn->derive_kdk)
+ pasn->kdk_len = WPA_KDK_MAX_LEN;
+ else
+ pasn->kdk_len = 0;
+
+ wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len);
+
+ 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_INVALID_PARAMETERS;
+ goto send_resp;
+ }
+
+ if (pasn_params.comeback) {
+ wpa_printf(MSG_DEBUG, "PASN: Checking peer comeback token");
+
+ ret = check_comeback_token(pasn->comeback_key,
+ pasn->comeback_pending_idx,
+ peer_addr,
+ pasn_params.comeback,
+ pasn_params.comeback_len);
+
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Invalid comeback token");
+ status = WLAN_STATUS_INVALID_PARAMETERS;
+ goto send_resp;
+ }
+ } else if (pasn->use_anti_clogging) {
+ wpa_printf(MSG_DEBUG, "PASN: Respond with comeback");
+ handle_auth_pasn_comeback(pasn, own_addr, peer_addr,
+ pasn_params.group);
+ return -1;
+ }
+
+ pasn->ecdh = crypto_ecdh_init(pasn_params.group);
+ if (!pasn->ecdh) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to init ECDH");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto send_resp;
+ }
+
+ pasn->group = pasn_params.group;
+
+ if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_UNCOMPRESSED) {
+ inc_y = 1;
+ } else if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_0 ||
+ pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_1) {
+ inc_y = 0;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Invalid first octet in pubkey=0x%x",
+ pasn_params.pubkey[0]);
+ status = WLAN_STATUS_INVALID_PUBLIC_KEY;
+ goto send_resp;
+ }
+
+ secret = crypto_ecdh_set_peerkey(pasn->ecdh, inc_y,
+ pasn_params.pubkey + 1,
+ pasn_params.pubkey_len - 1);
+ 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 (pasn->akmp == WPA_KEY_MGMT_SAE) {
+ ret = pasn_wd_handle_sae_commit(pasn, own_addr,
+ peer_addr,
+ 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 (pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
+ pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
+ if (!pasn->fils_wd_valid) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Invalid 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 */
+ }
+
+ pasn->wrapped_data_format = pasn_params.wrapped_data_format;
+
+ ret = pasn_auth_frame_hash(pasn->akmp, pasn->cipher,
+ ((const u8 *) mgmt) + IEEE80211_HDRLEN,
+ len - IEEE80211_HDRLEN, 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");
+ pasn->secret = secret;
+ wpabuf_free(wrapped_data);
+ return 0;
+ }
+
+ if (rsn_data.num_pmkid) {
+ if (wpa_key_mgmt_ft(pasn->akmp)) {
+#ifdef CONFIG_IEEE80211R_AP
+ wpa_printf(MSG_DEBUG, "PASN: FT: Fetch PMK-R1");
+
+ if (!pasn->pmk_r1_len) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: FT: Failed getting PMK-R1");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto send_resp;
+ }
+ cached_pmk = pasn->pmk_r1;
+ cached_pmk_len = pasn->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");
+
+ if (pasn->pmksa) {
+ const u8 *pmkid = NULL;
+
+ if (pasn->custom_pmkid_valid) {
+ ret = pasn->validate_custom_pmkid(
+ pasn->cb_ctx, peer_addr,
+ rsn_data.pmkid);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed custom PMKID validation");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto send_resp;
+ }
+ } else {
+ pmkid = rsn_data.pmkid;
+ }
+
+ pmksa = pmksa_cache_auth_get(pasn->pmksa,
+ peer_addr,
+ 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(pasn, own_addr, peer_addr,
+ cached_pmk, cached_pmk_len,
+ &pasn_params, wrapped_data, secret);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to derive keys");
+ status = WLAN_STATUS_PASN_BASE_AKMP_FAILED;
+ goto send_resp;
+ }
+
+ ret = pasn_auth_frame_hash(pasn->akmp, pasn->cipher,
+ ((const u8 *) mgmt) + IEEE80211_HDRLEN,
+ len - IEEE80211_HDRLEN, 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(pasn, own_addr, peer_addr, 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)
+ return -1;
+
+ return 0;
+}
+
+
+int handle_auth_pasn_3(struct pasn_data *pasn, const u8 *own_addr,
+ const u8 *peer_addr,
+ 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;
+ u8 *copy = NULL;
+ size_t copy_len, mic_offset;
+
+ 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(pasn->akmp, pasn->cipher);
+ if (!elems.mic || elems.mic_len != mic_len) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Invalid MIC. Expecting len=%u", mic_len);
+ goto fail;
+ }
+ os_memcpy(mic, elems.mic, 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 */
+ copy_len = len - offsetof(struct ieee80211_mgmt, u.auth);
+ mic_offset = elems.mic - (const u8 *) &mgmt->u.auth;
+ copy_len = len - offsetof(struct ieee80211_mgmt, u.auth);
+ if (mic_offset + mic_len > copy_len)
+ goto fail;
+ copy = os_memdup(&mgmt->u.auth, copy_len);
+ if (!copy)
+ goto fail;
+ os_memset(copy + mic_offset, 0, mic_len);
+ ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
+ peer_addr, own_addr,
+ pasn->hash, mic_len * 2,
+ copy, copy_len, out_mic);
+ os_free(copy);
+ copy = NULL;
+
+ 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 (pasn->akmp == WPA_KEY_MGMT_SAE) {
+ ret = pasn_wd_handle_sae_confirm(pasn, peer_addr,
+ 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 (pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
+ 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");
+ return 0;
+
+fail:
+ os_free(copy);
+ return -1;
+}
+
+#endif /* CONFIG_PASN */
diff --git a/src/radius/radius.c b/src/radius/radius.c
index a642280..be59a94 100644
--- a/src/radius/radius.c
+++ b/src/radius/radius.c
@@ -176,6 +176,7 @@
{ RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 },
{ RADIUS_ATTR_SERVICE_TYPE, "Service-Type", RADIUS_ATTR_INT32 },
{ RADIUS_ATTR_FRAMED_IP_ADDRESS, "Framed-IP-Address", RADIUS_ATTR_IP },
+ { RADIUS_ATTR_FILTER_ID, "Filter-Id", RADIUS_ATTR_TEXT },
{ RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 },
{ RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT },
{ RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST },
diff --git a/src/radius/radius.h b/src/radius/radius.h
index 177c64a..571c159 100644
--- a/src/radius/radius.h
+++ b/src/radius/radius.h
@@ -62,6 +62,7 @@
RADIUS_ATTR_NAS_PORT = 5,
RADIUS_ATTR_SERVICE_TYPE = 6,
RADIUS_ATTR_FRAMED_IP_ADDRESS = 8,
+ RADIUS_ATTR_FILTER_ID = 11,
RADIUS_ATTR_FRAMED_MTU = 12,
RADIUS_ATTR_REPLY_MESSAGE = 18,
RADIUS_ATTR_STATE = 24,
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index 93cc9a7..e7b4d54 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -30,6 +30,7 @@
enum pmksa_free_reason reason);
bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
void *ctx);
+ void (*notify_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx);
void *ctx;
};
@@ -47,16 +48,46 @@
struct rsn_pmksa_cache_entry *entry,
enum pmksa_free_reason reason)
{
- wpa_sm_remove_pmkid(pmksa->sm, entry->network_ctx, entry->aa,
- entry->pmkid,
- entry->fils_cache_id_set ? entry->fils_cache_id :
- NULL);
+ if (pmksa->sm)
+ wpa_sm_remove_pmkid(pmksa->sm, entry->network_ctx, entry->aa,
+ entry->pmkid,
+ entry->fils_cache_id_set ?
+ entry->fils_cache_id : NULL);
pmksa->pmksa_count--;
- pmksa->free_cb(entry, pmksa->ctx, reason);
+ if (pmksa->free_cb)
+ pmksa->free_cb(entry, pmksa->ctx, reason);
_pmksa_cache_free_entry(entry);
}
+void pmksa_cache_remove(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry)
+{
+ struct rsn_pmksa_cache_entry *e;
+
+ e = pmksa->pmksa;
+ while (e) {
+ if (e == entry) {
+ pmksa->pmksa = entry->next;
+ break;
+ }
+ if (e->next == entry) {
+ e->next = entry->next;
+ break;
+ }
+ }
+
+ if (!e) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: Could not remove PMKSA cache entry %p since it is not in the list",
+ entry);
+ return;
+ }
+
+ pmksa_cache_free_entry(pmksa, entry, PMKSA_FREE);
+}
+
+
static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
{
struct rsn_pmksa_cache *pmksa = eloop_ctx;
@@ -66,7 +97,7 @@
os_get_reltime(&now);
while (entry && entry->expiration <= now.sec) {
- if (wpa_key_mgmt_sae(entry->akmp) &&
+ if (wpa_key_mgmt_sae(entry->akmp) && pmksa->is_current_cb &&
pmksa->is_current_cb(entry, pmksa->ctx)) {
/* Do not expire the currently used PMKSA entry for SAE
* since there is no convenient mechanism for
@@ -99,6 +130,10 @@
static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx)
{
struct rsn_pmksa_cache *pmksa = eloop_ctx;
+
+ if (!pmksa->sm)
+ return;
+
pmksa->sm->cur_pmksa = NULL;
eapol_sm_request_reauth(pmksa->sm->eapol);
}
@@ -119,6 +154,7 @@
if (sec < 0) {
sec = 0;
if (wpa_key_mgmt_sae(pmksa->pmksa->akmp) &&
+ pmksa->is_current_cb &&
pmksa->is_current_cb(pmksa->pmksa, pmksa->ctx)) {
/* Do not continue polling for the current PMKSA entry
* from SAE to expire every second. Use the expiration
@@ -139,8 +175,11 @@
}
eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
+ if (!pmksa->sm)
+ return;
+
entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa :
- pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL, 0);
+ pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL, NULL, 0);
if (entry && !wpa_key_mgmt_sae(entry->akmp)) {
sec = pmksa->pmksa->reauth_time - now.sec;
if (sec < 0)
@@ -179,6 +218,8 @@
{
struct rsn_pmksa_cache_entry *entry;
struct os_reltime now;
+ unsigned int pmk_lifetime = 43200;
+ unsigned int pmk_reauth_threshold = 70;
if (pmk_len > PMK_LEN_MAX)
return NULL;
@@ -200,15 +241,21 @@
else
rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp);
os_get_reltime(&now);
- entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime;
- entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime *
- pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100;
+ if (pmksa->sm) {
+ pmk_lifetime = pmksa->sm->dot11RSNAConfigPMKLifetime;
+ pmk_reauth_threshold =
+ pmksa->sm->dot11RSNAConfigPMKReauthThreshold;
+ }
+ entry->expiration = now.sec + pmk_lifetime;
+ entry->reauth_time = now.sec +
+ pmk_lifetime * pmk_reauth_threshold / 100;
entry->akmp = akmp;
if (cache_id) {
entry->fils_cache_id_set = 1;
os_memcpy(entry->fils_cache_id, cache_id, FILS_CACHE_ID_LEN);
}
os_memcpy(entry->aa, aa, ETH_ALEN);
+ os_memcpy(entry->spa, spa, ETH_ALEN);
entry->network_ctx = network_ctx;
return pmksa_cache_add_entry(pmksa, entry);
@@ -226,7 +273,8 @@
pos = pmksa->pmksa;
prev = NULL;
while (pos) {
- if (os_memcmp(entry->aa, pos->aa, ETH_ALEN) == 0) {
+ if (os_memcmp(entry->aa, pos->aa, ETH_ALEN) == 0 &&
+ os_memcmp(entry->spa, pos->spa, ETH_ALEN) == 0) {
if (pos->pmk_len == entry->pmk_len &&
os_memcmp_const(pos->pmk, entry->pmk,
entry->pmk_len) == 0 &&
@@ -269,7 +317,7 @@
/* Remove the oldest entry to make room for the new entry */
pos = pmksa->pmksa;
- if (pos == pmksa->sm->cur_pmksa) {
+ if (pmksa->sm && pos == pmksa->sm->cur_pmksa) {
/*
* Never remove the current PMKSA cache entry, since
* it's in use, and removing it triggers a needless
@@ -308,9 +356,16 @@
}
pmksa->pmksa_count++;
wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR
- " network_ctx=%p akmp=0x%x", MAC2STR(entry->aa),
+ " spa=" MACSTR " network_ctx=%p akmp=0x%x",
+ MAC2STR(entry->aa), MAC2STR(entry->spa),
entry->network_ctx, entry->akmp);
- wpas_notify_pmk_cache_added((struct wpa_supplicant *)pmksa->sm->ctx->ctx, entry);
+
+ if (!pmksa->sm)
+ return entry;
+
+ if (pmksa->notify_cb)
+ pmksa->notify_cb(entry, pmksa->ctx);
+
wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa, entry->pmkid,
entry->fils_cache_id_set ? entry->fils_cache_id : NULL,
entry->pmk, entry->pmk_len,
@@ -398,13 +453,15 @@
* Returns: Pointer to PMKSA cache entry or %NULL if no match was found
*/
struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
- const u8 *aa, const u8 *pmkid,
+ const u8 *aa, const u8 *spa,
+ const u8 *pmkid,
const void *network_ctx,
int akmp)
{
struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
while (entry) {
if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) &&
+ (!spa || os_memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
(pmkid == NULL ||
os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) &&
(!akmp || akmp == entry->akmp) &&
@@ -426,6 +483,9 @@
os_time_t old_reauth_time = old_entry->reauth_time;
const u8 *pmkid = NULL;
+ if (!pmksa->sm)
+ return NULL;
+
if (wpa_key_mgmt_sae(old_entry->akmp) ||
wpa_key_mgmt_fils(old_entry->akmp))
pmkid = old_entry->pmkid;
@@ -577,11 +637,11 @@
sm->cur_pmksa = NULL;
if (pmkid)
- sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid,
- network_ctx, akmp);
+ sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, sm->own_addr,
+ pmkid, network_ctx, akmp);
if (sm->cur_pmksa == NULL && bssid)
- sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL,
- network_ctx, akmp);
+ sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, sm->own_addr,
+ NULL, network_ctx, akmp);
if (sm->cur_pmksa == NULL && try_opportunistic && bssid)
sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa,
network_ctx,
@@ -700,6 +760,8 @@
void *ctx, enum pmksa_free_reason reason),
bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
void *ctx),
+ void (*notify_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx),
void *ctx, struct wpa_sm *sm)
{
struct rsn_pmksa_cache *pmksa;
@@ -708,6 +770,7 @@
if (pmksa) {
pmksa->free_cb = free_cb;
pmksa->is_current_cb = is_current_cb;
+ pmksa->notify_cb = notify_cb;
pmksa->ctx = ctx;
pmksa->sm = sm;
}
diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h
index b801268..48c9e04 100644
--- a/src/rsn_supp/pmksa_cache.h
+++ b/src/rsn_supp/pmksa_cache.h
@@ -20,6 +20,7 @@
os_time_t expiration;
int akmp; /* WPA_KEY_MGMT_* */
u8 aa[ETH_ALEN];
+ u8 spa[ETH_ALEN];
/*
* If FILS Cache Identifier is included (fils_cache_id_set), this PMKSA
@@ -61,10 +62,13 @@
void *ctx, enum pmksa_free_reason reason),
bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
void *ctx),
+ void (*notify_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx),
void *ctx, struct wpa_sm *sm);
void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa);
struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
- const u8 *aa, const u8 *pmkid,
+ const u8 *aa, const u8 *spa,
+ const u8 *pmkid,
const void *network_ctx,
int akmp);
int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
@@ -88,6 +92,8 @@
void *network_ctx, const u8 *aa, int akmp);
void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx,
const u8 *pmk, size_t pmk_len, bool external_only);
+void pmksa_cache_remove(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry);
void pmksa_cache_reconfig(struct rsn_pmksa_cache *pmksa);
#else /* IEEE8021X_EAPOL */
@@ -97,6 +103,8 @@
void *ctx, enum pmksa_free_reason reason),
bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
void *ctx),
+ void (*notify_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx),
void *ctx, struct wpa_sm *sm)
{
return (void *) -1;
@@ -107,8 +115,8 @@
}
static inline struct rsn_pmksa_cache_entry *
-pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid,
- const void *network_ctx, int akmp)
+pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa,
+ const u8 *pmkid, const void *network_ctx, int akmp)
{
return NULL;
}
@@ -168,6 +176,11 @@
{
}
+static inline void pmksa_cache_remove(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry)
+{
+}
+
static inline void pmksa_cache_reconfig(struct rsn_pmksa_cache *pmksa)
{
}
diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c
index a96655f..8f86820 100644
--- a/src/rsn_supp/preauth.c
+++ b/src/rsn_supp/preauth.c
@@ -330,7 +330,8 @@
dl_list_for_each_safe(candidate, n, &sm->pmksa_candidates,
struct rsn_pmksa_candidate, list) {
struct rsn_pmksa_cache_entry *p = NULL;
- p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL, NULL, 0);
+ p = pmksa_cache_get(sm->pmksa, candidate->bssid, sm->own_addr,
+ NULL, NULL, 0);
if (os_memcmp(sm->bssid, candidate->bssid, ETH_ALEN) != 0 &&
(p == NULL || p->opportunistic)) {
wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA "
@@ -491,7 +492,7 @@
if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie))
return;
- pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL, NULL, 0);
+ pmksa = pmksa_cache_get(sm->pmksa, bssid, sm->own_addr, NULL, NULL, 0);
if (pmksa && (!pmksa->opportunistic ||
!(ie.capabilities & WPA_CAPABILITY_PREAUTH)))
return;
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index c26a63d..1531f51 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -180,7 +180,7 @@
static int wpa_tdls_del_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
{
- if (wpa_sm_set_key(sm, WPA_ALG_NONE, peer->addr,
+ if (wpa_sm_set_key(sm, -1, WPA_ALG_NONE, peer->addr,
0, 0, NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE) < 0) {
wpa_printf(MSG_WARNING, "TDLS: Failed to delete TPK-TK from "
"the driver");
@@ -230,7 +230,7 @@
wpa_printf(MSG_DEBUG, "TDLS: Configure pairwise key for peer " MACSTR,
MAC2STR(peer->addr));
- if (wpa_sm_set_key(sm, alg, peer->addr, 0, 1, rsc, sizeof(rsc),
+ if (wpa_sm_set_key(sm, -1, alg, peer->addr, 0, 1, rsc, sizeof(rsc),
peer->tpk.tk, key_len,
KEY_FLAG_PAIRWISE_RX_TX) < 0) {
wpa_printf(MSG_WARNING, "TDLS: Failed to set TPK to the "
@@ -465,23 +465,26 @@
* wpa_tdls_ftie_mic - Calculate TDLS FTIE MIC
* @kck: TPK-KCK
* @lnkid: Pointer to the beginning of Link Identifier IE
- * @rsnie: Pointer to the beginning of RSN IE used for handshake
+ * @rsne: Pointer to the beginning of RSNE used for handshake
+ * @rsne_len: Length of RSNE in octets
* @timeoutie: Pointer to the beginning of Timeout IE used for handshake
- * @ftie: Pointer to the beginning of FT IE
+ * @fte: Pointer to the beginning of FTE
+ * @fre_len: Length of FTE in octets
* @mic: Pointer for writing MIC
*
* Calculate MIC for TDLS frame.
*/
static int wpa_tdls_ftie_mic(const u8 *kck, u8 trans_seq, const u8 *lnkid,
- const u8 *rsnie, const u8 *timeoutie,
- const u8 *ftie, u8 *mic)
+ const u8 *rsne, size_t rsne_len,
+ const u8 *timeoutie,
+ const u8 *fte, size_t fte_len, u8 *mic)
{
u8 *buf, *pos;
struct wpa_tdls_ftie *_ftie;
const struct wpa_tdls_lnkid *_lnkid;
int ret;
- int len = 2 * ETH_ALEN + 1 + 2 + lnkid[1] + 2 + rsnie[1] +
- 2 + timeoutie[1] + 2 + ftie[1];
+ int len = 2 * ETH_ALEN + 1 + 2 + lnkid[1] + rsne_len +
+ 2 + timeoutie[1] + fte_len;
buf = os_zalloc(len);
if (!buf) {
wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation");
@@ -502,16 +505,16 @@
os_memcpy(pos, lnkid, 2 + lnkid[1]);
pos += 2 + lnkid[1];
/* 5) RSN IE */
- os_memcpy(pos, rsnie, 2 + rsnie[1]);
- pos += 2 + rsnie[1];
+ os_memcpy(pos, rsne, rsne_len);
+ pos += rsne_len;
/* 6) Timeout Interval IE */
os_memcpy(pos, timeoutie, 2 + timeoutie[1]);
pos += 2 + timeoutie[1];
/* 7) FTIE, with the MIC field of the FTIE set to 0 */
- os_memcpy(pos, ftie, 2 + ftie[1]);
+ os_memcpy(pos, fte, fte_len);
_ftie = (struct wpa_tdls_ftie *) pos;
os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
- pos += 2 + ftie[1];
+ pos += fte_len;
wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
@@ -529,14 +532,15 @@
* @rcode: Reason code for Teardown
* @dtoken: Dialog Token used for that particular link
* @lnkid: Pointer to the beginning of Link Identifier IE
- * @ftie: Pointer to the beginning of FT IE
+ * @fte: Pointer to the beginning of FTE
+ * @fre_len: Length of FTE in octets
* @mic: Pointer for writing MIC
*
* Calculate MIC for TDLS frame.
*/
static int wpa_tdls_key_mic_teardown(const u8 *kck, u8 trans_seq, u16 rcode,
u8 dtoken, const u8 *lnkid,
- const u8 *ftie, u8 *mic)
+ const u8 *fte, size_t fte_len, u8 *mic)
{
u8 *buf, *pos;
struct wpa_tdls_ftie *_ftie;
@@ -547,7 +551,7 @@
return -1;
len = 2 + lnkid[1] + sizeof(rcode) + sizeof(dtoken) +
- sizeof(trans_seq) + 2 + ftie[1];
+ sizeof(trans_seq) + fte_len;
buf = os_zalloc(len);
if (!buf) {
@@ -567,10 +571,10 @@
/* 4) Transaction Sequence number */
*pos++ = trans_seq;
/* 7) FTIE, with the MIC field of the FTIE set to 0 */
- os_memcpy(pos, ftie, 2 + ftie[1]);
+ os_memcpy(pos, fte, fte_len);
_ftie = (struct wpa_tdls_ftie *) pos;
os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
- pos += 2 + ftie[1];
+ pos += fte_len;
wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
@@ -584,14 +588,15 @@
static int wpa_supplicant_verify_tdls_mic(u8 trans_seq,
struct wpa_tdls_peer *peer,
const u8 *lnkid, const u8 *timeoutie,
- const struct wpa_tdls_ftie *ftie)
+ const struct wpa_tdls_ftie *ftie,
+ size_t fte_len)
{
u8 mic[16];
if (peer->tpk_set) {
wpa_tdls_ftie_mic(peer->tpk.kck, trans_seq, lnkid,
- peer->rsnie_p, timeoutie, (u8 *) ftie,
- mic);
+ peer->rsnie_p, peer->rsnie_p_len, timeoutie,
+ (const u8 *) ftie, fte_len, mic);
if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
wpa_printf(MSG_INFO, "TDLS: Invalid MIC in FTIE - "
"dropping packet");
@@ -612,13 +617,14 @@
static int wpa_supplicant_verify_tdls_mic_teardown(
u8 trans_seq, u16 rcode, u8 dtoken, struct wpa_tdls_peer *peer,
- const u8 *lnkid, const struct wpa_tdls_ftie *ftie)
+ const u8 *lnkid, const struct wpa_tdls_ftie *ftie, size_t fte_len)
{
u8 mic[16];
if (peer->tpk_set) {
wpa_tdls_key_mic_teardown(peer->tpk.kck, trans_seq, rcode,
- dtoken, lnkid, (u8 *) ftie, mic);
+ dtoken, lnkid, (const u8 *) ftie,
+ fte_len, mic);
if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
wpa_printf(MSG_INFO, "TDLS: Invalid MIC in Teardown - "
"dropping packet");
@@ -830,7 +836,8 @@
/* compute MIC before sending */
wpa_tdls_linkid(sm, peer, &lnkid);
wpa_tdls_key_mic_teardown(peer->tpk.kck, 4, reason_code,
- dialog_token, (u8 *) &lnkid, (u8 *) ftie,
+ dialog_token, (const u8 *) &lnkid,
+ (const u8 *) ftie, 2 + ftie->ie_len,
ftie->mic);
skip_ies:
@@ -1000,7 +1007,8 @@
/* Process MIC check to see if TDLS Teardown is right */
if (wpa_supplicant_verify_tdls_mic_teardown(4, reason_code,
peer->dtoken, peer,
- (u8 *) lnkid, ftie) < 0) {
+ (const u8 *) lnkid,
+ ftie, kde.ftie_len) < 0) {
wpa_printf(MSG_DEBUG, "TDLS: MIC failure for TDLS "
"Teardown Request from " MACSTR, MAC2STR(src_addr));
return -1;
@@ -1320,8 +1328,9 @@
lifetime);
/* compute MIC before sending */
- wpa_tdls_ftie_mic(peer->tpk.kck, 2, (u8 *) lnkid, peer->rsnie_p,
- (u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
+ wpa_tdls_ftie_mic(peer->tpk.kck, 2, (const u8 *) lnkid, peer->rsnie_p,
+ peer->rsnie_p_len, (const u8 *) &timeoutie,
+ (const u8 *) ftie, 2 + ftie->ie_len, ftie->mic);
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_WRONG_MIC) {
wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC");
@@ -1410,8 +1419,9 @@
lifetime);
/* compute MIC before sending */
- wpa_tdls_ftie_mic(peer->tpk.kck, 3, (u8 *) lnkid, peer->rsnie_p,
- (u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
+ wpa_tdls_ftie_mic(peer->tpk.kck, 3, (const u8 *) lnkid, peer->rsnie_p,
+ peer->rsnie_p_len, (const u8 *) &timeoutie,
+ (const u8 *) ftie, 2 + ftie->ie_len, ftie->mic);
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_WRONG_MIC) {
wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC");
@@ -2483,8 +2493,9 @@
wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
/* Process MIC check to see if TPK M2 is right */
- if (wpa_supplicant_verify_tdls_mic(2, peer, (u8 *) lnkid,
- (u8 *) timeoutie, ftie) < 0) {
+ if (wpa_supplicant_verify_tdls_mic(2, peer, (const u8 *) lnkid,
+ (const u8 *) timeoutie, ftie,
+ kde.ftie_len) < 0) {
/* Discard the frame */
wpa_tdls_del_key(sm, peer);
wpa_tdls_disable_peer_link(sm, peer);
@@ -2657,8 +2668,9 @@
goto error;
}
- if (wpa_supplicant_verify_tdls_mic(3, peer, (u8 *) lnkid,
- (u8 *) timeoutie, ftie) < 0) {
+ if (wpa_supplicant_verify_tdls_mic(3, peer, (const u8 *) lnkid,
+ (const u8 *) timeoutie, ftie,
+ kde.ftie_len) < 0) {
wpa_tdls_del_key(sm, peer);
goto error;
}
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 21f8b5b..3a39886 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -38,6 +38,45 @@
static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+static void _wpa_hexdump_link(int level, u8 link_id, const char *title,
+ const void *buf, size_t len, bool key)
+{
+ char *link_title = NULL;
+
+ if (link_id >= MAX_NUM_MLD_LINKS)
+ goto out;
+
+ link_title = os_malloc(os_strlen(title) + 20);
+ if (!link_title)
+ goto out;
+
+ os_snprintf(link_title, os_strlen(title) + 20, "MLO link[%u]: %s",
+ link_id, title);
+
+out:
+ if (key)
+ wpa_hexdump_key(level, link_title ? link_title : title, buf,
+ len);
+ else
+ wpa_hexdump(level, link_title ? link_title : title, buf, len);
+ os_free(link_title);
+}
+
+
+static void wpa_hexdump_link(int level, u8 link_id, const char *title,
+ const void *buf, size_t len)
+{
+ _wpa_hexdump_link(level, link_id, title, buf, len, false);
+}
+
+
+static void wpa_hexdump_link_key(int level, u8 link_id, const char *title,
+ const void *buf, size_t len)
+{
+ _wpa_hexdump_link(level, link_id, title, buf, len, true);
+}
+
+
/**
* wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message
* @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -280,7 +319,8 @@
* not have enough time to get the association information
* event before receiving this 1/4 message, so try to find a
* matching PMKSA cache entry here. */
- sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid,
+ sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr,
+ sm->own_addr, pmkid,
NULL, 0);
if (sm->cur_pmksa) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
@@ -396,8 +436,8 @@
fils_cache_id);
}
if (!sm->cur_pmksa && pmkid &&
- pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL,
- 0)) {
+ pmksa_cache_get(sm->pmksa, src_addr, sm->own_addr,
+ pmkid, NULL, 0)) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"RSN: the new PMK matches with the "
"PMKID");
@@ -621,7 +661,7 @@
kdk_len = 0;
ret = wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion",
- sm->own_addr, sm->bssid, sm->snonce,
+ sm->own_addr, wpa_sm_get_auth_addr(sm), sm->snonce,
key->key_nonce, ptk, akmp,
sm->pairwise_cipher, z, z_len,
kdk_len);
@@ -685,6 +725,139 @@
}
+static u8 * rsn_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len)
+{
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = RSN_SELECTOR_LEN + data_len;
+ RSN_SELECTOR_PUT(pos, kde);
+ pos += RSN_SELECTOR_LEN;
+ os_memcpy(pos, data, data_len);
+ pos += data_len;
+
+ return pos;
+}
+
+
+static size_t wpa_mlo_link_kde_len(struct wpa_sm *sm)
+{
+ int i;
+ unsigned int num_links = 0;
+
+ for (i = 0; i < MAX_NUM_MLO_LINKS; i++) {
+ if (sm->mlo.assoc_link_id != i && (sm->mlo.req_links & BIT(i)))
+ num_links++;
+ }
+
+ return num_links * (RSN_SELECTOR_LEN + 1 + ETH_ALEN + 2);
+}
+
+
+static u8 * wpa_mlo_link_kde(struct wpa_sm *sm, u8 *pos)
+{
+ int i;
+ u8 hdr[1 + ETH_ALEN];
+
+ for (i = 0; i < MAX_NUM_MLO_LINKS; i++) {
+ if (sm->mlo.assoc_link_id == i || !(sm->mlo.req_links & BIT(i)))
+ continue;
+
+ wpa_printf(MSG_DEBUG,
+ "MLO: Add MLO Link %d KDE in EAPOL-Key 2/4", i);
+ hdr[0] = i & 0xF; /* LinkID; no RSNE or RSNXE */
+ os_memcpy(&hdr[1], sm->mlo.links[i].addr, ETH_ALEN);
+ pos = rsn_add_kde(pos, RSN_KEY_DATA_MLO_LINK, hdr, sizeof(hdr));
+ }
+
+ return pos;
+}
+
+
+static bool is_valid_ap_mld_mac_kde(struct wpa_sm *sm, const u8 *mac_kde)
+{
+ return mac_kde &&
+ os_memcmp(mac_kde, sm->mlo.ap_mld_addr, ETH_ALEN) == 0;
+}
+
+
+static void wpas_swap_tkip_mic_keys(struct wpa_ptk *ptk)
+{
+ u8 buf[8];
+
+ /* Supplicant: swap tx/rx Mic keys */
+ os_memcpy(buf, &ptk->tk[16], 8);
+ os_memcpy(&ptk->tk[16], &ptk->tk[24], 8);
+ os_memcpy(&ptk->tk[24], buf, 8);
+ forced_memzero(buf, sizeof(buf));
+}
+
+
+static void wpa_supplicant_process_1_of_4_wpa(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ u16 ver, const u8 *key_data,
+ size_t key_data_len,
+ enum frame_encryption encrypted)
+{
+ struct wpa_eapol_ie_parse ie;
+ struct wpa_ptk *ptk;
+ int res;
+
+ if (wpa_sm_get_network_ctx(sm) == NULL) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: No SSID info found (msg 1 of 4)");
+ return;
+ }
+
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: RX message 1 of 4-Way Handshake from " MACSTR
+ " (ver=%d)", MAC2STR(src_addr), ver);
+
+ os_memset(&ie, 0, sizeof(ie));
+
+ res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid);
+ if (res == -2) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: Do not reply to msg 1/4 - requesting full EAP authentication");
+ return;
+ }
+ if (res)
+ goto failed;
+
+ wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
+
+ if (sm->renew_snonce) {
+ if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Failed to get random data for SNonce");
+ goto failed;
+ }
+ sm->renew_snonce = 0;
+ wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce",
+ sm->snonce, WPA_NONCE_LEN);
+ }
+
+ /* Calculate PTK which will be stored as a temporary PTK until it has
+ * been verified when processing message 3/4. */
+ ptk = &sm->tptk;
+ if (wpa_derive_ptk(sm, src_addr, key, ptk) < 0)
+ goto failed;
+ if (sm->pairwise_cipher == WPA_CIPHER_TKIP)
+ wpas_swap_tkip_mic_keys(ptk);
+ sm->tptk_set = 1;
+
+ if (wpa_supplicant_send_2_of_4(sm, wpa_sm_get_auth_addr(sm), key, ver,
+ sm->snonce, sm->assoc_wpa_ie,
+ sm->assoc_wpa_ie_len, ptk) < 0)
+ goto failed;
+
+ os_memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN);
+ return;
+
+failed:
+ wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+}
+
+
static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
const unsigned char *src_addr,
const struct wpa_eapol_key *key,
@@ -697,6 +870,7 @@
int res;
u8 *kde, *kde_buf = NULL;
size_t kde_len;
+ size_t mlo_kde_len = 0;
if (encrypted == FRAME_NOT_ENCRYPTED && sm->tk_set &&
wpa_sm_pmf_enabled(sm)) {
@@ -724,19 +898,22 @@
os_memset(&ie, 0, sizeof(ie));
- if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
- /* RSN: msg 1/4 should contain PMKID for the selected PMK */
- wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data",
- key_data, key_data_len);
- if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0) {
- wpa_printf(MSG_DEBUG,
- "RSN: Discard EAPOL-Key msg 1/4 with invalid IEs/KDEs");
- return;
- }
- if (ie.pmkid) {
- wpa_hexdump(MSG_DEBUG, "RSN: PMKID from "
- "Authenticator", ie.pmkid, PMKID_LEN);
- }
+ /* RSN: msg 1/4 should contain PMKID for the selected PMK */
+ wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", key_data, key_data_len);
+ if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: Discard EAPOL-Key msg 1/4 with invalid IEs/KDEs");
+ return;
+ }
+ if (ie.pmkid) {
+ wpa_hexdump(MSG_DEBUG, "RSN: PMKID from Authenticator",
+ ie.pmkid, PMKID_LEN);
+ }
+
+ if (sm->mlo.valid_links && !is_valid_ap_mld_mac_kde(sm, ie.mac_addr)) {
+ wpa_printf(MSG_INFO,
+ "RSN: Discard EAPOL-Key msg 1/4 with invalid AP MLD MAC address KDE");
+ return;
}
res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid);
@@ -766,23 +943,23 @@
ptk = &sm->tptk;
if (wpa_derive_ptk(sm, src_addr, key, ptk) < 0)
goto failed;
- if (sm->pairwise_cipher == WPA_CIPHER_TKIP) {
- u8 buf[8];
- /* Supplicant: swap tx/rx Mic keys */
- os_memcpy(buf, &ptk->tk[16], 8);
- os_memcpy(&ptk->tk[16], &ptk->tk[24], 8);
- os_memcpy(&ptk->tk[24], buf, 8);
- forced_memzero(buf, sizeof(buf));
- }
+ if (sm->pairwise_cipher == WPA_CIPHER_TKIP)
+ wpas_swap_tkip_mic_keys(ptk);
sm->tptk_set = 1;
+ /* Add MLO Link KDE and MAC KDE in M2 for ML connection */
+ if (sm->mlo.valid_links)
+ mlo_kde_len = wpa_mlo_link_kde_len(sm) +
+ RSN_SELECTOR_LEN + ETH_ALEN + 2;
+
kde = sm->assoc_wpa_ie;
kde_len = sm->assoc_wpa_ie_len;
kde_buf = os_malloc(kde_len +
2 + RSN_SELECTOR_LEN + 3 +
sm->assoc_rsnxe_len +
2 + RSN_SELECTOR_LEN + 1 +
- 2 + RSN_SELECTOR_LEN + 2);
+ 2 + RSN_SELECTOR_LEN + 2 + mlo_kde_len);
+
if (!kde_buf)
goto failed;
os_memcpy(kde_buf, kde, kde_len);
@@ -856,8 +1033,23 @@
}
#endif /* CONFIG_DPP2 */
- if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce,
- kde, kde_len, ptk) < 0)
+ if (sm->mlo.valid_links) {
+ u8 *pos;
+
+ /* Add MAC KDE */
+ wpa_printf(MSG_DEBUG, "MLO: Add MAC KDE into EAPOL-Key 2/4");
+ pos = kde + kde_len;
+ pos = rsn_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->own_addr,
+ ETH_ALEN);
+
+ /* Add MLO Link KDE */
+ wpa_printf(MSG_DEBUG, "Add MLO Link KDE(s) into EAPOL-Key 2/4");
+ pos = wpa_mlo_link_kde(sm, pos);
+ kde_len = pos - kde;
+ }
+
+ if (wpa_supplicant_send_2_of_4(sm, wpa_sm_get_auth_addr(sm), key, ver,
+ sm->snonce, kde, kde_len, ptk) < 0)
goto failed;
os_free(kde_buf);
@@ -979,13 +1171,13 @@
wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, rsclen);
}
- if (wpa_sm_set_key(sm, alg, sm->bssid, sm->keyidx_active, 1, key_rsc,
- rsclen, sm->ptk.tk, keylen,
- KEY_FLAG_PAIRWISE | key_flag) < 0) {
+ if (wpa_sm_set_key(sm, -1, alg, wpa_sm_get_auth_addr(sm),
+ sm->keyidx_active, 1, key_rsc, rsclen, sm->ptk.tk,
+ keylen, KEY_FLAG_PAIRWISE | key_flag) < 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: Failed to set PTK to the driver (alg=%d keylen=%d bssid="
+ "WPA: Failed to set PTK to the driver (alg=%d keylen=%d auth_addr="
MACSTR " idx=%d key_flag=0x%x)",
- alg, keylen, MAC2STR(sm->bssid),
+ alg, keylen, MAC2STR(wpa_sm_get_auth_addr(sm)),
sm->keyidx_active, key_flag);
return -1;
}
@@ -1025,14 +1217,16 @@
static int wpa_supplicant_activate_ptk(struct wpa_sm *sm)
{
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
- "WPA: Activate PTK (idx=%d bssid=" MACSTR ")",
- sm->keyidx_active, MAC2STR(sm->bssid));
+ "WPA: Activate PTK (idx=%d auth_addr=" MACSTR ")",
+ sm->keyidx_active, MAC2STR(wpa_sm_get_auth_addr(sm)));
- if (wpa_sm_set_key(sm, 0, sm->bssid, sm->keyidx_active, 0, NULL, 0,
- NULL, 0, KEY_FLAG_PAIRWISE_RX_TX_MODIFY) < 0) {
+ if (wpa_sm_set_key(sm, -1, 0, wpa_sm_get_auth_addr(sm),
+ sm->keyidx_active, 0, NULL, 0, NULL, 0,
+ KEY_FLAG_PAIRWISE_RX_TX_MODIFY) < 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: Failed to activate PTK for TX (idx=%d bssid="
- MACSTR ")", sm->keyidx_active, MAC2STR(sm->bssid));
+ "WPA: Failed to activate PTK for TX (idx=%d auth_addr="
+ MACSTR ")", sm->keyidx_active,
+ MAC2STR(wpa_sm_get_auth_addr(sm)));
return -1;
}
return 0;
@@ -1107,7 +1301,7 @@
_gtk = gtk_buf;
}
if (sm->pairwise_cipher == WPA_CIPHER_NONE) {
- if (wpa_sm_set_key(sm, gd->alg, NULL,
+ if (wpa_sm_set_key(sm, -1, gd->alg, NULL,
gd->keyidx, 1, key_rsc, gd->key_rsc_len,
_gtk, gd->gtk_len,
KEY_FLAG_GROUP_RX_TX_DEFAULT) < 0) {
@@ -1117,7 +1311,7 @@
forced_memzero(gtk_buf, sizeof(gtk_buf));
return -1;
}
- } else if (wpa_sm_set_key(sm, gd->alg, broadcast_ether_addr,
+ } else if (wpa_sm_set_key(sm, -1, gd->alg, broadcast_ether_addr,
gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len,
_gtk, gd->gtk_len, KEY_FLAG_GROUP_RX) < 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
@@ -1142,6 +1336,56 @@
}
+static int wpa_supplicant_install_mlo_gtk(struct wpa_sm *sm, u8 link_id,
+ const struct wpa_gtk_data *gd,
+ const u8 *key_rsc, int wnm_sleep)
+{
+ const u8 *gtk = gd->gtk;
+
+ /* Detect possible key reinstallation */
+ if ((sm->mlo.links[link_id].gtk.gtk_len == (size_t) gd->gtk_len &&
+ os_memcmp(sm->mlo.links[link_id].gtk.gtk, gd->gtk,
+ sm->mlo.links[link_id].gtk.gtk_len) == 0) ||
+ (sm->mlo.links[link_id].gtk_wnm_sleep.gtk_len ==
+ (size_t) gd->gtk_len &&
+ os_memcmp(sm->mlo.links[link_id].gtk_wnm_sleep.gtk, gd->gtk,
+ sm->mlo.links[link_id].gtk_wnm_sleep.gtk_len) == 0)) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Not reinstalling already in-use GTK to the driver (link_id=%d keyidx=%d tx=%d len=%d)",
+ link_id, gd->keyidx, gd->tx, gd->gtk_len);
+ return 0;
+ }
+
+ wpa_hexdump_link_key(MSG_DEBUG, link_id, "RSN: Group Key", gd->gtk,
+ gd->gtk_len);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Installing GTK to the driver (link_id=%d keyidx=%d tx=%d len=%d)",
+ link_id, gd->keyidx, gd->tx, gd->gtk_len);
+ wpa_hexdump_link(MSG_DEBUG, link_id, "RSN: RSC",
+ key_rsc, gd->key_rsc_len);
+ if (wpa_sm_set_key(sm, link_id, gd->alg, broadcast_ether_addr,
+ gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len, gtk,
+ gd->gtk_len, KEY_FLAG_GROUP_RX) < 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "RSN: Failed to set GTK to the driver (link_id=%d alg=%d keylen=%d keyidx=%d)",
+ link_id, gd->alg, gd->gtk_len, gd->keyidx);
+ return -1;
+ }
+
+ if (wnm_sleep) {
+ sm->mlo.links[link_id].gtk_wnm_sleep.gtk_len = gd->gtk_len;
+ os_memcpy(sm->mlo.links[link_id].gtk_wnm_sleep.gtk, gd->gtk,
+ sm->mlo.links[link_id].gtk_wnm_sleep.gtk_len);
+ } else {
+ sm->mlo.links[link_id].gtk.gtk_len = gd->gtk_len;
+ os_memcpy(sm->mlo.links[link_id].gtk.gtk, gd->gtk,
+ sm->mlo.links[link_id].gtk.gtk_len);
+ }
+
+ return 0;
+}
+
+
static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm,
int tx)
{
@@ -1190,6 +1434,84 @@
}
+static int wpa_supplicant_mlo_gtk(struct wpa_sm *sm, u8 link_id, const u8 *gtk,
+ size_t gtk_len, int key_info)
+{
+ struct wpa_gtk_data gd;
+ const u8 *key_rsc;
+ int ret;
+
+ /*
+ * MLO GTK KDE format:
+ * KeyID[bits 0-1], Tx [bit 2], Reserved [bit 3], link id [4-7]
+ * PN
+ * GTK
+ */
+ os_memset(&gd, 0, sizeof(gd));
+ wpa_hexdump_link_key(MSG_DEBUG, link_id,
+ "RSN: received GTK in pairwise handshake",
+ gtk, gtk_len);
+
+ if (gtk_len < RSN_MLO_GTK_KDE_PREFIX_LENGTH ||
+ gtk_len - RSN_MLO_GTK_KDE_PREFIX_LENGTH > sizeof(gd.gtk))
+ return -1;
+
+ gd.keyidx = gtk[0] & 0x3;
+ gtk += 1;
+ gtk_len -= 1;
+
+ key_rsc = gtk;
+
+ gtk += 6;
+ gtk_len -= 6;
+
+ os_memcpy(gd.gtk, gtk, gtk_len);
+ gd.gtk_len = gtk_len;
+
+ ret = 0;
+ if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gtk_len,
+ gtk_len, &gd.key_rsc_len,
+ &gd.alg) ||
+ wpa_supplicant_install_mlo_gtk(sm, link_id, &gd, key_rsc, 0)) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Failed to install GTK for MLO Link ID %u",
+ link_id);
+ ret = -1;
+ goto out;
+ }
+
+out:
+ forced_memzero(&gd, sizeof(gd));
+ return ret;
+}
+
+
+static int wpa_supplicant_pairwise_mlo_gtk(struct wpa_sm *sm,
+ const struct wpa_eapol_key *key,
+ struct wpa_eapol_ie_parse *ie,
+ int key_info)
+{
+ u8 i;
+
+ for (i = 0; i < MAX_NUM_MLO_LINKS; i++) {
+ if (!(sm->mlo.valid_links & BIT(i)))
+ continue;
+
+ if (!ie->mlo_gtk[i]) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+ "MLO RSN: GTK not found for link ID %u", i);
+ return -1;
+ }
+
+ if (wpa_supplicant_mlo_gtk(sm, i, ie->mlo_gtk[i],
+ ie->mlo_gtk_len[i], key_info))
+ return -1;
+ }
+
+ return 0;
+}
+
+
static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm,
const struct wpa_eapol_key *key,
const u8 *gtk, size_t gtk_len,
@@ -1270,7 +1592,7 @@
"WPA: Invalid IGTK KeyID %d", keyidx);
return -1;
}
- if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+ if (wpa_sm_set_key(sm, -1, wpa_cipher_to_alg(sm->mgmt_group_cipher),
broadcast_ether_addr,
keyidx, 0, igtk->pn, sizeof(igtk->pn),
igtk->igtk, len, KEY_FLAG_GROUP_RX) < 0) {
@@ -1339,7 +1661,7 @@
"WPA: Invalid BIGTK KeyID %d", keyidx);
return -1;
}
- if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+ if (wpa_sm_set_key(sm, -1, wpa_cipher_to_alg(sm->mgmt_group_cipher),
broadcast_ether_addr,
keyidx, 0, bigtk->pn, sizeof(bigtk->pn),
bigtk->bigtk, len, KEY_FLAG_GROUP_RX) < 0) {
@@ -1361,6 +1683,180 @@
}
+static int wpa_supplicant_install_mlo_igtk(struct wpa_sm *sm, u8 link_id,
+ const struct rsn_mlo_igtk_kde *igtk,
+ int wnm_sleep)
+{
+ size_t len = wpa_cipher_key_len(sm->mgmt_group_cipher);
+ u16 keyidx = WPA_GET_LE16(igtk->keyid);
+
+ /* Detect possible key reinstallation */
+ if ((sm->mlo.links[link_id].igtk.igtk_len == len &&
+ os_memcmp(sm->mlo.links[link_id].igtk.igtk, igtk->igtk,
+ sm->mlo.links[link_id].igtk.igtk_len) == 0) ||
+ (sm->mlo.links[link_id].igtk_wnm_sleep.igtk_len == len &&
+ os_memcmp(sm->mlo.links[link_id].igtk_wnm_sleep.igtk, igtk->igtk,
+ sm->mlo.links[link_id].igtk_wnm_sleep.igtk_len) == 0)) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Not reinstalling already in-use IGTK to the driver (link_id=%d keyidx=%d)",
+ link_id, keyidx);
+ return 0;
+ }
+
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: MLO Link %u IGTK keyid %d pn " COMPACT_MACSTR,
+ link_id, keyidx, MAC2STR(igtk->pn));
+ wpa_hexdump_link_key(MSG_DEBUG, link_id, "RSN: IGTK", igtk->igtk, len);
+ if (keyidx > 4095) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "RSN: Invalid MLO Link %d IGTK KeyID %d", link_id,
+ keyidx);
+ return -1;
+ }
+ if (wpa_sm_set_key(sm, link_id,
+ wpa_cipher_to_alg(sm->mgmt_group_cipher),
+ broadcast_ether_addr, keyidx, 0, igtk->pn,
+ sizeof(igtk->pn), igtk->igtk, len,
+ KEY_FLAG_GROUP_RX) < 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "RSN: Failed to configure MLO Link %d IGTK to the driver",
+ link_id);
+ return -1;
+ }
+
+ if (wnm_sleep) {
+ sm->mlo.links[link_id].igtk_wnm_sleep.igtk_len = len;
+ os_memcpy(sm->mlo.links[link_id].igtk_wnm_sleep.igtk,
+ igtk->igtk,
+ sm->mlo.links[link_id].igtk_wnm_sleep.igtk_len);
+ } else {
+ sm->mlo.links[link_id].igtk.igtk_len = len;
+ os_memcpy(sm->mlo.links[link_id].igtk.igtk, igtk->igtk,
+ sm->mlo.links[link_id].igtk.igtk_len);
+ }
+
+ return 0;
+}
+
+
+static int
+wpa_supplicant_install_mlo_bigtk(struct wpa_sm *sm, u8 link_id,
+ const struct rsn_mlo_bigtk_kde *bigtk,
+ int wnm_sleep)
+{
+ size_t len = wpa_cipher_key_len(sm->mgmt_group_cipher);
+ u16 keyidx = WPA_GET_LE16(bigtk->keyid);
+
+ /* Detect possible key reinstallation */
+ if ((sm->mlo.links[link_id].bigtk.bigtk_len == len &&
+ os_memcmp(sm->mlo.links[link_id].bigtk.bigtk, bigtk->bigtk,
+ sm->mlo.links[link_id].bigtk.bigtk_len) == 0) ||
+ (sm->mlo.links[link_id].bigtk_wnm_sleep.bigtk_len == len &&
+ os_memcmp(sm->mlo.links[link_id].bigtk_wnm_sleep.bigtk,
+ bigtk->bigtk,
+ sm->mlo.links[link_id].bigtk_wnm_sleep.bigtk_len) ==
+ 0)) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Not reinstalling already in-use BIGTK to the driver (link_id=%d keyidx=%d)",
+ link_id, keyidx);
+ return 0;
+ }
+
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: MLO Link %u BIGTK keyid %d pn " COMPACT_MACSTR,
+ link_id, keyidx, MAC2STR(bigtk->pn));
+ wpa_hexdump_link_key(MSG_DEBUG, link_id, "RSN: BIGTK", bigtk->bigtk,
+ len);
+ if (keyidx < 6 || keyidx > 7) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "RSN: Invalid MLO Link %d BIGTK KeyID %d", link_id,
+ keyidx);
+ return -1;
+ }
+ if (wpa_sm_set_key(sm, link_id,
+ wpa_cipher_to_alg(sm->mgmt_group_cipher),
+ broadcast_ether_addr, keyidx, 0, bigtk->pn,
+ sizeof(bigtk->pn), bigtk->bigtk, len,
+ KEY_FLAG_GROUP_RX) < 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "RSN: Failed to configure MLO Link %d BIGTK to the driver",
+ link_id);
+ return -1;
+ }
+
+ if (wnm_sleep) {
+ sm->mlo.links[link_id].bigtk_wnm_sleep.bigtk_len = len;
+ os_memcpy(sm->mlo.links[link_id].bigtk_wnm_sleep.bigtk,
+ bigtk->bigtk,
+ sm->mlo.links[link_id].bigtk_wnm_sleep.bigtk_len);
+ } else {
+ sm->mlo.links[link_id].bigtk.bigtk_len = len;
+ os_memcpy(sm->mlo.links[link_id].bigtk.bigtk, bigtk->bigtk,
+ sm->mlo.links[link_id].bigtk.bigtk_len);
+ }
+
+ return 0;
+}
+
+
+static int _mlo_ieee80211w_set_keys(struct wpa_sm *sm, u8 link_id,
+ struct wpa_eapol_ie_parse *ie)
+{
+ size_t len;
+
+ if (ie->mlo_igtk[link_id]) {
+ len = wpa_cipher_key_len(sm->mgmt_group_cipher);
+ if (ie->mlo_igtk_len[link_id] !=
+ RSN_MLO_IGTK_KDE_PREFIX_LENGTH + len)
+ return -1;
+
+ if (wpa_supplicant_install_mlo_igtk(
+ sm, link_id,
+ (const struct rsn_mlo_igtk_kde *)
+ ie->mlo_igtk[link_id],
+ 0) < 0)
+ return -1;
+ }
+
+ if (ie->mlo_bigtk[link_id] && sm->beacon_prot) {
+ len = wpa_cipher_key_len(sm->mgmt_group_cipher);
+ if (ie->mlo_bigtk_len[link_id] !=
+ RSN_MLO_BIGTK_KDE_PREFIX_LENGTH + len)
+ return -1;
+
+ if (wpa_supplicant_install_mlo_bigtk(
+ sm, link_id,
+ (const struct rsn_mlo_bigtk_kde *)
+ ie->mlo_bigtk[link_id],
+ 0) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int mlo_ieee80211w_set_keys(struct wpa_sm *sm,
+ struct wpa_eapol_ie_parse *ie)
+{
+ u8 i;
+
+ if (!wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher) ||
+ sm->mgmt_group_cipher == WPA_CIPHER_GTK_NOT_USED)
+ return 0;
+
+ for (i = 0; i < MAX_NUM_MLO_LINKS; i++) {
+ if (!(sm->mlo.valid_links & BIT(i)))
+ continue;
+
+ if (_mlo_ieee80211w_set_keys(sm, i, ie))
+ return -1;
+ }
+
+ return 0;
+}
+
+
static int ieee80211w_set_keys(struct wpa_sm *sm,
struct wpa_eapol_ie_parse *ie)
{
@@ -1664,13 +2160,32 @@
size_t mic_len, hdrlen, rlen;
struct wpa_eapol_key *reply;
u8 *rbuf, *key_mic;
+ u8 *kde = NULL;
+ size_t kde_len = 0;
+
+ if (sm->mlo.valid_links) {
+ u8 *pos;
+
+ kde = os_malloc(RSN_SELECTOR_LEN + ETH_ALEN + 2);
+ if (!kde)
+ return -1;
+
+ /* Add MAC KDE */
+ wpa_printf(MSG_DEBUG, "MLO: Add MAC KDE into EAPOL-Key 4/4");
+ pos = kde;
+ pos = rsn_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->own_addr,
+ ETH_ALEN);
+ kde_len = pos - kde;
+ }
mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
hdrlen = sizeof(*reply) + mic_len + 2;
rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
- hdrlen, &rlen, (void *) &reply);
- if (rbuf == NULL)
+ hdrlen + kde_len, &rlen, (void *) &reply);
+ if (!rbuf) {
+ os_free(kde);
return -1;
+ }
reply->type = (sm->proto == WPA_PROTO_RSN ||
sm->proto == WPA_PROTO_OSEN) ?
@@ -1690,7 +2205,11 @@
WPA_REPLAY_COUNTER_LEN);
key_mic = (u8 *) (reply + 1);
- WPA_PUT_BE16(key_mic + mic_len, 0);
+ WPA_PUT_BE16(key_mic + mic_len, kde_len); /* Key Data length */
+ if (kde) {
+ os_memcpy(key_mic + mic_len + 2, kde, kde_len); /* Key Data */
+ os_free(kde);
+ }
wpa_dbg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Sending EAPOL-Key 4/4");
return wpa_eapol_key_send(sm, ptk, ver, dst, ETH_P_EAPOL, rbuf, rlen,
@@ -1698,6 +2217,201 @@
}
+static int wpa_supplicant_validate_link_kde(struct wpa_sm *sm, u8 link_id,
+ const u8 *link_kde,
+ size_t link_kde_len)
+{
+ size_t rsne_len = 0, rsnxe_len = 0;
+ const u8 *rsne = NULL, *rsnxe = NULL;
+
+ if (!link_kde ||
+ link_kde_len < RSN_MLO_LINK_KDE_LINK_MAC_INDEX + ETH_ALEN) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: MLO Link KDE is not found for link ID %d",
+ link_id);
+ return -1;
+ }
+
+ if (os_memcmp(sm->mlo.links[link_id].bssid,
+ &link_kde[RSN_MLO_LINK_KDE_LINK_MAC_INDEX],
+ ETH_ALEN) != 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: MLO Link %u MAC address (" MACSTR
+ ") not matching association response (" MACSTR ")",
+ link_id,
+ MAC2STR(&link_kde[RSN_MLO_LINK_KDE_LINK_MAC_INDEX]),
+ MAC2STR(sm->mlo.links[link_id].bssid));
+ return -1;
+ }
+
+ if (link_kde[0] & RSN_MLO_LINK_KDE_LI_RSNE_INFO) {
+ rsne = link_kde + RSN_MLO_LINK_KDE_FIXED_LENGTH;
+ if (link_kde_len < RSN_MLO_LINK_KDE_FIXED_LENGTH + 2 ||
+ link_kde_len <
+ (size_t) (RSN_MLO_LINK_KDE_FIXED_LENGTH + 2 + rsne[1])) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: No room for link %u RSNE in MLO Link KDE",
+ link_id);
+ return -1;
+ }
+
+ rsne_len = rsne[1] + 2;
+ }
+
+ if (!rsne) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: RSNE not present in MLO Link %u KDE", link_id);
+ return -1;
+ }
+
+ if (link_kde[0] & RSN_MLO_LINK_KDE_LI_RSNXE_INFO) {
+ rsnxe = link_kde + RSN_MLO_LINK_KDE_FIXED_LENGTH + rsne_len;
+ if (link_kde_len <
+ (RSN_MLO_LINK_KDE_FIXED_LENGTH + rsne_len + 2) ||
+ link_kde_len <
+ (RSN_MLO_LINK_KDE_FIXED_LENGTH + rsne_len + 2 + rsnxe[1])) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: No room for link %u RSNXE in MLO Link KDE",
+ link_id);
+ return -1;
+ }
+
+ rsnxe_len = rsnxe[1] + 2;
+ }
+
+ if (wpa_compare_rsn_ie(wpa_key_mgmt_ft(sm->key_mgmt),
+ sm->mlo.links[link_id].ap_rsne,
+ sm->mlo.links[link_id].ap_rsne_len,
+ rsne, rsne_len)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN MLO: IE in 3/4 msg does not match with IE in Beacon/ProbeResp for link ID %u",
+ link_id);
+ wpa_hexdump(MSG_INFO, "RSNE in Beacon/ProbeResp",
+ sm->mlo.links[link_id].ap_rsne,
+ sm->mlo.links[link_id].ap_rsne_len);
+ wpa_hexdump(MSG_INFO, "RSNE in EAPOL-Key msg 3/4",
+ rsne, rsne_len);
+ return -1;
+ }
+
+ if ((sm->mlo.links[link_id].ap_rsnxe && !rsnxe) ||
+ (!sm->mlo.links[link_id].ap_rsnxe && rsnxe) ||
+ (sm->mlo.links[link_id].ap_rsnxe && rsnxe &&
+ (sm->mlo.links[link_id].ap_rsnxe_len != rsnxe_len ||
+ os_memcmp(sm->mlo.links[link_id].ap_rsnxe, rsnxe,
+ sm->mlo.links[link_id].ap_rsnxe_len) != 0))) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN MLO: RSNXE mismatch between Beacon/ProbeResp and EAPOL-Key msg 3/4 for link ID %u",
+ link_id);
+ wpa_hexdump(MSG_INFO, "RSNXE in Beacon/ProbeResp",
+ sm->mlo.links[link_id].ap_rsnxe,
+ sm->mlo.links[link_id].ap_rsnxe_len);
+ wpa_hexdump(MSG_INFO, "RSNXE in EAPOL-Key msg 3/4",
+ rsnxe, rsnxe_len);
+ wpa_sm_deauthenticate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_validate_mlo_ieee80211w_kdes(struct wpa_sm *sm,
+ u8 link_id,
+ struct wpa_eapol_ie_parse *ie)
+{
+ if (ie->mlo_igtk[link_id] &&
+ ie->mlo_igtk_len[link_id] != RSN_MLO_IGTK_KDE_PREFIX_LENGTH +
+ (unsigned int) wpa_cipher_key_len(sm->mgmt_group_cipher)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN MLO: Invalid IGTK KDE length %lu for link ID %u",
+ (unsigned long) ie->mlo_igtk_len, link_id);
+ return -1;
+ }
+
+ if (!sm->beacon_prot)
+ return 0;
+
+ if (ie->mlo_bigtk[link_id] &&
+ ie->mlo_bigtk_len[link_id] != RSN_MLO_BIGTK_KDE_PREFIX_LENGTH +
+ (unsigned int) wpa_cipher_key_len(sm->mgmt_group_cipher)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "RSN MLO: Invalid BIGTK KDE length %lu for link ID %u",
+ (unsigned long) ie->mlo_bigtk_len, link_id);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void wpa_supplicant_process_3_of_4_wpa(struct wpa_sm *sm,
+ const struct wpa_eapol_key *key,
+ u16 ver, const u8 *key_data,
+ size_t key_data_len)
+{
+ u16 key_info, keylen;
+ struct wpa_eapol_ie_parse ie;
+
+ wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: RX message 3 of 4-Way Handshake from " MACSTR
+ " (ver=%d)", MAC2STR(sm->bssid), ver);
+
+ key_info = WPA_GET_BE16(key->key_info);
+
+ wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", key_data, key_data_len);
+ if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
+ goto failed;
+
+ if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0)
+ goto failed;
+
+ if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: ANonce from message 1 of 4-Way Handshake differs from 3 of 4-Way Handshake - drop packet (src="
+ MACSTR ")", MAC2STR(sm->bssid));
+ goto failed;
+ }
+
+ keylen = WPA_GET_BE16(key->key_length);
+ if (keylen != wpa_cipher_key_len(sm->pairwise_cipher)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Invalid %s key length %d (src=" MACSTR ")",
+ wpa_cipher_txt(sm->pairwise_cipher), keylen,
+ MAC2STR(sm->bssid));
+ goto failed;
+ }
+
+ if (wpa_supplicant_send_4_of_4(sm, wpa_sm_get_auth_addr(sm), key, ver,
+ key_info, &sm->ptk) < 0)
+ goto failed;
+
+ /* SNonce was successfully used in msg 3/4, so mark it to be renewed
+ * for the next 4-Way Handshake. If msg 3 is received again, the old
+ * SNonce will still be used to avoid changing PTK. */
+ sm->renew_snonce = 1;
+
+ if ((key_info & WPA_KEY_INFO_INSTALL) &&
+ wpa_supplicant_install_ptk(sm, key, KEY_FLAG_RX_TX))
+ goto failed;
+
+ if (key_info & WPA_KEY_INFO_SECURE) {
+ wpa_sm_mlme_setprotection(
+ sm, sm->bssid, MLME_SETPROTECTION_PROTECT_TYPE_RX,
+ MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
+ eapol_sm_notify_portValid(sm->eapol, true);
+ }
+ wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
+
+ sm->msg_3_of_4_ok = 1;
+ return;
+
+failed:
+ wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+}
+
+
static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
const struct wpa_eapol_key *key,
u16 ver, const u8 *key_data,
@@ -1705,28 +2419,83 @@
{
u16 key_info, keylen;
struct wpa_eapol_ie_parse ie;
+ bool mlo = sm->mlo.valid_links;
+ int i;
wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
- wpa_dbg(sm->ctx->msg_ctx, MSG_INFO, "WPA: RX message 3 of 4-Way "
- "Handshake from " MACSTR " (ver=%d)", MAC2STR(sm->bssid), ver);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: RX message 3 of 4-Way Handshake from " MACSTR
+ " (ver=%d)%s", MAC2STR(sm->bssid), ver, mlo ? " (MLO)" : "");
key_info = WPA_GET_BE16(key->key_info);
wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", key_data, key_data_len);
if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
goto failed;
- if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+
+ if (mlo && !ie.valid_mlo_gtks) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "MLO RSN: No GTK KDE included in EAPOL-Key msg 3/4");
+ goto failed;
+ }
+ if (mlo &&
+ (key_info &
+ (WPA_KEY_INFO_ENCR_KEY_DATA | WPA_KEY_INFO_INSTALL |
+ WPA_KEY_INFO_SECURE)) !=
+ (WPA_KEY_INFO_ENCR_KEY_DATA | WPA_KEY_INFO_INSTALL |
+ WPA_KEY_INFO_SECURE)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "RSN MLO: Invalid key info (0x%x) in EAPOL-Key msg 3/4",
+ key_info);
+ goto failed;
+ }
+
+ if (mlo && !is_valid_ap_mld_mac_kde(sm, ie.mac_addr)) {
+ wpa_printf(MSG_DEBUG, "RSN: Invalid AP MLD MAC address KDE");
+ goto failed;
+ }
+
+ for (i = 0; mlo && i < MAX_NUM_MLD_LINKS; i++) {
+ if (!(sm->mlo.req_links & BIT(i)))
+ continue;
+
+ if (wpa_supplicant_validate_link_kde(sm, i, ie.mlo_link[i],
+ ie.mlo_link_len[i]) < 0)
+ goto failed;
+
+ if (!(sm->mlo.valid_links & BIT(i)))
+ continue;
+
+ if (!ie.mlo_gtk[i]) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+ "RSN: GTK not found for link ID %u", i);
+ goto failed;
+ }
+
+ if (sm->mgmt_group_cipher != WPA_CIPHER_GTK_NOT_USED &&
+ wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher) &&
+ wpa_validate_mlo_ieee80211w_kdes(sm, i, &ie) < 0)
+ goto failed;
+ }
+
+#ifdef CONFIG_IEEE80211R
+ if (mlo && wpa_key_mgmt_ft(sm->key_mgmt) &&
+ wpa_supplicant_validate_ie_ft(sm, sm->bssid, &ie) < 0)
+ goto failed;
+#endif /* CONFIG_IEEE80211R */
+
+ if (!mlo && ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: GTK IE in unencrypted key data");
goto failed;
}
- if (ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ if (!mlo && ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: IGTK KDE in unencrypted key data");
goto failed;
}
- if (ie.igtk &&
+ if (!mlo && 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 +
@@ -1737,7 +2506,7 @@
goto failed;
}
- if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0)
+ if (!mlo && wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0)
goto failed;
if (wpa_handle_ext_key_id(sm, &ie))
@@ -1807,10 +2576,9 @@
wpa_supplicant_install_ptk(sm, key, KEY_FLAG_RX))
goto failed;
- if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info,
- &sm->ptk) < 0) {
+ if (wpa_supplicant_send_4_of_4(sm, wpa_sm_get_auth_addr(sm), key, ver,
+ key_info, &sm->ptk) < 0)
goto failed;
- }
/* SNonce was successfully used in msg 3/4, so mark it to be renewed
* for the next 4-Way Handshake. If msg 3 is received again, the old
@@ -1837,7 +2605,14 @@
}
wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
- if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED) {
+ if (mlo) {
+ if (wpa_supplicant_pairwise_mlo_gtk(sm, key, &ie,
+ key_info) < 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "MLO RSN: Failed to configure MLO GTKs");
+ goto failed;
+ }
+ } else if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED) {
/* No GTK to be set to the driver */
} else if (!ie.gtk && sm->proto == WPA_PROTO_RSN) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
@@ -1851,17 +2626,18 @@
goto failed;
}
- if (ieee80211w_set_keys(sm, &ie) < 0) {
+ if ((mlo && mlo_ieee80211w_set_keys(sm, &ie) < 0) ||
+ (!mlo && ieee80211w_set_keys(sm, &ie) < 0)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"RSN: Failed to configure IGTK");
goto failed;
}
- if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED || ie.gtk)
+ if (mlo || sm->group_cipher == WPA_CIPHER_GTK_NOT_USED || ie.gtk)
wpa_supplicant_key_neg_complete(sm, sm->bssid,
key_info & WPA_KEY_INFO_SECURE);
- if (ie.gtk)
+ if (mlo || ie.gtk)
wpa_sm_set_rekey_offload(sm);
/* Add PMKSA cache entry for Suite B AKMs here since PMKID can be
@@ -1875,7 +2651,7 @@
sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, NULL,
sm->ptk.kck, sm->ptk.kck_len,
- sm->bssid, sm->own_addr,
+ wpa_sm_get_auth_addr(sm), sm->own_addr,
sm->network_ctx, sm->key_mgmt, NULL);
if (!sm->cur_pmksa)
sm->cur_pmksa = sa;
@@ -1891,173 +2667,6 @@
}
-static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm,
- const u8 *keydata,
- size_t keydatalen,
- u16 key_info,
- struct wpa_gtk_data *gd)
-{
- int maxkeylen;
- struct wpa_eapol_ie_parse ie;
- u16 gtk_len;
-
- wpa_hexdump_key(MSG_DEBUG, "RSN: msg 1/2 key data",
- keydata, keydatalen);
- if (wpa_supplicant_parse_ies(keydata, keydatalen, &ie) < 0)
- return -1;
- if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: GTK IE in unencrypted key data");
- return -1;
- }
- if (ie.gtk == NULL) {
- wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "WPA: No GTK IE in Group Key msg 1/2");
- return -1;
- }
- gtk_len = ie.gtk_len;
- if (gtk_len < 2) {
- wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "RSN: Invalid GTK KDE length (%u) in Group Key msg 1/2",
- gtk_len);
- return -1;
- }
- gtk_len -= 2;
- if (gtk_len > sizeof(gd->gtk)) {
- wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "RSN: Too long GTK in GTK KDE (len=%u)", gtk_len);
- return -1;
- }
- maxkeylen = gd->gtk_len = gtk_len;
-
-#ifdef CONFIG_OCV
- if (wpa_sm_ocv_enabled(sm)) {
- struct wpa_channel_info ci;
-
- if (wpa_sm_channel_info(sm, &ci) != 0) {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "Failed to get channel info to validate received OCI in EAPOL-Key group msg 1/2");
- return -1;
- }
-
- if (ocv_verify_tx_params(ie.oci, ie.oci_len, &ci,
- channel_width_to_int(ci.chanwidth),
- ci.seg1_idx) != OCI_SUCCESS) {
- wpa_msg(sm->ctx->msg_ctx, MSG_INFO, OCV_FAILURE
- "addr=" MACSTR " frame=eapol-key-g1 error=%s",
- MAC2STR(sm->bssid), ocv_errorstr);
- return -1;
- }
- }
-#endif /* CONFIG_OCV */
-
- if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
- gtk_len, maxkeylen,
- &gd->key_rsc_len, &gd->alg))
- return -1;
-
- wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in group key handshake",
- ie.gtk, 2 + gtk_len);
- gd->keyidx = ie.gtk[0] & 0x3;
- gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
- !!(ie.gtk[0] & BIT(2)));
- os_memcpy(gd->gtk, ie.gtk + 2, gtk_len);
-
- if (ieee80211w_set_keys(sm, &ie) < 0)
- wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "RSN: Failed to configure IGTK");
-
- return 0;
-}
-
-
-static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm,
- const struct wpa_eapol_key *key,
- const u8 *key_data,
- size_t key_data_len, u16 key_info,
- u16 ver, struct wpa_gtk_data *gd)
-{
- size_t maxkeylen;
- u16 gtk_len;
-
- gtk_len = WPA_GET_BE16(key->key_length);
- maxkeylen = key_data_len;
- if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
- if (maxkeylen < 8) {
- wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "WPA: Too short maxkeylen (%lu)",
- (unsigned long) maxkeylen);
- return -1;
- }
- maxkeylen -= 8;
- }
-
- if (gtk_len > maxkeylen ||
- wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
- gtk_len, maxkeylen,
- &gd->key_rsc_len, &gd->alg))
- return -1;
-
- gd->gtk_len = gtk_len;
- gd->keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
- WPA_KEY_INFO_KEY_INDEX_SHIFT;
- if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) {
-#ifdef CONFIG_NO_RC4
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: RC4 not supported in the build");
- return -1;
-#else /* CONFIG_NO_RC4 */
- u8 ek[32];
- if (key_data_len > sizeof(gd->gtk)) {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: RC4 key data too long (%lu)",
- (unsigned long) key_data_len);
- return -1;
- }
- os_memcpy(ek, key->key_iv, 16);
- os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len);
- os_memcpy(gd->gtk, key_data, key_data_len);
- if (rc4_skip(ek, 32, 256, gd->gtk, key_data_len)) {
- forced_memzero(ek, sizeof(ek));
- wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
- "WPA: RC4 failed");
- return -1;
- }
- forced_memzero(ek, sizeof(ek));
-#endif /* CONFIG_NO_RC4 */
- } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
- if (maxkeylen % 8) {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: Unsupported AES-WRAP len %lu",
- (unsigned long) maxkeylen);
- return -1;
- }
- if (maxkeylen > sizeof(gd->gtk)) {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: AES-WRAP key data "
- "too long (keydatalen=%lu maxkeylen=%lu)",
- (unsigned long) key_data_len,
- (unsigned long) maxkeylen);
- return -1;
- }
- if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, maxkeylen / 8,
- key_data, gd->gtk)) {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: AES unwrap failed - could not decrypt "
- "GTK");
- return -1;
- }
- } else {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: Unsupported key_info type %d", ver);
- return -1;
- }
- gd->tx = wpa_supplicant_gtk_tx_bit_workaround(
- sm, !!(key_info & WPA_KEY_INFO_TXRX));
- return 0;
-}
-
-
static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm,
const struct wpa_eapol_key *key,
int ver, u16 key_info)
@@ -2141,19 +2750,121 @@
}
-static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
- const unsigned char *src_addr,
- const struct wpa_eapol_key *key,
- const u8 *key_data,
- size_t key_data_len, u16 ver)
+static void wpa_supplicant_process_mlo_1_of_2(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ const u8 *key_data,
+ size_t key_data_len, u16 ver)
{
u16 key_info;
- int rekey, ret;
- struct wpa_gtk_data gd;
- const u8 *key_rsc;
+ u8 i;
+ struct wpa_eapol_ie_parse ie;
if (!sm->msg_3_of_4_ok && !wpa_fils_is_completed(sm)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "MLO RSN: Group Key Handshake started prior to completion of 4-way handshake");
+ goto failed;
+ }
+
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "MLO RSN: RX message 1 of Group "
+ "Key Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr),
+ ver);
+
+ key_info = WPA_GET_BE16(key->key_info);
+
+ wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
+
+ wpa_hexdump_key(MSG_DEBUG, "MLO RSN: msg 1/2 key data", key_data,
+ key_data_len);
+ if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
+ goto failed;
+
+ if (!ie.valid_mlo_gtks) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "MLO RSN: No MLO GTK KDE in Group Key msg 1/2");
+ goto failed;
+ }
+
+ if (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "MLO RSN: MLO GTK KDE in unencrypted key data");
+ goto failed;
+ }
+
+#ifdef CONFIG_OCV
+ if (wpa_sm_ocv_enabled(sm)) {
+ struct wpa_channel_info ci;
+
+ if (wpa_sm_channel_info(sm, &ci) != 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "Failed to get channel info to validate received OCI in EAPOL-Key group msg 1/2");
+ goto failed;
+ }
+
+ if (ocv_verify_tx_params(ie.oci, ie.oci_len, &ci,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx) != OCI_SUCCESS) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, OCV_FAILURE
+ "addr=" MACSTR " frame=eapol-key-g1 error=%s",
+ MAC2STR(sm->bssid), ocv_errorstr);
+ goto failed;
+ }
+ }
+#endif /* CONFIG_OCV */
+
+ if (mlo_ieee80211w_set_keys(sm, &ie) < 0)
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "MLO RSN: Failed to configure MLO IGTK");
+
+ for (i = 0; i < MAX_NUM_MLO_LINKS; i++) {
+ if (!(sm->mlo.valid_links & BIT(i)))
+ continue;
+
+ /*
+ * AP may send group keys for subset of the all links during
+ * rekey
+ */
+ if (!ie.mlo_gtk[i])
+ continue;
+
+ if (wpa_supplicant_mlo_gtk(sm, i, ie.mlo_gtk[i],
+ ie.mlo_gtk_len[i], key_info))
+ goto failed;
+ }
+
+ if (wpa_supplicant_send_2_of_2(sm, key, ver, key_info) < 0)
+ goto failed;
+
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "MLO RSN: Group rekeying completed "
+ "with " MACSTR " [GTK=%s]", MAC2STR(sm->mlo.ap_mld_addr),
+ wpa_cipher_txt(sm->group_cipher));
+ wpa_sm_cancel_auth_timeout(sm);
+ wpa_sm_set_state(sm, WPA_COMPLETED);
+
+ wpa_sm_set_rekey_offload(sm);
+
+ return;
+
+failed:
+ wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+}
+
+
+static void wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ const u8 *key_data,
+ size_t key_data_len, u16 ver)
+{
+ u16 key_info;
+ int rekey;
+ struct wpa_gtk_data gd;
+ const u8 *key_rsc;
+ size_t maxkeylen;
+ u16 gtk_len;
+
+ if (!sm->msg_3_of_4_ok) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: Group Key Handshake started prior to completion of 4-way handshake");
goto failed;
}
@@ -2161,25 +2872,88 @@
os_memset(&gd, 0, sizeof(gd));
rekey = wpa_sm_get_state(sm) == WPA_COMPLETED;
- wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of Group Key "
- "Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: RX message 1 of Group Key Handshake from " MACSTR
+ " (ver=%d)", MAC2STR(src_addr), ver);
key_info = WPA_GET_BE16(key->key_info);
- if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
- ret = wpa_supplicant_process_1_of_2_rsn(sm, key_data,
- key_data_len, key_info,
- &gd);
- } else {
- ret = wpa_supplicant_process_1_of_2_wpa(sm, key, key_data,
- key_data_len,
- key_info, ver, &gd);
+ gtk_len = WPA_GET_BE16(key->key_length);
+ maxkeylen = key_data_len;
+ if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ if (maxkeylen < 8) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Too short maxkeylen (%lu)",
+ (unsigned long) maxkeylen);
+ goto failed;
+ }
+ maxkeylen -= 8;
}
+ if (gtk_len > maxkeylen ||
+ wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
+ gtk_len, maxkeylen,
+ &gd.key_rsc_len, &gd.alg))
+ goto failed;
+
wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
- if (ret)
+ gd.gtk_len = gtk_len;
+ gd.keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
+ WPA_KEY_INFO_KEY_INDEX_SHIFT;
+ if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) {
+#if defined(CONFIG_NO_RC4) || defined(CONFIG_FIPS)
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: RC4 not supported in the build");
goto failed;
+#else /* CONFIG_NO_RC4 || CONFIG_FIPS */
+ u8 ek[32];
+ if (key_data_len > sizeof(gd.gtk)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: RC4 key data too long (%lu)",
+ (unsigned long) key_data_len);
+ goto failed;
+ }
+ os_memcpy(ek, key->key_iv, 16);
+ os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len);
+ os_memcpy(gd.gtk, key_data, key_data_len);
+ if (rc4_skip(ek, 32, 256, gd.gtk, key_data_len)) {
+ forced_memzero(ek, sizeof(ek));
+ wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+ "WPA: RC4 failed");
+ goto failed;
+ }
+ forced_memzero(ek, sizeof(ek));
+#endif /* CONFIG_NO_RC4 || CONFIG_FIPS */
+ } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ if (maxkeylen % 8) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported AES-WRAP len %lu",
+ (unsigned long) maxkeylen);
+ goto failed;
+ }
+ if (maxkeylen > sizeof(gd.gtk)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: AES-WRAP key data "
+ "too long (keydatalen=%lu maxkeylen=%lu)",
+ (unsigned long) key_data_len,
+ (unsigned long) maxkeylen);
+ goto failed;
+ }
+ if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, maxkeylen / 8,
+ key_data, gd.gtk)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: AES unwrap failed - could not decrypt "
+ "GTK");
+ goto failed;
+ }
+ } else {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported key_info type %d", ver);
+ goto failed;
+ }
+ gd.tx = wpa_supplicant_gtk_tx_bit_workaround(
+ sm, !!(key_info & WPA_KEY_INFO_TXRX));
key_rsc = key->key_rsc;
if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc))
@@ -2191,15 +2965,15 @@
forced_memzero(&gd, sizeof(gd));
if (rekey) {
- wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Group rekeying "
- "completed with " MACSTR " [GTK=%s]",
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Group rekeying completed with " MACSTR
+ " [GTK=%s]",
MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher));
wpa_sm_cancel_auth_timeout(sm);
wpa_sm_set_state(sm, WPA_COMPLETED);
} else {
wpa_supplicant_key_neg_complete(sm, sm->bssid,
- key_info &
- WPA_KEY_INFO_SECURE);
+ key_info & WPA_KEY_INFO_SECURE);
}
wpa_sm_set_rekey_offload(sm);
@@ -2212,6 +2986,127 @@
}
+static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ const u8 *key_data,
+ size_t key_data_len, u16 ver)
+{
+ u16 key_info;
+ struct wpa_gtk_data gd;
+ const u8 *key_rsc;
+ int maxkeylen;
+ struct wpa_eapol_ie_parse ie;
+ u16 gtk_len;
+
+ if (!sm->msg_3_of_4_ok && !wpa_fils_is_completed(sm)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Group Key Handshake started prior to completion of 4-way handshake");
+ goto failed;
+ }
+
+ os_memset(&gd, 0, sizeof(gd));
+
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: RX message 1 of Group Key Handshake from " MACSTR
+ " (ver=%d)", MAC2STR(src_addr), ver);
+
+ key_info = WPA_GET_BE16(key->key_info);
+
+ wpa_hexdump_key(MSG_DEBUG, "RSN: msg 1/2 key data",
+ key_data, key_data_len);
+ if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
+ goto failed;
+
+ wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
+
+ if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "RSN: GTK KDE in unencrypted key data");
+ goto failed;
+ }
+ if (!ie.gtk) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: No GTK KDE in Group Key msg 1/2");
+ goto failed;
+ }
+ gtk_len = ie.gtk_len;
+ if (gtk_len < 2) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Invalid GTK KDE length (%u) in Group Key msg 1/2",
+ gtk_len);
+ goto failed;
+ }
+ gtk_len -= 2;
+ if (gtk_len > sizeof(gd.gtk)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Too long GTK in GTK KDE (len=%u)", gtk_len);
+ goto failed;
+ }
+ maxkeylen = gd.gtk_len = gtk_len;
+
+#ifdef CONFIG_OCV
+ if (wpa_sm_ocv_enabled(sm)) {
+ struct wpa_channel_info ci;
+
+ if (wpa_sm_channel_info(sm, &ci) != 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "Failed to get channel info to validate received OCI in EAPOL-Key group msg 1/2");
+ goto failed;
+ }
+
+ if (ocv_verify_tx_params(ie.oci, ie.oci_len, &ci,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx) != OCI_SUCCESS) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, OCV_FAILURE
+ "addr=" MACSTR " frame=eapol-key-g1 error=%s",
+ MAC2STR(sm->bssid), ocv_errorstr);
+ goto failed;
+ }
+ }
+#endif /* CONFIG_OCV */
+
+ if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
+ gtk_len, maxkeylen,
+ &gd.key_rsc_len, &gd.alg))
+ goto failed;
+
+ wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in group key handshake",
+ ie.gtk, 2 + gtk_len);
+ gd.keyidx = ie.gtk[0] & 0x3;
+ gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
+ !!(ie.gtk[0] & BIT(2)));
+ os_memcpy(gd.gtk, ie.gtk + 2, gtk_len);
+
+ if (ieee80211w_set_keys(sm, &ie) < 0)
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Failed to configure IGTK");
+
+ key_rsc = key->key_rsc;
+ if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc))
+ key_rsc = null_rsc;
+
+ if (wpa_supplicant_install_gtk(sm, &gd, key_rsc, 0) ||
+ wpa_supplicant_send_2_of_2(sm, key, ver, key_info) < 0)
+ goto failed;
+ forced_memzero(&gd, sizeof(gd));
+
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Group rekeying completed with " MACSTR " [GTK=%s]",
+ MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher));
+ wpa_sm_cancel_auth_timeout(sm);
+ wpa_sm_set_state(sm, WPA_COMPLETED);
+
+ wpa_sm_set_rekey_offload(sm);
+
+ return;
+
+failed:
+ forced_memzero(&gd, sizeof(gd));
+ wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+}
+
+
static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
struct wpa_eapol_key *key,
u16 ver,
@@ -2310,11 +3205,11 @@
/* Decrypt key data here so that this operation does not need
* to be implemented separately for each message type. */
if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) {
-#ifdef CONFIG_NO_RC4
+#if defined(CONFIG_NO_RC4) || defined(CONFIG_FIPS)
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: RC4 not supported in the build");
return -1;
-#else /* CONFIG_NO_RC4 */
+#else /* CONFIG_NO_RC4 || CONFIG_FIPS */
u8 ek[32];
wpa_printf(MSG_DEBUG, "WPA: Decrypt Key Data using RC4");
@@ -2327,7 +3222,7 @@
return -1;
}
forced_memzero(ek, sizeof(ek));
-#endif /* CONFIG_NO_RC4 */
+#endif /* CONFIG_NO_RC4 || CONFIG_FIPS */
} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
ver == WPA_KEY_INFO_TYPE_AES_128_CMAC ||
wpa_use_aes_key_wrap(sm->key_mgmt)) {
@@ -2513,6 +3408,88 @@
#endif /* CONFIG_FILS */
+static int wpa_sm_rx_eapol_wpa(struct wpa_sm *sm, const u8 *src_addr,
+ struct wpa_eapol_key *key,
+ enum frame_encryption encrypted,
+ const u8 *tmp, size_t data_len,
+ u8 *key_data, size_t key_data_len)
+{
+ u16 key_info, ver;
+
+ key_info = WPA_GET_BE16(key->key_info);
+
+ if (key->type != EAPOL_KEY_TYPE_WPA) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Unsupported EAPOL-Key type %d", key->type);
+ return -1;
+ }
+
+ ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+ if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
+ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Unsupported EAPOL-Key descriptor version %d",
+ ver);
+ return -1;
+ }
+
+ if (sm->pairwise_cipher == WPA_CIPHER_CCMP &&
+ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: CCMP is used, but EAPOL-Key descriptor version (%d) is not 2",
+ ver);
+ if (sm->group_cipher != WPA_CIPHER_CCMP &&
+ !(key_info & WPA_KEY_INFO_KEY_TYPE)) {
+ /* Earlier versions of IEEE 802.11i did not explicitly
+ * require version 2 descriptor for all EAPOL-Key
+ * packets, so allow group keys to use version 1 if
+ * CCMP is not used for them. */
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Backwards compatibility: allow invalid version for non-CCMP group keys");
+ } else
+ return -1;
+ }
+
+ if ((key_info & WPA_KEY_INFO_MIC) &&
+ wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len))
+ return -1;
+
+ if (key_info & WPA_KEY_INFO_KEY_TYPE) {
+ if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Ignored EAPOL-Key (Pairwise) with non-zero key index");
+ return -1;
+ }
+ if (key_info & (WPA_KEY_INFO_MIC |
+ WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ /* 3/4 4-Way Handshake */
+ wpa_supplicant_process_3_of_4_wpa(sm, key, ver,
+ key_data,
+ key_data_len);
+ } else {
+ /* 1/4 4-Way Handshake */
+ wpa_supplicant_process_1_of_4_wpa(sm, src_addr, key,
+ ver, key_data,
+ key_data_len,
+ encrypted);
+ }
+ } else {
+ if (key_info & WPA_KEY_INFO_MIC) {
+ /* 1/2 Group Key Handshake */
+ wpa_supplicant_process_1_of_2_wpa(sm, src_addr, key,
+ key_data,
+ key_data_len,
+ ver);
+ } else {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: EAPOL-Key (Group) without Mic/Encr bit - dropped");
+ }
+ }
+
+ return 1;
+}
+
+
/**
* wpa_sm_rx_eapol - Process received WPA EAPOL frames
* @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -2624,19 +3601,77 @@
goto out;
}
+ if (sm->rx_replay_counter_set &&
+ os_memcmp(key->replay_counter, sm->rx_replay_counter,
+ WPA_REPLAY_COUNTER_LEN) <= 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: EAPOL-Key Replay Counter did not increase - dropping packet");
+ goto out;
+ }
+
eapol_sm_notify_lower_layer_success(sm->eapol, 0);
+
key_info = WPA_GET_BE16(key->key_info);
+
+ if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Unsupported SMK bit in key_info");
+ goto out;
+ }
+
+ if (!(key_info & WPA_KEY_INFO_ACK)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: No Ack bit in key_info");
+ goto out;
+ }
+
+ if (key_info & WPA_KEY_INFO_REQUEST) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: EAPOL-Key with Request bit - dropped");
+ goto out;
+ }
+
+ if (sm->proto == WPA_PROTO_WPA) {
+ ret = wpa_sm_rx_eapol_wpa(sm, src_addr, key, encrypted,
+ tmp, data_len,
+ key_data, key_data_len);
+ goto out;
+ }
+
+ if (key->type != EAPOL_KEY_TYPE_RSN) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Unsupported EAPOL-Key type %d", key->type);
+ goto out;
+ }
+
ver = key_info & WPA_KEY_INFO_TYPE_MASK;
if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
!wpa_use_akm_defined(sm->key_mgmt)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "WPA: Unsupported EAPOL-Key descriptor version %d",
+ "RSN: Unsupported EAPOL-Key descriptor version %d",
ver);
goto out;
}
+ if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
+ sm->pairwise_cipher != WPA_CIPHER_TKIP) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: EAPOL-Key descriptor version %d not allowed without TKIP as the pairwise cipher",
+ ver);
+ goto out;
+ }
+
+ if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
+ (sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X &&
+ sm->key_mgmt != WPA_KEY_MGMT_PSK)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: EAPOL-Key descriptor version %d not allowed due to negotiated AKM (0x%x)",
+ ver, sm->key_mgmt);
+ goto out;
+ }
+
if (wpa_use_akm_defined(sm->key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
@@ -2660,63 +3695,28 @@
if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
!wpa_use_akm_defined(sm->key_mgmt)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "WPA: AP did not use the "
- "negotiated AES-128-CMAC");
+ "RSN: AP did not use the negotiated AES-128-CMAC");
goto out;
}
} else if (sm->pairwise_cipher == WPA_CIPHER_CCMP &&
!wpa_use_akm_defined(sm->key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "WPA: CCMP is used, but EAPOL-Key "
- "descriptor version (%d) is not 2", ver);
- if (sm->group_cipher != WPA_CIPHER_CCMP &&
- !(key_info & WPA_KEY_INFO_KEY_TYPE)) {
- /* Earlier versions of IEEE 802.11i did not explicitly
- * require version 2 descriptor for all EAPOL-Key
- * packets, so allow group keys to use version 1 if
- * CCMP is not used for them. */
+ "RSN: CCMP is used, but EAPOL-Key descriptor version (%d) is not 2", ver);
+ if (ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "WPA: Backwards compatibility: allow invalid "
- "version for non-CCMP group keys");
- } else if (ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+ "RSN: Interoperability workaround: allow incorrect (should have been HMAC-SHA1), but stronger (is AES-128-CMAC), descriptor version to be used");
+ } else {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "WPA: Interoperability workaround: allow incorrect (should have been HMAC-SHA1), but stronger (is AES-128-CMAC), descriptor version to be used");
- } else
+ "RSN: Unexpected descriptor version %u", ver);
goto out;
+ }
} else if (sm->pairwise_cipher == WPA_CIPHER_GCMP &&
!wpa_use_akm_defined(sm->key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "WPA: GCMP is used, but EAPOL-Key "
- "descriptor version (%d) is not 2", ver);
- goto out;
- }
-
- if (sm->rx_replay_counter_set &&
- os_memcmp(key->replay_counter, sm->rx_replay_counter,
- WPA_REPLAY_COUNTER_LEN) <= 0) {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: EAPOL-Key Replay Counter did not increase - "
- "dropping packet");
- goto out;
- }
-
- if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
- wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "WPA: Unsupported SMK bit in key_info");
- goto out;
- }
-
- if (!(key_info & WPA_KEY_INFO_ACK)) {
- wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "WPA: No Ack bit in key_info");
- goto out;
- }
-
- if (key_info & WPA_KEY_INFO_REQUEST) {
- wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
- "WPA: EAPOL-Key with Request bit - dropped");
+ "RSN: GCMP is used, but EAPOL-Key descriptor version (%d) is not 2",
+ ver);
goto out;
}
@@ -2753,8 +3753,7 @@
if (key_info & WPA_KEY_INFO_KEY_TYPE) {
if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: Ignored EAPOL-Key (Pairwise) with "
- "non-zero key index");
+ "RSN: Ignored EAPOL-Key (Pairwise) with non-zero key index");
goto out;
}
if (key_info & (WPA_KEY_INFO_MIC |
@@ -2773,13 +3772,19 @@
if ((mic_len && (key_info & WPA_KEY_INFO_MIC)) ||
(!mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA))) {
/* 1/2 Group Key Handshake */
- wpa_supplicant_process_1_of_2(sm, src_addr, key,
- key_data, key_data_len,
- ver);
+ if (sm->mlo.valid_links)
+ wpa_supplicant_process_mlo_1_of_2(sm, src_addr,
+ key, key_data,
+ key_data_len,
+ ver);
+ else
+ wpa_supplicant_process_1_of_2(sm, src_addr, key,
+ key_data,
+ key_data_len,
+ ver);
} else {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: EAPOL-Key (Group) without Mic/Encr bit - "
- "dropped");
+ "RSN: EAPOL-Key (Group) without Mic/Encr bit - dropped");
}
}
@@ -2965,6 +3970,15 @@
}
+static void wpa_sm_pmksa_notify_cb(struct rsn_pmksa_cache_entry *entry,
+ void *ctx)
+{
+ struct wpa_sm *sm = ctx;
+
+ wpa_sm_notify_pmksa_cache_entry(sm, entry);
+}
+
+
/**
* wpa_sm_init - Initialize WPA state machine
* @ctx: Context pointer for callbacks; this needs to be an allocated buffer
@@ -2989,7 +4003,8 @@
sm->dot11RSNAConfigSATimeout = 60;
sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb,
- wpa_sm_pmksa_is_current_cb, sm, sm);
+ wpa_sm_pmksa_is_current_cb,
+ wpa_sm_pmksa_notify_cb, sm, sm);
if (sm->pmksa == NULL) {
wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
"RSN: PMKSA cache initialization failed");
@@ -3007,6 +4022,8 @@
*/
void wpa_sm_deinit(struct wpa_sm *sm)
{
+ int i;
+
if (sm == NULL)
return;
pmksa_cache_deinit(sm->pmksa);
@@ -3017,6 +4034,10 @@
os_free(sm->ap_wpa_ie);
os_free(sm->ap_rsn_ie);
os_free(sm->ap_rsnxe);
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ os_free(sm->mlo.links[i].ap_rsne);
+ os_free(sm->mlo.links[i].ap_rsnxe);
+ }
wpa_sm_drop_sa(sm);
os_free(sm->ctx);
#ifdef CONFIG_IEEE80211R
@@ -3041,6 +4062,32 @@
}
+static void wpa_sm_clear_ptk(struct wpa_sm *sm)
+{
+ int i;
+
+ sm->ptk_set = 0;
+ os_memset(&sm->ptk, 0, sizeof(sm->ptk));
+ sm->tptk_set = 0;
+ os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+ os_memset(&sm->gtk, 0, sizeof(sm->gtk));
+ os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep));
+ os_memset(&sm->igtk, 0, sizeof(sm->igtk));
+ os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep));
+ sm->tk_set = false;
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ os_memset(&sm->mlo.links[i].gtk, 0,
+ sizeof(sm->mlo.links[i].gtk));
+ os_memset(&sm->mlo.links[i].gtk_wnm_sleep, 0,
+ sizeof(sm->mlo.links[i].gtk_wnm_sleep));
+ os_memset(&sm->mlo.links[i].igtk, 0,
+ sizeof(sm->mlo.links[i].igtk));
+ os_memset(&sm->mlo.links[i].igtk_wnm_sleep, 0,
+ sizeof(sm->mlo.links[i].igtk_wnm_sleep));
+ }
+}
+
+
/**
* wpa_sm_notify_assoc - Notify WPA state machine about association
* @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -3100,15 +4147,7 @@
* this is not part of a Fast BSS Transition.
*/
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PTK");
- sm->ptk_set = 0;
- os_memset(&sm->ptk, 0, sizeof(sm->ptk));
- sm->tptk_set = 0;
- os_memset(&sm->tptk, 0, sizeof(sm->tptk));
- os_memset(&sm->gtk, 0, sizeof(sm->gtk));
- os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep));
- os_memset(&sm->igtk, 0, sizeof(sm->igtk));
- os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep));
- sm->tk_set = false;
+ wpa_sm_clear_ptk(sm);
}
#ifdef CONFIG_TDLS
@@ -3305,6 +4344,85 @@
}
+int wpa_sm_set_mlo_params(struct wpa_sm *sm, const struct wpa_sm_mlo *mlo)
+{
+ int i;
+
+ if (!sm)
+ return -1;
+
+ os_memcpy(sm->mlo.ap_mld_addr, mlo->ap_mld_addr, ETH_ALEN);
+ sm->mlo.assoc_link_id = mlo->assoc_link_id;
+ sm->mlo.valid_links = mlo->valid_links;
+ sm->mlo.req_links = mlo->req_links;
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ const u8 *ie;
+ size_t len;
+
+ if (sm->mlo.req_links & BIT(i)) {
+ if (!mlo->links[i].ap_rsne ||
+ mlo->links[i].ap_rsne_len == 0) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: No RSNE for AP MLO link %d with BSSID "
+ MACSTR,
+ i, MAC2STR(mlo->links[i].bssid));
+ return -1;
+
+ }
+ os_memcpy(sm->mlo.links[i].addr, mlo->links[i].addr,
+ ETH_ALEN);
+ os_memcpy(sm->mlo.links[i].bssid, mlo->links[i].bssid,
+ ETH_ALEN);
+ }
+
+ ie = mlo->links[i].ap_rsne;
+ len = mlo->links[i].ap_rsne_len;
+ os_free(sm->mlo.links[i].ap_rsne);
+ if (!ie || len == 0) {
+ if (sm->mlo.links[i].ap_rsne)
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Clearing MLO link[%u] AP RSNE",
+ i);
+ sm->mlo.links[i].ap_rsne = NULL;
+ sm->mlo.links[i].ap_rsne_len = 0;
+ } else {
+ wpa_hexdump_link(MSG_DEBUG, i, "RSN: Set AP RSNE",
+ ie, len);
+ sm->mlo.links[i].ap_rsne = os_memdup(ie, len);
+ if (!sm->mlo.links[i].ap_rsne) {
+ sm->mlo.links[i].ap_rsne_len = 0;
+ return -1;
+ }
+ sm->mlo.links[i].ap_rsne_len = len;
+ }
+
+ ie = mlo->links[i].ap_rsnxe;
+ len = mlo->links[i].ap_rsnxe_len;
+ os_free(sm->mlo.links[i].ap_rsnxe);
+ if (!ie || len == 0) {
+ if (sm->mlo.links[i].ap_rsnxe)
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Clearing MLO link[%u] AP RSNXE",
+ i);
+ sm->mlo.links[i].ap_rsnxe = NULL;
+ sm->mlo.links[i].ap_rsnxe_len = 0;
+ } else {
+ wpa_hexdump_link(MSG_DEBUG, i, "RSN: Set AP RSNXE", ie,
+ len);
+ sm->mlo.links[i].ap_rsnxe = os_memdup(ie, len);
+ if (!sm->mlo.links[i].ap_rsnxe) {
+ sm->mlo.links[i].ap_rsnxe_len = 0;
+ return -1;
+ }
+ sm->mlo.links[i].ap_rsnxe_len = len;
+ }
+ }
+
+ return 0;
+}
+
+
/**
* wpa_sm_set_own_addr - Set own MAC address
* @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -3874,10 +4992,11 @@
}
-int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid,
+int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid, const u8 *own_addr,
const void *network_ctx)
{
- return pmksa_cache_get(sm->pmksa, bssid, NULL, network_ctx, 0) != NULL;
+ return pmksa_cache_get(sm->pmksa, bssid, own_addr, NULL, network_ctx,
+ 0) != NULL;
}
@@ -3887,24 +5006,25 @@
const void *network_ctx,
int akmp)
{
- return pmksa_cache_get(sm->pmksa, aa, pmkid, network_ctx, akmp);
+ return pmksa_cache_get(sm->pmksa, aa, sm->own_addr, pmkid, network_ctx,
+ akmp);
+}
+
+
+void wpa_sm_pmksa_cache_remove(struct wpa_sm *sm,
+ struct rsn_pmksa_cache_entry *entry)
+{
+ if (sm && sm->pmksa)
+ pmksa_cache_remove(sm->pmksa, entry);
}
void wpa_sm_drop_sa(struct wpa_sm *sm)
{
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK");
- sm->ptk_set = 0;
- sm->tptk_set = 0;
- sm->tk_set = false;
+ wpa_sm_clear_ptk(sm);
sm->pmk_len = 0;
os_memset(sm->pmk, 0, sizeof(sm->pmk));
- os_memset(&sm->ptk, 0, sizeof(sm->ptk));
- os_memset(&sm->tptk, 0, sizeof(sm->tptk));
- os_memset(&sm->gtk, 0, sizeof(sm->gtk));
- os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep));
- os_memset(&sm->igtk, 0, sizeof(sm->igtk));
- os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep));
#ifdef CONFIG_IEEE80211R
os_memset(sm->xxkey, 0, sizeof(sm->xxkey));
sm->xxkey_len = 0;
@@ -3921,12 +5041,21 @@
}
-int wpa_sm_has_ptk(struct wpa_sm *sm)
+#ifdef CONFIG_IEEE80211R
+bool wpa_sm_has_ft_keys(struct wpa_sm *sm, const u8 *md)
{
- if (sm == NULL)
- return 0;
+ if (!sm)
+ return false;
+ if (!wpa_key_mgmt_ft(sm->key_mgmt) ||
+ os_memcmp(md, sm->key_mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN) != 0) {
+ /* Do not allow FT protocol to be used even if we were to have
+ * an PTK since the mobility domain has changed. */
+ return false;
+ }
return sm->ptk_set;
}
+#endif /* CONFIG_IEEE80211R */
int wpa_sm_has_ptk_installed(struct wpa_sm *sm)
@@ -3958,7 +5087,7 @@
void wpa_sm_install_pmk(struct wpa_sm *sm)
{
/* In case the driver wants to handle re-assocs, pass it down the PMK. */
- if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->pairwise_cipher), NULL, 0, 0, NULL, 0,
+ if (wpa_sm_set_key(sm, -1, wpa_cipher_to_alg(sm->pairwise_cipher), NULL, 0, 0, NULL, 0,
(u8*)sm->pmk, sm->pmk_len, KEY_FLAG_PMK) < 0) {
wpa_hexdump(MSG_DEBUG, "PSK: Install PMK to the driver for driver reassociations",
(u8*)sm->pmk, sm->pmk_len);
@@ -4127,6 +5256,12 @@
}
+const u8 * wpa_sm_get_auth_addr(struct wpa_sm *sm)
+{
+ return sm->mlo.valid_links ? sm->mlo.ap_mld_addr : sm->bssid;
+}
+
+
#ifdef CONFIG_FILS
struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md)
@@ -4362,7 +5497,7 @@
}
if (wpa_ft_parse_ies(pos, end - pos, &parse,
- wpa_key_mgmt_sha384(sm->key_mgmt)) < 0) {
+ sm->key_mgmt) < 0) {
wpa_printf(MSG_DEBUG, "FILS+FT: Failed to parse IEs");
goto fail;
}
@@ -4509,7 +5644,8 @@
else
kdk_len = 0;
- if (fils_pmk_to_ptk(sm->pmk, sm->pmk_len, sm->own_addr, sm->bssid,
+ if (fils_pmk_to_ptk(sm->pmk, sm->pmk_len, sm->own_addr,
+ wpa_sm_get_auth_addr(sm),
sm->fils_nonce, sm->fils_anonce,
dh_ss ? wpabuf_head(dh_ss) : NULL,
dh_ss ? wpabuf_len(dh_ss) : 0,
@@ -4651,21 +5787,27 @@
if (wpa_derive_pmk_r0(sm->fils_ft, sm->fils_ft_len, sm->ssid,
sm->ssid_len, sm->mobility_domain,
sm->r0kh_id, sm->r0kh_id_len, sm->own_addr,
- sm->pmk_r0, sm->pmk_r0_name, use_sha384) < 0) {
+ sm->pmk_r0, sm->pmk_r0_name, sm->key_mgmt) < 0) {
wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMK-R0");
return -1;
}
- sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
+ if (wpa_key_mgmt_sae_ext_key(sm->key_mgmt))
+ sm->pmk_r0_len = sm->fils_ft_len;
+ else
+ sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
wpa_printf(MSG_DEBUG, "FILS+FT: R1KH-ID: " MACSTR,
MAC2STR(sm->r1kh_id));
pos = wpabuf_put(buf, WPA_PMK_NAME_LEN);
if (wpa_derive_pmk_r1_name(sm->pmk_r0_name, sm->r1kh_id, sm->own_addr,
- sm->pmk_r1_name, use_sha384) < 0) {
+ sm->pmk_r1_name, sm->fils_ft_len) < 0) {
wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMKR1Name");
return -1;
}
os_memcpy(pos, sm->pmk_r1_name, WPA_PMK_NAME_LEN);
+ os_memcpy(sm->key_mobility_domain, sm->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN);
+
if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
/* Management Group Cipher Suite */
pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
@@ -5071,12 +6213,13 @@
rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
wpa_hexdump_key(MSG_DEBUG, "FILS: Set TK to driver",
sm->ptk.tk, keylen);
- if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, null_rsc, rsclen,
+ if (wpa_sm_set_key(sm, -1, alg, wpa_sm_get_auth_addr(sm), 0, 1,
+ null_rsc, rsclen,
sm->ptk.tk, keylen, KEY_FLAG_PAIRWISE_RX_TX) < 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "FILS: Failed to set PTK to the driver (alg=%d keylen=%d bssid="
+ "FILS: Failed to set PTK to the driver (alg=%d keylen=%d auth_addr="
MACSTR ")",
- alg, keylen, MAC2STR(sm->bssid));
+ alg, keylen, MAC2STR(wpa_sm_get_auth_addr(sm)));
goto fail;
}
@@ -5360,15 +6503,6 @@
#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);
-}
-
-
void wpa_pasn_sm_set_caps(struct wpa_sm *sm, unsigned int flags2)
{
if (flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA)
@@ -5387,3 +6521,17 @@
if (sm)
pmksa_cache_reconfig(sm->pmksa);
}
+
+
+struct rsn_pmksa_cache * wpa_sm_get_pmksa_cache(struct wpa_sm *sm)
+{
+ return sm ? sm->pmksa : NULL;
+}
+
+
+void wpa_sm_set_cur_pmksa(struct wpa_sm *sm,
+ struct rsn_pmksa_cache_entry *entry)
+{
+ if (sm)
+ sm->cur_pmksa = entry;
+}
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 411f328..e3c7892 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -19,6 +19,7 @@
struct wpa_config_blob;
struct hostapd_freq_params;
struct wpa_channel_info;
+struct rsn_pmksa_cache_entry;
enum frame_encryption;
struct wpa_sm_ctx {
@@ -29,7 +30,7 @@
enum wpa_states (*get_state)(void *ctx);
void (*deauthenticate)(void * ctx, u16 reason_code);
void (*reconnect)(void *ctx);
- int (*set_key)(void *ctx, enum wpa_alg alg,
+ int (*set_key)(void *ctx, int link_id, enum wpa_alg alg,
const u8 *addr, int key_idx, int set_tx,
const u8 *seq, size_t seq_len,
const u8 *key, size_t key_len, enum key_flag key_flag);
@@ -98,6 +99,8 @@
const u8 *peer_addr, size_t ltf_keyseed_len,
const u8 *ltf_keyseed);
#endif /* CONFIG_PASN */
+ void (*notify_pmksa_cache_entry)(void *ctx,
+ struct rsn_pmksa_cache_entry *entry);
};
@@ -145,6 +148,27 @@
bool force_kdk_derivation;
};
+struct wpa_sm_link {
+ u8 addr[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ u8 *ap_rsne, *ap_rsnxe;
+ size_t ap_rsne_len, ap_rsnxe_len;
+ struct wpa_gtk gtk;
+ struct wpa_gtk gtk_wnm_sleep;
+ struct wpa_igtk igtk;
+ struct wpa_igtk igtk_wnm_sleep;
+ struct wpa_bigtk bigtk;
+ struct wpa_bigtk bigtk_wnm_sleep;
+};
+
+struct wpa_sm_mlo {
+ u8 ap_mld_addr[ETH_ALEN];
+ u8 assoc_link_id;
+ u16 valid_links; /* bitmap of accepted links */
+ u16 req_links; /* bitmap of requested links */
+ struct wpa_sm_link links[MAX_NUM_MLD_LINKS];
+};
+
#ifndef CONFIG_NO_WPA
struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx);
@@ -200,7 +224,7 @@
void wpa_sm_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
const u8 *pmkid, const u8 *bssid,
const u8 *fils_cache_id);
-int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid,
+int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid, const u8 *own_addr,
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,
@@ -208,7 +232,9 @@
const u8 *pmkid,
const void *network_ctx,
int akmp);
-int wpa_sm_has_ptk(struct wpa_sm *sm);
+void wpa_sm_pmksa_cache_remove(struct wpa_sm *sm,
+ struct rsn_pmksa_cache_entry *entry);
+bool wpa_sm_has_ft_keys(struct wpa_sm *sm, const u8 *md);
int wpa_sm_has_ptk_installed(struct wpa_sm *sm);
void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr);
@@ -229,6 +255,7 @@
const u8 *ptk_kek, size_t ptk_kek_len);
int wpa_fils_is_completed(struct wpa_sm *sm);
void wpa_sm_pmksa_cache_reconfig(struct wpa_sm *sm);
+int wpa_sm_set_mlo_params(struct wpa_sm *sm, const struct wpa_sm_mlo *mlo);
#else /* CONFIG_NO_WPA */
@@ -443,6 +470,12 @@
{
}
+static inline int wpa_sm_set_mlo_params(struct wpa_sm *sm,
+ const struct wpa_sm_mlo *mlo)
+{
+ return 0;
+}
+
#endif /* CONFIG_NO_WPA */
#ifdef CONFIG_IEEE80211R
@@ -461,7 +494,7 @@
int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
size_t ies_len, const u8 *src_addr);
int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
- const u8 *mdie);
+ const u8 *mdie, bool force);
#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
int wpa_ft_is_ft_protocol(struct wpa_sm *sm);
#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
@@ -529,8 +562,9 @@
#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)
+static inline 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;
}
@@ -585,8 +619,11 @@
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);
void wpa_pasn_sm_set_caps(struct wpa_sm *sm, unsigned int flags2);
+struct rsn_pmksa_cache * wpa_sm_get_pmksa_cache(struct wpa_sm *sm);
+
+void wpa_sm_set_cur_pmksa(struct wpa_sm *sm,
+ struct rsn_pmksa_cache_entry *entry);
+const u8 * wpa_sm_get_auth_addr(struct wpa_sm *sm);
#endif /* WPA_H */
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index 96d293f..4d52542 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -11,6 +11,7 @@
#include "common.h"
#include "crypto/aes_wrap.h"
#include "crypto/sha384.h"
+#include "crypto/sha512.h"
#include "crypto/random.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
@@ -55,11 +56,14 @@
return -1;
}
- sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
+ if (wpa_key_mgmt_sae_ext_key(sm->key_mgmt))
+ sm->pmk_r0_len = mpmk_len;
+ else
+ sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
if (wpa_derive_pmk_r0(mpmk, mpmk_len, sm->ssid,
sm->ssid_len, sm->mobility_domain,
sm->r0kh_id, sm->r0kh_id_len, sm->own_addr,
- sm->pmk_r0, sm->pmk_r0_name, use_sha384) < 0)
+ sm->pmk_r0, sm->pmk_r0_name, sm->key_mgmt) < 0)
return -1;
sm->pmk_r1_len = sm->pmk_r0_len;
if (wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_len, sm->pmk_r0_name,
@@ -77,7 +81,7 @@
kdk_len = 0;
ret = wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce,
- anonce, sm->own_addr, sm->bssid,
+ anonce, sm->own_addr, wpa_sm_get_auth_addr(sm),
sm->pmk_r1_name, ptk, ptk_name, sm->key_mgmt,
sm->pairwise_cipher, kdk_len);
if (ret) {
@@ -85,6 +89,9 @@
return ret;
}
+ os_memcpy(sm->key_mobility_domain, sm->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN);
+
#ifdef CONFIG_PASN
if (sm->secure_ltf &&
ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF))
@@ -105,7 +112,6 @@
int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len)
{
struct wpa_ft_ies ft;
- int use_sha384;
if (sm == NULL)
return 0;
@@ -121,8 +127,7 @@
return 0;
}
- use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
- if (wpa_ft_parse_ies(ies, ies_len, &ft, use_sha384) < 0)
+ if (wpa_ft_parse_ies(ies, ies_len, &ft, sm->key_mgmt) < 0)
return -1;
if (ft.mdie_len < MOBILITY_DOMAIN_ID_LEN + 1)
@@ -184,7 +189,7 @@
* @len: Buffer for returning the length of the IEs
* @anonce: ANonce or %NULL if not yet available
* @pmk_name: PMKR0Name or PMKR1Name to be added into the RSN IE PMKID List
- * @kck: 128-bit KCK for MIC or %NULL if no MIC is used
+ * @kck: KCK for MIC or %NULL if no MIC is used
* @kck_len: KCK length in octets
* @target_ap: Target AP address
* @ric_ies: Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request or %NULL
@@ -211,12 +216,13 @@
size_t rsnxe_len;
int rsnxe_used;
int res;
+ u8 mic_control;
sm->ft_completed = 0;
sm->ft_reassoc_completed = 0;
buf_len = 2 + sizeof(struct rsn_mdie) + 2 +
- sizeof(struct rsn_ftie_sha384) +
+ sizeof(struct rsn_ftie_sha512) +
2 + sm->r0kh_id_len + ric_ies_len + 100;
buf = os_zalloc(buf_len);
if (buf == NULL)
@@ -334,7 +340,8 @@
*pos++ = WLAN_EID_FAST_BSS_TRANSITION;
ftie_len = pos++;
rsnxe_used = wpa_key_mgmt_sae(sm->key_mgmt) && anonce &&
- (sm->sae_pwe == 1 || sm->sae_pwe == 2);
+ (sm->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ sm->sae_pwe == SAE_PWE_BOTH);
#ifdef CONFIG_TESTING_OPTIONS
if (anonce && sm->ft_rsnxe_used) {
rsnxe_used = sm->ft_rsnxe_used == 1;
@@ -342,11 +349,28 @@
rsnxe_used);
}
#endif /* CONFIG_TESTING_OPTIONS */
- if (wpa_key_mgmt_sha384(sm->key_mgmt)) {
+ mic_control = rsnxe_used ? FTE_MIC_CTRL_RSNXE_USED : 0;
+ if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ sm->pmk_r0_len == SHA512_MAC_LEN) {
+ struct rsn_ftie_sha512 *ftie;
+
+ ftie = (struct rsn_ftie_sha512 *) pos;
+ mic_control |= FTE_MIC_LEN_32 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+ ftie->mic_control[0] = mic_control;
+ fte_mic = ftie->mic;
+ elem_count = &ftie->mic_control[1];
+ pos += sizeof(*ftie);
+ os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN);
+ if (anonce)
+ os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN);
+ } else if ((sm->key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ sm->pmk_r0_len == SHA384_MAC_LEN) ||
+ wpa_key_mgmt_sha384(sm->key_mgmt)) {
struct rsn_ftie_sha384 *ftie;
ftie = (struct rsn_ftie_sha384 *) pos;
- ftie->mic_control[0] = !!rsnxe_used;
+ mic_control |= FTE_MIC_LEN_24 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+ ftie->mic_control[0] = mic_control;
fte_mic = ftie->mic;
elem_count = &ftie->mic_control[1];
pos += sizeof(*ftie);
@@ -357,7 +381,8 @@
struct rsn_ftie *ftie;
ftie = (struct rsn_ftie *) pos;
- ftie->mic_control[0] = !!rsnxe_used;
+ mic_control |= FTE_MIC_LEN_16 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+ ftie->mic_control[0] = mic_control;
fte_mic = ftie->mic;
elem_count = &ftie->mic_control[1];
pos += sizeof(*ftie);
@@ -441,7 +466,8 @@
*elem_count = 3 + ieee802_11_ie_count(ric_ies, ric_ies_len);
if (rsnxe_len)
*elem_count += 1;
- if (wpa_ft_mic(kck, kck_len, sm->own_addr, target_ap, 5,
+ if (wpa_ft_mic(sm->key_mgmt, kck, kck_len,
+ sm->own_addr, target_ap, 5,
((u8 *) mdie) - 2, 2 + sizeof(*mdie),
ftie_pos, 2 + *ftie_len,
(u8 *) rsnie, 2 + rsnie->len, ric_ies,
@@ -476,7 +502,8 @@
alg = wpa_cipher_to_alg(sm->pairwise_cipher);
keylen = wpa_cipher_key_len(sm->pairwise_cipher);
- if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc, sizeof(null_rsc),
+ /* TODO: AP MLD address for MLO */
+ if (wpa_sm_set_key(sm, -1, alg, bssid, 0, 1, null_rsc, sizeof(null_rsc),
(u8 *) sm->ptk.tk, keylen,
KEY_FLAG_PAIRWISE_RX_TX) < 0) {
wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver");
@@ -563,8 +590,6 @@
const u8 *bssid;
const u8 *kck;
size_t kck_len, kdk_len;
- int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
- const u8 *anonce, *snonce;
wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
wpa_hexdump(MSG_DEBUG, "FT: RIC IEs", ric_ies, ric_ies_len);
@@ -590,7 +615,7 @@
return -1;
}
- if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) {
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, sm->key_mgmt) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
return -1;
}
@@ -603,34 +628,15 @@
return -1;
}
- if (use_sha384) {
- struct rsn_ftie_sha384 *ftie;
-
- ftie = (struct rsn_ftie_sha384 *) parse.ftie;
- if (!ftie || parse.ftie_len < sizeof(*ftie)) {
- wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
- return -1;
- }
-
- anonce = ftie->anonce;
- snonce = ftie->snonce;
- } else {
- struct rsn_ftie *ftie;
-
- ftie = (struct rsn_ftie *) parse.ftie;
- if (!ftie || parse.ftie_len < sizeof(*ftie)) {
- wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
- return -1;
- }
-
- anonce = ftie->anonce;
- snonce = ftie->snonce;
+ if (!parse.ftie || !parse.fte_anonce || !parse.fte_snonce) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTE");
+ return -1;
}
- if (os_memcmp(snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
+ if (os_memcmp(parse.fte_snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
- snonce, WPA_NONCE_LEN);
+ parse.fte_snonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
sm->snonce, WPA_NONCE_LEN);
return -1;
@@ -675,8 +681,8 @@
os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", sm->r1kh_id, FT_R1KH_ID_LEN);
wpa_hexdump(MSG_DEBUG, "FT: SNonce", sm->snonce, WPA_NONCE_LEN);
- wpa_hexdump(MSG_DEBUG, "FT: ANonce", anonce, WPA_NONCE_LEN);
- os_memcpy(sm->anonce, anonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: ANonce", parse.fte_anonce, WPA_NONCE_LEN);
+ os_memcpy(sm->anonce, parse.fte_anonce, WPA_NONCE_LEN);
if (wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_len, sm->pmk_r0_name,
sm->r1kh_id, sm->own_addr, sm->pmk_r1,
sm->pmk_r1_name) < 0)
@@ -694,13 +700,17 @@
else
kdk_len = 0;
+ /* TODO: AP MLD address for MLO */
if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce,
- anonce, sm->own_addr, bssid,
+ parse.fte_anonce, sm->own_addr, bssid,
sm->pmk_r1_name, &sm->ptk, ptk_name, sm->key_mgmt,
sm->pairwise_cipher,
kdk_len) < 0)
return -1;
+ os_memcpy(sm->key_mobility_domain, sm->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN);
+
#ifdef CONFIG_PASN
if (sm->secure_ltf &&
ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
@@ -717,7 +727,7 @@
kck = sm->ptk.kck;
kck_len = sm->ptk.kck_len;
}
- ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, anonce,
+ ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, parse.fte_anonce,
sm->pmk_r1_name,
kck, kck_len, bssid,
ric_ies, ric_ies_len,
@@ -866,7 +876,7 @@
os_memcpy(gtk + 16, gtk + 24, 8);
os_memcpy(gtk + 24, tmp, 8);
}
- if (wpa_sm_set_key(sm, alg, broadcast_ether_addr, keyidx, 0,
+ if (wpa_sm_set_key(sm, -1, alg, broadcast_ether_addr, keyidx, 0,
gtk_elem + 3, rsc_len, gtk, keylen,
KEY_FLAG_GROUP_RX) < 0) {
wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the "
@@ -933,7 +943,7 @@
wpa_hexdump_key(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk,
igtk_len);
- if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+ if (wpa_sm_set_key(sm, -1, wpa_cipher_to_alg(sm->mgmt_group_cipher),
broadcast_ether_addr, keyidx, 0,
igtk_elem + 2, 6, igtk, igtk_len,
KEY_FLAG_GROUP_RX) < 0) {
@@ -1001,7 +1011,7 @@
wpa_hexdump_key(MSG_DEBUG, "FT: BIGTK from Reassoc Resp", bigtk,
bigtk_len);
- if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+ if (wpa_sm_set_key(sm, -1, wpa_cipher_to_alg(sm->mgmt_group_cipher),
broadcast_ether_addr, keyidx, 0,
bigtk_elem + 2, 6, bigtk, bigtk_len,
KEY_FLAG_GROUP_RX) < 0) {
@@ -1025,10 +1035,8 @@
u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
const u8 *kck;
size_t kck_len;
- int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
- const u8 *anonce, *snonce, *fte_mic;
- u8 fte_elem_count;
- int own_rsnxe_used, rsnxe_used;
+ int own_rsnxe_used;
+ size_t mic_len;
wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
@@ -1043,7 +1051,7 @@
return 0;
}
- if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) {
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, sm->key_mgmt) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
return -1;
}
@@ -1056,49 +1064,37 @@
return -1;
}
- if (use_sha384) {
- struct rsn_ftie_sha384 *ftie;
+ if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ sm->pmk_r1_len == SHA512_MAC_LEN)
+ mic_len = 32;
+ else if ((sm->key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ sm->pmk_r1_len == SHA384_MAC_LEN) ||
+ wpa_key_mgmt_sha384(sm->key_mgmt))
+ mic_len = 24;
+ else
+ mic_len = 16;
- ftie = (struct rsn_ftie_sha384 *) parse.ftie;
- if (!ftie || parse.ftie_len < sizeof(*ftie)) {
- wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
- return -1;
- }
-
- anonce = ftie->anonce;
- snonce = ftie->snonce;
- rsnxe_used = ftie->mic_control[0] & 0x01;
- fte_elem_count = ftie->mic_control[1];
- fte_mic = ftie->mic;
- } else {
- struct rsn_ftie *ftie;
-
- ftie = (struct rsn_ftie *) parse.ftie;
- if (!ftie || parse.ftie_len < sizeof(*ftie)) {
- wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
- return -1;
- }
-
- anonce = ftie->anonce;
- snonce = ftie->snonce;
- rsnxe_used = ftie->mic_control[0] & 0x01;
- fte_elem_count = ftie->mic_control[1];
- fte_mic = ftie->mic;
+ if (!parse.ftie || !parse.fte_anonce || !parse.fte_snonce ||
+ parse.fte_mic_len != mic_len) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Invalid FTE (fte_mic_len=%zu mic_len=%zu)",
+ parse.fte_mic_len, mic_len);
+ return -1;
}
- if (os_memcmp(snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
+ if (os_memcmp(parse.fte_snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
- snonce, WPA_NONCE_LEN);
+ parse.fte_snonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
sm->snonce, WPA_NONCE_LEN);
return -1;
}
- if (os_memcmp(anonce, sm->anonce, WPA_NONCE_LEN) != 0) {
+ if (os_memcmp(parse.fte_anonce, sm->anonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
- anonce, WPA_NONCE_LEN);
+ parse.fte_anonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
sm->anonce, WPA_NONCE_LEN);
return -1;
@@ -1145,10 +1141,10 @@
count += ieee802_11_ie_count(parse.ric, parse.ric_len);
if (parse.rsnxe)
count++;
- if (fte_elem_count != count) {
+ if (parse.fte_elem_count != count) {
wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
"Control: received %u expected %u",
- fte_elem_count, count);
+ parse.fte_elem_count, count);
return -1;
}
@@ -1160,7 +1156,7 @@
kck_len = sm->ptk.kck_len;
}
- if (wpa_ft_mic(kck, kck_len, sm->own_addr, src_addr, 6,
+ if (wpa_ft_mic(sm->key_mgmt, kck, kck_len, sm->own_addr, src_addr, 6,
parse.mdie - 2, parse.mdie_len + 2,
parse.ftie - 2, parse.ftie_len + 2,
parse.rsn - 2, parse.rsn_len + 2,
@@ -1172,14 +1168,15 @@
return -1;
}
- if (os_memcmp_const(mic, fte_mic, 16) != 0) {
+ if (os_memcmp_const(mic, parse.fte_mic, mic_len) != 0) {
wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
- wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", fte_mic, 16);
- wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16);
+ wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC",
+ parse.fte_mic, mic_len);
+ wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, mic_len);
return -1;
}
- if (rsnxe_used && !sm->ap_rsnxe) {
+ if (parse.fte_rsnxe_used && !sm->ap_rsnxe) {
wpa_printf(MSG_INFO,
"FT: FTE indicated that AP uses RSNXE, but RSNXE was not included in Beacon/Probe Response frames");
return -1;
@@ -1213,7 +1210,8 @@
}
own_rsnxe_used = wpa_key_mgmt_sae(sm->key_mgmt) &&
- (sm->sae_pwe == 1 || sm->sae_pwe == 2);
+ (sm->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ sm->sae_pwe == SAE_PWE_BOTH);
if ((sm->ap_rsnxe && !parse.rsnxe && own_rsnxe_used) ||
(!sm->ap_rsnxe && parse.rsnxe) ||
(sm->ap_rsnxe && parse.rsnxe &&
@@ -1285,14 +1283,24 @@
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @target_ap: Target AP Address
* @mdie: Mobility Domain IE from the target AP
+ * @force: Whether to force the request and ignore mobility domain differences
+ * (only for testing purposes)
* Returns: 0 on success, -1 on failure
*/
int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
- const u8 *mdie)
+ const u8 *mdie, bool force)
{
u8 *ft_ies;
size_t ft_ies_len;
+ if (!force &&
+ (!mdie || mdie[1] < 3 || !wpa_sm_has_ft_keys(sm, mdie + 2))) {
+ wpa_printf(MSG_DEBUG, "FT: Cannot use over-the DS with " MACSTR
+ " - no keys matching the mobility domain",
+ MAC2STR(target_ap));
+ return -1;
+ }
+
wpa_printf(MSG_DEBUG, "FT: Request over-the-DS with " MACSTR,
MAC2STR(target_ap));
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 3811c3b..ed43cc1 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -106,7 +106,7 @@
int rsn_enabled; /* Whether RSN is enabled in configuration */
int mfp; /* 0 = disabled, 1 = optional, 2 = mandatory */
int ocv; /* Operating Channel Validation */
- int sae_pwe; /* SAE PWE generation options */
+ enum sae_pwe sae_pwe; /* SAE PWE generation options */
unsigned int sae_pk:1; /* whether SAE-PK is used */
unsigned int secure_ltf:1;
@@ -150,6 +150,7 @@
size_t pmk_r1_len;
u8 pmk_r1_name[WPA_PMK_NAME_LEN];
u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
+ u8 key_mobility_domain[MOBILITY_DOMAIN_ID_LEN];
u8 r0kh_id[FT_R0KH_ID_MAX_LEN];
size_t r0kh_id_len;
u8 r1kh_id[FT_R1KH_ID_LEN];
@@ -218,6 +219,7 @@
struct wpabuf *dpp_z;
int dpp_pfs;
#endif /* CONFIG_DPP2 */
+ struct wpa_sm_mlo mlo;
};
@@ -239,15 +241,15 @@
sm->ctx->deauthenticate(sm->ctx->ctx, reason_code);
}
-static inline int wpa_sm_set_key(struct wpa_sm *sm, enum wpa_alg alg,
- const u8 *addr, int key_idx, int set_tx,
- const u8 *seq, size_t seq_len,
+static inline int wpa_sm_set_key(struct wpa_sm *sm, int link_id,
+ enum wpa_alg alg, const u8 *addr, int key_idx,
+ int set_tx, const u8 *seq, size_t seq_len,
const u8 *key, size_t key_len,
enum key_flag key_flag)
{
WPA_ASSERT(sm->ctx->set_key);
- return sm->ctx->set_key(sm->ctx->ctx, alg, addr, key_idx, set_tx,
- seq, seq_len, key, key_len, key_flag);
+ return sm->ctx->set_key(sm->ctx->ctx, link_id, alg, addr, key_idx,
+ set_tx, seq, seq_len, key, key_len, key_flag);
}
static inline void wpa_sm_reconnect(struct wpa_sm *sm)
@@ -495,6 +497,14 @@
}
#endif /* CONFIG_PASN */
+static inline void
+wpa_sm_notify_pmksa_cache_entry(struct wpa_sm *sm,
+ struct rsn_pmksa_cache_entry *entry)
+{
+ if (sm->ctx->notify_pmksa_cache_entry)
+ sm->ctx->notify_pmksa_cache_entry(sm->ctx->ctx, entry);
+}
+
int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk,
int ver, const u8 *dest, u16 proto,
u8 *msg, size_t msg_len, u8 *key_mic);
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index c4e660f..50bd2b2 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -362,7 +362,8 @@
size_t flen;
if (wpa_key_mgmt_sae(sm->key_mgmt) &&
- (sm->sae_pwe == 1 || sm->sae_pwe == 2 || sm->sae_pk)) {
+ (sm->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ sm->sae_pwe == SAE_PWE_BOTH || sm->sae_pk)) {
capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
#ifdef CONFIG_SAE_PK
if (sm->sae_pk)
@@ -375,7 +376,7 @@
if (sm->secure_rtt)
capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
if (sm->prot_range_neg)
- capab |= BIT(WLAN_RSNX_CAPAB_PROT_RANGE_NEG);
+ capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
flen = (capab & 0xff00) ? 2 : 1;
if (!capab)
diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c
index 3825a73..9df56c2 100644
--- a/src/tls/tlsv1_client_read.c
+++ b/src/tls/tlsv1_client_read.c
@@ -771,7 +771,8 @@
hlen = tls_key_x_server_params_hash(
conn->rl.tls_version, conn->client_random,
conn->server_random, server_params,
- server_params_end - server_params, hash);
+ server_params_end - server_params, hash,
+ sizeof(hash));
}
if (hlen < 0)
diff --git a/src/tls/tlsv1_common.c b/src/tls/tlsv1_common.c
index e178915..0dd8e27 100644
--- a/src/tls/tlsv1_common.c
+++ b/src/tls/tlsv1_common.c
@@ -378,7 +378,7 @@
int tls_key_x_server_params_hash(u16 tls_version, const u8 *client_random,
const u8 *server_random,
const u8 *server_params,
- size_t server_params_len, u8 *hash)
+ size_t server_params_len, u8 *hash, size_t hsz)
{
u8 *hpos;
size_t hlen;
@@ -393,6 +393,8 @@
crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN);
crypto_hash_update(ctx, server_params, server_params_len);
hlen = MD5_MAC_LEN;
+ if (hsz < hlen)
+ return -1;
if (crypto_hash_finish(ctx, hash, &hlen) < 0)
return -1;
hpos += hlen;
@@ -403,7 +405,7 @@
crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN);
crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN);
crypto_hash_update(ctx, server_params, server_params_len);
- hlen = hash + sizeof(hash) - hpos;
+ hlen = hsz - hlen;
if (crypto_hash_finish(ctx, hpos, &hlen) < 0)
return -1;
hpos += hlen;
diff --git a/src/tls/tlsv1_common.h b/src/tls/tlsv1_common.h
index e30b15a..4cfdc2d 100644
--- a/src/tls/tlsv1_common.h
+++ b/src/tls/tlsv1_common.h
@@ -267,7 +267,8 @@
int tls_key_x_server_params_hash(u16 tls_version, const u8 *client_random,
const u8 *server_random,
const u8 *server_params,
- size_t server_params_len, u8 *hash);
+ size_t server_params_len,
+ u8 *hash, size_t hsz);
int tls_verify_signature(u16 tls_version, struct crypto_public_key *pk,
const u8 *data, size_t data_len,
const u8 *pos, size_t len, u8 *alert);
diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c
index 8d36cf1..545abae 100644
--- a/src/tls/tlsv1_server_write.c
+++ b/src/tls/tlsv1_server_write.c
@@ -620,7 +620,7 @@
hlen = tls_key_x_server_params_hash(
conn->rl.tls_version, conn->client_random,
conn->server_random, server_params,
- pos - server_params, hash);
+ pos - server_params, hash, sizeof(hash));
}
if (hlen < 0) {
diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c
index 9875b0e..4469712 100644
--- a/src/utils/wpa_debug.c
+++ b/src/utils/wpa_debug.c
@@ -596,13 +596,21 @@
}
-void wpa_debug_close_file(void)
+void wpa_debug_stop_log(void)
{
#ifdef CONFIG_DEBUG_FILE
if (!out_file)
return;
fclose(out_file);
out_file = NULL;
+#endif /* CONFIG_DEBUG_FILE */
+}
+
+
+void wpa_debug_close_file(void)
+{
+#ifdef CONFIG_DEBUG_FILE
+ wpa_debug_stop_log();
os_free(last_path);
last_path = NULL;
#endif /* CONFIG_DEBUG_FILE */
diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h
index 29fc48a..291aa07 100644
--- a/src/utils/wpa_debug.h
+++ b/src/utils/wpa_debug.h
@@ -49,6 +49,7 @@
int wpa_debug_reopen_file(void);
void wpa_debug_close_file(void);
void wpa_debug_setup_stdout(void);
+void wpa_debug_stop_log(void);
/**
* wpa_debug_printf_timestamp - Print timestamp for debug output
diff --git a/src/wps/ndef.c b/src/wps/ndef.c
index bb3c055..63f0d52 100644
--- a/src/wps/ndef.c
+++ b/src/wps/ndef.c
@@ -63,12 +63,18 @@
} else
record->id_length = 0;
+ if (record->type_length > data + size - pos)
+ return -1;
record->type = record->type_length == 0 ? NULL : pos;
pos += record->type_length;
+ if (record->id_length > data + size - pos)
+ return -1;
record->id = record->id_length == 0 ? NULL : pos;
pos += record->id_length;
+ if (record->payload_length > (size_t) (data + size - pos))
+ return -1;
record->payload = record->payload_length == 0 ? NULL : pos;
pos += record->payload_length;
diff --git a/wpa_supplicant/Android.bp b/wpa_supplicant/Android.bp
index be79765..dd4423a 100644
--- a/wpa_supplicant/Android.bp
+++ b/wpa_supplicant/Android.bp
@@ -279,6 +279,8 @@
"src/ap/ap_config.c",
"src/ap/ap_drv_ops.c",
"src/ap/ap_list.c",
+ "src/ap/comeback_token.c",
+ "src/pasn/pasn_responder.c",
"src/ap/ap_mlme.c",
"src/ap/authsrv.c",
"src/ap/beacon.c",
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 33dabf3..03dc209 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -108,6 +108,7 @@
INCLUDES += $(LOCAL_PATH)/src/tls
INCLUDES += $(LOCAL_PATH)/src/utils
INCLUDES += $(LOCAL_PATH)/src/wps
+INCLUDES += $(LOCAL_PATH)/src/pasn
INCLUDES += system/security/keystore/include
ifdef CONFIG_DRIVER_NL80211
ifneq ($(wildcard external/libnl),)
@@ -420,6 +421,7 @@
NEED_SHA256=y
NEED_SHA384=y
OBJS += src/common/ptksa_cache.c
+OBJS += src/pasn/pasn_initiator.c
OBJS += pasn_supplicant.c
endif
@@ -985,6 +987,8 @@
ifdef NEED_AP_MLME
OBJS += src/ap/wmm.c
OBJS += src/ap/ap_list.c
+OBJS += src/ap/comeback_token.c
+OBJS += src/pasn/pasn_responder.c
OBJS += src/ap/ieee802_11.c
OBJS += src/ap/hw_features.c
OBJS += src/ap/dfs.c
@@ -1757,6 +1761,142 @@
LDO=$(CC)
endif
+PASNOBJS =
+PASNOBJS += src/utils/$(CONFIG_ELOOP).c
+PASNOBJS += src/utils/wpa_debug.c
+PASNOBJS += src/utils/wpabuf.c
+PASNOBJS += src/utils/os_$(CONFIG_OS).c
+PASNOBJS += src/utils/config.c
+PASNOBJS += src/utils/common.c
+
+ifdef NEED_BASE64
+PASNOBJS += src/utils/base64.c
+endif
+
+ifdef CONFIG_WPA_TRACE
+PASNOBJS += src/utils/trace.c
+endif
+
+ifdef CONFIG_EXT_PASSWORD_FILE
+PASNOBJS += src/utils/ext_password_file.c
+endif
+
+ifdef CONFIG_EXT_PASSWORD_TEST
+PASNOBJS += src/utils/ext_password_test.c
+endif
+
+ifdef NEED_EXT_PASSWORD
+PASNOBJS += src/utils/ext_password.c
+endif
+
+ifdef CONFIG_SAE
+PASNOBJS += src/common/sae.c
+endif
+
+ifdef CONFIG_SAE_PK
+PASNOBJS += src/common/sae_pk.c
+endif
+
+ifndef CONFIG_NO_WPA
+PASNOBJS += src/common/wpa_common.c
+endif
+
+PASNOBJS += src/common/ieee802_11_common.c
+
+ifdef NEED_DRAGONFLY
+PASNOBJS += src/common/dragonfly.c
+endif
+
+PASNOBJS += src/common/ptksa_cache.c
+
+ifndef CONFIG_NO_WPA
+PASNOBJS += src/rsn_supp/pmksa_cache.c
+PASNOBJS += src/rsn_supp/wpa_ie.c
+endif
+
+PASNOBJS += src/ap/comeback_token.c
+PASNOBJS += src/ap/pmksa_cache_auth.c
+
+ifdef NEED_EAP_COMMON
+PASNOBJS += src/eap_common/eap_common.c
+endif
+
+ifdef CHAP
+PASNOBJS += src/eap_common/chap.c
+endif
+
+ifdef CONFIG_IEEE8021X_EAPOL
+PASNOBJS += src/eap_peer/eap.c
+PASNOBJS += src/eap_peer/eap_methods.c
+PASNOBJS += src/eapol_supp/eapol_supp_sm.c
+endif
+
+ifeq ($(CONFIG_TLS), openssl)
+PASNOBJS += src/crypto/crypto_openssl.c
+ifdef TLS_FUNCS
+PASNOBJS += src/crypto/tls_openssl.c
+#PASNOBJS += -lssl -lcrypto
+NEED_TLS_PRF_SHA256=y
+endif
+endif
+
+ifeq ($(CONFIG_TLS), gnutls)
+PASNOBJS += src/crypto/crypto_$(CONFIG_CRYPTO).c
+ifdef TLS_FUNCS
+PASNOBJS += src/crypto/tls_gnutls.c
+PASNOBJS += -lgnutls -lgpg-error
+PASNOBJS += -lgcrypt
+endif
+endif
+
+ifdef NEED_TLS_PRF_SHA256
+PASNOBJS += src/crypto/sha256-tlsprf.c
+endif
+
+ifdef NEED_SHA512
+PASNOBJS += src/crypto/sha512-prf.c
+endif
+
+ifdef NEED_SHA384
+PASNOBJS += src/crypto/sha384-prf.c
+endif
+
+PASNOBJS += src/crypto/sha256-prf.c
+
+ifdef NEED_HMAC_SHA512_KDF
+PASNOBJS += src/crypto/sha512-kdf.c
+endif
+
+ifdef NEED_HMAC_SHA384_KDF
+PASNOBJS += src/crypto/sha384-kdf.c
+endif
+
+ifdef NEED_HMAC_SHA256_KDF
+PASNOBJS += src/crypto/sha256-kdf.c
+endif
+
+ifdef NEED_DH_GROUPS
+PASNOBJS += src/crypto/dh_groups.c
+endif
+
+ifdef NEED_AES_SIV
+PASNOBJS += src/crypto/aes-siv.c
+endif
+
+ifdef NEED_AES_CTR
+PASNOBJS += src/crypto/aes-ctr.c
+endif
+
+ifdef NEED_SHA1
+PASNOBJS += src/crypto/sha1-prf.c
+ifdef NEED_TLS_PRF
+PASNOBJS += src/crypto/sha1-tlsprf.c
+endif
+endif
+
+PASNOBJS += src/pasn/pasn_initiator.c
+PASNOBJS += src/pasn/pasn_responder.c
+
########################
include $(CLEAR_VARS)
@@ -1897,3 +2037,14 @@
$(LOCAL_PATH)/aidl
include $(BUILD_STATIC_LIBRARY)
endif # WPA_SUPPLICANT_USE_AIDL == y
+
+#include $(CLEAR_VARS)
+#LOCAL_MODULE = libpasn
+#LOCAL_CFLAGS = $(L_CFLAGS)
+#LOCAL_SRC_FILES = $(PASNOBJS)
+#LOCAL_C_INCLUDES = $(INCLUDES)
+#LOCAL_SHARED_LIBRARIES := libc libcutils liblog
+#ifeq ($(CONFIG_TLS), openssl)
+#LOCAL_SHARED_LIBRARIES := libcrypto libssl
+#endif
+#include $(BUILD_SHARED_LIBRARY)
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 0a71558..c682f73 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -12,6 +12,12 @@
CONFIG_FILE=.config
include ../src/build.rules
+ifdef CONFIG_BUILD_PASN_SO
+# add the dependency this way to allow CONFIG_BUILD_PASN_SO
+# being set in the config which is read by build.rules
+_all: libpasn.so
+endif
+
ifdef CONFIG_BUILD_WPA_CLIENT_SO
# add the dependency this way to allow CONFIG_BUILD_WPA_CLIENT_SO
# being set in the config which is read by build.rules
@@ -76,6 +82,11 @@
ifndef CONFIG_NO_WPA_PASSPHRASE
install -D wpa_passphrase $(DESTDIR)/$(BINDIR)/wpa_passphrase
endif
+
+ifdef CONFIG_BUILD_PASN_SO
+ install -m 0644 -D libpasn.so $(DESTDIR)/$(LIBDIR)/libpasn.so
+endif
+
ifdef CONFIG_BUILD_WPA_CLIENT_SO
install -m 0644 -D libwpa_client.so $(DESTDIR)/$(LIBDIR)/libwpa_client.so
install -m 0644 -D ../src/common/wpa_ctrl.h $(DESTDIR)/$(INCDIR)/wpa_ctrl.h
@@ -418,6 +429,7 @@
NEED_SHA256=y
NEED_SHA384=y
OBJS += ../src/common/ptksa_cache.o
+OBJS += ../src/pasn/pasn_initiator.o
OBJS += pasn_supplicant.o
endif
@@ -987,6 +999,8 @@
ifdef NEED_AP_MLME
OBJS += ../src/ap/wmm.o
OBJS += ../src/ap/ap_list.o
+OBJS += ../src/ap/comeback_token.o
+OBJS += ../src/pasn/pasn_responder.o
OBJS += ../src/ap/ieee802_11.o
OBJS += ../src/ap/hw_features.o
OBJS += ../src/ap/dfs.o
@@ -2093,6 +2107,157 @@
lcov -c -d $(BUILDDIR) > lcov.info
genhtml lcov.info --output-directory lcov-html
+PASN_CFLAGS := $(CFLAGS)
+PASN_CFLAGS += -DCONFIG_PASN
+
+LIBPASNSO := ../src/utils/$(CONFIG_ELOOP).c
+LIBPASNSO += ../src/utils/wpa_debug.c
+LIBPASNSO += ../src/utils/wpabuf.c
+LIBPASNSO += ../src/utils/os_$(CONFIG_OS).c
+LIBPASNSO += ../src/utils/config.c
+LIBPASNSO += ../src/utils/common.c
+
+ifdef NEED_BASE64
+LIBPASNSO += ../src/utils/base64.c
+endif
+
+ifdef CONFIG_WPA_TRACE
+LIBPASNSO += ../src/utils/trace.c
+endif
+
+ifdef CONFIG_EXT_PASSWORD_FILE
+LIBPASNSO += ../src/utils/ext_password_file.c
+endif
+
+ifdef CONFIG_EXT_PASSWORD_TEST
+LIBPASNSO += ../src/utils/ext_password_test.c
+endif
+
+ifdef NEED_EXT_PASSWORD
+LIBPASNSO += ../src/utils/ext_password.c
+endif
+
+ifdef CONFIG_SAE
+LIBPASNSO += ../src/common/sae.c
+endif
+
+ifdef CONFIG_SAE_PK
+LIBPASNSO += ../src/common/sae_pk.c
+endif
+
+ifndef CONFIG_NO_WPA
+LIBPASNSO += ../src/common/wpa_common.c
+endif
+
+LIBPASNSO += ../src/common/ieee802_11_common.c
+
+ifdef NEED_DRAGONFLY
+LIBPASNSO += ../src/common/dragonfly.c
+endif
+
+LIBPASNSO += ../src/common/ptksa_cache.c
+
+ifndef CONFIG_NO_WPA
+LIBPASNSO += ../src/rsn_supp/pmksa_cache.c
+LIBPASNSO += ../src/rsn_supp/wpa_ie.c
+endif
+
+LIBPASNSO += ../src/ap/comeback_token.c
+LIBPASNSO += ../src/ap/pmksa_cache_auth.c
+
+ifdef NEED_EAP_COMMON
+LIBPASNSO += ../src/eap_common/eap_common.c
+endif
+
+ifdef CHAP
+LIBPASNSO += ../src/eap_common/chap.c
+endif
+
+ifdef CONFIG_IEEE8021X_EAPOL
+LIBPASNSO += ../src/eap_peer/eap.c
+LIBPASNSO += ../src/eap_peer/eap_methods.c
+LIBPASNSO += ../src/eapol_supp/eapol_supp_sm.c
+endif
+
+ifeq ($(CONFIG_TLS), wolfssl)
+LIBPASNSO += ../src/crypto/crypto_wolfssl.c
+ifdef TLS_FUNCS
+LIBPASNSO += ../src/crypto/tls_wolfssl.c
+NEED_TLS_PRF_SHA256=y
+LIBPASNSO += -lwolfssl -lm
+endif
+endif
+
+ifeq ($(CONFIG_TLS), openssl)
+LIBPASNSO += ../src/crypto/crypto_openssl.c
+ifdef TLS_FUNCS
+LIBPASNSO += ../src/crypto/tls_openssl.c
+LIBPASNSO += -lssl -lcrypto
+NEED_TLS_PRF_SHA256=y
+endif
+endif
+
+ifeq ($(CONFIG_TLS), gnutls)
+LIBPASNSO += ../src/crypto/crypto_$(CONFIG_CRYPTO).c
+ifdef TLS_FUNCS
+LIBPASNSO += ../src/crypto/tls_gnutls.c
+LIBPASNSO += -lgnutls -lgpg-error
+LIBPASNSO += -lgcrypt
+endif
+endif
+
+ifdef NEED_TLS_PRF_SHA256
+LIBPASNSO += ../src/crypto/sha256-tlsprf.c
+endif
+
+ifdef NEED_SHA512
+LIBPASNSO += ../src/crypto/sha512-prf.c
+endif
+
+ifdef NEED_SHA384
+LIBPASNSO += ../src/crypto/sha384-prf.c
+endif
+
+LIBPASNSO += ../src/crypto/sha256-prf.c
+
+ifdef NEED_HMAC_SHA512_KDF
+LIBPASNSO += ../src/crypto/sha512-kdf.c
+endif
+
+ifdef NEED_HMAC_SHA384_KDF
+LIBPASNSO += ../src/crypto/sha384-kdf.c
+endif
+
+ifdef NEED_HMAC_SHA256_KDF
+LIBPASNSO += ../src/crypto/sha256-kdf.c
+endif
+
+ifdef NEED_DH_GROUPS
+LIBPASNSO += ../src/crypto/dh_groups.c
+endif
+
+ifdef NEED_AES_SIV
+LIBPASNSO += ../src/crypto/aes-siv.c
+endif
+
+ifdef NEED_AES_CTR
+LIBPASNSO += ../src/crypto/aes-ctr.c
+endif
+
+ifdef NEED_SHA1
+LIBPASNSO += ../src/crypto/sha1-prf.c
+ifdef NEED_TLS_PRF
+LIBPASNSO += ../src/crypto/sha1-tlsprf.c
+endif
+endif
+
+LIBPASNSO += ../src/pasn/pasn_initiator.c
+LIBPASNSO += ../src/pasn/pasn_responder.c
+
+libpasn.so: $(LIBPASNSO)
+ @$(E) " CC $@ ($^)"
+ $(Q)$(CC) $(LDFLAGS) -o $@ $(PASN_CFLAGS) -shared -fPIC -lcrypto $^
+
clean: common-clean
$(MAKE) -C ../src clean
$(MAKE) -C dbus clean
@@ -2103,6 +2268,7 @@
rm -f lcov.info
rm -rf lcov-html
rm -f libwpa_client.a
+ rm -f libpasn.so
rm -f libwpa_client.so
rm -f libwpa_test1 libwpa_test2
rm -f wpa_passphrase
diff --git a/wpa_supplicant/README-HS20 b/wpa_supplicant/README-HS20
index 0cc5f39..7d30e23 100644
--- a/wpa_supplicant/README-HS20
+++ b/wpa_supplicant/README-HS20
@@ -199,7 +199,26 @@
# be used to configure alternative FQDNs that will be considered home
# networks.
#
+# home_ois: Home OI(s)
+# This string field contains one or more comma delimited OIs (hexdump)
+# identifying the access the access points that support authentication
+# with this credential. There are an alternative to the use of the realm
+# parameter. When using Home OIs to match the network, the EAP parameters
+# need to be pre-configured with the credentials since the NAI Realm
+# information may not be available or fetched.
+# A successful authentication with the access point is possible as soon
+# as at least one Home OI from the list matches an OI in the Roaming
+# Consortium advertised by the access point.
+# (Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/HomeOIList/<X+>/HomeOI)
+#
+# required_home_ois: Required Home OI(s)
+# This string field contains the set of Home OI(s) (hexdump) that are
+# required to be advertised by the AP for the credential to be considered
+# matching.
+# (Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/HomeOIList/<X+>/HomeOIRequired)
+#
# roaming_consortium: Roaming Consortium OI
+# Deprecated: use home_ois instead.
# If roaming_consortium_len is non-zero, this field contains the
# Roaming Consortium OI that can be used to determine which access
# points support authentication with this credential. This is an
@@ -209,6 +228,7 @@
# may not be available or fetched.
#
# required_roaming_consortium: Required Roaming Consortium OI
+# Deprecated: use required_home_ois instead.
# If required_roaming_consortium_len is non-zero, this field contains the
# Roaming Consortium OI that is required to be advertised by the AP for
# the credential to be considered matching.
@@ -325,7 +345,7 @@
# password="password"
# ca_cert="/etc/wpa_supplicant/ca.pem"
# domain="example.com"
-# roaming_consortium=223344
+# home_ois="223344"
# roaming_consortiums="112233,4455667788,aabbcc"
# eap=TTLS
# phase2="auth=MSCHAPV2"
diff --git a/wpa_supplicant/aidl/aidl_manager.cpp b/wpa_supplicant/aidl/aidl_manager.cpp
index f644a39..e2523d4 100644
--- a/wpa_supplicant/aidl/aidl_manager.cpp
+++ b/wpa_supplicant/aidl/aidl_manager.cpp
@@ -472,7 +472,7 @@
// Enable randomized source MAC address for GAS/ANQP
// Set the lifetime to 0, guarantees a unique address for each GAS
// session
- wpa_s->conf->gas_rand_mac_addr = 1;
+ wpa_s->conf->gas_rand_mac_addr = WPAS_MAC_ADDR_STYLE_RANDOM;
wpa_s->conf->gas_rand_addr_lifetime = 0;
}
diff --git a/wpa_supplicant/aidl/misc_utils.h b/wpa_supplicant/aidl/misc_utils.h
index c529e3d..d0330e0 100644
--- a/wpa_supplicant/aidl/misc_utils.h
+++ b/wpa_supplicant/aidl/misc_utils.h
@@ -88,6 +88,7 @@
ss.write((char *) pmksa_entry->pmk, pmksa_entry->pmk_len);
ss.write((char *) pmksa_entry->pmkid, PMKID_LEN);
ss.write((char *) pmksa_entry->aa, ETH_ALEN);
+ ss.write((char *) pmksa_entry->spa, ETH_ALEN);
// Omit wpa_ssid field because the network is created on connecting to a access point.
ss.write((char *) &pmksa_entry->akmp, sizeof(pmksa_entry->akmp));
ss.write((char *) &pmksa_entry->reauth_time, sizeof(pmksa_entry->reauth_time));
@@ -110,7 +111,7 @@
ss.read((char *) &pmksa_entry->pmk_len, sizeof(pmksa_entry->pmk_len));
if ((pmksa_entry->pmk_len > PMK_LEN_MAX) ||
(ss.str().size() < (sizeof(pmksa_entry->pmk_len) + pmksa_entry->pmk_len +
- PMKID_LEN + ETH_ALEN + sizeof(pmksa_entry->akmp) +
+ PMKID_LEN + ETH_ALEN + ETH_ALEN + sizeof(pmksa_entry->akmp) +
sizeof(pmksa_entry->reauth_time) + sizeof(pmksa_entry->expiration) +
sizeof(pmksa_entry->opportunistic) + 1 /* fils_cache_id_set */)))
return -1;
@@ -118,6 +119,7 @@
ss.read((char *) pmksa_entry->pmk, pmksa_entry->pmk_len);
ss.read((char *) pmksa_entry->pmkid, PMKID_LEN);
ss.read((char *) pmksa_entry->aa, ETH_ALEN);
+ ss.read((char *) pmksa_entry->spa, ETH_ALEN);
// Omit wpa_ssid field because the network is created on connecting to a access point.
ss.read((char *) &pmksa_entry->akmp, sizeof(pmksa_entry->akmp));
ss.read((char *) &pmksa_entry->reauth_time, sizeof(pmksa_entry->reauth_time));
diff --git a/wpa_supplicant/aidl/p2p_iface.cpp b/wpa_supplicant/aidl/p2p_iface.cpp
index 9927ba4..2afd75b 100644
--- a/wpa_supplicant/aidl/p2p_iface.cpp
+++ b/wpa_supplicant/aidl/p2p_iface.cpp
@@ -1652,6 +1652,10 @@
"Passphrase is invalid.");
}
+ wpa_printf(MSG_DEBUG,
+ "P2P: Add group with config Role: %s network name: %s freq: %d",
+ joinExistingGroup ? "CLIENT" : "GO",
+ wpa_ssid_txt(ssid.data(), ssid.size()), freq);
if (!joinExistingGroup) {
struct p2p_data *p2p = wpa_s->global->p2p;
os_memcpy(p2p->ssid, ssid.data(), ssid.size());
diff --git a/wpa_supplicant/aidl/sta_iface.cpp b/wpa_supplicant/aidl/sta_iface.cpp
index e8c1927..029b99a 100644
--- a/wpa_supplicant/aidl/sta_iface.cpp
+++ b/wpa_supplicant/aidl/sta_iface.cpp
@@ -2070,18 +2070,18 @@
SignalPollResult result;
result.linkId = 0;
- result.currentRssiDbm = mlo_si.links[i].current_signal;
- result.txBitrateMbps = mlo_si.links[i].current_txrate / 1000;
- result.rxBitrateMbps = mlo_si.links[i].current_rxrate / 1000;
+ result.currentRssiDbm = mlo_si.links[i].data.signal;
+ result.txBitrateMbps = mlo_si.links[i].data.current_tx_rate / 1000;
+ result.rxBitrateMbps = mlo_si.links[i].data.current_rx_rate / 1000;
result.frequencyMhz = mlo_si.links[i].frequency;
results.push_back(result);
}
} else if (wpa_drv_signal_poll(wpa_s, &si) == 0) {
SignalPollResult result;
result.linkId = 0;
- result.currentRssiDbm = si.current_signal;
- result.txBitrateMbps = si.current_txrate / 1000;
- result.rxBitrateMbps = si.current_rxrate / 1000;
+ result.currentRssiDbm = si.data.signal;
+ result.txBitrateMbps = si.data.current_tx_rate / 1000;
+ result.rxBitrateMbps = si.data.current_rx_rate / 1000;
result.frequencyMhz = si.frequency;
results.push_back(result);
}
diff --git a/wpa_supplicant/aidl/sta_network.cpp b/wpa_supplicant/aidl/sta_network.cpp
index 5a83b05..4d816e4 100644
--- a/wpa_supplicant/aidl/sta_network.cpp
+++ b/wpa_supplicant/aidl/sta_network.cpp
@@ -2617,13 +2617,13 @@
struct wpa_supplicant *wpa_s = retrieveIfacePtr();
switch (mode) {
case SaeH2eMode::DISABLED:
- wpa_s->conf->sae_pwe = 0;
+ wpa_s->conf->sae_pwe = SAE_PWE_HUNT_AND_PECK;
break;
case SaeH2eMode::H2E_MANDATORY:
- wpa_s->conf->sae_pwe = 1;
+ wpa_s->conf->sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
break;
case SaeH2eMode::H2E_OPTIONAL:
- wpa_s->conf->sae_pwe = 2;
+ wpa_s->conf->sae_pwe = SAE_PWE_BOTH;
break;
}
resetInternalStateAfterParamsUpdate();
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 16d6c90..f4c3811 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -706,8 +706,12 @@
bss->wpa_group_rekey = 86400;
}
- if (ssid->ieee80211w != MGMT_FRAME_PROTECTION_DEFAULT)
+ if (ssid->ieee80211w != MGMT_FRAME_PROTECTION_DEFAULT) {
bss->ieee80211w = ssid->ieee80211w;
+ } else if (wpa_s->conf->pmf != MGMT_FRAME_PROTECTION_DEFAULT) {
+ if (ssid->mode == WPAS_MODE_AP)
+ bss->ieee80211w = wpa_s->conf->pmf;
+ }
#ifdef CONFIG_OCV
bss->ocv = ssid->ocv;
diff --git a/wpa_supplicant/bgscan_learn.c b/wpa_supplicant/bgscan_learn.c
index cb732f7..75bdec1 100644
--- a/wpa_supplicant/bgscan_learn.c
+++ b/wpa_supplicant/bgscan_learn.c
@@ -422,7 +422,7 @@
/* Poll for signal info to set initial scan interval */
struct wpa_signal_info siginfo;
if (wpa_drv_signal_poll(wpa_s, &siginfo) == 0 &&
- siginfo.current_signal >= data->signal_threshold)
+ siginfo.data.signal >= data->signal_threshold)
data->scan_interval = data->long_interval;
}
diff --git a/wpa_supplicant/bgscan_simple.c b/wpa_supplicant/bgscan_simple.c
index 41a26df..5a8f97c 100644
--- a/wpa_supplicant/bgscan_simple.c
+++ b/wpa_supplicant/bgscan_simple.c
@@ -137,7 +137,7 @@
/* Poll for signal info to set initial scan interval */
struct wpa_signal_info siginfo;
if (wpa_drv_signal_poll(wpa_s, &siginfo) == 0 &&
- siginfo.current_signal >= data->signal_threshold)
+ siginfo.data.signal >= data->signal_threshold)
data->scan_interval = data->long_interval;
}
wpa_printf(MSG_DEBUG, "bgscan simple: Init scan interval: %d",
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index dcc28be..0f986bb 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -993,6 +993,14 @@
if (wpa_bss_in_use(wpa_s, bss))
continue;
+ if (wpa_s->reassoc_same_ess &&
+ wpa_s->wpa_state != WPA_COMPLETED &&
+ wpa_s->last_ssid &&
+ bss->ssid_len == wpa_s->last_ssid->ssid_len &&
+ os_memcmp(bss->ssid, wpa_s->last_ssid->ssid,
+ bss->ssid_len) == 0)
+ continue;
+
if (os_reltime_before(&bss->last_update, &t)) {
wpa_bss_remove(wpa_s, bss, __func__);
} else
@@ -1438,3 +1446,21 @@
return ieee802_11_ext_capab(wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB),
capab);
}
+
+
+/**
+ * wpa_bss_defrag_mle - Get a buffer holding a de-fragmented ML element
+ * @bss: BSS table entry
+ * @type: ML control type
+ */
+struct wpabuf * wpa_bss_defrag_mle(const struct wpa_bss *bss, u8 type)
+{
+ struct ieee802_11_elems elems;
+ const u8 *pos = wpa_bss_ie_ptr(bss);
+ size_t len = bss->ie_len;
+
+ if (ieee802_11_parse_elems(pos, len, &elems, 1) == ParseFailed)
+ return NULL;
+
+ return ieee802_11_defrag_mle(&elems, type);
+}
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index b68fc87..611da88 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -201,4 +201,6 @@
unsigned int age_ms,
struct os_reltime *update_time);
+struct wpabuf * wpa_bss_defrag_mle(const struct wpa_bss *bss, u8 type);
+
#endif /* BSS_H */
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 2b1bdeb..f3e2edb 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -2345,6 +2345,50 @@
#endif /* NO_CONFIG_WRITE */
+static int wpa_config_parse_mac_value(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ u8 mac_value[ETH_ALEN];
+
+ if (hwaddr_aton(value, mac_value) == 0) {
+ if (os_memcmp(mac_value, ssid->mac_value, ETH_ALEN) == 0)
+ return 1;
+ os_memcpy(ssid->mac_value, mac_value, ETH_ALEN);
+ return 0;
+ }
+
+ wpa_printf(MSG_ERROR, "Line %d: Invalid MAC address '%s'",
+ line, value);
+ return -1;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_mac_value(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ const size_t size = 3 * ETH_ALEN;
+ char *value;
+ int res;
+
+ if (ssid->mac_addr != WPAS_MAC_ADDR_STYLE_DEDICATED_PER_ESS)
+ return NULL;
+
+ value = os_malloc(size);
+ if (!value)
+ return NULL;
+ res = os_snprintf(value, size, MACSTR, MAC2STR(ssid->mac_value));
+ if (os_snprintf_error(size, res)) {
+ os_free(value);
+ return NULL;
+ }
+ value[size - 1] = '\0';
+ return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
/* Helper macros for network block parser */
#ifdef OFFSET
@@ -2644,7 +2688,8 @@
{ INT(update_identifier) },
{ STR_RANGE(roaming_consortium_selection, 0, MAX_ROAMING_CONS_OI_LEN) },
#endif /* CONFIG_HS20 */
- { INT_RANGE(mac_addr, 0, 2) },
+ { INT_RANGE(mac_addr, 0, 3) },
+ { FUNC_KEY(mac_value) },
{ INT_RANGE(pbss, 0, 2) },
{ INT_RANGE(wps_disabled, 0, 1) },
{ INT_RANGE(fils_dh_group, 0, 65535) },
@@ -2665,6 +2710,7 @@
{ INT_RANGE(beacon_prot, 0, 1) },
{ INT_RANGE(transition_disable, 0, 255) },
{ INT_RANGE(sae_pk, 0, 2) },
+ { INT_RANGE(disable_eht, 0, 1)},
};
#undef OFFSET
@@ -3181,7 +3227,7 @@
#ifdef CONFIG_MACSEC
ssid->mka_priority = DEFAULT_PRIO_NOT_KEY_SERVER;
#endif /* CONFIG_MACSEC */
- ssid->mac_addr = -1;
+ ssid->mac_addr = WPAS_MAC_ADDR_STYLE_NOT_SET;
ssid->max_oper_chwidth = DEFAULT_MAX_OPER_CHWIDTH;
}
@@ -3532,53 +3578,62 @@
}
-static int wpa_config_set_cred_roaming_consortiums(struct wpa_cred *cred,
- const char *value)
+static int
+wpa_config_set_cred_ois(u8 cred_ois[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN],
+ size_t cred_ois_len[MAX_ROAMING_CONS],
+ unsigned int *cred_num_ois,
+ const char *value)
{
- u8 roaming_consortiums[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN];
- size_t roaming_consortiums_len[MAX_ROAMING_CONS];
- unsigned int num_roaming_consortiums = 0;
+ u8 ois[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN];
+ size_t ois_len[MAX_ROAMING_CONS];
+ unsigned int num_ois = 0;
const char *pos, *end;
size_t len;
- os_memset(roaming_consortiums, 0, sizeof(roaming_consortiums));
- os_memset(roaming_consortiums_len, 0, sizeof(roaming_consortiums_len));
+ len = os_strlen(value);
+ if (len / 2 < 3) {
+ wpa_printf(MSG_ERROR,
+ "Invalid organisation identifier (OI) list: %s",
+ value);
+ return -1;
+ }
+
+ os_memset(ois, 0, sizeof(ois));
+ os_memset(ois_len, 0, sizeof(ois_len));
for (pos = value;;) {
end = os_strchr(pos, ',');
len = end ? (size_t) (end - pos) : os_strlen(pos);
if (!end && len == 0)
break;
- if (len == 0 || (len & 1) != 0 ||
+ if (len / 2 < 3 || (len & 1) != 0 ||
len / 2 > MAX_ROAMING_CONS_OI_LEN ||
hexstr2bin(pos,
- roaming_consortiums[num_roaming_consortiums],
+ ois[num_ois],
len / 2) < 0) {
wpa_printf(MSG_INFO,
- "Invalid roaming_consortiums entry: %s",
+ "Invalid organisation identifier (OI) entry: %s",
pos);
return -1;
}
- roaming_consortiums_len[num_roaming_consortiums] = len / 2;
- num_roaming_consortiums++;
+ ois_len[num_ois] = len / 2;
+ num_ois++;
if (!end)
break;
- if (num_roaming_consortiums >= MAX_ROAMING_CONS) {
+ if (num_ois >= MAX_ROAMING_CONS) {
wpa_printf(MSG_INFO,
- "Too many roaming_consortiums OIs");
+ "Too many OIs");
return -1;
}
pos = end + 1;
}
- os_memcpy(cred->roaming_consortiums, roaming_consortiums,
- sizeof(roaming_consortiums));
- os_memcpy(cred->roaming_consortiums_len, roaming_consortiums_len,
- sizeof(roaming_consortiums_len));
- cred->num_roaming_consortiums = num_roaming_consortiums;
+ os_memcpy(cred_ois, ois, sizeof(ois));
+ os_memcpy(cred_ois_len, ois_len, sizeof(ois_len));
+ *cred_num_ois = num_ois;
return 0;
}
@@ -3813,36 +3868,70 @@
}
if (os_strcmp(var, "roaming_consortium") == 0) {
- if (len < 3 || len > sizeof(cred->roaming_consortium)) {
+ if (len < 3 || len > sizeof(cred->home_ois[0])) {
wpa_printf(MSG_ERROR, "Line %d: invalid "
"roaming_consortium length %d (3..15 "
"expected)", line, (int) len);
os_free(val);
return -1;
}
- os_memcpy(cred->roaming_consortium, val, len);
- cred->roaming_consortium_len = len;
+ wpa_printf(MSG_WARNING,
+ "Line %d: option roaming_consortium is deprecated and will be removed in the future",
+ line);
+ os_memcpy(cred->home_ois[0], val, len);
+ cred->home_ois_len[0] = len;
+ cred->num_home_ois = 1;
os_free(val);
return 0;
}
if (os_strcmp(var, "required_roaming_consortium") == 0) {
- if (len < 3 || len > sizeof(cred->required_roaming_consortium))
- {
+ if (len < 3 || len > sizeof(cred->required_home_ois[0])) {
wpa_printf(MSG_ERROR, "Line %d: invalid "
"required_roaming_consortium length %d "
"(3..15 expected)", line, (int) len);
os_free(val);
return -1;
}
- os_memcpy(cred->required_roaming_consortium, val, len);
- cred->required_roaming_consortium_len = len;
+ wpa_printf(MSG_WARNING,
+ "Line %d: option required_roaming_consortium is deprecated and will be removed in the future",
+ line);
+ os_memcpy(cred->required_home_ois[0], val, len);
+ cred->required_home_ois_len[0] = len;
+ cred->num_required_home_ois = 1;
os_free(val);
return 0;
}
+ if (os_strcmp(var, "home_ois") == 0) {
+ res = wpa_config_set_cred_ois(cred->home_ois,
+ cred->home_ois_len,
+ &cred->num_home_ois,
+ val);
+ if (res < 0)
+ wpa_printf(MSG_ERROR, "Line %d: invalid home_ois",
+ line);
+ os_free(val);
+ return res;
+ }
+
+ if (os_strcmp(var, "required_home_ois") == 0) {
+ res = wpa_config_set_cred_ois(cred->required_home_ois,
+ cred->required_home_ois_len,
+ &cred->num_required_home_ois,
+ val);
+ if (res < 0)
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid required_home_ois", line);
+ os_free(val);
+ return res;
+ }
+
if (os_strcmp(var, "roaming_consortiums") == 0) {
- res = wpa_config_set_cred_roaming_consortiums(cred, val);
+ res = wpa_config_set_cred_ois(cred->roaming_consortiums,
+ cred->roaming_consortiums_len,
+ &cred->num_roaming_consortiums,
+ val);
if (res < 0)
wpa_printf(MSG_ERROR,
"Line %d: invalid roaming_consortiums",
@@ -4162,14 +4251,14 @@
size_t buflen;
char *buf;
- if (!cred->roaming_consortium_len)
+ if (!cred->num_home_ois || !cred->home_ois_len[0])
return NULL;
- buflen = cred->roaming_consortium_len * 2 + 1;
+ buflen = cred->home_ois_len[0] * 2 + 1;
buf = os_malloc(buflen);
if (buf == NULL)
return NULL;
- wpa_snprintf_hex(buf, buflen, cred->roaming_consortium,
- cred->roaming_consortium_len);
+ wpa_snprintf_hex(buf, buflen, cred->home_ois[0],
+ cred->home_ois_len[0]);
return buf;
}
@@ -4177,14 +4266,64 @@
size_t buflen;
char *buf;
- if (!cred->required_roaming_consortium_len)
+ if (!cred->num_required_home_ois ||
+ !cred->required_home_ois_len[0])
return NULL;
- buflen = cred->required_roaming_consortium_len * 2 + 1;
+ buflen = cred->required_home_ois_len[0] * 2 + 1;
buf = os_malloc(buflen);
if (buf == NULL)
return NULL;
- wpa_snprintf_hex(buf, buflen, cred->required_roaming_consortium,
- cred->required_roaming_consortium_len);
+ wpa_snprintf_hex(buf, buflen, cred->required_home_ois[0],
+ cred->required_home_ois_len[0]);
+ return buf;
+ }
+
+ if (os_strcmp(var, "home_ois") == 0) {
+ size_t buflen;
+ char *buf, *pos;
+ size_t i;
+
+ if (!cred->num_home_ois)
+ return NULL;
+ buflen = cred->num_home_ois * MAX_ROAMING_CONS_OI_LEN * 2 + 1;
+ buf = os_malloc(buflen);
+ if (!buf)
+ return NULL;
+ pos = buf;
+ for (i = 0; i < cred->num_home_ois; i++) {
+ if (i > 0)
+ *pos++ = ',';
+ pos += wpa_snprintf_hex(
+ pos, buf + buflen - pos,
+ cred->home_ois[i],
+ cred->home_ois_len[i]);
+ }
+ *pos = '\0';
+ return buf;
+ }
+
+ if (os_strcmp(var, "required_home_ois") == 0) {
+ size_t buflen;
+ char *buf, *pos;
+ size_t i;
+
+ if (!cred->num_required_home_ois)
+ return NULL;
+ buflen = cred->num_required_home_ois *
+ MAX_ROAMING_CONS_OI_LEN * 2 + 1;
+ buf = os_malloc(buflen);
+ if (!buf)
+ return NULL;
+ pos = buf;
+ for (i = 0; i < cred->num_required_home_ois; i++) {
+ if (i > 0)
+ *pos++ = ',';
+ pos += wpa_snprintf_hex(
+ pos, buf + buflen - pos,
+ cred->required_home_ois[i],
+ cred->required_home_ois_len[i]);
+ }
+ *pos = '\0';
return buf;
}
@@ -5288,6 +5427,7 @@
{ INT_RANGE(auto_interworking, 0, 1), 0 },
{ INT(okc), 0 },
{ INT(pmf), 0 },
+ { INT_RANGE(sae_check_mfp, 0, 1), 0 },
{ FUNC(sae_groups), 0 },
{ INT_RANGE(sae_pwe, 0, 3), 0 },
{ INT_RANGE(sae_pmkid_in_assoc, 0, 1), 0 },
@@ -5306,9 +5446,9 @@
{ STR(osu_dir), 0 },
{ STR(wowlan_triggers), CFG_CHANGED_WOWLAN_TRIGGERS },
{ INT(p2p_search_delay), 0},
- { INT(mac_addr), 0 },
+ { INT_RANGE(mac_addr, 0, 2), 0 },
{ INT(rand_addr_lifetime), 0 },
- { INT(preassoc_mac_addr), 0 },
+ { INT_RANGE(preassoc_mac_addr, 0, 2), 0 },
{ INT(key_mgmt_offload), 0},
{ INT(passive_scan), 0 },
{ INT(reassoc_same_bss_optim), 0 },
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 0316e9b..dbb79dd 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -272,36 +272,50 @@
size_t num_domain;
/**
- * roaming_consortium - Roaming Consortium OI
+ * home_ois - Home OIs
*
- * If roaming_consortium_len is non-zero, this field contains the
- * Roaming Consortium OI that can be used to determine which access
- * points support authentication with this credential. This is an
- * alternative to the use of the realm parameter. When using Roaming
- * Consortium to match the network, the EAP parameters need to be
- * pre-configured with the credential since the NAI Realm information
- * may not be available or fetched.
+ * If num_home_ois is non-zero, this field contains the set of Home OIs
+ * that can be use to determine which access points support
+ * authentication with this credential. These are an alternative to the
+ * use of the realm parameter. When using Home OIs to match the network,
+ * the EAP parameters need to be pre-configured with the credentials
+ * since the NAI Realm information may not be available or fetched.
+ * A successful authentication with the access point is possible as soon
+ * as at least one Home OI from the list matches an OI in the Roaming
+ * Consortium list advertised by the access point.
+ * (Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/HomeOIList/<X+>/HomeOI)
*/
- u8 roaming_consortium[15];
+ u8 home_ois[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN];
/**
- * roaming_consortium_len - Length of roaming_consortium
+ * home_ois_len - Length of home_ois[i]
*/
- size_t roaming_consortium_len;
+ size_t home_ois_len[MAX_ROAMING_CONS];
/**
- * required_roaming_consortium - Required Roaming Consortium OI
+ * num_home_ois - Number of entries in home_ois
+ */
+ unsigned int num_home_ois;
+
+ /**
+ * required_home_ois - Required Home OI(s)
*
- * If required_roaming_consortium_len is non-zero, this field contains
- * the Roaming Consortium OI that is required to be advertised by the AP
- * for the credential to be considered matching.
+ * If required_home_ois_len is non-zero, this field contains the set of
+ * Home OI(s) that are required to be advertised by the AP for the
+ * credential to be considered matching.
+ * (Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/HomeOIList/<X+>/HomeOIRequired)
*/
- u8 required_roaming_consortium[15];
+ u8 required_home_ois[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN];
/**
- * required_roaming_consortium_len - Length of required_roaming_consortium
+ * required_home_ois_len - Length of required_home_ois
*/
- size_t required_roaming_consortium_len;
+ size_t required_home_ois_len[MAX_ROAMING_CONS];
+
+ /**
+ * num_required_home_ois - Number of entries in required_home_ois
+ */
+ unsigned int num_required_home_ois;
/**
* roaming_consortiums - Roaming Consortium OI(s) memberships
@@ -1253,6 +1267,25 @@
enum mfp_options pmf;
/**
+ * sae_check_mfp - Whether to limit SAE based on PMF capabilities
+ *
+ * With this check SAE key_mgmt will not be selected if PMF is
+ * not enabled.
+ * Scenarios where enabling this check will limit SAE:
+ * 1) ieee8011w=0 is set for the network.
+ * 2) The AP does not have PMF enabled.
+ * 3) ieee8011w for the network is the default(3), pmf=1 is enabled
+ * globally and the device does not support the BIP cipher.
+ *
+ * Useful to allow the BIP cipher check that occurs for ieee80211w=3
+ * and pmf=1 to also avoid using SAE key_mgmt.
+ * Useful when hardware does not support BIP to still to allow
+ * connecting to sae_require_mfp=1 WPA2+WPA3-Personal transition mode
+ *access points by automatically selecting PSK instead of SAE.
+ */
+ int sae_check_mfp;
+
+ /**
* sae_groups - Preference list of enabled groups for SAE
*
* By default (if this parameter is not set), the mandatory group 19
@@ -1268,7 +1301,7 @@
* 1 = hash-to-element only
* 2 = both hunting-and-pecking loop and hash-to-element enabled
*/
- int sae_pwe;
+ enum sae_pwe sae_pwe;
/**
* sae_pmkid_in_assoc - Whether to include PMKID in SAE Assoc Req
@@ -1388,7 +1421,7 @@
* the per-network mac_addr parameter. Global mac_addr=1 can be used to
* change this default behavior.
*/
- int mac_addr;
+ enum wpas_mac_addr_style mac_addr;
/**
* rand_addr_lifetime - Lifetime of random MAC address in seconds
@@ -1402,7 +1435,7 @@
* 1 = use random MAC address
* 2 = like 1, but maintain OUI (with local admin bit set)
*/
- int preassoc_mac_addr;
+ enum wpas_mac_addr_style preassoc_mac_addr;
/**
* key_mgmt_offload - Use key management offload
@@ -1611,7 +1644,7 @@
* 1 = use random MAC address
* 2 = like 1, but maintain OUI (with local admin bit set)
*/
- int gas_rand_mac_addr;
+ enum wpas_mac_addr_style gas_rand_mac_addr;
/**
* dpp_config_processing - How to process DPP configuration
@@ -1836,6 +1869,7 @@
* @name: Name of the configuration (e.g., path and file name for the
* configuration file)
* @cfgp: Pointer to previously allocated configuration data or %NULL if none
+ * @ro: Whether to mark networks from this configuration as read-only
* Returns: Pointer to allocated configuration data or %NULL on failure
*
* This function reads configuration data, parses its contents, and allocates
@@ -1844,7 +1878,8 @@
*
* Each configuration backend needs to implement this function.
*/
-struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp);
+struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp,
+ bool ro);
/**
* wpa_config_write - Write or update configuration data
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 372a57b..1b8526b 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -53,6 +53,13 @@
ssid->group_cipher &= ~WPA_CIPHER_CCMP;
}
+ if (is_6ghz_freq(ssid->frequency) && ssid->mode == WPAS_MODE_MESH &&
+ ssid->key_mgmt == WPA_KEY_MGMT_NONE) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: key_mgmt for mesh network in 6 GHz should be SAE",
+ line);
+ errors++;
+ }
if (ssid->mode == WPAS_MODE_MESH &&
(ssid->key_mgmt != WPA_KEY_MGMT_NONE &&
ssid->key_mgmt != WPA_KEY_MGMT_SAE)) {
@@ -289,7 +296,8 @@
#endif /* CONFIG_NO_CONFIG_BLOBS */
-struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp)
+struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp,
+ bool ro)
{
FILE *f;
char buf[512], *pos;
@@ -331,6 +339,7 @@
while (wpa_config_get_line(buf, sizeof(buf), f, &line, &pos)) {
if (os_strcmp(pos, "network={") == 0) {
ssid = wpa_config_read_network(f, &line, id++);
+ ssid->ro = ro;
if (ssid == NULL) {
wpa_printf(MSG_ERROR, "Line %d: failed to "
"parse network block.", line);
@@ -880,6 +889,7 @@
#ifdef CONFIG_HE_OVERRIDES
INT(disable_he);
#endif /* CONFIG_HE_OVERRIDES */
+ INT(disable_eht);
#undef STR
#undef INT
@@ -921,12 +931,6 @@
if (cred->domain_suffix_match)
fprintf(f, "\tdomain_suffix_match=\"%s\"\n",
cred->domain_suffix_match);
- if (cred->roaming_consortium_len) {
- fprintf(f, "\troaming_consortium=");
- for (i = 0; i < cred->roaming_consortium_len; i++)
- fprintf(f, "%02x", cred->roaming_consortium[i]);
- fprintf(f, "\n");
- }
if (cred->eap_method) {
const char *name;
name = eap_get_name(cred->eap_method[0].vendor,
@@ -1002,12 +1006,32 @@
}
}
- if (cred->required_roaming_consortium_len) {
- fprintf(f, "\trequired_roaming_consortium=");
- for (i = 0; i < cred->required_roaming_consortium_len; i++)
- fprintf(f, "%02x",
- cred->required_roaming_consortium[i]);
- fprintf(f, "\n");
+ if (cred->num_home_ois) {
+ size_t j;
+
+ fprintf(f, "\thome_ois=\"");
+ for (i = 0; i < cred->num_home_ois; i++) {
+ if (i > 0)
+ fprintf(f, ",");
+ for (j = 0; j < cred->home_ois_len[i]; j++)
+ fprintf(f, "%02x",
+ cred->home_ois[i][j]);
+ }
+ fprintf(f, "\"\n");
+ }
+
+ if (cred->num_required_home_ois) {
+ size_t j;
+
+ fprintf(f, "\trequired_home_ois=\"");
+ for (i = 0; i < cred->num_required_home_ois; i++) {
+ if (i > 0)
+ fprintf(f, ",");
+ for (j = 0; j < cred->required_home_ois_len[i]; j++)
+ fprintf(f, "%02x",
+ cred->required_home_ois[i][j]);
+ }
+ fprintf(f, "\"\n");
}
if (cred->num_roaming_consortiums) {
@@ -1358,6 +1382,9 @@
if (config->beacon_int)
fprintf(f, "beacon_int=%d\n", config->beacon_int);
+ if (config->sae_check_mfp)
+ fprintf(f, "sae_check_mfp=%d\n", config->sae_check_mfp);
+
if (config->sae_groups) {
int i;
fprintf(f, "sae_groups=");
@@ -1634,7 +1661,8 @@
}
for (ssid = config->ssid; ssid; ssid = ssid->next) {
- if (ssid->key_mgmt == WPA_KEY_MGMT_WPS || ssid->temporary)
+ if (ssid->key_mgmt == WPA_KEY_MGMT_WPS || ssid->temporary ||
+ ssid->ro)
continue; /* do not save temporary networks */
if (wpa_key_mgmt_wpa_psk_no_sae(ssid->key_mgmt) &&
!ssid->psk_set && !ssid->passphrase)
diff --git a/wpa_supplicant/config_none.c b/wpa_supplicant/config_none.c
index 0bc977e..01e7aad 100644
--- a/wpa_supplicant/config_none.c
+++ b/wpa_supplicant/config_none.c
@@ -17,7 +17,8 @@
#include "base64.h"
-struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp)
+struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp,
+ bool ro)
{
struct wpa_config *config;
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index adc1a06..33b46e5 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -46,7 +46,7 @@
#define DEFAULT_MAX_OPER_CHWIDTH -1
/* Consider global sae_pwe for SAE mechanism for PWE derivation */
-#define DEFAULT_SAE_PWE 4
+#define DEFAULT_SAE_PWE SAE_PWE_NOT_SET
struct psk_list_entry {
struct dl_list list;
@@ -70,6 +70,14 @@
SAE_PK_MODE_DISABLED = 2,
};
+enum wpas_mac_addr_style {
+ WPAS_MAC_ADDR_STYLE_NOT_SET = -1,
+ WPAS_MAC_ADDR_STYLE_PERMANENT = 0,
+ WPAS_MAC_ADDR_STYLE_RANDOM = 1,
+ WPAS_MAC_ADDR_STYLE_RANDOM_SAME_OUI = 2,
+ WPAS_MAC_ADDR_STYLE_DEDICATED_PER_ESS = 3,
+};
+
/**
* struct wpa_ssid - Network configuration data
*
@@ -107,6 +115,21 @@
int id;
/**
+ * ro - Whether a network is declared as read-only
+ *
+ * Every network which is defined in a config file that is passed to
+ * wpa_supplicant using the -I option will be marked as read-only
+ * using this flag. It has the effect that it won't be written to
+ * /etc/wpa_supplicant.conf (from -c argument) if, e.g., wpa_gui tells
+ * the daemon to save all changed configs.
+ *
+ * This is necessary because networks from /etc/wpa_supplicant.conf
+ * have a higher priority and changes from an alternative file would be
+ * silently overwritten without this.
+ */
+ bool ro;
+
+ /**
* priority - Priority group
*
* By default, all networks will get same priority group (0). If some
@@ -837,6 +860,14 @@
struct os_reltime disabled_until;
/**
+ * disabled_due_to - BSSID of the disabling failure
+ *
+ * This identifies the BSS that failed the connection attempt that
+ * resulted in the network being temporarily disabled.
+ */
+ u8 disabled_due_to[ETH_ALEN];
+
+ /**
* parent_cred - Pointer to parent wpa_cred entry
*
* This pointer can be used to delete temporary networks when a wpa_cred
@@ -965,12 +996,21 @@
* 0 = use permanent MAC address
* 1 = use random MAC address for each ESS connection
* 2 = like 1, but maintain OUI (with local admin bit set)
+ * 3 = use dedicated/pregenerated MAC address (see mac_value)
*
* Internally, special value -1 is used to indicate that the parameter
* was not specified in the configuration (i.e., default behavior is
* followed).
*/
- int mac_addr;
+ enum wpas_mac_addr_style mac_addr;
+
+ /**
+ * mac_value - Specific MAC address to be used
+ *
+ * When mac_addr policy is equal to 3 this is the value of the MAC
+ * address that should be used.
+ */
+ u8 mac_value[ETH_ALEN];
/**
* no_auto_peer - Do not automatically peer with compatible mesh peers
@@ -1190,7 +1230,15 @@
* 1 = hash-to-element only
* 2 = both hunting-and-pecking loop and hash-to-element enabled
*/
- int sae_pwe;
+ enum sae_pwe sae_pwe;
+
+ /**
+ * disable_eht - Disable EHT (IEEE 802.11be) for this network
+ *
+ * By default, use it if it is available, but this can be configured
+ * to 1 to have it disabled.
+ */
+ int disable_eht;
};
#endif /* CONFIG_SSID_H */
diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c
index b27c6cf..05e6e37 100644
--- a/wpa_supplicant/config_winreg.c
+++ b/wpa_supplicant/config_winreg.c
@@ -446,7 +446,8 @@
}
-struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp)
+struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp,
+ bool ro)
{
TCHAR buf[256];
int errors = 0;
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 7586feb..27397e9 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -833,8 +833,6 @@
wpa_s->sae_commit_override = wpabuf_parse_bin(value);
} else if (os_strcasecmp(cmd, "driver_signal_override") == 0) {
ret = wpas_ctrl_iface_set_dso(wpa_s, value);
- } else if (os_strcasecmp(cmd, "force_hunting_and_pecking_pwe") == 0) {
- wpa_s->force_hunting_and_pecking_pwe = (atoi(value) != 0) ? 1 : 0;
} else if (os_strcasecmp(cmd, "disable_scs_support") == 0) {
wpa_s->disable_scs_support = !!atoi(value);
} else if (os_strcasecmp(cmd, "disable_mscs_support") == 0) {
@@ -1319,6 +1317,7 @@
u8 target_ap[ETH_ALEN];
struct wpa_bss *bss;
const u8 *mdie;
+ bool force = os_strstr(addr, " force") != NULL;
if (hwaddr_aton(addr, target_ap)) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS: invalid "
@@ -1334,7 +1333,7 @@
else
mdie = NULL;
- return wpa_ft_start_over_ds(wpa_s->wpa, target_ap, mdie);
+ return wpa_ft_start_over_ds(wpa_s->wpa, target_ap, mdie, force);
}
#endif /* CONFIG_IEEE80211R */
@@ -5002,6 +5001,250 @@
#endif /* CONFIG_FILS */
+static int print_rnr(struct wpa_bss *bss, char *pos, char *end)
+{
+ char *start = pos;
+ const u8 *ie, *ie_end;
+ unsigned int n = 0;
+ int ret;
+
+ ie = wpa_bss_get_ie(bss, WLAN_EID_REDUCED_NEIGHBOR_REPORT);
+ if (!ie)
+ return 0;
+
+ ie_end = ie + 2 + ie[1];
+ ie += 2;
+
+ while (ie < ie_end) {
+ const struct ieee80211_neighbor_ap_info *info =
+ (const struct ieee80211_neighbor_ap_info *) ie;
+ const u8 *tbtt_start;
+ size_t left = ie_end - ie;
+
+ if (left < sizeof(struct ieee80211_neighbor_ap_info))
+ return 0;
+
+ left -= sizeof(struct ieee80211_neighbor_ap_info);
+ if (left < info->tbtt_info_len)
+ return 0;
+
+ ret = os_snprintf(pos, end - pos,
+ "ap_info[%u]: tbtt_info: hdr=0x%x, len=%u, op_c=%u, channel=%u, ",
+ n, *ie, info->tbtt_info_len,
+ info->op_class, info->channel);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+
+ ie += sizeof(struct ieee80211_neighbor_ap_info);
+ tbtt_start = ie;
+ if (info->tbtt_info_len >= 1) {
+ ret = os_snprintf(pos, end - pos,
+ "tbtt_offset=%u, ", *ie);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+
+ ie++;
+ pos += ret;
+ }
+
+ if (info->tbtt_info_len >= 7) {
+ ret = os_snprintf(pos, end - pos,
+ "bssid=" MACSTR ", ",
+ MAC2STR(ie));
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+
+ ie += ETH_ALEN;
+ pos += ret;
+ }
+
+ if (info->tbtt_info_len >= 11) {
+ ret = os_snprintf(pos, end - pos,
+ "short SSID=0x%x, ",
+ WPA_GET_LE32(ie));
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+
+ ie += 4;
+ pos += ret;
+ }
+
+ if (info->tbtt_info_len >= 12) {
+ ret = os_snprintf(pos, end - pos,
+ "bss_params=0x%x, ", *ie);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+
+ ie++;
+ pos += ret;
+ }
+
+ if (info->tbtt_info_len >= 13) {
+ ret = os_snprintf(pos, end - pos,
+ "PSD=0x%x, ", *ie);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+
+ ie++;
+ pos += ret;
+ }
+
+ if (info->tbtt_info_len >= 16) {
+ ret = os_snprintf(pos, end - pos,
+ "mld ID=%u, link ID=%u",
+ *ie, *(ie + 1) & 0xF);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+
+ ie += 3;
+ pos += ret;
+ }
+
+ ie = tbtt_start + info->tbtt_info_len;
+
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+
+ n++;
+ }
+
+ return pos - start;
+}
+
+
+static int print_ml(struct wpa_bss *bss, char *pos, char *end)
+{
+ const struct ieee80211_eht_ml *ml;
+ char *start = pos;
+ const u8 *ie, *ie_end;
+ u16 ml_control;
+ u8 common_info_length;
+ int ret;
+
+ ie = get_ml_ie(wpa_bss_ie_ptr(bss), bss->ie_len,
+ MULTI_LINK_CONTROL_TYPE_BASIC);
+ if (!ie)
+ return 0;
+
+ ie_end = ie + 2 + ie[1];
+ ie += 3;
+ ml = (const struct ieee80211_eht_ml *) ie;
+
+ /* control + common info length + MLD MAC Address */
+ if (ie_end - ie < 2 + 1 + ETH_ALEN)
+ return 0;
+
+ ml_control = le_to_host16(ml->ml_control);
+
+ common_info_length = *(ie + 2);
+ ret = os_snprintf(pos, end - pos,
+ "multi-link: control=0x%x, common info len=%u",
+ ml_control, common_info_length);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+
+ ie += 2;
+ if (ie_end - ie < common_info_length)
+ return 0;
+
+ ie++;
+ common_info_length--;
+
+ if (common_info_length < ETH_ALEN)
+ return 0;
+
+ ret = os_snprintf(pos, end - pos, ", MLD addr=" MACSTR, MAC2STR(ie));
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+
+ ie += ETH_ALEN;
+ common_info_length -= ETH_ALEN;
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID) {
+ if (common_info_length < 1)
+ return 0;
+
+ ret = os_snprintf(pos, end - pos, ", link ID=%u", *ie & 0x0f);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ ie++;
+ common_info_length--;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT) {
+ if (common_info_length < 1)
+ return 0;
+
+ ret = os_snprintf(pos, end - pos,
+ ", BSS change parameters=0x%x", *ie);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ ie++;
+ common_info_length--;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO) {
+ if (common_info_length < 2)
+ return 0;
+
+ ret = os_snprintf(pos, end - pos, ", MSD Info=0x%x",
+ WPA_GET_LE16(ie));
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ ie += 2;
+ common_info_length -= 2;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
+ if (common_info_length < 2)
+ return 0;
+
+ ret = os_snprintf(pos, end - pos, ", EML capabilities=0x%x",
+ WPA_GET_LE16(ie));
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ ie += 2;
+ common_info_length -= 2;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA) {
+ if (common_info_length < 2)
+ return 0;
+
+ ret = os_snprintf(pos, end - pos, ", MLD capabilities=0x%x",
+ WPA_GET_LE16(ie));
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ ie += 2;
+ common_info_length -= 2;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID) {
+ if (common_info_length < 1)
+ return 0;
+
+ ret = os_snprintf(pos, end - pos, ", MLD ID=0x%x\n", *ie);
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ ie += 1;
+ common_info_length--;
+ }
+
+ return pos - start;
+}
+
+
static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
unsigned long mask, char *buf, size_t buflen)
{
@@ -5452,6 +5695,12 @@
pos += ret;
}
+ if (mask & WPA_BSS_MASK_RNR)
+ pos += print_rnr(bss, pos, end);
+
+ if (mask & WPA_BSS_MASK_ML)
+ pos += print_ml(bss, pos, end);
+
if (mask & WPA_BSS_MASK_DELIM) {
ret = os_snprintf(pos, end - pos, "====\n");
if (os_snprintf_error(end - pos, ret))
@@ -5657,23 +5906,23 @@
{
wpa_printf(MSG_DEBUG, "Dropping SA without deauthentication");
/* MLME-DELETEKEYS.request */
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 0, 0, NULL, 0, NULL,
+ wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 0, 0, NULL, 0, NULL,
0, KEY_FLAG_GROUP);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 1, 0, NULL, 0, NULL,
+ wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 1, 0, NULL, 0, NULL,
0, KEY_FLAG_GROUP);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 2, 0, NULL, 0, NULL,
+ wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 2, 0, NULL, 0, NULL,
0, KEY_FLAG_GROUP);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 3, 0, NULL, 0, NULL,
+ wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 3, 0, NULL, 0, NULL,
0, KEY_FLAG_GROUP);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 4, 0, NULL, 0, NULL,
+ wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 4, 0, NULL, 0, NULL,
0, KEY_FLAG_GROUP);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL,
+ wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL,
0, KEY_FLAG_GROUP);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 0, 0, NULL, 0, NULL,
- 0, KEY_FLAG_PAIRWISE);
+ wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, wpa_s->bssid, 0, 0, NULL, 0,
+ NULL, 0, KEY_FLAG_PAIRWISE);
if (wpa_sm_ext_key_id(wpa_s->wpa))
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 1, 0,
+ wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, wpa_s->bssid, 1, 0,
NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE);
/* MLME-SETPROTECTION.request(None) */
wpa_drv_mlme_setprotection(wpa_s, wpa_s->bssid,
@@ -8102,9 +8351,9 @@
pos = buf;
end = buf + buflen;
- ret = os_snprintf(pos, end - pos, "RSSI=%d\nLINKSPEED=%d\n"
+ ret = os_snprintf(pos, end - pos, "RSSI=%d\nLINKSPEED=%lu\n"
"NOISE=%d\nFREQUENCY=%u\n",
- si.current_signal, si.current_txrate / 1000,
+ si.data.signal, si.data.current_tx_rate / 1000,
si.current_noise, si.frequency);
if (os_snprintf_error(end - pos, ret))
return -1;
@@ -8134,17 +8383,18 @@
pos += ret;
}
- if (si.avg_signal) {
+ if (si.data.avg_signal) {
ret = os_snprintf(pos, end - pos,
- "AVG_RSSI=%d\n", si.avg_signal);
+ "AVG_RSSI=%d\n", si.data.avg_signal);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
- if (si.avg_beacon_signal) {
+ if (si.data.avg_beacon_signal) {
ret = os_snprintf(pos, end - pos,
- "AVG_BEACON_RSSI=%d\n", si.avg_beacon_signal);
+ "AVG_BEACON_RSSI=%d\n",
+ si.data.avg_beacon_signal);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
@@ -9998,14 +10248,15 @@
/* First, use a zero key to avoid any possible duplicate key avoidance
* in the driver. */
- if (wpa_drv_set_key(wpa_s, wpa_s->last_tk_alg, wpa_s->last_tk_addr,
+ if (wpa_drv_set_key(wpa_s, -1, wpa_s->last_tk_alg, wpa_s->last_tk_addr,
wpa_s->last_tk_key_idx, 1, zero, 6,
zero, wpa_s->last_tk_len,
KEY_FLAG_PAIRWISE_RX_TX) < 0)
return -1;
/* Set the previously configured key to reset its TSC/RSC */
- return wpa_drv_set_key(wpa_s, wpa_s->last_tk_alg, wpa_s->last_tk_addr,
+ return wpa_drv_set_key(wpa_s, -1, wpa_s->last_tk_alg,
+ wpa_s->last_tk_addr,
wpa_s->last_tk_key_idx, 1, zero, 6,
wpa_s->last_tk, wpa_s->last_tk_len,
KEY_FLAG_PAIRWISE_RX_TX);
@@ -10700,6 +10951,7 @@
entry->reauth_time = now.sec + reauth_time;
entry->network_ctx = ssid;
+ os_memcpy(entry->spa, wpa_s->own_addr, ETH_ALEN);
entry->external = true;
@@ -11564,10 +11816,10 @@
continue;
ret = os_snprintf(pos, end - pos,
- "LINK_ID=%d\nRSSI=%d\nLINKSPEED=%d\n"
+ "LINK_ID=%d\nRSSI=%d\nLINKSPEED=%lu\n"
"NOISE=%d\nFREQUENCY=%u\n",
- i, mlo_si.links[i].current_signal,
- mlo_si.links[i].current_txrate / 1000,
+ i, mlo_si.links[i].data.signal,
+ mlo_si.links[i].data.current_tx_rate / 1000,
mlo_si.links[i].current_noise,
mlo_si.links[i].frequency);
if (os_snprintf_error(end - pos, ret))
@@ -11599,19 +11851,19 @@
pos += ret;
}
- if (mlo_si.links[i].avg_signal) {
+ if (mlo_si.links[i].data.avg_signal) {
ret = os_snprintf(pos, end - pos,
"AVG_RSSI=%d\n",
- mlo_si.links[i].avg_signal);
+ mlo_si.links[i].data.avg_signal);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
- if (mlo_si.links[i].avg_beacon_signal) {
- ret = os_snprintf(pos, end - pos,
- "AVG_BEACON_RSSI=%d\n",
- mlo_si.links[i].avg_beacon_signal);
+ if (mlo_si.links[i].data.avg_beacon_signal) {
+ ret = os_snprintf(
+ pos, end - pos, "AVG_BEACON_RSSI=%d\n",
+ mlo_si.links[i].data.avg_beacon_signal);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
@@ -12818,8 +13070,11 @@
return 0;
fail:
- if (create_iface)
+ if (create_iface) {
+ /* wpa_supplicant does not create multi-BSS AP, so collapse to
+ * WPA_IF_STATION to avoid unwanted clean up in the driver. */
wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, iface.ifname);
+ }
return -1;
}
@@ -12841,6 +13096,8 @@
if (!ret && delete_iface) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE deleting the interface '%s'",
cmd);
+ /* wpa_supplicant does not create multi-BSS AP, so collapse to
+ * WPA_IF_STATION to avoid unwanted clean up in the driver. */
ret = wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, cmd);
}
return ret;
diff --git a/wpa_supplicant/ctrl_iface_udp.c b/wpa_supplicant/ctrl_iface_udp.c
index 1178f40..1cbf7fa 100644
--- a/wpa_supplicant/ctrl_iface_udp.c
+++ b/wpa_supplicant/ctrl_iface_udp.c
@@ -337,9 +337,6 @@
else
reply_len = 2;
} else {
- sockaddr_print(wpas_ctrl_cmd_debug_level(buf),
- "Control interface recv command from:",
- &from, fromlen);
reply = wpa_supplicant_ctrl_iface_process(wpa_s, pos,
&reply_len);
}
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.c b/wpa_supplicant/dbus/dbus_dict_helpers.c
index e4e9b8d..0e47534 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.c
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.c
@@ -299,6 +299,25 @@
/**
+ * Add a 64-bit unsigned integer entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ * wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The 64-bit unsigned integer value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_uint64(DBusMessageIter *iter_dict,
+ const char *key,
+ const dbus_uint64_t value)
+{
+ return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_UINT64,
+ &value);
+}
+
+
+/**
* Add a DBus object path entry to the dict.
*
* @param iter_dict A valid DBusMessageIter returned from
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.h b/wpa_supplicant/dbus/dbus_dict_helpers.h
index 94a0efd..44685ea 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.h
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.h
@@ -46,6 +46,10 @@
const char *key,
const dbus_uint32_t value);
+dbus_bool_t wpa_dbus_dict_append_uint64(DBusMessageIter *iter_dict,
+ const char *key,
+ const dbus_uint64_t value);
+
dbus_bool_t wpa_dbus_dict_append_object_path(DBusMessageIter *iter_dict,
const char *key,
const char *value);
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index 9279ae4..9c23588 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -2344,6 +2344,12 @@
case WPAS_DBUS_PROP_BSS_TM_STATUS:
prop = "BSSTMStatus";
break;
+ case WPAS_DBUS_PROP_MAC_ADDRESS:
+ prop = "MACAddress";
+ break;
+ case WPAS_DBUS_PROP_SIGNAL_CHANGE:
+ prop = "SignalChange";
+ break;
default:
wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d",
__func__, property);
@@ -3939,6 +3945,11 @@
wpas_dbus_setter_mac_address_randomization_mask,
NULL
},
+ { "MACAddress", WPAS_DBUS_NEW_IFACE_INTERFACE, "ay",
+ wpas_dbus_getter_mac_address,
+ NULL,
+ NULL,
+ },
{ NULL, NULL, NULL, NULL, NULL, NULL }
};
@@ -4521,6 +4532,11 @@
NULL,
NULL
},
+ { "SignalChange", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{sv}",
+ wpas_dbus_getter_signal_change,
+ NULL,
+ NULL
+ },
{ NULL, NULL, NULL, NULL, NULL, NULL }
};
diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h
index 26bdcb5..ca8506f 100644
--- a/wpa_supplicant/dbus/dbus_new.h
+++ b/wpa_supplicant/dbus/dbus_new.h
@@ -38,6 +38,8 @@
WPAS_DBUS_PROP_ROAM_COMPLETE,
WPAS_DBUS_PROP_SESSION_LENGTH,
WPAS_DBUS_PROP_BSS_TM_STATUS,
+ WPAS_DBUS_PROP_MAC_ADDRESS,
+ WPAS_DBUS_PROP_SIGNAL_CHANGE,
};
enum wpas_dbus_bss_prop {
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index 26f7738..67ce970 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -152,7 +152,7 @@
#ifdef CONFIG_INTERWORKING
"roaming_consortium", "required_roaming_consortium",
#endif /* CONFIG_INTERWORKING */
- NULL
+ "mac_value", NULL
};
static dbus_bool_t should_quote_opt(const char *key)
@@ -206,6 +206,8 @@
struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING };
DBusMessageIter iter_dict;
char *value = NULL;
+ bool mac_addr3_set = false;
+ bool mac_value_set = false;
if (!wpa_dbus_dict_open_read(iter, &iter_dict, error))
return FALSE;
@@ -315,12 +317,30 @@
else if (os_strcmp(entry.key, "priority") == 0)
wpa_config_update_prio_list(wpa_s->conf);
+ /*
+ * MAC address policy "3" needs to come with mac_value in
+ * the message so make sure that it is present (checked after
+ * the loop - here we just note what has been supplied).
+ */
+ if (os_strcmp(entry.key, "mac_addr") == 0 &&
+ atoi(value) == 3)
+ mac_addr3_set = true;
+ if (os_strcmp(entry.key, "mac_value") == 0)
+ mac_value_set = true;
+
skip_update:
os_free(value);
value = NULL;
wpa_dbus_dict_entry_clear(&entry);
}
+ if (mac_addr3_set && !mac_value_set) {
+ wpa_printf(MSG_INFO, "dbus: Invalid mac_addr policy config");
+ dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+ "Invalid mac_addr policy config");
+ return FALSE;
+ }
+
return TRUE;
error:
@@ -332,6 +352,118 @@
}
+static int set_cred_property(struct wpa_cred *cred,
+ struct wpa_dbus_dict_entry *entry)
+{
+ size_t size;
+ int ret;
+ char *value;
+
+ if (entry->type == DBUS_TYPE_ARRAY &&
+ entry->array_type == DBUS_TYPE_STRING) {
+ dbus_uint32_t i;
+
+ if (entry->array_len <= 0)
+ return -1;
+
+ for (i = 0; i < entry->array_len; i++) {
+ if (should_quote_opt(entry->key)) {
+ size = os_strlen(entry->strarray_value[i]);
+
+ size += 3;
+ value = os_zalloc(size);
+ if (!value)
+ return -1;
+
+ ret = os_snprintf(value, size, "\"%s\"",
+ entry->strarray_value[i]);
+ if (os_snprintf_error(size, ret)) {
+ os_free(value);
+ return -1;
+ }
+ } else {
+ value = os_strdup(entry->strarray_value[i]);
+ if (!value)
+ return -1;
+ }
+
+ ret = wpa_config_set_cred(cred, entry->key, value, 0);
+ os_free(value);
+ if (ret < 0)
+ return -1;
+ }
+ return 0;
+ }
+
+ if (entry->type == DBUS_TYPE_ARRAY &&
+ entry->array_type == DBUS_TYPE_BYTE) {
+ if (entry->array_len <= 0)
+ return -1;
+
+ size = entry->array_len * 2 + 1;
+ value = os_zalloc(size);
+ if (!value)
+ return -1;
+
+ ret = wpa_snprintf_hex(value, size,
+ (u8 *) entry->bytearray_value,
+ entry->array_len);
+ if (ret <= 0) {
+ os_free(value);
+ return -1;
+ }
+ } else if (entry->type == DBUS_TYPE_STRING) {
+ if (should_quote_opt(entry->key)) {
+ size = os_strlen(entry->str_value);
+
+ size += 3;
+ value = os_zalloc(size);
+ if (!value)
+ return -1;
+
+ ret = os_snprintf(value, size, "\"%s\"",
+ entry->str_value);
+ if (os_snprintf_error(size, ret)) {
+ os_free(value);
+ return -1;
+ }
+ } else {
+ value = os_strdup(entry->str_value);
+ if (!value)
+ return -1;
+ }
+ } else if (entry->type == DBUS_TYPE_UINT32) {
+ size = 50;
+ value = os_zalloc(size);
+ if (!value)
+ return -1;
+
+ ret = os_snprintf(value, size, "%u", entry->uint32_value);
+ if (os_snprintf_error(size, ret)) {
+ os_free(value);
+ return -1;
+ }
+ } else if (entry->type == DBUS_TYPE_INT32) {
+ size = 50;
+ value = os_zalloc(size);
+ if (!value)
+ return -1;
+
+ ret = os_snprintf(value, size, "%d", entry->int32_value);
+ if (os_snprintf_error(size, ret)) {
+ os_free(value);
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+
+ ret = wpa_config_set_cred(cred, entry->key, value, 0);
+ os_free(value);
+ return ret;
+}
+
+
/**
* set_cred_properties - Set the properties of a configured credential
* @wpa_s: wpa_supplicant structure for a network interface
@@ -348,91 +480,28 @@
{
struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING };
DBusMessageIter iter_dict;
- char *value = NULL;
if (!wpa_dbus_dict_open_read(iter, &iter_dict, error))
return FALSE;
while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
- size_t size = 50;
- int ret;
+ int res;
- if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
- goto error;
-
- value = NULL;
- if (entry.type == DBUS_TYPE_ARRAY &&
- entry.array_type == DBUS_TYPE_BYTE) {
- if (entry.array_len <= 0)
- goto error;
-
- size = entry.array_len * 2 + 1;
- value = os_zalloc(size);
- if (!value)
- goto error;
-
- ret = wpa_snprintf_hex(value, size,
- (u8 *) entry.bytearray_value,
- entry.array_len);
- if (ret <= 0)
- goto error;
- } else if (entry.type == DBUS_TYPE_STRING) {
- if (should_quote_opt(entry.key)) {
- size = os_strlen(entry.str_value);
-
- size += 3;
- value = os_zalloc(size);
- if (!value)
- goto error;
-
- ret = os_snprintf(value, size, "\"%s\"",
- entry.str_value);
- if (os_snprintf_error(size, ret))
- goto error;
- } else {
- value = os_strdup(entry.str_value);
- if (!value)
- goto error;
- }
- } else if (entry.type == DBUS_TYPE_UINT32) {
- value = os_zalloc(size);
- if (!value)
- goto error;
-
- ret = os_snprintf(value, size, "%u",
- entry.uint32_value);
- if (os_snprintf_error(size, ret))
- goto error;
- } else if (entry.type == DBUS_TYPE_INT32) {
- value = os_zalloc(size);
- if (!value)
- goto error;
-
- ret = os_snprintf(value, size, "%d",
- entry.int32_value);
- if (os_snprintf_error(size, ret))
- goto error;
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) {
+ res = -1;
} else {
- goto error;
+ res = set_cred_property(cred, &entry);
+ wpa_dbus_dict_entry_clear(&entry);
}
- ret = wpa_config_set_cred(cred, entry.key, value, 0);
- if (ret < 0)
- goto error;
-
- os_free(value);
- value = NULL;
- wpa_dbus_dict_entry_clear(&entry);
+ if (res < 0) {
+ dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+ "invalid message format");
+ return FALSE;
+ }
}
return TRUE;
-
-error:
- os_free(value);
- wpa_dbus_dict_entry_clear(&entry);
- dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
- "invalid message format");
- return FALSE;
}
@@ -704,6 +773,9 @@
char *ifname = NULL;
char *confname = NULL;
char *bridge_ifname = NULL;
+ bool create_iface = false;
+ u8 *if_addr = NULL;
+ enum wpa_driver_if_type if_type = WPA_IF_STATION;
dbus_message_iter_init(message, &iter);
@@ -740,6 +812,33 @@
wpa_dbus_dict_entry_clear(&entry);
if (bridge_ifname == NULL)
goto oom;
+ } else if (os_strcmp(entry.key, "Create") == 0 &&
+ entry.type == DBUS_TYPE_BOOLEAN) {
+ create_iface = entry.bool_value;
+ wpa_dbus_dict_entry_clear(&entry);
+ } else if (os_strcmp(entry.key, "Type") == 0 &&
+ entry.type == DBUS_TYPE_STRING) {
+ if (os_strcmp(entry.str_value, "sta") == 0) {
+ if_type = WPA_IF_STATION;
+ } else if (os_strcmp(entry.str_value, "ap") == 0) {
+ if_type = WPA_IF_AP_BSS;
+ } else {
+ wpa_dbus_dict_entry_clear(&entry);
+ goto error;
+ }
+ wpa_dbus_dict_entry_clear(&entry);
+ } else if (os_strcmp(entry.key, "Address") == 0 &&
+ entry.type == DBUS_TYPE_STRING) {
+ if_addr = os_malloc(ETH_ALEN);
+ if (if_addr == NULL) {
+ wpa_dbus_dict_entry_clear(&entry);
+ goto oom;
+ }
+ if (hwaddr_aton(entry.str_value, if_addr)) {
+ wpa_dbus_dict_entry_clear(&entry);
+ goto error;
+ }
+ wpa_dbus_dict_entry_clear(&entry);
} else {
wpa_dbus_dict_entry_clear(&entry);
goto error;
@@ -761,6 +860,23 @@
struct wpa_supplicant *wpa_s;
struct wpa_interface iface;
+ if (create_iface) {
+ u8 mac_addr[ETH_ALEN];
+
+ wpa_printf(MSG_DEBUG,
+ "%s[dbus]: creating an interface '%s'",
+ __func__, ifname);
+ if (!global->ifaces ||
+ wpa_drv_if_add(global->ifaces, if_type, ifname,
+ if_addr, NULL, NULL, mac_addr,
+ NULL) < 0) {
+ reply = wpas_dbus_error_unknown_error(
+ message,
+ "interface creation failed.");
+ goto out;
+ }
+ }
+
os_memset(&iface, 0, sizeof(iface));
iface.driver = driver;
iface.ifname = ifname;
@@ -771,6 +887,7 @@
if (wpa_s && wpa_s->dbus_new_path) {
const char *path = wpa_s->dbus_new_path;
+ wpa_s->added_vif = create_iface;
reply = dbus_message_new_method_return(message);
dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH,
&path, DBUS_TYPE_INVALID);
@@ -778,6 +895,13 @@
reply = wpas_dbus_error_unknown_error(
message,
"wpa_supplicant couldn't grab this interface.");
+ if (create_iface) {
+ /* wpa_supplicant does not create multi-BSS AP,
+ * so collapse to WPA_IF_STATION to avoid
+ * unwanted clean up in the driver. */
+ wpa_drv_if_remove(global->ifaces,
+ WPA_IF_STATION, ifname);
+ }
}
}
@@ -786,6 +910,7 @@
os_free(ifname);
os_free(confname);
os_free(bridge_ifname);
+ os_free(if_addr);
return reply;
error:
@@ -814,19 +939,38 @@
struct wpa_supplicant *wpa_s;
char *path;
DBusMessage *reply = NULL;
+ bool delete_iface;
dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
DBUS_TYPE_INVALID);
wpa_s = get_iface_by_dbus_path(global, path);
- if (wpa_s == NULL)
+ if (!wpa_s) {
reply = wpas_dbus_error_iface_unknown(message);
- else if (wpa_supplicant_remove_iface(global, wpa_s, 0)) {
+ goto out;
+ }
+ delete_iface = wpa_s->added_vif;
+ if (wpa_supplicant_remove_iface(global, wpa_s, 0)) {
reply = wpas_dbus_error_unknown_error(
message,
"wpa_supplicant couldn't remove this interface.");
+ goto out;
}
+ if (delete_iface) {
+ wpa_printf(MSG_DEBUG, "%s[dbus]: deleting the interface '%s'",
+ __func__, wpa_s->ifname);
+ /* wpa_supplicant does not create multi-BSS AP, so collapse to
+ * WPA_IF_STATION to avoid unwanted clean up in the driver. */
+ if (wpa_drv_if_remove(global->ifaces, WPA_IF_STATION,
+ wpa_s->ifname)) {
+ reply = wpas_dbus_error_unknown_error(
+ message,
+ "wpa_supplicant couldn't delete this interface.");
+ }
+ }
+
+out:
return reply;
}
@@ -1826,7 +1970,7 @@
{
struct wpa_signal_info si;
DBusMessage *reply = NULL;
- DBusMessageIter iter, iter_dict, variant_iter;
+ DBusMessageIter iter;
int ret;
ret = wpa_drv_signal_poll(wpa_s, &si);
@@ -1841,31 +1985,7 @@
dbus_message_iter_init_append(reply, &iter);
- if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
- "a{sv}", &variant_iter) ||
- !wpa_dbus_dict_open_write(&variant_iter, &iter_dict) ||
- !wpa_dbus_dict_append_int32(&iter_dict, "rssi",
- si.current_signal) ||
- !wpa_dbus_dict_append_int32(&iter_dict, "linkspeed",
- si.current_txrate / 1000) ||
- !wpa_dbus_dict_append_int32(&iter_dict, "noise",
- si.current_noise) ||
- !wpa_dbus_dict_append_uint32(&iter_dict, "frequency",
- si.frequency) ||
- (si.chanwidth != CHAN_WIDTH_UNKNOWN &&
- !wpa_dbus_dict_append_string(
- &iter_dict, "width",
- channel_width_to_string(si.chanwidth))) ||
- (si.center_frq1 > 0 && si.center_frq2 > 0 &&
- (!wpa_dbus_dict_append_int32(&iter_dict, "center-frq1",
- si.center_frq1) ||
- !wpa_dbus_dict_append_int32(&iter_dict, "center-frq2",
- si.center_frq2))) ||
- (si.avg_signal &&
- !wpa_dbus_dict_append_int32(&iter_dict, "avg-rssi",
- si.avg_signal)) ||
- !wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
- !dbus_message_iter_close_container(&iter, &variant_iter))
+ if (wpas_dbus_new_from_signal_information(&iter, &si) != 0)
goto nomem;
return reply;
@@ -4324,6 +4444,7 @@
const char *new_value = NULL;
char buf[250];
size_t combined_len;
+ int wpa_sm_param;
int ret;
if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
@@ -4342,6 +4463,35 @@
if (!new_value[0])
new_value = "NULL";
+ wpa_sm_param = -1;
+ if (os_strcmp(property_desc->data, "dot11RSNAConfigPMKLifetime") == 0)
+ wpa_sm_param = RSNA_PMK_LIFETIME;
+ else if (os_strcmp(property_desc->data,
+ "dot11RSNAConfigPMKReauthThreshold") == 0)
+ wpa_sm_param = RSNA_PMK_REAUTH_THRESHOLD;
+ else if (os_strcmp(property_desc->data, "dot11RSNAConfigSATimeout") == 0)
+ wpa_sm_param = RSNA_SA_TIMEOUT;
+
+ if (wpa_sm_param != -1) {
+ char *end;
+ int val;
+
+ val = strtol(new_value, &end, 0);
+ if (*end) {
+ dbus_set_error(error, DBUS_ERROR_INVALID_ARGS,
+ "Invalid value for property %s",
+ property_desc->dbus_property);
+ return FALSE;
+ }
+
+ if (wpa_sm_set_param(wpa_s->wpa, wpa_sm_param, val)) {
+ dbus_set_error(error, DBUS_ERROR_INVALID_ARGS,
+ "Failed to apply interface property %s",
+ property_desc->dbus_property);
+ return FALSE;
+ }
+ }
+
ret = os_snprintf(buf, combined_len, "%s=%s", property_desc->data,
new_value);
if (os_snprintf_error(combined_len, ret)) {
@@ -4600,6 +4750,27 @@
/**
+ * wpas_dbus_getter_mac_address - Get MAC address of an interface
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: a list of stations
+ *
+ * Getter for "MACAddress" property.
+ */
+dbus_bool_t wpas_dbus_getter_mac_address(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+
+ return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+ wpa_s->own_addr, ETH_ALEN,
+ error);
+}
+
+
+/**
* wpas_dbus_getter_sta_address - Return the address of a connected station
* @iter: Pointer to incoming dbus message iter
* @error: Location to store error on failure
@@ -5945,3 +6116,28 @@
}
#endif /* CONFIG_MESH */
+
+
+/**
+ * wpas_dbus_getter_signal_change - Get signal change
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "SignalChange" property.
+ */
+dbus_bool_t wpas_dbus_getter_signal_change(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ struct wpa_signal_info si = wpa_s->last_signal_info;
+
+ if (wpas_dbus_new_from_signal_information(iter, &si) != 0) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: error constructing reply", __func__);
+ return FALSE;
+ }
+ return TRUE;
+}
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index a421083..97fa337 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -196,6 +196,7 @@
DECLARE_ACCESSOR(wpas_dbus_getter_stas);
DECLARE_ACCESSOR(wpas_dbus_getter_mac_address_randomization_mask);
DECLARE_ACCESSOR(wpas_dbus_setter_mac_address_randomization_mask);
+DECLARE_ACCESSOR(wpas_dbus_getter_mac_address);
DECLARE_ACCESSOR(wpas_dbus_getter_sta_address);
DECLARE_ACCESSOR(wpas_dbus_getter_sta_aid);
DECLARE_ACCESSOR(wpas_dbus_getter_sta_caps);
@@ -246,6 +247,8 @@
DECLARE_ACCESSOR(wpas_dbus_getter_mesh_peers);
DECLARE_ACCESSOR(wpas_dbus_getter_mesh_group);
+DECLARE_ACCESSOR(wpas_dbus_getter_signal_change);
+
DBusMessage * wpas_dbus_handler_tdls_discover(DBusMessage *message,
struct wpa_supplicant *wpa_s);
DBusMessage * wpas_dbus_handler_tdls_setup(DBusMessage *message,
diff --git a/wpa_supplicant/dbus/dbus_new_helpers.c b/wpa_supplicant/dbus/dbus_new_helpers.c
index d9009ba..e21f912 100644
--- a/wpa_supplicant/dbus/dbus_new_helpers.c
+++ b/wpa_supplicant/dbus/dbus_new_helpers.c
@@ -11,6 +11,7 @@
#include "utils/common.h"
#include "utils/eloop.h"
+#include "drivers/driver.h"
#include "dbus_common.h"
#include "dbus_common_i.h"
#include "dbus_new.h"
@@ -1023,3 +1024,131 @@
}
return NULL;
}
+
+
+/**
+ * wpas_dbus_new_from_signal_information - Adds a wpa_signal_info
+ * to a DBusMessage.
+ * @msg: Pointer to message to append fields to
+ * @si: Pointer to wpa_signal_info to add to the message
+ * Returns: 0 on success, otherwise, an errorcode
+ *
+ * Adds all the pertinent fields from a wpa_signal_info to a DBusMessage.
+ * The same logic is useful in both responding to signal_poll calls, and
+ * sending signal_change signals.
+ */
+int wpas_dbus_new_from_signal_information(DBusMessageIter *iter,
+ struct wpa_signal_info *si)
+{
+ DBusMessageIter iter_dict, variant_iter;
+
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ "a{sv}", &variant_iter) ||
+ !wpa_dbus_dict_open_write(&variant_iter, &iter_dict) ||
+ !wpa_dbus_dict_append_int32(&iter_dict, "rssi",
+ si->data.signal) ||
+ !wpa_dbus_dict_append_uint32(&iter_dict, "linkspeed",
+ si->data.current_tx_rate / 1000) ||
+ !wpa_dbus_dict_append_int32(&iter_dict, "noise",
+ si->current_noise) ||
+ !wpa_dbus_dict_append_uint32(&iter_dict, "frequency",
+ si->frequency) ||
+ (si->chanwidth != CHAN_WIDTH_UNKNOWN &&
+ !wpa_dbus_dict_append_string(
+ &iter_dict, "width",
+ channel_width_to_string(si->chanwidth))) ||
+ (si->center_frq1 > 0 && si->center_frq2 > 0 &&
+ (!wpa_dbus_dict_append_int32(&iter_dict, "center-frq1",
+ si->center_frq1) ||
+ !wpa_dbus_dict_append_int32(&iter_dict, "center-frq2",
+ si->center_frq2))) ||
+ (si->data.avg_signal &&
+ !wpa_dbus_dict_append_int32(&iter_dict, "avg-rssi",
+ si->data.avg_signal)) ||
+ (si->data.rx_bytes &&
+ !wpa_dbus_dict_append_uint64(&iter_dict, "rx-bytes",
+ si->data.rx_bytes)) ||
+ (si->data.tx_bytes &&
+ !wpa_dbus_dict_append_uint64(&iter_dict, "tx-bytes",
+ si->data.tx_bytes)) ||
+ (si->data.rx_packets &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "rx-packets",
+ si->data.rx_packets)) ||
+ (si->data.tx_packets &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "tx-packets",
+ si->data.tx_packets)) ||
+ (si->data.beacons_count &&
+ !wpa_dbus_dict_append_uint64(&iter_dict, "beacons",
+ si->data.beacons_count)) ||
+ (si->data.current_rx_rate &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "linkrxspeed",
+ si->data.current_rx_rate)) ||
+ (si->data.current_rx_rate &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "linktxspeed",
+ si->data.current_tx_rate)) ||
+ (si->data.tx_retry_failed &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "retries-failed",
+ si->data.tx_retry_failed)) ||
+ (si->data.tx_retry_count &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "retries",
+ si->data.tx_retry_count)) ||
+ (si->data.last_ack_rssi &&
+ !wpa_dbus_dict_append_int32(&iter_dict, "last-ack-rssi",
+ si->data.last_ack_rssi)) ||
+ (si->data.fcs_error_count &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "fcs-errors",
+ si->data.fcs_error_count)) ||
+ (si->data.beacon_loss_count &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "beacon-losses",
+ si->data.beacon_loss_count)) ||
+ (si->data.expected_throughput &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "expected-throughput",
+ si->data.expected_throughput)) ||
+ (si->data.rx_drop_misc &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "rx-drop-misc",
+ si->data.rx_drop_misc)) ||
+ (si->data.rx_mpdus &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "rx-mpdus",
+ si->data.rx_mpdus)) ||
+ (si->data.rx_hemcs &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "rx-he-mcs",
+ si->data.rx_hemcs)) ||
+ (si->data.tx_hemcs &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "tx-he-mcs",
+ si->data.tx_hemcs)) ||
+ (si->data.rx_vhtmcs &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "rx-vht-mcs",
+ si->data.rx_vhtmcs)) ||
+ (si->data.tx_vhtmcs &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "tx-vht-mcs",
+ si->data.tx_vhtmcs)) ||
+ (si->data.rx_mcs &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "rx-mcs",
+ si->data.rx_mcs)) ||
+ (si->data.tx_mcs &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "tx-mcs",
+ si->data.tx_mcs)) ||
+ (si->data.rx_he_nss &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "rx-he-nss",
+ si->data.rx_he_nss)) ||
+ (si->data.tx_he_nss &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "tx-he-nss",
+ si->data.tx_he_nss)) ||
+ (si->data.rx_vht_nss &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "rx-vht-nss",
+ si->data.rx_vht_nss)) ||
+ (si->data.tx_vht_nss &&
+ !wpa_dbus_dict_append_uint32(&iter_dict, "tx-vht-nss",
+ si->data.tx_vht_nss)) ||
+ (si->data.avg_beacon_signal &&
+ !wpa_dbus_dict_append_int32(&iter_dict, "avg-beacon-rssi",
+ si->data.avg_beacon_signal)) ||
+ (si->data.avg_ack_signal &&
+ !wpa_dbus_dict_append_int32(&iter_dict, "avg-ack-rssi",
+ si->data.avg_ack_signal)) ||
+ !wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
+ !dbus_message_iter_close_container(iter, &variant_iter))
+ return -ENOMEM;
+
+ return 0;
+}
diff --git a/wpa_supplicant/dbus/dbus_new_helpers.h b/wpa_supplicant/dbus/dbus_new_helpers.h
index 7b63b28..c8d44a0 100644
--- a/wpa_supplicant/dbus/dbus_new_helpers.h
+++ b/wpa_supplicant/dbus/dbus_new_helpers.h
@@ -12,6 +12,8 @@
#include <dbus/dbus.h>
+struct wpa_signal_info;
+
typedef DBusMessage * (*WPADBusMethodHandler)(DBusMessage *message,
void *user_data);
typedef void (*WPADBusArgumentFreeFunction)(void *handler_arg);
@@ -151,4 +153,7 @@
const char *fallback_name,
const char *fallback_string);
+int wpas_dbus_new_from_signal_information(DBusMessageIter *iter,
+ struct wpa_signal_info *si);
+
#endif /* WPA_DBUS_CTRL_H */
diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c
index d13a8d0..895d5fa 100644
--- a/wpa_supplicant/dpp_supplicant.c
+++ b/wpa_supplicant/dpp_supplicant.c
@@ -1353,7 +1353,10 @@
if (dpp_akm_sae(conf->akm))
ssid->key_mgmt |= WPA_KEY_MGMT_SAE |
WPA_KEY_MGMT_FT_SAE;
- ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL;
+ if (dpp_akm_psk(conf->akm))
+ ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL;
+ else
+ ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED;
if (conf->passphrase[0]) {
if (wpa_config_set_quoted(ssid, "psk",
conf->passphrase) < 0)
@@ -1923,7 +1926,7 @@
static void wpas_dpp_auth_success(struct wpa_supplicant *wpa_s, int initiator)
{
wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
- wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=%d", initiator);
+ dpp_notify_auth_success(wpa_s->dpp_auth, initiator);
wpas_notify_dpp_auth_success(wpa_s);
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
@@ -2301,6 +2304,7 @@
u16 r_bootstrap_len;
struct dpp_bootstrap_info *peer_bi;
struct dpp_authentication *auth;
+ unsigned int wait_time, max_wait_time;
if (!wpa_s->dpp)
return;
@@ -2332,6 +2336,9 @@
return;
}
+ wpa_printf(MSG_DEBUG, "DPP: Start Authentication exchange with " MACSTR
+ " based on the received Presence Announcement",
+ MAC2STR(src));
auth = dpp_auth_init(wpa_s->dpp, wpa_s, peer_bi, NULL,
DPP_CAPAB_CONFIGURATOR, freq, NULL, 0);
if (!auth)
@@ -2348,6 +2355,13 @@
* MAC address information from the bootstrapping information. */
os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
+ wait_time = wpa_s->max_remain_on_chan;
+ max_wait_time = wpa_s->dpp_resp_wait_time ?
+ wpa_s->dpp_resp_wait_time : 2000;
+ if (wait_time > max_wait_time)
+ wait_time = max_wait_time;
+ wpas_dpp_stop_listen_for_tx(wpa_s, freq, wait_time);
+
wpa_s->dpp_auth = auth;
if (wpas_dpp_auth_init_next(wpa_s) < 0) {
dpp_auth_deinit(wpa_s->dpp_auth);
@@ -2642,6 +2656,8 @@
return;
}
+ os_memset(&intro, 0, sizeof(intro));
+
trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID,
&trans_id_len);
if (!trans_id || trans_id_len != 1) {
@@ -2693,7 +2709,7 @@
ssid->dpp_netaccesskey_len,
ssid->dpp_csign,
ssid->dpp_csign_len,
- connector, connector_len, &expiry);
+ connector, connector_len, &expiry, NULL);
if (res != DPP_STATUS_OK) {
wpa_printf(MSG_INFO,
"DPP: Network Introduction protocol resulted in failure");
@@ -2709,6 +2725,7 @@
if (!entry)
goto fail;
os_memcpy(entry->aa, src, ETH_ALEN);
+ os_memcpy(entry->spa, wpa_s->own_addr, ETH_ALEN);
os_memcpy(entry->pmkid, intro.pmkid, PMKID_LEN);
os_memcpy(entry->pmk, intro.pmk, intro.pmk_len);
entry->pmk_len = intro.pmk_len;
@@ -2752,7 +2769,7 @@
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
fail:
- os_memset(&intro, 0, sizeof(intro));
+ dpp_peer_intro_deinit(&intro);
}
@@ -3845,7 +3862,7 @@
ssid->dpp_netaccesskey_len,
ssid->dpp_csign,
ssid->dpp_csign_len,
- connector, connector_len, &expiry);
+ connector, connector_len, &expiry, NULL);
if (res != DPP_STATUS_OK) {
wpa_printf(MSG_INFO,
"DPP: Network Introduction protocol resulted in failure");
@@ -3871,6 +3888,7 @@
goto fail;
entry->dpp_pfs = peer_version >= 2;
os_memcpy(entry->aa, src, ETH_ALEN);
+ os_memcpy(entry->spa, wpa_s->own_addr, ETH_ALEN);
os_memcpy(entry->pmkid, intro.pmkid, PMKID_LEN);
os_memcpy(entry->pmk, intro.pmk, intro.pmk_len);
entry->pmk_len = intro.pmk_len;
@@ -4087,7 +4105,7 @@
* TX status handler, but since there was no such handler call
* yet, simply send out the event message and proceed with
* exchange. */
- wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=1");
+ dpp_notify_auth_success(auth, 1);
wpa_s->dpp_auth_ok_on_ack = 0;
}
@@ -4380,7 +4398,7 @@
if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 &&
!(ied.key_mgmt & WPA_KEY_MGMT_DPP))
return 0; /* AP does not support DPP AKM - continue */
- if (wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, ssid))
+ if (wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, wpa_s->own_addr, ssid))
return 0; /* PMKSA exists for DPP AKM - continue */
if (!ssid->dpp_connector || !ssid->dpp_netaccesskey ||
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 6be117c..5dd2a51 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -143,7 +143,7 @@
return -1;
}
-static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s,
+static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s, int link_id,
enum wpa_alg alg, const u8 *addr,
int key_idx, int set_tx,
const u8 *seq, size_t seq_len,
@@ -163,6 +163,7 @@
params.key = key;
params.key_len = key_len;
params.key_flag = key_flag;
+ params.link_id = link_id;
if (alg != WPA_ALG_NONE) {
/* keyidx = 1 can be either a broadcast or--with
@@ -406,20 +407,10 @@
return 0;
}
-static inline int wpa_drv_send_action(struct wpa_supplicant *wpa_s,
- unsigned int freq,
- unsigned int wait,
- const u8 *dst, const u8 *src,
- const u8 *bssid,
- const u8 *data, size_t data_len,
- int no_cck)
-{
- if (wpa_s->driver->send_action)
- return wpa_s->driver->send_action(wpa_s->drv_priv, freq,
- wait, dst, src, bssid,
- data, data_len, no_cck);
- return -1;
-}
+int wpa_drv_send_action(struct wpa_supplicant *wpa_s, unsigned int freq,
+ unsigned int wait, const u8 *dst, const u8 *src,
+ const u8 *bssid, const u8 *data, size_t data_len,
+ int no_cck);
static inline void wpa_drv_send_action_cancel_wait(struct wpa_supplicant *wpa_s)
{
diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
index efec31c..9641062 100644
--- a/wpa_supplicant/eapol_test.c
+++ b/wpa_supplicant/eapol_test.c
@@ -1472,7 +1472,7 @@
dl_list_init(&wpa_s.bss);
dl_list_init(&wpa_s.bss_id);
if (conf)
- wpa_s.conf = wpa_config_read(conf, NULL);
+ wpa_s.conf = wpa_config_read(conf, NULL, false);
else
wpa_s.conf = wpa_config_alloc_empty(ctrl_iface, NULL);
if (wpa_s.conf == NULL) {
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 6c82b66..3275b64 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -140,7 +140,7 @@
struct wpa_bss *bss = NULL;
struct wpa_ssid *ssid = wpa_s->current_ssid;
- if (ssid->ssid_len > 0)
+ if (ssid && ssid->ssid_len > 0)
bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
if (!bss)
bss = wpa_bss_get_bssid(wpa_s, bssid);
@@ -183,7 +183,8 @@
}
-static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s)
+static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
{
struct wpa_ssid *ssid, *old_ssid;
u8 drv_ssid[SSID_MAX_LEN];
@@ -256,8 +257,15 @@
if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
u8 wpa_ie[80];
size_t wpa_ie_len = sizeof(wpa_ie);
+ bool skip_default_rsne;
+
+ /* Do not override RSNE/RSNXE with the default values if the
+ * driver indicated the actual values used in the
+ * (Re)Association Request frame. */
+ skip_default_rsne = data && data->assoc_info.req_ies;
if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
- wpa_ie, &wpa_ie_len) < 0)
+ wpa_ie, &wpa_ie_len,
+ skip_default_rsne) < 0)
wpa_dbg(wpa_s, MSG_DEBUG, "Could not set WPA suites");
} else {
wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
@@ -301,16 +309,15 @@
}
-static void wpas_reset_mlo_info(struct wpa_supplicant *wpa_s)
+void wpas_reset_mlo_info(struct wpa_supplicant *wpa_s)
{
- int i;
-
if (!wpa_s->valid_links)
return;
wpa_s->valid_links = 0;
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++)
- wpa_s->links[i].bss = NULL;
+ wpa_s->mlo_assoc_link_id = 0;
+ os_memset(wpa_s->ap_mld_addr, 0, ETH_ALEN);
+ os_memset(wpa_s->links, 0, sizeof(wpa_s->links));
}
@@ -382,6 +389,7 @@
if (wpa_s->enabled_4addr_mode && wpa_drv_set_4addr_mode(wpa_s, 0) == 0)
wpa_s->enabled_4addr_mode = 0;
+ wpa_s->wps_scan_done = false;
wpas_reset_mlo_info(wpa_s);
}
@@ -601,7 +609,8 @@
#ifdef CONFIG_WEP
int wep_ok;
#endif /* CONFIG_WEP */
- bool is_6ghz_bss = is_6ghz_freq(bss->freq);
+ bool is_6ghz_bss_or_mld = is_6ghz_freq(bss->freq) ||
+ !is_zero_ether_addr(bss->mld_addr);
ret = wpas_wps_ssid_bss_match(wpa_s, ssid, bss);
if (ret >= 0)
@@ -616,10 +625,10 @@
#endif /* CONFIG_WEP */
rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
- if (is_6ghz_bss && !rsn_ie) {
+ if (is_6ghz_bss_or_mld && !rsn_ie) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - 6 GHz BSS without RSNE");
+ " skip - 6 GHz/MLD BSS without RSNE");
return 0;
}
@@ -637,7 +646,7 @@
if (!ie.has_group)
ie.group_cipher = wpa_default_rsn_cipher(bss->freq);
- if (is_6ghz_bss) {
+ if (is_6ghz_bss_or_mld) {
/* WEP and TKIP are not allowed on 6 GHz */
ie.pairwise_cipher &= ~(WPA_CIPHER_WEP40 |
WPA_CIPHER_WEP104 |
@@ -688,12 +697,12 @@
break;
}
- if (is_6ghz_bss) {
+ if (is_6ghz_bss_or_mld) {
/* MFPC must be supported on 6 GHz */
if (!(ie.capabilities & WPA_CAPABILITY_MFPC)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
- " skip RSNE - 6 GHz without MFPC");
+ " skip RSNE - 6 GHz/MLD without MFPC");
break;
}
@@ -733,10 +742,10 @@
return 1;
}
- if (is_6ghz_bss) {
+ if (is_6ghz_bss_or_mld) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - 6 GHz BSS without matching RSNE");
+ " skip - 6 GHz/MLD BSS without matching RSNE");
return 0;
}
@@ -956,10 +965,22 @@
continue;
}
+ if (flagged && ((rate_ie[j] & 0x7f) ==
+ BSS_MEMBERSHIP_SELECTOR_HE_PHY)) {
+ if (!he_supported(mode, IEEE80211_MODE_INFRA)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " hardware does not support HE PHY");
+ return 0;
+ }
+ continue;
+ }
+
#ifdef CONFIG_SAE
if (flagged && ((rate_ie[j] & 0x7f) ==
BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY)) {
- if (wpa_s->conf->sae_pwe == 0 &&
+ if (wpa_s->conf->sae_pwe ==
+ SAE_PWE_HUNT_AND_PECK &&
!ssid->sae_password_id &&
wpa_key_mgmt_sae(ssid->key_mgmt)) {
if (debug_print)
@@ -1393,9 +1414,10 @@
#ifdef CONFIG_SAE
/* When using SAE Password Identifier and when operationg on the 6 GHz
* band, only H2E is allowed. */
- if ((wpa_s->conf->sae_pwe == 1 || is_6ghz_freq(bss->freq) ||
- ssid->sae_password_id) &&
- wpa_s->conf->sae_pwe != 3 && wpa_key_mgmt_sae(ssid->key_mgmt) &&
+ if ((wpa_s->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ is_6ghz_freq(bss->freq) || ssid->sae_password_id) &&
+ wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
+ wpa_key_mgmt_sae(ssid->key_mgmt) &&
#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
!(wpa_key_mgmt_wpa_psk_no_sae(ssid->key_mgmt)) &&
#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
@@ -1511,7 +1533,8 @@
#ifdef CONFIG_DPP
if ((ssid->key_mgmt & WPA_KEY_MGMT_DPP) &&
- !wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, ssid) &&
+ !wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, wpa_s->own_addr,
+ ssid) &&
(!ssid->dpp_connector || !ssid->dpp_netaccesskey ||
!ssid->dpp_csign)) {
if (debug_print)
@@ -1802,10 +1825,12 @@
struct wpa_bss *selected,
struct wpa_ssid *ssid)
{
- if (wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) {
+ if (wpas_wps_partner_link_overlap_detect(wpa_s) ||
+ wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) {
wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP
"PBC session overlap");
wpas_notify_wps_event_pbc_overlap(wpa_s);
+ wpa_s->wps_overlap = true;
#ifdef CONFIG_P2P
if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT ||
wpa_s->p2p_in_provisioning) {
@@ -1994,9 +2019,9 @@
* information about our currently associated AP.
*/
if (wpa_drv_signal_poll(wpa_s, &si) == 0 &&
- (si.avg_beacon_signal || si.avg_signal)) {
- cur_level = si.avg_beacon_signal ? si.avg_beacon_signal :
- si.avg_signal;
+ (si.data.avg_beacon_signal || si.data.avg_signal)) {
+ cur_level = si.data.avg_beacon_signal ?
+ si.data.avg_beacon_signal : si.data.avg_signal;
cur_snr = wpas_get_snr_signal_info(si.frequency, cur_level,
si.current_noise);
@@ -2309,6 +2334,9 @@
}
}
+ if (wpa_s->supp_pbc_active && !wpas_wps_partner_link_scan_done(wpa_s))
+ return ret;
+
return wpas_select_network_from_last_scan(wpa_s, 1, own_request);
scan_work_done:
@@ -2375,6 +2403,8 @@
wpa_dbg(wpa_s, MSG_DEBUG, "Connect failed");
return -1;
}
+ wpa_s->supp_pbc_active = false;
+
if (new_scan)
wpa_supplicant_rsn_preauth_scan_results(wpa_s);
/*
@@ -2409,9 +2439,9 @@
if (res == 1)
return 0;
- if (wpas_p2p_retry_limit_exceeded(wpa_s)) {
+ if (wpas_p2p_retry_limit_exceeded(wpa_s))
return 0;
- }
+
if (wpa_s->p2p_in_provisioning ||
wpa_s->show_group_started ||
wpa_s->p2p_in_invitation) {
@@ -2545,6 +2575,17 @@
#endif /* CONFIG_NO_SCAN_PROCESSING */
}
+
+int wpa_wps_supplicant_fast_associate(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_NO_SCAN_PROCESSING
+ return -1;
+#else /* CONFIG_NO_SCAN_PROCESSING */
+ return wpas_select_network_from_last_scan(wpa_s, 1, 1);
+#endif /* CONFIG_NO_SCAN_PROCESSING */
+}
+
+
#ifdef CONFIG_WNM
static void wnm_bss_keep_alive(void *eloop_ctx, void *sock_ctx)
@@ -2844,15 +2885,15 @@
if (!ssid->psk_set) {
wpa_dbg(wpa_s, MSG_INFO,
"No PSK available for association");
- wpas_auth_failed(wpa_s, "NO_PSK_AVAILABLE");
+ wpas_auth_failed(wpa_s, "NO_PSK_AVAILABLE", NULL);
return -1;
}
wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL, NULL);
if (wpa_s->conf->key_mgmt_offload &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD) &&
- wpa_drv_set_key(wpa_s, 0, NULL, 0, 0, NULL, 0, ssid->psk,
- PMK_LEN, KEY_FLAG_PMK))
+ wpa_drv_set_key(wpa_s, -1, 0, NULL, 0, 0, NULL, 0,
+ ssid->psk, PMK_LEN, KEY_FLAG_PMK))
wpa_dbg(wpa_s, MSG_ERROR,
"WPA: Cannot set PMK for key management offload");
}
@@ -3453,7 +3494,7 @@
struct driver_sta_mlo_info mlo;
int i;
- mlo.valid_links = 0;
+ os_memset(&mlo, 0, sizeof(mlo));
if (wpas_drv_get_sta_mlo_info(wpa_s, &mlo)) {
wpa_dbg(wpa_s, MSG_ERROR, "Failed to get MLO link info");
wpa_supplicant_deauthenticate(wpa_s,
@@ -3480,13 +3521,14 @@
}
}
- if (match &&
+ if (match && wpa_s->mlo_assoc_link_id == mlo.assoc_link_id &&
os_memcmp(wpa_s->ap_mld_addr, mlo.ap_mld_addr,
ETH_ALEN) == 0)
return 0;
}
wpa_s->valid_links = mlo.valid_links;
+ wpa_s->mlo_assoc_link_id = mlo.assoc_link_id;
os_memcpy(wpa_s->ap_mld_addr, mlo.ap_mld_addr, ETH_ALEN);
for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
if (!(wpa_s->valid_links & BIT(i)))
@@ -3502,6 +3544,66 @@
}
+static int wpa_sm_set_ml_info(struct wpa_supplicant *wpa_s)
+{
+ struct driver_sta_mlo_info drv_mlo;
+ struct wpa_sm_mlo wpa_mlo;
+ const u8 *bss_rsn = NULL, *bss_rsnx = NULL;
+ int i;
+
+ os_memset(&drv_mlo, 0, sizeof(drv_mlo));
+ if (wpas_drv_get_sta_mlo_info(wpa_s, &drv_mlo)) {
+ wpa_dbg(wpa_s, MSG_INFO, "Failed to get MLO link info");
+ return -1;
+ }
+
+ os_memset(&wpa_mlo, 0, sizeof(wpa_mlo));
+ if (!drv_mlo.valid_links)
+ goto out;
+
+ os_memcpy(wpa_mlo.ap_mld_addr, drv_mlo.ap_mld_addr, ETH_ALEN);
+ wpa_mlo.assoc_link_id = drv_mlo.assoc_link_id;
+ wpa_mlo.valid_links = drv_mlo.valid_links;
+ wpa_mlo.req_links = drv_mlo.req_links;
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ struct wpa_bss *bss;
+
+ if (!(drv_mlo.req_links & BIT(i)))
+ continue;
+
+ bss = wpa_supplicant_get_new_bss(wpa_s, drv_mlo.links[i].bssid);
+ if (!bss) {
+ wpa_supplicant_update_scan_results(wpa_s);
+ bss = wpa_supplicant_get_new_bss(
+ wpa_s, drv_mlo.links[i].bssid);
+ }
+
+ if (!bss) {
+ wpa_dbg(wpa_s, MSG_INFO,
+ "Failed to get MLO link %d BSS", i);
+ return -1;
+ }
+
+ bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+ bss_rsnx = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
+
+ wpa_mlo.links[i].ap_rsne = bss_rsn ? (u8 *) bss_rsn : NULL;
+ wpa_mlo.links[i].ap_rsne_len = bss_rsn ? 2 + bss_rsn[1] : 0;
+ wpa_mlo.links[i].ap_rsnxe = bss_rsnx ? (u8 *) bss_rsnx : NULL;
+ wpa_mlo.links[i].ap_rsnxe_len = bss_rsnx ? 2 + bss_rsnx[1] : 0;
+
+ os_memcpy(wpa_mlo.links[i].bssid, drv_mlo.links[i].bssid,
+ ETH_ALEN);
+ os_memcpy(wpa_mlo.links[i].addr, drv_mlo.links[i].addr,
+ ETH_ALEN);
+ }
+
+out:
+ return wpa_sm_set_mlo_params(wpa_s->wpa, &wpa_mlo);
+}
+
+
static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
@@ -3631,7 +3733,7 @@
if (wpa_supplicant_dynamic_keys(wpa_s) && !ft_completed) {
wpa_clear_keys(wpa_s, bssid);
}
- if (wpa_supplicant_select_config(wpa_s) < 0) {
+ if (wpa_supplicant_select_config(wpa_s, data) < 0) {
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
return;
@@ -3668,6 +3770,15 @@
wpa_supplicant_scard_init(wpa_s, wpa_s->current_ssid);
}
wpa_sm_notify_assoc(wpa_s->wpa, bssid);
+
+ if (wpa_sm_set_ml_info(wpa_s)) {
+ wpa_dbg(wpa_s, MSG_INFO,
+ "Failed to set MLO connection info to wpa_sm");
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ return;
+ }
+
if (wpa_s->l2)
l2_packet_notify_auth_start(wpa_s->l2);
@@ -3737,12 +3848,12 @@
eapol_sm_notify_portValid(wpa_s->eapol, true);
eapol_sm_notify_eap_success(wpa_s->eapol, true);
} else {
- /* Update port, WPA_COMPLETED state from
- * EVENT_PORT_AUTHORIZED context when driver is done
- * with 4way handshake.
+ /* Update port, WPA_COMPLETED state from the
+ * EVENT_PORT_AUTHORIZED handler when the driver is done
+ * with the 4-way handshake.
*/
- wpa_msg(wpa_s, MSG_INFO, "ASSOC INFO: wait for driver port "
- "authorized indication");
+ wpa_msg(wpa_s, MSG_INFO,
+ "ASSOC INFO: wait for driver port authorized indication");
}
} else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X) &&
wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
@@ -3792,8 +3903,9 @@
os_get_reltime(&now);
os_reltime_sub(&now, &wpa_s->pending_eapol_rx_time, &age);
if (age.sec == 0 && age.usec < 200000 &&
- os_memcmp(wpa_s->pending_eapol_rx_src, bssid, ETH_ALEN) ==
- 0) {
+ os_memcmp(wpa_s->pending_eapol_rx_src,
+ wpa_s->valid_links ? wpa_s->ap_mld_addr : bssid,
+ ETH_ALEN) == 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "Process pending EAPOL "
"frame that was received just before "
"association notification");
@@ -3973,7 +4085,7 @@
"pre-shared key may be incorrect");
if (wpas_p2p_4way_hs_failed(wpa_s) > 0)
return; /* P2P group removed */
- wpas_auth_failed(wpa_s, "WRONG_KEY");
+ wpas_auth_failed(wpa_s, "WRONG_KEY", prev_pending_bssid);
#ifdef CONFIG_DPP2
wpas_dpp_send_conn_status_result(wpa_s,
DPP_STATUS_AUTH_FAILURE);
@@ -4477,7 +4589,7 @@
(wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) &&
eapol_sm_failed(wpa_s->eapol))) &&
!wpa_s->eap_expected_failure))
- wpas_auth_failed(wpa_s, "AUTH_FAILED");
+ wpas_auth_failed(wpa_s, "AUTH_FAILED", addr);
#ifdef CONFIG_P2P
if (deauth && reason_code > 0) {
@@ -5372,6 +5484,7 @@
os_reltime_sub(&now, &wpa_s->scan_start_time, &diff);
wpa_s->scan_start_time.sec = 0;
wpa_s->scan_start_time.usec = 0;
+ wpa_s->wps_scan_done = true;
wpa_dbg(wpa_s, MSG_DEBUG, "Scan completed in %ld.%06ld seconds",
diff.sec, diff.usec);
}
@@ -5821,18 +5934,21 @@
break;
case EVENT_SIGNAL_CHANGE:
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SIGNAL_CHANGE
- "above=%d signal=%d noise=%d txrate=%d",
+ "above=%d signal=%d noise=%d txrate=%lu",
data->signal_change.above_threshold,
- data->signal_change.current_signal,
+ data->signal_change.data.signal,
data->signal_change.current_noise,
- data->signal_change.current_txrate);
+ data->signal_change.data.current_tx_rate);
wpa_bss_update_level(wpa_s->current_bss,
- data->signal_change.current_signal);
+ data->signal_change.data.signal);
bgscan_notify_signal_change(
wpa_s, data->signal_change.above_threshold,
- data->signal_change.current_signal,
+ data->signal_change.data.signal,
data->signal_change.current_noise,
- data->signal_change.current_txrate);
+ data->signal_change.data.current_tx_rate);
+ os_memcpy(&wpa_s->last_signal_info, data,
+ sizeof(struct wpa_signal_info));
+ wpas_notify_signal_change(wpa_s);
break;
case EVENT_INTERFACE_MAC_CHANGED:
wpa_supplicant_update_mac_addr(wpa_s);
@@ -6099,6 +6215,15 @@
break;
#endif /* CONFIG_PASN */
case EVENT_PORT_AUTHORIZED:
+#ifndef CONFIG_NO_WPA
+ if (data->port_authorized.td_bitmap_len) {
+ wpa_printf(MSG_DEBUG,
+ "WPA3: Transition Disable bitmap from the driver event: 0x%x",
+ data->port_authorized.td_bitmap[0]);
+ wpas_transition_disable(
+ wpa_s, data->port_authorized.td_bitmap[0]);
+ }
+#endif /* CONFIG_NO_WPA */
wpa_supplicant_event_port_authorized(wpa_s);
break;
case EVENT_STATION_OPMODE_CHANGED:
diff --git a/wpa_supplicant/examples/dpp-nfc.py b/wpa_supplicant/examples/dpp-nfc.py
index 8e865f3..6cffe71 100755
--- a/wpa_supplicant/examples/dpp-nfc.py
+++ b/wpa_supplicant/examples/dpp-nfc.py
@@ -359,7 +359,7 @@
summary("NFC Handover Request message for DPP: " + str(message))
if handover.peer_crn is not None and not alt:
- summary("NFC handover request from peer was already received - do not send own")
+ summary("NFC handover request from peer was already received - do not send own[1]")
return
if handover.client:
summary("Use already started handover client")
@@ -382,7 +382,8 @@
handover.client = client
if handover.peer_crn is not None and not alt:
- summary("NFC handover request from peer was already received - do not send own")
+ summary("NFC handover request from peer was already received - do not send own[2] (except alt)")
+ run_client_alt(handover, alt)
return
summary("Sending handover request")
@@ -876,6 +877,11 @@
if init_on_touch:
summary("Starting handover client (init_on_touch)")
dpp_handover_client(handover)
+ summary("llcp_worker init_on_touch processing completed: try_own={} hs_sent={} no_alt_proposal={} start_client_alt={}".format(handover.try_own, handover.hs_sent, handover.no_alt_proposal, handover.start_client_alt))
+ if handover.start_client_alt and not handover.hs_sent:
+ summary("Try alternative handover request before exiting llcp_worker")
+ handover.start_client_alt = False
+ dpp_handover_client(handover, alt=True)
summary("Exiting llcp_worker thread (init_on_touch)")
return
diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c
index 802f120..c301f74 100644
--- a/wpa_supplicant/gas_query.c
+++ b/wpa_supplicant/gas_query.c
@@ -404,14 +404,14 @@
static void gas_query_rx_initial(struct gas_query *gas,
struct gas_query_pending *query,
- const u8 *adv_proto, const u8 *resp,
- size_t len, u16 comeback_delay)
+ const u8 *adv_proto, size_t adv_proto_len,
+ const u8 *resp, size_t len, u16 comeback_delay)
{
wpa_printf(MSG_DEBUG, "GAS: Received initial response from "
MACSTR " (dialog_token=%u comeback_delay=%u)",
MAC2STR(query->addr), query->dialog_token, comeback_delay);
- query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]);
+ query->adv_proto = wpabuf_alloc_copy(adv_proto, adv_proto_len);
if (query->adv_proto == NULL) {
gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
return;
@@ -436,9 +436,9 @@
static void gas_query_rx_comeback(struct gas_query *gas,
struct gas_query_pending *query,
- const u8 *adv_proto, const u8 *resp,
- size_t len, u8 frag_id, u8 more_frags,
- u16 comeback_delay)
+ const u8 *adv_proto, size_t adv_proto_len,
+ const u8 *resp, size_t len, u8 frag_id,
+ u8 more_frags, u16 comeback_delay)
{
wpa_printf(MSG_DEBUG, "GAS: Received comeback response from "
MACSTR " (dialog_token=%u frag_id=%u more_frags=%u "
@@ -447,7 +447,7 @@
more_frags, comeback_delay);
eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
- if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) ||
+ if (adv_proto_len != wpabuf_len(query->adv_proto) ||
os_memcmp(adv_proto, wpabuf_head(query->adv_proto),
wpabuf_len(query->adv_proto)) != 0) {
wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed "
@@ -516,6 +516,7 @@
u8 action, dialog_token, frag_id = 0, more_frags = 0;
u16 comeback_delay, resp_len;
const u8 *pos, *adv_proto;
+ size_t adv_proto_len;
int prot, pmf;
unsigned int left;
@@ -596,22 +597,26 @@
pos += 2;
/* Advertisement Protocol element */
- if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) {
+ adv_proto = pos;
+ left = data + len - adv_proto;
+ if (left < 2 || adv_proto[1] > left - 2) {
wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement "
"Protocol element in the response from " MACSTR,
MAC2STR(sa));
return 0;
}
- if (*pos != WLAN_EID_ADV_PROTO) {
+ if (*adv_proto != WLAN_EID_ADV_PROTO) {
wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
"Protocol element ID %u in response from " MACSTR,
- *pos, MAC2STR(sa));
+ *adv_proto, MAC2STR(sa));
return 0;
}
+ adv_proto_len = 2 + adv_proto[1];
+ if (adv_proto_len > (size_t) (data + len - pos))
+ return 0; /* unreachable due to an earlier check */
- adv_proto = pos;
- pos += 2 + pos[1];
+ pos += adv_proto_len;
/* Query Response Length */
if (pos + 2 > data + len) {
@@ -635,11 +640,12 @@
}
if (action == WLAN_PA_GAS_COMEBACK_RESP)
- gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len,
- frag_id, more_frags, comeback_delay);
+ gas_query_rx_comeback(gas, query, adv_proto, adv_proto_len,
+ pos, resp_len, frag_id, more_frags,
+ comeback_delay);
else
- gas_query_rx_initial(gas, query, adv_proto, pos, resp_len,
- comeback_delay);
+ gas_query_rx_initial(gas, query, adv_proto, adv_proto_len,
+ pos, resp_len, comeback_delay);
return 0;
}
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index 874c2bf..5b31f7b 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -143,7 +143,7 @@
}
-static int supp_set_key(void *ctx, enum wpa_alg alg,
+static int supp_set_key(void *ctx, int link_id, enum wpa_alg alg,
const u8 *addr, int key_idx, int set_tx,
const u8 *seq, size_t seq_len,
const u8 *key, size_t key_len, enum key_flag key_flag)
@@ -172,8 +172,9 @@
if (is_broadcast_ether_addr(addr))
addr = peer->addr;
- return wpa_drv_set_key(peer->ibss_rsn->wpa_s, alg, addr, key_idx,
- set_tx, seq, seq_len, key, key_len, key_flag);
+ return wpa_drv_set_key(peer->ibss_rsn->wpa_s, link_id, alg, addr,
+ key_idx, set_tx, seq, seq_len, key, key_len,
+ key_flag);
}
@@ -352,7 +353,7 @@
}
}
- return wpa_drv_set_key(ibss_rsn->wpa_s, alg, addr, idx,
+ return wpa_drv_set_key(ibss_rsn->wpa_s, -1, alg, addr, idx,
1, seq, 6, key, key_len, key_flag);
}
@@ -868,7 +869,7 @@
* still have a pairwise key configured. */
wpa_printf(MSG_DEBUG, "RSN: Clear pairwise key for peer "
MACSTR, MAC2STR(addr));
- wpa_drv_set_key(ibss_rsn->wpa_s, WPA_ALG_NONE, addr, 0, 0,
+ wpa_drv_set_key(ibss_rsn->wpa_s, -1, WPA_ALG_NONE, addr, 0, 0,
NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE);
}
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index acd9044..4d0fc63 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -144,9 +144,9 @@
struct wpa_cred *cred;
for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
- if (cred->roaming_consortium_len)
+ if (cred->num_home_ois)
return 1;
- if (cred->required_roaming_consortium_len)
+ if (cred->num_required_home_ois)
return 1;
if (cred->num_roaming_consortiums)
return 1;
@@ -421,6 +421,11 @@
case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2");
break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "Unsupported EAP-TTLS inner method %u",
+ *pos);
+ break;
}
break;
case NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD:
@@ -1098,8 +1103,7 @@
}
-static int roaming_consortium_element_match(const u8 *ie, const u8 *rc_id,
- size_t rc_len)
+static int oi_element_match(const u8 *ie, const u8 *oi, size_t oi_len)
{
const u8 *pos, *end;
u8 lens;
@@ -1124,24 +1128,24 @@
if ((lens & 0x0f) + (lens >> 4) > end - pos)
return 0;
- if ((lens & 0x0f) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
+ if ((lens & 0x0f) == oi_len && os_memcmp(pos, oi, oi_len) == 0)
return 1;
pos += lens & 0x0f;
- if ((lens >> 4) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
+ if ((lens >> 4) == oi_len && os_memcmp(pos, oi, oi_len) == 0)
return 1;
pos += lens >> 4;
- if (pos < end && (size_t) (end - pos) == rc_len &&
- os_memcmp(pos, rc_id, rc_len) == 0)
+ if (pos < end && (size_t) (end - pos) == oi_len &&
+ os_memcmp(pos, oi, oi_len) == 0)
return 1;
return 0;
}
-static int roaming_consortium_anqp_match(const struct wpabuf *anqp,
- const u8 *rc_id, size_t rc_len)
+static int oi_anqp_match(const struct wpabuf *anqp, const u8 *oi,
+ size_t oi_len)
{
const u8 *pos, *end;
u8 len;
@@ -1157,7 +1161,7 @@
len = *pos++;
if (len > end - pos)
break;
- if (len == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
+ if (len == oi_len && os_memcmp(pos, oi, oi_len) == 0)
return 1;
pos += len;
}
@@ -1166,11 +1170,26 @@
}
-static int roaming_consortium_match(const u8 *ie, const struct wpabuf *anqp,
- const u8 *rc_id, size_t rc_len)
+static int oi_match(const u8 *ie, const struct wpabuf *anqp,
+ const u8 *oi, size_t oi_len)
{
- return roaming_consortium_element_match(ie, rc_id, rc_len) ||
- roaming_consortium_anqp_match(anqp, rc_id, rc_len);
+ return oi_element_match(ie, oi, oi_len) ||
+ oi_anqp_match(anqp, oi, oi_len);
+}
+
+
+static int cred_home_ois_match(const u8 *ie, const struct wpabuf *anqp,
+ const struct wpa_cred *cred) {
+ unsigned int i;
+
+ /* There's a match if at least one of the home OI matches. */
+ for (i = 0; i < cred->num_home_ois; i++) {
+ if (oi_match(ie, anqp, cred->home_ois[i],
+ cred->home_ois_len[i]))
+ return 1;
+ }
+
+ return 0;
}
@@ -1181,9 +1200,8 @@
unsigned int i;
for (i = 0; i < cred->num_roaming_consortiums; i++) {
- if (roaming_consortium_match(ie, anqp,
- cred->roaming_consortiums[i],
- cred->roaming_consortiums_len[i]))
+ if (oi_match(ie, anqp, cred->roaming_consortiums[i],
+ cred->roaming_consortiums_len[i]))
return 1;
}
@@ -1194,8 +1212,9 @@
static int cred_no_required_oi_match(struct wpa_cred *cred, struct wpa_bss *bss)
{
const u8 *ie;
+ unsigned int i;
- if (cred->required_roaming_consortium_len == 0)
+ if (cred->num_required_home_ois == 0)
return 0;
ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
@@ -1204,11 +1223,16 @@
(bss->anqp == NULL || bss->anqp->roaming_consortium == NULL))
return 1;
- return !roaming_consortium_match(ie,
- bss->anqp ?
- bss->anqp->roaming_consortium : NULL,
- cred->required_roaming_consortium,
- cred->required_roaming_consortium_len);
+ /* According to Passpoint specification, there must be a match for
+ * each required home OI provided. */
+ for (i = 0; i < cred->num_required_home_ois; i++) {
+ if (!oi_match(ie, bss->anqp ?
+ bss->anqp->roaming_consortium : NULL,
+ cred->required_home_ois[i],
+ cred->required_home_ois_len[i]))
+ return 1;
+ }
+ return 0;
}
@@ -1408,26 +1432,24 @@
return NULL;
for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
- if (cred->roaming_consortium_len == 0 &&
+ if (cred->num_home_ois == 0 &&
+ cred->num_required_home_ois == 0 &&
cred->num_roaming_consortiums == 0)
continue;
if (!cred->eap_method)
continue;
- if ((cred->roaming_consortium_len == 0 ||
- !roaming_consortium_match(ie, anqp,
- cred->roaming_consortium,
- cred->roaming_consortium_len)) &&
- !cred_roaming_consortiums_match(ie, anqp, cred) &&
- (cred->required_roaming_consortium_len == 0 ||
- !roaming_consortium_match(
- ie, anqp, cred->required_roaming_consortium,
- cred->required_roaming_consortium_len)))
+ /* If there's required home OIs, there must be a match for each
+ * required OI (see Passpoint v3.2 - 9.1.2 - RequiredHomeOI). */
+ if (cred->num_required_home_ois > 0 &&
+ cred_no_required_oi_match(cred, bss))
continue;
- if (cred_no_required_oi_match(cred, bss))
+ if (!cred_home_ois_match(ie, anqp, cred) &&
+ !cred_roaming_consortiums_match(ie, anqp, cred))
continue;
+
if (!ignore_bw && cred_below_min_backhaul(wpa_s, cred, bss))
continue;
if (!ignore_bw && cred_over_max_bss_load(wpa_s, cred, bss))
@@ -1642,9 +1664,8 @@
ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
anqp = bss->anqp ? bss->anqp->roaming_consortium : NULL;
for (i = 0; (ie || anqp) && i < cred->num_roaming_consortiums; i++) {
- if (!roaming_consortium_match(
- ie, anqp, cred->roaming_consortiums[i],
- cred->roaming_consortiums_len[i]))
+ if (!oi_match(ie, anqp, cred->roaming_consortiums[i],
+ cred->roaming_consortiums_len[i]))
continue;
ssid->roaming_consortium_selection =
diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c
index 2eb9a7e..c1ed8c4 100644
--- a/wpa_supplicant/mesh_mpm.c
+++ b/wpa_supplicant/mesh_mpm.c
@@ -879,7 +879,8 @@
if (conf->security & MESH_CONF_SEC_AMPE) {
wpa_hexdump_key(MSG_DEBUG, "mesh: MTK", sta->mtk, sta->mtk_len);
- wpa_drv_set_key(wpa_s, wpa_cipher_to_alg(conf->pairwise_cipher),
+ wpa_drv_set_key(wpa_s, -1,
+ wpa_cipher_to_alg(conf->pairwise_cipher),
sta->addr, 0, 0, seq, sizeof(seq),
sta->mtk, sta->mtk_len,
KEY_FLAG_PAIRWISE_RX_TX);
@@ -888,7 +889,8 @@
sta->mgtk_rsc, sizeof(sta->mgtk_rsc));
wpa_hexdump_key(MSG_DEBUG, "mesh: RX MGTK",
sta->mgtk, sta->mgtk_len);
- wpa_drv_set_key(wpa_s, wpa_cipher_to_alg(conf->group_cipher),
+ wpa_drv_set_key(wpa_s, -1,
+ wpa_cipher_to_alg(conf->group_cipher),
sta->addr, sta->mgtk_key_id, 0,
sta->mgtk_rsc, sizeof(sta->mgtk_rsc),
sta->mgtk, sta->mgtk_len,
@@ -900,7 +902,7 @@
wpa_hexdump_key(MSG_DEBUG, "mesh: RX IGTK",
sta->igtk, sta->igtk_len);
wpa_drv_set_key(
- wpa_s,
+ wpa_s, -1,
wpa_cipher_to_alg(conf->mgmt_group_cipher),
sta->addr, sta->igtk_key_id, 0,
sta->igtk_rsc, sizeof(sta->igtk_rsc),
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
index 65daa77..12dcc30 100644
--- a/wpa_supplicant/mesh_rsn.c
+++ b/wpa_supplicant/mesh_rsn.c
@@ -118,7 +118,7 @@
}
wpa_hexdump_key(MSG_DEBUG, "AUTH: set_key - key", key, key_len);
- return wpa_drv_set_key(mesh_rsn->wpa_s, alg, addr, idx,
+ return wpa_drv_set_key(mesh_rsn->wpa_s, -1, alg, addr, idx,
1, seq, 6, key, key_len, key_flag);
}
@@ -194,7 +194,7 @@
/* group mgmt */
wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX IGTK",
rsn->igtk, rsn->igtk_len);
- wpa_drv_set_key(rsn->wpa_s,
+ wpa_drv_set_key(rsn->wpa_s, -1,
wpa_cipher_to_alg(rsn->mgmt_group_cipher),
broadcast_ether_addr,
rsn->igtk_key_id, 1,
@@ -205,7 +205,7 @@
/* group privacy / data frames */
wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX MGTK",
rsn->mgtk, rsn->mgtk_len);
- wpa_drv_set_key(rsn->wpa_s, wpa_cipher_to_alg(rsn->group_cipher),
+ wpa_drv_set_key(rsn->wpa_s, -1, wpa_cipher_to_alg(rsn->group_cipher),
broadcast_ether_addr,
rsn->mgtk_key_id, 1, seq, sizeof(seq),
rsn->mgtk, rsn->mgtk_len, KEY_FLAG_GROUP_TX_DEFAULT);
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 76f2232..32ddf1f 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -16,6 +16,7 @@
#include "dbus/dbus_common.h"
#include "dbus/dbus_new.h"
#include "rsn_supp/wpa.h"
+#include "rsn_supp/pmksa_cache.h"
#include "fst/fst.h"
#include "crypto/tls.h"
#include "bss.h"
@@ -237,6 +238,15 @@
}
+void wpas_notify_mac_address_changed(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_MAC_ADDRESS);
+}
+
+
void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s)
{
if (wpa_s->p2p_mgmt)
@@ -447,11 +457,6 @@
wpas_notify_persistent_group_removed(wpa_s, ssid);
wpas_p2p_network_removed(wpa_s, ssid);
-
-#ifdef CONFIG_PASN
- if (wpa_s->pasn.ssid == ssid)
- wpa_s->pasn.ssid = NULL;
-#endif /* CONFIG_PASN */
}
@@ -1357,3 +1362,9 @@
{
return wpas_aidl_get_certificate(alias, value);
}
+
+
+void wpas_notify_signal_change(struct wpa_supplicant *wpa_s)
+{
+ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_SIGNAL_CHANGE);
+}
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index 9a818ef..b1824ec 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -19,6 +19,7 @@
struct wps_event_fail;
struct tls_cert_data;
struct wpa_cred;
+struct rsn_pmksa_cache_entry;
int wpas_notify_supplicant_initialized(struct wpa_global *global);
void wpas_notify_supplicant_deinitialized(struct wpa_global *global);
@@ -39,6 +40,7 @@
void wpas_notify_network_changed(struct wpa_supplicant *wpa_s);
void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s);
void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s);
+void wpas_notify_mac_address_changed(struct wpa_supplicant *wpa_s);
void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s);
void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
@@ -199,8 +201,6 @@
const char *channel_list, unsigned short band_list[], int size);
void wpas_notify_dpp_config_accepted(struct wpa_supplicant *wpa_s);
void wpas_notify_dpp_config_rejected(struct wpa_supplicant *wpa_s);
-void wpas_notify_pmk_cache_added(struct wpa_supplicant *wpa_s,
- struct rsn_pmksa_cache_entry *entry);
void wpas_notify_transition_disable(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
u8 bitmap);
@@ -222,5 +222,8 @@
struct dscp_policy_data *policies, int num_policies);
void wpas_notify_frequency_changed(struct wpa_supplicant *wpa_s, int frequency);
ssize_t wpas_get_certificate(const char *alias, uint8_t** value);
+void wpas_notify_pmk_cache_added(struct wpa_supplicant *wpa_s,
+ struct rsn_pmksa_cache_entry *entry);
+void wpas_notify_signal_change(struct wpa_supplicant *wpa_s);
#endif /* NOTIFY_H */
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index a505e4f..926ba7a 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -192,7 +192,7 @@
return -1;
num = get_shared_radio_freqs(wpa_s, freqs,
- wpa_s->num_multichan_concurrent);
+ wpa_s->num_multichan_concurrent, false);
os_free(freqs);
unused = wpa_s->num_multichan_concurrent - num;
@@ -219,7 +219,8 @@
return 0;
num = get_shared_radio_freqs_data(wpa_s, freqs,
- wpa_s->num_multichan_concurrent);
+ wpa_s->num_multichan_concurrent,
+ false);
os_memset(p2p_freqs, 0, sizeof(struct wpa_used_freq_data) * len);
@@ -339,6 +340,23 @@
}
+void wpas_p2p_scan_freqs(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params,
+ bool include_6ghz)
+{
+ wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A,
+ params, false, false, false);
+ wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G,
+ params, false, false, false);
+ wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211AD,
+ params, false, false, false);
+ if (!wpa_s->conf->p2p_6ghz_disable &&
+ is_p2p_allow_6ghz(wpa_s->global->p2p) && include_6ghz)
+ wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A,
+ params, true, true, false);
+}
+
+
static void wpas_p2p_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
{
struct wpa_supplicant *wpa_s = work->wpa_s;
@@ -361,14 +379,9 @@
params->only_new_results = 1;
}
- if (!params->p2p_include_6ghz && !params->freqs) {
- wpa_printf(MSG_DEBUG,
- "P2P: Exclude 6 GHz channels - update the scan frequency list");
- wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, params,
- false, false);
- wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, params,
- false, false);
- }
+ if (!params->freqs)
+ wpas_p2p_scan_freqs(wpa_s, params, params->p2p_include_6ghz);
+
ret = wpa_drv_scan(wpa_s, params);
if (ret == 0)
wpa_s->curr_scan_cookie = params->scan_cookie;
@@ -447,6 +460,13 @@
num_req_dev_types, req_dev_types);
if (wps_ie == NULL)
goto fail;
+
+ /*
+ * In case 6 GHz channels are requested as part of the P2P scan, only
+ * the PSCs would be included as P2P GOs are not expected to be
+ * collocated, i.e., they would not be announced in the RNR element of
+ * other APs.
+ */
if (!wpa_s->conf->p2p_6ghz_disable)
params->p2p_include_6ghz = include_6ghz;
switch (type) {
@@ -533,9 +553,9 @@
return WPA_IF_P2P_GO;
case P2P_GROUP_INTERFACE_CLIENT:
return WPA_IF_P2P_CLIENT;
+ default:
+ return WPA_IF_P2P_GROUP;
}
-
- return WPA_IF_P2P_GROUP;
}
@@ -2090,7 +2110,7 @@
ssid->auth_alg |= WPA_AUTH_ALG_SAE;
ssid->key_mgmt = WPA_KEY_MGMT_SAE;
ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED;
- ssid->sae_pwe = 1;
+ ssid->sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use SAE auth_alg and key_mgmt");
} else {
p2p_set_6ghz_dev_capab(wpa_s->global->p2p, false);
@@ -2181,6 +2201,7 @@
d->passive_scan = s->passive_scan;
d->pmf = s->pmf;
d->p2p_6ghz_disable = s->p2p_6ghz_disable;
+ d->sae_pwe = s->sae_pwe;
if (s->wps_nfc_dh_privkey && s->wps_nfc_dh_pubkey &&
!d->wps_nfc_pw_from_config) {
@@ -2412,9 +2433,9 @@
bool wpas_p2p_retry_limit_exceeded(struct wpa_supplicant *wpa_s)
{
if (!wpa_s->p2p_in_invitation || !wpa_s->p2p_retry_limit ||
- wpa_s->p2p_in_invitation <= wpa_s->p2p_retry_limit) {
+ wpa_s->p2p_in_invitation <= wpa_s->p2p_retry_limit)
return false;
- }
+
wpa_printf(MSG_DEBUG, "P2P: Group join retry limit exceeded");
eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
wpa_s->p2pdev, NULL);
@@ -3997,8 +4018,7 @@
for (op = 0; global_op_class[op].op_class; op++) {
const struct oper_class_map *o = &global_op_class[op];
- u16 ch;
- int chan = channel;
+ u16 ch = 0;
/* Allow DFS channels marked as NO_P2P_SUPP to be used with
* driver offloaded DFS. */
@@ -4009,15 +4029,22 @@
wpa_s->conf->p2p_6ghz_disable))
continue;
+ /* IEEE Std 802.11ax-2021 26.17.2.3.2: "A 6 GHz-only AP should
+ * set up the BSS with a primary 20 MHz channel that coincides
+ * with a preferred scanning channel (PSC)."
+ * 6 GHz BW40 operation class 132 in wpa_supplicant uses the
+ * lowest 20 MHz channel for simplicity, so increase ch by 4 to
+ * match the PSC.
+ */
if (is_6ghz_op_class(o->op_class) && o->bw == BW40 &&
get_6ghz_sec_channel(channel) < 0)
- chan = channel - 4;
+ ch = 4;
- for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
+ for (ch += o->min_chan; ch <= o->max_chan; ch += o->inc) {
if (o->mode != HOSTAPD_MODE_IEEE80211A ||
(o->bw != BW40PLUS && o->bw != BW40MINUS &&
o->bw != BW40) ||
- ch != chan)
+ ch != channel)
continue;
ret = wpas_p2p_verify_channel(wpa_s, mode, o->op_class,
ch, o->bw);
@@ -5557,14 +5584,8 @@
if (freq > 0) {
freqs[0] = freq;
params.freqs = freqs;
- } else if (wpa_s->conf->p2p_6ghz_disable ||
- !is_p2p_allow_6ghz(wpa_s->global->p2p)) {
- wpa_printf(MSG_DEBUG,
- "P2P: 6 GHz disabled - update the scan frequency list");
- wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, ¶ms,
- false, false);
- wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, ¶ms,
- false, false);
+ } else {
+ wpas_p2p_scan_freqs(wpa_s, ¶ms, true);
}
ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
@@ -6541,7 +6562,8 @@
return -1;
num = get_shared_radio_freqs_data(wpa_s, freqs,
- wpa_s->num_multichan_concurrent);
+ wpa_s->num_multichan_concurrent,
+ false);
if (wpa_s->current_ssid &&
wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO &&
@@ -8426,7 +8448,7 @@
if (!freqs)
return;
- num = get_shared_radio_freqs_data(wpa_s, freqs, num);
+ num = get_shared_radio_freqs_data(wpa_s, freqs, num, false);
os_memset(&chan, 0, sizeof(chan));
os_memset(&cli_chan, 0, sizeof(cli_chan));
@@ -8612,6 +8634,10 @@
"in group formation",
wpa_s->global->p2p_group_formation->ifname);
ret = 1;
+ } else if (wpa_s->global->p2p_group_formation == wpa_s) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Skip Extended Listen timeout and allow scans on current interface for group formation");
+ ret = 2;
}
}
@@ -9973,7 +9999,7 @@
if (!freqs)
return;
- num = get_shared_radio_freqs_data(wpa_s, freqs, num);
+ num = get_shared_radio_freqs_data(wpa_s, freqs, num, false);
/* Previous attempt to move a GO was not possible -- try again. */
wpas_p2p_consider_moving_gos(wpa_s, freqs, num,
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
index c87e1bf..e113c62 100644
--- a/wpa_supplicant/p2p_supplicant.h
+++ b/wpa_supplicant/p2p_supplicant.h
@@ -147,6 +147,9 @@
void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
const u8 *addr);
int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s);
+void wpas_p2p_scan_freqs(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params,
+ bool include_6ghz);
int wpas_p2p_get_sec_channel_offset_40mhz(struct wpa_supplicant *wpa_s,
struct hostapd_hw_modes *mode,
u8 channel);
diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c
index 2e6d9a7..edecfde 100644
--- a/wpa_supplicant/pasn_supplicant.c
+++ b/wpa_supplicant/pasn_supplicant.c
@@ -30,7 +30,7 @@
struct wpa_pasn_auth_work {
u8 own_addr[ETH_ALEN];
- u8 bssid[ETH_ALEN];
+ u8 peer_addr[ETH_ALEN];
int akmp;
int cipher;
u16 group;
@@ -39,6 +39,15 @@
};
+static int wpas_pasn_send_mlme(void *ctx, const u8 *data, size_t data_len,
+ int noack, unsigned int freq, unsigned int wait)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ return wpa_drv_send_mlme(wpa_s, data, data_len, noack, freq, wait);
+}
+
+
static void wpas_pasn_free_auth_work(struct wpa_pasn_auth_work *awork)
{
wpabuf_free(awork->comeback);
@@ -68,7 +77,8 @@
}
-static void wpas_pasn_auth_status(struct wpa_supplicant *wpa_s, const u8 *bssid,
+static void wpas_pasn_auth_status(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr,
int akmp, int cipher, u8 status,
struct wpabuf *comeback,
u16 comeback_after)
@@ -84,7 +94,7 @@
wpa_msg(wpa_s, MSG_INFO, PASN_AUTH_STATUS MACSTR
" akmp=%s, status=%u comeback_after=%u comeback=%s",
- MAC2STR(bssid),
+ MAC2STR(peer_addr),
wpa_key_mgmt_txt(akmp, WPA_PROTO_RSN),
status, comeback_after, comeback_txt);
@@ -95,176 +105,15 @@
wpa_msg(wpa_s, MSG_INFO,
PASN_AUTH_STATUS MACSTR " akmp=%s, status=%u",
- MAC2STR(bssid), wpa_key_mgmt_txt(akmp, WPA_PROTO_RSN),
+ MAC2STR(peer_addr), wpa_key_mgmt_txt(akmp, WPA_PROTO_RSN),
status);
}
#ifdef CONFIG_SAE
-static struct wpabuf * wpas_pasn_wd_sae_commit(struct wpa_supplicant *wpa_s)
-{
- struct wpas_pasn *pasn = &wpa_s->pasn;
- struct wpabuf *buf = NULL;
- int ret;
-
- ret = sae_set_group(&pasn->sae, pasn->group);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to set SAE group");
- return NULL;
- }
-
- ret = sae_prepare_commit_pt(&pasn->sae, pasn->ssid->pt,
- pasn->own_addr, pasn->bssid,
- NULL, NULL);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit");
- return NULL;
- }
-
- /* Need to add the entire Authentication frame body */
- buf = wpabuf_alloc(6 + SAE_COMMIT_MAX_LEN);
- if (!buf) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
- return NULL;
- }
-
- wpabuf_put_le16(buf, WLAN_AUTH_SAE);
- wpabuf_put_le16(buf, 1);
- wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT);
-
- sae_write_commit(&pasn->sae, buf, NULL, 0);
- pasn->sae.state = SAE_COMMITTED;
-
- return buf;
-}
-
-
-static int wpas_pasn_wd_sae_rx(struct wpa_supplicant *wpa_s, struct wpabuf *wd)
-{
- struct wpas_pasn *pasn = &wpa_s->pasn;
- const u8 *data;
- size_t buf_len;
- u16 len, 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);
-
- /* first handle the commit message */
- if (buf_len < 2) {
- wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short (commit)");
- return -1;
- }
-
- len = WPA_GET_LE16(data);
- if (len < 6 || buf_len - 2 < len) {
- wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short for commit");
- return -1;
- }
-
- buf_len -= 2;
- data += 2;
-
- 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);
-
- if (alg != WLAN_AUTH_SAE || seq != 1 ||
- status != WLAN_STATUS_SAE_HASH_TO_ELEMENT) {
- wpa_printf(MSG_DEBUG, "PASN: SAE: dropping peer commit");
- return -1;
- }
-
- res = sae_parse_commit(&pasn->sae, data + 6, len - 6, NULL, 0, groups,
- 1);
- if (res != WLAN_STATUS_SUCCESS) {
- wpa_printf(MSG_DEBUG, "PASN: SAE failed parsing 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;
- }
-
- buf_len -= len;
- data += len;
-
- /* Handle the confirm message */
- if (buf_len < 2) {
- wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short (confirm)");
- return -1;
- }
-
- len = WPA_GET_LE16(data);
- if (len < 6 || buf_len - 2 < len) {
- wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short for confirm");
- return -1;
- }
-
- buf_len -= 2;
- data += 2;
-
- 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, len - 6);
- if (res != WLAN_STATUS_SUCCESS) {
- wpa_printf(MSG_DEBUG, "PASN: SAE failed checking confirm");
- return -1;
- }
-
- wpa_printf(MSG_DEBUG, "PASN: SAE completed successfully");
- pasn->sae.state = SAE_ACCEPTED;
-
- return 0;
-}
-
-
-static struct wpabuf * wpas_pasn_wd_sae_confirm(struct wpa_supplicant *wpa_s)
-{
- struct wpas_pasn *pasn = &wpa_s->pasn;
- struct wpabuf *buf = NULL;
-
- /* Need to add the entire authentication frame body */
- buf = wpabuf_alloc(6 + SAE_CONFIRM_MAX_LEN);
- if (!buf) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
- return NULL;
- }
-
- 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);
- pasn->sae.state = SAE_CONFIRMED;
-
- return buf;
-}
-
-
-static int wpas_pasn_sae_setup_pt(struct wpa_supplicant *wpa_s,
- struct wpa_ssid *ssid, int group)
+static struct sae_pt *
+wpas_pasn_sae_derive_pt(struct wpa_ssid *ssid, int group)
{
const char *password = ssid->sae_password;
int groups[2] = { group, 0 };
@@ -274,15 +123,26 @@
if (!password) {
wpa_printf(MSG_DEBUG, "PASN: SAE without a password");
+ return NULL;
+ }
+
+ return sae_derive_pt(groups, ssid->ssid, ssid->ssid_len,
+ (const u8 *) password, os_strlen(password),
+ ssid->sae_password_id);
+}
+
+
+static int wpas_pasn_sae_setup_pt(struct wpa_ssid *ssid, int group)
+{
+ if (!ssid->sae_password && !ssid->passphrase) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE without a password");
return -1;
}
if (ssid->pt)
return 0; /* PT already derived */
- ssid->pt = sae_derive_pt(groups, ssid->ssid, ssid->ssid_len,
- (const u8 *) password, os_strlen(password),
- ssid->sae_password_id);
+ ssid->pt = wpas_pasn_sae_derive_pt(ssid, group);
return ssid->pt ? 0 : -1;
}
@@ -302,12 +162,12 @@
struct wpa_ssid *ssid = NULL;
size_t ssid_str_len = 0;
const u8 *ssid_str = NULL;
- const u8 *bssid = peer->peer_addr;
+ const u8 *peer_addr = peer->peer_addr;
- bss = wpa_bss_get_bssid(wpa_s, bssid);
+ bss = wpa_bss_get_bssid(wpa_s, peer_addr);
if (!bss) {
wpa_supplicant_update_scan_results(wpa_s);
- bss = wpa_bss_get_bssid(wpa_s, bssid);
+ bss = wpa_bss_get_bssid(wpa_s, peer_addr);
if (!bss) {
wpa_printf(MSG_DEBUG, "PASN: BSS not found");
return -1;
@@ -394,13 +254,13 @@
} else if ((sel & WPA_KEY_MGMT_SAE_EXT_KEY) &&
(ieee802_11_rsnx_capab(rsnxe,
WLAN_RSNX_CAPAB_SAE_H2E)) &&
- (wpas_pasn_sae_setup_pt(wpa_s, ssid, group) == 0)) {
+ (wpas_pasn_sae_setup_pt(ssid, group) == 0)) {
key_mgmt = WPA_KEY_MGMT_SAE_EXT_KEY;
wpa_printf(MSG_DEBUG, "PASN: using KEY_MGMT SAE (ext key)");
} else if ((sel & WPA_KEY_MGMT_SAE) &&
(ieee802_11_rsnx_capab(rsnxe,
WLAN_RSNX_CAPAB_SAE_H2E)) &&
- (wpas_pasn_sae_setup_pt(wpa_s, ssid, group) == 0)) {
+ (wpas_pasn_sae_setup_pt(ssid, group) == 0)) {
key_mgmt = WPA_KEY_MGMT_SAE;
wpa_printf(MSG_DEBUG, "PASN: using KEY_MGMT SAE");
#endif /* CONFIG_SAE */
@@ -447,15 +307,16 @@
static int wpas_pasn_set_keys_from_cache(struct wpa_supplicant *wpa_s,
- const u8 *own_addr, const u8 *bssid,
+ const u8 *own_addr,
+ const u8 *peer_addr,
int cipher, int akmp)
{
struct ptksa_cache_entry *entry;
- entry = ptksa_cache_get(wpa_s->ptksa, bssid, cipher);
+ entry = ptksa_cache_get(wpa_s->ptksa, peer_addr, cipher);
if (!entry) {
wpa_printf(MSG_DEBUG, "PASN: peer " MACSTR
- " not present in PTKSA cache", MAC2STR(bssid));
+ " not present in PTKSA cache", MAC2STR(peer_addr));
return -1;
}
@@ -468,8 +329,8 @@
}
wpa_printf(MSG_DEBUG, "PASN: " MACSTR " present in PTKSA cache",
- MAC2STR(bssid));
- wpa_drv_set_secure_ranging_ctx(wpa_s, own_addr, bssid, cipher,
+ MAC2STR(peer_addr));
+ wpa_drv_set_secure_ranging_ctx(wpa_s, own_addr, peer_addr, cipher,
entry->ptk.tk_len,
entry->ptk.tk,
entry->ptk.ltf_keyseed_len,
@@ -559,871 +420,59 @@
for (i = 0; i < pasn_params->num_peers; i++) {
peer = &pasn_params->peer[i];
- wpas_pasn_deauthenticate(wpa_s, peer->own_addr,
- peer->peer_addr);
+ ptksa_cache_flush(wpa_s->ptksa, peer->peer_addr,
+ WPA_CIPHER_NONE);
}
}
-#ifdef CONFIG_FILS
-
-static struct wpabuf * wpas_pasn_fils_build_auth(struct wpa_supplicant *wpa_s)
+static void wpas_pasn_initiate_eapol(struct pasn_data *pasn,
+ struct wpa_ssid *ssid)
{
- struct wpas_pasn *pasn = &wpa_s->pasn;
- struct wpabuf *buf = NULL;
- struct wpabuf *erp_msg;
- int ret;
-
- erp_msg = eapol_sm_build_erp_reauth_start(wpa_s->eapol);
- if (!erp_msg) {
- wpa_printf(MSG_DEBUG,
- "PASN: FILS: ERP EAP-Initiate/Re-auth unavailable");
- return NULL;
- }
-
- if (random_get_bytes(pasn->fils.nonce, FILS_NONCE_LEN) < 0 ||
- random_get_bytes(pasn->fils.session, FILS_SESSION_LEN) < 0)
- goto fail;
-
- wpa_hexdump(MSG_DEBUG, "PASN: FILS: Nonce", pasn->fils.nonce,
- FILS_NONCE_LEN);
-
- wpa_hexdump(MSG_DEBUG, "PASN: FILS: Session", pasn->fils.session,
- FILS_SESSION_LEN);
-
- buf = wpabuf_alloc(1500);
- if (!buf)
- goto fail;
-
- /* Add the authentication algorithm */
- wpabuf_put_le16(buf, WLAN_AUTH_FILS_SK);
-
- /* Authentication Transaction seq# */
- wpabuf_put_le16(buf, 1);
-
- /* 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, pasn->fils.nonce, 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, pasn->fils.session, FILS_SESSION_LEN);
-
- /* Wrapped Data (ERP) */
- wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
- wpabuf_put_u8(buf, 1 + wpabuf_len(erp_msg));
- wpabuf_put_u8(buf, WLAN_EID_EXT_WRAPPED_DATA);
- wpabuf_put_buf(buf, erp_msg);
-
- /*
- * Calculate pending PMKID here so that we do not need to maintain a
- * copy of the EAP-Initiate/Reauth message.
- */
- ret = fils_pmkid_erp(pasn->akmp, wpabuf_head(erp_msg),
- wpabuf_len(erp_msg),
- pasn->fils.erp_pmkid);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to get ERP PMKID");
- goto fail;
- }
-
- wpabuf_free(erp_msg);
- erp_msg = NULL;
-
- wpa_hexdump_buf(MSG_DEBUG, "PASN: FILS: Authentication frame", buf);
- return buf;
-fail:
- wpabuf_free(erp_msg);
- wpabuf_free(buf);
- return NULL;
-}
-
-
-static void wpas_pasn_initiate_eapol(struct wpa_supplicant *wpa_s)
-{
- struct wpas_pasn *pasn = &wpa_s->pasn;
struct eapol_config eapol_conf;
- struct wpa_ssid *ssid = pasn->ssid;
wpa_printf(MSG_DEBUG, "PASN: FILS: Initiating EAPOL");
- eapol_sm_notify_eap_success(wpa_s->eapol, false);
- eapol_sm_notify_eap_fail(wpa_s->eapol, false);
- eapol_sm_notify_portControl(wpa_s->eapol, Auto);
+ eapol_sm_notify_eap_success(pasn->eapol, false);
+ eapol_sm_notify_eap_fail(pasn->eapol, false);
+ eapol_sm_notify_portControl(pasn->eapol, Auto);
os_memset(&eapol_conf, 0, sizeof(eapol_conf));
- eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
+ eapol_conf.fast_reauth = pasn->fast_reauth;
eapol_conf.workaround = ssid->eap_workaround;
- eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
-}
-
-
-static struct wpabuf * wpas_pasn_wd_fils_auth(struct wpa_supplicant *wpa_s)
-{
- struct wpas_pasn *pasn = &wpa_s->pasn;
- struct wpa_bss *bss;
- const u8 *indic;
- u16 fils_info;
-
- wpa_printf(MSG_DEBUG, "PASN: FILS: wrapped data - completed=%u",
- pasn->fils.completed);
-
- /* Nothing to add as we are done */
- if (pasn->fils.completed)
- return NULL;
-
- if (!pasn->ssid) {
- wpa_printf(MSG_DEBUG, "PASN: FILS: No network block");
- return NULL;
- }
-
- bss = wpa_bss_get_bssid(wpa_s, pasn->bssid);
- if (!bss) {
- wpa_printf(MSG_DEBUG, "PASN: FILS: BSS not found");
- return NULL;
- }
-
- indic = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION);
- if (!indic || indic[1] < 2) {
- wpa_printf(MSG_DEBUG, "PASN: Missing FILS Indication IE");
- return NULL;
- }
-
- fils_info = WPA_GET_LE16(indic + 2);
- if (!(fils_info & BIT(9))) {
- wpa_printf(MSG_DEBUG,
- "PASN: FILS auth without PFS not supported");
- return NULL;
- }
-
- wpas_pasn_initiate_eapol(wpa_s);
-
- return wpas_pasn_fils_build_auth(wpa_s);
-}
-
-
-static int wpas_pasn_wd_fils_rx(struct wpa_supplicant *wpa_s, struct wpabuf *wd)
-{
- struct wpas_pasn *pasn = &wpa_s->pasn;
- struct ieee802_11_elems elems;
- struct wpa_ie_data rsne_data;
- u8 rmsk[ERP_MAX_KEY_LEN];
- size_t rmsk_len;
- u8 anonce[FILS_NONCE_LEN];
- const u8 *data;
- size_t buf_len;
- struct wpabuf *fils_wd = NULL;
- u16 alg, seq, status;
- int ret;
-
- if (!wd)
- return -1;
-
- data = wpabuf_head(wd);
- buf_len = wpabuf_len(wd);
-
- wpa_hexdump(MSG_DEBUG, "PASN: FILS: Authentication frame len=%zu",
- data, buf_len);
-
- /* first handle the header */
- if (buf_len < 6) {
- wpa_printf(MSG_DEBUG, "PASN: FILS: Buffer too short");
- 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: commit: alg=%u, seq=%u, status=%u",
- alg, seq, status);
-
- if (alg != WLAN_AUTH_FILS_SK || seq != 2 ||
- 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(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: ANonce", elems.fils_nonce,
- FILS_NONCE_LEN);
- os_memcpy(anonce, elems.fils_nonce, FILS_NONCE_LEN);
-
- wpa_hexdump(MSG_DEBUG, "PASN: FILS: FILS Session", elems.fils_session,
- FILS_SESSION_LEN);
-
- if (os_memcmp(pasn->fils.session, elems.fils_session,
- FILS_SESSION_LEN)) {
- wpa_printf(MSG_DEBUG, "PASN: FILS: Session mismatch");
- return -1;
- }
-
- fils_wd = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION,
- WLAN_EID_EXT_WRAPPED_DATA);
-
- if (!fils_wd) {
- wpa_printf(MSG_DEBUG,
- "PASN: FILS: Failed getting wrapped data");
- return -1;
- }
-
- eapol_sm_process_erp_finish(wpa_s->eapol, wpabuf_head(fils_wd),
- wpabuf_len(fils_wd));
-
- wpabuf_free(fils_wd);
- fils_wd = NULL;
-
- if (eapol_sm_failed(wpa_s->eapol)) {
- wpa_printf(MSG_DEBUG, "PASN: FILS: ERP finish failed");
- return -1;
- }
-
- rmsk_len = ERP_MAX_KEY_LEN;
- ret = eapol_sm_get_key(wpa_s->eapol, rmsk, rmsk_len);
-
- if (ret == PMK_LEN) {
- rmsk_len = PMK_LEN;
- ret = eapol_sm_get_key(wpa_s->eapol, rmsk, rmsk_len);
- }
-
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: FILS: Failed getting RMSK");
- return -1;
- }
-
- ret = fils_rmsk_to_pmk(pasn->akmp, rmsk, rmsk_len,
- pasn->fils.nonce, anonce, NULL, 0,
- pasn->pmk, &pasn->pmk_len);
-
- forced_memzero(rmsk, sizeof(rmsk));
-
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to derive PMK");
- return -1;
- }
-
- wpa_hexdump(MSG_DEBUG, "PASN: FILS: PMKID", pasn->fils.erp_pmkid,
- PMKID_LEN);
-
- wpa_printf(MSG_DEBUG, "PASN: FILS: ERP processing succeeded");
-
- wpa_pasn_pmksa_cache_add(wpa_s->wpa, pasn->pmk,
- pasn->pmk_len, pasn->fils.erp_pmkid,
- pasn->bssid, pasn->akmp);
-
- pasn->fils.completed = true;
- return 0;
-}
-
-#endif /* CONFIG_FILS */
-
-
-static struct wpabuf * wpas_pasn_get_wrapped_data(struct wpa_supplicant *wpa_s)
-{
- struct wpas_pasn *pasn = &wpa_s->pasn;
-
- if (pasn->using_pmksa)
- return NULL;
-
- switch (pasn->akmp) {
- case WPA_KEY_MGMT_PASN:
- /* no wrapped data */
- return NULL;
- case WPA_KEY_MGMT_SAE:
-#ifdef CONFIG_SAE
- if (pasn->trans_seq == 0)
- return wpas_pasn_wd_sae_commit(wpa_s);
- if (pasn->trans_seq == 2)
- return wpas_pasn_wd_sae_confirm(wpa_s);
-#endif /* CONFIG_SAE */
- wpa_printf(MSG_ERROR,
- "PASN: SAE: Cannot derive wrapped data");
- return NULL;
- case WPA_KEY_MGMT_FILS_SHA256:
- case WPA_KEY_MGMT_FILS_SHA384:
-#ifdef CONFIG_FILS
- return wpas_pasn_wd_fils_auth(wpa_s);
-#endif /* CONFIG_FILS */
- case WPA_KEY_MGMT_FT_PSK:
- case WPA_KEY_MGMT_FT_IEEE8021X:
- case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
- /*
- * Wrapped data with these AKMs is optional and is only needed
- * for further validation of FT security parameters. For now do
- * not use them.
- */
- return NULL;
- default:
- wpa_printf(MSG_ERROR,
- "PASN: TODO: Wrapped data for akmp=0x%x",
- pasn->akmp);
- return NULL;
- }
-}
-
-
-static u8 wpas_pasn_get_wrapped_data_format(struct wpas_pasn *pasn)
-{
- if (pasn->using_pmksa)
- return WPA_PASN_WRAPPED_DATA_NO;
-
- /* Note: Valid AKMP is expected to already be validated */
- switch (pasn->akmp) {
- case WPA_KEY_MGMT_SAE:
- return WPA_PASN_WRAPPED_DATA_SAE;
- case WPA_KEY_MGMT_FILS_SHA256:
- case WPA_KEY_MGMT_FILS_SHA384:
- return WPA_PASN_WRAPPED_DATA_FILS_SK;
- case WPA_KEY_MGMT_FT_PSK:
- case WPA_KEY_MGMT_FT_IEEE8021X:
- case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
- /*
- * Wrapped data with these AKMs is optional and is only needed
- * for further validation of FT security parameters. For now do
- * not use them.
- */
- return WPA_PASN_WRAPPED_DATA_NO;
- case WPA_KEY_MGMT_PASN:
- default:
- return WPA_PASN_WRAPPED_DATA_NO;
- }
-}
-
-
-static struct wpabuf * wpas_pasn_build_auth_1(struct wpa_supplicant *wpa_s,
- const struct wpabuf *comeback)
-{
- struct wpas_pasn *pasn = &wpa_s->pasn;
- struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL;
- const u8 *pmkid;
- u8 wrapped_data;
- int ret;
- u16 capab;
-
- wpa_printf(MSG_DEBUG, "PASN: Building frame 1");
-
- if (pasn->trans_seq)
- return NULL;
-
- buf = wpabuf_alloc(1500);
- if (!buf)
- goto fail;
-
- /* Get public key */
- pubkey = crypto_ecdh_get_pubkey(pasn->ecdh, 0);
- pubkey = wpabuf_zeropad(pubkey, crypto_ecdh_prime_len(pasn->ecdh));
- if (!pubkey) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to get pubkey");
- goto fail;
- }
-
- wrapped_data = wpas_pasn_get_wrapped_data_format(pasn);
-
- wpa_pasn_build_auth_header(buf, pasn->bssid,
- pasn->own_addr, pasn->bssid,
- pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
-
- pmkid = NULL;
- if (wpa_key_mgmt_ft(pasn->akmp)) {
- ret = wpa_pasn_ft_derive_pmk_r1(wpa_s->wpa, pasn->akmp,
- pasn->bssid,
- pasn->pmk_r1,
- &pasn->pmk_r1_len,
- pasn->pmk_r1_name);
- if (ret) {
- wpa_printf(MSG_DEBUG,
- "PASN: FT: Failed to derive keys");
- goto fail;
- }
-
- pmkid = pasn->pmk_r1_name;
- } else if (wrapped_data != WPA_PASN_WRAPPED_DATA_NO) {
- struct rsn_pmksa_cache_entry *pmksa;
-
- pmksa = wpa_sm_pmksa_cache_get(wpa_s->wpa, pasn->bssid,
- NULL, NULL, pasn->akmp);
- if (pmksa)
- pmkid = pmksa->pmkid;
-
- /*
- * Note: Even when PMKSA is available, also add wrapped data as
- * it is possible that the PMKID is no longer valid at the AP.
- */
- wrapped_data_buf = wpas_pasn_get_wrapped_data(wpa_s);
- }
-
- if (wpa_pasn_add_rsne(buf, pmkid, pasn->akmp, pasn->cipher) < 0)
- goto fail;
-
- if (!wrapped_data_buf)
- wrapped_data = WPA_PASN_WRAPPED_DATA_NO;
-
- wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data,
- pubkey, true, comeback, -1);
-
- if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0)
- goto fail;
-
- /* Add own RNSXE */
- capab = 0;
- capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
- if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA)
- capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF);
- if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_STA)
- capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
- if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA)
- capab |= BIT(WLAN_RSNX_CAPAB_PROT_RANGE_NEG);
- wpa_pasn_add_rsnxe(buf, capab);
-
- ret = pasn_auth_frame_hash(pasn->akmp, pasn->cipher,
- wpabuf_head_u8(buf) + IEEE80211_HDRLEN,
- wpabuf_len(buf) - IEEE80211_HDRLEN,
- pasn->hash);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash");
- goto fail;
- }
-
- pasn->trans_seq++;
-
- wpabuf_free(wrapped_data_buf);
- wpabuf_free(pubkey);
-
- wpa_printf(MSG_DEBUG, "PASN: Frame 1: Success");
- return buf;
-fail:
- pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- wpabuf_free(wrapped_data_buf);
- wpabuf_free(pubkey);
- wpabuf_free(buf);
- return NULL;
-}
-
-
-static struct wpabuf * wpas_pasn_build_auth_3(struct wpa_supplicant *wpa_s)
-{
- struct wpas_pasn *pasn = &wpa_s->pasn;
- struct wpabuf *buf, *wrapped_data_buf = NULL;
- u8 mic[WPA_PASN_MAX_MIC_LEN];
- u8 mic_len, data_len;
- const u8 *data;
- u8 *ptr;
- u8 wrapped_data;
- int ret;
-
- wpa_printf(MSG_DEBUG, "PASN: Building frame 3");
-
- if (pasn->trans_seq != 2)
- return NULL;
-
- buf = wpabuf_alloc(1500);
- if (!buf)
- goto fail;
-
- wrapped_data = wpas_pasn_get_wrapped_data_format(pasn);
-
- wpa_pasn_build_auth_header(buf, pasn->bssid,
- pasn->own_addr, pasn->bssid,
- pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
-
- wrapped_data_buf = wpas_pasn_get_wrapped_data(wpa_s);
-
- if (!wrapped_data_buf)
- wrapped_data = WPA_PASN_WRAPPED_DATA_NO;
-
- wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data,
- NULL, false, NULL, -1);
-
- if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0)
- goto fail;
- wpabuf_free(wrapped_data_buf);
- wrapped_data_buf = NULL;
-
- /* Add the MIC */
- mic_len = pasn_mic_len(pasn->akmp, 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);
-
- data = wpabuf_head_u8(buf) + IEEE80211_HDRLEN;
- data_len = wpabuf_len(buf) - IEEE80211_HDRLEN;
-
- ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
- pasn->own_addr, pasn->bssid,
- pasn->hash, mic_len * 2, data, data_len, mic);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: frame 3: Failed MIC calculation");
- goto fail;
- }
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (wpa_s->conf->pasn_corrupt_mic) {
- wpa_printf(MSG_DEBUG, "PASN: frame 3: Corrupt MIC");
- mic[0] = ~mic[0];
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- os_memcpy(ptr, mic, mic_len);
-
- pasn->trans_seq++;
-
- wpa_printf(MSG_DEBUG, "PASN: frame 3: Success");
- return buf;
-fail:
- pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- wpabuf_free(wrapped_data_buf);
- wpabuf_free(buf);
- return NULL;
+ eapol_sm_notify_config(pasn->eapol, &ssid->eap, &eapol_conf);
}
static void wpas_pasn_reset(struct wpa_supplicant *wpa_s)
{
- struct wpas_pasn *pasn = &wpa_s->pasn;
-
- wpa_printf(MSG_DEBUG, "PASN: Reset");
-
- crypto_ecdh_deinit(pasn->ecdh);
- pasn->ecdh = NULL;
+ struct pasn_data *pasn = &wpa_s->pasn;
wpas_pasn_cancel_auth_work(wpa_s);
wpa_s->pasn_auth_work = NULL;
-
eloop_cancel_timeout(wpas_pasn_auth_work_timeout, wpa_s, NULL);
- pasn->akmp = 0;
- pasn->cipher = 0;
- pasn->group = 0;
- pasn->trans_seq = 0;
- pasn->pmk_len = 0;
- pasn->using_pmksa = false;
-
- forced_memzero(pasn->pmk, sizeof(pasn->pmk));
- forced_memzero(&pasn->ptk, sizeof(pasn->ptk));
- forced_memzero(&pasn->hash, sizeof(pasn->hash));
-
- wpabuf_free(pasn->beacon_rsne_rsnxe);
- pasn->beacon_rsne_rsnxe = NULL;
-
- wpabuf_free(pasn->comeback);
- pasn->comeback = NULL;
- pasn->comeback_after = 0;
-
-#ifdef CONFIG_SAE
- sae_clear_data(&pasn->sae);
-#endif /* CONFIG_SAE */
-
-#ifdef CONFIG_FILS
- os_memset(&pasn->fils, 0, sizeof(pasn->fils));
-#endif /* CONFIG_FILS*/
-
-#ifdef CONFIG_IEEE80211R
- forced_memzero(pasn->pmk_r1, sizeof(pasn->pmk_r1));
- pasn->pmk_r1_len = 0;
- os_memset(pasn->pmk_r1_name, 0, sizeof(pasn->pmk_r1_name));
-#endif /* CONFIG_IEEE80211R */
- pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-}
-
-
-static int wpas_pasn_set_pmk(struct wpa_supplicant *wpa_s,
- struct wpa_ie_data *rsn_data,
- struct wpa_pasn_params_data *pasn_data,
- struct wpabuf *wrapped_data)
-{
- static const u8 pasn_default_pmk[] = {'P', 'M', 'K', 'z'};
- struct wpas_pasn *pasn = &wpa_s->pasn;
-
- os_memset(pasn->pmk, 0, sizeof(pasn->pmk));
- pasn->pmk_len = 0;
-
- if (pasn->akmp == WPA_KEY_MGMT_PASN) {
- wpa_printf(MSG_DEBUG, "PASN: Using default PMK");
-
- pasn->pmk_len = WPA_PASN_PMK_LEN;
- os_memcpy(pasn->pmk, pasn_default_pmk,
- sizeof(pasn_default_pmk));
- return 0;
- }
-
- if (wpa_key_mgmt_ft(pasn->akmp)) {
-#ifdef CONFIG_IEEE80211R
- wpa_printf(MSG_DEBUG, "PASN: FT: Using PMK-R1");
- pasn->pmk_len = pasn->pmk_r1_len;
- os_memcpy(pasn->pmk, pasn->pmk_r1, pasn->pmk_r1_len);
- pasn->using_pmksa = true;
- return 0;
-#else /* CONFIG_IEEE80211R */
- wpa_printf(MSG_DEBUG, "PASN: FT: Not supported");
- return -1;
-#endif /* CONFIG_IEEE80211R */
- }
-
- if (rsn_data->num_pmkid) {
- struct rsn_pmksa_cache_entry *pmksa;
-
- pmksa = wpa_sm_pmksa_cache_get(wpa_s->wpa, pasn->bssid,
- rsn_data->pmkid, NULL,
- pasn->akmp);
- if (pmksa) {
- wpa_printf(MSG_DEBUG, "PASN: Using PMKSA");
-
- pasn->pmk_len = pmksa->pmk_len;
- os_memcpy(pasn->pmk, pmksa->pmk, pmksa->pmk_len);
- pasn->using_pmksa = true;
-
- return 0;
- }
- }
-
-#ifdef CONFIG_SAE
- if (pasn->akmp == WPA_KEY_MGMT_SAE) {
- int ret;
-
- ret = wpas_pasn_wd_sae_rx(wpa_s, wrapped_data);
- if (ret) {
- wpa_printf(MSG_DEBUG,
- "PASN: Failed processing SAE wrapped data");
- pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- return -1;
- }
-
- wpa_printf(MSG_DEBUG, "PASN: Success deriving PMK with SAE");
- pasn->pmk_len = PMK_LEN;
- os_memcpy(pasn->pmk, pasn->sae.pmk, PMK_LEN);
-
- wpa_pasn_pmksa_cache_add(wpa_s->wpa, pasn->pmk,
- pasn->pmk_len, pasn->sae.pmkid,
- pasn->bssid, pasn->akmp);
- return 0;
- }
-#endif /* CONFIG_SAE */
-
-#ifdef CONFIG_FILS
- if (pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
- pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
- int ret;
-
- ret = wpas_pasn_wd_fils_rx(wpa_s, wrapped_data);
- if (ret) {
- wpa_printf(MSG_DEBUG,
- "PASN: Failed processing FILS wrapped data");
- pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- return -1;
- }
-
- return 0;
- }
-#endif /* CONFIG_FILS */
-
- /* TODO: Derive PMK based on wrapped data */
- wpa_printf(MSG_DEBUG, "PASN: Missing implementation to derive PMK");
- pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- return -1;
-}
-
-
-static int wpas_pasn_start(struct wpa_supplicant *wpa_s, const u8 *own_addr,
- const u8 *bssid, int akmp, int cipher, u16 group,
- int freq, const u8 *beacon_rsne, u8 beacon_rsne_len,
- const u8 *beacon_rsnxe, u8 beacon_rsnxe_len,
- int network_id, struct wpabuf *comeback)
-{
- struct wpas_pasn *pasn = &wpa_s->pasn;
- struct wpa_ssid *ssid = NULL;
- struct wpabuf *frame;
- int ret;
- bool derive_kdk;
-
- /* TODO: Currently support only ECC groups */
- if (!dragonfly_suitable_group(group, 1)) {
- wpa_printf(MSG_DEBUG,
- "PASN: Reject unsuitable group %u", group);
- return -1;
- }
-
- ssid = wpa_config_get_network(wpa_s->conf, network_id);
-
- switch (akmp) {
- case WPA_KEY_MGMT_PASN:
- break;
-#ifdef CONFIG_SAE
- case WPA_KEY_MGMT_SAE:
- if (!ssid) {
- wpa_printf(MSG_DEBUG,
- "PASN: No network profile found for SAE");
- return -1;
- }
-
- if (!ieee802_11_rsnx_capab(beacon_rsnxe,
- WLAN_RSNX_CAPAB_SAE_H2E)) {
- wpa_printf(MSG_DEBUG,
- "PASN: AP does not support SAE H2E");
- return -1;
- }
-
- if (wpas_pasn_sae_setup_pt(wpa_s, ssid, group) < 0) {
- wpa_printf(MSG_DEBUG,
- "PASN: Failed to derive PT");
- return -1;
- }
-
- pasn->sae.state = SAE_NOTHING;
- pasn->sae.send_confirm = 0;
- pasn->ssid = ssid;
- break;
-#endif /* CONFIG_SAE */
-#ifdef CONFIG_FILS
- case WPA_KEY_MGMT_FILS_SHA256:
- case WPA_KEY_MGMT_FILS_SHA384:
- pasn->ssid = ssid;
- break;
-#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:
- break;
-#endif /* CONFIG_IEEE80211R */
- default:
- wpa_printf(MSG_ERROR, "PASN: Unsupported AKMP=0x%x", akmp);
- return -1;
- }
-
- pasn->ecdh = crypto_ecdh_init(group);
- if (!pasn->ecdh) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to init ECDH");
- goto fail;
- }
-
- pasn->beacon_rsne_rsnxe = wpabuf_alloc(beacon_rsne_len +
- beacon_rsnxe_len);
- if (!pasn->beacon_rsne_rsnxe) {
- wpa_printf(MSG_DEBUG, "PASN: Failed storing beacon RSNE/RSNXE");
- goto fail;
- }
-
- wpabuf_put_data(pasn->beacon_rsne_rsnxe, beacon_rsne, beacon_rsne_len);
- if (beacon_rsnxe && beacon_rsnxe_len)
- wpabuf_put_data(pasn->beacon_rsne_rsnxe, beacon_rsnxe,
- beacon_rsnxe_len);
-
- pasn->akmp = akmp;
- pasn->cipher = cipher;
- pasn->group = group;
- pasn->freq = freq;
-
- derive_kdk = (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA) &&
- ieee802_11_rsnx_capab(beacon_rsnxe,
- WLAN_RSNX_CAPAB_SECURE_LTF);
-#ifdef CONFIG_TESTING_OPTIONS
- if (!derive_kdk)
- derive_kdk = wpa_s->conf->force_kdk_derivation;
-#endif /* CONFIG_TESTING_OPTIONS */
- if (derive_kdk)
- pasn->kdk_len = WPA_KDK_MAX_LEN;
- else
- pasn->kdk_len = 0;
- wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len);
-
- if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA) &&
- ieee802_11_rsnx_capab(beacon_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF))
- pasn->secure_ltf = true;
- else
- pasn->secure_ltf = false;
-
- os_memcpy(pasn->own_addr, own_addr, ETH_ALEN);
- os_memcpy(pasn->bssid, bssid, ETH_ALEN);
-
- wpa_printf(MSG_DEBUG,
- "PASN: Init: " MACSTR " akmp=0x%x, cipher=0x%x, group=%u",
- MAC2STR(pasn->bssid), pasn->akmp, pasn->cipher,
- pasn->group);
-
- frame = wpas_pasn_build_auth_1(wpa_s, comeback);
- if (!frame) {
- wpa_printf(MSG_DEBUG, "PASN: Failed building 1st auth frame");
- goto fail;
- }
-
- ret = wpa_drv_send_mlme(wpa_s, wpabuf_head(frame), wpabuf_len(frame), 0,
- pasn->freq, 1000);
-
- wpabuf_free(frame);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed sending 1st auth frame");
- goto fail;
- }
-
- eloop_register_timeout(2, 0, wpas_pasn_auth_work_timeout, wpa_s, NULL);
- return 0;
-
-fail:
- return -1;
+ wpa_pasn_reset(pasn);
}
static struct wpa_bss * wpas_pasn_allowed(struct wpa_supplicant *wpa_s,
- const u8 *bssid, int akmp, int cipher)
+ const u8 *peer_addr, int akmp,
+ int cipher)
{
struct wpa_bss *bss;
const u8 *rsne;
struct wpa_ie_data rsne_data;
int ret;
- if (os_memcmp(wpa_s->bssid, bssid, ETH_ALEN) == 0) {
+ if (os_memcmp(wpa_s->bssid, peer_addr, ETH_ALEN) == 0) {
wpa_printf(MSG_DEBUG,
"PASN: Not doing authentication with current BSS");
return NULL;
}
- bss = wpa_bss_get_bssid(wpa_s, bssid);
+ bss = wpa_bss_get_bssid(wpa_s, peer_addr);
if (!bss) {
wpa_printf(MSG_DEBUG, "PASN: BSS not found");
return NULL;
@@ -1456,8 +505,14 @@
{
struct wpa_supplicant *wpa_s = work->wpa_s;
struct wpa_pasn_auth_work *awork = work->ctx;
+ struct pasn_data *pasn = &wpa_s->pasn;
+ struct wpa_ssid *ssid;
struct wpa_bss *bss;
const u8 *rsne, *rsnxe;
+ const u8 *indic;
+ u16 fils_info;
+ u16 capab = 0;
+ bool derive_kdk;
int ret;
wpa_printf(MSG_DEBUG, "PASN: auth_start_cb: deinit=%d", deinit);
@@ -1478,7 +533,7 @@
* authentication is not allowed, e.g., a connection with the AP was
* established.
*/
- bss = wpas_pasn_allowed(wpa_s, awork->bssid, awork->akmp,
+ bss = wpas_pasn_allowed(wpa_s, awork->peer_addr, awork->akmp,
awork->cipher);
if (!bss) {
wpa_printf(MSG_DEBUG, "PASN: auth_start_cb: Not allowed");
@@ -1493,16 +548,115 @@
rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
- ret = wpas_pasn_start(wpa_s, awork->own_addr, awork->bssid, awork->akmp,
- awork->cipher, awork->group, bss->freq,
- rsne, *(rsne + 1) + 2,
+ derive_kdk = (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA) &&
+ ieee802_11_rsnx_capab(rsnxe,
+ WLAN_RSNX_CAPAB_SECURE_LTF);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (!derive_kdk)
+ derive_kdk = wpa_s->conf->force_kdk_derivation;
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (derive_kdk)
+ pasn->kdk_len = WPA_KDK_MAX_LEN;
+ else
+ pasn->kdk_len = 0;
+ wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len);
+
+ if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA) &&
+ ieee802_11_rsnx_capab(rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF))
+ pasn->secure_ltf = true;
+ else
+ pasn->secure_ltf = false;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ pasn->corrupt_mic = wpa_s->conf->pasn_corrupt_mic;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+ if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA)
+ capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF);
+ if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_STA)
+ capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
+ if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA)
+ capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
+ pasn->rsnxe_capab = capab;
+ pasn->send_mgmt = wpas_pasn_send_mlme;
+
+ ssid = wpa_config_get_network(wpa_s->conf, awork->network_id);
+
+#ifdef CONFIG_SAE
+ if (awork->akmp == WPA_KEY_MGMT_SAE) {
+ if (!ssid) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: No network profile found for SAE");
+ goto fail;
+ }
+ pasn->pt = wpas_pasn_sae_derive_pt(ssid, awork->group);
+ if (!pasn->pt) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to derive PT");
+ goto fail;
+ }
+ pasn->network_id = ssid->id;
+ }
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_FILS
+ /* Prepare needed information for wpas_pasn_wd_fils_auth(). */
+ if (awork->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
+ awork->akmp == WPA_KEY_MGMT_FILS_SHA384) {
+ indic = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION);
+ if (!ssid) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: No network block");
+ } else if (!indic || indic[1] < 2) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Missing FILS Indication IE");
+ } else {
+ fils_info = WPA_GET_LE16(indic + 2);
+ if ((fils_info & BIT(9)) && ssid) {
+ pasn->eapol = wpa_s->eapol;
+ pasn->network_id = ssid->id;
+ wpas_pasn_initiate_eapol(pasn, ssid);
+ pasn->fils_eapol = true;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "PASN: FILS auth without PFS not supported");
+ }
+ }
+ pasn->fast_reauth = wpa_s->conf->fast_reauth;
+ }
+#endif /* CONFIG_FILS */
+
+ pasn->cb_ctx = wpa_s;
+ pasn->pmksa = wpa_sm_get_pmksa_cache(wpa_s->wpa);
+
+ if (wpa_key_mgmt_ft(awork->akmp)) {
+#ifdef CONFIG_IEEE80211R
+ ret = wpa_pasn_ft_derive_pmk_r1(wpa_s->wpa, awork->akmp,
+ awork->peer_addr,
+ pasn->pmk_r1,
+ &pasn->pmk_r1_len,
+ pasn->pmk_r1_name);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: FT: Failed to derive keys");
+ goto fail;
+ }
+#else /* CONFIG_IEEE80211R */
+ goto fail;
+#endif /* CONFIG_IEEE80211R */
+ }
+
+
+ ret = wpas_pasn_start(pasn, awork->own_addr, awork->peer_addr,
+ awork->peer_addr, awork->akmp, awork->cipher,
+ awork->group, bss->freq, rsne, *(rsne + 1) + 2,
rsnxe, rsnxe ? *(rsnxe + 1) + 2 : 0,
- awork->network_id, awork->comeback);
+ awork->comeback);
if (ret) {
wpa_printf(MSG_DEBUG,
"PASN: Failed to start PASN authentication");
goto fail;
}
+ eloop_register_timeout(2, 0, wpas_pasn_auth_work_timeout, wpa_s, NULL);
/* comeback token is no longer needed at this stage */
wpabuf_free(awork->comeback);
@@ -1518,7 +672,7 @@
int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s,
- const u8 *own_addr, const u8 *bssid,
+ const u8 *own_addr, const u8 *peer_addr,
int akmp, int cipher, u16 group, int network_id,
const u8 *comeback, size_t comeback_len)
{
@@ -1526,7 +680,7 @@
struct wpa_bss *bss;
wpa_printf(MSG_DEBUG, "PASN: Start: " MACSTR " akmp=0x%x, cipher=0x%x",
- MAC2STR(bssid), akmp, cipher);
+ MAC2STR(peer_addr), akmp, cipher);
/*
* TODO: Consider modifying the offchannel logic to handle additional
@@ -1550,7 +704,7 @@
return -1;
}
- bss = wpas_pasn_allowed(wpa_s, bssid, akmp, cipher);
+ bss = wpas_pasn_allowed(wpa_s, peer_addr, akmp, cipher);
if (!bss)
return -1;
@@ -1561,7 +715,7 @@
return -1;
os_memcpy(awork->own_addr, own_addr, ETH_ALEN);
- os_memcpy(awork->bssid, bssid, ETH_ALEN);
+ os_memcpy(awork->peer_addr, peer_addr, ETH_ALEN);
awork->akmp = akmp;
awork->cipher = cipher;
awork->group = group;
@@ -1588,14 +742,14 @@
void wpas_pasn_auth_stop(struct wpa_supplicant *wpa_s)
{
- struct wpas_pasn *pasn = &wpa_s->pasn;
+ struct pasn_data *pasn = &wpa_s->pasn;
if (!wpa_s->pasn.ecdh)
return;
wpa_printf(MSG_DEBUG, "PASN: Stopping authentication");
- wpas_pasn_auth_status(wpa_s, pasn->bssid, pasn->akmp, pasn->cipher,
+ wpas_pasn_auth_status(wpa_s, pasn->peer_addr, pasn->akmp, pasn->cipher,
pasn->status, pasn->comeback,
pasn->comeback_after);
@@ -1604,23 +758,22 @@
static int wpas_pasn_immediate_retry(struct wpa_supplicant *wpa_s,
- struct wpas_pasn *pasn,
+ struct pasn_data *pasn,
struct wpa_pasn_params_data *params)
{
int akmp = pasn->akmp;
int cipher = pasn->cipher;
u16 group = pasn->group;
u8 own_addr[ETH_ALEN];
- u8 bssid[ETH_ALEN];
- int network_id = pasn->ssid ? pasn->ssid->id : 0;
+ u8 peer_addr[ETH_ALEN];
wpa_printf(MSG_DEBUG, "PASN: Immediate retry");
os_memcpy(own_addr, pasn->own_addr, ETH_ALEN);
- os_memcpy(bssid, pasn->bssid, ETH_ALEN);
+ os_memcpy(peer_addr, pasn->peer_addr, ETH_ALEN);
wpas_pasn_reset(wpa_s);
- return wpas_pasn_auth_start(wpa_s, own_addr, bssid, akmp, cipher, group,
- network_id,
+ return wpas_pasn_auth_start(wpa_s, own_addr, peer_addr, akmp, cipher,
+ group, pasn->network_id,
params->comeback, params->comeback_len);
}
@@ -1628,279 +781,51 @@
static void wpas_pasn_deauth_cb(struct ptksa_cache_entry *entry)
{
struct wpa_supplicant *wpa_s = entry->ctx;
+ u8 own_addr[ETH_ALEN];
+ u8 peer_addr[ETH_ALEN];
- wpas_pasn_deauthenticate(wpa_s, entry->own_addr, entry->addr);
+ /* Use a copy of the addresses from the entry to avoid issues with the
+ * entry getting freed during deauthentication processing. */
+ os_memcpy(own_addr, entry->own_addr, ETH_ALEN);
+ os_memcpy(peer_addr, entry->addr, ETH_ALEN);
+ wpas_pasn_deauthenticate(wpa_s, own_addr, peer_addr);
}
int wpas_pasn_auth_rx(struct wpa_supplicant *wpa_s,
const struct ieee80211_mgmt *mgmt, size_t len)
{
- struct wpas_pasn *pasn = &wpa_s->pasn;
- struct ieee802_11_elems elems;
- struct wpa_ie_data rsn_data;
- struct wpa_pasn_params_data pasn_params;
- struct wpabuf *wrapped_data = NULL, *secret = NULL, *frame = NULL;
- u8 mic[WPA_PASN_MAX_MIC_LEN], out_mic[WPA_PASN_MAX_MIC_LEN];
- u8 mic_len;
- u16 status;
- int ret, inc_y;
- u16 fc = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
- (WLAN_FC_STYPE_AUTH << 4));
+ struct pasn_data *pasn = &wpa_s->pasn;
+ struct wpa_pasn_params_data pasn_data;
+ int ret;
- if (!wpa_s->pasn_auth_work || !mgmt ||
- len < offsetof(struct ieee80211_mgmt, u.auth.variable))
+ if (!wpa_s->pasn_auth_work)
return -2;
- /* Not an Authentication frame; do nothing */
- if ((mgmt->frame_control & fc) != fc)
- return -2;
+ pasn->cb_ctx = wpa_s;
+ ret = wpa_pasn_auth_rx(pasn, (const u8 *) mgmt, len, &pasn_data);
+ if (ret == 0) {
+ ptksa_cache_add(wpa_s->ptksa, pasn->own_addr, pasn->peer_addr,
+ pasn->cipher, dot11RSNAConfigPMKLifetime,
+ &pasn->ptk,
+ wpa_s->pasn_params ? wpas_pasn_deauth_cb : NULL,
+ wpa_s->pasn_params ? wpa_s : NULL, pasn->akmp);
- /* Not our frame; do nothing */
- if (os_memcmp(mgmt->da, pasn->own_addr, ETH_ALEN) != 0 ||
- os_memcmp(mgmt->sa, pasn->bssid, ETH_ALEN) != 0 ||
- os_memcmp(mgmt->bssid, pasn->bssid, ETH_ALEN) != 0)
- return -2;
-
- /* Not PASN; do nothing */
- if (mgmt->u.auth.auth_alg != host_to_le16(WLAN_AUTH_PASN))
- return -2;
-
- if (mgmt->u.auth.auth_transaction !=
- host_to_le16(pasn->trans_seq + 1)) {
- wpa_printf(MSG_DEBUG,
- "PASN: RX: Invalid transaction sequence: (%u != %u)",
- le_to_host16(mgmt->u.auth.auth_transaction),
- pasn->trans_seq + 1);
- return -1;
+ if (pasn->pmksa_entry)
+ wpa_sm_set_cur_pmksa(wpa_s->wpa, pasn->pmksa_entry);
}
- status = le_to_host16(mgmt->u.auth.status_code);
-
- if (status != WLAN_STATUS_SUCCESS &&
- status != WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
- wpa_printf(MSG_DEBUG,
- "PASN: Authentication rejected - status=%u", status);
- goto fail;
- }
-
- 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(pasn->akmp, pasn->cipher);
- if (status == WLAN_STATUS_SUCCESS) {
- 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 be modifying the
- * received message buffer. */
- os_memset((u8 *) elems.mic, 0, mic_len);
- }
- }
-
- if (!elems.pasn_params || !elems.pasn_params_len) {
- wpa_printf(MSG_DEBUG,
- "PASN: Missing PASN Parameters IE");
- goto fail;
- }
-
- ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
- elems.pasn_params_len + 3,
- true, &pasn_params);
- if (ret) {
- wpa_printf(MSG_DEBUG,
- "PASN: Failed validation PASN of Parameters IE");
- goto fail;
- }
-
- if (status == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
- wpa_printf(MSG_DEBUG,
- "PASN: Authentication temporarily rejected");
-
- if (pasn_params.comeback && pasn_params.comeback_len) {
- wpa_printf(MSG_DEBUG,
- "PASN: Comeback token available. After=%u",
- pasn_params.after);
-
- if (!pasn_params.after)
- return wpas_pasn_immediate_retry(wpa_s, pasn,
- &pasn_params);
-
- pasn->comeback = wpabuf_alloc_copy(
- pasn_params.comeback, pasn_params.comeback_len);
- if (pasn->comeback)
- pasn->comeback_after = pasn_params.after;
- }
-
- pasn->status = status;
- goto fail;
- }
-
- ret = wpa_parse_wpa_ie(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
- &rsn_data);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed parsing RNSE");
- goto fail;
- }
-
- ret = wpa_pasn_validate_rsne(&rsn_data);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE");
- goto fail;
- }
-
- if (pasn->akmp != rsn_data.key_mgmt ||
- pasn->cipher != rsn_data.pairwise_cipher) {
- wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher");
- goto fail;
- }
-
- if (pasn->group != pasn_params.group) {
- wpa_printf(MSG_DEBUG, "PASN: Mismatch in group");
- goto fail;
- }
-
- if (!pasn_params.pubkey || !pasn_params.pubkey_len) {
- wpa_printf(MSG_DEBUG, "PASN: Invalid public key");
- goto fail;
- }
-
- if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_UNCOMPRESSED) {
- inc_y = 1;
- } else if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_0 ||
- pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_1) {
- inc_y = 0;
- } else {
- wpa_printf(MSG_DEBUG,
- "PASN: Invalid first octet in pubkey=0x%x",
- pasn_params.pubkey[0]);
- goto fail;
- }
-
- secret = crypto_ecdh_set_peerkey(pasn->ecdh, inc_y,
- pasn_params.pubkey + 1,
- pasn_params.pubkey_len - 1);
-
- if (!secret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to derive shared secret");
- 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;
- }
- }
-
- ret = wpas_pasn_set_pmk(wpa_s, &rsn_data, &pasn_params, wrapped_data);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to set PMK");
- goto fail;
- }
-
- ret = pasn_pmk_to_ptk(pasn->pmk, pasn->pmk_len,
- pasn->own_addr, pasn->bssid,
- wpabuf_head(secret), wpabuf_len(secret),
- &pasn->ptk, pasn->akmp, pasn->cipher,
- pasn->kdk_len);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK");
- goto fail;
- }
-
- if (pasn->secure_ltf) {
- ret = wpa_ltf_keyseed(&pasn->ptk, pasn->akmp, pasn->cipher);
- if (ret) {
- wpa_printf(MSG_DEBUG,
- "PASN: Failed to derive LTF keyseed");
- goto fail;
- }
- }
-
- wpabuf_free(wrapped_data);
- wrapped_data = NULL;
- wpabuf_free(secret);
- secret = NULL;
-
- /* Verify the MIC */
- ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
- pasn->bssid, pasn->own_addr,
- wpabuf_head(pasn->beacon_rsne_rsnxe),
- wpabuf_len(pasn->beacon_rsne_rsnxe),
- (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;
- }
-
- pasn->trans_seq++;
-
- wpa_printf(MSG_DEBUG, "PASN: Success verifying Authentication frame");
-
- frame = wpas_pasn_build_auth_3(wpa_s);
- if (!frame) {
- wpa_printf(MSG_DEBUG, "PASN: Failed building 3rd auth frame");
- goto fail;
- }
-
- ret = wpa_drv_send_mlme(wpa_s, wpabuf_head(frame), wpabuf_len(frame), 0,
- pasn->freq, 100);
- wpabuf_free(frame);
- if (ret) {
- wpa_printf(MSG_DEBUG, "PASN: Failed sending 3st auth frame");
- goto fail;
- }
-
- wpa_printf(MSG_DEBUG, "PASN: Success sending last frame. Store PTK");
-
- ptksa_cache_add(wpa_s->ptksa, pasn->own_addr, pasn->bssid,
- pasn->cipher, dot11RSNAConfigPMKLifetime, &pasn->ptk,
- wpa_s->pasn_params ? wpas_pasn_deauth_cb : NULL,
- wpa_s->pasn_params ? wpa_s : NULL);
-
forced_memzero(&pasn->ptk, sizeof(pasn->ptk));
- pasn->status = WLAN_STATUS_SUCCESS;
- return 0;
-fail:
- wpa_printf(MSG_DEBUG, "PASN: Failed RX processing - terminating");
- wpabuf_free(wrapped_data);
- wpabuf_free(secret);
+ if (ret == -1) {
+ wpas_pasn_auth_stop(wpa_s);
+ wpas_pasn_auth_work_done(wpa_s, PASN_STATUS_FAILURE);
+ }
- /*
- * TODO: In case of an error the standard allows to silently drop
- * the frame and terminate the authentication exchange. However, better
- * reply to the AP with an error status.
- */
- if (status == WLAN_STATUS_SUCCESS)
- pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- else
- pasn->status = status;
+ if (ret == 1)
+ ret = wpas_pasn_immediate_retry(wpa_s, pasn, &pasn_data);
- wpas_pasn_auth_stop(wpa_s);
-
- wpas_pasn_auth_work_done(wpa_s, PASN_STATUS_FAILURE);
- return -1;
+ return ret;
}
@@ -1961,12 +886,8 @@
const u8 *data, size_t data_len, u8 acked)
{
- struct wpas_pasn *pasn = &wpa_s->pasn;
- const struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) data;
- u16 fc = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
- (WLAN_FC_STYPE_AUTH << 4));
-
- wpa_printf(MSG_DEBUG, "PASN: auth_tx_status: acked=%u", acked);
+ struct pasn_data *pasn = &wpa_s->pasn;
+ int ret;
if (!wpa_s->pasn_auth_work) {
wpa_printf(MSG_DEBUG,
@@ -1974,87 +895,46 @@
return -1;
}
- if (!mgmt ||
- data_len < offsetof(struct ieee80211_mgmt, u.auth.variable))
- return -1;
+ ret = wpa_pasn_auth_tx_status(pasn, data, data_len, acked);
+ if (ret != 1)
+ return ret;
- /* Not an authentication frame; do nothing */
- if ((mgmt->frame_control & fc) != fc)
- return -1;
-
- /* Not our frame; do nothing */
- if (os_memcmp(mgmt->da, pasn->bssid, ETH_ALEN) ||
- os_memcmp(mgmt->sa, pasn->own_addr, ETH_ALEN) ||
- os_memcmp(mgmt->bssid, pasn->bssid, ETH_ALEN))
- return -1;
-
- /* Not PASN; do nothing */
- if (mgmt->u.auth.auth_alg != host_to_le16(WLAN_AUTH_PASN))
- return -1;
-
- if (mgmt->u.auth.auth_transaction != host_to_le16(pasn->trans_seq)) {
- wpa_printf(MSG_ERROR,
- "PASN: Invalid transaction sequence: (%u != %u)",
- pasn->trans_seq,
- le_to_host16(mgmt->u.auth.auth_transaction));
+ if (!wpa_s->pasn_params) {
+ wpas_pasn_auth_stop(wpa_s);
return 0;
}
- wpa_printf(MSG_ERROR,
- "PASN: auth with trans_seq=%u, acked=%u", pasn->trans_seq,
- acked);
-
- /*
- * Even if the frame was not acked, do not treat this is an error, and
- * try to complete the flow, relying on the PASN timeout callback to
- * clean up.
- */
- if (pasn->trans_seq == 3) {
- wpa_printf(MSG_DEBUG, "PASN: auth complete with: " MACSTR,
- MAC2STR(pasn->bssid));
- /*
- * Either frame was not ACKed or it was ACKed but the trans_seq
- * != 1, i.e., not expecting an RX frame, so we are done.
- */
- if (!wpa_s->pasn_params) {
- wpas_pasn_auth_stop(wpa_s);
- return 0;
- }
-
- wpas_pasn_set_keys_from_cache(wpa_s, pasn->own_addr,
- pasn->bssid, pasn->cipher,
- pasn->akmp);
- wpas_pasn_auth_stop(wpa_s);
-
- wpas_pasn_auth_work_done(wpa_s, PASN_STATUS_SUCCESS);
- }
+ wpas_pasn_set_keys_from_cache(wpa_s, pasn->own_addr, pasn->peer_addr,
+ pasn->cipher, pasn->akmp);
+ wpas_pasn_auth_stop(wpa_s);
+ wpas_pasn_auth_work_done(wpa_s, PASN_STATUS_SUCCESS);
return 0;
}
int wpas_pasn_deauthenticate(struct wpa_supplicant *wpa_s, const u8 *own_addr,
- const u8 *bssid)
+ const u8 *peer_addr)
{
struct wpa_bss *bss;
struct wpabuf *buf;
struct ieee80211_mgmt *deauth;
int ret;
- if (os_memcmp(wpa_s->bssid, bssid, ETH_ALEN) == 0) {
+ if (os_memcmp(wpa_s->bssid, peer_addr, ETH_ALEN) == 0) {
wpa_printf(MSG_DEBUG,
"PASN: Cannot deauthenticate from current BSS");
return -1;
}
- wpa_drv_set_secure_ranging_ctx(wpa_s, own_addr, bssid, 0, 0, NULL, 0,
- NULL, 1);
+ wpa_drv_set_secure_ranging_ctx(wpa_s, own_addr, peer_addr, 0, 0, NULL,
+ 0, NULL, 1);
wpa_printf(MSG_DEBUG, "PASN: deauth: Flushing all PTKSA entries for "
- MACSTR, MAC2STR(bssid));
- ptksa_cache_flush(wpa_s->ptksa, bssid, WPA_CIPHER_NONE);
+ MACSTR, MAC2STR(peer_addr));
+ ptksa_cache_flush(wpa_s->ptksa, peer_addr, WPA_CIPHER_NONE);
- bss = wpa_bss_get_bssid(wpa_s, bssid);
+ bss = wpa_bss_get_bssid(wpa_s, peer_addr);
if (!bss) {
wpa_printf(MSG_DEBUG, "PASN: deauth: BSS not found");
return -1;
@@ -2072,9 +952,9 @@
deauth->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
(WLAN_FC_STYPE_DEAUTH << 4));
- os_memcpy(deauth->da, bssid, ETH_ALEN);
+ os_memcpy(deauth->da, peer_addr, ETH_ALEN);
os_memcpy(deauth->sa, own_addr, ETH_ALEN);
- os_memcpy(deauth->bssid, bssid, ETH_ALEN);
+ os_memcpy(deauth->bssid, peer_addr, ETH_ALEN);
deauth->u.deauth.reason_code =
host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
diff --git a/wpa_supplicant/preauth_test.c b/wpa_supplicant/preauth_test.c
index 3ae99da..3aa6675 100644
--- a/wpa_supplicant/preauth_test.c
+++ b/wpa_supplicant/preauth_test.c
@@ -131,7 +131,7 @@
}
-static int wpa_supplicant_set_key(void *wpa_s, enum wpa_alg alg,
+static int wpa_supplicant_set_key(void *wpa_s, int link_id, enum wpa_alg alg,
const u8 *addr, int key_idx, int set_tx,
const u8 *seq, size_t seq_len,
const u8 *key, size_t key_len,
@@ -318,7 +318,7 @@
}
os_memset(&wpa_s, 0, sizeof(wpa_s));
- wpa_s.conf = wpa_config_read(argv[1], NULL);
+ wpa_s.conf = wpa_config_read(argv[1], NULL, false);
if (wpa_s.conf == NULL) {
printf("Failed to parse configuration file '%s'.\n", argv[1]);
return -1;
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index faa97e0..88d7e6e 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -299,6 +299,8 @@
return -1;
}
+ wpa_s->wps_scan_done = false;
+
return 0;
}
@@ -476,25 +478,28 @@
wpa_s->p2p_invite_go_freq > 0) {
if (wpa_s->p2p_invite_go_freq == 2 ||
wpa_s->p2p_invite_go_freq == 5) {
- wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO preferred band %d GHz during invitation",
- wpa_s->p2p_invite_go_freq);
enum hostapd_hw_mode mode;
- if (wpa_s->hw.modes == NULL)
+
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Scan only GO preferred band %d GHz during invitation",
+ wpa_s->p2p_invite_go_freq);
+
+ if (!wpa_s->hw.modes)
return;
mode = wpa_s->p2p_invite_go_freq == 5 ?
- HOSTAPD_MODE_IEEE80211A :
- HOSTAPD_MODE_IEEE80211G;
+ HOSTAPD_MODE_IEEE80211A :
+ HOSTAPD_MODE_IEEE80211G;
if (wpa_s->p2p_in_invitation <= 2)
wpa_add_scan_freqs_list(wpa_s, mode,
params, false,
- true);
- if (params->freqs == NULL ||
- (params->freqs && params->freqs[0] == 0))
+ false, true);
+ if (!params->freqs || params->freqs[0] == 0)
wpa_add_scan_freqs_list(wpa_s, mode,
params, false,
- false);
+ false, false);
} else {
- wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO preferred frequency %d MHz during invitation",
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Scan only GO preferred frequency %d MHz during invitation",
wpa_s->p2p_invite_go_freq);
params->freqs = os_calloc(2, sizeof(int));
if (params->freqs)
@@ -757,7 +762,8 @@
int wpa_add_scan_freqs_list(struct wpa_supplicant *wpa_s,
enum hostapd_hw_mode band,
- struct wpa_driver_scan_params *params, bool is_6ghz,
+ struct wpa_driver_scan_params *params,
+ bool is_6ghz, bool only_6ghz_psc,
bool exclude_radar)
{
/* Include only supported channels for the specified band */
@@ -766,7 +772,7 @@
int *freqs, i;
mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, band, is_6ghz);
- if (!mode)
+ if (!mode || !mode->num_channels)
return -1;
if (params->freqs) {
@@ -783,8 +789,14 @@
for (i = 0; i < mode->num_channels; i++) {
if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)
continue;
- if (exclude_radar && (mode->channels[i].flag & HOSTAPD_CHAN_RADAR))
+ if (exclude_radar &&
+ (mode->channels[i].flag & HOSTAPD_CHAN_RADAR))
continue;
+
+ if (is_6ghz && only_6ghz_psc &&
+ !is_6ghz_psc_frequency(mode->channels[i].freq))
+ continue;
+
params->freqs[num_chans++] = mode->channels[i].freq;
}
params->freqs[num_chans] = 0;
@@ -803,13 +815,13 @@
if (wpa_s->setband_mask & WPA_SETBAND_5G)
wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, params,
- false, false);
+ false, false, false);
if (wpa_s->setband_mask & WPA_SETBAND_2G)
wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, params,
- false, false);
+ false, false, false);
if (wpa_s->setband_mask & WPA_SETBAND_6G)
wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, params,
- true, false);
+ true, false, false);
}
@@ -1318,7 +1330,8 @@
params.freqs = os_calloc(num + 1, sizeof(int));
if (params.freqs) {
- num = get_shared_radio_freqs(wpa_s, params.freqs, num);
+ num = get_shared_radio_freqs(wpa_s, params.freqs, num,
+ false);
if (num > 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the "
"current operating channels since "
@@ -1408,7 +1421,13 @@
params.freqs = os_calloc(num + 1, sizeof(int));
if (params.freqs) {
- num = get_shared_radio_freqs(wpa_s, params.freqs, num);
+ /*
+ * Exclude the operating frequency of the current
+ * interface since we're looking to transition off of
+ * it.
+ */
+ num = get_shared_radio_freqs(wpa_s, params.freqs, num,
+ true);
if (num > 0 && num == wpa_s->num_multichan_concurrent) {
wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the current operating channels since all channels are already used");
} else {
@@ -1418,18 +1437,9 @@
}
}
- if (!params.freqs &&
- (wpa_s->p2p_in_invitation || wpa_s->p2p_in_provisioning) &&
- !is_p2p_allow_6ghz(wpa_s->global->p2p) &&
- is_6ghz_supported(wpa_s)) {
- /* Exclude 6 GHz channels from the full scan for P2P connection
- * since the 6 GHz band is disabled for P2P uses. */
- wpa_printf(MSG_DEBUG,
- "P2P: 6 GHz disabled - update the scan frequency list");
- wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, ¶ms, false, false);
- wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, ¶ms, false, false);
- wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211AD, ¶ms, false, false);
- }
+ if (!params.freqs && is_6ghz_supported(wpa_s) &&
+ (wpa_s->p2p_in_invitation || wpa_s->p2p_in_provisioning))
+ wpas_p2p_scan_freqs(wpa_s, ¶ms, true);
#endif /* CONFIG_P2P */
ret = wpa_supplicant_trigger_scan(wpa_s, scan_params);
@@ -2418,6 +2428,8 @@
{ -1, 780000 } /* SNR > 37 */
};
+/* EHT needs to be enabled in order to achieve MCS12 and MCS13 rates. */
+#define EHT_MCS 12
static const struct minsnr_bitrate_entry he20_table[] = {
{ 0, 0 },
@@ -2433,7 +2445,9 @@
{ 31, 114700 }, /* HE20 MCS9 */
{ 34, 129000 }, /* HE20 MCS10 */
{ 36, 143400 }, /* HE20 MCS11 */
- { -1, 143400 } /* SNR > 29 */
+ { 39, 154900 }, /* EHT20 MCS12 */
+ { 42, 172100 }, /* EHT20 MCS13 */
+ { -1, 172100 } /* SNR > 42 */
};
static const struct minsnr_bitrate_entry he40_table[] = {
@@ -2450,7 +2464,9 @@
{ 34, 229400 }, /* HE40 MCS9 */
{ 37, 258100 }, /* HE40 MCS10 */
{ 39, 286800 }, /* HE40 MCS11 */
- { -1, 286800 } /* SNR > 34 */
+ { 42, 309500 }, /* EHT40 MCS12 */
+ { 45, 344100 }, /* EHT40 MCS13 */
+ { -1, 344100 } /* SNR > 45 */
};
static const struct minsnr_bitrate_entry he80_table[] = {
@@ -2467,7 +2483,9 @@
{ 37, 480400 }, /* HE80 MCS9 */
{ 40, 540400 }, /* HE80 MCS10 */
{ 42, 600500 }, /* HE80 MCS11 */
- { -1, 600500 } /* SNR > 37 */
+ { 45, 648500 }, /* EHT80 MCS12 */
+ { 48, 720600 }, /* EHT80 MCS13 */
+ { -1, 720600 } /* SNR > 48 */
};
@@ -2485,9 +2503,31 @@
{ 40, 960800 }, /* HE160 MCS9 */
{ 43, 1080900 }, /* HE160 MCS10 */
{ 45, 1201000 }, /* HE160 MCS11 */
- { -1, 1201000 } /* SNR > 37 */
+ { 48, 1297100 }, /* EHT160 MCS12 */
+ { 51, 1441200 }, /* EHT160 MCS13 */
+ { -1, 1441200 } /* SNR > 51 */
};
+/* See IEEE P802.11be/D2.0, Table 36-86: EHT-MCSs for 4x996-tone RU, NSS,u = 1
+ */
+static const struct minsnr_bitrate_entry eht320_table[] = {
+ { 0, 0 },
+ { 14, 144100 }, /* EHT320 MCS0 */
+ { 17, 288200 }, /* EHT320 MCS1 */
+ { 21, 432400 }, /* EHT320 MCS2 */
+ { 23, 576500 }, /* EHT320 MCS3 */
+ { 27, 864700 }, /* EHT320 MCS4 */
+ { 30, 1152900 }, /* EHT320 MCS5 */
+ { 32, 1297100 }, /* EHT320 MCS6 */
+ { 37, 1441200 }, /* EHT320 MCS7 */
+ { 41, 1729400 }, /* EHT320 MCS8 */
+ { 43, 1921500 }, /* EHT320 MCS9 */
+ { 46, 2161800 }, /* EHT320 MCS10 */
+ { 48, 2401900 }, /* EHT320 MCS11 */
+ { 51, 2594100 }, /* EHT320 MCS12 */
+ { 54, 2882400 }, /* EHT320 MCS13 */
+ { -1, 2882400 } /* SNR > 54 */
+};
static unsigned int interpolate_rate(int snr, int snr0, int snr1,
int rate0, int rate1)
@@ -2539,17 +2579,18 @@
}
-static unsigned int max_he_rate(const struct minsnr_bitrate_entry table[],
- int snr)
+static unsigned int max_he_eht_rate(const struct minsnr_bitrate_entry table[],
+ int snr, bool eht)
{
const struct minsnr_bitrate_entry *prev, *entry = table;
- while (entry->minsnr != -1 && snr >= entry->minsnr)
+ while (entry->minsnr != -1 && snr >= entry->minsnr &&
+ (eht || entry - table <= EHT_MCS))
entry++;
if (entry == table)
return 0;
prev = entry - 1;
- if (entry->minsnr == -1)
+ if (entry->minsnr == -1 || (!eht && entry - table > EHT_MCS))
return prev->bitrate;
return interpolate_rate(snr, prev->minsnr, entry->minsnr,
prev->bitrate, entry->bitrate);
@@ -2687,8 +2728,11 @@
if (hw_mode && hw_mode->he_capab[IEEE80211_MODE_INFRA].he_supported) {
/* Use +2 to assume HE is always faster than HT/VHT */
struct ieee80211_he_capabilities *he;
+ struct ieee80211_eht_capabilities *eht;
struct he_capabilities *own_he;
- u8 cw;
+ u8 cw, boost = 2;
+ const u8 *eht_ie;
+ bool is_eht = false;
ie = get_ie_ext(ies, ies_len, WLAN_EID_EXT_HE_CAPABILITIES);
if (!ie || (ie[1] < 1 + IEEE80211_HE_CAPAB_MIN_LEN))
@@ -2696,7 +2740,18 @@
he = (struct ieee80211_he_capabilities *) &ie[3];
own_he = &hw_mode->he_capab[IEEE80211_MODE_INFRA];
- tmp = max_he_rate(he20_table, snr) + 2;
+ /* Use +3 to assume EHT is always faster than HE */
+ if (hw_mode->eht_capab[IEEE80211_MODE_INFRA].eht_supported) {
+ eht_ie = get_ie_ext(ies, ies_len,
+ WLAN_EID_EXT_EHT_CAPABILITIES);
+ if (eht_ie &&
+ (eht_ie[1] >= 1 + IEEE80211_EHT_CAPAB_MIN_LEN)) {
+ is_eht = true;
+ boost = 3;
+ }
+ }
+
+ tmp = max_he_eht_rate(he20_table, snr, is_eht) + boost;
if (tmp > est)
est = tmp;
@@ -2705,14 +2760,14 @@
if (cw &
(IS_2P4GHZ(freq) ? HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G :
HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G)) {
- tmp = max_he_rate(he40_table, snr) + 2;
+ tmp = max_he_eht_rate(he40_table, snr, is_eht) + boost;
if (tmp > est)
est = tmp;
}
if (!IS_2P4GHZ(freq) &&
(cw & HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G)) {
- tmp = max_he_rate(he80_table, snr) + 2;
+ tmp = max_he_eht_rate(he80_table, snr, is_eht) + boost;
if (tmp > est)
est = tmp;
}
@@ -2720,7 +2775,20 @@
if (!IS_2P4GHZ(freq) &&
(cw & (HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G))) {
- tmp = max_he_rate(he160_table, snr) + 2;
+ tmp = max_he_eht_rate(he160_table, snr, is_eht) + boost;
+ if (tmp > est)
+ est = tmp;
+ }
+
+ if (!is_eht)
+ return est;
+
+ eht = (struct ieee80211_eht_capabilities *) &eht_ie[3];
+
+ if (is_6ghz_freq(freq) &&
+ (eht->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
+ EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK)) {
+ tmp = max_he_eht_rate(eht320_table, snr, true);
if (tmp > est)
est = tmp;
}
diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h
index 52d2696..30f4395 100644
--- a/wpa_supplicant/scan.h
+++ b/wpa_supplicant/scan.h
@@ -92,7 +92,7 @@
int wpa_add_scan_freqs_list(struct wpa_supplicant *wpa_s,
enum hostapd_hw_mode band,
struct wpa_driver_scan_params *params,
- bool is_6ghz,
+ bool is_6ghz, bool only_6ghz_psc,
bool exclude_radar);
#endif /* SCAN_H */
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index a847e2f..406e357 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -10,6 +10,7 @@
#include "common.h"
#include "utils/eloop.h"
+#include "utils/ext_password.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/ocv.h"
@@ -53,7 +54,7 @@
}
-static int sme_set_sae_group(struct wpa_supplicant *wpa_s)
+static int sme_set_sae_group(struct wpa_supplicant *wpa_s, bool external)
{
int *groups = wpa_s->conf->sae_groups;
int default_groups[] = { 19, 20, 21, 0 };
@@ -72,7 +73,8 @@
if (sae_set_group(&wpa_s->sme.sae, group) == 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected SAE group %d",
wpa_s->sme.sae.group);
- wpa_s->sme.sae.akmp = wpa_s->key_mgmt;
+ wpa_s->sme.sae.akmp = external ?
+ wpa_s->sme.ext_auth_key_mgmt : wpa_s->key_mgmt;
return 0;
}
wpa_s->sme.sae_group_index++;
@@ -84,17 +86,22 @@
static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
- const u8 *bssid, int external,
+ const u8 *bssid,
+ const u8 *mld_addr,
+ int external,
int reuse, int *ret_use_pt,
bool *ret_use_pk)
{
struct wpabuf *buf;
size_t len;
- const char *password;
+ char *password = NULL;
struct wpa_bss *bss;
int use_pt = 0;
bool use_pk = false;
u8 rsnxe_capa = 0;
+ int key_mgmt = external ? wpa_s->sme.ext_auth_key_mgmt :
+ wpa_s->key_mgmt;
+ const u8 *addr = mld_addr ? mld_addr : bssid;
if (ret_use_pt)
*ret_use_pt = 0;
@@ -106,7 +113,7 @@
wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override");
buf = wpabuf_alloc(4 + wpabuf_len(wpa_s->sae_commit_override));
if (!buf)
- return NULL;
+ goto fail;
if (!external) {
wpabuf_put_le16(buf, 1); /* Transaction seq# */
wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
@@ -116,25 +123,58 @@
}
#endif /* CONFIG_TESTING_OPTIONS */
- password = ssid->sae_password;
- if (!password)
- password = ssid->passphrase;
+ if (ssid->sae_password) {
+ password = os_strdup(ssid->sae_password);
+ if (!password) {
+ wpa_dbg(wpa_s, MSG_INFO,
+ "SAE: Failed to allocate password");
+ goto fail;
+ }
+ }
+ if (!password && ssid->passphrase) {
+ password = os_strdup(ssid->passphrase);
+ if (!password) {
+ wpa_dbg(wpa_s, MSG_INFO,
+ "SAE: Failed to allocate password");
+ goto fail;
+ }
+ }
+ if (!password && ssid->ext_psk) {
+ struct wpabuf *pw = ext_password_get(wpa_s->ext_pw,
+ ssid->ext_psk);
+
+ if (!pw) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "SAE: No password found from external storage");
+ goto fail;
+ }
+
+ password = os_malloc(wpabuf_len(pw) + 1);
+ if (!password) {
+ wpa_dbg(wpa_s, MSG_INFO,
+ "SAE: Failed to allocate password");
+ goto fail;
+ }
+ os_memcpy(password, wpabuf_head(pw), wpabuf_len(pw));
+ password[wpabuf_len(pw)] = '\0';
+ ext_password_free(pw);
+ }
if (!password) {
wpa_printf(MSG_DEBUG, "SAE: No password available");
- return NULL;
+ goto fail;
}
if (reuse && wpa_s->sme.sae.tmp &&
- os_memcmp(bssid, wpa_s->sme.sae.tmp->bssid, ETH_ALEN) == 0) {
+ os_memcmp(addr, wpa_s->sme.sae.tmp->bssid, ETH_ALEN) == 0) {
wpa_printf(MSG_DEBUG,
"SAE: Reuse previously generated PWE on a retry with the same AP");
use_pt = wpa_s->sme.sae.h2e;
use_pk = wpa_s->sme.sae.pk;
goto reuse_data;
}
- if (sme_set_sae_group(wpa_s) < 0) {
+ if (sme_set_sae_group(wpa_s, external) < 0) {
wpa_printf(MSG_DEBUG, "SAE: Failed to select group");
- return NULL;
+ goto fail;
}
bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
@@ -152,10 +192,11 @@
rsnxe_capa = rsnxe[2];
}
- if (ssid->sae_password_id && wpa_s->conf->sae_pwe != 3)
+ if (ssid->sae_password_id &&
+ wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
use_pt = 1;
- if (wpa_key_mgmt_sae_ext_key(wpa_s->key_mgmt) &&
- wpa_s->conf->sae_pwe != 3)
+ if (wpa_key_mgmt_sae_ext_key(key_mgmt) &&
+ wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
use_pt = 1;
#ifdef CONFIG_SAE_PK
if ((rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_PK)) &&
@@ -171,43 +212,45 @@
if (ssid->sae_pk == SAE_PK_MODE_ONLY && !use_pk) {
wpa_printf(MSG_DEBUG,
"SAE: Cannot use PK with the selected AP");
- return NULL;
+ goto fail;
}
#endif /* CONFIG_SAE_PK */
- if (use_pt || wpa_s->conf->sae_pwe == 1 || wpa_s->conf->sae_pwe == 2) {
+ if (use_pt || wpa_s->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ wpa_s->conf->sae_pwe == SAE_PWE_BOTH) {
use_pt = !!(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_H2E));
- if ((wpa_s->conf->sae_pwe == 1 || ssid->sae_password_id ||
- wpa_key_mgmt_sae_ext_key(wpa_s->key_mgmt)) &&
- wpa_s->conf->sae_pwe != 3 &&
+ if ((wpa_s->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ ssid->sae_password_id ||
+ wpa_key_mgmt_sae_ext_key(key_mgmt)) &&
+ wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
!use_pt) {
wpa_printf(MSG_DEBUG,
"SAE: Cannot use H2E with the selected AP");
- return NULL;
+ goto fail;
}
}
if (use_pt &&
sae_prepare_commit_pt(&wpa_s->sme.sae, ssid->pt,
- wpa_s->own_addr, bssid,
+ wpa_s->own_addr, addr,
wpa_s->sme.sae_rejected_groups, NULL) < 0)
- return NULL;
+ goto fail;
if (!use_pt &&
sae_prepare_commit(wpa_s->own_addr, bssid,
(u8 *) password, os_strlen(password),
&wpa_s->sme.sae) < 0) {
wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
- return NULL;
+ goto fail;
}
if (wpa_s->sme.sae.tmp) {
- os_memcpy(wpa_s->sme.sae.tmp->bssid, bssid, ETH_ALEN);
+ os_memcpy(wpa_s->sme.sae.tmp->bssid, addr, ETH_ALEN);
if (use_pt && use_pk)
wpa_s->sme.sae.pk = 1;
#ifdef CONFIG_SAE_PK
os_memcpy(wpa_s->sme.sae.tmp->own_addr, wpa_s->own_addr,
ETH_ALEN);
- os_memcpy(wpa_s->sme.sae.tmp->peer_addr, bssid, ETH_ALEN);
+ os_memcpy(wpa_s->sme.sae.tmp->peer_addr, addr, ETH_ALEN);
sae_pk_set_password(&wpa_s->sme.sae, password);
#endif /* CONFIG_SAE_PK */
}
@@ -218,7 +261,7 @@
len += 4 + os_strlen(ssid->sae_password_id);
buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + len);
if (buf == NULL)
- return NULL;
+ goto fail;
if (!external) {
wpabuf_put_le16(buf, 1); /* Transaction seq# */
if (use_pk)
@@ -231,14 +274,19 @@
if (sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token,
ssid->sae_password_id) < 0) {
wpabuf_free(buf);
- return NULL;
+ goto fail;
}
if (ret_use_pt)
*ret_use_pt = use_pt;
if (ret_use_pk)
*ret_use_pk = use_pk;
+ str_clear_free(password);
return buf;
+
+fail:
+ str_clear_free(password);
+ return NULL;
}
@@ -325,6 +373,188 @@
}
+static bool wpas_ml_element(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+ struct wpabuf *mlbuf;
+ const u8 *rnr_ie, *pos;
+ u8 ml_ie_len, rnr_ie_len;
+ const struct ieee80211_eht_ml *eht_ml;
+ const struct eht_ml_basic_common_info *ml_basic_common_info;
+ u8 i;
+ const u16 control =
+ host_to_le16(MULTI_LINK_CONTROL_TYPE_BASIC |
+ BASIC_MULTI_LINK_CTRL_PRES_LINK_ID |
+ BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT |
+ BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA);
+ bool ret = false;
+
+ if (!(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO))
+ return false;
+
+ mlbuf = wpa_bss_defrag_mle(bss, MULTI_LINK_CONTROL_TYPE_BASIC);
+ if (!mlbuf) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No ML element");
+ return false;
+ }
+
+ ml_ie_len = wpabuf_len(mlbuf);
+
+ /* control + common info len + MLD address + MLD link information */
+ if (ml_ie_len < 2 + 1 + ETH_ALEN + 1)
+ goto out;
+
+ eht_ml = wpabuf_head(mlbuf);
+ if ((eht_ml->ml_control & control) != control) {
+ wpa_printf(MSG_DEBUG, "MLD: Unexpected ML element control=0x%x",
+ eht_ml->ml_control);
+ goto out;
+ }
+
+ ml_basic_common_info =
+ (const struct eht_ml_basic_common_info *) eht_ml->variable;
+
+ /* common info length should be valid (self, mld_addr, link_id) */
+ if (ml_basic_common_info->len < 1 + ETH_ALEN + 1)
+ goto out;
+
+ /* get the MLD address and MLD link ID */
+ os_memcpy(wpa_s->ap_mld_addr, ml_basic_common_info->mld_addr,
+ ETH_ALEN);
+ wpa_s->mlo_assoc_link_id = ml_basic_common_info->variable[0] &
+ EHT_ML_LINK_ID_MSK;
+
+ os_memcpy(wpa_s->links[wpa_s->mlo_assoc_link_id].bssid, bss->bssid,
+ ETH_ALEN);
+ wpa_s->links[wpa_s->mlo_assoc_link_id].freq = bss->freq;
+
+ wpa_printf(MSG_DEBUG, "MLD: address=" MACSTR ", link ID=%u",
+ MAC2STR(wpa_s->ap_mld_addr), wpa_s->mlo_assoc_link_id);
+
+ wpa_s->valid_links = BIT(wpa_s->mlo_assoc_link_id);
+
+ rnr_ie = wpa_bss_get_ie(bss, WLAN_EID_REDUCED_NEIGHBOR_REPORT);
+ if (!rnr_ie) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No RNR element");
+ ret = true;
+ goto out;
+ }
+
+ rnr_ie_len = rnr_ie[1];
+ pos = rnr_ie + 2;
+
+ while (rnr_ie_len > sizeof(struct ieee80211_neighbor_ap_info)) {
+ const struct ieee80211_neighbor_ap_info *ap_info =
+ (const struct ieee80211_neighbor_ap_info *) pos;
+ const u8 *data = ap_info->data;
+ size_t len = sizeof(struct ieee80211_neighbor_ap_info) +
+ ap_info->tbtt_info_len;
+
+ wpa_printf(MSG_DEBUG, "MLD: op_class=%u, channel=%u",
+ ap_info->op_class, ap_info->channel);
+
+ if (len > rnr_ie_len)
+ break;
+
+ if (ap_info->tbtt_info_len < 16) {
+ rnr_ie_len -= len;
+ pos += len;
+ continue;
+ }
+
+ data += 13;
+
+ wpa_printf(MSG_DEBUG, "MLD: mld ID=%u, link ID=%u",
+ *data, *(data + 1) & 0xF);
+
+ if (*data) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Reported link not part of MLD");
+ } else {
+ struct wpa_bss *neigh_bss =
+ wpa_bss_get_bssid(wpa_s, ap_info->data + 1);
+ u8 link_id = *(data + 1) & 0xF;
+
+ if (neigh_bss) {
+ if (wpa_scan_res_match(wpa_s, 0, neigh_bss,
+ wpa_s->current_ssid,
+ 1, 0)) {
+ wpa_s->valid_links |= BIT(link_id);
+ os_memcpy(wpa_s->links[link_id].bssid,
+ ap_info->data + 1, ETH_ALEN);
+ wpa_s->links[link_id].freq =
+ neigh_bss->freq;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Neighbor doesn't match current SSID - skip link");
+ }
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Neighbor not found in scan");
+ }
+ }
+
+ rnr_ie_len -= len;
+ pos += len;
+ }
+
+ wpa_printf(MSG_DEBUG, "MLD: valid_links=0x%x", wpa_s->valid_links);
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (!(wpa_s->valid_links & BIT(i)))
+ continue;
+
+ wpa_printf(MSG_DEBUG, "MLD: link=%u, bssid=" MACSTR,
+ i, MAC2STR(wpa_s->links[i].bssid));
+ }
+
+ ret = true;
+out:
+ wpabuf_free(mlbuf);
+ return ret;
+}
+
+
+static void wpas_sme_ml_auth(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data,
+ int ie_offset)
+{
+ struct ieee802_11_elems elems;
+ const u8 *mld_addr;
+
+ if (!wpa_s->valid_links)
+ return;
+
+ if (ieee802_11_parse_elems(data->auth.ies + ie_offset,
+ data->auth.ies_len - ie_offset,
+ &elems, 0) != ParseOK) {
+ wpa_printf(MSG_DEBUG, "MLD: Failed parsing elements");
+ goto out;
+ }
+
+ if (!elems.basic_mle || !elems.basic_mle_len) {
+ wpa_printf(MSG_DEBUG, "MLD: No ML element in authentication");
+ goto out;
+ }
+
+ mld_addr = get_basic_mle_mld_addr(elems.basic_mle, elems.basic_mle_len);
+ if (!mld_addr)
+ goto out;
+
+ wpa_printf(MSG_DEBUG, "MLD: mld_address=" MACSTR, MAC2STR(mld_addr));
+
+ if (os_memcmp(wpa_s->ap_mld_addr, mld_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "MLD: Unexpected MLD address (expected "
+ MACSTR ")", MAC2STR(wpa_s->ap_mld_addr));
+ goto out;
+ }
+
+ return;
+out:
+ wpa_printf(MSG_DEBUG, "MLD: Authentication - clearing MLD state");
+ wpas_reset_mlo_info(wpa_s);
+}
+
+
static void sme_send_authentication(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid,
int start)
@@ -369,6 +599,13 @@
params.ssid_len = bss->ssid_len;
params.p2p = ssid->p2p_group;
+ if (wpas_ml_element(wpa_s, bss)) {
+ wpa_printf(MSG_DEBUG, "MLD: In authentication");
+ params.mld = true;
+ params.mld_link_id = wpa_s->mlo_assoc_link_id;
+ params.ap_mld_addr = wpa_s->ap_mld_addr;
+ }
+
if (wpa_s->sme.ssid_len != params.ssid_len ||
os_memcmp(wpa_s->sme.ssid, params.ssid, params.ssid_len) != 0)
wpa_s->sme.prev_bssid_set = 0;
@@ -413,8 +650,13 @@
#endif /* CONFIG_DPP */
} else if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 &&
wpa_key_mgmt_sae(ied.key_mgmt)) {
- wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg");
- params.auth_alg = WPA_AUTH_ALG_SAE;
+ if (wpas_is_sae_avoided(wpa_s, ssid, &ied)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "SAE enabled, but disallowing SAE auth_alg without PMF");
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg");
+ params.auth_alg = WPA_AUTH_ALG_SAE;
+ }
} else {
wpa_dbg(wpa_s, MSG_DEBUG,
"SAE enabled, but target BSS does not advertise SAE AKM for RSN");
@@ -449,7 +691,9 @@
if (wpa_key_mgmt_fils(ssid->key_mgmt))
cache_id = wpa_bss_get_fils_cache_id(bss);
#endif /* CONFIG_FILS */
- if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
+ if (pmksa_cache_set_current(wpa_s->wpa, NULL,
+ params.mld ? params.ap_mld_addr :
+ bss->bssid,
wpa_s->current_ssid,
try_opportunistic, cache_id,
0) == 0)
@@ -457,7 +701,8 @@
wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
wpa_s->sme.assoc_req_ie,
- &wpa_s->sme.assoc_req_ie_len)) {
+ &wpa_s->sme.assoc_req_ie_len,
+ false)) {
wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
"key management and encryption suites");
wpas_connect_work_done(wpa_s);
@@ -470,7 +715,8 @@
wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
wpa_s->sme.assoc_req_ie,
- &wpa_s->sme.assoc_req_ie_len)) {
+ &wpa_s->sme.assoc_req_ie_len,
+ false)) {
wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
"key management and encryption suites");
wpas_connect_work_done(wpa_s);
@@ -490,7 +736,8 @@
wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
wpa_s->sme.assoc_req_ie,
- &wpa_s->sme.assoc_req_ie_len)) {
+ &wpa_s->sme.assoc_req_ie_len,
+ false)) {
wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
"key management and encryption suites (no "
"scan results)");
@@ -572,7 +819,7 @@
if (wpa_s->sme.prev_bssid_set && wpa_s->sme.ft_used &&
os_memcmp(md, wpa_s->sme.mobility_domain, 2) == 0 &&
- wpa_sm_has_ptk(wpa_s->wpa)) {
+ wpa_sm_has_ft_keys(wpa_s->wpa, md)) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying to use FT "
"over-the-air");
params.auth_alg = WPA_AUTH_ALG_FT;
@@ -752,7 +999,10 @@
#ifdef CONFIG_SAE
if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE &&
- pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0,
+ pmksa_cache_set_current(wpa_s->wpa, NULL,
+ params.mld ? params.ap_mld_addr :
+ bss->bssid,
+ ssid, 0,
NULL,
wpa_key_mgmt_sae(wpa_s->key_mgmt) ?
wpa_s->key_mgmt :
@@ -767,7 +1017,10 @@
if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE) {
if (start)
resp = sme_auth_build_sae_commit(wpa_s, ssid,
- bss->bssid, 0,
+ bss->bssid,
+ params.mld ?
+ params.ap_mld_addr :
+ NULL, 0,
start == 2, NULL,
NULL);
else
@@ -846,7 +1099,9 @@
goto no_fils;
}
- if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
+ if (pmksa_cache_set_current(wpa_s->wpa, NULL,
+ params.mld ? params.ap_mld_addr :
+ bss->bssid,
ssid, 0,
wpa_bss_get_fils_cache_id(bss),
0) == 0)
@@ -899,7 +1154,7 @@
*/
if (wpa_s->num_multichan_concurrent < 2) {
int freq, num;
- num = get_shared_radio_freqs(wpa_s, &freq, 1);
+ num = get_shared_radio_freqs(wpa_s, &freq, 1, false);
if (num > 0 && freq > 0 && freq != params.freq) {
wpa_printf(MSG_DEBUG,
"Conflicting frequency found (%d != %d)",
@@ -984,6 +1239,7 @@
wpa_s->rsnxe_len = 0;
sme_send_authentication(wpa_s, cwork->bss, cwork->ssid, 1);
+ wpas_notify_auth_changed(wpa_s);
}
@@ -1081,8 +1337,8 @@
bool use_pk;
u16 status;
- resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, 1, 0, &use_pt,
- &use_pk);
+ resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, NULL,
+ 1, 0, &use_pt, &use_pk);
if (!resp) {
wpa_printf(MSG_DEBUG, "SAE: Failed to build SAE commit");
return -1;
@@ -1186,11 +1442,41 @@
}
+static bool is_sae_key_mgmt_suite(struct wpa_supplicant *wpa_s, u32 suite)
+{
+ /* suite is supposed to be the selector value in host byte order with
+ * the OUI in three most significant octets. However, the initial
+ * implementation swapped that byte order and did not work with drivers
+ * that followed the expected byte order. Keep a workaround here to
+ * match that initial implementation so that already deployed use cases
+ * remain functional. */
+ if (RSN_SELECTOR_GET(&suite) == RSN_AUTH_KEY_MGMT_SAE) {
+ /* Old drivers which follow initial implementation send SAE AKM
+ * for both SAE and FT-SAE connections. In that case, determine
+ * the actual AKM from wpa_s->key_mgmt. */
+ wpa_s->sme.ext_auth_key_mgmt = wpa_s->key_mgmt;
+ return true;
+ }
+
+ if (suite == RSN_AUTH_KEY_MGMT_SAE)
+ wpa_s->sme.ext_auth_key_mgmt = WPA_KEY_MGMT_SAE;
+ else if (suite == RSN_AUTH_KEY_MGMT_FT_SAE)
+ wpa_s->sme.ext_auth_key_mgmt = WPA_KEY_MGMT_FT_SAE;
+ else if (suite == RSN_AUTH_KEY_MGMT_SAE_EXT_KEY)
+ wpa_s->sme.ext_auth_key_mgmt = WPA_KEY_MGMT_SAE_EXT_KEY;
+ else if (suite == RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY)
+ wpa_s->sme.ext_auth_key_mgmt = WPA_KEY_MGMT_FT_SAE_EXT_KEY;
+ else
+ return false;
+
+ return true;
+}
+
+
void sme_external_auth_trigger(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
- if (RSN_SELECTOR_GET(&data->external_auth.key_mgmt_suite) !=
- RSN_AUTH_KEY_MGMT_SAE)
+ if (!is_sae_key_mgmt_suite(wpa_s, data->external_auth.key_mgmt_suite))
return;
if (data->external_auth.action == EXT_AUTH_START) {
@@ -1264,7 +1550,7 @@
static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
u16 status_code, const u8 *data, size_t len,
- int external, const u8 *sa)
+ int external, const u8 *sa, int *ie_offset)
{
int *groups;
@@ -1328,7 +1614,17 @@
}
token_len = elen - 1;
}
+
+ if (ie_offset)
+ *ie_offset = token_pos + token_len - data;
+
wpa_s->sme.sae_token = wpabuf_alloc_copy(token_pos, token_len);
+ if (!wpa_s->sme.sae_token) {
+ wpa_dbg(wpa_s, MSG_ERROR,
+ "SME: Failed to allocate SAE token");
+ return -1;
+ }
+
wpa_hexdump_buf(MSG_DEBUG, "SME: Requested anti-clogging token",
wpa_s->sme.sae_token);
if (!external)
@@ -1350,7 +1646,7 @@
int_array_add_unique(&wpa_s->sme.sae_rejected_groups,
wpa_s->sme.sae.group);
wpa_s->sme.sae_group_index++;
- if (sme_set_sae_group(wpa_s) < 0)
+ if (sme_set_sae_group(wpa_s, external) < 0)
return -1; /* no other groups enabled */
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Try next enabled SAE group");
if (!external)
@@ -1423,7 +1719,8 @@
res = sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL,
groups, status_code ==
WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
- status_code == WLAN_STATUS_SAE_PK);
+ status_code == WLAN_STATUS_SAE_PK,
+ ie_offset);
if (res == SAE_SILENTLY_DISCARD) {
wpa_printf(MSG_DEBUG,
"SAE: Drop commit message due to reflection attack");
@@ -1458,7 +1755,8 @@
wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE confirm");
if (wpa_s->sme.sae.state != SAE_CONFIRMED)
return -1;
- if (sae_check_confirm(&wpa_s->sme.sae, data, len) < 0)
+ if (sae_check_confirm(&wpa_s->sme.sae, data, len,
+ ie_offset) < 0)
return -1;
wpa_s->sme.sae.state = SAE_ACCEPTED;
sae_clear_temp_data(&wpa_s->sme.sae);
@@ -1530,7 +1828,7 @@
wpa_s, le_to_host16(header->u.auth.auth_transaction),
le_to_host16(header->u.auth.status_code),
header->u.auth.variable,
- len - auth_length, 1, header->sa);
+ len - auth_length, 1, header->sa, NULL);
if (res < 0) {
/* Notify failure to the driver */
sme_send_external_auth_status(
@@ -1554,6 +1852,7 @@
void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
{
struct wpa_ssid *ssid = wpa_s->current_ssid;
+ int ie_offset = 0;
if (ssid == NULL) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication event "
@@ -1567,7 +1866,9 @@
return;
}
- if (os_memcmp(wpa_s->pending_bssid, data->auth.peer, ETH_ALEN) != 0) {
+ if (os_memcmp(wpa_s->pending_bssid, data->auth.peer, ETH_ALEN) != 0 &&
+ !(wpa_s->valid_links &&
+ os_memcmp(wpa_s->ap_mld_addr, data->auth.peer, ETH_ALEN) == 0)) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication with "
"unexpected peer " MACSTR,
MAC2STR(data->auth.peer));
@@ -1585,10 +1886,13 @@
#ifdef CONFIG_SAE
if (data->auth.auth_type == WLAN_AUTH_SAE) {
+ const u8 *addr = wpa_s->pending_bssid;
int res;
+
res = sme_sae_auth(wpa_s, data->auth.auth_transaction,
data->auth.status_code, data->auth.ies,
- data->auth.ies_len, 0, data->auth.peer);
+ data->auth.ies_len, 0, data->auth.peer,
+ &ie_offset);
if (res < 0) {
wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
@@ -1597,7 +1901,10 @@
if (res != 1)
return;
- if (sme_sae_set_pmk(wpa_s, wpa_s->pending_bssid) < 0)
+ if (wpa_s->valid_links)
+ addr = wpa_s->ap_mld_addr;
+
+ if (sme_sae_set_pmk(wpa_s, addr) < 0)
return;
}
#endif /* CONFIG_SAE */
@@ -1725,6 +2032,11 @@
}
#endif /* CONFIG_FILS */
+ /* TODO: Support additional auth_type values as well */
+ if (data->auth.auth_type == WLAN_AUTH_OPEN ||
+ data->auth.auth_type == WLAN_AUTH_SAE)
+ wpas_sme_ml_auth(wpa_s, data, ie_offset);
+
sme_associate(wpa_s, ssid->mode, data->auth.peer,
data->auth.auth_type);
}
@@ -2014,6 +2326,7 @@
#ifdef CONFIG_HE_OVERRIDES
wpa_supplicant_apply_he_overrides(wpa_s, ssid, ¶ms);
#endif /* CONFIG_HE_OVERRIDES */
+ wpa_supplicant_apply_eht_overrides(wpa_s, ssid, ¶ms);
#ifdef CONFIG_IEEE80211R
if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies &&
get_ie(wpa_s->sme.ft_ies, wpa_s->sme.ft_ies_len,
@@ -2142,6 +2455,31 @@
else
params.uapsd = -1;
+ if (wpa_s->valid_links) {
+ unsigned int i;
+
+ wpa_printf(MSG_DEBUG,
+ "MLD: In association. assoc_link_id=%u, valid_links=0x%x",
+ wpa_s->mlo_assoc_link_id, wpa_s->valid_links);
+
+ params.mld_params.mld_addr = wpa_s->ap_mld_addr;
+ params.mld_params.valid_links = wpa_s->valid_links;
+ params.mld_params.assoc_link_id = wpa_s->mlo_assoc_link_id;
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (!(wpa_s->valid_links & BIT(i)))
+ continue;
+
+ params.mld_params.mld_links[i].bssid =
+ wpa_s->links[i].bssid;
+ params.mld_params.mld_links[i].freq =
+ wpa_s->links[i].freq;
+
+ wpa_printf(MSG_DEBUG, "MLD: id=%u, freq=%d, " MACSTR,
+ i, wpa_s->links[i].freq,
+ MAC2STR(wpa_s->links[i].bssid));
+ }
+ }
+
if (wpa_drv_associate(wpa_s, ¶ms) < 0) {
wpa_msg(wpa_s, MSG_INFO, "SME: Association request to the "
"driver failed");
@@ -2239,6 +2577,34 @@
}
#endif /* CONFIG_SAE */
+#ifdef CONFIG_DPP
+ if (wpa_s->current_ssid &&
+ wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_DPP &&
+ !data->assoc_reject.timed_out &&
+ data->assoc_reject.status_code == WLAN_STATUS_INVALID_PMKID) {
+ struct rsn_pmksa_cache_entry *pmksa;
+
+ pmksa = pmksa_cache_get_current(wpa_s->wpa);
+ if (pmksa) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "DPP: Drop PMKSA cache entry for the BSS due to invalid PMKID report");
+ wpa_sm_pmksa_cache_remove(wpa_s->wpa, pmksa);
+ }
+ wpa_sm_aborted_cached(wpa_s->wpa);
+ if (wpa_s->current_bss) {
+ struct wpa_bss *bss = wpa_s->current_bss;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "DPP: Try network introduction again");
+ wpas_connect_work_done(wpa_s);
+ wpa_supplicant_mark_disassoc(wpa_s);
+ wpa_supplicant_connect(wpa_s, bss, ssid);
+ return;
+ }
+ }
+#endif /* CONFIG_DPP */
+
/*
* For now, unconditionally terminate the previous authentication. In
* theory, this should not be needed, but mac80211 gets quite confused
diff --git a/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in b/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in
index da69a87..4eab335 100644
--- a/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in
+++ b/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in
@@ -1,5 +1,5 @@
[Unit]
-Description=WPA supplicant daemon (interface- and nl80211 driver-specific version)
+Description=WPA supplicant daemon (for interface %I using nl80211)
Requires=sys-subsystem-net-devices-%i.device
After=sys-subsystem-net-devices-%i.device
Before=network.target
diff --git a/wpa_supplicant/systemd/wpa_supplicant.service.arg.in b/wpa_supplicant/systemd/wpa_supplicant.service.arg.in
index 55d2b9c..b0d610f 100644
--- a/wpa_supplicant/systemd/wpa_supplicant.service.arg.in
+++ b/wpa_supplicant/systemd/wpa_supplicant.service.arg.in
@@ -1,5 +1,5 @@
[Unit]
-Description=WPA supplicant daemon (interface-specific version)
+Description=WPA supplicant daemon (for interface %I)
Requires=sys-subsystem-net-devices-%i.device
After=sys-subsystem-net-devices-%i.device
Before=network.target
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 5393f1c..72a119d 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -524,6 +524,11 @@
rep->mul_bssid->subelem_len = elen - 1;
os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1);
break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "WNM: Unsupported neighbor report subelement id %u",
+ id);
+ break;
}
}
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 197efe0..bfdc42d 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -503,7 +503,7 @@
"autoscan", "wps_nfc_dev_pw_id", "wps_nfc_dh_pubkey",
"wps_nfc_dh_privkey", "wps_nfc_dev_pw", "ext_password_backend",
"p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
- "sae_groups", "dtim_period", "beacon_int",
+ "sae_check_mfp", "sae_groups", "dtim_period", "beacon_int",
"ap_vendor_elements", "ignore_old_scan_res", "freq_list",
"scan_cur_freq", "scan_res_valid_for_connect",
"sched_scan_interval",
@@ -602,6 +602,7 @@
"go_venue_group", "go_venue_type",
"wps_nfc_dev_pw_id", "ext_password_backend",
"p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
+ "sae_check_mfp",
"dtim_period", "beacon_int", "ignore_old_scan_res",
"scan_cur_freq", "scan_res_valid_for_connect",
"sched_scan_interval",
@@ -1479,6 +1480,7 @@
#ifdef CONFIG_HE_OVERRIDES
"disable_he",
#endif /* CONFIG_HE_OVERRIDES */
+ "disable_eht",
"ap_max_inactivity", "dtim_period", "beacon_int",
#ifdef CONFIG_MACSEC
"macsec_policy",
@@ -3260,19 +3262,18 @@
#ifdef CONFIG_PASN
-static int wpa_cli_cmd_pasn_auth_start(struct wpa_ctrl *ctrl, int argc,
- char *argv[])
+static int wpa_cli_cmd_pasn_start(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
- return wpa_cli_cmd(ctrl, "PASN_AUTH_START", 4, argc, argv);
+ return wpa_cli_cmd(ctrl, "PASN_START", 4, argc, argv);
}
-static int wpa_cli_cmd_pasn_auth_stop(struct wpa_ctrl *ctrl, int argc,
- char *argv[])
+static int wpa_cli_cmd_pasn_stop(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
- return wpa_cli_cmd(ctrl, "PASN_AUTH_STOP", 0, argc, argv);
+ return wpa_cli_cmd(ctrl, "PASN_STOP", 0, argc, argv);
}
+
static int wpa_cli_cmd_ptksa_cache_list(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
@@ -4024,10 +4025,10 @@
{ "all_bss", wpa_cli_cmd_all_bss, NULL, cli_cmd_flag_none,
"= list all BSS entries (scan results)" },
#ifdef CONFIG_PASN
- { "pasn_auth_start", wpa_cli_cmd_pasn_auth_start, NULL,
+ { "pasn_start", wpa_cli_cmd_pasn_start, NULL,
cli_cmd_flag_none,
"bssid=<BSSID> akmp=<WPA key mgmt> cipher=<WPA cipher> group=<group> nid=<network id> = Start PASN authentication" },
- { "pasn_auth_stop", wpa_cli_cmd_pasn_auth_stop, NULL,
+ { "pasn_stop", wpa_cli_cmd_pasn_stop, NULL,
cli_cmd_flag_none,
"= Stop PASN authentication" },
{ "ptksa_cache_list", wpa_cli_cmd_ptksa_cache_list, NULL,
diff --git a/wpa_supplicant/wpa_passphrase.c b/wpa_supplicant/wpa_passphrase.c
index d9c07e6..cfab4f1 100644
--- a/wpa_supplicant/wpa_passphrase.c
+++ b/wpa_supplicant/wpa_passphrase.c
@@ -7,6 +7,7 @@
*/
#include "includes.h"
+#include <termios.h>
#include "common.h"
#include "crypto/sha1.h"
@@ -14,6 +15,7 @@
int main(int argc, char *argv[])
{
+ struct termios term;
unsigned char psk[32];
int i;
char *ssid, *passphrase, buf[64], *pos;
@@ -31,11 +33,28 @@
if (argc > 2) {
passphrase = argv[2];
} else {
+ bool ctrl_echo;
+
fprintf(stderr, "# reading passphrase from stdin\n");
+ if (tcgetattr(STDIN_FILENO, &term) < 0) {
+ perror("tcgetattr");
+ return 1;
+ }
+ ctrl_echo = term.c_lflag & ECHO;
+ term.c_lflag &= ~ECHO;
+ if (ctrl_echo && tcsetattr(STDIN_FILENO, TCSANOW, &term) < 0) {
+ perror("tcsetattr:error disabling echo");
+ return 1;
+ }
if (fgets(buf, sizeof(buf), stdin) == NULL) {
fprintf(stderr, "Failed to read passphrase\n");
return 1;
}
+ term.c_lflag |= ECHO;
+ if (ctrl_echo && tcsetattr(STDIN_FILENO, TCSANOW, &term) < 0) {
+ perror("tcsetattr:error enabling echo");
+ return 1;
+ }
buf[sizeof(buf) - 1] = '\0';
pos = buf;
while (*pos != '\0') {
diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
index ff1fb67..31a9af6 100644
--- a/wpa_supplicant/wpa_priv.c
+++ b/wpa_supplicant/wpa_priv.c
@@ -414,6 +414,7 @@
p.key = params->key_len ? params->key : NULL;
p.key_len = params->key_len;
p.key_flag = params->key_flag;
+ p.link_id = -1;
res = iface->driver->set_key(iface->drv_priv, &p);
wpa_printf(MSG_DEBUG, "drv->set_key: res=%d", res);
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 7d896f4..521ff90 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -143,7 +143,7 @@
continue;
set = 1;
- wpa_drv_set_key(wpa_s, WPA_ALG_WEP, NULL,
+ wpa_drv_set_key(wpa_s, -1, WPA_ALG_WEP, NULL,
i, i == ssid->wep_tx_keyidx, NULL, 0,
ssid->wep_key[i], ssid->wep_key_len[i],
i == ssid->wep_tx_keyidx ?
@@ -207,7 +207,7 @@
/* TODO: should actually remember the previously used seq#, both for TX
* and RX from each STA.. */
- ret = wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen,
+ ret = wpa_drv_set_key(wpa_s, -1, alg, NULL, 0, 1, seq, 6, key, keylen,
KEY_FLAG_GROUP_RX_TX_DEFAULT);
os_memset(key, 0, sizeof(key));
return ret;
@@ -404,6 +404,7 @@
#ifdef CONFIG_WEP
int i;
#endif /* CONFIG_WEP */
+ struct wpa_sm_mlo mlo;
if (ssid->key_mgmt & WPA_KEY_MGMT_WPS)
wpa_s->key_mgmt = WPA_KEY_MGMT_WPS;
@@ -444,6 +445,8 @@
wpa_s->mgmt_group_cipher);
pmksa_cache_clear_current(wpa_s->wpa);
+ os_memset(&mlo, 0, sizeof(mlo));
+ wpa_sm_set_mlo_params(wpa_s->wpa, &mlo);
}
@@ -766,18 +769,18 @@
for (i = 0; i < max; i++) {
if (wpa_s->keys_cleared & BIT(i))
continue;
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, i, 0, NULL, 0,
+ wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, i, 0, NULL, 0,
NULL, 0, KEY_FLAG_GROUP);
}
/* Pairwise Key ID 1 for Extended Key ID is tracked in bit 15 */
if (~wpa_s->keys_cleared & (BIT(0) | BIT(15)) && addr &&
!is_zero_ether_addr(addr)) {
if (!(wpa_s->keys_cleared & BIT(0)))
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL,
- 0, NULL, 0, KEY_FLAG_PAIRWISE);
+ wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, addr, 0, 0,
+ NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE);
if (!(wpa_s->keys_cleared & BIT(15)))
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 1, 0, NULL,
- 0, NULL, 0, KEY_FLAG_PAIRWISE);
+ wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, addr, 1, 0,
+ NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE);
/* MLME-SETPROTECTION.request(None) */
wpa_drv_mlme_setprotection(
wpa_s, addr,
@@ -1158,14 +1161,14 @@
if (wpa_s->confname == NULL)
return -1;
- conf = wpa_config_read(wpa_s->confname, NULL);
+ conf = wpa_config_read(wpa_s->confname, NULL, false);
if (conf == NULL) {
wpa_msg(wpa_s, MSG_ERROR, "Failed to parse the configuration "
"file '%s' - exiting", wpa_s->confname);
return -1;
}
if (wpa_s->confanother &&
- !wpa_config_read(wpa_s->confanother, conf)) {
+ !wpa_config_read(wpa_s->confanother, conf, true)) {
wpa_msg(wpa_s, MSG_ERROR,
"Failed to parse the configuration file '%s' - exiting",
wpa_s->confanother);
@@ -1350,6 +1353,108 @@
wpas_get_ssid_pmf(wpa_s, ssid));
}
+/**
+ * wpa_supplicant_get_psk - Get PSK from config or external database
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bss: Scan results for the selected BSS, or %NULL if not available
+ * @ssid: Configuration data for the selected network
+ * @psk: Buffer for the PSK
+ * Returns: 0 on success or -1 if configuration parsing failed
+ *
+ * This function obtains the PSK for a network, either included inline in the
+ * config or retrieved from an external database.
+ */
+static int wpa_supplicant_get_psk(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss, struct wpa_ssid *ssid,
+ u8 *psk)
+{
+ if (ssid->psk_set) {
+ wpa_hexdump_key(MSG_MSGDUMP, "PSK (set in config)",
+ ssid->psk, PMK_LEN);
+ os_memcpy(psk, ssid->psk, PMK_LEN);
+ return 0;
+ }
+
+#ifndef CONFIG_NO_PBKDF2
+ if (bss && ssid->bssid_set && ssid->ssid_len == 0 && ssid->passphrase) {
+ if (pbkdf2_sha1(ssid->passphrase, bss->ssid, bss->ssid_len,
+ 4096, psk, PMK_LEN) != 0) {
+ wpa_msg(wpa_s, MSG_WARNING, "Error in pbkdf2_sha1()");
+ return -1;
+ }
+ wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
+ psk, PMK_LEN);
+ return 0;
+ }
+#endif /* CONFIG_NO_PBKDF2 */
+
+#ifdef CONFIG_EXT_PASSWORD
+ if (ssid->ext_psk) {
+ struct wpabuf *pw = ext_password_get(wpa_s->ext_pw,
+ ssid->ext_psk);
+ char pw_str[64 + 1];
+
+ if (!pw) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "EXT PW: No PSK found from external storage");
+ return -1;
+ }
+
+ if (wpabuf_len(pw) < 8 || wpabuf_len(pw) > 64) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "EXT PW: Unexpected PSK length %d in external storage",
+ (int) wpabuf_len(pw));
+ ext_password_free(pw);
+ return -1;
+ }
+
+ os_memcpy(pw_str, wpabuf_head(pw), wpabuf_len(pw));
+ pw_str[wpabuf_len(pw)] = '\0';
+
+#ifndef CONFIG_NO_PBKDF2
+ if (wpabuf_len(pw) >= 8 && wpabuf_len(pw) < 64 && bss)
+ {
+ if (pbkdf2_sha1(pw_str, bss->ssid, bss->ssid_len,
+ 4096, psk, PMK_LEN) != 0) {
+ wpa_msg(wpa_s, MSG_WARNING,
+ "Error in pbkdf2_sha1()");
+ forced_memzero(pw_str, sizeof(pw_str));
+ ext_password_free(pw);
+ return -1;
+ }
+ wpa_hexdump_key(MSG_MSGDUMP,
+ "PSK (from external passphrase)",
+ psk, PMK_LEN);
+ } else
+#endif /* CONFIG_NO_PBKDF2 */
+ if (wpabuf_len(pw) == 2 * PMK_LEN) {
+ if (hexstr2bin(pw_str, psk, PMK_LEN) < 0) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "EXT PW: Invalid PSK hex string");
+ forced_memzero(pw_str, sizeof(pw_str));
+ ext_password_free(pw);
+ return -1;
+ }
+ wpa_hexdump_key(MSG_MSGDUMP, "PSK (from external PSK)",
+ psk, PMK_LEN);
+ } else {
+ wpa_msg(wpa_s, MSG_INFO,
+ "EXT PW: No suitable PSK available");
+ forced_memzero(pw_str, sizeof(pw_str));
+ ext_password_free(pw);
+ return -1;
+ }
+
+ forced_memzero(pw_str, sizeof(pw_str));
+ ext_password_free(pw);
+
+ return 0;
+ }
+#endif /* CONFIG_EXT_PASSWORD */
+
+ return -1;
+}
+
static void wpas_update_allowed_key_mgmt(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
@@ -1434,7 +1539,8 @@
return;
}
- if (wpa_s->conf->sae_pwe)
+ if (wpa_s->conf->sae_pwe != SAE_PWE_HUNT_AND_PECK &&
+ wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
#ifdef CONFIG_SAE_PK
if (ssid->sae_pk)
@@ -1464,6 +1570,7 @@
* @wpa_ie: Buffer for the WPA/RSN IE
* @wpa_ie_len: Maximum wpa_ie buffer size on input. This is changed to be the
* used buffer length in case the functions returns success.
+ * @skip_default_rsne: Whether to skip setting of the default RSNE/RSNXE
* Returns: 0 on success or -1 on failure
*
* This function is used to configure authentication and encryption parameters
@@ -1472,10 +1579,12 @@
*/
int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid,
- u8 *wpa_ie, size_t *wpa_ie_len)
+ u8 *wpa_ie, size_t *wpa_ie_len,
+ bool skip_default_rsne)
{
struct wpa_ie_data ie;
- int sel, proto, sae_pwe;
+ int sel, proto;
+ enum sae_pwe sae_pwe;
const u8 *bss_wpa, *bss_rsn, *bss_rsnx, *bss_osen;
if (bss) {
@@ -1655,7 +1764,8 @@
sel = ie.key_mgmt & ssid->key_mgmt;
#ifdef CONFIG_SAE
- if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE))
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE) ||
+ wpas_is_sae_avoided(wpa_s, ssid, &ie))
sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_SAE_EXT_KEY |
WPA_KEY_MGMT_FT_SAE | WPA_KEY_MGMT_FT_SAE_EXT_KEY);
#endif /* CONFIG_SAE */
@@ -1793,7 +1903,8 @@
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher);
if (!(ie.capabilities & WPA_CAPABILITY_MFPC) &&
- wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) {
+ (wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED ||
+ (bss && is_6ghz_freq(bss->freq)))) {
wpa_msg(wpa_s, MSG_INFO,
"RSN: Management frame protection required but the selected AP does not enable it");
return -1;
@@ -1808,18 +1919,14 @@
sae_pwe = wpa_s->conf->sae_pwe;
if ((ssid->sae_password_id ||
wpa_key_mgmt_sae_ext_key(wpa_s->key_mgmt)) &&
- sae_pwe != 3)
- sae_pwe = 1;
- if (bss && is_6ghz_freq(bss->freq)) {
- wpa_dbg(wpa_s, MSG_DEBUG, "WPA: force hash-to-element mode for 6GHz BSS.");
- sae_pwe = 1;
+ sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+ sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
+ if (bss && is_6ghz_freq(bss->freq) &&
+ sae_pwe == SAE_PWE_HUNT_AND_PECK) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "RSN: Enable SAE hash-to-element mode for 6 GHz BSS");
+ sae_pwe = SAE_PWE_BOTH;
}
-#ifdef CONFIG_TESTING_OPTIONS
- if (wpa_s->force_hunting_and_pecking_pwe) {
- wpa_dbg(wpa_s, MSG_DEBUG, "WPA: force hunting and pecking mode.");
- sae_pwe = 0;
- }
-#endif
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SAE_PWE, sae_pwe);
#ifdef CONFIG_SAE_PK
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SAE_PK,
@@ -1830,6 +1937,12 @@
(!ssid->sae_password && ssid->passphrase &&
sae_pk_valid_password(ssid->passphrase))));
#endif /* CONFIG_SAE_PK */
+ if (bss && is_6ghz_freq(bss->freq) &&
+ wpas_get_ssid_pmf(wpa_s, ssid) != MGMT_FRAME_PROTECTION_REQUIRED) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: Force MFPR=1 on 6 GHz");
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP,
+ MGMT_FRAME_PROTECTION_REQUIRED);
+ }
#ifdef CONFIG_TESTING_OPTIONS
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_FT_RSNXE_USED,
wpa_s->ft_rsnxe_used);
@@ -1869,16 +1982,21 @@
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_USE_EXT_KEY_ID, 0);
}
- if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) {
- wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to generate WPA IE");
- return -1;
- }
+ if (!skip_default_rsne) {
+ if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie,
+ wpa_ie_len)) {
+ wpa_msg(wpa_s, MSG_WARNING,
+ "RSN: Failed to generate RSNE/WPA IE");
+ return -1;
+ }
- wpa_s->rsnxe_len = sizeof(wpa_s->rsnxe);
- if (wpa_sm_set_assoc_rsnxe_default(wpa_s->wpa, wpa_s->rsnxe,
- &wpa_s->rsnxe_len)) {
- wpa_msg(wpa_s, MSG_WARNING, "RSN: Failed to generate RSNXE");
- return -1;
+ wpa_s->rsnxe_len = sizeof(wpa_s->rsnxe);
+ if (wpa_sm_set_assoc_rsnxe_default(wpa_s->wpa, wpa_s->rsnxe,
+ &wpa_s->rsnxe_len)) {
+ wpa_msg(wpa_s, MSG_WARNING,
+ "RSN: Failed to generate RSNXE");
+ return -1;
+ }
}
if (0) {
@@ -1892,120 +2010,27 @@
#endif /* CONFIG_DPP */
} else if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) {
int psk_set = 0;
- int sae_only;
- sae_only = (ssid->key_mgmt & (WPA_KEY_MGMT_PSK |
- WPA_KEY_MGMT_FT_PSK |
- WPA_KEY_MGMT_PSK_SHA256)) == 0;
+ if (wpa_key_mgmt_wpa_psk_no_sae(ssid->key_mgmt)) {
+ u8 psk[PMK_LEN];
- if (ssid->psk_set && !sae_only) {
- wpa_hexdump_key(MSG_MSGDUMP, "PSK (set in config)",
- ssid->psk, PMK_LEN);
- wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL,
- NULL);
- psk_set = 1;
+ if (wpa_supplicant_get_psk(wpa_s, bss, ssid,
+ psk) == 0) {
+ wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL,
+ NULL);
+ psk_set = 1;
+ }
+ forced_memzero(psk, sizeof(psk));
}
if (wpa_key_mgmt_sae(ssid->key_mgmt) &&
- (ssid->sae_password || ssid->passphrase))
+ (ssid->sae_password || ssid->passphrase || ssid->ext_psk))
psk_set = 1;
-#ifndef CONFIG_NO_PBKDF2
- if (bss && ssid->bssid_set && ssid->ssid_len == 0 &&
- ssid->passphrase && !sae_only) {
- u8 psk[PMK_LEN];
-
- if (pbkdf2_sha1(ssid->passphrase, bss->ssid,
- bss->ssid_len,
- 4096, psk, PMK_LEN) != 0) {
- wpa_msg(wpa_s, MSG_WARNING,
- "Error in pbkdf2_sha1()");
- return -1;
- }
- wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
- psk, PMK_LEN);
- wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL, NULL);
- psk_set = 1;
- os_memset(psk, 0, sizeof(psk));
- }
-#endif /* CONFIG_NO_PBKDF2 */
-#ifdef CONFIG_EXT_PASSWORD
- if (ssid->ext_psk && !sae_only) {
- struct wpabuf *pw = ext_password_get(wpa_s->ext_pw,
- ssid->ext_psk);
- char pw_str[64 + 1];
- u8 psk[PMK_LEN];
-
- if (pw == NULL) {
- wpa_msg(wpa_s, MSG_INFO, "EXT PW: No PSK "
- "found from external storage");
- return -1;
- }
-
- if (wpabuf_len(pw) < 8 || wpabuf_len(pw) > 64) {
- wpa_msg(wpa_s, MSG_INFO, "EXT PW: Unexpected "
- "PSK length %d in external storage",
- (int) wpabuf_len(pw));
- ext_password_free(pw);
- return -1;
- }
-
- os_memcpy(pw_str, wpabuf_head(pw), wpabuf_len(pw));
- pw_str[wpabuf_len(pw)] = '\0';
-
-#ifndef CONFIG_NO_PBKDF2
- if (wpabuf_len(pw) >= 8 && wpabuf_len(pw) < 64 && bss)
- {
- if (pbkdf2_sha1(pw_str, bss->ssid,
- bss->ssid_len,
- 4096, psk, PMK_LEN) != 0) {
- wpa_msg(wpa_s, MSG_WARNING,
- "Error in pbkdf2_sha1()");
- ext_password_free(pw);
- return -1;
- }
- os_memset(pw_str, 0, sizeof(pw_str));
- wpa_hexdump_key(MSG_MSGDUMP, "PSK (from "
- "external passphrase)",
- psk, PMK_LEN);
- wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL,
- NULL);
- psk_set = 1;
- os_memset(psk, 0, sizeof(psk));
- } else
-#endif /* CONFIG_NO_PBKDF2 */
- if (wpabuf_len(pw) == 2 * PMK_LEN) {
- if (hexstr2bin(pw_str, psk, PMK_LEN) < 0) {
- wpa_msg(wpa_s, MSG_INFO, "EXT PW: "
- "Invalid PSK hex string");
- os_memset(pw_str, 0, sizeof(pw_str));
- ext_password_free(pw);
- return -1;
- }
- wpa_hexdump_key(MSG_MSGDUMP,
- "PSK (from external PSK)",
- psk, PMK_LEN);
- wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL,
- NULL);
- psk_set = 1;
- os_memset(psk, 0, sizeof(psk));
- } else {
- wpa_msg(wpa_s, MSG_INFO, "EXT PW: No suitable "
- "PSK available");
- os_memset(pw_str, 0, sizeof(pw_str));
- ext_password_free(pw);
- return -1;
- }
-
- os_memset(pw_str, 0, sizeof(pw_str));
- ext_password_free(pw);
- }
-#endif /* CONFIG_EXT_PASSWORD */
-
if (!psk_set) {
wpa_msg(wpa_s, MSG_INFO,
"No PSK available for association");
- wpas_auth_failed(wpa_s, "NO_PSK_AVAILABLE");
+ wpas_auth_failed(wpa_s, "NO_PSK_AVAILABLE", NULL);
return -1;
}
#ifdef CONFIG_OWE
@@ -2228,31 +2253,53 @@
}
-int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style)
+int wpas_update_random_addr(struct wpa_supplicant *wpa_s,
+ enum wpas_mac_addr_style style,
+ struct wpa_ssid *ssid)
{
struct os_reltime now;
u8 addr[ETH_ALEN];
os_get_reltime(&now);
- if (wpa_s->last_mac_addr_style == style &&
- wpa_s->last_mac_addr_change.sec != 0 &&
- !os_reltime_expired(&now, &wpa_s->last_mac_addr_change,
- wpa_s->conf->rand_addr_lifetime)) {
- wpa_msg(wpa_s, MSG_DEBUG,
- "Previously selected random MAC address has not yet expired");
- return 0;
+ /* Random addresses are valid within a given ESS so check
+ * expiration/value only when continuing to use the same ESS. */
+ if (wpa_s->last_mac_addr_style == style && wpa_s->reassoc_same_ess) {
+ if (style == WPAS_MAC_ADDR_STYLE_DEDICATED_PER_ESS) {
+ /* Pregenerated addresses do not expire but their value
+ * might have changed, so let's check that. */
+ if (os_memcmp(wpa_s->own_addr, ssid->mac_value,
+ ETH_ALEN) == 0)
+ return 0;
+ } else if ((wpa_s->last_mac_addr_change.sec != 0 ||
+ wpa_s->last_mac_addr_change.usec != 0) &&
+ !os_reltime_expired(
+ &now,
+ &wpa_s->last_mac_addr_change,
+ wpa_s->conf->rand_addr_lifetime)) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Previously selected random MAC address has not yet expired");
+ return 0;
+ }
}
switch (style) {
- case 1:
+ case WPAS_MAC_ADDR_STYLE_RANDOM:
if (random_mac_addr(addr) < 0)
return -1;
break;
- case 2:
+ case WPAS_MAC_ADDR_STYLE_RANDOM_SAME_OUI:
os_memcpy(addr, wpa_s->perm_addr, ETH_ALEN);
if (random_mac_addr_keep_oui(addr) < 0)
return -1;
break;
+ case WPAS_MAC_ADDR_STYLE_DEDICATED_PER_ESS:
+ if (!ssid) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "Invalid 'ssid' for address policy 3");
+ return -1;
+ }
+ os_memcpy(addr, ssid->mac_value, ETH_ALEN);
+ break;
default:
return -1;
}
@@ -2276,7 +2323,7 @@
wpa_msg(wpa_s, MSG_DEBUG, "Using random MAC address " MACSTR,
MAC2STR(addr));
- return 0;
+ return 1;
}
@@ -2286,7 +2333,8 @@
!wpa_s->conf->preassoc_mac_addr)
return 0;
- return wpas_update_random_addr(wpa_s, wpa_s->conf->preassoc_mac_addr);
+ return wpas_update_random_addr(wpa_s, wpa_s->conf->preassoc_mac_addr,
+ NULL);
}
@@ -2305,10 +2353,10 @@
password = ssid->passphrase;
if (!password ||
- (conf->sae_pwe == 0 && !ssid->sae_password_id &&
+ (conf->sae_pwe == SAE_PWE_HUNT_AND_PECK && !ssid->sae_password_id &&
!wpa_key_mgmt_sae_ext_key(ssid->key_mgmt) &&
!sae_pk_valid_password(password)) ||
- conf->sae_pwe == 3) {
+ conf->sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK) {
/* PT derivation not needed */
sae_deinit_pt(ssid->pt);
ssid->pt = NULL;
@@ -2378,7 +2426,7 @@
struct wpa_bss *bss, struct wpa_ssid *ssid)
{
struct wpa_connect_work *cwork;
- int rand_style;
+ enum wpas_mac_addr_style rand_style;
wpa_s->own_disconnect_req = 0;
wpa_s->own_reconnect_req = 0;
@@ -2390,7 +2438,7 @@
wpabuf_free(wpa_s->pending_eapol_rx);
wpa_s->pending_eapol_rx = NULL;
- if (ssid->mac_addr == -1)
+ if (ssid->mac_addr == WPAS_MAC_ADDR_STYLE_NOT_SET)
rand_style = wpa_s->conf->mac_addr;
else
rand_style = ssid->mac_addr;
@@ -2422,11 +2470,16 @@
wpa_s_setup_sae_pt(wpa_s->conf, ssid);
#endif /* CONFIG_SAE */
- if (rand_style > 0 && !wpa_s->reassoc_same_ess) {
- if (wpas_update_random_addr(wpa_s, rand_style) < 0)
+ if (rand_style > WPAS_MAC_ADDR_STYLE_PERMANENT) {
+ int status = wpas_update_random_addr(wpa_s, rand_style, ssid);
+
+ if (status < 0)
return;
- wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
- } else if (rand_style == 0 && wpa_s->mac_addr_changed) {
+ if (rand_style != WPAS_MAC_ADDR_STYLE_DEDICATED_PER_ESS &&
+ status > 0) /* MAC changed */
+ wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
+ } else if (rand_style == WPAS_MAC_ADDR_STYLE_PERMANENT &&
+ wpa_s->mac_addr_changed) {
if (wpas_restore_permanent_mac_addr(wpa_s) < 0)
return;
}
@@ -3123,6 +3176,10 @@
wpa_key_mgmt_wpa(ssid->key_mgmt)) {
int try_opportunistic;
const u8 *cache_id = NULL;
+ const u8 *addr = bss->bssid;
+
+ if (wpa_s->valid_links)
+ addr = wpa_s->ap_mld_addr;
try_opportunistic = (ssid->proactive_key_caching < 0 ?
wpa_s->conf->okc :
@@ -3132,7 +3189,7 @@
if (wpa_key_mgmt_fils(ssid->key_mgmt))
cache_id = wpa_bss_get_fils_cache_id(bss);
#endif /* CONFIG_FILS */
- if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
+ if (pmksa_cache_set_current(wpa_s->wpa, NULL, addr,
ssid, try_opportunistic,
cache_id, 0) == 0) {
eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
@@ -3142,7 +3199,7 @@
}
wpa_ie_len = max_wpa_ie_len;
if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
- wpa_ie, &wpa_ie_len)) {
+ wpa_ie, &wpa_ie_len, false)) {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
"key management and encryption suites");
os_free(wpa_ie);
@@ -3154,7 +3211,7 @@
/* No PMKSA caching, but otherwise similar to RSN/WPA */
wpa_ie_len = max_wpa_ie_len;
if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
- wpa_ie, &wpa_ie_len)) {
+ wpa_ie, &wpa_ie_len, false)) {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
"key management and encryption suites");
os_free(wpa_ie);
@@ -3174,7 +3231,7 @@
} else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
wpa_ie_len = max_wpa_ie_len;
if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
- wpa_ie, &wpa_ie_len)) {
+ wpa_ie, &wpa_ie_len, false)) {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
"key management and encryption suites (no "
"scan results)");
@@ -3499,7 +3556,7 @@
}
#ifdef CONFIG_SME
if (len > 0 && wpa_s->sme.ft_used &&
- wpa_sm_has_ptk(wpa_s->wpa)) {
+ wpa_sm_has_ft_keys(wpa_s->wpa, md)) {
wpa_dbg(wpa_s, MSG_DEBUG,
"SME: Trying to use FT over-the-air");
algs |= WPA_AUTH_ALG_FT;
@@ -3783,6 +3840,7 @@
int use_crypt, ret, bssid_changed;
unsigned int cipher_pairwise, cipher_group, cipher_group_mgmt;
struct wpa_driver_associate_params params;
+ u8 psk[PMK_LEN];
#if defined(CONFIG_WEP) || defined(IEEE8021X_EAPOL)
int wep_keys_set = 0;
#endif /* CONFIG_WEP || IEEE8021X_EAPOL */
@@ -4080,8 +4138,8 @@
(WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK)))) {
#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
params.passphrase = ssid->passphrase;
- if (ssid->psk_set)
- params.psk = ssid->psk;
+ if (wpa_supplicant_get_psk(wpa_s, bss, ssid, psk) == 0)
+ params.psk = psk;
}
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X) &&
@@ -4110,8 +4168,8 @@
if ((wpa_key_mgmt_wpa_psk_no_sae(params.key_mgmt_suite) ||
wpa_key_mgmt_wpa_psk_no_sae(params.allowed_key_mgmts)) &&
#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
- ssid->psk_set)
- params.psk = ssid->psk;
+ wpa_supplicant_get_psk(wpa_s, bss, ssid, psk) == 0)
+ params.psk = psk;
}
params.drop_unencrypted = use_crypt;
@@ -4159,6 +4217,7 @@
#ifdef CONFIG_HE_OVERRIDES
wpa_supplicant_apply_he_overrides(wpa_s, ssid, ¶ms);
#endif /* CONFIG_HE_OVERRIDES */
+ wpa_supplicant_apply_eht_overrides(wpa_s, ssid, ¶ms);
#ifdef CONFIG_P2P
/*
@@ -4168,7 +4227,7 @@
*/
if (wpa_s->num_multichan_concurrent < 2) {
int freq, num;
- num = get_shared_radio_freqs(wpa_s, &freq, 1);
+ num = get_shared_radio_freqs(wpa_s, &freq, 1, false);
if (num > 0 && freq > 0 && freq != params.freq.freq) {
wpa_printf(MSG_DEBUG,
"Assoc conflicting freq found (%d != %d)",
@@ -4192,6 +4251,7 @@
#endif /* CONFIG_SAE */
ret = wpa_drv_associate(wpa_s, ¶ms);
+ forced_memzero(psk, sizeof(psk));
os_free(wpa_ie);
if (ret < 0) {
wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
@@ -4273,6 +4333,8 @@
wpa_supplicant_initiate_eapol(wpa_s);
if (old_ssid != wpa_s->current_ssid)
wpas_notify_network_changed(wpa_s);
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))
+ wpas_notify_auth_changed(wpa_s);
}
@@ -5184,6 +5246,8 @@
enum frame_encryption encrypted)
{
struct wpa_supplicant *wpa_s = ctx;
+ const u8 *connected_addr = wpa_s->valid_links ?
+ wpa_s->ap_mld_addr : wpa_s->bssid;
wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR " (encrypted=%d)",
MAC2STR(src_addr), encrypted);
@@ -5209,7 +5273,7 @@
#ifdef CONFIG_AP
!wpa_s->ap_iface &&
#endif /* CONFIG_AP */
- os_memcmp(src_addr, wpa_s->bssid, ETH_ALEN) != 0)) {
+ os_memcmp(src_addr, connected_addr, ETH_ALEN) != 0)) {
/*
* There is possible race condition between receiving the
* association event and the EAPOL frame since they are coming
@@ -5222,10 +5286,11 @@
* Authenticator uses BSSID as the src_addr (which is not the
* case with wired IEEE 802.1X).
*/
- wpa_dbg(wpa_s, MSG_DEBUG, "Not associated - Delay processing "
- "of received EAPOL frame (state=%s bssid=" MACSTR ")",
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Not associated - Delay processing of received EAPOL frame (state=%s connected_addr="
+ MACSTR ")",
wpa_supplicant_state_txt(wpa_s->wpa_state),
- MAC2STR(wpa_s->bssid));
+ MAC2STR(connected_addr));
wpabuf_free(wpa_s->pending_eapol_rx);
wpa_s->pending_eapol_rx = wpabuf_alloc_copy(buf, len);
if (wpa_s->pending_eapol_rx) {
@@ -5238,7 +5303,7 @@
}
wpa_s->last_eapol_matches_bssid =
- os_memcmp(src_addr, wpa_s->bssid, ETH_ALEN) == 0;
+ os_memcmp(src_addr, connected_addr, ETH_ALEN) == 0;
#ifdef CONFIG_AP
if (wpa_s->ap_iface) {
@@ -5354,6 +5419,10 @@
int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s)
{
+ u8 prev_mac_addr[ETH_ALEN];
+
+ os_memcpy(prev_mac_addr, wpa_s->own_addr, ETH_ALEN);
+
if ((!wpa_s->p2p_mgmt ||
!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) &&
!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)) {
@@ -5391,6 +5460,9 @@
fst_update_mac_addr(wpa_s->fst, wpa_s->own_addr);
#endif /* CONFIG_FST */
+ if (os_memcmp(prev_mac_addr, wpa_s->own_addr, ETH_ALEN) != 0)
+ wpas_notify_mac_address_changed(wpa_s);
+
return 0;
}
@@ -5957,6 +6029,17 @@
#endif /* CONFIG_HE_OVERRIDES */
+void wpa_supplicant_apply_eht_overrides(
+ struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ struct wpa_driver_associate_params *params)
+{
+ if (!ssid)
+ return;
+
+ params->disable_eht = ssid->disable_eht;
+}
+
+
static int pcsc_reader_init(struct wpa_supplicant *wpa_s)
{
#ifdef PCSC_FUNCS
@@ -6774,7 +6857,7 @@
#else /* CONFIG_BACKEND_FILE */
wpa_s->confname = os_strdup(iface->confname);
#endif /* CONFIG_BACKEND_FILE */
- wpa_s->conf = wpa_config_read(wpa_s->confname, NULL);
+ wpa_s->conf = wpa_config_read(wpa_s->confname, NULL, false);
if (wpa_s->conf == NULL) {
wpa_printf(MSG_ERROR, "Failed to read or parse "
"configuration '%s'.", wpa_s->confname);
@@ -6782,7 +6865,7 @@
}
wpa_s->confanother = os_rel2abs_path(iface->confanother);
if (wpa_s->confanother &&
- !wpa_config_read(wpa_s->confanother, wpa_s->conf)) {
+ !wpa_config_read(wpa_s->confanother, wpa_s->conf, true)) {
wpa_printf(MSG_ERROR,
"Failed to read or parse configuration '%s'.",
wpa_s->confanother);
@@ -6797,12 +6880,24 @@
os_free(wpa_s->conf->ctrl_interface);
wpa_s->conf->ctrl_interface =
os_strdup(iface->ctrl_interface);
+ if (!wpa_s->conf->ctrl_interface) {
+ wpa_printf(MSG_ERROR,
+ "Failed to duplicate control interface '%s'.",
+ iface->ctrl_interface);
+ return -1;
+ }
}
if (iface->driver_param) {
os_free(wpa_s->conf->driver_param);
wpa_s->conf->driver_param =
os_strdup(iface->driver_param);
+ if (!wpa_s->conf->driver_param) {
+ wpa_printf(MSG_ERROR,
+ "Failed to duplicate driver param '%s'.",
+ iface->driver_param);
+ return -1;
+ }
}
if (iface->p2p_mgmt && !iface->ctrl_interface) {
@@ -7571,26 +7666,63 @@
global->params.daemonize = params->daemonize;
global->params.wait_for_monitor = params->wait_for_monitor;
global->params.dbus_ctrl_interface = params->dbus_ctrl_interface;
- if (params->pid_file)
+
+ if (params->pid_file) {
global->params.pid_file = os_strdup(params->pid_file);
- if (params->ctrl_interface)
+ if (!global->params.pid_file) {
+ wpa_supplicant_deinit(global);
+ return NULL;
+ }
+ }
+
+ if (params->ctrl_interface) {
global->params.ctrl_interface =
os_strdup(params->ctrl_interface);
- if (params->ctrl_interface_group)
+ if (!global->params.ctrl_interface) {
+ wpa_supplicant_deinit(global);
+ return NULL;
+ }
+ }
+
+ if (params->ctrl_interface_group) {
global->params.ctrl_interface_group =
os_strdup(params->ctrl_interface_group);
- if (params->override_driver)
+ if (!global->params.ctrl_interface_group) {
+ wpa_supplicant_deinit(global);
+ return NULL;
+ }
+ }
+
+ if (params->override_driver) {
global->params.override_driver =
os_strdup(params->override_driver);
- if (params->override_ctrl_interface)
+ if (!global->params.override_driver) {
+ wpa_supplicant_deinit(global);
+ return NULL;
+ }
+ }
+
+ if (params->override_ctrl_interface) {
global->params.override_ctrl_interface =
os_strdup(params->override_ctrl_interface);
+ if (!global->params.override_ctrl_interface) {
+ wpa_supplicant_deinit(global);
+ return NULL;
+ }
+ }
+
#ifdef CONFIG_MATCH_IFACE
global->params.match_iface_count = params->match_iface_count;
if (params->match_iface_count) {
global->params.match_ifaces =
os_calloc(params->match_iface_count,
sizeof(struct wpa_interface));
+ if (!global->params.match_ifaces) {
+ wpa_printf(MSG_ERROR,
+ "Failed to allocate match interfaces");
+ wpa_supplicant_deinit(global);
+ return NULL;
+ }
os_memcpy(global->params.match_ifaces,
params->match_ifaces,
params->match_iface_count *
@@ -7598,9 +7730,15 @@
}
#endif /* CONFIG_MATCH_IFACE */
#ifdef CONFIG_P2P
- if (params->conf_p2p_dev)
+ if (params->conf_p2p_dev) {
global->params.conf_p2p_dev =
os_strdup(params->conf_p2p_dev);
+ if (!global->params.conf_p2p_dev) {
+ wpa_printf(MSG_ERROR, "Failed to allocate conf p2p");
+ wpa_supplicant_deinit(global);
+ return NULL;
+ }
+ }
#endif /* CONFIG_P2P */
wpa_debug_level = global->params.wpa_debug_level =
params->wpa_debug_level;
@@ -7935,7 +8073,7 @@
if (wpa_s->consecutive_conn_failures > 3 && wpa_s->current_ssid) {
wpa_printf(MSG_DEBUG, "Continuous association failures - "
"consider temporary network disabling");
- wpas_auth_failed(wpa_s, "CONN_FAILED");
+ wpas_auth_failed(wpa_s, "CONN_FAILED", bssid);
}
/*
* Multiple consecutive connection failures mean that other APs are
@@ -8112,6 +8250,8 @@
case WPA_CTRL_REQ_EAP_PASSWORD:
bin_clear_free(eap->password, eap->password_len);
eap->password = (u8 *) os_strdup(value);
+ if (!eap->password)
+ return -1;
eap->password_len = value_len;
eap->pending_req_password = 0;
if (ssid == wpa_s->current_ssid)
@@ -8120,6 +8260,8 @@
case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
bin_clear_free(eap->new_password, eap->new_password_len);
eap->new_password = (u8 *) os_strdup(value);
+ if (!eap->new_password)
+ return -1;
eap->new_password_len = value_len;
eap->pending_req_new_password = 0;
if (ssid == wpa_s->current_ssid)
@@ -8128,6 +8270,8 @@
case WPA_CTRL_REQ_EAP_PIN:
str_clear_free(eap->cert.pin);
eap->cert.pin = os_strdup(value);
+ if (!eap->cert.pin)
+ return -1;
eap->pending_req_pin = 0;
if (ssid == wpa_s->current_ssid)
wpa_s->reassociate = 1;
@@ -8135,6 +8279,8 @@
case WPA_CTRL_REQ_EAP_OTP:
bin_clear_free(eap->otp, eap->otp_len);
eap->otp = (u8 *) os_strdup(value);
+ if (!eap->otp)
+ return -1;
eap->otp_len = value_len;
os_free(eap->pending_req_otp);
eap->pending_req_otp = NULL;
@@ -8143,6 +8289,8 @@
case WPA_CTRL_REQ_EAP_PASSPHRASE:
str_clear_free(eap->cert.private_key_passwd);
eap->cert.private_key_passwd = os_strdup(value);
+ if (!eap->cert.private_key_passwd)
+ return -1;
eap->pending_req_passphrase = 0;
if (ssid == wpa_s->current_ssid)
wpa_s->reassociate = 1;
@@ -8150,6 +8298,8 @@
case WPA_CTRL_REQ_SIM:
str_clear_free(eap->external_sim_resp);
eap->external_sim_resp = os_strdup(value);
+ if (!eap->external_sim_resp)
+ return -1;
eap->pending_req_sim = 0;
break;
case WPA_CTRL_REQ_PSK_PASSPHRASE:
@@ -8277,6 +8427,13 @@
return NO_MGMT_FRAME_PROTECTION;
}
+#ifdef CONFIG_OCV
+ /* Enable PMF if OCV is being enabled */
+ if (wpa_s->conf->pmf == NO_MGMT_FRAME_PROTECTION &&
+ ssid && ssid->ocv)
+ return MGMT_FRAME_PROTECTION_OPTIONAL;
+#endif /* CONFIG_OCV */
+
return wpa_s->conf->pmf;
}
@@ -8284,6 +8441,19 @@
}
+#ifdef CONFIG_SAE
+bool wpas_is_sae_avoided(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ const struct wpa_ie_data *ie)
+{
+ return wpa_s->conf->sae_check_mfp &&
+ (!(ie->capabilities &
+ (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) ||
+ wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION);
+}
+#endif /* CONFIG_SAE */
+
+
int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr)
{
if (wpa_s->current_ssid == NULL ||
@@ -8304,7 +8474,8 @@
}
-void wpas_auth_failed(struct wpa_supplicant *wpa_s, char *reason)
+void wpas_auth_failed(struct wpa_supplicant *wpa_s, const char *reason,
+ const u8 *bssid)
{
struct wpa_ssid *ssid = wpa_s->current_ssid;
int dur;
@@ -8374,6 +8545,9 @@
ssid->auth_failures, dur, reason);
wpas_notify_ssid_temp_disabled(wpa_s, msg);
os_free(msg);
+
+ if (bssid)
+ os_memcpy(ssid->disabled_due_to, bssid, ETH_ALEN);
}
@@ -8390,8 +8564,15 @@
}
ssid->disabled_until.sec = 0;
ssid->disabled_until.usec = 0;
- if (clear_failures)
+ if (clear_failures) {
ssid->auth_failures = 0;
+ } else if (!is_zero_ether_addr(ssid->disabled_due_to)) {
+ wpa_printf(MSG_DEBUG, "Mark BSSID " MACSTR
+ " ignored to allow a lower priority BSS, if any, to be tried next",
+ MAC2STR(ssid->disabled_due_to));
+ wpa_bssid_ignore_add(wpa_s, ssid->disabled_due_to);
+ os_memset(ssid->disabled_due_to, 0, ETH_ALEN);
+ }
}
@@ -8505,7 +8686,7 @@
*/
int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s,
struct wpa_used_freq_data *freqs_data,
- unsigned int len)
+ unsigned int len, bool exclude_current)
{
struct wpa_supplicant *ifs;
u8 bssid[ETH_ALEN];
@@ -8521,6 +8702,9 @@
if (idx == len)
break;
+ if (exclude_current && ifs == wpa_s)
+ continue;
+
if (ifs->current_ssid == NULL || ifs->assoc_freq == 0)
continue;
@@ -8558,7 +8742,8 @@
* are using the same radio as the current interface.
*/
int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
- int *freq_array, unsigned int len)
+ int *freq_array, unsigned int len,
+ bool exclude_current)
{
struct wpa_used_freq_data *freqs_data;
int num, i;
@@ -8569,7 +8754,8 @@
if (!freqs_data)
return -1;
- num = get_shared_radio_freqs_data(wpa_s, freqs_data, len);
+ num = get_shared_radio_freqs_data(wpa_s, freqs_data, len,
+ exclude_current);
for (i = 0; i < num; i++)
freq_array[i] = freqs_data[i].freq;
@@ -8894,17 +9080,17 @@
continue;
wpa_printf(MSG_DEBUG,
"Override driver signal_poll information: current_signal: %d->%d avg_signal: %d->%d avg_beacon_signal: %d->%d current_noise: %d->%d",
- si->current_signal,
+ si->data.signal,
dso->si_current_signal,
- si->avg_signal,
+ si->data.avg_signal,
dso->si_avg_signal,
- si->avg_beacon_signal,
+ si->data.avg_beacon_signal,
dso->si_avg_beacon_signal,
si->current_noise,
dso->si_current_noise);
- si->current_signal = dso->si_current_signal;
- si->avg_signal = dso->si_avg_signal;
- si->avg_beacon_signal = dso->si_avg_beacon_signal;
+ si->data.signal = dso->si_current_signal;
+ si->data.avg_signal = dso->si_avg_signal;
+ si->data.avg_beacon_signal = dso->si_avg_beacon_signal;
si->current_noise = dso->si_current_noise;
break;
}
@@ -8955,3 +9141,43 @@
return scan_res;
}
+
+
+static bool wpas_ap_link_address(struct wpa_supplicant *wpa_s, const u8 *addr)
+{
+ int i;
+
+ if (!wpa_s->valid_links)
+ return false;
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (!(wpa_s->valid_links & BIT(i)))
+ continue;
+
+ if (os_memcmp(wpa_s->links[i].bssid, addr, ETH_ALEN) == 0)
+ return true;
+ }
+
+ return false;
+}
+
+
+int wpa_drv_send_action(struct wpa_supplicant *wpa_s, unsigned int freq,
+ unsigned int wait, const u8 *dst, const u8 *src,
+ const u8 *bssid, const u8 *data, size_t data_len,
+ int no_cck)
+{
+ if (!wpa_s->driver->send_action)
+ return -1;
+
+ if (data_len > 0 && data[0] != WLAN_ACTION_PUBLIC) {
+ if (wpas_ap_link_address(wpa_s, dst))
+ dst = wpa_s->ap_mld_addr;
+
+ if (wpas_ap_link_address(wpa_s, bssid))
+ bssid = wpa_s->ap_mld_addr;
+ }
+
+ return wpa_s->driver->send_action(wpa_s->drv_priv, freq, wait, dst, src,
+ bssid, data, data_len, no_cck);
+}
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index b6a1a74..7a5f9cb 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -419,6 +419,34 @@
# RSN.
#pmf=0
+# sae_check_mfp: Require PMF support to select SAE key_mgmt
+# 0 = Do not check PMF for SAE (default)
+# 1 = Limit SAE when PMF is not enabled
+#
+# When enabled SAE will not be selected if PMF will not be used
+# for the connection.
+# Scenarios where this check will limit SAE:
+# 1) ieee80211w=0 is set for the network
+# 2) The AP does not have PMF enabled.
+# 3) ieee80211w is unset, pmf=1 is enabled globally, and
+# the device does not support the BIP cipher.
+# Consider the configuration of global parameterss sae_check_mfp=1, pmf=1 and a
+# network configured with ieee80211w unset and key_mgmt=SAE WPA-PSK.
+# In the example WPA-PSK will be used if the device does not support
+# the BIP cipher or the AP has PMF disabled.
+# Limiting SAE with this check can avoid failing to associate to an AP
+# that is configured with sae_requires_mfp=1 if the device does
+# not support PMF due to lack of the BIP cipher.
+#
+# Enabling this check helps with compliance of the WPA3
+# specification for WPA3-Personal transition mode.
+# The WPA3 specification section 2.3 "WPA3-Personal transition mode" item 8
+# states "A STA shall negotiate PMF when associating to an AP using SAE".
+# With this check WPA3 capable devices when connecting
+# to transition mode APs that do not advertise PMF support
+# will not use SAE and instead fallback to PSK.
+#sae_check_mfp=0
+
# Enabled SAE finite cyclic groups in preference order
# By default (if this parameter is not set), the mandatory group 19 (ECC group
# defined over a 256-bit prime order field, NIST P-256) is preferred and groups
@@ -655,7 +683,26 @@
# be used to configure alternative FQDNs that will be considered home
# networks.
#
+# home_ois: Home OI(s)
+# This string field contains one or more comma delimited OIs (hexdump)
+# identifying the access the access points that support authentication
+# with this credential. There are an alternative to the use of the realm
+# parameter. When using Home OIs to match the network, the EAP parameters
+# need to be pre-configured with the credentials since the NAI Realm
+# information may not be available or fetched.
+# A successful authentication with the access point is possible as soon
+# as at least one Home OI from the list matches an OI in the Roaming
+# Consortium advertised by the access point.
+# (Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/HomeOIList/<X+>/HomeOI)
+#
+# required_home_ois: Required Home OI(s)
+# This string field contains the set of Home OI(s) (hexdump) that are
+# required to be advertised by the AP for the credential to be considered
+# matching.
+# (Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/HomeOIList/<X+>/HomeOIRequired)
+#
# roaming_consortium: Roaming Consortium OI
+# Deprecated: use home_ois instead.
# If roaming_consortium_len is non-zero, this field contains the
# Roaming Consortium OI that can be used to determine which access
# points support authentication with this credential. This is an
@@ -665,6 +712,7 @@
# may not be available or fetched.
#
# required_roaming_consortium: Required Roaming Consortium OI
+# Deprecated: use required_home_ois instead.
# If required_roaming_consortium_len is non-zero, this field contains the
# Roaming Consortium OI that is required to be advertised by the AP for
# the credential to be considered matching.
@@ -770,7 +818,7 @@
# password="password"
# ca_cert="/etc/wpa_supplicant/ca.pem"
# domain="example.com"
-# roaming_consortium=223344
+# home_ois="223344"
# eap=TTLS
# phase2="auth=MSCHAPV2"
#}
@@ -1635,6 +1683,10 @@
# 2: MCS 0-9
# 3: not supported
+# disable_eht: Whether EHT should be disabled.
+# 0 = EHT enabled (if supported) (default)
+# 1 = EHT disabled
+
# multi_ap_backhaul_sta: Multi-AP backhaul STA functionality
# 0 = normal STA (default)
# 1 = backhaul STA
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index e673a9c..d364212 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -22,6 +22,7 @@
#include "wmm_ac.h"
#include <netinet/in.h>
#include <netinet/in6.h>
+#include "pasn/pasn_common.h"
extern const char *const wpa_supplicant_version;
extern const char *const wpa_supplicant_license;
@@ -541,61 +542,6 @@
int num_policies;
};
-#ifdef CONFIG_PASN
-
-struct pasn_fils {
- u8 nonce[FILS_NONCE_LEN];
- u8 anonce[FILS_NONCE_LEN];
- u8 session[FILS_SESSION_LEN];
- u8 erp_pmkid[PMKID_LEN];
- bool completed;
-};
-
-struct wpas_pasn {
- int akmp;
- int cipher;
- u16 group;
- bool secure_ltf;
- int freq;
- size_t kdk_len;
-
- u8 trans_seq;
- u8 status;
-
- u8 own_addr[ETH_ALEN];
- u8 bssid[ETH_ALEN];
- size_t pmk_len;
- u8 pmk[PMK_LEN_MAX];
- bool using_pmksa;
-
- u8 hash[SHA384_MAC_LEN];
-
- struct wpabuf *beacon_rsne_rsnxe;
- struct wpa_ptk ptk;
- struct crypto_ecdh *ecdh;
-
- struct wpabuf *comeback;
- u16 comeback_after;
-
-#ifdef CONFIG_SAE
- struct sae_data sae;
-#endif /* CONFIG_SAE */
-
- struct wpa_ssid *ssid;
-
-#ifdef CONFIG_FILS
- struct pasn_fils fils;
-#endif /* CONFIG_FILS */
-
-#ifdef CONFIG_IEEE80211R
- u8 pmk_r1[PMK_LEN_MAX];
- size_t pmk_r1_len;
- u8 pmk_r1_name[WPA_PMK_NAME_LEN];
-#endif /* CONFIG_IEEE80211R */
-};
-#endif /* CONFIG_PASN */
-
-
enum ip_version {
IPV4 = 4,
IPV6 = 6,
@@ -757,8 +703,9 @@
int ap_ies_from_associnfo;
unsigned int assoc_freq;
u8 ap_mld_addr[ETH_ALEN];
- u8 valid_links; /* bitmap of valid MLO link IDs */
- struct {
+ u8 mlo_assoc_link_id;
+ u16 valid_links; /* bitmap of valid MLO link IDs */
+ struct ml_sta_link_info {
u8 addr[ETH_ALEN];
u8 bssid[ETH_ALEN];
unsigned int freq;
@@ -1002,7 +949,7 @@
unsigned int connection_11b_only:1;
struct os_reltime last_mac_addr_change;
- int last_mac_addr_style;
+ enum wpas_mac_addr_style last_mac_addr_style;
struct ibss_rsn *ibss_rsn;
@@ -1054,6 +1001,7 @@
struct wpa_ssid *ext_auth_wpa_ssid;
u8 ext_auth_ssid[SSID_MAX_LEN];
size_t ext_auth_ssid_len;
+ int ext_auth_key_mgmt;
int *sae_rejected_groups;
#endif /* CONFIG_SAE */
} sme;
@@ -1389,7 +1337,6 @@
unsigned int oci_freq_override_ft_assoc;
unsigned int oci_freq_override_fils_assoc;
unsigned int oci_freq_override_wnm_sleep;
- int force_hunting_and_pecking_pwe;
unsigned int disable_eapol_g2_tx;
#endif /* CONFIG_TESTING_OPTIONS */
@@ -1580,8 +1527,12 @@
struct robust_av_data robust_av;
bool mscs_setup_done;
+ bool wps_scan_done; /* Set upon receiving scan results event */
+ bool supp_pbc_active; /* Set for interface when PBC is triggered */
+ bool wps_overlap;
+
#ifdef CONFIG_PASN
- struct wpas_pasn pasn;
+ struct pasn_data pasn;
struct wpa_radio_work *pasn_auth_work;
unsigned int pasn_count;
struct pasn_auth *pasn_params;
@@ -1599,6 +1550,8 @@
unsigned int enable_dscp_policy_capa:1;
unsigned int connection_dscp:1;
unsigned int wait_for_dscp_req:1;
+
+ struct wpa_signal_info last_signal_info;
};
@@ -1612,6 +1565,9 @@
void wpa_supplicant_apply_he_overrides(
struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
struct wpa_driver_associate_params *params);
+void wpa_supplicant_apply_eht_overrides(
+ struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ struct wpa_driver_associate_params *params);
int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
@@ -1628,7 +1584,8 @@
struct wpa_ssid *ssid, struct wpa_ie_data *ie);
int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid,
- u8 *wpa_ie, size_t *wpa_ie_len);
+ u8 *wpa_ie, size_t *wpa_ie_len,
+ bool skip_default_rsne);
int wpas_restore_permanent_mac_addr(struct wpa_supplicant *wpa_s);
void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss,
@@ -1706,7 +1663,8 @@
void fils_pmksa_cache_flush(struct wpa_supplicant *wpa_s);
int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s);
int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s);
-void wpas_auth_failed(struct wpa_supplicant *wpa_s, char *reason);
+void wpas_auth_failed(struct wpa_supplicant *wpa_s, const char *reason,
+ const u8 *bssid);
void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid, int clear_failures);
int disallowed_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid);
@@ -1715,7 +1673,8 @@
void wpas_request_connection(struct wpa_supplicant *wpa_s);
void wpas_request_disconnection(struct wpa_supplicant *wpa_s);
int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen);
-int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style);
+int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style,
+ struct wpa_ssid *ssid);
int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s);
void add_freq(int *freqs, int *num_freqs, int freq);
@@ -1840,6 +1799,7 @@
void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, void *sock_ctx);
void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s);
int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s);
+int wpa_wps_supplicant_fast_associate(struct wpa_supplicant *wpa_s);
struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
struct wpa_ssid **selected_ssid);
int wpas_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
@@ -1848,6 +1808,7 @@
int wpa_supplicant_need_to_roam_within_ess(struct wpa_supplicant *wpa_s,
struct wpa_bss *current_bss,
struct wpa_bss *seleceted);
+void wpas_reset_mlo_info(struct wpa_supplicant *wpa_s);
/* eap_register.c */
int eap_register_methods(void);
@@ -1885,6 +1846,10 @@
int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr);
void wpa_s_setup_sae_pt(struct wpa_config *conf, struct wpa_ssid *ssid);
+bool wpas_is_sae_avoided(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ const struct wpa_ie_data *ie);
+
int wpas_init_ext_pw(struct wpa_supplicant *wpa_s);
void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title,
@@ -1893,9 +1858,10 @@
int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s,
struct wpa_used_freq_data *freqs_data,
- unsigned int len);
+ unsigned int len, bool exclude_current);
int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
- int *freq_array, unsigned int len);
+ int *freq_array, unsigned int len,
+ bool exclude_current);
void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx);
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 556f9cf..7d5ea4c 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -250,7 +250,7 @@
else
wpa_s->group_cipher = cipher;
}
- return wpa_drv_set_key(wpa_s, WPA_ALG_WEP,
+ return wpa_drv_set_key(wpa_s, -1, WPA_ALG_WEP,
unicast ? wpa_s->bssid : NULL,
keyidx, unicast, NULL, 0, key, keylen,
unicast ? KEY_FLAG_PAIRWISE_RX_TX :
@@ -375,7 +375,7 @@
wpa_hexdump_key(MSG_DEBUG, "RSN: Configure PMK for driver-based 4-way "
"handshake", pmk, pmk_len);
- if (wpa_drv_set_key(wpa_s, 0, NULL, 0, 0, NULL, 0, pmk,
+ if (wpa_drv_set_key(wpa_s, -1, 0, NULL, 0, 0, NULL, 0, pmk,
pmk_len, KEY_FLAG_PMK)) {
wpa_printf(MSG_DEBUG, "Failed to set PMK to the driver");
}
@@ -537,7 +537,7 @@
}
-static int wpa_supplicant_set_key(void *_wpa_s, enum wpa_alg alg,
+static int wpa_supplicant_set_key(void *_wpa_s, int link_id, enum wpa_alg alg,
const u8 *addr, int key_idx, int set_tx,
const u8 *seq, size_t seq_len,
const u8 *key, size_t key_len,
@@ -566,8 +566,8 @@
wpa_s->last_tk_len = key_len;
}
#endif /* CONFIG_TESTING_OPTIONS */
- return wpa_drv_set_key(wpa_s, alg, addr, key_idx, set_tx, seq, seq_len,
- key, key_len, key_flag);
+ return wpa_drv_set_key(wpa_s, link_id, alg, addr, key_idx, set_tx, seq,
+ seq_len, key, key_len, key_flag);
}
@@ -952,28 +952,9 @@
void wpas_send_ctrl_req(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
const char *field_name, const char *txt)
{
- char *buf;
- size_t buflen;
- int len;
-
- buflen = 100 + os_strlen(txt) + ssid->ssid_len;
- buf = os_malloc(buflen);
- if (buf == NULL)
- return;
- len = os_snprintf(buf, buflen, "%s-%d:%s needed for SSID ",
- field_name, ssid->id, txt);
- if (os_snprintf_error(buflen, len)) {
- os_free(buf);
- return;
- }
- if (ssid->ssid && buflen > len + ssid->ssid_len) {
- os_memcpy(buf + len, ssid->ssid, ssid->ssid_len);
- len += ssid->ssid_len;
- buf[len] = '\0';
- }
- buf[buflen - 1] = '\0';
- wpa_msg(wpa_s, MSG_INFO, WPA_CTRL_REQ "%s", buf);
- os_free(buf);
+ wpa_msg(wpa_s, MSG_INFO, WPA_CTRL_REQ "%s-%d:%s needed for SSID %s",
+ field_name, ssid->id, txt,
+ wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
}
@@ -1294,7 +1275,7 @@
if (wpa_s->conf->key_mgmt_offload &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD))
- return wpa_drv_set_key(wpa_s, 0, NULL, 0, 0,
+ return wpa_drv_set_key(wpa_s, -1, 0, NULL, 0, 0,
NULL, 0, pmk, pmk_len, KEY_FLAG_PMK);
else
return 0;
@@ -1342,9 +1323,8 @@
}
-static void wpa_supplicant_transition_disable(void *_wpa_s, u8 bitmap)
+void wpas_transition_disable(struct wpa_supplicant *wpa_s, u8 bitmap)
{
- struct wpa_supplicant *wpa_s = _wpa_s;
struct wpa_ssid *ssid;
int changed = 0;
@@ -1427,13 +1407,20 @@
}
+static void wpa_supplicant_transition_disable(void *_wpa_s, u8 bitmap)
+{
+ struct wpa_supplicant *wpa_s = _wpa_s;
+ wpas_transition_disable(wpa_s, bitmap);
+}
+
+
static void wpa_supplicant_store_ptk(void *ctx, u8 *addr, int cipher,
u32 life_time, const struct wpa_ptk *ptk)
{
struct wpa_supplicant *wpa_s = ctx;
ptksa_cache_add(wpa_s->ptksa, wpa_s->own_addr, addr, cipher, life_time,
- ptk, NULL, NULL);
+ ptk, NULL, NULL, 0);
}
#endif /* CONFIG_NO_WPA */
@@ -1454,6 +1441,16 @@
#endif /* CONFIG_PASN */
+static void
+wpa_supplicant_notify_pmksa_cache_entry(void *_wpa_s,
+ struct rsn_pmksa_cache_entry *entry)
+{
+ struct wpa_supplicant *wpa_s = _wpa_s;
+
+ wpas_notify_pmk_cache_added(wpa_s, entry);
+}
+
+
int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
{
#ifndef CONFIG_NO_WPA
@@ -1519,6 +1516,7 @@
#ifdef CONFIG_PASN
ctx->set_ltf_keyseed = wpa_supplicant_set_ltf_keyseed;
#endif /* CONFIG_PASN */
+ ctx->notify_pmksa_cache_entry = wpa_supplicant_notify_pmksa_cache_entry;
wpa_s->wpa = wpa_sm_init(ctx);
if (wpa_s->wpa == NULL) {
diff --git a/wpa_supplicant/wpas_glue.h b/wpa_supplicant/wpas_glue.h
index 338af4e..dd692b9 100644
--- a/wpa_supplicant/wpas_glue.h
+++ b/wpa_supplicant/wpas_glue.h
@@ -27,4 +27,6 @@
void wpas_send_ctrl_req(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
const char *field_name, const char *txt);
+void wpas_transition_disable(struct wpa_supplicant *wpa_s, u8 bitmap);
+
#endif /* WPAS_GLUE_H */
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 7428f02..de7dc98 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -79,6 +79,18 @@
}
+static struct wpabuf * wpas_wps_get_wps_ie(struct wpa_bss *bss)
+{
+ /* Return the latest receive WPS IE from the AP regardless of whether
+ * it was from a Beacon frame or Probe Response frame to avoid using
+ * stale information. */
+ if (bss->beacon_newer)
+ return wpa_bss_get_vendor_ie_multi_beacon(bss,
+ WPS_IE_VENDOR_TYPE);
+ return wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+}
+
+
int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
{
if (wpas_p2p_wps_eapol_cb(wpa_s) > 0)
@@ -141,8 +153,7 @@
struct wpabuf *wps;
struct wps_parse_attr attr;
- wps = wpa_bss_get_vendor_ie_multi(bss,
- WPS_IE_VENDOR_TYPE);
+ wps = wpas_wps_get_wps_ie(bss);
if (wps && wps_parse_msg(wps, &attr) == 0 &&
attr.wps_state &&
*attr.wps_state == WPS_STATE_CONFIGURED)
@@ -1002,6 +1013,7 @@
* during an EAP-WSC exchange).
*/
wpas_notify_wps_event_fail(wpa_s, &data.fail);
+ wpa_s->supp_pbc_active = false;
wpas_clear_wps(wpa_s);
}
@@ -1204,6 +1216,8 @@
ssid->eap.fragment_size = wpa_s->wps_fragment_size;
if (multi_ap_backhaul_sta)
ssid->multi_ap_backhaul_sta = 1;
+ wpa_s->supp_pbc_active = true;
+ wpa_s->wps_overlap = false;
wpa_supplicant_wps_event(wpa_s, WPS_EV_PBC_ACTIVE, NULL);
eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
wpa_s, NULL);
@@ -1371,6 +1385,7 @@
wpas_clear_wps(wpa_s);
}
+ wpa_s->supp_pbc_active = false;
wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_CANCEL);
wpa_s->after_wps = 0;
@@ -1705,7 +1720,7 @@
if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
return -1;
- wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+ wps_ie = wpas_wps_get_wps_ie(bss);
if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
if (!wps_ie) {
wpa_printf(MSG_DEBUG, " skip - non-WPS AP");
@@ -1779,13 +1794,13 @@
int ret = 0;
if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
- wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+ wps_ie = wpas_wps_get_wps_ie(bss);
if (wps_ie && wps_is_selected_pbc_registrar(wps_ie)) {
/* allow wildcard SSID for WPS PBC */
ret = 1;
}
} else if (eap_is_wps_pin_enrollee(&ssid->eap)) {
- wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+ wps_ie = wpas_wps_get_wps_ie(bss);
if (wps_ie &&
(wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1) ||
wpa_s->scan_runs >= WPS_PIN_SCAN_IGNORE_SEL_REG)) {
@@ -1828,9 +1843,40 @@
}
+static bool wpas_wps_is_pbc_overlap(struct wps_ap_info *ap,
+ struct wpa_bss *selected,
+ struct wpa_ssid *ssid,
+ const u8 *sel_uuid)
+{
+ if (!ap->pbc_active ||
+ os_memcmp(selected->bssid, ap->bssid, ETH_ALEN) == 0)
+ return false;
+
+ if (!is_zero_ether_addr(ssid->bssid) &&
+ os_memcmp(ap->bssid, ssid->bssid, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Ignore another BSS " MACSTR
+ " in active PBC mode due to local BSSID limitation",
+ MAC2STR(ap->bssid));
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Another BSS in active PBC mode: " MACSTR,
+ MAC2STR(ap->bssid));
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID of the other BSS",
+ ap->uuid, UUID_LEN);
+ if (!sel_uuid || os_memcmp(sel_uuid, ap->uuid, UUID_LEN) != 0)
+ return true;
+
+ /* TODO: verify that this is reasonable dual-band situation */
+
+ return false;
+}
+
+
int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
struct wpa_bss *selected, struct wpa_ssid *ssid)
{
+ struct wpa_supplicant *iface;
const u8 *sel_uuid;
struct wpabuf *wps_ie;
int ret = 0;
@@ -1859,36 +1905,21 @@
sel_uuid = NULL;
}
- for (i = 0; i < wpa_s->num_wps_ap; i++) {
- struct wps_ap_info *ap = &wpa_s->wps_ap[i];
+ for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
+ for (i = 0; i < iface->num_wps_ap; i++) {
+ struct wps_ap_info *ap = &iface->wps_ap[i];
- if (!ap->pbc_active ||
- os_memcmp(selected->bssid, ap->bssid, ETH_ALEN) == 0)
- continue;
-
- if (!is_zero_ether_addr(ssid->bssid) &&
- os_memcmp(ap->bssid, ssid->bssid, ETH_ALEN) != 0) {
- wpa_printf(MSG_DEBUG, "WPS: Ignore another BSS " MACSTR
- " in active PBC mode due to local BSSID limitation",
- MAC2STR(ap->bssid));
- continue;
+ if (wpas_wps_is_pbc_overlap(ap, selected, ssid,
+ sel_uuid)) {
+ ret = 1; /* PBC overlap */
+ wpa_msg(iface, MSG_INFO,
+ "WPS: PBC overlap detected: "
+ MACSTR " and " MACSTR,
+ MAC2STR(selected->bssid),
+ MAC2STR(ap->bssid));
+ break;
+ }
}
-
- wpa_printf(MSG_DEBUG, "WPS: Another BSS in active PBC mode: "
- MACSTR, MAC2STR(ap->bssid));
- wpa_hexdump(MSG_DEBUG, "WPS: UUID of the other BSS",
- ap->uuid, UUID_LEN);
- if (sel_uuid == NULL ||
- os_memcmp(sel_uuid, ap->uuid, UUID_LEN) != 0) {
- ret = 1; /* PBC overlap */
- wpa_msg(wpa_s, MSG_INFO, "WPS: PBC overlap detected: "
- MACSTR " and " MACSTR,
- MAC2STR(selected->bssid),
- MAC2STR(ap->bssid));
- break;
- }
-
- /* TODO: verify that this is reasonable dual-band situation */
}
wpabuf_free(wps_ie);
@@ -1907,7 +1938,8 @@
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
struct wpabuf *ie;
- ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+
+ ie = wpas_wps_get_wps_ie(bss);
if (!ie)
continue;
if (wps_is_selected_pbc_registrar(ie))
@@ -2995,6 +3027,48 @@
}
+bool wpas_wps_partner_link_scan_done(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_global *global = wpa_s->global;
+ struct wpa_supplicant *iface;
+
+ for (iface = global->ifaces; iface; iface = iface->next) {
+ if (iface == wpa_s)
+ continue;
+
+ if (!iface->supp_pbc_active)
+ continue;
+
+ /* Scan results are available for both links. While the current
+ * link will proceed for network selection, ensure the partner
+ * link also gets an attempt at network selection and connect
+ * with the selected BSS. */
+ if (iface->wps_scan_done)
+ wpa_wps_supplicant_fast_associate(iface);
+ else
+ return false;
+ }
+
+ return true;
+}
+
+
+bool wpas_wps_partner_link_overlap_detect(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_global *global = wpa_s->global;
+ struct wpa_supplicant *iface;
+
+ for (iface = global->ifaces; iface; iface = iface->next) {
+ if (iface == wpa_s)
+ continue;
+ if (iface->wps_overlap)
+ return true;
+ }
+
+ return false;
+}
+
+
void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid)
{
struct wps_ap_info *ap;
diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h
index 41b9298..5c8b833 100644
--- a/wpa_supplicant/wps_supplicant.h
+++ b/wpa_supplicant/wps_supplicant.h
@@ -87,6 +87,8 @@
const struct wpabuf *sel);
void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res);
+bool wpas_wps_partner_link_scan_done(struct wpa_supplicant *wpa_s);
+bool wpas_wps_partner_link_overlap_detect(struct wpa_supplicant *wpa_s);
void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid);
int wpas_wps_reenable_networks_pending(struct wpa_supplicant *wpa_s);
@@ -146,6 +148,17 @@
{
}
+static inline bool wpas_wps_partner_link_scan_done(struct wpa_supplicant *wpa_s)
+{
+ return true;
+}
+
+static inline bool
+wpas_wps_partner_link_overlap_detect(struct wpa_supplicant *wpa_s)
+{
+ return false;
+}
+
static inline void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s,
const u8 *bssid)
{