diff --git a/src/config/config_net80211.c b/src/config/config_net80211.c index bcb76a74..59986b90 100644 --- a/src/config/config_net80211.c +++ b/src/config/config_net80211.c @@ -31,3 +31,10 @@ REQUIRE_OBJECT ( iwmgmt_cmd ); REQUIRE_OBJECT ( wireless_errors ); #endif +/* + * Drag in 802.11 cryptosystems and handshaking protocols + * + */ +#ifdef CRYPTO_80211_WEP +REQUIRE_OBJECT ( wep ); +#endif diff --git a/src/config/general.h b/src/config/general.h index 0acc00d3..c0370ca4 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -64,6 +64,12 @@ FILE_LICENCE ( GPL2_OR_LATER ); //#undef SANBOOT_PROTO_AOE /* AoE protocol */ //#undef SANBOOT_PROTO_IB_SRP /* Infiniband SCSI RDMA protocol */ +/* + * 802.11 cryptosystems and handshaking protocols + * + */ +#define CRYPTO_80211_WEP /* WEP encryption (deprecated and insecure!) */ + /* * Name resolution modules * diff --git a/src/include/gpxe/errfile.h b/src/include/gpxe/errfile.h index 55dbb6f6..8a997e1a 100644 --- a/src/include/gpxe/errfile.h +++ b/src/include/gpxe/errfile.h @@ -159,6 +159,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_ib_cmrc ( ERRFILE_NET | 0x00210000 ) #define ERRFILE_ib_srp ( ERRFILE_NET | 0x00220000 ) #define ERRFILE_sec80211 ( ERRFILE_NET | 0x00230000 ) +#define ERRFILE_wep ( ERRFILE_NET | 0x00240000 ) #define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 ) diff --git a/src/net/80211/wep.c b/src/net/80211/wep.c new file mode 100644 index 00000000..1c37e0c3 --- /dev/null +++ b/src/net/80211/wep.c @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2009 Joshua Oreman . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * The WEP wireless encryption method (insecure!) + * + * The data field in a WEP-encrypted packet contains a 3-byte + * initialisation vector, one-byte Key ID field (only the bottom two + * bits are ever used), encrypted data, and a 4-byte encrypted CRC of + * the plaintext data, called the ICV. To decrypt it, the IV is + * prepended to the shared key and the data stream (including ICV) is + * run through the ARC4 stream cipher; if the ICV matches a CRC32 + * calculated on the plaintext, the packet is valid. + * + * For efficiency and code-size reasons, this file assumes it is + * running on a little-endian machine. + */ + +/** Length of WEP initialisation vector */ +#define WEP_IV_LEN 3 + +/** Length of WEP key ID byte */ +#define WEP_KID_LEN 1 + +/** Length of WEP ICV checksum */ +#define WEP_ICV_LEN 4 + +/** Maximum length of WEP key */ +#define WEP_MAX_KEY 16 + +/** Amount of data placed before the encrypted bytes */ +#define WEP_HEADER_LEN 4 + +/** Amount of data placed after the encrypted bytes */ +#define WEP_TRAILER_LEN 4 + +/** Total WEP overhead bytes */ +#define WEP_OVERHEAD 8 + +/** Context for WEP encryption and decryption */ +struct wep_ctx +{ + /** Encoded WEP key + * + * The actual key bytes are stored beginning at offset 3, to + * leave room for easily inserting the IV before a particular + * operation. + */ + u8 key[WEP_IV_LEN + WEP_MAX_KEY]; + + /** Length of WEP key (not including IV bytes) */ + int keylen; + + /** ARC4 context */ + struct arc4_ctx arc4; +}; + +/** + * Initialize WEP algorithm + * + * @v crypto 802.11 cryptographic algorithm + * @v key WEP key to use + * @v keylen Length of WEP key + * @v rsc Initial receive sequence counter (unused) + * @ret rc Return status code + * + * Standard key lengths are 5 and 13 bytes; 16-byte keys are + * occasionally supported as an extension to the standard. + */ +static int wep_init ( struct net80211_crypto *crypto, const void *key, + int keylen, const void *rsc __unused ) +{ + struct wep_ctx *ctx = crypto->priv; + + ctx->keylen = ( keylen > WEP_MAX_KEY ? WEP_MAX_KEY : keylen ); + memcpy ( ctx->key + WEP_IV_LEN, key, ctx->keylen ); + + return 0; +} + +/** + * Encrypt packet using WEP + * + * @v crypto 802.11 cryptographic algorithm + * @v iob I/O buffer of plaintext packet + * @ret eiob Newly allocated I/O buffer for encrypted packet, or NULL + * + * If memory allocation fails, @c NULL is returned. + */ +static struct io_buffer * wep_encrypt ( struct net80211_crypto *crypto, + struct io_buffer *iob ) +{ + struct wep_ctx *ctx = crypto->priv; + struct io_buffer *eiob; + struct ieee80211_frame *hdr; + const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN; + int datalen = iob_len ( iob ) - hdrlen; + int newlen = hdrlen + datalen + WEP_OVERHEAD; + u32 iv, icv; + + eiob = alloc_iob ( newlen ); + if ( ! eiob ) + return NULL; + + memcpy ( iob_put ( eiob, hdrlen ), iob->data, hdrlen ); + hdr = eiob->data; + hdr->fc |= IEEE80211_FC_PROTECTED; + + /* Calculate IV, put it in the header (with key ID byte = 0), and + set it up at the start of the encryption key. */ + iv = random() & 0xffffff; /* IV in bottom 3 bytes, top byte = KID = 0 */ + memcpy ( iob_put ( eiob, WEP_HEADER_LEN ), &iv, WEP_HEADER_LEN ); + memcpy ( ctx->key, &iv, WEP_IV_LEN ); + + /* Encrypt the data using RC4 */ + cipher_setkey ( &arc4_algorithm, &ctx->arc4, ctx->key, + ctx->keylen + WEP_IV_LEN ); + cipher_encrypt ( &arc4_algorithm, &ctx->arc4, iob->data + hdrlen, + iob_put ( eiob, datalen ), datalen ); + + /* Add ICV */ + icv = ~crc32_le ( ~0, iob->data + hdrlen, datalen ); + cipher_encrypt ( &arc4_algorithm, &ctx->arc4, &icv, + iob_put ( eiob, WEP_ICV_LEN ), WEP_ICV_LEN ); + + return eiob; +} + +/** + * Decrypt packet using WEP + * + * @v crypto 802.11 cryptographic algorithm + * @v eiob I/O buffer of encrypted packet + * @ret iob Newly allocated I/O buffer for plaintext packet, or NULL + * + * If a consistency check for the decryption fails (usually indicating + * an invalid key), @c NULL is returned. + */ +static struct io_buffer * wep_decrypt ( struct net80211_crypto *crypto, + struct io_buffer *eiob ) +{ + struct wep_ctx *ctx = crypto->priv; + struct io_buffer *iob; + struct ieee80211_frame *hdr; + const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN; + int datalen = iob_len ( eiob ) - hdrlen - WEP_OVERHEAD; + int newlen = hdrlen + datalen; + u32 iv, icv, crc; + + iob = alloc_iob ( newlen ); + if ( ! iob ) + return NULL; + + memcpy ( iob_put ( iob, hdrlen ), eiob->data, hdrlen ); + hdr = iob->data; + hdr->fc &= ~IEEE80211_FC_PROTECTED; + + /* Strip off IV and use it to initialize cryptosystem */ + memcpy ( &iv, eiob->data + hdrlen, 4 ); + iv &= 0xffffff; /* ignore key ID byte */ + memcpy ( ctx->key, &iv, WEP_IV_LEN ); + + /* Decrypt the data using RC4 */ + cipher_setkey ( &arc4_algorithm, &ctx->arc4, ctx->key, + ctx->keylen + WEP_IV_LEN ); + cipher_decrypt ( &arc4_algorithm, &ctx->arc4, eiob->data + hdrlen + + WEP_HEADER_LEN, iob_put ( iob, datalen ), datalen ); + + /* Strip off ICV and verify it */ + cipher_decrypt ( &arc4_algorithm, &ctx->arc4, eiob->data + hdrlen + + WEP_HEADER_LEN + datalen, &icv, WEP_ICV_LEN ); + crc = ~crc32_le ( ~0, iob->data + hdrlen, datalen ); + if ( crc != icv ) { + DBGC ( crypto, "WEP %p CRC mismatch: expect %08x, get %08x\n", + crypto, icv, crc ); + free_iob ( iob ); + return NULL; + } + return iob; +} + +/** WEP cryptosystem for 802.11 */ +struct net80211_crypto wep_crypto __net80211_crypto = { + .algorithm = NET80211_CRYPT_WEP, + .init = wep_init, + .encrypt = wep_encrypt, + .decrypt = wep_decrypt, + .priv_len = sizeof ( struct wep_ctx ), +}; + +/** + * Initialize trivial 802.11 security handshaker + * + * @v dev 802.11 device + * @v ctx Security handshaker + * + * This simply fetches a WEP key from netX/key, and if it exists, + * installs WEP cryptography on the 802.11 device. No real handshaking + * is performed. + */ +static int trivial_init ( struct net80211_device *dev ) +{ + u8 key[WEP_MAX_KEY]; /* support up to 128-bit keys */ + int len; + int rc; + + if ( dev->associating && + dev->associating->crypto == NET80211_CRYPT_NONE ) + return 0; /* no crypto? OK. */ + + len = fetch_setting ( netdev_settings ( dev->netdev ), + &net80211_key_setting, key, WEP_MAX_KEY ); + + if ( len <= 0 ) { + DBGC ( dev, "802.11 %p cannot do WEP without a key\n", dev ); + return -EACCES; + } + + /* Full 128-bit keys are a nonstandard extension, but they're + utterly trivial to support, so we do. */ + if ( len != 5 && len != 13 && len != 16 ) { + DBGC ( dev, "802.11 %p invalid WEP key length %d\n", + dev, len ); + return -EINVAL; + } + + DBGC ( dev, "802.11 %p installing %d-bit WEP\n", dev, len * 8 ); + + rc = sec80211_install ( &dev->crypto, NET80211_CRYPT_WEP, key, len, + NULL ); + if ( rc < 0 ) + return rc; + + return 0; +} + +/** + * Check for key change on trivial 802.11 security handshaker + * + * @v dev 802.11 device + * @v ctx Security handshaker + */ +static int trivial_change_key ( struct net80211_device *dev ) +{ + u8 key[WEP_MAX_KEY]; + int len; + int change = 0; + + /* If going from WEP to clear, or something else to WEP, reassociate. */ + if ( ! dev->crypto || ( dev->crypto->init != wep_init ) ) + change ^= 1; + + len = fetch_setting ( netdev_settings ( dev->netdev ), + &net80211_key_setting, key, WEP_MAX_KEY ); + if ( len <= 0 ) + change ^= 1; + + /* Changing crypto type => return nonzero to reassociate. */ + if ( change ) + return -EINVAL; + + /* Going from no crypto to still no crypto => nothing to do. */ + if ( len <= 0 ) + return 0; + + /* Otherwise, reinitialise WEP with new key. */ + return wep_init ( dev->crypto, key, len, NULL ); +} + +/** Trivial 802.11 security handshaker */ +struct net80211_handshaker trivial_handshaker __net80211_handshaker = { + .protocol = NET80211_SECPROT_NONE, + .init = trivial_init, + .change_key = trivial_change_key, + .priv_len = 0, +};