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); } /*