[PATCH 06/20] AP: Reject first Authentication frame with mismatched security profile

Andrei Otcheretianski andrei.otcheretianski at intel.com
Wed Jun 10 06:11:59 PDT 2026


From: Ilan Peer <ilan.peer at intel.com>

In case security profile support is enabled and the first authentication
frame includes a security profile verify that:

- It contains a single security profile that is supported by the AP.
- The values in the RSN element and the extended RSN element match
  the security profile

If the verification fails, reject the authentication with status code
REJECTED_INVALID_SECURITY_PROFILE.

Signed-off-by: Ilan Peer <ilan.peer at intel.com>
---
 src/ap/ieee802_11.c  |  43 ++++++++++++-
 src/ap/wpa_auth.h    |   5 ++
 src/ap/wpa_auth_ie.c | 148 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 194 insertions(+), 2 deletions(-)

diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index feebcbcd2a..e100dfa2f1 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -4087,9 +4087,8 @@ static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta,
 			     const struct ieee80211_mgmt *mgmt, size_t len,
 			     u16 trans_seq, u16 status)
 {
-	int ret;
-#ifdef CONFIG_P2P
 	struct ieee802_11_elems elems;
+	int ret;
 
 	if (len < 24) {
 		wpa_printf(MSG_DEBUG, "PASN: Too short Management frame");
@@ -4105,6 +4104,7 @@ static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta,
 		return;
 	}
 
+#ifdef CONFIG_P2P
 	if ((hapd->conf->p2p & (P2P_ENABLED | P2P_GROUP_OWNER)) ==
 	    (P2P_ENABLED | P2P_GROUP_OWNER) &&
 	    hapd->p2p && elems.p2p2_ie && elems.p2p2_ie_len) {
@@ -4145,6 +4145,45 @@ static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta,
 		hapd_initialize_pasn(hapd, sta);
 
 		hapd_pasn_update_params(hapd, sta, mgmt, len);
+
+		/*
+		 * When support for security profiles is enabled, need to
+		 * verify that if the first authentication frame carries the
+		 * necessary IEs, they are consistent and match exactly one of
+		 * the supported security profiles.
+		 */
+		if (hapd->conf->sec_profile_enabled) {
+			u16 secp_resp =
+				WLAN_STATUS_REJECTED_INVALID_SECURITY_PROFILE;
+
+			if (elems.security_profile) {
+				if (!elems.rsn_ie ||
+				    !wpa_auth_validate_security_profile(
+					    hapd->wpa_auth,
+					    elems.rsn_ie - 2,
+					    elems.rsn_ie_len + 2,
+					    elems.rsnxe ?
+					    elems.rsnxe - 2 : NULL,
+					    elems.rsnxe ?
+					    elems.rsnxe_len + 2 : 0,
+					    elems.security_profile,
+					    elems.security_profile_len)) {
+					wpa_printf(MSG_INFO,
+						   "RSN: First Authentication frame from "
+						   MACSTR
+						   " does not match any advertised security profile",
+						   MAC2STR(sta->addr));
+
+					send_auth_reply(hapd, sta, sta->addr,
+							mgmt->u.auth.auth_alg,
+							2, secp_resp, NULL, 0,
+							"sec-profile-mismatch");
+					ap_free_sta(hapd, sta);
+					return;
+				}
+			}
+		}
+
 		ret = handle_auth_pasn_1(sta->pasn, hapd->own_addr, sta->addr,
 					 mgmt, len, false);
 		wpabuf_free(sta->pasn->frame);
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 7efaff88f6..249e410caa 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -493,6 +493,11 @@ int wpa_auth_uses_mfp(struct wpa_state_machine *sm);
 int wpa_auth_uses_spp_amsdu(struct wpa_state_machine *sm);
 void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv);
 int wpa_auth_uses_ocv(struct wpa_state_machine *sm);
+bool wpa_auth_validate_security_profile(struct wpa_authenticator *wpa_auth,
+					const u8 *rsne, size_t rsne_len,
+					const u8 *rsnxe, size_t rsnxe_len,
+					const u8 *secp_body,
+					size_t secp_body_len);
 struct wpa_state_machine *
 wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr,
 		  const u8 *p2p_dev_addr);
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 50327b6d62..0a3e2cafc1 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -713,6 +713,154 @@ static u16 wpa_match_security_profiles(struct wpa_auth_config *conf)
 	return bitmap;
 }
 
+
+/*
+ * wpa_auth_validate_security_profile - Validate Security Profile Element
+ *
+ * @wpa_auth: WPA authenticator context
+ * @rsne: Pointer to the RSN element
+ * @rsne_len: Length of the RSN element
+ * @rsnxe: Pointer to the RSNXE element
+ * @rsnxe_len: Length of the RSNXE element
+ * @secp_body: Pointer to the Security Profile element
+ * @secp_body_len: Length of the Security Profile element
+ * Returns: true on match, false on mismatch or error
+ */
+bool wpa_auth_validate_security_profile(struct wpa_authenticator *wpa_auth,
+					const u8 *rsne, size_t rsne_len,
+					const u8 *rsnxe, size_t rsnxe_len,
+					const u8 *secp_body,
+					size_t secp_body_len)
+{
+	u16 profile_bitmap;
+	struct wpa_ie_data data;
+	const struct ieee80211_security_profile *p;
+	const u8 *bitmap;
+	size_t bitmap_octets, bit, bits = 0;
+	u8 match = 0xff;
+	bool mfp;
+
+	/* Sanity check */
+	if (!wpa_auth || !wpa_auth->conf.sec_profile_enabled)
+		return true;
+
+	if (!rsne || !secp_body)
+		return true;
+
+	if (wpa_parse_wpa_ie_rsn(rsne, rsne_len, &data) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: Failed to parse RSNE in for security profile validation");
+		return false;
+	}
+
+	if (secp_body_len < 2) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: Truncated Security Profile element");
+		return false;
+	}
+
+	bitmap_octets = (secp_body[1] &
+			 SECURITY_PROFILE_IND_OCTETS_BITMAP_MASK) >>
+		SECURITY_PROFILE_IND_OCTETS_BITMAP_SHIFT;
+	if (!bitmap_octets || secp_body_len < 2 + bitmap_octets) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: Invalid Security Profile bitmap length");
+		return false;
+	}
+
+	bitmap = &secp_body[2];
+	for (bit = 0; bit < bitmap_octets * 8; bit++) {
+		if (bitmap[bit / 8] & BIT(bit % 8)) {
+			match = bit;
+			bits++;
+		}
+	}
+	if (bits != 1 || match > SECURITY_PROFILE_MAX) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: Security Profile element does not indicate exactly one profile (bits=%zu)",
+			   bits);
+		return false;
+	}
+
+	profile_bitmap = wpa_match_security_profiles(&wpa_auth->conf);
+	if (!(profile_bitmap & BIT(match))) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: Indicated security profile %u not advertised by AP",
+			   match);
+		return false;
+	}
+	p = &g_security_profiles[match];
+
+	if ((data.key_mgmt & (p->akm & ~WPA_KEY_MGMT_EPPKE)) !=
+	    (p->akm & ~WPA_KEY_MGMT_EPPKE) ||
+	    (p->akm2 && (data.key_mgmt & p->akm2) != p->akm2)) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: RSNE AKM 0x%x does not match security profile %u (akm=0x%x akm2=0x%x)",
+			   data.key_mgmt, match, p->akm, p->akm2);
+		return false;
+	}
+
+	if ((data.pairwise_cipher & p->pairwise_cipher) !=
+	    p->pairwise_cipher) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: RSNE pairwise cipher 0x%x does not match security profile %u (cipher=0x%x)",
+			   data.pairwise_cipher, match, p->pairwise_cipher);
+		return false;
+	}
+
+	mfp = (data.capabilities & WPA_CAPABILITY_MFPC) != 0;
+	if (mfp != p->mfp) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: RSNE MFPC=%d does not match security profile %u (mfp=%d)",
+			   mfp, match, p->mfp);
+		return false;
+	}
+
+	/* Validate RSNXE capabilities against the security profile */
+	if (p->assoc_frame_enc_and_pmksa_privacy !=
+	    ieee802_11_rsnx_capab(rsnxe,
+				  WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION)) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: RSNXE assoc frame encryption=%d does not match security profile %u (%d)",
+			   !p->assoc_frame_enc_and_pmksa_privacy,
+			   match, p->assoc_frame_enc_and_pmksa_privacy);
+		return false;
+	}
+
+	if (p->assoc_frame_enc_and_pmksa_privacy !=
+	    ieee802_11_rsnx_capab(rsnxe,
+				  WLAN_RSNX_CAPAB_PMKSA_CACHING_PRIVACY)) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: RSNXE PMKSA caching privacy=%d does not match security profile %u (%d)",
+			   !p->assoc_frame_enc_and_pmksa_privacy,
+			   match, p->assoc_frame_enc_and_pmksa_privacy);
+		return false;
+	}
+
+	if (p->kek_in_pasn !=
+	    ieee802_11_rsnx_capab(rsnxe, WLAN_RSNX_CAPAB_KEK_IN_PASN)) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: RSNXE KEK in PASN=%d does not match security profile %u (%d)",
+			   !p->kek_in_pasn, match, p->kek_in_pasn);
+		return false;
+	}
+
+	if (p->dot1x_in_auth_frame !=
+	    ieee802_11_rsnx_capab(rsnxe,
+				  WLAN_RSNX_CAPAB_802_1X_IN_AUTH_FRAMES)) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: RSNXE 802.1X in auth frames=%d does not match security profile %u (%d)",
+			   !p->dot1x_in_auth_frame,
+			   match, p->dot1x_in_auth_frame);
+		return false;
+	}
+
+	wpa_printf(MSG_DEBUG, "RSN: Security profile %u validation successful",
+		   match);
+	return true;
+}
+
+
 /*
  * wpa_write_security_profile - Write Security Profile element
  *
-- 
2.53.0




More information about the Hostap mailing list