diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 1436bd4e0..f003a7642 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -396,17 +396,38 @@ 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; + +#ifdef CONFIG_IEEE80211BE + /* + * Once a non-AP MLD is added to the driver, the addressing should use + * the MLD MAC address. Thus, use the MLD address instead of translating + * the addresses. + */ + 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; + if (ml_resp) + rlen += wpabuf_len(ml_resp); buf = os_zalloc(rlen); - if (buf == NULL) + if (!buf) { + wpabuf_free(ml_resp); return -1; + } reply = (struct ieee80211_mgmt *) buf; 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 +437,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, @@ -2748,6 +2777,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)", @@ -2765,6 +2796,20 @@ 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 hostapd and + * the driver so that the station MLD MAC address would be used in both + * hostapd 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); @@ -2780,7 +2825,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" : "", @@ -2846,7 +2891,17 @@ 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) == 0 || + os_memcmp(sa, hapd->mld_addr, ETH_ALEN) == 0)) { + wpa_printf(MSG_INFO, + "Station " MACSTR " not allowed to authenticate", + MAC2STR(sa)); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } @@ -2854,7 +2909,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; @@ -2863,7 +2918,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; @@ -2906,12 +2961,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; } @@ -2921,7 +2976,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, @@ -2934,7 +2989,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; @@ -2954,7 +3009,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 */ @@ -2974,7 +3029,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. @@ -2986,13 +3041,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 @@ -3130,7 +3210,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 a non-AP MLD is added to the driver, the addressing should use + * the MLD MAC 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, diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index b174fdd1f..842881358 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -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); diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c index 79062b003..2c003e3bb 100644 --- a/src/ap/ieee802_11_eht.c +++ b/src/ap/ieee802_11_eht.c @@ -8,6 +8,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "crypto/dh_groups.h" #include "hostapd.h" #include "sta_info.h" #include "ieee802_11.h" @@ -621,3 +622,208 @@ 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 * +sae_commit_skip_fixed_fields(const struct ieee80211_mgmt *mgmt, size_t len, + const u8 *pos, u16 status_code) +{ + u16 group; + size_t prime_len; + struct crypto_ec *ec; + + if (status_code != WLAN_STATUS_SAE_HASH_TO_ELEMENT) + return pos; + + /* SAE 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: Unknown SAE 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: Too short SAE commit Authentication frame"); + return NULL; + } + + wpa_hexdump(MSG_DEBUG, "EHT: SAE: Authentication frame elements", + pos, (int) len - (pos - mgmt->u.auth.variable)); + + return pos; +} + + +static const u8 * +sae_confirm_skip_fixed_fields(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len, + const u8 *pos, u16 status_code) +{ + 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 with the MLD MAC address by the driver. + */ + 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->state); + else + wpa_printf(MSG_DEBUG, "SAE: No SAE context"); + 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 confirm Authentication frame"); + return NULL; + } + + return pos; +} + + +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 based on IEE P802.11-REVme/D3.0, Table 9-69 + * (Presence of fields and elements in Authentications frames) */ + switch (auth_alg) { + case WLAN_AUTH_OPEN: + return pos; + case WLAN_AUTH_SAE: + if (auth_transaction == 1) { + if (status_code == WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, + "EHT: SAE H2E is mandatory for MLD"); + goto out; + } + + return sae_commit_skip_fixed_fields(mgmt, len, pos, + status_code); + } else if (auth_transaction == 2) { + return sae_confirm_skip_fixed_fields(hapd, mgmt, len, + pos, status_code); + } + + return pos; + /* TODO: Support additional algorithms that can be used for MLO */ + case WLAN_AUTH_FT: + case WLAN_AUTH_FILS_SK: + case WLAN_AUTH_FILS_SK_PFS: + case WLAN_AUTH_FILS_PK: + case WLAN_AUTH_PASN: + default: + break; + } + +out: + wpa_printf(MSG_DEBUG, + "TODO: Authentication algorithm %u not supported with MLD", + 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); +}