/* $NetBSD: ah_core.c,v 1.16 2000/02/06 12:49:40 itojun Exp $ */ /* * 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. */ /* * RFC1826/2402 authentication header. */ #include "opt_inet.h" #include "opt_ipsec.h" /* Some of operating systems have standard crypto checksum library */ #ifdef __NetBSD__ #define HAVE_MD5 #define HAVE_SHA1 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET6 #include #include #include #endif #include #include #ifdef IPSEC_ESP #include #endif #include #include #ifdef HAVE_MD5 #include #else #include #endif #ifdef HAVE_SHA1 #include #define SHA1_RESULTLEN 20 #else #include #endif #include #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 net/pfkeyv2.h.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) { ipseclog((LOG_ERR, "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) { ipseclog((LOG_ERR, "ah_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) { ipseclog((LOG_ERR, "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; SHA1Update(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) { ipseclog((LOG_ERR, "ah_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) { ipseclog((LOG_ERR, "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) { ipseclog((LOG_ERR, "ah_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) { ipseclog((LOG_ERR, "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) { ipseclog((LOG_ERR, "ah4_calccksum: 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: ipseclog((LOG_DEBUG, "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: case IPPROTO_IPCOMP: 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 { ipseclog((LOG_DEBUG, "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_MASK; 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: ipseclog((LOG_DEBUG, "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: case IPPROTO_IPCOMP: 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 { ipseclog((LOG_DEBUG, "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