]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
SAE: Add support for Anti-Clogging mechanism
authorJouni Malinen <j@w1.fi>
Mon, 31 Dec 2012 14:58:36 +0000 (16:58 +0200)
committerJouni Malinen <j@w1.fi>
Sat, 12 Jan 2013 15:51:52 +0000 (17:51 +0200)
hostapd can now be configured to use anti-clogging mechanism based on
the new sae_anti_clogging_threshold parameter (which is
dot11RSNASAEAntiCloggingThreshold in the standard). The token is
generated using a temporary key and the peer station's MAC address.
wpa_supplicant will re-try SAE authentication with the token included if
commit message is rejected with a token request.

Signed-hostap: Jouni Malinen <j@w1.fi>

hostapd/config_file.c
hostapd/hostapd.conf
src/ap/ap_config.c
src/ap/ap_config.h
src/ap/hostapd.h
src/ap/ieee802_11.c
src/common/sae.c
src/common/sae.h
wpa_supplicant/sme.c
wpa_supplicant/wpa_supplicant_i.h

index 2ba7cc1272599fe338f678f954ebc6e9db84199a..3032919c93adeded560bafc8d10604a92b505d04 100644 (file)
@@ -2935,6 +2935,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
 
                        wpabuf_free(bss->vendor_elements);
                        bss->vendor_elements = elems;
+               } else if (os_strcmp(buf, "sae_anti_clogging_threshold") == 0) {
+                       bss->sae_anti_clogging_threshold = atoi(pos);
                } else {
                        wpa_printf(MSG_ERROR, "Line %d: unknown configuration "
                                   "item '%s'", line, buf);
index 75b1941653508814c59cadcaf4d2329ba7ee7e15..a25fe378dd3b836e2bbf10ffd428c46c9826ae0e 100644 (file)
@@ -1037,6 +1037,10 @@ own_ip_addr=127.0.0.1
 # 1 = enabled
 #okc=1
 
+# SAE threshold for anti-clogging mechanism (dot11RSNASAEAntiCloggingThreshold)
+# This parameter defines how many open SAE instances can be in progress at the
+# same time before the anti-clogging mechanism is taken into use.
+#sae_anti_clogging_threshold=5
 
 ##### IEEE 802.11r configuration ##############################################
 
index 25d26e5e77a7022bcde26ac4696a0324910208a4..c9bf02b23b6d68f13db20cd92abf2664605a8c84 100644 (file)
@@ -89,6 +89,8 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
 #endif /* CONFIG_IEEE80211R */
 
        bss->radius_das_time_window = 300;
+
+       bss->sae_anti_clogging_threshold = 5;
 }
 
 
index a1d2b048b513104dc79066ef07d93011b48b3ea2..b3efff76d2aa4e03e5cab946aa06cfa89024a3fa 100644 (file)
@@ -455,6 +455,8 @@ struct hostapd_bss_config {
 #endif /* CONFIG_RADIUS_TEST */
 
        struct wpabuf *vendor_elements;
+
+       unsigned int sae_anti_clogging_threshold;
 };
 
 
index f1e7d9ff7ec5a73baf0364a53343ac19249bf0b6..c9087b31337e0abe407dc0ce3d462f185ff7acd9 100644 (file)
@@ -192,6 +192,12 @@ struct hostapd_data {
 #ifdef CONFIG_SQLITE
        struct hostapd_eap_user tmp_eap_user;
 #endif /* CONFIG_SQLITE */
+
+#ifdef CONFIG_SAE
+       /** Key used for generating SAE anti-clogging tokens */
+       u8 sae_token_key[8];
+       os_time_t last_sae_token_key_update;
+#endif /* CONFIG_SAE */
 };
 
 
index 88825b6ae55d4c883884c48e7c2f8b7e645a4d25..4a04aa906e6b6225bd08f1e11c2a62bce5a60cf6 100644 (file)
@@ -13,6 +13,8 @@
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
 #include "drivers/driver.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
@@ -344,7 +346,7 @@ static struct wpabuf * auth_process_sae_commit(struct hostapd_data *hapd,
        buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN);
        if (buf == NULL)
                return NULL;
-       sae_write_commit(sta->sae, buf);
+       sae_write_commit(sta->sae, buf, NULL);
 
        return buf;
 }
@@ -365,6 +367,74 @@ static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd,
 }
 
 
+static int use_sae_anti_clogging(struct hostapd_data *hapd)
+{
+       struct sta_info *sta;
+       unsigned int open = 0;
+
+       if (hapd->conf->sae_anti_clogging_threshold == 0)
+               return 1;
+
+       for (sta = hapd->sta_list; sta; sta = sta->next) {
+               if (!sta->sae)
+                       continue;
+               if (sta->sae->state != SAE_COMMITTED &&
+                   sta->sae->state != SAE_CONFIRMED)
+                       continue;
+               open++;
+               if (open >= hapd->conf->sae_anti_clogging_threshold)
+                       return 1;
+       }
+
+       return 0;
+}
+
+
+static int check_sae_token(struct hostapd_data *hapd, const u8 *addr,
+                          const u8 *token, size_t token_len)
+{
+       u8 mac[SHA256_MAC_LEN];
+
+       if (token_len != SHA256_MAC_LEN)
+               return -1;
+       if (hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key),
+                       addr, ETH_ALEN, mac) < 0 ||
+           os_memcmp(token, mac, SHA256_MAC_LEN) != 0)
+               return -1;
+
+       return 0;
+}
+
+
+static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd,
+                                           const u8 *addr)
+{
+       struct wpabuf *buf;
+       u8 *token;
+       struct os_time t;
+
+       os_get_time(&t);
+       if (hapd->last_sae_token_key_update == 0 ||
+           t.sec > hapd->last_sae_token_key_update + 60) {
+               random_get_bytes(hapd->sae_token_key,
+                                sizeof(hapd->sae_token_key));
+               wpa_hexdump(MSG_DEBUG, "SAE: Updated token key",
+                           hapd->sae_token_key, sizeof(hapd->sae_token_key));
+               hapd->last_sae_token_key_update = t.sec;
+       }
+
+       buf = wpabuf_alloc(SHA256_MAC_LEN);
+       if (buf == NULL)
+               return NULL;
+
+       token = wpabuf_put(buf, SHA256_MAC_LEN);
+       hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key),
+                   addr, ETH_ALEN, token);
+
+       return buf;
+}
+
+
 static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
                            const struct ieee80211_mgmt *mgmt, size_t len,
                            u8 auth_transaction)
@@ -373,6 +443,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
        struct wpabuf *data = NULL;
 
        if (!sta->sae) {
+               if (auth_transaction != 1)
+                       return;
                sta->sae = os_zalloc(sizeof(*sta->sae));
                if (sta->sae == NULL)
                        return;
@@ -380,18 +452,37 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
        }
 
        if (auth_transaction == 1) {
+               const u8 *token = NULL;
+               size_t token_len = 0;
                hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
                               HOSTAPD_LEVEL_DEBUG,
                               "start SAE authentication (RX commit)");
                resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable,
                                        ((const u8 *) mgmt) + len -
-                                       mgmt->u.auth.variable);
+                                       mgmt->u.auth.variable, &token,
+                                       &token_len);
+               if (token && check_sae_token(hapd, sta->addr, token, token_len)
+                   < 0) {
+                       wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "
+                                  "incorrect token from " MACSTR,
+                                  MAC2STR(sta->addr));
+                       return;
+               }
+
                if (resp == WLAN_STATUS_SUCCESS) {
-                       data = auth_process_sae_commit(hapd, sta);
-                       if (data == NULL)
-                               resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
-                       else
-                               sta->sae->state = SAE_COMMITTED;
+                       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;
+                       }
                }
        } else if (auth_transaction == 2) {
                if (sta->sae->state != SAE_COMMITTED) {
index 341aa26e28ba5756c87057a447cbf97022998f44..919e520732a996a927a0e370c0d1d32bd8273f3e 100644 (file)
@@ -487,21 +487,28 @@ int sae_process_commit(struct sae_data *sae)
 }
 
 
-void sae_write_commit(struct sae_data *sae, struct wpabuf *buf)
+void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
+                     const struct wpabuf *token)
 {
        wpabuf_put_le16(buf, 19); /* Finite Cyclic Group */
-       /* TODO: Anti-Clogging Token (if requested) */
+       if (token)
+               wpabuf_put_buf(buf, token);
        wpabuf_put_data(buf, sae->own_commit_scalar, 32);
        wpabuf_put_data(buf, sae->own_commit_element, 2 * 32);
 }
 
 
-u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len)
+u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
+                    const u8 **token, size_t *token_len)
 {
        const u8 *pos = data, *end = data + len;
        size_t val_len;
 
        wpa_hexdump(MSG_DEBUG, "SAE: Commit fields", data, len);
+       if (token)
+               *token = NULL;
+       if (token_len)
+               *token_len = 0;
 
        /* Check Finite Cyclic Group */
        if (pos + 2 > end)
@@ -514,6 +521,16 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len)
        pos += 2;
        val_len = 32;
 
+       if (pos + 3 * val_len < end) {
+               size_t tlen = end - (pos + 3 * val_len);
+               wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", pos, tlen);
+               if (token)
+                       *token = pos;
+               if (token_len)
+                       *token_len = tlen;
+               pos += tlen;
+       }
+
        if (pos + val_len > end) {
                wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar");
                return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -547,12 +564,6 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len)
                    sae->peer_commit_element, val_len);
        wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)",
                    sae->peer_commit_element + val_len, val_len);
-       pos += 2 * val_len;
-
-       if (end > pos) {
-               wpa_hexdump(MSG_DEBUG, "SAE: Unexpected extra data in commit",
-                           pos, end - pos);
-       }
 
        return WLAN_STATUS_SUCCESS;
 }
index 2cafd9d651af7fce7bf0101aa0ef2bd990b54657..0559eb1894cb35f0089a7e1f90ea38b5d15054b5 100644 (file)
@@ -29,8 +29,10 @@ int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
                       const u8 *password, size_t password_len,
                       struct sae_data *sae);
 int sae_process_commit(struct sae_data *sae);
-void sae_write_commit(struct sae_data *sae, struct wpabuf *buf);
-u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len);
+void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
+                     const struct wpabuf *token);
+u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
+                    const u8 **token, size_t *token_len);
 void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf);
 int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len);
 
index 814beb75615e42b306344cd45abb78aaa9075274..3aabe17c88f4abcac19315a7f3bd3c7c0c79cacb 100644 (file)
@@ -47,6 +47,7 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
                                                 const u8 *bssid)
 {
        struct wpabuf *buf;
+       size_t len;
 
        if (ssid->passphrase == NULL) {
                wpa_printf(MSG_DEBUG, "SAE: No password available");
@@ -61,13 +62,14 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
                return NULL;
        }
 
-       buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN);
+       len = wpa_s->sme.sae_token ? wpabuf_len(wpa_s->sme.sae_token) : 0;
+       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);
-       sae_write_commit(&wpa_s->sme.sae, buf);
+       sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token);
 
        return buf;
 }
@@ -406,6 +408,19 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
                "status code %u", auth_transaction, status_code);
        wpa_hexdump(MSG_DEBUG, "SME: SAE fields", data, len);
 
+       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) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE anti-clogging token "
+                       "requested");
+               wpabuf_free(wpa_s->sme.sae_token);
+               wpa_s->sme.sae_token = wpabuf_alloc_copy(data, len);
+               sme_send_authentication(wpa_s, wpa_s->current_bss,
+                                       wpa_s->current_ssid, 1);
+               return 0;
+       }
+
        if (status_code != WLAN_STATUS_SUCCESS)
                return -1;
 
@@ -416,7 +431,7 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
                        return -1;
                if (wpa_s->sme.sae.state != SAE_COMMITTED)
                        return -1;
-               if (sae_parse_commit(&wpa_s->sme.sae, data, len) !=
+               if (sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL) !=
                    WLAN_STATUS_SUCCESS)
                        return -1;
 
@@ -426,6 +441,8 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
                        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;
@@ -795,6 +812,10 @@ void sme_deinit(struct wpa_supplicant *wpa_s)
 #ifdef CONFIG_IEEE80211W
        sme_stop_sa_query(wpa_s);
 #endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_SAE
+       wpabuf_free(wpa_s->sme.sae_token);
+       wpa_s->sme.sae_token = NULL;
+#endif /* CONFIG_SAE */
 
        eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
        eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
index 74c3c0f971b6c9b68cd7e13379d396b400255b00..081928ab40c4f8e3320b44c6138a24c304a94fdf 100644 (file)
@@ -516,6 +516,7 @@ struct wpa_supplicant {
                u16 bss_max_idle_period;
 #ifdef CONFIG_SAE
                struct sae_data sae;
+               struct wpabuf *sae_token;
 #endif /* CONFIG_SAE */
        } sme;
 #endif /* CONFIG_SME */