dev[2].connect("test-sae", psk="12345678", ieee80211w="0", scan_freq="2412")
dev[2].dump_monitor()
+def _test_sae_mixed_check_mfp(dev, apdev):
+ """Mixed SAE and non-SAE network with the sae_check_mfp option"""
+ check_sae_capab(dev[0])
+ check_sae_capab(dev[1])
+ check_sae_capab(dev[2])
+
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE WPA-PSK'
+ params["ieee80211w"] = "1"
+ hostapd.add_ap(apdev[0], params)
+
+ params = hostapd.wpa2_params(ssid="test-sae-no-mfp", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE WPA-PSK'
+ params["ieee80211w"] = "0"
+ hostapd.add_ap(apdev[1], params)
+
+ dev[0].set("sae_check_mfp", "0")
+ dev[0].set("sae_groups", "")
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE WPA-PSK",
+ ieee80211w="0", scan_freq="2412")
+ dev[0].dump_monitor()
+ status = dev[0].get_status()
+ if status['key_mgmt'] != "SAE":
+ raise Exception("SAE without sae_check_mfp was not allowed")
+
+ # Confirm SAE is used when sae_check_mfp is not set for non-PMF AP.
+ dev[2].set("sae_check_mfp", "0")
+ dev[2].set("sae_groups", "")
+ dev[2].connect("test-sae-no-mfp", psk="12345678", key_mgmt="SAE WPA-PSK",
+ ieee80211w="1", scan_freq="2412")
+ status = dev[2].get_status()
+ if status['key_mgmt'] != "SAE":
+ raise Exception("SAE without sae_check_mfp was not allowed")
+ dev[2].dump_monitor()
+
+ # Confirm SAE is not used with the PMF disabled network configuration.
+ dev[1].set("sae_check_mfp", "1")
+ dev[1].set("sae_groups", "")
+ dev[1].connect("test-sae", psk="12345678", key_mgmt="SAE WPA-PSK",
+ ieee80211w="0", scan_freq="2412")
+ status = dev[1].get_status()
+ dev[1].request("DISCONNECT")
+ dev[1].wait_disconnected()
+ dev[1].dump_monitor()
+ if status['key_mgmt'] != "WPA2-PSK":
+ raise Exception("SAE without MFP was allowed")
+ dev[1].request("REMOVE_NETWORK all")
+
+ # Confirm SAE is not used connecting to PMF disabled AP.
+ dev[1].set("sae_check_mfp", "1")
+ dev[1].set("sae_groups", "")
+ dev[1].connect("test-sae-no-mfp", psk="12345678", key_mgmt="SAE WPA-PSK",
+ ieee80211w="1", scan_freq="2412")
+ status = dev[1].get_status()
+ if status['key_mgmt'] != "WPA2-PSK":
+ raise Exception("SAE without MFP was allowed")
+
+def test_sae_mixed_check_mfp(dev, apdev):
+ """Mixed SAE and non-SAE network with the sae_check_mfp option"""
+ try:
+ _test_sae_mixed_check_mfp(dev, apdev)
+ finally:
+ dev[0].set("sae_check_mfp", "0")
+ dev[1].set("sae_check_mfp", "0")
+ dev[2].set("sae_check_mfp", "0")
+
def test_sae_and_psk_transition_disable(dev, apdev):
"""SAE and PSK transition disable indication"""
check_sae_capab(dev[0])
{ INT_RANGE(auto_interworking, 0, 1), 0 },
{ INT(okc), 0 },
{ INT(pmf), 0 },
+ { INT_RANGE(sae_check_mfp, 0, 1), 0 },
{ FUNC(sae_groups), 0 },
{ INT_RANGE(sae_pwe, 0, 3), 0 },
{ INT_RANGE(sae_pmkid_in_assoc, 0, 1), 0 },
*/
enum mfp_options pmf;
+ /**
+ * sae_check_mfp - Whether to limit SAE based on PMF capabilities
+ *
+ * With this check SAE key_mgmt will not be selected if PMF is
+ * not enabled.
+ * Scenarios where enabling this check will limit SAE:
+ * 1) ieee8011w=0 is set for the network.
+ * 2) The AP does not have PMF enabled.
+ * 3) ieee8011w for the network is the default(3), pmf=1 is enabled
+ * globally and the device does not support the BIP cipher.
+ *
+ * Useful to allow the BIP cipher check that occurs for ieee80211w=3
+ * and pmf=1 to also avoid using SAE key_mgmt.
+ * Useful when hardware does not support BIP to still to allow
+ * connecting to sae_require_mfp=1 WPA2+WPA3-Personal transition mode
+ *access points by automatically selecting PSK instead of SAE.
+ */
+ int sae_check_mfp;
+
/**
* sae_groups - Preference list of enabled groups for SAE
*
if (config->beacon_int)
fprintf(f, "beacon_int=%d\n", config->beacon_int);
+ if (config->sae_check_mfp)
+ fprintf(f, "sae_check_mfp=%d\n", config->sae_check_mfp);
+
if (config->sae_groups) {
int i;
fprintf(f, "sae_groups=");
#endif /* CONFIG_DPP */
} else if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 &&
wpa_key_mgmt_sae(ied.key_mgmt)) {
- wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg");
- params.auth_alg = WPA_AUTH_ALG_SAE;
+ if (wpas_is_sae_avoided(wpa_s, ssid, &ied)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "SAE enabled, but disallowing SAE auth_alg without PMF");
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg");
+ params.auth_alg = WPA_AUTH_ALG_SAE;
+ }
} else {
wpa_dbg(wpa_s, MSG_DEBUG,
"SAE enabled, but target BSS does not advertise SAE AKM for RSN");
"autoscan", "wps_nfc_dev_pw_id", "wps_nfc_dh_pubkey",
"wps_nfc_dh_privkey", "wps_nfc_dev_pw", "ext_password_backend",
"p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
- "sae_groups", "dtim_period", "beacon_int",
+ "sae_check_mfp", "sae_groups", "dtim_period", "beacon_int",
"ap_vendor_elements", "ignore_old_scan_res", "freq_list",
"scan_cur_freq", "scan_res_valid_for_connect",
"sched_scan_interval",
"go_venue_group", "go_venue_type",
"wps_nfc_dev_pw_id", "ext_password_backend",
"p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
+ "sae_check_mfp",
"dtim_period", "beacon_int", "ignore_old_scan_res",
"scan_cur_freq", "scan_res_valid_for_connect",
"sched_scan_interval",
sel = ie.key_mgmt & ssid->key_mgmt;
#ifdef CONFIG_SAE
- if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE))
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE) ||
+ wpas_is_sae_avoided(wpa_s, ssid, &ie))
sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_SAE_EXT_KEY |
WPA_KEY_MGMT_FT_SAE | WPA_KEY_MGMT_FT_SAE_EXT_KEY);
#endif /* CONFIG_SAE */
}
+#ifdef CONFIG_SAE
+bool wpas_is_sae_avoided(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ const struct wpa_ie_data *ie)
+{
+ return wpa_s->conf->sae_check_mfp &&
+ (!(ie->capabilities &
+ (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) ||
+ wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION);
+}
+#endif /* CONFIG_SAE */
+
+
int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr)
{
if (wpa_s->current_ssid == NULL ||
# RSN.
#pmf=0
+# sae_check_mfp: Require PMF support to select SAE key_mgmt
+# 0 = Do not check PMF for SAE (default)
+# 1 = Limit SAE when PMF is not enabled
+#
+# When enabled SAE will not be selected if PMF will not be used
+# for the connection.
+# Scenarios where this check will limit SAE:
+# 1) ieee80211w=0 is set for the network
+# 2) The AP does not have PMF enabled.
+# 3) ieee80211w is unset, pmf=1 is enabled globally, and
+# the device does not support the BIP cipher.
+# Consider the configuration of global parameterss sae_check_mfp=1, pmf=1 and a
+# network configured with ieee80211w unset and key_mgmt=SAE WPA-PSK.
+# In the example WPA-PSK will be used if the device does not support
+# the BIP cipher or the AP has PMF disabled.
+# Limiting SAE with this check can avoid failing to associate to an AP
+# that is configured with sae_requires_mfp=1 if the device does
+# not support PMF due to lack of the BIP cipher.
+#
+# Enabling this check helps with compliance of the WPA3
+# specification for WPA3-Personal transition mode.
+# The WPA3 specification section 2.3 "WPA3-Personal transition mode" item 8
+# states "A STA shall negotiate PMF when associating to an AP using SAE".
+# With this check WPA3 capable devices when connecting
+# to transition mode APs that do not advertise PMF support
+# will not use SAE and instead fallback to PSK.
+#sae_check_mfp=0
+
# Enabled SAE finite cyclic groups in preference order
# By default (if this parameter is not set), the mandatory group 19 (ECC group
# defined over a 256-bit prime order field, NIST P-256) is preferred and groups
int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr);
void wpa_s_setup_sae_pt(struct wpa_config *conf, struct wpa_ssid *ssid);
+bool wpas_is_sae_avoided(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ const struct wpa_ie_data *ie);
+
int wpas_init_ext_pw(struct wpa_supplicant *wpa_s);
void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title,