mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-23 23:42:43 +00:00
mac80211: backport upstream fixes for FragAttacks
From the patch series description: Several security issues in the 802.11 implementations were found by Mathy Vanhoef (New York University Abu Dhabi), who has published all the details at https://papers.mathyvanhoef.com/usenix2021.pdf Specifically, the following CVEs were assigned: * CVE-2020-24586 - Fragmentation cache not cleared on reconnection * CVE-2020-24587 - Reassembling fragments encrypted under different keys * CVE-2020-24588 - Accepting non-SPP A-MSDU frames, which leads to payload being parsed as an L2 frame under an A-MSDU bit toggling attack * CVE-2020-26139 - Forwarding EAPOL from unauthenticated sender * CVE-2020-26140 - Accepting plaintext data frames in protected networks * CVE-2020-26141 - Not verifying TKIP MIC of fragmented frames * CVE-2020-26142 - Processing fragmented frames as full frames * CVE-2020-26143 - Accepting fragmented plaintext frames in protected networks * CVE-2020-26144 - Always accepting unencrypted A-MSDU frames that start with RFC1042 header with EAPOL ethertype * CVE-2020-26145 - Accepting plaintext broadcast fragments as full frames * CVE-2020-26146 - Reassembling encrypted fragments with non-consecutive packet numbers * CVE-2020-26147 - Reassembling mixed encrypted/plaintext fragments In general, the scope of these attacks is that they may allow an attacker to * inject L2 frames that they can more or less control (depending on the vulnerability and attack method) into an otherwise protected network; * exfiltrate (some) network data under certain conditions, this is specific to the fragmentation issues. A subset of these issues is known to apply to the Linux IEEE 802.11 implementation (mac80211). Where it is affected, the attached patches fix the issues, even if not all of them reference the exact CVE IDs. In addition, driver and/or firmware updates may be necessary, as well as potentially more fixes to mac80211, depending on how drivers are using it. Specifically, for Intel devices, firmware needs to be updated to the most recently released versions (which was done without any reference to the security issues) to address some of the vulnerabilities. To have a single set of patches, I'm also including patches for the ath10k and ath11k drivers here. We currently don't have information about how other drivers are, if at all, affected. Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
parent
5a9608102b
commit
025bd93f36
@ -37,7 +37,7 @@
|
||||
void ath10k_thermal_event_temperature(struct ath10k *ar, int temperature);
|
||||
--- a/local-symbols
|
||||
+++ b/local-symbols
|
||||
@@ -142,6 +142,7 @@ ATH10K_SNOC=
|
||||
@@ -143,6 +143,7 @@ ATH10K_SNOC=
|
||||
ATH10K_DEBUG=
|
||||
ATH10K_DEBUGFS=
|
||||
ATH10K_SPECTRAL=
|
||||
|
@ -0,0 +1,180 @@
|
||||
From: Wen Gong <wgong@codeaurora.org>
|
||||
Date: Tue, 11 May 2021 20:02:52 +0200
|
||||
Subject: [PATCH] ath10k: add CCMP PN replay protection for fragmented
|
||||
frames for PCIe
|
||||
|
||||
PN replay check for not fragmented frames is finished in the firmware,
|
||||
but this was not done for fragmented frames when ath10k is used with
|
||||
QCA6174/QCA6377 PCIe. mac80211 has the function
|
||||
ieee80211_rx_h_defragment() for PN replay check for fragmented frames,
|
||||
but this does not get checked with QCA6174 due to the
|
||||
ieee80211_has_protected() condition not matching the cleared Protected
|
||||
bit case.
|
||||
|
||||
Validate the PN of received fragmented frames within ath10k when CCMP is
|
||||
used and drop the fragment if the PN is not correct (incremented by
|
||||
exactly one from the previous fragment). This applies only for
|
||||
QCA6174/QCA6377 PCIe.
|
||||
|
||||
Tested-on: QCA6174 hw3.2 PCI WLAN.RM.4.4.1-00110-QCARMSWP-1
|
||||
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Wen Gong <wgong@codeaurora.org>
|
||||
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath10k/htt.h
|
||||
+++ b/drivers/net/wireless/ath/ath10k/htt.h
|
||||
@@ -846,6 +846,7 @@ enum htt_security_types {
|
||||
|
||||
#define ATH10K_HTT_TXRX_PEER_SECURITY_MAX 2
|
||||
#define ATH10K_TXRX_NUM_EXT_TIDS 19
|
||||
+#define ATH10K_TXRX_NON_QOS_TID 16
|
||||
|
||||
enum htt_security_flags {
|
||||
#define HTT_SECURITY_TYPE_MASK 0x7F
|
||||
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
|
||||
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
|
||||
@@ -1746,16 +1746,87 @@ static void ath10k_htt_rx_h_csum_offload
|
||||
msdu->ip_summed = ath10k_htt_rx_get_csum_state(msdu);
|
||||
}
|
||||
|
||||
+static u64 ath10k_htt_rx_h_get_pn(struct ath10k *ar, struct sk_buff *skb,
|
||||
+ u16 offset,
|
||||
+ enum htt_rx_mpdu_encrypt_type enctype)
|
||||
+{
|
||||
+ struct ieee80211_hdr *hdr;
|
||||
+ u64 pn = 0;
|
||||
+ u8 *ehdr;
|
||||
+
|
||||
+ hdr = (struct ieee80211_hdr *)(skb->data + offset);
|
||||
+ ehdr = skb->data + offset + ieee80211_hdrlen(hdr->frame_control);
|
||||
+
|
||||
+ if (enctype == HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2) {
|
||||
+ pn = ehdr[0];
|
||||
+ pn |= (u64)ehdr[1] << 8;
|
||||
+ pn |= (u64)ehdr[4] << 16;
|
||||
+ pn |= (u64)ehdr[5] << 24;
|
||||
+ pn |= (u64)ehdr[6] << 32;
|
||||
+ pn |= (u64)ehdr[7] << 40;
|
||||
+ }
|
||||
+ return pn;
|
||||
+}
|
||||
+
|
||||
+static bool ath10k_htt_rx_h_frag_pn_check(struct ath10k *ar,
|
||||
+ struct sk_buff *skb,
|
||||
+ u16 peer_id,
|
||||
+ u16 offset,
|
||||
+ enum htt_rx_mpdu_encrypt_type enctype)
|
||||
+{
|
||||
+ struct ath10k_peer *peer;
|
||||
+ union htt_rx_pn_t *last_pn, new_pn = {0};
|
||||
+ struct ieee80211_hdr *hdr;
|
||||
+ bool more_frags;
|
||||
+ u8 tid, frag_number;
|
||||
+ u32 seq;
|
||||
+
|
||||
+ peer = ath10k_peer_find_by_id(ar, peer_id);
|
||||
+ if (!peer) {
|
||||
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "invalid peer for frag pn check\n");
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ hdr = (struct ieee80211_hdr *)(skb->data + offset);
|
||||
+ if (ieee80211_is_data_qos(hdr->frame_control))
|
||||
+ tid = ieee80211_get_tid(hdr);
|
||||
+ else
|
||||
+ tid = ATH10K_TXRX_NON_QOS_TID;
|
||||
+
|
||||
+ last_pn = &peer->frag_tids_last_pn[tid];
|
||||
+ new_pn.pn48 = ath10k_htt_rx_h_get_pn(ar, skb, offset, enctype);
|
||||
+ more_frags = ieee80211_has_morefrags(hdr->frame_control);
|
||||
+ frag_number = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG;
|
||||
+ seq = (__le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4;
|
||||
+
|
||||
+ if (frag_number == 0) {
|
||||
+ last_pn->pn48 = new_pn.pn48;
|
||||
+ peer->frag_tids_seq[tid] = seq;
|
||||
+ } else {
|
||||
+ if (seq != peer->frag_tids_seq[tid])
|
||||
+ return false;
|
||||
+
|
||||
+ if (new_pn.pn48 != last_pn->pn48 + 1)
|
||||
+ return false;
|
||||
+
|
||||
+ last_pn->pn48 = new_pn.pn48;
|
||||
+ }
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
|
||||
struct sk_buff_head *amsdu,
|
||||
struct ieee80211_rx_status *status,
|
||||
bool fill_crypt_header,
|
||||
u8 *rx_hdr,
|
||||
- enum ath10k_pkt_rx_err *err)
|
||||
+ enum ath10k_pkt_rx_err *err,
|
||||
+ u16 peer_id,
|
||||
+ bool frag)
|
||||
{
|
||||
struct sk_buff *first;
|
||||
struct sk_buff *last;
|
||||
- struct sk_buff *msdu;
|
||||
+ struct sk_buff *msdu, *temp;
|
||||
struct htt_rx_desc *rxd;
|
||||
struct ieee80211_hdr *hdr;
|
||||
enum htt_rx_mpdu_encrypt_type enctype;
|
||||
@@ -1768,6 +1839,7 @@ static void ath10k_htt_rx_h_mpdu(struct
|
||||
bool is_decrypted;
|
||||
bool is_mgmt;
|
||||
u32 attention;
|
||||
+ bool frag_pn_check = true;
|
||||
|
||||
if (skb_queue_empty(amsdu))
|
||||
return;
|
||||
@@ -1866,6 +1938,24 @@ static void ath10k_htt_rx_h_mpdu(struct
|
||||
}
|
||||
|
||||
skb_queue_walk(amsdu, msdu) {
|
||||
+ if (frag && !fill_crypt_header && is_decrypted &&
|
||||
+ enctype == HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2)
|
||||
+ frag_pn_check = ath10k_htt_rx_h_frag_pn_check(ar,
|
||||
+ msdu,
|
||||
+ peer_id,
|
||||
+ 0,
|
||||
+ enctype);
|
||||
+
|
||||
+ if (!frag_pn_check) {
|
||||
+ /* Discard the fragment with invalid PN */
|
||||
+ temp = msdu->prev;
|
||||
+ __skb_unlink(msdu, amsdu);
|
||||
+ dev_kfree_skb_any(msdu);
|
||||
+ msdu = temp;
|
||||
+ frag_pn_check = true;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
ath10k_htt_rx_h_csum_offload(msdu);
|
||||
ath10k_htt_rx_h_undecap(ar, msdu, status, first_hdr, enctype,
|
||||
is_decrypted);
|
||||
@@ -2071,7 +2161,8 @@ static int ath10k_htt_rx_handle_amsdu(st
|
||||
ath10k_htt_rx_h_unchain(ar, &amsdu, &drop_cnt, &unchain_cnt);
|
||||
|
||||
ath10k_htt_rx_h_filter(ar, &amsdu, rx_status, &drop_cnt_filter);
|
||||
- ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status, true, first_hdr, &err);
|
||||
+ ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status, true, first_hdr, &err, 0,
|
||||
+ false);
|
||||
msdus_to_queue = skb_queue_len(&amsdu);
|
||||
ath10k_htt_rx_h_enqueue(ar, &amsdu, rx_status);
|
||||
|
||||
@@ -3027,7 +3118,7 @@ static int ath10k_htt_rx_in_ord_ind(stru
|
||||
ath10k_htt_rx_h_ppdu(ar, &amsdu, status, vdev_id);
|
||||
ath10k_htt_rx_h_filter(ar, &amsdu, status, NULL);
|
||||
ath10k_htt_rx_h_mpdu(ar, &amsdu, status, false, NULL,
|
||||
- NULL);
|
||||
+ NULL, peer_id, frag);
|
||||
ath10k_htt_rx_h_enqueue(ar, &amsdu, status);
|
||||
break;
|
||||
case -EAGAIN:
|
@ -0,0 +1,66 @@
|
||||
From: Wen Gong <wgong@codeaurora.org>
|
||||
Date: Tue, 11 May 2021 20:02:53 +0200
|
||||
Subject: [PATCH] ath10k: drop fragments with multicast DA for PCIe
|
||||
|
||||
Fragmentation is not used with multicast frames. Discard unexpected
|
||||
fragments with multicast DA. This fixes CVE-2020-26145.
|
||||
|
||||
Tested-on: QCA6174 hw3.2 PCI WLAN.RM.4.4.1-00110-QCARMSWP-1
|
||||
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Wen Gong <wgong@codeaurora.org>
|
||||
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
|
||||
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
|
||||
@@ -1768,6 +1768,16 @@ static u64 ath10k_htt_rx_h_get_pn(struct
|
||||
return pn;
|
||||
}
|
||||
|
||||
+static bool ath10k_htt_rx_h_frag_multicast_check(struct ath10k *ar,
|
||||
+ struct sk_buff *skb,
|
||||
+ u16 offset)
|
||||
+{
|
||||
+ struct ieee80211_hdr *hdr;
|
||||
+
|
||||
+ hdr = (struct ieee80211_hdr *)(skb->data + offset);
|
||||
+ return !is_multicast_ether_addr(hdr->addr1);
|
||||
+}
|
||||
+
|
||||
static bool ath10k_htt_rx_h_frag_pn_check(struct ath10k *ar,
|
||||
struct sk_buff *skb,
|
||||
u16 peer_id,
|
||||
@@ -1839,7 +1849,7 @@ static void ath10k_htt_rx_h_mpdu(struct
|
||||
bool is_decrypted;
|
||||
bool is_mgmt;
|
||||
u32 attention;
|
||||
- bool frag_pn_check = true;
|
||||
+ bool frag_pn_check = true, multicast_check = true;
|
||||
|
||||
if (skb_queue_empty(amsdu))
|
||||
return;
|
||||
@@ -1946,13 +1956,20 @@ static void ath10k_htt_rx_h_mpdu(struct
|
||||
0,
|
||||
enctype);
|
||||
|
||||
- if (!frag_pn_check) {
|
||||
- /* Discard the fragment with invalid PN */
|
||||
+ if (frag)
|
||||
+ multicast_check = ath10k_htt_rx_h_frag_multicast_check(ar,
|
||||
+ msdu,
|
||||
+ 0);
|
||||
+
|
||||
+ if (!frag_pn_check || !multicast_check) {
|
||||
+ /* Discard the fragment with invalid PN or multicast DA
|
||||
+ */
|
||||
temp = msdu->prev;
|
||||
__skb_unlink(msdu, amsdu);
|
||||
dev_kfree_skb_any(msdu);
|
||||
msdu = temp;
|
||||
frag_pn_check = true;
|
||||
+ multicast_check = true;
|
||||
continue;
|
||||
}
|
||||
|
@ -0,0 +1,40 @@
|
||||
From: Wen Gong <wgong@codeaurora.org>
|
||||
Date: Tue, 11 May 2021 20:02:54 +0200
|
||||
Subject: [PATCH] ath10k: drop fragments with multicast DA for SDIO
|
||||
|
||||
Fragmentation is not used with multicast frames. Discard unexpected
|
||||
fragments with multicast DA. This fixes CVE-2020-26145.
|
||||
|
||||
Tested-on: QCA6174 hw3.2 SDIO WLAN.RMH.4.4.1-00049
|
||||
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Wen Gong <wgong@codeaurora.org>
|
||||
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
|
||||
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
|
||||
@@ -2617,6 +2617,13 @@ static bool ath10k_htt_rx_proc_rx_frag_i
|
||||
rx_desc = (struct htt_hl_rx_desc *)(skb->data + tot_hdr_len);
|
||||
rx_desc_info = __le32_to_cpu(rx_desc->info);
|
||||
|
||||
+ hdr = (struct ieee80211_hdr *)((u8 *)rx_desc + rx_hl->fw_desc.len);
|
||||
+
|
||||
+ if (is_multicast_ether_addr(hdr->addr1)) {
|
||||
+ /* Discard the fragment with multicast DA */
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
if (!MS(rx_desc_info, HTT_RX_DESC_HL_INFO_ENCRYPTED)) {
|
||||
spin_unlock_bh(&ar->data_lock);
|
||||
return ath10k_htt_rx_proc_rx_ind_hl(htt, &resp->rx_ind_hl, skb,
|
||||
@@ -2624,8 +2631,6 @@ static bool ath10k_htt_rx_proc_rx_frag_i
|
||||
HTT_RX_NON_TKIP_MIC);
|
||||
}
|
||||
|
||||
- hdr = (struct ieee80211_hdr *)((u8 *)rx_desc + rx_hl->fw_desc.len);
|
||||
-
|
||||
if (ieee80211_has_retry(hdr->frame_control))
|
||||
goto err;
|
||||
|
@ -0,0 +1,54 @@
|
||||
From: Wen Gong <wgong@codeaurora.org>
|
||||
Date: Tue, 11 May 2021 20:02:55 +0200
|
||||
Subject: [PATCH] ath10k: drop MPDU which has discard flag set by firmware
|
||||
for SDIO
|
||||
|
||||
When the discard flag is set by the firmware for an MPDU, it should be
|
||||
dropped. This allows a mitigation for CVE-2020-24588 to be implemented
|
||||
in the firmware.
|
||||
|
||||
Tested-on: QCA6174 hw3.2 SDIO WLAN.RMH.4.4.1-00049
|
||||
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Wen Gong <wgong@codeaurora.org>
|
||||
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
|
||||
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
|
||||
@@ -2312,6 +2312,11 @@ static bool ath10k_htt_rx_proc_rx_ind_hl
|
||||
fw_desc = &rx->fw_desc;
|
||||
rx_desc_len = fw_desc->len;
|
||||
|
||||
+ if (fw_desc->u.bits.discard) {
|
||||
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt discard mpdu\n");
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
/* I have not yet seen any case where num_mpdu_ranges > 1.
|
||||
* qcacld does not seem handle that case either, so we introduce the
|
||||
* same limitiation here as well.
|
||||
--- a/drivers/net/wireless/ath/ath10k/rx_desc.h
|
||||
+++ b/drivers/net/wireless/ath/ath10k/rx_desc.h
|
||||
@@ -1282,7 +1282,19 @@ struct fw_rx_desc_base {
|
||||
#define FW_RX_DESC_UDP (1 << 6)
|
||||
|
||||
struct fw_rx_desc_hl {
|
||||
- u8 info0;
|
||||
+ union {
|
||||
+ struct {
|
||||
+ u8 discard:1,
|
||||
+ forward:1,
|
||||
+ any_err:1,
|
||||
+ dup_err:1,
|
||||
+ reserved:1,
|
||||
+ inspect:1,
|
||||
+ extension:2;
|
||||
+ } bits;
|
||||
+ u8 info0;
|
||||
+ } u;
|
||||
+
|
||||
u8 version;
|
||||
u8 len;
|
||||
u8 flags;
|
@ -0,0 +1,48 @@
|
||||
From: Wen Gong <wgong@codeaurora.org>
|
||||
Date: Tue, 11 May 2021 20:02:56 +0200
|
||||
Subject: [PATCH] ath10k: Fix TKIP Michael MIC verification for PCIe
|
||||
|
||||
TKIP Michael MIC was not verified properly for PCIe cases since the
|
||||
validation steps in ieee80211_rx_h_michael_mic_verify() in mac80211 did
|
||||
not get fully executed due to unexpected flag values in
|
||||
ieee80211_rx_status.
|
||||
|
||||
Fix this by setting the flags property to meet mac80211 expectations for
|
||||
performing Michael MIC validation there. This fixes CVE-2020-26141. It
|
||||
does the same as ath10k_htt_rx_proc_rx_ind_hl() for SDIO which passed
|
||||
MIC verification case. This applies only to QCA6174/QCA9377 PCIe.
|
||||
|
||||
Tested-on: QCA6174 hw3.2 PCI WLAN.RM.4.4.1-00110-QCARMSWP-1
|
||||
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Wen Gong <wgong@codeaurora.org>
|
||||
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
|
||||
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
|
||||
@@ -1974,6 +1974,11 @@ static void ath10k_htt_rx_h_mpdu(struct
|
||||
}
|
||||
|
||||
ath10k_htt_rx_h_csum_offload(msdu);
|
||||
+
|
||||
+ if (frag && !fill_crypt_header &&
|
||||
+ enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
|
||||
+ status->flag &= ~RX_FLAG_MMIC_STRIPPED;
|
||||
+
|
||||
ath10k_htt_rx_h_undecap(ar, msdu, status, first_hdr, enctype,
|
||||
is_decrypted);
|
||||
|
||||
@@ -1991,6 +1996,11 @@ static void ath10k_htt_rx_h_mpdu(struct
|
||||
|
||||
hdr = (void *)msdu->data;
|
||||
hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
|
||||
+
|
||||
+ if (frag && !fill_crypt_header &&
|
||||
+ enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
|
||||
+ status->flag &= ~RX_FLAG_IV_STRIPPED &
|
||||
+ ~RX_FLAG_MMIC_STRIPPED;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,109 @@
|
||||
From: Sriram R <srirrama@codeaurora.org>
|
||||
Date: Tue, 11 May 2021 20:02:57 +0200
|
||||
Subject: [PATCH] ath10k: Validate first subframe of A-MSDU before
|
||||
processing the list
|
||||
|
||||
In certain scenarios a normal MSDU can be received as an A-MSDU when
|
||||
the A-MSDU present bit of a QoS header gets flipped during reception.
|
||||
Since this bit is unauthenticated, the hardware crypto engine can pass
|
||||
the frame to the driver without any error indication.
|
||||
|
||||
This could result in processing unintended subframes collected in the
|
||||
A-MSDU list. Hence, validate A-MSDU list by checking if the first frame
|
||||
has a valid subframe header.
|
||||
|
||||
Comparing the non-aggregated MSDU and an A-MSDU, the fields of the first
|
||||
subframe DA matches the LLC/SNAP header fields of a normal MSDU.
|
||||
In order to avoid processing such frames, add a validation to
|
||||
filter such A-MSDU frames where the first subframe header DA matches
|
||||
with the LLC/SNAP header pattern.
|
||||
|
||||
Tested-on: QCA9984 hw1.0 PCI 10.4-3.10-00047
|
||||
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Sriram R <srirrama@codeaurora.org>
|
||||
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
|
||||
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
|
||||
@@ -2108,14 +2108,62 @@ static void ath10k_htt_rx_h_unchain(stru
|
||||
ath10k_unchain_msdu(amsdu, unchain_cnt);
|
||||
}
|
||||
|
||||
+static bool ath10k_htt_rx_validate_amsdu(struct ath10k *ar,
|
||||
+ struct sk_buff_head *amsdu)
|
||||
+{
|
||||
+ u8 *subframe_hdr;
|
||||
+ struct sk_buff *first;
|
||||
+ bool is_first, is_last;
|
||||
+ struct htt_rx_desc *rxd;
|
||||
+ struct ieee80211_hdr *hdr;
|
||||
+ size_t hdr_len, crypto_len;
|
||||
+ enum htt_rx_mpdu_encrypt_type enctype;
|
||||
+ int bytes_aligned = ar->hw_params.decap_align_bytes;
|
||||
+
|
||||
+ first = skb_peek(amsdu);
|
||||
+
|
||||
+ rxd = (void *)first->data - sizeof(*rxd);
|
||||
+ hdr = (void *)rxd->rx_hdr_status;
|
||||
+
|
||||
+ is_first = !!(rxd->msdu_end.common.info0 &
|
||||
+ __cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU));
|
||||
+ is_last = !!(rxd->msdu_end.common.info0 &
|
||||
+ __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU));
|
||||
+
|
||||
+ /* Return in case of non-aggregated msdu */
|
||||
+ if (is_first && is_last)
|
||||
+ return true;
|
||||
+
|
||||
+ /* First msdu flag is not set for the first msdu of the list */
|
||||
+ if (!is_first)
|
||||
+ return false;
|
||||
+
|
||||
+ enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
|
||||
+ RX_MPDU_START_INFO0_ENCRYPT_TYPE);
|
||||
+
|
||||
+ hdr_len = ieee80211_hdrlen(hdr->frame_control);
|
||||
+ crypto_len = ath10k_htt_rx_crypto_param_len(ar, enctype);
|
||||
+
|
||||
+ subframe_hdr = (u8 *)hdr + round_up(hdr_len, bytes_aligned) +
|
||||
+ crypto_len;
|
||||
+
|
||||
+ /* Validate if the amsdu has a proper first subframe.
|
||||
+ * There are chances a single msdu can be received as amsdu when
|
||||
+ * the unauthenticated amsdu flag of a QoS header
|
||||
+ * gets flipped in non-SPP AMSDU's, in such cases the first
|
||||
+ * subframe has llc/snap header in place of a valid da.
|
||||
+ * return false if the da matches rfc1042 pattern
|
||||
+ */
|
||||
+ if (ether_addr_equal(subframe_hdr, rfc1042_header))
|
||||
+ return false;
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
static bool ath10k_htt_rx_amsdu_allowed(struct ath10k *ar,
|
||||
struct sk_buff_head *amsdu,
|
||||
struct ieee80211_rx_status *rx_status)
|
||||
{
|
||||
- /* FIXME: It might be a good idea to do some fuzzy-testing to drop
|
||||
- * invalid/dangerous frames.
|
||||
- */
|
||||
-
|
||||
if (!rx_status->freq) {
|
||||
ath10k_dbg(ar, ATH10K_DBG_HTT, "no channel configured; ignoring frame(s)!\n");
|
||||
return false;
|
||||
@@ -2126,6 +2174,11 @@ static bool ath10k_htt_rx_amsdu_allowed(
|
||||
return false;
|
||||
}
|
||||
|
||||
+ if (!ath10k_htt_rx_validate_amsdu(ar, amsdu)) {
|
||||
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "invalid amsdu received\n");
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
return true;
|
||||
}
|
||||
|
@ -82,7 +82,7 @@
|
||||
help
|
||||
--- a/local-symbols
|
||||
+++ b/local-symbols
|
||||
@@ -85,6 +85,7 @@ ADM8211=
|
||||
@@ -86,6 +86,7 @@ ADM8211=
|
||||
ATH_COMMON=
|
||||
WLAN_VENDOR_ATH=
|
||||
ATH_DEBUG=
|
||||
|
@ -371,7 +371,7 @@
|
||||
|
||||
--- a/local-symbols
|
||||
+++ b/local-symbols
|
||||
@@ -112,6 +112,7 @@ ATH9K_WOW=
|
||||
@@ -113,6 +113,7 @@ ATH9K_WOW=
|
||||
ATH9K_RFKILL=
|
||||
ATH9K_CHANNEL_CONTEXT=
|
||||
ATH9K_PCOEM=
|
||||
|
@ -26,7 +26,7 @@ Forwarded: https://patchwork.kernel.org/patch/11367055/
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath10k/htt.h
|
||||
+++ b/drivers/net/wireless/ath/ath10k/htt.h
|
||||
@@ -2242,7 +2242,7 @@ struct htt_rx_chan_info {
|
||||
@@ -2243,7 +2243,7 @@ struct htt_rx_chan_info {
|
||||
* Should be: sizeof(struct htt_host_rx_desc) + max rx MSDU size,
|
||||
* rounded up to a cache line size.
|
||||
*/
|
||||
|
@ -114,7 +114,7 @@ v13:
|
||||
ath10k_core-$(CONFIG_DEV_COREDUMP) += coredump.o
|
||||
--- a/local-symbols
|
||||
+++ b/local-symbols
|
||||
@@ -145,6 +145,7 @@ ATH10K_DEBUG=
|
||||
@@ -146,6 +146,7 @@ ATH10K_DEBUG=
|
||||
ATH10K_DEBUGFS=
|
||||
ATH10K_SPECTRAL=
|
||||
ATH10K_THERMAL=
|
||||
|
@ -1,6 +1,6 @@
|
||||
--- a/local-symbols
|
||||
+++ b/local-symbols
|
||||
@@ -332,6 +332,7 @@ RT2X00_LIB_FIRMWARE=
|
||||
@@ -333,6 +333,7 @@ RT2X00_LIB_FIRMWARE=
|
||||
RT2X00_LIB_CRYPTO=
|
||||
RT2X00_LIB_LEDS=
|
||||
RT2X00_LIB_DEBUGFS=
|
||||
|
@ -0,0 +1,69 @@
|
||||
From: Mathy Vanhoef <Mathy.Vanhoef@kuleuven.be>
|
||||
Date: Tue, 11 May 2021 20:02:42 +0200
|
||||
Subject: [PATCH] mac80211: assure all fragments are encrypted
|
||||
|
||||
Do not mix plaintext and encrypted fragments in protected Wi-Fi
|
||||
networks. This fixes CVE-2020-26147.
|
||||
|
||||
Previously, an attacker was able to first forward a legitimate encrypted
|
||||
fragment towards a victim, followed by a plaintext fragment. The
|
||||
encrypted and plaintext fragment would then be reassembled. For further
|
||||
details see Section 6.3 and Appendix D in the paper "Fragment and Forge:
|
||||
Breaking Wi-Fi Through Frame Aggregation and Fragmentation".
|
||||
|
||||
Because of this change there are now two equivalent conditions in the
|
||||
code to determine if a received fragment requires sequential PNs, so we
|
||||
also move this test to a separate function to make the code easier to
|
||||
maintain.
|
||||
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Mathy Vanhoef <Mathy.Vanhoef@kuleuven.be>
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/rx.c
|
||||
+++ b/net/mac80211/rx.c
|
||||
@@ -2204,6 +2204,16 @@ ieee80211_reassemble_find(struct ieee802
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+static bool requires_sequential_pn(struct ieee80211_rx_data *rx, __le16 fc)
|
||||
+{
|
||||
+ return 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_GCMP ||
|
||||
+ rx->key->conf.cipher == WLAN_CIPHER_SUITE_GCMP_256) &&
|
||||
+ ieee80211_has_protected(fc);
|
||||
+}
|
||||
+
|
||||
static ieee80211_rx_result debug_noinline
|
||||
ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
|
||||
{
|
||||
@@ -2248,12 +2258,7 @@ ieee80211_rx_h_defragment(struct ieee802
|
||||
/* This is the first fragment of a new frame. */
|
||||
entry = ieee80211_reassemble_add(rx->sdata, frag, seq,
|
||||
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_GCMP ||
|
||||
- rx->key->conf.cipher == WLAN_CIPHER_SUITE_GCMP_256) &&
|
||||
- ieee80211_has_protected(fc)) {
|
||||
+ if (requires_sequential_pn(rx, fc)) {
|
||||
int queue = rx->security_idx;
|
||||
|
||||
/* Store CCMP/GCMP PN so that we can verify that the
|
||||
@@ -2295,11 +2300,7 @@ ieee80211_rx_h_defragment(struct ieee802
|
||||
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_GCMP &&
|
||||
- rx->key->conf.cipher != WLAN_CIPHER_SUITE_GCMP_256))
|
||||
+ if (!requires_sequential_pn(rx, fc))
|
||||
return RX_DROP_UNUSABLE;
|
||||
memcpy(pn, entry->last_pn, IEEE80211_CCMP_PN_LEN);
|
||||
for (i = IEEE80211_CCMP_PN_LEN - 1; i >= 0; i--) {
|
@ -0,0 +1,87 @@
|
||||
From: Mathy Vanhoef <Mathy.Vanhoef@kuleuven.be>
|
||||
Date: Tue, 11 May 2021 20:02:43 +0200
|
||||
Subject: [PATCH] mac80211: prevent mixed key and fragment cache attacks
|
||||
|
||||
Simultaneously prevent mixed key attacks (CVE-2020-24587) and fragment
|
||||
cache attacks (CVE-2020-24586). This is accomplished by assigning a
|
||||
unique color to every key (per interface) and using this to track which
|
||||
key was used to decrypt a fragment. When reassembling frames, it is
|
||||
now checked whether all fragments were decrypted using the same key.
|
||||
|
||||
To assure that fragment cache attacks are also prevented, the ID that is
|
||||
assigned to keys is unique even over (re)associations and (re)connects.
|
||||
This means fragments separated by a (re)association or (re)connect will
|
||||
not be reassembled. Because mac80211 now also prevents the reassembly of
|
||||
mixed encrypted and plaintext fragments, all cache attacks are prevented.
|
||||
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Mathy Vanhoef <Mathy.Vanhoef@kuleuven.be>
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/ieee80211_i.h
|
||||
+++ b/net/mac80211/ieee80211_i.h
|
||||
@@ -97,6 +97,7 @@ struct ieee80211_fragment_entry {
|
||||
u8 rx_queue;
|
||||
bool check_sequential_pn; /* needed for CCMP/GCMP */
|
||||
u8 last_pn[6]; /* PN of the last fragment if CCMP was used */
|
||||
+ unsigned int key_color;
|
||||
};
|
||||
|
||||
|
||||
--- a/net/mac80211/key.c
|
||||
+++ b/net/mac80211/key.c
|
||||
@@ -799,6 +799,7 @@ int ieee80211_key_link(struct ieee80211_
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
struct sta_info *sta)
|
||||
{
|
||||
+ static atomic_t key_color = ATOMIC_INIT(0);
|
||||
struct ieee80211_key *old_key;
|
||||
int idx = key->conf.keyidx;
|
||||
bool pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE;
|
||||
@@ -850,6 +851,12 @@ int ieee80211_key_link(struct ieee80211_
|
||||
key->sdata = sdata;
|
||||
key->sta = sta;
|
||||
|
||||
+ /*
|
||||
+ * Assign a unique ID to every key so we can easily prevent mixed
|
||||
+ * key and fragment cache attacks.
|
||||
+ */
|
||||
+ key->color = atomic_inc_return(&key_color);
|
||||
+
|
||||
increment_tailroom_need_count(sdata);
|
||||
|
||||
ret = ieee80211_key_replace(sdata, sta, pairwise, old_key, key);
|
||||
--- a/net/mac80211/key.h
|
||||
+++ b/net/mac80211/key.h
|
||||
@@ -128,6 +128,8 @@ struct ieee80211_key {
|
||||
} debugfs;
|
||||
#endif
|
||||
|
||||
+ unsigned int color;
|
||||
+
|
||||
/*
|
||||
* key config, must be last because it contains key
|
||||
* material as variable length member
|
||||
--- a/net/mac80211/rx.c
|
||||
+++ b/net/mac80211/rx.c
|
||||
@@ -2265,6 +2265,7 @@ ieee80211_rx_h_defragment(struct ieee802
|
||||
* next fragment has a sequential PN value.
|
||||
*/
|
||||
entry->check_sequential_pn = true;
|
||||
+ entry->key_color = rx->key->color;
|
||||
memcpy(entry->last_pn,
|
||||
rx->key->u.ccmp.rx_pn[queue],
|
||||
IEEE80211_CCMP_PN_LEN);
|
||||
@@ -2302,6 +2303,11 @@ ieee80211_rx_h_defragment(struct ieee802
|
||||
|
||||
if (!requires_sequential_pn(rx, fc))
|
||||
return RX_DROP_UNUSABLE;
|
||||
+
|
||||
+ /* Prevent mixed key and fragment cache attacks */
|
||||
+ if (entry->key_color != rx->key->color)
|
||||
+ return RX_DROP_UNUSABLE;
|
||||
+
|
||||
memcpy(pn, entry->last_pn, IEEE80211_CCMP_PN_LEN);
|
||||
for (i = IEEE80211_CCMP_PN_LEN - 1; i >= 0; i--) {
|
||||
pn[i]++;
|
@ -0,0 +1,66 @@
|
||||
From: Mathy Vanhoef <Mathy.Vanhoef@kuleuven.be>
|
||||
Date: Tue, 11 May 2021 20:02:44 +0200
|
||||
Subject: [PATCH] mac80211: properly handle A-MSDUs that start with an
|
||||
RFC 1042 header
|
||||
|
||||
Properly parse A-MSDUs whose first 6 bytes happen to equal a rfc1042
|
||||
header. This can occur in practice when the destination MAC address
|
||||
equals AA:AA:03:00:00:00. More importantly, this simplifies the next
|
||||
patch to mitigate A-MSDU injection attacks.
|
||||
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Mathy Vanhoef <Mathy.Vanhoef@kuleuven.be>
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/include/net/cfg80211.h
|
||||
+++ b/include/net/cfg80211.h
|
||||
@@ -5628,7 +5628,7 @@ unsigned int ieee80211_get_mesh_hdrlen(s
|
||||
*/
|
||||
int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
|
||||
const u8 *addr, enum nl80211_iftype iftype,
|
||||
- u8 data_offset);
|
||||
+ u8 data_offset, bool is_amsdu);
|
||||
|
||||
/**
|
||||
* ieee80211_data_to_8023 - convert an 802.11 data frame to 802.3
|
||||
@@ -5640,7 +5640,7 @@ int ieee80211_data_to_8023_exthdr(struct
|
||||
static inline int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
|
||||
enum nl80211_iftype iftype)
|
||||
{
|
||||
- return ieee80211_data_to_8023_exthdr(skb, NULL, addr, iftype, 0);
|
||||
+ return ieee80211_data_to_8023_exthdr(skb, NULL, addr, iftype, 0, false);
|
||||
}
|
||||
|
||||
/**
|
||||
--- a/net/mac80211/rx.c
|
||||
+++ b/net/mac80211/rx.c
|
||||
@@ -2696,7 +2696,7 @@ __ieee80211_rx_h_amsdu(struct ieee80211_
|
||||
if (ieee80211_data_to_8023_exthdr(skb, ðhdr,
|
||||
rx->sdata->vif.addr,
|
||||
rx->sdata->vif.type,
|
||||
- data_offset))
|
||||
+ data_offset, true))
|
||||
return RX_DROP_UNUSABLE;
|
||||
|
||||
ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr,
|
||||
--- a/net/wireless/util.c
|
||||
+++ b/net/wireless/util.c
|
||||
@@ -541,7 +541,7 @@ EXPORT_SYMBOL(ieee80211_get_mesh_hdrlen)
|
||||
|
||||
int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
|
||||
const u8 *addr, enum nl80211_iftype iftype,
|
||||
- u8 data_offset)
|
||||
+ u8 data_offset, bool is_amsdu)
|
||||
{
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
||||
struct {
|
||||
@@ -629,7 +629,7 @@ int ieee80211_data_to_8023_exthdr(struct
|
||||
skb_copy_bits(skb, hdrlen, &payload, sizeof(payload));
|
||||
tmp.h_proto = payload.proto;
|
||||
|
||||
- if (likely((ether_addr_equal(payload.hdr, rfc1042_header) &&
|
||||
+ if (likely((!is_amsdu && ether_addr_equal(payload.hdr, rfc1042_header) &&
|
||||
tmp.h_proto != htons(ETH_P_AARP) &&
|
||||
tmp.h_proto != htons(ETH_P_IPX)) ||
|
||||
ether_addr_equal(payload.hdr, bridge_tunnel_header)))
|
@ -0,0 +1,40 @@
|
||||
From: Mathy Vanhoef <Mathy.Vanhoef@kuleuven.be>
|
||||
Date: Tue, 11 May 2021 20:02:45 +0200
|
||||
Subject: [PATCH] cfg80211: mitigate A-MSDU aggregation attacks
|
||||
|
||||
Mitigate A-MSDU injection attacks (CVE-2020-24588) by detecting if the
|
||||
destination address of a subframe equals an RFC1042 (i.e., LLC/SNAP)
|
||||
header, and if so dropping the complete A-MSDU frame. This mitigates
|
||||
known attacks, although new (unknown) aggregation-based attacks may
|
||||
remain possible.
|
||||
|
||||
This defense works because in A-MSDU aggregation injection attacks, a
|
||||
normal encrypted Wi-Fi frame is turned into an A-MSDU frame. This means
|
||||
the first 6 bytes of the first A-MSDU subframe correspond to an RFC1042
|
||||
header. In other words, the destination MAC address of the first A-MSDU
|
||||
subframe contains the start of an RFC1042 header during an aggregation
|
||||
attack. We can detect this and thereby prevent this specific attack.
|
||||
For details, see Section 7.2 of "Fragment and Forge: Breaking Wi-Fi
|
||||
Through Frame Aggregation and Fragmentation".
|
||||
|
||||
Note that for kernel 4.9 and above this patch depends on "mac80211:
|
||||
properly handle A-MSDUs that start with a rfc1042 header". Otherwise
|
||||
this patch has no impact and attacks will remain possible.
|
||||
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Mathy Vanhoef <Mathy.Vanhoef@kuleuven.be>
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/net/wireless/util.c
|
||||
+++ b/net/wireless/util.c
|
||||
@@ -775,6 +775,9 @@ void ieee80211_amsdu_to_8023s(struct sk_
|
||||
remaining = skb->len - offset;
|
||||
if (subframe_len > remaining)
|
||||
goto purge;
|
||||
+ /* mitigate A-MSDU aggregation injection attacks */
|
||||
+ if (ether_addr_equal(eth.h_dest, rfc1042_header))
|
||||
+ goto purge;
|
||||
|
||||
offset += sizeof(struct ethhdr);
|
||||
last = remaining <= subframe_len + padding;
|
@ -0,0 +1,54 @@
|
||||
From: Johannes Berg <johannes.berg@intel.com>
|
||||
Date: Tue, 11 May 2021 20:02:46 +0200
|
||||
Subject: [PATCH] mac80211: drop A-MSDUs on old ciphers
|
||||
|
||||
With old ciphers (WEP and TKIP) we shouldn't be using A-MSDUs
|
||||
since A-MSDUs are only supported if we know that they are, and
|
||||
the only practical way for that is HT support which doesn't
|
||||
support old ciphers.
|
||||
|
||||
However, we would normally accept them anyway. Since we check
|
||||
the MMIC before deaggregating A-MSDUs, and the A-MSDU bit in
|
||||
the QoS header is not protected in TKIP (or WEP), this enables
|
||||
attacks similar to CVE-2020-24588. To prevent that, drop A-MSDUs
|
||||
completely with old ciphers.
|
||||
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/rx.c
|
||||
+++ b/net/mac80211/rx.c
|
||||
@@ -6,7 +6,7 @@
|
||||
* Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net>
|
||||
* Copyright 2013-2014 Intel Mobile Communications GmbH
|
||||
* Copyright(c) 2015 - 2017 Intel Deutschland GmbH
|
||||
- * Copyright (C) 2018-2020 Intel Corporation
|
||||
+ * Copyright (C) 2018-2021 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/jiffies.h>
|
||||
@@ -2753,6 +2753,23 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx
|
||||
if (is_multicast_ether_addr(hdr->addr1))
|
||||
return RX_DROP_UNUSABLE;
|
||||
|
||||
+ if (rx->key) {
|
||||
+ /*
|
||||
+ * We should not receive A-MSDUs on pre-HT connections,
|
||||
+ * and HT connections cannot use old ciphers. Thus drop
|
||||
+ * them, as in those cases we couldn't even have SPP
|
||||
+ * A-MSDUs or such.
|
||||
+ */
|
||||
+ switch (rx->key->conf.cipher) {
|
||||
+ case WLAN_CIPHER_SUITE_WEP40:
|
||||
+ case WLAN_CIPHER_SUITE_WEP104:
|
||||
+ case WLAN_CIPHER_SUITE_TKIP:
|
||||
+ return RX_DROP_UNUSABLE;
|
||||
+ default:
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
return __ieee80211_rx_h_amsdu(rx, 0);
|
||||
}
|
||||
|
@ -0,0 +1,313 @@
|
||||
From: Johannes Berg <johannes.berg@intel.com>
|
||||
Date: Tue, 11 May 2021 20:02:47 +0200
|
||||
Subject: [PATCH] mac80211: add fragment cache to sta_info
|
||||
|
||||
Prior patches protected against fragmentation cache attacks
|
||||
by coloring keys, but this shows that it can lead to issues
|
||||
when multiple stations use the same sequence number. Add a
|
||||
fragment cache to struct sta_info (in addition to the one in
|
||||
the interface) to separate fragments for different stations
|
||||
properly.
|
||||
|
||||
This then automatically clear most of the fragment cache when a
|
||||
station disconnects (or reassociates) from an AP, or when client
|
||||
interfaces disconnect from the network, etc.
|
||||
|
||||
On the way, also fix the comment there since this brings us in line
|
||||
with the recommendation in 802.11-2016 ("An AP should support ...").
|
||||
Additionally, remove a useless condition (since there's no problem
|
||||
purging an already empty list).
|
||||
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/ieee80211_i.h
|
||||
+++ b/net/mac80211/ieee80211_i.h
|
||||
@@ -50,12 +50,6 @@ struct ieee80211_local;
|
||||
#define IEEE80211_ENCRYPT_HEADROOM 8
|
||||
#define IEEE80211_ENCRYPT_TAILROOM 18
|
||||
|
||||
-/* IEEE 802.11 (Ch. 9.5 Defragmentation) requires support for concurrent
|
||||
- * reception of at least three fragmented frames. This limit can be increased
|
||||
- * by changing this define, at the cost of slower frame reassembly and
|
||||
- * increased memory use (about 2 kB of RAM per entry). */
|
||||
-#define IEEE80211_FRAGMENT_MAX 4
|
||||
-
|
||||
/* power level hasn't been configured (or set to automatic) */
|
||||
#define IEEE80211_UNSET_POWER_LEVEL INT_MIN
|
||||
|
||||
@@ -88,19 +82,6 @@ extern const u8 ieee80211_ac_to_qos_mask
|
||||
|
||||
#define IEEE80211_MAX_NAN_INSTANCE_ID 255
|
||||
|
||||
-struct ieee80211_fragment_entry {
|
||||
- struct sk_buff_head skb_list;
|
||||
- unsigned long first_frag_time;
|
||||
- u16 seq;
|
||||
- u16 extra_len;
|
||||
- u16 last_frag;
|
||||
- u8 rx_queue;
|
||||
- bool check_sequential_pn; /* needed for CCMP/GCMP */
|
||||
- u8 last_pn[6]; /* PN of the last fragment if CCMP was used */
|
||||
- unsigned int key_color;
|
||||
-};
|
||||
-
|
||||
-
|
||||
struct ieee80211_bss {
|
||||
u32 device_ts_beacon, device_ts_presp;
|
||||
|
||||
@@ -912,9 +893,7 @@ struct ieee80211_sub_if_data {
|
||||
|
||||
char name[IFNAMSIZ];
|
||||
|
||||
- /* Fragment table for host-based reassembly */
|
||||
- struct ieee80211_fragment_entry fragments[IEEE80211_FRAGMENT_MAX];
|
||||
- unsigned int fragment_next;
|
||||
+ struct ieee80211_fragment_cache frags;
|
||||
|
||||
/* TID bitmap for NoAck policy */
|
||||
u16 noack_map;
|
||||
@@ -2329,4 +2308,7 @@ u32 ieee80211_calc_expected_tx_airtime(s
|
||||
#define debug_noinline
|
||||
#endif
|
||||
|
||||
+void ieee80211_init_frag_cache(struct ieee80211_fragment_cache *cache);
|
||||
+void ieee80211_destroy_frag_cache(struct ieee80211_fragment_cache *cache);
|
||||
+
|
||||
#endif /* IEEE80211_I_H */
|
||||
--- a/net/mac80211/iface.c
|
||||
+++ b/net/mac80211/iface.c
|
||||
@@ -8,7 +8,7 @@
|
||||
* Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
|
||||
* Copyright 2013-2014 Intel Mobile Communications GmbH
|
||||
* Copyright (c) 2016 Intel Deutschland GmbH
|
||||
- * Copyright (C) 2018-2020 Intel Corporation
|
||||
+ * Copyright (C) 2018-2021 Intel Corporation
|
||||
*/
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
@@ -679,16 +679,12 @@ static void ieee80211_set_multicast_list
|
||||
*/
|
||||
static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
- int i;
|
||||
-
|
||||
/* free extra data */
|
||||
ieee80211_free_keys(sdata, false);
|
||||
|
||||
ieee80211_debugfs_remove_netdev(sdata);
|
||||
|
||||
- for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)
|
||||
- __skb_queue_purge(&sdata->fragments[i].skb_list);
|
||||
- sdata->fragment_next = 0;
|
||||
+ ieee80211_destroy_frag_cache(&sdata->frags);
|
||||
|
||||
if (ieee80211_vif_is_mesh(&sdata->vif))
|
||||
ieee80211_mesh_teardown_sdata(sdata);
|
||||
@@ -2038,8 +2034,7 @@ int ieee80211_if_add(struct ieee80211_lo
|
||||
sdata->wdev.wiphy = local->hw.wiphy;
|
||||
sdata->local = local;
|
||||
|
||||
- for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)
|
||||
- skb_queue_head_init(&sdata->fragments[i].skb_list);
|
||||
+ ieee80211_init_frag_cache(&sdata->frags);
|
||||
|
||||
INIT_LIST_HEAD(&sdata->key_list);
|
||||
|
||||
--- a/net/mac80211/rx.c
|
||||
+++ b/net/mac80211/rx.c
|
||||
@@ -2133,19 +2133,34 @@ ieee80211_rx_h_decrypt(struct ieee80211_
|
||||
return result;
|
||||
}
|
||||
|
||||
+void ieee80211_init_frag_cache(struct ieee80211_fragment_cache *cache)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < ARRAY_SIZE(cache->entries); i++)
|
||||
+ skb_queue_head_init(&cache->entries[i].skb_list);
|
||||
+}
|
||||
+
|
||||
+void ieee80211_destroy_frag_cache(struct ieee80211_fragment_cache *cache)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < ARRAY_SIZE(cache->entries); i++)
|
||||
+ __skb_queue_purge(&cache->entries[i].skb_list);
|
||||
+}
|
||||
+
|
||||
static inline struct ieee80211_fragment_entry *
|
||||
-ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata,
|
||||
+ieee80211_reassemble_add(struct ieee80211_fragment_cache *cache,
|
||||
unsigned int frag, unsigned int seq, int rx_queue,
|
||||
struct sk_buff **skb)
|
||||
{
|
||||
struct ieee80211_fragment_entry *entry;
|
||||
|
||||
- entry = &sdata->fragments[sdata->fragment_next++];
|
||||
- if (sdata->fragment_next >= IEEE80211_FRAGMENT_MAX)
|
||||
- sdata->fragment_next = 0;
|
||||
+ entry = &cache->entries[cache->next++];
|
||||
+ if (cache->next >= IEEE80211_FRAGMENT_MAX)
|
||||
+ cache->next = 0;
|
||||
|
||||
- if (!skb_queue_empty(&entry->skb_list))
|
||||
- __skb_queue_purge(&entry->skb_list);
|
||||
+ __skb_queue_purge(&entry->skb_list);
|
||||
|
||||
__skb_queue_tail(&entry->skb_list, *skb); /* no need for locking */
|
||||
*skb = NULL;
|
||||
@@ -2160,14 +2175,14 @@ ieee80211_reassemble_add(struct ieee8021
|
||||
}
|
||||
|
||||
static inline struct ieee80211_fragment_entry *
|
||||
-ieee80211_reassemble_find(struct ieee80211_sub_if_data *sdata,
|
||||
+ieee80211_reassemble_find(struct ieee80211_fragment_cache *cache,
|
||||
unsigned int frag, unsigned int seq,
|
||||
int rx_queue, struct ieee80211_hdr *hdr)
|
||||
{
|
||||
struct ieee80211_fragment_entry *entry;
|
||||
int i, idx;
|
||||
|
||||
- idx = sdata->fragment_next;
|
||||
+ idx = cache->next;
|
||||
for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) {
|
||||
struct ieee80211_hdr *f_hdr;
|
||||
struct sk_buff *f_skb;
|
||||
@@ -2176,7 +2191,7 @@ ieee80211_reassemble_find(struct ieee802
|
||||
if (idx < 0)
|
||||
idx = IEEE80211_FRAGMENT_MAX - 1;
|
||||
|
||||
- entry = &sdata->fragments[idx];
|
||||
+ entry = &cache->entries[idx];
|
||||
if (skb_queue_empty(&entry->skb_list) || entry->seq != seq ||
|
||||
entry->rx_queue != rx_queue ||
|
||||
entry->last_frag + 1 != frag)
|
||||
@@ -2217,6 +2232,7 @@ static bool requires_sequential_pn(struc
|
||||
static ieee80211_rx_result debug_noinline
|
||||
ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
|
||||
{
|
||||
+ struct ieee80211_fragment_cache *cache = &rx->sdata->frags;
|
||||
struct ieee80211_hdr *hdr;
|
||||
u16 sc;
|
||||
__le16 fc;
|
||||
@@ -2238,6 +2254,9 @@ ieee80211_rx_h_defragment(struct ieee802
|
||||
goto out_no_led;
|
||||
}
|
||||
|
||||
+ if (rx->sta)
|
||||
+ cache = &rx->sta->frags;
|
||||
+
|
||||
if (likely(!ieee80211_has_morefrags(fc) && frag == 0))
|
||||
goto out;
|
||||
|
||||
@@ -2256,7 +2275,7 @@ ieee80211_rx_h_defragment(struct ieee802
|
||||
|
||||
if (frag == 0) {
|
||||
/* This is the first fragment of a new frame. */
|
||||
- entry = ieee80211_reassemble_add(rx->sdata, frag, seq,
|
||||
+ entry = ieee80211_reassemble_add(cache, frag, seq,
|
||||
rx->seqno_idx, &(rx->skb));
|
||||
if (requires_sequential_pn(rx, fc)) {
|
||||
int queue = rx->security_idx;
|
||||
@@ -2284,7 +2303,7 @@ ieee80211_rx_h_defragment(struct ieee802
|
||||
/* This is a fragment for a frame that should already be pending in
|
||||
* fragment cache. Add this fragment to the end of the pending entry.
|
||||
*/
|
||||
- entry = ieee80211_reassemble_find(rx->sdata, frag, seq,
|
||||
+ entry = ieee80211_reassemble_find(cache, frag, seq,
|
||||
rx->seqno_idx, hdr);
|
||||
if (!entry) {
|
||||
I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag);
|
||||
--- a/net/mac80211/sta_info.c
|
||||
+++ b/net/mac80211/sta_info.c
|
||||
@@ -4,7 +4,7 @@
|
||||
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
|
||||
* Copyright 2013-2014 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2015 - 2017 Intel Deutschland GmbH
|
||||
- * Copyright (C) 2018-2020 Intel Corporation
|
||||
+ * Copyright (C) 2018-2021 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
@@ -393,6 +393,8 @@ struct sta_info *sta_info_alloc(struct i
|
||||
|
||||
u64_stats_init(&sta->rx_stats.syncp);
|
||||
|
||||
+ ieee80211_init_frag_cache(&sta->frags);
|
||||
+
|
||||
sta->sta_state = IEEE80211_STA_NONE;
|
||||
|
||||
/* Mark TID as unreserved */
|
||||
@@ -1103,6 +1105,8 @@ static void __sta_info_destroy_part2(str
|
||||
|
||||
ieee80211_sta_debugfs_remove(sta);
|
||||
|
||||
+ ieee80211_destroy_frag_cache(&sta->frags);
|
||||
+
|
||||
cleanup_single_sta(sta);
|
||||
}
|
||||
|
||||
--- a/net/mac80211/sta_info.h
|
||||
+++ b/net/mac80211/sta_info.h
|
||||
@@ -3,7 +3,7 @@
|
||||
* Copyright 2002-2005, Devicescape Software, Inc.
|
||||
* Copyright 2013-2014 Intel Mobile Communications GmbH
|
||||
* Copyright(c) 2015-2017 Intel Deutschland GmbH
|
||||
- * Copyright(c) 2020 Intel Corporation
|
||||
+ * Copyright(c) 2020-2021 Intel Corporation
|
||||
*/
|
||||
|
||||
#ifndef STA_INFO_H
|
||||
@@ -439,6 +439,33 @@ struct ieee80211_sta_rx_stats {
|
||||
};
|
||||
|
||||
/*
|
||||
+ * IEEE 802.11-2016 (10.6 "Defragmentation") recommends support for "concurrent
|
||||
+ * reception of at least one MSDU per access category per associated STA"
|
||||
+ * on APs, or "at least one MSDU per access category" on other interface types.
|
||||
+ *
|
||||
+ * This limit can be increased by changing this define, at the cost of slower
|
||||
+ * frame reassembly and increased memory use while fragments are pending.
|
||||
+ */
|
||||
+#define IEEE80211_FRAGMENT_MAX 4
|
||||
+
|
||||
+struct ieee80211_fragment_entry {
|
||||
+ struct sk_buff_head skb_list;
|
||||
+ unsigned long first_frag_time;
|
||||
+ u16 seq;
|
||||
+ u16 extra_len;
|
||||
+ u16 last_frag;
|
||||
+ u8 rx_queue;
|
||||
+ bool check_sequential_pn; /* needed for CCMP/GCMP */
|
||||
+ u8 last_pn[6]; /* PN of the last fragment if CCMP was used */
|
||||
+ unsigned int key_color;
|
||||
+};
|
||||
+
|
||||
+struct ieee80211_fragment_cache {
|
||||
+ struct ieee80211_fragment_entry entries[IEEE80211_FRAGMENT_MAX];
|
||||
+ unsigned int next;
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
* The bandwidth threshold below which the per-station CoDel parameters will be
|
||||
* scaled to be more lenient (to prevent starvation of slow stations). This
|
||||
* value will be scaled by the number of active stations when it is being
|
||||
@@ -531,6 +558,7 @@ struct ieee80211_sta_rx_stats {
|
||||
* @status_stats.last_ack_signal: last ACK signal
|
||||
* @status_stats.ack_signal_filled: last ACK signal validity
|
||||
* @status_stats.avg_ack_signal: average ACK signal
|
||||
+ * @frags: fragment cache
|
||||
*/
|
||||
struct sta_info {
|
||||
/* General information, mostly static */
|
||||
@@ -639,6 +667,8 @@ struct sta_info {
|
||||
|
||||
struct cfg80211_chan_def tdls_chandef;
|
||||
|
||||
+ struct ieee80211_fragment_cache frags;
|
||||
+
|
||||
/* keep last! */
|
||||
struct ieee80211_sta sta;
|
||||
};
|
@ -0,0 +1,109 @@
|
||||
From: Johannes Berg <johannes.berg@intel.com>
|
||||
Date: Tue, 11 May 2021 20:02:48 +0200
|
||||
Subject: [PATCH] mac80211: check defrag PN against current frame
|
||||
|
||||
As pointed out by Mathy Vanhoef, we implement the RX PN check
|
||||
on fragmented frames incorrectly - we check against the last
|
||||
received PN prior to the new frame, rather than to the one in
|
||||
this frame itself.
|
||||
|
||||
Prior patches addressed the security issue here, but in order
|
||||
to be able to reason better about the code, fix it to really
|
||||
compare against the current frame's PN, not the last stored
|
||||
one.
|
||||
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/ieee80211_i.h
|
||||
+++ b/net/mac80211/ieee80211_i.h
|
||||
@@ -227,8 +227,15 @@ struct ieee80211_rx_data {
|
||||
*/
|
||||
int security_idx;
|
||||
|
||||
- u32 tkip_iv32;
|
||||
- u16 tkip_iv16;
|
||||
+ union {
|
||||
+ struct {
|
||||
+ u32 iv32;
|
||||
+ u16 iv16;
|
||||
+ } tkip;
|
||||
+ struct {
|
||||
+ u8 pn[IEEE80211_CCMP_PN_LEN];
|
||||
+ } ccm_gcm;
|
||||
+ };
|
||||
};
|
||||
|
||||
struct ieee80211_csa_settings {
|
||||
--- a/net/mac80211/rx.c
|
||||
+++ b/net/mac80211/rx.c
|
||||
@@ -2318,7 +2318,6 @@ ieee80211_rx_h_defragment(struct ieee802
|
||||
if (entry->check_sequential_pn) {
|
||||
int i;
|
||||
u8 pn[IEEE80211_CCMP_PN_LEN], *rpn;
|
||||
- int queue;
|
||||
|
||||
if (!requires_sequential_pn(rx, fc))
|
||||
return RX_DROP_UNUSABLE;
|
||||
@@ -2333,8 +2332,8 @@ ieee80211_rx_h_defragment(struct ieee802
|
||||
if (pn[i])
|
||||
break;
|
||||
}
|
||||
- queue = rx->security_idx;
|
||||
- rpn = rx->key->u.ccmp.rx_pn[queue];
|
||||
+
|
||||
+ rpn = rx->ccm_gcm.pn;
|
||||
if (memcmp(pn, rpn, IEEE80211_CCMP_PN_LEN))
|
||||
return RX_DROP_UNUSABLE;
|
||||
memcpy(entry->last_pn, pn, IEEE80211_CCMP_PN_LEN);
|
||||
--- a/net/mac80211/wpa.c
|
||||
+++ b/net/mac80211/wpa.c
|
||||
@@ -3,6 +3,7 @@
|
||||
* Copyright 2002-2004, Instant802 Networks, Inc.
|
||||
* Copyright 2008, Jouni Malinen <j@w1.fi>
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
+ * Copyright (C) 2020-2021 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
@@ -167,8 +168,8 @@ ieee80211_rx_h_michael_mic_verify(struct
|
||||
|
||||
update_iv:
|
||||
/* update IV in key information to be able to detect replays */
|
||||
- rx->key->u.tkip.rx[rx->security_idx].iv32 = rx->tkip_iv32;
|
||||
- rx->key->u.tkip.rx[rx->security_idx].iv16 = rx->tkip_iv16;
|
||||
+ rx->key->u.tkip.rx[rx->security_idx].iv32 = rx->tkip.iv32;
|
||||
+ rx->key->u.tkip.rx[rx->security_idx].iv16 = rx->tkip.iv16;
|
||||
|
||||
return RX_CONTINUE;
|
||||
|
||||
@@ -294,8 +295,8 @@ ieee80211_crypto_tkip_decrypt(struct iee
|
||||
key, skb->data + hdrlen,
|
||||
skb->len - hdrlen, rx->sta->sta.addr,
|
||||
hdr->addr1, hwaccel, rx->security_idx,
|
||||
- &rx->tkip_iv32,
|
||||
- &rx->tkip_iv16);
|
||||
+ &rx->tkip.iv32,
|
||||
+ &rx->tkip.iv16);
|
||||
if (res != TKIP_DECRYPT_OK)
|
||||
return RX_DROP_UNUSABLE;
|
||||
|
||||
@@ -552,6 +553,8 @@ ieee80211_crypto_ccmp_decrypt(struct iee
|
||||
}
|
||||
|
||||
memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN);
|
||||
+ if (unlikely(ieee80211_is_frag(hdr)))
|
||||
+ memcpy(rx->ccm_gcm.pn, pn, IEEE80211_CCMP_PN_LEN);
|
||||
}
|
||||
|
||||
/* Remove CCMP header and MIC */
|
||||
@@ -782,6 +785,8 @@ ieee80211_crypto_gcmp_decrypt(struct iee
|
||||
}
|
||||
|
||||
memcpy(key->u.gcmp.rx_pn[queue], pn, IEEE80211_GCMP_PN_LEN);
|
||||
+ if (unlikely(ieee80211_is_frag(hdr)))
|
||||
+ memcpy(rx->ccm_gcm.pn, pn, IEEE80211_CCMP_PN_LEN);
|
||||
}
|
||||
|
||||
/* Remove GCMP header and MIC */
|
@ -0,0 +1,62 @@
|
||||
From: Johannes Berg <johannes.berg@intel.com>
|
||||
Date: Tue, 11 May 2021 20:02:49 +0200
|
||||
Subject: [PATCH] mac80211: prevent attacks on TKIP/WEP as well
|
||||
|
||||
Similar to the issues fixed in previous patches, TKIP and WEP
|
||||
should be protected even if for TKIP we have the Michael MIC
|
||||
protecting it, and WEP is broken anyway.
|
||||
|
||||
However, this also somewhat protects potential other algorithms
|
||||
that drivers might implement.
|
||||
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/rx.c
|
||||
+++ b/net/mac80211/rx.c
|
||||
@@ -2284,6 +2284,7 @@ ieee80211_rx_h_defragment(struct ieee802
|
||||
* next fragment has a sequential PN value.
|
||||
*/
|
||||
entry->check_sequential_pn = true;
|
||||
+ entry->is_protected = true;
|
||||
entry->key_color = rx->key->color;
|
||||
memcpy(entry->last_pn,
|
||||
rx->key->u.ccmp.rx_pn[queue],
|
||||
@@ -2296,6 +2297,9 @@ ieee80211_rx_h_defragment(struct ieee802
|
||||
sizeof(rx->key->u.gcmp.rx_pn[queue]));
|
||||
BUILD_BUG_ON(IEEE80211_CCMP_PN_LEN !=
|
||||
IEEE80211_GCMP_PN_LEN);
|
||||
+ } else if (rx->key && ieee80211_has_protected(fc)) {
|
||||
+ entry->is_protected = true;
|
||||
+ entry->key_color = rx->key->color;
|
||||
}
|
||||
return RX_QUEUED;
|
||||
}
|
||||
@@ -2337,6 +2341,14 @@ ieee80211_rx_h_defragment(struct ieee802
|
||||
if (memcmp(pn, rpn, IEEE80211_CCMP_PN_LEN))
|
||||
return RX_DROP_UNUSABLE;
|
||||
memcpy(entry->last_pn, pn, IEEE80211_CCMP_PN_LEN);
|
||||
+ } else if (entry->is_protected &&
|
||||
+ (!rx->key || !ieee80211_has_protected(fc) ||
|
||||
+ rx->key->color != entry->key_color)) {
|
||||
+ /* Drop this as a mixed key or fragment cache attack, even
|
||||
+ * if for TKIP Michael MIC should protect us, and WEP is a
|
||||
+ * lost cause anyway.
|
||||
+ */
|
||||
+ return RX_DROP_UNUSABLE;
|
||||
}
|
||||
|
||||
skb_pull(rx->skb, ieee80211_hdrlen(fc));
|
||||
--- a/net/mac80211/sta_info.h
|
||||
+++ b/net/mac80211/sta_info.h
|
||||
@@ -455,7 +455,8 @@ struct ieee80211_fragment_entry {
|
||||
u16 extra_len;
|
||||
u16 last_frag;
|
||||
u8 rx_queue;
|
||||
- bool check_sequential_pn; /* needed for CCMP/GCMP */
|
||||
+ u8 check_sequential_pn:1, /* needed for CCMP/GCMP */
|
||||
+ is_protected:1;
|
||||
u8 last_pn[6]; /* PN of the last fragment if CCMP was used */
|
||||
unsigned int key_color;
|
||||
};
|
@ -0,0 +1,94 @@
|
||||
From: Johannes Berg <johannes.berg@intel.com>
|
||||
Date: Tue, 11 May 2021 20:02:50 +0200
|
||||
Subject: [PATCH] mac80211: do not accept/forward invalid EAPOL frames
|
||||
|
||||
EAPOL frames are used for authentication and key management between the
|
||||
AP and each individual STA associated in the BSS. Those frames are not
|
||||
supposed to be sent by one associated STA to another associated STA
|
||||
(either unicast for broadcast/multicast).
|
||||
|
||||
Similarly, in 802.11 they're supposed to be sent to the authenticator
|
||||
(AP) address.
|
||||
|
||||
Since it is possible for unexpected EAPOL frames to result in misbehavior
|
||||
in supplicant implementations, it is better for the AP to not allow such
|
||||
cases to be forwarded to other clients either directly, or indirectly if
|
||||
the AP interface is part of a bridge.
|
||||
|
||||
Accept EAPOL (control port) frames only if they're transmitted to the
|
||||
own address, or, due to interoperability concerns, to the PAE group
|
||||
address.
|
||||
|
||||
Disable forwarding of EAPOL (or well, the configured control port
|
||||
protocol) frames back to wireless medium in all cases. Previously, these
|
||||
frames were accepted from fully authenticated and authorized stations
|
||||
and also from unauthenticated stations for one of the cases.
|
||||
|
||||
Additionally, to avoid forwarding by the bridge, rewrite the PAE group
|
||||
address case to the local MAC address.
|
||||
|
||||
Cc: stable@vger.kernel.org
|
||||
Co-developed-by: Jouni Malinen <jouni@codeaurora.org>
|
||||
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/rx.c
|
||||
+++ b/net/mac80211/rx.c
|
||||
@@ -2541,13 +2541,13 @@ static bool ieee80211_frame_allowed(stru
|
||||
struct ethhdr *ehdr = (struct ethhdr *) rx->skb->data;
|
||||
|
||||
/*
|
||||
- * Allow EAPOL frames to us/the PAE group address regardless
|
||||
- * of whether the frame was encrypted or not.
|
||||
+ * Allow EAPOL frames to us/the PAE group address regardless of
|
||||
+ * whether the frame was encrypted or not, and always disallow
|
||||
+ * all other destination addresses for them.
|
||||
*/
|
||||
- if (ehdr->h_proto == rx->sdata->control_port_protocol &&
|
||||
- (ether_addr_equal(ehdr->h_dest, rx->sdata->vif.addr) ||
|
||||
- ether_addr_equal(ehdr->h_dest, pae_group_addr)))
|
||||
- return true;
|
||||
+ if (unlikely(ehdr->h_proto == rx->sdata->control_port_protocol))
|
||||
+ return ether_addr_equal(ehdr->h_dest, rx->sdata->vif.addr) ||
|
||||
+ ether_addr_equal(ehdr->h_dest, pae_group_addr);
|
||||
|
||||
if (ieee80211_802_1x_port_control(rx) ||
|
||||
ieee80211_drop_unencrypted(rx, fc))
|
||||
@@ -2572,8 +2572,28 @@ static void ieee80211_deliver_skb_to_loc
|
||||
cfg80211_rx_control_port(dev, skb, noencrypt);
|
||||
dev_kfree_skb(skb);
|
||||
} else {
|
||||
+ struct ethhdr *ehdr = (void *)skb_mac_header(skb);
|
||||
+
|
||||
memset(skb->cb, 0, sizeof(skb->cb));
|
||||
|
||||
+ /*
|
||||
+ * 802.1X over 802.11 requires that the authenticator address
|
||||
+ * be used for EAPOL frames. However, 802.1X allows the use of
|
||||
+ * the PAE group address instead. If the interface is part of
|
||||
+ * a bridge and we pass the frame with the PAE group address,
|
||||
+ * then the bridge will forward it to the network (even if the
|
||||
+ * client was not associated yet), which isn't supposed to
|
||||
+ * happen.
|
||||
+ * To avoid that, rewrite the destination address to our own
|
||||
+ * address, so that the authenticator (e.g. hostapd) will see
|
||||
+ * the frame, but bridge won't forward it anywhere else. Note
|
||||
+ * that due to earlier filtering, the only other address can
|
||||
+ * be the PAE group address.
|
||||
+ */
|
||||
+ if (unlikely(skb->protocol == sdata->control_port_protocol &&
|
||||
+ !ether_addr_equal(ehdr->h_dest, sdata->vif.addr)))
|
||||
+ ether_addr_copy(ehdr->h_dest, sdata->vif.addr);
|
||||
+
|
||||
/* deliver to local stack */
|
||||
if (rx->list)
|
||||
#if LINUX_VERSION_IS_GEQ(4,19,0)
|
||||
@@ -2617,6 +2637,7 @@ ieee80211_deliver_skb(struct ieee80211_r
|
||||
if ((sdata->vif.type == NL80211_IFTYPE_AP ||
|
||||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN) &&
|
||||
!(sdata->flags & IEEE80211_SDATA_DONT_BRIDGE_PACKETS) &&
|
||||
+ ehdr->h_proto != rx->sdata->control_port_protocol &&
|
||||
(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->u.vlan.sta)) {
|
||||
if (is_multicast_ether_addr(ehdr->h_dest) &&
|
||||
ieee80211_vif_get_num_mcast_if(sdata) != 0) {
|
@ -0,0 +1,68 @@
|
||||
From: Wen Gong <wgong@codeaurora.org>
|
||||
Date: Tue, 11 May 2021 20:02:51 +0200
|
||||
Subject: [PATCH] mac80211: extend protection against mixed key and
|
||||
fragment cache attacks
|
||||
|
||||
For some chips/drivers, e.g., QCA6174 with ath10k, the decryption is
|
||||
done by the hardware, and the Protected bit in the Frame Control field
|
||||
is cleared in the lower level driver before the frame is passed to
|
||||
mac80211. In such cases, the condition for ieee80211_has_protected() is
|
||||
not met in ieee80211_rx_h_defragment() of mac80211 and the new security
|
||||
validation steps are not executed.
|
||||
|
||||
Extend mac80211 to cover the case where the Protected bit has been
|
||||
cleared, but the frame is indicated as having been decrypted by the
|
||||
hardware. This extends protection against mixed key and fragment cache
|
||||
attack for additional drivers/chips. This fixes CVE-2020-24586 and
|
||||
CVE-2020-24587 for such cases.
|
||||
|
||||
Tested-on: QCA6174 hw3.2 PCI WLAN.RM.4.4.1-00110-QCARMSWP-1
|
||||
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Wen Gong <wgong@codeaurora.org>
|
||||
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/rx.c
|
||||
+++ b/net/mac80211/rx.c
|
||||
@@ -2239,6 +2239,7 @@ ieee80211_rx_h_defragment(struct ieee802
|
||||
unsigned int frag, seq;
|
||||
struct ieee80211_fragment_entry *entry;
|
||||
struct sk_buff *skb;
|
||||
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
|
||||
|
||||
hdr = (struct ieee80211_hdr *)rx->skb->data;
|
||||
fc = hdr->frame_control;
|
||||
@@ -2297,7 +2298,9 @@ ieee80211_rx_h_defragment(struct ieee802
|
||||
sizeof(rx->key->u.gcmp.rx_pn[queue]));
|
||||
BUILD_BUG_ON(IEEE80211_CCMP_PN_LEN !=
|
||||
IEEE80211_GCMP_PN_LEN);
|
||||
- } else if (rx->key && ieee80211_has_protected(fc)) {
|
||||
+ } else if (rx->key &&
|
||||
+ (ieee80211_has_protected(fc) ||
|
||||
+ (status->flag & RX_FLAG_DECRYPTED))) {
|
||||
entry->is_protected = true;
|
||||
entry->key_color = rx->key->color;
|
||||
}
|
||||
@@ -2342,13 +2345,19 @@ ieee80211_rx_h_defragment(struct ieee802
|
||||
return RX_DROP_UNUSABLE;
|
||||
memcpy(entry->last_pn, pn, IEEE80211_CCMP_PN_LEN);
|
||||
} else if (entry->is_protected &&
|
||||
- (!rx->key || !ieee80211_has_protected(fc) ||
|
||||
+ (!rx->key ||
|
||||
+ (!ieee80211_has_protected(fc) &&
|
||||
+ !(status->flag & RX_FLAG_DECRYPTED)) ||
|
||||
rx->key->color != entry->key_color)) {
|
||||
/* Drop this as a mixed key or fragment cache attack, even
|
||||
* if for TKIP Michael MIC should protect us, and WEP is a
|
||||
* lost cause anyway.
|
||||
*/
|
||||
return RX_DROP_UNUSABLE;
|
||||
+ } else if (entry->is_protected && rx->key &&
|
||||
+ entry->key_color != rx->key->color &&
|
||||
+ (status->flag & RX_FLAG_DECRYPTED)) {
|
||||
+ return RX_DROP_UNUSABLE;
|
||||
}
|
||||
|
||||
skb_pull(rx->skb, ieee80211_hdrlen(fc));
|
@ -87,7 +87,7 @@
|
||||
CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)
|
||||
--- a/net/mac80211/ieee80211_i.h
|
||||
+++ b/net/mac80211/ieee80211_i.h
|
||||
@@ -1403,6 +1403,7 @@ struct ieee80211_local {
|
||||
@@ -1390,6 +1390,7 @@ struct ieee80211_local {
|
||||
int dynamic_ps_forced_timeout;
|
||||
|
||||
int user_power_level; /* in dBm, for all interfaces */
|
||||
|
Loading…
Reference in New Issue
Block a user