diff options
Diffstat (limited to 'sys/netinet6/ah_core.c')
-rw-r--r-- | sys/netinet6/ah_core.c | 1125 |
1 files changed, 1125 insertions, 0 deletions
diff --git a/sys/netinet6/ah_core.c b/sys/netinet6/ah_core.c new file mode 100644 index 0000000..e30bf2a --- /dev/null +++ b/sys/netinet6/ah_core.c @@ -0,0 +1,1125 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * 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. Neither the name of the project 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 PROJECT 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 PROJECT 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. + * + * $FreeBSD$ + */ + +/* + * RFC1826/2402 authentication header. + */ + +#include "opt_inet6.h" +#include "opt_ipsec.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/domain.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/errno.h> +#include <sys/time.h> +#include <sys/kernel.h> + +#include <net/if.h> +#include <net/route.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/in_var.h> +#include <netinet/in_pcb.h> + +#ifdef INET6 +#include <netinet6/ip6.h> +#include <netinet6/ip6_var.h> +#include <netinet6/icmp6.h> +#endif + +#include <netinet6/ipsec.h> +#include <netinet6/ah.h> +#ifdef INET6 +#include <netinet6/ipsec6.h> +#include <netinet6/ah6.h> +#endif +#ifdef IPSEC_ESP +#include <netinet6/esp.h> +#ifdef INET6 +#include <netinet6/esp6.h> +#endif +#endif +#include <net/pfkeyv2.h> +#include <netkey/key_var.h> +#include <netkey/keydb.h> +#include <sys/md5.h> +#include <crypto/sha1.h> + +#include <net/net_osdep.h> + +#define HMACSIZE 16 + +#ifdef INET6 +#define ZEROBUFLEN 256 +static char zerobuf[ZEROBUFLEN]; +#endif + +static int ah_sumsiz_1216 __P((struct secasvar *)); +static int ah_sumsiz_zero __P((struct secasvar *)); +static int ah_none_mature __P((struct secasvar *)); +static void ah_none_init __P((struct ah_algorithm_state *, + struct secasvar *)); +static void ah_none_loop __P((struct ah_algorithm_state *, caddr_t, size_t)); +static void ah_none_result __P((struct ah_algorithm_state *, caddr_t)); +static int ah_keyed_md5_mature __P((struct secasvar *)); +static void ah_keyed_md5_init __P((struct ah_algorithm_state *, + struct secasvar *)); +static void ah_keyed_md5_loop __P((struct ah_algorithm_state *, caddr_t, + size_t)); +static void ah_keyed_md5_result __P((struct ah_algorithm_state *, caddr_t)); +static int ah_keyed_sha1_mature __P((struct secasvar *)); +static void ah_keyed_sha1_init __P((struct ah_algorithm_state *, + struct secasvar *)); +static void ah_keyed_sha1_loop __P((struct ah_algorithm_state *, caddr_t, + size_t)); +static void ah_keyed_sha1_result __P((struct ah_algorithm_state *, caddr_t)); +static int ah_hmac_md5_mature __P((struct secasvar *)); +static void ah_hmac_md5_init __P((struct ah_algorithm_state *, + struct secasvar *)); +static void ah_hmac_md5_loop __P((struct ah_algorithm_state *, caddr_t, + size_t)); +static void ah_hmac_md5_result __P((struct ah_algorithm_state *, caddr_t)); +static int ah_hmac_sha1_mature __P((struct secasvar *)); +static void ah_hmac_sha1_init __P((struct ah_algorithm_state *, + struct secasvar *)); +static void ah_hmac_sha1_loop __P((struct ah_algorithm_state *, caddr_t, + size_t)); +static void ah_hmac_sha1_result __P((struct ah_algorithm_state *, caddr_t)); + +/* checksum algorithms */ +/* NOTE: The order depends on SADB_AALG_x in netkey/keyv2.h */ +struct ah_algorithm ah_algorithms[] = { + { 0, 0, 0, 0, 0, 0, }, + { ah_sumsiz_1216, ah_hmac_md5_mature, 128, 128, + ah_hmac_md5_init, ah_hmac_md5_loop, ah_hmac_md5_result, }, + { ah_sumsiz_1216, ah_hmac_sha1_mature, 160, 160, + ah_hmac_sha1_init, ah_hmac_sha1_loop, ah_hmac_sha1_result, }, + { ah_sumsiz_1216, ah_keyed_md5_mature, 128, 128, + ah_keyed_md5_init, ah_keyed_md5_loop, ah_keyed_md5_result, }, + { ah_sumsiz_1216, ah_keyed_sha1_mature, 160, 160, + ah_keyed_sha1_init, ah_keyed_sha1_loop, ah_keyed_sha1_result, }, + { ah_sumsiz_zero, ah_none_mature, 0, 2048, + ah_none_init, ah_none_loop, ah_none_result, }, +}; + +static int +ah_sumsiz_1216(sav) + struct secasvar *sav; +{ + if (!sav) + return -1; + if (sav->flags & SADB_X_EXT_OLD) + return 16; + else + return 12; +} + +static int +ah_sumsiz_zero(sav) + struct secasvar *sav; +{ + if (!sav) + return -1; + return 0; +} + +static int +ah_none_mature(sav) + struct secasvar *sav; +{ + if (sav->sah->saidx.proto == IPPROTO_AH) { + printf("ah_none_mature: protocol and algorithm mismatch.\n"); + return 1; + } + return 0; +} + +static void +ah_none_init(state, sav) + struct ah_algorithm_state *state; + struct secasvar *sav; +{ + state->foo = NULL; +} + +static void +ah_none_loop(state, addr, len) + struct ah_algorithm_state *state; + caddr_t addr; + size_t len; +{ +} + +static void +ah_none_result(state, addr) + struct ah_algorithm_state *state; + caddr_t addr; +{ +} + +static int +ah_keyed_md5_mature(sav) + struct secasvar *sav; +{ + /* anything is okay */ + return 0; +} + +static void +ah_keyed_md5_init(state, sav) + struct ah_algorithm_state *state; + struct secasvar *sav; +{ + if (!state) + panic("ah_keyed_md5_init: what?"); + + state->sav = sav; + state->foo = (void *)malloc(sizeof(MD5_CTX), M_TEMP, M_NOWAIT); + if (state->foo == NULL) + panic("ah_keyed_md5_init: what?"); + MD5Init((MD5_CTX *)state->foo); + if (state->sav) { + MD5Update((MD5_CTX *)state->foo, + (u_int8_t *)_KEYBUF(state->sav->key_auth), + (u_int)_KEYLEN(state->sav->key_auth)); + + { + /* + * Pad after the key. + * We cannot simply use md5_pad() since the function + * won't update the total length. + */ + size_t padlen; + size_t keybitlen; + u_int8_t buf[32]; + + if (_KEYLEN(state->sav->key_auth) < 56) + padlen = 64 - 8 - _KEYLEN(state->sav->key_auth); + else + padlen = 64 + 64 - 8 - _KEYLEN(state->sav->key_auth); + keybitlen = _KEYLEN(state->sav->key_auth); + keybitlen *= 8; + + buf[0] = 0x80; + MD5Update((MD5_CTX *)state->foo, &buf[0], 1); + padlen--; + + bzero(buf, sizeof(buf)); + while (sizeof(buf) < padlen) { + MD5Update((MD5_CTX *)state->foo, &buf[0], sizeof(buf)); + padlen -= sizeof(buf); + } + if (padlen) { + MD5Update((MD5_CTX *)state->foo, &buf[0], padlen); + } + + buf[0] = (keybitlen >> 0) & 0xff; + buf[1] = (keybitlen >> 8) & 0xff; + buf[2] = (keybitlen >> 16) & 0xff; + buf[3] = (keybitlen >> 24) & 0xff; + MD5Update((MD5_CTX *)state->foo, buf, 8); + } + } +} + +static void +ah_keyed_md5_loop(state, addr, len) + struct ah_algorithm_state *state; + caddr_t addr; + size_t len; +{ + if (!state) + panic("ah_keyed_md5_loop: what?"); + + MD5Update((MD5_CTX *)state->foo, addr, len); +} + +static void +ah_keyed_md5_result(state, addr) + struct ah_algorithm_state *state; + caddr_t addr; +{ + u_char digest[16]; + + if (!state) + panic("ah_keyed_md5_result: what?"); + + if (state->sav) { + MD5Update((MD5_CTX *)state->foo, + (u_int8_t *)_KEYBUF(state->sav->key_auth), + (u_int)_KEYLEN(state->sav->key_auth)); + } + MD5Final(&digest[0], (MD5_CTX *)state->foo); + free(state->foo, M_TEMP); + bcopy(&digest[0], (void *)addr, sizeof(digest)); +} + +static int +ah_keyed_sha1_mature(sav) + struct secasvar *sav; +{ + struct ah_algorithm *algo; + + if (!sav->key_auth) { + printf("esp_keyed_sha1_mature: no key is given.\n"); + return 1; + } + algo = &ah_algorithms[sav->alg_auth]; + if (sav->key_auth->sadb_key_bits < algo->keymin + || algo->keymax < sav->key_auth->sadb_key_bits) { + printf("ah_keyed_sha1_mature: invalid key length %d.\n", + sav->key_auth->sadb_key_bits); + return 1; + } + + return 0; +} + +static void +ah_keyed_sha1_init(state, sav) + struct ah_algorithm_state *state; + struct secasvar *sav; +{ + SHA1_CTX *ctxt; + + if (!state) + panic("ah_keyed_sha1_init: what?"); + + state->sav = sav; + state->foo = (void *)malloc(sizeof(SHA1_CTX), M_TEMP, M_NOWAIT); + if (!state->foo) + panic("ah_keyed_sha1_init: what?"); + + ctxt = (SHA1_CTX *)state->foo; + SHA1Init(ctxt); + + if (state->sav) { + SHA1Update(ctxt, (u_int8_t *)_KEYBUF(state->sav->key_auth), + (u_int)_KEYLEN(state->sav->key_auth)); + + { + /* + * Pad after the key. + */ + size_t padlen; + size_t keybitlen; + u_int8_t buf[32]; + + if (_KEYLEN(state->sav->key_auth) < 56) + padlen = 64 - 8 - _KEYLEN(state->sav->key_auth); + else + padlen = 64 + 64 - 8 - _KEYLEN(state->sav->key_auth); + keybitlen = _KEYLEN(state->sav->key_auth); + keybitlen *= 8; + + buf[0] = 0x80; + SHA1Update(ctxt, &buf[0], 1); + padlen--; + + bzero(buf, sizeof(buf)); + while (sizeof(buf) < padlen) { + SHA1Update(ctxt, &buf[0], sizeof(buf)); + padlen -= sizeof(buf); + } + if (padlen) { + SHA1Update(ctxt, &buf[0], padlen); + } + + buf[0] = (keybitlen >> 0) & 0xff; + buf[1] = (keybitlen >> 8) & 0xff; + buf[2] = (keybitlen >> 16) & 0xff; + buf[3] = (keybitlen >> 24) & 0xff; + SHA1Update(ctxt, buf, 8); + } + } +} + +static void +ah_keyed_sha1_loop(state, addr, len) + struct ah_algorithm_state *state; + caddr_t addr; + size_t len; +{ + SHA1_CTX *ctxt; + + if (!state || !state->foo) + panic("ah_keyed_sha1_loop: what?"); + ctxt = (SHA1_CTX *)state->foo; + + sha1_loop(ctxt, (caddr_t)addr, (size_t)len); +} + +static void +ah_keyed_sha1_result(state, addr) + struct ah_algorithm_state *state; + caddr_t addr; +{ + u_char digest[SHA1_RESULTLEN]; /* SHA-1 generates 160 bits */ + SHA1_CTX *ctxt; + + if (!state || !state->foo) + panic("ah_keyed_sha1_result: what?"); + ctxt = (SHA1_CTX *)state->foo; + + if (state->sav) { + SHA1Update(ctxt, (u_int8_t *)_KEYBUF(state->sav->key_auth), + (u_int)_KEYLEN(state->sav->key_auth)); + } + SHA1Final((caddr_t)&digest[0], ctxt); + bcopy(&digest[0], (void *)addr, HMACSIZE); + + free(state->foo, M_TEMP); +} + +static int +ah_hmac_md5_mature(sav) + struct secasvar *sav; +{ + struct ah_algorithm *algo; + + if (!sav->key_auth) { + printf("esp_hmac_md5_mature: no key is given.\n"); + return 1; + } + algo = &ah_algorithms[sav->alg_auth]; + if (sav->key_auth->sadb_key_bits < algo->keymin + || algo->keymax < sav->key_auth->sadb_key_bits) { + printf("ah_hmac_md5_mature: invalid key length %d.\n", + sav->key_auth->sadb_key_bits); + return 1; + } + + return 0; +} + +static void +ah_hmac_md5_init(state, sav) + struct ah_algorithm_state *state; + struct secasvar *sav; +{ + u_char *ipad; + u_char *opad; + u_char tk[16]; + u_char *key; + size_t keylen; + size_t i; + MD5_CTX *ctxt; + + if (!state) + panic("ah_hmac_md5_init: what?"); + + state->sav = sav; + state->foo = (void *)malloc(64 + 64 + sizeof(MD5_CTX), M_TEMP, M_NOWAIT); + if (!state->foo) + panic("ah_hmac_md5_init: what?"); + + ipad = (u_char *)state->foo; + opad = (u_char *)(ipad + 64); + ctxt = (MD5_CTX *)(opad + 64); + + /* compress the key if necessery */ + if (64 < _KEYLEN(state->sav->key_auth)) { + MD5Init(ctxt); + MD5Update(ctxt, _KEYBUF(state->sav->key_auth), + _KEYLEN(state->sav->key_auth)); + MD5Final(&tk[0], ctxt); + key = &tk[0]; + keylen = 16; + } else { + key = _KEYBUF(state->sav->key_auth); + keylen = _KEYLEN(state->sav->key_auth); + } + + bzero(ipad, 64); + bzero(opad, 64); + bcopy(key, ipad, keylen); + bcopy(key, opad, keylen); + for (i = 0; i < 64; i++) { + ipad[i] ^= 0x36; + opad[i] ^= 0x5c; + } + + MD5Init(ctxt); + MD5Update(ctxt, ipad, 64); +} + +static void +ah_hmac_md5_loop(state, addr, len) + struct ah_algorithm_state *state; + caddr_t addr; + size_t len; +{ + MD5_CTX *ctxt; + + if (!state || !state->foo) + panic("ah_hmac_md5_loop: what?"); + ctxt = (MD5_CTX *)(((caddr_t)state->foo) + 128); + MD5Update(ctxt, addr, len); +} + +static void +ah_hmac_md5_result(state, addr) + struct ah_algorithm_state *state; + caddr_t addr; +{ + u_char digest[16]; + u_char *ipad; + u_char *opad; + MD5_CTX *ctxt; + + if (!state || !state->foo) + panic("ah_hmac_md5_result: what?"); + + ipad = (u_char *)state->foo; + opad = (u_char *)(ipad + 64); + ctxt = (MD5_CTX *)(opad + 64); + + MD5Final(&digest[0], ctxt); + + MD5Init(ctxt); + MD5Update(ctxt, opad, 64); + MD5Update(ctxt, &digest[0], sizeof(digest)); + MD5Final(&digest[0], ctxt); + + bcopy(&digest[0], (void *)addr, HMACSIZE); + + free(state->foo, M_TEMP); +} + +static int +ah_hmac_sha1_mature(sav) + struct secasvar *sav; +{ + struct ah_algorithm *algo; + + if (!sav->key_auth) { + printf("esp_hmac_sha1_mature: no key is given.\n"); + return 1; + } + algo = &ah_algorithms[sav->alg_auth]; + if (sav->key_auth->sadb_key_bits < algo->keymin + || algo->keymax < sav->key_auth->sadb_key_bits) { + printf("ah_hmac_sha1_mature: invalid key length %d.\n", + sav->key_auth->sadb_key_bits); + return 1; + } + + return 0; +} + +static void +ah_hmac_sha1_init(state, sav) + struct ah_algorithm_state *state; + struct secasvar *sav; +{ + u_char *ipad; + u_char *opad; + SHA1_CTX *ctxt; + u_char tk[SHA1_RESULTLEN]; /* SHA-1 generates 160 bits */ + u_char *key; + size_t keylen; + size_t i; + + if (!state) + panic("ah_hmac_sha1_init: what?"); + + state->sav = sav; + state->foo = (void *)malloc(64 + 64 + sizeof(SHA1_CTX), + M_TEMP, M_NOWAIT); + if (!state->foo) + panic("ah_hmac_sha1_init: what?"); + + ipad = (u_char *)state->foo; + opad = (u_char *)(ipad + 64); + ctxt = (SHA1_CTX *)(opad + 64); + + /* compress the key if necessery */ + if (64 < _KEYLEN(state->sav->key_auth)) { + SHA1Init(ctxt); + SHA1Update(ctxt, _KEYBUF(state->sav->key_auth), + _KEYLEN(state->sav->key_auth)); + SHA1Final(&tk[0], ctxt); + key = &tk[0]; + keylen = SHA1_RESULTLEN; + } else { + key = _KEYBUF(state->sav->key_auth); + keylen = _KEYLEN(state->sav->key_auth); + } + + bzero(ipad, 64); + bzero(opad, 64); + bcopy(key, ipad, keylen); + bcopy(key, opad, keylen); + for (i = 0; i < 64; i++) { + ipad[i] ^= 0x36; + opad[i] ^= 0x5c; + } + + SHA1Init(ctxt); + SHA1Update(ctxt, ipad, 64); +} + +static void +ah_hmac_sha1_loop(state, addr, len) + struct ah_algorithm_state *state; + caddr_t addr; + size_t len; +{ + SHA1_CTX *ctxt; + + if (!state || !state->foo) + panic("ah_hmac_sha1_loop: what?"); + + ctxt = (SHA1_CTX *)(((u_char *)state->foo) + 128); + SHA1Update(ctxt, (caddr_t)addr, (size_t)len); +} + +static void +ah_hmac_sha1_result(state, addr) + struct ah_algorithm_state *state; + caddr_t addr; +{ + u_char digest[SHA1_RESULTLEN]; /* SHA-1 generates 160 bits */ + u_char *ipad; + u_char *opad; + SHA1_CTX *ctxt; + + if (!state || !state->foo) + panic("ah_hmac_sha1_result: what?"); + + ipad = (u_char *)state->foo; + opad = (u_char *)(ipad + 64); + ctxt = (SHA1_CTX *)(opad + 64); + + SHA1Final((caddr_t)&digest[0], ctxt); + + SHA1Init(ctxt); + SHA1Update(ctxt, opad, 64); + SHA1Update(ctxt, (caddr_t)&digest[0], sizeof(digest)); + SHA1Final((caddr_t)&digest[0], ctxt); + + bcopy(&digest[0], (void *)addr, HMACSIZE); + + free(state->foo, M_TEMP); +} + +/*------------------------------------------------------------*/ + +/* + * go generate the checksum. + */ +int +ah4_calccksum(m0, ahdat, algo, sav) + struct mbuf *m0; + caddr_t ahdat; + struct ah_algorithm *algo; + struct secasvar *sav; +{ + struct mbuf *m; + int hdrtype; + u_char *p; + size_t advancewidth; + struct ah_algorithm_state algos; + int tlen; + u_char sumbuf[AH_MAXSUMSIZE]; + int error = 0; + + hdrtype = -1; /*dummy, it is called IPPROTO_IP*/ + + m = m0; + + p = mtod(m, u_char *); + + (algo->init)(&algos, sav); + + advancewidth = 0; /*safety*/ + +again: + /* gory. */ + switch (hdrtype) { + case -1: /*first one*/ + { + /* + * copy ip hdr, modify to fit the AH checksum rule, + * then take a checksum. + * XXX need to care about source routing... jesus. + */ + struct ip iphdr; + size_t hlen; + + bcopy((caddr_t)p, (caddr_t)&iphdr, sizeof(struct ip)); +#ifdef _IP_VHL + hlen = IP_VHL_HL(iphdr.ip_vhl) << 2; +#else + hlen = iphdr.ip_hl << 2; +#endif + iphdr.ip_ttl = 0; + iphdr.ip_sum = htons(0); + if (ip4_ah_cleartos) iphdr.ip_tos = 0; + iphdr.ip_off = htons(ntohs(iphdr.ip_off) & ip4_ah_offsetmask); + (algo->update)(&algos, (caddr_t)&iphdr, sizeof(struct ip)); + + if (hlen != sizeof(struct ip)) { + u_char *p; + int i, j; + int l, skip; + u_char dummy[4]; + + /* + * IP options processing. + * See RFC2402 appendix A. + */ + bzero(dummy, sizeof(dummy)); + p = mtod(m, u_char *); + i = sizeof(struct ip); + while (i < hlen) { + skip = 1; + switch (p[i + IPOPT_OPTVAL]) { + case IPOPT_EOL: + case IPOPT_NOP: + l = 1; + skip = 0; + break; + case IPOPT_SECURITY: /* 0x82 */ + case 0x85: /* Extended security */ + case 0x86: /* Commercial security */ + case 0x94: /* Router alert */ + case 0x95: /* RFC1770 */ + l = p[i + IPOPT_OLEN]; + skip = 0; + break; + default: + l = p[i + IPOPT_OLEN]; + skip = 1; + break; + } + if (l <= 0 || hlen - i < l) { + printf("ah4_input: invalid IP option " + "(type=%02x len=%02x)\n", + p[i + IPOPT_OPTVAL], + p[i + IPOPT_OLEN]); + break; + } + if (skip) { + for (j = 0; j < l / sizeof(dummy); j++) + (algo->update)(&algos, dummy, sizeof(dummy)); + + (algo->update)(&algos, dummy, l % sizeof(dummy)); + } else + (algo->update)(&algos, p + i, l); + if (p[i + IPOPT_OPTVAL] == IPOPT_EOL) + break; + i += l; + } + } + + hdrtype = (iphdr.ip_p) & 0xff; + advancewidth = hlen; + break; + } + + case IPPROTO_AH: + { + u_char dummy[4]; + int siz; + int hdrsiz; + + hdrsiz = (sav->flags & SADB_X_EXT_OLD) ? + sizeof(struct ah) : sizeof(struct newah); + + (algo->update)(&algos, p, hdrsiz); + + /* key data region. */ + siz = (*algo->sumsiz)(sav); + bzero(&dummy[0], sizeof(dummy)); + while (sizeof(dummy) <= siz) { + (algo->update)(&algos, dummy, sizeof(dummy)); + siz -= sizeof(dummy); + } + /* can't happen, but just in case */ + if (siz) + (algo->update)(&algos, dummy, siz); + + /* padding region, just in case */ + siz = (((struct ah *)p)->ah_len << 2) - (*algo->sumsiz)(sav); + if ((sav->flags & SADB_X_EXT_OLD) == 0) + siz -= 4; /* sequence number field */ + if (0 < siz) { + /* RFC 1826 */ + (algo->update)(&algos, p + hdrsiz + (*algo->sumsiz)(sav), + siz); + } + + hdrtype = ((struct ah *)p)->ah_nxt; + advancewidth = hdrsiz; + advancewidth += ((struct ah *)p)->ah_len << 2; + if ((sav->flags & SADB_X_EXT_OLD) == 0) + advancewidth -= 4; /* sequence number field */ + break; + } + + default: + printf("ah4_calccksum: unexpected hdrtype=%x; " + "treating rest as payload\n", hdrtype); + /*fall through*/ + case IPPROTO_ICMP: + case IPPROTO_IGMP: + case IPPROTO_IPIP: +#ifdef INET6 + case IPPROTO_IPV6: + case IPPROTO_ICMPV6: +#endif + case IPPROTO_UDP: + case IPPROTO_TCP: + case IPPROTO_ESP: + while (m) { + tlen = m->m_len - (p - mtod(m, u_char *)); + (algo->update)(&algos, p, tlen); + m = m->m_next; + p = m ? mtod(m, u_char *) : NULL; + } + + advancewidth = 0; /*loop finished*/ + break; + } + + if (advancewidth) { + /* is it safe? */ + while (m && advancewidth) { + tlen = m->m_len - (p - mtod(m, u_char *)); + if (advancewidth < tlen) { + p += advancewidth; + advancewidth = 0; + } else { + advancewidth -= tlen; + m = m->m_next; + if (m) + p = mtod(m, u_char *); + else { + printf("ERR: hit the end-of-mbuf...\n"); + p = NULL; + } + } + } + + if (m) + goto again; + } + + /* for HMAC algorithms... */ + (algo->result)(&algos, &sumbuf[0]); + bcopy(&sumbuf[0], ahdat, (*algo->sumsiz)(sav)); + + return error; +} + +#ifdef INET6 +/* + * go generate the checksum. This function won't modify the mbuf chain + * except AH itself. + */ +int +ah6_calccksum(m0, ahdat, algo, sav) + struct mbuf *m0; + caddr_t ahdat; + struct ah_algorithm *algo; + struct secasvar *sav; +{ + struct mbuf *m; + int hdrtype; + u_char *p; + size_t advancewidth; + struct ah_algorithm_state algos; + int tlen; + int error = 0; + u_char sumbuf[AH_MAXSUMSIZE]; + int nest; + + hdrtype = -1; /*dummy, it is called IPPROTO_IPV6 */ + + m = m0; + + p = mtod(m, u_char *); + + (algo->init)(&algos, sav); + + advancewidth = 0; /*safety*/ + nest = 0; + +again: + if (ip6_hdrnestlimit && (++nest > ip6_hdrnestlimit)) { + ip6stat.ip6s_toomanyhdr++; + error = EINVAL; /*XXX*/ + goto bad; + } + + /* gory. */ + switch (hdrtype) { + case -1: /*first one*/ + { + struct ip6_hdr ip6copy; + + bcopy(p, &ip6copy, sizeof(struct ip6_hdr)); + /* RFC2402 */ + ip6copy.ip6_flow = 0; + ip6copy.ip6_vfc = IPV6_VERSION; + ip6copy.ip6_hlim = 0; + if (IN6_IS_ADDR_LINKLOCAL(&ip6copy.ip6_src)) + ip6copy.ip6_src.s6_addr16[1] = 0x0000; + if (IN6_IS_ADDR_LINKLOCAL(&ip6copy.ip6_dst)) + ip6copy.ip6_dst.s6_addr16[1] = 0x0000; + (algo->update)(&algos, (caddr_t)&ip6copy, + sizeof(struct ip6_hdr)); + hdrtype = (((struct ip6_hdr *)p)->ip6_nxt) & 0xff; + advancewidth = sizeof(struct ip6_hdr); + break; + } + + case IPPROTO_AH: + { + u_char dummy[4]; + int siz; + int hdrsiz; + + hdrsiz = (sav->flags & SADB_X_EXT_OLD) ? + sizeof(struct ah) : sizeof(struct newah); + + (algo->update)(&algos, p, hdrsiz); + + /* key data region. */ + siz = (*algo->sumsiz)(sav); + bzero(&dummy[0], 4); + while (4 <= siz) { + (algo->update)(&algos, dummy, 4); + siz -= 4; + } + /* can't happen, but just in case */ + if (siz) + (algo->update)(&algos, dummy, siz); + + /* padding region, just in case */ + siz = (((struct ah *)p)->ah_len << 2) - (*algo->sumsiz)(sav); + if ((sav->flags & SADB_X_EXT_OLD) == 0) + siz -= 4; /* sequence number field */ + if (0 < siz) { + (algo->update)(&algos, p + hdrsiz + (*algo->sumsiz)(sav), + siz); + } + + hdrtype = ((struct ah *)p)->ah_nxt; + advancewidth = hdrsiz; + advancewidth += ((struct ah *)p)->ah_len << 2; + if ((sav->flags & SADB_X_EXT_OLD) == 0) + advancewidth -= 4; /* sequence number field */ + break; + } + + case IPPROTO_HOPOPTS: + case IPPROTO_DSTOPTS: + { + int hdrlen, optlen; + u_int8_t *optp, *lastp = p, *optend, opt; + + tlen = m->m_len - (p - mtod(m, u_char *)); + /* We assume all the options is contained in a single mbuf */ + if (tlen < sizeof(struct ip6_ext)) { + error = EINVAL; + goto bad; + } + hdrlen = (((struct ip6_ext *)p)->ip6e_len + 1) << 3; + hdrtype = (int)((struct ip6_ext *)p)->ip6e_nxt; + if (tlen < hdrlen) { + error = EINVAL; + goto bad; + } + optend = p + hdrlen; + + /* + * ICV calculation for the options header including all + * options. This part is a little tricky since there are + * two type of options; mutable and immutable. Our approach + * is to calculate ICV for a consecutive immutable options + * at once. Here is an example. In the following figure, + * suppose that we've calculated ICV from the top of the + * header to MutableOpt1, which is a mutable option. + * lastp points to the end of MutableOpt1. Some immutable + * options follows MutableOpt1, and we encounter a new + * mutable option; MutableOpt2. optp points to the head + * of MutableOpt2. In this situation, uncalculated immutable + * field is the field from lastp to optp+2 (note that the + * type and the length fields are considered as immutable + * even in a mutable option). So we first calculate ICV + * for the field as immutable, then calculate from optp+2 + * to the end of MutableOpt2, whose length is optlen-2, + * where optlen is the length of MutableOpt2. Finally, + * lastp is updated to point to the end of MutableOpt2 + * for further calculation. The updated point is shown as + * lastp' in the figure. + * <------ optlen -----> + * -----------+-------------------+---+---+-----------+ + * MutableOpt1|ImmutableOptions...|typ|len|MutableOpt2| + * -----------+-------------------+---+---+-----------+ + * ^ ^ ^ + * lastp optp optp+2 + * <---- optp + 2 - lastp -----><-optlen-2-> + * ^ + * lastp' + */ + for (optp = p + 2; optp < optend; optp += optlen) { + opt = optp[0]; + if (opt == IP6OPT_PAD1) { + optlen = 1; + } else { + if (optp + 2 > optend) { + error = EINVAL; /* malformed option */ + goto bad; + } + optlen = optp[1] + 2; + if (opt & IP6OPT_MUTABLE) { + /* + * ICV calc. for the (consecutive) + * immutable field followd by the + * option. + */ + (algo->update)(&algos, lastp, + optp + 2 - lastp); + if (optlen - 2 > ZEROBUFLEN) { + error = EINVAL; /* XXX */ + goto bad; + } + /* + * ICV calc. for the mutable + * option using an all-0 buffer. + */ + (algo->update)(&algos, zerobuf, + optlen - 2); + lastp = optp + optlen; + } + } + } + /* + * Wrap up the calulation; compute ICV for the consecutive + * immutable options at the end of the header(if any). + */ + (algo->update)(&algos, lastp, p + hdrlen - lastp); + advancewidth = hdrlen; + break; + } + case IPPROTO_ROUTING: + { + /* + * For an input packet, we can just calculate `as is'. + * For an output packet, we assume ip6_output have already + * made packet how it will be received at the final destination. + * So we'll only check if the header is malformed. + */ + int hdrlen; + + tlen = m->m_len - (p - mtod(m, u_char *)); + /* We assume all the options is contained in a single mbuf */ + if (tlen < sizeof(struct ip6_ext)) { + error = EINVAL; + goto bad; + } + hdrlen = (((struct ip6_ext *)p)->ip6e_len + 1) << 3; + hdrtype = (int)((struct ip6_ext *)p)->ip6e_nxt; + if (tlen < hdrlen) { + error = EINVAL; + goto bad; + } + advancewidth = hdrlen; + (algo->update)(&algos, p, hdrlen); + break; + } + default: + printf("ah6_calccksum: unexpected hdrtype=%x; " + "treating rest as payload\n", hdrtype); + /*fall through*/ + case IPPROTO_ICMP: + case IPPROTO_IGMP: + case IPPROTO_IPIP: /*?*/ + case IPPROTO_IPV6: + case IPPROTO_ICMPV6: + case IPPROTO_UDP: + case IPPROTO_TCP: + case IPPROTO_ESP: + while (m) { + tlen = m->m_len - (p - mtod(m, u_char *)); + (algo->update)(&algos, p, tlen); + m = m->m_next; + p = m ? mtod(m, u_char *) : NULL; + } + + advancewidth = 0; /*loop finished*/ + break; + } + + if (advancewidth) { + /* is it safe? */ + while (m && advancewidth) { + tlen = m->m_len - (p - mtod(m, u_char *)); + if (advancewidth < tlen) { + p += advancewidth; + advancewidth = 0; + } else { + advancewidth -= tlen; + m = m->m_next; + if (m) + p = mtod(m, u_char *); + else { + printf("ERR: hit the end-of-mbuf...\n"); + p = NULL; + } + } + } + + if (m) + goto again; + } + + /* for HMAC algorithms... */ + (algo->result)(&algos, &sumbuf[0]); + bcopy(&sumbuf[0], ahdat, (*algo->sumsiz)(sav)); + + return(0); + + bad: + return(error); +} +#endif |