summaryrefslogtreecommitdiff
path: root/sys/net80211/ieee80211_input.c
diff options
context:
space:
mode:
authorDamien Bergamini <damien@cvs.openbsd.org>2008-04-16 18:32:16 +0000
committerDamien Bergamini <damien@cvs.openbsd.org>2008-04-16 18:32:16 +0000
commit792a7af405f1bcd074df2d192736f0ff71180ce6 (patch)
treed1c8127338d31609e63b5a8c220604e11d416fa8 /sys/net80211/ieee80211_input.c
parentc257d1252aa088da82103c7952e3c42933d743da (diff)
Kernel implementation of the 4-way handshake and group-key
handshake protocols (both supplicant and authenticator state machines) as defined in the IEEE 802.11i standard. Software implementation of the TKIP (Temporal Key Integrity Protocol) and CCMP (CTR with CBC-MAC Protocol) protocols. This diff doesn't implement any of the 802.1X authentication protocols and thus only PSK authentication (using pre-shared keys) is currently supported. In concrete terms, this adds support for WPA-PSK and WPA2-PSK protocols, both in station and hostap modes. The following drivers are marked as WPA-capable and should work: bwi(4), malo(4), ral(4), iwn(4), wpi(4), ural(4), rum(4), upgt(4), and zyd(4) The following options have been added to ifconfig(8): wpa, wpapsk, wpaprotos, wpaakms, wpaciphers, wpagroupcipher wpa-psk(8) can be used to generate keys from passphrases. tested by many@ ok deraadt@
Diffstat (limited to 'sys/net80211/ieee80211_input.c')
-rw-r--r--sys/net80211/ieee80211_input.c1071
1 files changed, 660 insertions, 411 deletions
diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c
index f9b8d6dcc22..5e1228dfd7b 100644
--- a/sys/net80211/ieee80211_input.c
+++ b/sys/net80211/ieee80211_input.c
@@ -1,9 +1,10 @@
/* $NetBSD: ieee80211_input.c,v 1.24 2004/05/31 11:12:24 dyoung Exp $ */
-/* $OpenBSD: ieee80211_input.c,v 1.72 2007/10/29 15:40:23 chl Exp $ */
+/* $OpenBSD: ieee80211_input.c,v 1.73 2008/04/16 18:32:15 damien Exp $ */
+
/*-
* Copyright (c) 2001 Atsushi Onoe
* Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
- * Copyright (c) 2007 Damien Bergamini
+ * Copyright (c) 2007, 2008 Damien Bergamini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -63,7 +64,6 @@
#include <dev/rndvar.h>
-
int ieee80211_setup_rates(struct ieee80211com *, struct ieee80211_node *,
const u_int8_t *, const u_int8_t *, int);
void ieee80211_auth_open(struct ieee80211com *,
@@ -75,30 +75,30 @@ int ieee80211_parse_edca_params(struct ieee80211com *, const u_int8_t *);
int ieee80211_parse_wmm_params(struct ieee80211com *, const u_int8_t *);
enum ieee80211_cipher ieee80211_parse_rsn_cipher(const u_int8_t[]);
enum ieee80211_akm ieee80211_parse_rsn_akm(const u_int8_t[]);
-int ieee80211_parse_rsn_body(struct ieee80211com *,
- struct ieee80211_node *, const u_int8_t *, u_int);
-int ieee80211_parse_rsn(struct ieee80211com *, struct ieee80211_node *,
- const u_int8_t *);
-int ieee80211_parse_wpa1(struct ieee80211com *, struct ieee80211_node *,
- const u_int8_t *);
+int ieee80211_parse_rsn_body(struct ieee80211com *, const u_int8_t *,
+ u_int, struct ieee80211_rsnparams *);
+int ieee80211_parse_rsn(struct ieee80211com *, const u_int8_t *,
+ struct ieee80211_rsnparams *);
+int ieee80211_parse_wpa(struct ieee80211com *, const u_int8_t *,
+ struct ieee80211_rsnparams *);
int ieee80211_save_ie(const u_int8_t *, u_int8_t **);
void ieee80211_recv_pspoll(struct ieee80211com *, struct mbuf *, int,
u_int32_t);
int ieee80211_do_slow_print(struct ieee80211com *, int *);
void ieee80211_recv_probe_resp(struct ieee80211com *, struct mbuf *,
- struct ieee80211_node *, int, u_int32_t);
+ struct ieee80211_node *, int, u_int32_t, int);
void ieee80211_recv_probe_req(struct ieee80211com *, struct mbuf *,
struct ieee80211_node *, int, u_int32_t);
void ieee80211_recv_auth(struct ieee80211com *, struct mbuf *,
struct ieee80211_node *, int, u_int32_t);
void ieee80211_recv_assoc_req(struct ieee80211com *, struct mbuf *,
- struct ieee80211_node *, int, u_int32_t);
+ struct ieee80211_node *, int, u_int32_t, int);
void ieee80211_recv_assoc_resp(struct ieee80211com *, struct mbuf *,
- struct ieee80211_node *, int, u_int32_t);
+ struct ieee80211_node *, int);
void ieee80211_recv_deauth(struct ieee80211com *, struct mbuf *,
- struct ieee80211_node *, int, u_int32_t);
+ struct ieee80211_node *);
void ieee80211_recv_disassoc(struct ieee80211com *, struct mbuf *,
- struct ieee80211_node *, int, u_int32_t);
+ struct ieee80211_node *);
void ieee80211_recv_4way_msg1(struct ieee80211com *,
struct ieee80211_eapol_key *, struct ieee80211_node *);
void ieee80211_recv_4way_msg2(struct ieee80211com *,
@@ -156,7 +156,7 @@ ieee80211_get_hdrlen(const void *data)
*/
void
ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
- int rssi, u_int32_t rstamp)
+ int rssi, u_int32_t rstamp)
{
struct ieee80211com *ic = (void *)ifp;
struct ieee80211_frame *wh;
@@ -167,7 +167,7 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
u_int16_t orxseq, nrxseq;
if (ni == NULL)
- panic("null mode");
+ panic("null node");
/* trim CRC here so WEP can find its own CRC at the end of packet. */
if (m->m_flags & M_HASFCS) {
@@ -241,34 +241,35 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
ni->ni_inact = 0;
}
- if (ic->ic_set_tim != NULL &&
- (wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) &&
- ni->ni_pwrsave == 0) {
- /* turn on power save mode */
+ if ((ic->ic_opmode == IEEE80211_M_HOSTAP ||
+ ic->ic_opmode == IEEE80211_M_IBSS) && ic->ic_set_tim != NULL) {
+ if ((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) &&
+ ni->ni_pwrsave == 0) {
+ /* turn on power save mode */
- if (ifp->if_flags & IFF_DEBUG)
- printf("%s: power save mode on for %s\n",
- ifp->if_xname, ether_sprintf(wh->i_addr2));
-
- ni->ni_pwrsave = IEEE80211_PS_SLEEP;
- }
- if (ic->ic_set_tim != NULL &&
- !(wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) &&
- ni->ni_pwrsave != 0) {
- /* turn off power save mode, dequeue stored packets */
+ if (ifp->if_flags & IFF_DEBUG)
+ printf("%s: power save mode on for %s\n",
+ ifp->if_xname, ether_sprintf(wh->i_addr2));
- ni->ni_pwrsave = 0;
- (*ic->ic_set_tim)(ic, ni->ni_associd, 0);
+ ni->ni_pwrsave = IEEE80211_PS_SLEEP;
+ }
+ if (!(wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) &&
+ ni->ni_pwrsave != 0) {
+ /* turn off power save mode, dequeue stored packets */
- if (ifp->if_flags & IFF_DEBUG)
- printf("%s: power save mode off for %s\n",
- ifp->if_xname, ether_sprintf(wh->i_addr2));
+ ni->ni_pwrsave = 0;
+ (*ic->ic_set_tim)(ic, ni->ni_associd, 0);
- while (!IF_IS_EMPTY(&ni->ni_savedq)) {
- struct mbuf *m;
- IF_DEQUEUE(&ni->ni_savedq, m);
- IF_ENQUEUE(&ic->ic_pwrsaveq, m);
- (*ifp->if_start)(ifp);
+ if (ifp->if_flags & IFF_DEBUG)
+ printf("%s: power save mode off for %s\n",
+ ifp->if_xname, ether_sprintf(wh->i_addr2));
+
+ while (!IF_IS_EMPTY(&ni->ni_savedq)) {
+ struct mbuf *m;
+ IF_DEQUEUE(&ni->ni_savedq, m);
+ IF_ENQUEUE(&ic->ic_pwrsaveq, m);
+ (*ifp->if_start)(ifp);
+ }
}
}
@@ -371,9 +372,10 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
hdrlen = ieee80211_get_hdrlen(wh);
- if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- if (ic->ic_flags & IEEE80211_F_WEPON) {
- m = ieee80211_wep_crypt(ifp, m, 0);
+ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
+ if (ic->ic_flags &
+ (IEEE80211_F_WEPON | IEEE80211_F_RSNON)) {
+ m = ieee80211_decrypt(ic, m, ni);
if (m == NULL) {
ic->ic_stats.is_rx_wepfail++;
goto err;
@@ -384,6 +386,10 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
goto out;
}
}
+ /*
+ * XXX else: drivers should pass a flag to indicate if the
+ * frame was successfully decrypted or not.
+ */
#if NBPFILTER > 0
/* copy to listener after decrypt */
if (ic->ic_rawbpf)
@@ -398,21 +404,24 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
goto err;
}
eh = mtod(m, struct ether_header *);
-#if 0
- if (!ni->ni_port_valid &&
+
+ if ((ic->ic_flags & IEEE80211_F_RSNON) && !ni->ni_port_valid &&
eh->ether_type != htons(ETHERTYPE_PAE)) {
IEEE80211_DPRINTF(("%s: port not valid: %s\n",
__func__, ether_sprintf(wh->i_addr2)));
ic->ic_stats.is_rx_unauth++;
goto err;
}
-#endif
ifp->if_ipackets++;
- /* perform as a bridge within the AP */
+ /*
+ * Perform as a bridge within the AP. XXX we do not bridge
+ * 802.1X frames as suggested in C.1.1 of IEEE Std 802.1X.
+ */
m1 = NULL;
if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
- (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0) {
+ !(ic->ic_flags & IEEE80211_F_NOBRIDGE) &&
+ eh->ether_type != htons(ETHERTYPE_PAE)) {
if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
m1 = m_copym(m, 0, M_COPYALL, M_DONTWAIT);
if (m1 == NULL)
@@ -420,6 +429,7 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
else
m1->m_flags |= M_MCAST;
} else {
+ struct ieee80211_node *ni;
ni = ieee80211_find_node(ic, eh->ether_dhost);
if (ni != NULL) {
if (ni->ni_associd != 0) {
@@ -449,7 +459,11 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
if (ifp->if_bpf && m1 == NULL)
bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN);
#endif
- ether_input_mbuf(ifp, m);
+ if (eh->ether_type == htons(ETHERTYPE_PAE)) {
+ (*ic->ic_recv_eapol)(ic, m, ni);
+ m_freem(m);
+ } else
+ ether_input_mbuf(ifp, m);
}
return;
@@ -823,6 +837,13 @@ ieee80211_auth_open(struct ieee80211com *ic, const struct ieee80211_frame *wh,
ic->ic_state, seq));
return;
}
+ if (ic->ic_flags & IEEE80211_F_RSNON) {
+ /* XXX not here! */
+ ic->ic_bss->ni_port_valid = 0;
+ ic->ic_bss->ni_replaycnt_ok = 0;
+ (*ic->ic_delete_key)(ic, ic->ic_bss,
+ &ic->ic_bss->ni_pairwise_key);
+ }
if (status != 0) {
if (ifp->if_flags & IFF_DEBUG)
printf("%s: open authentication failed "
@@ -923,8 +944,8 @@ enum ieee80211_cipher
ieee80211_parse_rsn_cipher(const u_int8_t selector[4])
{
/* from IEEE Std 802.11i-2004 - Table 20da */
- if (memcmp(selector, MICROSOFT_OUI, 3) == 0 || /* WPA1 */
- memcmp(selector, IEEE80211_OUI, 3) == 0) { /* RSN (aka WPA2) */
+ if (memcmp(selector, MICROSOFT_OUI, 3) == 0 || /* WPA */
+ memcmp(selector, IEEE80211_OUI, 3) == 0) { /* RSN */
switch (selector[3]) {
case 0: /* use group cipher suite */
return IEEE80211_CIPHER_USEGROUP;
@@ -945,8 +966,8 @@ enum ieee80211_akm
ieee80211_parse_rsn_akm(const u_int8_t selector[4])
{
/* from IEEE Std 802.11i-2004 - Table 20dc */
- if (memcmp(selector, MICROSOFT_OUI, 3) == 0 || /* WPA1 */
- memcmp(selector, IEEE80211_OUI, 3) == 0) { /* RSN (aka WPA2) */
+ if (memcmp(selector, MICROSOFT_OUI, 3) == 0 || /* WPA */
+ memcmp(selector, IEEE80211_OUI, 3) == 0) { /* RSN */
switch (selector[3]) {
case 1: /* IEEE 802.1X (RSNA default) */
return IEEE80211_AKM_IEEE8021X;
@@ -961,78 +982,79 @@ ieee80211_parse_rsn_akm(const u_int8_t selector[4])
* Parse an RSN element (see 7.3.2.25).
*/
int
-ieee80211_parse_rsn_body(struct ieee80211com *ic, struct ieee80211_node *ni,
- const u_int8_t *frm, u_int len)
+ieee80211_parse_rsn_body(struct ieee80211com *ic, const u_int8_t *frm,
+ u_int len, struct ieee80211_rsnparams *rsn)
{
const u_int8_t *efrm;
u_int16_t m, n, s;
- u_int16_t rsncaps;
- enum ieee80211_cipher group_cipher;
- u_int akmset, pairwise_cipherset;
efrm = frm + len;
/* check Version field */
if (LE_READ_2(frm) != 1)
- return IEEE80211_REASON_RSN_IE_VER_UNSUP;
+ return IEEE80211_STATUS_RSN_IE_VER_UNSUP;
frm += 2;
/* all fields after the Version field are optional */
/* if Cipher Suite missing, default to CCMP */
- ni->ni_group_cipher = IEEE80211_CIPHER_CCMP;
- ni->ni_pairwise_cipherset = IEEE80211_CIPHER_CCMP;
+ rsn->rsn_groupcipher = IEEE80211_CIPHER_CCMP;
+ rsn->rsn_nciphers = 1;
+ rsn->rsn_ciphers = IEEE80211_CIPHER_CCMP;
/* if AKM Suite missing, default to 802.1X */
- ni->ni_akmset = IEEE80211_AKM_IEEE8021X;
+ rsn->rsn_nakms = 1;
+ rsn->rsn_akms = IEEE80211_AKM_IEEE8021X;
+ /* if RSN capabilities missing, default to 0 */
+ rsn->rsn_caps = 0;
/* read Group Cipher Suite field */
if (frm + 4 > efrm)
return 0;
- group_cipher = ieee80211_parse_rsn_cipher(frm);
- if (group_cipher == IEEE80211_CIPHER_USEGROUP)
- return IEEE80211_REASON_BAD_GROUP_CIPHER;
+ rsn->rsn_groupcipher = ieee80211_parse_rsn_cipher(frm);
+ if (rsn->rsn_groupcipher == IEEE80211_CIPHER_USEGROUP)
+ return IEEE80211_STATUS_BAD_GROUP_CIPHER;
frm += 4;
/* read Pairwise Cipher Suite Count field */
if (frm + 2 > efrm)
return 0;
- m = LE_READ_2(frm);
+ m = rsn->rsn_nciphers = LE_READ_2(frm);
frm += 2;
/* read Pairwise Cipher Suite List */
if (frm + m * 4 > efrm)
- return IEEE80211_REASON_IE_INVALID;
- pairwise_cipherset = IEEE80211_CIPHER_NONE;
+ return IEEE80211_STATUS_IE_INVALID;
+ rsn->rsn_ciphers = IEEE80211_CIPHER_NONE;
while (m-- > 0) {
- pairwise_cipherset |= ieee80211_parse_rsn_cipher(frm);
+ rsn->rsn_ciphers |= ieee80211_parse_rsn_cipher(frm);
frm += 4;
}
- if (pairwise_cipherset & IEEE80211_CIPHER_USEGROUP) {
- if (pairwise_cipherset != IEEE80211_CIPHER_USEGROUP)
- return IEEE80211_REASON_BAD_PAIRWISE_CIPHER;
- if (group_cipher == IEEE80211_CIPHER_CCMP)
- return IEEE80211_REASON_BAD_PAIRWISE_CIPHER;
+ if (rsn->rsn_ciphers & IEEE80211_CIPHER_USEGROUP) {
+ if (rsn->rsn_ciphers != IEEE80211_CIPHER_USEGROUP)
+ return IEEE80211_STATUS_BAD_PAIRWISE_CIPHER;
+ if (rsn->rsn_groupcipher == IEEE80211_CIPHER_CCMP)
+ return IEEE80211_STATUS_BAD_PAIRWISE_CIPHER;
}
/* read AKM Suite List Count field */
if (frm + 2 > efrm)
return 0;
- n = LE_READ_2(frm);
+ n = rsn->rsn_nakms = LE_READ_2(frm);
frm += 2;
/* read AKM Suite List */
if (frm + n * 4 > efrm)
- return IEEE80211_REASON_IE_INVALID;
- akmset = IEEE80211_AKM_NONE;
+ return IEEE80211_STATUS_IE_INVALID;
+ rsn->rsn_akms = IEEE80211_AKM_NONE;
while (n-- > 0) {
- akmset |= ieee80211_parse_rsn_akm(frm);
+ rsn->rsn_akms |= ieee80211_parse_rsn_akm(frm);
frm += 4;
}
/* read RSN Capabilities field */
if (frm + 2 > efrm)
return 0;
- rsncaps = LE_READ_2(frm);
+ rsn->rsn_caps = LE_READ_2(frm);
frm += 2;
/* read PMKID Count field */
@@ -1043,50 +1065,45 @@ ieee80211_parse_rsn_body(struct ieee80211com *ic, struct ieee80211_node *ni,
/* read PMKID List */
if (frm + s * IEEE80211_PMKID_LEN > efrm)
- return IEEE80211_REASON_IE_INVALID;
+ return IEEE80211_STATUS_IE_INVALID;
while (s-- > 0) {
/* ignore PMKIDs for now */
frm += IEEE80211_PMKID_LEN;
}
- ni->ni_group_cipher = group_cipher;
- ni->ni_pairwise_cipherset = pairwise_cipherset;
- ni->ni_akmset = akmset;
- ni->ni_rsncaps = rsncaps;
-
- return 0;
+ return IEEE80211_STATUS_SUCCESS;
}
int
-ieee80211_parse_rsn(struct ieee80211com *ic, struct ieee80211_node *ni,
- const u_int8_t *frm)
+ieee80211_parse_rsn(struct ieee80211com *ic, const u_int8_t *frm,
+ struct ieee80211_rsnparams *rsn)
{
/* check IE length */
if (frm[1] < 2) {
IEEE80211_DPRINTF(("%s: invalid RSN/WPA2 IE;"
" length %u, expecting at least 2\n", __func__, frm[1]));
ic->ic_stats.is_rx_elem_toosmall++;
- return IEEE80211_REASON_IE_INVALID;
+ return IEEE80211_STATUS_IE_INVALID;
}
- return ieee80211_parse_rsn_body(ic, ni, frm + 2, frm[1]);
+ return ieee80211_parse_rsn_body(ic, frm + 2, frm[1], rsn);
}
int
-ieee80211_parse_wpa1(struct ieee80211com *ic, struct ieee80211_node *ni,
- const u_int8_t *frm)
+ieee80211_parse_wpa(struct ieee80211com *ic, const u_int8_t *frm,
+ struct ieee80211_rsnparams *rsn)
{
/* check IE length */
if (frm[1] < 6) {
- IEEE80211_DPRINTF(("%s: invalid WPA1 IE;"
+ IEEE80211_DPRINTF(("%s: invalid WPA IE;"
" length %u, expecting at least 6\n", __func__, frm[1]));
ic->ic_stats.is_rx_elem_toosmall++;
- return IEEE80211_REASON_IE_INVALID;
+ return IEEE80211_STATUS_IE_INVALID;
}
- return ieee80211_parse_rsn_body(ic, ni, frm + 6, frm[1] - 4);
+ return ieee80211_parse_rsn_body(ic, frm + 6, frm[1] - 4, rsn);
}
/*
- * Create a copy of an information element.
+ * Create (or update) a copy of an information element.
*/
int
ieee80211_save_ie(const u_int8_t *frm, u_int8_t **ie)
@@ -1119,15 +1136,12 @@ ieee80211_save_ie(const u_int8_t *frm, u_int8_t **ie)
*/
void
ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m0,
- struct ieee80211_node *ni, int rssi, u_int32_t rstamp)
+ struct ieee80211_node *ni, int rssi, u_int32_t rstamp, int isprobe)
{
-#define ISPROBE(_wh) (((_wh)->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == \
- IEEE80211_FC0_SUBTYPE_PROBE_RESP)
-
const struct ieee80211_frame *wh;
const u_int8_t *frm, *efrm;
- const u_int8_t *tstamp, *ssid, *rates, *xrates, *edca, *wmm;
- const u_int8_t *rsn, *wpa;
+ const u_int8_t *tstamp, *ssid, *rates, *xrates, *edcaie, *wmmie;
+ const u_int8_t *rsnie, *wpaie;
u_int16_t capinfo, bintval, fhdwell;
u_int8_t chan, bchan, fhindex, erp;
int is_new;
@@ -1160,7 +1174,7 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m0,
tstamp = frm; frm += 8;
bintval = LE_READ_2(frm); frm += 2;
capinfo = LE_READ_2(frm); frm += 2;
- ssid = rates = xrates = edca = wmm = rsn = wpa = NULL;
+ ssid = rates = xrates = edcaie = wmmie = rsnie = wpaie = NULL;
bchan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
chan = bchan;
fhdwell = 0;
@@ -1213,10 +1227,10 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m0,
erp = frm[2];
break;
case IEEE80211_ELEMID_RSN:
- rsn = frm;
+ rsnie = frm;
break;
case IEEE80211_ELEMID_EDCAPARMS:
- edca = frm;
+ edcaie = frm;
break;
case IEEE80211_ELEMID_QOS_CAP:
break;
@@ -1227,10 +1241,10 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m0,
}
if (memcmp(frm + 2, MICROSOFT_OUI, 3) == 0) {
if (frm[5] == 1)
- wpa = frm;
+ wpaie = frm;
else if (frm[1] >= 5 &&
frm[5] == 2 && frm[6] == 1)
- wmm = frm;
+ wmmie = frm;
}
break;
default:
@@ -1250,7 +1264,7 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m0,
#endif
isclr(ic->ic_chan_active, chan)) {
IEEE80211_DPRINTF(("%s: ignore %s with invalid channel "
- "%u\n", __func__, ISPROBE(wh) ?
+ "%u\n", __func__, isprobe ?
"probe response" : "beacon", chan));
ic->ic_stats.is_rx_badchan++;
return;
@@ -1268,7 +1282,7 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m0,
* different hop pattern in FH.
*/
IEEE80211_DPRINTF(("%s: ignore %s on channel %u marked "
- "for channel %u\n", __func__, ISPROBE(wh) ?
+ "for channel %u\n", __func__, isprobe ?
"probe response" : "beacon", bchan, chan));
ic->ic_stats.is_rx_chanmismatch++;
return;
@@ -1290,7 +1304,7 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m0,
(ni == NULL || ic->ic_state == IEEE80211_S_SCAN)) {
printf("%s: %s%s on chan %u (bss chan %u) ",
__func__, (ni == NULL ? "new " : ""),
- ISPROBE(wh) ? "probe response" : "beacon",
+ isprobe ? "probe response" : "beacon",
chan, bchan);
ieee80211_print_essid(ssid + 2, ssid[1]);
printf(" from %s\n", ether_sprintf((u_int8_t *)wh->i_addr2));
@@ -1313,11 +1327,10 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m0,
* now.
*/
if (ic->ic_opmode == IEEE80211_M_STA &&
- ic->ic_state == IEEE80211_S_ASSOC &&
+ ic->ic_state == IEEE80211_S_RUN &&
ni->ni_state == IEEE80211_STA_BSS) {
/*
- * Check if protection mode has changed since last
- * beacon.
+ * Check if protection mode has changed since last beacon.
*/
if (ni->ni_erp != erp) {
IEEE80211_DPRINTF((
@@ -1343,12 +1356,50 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m0,
(capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
}
}
+ /*
+ * We do not try to update EDCA parameters if QoS was not negotiated
+ * with the AP at association time.
+ */
if (ni->ni_flags & IEEE80211_NODE_QOS) {
- if (edca != NULL)
- ieee80211_parse_edca_params(ic, edca);
- else if (wmm != NULL)
- ieee80211_parse_wmm_params(ic, edca);
+ /* always prefer EDCA IE over Wi-Fi Alliance WMM IE */
+ if (edcaie != NULL)
+ ieee80211_parse_edca_params(ic, edcaie);
+ else if (wmmie != NULL)
+ ieee80211_parse_wmm_params(ic, wmmie);
}
+
+ if (ic->ic_flags & IEEE80211_F_RSNON) {
+ struct ieee80211_rsnparams rsn;
+ const u_int8_t *saveie = NULL;
+ /*
+ * If the AP advertises both RSN and WPA IEs (WPA1+WPA2),
+ * we only store the parameters of the highest protocol
+ * version we support.
+ */
+ if (rsnie != NULL &&
+ (ic->ic_rsnprotos & IEEE80211_PROTO_RSN)) {
+ if (ieee80211_parse_rsn(ic, rsnie, &rsn) == 0) {
+ ni->ni_rsnprotos = IEEE80211_PROTO_RSN;
+ saveie = rsnie;
+ }
+ } else if (wpaie != NULL &&
+ (ic->ic_rsnprotos & IEEE80211_PROTO_WPA)) {
+ if (ieee80211_parse_wpa(ic, wpaie, &rsn) == 0) {
+ ni->ni_rsnprotos = IEEE80211_PROTO_WPA;
+ saveie = wpaie;
+ }
+ }
+ if (saveie != NULL &&
+ ieee80211_save_ie(saveie, &ni->ni_rsnie) == 0) {
+ ni->ni_rsnakms = rsn.rsn_akms;
+ ni->ni_rsnciphers = rsn.rsn_ciphers;
+ ni->ni_rsngroupcipher = rsn.rsn_groupcipher;
+ ni->ni_rsncaps = rsn.rsn_caps;
+ } else
+ ni->ni_rsnprotos = IEEE80211_PROTO_NONE;
+ } else
+ ni->ni_rsnprotos = IEEE80211_PROTO_NONE;
+
if (ssid[1] != 0 && ni->ni_esslen == 0) {
/*
* Update ESSID at probe response to adopt hidden AP by
@@ -1379,7 +1430,7 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m0,
* Anything else can be discarded (XXX and should be handled
* above so we don't do so much work).
*/
- if (ic->ic_opmode == IEEE80211_M_IBSS || (is_new && ISPROBE(wh))) {
+ if (ic->ic_opmode == IEEE80211_M_IBSS || (is_new && isprobe)) {
/*
* Fake an association so the driver can setup it's
* private state. The rate set has been setup above;
@@ -1388,7 +1439,6 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m0,
if (ic->ic_newassoc)
(*ic->ic_newassoc)(ic, ni, 1);
}
-#undef ISPROBE
}
/*-
@@ -1458,10 +1508,9 @@ ieee80211_recv_probe_req(struct ieee80211com *ic, struct mbuf *m0,
if (rate & IEEE80211_RATE_BASIC) {
IEEE80211_DPRINTF(("%s: rate mismatch for %s\n",
__func__, ether_sprintf((u_int8_t *)wh->i_addr2)));
- } else {
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0);
+ return;
}
+ IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0);
}
/*-
@@ -1518,16 +1567,14 @@ ieee80211_recv_auth(struct ieee80211com *ic, struct mbuf *m0,
*/
void
ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m0,
- struct ieee80211_node *ni, int rssi, u_int32_t rstamp)
+ struct ieee80211_node *ni, int rssi, u_int32_t rstamp, int reassoc)
{
-#define ISREASSOC(_wh) (((_wh)->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == \
- IEEE80211_FC0_SUBTYPE_REASSOC_REQ)
-
const struct ieee80211_frame *wh;
const u_int8_t *frm, *efrm;
- const u_int8_t *ssid, *rates, *xrates, *rsn, *wpa;
+ const u_int8_t *ssid, *rates, *xrates, *rsnie, *wpaie;
u_int16_t capinfo, bintval;
- int reassoc, resp, reason = 0;
+ int resp, status = 0;
+ struct ieee80211_rsnparams rsn;
u_int8_t rate;
if (ic->ic_opmode != IEEE80211_M_HOSTAP ||
@@ -1538,13 +1585,8 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m0,
frm = (const u_int8_t *)&wh[1];
efrm = mtod(m0, u_int8_t *) + m0->m_len;
- if (ISREASSOC(wh)) {
- reassoc = 1;
- resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP;
- } else {
- reassoc = 0;
- resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
- }
+ resp = reassoc ? IEEE80211_FC0_SUBTYPE_REASSOC_RESP :
+ IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4));
if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) {
@@ -1557,7 +1599,7 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m0,
bintval = LE_READ_2(frm); frm += 2;
if (reassoc)
frm += IEEE80211_ADDR_LEN; /* skip current AP address */
- ssid = rates = xrates = rsn = wpa = NULL;
+ ssid = rates = xrates = rsnie = wpaie = NULL;
while (frm + 2 <= efrm) {
if (frm + 2 + frm[1] > efrm) {
ic->ic_stats.is_rx_elem_toosmall++;
@@ -1574,7 +1616,7 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m0,
xrates = frm;
break;
case IEEE80211_ELEMID_RSN:
- rsn = frm;
+ rsnie = frm;
break;
case IEEE80211_ELEMID_QOS_CAP:
break;
@@ -1585,7 +1627,7 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m0,
}
if (memcmp(frm + 2, MICROSOFT_OUI, 3) == 0) {
if (frm[5] == 1)
- wpa = frm;
+ wpaie = frm;
}
break;
}
@@ -1610,38 +1652,102 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m0,
ic->ic_stats.is_rx_assoc_notauth++;
return;
}
- if (rsn != NULL)
- reason = ieee80211_parse_rsn(ic, ni, rsn);
- else if (wpa != NULL)
- reason = ieee80211_parse_wpa1(ic, ni, wpa);
- if (reason != 0) {
- IEEE80211_DPRINTF(("%s: invalid RSN IE for %s\n",
- __func__, ether_sprintf((u_int8_t *)wh->i_addr2)));
- IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
- reason);
- ieee80211_node_leave(ic, ni);
- ic->ic_stats.is_rx_assoc_badrsnie++;
- return;
+ if (reassoc && ni->ni_state != IEEE80211_STA_ASSOC) {
+ IEEE80211_DPRINTF(("%s: deny reassoc from %s, not "
+ "associated\n", __func__,
+ ether_sprintf((u_int8_t *)wh->i_addr2)));
+ status = IEEE80211_STATUS_NOT_ASSOCED;
+ goto end;
}
+
if (!(capinfo & IEEE80211_CAPINFO_ESS)) {
- IEEE80211_DPRINTF(("%s: capinfo mismatch for %s\n",
- __func__, ether_sprintf((u_int8_t *)wh->i_addr2)));
- IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_CAPINFO);
- ieee80211_node_leave(ic, ni);
ic->ic_stats.is_rx_assoc_capmismatch++;
- return;
+ status = IEEE80211_STATUS_CAPINFO;
+ goto end;
}
rate = ieee80211_setup_rates(ic, ni, rates, xrates,
IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO |
IEEE80211_F_DODEL);
if (rate & IEEE80211_RATE_BASIC) {
- IEEE80211_DPRINTF(("%s: rate mismatch for %s\n",
- __func__, ether_sprintf((u_int8_t *)wh->i_addr2)));
- IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_BASIC_RATE);
- ieee80211_node_leave(ic, ni);
ic->ic_stats.is_rx_assoc_norate++;
- return;
+ status = IEEE80211_STATUS_BASIC_RATE;
+ goto end;
}
+
+ if (ic->ic_flags & IEEE80211_F_RSNON) {
+ const u_int8_t *saveie;
+ /*
+ * A station should never include both a WPA and an RSN IE
+ * in its (Re)Association Requests, but if it does, we only
+ * consider the IE of the highest version of the protocol
+ * that is allowed (ie RSN over WPA).
+ */
+ if (rsnie != NULL &&
+ (ic->ic_rsnprotos & IEEE80211_PROTO_RSN)) {
+ status = ieee80211_parse_rsn(ic, rsnie, &rsn);
+ if (status != 0)
+ goto end;
+ ni->ni_rsnprotos = IEEE80211_PROTO_RSN;
+ saveie = rsnie;
+ } else if (wpaie != NULL &&
+ (ic->ic_rsnprotos & IEEE80211_PROTO_WPA)) {
+ status = ieee80211_parse_wpa(ic, wpaie, &rsn);
+ if (status != 0)
+ goto end;
+ ni->ni_rsnprotos = IEEE80211_PROTO_WPA;
+ saveie = wpaie;
+ } else {
+ /*
+ * In an RSN, an AP shall not associate with STAs
+ * that fail to include the RSN IE in the
+ * (Re)Association Request.
+ */
+ status = IEEE80211_STATUS_IE_INVALID;
+ goto end;
+ }
+ /*
+ * The initiating STA's RSN IE shall include one authentication
+ * and pairwise cipher suite among those advertised by the
+ * targeted AP. It shall also specify the group cipher suite
+ * specified by the targeted AP.
+ */
+ if (rsn.rsn_nakms != 1 ||
+ !(rsn.rsn_akms & ic->ic_bss->ni_rsnakms)) {
+ status = IEEE80211_STATUS_BAD_AKMP;
+ goto end;
+ }
+ if (rsn.rsn_nciphers != 1 ||
+ !(rsn.rsn_ciphers & ic->ic_bss->ni_rsnciphers)) {
+ status = IEEE80211_STATUS_BAD_PAIRWISE_CIPHER;
+ goto end;
+ }
+ if (rsn.rsn_groupcipher != ic->ic_bss->ni_rsngroupcipher) {
+ status = IEEE80211_STATUS_BAD_GROUP_CIPHER;
+ goto end;
+ }
+ /*
+ * Disallow new associations using TKIP if countermeasures
+ * are active.
+ */
+ if ((ic->ic_flags & IEEE80211_F_COUNTERM) &&
+ (rsn.rsn_ciphers == IEEE80211_CIPHER_TKIP ||
+ rsn.rsn_groupcipher == IEEE80211_CIPHER_TKIP)) {
+ status = IEEE80211_STATUS_CIPHER_REJ_POLICY;
+ goto end;
+ }
+
+ /* everything looks fine, save IE and parameters */
+ if (ieee80211_save_ie(saveie, &ni->ni_rsnie) != 0) {
+ status = IEEE80211_STATUS_TOOMANY;
+ goto end;
+ }
+ ni->ni_rsnakms = rsn.rsn_akms;
+ ni->ni_rsnciphers = rsn.rsn_ciphers;
+ ni->ni_rsngroupcipher = ic->ic_bss->ni_rsngroupcipher;
+ ni->ni_rsncaps = rsn.rsn_caps;
+ } else
+ ni->ni_rsnprotos = IEEE80211_PROTO_NONE;
+
ni->ni_rssi = rssi;
ni->ni_rstamp = rstamp;
ni->ni_intval = bintval;
@@ -1649,9 +1755,12 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m0,
ni->ni_chan = ic->ic_bss->ni_chan;
ni->ni_fhdwell = ic->ic_bss->ni_fhdwell;
ni->ni_fhindex = ic->ic_bss->ni_fhindex;
-
- ieee80211_node_join(ic, ni, resp);
-#undef ISREASSOC
+ end:
+ if (status != 0) {
+ IEEE80211_SEND_MGMT(ic, ni, resp, status);
+ ieee80211_node_leave(ic, ni);
+ } else
+ ieee80211_node_join(ic, ni, resp);
}
/*-
@@ -1665,15 +1774,12 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m0,
*/
void
ieee80211_recv_assoc_resp(struct ieee80211com *ic, struct mbuf *m0,
- struct ieee80211_node *ni, int rssi, u_int32_t rstamp)
+ struct ieee80211_node *ni, int reassoc)
{
-#define ISREASSOC(_wh) (((_wh)->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == \
- IEEE80211_FC0_SUBTYPE_REASSOC_RESP)
-
struct ifnet *ifp = &ic->ic_if;
const struct ieee80211_frame *wh;
const u_int8_t *frm, *efrm;
- const u_int8_t *rates, *xrates, *edca, *wmm;
+ const u_int8_t *rates, *xrates, *edcaie, *wmmie;
u_int16_t capinfo, status, associd;
u_int8_t rate;
@@ -1688,14 +1794,13 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, struct mbuf *m0,
efrm = mtod(m0, u_int8_t *) + m0->m_len;
IEEE80211_VERIFY_LENGTH(efrm - frm, 6);
- ni = ic->ic_bss;
capinfo = LE_READ_2(frm); frm += 2;
status = LE_READ_2(frm); frm += 2;
if (status != 0) {
if (ifp->if_flags & IFF_DEBUG)
printf("%s: %sassociation failed (reason %d)"
" for %s\n", ifp->if_xname,
- ISREASSOC(wh) ? "re" : "",
+ reassoc ? "re" : "",
status, ether_sprintf((u_int8_t *)wh->i_addr3));
if (ni != ic->ic_bss)
ni->ni_fails++;
@@ -1704,7 +1809,7 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, struct mbuf *m0,
}
associd = LE_READ_2(frm); frm += 2;
- rates = xrates = edca = wmm = NULL;
+ rates = xrates = edcaie = wmmie = NULL;
while (frm + 2 <= efrm) {
if (frm + 2 + frm[1] > efrm) {
ic->ic_stats.is_rx_elem_toosmall++;
@@ -1718,7 +1823,7 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, struct mbuf *m0,
xrates = frm;
break;
case IEEE80211_ELEMID_EDCAPARMS:
- edca = frm;
+ edcaie = frm;
break;
case IEEE80211_ELEMID_VENDOR:
if (frm[1] < 4) {
@@ -1727,7 +1832,7 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, struct mbuf *m0,
}
if (memcmp(frm + 2, MICROSOFT_OUI, 3) == 0) {
if (frm[1] >= 5 && frm[5] == 2 && frm[6] == 1)
- wmm = frm;
+ wmmie = frm;
}
break;
}
@@ -1746,14 +1851,14 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, struct mbuf *m0,
}
ni->ni_capinfo = capinfo;
ni->ni_associd = associd;
- if (edca != NULL || wmm != NULL) {
+ if (edcaie != NULL || wmmie != NULL) {
/* force update of EDCA parameters */
ic->ic_edca_updtcount = -1;
- if ((edca != NULL &&
- ieee80211_parse_edca_params(ic, edca) == 0) ||
- (wmm != NULL &&
- ieee80211_parse_wmm_params(ic, wmm) == 0))
+ if ((edcaie != NULL &&
+ ieee80211_parse_edca_params(ic, edcaie) == 0) ||
+ (wmmie != NULL &&
+ ieee80211_parse_wmm_params(ic, wmmie) == 0))
ni->ni_flags |= IEEE80211_NODE_QOS;
else /* for Reassociation */
ni->ni_flags &= ~IEEE80211_NODE_QOS;
@@ -1778,10 +1883,15 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, struct mbuf *m0,
ic->ic_flags |= IEEE80211_F_USEPROT;
else
ic->ic_flags &= ~IEEE80211_F_USEPROT;
-
+ /*
+ * If not an RSNA, mark the port as valid, otherwise wait for
+ * 802.1X authentication and 4-way handshake to complete..
+ */
+ if (ic->ic_flags & IEEE80211_F_RSNON) {
+ /* XXX ic->ic_mgt_timer = 5; */
+ }
ieee80211_new_state(ic, IEEE80211_S_RUN,
- wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
-#undef ISREASSOC
+ IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
}
/*-
@@ -1790,7 +1900,7 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, struct mbuf *m0,
*/
void
ieee80211_recv_deauth(struct ieee80211com *ic, struct mbuf *m0,
- struct ieee80211_node *ni, int rssi, u_int32_t rstamp)
+ struct ieee80211_node *ni)
{
struct ifnet *ifp = &ic->ic_if;
const struct ieee80211_frame *wh;
@@ -1807,7 +1917,7 @@ ieee80211_recv_deauth(struct ieee80211com *ic, struct mbuf *m0,
switch (ic->ic_opmode) {
case IEEE80211_M_STA:
ieee80211_new_state(ic, IEEE80211_S_AUTH,
- wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+ IEEE80211_FC0_SUBTYPE_DEAUTH);
break;
case IEEE80211_M_HOSTAP:
if (ni != ic->ic_bss) {
@@ -1831,7 +1941,7 @@ ieee80211_recv_deauth(struct ieee80211com *ic, struct mbuf *m0,
*/
void
ieee80211_recv_disassoc(struct ieee80211com *ic, struct mbuf *m0,
- struct ieee80211_node *ni, int rssi, u_int32_t rstamp)
+ struct ieee80211_node *ni)
{
struct ifnet *ifp = &ic->ic_if;
const struct ieee80211_frame *wh;
@@ -1848,7 +1958,7 @@ ieee80211_recv_disassoc(struct ieee80211com *ic, struct mbuf *m0,
switch (ic->ic_opmode) {
case IEEE80211_M_STA:
ieee80211_new_state(ic, IEEE80211_S_ASSOC,
- wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+ IEEE80211_FC0_SUBTYPE_DISASSOC);
break;
case IEEE80211_M_HOSTAP:
if (ni != ic->ic_bss) {
@@ -1871,9 +1981,11 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
struct ieee80211_node *ni, int subtype, int rssi, u_int32_t rstamp)
{
switch (subtype) {
- case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
case IEEE80211_FC0_SUBTYPE_BEACON:
- ieee80211_recv_probe_resp(ic, m0, ni, rssi, rstamp);
+ ieee80211_recv_probe_resp(ic, m0, ni, rssi, rstamp, 0);
+ break;
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ ieee80211_recv_probe_resp(ic, m0, ni, rssi, rstamp, 1);
break;
case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
ieee80211_recv_probe_req(ic, m0, ni, rssi, rstamp);
@@ -1882,18 +1994,22 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
ieee80211_recv_auth(ic, m0, ni, rssi, rstamp);
break;
case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
+ ieee80211_recv_assoc_req(ic, m0, ni, rssi, rstamp, 0);
+ break;
case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
- ieee80211_recv_assoc_req(ic, m0, ni, rssi, rstamp);
+ ieee80211_recv_assoc_req(ic, m0, ni, rssi, rstamp, 1);
break;
case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+ ieee80211_recv_assoc_resp(ic, m0, ni, 0);
+ break;
case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
- ieee80211_recv_assoc_resp(ic, m0, ni, rssi, rstamp);
+ ieee80211_recv_assoc_resp(ic, m0, ni, 1);
break;
case IEEE80211_FC0_SUBTYPE_DEAUTH:
- ieee80211_recv_deauth(ic, m0, ni, rssi, rstamp);
+ ieee80211_recv_deauth(ic, m0, ni);
break;
case IEEE80211_FC0_SUBTYPE_DISASSOC:
- ieee80211_recv_disassoc(ic, m0, ni, rssi, rstamp);
+ ieee80211_recv_disassoc(ic, m0, ni);
break;
default:
IEEE80211_DPRINTF(("%s: mgmt frame with subtype 0x%x not "
@@ -1916,15 +2032,9 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
(u_int64_t)(p)[4] << 24 | (u_int64_t)(p)[5] << 16 | \
(u_int64_t)(p)[6] << 8 | (u_int64_t)(p)[7])
-#define BE_WRITE_2(p, v) do { \
- (p)[0] = (v) >> 8; \
- (p)[1] = (v) & 0xff; \
-} while (0)
-
/* unaligned little endian access */
-#define LE_READ_8(p) \
- ((u_int64_t)(p)[7] << 56 | (u_int64_t)(p)[6] << 48 | \
- (u_int64_t)(p)[5] << 40 | (u_int64_t)(p)[4] << 32 | \
+#define LE_READ_6(p) \
+ ((u_int64_t)(p)[5] << 40 | (u_int64_t)(p)[4] << 32 | \
(u_int64_t)(p)[3] << 24 | (u_int64_t)(p)[2] << 16 | \
(u_int64_t)(p)[1] << 8 | (u_int64_t)(p)[0])
@@ -1936,24 +2046,24 @@ void
ieee80211_recv_4way_msg1(struct ieee80211com *ic,
struct ieee80211_eapol_key *key, struct ieee80211_node *ni)
{
- u_int8_t snonce[EAPOL_KEY_NONCE_LEN];
+ struct ieee80211_ptk tptk;
const u_int8_t *frm, *efrm;
const u_int8_t *pmkid;
const u_int8_t *pmk;
- size_t pmk_len;
if (ic->ic_opmode != IEEE80211_M_STA &&
ic->ic_opmode != IEEE80211_M_IBSS)
return;
if (ni->ni_replaycnt_ok &&
- BE_READ_8(key->replaycnt) <= ni->ni_replaycnt)
+ BE_READ_8(key->replaycnt) <= ni->ni_replaycnt) {
+ ic->ic_stats.is_rx_eapol_replay++;
return;
-
+ }
/* save authenticator's nonce (ANonce) */
memcpy(ni->ni_nonce, key->nonce, EAPOL_KEY_NONCE_LEN);
- /* parse key data field (shall contain an encapsulated PMKID) */
+ /* parse key data field (may contain an encapsulated PMKID) */
frm = (const u_int8_t *)&key[1];
efrm = frm + BE_READ_2(key->paylen);
@@ -1976,32 +2086,29 @@ ieee80211_recv_4way_msg1(struct ieee80211com *ic,
}
frm += 2 + frm[1];
}
- /* check that the PMKID KDE is valid */
+ /* check that the PMKID KDE is valid (if present) */
if (pmkid != NULL && pmkid[1] < 4 + 16)
return;
- /* generate a new nonce (SNonce) */
- arc4random_bytes(snonce, EAPOL_KEY_NONCE_LEN);
+ /* generate a new supplicant's nonce (SNonce) */
+ arc4random_bytes(ic->ic_nonce, EAPOL_KEY_NONCE_LEN);
- if (ni->ni_akm == IEEE80211_AKM_IEEE8021X) {
- /* XXX find the PMK in the PMKSA cache using the PMKID */
- } else {
- /* the PMK is the PSK */
- pmk = ic->ic_psk;
- pmk_len = IEEE80211_PMK_LEN;
+ /* retrieve PMK and derive TPTK */
+ if ((pmk = ieee80211_get_pmk(ic, ni, pmkid)) == NULL) {
+ /* no PMK configured for this STA/PMKID */
+ return;
}
-
- /* derive PTK from PMK */
- ieee80211_derive_ptk(pmk, pmk_len, ni->ni_macaddr, ic->ic_myaddr,
- ni->ni_nonce, snonce, (u_int8_t *)&ni->ni_ptk, sizeof(ni->ni_ptk));
+ ieee80211_derive_ptk(pmk, IEEE80211_PMK_LEN, ni->ni_macaddr,
+ ic->ic_myaddr, key->nonce, ic->ic_nonce, (u_int8_t *)&tptk,
+ sizeof(tptk));
if (ic->ic_if.if_flags & IFF_DEBUG)
printf("%s: received msg %d/%d of the %s handshake from %s\n",
ic->ic_if.if_xname, 1, 4, "4-way",
ether_sprintf(ni->ni_macaddr));
- /* send message 2 to authenticator */
- ieee80211_send_4way_msg2(ic, ni, snonce);
+ /* send message 2 to authenticator using TPTK */
+ (void)ieee80211_send_4way_msg2(ic, ni, key->replaycnt, &tptk);
}
/*
@@ -2015,24 +2122,43 @@ ieee80211_recv_4way_msg2(struct ieee80211com *ic,
{
struct ieee80211_ptk tptk;
const u_int8_t *pmk;
- size_t pmk_len;
+
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
+ ic->ic_opmode != IEEE80211_M_IBSS)
+ return;
/* discard if we're not expecting this message */
if (ni->ni_rsn_state != RSNA_PTKSTART &&
- ni->ni_rsn_state != RSNA_PTKCALCNEGOTIATING)
+ ni->ni_rsn_state != RSNA_PTKCALCNEGOTIATING) {
+ IEEE80211_DPRINTF(("%s: unexpected in state: %d\n",
+ __func__, ni->ni_rsn_state));
return;
-
+ }
ni->ni_rsn_state = RSNA_PTKCALCNEGOTIATING;
- /* derive TPTK from PMK */
- ieee80211_derive_ptk(pmk, pmk_len, ic->ic_myaddr, ni->ni_macaddr,
- ni->ni_nonce, key->nonce, (u_int8_t *)&tptk, sizeof(tptk));
+ /* replay counter has already been verified by caller */
+
+ /* retrieve PMK and derive TPTK */
+ if ((pmk = ieee80211_get_pmk(ic, ni, NULL)) == NULL) {
+ /* no PMK configured for this STA */
+ return; /* will timeout.. */
+ }
+ ieee80211_derive_ptk(pmk, IEEE80211_PMK_LEN, ic->ic_myaddr,
+ ni->ni_macaddr, ni->ni_nonce, key->nonce, (u_int8_t *)&tptk,
+ sizeof(tptk));
/* check Key MIC field using KCK */
- if (ieee80211_eapol_key_check_mic(key, tptk.kck) != 0)
- return;
+ if (ieee80211_eapol_key_check_mic(key, tptk.kck) != 0) {
+ IEEE80211_DPRINTF(("%s: key MIC failed\n", __func__));
+ ic->ic_stats.is_rx_eapol_badmic++;
+ return; /* will timeout.. */
+ }
- /* use TPTK as PTK now that MIC is verified */
+ timeout_del(&ni->ni_rsn_timeout);
+ ni->ni_rsn_state = RSNA_PTKCALCNEGOTIATING_2;
+ ni->ni_rsn_retries = 0;
+
+ /* install TPTK as PTK now that MIC is verified */
memcpy(&ni->ni_ptk, &tptk, sizeof(tptk));
/*
@@ -2042,21 +2168,18 @@ ieee80211_recv_4way_msg2(struct ieee80211com *ic,
if (ni->ni_rsnie == NULL || rsn[1] != ni->ni_rsnie[1] ||
memcmp(rsn, ni->ni_rsnie, 2 + rsn[1]) != 0) {
IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_IE_INVALID);
+ IEEE80211_REASON_RSN_DIFFERENT_IE);
ieee80211_node_leave(ic, ni);
return;
}
- ni->ni_rsn_state = RSNA_PTKCALCNEGOTIATING_2;
- ni->ni_rsn_tocnt = 0;
-
if (ic->ic_if.if_flags & IFF_DEBUG)
printf("%s: received msg %d/%d of the %s handshake from %s\n",
ic->ic_if.if_xname, 2, 4, "4-way",
ether_sprintf(ni->ni_macaddr));
/* send message 3 to supplicant */
- ieee80211_send_4way_msg3(ic, ni);
+ (void)ieee80211_send_4way_msg3(ic, ni);
}
/*
@@ -2067,48 +2190,71 @@ void
ieee80211_recv_4way_msg3(struct ieee80211com *ic,
struct ieee80211_eapol_key *key, struct ieee80211_node *ni)
{
+ struct ieee80211_ptk tptk;
struct ieee80211_key *k;
const u_int8_t *frm, *efrm;
- const u_int8_t *rsn1, *rsn2, *gtk;
- u_int16_t info;
+ const u_int8_t *rsnie1, *rsnie2, *gtk;
+ const u_int8_t *pmk;
+ u_int16_t info, reason = 0;
if (ic->ic_opmode != IEEE80211_M_STA &&
ic->ic_opmode != IEEE80211_M_IBSS)
return;
- if (BE_READ_8(key->replaycnt) <= ni->ni_replaycnt)
+ if (ni->ni_replaycnt_ok &&
+ BE_READ_8(key->replaycnt) <= ni->ni_replaycnt) {
+ ic->ic_stats.is_rx_eapol_replay++;
return;
+ }
- /* check that ANonce matches the one received in message 1 */
- if (memcmp(key->nonce, ni->ni_nonce, EAPOL_KEY_NONCE_LEN) != 0)
+ /* check that ANonce matches that of message 1 */
+ if (memcmp(key->nonce, ni->ni_nonce, EAPOL_KEY_NONCE_LEN) != 0) {
+ IEEE80211_DPRINTF(("%s: ANonce does not match msg 1/4\n",
+ __func__));
return;
+ }
+ /* retrieve PMK and derive TPTK */
+ if ((pmk = ieee80211_get_pmk(ic, ni, NULL)) == NULL) {
+ /* no PMK configured for this STA */
+ return;
+ }
+ ieee80211_derive_ptk(pmk, IEEE80211_PMK_LEN, ni->ni_macaddr,
+ ic->ic_myaddr, key->nonce, ic->ic_nonce, (u_int8_t *)&tptk,
+ sizeof(tptk));
info = BE_READ_2(key->info);
/* check Key MIC field using KCK */
- if (ieee80211_eapol_key_check_mic(key, ni->ni_ptk.kck) != 0)
+ if (ieee80211_eapol_key_check_mic(key, tptk.kck) != 0) {
+ IEEE80211_DPRINTF(("%s: key MIC failed\n", __func__));
+ ic->ic_stats.is_rx_eapol_badmic++;
return;
+ }
+ /* install TPTK as PTK now that MIC is verified */
+ memcpy(&ni->ni_ptk, &tptk, sizeof(tptk));
/* if encrypted, decrypt Key Data field using KEK */
if ((info & EAPOL_KEY_ENCRYPTED) &&
- ieee80211_eapol_key_decrypt(key, ni->ni_ptk.kek) != 0)
+ ieee80211_eapol_key_decrypt(key, ni->ni_ptk.kek) != 0) {
+ IEEE80211_DPRINTF(("%s: decryption failed\n", __func__));
return;
+ }
/* parse key data field */
frm = (const u_int8_t *)&key[1];
efrm = frm + BE_READ_2(key->paylen);
- rsn1 = rsn2 = gtk = NULL;
+ rsnie1 = rsnie2 = gtk = NULL;
while (frm + 2 <= efrm) {
if (frm + 2 + frm[1] > efrm)
break;
switch (frm[0]) {
case IEEE80211_ELEMID_RSN:
- if (rsn1 == NULL)
- rsn1 = frm;
- else if (rsn2 == NULL)
- rsn2 = frm;
- /* ignore if more than two RSN IEs */
+ if (rsnie1 == NULL)
+ rsnie1 = frm;
+ else if (rsnie2 == NULL)
+ rsnie2 = frm;
+ /* ignore others if more than two RSN IEs */
break;
case IEEE80211_ELEMID_VENDOR:
if (frm[1] < 4)
@@ -2122,7 +2268,7 @@ ieee80211_recv_4way_msg3(struct ieee80211com *ic,
} else if (memcmp(&frm[2], MICROSOFT_OUI, 3) == 0) {
switch (frm[5]) {
case 1: /* WPA */
- rsn1 = frm;
+ rsnie1 = frm;
break;
}
}
@@ -2131,33 +2277,47 @@ ieee80211_recv_4way_msg3(struct ieee80211com *ic,
frm += 2 + frm[1];
}
/* first WPA/RSN IE is mandatory */
- if (rsn1 == NULL)
+ if (rsnie1 == NULL) {
+ IEEE80211_DPRINTF(("%s: missing RSN IE\n", __func__));
return;
+ }
/* key data must be encrypted if GTK is included */
- if (gtk != NULL && !(info & EAPOL_KEY_ENCRYPTED))
+ if (gtk != NULL && !(info & EAPOL_KEY_ENCRYPTED)) {
+ IEEE80211_DPRINTF(("%s: GTK not encrypted\n", __func__));
return;
-
+ }
/*
* Check that first WPA/RSN IE is identical to the one received in
* the beacon or probe response frame.
*/
- if (ni->ni_rsnie == NULL || rsn1[1] != ni->ni_rsnie[1] ||
- memcmp(rsn1, ni->ni_rsnie, 2 + rsn1[1]) != 0) {
- /*ieee80211_new_state();*/
- return;
+ if (ni->ni_rsnie == NULL || rsnie1[1] != ni->ni_rsnie[1] ||
+ memcmp(rsnie1, ni->ni_rsnie, 2 + rsnie1[1]) != 0) {
+ reason = IEEE80211_REASON_RSN_DIFFERENT_IE;
+ goto deauth;
}
/*
* If a second RSN information element is present, use its pairwise
* cipher suite or deauthenticate.
*/
- if (rsn2 != NULL) {
- /* XXX ieee80211_parse_rsn(rsn2); */
- /*ieee80211_new_state();*/
- return;
+ if (rsnie2 != NULL && ni->ni_rsnprotos == IEEE80211_PROTO_RSN) {
+ struct ieee80211_rsnparams rsn;
+
+ if (ieee80211_parse_rsn(ic, rsnie2, &rsn) == 0) {
+ if (rsn.rsn_akms != ni->ni_rsnakms ||
+ rsn.rsn_groupcipher != ni->ni_rsngroupcipher ||
+ rsn.rsn_nciphers != 1 ||
+ !(rsn.rsn_ciphers & ic->ic_rsnciphers)) {
+ reason = IEEE80211_REASON_BAD_PAIRWISE_CIPHER;
+ goto deauth;
+ }
+ /* use pairwise cipher suite of second RSN IE */
+ ni->ni_rsnciphers = rsn.rsn_ciphers;
+ ni->ni_rsncipher = ni->ni_rsnciphers;
+ }
}
- /* update the last-seen value of the key replay counter field */
+ /* update the last seen value of the key replay counter field */
ni->ni_replaycnt = BE_READ_8(key->replaycnt);
ni->ni_replaycnt_ok = 1;
@@ -2168,20 +2328,24 @@ ieee80211_recv_4way_msg3(struct ieee80211com *ic,
/* send message 4 to authenticator */
if (ieee80211_send_4way_msg4(ic, ni) != 0)
- return; /* ..authenticator will timeout */
+ return; /* ..authenticator will retry */
if (info & EAPOL_KEY_INSTALL) {
+ u_int64_t prsc;
+
/* check that key length matches that of pairwise cipher */
if (BE_READ_2(key->keylen) !=
- ieee80211_cipher_keylen(ni->ni_pairwise_cipher))
- return;
+ ieee80211_cipher_keylen(ni->ni_rsncipher)) {
+ reason = IEEE80211_REASON_AUTH_LEAVE;
+ goto deauth;
+ }
/* install the PTK */
+ prsc = (gtk == NULL) ? LE_READ_6(key->rsc) : 0;
k = &ni->ni_pairwise_key;
- ieee80211_map_ptk(&ni->ni_ptk, ni->ni_pairwise_cipher, k);
- if (ic->ic_set_key != NULL &&
- (*ic->ic_set_key)(ic, ni, k) != 0) {
- /* XXX deauthenticate */
- return;
+ ieee80211_map_ptk(&ni->ni_ptk, ni->ni_rsncipher, prsc, k);
+ if ((*ic->ic_set_key)(ic, ni, k) != 0) {
+ reason = IEEE80211_REASON_AUTH_LEAVE;
+ goto deauth;
}
}
if (gtk != NULL) {
@@ -2189,29 +2353,40 @@ ieee80211_recv_4way_msg3(struct ieee80211com *ic,
u_int8_t kid;
/* check that the GTK KDE is valid */
- if (gtk[1] < 4 + 2)
- return;
+ if (gtk[1] < 4 + 2) {
+ reason = IEEE80211_REASON_AUTH_LEAVE;
+ goto deauth;
+ }
/* check that key length matches that of group cipher */
- if (gtk[1] - 6 != ieee80211_cipher_keylen(ni->ni_group_cipher))
- return; /* XXX PTK already installed! */
+ if (gtk[1] - 6 !=
+ ieee80211_cipher_keylen(ni->ni_rsngroupcipher)) {
+ reason = IEEE80211_REASON_AUTH_LEAVE;
+ goto deauth;
+ }
/* install the GTK */
kid = gtk[6] & 3;
- rsc = LE_READ_8(key->rsc);
+ rsc = LE_READ_6(key->rsc);
k = &ic->ic_nw_keys[kid];
- ieee80211_map_gtk(&gtk[8], ni->ni_group_cipher, kid,
+ ieee80211_map_gtk(&gtk[8], ni->ni_rsngroupcipher, kid,
gtk[6] & (1 << 2), rsc, k);
- if (ic->ic_set_key != NULL &&
- (*ic->ic_set_key)(ic, ni, k) != 0) {
- /* XXX deauthenticate */
- return;
+ if ((*ic->ic_set_key)(ic, ni, k) != 0) {
+ reason = IEEE80211_REASON_AUTH_LEAVE;
+ goto deauth;
}
}
if (info & EAPOL_KEY_SECURE) {
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
- if (++ni->ni_key_count == 2)
- ni->ni_port_valid = 1;
- } else
+ if (ic->ic_opmode != IEEE80211_M_IBSS ||
+ ++ni->ni_key_count == 2) {
+ IEEE80211_DPRINTF(("%s: marking port %s valid\n",
+ __func__, ether_sprintf(ni->ni_macaddr)));
ni->ni_port_valid = 1;
+ }
+ }
+ deauth:
+ if (reason != 0) {
+ IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
+ reason);
+ ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
}
}
@@ -2223,41 +2398,58 @@ void
ieee80211_recv_4way_msg4(struct ieee80211com *ic,
struct ieee80211_eapol_key *key, struct ieee80211_node *ni)
{
- struct ieee80211_key *k;
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
+ ic->ic_opmode != IEEE80211_M_IBSS)
+ return;
/* discard if we're not expecting this message */
- if (ni->ni_rsn_state != RSNA_PTKINITNEGOTIATING)
+ if (ni->ni_rsn_state != RSNA_PTKINITNEGOTIATING) {
+ IEEE80211_DPRINTF(("%s: unexpected in state: %d\n",
+ __func__, ni->ni_rsn_state));
return;
+ }
+
+ /* replay counter has already been verified by caller */
/* check Key MIC field using KCK */
- if (ieee80211_eapol_key_check_mic(key, ni->ni_ptk.kck) != 0)
- return;
+ if (ieee80211_eapol_key_check_mic(key, ni->ni_ptk.kck) != 0) {
+ IEEE80211_DPRINTF(("%s: key MIC failed\n", __func__));
+ ic->ic_stats.is_rx_eapol_badmic++;
+ return; /* will timeout.. */
+ }
+ timeout_del(&ni->ni_rsn_timeout);
ni->ni_rsn_state = RSNA_PTKINITDONE;
+ ni->ni_rsn_retries = 0;
- /* empty key data field */
-
- /* install the PTK */
- k = &ni->ni_pairwise_key;
- ieee80211_map_ptk(&ni->ni_ptk, ni->ni_pairwise_cipher, k);
- if (ic->ic_set_key != NULL && (*ic->ic_set_key)(ic, ni, k) != 0)
- return;
-
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
- if (++ni->ni_key_count == 2)
- ni->ni_port_valid = 1;
- } else
+ if (ni->ni_rsncipher != IEEE80211_CIPHER_USEGROUP) {
+ /* install the PTK */
+ struct ieee80211_key *k = &ni->ni_pairwise_key;
+ ieee80211_map_ptk(&ni->ni_ptk, ni->ni_rsncipher, 0, k);
+ if ((*ic->ic_set_key)(ic, ni, k) != 0) {
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_ASSOC_TOOMANY);
+ ieee80211_node_leave(ic, ni);
+ return;
+ }
+ }
+ if (ic->ic_opmode != IEEE80211_M_IBSS || ++ni->ni_key_count == 2) {
+ IEEE80211_DPRINTF(("%s: marking port %s valid\n", __func__,
+ ether_sprintf(ni->ni_macaddr)));
ni->ni_port_valid = 1;
-
- /* increment the 64-bit Key Replay Counter */
- ni->ni_replaycnt++;
+ }
if (ic->ic_if.if_flags & IFF_DEBUG)
printf("%s: received msg %d/%d of the %s handshake from %s\n",
ic->ic_if.if_xname, 4, 4, "4-way",
ether_sprintf(ni->ni_macaddr));
- /* XXX start a group key handshake w/ WPA1 */
+ /* initiate a group key handshake for WPA */
+ if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA)
+ (void)ieee80211_send_group_msg1(ic, ni);
+ else
+ ni->ni_rsn_gstate = RSNA_IDLE;
}
/*
@@ -2269,22 +2461,24 @@ ieee80211_recv_4way_msg2or4(struct ieee80211com *ic,
struct ieee80211_eapol_key *key, struct ieee80211_node *ni)
{
const u_int8_t *frm, *efrm;
- const u_int8_t *rsn;
+ const u_int8_t *rsnie;
- if (BE_READ_8(key->replaycnt) != ni->ni_replaycnt)
+ if (BE_READ_8(key->replaycnt) != ni->ni_replaycnt) {
+ ic->ic_stats.is_rx_eapol_replay++;
return;
+ }
/* parse key data field (check if an RSN IE is present) */
frm = (const u_int8_t *)&key[1];
efrm = frm + BE_READ_2(key->paylen);
- rsn = NULL;
+ rsnie = NULL;
while (frm + 2 <= efrm) {
if (frm + 2 + frm[1] > efrm)
break;
switch (frm[0]) {
case IEEE80211_ELEMID_RSN:
- rsn = frm;
+ rsnie = frm;
break;
case IEEE80211_ELEMID_VENDOR:
if (frm[1] < 4)
@@ -2292,15 +2486,15 @@ ieee80211_recv_4way_msg2or4(struct ieee80211com *ic,
if (memcmp(&frm[2], MICROSOFT_OUI, 3) == 0) {
switch (frm[5]) {
case 1: /* WPA */
- rsn = frm;
+ rsnie = frm;
break;
}
}
}
frm += 2 + frm[1];
}
- if (rsn != NULL)
- ieee80211_recv_4way_msg2(ic, key, ni, rsn);
+ if (rsnie != NULL)
+ ieee80211_recv_4way_msg2(ic, key, ni, rsnie);
else
ieee80211_recv_4way_msg4(ic, key, ni);
}
@@ -2324,19 +2518,24 @@ ieee80211_recv_rsn_group_msg1(struct ieee80211com *ic,
ic->ic_opmode != IEEE80211_M_IBSS)
return;
- if (BE_READ_8(key->replaycnt) <= ni->ni_replaycnt)
+ if (BE_READ_8(key->replaycnt) <= ni->ni_replaycnt) {
+ ic->ic_stats.is_rx_eapol_replay++;
return;
-
- info = BE_READ_2(key->info);
-
+ }
/* check Key MIC field using KCK */
- if (ieee80211_eapol_key_check_mic(key, ni->ni_ptk.kck) != 0)
+ if (ieee80211_eapol_key_check_mic(key, ni->ni_ptk.kck) != 0) {
+ IEEE80211_DPRINTF(("%s: key MIC failed\n", __func__));
+ ic->ic_stats.is_rx_eapol_badmic++;
return;
+ }
+ info = BE_READ_2(key->info);
/* check that encrypted and decrypt Key Data field using KEK */
if (!(info & EAPOL_KEY_ENCRYPTED) ||
- ieee80211_eapol_key_decrypt(key, ni->ni_ptk.kek) != 0)
+ ieee80211_eapol_key_decrypt(key, ni->ni_ptk.kek) != 0) {
+ IEEE80211_DPRINTF(("%s: decryption failed\n", __func__));
return;
+ }
/* parse key data field (shall contain a GTK KDE) */
frm = (const u_int8_t *)&key[1];
@@ -2361,24 +2560,37 @@ ieee80211_recv_rsn_group_msg1(struct ieee80211com *ic,
}
frm += 2 + frm[1];
}
- if (gtk == NULL)
+ /* check that the GTK KDE is present and valid */
+ if (gtk == NULL || gtk[1] < 4 + 2) {
+ IEEE80211_DPRINTF(("%s: missing or invalid GTK KDE\n",
+ __func__));
return;
+ }
- /* check that the GTK KDE is valid */
- if (gtk[1] < 4 + 2)
- return;
/* check that key length matches that of group cipher */
- if (gtk[1] - 6 != ieee80211_cipher_keylen(ni->ni_group_cipher))
+ if (gtk[1] - 6 != ieee80211_cipher_keylen(ni->ni_rsngroupcipher))
return;
+
/* install the GTK */
kid = gtk[6] & 3;
- rsc = LE_READ_8(key->rsc);
+ rsc = LE_READ_6(key->rsc);
k = &ic->ic_nw_keys[kid];
- ieee80211_map_gtk(&gtk[8], ni->ni_group_cipher, kid,
+ ieee80211_map_gtk(&gtk[8], ni->ni_rsngroupcipher, kid,
gtk[6] & (1 << 2), rsc, k);
- if (ic->ic_set_key != NULL && (*ic->ic_set_key)(ic, ni, k) != 0)
+ if ((*ic->ic_set_key)(ic, ni, k) != 0) {
+ IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_AUTH_LEAVE);
+ ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
return;
-
+ }
+ if (info & EAPOL_KEY_SECURE) {
+ if (ic->ic_opmode != IEEE80211_M_IBSS ||
+ ++ni->ni_key_count == 2) {
+ IEEE80211_DPRINTF(("%s: marking port %s valid\n",
+ __func__, ether_sprintf(ni->ni_macaddr)));
+ ni->ni_port_valid = 1;
+ }
+ }
/* update the last seen value of the key replay counter field */
ni->ni_replaycnt = BE_READ_8(key->replaycnt);
@@ -2388,7 +2600,7 @@ ieee80211_recv_rsn_group_msg1(struct ieee80211com *ic,
ether_sprintf(ni->ni_macaddr));
/* send message 2 to authenticator */
- ieee80211_send_group_msg2(ic, ni, k);
+ (void)ieee80211_send_group_msg2(ic, ni, k);
}
void
@@ -2396,43 +2608,68 @@ ieee80211_recv_wpa_group_msg1(struct ieee80211com *ic,
struct ieee80211_eapol_key *key, struct ieee80211_node *ni)
{
struct ieee80211_key *k;
+ const u_int8_t *frm;
u_int64_t rsc;
u_int16_t info;
u_int8_t kid;
+ int keylen;
if (ic->ic_opmode != IEEE80211_M_STA &&
ic->ic_opmode != IEEE80211_M_IBSS)
return;
- if (BE_READ_8(key->replaycnt) <= ni->ni_replaycnt)
+ if (BE_READ_8(key->replaycnt) <= ni->ni_replaycnt) {
+ ic->ic_stats.is_rx_eapol_replay++;
return;
-
+ }
/* check Key MIC field using KCK */
- if (ieee80211_eapol_key_check_mic(key, ni->ni_ptk.kck) != 0)
+ if (ieee80211_eapol_key_check_mic(key, ni->ni_ptk.kck) != 0) {
+ IEEE80211_DPRINTF(("%s: key MIC failed\n", __func__));
+ ic->ic_stats.is_rx_eapol_badmic++;
return;
-
+ }
/*
- * EAPOL-Key data field is encrypted even though WPA1 doesn't set
+ * EAPOL-Key data field is encrypted even though WPA doesn't set
* the ENCRYPTED bit in the info field.
*/
- if (ieee80211_eapol_key_decrypt(key, ni->ni_ptk.kek) != 0)
+ if (ieee80211_eapol_key_decrypt(key, ni->ni_ptk.kek) != 0) {
+ IEEE80211_DPRINTF(("%s: decryption failed\n", __func__));
return;
-
+ }
info = BE_READ_2(key->info);
+ keylen = ieee80211_cipher_keylen(ni->ni_rsngroupcipher);
/* check that key length matches that of group cipher */
- if (BE_READ_2(key->keylen) !=
- ieee80211_cipher_keylen(ni->ni_group_cipher))
+ if (BE_READ_2(key->keylen) != keylen)
+ return;
+
+ /* check that the data length is large enough to hold the key */
+ if (BE_READ_2(key->paylen) < keylen)
return;
+
+ /* key data field contains the GTK */
+ frm = (const u_int8_t *)&key[1];
+
/* install the GTK */
kid = (info >> EAPOL_KEY_WPA_KID_SHIFT) & 3;
- rsc = LE_READ_8(key->rsc);
+ rsc = LE_READ_6(key->rsc);
k = &ic->ic_nw_keys[kid];
- ieee80211_map_gtk((u_int8_t *)&key[1], ni->ni_group_cipher, kid,
+ ieee80211_map_gtk(frm, ni->ni_rsngroupcipher, kid,
info & EAPOL_KEY_WPA_TX, rsc, k);
- if (ic->ic_set_key != NULL && (*ic->ic_set_key)(ic, ni, k) != 0)
+ if ((*ic->ic_set_key)(ic, ni, k) != 0) {
+ IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_AUTH_LEAVE);
+ ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
return;
-
+ }
+ if (info & EAPOL_KEY_SECURE) {
+ if (ic->ic_opmode != IEEE80211_M_IBSS ||
+ ++ni->ni_key_count == 2) {
+ IEEE80211_DPRINTF(("%s: marking port %s valid\n",
+ __func__, ether_sprintf(ni->ni_macaddr)));
+ ni->ni_port_valid = 1;
+ }
+ }
/* update the last seen value of the key replay counter field */
ni->ni_replaycnt = BE_READ_8(key->replaycnt);
@@ -2442,7 +2679,7 @@ ieee80211_recv_wpa_group_msg1(struct ieee80211com *ic,
ether_sprintf(ni->ni_macaddr));
/* send message 2 to authenticator */
- ieee80211_send_group_msg2(ic, ni, k);
+ (void)ieee80211_send_group_msg2(ic, ni, k);
}
/*
@@ -2457,20 +2694,34 @@ ieee80211_recv_group_msg2(struct ieee80211com *ic,
ic->ic_opmode != IEEE80211_M_IBSS)
return;
- if (BE_READ_8(key->replaycnt) != ni->ni_replaycnt)
+ /* discard if we're not expecting this message */
+ if (ni->ni_rsn_gstate != RSNA_REKEYNEGOTIATING) {
+ IEEE80211_DPRINTF(("%s: unexpected in state: %d\n",
+ __func__, ni->ni_rsn_state));
return;
-
+ }
+ if (BE_READ_8(key->replaycnt) != ni->ni_replaycnt) {
+ ic->ic_stats.is_rx_eapol_replay++;
+ return;
+ }
/* check Key MIC field using KCK */
- if (ieee80211_eapol_key_check_mic(key, ni->ni_ptk.kck) != 0)
+ if (ieee80211_eapol_key_check_mic(key, ni->ni_ptk.kck) != 0) {
+ IEEE80211_DPRINTF(("%s: key MIC failed\n", __func__));
+ ic->ic_stats.is_rx_eapol_badmic++;
return;
+ }
- /* empty key data field */
+ timeout_del(&ni->ni_rsn_timeout);
+ ni->ni_rsn_gstate = RSNA_REKEYESTABLISHED;
+
+ if ((ni->ni_flags & IEEE80211_NODE_REKEY) &&
+ --ic->ic_rsn_keydonesta == 0)
+ ieee80211_setkeysdone(ic);
+ ni->ni_flags &= ~IEEE80211_NODE_REKEY;
+
+ ni->ni_rsn_gstate = RSNA_IDLE;
+ ni->ni_rsn_retries = 0;
-#ifdef notyet
- if (--ic->ic_keydone_sta == 0) {
- /* install GTK */
- }
-#endif
if (ic->ic_if.if_flags & IFF_DEBUG)
printf("%s: received msg %d/%d of the %s handshake from %s\n",
ic->ic_if.if_xname, 2, 2, "group key",
@@ -2479,8 +2730,8 @@ ieee80211_recv_group_msg2(struct ieee80211com *ic,
/*
* EAPOL-Key Request frames are sent by the supplicant to request that the
- * authenticator initiate either a 4-Way Handshake or Group Key Handshake
- * and to report a MIC failure in a TKIP MSDU.
+ * authenticator initiates either a 4-Way Handshake or Group Key Handshake,
+ * or to report a MIC failure in a TKIP MSDU.
*/
void
ieee80211_recv_eapol_key_req(struct ieee80211com *ic,
@@ -2494,11 +2745,36 @@ ieee80211_recv_eapol_key_req(struct ieee80211com *ic,
info = BE_READ_2(key->info);
- if (info & EAPOL_KEY_ERROR) {
- /* TKIP MIC failure */
+ /* enforce monotonicity of key request replay counter */
+ if (ni->ni_reqreplaycnt_ok &&
+ BE_READ_8(key->replaycnt) <= ni->ni_reqreplaycnt) {
+ ic->ic_stats.is_rx_eapol_replay++;
+ return;
+ }
+ if (!(info & EAPOL_KEY_KEYMIC) ||
+ ieee80211_eapol_key_check_mic(key, ni->ni_ptk.kck) != 0) {
+ IEEE80211_DPRINTF(("%s: key MIC failed\n", __func__));
+ ic->ic_stats.is_rx_eapol_badmic++;
+ return;
+ }
+ /* update key request replay counter now that MIC is verified */
+ ni->ni_reqreplaycnt = BE_READ_8(key->replaycnt);
+ ni->ni_reqreplaycnt_ok = 1;
+
+ if (info & EAPOL_KEY_ERROR) { /* TKIP MIC failure */
+ /* ignore reports from STAs not using TKIP */
+ if (ic->ic_bss->ni_rsngroupcipher != IEEE80211_CIPHER_TKIP &&
+ ni->ni_rsncipher != IEEE80211_CIPHER_TKIP) {
+ IEEE80211_DPRINTF(("%s: MIC failure report from "
+ "STA not using TKIP: %s\n", __func__,
+ ether_sprintf(ni->ni_macaddr)));
+ return;
+ }
+ ic->ic_stats.is_rx_remmicfail++;
+ ieee80211_michael_mic_failure(ic, LE_READ_6(key->rsc));
} else if (info & EAPOL_KEY_PAIRWISE) {
- /* initiate the 4-Way Handshake */
+ /* initiate a 4-Way Handshake */
} else {
/*
@@ -2508,89 +2784,64 @@ ieee80211_recv_eapol_key_req(struct ieee80211com *ic,
}
}
-#ifdef IEEE80211_DEBUG
-static void
-ieee80211_print_eapol_key(struct ieee80211com *ic,
- const struct ieee80211_eapol_key *key, const struct ieee80211_node *ni)
-{
- int i;
- printf("%s: received EAPOL-Key frame from %s\n",
- ic->ic_if.if_xname, ether_sprintf((u_int8_t *)ni->ni_macaddr));
- printf("version=0x%02x type=0x%02x desc=0x%02x body length=%d "
- "data length=%d\n", key->version, key->type, key->desc,
- BE_READ_2(key->len), BE_READ_2(key->paylen));
- printf("info=%b\n", BE_READ_2(key->info),
- "\20\x03PAIRWISE\x06INSTALL\x07KEYACK\x08KEYMIC\x09SECURE"
- "\x0aERROR\x0bREQUEST\x0cENCRYPTED\x0dSMK");
- printf("Key Replay Counter=0x");
- for (i = 0; i < 8; i++)
- printf("%02x", key->replaycnt[i]);
- printf("\n");
- printf("Key Nonce=0x");
- for (i = 0; i < EAPOL_KEY_NONCE_LEN; i++)
- printf("%02x", key->nonce[i]);
- printf("\n");
- printf("Key IV=0x");
- for (i = 0; i < EAPOL_KEY_IV_LEN; i++)
- printf("%02x", key->iv[i]);
- printf("\n");
- printf("Key RSC=0x");
- for (i = 0; i < 8; i++)
- printf("%02x", key->rsc[i]);
- printf("\n");
- printf("Key MIC=0x");
- for (i = 0; i < EAPOL_KEY_MIC_LEN; i++)
- printf("%02x", key->mic[i]);
- printf("\n");
-}
-#endif
-
/*
* Process an incoming EAPOL frame. Notice that we are only interested in
- * EAPOL-Key frames with an IEEE 802.11 or WPA1 descriptor type.
+ * EAPOL-Key frames with an IEEE 802.11 or WPA descriptor type.
*/
void
ieee80211_recv_eapol(struct ieee80211com *ic, struct mbuf *m0,
struct ieee80211_node *ni)
{
+ struct ifnet *ifp = &ic->ic_if;
+ struct ether_header *eh;
struct ieee80211_eapol_key *key;
u_int16_t info, desc;
- if (m0->m_len < sizeof(struct ether_header) + sizeof(*key))
- goto out;
+ ifp->if_ibytes += m0->m_pkthdr.len;
- m_adj(m0, sizeof(struct ether_header));
+ if (m0->m_len < sizeof(*eh) + sizeof(*key))
+ return;
+ eh = mtod(m0, struct ether_header *);
+ if (IEEE80211_IS_MULTICAST(eh->ether_dhost)) {
+ ifp->if_imcasts++;
+ return;
+ }
+ m_adj(m0, sizeof(*eh));
key = mtod(m0, struct ieee80211_eapol_key *);
- if (key->type != EAPOL_KEY || key->desc != ni->ni_eapol_desc)
- goto out;
+ if (key->type != EAPOL_KEY)
+ return;
+ ic->ic_stats.is_rx_eapol_key++;
+
+ if ((ni->ni_rsnprotos == IEEE80211_PROTO_RSN &&
+ key->desc != EAPOL_KEY_DESC_IEEE80211) ||
+ (ni->ni_rsnprotos == IEEE80211_PROTO_WPA &&
+ key->desc != EAPOL_KEY_DESC_WPA))
+ return;
/* check packet body length */
if (m0->m_len < 4 + BE_READ_2(key->len))
- goto out;
+ return;
/* check key data length */
if (m0->m_len < sizeof(*key) + BE_READ_2(key->paylen))
- goto out;
+ return;
-#ifdef IEEE80211_DEBUG
- if (ieee80211_debug > 0)
- ieee80211_print_eapol_key(ic, key, ni);
-#endif
info = BE_READ_2(key->info);
/* discard EAPOL-Key frames with an unknown descriptor version */
desc = info & EAPOL_KEY_VERSION_MASK;
if (desc != EAPOL_KEY_DESC_V1 && desc != EAPOL_KEY_DESC_V2)
- goto out;
+ return;
- if (ni->ni_pairwise_cipher == IEEE80211_CIPHER_CCMP &&
+ if ((ni->ni_rsncipher == IEEE80211_CIPHER_CCMP ||
+ ni->ni_rsngroupcipher == IEEE80211_CIPHER_CCMP) &&
desc != EAPOL_KEY_DESC_V2)
- goto out;
+ return;
/* determine message type (see 8.5.3.7) */
if (info & EAPOL_KEY_REQUEST) {
- /* EAPOL-Key Request */
+ /* EAPOL-Key Request frame */
ieee80211_recv_eapol_key_req(ic, key, ni);
} else if (info & EAPOL_KEY_PAIRWISE) {
@@ -2600,22 +2851,20 @@ ieee80211_recv_eapol(struct ieee80211com *ic, struct mbuf *m0,
ieee80211_recv_4way_msg3(ic, key, ni);
else
ieee80211_recv_4way_msg2or4(ic, key, ni);
- } else
+ } else if (info & EAPOL_KEY_KEYACK)
ieee80211_recv_4way_msg1(ic, key, ni);
} else {
/* Group Key Handshake */
if (!(info & EAPOL_KEY_KEYMIC))
- goto out;
+ return;
if (info & EAPOL_KEY_KEYACK) {
- if (key->desc == EAPOL_KEY_DESC_WPA1)
+ if (key->desc == EAPOL_KEY_DESC_WPA)
ieee80211_recv_wpa_group_msg1(ic, key, ni);
else
ieee80211_recv_rsn_group_msg1(ic, key, ni);
} else
ieee80211_recv_group_msg2(ic, key, ni);
}
- out:
- m_freem(m0);
}
void
@@ -2628,7 +2877,7 @@ ieee80211_recv_pspoll(struct ieee80211com *ic, struct mbuf *m0, int rssi,
struct mbuf *m;
u_int16_t aid;
- if (ic->ic_set_tim == NULL) /* no powersaving functionality */
+ if (ic->ic_set_tim == NULL) /* no powersaving functionality */
return;
wh = mtod(m0, struct ieee80211_frame *);