+static void sme_auth_start_cb(struct wpa_radio_work *work, int deinit)
+{
+ struct wpa_connect_work *cwork = work->ctx;
+ struct wpa_supplicant *wpa_s = work->wpa_s;
+
+ if (deinit) {
+ if (work->started)
+ wpa_s->connect_work = NULL;
+
+ wpas_connect_work_free(cwork);
+ return;
+ }
+
+ wpa_s->connect_work = work;
+
+ if (cwork->bss_removed ||
+ !wpas_valid_bss_ssid(wpa_s, cwork->bss, cwork->ssid) ||
+ wpas_network_disabled(wpa_s, cwork->ssid)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: BSS/SSID entry for authentication not valid anymore - drop connection attempt");
+ wpas_connect_work_done(wpa_s);
+ return;
+ }
+
+ sme_send_authentication(wpa_s, cwork->bss, cwork->ssid, 1);
+}
+
+
+void sme_authenticate(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss, struct wpa_ssid *ssid)
+{
+ struct wpa_connect_work *cwork;
+
+ if (bss == NULL || ssid == NULL)
+ return;
+ if (wpa_s->connect_work) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Reject sme_authenticate() call since connect_work exist");
+ return;
+ }
+
+ if (radio_work_pending(wpa_s, "sme-connect")) {
+ /*
+ * The previous sme-connect work might no longer be valid due to
+ * the fact that the BSS list was updated. In addition, it makes
+ * sense to adhere to the 'newer' decision.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "SME: Remove previous pending sme-connect");
+ radio_remove_works(wpa_s, "sme-connect", 0);
+ }
+
+ wpas_abort_ongoing_scan(wpa_s);
+
+ cwork = os_zalloc(sizeof(*cwork));
+ if (cwork == NULL)
+ return;
+ cwork->bss = bss;
+ cwork->ssid = ssid;
+ cwork->sme = 1;
+
+#ifdef CONFIG_SAE
+ wpa_s->sme.sae.state = SAE_NOTHING;
+ wpa_s->sme.sae.send_confirm = 0;
+ wpa_s->sme.sae_group_index = 0;
+#endif /* CONFIG_SAE */
+
+ if (radio_add_work(wpa_s, bss->freq, "sme-connect", 1,
+ sme_auth_start_cb, cwork) < 0)
+ wpas_connect_work_free(cwork);
+}
+
+
+#ifdef CONFIG_SAE
+
+static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
+ u16 status_code, const u8 *data, size_t len)
+{
+ int *groups;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE authentication transaction %u "
+ "status code %u", auth_transaction, status_code);
+
+ 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) {
+ int default_groups[] = { 19, 20, 21, 25, 26, 0 };
+ u16 group;
+
+ groups = wpa_s->conf->sae_groups;
+ if (!groups || groups[0] <= 0)
+ groups = default_groups;
+
+ if (len < sizeof(le16)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "SME: Too short SAE anti-clogging token request");
+ return -1;
+ }
+ group = WPA_GET_LE16(data);
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "SME: SAE anti-clogging token requested (group %u)",
+ group);
+ if (sae_group_allowed(&wpa_s->sme.sae, groups, group) !=
+ WLAN_STATUS_SUCCESS) {
+ wpa_dbg(wpa_s, MSG_ERROR,
+ "SME: SAE group %u of anti-clogging request is invalid",
+ group);
+ return -1;
+ }
+ 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);
+ 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) {
+ 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);
+ return 0;
+ }
+
+ if (status_code != WLAN_STATUS_SUCCESS)
+ return -1;
+
+ if (auth_transaction == 1) {
+ u16 res;
+
+ groups = wpa_s->conf->sae_groups;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE commit");
+ if (wpa_s->current_bss == NULL ||
+ wpa_s->current_ssid == NULL)
+ return -1;
+ if (wpa_s->sme.sae.state != SAE_COMMITTED)
+ return -1;
+ if (groups && groups[0] <= 0)
+ groups = NULL;
+ res = sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL,
+ groups);
+ if (res == SAE_SILENTLY_DISCARD) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Drop commit message due to reflection attack");
+ return 0;
+ }
+ if (res != WLAN_STATUS_SUCCESS)
+ return -1;
+
+ if (sae_process_commit(&wpa_s->sme.sae) < 0) {
+ wpa_printf(MSG_DEBUG, "SAE: Failed to process peer "
+ "commit");
+ return -1;
+ }
+
+ 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);
+ return 0;
+ } else if (auth_transaction == 2) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE confirm");
+ if (wpa_s->sme.sae.state != SAE_CONFIRMED)
+ return -1;
+ if (sae_check_confirm(&wpa_s->sme.sae, data, len) < 0)
+ return -1;
+ wpa_s->sme.sae.state = SAE_ACCEPTED;
+ sae_clear_temp_data(&wpa_s->sme.sae);
+ return 1;
+ }
+
+ return -1;
+}
+#endif /* CONFIG_SAE */
+
+