diff options
author | Damien Bergamini <damien@cvs.openbsd.org> | 2008-04-16 18:32:16 +0000 |
---|---|---|
committer | Damien Bergamini <damien@cvs.openbsd.org> | 2008-04-16 18:32:16 +0000 |
commit | 792a7af405f1bcd074df2d192736f0ff71180ce6 (patch) | |
tree | d1c8127338d31609e63b5a8c220604e11d416fa8 | |
parent | c257d1252aa088da82103c7952e3c42933d743da (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@
37 files changed, 3799 insertions, 1450 deletions
diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8 index 334500ad5ec..c3bfa40284b 100644 --- a/sbin/ifconfig/ifconfig.8 +++ b/sbin/ifconfig/ifconfig.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ifconfig.8,v 1.154 2008/03/19 08:44:54 jmc Exp $ +.\" $OpenBSD: ifconfig.8,v 1.155 2008/04/16 18:32:15 damien Exp $ .\" $NetBSD: ifconfig.8,v 1.11 1996/01/04 21:27:29 pk Exp $ .\" $FreeBSD: ifconfig.8,v 1.16 1998/02/01 07:03:29 steve Exp $ .\" @@ -31,7 +31,7 @@ .\" .\" @(#)ifconfig.8 8.4 (Berkeley) 6/1/94 .\" -.Dd $Mdocdate: March 19 2008 $ +.Dd $Mdocdate: April 16 2008 $ .Dt IFCONFIG 8 .Os .Sh NAME @@ -547,6 +547,14 @@ will begin advertising as master. .Op Oo Fl Oc Cm powersave .Op Cm powersavesleep Ar duration .Op Oo Fl Oc Cm txpower Ar dBm +.Op Oo Fl Oc Cm wmm +.Op Oo Fl Oc Cm wpa +.Op Cm wpaakms Ar akm,akm,... +.Op Cm wpaciphers Ar cipher,cipher,... +.Op Cm wpagroupcipher Ar cipher +.Op Cm wpaprotos Ar proto,proto,... +.Op Oo Fl Oc Cm wpapsk Ar psk +.Op Cm wparekey Ar duration .Ek .Pp The options are as follows: @@ -635,10 +643,8 @@ If a set of keys is specified, a comma within the key must be escaped with a backslash. Note that if multiple keys are used, their order must be the same within the network. -For IEEE 802.11 wireless networks, the length of each key is restricted to -40 bits, i.e. a 5-character string or 10 hexadecimal digits. -WaveLAN/IEEE Gold and newer Prism cards will also accept a 104-bit -(13-character) key. +The length of each key must be either 40 bits, i.e. a 5-character string or +10 hexadecimal digits or 104 bits (13-character). .It Fl nwkey Disable WEP encryption for IEEE 802.11-based wireless network interfaces. .It Cm nwkey Cm persist @@ -664,6 +670,131 @@ mode. .It Fl txpower Disable manual transmit power mode and enable any auto level and transmit power controls. +.It Cm wmm +Enable Wi-Fi Multimedia (WMM). +WMM is a Wi-Fi Alliance protocol based on the IEEE 802.11e standard and +provides basic Quality of Service (QoS) features to Wi-Fi networks. +For a station, this option enables the use of QoS if the access point +supports it but it does not prevent the association to an access point +not supporting QoS. +In Host AP mode, this option allows QoS stations to negotiate QoS during +association. +Notice that not all drivers support QoS. +Check the driver's manual page to know if this option is supported. +QoS priorities can be defined using VLANs (see the +.Cm vlanprio +option). +Otherwise, the IEEE 802.11 layer will automatically affect priorities to +frames based on the Differentiated Services Codepoint field of IP packets. +.It Fl wmm +Disable Wi-Fi Multimedia. +.It Cm wpa +Enable Wi-Fi Protected Access. +WPA is a Wi-Fi Alliance protocol based on the IEEE 802.11i standard. +It was designed to enhance the security of wireless networks. +Notice that not all drivers support WPA. +Check the driver's manual page to know if this option is supported. +.It Fl wpa +Disable Wi-Fi Protected Access. +.It Cm wpaakms Ar akm,akm,... +Set the comma-separated list of allowed authentication and key management +protocols. +.Pp +The supported values are +.Dq psk +and +.Dq 802.1x . +.Ar psk +authentication (also known as personal mode) uses a 256-bit pre-shared key. +.Ar 802.1x +authentication (also known as enterprise mode) is meant to be used with +an external IEEE 802.1X authentication server. +The default value is +.Dq psk,802.1x . +.Dq psk +can only be used if a pre-shared key is configured using the +.Cm wpapsk +option. +.It Cm wpaciphers Ar cipher,cipher,... +Set the comma-separated list of allowed pairwise ciphers. +.Pp +The supported values are +.Dq tkip , +.Dq ccmp , +and +.Dq usegroup . +.Ar usegroup +specifies that no pairwise ciphers are supported and that only group keys +should be used. +The default value is +.Dq tkip,ccmp . +If multiple pairwise ciphers are specified, the pairwise cipher will +be negotiated between the station and the access point at association +time. +A station will always try to use +.Ar ccmp +over +.Ar tkip +if both ciphers are allowed and supported by the access point. +If the selected cipher is not supported by the hardware, software +encryption will be used. +Check the driver's manual page to know which ciphers are supported in +hardware. +.It Cm wpagroupcipher Ar cipher +Set the group cipher to be used to encrypt broadcast and multicast traffic. +.Pp +The supported values are +.Dq wep40 , +.Dq wep104 , +.Dq tkip , +and +.Dq ccmp . +The default value is +.Dq tkip . +The use of +.Ar wep40 +or +.Ar wep104 +as the group cipher is discouraged due to weaknesses in WEP. +The +.Cm wpagroupcipher +option is available in Host AP mode only. +A station will always use the group cipher of the BSS. +.It Cm wpaprotos Ar proto,proto,... +Set the comma-separated list of allowed WPA protocol versions. +.Pp +The supported values are +.Dq wpa1 +and +.Dq wpa2 . +.Ar wpa1 +is based on draft 3 of the IEEE 802.11i standard whereas +.Ar wpa2 +is based on the ratified standard. +The default value is +.Dq wpa1,wpa2 . +If +.Dq wpa1,wpa2 +is specified, a station will always use the +.Ar wpa2 +protocol when supported by the access point. +.It Cm wpapsk Ar psk +Set the 256-bit pre-shared key. +The pre-shared key must be specified using a series of 64 hexadecimal digits +(preceded by +.So 0x Sc ) . +The pre-shared key will be used only if +.Dq psk +authentication is allowed using the +.Cm wpaakms +option. +Pre-shared keys can be generated from pass-phrases using +.Xr wpa-psk 1 . +.It Fl wpapsk +Delete the pre-shared key. +This will prevent +.Dq psk +authentication. .El .\" INET6 .Sh INET6 diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c index c4d80c3c16f..24eeeb031ae 100644 --- a/sbin/ifconfig/ifconfig.c +++ b/sbin/ifconfig/ifconfig.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ifconfig.c,v 1.194 2008/03/29 18:56:43 damien Exp $ */ +/* $OpenBSD: ifconfig.c,v 1.195 2008/04/16 18:32:15 damien Exp $ */ /* $NetBSD: ifconfig.c,v 1.40 1997/10/01 02:19:43 enami Exp $ */ /* @@ -153,6 +153,13 @@ void setifmtu(const char *, int); void setifnwid(const char *, int); void setifbssid(const char *, int); void setifnwkey(const char *, int); +void setifwmm(const char *, int); +void setifwpa(const char *, int); +void setifwpaprotos(const char *, int); +void setifwpaakms(const char *, int); +void setifwpaciphers(const char *, int); +void setifwpagroupcipher(const char *, int); +void setifwpapsk(const char *, int); void setifchan(const char *, int); void setiftxpower(const char *, int); void setifpowersave(const char *, int); @@ -283,6 +290,16 @@ const struct cmd { { "-bssid", -1, 0, setifbssid }, { "nwkey", NEXTARG, 0, setifnwkey }, { "-nwkey", -1, 0, setifnwkey }, + { "wmm", 1, 0, setifwmm }, + { "-wmm", 0, 0, setifwmm }, + { "wpa", 1, 0, setifwpa }, + { "-wpa", 0, 0, setifwpa }, + { "wpaakms", NEXTARG, 0, setifwpaakms }, + { "wpaciphers", NEXTARG, 0, setifwpaciphers }, + { "wpagroupcipher", NEXTARG, 0, setifwpagroupcipher }, + { "wpaprotos", NEXTARG, 0, setifwpaprotos }, + { "wpapsk", NEXTARG, 0, setifwpapsk }, + { "-wpapsk", -1, 0, setifwpapsk }, { "chan", NEXTARG, 0, setifchan }, { "-chan", -1, 0, setifchan }, { "powersave", 1, 0, setifpowersave }, @@ -1292,7 +1309,6 @@ print_string(const u_int8_t *buf, int len) } } -/* ARGSUSED */ void setifnwid(const char *val, int d) { @@ -1399,6 +1415,184 @@ setifnwkey(const char *val, int d) warn("SIOCS80211NWKEY"); } +/* ARGSUSED */ +void +setifwmm(const char *val, int d) +{ + struct ieee80211_wmmparams wmm; + + (void)strlcpy(wmm.i_name, name, sizeof(wmm.i_name)); + wmm.i_enabled = d; + if (ioctl(s, SIOCS80211WMMPARMS, (caddr_t)&wmm) < 0) + err(1, "SIOCS80211WMMPARMS"); +} + +/* ARGSUSED */ +void +setifwpa(const char *val, int d) +{ + struct ieee80211_wpaparams wpa; + + (void)strlcpy(wpa.i_name, name, sizeof(wpa.i_name)); + if (ioctl(s, SIOCG80211WPAPARMS, (caddr_t)&wpa) < 0) + err(1, "SIOCG80211WPAPARMS"); + wpa.i_enabled = d; + if (ioctl(s, SIOCS80211WPAPARMS, (caddr_t)&wpa) < 0) + err(1, "SIOCS80211WPAPARMS"); +} + +/* ARGSUSED */ +void +setifwpaprotos(const char *val, int d) +{ + struct ieee80211_wpaparams wpa; + char *optlist, *str; + u_int rval = 0; + + if ((optlist = strdup(val)) == NULL) + err(1, "strdup"); + str = strtok(optlist, ","); + while (str != NULL) { + if (strcasecmp(str, "wpa1") == 0) + rval |= IEEE80211_WPA_PROTO_WPA1; + else if (strcasecmp(str, "wpa2") == 0) + rval |= IEEE80211_WPA_PROTO_WPA2; + else + errx(1, "wpaprotos: unknown protocol: %s", str); + str = strtok(NULL, ","); + } + free(optlist); + + (void)strlcpy(wpa.i_name, name, sizeof(wpa.i_name)); + if (ioctl(s, SIOCG80211WPAPARMS, (caddr_t)&wpa) < 0) + err(1, "SIOCG80211WPAPARMS"); + wpa.i_protos = rval; + if (ioctl(s, SIOCS80211WPAPARMS, (caddr_t)&wpa) < 0) + err(1, "SIOCS80211WPAPARMS"); +} + +/* ARGSUSED */ +void +setifwpaakms(const char *val, int d) +{ + struct ieee80211_wpaparams wpa; + char *optlist, *str; + u_int rval = 0; + + if ((optlist = strdup(val)) == NULL) + err(1, "strdup"); + str = strtok(optlist, ","); + while (str != NULL) { + if (strcasecmp(str, "psk") == 0) + rval |= IEEE80211_WPA_AKM_PSK; + else if (strcasecmp(str, "802.1x") == 0) + rval |= IEEE80211_WPA_AKM_IEEE8021X; + else + errx(1, "wpaakms: unknown akm: %s", str); + str = strtok(NULL, ","); + } + free(optlist); + + (void)strlcpy(wpa.i_name, name, sizeof(wpa.i_name)); + if (ioctl(s, SIOCG80211WPAPARMS, (caddr_t)&wpa) < 0) + err(1, "SIOCG80211WPAPARMS"); + wpa.i_akms = rval; + if (ioctl(s, SIOCS80211WPAPARMS, (caddr_t)&wpa) < 0) + err(1, "SIOCS80211WPAPARMS"); +} + +static const struct { + const char *name; + u_int cipher; +} ciphers[] = { + { "usegroup", IEEE80211_WPA_CIPHER_USEGROUP }, + { "wep40", IEEE80211_WPA_CIPHER_WEP40 }, + { "tkip", IEEE80211_WPA_CIPHER_TKIP }, + { "ccmp", IEEE80211_WPA_CIPHER_CCMP }, + { "wep104", IEEE80211_WPA_CIPHER_WEP104 } +}; + +u_int +getwpacipher(const char *name) +{ + int i; + + for (i = 0; i < sizeof(ciphers) / sizeof(ciphers[0]); i++) + if (strcasecmp(name, ciphers[i].name) == 0) + return ciphers[i].cipher; + return IEEE80211_WPA_CIPHER_NONE; +} + +/* ARGSUSED */ +void +setifwpaciphers(const char *val, int d) +{ + struct ieee80211_wpaparams wpa; + char *optlist, *str; + u_int rval = 0; + + if ((optlist = strdup(val)) == NULL) + err(1, "strdup"); + str = strtok(optlist, ","); + while (str != NULL) { + u_int cipher = getwpacipher(str); + if (cipher == IEEE80211_WPA_CIPHER_NONE) + errx(1, "wpaciphers: unknown cipher: %s", str); + + rval |= cipher; + str = strtok(NULL, ","); + } + free(optlist); + + (void)strlcpy(wpa.i_name, name, sizeof(wpa.i_name)); + if (ioctl(s, SIOCG80211WPAPARMS, (caddr_t)&wpa) < 0) + err(1, "SIOCG80211WPAPARMS"); + wpa.i_ciphers = rval; + if (ioctl(s, SIOCS80211WPAPARMS, (caddr_t)&wpa) < 0) + err(1, "SIOCS80211WPAPARMS"); +} + +/* ARGSUSED */ +void +setifwpagroupcipher(const char *val, int d) +{ + struct ieee80211_wpaparams wpa; + u_int cipher; + + cipher = getwpacipher(val); + if (cipher == IEEE80211_WPA_CIPHER_NONE) + errx(1, "wpagroupcipher: unknown cipher: %s", val); + + (void)strlcpy(wpa.i_name, name, sizeof(wpa.i_name)); + if (ioctl(s, SIOCG80211WPAPARMS, (caddr_t)&wpa) < 0) + err(1, "SIOCG80211WPAPARMS"); + wpa.i_groupcipher = cipher; + if (ioctl(s, SIOCS80211WPAPARMS, (caddr_t)&wpa) < 0) + err(1, "SIOCS80211WPAPARMS"); +} + +void +setifwpapsk(const char *val, int d) +{ + struct ieee80211_wpapsk psk; + int len; + + if (d != -1) { + len = sizeof(psk.i_psk); + val = get_string(val, NULL, psk.i_psk, &len); + if (val == NULL) + errx(1, "wpapsk: invalid pre-shared key\n"); + if (len != sizeof(psk.i_psk)) + errx(1, "wpapsk: bad pre-shared key length"); + psk.i_enabled = 1; + } else + psk.i_enabled = 0; + + (void)strlcpy(psk.i_name, name, sizeof(psk.i_name)); + if (ioctl(s, SIOCS80211WPAPSK, (caddr_t)&psk) < 0) + err(1, "SIOCS80211WPAPSK"); +} + void setifchan(const char *val, int d) { @@ -1518,15 +1712,37 @@ setifpowersavesleep(const char *val, int d) } void +print_cipherset(u_int32_t cipherset) +{ + const char *sep = ""; + int i; + + if (cipherset == IEEE80211_WPA_CIPHER_NONE) { + printf("none"); + return; + } + for (i = 0; i < sizeof(ciphers) / sizeof(ciphers[0]); i++) { + if (cipherset & ciphers[i].cipher) { + printf("%s%s", sep, ciphers[i].name); + sep = ","; + } + } +} + +void ieee80211_status(void) { - int len, i, nwkey_verbose, inwid, inwkey, ichan, ipwr, ibssid, itxpower; + int len, i, nwkey_verbose, inwid, inwkey, ipsk, ichan, ipwr; + int ibssid, itxpower, iwmm, iwpa; struct ieee80211_nwid nwid; struct ieee80211_nwkey nwkey; + struct ieee80211_wpapsk psk; struct ieee80211_power power; struct ieee80211chanreq channel; struct ieee80211_bssid bssid; struct ieee80211_txpower txpower; + struct ieee80211_wmmparams wmm; + struct ieee80211_wpaparams wpa; struct ieee80211_nodereq nr; u_int8_t zero_bssid[IEEE80211_ADDR_LEN]; u_int8_t keybuf[IEEE80211_WEP_NKID][16]; @@ -1542,6 +1758,10 @@ ieee80211_status(void) strlcpy(nwkey.i_name, name, sizeof(nwkey.i_name)); inwkey = ioctl(s, SIOCG80211NWKEY, (caddr_t)&nwkey); + memset(&psk, 0, sizeof(psk)); + strlcpy(psk.i_name, name, sizeof(psk.i_name)); + ipsk = ioctl(s, SIOCG80211WPAPSK, (caddr_t)&psk); + memset(&power, 0, sizeof(power)); strlcpy(power.i_name, name, sizeof(power.i_name)); ipwr = ioctl(s, SIOCG80211POWER, &power); @@ -1558,9 +1778,18 @@ ieee80211_status(void) strlcpy(txpower.i_name, name, sizeof(txpower.i_name)); itxpower = ioctl(s, SIOCG80211TXPOWER, &txpower); + memset(&wmm, 0, sizeof(wmm)); + strlcpy(wmm.i_name, name, sizeof(wmm.i_name)); + iwmm = ioctl(s, SIOCG80211WMMPARMS, &wmm); + + memset(&wpa, 0, sizeof(wpa)); + strlcpy(wpa.i_name, name, sizeof(wpa.i_name)); + iwpa = ioctl(s, SIOCG80211WPAPARMS, &wpa); + /* check if any ieee80211 option is active */ - if (inwid == 0 || inwkey == 0 || ipwr == 0 || - ichan == 0 || ibssid == 0 || itxpower == 0) + if (inwid == 0 || inwkey == 0 || ipsk == 0 || ipwr == 0 || + ichan == 0 || ibssid == 0 || iwmm == 0 || iwpa == 0 || + itxpower == 0) fputs("\tieee80211:", stdout); else return; @@ -1653,6 +1882,41 @@ ieee80211_status(void) } } + if (ipsk == 0 && psk.i_enabled) { + fputs(" wpapsk ", stdout); + if (psk.i_enabled == 2) + fputs("<not displayed>", stdout); + else + print_string(psk.i_psk, sizeof(psk.i_psk)); + } + if (iwpa == 0 && wpa.i_enabled) { + const char *sep; + + fputs(" wpaprotos ", stdout); sep = ""; + if (wpa.i_protos & IEEE80211_WPA_PROTO_WPA1) { + fputs("wpa1", stdout); + sep = ","; + } + if (wpa.i_protos & IEEE80211_WPA_PROTO_WPA2) + printf("%swpa2", sep); + + fputs(" wpaakms ", stdout); sep = ""; + if (wpa.i_akms & IEEE80211_WPA_AKM_PSK) { + fputs("psk", stdout); + sep = ","; + } + if (wpa.i_akms & IEEE80211_WPA_AKM_IEEE8021X) + printf("%s802.1x", sep); + + fputs(" wpaciphers ", stdout); + print_cipherset(wpa.i_ciphers); + + fputs(" wpagroupcipher ", stdout); + print_cipherset(wpa.i_groupcipher); + } + if (iwmm == 0 && wmm.i_enabled) + fputs(" wmm", stdout); + if (ipwr == 0 && power.i_enabled) printf(" powersave on (%dms sleep)", power.i_maxsleep); @@ -1761,6 +2025,7 @@ ieee80211_printnode(struct ieee80211_nodereq *nr) printb_status(nr->nr_capinfo, IEEE80211_CAPINFO_BITS); putchar(' '); } + if ((nr->nr_flags & IEEE80211_NODEREQ_AP) == 0) printb_status(IEEE80211_NODEREQ_STATE(nr->nr_state), IEEE80211_NODEREQ_STATE_BITS); diff --git a/sys/conf/files b/sys/conf/files index 9c45d640adf..225e7fc8b25 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $OpenBSD: files,v 1.427 2008/04/09 19:49:55 robert Exp $ +# $OpenBSD: files,v 1.428 2008/04/16 18:32:14 damien Exp $ # $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -783,6 +783,9 @@ file net/if_trunk.c trunk needs-count file net80211/ieee80211.c wlan file net80211/ieee80211_amrr.c wlan file net80211/ieee80211_crypto.c wlan +file net80211/ieee80211_crypto_ccmp.c wlan +file net80211/ieee80211_crypto_tkip.c wlan +file net80211/ieee80211_crypto_wep.c wlan file net80211/ieee80211_input.c wlan file net80211/ieee80211_ioctl.c wlan file net80211/ieee80211_node.c wlan diff --git a/sys/dev/ic/acx.c b/sys/dev/ic/acx.c index 472ccf0f90c..f385eb55c7c 100644 --- a/sys/dev/ic/acx.c +++ b/sys/dev/ic/acx.c @@ -1,4 +1,4 @@ -/* $OpenBSD: acx.c,v 1.81 2008/03/13 23:07:28 brad Exp $ */ +/* $OpenBSD: acx.c,v 1.82 2008/04/16 18:32:15 damien Exp $ */ /* * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org> @@ -998,10 +998,11 @@ acx_start(struct ifnet *ifp) wh = mtod(m, struct ieee80211_frame *); if ((wh->i_fc[1] & IEEE80211_FC1_WEP) && !sc->chip_hw_crypt) { - m = ieee80211_wep_crypt(ifp, m, 1); - if (m == NULL) { + struct ieee80211_key *k; + + k = ieee80211_get_txkey(ic, wh, ni); + if ((m = ieee80211_encrypt(ic, m, k)) == NULL) { ieee80211_release_node(ic, ni); - m_freem(m); ifp->if_oerrors++; continue; } diff --git a/sys/dev/ic/atw.c b/sys/dev/ic/atw.c index 3525221e361..56b9ae24c6a 100644 --- a/sys/dev/ic/atw.c +++ b/sys/dev/ic/atw.c @@ -1,4 +1,4 @@ -/* $OpenBSD: atw.c,v 1.56 2008/03/13 23:07:29 brad Exp $ */ +/* $OpenBSD: atw.c,v 1.57 2008/04/16 18:32:15 damien Exp $ */ /* $NetBSD: atw.c,v 1.69 2004/07/23 07:07:55 dyoung Exp $ */ /*- @@ -3614,6 +3614,7 @@ atw_start(struct ifnet *ifp) struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni; struct ieee80211_frame *wh; + struct ieee80211_key *k; struct atw_frame *hh; struct mbuf *m0, *m; struct atw_txsoft *txs, *last_txs; @@ -3670,8 +3671,11 @@ atw_start(struct ifnet *ifp) break; } - if (sc->sc_ic.ic_flags & IEEE80211_F_WEPON) { - if ((m0 = ieee80211_wep_crypt(ifp, m0, 1)) == NULL) { + if (ic->ic_flags & IEEE80211_F_WEPON) { + wh = mtod(m0, struct ieee80211_frame *); + k = ieee80211_get_txkey(ic, wh, ni); + m0 = ieee80211_encrypt(ic, m0, k); + if (m0 == NULL) { ifp->if_oerrors++; break; } diff --git a/sys/dev/ic/bwi.c b/sys/dev/ic/bwi.c index 18f2da657e4..1be7833615a 100644 --- a/sys/dev/ic/bwi.c +++ b/sys/dev/ic/bwi.c @@ -1,4 +1,4 @@ -/* $OpenBSD: bwi.c,v 1.74 2008/02/25 21:13:30 mglocker Exp $ */ +/* $OpenBSD: bwi.c,v 1.75 2008/04/16 18:32:15 damien Exp $ */ /* * Copyright (c) 2007 The DragonFly Project. All rights reserved. @@ -864,6 +864,7 @@ bwi_attach(struct bwi_softc *sc) ic->ic_caps = IEEE80211_C_SHSLOT | IEEE80211_C_SHPREAMBLE | IEEE80211_C_WEP | + IEEE80211_C_RSN | IEEE80211_C_MONITOR; ic->ic_state = IEEE80211_S_INIT; ic->ic_opmode = IEEE80211_M_STA; @@ -7196,6 +7197,7 @@ bwi_start(struct ifnet *ifp) while (tbd->tbd_buf[idx].tb_mbuf == NULL) { struct ieee80211_frame *wh; struct ieee80211_node *ni; + struct ieee80211_key *k; struct mbuf *m; int mgt_pkt = 0; @@ -7249,9 +7251,9 @@ bwi_start(struct ifnet *ifp) bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_OUT); #endif wh = mtod(m, struct ieee80211_frame *); - if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - m = ieee80211_wep_crypt(ifp, m, 1); - if (m == NULL) + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_get_txkey(ic, wh, ni); + if ((m = ieee80211_encrypt(ic, m, k)) == NULL) return; } wh = NULL; /* Catch any invalid use */ diff --git a/sys/dev/ic/malo.c b/sys/dev/ic/malo.c index a0a30abd071..e17cd6585cb 100644 --- a/sys/dev/ic/malo.c +++ b/sys/dev/ic/malo.c @@ -1,4 +1,4 @@ -/* $OpenBSD: malo.c,v 1.81 2007/11/10 14:20:15 mglocker Exp $ */ +/* $OpenBSD: malo.c,v 1.82 2008/04/16 18:32:15 damien Exp $ */ /* * Copyright (c) 2006 Claudio Jeker <claudio@openbsd.org> @@ -378,7 +378,8 @@ malo_attach(struct malo_softc *sc) IEEE80211_C_MONITOR | IEEE80211_C_SHPREAMBLE | IEEE80211_C_SHSLOT | - IEEE80211_C_WEP; + IEEE80211_C_WEP | + IEEE80211_C_RSN; ic->ic_opmode = IEEE80211_M_STA; ic->ic_state = IEEE80211_S_INIT; ic->ic_max_rssi = 75; @@ -1411,15 +1412,6 @@ malo_tx_mgt(struct malo_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) } wh = mtod(m0, struct ieee80211_frame *); - if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - m0 = ieee80211_wep_crypt(ifp, m0, 1); - if (m0 == NULL) - return (ENOBUFS); - - /* packet header may have moved, reset our local pointer */ - wh = mtod(m0, struct ieee80211_frame *); - } - #if NBPFILTER > 0 if (sc->sc_drvbpf != NULL) { struct mbuf mb; @@ -1506,6 +1498,7 @@ malo_tx_data(struct malo_softc *sc, struct mbuf *m0, struct malo_tx_desc *desc; struct malo_tx_data *data; struct ieee80211_frame *wh; + struct ieee80211_key *k; struct mbuf *mnew; int error; @@ -1524,8 +1517,8 @@ malo_tx_data(struct malo_softc *sc, struct mbuf *m0, wh = mtod(m0, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - m0 = ieee80211_wep_crypt(ifp, m0, 1); - if (m0 == NULL) + k = ieee80211_get_txkey(ic, wh, ni); + if ((m0 = ieee80211_encrypt(ic, m0, k)) == NULL) return (ENOBUFS); /* packet header may have moved, reset our local pointer */ diff --git a/sys/dev/ic/pgt.c b/sys/dev/ic/pgt.c index 4b7c9ea9866..b3da4357569 100644 --- a/sys/dev/ic/pgt.c +++ b/sys/dev/ic/pgt.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pgt.c,v 1.47 2008/03/13 23:07:29 brad Exp $ */ +/* $OpenBSD: pgt.c,v 1.48 2008/04/16 18:32:15 damien Exp $ */ /* * Copyright (c) 2006 Claudio Jeker <claudio@openbsd.org> @@ -2978,10 +2978,6 @@ pgt_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) ic->ic_if.if_timer = 0; ic->ic_mgt_timer = 0; ic->ic_flags &= ~IEEE80211_F_SIBSS; - if (ic->ic_wep_ctx != NULL) { - free(ic->ic_wep_ctx, M_DEVBUF); - ic->ic_wep_ctx = NULL; - } ieee80211_free_allnodes(ic); break; case IEEE80211_S_SCAN: diff --git a/sys/dev/ic/rt2560.c b/sys/dev/ic/rt2560.c index de3de628cb3..81bbb9f93f0 100644 --- a/sys/dev/ic/rt2560.c +++ b/sys/dev/ic/rt2560.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rt2560.c,v 1.35 2007/11/17 14:29:11 damien Exp $ */ +/* $OpenBSD: rt2560.c,v 1.36 2008/04/16 18:32:15 damien Exp $ */ /*- * Copyright (c) 2005, 2006 @@ -242,7 +242,8 @@ rt2560_attach(void *xsc, int id) IEEE80211_C_TXPMGT | /* tx power management */ IEEE80211_C_SHPREAMBLE | /* short preamble supported */ IEEE80211_C_SHSLOT | /* short slot time supported */ - IEEE80211_C_WEP; /* s/w WEP */ + IEEE80211_C_WEP | /* s/w WEP */ + IEEE80211_C_RSN; /* WPA/RSN */ /* set supported .11b and .11g rates */ ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b; @@ -1595,7 +1596,6 @@ rt2560_tx_mgt(struct rt2560_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) { struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = &ic->ic_if; struct rt2560_tx_desc *desc; struct rt2560_tx_data *data; struct ieee80211_frame *wh; @@ -1606,17 +1606,6 @@ rt2560_tx_mgt(struct rt2560_softc *sc, struct mbuf *m0, desc = &sc->prioq.desc[sc->prioq.cur]; data = &sc->prioq.data[sc->prioq.cur]; - wh = mtod(m0, struct ieee80211_frame *); - - if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - m0 = ieee80211_wep_crypt(ifp, m0, 1); - if (m0 == NULL) - return ENOBUFS; - - /* packet header may have moved, reset our local pointer */ - wh = mtod(m0, struct ieee80211_frame *); - } - error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m0, BUS_DMA_NOWAIT); if (error != 0) { @@ -1691,11 +1680,11 @@ rt2560_tx_data(struct rt2560_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) { struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = &ic->ic_if; struct rt2560_tx_ring *txq = &sc->txq; struct rt2560_tx_desc *desc; struct rt2560_tx_data *data; struct ieee80211_frame *wh; + struct ieee80211_key *k; struct mbuf *mnew; uint16_t dur; uint32_t flags = 0; @@ -1703,9 +1692,10 @@ rt2560_tx_data(struct rt2560_softc *sc, struct mbuf *m0, wh = mtod(m0, struct ieee80211_frame *); - if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - m0 = ieee80211_wep_crypt(ifp, m0, 1); - if (m0 == NULL) + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_get_txkey(ic, wh, ni); + + if ((m0 = ieee80211_encrypt(ic, m0, k)) == NULL) return ENOBUFS; /* packet header may have moved, reset our local pointer */ diff --git a/sys/dev/ic/rt2661.c b/sys/dev/ic/rt2661.c index 0cc21cd0910..04a98615465 100644 --- a/sys/dev/ic/rt2661.c +++ b/sys/dev/ic/rt2661.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rt2661.c,v 1.40 2007/11/17 14:29:11 damien Exp $ */ +/* $OpenBSD: rt2661.c,v 1.41 2008/04/16 18:32:15 damien Exp $ */ /*- * Copyright (c) 2006 @@ -252,7 +252,8 @@ rt2661_attach(void *xsc, int id) IEEE80211_C_TXPMGT | /* tx power management */ IEEE80211_C_SHPREAMBLE | /* short preamble supported */ IEEE80211_C_SHSLOT | /* short slot time supported */ - IEEE80211_C_WEP; /* s/w WEP */ + IEEE80211_C_WEP | /* s/w WEP */ + IEEE80211_C_RSN; /* WPA/RSN */ if (sc->rf_rev == RT2661_RF_5225 || sc->rf_rev == RT2661_RF_5325) { /* set supported .11a rates */ @@ -1442,7 +1443,6 @@ rt2661_tx_mgt(struct rt2661_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) { struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = &ic->ic_if; struct rt2661_tx_desc *desc; struct rt2661_tx_data *data; struct ieee80211_frame *wh; @@ -1456,17 +1456,6 @@ rt2661_tx_mgt(struct rt2661_softc *sc, struct mbuf *m0, /* send mgt frames at the lowest available rate */ rate = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ? 12 : 2; - wh = mtod(m0, struct ieee80211_frame *); - - if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - m0 = ieee80211_wep_crypt(ifp, m0, 1); - if (m0 == NULL) - return ENOBUFS; - - /* packet header may have moved, reset our local pointer */ - wh = mtod(m0, struct ieee80211_frame *); - } - error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m0, BUS_DMA_NOWAIT); if (error != 0) { @@ -1499,6 +1488,8 @@ rt2661_tx_mgt(struct rt2661_softc *sc, struct mbuf *m0, data->m = m0; data->ni = ni; + wh = mtod(m0, struct ieee80211_frame *); + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { flags |= RT2661_TX_NEED_ACK; @@ -1539,11 +1530,11 @@ rt2661_tx_data(struct rt2661_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, int ac) { struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = &ic->ic_if; struct rt2661_tx_ring *txq = &sc->txq[ac]; struct rt2661_tx_desc *desc; struct rt2661_tx_data *data; struct ieee80211_frame *wh; + struct ieee80211_key *k; struct mbuf *mnew; uint16_t dur; uint32_t flags = 0; @@ -1551,9 +1542,10 @@ rt2661_tx_data(struct rt2661_softc *sc, struct mbuf *m0, wh = mtod(m0, struct ieee80211_frame *); - if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - m0 = ieee80211_wep_crypt(ifp, m0, 1); - if (m0 == NULL) + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_get_txkey(ic, wh, ni); + + if ((m0 = ieee80211_encrypt(ic, m0, k)) == NULL) return ENOBUFS; /* packet header may have moved, reset our local pointer */ diff --git a/sys/dev/ic/rt2860.c b/sys/dev/ic/rt2860.c index 292dacacb70..82c085e6a4e 100644 --- a/sys/dev/ic/rt2860.c +++ b/sys/dev/ic/rt2860.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rt2860.c,v 1.11 2007/12/14 21:28:49 damien Exp $ */ +/* $OpenBSD: rt2860.c,v 1.12 2008/04/16 18:32:15 damien Exp $ */ /*- * Copyright (c) 2007 @@ -127,9 +127,9 @@ void rt2860_updateslot(struct ieee80211com *); void rt2860_updateprot(struct ieee80211com *); void rt2860_updateedca(struct ieee80211com *); int rt2860_set_key(struct ieee80211com *, struct ieee80211_node *, - const struct ieee80211_key *); + struct ieee80211_key *); void rt2860_delete_key(struct ieee80211com *, - struct ieee80211_node *, int); + struct ieee80211_node *, struct ieee80211_key *); int8_t rt2860_rssi2dbm(struct rt2860_softc *, uint8_t, uint8_t); uint8_t rt2860_maxrssi_chain(struct rt2860_softc *, const struct rt2860_rxwi *); @@ -236,7 +236,8 @@ rt2860_attach(void *xsc, int id) IEEE80211_C_TXPMGT | /* tx power management */ IEEE80211_C_SHPREAMBLE | /* short preamble supported */ IEEE80211_C_SHSLOT | /* short slot time supported */ - IEEE80211_C_WEP; /* s/w WEP */ + IEEE80211_C_WEP | /* s/w WEP */ + IEEE80211_C_RSN; /* WPA/RSN */ if (sc->rf_rev == RT2860_RF_2750 || sc->rf_rev == RT2860_RF_2850) { /* set supported .11a rates */ @@ -282,9 +283,10 @@ rt2860_attach(void *xsc, int id) ic->ic_newassoc = rt2860_newassoc; ic->ic_updateslot = rt2860_updateslot; ic->ic_updateedca = rt2860_updateedca; +#ifdef notyet ic->ic_set_key = rt2860_set_key; ic->ic_delete_key = rt2860_delete_key; - +#endif /* override state transition machine */ sc->sc_newstate = ic->ic_newstate; ic->ic_newstate = rt2860_newstate; @@ -1322,12 +1324,12 @@ rt2860_tx_data(struct rt2860_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, int qid) { struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = &ic->ic_if; struct rt2860_tx_ring *ring = &sc->txq[qid]; struct rt2860_tx_data *data; struct rt2860_txd *txd; struct rt2860_txwi *txwi; struct ieee80211_frame *wh; + struct ieee80211_key *k; bus_dma_segment_t *seg; u_int hdrlen; uint16_t dur; @@ -1356,8 +1358,9 @@ rt2860_tx_data(struct rt2860_softc *sc, struct mbuf *m0, mcs = rt2860_rate2mcs(rate); if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { - m0 = ieee80211_wep_crypt(ifp, m0, 1); - if (m0 == NULL) + k = ieee80211_get_txkey(ic, wh, ni); + + if ((m0 = ieee80211_encrypt(ic, m0, k)) == NULL) return ENOBUFS; /* packet header may have moved, reset our local pointer */ @@ -2098,7 +2101,7 @@ rt2860_updateedca(struct ieee80211com *ic) int rt2860_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, - const struct ieee80211_key *k) + struct ieee80211_key *k) { struct rt2860_softc *sc = ic->ic_softc; bus_size_t base; @@ -2127,8 +2130,7 @@ rt2860_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, /* install group key */ base = RT2860_SKEY(0, k->k_id); RAL_WRITE_REGION_1(sc, base, k->k_key, k->k_len); - RAL_WRITE_REGION_1(sc, base + 16, k->k_txmic, 8); - RAL_WRITE_REGION_1(sc, base + 24, k->k_rxmic, 8); + /* XXX TKIP + HostAP: swap Tx/Rx MIC */ attr = RAL_READ(sc, RT2860_SKEY_MODE_0_7); attr &= ~(0xf << (k->k_id * 4)); @@ -2139,8 +2141,7 @@ rt2860_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, wcid = RT2860_AID2WCID(ni->ni_associd); base = RT2860_PKEY(wcid); RAL_WRITE_REGION_1(sc, base, k->k_key, k->k_len); - RAL_WRITE_REGION_1(sc, base + 16, k->k_txmic, 8); - RAL_WRITE_REGION_1(sc, base + 24, k->k_rxmic, 8); + /* XXX TKIP + HostAP: swap Tx/Rx MIC */ attr = RAL_READ(sc, RT2860_WCID_ATTR(wcid)); attr = (attr & ~0xf) | (mode << 1) | RT2860_RX_PKEY_EN; @@ -2150,16 +2151,17 @@ rt2860_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, } void -rt2860_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni, int kid) +rt2860_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_key *k) { struct rt2860_softc *sc = ic->ic_softc; uint32_t attr; uint8_t wcid; - if (ni == NULL) { + if (k->k_flags & IEEE80211_KEY_GROUP) { /* remove group key */ attr = RAL_READ(sc, RT2860_SKEY_MODE_0_7); - attr &= ~(0xf << (kid * 4)); + attr &= ~(0xf << (k->k_id * 4)); RAL_WRITE(sc, RT2860_SKEY_MODE_0_7, attr); } else { diff --git a/sys/dev/ic/rt2860reg.h b/sys/dev/ic/rt2860reg.h index 951abe93a1f..b82baccef67 100644 --- a/sys/dev/ic/rt2860reg.h +++ b/sys/dev/ic/rt2860reg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rt2860reg.h,v 1.7 2007/12/14 21:28:49 damien Exp $ */ +/* $OpenBSD: rt2860reg.h,v 1.8 2008/04/16 18:32:15 damien Exp $ */ /*- * Copyright (c) 2007 @@ -842,7 +842,8 @@ struct rt2860_rxwi { { RT2860_GF40_PROT_CFG, 0x03f44084 }, \ { RT2860_MM20_PROT_CFG, 0x01744004 }, \ { RT2860_MM40_PROT_CFG, 0x03f54084 }, \ - { RT2860_TXOP_CTRL_CFG, 0x0000243f }, \ + { RT2860_TXOP_CTRL_CFG, 0x0000583f }, \ + { RT2860_TXOP_HLDR_ET, 0x00000002 }, \ { RT2860_TX_RTS_CFG, 0x00092b20 }, \ { RT2860_EXP_ACK_TIME, 0x002400ca } diff --git a/sys/dev/ic/rtw.c b/sys/dev/ic/rtw.c index 9ccbdf20f2f..f0071a2be4e 100644 --- a/sys/dev/ic/rtw.c +++ b/sys/dev/ic/rtw.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rtw.c,v 1.65 2007/11/21 15:58:22 blambert Exp $ */ +/* $OpenBSD: rtw.c,v 1.66 2008/04/16 18:32:15 damien Exp $ */ /* $NetBSD: rtw.c,v 1.29 2004/12/27 19:49:16 dyoung Exp $ */ /*- @@ -2718,18 +2718,22 @@ rtw_dequeue(struct ifnet *ifp, struct rtw_txsoft_blk **tsbp, struct rtw_txdesc_blk **tdbp, struct mbuf **mp, struct ieee80211_node **nip) { + struct ieee80211com *ic; + struct ieee80211_frame *wh; + struct ieee80211_key *k; struct mbuf *m0; struct rtw_softc *sc; short *if_flagsp; sc = (struct rtw_softc *)ifp->if_softc; + ic = &sc->sc_ic; DPRINTF(sc, RTW_DEBUG_XMIT, ("%s: enter %s\n", sc->sc_dev.dv_xname, __func__)); if_flagsp = &ifp->if_flags; - if (sc->sc_ic.ic_state == IEEE80211_S_RUN && + if (ic->ic_state == IEEE80211_S_RUN && (*mp = rtw_80211_dequeue(sc, &sc->sc_beaconq, RTW_TXPRIBCN, tsbp, tdbp, nip, if_flagsp)) != NULL) { DPRINTF(sc, RTW_DEBUG_XMIT, ("%s: dequeue beacon frame\n", @@ -2737,7 +2741,7 @@ rtw_dequeue(struct ifnet *ifp, struct rtw_txsoft_blk **tsbp, return 0; } - if ((*mp = rtw_80211_dequeue(sc, &sc->sc_ic.ic_mgtq, RTW_TXPRIMD, tsbp, + if ((*mp = rtw_80211_dequeue(sc, &ic->ic_mgtq, RTW_TXPRIMD, tsbp, tdbp, nip, if_flagsp)) != NULL) { DPRINTF(sc, RTW_DEBUG_XMIT, ("%s: dequeue mgt frame\n", __func__)); @@ -2749,14 +2753,14 @@ rtw_dequeue(struct ifnet *ifp, struct rtw_txsoft_blk **tsbp, return 0; } - if ((*mp = rtw_80211_dequeue(sc, &sc->sc_ic.ic_pwrsaveq, RTW_TXPRIHI, + if ((*mp = rtw_80211_dequeue(sc, &ic->ic_pwrsaveq, RTW_TXPRIHI, tsbp, tdbp, nip, if_flagsp)) != NULL) { DPRINTF(sc, RTW_DEBUG_XMIT, ("%s: dequeue pwrsave frame\n", __func__)); return 0; } - if (sc->sc_ic.ic_state != IEEE80211_S_RUN) { + if (ic->ic_state != IEEE80211_S_RUN) { DPRINTF(sc, RTW_DEBUG_XMIT, ("%s: not running\n", __func__)); return 0; } @@ -2797,8 +2801,10 @@ rtw_dequeue(struct ifnet *ifp, struct rtw_txsoft_blk **tsbp, } /* XXX should do WEP in hardware */ - if (sc->sc_ic.ic_flags & IEEE80211_F_WEPON) { - if ((m0 = ieee80211_wep_crypt(ifp, m0, 1)) == NULL) + if (ic->ic_flags & IEEE80211_F_WEPON) { + wh = mtod(m0, struct ieee80211_frame *); + k = ieee80211_get_txkey(ic, wh, *nip); + if ((m0 = ieee80211_encrypt(ic, m0, k)) == NULL) return -1; } diff --git a/sys/dev/pci/if_ipw.c b/sys/dev/pci/if_ipw.c index 9810a9f77d6..b6ff2171dcd 100644 --- a/sys/dev/pci/if_ipw.c +++ b/sys/dev/pci/if_ipw.c @@ -1,7 +1,7 @@ -/* $OpenBSD: if_ipw.c,v 1.71 2008/02/23 20:38:08 hshoexer Exp $ */ +/* $OpenBSD: if_ipw.c,v 1.72 2008/04/16 18:32:15 damien Exp $ */ /*- - * Copyright (c) 2004-2006 + * Copyright (c) 2004-2008 * Damien Bergamini <damien.bergamini@free.fr>. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -496,7 +496,6 @@ ipw_dma_alloc(struct ipw_softc *sc) error = ENOMEM; goto fail; } - MCLGET(sbuf->m, M_DONTWAIT); if (!(sbuf->m->m_flags & M_EXT)) { m_freem(sbuf->m); @@ -645,15 +644,12 @@ ipw_media_status(struct ifnet *ifp, struct ifmediareq *imr) switch (ic->ic_opmode) { case IEEE80211_M_STA: break; - case IEEE80211_M_IBSS: imr->ifm_active |= IFM_IEEE80211_IBSS; break; - case IEEE80211_M_MONITOR: imr->ifm_active |= IFM_IEEE80211_MONITOR; break; - case IEEE80211_M_AHDEMO: case IEEE80211_M_HOSTAP: /* should not get there */ @@ -835,7 +831,6 @@ ipw_data_intr(struct ipw_softc *sc, struct ipw_status *status, ifp->if_ierrors++; return; } - MCLGET(mnew, M_DONTWAIT); if (!(mnew->m_flags & M_EXT)) { m_freem(mnew); @@ -894,7 +889,6 @@ ipw_data_intr(struct ipw_softc *sc, struct ipw_status *status, #endif wh = mtod(m, struct ieee80211_frame *); - ni = ieee80211_find_rxnode(ic, wh); /* send the frame to the upper layer */ @@ -1101,7 +1095,7 @@ ipw_cmd(struct ipw_softc *sc, uint32_t type, void *data, uint32_t len) bus_dmamap_sync(sc->sc_dmat, sc->cmd_map, 0, sizeof (struct ipw_cmd), BUS_DMASYNC_PREWRITE); - + bus_dmamap_sync(sc->sc_dmat, sc->tbd_map, sc->txcur * sizeof (struct ipw_bd), sizeof (struct ipw_bd), BUS_DMASYNC_PREWRITE); @@ -1122,6 +1116,7 @@ ipw_tx_start(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni) struct ipw_softc *sc = ifp->if_softc; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_frame *wh; + struct ieee80211_key *k; struct ipw_soft_bd *sbd; struct ipw_soft_hdr *shdr; struct ipw_soft_buf *sbuf; @@ -1130,9 +1125,10 @@ ipw_tx_start(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni) wh = mtod(m, struct ieee80211_frame *); - if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - m = ieee80211_wep_crypt(ifp, m, 1); - if (m == NULL) + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_get_txkey(ic, wh, ni); + + if ((m = ieee80211_encrypt(ic, m, k)) == NULL) return ENOBUFS; /* packet header may have moved, reset our local pointer */ @@ -1163,7 +1159,7 @@ ipw_tx_start(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni) shdr->hdr.type = htole32(IPW_HDR_TYPE_SEND); shdr->hdr.subtype = htole32(0); - shdr->hdr.encrypted = (wh->i_fc[1] & IEEE80211_FC1_WEP) ? 1 : 0; + shdr->hdr.encrypted = (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) ? 1 : 0; shdr->hdr.encrypt = 0; shdr->hdr.keyidx = 0; shdr->hdr.keysz = 0; @@ -1192,7 +1188,6 @@ ipw_tx_start(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni) m_freem(m); return ENOMEM; } - M_DUP_PKTHDR(mnew, m); if (m->m_pkthdr.len > MHLEN) { MCLGET(mnew, M_DONTWAIT); @@ -1202,7 +1197,6 @@ ipw_tx_start(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni) return ENOMEM; } } - m_copydata(m, 0, m->m_pkthdr.len, mtod(mnew, caddr_t)); m_freem(m); mnew->m_len = mnew->m_pkthdr.len; @@ -1297,8 +1291,8 @@ ipw_start(struct ifnet *ifp) { struct ipw_softc *sc = ifp->if_softc; struct ieee80211com *ic = &sc->sc_ic; - struct mbuf *m; struct ieee80211_node *ni; + struct mbuf *m; if (ic->ic_state != IEEE80211_S_RUN) return; @@ -1317,16 +1311,13 @@ ipw_start(struct ifnet *ifp) if (ifp->if_bpf != NULL) bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT); #endif - m = ieee80211_encap(ifp, m, &ni); if (m == NULL) continue; - #if NBPFILTER > 0 if (ic->ic_rawbpf != NULL) bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_OUT); #endif - if (ipw_tx_start(ifp, m, ni) != 0) { if (ni != NULL) ieee80211_release_node(ic, ni); @@ -1452,14 +1443,13 @@ ipw_read_table2(struct ipw_softc *sc, uint32_t off, void *buf, uint32_t *len) info = MEM_READ_4(sc, sc->table2_base + off + 4); count = info >> 16; - size = info & 0xffff; + size = info & 0xffff; total = count * size; if (total > *len) { *len = total; return EINVAL; } - *len = total; ipw_read_mem_1(sc, addr, buf, total); @@ -1469,6 +1459,7 @@ ipw_read_table2(struct ipw_softc *sc, uint32_t off, void *buf, uint32_t *len) void ipw_stop_master(struct ipw_softc *sc) { + uint32_t tmp; int ntries; /* disable interrupts */ @@ -1484,8 +1475,8 @@ ipw_stop_master(struct ipw_softc *sc) printf("%s: timeout waiting for master\n", sc->sc_dev.dv_xname); - CSR_WRITE_4(sc, IPW_CSR_RST, CSR_READ_4(sc, IPW_CSR_RST) | - IPW_RST_PRINCETON_RESET); + tmp = CSR_READ_4(sc, IPW_CSR_RST); + CSR_WRITE_4(sc, IPW_CSR_RST, tmp | IPW_RST_PRINCETON_RESET); sc->flags &= ~IPW_FLAG_FW_INITED; } @@ -1493,13 +1484,14 @@ ipw_stop_master(struct ipw_softc *sc) int ipw_reset(struct ipw_softc *sc) { + uint32_t tmp; int ntries; ipw_stop_master(sc); /* move adapter to D0 state */ - CSR_WRITE_4(sc, IPW_CSR_CTL, CSR_READ_4(sc, IPW_CSR_CTL) | - IPW_CTL_INIT); + tmp = CSR_READ_4(sc, IPW_CSR_CTL); + CSR_WRITE_4(sc, IPW_CSR_CTL, tmp | IPW_CTL_INIT); /* wait for clock stabilization */ for (ntries = 0; ntries < 1000; ntries++) { @@ -1510,13 +1502,13 @@ ipw_reset(struct ipw_softc *sc) if (ntries == 1000) return EIO; - CSR_WRITE_4(sc, IPW_CSR_RST, CSR_READ_4(sc, IPW_CSR_RST) | - IPW_RST_SW_RESET); + tmp = CSR_READ_4(sc, IPW_CSR_RST); + CSR_WRITE_4(sc, IPW_CSR_RST, tmp | IPW_RST_SW_RESET); DELAY(10); - CSR_WRITE_4(sc, IPW_CSR_CTL, CSR_READ_4(sc, IPW_CSR_CTL) | - IPW_CTL_INIT); + tmp = CSR_READ_4(sc, IPW_CSR_CTL); + CSR_WRITE_4(sc, IPW_CSR_CTL, tmp | IPW_CTL_INIT); return 0; } @@ -1526,6 +1518,7 @@ ipw_load_ucode(struct ipw_softc *sc, u_char *uc, int size) { int ntries; + /* voodoo from the Intel Linux driver */ MEM_WRITE_4(sc, 0x3000e0, 0x80000000); CSR_WRITE_4(sc, IPW_CSR_RST, 0); @@ -1577,7 +1570,7 @@ int ipw_load_firmware(struct ipw_softc *sc, u_char *fw, int size) { u_char *p, *end; - uint32_t dst; + uint32_t tmp, dst; uint16_t len; int error; @@ -1605,8 +1598,8 @@ ipw_load_firmware(struct ipw_softc *sc, u_char *fw, int size) /* tell the adapter to initialize the firmware */ CSR_WRITE_4(sc, IPW_CSR_RST, 0); - CSR_WRITE_4(sc, IPW_CSR_CTL, CSR_READ_4(sc, IPW_CSR_CTL) | - IPW_CTL_ALLOW_STANDBY); + tmp = CSR_READ_4(sc, IPW_CSR_CTL); + CSR_WRITE_4(sc, IPW_CSR_CTL, tmp | IPW_CTL_ALLOW_STANDBY); /* wait at most one second for firmware initialization to complete */ if ((error = tsleep(sc, 0, "ipwinit", hz)) != 0) { @@ -1615,8 +1608,9 @@ ipw_load_firmware(struct ipw_softc *sc, u_char *fw, int size) return error; } - CSR_WRITE_4(sc, IPW_CSR_IO, CSR_READ_4(sc, IPW_CSR_IO) | - IPW_IO_GPIO1_MASK | IPW_IO_GPIO3_MASK); + tmp = CSR_READ_4(sc, IPW_CSR_IO); + CSR_WRITE_4(sc, IPW_CSR_IO, tmp | IPW_IO_GPIO1_MASK | + IPW_IO_GPIO3_MASK); return 0; } @@ -1624,52 +1618,42 @@ ipw_load_firmware(struct ipw_softc *sc, u_char *fw, int size) int ipw_read_firmware(struct ipw_softc *sc, struct ipw_firmware *fw) { - struct ipw_firmware_hdr *hdr; + const struct ipw_firmware_hdr *hdr; const char *name; - u_char *p; size_t size; int error; switch (sc->sc_ic.ic_opmode) { case IEEE80211_M_STA: - case IEEE80211_M_HOSTAP: name = "ipw-bss"; break; - case IEEE80211_M_IBSS: - case IEEE80211_M_AHDEMO: name = "ipw-ibss"; break; - case IEEE80211_M_MONITOR: name = "ipw-monitor"; break; + default: + /* should not get there */ + return ENODEV; } - if ((error = loadfirmware(name, &fw->data, &size)) != 0) return error; - if (size < sizeof (struct ipw_firmware_hdr)) { + if (size < sizeof (*hdr)) { error = EINVAL; goto fail; } - - p = fw->data; - hdr = (struct ipw_firmware_hdr *)p; - fw->main_size = letoh32(hdr->main_size); + hdr = (const struct ipw_firmware_hdr *)fw->data; + fw->main_size = letoh32(hdr->main_size); fw->ucode_size = letoh32(hdr->ucode_size); - p += sizeof (struct ipw_firmware_hdr); - size -= sizeof (struct ipw_firmware_hdr); - - if (size < fw->main_size + fw->ucode_size) { + if (size < sizeof (*hdr) + fw->main_size + fw->ucode_size) { error = EINVAL; goto fail; } - - fw->main = p; - fw->ucode = p + fw->main_size; - sc->fw_data = fw->data; + fw->main = fw->data + sizeof (*hdr); + fw->ucode = fw->main + fw->main_size; return 0; @@ -1692,18 +1676,17 @@ ipw_config(struct ipw_softc *sc) switch (ic->ic_opmode) { case IEEE80211_M_STA: - case IEEE80211_M_HOSTAP: data = htole32(IPW_MODE_BSS); break; - case IEEE80211_M_IBSS: - case IEEE80211_M_AHDEMO: data = htole32(IPW_MODE_IBSS); break; - case IEEE80211_M_MONITOR: data = htole32(IPW_MODE_MONITOR); break; + default: + /* should not get there */ + return ENODEV; } DPRINTF(("Setting mode to %u\n", letoh32(data))); error = ipw_cmd(sc, IPW_CMD_SET_MODE, &data, sizeof data); @@ -1892,7 +1875,6 @@ ipw_init(struct ifnet *ifp) sc->sc_dev.dv_xname, error); goto fail1; } - if ((error = ipw_load_ucode(sc, fw.ucode, fw.ucode_size)) != 0) { printf("%s: could not load microcode\n", sc->sc_dev.dv_xname); goto fail2; @@ -1924,8 +1906,8 @@ ipw_init(struct ifnet *ifp) printf("%s: could not load firmware\n", sc->sc_dev.dv_xname); goto fail2; } - sc->flags |= IPW_FLAG_FW_INITED; + free(fw.data, M_DEVBUF); /* retrieve information tables base addresses */ sc->table1_base = CSR_READ_4(sc, IPW_CSR_TABLE1_BASE); @@ -1938,7 +1920,6 @@ ipw_init(struct ifnet *ifp) sc->sc_dev.dv_xname); goto fail2; } - ifp->if_flags &= ~IFF_OACTIVE; ifp->if_flags |= IFF_RUNNING; @@ -1969,14 +1950,6 @@ ipw_stop(struct ifnet *ifp, int disable) for (i = 0; i < IPW_NTBD; i++) ipw_release_sbd(sc, &sc->stbd_list[i]); - /* - * Free memory claimed by firmware. - */ - if (sc->fw_data) { - free(sc->fw_data, M_DEVBUF); - sc->fw_data = NULL; - } - ieee80211_new_state(ic, IEEE80211_S_INIT, -1); } diff --git a/sys/dev/pci/if_ipwvar.h b/sys/dev/pci/if_ipwvar.h index 56348ad2f92..ca0d5b73a4d 100644 --- a/sys/dev/pci/if_ipwvar.h +++ b/sys/dev/pci/if_ipwvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_ipwvar.h,v 1.15 2008/02/23 21:35:41 hshoexer Exp $ */ +/* $OpenBSD: if_ipwvar.h,v 1.16 2008/04/16 18:32:15 damien Exp $ */ /*- * Copyright (c) 2004-2006 @@ -158,6 +158,4 @@ struct ipw_softc { #define sc_txtap sc_txtapu.th int sc_txtap_len; #endif - - void *fw_data; }; diff --git a/sys/dev/pci/if_iwn.c b/sys/dev/pci/if_iwn.c index 71f793a9bac..11eb562932e 100644 --- a/sys/dev/pci/if_iwn.c +++ b/sys/dev/pci/if_iwn.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_iwn.c,v 1.17 2008/03/08 16:24:44 espie Exp $ */ +/* $OpenBSD: if_iwn.c,v 1.18 2008/04/16 18:32:15 damien Exp $ */ /*- * Copyright (c) 2007 @@ -135,7 +135,7 @@ int iwn_ioctl(struct ifnet *, u_long, caddr_t); int iwn_cmd(struct iwn_softc *, int, const void *, int, int); int iwn_setup_node_mrr(struct iwn_softc *, uint8_t, int); int iwn_set_key(struct ieee80211com *, struct ieee80211_node *, - const struct ieee80211_key *); + struct ieee80211_key *); void iwn_updateedca(struct ieee80211com *); void iwn_set_led(struct iwn_softc *, uint8_t, uint8_t, uint8_t); int iwn_set_critical_temp(struct iwn_softc *); @@ -293,6 +293,7 @@ iwn_attach(struct device *parent, struct device *self, void *aux) /* set device capabilities */ ic->ic_caps = IEEE80211_C_WEP | /* s/w WEP */ + IEEE80211_C_RSN | /* WPA/RSN */ IEEE80211_C_MONITOR | /* monitor mode supported */ IEEE80211_C_TXPMGT | /* tx power management */ IEEE80211_C_SHSLOT | /* short slot time supported */ @@ -322,7 +323,9 @@ iwn_attach(struct device *parent, struct device *self, void *aux) ieee80211_ifattach(ifp); ic->ic_node_alloc = iwn_node_alloc; ic->ic_newassoc = iwn_newassoc; +#ifdef notyet ic->ic_set_key = iwn_set_key; +#endif ic->ic_updateedca = iwn_updateedca; /* override state transition machine */ @@ -1457,7 +1460,7 @@ iwn_notif_intr(struct iwn_softc *sc) DPRINTFN(4,("rx notification qid=%x idx=%d flags=%x type=%d " "len=%d\n", desc->qid, desc->idx, desc->flags, desc->type, - letoh32(desc->len))); + letoh16(desc->len))); if (!(desc->qid & 0x80)) /* reply to a command */ iwn_cmd_intr(sc, desc); @@ -1674,12 +1677,13 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, struct iwn_tx_cmd *cmd; struct iwn_cmd_data *tx; struct ieee80211_frame *wh; + struct ieee80211_key *k; struct mbuf *mnew; bus_addr_t paddr; uint32_t flags; uint8_t type; u_int hdrlen; - int i, rate, error, pad, ovhd = 0; + int i, rate, error, pad; desc = &ring->desc[ring->cur]; data = &ring->data[ring->cur]; @@ -1733,18 +1737,13 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, /* no need to bzero tx, all fields are reinitialized here */ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { - const struct ieee80211_key *key = - &ic->ic_nw_keys[ic->ic_wep_txkey]; - if (key->k_cipher == IEEE80211_CIPHER_WEP40) - tx->security = IWN_CIPHER_WEP40; - else - tx->security = IWN_CIPHER_WEP104; - tx->security |= ic->ic_wep_txkey << 6; - memcpy(&tx->key[3], key->k_key, key->k_len); - /* compute crypto overhead */ - ovhd = IEEE80211_WEP_TOTLEN; - } else - tx->security = 0; + k = ieee80211_get_txkey(ic, wh, ni); + + if ((m0 = ieee80211_encrypt(ic, m0, k)) == NULL) + return ENOBUFS; + + wh = mtod(m0, struct ieee80211_frame *); + } flags = IWN_TX_AUTO_SEQ; if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) @@ -1759,7 +1758,7 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, /* check if RTS/CTS or CTS-to-self protection must be used */ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { /* multicast frames are not sent at OFDM rates in 802.11b/g */ - if (m0->m_pkthdr.len + ovhd + IEEE80211_CRC_LEN > + if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) { flags |= IWN_TX_NEED_RTS | IWN_TX_FULL_TXOP; } else if ((ic->ic_flags & IEEE80211_F_USEPROT) && @@ -2256,7 +2255,7 @@ iwn_setup_node_mrr(struct iwn_softc *sc, uint8_t id, int async) */ int iwn_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, - const struct ieee80211_key *k) + struct ieee80211_key *k) { struct iwn_softc *sc = ic->ic_softc; struct iwn_node_info node; diff --git a/sys/dev/pci/if_wpi.c b/sys/dev/pci/if_wpi.c index f0cd31a8a1e..0549b16de86 100644 --- a/sys/dev/pci/if_wpi.c +++ b/sys/dev/pci/if_wpi.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_wpi.c,v 1.59 2008/03/08 16:24:45 espie Exp $ */ +/* $OpenBSD: if_wpi.c,v 1.60 2008/04/16 18:32:15 damien Exp $ */ /*- * Copyright (c) 2006, 2007 @@ -130,7 +130,7 @@ int wpi_ioctl(struct ifnet *, u_long, caddr_t); int wpi_cmd(struct wpi_softc *, int, const void *, int, int); int wpi_mrr_setup(struct wpi_softc *); int wpi_set_key(struct ieee80211com *, struct ieee80211_node *, - const struct ieee80211_key *); + struct ieee80211_key *); void wpi_updateedca(struct ieee80211com *); void wpi_set_led(struct wpi_softc *, uint8_t, uint8_t, uint8_t); void wpi_enable_tsf(struct wpi_softc *, struct ieee80211_node *); @@ -270,6 +270,7 @@ wpi_attach(struct device *parent, struct device *self, void *aux) /* set device capabilities */ ic->ic_caps = IEEE80211_C_WEP | /* s/w WEP */ + IEEE80211_C_RSN | /* WPA/RSN */ IEEE80211_C_MONITOR | /* monitor mode supported */ IEEE80211_C_TXPMGT | /* tx power management */ IEEE80211_C_SHSLOT | /* short slot time supported */ @@ -299,7 +300,9 @@ wpi_attach(struct device *parent, struct device *self, void *aux) ieee80211_ifattach(ifp); ic->ic_node_alloc = wpi_node_alloc; ic->ic_newassoc = wpi_newassoc; +#ifdef notyet ic->ic_set_key = wpi_set_key; +#endif ic->ic_updateedca = wpi_updateedca; /* override state transition machine */ @@ -1569,9 +1572,10 @@ wpi_tx_data(struct wpi_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, struct wpi_tx_cmd *cmd; struct wpi_cmd_data *tx; struct ieee80211_frame *wh; + struct ieee80211_key *k; struct mbuf *mnew; u_int hdrlen; - int i, rate, error, ovhd = 0; + int i, rate, error; desc = &ring->desc[ring->cur]; data = &ring->data[ring->cur]; @@ -1626,18 +1630,13 @@ wpi_tx_data(struct wpi_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, tx->flags = 0; if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { - const struct ieee80211_key *key = - &ic->ic_nw_keys[ic->ic_wep_txkey]; - if (key->k_cipher == IEEE80211_CIPHER_WEP40) - tx->security = WPI_CIPHER_WEP40; - else - tx->security = WPI_CIPHER_WEP104; - tx->security |= ic->ic_wep_txkey << 6; - memcpy(&tx->key[3], key->k_key, key->k_len); - /* compute crypto overhead */ - ovhd = IEEE80211_WEP_TOTLEN; - } else - tx->security = 0; + k = ieee80211_get_txkey(ic, wh, ni); + + if ((m0 = ieee80211_encrypt(ic, m0, k)) == NULL) + return ENOBUFS; + + wh = mtod(m0, struct ieee80211_frame *); + } if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { tx->id = WPI_ID_BSS; @@ -1648,7 +1647,7 @@ wpi_tx_data(struct wpi_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, /* check if RTS/CTS or CTS-to-self protection must be used */ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { /* multicast frames are not sent at OFDM rates in 802.11b/g */ - if (m0->m_pkthdr.len + ovhd + IEEE80211_CRC_LEN > + if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) { tx->flags |= htole32(WPI_TX_NEED_RTS | WPI_TX_FULL_TXOP); @@ -2118,7 +2117,7 @@ wpi_mrr_setup(struct wpi_softc *sc) */ int wpi_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, - const struct ieee80211_key *k) + struct ieee80211_key *k) { struct wpi_softc *sc = ic->ic_softc; struct wpi_node_info node; diff --git a/sys/dev/usb/if_ral.c b/sys/dev/usb/if_ral.c index d1ebf3024e6..f125f66eb12 100644 --- a/sys/dev/usb/if_ral.c +++ b/sys/dev/usb/if_ral.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_ral.c,v 1.102 2007/10/11 18:33:14 deraadt Exp $ */ +/* $OpenBSD: if_ral.c,v 1.103 2008/04/16 18:32:15 damien Exp $ */ /*- * Copyright (c) 2005, 2006 @@ -301,7 +301,8 @@ ural_attach(struct device *parent, struct device *self, void *aux) IEEE80211_C_TXPMGT | /* tx power management */ IEEE80211_C_SHPREAMBLE | /* short preamble supported */ IEEE80211_C_SHSLOT | /* short slot time supported */ - IEEE80211_C_WEP; /* s/w WEP */ + IEEE80211_C_WEP | /* s/w WEP */ + IEEE80211_C_RSN; /* WPA/RSN */ /* set supported .11b and .11g rates */ ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b; @@ -1034,10 +1035,10 @@ int ural_tx_data(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) { struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = &ic->ic_if; struct ural_tx_desc *desc; struct ural_tx_data *data; struct ieee80211_frame *wh; + struct ieee80211_key *k; uint32_t flags = RAL_TX_NEWSEQ; uint16_t dur; usbd_status error; @@ -1045,9 +1046,10 @@ ural_tx_data(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) wh = mtod(m0, struct ieee80211_frame *); - if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - m0 = ieee80211_wep_crypt(ifp, m0, 1); - if (m0 == NULL) + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_get_txkey(ic, wh, ni); + + if ((m0 = ieee80211_encrypt(ic, m0, k)) == NULL) return ENOBUFS; /* packet header may have moved, reset our local pointer */ diff --git a/sys/dev/usb/if_rum.c b/sys/dev/usb/if_rum.c index 8864c54f7c7..ae18d4ee606 100644 --- a/sys/dev/usb/if_rum.c +++ b/sys/dev/usb/if_rum.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_rum.c,v 1.70 2008/04/01 13:43:53 jsg Exp $ */ +/* $OpenBSD: if_rum.c,v 1.71 2008/04/16 18:32:15 damien Exp $ */ /*- * Copyright (c) 2005-2007 Damien Bergamini <damien.bergamini@free.fr> @@ -362,7 +362,8 @@ rum_attach(struct device *parent, struct device *self, void *aux) IEEE80211_C_TXPMGT | /* tx power management */ IEEE80211_C_SHPREAMBLE | /* short preamble supported */ IEEE80211_C_SHSLOT | /* short slot time supported */ - IEEE80211_C_WEP; /* s/w WEP */ + IEEE80211_C_WEP | /* s/w WEP */ + IEEE80211_C_RSN; /* WPA/RSN */ if (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_5226) { /* set supported .11a rates */ @@ -1048,10 +1049,10 @@ int rum_tx_data(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) { struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = &ic->ic_if; struct rum_tx_desc *desc; struct rum_tx_data *data; struct ieee80211_frame *wh; + struct ieee80211_key *k; uint32_t flags = 0; uint16_t dur; usbd_status error; @@ -1059,9 +1060,10 @@ rum_tx_data(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) wh = mtod(m0, struct ieee80211_frame *); - if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - m0 = ieee80211_wep_crypt(ifp, m0, 1); - if (m0 == NULL) + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_get_txkey(ic, wh, ni); + + if ((m0 = ieee80211_encrypt(ic, m0, k)) == NULL) return ENOBUFS; /* packet header may have moved, reset our local pointer */ diff --git a/sys/dev/usb/if_upgt.c b/sys/dev/usb/if_upgt.c index 3ae7fe44d3e..e2f368a9ba2 100644 --- a/sys/dev/usb/if_upgt.c +++ b/sys/dev/usb/if_upgt.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_upgt.c,v 1.34 2008/02/16 21:56:43 mglocker Exp $ */ +/* $OpenBSD: if_upgt.c,v 1.35 2008/04/16 18:32:15 damien Exp $ */ /* * Copyright (c) 2007 Marcus Glocker <mglocker@openbsd.org> @@ -401,7 +401,8 @@ upgt_attach_hook(void *arg) IEEE80211_C_MONITOR | IEEE80211_C_SHPREAMBLE | IEEE80211_C_SHSLOT | - IEEE80211_C_WEP; + IEEE80211_C_WEP | + IEEE80211_C_RSN; ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b; ic->ic_sup_rates[IEEE80211_MODE_11G] = ieee80211_std_rateset_11g; @@ -1507,8 +1508,8 @@ upgt_tx_task(void *arg) { struct upgt_softc *sc = arg; struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = &ic->ic_if; struct ieee80211_frame *wh; + struct ieee80211_key *k; struct upgt_lmac_mem *mem; struct upgt_lmac_tx_desc *txdesc; struct mbuf *m; @@ -1533,14 +1534,16 @@ upgt_tx_task(void *arg) addr = data_tx->addr + UPGT_MEMSIZE_FRAME_HEAD; /* - * Software WEP. + * Software crypto. */ wh = mtod(m, struct ieee80211_frame *); - if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - m = ieee80211_wep_crypt(ifp, m, 1); - if (m == NULL) + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_get_txkey(ic, wh, ic->ic_bss); + + if ((m = ieee80211_encrypt(ic, m, k)) == NULL) return; + /* in case packet header moved, reset pointer */ wh = mtod(m, struct ieee80211_frame *); } diff --git a/sys/dev/usb/if_zyd.c b/sys/dev/usb/if_zyd.c index a01057d99f3..9b9fb3a758f 100644 --- a/sys/dev/usb/if_zyd.c +++ b/sys/dev/usb/if_zyd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_zyd.c,v 1.66 2007/12/07 05:05:02 deraadt Exp $ */ +/* $OpenBSD: if_zyd.c,v 1.67 2008/04/16 18:32:15 damien Exp $ */ /*- * Copyright (c) 2006 by Damien Bergamini <damien.bergamini@free.fr> @@ -369,7 +369,8 @@ zyd_complete_attach(struct zyd_softc *sc) IEEE80211_C_MONITOR | /* monitor mode supported */ IEEE80211_C_TXPMGT | /* tx power management */ IEEE80211_C_SHPREAMBLE | /* short preamble supported */ - IEEE80211_C_WEP; /* s/w WEP */ + IEEE80211_C_WEP | /* s/w WEP */ + IEEE80211_C_RSN; /* WPA/RSN */ /* set supported .11b and .11g rates */ ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b; @@ -2093,15 +2094,17 @@ zyd_tx_data(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) struct zyd_tx_desc *desc; struct zyd_tx_data *data; struct ieee80211_frame *wh; + struct ieee80211_key *k; int xferlen, totlen, rate; uint16_t pktlen; usbd_status error; wh = mtod(m0, struct ieee80211_frame *); - if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - m0 = ieee80211_wep_crypt(ifp, m0, 1); - if (m0 == NULL) + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_get_txkey(ic, wh, ni); + + if ((m0 = ieee80211_encrypt(ic, m0, k)) == NULL) return ENOBUFS; /* packet header may have moved, reset our local pointer */ diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c index c61325065b0..5218138a16d 100644 --- a/sys/net80211/ieee80211.c +++ b/sys/net80211/ieee80211.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211.c,v 1.29 2007/11/17 14:05:01 damien Exp $ */ +/* $OpenBSD: ieee80211.c,v 1.30 2008/04/16 18:32:15 damien Exp $ */ /* $NetBSD: ieee80211.c,v 1.19 2004/06/06 05:45:29 dyoung Exp $ */ /*- @@ -148,6 +148,18 @@ ieee80211_ifattach(struct ifnet *ifp) ic->ic_bmisstimeout = 7*ic->ic_lintval; /* default 7 beacons */ ic->ic_dtim_period = 1; /* all TIMs are DTIMs */ + if (ic->ic_caps & IEEE80211_C_RSN) { + ic->ic_rsnprotos = + IEEE80211_PROTO_WPA | IEEE80211_PROTO_RSN; + ic->ic_rsnakms = + IEEE80211_AKM_PSK | IEEE80211_AKM_IEEE8021X; + ic->ic_rsnciphers = + IEEE80211_CIPHER_TKIP | IEEE80211_CIPHER_CCMP; + ic->ic_rsngroupcipher = IEEE80211_CIPHER_TKIP; + } + ic->ic_set_key = ieee80211_set_key; + ic->ic_delete_key = ieee80211_delete_key; + LIST_INSERT_HEAD(&ieee80211com_head, ic, ic_list); ieee80211_node_attach(ifp); ieee80211_proto_attach(ifp); @@ -993,4 +1005,3 @@ ieee80211_plcp2rate(u_int8_t plcp, enum ieee80211_phymode mode) return 0; } - diff --git a/sys/net80211/ieee80211.h b/sys/net80211/ieee80211.h index 3ed7d8ff17b..8914594ed74 100644 --- a/sys/net80211/ieee80211.h +++ b/sys/net80211/ieee80211.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211.h,v 1.34 2007/08/29 19:57:17 damien Exp $ */ +/* $OpenBSD: ieee80211.h,v 1.35 2008/04/16 18:32:15 damien Exp $ */ /* $NetBSD: ieee80211.h,v 1.6 2004/04/30 23:51:53 dyoung Exp $ */ /*- @@ -466,14 +466,16 @@ enum { IEEE80211_REASON_IE_INVALID = 13, IEEE80211_REASON_MIC_FAILURE = 14, - + IEEE80211_REASON_4WAY_TIMEOUT = 15, + IEEE80211_REASON_GROUP_TIMEOUT = 16, + IEEE80211_REASON_RSN_DIFFERENT_IE = 17, IEEE80211_REASON_BAD_GROUP_CIPHER = 18, IEEE80211_REASON_BAD_PAIRWISE_CIPHER = 19, IEEE80211_REASON_BAD_AKMP = 20, IEEE80211_REASON_RSN_IE_VER_UNSUP = 21, IEEE80211_REASON_RSN_IE_BAD_CAP = 22, - IEEE80211_REASON_CIPHER_REJ = 24 + IEEE80211_REASON_CIPHER_REJ_POLICY = 24 }; /* @@ -497,7 +499,15 @@ enum { IEEE80211_STATUS_TOO_MANY_STATIONS = 22, IEEE80211_STATUS_RATES = 23, IEEE80211_STATUS_SHORTSLOT_REQUIRED = 25, - IEEE80211_STATUS_DSSSOFDM_REQUIRED = 26 + IEEE80211_STATUS_DSSSOFDM_REQUIRED = 26, + + IEEE80211_STATUS_IE_INVALID = 40, + IEEE80211_STATUS_BAD_GROUP_CIPHER = 41, + IEEE80211_STATUS_BAD_PAIRWISE_CIPHER = 42, + IEEE80211_STATUS_BAD_AKMP = 43, + IEEE80211_STATUS_RSN_IE_VER_UNSUP = 44, + + IEEE80211_STATUS_CIPHER_REJ_POLICY = 46, }; #define IEEE80211_WEP_KEYLEN 5 /* 40bit */ @@ -630,7 +640,7 @@ struct ieee80211_eapol_key { /* IEEE Std 802.1X-2004, 7.6.1 */ #define EAPOL_KEY_DESC_RC4 1 /* deprecated */ #define EAPOL_KEY_DESC_IEEE80211 2 -#define EAPOL_KEY_DESC_WPA1 254 /* non-standard WPA1 */ +#define EAPOL_KEY_DESC_WPA 254 /* non-standard WPA */ u_int8_t info[2]; #define EAPOL_KEY_VERSION_MASK 0x7 diff --git a/sys/net80211/ieee80211_crypto.c b/sys/net80211/ieee80211_crypto.c index 219bc7b6411..7147980a019 100644 --- a/sys/net80211/ieee80211_crypto.c +++ b/sys/net80211/ieee80211_crypto.c @@ -1,37 +1,21 @@ -/* $OpenBSD: ieee80211_crypto.c,v 1.36 2007/09/11 19:03:33 damien Exp $ */ -/* $NetBSD: ieee80211_crypto.c,v 1.5 2003/12/14 09:56:53 dyoung Exp $ */ +/* $OpenBSD: ieee80211_crypto.c,v 1.37 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 - * All rights reserved. + * Copyright (c) 2008 Damien Bergamini <damien.bergamini@free.fr> * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "bpfilter.h" - #include <sys/param.h> #include <sys/systm.h> #include <sys/mbuf.h> @@ -50,10 +34,6 @@ #include <net/if_arp.h> #include <net/if_llc.h> -#if NBPFILTER > 0 -#include <net/bpf.h> -#endif - #ifdef INET #include <netinet/in.h> #include <netinet/if_ether.h> @@ -67,33 +47,13 @@ #include <crypto/sha1.h> #include <crypto/rijndael.h> +/* similar to iovec except that it accepts const pointers */ struct vector { const void *base; size_t len; }; void ieee80211_crc_init(void); -u_int32_t ieee80211_crc_update(u_int32_t, const u_int8_t *, int); -struct mbuf *ieee80211_ccmp_encrypt(struct ieee80211com *, struct mbuf *, - struct ieee80211_key *); -struct mbuf *ieee80211_ccmp_decrypt(struct ieee80211com *, struct mbuf *, - struct ieee80211_key *); -struct mbuf *ieee80211_tkip_encrypt(struct ieee80211com *, struct mbuf *, - struct ieee80211_key *); -struct mbuf *ieee80211_tkip_decrypt(struct ieee80211com *, struct mbuf *, - struct ieee80211_key *); -void ieee80211_aes_key_wrap(const u_int8_t *, size_t, const u_int8_t *, - size_t, u_int8_t *); -int ieee80211_aes_key_unwrap(const u_int8_t *, size_t, const u_int8_t *, - u_int8_t *, size_t); -void ieee80211_hmac_md5_v(const struct vector *, int, const u_int8_t *, - size_t, u_int8_t digest[]); -void ieee80211_hmac_md5(const u_int8_t *, size_t, const u_int8_t *, size_t, - u_int8_t digest[]); -void ieee80211_hmac_sha1_v(const struct vector *, int, const u_int8_t *, - size_t, u_int8_t digest[]); -void ieee80211_hmac_sha1(const u_int8_t *, size_t, const u_int8_t *, size_t, - u_int8_t digest[]); void ieee80211_prf(const u_int8_t *, size_t, struct vector *, int, u_int8_t *, size_t); void ieee80211_derive_pmkid(const u_int8_t *, size_t, const u_int8_t *, @@ -104,31 +64,94 @@ void ieee80211_derive_gtk(const u_int8_t *, size_t, const u_int8_t *, void ieee80211_crypto_attach(struct ifnet *ifp) { - struct ieee80211com *ic = (void *)ifp; - - ieee80211_crc_init(); - - /* initialize 256-bit global key counter to a random value */ - get_random_bytes(ic->ic_globalcnt, EAPOL_KEY_NONCE_LEN); + ieee80211_crc_init(); /* XXX only once? */ } void ieee80211_crypto_detach(struct ifnet *ifp) { struct ieee80211com *ic = (void *)ifp; + int i; + + /* clear all keys from memory */ + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + if (ic->ic_nw_keys[i].k_cipher != IEEE80211_CIPHER_NONE) + (*ic->ic_delete_key)(ic, NULL, &ic->ic_nw_keys[i]); + memset(&ic->ic_nw_keys[i], 0, sizeof(struct ieee80211_key)); + } + memset(ic->ic_psk, 0, IEEE80211_PMK_LEN); +} + +int +ieee80211_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_key *k) +{ + int error; + + switch (k->k_cipher) { + case IEEE80211_CIPHER_WEP40: + case IEEE80211_CIPHER_WEP104: + error = ieee80211_wep_set_key(ic, k); + break; + case IEEE80211_CIPHER_TKIP: + error = ieee80211_tkip_set_key(ic, k); + break; + case IEEE80211_CIPHER_CCMP: + error = ieee80211_ccmp_set_key(ic, k); + break; + default: + /* should not get there */ + error = EINVAL; + } + return error; +} - if (ic->ic_wep_ctx != NULL) { - free(ic->ic_wep_ctx, M_DEVBUF); - ic->ic_wep_ctx = NULL; +void +ieee80211_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_key *k) +{ + switch (k->k_cipher) { + case IEEE80211_CIPHER_WEP40: + case IEEE80211_CIPHER_WEP104: + ieee80211_wep_delete_key(ic, k); + break; + case IEEE80211_CIPHER_TKIP: + ieee80211_tkip_delete_key(ic, k); + break; + case IEEE80211_CIPHER_CCMP: + ieee80211_ccmp_delete_key(ic, k); + break; + default: + /* should not get there */ + break; } + memset(k, 0, sizeof(*k)); /* XXX */ +} + +/* + * Retrieve the pairwise master key configured for a given node. + * When PSK AKMP is in use, the pairwise master key is the pre-shared key + * and the node is not used. + */ +const u_int8_t * +ieee80211_get_pmk(struct ieee80211com *ic, struct ieee80211_node *ni, + const u_int8_t *pmkid) +{ + if (ni->ni_rsnakms == IEEE80211_AKM_PSK) + return ic->ic_psk; /* the PMK is the PSK */ + + /* XXX find the PMK in the PMKSA cache using the PMKID */ + + return NULL; /* not yet supported */ } struct ieee80211_key * ieee80211_get_txkey(struct ieee80211com *ic, const struct ieee80211_frame *wh, struct ieee80211_node *ni) { - if (IEEE80211_IS_MULTICAST(wh->i_addr1) || - ni->ni_pairwise_cipher == IEEE80211_CIPHER_USEGROUP) + if (!(ic->ic_flags & IEEE80211_F_RSNON) || + IEEE80211_IS_MULTICAST(wh->i_addr1) || + ni->ni_rsncipher == IEEE80211_CIPHER_USEGROUP) return &ic->ic_nw_keys[ic->ic_wep_txkey]; return &ni->ni_pairwise_key; } @@ -140,7 +163,7 @@ ieee80211_encrypt(struct ieee80211com *ic, struct mbuf *m0, switch (k->k_cipher) { case IEEE80211_CIPHER_WEP40: case IEEE80211_CIPHER_WEP104: - m0 = ieee80211_wep_crypt(&ic->ic_if, m0, 1); + m0 = ieee80211_wep_encrypt(ic, m0, k); break; case IEEE80211_CIPHER_TKIP: m0 = ieee80211_tkip_encrypt(ic, m0, k); @@ -165,10 +188,12 @@ ieee80211_decrypt(struct ieee80211com *ic, struct mbuf *m0, /* select the key for decryption */ wh = mtod(m0, struct ieee80211_frame *); - if (IEEE80211_IS_MULTICAST(wh->i_addr1) || - ni->ni_pairwise_cipher == IEEE80211_CIPHER_USEGROUP) { + if (!(ic->ic_flags & IEEE80211_F_RSNON) || + IEEE80211_IS_MULTICAST(wh->i_addr1) || + ni->ni_rsncipher == IEEE80211_CIPHER_USEGROUP) { + /* XXX check length! */ int hdrlen = ieee80211_get_hdrlen(wh); - u_int8_t *ivp = (u_int8_t *)wh + hdrlen; + const u_int8_t *ivp = (u_int8_t *)wh + hdrlen; /* key identifier is always located at the same index */ int kid = ivp[IEEE80211_WEP_IVLEN] >> 6; k = &ic->ic_nw_keys[kid]; @@ -178,7 +203,7 @@ ieee80211_decrypt(struct ieee80211com *ic, struct mbuf *m0, switch (k->k_cipher) { case IEEE80211_CIPHER_WEP40: case IEEE80211_CIPHER_WEP104: - m0 = ieee80211_wep_crypt(&ic->ic_if, m0, 0); + m0 = ieee80211_wep_decrypt(ic, m0, k); break; case IEEE80211_CIPHER_TKIP: m0 = ieee80211_tkip_decrypt(ic, m0, k); @@ -187,368 +212,13 @@ ieee80211_decrypt(struct ieee80211com *ic, struct mbuf *m0, m0 = ieee80211_ccmp_decrypt(ic, m0, k); break; default: - /* should not get there */ + /* key not defined */ m_freem(m0); m0 = NULL; } return m0; } -struct mbuf * -ieee80211_ccmp_encrypt(struct ieee80211com *ic, struct mbuf *m0, - struct ieee80211_key *k) -{ - struct ieee80211_frame *wh; - u_int8_t *ivp; - int hdrlen; - - wh = mtod(m0, struct ieee80211_frame *); - hdrlen = ieee80211_get_hdrlen(wh); - M_PREPEND(m0, IEEE80211_CCMP_HDRLEN, M_NOWAIT); - if (m0 == NULL) - return m0; - wh = mtod(m0, struct ieee80211_frame *); - ovbcopy(mtod(m0, u_int8_t *) + IEEE80211_CCMP_HDRLEN, wh, hdrlen); - ivp = (u_int8_t *)wh + hdrlen; - - k->k_tsc++; /* increment the 48-bit PN */ - ivp[0] = k->k_tsc; /* PN0 */ - ivp[1] = k->k_tsc >> 8; /* PN1 */ - ivp[2] = 0; /* Rsvd */ - ivp[3] = k->k_id << 6 | IEEE80211_WEP_EXTIV; /* KeyID | ExtIV */ - ivp[4] = k->k_tsc >> 16; /* PN2 */ - ivp[5] = k->k_tsc >> 24; /* PN3 */ - ivp[6] = k->k_tsc >> 32; /* PN4 */ - ivp[7] = k->k_tsc >> 40; /* PN5 */ - - /* XXX encrypt payload if HW encryption not supported */ - - return m0; -} - -struct mbuf * -ieee80211_ccmp_decrypt(struct ieee80211com *ic, struct mbuf *m0, - struct ieee80211_key *k) -{ - struct ieee80211_frame *wh; - u_int64_t pn; - u_int8_t *ivp; - int hdrlen; - - wh = mtod(m0, struct ieee80211_frame *); - hdrlen = ieee80211_get_hdrlen(wh); - ivp = (u_int8_t *)wh + hdrlen; - - /* check that ExtIV bit is be set */ - if (!(ivp[3] & IEEE80211_WEP_EXTIV)) { - m_freem(m0); - return NULL; - } - /* extract the 48-bit PN from the CCMP header */ - pn = (u_int64_t)ivp[0] | - (u_int64_t)ivp[1] << 8 | - (u_int64_t)ivp[4] << 16 | - (u_int64_t)ivp[5] << 24 | - (u_int64_t)ivp[6] << 32 | - (u_int64_t)ivp[7] << 40; - /* NB: the keys are refreshed, we'll never overflow the 48 bits */ - if (pn <= k->k_rsc) { - /* replayed frame, discard */ - /* XXX statistics */ - m_freem(m0); - return NULL; - } - - /* XXX decrypt payload if HW encryption not supported */ - - ovbcopy(mtod(m0, u_int8_t *), - mtod(m0, u_int8_t *) + IEEE80211_CCMP_HDRLEN, hdrlen); - m_adj(m0, IEEE80211_CCMP_HDRLEN); - m_adj(m0, -IEEE80211_CCMP_MICLEN); - - /* update last seen packet number */ - k->k_rsc = pn; - - return m0; -} - -struct mbuf * -ieee80211_tkip_encrypt(struct ieee80211com *ic, struct mbuf *m0, - struct ieee80211_key *k) -{ - struct ieee80211_frame *wh; - u_int8_t *ivp; - int hdrlen; - - wh = mtod(m0, struct ieee80211_frame *); - hdrlen = ieee80211_get_hdrlen(wh); - M_PREPEND(m0, IEEE80211_TKIP_HDRLEN, M_NOWAIT); - if (m0 == NULL) - return m0; - wh = mtod(m0, struct ieee80211_frame *); - ovbcopy(mtod(m0, u_int8_t *) + IEEE80211_TKIP_HDRLEN, wh, hdrlen); - ivp = (u_int8_t *)wh + hdrlen; - - ivp[0] = k->k_tsc >> 8; /* TSC1 */ - /* WEP Seed = (TSC1 | 0x20) & 0x7f (see 8.3.2.2) */ - ivp[1] = (ivp[0] | 0x20) & 0x7f; - ivp[2] = k->k_tsc; /* TSC0 */ - ivp[3] = k->k_id << 6 | IEEE80211_WEP_EXTIV; /* KeyID | ExtIV */ - ivp[4] = k->k_tsc >> 16; /* TSC2 */ - ivp[5] = k->k_tsc >> 24; /* TSC3 */ - ivp[6] = k->k_tsc >> 32; /* TSC4 */ - ivp[7] = k->k_tsc >> 40; /* TSC5 */ - - /* XXX encrypt payload if HW encryption not supported */ - - k->k_tsc++; /* increment the 48-bit TSC */ - - return m0; -} - -struct mbuf * -ieee80211_tkip_decrypt(struct ieee80211com *ic, struct mbuf *m0, - struct ieee80211_key *k) -{ - struct ieee80211_frame *wh; - u_int64_t tsc; - u_int8_t *ivp; - int hdrlen; - - wh = mtod(m0, struct ieee80211_frame *); - hdrlen = ieee80211_get_hdrlen(wh); - ivp = (u_int8_t *)wh + hdrlen; - - /* check that ExtIV bit is be set */ - if (!(ivp[3] & IEEE80211_WEP_EXTIV)) { - m_freem(m0); - return NULL; - } - /* extract the 48-bit TSC from the TKIP header */ - tsc = (u_int64_t)ivp[2] | - (u_int64_t)ivp[0] << 8 | - (u_int64_t)ivp[4] << 16 | - (u_int64_t)ivp[5] << 24 | - (u_int64_t)ivp[6] << 32 | - (u_int64_t)ivp[7] << 40; - /* NB: the keys are refreshed, we'll never overflow the 48 bits */ - if (tsc <= k->k_rsc) { - /* replayed frame, discard */ - /* XXX statistics */ - m_freem(m0); - return NULL; - } - - /* XXX decrypt payload if HW encryption not supported */ - - ovbcopy(mtod(m0, u_int8_t *), - mtod(m0, u_int8_t *) + IEEE80211_TKIP_HDRLEN, hdrlen); - m_adj(m0, IEEE80211_TKIP_HDRLEN); - m_adj(m0, -IEEE80211_TKIP_ICVLEN); - - /* update last seen packet number */ - k->k_rsc = tsc; - - return m0; -} - -/* Round up to a multiple of IEEE80211_WEP_KEYLEN + IEEE80211_WEP_IVLEN */ -#define klen_round(x) \ - (((x) + (IEEE80211_WEP_KEYLEN + IEEE80211_WEP_IVLEN - 1)) & \ - ~(IEEE80211_WEP_KEYLEN + IEEE80211_WEP_IVLEN - 1)) - -struct mbuf * -ieee80211_wep_crypt(struct ifnet *ifp, struct mbuf *m0, int txflag) -{ - struct ieee80211com *ic = (void *)ifp; - struct mbuf *m, *n, *n0; - struct ieee80211_frame *wh; - int i, left, len, moff, noff, kid; - u_int32_t iv, crc; - u_int8_t *ivp; - void *ctx; - u_int8_t keybuf[klen_round(IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE)]; - u_int8_t crcbuf[IEEE80211_WEP_CRCLEN]; - - n0 = NULL; - if ((ctx = ic->ic_wep_ctx) == NULL) { - ctx = malloc(sizeof(struct rc4_ctx), M_DEVBUF, M_NOWAIT); - if (ctx == NULL) { - ic->ic_stats.is_crypto_nomem++; - goto fail; - } - ic->ic_wep_ctx = ctx; - } - m = m0; - left = m->m_pkthdr.len; - MGET(n, M_DONTWAIT, m->m_type); - n0 = n; - if (n == NULL) { - if (txflag) - ic->ic_stats.is_tx_nombuf++; - else - ic->ic_stats.is_rx_nombuf++; - goto fail; - } - M_DUP_PKTHDR(n, m); - len = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN; - if (txflag) { - n->m_pkthdr.len += len; - } else { - n->m_pkthdr.len -= len; - left -= len; - } - n->m_len = MHLEN; - if (n->m_pkthdr.len >= MINCLSIZE) { - MCLGET(n, M_DONTWAIT); - if (n->m_flags & M_EXT) - n->m_len = n->m_ext.ext_size; - } - wh = mtod(m, struct ieee80211_frame *); - len = ieee80211_get_hdrlen(wh); - memcpy(mtod(n, caddr_t), wh, len); - wh = mtod(n, struct ieee80211_frame *); - left -= len; - moff = len; - noff = len; - if (txflag) { - kid = ic->ic_wep_txkey; - wh->i_fc[1] |= IEEE80211_FC1_WEP; - iv = ic->ic_iv ? ic->ic_iv : arc4random(); - /* - * Skip 'bad' IVs from Fluhrer/Mantin/Shamir: - * (B, 255, N) with 3 <= B < 8 - */ - if (iv >= 0x03ff00 && - (iv & 0xf8ff00) == 0x00ff00) - iv += 0x000100; - ic->ic_iv = iv + 1; - /* put iv in little endian to prepare 802.11i */ - ivp = mtod(n, u_int8_t *) + noff; - for (i = 0; i < IEEE80211_WEP_IVLEN; i++) { - ivp[i] = iv & 0xff; - iv >>= 8; - } - ivp[IEEE80211_WEP_IVLEN] = kid << 6; /* pad and keyid */ - noff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN; - } else { - wh->i_fc[1] &= ~IEEE80211_FC1_WEP; - ivp = mtod(m, u_int8_t *) + moff; - kid = ivp[IEEE80211_WEP_IVLEN] >> 6; - moff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN; - } - - /* - * Copy the IV and the key material. The input key has been padded - * with zeros by the ioctl. The output key buffer length is rounded - * to a multiple of 64bit to allow variable length keys padded by - * zeros. - */ - bzero(&keybuf, sizeof(keybuf)); - memcpy(keybuf, ivp, IEEE80211_WEP_IVLEN); - memcpy(keybuf + IEEE80211_WEP_IVLEN, ic->ic_nw_keys[kid].k_key, - ic->ic_nw_keys[kid].k_len); - len = klen_round(IEEE80211_WEP_IVLEN + ic->ic_nw_keys[kid].k_len); - rc4_keysetup(ctx, keybuf, len); - - /* encrypt with calculating CRC */ - crc = ~0; - while (left > 0) { - len = m->m_len - moff; - if (len == 0) { - m = m->m_next; - moff = 0; - continue; - } - if (len > n->m_len - noff) { - len = n->m_len - noff; - if (len == 0) { - MGET(n->m_next, M_DONTWAIT, n->m_type); - if (n->m_next == NULL) { - if (txflag) - ic->ic_stats.is_tx_nombuf++; - else - ic->ic_stats.is_rx_nombuf++; - goto fail; - } - n = n->m_next; - n->m_len = MLEN; - if (left >= MINCLSIZE) { - MCLGET(n, M_DONTWAIT); - if (n->m_flags & M_EXT) - n->m_len = n->m_ext.ext_size; - } - noff = 0; - continue; - } - } - if (len > left) - len = left; - rc4_crypt(ctx, mtod(m, caddr_t) + moff, - mtod(n, caddr_t) + noff, len); - if (txflag) - crc = ieee80211_crc_update(crc, - mtod(m, u_int8_t *) + moff, len); - else - crc = ieee80211_crc_update(crc, - mtod(n, u_int8_t *) + noff, len); - left -= len; - moff += len; - noff += len; - } - crc = ~crc; - if (txflag) { - *(u_int32_t *)crcbuf = htole32(crc); - if (n->m_len >= noff + sizeof(crcbuf)) - n->m_len = noff + sizeof(crcbuf); - else { - n->m_len = noff; - MGET(n->m_next, M_DONTWAIT, n->m_type); - if (n->m_next == NULL) { - ic->ic_stats.is_tx_nombuf++; - goto fail; - } - n = n->m_next; - n->m_len = sizeof(crcbuf); - noff = 0; - } - rc4_crypt(ctx, crcbuf, mtod(n, caddr_t) + noff, - sizeof(crcbuf)); - } else { - n->m_len = noff; - for (noff = 0; noff < sizeof(crcbuf); noff += len) { - len = sizeof(crcbuf) - noff; - if (len > m->m_len - moff) - len = m->m_len - moff; - if (len > 0) - rc4_crypt(ctx, mtod(m, caddr_t) + moff, - crcbuf + noff, len); - m = m->m_next; - moff = 0; - } - if (crc != letoh32(*(u_int32_t *)crcbuf)) { -#ifdef IEEE80211_DEBUG - if (ieee80211_debug) { - printf("%s: decrypt CRC error\n", - ifp->if_xname); - if (ieee80211_debug > 1) - ieee80211_dump_pkt(n0->m_data, - n0->m_len, -1, -1); - } -#endif - ic->ic_stats.is_rx_decryptcrc++; - goto fail; - } - } - m_freem(m0); - return n0; - - fail: - m_freem(m0); - m_freem(n0); - return NULL; -} - /* * CRC 32 -- routine from RFC 2083 */ @@ -591,13 +261,13 @@ ieee80211_crc_update(u_int32_t crc, const u_int8_t *buf, int len) } /* - * AES Key Wrap Algorithm (see RFC 3394). + * AES Key Wrap (see RFC 3394). */ static const u_int8_t aes_key_wrap_iv[8] = { 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6 }; -void -ieee80211_aes_key_wrap(const u_int8_t *kek, size_t kek_len, const u_int8_t *pt, +static void +aes_key_wrap(const u_int8_t *kek, size_t kek_len, const u_int8_t *pt, size_t len, u_int8_t *ct) { rijndael_ctx ctx; @@ -628,9 +298,9 @@ ieee80211_aes_key_wrap(const u_int8_t *kek, size_t kek_len, const u_int8_t *pt, } } -int -ieee80211_aes_key_unwrap(const u_int8_t *kek, size_t kek_len, - const u_int8_t *ct, u_int8_t *pt, size_t len) +static int +aes_key_unwrap(const u_int8_t *kek, size_t kek_len, const u_int8_t *ct, + u_int8_t *pt, size_t len) { rijndael_ctx ctx; u_int8_t a[8], *r, b[16]; @@ -660,8 +330,11 @@ ieee80211_aes_key_unwrap(const u_int8_t *kek, size_t kek_len, return memcmp(a, aes_key_wrap_iv, 8) != 0; } -void -ieee80211_hmac_md5_v(const struct vector *vec, int vcnt, const u_int8_t *key, +/* + * HMAC-MD5 (see RFC 2104). + */ +static void +hmac_md5(const struct vector *vec, int vcnt, const u_int8_t *key, size_t key_len, u_int8_t digest[MD5_DIGEST_LENGTH]) { MD5_CTX ctx; @@ -671,7 +344,7 @@ ieee80211_hmac_md5_v(const struct vector *vec, int vcnt, const u_int8_t *key, if (key_len > MD5_BLOCK_LENGTH) { MD5Init(&ctx); - MD5Update(&ctx, (u_int8_t *)key, key_len); + MD5Update(&ctx, key, key_len); MD5Final(tk, &ctx); key = tk; @@ -686,7 +359,7 @@ ieee80211_hmac_md5_v(const struct vector *vec, int vcnt, const u_int8_t *key, MD5Init(&ctx); MD5Update(&ctx, k_pad, MD5_BLOCK_LENGTH); for (i = 0; i < vcnt; i++) - MD5Update(&ctx, (u_int8_t *)vec[i].base, vec[i].len); + MD5Update(&ctx, vec[i].base, vec[i].len); MD5Final(digest, &ctx); bzero(k_pad, sizeof k_pad); @@ -700,18 +373,11 @@ ieee80211_hmac_md5_v(const struct vector *vec, int vcnt, const u_int8_t *key, MD5Final(digest, &ctx); } -void -ieee80211_hmac_md5(const u_int8_t *text, size_t text_len, const u_int8_t *key, - size_t key_len, u_int8_t digest[MD5_DIGEST_LENGTH]) -{ - struct vector vec; - vec.base = text; - vec.len = text_len; - ieee80211_hmac_md5_v(&vec, 1, key, key_len, digest); -} - -void -ieee80211_hmac_sha1_v(const struct vector *vec, int vcnt, const u_int8_t *key, +/* + * HMAC-SHA1 (see RFC 2104). + */ +static void +hmac_sha1(const struct vector *vec, int vcnt, const u_int8_t *key, size_t key_len, u_int8_t digest[SHA1_DIGEST_LENGTH]) { SHA1_CTX ctx; @@ -750,16 +416,6 @@ ieee80211_hmac_sha1_v(const struct vector *vec, int vcnt, const u_int8_t *key, SHA1Final(digest, &ctx); } -void -ieee80211_hmac_sha1(const u_int8_t *text, size_t text_len, const u_int8_t *key, - size_t key_len, u_int8_t digest[SHA1_DIGEST_LENGTH]) -{ - struct vector vec; - vec.base = text; - vec.len = text_len; - ieee80211_hmac_sha1_v(&vec, 1, key, key_len, digest); -} - /* * SHA1-based Pseudo-Random Function (see 8.5.1.1). */ @@ -775,15 +431,15 @@ ieee80211_prf(const u_int8_t *key, size_t key_len, struct vector *vec, vec[vcnt].len = 1; vcnt++; - while (len > SHA1_DIGEST_LENGTH) { - ieee80211_hmac_sha1_v(vec, vcnt, key, key_len, output); + while (len >= SHA1_DIGEST_LENGTH) { + hmac_sha1(vec, vcnt, key, key_len, output); count++; output += SHA1_DIGEST_LENGTH; len -= SHA1_DIGEST_LENGTH; } if (len > 0) { - ieee80211_hmac_sha1_v(vec, vcnt, key, key_len, hash); + hmac_sha1(vec, vcnt, key, key_len, hash); /* truncate HMAC-SHA1 to len bytes */ memcpy(output, hash, len); } @@ -839,30 +495,11 @@ ieee80211_derive_pmkid(const u_int8_t *pmk, size_t pmk_len, const u_int8_t *aa, vec[2].base = spa; vec[2].len = IEEE80211_ADDR_LEN; - ieee80211_hmac_sha1_v(vec, 3, pmk, pmk_len, hash); + hmac_sha1(vec, 3, pmk, pmk_len, hash); /* use the first 128 bits of the HMAC-SHA1 */ memcpy(pmkid, hash, IEEE80211_PMKID_LEN); } -/* - * Derive Group Temporal Key (GTK) (see 8.5.1.3). - */ -void -ieee80211_derive_gtk(const u_int8_t *gmk, size_t gmk_len, const u_int8_t *aa, - const u_int8_t *gnonce, u_int8_t *gtk, size_t gtk_len) -{ - struct vector vec[4]; /* +1 for PRF */ - - vec[0].base = "Group key expansion"; - vec[0].len = 20; /* include trailing '\0' */ - vec[1].base = aa; - vec[1].len = IEEE80211_ADDR_LEN; - vec[2].base = gnonce; - vec[2].len = EAPOL_KEY_NONCE_LEN; - - ieee80211_prf(gmk, gmk_len, vec, 3, gtk, gtk_len); -} - /* unaligned big endian access */ #define BE_READ_2(p) \ ((u_int16_t) \ @@ -883,17 +520,17 @@ void ieee80211_eapol_key_mic(struct ieee80211_eapol_key *key, const u_int8_t *kck) { u_int8_t hash[SHA1_DIGEST_LENGTH]; - u_int16_t len, info; + struct vector vec; - len = BE_READ_2(key->len) + 4; - info = BE_READ_2(key->info); + vec.base = key; + vec.len = BE_READ_2(key->len) + 4; - switch (info & EAPOL_KEY_VERSION_MASK) { + switch (BE_READ_2(key->info) & EAPOL_KEY_VERSION_MASK) { case EAPOL_KEY_DESC_V1: - ieee80211_hmac_md5((u_int8_t *)key, len, kck, 16, key->mic); + hmac_md5(&vec, 1, kck, 16, key->mic); break; case EAPOL_KEY_DESC_V2: - ieee80211_hmac_sha1((u_int8_t *)key, len, kck, 16, hash); + hmac_sha1(&vec, 1, kck, 16, hash); /* truncate HMAC-SHA1 to its 128 MSBs */ memcpy(key->mic, hash, EAPOL_KEY_MIC_LEN); break; @@ -960,7 +597,7 @@ ieee80211_eapol_key_encrypt(struct ieee80211com *ic, memset(&data[len], 0, n - 1); len += n - 1; } - ieee80211_aes_key_wrap(kek, 16, data, len / 8, data); + aes_key_wrap(kek, 16, data, len / 8, data); len += 8; /* AES Key Wrap adds 8 bytes */ /* update key data length */ BE_WRITE_2(key->paylen, len); @@ -1004,7 +641,7 @@ ieee80211_eapol_key_decrypt(struct ieee80211_eapol_key *key, if (len < 16 + 8 || (len & 7) != 0) return 1; len -= 8; /* AES Key Wrap adds 8 bytes */ - return ieee80211_aes_key_unwrap(kek, 16, data, data, len / 8); + return aes_key_unwrap(kek, 16, data, data, len / 8); } return 1; /* unknown Key Descriptor Version */ @@ -1035,20 +672,14 @@ ieee80211_cipher_keylen(enum ieee80211_cipher cipher) */ void ieee80211_map_ptk(const struct ieee80211_ptk *ptk, - enum ieee80211_cipher cipher, struct ieee80211_key *k) + enum ieee80211_cipher cipher, u_int64_t rsc, struct ieee80211_key *k) { memset(k, 0, sizeof(*k)); k->k_cipher = cipher; k->k_flags = IEEE80211_KEY_TX; k->k_len = ieee80211_cipher_keylen(cipher); - if (cipher == IEEE80211_CIPHER_TKIP) { - memcpy(k->k_key, ptk->tk, 16); - /* use bits 128-191 as the Michael key for AA->SPA */ - memcpy(k->k_rxmic, &ptk->tk[16], 8); - /* use bits 192-255 as the Michael key for SPA->AA */ - memcpy(k->k_txmic, &ptk->tk[24], 8); - } else - memcpy(k->k_key, ptk->tk, k->k_len); + k->k_rsc[0] = rsc; + memcpy(k->k_key, ptk->tk, k->k_len); } /* @@ -1065,13 +696,6 @@ ieee80211_map_gtk(const u_int8_t *gtk, enum ieee80211_cipher cipher, int kid, if (txflag) k->k_flags |= IEEE80211_KEY_TX; k->k_len = ieee80211_cipher_keylen(cipher); - k->k_rsc = rsc; - if (cipher == IEEE80211_CIPHER_TKIP) { - memcpy(k->k_key, gtk, 16); - /* use bits 128-191 as the Michael key for AA->SPA */ - memcpy(k->k_rxmic, >k[16], 8); - /* use bits 192-255 as the Michael key for SPA->AA */ - memcpy(k->k_txmic, >k[24], 8); - } else - memcpy(k->k_key, gtk, k->k_len); + k->k_rsc[0] = rsc; + memcpy(k->k_key, gtk, k->k_len); } diff --git a/sys/net80211/ieee80211_crypto.h b/sys/net80211/ieee80211_crypto.h index fa47ab8f313..3447da8c0f5 100644 --- a/sys/net80211/ieee80211_crypto.h +++ b/sys/net80211/ieee80211_crypto.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_crypto.h,v 1.9 2007/08/23 16:50:30 damien Exp $ */ +/* $OpenBSD: ieee80211_crypto.h,v 1.10 2008/04/16 18:32:15 damien Exp $ */ /* $NetBSD: ieee80211_crypto.h,v 1.2 2003/09/14 01:14:55 dyoung Exp $ */ /*- @@ -50,7 +50,7 @@ enum ieee80211_cipher { }; /* - * 802.11i Authentication and Key Management. + * 802.11i Authentication and Key Management Protocols. */ enum ieee80211_akm { IEEE80211_AKM_NONE = 0x00000000, @@ -75,42 +75,73 @@ struct ieee80211_key { #define IEEE80211_KEY_GROUP 0x00000001 /* group key */ #define IEEE80211_KEY_TX 0x00000002 /* Tx+Rx */ - u_int64_t k_rsc; + u_int k_len; + u_int64_t k_rsc[IEEE80211_NUM_TID]; u_int64_t k_tsc; - int k_len; - u_int8_t k_key[IEEE80211_KEYBUF_SIZE]; - u_int8_t k_rxmic[IEEE80211_TKIP_MICLEN]; - u_int8_t k_txmic[IEEE80211_TKIP_MICLEN]; + u_int8_t k_key[32]; + void *k_priv; }; -/* pseudo-header used for TKIP MIC computation */ -struct ieee80211_tkip_frame { - u_int8_t i_da[IEEE80211_ADDR_LEN]; - u_int8_t i_sa[IEEE80211_ADDR_LEN]; - u_int8_t i_pri; - u_int8_t i_pad[3]; -} __packed; - /* forward references */ struct ieee80211com; struct ieee80211_node; extern void ieee80211_crypto_attach(struct ifnet *); extern void ieee80211_crypto_detach(struct ifnet *); + +extern const u_int8_t *ieee80211_get_pmk(struct ieee80211com *, + struct ieee80211_node *, const u_int8_t *); + + extern struct ieee80211_key *ieee80211_get_txkey(struct ieee80211com *, const struct ieee80211_frame *, struct ieee80211_node *); extern struct mbuf *ieee80211_encrypt(struct ieee80211com *, struct mbuf *, struct ieee80211_key *); extern struct mbuf *ieee80211_decrypt(struct ieee80211com *, struct mbuf *, struct ieee80211_node *); -extern struct mbuf *ieee80211_wep_crypt(struct ifnet *, struct mbuf *, int); + +u_int32_t ieee80211_crc_update(u_int32_t, const u_int8_t *, int); + +int ieee80211_set_key(struct ieee80211com *, struct ieee80211_node *, + struct ieee80211_key *); +void ieee80211_delete_key(struct ieee80211com *, struct ieee80211_node *, + struct ieee80211_key *); + +int ieee80211_wep_set_key(struct ieee80211com *, struct ieee80211_key *); +void ieee80211_wep_delete_key(struct ieee80211com *, struct ieee80211_key *); +struct mbuf * +ieee80211_wep_encrypt(struct ieee80211com *, struct mbuf *, + struct ieee80211_key *); +struct mbuf * +ieee80211_wep_decrypt(struct ieee80211com *, struct mbuf *, + struct ieee80211_key *); + +int ieee80211_tkip_set_key(struct ieee80211com *, struct ieee80211_key *); +void ieee80211_tkip_delete_key(struct ieee80211com *, struct ieee80211_key *); +struct mbuf *ieee80211_tkip_encrypt(struct ieee80211com *, struct mbuf *, + struct ieee80211_key *); +struct mbuf *ieee80211_tkip_decrypt(struct ieee80211com *, struct mbuf *, + struct ieee80211_key *); + +int ieee80211_ccmp_set_key(struct ieee80211com *, struct ieee80211_key *); +void ieee80211_ccmp_delete_key(struct ieee80211com *, struct ieee80211_key *); +struct mbuf *ieee80211_ccmp_encrypt(struct ieee80211com *, struct mbuf *, + struct ieee80211_key *); +struct mbuf *ieee80211_ccmp_decrypt(struct ieee80211com *, struct mbuf *, + struct ieee80211_key *); + +extern void ieee80211_tkip_mic(struct mbuf *, int, const u_int8_t *, + u_int8_t[IEEE80211_TKIP_MICLEN]); +extern void ieee80211_michael_mic_failure(struct ieee80211com *, u_int64_t); + extern void ieee80211_derive_ptk(const u_int8_t *, size_t, const u_int8_t *, const u_int8_t *, const u_int8_t *, const u_int8_t *, u_int8_t *, size_t); extern int ieee80211_cipher_keylen(enum ieee80211_cipher); extern void ieee80211_map_ptk(const struct ieee80211_ptk *, - enum ieee80211_cipher, struct ieee80211_key *); + enum ieee80211_cipher, u_int64_t, struct ieee80211_key *); extern void ieee80211_map_gtk(const u_int8_t *, enum ieee80211_cipher, int, int, u_int64_t, struct ieee80211_key *); + #endif /* _NET80211_IEEE80211_CRYPTO_H_ */ diff --git a/sys/net80211/ieee80211_crypto_ccmp.c b/sys/net80211/ieee80211_crypto_ccmp.c new file mode 100644 index 00000000000..c55e8928f35 --- /dev/null +++ b/sys/net80211/ieee80211_crypto_ccmp.c @@ -0,0 +1,455 @@ +/* $OpenBSD: ieee80211_crypto_ccmp.c,v 1.1 2008/04/16 18:32:15 damien Exp $ */ + +/*- + * Copyright (c) 2008 Damien Bergamini <damien.bergamini@free.fr> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/endian.h> +#include <sys/errno.h> +#include <sys/proc.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_arp.h> +#include <net/if_llc.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/if_ether.h> +#endif + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_crypto.h> + +#include <crypto/rijndael.h> + +/* CCMP software crypto context */ +struct ieee80211_ccmp_ctx { + rijndael_ctx rijndael; +}; + +/* + * Initialize software crypto context. This function can be overridden + * by drivers doing hardware crypto. + */ +int +ieee80211_ccmp_set_key(struct ieee80211com *ic, struct ieee80211_key *k) +{ + struct ieee80211_ccmp_ctx *ctx; + + ctx = malloc(sizeof(*ctx), M_DEVBUF, M_NOWAIT | M_ZERO); + if (ctx == NULL) + return ENOMEM; + rijndael_set_key_enc_only(&ctx->rijndael, k->k_key, 128); + k->k_priv = ctx; + return 0; +} + +void +ieee80211_ccmp_delete_key(struct ieee80211com *ic, struct ieee80211_key *k) +{ + if (k->k_priv != NULL) + free(k->k_priv, M_DEVBUF); + k->k_priv = NULL; +} + +/* + * Counter with CBC-MAC (CCM) - see RFC3610. + * CCMP uses the following CCM parameters: M = 8, L = 2 + */ +static void +ieee80211_ccmp_phase1(rijndael_ctx *ctx, const struct ieee80211_frame *wh, + u_int64_t pn, int lm, u_int8_t b[16], u_int8_t a[16], u_int8_t s0[16]) +{ + u_int8_t auth[32], nonce[13]; + u_int8_t *aad; + u_int8_t tid = 0; + int la, i; + + /* construct AAD (additional authentication data) */ + aad = &auth[2]; /* skip l(a), will be filled later */ + *aad++ = wh->i_fc[0] & ~IEEE80211_FC0_SUBTYPE_MASK; + /* NB: 'Protected' bit is already set in wh->i_fc[1] */ + /* 'Order' bit was added as part of 802.11n-Draft 2.0 */ + *aad++ = wh->i_fc[1] & ~(IEEE80211_FC1_RETRY | IEEE80211_FC1_ORDER | + IEEE80211_FC1_PWR_MGT | IEEE80211_FC1_MORE_DATA); + IEEE80211_ADDR_COPY(aad, wh->i_addr1); aad += IEEE80211_ADDR_LEN; + IEEE80211_ADDR_COPY(aad, wh->i_addr2); aad += IEEE80211_ADDR_LEN; + IEEE80211_ADDR_COPY(aad, wh->i_addr3); aad += IEEE80211_ADDR_LEN; + *aad++ = wh->i_seq[0] & ~0xf0; + *aad++ = 0; + if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == + IEEE80211_FC1_DIR_DSTODS) { + const struct ieee80211_frame_addr4 *wh4 = + (const struct ieee80211_frame_addr4 *)wh; + IEEE80211_ADDR_COPY(aad, wh4->i_addr4); + aad += IEEE80211_ADDR_LEN; + if ((wh->i_fc[0] & + (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_QOS)) == + (IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS)) { + const struct ieee80211_qosframe_addr4 *qwh4 = + (const struct ieee80211_qosframe_addr4 *)wh; + *aad++ = tid = qwh4->i_qos[0] & 0x0f; + *aad++ = 0; + } + } else if ((wh->i_fc[0] & + (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_QOS)) == + (IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS)) { + const struct ieee80211_qosframe *qwh = + (const struct ieee80211_qosframe *)wh; + *aad++ = tid = qwh->i_qos[0] & 0x0f; + *aad++ = 0; + } + + /* construct CCM nonce */ + nonce[0] = tid; + IEEE80211_ADDR_COPY(&nonce[1], wh->i_addr2); + nonce[7] = pn >> 40; /* PN5 */ + nonce[8] = pn >> 32; /* PN4 */ + nonce[9] = pn >> 24; /* PN3 */ + nonce[10] = pn >> 16; /* PN2 */ + nonce[11] = pn >> 8; /* PN1 */ + nonce[12] = pn; /* PN0 */ + + /* add 2 authentication blocks (including l(a) and padded AAD) */ + la = aad - &auth[2]; /* fill l(a) */ + auth[0] = la >> 8; + auth[1] = la & 0xff; + memset(aad, 0, 30 - la); /* pad AAD with zeros */ + + /* construct first block B_0 */ + b[0] = 89; /* Flags = 64*Adata + 8*((M-2)/2) + (L-1) */ + memcpy(&b[1], nonce, 13); + b[14] = lm >> 8; + b[15] = lm & 0xff; + rijndael_encrypt(ctx, b, b); + + for (i = 0; i < 16; i++) + b[i] ^= auth[i]; + rijndael_encrypt(ctx, b, b); + for (i = 0; i < 16; i++) + b[i] ^= auth[16 + i]; + rijndael_encrypt(ctx, b, b); + + /* construct S_0 */ + a[0] = 1; /* Flags = L' = (L-1) */ + memcpy(&a[1], nonce, 13); + a[14] = a[15] = 0; + rijndael_encrypt(ctx, a, s0); +} + +struct mbuf * +ieee80211_ccmp_encrypt(struct ieee80211com *ic, struct mbuf *m0, + struct ieee80211_key *k) +{ + struct ieee80211_ccmp_ctx *ctx = k->k_priv; + const struct ieee80211_frame *wh; + const u_int8_t *src; + u_int8_t *ivp, *mic, *dst; + u_int8_t a[16], b[16], s0[16], s[16]; + struct mbuf *n0, *m, *n; + int hdrlen, left, moff, noff, len; + u_int16_t ctr; + int i, j; + + MGET(n0, M_DONTWAIT, m0->m_type); + if (n0 == NULL) + goto nospace; + M_DUP_PKTHDR(n0, m0); + n0->m_pkthdr.len += IEEE80211_CCMP_HDRLEN; + n0->m_len = MHLEN; + if (n0->m_pkthdr.len >= MINCLSIZE - IEEE80211_CCMP_MICLEN) { + MCLGET(n0, M_DONTWAIT); + if (n0->m_flags & M_EXT) + n0->m_len = n0->m_ext.ext_size; + } + if (n0->m_len > n0->m_pkthdr.len) + n0->m_len = n0->m_pkthdr.len; + + /* copy 802.11 header */ + wh = mtod(m0, struct ieee80211_frame *); + hdrlen = ieee80211_get_hdrlen(wh); + memcpy(mtod(n0, caddr_t), wh, hdrlen); + + k->k_tsc++; /* increment the 48-bit PN */ + + /* construct CCMP header */ + ivp = mtod(n0, u_int8_t *) + hdrlen; + ivp[0] = k->k_tsc; /* PN0 */ + ivp[1] = k->k_tsc >> 8; /* PN1 */ + ivp[2] = 0; /* Rsvd */ + ivp[3] = k->k_id << 6 | IEEE80211_WEP_EXTIV; /* KeyID | ExtIV */ + ivp[4] = k->k_tsc >> 16; /* PN2 */ + ivp[5] = k->k_tsc >> 24; /* PN3 */ + ivp[6] = k->k_tsc >> 32; /* PN4 */ + ivp[7] = k->k_tsc >> 40; /* PN5 */ + + /* construct initial B, A and S_0 blocks */ + ieee80211_ccmp_phase1(&ctx->rijndael, wh, k->k_tsc, + m0->m_pkthdr.len - hdrlen, b, a, s0); + + /* construct S_1 */ + ctr = 1; + a[14] = ctr >> 8; + a[15] = ctr & 0xff; + rijndael_encrypt(&ctx->rijndael, a, s); + + /* encrypt frame body and compute MIC */ + j = 0; + m = m0; + n = n0; + moff = hdrlen; + noff = hdrlen + IEEE80211_CCMP_HDRLEN; + left = m0->m_pkthdr.len - moff; + while (left > 0) { + if (moff == m->m_len) { + /* nothing left to copy from m */ + m = m->m_next; + moff = 0; + } + if (noff == n->m_len) { + /* n is full and there's more data to copy */ + MGET(n->m_next, M_DONTWAIT, n->m_type); + if (n->m_next == NULL) + goto nospace; + n = n->m_next; + n->m_len = MLEN; + if (left > MLEN - IEEE80211_CCMP_MICLEN) { + MCLGET(n, M_DONTWAIT); + if (n->m_flags & M_EXT) + n->m_len = n->m_ext.ext_size; + } + if (n->m_len > left) + n->m_len = left; + noff = 0; + } + len = min(m->m_len - moff, n->m_len - noff); + + src = mtod(m, u_int8_t *) + moff; + dst = mtod(n, u_int8_t *) + noff; + for (i = 0; i < len; i++) { + /* update MIC with clear text */ + b[j] ^= src[i]; + /* encrypt message */ + dst[i] = src[i] ^ s[j]; + if (++j < 16) + continue; + /* we have a full block, encrypt MIC */ + rijndael_encrypt(&ctx->rijndael, b, b); + /* construct a new S_ctr block */ + ctr++; + a[14] = ctr >> 8; + a[15] = ctr & 0xff; + rijndael_encrypt(&ctx->rijndael, a, s); + j = 0; + } + + moff += len; + noff += len; + left -= len; + } + if (j != 0) /* partial block, encrypt MIC */ + rijndael_encrypt(&ctx->rijndael, b, b); + + /* reserve trailing space for MIC */ + if (M_TRAILINGSPACE(n) < IEEE80211_CCMP_MICLEN) { + MGET(n->m_next, M_DONTWAIT, n->m_type); + if (n->m_next == NULL) + goto nospace; + n = n->m_next; + n->m_len = 0; + } + /* finalize MIC, U := T XOR first-M-bytes( S_O ) */ + mic = mtod(n, u_int8_t *) + n->m_len; + for (i = 0; i < IEEE80211_CCMP_MICLEN; i++) + mic[i] = b[i] ^ s0[i]; + n->m_len += IEEE80211_CCMP_MICLEN; + n0->m_pkthdr.len += IEEE80211_CCMP_MICLEN; + + m_freem(m0); + return n0; + nospace: + ic->ic_stats.is_tx_nombuf++; + m_freem(m0); + if (n0 != NULL) + m_freem(n0); + return NULL; +} + +struct mbuf * +ieee80211_ccmp_decrypt(struct ieee80211com *ic, struct mbuf *m0, + struct ieee80211_key *k) +{ + struct ieee80211_ccmp_ctx *ctx = k->k_priv; + struct ieee80211_frame *wh; + u_int64_t pn; + const u_int8_t *ivp, *src; + u_int8_t *dst; + u_int8_t mic0[IEEE80211_CCMP_MICLEN]; + u_int8_t a[16], b[16], s0[16], s[16]; + struct mbuf *n0, *m, *n; + int hdrlen, left, moff, noff, len; + u_int16_t ctr; + int i, j; + + wh = mtod(m0, struct ieee80211_frame *); + hdrlen = ieee80211_get_hdrlen(wh); + ivp = (u_int8_t *)wh + hdrlen; + + if (m0->m_pkthdr.len < hdrlen + IEEE80211_CCMP_HDRLEN + + IEEE80211_CCMP_MICLEN) { + m_freem(m0); + return NULL; + } + /* check that ExtIV bit is be set */ + if (!(ivp[3] & IEEE80211_WEP_EXTIV)) { + m_freem(m0); + return NULL; + } + /* extract the 48-bit PN from the CCMP header */ + pn = (u_int64_t)ivp[0] | + (u_int64_t)ivp[1] << 8 | + (u_int64_t)ivp[4] << 16 | + (u_int64_t)ivp[5] << 24 | + (u_int64_t)ivp[6] << 32 | + (u_int64_t)ivp[7] << 40; + if (pn <= k->k_rsc[0]) { + /* replayed frame, discard */ + m_freem(m0); + return NULL; + } + + MGET(n0, M_DONTWAIT, m0->m_type); + if (n0 == NULL) + goto nospace; + M_DUP_PKTHDR(n0, m0); + n0->m_pkthdr.len -= IEEE80211_CCMP_HDRLEN + IEEE80211_CCMP_MICLEN; + n0->m_len = MHLEN; + if (n0->m_pkthdr.len >= MINCLSIZE) { + MCLGET(n0, M_DONTWAIT); + if (n0->m_flags & M_EXT) + n0->m_len = n0->m_ext.ext_size; + } + if (n0->m_len > n0->m_pkthdr.len) + n0->m_len = n0->m_pkthdr.len; + + /* construct initial B, A and S_0 blocks */ + ieee80211_ccmp_phase1(&ctx->rijndael, wh, pn, + n0->m_pkthdr.len - hdrlen, b, a, s0); + + /* copy 802.11 header and clear protected bit */ + memcpy(mtod(n0, caddr_t), wh, hdrlen); + wh = mtod(n0, struct ieee80211_frame *); + wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; + + /* construct S_1 */ + ctr = 1; + a[14] = ctr >> 8; + a[15] = ctr & 0xff; + rijndael_encrypt(&ctx->rijndael, a, s); + + /* decrypt frame body and compute MIC */ + j = 0; + m = m0; + n = n0; + moff = hdrlen + IEEE80211_CCMP_HDRLEN; + noff = hdrlen; + left = n0->m_pkthdr.len - noff; + while (left > 0) { + if (moff == m->m_len) { + /* nothing left to copy from m */ + m = m->m_next; + moff = 0; + } + if (noff == n->m_len) { + /* n is full and there's more data to copy */ + MGET(n->m_next, M_DONTWAIT, n->m_type); + if (n->m_next == NULL) + goto nospace; + n = n->m_next; + n->m_len = MLEN; + if (left > MLEN) { + MCLGET(n, M_DONTWAIT); + if (n->m_flags & M_EXT) + n->m_len = n->m_ext.ext_size; + } + if (n->m_len > left) + n->m_len = left; + noff = 0; + } + len = min(m->m_len - moff, n->m_len - noff); + + src = mtod(m, u_int8_t *) + moff; + dst = mtod(n, u_int8_t *) + noff; + for (i = 0; i < len; i++) { + /* decrypt message */ + dst[i] = src[i] ^ s[j]; + /* update MIC with clear text */ + b[j] ^= dst[i]; + if (++j < 16) + continue; + /* we have a full block, encrypt MIC */ + rijndael_encrypt(&ctx->rijndael, b, b); + /* construct a new S_ctr block */ + ctr++; + a[14] = ctr >> 8; + a[15] = ctr & 0xff; + rijndael_encrypt(&ctx->rijndael, a, s); + j = 0; + } + + moff += len; + noff += len; + left -= len; + } + if (j != 0) /* partial block, encrypt MIC */ + rijndael_encrypt(&ctx->rijndael, b, b); + + /* finalize MIC, U := T XOR first-M-bytes( S_O ) */ + for (i = 0; i < IEEE80211_CCMP_MICLEN; i++) + b[i] ^= s0[i]; + + /* check that it matches the MIC in received frame */ + m_copydata(m, moff, IEEE80211_CCMP_MICLEN, mic0); + if (memcmp(mic0, b, IEEE80211_CCMP_MICLEN) != 0) { + m_freem(m0); + m_freem(n0); + return NULL; + } + + /* + * Update last seen packet number (note that it must be done + * after MIC is validated.) + */ + k->k_rsc[0] = pn; + + m_freem(m0); + return n0; + nospace: + ic->ic_stats.is_rx_nombuf++; + m_freem(m0); + if (n0 != NULL) + m_freem(n0); + return NULL; +} diff --git a/sys/net80211/ieee80211_crypto_tkip.c b/sys/net80211/ieee80211_crypto_tkip.c new file mode 100644 index 00000000000..53af48dca29 --- /dev/null +++ b/sys/net80211/ieee80211_crypto_tkip.c @@ -0,0 +1,707 @@ +/* $OpenBSD: ieee80211_crypto_tkip.c,v 1.1 2008/04/16 18:32:15 damien Exp $ */ + +/*- + * Copyright (c) 2008 Damien Bergamini <damien.bergamini@free.fr> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/endian.h> +#include <sys/errno.h> +#include <sys/proc.h> +#include <sys/sysctl.h> +#include <sys/syslog.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_arp.h> +#include <net/if_llc.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/if_ether.h> +#endif + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_crypto.h> + +#include <crypto/arc4.h> +#include <crypto/michael.h> + +typedef u_int8_t byte; /* 8-bit byte (octet) */ +typedef u_int16_t u16b; /* 16-bit unsigned word */ +typedef u_int32_t u32b; /* 32-bit unsigned word */ + +static void Phase1(u16b *, const byte *, const byte *, u32b); +static void Phase2(byte *, const byte *, const u16b *, u16b); + +/* TKIP software crypto context */ +struct ieee80211_tkip_ctx { + struct rc4_ctx rc4; + const u_int8_t *txmic; + const u_int8_t *rxmic; + u_int16_t TTAK1[5]; + u_int16_t TTAK2[5]; +}; + +/* + * Initialize software crypto context. This function can be overridden + * by drivers doing hardware crypto. + */ +int +ieee80211_tkip_set_key(struct ieee80211com *ic, struct ieee80211_key *k) +{ + struct ieee80211_tkip_ctx *ctx; + + ctx = malloc(sizeof(*ctx), M_DEVBUF, M_NOWAIT | M_ZERO); + if (ctx == NULL) + return ENOMEM; + /* + * Use bits 128-191 as the Michael key for AA->SPA and bits + * 192-255 as the Michael key for SPA->AA. + */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + ctx->txmic = &k->k_key[16]; + ctx->rxmic = &k->k_key[24]; + } else { + ctx->rxmic = &k->k_key[16]; + ctx->txmic = &k->k_key[24]; + } + k->k_priv = ctx; + return 0; +} + +void +ieee80211_tkip_delete_key(struct ieee80211com *ic, struct ieee80211_key *k) +{ + if (k->k_priv != NULL) + free(k->k_priv, M_DEVBUF); + k->k_priv = NULL; +} + +/* pseudo-header used for TKIP MIC computation */ +struct ieee80211_tkip_frame { + u_int8_t i_da[IEEE80211_ADDR_LEN]; + u_int8_t i_sa[IEEE80211_ADDR_LEN]; + u_int8_t i_pri; + u_int8_t i_pad[3]; +} __packed; + +/* + * Compute TKIP MIC over an mbuf chain starting "off" bytes from the + * beginning. This function should be kept independant from the software + * TKIP crypto code so that drivers doing hardware crypto but not MIC can + * call it without a software crypto context. + */ +void +ieee80211_tkip_mic(struct mbuf *m0, int off, const u_int8_t *key, + u_int8_t mic[IEEE80211_TKIP_MICLEN]) +{ + const struct ieee80211_frame *wh; + struct ieee80211_tkip_frame wht; + MICHAEL_CTX ctx; /* small enough */ + struct mbuf *m; + caddr_t pos; + int len; + + /* assumes 802.11 header is contiguous */ + wh = mtod(m0, struct ieee80211_frame *); + + /* construct pseudo-header for TKIP MIC computation */ + switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { + case IEEE80211_FC1_DIR_NODS: + IEEE80211_ADDR_COPY(wht.i_da, wh->i_addr1); + IEEE80211_ADDR_COPY(wht.i_sa, wh->i_addr2); + break; + case IEEE80211_FC1_DIR_TODS: + IEEE80211_ADDR_COPY(wht.i_da, wh->i_addr3); + IEEE80211_ADDR_COPY(wht.i_sa, wh->i_addr2); + break; + case IEEE80211_FC1_DIR_FROMDS: + IEEE80211_ADDR_COPY(wht.i_da, wh->i_addr1); + IEEE80211_ADDR_COPY(wht.i_sa, wh->i_addr3); + break; + case IEEE80211_FC1_DIR_DSTODS: + /* not yet supported */ + break; + } + if ((wh->i_fc[0] & + (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_QOS)) == + (IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS)) { + const struct ieee80211_qosframe *qwh = + (const struct ieee80211_qosframe *)wh; + wht.i_pri = qwh->i_qos[0] & 0xf; + } else + wht.i_pri = 0; + wht.i_pad[0] = wht.i_pad[1] = wht.i_pad[2] = 0; + + michael_init(&ctx); + michael_key(key, &ctx); + + michael_update(&ctx, (caddr_t)&wht, sizeof(wht)); + + m = m0; + /* assumes the first "off" bytes are contiguous */ + pos = mtod(m, caddr_t) + off; + len = m->m_len - off; + for (;;) { + michael_update(&ctx, pos, len); + if ((m = m->m_next) == NULL) + break; + pos = mtod(m, caddr_t); + len = m->m_len; + } + + michael_final(mic, &ctx); +} + +/* shortcuts */ +#define IEEE80211_TKIP_TAILLEN \ + (IEEE80211_TKIP_MICLEN + IEEE80211_WEP_CRCLEN) +#define IEEE80211_TKIP_OVHD \ + (IEEE80211_TKIP_HDRLEN + IEEE80211_TKIP_TAILLEN) + +struct mbuf * +ieee80211_tkip_encrypt(struct ieee80211com *ic, struct mbuf *m0, + struct ieee80211_key *k) +{ + struct ieee80211_tkip_ctx *ctx = k->k_priv; + u_int8_t wepseed[16]; + const struct ieee80211_frame *wh; + u_int8_t *ivp, *mic, *icvp; + struct mbuf *n0, *m, *n; + u_int32_t crc; + int left, moff, noff, len, hdrlen; + + MGET(n0, M_DONTWAIT, m0->m_type); + if (n0 == NULL) + goto nospace; + M_DUP_PKTHDR(n0, m0); + n0->m_pkthdr.len += IEEE80211_TKIP_HDRLEN; + n0->m_len = MHLEN; + if (n0->m_pkthdr.len >= MINCLSIZE - IEEE80211_TKIP_TAILLEN) { + MCLGET(n0, M_DONTWAIT); + if (n0->m_flags & M_EXT) + n0->m_len = n0->m_ext.ext_size; + } + if (n0->m_len > n0->m_pkthdr.len) + n0->m_len = n0->m_pkthdr.len; + + /* copy 802.11 header */ + wh = mtod(m0, struct ieee80211_frame *); + hdrlen = ieee80211_get_hdrlen(wh); + memcpy(mtod(n0, caddr_t), wh, hdrlen); + + /* construct TKIP header */ + ivp = mtod(n0, u_int8_t *) + hdrlen; + ivp[0] = k->k_tsc >> 8; /* TSC1 */ + /* WEP Seed = (TSC1 | 0x20) & 0x7f (see 8.3.2.2) */ + ivp[1] = (ivp[0] | 0x20) & 0x7f; + ivp[2] = k->k_tsc; /* TSC0 */ + ivp[3] = k->k_id << 6 | IEEE80211_WEP_EXTIV; /* KeyID | ExtIV */ + ivp[4] = k->k_tsc >> 16; /* TSC2 */ + ivp[5] = k->k_tsc >> 24; /* TSC3 */ + ivp[6] = k->k_tsc >> 32; /* TSC4 */ + ivp[7] = k->k_tsc >> 40; /* TSC5 */ + + /* compute WEP seed */ +#ifdef notyet + if ((k->k_tsc & 0xffff) == 0) +#endif + Phase1(ctx->TTAK1, k->k_key, wh->i_addr2, k->k_tsc >> 16); + Phase2(wepseed, k->k_key, ctx->TTAK1, k->k_tsc & 0xffff); + rc4_keysetup(&ctx->rc4, wepseed, 16); + + /* encrypt frame body and compute WEP ICV */ + m = m0; + n = n0; + moff = hdrlen; + noff = hdrlen + IEEE80211_TKIP_HDRLEN; + left = m0->m_pkthdr.len - moff; + crc = ~0; + while (left > 0) { + if (moff == m->m_len) { + /* nothing left to copy from m */ + m = m->m_next; + moff = 0; + } + if (noff == n->m_len) { + /* n is full and there's more data to copy */ + MGET(n->m_next, M_DONTWAIT, n->m_type); + if (n->m_next == NULL) + goto nospace; + n = n->m_next; + n->m_len = MLEN; + if (left > MLEN - IEEE80211_TKIP_TAILLEN) { + MCLGET(n, M_DONTWAIT); + if (n->m_flags & M_EXT) + n->m_len = n->m_ext.ext_size; + } + if (n->m_len > left) + n->m_len = left; + noff = 0; + } + len = min(m->m_len - moff, n->m_len - noff); + + crc = ieee80211_crc_update(crc, mtod(m, caddr_t) + moff, len); + rc4_crypt(&ctx->rc4, mtod(m, caddr_t) + moff, + mtod(n, caddr_t) + noff, len); + + moff += len; + noff += len; + left -= len; + } + + /* reserve trailing space for TKIP MIC and WEP ICV */ + if (M_TRAILINGSPACE(n) < IEEE80211_TKIP_TAILLEN) { + MGET(n->m_next, M_DONTWAIT, n->m_type); + if (n->m_next == NULL) + goto nospace; + n = n->m_next; + n->m_len = 0; + } + + /* compute TKIP MIC over clear text */ + mic = mtod(n, caddr_t) + n->m_len; + ieee80211_tkip_mic(m0, hdrlen, ctx->txmic, mic); + crc = ieee80211_crc_update(crc, mic, IEEE80211_TKIP_MICLEN); + rc4_crypt(&ctx->rc4, mic, mic, IEEE80211_TKIP_MICLEN); + n->m_len += IEEE80211_TKIP_MICLEN; + + /* finalize WEP ICV */ + icvp = mtod(n, caddr_t) + n->m_len; + crc = ~crc; + icvp[0] = crc; + icvp[1] = crc >> 8; + icvp[2] = crc >> 16; + icvp[3] = crc >> 24; + rc4_crypt(&ctx->rc4, icvp, icvp, IEEE80211_WEP_CRCLEN); + n->m_len += IEEE80211_WEP_CRCLEN; + + n0->m_pkthdr.len += IEEE80211_TKIP_TAILLEN; + + k->k_tsc++; /* increment the 48-bit TSC */ + + m_freem(m0); + return n0; + nospace: + ic->ic_stats.is_tx_nombuf++; + m_freem(m0); + if (n0 != NULL) + m_freem(n0); + return NULL; +} + +struct mbuf * +ieee80211_tkip_decrypt(struct ieee80211com *ic, struct mbuf *m0, + struct ieee80211_key *k) +{ + struct ieee80211_tkip_ctx *ctx = k->k_priv; + struct ieee80211_frame *wh; + u_int8_t wepseed[16]; + u_int8_t buf[IEEE80211_TKIP_MICLEN + IEEE80211_WEP_CRCLEN]; + u_int8_t mic[IEEE80211_TKIP_MICLEN]; + u_int64_t tsc; + u_int32_t crc, crc0; + u_int8_t *ivp, *mic0; + struct mbuf *n0, *m, *n; + int hdrlen, left, moff, noff, len; + + wh = mtod(m0, struct ieee80211_frame *); + hdrlen = ieee80211_get_hdrlen(wh); + + if (m0->m_pkthdr.len < hdrlen + IEEE80211_TKIP_OVHD) { + m_freem(m0); + return NULL; + } + + ivp = (u_int8_t *)wh + hdrlen; + /* check that ExtIV bit is be set */ + if (!(ivp[3] & IEEE80211_WEP_EXTIV)) { + m_freem(m0); + return NULL; + } + /* extract the 48-bit TSC from the TKIP header */ + tsc = (u_int64_t)ivp[2] | + (u_int64_t)ivp[0] << 8 | + (u_int64_t)ivp[4] << 16 | + (u_int64_t)ivp[5] << 24 | + (u_int64_t)ivp[6] << 32 | + (u_int64_t)ivp[7] << 40; + /* NB: the keys are refreshed, we'll never overflow the 48 bits */ + if (tsc <= k->k_rsc[0]) { + /* replayed frame, discard */ + m_freem(m0); + return NULL; + } + + MGET(n0, M_DONTWAIT, m0->m_type); + if (n0 == NULL) + goto nospace; + M_DUP_PKTHDR(n0, m0); + n0->m_pkthdr.len -= IEEE80211_TKIP_OVHD; + n0->m_len = MHLEN; + if (n0->m_pkthdr.len >= MINCLSIZE) { + MCLGET(n0, M_DONTWAIT); + if (n0->m_flags & M_EXT) + n0->m_len = n0->m_ext.ext_size; + } + if (n0->m_len > n0->m_pkthdr.len) + n0->m_len = n0->m_pkthdr.len; + + /* copy 802.11 header and clear protected bit */ + memcpy(mtod(n0, caddr_t), wh, hdrlen); + wh = mtod(n0, struct ieee80211_frame *); + wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; + + /* compute WEP seed */ +#ifdef notyet + if (k->k_rsc[0] == 0 || ((tsc >> 16) != (k->k_rsc[0] >> 16))) +#endif + Phase1(ctx->TTAK2, k->k_key, wh->i_addr2, tsc >> 16); + Phase2(wepseed, k->k_key, ctx->TTAK2, tsc & 0xffff); + rc4_keysetup(&ctx->rc4, wepseed, 16); + + /* decrypt frame body and compute WEP ICV */ + m = m0; + n = n0; + moff = hdrlen + IEEE80211_TKIP_HDRLEN; + noff = hdrlen; + left = n0->m_pkthdr.len - noff; + crc = ~0; + while (left > 0) { + if (moff == m->m_len) { + /* nothing left to copy from m */ + m = m->m_next; + moff = 0; + } + if (noff == n->m_len) { + /* n is full and there's more data to copy */ + MGET(n->m_next, M_DONTWAIT, n->m_type); + if (n->m_next == NULL) + goto nospace; + n = n->m_next; + n->m_len = MLEN; + if (left > MLEN) { + MCLGET(n, M_DONTWAIT); + if (n->m_flags & M_EXT) + n->m_len = n->m_ext.ext_size; + } + if (n->m_len > left) + n->m_len = left; + noff = 0; + } + len = min(m->m_len - moff, n->m_len - noff); + + rc4_crypt(&ctx->rc4, mtod(m, caddr_t) + moff, + mtod(n, caddr_t) + noff, len); + crc = ieee80211_crc_update(crc, mtod(n, caddr_t) + noff, len); + + moff += len; + noff += len; + left -= len; + } + + /* extract and decrypt TKIP MIC and WEP ICV from m0's tail */ + m_copydata(m, moff, IEEE80211_TKIP_TAILLEN, buf); + rc4_crypt(&ctx->rc4, buf, buf, IEEE80211_TKIP_TAILLEN); + + /* include TKIP MIC in WEP ICV */ + mic0 = buf; + crc = ieee80211_crc_update(crc, mic0, IEEE80211_TKIP_MICLEN); + crc = ~crc; + + /* decrypt ICV and compare it with calculated ICV */ + crc0 = *(u_int32_t *)(buf + IEEE80211_TKIP_MICLEN); + if (crc != letoh32(crc0)) { + ic->ic_stats.is_rx_decryptcrc++; + m_freem(m0); + m_freem(n0); + return NULL; + } + + /* compute TKIP MIC over decrypted message */ + ieee80211_tkip_mic(n0, hdrlen, ctx->rxmic, mic); + /* check that it matches the MIC in received frame */ + if (memcmp(mic0, mic, IEEE80211_TKIP_MICLEN) != 0) { + m_freem(m0); + m_freem(n0); + ic->ic_stats.is_rx_locmicfail++; + ieee80211_michael_mic_failure(ic, tsc); + return NULL; + } + + /* + * Update last seen packet number (note that it must be done + * after MIC is validated.) + */ + k->k_rsc[0] = tsc; + + m_freem(m0); + return n0; + nospace: + ic->ic_stats.is_rx_nombuf++; + m_freem(m0); + if (n0 != NULL) + m_freem(n0); + return NULL; +} + +/* + * This function is called in HostAP mode to deauthenticate all STAs using + * TKIP as their pairwise or group cipher (as part of TKIP countermeasures). + */ +static void +ieee80211_tkip_deauth(void *arg, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = arg; + + if (ni->ni_state == IEEE80211_STA_ASSOC && + (ic->ic_bss->ni_rsngroupcipher == IEEE80211_CIPHER_TKIP || + ni->ni_rsncipher == IEEE80211_CIPHER_TKIP)) { + /* deauthenticate STA */ + IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_MIC_FAILURE); + ieee80211_node_leave(ic, ni); + } +} + +/* + * This function can be called by the software TKIP crypto code or by the + * drivers when their hardware crypto engines detect a Michael MIC failure. + */ +void +ieee80211_michael_mic_failure(struct ieee80211com *ic, u_int64_t tsc) +{ + extern int ticks; + + if (ic->ic_flags & IEEE80211_F_COUNTERM) + return; /* countermeasures already active */ + + log(LOG_WARNING, "%s: Michael MIC failure", ic->ic_if.if_xname); + + if (ic->ic_opmode == IEEE80211_M_STA) { + /* send a Michael MIC Failure Report frame to the AP */ + (void)ieee80211_send_eapol_key_req(ic, ic->ic_bss, + EAPOL_KEY_KEYMIC | EAPOL_KEY_ERROR | EAPOL_KEY_SECURE, + tsc); + } + /* + * Activate TKIP countermeasures (see 8.3.2.4) if less than 60 + * seconds have passed since the most recent previous MIC failure. + */ + if (ic->ic_tkip_micfail == 0 || + ticks >= ic->ic_tkip_micfail + 60 * hz) { + ic->ic_tkip_micfail = ticks; + return; + } + ic->ic_tkip_micfail = ticks; + + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + /* refuse new TKIP associations for the next 60 seconds */ + ic->ic_flags |= IEEE80211_F_COUNTERM; + + /* deauthenticate all currently associated STAs using TKIP */ + ieee80211_iterate_nodes(ic, ieee80211_tkip_deauth, ic); + + } else if (ic->ic_opmode == IEEE80211_M_STA) { + /* deauthenticate from the AP.. */ + IEEE80211_SEND_MGMT(ic, ic->ic_bss, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_MIC_FAILURE); + /* ..and find another one */ + (void)ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + } +} + +/*********************************************************************** + Contents: Generate IEEE 802.11 per-frame RC4 key hash test vectors + Date: April 19, 2002 + Notes: + This code is written for pedagogical purposes, NOT for performance. +************************************************************************/ + +/* macros for extraction/creation of byte/u16b values */ +#define RotR1(v16) ((((v16) >> 1) & 0x7FFF) ^ (((v16) & 1) << 15)) +#define Lo8(v16) ((byte)( (v16) & 0x00FF)) +#define Hi8(v16) ((byte)(((v16) >> 8) & 0x00FF)) +#define Lo16(v32) ((u16b)( (v32) & 0xFFFF)) +#define Hi16(v32) ((u16b)(((v32) >>16) & 0xFFFF)) +#define Mk16(hi,lo) ((lo) ^ (((u16b)(hi)) << 8)) + +/* select the Nth 16-bit word of the Temporal Key byte array TK[] */ +#define TK16(N) Mk16(TK[2 * (N) + 1], TK[2 * (N)]) + +/* S-box lookup: 16 bits --> 16 bits */ +#define _S_(v16) (Sbox[Lo8(v16)] ^ swap16(Sbox[Hi8(v16)])) + +/* fixed algorithm "parameters" */ +#define PHASE1_LOOP_CNT 8 /* this needs to be "big enough" */ +#define TA_SIZE 6 /* 48-bit transmitter address */ +#define TK_SIZE 16 /* 128-bit Temporal Key */ +#define P1K_SIZE 10 /* 80-bit Phase1 key */ +#define RC4_KEY_SIZE 16 /* 128-bit RC4KEY (104 bits unknown) */ + +/* 2-byte by 2-byte subset of the full AES S-box table */ +static const u16b Sbox[256]= /* Sbox for hash */ +{ + 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154, + 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A, + 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B, + 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B, + 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F, + 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F, + 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5, + 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F, + 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB, + 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397, + 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED, + 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A, + 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194, + 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3, + 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104, + 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D, + 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39, + 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695, + 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83, + 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76, + 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4, + 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B, + 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0, + 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018, + 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751, + 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85, + 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12, + 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9, + 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7, + 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A, + 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8, + 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A +}; + +/* + ********************************************************************** + * Routine: Phase 1 -- generate P1K, given TA, TK, IV32 + * + * Inputs: + * TK[] = Temporal Key [128 bits] + * TA[] = transmitter's MAC address [ 48 bits] + * IV32 = upper 32 bits of IV [ 32 bits] + * Output: + * P1K[] = Phase 1 key [ 80 bits] + * + * Note: + * This function only needs to be called every 2**16 frames, + * although in theory it could be called every frame. + * + ********************************************************************** + */ +static void +Phase1(u16b *P1K, const byte *TK, const byte *TA, u32b IV32) +{ + int i; + + /* Initialize the 80 bits of P1K[] from IV32 and TA[0..5] */ + P1K[0] = Lo16(IV32); + P1K[1] = Hi16(IV32); + P1K[2] = Mk16(TA[1], TA[0]); /* use TA[] as little-endian */ + P1K[3] = Mk16(TA[3], TA[2]); + P1K[4] = Mk16(TA[5], TA[4]); + + /* Now compute an unbalanced Feistel cipher with 80-bit block */ + /* size on the 80-bit block P1K[], using the 128-bit key TK[] */ + for (i = 0; i < PHASE1_LOOP_CNT; i++) { + /* Each add operation here is mod 2**16 */ + P1K[0] += _S_(P1K[4] ^ TK16((i & 1) + 0)); + P1K[1] += _S_(P1K[0] ^ TK16((i & 1) + 2)); + P1K[2] += _S_(P1K[1] ^ TK16((i & 1) + 4)); + P1K[3] += _S_(P1K[2] ^ TK16((i & 1) + 6)); + P1K[4] += _S_(P1K[3] ^ TK16((i & 1) + 0)); + P1K[4] += i; /* avoid "slide attacks" */ + } +} + +/* + ********************************************************************** + * Routine: Phase 2 -- generate RC4KEY, given TK, P1K, IV16 + * + * Inputs: + * TK[] = Temporal Key [128 bits] + * P1K[] = Phase 1 output key [ 80 bits] + * IV16 = low 16 bits of IV counter [ 16 bits] + * Output: + * RC4KEY[] = the key used to encrypt the frame [128 bits] + * + * Note: + * The value {TA,IV32,IV16} for Phase1/Phase2 must be unique + * across all frames using the same key TK value. Then, for a + * given value of TK[], this TKIP48 construction guarantees that + * the final RC4KEY value is unique across all frames. + * + * Suggested implementation optimization: if PPK[] is "overlaid" + * appropriately on RC4KEY[], there is no need for the final + * for loop below that copies the PPK[] result into RC4KEY[]. + * + ********************************************************************** + */ +static void +Phase2(byte *RC4KEY, const byte *TK, const u16b *P1K, u16b IV16) +{ + u16b PPK[6]; /* temporary key for mixing */ + int i; + + /* all adds in the PPK[] equations below are mod 2**16 */ + for (i = 0; i < 5; i++) + PPK[i] = P1K[i]; /* first, copy P1K to PPK */ + PPK[5] = P1K[4] + IV16; /* next, add in IV16 */ + + /* Bijective non-linear mixing of the 96 bits of PPK[0..5] */ + PPK[0] += _S_(PPK[5] ^ TK16(0)); /* Mix key in each "round" */ + PPK[1] += _S_(PPK[0] ^ TK16(1)); + PPK[2] += _S_(PPK[1] ^ TK16(2)); + PPK[3] += _S_(PPK[2] ^ TK16(3)); + PPK[4] += _S_(PPK[3] ^ TK16(4)); + PPK[5] += _S_(PPK[4] ^ TK16(5)); /* Total # S-box lookups == 6 */ + + /* Final sweep: bijective, linear. Rotates kill LSB correlations */ + PPK[0] += RotR1(PPK[5] ^ TK16(6)); + PPK[1] += RotR1(PPK[0] ^ TK16(7)); /* Use all of TK[] in Phase2 */ + PPK[2] += RotR1(PPK[1]); + PPK[3] += RotR1(PPK[2]); + PPK[4] += RotR1(PPK[3]); + PPK[5] += RotR1(PPK[4]); + + /* At this point, for a given key TK[0..15], the 96-bit output */ + /* value PPK[0..5] is guaranteed to be unique, as a function */ + /* of the 96-bit "input" value {TA,IV32,IV16}. That is, P1K */ + /* is now a keyed permutation of {TA,IV32,IV16}. */ + /* Set RC4KEY[0..3], which includes cleartext portion of RC4 key */ + RC4KEY[0] = Hi8(IV16); /* RC4KEY[0..2] is the WEP IV */ + RC4KEY[1] =(Hi8(IV16) | 0x20) & 0x7F; /* Help avoid FMS weak keys */ + RC4KEY[2] = Lo8(IV16); + RC4KEY[3] = Lo8((PPK[5] ^ TK16(0)) >> 1); + + /* Copy 96 bits of PPK[0..5] to RC4KEY[4..15] (little-endian) */ + for (i = 0; i < 6; i++) { + RC4KEY[4 + 2 * i] = Lo8(PPK[i]); + RC4KEY[5 + 2 * i] = Hi8(PPK[i]); + } +} diff --git a/sys/net80211/ieee80211_crypto_wep.c b/sys/net80211/ieee80211_crypto_wep.c new file mode 100644 index 00000000000..0e639558b70 --- /dev/null +++ b/sys/net80211/ieee80211_crypto_wep.c @@ -0,0 +1,305 @@ +/* $OpenBSD: ieee80211_crypto_wep.c,v 1.1 2008/04/16 18:32:15 damien Exp $ */ + +/*- + * Copyright (c) 2008 Damien Bergamini <damien.bergamini@free.fr> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/endian.h> +#include <sys/errno.h> +#include <sys/proc.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_arp.h> +#include <net/if_llc.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/if_ether.h> +#endif + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_crypto.h> + +#include <dev/rndvar.h> +#include <crypto/arc4.h> + +/* WEP software crypto context */ +struct ieee80211_wep_ctx { + struct rc4_ctx rc4; + u_int32_t iv; +}; + +/* + * Initialize software crypto context. This function can be overridden + * by drivers doing hardware crypto. + */ +int +ieee80211_wep_set_key(struct ieee80211com *ic, struct ieee80211_key *k) +{ + struct ieee80211_wep_ctx *ctx; + + ctx = malloc(sizeof(*ctx), M_DEVBUF, M_NOWAIT | M_ZERO); + if (ctx == NULL) + return ENOMEM; + k->k_priv = ctx; + return 0; +} + +void +ieee80211_wep_delete_key(struct ieee80211com *ic, struct ieee80211_key *k) +{ + if (k->k_priv != NULL) + free(k->k_priv, M_DEVBUF); + k->k_priv = NULL; +} + +/* shortcut */ +#define IEEE80211_WEP_HDRLEN \ + (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN) + +struct mbuf * +ieee80211_wep_encrypt(struct ieee80211com *ic, struct mbuf *m0, + struct ieee80211_key *k) +{ + struct ieee80211_wep_ctx *ctx = k->k_priv; + u_int8_t wepseed[16]; + const struct ieee80211_frame *wh; + struct mbuf *n0, *m, *n; + u_int8_t *ivp, *icvp; + u_int32_t iv, crc; + int left, moff, noff, len, hdrlen; + + MGET(n0, M_DONTWAIT, m0->m_type); + if (n0 == NULL) + goto nospace; + M_DUP_PKTHDR(n0, m0); + n0->m_pkthdr.len += IEEE80211_WEP_HDRLEN; + n0->m_len = MHLEN; + if (n0->m_pkthdr.len >= MINCLSIZE - IEEE80211_WEP_CRCLEN) { + MCLGET(n0, M_DONTWAIT); + if (n0->m_flags & M_EXT) + n0->m_len = n0->m_ext.ext_size; + } + if (n0->m_len > n0->m_pkthdr.len) + n0->m_len = n0->m_pkthdr.len; + + /* copy 802.11 header */ + wh = mtod(m0, struct ieee80211_frame *); + hdrlen = ieee80211_get_hdrlen(wh); + memcpy(mtod(n0, caddr_t), wh, hdrlen); + + /* select a new IV for every MPDU */ + iv = (ctx->iv != 0) ? ctx->iv : arc4random(); + /* skip weak IVs from Fluhrer/Mantin/Shamir */ + if (iv >= 0x03ff00 && (iv & 0xf8ff00) == 0x00ff00) + iv += 0x000100; + ctx->iv = iv + 1; + ivp = mtod(n0, u_int8_t *) + hdrlen; + ivp[0] = iv; + ivp[1] = iv >> 8; + ivp[2] = iv >> 16; + ivp[3] = k->k_id << 6; + + /* compute WEP seed: concatenate IV and WEP Key */ + memcpy(wepseed, ivp, IEEE80211_WEP_IVLEN); + memcpy(wepseed + IEEE80211_WEP_IVLEN, k->k_key, k->k_len); + rc4_keysetup(&ctx->rc4, wepseed, IEEE80211_WEP_IVLEN + k->k_len); + + /* encrypt frame body and compute WEP ICV */ + m = m0; + n = n0; + moff = hdrlen; + noff = hdrlen + IEEE80211_WEP_HDRLEN; + left = m0->m_pkthdr.len - moff; + crc = ~0; + while (left > 0) { + if (moff == m->m_len) { + /* nothing left to copy from m */ + m = m->m_next; + moff = 0; + } + if (noff == n->m_len) { + /* n is full and there's more data to copy */ + MGET(n->m_next, M_DONTWAIT, n->m_type); + if (n->m_next == NULL) + goto nospace; + n = n->m_next; + n->m_len = MLEN; + if (left > MLEN - IEEE80211_WEP_CRCLEN) { + MCLGET(n, M_DONTWAIT); + if (n->m_flags & M_EXT) + n->m_len = n->m_ext.ext_size; + } + if (n->m_len > left) + n->m_len = left; + noff = 0; + } + len = min(m->m_len - moff, n->m_len - noff); + + crc = ieee80211_crc_update(crc, mtod(m, caddr_t) + moff, len); + rc4_crypt(&ctx->rc4, mtod(m, caddr_t) + moff, + mtod(n, caddr_t) + noff, len); + + moff += len; + noff += len; + left -= len; + } + + /* reserve trailing space for WEP ICV */ + if (M_TRAILINGSPACE(n) < IEEE80211_WEP_CRCLEN) { + MGET(n->m_next, M_DONTWAIT, n->m_type); + if (n->m_next == NULL) + goto nospace; + n = n->m_next; + n->m_len = 0; + } + + /* finalize WEP ICV */ + icvp = mtod(n, caddr_t) + n->m_len; + crc = ~crc; + icvp[0] = crc; + icvp[1] = crc >> 8; + icvp[2] = crc >> 16; + icvp[3] = crc >> 24; + rc4_crypt(&ctx->rc4, icvp, icvp, IEEE80211_WEP_CRCLEN); + n->m_len += IEEE80211_WEP_CRCLEN; + n0->m_pkthdr.len += IEEE80211_WEP_CRCLEN; + + m_freem(m0); + return n0; + nospace: + ic->ic_stats.is_tx_nombuf++; + m_freem(m0); + if (n0 != NULL) + m_freem(n0); + return NULL; +} + +struct mbuf * +ieee80211_wep_decrypt(struct ieee80211com *ic, struct mbuf *m0, + struct ieee80211_key *k) +{ + struct ieee80211_wep_ctx *ctx = k->k_priv; + struct ieee80211_frame *wh; + u_int8_t wepseed[16]; + u_int32_t crc, crc0; + u_int8_t *ivp; + struct mbuf *n0, *m, *n; + int hdrlen, left, moff, noff, len; + + wh = mtod(m0, struct ieee80211_frame *); + hdrlen = ieee80211_get_hdrlen(wh); + + if (m0->m_pkthdr.len < hdrlen + IEEE80211_WEP_TOTLEN) { + m_freem(m0); + return NULL; + } + + /* concatenate IV and WEP Key */ + ivp = (u_int8_t *)wh + hdrlen; + memcpy(wepseed, ivp, IEEE80211_WEP_IVLEN); + memcpy(wepseed + IEEE80211_WEP_IVLEN, k->k_key, k->k_len); + rc4_keysetup(&ctx->rc4, wepseed, IEEE80211_WEP_IVLEN + k->k_len); + + MGET(n0, M_DONTWAIT, m0->m_type); + if (n0 == NULL) + goto nospace; + M_DUP_PKTHDR(n0, m0); + n0->m_pkthdr.len -= IEEE80211_WEP_TOTLEN; + n0->m_len = MHLEN; + if (n0->m_pkthdr.len >= MINCLSIZE) { + MCLGET(n0, M_DONTWAIT); + if (n0->m_flags & M_EXT) + n0->m_len = n0->m_ext.ext_size; + } + if (n0->m_len > n0->m_pkthdr.len) + n0->m_len = n0->m_pkthdr.len; + + /* copy 802.11 header and clear protected bit */ + memcpy(mtod(n0, caddr_t), wh, hdrlen); + wh = mtod(n0, struct ieee80211_frame *); + wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; + + /* decrypt frame body and compute WEP ICV */ + m = m0; + n = n0; + moff = hdrlen + IEEE80211_WEP_HDRLEN; + noff = hdrlen; + left = n0->m_pkthdr.len - noff; + crc = ~0; + while (left > 0) { + if (moff == m->m_len) { + /* nothing left to copy from m */ + m = m->m_next; + moff = 0; + } + if (noff == n->m_len) { + /* n is full and there's more data to copy */ + MGET(n->m_next, M_DONTWAIT, n->m_type); + if (n->m_next == NULL) + goto nospace; + n = n->m_next; + n->m_len = MLEN; + if (left > MLEN) { + MCLGET(n, M_DONTWAIT); + if (n->m_flags & M_EXT) + n->m_len = n->m_ext.ext_size; + } + if (n->m_len > left) + n->m_len = left; + noff = 0; + } + len = min(m->m_len - moff, n->m_len - noff); + + rc4_crypt(&ctx->rc4, mtod(m, caddr_t) + moff, + mtod(n, caddr_t) + noff, len); + crc = ieee80211_crc_update(crc, mtod(n, caddr_t) + noff, len); + + moff += len; + noff += len; + left -= len; + } + + /* decrypt ICV and compare it with calculated ICV */ + m_copydata(m, moff, IEEE80211_WEP_CRCLEN, (caddr_t)&crc0); + rc4_crypt(&ctx->rc4, (caddr_t)&crc0, (caddr_t)&crc0, + IEEE80211_WEP_CRCLEN); + crc = ~crc; + if (crc != letoh32(crc0)) { + ic->ic_stats.is_rx_decryptcrc++; + m_freem(m0); + m_freem(n0); + return NULL; + } + + m_freem(m0); + return n0; + nospace: + ic->ic_stats.is_rx_nombuf++; + m_freem(m0); + if (n0 != NULL) + m_freem(n0); + return NULL; +} 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(>k[8], ni->ni_group_cipher, kid, + ieee80211_map_gtk(>k[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(>k[8], ni->ni_group_cipher, kid, + ieee80211_map_gtk(>k[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 *); diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c index adc7a3efc66..bf379639e9c 100644 --- a/sys/net80211/ieee80211_ioctl.c +++ b/sys/net80211/ieee80211_ioctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_ioctl.c,v 1.20 2007/11/25 16:47:44 brad Exp $ */ +/* $OpenBSD: ieee80211_ioctl.c,v 1.21 2008/04/16 18:32:15 damien Exp $ */ /* $NetBSD: ieee80211_ioctl.c,v 1.15 2004/05/06 02:58:16 dyoung Exp $ */ /*- @@ -92,6 +92,7 @@ ieee80211_node2req(struct ieee80211com *ic, const struct ieee80211_node *ni, nr->nr_inact = ni->ni_inact; nr->nr_txrate = ni->ni_txrate; nr->nr_state = ni->ni_state; + /* XXX RSN */ /* Node flags */ nr->nr_flags = 0; @@ -131,6 +132,192 @@ ieee80211_req2node(struct ieee80211com *ic, const struct ieee80211_nodereq *nr, ni->ni_state = nr->nr_state; } +static int +ieee80211_ioctl_setnwkeys(struct ieee80211com *ic, + const struct ieee80211_nwkey *nwkey) +{ + struct ieee80211_key *k; + int error, i; + + if (!(ic->ic_caps & IEEE80211_C_WEP)) + return ENODEV; + + if (nwkey->i_wepon == IEEE80211_NWKEY_OPEN) { + if (!(ic->ic_flags & IEEE80211_F_WEPON)) + return 0; + ic->ic_flags &= ~IEEE80211_F_WEPON; + return ENETRESET; + } + if (nwkey->i_defkid < 1 || nwkey->i_defkid > IEEE80211_WEP_NKID) + return EINVAL; + + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + if (nwkey->i_key[i].i_keylen == 0 || + nwkey->i_key[i].i_keydat == NULL) + continue; /* entry not set */ + if (nwkey->i_key[i].i_keylen > IEEE80211_KEYBUF_SIZE) + return EINVAL; + + /* map wep key to ieee80211_key */ + k = &ic->ic_nw_keys[i]; + if (k->k_cipher != IEEE80211_CIPHER_NONE) + (*ic->ic_delete_key)(ic, NULL, k); + memset(k, 0, sizeof(*k)); + if (nwkey->i_key[i].i_keylen <= 5) + k->k_cipher = IEEE80211_CIPHER_WEP40; + else + k->k_cipher = IEEE80211_CIPHER_WEP104; + k->k_len = nwkey->i_key[i].i_keylen; + k->k_flags = IEEE80211_KEY_GROUP | IEEE80211_KEY_TX; + error = copyin(nwkey->i_key[i].i_keydat, k->k_key, + nwkey->i_key[i].i_keylen); + if (error != 0) + return error; + if ((error = (*ic->ic_set_key)(ic, NULL, k)) != 0) + return error; + } + + ic->ic_def_txkey = nwkey->i_defkid - 1; + ic->ic_flags |= IEEE80211_F_WEPON; + + return ENETRESET; +} + +static int +ieee80211_ioctl_getnwkeys(struct ieee80211com *ic, + struct ieee80211_nwkey *nwkey) +{ + struct ieee80211_key *k; + int error, i; + + if (ic->ic_flags & IEEE80211_F_WEPON) + nwkey->i_wepon = IEEE80211_NWKEY_WEP; + else + nwkey->i_wepon = IEEE80211_NWKEY_OPEN; + + nwkey->i_defkid = ic->ic_wep_txkey + 1; + + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + if (nwkey->i_key[i].i_keydat == NULL) + continue; + /* do not show any keys to non-root user */ + if ((error = suser(curproc, 0)) != 0) + return error; + k = &ic->ic_nw_keys[i]; + if (k->k_cipher != IEEE80211_CIPHER_WEP40 && + k->k_cipher != IEEE80211_CIPHER_WEP104) + nwkey->i_key[i].i_keylen = 0; + else + nwkey->i_key[i].i_keylen = k->k_len; + error = copyout(k->k_key, nwkey->i_key[i].i_keydat, + nwkey->i_key[i].i_keylen); + if (error != 0) + return error; + } + return 0; +} + +static int +ieee80211_ioctl_setwpaparms(struct ieee80211com *ic, + const struct ieee80211_wpaparams *wpa) +{ + if (!(ic->ic_caps & IEEE80211_C_RSN)) + return ENODEV; + + if (!wpa->i_enabled) { + if (!(ic->ic_flags & IEEE80211_F_RSNON)) + return 0; + ic->ic_flags &= ~IEEE80211_F_RSNON; + return ENETRESET; + } + + ic->ic_rsnprotos = 0; + if (wpa->i_protos & IEEE80211_WPA_PROTO_WPA1) + ic->ic_rsnprotos |= IEEE80211_PROTO_WPA; + if (wpa->i_protos & IEEE80211_WPA_PROTO_WPA2) + ic->ic_rsnprotos |= IEEE80211_PROTO_RSN; + if (ic->ic_rsnprotos == 0) /* set to default (WPA+RSN) */ + ic->ic_rsnprotos = IEEE80211_PROTO_WPA | IEEE80211_PROTO_RSN; + + ic->ic_rsnakms = 0; + if (wpa->i_akms & IEEE80211_WPA_AKM_PSK) + ic->ic_rsnakms |= IEEE80211_AKM_PSK; + if (wpa->i_akms & IEEE80211_WPA_AKM_IEEE8021X) + ic->ic_rsnakms |= IEEE80211_AKM_IEEE8021X; + if (ic->ic_rsnakms == 0) /* set to default (PSK+802.1X) */ + ic->ic_rsnakms = IEEE80211_AKM_PSK | IEEE80211_AKM_IEEE8021X; + + if (wpa->i_groupcipher == IEEE80211_WPA_CIPHER_WEP40) + ic->ic_rsngroupcipher = IEEE80211_CIPHER_WEP40; + else if (wpa->i_groupcipher == IEEE80211_WPA_CIPHER_TKIP) + ic->ic_rsngroupcipher = IEEE80211_CIPHER_TKIP; + else if (wpa->i_groupcipher == IEEE80211_WPA_CIPHER_CCMP) + ic->ic_rsngroupcipher = IEEE80211_CIPHER_CCMP; + else if (wpa->i_groupcipher == IEEE80211_WPA_CIPHER_WEP104) + ic->ic_rsngroupcipher = IEEE80211_CIPHER_WEP104; + else { /* set to default */ + if (ic->ic_rsnprotos & IEEE80211_PROTO_WPA) + ic->ic_rsngroupcipher = IEEE80211_CIPHER_TKIP; + else + ic->ic_rsngroupcipher = IEEE80211_CIPHER_CCMP; + } + + ic->ic_rsnciphers = 0; + if (wpa->i_ciphers & IEEE80211_WPA_CIPHER_TKIP) + ic->ic_rsnciphers |= IEEE80211_CIPHER_TKIP; + if (wpa->i_ciphers & IEEE80211_WPA_CIPHER_CCMP) + ic->ic_rsnciphers |= IEEE80211_CIPHER_CCMP; + if (wpa->i_ciphers & IEEE80211_WPA_CIPHER_USEGROUP) + ic->ic_rsnciphers = IEEE80211_CIPHER_USEGROUP; + if (ic->ic_rsnciphers == 0) /* set to default (TKIP+CCMP) */ + ic->ic_rsnciphers = IEEE80211_CIPHER_TKIP | + IEEE80211_CIPHER_CCMP; + + ic->ic_flags |= IEEE80211_F_RSNON; + + return ENETRESET; +} + +static int +ieee80211_ioctl_getwpaparms(struct ieee80211com *ic, + struct ieee80211_wpaparams *wpa) +{ + wpa->i_enabled = (ic->ic_flags & IEEE80211_F_RSNON) ? 1 : 0; + + wpa->i_protos = 0; + if (ic->ic_rsnprotos & IEEE80211_PROTO_WPA) + wpa->i_protos |= IEEE80211_WPA_PROTO_WPA1; + if (ic->ic_rsnprotos & IEEE80211_PROTO_RSN) + wpa->i_protos |= IEEE80211_WPA_PROTO_WPA2; + + wpa->i_akms = 0; + if (ic->ic_rsnakms & IEEE80211_AKM_PSK) + wpa->i_akms |= IEEE80211_WPA_AKM_PSK; + if (ic->ic_rsnakms & IEEE80211_AKM_IEEE8021X) + wpa->i_akms |= IEEE80211_WPA_AKM_IEEE8021X; + + if (ic->ic_rsngroupcipher == IEEE80211_CIPHER_WEP40) + wpa->i_groupcipher = IEEE80211_WPA_CIPHER_WEP40; + else if (ic->ic_rsngroupcipher == IEEE80211_CIPHER_TKIP) + wpa->i_groupcipher = IEEE80211_WPA_CIPHER_TKIP; + else if (ic->ic_rsngroupcipher == IEEE80211_CIPHER_CCMP) + wpa->i_groupcipher = IEEE80211_WPA_CIPHER_CCMP; + else if (ic->ic_rsngroupcipher == IEEE80211_CIPHER_WEP104) + wpa->i_groupcipher = IEEE80211_WPA_CIPHER_WEP104; + else + wpa->i_groupcipher = IEEE80211_WPA_CIPHER_NONE; + + wpa->i_ciphers = 0; + if (ic->ic_rsnciphers & IEEE80211_CIPHER_TKIP) + wpa->i_ciphers |= IEEE80211_WPA_CIPHER_TKIP; + if (ic->ic_rsnciphers & IEEE80211_CIPHER_CCMP) + wpa->i_ciphers |= IEEE80211_WPA_CIPHER_CCMP; + if (ic->ic_rsnciphers & IEEE80211_CIPHER_USEGROUP) + wpa->i_ciphers = IEEE80211_WPA_CIPHER_USEGROUP; + + return 0; +} + int ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { @@ -138,13 +325,13 @@ ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) struct ifreq *ifr = (struct ifreq *)data; int i, error = 0; struct ieee80211_nwid nwid; - struct ieee80211_nwkey *nwkey; + struct ieee80211_wpapsk *psk; + struct ieee80211_wmmparams *wmm; struct ieee80211_power *power; struct ieee80211_bssid *bssid; struct ieee80211chanreq *chanreq; struct ieee80211_channel *chan; struct ieee80211_txpower *txpower; - struct ieee80211_key keys[IEEE80211_WEP_NKID]; static const u_int8_t empty_macaddr[IEEE80211_ADDR_LEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; @@ -194,81 +381,63 @@ ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) case SIOCS80211NWKEY: if ((error = suser(curproc, 0)) != 0) break; - nwkey = (struct ieee80211_nwkey *)data; - if ((ic->ic_caps & IEEE80211_C_WEP) == 0 && - nwkey->i_wepon != IEEE80211_NWKEY_OPEN) { - error = EINVAL; + error = ieee80211_ioctl_setnwkeys(ic, (void *)data); + break; + case SIOCG80211NWKEY: + error = ieee80211_ioctl_getnwkeys(ic, (void *)data); + break; + case SIOCS80211WMMPARMS: + if ((error = suser(curproc, 0)) != 0) break; - } - /* check and copy keys */ - memset(keys, 0, sizeof(keys)); - for (i = 0; i < IEEE80211_WEP_NKID; i++) { - keys[i].k_len = nwkey->i_key[i].i_keylen; - /* - * Limit the maximal allowed key size to - * IEEE80211_KEYBUF_SIZE bytes. - */ - if (keys[i].k_len > sizeof(keys[i].k_key)) { - error = EINVAL; - break; - } - if (keys[i].k_len <= 0) - continue; - if ((error = copyin(nwkey->i_key[i].i_keydat, - keys[i].k_key, keys[i].k_len)) != 0) - break; - } - if (error) + if (!(ic->ic_flags & IEEE80211_C_QOS)) { + error = ENODEV; break; - i = nwkey->i_defkid - 1; - if (i < 0 || i >= IEEE80211_WEP_NKID || - keys[i].k_len == 0 || - (keys[i].k_len == -1 && ic->ic_nw_keys[i].k_len == 0)) { - if (nwkey->i_wepon != IEEE80211_NWKEY_OPEN) { - error = EINVAL; - break; - } - } else - ic->ic_wep_txkey = i; - /* save the key */ - if (nwkey->i_wepon == IEEE80211_NWKEY_OPEN) - ic->ic_flags &= ~IEEE80211_F_WEPON; + } + wmm = (struct ieee80211_wmmparams *)data; + if (wmm->i_enabled) + ic->ic_flags |= IEEE80211_F_QOS; else - ic->ic_flags |= IEEE80211_F_WEPON; - for (i = 0; i < IEEE80211_WEP_NKID; i++) { - struct ieee80211_key *k = &ic->ic_nw_keys[i]; - if (keys[i].k_len < 0) - continue; - if (keys[i].k_len == 0) - k->k_cipher = IEEE80211_CIPHER_NONE; - else if (keys[i].k_len <= 5) - k->k_cipher = IEEE80211_CIPHER_WEP40; - else - k->k_cipher = IEEE80211_CIPHER_WEP104; - k->k_len = keys[i].k_len; - memcpy(k->k_key, keys[i].k_key, sizeof(keys[i].k_key)); + ic->ic_flags &= ~IEEE80211_F_QOS; + error = ENETRESET; + break; + case SIOCG80211WMMPARMS: + wmm = (struct ieee80211_wmmparams *)data; + wmm->i_enabled = (ic->ic_flags & IEEE80211_F_QOS) ? 1 : 0; + break; + case SIOCS80211WPAPARMS: + if ((error = suser(curproc, 0)) != 0) + break; + error = ieee80211_ioctl_setwpaparms(ic, (void *)data); + break; + case SIOCG80211WPAPARMS: + error = ieee80211_ioctl_getwpaparms(ic, (void *)data); + break; + case SIOCS80211WPAPSK: + if ((error = suser(curproc, 0)) != 0) + break; + psk = (struct ieee80211_wpapsk *)data; + if (psk->i_enabled) { + ic->ic_flags |= IEEE80211_F_PSK; + memcpy(ic->ic_psk, psk->i_psk, sizeof(ic->ic_psk)); + } else { + ic->ic_flags &= ~IEEE80211_F_PSK; + memset(ic->ic_psk, 0, sizeof(ic->ic_psk)); } error = ENETRESET; break; - case SIOCG80211NWKEY: - nwkey = (struct ieee80211_nwkey *)data; - if (ic->ic_flags & IEEE80211_F_WEPON) - nwkey->i_wepon = IEEE80211_NWKEY_WEP; - else - nwkey->i_wepon = IEEE80211_NWKEY_OPEN; - nwkey->i_defkid = ic->ic_wep_txkey + 1; - for (i = 0; i < IEEE80211_WEP_NKID; i++) { - if (nwkey->i_key[i].i_keydat == NULL) - continue; + case SIOCG80211WPAPSK: + psk = (struct ieee80211_wpapsk *)data; + if (ic->ic_flags & IEEE80211_F_PSK) { + psk->i_enabled = 1; /* do not show any keys to non-root user */ - if ((error = suser(curproc, 0)) != 0) - break; - nwkey->i_key[i].i_keylen = ic->ic_nw_keys[i].k_len; - if ((error = copyout(ic->ic_nw_keys[i].k_key, - nwkey->i_key[i].i_keydat, - ic->ic_nw_keys[i].k_len)) != 0) - break; - } + if (suser(curproc, 0) != 0) { + psk->i_enabled = 2; + memset(psk->i_psk, 0, sizeof(psk->i_psk)); + break; /* return ok but w/o key */ + } + memcpy(psk->i_psk, ic->ic_psk, sizeof(psk->i_psk)); + } else + psk->i_enabled = 0; break; case SIOCS80211POWER: if ((error = suser(curproc, 0)) != 0) diff --git a/sys/net80211/ieee80211_ioctl.h b/sys/net80211/ieee80211_ioctl.h index 5cf82ec1676..8541ac556a0 100644 --- a/sys/net80211/ieee80211_ioctl.h +++ b/sys/net80211/ieee80211_ioctl.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_ioctl.h,v 1.10 2007/08/14 20:33:47 bluhm Exp $ */ +/* $OpenBSD: ieee80211_ioctl.h,v 1.11 2008/04/16 18:32:15 damien Exp $ */ /* $NetBSD: ieee80211_ioctl.h,v 1.7 2004/04/30 22:51:04 dyoung Exp $ */ /*- @@ -82,6 +82,13 @@ struct ieee80211_stats { u_int32_t is_node_timeout; /* nodes timed out inactivity */ u_int32_t is_crypto_nomem; /* no memory for crypto ctx */ u_int32_t is_rx_assoc_badrsnie; /* rx assoc w/ bad RSN IE */ + u_int32_t is_rx_unauth; /* rx port not valid */ + u_int32_t is_tx_noauth; /* tx port not valid */ + u_int32_t is_rx_eapol_key; /* rx eapol-key frames */ + u_int32_t is_rx_eapol_replay; /* rx replayed eapol frames */ + u_int32_t is_rx_eapol_badmic; /* rx eapol frames w/ bad mic */ + u_int32_t is_rx_remmicfail; /* rx tkip remote mic fails */ + u_int32_t is_rx_locmicfail; /* rx tkip local mic fails */ }; #define SIOCG80211STATS _IOWR('i', 242, struct ifreq) @@ -171,6 +178,49 @@ struct ieee80211_txpower { #define IEEE80211_TXPOWER_MODE_FIXED 0 /* fixed tx power value */ #define IEEE80211_TXPOWER_MODE_AUTO 1 /* auto level control */ +struct ieee80211_wpapsk { + char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */ + int i_enabled; + u_int8_t i_psk[32]; +}; + +#define SIOCS80211WPAPSK _IOW('i', 245, struct ieee80211_wpapsk) +#define SIOCG80211WPAPSK _IOWR('i', 246, struct ieee80211_wpapsk) + +#define IEEE80211_WPA_PROTO_WPA1 0x01 +#define IEEE80211_WPA_PROTO_WPA2 0x02 + +#define IEEE80211_WPA_CIPHER_NONE 0x00 +#define IEEE80211_WPA_CIPHER_USEGROUP 0x01 +#define IEEE80211_WPA_CIPHER_WEP40 0x02 +#define IEEE80211_WPA_CIPHER_TKIP 0x04 +#define IEEE80211_WPA_CIPHER_CCMP 0x08 +#define IEEE80211_WPA_CIPHER_WEP104 0x10 + +#define IEEE80211_WPA_AKM_PSK 0x01 +#define IEEE80211_WPA_AKM_IEEE8021X 0x02 + +struct ieee80211_wpaparams { + char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */ + int i_enabled; + u_int i_protos; + u_int i_akms; + u_int i_ciphers; + u_int i_groupcipher; +}; + +#define SIOCS80211WPAPARMS _IOW('i', 247, struct ieee80211_wpaparams) +#define SIOCG80211WPAPARMS _IOWR('i', 248, struct ieee80211_wpaparams) + +struct ieee80211_wmmparams { + char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */ + int i_enabled; + /* XXX more */ +}; + +#define SIOCS80211WMMPARMS _IOW('i', 249, struct ieee80211_wmmparams) +#define SIOCG80211WMMPARMS _IOWR('i', 250, struct ieee80211_wmmparams) + /* scan request (will block) */ #define IEEE80211_SCAN_TIMEOUT 30 /* timeout in seconds */ @@ -210,6 +260,8 @@ struct ieee80211_nodereq { u_int8_t nr_txrate; /* index to nr_rates[] */ u_int16_t nr_state; /* node state in the cache */ + /* XXX RSN */ + /* Node flags */ u_int8_t nr_flags; }; diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c index 1384c998a10..53f4fe4af29 100644 --- a/sys/net80211/ieee80211_node.c +++ b/sys/net80211/ieee80211_node.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_node.c,v 1.30 2007/10/29 15:40:23 chl Exp $ */ +/* $OpenBSD: ieee80211_node.c,v 1.31 2008/04/16 18:32:15 damien Exp $ */ /* $NetBSD: ieee80211_node.c,v 1.14 2004/05/09 09:18:47 dyoung Exp $ */ /*- @@ -79,7 +79,9 @@ void ieee80211_setup_node(struct ieee80211com *, struct ieee80211_node *, void ieee80211_free_node(struct ieee80211com *, struct ieee80211_node *); struct ieee80211_node *ieee80211_alloc_node_helper(struct ieee80211com *); void ieee80211_node_cleanup(struct ieee80211com *, struct ieee80211_node *); +void ieee80211_node_join_rsn(struct ieee80211com *, struct ieee80211_node *); void ieee80211_node_join_11g(struct ieee80211com *, struct ieee80211_node *); +void ieee80211_node_leave_rsn(struct ieee80211com *, struct ieee80211_node *); void ieee80211_node_leave_11g(struct ieee80211com *, struct ieee80211_node *); void ieee80211_set_tim(struct ieee80211com *, int, int); @@ -110,7 +112,6 @@ ieee80211_node_attach(struct ifnet *ifp) printf("%s: no memory for AID bitmap!\n", __func__); ic->ic_max_aid = 0; } - if (ic->ic_caps & (IEEE80211_C_HOSTAP | IEEE80211_C_IBSS)) { ic->ic_tim_len = howmany(ic->ic_max_aid, 8); ic->ic_tim_bitmap = malloc(ic->ic_tim_len, M_DEVBUF, @@ -120,6 +121,8 @@ ieee80211_node_attach(struct ifnet *ifp) ic->ic_tim_len = 0; } else ic->ic_set_tim = ieee80211_set_tim; + timeout_set(&ic->ic_rsn_timeout, + ieee80211_gtk_rekey_timeout, ic); } } @@ -299,6 +302,36 @@ ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan) ni->ni_capinfo = IEEE80211_CAPINFO_IBSS; if (ic->ic_flags & IEEE80211_F_WEPON) ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY; + if (ic->ic_flags & IEEE80211_F_RSNON) { + struct ieee80211_key *k; + u_int8_t gtk[IEEE80211_PMK_LEN]; + + /* initialize 256-bit global key counter to a random value */ + arc4random_bytes(ic->ic_globalcnt, EAPOL_KEY_NONCE_LEN); + + ni->ni_rsnprotos = ic->ic_rsnprotos; + ni->ni_rsnakms = ic->ic_rsnakms; + ni->ni_rsnciphers = ic->ic_rsnciphers; + ni->ni_rsngroupcipher = ic->ic_rsngroupcipher; + ni->ni_rsncaps = 0; + + ic->ic_def_txkey = 1; + k = &ic->ic_nw_keys[ic->ic_def_txkey]; + arc4random_bytes(gtk, sizeof(gtk)); + ieee80211_map_gtk(gtk, ni->ni_rsngroupcipher, + ic->ic_def_txkey, 1, 0, k); + (*ic->ic_set_key)(ic, ni, k); /* XXX */ + + /* + * In HostAP mode, multicast traffic is sent using ic_bss + * as the Tx node, so mark our node as valid so we can send + * multicast frames using the group key we've just configured. + */ + ni->ni_port_valid = 1; + + /* schedule a GTK rekeying after 3600s */ + timeout_add(&ic->ic_rsn_timeout, 3600 * hz); + } if (ic->ic_phytype == IEEE80211_T_FH) { ni->ni_fhdwell = 200; /* XXX */ ni->ni_fhindex = 1; @@ -325,14 +358,14 @@ ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni) if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0) fail |= 0x02; } - if (ic->ic_flags & IEEE80211_F_WEPON) { + if (ic->ic_flags & (IEEE80211_F_WEPON | IEEE80211_F_RSNON)) { if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) fail |= 0x04; } else { - /* XXX does this mean privacy is supported or required? */ if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) fail |= 0x04; } + rate = ieee80211_fix_rate(ic, ni, IEEE80211_F_DONEGO); if (rate & IEEE80211_RATE_BASIC) fail |= 0x08; @@ -343,6 +376,24 @@ ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni) if ((ic->ic_flags & IEEE80211_F_DESBSSID) && !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid)) fail |= 0x20; + + if (ic->ic_flags & IEEE80211_F_RSNON) { + /* + * If at least one RSN IE field from the AP's RSN IE fails + * to overlap with any value the STA supports, the STA shall + * decline to associate with that AP. + */ + if ((ni->ni_rsnprotos & ic->ic_rsnprotos) == 0) + fail |= 0x40; + if ((ni->ni_rsnakms & ic->ic_rsnakms) == 0) + fail |= 0x40; + if ((ni->ni_rsnakms & ic->ic_rsnakms) == IEEE80211_AKM_PSK && + !(ic->ic_flags & IEEE80211_F_PSK)) + fail |= 0x40; + if ((ni->ni_rsnciphers & ic->ic_rsnciphers) == 0) + fail |= 0x40; + } + #ifdef IEEE80211_DEBUG if (ic->ic_if.if_flags & IFF_DEBUG) { printf(" %c %s", fail ? '-' : '+', @@ -359,10 +410,14 @@ ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni) (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" : "????", fail & 0x02 ? '!' : ' '); - printf(" %3s%c ", + printf(" %7s%c ", (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) ? - "wep" : "no", + "privacy" : "no", fail & 0x04 ? '!' : ' '); + printf(" %3s%c ", + (ic->ic_flags & IEEE80211_F_RSNON) ? + "rsn" : "no", + fail & 0x40 ? '!' : ' '); ieee80211_print_essid(ni->ni_essid, ni->ni_esslen); printf("%s\n", fail & 0x10 ? "!" : ""); } @@ -472,20 +527,51 @@ ieee80211_end_scan(struct ifnet *ifp) if (selbs == NULL) goto notfound; (*ic->ic_node_copy)(ic, ic->ic_bss, selbs); + ni = ic->ic_bss; /* * Set the erp state (mostly the slot time) to deal with * the auto-select case; this should be redundant if the * mode is locked. */ - ic->ic_curmode = ieee80211_chan2mode(ic, selbs->ni_chan); + ic->ic_curmode = ieee80211_chan2mode(ic, ni->ni_chan); ieee80211_reset_erp(ic); + if (ic->ic_flags & IEEE80211_F_RSNON) { + /* prefer RSN (WPA2) over WPA */ + ni->ni_rsnprotos &= ic->ic_rsnprotos; + if (ni->ni_rsnprotos & IEEE80211_PROTO_RSN) + ni->ni_rsnprotos = IEEE80211_PROTO_RSN; + else + ni->ni_rsnprotos = IEEE80211_PROTO_WPA; + /* + * If a pre-shared key is configured and AP supports PSK, + * choose PSK as AKMP. + */ + ni->ni_rsnakms &= ic->ic_rsnakms; + if ((ni->ni_rsnakms & IEEE80211_AKM_PSK) && + (ic->ic_flags & IEEE80211_F_PSK)) + ni->ni_rsnakms = IEEE80211_AKM_PSK; + else + ni->ni_rsnakms = IEEE80211_AKM_IEEE8021X; + + /* prefer CCMP over TKIP if the AP supports it */ + ni->ni_rsnciphers &= ic->ic_rsnciphers; + if (ni->ni_rsnciphers & IEEE80211_CIPHER_CCMP) + ni->ni_rsnciphers = IEEE80211_CIPHER_CCMP; + else + ni->ni_rsnciphers = IEEE80211_CIPHER_TKIP; + + ni->ni_rsncipher = ni->ni_rsnciphers; + + } else if (ic->ic_flags & IEEE80211_F_WEPON) + ni->ni_rsncipher = IEEE80211_CIPHER_USEGROUP; + ieee80211_node_newstate(selbs, IEEE80211_STA_BSS); if (ic->ic_opmode == IEEE80211_M_IBSS) { - ieee80211_fix_rate(ic, ic->ic_bss, IEEE80211_F_DOFRATE | + ieee80211_fix_rate(ic, ni, IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO | IEEE80211_F_DODEL); - if (ic->ic_bss->ni_rates.rs_nrates == 0) + if (ni->ni_rates.rs_nrates == 0) goto notfound; ieee80211_new_state(ic, IEEE80211_S_RUN, -1); } else { @@ -529,6 +615,10 @@ ieee80211_node_alloc(struct ieee80211com *ic) void ieee80211_node_cleanup(struct ieee80211com *ic, struct ieee80211_node *ni) { + if (ni->ni_rsnie != NULL) { + free(ni->ni_rsnie, M_DEVBUF); + ni->ni_rsnie = NULL; + } } void @@ -544,6 +634,9 @@ ieee80211_node_copy(struct ieee80211com *ic, { ieee80211_node_cleanup(ic, dst); *dst = *src; + dst->ni_rsnie = NULL; + if (src->ni_rsnie != NULL) + ieee80211_save_ie(src->ni_rsnie, &dst->ni_rsnie); } u_int8_t @@ -564,6 +657,8 @@ ieee80211_setup_node(struct ieee80211com *ic, IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr); ieee80211_node_newstate(ni, IEEE80211_STA_CACHE); + ni->ni_ic = ic; /* back-pointer */ + /* * Note we don't enable the inactive timer when acting * as a station. Nodes created in this mode represent @@ -945,6 +1040,37 @@ ieee80211_iserp_sta(const struct ieee80211_node *ni) } /* + * Handle a station joining an RSN network. + */ +void +ieee80211_node_join_rsn(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + IEEE80211_DPRINTF(("station %s associated using proto %d akm 0x%x " + "cipher 0x%x groupcipher 0x%x\n", ether_sprintf(ni->ni_macaddr), + ni->ni_rsnprotos, ni->ni_rsnakms, ni->ni_rsnciphers, + ni->ni_rsngroupcipher)); + + ni->ni_rsn_state = RSNA_AUTHENTICATION; + ic->ic_rsnsta++; + + ni->ni_key_count = 0; + ni->ni_port_valid = 0; + ni->ni_replaycnt = -1; /* XXX */ + ni->ni_rsn_retries = 0; + ni->ni_rsncipher = ni->ni_rsnciphers; + timeout_set(&ni->ni_rsn_timeout, ieee80211_eapol_timeout, ni); + + ni->ni_rsn_state = RSNA_AUTHENTICATION_2; + + /* generate a new authenticator nonce (ANonce) */ + arc4random_bytes(ni->ni_nonce, EAPOL_KEY_NONCE_LEN); + + /* initiate 4-way handshake */ + if (ni->ni_rsnakms == IEEE80211_AKM_PSK) + (void)ieee80211_send_4way_msg1(ic, ni); +} + +/* * Handle a station joining an 11g network. */ void @@ -1032,6 +1158,12 @@ ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_SUCCESS); ieee80211_node_newstate(ni, IEEE80211_STA_ASSOC); + if (!(ic->ic_flags & IEEE80211_F_RSNON)) { + ni->ni_port_valid = 1; + ni->ni_rsncipher = IEEE80211_CIPHER_USEGROUP; + } else + ieee80211_node_join_rsn(ic, ni); + #if NBRIDGE > 0 /* * If the parent interface belongs to a bridge, learn @@ -1044,6 +1176,29 @@ ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, } /* + * Handle a station leaving an RSN network. + */ +void +ieee80211_node_leave_rsn(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + ni->ni_rsn_state = RSNA_DISCONNECTED; + ic->ic_rsnsta--; + + ni->ni_rsn_state = RSNA_INITIALIZE; + 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; + + timeout_del(&ni->ni_rsn_timeout); + ni->ni_rsn_retries = 0; + ni->ni_port_valid = 0; + (*ic->ic_delete_key)(ic, ni, &ni->ni_pairwise_key); +} + +/* * Handle a station leaving an 11g network. */ void @@ -1114,6 +1269,9 @@ ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni) IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap); ni->ni_associd = 0; + if (ic->ic_flags & IEEE80211_F_RSNON) + ieee80211_node_leave_rsn(ic, ni); + if (ic->ic_curmode == IEEE80211_MODE_11G) ieee80211_node_leave_11g(ic, ni); diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h index f7d971a78e9..4cb40c5d397 100644 --- a/sys/net80211/ieee80211_node.h +++ b/sys/net80211/ieee80211_node.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_node.h,v 1.25 2007/11/03 14:59:55 mglocker Exp $ */ +/* $OpenBSD: ieee80211_node.h,v 1.26 2008/04/16 18:32:15 damien Exp $ */ /* $NetBSD: ieee80211_node.h,v 1.9 2004/04/30 22:57:32 dyoung Exp $ */ /*- @@ -65,8 +65,9 @@ enum ieee80211_node_state { (__ni)->ni_state = (__state); \ } while (0) -/* RSNA Authenticator state machine (see 8.5.6). */ +/* Authenticator state machine: 4-Way Handshake (see 8.5.6.1.1) */ enum { + RSNA_INITIALIZE, RSNA_AUTHENTICATION, RSNA_AUTHENTICATION_2, RSNA_INITPMK, @@ -77,15 +78,15 @@ enum { RSNA_PTKINITNEGOTIATING, RSNA_PTKINITDONE, RSNA_DISCONNECT, - RSNA_DISCONNECTED, - RSNA_INITIALIZE, + RSNA_DISCONNECTED +}; + +/* Authenticator state machine: Group Key Handshake (see 8.5.6.1.2) */ +enum { RSNA_IDLE, RSNA_REKEYNEGOTIATING, - RSNA_KEYERROR, RSNA_REKEYESTABLISHED, - RSNA_GTK_INIT, - RSNA_SETKEYSDONE, - RSNA_SETKEYS + RSNA_KEYERROR }; /* @@ -97,6 +98,8 @@ enum { struct ieee80211_node { RB_ENTRY(ieee80211_node) ni_node; + struct ieee80211com *ni_ic; /* back-pointer */ + u_int ni_refcnt; u_int ni_scangen; /* gen# for timeout scan */ @@ -137,22 +140,25 @@ struct ieee80211_node { /* RSN */ u_int ni_rsn_state; - u_int ni_rsn_tocnt; - u_int ni_group_cipher; - enum ieee80211_cipher ni_pairwise_cipher; - u_int ni_pairwise_cipherset; - enum ieee80211_akm ni_akm; - u_int ni_akmset; + u_int ni_rsn_gstate; + u_int ni_rsn_retries; + struct timeout ni_rsn_timeout; + u_int ni_rsnprotos; + u_int ni_rsnakms; + u_int ni_rsnciphers; + enum ieee80211_cipher ni_rsngroupcipher; u_int16_t ni_rsncaps; - int ni_port_valid; - u_int8_t ni_eapol_desc; + enum ieee80211_cipher ni_rsncipher; u_int8_t ni_nonce[EAPOL_KEY_NONCE_LEN]; u_int64_t ni_replaycnt; u_int8_t ni_replaycnt_ok; + u_int64_t ni_reqreplaycnt; + u_int8_t ni_reqreplaycnt_ok; u_int8_t *ni_rsnie; struct ieee80211_key ni_pairwise_key; struct ieee80211_ptk ni_ptk; u_int8_t ni_key_count; + int ni_port_valid; /* others */ u_int16_t ni_associd; /* assoc response */ @@ -168,6 +174,7 @@ struct ieee80211_node { u_int8_t ni_flags; /* special-purpose state */ #define IEEE80211_NODE_ERP 0x01 #define IEEE80211_NODE_QOS 0x02 +#define IEEE80211_NODE_REKEY 0x04 }; RB_HEAD(ieee80211_tree, ieee80211_node); diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c index 6523e409cf8..33654eb2d32 100644 --- a/sys/net80211/ieee80211_output.c +++ b/sys/net80211/ieee80211_output.c @@ -1,10 +1,10 @@ -/* $OpenBSD: ieee80211_output.c,v 1.58 2007/08/27 20:14:21 damien Exp $ */ +/* $OpenBSD: ieee80211_output.c,v 1.59 2008/04/16 18:32:15 damien Exp $ */ /* $NetBSD: ieee80211_output.c,v 1.13 2004/05/31 11:02:55 dyoung 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 @@ -65,15 +65,13 @@ #include <net80211/ieee80211_var.h> -#include <dev/rndvar.h> - enum ieee80211_edca_ac ieee80211_up_to_ac(struct ieee80211com *, int); int ieee80211_classify(struct ieee80211com *, struct mbuf *); int ieee80211_mgmt_output(struct ifnet *, struct ieee80211_node *, struct mbuf *, int); u_int8_t *ieee80211_add_rsn_body(u_int8_t *, struct ieee80211com *, const struct ieee80211_node *, int); -struct mbuf *ieee80211_getmbuf(int, int, u_int); +struct mbuf *ieee80211_getmgmt(int, int, u_int); struct mbuf *ieee80211_get_probe_req(struct ieee80211com *, struct ieee80211_node *); struct mbuf *ieee80211_get_probe_resp(struct ieee80211com *, @@ -89,8 +87,9 @@ struct mbuf *ieee80211_get_assoc_resp(struct ieee80211com *, struct mbuf *ieee80211_get_disassoc(struct ieee80211com *, struct ieee80211_node *, u_int16_t); int ieee80211_send_eapol_key(struct ieee80211com *, struct mbuf *, - struct ieee80211_node *); -u_int8_t *ieee80211_add_gtk_kde(u_int8_t *, const struct ieee80211_key *); + struct ieee80211_node *, const struct ieee80211_ptk *); +u_int8_t *ieee80211_add_gtk_kde(u_int8_t *, struct ieee80211_node *, + const struct ieee80211_key *); u_int8_t *ieee80211_add_pmkid_kde(u_int8_t *, const u_int8_t *); struct mbuf *ieee80211_get_eapol_key(int, int, u_int); @@ -507,14 +506,19 @@ ieee80211_encap(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node **pni) ic->ic_stats.is_tx_nonode++; goto bad; } -#if 0 - if (!ni->ni_port_valid && eh.ether_type != htons(ETHERTYPE_PAE)) { + + 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(eh.ether_dhost))); ic->ic_stats.is_tx_noauth++; goto bad; } -#endif + + if ((ic->ic_flags & IEEE80211_F_COUNTERM) && + ni->ni_rsncipher == IEEE80211_CIPHER_TKIP) + /* XXX TKIP countermeasures! */; + ni->ni_inact = 0; if ((ic->ic_flags & IEEE80211_F_QOS) && @@ -581,8 +585,11 @@ ieee80211_encap(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node **pni) case IEEE80211_M_MONITOR: goto bad; } - if (ic->ic_flags & IEEE80211_F_WEPON) - wh->i_fc[1] |= IEEE80211_FC1_WEP; + + if ((ic->ic_flags & IEEE80211_F_WEPON) || + ((ic->ic_flags & IEEE80211_F_RSNON) && ni->ni_port_valid)) + wh->i_fc[1] |= IEEE80211_FC1_PROTECTED; + *pni = ni; return m; bad: @@ -615,7 +622,8 @@ ieee80211_add_capinfo(u_int8_t *frm, struct ieee80211com *ic, capinfo = IEEE80211_CAPINFO_ESS; else capinfo = 0; - if (ic->ic_flags & IEEE80211_F_WEPON) + if (ic->ic_opmode == IEEE80211_M_HOSTAP && + (ic->ic_flags & (IEEE80211_F_WEPON | IEEE80211_F_RSNON))) capinfo |= IEEE80211_CAPINFO_PRIVACY; /* NB: some 11a AP's reject the request when short preamble is set */ if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && @@ -814,9 +822,9 @@ ieee80211_add_qos_capability(u_int8_t *frm, struct ieee80211com *ic) */ u_int8_t * ieee80211_add_rsn_body(u_int8_t *frm, struct ieee80211com *ic, - const struct ieee80211_node *ni, int wpa1) + const struct ieee80211_node *ni, int wpa) { - const u_int8_t *oui = wpa1 ? MICROSOFT_OUI : IEEE80211_OUI; + const u_int8_t *oui = wpa ? MICROSOFT_OUI : IEEE80211_OUI; u_int8_t *pcount; u_int16_t count; @@ -825,11 +833,7 @@ ieee80211_add_rsn_body(u_int8_t *frm, struct ieee80211com *ic, /* write Group Cipher Suite field (see Table 20da) */ memcpy(frm, oui, 3); frm += 3; - switch (ni->ni_group_cipher) { - case IEEE80211_CIPHER_USEGROUP: - /* can't get there */ - panic("invalid group cipher!"); - break; + switch (ni->ni_rsngroupcipher) { case IEEE80211_CIPHER_WEP40: *frm++ = 1; break; @@ -842,22 +846,25 @@ ieee80211_add_rsn_body(u_int8_t *frm, struct ieee80211com *ic, case IEEE80211_CIPHER_WEP104: *frm++ = 5; break; + default: + /* can't get there */ + panic("invalid group cipher!"); } pcount = frm; frm += 2; count = 0; /* write Pairwise Cipher Suite List */ - if (ni->ni_pairwise_cipherset & IEEE80211_CIPHER_USEGROUP) { + if (ni->ni_rsnciphers & IEEE80211_CIPHER_USEGROUP) { memcpy(frm, oui, 3); frm += 3; *frm++ = 0; count++; } - if (ni->ni_pairwise_cipherset & IEEE80211_CIPHER_TKIP) { + if (ni->ni_rsnciphers & IEEE80211_CIPHER_TKIP) { memcpy(frm, oui, 3); frm += 3; *frm++ = 2; count++; } - if (ni->ni_pairwise_cipherset & IEEE80211_CIPHER_CCMP) { + if (ni->ni_rsnciphers & IEEE80211_CIPHER_CCMP) { memcpy(frm, oui, 3); frm += 3; *frm++ = 4; count++; @@ -868,12 +875,12 @@ ieee80211_add_rsn_body(u_int8_t *frm, struct ieee80211com *ic, pcount = frm; frm += 2; count = 0; /* write AKM Suite List (see Table 20dc) */ - if (ni->ni_akmset & IEEE80211_AKM_IEEE8021X) { + if (ni->ni_rsnakms & IEEE80211_AKM_IEEE8021X) { memcpy(frm, oui, 3); frm += 3; *frm++ = 1; count++; } - if (ni->ni_akmset & IEEE80211_AKM_PSK) { + if (ni->ni_rsnakms & IEEE80211_AKM_PSK) { memcpy(frm, oui, 3); frm += 3; *frm++ = 2; count++; @@ -881,11 +888,12 @@ ieee80211_add_rsn_body(u_int8_t *frm, struct ieee80211com *ic, /* write AKM Suite List Count field */ LE_WRITE_2(pcount, count); - /* write RSN Capabilities field */ - LE_WRITE_2(frm, ni->ni_rsncaps); frm += 2; - - /* no PMKID List for now */ + if (!wpa) { + /* write RSN Capabilities field */ + LE_WRITE_2(frm, ni->ni_rsncaps); frm += 2; + /* no PMKID List for now */ + } return frm; } @@ -905,11 +913,11 @@ ieee80211_add_rsn(u_int8_t *frm, struct ieee80211com *ic, } /* - * Add a vendor specific WPA1 element to a frame. - * This is required for compatibility with Wi-Fi Alliance WPA1/WPA1+WPA2. + * Add a vendor-specific WPA element to a frame. + * This is required for compatibility with Wi-Fi Alliance WPA. */ u_int8_t * -ieee80211_add_wpa1(u_int8_t *frm, struct ieee80211com *ic, +ieee80211_add_wpa(u_int8_t *frm, struct ieee80211com *ic, const struct ieee80211_node *ni) { u_int8_t *plen; @@ -917,7 +925,7 @@ ieee80211_add_wpa1(u_int8_t *frm, struct ieee80211com *ic, *frm++ = IEEE80211_ELEMID_VENDOR; plen = frm++; /* length filled in later */ memcpy(frm, MICROSOFT_OUI, 3); frm += 3; - *frm++ = 1; /* WPA1 */ + *frm++ = 1; /* WPA */ frm = ieee80211_add_rsn_body(frm, ic, ni, 1); /* write length field */ @@ -943,21 +951,24 @@ ieee80211_add_xrates(u_int8_t *frm, const struct ieee80211_rateset *rs) } struct mbuf * -ieee80211_getmbuf(int flags, int type, u_int pktlen) +ieee80211_getmgmt(int flags, int type, u_int pktlen) { struct mbuf *m; - /* account for 802.11 header */ + /* reserve space for 802.11 header */ pktlen += sizeof(struct ieee80211_frame); if (pktlen > MCLBYTES) - panic("802.11 packet too large: %u", pktlen); + panic("management frame too large: %u", pktlen); MGETHDR(m, flags, type); - if (m != NULL && pktlen > MHLEN) { + if (m == NULL) + return NULL; + if (pktlen >= MINCLSIZE) { MCLGET(m, flags); if (!(m->m_flags & M_EXT)) - m = m_free(m); + return m_free(m); } + m->m_data += sizeof(struct ieee80211_frame); return m; } @@ -975,7 +986,7 @@ ieee80211_get_probe_req(struct ieee80211com *ic, struct ieee80211_node *ni) struct mbuf *m; u_int8_t *frm; - m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA, + m = ieee80211_getmgmt(M_DONTWAIT, MT_DATA, 2 + ic->ic_des_esslen + 2 + min(rs->rs_nrates, IEEE80211_RATE_SIZE) + ((rs->rs_nrates > IEEE80211_RATE_SIZE) ? @@ -983,8 +994,6 @@ ieee80211_get_probe_req(struct ieee80211com *ic, struct ieee80211_node *ni) if (m == NULL) return NULL; - m->m_data += sizeof(struct ieee80211_frame); - frm = mtod(m, u_int8_t *); frm = ieee80211_add_ssid(frm, ic->ic_des_essid, ic->ic_des_esslen); frm = ieee80211_add_rates(frm, rs); @@ -1017,7 +1026,7 @@ ieee80211_get_probe_resp(struct ieee80211com *ic, struct ieee80211_node *ni) struct mbuf *m; u_int8_t *frm; - m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA, + m = ieee80211_getmgmt(M_DONTWAIT, MT_DATA, 8 + 2 + 2 + 2 + ni->ni_esslen + 2 + min(rs->rs_nrates, IEEE80211_RATE_SIZE) + @@ -1026,14 +1035,14 @@ ieee80211_get_probe_resp(struct ieee80211com *ic, struct ieee80211_node *ni) ((ic->ic_curmode == IEEE80211_MODE_11G) ? 2 + 1 : 0) + ((rs->rs_nrates > IEEE80211_RATE_SIZE) ? 2 + rs->rs_nrates - IEEE80211_RATE_SIZE : 0) + - ((ic->ic_flags & IEEE80211_F_RSN) ? 2 + 44 : 0) + + (((ic->ic_flags & IEEE80211_F_RSNON) && + (ic->ic_bss->ni_rsnprotos & IEEE80211_PROTO_RSN)) ? 2 + 44 : 0) + ((ic->ic_flags & IEEE80211_F_QOS) ? 2 + 18 : 0) + - ((ic->ic_flags & IEEE80211_F_WPA1) ? 2 + 48 : 0)); + (((ic->ic_flags & IEEE80211_F_RSNON) && + (ic->ic_bss->ni_rsnprotos & IEEE80211_PROTO_WPA)) ? 2 + 48 : 0)); if (m == NULL) return NULL; - m->m_data += sizeof(struct ieee80211_frame); - frm = mtod(m, u_int8_t *); memset(frm, 0, 8); frm += 8; /* timestamp is set by hardware */ LE_WRITE_2(frm, ic->ic_bss->ni_intval); frm += 2; @@ -1051,12 +1060,14 @@ ieee80211_get_probe_resp(struct ieee80211com *ic, struct ieee80211_node *ni) frm = ieee80211_add_erp(frm, ic); if (rs->rs_nrates > IEEE80211_RATE_SIZE) frm = ieee80211_add_xrates(frm, rs); - if (ic->ic_flags & IEEE80211_F_RSN) + if ((ic->ic_flags & IEEE80211_F_RSNON) && + (ic->ic_bss->ni_rsnprotos & IEEE80211_PROTO_RSN)) frm = ieee80211_add_rsn(frm, ic, ic->ic_bss); if (ic->ic_flags & IEEE80211_F_QOS) frm = ieee80211_add_edca_params(frm, ic); - if (ic->ic_flags & IEEE80211_F_WPA1) - frm = ieee80211_add_wpa1(frm, ic, ic->ic_bss); + if ((ic->ic_flags & IEEE80211_F_RSNON) && + (ic->ic_bss->ni_rsnprotos & IEEE80211_PROTO_WPA)) + frm = ieee80211_add_wpa(frm, ic, ic->ic_bss); m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); @@ -1131,7 +1142,7 @@ ieee80211_get_assoc_req(struct ieee80211com *ic, struct ieee80211_node *ni, u_int8_t *frm; u_int16_t capinfo; - m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA, + m = ieee80211_getmgmt(M_DONTWAIT, MT_DATA, 2 + 2 + ((reassoc == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) ? IEEE80211_ADDR_LEN : 0) + @@ -1139,14 +1150,14 @@ ieee80211_get_assoc_req(struct ieee80211com *ic, struct ieee80211_node *ni, 2 + min(rs->rs_nrates, IEEE80211_RATE_SIZE) + ((rs->rs_nrates > IEEE80211_RATE_SIZE) ? 2 + rs->rs_nrates - IEEE80211_RATE_SIZE : 0) + - ((ic->ic_flags & IEEE80211_F_RSN) ? 2 + 44 : 0) + + (((ic->ic_flags & IEEE80211_F_RSNON) && + (ni->ni_rsnprotos & IEEE80211_PROTO_RSN)) ? 2 + 44 : 0) + ((ic->ic_flags & IEEE80211_F_QOS) ? 2 + 1 : 0) + - ((ic->ic_flags & IEEE80211_F_WPA1) ? 2 + 48 : 0)); + (((ic->ic_flags & IEEE80211_F_RSNON) && + (ni->ni_rsnprotos & IEEE80211_PROTO_WPA)) ? 2 + 48 : 0)); if (m == NULL) return NULL; - m->m_data += sizeof(struct ieee80211_frame); - frm = mtod(m, u_int8_t *); capinfo = IEEE80211_CAPINFO_ESS; if (ic->ic_flags & IEEE80211_F_WEPON) @@ -1159,7 +1170,7 @@ ieee80211_get_assoc_req(struct ieee80211com *ic, struct ieee80211_node *ni, capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; LE_WRITE_2(frm, capinfo); frm += 2; LE_WRITE_2(frm, ic->ic_lintval); frm += 2; - if (reassoc == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { + if (reassoc) { IEEE80211_ADDR_COPY(frm, ic->ic_bss->ni_bssid); frm += IEEE80211_ADDR_LEN; } @@ -1167,13 +1178,15 @@ ieee80211_get_assoc_req(struct ieee80211com *ic, struct ieee80211_node *ni, frm = ieee80211_add_rates(frm, rs); if (rs->rs_nrates > IEEE80211_RATE_SIZE) frm = ieee80211_add_xrates(frm, rs); - if (ic->ic_flags & IEEE80211_F_RSN) - frm = ieee80211_add_rsn(frm, ic, ic->ic_bss); + if ((ic->ic_flags & IEEE80211_F_RSNON) && + (ni->ni_rsnprotos & IEEE80211_PROTO_RSN)) + frm = ieee80211_add_rsn(frm, ic, ni); if ((ic->ic_flags & IEEE80211_F_QOS) && (ni->ni_flags & IEEE80211_NODE_QOS)) frm = ieee80211_add_qos_capability(frm, ic); - if (ic->ic_flags & IEEE80211_F_WPA1) - frm = ieee80211_add_wpa1(frm, ic, ic->ic_bss); + if ((ic->ic_flags & IEEE80211_F_RSNON) && + (ni->ni_rsnprotos & IEEE80211_PROTO_WPA)) + frm = ieee80211_add_wpa(frm, ic, ni); m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); @@ -1197,7 +1210,7 @@ ieee80211_get_assoc_resp(struct ieee80211com *ic, struct ieee80211_node *ni, struct mbuf *m; u_int8_t *frm; - m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA, + m = ieee80211_getmgmt(M_DONTWAIT, MT_DATA, 2 + 2 + 2 + 2 + min(rs->rs_nrates, IEEE80211_RATE_SIZE) + ((rs->rs_nrates > IEEE80211_RATE_SIZE) ? @@ -1206,8 +1219,6 @@ ieee80211_get_assoc_resp(struct ieee80211com *ic, struct ieee80211_node *ni, if (m == NULL) return NULL; - m->m_data += sizeof(struct ieee80211_frame); - frm = mtod(m, u_int8_t *); frm = ieee80211_add_capinfo(frm, ic, ni); LE_WRITE_2(frm, status); frm += 2; @@ -1362,7 +1373,7 @@ ieee80211_get_rts(struct ieee80211com *ic, const struct ieee80211_frame *wh, if (m == NULL) return NULL; - m->m_pkthdr.len = m->m_len = sizeof (struct ieee80211_frame_rts); + m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_rts); rts = mtod(m, struct ieee80211_frame_rts *); rts->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_CTL | @@ -1388,7 +1399,7 @@ ieee80211_get_cts_to_self(struct ieee80211com *ic, u_int16_t dur) if (m == NULL) return NULL; - m->m_pkthdr.len = m->m_len = sizeof (struct ieee80211_frame_cts); + m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_cts); cts = mtod(m, struct ieee80211_frame_cts *); cts->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_CTL | @@ -1424,7 +1435,7 @@ ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni) struct mbuf *m; u_int8_t *frm; - m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA, + m = ieee80211_getmgmt(M_DONTWAIT, MT_DATA, 8 + 2 + 2 + 2 + ((ic->ic_flags & IEEE80211_F_HIDENWID) ? 0 : ni->ni_esslen) + 2 + min(rs->rs_nrates, IEEE80211_RATE_SIZE) + @@ -1433,12 +1444,17 @@ ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni) ((ic->ic_curmode == IEEE80211_MODE_11G) ? 2 + 1 : 0) + ((rs->rs_nrates > IEEE80211_RATE_SIZE) ? 2 + rs->rs_nrates - IEEE80211_RATE_SIZE : 0) + - ((ic->ic_flags & IEEE80211_F_RSN) ? 2 + 44 : 0) + + (((ic->ic_flags & IEEE80211_F_RSNON) && + (ni->ni_rsnprotos & IEEE80211_PROTO_RSN)) ? 2 + 44 : 0) + ((ic->ic_flags & IEEE80211_F_QOS) ? 2 + 18 : 0) + - ((ic->ic_flags & IEEE80211_F_WPA1) ? 2 + 48 : 0)); + (((ic->ic_flags & IEEE80211_F_RSNON) && + (ni->ni_rsnprotos & IEEE80211_PROTO_WPA)) ? 2 + 48 : 0)); if (m == NULL) return NULL; + M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); + if (m == NULL) + return NULL; wh = mtod(m, struct ieee80211_frame *); wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_BEACON; @@ -1470,12 +1486,14 @@ ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni) frm = ieee80211_add_erp(frm, ic); if (rs->rs_nrates > IEEE80211_RATE_SIZE) frm = ieee80211_add_xrates(frm, rs); - if (ic->ic_flags & IEEE80211_F_RSN) + if ((ic->ic_flags & IEEE80211_F_RSNON) && + (ni->ni_rsnprotos & IEEE80211_PROTO_RSN)) frm = ieee80211_add_rsn(frm, ic, ni); if (ic->ic_flags & IEEE80211_F_QOS) frm = ieee80211_add_edca_params(frm, ic); - if (ic->ic_flags & IEEE80211_F_WPA1) - frm = ieee80211_add_wpa1(frm, ic, ni); + if ((ic->ic_flags & IEEE80211_F_RSNON) && + (ni->ni_rsnprotos & IEEE80211_PROTO_WPA)) + frm = ieee80211_add_wpa(frm, ic, ni); m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); m->m_pkthdr.rcvif = (void *)ni; @@ -1499,16 +1517,53 @@ ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni) } while (0) /* unaligned little endian access */ -#define LE_WRITE_8(p, v) do { \ - (p)[7] = (v) >> 56; (p)[6] = (v) >> 48; \ +#define LE_WRITE_6(p, v) do { \ (p)[5] = (v) >> 40; (p)[4] = (v) >> 32; \ (p)[3] = (v) >> 24; (p)[2] = (v) >> 16; \ (p)[1] = (v) >> 8; (p)[0] = (v); \ } while (0) +/* + * Handle EAPOL-Key timeouts (no answer from supplicant). + */ +void +ieee80211_eapol_timeout(void *arg) +{ + struct ieee80211_node *ni = arg; + struct ieee80211com *ic = ni->ni_ic; + int s; + + IEEE80211_DPRINTF(("%s: no answer from station %s in state %d\n", + __func__, ether_sprintf(ni->ni_macaddr), ni->ni_rsn_state)); + + s = splnet(); + + switch (ni->ni_rsn_state) { + case RSNA_PTKSTART: + case RSNA_PTKCALCNEGOTIATING: + (void)ieee80211_send_4way_msg1(ic, ni); + break; + case RSNA_PTKINITNEGOTIATING: + (void)ieee80211_send_4way_msg3(ic, ni); + break; + } + + switch (ni->ni_rsn_gstate) { + case RSNA_REKEYNEGOTIATING: + (void)ieee80211_send_group_msg1(ic, ni); + break; + } + + splx(s); +} + +/* + * Send an EAPOL-Key frame to node `ni'. If MIC or encryption is required, + * the PTK must be passed (otherwise it can be set to NULL.) + */ int ieee80211_send_eapol_key(struct ieee80211com *ic, struct mbuf *m, - struct ieee80211_node *ni) + struct ieee80211_node *ni, const struct ieee80211_ptk *ptk) { struct ifnet *ifp = &ic->ic_if; struct ether_header *eh; @@ -1519,6 +1574,7 @@ ieee80211_send_eapol_key(struct ieee80211com *ic, struct mbuf *m, M_PREPEND(m, sizeof(struct ether_header), M_DONTWAIT); if (m == NULL) return ENOMEM; + /* no need to m_pullup here (ok by construction) */ eh = mtod(m, struct ether_header *); eh->ether_type = htons(ETHERTYPE_PAE); IEEE80211_ADDR_COPY(eh->ether_shost, ic->ic_myaddr); @@ -1527,43 +1583,60 @@ ieee80211_send_eapol_key(struct ieee80211com *ic, struct mbuf *m, key = (struct ieee80211_eapol_key *)&eh[1]; key->version = EAPOL_VERSION; key->type = EAPOL_KEY; - key->desc = ni->ni_eapol_desc; + key->desc = (ni->ni_rsnprotos == IEEE80211_PROTO_RSN) ? + EAPOL_KEY_DESC_IEEE80211 : EAPOL_KEY_DESC_WPA; info = BE_READ_2(key->info); - /* use V2 descriptor iff pairwise cipher is CCMP */ - info |= (ni->ni_pairwise_cipher != IEEE80211_CIPHER_CCMP) ? - EAPOL_KEY_DESC_V1 : EAPOL_KEY_DESC_V2; + /* use V2 descriptor if pairwise or group cipher is CCMP */ + if (ni->ni_rsncipher == IEEE80211_CIPHER_CCMP || + ni->ni_rsngroupcipher == IEEE80211_CIPHER_CCMP) + info |= EAPOL_KEY_DESC_V2; + else + info |= EAPOL_KEY_DESC_V1; BE_WRITE_2(key->info, info); len = m->m_len - sizeof(struct ether_header); BE_WRITE_2(key->paylen, len - sizeof(*key)); BE_WRITE_2(key->len, len - 4); - if (info & EAPOL_KEY_ENCRYPTED) - ieee80211_eapol_key_encrypt(ic, key, ni->ni_ptk.kek); + if (info & EAPOL_KEY_ENCRYPTED) { + if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) { + /* clear "Encrypted" bit for WPA */ + info &= ~EAPOL_KEY_ENCRYPTED; + BE_WRITE_2(key->info, info); + } + ieee80211_eapol_key_encrypt(ic, key, ptk->kek); + if ((info & EAPOL_KEY_VERSION_MASK) == EAPOL_KEY_DESC_V2) { + /* AES Key Wrap adds 8 bytes + padding */ + m->m_pkthdr.len = m->m_len = + sizeof(*eh) + 4 + BE_READ_2(key->len); + } + } if (info & EAPOL_KEY_KEYMIC) - ieee80211_eapol_key_mic(key, ni->ni_ptk.kck); + ieee80211_eapol_key_mic(key, ptk->kck); s = splnet(); + /* start a 100ms timeout if an answer is expected from supplicant */ + if (info & EAPOL_KEY_KEYACK) + timeout_add(&ni->ni_rsn_timeout, hz / 10); IFQ_ENQUEUE(&ifp->if_snd, m, NULL, error); - if (error) { - splx(s); - return error; + if (error == 0) { + ifp->if_obytes += m->m_pkthdr.len; + if ((ifp->if_flags & IFF_OACTIVE) == 0) + (*ifp->if_start)(ifp); } - ifp->if_obytes += m->m_pkthdr.len; - if ((ifp->if_flags & IFF_OACTIVE) == 0) - (*ifp->if_start)(ifp); splx(s); - return 0; + return error; } /* * Add a GTK KDE to an EAPOL-Key frame (see Figure 144). */ u_int8_t * -ieee80211_add_gtk_kde(u_int8_t *frm, const struct ieee80211_key *k) +ieee80211_add_gtk_kde(u_int8_t *frm, struct ieee80211_node *ni, + const struct ieee80211_key *k) { KASSERT(k->k_flags & IEEE80211_KEY_GROUP); @@ -1572,7 +1645,11 @@ ieee80211_add_gtk_kde(u_int8_t *frm, const struct ieee80211_key *k) memcpy(frm, IEEE80211_OUI, 3); frm += 3; *frm++ = IEEE80211_KDE_GTK; *frm = k->k_id & 3; - if (k->k_flags & IEEE80211_KEY_TX) + /* + * The TxRx flag for sending a GTK is always the opposite of whether + * the pairwise key is used for data encryption/integrity or not. + */ + if (ni->ni_rsncipher == IEEE80211_CIPHER_USEGROUP) *frm |= 1 << 2; /* set the Tx bit */ frm++; *frm++ = 0; /* reserved */ @@ -1599,18 +1676,21 @@ ieee80211_get_eapol_key(int flags, int type, u_int pktlen) { struct mbuf *m; - pktlen += sizeof(struct ether_header) + + /* reserve space for 802.11 encapsulation and EAPOL-Key header */ + pktlen += sizeof(struct ieee80211_frame) + sizeof(struct llc) + sizeof(struct ieee80211_eapol_key); if (pktlen > MCLBYTES) panic("EAPOL-Key frame too large: %u", pktlen); MGETHDR(m, flags, type); - if (m != NULL && pktlen > MHLEN) { + if (m == NULL) + return NULL; + if (pktlen >= MINCLSIZE) { MCLGET(m, flags); if (!(m->m_flags & M_EXT)) - m = m_free(m); + return m_free(m); } - m->m_data += sizeof(struct ether_header); + m->m_data += sizeof(struct ieee80211_frame) + sizeof(struct llc); return m; } @@ -1624,15 +1704,17 @@ ieee80211_send_4way_msg1(struct ieee80211com *ic, struct ieee80211_node *ni) struct ieee80211_eapol_key *key; struct mbuf *m; u_int16_t info, keylen; - u_int8_t *pmkid; u_int8_t *frm; ni->ni_rsn_state = RSNA_PTKSTART; - if (++ni->ni_rsn_tocnt == 3) - return 0; /* XXX move to RSNA_DISCONNECT */ - + if (++ni->ni_rsn_retries > 3) { + IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_4WAY_TIMEOUT); + ieee80211_node_leave(ic, ni); + return 0; + } m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, - (ni->ni_eapol_desc == EAPOL_KEY_DESC_IEEE80211) ? 2 + 20 : 0); + (ni->ni_rsnprotos == IEEE80211_PROTO_RSN) ? 2 + 20 : 0); if (m == NULL) return ENOMEM; key = mtod(m, struct ieee80211_eapol_key *); @@ -1644,14 +1726,15 @@ ieee80211_send_4way_msg1(struct ieee80211com *ic, struct ieee80211_node *ni) /* copy the authenticator's nonce (ANonce) */ memcpy(key->nonce, ni->ni_nonce, EAPOL_KEY_NONCE_LEN); - keylen = ieee80211_cipher_keylen(ni->ni_pairwise_cipher); + keylen = ieee80211_cipher_keylen(ni->ni_rsncipher); BE_WRITE_2(key->keylen, keylen); frm = (u_int8_t *)&key[1]; - /* WPA1 does not have PMKID KDE */ - if (ni->ni_eapol_desc == EAPOL_KEY_DESC_IEEE80211) { + /* WPA does not have PMKID KDE */ + if (ni->ni_rsnprotos == IEEE80211_PROTO_RSN && + ni->ni_rsnakms == IEEE80211_AKM_IEEE8021X) { /* XXX retrieve PMKID from the PMKSA cache */ - frm = ieee80211_add_pmkid_kde(frm, pmkid); + /* frm = ieee80211_add_pmkid_kde(frm, pmkid); */ } m->m_pkthdr.len = m->m_len = frm - (u_int8_t *)key; @@ -1661,7 +1744,10 @@ ieee80211_send_4way_msg1(struct ieee80211com *ic, struct ieee80211_node *ni) ic->ic_if.if_xname, 1, 4, "4-way", ether_sprintf(ni->ni_macaddr)); - return ieee80211_send_eapol_key(ic, m, ni); + ni->ni_replaycnt++; + BE_WRITE_8(key->replaycnt, ni->ni_replaycnt); + + return ieee80211_send_eapol_key(ic, m, ni, NULL); } /* @@ -1670,7 +1756,7 @@ ieee80211_send_4way_msg1(struct ieee80211com *ic, struct ieee80211_node *ni) */ int ieee80211_send_4way_msg2(struct ieee80211com *ic, struct ieee80211_node *ni, - const u_int8_t *snonce) + const u_int8_t *replaycnt, const struct ieee80211_ptk *tptk) { struct ieee80211_eapol_key *key; struct mbuf *m; @@ -1687,19 +1773,19 @@ ieee80211_send_4way_msg2(struct ieee80211com *ic, struct ieee80211_node *ni, info = EAPOL_KEY_PAIRWISE | EAPOL_KEY_KEYMIC; BE_WRITE_2(key->info, info); - /* copy key replay counter from authenticator */ - BE_WRITE_8(key->replaycnt, ni->ni_replaycnt); + /* copy key replay counter from Message 1/4 */ + memcpy(key->replaycnt, replaycnt, 8); /* copy the supplicant's nonce (SNonce) */ - memcpy(key->nonce, snonce, EAPOL_KEY_NONCE_LEN); + memcpy(key->nonce, ic->ic_nonce, EAPOL_KEY_NONCE_LEN); frm = (u_int8_t *)&key[1]; /* add the WPA/RSN IE used in the (Re)Association Request */ - if (ni->ni_eapol_desc == EAPOL_KEY_DESC_WPA1) { + if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) { u_int16_t keylen; - frm = ieee80211_add_wpa1(frm, ic, ni); - /* WPA1 sets the key length field here */ - keylen = ieee80211_cipher_keylen(ni->ni_pairwise_cipher); + frm = ieee80211_add_wpa(frm, ic, ni); + /* WPA sets the key length field here */ + keylen = ieee80211_cipher_keylen(ni->ni_rsncipher); BE_WRITE_2(key->keylen, keylen); } else /* RSN */ frm = ieee80211_add_rsn(frm, ic, ni); @@ -1711,7 +1797,7 @@ ieee80211_send_4way_msg2(struct ieee80211com *ic, struct ieee80211_node *ni, ic->ic_if.if_xname, 2, 4, "4-way", ether_sprintf(ni->ni_macaddr)); - return ieee80211_send_eapol_key(ic, m, ni); + return ieee80211_send_eapol_key(ic, m, ni, tptk); } /* @@ -1722,45 +1808,54 @@ int ieee80211_send_4way_msg3(struct ieee80211com *ic, struct ieee80211_node *ni) { struct ieee80211_eapol_key *key; - struct ieee80211_key *gtk; + struct ieee80211_key *k; struct mbuf *m; u_int16_t info, keylen; u_int8_t *frm; ni->ni_rsn_state = RSNA_PTKINITNEGOTIATING; - if (++ni->ni_rsn_tocnt == 3) - return 0; /* XXX move to RSNA_KEYERROR */ + if (++ni->ni_rsn_retries > 3) { + IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_4WAY_TIMEOUT); + ieee80211_node_leave(ic, ni); + return 0; + } + if (ni->ni_rsnprotos == IEEE80211_PROTO_RSN) + k = &ic->ic_nw_keys[ic->ic_def_txkey]; m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, 2 + 48 + - ((ni->ni_eapol_desc == EAPOL_KEY_DESC_IEEE80211) ? - 2 + 6 + gtk->k_len : 0) + + ((ni->ni_rsnprotos == IEEE80211_PROTO_RSN) ? + 2 + 6 + k->k_len : 0) + 8); if (m == NULL) return ENOMEM; key = mtod(m, struct ieee80211_eapol_key *); memset(key, 0, sizeof(*key)); - info = EAPOL_KEY_PAIRWISE | EAPOL_KEY_INSTALL | EAPOL_KEY_KEYACK | - EAPOL_KEY_KEYMIC; + info = EAPOL_KEY_PAIRWISE | EAPOL_KEY_KEYACK | EAPOL_KEY_KEYMIC; + if (ni->ni_rsncipher != IEEE80211_CIPHER_USEGROUP) + info |= EAPOL_KEY_INSTALL; - BE_WRITE_8(key->replaycnt, ni->ni_replaycnt); /* use same nonce as in Message 1 */ memcpy(key->nonce, ni->ni_nonce, EAPOL_KEY_NONCE_LEN); - keylen = ieee80211_cipher_keylen(ni->ni_pairwise_cipher); + ni->ni_replaycnt++; + BE_WRITE_8(key->replaycnt, ni->ni_replaycnt); + + keylen = ieee80211_cipher_keylen(ni->ni_rsncipher); BE_WRITE_2(key->keylen, keylen); frm = (u_int8_t *)&key[1]; /* add the WPA/RSN IE included in Beacon/Probe Response */ - if (ni->ni_eapol_desc == EAPOL_KEY_DESC_IEEE80211) { + if (ni->ni_rsnprotos == IEEE80211_PROTO_RSN) { frm = ieee80211_add_rsn(frm, ic, ic->ic_bss); - /* RSN: encapsulate the GTK and ask for encryption */ - frm = ieee80211_add_gtk_kde(frm, gtk); - LE_WRITE_8(key->rsc, gtk->k_rsc); + /* encapsulate the GTK and ask for encryption */ + frm = ieee80211_add_gtk_kde(frm, ni, k); + LE_WRITE_6(key->rsc, k->k_tsc); info |= EAPOL_KEY_ENCRYPTED | EAPOL_KEY_SECURE; - } else /* WPA1 */ - frm = ieee80211_add_wpa1(frm, ic, ic->ic_bss); + } else /* WPA */ + frm = ieee80211_add_wpa(frm, ic, ic->ic_bss); /* write the key info field */ BE_WRITE_2(key->info, info); @@ -1772,7 +1867,7 @@ ieee80211_send_4way_msg3(struct ieee80211com *ic, struct ieee80211_node *ni) ic->ic_if.if_xname, 3, 4, "4-way", ether_sprintf(ni->ni_macaddr)); - return ieee80211_send_eapol_key(ic, m, ni); + return ieee80211_send_eapol_key(ic, m, ni, &ni->ni_ptk); } /* @@ -1797,10 +1892,10 @@ ieee80211_send_4way_msg4(struct ieee80211com *ic, struct ieee80211_node *ni) /* copy key replay counter from authenticator */ BE_WRITE_8(key->replaycnt, ni->ni_replaycnt); - if (ni->ni_eapol_desc == EAPOL_KEY_DESC_WPA1) { + if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) { u_int16_t keylen; - /* WPA1 sets the key length field here */ - keylen = ieee80211_cipher_keylen(ni->ni_pairwise_cipher); + /* WPA sets the key length field here */ + keylen = ieee80211_cipher_keylen(ni->ni_rsncipher); BE_WRITE_2(key->keylen, keylen); } else info |= EAPOL_KEY_SECURE; @@ -1816,7 +1911,7 @@ ieee80211_send_4way_msg4(struct ieee80211com *ic, struct ieee80211_node *ni) ic->ic_if.if_xname, 4, 4, "4-way", ether_sprintf(ni->ni_macaddr)); - return ieee80211_send_eapol_key(ic, m, ni); + return ieee80211_send_eapol_key(ic, m, ni, &ni->ni_ptk); } /* @@ -1827,14 +1922,23 @@ int ieee80211_send_group_msg1(struct ieee80211com *ic, struct ieee80211_node *ni) { struct ieee80211_eapol_key *key; - struct ieee80211_key *gtk; + const struct ieee80211_key *k; struct mbuf *m; u_int16_t info; u_int8_t *frm; + ni->ni_rsn_gstate = RSNA_REKEYNEGOTIATING; + if (++ni->ni_rsn_retries > 3) { + IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_GROUP_TIMEOUT); + ieee80211_node_leave(ic, ni); + return 0; + } + k = &ic->ic_nw_keys[ic->ic_def_txkey]; + m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, - ((ni->ni_eapol_desc == EAPOL_KEY_DESC_WPA1) ? - gtk->k_len : 2 + 6 + gtk->k_len) + + ((ni->ni_rsnprotos == IEEE80211_PROTO_WPA) ? + k->k_len : 2 + 6 + k->k_len) + 8); if (m == NULL) return ENOMEM; @@ -1844,26 +1948,23 @@ ieee80211_send_group_msg1(struct ieee80211com *ic, struct ieee80211_node *ni) info = EAPOL_KEY_KEYACK | EAPOL_KEY_KEYMIC | EAPOL_KEY_SECURE | EAPOL_KEY_ENCRYPTED; + ni->ni_replaycnt++; BE_WRITE_8(key->replaycnt, ni->ni_replaycnt); -#if 0 - /* use global counter as GNonce */ - ieee80211_derive_gtk(ic->ic_gmk, IEEE80211_PMK_LEN, ic->ic_myaddr, - ic->ic_globalcnt, >k, sizeof gtk); - /* XXX increment global counter */ -#endif + frm = (u_int8_t *)&key[1]; - if (ni->ni_eapol_desc == EAPOL_KEY_DESC_WPA1) { - /* WPA1 does not have GTK KDE */ - BE_WRITE_2(key->keylen, gtk->k_len); - memcpy(frm, gtk->k_key, gtk->k_len); - frm += gtk->k_len; - info |= gtk->k_id << EAPOL_KEY_WPA_KID_SHIFT; - if (gtk->k_flags & IEEE80211_KEY_TX) + if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) { + /* WPA does not have GTK KDE */ + BE_WRITE_2(key->keylen, k->k_len); + memcpy(frm, k->k_key, k->k_len); + frm += k->k_len; + info |= (k->k_id & 0x3) << EAPOL_KEY_WPA_KID_SHIFT; + if (ni->ni_rsncipher == IEEE80211_CIPHER_USEGROUP) info |= EAPOL_KEY_WPA_TX; } else /* RSN */ - frm = ieee80211_add_gtk_kde(frm, gtk); + frm = ieee80211_add_gtk_kde(frm, ni, k); - LE_WRITE_8(key->rsc, gtk->k_rsc); + /* RSC = last transmit sequence number for the GTK */ + LE_WRITE_6(key->rsc, k->k_tsc); /* write the key info field */ BE_WRITE_2(key->info, info); @@ -1875,7 +1976,7 @@ ieee80211_send_group_msg1(struct ieee80211com *ic, struct ieee80211_node *ni) ic->ic_if.if_xname, 1, 2, "group key", ether_sprintf(ni->ni_macaddr)); - return ieee80211_send_eapol_key(ic, m, ni); + return ieee80211_send_eapol_key(ic, m, ni, &ni->ni_ptk); } /* @@ -1884,7 +1985,7 @@ ieee80211_send_group_msg1(struct ieee80211com *ic, struct ieee80211_node *ni) */ int ieee80211_send_group_msg2(struct ieee80211com *ic, struct ieee80211_node *ni, - const struct ieee80211_key *gtk) + const struct ieee80211_key *k) { struct ieee80211_eapol_key *key; u_int16_t info; @@ -1901,10 +2002,10 @@ ieee80211_send_group_msg2(struct ieee80211com *ic, struct ieee80211_node *ni, /* copy key replay counter from authenticator */ BE_WRITE_8(key->replaycnt, ni->ni_replaycnt); - if (ni->ni_eapol_desc == EAPOL_KEY_DESC_WPA1) { - /* WPA1 sets the key length and key id fields here */ - BE_WRITE_2(key->keylen, gtk->k_len); - info |= (gtk->k_id & 3) << EAPOL_KEY_WPA_KID_SHIFT; + if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) { + /* WPA sets the key length and key id fields here */ + BE_WRITE_2(key->keylen, k->k_len); + info |= (k->k_id & 3) << EAPOL_KEY_WPA_KID_SHIFT; } /* write the key info field */ @@ -1918,13 +2019,13 @@ ieee80211_send_group_msg2(struct ieee80211com *ic, struct ieee80211_node *ni, ic->ic_if.if_xname, 2, 2, "group key", ether_sprintf(ni->ni_macaddr)); - return ieee80211_send_eapol_key(ic, m, ni); + return ieee80211_send_eapol_key(ic, m, ni, &ni->ni_ptk); } /* * 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. */ int ieee80211_send_eapol_key_req(struct ieee80211com *ic, @@ -1939,21 +2040,22 @@ ieee80211_send_eapol_key_req(struct ieee80211com *ic, key = mtod(m, struct ieee80211_eapol_key *); memset(key, 0, sizeof(*key)); + info |= EAPOL_KEY_REQUEST; BE_WRITE_2(key->info, info); /* in case of TKIP MIC failure, fill the RSC field */ if (info & EAPOL_KEY_ERROR) - LE_WRITE_8(key->rsc, tsc); + LE_WRITE_6(key->rsc, tsc); /* use our separate key replay counter for key requests */ - BE_WRITE_8(key->replaycnt, ic->ic_keyreplaycnt); - ic->ic_keyreplaycnt++; + BE_WRITE_8(key->replaycnt, ni->ni_reqreplaycnt); + ni->ni_reqreplaycnt++; if (ic->ic_if.if_flags & IFF_DEBUG) printf("%s: sending EAPOL-Key request to %s\n", ic->ic_if.if_xname, ether_sprintf(ni->ni_macaddr)); - return ieee80211_send_eapol_key(ic, m, ni); + return ieee80211_send_eapol_key(ic, m, ni, &ni->ni_ptk); } void diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c index 17e8d60e85d..b77d2477289 100644 --- a/sys/net80211/ieee80211_proto.c +++ b/sys/net80211/ieee80211_proto.c @@ -1,9 +1,10 @@ -/* $OpenBSD: ieee80211_proto.c,v 1.21 2007/10/08 17:31:24 mglocker Exp $ */ +/* $OpenBSD: ieee80211_proto.c,v 1.22 2008/04/16 18:32:15 damien Exp $ */ /* $NetBSD: ieee80211_proto.c,v 1.8 2004/04/30 23:58:20 dyoung Exp $ */ /*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2008 Damien Bergamini * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -63,6 +64,8 @@ #include <net80211/ieee80211_var.h> +#include <dev/rndvar.h> + const char * const ieee80211_mgt_subtype_name[] = { "assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp", "probe_req", "probe_resp", "reserved#6", "reserved#7", @@ -356,6 +359,77 @@ ieee80211_set_shortslottime(struct ieee80211com *ic, int on) ic->ic_updateslot(ic); } +/* + * Initiate a group key handshake with a node. + */ +static void +ieee80211_node_gtk_rekey(void *arg, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = arg; + + if (ni->ni_state != IEEE80211_STA_ASSOC || + ni->ni_rsn_gstate != RSNA_IDLE) + return; + + /* initiate a group key handshake with STA */ + if (ieee80211_send_group_msg1(ic, ni) == 0) { + ni->ni_flags |= IEEE80211_NODE_REKEY; + ic->ic_rsn_keydonesta++; + } +} + +/* + * This function is called in HostAP mode when the group key needs to be + * changed. + */ +void +ieee80211_setkeys(struct ieee80211com *ic) +{ + u_int8_t gtk[IEEE80211_PMK_LEN]; + u_int8_t kid; + + /* Swap(GM, GN) */ + kid = (ic->ic_def_txkey == 1) ? 2 : 1; + + arc4random_bytes(gtk, sizeof(gtk)); + ieee80211_map_gtk(gtk, ic->ic_bss->ni_rsngroupcipher, kid, 1, 0, + &ic->ic_nw_keys[kid]); + + ic->ic_rsn_keydonesta = 0; + ieee80211_iterate_nodes(ic, ieee80211_node_gtk_rekey, ic); +} + +/* + * The group key handshake has been completed with all associated stations. + */ +void +ieee80211_setkeysdone(struct ieee80211com *ic) +{ + u_int8_t kid; + + /* install GTK */ + kid = (ic->ic_def_txkey == 1) ? 2 : 1; + if ((*ic->ic_set_key)(ic, ic->ic_bss, &ic->ic_nw_keys[kid]) == 0) + ic->ic_def_txkey = kid; +} + +/* + * Group key lifetime has expired, update it. + */ +void +ieee80211_gtk_rekey_timeout(void *arg) +{ + struct ieee80211com *ic = arg; + int s; + + s = splnet(); + ieee80211_setkeys(ic); + splx(s); + + /* re-schedule a GTK rekeying after 3600s */ + timeout_add(&ic->ic_rsn_timeout, 3600 * hz); +} + int ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt) @@ -422,13 +496,11 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, /* FALLTHROUGH */ case IEEE80211_S_AUTH: case IEEE80211_S_SCAN: + if (ic->ic_opmode == IEEE80211_M_HOSTAP) + timeout_del(&ic->ic_rsn_timeout); ic->ic_mgt_timer = 0; IF_PURGE(&ic->ic_mgtq); IF_PURGE(&ic->ic_pwrsaveq); - if (ic->ic_wep_ctx != NULL) { - free(ic->ic_wep_ctx, M_DEVBUF); - ic->ic_wep_ctx = NULL; - } ieee80211_free_allnodes(ic); break; } diff --git a/sys/net80211/ieee80211_proto.h b/sys/net80211/ieee80211_proto.h index fd43c0c49a3..e3fb831dfb8 100644 --- a/sys/net80211/ieee80211_proto.h +++ b/sys/net80211/ieee80211_proto.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_proto.h,v 1.27 2007/08/23 16:59:32 damien Exp $ */ +/* $OpenBSD: ieee80211_proto.h,v 1.28 2008/04/16 18:32:15 damien Exp $ */ /* $NetBSD: ieee80211_proto.h,v 1.3 2003/10/13 04:23:56 dyoung Exp $ */ /*- @@ -76,10 +76,13 @@ extern struct mbuf *ieee80211_get_cts_to_self(struct ieee80211com *, u_int16_t); extern struct mbuf *ieee80211_beacon_alloc(struct ieee80211com *, struct ieee80211_node *); +extern int ieee80211_save_ie(const u_int8_t *, u_int8_t **); +extern void ieee80211_eapol_timeout(void *); extern int ieee80211_send_4way_msg1(struct ieee80211com *, struct ieee80211_node *); extern int ieee80211_send_4way_msg2(struct ieee80211com *, - struct ieee80211_node *, const u_int8_t *); + struct ieee80211_node *, const u_int8_t *, + const struct ieee80211_ptk *); extern int ieee80211_send_4way_msg3(struct ieee80211com *, struct ieee80211_node *); extern int ieee80211_send_4way_msg4(struct ieee80211com *, @@ -113,7 +116,7 @@ extern u_int8_t *ieee80211_add_qos_capability(u_int8_t *, struct ieee80211com *); extern u_int8_t *ieee80211_add_rsn(u_int8_t *, struct ieee80211com *, const struct ieee80211_node *); -extern u_int8_t *ieee80211_add_wpa1(u_int8_t *, struct ieee80211com *, +extern u_int8_t *ieee80211_add_wpa(u_int8_t *, struct ieee80211com *, const struct ieee80211_node *); extern u_int8_t *ieee80211_add_xrates(u_int8_t *, const struct ieee80211_rateset *); @@ -131,5 +134,8 @@ extern void ieee80211_eapol_key_encrypt(struct ieee80211com *, struct ieee80211_eapol_key *, const u_int8_t *); extern int ieee80211_eapol_key_decrypt(struct ieee80211_eapol_key *, const u_int8_t *); +extern void ieee80211_gtk_rekey_timeout(void *); +extern void ieee80211_setkeys(struct ieee80211com *); +extern void ieee80211_setkeysdone(struct ieee80211com *); #endif /* _NET80211_IEEE80211_PROTO_H_ */ diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h index 17d856f3797..973afb5005f 100644 --- a/sys/net80211/ieee80211_var.h +++ b/sys/net80211/ieee80211_var.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_var.h,v 1.37 2007/11/17 14:05:01 damien Exp $ */ +/* $OpenBSD: ieee80211_var.h,v 1.38 2008/04/16 18:32:15 damien Exp $ */ /* $NetBSD: ieee80211_var.h,v 1.7 2004/05/06 03:07:10 dyoung Exp $ */ /*- @@ -37,6 +37,8 @@ * Definitions for IEEE 802.11 drivers. */ +#include <sys/timeout.h> + #include <net80211/ieee80211.h> #include <net80211/ieee80211_crypto.h> #include <net80211/ieee80211_ioctl.h> /* for ieee80211_stats */ @@ -143,6 +145,19 @@ struct ieee80211_edca_ac_params { u_int8_t ac_acm; }; +#define IEEE80211_PROTO_NONE 0 +#define IEEE80211_PROTO_RSN (1 << 0) +#define IEEE80211_PROTO_WPA (1 << 1) + +struct ieee80211_rsnparams { + u_int16_t rsn_nakms; + u_int32_t rsn_akms; + u_int16_t rsn_nciphers; + u_int32_t rsn_ciphers; + enum ieee80211_cipher rsn_groupcipher; + u_int16_t rsn_caps; +}; + #define IEEE80211_PS_SLEEP 0x1 /* STA is in power saving mode */ #define IEEE80211_PS_MAX_QUEUE 50 /* maximum saved packets */ @@ -171,9 +186,10 @@ struct ieee80211com { void (*ic_set_tim)(struct ieee80211com *, int, int); int (*ic_set_key)(struct ieee80211com *, struct ieee80211_node *, - const struct ieee80211_key *); + struct ieee80211_key *); void (*ic_delete_key)(struct ieee80211com *, - struct ieee80211_node *, int); + struct ieee80211_node *, + struct ieee80211_key *); u_int8_t ic_myaddr[IEEE80211_ADDR_LEN]; struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX]; struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1]; @@ -223,6 +239,7 @@ struct ieee80211com { u_int16_t ic_bmisstimeout;/* beacon miss threshold (ms) */ u_int16_t ic_nonerpsta; /* # non-ERP stations */ u_int16_t ic_longslotsta; /* # long slot time stations */ + u_int16_t ic_rsnsta; /* # RSN stations */ int ic_mgt_timer; /* mgmt timeout */ int ic_inact_timer; /* inactivity timer wait */ int ic_des_esslen; @@ -230,8 +247,8 @@ struct ieee80211com { struct ieee80211_channel *ic_des_chan; /* desired channel */ u_int8_t ic_des_bssid[IEEE80211_ADDR_LEN]; struct ieee80211_key ic_nw_keys[IEEE80211_WEP_NKID]; - int ic_wep_txkey; /* default tx key index */ - void *ic_wep_ctx; /* wep crypt context */ + int ic_def_txkey; /* default tx key index */ +#define ic_wep_txkey ic_def_txkey u_int32_t ic_iv; /* initial vector for wep */ struct ieee80211_stats ic_stats; /* statistics */ struct timeval ic_last_merge_print; /* for rate-limiting @@ -240,8 +257,16 @@ struct ieee80211com { struct ieee80211_edca_ac_params ic_edca_ac[EDCA_NUM_AC]; u_int ic_edca_updtcount; u_int8_t ic_globalcnt[EAPOL_KEY_NONCE_LEN]; - u_int64_t ic_keyreplaycnt; + u_int8_t ic_nonce[EAPOL_KEY_NONCE_LEN]; u_int8_t ic_psk[IEEE80211_PMK_LEN]; + struct timeout ic_rsn_timeout; + u_int16_t ic_rsn_keydonesta; + int ic_tkip_micfail; + + u_int ic_rsnprotos; + u_int ic_rsnakms; + u_int ic_rsnciphers; + enum ieee80211_cipher ic_rsngroupcipher; u_int8_t *ic_tim_bitmap; u_int ic_tim_len; @@ -276,8 +301,9 @@ extern struct ieee80211com_head ieee80211com_head; #define IEEE80211_F_SHPREAMBLE 0x00040000 /* STATUS: short preamble */ #define IEEE80211_F_QOS 0x00080000 /* CONF: QoS enabled */ #define IEEE80211_F_USEPROT 0x00100000 /* STATUS: protection enabled */ -#define IEEE80211_F_RSN 0x00200000 /* CONF: RSN enabled */ -#define IEEE80211_F_WPA1 0x00400000 /* CONF: WPA1 enabled */ +#define IEEE80211_F_RSNON 0x00200000 /* CONF: RSN enabled */ +#define IEEE80211_F_PSK 0x00400000 /* CONF: pre-shared key set */ +#define IEEE80211_F_COUNTERM 0x00800000 /* STATUS: countermeasures */ #define IEEE80211_F_USERMASK 0xf0000000 /* CONF: ioctl flag mask */ /* ic_caps */ |