+static int sae_check_big_sync(struct sta_info *sta)
+{
+ if (sta->sae->sync > dot11RSNASAESync) {
+ sta->sae->state = SAE_NOTHING;
+ sta->sae->sync = 0;
+ return -1;
+ }
+ return 0;
+}
+
+
+static void auth_sae_retransmit_timer(void *eloop_ctx, void *eloop_data)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = eloop_data;
+ int ret;
+
+ if (sae_check_big_sync(sta))
+ return;
+ sta->sae->sync++;
+ wpa_printf(MSG_DEBUG, "SAE: Auth SAE retransmit timer for " MACSTR
+ " (sync=%d state=%d)",
+ MAC2STR(sta->addr), sta->sae->sync, sta->sae->state);
+
+ switch (sta->sae->state) {
+ case SAE_COMMITTED:
+ ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0);
+ eloop_register_timeout(0,
+ hapd->dot11RSNASAERetransPeriod * 1000,
+ auth_sae_retransmit_timer, hapd, sta);
+ break;
+ case SAE_CONFIRMED:
+ ret = auth_sae_send_confirm(hapd, sta, hapd->own_addr);
+ eloop_register_timeout(0,
+ hapd->dot11RSNASAERetransPeriod * 1000,
+ auth_sae_retransmit_timer, hapd, sta);
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+
+ if (ret != WLAN_STATUS_SUCCESS)
+ wpa_printf(MSG_INFO, "SAE: Failed to retransmit: ret=%d", ret);
+}
+
+
+void sae_clear_retransmit_timer(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta);
+}
+
+
+static void sae_set_retransmit_timer(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ if (!(hapd->conf->mesh & MESH_ENABLED))
+ return;
+
+ eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta);
+ eloop_register_timeout(0, hapd->dot11RSNASAERetransPeriod * 1000,
+ auth_sae_retransmit_timer, hapd, sta);
+}
+
+
+void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ sta->flags |= WLAN_STA_AUTH;
+ sta->auth_alg = WLAN_AUTH_SAE;
+ mlme_authenticate_indication(hapd, sta);
+ wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+ sta->sae->state = SAE_ACCEPTED;
+ wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
+ sta->sae->pmk, sta->sae->pmkid);
+}
+
+
+static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *bssid, u8 auth_transaction)
+{
+ int ret;
+
+ if (auth_transaction != 1 && auth_transaction != 2)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ switch (sta->sae->state) {
+ case SAE_NOTHING:
+ if (auth_transaction == 1) {
+ ret = auth_sae_send_commit(hapd, sta, bssid, 1);
+ if (ret)
+ return ret;
+ sta->sae->state = SAE_COMMITTED;
+
+ if (sae_process_commit(sta->sae) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ /*
+ * In mesh case, both Commit and Confirm can be sent
+ * immediately. In infrastructure BSS, only a single
+ * Authentication frame (Commit) is expected from the AP
+ * here and the second one (Confirm) will be sent once
+ * the STA has sent its second Authentication frame
+ * (Confirm).
+ */
+ if (hapd->conf->mesh & MESH_ENABLED) {
+ /*
+ * Send both Commit and Confirm immediately
+ * based on SAE finite state machine
+ * Nothing -> Confirm transition.
+ */
+ ret = auth_sae_send_confirm(hapd, sta, bssid);
+ if (ret)
+ return ret;
+ sta->sae->state = SAE_CONFIRMED;
+ } else {
+ /*
+ * For infrastructure BSS, send only the Commit
+ * message now to get alternating sequence of
+ * Authentication frames between the AP and STA.
+ * Confirm will be sent in
+ * Commited -> Confirmed/Accepted transition
+ * when receiving Confirm from STA.
+ */
+ }
+ sta->sae->sync = 0;
+ sae_set_retransmit_timer(hapd, sta);
+ } else {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "SAE confirm before commit");
+ }
+ break;
+ case SAE_COMMITTED:
+ sae_clear_retransmit_timer(hapd, sta);
+ if (auth_transaction == 1) {
+ if (sae_process_commit(sta->sae) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ ret = auth_sae_send_confirm(hapd, sta, bssid);
+ if (ret)
+ return ret;
+ sta->sae->state = SAE_CONFIRMED;
+ sta->sae->sync = 0;
+ sae_set_retransmit_timer(hapd, sta);
+ } else if (hapd->conf->mesh & MESH_ENABLED) {
+ /*
+ * In mesh case, follow SAE finite state machine and
+ * send Commit now, if sync count allows.
+ */
+ if (sae_check_big_sync(sta))
+ return WLAN_STATUS_SUCCESS;
+ sta->sae->sync++;
+
+ ret = auth_sae_send_commit(hapd, sta, bssid, 0);
+ if (ret)
+ return ret;
+
+ sae_set_retransmit_timer(hapd, sta);
+ } else {
+ /*
+ * For instructure BSS, send the postponed Confirm from
+ * Nothing -> Confirmed transition that was reduced to
+ * Nothing -> Committed above.
+ */
+ ret = auth_sae_send_confirm(hapd, sta, bssid);
+ if (ret)
+ return ret;
+
+ sta->sae->state = SAE_CONFIRMED;
+
+ /*
+ * Since this was triggered on Confirm RX, run another
+ * step to get to Accepted without waiting for
+ * additional events.
+ */
+ return sae_sm_step(hapd, sta, bssid, auth_transaction);
+ }
+ break;
+ case SAE_CONFIRMED:
+ sae_clear_retransmit_timer(hapd, sta);
+ if (auth_transaction == 1) {
+ if (sae_check_big_sync(sta))
+ return WLAN_STATUS_SUCCESS;
+ sta->sae->sync++;
+
+ ret = auth_sae_send_commit(hapd, sta, bssid, 1);
+ if (ret)
+ return ret;
+
+ if (sae_process_commit(sta->sae) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ ret = auth_sae_send_confirm(hapd, sta, bssid);
+ if (ret)
+ return ret;
+
+ sae_set_retransmit_timer(hapd, sta);
+ } else {
+ sae_accept_sta(hapd, sta);
+ }
+ break;
+ case SAE_ACCEPTED:
+ if (auth_transaction == 1) {
+ wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR
+ ") doing reauthentication",
+ MAC2STR(sta->addr));
+ ap_free_sta(hapd, sta);
+ wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+ } else {
+ if (sae_check_big_sync(sta))
+ return WLAN_STATUS_SUCCESS;
+ sta->sae->sync++;
+
+ ret = auth_sae_send_confirm(hapd, sta, bssid);
+ sae_clear_temp_data(sta->sae);
+ if (ret)
+ return ret;
+ }
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "SAE: invalid state %d",
+ sta->sae->state);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+static void sae_pick_next_group(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ struct sae_data *sae = sta->sae;
+ int i, *groups = hapd->conf->sae_groups;
+
+ if (sae->state != SAE_COMMITTED)
+ return;
+
+ wpa_printf(MSG_DEBUG, "SAE: Previously selected group: %d", sae->group);
+
+ for (i = 0; groups && groups[i] > 0; i++) {
+ if (sae->group == groups[i])
+ break;
+ }
+
+ if (!groups || groups[i] <= 0) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Previously selected group not found from the current configuration");
+ return;
+ }
+
+ for (;;) {
+ i++;
+ if (groups[i] <= 0) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: No alternative group enabled");
+ return;
+ }
+
+ if (sae_set_group(sae, groups[i]) < 0)
+ continue;
+
+ break;
+ }
+ wpa_printf(MSG_DEBUG, "SAE: Selected new group: %d", groups[i]);
+}
+
+