@@ -396,8 +396,26 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta,
u8 *buf;
size_t rlen;
int reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ const u8 *sa = hapd->own_addr;
+ struct wpabuf *ml_resp = NULL;
- rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len;
+#ifdef CONFIG_IEEE80211BE
+ /*
+ * Once an non-AP MLD station is added to the driver, the addressing
+ * should use MLD address. Thus, use MLD address an not the
+ * handle the translations
+ */
+ if (hapd->conf->mld_ap && sta && sta->mld_info.mld_sta) {
+ sa = hapd->mld_addr;
+
+ ml_resp = hostapd_ml_auth_resp(hapd);
+ if (!ml_resp)
+ return -1;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len +
+ (ml_resp ? wpabuf_len(ml_resp) : 0);
buf = os_zalloc(rlen);
if (buf == NULL)
return -1;
@@ -406,7 +424,7 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta,
reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_AUTH);
os_memcpy(reply->da, dst, ETH_ALEN);
- os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(reply->sa, sa, ETH_ALEN);
os_memcpy(reply->bssid, bssid, ETH_ALEN);
reply->u.auth.auth_alg = host_to_le16(auth_alg);
@@ -416,6 +434,14 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta,
if (ies && ies_len)
os_memcpy(reply->u.auth.variable, ies, ies_len);
+#ifdef CONFIG_IEEE80211BE
+ if (ml_resp)
+ os_memcpy(reply->u.auth.variable + ies_len,
+ wpabuf_head(ml_resp), wpabuf_len(ml_resp));
+
+ wpabuf_free(ml_resp);
+#endif /* CONFIG_IEEE80211BE */
+
wpa_printf(MSG_DEBUG, "authentication reply: STA=" MACSTR
" auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu) (dbg=%s)",
MAC2STR(dst), auth_alg, auth_transaction,
@@ -2747,6 +2773,8 @@ static void handle_auth(struct hostapd_data *hapd,
size_t resp_ies_len = 0;
u16 seq_ctrl;
struct radius_sta rad_info;
+ const u8 *dst, *sa, *bssid;
+ bool mld_sta = false;
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
@@ -2764,6 +2792,21 @@ static void handle_auth(struct hostapd_data *hapd,
}
#endif /* CONFIG_TESTING_OPTIONS */
+ sa = mgmt->sa;
+#ifdef CONFIG_IEEE80211BE
+ /*
+ * Handle MLO authentication before the station is added to hapd and the
+ * driver so that the station MLD address would be used in both hapd and
+ * the driver.
+ */
+ sa = hostapd_process_ml_auth(hapd, mgmt, len);
+ if (sa)
+ mld_sta = true;
+ else
+ sa = mgmt->sa;
+#endif /* CONFIG_IEEE80211BE */
+
+
auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
status_code = le_to_host16(mgmt->u.auth.status_code);
@@ -2779,7 +2822,7 @@ static void handle_auth(struct hostapd_data *hapd,
wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d "
"auth_transaction=%d status_code=%d wep=%d%s "
"seq_ctrl=0x%x%s%s",
- MAC2STR(mgmt->sa), auth_alg, auth_transaction,
+ MAC2STR(sa), auth_alg, auth_transaction,
status_code, !!(fc & WLAN_FC_ISWEP),
challenge ? " challenge" : "",
seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "",
@@ -2845,7 +2888,16 @@ static void handle_auth(struct hostapd_data *hapd,
if (os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) {
wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
- MAC2STR(mgmt->sa));
+ MAC2STR(sa));
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ if (mld_sta &&
+ (!os_memcmp(sa, hapd->own_addr, ETH_ALEN) ||
+ !os_memcmp(sa, hapd->mld_addr, ETH_ALEN))) {
+ wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
+ MAC2STR(sa));
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
@@ -2853,7 +2905,7 @@ static void handle_auth(struct hostapd_data *hapd,
if (hapd->conf->no_auth_if_seen_on) {
struct hostapd_data *other;
- other = sta_track_seen_on(hapd->iface, mgmt->sa,
+ other = sta_track_seen_on(hapd->iface, sa,
hapd->conf->no_auth_if_seen_on);
if (other) {
u8 *pos;
@@ -2862,7 +2914,7 @@ static void handle_auth(struct hostapd_data *hapd,
wpa_printf(MSG_DEBUG, "%s: Reject authentication from "
MACSTR " since STA has been seen on %s",
- hapd->conf->iface, MAC2STR(mgmt->sa),
+ hapd->conf->iface, MAC2STR(sa),
hapd->conf->no_auth_if_seen_on);
resp = WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION;
@@ -2905,12 +2957,12 @@ static void handle_auth(struct hostapd_data *hapd,
}
}
- res = ieee802_11_allowed_address(hapd, mgmt->sa, (const u8 *) mgmt, len,
+ res = ieee802_11_allowed_address(hapd, sa, (const u8 *) mgmt, len,
&rad_info);
if (res == HOSTAPD_ACL_REJECT) {
wpa_msg(hapd->msg_ctx, MSG_DEBUG,
"Ignore Authentication frame from " MACSTR
- " due to ACL reject", MAC2STR(mgmt->sa));
+ " due to ACL reject", MAC2STR(sa));
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
@@ -2920,7 +2972,7 @@ static void handle_auth(struct hostapd_data *hapd,
#ifdef CONFIG_SAE
if (auth_alg == WLAN_AUTH_SAE && !from_queue &&
(auth_transaction == 1 ||
- (auth_transaction == 2 && auth_sae_queued_addr(hapd, mgmt->sa)))) {
+ (auth_transaction == 2 && auth_sae_queued_addr(hapd, sa)))) {
/* Handle SAE Authentication commit message through a queue to
* provide more control for postponing the needed heavy
* processing under a possible DoS attack scenario. In addition,
@@ -2933,7 +2985,7 @@ static void handle_auth(struct hostapd_data *hapd,
}
#endif /* CONFIG_SAE */
- sta = ap_get_sta(hapd, mgmt->sa);
+ sta = ap_get_sta(hapd, sa);
if (sta) {
sta->flags &= ~WLAN_STA_PENDING_FILS_ERP;
sta->ft_over_ds = 0;
@@ -2953,7 +3005,7 @@ static void handle_auth(struct hostapd_data *hapd,
sta->plink_state == PLINK_BLOCKED) {
wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
" is blocked - drop Authentication frame",
- MAC2STR(mgmt->sa));
+ MAC2STR(sa));
return;
}
#endif /* CONFIG_MESH */
@@ -2973,7 +3025,7 @@ static void handle_auth(struct hostapd_data *hapd,
*/
wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
" not yet known - drop Authentication frame",
- MAC2STR(mgmt->sa));
+ MAC2STR(sa));
/*
* Save a copy of the frame so that it can be processed
* if a new peer entry is added shortly after this.
@@ -2985,13 +3037,38 @@ static void handle_auth(struct hostapd_data *hapd,
}
#endif /* CONFIG_MESH */
- sta = ap_sta_add(hapd, mgmt->sa);
+ sta = ap_sta_add(hapd, sa);
if (!sta) {
wpa_printf(MSG_DEBUG, "ap_sta_add() failed");
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
goto fail;
}
}
+
+#ifdef CONFIG_IEEE80211BE
+ if (auth_transaction == 1) {
+ os_memset(&sta->mld_info, 0, sizeof(sta->mld_info));
+
+ if (mld_sta) {
+ u8 link_id = hapd->mld_link_id;
+
+ sta->mld_info.mld_sta = true;
+ sta->mld_assoc_link_id = link_id;
+
+ /*
+ * Set the MLD address as the station address and the
+ * station addresses.
+ */
+ os_memcpy(sta->mld_info.common_info.mld_addr, sa,
+ ETH_ALEN);
+ os_memcpy(sta->mld_info.links[link_id].peer_addr,
+ mgmt->sa, ETH_ALEN);
+ os_memcpy(sta->mld_info.links[link_id].local_addr,
+ hapd->own_addr, ETH_ALEN);
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
sta->last_seq_ctrl = seq_ctrl;
sta->last_subtype = WLAN_FC_STYPE_AUTH;
#ifdef CONFIG_MBO
@@ -3129,7 +3206,22 @@ static void handle_auth(struct hostapd_data *hapd,
}
fail:
- reply_res = send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, auth_alg,
+ dst = mgmt->sa;
+ bssid = mgmt->bssid;
+
+#ifdef CONFIG_IEEE80211BE
+ /*
+ * Once an non-AP MLD station is added to the driver, the addressing
+ * should use MLD address. It is the responsibility of the driver to
+ * handle the translations
+ */
+ if (hapd->conf->mld_ap && sta && sta->mld_info.mld_sta) {
+ dst = sta->addr;
+ bssid = hapd->mld_addr;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ reply_res = send_auth_reply(hapd, sta, dst, bssid, auth_alg,
auth_alg == WLAN_AUTH_SAE ?
auth_transaction : auth_transaction + 1,
resp, resp_ies, resp_ies_len,
@@ -86,6 +86,10 @@ void hostapd_get_eht_capab(struct hostapd_data *hapd,
size_t len);
u8 *hostapd_eid_eht_basic_ml(struct hostapd_data *hapd, u8 *eid,
struct sta_info *info, bool include_mld_id);
+struct wpabuf *hostapd_ml_auth_resp(struct hostapd_data *hapd);
+const u8 *hostapd_process_ml_auth(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len);
int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta);
u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ht_capab);
@@ -11,6 +11,7 @@
#include "hostapd.h"
#include "sta_info.h"
#include "ieee802_11.h"
+#include "crypto/dh_groups.h"
static u16 ieee80211_eht_ppet_size(u16 ppe_thres_hdr, const u8 *phy_cap_info)
@@ -613,3 +614,198 @@ out:
wpabuf_free(buf);
return pos;
}
+
+struct wpabuf *hostapd_ml_auth_resp(struct hostapd_data *hapd)
+{
+ struct wpabuf *buf = wpabuf_alloc(12);
+
+ if (!buf)
+ return NULL;
+
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(buf, 10);
+ wpabuf_put_u8(buf, WLAN_EID_EXT_MULTI_LINK);
+ wpabuf_put_le16(buf, MULTI_LINK_CONTROL_TYPE_BASIC);
+ wpabuf_put_u8(buf, ETH_ALEN + 1);
+ wpabuf_put_data(buf, hapd->mld_addr, ETH_ALEN);
+
+ return buf;
+}
+
+
+static const u8 *auth_skip_fixed_fields(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+ u16 auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
+ u16 status_code = le_to_host16(mgmt->u.auth.status_code);
+ const u8 *pos = mgmt->u.auth.variable;
+
+ /* Skip fixed fields as defined in table 9-41 */
+ switch (auth_alg) {
+ case WLAN_AUTH_OPEN:
+ return pos;
+ case WLAN_AUTH_SAE:
+ if (auth_transaction == 1) {
+ u16 group;
+ size_t prime_len;
+ struct crypto_ec *ec;
+
+ if (status_code == WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG,
+ "EHT: SAE: H2E is mandatory for MLD");
+ goto out;
+ }
+
+ if (status_code != WLAN_STATUS_SAE_HASH_TO_ELEMENT)
+ return pos;
+
+ /* H2E commit message (group, scalar, FFE) */
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG,
+ "EHT: SAE: Group is not present");
+ return NULL;
+ }
+
+ group = WPA_GET_LE16(pos);
+ pos += 2;
+
+ /* TODO: how to parse when the group is unknown? */
+ ec = crypto_ec_init(group);
+ if (!ec) {
+ const struct dh_group *dh =
+ dh_groups_get(group);
+
+ if (!dh) {
+ wpa_printf(MSG_DEBUG,
+ "EHT: SAE: Unknown group=%u",
+ group);
+ return NULL;
+ }
+
+ prime_len = dh->prime_len;
+ } else {
+ prime_len = crypto_ec_prime_len(ec);
+ }
+
+ wpa_printf(MSG_DEBUG, "EHT: SAE: scalar length is %zu",
+ prime_len);
+
+ /* scalar */
+ pos += prime_len;
+
+ if (ec) {
+ pos += prime_len * 2;
+ crypto_ec_deinit(ec);
+ } else {
+ pos += prime_len;
+ }
+
+ if (pos - mgmt->u.auth.variable > (int)len) {
+ wpa_printf(MSG_DEBUG,
+ "EHT: SAE: frame too short");
+ return NULL;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EHT: SAE: remaining auth:",
+ pos,
+ (int)len - (pos - mgmt->u.auth.variable));
+ } else if (auth_transaction == 2) {
+ struct sta_info *sta;
+
+ if (status_code ==
+ WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION)
+ return pos;
+
+ /* send confirm integer */
+ pos += 2;
+
+ /*
+ * At this stage we should already have an MLD station
+ * and actually sa, will be replaced to MLD address by
+ * the kernel.
+ */
+ sta = ap_get_sta(hapd, mgmt->sa);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: No MLD sta for SAE confirm");
+ return NULL;
+ }
+
+ if (!sta->sae || sta->sae->state < SAE_COMMITTED ||
+ !sta->sae->tmp) {
+ if (sta->sae)
+ wpa_printf(MSG_DEBUG,
+ "SAE: Invalid state=%u",
+ sta->sae ?
+ sta->sae->state :
+ SAE_NOTHING);
+ else
+ wpa_printf(MSG_DEBUG,
+ "SAE: state is NULL");
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "SAE: confirm: kck_len=%zu",
+ sta->sae->tmp->kck_len);
+
+ pos += sta->sae->tmp->kck_len;
+
+ if (pos - mgmt->u.auth.variable > (int)len) {
+ wpa_printf(MSG_DEBUG,
+ "EHT: Too short SAE AUTH frame");
+ return NULL;
+ }
+ }
+
+ return pos;
+
+ /* TODO: support additional algorithms */
+ case WLAN_AUTH_FT:
+ case WLAN_AUTH_FILS_SK:
+ case WLAN_AUTH_FILS_SK_PFS:
+ case WLAN_AUTH_FILS_PK:
+ case WLAN_AUTH_PASN:
+ case WLAN_AUTH_LEAP:
+ case WLAN_AUTH_SHARED_KEY:
+ default:
+ break;
+ }
+
+out:
+ wpa_printf(MSG_DEBUG,
+ "TODO: Auth method not supported with MLD (%d)",
+ auth_alg);
+ return NULL;
+}
+
+
+const u8 *hostapd_process_ml_auth(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ struct ieee802_11_elems elems;
+ const u8 *pos;
+
+ if (!hapd->conf->mld_ap)
+ return NULL;
+
+ len -= offsetof(struct ieee80211_mgmt, u.auth.variable);
+
+ pos = auth_skip_fixed_fields(hapd, mgmt, len);
+ if (!pos)
+ return NULL;
+
+ if (ieee802_11_parse_elems(pos,
+ (int)len - (pos - mgmt->u.auth.variable),
+ &elems, 0) == ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Failed parsing Authentication frame");
+ }
+
+ if (!elems.basic_mle || !elems.basic_mle_len)
+ return NULL;
+
+ return get_basic_mle_mld_addr(elems.basic_mle, elems.basic_mle_len);
+}