net80211: reject mixed plaintext/encrypted fragments

ieee80211_defrag() accepts fragmented 802.11 frames in a protected Wi-Fi
network even when some of the fragments are not encrypted.
Track whether the fragments are encrypted or not and only accept
successive ones if they match the state of the first fragment.

This relates to section 6.3 in the 2021 Usenix "FragAttacks" (Fragment
and Forge: Breaking Wi-Fi Through Frame Aggregation and Fragmentation)
paper.

Submitted by:	Mathy Vanhoef (Mathy.Vanhoef kuleuven.be)
Security:	CVE-2020-26147
PR:		256118
Differential Revision: https://reviews.freebsd.org/D30663
This commit is contained in:
Mathy Vanhoef 2021-06-06 22:10:41 +00:00 committed by Bjoern A. Zeeb
parent a20c10893e
commit 11572d7d7f
7 changed files with 24 additions and 9 deletions

View File

@ -531,7 +531,7 @@ adhoc_input(struct ieee80211_node *ni, struct mbuf *m,
* Next up, any fragmentation.
*/
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
m = ieee80211_defrag(ni, m, hdrspace);
m = ieee80211_defrag(ni, m, hdrspace, has_decrypted);
if (m == NULL) {
/* Fragment dropped or frame not complete yet */
goto out;

View File

@ -719,7 +719,7 @@ hostap_input(struct ieee80211_node *ni, struct mbuf *m,
* Next up, any fragmentation.
*/
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
m = ieee80211_defrag(ni, m, hdrspace);
m = ieee80211_defrag(ni, m, hdrspace, has_decrypted);
if (m == NULL) {
/* Fragment dropped or frame not complete yet */
goto out;

View File

@ -170,7 +170,8 @@ ieee80211_input_mimo_all(struct ieee80211com *ic, struct mbuf *m)
* XXX should handle 3 concurrent reassemblies per-spec.
*/
struct mbuf *
ieee80211_defrag(struct ieee80211_node *ni, struct mbuf *m, int hdrspace)
ieee80211_defrag(struct ieee80211_node *ni, struct mbuf *m, int hdrspace,
int has_decrypted)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
@ -189,6 +190,11 @@ ieee80211_defrag(struct ieee80211_node *ni, struct mbuf *m, int hdrspace)
if (!more_frag && fragno == 0 && ni->ni_rxfrag[0] == NULL)
return m;
/* Temporarily set flag to remember if fragment was encrypted. */
/* XXX use a non-packet altering storage for this in the future. */
if (has_decrypted)
wh->i_fc[1] |= IEEE80211_FC1_PROTECTED;
/*
* Remove frag to insure it doesn't get reaped by timer.
*/
@ -219,10 +225,14 @@ ieee80211_defrag(struct ieee80211_node *ni, struct mbuf *m, int hdrspace)
lwh = mtod(mfrag, struct ieee80211_frame *);
last_rxseq = le16toh(*(uint16_t *)lwh->i_seq);
/* NB: check seq # and frag together */
/*
* NB: check seq # and frag together. Also check that both
* fragments are plaintext or that both are encrypted.
*/
if (rxseq == last_rxseq+1 &&
IEEE80211_ADDR_EQ(wh->i_addr1, lwh->i_addr1) &&
IEEE80211_ADDR_EQ(wh->i_addr2, lwh->i_addr2)) {
IEEE80211_ADDR_EQ(wh->i_addr2, lwh->i_addr2) &&
!((wh->i_fc[1] ^ lwh->i_fc[1]) & IEEE80211_FC1_PROTECTED)) {
/* XXX clear MORE_FRAG bit? */
/* track last seqnum and fragno */
*(uint16_t *) lwh->i_seq = *(uint16_t *) wh->i_seq;
@ -253,6 +263,11 @@ ieee80211_defrag(struct ieee80211_node *ni, struct mbuf *m, int hdrspace)
ni->ni_rxfrag[0] = mfrag;
mfrag = NULL;
}
/* Remember to clear protected flag that was temporarily set. */
if (mfrag != NULL) {
wh = mtod(mfrag, struct ieee80211_frame *);
wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
}
return mfrag;
}

View File

@ -309,7 +309,7 @@ ieee80211_check_rxseq(struct ieee80211_node *ni, struct ieee80211_frame *wh,
void ieee80211_deliver_data(struct ieee80211vap *,
struct ieee80211_node *, struct mbuf *);
struct mbuf *ieee80211_defrag(struct ieee80211_node *,
struct mbuf *, int);
struct mbuf *, int, int);
struct mbuf *ieee80211_realign(struct ieee80211vap *, struct mbuf *, size_t);
struct mbuf *ieee80211_decap(struct ieee80211vap *, struct mbuf *, int);
struct mbuf *ieee80211_decap1(struct mbuf *, int *);

View File

@ -1641,7 +1641,7 @@ mesh_input(struct ieee80211_node *ni, struct mbuf *m,
*/
hdrspace = ieee80211_hdrspace(ic, wh);
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
m = ieee80211_defrag(ni, m, hdrspace);
m = ieee80211_defrag(ni, m, hdrspace, 0);
if (m == NULL) {
/* Fragment dropped or frame not complete yet */
goto out;

View File

@ -795,7 +795,7 @@ sta_input(struct ieee80211_node *ni, struct mbuf *m,
* Next up, any fragmentation.
*/
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
m = ieee80211_defrag(ni, m, hdrspace);
m = ieee80211_defrag(ni, m, hdrspace, has_decrypted);
if (m == NULL) {
/* Fragment dropped or frame not complete yet */
goto out;

View File

@ -594,7 +594,7 @@ wds_input(struct ieee80211_node *ni, struct mbuf *m,
* Next up, any fragmentation.
*/
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
m = ieee80211_defrag(ni, m, hdrspace);
m = ieee80211_defrag(ni, m, hdrspace, has_decrypted);
if (m == NULL) {
/* Fragment dropped or frame not complete yet */
goto out;