diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index 6cb5e8783..a56b7631b 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -2392,6 +2392,8 @@ const char * wpa_key_mgmt_txt(int key_mgmt, int proto) return "OWE"; case WPA_KEY_MGMT_DPP: return "DPP"; + case WPA_KEY_MGMT_PASN: + return "PASN"; default: return "UNKNOWN"; } diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index acc2d6c4c..0f4208518 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -408,6 +408,9 @@ extern "C" { #define BIT(x) (1U << (x)) #endif +/* PASN authentication status */ +#define PASN_AUTH_STATUS "PASN-AUTH-STATUS " + /* BSS command information masks */ #define WPA_BSS_MASK_ALL 0xFFFDFFFF diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk index 335279428..a283f897e 100644 --- a/wpa_supplicant/Android.mk +++ b/wpa_supplicant/Android.mk @@ -379,6 +379,7 @@ NEED_HMAC_SHA384_KDF=y NEED_SHA256=y NEED_SHA384=y OBJS += src/common/ptksa_cache.c +OBJS += pasn_supplicant.c endif ifdef CONFIG_HS20 diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index bb18d821c..765edc07b 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -398,6 +398,7 @@ NEED_HMAC_SHA384_KDF=y NEED_SHA256=y NEED_SHA384=y OBJS += ../src/common/ptksa_cache.o +OBJS += pasn_supplicant.o endif ifdef CONFIG_HS20 diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 10743745b..3dd1c2561 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -8533,6 +8533,11 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) free_bss_tmp_disallowed(wpa_s); os_memset(&wpa_s->robust_av, 0, sizeof(struct robust_av_data)); + +#ifdef CONFIG_PASN + wpas_pasn_auth_stop(wpa_s); +#endif /* CONFIG_PASN */ + } diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 7db906461..48757dbd1 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -4883,6 +4883,14 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, " type=%d stype=%d", MAC2STR(data->tx_status.dst), data->tx_status.type, data->tx_status.stype); +#ifdef CONFIG_PASN + if (data->tx_status.type == WLAN_FC_TYPE_MGMT && + data->tx_status.stype == WLAN_FC_STYPE_AUTH && + wpas_pasn_auth_tx_status(wpa_s, data->tx_status.data, + data->tx_status.data_len, + data->tx_status.ack) == 0) + break; +#endif /* CONFIG_PASN */ #ifdef CONFIG_AP if (wpa_s->ap_iface == NULL) { #ifdef CONFIG_OFFCHANNEL @@ -5111,6 +5119,12 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, mesh_mpm_mgmt_rx(wpa_s, &data->rx_mgmt); break; } +#ifdef CONFIG_PASN + if (stype == WLAN_FC_STYPE_AUTH && + wpas_pasn_auth_rx(wpa_s, mgmt, + data->rx_mgmt.frame_len) != -2) + break; +#endif /* CONFIG_PASN */ #ifdef CONFIG_SAE if (stype == WLAN_FC_STYPE_AUTH && diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c new file mode 100644 index 000000000..d4ff650a2 --- /dev/null +++ b/wpa_supplicant/pasn_supplicant.c @@ -0,0 +1,880 @@ +/* + * wpa_supplicant - PASN processing + * + * Copyright (C) 2019 Intel Corporation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "common/dragonfly.h" +#include "common/ptksa_cache.h" +#include "utils/eloop.h" +#include "drivers/driver.h" +#include "crypto/crypto.h" +#include "rsn_supp/wpa.h" +#include "rsn_supp/pmksa_cache.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "bss.h" + +static const int dot11RSNAConfigPMKLifetime = 43200; + +struct wpa_pasn_auth_work { + u8 bssid[ETH_ALEN]; + int akmp; + int cipher; + u16 group; +}; + + +static void wpas_pasn_auth_work_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + wpa_printf(MSG_DEBUG, "PASN: Auth work timeout - stopping auth"); + + wpas_pasn_auth_stop(wpa_s); +} + + +static void wpas_pasn_cancel_auth_work(struct wpa_supplicant *wpa_s) +{ + wpa_printf(MSG_DEBUG, "PASN: Cancel pasn-start-auth work"); + + /* Remove pending/started work */ + radio_remove_works(wpa_s, "pasn-start-auth", 0); +} + + +static void wpas_pasn_auth_status(struct wpa_supplicant *wpa_s, const u8 *bssid, + int akmp, int cipher, u8 status) +{ + wpa_msg(wpa_s, MSG_INFO, + PASN_AUTH_STATUS MACSTR " akmp=%s, status=%u", + MAC2STR(bssid), wpa_key_mgmt_txt(akmp, WPA_PROTO_RSN), + status); +} + + +static struct wpabuf * wpas_pasn_get_wrapped_data(struct wpas_pasn *pasn) +{ + switch (pasn->akmp) { + case WPA_KEY_MGMT_PASN: + /* no wrapped data */ + return NULL; + case WPA_KEY_MGMT_SAE: + case WPA_KEY_MGMT_FILS_SHA256: + case WPA_KEY_MGMT_FILS_SHA384: + case WPA_KEY_MGMT_FT_PSK: + case WPA_KEY_MGMT_FT_IEEE8021X: + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: + default: + wpa_printf(MSG_ERROR, + "PASN: TODO: Wrapped data for akmp=0x%x", + pasn->akmp); + return NULL; + } +} + + +static u8 wpas_pasn_get_wrapped_data_format(struct wpas_pasn *pasn) +{ + /* Note: Valid AKMP is expected to already be validated */ + switch (pasn->akmp) { + case WPA_KEY_MGMT_SAE: + return WPA_PASN_WRAPPED_DATA_SAE; + case WPA_KEY_MGMT_FILS_SHA256: + case WPA_KEY_MGMT_FILS_SHA384: + return WPA_PASN_WRAPPED_DATA_FILS_SK; + case WPA_KEY_MGMT_FT_PSK: + case WPA_KEY_MGMT_FT_IEEE8021X: + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: + return WPA_PASN_WRAPPED_DATA_FT; + case WPA_KEY_MGMT_PASN: + default: + return WPA_PASN_WRAPPED_DATA_NO; + } +} + + +static struct wpabuf * wpas_pasn_build_auth_1(struct wpa_supplicant *wpa_s) +{ + struct wpas_pasn *pasn = &wpa_s->pasn; + struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL; + struct rsn_pmksa_cache_entry *pmksa; + u8 wrapped_data; + int ret; + + wpa_printf(MSG_DEBUG, "PASN: Building frame 1"); + + if (pasn->trans_seq) + return NULL; + + buf = wpabuf_alloc(1500); + if (!buf) + goto fail; + + /* Get public key */ + pubkey = crypto_ecdh_get_pubkey(pasn->ecdh, 0); + pubkey = wpabuf_zeropad(pubkey, crypto_ecdh_prime_len(pasn->ecdh)); + if (!pubkey) { + wpa_printf(MSG_DEBUG, "PASN: Failed to get pubkey"); + goto fail; + } + + wrapped_data = wpas_pasn_get_wrapped_data_format(pasn); + + wpa_pasn_build_auth_header(buf, pasn->bssid, + wpa_s->own_addr, pasn->bssid, + pasn->trans_seq + 1, WLAN_STATUS_SUCCESS); + + if (wrapped_data != WPA_PASN_WRAPPED_DATA_NO) { + pmksa = wpa_sm_pmksa_cache_get(wpa_s->wpa, pasn->bssid, + NULL, NULL, pasn->akmp); + + /* + * Note: Even when PMKSA is available, also add wrapped data as + * it is possible that the PMKID is no longer valid at the AP. + */ + wrapped_data_buf = wpas_pasn_get_wrapped_data(pasn); + } else { + pmksa = NULL; + } + + if (wpa_pasn_add_rsne(buf, pmksa ? pmksa->pmkid : NULL, + pasn->akmp, pasn->cipher) < 0) + goto fail; + + if (!wrapped_data_buf) + wrapped_data = WPA_PASN_WRAPPED_DATA_NO; + + wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data, + pubkey, NULL, -1); + + if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0) + goto fail; + + ret = pasn_auth_frame_hash(pasn->akmp, pasn->cipher, + wpabuf_head_u8(buf) + IEEE80211_HDRLEN, + wpabuf_len(buf) - IEEE80211_HDRLEN, + pasn->hash); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash"); + goto fail; + } + + pasn->trans_seq++; + + wpabuf_free(wrapped_data_buf); + wpabuf_free(pubkey); + + wpa_printf(MSG_DEBUG, "PASN: Frame 1: Success"); + return buf; +fail: + pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE; + wpabuf_free(wrapped_data_buf); + wpabuf_free(pubkey); + wpabuf_free(buf); + return NULL; +} + + +static struct wpabuf * wpas_pasn_build_auth_3(struct wpa_supplicant *wpa_s) +{ + struct wpas_pasn *pasn = &wpa_s->pasn; + struct wpabuf *buf, *wrapped_data_buf = NULL; + u8 mic[WPA_PASN_MAX_MIC_LEN]; + u8 mic_len, data_len; + const u8 *data; + u8 *ptr; + u8 wrapped_data; + int ret; + + wpa_printf(MSG_DEBUG, "PASN: Building frame 3"); + + if (pasn->trans_seq != 2) + return NULL; + + buf = wpabuf_alloc(1500); + if (!buf) + goto fail; + + wrapped_data = wpas_pasn_get_wrapped_data_format(pasn); + + wpa_pasn_build_auth_header(buf, pasn->bssid, + wpa_s->own_addr, pasn->bssid, + pasn->trans_seq + 1, WLAN_STATUS_SUCCESS); + + wrapped_data_buf = wpas_pasn_get_wrapped_data(pasn); + + if (!wrapped_data_buf) + wrapped_data = WPA_PASN_WRAPPED_DATA_NO; + + wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data, + NULL, NULL, -1); + + if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0) + goto fail; + wpabuf_free(wrapped_data_buf); + wrapped_data_buf = NULL; + + /* Add the MIC */ + mic_len = pasn_mic_len(pasn->akmp, pasn->cipher); + wpabuf_put_u8(buf, WLAN_EID_MIC); + wpabuf_put_u8(buf, mic_len); + ptr = wpabuf_put(buf, mic_len); + + os_memset(ptr, 0, mic_len); + + data = wpabuf_head_u8(buf) + IEEE80211_HDRLEN; + data_len = wpabuf_len(buf) - IEEE80211_HDRLEN; + + ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher, + wpa_s->own_addr, pasn->bssid, + pasn->hash, mic_len * 2, data, data_len, mic); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: frame 3: Failed MIC calculation"); + goto fail; + } + + os_memcpy(ptr, mic, mic_len); + + pasn->trans_seq++; + + wpa_printf(MSG_DEBUG, "PASN: frame 3: Success"); + return buf; +fail: + pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE; + wpabuf_free(wrapped_data_buf); + wpabuf_free(buf); + return NULL; +} + + +static void wpas_pasn_reset(struct wpa_supplicant *wpa_s) +{ + struct wpas_pasn *pasn = &wpa_s->pasn; + + wpa_printf(MSG_DEBUG, "PASN: Reset"); + + crypto_ecdh_deinit(pasn->ecdh); + pasn->ecdh = NULL; + + wpas_pasn_cancel_auth_work(wpa_s); + wpa_s->pasn_auth_work = NULL; + + eloop_cancel_timeout(wpas_pasn_auth_work_timeout, wpa_s, NULL); + + pasn->akmp = 0; + pasn->cipher = 0; + pasn->group = 0; + pasn->trans_seq = 0; + pasn->pmk_len = 0; + + forced_memzero(pasn->pmk, sizeof(pasn->pmk)); + forced_memzero(&pasn->ptk, sizeof(pasn->ptk)); + forced_memzero(&pasn->hash, sizeof(pasn->hash)); + + wpabuf_free(pasn->beacon_rsne); + pasn->beacon_rsne = NULL; + + pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE; +} + + +static int wpas_pasn_set_pmk(struct wpa_supplicant *wpa_s, + struct wpa_ie_data *rsn_data, + struct wpa_pasn_params_data *pasn_data, + struct wpabuf *wrapped_data) +{ + static const u8 pasn_default_pmk[] = {'P', 'M', 'K', 'z'}; + struct wpas_pasn *pasn = &wpa_s->pasn; + + os_memset(pasn->pmk, 0, sizeof(pasn->pmk)); + pasn->pmk_len = 0; + + if (pasn->akmp == WPA_KEY_MGMT_PASN) { + wpa_printf(MSG_DEBUG, "PASN: Using default PMK"); + + pasn->pmk_len = WPA_PASN_PMK_LEN; + os_memcpy(pasn->pmk, pasn_default_pmk, + sizeof(pasn_default_pmk)); + return 0; + } + + if (rsn_data->num_pmkid) { + struct rsn_pmksa_cache_entry *pmksa; + + pmksa = wpa_sm_pmksa_cache_get(wpa_s->wpa, pasn->bssid, + rsn_data->pmkid, NULL, + pasn->akmp); + if (pmksa) { + wpa_printf(MSG_DEBUG, "PASN: Using PMKSA"); + + pasn->pmk_len = pmksa->pmk_len; + os_memcpy(pasn->pmk, pmksa->pmk, pmksa->pmk_len); + + return 0; + } + } + + /* TODO: Derive PMK based on wrapped data */ + wpa_printf(MSG_DEBUG, "PASN: Missing implementation to derive PMK"); + pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE; + return -1; +} + + +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) +{ + struct wpas_pasn *pasn = &wpa_s->pasn; + struct wpabuf *frame; + int ret; + + /* TODO: Currently support only ECC groups */ + if (!dragonfly_suitable_group(group, 1)) { + wpa_printf(MSG_DEBUG, + "PASN: Reject unsuitable group %u", group); + return -1; + } + + switch (akmp) { + case WPA_KEY_MGMT_PASN: + break; +#ifdef CONFIG_SAE + case WPA_KEY_MGMT_SAE: + break; +#endif /* CONFIG_SAE */ +#ifdef CONFIG_FILS + case WPA_KEY_MGMT_FILS_SHA256: + break; + case WPA_KEY_MGMT_FILS_SHA384: + break; +#endif /* CONFIG_FILS */ +#ifdef CONFIG_IEEE80211R + case WPA_KEY_MGMT_FT_PSK: + case WPA_KEY_MGMT_FT_IEEE8021X: + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: + break; +#endif /* CONFIG_IEEE80211R */ + default: + wpa_printf(MSG_ERROR, "PASN: Unsupported AKMP=0x%x", akmp); + return -1; + } + + pasn->ecdh = crypto_ecdh_init(group); + if (!pasn->ecdh) { + wpa_printf(MSG_DEBUG, "PASN: Failed to init ECDH"); + goto fail; + } + + pasn->beacon_rsne = wpabuf_alloc_copy(beacon_rsne, beacon_rsne_len); + if (!pasn->beacon_rsne) { + wpa_printf(MSG_DEBUG, "PASN: Failed storing beacon RSNE"); + goto fail; + } + + pasn->akmp = akmp; + pasn->cipher = cipher; + pasn->group = group; + pasn->freq = freq; + os_memcpy(pasn->bssid, bssid, ETH_ALEN); + + wpa_printf(MSG_DEBUG, + "PASN: Init: " MACSTR " akmp=0x%x, cipher=0x%x, group=%u", + MAC2STR(pasn->bssid), pasn->akmp, pasn->cipher, + pasn->group); + + frame = wpas_pasn_build_auth_1(wpa_s); + if (!frame) { + wpa_printf(MSG_DEBUG, "PASN: Failed building 1st auth frame"); + goto fail; + } + + ret = wpa_drv_send_mlme(wpa_s, wpabuf_head(frame), wpabuf_len(frame), 0, + pasn->freq, 1000); + + wpabuf_free(frame); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed sending 1st auth frame"); + goto fail; + } + + eloop_register_timeout(2, 0, wpas_pasn_auth_work_timeout, wpa_s, NULL); + return 0; + +fail: + return -1; +} + + +static struct wpa_bss * wpas_pasn_allowed(struct wpa_supplicant *wpa_s, + const u8 *bssid, int akmp, int cipher) +{ + struct wpa_bss *bss; + const u8 *rsne; + struct wpa_ie_data rsne_data; + int ret; + + if (os_memcmp(wpa_s->bssid, bssid, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, + "PASN: Not doing authentication with current BSS"); + return NULL; + } + + bss = wpa_bss_get_bssid(wpa_s, bssid); + if (!bss) { + wpa_printf(MSG_DEBUG, "PASN: BSS not found"); + return NULL; + } + + rsne = wpa_bss_get_ie(bss, WLAN_EID_RSN); + if (!rsne) { + wpa_printf(MSG_DEBUG, "PASN: BSS without RSNE"); + return NULL; + } + + ret = wpa_parse_wpa_ie(rsne, *(rsne + 1) + 2, &rsne_data); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed parsing RSNE data"); + return NULL; + } + + if (!(rsne_data.key_mgmt & akmp) || + !(rsne_data.pairwise_cipher & cipher)) { + wpa_printf(MSG_DEBUG, + "PASN: AP does not support requested AKMP or cipher"); + return NULL; + } + + return bss; +} + + +static void wpas_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit) +{ + struct wpa_supplicant *wpa_s = work->wpa_s; + struct wpa_pasn_auth_work *awork = work->ctx; + struct wpa_bss *bss; + const u8 *rsne; + int ret; + + wpa_printf(MSG_DEBUG, "PASN: auth_start_cb: deinit=%d", deinit); + + if (deinit) { + if (work->started) { + eloop_cancel_timeout(wpas_pasn_auth_work_timeout, + wpa_s, NULL); + wpa_s->pasn_auth_work = NULL; + } + os_free(awork); + return; + } + + /* + * It is possible that by the time the callback is called, the PASN + * authentication is not allowed, e.g., a connection with the AP was + * established. + */ + bss = wpas_pasn_allowed(wpa_s, awork->bssid, awork->akmp, + awork->cipher); + if (!bss) { + wpa_printf(MSG_DEBUG, "PASN: auth_start_cb: Not allowed"); + goto fail; + } + + rsne = wpa_bss_get_ie(bss, WLAN_EID_RSN); + if (!rsne) { + wpa_printf(MSG_DEBUG, "PASN: BSS without RSNE"); + goto fail; + } + + ret = wpas_pasn_start(wpa_s, awork->bssid, awork->akmp, awork->cipher, + awork->group, bss->freq, rsne, *(rsne + 1) + 2); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: Failed to start PASN authentication"); + goto fail; + } + + wpa_s->pasn_auth_work = work; + return; +fail: + os_free(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) +{ + struct wpa_pasn_auth_work *awork; + struct wpa_bss *bss; + + wpa_printf(MSG_DEBUG, "PASN: Start: " MACSTR " akmp=0x%x, cipher=0x%x", + MAC2STR(bssid), akmp, cipher); + + /* + * TODO: Consider modifying the offchannel logic to handle additional + * Management frames other then Action frames. For now allow PASN only + * with drivers that support off-channel TX. + */ + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX)) { + wpa_printf(MSG_DEBUG, + "PASN: Driver does not support offchannel TX"); + return -1; + } + + if (radio_work_pending(wpa_s, "pasn-start-auth")) { + wpa_printf(MSG_DEBUG, + "PASN: send_auth: Work is already pending"); + return -1; + } + + if (wpa_s->pasn_auth_work) { + wpa_printf(MSG_DEBUG, "PASN: send_auth: Already in progress"); + return -1; + } + + bss = wpas_pasn_allowed(wpa_s, bssid, akmp, cipher); + if (!bss) + return -1; + + wpas_pasn_reset(wpa_s); + + awork = os_zalloc(sizeof(*awork)); + if (!awork) + return -1; + + os_memcpy(awork->bssid, bssid, ETH_ALEN); + awork->akmp = akmp; + awork->cipher = cipher; + awork->group = group; + + if (radio_add_work(wpa_s, bss->freq, "pasn-start-auth", 1, + wpas_pasn_auth_start_cb, awork) < 0) { + os_free(awork); + return -1; + } + + wpa_printf(MSG_DEBUG, "PASN: Auth work successfully added"); + return 0; +} + + +void wpas_pasn_auth_stop(struct wpa_supplicant *wpa_s) +{ + struct wpas_pasn *pasn = &wpa_s->pasn; + + if (!wpa_s->pasn.ecdh) + return; + + wpa_printf(MSG_DEBUG, "PASN: Stopping authentication"); + + wpas_pasn_auth_status(wpa_s, pasn->bssid, pasn->akmp, pasn->cipher, + pasn->status); + + wpas_pasn_reset(wpa_s); +} + + +int wpas_pasn_auth_rx(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + struct wpas_pasn *pasn = &wpa_s->pasn; + struct ieee802_11_elems elems; + struct wpa_ie_data rsn_data; + struct wpa_pasn_params_data pasn_params; + struct wpabuf *wrapped_data = NULL, *secret = NULL, *frame = NULL; + u8 mic[WPA_PASN_MAX_MIC_LEN], out_mic[WPA_PASN_MAX_MIC_LEN]; + u8 mic_len; + u16 status; + int ret; + u16 fc = host_to_le16((WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_AUTH << 4)); + + if (!wpa_s->pasn_auth_work || !mgmt || + len < offsetof(struct ieee80211_mgmt, u.auth.variable)) + return -2; + + /* Not an Authentication frame; do nothing */ + if ((mgmt->frame_control & fc) != fc) + return -2; + + /* Not our frame; do nothing */ + if (os_memcmp(mgmt->da, wpa_s->own_addr, ETH_ALEN) != 0 || + os_memcmp(mgmt->sa, pasn->bssid, ETH_ALEN) != 0 || + os_memcmp(mgmt->bssid, pasn->bssid, ETH_ALEN) != 0) + return -2; + + /* Not PASN; do nothing */ + if (mgmt->u.auth.auth_alg != host_to_le16(WLAN_AUTH_PASN)) + return -2; + + if (mgmt->u.auth.auth_transaction != + host_to_le16(pasn->trans_seq + 1)) { + wpa_printf(MSG_DEBUG, + "PASN: RX: Invalid transaction sequence: (%u != %u)", + le_to_host16(mgmt->u.auth.auth_transaction), + pasn->trans_seq + 1); + return -1; + } + + status = le_to_host16(mgmt->u.auth.status_code); + + if (status != WLAN_STATUS_SUCCESS && + status != WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) { + wpa_printf(MSG_DEBUG, + "PASN: Authentication rejected - status=%u", status); + pasn->status = status; + wpas_pasn_auth_stop(wpa_s); + return -1; + } + + if (ieee802_11_parse_elems(mgmt->u.auth.variable, + len - offsetof(struct ieee80211_mgmt, + u.auth.variable), + &elems, 0) == ParseFailed) { + wpa_printf(MSG_DEBUG, + "PASN: Failed parsing Authentication frame"); + goto fail; + } + + /* Check that the MIC IE exists. Save it and zero out the memory */ + mic_len = pasn_mic_len(pasn->akmp, pasn->cipher); + if (status == WLAN_STATUS_SUCCESS) { + if (!elems.mic || elems.mic_len != mic_len) { + wpa_printf(MSG_DEBUG, + "PASN: Invalid MIC. Expecting len=%u", + mic_len); + goto fail; + } else { + os_memcpy(mic, elems.mic, mic_len); + /* TODO: Clean this up.. Should not be modifying the + * received message buffer. */ + os_memset((u8 *) elems.mic, 0, mic_len); + } + } + + if (!elems.pasn_params || !elems.pasn_params_len) { + wpa_printf(MSG_DEBUG, + "PASN: Missing PASN Parameters IE"); + goto fail; + } + + ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3, + elems.pasn_params_len + 3, + true, &pasn_params); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: Failed validation PASN of Parameters IE"); + goto fail; + } + + /* TODO: handle comeback flow */ + if (status == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) { + wpa_printf(MSG_DEBUG, + "PASN: Authentication temporarily rejected"); + goto fail; + } + + ret = wpa_parse_wpa_ie(elems.rsn_ie - 2, elems.rsn_ie_len + 2, + &rsn_data); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed parsing RNSE"); + goto fail; + } + + ret = wpa_pasn_validate_rsne(&rsn_data); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE"); + goto fail; + } + + if (pasn->akmp != rsn_data.key_mgmt || + pasn->cipher != rsn_data.pairwise_cipher) { + wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher"); + goto fail; + } + + if (pasn->group != pasn_params.group) { + wpa_printf(MSG_DEBUG, "PASN: Mismatch in group"); + goto fail; + } + + if (!pasn_params.pubkey || !pasn_params.pubkey_len) { + wpa_printf(MSG_DEBUG, "PASN: Invalid public key"); + goto fail; + } + + secret = crypto_ecdh_set_peerkey(pasn->ecdh, 0, + pasn_params.pubkey, + pasn_params.pubkey_len); + + if (!secret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to derive shared secret"); + goto fail; + } + + if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) { + wrapped_data = ieee802_11_defrag(&elems, + WLAN_EID_EXTENSION, + WLAN_EID_EXT_WRAPPED_DATA); + + if (!wrapped_data) { + wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data"); + goto fail; + } + } + + ret = wpas_pasn_set_pmk(wpa_s, &rsn_data, &pasn_params, wrapped_data); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to set PMK"); + goto fail; + } + + ret = pasn_pmk_to_ptk(pasn->pmk, pasn->pmk_len, + wpa_s->own_addr, pasn->bssid, + wpabuf_head(secret), wpabuf_len(secret), + &pasn->ptk, pasn->akmp, pasn->cipher, + WPA_KDK_MAX_LEN); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK"); + goto fail; + } + + wpabuf_free(wrapped_data); + wrapped_data = NULL; + wpabuf_free(secret); + secret = NULL; + + /* Verify the MIC */ + ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher, + pasn->bssid, wpa_s->own_addr, + wpabuf_head(pasn->beacon_rsne), + wpabuf_len(pasn->beacon_rsne), + (u8 *) &mgmt->u.auth, + len - offsetof(struct ieee80211_mgmt, u.auth), + out_mic); + + wpa_hexdump_key(MSG_DEBUG, "PASN: Frame MIC", mic, mic_len); + if (ret || os_memcmp(mic, out_mic, mic_len) != 0) { + wpa_printf(MSG_DEBUG, "PASN: Failed MIC verification"); + goto fail; + } + + pasn->trans_seq++; + + wpa_printf(MSG_DEBUG, "PASN: Success verifying Authentication frame"); + + frame = wpas_pasn_build_auth_3(wpa_s); + if (!frame) { + wpa_printf(MSG_DEBUG, "PASN: Failed building 3rd auth frame"); + goto fail; + } + + ret = wpa_drv_send_mlme(wpa_s, wpabuf_head(frame), wpabuf_len(frame), 0, + pasn->freq, 100); + wpabuf_free(frame); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed sending 3st auth frame"); + goto fail; + } + + wpa_printf(MSG_DEBUG, "PASN: Success sending last frame. Store PTK"); + + ptksa_cache_add(wpa_s->ptksa, pasn->bssid, pasn->cipher, + dot11RSNAConfigPMKLifetime, &pasn->ptk); + + forced_memzero(&pasn->ptk, sizeof(pasn->ptk)); + + pasn->status = WLAN_STATUS_SUCCESS; + return 0; +fail: + wpa_printf(MSG_DEBUG, "PASN: Failed RX processing - terminating"); + wpabuf_free(wrapped_data); + wpabuf_free(secret); + + /* + * TODO: In case of an error the standard allows to silently drop + * the frame and terminate the authentication exchange. However, better + * reply to the AP with an error status. + */ + pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE; + wpas_pasn_auth_stop(wpa_s); + return -1; +} + + +int wpas_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, + const u8 *data, size_t data_len, u8 acked) + +{ + struct wpas_pasn *pasn = &wpa_s->pasn; + const struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) data; + u16 fc = host_to_le16((WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_AUTH << 4)); + + wpa_printf(MSG_DEBUG, "PASN: auth_tx_status: acked=%u", acked); + + if (!wpa_s->pasn_auth_work) { + wpa_printf(MSG_DEBUG, + "PASN: auth_tx_status: no work in progress"); + return -1; + } + + if (!mgmt || + data_len < offsetof(struct ieee80211_mgmt, u.auth.variable)) + return -1; + + /* Not an authentication frame; do nothing */ + if ((mgmt->frame_control & fc) != fc) + return -1; + + /* Not our frame; do nothing */ + if (os_memcmp(mgmt->da, pasn->bssid, ETH_ALEN) || + os_memcmp(mgmt->sa, wpa_s->own_addr, ETH_ALEN) || + os_memcmp(mgmt->bssid, pasn->bssid, ETH_ALEN)) + return -1; + + /* Not PASN; do nothing */ + if (mgmt->u.auth.auth_alg != host_to_le16(WLAN_AUTH_PASN)) + return -1; + + if (mgmt->u.auth.auth_transaction != host_to_le16(pasn->trans_seq)) { + wpa_printf(MSG_ERROR, + "PASN: Invalid transaction sequence: (%u != %u)", + pasn->trans_seq, + le_to_host16(mgmt->u.auth.auth_transaction)); + return 0; + } + + wpa_printf(MSG_ERROR, + "PASN: auth with trans_seq=%u, acked=%u", pasn->trans_seq, + acked); + + /* + * Even if the frame was not acked, do not treat this is an error, and + * try to complete the flow, relying on the PASN timeout callback to + * clean up. + */ + if (pasn->trans_seq == 3) { + wpa_printf(MSG_DEBUG, "PASN: auth complete with: " MACSTR, + MAC2STR(pasn->bssid)); + /* + * Either frame was not ACKed or it was ACKed but the trans_seq + * != 1, i.e., not expecting an RX frame, so we are done. + */ + wpas_pasn_auth_stop(wpa_s); + } + + return 0; +} diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 1ba35c3c2..a7e74344c 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -580,6 +580,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpa_s->wpa = NULL; wpa_blacklist_clear(wpa_s); +#ifdef CONFIG_PASN + wpas_pasn_auth_stop(wpa_s); +#endif /* CONFIG_PASN */ + wpa_bss_deinit(wpa_s); wpa_supplicant_cancel_delayed_sched_scan(wpa_s); @@ -729,6 +733,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) dpp_global_deinit(wpa_s->dpp); wpa_s->dpp = NULL; #endif /* CONFIG_DPP */ + +#ifdef CONFIG_PASN + wpas_pasn_auth_stop(wpa_s); +#endif /* CONFIG_PASN */ } diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 09f6a7b4a..506de6f03 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -14,6 +14,7 @@ #include "common/defs.h" #include "common/sae.h" #include "common/wpa_ctrl.h" +#include "crypto/sha384.h" #include "wps/wps_defs.h" #include "config_ssid.h" #include "wmm_ac.h" @@ -514,6 +515,28 @@ struct robust_av_data { bool valid_config; }; +#ifdef CONFIG_PASN +struct wpas_pasn { + int akmp; + int cipher; + u16 group; + int freq; + + u8 trans_seq; + u8 status; + + u8 bssid[ETH_ALEN]; + size_t pmk_len; + u8 pmk[PMK_LEN_MAX]; + + u8 hash[SHA384_MAC_LEN]; + + struct wpabuf *beacon_rsne; + struct wpa_ptk ptk; + struct crypto_ecdh *ecdh; +}; +#endif /* CONFIG_PASN */ + /** * struct wpa_supplicant - Internal data for wpa_supplicant interface * @@ -1334,6 +1357,11 @@ struct wpa_supplicant { unsigned int multi_ap_fronthaul:1; struct robust_av_data robust_av; bool mscs_setup_done; + +#ifdef CONFIG_PASN + struct wpas_pasn pasn; + struct wpa_radio_work *pasn_auth_work; +#endif /* CONFIG_PASN */ }; @@ -1655,4 +1683,13 @@ void wpas_handle_robust_av_recv_action(struct wpa_supplicant *wpa_s, void wpas_handle_assoc_resp_mscs(struct wpa_supplicant *wpa_s, const u8 *bssid, const u8 *ies, size_t ies_len); +int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s, + const u8 *bssid, int akmp, int cipher, + u16 group); +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); +int wpas_pasn_auth_rx(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, size_t len); + #endif /* WPA_SUPPLICANT_I_H */