untrusted comment: signature from openbsd 6.1 base secret key
RWQEQa33SgQSEloZjPqFuBpjbCKFPBgnFy5UtpQ8PKrq1HXqPI5u06SBhddcqm4UsdODwT9hqzBpzOxreAC+EIpNW+qzbXaFWQM=

OpenBSD 6.1 errata 027, August 30, 2017

State transition errors could cause reinstallation of old WPA keys.

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

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

Index: sys/net80211/ieee80211_crypto.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_crypto.c,v
retrieving revision 1.69
diff -u -p -r1.69 ieee80211_crypto.c
--- sys/net80211/ieee80211_crypto.c     23 Mar 2017 04:10:10 -0000      1.69
+++ sys/net80211/ieee80211_crypto.c     29 Aug 2017 20:27:26 -0000
@@ -80,7 +80,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) {
@@ -90,12 +89,7 @@ ieee80211_crypto_detach(struct ifnet *if
       }

       /* clear all group keys from memory */
-       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));
-       }
+       ieee80211_crypto_clear_groupkeys(ic);

       /* clear pre-shared key from memory */
       explicit_bzero(ic->ic_psk, IEEE80211_PMK_LEN);
@@ -103,6 +97,19 @@ ieee80211_crypto_detach(struct ifnet *if
#ifndef IEEE80211_STA_ONLY
       timeout_del(&ic->ic_tkip_micfail_timeout);
#endif
+}
+
+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));
+       }
}

/*
Index: sys/net80211/ieee80211_crypto.h
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_crypto.h,v
retrieving revision 1.24
diff -u -p -r1.24 ieee80211_crypto.h
--- sys/net80211/ieee80211_crypto.h     17 Dec 2016 18:35:54 -0000      1.24
+++ sys/net80211/ieee80211_crypto.h     29 Aug 2017 20:28:24 -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.115
diff -u -p -r1.115 ieee80211_node.c
--- sys/net80211/ieee80211_node.c       4 Mar 2017 12:44:27 -0000       1.115
+++ sys/net80211/ieee80211_node.c       29 Aug 2017 20:24:22 -0000
@@ -1532,6 +1532,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.68
diff -u -p -r1.68 ieee80211_node.h
--- sys/net80211/ieee80211_node.h       12 Mar 2017 03:13:50 -0000      1.68
+++ sys/net80211/ieee80211_node.h       29 Aug 2017 20:25:21 -0000
@@ -296,6 +296,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 */
};

RBT_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.28.4.1
diff -u -p -r1.28.4.1 ieee80211_pae_input.c
--- sys/net80211/ieee80211_pae_input.c  1 Aug 2017 22:30:29 -0000       1.28.4.1
+++ sys/net80211/ieee80211_pae_input.c  29 Aug 2017 20:25:59 -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.74
diff -u -p -r1.74 ieee80211_proto.c
--- sys/net80211/ieee80211_proto.c      2 Mar 2017 09:41:27 -0000       1.74
+++ sys/net80211/ieee80211_proto.c      29 Aug 2017 20:29:04 -0000
@@ -903,6 +903,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;
@@ -914,6 +915,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
@@ -957,6 +959,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"));