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 <jouni@qca.qualcomm.com>
This commit is contained in:
Jouni Malinen 2015-09-09 17:34:13 +03:00 committed by Jouni Malinen
parent e73ffa0925
commit 706df4291b
4 changed files with 202 additions and 0 deletions

View file

@ -2478,6 +2478,16 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
clear_ptk = 0; clear_ptk = 0;
} }
#endif /* CONFIG_IEEE80211R */ #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) { if (clear_ptk) {
/* /*
@ -2520,6 +2530,9 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm)
#ifdef CONFIG_TDLS #ifdef CONFIG_TDLS
wpa_tdls_disassoc(sm); wpa_tdls_disassoc(sm);
#endif /* CONFIG_TDLS */ #endif /* CONFIG_TDLS */
#ifdef CONFIG_FILS
sm->fils_completed = 0;
#endif /* CONFIG_FILS */
/* Keys are not needed in the WPA state machine anymore */ /* Keys are not needed in the WPA state machine anymore */
wpa_sm_drop_sa(sm); 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)", wpa_printf(MSG_DEBUG, "FILS: Try to use FILS (erp=%d pmksa_cache=%d)",
erp_msg != NULL, sm->cur_pmksa != NULL); erp_msg != NULL, sm->cur_pmksa != NULL);
sm->fils_completed = 0;
if (!sm->assoc_wpa_ie) { if (!sm->assoc_wpa_ie) {
wpa_printf(MSG_INFO, "FILS: No own RSN IE set for FILS"); wpa_printf(MSG_INFO, "FILS: No own RSN IE set for FILS");
goto fail; goto fail;
@ -3471,4 +3486,168 @@ struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek,
return buf; 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 */ #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 */
}

View file

@ -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, struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek,
size_t *kek_len, const u8 **snonce, size_t *kek_len, const u8 **snonce,
const u8 **anonce); 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 */ #endif /* WPA_H */

View file

@ -146,6 +146,7 @@ struct wpa_sm {
u8 fils_key_auth_ap[FILS_MAX_KEY_AUTH_LEN]; u8 fils_key_auth_ap[FILS_MAX_KEY_AUTH_LEN];
u8 fils_key_auth_sta[FILS_MAX_KEY_AUTH_LEN]; u8 fils_key_auth_sta[FILS_MAX_KEY_AUTH_LEN];
size_t fils_key_auth_len; size_t fils_key_auth_len;
unsigned int fils_completed:1;
#endif /* CONFIG_FILS */ #endif /* CONFIG_FILS */
}; };

View file

@ -2058,6 +2058,19 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
if (!found && data->assoc_info.req_ies) if (!found && data->assoc_info.req_ies)
wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); 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_IEEE80211R
#ifdef CONFIG_SME #ifdef CONFIG_SME
if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FT) { 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); ft_completed = wpa_ft_is_completed(wpa_s->wpa);
if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0) if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0)
return; 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) { if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
wpa_dbg(wpa_s, MSG_ERROR, "Failed to get BSSID"); wpa_dbg(wpa_s, MSG_ERROR, "Failed to get BSSID");