From bcbe80a66a9b87da49b0af4ecb27f8c696012c57 Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Mon, 22 May 2023 22:33:42 +0300 Subject: [PATCH] AP: MLO: Handle Multi-Link element during authentication In case the AP is an AP MLD, parse the Multi-Link element from the Authentication frame, store the relevant information, and prepare the response Multi-Link element. If the AP is not an AP MLD or the parsing of the element fails, continue the authentication flow without MLD support. For SAE, it is needed to skip various fixed fields in the Authentication frame. Implement it for SAE with H2E. TODO: This should be extended to other authentication algorithms which are allowed for MLD connections and have fixed fields in the Authentication frames, according to IEEE P802.11-REVme/D3.0, Table 9-69 (Presence of fields and elements in Authentications frames). This commit doesn't support FILS, FT, etc. Signed-off-by: Ilan Peer Signed-off-by: Andrei Otcheretianski --- src/ap/ieee802_11.c | 123 +++++++++++++++++++++--- src/ap/ieee802_11.h | 4 + src/ap/ieee802_11_eht.c | 206 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 319 insertions(+), 14 deletions(-) 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); +}