From d136c376f2aa5414ad30f345085a00971e2156aa Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 31 Dec 2012 16:58:36 +0200 Subject: [PATCH] SAE: Add support for Anti-Clogging mechanism 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 --- hostapd/config_file.c | 2 + hostapd/hostapd.conf | 4 ++ src/ap/ap_config.c | 2 + src/ap/ap_config.h | 2 + src/ap/hostapd.h | 6 ++ src/ap/ieee802_11.c | 105 ++++++++++++++++++++++++++++-- src/common/sae.c | 29 ++++++--- src/common/sae.h | 6 +- wpa_supplicant/sme.c | 27 +++++++- wpa_supplicant/wpa_supplicant_i.h | 1 + 10 files changed, 163 insertions(+), 21 deletions(-) diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 2ba7cc127..3032919c9 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -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); diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index 75b194165..a25fe378d 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -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 ############################################## diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 25d26e5e7..c9bf02b23 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -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; } diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index a1d2b048b..b3efff76d 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -455,6 +455,8 @@ struct hostapd_bss_config { #endif /* CONFIG_RADIUS_TEST */ struct wpabuf *vendor_elements; + + unsigned int sae_anti_clogging_threshold; }; diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index f1e7d9ff7..c9087b313 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -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 */ }; diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 88825b6ae..4a04aa906 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -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) { diff --git a/src/common/sae.c b/src/common/sae.c index 341aa26e2..919e52073 100644 --- a/src/common/sae.c +++ b/src/common/sae.c @@ -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; } diff --git a/src/common/sae.h b/src/common/sae.h index 2cafd9d65..0559eb189 100644 --- a/src/common/sae.h +++ b/src/common/sae.h @@ -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); diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 814beb756..3aabe17c8 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -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); diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 74c3c0f97..081928ab4 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -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 */