diff options
Diffstat (limited to 'sys/dev/awi/awi_wep.c')
-rw-r--r-- | sys/dev/awi/awi_wep.c | 531 |
1 files changed, 531 insertions, 0 deletions
diff --git a/sys/dev/awi/awi_wep.c b/sys/dev/awi/awi_wep.c new file mode 100644 index 0000000..b24bd9f --- /dev/null +++ b/sys/dev/awi/awi_wep.c @@ -0,0 +1,531 @@ +/* $NetBSD: awi_wep.c,v 1.4 2000/08/14 11:28:03 onoe Exp $ */ +/* $FreeBSD$ */ + +/* + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Atsushi Onoe. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``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 FOUNDATION OR CONTRIBUTORS + * 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. + */ + +/* + * WEP support framework for the awi driver. + * + * No actual encryption capability is provided here, but any can be added + * to awi_wep_algo table below. + * + * Note that IEEE802.11 specification states WEP uses RC4 with 40bit key, + * which is a proprietary encryption algorithm available under license + * from RSA Data Security Inc. Using another algorithm, includes null + * encryption provided here, the awi driver cannot be able to communicate + * with other stations. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/mbuf.h> +#include <sys/proc.h> +#include <sys/malloc.h> +#include <sys/socket.h> +#include <sys/errno.h> +#include <sys/sockio.h> +#if defined(__FreeBSD__) && __FreeBSD_version >= 400000 +#include <sys/bus.h> +#else +#include <sys/device.h> +#endif + +#include <net/if.h> +#include <net/if_dl.h> +#ifdef __FreeBSD__ +#include <net/ethernet.h> +#include <net/if_arp.h> +#else +#include <net/if_ether.h> +#endif +#include <net/if_media.h> +#include <net/if_ieee80211.h> + +#include <machine/cpu.h> +#include <machine/bus.h> + +#ifdef __NetBSD__ +#include <dev/ic/am79c930reg.h> +#include <dev/ic/am79c930var.h> +#include <dev/ic/awireg.h> +#include <dev/ic/awivar.h> + +#include <crypto/arc4/arc4.h> +#endif + +#ifdef __FreeBSD__ +#include <dev/awi/am79c930reg.h> +#include <dev/awi/am79c930var.h> +#include <dev/awi/awireg.h> +#include <dev/awi/awivar.h> + +#include <crypto/rc4/rc4.h> +static __inline int +arc4_ctxlen(void) +{ + return sizeof(struct rc4_state); +} + +static __inline void +arc4_setkey(void *ctx, u_int8_t *key, int keylen) +{ + rc4_init(ctx, key, keylen); +} + +static __inline void +arc4_encrypt(void *ctx, u_int8_t *dst, u_int8_t *src, int len) +{ + rc4_crypt(ctx, src, dst, len); +} +#endif + +static void awi_crc_init(void); +static u_int32_t awi_crc_update(u_int32_t crc, u_int8_t *buf, int len); + +static int awi_null_ctxlen(void); +static void awi_null_setkey(void *ctx, u_int8_t *key, int keylen); +static void awi_null_copy(void *ctx, u_int8_t *dst, u_int8_t *src, int len); + +/* XXX: the order should be known to wiconfig/user */ + +static struct awi_wep_algo awi_wep_algo[] = { +/* 0: no wep */ + { "no" }, /* dummy for no wep */ + +/* 1: normal wep (arc4) */ + { "arc4", arc4_ctxlen, arc4_setkey, + arc4_encrypt, arc4_encrypt }, + +/* 2: debug wep (null) */ + { "null", awi_null_ctxlen, awi_null_setkey, + awi_null_copy, awi_null_copy }, + /* dummy for wep without encryption */ +}; + +int +awi_wep_setnwkey(sc, nwkey) + struct awi_softc *sc; + struct ieee80211_nwkey *nwkey; +{ + int i, len, error; + u_int8_t keybuf[AWI_MAX_KEYLEN]; + + if (nwkey->i_defkid <= 0 || + nwkey->i_defkid > IEEE80211_WEP_NKID) + return EINVAL; + error = 0; + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + if (nwkey->i_key[i].i_keydat == NULL) + continue; + len = nwkey->i_key[i].i_keylen; + if (len > sizeof(keybuf)) { + error = EINVAL; + break; + } + error = copyin(nwkey->i_key[i].i_keydat, keybuf, len); + if (error) + break; + error = awi_wep_setkey(sc, i, keybuf, len); + if (error) + break; + } + if (error == 0) { + sc->sc_wep_defkid = nwkey->i_defkid - 1; + error = awi_wep_setalgo(sc, nwkey->i_wepon); + if (error == 0 && sc->sc_enabled) { + awi_stop(sc); + error = awi_init(sc); + } + } + return error; +} + +int +awi_wep_getnwkey(sc, nwkey) + struct awi_softc *sc; + struct ieee80211_nwkey *nwkey; +{ + int i, len, error, suerr; + u_int8_t keybuf[AWI_MAX_KEYLEN]; + + nwkey->i_wepon = awi_wep_getalgo(sc); + nwkey->i_defkid = sc->sc_wep_defkid + 1; + /* do not show any keys to non-root user */ +#ifdef __FreeBSD__ +#if __FreeBSD_version < 500028 + suerr = suser(curproc); +#else + suerr = suser(curthread); +#endif +#else + suerr = suser(curproc->p_ucred, &curproc->p_acflag); +#endif + error = 0; + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + if (nwkey->i_key[i].i_keydat == NULL) + continue; + if (suerr) { + error = suerr; + break; + } + len = sizeof(keybuf); + error = awi_wep_getkey(sc, i, keybuf, &len); + if (error) + break; + if (nwkey->i_key[i].i_keylen < len) { + error = ENOSPC; + break; + } + nwkey->i_key[i].i_keylen = len; + error = copyout(keybuf, nwkey->i_key[i].i_keydat, len); + if (error) + break; + } + return error; +} + +int +awi_wep_getalgo(sc) + struct awi_softc *sc; +{ + + if (sc->sc_wep_algo == NULL) + return 0; + return sc->sc_wep_algo - awi_wep_algo; +} + +int +awi_wep_setalgo(sc, algo) + struct awi_softc *sc; + int algo; +{ + struct awi_wep_algo *awa; + int ctxlen; + + awi_crc_init(); /* XXX: not belongs here */ + if (algo < 0 || algo > sizeof(awi_wep_algo)/sizeof(awi_wep_algo[0])) + return EINVAL; + awa = &awi_wep_algo[algo]; + if (awa->awa_name == NULL) + return EINVAL; + if (awa->awa_ctxlen == NULL) { + awa = NULL; + ctxlen = 0; + } else + ctxlen = awa->awa_ctxlen(); + if (sc->sc_wep_ctx != NULL) { + free(sc->sc_wep_ctx, M_DEVBUF); + sc->sc_wep_ctx = NULL; + } + if (ctxlen) { + sc->sc_wep_ctx = malloc(ctxlen, M_DEVBUF, M_NOWAIT); + if (sc->sc_wep_ctx == NULL) + return ENOMEM; + } + sc->sc_wep_algo = awa; + return 0; +} + +int +awi_wep_setkey(sc, kid, key, keylen) + struct awi_softc *sc; + int kid; + unsigned char *key; + int keylen; +{ + + if (kid < 0 || kid >= IEEE80211_WEP_NKID) + return EINVAL; + if (keylen < 0 || keylen + IEEE80211_WEP_IVLEN > AWI_MAX_KEYLEN) + return EINVAL; + sc->sc_wep_keylen[kid] = keylen; + if (keylen > 0) + memcpy(sc->sc_wep_key[kid] + IEEE80211_WEP_IVLEN, key, keylen); + return 0; +} + +int +awi_wep_getkey(sc, kid, key, keylen) + struct awi_softc *sc; + int kid; + unsigned char *key; + int *keylen; +{ + + if (kid < 0 || kid >= IEEE80211_WEP_NKID) + return EINVAL; + if (*keylen < sc->sc_wep_keylen[kid]) + return ENOSPC; + *keylen = sc->sc_wep_keylen[kid]; + if (*keylen > 0) + memcpy(key, sc->sc_wep_key[kid] + IEEE80211_WEP_IVLEN, *keylen); + return 0; +} + +struct mbuf * +awi_wep_encrypt(sc, m0, txflag) + struct awi_softc *sc; + struct mbuf *m0; + int txflag; +{ + struct mbuf *m, *n, *n0; + struct ieee80211_frame *wh; + struct awi_wep_algo *awa; + int left, len, moff, noff, keylen, kid; + u_int32_t iv, crc; + u_int8_t *key, *ivp; + void *ctx; + u_int8_t crcbuf[IEEE80211_WEP_CRCLEN]; + + n0 = NULL; + awa = sc->sc_wep_algo; + if (awa == NULL) + goto fail; + ctx = sc->sc_wep_ctx; + m = m0; + left = m->m_pkthdr.len; + MGET(n, M_DONTWAIT, m->m_type); + n0 = n; + if (n == NULL) + goto fail; + M_COPY_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; + } + len = sizeof(struct ieee80211_frame); + memcpy(mtod(n, caddr_t), mtod(m, caddr_t), len); + left -= len; + moff = len; + noff = len; + if (txflag) { + kid = sc->sc_wep_defkid; + wh = mtod(n, struct ieee80211_frame *); + wh->i_fc[1] |= IEEE80211_FC1_WEP; + iv = random(); + /* + * store IV, byte order is not the matter since it's random. + * assuming IEEE80211_WEP_IVLEN is 3 + */ + ivp = mtod(n, u_int8_t *) + noff; + ivp[0] = (iv >> 16) & 0xff; + ivp[1] = (iv >> 8) & 0xff; + ivp[2] = iv & 0xff; + ivp[IEEE80211_WEP_IVLEN] = kid << 6; /* pad and keyid */ + noff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN; + } else { + ivp = mtod(m, u_int8_t *) + moff; + kid = ivp[IEEE80211_WEP_IVLEN] >> 6; + moff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN; + } + key = sc->sc_wep_key[kid]; + keylen = sc->sc_wep_keylen[kid]; + /* assuming IEEE80211_WEP_IVLEN is 3 */ + key[0] = ivp[0]; + key[1] = ivp[1]; + key[2] = ivp[2]; + awa->awa_setkey(ctx, key, IEEE80211_WEP_IVLEN + keylen); + + /* 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) + 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; + if (txflag) { + awa->awa_encrypt(ctx, mtod(n, caddr_t) + noff, + mtod(m, caddr_t) + moff, len); + crc = awi_crc_update(crc, mtod(m, caddr_t) + moff, len); + } else { + awa->awa_decrypt(ctx, mtod(n, caddr_t) + noff, + mtod(m, caddr_t) + moff, len); + crc = awi_crc_update(crc, mtod(n, caddr_t) + noff, len); + } + left -= len; + moff += len; + noff += len; + } + crc = ~crc; + if (txflag) { + LE_WRITE_4(crcbuf, 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) + goto fail; + n = n->m_next; + n->m_len = sizeof(crcbuf); + noff = 0; + } + awa->awa_encrypt(ctx, mtod(n, caddr_t) + noff, crcbuf, + 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) + awa->awa_decrypt(ctx, crcbuf + noff, + mtod(m, caddr_t) + moff, len); + m = m->m_next; + moff = 0; + } + if (crc != LE_READ_4(crcbuf)) + goto fail; + } + m_freem(m0); + return n0; + + fail: + m_freem(m0); + m_freem(n0); + return NULL; +} + +/* + * CRC 32 -- routine from RFC 2083 + */ + +/* Table of CRCs of all 8-bit messages */ +static u_int32_t awi_crc_table[256]; +static int awi_crc_table_computed = 0; + +/* Make the table for a fast CRC. */ +static void +awi_crc_init() +{ + u_int32_t c; + int n, k; + + if (awi_crc_table_computed) + return; + for (n = 0; n < 256; n++) { + c = (u_int32_t)n; + for (k = 0; k < 8; k++) { + if (c & 1) + c = 0xedb88320UL ^ (c >> 1); + else + c = c >> 1; + } + awi_crc_table[n] = c; + } + awi_crc_table_computed = 1; +} + +/* + * Update a running CRC with the bytes buf[0..len-1]--the CRC + * should be initialized to all 1's, and the transmitted value + * is the 1's complement of the final running CRC + */ + +static u_int32_t +awi_crc_update(crc, buf, len) + u_int32_t crc; + u_int8_t *buf; + int len; +{ + u_int8_t *endbuf; + + for (endbuf = buf + len; buf < endbuf; buf++) + crc = awi_crc_table[(crc ^ *buf) & 0xff] ^ (crc >> 8); + return crc; +} + +/* + * Null -- do nothing but copy. + */ + +static int +awi_null_ctxlen() +{ + + return 0; +} + +static void +awi_null_setkey(ctx, key, keylen) + void *ctx; + u_char *key; + int keylen; +{ +} + +static void +awi_null_copy(ctx, dst, src, len) + void *ctx; + u_char *dst; + u_char *src; + int len; +{ + + memcpy(dst, src, len); +} |