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:
parent
4838ff3ef4
commit
d136c376f2
10 changed files with 163 additions and 21 deletions
|
@ -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);
|
||||
|
|
|
@ -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 ##############################################
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -455,6 +455,8 @@ struct hostapd_bss_config {
|
|||
#endif /* CONFIG_RADIUS_TEST */
|
||||
|
||||
struct wpabuf *vendor_elements;
|
||||
|
||||
unsigned int sae_anti_clogging_threshold;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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 */
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in a new issue