iwlwifi: mvm: fix security bug in PN checking
authorSara Sharon <sara.sharon@intel.com>
Tue, 29 Mar 2016 07:56:57 +0000 (10:56 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 30 May 2018 05:50:27 +0000 (07:50 +0200)
[ Upstream commit 5ab2ba931255d8bf03009c06d58dce97de32797c ]

A previous patch allowed the same PN for packets originating from the
same AMSDU by copying PN only for the last packet in the series.

This however is bogus since we cannot assume the last frame will be
received on the same queue, and if it is received on a different ueue
we will end up not incrementing the PN and possibly let the next
packet to have the same PN and pass through.

Change the logic instead to driver explicitly indicate for the second
sub frame and on to be allowed to have the same PN as the first
subframe. Indicate it to mac80211 as well for the fallback queue.

Fixes: f1ae02b186d9 ("iwlwifi: mvm: allow same PN for de-aggregated AMSDU")
Signed-off-by: Sara Sharon <sara.sharon@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
Signed-off-by: Sasha Levin <alexander.levin@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c

index a481eb4..c2bbc8c 100644 (file)
@@ -72,6 +72,7 @@ static inline int iwl_mvm_check_pn(struct iwl_mvm *mvm, struct sk_buff *skb,
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
        struct ieee80211_rx_status *stats = IEEE80211_SKB_RXCB(skb);
        struct iwl_mvm_key_pn *ptk_pn;
+       int res;
        u8 tid, keyidx;
        u8 pn[IEEE80211_CCMP_PN_LEN];
        u8 *extiv;
@@ -128,12 +129,13 @@ static inline int iwl_mvm_check_pn(struct iwl_mvm *mvm, struct sk_buff *skb,
        pn[4] = extiv[1];
        pn[5] = extiv[0];
 
-       if (memcmp(pn, ptk_pn->q[queue].pn[tid],
-                  IEEE80211_CCMP_PN_LEN) <= 0)
+       res = memcmp(pn, ptk_pn->q[queue].pn[tid], IEEE80211_CCMP_PN_LEN);
+       if (res < 0)
+               return -1;
+       if (!res && !(stats->flag & RX_FLAG_ALLOW_SAME_PN))
                return -1;
 
-       if (!(stats->flag & RX_FLAG_AMSDU_MORE))
-               memcpy(ptk_pn->q[queue].pn[tid], pn, IEEE80211_CCMP_PN_LEN);
+       memcpy(ptk_pn->q[queue].pn[tid], pn, IEEE80211_CCMP_PN_LEN);
        stats->flag |= RX_FLAG_PN_VALIDATED;
 
        return 0;
@@ -295,28 +297,21 @@ static void iwl_mvm_rx_csum(struct ieee80211_sta *sta,
 }
 
 /*
- * returns true if a packet outside BA session is a duplicate and
- * should be dropped
+ * returns true if a packet is a duplicate and should be dropped.
+ * Updates AMSDU PN tracking info
  */
-static bool iwl_mvm_is_nonagg_dup(struct ieee80211_sta *sta, int queue,
-                                 struct ieee80211_rx_status *rx_status,
-                                 struct ieee80211_hdr *hdr,
-                                 struct iwl_rx_mpdu_desc *desc)
+static bool iwl_mvm_is_dup(struct ieee80211_sta *sta, int queue,
+                          struct ieee80211_rx_status *rx_status,
+                          struct ieee80211_hdr *hdr,
+                          struct iwl_rx_mpdu_desc *desc)
 {
        struct iwl_mvm_sta *mvm_sta;
        struct iwl_mvm_rxq_dup_data *dup_data;
-       u8 baid, tid, sub_frame_idx;
+       u8 tid, sub_frame_idx;
 
        if (WARN_ON(IS_ERR_OR_NULL(sta)))
                return false;
 
-       baid = (le32_to_cpu(desc->reorder_data) &
-               IWL_RX_MPDU_REORDER_BAID_MASK) >>
-               IWL_RX_MPDU_REORDER_BAID_SHIFT;
-
-       if (baid != IWL_RX_REORDER_DATA_INVALID_BAID)
-               return false;
-
        mvm_sta = iwl_mvm_sta_from_mac80211(sta);
        dup_data = &mvm_sta->dup_data[queue];
 
@@ -346,6 +341,12 @@ static bool iwl_mvm_is_nonagg_dup(struct ieee80211_sta *sta, int queue,
                     dup_data->last_sub_frame[tid] >= sub_frame_idx))
                return true;
 
+       /* Allow same PN as the first subframe for following sub frames */
+       if (dup_data->last_seq[tid] == hdr->seq_ctrl &&
+           sub_frame_idx > dup_data->last_sub_frame[tid] &&
+           desc->mac_flags2 & IWL_RX_MPDU_MFLG2_AMSDU)
+               rx_status->flag |= RX_FLAG_ALLOW_SAME_PN;
+
        dup_data->last_seq[tid] = hdr->seq_ctrl;
        dup_data->last_sub_frame[tid] = sub_frame_idx;
 
@@ -882,7 +883,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
                if (ieee80211_is_data(hdr->frame_control))
                        iwl_mvm_rx_csum(sta, skb, desc);
 
-               if (iwl_mvm_is_nonagg_dup(sta, queue, rx_status, hdr, desc)) {
+               if (iwl_mvm_is_dup(sta, queue, rx_status, hdr, desc)) {
                        kfree_skb(skb);
                        rcu_read_unlock();
                        return;