diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index a0e37485b..9201a1035 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -16,6 +16,7 @@ #include "crypto/random.h" #include "crypto/aes_siv.h" #include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" #include "eapol_supp/eapol_supp_sm.h" #include "wpa.h" #include "eloop.h" @@ -3286,4 +3287,139 @@ fail: return buf; } + +int fils_process_auth(struct wpa_sm *sm, const u8 *data, size_t len) +{ + const u8 *pos, *end; + struct ieee802_11_elems elems; + struct wpa_ie_data rsn; + int pmkid_match = 0; + u8 ick[FILS_ICK_MAX_LEN]; + size_t ick_len; + int res; + + wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields", + data, len); + pos = data; + end = data + len; + + /* TODO: Finite Cyclic Group when using PK or PFS */ + /* TODO: Element when using PK or PFS */ + + wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos); + if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) { + wpa_printf(MSG_DEBUG, "FILS: Could not parse elements"); + return -1; + } + + /* RSNE */ + wpa_hexdump(MSG_DEBUG, "FILS: RSN element", elems.rsn_ie, + elems.rsn_ie_len); + if (!elems.rsn_ie || + wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2, + &rsn) < 0) { + wpa_printf(MSG_DEBUG, "FILS: No RSN element"); + return -1; + } + + if (!elems.fils_nonce) { + wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field"); + return -1; + } + os_memcpy(sm->fils_anonce, elems.fils_nonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: ANonce", sm->fils_anonce, FILS_NONCE_LEN); + + /* TODO: MDE when using FILS+FT */ + /* TODO: FTE when using FILS+FT */ + + /* PMKID List */ + if (rsn.pmkid && rsn.num_pmkid > 0) { + wpa_hexdump(MSG_DEBUG, "FILS: PMKID List", + rsn.pmkid, rsn.num_pmkid * PMKID_LEN); + + if (rsn.num_pmkid != 1) { + wpa_printf(MSG_DEBUG, "FILS: Invalid PMKID selection"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "FILS: PMKID", rsn.pmkid, PMKID_LEN); + if (os_memcmp(sm->cur_pmksa->pmkid, rsn.pmkid, PMKID_LEN) != 0) + { + wpa_printf(MSG_DEBUG, "FILS: PMKID mismatch"); + wpa_hexdump(MSG_DEBUG, "FILS: Expected PMKID", + sm->cur_pmksa->pmkid, PMKID_LEN); + return -1; + } + wpa_printf(MSG_DEBUG, + "FILS: Matching PMKID - continue using PMKSA caching"); + pmkid_match = 1; + } + if (!pmkid_match && sm->cur_pmksa) { + wpa_printf(MSG_DEBUG, + "FILS: No PMKID match - cannot use cached PMKSA entry"); + sm->cur_pmksa = NULL; + } + + /* FILS Session */ + if (!elems.fils_session) { + wpa_printf(MSG_DEBUG, "FILS: No FILS Session element"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session, + FILS_SESSION_LEN); + if (os_memcmp(sm->fils_session, elems.fils_session, FILS_SESSION_LEN) + != 0) { + wpa_printf(MSG_DEBUG, "FILS: Session mismatch"); + wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session", + sm->fils_session, FILS_SESSION_LEN); + return -1; + } + + /* FILS Wrapped Data */ + if (!sm->cur_pmksa && elems.fils_wrapped_data) { + wpa_hexdump(MSG_DEBUG, "FILS: Wrapped Data", + elems.fils_wrapped_data, + elems.fils_wrapped_data_len); + eapol_sm_process_erp_finish(sm->eapol, elems.fils_wrapped_data, + elems.fils_wrapped_data_len); + if (eapol_sm_failed(sm->eapol)) + return -1; + + res = eapol_sm_get_key(sm->eapol, sm->pmk, PMK_LEN); + if (res) + return -1; + + wpa_printf(MSG_DEBUG, "FILS: ERP processing succeeded - add PMKSA cache entry for the result"); + sm->cur_pmksa = pmksa_cache_add(sm->pmksa, sm->pmk, PMK_LEN, + NULL, NULL, 0, sm->bssid, + sm->own_addr, + sm->network_ctx, sm->key_mgmt); + } + + if (!sm->cur_pmksa) { + wpa_printf(MSG_DEBUG, + "FILS: No remaining options to continue FILS authentication"); + return -1; + } + + if (fils_pmk_to_ptk(sm->pmk, sm->pmk_len, sm->own_addr, sm->bssid, + sm->fils_nonce, sm->fils_anonce, &sm->ptk, + ick, &ick_len, sm->key_mgmt, sm->pairwise_cipher) < + 0) { + wpa_printf(MSG_DEBUG, "FILS: Failed to derive PTK"); + return -1; + } + sm->ptk_set = 1; + sm->tptk_set = 0; + os_memset(&sm->tptk, 0, sizeof(sm->tptk)); + + res = fils_key_auth_sk(ick, ick_len, sm->fils_nonce, + sm->fils_anonce, sm->own_addr, sm->bssid, + NULL, 0, NULL, 0, /* TODO: SK+PFS */ + sm->key_mgmt, sm->fils_key_auth_sta, + sm->fils_key_auth_ap, + &sm->fils_key_auth_len); + os_memset(ick, 0, sizeof(ick)); + return res; +} + #endif /* CONFIG_FILS */ diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index 4b4b97378..2f99b6ee1 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -427,5 +427,6 @@ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf); void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf); struct wpabuf * fils_build_auth(struct wpa_sm *sm); +int fils_process_auth(struct wpa_sm *sm, const u8 *data, size_t len); #endif /* WPA_H */ diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 5a3effc0c..45000687a 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -142,6 +142,10 @@ struct wpa_sm { #ifdef CONFIG_FILS u8 fils_nonce[FILS_NONCE_LEN]; u8 fils_session[FILS_SESSION_LEN]; + u8 fils_anonce[FILS_NONCE_LEN]; + u8 fils_key_auth_ap[FILS_MAX_KEY_AUTH_LEN]; + u8 fils_key_auth_sta[FILS_MAX_KEY_AUTH_LEN]; + size_t fils_key_auth_len; #endif /* CONFIG_FILS */ }; diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index bcb6a46f4..4e004e979 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -958,6 +958,24 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) } #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_FILS + if (data->auth.auth_type == WLAN_AUTH_FILS_SK) { + if (fils_process_auth(wpa_s->wpa, data->auth.ies, + data->auth.ies_len) < 0) { + wpa_dbg(wpa_s, MSG_DEBUG, + "SME: FILS Authentication response processing failed"); + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" + MACSTR + " reason=%d locally_generated=1", + MAC2STR(wpa_s->pending_bssid), + WLAN_REASON_DEAUTH_LEAVING); + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); + wpa_supplicant_mark_disassoc(wpa_s); + return; + } + } +#endif /* CONFIG_FILS */ + sme_associate(wpa_s, ssid->mode, data->auth.peer, data->auth.auth_type); }