diff -ur linux-4.13.0-gentoo_/net/mac80211/key.c linux-4.13.0-gentoo/net/mac80211/key.c --- linux-4.13.0-gentoo_/net/mac80211/key.c 2017-09-03 22:56:17.000000000 +0200 +++ linux-4.13.0-gentoo/net/mac80211/key.c 2017-09-10 21:02:23.822346404 +0200 @@ -626,9 +626,21 @@ mutex_lock(&sdata->local->key_mtx); - if (sta && pairwise) + if (sta && pairwise) { old_key = key_mtx_dereference(sdata->local, sta->ptk[idx]); - else if (sta) + if (old_key) + switch (key->conf.cipher) { + /* For now we only fix the issue for CCMP */ + case WLAN_CIPHER_SUITE_CCMP: + /* Only TID=0 seems to be relevant, but that assumption may be wrong... */ + memcpy(&key->u.ccmp.rx_pn_old, old_key->u.ccmp.rx_pn[0], IEEE80211_CCMP_PN_LEN); + key->check_pn_old = true; + break; + } + else + /* No old key, bypass hack code */ + key->check_pn_old = false; + } else if (sta) old_key = key_mtx_dereference(sdata->local, sta->gtk[idx]); else old_key = key_mtx_dereference(sdata->local, sdata->keys[idx]); diff -ur linux-4.13.0-gentoo_/net/mac80211/key.h linux-4.13.0-gentoo/net/mac80211/key.h --- linux-4.13.0-gentoo_/net/mac80211/key.h 2017-09-03 22:56:17.000000000 +0200 +++ linux-4.13.0-gentoo/net/mac80211/key.h 2017-09-10 21:02:54.752438385 +0200 @@ -59,6 +59,7 @@ struct ieee80211_local *local; struct ieee80211_sub_if_data *sdata; struct sta_info *sta; + bool check_pn_old; /* for sdata list */ struct list_head list; @@ -88,6 +89,7 @@ * Management frames. */ u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN]; + u8 rx_pn_old[IEEE80211_CMAC_PN_LEN]; struct crypto_aead *tfm; u32 replays; /* dot11RSNAStatsCCMPReplays */ } ccmp; diff -ur linux-4.13.0-gentoo_/net/mac80211/wpa.c linux-4.13.0-gentoo/net/mac80211/wpa.c --- linux-4.13.0-gentoo_/net/mac80211/wpa.c 2017-09-03 22:56:17.000000000 +0200 +++ linux-4.13.0-gentoo/net/mac80211/wpa.c 2017-09-10 21:08:04.203331545 +0200 @@ -532,6 +532,31 @@ key->u.ccmp.replays++; return RX_DROP_UNUSABLE; } + if (unlikely(key->check_pn_old)) { + /* Code only handles TID=0, which seems to be the only relevant TID for the race */ + if (queue == 0) { + printk ("HACK: virgin key detected, enable HACK code path!"); + print_hex_dump_debug("HACK cnt: ", DUMP_PREFIX_NONE, IEEE80211_CCMP_PN_LEN, 6, key->u.ccmp.rx_pn[queue], IEEE80211_CCMP_PN_LEN, false); + print_hex_dump_debug("HACK old_cnt: ", DUMP_PREFIX_NONE, IEEE80211_CCMP_PN_LEN, 6, key->u.ccmp.rx_pn_old, IEEE80211_CCMP_PN_LEN, false); + print_hex_dump_debug("HACK pn: ", DUMP_PREFIX_NONE, IEEE80211_CCMP_PN_LEN, 6, pn, IEEE80211_CCMP_PN_LEN, false); + + if (memcmp(pn, key->u.ccmp.rx_pn_old, IEEE80211_CCMP_PN_LEN) < 0 || + memcmp(key->u.ccmp.rx_pn[queue], key->u.ccmp.rx_pn_old, IEEE80211_CCMP_PN_LEN) == 0 ) { + /* pn is < the pn from old key or rx_pn_old and rx_pn are identical, complete switch to new key */ + printk ("HACK: Switching key over to normal counter\n"); + memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN); + key->check_pn_old = false; + } else { + /* This case would freeze the wlan on an unpatched kernel */ + printk ("HACK: -RESCUE- new key packet with old pn mitigated\n"); + memcpy(key->u.ccmp.rx_pn_old, pn, IEEE80211_CCMP_PN_LEN); + } + } else { + printk ("HACK: Sanity ERROR - Found a key with check_pn_old set were TID!=0"); + } + } else { + memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN); + } if (!(status->flag & RX_FLAG_DECRYPTED)) { u8 aad[2 * AES_BLOCK_SIZE]; @@ -546,8 +571,6 @@ skb->data + skb->len - mic_len, mic_len)) return RX_DROP_UNUSABLE; } - - memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN); } /* Remove CCMP header and MIC */