]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
FILS: Association Response processing (STA)
authorJouni Malinen <jouni@qca.qualcomm.com>
Wed, 9 Sep 2015 14:34:13 +0000 (17:34 +0300)
committerJouni Malinen <j@w1.fi>
Tue, 25 Oct 2016 21:20:04 +0000 (00:20 +0300)
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>
src/rsn_supp/wpa.c
src/rsn_supp/wpa.h
src/rsn_supp/wpa_i.h
wpa_supplicant/events.c

index 136fd091e7b90256ba2db5928ab0e980711fc3b8..65e257a939aceafc67b032c85f4cd1dd9610f81c 100644 (file)
@@ -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 */
+}
index 3b2dbcf43746f480ba91229c80789e7102a547f2..20d5b8e8482b13c77a85c74831879a47cb50ebbe 100644 (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,
                                     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 */
index 45000687a97a730920150c9fa9bdc83602b53248..44d5424d7024cf9b7e23e28b7affca50beea1ea1 100644 (file)
@@ -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 */
 };
 
index e15109c9b16605c425c8aa692b4ebaa17b9e5d69..99e9196706919f4bcbcf0d2e9d4a66adbb42977b 100644 (file)
@@ -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");