]> git.ipfire.org Git - thirdparty/hostap.git/blobdiff - src/ap/wpa_auth.c
AP: Allow PTK rekeying without Ext KeyID to be disabled as a workaround
[thirdparty/hostap.git] / src / ap / wpa_auth.c
index 423528d128e338e511a3b0a7917a4336e409f02e..e67c34498815a3a287ffcf5231d5a3a8020fa1cc 100644 (file)
@@ -56,13 +56,14 @@ static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
                                       struct wpa_group *group);
 static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
                          const u8 *pmk, unsigned int pmk_len,
-                         struct wpa_ptk *ptk);
+                         struct wpa_ptk *ptk, int force_sha256);
 static void wpa_group_free(struct wpa_authenticator *wpa_auth,
                           struct wpa_group *group);
 static void wpa_group_get(struct wpa_authenticator *wpa_auth,
                          struct wpa_group *group);
 static void wpa_group_put(struct wpa_authenticator *wpa_auth,
                          struct wpa_group *group);
+static int ieee80211w_kde_len(struct wpa_state_machine *sm);
 static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos);
 
 static const u32 eapol_key_timeout_first = 100; /* ms */
@@ -780,8 +781,18 @@ static void wpa_request_new_ptk(struct wpa_state_machine *sm)
        if (sm == NULL)
                return;
 
-       sm->PTKRequest = TRUE;
-       sm->PTK_valid = 0;
+       if (sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
+               wpa_printf(MSG_INFO,
+                          "WPA: PTK0 rekey not allowed, disconnect " MACSTR,
+                          MAC2STR(sm->addr));
+               sm->Disconnect = TRUE;
+               /* Try to encourage the STA to reconnect */
+               sm->disconnect_reason =
+                       WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA;
+       } else {
+               sm->PTKRequest = TRUE;
+               sm->PTK_valid = 0;
+       }
 }
 
 
@@ -926,7 +937,8 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
                        pmk_len = sm->pmk_len;
                }
 
-               if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK) < 0)
+               if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK, 0) <
+                   0)
                        break;
 
                if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
@@ -1800,6 +1812,15 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
                        sm->Init = FALSE;
                        sm->AuthenticationRequest = TRUE;
                        break;
+               } else if (sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
+                       wpa_printf(MSG_INFO,
+                                  "WPA: PTK0 rekey not allowed, disconnect "
+                                  MACSTR, MAC2STR(sm->addr));
+                       sm->Disconnect = TRUE;
+                       /* Try to encourage the STA reconnect */
+                       sm->disconnect_reason =
+                               WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA;
+                       break;
                }
                if (sm->GUpdateStationKeys) {
                        /*
@@ -2233,10 +2254,11 @@ SM_STATE(WPA_PTK, PTKSTART)
 
 static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
                          const u8 *pmk, unsigned int pmk_len,
-                         struct wpa_ptk *ptk)
+                         struct wpa_ptk *ptk, int force_sha256)
 {
        const u8 *z = NULL;
        size_t z_len = 0;
+       int akmp;
 
 #ifdef CONFIG_IEEE80211R_AP
        if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
@@ -2262,9 +2284,12 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
        }
 #endif /* CONFIG_DPP2 */
 
+       akmp = sm->wpa_key_mgmt;
+       if (force_sha256)
+               akmp = WPA_KEY_MGMT_PSK_SHA256;
        return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion",
                              sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce,
-                             ptk, sm->wpa_key_mgmt, sm->pairwise, z, z_len);
+                             ptk, akmp, sm->pairwise, z, z_len);
 }
 
 
@@ -2674,7 +2699,7 @@ static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
        size_t gtk_len;
        struct wpa_group *gsm;
 
-       plain = wpabuf_alloc(1000);
+       plain = wpabuf_alloc(1000 + ieee80211w_kde_len(sm));
        if (!plain)
                return NULL;
 
@@ -2722,7 +2747,7 @@ static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
                           gtk, gtk_len);
        wpabuf_put(plain, tmp2 - tmp);
 
-       /* IGTK KDE */
+       /* IGTK KDE and BIGTK KDE */
        tmp = wpabuf_put(plain, 0);
        tmp2 = ieee80211w_kde_add(sm, tmp);
        wpabuf_put(plain, tmp2 - tmp);
@@ -2844,6 +2869,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
        struct wpa_eapol_key *key;
        struct wpa_eapol_ie_parse kde;
        int vlan_id = 0;
+       int owe_ptk_workaround = !!wpa_auth->conf.owe_ptk_workaround;
 
        SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
        sm->EAPOLKeyReceived = FALSE;
@@ -2881,7 +2907,8 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
                        pmk_len = sm->pmksa->pmk_len;
                }
 
-               if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK) < 0)
+               if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK,
+                                  owe_ptk_workaround == 2) < 0)
                        break;
 
                if (mic_len &&
@@ -2905,6 +2932,16 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
                }
 #endif /* CONFIG_FILS */
 
+#ifdef CONFIG_OWE
+               if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && pmk_len > 32 &&
+                   owe_ptk_workaround == 1) {
+                       wpa_printf(MSG_DEBUG,
+                                  "OWE: Try PTK derivation workaround with SHA256");
+                       owe_ptk_workaround = 2;
+                       continue;
+               }
+#endif /* CONFIG_OWE */
+
                if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
                    wpa_key_mgmt_sae(sm->wpa_key_mgmt))
                        break;
@@ -3088,19 +3125,25 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2)
 
 static int ieee80211w_kde_len(struct wpa_state_machine *sm)
 {
+       size_t len = 0;
+
        if (sm->mgmt_frame_prot) {
-               size_t len;
-               len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
-               return 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN + len;
+               len += 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN;
+               len += wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+       }
+       if (sm->mgmt_frame_prot && sm->wpa_auth->conf.beacon_prot) {
+               len += 2 + RSN_SELECTOR_LEN + WPA_BIGTK_KDE_PREFIX_LEN;
+               len += wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
        }
 
-       return 0;
+       return len;
 }
 
 
 static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
 {
        struct wpa_igtk_kde igtk;
+       struct wpa_bigtk_kde bigtk;
        struct wpa_group *gsm = sm->group;
        u8 rsc[WPA_KEY_RSC_LEN];
        size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
@@ -3129,6 +3172,21 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
                          (const u8 *) &igtk, WPA_IGTK_KDE_PREFIX_LEN + len,
                          NULL, 0);
 
+       if (!sm->wpa_auth->conf.beacon_prot)
+               return pos;
+
+       bigtk.keyid[0] = gsm->GN_bigtk;
+       bigtk.keyid[1] = 0;
+       if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE ||
+           wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, rsc) < 0)
+               os_memset(bigtk.pn, 0, sizeof(bigtk.pn));
+       else
+               os_memcpy(bigtk.pn, rsc, sizeof(bigtk.pn));
+       os_memcpy(bigtk.bigtk, gsm->BIGTK[gsm->GN_bigtk - 6], len);
+       pos = wpa_add_kde(pos, RSN_KEY_DATA_BIGTK,
+                         (const u8 *) &bigtk, WPA_BIGTK_KDE_PREFIX_LEN + len,
+                         NULL, 0);
+
        return pos;
 }
 
@@ -3188,7 +3246,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
        }
 
        /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
-          GTK[GN], IGTK, [FTIE], [TIE * 2])
+          GTK[GN], IGTK, [BIGTK], [FTIE], [TIE * 2])
         */
        os_memset(rsc, 0, WPA_KEY_RSC_LEN);
        wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc);
@@ -3788,6 +3846,7 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
                          struct wpa_group *group)
 {
        int ret = 0;
+       size_t len;
 
        os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
        inc_byte_array(group->Counter, WPA_NONCE_LEN);
@@ -3799,7 +3858,6 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
                        group->GTK[group->GN - 1], group->GTK_len);
 
        if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
-               size_t len;
                len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
                os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
                inc_byte_array(group->Counter, WPA_NONCE_LEN);
@@ -3811,6 +3869,19 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
                                group->IGTK[group->GN_igtk - 4], len);
        }
 
+       if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION &&
+           wpa_auth->conf.beacon_prot) {
+               len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
+               os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
+               inc_byte_array(group->Counter, WPA_NONCE_LEN);
+               if (wpa_gmk_to_gtk(group->GMK, "BIGTK key expansion",
+                                  wpa_auth->addr, group->GNonce,
+                                  group->BIGTK[group->GN_bigtk - 6], len) < 0)
+                       ret = -1;
+               wpa_hexdump_key(MSG_DEBUG, "BIGTK",
+                               group->BIGTK[group->GN_bigtk - 6], len);
+       }
+
        return ret;
 }
 
@@ -3829,6 +3900,8 @@ static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth,
        group->GM = 2;
        group->GN_igtk = 4;
        group->GM_igtk = 5;
+       group->GN_bigtk = 6;
+       group->GM_bigtk = 7;
        /* GTK[GN] = CalcGTK() */
        wpa_gtk_update(wpa_auth, group);
 }
@@ -3946,6 +4019,36 @@ int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos)
        return pos - start;
 }
 
+
+int wpa_wnmsleep_bigtk_subelem(struct wpa_state_machine *sm, u8 *pos)
+{
+       struct wpa_group *gsm = sm->group;
+       u8 *start = pos;
+       size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+
+       /*
+        * BIGTK subelement:
+        * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16]
+        */
+       *pos++ = WNM_SLEEP_SUBELEM_BIGTK;
+       *pos++ = 2 + 6 + len;
+       WPA_PUT_LE16(pos, gsm->GN_bigtk);
+       pos += 2;
+       if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, pos) != 0)
+               return 0;
+       pos += 6;
+
+       os_memcpy(pos, gsm->BIGTK[gsm->GN_bigtk - 6], len);
+       pos += len;
+
+       wpa_printf(MSG_DEBUG, "WNM: BIGTK Key ID %u in WNM-Sleep Mode exit",
+                  gsm->GN_bigtk);
+       wpa_hexdump_key(MSG_DEBUG, "WNM: BIGTK in WNM-Sleep Mode exit",
+                       gsm->IGTK[gsm->GN_bigtk - 6], len);
+
+       return pos - start;
+}
+
 #endif /* CONFIG_WNM_AP */
 
 
@@ -3965,6 +4068,9 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
        tmp = group->GM_igtk;
        group->GM_igtk = group->GN_igtk;
        group->GN_igtk = tmp;
+       tmp = group->GM_bigtk;
+       group->GM_bigtk = group->GN_bigtk;
+       group->GN_bigtk = tmp;
        /* "GKeyDoneStations = GNoStations" is done in more robust way by
         * counting the STAs that are marked with GUpdateStationKeys instead of
         * including all STAs that could be in not-yet-completed state. */
@@ -4007,6 +4113,13 @@ static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
                                     group->IGTK[group->GN_igtk - 4], len,
                                     KEY_FLAG_GROUP_TX_DEFAULT) < 0)
                        ret = -1;
+
+               if (ret == 0 && wpa_auth->conf.beacon_prot &&
+                   wpa_auth_set_key(wpa_auth, group->vlan_id, alg,
+                                    broadcast_ether_addr, group->GN_bigtk,
+                                    group->BIGTK[group->GN_bigtk - 6], len,
+                                    KEY_FLAG_GROUP_TX_DEFAULT) < 0)
+                       ret = -1;
        }
 
        return ret;
@@ -4148,6 +4261,9 @@ void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth)
                tmp = group->GM_igtk;
                group->GM_igtk = group->GN_igtk;
                group->GN_igtk = tmp;
+               tmp = group->GM_bigtk;
+               group->GM_bigtk = group->GN_bigtk;
+               group->GN_bigtk = tmp;
                wpa_gtk_update(wpa_auth, group);
                wpa_group_config_group_keys(wpa_auth, group);
        }
@@ -5029,7 +5145,7 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm,
        int wpa_ie_len, secure, gtkidx, encr = 0;
 
        /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
-          GTK[GN], IGTK, [FTIE], [TIE * 2])
+          GTK[GN], IGTK, [BIGTK], [FTIE], [TIE * 2])
         */
 
        /* Use 0 RSC */