[RFC PATCH v1 10/13] hostapd: Add DecoyAuth SAE responder
Jeff Hansen
x at jeffhansen.com
Fri Sep 11 12:20:03 PDT 2026
Negotiate DecoyAuth in H2E SAE Authentication frames and derive
per-exchange candidate material. Process fragmented exchanges while
preserving standard SAE behavior for legacy peers in optional mode.
Signed-off-by: Jeff Hansen <x at jeffhansen.com>
---
src/ap/beacon.c | 28 ++-
src/ap/beacon.h | 1 -
src/ap/ieee802_11.c | 454 +++++++++++++++++++++++++++++++++++++++++---
src/ap/ieee802_11.h | 6 +
4 files changed, 452 insertions(+), 37 deletions(-)
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index a5565e96b..775ea7c98 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -736,13 +736,12 @@ struct probe_resp_params {
#endif /* CONFIG_IEEE80211AX */
};
-
static void hostapd_free_probe_resp_params(struct probe_resp_params *params)
{
-#ifdef CONFIG_IEEE80211BE
if (!params)
return;
+#ifdef CONFIG_IEEE80211BE
os_free(params->mld_info);
params->mld_info = NULL;
#endif /* CONFIG_IEEE80211BE */
@@ -1315,7 +1314,9 @@ static struct hostapd_sta_info * sta_track_get(struct hostapd_iface *iface,
}
-void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal)
+static struct hostapd_sta_info *
+sta_track_add_internal(struct hostapd_iface *iface, const u8 *addr,
+ int ssi_signal, unsigned int max_entries)
{
struct hostapd_sta_info *info;
@@ -1326,18 +1327,18 @@ void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal)
dl_list_add_tail(&iface->sta_seen, &info->list);
os_get_reltime(&info->last_seen);
info->ssi_signal = ssi_signal;
- return;
+ return info;
}
/* Add a new entry */
info = os_zalloc(sizeof(*info));
if (info == NULL)
- return;
+ return NULL;
os_memcpy(info->addr, addr, ETH_ALEN);
os_get_reltime(&info->last_seen);
info->ssi_signal = ssi_signal;
- if (iface->num_sta_seen >= iface->conf->track_sta_max_num) {
+ if (max_entries && iface->num_sta_seen >= max_entries) {
/* Expire oldest entry to make room for a new one */
sta_track_expire(iface, 1);
}
@@ -1346,6 +1347,14 @@ void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal)
MACSTR, iface->bss[0]->conf->iface, MAC2STR(addr));
dl_list_add_tail(&iface->sta_seen, &info->list);
iface->num_sta_seen++;
+ return info;
+}
+
+
+void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal)
+{
+ sta_track_add_internal(iface, addr, ssi_signal,
+ iface->conf->track_sta_max_num);
}
@@ -1780,7 +1789,6 @@ void handle_probe_req(struct hostapd_data *hapd,
params.known_bss_len = elems.mbssid_known_bss_len;
hostapd_gen_probe_resp(hapd, ¶ms);
-
hostapd_free_probe_resp_params(¶ms);
if (!params.resp)
@@ -2630,6 +2638,12 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
hapd->conf->rsn_override_key_mgmt |
hapd->conf->rsn_override_key_mgmt_2) &&
(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP)) {
+ if (hapd->conf->decoyauth == SAE_DECOYAUTH_REQUIRED) {
+ wpa_printf(MSG_ERROR,
+ "Required DecoyAuth not supported with SAE offload");
+ goto error;
+ }
+
if (hostapd_sae_pk_in_use(hapd->conf)) {
wpa_printf(MSG_ERROR,
"SAE PK not supported with SAE offload");
diff --git a/src/ap/beacon.h b/src/ap/beacon.h
index e381542a8..c46d28c20 100644
--- a/src/ap/beacon.h
+++ b/src/ap/beacon.h
@@ -30,7 +30,6 @@ sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr,
const char *ifname);
void sta_track_claim_taxonomy_info(struct hostapd_iface *iface, const u8 *addr,
struct wpabuf **probe_ie_taxonomy);
-
const u8 * hostapd_wpa_ie(struct hostapd_data *hapd, u8 eid);
u8 * hostapd_unsol_bcast_probe_resp(struct hostapd_data *hapd,
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index a017e7fe3..18d3b76fd 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -1232,13 +1232,216 @@ found:
return password;
}
+int sae_get_matching_passwords(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *rx_id, size_t rx_id_len,
+ const u8 ***passwords,
+ size_t **password_lens,
+ int *num_passwords)
+{
+ struct sae_password_entry *pw;
+ struct hostapd_sta_wpa_psk_short *psk;
+ const char *password;
+ int count = 0, i = 0;
+ int use_sta_psks = 0;
+
+ *passwords = NULL;
+ *password_lens = NULL;
+ *num_passwords = 0;
+
+ for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
+ if (!is_broadcast_ether_addr(pw->peer_addr) &&
+ (!sta || !ether_addr_equal(pw->peer_addr, sta->addr)))
+ continue;
+ if ((rx_id && !pw->identifier) || (!rx_id && pw->identifier))
+ continue;
+ if (rx_id && pw->identifier &&
+ (rx_id_len != os_strlen(pw->identifier) ||
+ os_memcmp(rx_id, pw->identifier, rx_id_len) != 0))
+ continue;
+ count++;
+ }
+ if (count == 0 && sta && !rx_id) {
+ for (psk = sta->psk; psk; psk = psk->next) {
+ if (psk->is_passphrase)
+ count++;
+ }
+ use_sta_psks = count > 0;
+ }
+ if (count > SAE_MAX_PASSWORDS)
+ return -1;
+
+ if (count == 0) {
+ password = sae_get_password(hapd, sta, rx_id, rx_id_len,
+ NULL, NULL, NULL);
+ if (!password)
+ return -1;
+
+ *passwords = os_calloc(1, sizeof(**passwords));
+ *password_lens = os_calloc(1, sizeof(**password_lens));
+ if (!*passwords || !*password_lens) {
+ os_free(*passwords);
+ os_free(*password_lens);
+ *passwords = NULL;
+ *password_lens = NULL;
+ return -1;
+ }
+ (*passwords)[0] = (const u8 *) password;
+ (*password_lens)[0] = os_strlen(password);
+ *num_passwords = 1;
+ return 0;
+ }
+
+ *passwords = os_calloc(count, sizeof(**passwords));
+ *password_lens = os_calloc(count, sizeof(**password_lens));
+ if (!*passwords || !*password_lens) {
+ os_free(*passwords);
+ os_free(*password_lens);
+ *passwords = NULL;
+ *password_lens = NULL;
+ return -1;
+ }
+
+ if (use_sta_psks) {
+ for (psk = sta->psk; psk; psk = psk->next) {
+ if (!psk->is_passphrase)
+ continue;
+ (*passwords)[i] = (const u8 *) psk->passphrase;
+ (*password_lens)[i] = os_strlen(psk->passphrase);
+ i++;
+ }
+ } else {
+ for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
+ if (!is_broadcast_ether_addr(pw->peer_addr) &&
+ (!sta || !ether_addr_equal(pw->peer_addr, sta->addr)))
+ continue;
+ if ((rx_id && !pw->identifier) ||
+ (!rx_id && pw->identifier))
+ continue;
+ if (rx_id && pw->identifier &&
+ (rx_id_len != os_strlen(pw->identifier) ||
+ os_memcmp(rx_id, pw->identifier, rx_id_len) != 0))
+ continue;
+ (*passwords)[i] = (const u8 *) pw->password;
+ (*password_lens)[i] = os_strlen(pw->password);
+ i++;
+ }
+ }
+ *num_passwords = count;
+ return 0;
+}
+
+
+static struct sae_password_entry *
+sae_get_matching_password_entry(struct hostapd_data *hapd,
+ struct sta_info *sta, int password_idx)
+{
+ struct sae_password_entry *pw;
+ const u8 *rx_id = NULL;
+ size_t rx_id_len = 0;
+
+ if (sta->sae && sta->sae->tmp) {
+ rx_id = sta->sae->tmp->parsed_pw_id ?
+ sta->sae->tmp->parsed_pw_id : sta->sae->tmp->pw_id;
+ rx_id_len = sta->sae->tmp->parsed_pw_id ?
+ sta->sae->tmp->parsed_pw_id_len :
+ sta->sae->tmp->pw_id_len;
+ }
+
+ for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
+ if (!is_broadcast_ether_addr(pw->peer_addr) &&
+ os_memcmp(pw->peer_addr, sta->addr, ETH_ALEN) != 0)
+ continue;
+ if ((rx_id && !pw->identifier) || (!rx_id && pw->identifier))
+ continue;
+ if (rx_id &&
+ (rx_id_len != os_strlen(pw->identifier) ||
+ os_memcmp(rx_id, pw->identifier, rx_id_len) != 0))
+ continue;
+ if (password_idx-- == 0)
+ return pw;
+ }
+
+ return NULL;
+}
+
+
+static size_t hostapd_sae_commit_len(struct sae_data *sae,
+ const struct wpabuf *token,
+ const u8 *identifier,
+ size_t identifier_len,
+ int include_values)
+{
+ size_t len, values_len, values_frags;
+
+ if (!sae || !sae->tmp)
+ return 0;
+
+ len = 2 + sae->tmp->prime_len;
+ len += sae->tmp->ec ? 2 * sae->tmp->prime_len : sae->tmp->prime_len;
+ if (!sae->h2e && token)
+ len += wpabuf_len(token);
+ if (identifier)
+ len += 3 + identifier_len;
+ if (sae->h2e && sae->tmp->own_rejected_groups)
+ len += 3 + wpabuf_len(sae->tmp->own_rejected_groups);
+ if (sae->h2e && token)
+ len += 3 + wpabuf_len(token);
+ if (wpa_key_mgmt_sae_ext_key(sae->akmp))
+ len += 3 + RSN_SELECTOR_LEN;
+ if (!include_values)
+ return len;
+
+ values_len = sae_write_values_len(sae);
+ values_frags =
+ (values_len + SAE_DECOYAUTH_VALUES_ELEM_DATA_LEN - 1) /
+ SAE_DECOYAUTH_VALUES_ELEM_DATA_LEN;
+ return len + values_len +
+ SAE_DECOYAUTH_VALUES_ELEM_OVERHEAD * values_frags;
+}
+
+
+static const struct sae_pt **
+sae_get_cached_password_pts(struct hostapd_data *hapd,
+ const u8 **passwords, int num_passwords)
+{
+ const struct sae_pt **pts;
+ struct sae_password_entry *pw;
+
+ pts = os_calloc(num_passwords, sizeof(*pts));
+ if (!pts)
+ return NULL;
+ for (int i = 0; i < num_passwords; i++) {
+ if (hapd->conf->ssid.wpa_passphrase &&
+ passwords[i] ==
+ (const u8 *) hapd->conf->ssid.wpa_passphrase) {
+ pts[i] = hapd->conf->ssid.pt;
+ continue;
+ }
+ for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
+ if (passwords[i] == (const u8 *) pw->password) {
+ pts[i] = pw->pt;
+ break;
+ }
+ }
+ }
+ return pts;
+}
+
static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
struct sta_info *sta, int update,
- int status_code)
+ int status_code, u16 *reply_status)
{
struct wpabuf *buf;
const char *password = NULL;
+ const u8 **passwords = NULL;
+ const struct sae_pt **password_pts = NULL;
+ size_t *password_lens = NULL;
+ size_t commit_len;
+ int num_passwords = 0;
+ int use_decoyauth = 0;
+ int use_mld_addr = 0;
struct sae_password_entry *pw;
const u8 *rx_id = NULL;
size_t rx_id_len = 0;
@@ -1248,8 +1451,10 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
const u8 *own_addr = hapd->own_addr;
#ifdef CONFIG_IEEE80211BE
- if (ap_sta_is_mld(hapd, sta))
+ if (ap_sta_is_mld(hapd, sta)) {
own_addr = hapd->mld->mld_addr;
+ use_mld_addr = 1;
+ }
#endif /* CONFIG_IEEE80211BE */
if (sta->sae->tmp) {
@@ -1259,6 +1464,7 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
sta->sae->tmp->parsed_pw_id_len :
sta->sae->tmp->pw_id_len;
use_pt = sta->sae->h2e;
+ use_decoyauth = sta->sae->tmp->decoyauth;
#ifdef CONFIG_SAE_PK
os_memcpy(sta->sae->tmp->own_addr, own_addr, ETH_ALEN);
os_memcpy(sta->sae->tmp->peer_addr, sta->addr, ETH_ALEN);
@@ -1279,7 +1485,7 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
return NULL;
}
- if (use_pt) {
+ if (use_pt && !use_decoyauth) {
struct sae_pt *tmp_pt = NULL;
bool failed = false;
@@ -1316,15 +1522,60 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
if (failed)
return NULL;
}
-
- if (update && !use_pt &&
- sae_prepare_commit(own_addr, sta->addr,
- (u8 *) password, os_strlen(password),
- sta->sae) < 0) {
+ if (update && use_decoyauth &&
+ (sae_get_matching_passwords(hapd, sta, rx_id, rx_id_len,
+ &passwords, &password_lens,
+ &num_passwords) < 0)) {
+ os_free(password_lens);
+ os_free(passwords);
wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
return NULL;
}
-
+ if (update && use_pt && use_decoyauth) {
+ password_pts = sae_get_cached_password_pts(
+ hapd, passwords, num_passwords);
+ if (!password_pts) {
+ os_free(password_lens);
+ os_free(passwords);
+ wpa_printf(MSG_DEBUG, "SAE: Could not load cached PTs");
+ return NULL;
+ }
+ }
+ if (update && use_decoyauth && sta->sae->tmp) {
+ sta->sae->tmp->decoyauth_cache_dir =
+ hapd->conf->decoyauth_cache_dir;
+ sta->sae->tmp->decoyauth_cache_entries =
+ hapd->conf->decoyauth_cache_entries;
+ }
+ if (update && use_decoyauth)
+ wpa_printf(MSG_DEBUG,
+ "SAE: DecoyAuth AP exchange h2e=%d mlo=%d group=%d link=" MACSTR
+ " own=" MACSTR " peer=" MACSTR " passwords=%d",
+ use_pt, use_mld_addr, sta->sae->group,
+ MAC2STR(hapd->own_addr), MAC2STR(own_addr),
+ MAC2STR(sta->addr), num_passwords);
+ if (update && use_decoyauth &&
+ sae_ap_prepare_commit_h2e_with_pt(
+ own_addr, sta->addr, hapd->conf->ssid.ssid,
+ hapd->conf->ssid.ssid_len, passwords, password_lens,
+ password_pts, num_passwords, sta->sae) < 0) {
+ os_free(password_pts);
+ os_free(password_lens);
+ os_free(passwords);
+ wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
+ return NULL;
+ }
+ if (update && !use_pt && !use_decoyauth &&
+ sae_prepare_commit(own_addr, sta->addr, (const u8 *) password,
+ os_strlen(password), sta->sae) < 0) {
+ os_free(password_lens);
+ os_free(passwords);
+ wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
+ return NULL;
+ }
+ os_free(password_pts);
+ os_free(password_lens);
+ os_free(passwords);
if (pw && sta->sae->tmp)
sta->sae->tmp->used_pw = pw;
@@ -1337,12 +1588,57 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
sta->sae->tmp->vlan_id = pw->vlan_id;
}
- buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN +
- (rx_id ? 3 + rx_id_len : 0));
+ if (!use_decoyauth) {
+ buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN +
+ (rx_id ? 3 + rx_id_len : 0));
+ if (buf &&
+ sae_write_commit(sta->sae, buf, sta->sae->tmp ?
+ sta->sae->tmp->anti_clogging_token : NULL,
+ rx_id, rx_id_len) < 0) {
+ wpabuf_free(buf);
+ buf = NULL;
+ }
+ return buf;
+ }
+
+ commit_len = hostapd_sae_commit_len(sta->sae,
+ sta->sae->tmp ?
+ sta->sae->tmp->anti_clogging_token :
+ NULL,
+ rx_id, rx_id_len, 1);
+ if (!commit_len)
+ return NULL;
+ if (commit_len > SAE_DECOYAUTH_MAX_AUTH_BODY_LEN) {
+ size_t count = sae_decoyauth_fragment_count(sta->sae);
+ u16 fragment = sta->sae->tmp->decoyauth_fragment_request;
+
+ if (!count || count > UINT16_MAX || fragment > count)
+ return NULL;
+ sta->sae->tmp->decoyauth_fragmented = 1;
+ if (fragment < count) {
+ buf = wpabuf_alloc(SAE_DECOYAUTH_MAX_AUTH_BODY_LEN);
+ if (!buf ||
+ sae_write_decoyauth_fragment(sta->sae, buf,
+ fragment) < 0) {
+ wpabuf_free(buf);
+ return NULL;
+ }
+ *reply_status = SAE_DECOYAUTH_FRAGMENT_STATUS;
+ return buf;
+ }
+ commit_len = hostapd_sae_commit_len(
+ sta->sae,
+ sta->sae->tmp->anti_clogging_token,
+ rx_id, rx_id_len, 0);
+ }
+ if (commit_len > SAE_DECOYAUTH_MAX_AUTH_BODY_LEN)
+ return NULL;
+ buf = wpabuf_alloc(commit_len);
if (buf &&
- sae_write_commit(sta->sae, buf, sta->sae->tmp ?
+ sae_ap_write_commit(sta->sae, buf, sta->sae->tmp ?
sta->sae->tmp->anti_clogging_token : NULL,
- rx_id, rx_id_len) < 0) {
+ rx_id, rx_id_len,
+ !sta->sae->tmp->decoyauth_fragmented) < 0) {
wpabuf_free(buf);
buf = NULL;
}
@@ -1376,6 +1672,38 @@ static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd,
}
+static int auth_sae_process_commit_data(struct sae_data *sae)
+{
+ if (sae->tmp && sae->tmp->decoyauth)
+ return sae_ap_process_commit(sae);
+ return sae_process_commit(sae);
+}
+
+
+static int auth_sae_check_confirm(struct hostapd_data *hapd,
+ struct sta_info *sta, const u8 *data,
+ size_t len)
+{
+ struct sae_data *sae = sta->sae;
+ struct sae_password_entry *pw;
+ int password_idx;
+ int ret;
+
+ if (!sae->tmp || !sae->tmp->decoyauth)
+ return sae_check_confirm(sae, data, len, NULL);
+
+ ret = sae_ap_check_confirm(sae, data, len, NULL, &password_idx);
+ if (ret < 0)
+ return ret;
+ pw = sae_get_matching_password_entry(hapd, sta, password_idx);
+ if (pw) {
+ sae->tmp->used_pw = pw;
+ sae->tmp->vlan_id = pw->vlan_id;
+ }
+ return 0;
+}
+
+
static int auth_sae_send_commit(struct hostapd_data *hapd,
struct sta_info *sta,
int update, int status_code)
@@ -1384,19 +1712,18 @@ static int auth_sae_send_commit(struct hostapd_data *hapd,
int reply_res;
u16 status;
- data = auth_build_sae_commit(hapd, sta, update, status_code);
- if (!data && sta->sae->tmp &&
- (sta->sae->tmp->pw_id || sta->sae->tmp->parsed_pw_id))
- return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
- if (data == NULL)
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
-
if (sta->sae->tmp && sta->sae->pk)
status = WLAN_STATUS_SAE_PK;
else if (sta->sae->tmp && sta->sae->h2e)
status = WLAN_STATUS_SAE_HASH_TO_ELEMENT;
else
status = WLAN_STATUS_SUCCESS;
+ data = auth_build_sae_commit(hapd, sta, update, status_code, &status);
+ if (!data && sta->sae->tmp &&
+ (sta->sae->tmp->pw_id || sta->sae->tmp->parsed_pw_id))
+ return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
+ if (data == NULL)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
#ifdef CONFIG_TESTING_OPTIONS
if (hapd->conf->sae_commit_status >= 0 &&
hapd->conf->sae_commit_status != status) {
@@ -1735,8 +2062,13 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
- if (sae_process_commit(sta->sae) < 0)
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ if (!sta->sae->tmp->decoyauth ||
+ !sta->sae->tmp->decoyauth_commit_processed) {
+ if (auth_sae_process_commit_data(sta->sae) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ if (sta->sae->tmp->decoyauth)
+ sta->sae->tmp->decoyauth_commit_processed = 1;
+ }
/*
* In mesh case, both Commit and Confirm are sent
@@ -1748,8 +2080,9 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
* overridden with explicit configuration so that the
* infrastructure BSS case sends both frames together.
*/
- immediate_confirm = (hapd->conf->mesh & MESH_ENABLED) ||
- hapd->conf->sae_confirm_immediate;
+ immediate_confirm = !sta->sae->tmp->decoyauth_fragmented &&
+ ((hapd->conf->mesh & MESH_ENABLED) ||
+ hapd->conf->sae_confirm_immediate);
/* If sae_track_password is enabled and the STA has not
* yet been tracked to having successfully completed
@@ -1796,7 +2129,15 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
case SAE_COMMITTED:
sae_clear_retransmit_timer(hapd, sta);
if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT) {
- if (sae_process_commit(sta->sae) < 0)
+ if (sta->sae->tmp->decoyauth_fragmented) {
+ ret = auth_sae_send_commit(hapd, sta, 0,
+ status_code);
+ if (ret)
+ return ret;
+ sae_set_retransmit_timer(hapd, sta);
+ return WLAN_STATUS_SUCCESS;
+ }
+ if (auth_sae_process_commit_data(sta->sae) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
ret = auth_sae_send_confirm(hapd, sta);
@@ -1851,7 +2192,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
if (ret)
return ret;
- if (sae_process_commit(sta->sae) < 0)
+ if (auth_sae_process_commit_data(sta->sae) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
ret = auth_sae_send_confirm(hapd, sta);
@@ -1880,7 +2221,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
return ret;
sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
- if (sae_process_commit(sta->sae) < 0)
+ if (auth_sae_process_commit_data(sta->sae) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
sta->sae->sync = 0;
sae_set_retransmit_timer(hapd, sta);
@@ -2046,6 +2387,28 @@ static int check_sae_rejected_groups(struct hostapd_data *hapd,
}
+static int sae_get_peer_decoyauth_mode(const u8 *ies, size_t ies_len,
+ u16 *fragment_request)
+{
+ const u8 *ie;
+ int mode;
+
+ ie = get_vendor_ie(ies, ies_len, SAE_DECOYAUTH_VENDOR_TYPE);
+ if (fragment_request)
+ *fragment_request = 0;
+ if (!ie || (ie[1] != 6 && ie[1] != 8) ||
+ ie[6] != SAE_DECOYAUTH_VENDOR_VERSION)
+ return SAE_DECOYAUTH_DISABLED;
+ mode = ie[7];
+ if (mode != SAE_DECOYAUTH_OPTIONAL &&
+ mode != SAE_DECOYAUTH_REQUIRED)
+ return SAE_DECOYAUTH_DISABLED;
+ if (fragment_request && ie[1] == 8)
+ *fragment_request = WPA_GET_LE16(ie + 8);
+ return mode;
+}
+
+
static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
const struct ieee80211_mgmt *mgmt, size_t len,
u16 auth_transaction, u16 status_code)
@@ -2267,6 +2630,40 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
if (resp != WLAN_STATUS_SUCCESS)
goto reply;
+ if (sta->sae->tmp) {
+ const u8 *ies = mgmt->u.auth.variable +
+ ie_offset;
+ size_t ies_len = ((const u8 *) mgmt) + len - ies;
+ u16 fragment_request;
+ int peer_mode = sae_get_peer_decoyauth_mode(
+ ies, ies_len, &fragment_request);
+ int compatible = peer_mode != SAE_DECOYAUTH_DISABLED &&
+ status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT &&
+ !(conf->mesh & MESH_ENABLED) &&
+ sta->sae->tmp->ec &&
+ !sta->sae->tmp->pw_id &&
+ !sta->sae->tmp->parsed_pw_id;
+
+ /* A zero fragment request starts a new SAE exchange. */
+ if (!fragment_request) {
+ sta->sae->tmp->decoyauth_fragmented = 0;
+ sta->sae->tmp->decoyauth_commit_processed = 0;
+ }
+
+ sta->sae->tmp->decoyauth =
+ conf->decoyauth != SAE_DECOYAUTH_DISABLED &&
+ compatible;
+ sta->sae->tmp->decoyauth_fragment_request =
+ fragment_request;
+ if (conf->decoyauth == SAE_DECOYAUTH_REQUIRED &&
+ !sta->sae->tmp->decoyauth) {
+ wpa_printf(MSG_INFO,
+ "SAE: Peer does not support required DecoyAuth exchange");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto reply;
+ }
+ }
+
if (check_sae_rejected_groups(hapd, sta->sae)) {
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto reply;
@@ -2355,8 +2752,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
return;
}
- if (sae_check_confirm(sta->sae, var, var_len,
- NULL) < 0) {
+ if (auth_sae_check_confirm(hapd, sta, var, var_len) < 0) {
if (sae_password_track_fail(hapd, sta)) {
wpa_printf(MSG_DEBUG,
"SAE: Reject mismatching Confirm so that another password can be attempted by "
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index da87f0b29..bd8ac15ff 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -304,6 +304,12 @@ const char * sae_get_password(struct hostapd_data *hapd,
size_t rx_id_len,
struct sae_password_entry **pw_entry,
struct sae_pt **s_pt, const struct sae_pk **s_pk);
+int sae_get_matching_passwords(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *rx_id, size_t rx_id_len,
+ const u8 ***passwords,
+ size_t **password_lens,
+ int *num_passwords);
struct sta_info * hostapd_ml_get_assoc_sta(struct hostapd_data *hapd,
struct sta_info *sta,
struct hostapd_data **assoc_hapd);
--
2.53.0
More information about the Hostap
mailing list