$OpenBSD: patch-src_rsn_supp_wpa_c,v 1.3 2017/10/16 21:27:36 dcoppa Exp $

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.

From 8280294e74846ea342389a0cd17215050fa5afe8 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <j@w1.fi>
Date: Sun, 1 Oct 2017 12:12:24 +0300
Subject: [PATCH 3/8] Extend protection of GTK/IGTK reinstallation of WNM-Sleep
 Mode cases

This extends the protection to track last configured GTK/IGTK value
separately from EAPOL-Key frames and WNM-Sleep Mode frames to cover a
corner case where these two different mechanisms may get used when the
GTK/IGTK has changed and tracking a single value is not sufficient to
detect a possible key reconfiguration.

From 8f82bc94e8697a9d47fa8774dfdaaede1084912c Mon Sep 17 00:00:00 2001
From: Mathy Vanhoef <Mathy.Vanhoef@cs.kuleuven.be>
Date: Fri, 29 Sep 2017 04:22:51 +0200
Subject: [PATCH 4/8] Prevent installation of an all-zero TK

Properly track whether a PTK has already been installed to the driver
and the TK part cleared from memory. This prevents an attacker from
trying to trick the client into installing an all-zero TK.

From b372ab0b7daea719749194dc554b26e6367603f2 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <j@w1.fi>
Date: Fri, 22 Sep 2017 12:06:37 +0300
Subject: [PATCH 8/8] FT: Do not allow multiple Reassociation Response frames

The driver is expected to not report a second association event without
the station having explicitly request a new association. As such, this
case should not be reachable. However, since reconfiguring the same
pairwise or group keys to the driver could result in nonce reuse issues,
be extra careful here and do an additional state check to avoid this
even if the local driver ends up somehow accepting an unexpected
Reassociation Response frame.

Index: src/rsn_supp/wpa.c
--- src/rsn_supp/wpa.c.orig
+++ src/rsn_supp/wpa.c
@@ -510,7 +510,6 @@ static void wpa_supplicant_process_1_of_4(struct wpa_s
 		os_memset(buf, 0, sizeof(buf));
 	}
 	sm->tptk_set = 1;
-	sm->tk_to_set = 1;
 
 	kde = sm->assoc_wpa_ie;
 	kde_len = sm->assoc_wpa_ie_len;
@@ -615,7 +614,7 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *s
 	enum wpa_alg alg;
 	const u8 *key_rsc;
 
-	if (!sm->tk_to_set) {
+	if (sm->ptk.installed) {
 		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 			"WPA: Do not re-install same PTK to the driver");
 		return 0;
@@ -659,7 +658,7 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *s
 
 	/* TK is not needed anymore in supplicant */
 	os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
-	sm->tk_to_set = 0;
+	sm->ptk.installed = 1;
 
 	if (sm->wpa_ptk_rekey) {
 		eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
@@ -709,11 +708,23 @@ struct wpa_gtk_data {
 
 static int wpa_supplicant_install_gtk(struct wpa_sm *sm,
 				      const struct wpa_gtk_data *gd,
-				      const u8 *key_rsc)
+				      const u8 *key_rsc, int wnm_sleep)
 {
 	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) ||
+	    (sm->gtk_wnm_sleep.gtk_len == (size_t) gd->gtk_len &&
+	     os_memcmp(sm->gtk_wnm_sleep.gtk, gd->gtk,
+		       sm->gtk_wnm_sleep.gtk_len) == 0)) {
+		wpa_dbg(sm->ctx->msg_ctx, 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_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 		"WPA: Installing GTK to the driver (keyidx=%d tx=%d len=%d)",
@@ -748,6 +759,15 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *s
 	}
 	os_memset(gtk_buf, 0, sizeof(gtk_buf));
 
+	if (wnm_sleep) {
+		sm->gtk_wnm_sleep.gtk_len = gd->gtk_len;
+		os_memcpy(sm->gtk_wnm_sleep.gtk, gd->gtk,
+			  sm->gtk_wnm_sleep.gtk_len);
+	} else {
+		sm->gtk.gtk_len = gd->gtk_len;
+		os_memcpy(sm->gtk.gtk, gd->gtk, sm->gtk.gtk_len);
+	}
+
 	return 0;
 }
 
@@ -840,7 +860,7 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *
 	    (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
 					       gtk_len, gtk_len,
 					       &gd.key_rsc_len, &gd.alg) ||
-	     wpa_supplicant_install_gtk(sm, &gd, key_rsc))) {
+	     wpa_supplicant_install_gtk(sm, &gd, key_rsc, 0))) {
 		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 			"RSN: Failed to install GTK");
 		os_memset(&gd, 0, sizeof(gd));
@@ -854,6 +874,58 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *
 }
 
 
+#ifdef CONFIG_IEEE80211W
+static int wpa_supplicant_install_igtk(struct wpa_sm *sm,
+				       const struct wpa_igtk_kde *igtk,
+				       int wnm_sleep)
+{
+	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) ||
+	    (sm->igtk_wnm_sleep.igtk_len == len &&
+	     os_memcmp(sm->igtk_wnm_sleep.igtk, igtk->igtk,
+		       sm->igtk_wnm_sleep.igtk_len) == 0)) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"WPA: Not reinstalling already in-use IGTK to the driver (keyidx=%d)",
+			keyidx);
+		return  0;
+	}
+
+	wpa_dbg(sm->ctx->msg_ctx, 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;
+	}
+
+	if (wnm_sleep) {
+		sm->igtk_wnm_sleep.igtk_len = len;
+		os_memcpy(sm->igtk_wnm_sleep.igtk, igtk->igtk,
+			  sm->igtk_wnm_sleep.igtk_len);
+	} else {
+		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)
 {
@@ -864,30 +936,14 @@ static int ieee80211w_set_keys(struct wpa_sm *sm,
 	if (ie->igtk) {
 		size_t len;
 		const struct wpa_igtk_kde *igtk;
-		u16 keyidx;
+
 		len = wpa_cipher_key_len(sm->mgmt_group_cipher);
 		if (ie->igtk_len != WPA_IGTK_KDE_PREFIX_LEN + len)
 			return -1;
+
 		igtk = (const struct wpa_igtk_kde *) ie->igtk;
-		keyidx = WPA_GET_LE16(igtk->keyid);
-		wpa_dbg(sm->ctx->msg_ctx, 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);
+		if (wpa_supplicant_install_igtk(sm, igtk, 0) < 0)
 			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;
-		}
 	}
 
 	return 0;
@@ -1536,7 +1592,7 @@ static void wpa_supplicant_process_1_of_2(struct wpa_s
 	if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc))
 		key_rsc = null_rsc;
 
-	if (wpa_supplicant_install_gtk(sm, &gd, key_rsc) ||
+	if (wpa_supplicant_install_gtk(sm, &gd, key_rsc, 0) ||
 	    wpa_supplicant_send_2_of_2(sm, key, ver, key_info) < 0)
 		goto failed;
 	os_memset(&gd, 0, sizeof(gd));
@@ -2307,7 +2363,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;
@@ -2333,11 +2389,11 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *
 		/* Prepare for the next transition */
 		wpa_ft_prepare_auth_request(sm, NULL);
 
-		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.
@@ -2347,6 +2403,12 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *
 		os_memset(&sm->ptk, 0, sizeof(sm->ptk));
 		sm->tptk_set = 0;
 		os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+		os_memset(&sm->gtk, 0, sizeof(sm->gtk));
+		os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep));
+#ifdef CONFIG_IEEE80211W
+		os_memset(&sm->igtk, 0, sizeof(sm->igtk));
+		os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep));
+#endif /* CONFIG_IEEE80211W */
 	}
 
 #ifdef CONFIG_TDLS
@@ -2378,6 +2440,9 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm)
 #ifdef CONFIG_TDLS
 	wpa_tdls_disassoc(sm);
 #endif /* CONFIG_TDLS */
+#ifdef CONFIG_IEEE80211R
+	sm->ft_reassoc_completed = 0;
+#endif /* CONFIG_IEEE80211R */
 
 	/* Keys are not needed in the WPA state machine anymore */
 	wpa_sm_drop_sa(sm);
@@ -2877,6 +2942,12 @@ void wpa_sm_drop_sa(struct wpa_sm *sm)
 	os_memset(sm->pmk, 0, sizeof(sm->pmk));
 	os_memset(&sm->ptk, 0, sizeof(sm->ptk));
 	os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+	os_memset(&sm->gtk, 0, sizeof(sm->gtk));
+	os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep));
+#ifdef CONFIG_IEEE80211W
+	os_memset(&sm->igtk, 0, sizeof(sm->igtk));
+	os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep));
+#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_IEEE80211R
 	os_memset(sm->xxkey, 0, sizeof(sm->xxkey));
 	os_memset(sm->pmk_r0, 0, sizeof(sm->pmk_r0));
@@ -2940,7 +3011,7 @@ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 sub
 
 		wpa_hexdump_key(MSG_DEBUG, "Install GTK (WNM SLEEP)",
 				gd.gtk, gd.gtk_len);
-		if (wpa_supplicant_install_gtk(sm, &gd, key_rsc)) {
+		if (wpa_supplicant_install_gtk(sm, &gd, key_rsc, 1)) {
 			os_memset(&gd, 0, sizeof(gd));
 			wpa_printf(MSG_DEBUG, "Failed to install the GTK in "
 				   "WNM mode");
@@ -2949,29 +3020,11 @@ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 sub
 		os_memset(&gd, 0, sizeof(gd));
 #ifdef CONFIG_IEEE80211W
 	} else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) {
-		struct wpa_igtk_kde igd;
-		u16 keyidx;
+		const struct wpa_igtk_kde *igtk;
 
-		os_memset(&igd, 0, sizeof(igd));
-		keylen = wpa_cipher_key_len(sm->mgmt_group_cipher);
-		os_memcpy(igd.keyid, buf + 2, 2);
-		os_memcpy(igd.pn, buf + 4, 6);
-
-		keyidx = WPA_GET_LE16(igd.keyid);
-		os_memcpy(igd.igtk, buf + 10, keylen);
-
-		wpa_hexdump_key(MSG_DEBUG, "Install IGTK (WNM SLEEP)",
-				igd.igtk, keylen);
-		if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
-				   broadcast_ether_addr,
-				   keyidx, 0, igd.pn, sizeof(igd.pn),
-				   igd.igtk, keylen) < 0) {
-			wpa_printf(MSG_DEBUG, "Failed to install the IGTK in "
-				   "WNM mode");
-			os_memset(&igd, 0, sizeof(igd));
+		igtk = (const struct wpa_igtk_kde *) (buf + 2);
+		if (wpa_supplicant_install_igtk(sm, igtk, 1) < 0)
 			return -1;
-		}
-		os_memset(&igd, 0, sizeof(igd));
 #endif /* CONFIG_IEEE80211W */
 	} else {
 		wpa_printf(MSG_DEBUG, "Unknown element id");
