From 343306a96b413775f398b1f39717da6e50c94323 Mon Sep 17 00:00:00 2001 From: Sepherosa Ziehau Date: Mon, 7 May 2007 14:12:16 +0000 Subject: [PATCH] Adapt 802.11 generic layer to support hardware crypto other than ath(4). More specificly, it is changed for Wifi chips from Ralink (2x61 is used as an example here), which have following hardware crypto features. These features are different from ath(4)'s and need special cares: 1) For TX, host does not need to insert IV and Extended IV after 802.11 MAC header, instead, host provides them in TX descriptor. 2) For RX, IV and Extended IV will not be left in RX buffer, instead, they are recorded in RX descriptor. 3) For RX and TKIP is used as crypto method, if the received MPDU is the only fragment of an MSDU then MIC is stripped and hardware will verify MIC for host. Since these kinds of hardwares need to know IV and Extended IV, ieee80211_crypto_iv structure is added. It can hold IV and Extended IV, and is used to pass these two IVs to and from 802.11 generic layer. It requires a special layout to ease crypto modules' processing, so comment is added to make sure the structure's layout will not be changed. To address the problems introduced by the hardware feature 1), following changes are made: - Add ic_getiv() interface for all crypto modules, which is (obviously) used to get IV and extended IV from crypto module. Except that it puts IVs in ieee80211_crypto_iv instead of TX buffer, the new interface is quite similar to ic_encap(). Wrap ic_getiv() interface by ieee80211_crypto_getiv(). - Split ieee80211_crypto_encap() into two functions: o ieee80211_crypto_findkey(). It is used to find the crypto key for given MPDU and receiver. o ieee80211_crypto_encap_withkey(). It does the real encryption work. For Ralink's Wifi chips, this function is only called when host based encryption is used. After this splition, driver will have a chance to decide whether it need to do host encryption, which could happen when there are not enough hardware pairwise keys, or offload the encryption to hardware. ath(4) does not need this interception, since no matter hardware encryption is used or not, host always has to insert IVs, while for Ralink Wifi chips, IV insertion can be done only if host encryption is to be used, for hardware encryption, they must be set in TX descriptor. This splition also causes another problem: ieee80211_crypto_encap_withkey() requires a keyid (read: not key index) and it will be too bloated to add a keyid parameter for both ieee80211_crypto_encap_withkey() and ieee80211_crypto_findkey(), so o Change ieee80211_key.wk_pad to ieee80211_key.wk_keyid, which is keyid for a given key, and is set in ieee80211_crypto_resetkey(). Since ieee80211_crypto_resetkey() will need to know internals of ieee80211com, put it into ieee80211_crypto.c. o Add assertion in ieee80211_crypto_findkey() to make sure that the crypto key has correct keyid. o Make ieee80211_crypto_encap() a wrapper of ieee80211_crypto_findkey() and ieee80211_crypto_encap_withkey(). Old symantic of this function is still kept. The crypto encapsulation for Ralink Wifi chips will look like following: ... k = ieee80211_crypto_findkey(); if (k is hardware encryption key) k = ieee80211_crypto_getiv(k, iv); else k = ieee80211_crypto_encap_withkey(k); ... - Add a crypto key flag, IEEE80211_KEY_NOHDR, to indicate that host does not need to reserve space in TX buffer if hardware encryption is used. - Honor IEEE80211_KEY_NOHDR in ieee80211_mbuf_adjust(). - Add an extended capability flag, IEEE80211_CEXT_CRYPTO_HDR, which is set by driver to inform crypto module that if hardware encryption is used for a crypto key, the key should have IEEE80211_KEY_NOHDR turned on. To address the problems introduced by the hardware feature 2), following changes are made: - Add ic_update() interface for all crypto modules, which is used to update crypto modules' internal state according to the IVs passed in. Except that it peeks at the passed in ieee80211_crypto_iv instead of RX buffer, it acts similarly to ic_decap(). Wrap ic_update() interface by ieee80211_crypto_update(). ieee80211_crypto_update() also locates the crypto key for given MPDU and sender. - Add ieee80211_input_withiv(), which accepts an ieee80211_crypto_iv 'iv' parameter in addition to the original ieee80211_input() parameters. If 'iv' parameter is NULL, old ieee80211_input() behaviour is used, if 'iv' is not NULL, ieee80211_crypto_update() will be called instead of ieee80211_crypto_decap(). ath(4) does not require this special processing, since no matter hardware encryption is used or not, IVs are always in RX buffer, but for Wifi chips from Ralink, we will have to explicitly pass the recorded IVs in RX descriptor down to crypto modules. - Change ieee80211_input() to call ieee80211_input_withiv() with NULL 'iv'. Old symantic of this function is still kept. To address the problems introduced by the hardware feature 3), following changes are made: - Add a key flag, IEEE80211_KEY_NOMIC, to give hint to TKIP crypto module that hardware will strip TKIP MIC. - Honor IEEE80211_KEY_NOMIC in tkip_demic(). - Add an extended capability flag, IEEE80211_CEXT_STRIP_MIC, which is set by driver to inform crypto module that if hardware TKIP MIC is used for a crypto key, then the key should have IEEE80211_KEY_NOMIC turned on. --- sys/netproto/802_11/ieee80211_crypto.h | 48 ++++++---- sys/netproto/802_11/ieee80211_proto.h | 6 +- sys/netproto/802_11/ieee80211_var.h | 8 +- sys/netproto/802_11/wlan/ieee80211_crypto.c | 92 ++++++++++++++++-- sys/netproto/802_11/wlan/ieee80211_crypto_none.c | 44 ++++++++- sys/netproto/802_11/wlan/ieee80211_input.c | 26 ++++- sys/netproto/802_11/wlan/ieee80211_output.c | 5 +- .../802_11/wlan_ccmp/ieee80211_crypto_ccmp.c | 67 ++++++++++++- .../802_11/wlan_tkip/ieee80211_crypto_tkip.c | 106 ++++++++++++++++++++- .../802_11/wlan_wep/ieee80211_crypto_wep.c | 53 ++++++++++- 10 files changed, 415 insertions(+), 40 deletions(-) diff --git a/sys/netproto/802_11/ieee80211_crypto.h b/sys/netproto/802_11/ieee80211_crypto.h index f18411a744..f745b7fefb 100644 --- a/sys/netproto/802_11/ieee80211_crypto.h +++ b/sys/netproto/802_11/ieee80211_crypto.h @@ -30,7 +30,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/net80211/ieee80211_crypto.h,v 1.9.2.1 2005/09/03 22:40:02 sam Exp $ - * $DragonFly: src/sys/netproto/802_11/ieee80211_crypto.h,v 1.3 2006/09/03 09:37:58 sephe Exp $ + * $DragonFly: src/sys/netproto/802_11/ieee80211_crypto.h,v 1.4 2007/05/07 14:12:16 sephe Exp $ */ #ifndef _NET80211_IEEE80211_CRYPTO_H_ #define _NET80211_IEEE80211_CRYPTO_H_ @@ -71,13 +71,15 @@ typedef uint16_t ieee80211_keyix; /* h/w key index */ struct ieee80211_key { uint8_t wk_keylen; /* key length in bytes */ - uint8_t wk_pad; + uint8_t wk_keyid; /* key id */ uint16_t wk_flags; #define IEEE80211_KEY_XMIT 0x01 /* key used for xmit */ #define IEEE80211_KEY_RECV 0x02 /* key used for recv */ #define IEEE80211_KEY_GROUP 0x04 /* key used for WPA group operation */ #define IEEE80211_KEY_SWCRYPT 0x10 /* host-based encrypt/decrypt */ #define IEEE80211_KEY_SWMIC 0x20 /* host-based enmic/demic */ +#define IEEE80211_KEY_NOHDR 0x40 /* driver appends crypto header */ +#define IEEE80211_KEY_NOMIC 0x80 /* driver strips TKIP MIC */ ieee80211_keyix wk_keyix; /* h/w key index */ ieee80211_keyix wk_rxkeyix; /* optional h/w rx key index */ uint8_t wk_key[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE]; @@ -147,6 +149,15 @@ int ieee80211_crypto_setkey(struct ieee80211com *, struct ieee80211_key *, const uint8_t macaddr[IEEE80211_ADDR_LEN]); void ieee80211_crypto_delglobalkeys(struct ieee80211com *); +struct ieee80211_crypto_iv { + /* + * Keep this layout! + * Crypto modules will assume that ic_iv and ic_eiv are contiguous. + */ + uint8_t ic_iv[4]; + uint8_t ic_eiv[4]; +}; + /* * Template for a supported cipher. Ciphers register with the * crypto code and are typically loaded as separate modules @@ -167,6 +178,11 @@ struct ieee80211_cipher { int (*ic_decap)(struct ieee80211_key *, struct mbuf *, int); int (*ic_enmic)(struct ieee80211_key *, struct mbuf *, int); int (*ic_demic)(struct ieee80211_key *, struct mbuf *, int); + int (*ic_getiv)(struct ieee80211_key *, + struct ieee80211_crypto_iv *, uint8_t); + int (*ic_update)(struct ieee80211_key *, + const struct ieee80211_crypto_iv *, + const struct ieee80211_frame *); }; extern const struct ieee80211_cipher ieee80211_cipher_none; @@ -175,10 +191,23 @@ void ieee80211_crypto_unregister(const struct ieee80211_cipher *); int ieee80211_crypto_available(u_int cipher); const struct ieee80211_cipher *ieee80211_crypto_cipher(u_int cipher); +void ieee80211_crypto_resetkey(struct ieee80211com *, + struct ieee80211_key *, ieee80211_keyix); + struct ieee80211_key *ieee80211_crypto_encap(struct ieee80211com *, struct ieee80211_node *, struct mbuf *); struct ieee80211_key *ieee80211_crypto_decap(struct ieee80211com *, struct ieee80211_node *, struct mbuf *, int); +struct ieee80211_key *ieee80211_crypto_findkey(struct ieee80211com *, + struct ieee80211_node *, struct mbuf *); +struct ieee80211_key *ieee80211_crypto_encap_withkey(struct ieee80211com *, + struct mbuf *, struct ieee80211_key *); + +struct ieee80211_key *ieee80211_crypto_update(struct ieee80211com *, + struct ieee80211_node *, const struct ieee80211_crypto_iv *, + const struct ieee80211_frame *); +struct ieee80211_key *ieee80211_crypto_getiv(struct ieee80211com *, + struct ieee80211_crypto_iv *, struct ieee80211_key *); /* * Check and remove any MIC. @@ -202,21 +231,6 @@ ieee80211_crypto_enmic(struct ieee80211com *ic, return (cip->ic_miclen > 0 ? cip->ic_enmic(k, m, force) : 1); } -/* - * Reset key state to an unused state. The crypto - * key allocation mechanism insures other state (e.g. - * key data) is properly setup before a key is used. - */ -static __inline void -ieee80211_crypto_resetkey(struct ieee80211com *ic, - struct ieee80211_key *k, ieee80211_keyix ix) -{ - k->wk_cipher = &ieee80211_cipher_none; - k->wk_private = k->wk_cipher->ic_attach(ic, k); - k->wk_keyix = k->wk_rxkeyix = ix; - k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV; -} - /* * Crypt-related notification methods. */ diff --git a/sys/netproto/802_11/ieee80211_proto.h b/sys/netproto/802_11/ieee80211_proto.h index 3a7184f0d0..f2639dc157 100644 --- a/sys/netproto/802_11/ieee80211_proto.h +++ b/sys/netproto/802_11/ieee80211_proto.h @@ -30,7 +30,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/net80211/ieee80211_proto.h,v 1.11.2.5 2006/02/12 19:00:39 sam Exp $ - * $DragonFly: src/sys/netproto/802_11/ieee80211_proto.h,v 1.12 2007/04/26 12:59:14 sephe Exp $ + * $DragonFly: src/sys/netproto/802_11/ieee80211_proto.h,v 1.13 2007/05/07 14:12:16 sephe Exp $ */ #ifndef _NET80211_IEEE80211_PROTO_H_ #define _NET80211_IEEE80211_PROTO_H_ @@ -58,8 +58,12 @@ void ieee80211_proto_attach(struct ieee80211com *); void ieee80211_proto_detach(struct ieee80211com *); struct ieee80211_node; +struct ieee80211_crypto_iv; int ieee80211_input(struct ieee80211com *, struct mbuf *, struct ieee80211_node *, int, uint32_t); +int ieee80211_input_withiv(struct ieee80211com *, struct mbuf *, + struct ieee80211_node *, int, uint32_t, + const struct ieee80211_crypto_iv *); int ieee80211_setup_rates(struct ieee80211_node *ni, const uint8_t *rates, const uint8_t *xrates, int flags, int join); diff --git a/sys/netproto/802_11/ieee80211_var.h b/sys/netproto/802_11/ieee80211_var.h index 96d7d285b7..2cf2e63c9f 100644 --- a/sys/netproto/802_11/ieee80211_var.h +++ b/sys/netproto/802_11/ieee80211_var.h @@ -30,7 +30,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/net80211/ieee80211_var.h,v 1.22.2.11 2006/03/13 03:05:48 sam Exp $ - * $DragonFly: src/sys/netproto/802_11/ieee80211_var.h,v 1.15 2007/04/01 13:59:40 sephe Exp $ + * $DragonFly: src/sys/netproto/802_11/ieee80211_var.h,v 1.16 2007/05/07 14:12:16 sephe Exp $ */ #ifndef _NET80211_IEEE80211_VAR_H_ #define _NET80211_IEEE80211_VAR_H_ @@ -301,6 +301,12 @@ struct ieee80211com { /* ic_caps_ext */ #define IEEE80211_CEXT_PBCC 0x00000001 /* CAPABILITY: PBCC modulation */ +#define IEEE80211_CEXT_CRYPTO_HDR 0x00000002 /* CAPABILITY: + * driver appends crypto header + */ +#define IEEE80211_CEXT_STRIP_MIC 0x00000004 /* CAPABILITY: + * driver strips TKIP MIC + */ void ieee80211_ifattach(struct ieee80211com *); void ieee80211_ifdetach(struct ieee80211com *); diff --git a/sys/netproto/802_11/wlan/ieee80211_crypto.c b/sys/netproto/802_11/wlan/ieee80211_crypto.c index 8a2e2d725f..d727391b00 100644 --- a/sys/netproto/802_11/wlan/ieee80211_crypto.c +++ b/sys/netproto/802_11/wlan/ieee80211_crypto.c @@ -30,7 +30,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/net80211/ieee80211_crypto.c,v 1.10.2.2 2005/09/03 22:40:02 sam Exp $ - * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211_crypto.c,v 1.5 2006/12/23 00:57:53 swildner Exp $ + * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211_crypto.c,v 1.6 2007/05/07 14:12:16 sephe Exp $ */ /* @@ -230,6 +230,27 @@ ieee80211_crypto_cipher(u_int cipher) return cipher < IEEE80211_CIPHER_MAX ? ciphers[cipher] : NULL; } +/* + * Reset key state to an unused state. The crypto + * key allocation mechanism insures other state (e.g. + * key data) is properly setup before a key is used. + */ +void +ieee80211_crypto_resetkey(struct ieee80211com *ic, + struct ieee80211_key *k, ieee80211_keyix ix) +{ + if (k < &ic->ic_nw_keys[IEEE80211_WEP_NKID] && + k >= &ic->ic_nw_keys[0]) + k->wk_keyid = k - ic->ic_nw_keys; + else + k->wk_keyid = 0; + + k->wk_cipher = &ieee80211_cipher_none; + k->wk_private = k->wk_cipher->ic_attach(ic, k); + k->wk_keyix = k->wk_rxkeyix = ix; + k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV; +} + /* XXX well-known names! */ static const char *cipher_modnames[] = { "wlan_wep", /* IEEE80211_CIPHER_WEP */ @@ -313,6 +334,8 @@ ieee80211_crypto_newkey(struct ieee80211com *ic, "%s: no h/w support for cipher %s, falling back to s/w\n", __func__, cip->ic_name); flags |= IEEE80211_KEY_SWCRYPT; + } else if (ic->ic_caps_ext & IEEE80211_CEXT_CRYPTO_HDR) { + flags |= IEEE80211_KEY_NOHDR; } /* * Hardware TKIP with software MIC is an important @@ -325,6 +348,8 @@ ieee80211_crypto_newkey(struct ieee80211com *ic, "%s: no h/w support for TKIP MIC, falling back to s/w\n", __func__); flags |= IEEE80211_KEY_SWMIC; + } else if (ic->ic_caps_ext & IEEE80211_CEXT_STRIP_MIC) { + flags |= IEEE80211_KEY_NOMIC; } /* @@ -388,6 +413,7 @@ again: "falling back to s/w\n", __func__, cip->ic_name); oflags = key->wk_flags; + flags &= IEEE80211_KEY_COMMON; flags |= IEEE80211_KEY_SWCRYPT; if (cipher == IEEE80211_CIPHER_TKIP) flags |= IEEE80211_KEY_SWMIC; @@ -521,9 +547,19 @@ ieee80211_crypto_encap(struct ieee80211com *ic, struct ieee80211_node *ni, struct mbuf *m) { struct ieee80211_key *k; + + k = ieee80211_crypto_findkey(ic, ni, m); + if (k != NULL) + k = ieee80211_crypto_encap_withkey(ic, m, k); + return k; +} + +struct ieee80211_key * +ieee80211_crypto_findkey(struct ieee80211com *ic, + struct ieee80211_node *ni, struct mbuf *m) +{ struct ieee80211_frame *wh; - const struct ieee80211_cipher *cip; - uint8_t keyid; + struct ieee80211_key *k; /* * Multicast traffic always uses the multicast key. @@ -542,14 +578,30 @@ ieee80211_crypto_encap(struct ieee80211com *ic, ic->ic_stats.is_tx_nodefkey++; return NULL; } - keyid = ic->ic_def_txkey; k = &ic->ic_nw_keys[ic->ic_def_txkey]; + KASSERT(k->wk_keyid == ic->ic_def_txkey, + ("keyid mismatch: wk_keyid %d, def_txkey %d\n", + k->wk_keyid, ic->ic_def_txkey)); } else { - keyid = 0; k = &ni->ni_ucastkey; + KASSERT(k->wk_keyid == 0, ("unicast key keyid is not zero\n")); } - cip = k->wk_cipher; - return (cip->ic_encap(k, m, keyid<<6) ? k : NULL); + return k; +} + +struct ieee80211_key * +ieee80211_crypto_encap_withkey(struct ieee80211com *ic, + struct mbuf *m, struct ieee80211_key *k) +{ + return (k->wk_cipher->ic_encap(k, m, k->wk_keyid << 6) ? k : NULL); +} + +struct ieee80211_key * +ieee80211_crypto_getiv(struct ieee80211com *ic, struct ieee80211_crypto_iv *iv, + struct ieee80211_key *k) +{ + memset(iv, 0, sizeof(*iv)); + return (k->wk_cipher->ic_getiv(k, iv, k->wk_keyid << 6) ? k : NULL); } /* @@ -611,3 +663,29 @@ ieee80211_crypto_decap(struct ieee80211com *ic, #undef IEEE80211_WEP_MINLEN #undef IEEE80211_WEP_HDRLEN } + +struct ieee80211_key * +ieee80211_crypto_update(struct ieee80211com *ic, struct ieee80211_node *ni, + const struct ieee80211_crypto_iv *iv, const struct ieee80211_frame *wh) +{ + struct ieee80211_key *k; + + /* + * Locate the key. If unicast and there is no unicast + * key then we fall back to the key id in the header. + * This assumes unicast keys are only configured when + * the key id in the header is meaningless (typically 0). + */ + if (IEEE80211_IS_MULTICAST(wh->i_addr1) || + ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) { + const uint8_t *ivp; + uint8_t keyid; + + ivp = (const uint8_t *)iv; + keyid = ivp[IEEE80211_WEP_IVLEN]; + k = &ic->ic_nw_keys[keyid >> 6]; + } else { + k = &ni->ni_ucastkey; + } + return (k->wk_cipher->ic_update(k, iv, wh) ? k : NULL); +} diff --git a/sys/netproto/802_11/wlan/ieee80211_crypto_none.c b/sys/netproto/802_11/wlan/ieee80211_crypto_none.c index 76f07aafdb..cf3bf69625 100644 --- a/sys/netproto/802_11/wlan/ieee80211_crypto_none.c +++ b/sys/netproto/802_11/wlan/ieee80211_crypto_none.c @@ -29,7 +29,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/net80211/ieee80211_crypto_none.c,v 1.5 2005/06/10 16:11:24 sam Exp $ - * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211_crypto_none.c,v 1.2 2006/08/05 05:18:27 sephe Exp $ + * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211_crypto_none.c,v 1.3 2007/05/07 14:12:16 sephe Exp $ */ /* @@ -56,6 +56,11 @@ static int none_crypto_encap(struct ieee80211_key *, struct mbuf *, uint8_t); static int none_crypto_decap(struct ieee80211_key *, struct mbuf *, int); static int none_crypto_enmic(struct ieee80211_key *, struct mbuf *, int); static int none_crypto_demic(struct ieee80211_key *, struct mbuf *, int); +static int none_crypto_getiv(struct ieee80211_key *, + struct ieee80211_crypto_iv *, uint8_t); +static int none_crypto_update(struct ieee80211_key *, + const struct ieee80211_crypto_iv *, + const struct ieee80211_frame *); const struct ieee80211_cipher ieee80211_cipher_none = { .ic_name = "NONE", @@ -70,6 +75,8 @@ const struct ieee80211_cipher ieee80211_cipher_none = { .ic_decap = none_crypto_decap, .ic_enmic = none_crypto_enmic, .ic_demic = none_crypto_demic, + .ic_getiv = none_crypto_getiv, + .ic_update = none_crypto_update }; static void * @@ -132,6 +139,25 @@ none_crypto_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) } static int +none_crypto_update(struct ieee80211_key *k, + const struct ieee80211_crypto_iv *iv, + const struct ieee80211_frame *wh) +{ + struct ieee80211com *ic = k->wk_private; + const uint8_t *ivp = (const uint8_t *)iv; + + /* + * The specified key is not setup; this can + * happen, at least, when changing keys. + */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "[%6D] key id %u is not set (update)\n", + wh->i_addr2, ":", ivp[IEEE80211_WEP_IVLEN] >> 6); + ic->ic_stats.is_rx_badkeyid++; + return 0; +} + +static int none_crypto_enmic(struct ieee80211_key *k, struct mbuf *m, int force) { struct ieee80211com *ic = k->wk_private; @@ -148,3 +174,19 @@ none_crypto_demic(struct ieee80211_key *k, struct mbuf *m, int force) ic->ic_stats.is_rx_badkeyid++; return 0; } + +static int +none_crypto_getiv(struct ieee80211_key *k, struct ieee80211_crypto_iv *iv, + uint8_t keyid) +{ + struct ieee80211com *ic = k->wk_private; + + /* + * The specified key is not setup; this can + * happen, at least, when changing keys. + */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "key id %u is not set (getiv)\n", keyid>>6); + ic->ic_stats.is_tx_badcipher++; + return 0; +} diff --git a/sys/netproto/802_11/wlan/ieee80211_input.c b/sys/netproto/802_11/wlan/ieee80211_input.c index b960100705..3758ece94c 100644 --- a/sys/netproto/802_11/wlan/ieee80211_input.c +++ b/sys/netproto/802_11/wlan/ieee80211_input.c @@ -30,7 +30,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/net80211/ieee80211_input.c,v 1.62.2.14 2006/09/02 15:16:12 sam Exp $ - * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211_input.c,v 1.19 2007/03/26 11:08:30 sephe Exp $ + * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211_input.c,v 1.20 2007/05/07 14:12:16 sephe Exp $ */ #include @@ -135,6 +135,14 @@ int ieee80211_input(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_node *ni, int rssi, uint32_t rstamp) { + return ieee80211_input_withiv(ic, m, ni, rssi, rstamp, NULL); +} + +int +ieee80211_input_withiv(struct ieee80211com *ic, struct mbuf *m, + struct ieee80211_node *ni, int rssi, uint32_t rstamp, + const struct ieee80211_crypto_iv *iv) +{ #define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0) #define HAS_SEQ(type) ((type & 0x4) == 0) struct ifnet *ifp = ic->ic_ifp; @@ -388,7 +396,12 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, IEEE80211_NODE_STAT(ni, rx_noprivacy); goto out; } - key = ieee80211_crypto_decap(ic, ni, m, hdrspace); + if (iv == NULL) { + key = ieee80211_crypto_decap(ic, ni, m, + hdrspace); + } else { + key = ieee80211_crypto_update(ic, ni, iv, wh); + } if (key == NULL) { /* NB: stats+msgs handled in crypto_decap */ IEEE80211_NODE_STAT(ni, rx_wepfail); @@ -527,8 +540,13 @@ ieee80211_input(struct ieee80211com *ic, struct mbuf *m, ic->ic_stats.is_rx_noprivacy++; goto out; } - hdrspace = ieee80211_hdrspace(ic, wh); - key = ieee80211_crypto_decap(ic, ni, m, hdrspace); + if (iv == NULL) { + hdrspace = ieee80211_hdrspace(ic, wh); + key = ieee80211_crypto_decap(ic, ni, m, + hdrspace); + } else { + key = ieee80211_crypto_update(ic, ni, iv, wh); + } if (key == NULL) { /* NB: stats+msgs handled in crypto_decap */ goto out; diff --git a/sys/netproto/802_11/wlan/ieee80211_output.c b/sys/netproto/802_11/wlan/ieee80211_output.c index d6c95a426f..cbe4d61b78 100644 --- a/sys/netproto/802_11/wlan/ieee80211_output.c +++ b/sys/netproto/802_11/wlan/ieee80211_output.c @@ -30,7 +30,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/net80211/ieee80211_output.c,v 1.26.2.8 2006/09/02 15:06:04 sam Exp $ - * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211_output.c,v 1.20 2007/04/01 13:59:41 sephe Exp $ + * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211_output.c,v 1.21 2007/05/07 14:12:16 sephe Exp $ */ #include "opt_inet.h" @@ -383,7 +383,8 @@ ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize, if (key != NULL) { /* XXX belongs in crypto code? */ - needed_space += key->wk_cipher->ic_header; + if ((key->wk_flags & IEEE80211_KEY_NOHDR) == 0) + needed_space += key->wk_cipher->ic_header; /* XXX frags */ /* * When crypto is being done in the host we must insure diff --git a/sys/netproto/802_11/wlan_ccmp/ieee80211_crypto_ccmp.c b/sys/netproto/802_11/wlan_ccmp/ieee80211_crypto_ccmp.c index e73140e041..15947052ef 100644 --- a/sys/netproto/802_11/wlan_ccmp/ieee80211_crypto_ccmp.c +++ b/sys/netproto/802_11/wlan_ccmp/ieee80211_crypto_ccmp.c @@ -29,7 +29,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/net80211/ieee80211_crypto_ccmp.c,v 1.7.2.1 2005/12/22 19:02:08 sam Exp $ - * $DragonFly: src/sys/netproto/802_11/wlan_ccmp/ieee80211_crypto_ccmp.c,v 1.4 2006/12/23 00:57:53 swildner Exp $ + * $DragonFly: src/sys/netproto/802_11/wlan_ccmp/ieee80211_crypto_ccmp.c,v 1.5 2007/05/07 14:12:16 sephe Exp $ */ /* @@ -71,6 +71,11 @@ static int ccmp_encap(struct ieee80211_key *k, struct mbuf *, uint8_t keyid); static int ccmp_decap(struct ieee80211_key *, struct mbuf *, int); static int ccmp_enmic(struct ieee80211_key *, struct mbuf *, int); static int ccmp_demic(struct ieee80211_key *, struct mbuf *, int); +static int ccmp_getiv(struct ieee80211_key *, struct ieee80211_crypto_iv *, + uint8_t); +static int ccmp_update(struct ieee80211_key *, + const struct ieee80211_crypto_iv *, + const struct ieee80211_frame *); static const struct ieee80211_cipher ccmp = { .ic_name = "AES-CCM", @@ -86,6 +91,8 @@ static const struct ieee80211_cipher ccmp = { .ic_decap = ccmp_decap, .ic_enmic = ccmp_enmic, .ic_demic = ccmp_demic, + .ic_getiv = ccmp_getiv, + .ic_update = ccmp_update }; static int ccmp_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen); @@ -181,6 +188,25 @@ ccmp_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid) return 1; } +static int +ccmp_getiv(struct ieee80211_key *k, struct ieee80211_crypto_iv *iv, + uint8_t keyid) +{ + uint8_t *ivp = (uint8_t *)iv; + + k->wk_keytsc++; /* XXX wrap at 48 bits */ + ivp[0] = k->wk_keytsc >> 0; /* PN0 */ + ivp[1] = k->wk_keytsc >> 8; /* PN1 */ + ivp[2] = 0; /* Reserved */ + ivp[3] = keyid | IEEE80211_WEP_EXTIV; /* KeyID | ExtID */ + ivp[4] = k->wk_keytsc >> 16; /* PN2 */ + ivp[5] = k->wk_keytsc >> 24; /* PN3 */ + ivp[6] = k->wk_keytsc >> 32; /* PN4 */ + ivp[7] = k->wk_keytsc >> 40; /* PN5 */ + + return 1; +} + /* * Add MIC to the frame as needed. */ @@ -263,6 +289,45 @@ ccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) return 1; } +static int +ccmp_update(struct ieee80211_key *k, const struct ieee80211_crypto_iv *iv, + const struct ieee80211_frame *wh) +{ + struct ccmp_ctx *ctx = k->wk_private; + const uint8_t *ivp = (const uint8_t *)iv; + uint64_t pn; + + /* + * Header should have extended IV and sequence number; + * verify the former and validate the latter. + */ + if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) { + /* + * No extended IV; discard frame. + */ + IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO, + "[%6D] Missing ExtIV for AES-CCM cipher\n", + wh->i_addr2, ":"); + ctx->cc_ic->ic_stats.is_rx_ccmpformat++; + return 0; + } + pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]); + if (pn <= k->wk_keyrsc) { + /* + * Replay violation. + */ + ieee80211_notify_replay_failure(ctx->cc_ic, wh, k, pn); + ctx->cc_ic->ic_stats.is_rx_ccmpreplay++; + return 0; + } + + /* + * Ok to update rsc now. + */ + k->wk_keyrsc = pn; + return 1; +} + /* * Verify and strip MIC from the frame. */ diff --git a/sys/netproto/802_11/wlan_tkip/ieee80211_crypto_tkip.c b/sys/netproto/802_11/wlan_tkip/ieee80211_crypto_tkip.c index 7b9cc817ed..609e5e6329 100644 --- a/sys/netproto/802_11/wlan_tkip/ieee80211_crypto_tkip.c +++ b/sys/netproto/802_11/wlan_tkip/ieee80211_crypto_tkip.c @@ -29,7 +29,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/net80211/ieee80211_crypto_tkip.c,v 1.9.2.2 2005/12/22 19:02:08 sam Exp $ - * $DragonFly: src/sys/netproto/802_11/wlan_tkip/ieee80211_crypto_tkip.c,v 1.4 2006/12/23 00:57:53 swildner Exp $ + * $DragonFly: src/sys/netproto/802_11/wlan_tkip/ieee80211_crypto_tkip.c,v 1.5 2007/05/07 14:12:16 sephe Exp $ */ /* @@ -63,6 +63,11 @@ static int tkip_encap(struct ieee80211_key *, struct mbuf *m, uint8_t keyid); static int tkip_enmic(struct ieee80211_key *, struct mbuf *, int); static int tkip_decap(struct ieee80211_key *, struct mbuf *, int); static int tkip_demic(struct ieee80211_key *, struct mbuf *, int); +static int tkip_getiv(struct ieee80211_key *, struct ieee80211_crypto_iv *, + uint8_t); +static int tkip_update(struct ieee80211_key *, + const struct ieee80211_crypto_iv *, + const struct ieee80211_frame *); static const struct ieee80211_cipher tkip = { .ic_name = "TKIP", @@ -78,6 +83,8 @@ static const struct ieee80211_cipher tkip = { .ic_decap = tkip_decap, .ic_enmic = tkip_enmic, .ic_demic = tkip_demic, + .ic_getiv = tkip_getiv, + .ic_update = tkip_update }; #define memmove(dst, src, n) ovbcopy(src, dst, n) @@ -207,6 +214,38 @@ tkip_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid) return 1; } +static int +tkip_getiv(struct ieee80211_key *k, struct ieee80211_crypto_iv *iv, + uint8_t keyid) +{ + struct tkip_ctx *ctx = k->wk_private; + struct ieee80211com *ic = ctx->tc_ic; + uint8_t *ivp = (uint8_t *)iv; + + /* + * Handle TKIP counter measures requirement. + */ + if (ic->ic_flags & IEEE80211_F_COUNTERM) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "Discard frame due to countermeasures (%s)\n", + __func__); + ic->ic_stats.is_crypto_tkipcm++; + return 0; + } + + ivp[0] = k->wk_keytsc >> 8; /* TSC1 */ + ivp[1] = (ivp[0] | 0x20) & 0x7f; /* WEP seed */ + ivp[2] = k->wk_keytsc >> 0; /* TSC0 */ + ivp[3] = keyid | IEEE80211_WEP_EXTIV; /* KeyID | ExtID */ + ivp[4] = k->wk_keytsc >> 16; /* TSC2 */ + ivp[5] = k->wk_keytsc >> 24; /* TSC3 */ + ivp[6] = k->wk_keytsc >> 32; /* TSC4 */ + ivp[7] = k->wk_keytsc >> 40; /* TSC5 */ + + k->wk_keytsc++; + return 1; +} + /* * Add MIC to the frame as needed. */ @@ -317,6 +356,60 @@ tkip_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) return 1; } +static int +tkip_update(struct ieee80211_key *k, const struct ieee80211_crypto_iv *iv, + const struct ieee80211_frame *wh) +{ + struct tkip_ctx *ctx = k->wk_private; + struct ieee80211com *ic = ctx->tc_ic; + const uint8_t *ivp = (const uint8_t *)iv; + + /* + * Header should have extended IV and sequence number; + * verify the former and validate the latter. + */ + if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) { + /* + * No extended IV; discard frame. + */ + IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO, + "[%6D] missing ExtIV for TKIP cipher\n", + wh->i_addr2, ":"); + ctx->tc_ic->ic_stats.is_rx_tkipformat++; + return 0; + } + /* + * Handle TKIP counter measures requirement. + */ + if (ic->ic_flags & IEEE80211_F_COUNTERM) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + "[%6D] discard frame due to countermeasures (%s)\n", + wh->i_addr2, ":", __func__); + ic->ic_stats.is_crypto_tkipcm++; + return 0; + } + + ctx->rx_rsc = READ_6(ivp[2], ivp[0], ivp[4], ivp[5], ivp[6], ivp[7]); + if (ctx->rx_rsc <= k->wk_keyrsc) { + /* + * Replay violation; notify upper layer. + */ + ieee80211_notify_replay_failure(ctx->tc_ic, wh, k, ctx->rx_rsc); + ctx->tc_ic->ic_stats.is_rx_tkipreplay++; + return 0; + } + + /* + * NB: We can't update the rsc in the key until MIC is verified. + * + * We assume we are not preempted between doing the check above + * and updating wk_keyrsc when stripping the MIC in tkip_demic. + * Otherwise we might process another packet and discard it as + * a replay. + */ + return 1; +} + /* * Verify and strip MIC from the frame. */ @@ -347,10 +440,13 @@ tkip_demic(struct ieee80211_key *k, struct mbuf *m, int force) return 0; } } - /* - * Strip MIC from the tail. - */ - m_adj(m, -tkip.ic_miclen); + + if (force || (k->wk_flags & IEEE80211_KEY_NOMIC) == 0) { + /* + * Strip MIC from the tail. + */ + m_adj(m, -tkip.ic_miclen); + } /* * Ok to update rsc now that MIC has been verified. diff --git a/sys/netproto/802_11/wlan_wep/ieee80211_crypto_wep.c b/sys/netproto/802_11/wlan_wep/ieee80211_crypto_wep.c index 9aa9107bbb..e2b4ee27ed 100644 --- a/sys/netproto/802_11/wlan_wep/ieee80211_crypto_wep.c +++ b/sys/netproto/802_11/wlan_wep/ieee80211_crypto_wep.c @@ -29,7 +29,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/net80211/ieee80211_crypto_wep.c,v 1.7.2.1 2005/12/22 19:02:08 sam Exp $ - * $DragonFly: src/sys/netproto/802_11/wlan_wep/ieee80211_crypto_wep.c,v 1.4 2006/12/23 00:57:53 swildner Exp $ + * $DragonFly: src/sys/netproto/802_11/wlan_wep/ieee80211_crypto_wep.c,v 1.5 2007/05/07 14:12:16 sephe Exp $ */ /* @@ -59,6 +59,11 @@ static int wep_encap(struct ieee80211_key *, struct mbuf *, uint8_t keyid); static int wep_decap(struct ieee80211_key *, struct mbuf *, int hdrlen); static int wep_enmic(struct ieee80211_key *, struct mbuf *, int); static int wep_demic(struct ieee80211_key *, struct mbuf *, int); +static int wep_getiv(struct ieee80211_key *, struct ieee80211_crypto_iv *, + uint8_t); +static int wep_update(struct ieee80211_key *, + const struct ieee80211_crypto_iv *, + const struct ieee80211_frame *); static const struct ieee80211_cipher wep = { .ic_name = "WEP", @@ -73,6 +78,8 @@ static const struct ieee80211_cipher wep = { .ic_decap = wep_decap, .ic_enmic = wep_enmic, .ic_demic = wep_demic, + .ic_getiv = wep_getiv, + .ic_update = wep_update }; static int wep_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen); @@ -241,6 +248,13 @@ wep_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) return 1; } +static int +wep_update(struct ieee80211_key *k, const struct ieee80211_crypto_iv *iv, + const struct ieee80211_frame *wh) +{ + return 1; +} + /* * Verify and strip MIC from the frame. */ @@ -250,6 +264,43 @@ wep_demic(struct ieee80211_key *k, struct mbuf *skb, int force) return 1; } +static int +wep_getiv(struct ieee80211_key *k, struct ieee80211_crypto_iv *ivp, + uint8_t keyid) +{ + struct wep_ctx *ctx = k->wk_private; + uint32_t iv; + + /* + * Skip 'bad' IVs from Fluhrer/Mantin/Shamir: + * (B, 255, N) with 3 <= B < 16 and 0 <= N <= 255 + */ + iv = ctx->wc_iv; + if ((iv & 0xff00) == 0xff00) { + int B = (iv & 0xff0000) >> 16; + if (3 <= B && B < 16) + iv += 0x0100; + } + ctx->wc_iv = iv + 1; + + /* + * NB: Preserve byte order of IV for packet + * sniffers; it doesn't matter otherwise. + */ +#if _BYTE_ORDER == _BIG_ENDIAN + ivp->ic_iv[0] = iv >> 0; + ivp->ic_iv[1] = iv >> 8; + ivp->ic_iv[2] = iv >> 16; +#else + ivp->ic_iv[2] = iv >> 0; + ivp->ic_iv[1] = iv >> 8; + ivp->ic_iv[0] = iv >> 16; +#endif + ivp->ic_iv[3] = keyid; + + return 1; +} + static const uint32_t crc32_table[256] = { 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, -- 2.11.4.GIT