Parse EAPOL-Key msg 2/4 Key Data IEs/KDEs before checking RSN/WPA IE
[libeap.git] / src / ap / wpa_auth.c
index fd6dba5..105f141 100644 (file)
@@ -14,8 +14,6 @@
 
 #include "utils/includes.h"
 
-#ifndef CONFIG_NATIVE_WINDOWS
-
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "utils/state_machine.h"
@@ -611,6 +609,9 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
               SMK_M1, SMK_M3, SMK_ERROR } msg;
        char *msgtxt;
        struct wpa_eapol_ie_parse kde;
+       int ft;
+       const u8 *eapol_key_ie;
+       size_t eapol_key_ie_len;
 
        if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
                return;
@@ -741,9 +742,26 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                                         sm->wpa_ptk_state);
                        return;
                }
+               if (wpa_parse_kde_ies((u8 *) (key + 1), key_data_length,
+                                     &kde) < 0) {
+                       wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+                                        "received EAPOL-Key msg 2/4 with "
+                                        "invalid Key Data contents");
+                       return;
+               }
+               if (kde.rsn_ie) {
+                       eapol_key_ie = kde.rsn_ie;
+                       eapol_key_ie_len = kde.rsn_ie_len;
+               } else {
+                       eapol_key_ie = kde.wpa_ie;
+                       eapol_key_ie_len = kde.wpa_ie_len;
+               }
+               ft = sm->wpa == WPA_VERSION_WPA2 &&
+                       wpa_key_mgmt_ft(sm->wpa_key_mgmt);
                if (sm->wpa_ie == NULL ||
-                   sm->wpa_ie_len != key_data_length ||
-                   os_memcmp(sm->wpa_ie, key + 1, key_data_length) != 0) {
+                   wpa_compare_rsn_ie(ft,
+                                      sm->wpa_ie, sm->wpa_ie_len,
+                                      eapol_key_ie, eapol_key_ie_len)) {
                        wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
                                        "WPA IE from (Re)AssocReq did not "
                                        "match with msg 2/4");
@@ -752,11 +770,27 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                                            sm->wpa_ie, sm->wpa_ie_len);
                        }
                        wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4",
-                                   (u8 *) (key + 1), key_data_length);
+                                   eapol_key_ie, eapol_key_ie_len);
                        /* MLME-DEAUTHENTICATE.request */
                        wpa_sta_disconnect(wpa_auth, sm->addr);
                        return;
                }
+#ifdef CONFIG_IEEE80211R
+               if (ft) {
+                       struct wpa_ie_data ie;
+                       if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len,
+                                                &ie) < 0 ||
+                           ie.num_pmkid != 1 || ie.pmkid == NULL) {
+                               wpa_printf(MSG_DEBUG, "FT: No PMKR1Name in "
+                                          "FT 4-way handshake message 2/4");
+                               wpa_sta_disconnect(wpa_auth, sm->addr);
+                               return;
+                       }
+                       os_memcpy(sm->sup_pmk_r1_name, ie.pmkid, PMKID_LEN);
+                       wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Supplicant",
+                                   sm->sup_pmk_r1_name, PMKID_LEN);
+               }
+#endif /* CONFIG_IEEE80211R */
                break;
        case PAIRWISE_4:
                if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING ||
@@ -1210,6 +1244,10 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event)
                break;
        case WPA_ASSOC_FT:
 #ifdef CONFIG_IEEE80211R
+               wpa_printf(MSG_DEBUG, "FT: Retry PTK configuration "
+                          "after association");
+               wpa_ft_install_ptk(sm);
+
                /* Using FT protocol, not WPA auth state machine */
                sm->ft_completed = 1;
                return 0;
@@ -1485,6 +1523,27 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
                return;
        }
 
+#ifdef CONFIG_IEEE80211R
+       if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+               /*
+                * Verify that PMKR1Name from EAPOL-Key message 2/4 matches
+                * with the value we derived.
+                */
+               if (os_memcmp(sm->sup_pmk_r1_name, sm->pmk_r1_name,
+                             WPA_PMK_NAME_LEN) != 0) {
+                       wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+                                       "PMKR1Name mismatch in FT 4-way "
+                                       "handshake");
+                       wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from "
+                                   "Supplicant",
+                                   sm->sup_pmk_r1_name, WPA_PMK_NAME_LEN);
+                       wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
+                                   sm->pmk_r1_name, WPA_PMK_NAME_LEN);
+                       return;
+               }
+       }
+#endif /* CONFIG_IEEE80211R */
+
        eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
 
        if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
@@ -1530,7 +1589,8 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
 
        igtk.keyid[0] = gsm->GN_igtk;
        igtk.keyid[1] = 0;
-       if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, igtk.pn) < 0)
+       if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE ||
+           wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, igtk.pn) < 0)
                os_memset(igtk.pn, 0, sizeof(igtk.pn));
        os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN);
        pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK,
@@ -1608,6 +1668,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
        kde_len = wpa_ie_len + ieee80211w_kde_len(sm);
        if (gtk)
                kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
+#ifdef CONFIG_IEEE80211R
+       if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
+               kde_len += 2 + PMKID_LEN;
+#endif /* CONFIG_IEEE80211R */
        kde = os_malloc(kde_len);
        if (kde == NULL)
                return;
@@ -1615,6 +1679,18 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
        pos = kde;
        os_memcpy(pos, wpa_ie, wpa_ie_len);
        pos += wpa_ie_len;
+#ifdef CONFIG_IEEE80211R
+       if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+               int res = wpa_insert_pmkid(kde, pos - kde, sm->pmk_r1_name);
+               if (res < 0) {
+                       wpa_printf(MSG_ERROR, "FT: Failed to insert "
+                                  "PMKR1Name into RSN IE in EAPOL-Key data");
+                       os_free(kde);
+                       return;
+               }
+               pos += res;
+       }
+#endif /* CONFIG_IEEE80211R */
        if (gtk) {
                u8 hdr[2];
                hdr[0] = keyidx & 0x03;
@@ -2474,5 +2550,3 @@ int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id)
        sm->group = group;
        return 0;
 }
-
-#endif /* CONFIG_NATIVE_WINDOWS */