]> git.ipfire.org Git - thirdparty/hostap.git/blobdiff - src/ap/wpa_auth.c
wlantest: Update STA State based on broadcast Deauth/Disassoc
[thirdparty/hostap.git] / src / ap / wpa_auth.c
index 12290dd2bc2fcb84dfd3bf9998d1c744b1fadf91..dc9fef1daced02f1496c73afe80a4fede30619db 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"
@@ -222,6 +220,8 @@ static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx)
                           "initialization.");
        } else {
                wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "GMK rekeyd");
+               wpa_hexdump_key(MSG_DEBUG, "GMK",
+                               wpa_auth->group->GMK, WPA_GMK_LEN);
        }
 
        if (wpa_auth->conf.wpa_gmk_rekey) {
@@ -327,9 +327,12 @@ static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth,
                os_free(group);
                return NULL;
        }
+       wpa_hexdump_key(MSG_DEBUG, "GMK", group->GMK, WPA_GMK_LEN);
 
        sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf),
                 group->Counter, WPA_NONCE_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "Key Counter",
+                       group->Counter, WPA_NONCE_LEN);
 
        group->GInit = TRUE;
        wpa_group_sm_step(wpa_auth, group);
@@ -541,6 +544,9 @@ void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm)
 
 static void wpa_free_sta_sm(struct wpa_state_machine *sm)
 {
+#ifdef CONFIG_IEEE80211R
+       os_free(sm->assoc_resp_ftie);
+#endif /* CONFIG_IEEE80211R */
        os_free(sm->last_rx_eapol_key);
        os_free(sm->wpa_ie);
        os_free(sm);
@@ -600,6 +606,56 @@ static int wpa_replay_counter_valid(struct wpa_state_machine *sm,
 }
 
 
+#ifdef CONFIG_IEEE80211R
+static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth,
+                              struct wpa_state_machine *sm,
+                              struct wpa_eapol_ie_parse *kde)
+{
+       struct wpa_ie_data ie;
+       struct rsn_mdie *mdie;
+
+       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");
+               return -1;
+       }
+
+       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);
+
+       if (!kde->mdie || !kde->ftie) {
+               wpa_printf(MSG_DEBUG, "FT: No %s in FT 4-way handshake "
+                          "message 2/4", kde->mdie ? "FTIE" : "MDIE");
+               return -1;
+       }
+
+       mdie = (struct rsn_mdie *) (kde->mdie + 2);
+       if (kde->mdie[1] < sizeof(struct rsn_mdie) ||
+           os_memcmp(wpa_auth->conf.mobility_domain, mdie->mobility_domain,
+                     MOBILITY_DOMAIN_ID_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "FT: MDIE mismatch");
+               return -1;
+       }
+
+       if (sm->assoc_resp_ftie &&
+           (kde->ftie[1] != sm->assoc_resp_ftie[1] ||
+            os_memcmp(kde->ftie, sm->assoc_resp_ftie,
+                      2 + sm->assoc_resp_ftie[1]) != 0)) {
+               wpa_printf(MSG_DEBUG, "FT: FTIE mismatch");
+               wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 2/4",
+                           kde->ftie, kde->ftie_len);
+               wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)AssocResp",
+                           sm->assoc_resp_ftie, 2 + sm->assoc_resp_ftie[1]);
+               return -1;
+       }
+
+       return 0;
+}
+#endif /* CONFIG_IEEE80211R */
+
+
 void wpa_receive(struct wpa_authenticator *wpa_auth,
                 struct wpa_state_machine *sm,
                 u8 *data, size_t data_len)
@@ -611,6 +667,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 +800,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 +828,17 @@ 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 && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
+                       wpa_sta_disconnect(wpa_auth, sm->addr);
+                       return;
+               }
+#endif /* CONFIG_IEEE80211R */
                break;
        case PAIRWISE_4:
                if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING ||
@@ -918,25 +1000,37 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
 }
 
 
-static void wpa_gmk_to_gtk(const u8 *gmk, const u8 *addr, const u8 *gnonce,
-                          u8 *gtk, size_t gtk_len)
+static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr,
+                         const u8 *gnonce, u8 *gtk, size_t gtk_len)
 {
-       u8 data[ETH_ALEN + WPA_NONCE_LEN];
+       u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + 16];
+       u8 *pos;
+       int ret = 0;
 
-       /* GTK = PRF-X(GMK, "Group key expansion", AA || GNonce) */
+       /* GTK = PRF-X(GMK, "Group key expansion",
+        *      AA || GNonce || Time || random data)
+        * The example described in the IEEE 802.11 standard uses only AA and
+        * GNonce as inputs here. Add some more entropy since this derivation
+        * is done only at the Authenticator and as such, does not need to be
+        * exactly same.
+        */
        os_memcpy(data, addr, ETH_ALEN);
        os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN);
+       pos = data + ETH_ALEN + WPA_NONCE_LEN;
+       wpa_get_ntp_timestamp(pos);
+       pos += 8;
+       if (os_get_random(pos, 16) < 0)
+               ret = -1;
 
 #ifdef CONFIG_IEEE80211W
-       sha256_prf(gmk, WPA_GMK_LEN, "Group key expansion",
-                  data, sizeof(data), gtk, gtk_len);
+       sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len);
 #else /* CONFIG_IEEE80211W */
-       sha1_prf(gmk, WPA_GMK_LEN, "Group key expansion",
-                data, sizeof(data), gtk, gtk_len);
+       if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len)
+           < 0)
+               ret = -1;
 #endif /* CONFIG_IEEE80211W */
 
-       wpa_hexdump_key(MSG_DEBUG, "GMK", gmk, WPA_GMK_LEN);
-       wpa_hexdump_key(MSG_DEBUG, "GTK", gtk, gtk_len);
+       return ret;
 }
 
 
@@ -1197,6 +1291,24 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event)
                break;
        case WPA_REAUTH:
        case WPA_REAUTH_EAPOL:
+               if (!sm->started) {
+                       /*
+                        * When using WPS, we may end up here if the STA
+                        * manages to re-associate without the previous STA
+                        * entry getting removed. Consequently, we need to make
+                        * sure that the WPA state machines gets initialized
+                        * properly at this point.
+                        */
+                       wpa_printf(MSG_DEBUG, "WPA state machine had not been "
+                                  "started - initialize now");
+                       sm->started = 1;
+                       sm->Init = TRUE;
+                       if (wpa_sm_step(sm) == 1)
+                               return 1; /* should not really happen */
+                       sm->Init = FALSE;
+                       sm->AuthenticationRequest = TRUE;
+                       break;
+               }
                if (sm->GUpdateStationKeys) {
                        /*
                         * Reauthentication cancels the pending group key
@@ -1210,11 +1322,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) {
-                       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;
@@ -1490,6 +1601,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)) {
@@ -1579,10 +1711,12 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
                return;
        }
 
-       /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, GTK[GN])
+       /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
+          GTK[GN], IGTK, [FTIE], [TIE * 2])
         */
        os_memset(rsc, 0, WPA_KEY_RSC_LEN);
        wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc);
+       /* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */
        wpa_ie = sm->wpa_auth->wpa_ie;
        wpa_ie_len = sm->wpa_auth->wpa_ie_len;
        if (sm->wpa == WPA_VERSION_WPA &&
@@ -1614,6 +1748,12 @@ 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; /* PMKR1Name into RSN IE */
+               kde_len += 300; /* FTIE + 2 * TIE */
+       }
+#endif /* CONFIG_IEEE80211R */
        kde = os_malloc(kde_len);
        if (kde == NULL)
                return;
@@ -1621,6 +1761,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;
@@ -1630,6 +1782,40 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
        }
        pos = ieee80211w_kde_add(sm, pos);
 
+#ifdef CONFIG_IEEE80211R
+       if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+               int res;
+               struct wpa_auth_config *conf;
+
+               conf = &sm->wpa_auth->conf;
+               res = wpa_write_ftie(conf, conf->r0_key_holder,
+                                    conf->r0_key_holder_len,
+                                    NULL, NULL, pos, kde + kde_len - pos,
+                                    NULL, 0);
+               if (res < 0) {
+                       wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE "
+                                  "into EAPOL-Key Key Data");
+                       os_free(kde);
+                       return;
+               }
+               pos += res;
+
+               /* TIE[ReassociationDeadline] (TU) */
+               *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+               *pos++ = 5;
+               *pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE;
+               WPA_PUT_LE32(pos, conf->reassociation_deadline);
+               pos += 4;
+
+               /* TIE[KeyLifetime] (seconds) */
+               *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+               *pos++ = 5;
+               *pos++ = WLAN_TIMEOUT_KEY_LIFETIME;
+               WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60);
+               pos += 4;
+       }
+#endif /* CONFIG_IEEE80211R */
+
        wpa_send_eapol(sm->wpa_auth, sm,
                       (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC |
                       WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
@@ -1925,20 +2111,24 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
 {
        int ret = 0;
 
-       /* FIX: is this the correct way of getting GNonce? */
        os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
        inc_byte_array(group->Counter, WPA_NONCE_LEN);
-       wpa_gmk_to_gtk(group->GMK, wpa_auth->addr, group->GNonce,
-                      group->GTK[group->GN - 1], group->GTK_len);
+       if (wpa_gmk_to_gtk(group->GMK, "Group key expansion",
+                          wpa_auth->addr, group->GNonce,
+                          group->GTK[group->GN - 1], group->GTK_len) < 0)
+               ret = -1;
+       wpa_hexdump_key(MSG_DEBUG, "GTK",
+                       group->GTK[group->GN - 1], group->GTK_len);
 
 #ifdef CONFIG_IEEE80211W
        if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
-               if (os_get_random(group->IGTK[group->GN_igtk - 4],
-                                 WPA_IGTK_LEN) < 0) {
-                       wpa_printf(MSG_INFO, "RSN: Failed to get new random "
-                                  "IGTK");
+               os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
+               inc_byte_array(group->Counter, WPA_NONCE_LEN);
+               if (wpa_gmk_to_gtk(group->GMK, "IGTK key expansion",
+                                  wpa_auth->addr, group->GNonce,
+                                  group->IGTK[group->GN_igtk - 4],
+                                  WPA_IGTK_LEN) < 0)
                        ret = -1;
-               }
                wpa_hexdump_key(MSG_DEBUG, "IGTK",
                                group->IGTK[group->GN_igtk - 4], WPA_IGTK_LEN);
        }
@@ -2022,26 +2212,31 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
 }
 
 
-static void wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth,
-                                 struct wpa_group *group)
+static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth,
+                                struct wpa_group *group)
 {
+       int ret = 0;
+
        wpa_printf(MSG_DEBUG, "WPA: group state machine entering state "
                   "SETKEYSDONE (VLAN-ID %d)", group->vlan_id);
        group->changed = TRUE;
        group->wpa_group_state = WPA_GROUP_SETKEYSDONE;
-       wpa_auth_set_key(wpa_auth, group->vlan_id,
-                        wpa_alg_enum(wpa_auth->conf.wpa_group),
-                        NULL, group->GN, group->GTK[group->GN - 1],
-                        group->GTK_len);
+       if (wpa_auth_set_key(wpa_auth, group->vlan_id,
+                            wpa_alg_enum(wpa_auth->conf.wpa_group),
+                            NULL, group->GN, group->GTK[group->GN - 1],
+                            group->GTK_len) < 0)
+               ret = -1;
 
 #ifdef CONFIG_IEEE80211W
-       if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
-               wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK,
-                                NULL, group->GN_igtk,
-                                group->IGTK[group->GN_igtk - 4],
-                                WPA_IGTK_LEN);
-       }
+       if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION &&
+           wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK,
+                            NULL, group->GN_igtk,
+                            group->IGTK[group->GN_igtk - 4],
+                            WPA_IGTK_LEN) < 0)
+               ret = -1;
 #endif /* CONFIG_IEEE80211W */
+
+       return ret;
 }
 
 
@@ -2480,5 +2675,3 @@ int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id)
        sm->group = group;
        return 0;
 }
-
-#endif /* CONFIG_NATIVE_WINDOWS */