untrusted comment: verify with openbsd-68-base.pub
RWQZj25CSG5R2kiUB7dvX85fJT/6luPcuAMPxgNhFbqQRNk3s4eI2zXYxMnAbZ44hdFHY4u1Pz686oMFbj9bxcuSqb8oFG95ZgY=
OpenBSD 6.8 errata 021, May 21, 2021:
Insufficient validation of A-MSDUs and fragmented 802.11 frames could
be abused to inject arbitrary frames.
Apply by doing:
signify -Vep /etc/signify/openbsd-68-base.pub -x 021_net80211.patch.sig \
-m - | (cd /usr/src && patch -p0)
And then rebuild and install a new kernel:
KK=`sysctl -n kern.osversion | cut -d# -f1`
cd /usr/src/sys/arch/`machine`/compile/$KK
make obj
make config
make
make install
Index: sys/net80211/ieee80211_input.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_input.c,v
retrieving revision 1.221
diff -u -p -r1.221 ieee80211_input.c
--- sys/net80211/ieee80211_input.c 28 Aug 2020 12:01:48 -0000 1.221
+++ sys/net80211/ieee80211_input.c 19 May 2021 06:44:40 -0000
@@ -76,6 +76,8 @@ void ieee80211_input_ba_seq(struct ieee8
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 *,
@@ -361,6 +363,20 @@ ieee80211_inputm(struct ifnet *ifp, stru
}
}
+ /*
+ * We do not yet support fragments. Drop any fragmented packets.
+ * Counter-measure against attacks where an arbitrary packet is
+ * injected via a fragment with attacker-controlled content.
+ * See
https://papers.mathyvanhoef.com/usenix2021.pdf
+ * Section 6.8 "Treating fragments as full frames"
+ */
+ if (ieee80211_has_seq(wh)) {
+ uint16_t rxseq = letoh16(*(const u_int16_t *)wh->i_seq);
+ if ((wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) ||
+ (rxseq & IEEE80211_SEQ_FRAG_MASK))
+ goto err;
+ }
+
/* duplicate detection (see 9.2.9) */
if (ieee80211_has_seq(wh) &&
ic->ic_state != IEEE80211_S_SCAN) {
@@ -1130,6 +1146,50 @@ ieee80211_decap(struct ieee80211com *ic,
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).
*/
@@ -1142,6 +1202,7 @@ ieee80211_amsdu_decap(struct ieee80211co
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);
@@ -1149,15 +1210,11 @@ ieee80211_amsdu_decap(struct ieee80211co
/* strip 802.11 header */
m_adj(m, hdrlen);
- for (;;) {
+ while (m->m_pkthdr.len >= ETHER_HDR_LEN + LLC_SNAPFRAMELEN) {
/* process an A-MSDU subframe */
- if (m->m_len < ETHER_HDR_LEN + LLC_SNAPFRAMELEN) {
- m = m_pullup(m, ETHER_HDR_LEN + LLC_SNAPFRAMELEN);
- if (m == NULL) {
- ic->ic_stats.is_rx_decap++;
- break;
- }
- }
+ m = m_pullup(m, ETHER_HDR_LEN + LLC_SNAPFRAMELEN);
+ if (m == NULL)
+ break;
eh = mtod(m, struct ether_header *);
/* examine 802.3 header */
len = ntohs(eh->ether_type);
@@ -1165,11 +1222,12 @@ ieee80211_amsdu_decap(struct ieee80211co
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);
- break;
+ 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 &&
@@ -1189,8 +1247,9 @@ ieee80211_amsdu_decap(struct ieee80211co
/* 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);
- break;
+ return;
}
/* "detach" our A-MSDU subframe from the others */
@@ -1198,20 +1257,31 @@ ieee80211_amsdu_decap(struct ieee80211co
if (n == NULL) {
/* stop processing A-MSDU subframes */
ic->ic_stats.is_rx_decap++;
+ ml_purge(&subframes);
m_freem(m);
- break;
+ return;
}
- ieee80211_enqueue_data(ic, m, ni, mcast, ml);
- if (n->m_pkthdr.len == 0) {
- m_freem(n);
- break;
+ 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);
}
/*