SAE: Enforce single use for anti-clogging tokens

Add a 16-bit token index into the anti-clogging token. This can be used
to enforce only a single use of each issued anti-clogging token request.
The token value is now token-index |
last-30-octets-of(HMAC-SHA256(sae_token_key, STA-MAC-address |
token-index)), i.e., the first two octets of the SHA256 hash value are
replaced with the token-index and token-index itself is protected as
part of the HMAC context data.

Track the used 16-bit token index values and accept received tokens only
if they use an index value that has been requested, but has not yet been
used. This makes it a bit more difficult for an attacker to perform DoS
attacks against the heavy CPU operations needed for processing SAE
commit since the attacker cannot simply replay the same frame multiple
times and instead, needs to request each token separately.

While this does not add significant extra processing/CPU need for the
attacker, this can be helpful in combination with the queued processing
of SAE commit messages in enforcing more delay during flooding of SAE
commit messages since the new anti-clogging token values are not
returned before the new message goes through the processing queue.

Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
This commit is contained in:
Jouni Malinen 2019-03-01 22:05:52 +02:00 committed by Jouni Malinen
parent ff9f40aee1
commit a9af1da0b5
2 changed files with 62 additions and 6 deletions

View File

@ -314,6 +314,8 @@ struct hostapd_data {
/** Key used for generating SAE anti-clogging tokens */ /** Key used for generating SAE anti-clogging tokens */
u8 sae_token_key[8]; u8 sae_token_key[8];
struct os_reltime last_sae_token_key_update; struct os_reltime last_sae_token_key_update;
u16 sae_token_idx;
u16 sae_pending_token_idx[256];
int dot11RSNASAERetransPeriod; /* msec */ int dot11RSNASAERetransPeriod; /* msec */
struct dl_list sae_commit_queue; /* struct hostapd_sae_commit_queue */ struct dl_list sae_commit_queue; /* struct hostapd_sae_commit_queue */
#endif /* CONFIG_SAE */ #endif /* CONFIG_SAE */

View File

@ -532,17 +532,46 @@ static int use_sae_anti_clogging(struct hostapd_data *hapd)
} }
static u8 sae_token_hash(struct hostapd_data *hapd, const u8 *addr)
{
u8 hash[SHA256_MAC_LEN];
hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key),
addr, ETH_ALEN, hash);
return hash[0];
}
static int check_sae_token(struct hostapd_data *hapd, const u8 *addr, static int check_sae_token(struct hostapd_data *hapd, const u8 *addr,
const u8 *token, size_t token_len) const u8 *token, size_t token_len)
{ {
u8 mac[SHA256_MAC_LEN]; u8 mac[SHA256_MAC_LEN];
const u8 *addrs[2];
size_t len[2];
u16 token_idx;
u8 idx;
if (token_len != SHA256_MAC_LEN) if (token_len != SHA256_MAC_LEN)
return -1; return -1;
if (hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key), idx = sae_token_hash(hapd, addr);
addr, ETH_ALEN, mac) < 0 || token_idx = hapd->sae_pending_token_idx[idx];
os_memcmp_const(token, mac, SHA256_MAC_LEN) != 0) if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) {
wpa_printf(MSG_DEBUG, "SAE: Invalid anti-clogging token from "
MACSTR " - token_idx 0x%04x, expected 0x%04x",
MAC2STR(addr), WPA_GET_BE16(token), token_idx);
return -1; return -1;
}
addrs[0] = addr;
len[0] = ETH_ALEN;
addrs[1] = token;
len[1] = 2;
if (hmac_sha256_vector(hapd->sae_token_key, sizeof(hapd->sae_token_key),
2, addrs, len, mac) < 0 ||
os_memcmp_const(token + 2, &mac[2], SHA256_MAC_LEN - 2) != 0)
return -1;
hapd->sae_pending_token_idx[idx] = 0; /* invalidate used token */
return 0; return 0;
} }
@ -554,16 +583,25 @@ static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd,
struct wpabuf *buf; struct wpabuf *buf;
u8 *token; u8 *token;
struct os_reltime now; struct os_reltime now;
u8 idx[2];
const u8 *addrs[2];
size_t len[2];
u8 p_idx;
u16 token_idx;
os_get_reltime(&now); os_get_reltime(&now);
if (!os_reltime_initialized(&hapd->last_sae_token_key_update) || if (!os_reltime_initialized(&hapd->last_sae_token_key_update) ||
os_reltime_expired(&now, &hapd->last_sae_token_key_update, 60)) { os_reltime_expired(&now, &hapd->last_sae_token_key_update, 60) ||
hapd->sae_token_idx == 0xffff) {
if (random_get_bytes(hapd->sae_token_key, if (random_get_bytes(hapd->sae_token_key,
sizeof(hapd->sae_token_key)) < 0) sizeof(hapd->sae_token_key)) < 0)
return NULL; return NULL;
wpa_hexdump(MSG_DEBUG, "SAE: Updated token key", wpa_hexdump(MSG_DEBUG, "SAE: Updated token key",
hapd->sae_token_key, sizeof(hapd->sae_token_key)); hapd->sae_token_key, sizeof(hapd->sae_token_key));
hapd->last_sae_token_key_update = now; hapd->last_sae_token_key_update = now;
hapd->sae_token_idx = 0;
os_memset(hapd->sae_pending_token_idx, 0,
sizeof(hapd->sae_pending_token_idx));
} }
buf = wpabuf_alloc(sizeof(le16) + SHA256_MAC_LEN); buf = wpabuf_alloc(sizeof(le16) + SHA256_MAC_LEN);
@ -572,9 +610,25 @@ static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd,
wpabuf_put_le16(buf, group); /* Finite Cyclic Group */ wpabuf_put_le16(buf, group); /* Finite Cyclic Group */
p_idx = sae_token_hash(hapd, addr);
token_idx = hapd->sae_pending_token_idx[p_idx];
if (!token_idx) {
hapd->sae_token_idx++;
token_idx = hapd->sae_token_idx;
hapd->sae_pending_token_idx[p_idx] = token_idx;
}
WPA_PUT_BE16(idx, token_idx);
token = wpabuf_put(buf, SHA256_MAC_LEN); token = wpabuf_put(buf, SHA256_MAC_LEN);
hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key), addrs[0] = addr;
addr, ETH_ALEN, token); len[0] = ETH_ALEN;
addrs[1] = idx;
len[1] = sizeof(idx);
if (hmac_sha256_vector(hapd->sae_token_key, sizeof(hapd->sae_token_key),
2, addrs, len, token) < 0) {
wpabuf_free(buf);
return NULL;
}
WPA_PUT_BE16(token, token_idx);
return buf; return buf;
} }