summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoshua Oreman2009-06-24 09:37:52 +0200
committerMarty Connor2010-01-05 15:14:08 +0100
commit01b4f520893b369da1fffd148dfa4b882ea4f0e5 (patch)
tree01718afbb55cfa117105187c24d3759d811e8ab4
parent[iwmgmt] Add wireless management commands and text for common errors (diff)
downloadipxe-01b4f520893b369da1fffd148dfa4b882ea4f0e5.tar.gz
ipxe-01b4f520893b369da1fffd148dfa4b882ea4f0e5.tar.xz
ipxe-01b4f520893b369da1fffd148dfa4b882ea4f0e5.zip
[802.11] Add support for WEP-protected networks
WEP is a highly flawed cryptosystem, barely better than no encryption at all, but many people still use it. It does have the advantage of being very simple and small in code size. Signed-off-by: Marty Connor <mdc@etherboot.org>
-rw-r--r--src/config/config_net80211.c7
-rw-r--r--src/config/general.h6
-rw-r--r--src/include/gpxe/errfile.h1
-rw-r--r--src/net/80211/wep.c303
4 files changed, 317 insertions, 0 deletions
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
@@ -65,6 +65,12 @@ FILE_LICENCE ( GPL2_OR_LATER );
//#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 <oremanj@rwcr.net>.
+ *
+ * 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 <gpxe/net80211.h>
+#include <gpxe/sec80211.h>
+#include <gpxe/crypto.h>
+#include <gpxe/arc4.h>
+#include <gpxe/crc32.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+/** @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,
+};