]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
MLD STA: Add support for SAE external authentication offload to userspace
authorVeerendranath Jakkam <quic_vjakkam@quicinc.com>
Wed, 19 Oct 2022 14:14:06 +0000 (19:44 +0530)
committerJouni Malinen <j@w1.fi>
Wed, 15 Feb 2023 21:49:59 +0000 (23:49 +0200)
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>
src/drivers/driver.h
src/drivers/driver_nl80211_event.c
wpa_supplicant/sme.c
wpa_supplicant/wpa_supplicant_i.h

index cb27282aa502933c8a9cda520abf8e34c3a5dc55..eb13668185b720607ccc238ddefa109aaccdcae6 100644 (file)
@@ -2730,6 +2730,7 @@ enum wpa_drv_update_connect_params_mask {
  *     the real status code for failures. Used only for the request interface
  *     from user space to the driver.
  * @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 {
        enum {
@@ -2742,6 +2743,7 @@ struct external_auth {
        unsigned int key_mgmt_suite;
        u16 status;
        const u8 *pmkid;
+       const u8 *mld_addr;
 };
 
 #define WPAS_MAX_PASN_PEERS 10
index 29613161b93844235ea70fa7bbfb812c76add753..e3fcb440226777e96ccaadee9102c93fc0a7c034 100644 (file)
@@ -3116,6 +3116,7 @@ static void nl80211_external_auth(struct wpa_driver_nl80211_data *drv,
 {
        union wpa_event_data event;
        enum nl80211_external_auth_action act;
+       char mld_addr[50];
 
        if (!tb[NL80211_ATTR_AKM_SUITES] ||
            !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]);
 
+       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,
-                  "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.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);
 }
 
index a728a0fffe5033991cc126841375d6de0fa1ed0b..f64072e0037bab5f68355cc69560c5c4e2f61714 100644 (file)
@@ -1302,11 +1302,30 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
 
 #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,
                                       struct wpabuf *params,
                                       const u8 *sa, const u8 *da,
                                       u16 auth_transaction, u16 seq_num,
-                                      u16 status_code)
+                                      u16 status_code, const u8 *mld_addr)
 {
        struct ieee80211_mgmt *resp;
 
@@ -1325,6 +1344,9 @@ static int sme_external_auth_build_buf(struct wpabuf *buf,
        if (params)
                wpabuf_put_buf(buf, params);
 
+       if (mld_addr)
+               wpa_auth_ml_ie(buf, mld_addr);
+
        return 0;
 }
 
@@ -1338,7 +1360,9 @@ static int sme_external_auth_send_sae_commit(struct wpa_supplicant *wpa_s,
        bool use_pk;
        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);
        if (!resp) {
                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;
-       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) {
                wpabuf_free(resp);
                return -1;
@@ -1360,7 +1386,11 @@ static int sme_external_auth_send_sae_commit(struct wpa_supplicant *wpa_s,
        else
                status = WLAN_STATUS_SUCCESS;
        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);
        wpabuf_free(resp);
        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;
-       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) {
                wpa_printf(MSG_DEBUG, "SAE: Auth Confirm buf alloc failure");
                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++;
        sme_external_auth_build_buf(buf, resp, wpa_s->own_addr,
                                    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);
        wpabuf_free(resp);
        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,
                          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.sae.state = SAE_NOTHING;
                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,
                        u16 status_code, const u8 *data, size_t len,
                        int external, const u8 *sa, int *ie_offset)
@@ -1616,8 +1694,7 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
                        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);
                if (!wpa_s->sme.sae_token) {
@@ -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_s->sme.sae_token);
-               if (!external)
+               if (!external) {
                        sme_send_authentication(wpa_s, wpa_s->current_bss,
                                                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(
                                wpa_s, wpa_s->sme.ext_auth_bssid,
                                wpa_s->sme.ext_auth_wpa_ssid);
+               }
                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)
                        return -1; /* no other groups enabled */
                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,
                                                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(
                                wpa_s, wpa_s->sme.ext_auth_bssid,
                                wpa_s->sme.ext_auth_wpa_ssid);
+               }
                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);
                wpa_s->sme.sae_token = NULL;
-               if (!external)
+               if (!external) {
                        sme_send_authentication(wpa_s, wpa_s->current_bss,
                                                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);
+               }
                return 0;
        } else if (auth_transaction == 2) {
                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,
                                      ie_offset) < 0)
                        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;
                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) {
                int res;
+               int ie_offset = 0;
 
                res = sme_sae_auth(
                        wpa_s, le_to_host16(header->u.auth.auth_transaction),
                        le_to_host16(header->u.auth.status_code),
                        header->u.auth.variable,
-                       len - auth_length, 1, header->sa, NULL);
+                       len - auth_length, 1, header->sa, &ie_offset);
                if (res < 0) {
                        /* Notify failure to the driver */
                        sme_send_external_auth_status(
@@ -1842,7 +1939,10 @@ void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s,
                if (res != 1)
                        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;
        }
 }
index 06dcde199e471de79a353d7ea71bc9121d05df65..cb7d5f5a1988ff6021228c7b51cfcccf8439947d 100644 (file)
@@ -981,6 +981,8 @@ struct wpa_supplicant {
                u8 ext_auth_ssid[SSID_MAX_LEN];
                size_t ext_auth_ssid_len;
                int ext_auth_key_mgmt;
+               u8 ext_auth_ap_mld_addr[ETH_ALEN];
+               bool ext_ml_auth;
                int *sae_rejected_groups;
 #endif /* CONFIG_SAE */
        } sme;