[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