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 <j@w1.fi>
This commit is contained in:
Jouni Malinen 2012-12-31 16:58:36 +02:00
parent 4838ff3ef4
commit d136c376f2
10 changed files with 163 additions and 21 deletions

View file

@ -2935,6 +2935,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
wpabuf_free(bss->vendor_elements); wpabuf_free(bss->vendor_elements);
bss->vendor_elements = elems; bss->vendor_elements = elems;
} else if (os_strcmp(buf, "sae_anti_clogging_threshold") == 0) {
bss->sae_anti_clogging_threshold = atoi(pos);
} else { } else {
wpa_printf(MSG_ERROR, "Line %d: unknown configuration " wpa_printf(MSG_ERROR, "Line %d: unknown configuration "
"item '%s'", line, buf); "item '%s'", line, buf);

View file

@ -1037,6 +1037,10 @@ own_ip_addr=127.0.0.1
# 1 = enabled # 1 = enabled
#okc=1 #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 ############################################## ##### IEEE 802.11r configuration ##############################################

View file

@ -89,6 +89,8 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
#endif /* CONFIG_IEEE80211R */ #endif /* CONFIG_IEEE80211R */
bss->radius_das_time_window = 300; bss->radius_das_time_window = 300;
bss->sae_anti_clogging_threshold = 5;
} }

View file

@ -455,6 +455,8 @@ struct hostapd_bss_config {
#endif /* CONFIG_RADIUS_TEST */ #endif /* CONFIG_RADIUS_TEST */
struct wpabuf *vendor_elements; struct wpabuf *vendor_elements;
unsigned int sae_anti_clogging_threshold;
}; };

View file

@ -192,6 +192,12 @@ struct hostapd_data {
#ifdef CONFIG_SQLITE #ifdef CONFIG_SQLITE
struct hostapd_eap_user tmp_eap_user; struct hostapd_eap_user tmp_eap_user;
#endif /* CONFIG_SQLITE */ #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 */
}; };

View file

@ -13,6 +13,8 @@
#include "utils/common.h" #include "utils/common.h"
#include "utils/eloop.h" #include "utils/eloop.h"
#include "crypto/crypto.h" #include "crypto/crypto.h"
#include "crypto/sha256.h"
#include "crypto/random.h"
#include "drivers/driver.h" #include "drivers/driver.h"
#include "common/ieee802_11_defs.h" #include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.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); buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN);
if (buf == NULL) if (buf == NULL)
return NULL; return NULL;
sae_write_commit(sta->sae, buf); sae_write_commit(sta->sae, buf, NULL);
return buf; 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, static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
const struct ieee80211_mgmt *mgmt, size_t len, const struct ieee80211_mgmt *mgmt, size_t len,
u8 auth_transaction) u8 auth_transaction)
@ -373,6 +443,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
struct wpabuf *data = NULL; struct wpabuf *data = NULL;
if (!sta->sae) { if (!sta->sae) {
if (auth_transaction != 1)
return;
sta->sae = os_zalloc(sizeof(*sta->sae)); sta->sae = os_zalloc(sizeof(*sta->sae));
if (sta->sae == NULL) if (sta->sae == NULL)
return; return;
@ -380,18 +452,37 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
} }
if (auth_transaction == 1) { if (auth_transaction == 1) {
const u8 *token = NULL;
size_t token_len = 0;
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG, HOSTAPD_LEVEL_DEBUG,
"start SAE authentication (RX commit)"); "start SAE authentication (RX commit)");
resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable, resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable,
((const u8 *) mgmt) + len - ((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) { if (resp == WLAN_STATUS_SUCCESS) {
data = auth_process_sae_commit(hapd, sta); if (!token && use_sae_anti_clogging(hapd)) {
if (data == NULL) wpa_printf(MSG_DEBUG, "SAE: Request anti-"
resp = WLAN_STATUS_UNSPECIFIED_FAILURE; "clogging token from " MACSTR,
else MAC2STR(sta->addr));
sta->sae->state = SAE_COMMITTED; 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) { } else if (auth_transaction == 2) {
if (sta->sae->state != SAE_COMMITTED) { if (sta->sae->state != SAE_COMMITTED) {

View 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 */ 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_scalar, 32);
wpabuf_put_data(buf, sae->own_commit_element, 2 * 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; const u8 *pos = data, *end = data + len;
size_t val_len; size_t val_len;
wpa_hexdump(MSG_DEBUG, "SAE: Commit fields", data, len); wpa_hexdump(MSG_DEBUG, "SAE: Commit fields", data, len);
if (token)
*token = NULL;
if (token_len)
*token_len = 0;
/* Check Finite Cyclic Group */ /* Check Finite Cyclic Group */
if (pos + 2 > end) if (pos + 2 > end)
@ -514,6 +521,16 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len)
pos += 2; pos += 2;
val_len = 32; 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) { if (pos + val_len > end) {
wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar"); wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar");
return WLAN_STATUS_UNSPECIFIED_FAILURE; 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); sae->peer_commit_element, val_len);
wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)", wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)",
sae->peer_commit_element + val_len, val_len); 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; return WLAN_STATUS_SUCCESS;
} }

View file

@ -29,8 +29,10 @@ int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
const u8 *password, size_t password_len, const u8 *password, size_t password_len,
struct sae_data *sae); struct sae_data *sae);
int sae_process_commit(struct sae_data *sae); 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,
u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len); 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); 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); int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len);

View file

@ -47,6 +47,7 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
const u8 *bssid) const u8 *bssid)
{ {
struct wpabuf *buf; struct wpabuf *buf;
size_t len;
if (ssid->passphrase == NULL) { if (ssid->passphrase == NULL) {
wpa_printf(MSG_DEBUG, "SAE: No password available"); 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; 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) if (buf == NULL)
return NULL; return NULL;
wpabuf_put_le16(buf, 1); /* Transaction seq# */ wpabuf_put_le16(buf, 1); /* Transaction seq# */
wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); 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; 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); "status code %u", auth_transaction, status_code);
wpa_hexdump(MSG_DEBUG, "SME: SAE fields", data, len); 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) if (status_code != WLAN_STATUS_SUCCESS)
return -1; return -1;
@ -416,7 +431,7 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
return -1; return -1;
if (wpa_s->sme.sae.state != SAE_COMMITTED) if (wpa_s->sme.sae.state != SAE_COMMITTED)
return -1; 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) WLAN_STATUS_SUCCESS)
return -1; return -1;
@ -426,6 +441,8 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
return -1; return -1;
} }
wpabuf_free(wpa_s->sme.sae_token);
wpa_s->sme.sae_token = NULL;
sme_send_authentication(wpa_s, wpa_s->current_bss, sme_send_authentication(wpa_s, wpa_s->current_bss,
wpa_s->current_ssid, 0); wpa_s->current_ssid, 0);
return 0; return 0;
@ -795,6 +812,10 @@ void sme_deinit(struct wpa_supplicant *wpa_s)
#ifdef CONFIG_IEEE80211W #ifdef CONFIG_IEEE80211W
sme_stop_sa_query(wpa_s); sme_stop_sa_query(wpa_s);
#endif /* CONFIG_IEEE80211W */ #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_assoc_timer, wpa_s, NULL);
eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);

View file

@ -516,6 +516,7 @@ struct wpa_supplicant {
u16 bss_max_idle_period; u16 bss_max_idle_period;
#ifdef CONFIG_SAE #ifdef CONFIG_SAE
struct sae_data sae; struct sae_data sae;
struct wpabuf *sae_token;
#endif /* CONFIG_SAE */ #endif /* CONFIG_SAE */
} sme; } sme;
#endif /* CONFIG_SME */ #endif /* CONFIG_SME */