From 927f891007c402fefd1ff384645b3f07597c3ede Mon Sep 17 00:00:00 2001
From: Mathy Vanhoef <Mathy.Vanhoef@cs.kuleuven.be>
Date: Wed, 12 Jul 2017 16:03:24 +0200
Subject: [PATCH 2/8] Prevent reinstallation of an already in-use group key

Track the current GTK and IGTK that is in use and when receiving a
(possibly retransmitted) Group Message 1 or WNM-Sleep Mode Response, do
not install the given key if it is already in use. This prevents an
attacker from trying to trick the client into resetting or lowering the
sequence counter associated to the group key.

Signed-off-by: Mathy Vanhoef <Mathy.Vanhoef@cs.kuleuven.be>
---
 src/common/wpa_common.h |  11 +++++
 src/rsn_supp/wpa.c      | 116 ++++++++++++++++++++++++++++++------------------
 src/rsn_supp/wpa_i.h    |   4 ++
 3 files changed, 87 insertions(+), 44 deletions(-)

diff --git a/src/utils/common.h b/src/utils/common.h
index af1d0f0..d200285 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -458,6 +458,13 @@
 	return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]);
 }
 
+static inline int is_broadcast_ether_addr(const u8 *a)
+{
+        return (a[0] & a[1] & a[2] & a[3] & a[4] & a[5]) == 0xff;
+}
+
+#define broadcast_ether_addr (const u8 *) "\xff\xff\xff\xff\xff\xff"
+
 #include "wpa_debug.h"
 
 
diff --git a/src/common/defs.h b/src/common/defs.h
index af1d0f0..d200285 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -32,6 +32,14 @@
 #ifdef CONFIG_IEEE80211W
 #define WPA_CIPHER_AES_128_CMAC BIT(5)
 #endif /* CONFIG_IEEE80211W */
+#define WPA_CIPHER_GCMP BIT(6)
+#define WPA_CIPHER_SMS4 BIT(7)
+#define WPA_CIPHER_GCMP_256 BIT(8)
+#define WPA_CIPHER_CCMP_256 BIT(9)
+#define WPA_CIPHER_BIP_GMAC_128 BIT(11)
+#define WPA_CIPHER_BIP_GMAC_256 BIT(12)
+#define WPA_CIPHER_BIP_CMAC_256 BIT(13)
+#define WPA_CIPHER_GTK_NOT_USED BIT(14)
 
 #define WPA_KEY_MGMT_IEEE8021X BIT(0)
 #define WPA_KEY_MGMT_PSK BIT(1)
@@ -86,7 +94,15 @@
 	WPA_ALG_TKIP,
 	WPA_ALG_CCMP,
 	WPA_ALG_IGTK,
-	WPA_ALG_PMK
+	WPA_ALG_PMK,
+        WPA_ALG_GCMP,
+        WPA_ALG_SMS4,
+        WPA_ALG_KRK,
+        WPA_ALG_GCMP_256,
+        WPA_ALG_CCMP_256,
+        WPA_ALG_BIP_GMAC_128,
+        WPA_ALG_BIP_GMAC_256,
+        WPA_ALG_BIP_CMAC_256
 };
 
 /**
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index af1d0f0..d200285 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -669,3 +669,49 @@
 		return "UNKNOWN";
 	}
 }
+
+int wpa_cipher_key_len(int cipher)
+{
+        switch (cipher) {
+        case WPA_CIPHER_CCMP_256:
+        case WPA_CIPHER_GCMP_256:
+        case WPA_CIPHER_BIP_GMAC_256:
+        case WPA_CIPHER_BIP_CMAC_256:
+                return 32;
+        case WPA_CIPHER_CCMP:
+        case WPA_CIPHER_GCMP:
+        case WPA_CIPHER_AES_128_CMAC:
+        case WPA_CIPHER_BIP_GMAC_128:
+                return 16;
+        case WPA_CIPHER_TKIP:
+                return 32;
+        }
+
+        return 0;
+}
+
+enum wpa_alg wpa_cipher_to_alg(int cipher)
+{
+        switch (cipher) {
+        case WPA_CIPHER_CCMP_256:
+                return WPA_ALG_CCMP_256;
+        case WPA_CIPHER_GCMP_256:
+                return WPA_ALG_GCMP_256;
+        case WPA_CIPHER_CCMP:
+                return WPA_ALG_CCMP;
+        case WPA_CIPHER_GCMP:
+                return WPA_ALG_GCMP;
+        case WPA_CIPHER_TKIP:
+                return WPA_ALG_TKIP;
+        case WPA_CIPHER_AES_128_CMAC:
+                return WPA_ALG_IGTK;
+        case WPA_CIPHER_BIP_GMAC_128:
+                return WPA_ALG_BIP_GMAC_128;
+        case WPA_CIPHER_BIP_GMAC_256:
+                return WPA_ALG_BIP_GMAC_256;
+        case WPA_CIPHER_BIP_CMAC_256:
+                return WPA_ALG_BIP_CMAC_256;
+        }
+        return WPA_ALG_NONE;
+}
+
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index af1d0f0..d200285 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -105,6 +105,7 @@
 
 #ifdef CONFIG_IEEE80211W
 #define WPA_IGTK_LEN 16
+#define WPA_IGTK_MAX_LEN 32
 #endif /* CONFIG_IEEE80211W */
 
 
@@ -217,6 +217,17 @@ struct wpa_ptk {
 	} u;
 } STRUCT_PACKED;
 
+struct wpa_gtk {
+	u8 gtk[WPA_GTK_MAX_LEN];
+	size_t gtk_len;
+};
+
+#ifdef CONFIG_IEEE80211W
+struct wpa_igtk {
+	u8 igtk[WPA_IGTK_MAX_LEN];
+	size_t igtk_len;
+};
+#endif /* CONFIG_IEEE80211W */
 
 /* WPA IE version 1
  * 00-50-f2:1 (OUI:OUI type)
@@ -355,4 +356,7 @@
 const char * wpa_cipher_txt(int cipher);
 const char * wpa_key_mgmt_txt(int key_mgmt, int proto);
 
+int wpa_cipher_key_len(int cipher);
+enum wpa_alg wpa_cipher_to_alg(int cipher);
+
 #endif /* WPA_COMMON_H */
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 3c47879..95bd7be 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -583,6 +583,15 @@
 	const u8 *_gtk = gd->gtk;
 	u8 gtk_buf[32];
 
+	/* Detect possible key reinstallation */
+	if (sm->gtk.gtk_len == (size_t) gd->gtk_len &&
+	    os_memcmp(sm->gtk.gtk, gd->gtk, sm->gtk.gtk_len) == 0) {
+		wpa_printf(MSG_DEBUG,
+			"WPA: Not reinstalling already in-use GTK to the driver (keyidx=%d tx=%d len=%d)",
+			gd->keyidx, gd->tx, gd->gtk_len);
+		return 0;
+	}
+
 	wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len);
 	wpa_printf(MSG_DEBUG, "WPA: Installing GTK to the driver "
 		   "(keyidx=%d tx=%d len=%d).", gd->keyidx, gd->tx,
@@ -614,6 +623,8 @@
 		return -1;
 	}
 
+	sm->gtk.gtk_len = gd->gtk_len;
+	os_memcpy(sm->gtk.gtk, gd->gtk, sm->gtk.gtk_len);
 	return 0;
 }
 
@@ -854,6 +866,48 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm,
 }
 
 
+#ifdef CONFIG_IEEE80211W
+static int wpa_supplicant_install_igtk(struct wpa_sm *sm,
+				       const struct wpa_igtk_kde *igtk)
+{
+	size_t len = wpa_cipher_key_len(sm->mgmt_group_cipher);
+	u16 keyidx = WPA_GET_LE16(igtk->keyid);
+
+	/* Detect possible key reinstallation */
+	if (sm->igtk.igtk_len == len &&
+	    os_memcmp(sm->igtk.igtk, igtk->igtk, sm->igtk.igtk_len) == 0) {
+		wpa_printf(MSG_DEBUG,
+			"WPA: Not reinstalling already in-use IGTK to the driver (keyidx=%d)",
+			keyidx);
+		return  0;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		"WPA: IGTK keyid %d pn %02x%02x%02x%02x%02x%02x",
+		keyidx, MAC2STR(igtk->pn));
+	wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK", igtk->igtk, len);
+	if (keyidx > 4095) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: Invalid IGTK KeyID %d", keyidx);
+		return -1;
+	}
+	if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+			   broadcast_ether_addr,
+			   keyidx, 0, igtk->pn, sizeof(igtk->pn),
+			   igtk->igtk, len) < 0) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: Failed to configure IGTK to the driver");
+		return -1;
+	}
+
+	sm->igtk.igtk_len = len;
+	os_memcpy(sm->igtk.igtk, igtk->igtk, sm->igtk.igtk_len);
+
+	return 0;
+}
+#endif /* CONFIG_IEEE80211W */
+
+
 static int ieee80211w_set_keys(struct wpa_sm *sm,
 			       struct wpa_eapol_ie_parse *ie)
 {
@@ -735,29 +746,13 @@
 
 	if (ie->igtk) {
 		const struct wpa_igtk_kde *igtk;
-		u16 keyidx;
+
 		if (ie->igtk_len != sizeof(*igtk))
 			return -1;
+
 		igtk = (const struct wpa_igtk_kde *) ie->igtk;
-		keyidx = WPA_GET_LE16(igtk->keyid);
-		wpa_printf(MSG_DEBUG, "WPA: IGTK keyid %d "
-			   "pn %02x%02x%02x%02x%02x%02x",
-			   keyidx, MAC2STR(igtk->pn));
-		wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK",
-				igtk->igtk, WPA_IGTK_LEN);
-		if (keyidx > 4095) {
-			wpa_printf(MSG_WARNING, "WPA: Invalid IGTK KeyID %d",
-				   keyidx);
+		if (wpa_supplicant_install_igtk(sm, igtk) < 0)
 			return -1;
-		}
-		if (wpa_sm_set_key(sm, WPA_ALG_IGTK,
-				   (u8 *) "\xff\xff\xff\xff\xff\xff",
-				   keyidx, 0, igtk->pn, sizeof(igtk->pn),
-				   igtk->igtk, WPA_IGTK_LEN) < 0) {
-			wpa_printf(MSG_WARNING, "WPA: Failed to configure IGTK"
-				   " to the driver");
-			return -1;
-		}
 	}
 
 	return 0;
@@ -2307,7 +2345,7 @@ void wpa_sm_deinit(struct wpa_sm *sm)
  */
 void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
 {
-	int clear_ptk = 1;
+	int clear_keys = 1;
 
 	if (sm == NULL)
 		return;
@@ -1929,11 +1924,11 @@
 		/* Prepare for the next transition */
 		wpa_ft_prepare_auth_request(sm);
 
-		clear_ptk = 0;
+		clear_keys = 0;
 	}
 #endif /* CONFIG_IEEE80211R */
 
-	if (clear_ptk) {
+	if (clear_keys) {
 		/*
 		 * IEEE 802.11, 8.4.10: Delete PTK SA on (re)association if
 		 * this is not part of a Fast BSS Transition.
@@ -1941,7 +1936,12 @@
 		wpa_printf(MSG_DEBUG, "WPA: Clear old PTK");
 		sm->ptk_set = 0;
 		sm->tptk_set = 0;
+		os_memset(&sm->gtk, 0, sizeof(sm->gtk));
+#ifdef CONFIG_IEEE80211W
+		os_memset(&sm->igtk, 0, sizeof(sm->igtk));
+#endif /* CONFIG_IEEE80211W */
 	}
+
 }
 
 
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index f653ba6..afc9e37 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -31,6 +31,10 @@ struct wpa_sm {
 	u8 rx_replay_counter[WPA_REPLAY_COUNTER_LEN];
 	int rx_replay_counter_set;
 	u8 request_counter[WPA_REPLAY_COUNTER_LEN];
+	struct wpa_gtk gtk;
+#ifdef CONFIG_IEEE80211W
+	struct wpa_igtk igtk;
+#endif /* CONFIG_IEEE80211W */
 
 	struct eapol_sm *eapol; /* EAPOL state machine from upper level code */
 
-- 
2.7.4

