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;
|
||||
}
|
||||
#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 */
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
};
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Reference in a new issue