[PATCH 05/20] AP: Advertise IEEE 802.11bn Security Profile element

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


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

Add a new configuration option to hostapd and to the WPA
authenticator to enable the usage of Security profiles. When enabled:

- The WPA authenticator tries to find if there are security profiles
  matching its current configuration.
- If there are one or more matches, a Security profile element would
  be added to beacons frames, probe response frames, containing the
  information about the security profiles supported the AP.

Signed-off-by: Ilan Peer <ilan.peer at intel.com>
---
 hostapd/config_file.c      |   2 +
 hostapd/hostapd.conf       |   6 ++
 src/ap/ap_config.c         |   1 +
 src/ap/ap_config.h         |   7 ++
 src/ap/beacon.c            |  28 +++++++
 src/ap/ieee802_11.c        |   2 +
 src/ap/ieee802_11.h        |   2 +
 src/ap/ieee802_11_shared.c |  24 ++++++
 src/ap/wpa_auth.h          |   7 ++
 src/ap/wpa_auth_glue.c     |   1 +
 src/ap/wpa_auth_ie.c       | 159 +++++++++++++++++++++++++++++++++++++
 11 files changed, 239 insertions(+)

diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 5e90220048..5006461388 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -2877,6 +2877,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
 	} else if (os_strcmp(buf, "eap_using_authentication_frames") == 0) {
 		bss->eap_using_authentication_frames = atoi(pos);
 #endif /* CONFIG_ENC_ASSOC  */
+	} else if (os_strcmp(buf, "sec_profile_enabled") == 0) {
+		bss->sec_profile_enabled = !!atoi(pos);
 	} else if (os_strcmp(buf, "wpa_group_rekey") == 0) {
 		bss->wpa_group_rekey = atoi(pos);
 		bss->wpa_group_rekey_set = 1;
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 715833ea94..9c09e4e835 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -2655,6 +2655,12 @@ own_ip_addr=127.0.0.1
 # 1 = enabled
 #eap_using_authentication_frames=0
 
+# IEEE 802.11bn Security Profile element (Table 9-bb14): Support the usage of
+# Security Profile element as defined in section 37.32 in Draft IEEE802.11bn/D1.4
+# 0 = disabled (default)
+# 1 = enabled
+#sec_profile_enabled=0
+
 ##### Neighbor table ##########################################################
 # Maximum number of entries kept in AP table (either for neighbor table or for
 # detecting Overlapping Legacy BSS Condition). The oldest entry will be
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 36a4dad656..78399d6864 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -185,6 +185,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
 	bss->pmksa_caching_privacy = 0;
 	bss->eap_using_authentication_frames = 0;
 #endif /* CONFIG_ENC_ASSOC */
+	bss->sec_profile_enabled = false;
 }
 
 
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 18a5c469b4..710ad0e920 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -1008,6 +1008,13 @@ struct hostapd_bss_config {
 	unsigned int pmksa_caching_privacy:1;
 	unsigned int eap_using_authentication_frames:1;
 #endif /* CONFIG_ENC_ASSOC  */
+
+	/*
+	 * Advertise Security Profile element when the BSS configuration
+	 * matches one or more entries defined in Table 9-bb14 in Draft
+	 * P802.11bn/D1.4.
+	 */
+	bool sec_profile_enabled;
 };
 
 /**
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index ca59c3dfd4..7af10d1388 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -481,6 +481,27 @@ static size_t hostapd_get_rsnxe_override_len(struct hostapd_data *hapd)
 }
 
 
+size_t hostapd_get_security_profile_len(struct hostapd_data *hapd)
+{
+	const u8 *ie;
+	size_t ies_len;
+
+	if (!(hapd->conf->wpa & WPA_PROTO_RSN) ||
+	    !hapd->conf->sec_profile_enabled || !hapd->wpa_auth)
+		return 0;
+
+	ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ies_len);
+	if (!ie)
+		return 0;
+
+	ie = get_ie_ext(ie, ies_len, WLAN_EID_EXT_SECURITY_PROFILE);
+	if (!ie)
+		return 0;
+
+	return 2 + ie[1];
+}
+
+
 static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid)
 {
 #ifdef CONFIG_TESTING_OPTIONS
@@ -828,6 +849,7 @@ static size_t hostapd_probe_resp_elems_len(struct hostapd_data *hapd,
 	buflen += hostapd_get_rsne_override_len(hapd);
 	buflen += hostapd_get_rsne_override_2_len(hapd);
 	buflen += hostapd_get_rsnxe_override_len(hapd);
+	buflen += hostapd_get_security_profile_len(hapd);
 
 	return buflen;
 }
@@ -996,6 +1018,8 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
 	}
 #endif /* CONFIG_IEEE80211BN */
 
+	pos = hostapd_eid_security_profile(hapd, pos, epos - pos);
+
 #ifdef CONFIG_IEEE80211AC
 	if (hapd->conf->vendor_vht)
 		pos = hostapd_eid_vendor_vht(hapd, pos);
@@ -2333,6 +2357,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
 	tail_len += hostapd_get_rsne_override_len(hapd);
 	tail_len += hostapd_get_rsne_override_2_len(hapd);
 	tail_len += hostapd_get_rsnxe_override_len(hapd);
+	tail_len += hostapd_get_security_profile_len(hapd);
 
 	tailpos = tail = os_malloc(tail_len);
 	if (head == NULL || tail == NULL) {
@@ -2528,6 +2553,9 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
 	}
 #endif /* CONFIG_IEEE80211BN */
 
+	tailpos = hostapd_eid_security_profile(hapd, tailpos,
+					       tailend - tailpos);
+
 #ifdef CONFIG_IEEE80211AC
 	if (hapd->conf->vendor_vht)
 		tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index c12bab0cb1..feebcbcd2a 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -6957,6 +6957,8 @@ rsnxe_done:
 	}
 #endif /* CONFIG_DPP2 */
 
+	p = hostapd_eid_security_profile(hapd, p, buf + buflen - p);
+
 #ifdef CONFIG_IEEE80211AC
 	if (sta && hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT))
 		p = hostapd_eid_vendor_vht(hapd, p);
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 32db78f471..1f2c1ee7b9 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -265,6 +265,8 @@ int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth,
 
 void auth_sae_process_commit(void *eloop_ctx, void *user_ctx);
 u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len);
+u8 * hostapd_eid_security_profile(struct hostapd_data *hapd, u8 *eid,
+				  size_t len);
 u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
 		    const u8 *ext_capab_ie, size_t ext_capab_ie_len);
 size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type,
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 831fbb0978..230271777a 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -10,6 +10,7 @@
 
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
 #include "common/ocv.h"
 #include "common/wpa_ctrl.h"
 #include "hostapd.h"
@@ -1199,6 +1200,29 @@ u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len)
 }
 
 
+u8 * hostapd_eid_security_profile(struct hostapd_data *hapd, u8 *eid,
+				  size_t len)
+{
+	const u8 *ies, *ie;
+	size_t ies_len;
+
+	if (!(hapd->conf->wpa & WPA_PROTO_RSN) ||
+	    !hapd->conf->sec_profile_enabled || !hapd->wpa_auth)
+		return eid;
+
+	ies = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ies_len);
+	if (!ies)
+		return eid;
+
+	ie = get_ie_ext(ies, ies_len, WLAN_EID_EXT_SECURITY_PROFILE);
+	if (!ie || 2U + ie[1] > len)
+		return eid;
+
+	os_memcpy(eid, ie, 2 + ie[1]);
+	return eid + 2 + ie[1];
+}
+
+
 u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
 		    const u8 *ext_capab_ie, size_t ext_capab_ie_len)
 {
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 97f55e67a1..7efaff88f6 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -300,6 +300,13 @@ struct wpa_auth_config {
 #endif /* CONFIG_PASN */
 #endif /* CONFIG_ENC_ASSOC */
 
+	/*
+	 * Advertise Security Profile element when the BSS configuration
+	 * matches one or more entries defined in Table 9-bb14 in Draft
+	 * P802.11bn/D1.4.
+	 */
+	 bool sec_profile_enabled;
+
 	int owe_ptk_workaround;
 	u8 transition_disable;
 #ifdef CONFIG_DPP2
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 5a9d933b24..4b8a4a4c35 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -124,6 +124,7 @@ static void hostapd_wpa_auth_conf(struct hostapd_iface *iface,
 	wconf->eap_using_authentication_frames =
 		conf->eap_using_authentication_frames;
 #endif /* CONFIG_ENC_ASSOC */
+	wconf->sec_profile_enabled = conf->sec_profile_enabled;
 	wconf->extended_key_id = conf->extended_key_id;
 	wconf->wpa_key_mgmt = conf->wpa_key_mgmt;
 	wconf->rsn_override_key_mgmt = conf->rsn_override_key_mgmt;
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index ff797f73e9..50327b6d62 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -629,6 +629,156 @@ int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len)
 }
 
 
+/*
+ * wpa_match_security_profiles - Match configured security parameters with
+ * predefined security profiles and return a bitmap of matching profiles.
+ *
+ * @conf: WPA authentication configuration to match against the profiles.
+ * Returns: Bitmap of matching security profile numbers (bit N set means
+ * profile N matched).
+ *
+ * A profile is considered supported if the configured AKM bitmask contains
+ * the AKM of the profile (and akm2, if non-zero), if the configured
+ * rsn_pairwise cipher mask contains the profile's pairwise cipher, and if
+ * the AP's MFP, IEEE 802.1X-in-Authentication-frame, (Re)Association frame
+ * encryption / PMKSA caching privacy, and KEK-in-PASN settings match the
+ * profile.
+ */
+static u16 wpa_match_security_profiles(struct wpa_auth_config *conf)
+{
+	bool mfp, dot1x_in_auth_frame = false;
+	bool assoc_frame_enc_and_pmksa_privacy = false;
+	bool kek_in_pasn = false;
+	u16 bitmap = 0;
+	size_t i;
+
+	mfp = conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED;
+
+#ifdef CONFIG_ENC_ASSOC
+	dot1x_in_auth_frame = conf->eap_using_authentication_frames;
+	assoc_frame_enc_and_pmksa_privacy = conf->assoc_frame_encryption &&
+		conf->pmksa_caching_privacy;
+	kek_in_pasn = !!(conf->wpa_key_mgmt & WPA_KEY_MGMT_EPPKE);
+#endif /* CONFIG_ENC_ASSOC */
+
+	wpa_printf(MSG_DEBUG,
+		   "RSN: Match security profiles: wpa_key_mgmt=0x%x rsn_pairwise=0x%x mfp=%d dot1x_in_auth_frame=%d assoc_frame_enc_and_pmksa_privacy=%d kek_in_pasn=%d",
+		   conf->wpa_key_mgmt, conf->rsn_pairwise, mfp,
+		   dot1x_in_auth_frame, assoc_frame_enc_and_pmksa_privacy,
+		   kek_in_pasn);
+
+	for (i = 0; i < ARRAY_SIZE(g_security_profiles); i++) {
+		const struct ieee80211_security_profile *p =
+			&g_security_profiles[i];
+
+		if ((conf->wpa_key_mgmt & p->akm) != p->akm) {
+			wpa_printf(MSG_EXCESSIVE,
+				   "RSN: Skip security profile %u: akm 0x%x not supported",
+				   p->number, p->akm);
+			continue;
+		}
+		if (p->akm2 && (conf->wpa_key_mgmt & p->akm2) != p->akm2) {
+			wpa_printf(MSG_EXCESSIVE,
+				   "RSN: Skip security profile %u: akm2 0x%x not supported",
+				   p->number, p->akm2);
+			continue;
+		}
+		if ((conf->rsn_pairwise & p->pairwise_cipher) !=
+		    p->pairwise_cipher) {
+			wpa_printf(MSG_EXCESSIVE,
+				   "RSN: Skip security profile %u: pairwise cipher 0x%x not supported",
+				   p->number, p->pairwise_cipher);
+			continue;
+		}
+		if (p->mfp != mfp ||
+		    p->dot1x_in_auth_frame != dot1x_in_auth_frame ||
+		    p->assoc_frame_enc_and_pmksa_privacy !=
+		    assoc_frame_enc_and_pmksa_privacy ||
+		    p->kek_in_pasn != kek_in_pasn) {
+			wpa_printf(MSG_EXCESSIVE,
+				   "RSN: Skip security profile %u: capability mismatch (mfp=%d dot1x=%d enc_priv=%d kek_pasn=%d)",
+				   p->number, p->mfp, p->dot1x_in_auth_frame,
+				   p->assoc_frame_enc_and_pmksa_privacy,
+				   p->kek_in_pasn);
+			continue;
+		}
+
+		wpa_printf(MSG_DEBUG, "RSN: Security profile %u matched",
+			   p->number);
+		bitmap |= BIT(p->number);
+	}
+
+	wpa_printf(MSG_DEBUG, "RSN: Security profile bitmap: 0x%04x", bitmap);
+
+	return bitmap;
+}
+
+/*
+ * wpa_write_security_profile - Write Security Profile element
+ *
+ * @conf: WPA authentication configuration to match against the profiles
+ * @buf: Buffer to which the Security Profile element will be written
+ * @len: Length of the @buf buffer
+ * Returns: The length of the Security Profile element written to @buf,
+ * or -1 on error
+ */
+static int wpa_write_security_profile(struct wpa_auth_config *conf,
+				      u8 *buf, size_t len)
+{
+	u8 *pos = buf;
+	u16 profile_bitmap = wpa_match_security_profiles(conf);
+	u8 reduced_rsn_capab = 0;
+	u8 bitmap_octets;
+	u8 max_profile;
+	size_t elen;
+
+	if (!profile_bitmap)
+		return 0;
+
+	/* Find highest set bit to determine max profile number */
+	max_profile = 0;
+	while (profile_bitmap >> (max_profile + 1))
+		max_profile++;
+
+	bitmap_octets = (max_profile / 8) + 1;
+
+	/*
+	 * Element ID + Length + Element ID Extension +
+	 * Reduced RSN Capabilities + Security Profile Indication +
+	 * Security Profile Bitmap.
+	 */
+	elen = 1 + 1 + 1 + 1 + 1 + bitmap_octets;
+	if (len < elen)
+		return -1;
+
+	if (conf->extended_key_id)
+		reduced_rsn_capab |=
+			SECURITY_PROFILE_REDUCED_RSN_CAPAB_EXT_KEY_ID;
+
+#ifdef CONFIG_OCV
+	if (conf->ocv)
+		reduced_rsn_capab |= SECURITY_PROFILE_REDUCED_RSN_CAPAB_OCVC;
+#endif /* CONFIG_OCV */
+
+	*pos++ = WLAN_EID_EXTENSION;
+	*pos++ = elen - 2;
+	*pos++ = WLAN_EID_EXT_SECURITY_PROFILE;
+	*pos++ = reduced_rsn_capab;
+
+	/*
+	 * Security Profile Indication: number of bitmap octets, no vendor
+	 * specific entries.
+	 */
+	*pos++ = (bitmap_octets <<
+		  SECURITY_PROFILE_IND_OCTETS_BITMAP_SHIFT) &
+		SECURITY_PROFILE_IND_OCTETS_BITMAP_MASK;
+	*pos++ = profile_bitmap & 0xff;
+	if (bitmap_octets > 1)
+		*pos++ = (profile_bitmap >> 8) & 0xff;
+
+	return pos - buf;
+}
+
 static int wpa_write_rsnxe_override(struct wpa_auth_config *conf, u8 *buf,
 				    size_t len)
 {
@@ -738,6 +888,15 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
 #ifdef CONFIG_TESTING_OPTIONS
 fte:
 #endif /* CONFIG_TESTING_OPTIONS */
+	if ((wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+	    wpa_auth->conf.sec_profile_enabled) {
+		res = wpa_write_security_profile(&wpa_auth->conf, pos,
+						 buf + sizeof(buf) -
+						 pos);
+		if (res < 0)
+			return res;
+		pos += res;
+	}
 #ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) {
 		res = wpa_write_mdie(&wpa_auth->conf, pos,
-- 
2.53.0




More information about the Hostap mailing list