[PATCH] AP MLD: never keep a per-link wpa_sm
Benjamin Berg
benjamin at sipsolutions.net
Tue Jun 17 10:39:58 PDT 2025
From: Benjamin Berg <benjamin.berg at intel.com>
The __checK_assoc_ies function needs to create a per-link wpa_sm
temporarily. However, later on sta->wpa_sm should just be a reference to
the main wpa_sm.
Change the code so that the temporary state is self-contained to
__check_assoc_ies. This fixes a memory leak should __check_assoc_ies
return an error.
Also change the function to pass the wpa_sm for the assoc_link instead
of just a link boolean. The internal lookup appears to not have worked
and this also simplifies the code.
Fixes: 5f5db9366cde ("AP: MLO: Process Multi-Link element from (Re)Association Request frame")
Fixes: 84d2a36da02a ("AP MLD: Require same AKM and pairwise cipher for all links")
Signed-off-by: Benjamin Berg <benjamin.berg at intel.com>
---
src/ap/ieee802_11.c | 134 ++++++++++++++++++++++++++------------------
1 file changed, 80 insertions(+), 54 deletions(-)
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index bf36d7f9e3..7551310e4e 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -4220,53 +4220,52 @@ static bool check_sa_query(struct hostapd_data *hapd, struct sta_info *sta,
static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ies, size_t ies_len,
struct ieee802_11_elems *elems, int reassoc,
- bool link)
+ struct wpa_state_machine *assoc_wpa_sm)
{
int resp;
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)
- return resp;
+ goto out;
resp = check_wmm(hapd, sta, elems->wmm, elems->wmm_len);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
resp = check_ext_capab(hapd, sta, elems->ext_capab,
elems->ext_capab_len);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
resp = copy_supp_rates(hapd, sta, elems);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
resp = check_multi_ap(hapd, sta, elems->multi_ap, elems->multi_ap_len);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
resp = copy_sta_ht_capab(hapd, sta, elems->ht_capabilities);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
if (hapd->iconf->ieee80211n && hapd->iconf->require_ht &&
!(sta->flags & WLAN_STA_HT)) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "Station does not support "
"mandatory HT PHY - reject association");
- return WLAN_STATUS_ASSOC_DENIED_NO_HT;
+ resp = WLAN_STATUS_ASSOC_DENIED_NO_HT;
+ goto out;
}
#ifdef CONFIG_IEEE80211AC
if (hapd->iconf->ieee80211ac) {
resp = copy_sta_vht_capab(hapd, sta, elems->vht_capabilities);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
resp = set_sta_vht_opmode(hapd, sta, elems->opmode_notif);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
}
if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht &&
@@ -4274,14 +4273,15 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "Station does not support "
"mandatory VHT PHY - reject association");
- return WLAN_STATUS_ASSOC_DENIED_NO_VHT;
+ resp = WLAN_STATUS_ASSOC_DENIED_NO_VHT;
+ goto out;
}
if (hapd->conf->vendor_vht && !elems->vht_capabilities) {
resp = copy_sta_vendor_vht(hapd, sta, elems->vendor_vht,
elems->vendor_vht_len);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
}
#endif /* CONFIG_IEEE80211AC */
#ifdef CONFIG_IEEE80211AX
@@ -4290,14 +4290,15 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
elems->he_capabilities,
elems->he_capabilities_len);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
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;
+ resp = WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
+ goto out;
}
if (is_6ghz_op_class(hapd->iconf->op_class)) {
@@ -4306,12 +4307,13 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"Station does not support mandatory HE PHY - reject association");
- return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
+ resp = WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
+ goto out;
}
resp = copy_sta_he_6ghz_capab(hapd, sta,
elems->he_6ghz_band_cap);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
}
}
#endif /* CONFIG_IEEE80211AX */
@@ -4323,12 +4325,12 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
elems->eht_capabilities,
elems->eht_capabilities_len);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
- if (!link) {
+ if (!assoc_wpa_sm) {
resp = hostapd_process_ml_assoc_req(hapd, elems, sta);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
}
}
#endif /* CONFIG_IEEE80211BE */
@@ -4376,7 +4378,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
if (sta->wps_ie && wps_validate_assoc_req(sta->wps_ie) < 0) {
wpa_printf(MSG_DEBUG, "WPS: Invalid WPS IE in "
"(Re)Association Request - reject");
- return WLAN_STATUS_INVALID_IE;
+ resp = WLAN_STATUS_INVALID_IE;
+ goto out;
}
} else if (hapd->conf->wps_state && wpa_ie == NULL) {
wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE in "
@@ -4388,7 +4391,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"No WPA/RSN IE in association request");
- return WLAN_STATUS_INVALID_IE;
+ resp = WLAN_STATUS_INVALID_IE;
+ goto out;
}
if (hapd->conf->wpa && wpa_ie) {
@@ -4402,10 +4406,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);
-
+ /* NOTE: For links other than the assoc-link the
+ * separate wpa_sm is only allocated internally to this
+ * function.
+ */
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
sta->addr,
p2p_dev_addr);
@@ -4413,7 +4417,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
if (!sta->wpa_sm) {
wpa_printf(MSG_WARNING,
"Failed to initialize RSN state machine");
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
}
}
@@ -4440,11 +4445,11 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
0,
elems->mdie, elems->mdie_len,
elems->owe_dh, elems->owe_dh_len,
- assoc_sta ? assoc_sta->wpa_sm : NULL,
+ assoc_wpa_sm,
ap_sta_is_mld(hapd, sta));
resp = wpa_res_to_status_code(res);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
if (wpa_auth_uses_mfp(sta->wpa_sm))
sta->flags |= WLAN_STA_MFP;
@@ -4463,17 +4468,18 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
"to use association (not "
"re-association) with FT auth_alg",
MAC2STR(sta->addr));
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
}
resp = wpa_ft_validate_reassoc(sta->wpa_sm, ies,
ies_len);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
}
#endif /* CONFIG_IEEE80211R_AP */
- if (link)
+ if (assoc_wpa_sm)
goto skip_sae_owe;
#ifdef CONFIG_SAE
if (wpa_auth_uses_sae(sta->wpa_sm) && sta->sae &&
@@ -4488,7 +4494,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
wpa_printf(MSG_DEBUG,
"SAE: No PMKSA cache entry found for "
MACSTR, MAC2STR(sta->addr));
- return WLAN_STATUS_INVALID_PMKID;
+ resp = WLAN_STATUS_INVALID_PMKID;
+ goto out;
}
wpa_printf(MSG_DEBUG, "SAE: " MACSTR
" using PMKSA caching", MAC2STR(sta->addr));
@@ -4499,7 +4506,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
wpa_printf(MSG_DEBUG, "SAE: " MACSTR " tried to use "
"SAE AKM after non-SAE auth_alg %u",
MAC2STR(sta->addr), sta->auth_alg);
- return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+ resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+ goto out;
}
if (hapd->conf->sae_pwe == SAE_PWE_BOTH &&
@@ -4510,7 +4518,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
wpa_printf(MSG_INFO, "SAE: " MACSTR
" indicates support for SAE H2E, but did not use it",
MAC2STR(sta->addr));
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
}
#endif /* CONFIG_SAE */
@@ -4521,7 +4530,7 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
resp = owe_process_assoc_req(hapd, sta, elems->owe_dh,
elems->owe_dh_len);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
}
#endif /* CONFIG_OWE */
skip_sae_owe:
@@ -4549,7 +4558,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
elems->owe_dh_len) < 0) {
dpp_pfs_free(sta->dpp_pfs);
sta->dpp_pfs = NULL;
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
}
}
@@ -4565,7 +4575,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
HOSTAPD_LEVEL_INFO,
"Station tried to use TKIP with HT "
"association");
- return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
+ resp = WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
+ goto out;
}
wpa_auth_set_ssid_protection(
@@ -4595,7 +4606,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
wpa_printf(MSG_DEBUG,
"HS 2.0: PMF not negotiated by release %d station "
MACSTR, release, MAC2STR(sta->addr));
- return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+ resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+ goto out;
}
} else {
sta->hs20_ie = NULL;
@@ -4626,7 +4638,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
wpa_printf(MSG_INFO,
"MBO: Reject WPA2 association without PMF");
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
}
#endif /* CONFIG_MBO */
@@ -4643,14 +4656,17 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
if (hostapd_drv_channel_info(hapd, &ci) != 0) {
wpa_printf(MSG_WARNING,
"Failed to get channel info to validate received OCI in FILS (Re)Association Request frame");
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
}
if (get_sta_tx_parameters(sta->wpa_sm,
channel_width_to_int(ci.chanwidth),
ci.seg1_idx, &tx_chanwidth,
- &tx_seg1_idx) < 0)
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ &tx_seg1_idx) < 0) {
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
+ }
res = ocv_verify_tx_params(elems->oci, elems->oci_len, &ci,
tx_chanwidth, tx_seg1_idx);
@@ -4666,7 +4682,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
wpa_msg(hapd->msg_ctx, MSG_INFO, OCV_FAILURE "addr="
MACSTR " frame=fils-reassoc-req error=%s",
MAC2STR(sta->addr), ocv_errorstr);
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
}
}
#endif /* CONFIG_FILS && CONFIG_OCV */
@@ -4705,7 +4722,21 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
hostapd_wfa_capab(hapd, sta, elems->wfa_capab,
elems->wfa_capab + elems->wfa_capab_len);
- return WLAN_STATUS_SUCCESS;
+out:
+ if (resp != WLAN_STATUS_SUCCESS || assoc_wpa_sm) {
+ wpa_auth_sta_deinit(sta->wpa_sm);
+
+ /* Only keep a reference to the main wpa_sm and drop the
+ * per-link instance.
+ * This reference is needed during group rekey handling.
+ */
+ if (resp == WLAN_STATUS_SUCCESS)
+ sta->wpa_sm = assoc_wpa_sm;
+ else
+ sta->wpa_sm = NULL;
+ }
+
+ return resp;
}
@@ -4722,7 +4753,7 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
}
return __check_assoc_ies(hapd, sta, ies, ies_len, &elems, reassoc,
- false);
+ NULL);
}
@@ -4841,7 +4872,8 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
ap_sta_set_mld(sta, true);
- status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc, true);
+ status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc,
+ origin_sta->wpa_sm);
if (status != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_DEBUG, "MLD: link: Element check failed");
goto out;
@@ -4872,12 +4904,6 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
ieee802_11_update_beacons(hapd->iface);
}
- /* Maintain state machine reference on all link STAs, this is needed
- * during group rekey handling.
- */
- wpa_auth_sta_deinit(sta->wpa_sm);
- sta->wpa_sm = origin_sta->wpa_sm;
-
/*
* Do not initialize the EAPOL state machine.
* TODO: Maybe it is needed?
--
2.49.0
More information about the Hostap
mailing list