2021-05-31 20:32:50

by Johannes Berg

[permalink] [raw]
Subject: [PATCH v4.14 00/10] wireless security fixes backports

Some of these patches here were already applied since they
applied cleanly, but I'm resending the whole set for review
now anyway.

johannes



2021-05-31 20:34:45

by Johannes Berg

[permalink] [raw]
Subject: [PATCH v4.14 01/10] mac80211: assure all fragments are encrypted

From: Mathy Vanhoef <[email protected]>

commit 965a7d72e798eb7af0aa67210e37cf7ecd1c9cad upstream.

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: [email protected]
Signed-off-by: Mathy Vanhoef <[email protected]>
Link: https://lore.kernel.org/r/202105[email protected]changeid
Signed-off-by: Johannes Berg <[email protected]>
---
net/mac80211/rx.c | 23 ++++++++++++-----------
1 file changed, 12 insertions(+), 11 deletions(-)

diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 48c6aa337c92..9b313c2f7d49 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1968,6 +1968,16 @@ ieee80211_reassemble_find(struct ieee80211_sub_if_data *sdata,
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)
{
@@ -2012,12 +2022,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
/* 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
@@ -2059,11 +2064,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
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--) {
--
2.31.1

2021-05-31 20:34:51

by Johannes Berg

[permalink] [raw]
Subject: [PATCH v4.14 03/10] mac80211: properly handle A-MSDUs that start with an RFC 1042 header

From: Mathy Vanhoef <[email protected]>

commit a1d5ff5651ea592c67054233b14b30bf4452999c upstream.

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: [email protected]
Signed-off-by: Mathy Vanhoef <[email protected]>
Link: https://lore.kernel.org/r/202105[email protected]changeid
Signed-off-by: Johannes Berg <[email protected]>
---
include/net/cfg80211.h | 5 +++--
net/mac80211/rx.c | 3 ++-
net/wireless/util.c | 5 +++--
3 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 030eea38f258..be161f82abc6 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -4338,7 +4338,8 @@ unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr);
* Return: 0 on success. Non-zero on error.
*/
int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
- const u8 *addr, enum nl80211_iftype iftype);
+ const u8 *addr, enum nl80211_iftype iftype,
+ bool is_amsdu);

/**
* ieee80211_data_to_8023 - convert an 802.11 data frame to 802.3
@@ -4350,7 +4351,7 @@ int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
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);
+ return ieee80211_data_to_8023_exthdr(skb, NULL, addr, iftype, false);
}

/**
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 8c126854aa06..be7e37edad12 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2451,7 +2451,8 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)

if (ieee80211_data_to_8023_exthdr(skb, &ethhdr,
rx->sdata->vif.addr,
- rx->sdata->vif.type))
+ rx->sdata->vif.type,
+ true))
return RX_DROP_UNUSABLE;

ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr,
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 935929b45411..2aef1c487be6 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -422,7 +422,8 @@ unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
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)
+ const u8 *addr, enum nl80211_iftype iftype,
+ bool is_amsdu)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct {
@@ -510,7 +511,7 @@ int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
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)))
--
2.31.1

2021-05-31 20:34:52

by Johannes Berg

[permalink] [raw]
Subject: [PATCH v4.14 08/10] mac80211: prevent attacks on TKIP/WEP as well

From: Johannes Berg <[email protected]>

commit 7e44a0b597f04e67eee8cdcbe7ee706c6f5de38b upstream.

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: [email protected]
Link: https://lore.kernel.org/r/202105[email protected]changeid
Signed-off-by: Johannes Berg <[email protected]>
---
net/mac80211/rx.c | 12 ++++++++++++
net/mac80211/sta_info.h | 3 ++-
2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index a00ec2ccb363..61fe65696e6e 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2048,6 +2048,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
* 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],
@@ -2060,6 +2061,9 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
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;
}
@@ -2101,6 +2105,14 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
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));
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 0447197c4a2b..f1d293f5678f 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -415,7 +415,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;
};
--
2.31.1

2021-05-31 20:34:52

by Johannes Berg

[permalink] [raw]
Subject: [PATCH v4.14 07/10] mac80211: check defrag PN against current frame

From: Johannes Berg <[email protected]>

commit bf30ca922a0c0176007e074b0acc77ed345e9990 upstream.

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: [email protected]
Link: https://lore.kernel.org/r/202105[email protected]changeid
Signed-off-by: Johannes Berg <[email protected]>
---
net/mac80211/ieee80211_i.h | 11 +++++++++--
net/mac80211/rx.c | 5 ++---
net/mac80211/wpa.c | 12 ++++++++----
3 files changed, 19 insertions(+), 9 deletions(-)

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index f38748efb98a..790c771e8108 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -223,8 +223,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 {
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 39bc3d78f71a..a00ec2ccb363 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2082,7 +2082,6 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
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;
@@ -2097,8 +2096,8 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
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);
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index e3bb69ba6887..09b4f913e20b 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -162,8 +162,8 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)

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;

@@ -289,8 +289,8 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
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;

@@ -548,6 +548,8 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx,
}

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 */
@@ -777,6 +779,8 @@ ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx)
}

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 */
--
2.31.1

2021-05-31 20:34:52

by Johannes Berg

[permalink] [raw]
Subject: [PATCH v4.14 04/10] cfg80211: mitigate A-MSDU aggregation attacks

From: Mathy Vanhoef <[email protected]>

commit 2b8a1fee3488c602aca8bea004a087e60806a5cf upstream.

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: [email protected]
Signed-off-by: Mathy Vanhoef <[email protected]>
Link: https://lore.kernel.org/r/202105[email protected]changeid
Signed-off-by: Johannes Berg <[email protected]>
---
net/wireless/util.c | 3 +++
1 file changed, 3 insertions(+)

diff --git a/net/wireless/util.c b/net/wireless/util.c
index 2aef1c487be6..b3895a8a48ab 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -768,6 +768,9 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
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;
--
2.31.1

2021-05-31 20:34:51

by Johannes Berg

[permalink] [raw]
Subject: [PATCH v4.14 06/10] mac80211: add fragment cache to sta_info

From: Johannes Berg <[email protected]>

commit 3a11ce08c45b50d69c891d71760b7c5b92074709 upstream.

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: [email protected]
Link: https://lore.kernel.org/r/202105[email protected]changeid
Signed-off-by: Johannes Berg <[email protected]>
---
net/mac80211/ieee80211_i.h | 26 ++++--------------------
net/mac80211/iface.c | 9 ++-------
net/mac80211/rx.c | 41 ++++++++++++++++++++++++++++----------
net/mac80211/sta_info.c | 4 ++++
net/mac80211/sta_info.h | 30 ++++++++++++++++++++++++++++
5 files changed, 70 insertions(+), 40 deletions(-)

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 74186c2d9e6e..f38748efb98a 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -52,12 +52,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

@@ -90,19 +84,6 @@ extern const u8 ieee80211_ac_to_qos_mask[IEEE80211_NUM_ACS];

#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;

@@ -882,9 +863,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;
@@ -2164,4 +2143,7 @@ extern const struct ethtool_ops ieee80211_ethtool_ops;
#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 */
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index dc398a181678..adafa29d3021 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1127,16 +1127,12 @@ static void ieee80211_set_multicast_list(struct net_device *dev)
*/
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);
@@ -1846,8 +1842,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
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);

diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index d6cdd0c025ca..39bc3d78f71a 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1899,19 +1899,34 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
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;
@@ -1926,14 +1941,14 @@ ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata,
}

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;

@@ -1941,7 +1956,7 @@ ieee80211_reassemble_find(struct ieee80211_sub_if_data *sdata,
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)
@@ -1981,6 +1996,7 @@ static bool requires_sequential_pn(struct ieee80211_rx_data *rx, __le16 fc)
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;
@@ -2002,6 +2018,9 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
goto out_no_led;
}

+ if (rx->sta)
+ cache = &rx->sta->frags;
+
if (likely(!ieee80211_has_morefrags(fc) && frag == 0))
goto out;

@@ -2020,7 +2039,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)

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;
@@ -2048,7 +2067,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
/* 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);
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 32fede73ecd4..0d5265adf539 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -367,6 +367,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,

u64_stats_init(&sta->rx_stats.syncp);

+ ieee80211_init_frag_cache(&sta->frags);
+
sta->sta_state = IEEE80211_STA_NONE;

/* Mark TID as unreserved */
@@ -1005,6 +1007,8 @@ static void __sta_info_destroy_part2(struct sta_info *sta)
rate_control_remove_sta_debugfs(sta);
ieee80211_sta_debugfs_remove(sta);

+ ieee80211_destroy_frag_cache(&sta->frags);
+
cleanup_single_sta(sta);
}

diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 3acbdfa9f649..0447197c4a2b 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -398,6 +398,33 @@ struct ieee80211_sta_rx_stats {
u64 msdu[IEEE80211_NUM_TIDS + 1];
};

+/*
+ * 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
@@ -470,6 +497,7 @@ struct ieee80211_sta_rx_stats {
* @pcpu_rx_stats: per-CPU RX statistics, assigned only if the driver needs
* this (by advertising the USES_RSS hw flag)
* @status_stats: TX status statistics
+ * @frags: fragment cache
*/
struct sta_info {
/* General information, mostly static */
@@ -569,6 +597,8 @@ struct sta_info {

struct cfg80211_chan_def tdls_chandef;

+ struct ieee80211_fragment_cache frags;
+
/* keep last! */
struct ieee80211_sta sta;
};
--
2.31.1

2021-05-31 20:34:52

by Johannes Berg

[permalink] [raw]
Subject: [PATCH v4.14 09/10] mac80211: do not accept/forward invalid EAPOL frames

From: Johannes Berg <[email protected]>

commit a8c4d76a8dd4fb9666fc8919a703d85fb8f44ed8 upstream.

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: [email protected]
Co-developed-by: Jouni Malinen <[email protected]>
Signed-off-by: Jouni Malinen <[email protected]>
Link: https://lore.kernel.org/r/202105[email protected]changeid
Signed-off-by: Johannes Berg <[email protected]>
---
net/mac80211/rx.c | 34 ++++++++++++++++++++++++++++------
1 file changed, 28 insertions(+), 6 deletions(-)

diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 61fe65696e6e..2fe18dc6af86 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2298,13 +2298,13 @@ static bool ieee80211_frame_allowed(struct ieee80211_rx_data *rx, __le16 fc)
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))
@@ -2344,6 +2344,7 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
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) {
@@ -2397,9 +2398,30 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
#endif

if (skb) {
+ struct ethhdr *ehdr = (void *)skb_mac_header(skb);
+
/* deliver to local stack */
skb->protocol = eth_type_trans(skb, dev);
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);
+
if (rx->napi)
napi_gro_receive(rx->napi, skb);
else
--
2.31.1

2021-05-31 20:35:03

by Johannes Berg

[permalink] [raw]
Subject: [PATCH v4.14 10/10] mac80211: extend protection against mixed key and fragment cache attacks

From: Wen Gong <[email protected]>

commit 3edc6b0d6c061a70d8ca3c3c72eb1f58ce29bfb1 upstream.

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: [email protected]
Signed-off-by: Wen Gong <[email protected]>
Signed-off-by: Jouni Malinen <[email protected]>
Link: https://lore.kernel.org/r/202105[email protected]changeid
Signed-off-by: Johannes Berg <[email protected]>
---
net/mac80211/rx.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 2fe18dc6af86..6b4fd56800f7 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2003,6 +2003,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
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;
@@ -2061,7 +2062,9 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
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;
}
@@ -2106,13 +2109,19 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
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));
--
2.31.1

2021-05-31 20:35:15

by Johannes Berg

[permalink] [raw]
Subject: [PATCH v4.14 05/10] mac80211: drop A-MSDUs on old ciphers

From: Johannes Berg <[email protected]>

commit 270032a2a9c4535799736142e1e7c413ca7b836e upstream.

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: [email protected]
Link: https://lore.kernel.org/r/202105[email protected]changeid
Signed-off-by: Johannes Berg <[email protected]>
---
net/mac80211/rx.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)

diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index be7e37edad12..d6cdd0c025ca 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2455,6 +2455,23 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
true))
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;
+ }
+ }
+
ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr,
rx->sdata->vif.type,
rx->local->hw.extra_tx_headroom,
--
2.31.1

2021-05-31 20:35:16

by Johannes Berg

[permalink] [raw]
Subject: [PATCH v4.14 02/10] mac80211: prevent mixed key and fragment cache attacks

From: Mathy Vanhoef <[email protected]>

commit 94034c40ab4a3fcf581fbc7f8fdf4e29943c4a24 upstream.

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: [email protected]
Signed-off-by: Mathy Vanhoef <[email protected]>
Link: https://lore.kernel.org/r/202105[email protected]changeid
Signed-off-by: Johannes Berg <[email protected]>
---
net/mac80211/ieee80211_i.h | 1 +
net/mac80211/key.c | 7 +++++++
net/mac80211/key.h | 2 ++
net/mac80211/rx.c | 6 ++++++
4 files changed, 16 insertions(+)

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 651705565dfb..74186c2d9e6e 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -99,6 +99,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;
};


diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index fa10c6142244..d122031e389a 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -648,6 +648,7 @@ int ieee80211_key_link(struct ieee80211_key *key,
struct sta_info *sta)
{
struct ieee80211_local *local = sdata->local;
+ 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;
@@ -659,6 +660,12 @@ int ieee80211_key_link(struct ieee80211_key *key,
bool delay_tailroom = sdata->vif.type == NL80211_IFTYPE_STATION;
int ret;

+ /*
+ * 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);
+
mutex_lock(&sdata->local->key_mtx);

if (sta && pairwise)
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index ebdb80b85dc3..d8e187bcb751 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -127,6 +127,8 @@ struct ieee80211_key {
} debugfs;
#endif

+ unsigned int color;
+
/*
* key config, must be last because it contains key
* material as variable length member
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 9b313c2f7d49..8c126854aa06 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2029,6 +2029,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
* 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);
@@ -2066,6 +2067,11 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)

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]++;
--
2.31.1

2021-06-01 07:28:23

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v4.14 00/10] wireless security fixes backports

On Mon, May 31, 2021 at 10:31:25PM +0200, Johannes Berg wrote:
> Some of these patches here were already applied since they
> applied cleanly, but I'm resending the whole set for review
> now anyway.

I've applied your updated versions just to make sure that my backports
were not somehow messed up, thank for these!

greg k-h

2021-06-01 07:30:06

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v4.14 00/10] wireless security fixes backports

On Tue, 2021-06-01 at 09:27 +0200, Greg KH wrote:
> On Mon, May 31, 2021 at 10:31:25PM +0200, Johannes Berg wrote:
> > Some of these patches here were already applied since they
> > applied cleanly, but I'm resending the whole set for review
> > now anyway.
>
> I've applied your updated versions just to make sure that my backports
> were not somehow messed up, thank for these!

I'm sure they were the same, I just cherry-picked them (cleanly) :)

Honestly, I was just too lazy to dig out the stable queue, apply the
patches from there manually, and then not send them etc. ... when
cherry-pick was easy.

Thanks,
johannes