--- sys/net80211/ieee80211_adhoc.c.orig +++ sys/net80211/ieee80211_adhoc.c @@ -531,7 +531,7 @@ * 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; @@ -558,7 +558,7 @@ /* * Finally, strip the 802.11 header. */ - m = ieee80211_decap(vap, m, hdrspace); + m = ieee80211_decap(vap, m, hdrspace, qos); if (m == NULL) { /* XXX mask bit to check for both */ /* don't count Null data frames as errors */ @@ -571,7 +571,10 @@ IEEE80211_NODE_STAT(ni, rx_decap); goto err; } - eh = mtod(m, struct ether_header *); + if (!(qos & IEEE80211_QOS_AMSDU)) + eh = mtod(m, struct ether_header *); + else + eh = NULL; if (!ieee80211_node_is_authorized(ni)) { /* * Deny any non-PAE frames received prior to @@ -581,11 +584,13 @@ * the port is not marked authorized by the * authenticator until the handshake has completed. */ - if (eh->ether_type != htons(ETHERTYPE_PAE)) { + if (eh == NULL || + eh->ether_type != htons(ETHERTYPE_PAE)) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, - eh->ether_shost, "data", - "unauthorized port: ether type 0x%x len %u", - eh->ether_type, m->m_pkthdr.len); + ni->ni_macaddr, "data", "unauthorized or " + "unknown port: ether type 0x%x len %u", + eh == NULL ? -1 : eh->ether_type, + m->m_pkthdr.len); vap->iv_stats.is_rx_unauth++; IEEE80211_NODE_STAT(ni, rx_unauth); goto err; @@ -598,7 +603,8 @@ if ((vap->iv_flags & IEEE80211_F_DROPUNENC) && ((has_decrypted == 0) && (m->m_flags & M_WEP) == 0) && (is_hw_decrypted == 0) && - eh->ether_type != htons(ETHERTYPE_PAE)) { + (eh == NULL || + eh->ether_type != htons(ETHERTYPE_PAE))) { /* * Drop unencrypted frames. */ --- sys/net80211/ieee80211_hostap.c.orig +++ sys/net80211/ieee80211_hostap.c @@ -719,7 +719,7 @@ * 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; @@ -744,7 +744,7 @@ /* * Finally, strip the 802.11 header. */ - m = ieee80211_decap(vap, m, hdrspace); + m = ieee80211_decap(vap, m, hdrspace, qos); if (m == NULL) { /* XXX mask bit to check for both */ /* don't count Null data frames as errors */ @@ -757,7 +757,10 @@ IEEE80211_NODE_STAT(ni, rx_decap); goto err; } - eh = mtod(m, struct ether_header *); + if (!(qos & IEEE80211_QOS_AMSDU)) + eh = mtod(m, struct ether_header *); + else + eh = NULL; if (!ieee80211_node_is_authorized(ni)) { /* * Deny any non-PAE frames received prior to @@ -767,11 +770,13 @@ * the port is not marked authorized by the * authenticator until the handshake has completed. */ - if (eh->ether_type != htons(ETHERTYPE_PAE)) { + if (eh == NULL || + eh->ether_type != htons(ETHERTYPE_PAE)) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, - eh->ether_shost, "data", - "unauthorized port: ether type 0x%x len %u", - eh->ether_type, m->m_pkthdr.len); + ni->ni_macaddr, "data", "unauthorized or " + "unknown port: ether type 0x%x len %u", + eh == NULL ? -1 : eh->ether_type, + m->m_pkthdr.len); vap->iv_stats.is_rx_unauth++; IEEE80211_NODE_STAT(ni, rx_unauth); goto err; @@ -784,7 +789,8 @@ if ((vap->iv_flags & IEEE80211_F_DROPUNENC) && ((has_decrypted == 0) && (m->m_flags & M_WEP) == 0) && (is_hw_decrypted == 0) && - eh->ether_type != htons(ETHERTYPE_PAE)) { + (eh == NULL || + eh->ether_type != htons(ETHERTYPE_PAE))) { /* * Drop unencrypted frames. */ --- sys/net80211/ieee80211_input.c.orig +++ sys/net80211/ieee80211_input.c @@ -170,7 +170,8 @@ * 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 @@ 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 @@ 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 @@ 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; } @@ -294,7 +309,8 @@ } struct mbuf * -ieee80211_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen) +ieee80211_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen, + uint8_t qos) { struct ieee80211_qosframe_addr4 wh; struct ether_header *eh; @@ -316,7 +332,9 @@ llc->llc_snap.org_code[1] == 0 && llc->llc_snap.org_code[2] == 0 && /* NB: preserve AppleTalk frames that have a native SNAP hdr */ !(llc->llc_snap.ether_type == htons(ETHERTYPE_AARP) || - llc->llc_snap.ether_type == htons(ETHERTYPE_IPX))) { + llc->llc_snap.ether_type == htons(ETHERTYPE_IPX)) && + /* Do not want to touch A-MSDU frames. */ + !(qos & IEEE80211_QOS_AMSDU)) { m_adj(m, hdrlen + sizeof(struct llc) - sizeof(*eh)); llc = NULL; } else { @@ -364,6 +382,10 @@ #define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc)) struct ether_header *eh; struct llc *llc; + 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 + }; /* * The frame has an 802.3 header followed by an 802.2 @@ -376,6 +398,15 @@ if (m->m_len < FF_LLC_SIZE && (m = m_pullup(m, FF_LLC_SIZE)) == NULL) return NULL; eh = mtod(m, struct ether_header *); /* 802.3 header is first */ + + /* + * Detect possible attack where a single 802.11 frame is processed + * as an A-MSDU frame due to an adversary setting the A-MSDU present + * bit in the 802.11 QoS header. [FragAttacks] + */ + if (memcmp(eh->ether_dhost, llc_hdr_mac, ETHER_ADDR_LEN) == 0) + return NULL; + llc = (struct llc *)&eh[1]; /* 802.2 header follows */ *framelen = ntohs(eh->ether_type) /* encap'd frame size */ + sizeof(struct ether_header) - sizeof(struct llc); --- sys/net80211/ieee80211_input.h.orig +++ sys/net80211/ieee80211_input.h @@ -309,9 +309,10 @@ 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_decap(struct ieee80211vap *, struct mbuf *, int, + uint8_t); struct mbuf *ieee80211_decap1(struct mbuf *, int *); int ieee80211_setup_rates(struct ieee80211_node *ni, const uint8_t *rates, const uint8_t *xrates, int flags); --- sys/net80211/ieee80211_ioctl.c.orig +++ sys/net80211/ieee80211_ioctl.c @@ -1591,7 +1591,7 @@ ("expected opmode IBSS or AHDEMO not %s", ieee80211_opmode_name[vap->iv_opmode])); - if (ssid_len == 0) + if (ssid_len == 0 || ssid_len > IEEE80211_NWID_LEN) return EINVAL; sr = IEEE80211_MALLOC(sizeof(*sr), M_TEMP, --- sys/net80211/ieee80211_mesh.c.orig +++ sys/net80211/ieee80211_mesh.c @@ -1637,7 +1637,7 @@ */ 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; --- sys/net80211/ieee80211_node.c.orig +++ sys/net80211/ieee80211_node.c @@ -1134,7 +1134,7 @@ ie = ies->data; ielen = ies->len; - while (ielen > 0) { + while (ielen > 1) { switch (ie[0]) { case IEEE80211_ELEMID_VENDOR: if (iswpaoui(ie)) --- sys/net80211/ieee80211_sta.c.orig +++ sys/net80211/ieee80211_sta.c @@ -795,7 +795,7 @@ * 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; @@ -827,7 +827,7 @@ /* * Finally, strip the 802.11 header. */ - m = ieee80211_decap(vap, m, hdrspace); + m = ieee80211_decap(vap, m, hdrspace, qos); if (m == NULL) { /* XXX mask bit to check for both */ /* don't count Null data frames as errors */ @@ -840,7 +840,10 @@ IEEE80211_NODE_STAT(ni, rx_decap); goto err; } - eh = mtod(m, struct ether_header *); + if (!(qos & IEEE80211_QOS_AMSDU)) + eh = mtod(m, struct ether_header *); + else + eh = NULL; if (!ieee80211_node_is_authorized(ni)) { /* * Deny any non-PAE frames received prior to @@ -850,11 +853,13 @@ * the port is not marked authorized by the * authenticator until the handshake has completed. */ - if (eh->ether_type != htons(ETHERTYPE_PAE)) { + if (eh == NULL || + eh->ether_type != htons(ETHERTYPE_PAE)) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, - eh->ether_shost, "data", - "unauthorized port: ether type 0x%x len %u", - eh->ether_type, m->m_pkthdr.len); + ni->ni_macaddr, "data", "unauthorized or " + "unknown port: ether type 0x%x len %u", + eh == NULL ? -1 : eh->ether_type, + m->m_pkthdr.len); vap->iv_stats.is_rx_unauth++; IEEE80211_NODE_STAT(ni, rx_unauth); goto err; @@ -867,7 +872,8 @@ if ((vap->iv_flags & IEEE80211_F_DROPUNENC) && ((has_decrypted == 0) && (m->m_flags & M_WEP) == 0) && (is_hw_decrypted == 0) && - eh->ether_type != htons(ETHERTYPE_PAE)) { + (eh == NULL || + eh->ether_type != htons(ETHERTYPE_PAE))) { /* * Drop unencrypted frames. */ --- sys/net80211/ieee80211_wds.c.orig +++ sys/net80211/ieee80211_wds.c @@ -592,7 +592,7 @@ * 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; @@ -619,7 +619,7 @@ /* * Finally, strip the 802.11 header. */ - m = ieee80211_decap(vap, m, hdrspace); + m = ieee80211_decap(vap, m, hdrspace, qos); if (m == NULL) { /* XXX mask bit to check for both */ /* don't count Null data frames as errors */ @@ -632,7 +632,10 @@ IEEE80211_NODE_STAT(ni, rx_decap); goto err; } - eh = mtod(m, struct ether_header *); + if (!(qos & IEEE80211_QOS_AMSDU)) + eh = mtod(m, struct ether_header *); + else + eh = NULL; if (!ieee80211_node_is_authorized(ni)) { /* * Deny any non-PAE frames received prior to @@ -642,11 +645,13 @@ * the port is not marked authorized by the * authenticator until the handshake has completed. */ - if (eh->ether_type != htons(ETHERTYPE_PAE)) { + if (eh == NULL || + eh->ether_type != htons(ETHERTYPE_PAE)) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, - eh->ether_shost, "data", - "unauthorized port: ether type 0x%x len %u", - eh->ether_type, m->m_pkthdr.len); + ni->ni_macaddr, "data", "unauthorized or " + "unknown port: ether type 0x%x len %u", + eh == NULL ? -1 : eh->ether_type, + m->m_pkthdr.len); vap->iv_stats.is_rx_unauth++; IEEE80211_NODE_STAT(ni, rx_unauth); goto err; @@ -659,7 +664,8 @@ if ((vap->iv_flags & IEEE80211_F_DROPUNENC) && ((has_decrypted == 0) && (m->m_flags & M_WEP) == 0) && (is_hw_decrypted == 0) && - eh->ether_type != htons(ETHERTYPE_PAE)) { + (eh == NULL || + eh->ether_type != htons(ETHERTYPE_PAE))) { /* * Drop unencrypted frames. */