/* $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 #include #include #include #include #include #include #include #include #if defined(__FreeBSD__) && __FreeBSD_version >= 400000 #include #else #include #endif #include #include #ifdef __FreeBSD__ #include #include #else #include #endif #include #include #include #include #ifdef __NetBSD__ #include #include #include #include #include #endif #ifdef __FreeBSD__ #include #include #include #include #include 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); }