aboutsummaryrefslogtreecommitdiff
path: root/wpa_supplicant/pasn_supplicant.c
diff options
context:
space:
mode:
Diffstat (limited to 'wpa_supplicant/pasn_supplicant.c')
-rw-r--r--wpa_supplicant/pasn_supplicant.c196
1 files changed, 161 insertions, 35 deletions
diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c
index e6adf73f3e42..baf4c2643e42 100644
--- a/wpa_supplicant/pasn_supplicant.c
+++ b/wpa_supplicant/pasn_supplicant.c
@@ -33,9 +33,18 @@ struct wpa_pasn_auth_work {
int cipher;
u16 group;
int network_id;
+ struct wpabuf *comeback;
};
+static void wpas_pasn_free_auth_work(struct wpa_pasn_auth_work *awork)
+{
+ wpabuf_free(awork->comeback);
+ awork->comeback = NULL;
+ os_free(awork);
+}
+
+
static void wpas_pasn_auth_work_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
@@ -56,8 +65,30 @@ static void wpas_pasn_cancel_auth_work(struct wpa_supplicant *wpa_s)
static void wpas_pasn_auth_status(struct wpa_supplicant *wpa_s, const u8 *bssid,
- int akmp, int cipher, u8 status)
+ int akmp, int cipher, u8 status,
+ struct wpabuf *comeback,
+ u16 comeback_after)
{
+ if (comeback) {
+ size_t comeback_len = wpabuf_len(comeback);
+ size_t buflen = comeback_len * 2 + 1;
+ char *comeback_txt = os_malloc(buflen);
+
+ if (comeback_txt) {
+ wpa_snprintf_hex(comeback_txt, buflen,
+ wpabuf_head(comeback), comeback_len);
+
+ wpa_msg(wpa_s, MSG_INFO, PASN_AUTH_STATUS MACSTR
+ " akmp=%s, status=%u comeback_after=%u comeback=%s",
+ MAC2STR(bssid),
+ wpa_key_mgmt_txt(akmp, WPA_PROTO_RSN),
+ status, comeback_after, comeback_txt);
+
+ os_free(comeback_txt);
+ return;
+ }
+ }
+
wpa_msg(wpa_s, MSG_INFO,
PASN_AUTH_STATUS MACSTR " akmp=%s, status=%u",
MAC2STR(bssid), wpa_key_mgmt_txt(akmp, WPA_PROTO_RSN),
@@ -71,30 +102,17 @@ static struct wpabuf * wpas_pasn_wd_sae_commit(struct wpa_supplicant *wpa_s)
{
struct wpas_pasn *pasn = &wpa_s->pasn;
struct wpabuf *buf = NULL;
- const char *password = NULL;
int ret;
- if (pasn->ssid) {
- password = pasn->ssid->sae_password;
- if (!password)
- password = pasn->ssid->passphrase;
- }
-
- if (!password) {
- wpa_printf(MSG_DEBUG, "PASN: SAE without a password");
- return NULL;
- }
-
ret = sae_set_group(&pasn->sae, pasn->group);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: Failed to set SAE group");
return NULL;
}
- /* TODO: SAE H2E */
- ret = sae_prepare_commit(wpa_s->own_addr, pasn->bssid,
- (const u8 *) password, os_strlen(password), 0,
- &pasn->sae);
+ ret = sae_prepare_commit_pt(&pasn->sae, pasn->ssid->pt,
+ wpa_s->own_addr, pasn->bssid,
+ NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit");
return NULL;
@@ -109,7 +127,7 @@ static struct wpabuf * wpas_pasn_wd_sae_commit(struct wpa_supplicant *wpa_s)
wpabuf_put_le16(buf, WLAN_AUTH_SAE);
wpabuf_put_le16(buf, 1);
- wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+ wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT);
sae_write_commit(&pasn->sae, buf, NULL, 0);
pasn->sae.state = SAE_COMMITTED;
@@ -155,14 +173,14 @@ static int wpas_pasn_wd_sae_rx(struct wpa_supplicant *wpa_s, struct wpabuf *wd)
wpa_printf(MSG_DEBUG, "PASN: SAE: commit: alg=%u, seq=%u, status=%u",
alg, seq, status);
- /* TODO: SAE H2E */
- if (alg != WLAN_AUTH_SAE || seq != 1 || status != WLAN_STATUS_SUCCESS) {
+ if (alg != WLAN_AUTH_SAE || seq != 1 ||
+ status != WLAN_STATUS_SAE_HASH_TO_ELEMENT) {
wpa_printf(MSG_DEBUG, "PASN: SAE: dropping peer commit");
return -1;
}
res = sae_parse_commit(&pasn->sae, data + 6, len - 6, NULL, 0, groups,
- 0);
+ 1);
if (res != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_DEBUG, "PASN: SAE failed parsing commit");
return -1;
@@ -240,6 +258,31 @@ static struct wpabuf * wpas_pasn_wd_sae_confirm(struct wpa_supplicant *wpa_s)
return buf;
}
+
+static int wpas_pasn_sae_setup_pt(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, int group)
+{
+ const char *password = ssid->sae_password;
+ int groups[2] = { group, 0 };
+
+ if (!password)
+ password = ssid->passphrase;
+
+ if (!password) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE without a password");
+ return -1;
+ }
+
+ if (ssid->pt)
+ return 0; /* PT already derived */
+
+ ssid->pt = sae_derive_pt(groups, ssid->ssid, ssid->ssid_len,
+ (const u8 *) password, os_strlen(password),
+ ssid->sae_password_id);
+
+ return ssid->pt ? 0 : -1;
+}
+
#endif /* CONFIG_SAE */
@@ -612,7 +655,8 @@ static u8 wpas_pasn_get_wrapped_data_format(struct wpas_pasn *pasn)
}
-static struct wpabuf * wpas_pasn_build_auth_1(struct wpa_supplicant *wpa_s)
+static struct wpabuf * wpas_pasn_build_auth_1(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *comeback)
{
struct wpas_pasn *pasn = &wpa_s->pasn;
struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL;
@@ -680,14 +724,14 @@ static struct wpabuf * wpas_pasn_build_auth_1(struct wpa_supplicant *wpa_s)
wrapped_data = WPA_PASN_WRAPPED_DATA_NO;
wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data,
- pubkey, true, NULL, -1);
+ pubkey, true, comeback, -1);
if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0)
goto fail;
/* Add own RNSXE */
- /* TODO: How to handle protected TWT and SAE H2E? */
capab = 0;
+ capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF)
capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF);
if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT)
@@ -828,6 +872,10 @@ static void wpas_pasn_reset(struct wpa_supplicant *wpa_s)
wpabuf_free(pasn->beacon_rsne_rsnxe);
pasn->beacon_rsne_rsnxe = NULL;
+ wpabuf_free(pasn->comeback);
+ pasn->comeback = NULL;
+ pasn->comeback_after = 0;
+
#ifdef CONFIG_SAE
sae_clear_data(&pasn->sae);
#endif /* CONFIG_SAE */
@@ -946,7 +994,7 @@ static int wpas_pasn_start(struct wpa_supplicant *wpa_s, const u8 *bssid,
int akmp, int cipher, u16 group, int freq,
const u8 *beacon_rsne, u8 beacon_rsne_len,
const u8 *beacon_rsnxe, u8 beacon_rsnxe_len,
- int network_id)
+ int network_id, struct wpabuf *comeback)
{
struct wpas_pasn *pasn = &wpa_s->pasn;
struct wpa_ssid *ssid = NULL;
@@ -972,6 +1020,20 @@ static int wpas_pasn_start(struct wpa_supplicant *wpa_s, const u8 *bssid,
"PASN: No network profile found for SAE");
return -1;
}
+
+ if (!ieee802_11_rsnx_capab(beacon_rsnxe,
+ WLAN_RSNX_CAPAB_SAE_H2E)) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: AP does not support SAE H2E");
+ return -1;
+ }
+
+ if (wpas_pasn_sae_setup_pt(wpa_s, ssid, group) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed to derive PT");
+ return -1;
+ }
+
pasn->sae.state = SAE_NOTHING;
pasn->sae.send_confirm = 0;
pasn->ssid = ssid;
@@ -1016,6 +1078,15 @@ static int wpas_pasn_start(struct wpa_supplicant *wpa_s, const u8 *bssid,
pasn->cipher = cipher;
pasn->group = group;
pasn->freq = freq;
+
+ if (wpa_s->conf->force_kdk_derivation ||
+ (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF &&
+ ieee802_11_rsnx_capab(beacon_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)))
+ pasn->kdk_len = WPA_KDK_MAX_LEN;
+ else
+ pasn->kdk_len = 0;
+ wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len);
+
os_memcpy(pasn->bssid, bssid, ETH_ALEN);
wpa_printf(MSG_DEBUG,
@@ -1023,7 +1094,7 @@ static int wpas_pasn_start(struct wpa_supplicant *wpa_s, const u8 *bssid,
MAC2STR(pasn->bssid), pasn->akmp, pasn->cipher,
pasn->group);
- frame = wpas_pasn_build_auth_1(wpa_s);
+ frame = wpas_pasn_build_auth_1(wpa_s, comeback);
if (!frame) {
wpa_printf(MSG_DEBUG, "PASN: Failed building 1st auth frame");
goto fail;
@@ -1105,7 +1176,8 @@ static void wpas_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit)
wpa_s, NULL);
wpa_s->pasn_auth_work = NULL;
}
- os_free(awork);
+
+ wpas_pasn_free_auth_work(awork);
return;
}
@@ -1132,24 +1204,29 @@ static void wpas_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit)
ret = wpas_pasn_start(wpa_s, awork->bssid, awork->akmp, awork->cipher,
awork->group, bss->freq, rsne, *(rsne + 1) + 2,
rsnxe, rsnxe ? *(rsnxe + 1) + 2 : 0,
- awork->network_id);
+ awork->network_id, awork->comeback);
if (ret) {
wpa_printf(MSG_DEBUG,
"PASN: Failed to start PASN authentication");
goto fail;
}
+ /* comeback token is no longer needed at this stage */
+ wpabuf_free(awork->comeback);
+ awork->comeback = NULL;
+
wpa_s->pasn_auth_work = work;
return;
fail:
- os_free(awork);
+ wpas_pasn_free_auth_work(awork);
work->ctx = NULL;
radio_work_done(work);
}
int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s, const u8 *bssid,
- int akmp, int cipher, u16 group, int network_id)
+ int akmp, int cipher, u16 group, int network_id,
+ const u8 *comeback, size_t comeback_len)
{
struct wpa_pasn_auth_work *awork;
struct wpa_bss *bss;
@@ -1195,9 +1272,17 @@ int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s, const u8 *bssid,
awork->group = group;
awork->network_id = network_id;
+ if (comeback && comeback_len) {
+ awork->comeback = wpabuf_alloc_copy(comeback, comeback_len);
+ if (!awork->comeback) {
+ wpas_pasn_free_auth_work(awork);
+ return -1;
+ }
+ }
+
if (radio_add_work(wpa_s, bss->freq, "pasn-start-auth", 1,
wpas_pasn_auth_start_cb, awork) < 0) {
- os_free(awork);
+ wpas_pasn_free_auth_work(awork);
return -1;
}
@@ -1216,12 +1301,33 @@ void wpas_pasn_auth_stop(struct wpa_supplicant *wpa_s)
wpa_printf(MSG_DEBUG, "PASN: Stopping authentication");
wpas_pasn_auth_status(wpa_s, pasn->bssid, pasn->akmp, pasn->cipher,
- pasn->status);
+ pasn->status, pasn->comeback,
+ pasn->comeback_after);
wpas_pasn_reset(wpa_s);
}
+static int wpas_pasn_immediate_retry(struct wpa_supplicant *wpa_s,
+ struct wpas_pasn *pasn,
+ struct wpa_pasn_params_data *params)
+{
+ int akmp = pasn->akmp;
+ int cipher = pasn->cipher;
+ u16 group = pasn->group;
+ u8 bssid[ETH_ALEN];
+ int network_id = pasn->ssid ? pasn->ssid->id : 0;
+
+ wpa_printf(MSG_DEBUG, "PASN: Immediate retry");
+ os_memcpy(bssid, pasn->bssid, ETH_ALEN);
+ wpas_pasn_reset(wpa_s);
+
+ return wpas_pasn_auth_start(wpa_s, bssid, akmp, cipher, group,
+ network_id,
+ params->comeback, params->comeback_len);
+}
+
+
int wpas_pasn_auth_rx(struct wpa_supplicant *wpa_s,
const struct ieee80211_mgmt *mgmt, size_t len)
{
@@ -1315,10 +1421,26 @@ int wpas_pasn_auth_rx(struct wpa_supplicant *wpa_s,
goto fail;
}
- /* TODO: handle comeback flow */
if (status == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
wpa_printf(MSG_DEBUG,
"PASN: Authentication temporarily rejected");
+
+ if (pasn_params.comeback && pasn_params.comeback_len) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Comeback token available. After=%u",
+ pasn_params.after);
+
+ if (!pasn_params.after)
+ return wpas_pasn_immediate_retry(wpa_s, pasn,
+ &pasn_params);
+
+ pasn->comeback = wpabuf_alloc_copy(
+ pasn_params.comeback, pasn_params.comeback_len);
+ if (pasn->comeback)
+ pasn->comeback_after = pasn_params.after;
+ }
+
+ pasn->status = status;
goto fail;
}
@@ -1393,7 +1515,7 @@ int wpas_pasn_auth_rx(struct wpa_supplicant *wpa_s,
wpa_s->own_addr, pasn->bssid,
wpabuf_head(secret), wpabuf_len(secret),
&pasn->ptk, pasn->akmp, pasn->cipher,
- WPA_KDK_MAX_LEN);
+ pasn->kdk_len);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK");
goto fail;
@@ -1456,7 +1578,11 @@ fail:
* the frame and terminate the authentication exchange. However, better
* reply to the AP with an error status.
*/
- pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ if (status == WLAN_STATUS_SUCCESS)
+ pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ else
+ pasn->status = status;
+
wpas_pasn_auth_stop(wpa_s);
return -1;
}