untrusted comment: signature from openbsd 6.0 base secret key
RWSho3oKSqgLQzcYx1qpk3xo94+fk008L6WnGNF5Rq+pk+B8EeR8RUe9I0jZN9REXpEKpAKpqGE4v/2/P86nedk8e2Ywvdl79Qs=

OpenBSD 6.0 errata 041, August 30, 2017

State transition errors could cause reinstallation of old WPA keys.

Apply by doing:
   signify -Vep /etc/signify/openbsd-60-base.pub -x 041_net80211_replay.patch.sig \
       -m - | (cd /usr/src && patch -p0)

And then rebuild and install a new kernel:
   cd /usr/src/sys/arch/`machine`/conf
   KK=`sysctl -n kern.osversion | cut -d# -f1`
   config $KK
   cd ../compile/$KK
   make
   make install

Index: sys/net80211/ieee80211_crypto.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_crypto.c,v
retrieving revision 1.66
diff -u -p -r1.66 ieee80211_crypto.c
--- sys/net80211/ieee80211_crypto.c     24 Nov 2015 13:45:06 -0000      1.66
+++ sys/net80211/ieee80211_crypto.c     29 Aug 2017 21:20:52 -0000
@@ -76,7 +76,6 @@ ieee80211_crypto_detach(struct ifnet *if
{
       struct ieee80211com *ic = (void *)ifp;
       struct ieee80211_pmk *pmk;
-       int i;

       /* purge the PMKSA cache */
       while ((pmk = TAILQ_FIRST(&ic->ic_pmksa)) != NULL) {
@@ -86,15 +85,23 @@ ieee80211_crypto_detach(struct ifnet *if
       }

       /* clear all group keys from memory */
+       ieee80211_crypto_clear_groupkeys(ic);
+
+       /* clear pre-shared key from memory */
+       explicit_bzero(ic->ic_psk, IEEE80211_PMK_LEN);
+}
+
+void
+ieee80211_crypto_clear_groupkeys(struct ieee80211com *ic)
+{
+       int i;
+
       for (i = 0; i < IEEE80211_GROUP_NKID; i++) {
               struct ieee80211_key *k = &ic->ic_nw_keys[i];
               if (k->k_cipher != IEEE80211_CIPHER_NONE)
                       (*ic->ic_delete_key)(ic, NULL, k);
               explicit_bzero(k, sizeof(*k));
       }
-
-       /* clear pre-shared key from memory */
-       explicit_bzero(ic->ic_psk, IEEE80211_PMK_LEN);
}

/*
Index: sys/net80211/ieee80211_crypto.h
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_crypto.h,v
retrieving revision 1.23
diff -u -p -r1.23 ieee80211_crypto.h
--- sys/net80211/ieee80211_crypto.h     5 Dec 2015 16:26:53 -0000       1.23
+++ sys/net80211/ieee80211_crypto.h     29 Aug 2017 21:20:52 -0000
@@ -111,6 +111,7 @@ struct      ieee80211_node;
void   ieee80211_crypto_attach(struct ifnet *);
void   ieee80211_crypto_detach(struct ifnet *);

+void   ieee80211_crypto_clear_groupkeys(struct ieee80211com *);
struct ieee80211_key *ieee80211_get_txkey(struct ieee80211com *,
           const struct ieee80211_frame *, struct ieee80211_node *);
struct ieee80211_key *ieee80211_get_rxkey(struct ieee80211com *,
Index: sys/net80211/ieee80211_node.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_node.c,v
retrieving revision 1.103
diff -u -p -r1.103 ieee80211_node.c
--- sys/net80211/ieee80211_node.c       21 May 2016 09:07:11 -0000      1.103
+++ sys/net80211/ieee80211_node.c       29 Aug 2017 21:20:52 -0000
@@ -1430,6 +1430,7 @@ ieee80211_node_join_rsn(struct ieee80211
       ni->ni_key_count = 0;
       ni->ni_port_valid = 0;
       ni->ni_flags &= ~IEEE80211_NODE_TXRXPROT;
+       ni->ni_flags &= ~IEEE80211_NODE_RSN_NEW_PTK;
       ni->ni_replaycnt = -1;  /* XXX */
       ni->ni_rsn_retries = 0;
       ni->ni_rsncipher = ni->ni_rsnciphers;
Index: sys/net80211/ieee80211_node.h
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_node.h,v
retrieving revision 1.60.2.1
diff -u -p -r1.60.2.1 ieee80211_node.h
--- sys/net80211/ieee80211_node.h       1 Mar 2017 20:57:51 -0000       1.60.2.1
+++ sys/net80211/ieee80211_node.h       29 Aug 2017 21:20:52 -0000
@@ -291,6 +291,7 @@ struct ieee80211_node {
#define IEEE80211_NODE_HT              0x0400  /* HT negotiated */
#define IEEE80211_NODE_SA_QUERY                0x0800  /* SA Query in progress */
#define IEEE80211_NODE_SA_QUERY_FAILED 0x1000  /* last SA Query failed */
+#define IEEE80211_NODE_RSN_NEW_PTK     0x2000  /* expecting a new PTK */
};

RB_HEAD(ieee80211_tree, ieee80211_node);
Index: sys/net80211/ieee80211_pae_input.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_pae_input.c,v
retrieving revision 1.25.6.2
diff -u -p -r1.25.6.2 ieee80211_pae_input.c
--- sys/net80211/ieee80211_pae_input.c  2 Aug 2017 16:51:38 -0000       1.25.6.2
+++ sys/net80211/ieee80211_pae_input.c  29 Aug 2017 21:20:52 -0000
@@ -47,6 +47,8 @@ void  ieee80211_recv_4way_msg2(struct iee
           struct ieee80211_eapol_key *, struct ieee80211_node *,
           const u_int8_t *);
#endif
+int    ieee80211_must_update_group_key(struct ieee80211_key *, const uint8_t *,
+           int);
void   ieee80211_recv_4way_msg3(struct ieee80211com *,
           struct ieee80211_eapol_key *, struct ieee80211_node *);
#ifndef IEEE80211_STA_ONLY
@@ -261,6 +263,9 @@ ieee80211_recv_4way_msg1(struct ieee8021
       ieee80211_derive_ptk(ni->ni_rsnakms, ni->ni_pmk, ni->ni_macaddr,
           ic->ic_myaddr, ni->ni_nonce, ic->ic_nonce, &tptk);

+       /* We are now expecting a new pairwise key. */
+       ni->ni_flags |= IEEE80211_NODE_RSN_NEW_PTK;
+
       if (ic->ic_if.if_flags & IFF_DEBUG)
               printf("%s: received msg %d/%d of the %s handshake from %s\n",
                   ic->ic_if.if_xname, 1, 4, "4-way",
@@ -335,6 +340,14 @@ ieee80211_recv_4way_msg2(struct ieee8021
}
#endif /* IEEE80211_STA_ONLY */

+int
+ieee80211_must_update_group_key(struct ieee80211_key *k, const uint8_t *gtk,
+    int len)
+{
+       return (k->k_cipher == IEEE80211_CIPHER_NONE || k->k_len != len ||
+           memcmp(k->k_key, gtk, len) != 0);
+}
+
/*
 * Process Message 3 of the 4-Way Handshake (sent by Authenticator).
 */
@@ -515,7 +528,8 @@ ieee80211_recv_4way_msg3(struct ieee8021
       if (ieee80211_send_4way_msg4(ic, ni) != 0)
               return; /* ..authenticator will retry */

-       if (ni->ni_rsncipher != IEEE80211_CIPHER_USEGROUP) {
+       if (ni->ni_rsncipher != IEEE80211_CIPHER_USEGROUP &&
+           (ni->ni_flags & IEEE80211_NODE_RSN_NEW_PTK)) {
               u_int64_t prsc;

               /* check that key length matches that of pairwise cipher */
@@ -538,9 +552,13 @@ ieee80211_recv_4way_msg3(struct ieee8021
                       reason = IEEE80211_REASON_AUTH_LEAVE;
                       goto deauth;
               }
+               ni->ni_flags &= ~IEEE80211_NODE_RSN_NEW_PTK;
               ni->ni_flags &= ~IEEE80211_NODE_TXRXPROT;
               ni->ni_flags |= IEEE80211_NODE_RXPROT;
-       }
+       } else if (ni->ni_rsncipher != IEEE80211_CIPHER_USEGROUP)
+               printf("%s: unexpected pairwise key update received from %s\n",
+                   ic->ic_if.if_xname, ether_sprintf(ni->ni_macaddr));
+
       if (gtk != NULL) {
               u_int8_t kid;

@@ -553,20 +571,24 @@ ieee80211_recv_4way_msg3(struct ieee8021
               /* map GTK to 802.11 key */
               kid = gtk[6] & 3;
               k = &ic->ic_nw_keys[kid];
-               memset(k, 0, sizeof(*k));
-               k->k_id = kid;  /* 0-3 */
-               k->k_cipher = ni->ni_rsngroupcipher;
-               k->k_flags = IEEE80211_KEY_GROUP;
-               if (gtk[6] & (1 << 2))
-                       k->k_flags |= IEEE80211_KEY_TX;
-               k->k_rsc[0] = LE_READ_6(key->rsc);
-               k->k_len = keylen;
-               memcpy(k->k_key, &gtk[8], k->k_len);
-               /* install the GTK */
-               if ((*ic->ic_set_key)(ic, ni, k) != 0) {
-                       reason = IEEE80211_REASON_AUTH_LEAVE;
-                       goto deauth;
-               }
+               if (ieee80211_must_update_group_key(k, &gtk[8], keylen)) {
+                       memset(k, 0, sizeof(*k));
+                       k->k_id = kid;  /* 0-3 */
+                       k->k_cipher = ni->ni_rsngroupcipher;
+                       k->k_flags = IEEE80211_KEY_GROUP;
+                       if (gtk[6] & (1 << 2))
+                               k->k_flags |= IEEE80211_KEY_TX;
+                       k->k_rsc[0] = LE_READ_6(key->rsc);
+                       k->k_len = keylen;
+                       memcpy(k->k_key, &gtk[8], k->k_len);
+                       /* install the GTK */
+                       if ((*ic->ic_set_key)(ic, ni, k) != 0) {
+                               reason = IEEE80211_REASON_AUTH_LEAVE;
+                               goto deauth;
+                       }
+               } else
+                       printf("%s: reused group key update received from %s\n",
+                           ic->ic_if.if_xname, ether_sprintf(ni->ni_macaddr));
       }
       if (igtk != NULL) {     /* implies MFP && gtk != NULL */
               u_int16_t kid;
@@ -584,18 +606,22 @@ ieee80211_recv_4way_msg3(struct ieee8021
               }
               /* map IGTK to 802.11 key */
               k = &ic->ic_nw_keys[kid];
-               memset(k, 0, sizeof(*k));
-               k->k_id = kid;  /* either 4 or 5 */
-               k->k_cipher = ni->ni_rsngroupmgmtcipher;
-               k->k_flags = IEEE80211_KEY_IGTK;
-               k->k_mgmt_rsc = LE_READ_6(&igtk[8]);    /* IPN */
-               k->k_len = 16;
-               memcpy(k->k_key, &igtk[14], k->k_len);
-               /* install the IGTK */
-               if ((*ic->ic_set_key)(ic, ni, k) != 0) {
-                       reason = IEEE80211_REASON_AUTH_LEAVE;
-                       goto deauth;
-               }
+               if (ieee80211_must_update_group_key(k, &igtk[14], 16)) {
+                       memset(k, 0, sizeof(*k));
+                       k->k_id = kid;  /* either 4 or 5 */
+                       k->k_cipher = ni->ni_rsngroupmgmtcipher;
+                       k->k_flags = IEEE80211_KEY_IGTK;
+                       k->k_mgmt_rsc = LE_READ_6(&igtk[8]);    /* IPN */
+                       k->k_len = 16;
+                       memcpy(k->k_key, &igtk[14], k->k_len);
+                       /* install the IGTK */
+                       if ((*ic->ic_set_key)(ic, ni, k) != 0) {
+                               reason = IEEE80211_REASON_AUTH_LEAVE;
+                               goto deauth;
+                       }
+               } else
+                       printf("%s: reused group key update received from %s\n",
+                           ic->ic_if.if_xname, ether_sprintf(ni->ni_macaddr));
       }
       if (info & EAPOL_KEY_INSTALL)
               ni->ni_flags |= IEEE80211_NODE_TXRXPROT;
@@ -821,20 +847,24 @@ ieee80211_recv_rsn_group_msg1(struct iee
       /* map GTK to 802.11 key */
       kid = gtk[6] & 3;
       k = &ic->ic_nw_keys[kid];
-       memset(k, 0, sizeof(*k));
-       k->k_id = kid;  /* 0-3 */
-       k->k_cipher = ni->ni_rsngroupcipher;
-       k->k_flags = IEEE80211_KEY_GROUP;
-       if (gtk[6] & (1 << 2))
-               k->k_flags |= IEEE80211_KEY_TX;
-       k->k_rsc[0] = LE_READ_6(key->rsc);
-       k->k_len = keylen;
-       memcpy(k->k_key, &gtk[8], k->k_len);
-       /* install the GTK */
-       if ((*ic->ic_set_key)(ic, ni, k) != 0) {
-               reason = IEEE80211_REASON_AUTH_LEAVE;
-               goto deauth;
-       }
+       if (ieee80211_must_update_group_key(k, &gtk[8], keylen)) {
+               memset(k, 0, sizeof(*k));
+               k->k_id = kid;  /* 0-3 */
+               k->k_cipher = ni->ni_rsngroupcipher;
+               k->k_flags = IEEE80211_KEY_GROUP;
+               if (gtk[6] & (1 << 2))
+                       k->k_flags |= IEEE80211_KEY_TX;
+               k->k_rsc[0] = LE_READ_6(key->rsc);
+               k->k_len = keylen;
+               memcpy(k->k_key, &gtk[8], k->k_len);
+               /* install the GTK */
+               if ((*ic->ic_set_key)(ic, ni, k) != 0) {
+                       reason = IEEE80211_REASON_AUTH_LEAVE;
+                       goto deauth;
+               }
+       } else
+               printf("%s: reused group key update received from %s\n",
+                   ic->ic_if.if_xname, ether_sprintf(ni->ni_macaddr));
       if (igtk != NULL) {     /* implies MFP */
               /* check that the IGTK KDE is valid */
               if (igtk[1] != 4 + 24) {
@@ -849,18 +879,22 @@ ieee80211_recv_rsn_group_msg1(struct iee
               }
               /* map IGTK to 802.11 key */
               k = &ic->ic_nw_keys[kid];
-               memset(k, 0, sizeof(*k));
-               k->k_id = kid;  /* either 4 or 5 */
-               k->k_cipher = ni->ni_rsngroupmgmtcipher;
-               k->k_flags = IEEE80211_KEY_IGTK;
-               k->k_mgmt_rsc = LE_READ_6(&igtk[8]);    /* IPN */
-               k->k_len = 16;
-               memcpy(k->k_key, &igtk[14], k->k_len);
-               /* install the IGTK */
-               if ((*ic->ic_set_key)(ic, ni, k) != 0) {
-                       reason = IEEE80211_REASON_AUTH_LEAVE;
-                       goto deauth;
-               }
+               if (ieee80211_must_update_group_key(k, &igtk[14], 16)) {
+                       memset(k, 0, sizeof(*k));
+                       k->k_id = kid;  /* either 4 or 5 */
+                       k->k_cipher = ni->ni_rsngroupmgmtcipher;
+                       k->k_flags = IEEE80211_KEY_IGTK;
+                       k->k_mgmt_rsc = LE_READ_6(&igtk[8]);    /* IPN */
+                       k->k_len = 16;
+                       memcpy(k->k_key, &igtk[14], k->k_len);
+                       /* install the IGTK */
+                       if ((*ic->ic_set_key)(ic, ni, k) != 0) {
+                               reason = IEEE80211_REASON_AUTH_LEAVE;
+                               goto deauth;
+                       }
+               } else
+                       printf("%s: reused group key update received from %s\n",
+                           ic->ic_if.if_xname, ether_sprintf(ni->ni_macaddr));
       }
       if (info & EAPOL_KEY_SECURE) {
#ifndef IEEE80211_STA_ONLY
@@ -901,6 +935,7 @@ ieee80211_recv_wpa_group_msg1(struct iee
       u_int16_t info;
       u_int8_t kid;
       int keylen;
+       const uint8_t *gtk;

#ifndef IEEE80211_STA_ONLY
       if (ic->ic_opmode != IEEE80211_M_STA &&
@@ -946,23 +981,27 @@ ieee80211_recv_wpa_group_msg1(struct iee
       /* map GTK to 802.11 key */
       kid = (info >> EAPOL_KEY_WPA_KID_SHIFT) & 3;
       k = &ic->ic_nw_keys[kid];
-       memset(k, 0, sizeof(*k));
-       k->k_id = kid;  /* 0-3 */
-       k->k_cipher = ni->ni_rsngroupcipher;
-       k->k_flags = IEEE80211_KEY_GROUP;
-       if (info & EAPOL_KEY_WPA_TX)
-               k->k_flags |= IEEE80211_KEY_TX;
-       k->k_rsc[0] = LE_READ_6(key->rsc);
-       k->k_len = keylen;
-       /* key data field contains the GTK */
-       memcpy(k->k_key, &key[1], k->k_len);
-       /* install the GTK */
-       if ((*ic->ic_set_key)(ic, ni, k) != 0) {
-               IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
-                   IEEE80211_REASON_AUTH_LEAVE);
-               ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
-               return;
-       }
+       gtk = (const uint8_t *)&key[1]; /* key data field contains the GTK */
+       if (ieee80211_must_update_group_key(k, gtk, keylen)) {
+               memset(k, 0, sizeof(*k));
+               k->k_id = kid;  /* 0-3 */
+               k->k_cipher = ni->ni_rsngroupcipher;
+               k->k_flags = IEEE80211_KEY_GROUP;
+               if (info & EAPOL_KEY_WPA_TX)
+                       k->k_flags |= IEEE80211_KEY_TX;
+               k->k_rsc[0] = LE_READ_6(key->rsc);
+               k->k_len = keylen;
+               memcpy(k->k_key, gtk, k->k_len);
+               /* install the GTK */
+               if ((*ic->ic_set_key)(ic, ni, k) != 0) {
+                       IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
+                           IEEE80211_REASON_AUTH_LEAVE);
+                       ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+                       return;
+               }
+       } else
+               printf("%s: reused group key update received from %s\n",
+                   ic->ic_if.if_xname, ether_sprintf(ni->ni_macaddr));
       if (info & EAPOL_KEY_SECURE) {
#ifndef IEEE80211_STA_ONLY
               if (ic->ic_opmode != IEEE80211_M_IBSS ||
Index: sys/net80211/ieee80211_proto.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_proto.c,v
retrieving revision 1.68.2.2
diff -u -p -r1.68.2.2 ieee80211_proto.c
--- sys/net80211/ieee80211_proto.c      2 Mar 2017 09:47:32 -0000       1.68.2.2
+++ sys/net80211/ieee80211_proto.c      29 Aug 2017 21:20:52 -0000
@@ -904,6 +904,7 @@ justcleanup:
                       break;
               }
               ni->ni_rsn_supp_state = RSNA_SUPP_INITIALIZE;
+               ieee80211_crypto_clear_groupkeys(ic);
               break;
       case IEEE80211_S_SCAN:
               ic->ic_flags &= ~IEEE80211_F_SIBSS;
@@ -915,6 +916,7 @@ justcleanup:
               ni->ni_associd = 0;
               ni->ni_rstamp = 0;
               ni->ni_rsn_supp_state = RSNA_SUPP_INITIALIZE;
+               ieee80211_crypto_clear_groupkeys(ic);
               switch (ostate) {
               case IEEE80211_S_INIT:
#ifndef IEEE80211_STA_ONLY
@@ -958,6 +960,7 @@ justcleanup:
               break;
       case IEEE80211_S_AUTH:
               ni->ni_rsn_supp_state = RSNA_SUPP_INITIALIZE;
+               ieee80211_crypto_clear_groupkeys(ic);
               switch (ostate) {
               case IEEE80211_S_INIT:
                       DPRINTF(("invalid transition\n"));