diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h index 81b4b5537..078de23f7 100644 --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -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 */ diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 8e46b76e8..4f3604358 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -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; diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 723a77969..b4b1c6429 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -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(¶ms, 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, ¶ms); +} + + +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); diff --git a/wpa_supplicant/sme.h b/wpa_supplicant/sme.h index fd5c3b4e1..f3c822025 100644 --- a/wpa_supplicant/sme.h +++ b/wpa_supplicant/sme.h @@ -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 */ diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 8544aef22..4209c4145 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -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) { diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 3516c3e7f..b154d3e48 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -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 */