[PATCH 13/20] wpa_supplicant: Prefer matching Security Profile in RSN selection

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


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

When connecting to an AP that advertises a Security Profile element,
wpa_supplicant_set_suites() now searches for a single advertised
profile that is compatible with the configured network and uses it to
constrain RSN parameter selection. If no advertised profile matches,
behavior is unchanged.

Signed-off-by: Ilan Peer <ilan.peer at intel.com>
---
 wpa_supplicant/wpa_supplicant.c | 204 ++++++++++++++++++++++++++++++--
 1 file changed, 192 insertions(+), 12 deletions(-)

diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 043411707c..08bba0bed8 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -1562,6 +1562,142 @@ static int matching_ciphers(struct wpa_ssid *ssid, struct wpa_ie_data *ie,
 }
 
 
+/*
+ * wpas_match_security_profile - match advertised Security Profiles against
+ * the configured network
+ *
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @ssid: Configured network to match against
+ * @secp_ie: Security Profile element from the AP's Beacon or Probe Response
+ * Returns: Pointer to the matching Security Profile or %NULL if no match
+ */
+static const struct ieee80211_security_profile *
+wpas_match_security_profile(struct wpa_supplicant *wpa_s,
+			    struct wpa_ssid *ssid, const u8 *secp_ie)
+{
+	const struct ieee80211_security_profile *best = NULL;
+	int best_score = -1;
+	const u8 *bitmap;
+	size_t bitmap_octets;
+	bool sta_mfp;
+	int i;
+
+	if (!secp_ie || secp_ie[1] < 4)
+		return NULL;
+
+	bitmap_octets = (secp_ie[4] &
+			 SECURITY_PROFILE_IND_OCTETS_BITMAP_MASK) >>
+		SECURITY_PROFILE_IND_OCTETS_BITMAP_SHIFT;
+
+	if (!bitmap_octets || secp_ie[1] < 3 + bitmap_octets)
+		return NULL;
+
+	bitmap = &secp_ie[5];
+	sta_mfp = wpas_get_ssid_pmf(wpa_s, ssid) ==
+		MGMT_FRAME_PROTECTION_REQUIRED;
+
+	for (i = 0; i <= SECURITY_PROFILE_MAX; i++) {
+		const struct ieee80211_security_profile *p;
+		unsigned int akms;
+		int score;
+
+		if ((size_t)(i / 8) >= bitmap_octets)
+			break;
+
+		if (!(bitmap[i / 8] & BIT(i % 8)))
+			continue;
+
+		p = &g_security_profiles[i];
+		if ((ssid->key_mgmt & p->akm) != p->akm)
+			continue;
+
+		if (p->akm2 && (ssid->key_mgmt & p->akm2) != p->akm2)
+			continue;
+
+		if ((ssid->pairwise_cipher & p->pairwise_cipher) !=
+		    p->pairwise_cipher)
+			continue;
+
+		if (p->mfp != sta_mfp)
+			continue;
+
+		if (p->assoc_frame_enc_and_pmksa_privacy != ssid->pmksa_privacy)
+			continue;
+
+		/*
+		 * Skip profiles that require 802.1X EAP in authentication
+		 * frames if driver does not support it
+		 */
+		if (p->dot1x_in_auth_frame &&
+		    !(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_802_1X_AUTH))
+			continue;
+
+		/*
+		 * Prefer the most specific advertised profile. Profiles that
+		 * pin two AKMs (e.g. EPPKE + SAE-EXT-KEY) win over profiles
+		 * that only pin one (e.g. EPPKE alone) when both match the
+		 * configured network.
+		 */
+		akms = p->akm | p->akm2;
+		score = 0;
+		while (akms) {
+			score += akms & 1;
+			akms >>= 1;
+		}
+
+		if (score > best_score) {
+			best = p;
+			best_score = score;
+		}
+	}
+
+	return best;
+}
+
+
+/*
+ * wpas_apply_security_profile - Override RSNE data based Security Profile
+ *
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @ie: Parsed RSNE fields
+ * @p: Matched Security Profile
+ * @reduced_rsn_capab: Reduced RSN Capabilities field from the Security Profile
+ *	element
+ */
+static void
+wpas_apply_security_profile(struct wpa_supplicant *wpa_s,
+			    struct wpa_ie_data *ie,
+			    const struct ieee80211_security_profile *p,
+			    u8 reduced_rsn_capab)
+{
+	wpa_dbg(wpa_s, MSG_DEBUG,
+		"RSN: Advertised Security Profile %u (akm=0x%x akm2=0x%x pairwise=0x%x mfp=%d)",
+		p->number, p->akm, p->akm2, p->pairwise_cipher, p->mfp);
+
+	ie->key_mgmt = p->akm | p->akm2;
+	ie->pairwise_cipher = p->pairwise_cipher;
+
+	/*
+	 * Note that these settings might be set int RSN element but might not
+	 * be set in the security profile, so need to clear them
+	 */
+	ie->capabilities &= ~(WPA_CAPABILITY_MFPR |
+			      WPA_CAPABILITY_MFPC |
+			      WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST |
+			      WPA_CAPABILITY_OCVC);
+	if (p->mfp)
+		ie->capabilities |= WPA_CAPABILITY_MFPR | WPA_CAPABILITY_MFPC;
+
+	if (reduced_rsn_capab & SECURITY_PROFILE_REDUCED_RSN_CAPAB_EXT_KEY_ID)
+		ie->capabilities |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST;
+
+	if (reduced_rsn_capab & SECURITY_PROFILE_REDUCED_RSN_CAPAB_OCVC)
+		ie->capabilities |= WPA_CAPABILITY_OCVC;
+
+	wpa_sm_set_matched_security_profile(wpa_s->wpa, p->number);
+}
+
+
 void wpas_set_mgmt_group_cipher(struct wpa_supplicant *wpa_s,
 				struct wpa_ssid *ssid, struct wpa_ie_data *ie)
 {
@@ -1843,6 +1979,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 	enum sae_pwe sae_pwe;
 #endif /* CONFIG_SAE */
 	const u8 *bss_wpa, *bss_rsn, *bss_rsnx;
+	const u8 *bss_secp = NULL;
+	const struct ieee80211_security_profile *sec_profile = NULL;
 	bool wmm;
 	struct rsn_pmksa_cache_entry *pmksa;
 
@@ -1850,21 +1988,56 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 		bss_wpa = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
 		bss_rsn = wpa_bss_get_rsne(wpa_s, bss, ssid, false);
 		bss_rsnx = wpa_bss_get_rsnxe(wpa_s, bss, ssid, false);
+		bss_secp = wpa_bss_get_ie_ext(
+			bss, WLAN_EID_EXT_SECURITY_PROFILE);
 	} else {
 		bss_wpa = bss_rsn = bss_rsnx = NULL;
 	}
 
+	/* Clear the previously matched Security Profile */
+	wpa_sm_set_matched_security_profile(wpa_s->wpa, -1);
+
 	if (bss_rsn && (ssid->proto & WPA_PROTO_RSN) &&
-	    wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie) == 0 &&
-	    matching_ciphers(ssid, &ie, bss->freq) &&
+		wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1],
+				 &ie) == 0) {
+		/*
+		 * Look up a Security Profile advertised by the AP that
+		 * matches the configured network before validating the
+		 * AP's RSNE so that the matched profile takes preference
+		 * over the RSNE/RSNXE in driving subsequent AKM, pairwise
+		 * cipher etc.
+		 */
+		if (bss_secp && ie.has_group &&
+		    ie.group_cipher == ssid->group_cipher) {
+			sec_profile = wpas_match_security_profile(wpa_s, ssid,
+								  bss_secp);
+			if (sec_profile) {
+				wpas_apply_security_profile(wpa_s, &ie,
+							    sec_profile,
+							    bss_secp[3]);
+				wpa_printf(MSG_DEBUG,
+					   "RSN: Using matched Security Profile %u",
+					   sec_profile->number);
+
+				proto = WPA_PROTO_RSN;
+				goto proto_match_done;
+			}
+		}
+
+		if (matching_ciphers(ssid, &ie, bss->freq) &&
+		    (ie.key_mgmt & ssid->key_mgmt)) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"RSN: using IEEE 802.11i/D9.0");
+			proto = WPA_PROTO_RSN;
+			goto proto_match_done;
+		}
+	}
+
+	if (bss_wpa && (ssid->proto & WPA_PROTO_WPA) &&
+	    wpa_parse_wpa_ie(bss_wpa, 2 + bss_wpa[1], &ie) == 0 &&
+	    (ie.group_cipher & ssid->group_cipher) &&
+	    (ie.pairwise_cipher & ssid->pairwise_cipher) &&
 	    (ie.key_mgmt & ssid->key_mgmt)) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0");
-		proto = WPA_PROTO_RSN;
-	} else if (bss_wpa && (ssid->proto & WPA_PROTO_WPA) &&
-		   wpa_parse_wpa_ie(bss_wpa, 2 + bss_wpa[1], &ie) == 0 &&
-		   (ie.group_cipher & ssid->group_cipher) &&
-		   (ie.pairwise_cipher & ssid->pairwise_cipher) &&
-		   (ie.key_mgmt & ssid->key_mgmt)) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0");
 		proto = WPA_PROTO_WPA;
 	} else if (bss) {
@@ -1947,6 +2120,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 			proto = ie.proto;
 	}
 
+proto_match_done:
 	wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected cipher suites: group %d "
 		"pairwise %d key_mgmt %d proto %d",
 		ie.group_cipher, ie.pairwise_cipher, ie.key_mgmt, proto);
@@ -1962,7 +2136,6 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 
 	if (bss || !wpa_s->ap_ies_from_associnfo) {
 		const u8 *rsnoe = NULL, *rsno2e = NULL, *rsnxoe = NULL;
-		const u8 *bss_secp = NULL;
 
 		if (bss) {
 			bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
@@ -2322,8 +2495,13 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 		 * interoperability issues with incorrect AP behavior if we
 		 * were to send an "unexpected" RSNXE with multiple octets of
 		 * payload. */
-		assoc_enc = ieee802_11_rsnx_capab(
-			bss_rsnx, WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION);
+		if (sec_profile)
+			assoc_enc =
+				sec_profile->assoc_frame_enc_and_pmksa_privacy;
+		else
+			assoc_enc = ieee802_11_rsnx_capab(
+				bss_rsnx,
+				WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION);
 		if (!skip_default_rsne)
 			wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_ASSOC_ENC,
 					 assoc_enc);
@@ -2346,6 +2524,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 			 ssid->sae_password_id && ssid->sae_password_id_change);
 #ifdef CONFIG_PMKSA_PRIVACY
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PMKSA_CACHING_PRIVACY,
+			 sec_profile ?
+			 sec_profile->assoc_frame_enc_and_pmksa_privacy :
 			 ssid->pmksa_privacy);
 #endif /* CONFIG_PMKSA_PRIVACY */
 
-- 
2.53.0




More information about the Hostap mailing list