diff mbox series

[20/50] AP: MLO: Handle ML element during authentication

Message ID 20230215230904.933291-21-andrei.otcheretianski@intel.com
State Changes Requested
Headers show
Series Add basic MLO support for AP | expand

Commit Message

Andrei Otcheretianski Feb. 15, 2023, 11:08 p.m. UTC
In case the AP is an MLD AP, parse the ML element
from the authentication frame, store the relevant
information, and prepare the response ML element.

If the AP is not an MLD AP 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
table 9-69 in IEEE P802.11-REVme/D2.0.

The change currently doesn't support FILS, FT etc.

Signed-off-by: Ilan Peer <ilan.peer@intel.com>
Signed-off-by: Andrei Otcheretianski <andrei.otcheretianski@intel.com>
---
 src/ap/ieee802_11.c     | 120 +++++++++++++++++++++---
 src/ap/ieee802_11.h     |   4 +
 src/ap/ieee802_11_eht.c | 196 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 306 insertions(+), 14 deletions(-)
diff mbox series

Patch

diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index acec8f7620..c964353027 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -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->conf->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,
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index a5b33e7a89..b39ec0cdbe 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -85,6 +85,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 d5d37995ce..a086ed3048 100644
--- a/src/ap/ieee802_11_eht.c
+++ b/src/ap/ieee802_11_eht.c
@@ -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)
@@ -600,3 +601,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);
+}