blob: 5895635bc27d53173b9a1e83ed067df875ce1790 [file] [log] [blame]
From 5e6164cb6143d55409c08ae9bfd859efa188e383 Mon Sep 17 00:00:00 2001
From: Rameshkumar Sundaram <quic_ramess@quicinc.com>
Date: Thu, 28 Mar 2024 23:46:47 +0530
Subject: [PATCH 017/104] hostapd: MLO: add support for MLO rekey
Currently wpa group rekey is not supported for ML Stations when non-assoc
link initiates a group rekey, to support the same following changes have
been made-
* Calculate links specific MLO GTK/IGTK and BIGTK KDE lengths based on
corresponding cipher and key instead of taking length of one link and
multiplying it by no of associated links.
* For MLD, Arm group key rekey timer on one of the links and whenever it
fires do group key rekey for all links.
Signed-off-by: Rameshkumar Sundaram <quic_ramess@quicinc.com>
Co-developed-by: Adil Saeed Musthafa <quic_adilm@quicinc.com>
Signed-off-by: Adil Saeed Musthafa <quic_adilm@quicinc.com>
Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
---
src/ap/drv_callbacks.c | 2 +-
src/ap/ieee802_11.c | 13 +-
src/ap/wpa_auth.c | 310 +++++++++++++++---
src/ap/wpa_auth.h | 9 +-
src/ap/wpa_auth_glue.c | 22 ++
src/ap/wpa_auth_i.h | 1 +
src/ap/wpa_auth_ie.c | 12 +-
src/common/wpa_common.h | 1 +
tests/fuzzing/eapol-key-auth/eapol-key-auth.c | 2 +-
wpa_supplicant/ibss_rsn.c | 2 +-
10 files changed, 317 insertions(+), 57 deletions(-)
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 064c7abae..dc21977ff 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -528,7 +528,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
elems.rsnxe ? elems.rsnxe - 2 : NULL,
elems.rsnxe ? elems.rsnxe_len + 2 : 0,
elems.mdie, elems.mdie_len,
- elems.owe_dh, elems.owe_dh_len);
+ elems.owe_dh, elems.owe_dh_len, NULL);
reason = WLAN_REASON_INVALID_IE;
status = WLAN_STATUS_INVALID_IE;
switch (res) {
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 9d04bdf43..7ee18f4ae 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -1887,7 +1887,7 @@ void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
elems.rsn_ie - 2, elems.rsn_ie_len + 2,
elems.rsnxe ? elems.rsnxe - 2 : NULL,
elems.rsnxe ? elems.rsnxe_len + 2 : 0,
- elems.mdie, elems.mdie_len, NULL, 0);
+ elems.mdie, elems.mdie_len, NULL, 0, NULL);
resp = wpa_res_to_status_code(res);
if (resp != WLAN_STATUS_SUCCESS)
goto fail;
@@ -3778,7 +3778,7 @@ u16 owe_process_rsn_ie(struct hostapd_data *hapd,
rsn_ie_len += 2;
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
hapd->iface->freq, rsn_ie, rsn_ie_len,
- NULL, 0, NULL, 0, owe_dh, owe_dh_len);
+ NULL, 0, NULL, 0, owe_dh, owe_dh_len, NULL);
status = wpa_res_to_status_code(res);
if (status != WLAN_STATUS_SUCCESS)
goto end;
@@ -3867,6 +3867,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *wpa_ie;
size_t wpa_ie_len;
const u8 *p2p_dev_addr = NULL;
+ struct hostapd_data *assoc_hapd;
+ struct sta_info *assoc_sta = NULL;
resp = check_ssid(hapd, sta, elems->ssid, elems->ssid_len);
if (resp != WLAN_STATUS_SUCCESS)
@@ -4041,6 +4043,10 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
wpa_ie_len += 2;
if (!sta->wpa_sm) {
+ if (!link)
+ assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta,
+ &assoc_hapd);
+
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
sta->addr,
p2p_dev_addr);
@@ -4076,7 +4082,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
elems->rsnxe ? elems->rsnxe_len + 2 :
0,
elems->mdie, elems->mdie_len,
- elems->owe_dh, elems->owe_dh_len);
+ elems->owe_dh, elems->owe_dh_len,
+ assoc_sta ? assoc_sta->wpa_sm : NULL);
resp = wpa_res_to_status_code(res);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 8c1052c25..7a07dcc4c 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -71,6 +71,9 @@ static void wpa_group_put(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
static int ieee80211w_kde_len(struct wpa_state_machine *sm);
static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos);
+static void wpa_group_update_gtk(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group);
+
static const u32 eapol_key_timeout_first = 100; /* ms */
static const u32 eapol_key_timeout_subseq = 1000; /* ms */
@@ -102,6 +105,22 @@ static const u8 * wpa_auth_get_spa(const struct wpa_state_machine *sm)
return sm->addr;
}
+static void wpa_update_gkeydone(struct wpa_state_machine *sm, int update)
+{
+#ifdef CONFIG_IEEE80211BE
+ int link_id;
+#endif /* CONFIG_IEEE80211BE */
+ if (!sm || !sm->wpa_auth)
+ return;
+
+ sm->wpa_auth->group->GKeyDoneStations += update;
+
+#ifdef CONFIG_IEEE80211BE
+ for_each_sm_auth(sm, link_id)
+ sm->mld_links[link_id].wpa_auth->group->GKeyDoneStations += update;
+#endif /* CONFIG_IEEE80211BE */
+}
+
#ifdef CONFIG_IEEE80211BE
void wpa_release_link_auth_ref(struct wpa_state_machine *sm, int release_link_id)
{
@@ -139,10 +158,12 @@ static int wpa_get_primary_wpa_auth_cb(struct wpa_authenticator *wpa_auth, void
ctx->wpa_auth = wpa_auth;
return 1;
}
+#endif /* CONFIG_IEEE80211BE */
static struct wpa_authenticator *
wpa_get_primary_wpa_auth(struct wpa_authenticator *wpa_auth)
{
+#ifdef CONFIG_IEEE80211BE
struct wpa_get_link_auth_ctx ctx;
if (!wpa_auth || !wpa_auth->is_ml || wpa_auth->primary_auth)
@@ -153,8 +174,10 @@ wpa_get_primary_wpa_auth(struct wpa_authenticator *wpa_auth)
wpa_auth_for_each_auth(wpa_auth, wpa_get_primary_wpa_auth_cb, &ctx);
return ctx.wpa_auth;
-}
+#else
+ return wpa_auth;
#endif /* CONFIG_IEEE80211BE */
+}
static inline int wpa_auth_mic_failure_report(
struct wpa_authenticator *wpa_auth, const u8 *addr)
@@ -420,15 +443,16 @@ static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx)
}
}
-
-static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
+static void wpa_rekey_all_groups(struct wpa_authenticator *wpa_auth)
{
- struct wpa_authenticator *wpa_auth = eloop_ctx;
struct wpa_group *group, *next;
wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK");
group = wpa_auth->group;
while (group) {
+ wpa_printf(MSG_DEBUG, "GTK rekey start for authenticator("
+ MACSTR "), group vlan %d",
+ MAC2STR(wpa_auth->addr), group->vlan_id);
wpa_group_get(wpa_auth, group);
group->GTKReKey = true;
@@ -441,6 +465,80 @@ static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
wpa_group_put(wpa_auth, group);
group = next;
}
+}
+
+#ifdef CONFIG_IEEE80211BE
+static void wpa_update_all_gtks(struct wpa_authenticator *wpa_auth)
+{
+ struct wpa_group *group, *next;
+
+ group = wpa_auth->group;
+ while (group) {
+ wpa_group_get(wpa_auth, group);
+
+ wpa_group_update_gtk(wpa_auth, group);
+ next = group->next;
+ wpa_group_put(wpa_auth, group);
+ group = next;
+ }
+}
+
+static int wpa_update_all_gtks_cb(struct wpa_authenticator *wpa_auth, void *ctx)
+{
+ u8 *mld_addr = ctx;
+
+ if (os_memcmp(wpa_auth->mld_addr, mld_addr, ETH_ALEN) != 0)
+ return 0;
+
+ wpa_update_all_gtks(wpa_auth);
+ return 0;
+}
+
+static int wpa_rekey_all_groups_cb(struct wpa_authenticator *wpa_auth,
+ void *ctx)
+{
+ u8 *mld_addr = ctx;
+
+ if (os_memcmp(wpa_auth->mld_addr, mld_addr, ETH_ALEN) != 0)
+ return 0;
+
+ wpa_rekey_all_groups(wpa_auth);
+ return 0;
+}
+#endif /* CONFIG_IEEE80211BE */
+
+static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_authenticator *wpa_auth = eloop_ctx;
+
+#ifdef CONFIG_IEEE80211BE
+ if (wpa_auth->is_ml) {
+ /* Non Primary ML authenticator eloop timer for group rekey is never
+ * started and shouldn't fire too check and warn just in case
+ */
+ if (!wpa_auth->primary_auth) {
+ wpa_printf(MSG_DEBUG,
+ "WPA: Can't start GTK rekey on non-primary ML authenticator");
+ return;
+ }
+ /*
+ * Generate all the new I/BIG/GTKs
+ */
+ wpa_auth_for_each_auth(wpa_auth, wpa_update_all_gtks_cb,
+ wpa_auth->mld_addr);
+
+ /*
+ * Send all the generated I/BIG/GTKs to the respective
+ * stations via G1 messages
+ */
+ wpa_auth_for_each_auth(wpa_auth, wpa_rekey_all_groups_cb,
+ wpa_auth->mld_addr);
+ } else {
+ wpa_rekey_all_groups(wpa_auth);
+ }
+#else
+ wpa_rekey_all_groups(wpa_auth);
+#endif /* CONFIG_IEEE80211BE */
if (wpa_auth->conf.wpa_group_rekey) {
eloop_register_timeout(wpa_auth->conf.wpa_group_rekey,
@@ -590,8 +688,19 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
wpa_auth = os_zalloc(sizeof(struct wpa_authenticator));
if (!wpa_auth)
return NULL;
+
os_memcpy(wpa_auth->addr, addr, ETH_ALEN);
os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
+
+#ifdef CONFIG_IEEE80211BE
+ if (conf->mld_addr) {
+ wpa_auth->is_ml = true;
+ wpa_auth->link_id = conf->link_id;
+ wpa_auth->primary_auth = !conf->first_link_auth;
+ os_memcpy(wpa_auth->mld_addr, conf->mld_addr, ETH_ALEN);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
wpa_auth->cb = cb;
wpa_auth->cb_ctx = cb_ctx;
@@ -635,7 +744,15 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
wpa_rekey_gmk, wpa_auth, NULL);
}
+#ifdef CONFIG_IEEE80211BE
+ /* For ML AP, run Group rekey timer only on one link(first) and whenever
+ * it fires do rekey on all associated ML links at one shot.
+ */
+ if ((!wpa_auth->is_ml || !conf->first_link_auth) &&
+ wpa_auth->conf.wpa_group_rekey) {
+#else
if (wpa_auth->conf.wpa_group_rekey) {
+#endif /* CONFIG_IEEE80211BE */
eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0,
wpa_rekey_gtk, wpa_auth, NULL);
}
@@ -699,6 +816,10 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth)
struct wpa_group *group, *prev;
eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL);
+
+ /* TODO: assign ML Primary authenticator to next link auth and
+ * start rekey timer.
+ */
eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
pmksa_cache_auth_deinit(wpa_auth->pmksa);
@@ -868,7 +989,7 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
}
#endif /* CONFIG_P2P */
if (sm->GUpdateStationKeys) {
- sm->group->GKeyDoneStations--;
+ wpa_update_gkeydone(sm, -1);
sm->GUpdateStationKeys = false;
}
#ifdef CONFIG_IEEE80211R_AP
@@ -1669,12 +1790,14 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
LOGGER_INFO,
"received EAPOL-Key Request for GTK rekeying");
- eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
+
+ eloop_cancel_timeout(wpa_rekey_gtk,
+ wpa_get_primary_wpa_auth(wpa_auth), NULL);
if (wpa_auth_gtk_rekey_in_process(wpa_auth))
wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG,
"skip new GTK rekey - already in process");
else
- wpa_rekey_gtk(wpa_auth, NULL);
+ wpa_rekey_gtk(wpa_get_primary_wpa_auth(wpa_auth), NULL);
}
} else {
/* Do not allow the same key replay counter to be reused. */
@@ -2207,7 +2330,7 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
* Reauthentication cancels the pending group key
* update for this STA.
*/
- sm->group->GKeyDoneStations--;
+ wpa_update_gkeydone(sm, -1);
sm->GUpdateStationKeys = false;
sm->PtkGroupInit = true;
}
@@ -2284,7 +2407,7 @@ SM_STATE(WPA_PTK, INITIALIZE)
sm->keycount = 0;
if (sm->GUpdateStationKeys)
- sm->group->GKeyDoneStations--;
+ wpa_update_gkeydone(sm, -1);
sm->GUpdateStationKeys = false;
if (sm->wpa == WPA_VERSION_WPA)
sm->PInitAKeys = false;
@@ -4058,41 +4181,54 @@ static void wpa_auth_get_ml_key_info(struct wpa_authenticator *wpa_auth,
wpa_auth->cb->get_ml_key_info(wpa_auth->cb_ctx, info);
}
+#define KDE_HDR_LEN (1 + 1 + RSN_SELECTOR_LEN)
static size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm)
{
- struct wpa_authenticator *wpa_auth = sm->wpa_auth;
- struct wpa_group *gsm = sm->group;
- size_t gtk_len = gsm->GTK_len;
- size_t igtk_len;
- size_t kde_len;
- unsigned int n_links;
+ struct wpa_authenticator *wpa_auth;
+ size_t kde_len = 0;
+ int link_id;
if (sm->mld_assoc_link_id < 0)
return 0;
- n_links = sm->n_mld_affiliated_links + 1;
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ if (!sm->mld_links[link_id].valid)
+ continue;
+
+ wpa_auth = sm->mld_links[link_id].wpa_auth;
+ if (!wpa_auth || !wpa_auth->group)
+ continue;
- /* MLO GTK KDE for each link */
- kde_len = n_links * (2 + RSN_SELECTOR_LEN + 1 + 6 + gtk_len);
+ /* MLO GTK KDE
+ * Header + Key-idx and Link-id + PN
+ */
+ kde_len += (KDE_HDR_LEN + 1 + WPA_MLO_GTK_KDE_PN_LEN);
+ kde_len += wpa_auth->group->GTK_len;
- if (!sm->mgmt_frame_prot)
- return kde_len;
+ if (!sm->mgmt_frame_prot)
+ continue;
- /* MLO IGTK KDE for each link */
- igtk_len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
- kde_len += n_links * (2 + RSN_SELECTOR_LEN + 2 + 6 + 1 + igtk_len);
+ if (wpa_auth->conf.tx_bss_auth)
+ wpa_auth = wpa_auth->conf.tx_bss_auth;
- if (wpa_auth->conf.tx_bss_auth) {
- wpa_auth = wpa_auth->conf.tx_bss_auth;
- igtk_len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
- }
+ /* MLO IGTK KDE
+ * Header + Key-idx & IPN + Link-id
+ */
+ kde_len += (KDE_HDR_LEN + WPA_IGTK_KDE_PREFIX_LEN + 1);
+ kde_len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
- if (!wpa_auth->conf.beacon_prot)
- return kde_len;
+ if (!wpa_auth->conf.beacon_prot)
+ continue;
+
+ /* MLO BIGTK KDE
+ * Header + Key-idx & IPN + Link-id
+ */
+ kde_len += (KDE_HDR_LEN + WPA_BIGTK_KDE_PREFIX_LEN + 1);
+ kde_len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
+ }
- /* MLO BIGTK KDE for each link */
- kde_len += n_links * (2 + RSN_SELECTOR_LEN + 2 + 6 + 1 + igtk_len);
+ wpa_printf(MSG_DEBUG, "MLO Group kdes len = %zu", kde_len);
return kde_len;
}
@@ -4102,6 +4238,7 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
{
struct wpa_auth_ml_key_info ml_key_info;
unsigned int i, link_id;
+ u8 *start = pos;
/* First fetch the key information from all the authenticators */
os_memset(&ml_key_info, 0, sizeof(ml_key_info));
@@ -4153,8 +4290,10 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
i++;
}
- if (!sm->mgmt_frame_prot)
+ if (!sm->mgmt_frame_prot) {
+ wpa_printf(MSG_DEBUG, "RSN: MLO Group kde len = %ld", pos - start);
return pos;
+ }
/* Add MLO IGTK KDEs */
for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
@@ -4193,8 +4332,10 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
i++;
}
- if (!sm->wpa_auth->conf.beacon_prot)
+ if (!sm->wpa_auth->conf.beacon_prot) {
+ wpa_printf(MSG_DEBUG, "RSN: MLO Group kde len = %ld", pos - start);
return pos;
+ }
/* Add MLO BIGTK KDEs */
for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
@@ -4234,6 +4375,7 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
i++;
}
+ wpa_printf(MSG_DEBUG, "RSN: MLO Group kde len = %ld", pos - start);
return pos;
}
@@ -4274,6 +4416,7 @@ static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos)
{
#ifdef CONFIG_IEEE80211BE
u8 link_id;
+ u8 *start = pos;
if (sm->mld_assoc_link_id < 0)
return pos;
@@ -4324,6 +4467,7 @@ static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos)
}
}
+ wpa_printf(MSG_DEBUG, "RSN: MLO Link kde len = %ld", pos - start);
pos = wpa_auth_ml_group_kdes(sm, pos);
#endif /* CONFIG_IEEE80211BE */
@@ -5106,7 +5250,7 @@ SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED)
#endif /* CONFIG_OCV */
if (sm->GUpdateStationKeys)
- sm->group->GKeyDoneStations--;
+ wpa_update_gkeydone(sm, -1);
sm->GUpdateStationKeys = false;
sm->GTimeoutCtr = 0;
/* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */
@@ -5121,7 +5265,7 @@ SM_STATE(WPA_PTK_GROUP, KEYERROR)
{
SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group);
if (sm->GUpdateStationKeys)
- sm->group->GKeyDoneStations--;
+ wpa_update_gkeydone(sm, -1);
sm->GUpdateStationKeys = false;
sm->Disconnect = true;
sm->disconnect_reason = WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT;
@@ -5415,18 +5559,11 @@ int wpa_wnmsleep_bigtk_subelem(struct wpa_state_machine *sm, u8 *pos)
#endif /* CONFIG_WNM_AP */
-
-static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
- struct wpa_group *group)
+static void wpa_group_update_gtk(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
{
int tmp;
- wpa_printf(MSG_DEBUG,
- "WPA: group state machine entering state SETKEYS (VLAN-ID %d)",
- group->vlan_id);
- group->changed = true;
- group->wpa_group_state = WPA_GROUP_SETKEYS;
- group->GTKReKey = false;
tmp = group->GM;
group->GM = group->GN;
group->GN = tmp;
@@ -5440,6 +5577,24 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
* counting the STAs that are marked with GUpdateStationKeys instead of
* including all STAs that could be in not-yet-completed state. */
wpa_gtk_update(wpa_auth, group);
+}
+
+static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
+{
+ wpa_printf(MSG_DEBUG,
+ "WPA: group state machine entering state SETKEYS (VLAN-ID %d)",
+ group->vlan_id);
+ group->changed = true;
+ group->wpa_group_state = WPA_GROUP_SETKEYS;
+ group->GTKReKey = false;
+
+#ifdef CONFIG_IEEE80211BE
+ if (wpa_auth->is_ml)
+ goto skip_update;
+#endif /* CONFIG_IEEE80211BE */
+
+ wpa_group_update_gtk(wpa_auth, group);
if (group->GKeyDoneStations) {
wpa_printf(MSG_DEBUG,
@@ -5447,6 +5602,10 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
group->GKeyDoneStations);
group->GKeyDoneStations = 0;
}
+
+#ifdef CONFIG_IEEE80211BE
+skip_update:
+#endif /* CONFIG_IEEE80211BE */
wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group);
wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d",
group->GKeyDoneStations);
@@ -5564,6 +5723,57 @@ static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
}
}
+static void wpa_mark_group_change(struct wpa_state_machine *sm, bool change)
+{
+#ifdef CONFIG_IEEE80211BE
+ int link_id;
+#endif /* CONFIG_IEEE80211BE */
+
+ if (!sm || !sm->wpa_auth)
+ return;
+ sm->wpa_auth->group->changed = change;
+
+#ifdef CONFIG_IEEE80211BE
+ for_each_sm_auth(sm, link_id)
+ sm->mld_links[link_id].wpa_auth->group->changed = change;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+static void wpa_group_sm_step_links(struct wpa_state_machine *sm)
+{
+#ifdef CONFIG_IEEE80211BE
+ int link_id;
+#endif /* CONFIG_IEEE80211BE */
+
+ if (!sm || !sm->wpa_auth)
+ return;
+ wpa_group_sm_step(sm->wpa_auth, sm->wpa_auth->group);
+
+#ifdef CONFIG_IEEE80211BE
+ for_each_sm_auth(sm, link_id)
+ wpa_group_sm_step(sm->mld_links[link_id].wpa_auth,
+ sm->mld_links[link_id].wpa_auth->group);
+#endif /* CONFIG_IEEE80211BE */
+}
+
+static bool wpa_group_sm_changed(struct wpa_state_machine *sm)
+{
+#ifdef CONFIG_IEEE80211BE
+ int link_id;
+#endif /* CONFIG_IEEE80211BE */
+ bool changed;
+
+ if (!sm || !sm->wpa_auth)
+ return false;
+ changed = sm->wpa_auth->group->changed;
+
+#ifdef CONFIG_IEEE80211BE
+ for_each_sm_auth(sm, link_id)
+ changed |= sm->mld_links[link_id].wpa_auth->group->changed;
+#endif /* CONFIG_IEEE80211BE */
+
+ return changed;
+}
static int wpa_sm_step(struct wpa_state_machine *sm)
{
@@ -5584,7 +5794,7 @@ static int wpa_sm_step(struct wpa_state_machine *sm)
break;
sm->changed = false;
- sm->wpa_auth->group->changed = false;
+ wpa_mark_group_change(sm, false);
SM_STEP_RUN(WPA_PTK);
if (sm->pending_deinit)
@@ -5592,8 +5802,8 @@ static int wpa_sm_step(struct wpa_state_machine *sm)
SM_STEP_RUN(WPA_PTK_GROUP);
if (sm->pending_deinit)
break;
- wpa_group_sm_step(sm->wpa_auth, sm->group);
- } while (sm->changed || sm->wpa_auth->group->changed);
+ wpa_group_sm_step_links(sm);
+ } while (sm->changed || wpa_group_sm_changed(sm));
sm->in_step_loop = 0;
if (sm->pending_deinit) {
@@ -6807,8 +7017,10 @@ int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth)
{
if (!wpa_auth)
return -1;
- eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
- return eloop_register_timeout(0, 0, wpa_rekey_gtk, wpa_auth, NULL);
+ eloop_cancel_timeout(wpa_rekey_gtk,
+ wpa_get_primary_wpa_auth(wpa_auth), NULL);
+ return eloop_register_timeout(0, 0, wpa_rekey_gtk,
+ wpa_get_primary_wpa_auth(wpa_auth), NULL);
}
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 1446872f3..331d217b5 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -285,6 +285,12 @@ struct wpa_auth_config {
* Set only in nontransmitted BSSs, i.e., is NULL for transmitted BSS
* and in BSSs that are not part of a Multi-BSSID set. */
struct wpa_authenticator *tx_bss_auth;
+
+#ifdef CONFIG_IEEE80211BE
+ u8 *mld_addr;
+ int link_id;
+ struct wpa_authenticator *first_link_auth;
+#endif /* CONFIG_IEEE80211BE */
};
typedef enum {
@@ -429,7 +435,8 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
const u8 *wpa_ie, size_t wpa_ie_len,
const u8 *rsnxe, size_t rsnxe_len,
const u8 *mdie, size_t mdie_len,
- const u8 *owe_dh, size_t owe_dh_len);
+ const u8 *owe_dh, size_t owe_dh_len,
+ struct wpa_state_machine *assoc_sm);
int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
const u8 *osen_ie, size_t osen_ie_len);
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index d3cd44695..1726c7201 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -1713,6 +1713,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &_conf);
_conf.msg_ctx = hapd->msg_ctx;
+
tx_bss = hostapd_mbssid_get_tx_bss(hapd);
if (tx_bss != hapd)
_conf.tx_bss_auth = tx_bss->wpa_auth;
@@ -1753,6 +1754,27 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
!!(hapd->iface->drv_flags2 &
WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP);
+#ifdef CONFIG_IEEE80211BE
+ _conf.mld_addr = NULL;
+ _conf.link_id = -1;
+ _conf.first_link_auth = NULL;
+
+ if (hapd->conf->mld_ap) {
+ struct hostapd_data *lhapd;
+
+ _conf.mld_addr = hapd->mld->mld_addr;
+ _conf.link_id = hapd->mld_link_id;
+
+ for_each_mld_link(lhapd, hapd) {
+ if (lhapd == hapd)
+ continue;
+
+ if (lhapd->wpa_auth)
+ _conf.first_link_auth = lhapd->wpa_auth;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd);
if (hapd->wpa_auth == NULL) {
wpa_printf(MSG_ERROR, "WPA initialization failed.");
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 9ba90749d..29bb66733 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -176,6 +176,7 @@ struct wpa_state_machine {
u8 peer_mld_addr[ETH_ALEN];
s8 mld_assoc_link_id;
u8 n_mld_affiliated_links;
+ u16 valid_links;
struct mld_link {
bool valid;
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index a5f2861c9..bf2303e4f 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -608,7 +608,8 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
const u8 *wpa_ie, size_t wpa_ie_len,
const u8 *rsnxe, size_t rsnxe_len,
const u8 *mdie, size_t mdie_len,
- const u8 *owe_dh, size_t owe_dh_len)
+ const u8 *owe_dh, size_t owe_dh_len,
+ struct wpa_state_machine *assoc_sm)
{
struct wpa_auth_config *conf = &wpa_auth->conf;
struct wpa_ie_data data;
@@ -956,6 +957,15 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
else
sm->wpa = WPA_VERSION_WPA;
+ if (assoc_sm) {
+ /* For ML Association Link STA cannot choose a different
+ * akm or pairwise cipher from assoc STA
+ */
+ if (sm->wpa_key_mgmt != assoc_sm->wpa_key_mgmt)
+ return WPA_INVALID_AKMP;
+ if (sm->pairwise != assoc_sm->pairwise)
+ return WPA_INVALID_PAIRWISE;
+ }
#if defined(CONFIG_IEEE80211R_AP) && defined(CONFIG_FILS)
if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256 ||
sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) &&
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 01efeea3a..24ceed600 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -24,6 +24,7 @@
#define WPA_PASN_PMK_LEN 32
#define WPA_PASN_MAX_MIC_LEN 24
#define WPA_MAX_RSNXE_LEN 4
+#define WPA_MLO_GTK_KDE_PN_LEN 6
#define OWE_DH_GROUP 19
diff --git a/tests/fuzzing/eapol-key-auth/eapol-key-auth.c b/tests/fuzzing/eapol-key-auth/eapol-key-auth.c
index bb46422c6..17f69fd76 100644
--- a/tests/fuzzing/eapol-key-auth/eapol-key-auth.c
+++ b/tests/fuzzing/eapol-key-auth/eapol-key-auth.c
@@ -262,7 +262,7 @@ static int auth_init(struct wpa *wpa)
}
if (wpa_validate_wpa_ie(wpa->auth_group, wpa->auth, 2412, supp_ie,
- supp_ie_len, NULL, 0, NULL, 0, NULL, 0) !=
+ supp_ie_len, NULL, 0, NULL, 0, NULL, 0, NULL) !=
WPA_IE_OK) {
wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed");
return -1;
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index 554268a47..2d06f1a6a 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -484,7 +484,7 @@ static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn,
"\x00\x0f\xac\x04"
"\x01\x00\x00\x0f\xac\x04"
"\x01\x00\x00\x0f\xac\x02"
- "\x00\x00", 22, NULL, 0, NULL, 0, NULL, 0) !=
+ "\x00\x00", 22, NULL, 0, NULL, 0, NULL, 0, NULL) !=
WPA_IE_OK) {
wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed");
return -1;
--
2.39.2