Parse EAPOL-Key msg 2/4 Key Data IEs/KDEs before checking RSN/WPA IE
[libeap.git] / src / ap / wpa_auth.c
index 2299c0e..105f141 100644 (file)
@@ -609,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;
@@ -739,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");
@@ -750,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 ||
@@ -1208,11 +1244,10 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event)
                break;
        case WPA_ASSOC_FT:
 #ifdef CONFIG_IEEE80211R
-               if (!sm->pairwise_set || sm->ft_over_ds) {
-                       wpa_printf(MSG_DEBUG, "FT: Retry PTK configuration "
-                                  "after association");
-                       wpa_ft_install_ptk(sm);
-               }
+               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;
@@ -1488,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)) {
@@ -1612,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;
@@ -1619,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;