From 706df4291bea691c65631bc498dddc29790d85bb Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Wed, 9 Sep 2015 17:34:13 +0300 Subject: [PATCH] FILS: Association Response processing (STA) Decrypt the AES-SIV protected elements and verify Key-Auth. Parse and configure keys to the driver. Signed-off-by: Jouni Malinen --- src/rsn_supp/wpa.c | 179 ++++++++++++++++++++++++++++++++++++++++ src/rsn_supp/wpa.h | 2 + src/rsn_supp/wpa_i.h | 1 + wpa_supplicant/events.c | 20 +++++ 4 files changed, 202 insertions(+) diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 136fd091e..65e257a93 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -2478,6 +2478,16 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) clear_ptk = 0; } #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_FILS + if (sm->fils_completed) { + /* + * Clear portValid to kick EAPOL state machine to re-enter + * AUTHENTICATED state to get the EAPOL port Authorized. + */ + wpa_supplicant_key_neg_complete(sm, sm->bssid, 1); + clear_ptk = 0; + } +#endif /* CONFIG_FILS */ if (clear_ptk) { /* @@ -2520,6 +2530,9 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm) #ifdef CONFIG_TDLS wpa_tdls_disassoc(sm); #endif /* CONFIG_TDLS */ +#ifdef CONFIG_FILS + sm->fils_completed = 0; +#endif /* CONFIG_FILS */ /* Keys are not needed in the WPA state machine anymore */ wpa_sm_drop_sa(sm); @@ -3219,6 +3232,8 @@ struct wpabuf * fils_build_auth(struct wpa_sm *sm) wpa_printf(MSG_DEBUG, "FILS: Try to use FILS (erp=%d pmksa_cache=%d)", erp_msg != NULL, sm->cur_pmksa != NULL); + sm->fils_completed = 0; + if (!sm->assoc_wpa_ie) { wpa_printf(MSG_INFO, "FILS: No own RSN IE set for FILS"); goto fail; @@ -3471,4 +3486,168 @@ struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek, return buf; } + +int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len) +{ + const struct ieee80211_mgmt *mgmt; + const u8 *end, *ie_start; + struct ieee802_11_elems elems; + int keylen, rsclen; + enum wpa_alg alg; + struct wpa_gtk_data gd; + int maxkeylen; + struct wpa_eapol_ie_parse kde; + + if (!sm || !sm->ptk_set) { + wpa_printf(MSG_DEBUG, "FILS: No KEK available"); + return -1; + } + + if (!wpa_key_mgmt_fils(sm->key_mgmt)) { + wpa_printf(MSG_DEBUG, "FILS: Not a FILS AKM"); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "FILS: (Re)Association Response frame", + resp, len); + + mgmt = (const struct ieee80211_mgmt *) resp; + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_resp)) + return -1; + + end = resp + len; + /* Same offset for Association Response and Reassociation Response */ + ie_start = mgmt->u.assoc_resp.variable; + + if (ieee802_11_parse_elems(ie_start, end - ie_start, &elems, 1) == + ParseFailed) { + wpa_printf(MSG_DEBUG, + "FILS: Failed to parse decrypted elements"); + goto fail; + } + + if (!elems.fils_session) { + wpa_printf(MSG_DEBUG, "FILS: No FILS Session element"); + return -1; + } + if (os_memcmp(elems.fils_session, sm->fils_session, + FILS_SESSION_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FILS: FILS Session mismatch"); + wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session", + elems.fils_session, FILS_SESSION_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session", + sm->fils_session, FILS_SESSION_LEN); + } + + /* TODO: FILS Public Key */ + + if (!elems.fils_key_confirm) { + wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element"); + goto fail; + } + if (elems.fils_key_confirm_len != sm->fils_key_auth_len) { + wpa_printf(MSG_DEBUG, + "FILS: Unexpected Key-Auth length %d (expected %d)", + elems.fils_key_confirm_len, + (int) sm->fils_key_auth_len); + goto fail; + } + if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_ap, + sm->fils_key_auth_len) != 0) { + wpa_printf(MSG_DEBUG, "FILS: Key-Auth mismatch"); + wpa_hexdump(MSG_DEBUG, "FILS: Received Key-Auth", + elems.fils_key_confirm, + elems.fils_key_confirm_len); + wpa_hexdump(MSG_DEBUG, "FILS: Expected Key-Auth", + sm->fils_key_auth_ap, sm->fils_key_auth_len); + goto fail; + } + + /* Key Delivery */ + if (!elems.key_delivery) { + wpa_printf(MSG_DEBUG, "FILS: No Key Delivery element"); + goto fail; + } + + /* Parse GTK and set the key to the driver */ + os_memset(&gd, 0, sizeof(gd)); + if (wpa_supplicant_parse_ies(elems.key_delivery + WPA_KEY_RSC_LEN, + elems.key_delivery_len - WPA_KEY_RSC_LEN, + &kde) < 0) { + wpa_printf(MSG_DEBUG, "FILS: Failed to parse KDEs"); + goto fail; + } + if (!kde.gtk) { + wpa_printf(MSG_DEBUG, "FILS: No GTK KDE"); + goto fail; + } + maxkeylen = gd.gtk_len = kde.gtk_len - 2; + if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, + gd.gtk_len, maxkeylen, + &gd.key_rsc_len, &gd.alg)) + goto fail; + + wpa_hexdump_key(MSG_DEBUG, "FILS: Received GTK", kde.gtk, kde.gtk_len); + gd.keyidx = kde.gtk[0] & 0x3; + gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm, + !!(kde.gtk[0] & BIT(2))); + if (kde.gtk_len - 2 > sizeof(gd.gtk)) { + wpa_printf(MSG_DEBUG, "FILS: Too long GTK in GTK KDE (len=%lu)", + (unsigned long) kde.gtk_len - 2); + goto fail; + } + os_memcpy(gd.gtk, kde.gtk + 2, kde.gtk_len - 2); + + wpa_printf(MSG_DEBUG, "FILS: Set GTK to driver"); + if (wpa_supplicant_install_gtk(sm, &gd, elems.key_delivery) < 0) { + wpa_printf(MSG_DEBUG, "FILS: Failed to set GTK"); + goto fail; + } + + if (ieee80211w_set_keys(sm, &kde) < 0) { + wpa_printf(MSG_DEBUG, "FILS: Failed to set IGTK"); + goto fail; + } + + alg = wpa_cipher_to_alg(sm->pairwise_cipher); + keylen = wpa_cipher_key_len(sm->pairwise_cipher); + rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher); + wpa_hexdump_key(MSG_DEBUG, "FILS: Set TK to driver", + sm->ptk.tk, keylen); + if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, null_rsc, rsclen, + sm->ptk.tk, keylen) < 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "FILS: Failed to set PTK to the driver (alg=%d keylen=%d bssid=" + MACSTR ")", + alg, keylen, MAC2STR(sm->bssid)); + goto fail; + } + + /* TODO: TK could be cleared after auth frame exchange now that driver + * takes care of association frame encryption/decryption. */ + /* TK is not needed anymore in supplicant */ + os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN); + + /* TODO: FILS HLP Container */ + + /* TODO: FILS IP Address Assignment */ + + wpa_printf(MSG_DEBUG, "FILS: Auth+Assoc completed successfully"); + sm->fils_completed = 1; + + return 0; +fail: + return -1; +} + +#endif /* CONFIG_FILS */ + + +int wpa_fils_is_completed(struct wpa_sm *sm) +{ +#ifdef CONFIG_FILS + return sm && sm->fils_completed; +#else /* CONFIG_FILS */ + return 0; #endif /* CONFIG_FILS */ +} diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index 3b2dbcf43..20d5b8e84 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -431,5 +431,7 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *data, size_t len); struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek, size_t *kek_len, const u8 **snonce, const u8 **anonce); +int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len); +int wpa_fils_is_completed(struct wpa_sm *sm); #endif /* WPA_H */ diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 45000687a..44d5424d7 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -146,6 +146,7 @@ struct wpa_sm { 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; + unsigned int fils_completed:1; #endif /* CONFIG_FILS */ }; diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index e15109c9b..99e919670 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -2058,6 +2058,19 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, if (!found && data->assoc_info.req_ies) wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); +#ifdef CONFIG_FILS +#ifdef CONFIG_SME + if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS && + (!data->assoc_info.resp_frame || + fils_process_assoc_resp(wpa_s->wpa, + data->assoc_info.resp_frame, + data->assoc_info.resp_frame_len) < 0)) { + wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_UNSPECIFIED); + return -1; + } +#endif /* CONFIG_SME */ +#endif /* CONFIG_FILS */ + #ifdef CONFIG_IEEE80211R #ifdef CONFIG_SME if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FT) { @@ -2279,6 +2292,13 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, ft_completed = wpa_ft_is_completed(wpa_s->wpa); if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0) return; + /* + * FILS authentication can share the same mechanism to mark the + * connection fully authenticated, so set ft_completed also based on + * FILS result. + */ + if (!ft_completed) + ft_completed = wpa_fils_is_completed(wpa_s->wpa); if (wpa_drv_get_bssid(wpa_s, bssid) < 0) { wpa_dbg(wpa_s, MSG_ERROR, "Failed to get BSSID"); -- 2.39.5