SAE: Enhance AP implementation to handle auth for mesh interfaces
authorBob Copeland <me@bobcopeland.com>
Mon, 1 Sep 2014 04:23:30 +0000 (00:23 -0400)
committerJouni Malinen <j@w1.fi>
Sun, 16 Nov 2014 17:43:11 +0000 (19:43 +0200)
Add state transition logic to the SAE frame handling in order to more
fully implement the state machine from the IEEE 802.11 standard. Special
cases are needed for infrastructure BSS case to avoid unexpected
Authentication frame sequence by postponing transmission of the second
Authentication frame untile the STA sends its Confirm.

[original patch by: Thomas Pedersen <thomas@noack.us>]
Signed-off-by: Bob Copeland <me@bobcopeland.com>
src/ap/ieee802_11.c

index d59a2b4..8ced9af 100644 (file)
@@ -325,8 +325,8 @@ static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid,
 
 #ifdef CONFIG_SAE
 
-static struct wpabuf * auth_process_sae_commit(struct hostapd_data *hapd,
-                                              struct sta_info *sta)
+static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
+                                            struct sta_info *sta)
 {
        struct wpabuf *buf;
 
@@ -343,11 +343,6 @@ static struct wpabuf * auth_process_sae_commit(struct hostapd_data *hapd,
                return NULL;
        }
 
-       if (sae_process_commit(sta->sae) < 0) {
-               wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit");
-               return NULL;
-       }
-
        buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN);
        if (buf == NULL)
                return NULL;
@@ -372,6 +367,46 @@ static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd,
 }
 
 
+static int auth_sae_send_commit(struct hostapd_data *hapd,
+                               struct sta_info *sta,
+                               const u8 *bssid)
+{
+       struct wpabuf *data;
+
+       data = auth_build_sae_commit(hapd, sta);
+       if (data == NULL)
+               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+       send_auth_reply(hapd, sta->addr, bssid,
+                       WLAN_AUTH_SAE, 1, WLAN_STATUS_SUCCESS,
+                       wpabuf_head(data), wpabuf_len(data));
+
+       wpabuf_free(data);
+
+       return WLAN_STATUS_SUCCESS;
+}
+
+
+static int auth_sae_send_confirm(struct hostapd_data *hapd,
+                                struct sta_info *sta,
+                                const u8 *bssid)
+{
+       struct wpabuf *data;
+
+       data = auth_build_sae_confirm(hapd, sta);
+       if (data == NULL)
+               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+       send_auth_reply(hapd, sta->addr, bssid,
+                       WLAN_AUTH_SAE, 2, WLAN_STATUS_SUCCESS,
+                       wpabuf_head(data), wpabuf_len(data));
+
+       wpabuf_free(data);
+
+       return WLAN_STATUS_SUCCESS;
+}
+
+
 static int use_sae_anti_clogging(struct hostapd_data *hapd)
 {
        struct sta_info *sta;
@@ -441,6 +476,141 @@ static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd,
 }
 
 
+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);
+                       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.
+                                */
+                       }
+               } else {
+                       hostapd_logger(hapd, sta->addr,
+                                      HOSTAPD_MODULE_IEEE80211,
+                                      HOSTAPD_LEVEL_DEBUG,
+                                      "SAE confirm before commit");
+               }
+               break;
+       case SAE_COMMITTED:
+               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;
+               } else if (hapd->conf->mesh & MESH_ENABLED) {
+                       /*
+                        * In mesh case, follow SAE finite state machine and
+                        * send Commit now.
+                        */
+                       ret = auth_sae_send_commit(hapd, sta, bssid);
+                       if (ret)
+                               return ret;
+               } 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:
+               if (auth_transaction == 1) {
+                       ret = auth_sae_send_commit(hapd, sta, bssid);
+                       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;
+               } else {
+                       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);
+               }
+               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);
+               } else {
+                       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 handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
                            const struct ieee80211_mgmt *mgmt, size_t len,
                            u8 auth_transaction)
@@ -475,55 +645,34 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
                        return;
                }
 
-               if (resp == WLAN_STATUS_SUCCESS) {
-                       if (!token && use_sae_anti_clogging(hapd)) {
-                               wpa_printf(MSG_DEBUG, "SAE: Request anti-"
-                                          "clogging token from " MACSTR,
-                                          MAC2STR(sta->addr));
-                               data = auth_build_token_req(hapd, sta->addr);
-                               resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
-                       } else {
-                               data = auth_process_sae_commit(hapd, sta);
-                               if (data == NULL)
-                                       resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
-                               else
-                                       sta->sae->state = SAE_COMMITTED;
-                       }
+               if (resp != WLAN_STATUS_SUCCESS)
+                       goto reply;
+
+               if (!token && use_sae_anti_clogging(hapd)) {
+                       wpa_printf(MSG_DEBUG,
+                                  "SAE: Request anti-clogging token from "
+                                  MACSTR, MAC2STR(sta->addr));
+                       data = auth_build_token_req(hapd, sta->addr);
+                       resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
+                       goto reply;
                }
+
+               resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction);
        } else if (auth_transaction == 2) {
-               if (sta->sae->state != SAE_COMMITTED) {
-                       hostapd_logger(hapd, sta->addr,
-                                      HOSTAPD_MODULE_IEEE80211,
-                                      HOSTAPD_LEVEL_DEBUG,
-                                      "SAE confirm before commit");
-                       resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
-                       goto failed;
-               }
                hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
                               HOSTAPD_LEVEL_DEBUG,
                               "SAE authentication (RX confirm)");
-               if (sae_check_confirm(sta->sae, mgmt->u.auth.variable,
-                                      ((u8 *) mgmt) + len -
-                                      mgmt->u.auth.variable) < 0) {
-                       resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
-               } else {
-                       resp = WLAN_STATUS_SUCCESS;
-                       sta->flags |= WLAN_STA_AUTH;
-                       wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
-                       sta->auth_alg = WLAN_AUTH_SAE;
-                       mlme_authenticate_indication(hapd, sta);
-
-                       data = auth_build_sae_confirm(hapd, sta);
-                       if (data == NULL)
+               if (sta->sae->state >= SAE_CONFIRMED ||
+                   !(hapd->conf->mesh & MESH_ENABLED)) {
+                       if (sae_check_confirm(sta->sae, mgmt->u.auth.variable,
+                                             ((u8 *) mgmt) + len -
+                                             mgmt->u.auth.variable) < 0) {
                                resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
-                       else {
-                               sta->sae->state = SAE_ACCEPTED;
-                               wpa_auth_pmksa_add_sae(hapd->wpa_auth,
-                                                      sta->addr,
-                                                      sta->sae->pmk);
-                               sae_clear_temp_data(sta->sae);
+                               goto reply;
                        }
                }
+               resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction);
+
        } else {
                hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
                               HOSTAPD_LEVEL_DEBUG,
@@ -532,13 +681,13 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
                resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
        }
 
-failed:
-       sta->auth_alg = WLAN_AUTH_SAE;
-
-       send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
-                       auth_transaction, resp,
-                       data ? wpabuf_head(data) : (u8 *) "",
-                       data ? wpabuf_len(data) : 0);
+reply:
+       if (resp != WLAN_STATUS_SUCCESS) {
+               send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+                               auth_transaction, resp,
+                               data ? wpabuf_head(data) : (u8 *) "",
+                               data ? wpabuf_len(data) : 0);
+       }
        wpabuf_free(data);
 }
 #endif /* CONFIG_SAE */