diff --git a/src/common/defs.h b/src/common/defs.h index 3c21ab4c1..e3d02f810 100644 --- a/src/common/defs.h +++ b/src/common/defs.h @@ -181,6 +181,13 @@ static inline int wpa_key_mgmt_cckm(int akm) return akm == WPA_KEY_MGMT_CCKM; } +static inline int wpa_key_mgmt_cross_akm(int akm) +{ + return !!(akm & (WPA_KEY_MGMT_PSK | + WPA_KEY_MGMT_PSK_SHA256 | + WPA_KEY_MGMT_SAE | + WPA_KEY_MGMT_SAE_EXT_KEY)); +} #define WPA_PROTO_WPA BIT(0) #define WPA_PROTO_RSN BIT(1) diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 48b4bb4a1..ccbae59a3 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -999,6 +999,17 @@ struct wpa_driver_associate_params { */ unsigned int key_mgmt_suite; + /** + * allowed_key_mgmts - Bitfield of allowed key management suites + * (WPA_KEY_MGMT_*) other than @key_mgmt_suite for the current + * connection + * + * SME in the driver may choose key_mgmt from this list for the initial + * connection or roaming. The driver which doesn't support this + * ignores this parameter. + */ + unsigned int allowed_key_mgmts; + /** * auth_alg - Allowed authentication algorithms * Bit field of WPA_AUTH_ALG_* diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index b52a4800e..3df5dfb0b 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -6137,6 +6137,17 @@ static int nl80211_put_fils_connect_params(struct wpa_driver_nl80211_data *drv, } +static unsigned int num_bits_set(u32 val) +{ + unsigned int c; + + for (c = 0; val; c++) + val &= val - 1; + + return c; +} + + static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, struct wpa_driver_associate_params *params, struct nl_msg *msg) @@ -6268,77 +6279,111 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA384 || params->key_mgmt_suite == WPA_KEY_MGMT_OWE || params->key_mgmt_suite == WPA_KEY_MGMT_DPP) { - int mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; + u32 *mgmt; + unsigned int akm_count = 1, i; + + /* + * Make sure the driver has capability to handle default AKM in + * key_mgmt_suite plus allowed AKMs in allowed_key_mgmts. + */ + if (drv->capa.max_num_akms <= + num_bits_set(params->allowed_key_mgmts)) { + wpa_printf(MSG_INFO, + "nl80211: Not enough support for the allowed AKMs (max_num_akms=%u <= num_bits_set=%u)", + drv->capa.max_num_akms, + num_bits_set(params->allowed_key_mgmts)); + return -1; + } + + mgmt = os_malloc(sizeof(u32) * drv->capa.max_num_akms); + if (!mgmt) + return -1; + + mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; switch (params->key_mgmt_suite) { case WPA_KEY_MGMT_CCKM: - mgmt = RSN_AUTH_KEY_MGMT_CCKM; + mgmt[0] = RSN_AUTH_KEY_MGMT_CCKM; break; case WPA_KEY_MGMT_IEEE8021X: - mgmt = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; + mgmt[0] = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; break; case WPA_KEY_MGMT_FT_IEEE8021X: - mgmt = RSN_AUTH_KEY_MGMT_FT_802_1X; + mgmt[0] = RSN_AUTH_KEY_MGMT_FT_802_1X; break; case WPA_KEY_MGMT_FT_PSK: - mgmt = RSN_AUTH_KEY_MGMT_FT_PSK; + mgmt[0] = RSN_AUTH_KEY_MGMT_FT_PSK; break; case WPA_KEY_MGMT_IEEE8021X_SHA256: - mgmt = RSN_AUTH_KEY_MGMT_802_1X_SHA256; + mgmt[0] = RSN_AUTH_KEY_MGMT_802_1X_SHA256; break; case WPA_KEY_MGMT_PSK_SHA256: - mgmt = RSN_AUTH_KEY_MGMT_PSK_SHA256; + mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_SHA256; break; case WPA_KEY_MGMT_OSEN: - mgmt = RSN_AUTH_KEY_MGMT_OSEN; + mgmt[0] = RSN_AUTH_KEY_MGMT_OSEN; break; case WPA_KEY_MGMT_SAE: - mgmt = RSN_AUTH_KEY_MGMT_SAE; + mgmt[0] = RSN_AUTH_KEY_MGMT_SAE; break; case WPA_KEY_MGMT_SAE_EXT_KEY: - mgmt = RSN_AUTH_KEY_MGMT_SAE_EXT_KEY; + mgmt[0] = RSN_AUTH_KEY_MGMT_SAE_EXT_KEY; break; case WPA_KEY_MGMT_FT_SAE: - mgmt = RSN_AUTH_KEY_MGMT_FT_SAE; + mgmt[0] = RSN_AUTH_KEY_MGMT_FT_SAE; break; case WPA_KEY_MGMT_FT_SAE_EXT_KEY: - mgmt = RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY; + mgmt[0] = RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY; break; case WPA_KEY_MGMT_IEEE8021X_SUITE_B: - mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B; + mgmt[0] = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B; break; case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: - mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192; + mgmt[0] = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192; break; case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: - mgmt = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384; + mgmt[0] = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384; break; case WPA_KEY_MGMT_FILS_SHA256: - mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA256; + mgmt[0] = RSN_AUTH_KEY_MGMT_FILS_SHA256; break; case WPA_KEY_MGMT_FILS_SHA384: - mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA384; + mgmt[0] = RSN_AUTH_KEY_MGMT_FILS_SHA384; break; case WPA_KEY_MGMT_FT_FILS_SHA256: - mgmt = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256; + mgmt[0] = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256; break; case WPA_KEY_MGMT_FT_FILS_SHA384: - mgmt = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384; + mgmt[0] = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384; break; case WPA_KEY_MGMT_OWE: - mgmt = RSN_AUTH_KEY_MGMT_OWE; + mgmt[0] = RSN_AUTH_KEY_MGMT_OWE; break; case WPA_KEY_MGMT_DPP: - mgmt = RSN_AUTH_KEY_MGMT_DPP; + mgmt[0] = RSN_AUTH_KEY_MGMT_DPP; break; case WPA_KEY_MGMT_PSK: default: - mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; + mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; break; } - wpa_printf(MSG_DEBUG, " * akm=0x%x", mgmt); - if (nla_put_u32(msg, NL80211_ATTR_AKM_SUITES, mgmt)) + + if (drv->capa.max_num_akms > 1) { + akm_count += wpa_key_mgmt_to_suites( + params->allowed_key_mgmts, &mgmt[1], + drv->capa.max_num_akms - 1); + } + + for (i = 0; i < akm_count; i++) + wpa_printf(MSG_DEBUG, " * akm[%d]=0x%x", i, mgmt[i]); + + if (nla_put(msg, NL80211_ATTR_AKM_SUITES, + akm_count * sizeof(u32), mgmt)) { + os_free(mgmt); return -1; + } + + os_free(mgmt); } if (params->req_handshake_offload && @@ -6403,7 +6448,8 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, nl80211_put_fils_connect_params(drv, params, msg) != 0) return -1; - if (wpa_key_mgmt_sae(params->key_mgmt_suite) && + if ((wpa_key_mgmt_sae(params->key_mgmt_suite) || + wpa_key_mgmt_sae(params->allowed_key_mgmts)) && (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) && nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT)) return -1; @@ -6424,9 +6470,8 @@ static int wpa_driver_nl80211_try_connect( #ifdef CONFIG_DRIVER_NL80211_QCA if (params->req_key_mgmt_offload && params->psk && - (params->key_mgmt_suite == WPA_KEY_MGMT_PSK || - params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 || - params->key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) { + (wpa_key_mgmt_wpa_psk_no_sae(params->key_mgmt_suite) || + wpa_key_mgmt_wpa_psk_no_sae(params->allowed_key_mgmts))) { wpa_printf(MSG_DEBUG, "nl80211: Key management set PSK"); ret = issue_key_mgmt_set_key(drv, params->psk, 32); if (ret) @@ -6453,7 +6498,8 @@ static int wpa_driver_nl80211_try_connect( goto fail; #ifdef CONFIG_SAE - if (wpa_key_mgmt_sae(params->key_mgmt_suite) && + if ((wpa_key_mgmt_sae(params->key_mgmt_suite) || + wpa_key_mgmt_sae(params->allowed_key_mgmts)) && nl80211_put_sae_pwe(msg, params->sae_pwe) < 0) goto fail; #endif /* CONFIG_SAE */ @@ -6561,7 +6607,8 @@ static int wpa_driver_nl80211_associate( if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0) return -1; - if (wpa_key_mgmt_sae(params->key_mgmt_suite)) + if (wpa_key_mgmt_sae(params->key_mgmt_suite) || + wpa_key_mgmt_sae(params->allowed_key_mgmts)) bss->use_nl_connect = 1; else bss->use_nl_connect = 0; diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 5ddee6717..6d8be2a78 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -337,6 +337,7 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) wpa_s->current_ssid = NULL; eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); wpa_s->key_mgmt = 0; + wpa_s->allowed_key_mgmts = 0; wpas_rrm_reset(wpa_s); wpa_s->wnmsleep_used = 0; diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 096e2ad27..f91609bd7 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -1119,6 +1119,7 @@ void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s) wpa_s->group_cipher = 0; wpa_s->mgmt_group_cipher = 0; wpa_s->key_mgmt = 0; + wpa_s->allowed_key_mgmts = 0; if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED) wpa_supplicant_set_state(wpa_s, new_state); @@ -1339,6 +1340,111 @@ void wpas_set_mgmt_group_cipher(struct wpa_supplicant *wpa_s, } +static void wpas_update_allowed_key_mgmt(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + int akm_count = wpa_s->max_num_akms; + u8 capab = 0; + + if (akm_count < 2) + return; + + akm_count--; + wpa_s->allowed_key_mgmts = 0; + switch (wpa_s->key_mgmt) { + case WPA_KEY_MGMT_PSK: + if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) { + akm_count--; + wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_SAE; + } + if (!akm_count) + break; + if (ssid->key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) { + akm_count--; + wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_SAE_EXT_KEY; + } + if (!akm_count) + break; + if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256) + wpa_s->allowed_key_mgmts |= + WPA_KEY_MGMT_PSK_SHA256; + break; + case WPA_KEY_MGMT_PSK_SHA256: + if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) { + akm_count--; + wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_SAE; + } + if (!akm_count) + break; + if (ssid->key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) { + akm_count--; + wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_SAE_EXT_KEY; + } + if (!akm_count) + break; + if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) + wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_PSK; + break; + case WPA_KEY_MGMT_SAE: + if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) { + akm_count--; + wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_PSK; + } + if (!akm_count) + break; + if (ssid->key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) { + akm_count--; + wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_SAE_EXT_KEY; + } + if (!akm_count) + break; + if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256) + wpa_s->allowed_key_mgmts |= + WPA_KEY_MGMT_PSK_SHA256; + break; + case WPA_KEY_MGMT_SAE_EXT_KEY: + if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) { + akm_count--; + wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_SAE; + } + if (!akm_count) + break; + if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) { + akm_count--; + wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_PSK; + } + if (!akm_count) + break; + if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256) + wpa_s->allowed_key_mgmts |= + WPA_KEY_MGMT_PSK_SHA256; + break; + default: + return; + } + + if (wpa_s->conf->sae_pwe) + capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E); +#ifdef CONFIG_SAE_PK + if (ssid->sae_pk) + capab |= BIT(WLAN_RSNX_CAPAB_SAE_PK); +#endif /* CONFIG_SAE_PK */ + + if (!((wpa_s->allowed_key_mgmts & + (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_SAE_EXT_KEY)) && capab)) + return; + + if (!wpa_s->rsnxe_len) { + wpa_s->rsnxe_len = 3; + wpa_s->rsnxe[0] = WLAN_EID_RSNX; + wpa_s->rsnxe[1] = 1; + wpa_s->rsnxe[2] = 0; + } + + wpa_s->rsnxe[2] |= capab; +} + + /** * wpa_supplicant_set_suites - Set authentication and encryption parameters * @wpa_s: Pointer to wpa_supplicant data @@ -1902,6 +2008,10 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_DENY_PTK0_REKEY, 0); } + if (wpa_key_mgmt_cross_akm(wpa_s->key_mgmt) && + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) + wpas_update_allowed_key_mgmt(wpa_s, ssid); + return 0; } @@ -3903,6 +4013,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) params.group_suite = cipher_group; params.mgmt_group_suite = cipher_group_mgmt; params.key_mgmt_suite = wpa_s->key_mgmt; + params.allowed_key_mgmts = wpa_s->allowed_key_mgmts; params.wpa_proto = wpa_s->wpa_proto; wpa_s->auth_alg = params.auth_alg; params.mode = ssid->mode; @@ -3922,7 +4033,9 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK) && (params.key_mgmt_suite == WPA_KEY_MGMT_PSK || - params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) { + params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK || + (params.allowed_key_mgmts & + (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK)))) { params.passphrase = ssid->passphrase; if (ssid->psk_set) params.psk = ssid->psk; @@ -3946,9 +4059,8 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) else params.req_key_mgmt_offload = 1; - if ((params.key_mgmt_suite == WPA_KEY_MGMT_PSK || - params.key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 || - params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK) && + if ((wpa_key_mgmt_wpa_psk_no_sae(params.key_mgmt_suite) || + wpa_key_mgmt_wpa_psk_no_sae(params.allowed_key_mgmts)) && ssid->psk_set) params.psk = ssid->psk; } diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index eb9855c23..c8d023680 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -747,6 +747,11 @@ struct wpa_supplicant { int key_mgmt; int wpa_proto; int mgmt_group_cipher; + /* + * Allowed key management suites for roaming/initial connection + * when the driver's SME is in use. + */ + int allowed_key_mgmts; void *drv_priv; /* private data used by driver_ops */ void *global_drv_priv;