]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
SAE: Support external authentication offload for driver-SME cases
authorSunil Dutt <usdutt@codeaurora.org>
Thu, 1 Feb 2018 11:31:28 +0000 (17:01 +0530)
committerJouni Malinen <j@w1.fi>
Fri, 2 Feb 2018 19:17:55 +0000 (21:17 +0200)
Extend the SME functionality to support the external authentication.
External authentication may be used by the drivers that do not define
separate commands for authentication and association
(~WPA_DRIVER_FLAGS_SME) but rely on wpa_supplicant's SME for the
authentication.

Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
wpa_supplicant/driver_i.h
wpa_supplicant/events.c
wpa_supplicant/sme.c
wpa_supplicant/sme.h
wpa_supplicant/wpa_supplicant.c
wpa_supplicant/wpa_supplicant_i.h

index 81b4b5537c10e9ec7a8b38c6e22a88165ad06030..078de23f794fd43290270cfb38e2d37b3bfeae9c 100644 (file)
@@ -1036,4 +1036,14 @@ static inline int wpa_drv_update_connect_params(
                                                    mask);
 }
 
+static inline int
+wpa_drv_send_external_auth_status(struct wpa_supplicant *wpa_s,
+                                 struct external_auth *params)
+{
+       if (!wpa_s->driver->send_external_auth_status)
+               return -1;
+       return wpa_s->driver->send_external_auth_status(wpa_s->drv_priv,
+                                                       params);
+}
+
 #endif /* DRIVER_I_H */
index 8e46b76e8521413b4a94750aafef00e52a7bb819..4f3604358a726a878e87e98f8a6430dc1f8f9324 100644 (file)
@@ -4267,6 +4267,16 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                                break;
                        }
 
+#ifdef CONFIG_SAE
+                       if (stype == WLAN_FC_STYPE_AUTH &&
+                           !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+                           (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) {
+                               sme_external_auth_mgmt_rx(
+                                       wpa_s, data->rx_mgmt.frame,
+                                       data->rx_mgmt.frame_len);
+                               break;
+                       }
+#endif /* CONFIG_SAE */
                        wpa_dbg(wpa_s, MSG_DEBUG, "AP: ignore received "
                                "management frame in non-AP mode");
                        break;
@@ -4579,6 +4589,15 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_BEACON_LOSS);
                bgscan_notify_beacon_loss(wpa_s);
                break;
+       case EVENT_EXTERNAL_AUTH:
+#ifdef CONFIG_SAE
+               if (!wpa_s->current_ssid) {
+                       wpa_printf(MSG_DEBUG, "SAE: current_ssid is NULL");
+                       break;
+               }
+               sme_external_auth_trigger(wpa_s, data);
+#endif /* CONFIG_SAE */
+               break;
        default:
                wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event);
                break;
index 723a77969aa79106f9375d650419f1da194d396f..b4b1c64294002e417504855685ef604465255af8 100644 (file)
@@ -83,7 +83,7 @@ static int sme_set_sae_group(struct wpa_supplicant *wpa_s)
 
 static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
                                                 struct wpa_ssid *ssid,
-                                                const u8 *bssid)
+                                                const u8 *bssid, int external)
 {
        struct wpabuf *buf;
        size_t len;
@@ -126,16 +126,18 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
        buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + len);
        if (buf == NULL)
                return NULL;
-
-       wpabuf_put_le16(buf, 1); /* Transaction seq# */
-       wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+       if (!external) {
+               wpabuf_put_le16(buf, 1); /* Transaction seq# */
+               wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+       }
        sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token);
 
        return buf;
 }
 
 
-static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s)
+static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s,
+                                                 int external)
 {
        struct wpabuf *buf;
 
@@ -143,8 +145,10 @@ static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s)
        if (buf == NULL)
                return NULL;
 
-       wpabuf_put_le16(buf, 2); /* Transaction seq# */
-       wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+       if (!external) {
+               wpabuf_put_le16(buf, 2); /* Transaction seq# */
+               wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+       }
        sae_write_confirm(&wpa_s->sme.sae, buf);
 
        return buf;
@@ -554,9 +558,9 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
        if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE) {
                if (start)
                        resp = sme_auth_build_sae_commit(wpa_s, ssid,
-                                                        bss->bssid);
+                                                        bss->bssid, 0);
                else
-                       resp = sme_auth_build_sae_confirm(wpa_s);
+                       resp = sme_auth_build_sae_confirm(wpa_s, 0);
                if (resp == NULL) {
                        wpas_connection_failed(wpa_s, bss->bssid);
                        return;
@@ -789,8 +793,150 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
 
 #ifdef CONFIG_SAE
 
+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)
+{
+       struct ieee80211_mgmt *resp;
+
+       resp = wpabuf_put(buf, offsetof(struct ieee80211_mgmt,
+                                       u.auth.variable));
+
+       resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
+                                          (WLAN_FC_STYPE_AUTH << 4));
+       os_memcpy(resp->da, da, ETH_ALEN);
+       os_memcpy(resp->sa, sa, ETH_ALEN);
+       os_memcpy(resp->bssid, da, ETH_ALEN);
+       resp->u.auth.auth_alg = WLAN_AUTH_SAE;
+       resp->seq_ctrl = seq_num << 4;
+       resp->u.auth.auth_transaction = auth_transaction;
+       resp->u.auth.status_code = WLAN_STATUS_SUCCESS;
+       if (params)
+               wpabuf_put_buf(buf, params);
+
+       return 0;
+}
+
+
+static void sme_external_auth_send_sae_commit(struct wpa_supplicant *wpa_s,
+                                             const u8 *bssid,
+                                             struct wpa_ssid *ssid)
+{
+       struct wpabuf *resp, *buf;
+
+       resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, 1);
+       if (!resp)
+               return;
+
+       wpa_s->sme.sae.state = SAE_COMMITTED;
+       buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + wpabuf_len(resp));
+       if (!buf) {
+               wpabuf_free(resp);
+               return;
+       }
+
+       wpa_s->sme.seq_num++;
+       sme_external_auth_build_buf(buf, resp, wpa_s->own_addr,
+                                   bssid, 1, wpa_s->sme.seq_num);
+       wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0);
+       wpabuf_free(resp);
+       wpabuf_free(buf);
+}
+
+
+static void sme_send_external_auth_status(struct wpa_supplicant *wpa_s,
+                                         u16 status)
+{
+       struct external_auth params;
+
+       os_memset(&params, 0, sizeof(params));
+       params.status = status;
+       os_memcpy(params.ssid, wpa_s->sme.ext_auth.ssid,
+                 wpa_s->sme.ext_auth.ssid_len);
+       params.ssid_len = wpa_s->sme.ext_auth.ssid_len;
+       os_memcpy(params.bssid, wpa_s->sme.ext_auth.bssid, ETH_ALEN);
+       wpa_drv_send_external_auth_status(wpa_s, &params);
+}
+
+
+static void sme_handle_external_auth_start(struct wpa_supplicant *wpa_s,
+                                          union wpa_event_data *data)
+{
+       struct wpa_ssid *ssid;
+       size_t ssid_str_len = data->external_auth.ssid_len;
+       u8 *ssid_str = data->external_auth.ssid;
+
+       /* Get the SSID conf from the ssid string obtained */
+       for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+               if (!wpas_network_disabled(wpa_s, ssid) &&
+                   ssid_str_len == ssid->ssid_len &&
+                   os_memcmp(ssid_str, ssid->ssid, ssid_str_len) == 0)
+                       break;
+       }
+       if (ssid)
+               sme_external_auth_send_sae_commit(wpa_s,
+                                                 data->external_auth.bssid,
+                                                 ssid);
+       else
+               sme_send_external_auth_status(wpa_s,
+                                             WLAN_STATUS_UNSPECIFIED_FAILURE);
+}
+
+
+static void sme_external_auth_send_sae_confirm(struct wpa_supplicant *wpa_s,
+                                              const u8 *da)
+{
+       struct wpabuf *resp, *buf;
+
+       resp = sme_auth_build_sae_confirm(wpa_s, 1);
+       if (!resp) {
+               wpa_printf(MSG_DEBUG, "SAE: Confirm message buf alloc failure");
+               return;
+       }
+
+       wpa_s->sme.sae.state = SAE_CONFIRMED;
+       buf = wpabuf_alloc(4 + SAE_CONFIRM_MAX_LEN + wpabuf_len(resp));
+       if (!buf) {
+               wpa_printf(MSG_DEBUG, "SAE: Auth Confirm buf alloc failure");
+               wpabuf_free(resp);
+               return;
+       }
+       wpa_s->sme.seq_num++;
+       sme_external_auth_build_buf(buf, resp, wpa_s->own_addr,
+                                   da, 2, wpa_s->sme.seq_num);
+       wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0);
+       wpabuf_free(resp);
+       wpabuf_free(buf);
+}
+
+
+void sme_external_auth_trigger(struct wpa_supplicant *wpa_s,
+                              union wpa_event_data *data)
+{
+       if (RSN_SELECTOR_GET(&data->external_auth.key_mgmt_suite) !=
+           RSN_AUTH_KEY_MGMT_SAE)
+               return;
+
+       if (data->external_auth.action == EXT_AUTH_START) {
+               os_memcpy(&wpa_s->sme.ext_auth, data,
+                         sizeof(struct external_auth));
+               wpa_s->sme.seq_num = 0;
+               wpa_s->sme.sae.state = SAE_NOTHING;
+               wpa_s->sme.sae.send_confirm = 0;
+               wpa_s->sme.sae_group_index = 0;
+               sme_handle_external_auth_start(wpa_s, data);
+       } else if (data->external_auth.action == EXT_AUTH_ABORT) {
+               /* Report failure to driver for the wrong trigger */
+               sme_send_external_auth_status(wpa_s,
+                                             WLAN_STATUS_UNSPECIFIED_FAILURE);
+       }
+}
+
+
 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 *groups;
 
@@ -800,7 +946,7 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
        if (auth_transaction == 1 &&
            status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ &&
            wpa_s->sme.sae.state == SAE_COMMITTED &&
-           wpa_s->current_bss && wpa_s->current_ssid) {
+           (external || wpa_s->current_bss) && wpa_s->current_ssid) {
                int default_groups[] = { 19, 20, 21, 25, 26, 0 };
                u16 group;
 
@@ -827,22 +973,32 @@ 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 = wpabuf_alloc_copy(data + sizeof(le16),
                                                         len - sizeof(le16));
-               sme_send_authentication(wpa_s, wpa_s->current_bss,
-                                       wpa_s->current_ssid, 1);
+               if (!external)
+                       sme_send_authentication(wpa_s, wpa_s->current_bss,
+                                               wpa_s->current_ssid, 1);
+               else
+                       sme_external_auth_send_sae_commit(
+                               wpa_s, wpa_s->sme.ext_auth.bssid,
+                               wpa_s->current_ssid);
                return 0;
        }
 
        if (auth_transaction == 1 &&
            status_code == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
            wpa_s->sme.sae.state == SAE_COMMITTED &&
-           wpa_s->current_bss && wpa_s->current_ssid) {
+           (external || wpa_s->current_bss) && wpa_s->current_ssid) {
                wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE group not supported");
                wpa_s->sme.sae_group_index++;
                if (sme_set_sae_group(wpa_s) < 0)
                        return -1; /* no other groups enabled */
                wpa_dbg(wpa_s, MSG_DEBUG, "SME: Try next enabled SAE group");
-               sme_send_authentication(wpa_s, wpa_s->current_bss,
-                                       wpa_s->current_ssid, 1);
+               if (!external)
+                       sme_send_authentication(wpa_s, wpa_s->current_bss,
+                                               wpa_s->current_ssid, 1);
+               else
+                       sme_external_auth_send_sae_commit(
+                               wpa_s, wpa_s->sme.ext_auth.bssid,
+                               wpa_s->current_ssid);
                return 0;
        }
 
@@ -855,7 +1011,7 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
                groups = wpa_s->conf->sae_groups;
 
                wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE commit");
-               if (wpa_s->current_bss == NULL ||
+               if ((!external && wpa_s->current_bss == NULL) ||
                    wpa_s->current_ssid == NULL)
                        return -1;
                if (wpa_s->sme.sae.state != SAE_COMMITTED)
@@ -880,8 +1036,11 @@ 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;
-               sme_send_authentication(wpa_s, wpa_s->current_bss,
-                                       wpa_s->current_ssid, 0);
+               if (!external)
+                       sme_send_authentication(wpa_s, wpa_s->current_bss,
+                                               wpa_s->current_ssid, 0);
+               else
+                       sme_external_auth_send_sae_confirm(wpa_s, sa);
                return 0;
        } else if (auth_transaction == 2) {
                wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE confirm");
@@ -891,11 +1050,59 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
                        return -1;
                wpa_s->sme.sae.state = SAE_ACCEPTED;
                sae_clear_temp_data(&wpa_s->sme.sae);
+
+               if (external) {
+                       /* Report success to driver */
+                       sme_send_external_auth_status(wpa_s,
+                                                     WLAN_STATUS_SUCCESS);
+               }
+
                return 1;
        }
 
        return -1;
 }
+
+
+void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s,
+                              const u8 *auth_frame, size_t len)
+{
+       const struct ieee80211_mgmt *header;
+       size_t auth_length;
+
+       header = (const struct ieee80211_mgmt *) auth_frame;
+       auth_length = IEEE80211_HDRLEN + sizeof(header->u.auth);
+
+       if (len < auth_length) {
+               /* Notify failure to the driver */
+               sme_send_external_auth_status(wpa_s,
+                                             WLAN_STATUS_UNSPECIFIED_FAILURE);
+               return;
+       }
+
+       if (header->u.auth.auth_alg == WLAN_AUTH_SAE) {
+               int res;
+
+               res = sme_sae_auth(wpa_s, header->u.auth.auth_transaction,
+                                  header->u.auth.status_code,
+                                  header->u.auth.variable,
+                                  len - auth_length, 1, header->sa);
+               if (res < 0) {
+                       /* Notify failure to the driver */
+                       sme_send_external_auth_status(
+                               wpa_s, WLAN_STATUS_UNSPECIFIED_FAILURE);
+                       return;
+               }
+               if (res != 1)
+                       return;
+
+               wpa_printf(MSG_DEBUG,
+                          "SME: SAE completed - setting PMK for 4-way handshake");
+               wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN,
+                              wpa_s->sme.sae.pmkid, wpa_s->pending_bssid);
+       }
+}
+
 #endif /* CONFIG_SAE */
 
 
@@ -936,7 +1143,7 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
                int res;
                res = sme_sae_auth(wpa_s, data->auth.auth_transaction,
                                   data->auth.status_code, data->auth.ies,
-                                  data->auth.ies_len);
+                                  data->auth.ies_len, 0, NULL);
                if (res < 0) {
                        wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
                        wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
index fd5c3b4e1ed8315cfec1e1afb339fa9b6940314b..f3c8220255745a32ce664c66a05d796ff7eeb2fc 100644 (file)
@@ -38,6 +38,10 @@ void sme_deinit(struct wpa_supplicant *wpa_s);
 
 int sme_proc_obss_scan(struct wpa_supplicant *wpa_s);
 void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable);
+void sme_external_auth_trigger(struct wpa_supplicant *wpa_s,
+                              union wpa_event_data *data);
+void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s,
+                              const u8 *auth_frame, size_t len);
 
 #else /* CONFIG_SME */
 
@@ -113,6 +117,16 @@ static inline void sme_sched_obss_scan(struct wpa_supplicant *wpa_s,
 {
 }
 
+static inline void sme_external_auth_trigger(struct wpa_supplicant *wpa_s,
+                                            union wpa_event_data *data)
+{
+}
+
+static inline void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s,
+                                            const u8 *auth_frame, size_t len)
+{
+}
+
 #endif /* CONFIG_SME */
 
 #endif /* SME_H */
index 8544aef22dcbd29c31752759ee05b9a1c5649dc5..4209c41452a4f192080b7750f81c813a589c3ace 100644 (file)
@@ -2473,6 +2473,10 @@ static u8 * wpas_populate_assoc_ies(
        }
 #endif /* CONFIG_FILS */
 #endif /* IEEE8021X_EAPOL */
+#ifdef CONFIG_SAE
+       if (wpa_s->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE))
+               algs = WPA_AUTH_ALG_SAE;
+#endif /* CONFIG_SAE */
 
        wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs);
        if (ssid->auth_alg) {
index 3516c3e7f3bb962f0fe42edf49d111097d40ca31..b154d3e4855c7f0fcfb823ac626293f2b94454b8 100644 (file)
@@ -789,6 +789,8 @@ struct wpa_supplicant {
                struct wpabuf *sae_token;
                int sae_group_index;
                unsigned int sae_pmksa_caching:1;
+               u16 seq_num;
+               struct external_auth ext_auth;
 #endif /* CONFIG_SAE */
        } sme;
 #endif /* CONFIG_SME */