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:
parent
e73ffa0925
commit
706df4291b
4 changed files with 202 additions and 0 deletions
|
@ -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 */
|
||||||
|
}
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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 */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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");
|
||||||
|
|
Loading…
Reference in a new issue