From 67014b3f7437feda2ba283c40af955c0ec510237 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Sun, 21 Mar 2021 13:55:09 +0200 Subject: [PATCH] PASN: Add support for comeback flow to wpa_supplicant Process the received comeback cookie and retry automatically if the AP allows this. Otherwise, provide the cookie to upper layers to allow a later attempt with the cookie. Signed-off-by: Ilan Peer --- wpa_supplicant/ctrl_iface.c | 27 ++++++-- wpa_supplicant/pasn_supplicant.c | 107 +++++++++++++++++++++++++++--- wpa_supplicant/wpa_supplicant_i.h | 6 +- 3 files changed, 124 insertions(+), 16 deletions(-) diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index b54d7d496..003de7bb1 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -10598,15 +10598,18 @@ static int wpas_ctrl_iface_pasn_start(struct wpa_supplicant *wpa_s, char *cmd) u8 bssid[ETH_ALEN]; int akmp = -1, cipher = -1, got_bssid = 0; u16 group = 0xFFFF; - int id = 0; + u8 *comeback = NULL; + size_t comeback_len = 0; + int id = 0, ret = -1; /* * Entry format: bssid= akmp= cipher= group= + * [comeback=] */ while ((token = str_token(cmd, " ", &context))) { if (os_strncmp(token, "bssid=", 6) == 0) { if (hwaddr_aton(token + 6, bssid)) - return -1; + goto out; got_bssid = 1; } else if (os_strcmp(token, "akmp=PASN") == 0) { akmp = WPA_KEY_MGMT_PASN; @@ -10640,20 +10643,34 @@ static int wpas_ctrl_iface_pasn_start(struct wpa_supplicant *wpa_s, char *cmd) group = atoi(token + 6); } else if (os_strncmp(token, "nid=", 4) == 0) { id = atoi(token + 4); + } else if (os_strncmp(token, "comeback=", 9) == 0) { + comeback_len = os_strlen(token + 9); + if (comeback || !comeback_len || comeback_len % 2) + goto out; + + comeback_len /= 2; + comeback = os_malloc(comeback_len); + if (!comeback || + hexstr2bin(token + 9, comeback, comeback_len)) + goto out; } else { wpa_printf(MSG_DEBUG, "CTRL: PASN Invalid parameter: '%s'", token); - return -1; + goto out; } } if (!got_bssid || akmp == -1 || cipher == -1 || group == 0xFFFF) { wpa_printf(MSG_DEBUG,"CTRL: PASN missing parameter"); - return -1; + goto out; } - return wpas_pasn_auth_start(wpa_s, bssid, akmp, cipher, group, id); + ret = wpas_pasn_auth_start(wpa_s, bssid, akmp, cipher, group, id, + comeback, comeback_len); +out: + os_free(comeback); + return ret; } diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c index e6adf73f3..2d7d819c9 100644 --- a/wpa_supplicant/pasn_supplicant.c +++ b/wpa_supplicant/pasn_supplicant.c @@ -33,6 +33,7 @@ struct wpa_pasn_auth_work { int cipher; u16 group; int network_id; + struct wpabuf *comeback; }; @@ -56,8 +57,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), @@ -612,7 +635,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, + struct wpabuf *comeback) { struct wpas_pasn *pasn = &wpa_s->pasn; struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL; @@ -680,7 +704,7 @@ 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; @@ -828,6 +852,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 +974,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; @@ -1023,7 +1051,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,6 +1133,8 @@ static void wpas_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit) wpa_s, NULL); wpa_s->pasn_auth_work = NULL; } + + wpabuf_free(awork->comeback); os_free(awork); return; } @@ -1132,16 +1162,22 @@ 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: + wpabuf_free(awork->comeback); + awork->comeback = NULL; os_free(awork); work->ctx = NULL; radio_work_done(work); @@ -1149,7 +1185,8 @@ fail: 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,8 +1232,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) { + os_free(awork); + return -1; + } + } + if (radio_add_work(wpa_s, bss->freq, "pasn-start-auth", 1, wpas_pasn_auth_start_cb, awork) < 0) { + wpabuf_free(awork->comeback); os_free(awork); return -1; } @@ -1216,9 +1262,30 @@ 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); } @@ -1315,10 +1382,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; } @@ -1456,7 +1539,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; } diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 7ed8c0ee4..8813ddb71 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -554,6 +554,9 @@ struct wpas_pasn { struct wpa_ptk ptk; struct crypto_ecdh *ecdh; + struct wpabuf *comeback; + u16 comeback_after; + #ifdef CONFIG_SAE struct sae_data sae; #endif /* CONFIG_SAE */ @@ -1730,7 +1733,8 @@ void wpas_handle_assoc_resp_mscs(struct wpa_supplicant *wpa_s, const u8 *bssid, int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s, const u8 *bssid, int akmp, int cipher, - u16 group, int network_id); + u16 group, int network_id, + const u8 *comeback, size_t comeback_len); void wpas_pasn_auth_stop(struct wpa_supplicant *wpa_s); int wpas_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, const u8 *data, size_t data_len, u8 acked);