Add support to send multi AKM connect request when driver's SME in use

Add support to configure SAE, PSK, and PSK-SHA256 AKMs in connect
request when driver's SME in use. This is needed for implementing
WPA3-Personal transition mode correctly with any driver that handles
roaming internally.

Send additional AKMs configured in network block to driver based on
the maximum number of AKMs allowed by driver in connect request. Keep
first AKM in the list AKMs in the connect request as AKM selected by
wpa_supplicant to maintain backwards compatibility.

Signed-off-by: Veerendranath Jakkam <quic_vjakkam@quicinc.com>
This commit is contained in:
Veerendranath Jakkam 2022-02-20 07:21:56 +05:30 committed by Jouni Malinen
parent 0ce1545dcb
commit 5de45546d5
6 changed files with 217 additions and 34 deletions

View file

@ -181,6 +181,13 @@ static inline int wpa_key_mgmt_cckm(int akm)
return akm == WPA_KEY_MGMT_CCKM; 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_WPA BIT(0)
#define WPA_PROTO_RSN BIT(1) #define WPA_PROTO_RSN BIT(1)

View file

@ -999,6 +999,17 @@ struct wpa_driver_associate_params {
*/ */
unsigned int key_mgmt_suite; 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 * auth_alg - Allowed authentication algorithms
* Bit field of WPA_AUTH_ALG_* * Bit field of WPA_AUTH_ALG_*

View file

@ -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, static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
struct wpa_driver_associate_params *params, struct wpa_driver_associate_params *params,
struct nl_msg *msg) 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_FT_FILS_SHA384 ||
params->key_mgmt_suite == WPA_KEY_MGMT_OWE || params->key_mgmt_suite == WPA_KEY_MGMT_OWE ||
params->key_mgmt_suite == WPA_KEY_MGMT_DPP) { 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) { switch (params->key_mgmt_suite) {
case WPA_KEY_MGMT_CCKM: case WPA_KEY_MGMT_CCKM:
mgmt = RSN_AUTH_KEY_MGMT_CCKM; mgmt[0] = RSN_AUTH_KEY_MGMT_CCKM;
break; break;
case WPA_KEY_MGMT_IEEE8021X: case WPA_KEY_MGMT_IEEE8021X:
mgmt = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; mgmt[0] = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
break; break;
case WPA_KEY_MGMT_FT_IEEE8021X: case WPA_KEY_MGMT_FT_IEEE8021X:
mgmt = RSN_AUTH_KEY_MGMT_FT_802_1X; mgmt[0] = RSN_AUTH_KEY_MGMT_FT_802_1X;
break; break;
case WPA_KEY_MGMT_FT_PSK: case WPA_KEY_MGMT_FT_PSK:
mgmt = RSN_AUTH_KEY_MGMT_FT_PSK; mgmt[0] = RSN_AUTH_KEY_MGMT_FT_PSK;
break; break;
case WPA_KEY_MGMT_IEEE8021X_SHA256: case WPA_KEY_MGMT_IEEE8021X_SHA256:
mgmt = RSN_AUTH_KEY_MGMT_802_1X_SHA256; mgmt[0] = RSN_AUTH_KEY_MGMT_802_1X_SHA256;
break; break;
case WPA_KEY_MGMT_PSK_SHA256: case WPA_KEY_MGMT_PSK_SHA256:
mgmt = RSN_AUTH_KEY_MGMT_PSK_SHA256; mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_SHA256;
break; break;
case WPA_KEY_MGMT_OSEN: case WPA_KEY_MGMT_OSEN:
mgmt = RSN_AUTH_KEY_MGMT_OSEN; mgmt[0] = RSN_AUTH_KEY_MGMT_OSEN;
break; break;
case WPA_KEY_MGMT_SAE: case WPA_KEY_MGMT_SAE:
mgmt = RSN_AUTH_KEY_MGMT_SAE; mgmt[0] = RSN_AUTH_KEY_MGMT_SAE;
break; break;
case WPA_KEY_MGMT_SAE_EXT_KEY: 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; break;
case WPA_KEY_MGMT_FT_SAE: case WPA_KEY_MGMT_FT_SAE:
mgmt = RSN_AUTH_KEY_MGMT_FT_SAE; mgmt[0] = RSN_AUTH_KEY_MGMT_FT_SAE;
break; break;
case WPA_KEY_MGMT_FT_SAE_EXT_KEY: 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; break;
case WPA_KEY_MGMT_IEEE8021X_SUITE_B: 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; break;
case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: 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; break;
case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: 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; break;
case WPA_KEY_MGMT_FILS_SHA256: case WPA_KEY_MGMT_FILS_SHA256:
mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA256; mgmt[0] = RSN_AUTH_KEY_MGMT_FILS_SHA256;
break; break;
case WPA_KEY_MGMT_FILS_SHA384: case WPA_KEY_MGMT_FILS_SHA384:
mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA384; mgmt[0] = RSN_AUTH_KEY_MGMT_FILS_SHA384;
break; break;
case WPA_KEY_MGMT_FT_FILS_SHA256: 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; break;
case WPA_KEY_MGMT_FT_FILS_SHA384: 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; break;
case WPA_KEY_MGMT_OWE: case WPA_KEY_MGMT_OWE:
mgmt = RSN_AUTH_KEY_MGMT_OWE; mgmt[0] = RSN_AUTH_KEY_MGMT_OWE;
break; break;
case WPA_KEY_MGMT_DPP: case WPA_KEY_MGMT_DPP:
mgmt = RSN_AUTH_KEY_MGMT_DPP; mgmt[0] = RSN_AUTH_KEY_MGMT_DPP;
break; break;
case WPA_KEY_MGMT_PSK: case WPA_KEY_MGMT_PSK:
default: default:
mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
break; 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; return -1;
}
os_free(mgmt);
} }
if (params->req_handshake_offload && 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) nl80211_put_fils_connect_params(drv, params, msg) != 0)
return -1; 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)) && (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) &&
nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT)) nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT))
return -1; return -1;
@ -6424,9 +6470,8 @@ static int wpa_driver_nl80211_try_connect(
#ifdef CONFIG_DRIVER_NL80211_QCA #ifdef CONFIG_DRIVER_NL80211_QCA
if (params->req_key_mgmt_offload && params->psk && if (params->req_key_mgmt_offload && params->psk &&
(params->key_mgmt_suite == WPA_KEY_MGMT_PSK || (wpa_key_mgmt_wpa_psk_no_sae(params->key_mgmt_suite) ||
params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 || wpa_key_mgmt_wpa_psk_no_sae(params->allowed_key_mgmts))) {
params->key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) {
wpa_printf(MSG_DEBUG, "nl80211: Key management set PSK"); wpa_printf(MSG_DEBUG, "nl80211: Key management set PSK");
ret = issue_key_mgmt_set_key(drv, params->psk, 32); ret = issue_key_mgmt_set_key(drv, params->psk, 32);
if (ret) if (ret)
@ -6453,7 +6498,8 @@ static int wpa_driver_nl80211_try_connect(
goto fail; goto fail;
#ifdef CONFIG_SAE #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) nl80211_put_sae_pwe(msg, params->sae_pwe) < 0)
goto fail; goto fail;
#endif /* CONFIG_SAE */ #endif /* CONFIG_SAE */
@ -6561,7 +6607,8 @@ static int wpa_driver_nl80211_associate(
if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0) if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0)
return -1; 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; bss->use_nl_connect = 1;
else else
bss->use_nl_connect = 0; bss->use_nl_connect = 0;

View file

@ -337,6 +337,7 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
wpa_s->current_ssid = NULL; wpa_s->current_ssid = NULL;
eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
wpa_s->key_mgmt = 0; wpa_s->key_mgmt = 0;
wpa_s->allowed_key_mgmts = 0;
wpas_rrm_reset(wpa_s); wpas_rrm_reset(wpa_s);
wpa_s->wnmsleep_used = 0; wpa_s->wnmsleep_used = 0;

View file

@ -1119,6 +1119,7 @@ void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s)
wpa_s->group_cipher = 0; wpa_s->group_cipher = 0;
wpa_s->mgmt_group_cipher = 0; wpa_s->mgmt_group_cipher = 0;
wpa_s->key_mgmt = 0; wpa_s->key_mgmt = 0;
wpa_s->allowed_key_mgmts = 0;
if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED) if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED)
wpa_supplicant_set_state(wpa_s, new_state); 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_supplicant_set_suites - Set authentication and encryption parameters
* @wpa_s: Pointer to wpa_supplicant data * @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); 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; 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.group_suite = cipher_group;
params.mgmt_group_suite = cipher_group_mgmt; params.mgmt_group_suite = cipher_group_mgmt;
params.key_mgmt_suite = wpa_s->key_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; params.wpa_proto = wpa_s->wpa_proto;
wpa_s->auth_alg = params.auth_alg; wpa_s->auth_alg = params.auth_alg;
params.mode = ssid->mode; 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) && 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_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; params.passphrase = ssid->passphrase;
if (ssid->psk_set) if (ssid->psk_set)
params.psk = ssid->psk; params.psk = ssid->psk;
@ -3946,9 +4059,8 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
else else
params.req_key_mgmt_offload = 1; params.req_key_mgmt_offload = 1;
if ((params.key_mgmt_suite == WPA_KEY_MGMT_PSK || if ((wpa_key_mgmt_wpa_psk_no_sae(params.key_mgmt_suite) ||
params.key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 || wpa_key_mgmt_wpa_psk_no_sae(params.allowed_key_mgmts)) &&
params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK) &&
ssid->psk_set) ssid->psk_set)
params.psk = ssid->psk; params.psk = ssid->psk;
} }

View file

@ -747,6 +747,11 @@ struct wpa_supplicant {
int key_mgmt; int key_mgmt;
int wpa_proto; int wpa_proto;
int mgmt_group_cipher; 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 *drv_priv; /* private data used by driver_ops */
void *global_drv_priv; void *global_drv_priv;