From 5e3a759cd4136fbf7958fd50cc185dc3590ded97 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Fri, 15 Mar 2019 00:03:29 +0200 Subject: [PATCH] SAE: Improved queuing policy for pending authentication frames The previous design of simply queuing all SAE commit messages was not exactly good at allowing recovery from a flooding state if the valid peer used frequent retransmissions of the SAE message. This could happen, e.g., with mesh BSSs using SAE. The frequent retransmissions and restarts of SAE authentication combined with SAE confirm messages bypassing the queue ended up in not being able to finish SAE exchange successfully. Fix this by modifying the queuing policy to queue SAE confirm messages if there is a queued SAE commit message from the same peer so that the messages within the same exchange do not get reordered. In addition, replace queued SAE commit/confirm message if a new matching message is received from the same peer STA. This is useful for the case where the peer restarts SAE more quickly than the local end has time to process the queued messages. Signed-off-by: Jouni Malinen --- src/ap/ieee802_11.c | 71 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 11 deletions(-) diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 78f01dfa5..03e1756f6 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -1278,7 +1278,7 @@ void auth_sae_process_commit(void *eloop_ctx, void *user_ctx) if (!q) return; wpa_printf(MSG_DEBUG, - "SAE: Process next available commit message from queue"); + "SAE: Process next available message from queue"); dl_list_del(&q->list); handle_auth(hapd, (const struct ieee80211_mgmt *) q->msg, q->len, q->rssi, 1); @@ -1292,36 +1292,79 @@ void auth_sae_process_commit(void *eloop_ctx, void *user_ctx) } -static void auth_sae_queue_commit(struct hostapd_data *hapd, - const struct ieee80211_mgmt *mgmt, size_t len, - int rssi) +static void auth_sae_queue(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len, + int rssi) { - struct hostapd_sae_commit_queue *q; + struct hostapd_sae_commit_queue *q, *q2; unsigned int queue_len; + const struct ieee80211_mgmt *mgmt2; queue_len = dl_list_len(&hapd->sae_commit_queue); if (queue_len >= 15) { wpa_printf(MSG_DEBUG, - "SAE: No more room in commit message queue - drop the new frame from " + "SAE: No more room in message queue - drop the new frame from " MACSTR, MAC2STR(mgmt->sa)); return; } - wpa_printf(MSG_DEBUG, "SAE: Queue Authentication commit message from " - MACSTR " for processing", MAC2STR(mgmt->sa)); + wpa_printf(MSG_DEBUG, "SAE: Queue Authentication message from " + MACSTR " for processing (queue_len %u)", MAC2STR(mgmt->sa), + queue_len); q = os_zalloc(sizeof(*q) + len); if (!q) return; q->rssi = rssi; q->len = len; os_memcpy(q->msg, mgmt, len); + + /* Check whether there is already a queued Authentication frame from the + * same station with the same transaction number and if so, replace that + * queue entry with the new one. This avoids issues with a peer that + * sends multiple times (e.g., due to frequent SAE retries). There is no + * point in us trying to process the old attempts after a new one has + * obsoleted them. */ + dl_list_for_each(q2, &hapd->sae_commit_queue, + struct hostapd_sae_commit_queue, list) { + mgmt2 = (const struct ieee80211_mgmt *) q2->msg; + if (os_memcmp(mgmt->sa, mgmt2->sa, ETH_ALEN) == 0 && + mgmt->u.auth.auth_transaction == + mgmt2->u.auth.auth_transaction) { + wpa_printf(MSG_DEBUG, + "SAE: Replace queued message from same STA with same transaction number"); + dl_list_add(&q2->list, &q->list); + dl_list_del(&q2->list); + os_free(q2); + goto queued; + } + } + + /* No pending identical entry, so add to the end of the queue */ dl_list_add_tail(&hapd->sae_commit_queue, &q->list); + +queued: if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL)) return; eloop_register_timeout(0, queue_len * 50000, auth_sae_process_commit, hapd, NULL); } + +static int auth_sae_queued_addr(struct hostapd_data *hapd, const u8 *addr) +{ + struct hostapd_sae_commit_queue *q; + const struct ieee80211_mgmt *mgmt; + + dl_list_for_each(q, &hapd->sae_commit_queue, + struct hostapd_sae_commit_queue, list) { + mgmt = (const struct ieee80211_mgmt *) q->msg; + if (os_memcmp(addr, mgmt->sa, ETH_ALEN) == 0) + return 1; + } + + return 0; +} + #endif /* CONFIG_SAE */ @@ -2135,11 +2178,17 @@ static void handle_auth(struct hostapd_data *hapd, return; #ifdef CONFIG_SAE - if (auth_alg == WLAN_AUTH_SAE && auth_transaction == 1 && !from_queue) { + if (auth_alg == WLAN_AUTH_SAE && !from_queue && + (auth_transaction == 1 || + (auth_transaction == 2 && auth_sae_queued_addr(hapd, mgmt->sa)))) { /* Handle SAE Authentication commit message through a queue to * provide more control for postponing the needed heavy - * processing under a possible DoS attack scenario. */ - auth_sae_queue_commit(hapd, mgmt, len, rssi); + * processing under a possible DoS attack scenario. In addition, + * queue SAE Authentication confirm message if there happens to + * be a queued commit message from the same peer. This is needed + * to avoid reordering Authentication frames within the same + * SAE exchange. */ + auth_sae_queue(hapd, mgmt, len, rssi); return; } #endif /* CONFIG_SAE */