]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
wlantest: TKIP frame reassembly for Michael MIC check in fragmented case
authorJouni Malinen <jouni@codeaurora.org>
Mon, 8 Feb 2021 16:38:39 +0000 (18:38 +0200)
committerJouni Malinen <j@w1.fi>
Tue, 11 May 2021 18:13:56 +0000 (21:13 +0300)
Reassemble the full MSDU when processing TKIP protected fragmented
frames so that the Michael MIC can be validated once the last fragment
has been received.

Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
wlantest/rx_data.c
wlantest/test_vectors.c
wlantest/tkip.c
wlantest/wlantest.c
wlantest/wlantest.h

index 9e84b8a8e9ce4d53816903982d8c216b1faf2346..16e0f53bc22f3f0493680c7fbd3e7e5af529cad8 100644 (file)
@@ -177,7 +177,8 @@ static u8 * try_ptk(struct wlantest *wt, int pairwise_cipher,
                enum michael_mic_result mic_res;
 
                decrypted = tkip_decrypt(ptk->tk, hdr, data, data_len,
-                                        decrypted_len, &mic_res);
+                                        decrypted_len, &mic_res,
+                                        &wt->tkip_frag);
                if (decrypted && mic_res == MICHAEL_MIC_INCORRECT)
                        add_note(wt, MSG_INFO, "Invalid Michael MIC");
                else if (decrypted && mic_res == MICHAEL_MIC_NOT_VERIFIED)
@@ -328,7 +329,7 @@ skip_replay_det:
                enum michael_mic_result mic_res;
 
                decrypted = tkip_decrypt(bss->gtk[keyid], hdr, data, len,
-                                        &dlen, &mic_res);
+                                        &dlen, &mic_res, &wt->tkip_frag);
                if (decrypted && mic_res == MICHAEL_MIC_INCORRECT)
                        add_note(wt, MSG_INFO, "Invalid Michael MIC");
                else if (decrypted && mic_res == MICHAEL_MIC_NOT_VERIFIED)
@@ -619,7 +620,7 @@ skip_replay_det:
                enum michael_mic_result mic_res;
 
                decrypted = tkip_decrypt(sta->ptk.tk, hdr, data, len, &dlen,
-                                        &mic_res);
+                                        &mic_res, &wt->tkip_frag);
                if (decrypted && mic_res == MICHAEL_MIC_INCORRECT)
                        add_note(wt, MSG_INFO, "Invalid Michael MIC");
                else if (decrypted && mic_res == MICHAEL_MIC_NOT_VERIFIED)
index ca0449d318c50bccc93f89ce828d763e8d3d74ba..7f39c426433b8310a2807196403109f9a850b9fc 100644 (file)
@@ -63,7 +63,7 @@ static void test_vector_tkip(void)
 
        wpa_debug_level = MSG_INFO;
        plain = tkip_decrypt(tk, (const struct ieee80211_hdr *) enc,
-                            enc + 24, enc_len - 24, &plain_len, NULL);
+                            enc + 24, enc_len - 24, &plain_len, NULL, NULL);
        wpa_debug_level = MSG_EXCESSIVE;
        os_free(enc);
 
index 613a08aa770a615fdbf23e2668b2191ada47cfc9..843f6518a382de0b2c53fef91363a4067d3a1daf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Temporal Key Integrity Protocol (CCMP)
+ * Temporal Key Integrity Protocol (TKIP)
  * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
@@ -291,7 +291,7 @@ static void michael_mic_hdr(const struct ieee80211_hdr *hdr11, u8 *hdr)
 
 u8 * tkip_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
                  const u8 *data, size_t data_len, size_t *decrypted_len,
-                 enum michael_mic_result *mic_res)
+                 enum michael_mic_result *mic_res, struct tkip_frag *frag)
 {
        u16 iv16;
        u32 iv32;
@@ -304,7 +304,11 @@ u8 * tkip_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
        u8 michael_hdr[16];
        u8 mic[8];
        u16 fc = le_to_host16(hdr->frame_control);
+       const u8 *full_payload;
+       size_t full_payload_len;
        u16 sc = le_to_host16(hdr->seq_ctrl);
+       u16 sn;
+       u8 fn;
 
        if (data_len < 8 + 4)
                return NULL;
@@ -337,10 +341,49 @@ u8 * tkip_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
        }
        plain_len -= 4;
 
-       /* TODO: MSDU reassembly */
-       if ((fc & WLAN_FC_MOREFRAG) || WLAN_GET_SEQ_FRAG(sc) > 0) {
-               /* For now, return the decrypted fragment and do not check the
-                * Michael MIC value in the last fragment */
+       full_payload = plain;
+       full_payload_len = plain_len;
+
+       sn = WLAN_GET_SEQ_SEQ(sc);
+       fn = WLAN_GET_SEQ_FRAG(sc);
+
+       if (frag) {
+               /* MSDU reassembly for Michael MIC validation */
+               if (fn == 0 && (fc & WLAN_FC_MOREFRAG)) {
+                       /* Start of a new fragmented MSDU */
+                       wpabuf_free(frag->buf);
+                       frag->buf = NULL;
+                       frag->buf = wpabuf_alloc_copy(plain, plain_len);
+                       os_memcpy(frag->ra, hdr->addr1, ETH_ALEN);
+                       os_memcpy(frag->ta, hdr->addr2, ETH_ALEN);
+                       frag->sn = sn;
+                       frag->fn = 0;
+               }
+
+               if (frag->buf && (fn || (fc & WLAN_FC_MOREFRAG)) &&
+                   sn == frag->sn && fn == frag->fn + 1 &&
+                   os_memcmp(frag->ra, hdr->addr1, ETH_ALEN) == 0 &&
+                   os_memcmp(frag->ta, hdr->addr2, ETH_ALEN) == 0) {
+                       /* Add the next fragment */
+                       if (wpabuf_resize(&frag->buf, plain_len) == 0) {
+                               wpabuf_put_data(frag->buf, plain, plain_len);
+                               frag->fn = fn;
+                               if (!(fc & WLAN_FC_MOREFRAG)) {
+                                       full_payload = wpabuf_head(frag->buf);
+                                       full_payload_len =
+                                               wpabuf_len(frag->buf);
+                                       wpa_hexdump(MSG_MSGDUMP,
+                                                   "TKIP reassembled full payload",
+                                                   full_payload,
+                                                   full_payload_len);
+                               }
+                       }
+               }
+       }
+
+       if ((fc & WLAN_FC_MOREFRAG) || (fn > 0 && full_payload == plain)) {
+               /* Return the decrypted fragment and do not check the
+                * Michael MIC value since no reassembled frame is available. */
                *decrypted_len = plain_len;
                if (mic_res) {
                        *mic_res = MICHAEL_MIC_NOT_VERIFIED;
@@ -348,7 +391,7 @@ u8 * tkip_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
                }
        }
 
-       if (plain_len < 8) {
+       if (full_payload_len < 8) {
                wpa_printf(MSG_INFO, "TKIP: Not enough room for Michael MIC "
                           "in a frame from " MACSTR, MAC2STR(hdr->addr2));
                os_free(plain);
@@ -357,13 +400,14 @@ u8 * tkip_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
 
        michael_mic_hdr(hdr, michael_hdr);
        mic_key = tk + ((fc & WLAN_FC_FROMDS) ? 16 : 24);
-       michael_mic(mic_key, michael_hdr, plain, plain_len - 8, mic);
-       if (os_memcmp(mic, plain + plain_len - 8, 8) != 0) {
+       michael_mic(mic_key, michael_hdr, full_payload, full_payload_len - 8,
+                   mic);
+       if (os_memcmp(mic, full_payload + full_payload_len - 8, 8) != 0) {
                wpa_printf(MSG_INFO, "TKIP: Michael MIC mismatch in a frame "
                           "from " MACSTR, MAC2STR(hdr->addr2));
                wpa_hexdump(MSG_DEBUG, "TKIP: Calculated MIC", mic, 8);
                wpa_hexdump(MSG_DEBUG, "TKIP: Received MIC",
-                           plain + plain_len - 8, 8);
+                           full_payload + full_payload_len - 8, 8);
                if (mic_res) {
                        *decrypted_len = plain_len - 8;
                        *mic_res = MICHAEL_MIC_INCORRECT;
index 62c89e2261502f283b96a1e185cb3795e6a2073d..ac20b068eb8af033afd532a6b7c34a4413d7364d 100644 (file)
@@ -110,6 +110,8 @@ static void wlantest_deinit(struct wlantest *wt)
        clear_notes(wt);
        os_free(wt->decrypted);
        wt->decrypted = NULL;
+       wpabuf_free(wt->tkip_frag.buf);
+       wt->tkip_frag.buf = NULL;
 }
 
 
index 4ea10c9e8782291383ac31a436e3659a575499fd..33ab42229a542aa76003c6762a4ff667003fe4b6 100644 (file)
@@ -184,6 +184,14 @@ struct wlantest_radius {
 #define MAX_CTRL_CONNECTIONS 10
 #define MAX_NOTES 10
 
+struct tkip_frag {
+       struct wpabuf *buf;
+       u8 ra[ETH_ALEN];
+       u8 ta[ETH_ALEN];
+       u16 sn;
+       u8 fn;
+};
+
 struct wlantest {
        int monitor_sock;
        int monitor_wired;
@@ -227,6 +235,8 @@ struct wlantest {
 
        const char *write_file;
        const char *pcapng_file;
+
+       struct tkip_frag tkip_frag;
 };
 
 void add_note(struct wlantest *wt, int level, const char *fmt, ...)
@@ -311,7 +321,7 @@ enum michael_mic_result {
 };
 u8 * tkip_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
                  const u8 *data, size_t data_len, size_t *decrypted_len,
-                 enum michael_mic_result *mic_res);
+                 enum michael_mic_result *mic_res, struct tkip_frag *frag);
 u8 * tkip_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen, u8 *qos,
                  u8 *pn, int keyid, size_t *encrypted_len);
 void tkip_get_pn(u8 *pn, const u8 *data);