[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