MLD STA: Add support for SAE external authentication offload to userspace

Enable MLO for SAE authentication when the driver indicates the AP MLD
address in an external authentication request. The MAC address of the
interface on which the external authentication request received will be
used as the own MLD address.

This commit does below for enabling MLO during external SAE
authentication:
- Use MLD addresses for SAE authentication.
- Add Basic Multi-Link element with the own MLD address in SAE
  Authentication frames.
- Send SAE Authentication frames with the source address as the own MLD
  address, destination address and BSSID as the AP MLD address to the
  driver.
- Validate the MLD address indicated by the AP in SAE Authentication
  frames against the AP MLD address indicated in external authentication
  request.
- Store the PMKSA with the AP MLD address after completing SAE
  authentication.

Signed-off-by: Veerendranath Jakkam <quic_vjakkam@quicinc.com>
This commit is contained in:
Veerendranath Jakkam 2022-10-19 19:44:06 +05:30 committed by Jouni Malinen
parent 575712450a
commit c91852044d
4 changed files with 134 additions and 18 deletions

View file

@ -2730,6 +2730,7 @@ enum wpa_drv_update_connect_params_mask {
* the real status code for failures. Used only for the request interface * the real status code for failures. Used only for the request interface
* from user space to the driver. * from user space to the driver.
* @pmkid: Generated PMKID as part of external auth exchange (e.g., SAE). * @pmkid: Generated PMKID as part of external auth exchange (e.g., SAE).
* @mld_addr: AP's MLD address or %NULL if MLO is not used
*/ */
struct external_auth { struct external_auth {
enum { enum {
@ -2742,6 +2743,7 @@ struct external_auth {
unsigned int key_mgmt_suite; unsigned int key_mgmt_suite;
u16 status; u16 status;
const u8 *pmkid; const u8 *pmkid;
const u8 *mld_addr;
}; };
#define WPAS_MAX_PASN_PEERS 10 #define WPAS_MAX_PASN_PEERS 10

View file

@ -3116,6 +3116,7 @@ static void nl80211_external_auth(struct wpa_driver_nl80211_data *drv,
{ {
union wpa_event_data event; union wpa_event_data event;
enum nl80211_external_auth_action act; enum nl80211_external_auth_action act;
char mld_addr[50];
if (!tb[NL80211_ATTR_AKM_SUITES] || if (!tb[NL80211_ATTR_AKM_SUITES] ||
!tb[NL80211_ATTR_EXTERNAL_AUTH_ACTION] || !tb[NL80211_ATTR_EXTERNAL_AUTH_ACTION] ||
@ -3146,10 +3147,21 @@ static void nl80211_external_auth(struct wpa_driver_nl80211_data *drv,
event.external_auth.bssid = nla_data(tb[NL80211_ATTR_BSSID]); event.external_auth.bssid = nla_data(tb[NL80211_ATTR_BSSID]);
mld_addr[0] = '\0';
if (tb[NL80211_ATTR_MLD_ADDR]) {
event.external_auth.mld_addr =
nla_data(tb[NL80211_ATTR_MLD_ADDR]);
os_snprintf(mld_addr, sizeof(mld_addr), ", MLD ADDR: " MACSTR,
MAC2STR(event.external_auth.mld_addr));
}
wpa_printf(MSG_DEBUG, wpa_printf(MSG_DEBUG,
"nl80211: External auth action: %u, AKM: 0x%x", "nl80211: External auth action: %u, AKM: 0x%x, SSID: %s, BSSID: " MACSTR "%s",
event.external_auth.action, event.external_auth.action,
event.external_auth.key_mgmt_suite); event.external_auth.key_mgmt_suite,
wpa_ssid_txt(event.external_auth.ssid,
event.external_auth.ssid_len),
MAC2STR(event.external_auth.bssid), mld_addr);
wpa_supplicant_event(drv->ctx, EVENT_EXTERNAL_AUTH, &event); wpa_supplicant_event(drv->ctx, EVENT_EXTERNAL_AUTH, &event);
} }

View file

@ -1302,11 +1302,30 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
#ifdef CONFIG_SAE #ifdef CONFIG_SAE
#define WPA_AUTH_FRAME_ML_IE_LEN (6 + ETH_ALEN)
static void wpa_auth_ml_ie(struct wpabuf *buf, const u8 *mld_addr)
{
wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
wpabuf_put_u8(buf, 4 + ETH_ALEN);
wpabuf_put_u8(buf, WLAN_EID_EXT_MULTI_LINK);
/* Basic Multi-Link element Control field */
wpabuf_put_u8(buf, 0x0);
wpabuf_put_u8(buf, 0x0);
/* Common Info */
wpabuf_put_u8(buf, 0x7); /* length = Length field + MLD MAC address */
wpabuf_put_data(buf, mld_addr, ETH_ALEN);
}
static int sme_external_auth_build_buf(struct wpabuf *buf, static int sme_external_auth_build_buf(struct wpabuf *buf,
struct wpabuf *params, struct wpabuf *params,
const u8 *sa, const u8 *da, const u8 *sa, const u8 *da,
u16 auth_transaction, u16 seq_num, u16 auth_transaction, u16 seq_num,
u16 status_code) u16 status_code, const u8 *mld_addr)
{ {
struct ieee80211_mgmt *resp; struct ieee80211_mgmt *resp;
@ -1325,6 +1344,9 @@ static int sme_external_auth_build_buf(struct wpabuf *buf,
if (params) if (params)
wpabuf_put_buf(buf, params); wpabuf_put_buf(buf, params);
if (mld_addr)
wpa_auth_ml_ie(buf, mld_addr);
return 0; return 0;
} }
@ -1338,7 +1360,9 @@ static int sme_external_auth_send_sae_commit(struct wpa_supplicant *wpa_s,
bool use_pk; bool use_pk;
u16 status; u16 status;
resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, NULL, resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid,
wpa_s->sme.ext_ml_auth ?
wpa_s->sme.ext_auth_ap_mld_addr : NULL,
1, 0, &use_pt, &use_pk); 1, 0, &use_pt, &use_pk);
if (!resp) { if (!resp) {
wpa_printf(MSG_DEBUG, "SAE: Failed to build SAE commit"); wpa_printf(MSG_DEBUG, "SAE: Failed to build SAE commit");
@ -1346,7 +1370,9 @@ static int sme_external_auth_send_sae_commit(struct wpa_supplicant *wpa_s,
} }
wpa_s->sme.sae.state = SAE_COMMITTED; wpa_s->sme.sae.state = SAE_COMMITTED;
buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + wpabuf_len(resp)); buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + wpabuf_len(resp) +
(wpa_s->sme.ext_ml_auth ? WPA_AUTH_FRAME_ML_IE_LEN :
0));
if (!buf) { if (!buf) {
wpabuf_free(resp); wpabuf_free(resp);
return -1; return -1;
@ -1360,7 +1386,11 @@ static int sme_external_auth_send_sae_commit(struct wpa_supplicant *wpa_s,
else else
status = WLAN_STATUS_SUCCESS; status = WLAN_STATUS_SUCCESS;
sme_external_auth_build_buf(buf, resp, wpa_s->own_addr, sme_external_auth_build_buf(buf, resp, wpa_s->own_addr,
bssid, 1, wpa_s->sme.seq_num, status); wpa_s->sme.ext_ml_auth ?
wpa_s->sme.ext_auth_ap_mld_addr : bssid, 1,
wpa_s->sme.seq_num, status,
wpa_s->sme.ext_ml_auth ?
wpa_s->own_addr : NULL);
wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0, 0); wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0, 0);
wpabuf_free(resp); wpabuf_free(resp);
wpabuf_free(buf); wpabuf_free(buf);
@ -1427,7 +1457,9 @@ static void sme_external_auth_send_sae_confirm(struct wpa_supplicant *wpa_s,
} }
wpa_s->sme.sae.state = SAE_CONFIRMED; wpa_s->sme.sae.state = SAE_CONFIRMED;
buf = wpabuf_alloc(4 + SAE_CONFIRM_MAX_LEN + wpabuf_len(resp)); buf = wpabuf_alloc(4 + SAE_CONFIRM_MAX_LEN + wpabuf_len(resp) +
(wpa_s->sme.ext_ml_auth ? WPA_AUTH_FRAME_ML_IE_LEN :
0));
if (!buf) { if (!buf) {
wpa_printf(MSG_DEBUG, "SAE: Auth Confirm buf alloc failure"); wpa_printf(MSG_DEBUG, "SAE: Auth Confirm buf alloc failure");
wpabuf_free(resp); wpabuf_free(resp);
@ -1436,7 +1468,10 @@ static void sme_external_auth_send_sae_confirm(struct wpa_supplicant *wpa_s,
wpa_s->sme.seq_num++; wpa_s->sme.seq_num++;
sme_external_auth_build_buf(buf, resp, wpa_s->own_addr, sme_external_auth_build_buf(buf, resp, wpa_s->own_addr,
da, 2, wpa_s->sme.seq_num, da, 2, wpa_s->sme.seq_num,
WLAN_STATUS_SUCCESS); WLAN_STATUS_SUCCESS,
wpa_s->sme.ext_ml_auth ?
wpa_s->own_addr : NULL);
wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0, 0); wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0, 0);
wpabuf_free(resp); wpabuf_free(resp);
wpabuf_free(buf); wpabuf_free(buf);
@ -1488,6 +1523,13 @@ void sme_external_auth_trigger(struct wpa_supplicant *wpa_s,
os_memcpy(wpa_s->sme.ext_auth_ssid, data->external_auth.ssid, os_memcpy(wpa_s->sme.ext_auth_ssid, data->external_auth.ssid,
data->external_auth.ssid_len); data->external_auth.ssid_len);
wpa_s->sme.ext_auth_ssid_len = data->external_auth.ssid_len; wpa_s->sme.ext_auth_ssid_len = data->external_auth.ssid_len;
if (data->external_auth.mld_addr) {
wpa_s->sme.ext_ml_auth = true;
os_memcpy(wpa_s->sme.ext_auth_ap_mld_addr,
data->external_auth.mld_addr, ETH_ALEN);
} else {
wpa_s->sme.ext_ml_auth = false;
}
wpa_s->sme.seq_num = 0; wpa_s->sme.seq_num = 0;
wpa_s->sme.sae.state = SAE_NOTHING; wpa_s->sme.sae.state = SAE_NOTHING;
wpa_s->sme.sae.send_confirm = 0; wpa_s->sme.sae.send_confirm = 0;
@ -1549,6 +1591,42 @@ static int sme_check_sae_rejected_groups(struct wpa_supplicant *wpa_s,
} }
static int sme_external_ml_auth(struct wpa_supplicant *wpa_s,
const u8 *data, size_t len, int ie_offset)
{
struct ieee802_11_elems elems;
const u8 *mld_addr;
if (ieee802_11_parse_elems(data + ie_offset, len - ie_offset,
&elems, 0) != ParseOK) {
wpa_printf(MSG_DEBUG, "MLD: Failed parsing elements");
return -1;
}
if (!elems.basic_mle || !elems.basic_mle_len) {
wpa_printf(MSG_DEBUG, "MLD: No ML element in authentication");
return -1;
}
mld_addr = get_basic_mle_mld_addr(elems.basic_mle, elems.basic_mle_len);
if (!mld_addr) {
wpa_printf(MSG_DEBUG, "MLD: No MLD address in ML element");
return -1;
}
wpa_printf(MSG_DEBUG, "MLD: mld_address=" MACSTR, MAC2STR(mld_addr));
if (os_memcmp(wpa_s->sme.ext_auth_ap_mld_addr, mld_addr, ETH_ALEN) !=
0) {
wpa_printf(MSG_DEBUG, "MLD: Unexpected MLD address (expected "
MACSTR ")", MAC2STR(wpa_s->ap_mld_addr));
return -1;
}
return 0;
}
static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
u16 status_code, const u8 *data, size_t len, u16 status_code, const u8 *data, size_t len,
int external, const u8 *sa, int *ie_offset) int external, const u8 *sa, int *ie_offset)
@ -1616,7 +1694,6 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
token_len = elen - 1; token_len = elen - 1;
} }
if (ie_offset)
*ie_offset = token_pos + token_len - data; *ie_offset = token_pos + token_len - data;
wpa_s->sme.sae_token = wpabuf_alloc_copy(token_pos, token_len); wpa_s->sme.sae_token = wpabuf_alloc_copy(token_pos, token_len);
@ -1628,13 +1705,18 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
wpa_hexdump_buf(MSG_DEBUG, "SME: Requested anti-clogging token", wpa_hexdump_buf(MSG_DEBUG, "SME: Requested anti-clogging token",
wpa_s->sme.sae_token); wpa_s->sme.sae_token);
if (!external) if (!external) {
sme_send_authentication(wpa_s, wpa_s->current_bss, sme_send_authentication(wpa_s, wpa_s->current_bss,
wpa_s->current_ssid, 2); wpa_s->current_ssid, 2);
else } else {
if (wpa_s->sme.ext_ml_auth &&
sme_external_ml_auth(wpa_s, data, len, *ie_offset))
return -1;
sme_external_auth_send_sae_commit( sme_external_auth_send_sae_commit(
wpa_s, wpa_s->sme.ext_auth_bssid, wpa_s, wpa_s->sme.ext_auth_bssid,
wpa_s->sme.ext_auth_wpa_ssid); wpa_s->sme.ext_auth_wpa_ssid);
}
return 0; return 0;
} }
@ -1650,13 +1732,18 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
if (sme_set_sae_group(wpa_s, external) < 0) if (sme_set_sae_group(wpa_s, external) < 0)
return -1; /* no other groups enabled */ return -1; /* no other groups enabled */
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Try next enabled SAE group"); wpa_dbg(wpa_s, MSG_DEBUG, "SME: Try next enabled SAE group");
if (!external) if (!external) {
sme_send_authentication(wpa_s, wpa_s->current_bss, sme_send_authentication(wpa_s, wpa_s->current_bss,
wpa_s->current_ssid, 1); wpa_s->current_ssid, 1);
else } else {
if (wpa_s->sme.ext_ml_auth &&
sme_external_ml_auth(wpa_s, data, len, *ie_offset))
return -1;
sme_external_auth_send_sae_commit( sme_external_auth_send_sae_commit(
wpa_s, wpa_s->sme.ext_auth_bssid, wpa_s, wpa_s->sme.ext_auth_bssid,
wpa_s->sme.ext_auth_wpa_ssid); wpa_s->sme.ext_auth_wpa_ssid);
}
return 0; return 0;
} }
@ -1744,11 +1831,16 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
wpabuf_free(wpa_s->sme.sae_token); wpabuf_free(wpa_s->sme.sae_token);
wpa_s->sme.sae_token = NULL; wpa_s->sme.sae_token = NULL;
if (!external) if (!external) {
sme_send_authentication(wpa_s, wpa_s->current_bss, sme_send_authentication(wpa_s, wpa_s->current_bss,
wpa_s->current_ssid, 0); wpa_s->current_ssid, 0);
else } else {
if (wpa_s->sme.ext_ml_auth &&
sme_external_ml_auth(wpa_s, data, len, *ie_offset))
return -1;
sme_external_auth_send_sae_confirm(wpa_s, sa); sme_external_auth_send_sae_confirm(wpa_s, sa);
}
return 0; return 0;
} else if (auth_transaction == 2) { } else if (auth_transaction == 2) {
if (status_code != WLAN_STATUS_SUCCESS) if (status_code != WLAN_STATUS_SUCCESS)
@ -1759,6 +1851,10 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
if (sae_check_confirm(&wpa_s->sme.sae, data, len, if (sae_check_confirm(&wpa_s->sme.sae, data, len,
ie_offset) < 0) ie_offset) < 0)
return -1; return -1;
if (external && wpa_s->sme.ext_ml_auth &&
sme_external_ml_auth(wpa_s, data, len, *ie_offset))
return -1;
wpa_s->sme.sae.state = SAE_ACCEPTED; wpa_s->sme.sae.state = SAE_ACCEPTED;
sae_clear_temp_data(&wpa_s->sme.sae); sae_clear_temp_data(&wpa_s->sme.sae);
@ -1824,12 +1920,13 @@ void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s,
if (le_to_host16(header->u.auth.auth_alg) == WLAN_AUTH_SAE) { if (le_to_host16(header->u.auth.auth_alg) == WLAN_AUTH_SAE) {
int res; int res;
int ie_offset = 0;
res = sme_sae_auth( res = sme_sae_auth(
wpa_s, le_to_host16(header->u.auth.auth_transaction), wpa_s, le_to_host16(header->u.auth.auth_transaction),
le_to_host16(header->u.auth.status_code), le_to_host16(header->u.auth.status_code),
header->u.auth.variable, header->u.auth.variable,
len - auth_length, 1, header->sa, NULL); len - auth_length, 1, header->sa, &ie_offset);
if (res < 0) { if (res < 0) {
/* Notify failure to the driver */ /* Notify failure to the driver */
sme_send_external_auth_status( sme_send_external_auth_status(
@ -1842,7 +1939,10 @@ void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s,
if (res != 1) if (res != 1)
return; return;
if (sme_sae_set_pmk(wpa_s, wpa_s->sme.ext_auth_bssid) < 0) if (sme_sae_set_pmk(wpa_s,
wpa_s->sme.ext_ml_auth ?
wpa_s->sme.ext_auth_ap_mld_addr :
wpa_s->sme.ext_auth_bssid) < 0)
return; return;
} }
} }

View file

@ -981,6 +981,8 @@ struct wpa_supplicant {
u8 ext_auth_ssid[SSID_MAX_LEN]; u8 ext_auth_ssid[SSID_MAX_LEN];
size_t ext_auth_ssid_len; size_t ext_auth_ssid_len;
int ext_auth_key_mgmt; int ext_auth_key_mgmt;
u8 ext_auth_ap_mld_addr[ETH_ALEN];
bool ext_ml_auth;
int *sae_rejected_groups; int *sae_rejected_groups;
#endif /* CONFIG_SAE */ #endif /* CONFIG_SAE */
} sme; } sme;