| From 9acc54beb474c81148e2946603d141cf8716b19f Mon Sep 17 00:00:00 2001 |
| From: Johannes Berg <johannes.berg@intel.com> |
| Date: Fri, 26 Feb 2016 22:13:40 +0100 |
| Subject: mac80211: check PN correctly for GCMP-encrypted fragmented MPDUs |
| |
| From: Johannes Berg <johannes.berg@intel.com> |
| |
| commit 9acc54beb474c81148e2946603d141cf8716b19f upstream. |
| |
| Just like for CCMP we need to check that for GCMP the fragments |
| have PNs that increment by one; the spec was updated to fix this |
| security issue and now has the following text: |
| |
| The receiver shall discard MSDUs and MMPDUs whose constituent |
| MPDU PN values are not incrementing in steps of 1. |
| |
| Adapt the code for CCMP to work for GCMP as well, luckily the |
| relevant fields already alias each other so no code duplication |
| is needed (just check the aliasing with BUILD_BUG_ON.) |
| |
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| net/mac80211/ieee80211_i.h | 2 +- |
| net/mac80211/rx.c | 36 +++++++++++++++++++++++++++--------- |
| 2 files changed, 28 insertions(+), 10 deletions(-) |
| |
| --- a/net/mac80211/ieee80211_i.h |
| +++ b/net/mac80211/ieee80211_i.h |
| @@ -92,7 +92,7 @@ struct ieee80211_fragment_entry { |
| u16 extra_len; |
| u16 last_frag; |
| u8 rx_queue; |
| - bool ccmp; /* Whether fragments were encrypted with CCMP */ |
| + bool check_sequential_pn; /* needed for CCMP/GCMP */ |
| u8 last_pn[6]; /* PN of the last fragment if CCMP was used */ |
| }; |
| |
| --- a/net/mac80211/rx.c |
| +++ b/net/mac80211/rx.c |
| @@ -1754,7 +1754,7 @@ ieee80211_reassemble_add(struct ieee8021 |
| entry->seq = seq; |
| entry->rx_queue = rx_queue; |
| entry->last_frag = frag; |
| - entry->ccmp = 0; |
| + entry->check_sequential_pn = false; |
| entry->extra_len = 0; |
| |
| return entry; |
| @@ -1850,15 +1850,27 @@ ieee80211_rx_h_defragment(struct ieee802 |
| rx->seqno_idx, &(rx->skb)); |
| if (rx->key && |
| (rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP || |
| - rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP_256) && |
| + rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP_256 || |
| + rx->key->conf.cipher == WLAN_CIPHER_SUITE_GCMP || |
| + rx->key->conf.cipher == WLAN_CIPHER_SUITE_GCMP_256) && |
| ieee80211_has_protected(fc)) { |
| int queue = rx->security_idx; |
| - /* Store CCMP PN so that we can verify that the next |
| - * fragment has a sequential PN value. */ |
| - entry->ccmp = 1; |
| + |
| + /* Store CCMP/GCMP PN so that we can verify that the |
| + * next fragment has a sequential PN value. |
| + */ |
| + entry->check_sequential_pn = true; |
| memcpy(entry->last_pn, |
| rx->key->u.ccmp.rx_pn[queue], |
| IEEE80211_CCMP_PN_LEN); |
| + BUILD_BUG_ON(offsetof(struct ieee80211_key, |
| + u.ccmp.rx_pn) != |
| + offsetof(struct ieee80211_key, |
| + u.gcmp.rx_pn)); |
| + BUILD_BUG_ON(sizeof(rx->key->u.ccmp.rx_pn[queue]) != |
| + sizeof(rx->key->u.gcmp.rx_pn[queue])); |
| + BUILD_BUG_ON(IEEE80211_CCMP_PN_LEN != |
| + IEEE80211_GCMP_PN_LEN); |
| } |
| return RX_QUEUED; |
| } |
| @@ -1873,15 +1885,21 @@ ieee80211_rx_h_defragment(struct ieee802 |
| return RX_DROP_MONITOR; |
| } |
| |
| - /* Verify that MPDUs within one MSDU have sequential PN values. |
| - * (IEEE 802.11i, 8.3.3.4.5) */ |
| - if (entry->ccmp) { |
| + /* "The receiver shall discard MSDUs and MMPDUs whose constituent |
| + * MPDU PN values are not incrementing in steps of 1." |
| + * see IEEE P802.11-REVmc/D5.0, 12.5.3.4.4, item d (for CCMP) |
| + * and IEEE P802.11-REVmc/D5.0, 12.5.5.4.4, item d (for GCMP) |
| + */ |
| + if (entry->check_sequential_pn) { |
| int i; |
| u8 pn[IEEE80211_CCMP_PN_LEN], *rpn; |
| int queue; |
| + |
| if (!rx->key || |
| (rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP && |
| - rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP_256)) |
| + rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP_256 && |
| + rx->key->conf.cipher != WLAN_CIPHER_SUITE_GCMP && |
| + rx->key->conf.cipher != WLAN_CIPHER_SUITE_GCMP_256)) |
| return RX_DROP_UNUSABLE; |
| memcpy(pn, entry->last_pn, IEEE80211_CCMP_PN_LEN); |
| for (i = IEEE80211_CCMP_PN_LEN - 1; i >= 0; i--) { |