]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
Configure received BIGTK on station/supplicant side
authorJouni Malinen <jouni@codeaurora.org>
Mon, 17 Feb 2020 22:06:26 +0000 (00:06 +0200)
committerJouni Malinen <j@w1.fi>
Mon, 17 Feb 2020 22:18:47 +0000 (00:18 +0200)
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
src/rsn_supp/wpa.c
src/rsn_supp/wpa_ft.c
src/rsn_supp/wpa_i.h
wpa_supplicant/wnm_sta.c

index 981602c5d7571996101bfbc5a275c5bcf69759cc..1e209ada59207fbc2b451826b1856e4ea6cedf6c 100644 (file)
@@ -1140,14 +1140,66 @@ static int wpa_supplicant_install_igtk(struct wpa_sm *sm,
 }
 
 
+static int wpa_supplicant_install_bigtk(struct wpa_sm *sm,
+                                      const struct wpa_bigtk_kde *bigtk,
+                                      int wnm_sleep)
+{
+       size_t len = wpa_cipher_key_len(sm->mgmt_group_cipher);
+       u16 keyidx = WPA_GET_LE16(bigtk->keyid);
+
+       /* Detect possible key reinstallation */
+       if ((sm->bigtk.bigtk_len == len &&
+            os_memcmp(sm->bigtk.bigtk, bigtk->bigtk,
+                      sm->bigtk.bigtk_len) == 0) ||
+           (sm->bigtk_wnm_sleep.bigtk_len == len &&
+            os_memcmp(sm->bigtk_wnm_sleep.bigtk, bigtk->bigtk,
+                      sm->bigtk_wnm_sleep.bigtk_len) == 0)) {
+               wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+                       "WPA: Not reinstalling already in-use BIGTK to the driver (keyidx=%d)",
+                       keyidx);
+               return  0;
+       }
+
+       wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+               "WPA: BIGTK keyid %d pn " COMPACT_MACSTR,
+               keyidx, MAC2STR(bigtk->pn));
+       wpa_hexdump_key(MSG_DEBUG, "WPA: BIGTK", bigtk->bigtk, len);
+       if (keyidx < 6 || keyidx > 7) {
+               wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                       "WPA: Invalid BIGTK KeyID %d", keyidx);
+               return -1;
+       }
+       if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+                          broadcast_ether_addr,
+                          keyidx, 0, bigtk->pn, sizeof(bigtk->pn),
+                          bigtk->bigtk, len, KEY_FLAG_GROUP_RX) < 0) {
+               wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                       "WPA: Failed to configure BIGTK to the driver");
+               return -1;
+       }
+
+       if (wnm_sleep) {
+               sm->bigtk_wnm_sleep.bigtk_len = len;
+               os_memcpy(sm->bigtk_wnm_sleep.bigtk, bigtk->bigtk,
+                         sm->bigtk_wnm_sleep.bigtk_len);
+       } else {
+               sm->bigtk.bigtk_len = len;
+               os_memcpy(sm->bigtk.bigtk, bigtk->bigtk, sm->bigtk.bigtk_len);
+       }
+
+       return 0;
+}
+
+
 static int ieee80211w_set_keys(struct wpa_sm *sm,
                               struct wpa_eapol_ie_parse *ie)
 {
+       size_t len;
+
        if (!wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher))
                return 0;
 
        if (ie->igtk) {
-               size_t len;
                const struct wpa_igtk_kde *igtk;
 
                len = wpa_cipher_key_len(sm->mgmt_group_cipher);
@@ -1159,6 +1211,18 @@ static int ieee80211w_set_keys(struct wpa_sm *sm,
                        return -1;
        }
 
+       if (ie->bigtk && sm->beacon_prot) {
+               const struct wpa_bigtk_kde *bigtk;
+
+               len = wpa_cipher_key_len(sm->mgmt_group_cipher);
+               if (ie->bigtk_len != WPA_BIGTK_KDE_PREFIX_LEN + len)
+                       return -1;
+
+               bigtk = (const struct wpa_bigtk_kde *) ie->bigtk;
+               if (wpa_supplicant_install_bigtk(sm, bigtk, 0) < 0)
+                       return -1;
+       }
+
        return 0;
 }
 
@@ -3595,6 +3659,13 @@ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf)
                igtk = (const struct wpa_igtk_kde *) (buf + 2);
                if (wpa_supplicant_install_igtk(sm, igtk, 1) < 0)
                        return -1;
+       } else if (subelem_id == WNM_SLEEP_SUBELEM_BIGTK) {
+               const struct wpa_bigtk_kde *bigtk;
+
+               bigtk = (const struct wpa_bigtk_kde *) (buf + 2);
+               if (sm->beacon_prot &&
+                   wpa_supplicant_install_bigtk(sm, bigtk, 1) < 0)
+                       return -1;
        } else {
                wpa_printf(MSG_DEBUG, "Unknown element id");
                return -1;
index 8a8c545d38fa99ad9a1f39d8add80f445ba4179c..046bdfd169905f249ec86cb41d71e82b85bea6d3 100644 (file)
@@ -855,6 +855,74 @@ static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem,
 }
 
 
+static int wpa_ft_process_bigtk_subelem(struct wpa_sm *sm, const u8 *bigtk_elem,
+                                      size_t bigtk_elem_len)
+{
+       u8 bigtk[WPA_BIGTK_MAX_LEN];
+       size_t bigtk_len;
+       u16 keyidx;
+       const u8 *kek;
+       size_t kek_len;
+
+       if (!sm->beacon_prot || !bigtk_elem ||
+           (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC &&
+            sm->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_128 &&
+            sm->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_256 &&
+            sm->mgmt_group_cipher != WPA_CIPHER_BIP_CMAC_256))
+               return 0;
+
+       if (wpa_key_mgmt_fils(sm->key_mgmt)) {
+               kek = sm->ptk.kek2;
+               kek_len = sm->ptk.kek2_len;
+       } else {
+               kek = sm->ptk.kek;
+               kek_len = sm->ptk.kek_len;
+       }
+
+       wpa_hexdump_key(MSG_DEBUG, "FT: Received BIGTK in Reassoc Resp",
+                       bigtk_elem, bigtk_elem_len);
+
+       bigtk_len = wpa_cipher_key_len(sm->mgmt_group_cipher);
+       if (bigtk_elem_len != 2 + 6 + 1 + bigtk_len + 8) {
+               wpa_printf(MSG_DEBUG,
+                          "FT: Invalid BIGTK sub-elem length %lu",
+                          (unsigned long) bigtk_elem_len);
+               return -1;
+       }
+       if (bigtk_elem[8] != bigtk_len) {
+               wpa_printf(MSG_DEBUG,
+                          "FT: Invalid BIGTK sub-elem Key Length %d",
+                          bigtk_elem[8]);
+               return -1;
+       }
+
+       if (aes_unwrap(kek, kek_len, bigtk_len / 8, bigtk_elem + 9, bigtk)) {
+               wpa_printf(MSG_WARNING,
+                          "FT: AES unwrap failed - could not decrypt BIGTK");
+               return -1;
+       }
+
+       /* KeyID[2] | IPN[6] | Key Length[1] | Key[16+8] */
+
+       keyidx = WPA_GET_LE16(bigtk_elem);
+
+       wpa_hexdump_key(MSG_DEBUG, "FT: BIGTK from Reassoc Resp", bigtk,
+                       bigtk_len);
+       if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+                          broadcast_ether_addr, keyidx, 0,
+                          bigtk_elem + 2, 6, bigtk, bigtk_len,
+                          KEY_FLAG_GROUP_RX) < 0) {
+               wpa_printf(MSG_WARNING,
+                          "WPA: Failed to set BIGTK to the driver");
+               forced_memzero(bigtk, sizeof(bigtk));
+               return -1;
+       }
+       forced_memzero(bigtk, sizeof(bigtk));
+
+       return 0;
+}
+
+
 int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
                                 size_t ies_len, const u8 *src_addr)
 {
@@ -1036,10 +1104,9 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
 
        sm->ft_reassoc_completed = 1;
 
-       if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0)
-               return -1;
-
-       if (wpa_ft_process_igtk_subelem(sm, parse.igtk, parse.igtk_len) < 0)
+       if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0 ||
+           wpa_ft_process_igtk_subelem(sm, parse.igtk, parse.igtk_len) < 0 ||
+           wpa_ft_process_bigtk_subelem(sm, parse.bigtk, parse.bigtk_len) < 0)
                return -1;
 
        if (sm->set_ptk_after_assoc) {
index 164adfbc0dc6b7eb3324b04df18d759c0f2d70c0..bd44464023bfb5fd333bbe94a253c9735b5bfd6c 100644 (file)
@@ -33,6 +33,8 @@ struct wpa_sm {
        struct wpa_gtk gtk_wnm_sleep;
        struct wpa_igtk igtk;
        struct wpa_igtk igtk_wnm_sleep;
+       struct wpa_bigtk bigtk;
+       struct wpa_bigtk bigtk_wnm_sleep;
 
        struct eapol_sm *eapol; /* EAPOL state machine from upper level code */
 
index 270be9e2ece8e1d0ba421ebbfa65a540b2b8421b..06cb70fe55c3aab6d3ed32514eabfa87cbef00ed 100644 (file)
@@ -280,6 +280,15 @@ static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s,
                        wpa_wnmsleep_install_key(wpa_s->wpa,
                                                 WNM_SLEEP_SUBELEM_IGTK, ptr);
                        ptr += 10 + WPA_IGTK_LEN;
+               } else if (*ptr == WNM_SLEEP_SUBELEM_BIGTK) {
+                       if (ptr[1] < 2 + 6 + WPA_BIGTK_LEN) {
+                               wpa_printf(MSG_DEBUG,
+                                          "WNM: Too short BIGTK subelem");
+                               break;
+                       }
+                       wpa_wnmsleep_install_key(wpa_s->wpa,
+                                                WNM_SLEEP_SUBELEM_BIGTK, ptr);
+                       ptr += 10 + WPA_BIGTK_LEN;
                } else
                        break; /* skip the loop */
        }