Module Name: src Committed By: maxv Date: Wed Jan 17 16:03:16 UTC 2018
Modified Files: src/sys/net80211: ieee80211_input.c Log Message: Several changes: * Style in several places, to make the code more readable or easier to understand. * Instead of checking m->m_pkthdr.len, check m->m_len. m_pkthdr.len is the total size of the packet, not the size of the current mbuf (which may be smaller). * Add a missing length check when handling QoS frames. * Cast the lengths passed in IEEE80211_VERIFY_LENGTH to size_t. * Remove the length check on scan.sp_xrates, that I added yesterday. xrates gets silently truncated in ieee80211_setup_rates(). * Fix several buffer overflows in the parsers of the MANAGEMENT frames. To generate a diff of this commit: cvs rdiff -u -r1.108 -r1.109 src/sys/net80211/ieee80211_input.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/net80211/ieee80211_input.c diff -u src/sys/net80211/ieee80211_input.c:1.108 src/sys/net80211/ieee80211_input.c:1.109 --- src/sys/net80211/ieee80211_input.c:1.108 Tue Jan 16 18:53:32 2018 +++ src/sys/net80211/ieee80211_input.c Wed Jan 17 16:03:16 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: ieee80211_input.c,v 1.108 2018/01/16 18:53:32 maxv Exp $ */ +/* $NetBSD: ieee80211_input.c,v 1.109 2018/01/17 16:03:16 maxv Exp $ */ /* * Copyright (c) 2001 Atsushi Onoe @@ -37,7 +37,7 @@ __FBSDID("$FreeBSD: src/sys/net80211/ieee80211_input.c,v 1.81 2005/08/10 16:22:29 sam Exp $"); #endif #ifdef __NetBSD__ -__KERNEL_RCSID(0, "$NetBSD: ieee80211_input.c,v 1.108 2018/01/16 18:53:32 maxv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ieee80211_input.c,v 1.109 2018/01/17 16:03:16 maxv Exp $"); #endif #ifdef _KERNEL_OPT @@ -354,8 +354,7 @@ ieee80211_input_data(struct ieee80211com * any non-PAE frames received without encryption. */ if ((ic->ic_flags & IEEE80211_F_DROPUNENC) && - key == NULL && - eh->ether_type != htons(ETHERTYPE_PAE)) { + key == NULL && eh->ether_type != htons(ETHERTYPE_PAE)) { /* * Drop unencrypted frames. */ @@ -407,10 +406,9 @@ ieee80211_input_management(struct ieee80 ic->ic_stats.is_rx_wrongdir++; goto err; } - if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { - IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, - ni->ni_macaddr, "mgt", "too short: len %u", - m->m_pkthdr.len); + if (m->m_len < sizeof(struct ieee80211_frame)) { + IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, ni->ni_macaddr, + "mgt", "too short: len %u", m->m_len); ic->ic_stats.is_rx_tooshort++; goto out; } @@ -542,10 +540,10 @@ ieee80211_input(struct ieee80211com *ic, if (ic->ic_opmode == IEEE80211_M_MONITOR) goto out; - if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) { + if (m->m_len < sizeof(struct ieee80211_frame_min)) { IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, ni->ni_macaddr, NULL, - "too short (1): len %u", m->m_pkthdr.len); + "too short (1): len %u", m->m_len); ic->ic_stats.is_rx_tooshort++; goto out; } @@ -607,11 +605,11 @@ ieee80211_input(struct ieee80211com *ic, else if (type == IEEE80211_FC0_TYPE_CTL) bssid = wh->i_addr1; else { - if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { + if (m->m_len < sizeof(struct ieee80211_frame)) { IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, ni->ni_macaddr, NULL, "too short (2): len %u", - m->m_pkthdr.len); + m->m_len); ic->ic_stats.is_rx_tooshort++; goto out; } @@ -674,7 +672,14 @@ ieee80211_input(struct ieee80211com *ic, if (ieee80211_has_qos(wh)) { struct ieee80211_qosframe *qosf; - /* XXX mbuf length check */ + if (m->m_len < sizeof(struct ieee80211_qosframe)) { + IEEE80211_DISCARD_MAC(ic, + IEEE80211_MSG_ANY, + ni->ni_macaddr, NULL, + "too short (1): len %u", m->m_len); + ic->ic_stats.is_rx_tooshort++; + goto out; + } qosf = mtod(m, struct ieee80211_qosframe *); tid = qosf->i_qos[0] & IEEE80211_QOS_TID; @@ -1089,6 +1094,7 @@ ieee80211_auth_open(struct ieee80211com ni->ni_macaddr, "open auth", "bad sta auth mode %u", ni->ni_authmode); ic->ic_stats.is_rx_bad_auth++; /* XXX */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* XXX hack to workaround calling convention */ ieee80211_send_error(ic, ni, wh->i_addr2, @@ -1097,6 +1103,7 @@ ieee80211_auth_open(struct ieee80211com } return; } + switch (ic->ic_opmode) { case IEEE80211_M_IBSS: case IEEE80211_M_AHDEMO: @@ -1114,13 +1121,16 @@ ieee80211_auth_open(struct ieee80211com ic->ic_stats.is_rx_bad_auth++; return; } + /* always accept open authentication requests */ if (ni == ic->ic_bss) { ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2); if (ni == NULL) return; - } else if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0) - (void) ieee80211_ref_node(ni); + } else if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0) { + (void)ieee80211_ref_node(ni); + } + /* * Mark the node as referenced to reflect that its * reference count has been bumped to insure it remains @@ -1128,11 +1138,12 @@ ieee80211_auth_open(struct ieee80211com */ ni->ni_flags |= IEEE80211_NODE_AREF; - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); + IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, + seq + 1); IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, "[%s] station authenticated (open)\n", ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr)); + /* * When 802.1x is not in use mark the port * authorized at this point so traffic can flow. @@ -1149,20 +1160,22 @@ ieee80211_auth_open(struct ieee80211com return; } if (status != 0) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, "[%s] open auth failed (reason %d)\n", ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr), status); + /* XXX can this happen? */ if (ni != ic->ic_bss) ni->ni_fails++; + ic->ic_stats.is_rx_auth_fail++; ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); - } else + } else { ieee80211_new_state(ic, IEEE80211_S_ASSOC, wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + } break; } } @@ -1176,9 +1189,9 @@ ieee80211_auth_open(struct ieee80211com */ static void ieee80211_send_error(struct ieee80211com *ic, struct ieee80211_node *ni, - const u_int8_t *mac, int subtype, int arg) + const u_int8_t *mac, int subtype, int arg) { - int istmp; + bool istmp; if (ni == ic->ic_bss) { ni = ieee80211_tmp_node(ic, mac); @@ -1186,9 +1199,11 @@ ieee80211_send_error(struct ieee80211com /* XXX msg */ return; } - istmp = 1; - } else - istmp = 0; + istmp = true; + } else { + istmp = false; + } + IEEE80211_SEND_MGMT(ic, ni, subtype, arg); if (istmp) ieee80211_free_node(ni); @@ -1197,11 +1212,12 @@ ieee80211_send_error(struct ieee80211com static int alloc_challenge(struct ieee80211com *ic, struct ieee80211_node *ni) { - if (ni->ni_challenge == NULL) + if (ni->ni_challenge == NULL) { ni->ni_challenge = malloc(IEEE80211_CHALLENGE_LEN, M_DEVBUF, M_NOWAIT); + } if (ni->ni_challenge == NULL) { - IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]); + IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]); IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, "[%s] shared key challenge alloc failed\n", @@ -1236,6 +1252,7 @@ ieee80211_auth_shared(struct ieee80211co estatus = IEEE80211_STATUS_ALG; goto bad; } + /* * Pre-shared key authentication is evil; accept * it only if explicitly configured (it is supported @@ -1266,6 +1283,7 @@ ieee80211_auth_shared(struct ieee80211co challenge = frm; frm += frm[1] + 2; } + switch (seq) { case IEEE80211_AUTH_SHARED_CHALLENGE: case IEEE80211_AUTH_SHARED_RESPONSE: @@ -1288,6 +1306,7 @@ ieee80211_auth_shared(struct ieee80211co default: break; } + switch (ic->ic_opmode) { case IEEE80211_M_MONITOR: case IEEE80211_M_AHDEMO: @@ -1296,6 +1315,7 @@ ieee80211_auth_shared(struct ieee80211co ni->ni_macaddr, "shared key auth", "bad operating mode %u", ic->ic_opmode); return; + case IEEE80211_M_HOSTAP: #ifndef IEEE80211_NO_HOSTAP { @@ -1318,10 +1338,11 @@ ieee80211_auth_shared(struct ieee80211co allocbs = 1; } else { if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0) - (void) ieee80211_ref_node(ni); + (void)ieee80211_ref_node(ni); allocbs = 0; } __USE(allocbs); + /* * Mark the node as referenced to reflect that its * reference count has been bumped to insure it remains @@ -1334,8 +1355,10 @@ ieee80211_auth_shared(struct ieee80211co /* NB: don't return error so they rexmit */ return; } + get_random_bytes(ni->ni_challenge, IEEE80211_CHALLENGE_LEN); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, "[%s] shared key %sauth request\n", @@ -1343,6 +1366,7 @@ ieee80211_auth_shared(struct ieee80211co ni->ni_macaddr), allocbs ? "" : "re"); break; + case IEEE80211_AUTH_SHARED_RESPONSE: if (ni == ic->ic_bss) { IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, @@ -1351,6 +1375,7 @@ ieee80211_auth_shared(struct ieee80211co /* NB: don't send a response */ return; } + if (ni->ni_challenge == NULL) { IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, ni->ni_macaddr, "shared key response", @@ -1359,8 +1384,9 @@ ieee80211_auth_shared(struct ieee80211co estatus = IEEE80211_STATUS_CHALLENGE; goto bad; } + if (memcmp(ni->ni_challenge, &challenge[2], - challenge[1]) != 0) { + challenge[1]) != 0) { IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, ni->ni_macaddr, "shared key response", "%s", "challenge mismatch"); @@ -1368,12 +1394,15 @@ ieee80211_auth_shared(struct ieee80211co estatus = IEEE80211_STATUS_CHALLENGE; goto bad; } + IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, "[%s] station authenticated (shared key)\n", ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr)); + ieee80211_node_authorize(ni); break; + default: IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH, ni->ni_macaddr, "shared key auth", @@ -1382,8 +1411,9 @@ ieee80211_auth_shared(struct ieee80211co estatus = IEEE80211_STATUS_SEQUENCE; goto bad; } - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); + + IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, + seq + 1); } #endif /* !IEEE80211_NO_HOSTAP */ break; @@ -1404,23 +1434,29 @@ ieee80211_auth_shared(struct ieee80211co ether_snprintf(ebuf, sizeof(ebuf), ieee80211_getbssid(ic, wh)), status); + /* XXX can this happen? */ if (ni != ic->ic_bss) ni->ni_fails++; + ic->ic_stats.is_rx_auth_fail++; return; } + ieee80211_new_state(ic, IEEE80211_S_ASSOC, wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); break; + case IEEE80211_AUTH_SHARED_CHALLENGE: if (!alloc_challenge(ic, ni)) return; /* XXX could optimize by passing recvd challenge */ memcpy(ni->ni_challenge, &challenge[2], challenge[1]); - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); + + IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, + seq + 1); break; + default: IEEE80211_DISCARD(ic, IEEE80211_MSG_AUTH, wh, "shared key auth", "bad seq %d", seq); @@ -1430,6 +1466,7 @@ ieee80211_auth_shared(struct ieee80211co break; } return; + bad: #ifndef IEEE80211_NO_HOSTAP /* @@ -1589,30 +1626,34 @@ wpa_keymgmt(u_int8_t *sel) */ static int ieee80211_parse_wpa(struct ieee80211com *ic, u_int8_t *frm, - struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh) + struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh) { u_int8_t len = frm[1]; u_int32_t w; int n; - /* - * Check the length once for fixed parts: OUI, type, - * version, mcast cipher, and 2 selector counts. - * Other, variable-length data, must be checked separately. - */ if ((ic->ic_flags & IEEE80211_F_WPA1) == 0) { IEEE80211_DISCARD_IE(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, wh, "WPA", "not WPA, flags 0x%x", ic->ic_flags); return IEEE80211_REASON_IE_INVALID; } + + /* + * Check the length once for fixed parts: OUI, type, + * version, mcast cipher, and 2 selector counts. + * Other, variable-length data, must be checked separately. + */ if (len < 14) { IEEE80211_DISCARD_IE(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, wh, "WPA", "too short, len %u", len); return IEEE80211_REASON_IE_INVALID; } - frm += 6, len -= 4; /* NB: len is payload only */ + + frm += 2; /* beginning of payload */ + frm += 4, len -= 4; + /* NB: iswapoui already validated the OUI and type */ w = LE_READ_2(frm); if (w != WPA_VERSION) { @@ -1646,7 +1687,7 @@ ieee80211_parse_wpa(struct ieee80211com } w = 0; for (; n > 0; n--) { - w |= 1<<wpa_cipher(frm, &rsn->rsn_ucastkeylen); + w |= 1 << wpa_cipher(frm, &rsn->rsn_ucastkeylen); frm += 4, len -= 4; } w &= rsn->rsn_ucastcipherset; @@ -1656,7 +1697,7 @@ ieee80211_parse_wpa(struct ieee80211com wh, "WPA", "%s", "ucast cipher set empty"); return IEEE80211_REASON_IE_INVALID; } - if (w & (1<<IEEE80211_CIPHER_TKIP)) + if (w & (1 << IEEE80211_CIPHER_TKIP)) rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP; else rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM; @@ -1756,30 +1797,32 @@ rsn_keymgmt(u_int8_t *sel) */ static int ieee80211_parse_rsn(struct ieee80211com *ic, u_int8_t *frm, - struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh) + struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh) { u_int8_t len = frm[1]; u_int32_t w; int n; - /* - * Check the length once for fixed parts: - * version, mcast cipher, and 2 selector counts. - * Other, variable-length data, must be checked separately. - */ if ((ic->ic_flags & IEEE80211_F_WPA2) == 0) { IEEE80211_DISCARD_IE(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, wh, "WPA", "not RSN, flags 0x%x", ic->ic_flags); return IEEE80211_REASON_IE_INVALID; } + + /* + * Check the length once for fixed parts: + * version, mcast cipher, and 2 selector counts. + * Other, variable-length data, must be checked separately. + */ if (len < 10) { IEEE80211_DISCARD_IE(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, wh, "RSN", "too short, len %u", len); return IEEE80211_REASON_IE_INVALID; } - frm += 2; + + frm += 2; /* beginning of payload */ w = LE_READ_2(frm); if (w != RSN_VERSION) { IEEE80211_DISCARD_IE(ic, @@ -1812,7 +1855,7 @@ ieee80211_parse_rsn(struct ieee80211com } w = 0; for (; n > 0; n--) { - w |= 1<<rsn_cipher(frm, &rsn->rsn_ucastkeylen); + w |= 1 << rsn_cipher(frm, &rsn->rsn_ucastkeylen); frm += 4, len -= 4; } w &= rsn->rsn_ucastcipherset; @@ -1822,7 +1865,7 @@ ieee80211_parse_rsn(struct ieee80211com wh, "RSN", "%s", "ucast cipher set empty"); return IEEE80211_REASON_IE_INVALID; } - if (w & (1<<IEEE80211_CIPHER_TKIP)) + if (w & (1 << IEEE80211_CIPHER_TKIP)) rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP; else rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM; @@ -1864,7 +1907,7 @@ ieee80211_parse_rsn(struct ieee80211com static int ieee80211_parse_wmeparams(struct ieee80211com *ic, u_int8_t *frm, - const struct ieee80211_frame *wh) + const struct ieee80211_frame *wh) { #define MS(_v, _f) (((_v) & _f) >> _f##_S) struct ieee80211_wme_state *wme = &ic->ic_wme; @@ -1907,9 +1950,8 @@ void ieee80211_saveie(u_int8_t **iep, const u_int8_t *ie) { u_int ielen = ie[1]+2; - /* - * Record information element for later use. - */ + + /* Record information element for later use. */ if (*iep == NULL || (*iep)[1] != ie[1]) { if (*iep != NULL) free(*iep, M_DEVBUF); @@ -2004,7 +2046,7 @@ ieee80211_update_adhoc_node(struct ieee8 } while (0) #define IEEE80211_VERIFY_LENGTH(_len, _minlen) do { \ - if ((_len) < (_minlen)) { \ + if ((size_t)(_len) < (size_t)(_minlen)) { \ IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \ wh, ieee80211_mgt_subtype_name[subtype >> \ IEEE80211_FC0_SUBTYPE_SHIFT], \ @@ -2106,12 +2148,6 @@ ieee80211_recv_mgmt_beacon(struct ieee80 case IEEE80211_ELEMID_IBSSPARMS: break; case IEEE80211_ELEMID_XRATES: - if (frm[1] > IEEE80211_RATE_MAXSIZE) { - IEEE80211_DISCARD_IE(ic, IEEE80211_MSG_ELEMID, - wh, "XRATE", "bad len %u", frm[1]); - ic->ic_stats.is_rx_elem_toobig++; - break; - } scan.sp_xrates = frm; break; case IEEE80211_ELEMID_ERP: @@ -2225,11 +2261,13 @@ ieee80211_recv_mgmt_beacon(struct ieee80 "[%s] erp change: was 0x%x, now 0x%x\n", ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2), ni->ni_erp, scan.sp_erp); + if (ic->ic_curmode == IEEE80211_MODE_11G && - (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)) + (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)) { ic->ic_flags |= IEEE80211_F_USEPROT; - else + } else { ic->ic_flags &= ~IEEE80211_F_USEPROT; + } ni->ni_erp = scan.sp_erp; } @@ -2304,7 +2342,7 @@ ieee80211_recv_mgmt_probe_req(struct iee struct ieee80211_frame *wh; u_int8_t *frm, *efrm; u_int8_t *ssid, *rates, *xrates; - int allocbs; + bool need_free = false; u_int8_t rate; IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]); @@ -2319,7 +2357,7 @@ ieee80211_recv_mgmt_probe_req(struct iee } if (IEEE80211_IS_MULTICAST(wh->i_addr2)) { /* frame must be directed */ - ic->ic_stats.is_rx_mgtdiscard++; /* XXX stat */ + ic->ic_stats.is_rx_mgtdiscard++; return; } @@ -2330,7 +2368,9 @@ ieee80211_recv_mgmt_probe_req(struct iee * [tlv] extended supported rates */ ssid = rates = xrates = NULL; - while (frm < efrm) { + while (frm + 1 < efrm) { + IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2); + switch (*frm) { case IEEE80211_ELEMID_SSID: ssid = frm; @@ -2342,6 +2382,7 @@ ieee80211_recv_mgmt_probe_req(struct iee xrates = frm; break; } + frm += frm[1] + 2; } @@ -2361,6 +2402,7 @@ ieee80211_recv_mgmt_probe_req(struct iee if (ni == ic->ic_bss) { if (ic->ic_opmode != IEEE80211_M_IBSS) { ni = ieee80211_tmp_node(ic, wh->i_addr2); + need_free = true; } else if (IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) { ; } else { @@ -2375,9 +2417,6 @@ ieee80211_recv_mgmt_probe_req(struct iee } if (ni == NULL) return; - allocbs = 1; - } else { - allocbs = 0; } IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, "[%s] recv probe req\n", @@ -2386,8 +2425,8 @@ ieee80211_recv_mgmt_probe_req(struct iee ni->ni_rssi = rssi; ni->ni_rstamp = rstamp; rate = ieee80211_setup_rates(ni, rates, xrates, - IEEE80211_R_DOSORT | IEEE80211_R_DOFRATE - | IEEE80211_R_DONEGO | IEEE80211_R_DODEL); + IEEE80211_R_DOSORT | IEEE80211_R_DOFRATE | + IEEE80211_R_DONEGO | IEEE80211_R_DODEL); if (rate & IEEE80211_RATE_BASIC) { IEEE80211_DISCARD(ic, IEEE80211_MSG_XRATE, @@ -2399,7 +2438,7 @@ ieee80211_recv_mgmt_probe_req(struct iee IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0); } - if (allocbs && ic->ic_opmode != IEEE80211_M_IBSS) { + if (need_free) { /* reclaim immediately */ ieee80211_free_node(ni); } @@ -2464,13 +2503,12 @@ ieee80211_recv_mgmt_auth(struct ieee8021 return; } - if (algo == IEEE80211_AUTH_ALG_SHARED) + if (algo == IEEE80211_AUTH_ALG_SHARED) { ieee80211_auth_shared(ic, wh, frm + 6, efrm, ni, rssi, rstamp, seq, status); - else if (algo == IEEE80211_AUTH_ALG_OPEN) - ieee80211_auth_open(ic, wh, ni, rssi, rstamp, seq, - status); - else { + } else if (algo == IEEE80211_AUTH_ALG_OPEN) { + ieee80211_auth_open(ic, wh, ni, rssi, rstamp, seq, status); + } else { IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, wh, "auth", "unsupported alg %d", algo); ic->ic_stats.is_rx_auth_unsupported++; @@ -2482,7 +2520,6 @@ ieee80211_recv_mgmt_auth(struct ieee8021 (seq+1) | (IEEE80211_STATUS_ALG<<16)); } #endif - return; } } @@ -2529,6 +2566,7 @@ ieee80211_recv_mgmt_assoc_req(struct iee * [tlv] WPA or RSN */ IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4)); + if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) { IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY, wh, ieee80211_mgt_subtype_name[subtype >> @@ -2544,7 +2582,9 @@ ieee80211_recv_mgmt_assoc_req(struct iee frm += 6; /* ignore current AP info */ ssid = rates = xrates = wpa = wme = NULL; - while (frm < efrm) { + while (frm + 1 < efrm) { + IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2); + switch (*frm) { case IEEE80211_ELEMID_SSID: ssid = frm; @@ -2567,6 +2607,7 @@ ieee80211_recv_mgmt_assoc_req(struct iee /* XXX Atheros OUI support */ break; } + frm += frm[1] + 2; } @@ -2586,16 +2627,18 @@ ieee80211_recv_mgmt_assoc_req(struct iee return; } - /* assert right associstion security credentials */ + /* assert right association security credentials */ if (wpa == NULL && (ic->ic_flags & IEEE80211_F_WPA)) { IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, "[%s] no WPA/RSN IE in association request\n", ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2)); + IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, IEEE80211_REASON_RSN_REQUIRED); ieee80211_node_leave(ic, ni); + /* XXX distinguish WPA/RSN? */ ic->ic_stats.is_rx_assoc_badwpaie++; return; @@ -2614,14 +2657,17 @@ ieee80211_recv_mgmt_assoc_req(struct iee reason = ieee80211_parse_wpa(ic, wpa, &rsn, wh); else reason = ieee80211_parse_rsn(ic, wpa, &rsn, wh); + if (reason != 0) { IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, reason); ieee80211_node_leave(ic, ni); + /* XXX distinguish WPA/RSN? */ ic->ic_stats.is_rx_assoc_badwpaie++; return; } + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, "[%s] %s ie: mc %u/%u uc %u/%u key %u caps 0x%x\n", @@ -2726,7 +2772,7 @@ ieee80211_recv_mgmt_assoc_resp(struct ie { struct ieee80211_frame *wh; u_int8_t *frm, *efrm; - u_int8_t *rates, *xrates, *wpa, *wme; + u_int8_t *rates, *xrates, *wme; u_int8_t rate; IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]); u_int16_t capinfo, associd; @@ -2770,8 +2816,10 @@ ieee80211_recv_mgmt_assoc_resp(struct ie associd = le16toh(*(u_int16_t *)frm); frm += 2; - rates = xrates = wpa = wme = NULL; - while (frm < efrm) { + rates = xrates = wme = NULL; + while (frm + 1 < efrm) { + IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2); + switch (*frm) { case IEEE80211_ELEMID_RATES: rates = frm; @@ -2785,13 +2833,16 @@ ieee80211_recv_mgmt_assoc_resp(struct ie /* XXX Atheros OUI support */ break; } + frm += frm[1] + 2; } IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); + rate = ieee80211_setup_rates(ni, rates, xrates, - IEEE80211_R_DOSORT | IEEE80211_R_DOFRATE | - IEEE80211_R_DONEGO | IEEE80211_R_DODEL); + IEEE80211_R_DOSORT | IEEE80211_R_DOFRATE | + IEEE80211_R_DONEGO | IEEE80211_R_DODEL); + if (rate & IEEE80211_RATE_BASIC) { IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, "[%s] %sassoc failed (rate set mismatch)\n", @@ -2806,8 +2857,7 @@ ieee80211_recv_mgmt_assoc_resp(struct ie ni->ni_capinfo = capinfo; ni->ni_associd = associd; - if (wme != NULL && - ieee80211_parse_wmeparams(ic, wme, wh) >= 0) { + if (wme != NULL && ieee80211_parse_wmeparams(ic, wme, wh) >= 0) { ni->ni_flags |= IEEE80211_NODE_QOS; ieee80211_wme_updateparams(ic); } else { @@ -2838,10 +2888,11 @@ ieee80211_recv_mgmt_assoc_resp(struct ie * XXX check ic_curmode anyway? */ if (ic->ic_curmode == IEEE80211_MODE_11G && - (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)) + (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)) { ic->ic_flags |= IEEE80211_F_USEPROT; - else + } else { ic->ic_flags &= ~IEEE80211_F_USEPROT; + } IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, "[%s] %sassoc success: %s preamble, %s slot time%s%s\n", @@ -3060,16 +3111,19 @@ ieee80211_node_pwrsave(struct ieee80211_ ic->ic_set_tim(ni, 0); /* just in case */ return; } + IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, "[%s] flush ps queue, %u packets queued\n", ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr), IEEE80211_NODE_SAVEQ_QLEN(ni)); + for (;;) { int qlen; IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen); if (m == NULL) break; + /* * If this is the last packet, turn off the TIM bit. * If there are more packets, set the more packets bit @@ -3078,10 +3132,12 @@ ieee80211_node_pwrsave(struct ieee80211_ */ if (qlen != 0) m->m_flags |= M_MORE_DATA; + /* XXX need different driver interface */ /* XXX bypasses q max */ IF_ENQUEUE(&ic->ic_ifp->if_snd, m); } + if (ic->ic_set_tim != NULL) ic->ic_set_tim(ni, 0); } @@ -3090,8 +3146,8 @@ ieee80211_node_pwrsave(struct ieee80211_ * Process a received ps-poll frame. */ static void -ieee80211_recv_pspoll(struct ieee80211com *ic, - struct ieee80211_node *ni, struct mbuf *m0) +ieee80211_recv_pspoll(struct ieee80211com *ic, struct ieee80211_node *ni, + struct mbuf *m0) { struct ieee80211_frame_min *wh; struct mbuf *m; @@ -3102,7 +3158,7 @@ ieee80211_recv_pspoll(struct ieee80211co wh = mtod(m0, struct ieee80211_frame_min *); if (ni->ni_associd == 0) { IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG, - (struct ieee80211_frame *) wh, "ps-poll", + (struct ieee80211_frame *)wh, "ps-poll", "%s", "unassociated station"); ic->ic_stats.is_ps_unassoc++; IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, @@ -3113,7 +3169,7 @@ ieee80211_recv_pspoll(struct ieee80211co aid = le16toh(*(u_int16_t *)wh->i_dur); if (aid != ni->ni_associd) { IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG, - (struct ieee80211_frame *) wh, "ps-poll", + (struct ieee80211_frame *)wh, "ps-poll", "aid mismatch: sta aid 0x%x poll aid 0x%x", ni->ni_associd, aid); ic->ic_stats.is_ps_badaid++; @@ -3134,6 +3190,7 @@ ieee80211_recv_pspoll(struct ieee80211co ic->ic_set_tim(ni, 0); /* just in case */ return; } + /* * If there are more packets, set the more packets bit * in the packet dispatched to the station; otherwise @@ -3151,6 +3208,7 @@ ieee80211_recv_pspoll(struct ieee80211co if (ic->ic_set_tim != NULL) ic->ic_set_tim(ni, 0); } + m->m_flags |= M_PWR_SAV; /* bypass PS handling */ IF_ENQUEUE(&ic->ic_ifp->if_snd, m); }