From 5e32fb0170f4a0dc82fbeaac2c283be8246f84ee Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 20 Jan 2020 20:27:06 +0200 Subject: [PATCH] SAE: Use Anti-Clogging Token Container element with H2E IEEE P802.11-REVmd was modified to use a container IE for anti-clogging token whenver H2E is used so that parsing of the SAE Authentication frames can be simplified. See this document for more details of the approved changes: https://mentor.ieee.org/802.11/dcn/19/11-19-2154-02-000m-sae-anti-clogging-token.docx Signed-off-by: Jouni Malinen --- src/ap/ieee802_11.c | 19 +++++-- src/common/ieee802_11_defs.h | 1 + src/common/sae.c | 99 +++++++++++++++++++----------------- wpa_supplicant/sme.c | 33 ++++++++++-- 4 files changed, 98 insertions(+), 54 deletions(-) diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 5ff65b901..ffa303d69 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -658,7 +658,7 @@ static int check_sae_token(struct hostapd_data *hapd, const u8 *addr, static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd, - int group, const u8 *addr) + int group, const u8 *addr, int h2e) { struct wpabuf *buf; u8 *token; @@ -684,12 +684,19 @@ static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd, sizeof(hapd->sae_pending_token_idx)); } - buf = wpabuf_alloc(sizeof(le16) + SHA256_MAC_LEN); + buf = wpabuf_alloc(sizeof(le16) + 3 + SHA256_MAC_LEN); if (buf == NULL) return NULL; wpabuf_put_le16(buf, group); /* Finite Cyclic Group */ + if (h2e) { + /* Encapsulate Anti-clogging Token field in a container IE */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 1 + SHA256_MAC_LEN); + wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN); + } + p_idx = sae_token_hash(hapd, addr); token_idx = hapd->sae_pending_token_idx[p_idx]; if (!token_idx) { @@ -1332,11 +1339,17 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, } if (!token && use_sae_anti_clogging(hapd) && !allow_reuse) { + int h2e = 0; + wpa_printf(MSG_DEBUG, "SAE: Request anti-clogging token from " MACSTR, MAC2STR(sta->addr)); + if (sta->sae->tmp) + h2e = sta->sae->tmp->h2e; + if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT) + h2e = 1; data = auth_build_token_req(hapd, sta->sae->group, - sta->addr); + sta->addr, h2e); resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ; if (hapd->conf->mesh & MESH_ENABLED) sae_set_state(sta, SAE_NOTHING, diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index d999a3665..ad4ef1a33 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -476,6 +476,7 @@ #define WLAN_EID_EXT_EDMG_CAPABILITIES 61 #define WLAN_EID_EXT_EDMG_OPERATION 62 #define WLAN_EID_EXT_REJECTED_GROUPS 92 +#define WLAN_EID_EXT_ANTI_CLOGGING_TOKEN 93 /* Extended Capabilities field */ #define WLAN_EXT_CAPAB_20_40_COEX 0 diff --git a/src/common/sae.c b/src/common/sae.c index 1682f81af..94ec1a39c 100644 --- a/src/common/sae.c +++ b/src/common/sae.c @@ -1631,7 +1631,7 @@ void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, return; wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */ - if (token) { + if (!sae->tmp->h2e && token) { wpabuf_put_buf(buf, token); wpa_hexdump(MSG_DEBUG, "SAE: Anti-clogging token", wpabuf_head(token), wpabuf_len(token)); @@ -1677,6 +1677,16 @@ void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, wpabuf_put_u8(buf, WLAN_EID_EXT_REJECTED_GROUPS); wpabuf_put_buf(buf, sae->tmp->own_rejected_groups); } + + if (sae->tmp->h2e && token) { + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 1 + wpabuf_len(token)); + wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN); + wpabuf_put_buf(buf, token); + wpa_hexdump_buf(MSG_DEBUG, + "SAE: Anti-clogging token (in container)", + token); + } } @@ -1742,32 +1752,34 @@ static int sae_is_rejected_groups_elem(const u8 *pos, const u8 *end) } +static int sae_is_token_container_elem(const u8 *pos, const u8 *end) +{ + return end - pos >= 3 && + pos[0] == WLAN_EID_EXTENSION && + pos[1] >= 1 && + end - pos - 2 >= pos[1] && + pos[2] == WLAN_EID_EXT_ANTI_CLOGGING_TOKEN; +} + + static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos, const u8 *end, const u8 **token, size_t *token_len, int h2e) { size_t scalar_elem_len, tlen; - const u8 *elem; if (token) *token = NULL; if (token_len) *token_len = 0; + if (h2e) + return; /* No Anti-Clogging Token field outside container IE */ + scalar_elem_len = (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len; if (scalar_elem_len >= (size_t) (end - *pos)) return; /* No extra data beyond peer scalar and element */ - /* It is a bit difficult to parse this now that there is an - * optional variable length Anti-Clogging Token field and - * optional variable length Password Identifier element in the - * frame. We are sending out fixed length Anti-Clogging Token - * fields, so use that length as a requirement for the received - * token and check for the presence of possible Password - * Identifier element based on the element header information. - * When parsing H2E case, also consider the Rejected Groupd element - * similarly. - */ tlen = end - (*pos + scalar_elem_len); if (tlen < SHA256_MAC_LEN) { @@ -1777,36 +1789,6 @@ static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos, return; } - elem = *pos + scalar_elem_len; - if (sae_is_password_id_elem(elem, end)) { - /* Password Identifier element takes out all available - * extra octets, so there can be no Anti-Clogging token in - * this frame. */ - return; - } - if (h2e && sae_is_rejected_groups_elem(elem, end)) { - /* Rejected Groups takes out all available extra octets, so - * there can be no Anti-Clogging token in this frame. */ - return; - } - - elem += SHA256_MAC_LEN; - if (sae_is_password_id_elem(elem, end)) { - /* Password Identifier element is included in the end, so - * remove its length from the Anti-Clogging token field. */ - tlen -= 2 + elem[1]; - elem += 2 + elem[1]; - if (h2e && sae_is_rejected_groups_elem(elem, end)) { - /* Also remove Rejected Groups element from the - * Anti-Clogging token field length */ - tlen -= 2 + elem[1]; - } - } else if (h2e && sae_is_rejected_groups_elem(elem, end)) { - /* Rejected Groups element is included in the end, so - * remove its length from the Anti-Clogging token field. */ - tlen -= 2 + elem[1]; - } - wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen); if (token) *token = *pos; @@ -1816,6 +1798,21 @@ static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos, } +static void sae_parse_token_container(struct sae_data *sae, + const u8 *pos, const u8 *end, + const u8 **token, size_t *token_len) +{ + wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", + pos, end - pos); + if (!sae_is_token_container_elem(pos, end)) + return; + *token = pos + 3; + *token_len = pos[1] - 1; + wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token (in container)", + *token, *token_len); +} + + static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, const u8 *end) { @@ -2010,19 +2007,21 @@ static int sae_parse_password_identifier(struct sae_data *sae, static int sae_parse_rejected_groups(struct sae_data *sae, - const u8 *pos, const u8 *end) + const u8 **pos, const u8 *end) { wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", - pos, end - pos); - if (!sae_is_rejected_groups_elem(pos, end)) + *pos, end - *pos); + if (!sae_is_rejected_groups_elem(*pos, end)) return WLAN_STATUS_SUCCESS; wpabuf_free(sae->tmp->peer_rejected_groups); - sae->tmp->peer_rejected_groups = wpabuf_alloc(pos[1] - 1); + sae->tmp->peer_rejected_groups = wpabuf_alloc((*pos)[1] - 1); if (!sae->tmp->peer_rejected_groups) return WLAN_STATUS_UNSPECIFIED_FAILURE; - wpabuf_put_data(sae->tmp->peer_rejected_groups, pos + 3, pos[1] - 1); + wpabuf_put_data(sae->tmp->peer_rejected_groups, (*pos) + 3, + (*pos)[1] - 1); wpa_hexdump_buf(MSG_DEBUG, "SAE: Received Rejected Groups list", sae->tmp->peer_rejected_groups); + *pos = *pos + 2 + (*pos)[1]; return WLAN_STATUS_SUCCESS; } @@ -2062,11 +2061,15 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, /* Conditional Rejected Groups element */ if (h2e) { - res = sae_parse_rejected_groups(sae, pos, end); + res = sae_parse_rejected_groups(sae, &pos, end); if (res != WLAN_STATUS_SUCCESS) return res; } + /* Optional Anti-Clogging Token Container element */ + if (h2e) + sae_parse_token_container(sae, pos, end, token, token_len); + /* * Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as * the values we sent which would be evidence of a reflection attack. diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 51f8d6105..ea4bad2b4 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -170,7 +170,7 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s, os_memcpy(wpa_s->sme.sae.tmp->bssid, bssid, ETH_ALEN); reuse_data: - len = wpa_s->sme.sae_token ? wpabuf_len(wpa_s->sme.sae_token) : 0; + len = wpa_s->sme.sae_token ? 3 + wpabuf_len(wpa_s->sme.sae_token) : 0; if (ssid->sae_password_id) len += 4 + os_strlen(ssid->sae_password_id); buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + len); @@ -1181,11 +1181,16 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, (external || wpa_s->current_bss) && wpa_s->current_ssid) { int default_groups[] = { 19, 20, 21, 0 }; u16 group; + const u8 *token_pos; + size_t token_len; + int h2e = 0; groups = wpa_s->conf->sae_groups; if (!groups || groups[0] <= 0) groups = default_groups; + wpa_hexdump(MSG_DEBUG, "SME: SAE anti-clogging token request", + data, len); if (len < sizeof(le16)) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Too short SAE anti-clogging token request"); @@ -1203,8 +1208,30 @@ 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 = wpabuf_alloc_copy(data + sizeof(le16), - len - sizeof(le16)); + token_pos = data + sizeof(le16); + token_len = len - sizeof(le16); + if (wpa_s->sme.sae.tmp) + h2e = wpa_s->sme.sae.tmp->h2e; + if (h2e) { + if (token_len < 3) { + wpa_dbg(wpa_s, MSG_DEBUG, + "SME: Too short SAE anti-clogging token container"); + return -1; + } + if (token_pos[0] != WLAN_EID_EXTENSION || + token_pos[1] == 0 || + token_pos[1] > token_len - 2 || + token_pos[2] != WLAN_EID_EXT_ANTI_CLOGGING_TOKEN) { + wpa_dbg(wpa_s, MSG_DEBUG, + "SME: Invalid SAE anti-clogging token container header"); + return -1; + } + token_len = token_pos[1] - 1; + token_pos += 3; + } + wpa_s->sme.sae_token = wpabuf_alloc_copy(token_pos, token_len); + wpa_hexdump_buf(MSG_DEBUG, "SME: Requested anti-clogging token", + wpa_s->sme.sae_token); if (!external) sme_send_authentication(wpa_s, wpa_s->current_bss, wpa_s->current_ssid, 2);