diff options
author | Stefan Sperling <stsp@cvs.openbsd.org> | 2021-05-17 08:02:21 +0000 |
---|---|---|
committer | Stefan Sperling <stsp@cvs.openbsd.org> | 2021-05-17 08:02:21 +0000 |
commit | d4cb904761bcdb84090f56a46d086d45fdc47c63 (patch) | |
tree | f2c3115a1dd0deeca1d60e90cd43ebbdee4a4b77 /sys/net80211/ieee80211_input.c | |
parent | 8e477e1862472e09c8f8d46130099524306d4fb8 (diff) |
Prevent frame injection via forged 802.11n A-MSDUs.
This mitigates an attack where a single 802.11 frame is interpreted as an
A-MSDU because of a forged AMSDU-present bit in the 802.11 QoS frame header.
See https://papers.mathyvanhoef.com/usenix2021.pdf section 3.2.
MAC address validation is added as an additional measure to prevent hostap
clients from sending A-MSDU subframes with a spoofed source address.
An earlier version of this patch was reviewed by Mathy Vanhoef, who spotted
a bug in my original attempt at preventing spoofed addresses.
ok mpi@
Diffstat (limited to 'sys/net80211/ieee80211_input.c')
-rw-r--r-- | sys/net80211/ieee80211_input.c | 74 |
1 files changed, 67 insertions, 7 deletions
diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c index 633161c60bc..1197112c055 100644 --- a/sys/net80211/ieee80211_input.c +++ b/sys/net80211/ieee80211_input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_input.c,v 1.234 2021/04/29 21:43:46 stsp Exp $ */ +/* $OpenBSD: ieee80211_input.c,v 1.235 2021/05/17 08:02:20 stsp Exp $ */ /*- * Copyright (c) 2001 Atsushi Onoe @@ -77,6 +77,8 @@ void ieee80211_input_ba_seq(struct ieee80211com *, struct mbuf *ieee80211_align_mbuf(struct mbuf *); void ieee80211_decap(struct ieee80211com *, struct mbuf *, struct ieee80211_node *, int, struct mbuf_list *); +int ieee80211_amsdu_decap_validate(struct ieee80211com *, struct mbuf *, + struct ieee80211_node *); void ieee80211_amsdu_decap(struct ieee80211com *, struct mbuf *, struct ieee80211_node *, int, struct mbuf_list *); void ieee80211_enqueue_data(struct ieee80211com *, struct mbuf *, @@ -1155,6 +1157,50 @@ ieee80211_decap(struct ieee80211com *ic, struct mbuf *m, ieee80211_enqueue_data(ic, m, ni, mcast, ml); } +int +ieee80211_amsdu_decap_validate(struct ieee80211com *ic, struct mbuf *m, + struct ieee80211_node *ni) +{ + struct ether_header *eh = mtod(m, struct ether_header *); + const uint8_t llc_hdr_mac[ETHER_ADDR_LEN] = { + /* MAC address matching the 802.2 LLC header. */ + LLC_SNAP_LSAP, LLC_SNAP_LSAP, LLC_UI, 0, 0, 0 + }; + + /* + * We are sorry, but this particular MAC address cannot be used. + * This mitigates an attack where a single 802.11 frame is interpreted + * as an A-MSDU because of a forged AMSDU-present bit in the 802.11 + * QoS frame header: https://papers.mathyvanhoef.com/usenix2021.pdf + * See Section 7.2, 'Countermeasures for the design flaws' + */ + if (ETHER_IS_EQ(eh->ether_dhost, llc_hdr_mac)) + return 1; + + switch (ic->ic_opmode) { +#ifndef IEEE80211_STA_ONLY + case IEEE80211_M_HOSTAP: + /* + * Subframes must use the source address of the node which + * transmitted the A-MSDU. Prevents MAC spoofing. + */ + if (!ETHER_IS_EQ(ni->ni_macaddr, eh->ether_shost)) + return 1; + break; +#endif + case IEEE80211_M_STA: + /* Subframes must be addressed to me. */ + if (!ETHER_IS_EQ(ic->ic_myaddr, eh->ether_dhost)) + return 1; + break; + default: + /* Ignore MONITOR/IBSS modes for now. */ + break; + } + + return 0; +} + /* * Decapsulate an Aggregate MSDU (see 7.2.2.2). */ @@ -1167,6 +1213,7 @@ ieee80211_amsdu_decap(struct ieee80211com *ic, struct mbuf *m, struct llc *llc; int len, pad, mcast; struct ieee80211_frame *wh; + struct mbuf_list subframes = MBUF_LIST_INITIALIZER(); wh = mtod(m, struct ieee80211_frame *); mcast = IEEE80211_IS_MULTICAST(wh->i_addr1); @@ -1177,10 +1224,8 @@ ieee80211_amsdu_decap(struct ieee80211com *ic, struct mbuf *m, while (m->m_pkthdr.len >= ETHER_HDR_LEN + LLC_SNAPFRAMELEN) { /* process an A-MSDU subframe */ m = m_pullup(m, ETHER_HDR_LEN + LLC_SNAPFRAMELEN); - if (m == NULL) { - ic->ic_stats.is_rx_decap++; - return; - } + if (m == NULL) + break; eh = mtod(m, struct ether_header *); /* examine 802.3 header */ len = ntohs(eh->ether_type); @@ -1188,11 +1233,12 @@ ieee80211_amsdu_decap(struct ieee80211com *ic, struct mbuf *m, DPRINTF(("A-MSDU subframe too short (%d)\n", len)); /* stop processing A-MSDU subframes */ ic->ic_stats.is_rx_decap++; + ml_purge(&subframes); m_freem(m); return; } llc = (struct llc *)&eh[1]; - /* examine 802.2 LLC header */ + /* Examine the 802.2 LLC header after the A-MSDU header. */ if (llc->llc_dsap == LLC_SNAP_LSAP && llc->llc_ssap == LLC_SNAP_LSAP && llc->llc_control == LLC_UI && @@ -1212,6 +1258,7 @@ ieee80211_amsdu_decap(struct ieee80211com *ic, struct mbuf *m, /* stop processing A-MSDU subframes */ DPRINTF(("A-MSDU subframe too long (%d)\n", len)); ic->ic_stats.is_rx_decap++; + ml_purge(&subframes); m_freem(m); return; } @@ -1221,16 +1268,29 @@ ieee80211_amsdu_decap(struct ieee80211com *ic, struct mbuf *m, if (n == NULL) { /* stop processing A-MSDU subframes */ ic->ic_stats.is_rx_decap++; + ml_purge(&subframes); m_freem(m); return; } - ieee80211_enqueue_data(ic, m, ni, mcast, ml); + if (ieee80211_amsdu_decap_validate(ic, m, ni)) { + /* stop processing A-MSDU subframes */ + ic->ic_stats.is_rx_decap++; + ml_purge(&subframes); + m_freem(m); + return; + } + + ml_enqueue(&subframes, m); + m = n; /* remove padding */ pad = ((len + 3) & ~3) - len; m_adj(m, pad); } + + while ((n = ml_dequeue(&subframes)) != NULL) + ieee80211_enqueue_data(ic, n, ni, mcast, ml); m_freem(m); } |