diff options
author | shin <shin@FreeBSD.org> | 1999-12-22 19:13:38 +0000 |
---|---|---|
committer | shin <shin@FreeBSD.org> | 1999-12-22 19:13:38 +0000 |
commit | 50ba589c666f7d356304339b9cfc7fc9d173ad8d (patch) | |
tree | 46d6ae7c9680a93ce1c3a13378cef283df9f6544 /sys/netinet6 | |
parent | e396740391e7e60805bda6799ac3397d1fc8c539 (diff) | |
download | FreeBSD-src-50ba589c666f7d356304339b9cfc7fc9d173ad8d.zip FreeBSD-src-50ba589c666f7d356304339b9cfc7fc9d173ad8d.tar.gz |
IPSEC support in the kernel.
pr_input() routines prototype is also changed to support IPSEC and IPV6
chained protocol headers.
Reviewed by: freebsd-arch, cvs-committers
Obtained from: KAME project
Diffstat (limited to 'sys/netinet6')
29 files changed, 8764 insertions, 282 deletions
diff --git a/sys/netinet6/ah.h b/sys/netinet6/ah.h new file mode 100644 index 0000000..2786272 --- /dev/null +++ b/sys/netinet6/ah.h @@ -0,0 +1,88 @@ +/* + * 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. + */ + +#ifndef _NETINET6_AH_H_ +#define _NETINET6_AH_H_ + +struct secasvar; + +struct ah { + u_int8_t ah_nxt; /* Next Header */ + u_int8_t ah_len; /* Length of data, in 32bit */ + u_int16_t ah_reserve; /* Reserved for future use */ + u_int32_t ah_spi; /* Security parameter index */ + /* variable size, 32bit bound*/ /* Authentication data */ +}; + +struct newah { + u_int8_t ah_nxt; /* Next Header */ + u_int8_t ah_len; /* Length of data + 1, in 32bit */ + u_int16_t ah_reserve; /* Reserved for future use */ + u_int32_t ah_spi; /* Security parameter index */ + u_int32_t ah_seq; /* Sequence number field */ + /* variable size, 32bit bound*/ /* Authentication data */ +}; + +struct ah_algorithm_state { + struct secasvar *sav; + void* foo; /*per algorithm data - maybe*/ +}; + +struct ah_algorithm { + int (*sumsiz) __P((struct secasvar *)); + int (*mature) __P((struct secasvar *)); + int keymin; /* in bits */ + int keymax; /* in bits */ + void (*init) __P((struct ah_algorithm_state *, struct secasvar *)); + void (*update) __P((struct ah_algorithm_state *, caddr_t, size_t)); + void (*result) __P((struct ah_algorithm_state *, caddr_t)); +}; + +#define AH_MAXSUMSIZE 16 + +#ifdef KERNEL +extern struct ah_algorithm ah_algorithms[]; + +/* cksum routines */ +extern int ah_hdrlen __P((struct secasvar *)); + +extern size_t ah_hdrsiz __P((struct ipsecrequest *)); +extern void ah4_input __P((struct mbuf *, int, int)); +extern int ah4_output __P((struct mbuf *, struct ipsecrequest *)); +extern int ah4_calccksum __P((struct mbuf *, caddr_t, + struct ah_algorithm *, struct secasvar *)); +#endif /*KERNEL*/ + +#endif /*_NETINET6_AH_H_*/ diff --git a/sys/netinet6/ah6.h b/sys/netinet6/ah6.h new file mode 100644 index 0000000..9ae83a9 --- /dev/null +++ b/sys/netinet6/ah6.h @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#ifndef _NETINET6_AH6_H_ +#define _NETINET6_AH6_H_ + +#ifdef KERNEL +struct secasvar; + +extern int ah6_input __P((struct mbuf **, int *, int)); +extern int ah6_output __P((struct mbuf *, u_char *, struct mbuf *, + struct ipsecrequest *)); +extern int ah6_calccksum __P((struct mbuf *, caddr_t, + struct ah_algorithm *, struct secasvar *)); +#endif /*KERNEL*/ + +#endif /*_NETINET6_AH6_H_*/ 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 diff --git a/sys/netinet6/ah_input.c b/sys/netinet6/ah_input.c new file mode 100644 index 0000000..8f42814 --- /dev/null +++ b/sys/netinet6/ah_input.c @@ -0,0 +1,708 @@ +/* + * 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_inet.h" +#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/errno.h> +#include <sys/time.h> +#include <sys/kernel.h> +#include <sys/syslog.h> + +#include <net/if.h> +#include <net/route.h> +#include <net/netisr.h> +#include <machine/cpu.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/ip_var.h> +#include <netinet/ip_ecn.h> +#ifdef INET6 +#include <netinet6/ip6_ecn.h> +#endif + +#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 +#include <netkey/key.h> +#include <netkey/keydb.h> +#ifdef IPSEC_DEBUG +#include <netkey/key_debug.h> +#else +#define KEYDEBUG(lev,arg) +#endif + +#include <netinet/ipprotosw.h> + +#include <machine/stdarg.h> + +#include <net/net_osdep.h> + +#ifdef INET +extern struct ipprotosw inetsw[]; + +void +ah4_input(m, off, proto) + struct mbuf *m; + int off, proto; +{ + struct ip *ip; + struct ah *ah; + u_int32_t spi; + struct ah_algorithm *algo; + size_t siz; + size_t siz1; + u_char *cksum; + struct secasvar *sav = NULL; + u_int16_t nxt; + size_t hlen; + int s; + + if (m->m_len < off + sizeof(struct newah)) { + m = m_pullup(m, off + sizeof(struct newah)); + if (!m) { + printf("IPv4 AH input: can't pullup;" + "dropping the packet for simplicity\n"); + ipsecstat.in_inval++; + goto fail; + } + } + + ip = mtod(m, struct ip *); + ah = (struct ah *)(((caddr_t)ip) + off); + nxt = ah->ah_nxt; +#ifdef _IP_VHL + hlen = IP_VHL_HL(ip->ip_vhl) << 2; +#else + hlen = ip->ip_hl << 2; +#endif + + /* find the sassoc. */ + spi = ah->ah_spi; + + if ((sav = key_allocsa(AF_INET, + (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst, + IPPROTO_AH, spi)) == 0) { + printf("IPv4 AH input: no key association found for spi %u;" + "dropping the packet for simplicity\n", + (u_int32_t)ntohl(spi)); + ipsecstat.in_nosa++; + goto fail; + } + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP ah4_input called to allocate SA:%p\n", sav)); + if (sav->state != SADB_SASTATE_MATURE + && sav->state != SADB_SASTATE_DYING) { + printf("IPv4 AH input: non-mature/dying SA found for spi %u; " + "dropping the packet for simplicity\n", + (u_int32_t)ntohl(spi)); + ipsecstat.in_badspi++; + goto fail; + } + if (sav->alg_auth == SADB_AALG_NONE) { + printf("IPv4 AH input: unspecified authentication algorithm " + "for spi %u;" + "dropping the packet for simplicity\n", + (u_int32_t)ntohl(spi)); + ipsecstat.in_badspi++; + goto fail; + } + + algo = &ah_algorithms[sav->alg_auth]; + + siz = (*algo->sumsiz)(sav); + siz1 = ((siz + 3) & ~(4 - 1)); + + /* + * sanity checks for header, 1. + */ + { + int sizoff; + + sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4; + + if ((ah->ah_len << 2) - sizoff != siz1) { + log(LOG_NOTICE, "sum length mismatch in IPv4 AH input " + "(%d should be %u): %s\n", + (ah->ah_len << 2) - sizoff, (unsigned int)siz1, + ipsec4_logpacketstr(ip, spi)); + ipsecstat.in_inval++; + goto fail; + } + + if (m->m_len < off + sizeof(struct ah) + sizoff + siz1) { + m = m_pullup(m, off + sizeof(struct ah) + sizoff + siz1); + if (!m) { + printf("IPv4 AH input: can't pullup;" + "dropping the packet for simplicity\n"); + ipsecstat.in_inval++; + goto fail; + } + + ip = mtod(m, struct ip *); + ah = (struct ah *)(((caddr_t)ip) + off); + } + } + + /* + * check for sequence number. + */ + if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { + if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav)) + ; /*okey*/ + else { + ipsecstat.in_ahreplay++; + log(LOG_AUTH, "replay packet in IPv4 AH input: %s %s\n", + ipsec4_logpacketstr(ip, spi), + ipsec_logsastr(sav)); + goto fail; + } + } + + /* + * alright, it seems sane. now we are going to check the + * cryptographic checksum. + */ + cksum = malloc(siz1, M_TEMP, M_NOWAIT); + if (!cksum) { + printf("IPv4 AH input: couldn't alloc temporary region for cksum\n"); + ipsecstat.in_inval++; + goto fail; + } + + { + /* + * some of IP header fields are flipped to the host endian. + * convert them back to network endian. VERY stupid. + */ + ip->ip_len = htons(ip->ip_len + hlen); + ip->ip_id = htons(ip->ip_id); + ip->ip_off = htons(ip->ip_off); + if (ah4_calccksum(m, (caddr_t)cksum, algo, sav)) { + free(cksum, M_TEMP); + ipsecstat.in_inval++; + goto fail; + } + ipsecstat.in_ahhist[sav->alg_auth]++; + /* + * flip them back. + */ + ip->ip_len = ntohs(ip->ip_len) - hlen; + ip->ip_id = ntohs(ip->ip_id); + ip->ip_off = ntohs(ip->ip_off); + } + + { + caddr_t sumpos = NULL; + + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1826 */ + sumpos = (caddr_t)(ah + 1); + } else { + /* RFC 2402 */ + sumpos = (caddr_t)(((struct newah *)ah) + 1); + } + + if (bcmp(sumpos, cksum, siz) != 0) { + log(LOG_AUTH, "checksum mismatch in IPv4 AH input: %s %s\n", + ipsec4_logpacketstr(ip, spi), + ipsec_logsastr(sav)); + free(cksum, M_TEMP); + ipsecstat.in_ahauthfail++; + goto fail; + } + } + + free(cksum, M_TEMP); + + m->m_flags |= M_AUTHIPHDR; + m->m_flags |= M_AUTHIPDGM; + + /* M_AUTH related flags might be cleared here in the future */ + + if (m->m_flags & M_AUTHIPHDR + && m->m_flags & M_AUTHIPDGM) { + ipsecstat.in_ahauthsucc++; + } else { + log(LOG_AUTH, "authentication failed in IPv4 AH input: %s %s\n", + ipsec4_logpacketstr(ip, spi), + ipsec_logsastr(sav)); + ipsecstat.in_ahauthfail++; + } + + /* + * update sequence number. + */ + if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { + (void)ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav); + } + + /* was it transmitted over the IPsec tunnel SA? */ + if (ipsec4_tunnel_validate(ip, nxt, sav) && nxt == IPPROTO_IPV4) { + /* + * strip off all the headers that precedes AH. + * IP xx AH IP' payload -> IP' payload + * + * XXX more sanity checks + * XXX relationship with gif? + */ + size_t stripsiz = 0; + u_int8_t tos; + + tos = ip->ip_tos; + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1826 */ + stripsiz = sizeof(struct ah) + siz1; + } else { + /* RFC 2402 */ + stripsiz = sizeof(struct newah) + siz1; + } + m_adj(m, off + stripsiz); + if (m->m_len < sizeof(*ip)) { + m = m_pullup(m, sizeof(*ip)); + if (!m) { + ipsecstat.in_inval++; + goto fail; + } + } + ip = mtod(m, struct ip *); + /* ECN consideration. */ + ip_ecn_egress(ip4_ipsec_ecn, &tos, &ip->ip_tos); + if (!key_checktunnelsanity(sav, AF_INET, + (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst)) { + log(LOG_NOTICE, "ipsec tunnel address mismatch in IPv4 AH input: %s %s\n", + ipsec4_logpacketstr(ip, spi), + ipsec_logsastr(sav)); + ipsecstat.in_inval++; + goto fail; + } + + /* + * Should the inner packet be considered authentic? + * My current answer is: NO. + * + * host1 -- gw1 === gw2 -- host2 + * In this case, gw2 can trust the authenticity of the + * outer packet, but NOT inner. Packet may be altered + * between host1 and gw1. + * + * host1 -- gw1 === host2 + * This case falls into the same scenario as above. + * + * host1 === host2 + * This case is the only case when we may be able to leave + * M_AUTHIPHDR and M_AUTHIPDGM set. + * However, if host1 is wrongly configured, and allows + * attacker to inject some packet with src=host1 and + * dst=host2, you are in risk. + */ + m->m_flags &= ~M_AUTHIPHDR; + m->m_flags &= ~M_AUTHIPDGM; + + key_sa_recordxfer(sav, m); + + s = splimp(); + if (IF_QFULL(&ipintrq)) { + ipsecstat.in_inval++; + goto fail; + } + IF_ENQUEUE(&ipintrq, m); + m = NULL; + schednetisr(NETISR_IP); /*can be skipped but to make sure*/ + splx(s); + nxt = IPPROTO_DONE; + } else { + /* + * strip off AH. + * We do deep-copy since KAME requires that + * the packet is placed in a single external mbuf. + */ + size_t stripsiz = 0; + + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1826 */ + stripsiz = sizeof(struct ah) + siz1; + } else { + /* RFC 2402 */ + stripsiz = sizeof(struct newah) + siz1; + } + + ip = mtod(m, struct ip *); + ovbcopy((caddr_t)ip, (caddr_t)(((u_char *)ip) + stripsiz), off); + m->m_data += stripsiz; + m->m_len -= stripsiz; + m->m_pkthdr.len -= stripsiz; + + ip = mtod(m, struct ip *); + /*ip_len is in host endian*/ + ip->ip_len = ip->ip_len - stripsiz; + ip->ip_p = nxt; + /* forget about IP hdr checksum, the check has already been passed */ + + if (nxt != IPPROTO_DONE) + (*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt); + else + m_freem(m); + m = NULL; + } + + if (sav) { + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP ah4_input call free SA:%p\n", sav)); + key_freesav(sav); + } + ipsecstat.in_success++; + return; + +fail: + if (sav) { + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP ah4_input call free SA:%p\n", sav)); + key_freesav(sav); + } + if (m) + m_freem(m); + return; +} +#endif /* INET */ + +#ifdef INET6 +int +ah6_input(mp, offp, proto) + struct mbuf **mp; + int *offp, proto; +{ + struct mbuf *m = *mp; + int off = *offp; + struct ip6_hdr *ip6; + struct ah *ah; + u_int32_t spi; + struct ah_algorithm *algo; + size_t siz; + size_t siz1; + u_char *cksum; + struct secasvar *sav = NULL; + u_int16_t nxt; + int s; + + IP6_EXTHDR_CHECK(m, off, sizeof(struct ah), IPPROTO_DONE); + + ip6 = mtod(m, struct ip6_hdr *); + ah = (struct ah *)(((caddr_t)ip6) + off); + + nxt = ah->ah_nxt; + + /* find the sassoc. */ + spi = ah->ah_spi; + + if (ntohs(ip6->ip6_plen) == 0) { + printf("IPv6 AH input: AH with IPv6 jumbogram is not supported.\n"); + ipsec6stat.in_inval++; + goto fail; + } + + if ((sav = key_allocsa(AF_INET6, + (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst, + IPPROTO_AH, spi)) == 0) { + printf("IPv6 AH input: no key association found for spi %u;" + "dropping the packet for simplicity\n", + (u_int32_t)ntohl(spi)); + ipsec6stat.in_nosa++; + goto fail; + } + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP ah6_input called to allocate SA:%p\n", sav)); + if (sav->state != SADB_SASTATE_MATURE + && sav->state != SADB_SASTATE_DYING) { + printf("IPv6 AH input: non-mature/dying SA found for spi %u; " + "dropping the packet for simplicity\n", + (u_int32_t)ntohl(spi)); + ipsec6stat.in_badspi++; + goto fail; + } + if (sav->alg_auth == SADB_AALG_NONE) { + printf("IPv6 AH input: unspecified authentication algorithm " + "for spi %u;" + "dropping the packet for simplicity\n", + (u_int32_t)ntohl(spi)); + ipsec6stat.in_badspi++; + goto fail; + } + + algo = &ah_algorithms[sav->alg_auth]; + + siz = (*algo->sumsiz)(sav); + siz1 = ((siz + 3) & ~(4 - 1)); + + /* + * sanity checks for header, 1. + */ + { + int sizoff; + + sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4; + + if ((ah->ah_len << 2) - sizoff != siz1) { + log(LOG_NOTICE, "sum length mismatch in IPv6 AH input " + "(%d should be %u): %s\n", + (ah->ah_len << 2) - sizoff, (unsigned int)siz1, + ipsec6_logpacketstr(ip6, spi)); + ipsec6stat.in_inval++; + goto fail; + } + IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1, IPPROTO_DONE); + } + + /* + * check for sequence number. + */ + if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { + if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav)) + ; /*okey*/ + else { + ipsec6stat.in_ahreplay++; + log(LOG_AUTH, "replay packet in IPv6 AH input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), + ipsec_logsastr(sav)); + goto fail; + } + } + + /* + * alright, it seems sane. now we are going to check the + * cryptographic checksum. + */ + cksum = malloc(siz1, M_TEMP, M_NOWAIT); + if (!cksum) { + printf("IPv6 AH input: couldn't alloc temporary region for cksum\n"); + ipsec6stat.in_inval++; + goto fail; + } + + if (ah6_calccksum(m, (caddr_t)cksum, algo, sav)) { + free(cksum, M_TEMP); + ipsec6stat.in_inval++; + goto fail; + } + ipsec6stat.in_ahhist[sav->alg_auth]++; + + { + caddr_t sumpos = NULL; + + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1826 */ + sumpos = (caddr_t)(ah + 1); + } else { + /* RFC 2402 */ + sumpos = (caddr_t)(((struct newah *)ah) + 1); + } + + if (bcmp(sumpos, cksum, siz) != 0) { + log(LOG_AUTH, "checksum mismatch in IPv6 AH input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), + ipsec_logsastr(sav)); + free(cksum, M_TEMP); + ipsec6stat.in_ahauthfail++; + goto fail; + } + } + + free(cksum, M_TEMP); + + m->m_flags |= M_AUTHIPHDR; + m->m_flags |= M_AUTHIPDGM; + + /* M_AUTH related flags might be cleared here in the future */ + + if (m->m_flags & M_AUTHIPHDR + && m->m_flags & M_AUTHIPDGM) { + ipsec6stat.in_ahauthsucc++; + } else { + log(LOG_AUTH, "authentication failed in IPv6 AH input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), + ipsec_logsastr(sav)); + ipsec6stat.in_ahauthfail++; + } + + /* + * update sequence number. + */ + if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { + (void)ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav); + } + + /* was it transmitted over the IPsec tunnel SA? */ + if (ipsec6_tunnel_validate(ip6, nxt, sav) && nxt == IPPROTO_IPV6) { + /* + * strip off all the headers that precedes AH. + * IP6 xx AH IP6' payload -> IP6' payload + * + * XXX more sanity checks + * XXX relationship with gif? + */ + size_t stripsiz = 0; + u_int32_t flowinfo; /*net endian*/ + + flowinfo = ip6->ip6_flow; + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1826 */ + stripsiz = sizeof(struct ah) + siz1; + } else { + /* RFC 2402 */ + stripsiz = sizeof(struct newah) + siz1; + } + m_adj(m, off + stripsiz); + if (m->m_len < sizeof(*ip6)) { + /* + * m_pullup is prohibited in KAME IPv6 input processing + * but there's no other way! + */ + m = m_pullup(m, sizeof(*ip6)); + if (!m) { + ipsec6stat.in_inval++; + goto fail; + } + } + ip6 = mtod(m, struct ip6_hdr *); + /* ECN consideration. */ + ip6_ecn_egress(ip6_ipsec_ecn, &flowinfo, &ip6->ip6_flow); + if (!key_checktunnelsanity(sav, AF_INET6, + (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst)) { + log(LOG_NOTICE, "ipsec tunnel address mismatch in IPv6 AH input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), + ipsec_logsastr(sav)); + ipsec6stat.in_inval++; + goto fail; + } + + /* + * should the inner packet be considered authentic? + * see comment in ah4_input(). + */ + m->m_flags &= ~M_AUTHIPHDR; + m->m_flags &= ~M_AUTHIPDGM; + + key_sa_recordxfer(sav, m); + + s = splimp(); + if (IF_QFULL(&ip6intrq)) { + ipsec6stat.in_inval++; + goto fail; + } + IF_ENQUEUE(&ip6intrq, m); + m = NULL; + schednetisr(NETISR_IPV6); /*can be skipped but to make sure*/ + splx(s); + nxt = IPPROTO_DONE; + } else { + /* + * strip off AH. + * We do deep-copy since KAME requires that + * the packet is placed in a single mbuf. + */ + size_t stripsiz = 0; + char *prvnxtp; + + /* + * Copy the value of the next header field of AH to the + * next header field of the previous header. + * This is necessary because AH will be stripped off below. + */ + prvnxtp = ip6_get_prevhdr(m, off); /* XXX */ + *prvnxtp = nxt; + + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1826 */ + stripsiz = sizeof(struct ah) + siz1; + } else { + /* RFC 2402 */ + stripsiz = sizeof(struct newah) + siz1; + } + + ip6 = mtod(m, struct ip6_hdr *); + ovbcopy((caddr_t)ip6, (caddr_t)(((u_char *)ip6) + stripsiz), + off); + m->m_data += stripsiz; + m->m_len -= stripsiz; + m->m_pkthdr.len -= stripsiz; + + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz); + + key_sa_recordxfer(sav, m); + } + + *offp = off; + *mp = m; + + if (sav) { + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP ah6_input call free SA:%p\n", sav)); + key_freesav(sav); + } + ipsec6stat.in_success++; + return nxt; + +fail: + if (sav) { + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP ah6_input call free SA:%p\n", sav)); + key_freesav(sav); + } + if (m) + m_freem(m); + return IPPROTO_DONE; +} +#endif /* INET6 */ diff --git a/sys/netinet6/ah_output.c b/sys/netinet6/ah_output.c new file mode 100644 index 0000000..0cb3279 --- /dev/null +++ b/sys/netinet6/ah_output.c @@ -0,0 +1,532 @@ +/* + * 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" + +#define ahdprintf(x) printf x + +#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 <sys/syslog.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 +#include <netkey/key.h> +#include <netkey/keydb.h> +#ifdef IPSEC_DEBUG +#include <netkey/key_debug.h> +#else +#define KEYDEBUG(lev,arg) +#endif + +#include <net/net_osdep.h> + +static struct in_addr *ah4_finaldst __P((struct mbuf *)); + +/* + * compute AH header size. + * transport mode only. for tunnel mode, we should implement + * virtual interface, and control MTU/MSS by the interface MTU. + */ +size_t +ah_hdrsiz(isr) + struct ipsecrequest *isr; +{ + struct ah_algorithm *algo; + size_t hdrsiz; + + /* sanity check */ + if (isr == NULL) + panic("ah_hdrsiz: NULL was passed.\n"); + + if (isr->saidx.proto != IPPROTO_AH) + panic("unsupported mode passed to ah_hdrsiz"); + + if (isr->sav == NULL) + goto contrive; + if (isr->sav->state != SADB_SASTATE_MATURE + && isr->sav->state != SADB_SASTATE_DYING) + goto contrive; + + /* we need transport mode AH. */ + algo = &ah_algorithms[isr->sav->alg_auth]; + if (!algo) + goto contrive; + + /* + * XXX + * right now we don't calcurate the padding size. simply + * treat the padding size as constant, for simplicity. + * + * XXX variable size padding support + */ + hdrsiz = (((*algo->sumsiz)(isr->sav) + 3) & ~(4 - 1)); + if (isr->sav->flags & SADB_X_EXT_OLD) + hdrsiz += sizeof(struct ah); + else + hdrsiz += sizeof(struct newah); + + return hdrsiz; + + contrive: + /* ASSUMING: + * sizeof(struct newah) > sizeof(struct ah). + * 16 = (16 + 3) & ~(4 - 1). + */ + return sizeof(struct newah) + 16; +} + +/* + * Modify the packet so that it includes the authentication data. + * The mbuf passed must start with IPv4 header. + * + * assumes that the first mbuf contains IPv4 header + option only. + * the function does not modify m. + */ +int +ah4_output(m, isr) + struct mbuf *m; + struct ipsecrequest *isr; +{ + struct secasvar *sav = isr->sav; + struct ah_algorithm *algo; + u_int32_t spi; + u_char *ahdrpos; + u_char *ahsumpos = NULL; + size_t hlen = 0; /*IP header+option in bytes*/ + size_t plen = 0; /*AH payload size in bytes*/ + size_t ahlen = 0; /*plen + sizeof(ah)*/ + struct ip *ip; + struct in_addr dst; + struct in_addr *finaldst; + int error; + + /* sanity checks */ + if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) { + struct ip *ip; + + ip = mtod(m, struct ip *); + printf("ah4_output: internal error: " + "sav->replay is null: " + "%x->%x, SPI=%u\n", + (u_int32_t)ntohl(ip->ip_src.s_addr), + (u_int32_t)ntohl(ip->ip_dst.s_addr), + (u_int32_t)ntohl(sav->spi)); + ipsecstat.out_inval++; + m_freem(m); + return EINVAL; + } + + algo = &ah_algorithms[sav->alg_auth]; + spi = sav->spi; + + /* + * determine the size to grow. + */ + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1826 */ + plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/ + ahlen = plen + sizeof(struct ah); + } else { + /* RFC 2402 */ + plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/ + ahlen = plen + sizeof(struct newah); + } + + /* + * grow the mbuf to accomodate AH. + */ + ip = mtod(m, struct ip *); +#ifdef _IP_VHL + hlen = IP_VHL_HL(ip->ip_vhl) << 2; +#else + hlen = ip->ip_hl << 2; +#endif + + if (m->m_len != hlen) + panic("ah4_output: assumption failed (first mbuf length)"); + if (M_LEADINGSPACE(m->m_next) < ahlen) { + struct mbuf *n; + MGET(n, M_DONTWAIT, MT_DATA); + if (!n) { + printf("ENOBUFS in ah4_output %d\n", __LINE__); + m_freem(m); + return ENOBUFS; + } + n->m_len = ahlen; + n->m_next = m->m_next; + m->m_next = n; + m->m_pkthdr.len += ahlen; + ahdrpos = mtod(n, u_char *); + } else { + m->m_next->m_len += ahlen; + m->m_next->m_data -= ahlen; + m->m_pkthdr.len += ahlen; + ahdrpos = mtod(m->m_next, u_char *); + } + + ip = mtod(m, struct ip *); /*just to be sure*/ + + /* + * initialize AH. + */ + if (sav->flags & SADB_X_EXT_OLD) { + struct ah *ahdr; + + ahdr = (struct ah *)ahdrpos; + ahsumpos = (u_char *)(ahdr + 1); + ahdr->ah_len = plen >> 2; + ahdr->ah_nxt = ip->ip_p; + ahdr->ah_reserve = htons(0); + ahdr->ah_spi = spi; + bzero(ahdr + 1, plen); + } else { + struct newah *ahdr; + + ahdr = (struct newah *)ahdrpos; + ahsumpos = (u_char *)(ahdr + 1); + ahdr->ah_len = (plen >> 2) + 1; /* plus one for seq# */ + ahdr->ah_nxt = ip->ip_p; + ahdr->ah_reserve = htons(0); + ahdr->ah_spi = spi; + sav->replay->count++; + /* + * XXX sequence number must not be cycled, if the SA is + * installed by IKE daemon. + */ + ahdr->ah_seq = htonl(sav->replay->count); + bzero(ahdr + 1, plen); + } + + /* + * modify IPv4 header. + */ + ip->ip_p = IPPROTO_AH; + if (ahlen < (IP_MAXPACKET - ntohs(ip->ip_len))) + ip->ip_len = htons(ntohs(ip->ip_len) + ahlen); + else { + printf("IPv4 AH output: size exceeds limit\n"); + ipsecstat.out_inval++; + m_freem(m); + return EMSGSIZE; + } + + /* + * If there is source routing option, update destination field in + * the IPv4 header to the final destination. + * Note that we do not need to update source routing option itself + * (as done in IPv4 AH processing -- see ip6_output()), since + * source routing option is not part of the ICV computation. + */ + finaldst = ah4_finaldst(m); + if (finaldst) { + dst.s_addr = ip->ip_dst.s_addr; + ip->ip_dst.s_addr = finaldst->s_addr; + } + + /* + * calcurate the checksum, based on security association + * and the algorithm specified. + */ + error = ah4_calccksum(m, (caddr_t)ahsumpos, algo, sav); + if (error) { + printf("error after ah4_calccksum, called from ah4_output"); + m = NULL; + ipsecstat.out_inval++; + return error; + } + + if (finaldst) { + ip = mtod(m, struct ip *); /*just to make sure*/ + ip->ip_dst.s_addr = dst.s_addr; + } + ipsecstat.out_success++; + ipsecstat.out_ahhist[sav->alg_auth]++; + key_sa_recordxfer(sav, m); + + return 0; +} + +/* Calculate AH length */ +int +ah_hdrlen(sav) + struct secasvar *sav; +{ + struct ah_algorithm *algo; + int plen, ahlen; + + algo = &ah_algorithms[sav->alg_auth]; + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1826 */ + plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/ + ahlen = plen + sizeof(struct ah); + } else { + /* RFC 2402 */ + plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/ + ahlen = plen + sizeof(struct newah); + } + + return(ahlen); +} + +#ifdef INET6 +/* + * Fill in the Authentication Header and calculate checksum. + */ +int +ah6_output(m, nexthdrp, md, isr) + struct mbuf *m; + u_char *nexthdrp; + struct mbuf *md; + struct ipsecrequest *isr; +{ + struct mbuf *mprev; + struct mbuf *mah; + struct secasvar *sav = isr->sav; + struct ah_algorithm *algo; + u_int32_t spi; + u_char *ahsumpos = NULL; + size_t plen; /*AH payload size in bytes*/ + int error = 0; + int ahlen; + struct ip6_hdr *ip6; + + if (m->m_len < sizeof(struct ip6_hdr)) { + printf("ah6_output: first mbuf too short\n"); + m_freem(m); + return EINVAL; + } + + ahlen = ah_hdrlen(sav); + if (ahlen == 0) + return 0; + + for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next) + ; + if (!mprev || mprev->m_next != md) { + printf("ah6_output: md is not in chain\n"); + m_freem(m); + return EINVAL; + } + + MGET(mah, M_DONTWAIT, MT_DATA); + if (!mah) { + m_freem(m); + return ENOBUFS; + } + if (ahlen > MLEN) { + MCLGET(mah, M_DONTWAIT); + if ((mah->m_flags & M_EXT) == 0) { + m_free(mah); + m_freem(m); + return ENOBUFS; + } + } + mah->m_len = ahlen; + mah->m_next = md; + mprev->m_next = mah; + m->m_pkthdr.len += ahlen; + + /* fix plen */ + if (m->m_pkthdr.len - sizeof(struct ip6_hdr) > IPV6_MAXPACKET) { + printf("ip6_output: AH with IPv6 jumbogram is not supported\n"); + m_freem(m); + return EINVAL; + } + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); + + if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) { + printf("ah6_output: internal error: " + "sav->replay is null: SPI=%u\n", + (u_int32_t)ntohl(sav->spi)); + ipsec6stat.out_inval++; + return 0; /* no change at all */ + } + + algo = &ah_algorithms[sav->alg_auth]; + spi = sav->spi; + + /* + * initialize AH. + */ + if (sav->flags & SADB_X_EXT_OLD) { + struct ah *ahdr = mtod(mah, struct ah *); + + plen = mah->m_len - sizeof(struct ah); + ahsumpos = (u_char *)(ahdr + 1); + ahdr->ah_nxt = *nexthdrp; + *nexthdrp = IPPROTO_AH; + ahdr->ah_len = plen >> 2; + ahdr->ah_reserve = htons(0); + ahdr->ah_spi = spi; + bzero(ahdr + 1, plen); + } else { + struct newah *ahdr = mtod(mah, struct newah *); + + plen = mah->m_len - sizeof(struct newah); + ahsumpos = (u_char *)(ahdr + 1); + ahdr->ah_nxt = *nexthdrp; + *nexthdrp = IPPROTO_AH; + ahdr->ah_len = (plen >> 2) + 1; /* plus one for seq# */ + ahdr->ah_reserve = htons(0); + ahdr->ah_spi = spi; + sav->replay->count++; + /* + * XXX sequence number must not be cycled, if the SA is + * installed by IKE daemon. + */ + ahdr->ah_seq = htonl(sav->replay->count); + bzero(ahdr + 1, plen); + } + + /* + * calcurate the checksum, based on security association + * and the algorithm specified. + */ + error = ah6_calccksum(m, (caddr_t)ahsumpos, algo, sav); + if (error) + ipsec6stat.out_inval++; + else + ipsec6stat.out_success++; + ipsec6stat.out_ahhist[sav->alg_auth]++; + key_sa_recordxfer(sav, m); + + return(error); +} +#endif + +/* + * Find the final destination if there is loose/strict source routing option. + * Returns NULL if there's no source routing options. + * Returns NULL on errors too. + * Note that this function will return a pointer INTO the given parameter, + * struct mbuf *m. + * The mbuf must be pulled up toward, at least, ip option part. + */ +static struct in_addr * +ah4_finaldst(m) + struct mbuf *m; +{ + struct ip *ip; + int optlen; + u_char *q; + int i; + int hlen; + + if (!m) + panic("ah4_finaldst: m == NULL"); + ip = mtod(m, struct ip *); + hlen = (ip->ip_hl << 2); + + if (m->m_len < hlen) { + printf("ah4_finaldst: parameter mbuf wrong (not pulled up)\n"); + return NULL; + } + + if (hlen == sizeof(struct ip)) + return NULL; + + optlen = hlen - sizeof(struct ip); + if (optlen < 0) { + printf("ah4_finaldst: wrong optlen %d\n", optlen); + return NULL; + } + + q = (u_char *)(ip + 1); + i = 0; + while (i < optlen) { + switch (q[i + IPOPT_OPTVAL]) { + case IPOPT_EOL: + i = optlen; /* bye */ + break; + case IPOPT_NOP: + i++; + break; + case IPOPT_LSRR: + case IPOPT_SSRR: + if (q[i + IPOPT_OLEN] <= 0 + || optlen - i < q[i + IPOPT_OLEN]) { + printf("ip_finaldst: invalid IP option " + "(code=%02x len=%02x)\n", + q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN]); + return NULL; + } + i += q[i + IPOPT_OLEN] - sizeof(struct in_addr); + return (struct in_addr *)(q + i); + default: + if (q[i + IPOPT_OLEN] <= 0 + || optlen - i < q[i + IPOPT_OLEN]) { + printf("ip_finaldst: invalid IP option " + "(code=%02x len=%02x)\n", + q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN]); + return NULL; + } + i += q[i + IPOPT_OLEN]; + break; + } + } + return NULL; +} diff --git a/sys/netinet6/esp.h b/sys/netinet6/esp.h new file mode 100644 index 0000000..595aff1 --- /dev/null +++ b/sys/netinet6/esp.h @@ -0,0 +1,99 @@ +/* + * 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$ + */ + +/* + * RFC1827/2406 Encapsulated Security Payload. + */ + +#ifndef _NETINET6_ESP_H_ +#define _NETINET6_ESP_H_ + +struct secasvar; + +struct esp { + u_int32_t esp_spi; /* ESP */ + /*variable size, 32bit bound*/ /* Initialization Vector */ + /*variable size*/ /* Payload data */ + /*variable size*/ /* padding */ + /*8bit*/ /* pad size */ + /*8bit*/ /* next header */ + /*8bit*/ /* next header */ + /*variable size, 32bit bound*/ /* Authentication data (new IPsec) */ +}; + +struct newesp { + u_int32_t esp_spi; /* ESP */ + u_int32_t esp_seq; /* Sequence number */ + /*variable size*/ /* (IV and) Payload data */ + /*variable size*/ /* padding */ + /*8bit*/ /* pad size */ + /*8bit*/ /* next header */ + /*8bit*/ /* next header */ + /*variable size, 32bit bound*/ /* Authentication data */ +}; + +struct esptail { + u_int8_t esp_padlen; /* pad length */ + u_int8_t esp_nxt; /* Next header */ + /*variable size, 32bit bound*/ /* Authentication data (new IPsec)*/ +}; + +struct esp_algorithm_state { + struct secasvar *sav; + void* foo; /*per algorithm data - maybe*/ +}; + +/* XXX yet to be defined */ +struct esp_algorithm { + size_t padbound; /* pad boundary, in byte */ + int (*mature) __P((struct secasvar *)); + int keymin; /* in bits */ + int keymax; /* in bits */ + int (*ivlen) __P((struct secasvar *)); + int (*decrypt) __P((struct mbuf *, size_t, + struct secasvar *, struct esp_algorithm *, int)); + int (*encrypt) __P((struct mbuf *, size_t, size_t, + struct secasvar *, struct esp_algorithm *, int)); +}; + +#ifdef KERNEL +extern struct esp_algorithm esp_algorithms[]; + +/* crypt routines */ +extern int esp4_output __P((struct mbuf *, struct ipsecrequest *)); +extern void esp4_input __P((struct mbuf *, int, int)); +extern size_t esp_hdrsiz __P((struct ipsecrequest *)); +#endif /*KERNEL*/ + +extern int esp_auth __P((struct mbuf *, size_t, size_t, + struct secasvar *, u_char *)); + +#endif /*_NETINET6_ESP_H_*/ diff --git a/sys/netinet6/esp6.h b/sys/netinet6/esp6.h new file mode 100644 index 0000000..e206376 --- /dev/null +++ b/sys/netinet6/esp6.h @@ -0,0 +1,45 @@ +/* + * 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$ + */ + +/* + * RFC1827/2406 Encapsulated Security Payload. + */ + +#ifndef _NETINET6_ESP6_H_ +#define _NETINET6_ESP6_H_ + +#ifdef KERNEL +extern int esp6_output __P((struct mbuf *, u_char *, struct mbuf *, + struct ipsecrequest *)); +extern int esp6_input __P((struct mbuf **, int *, int)); +#endif /*KERNEL*/ + +#endif /*_NETINET6_ESP6_H_*/ diff --git a/sys/netinet6/esp_core.c b/sys/netinet6/esp_core.c new file mode 100644 index 0000000..8eb007e --- /dev/null +++ b/sys/netinet6/esp_core.c @@ -0,0 +1,1236 @@ +/* + * 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$ + */ + +#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/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_var.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 <crypto/des/des.h> +#include <crypto/blowfish/blowfish.h> +#include <crypto/cast128/cast128.h> +#include <crypto/rc5/rc5.h> + +#include <net/net_osdep.h> + +static int esp_null_mature __P((struct secasvar *)); +static int esp_null_ivlen __P((struct secasvar *)); +static int esp_null_decrypt __P((struct mbuf *, size_t, + struct secasvar *, struct esp_algorithm *, int)); +static int esp_null_encrypt __P((struct mbuf *, size_t, size_t, + struct secasvar *, struct esp_algorithm *, int)); +static int esp_descbc_mature __P((struct secasvar *)); +static int esp_descbc_ivlen __P((struct secasvar *)); +static int esp_descbc_decrypt __P((struct mbuf *, size_t, + struct secasvar *, struct esp_algorithm *, int)); +static int esp_descbc_encrypt __P((struct mbuf *, size_t, size_t, + struct secasvar *, struct esp_algorithm *, int)); +static int esp_cbc_mature __P((struct secasvar *)); +static int esp_blowfish_cbc_decrypt __P((struct mbuf *, size_t, + struct secasvar *, struct esp_algorithm *, int)); +static int esp_blowfish_cbc_encrypt __P((struct mbuf *, size_t, + size_t, struct secasvar *, struct esp_algorithm *, int)); +static int esp_blowfish_cbc_ivlen __P((struct secasvar *)); +static int esp_cast128cbc_ivlen __P((struct secasvar *)); +static int esp_cast128cbc_decrypt __P((struct mbuf *, size_t, + struct secasvar *, struct esp_algorithm *, int)); +static int esp_cast128cbc_encrypt __P((struct mbuf *, size_t, size_t, + struct secasvar *, struct esp_algorithm *, int)); +static int esp_3descbc_ivlen __P((struct secasvar *)); +static int esp_3descbc_decrypt __P((struct mbuf *, size_t, + struct secasvar *, struct esp_algorithm *, int)); +static int esp_3descbc_encrypt __P((struct mbuf *, size_t, size_t, + struct secasvar *, struct esp_algorithm *, int)); +static int esp_rc5cbc_ivlen __P((struct secasvar *)); +static int esp_rc5cbc_decrypt __P((struct mbuf *, size_t, + struct secasvar *, struct esp_algorithm *, int)); +static int esp_rc5cbc_encrypt __P((struct mbuf *, size_t, size_t, + struct secasvar *, struct esp_algorithm *, int)); +static void esp_increment_iv __P((struct secasvar *)); +static caddr_t mbuf_find_offset __P((struct mbuf *, size_t, size_t)); + +/* NOTE: The order depends on SADB_EALG_x in netkey/keyv2.h */ +struct esp_algorithm esp_algorithms[] = { + { 0, 0, 0, 0, 0, 0, 0, }, + { 8, esp_descbc_mature, 64, 64, + esp_descbc_ivlen, esp_descbc_decrypt, esp_descbc_encrypt, }, + { 8, esp_cbc_mature, 192, 192, + esp_3descbc_ivlen, esp_3descbc_decrypt, esp_3descbc_encrypt, }, + { 1, esp_null_mature, 0, 2048, + esp_null_ivlen, esp_null_decrypt, esp_null_encrypt, }, + { 8, esp_cbc_mature, 40, 448, + esp_blowfish_cbc_ivlen, esp_blowfish_cbc_decrypt, + esp_blowfish_cbc_encrypt, }, + { 8, esp_cbc_mature, 40, 128, + esp_cast128cbc_ivlen, esp_cast128cbc_decrypt, + esp_cast128cbc_encrypt, }, + { 8, esp_cbc_mature, 40, 2040, + esp_rc5cbc_ivlen, esp_rc5cbc_decrypt, esp_rc5cbc_encrypt, }, +}; + +/* + * mbuf assumption: foo_encrypt() assumes that IV part is placed in a single + * mbuf, not across multiple mbufs. + */ + +static int +esp_null_mature(sav) + struct secasvar *sav; +{ + /* anything is okay */ + return 0; +} + +static int +esp_null_ivlen(sav) + struct secasvar *sav; +{ + return 0; +} + +static int +esp_null_decrypt(m, off, sav, algo, ivlen) + struct mbuf *m; + size_t off; /* offset to ESP header */ + struct secasvar *sav; + struct esp_algorithm *algo; + int ivlen; +{ + return 0; /* do nothing */ +} + +static int +esp_null_encrypt(m, off, plen, sav, algo, ivlen) + struct mbuf *m; + size_t off; /* offset to ESP header */ + size_t plen; /* payload length (to be encrypted) */ + struct secasvar *sav; + struct esp_algorithm *algo; + int ivlen; +{ + return 0; /* do nothing */ +} + +static int +esp_descbc_mature(sav) + struct secasvar *sav; +{ + struct esp_algorithm *algo; + + if (!(sav->flags & SADB_X_EXT_OLD) && (sav->flags & SADB_X_EXT_IV4B)) { + printf("esp_cbc_mature: algorithm incompatible with 4 octets IV length\n"); + return 1; + } + + if (!sav->key_enc) { + printf("esp_descbc_mature: no key is given.\n"); + return 1; + } + algo = &esp_algorithms[sav->alg_enc]; + if (_KEYBITS(sav->key_enc) < algo->keymin + || algo->keymax < _KEYBITS(sav->key_enc)) { + printf("esp_descbc_mature: invalid key length %d.\n", + _KEYBITS(sav->key_enc)); + return 1; + } + + /* weak key check */ + if (des_is_weak_key((C_Block *)_KEYBUF(sav->key_enc))) { + printf("esp_descbc_mature: weak key was passed.\n"); + return 1; + } + + return 0; +} + +static int +esp_descbc_ivlen(sav) + struct secasvar *sav; +{ + if (sav && (sav->flags & SADB_X_EXT_OLD) && (sav->flags & SADB_X_EXT_IV4B)) + return 4; + + if (sav && !(sav->flags & SADB_X_EXT_OLD) && (sav->flags & SADB_X_EXT_DERIV)) + return 4; + else + return 8; +} + +static int +esp_descbc_decrypt(m, off, sav, algo, ivlen) + struct mbuf *m; + size_t off; /* offset to ESP header */ + struct secasvar *sav; + struct esp_algorithm *algo; + int ivlen; +{ + size_t ivoff = 0; + size_t bodyoff = 0; + u_int8_t *iv; + size_t plen; + u_int8_t tiv[8]; + int derived; + + derived = 0; + /* sanity check */ + if (ivlen != sav->ivlen) { + printf("esp_descbc_decrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen); + return EINVAL; + } + if (_KEYBITS(sav->key_enc) < algo->keymin + || algo->keymax < _KEYBITS(sav->key_enc)) { + printf("esp_descbc_decrypt: bad keylen %d\n", + _KEYBITS(sav->key_enc)); + return EINVAL; + } + + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1827 */ + ivoff = off + sizeof(struct esp); + bodyoff = off + sizeof(struct esp) + ivlen; + derived = 0; + } else { + /* RFC 2406 */ + if (sav->flags & SADB_X_EXT_DERIV) { + /* + * draft-ietf-ipsec-ciph-des-derived-00.txt + * uses sequence number field as IV field. + * This draft has been deleted, but you can get from + * ftp://ftp.kame.net/pub/internet-drafts/. + */ + ivoff = off + sizeof(struct esp); + bodyoff = off + sizeof(struct esp) + sizeof(u_int32_t); + ivlen = sizeof(u_int32_t); + derived = 1; + } else { + ivoff = off + sizeof(struct newesp); + bodyoff = off + sizeof(struct newesp) + ivlen; + derived = 0; + } + } + if (ivlen == 4) { + iv = &tiv[0]; + m_copydata(m, ivoff, 4, &tiv[0]); + m_copydata(m, ivoff, 4, &tiv[4]); + tiv[4] ^= 0xff; + tiv[5] ^= 0xff; + tiv[6] ^= 0xff; + tiv[7] ^= 0xff; + } else if (ivlen == 8) { + iv = &tiv[0]; + m_copydata(m, ivoff, 8, &tiv[0]); + } else { + printf("esp_descbc_decrypt: unsupported ivlen %d\n", ivlen); + return EINVAL; + } + + plen = m->m_pkthdr.len; + if (plen < bodyoff) + panic("esp_descbc_decrypt: too short packet: len=%lu", + (u_long)plen); + plen -= bodyoff; + + if (plen % 8) { + printf("esp_descbc_decrypt: " + "payload length must be multiple of 8\n"); + return EINVAL; + } + + { + int deserr; + des_key_schedule ks; + + deserr = des_key_sched((C_Block *)_KEYBUF(sav->key_enc), ks); + if (deserr != 0) { + printf("esp_descbc_decrypt: key error %d\n", deserr); + return EINVAL; + } + + des_cbc_encrypt(m, bodyoff, plen, ks, (C_Block *)iv, DES_DECRYPT); + + /* for safety */ + bzero(&ks, sizeof(des_key_schedule)); + } + + /* for safety */ + bzero(&tiv[0], sizeof(tiv)); + + return 0; +} + +static int +esp_descbc_encrypt(m, off, plen, sav, algo, ivlen) + struct mbuf *m; + size_t off; /* offset to ESP header */ + size_t plen; /* payload length (to be decrypted) */ + struct secasvar *sav; + struct esp_algorithm *algo; + int ivlen; +{ + size_t ivoff = 0; + size_t bodyoff = 0; + u_int8_t *iv; + u_int8_t tiv[8]; + int derived; + + derived = 0; + + /* sanity check */ + if (plen % 8) { + printf("esp_descbc_encrypt: " + "payload length must be multiple of 8\n"); + return EINVAL; + } + if (sav->ivlen != ivlen) { + printf("esp_descbc_encrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen); + return EINVAL; + } + if (_KEYBITS(sav->key_enc) < algo->keymin + || algo->keymax < _KEYBITS(sav->key_enc)) { + printf("esp_descbc_encrypt: bad keylen %d\n", + _KEYBITS(sav->key_enc)); + return EINVAL; + } + + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1827 */ + /* + * draft-ietf-ipsec-ciph-des-derived-00.txt + * uses sequence number field as IV field. + * This draft has been deleted, see above. + */ + ivoff = off + sizeof(struct esp); + bodyoff = off + sizeof(struct esp) + ivlen; + derived = 0; + } else { + /* RFC 2406 */ + if (sav->flags & SADB_X_EXT_DERIV) { + /* + * draft-ietf-ipsec-ciph-des-derived-00.txt + * uses sequence number field as IV field. + * This draft has been deleted, see above. + */ + ivoff = off + sizeof(struct esp); + bodyoff = off + sizeof(struct esp) + sizeof(u_int32_t); + ivlen = sizeof(u_int32_t); + derived = 1; + } else { + ivoff = off + sizeof(struct newesp); + bodyoff = off + sizeof(struct newesp) + ivlen; + derived = 0; + } + } + + if (m->m_pkthdr.len < bodyoff) + panic("assumption failed: mbuf too short"); + iv = mbuf_find_offset(m, ivoff, ivlen); + if (!iv) + panic("assumption failed: bad mbuf chain"); + if (ivlen == 4) { + if (!derived) { + bcopy(sav->iv, &tiv[0], 4); + bcopy(sav->iv, &tiv[4], 4); + tiv[4] ^= 0xff; + tiv[5] ^= 0xff; + tiv[6] ^= 0xff; + tiv[7] ^= 0xff; + bcopy(&tiv[0], iv, 4); + iv = &tiv[0]; + } else { + bcopy(iv, &tiv[0], 4); + bcopy(iv, &tiv[4], 4); + tiv[4] ^= 0xff; + tiv[5] ^= 0xff; + tiv[6] ^= 0xff; + tiv[7] ^= 0xff; + iv = &tiv[0]; + } + } else if (ivlen == 8) + bcopy((caddr_t)sav->iv, (caddr_t)iv, ivlen); + else { + printf("esp_descbc_encrypt: unsupported ivlen %d\n", ivlen); + return EINVAL; + } + + { + int deserr; + des_key_schedule ks; + + deserr = des_key_sched((C_Block *)_KEYBUF(sav->key_enc), ks); + if (deserr != 0) { + printf("esp_descbc_encrypt: key error %d\n", deserr); + return EINVAL; + } + + des_cbc_encrypt(m, bodyoff, plen, ks, (C_Block *)iv, DES_ENCRYPT); + + /* for safety */ + bzero(&ks, sizeof(des_key_schedule)); + } + + esp_increment_iv(sav); + + /* for safety */ + bzero(&tiv[0], sizeof(tiv)); + + return 0; +} + +static int +esp_cbc_mature(sav) + struct secasvar *sav; +{ + int keylen; + struct esp_algorithm *algo; + + if (sav->flags & SADB_X_EXT_OLD) { + printf("esp_cbc_mature: algorithm incompatible with esp-old\n"); + return 1; + } + if (sav->flags & SADB_X_EXT_DERIV) { + printf("esp_cbc_mature: algorithm incompatible with derived\n"); + return 1; + } + + if (!sav->key_enc) { + printf("esp_cbc_mature: no key is given.\n"); + return 1; + } + algo = &esp_algorithms[sav->alg_enc]; + keylen = sav->key_enc->sadb_key_bits; + if (keylen < algo->keymin || algo->keymax < keylen) { + printf("esp_cbc_mature: invalid key length %d.\n", + sav->key_enc->sadb_key_bits); + return 1; + } + switch (sav->alg_enc) { + case SADB_EALG_3DESCBC: + /* weak key check */ + if (des_is_weak_key((C_Block *)_KEYBUF(sav->key_enc)) + || des_is_weak_key((C_Block *)(_KEYBUF(sav->key_enc) + 8)) + || des_is_weak_key((C_Block *)(_KEYBUF(sav->key_enc) + 16))) { + printf("esp_cbc_mature: weak key was passed.\n"); + return 1; + } + break; + case SADB_EALG_BLOWFISHCBC: + case SADB_EALG_CAST128CBC: + case SADB_EALG_RC5CBC: + break; + } + + return 0; +} + +static int +esp_blowfish_cbc_decrypt(m, off, sav, algo, ivlen) + struct mbuf *m; + size_t off; /* offset to ESP header */ + struct secasvar *sav; + struct esp_algorithm *algo; + int ivlen; +{ + size_t ivoff; + size_t bodyoff; + u_int8_t *iv; + u_int8_t tiv[8]; + size_t plen; + static BF_KEY key; /* made static to avoid kernel stack overflow */ + int s; + + /* sanity check */ + if (sav->ivlen != ivlen) { + printf("esp_blowfish_cbc_decrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen); + return EINVAL; + } + if (_KEYBITS(sav->key_enc) < algo->keymin + || algo->keymax < _KEYBITS(sav->key_enc)) { + printf("esp_blowfish_cbc_decrypt: unsupported key length %d: " + "need %d to %d bits\n", _KEYBITS(sav->key_enc), + algo->keymin, algo->keymax); + return EINVAL; + } + if (sav->flags & SADB_X_EXT_OLD) { + printf("esp_blowfish_cbc_decrypt: unsupported ESP version\n"); + return EINVAL; + } + if (ivlen != 8) { + printf("esp_blowfish_cbc_decrypt: unsupported ivlen %d " + "(this should never happen)\n", ivlen); + return EINVAL; + } + + ivoff = off + sizeof(struct newesp); + bodyoff = off + sizeof(struct newesp) + ivlen; + iv = &tiv[0]; + m_copydata(m, ivoff, 8, &tiv[0]); + + plen = m->m_pkthdr.len; + if (plen < bodyoff) + panic("esp_blowfish_cbc_decrypt: too short packet: len=%lu", + (u_long)plen); + plen -= bodyoff; + + if (plen % 8) { + printf("esp_blowfish_cbc_decrypt: " + "payload length must be multiple of 8\n"); + return EINVAL; + } + + s = splnet(); /* XXX correct? */ + + BF_set_key(&key, _KEYBITS(sav->key_enc) / 8, _KEYBUF(sav->key_enc)); + BF_cbc_encrypt_m(m, bodyoff, plen, &key, iv, BF_DECRYPT); + + /* for safety */ + bzero(&key, sizeof(BF_KEY)); + + splx(s); + + /* for safety */ + bzero(&tiv[0], sizeof(tiv)); + + return 0; +} + +static int +esp_blowfish_cbc_encrypt(m, off, plen, sav, algo, ivlen) + struct mbuf *m; + size_t off; /* offset to ESP header */ + size_t plen; /* payload length (to be decrypted) */ + struct secasvar *sav; + struct esp_algorithm *algo; + int ivlen; +{ + size_t ivoff; + size_t bodyoff; + u_int8_t *iv; + static BF_KEY key; /* made static to avoid kernel stack overflow */ + int s; + + /* sanity check */ + if (plen % 8) { + printf("esp_blowfish_cbc_encrypt: " + "payload length must be multiple of 8\n"); + return EINVAL; + } + if (sav->ivlen != ivlen) { + printf("esp_blowfish_cbc_encrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen); + return EINVAL; + } + if (_KEYBITS(sav->key_enc) < algo->keymin + || algo->keymax < _KEYBITS(sav->key_enc)) { + printf("esp_blowfish_cbc_encrypt: unsupported key length %d: " + "need %d to %d bits\n", _KEYBITS(sav->key_enc), + algo->keymin, algo->keymax); + return EINVAL; + } + if (sav->flags & SADB_X_EXT_OLD) { + printf("esp_blowfish_cbc_encrypt: unsupported ESP version\n"); + return EINVAL; + } + if (ivlen != 8) { + printf("esp_blowfish_cbc_encrypt: unsupported ivlen %d " + "(this should never happen)\n", ivlen); + return EINVAL; + } + + ivoff = off + sizeof(struct newesp); + bodyoff = off + sizeof(struct newesp) + ivlen; + + if (m->m_pkthdr.len < bodyoff) + panic("assumption failed: mbuf too short"); + iv = mbuf_find_offset(m, ivoff, ivlen); + if (!iv) + panic("assumption failed: bad mbuf chain"); + + bcopy((caddr_t)sav->iv, (caddr_t)iv, ivlen); + + s = splnet(); /* XXX correct? */ + + BF_set_key(&key, _KEYBITS(sav->key_enc) / 8, _KEYBUF(sav->key_enc)); + BF_cbc_encrypt_m(m, bodyoff, plen, &key, iv, BF_ENCRYPT); + + /* for safety */ + bzero(&key, sizeof(BF_KEY)); + + splx(s); + + esp_increment_iv(sav); + + return 0; +} + +static int +esp_blowfish_cbc_ivlen(sav) + struct secasvar *sav; +{ + return 8; +} + +static int +esp_cast128cbc_ivlen(sav) + struct secasvar *sav; +{ + return 8; +} + +static int +esp_cast128cbc_decrypt(m, off, sav, algo, ivlen) + struct mbuf *m; + size_t off; + struct secasvar *sav; + struct esp_algorithm *algo; + int ivlen; +{ + size_t ivoff; + size_t bodyoff; + u_int8_t iv[8]; + size_t plen; + + /* sanity check */ + if (ivlen != sav->ivlen) { + printf("esp_cast128cbc_decrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen); + return EINVAL; + } + if (_KEYBITS(sav->key_enc) < algo->keymin + || _KEYBITS(sav->key_enc) > algo->keymax) { + printf("esp_cast128cbc_decrypt: unsupported key length %d: " + "need %d to %d bits\n", _KEYBITS(sav->key_enc), + algo->keymin, algo->keymax); + return EINVAL; + } + if (sav->flags & SADB_X_EXT_OLD) { + printf("esp_cast128cbc_decrypt: unsupported ESP version\n"); + return EINVAL; + } + if (ivlen != 8) { + printf("esp_cast128cbc_decrypt: unsupported ivlen %d " + "(this should never happen)\n", ivlen); + return EINVAL; + } + + ivoff = off + sizeof(struct newesp); + bodyoff = off + sizeof(struct newesp) + ivlen; + + /* copy mbuf's IV into iv */ + m_copydata(m, ivoff, 8, iv); + + plen = m->m_pkthdr.len; + if (plen < bodyoff) { + panic("esp_cast128cbc_decrypt: too short packet: len=%lu\n", + (u_long)plen); + } + plen -= bodyoff; + + if (plen % 8) { + printf("esp_cast128cbc_decrypt: " + "payload length must be multiple of 8\n"); + return EINVAL; + } + + /* decrypt */ + { + u_int8_t key[16]; + u_int32_t subkey[32]; + + bzero(key, sizeof(key)); + bcopy(_KEYBUF(sav->key_enc), key, _KEYLEN(sav->key_enc)); + + set_cast128_subkey(subkey, key); + cast128_cbc_process(m, bodyoff, plen, subkey, iv, + _KEYBITS(sav->key_enc) / 8, CAST128_DECRYPT); + + /* for safety */ + bzero(subkey, sizeof(subkey)); + bzero(key, sizeof(key)); + } + + return 0; +} + +static int +esp_cast128cbc_encrypt(m, off, plen, sav, algo, ivlen) + struct mbuf *m; + size_t off; + size_t plen; + struct secasvar *sav; + struct esp_algorithm *algo; + int ivlen; +{ + size_t ivoff; + size_t bodyoff; + u_int8_t *iv; + + /* sanity check */ + if (plen % 8) { + printf("esp_cast128cbc_encrypt: " + "payload length must be multiple of 8\n"); + return EINVAL; + } + if (sav->ivlen != ivlen) { + printf("esp_cast128cbc_encrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen); + return EINVAL; + } + if (_KEYBITS(sav->key_enc) < algo->keymin + || _KEYBITS(sav->key_enc) > algo->keymax) { + printf("esp_cast128cbc_encrypt: unsupported key length %d: " + "needs %d to %d bits\n", _KEYBITS(sav->key_enc), + algo->keymin, algo->keymax); + return EINVAL; + } + if (sav->flags & SADB_X_EXT_OLD) { + printf("esp_cast128cbc_encrypt: unsupported ESP version\n"); + return EINVAL; + } + if (ivlen != 8) { + printf("esp_cast128cbc_encrypt: unsupported ivlen %d " + "(this should never happen)\n", ivlen); + return EINVAL; + } + + ivoff = off + sizeof(struct newesp); + bodyoff = off + sizeof(struct newesp) + ivlen; + + if (m->m_pkthdr.len < bodyoff) + panic("assumption failed: mbuf too short"); + iv = mbuf_find_offset(m, ivoff, ivlen); + if (!iv) + panic("assumption failed: bad mbuf chain"); + + bcopy(sav->iv, iv, ivlen); + + /* encrypt */ + { + u_int8_t key[16]; + u_int32_t subkey[32]; + + bzero(key, sizeof(key)); + bcopy(_KEYBUF(sav->key_enc), key, _KEYLEN(sav->key_enc)); + + set_cast128_subkey(subkey, key); + cast128_cbc_process(m, bodyoff, plen, subkey, iv, + _KEYBITS(sav->key_enc) / 8, CAST128_ENCRYPT); + + /* for safety */ + bzero(subkey, sizeof(subkey)); + bzero(key, sizeof(key)); + } + + esp_increment_iv(sav); + + return 0; +} + +static int +esp_3descbc_ivlen(sav) + struct secasvar *sav; +{ + return 8; +} + +static int +esp_3descbc_decrypt(m, off, sav, algo, ivlen) + struct mbuf *m; + size_t off; + struct secasvar *sav; + struct esp_algorithm *algo; + int ivlen; +{ + size_t ivoff; + size_t bodyoff; + u_int8_t *iv; + size_t plen; + u_int8_t tiv[8]; + + /* sanity check */ + if (ivlen != sav->ivlen) { + printf("esp_3descbc_decrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen); + return EINVAL; + } + if (_KEYBITS(sav->key_enc) < algo->keymin + || algo->keymax < _KEYBITS(sav->key_enc)) { + printf("esp_3descbc_decrypt: bad keylen %d\n", + _KEYBITS(sav->key_enc)); + return EINVAL; + } + if (sav->flags & SADB_X_EXT_OLD) { + printf("esp_3descbc_decrypt: unsupported ESP version\n"); + return EINVAL; + } + if (ivlen != 8) { + printf("esp_3descbc_decrypt: unsupported ivlen %d " + "(this should never happen)\n", ivlen); + return EINVAL; + } + + ivoff = off + sizeof(struct newesp); + bodyoff = off + sizeof(struct newesp) + ivlen; + iv = &tiv[0]; + m_copydata(m, ivoff, 8, &tiv[0]); + + plen = m->m_pkthdr.len; + if (plen < bodyoff) + panic("esp_3descbc_decrypt: too short packet: len=%lu", + (u_long)plen); + + plen -= bodyoff; + + if (plen % 8) { + printf("esp_3descbc_decrypt: " + "payload length must be multiple of 8\n"); + return EINVAL; + } + + /* decrypt packet */ + { + int deserr[3]; + des_key_schedule ks[3]; + + deserr[0] = des_key_sched((C_Block *)_KEYBUF(sav->key_enc),ks[0]); + deserr[1] = des_key_sched((C_Block *)(_KEYBUF(sav->key_enc) + 8), ks[1]); + deserr[2] = des_key_sched((C_Block *)(_KEYBUF(sav->key_enc) + 16), ks[2]); + if ((deserr[0] != 0) || (deserr[1] != 0) || (deserr[2] != 0)) { + printf("esp_3descbc_decrypt: key error %d/%d/%d\n", + deserr[0], deserr[1], deserr[2]); + return EINVAL; + } + + des_3cbc_process(m, bodyoff, plen, ks, (C_Block *)iv, DES_DECRYPT); + + /* for safety */ + bzero(ks[0], sizeof(des_key_schedule)*3); + } + + /* for safety */ + bzero(&tiv[0], sizeof(tiv)); + + return 0; +} + +static int +esp_3descbc_encrypt(m, off, plen, sav, algo, ivlen) + struct mbuf *m; + size_t off; + size_t plen; + struct secasvar *sav; + struct esp_algorithm *algo; + int ivlen; +{ + size_t ivoff; + size_t bodyoff; + u_int8_t *iv; + + /* sanity check */ + if (plen % 8) { + printf("esp_3descbc_encrypt: " + "payload length must be multiple of 8\n"); + return EINVAL; + } + if (sav->ivlen != ivlen) { + printf("esp_3descbc_encrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen); + return EINVAL; + } + if (_KEYBITS(sav->key_enc) < algo->keymin + || algo->keymax < _KEYBITS(sav->key_enc)) { + printf("esp_3descbc_encrypt: bad keylen %d\n", + _KEYBITS(sav->key_enc)); + return EINVAL; + } + if (sav->flags & SADB_X_EXT_OLD) { + printf("esp_3descbc_encrypt: unsupported ESP version\n"); + return EINVAL; + } + if (ivlen != 8) { + printf("esp_3descbc_encrypt: unsupported ivlen %d " + "(this should never happen)\n", ivlen); + return EINVAL; + } + + ivoff = off + sizeof(struct newesp); + bodyoff = off + sizeof(struct newesp) + ivlen; + + if (m->m_pkthdr.len < bodyoff) + panic("assumption failed: mbuf too short"); + iv = mbuf_find_offset(m, ivoff, ivlen); + if (!iv) + panic("assumption failed: bad mbuf chain"); + + bcopy((caddr_t)sav->iv, (caddr_t)iv, ivlen); + + /* encrypt packet */ + { + int deserr[3]; + des_key_schedule ks[3]; + + deserr[0] = des_key_sched((C_Block *)_KEYBUF(sav->key_enc), ks[0]); + deserr[1] = des_key_sched((C_Block *)(_KEYBUF(sav->key_enc) + 8), ks[1]); + deserr[2] = des_key_sched((C_Block *)(_KEYBUF(sav->key_enc) + 16), ks[2]); + if ((deserr[0] != 0) || (deserr[1] != 0) || (deserr[2] != 0)) { + printf("esp_3descbc_encrypt: key error %d/%d/%d\n", + deserr[0], deserr[1], deserr[2]); + return EINVAL; + } + + des_3cbc_process(m, bodyoff, plen, ks, (C_Block *)iv, DES_ENCRYPT); + + /* for safety */ + bzero(ks[0], sizeof(des_key_schedule)*3); + } + + esp_increment_iv(sav); + + return 0; +} + +static int +esp_rc5cbc_ivlen(sav) + struct secasvar *sav; +{ + return 8; +} + +static int +esp_rc5cbc_decrypt(m, off, sav, algo, ivlen) + struct mbuf *m; + size_t off; + struct secasvar *sav; + struct esp_algorithm *algo; + int ivlen; +{ + size_t ivoff; + size_t bodyoff; + u_int8_t iv[8]; + size_t plen; + + /* sanity check */ + if (sav->ivlen != ivlen) { + printf("esp_rc5cbc_decrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen); + return EINVAL; + } + if ((_KEYBITS(sav->key_enc) < 40) || (_KEYBITS(sav->key_enc) > 2040)) { + printf("esp_rc5cbc_decrypt: unsupported key length %d: " + "need 40 to 2040 bit\n", _KEYBITS(sav->key_enc)); + return EINVAL; + } + if (sav->flags & SADB_X_EXT_OLD) { + printf("esp_rc5cbc_decrypt: unsupported ESP version\n"); + return EINVAL; + } + if (ivlen != 8) { + printf("esp_rc5cbc_decrypt: unsupported ivlen %d " + "(this should never happen)\n", ivlen); + return EINVAL; + } + + ivoff = off + sizeof(struct newesp); + bodyoff = off + sizeof(struct newesp) + ivlen; + + /* copy mbuf's IV into iv */ + m_copydata(m, ivoff, 8, iv); + + plen = m->m_pkthdr.len; + if (plen < bodyoff) { + panic("esp_rc5cbc_decrypt: too short packet: len=%lu", + (u_long)plen); + } + plen -= bodyoff; + + if (plen % 8) { + printf("esp_rc5cbc_decrypt: " + "payload length must be multiple of 8\n"); + return EINVAL; + } + + /* decrypt */ + { + RC5_WORD e_key[34]; + + set_rc5_expandkey(e_key, _KEYBUF(sav->key_enc), + _KEYBITS(sav->key_enc) / 8, 16); + rc5_cbc_process(m, bodyoff, plen, e_key, iv, RC5_DECRYPT); + + /* for safety */ + bzero(e_key, sizeof(e_key)); + } + + return 0; +} + +static int +esp_rc5cbc_encrypt(m, off, plen, sav, algo, ivlen) + struct mbuf *m; + size_t off; + size_t plen; + struct secasvar *sav; + struct esp_algorithm *algo; + int ivlen; +{ + size_t ivoff; + size_t bodyoff; + u_int8_t *iv; + + /* sanity check */ + if (plen % 8) { + printf("esp_rc5cbc_encrypt: " + "payload length must be multiple of 8\n"); + return EINVAL; + } + if (sav->ivlen != ivlen) { + printf("esp_rc5cbc_encrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen); + return EINVAL; + } + if (_KEYBITS(sav->key_enc) < algo->keymin + || _KEYBITS(sav->key_enc) > algo->keymax) { + printf("esp_rc5cbc_encrypt: unsupported key length %d: " + "need %d to %d bits\n", _KEYBITS(sav->key_enc), + algo->keymin, algo->keymax); + return EINVAL; + } + if (sav->flags & SADB_X_EXT_OLD) { + printf("esp_rc5cbc_encrypt: unsupported ESP version\n"); + return EINVAL; + } + if (ivlen != 8) { + printf("esp_rc5cbc_encrypt: unsupported ivlen %d " + "(this should never happen)\n", ivlen); + return EINVAL; + } + + ivoff = off + sizeof(struct newesp); + bodyoff = off + sizeof(struct newesp) + ivlen; + + if (m->m_pkthdr.len < bodyoff) + panic("assumption failed: mbuf too short"); + iv = mbuf_find_offset(m, ivoff, ivlen); + if (!iv) + panic("assumption failed: bad mbuf chain"); + + bcopy(sav->iv, iv, ivlen); + + /* encrypt */ + { + RC5_WORD e_key[34]; + + set_rc5_expandkey(e_key, _KEYBUF(sav->key_enc), + _KEYBITS(sav->key_enc) / 8, 16); + rc5_cbc_process(m, bodyoff, plen, e_key, iv, RC5_ENCRYPT); + + /* for safety */ + bzero(e_key, sizeof(e_key)); + } + + esp_increment_iv(sav); + + return 0; +} + +/* + * increment iv. + */ +static void +esp_increment_iv(sav) + struct secasvar *sav; +{ + u_int8_t *x; + u_int8_t y; + int i; + + y = time_second & 0xff; + + if (!y) y++; + x = (u_int8_t *)sav->iv; + for (i = 0; i < sav->ivlen; i++) { + *x = (*x + y) & 0xff; + x++; + } +} + +static caddr_t +mbuf_find_offset(m, off, len) + struct mbuf *m; + size_t off; + size_t len; +{ + struct mbuf *n; + size_t cnt; + + if (m->m_pkthdr.len < off || m->m_pkthdr.len < off + len) + return (caddr_t)NULL; + cnt = 0; + for (n = m; n; n = n->m_next) { + if (cnt + n->m_len <= off) { + cnt += n->m_len; + continue; + } + if (cnt <= off && off < cnt + n->m_len + && cnt <= off + len && off + len <= cnt + n->m_len) { + return mtod(n, caddr_t) + off - cnt; + } else + return (caddr_t)NULL; + } + return (caddr_t)NULL; +} + +/*------------------------------------------------------------*/ + +int +esp_auth(m0, skip, length, sav, sum) + struct mbuf *m0; + size_t skip; /* offset to ESP header */ + size_t length; /* payload length */ + struct secasvar *sav; + u_char *sum; +{ + struct mbuf *m; + size_t off; + struct ah_algorithm_state s; + u_char sumbuf[AH_MAXSUMSIZE]; + struct ah_algorithm *algo; + size_t siz; + + /* sanity checks */ + if (m0->m_pkthdr.len < skip) { + printf("esp_auth: mbuf length < skip\n"); + return EINVAL; + } + if (m0->m_pkthdr.len < skip + length) { + printf("esp_auth: mbuf length < skip + length\n"); + return EINVAL; + } + /* + * length of esp part (excluding authentication data) must be 4n, + * since nexthdr must be at offset 4n+3. + */ + if (length % 4) { + printf("esp_auth: length is not multiple of 4\n"); + return EINVAL; + } + if (!sav) { + printf("esp_auth: NULL SA passed\n"); + return EINVAL; + } + if (!sav->alg_auth) { + printf("esp_auth: bad ESP auth algorithm passed: %d\n", sav->alg_auth); + return EINVAL; + } + + m = m0; + off = 0; + + algo = &ah_algorithms[sav->alg_auth]; + siz = (((*algo->sumsiz)(sav) + 3) & ~(4 - 1)); + if (sizeof(sumbuf) < siz) { + printf("esp_auth: AH_MAXSUMSIZE is too small: siz=%lu\n", + (u_long)siz); + return EINVAL; + } + + /* skip the header */ + while (skip) { + if (!m) + panic("mbuf chain?"); + if (m->m_len <= skip) { + skip -= m->m_len; + m = m->m_next; + off = 0; + } else { + off = skip; + skip = 0; + } + } + + (*algo->init)(&s, sav); + while (0 < length) { + if (!m) + panic("mbuf chain?"); + + if (m->m_len - off < length) { + (*algo->update)(&s, mtod(m, u_char *) + off, + m->m_len - off); + length -= m->m_len - off; + m = m->m_next; + off = 0; + } else { + (*algo->update)(&s, mtod(m, u_char *) + off, length); + break; + } + } + (*algo->result)(&s, sumbuf); + bcopy(sumbuf, sum, siz); /*XXX*/ + + return 0; +} diff --git a/sys/netinet6/esp_input.c b/sys/netinet6/esp_input.c new file mode 100644 index 0000000..d09ba27 --- /dev/null +++ b/sys/netinet6/esp_input.c @@ -0,0 +1,984 @@ +/* + * 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$ + */ + +/* + * RFC1827/2406 Encapsulated Security Payload. + */ + +#include "opt_inet.h" +#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/errno.h> +#include <sys/time.h> +#include <sys/kernel.h> +#include <sys/syslog.h> + +#include <net/if.h> +#include <net/route.h> +#include <net/netisr.h> +#include <machine/cpu.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/ip_var.h> +#include <netinet/in_var.h> +#include <netinet/ip_ecn.h> +#ifdef INET6 +#include <netinet6/ip6_ecn.h> +#endif + +#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 <netkey/key.h> +#include <netkey/keydb.h> +#ifdef IPSEC_DEBUG +#include <netkey/key_debug.h> +#else +#define KEYDEBUG(lev,arg) +#endif + +#include <netinet/ipprotosw.h> + +#include <machine/stdarg.h> + +#include <net/net_osdep.h> + +#ifdef INET +extern struct ipprotosw inetsw[]; + +void +esp4_input(m, off, proto) + struct mbuf *m; + int off, proto; + +{ + struct ip *ip; + struct esp *esp; + struct esptail *esptail; + u_int32_t spi; + struct secasvar *sav = NULL; + size_t taillen; + u_int16_t nxt; + struct esp_algorithm *algo; + int ivlen; + size_t hlen; + size_t esplen; + int s; + + /* sanity check for alignment. */ + if (off % 4 != 0 || m->m_pkthdr.len % 4 != 0) { + printf("IPv4 ESP input: packet alignment problem " + "(off=%d, pktlen=%d)\n", off, m->m_pkthdr.len); + ipsecstat.in_inval++; + goto bad; + } + + if (m->m_len < off + sizeof(struct esp)) { + m = m_pullup(m, off + sizeof(struct esp)); + if (!m) { + printf("IPv4 ESP input: can't pullup in esp4_input\n"); + ipsecstat.in_inval++; + goto bad; + } + } + + ip = mtod(m, struct ip *); + esp = (struct esp *)(((u_int8_t *)ip) + off); +#ifdef _IP_VHL + hlen = IP_VHL_HL(ip->ip_vhl) << 2; +#else + hlen = ip->ip_hl << 2; +#endif + + /* find the sassoc. */ + spi = esp->esp_spi; + + if ((sav = key_allocsa(AF_INET, + (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst, + IPPROTO_ESP, spi)) == 0) { + printf("IPv4 ESP input: no key association found for spi %u;" + "dropping the packet for simplicity\n", + (u_int32_t)ntohl(spi)); + ipsecstat.in_nosa++; + goto bad; + } + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP esp4_input called to allocate SA:%p\n", sav)); + if (sav->state != SADB_SASTATE_MATURE + && sav->state != SADB_SASTATE_DYING) { + printf("IPv4 ESP input: non-mature/dying SA found for spi %u; " + "dropping the packet for simplicity\n", + (u_int32_t)ntohl(spi)); + ipsecstat.in_badspi++; + goto bad; + } + if (sav->alg_enc == SADB_EALG_NONE) { + printf("IPv4 ESP input: unspecified encryption algorithm " + "for spi %u;" + "dropping the packet for simplicity\n", + (u_int32_t)ntohl(spi)); + ipsecstat.in_badspi++; + goto bad; + } + + algo = &esp_algorithms[sav->alg_enc]; /*XXX*/ + + /* check if we have proper ivlen information */ + ivlen = sav->ivlen; + if (ivlen < 0) { + log(LOG_NOTICE, "inproper ivlen in IPv4 ESP input: %s %s\n", + ipsec4_logpacketstr(ip, spi), + ipsec_logsastr(sav)); + ipsecstat.in_inval++; + goto bad; + } + + if (!((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay + && (sav->alg_auth && sav->key_auth))) + goto noreplaycheck; + + if (sav->alg_auth == SADB_AALG_NULL) + goto noreplaycheck; + + /* + * check for sequence number. + */ + if (ipsec_chkreplay(ntohl(((struct newesp *)esp)->esp_seq), sav)) + ; /*okey*/ + else { + ipsecstat.in_espreplay++; + log(LOG_AUTH, "replay packet in IPv4 ESP input: %s %s\n", + ipsec4_logpacketstr(ip, spi), + ipsec_logsastr(sav)); + goto bad; + } + + /* check ICV */ + { + struct mbuf *n; + int len; + u_char sum0[AH_MAXSUMSIZE]; + u_char sum[AH_MAXSUMSIZE]; + struct ah_algorithm *sumalgo; + size_t siz; + + sumalgo = &ah_algorithms[sav->alg_auth]; + siz = (((*sumalgo->sumsiz)(sav) + 3) & ~(4 - 1)); + if (AH_MAXSUMSIZE < siz) { + printf("internal error: AH_MAXSUMSIZE must be larger than %lu\n", + (u_long)siz); + ipsecstat.in_inval++; + goto bad; + } + + n = m; + len = m->m_pkthdr.len; + len -= siz; + while (n && 0 < len) { + if (len < n->m_len) + break; + len -= n->m_len; + n = n->m_next; + } + if (!n) { + printf("mbuf chain problem?\n"); + ipsecstat.in_inval++; + goto bad; + } + m_copydata(n, len, siz, &sum0[0]); + + if (esp_auth(m, off, m->m_pkthdr.len - off - siz, sav, sum)) { + log(LOG_AUTH, "auth fail in IPv4 ESP input: %s %s\n", + ipsec4_logpacketstr(ip, spi), + ipsec_logsastr(sav)); + ipsecstat.in_espauthfail++; + goto bad; + } + + if (bcmp(sum0, sum, siz) != 0) { + log(LOG_AUTH, "auth fail in IPv4 ESP input: %s %s\n", + ipsec4_logpacketstr(ip, spi), + ipsec_logsastr(sav)); + ipsecstat.in_espauthfail++; + goto bad; + } + + /* strip off */ + m->m_pkthdr.len -= siz; + n->m_len -= siz; + ip = mtod(m, struct ip *); + ip->ip_len = ip->ip_len - siz; + m->m_flags |= M_AUTHIPDGM; + ipsecstat.in_espauthsucc++; + } + + /* + * update sequence number. + */ + if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { + (void)ipsec_updatereplay(ntohl(((struct newesp *)esp)->esp_seq), sav); + } + +noreplaycheck: + + /* process main esp header. */ + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1827 */ + esplen = sizeof(struct esp); + } else { + /* RFC 2406 */ + if (sav->flags & SADB_X_EXT_DERIV) + esplen = sizeof(struct esp); + else + esplen = sizeof(struct newesp); + } + + if (m->m_len < off + esplen + ivlen) { + m = m_pullup(m, off + esplen + ivlen); + if (!m) { + printf("IPv4 ESP input: can't pullup in esp4_input\n"); + ipsecstat.in_inval++; + goto bad; + } + } + + { + /* + * decrypt the packet. + */ + if (!algo->decrypt) + panic("internal error: no decrypt function"); + if ((*algo->decrypt)(m, off, sav, algo, ivlen)) { + log(LOG_AUTH, "decrypt fail in IPv4 ESP input: %s %s\n", + ipsec4_logpacketstr(ip, spi), + ipsec_logsastr(sav)); + ipsecstat.in_inval++; + goto bad; + } + ipsecstat.in_esphist[sav->alg_enc]++; + + m->m_flags |= M_DECRYPTED; + } + +#ifdef garbled_data_found_on_mbuf_after_packet + { + /* + * For simplicity, we'll trim the packet so that there's no extra + * part appended after IP packet. + * This is rare case for some odd drivers, so there should be no + * performance hit. + */ + + /* + * Note that, in ip_input, ip_len was already flipped and header + * length was subtracted from ip_len. + */ + if (m->m_pkthdr.len != hlen + ip->ip_len) + { + size_t siz; + struct mbuf *n; + + siz = hlen + ip->ip_len; + + /* find the final mbuf */ + for (n = m; n; n = n->m_next) { + if (n->m_len < siz) + siz -= n->m_len; + else + break; + } + if (!n) { + printf("invalid packet\n"); + ipsecstat.in_inval++; + goto bad; + } + + /* trim the final mbuf */ + if (n->m_len < siz) { + printf("invalid size: %d %d\n", n->m_len, siz); + ipsecstat.in_inval++; + goto bad; + } + n->m_len = siz; + + /* dispose the rest of the packet */ + m_freem(n->m_next); + n->m_next = NULL; + + m->m_pkthdr.len = hlen + ip->ip_len; + } + } +#endif + + { + /* + * find the trailer of the ESP. + */ + struct mbuf *n; /*the last mbuf on the mbuf chain, m_len > 0 */ + struct mbuf *o; /*the last mbuf on the mbuf chain */ + + o = m; + n = NULL; + while (o) { + if (0 < o->m_len) + n = o; + o = o->m_next; + } + if (!n || n->m_len < sizeof(struct esptail)) { + printf("IPv4 ESP input: assertion on pad part failed; " + "dropping the packet\n"); + ipsecstat.in_inval++; + goto bad; + } + + esptail = (struct esptail *) + (mtod(n, u_int8_t *) + n->m_len - sizeof(struct esptail)); + nxt = esptail->esp_nxt; + taillen = esptail->esp_padlen + 2; + + if (m->m_pkthdr.len < taillen + || m->m_pkthdr.len - taillen < hlen) { /*?*/ + log(LOG_NOTICE, "bad pad length in IPv4 ESP input: %s %s\n", + ipsec4_logpacketstr(ip, spi), + ipsec_logsastr(sav)); + ipsecstat.in_inval++; + goto bad; + } + + /* + * strip off the trailing pad area. + */ + if (taillen < n->m_len) { + /* trailing pad data is included in the last mbuf item. */ + n->m_len -= taillen; + m->m_pkthdr.len -= taillen; + } else { + /* trailing pad data spans on multiple mbuf item. */ + size_t siz; + + siz = m->m_pkthdr.len; + if (siz < taillen) { + log(LOG_NOTICE, "bad packet length in IPv4 ESP input: %s %s\n", + ipsec4_logpacketstr(ip, spi), + ipsec_logsastr(sav)); + ipsecstat.in_inval++; + goto bad; + } + siz -= taillen; + + /* find the final mbuf */ + for (n = m; n; n = n->m_next) { + if (n->m_len < siz) + siz -= n->m_len; + else + break; + } + if (!n) { + printf("invalid packet\n"); + ipsecstat.in_inval++; + goto bad; + } + + /* trim the final mbuf */ + if (n->m_len < siz) { + printf("invalid size: %d %lu\n", n->m_len, (u_long)siz); + ipsecstat.in_inval++; + goto bad; + } + n->m_len = siz; + + /* dispose the rest of the packet */ + m_freem(n->m_next); + n->m_next = NULL; + + m->m_pkthdr.len -= taillen; + } + + ip->ip_len = ip->ip_len - taillen; + } + + /* was it transmitted over the IPsec tunnel SA? */ + if (ipsec4_tunnel_validate(ip, nxt, sav) && nxt == IPPROTO_IPV4) { + /* + * strip off all the headers that precedes ESP header. + * IP4 xx ESP IP4' payload -> IP4' payload + * + * XXX more sanity checks + * XXX relationship with gif? + */ + struct ip oip; /* for debug */ + u_int8_t tos; + + tos = ip->ip_tos; + bcopy(mtod(m, struct ip *), &oip, sizeof(oip)); /* for debug */ + m_adj(m, off + esplen + ivlen); + if (m->m_len < sizeof(*ip)) { + m = m_pullup(m, sizeof(*ip)); + if (!m) { + ipsecstat.in_inval++; + goto bad; + } + } + ip = mtod(m, struct ip *); + /* ECN consideration. */ + ip_ecn_egress(ip4_ipsec_ecn, &tos, &ip->ip_tos); + if (!key_checktunnelsanity(sav, AF_INET, + (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst)) { + log(LOG_NOTICE, "ipsec tunnel address mismatch in IPv4 ESP input: %s %s\n", + ipsec4_logpacketstr(ip, spi), + ipsec_logsastr(sav)); + ipsecstat.in_inval++; + goto bad; + } + + key_sa_recordxfer(sav, m); + + s = splimp(); + if (IF_QFULL(&ipintrq)) { + ipsecstat.in_inval++; + goto bad; + } + IF_ENQUEUE(&ipintrq, m); + m = NULL; + schednetisr(NETISR_IP); /*can be skipped but to make sure*/ + splx(s); + nxt = IPPROTO_DONE; + } else { + /* + * strip off ESP header and IV. + * We do deep-copy since KAME requires packet to be placed + * in a single mbuf. + */ + size_t stripsiz; + + stripsiz = esplen + ivlen; + + ip = mtod(m, struct ip *); + ovbcopy((caddr_t)ip, (caddr_t)(((u_char *)ip) + stripsiz), off); + m->m_data += stripsiz; + m->m_len -= stripsiz; + m->m_pkthdr.len -= stripsiz; + + ip = mtod(m, struct ip *); + ip->ip_len = ip->ip_len - stripsiz; + ip->ip_p = nxt; + + key_sa_recordxfer(sav, m); + + if (nxt != IPPROTO_DONE) + (*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt); + else + m_freem(m); + m = NULL; + } + + if (sav) { + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP esp4_input call free SA:%p\n", sav)); + key_freesav(sav); + } + ipsecstat.in_success++; + return; + +bad: + if (sav) { + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP esp4_input call free SA:%p\n", sav)); + key_freesav(sav); + } + if (m) + m_freem(m); + return; +} +#endif /* INET */ + +#ifdef INET6 +int +esp6_input(mp, offp, proto) + struct mbuf **mp; + int *offp, proto; +{ + struct mbuf *m = *mp; + int off = *offp; + struct ip6_hdr *ip6; + struct esp *esp; + struct esptail *esptail; + u_int32_t spi; + struct secasvar *sav = NULL; + size_t taillen; + u_int16_t nxt; + struct esp_algorithm *algo; + int ivlen; + size_t esplen; + int s; + + /* sanity check for alignment. */ + if (off % 4 != 0 || m->m_pkthdr.len % 4 != 0) { + printf("IPv6 ESP input: packet alignment problem " + "(off=%d, pktlen=%d)\n", off, m->m_pkthdr.len); + ipsec6stat.in_inval++; + goto bad; + } + + IP6_EXTHDR_CHECK(m, off, sizeof(struct esp), IPPROTO_DONE); + + ip6 = mtod(m, struct ip6_hdr *); + esp = (struct esp *)(((u_int8_t *)ip6) + off); + + if (ntohs(ip6->ip6_plen) == 0) { + printf("IPv6 ESP input: ESP with IPv6 jumbogram is not supported.\n"); + ipsec6stat.in_inval++; + goto bad; + } + + /* find the sassoc. */ + spi = esp->esp_spi; + + if ((sav = key_allocsa(AF_INET6, + (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst, + IPPROTO_ESP, spi)) == 0) { + printf("IPv6 ESP input: no key association found for spi %u;" + "dropping the packet for simplicity\n", + (u_int32_t)ntohl(spi)); + ipsec6stat.in_nosa++; + goto bad; + } + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP esp6_input called to allocate SA:%p\n", sav)); + if (sav->state != SADB_SASTATE_MATURE + && sav->state != SADB_SASTATE_DYING) { + printf("IPv6 ESP input: non-mature/dying SA found for spi %u; " + "dropping the packet for simplicity\n", + (u_int32_t)ntohl(spi)); + ipsec6stat.in_badspi++; + goto bad; + } + if (sav->alg_enc == SADB_EALG_NONE) { + printf("IPv6 ESP input: unspecified encryption algorithm " + "for spi %u;" + "dropping the packet for simplicity\n", + (u_int32_t)ntohl(spi)); + ipsec6stat.in_badspi++; + goto bad; + } + + algo = &esp_algorithms[sav->alg_enc]; /*XXX*/ + + /* check if we have proper ivlen information */ + ivlen = sav->ivlen; + if (ivlen < 0) { + log(LOG_NOTICE, "inproper ivlen in IPv6 ESP input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), + ipsec_logsastr(sav)); + ipsec6stat.in_badspi++; + goto bad; + } + + if (!((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay + && (sav->alg_auth && sav->key_auth))) + goto noreplaycheck; + + if (sav->alg_auth == SADB_AALG_NULL) + goto noreplaycheck; + + /* + * check for sequence number. + */ + if (ipsec_chkreplay(ntohl(((struct newesp *)esp)->esp_seq), sav)) + ; /*okey*/ + else { + ipsec6stat.in_espreplay++; + log(LOG_AUTH, "replay packet in IPv6 ESP input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), + ipsec_logsastr(sav)); + goto bad; + } + + /* check ICV */ + { + struct mbuf *n; + size_t len; + u_char sum0[AH_MAXSUMSIZE]; + u_char sum[AH_MAXSUMSIZE]; + struct ah_algorithm *sumalgo; + size_t siz; + + sumalgo = &ah_algorithms[sav->alg_auth]; + siz = (((*sumalgo->sumsiz)(sav) + 3) & ~(4 - 1)); + if (AH_MAXSUMSIZE < siz) { + printf("internal error: AH_MAXSUMSIZE must be larger than %lu\n", + (u_long)siz); + ipsec6stat.in_inval++; + goto bad; + } + + n = m; + len = m->m_pkthdr.len; + len -= siz; /*XXX*/ + while (n && 0 < len) { + if (len < n->m_len) + break; + len -= n->m_len; + n = n->m_next; + } + if (!n) { + printf("mbuf chain problem?\n"); + ipsec6stat.in_inval++; + goto bad; + } + m_copydata(n, len, siz, &sum0[0]); + + if (esp_auth(m, off, m->m_pkthdr.len - off - siz, sav, sum)) { + log(LOG_AUTH, "auth fail in IPv6 ESP input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), + ipsec_logsastr(sav)); + ipsec6stat.in_espauthfail++; + goto bad; + } + + if (bcmp(sum0, sum, siz) != 0) { + log(LOG_AUTH, "auth fail in IPv6 ESP input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), + ipsec_logsastr(sav)); + ipsec6stat.in_espauthfail++; + goto bad; + } + + /* strip off */ + m->m_pkthdr.len -= siz; + n->m_len -= siz; + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - siz); + + m->m_flags |= M_AUTHIPDGM; + ipsec6stat.in_espauthsucc++; + } + + /* + * update sequence number. + */ + if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { + (void)ipsec_updatereplay(ntohl(((struct newesp *)esp)->esp_seq), sav); + } + +noreplaycheck: + + /* process main esp header. */ + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1827 */ + esplen = sizeof(struct esp); + } else { + /* RFC 2406 */ + if (sav->flags & SADB_X_EXT_DERIV) + esplen = sizeof(struct esp); + else + esplen = sizeof(struct newesp); + } + + IP6_EXTHDR_CHECK(m, off, esplen + ivlen, IPPROTO_DONE); /*XXX*/ + + /* + * decrypt the packet. + */ + if (!algo->decrypt) + panic("internal error: no decrypt function"); + if ((*algo->decrypt)(m, off, sav, algo, ivlen)) { + log(LOG_AUTH, "decrypt fail in IPv6 ESP input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), + ipsec_logsastr(sav)); + ipsec6stat.in_inval++; + goto bad; + } + ipsec6stat.in_esphist[sav->alg_enc]++; + + m->m_flags |= M_DECRYPTED; + +#ifdef garbled_data_found_on_mbuf_after_packet + { + /* + * For simplicity, we'll trim the packet so that there's no extra + * part appended after IP packet. + * This is rare case for some odd drivers, so there should be no + * performance hit. + */ + + /* + * Note that, in ip_input, ip_len was already flipped and header + * length was subtracted from ip_len. + */ + if (m->m_pkthdr.len != hlen + ip->ip_len) + { + size_t siz; + struct mbuf *n; + + siz = hlen + ip->ip_len; + + /* find the final mbuf */ + for (n = m; n; n = n->m_next) { + if (n->m_len < siz) + siz -= n->m_len; + else + break; + } + if (!n) { + printf("invalid packet\n"); + ipsec6stat.in_inval++; + goto bad; + } + + /* trim the final mbuf */ + if (n->m_len < siz) { + printf("invalid size: %d %d\n", n->m_len, siz); + ipsec6stat.in_inval++; + goto bad; + } + n->m_len = siz; + + /* dispose the rest of the packet */ + m_freem(n->m_next); + n->m_next = NULL; + + m->m_pkthdr.len = hlen + ip->ip_len; + } + } +#endif + + { + /* + * find the trailer of the ESP. + */ + struct mbuf *n; /*the last mbuf on the mbuf chain, m_len > 0 */ + struct mbuf *o; /*the last mbuf on the mbuf chain */ + + o = m; + n = NULL; + while (o) { + if (0 < o->m_len) + n = o; + o = o->m_next; + } + if (!n || n->m_len < sizeof(struct esptail)) { + printf("IPv6 ESP input: assertion on pad part failed; " + "dropping the packet\n"); + ipsec6stat.in_inval++; + goto bad; + } + + esptail = (struct esptail *) + (mtod(n, u_int8_t *) + n->m_len - sizeof(struct esptail)); + nxt = esptail->esp_nxt; + taillen = esptail->esp_padlen + 2; + + if (m->m_pkthdr.len < taillen + || m->m_pkthdr.len - taillen < sizeof(struct ip6_hdr)) { /*?*/ + log(LOG_NOTICE, "bad pad length in IPv6 ESP input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), + ipsec_logsastr(sav)); + ipsec6stat.in_inval++; + goto bad; + } + + /* + * XXX strip off the padding. + */ + if (taillen < n->m_len) { + /* trailing pad data is included in the last mbuf item. */ + n->m_len -= taillen; + m->m_pkthdr.len -= taillen; + } else { + /* trailing pad data spans on multiple mbuf item. */ + size_t siz; + + siz = m->m_pkthdr.len; + if (siz < taillen) { + log(LOG_NOTICE, "bad packet length in IPv6 ESP input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), + ipsec_logsastr(sav)); + ipsec6stat.in_inval++; + goto bad; + } + siz -= taillen; + + /* find the final mbuf */ + for (n = m; n; n = n->m_next) { + if (n->m_len < siz) + siz -= n->m_len; + else + break; + } + if (!n) { + printf("invalid packet\n"); + ipsec6stat.in_inval++; + goto bad; + } + + /* trim the final mbuf */ + if (n->m_len < siz) { + printf("invalid size: %d %lu\n", n->m_len, (u_long)siz); + ipsec6stat.in_inval++; + goto bad; + } + n->m_len = siz; + + /* dispose the rest of the packet */ + m_freem(n->m_next); + n->m_next = NULL; + + m->m_pkthdr.len -= taillen; + } + + ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - taillen); + } + + /* was it transmitted over the IPsec tunnel SA? */ + if (ipsec6_tunnel_validate(ip6, nxt, sav) && nxt == IPPROTO_IPV6) { + /* + * strip off all the headers that precedes ESP header. + * IP6 xx ESP IP6' payload -> IP6' payload + * + * XXX more sanity checks + * XXX relationship with gif? + */ + u_int32_t flowinfo; /*net endian*/ + flowinfo = ip6->ip6_flow; + m_adj(m, off + esplen + ivlen); + if (m->m_len < sizeof(*ip6)) { + /* + * m_pullup is prohibited in KAME IPv6 input processing + * but there's no other way! + */ + m = m_pullup(m, sizeof(*ip6)); + if (!m) { + ipsec6stat.in_inval++; + goto bad; + } + } + ip6 = mtod(m, struct ip6_hdr *); + /* ECN consideration. */ + ip6_ecn_egress(ip6_ipsec_ecn, &flowinfo, &ip6->ip6_flow); + if (!key_checktunnelsanity(sav, AF_INET6, + (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst)) { + log(LOG_NOTICE, "ipsec tunnel address mismatch in IPv6 ESP input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), + ipsec_logsastr(sav)); + ipsec6stat.in_inval++; + goto bad; + } + + key_sa_recordxfer(sav, m); + + s = splimp(); + if (IF_QFULL(&ip6intrq)) { + ipsec6stat.in_inval++; + goto bad; + } + IF_ENQUEUE(&ip6intrq, m); + m = NULL; + schednetisr(NETISR_IPV6); /*can be skipped but to make sure*/ + splx(s); + nxt = IPPROTO_DONE; + } else { + /* + * strip off ESP header and IV. + * We do deep-copy since KAME requires packet to be placed + * in a single mbuf. + */ + size_t stripsiz; + char *prvnxtp; + + /* + * Set the next header field of the previous header correctly. + */ + prvnxtp = ip6_get_prevhdr(m, off); /* XXX */ + *prvnxtp = nxt; + + stripsiz = esplen + ivlen; + + ip6 = mtod(m, struct ip6_hdr *); + ovbcopy((caddr_t)ip6, (caddr_t)(((u_char *)ip6) + stripsiz), + off); + m->m_data += stripsiz; + m->m_len -= stripsiz; + m->m_pkthdr.len -= stripsiz; + + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz); + + key_sa_recordxfer(sav, m); + } + + *offp = off; + *mp = m; + + if (sav) { + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP esp6_input call free SA:%p\n", sav)); + key_freesav(sav); + } + ipsec6stat.in_success++; + return nxt; + +bad: + if (sav) { + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP esp6_input call free SA:%p\n", sav)); + key_freesav(sav); + } + if (m) + m_freem(m); + return IPPROTO_DONE; +} +#endif /* INET6 */ diff --git a/sys/netinet6/esp_output.c b/sys/netinet6/esp_output.c new file mode 100644 index 0000000..2386bfd --- /dev/null +++ b/sys/netinet6/esp_output.c @@ -0,0 +1,683 @@ +/* + * 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$ + */ + +#include "opt_inet.h" +#include "opt_inet6.h" +#include "opt_ipsec.h" + +/* + * RFC1827/2406 Encapsulated Security Payload. + */ + +#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 <sys/syslog.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 <netkey/key.h> +#include <netkey/keydb.h> +#ifdef IPSEC_DEBUG +#include <netkey/key_debug.h> +#else +#define KEYDEBUG(lev,arg) +#endif + +#include <net/net_osdep.h> + +static int esp_output __P((struct mbuf *, u_char *, struct mbuf *, + struct ipsecrequest *, int)); + +/* + * compute ESP header size. + */ +size_t +esp_hdrsiz(isr) + struct ipsecrequest *isr; +{ + struct secasvar *sav; + struct esp_algorithm *algo; + size_t ivlen; + size_t authlen; + size_t hdrsiz; + + /* sanity check */ + if (isr == NULL) + panic("esp_hdrsiz: NULL was passed.\n"); + + sav = isr->sav; + + if (isr->saidx.proto != IPPROTO_ESP) + panic("unsupported mode passed to esp_hdrsiz"); + + if (sav == NULL) + goto contrive; + if (sav->state != SADB_SASTATE_MATURE + && sav->state != SADB_SASTATE_DYING) + goto contrive; + + /* we need transport mode ESP. */ + algo = &esp_algorithms[sav->alg_enc]; + if (!algo) + goto contrive; + ivlen = sav->ivlen; + if (ivlen < 0) + goto contrive; + + /* + * XXX + * right now we don't calcurate the padding size. simply + * treat the padding size as constant, for simplicity. + * + * XXX variable size padding support + */ + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1827 */ + hdrsiz = sizeof(struct esp) + ivlen + 9; + } else { + /* RFC 2406 */ + if (sav->replay && sav->alg_auth && sav->key_auth) + authlen = (*ah_algorithms[sav->alg_auth].sumsiz)(sav); + else + authlen = 0; + hdrsiz = sizeof(struct newesp) + ivlen + 9 + authlen; + } + + return hdrsiz; + + contrive: + /* + * ASSUMING: + * sizeof(struct newesp) > sizeof(struct esp). + * 8 = ivlen for CBC mode (RFC2451). + * 9 = (maximum padding length without random Padding length) + * + (Pad Length field) + (Next Header field). + * 16 = maximum ICV we supported. + */ + return sizeof(struct newesp) + 8 + 9 + 16; +} + +/* + * Modify the packet so that the payload is encrypted. + * The mbuf (m) must start with IPv4 or IPv6 header. + * On failure, free the given mbuf and return NULL. + * + * on invocation: + * m nexthdrp md + * v v v + * IP ......... payload + * during the encryption: + * m nexthdrp mprev md + * v v v v + * IP ............... esp iv payload pad padlen nxthdr + * <--><-><------><---------------> + * esplen plen extendsiz + * ivlen + * <-----> esphlen + * <-> hlen + * <-----------------> espoff + */ +static int +esp_output(m, nexthdrp, md, isr, af) + struct mbuf *m; + u_char *nexthdrp; + struct mbuf *md; + struct ipsecrequest *isr; + int af; +{ + struct mbuf *n; + struct mbuf *mprev; + struct esp *esp; + struct esptail *esptail; + struct secasvar *sav = isr->sav; + struct esp_algorithm *algo; + u_int32_t spi; + u_int8_t nxt = 0; + size_t plen; /*payload length to be encrypted*/ + size_t espoff; + int ivlen; + int afnumber; + size_t extendsiz; + int error = 0; + + switch (af) { +#ifdef INET + case AF_INET: + afnumber = 4; + break; +#endif +#ifdef INET6 + case AF_INET6: + afnumber = 6; + break; +#endif + default: + printf("esp_output: unsupported af %d\n", af); + return 0; /* no change at all */ + } + + /* some sanity check */ + if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) { + switch (af) { +#ifdef INET + case AF_INET: + { + struct ip *ip; + + ip = mtod(m, struct ip *); + printf("esp4_output: internal error: " + "sav->replay is null: " + "%x->%x, SPI=%u\n", + (u_int32_t)ntohl(ip->ip_src.s_addr), + (u_int32_t)ntohl(ip->ip_dst.s_addr), + (u_int32_t)ntohl(sav->spi)); + ipsecstat.out_inval++; + m_freem(m); + return EINVAL; + } +#endif /*INET*/ +#ifdef INET6 + case AF_INET6: + { + struct ip6_hdr *ip6; + + ip6 = mtod(m, struct ip6_hdr *); + printf("esp6_output: internal error: " + "sav->replay is null: SPI=%u\n", + (u_int32_t)ntohl(sav->spi)); + ipsec6stat.out_inval++; + m_freem(m); + return EINVAL; + } +#endif /*INET6*/ + } + } + + algo = &esp_algorithms[sav->alg_enc]; /*XXX*/ + spi = sav->spi; + ivlen = sav->ivlen; + /* should be okey */ + if (ivlen < 0) { + panic("invalid ivlen"); + } + + { + /* + * insert ESP header. + * XXX inserts ESP header right after IPv4 header. should + * chase the header chain. + * XXX sequential number + */ +#ifdef INET + struct ip *ip = NULL; +#endif +#ifdef INET6 + struct ip6_hdr *ip6 = NULL; +#endif + size_t esplen; /*sizeof(struct esp/newesp)*/ + size_t esphlen; /*sizeof(struct esp/newesp) + ivlen*/ + size_t hlen = 0; /*ip header len*/ + + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1827 */ + esplen = sizeof(struct esp); + } else { + /* RFC 2406 */ + if (sav->flags & SADB_X_EXT_DERIV) + esplen = sizeof(struct esp); + else + esplen = sizeof(struct newesp); + } + esphlen = esplen + ivlen; + + for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next) + ; + if (mprev == NULL || mprev->m_next != md) { + printf("esp%d_output: md is not in chain\n", afnumber); + m_freem(m); + return EINVAL; + } + + plen = 0; + for (n = md; n; n = n->m_next) + plen += n->m_len; + + switch (af) { +#ifdef INET + case AF_INET: + ip = mtod(m, struct ip *); +#ifdef _IP_VHL + hlen = IP_VHL_HL(ip->ip_vhl) << 2; +#else + hlen = ip->ip_hl << 2; +#endif + break; +#endif +#ifdef INET6 + case AF_INET6: + ip6 = mtod(m, struct ip6_hdr *); + hlen = sizeof(*ip6); + break; +#endif + } + + /* make the packet over-writable */ + mprev->m_next = NULL; + if ((md = ipsec_copypkt(md)) == NULL) { + m_freem(m); + error = ENOBUFS; + goto fail; + } + mprev->m_next = md; + + espoff = m->m_pkthdr.len - plen; + + /* + * grow the mbuf to accomodate ESP header. + * before: IP ... payload + * after: IP ... ESP IV payload + */ + if (M_LEADINGSPACE(md) < esphlen) { + MGET(n, M_DONTWAIT, MT_DATA); + if (!n) { + m_freem(m); + error = ENOBUFS; + goto fail; + } + n->m_len = esphlen; + mprev->m_next = n; + n->m_next = md; + m->m_pkthdr.len += esphlen; + esp = mtod(n, struct esp *); + } else { + md->m_len += esphlen; + md->m_data -= esphlen; + m->m_pkthdr.len += esphlen; + esp = mtod(md, struct esp *); + } + + nxt = *nexthdrp; + *nexthdrp = IPPROTO_ESP; + switch (af) { +#ifdef INET + case AF_INET: + if (esphlen < (IP_MAXPACKET - ntohs(ip->ip_len))) + ip->ip_len = htons(ntohs(ip->ip_len) + esphlen); + else { + printf("IPv4 ESP output: size exceeds limit\n"); + ipsecstat.out_inval++; + m_freem(m); + error = EMSGSIZE; + goto fail; + } + break; +#endif +#ifdef INET6 + case AF_INET6: + /* total packet length will be computed in ip6_output() */ + break; +#endif + } + } + + /* initialize esp header. */ + esp->esp_spi = spi; + if ((sav->flags & SADB_X_EXT_OLD) == 0) { + struct newesp *nesp; + nesp = (struct newesp *)esp; + sav->replay->count++; + /* + * XXX sequence number must not be cycled, if the SA is + * installed by IKE daemon. + */ + nesp->esp_seq = htonl(sav->replay->count); + } + + { + /* + * find the last mbuf. make some room for ESP trailer. + * XXX new-esp authentication data + */ +#ifdef INET + struct ip *ip = NULL; +#endif + size_t padbound; + + if (algo->padbound) + padbound = algo->padbound; + else + padbound = 4; + /* ESP packet, including nxthdr field, must be length of 4n */ + if (padbound < 4) + padbound = 4; + + extendsiz = padbound - (plen % padbound); + if (extendsiz == 1) + extendsiz = padbound + 1; + + n = m; + while (n->m_next) + n = n->m_next; + + /* + * if M_EXT, the external part may be shared among + * two consequtive TCP packets. + */ + if (!(n->m_flags & M_EXT) && extendsiz < M_TRAILINGSPACE(n)) { + switch (sav->flags & SADB_X_EXT_PMASK) { + case SADB_X_EXT_PRAND: + break; + case SADB_X_EXT_PZERO: + bzero((caddr_t)(mtod(n, u_int8_t *) + n->m_len), + extendsiz); + break; + case SADB_X_EXT_PSEQ: + { + int i; + u_char *p; + p = mtod(n, u_char *) + n->m_len; + for (i = 0; i < extendsiz; i++) + p[i] = i + 1; + break; + } + } + n->m_len += extendsiz; + m->m_pkthdr.len += extendsiz; + } else { + struct mbuf *nn; + + MGET(nn, M_DONTWAIT, MT_DATA); + if (!nn) { + printf("esp%d_output: can't alloc mbuf", afnumber); + m_freem(m); + error = ENOBUFS; + goto fail; + } + nn->m_len = extendsiz; + switch (sav->flags & SADB_X_EXT_PMASK) { + case SADB_X_EXT_PRAND: + break; + case SADB_X_EXT_PZERO: + bzero(mtod(nn, caddr_t), extendsiz); + break; + case SADB_X_EXT_PSEQ: + { + int i; + u_char *p; + p = mtod(nn, u_char *); + for (i = 0; i < extendsiz; i++) + p[i] = i + 1; + break; + } + } + nn->m_next = NULL; + n->m_next = nn; + n = nn; + m->m_pkthdr.len += extendsiz; + } + + /* initialize esp trailer. */ + esptail = (struct esptail *) + (mtod(n, u_int8_t *) + n->m_len - sizeof(struct esptail)); + esptail->esp_nxt = nxt; + esptail->esp_padlen = extendsiz - 2; + + /* modify IP header (for ESP header part only) */ + switch (af) { +#ifdef INET + case AF_INET: + ip = mtod(m, struct ip *); + if (extendsiz < (IP_MAXPACKET - ntohs(ip->ip_len))) + ip->ip_len = htons(ntohs(ip->ip_len) + extendsiz); + else { + printf("IPv4 ESP output: size exceeds limit\n"); + ipsecstat.out_inval++; + m_freem(m); + error = EMSGSIZE; + goto fail; + } + break; +#endif +#ifdef INET6 + case AF_INET6: + /* total packet length will be computed in ip6_output() */ + break; +#endif + } + } + + /* + * encrypt the packet, based on security association + * and the algorithm specified. + */ + if (!algo->encrypt) + panic("internal error: no encrypt function"); + if ((*algo->encrypt)(m, espoff, plen + extendsiz, sav, algo, ivlen)) { + printf("packet encryption failure\n"); + m_freem(m); + switch (af) { +#ifdef INET + case AF_INET: + ipsecstat.out_inval++; + break; +#endif +#ifdef INET6 + case AF_INET6: + ipsec6stat.out_inval++; + break; +#endif + } + error = EINVAL; + goto fail; + } + + /* + * calculate ICV if required. + */ + if (!sav->replay) + goto noantireplay; + if (!sav->key_auth) + goto noantireplay; + if (!sav->alg_auth) + goto noantireplay; + { + u_char authbuf[AH_MAXSUMSIZE]; + struct mbuf *n; + u_char *p; + size_t siz; + struct ip *ip; + + siz = (((*ah_algorithms[sav->alg_auth].sumsiz)(sav) + 3) & ~(4 - 1)); + if (AH_MAXSUMSIZE < siz) + panic("assertion failed for AH_MAXSUMSIZE"); + + if (esp_auth(m, espoff, m->m_pkthdr.len - espoff, sav, authbuf)) + goto noantireplay; + + n = m; + while (n->m_next) + n = n->m_next; + + if (!(n->m_flags & M_EXT) && siz < M_TRAILINGSPACE(n)) { /*XXX*/ + n->m_len += siz; + m->m_pkthdr.len += siz; + p = mtod(n, u_char *) + n->m_len - siz; + } else { + struct mbuf *nn; + + MGET(nn, M_DONTWAIT, MT_DATA); + if (!nn) { + printf("can't alloc mbuf in esp%d_output", afnumber); + m_freem(m); + error = ENOBUFS; + goto fail; + } + nn->m_len = siz; + nn->m_next = NULL; + n->m_next = nn; + n = nn; + m->m_pkthdr.len += siz; + p = mtod(nn, u_char *); + } + bcopy(authbuf, p, siz); + + /* modify IP header (for ESP header part only) */ + switch (af) { +#ifdef INET + case AF_INET: + ip = mtod(m, struct ip *); + if (siz < (IP_MAXPACKET - ntohs(ip->ip_len))) + ip->ip_len = htons(ntohs(ip->ip_len) + siz); + else { + printf("IPv4 ESP output: size exceeds limit\n"); + ipsecstat.out_inval++; + m_freem(m); + error = EMSGSIZE; + goto fail; + } + break; +#endif +#ifdef INET6 + case AF_INET6: + /* total packet length will be computed in ip6_output() */ + break; +#endif + } + } + +noantireplay: + if (!m) + printf("NULL mbuf after encryption in esp%d_output", afnumber); + else { + switch (af) { +#ifdef INET + case AF_INET: + ipsecstat.out_success++; + break; +#endif +#ifdef INET6 + case AF_INET6: + ipsec6stat.out_success++; + break; +#endif + } + } + switch (af) { +#ifdef INET + case AF_INET: + ipsecstat.out_esphist[sav->alg_enc]++; + break; +#endif +#ifdef INET6 + case AF_INET6: + ipsec6stat.out_esphist[sav->alg_enc]++; + break; +#endif + } + key_sa_recordxfer(sav, m); + return 0; + +fail: +#if 1 + return error; +#else + panic("something bad in esp_output"); +#endif +} + +#ifdef INET +int +esp4_output(m, isr) + struct mbuf *m; + struct ipsecrequest *isr; +{ + struct ip *ip; + if (m->m_len < sizeof(struct ip)) { + printf("esp4_output: first mbuf too short\n"); + m_freem(m); + return NULL; + } + ip = mtod(m, struct ip *); + /* XXX assumes that m->m_next points to payload */ + return esp_output(m, &ip->ip_p, m->m_next, isr, AF_INET); +} +#endif /*INET*/ + +#ifdef INET6 +int +esp6_output(m, nexthdrp, md, isr) + struct mbuf *m; + u_char *nexthdrp; + struct mbuf *md; + struct ipsecrequest *isr; +{ + if (m->m_len < sizeof(struct ip6_hdr)) { + printf("esp6_output: first mbuf too short\n"); + m_freem(m); + return NULL; + } + return esp_output(m, nexthdrp, md, isr, AF_INET6); +} +#endif /*INET6*/ diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c index f7d03cd..02c2219 100644 --- a/sys/netinet6/icmp6.c +++ b/sys/netinet6/icmp6.c @@ -64,7 +64,7 @@ * @(#)ip_icmp.c 8.2 (Berkeley) 1/4/94 */ -#include "opt_key.h" +#include "opt_ipsec.h" #include <sys/param.h> #include <sys/systm.h> @@ -96,15 +96,15 @@ #ifdef IPSEC #include <netinet6/ipsec.h> +#include <netinet6/ah.h> #include <netinet6/ipsec6.h> +#include <netinet6/ah6.h> #include <netkey/key.h> -#ifdef KEY_DEBUG +#ifdef IPSEC_DEBUG #include <netkey/key_debug.h> #else -#define DPRINTF(lev,arg) -#define DDO(lev, stmt) -#define DP(x, y, z) -#endif /* KEY_DEBUG */ +#define KEYDEBUG(lev,arg) +#endif #endif /* IPSEC */ #include "faith.h" @@ -1285,9 +1285,6 @@ icmp6_reflect(m, off) */ m->m_flags &= ~(M_BCAST|M_MCAST); -#ifdef IPSEC - m->m_pkthdr.rcvif = NULL; -#endif /*IPSEC*/ #ifdef COMPAT_RFC1885 ip6_output(m, NULL, &icmp6_reflect_rt, 0, NULL, &outif); @@ -1725,9 +1722,6 @@ noredhdropt:; = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), ntohs(ip6->ip6_plen)); /* send the packet to outside... */ -#ifdef IPSEC - m->m_pkthdr.rcvif = NULL; -#endif /*IPSEC*/ ip6_output(m, NULL, NULL, 0, NULL, &outif); if (outif) { icmp6_ifstat_inc(outif, ifs6_out_msg); diff --git a/sys/netinet6/icmp6.h b/sys/netinet6/icmp6.h index 2fdb28b..c67e961 100644 --- a/sys/netinet6/icmp6.h +++ b/sys/netinet6/icmp6.h @@ -386,8 +386,8 @@ struct rr_pco_use { /* use prefix part */ u_int32_t rpu_flags; struct in6_addr rpu_prefix; }; -#define ICMP6_RR_PCOUSE_RAFLAGS_ONLINK 0x20 -#define ICMP6_RR_PCOUSE_RAFLAGS_AUTO 0x10 +#define ICMP6_RR_PCOUSE_RAFLAGS_ONLINK 0x80 +#define ICMP6_RR_PCOUSE_RAFLAGS_AUTO 0x40 #if BYTE_ORDER == BIG_ENDIAN #define ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME 0x80000000 diff --git a/sys/netinet6/in6_gif.c b/sys/netinet6/in6_gif.c index dd7cd2f..35c4088 100644 --- a/sys/netinet6/in6_gif.c +++ b/sys/netinet6/in6_gif.c @@ -55,6 +55,8 @@ #include <netinet6/ip6_var.h> #include <netinet6/in6_gif.h> #include <netinet6/ip6.h> +#include <netinet/ip_ecn.h> +#include <netinet6/ip6_ecn.h> #include <net/if_gif.h> @@ -73,6 +75,7 @@ in6_gif_output(ifp, family, m, rt) struct sockaddr_in6 *sin6_dst = (struct sockaddr_in6 *)sc->gif_pdst; struct ip6_hdr *ip6; int proto; + u_int8_t itos, otos; if (sin6_src == NULL || sin6_dst == NULL || sin6_src->sin6_family != AF_INET6 || @@ -94,6 +97,7 @@ in6_gif_output(ifp, family, m, rt) return ENOBUFS; } ip = mtod(m, struct ip *); + itos = ip->ip_tos; break; } #endif @@ -107,6 +111,7 @@ in6_gif_output(ifp, family, m, rt) return ENOBUFS; } ip6 = mtod(m, struct ip6_hdr *); + itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; break; } default: @@ -153,6 +158,11 @@ in6_gif_output(ifp, family, m, rt) return ENETUNREACH; } } + if (ifp->if_flags & IFF_LINK1) { + otos = 0; + ip_ecn_ingress(ECN_ALLOWED, &otos, &itos); + ip6->ip6_flow |= htonl((u_int32_t)otos << 20); + } if (dst->sin6_family != sin6_dst->sin6_family || !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &sin6_dst->sin6_addr)) { @@ -175,9 +185,6 @@ in6_gif_output(ifp, family, m, rt) } } -#ifdef IPSEC - m->m_pkthdr.rcvif = NULL; -#endif /*IPSEC*/ return(ip6_output(m, 0, &sc->gif_ro6, 0, 0, NULL)); } @@ -191,6 +198,7 @@ int in6_gif_input(mp, offp, proto) struct ip6_hdr *ip6; int i; int af = 0; + u_int32_t otos; ip6 = mtod(m, struct ip6_hdr *); @@ -222,7 +230,8 @@ int in6_gif_input(mp, offp, proto) ip6stat.ip6s_nogif++; return IPPROTO_DONE; } - + + otos = ip6->ip6_flow; m_adj(m, *offp); switch (proto) { @@ -230,13 +239,17 @@ int in6_gif_input(mp, offp, proto) case IPPROTO_IPV4: { struct ip *ip; + u_int8_t otos8; af = AF_INET; + otos8 = (ntohl(otos) >> 20) & 0xff; if (m->m_len < sizeof(*ip)) { m = m_pullup(m, sizeof(*ip)); if (!m) return IPPROTO_DONE; } ip = mtod(m, struct ip *); + if (gifp->if_flags & IFF_LINK1) + ip_ecn_egress(ECN_ALLOWED, &otos8, &ip->ip_tos); break; } #endif /* INET */ @@ -250,6 +263,8 @@ int in6_gif_input(mp, offp, proto) return IPPROTO_DONE; } ip6 = mtod(m, struct ip6_hdr *); + if (gifp->if_flags & IFF_LINK1) + ip6_ecn_egress(ECN_ALLOWED, &otos, &ip6->ip6_flow); break; } default: diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c index 89db824..acc155e 100644 --- a/sys/netinet6/in6_pcb.c +++ b/sys/netinet6/in6_pcb.c @@ -65,7 +65,7 @@ * $FreeBSD$ */ -#include "opt_key.h" +#include "opt_ipsec.h" #include <sys/param.h> #include <sys/systm.h> @@ -99,15 +99,15 @@ #ifdef IPSEC #include <netinet6/ipsec.h> +#include <netinet6/ah.h> #include <netinet6/ipsec6.h> +#include <netinet6/ah6.h> #include <netkey/key.h> -#ifdef KEY_DEBUG +#ifdef IPSEC_DEBUG #include <netkey/key_debug.h> #else -#define DPRINTF(lev,arg) -#define DDO(lev, stmt) -#define DP(x, y, z) -#endif /* KEY_DEBUG */ +#define KEYDEBUG(lev,arg) +#endif /* IPSEC_DEBUG */ #endif /* IPSEC */ struct in6_addr zeroin6_addr; diff --git a/sys/netinet6/in6_proto.c b/sys/netinet6/in6_proto.c index 3f59ec2..60c3fcf 100644 --- a/sys/netinet6/in6_proto.c +++ b/sys/netinet6/in6_proto.c @@ -64,6 +64,8 @@ * @(#)in_proto.c 8.1 (Berkeley) 6/10/93 */ +#include "opt_ipsec.h" + #include <sys/param.h> #include <sys/socket.h> #include <sys/socketvar.h> @@ -102,12 +104,13 @@ #ifdef IPSEC #include <netinet6/ipsec.h> -#include <netinet6/ipsec6.h> #include <netinet6/ah.h> +#include <netinet6/ipsec6.h> +#include <netinet6/ah6.h> #ifdef IPSEC_ESP #include <netinet6/esp.h> +#include <netinet6/esp6.h> #endif -#include <netinet6/ipcomp.h> #endif /*IPSEC*/ #include <netinet6/ip6protosw.h> @@ -186,12 +189,6 @@ struct ip6protosw inet6sw[] = { &nousrreqs, }, #endif -{ SOCK_RAW, &inet6domain, IPPROTO_IPCOMP, PR_ATOMIC|PR_ADDR, - ipcomp6_input, 0, 0, 0, - 0, - 0, 0, 0, 0, - &nousrreqs, -}, #endif /* IPSEC */ #if NGIF > 0 { SOCK_RAW, &inet6domain, IPPROTO_IPV4, PR_ATOMIC|PR_ADDR, diff --git a/sys/netinet6/ip6.h b/sys/netinet6/ip6.h index 1dacb10..9ebd51e 100644 --- a/sys/netinet6/ip6.h +++ b/sys/netinet6/ip6.h @@ -75,12 +75,16 @@ struct ip6_hdr { union { struct ip6_hdrctl { - u_int32_t ip6_un1_flow; /* 20 bits of flow-ID */ + u_int32_t ip6_un1_flow; /* 4 bits version, + * 8 bits traffic + * class, + * 20 bits flow-ID */ u_int16_t ip6_un1_plen; /* payload length */ u_int8_t ip6_un1_nxt; /* next header */ u_int8_t ip6_un1_hlim; /* hop limit */ } ip6_un1; - u_int8_t ip6_un2_vfc; /* 4 bits version, 4 bits class */ + u_int8_t ip6_un2_vfc; /* 4 bits version, + * top 4 bits trafic class */ } ip6_ctlun; struct in6_addr ip6_src; /* source address */ struct in6_addr ip6_dst; /* destination address */ diff --git a/sys/netinet6/ip6_ecn.h b/sys/netinet6/ip6_ecn.h new file mode 100644 index 0000000..cafe9a5 --- /dev/null +++ b/sys/netinet6/ip6_ecn.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 1999 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. + * + * $Id: ip_ecn.h,v 1.2 1999/08/19 12:57:44 itojun Exp $ + * $FreeBSD$ + */ +/* + * ECN consideration on tunnel ingress/egress operation. + * http://www.aciri.org/floyd/papers/draft-ipsec-ecn-00.txt + */ + +#if defined(KERNEL) || defined(_KERNEL) +extern void ip6_ecn_ingress __P((int, u_int32_t *, u_int32_t *)); +extern void ip6_ecn_egress __P((int, u_int32_t *, u_int32_t *)); +#endif + + + diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c index 46b3188..2eaf6f5 100644 --- a/sys/netinet6/ip6_forward.c +++ b/sys/netinet6/ip6_forward.c @@ -29,7 +29,7 @@ * $FreeBSD$ */ -#include "opt_key.h" +#include "opt_ipsec.h" #include <sys/param.h> #include <sys/systm.h> @@ -57,13 +57,11 @@ #include <netinet6/ipsec.h> #include <netinet6/ipsec6.h> #include <netkey/key.h> -#ifdef KEY_DEBUG +#ifdef IPSEC_DEBUG #include <netkey/key_debug.h> #else -#define DPRINTF(lev,arg) -#define DDO(lev, stmt) -#define DP(x, y, z) -#endif /* KEY_DEBUG */ +#define KEYDEBUG(lev,arg) +#endif #endif /* IPSEC_IPV6FWD */ #ifdef IPV6FIREWALL diff --git a/sys/netinet6/ip6_fw.h b/sys/netinet6/ip6_fw.h deleted file mode 100644 index a356ac3..0000000 --- a/sys/netinet6/ip6_fw.h +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (c) 1993 Daniel Boulet - * Copyright (c) 1994 Ugen J.S.Antsilevich - * - * Redistribution and use in source forms, with and without modification, - * are permitted provided that this entire comment appears intact. - * - * Redistribution in binary form may occur without any restrictions. - * Obviously, it would be nice if you gave credit where credit is due - * but requiring it would be too onerous. - * - * This software is provided ``AS IS'' without any warranties of any kind. - * - * $Id: ip6_fw.h,v 1.1 1999/08/06 14:10:09 itojun Exp $ - * $FreeBSD$ - */ - -#ifndef _IP6_FW_H -#define _IP6_FW_H - -#include <net/if.h> - -/* - * This union structure identifies an interface, either explicitly - * by name or implicitly by IP address. The flags IP_FW_F_IIFNAME - * and IP_FW_F_OIFNAME say how to interpret this structure. An - * interface unit number of -1 matches any unit number, while an - * IP address of 0.0.0.0 indicates matches any interface. - * - * The receive and transmit interfaces are only compared against the - * the packet if the corresponding bit (IP_FW_F_IIFACE or IP_FW_F_OIFACE) - * is set. Note some packets lack a receive or transmit interface - * (in which case the missing "interface" never matches). - */ - -union ip6_fw_if { - struct in6_addr fu_via_ip6; /* Specified by IPv6 address */ - struct { /* Specified by interface name */ -#define FW_IFNLEN IFNAMSIZ - char name[FW_IFNLEN]; - short unit; /* -1 means match any unit */ - } fu_via_if; -}; - -/* - * Format of an IP firewall descriptor - * - * fw_src, fw_dst, fw_smsk, fw_dmsk are always stored in network byte order. - * fw_flg and fw_n*p are stored in host byte order (of course). - * Port numbers are stored in HOST byte order. - * Warning: setsockopt() will fail if sizeof(struct ip_fw) > MLEN (108) - */ - -struct ip6_fw { - u_long fw_pcnt,fw_bcnt; /* Packet and byte counters */ - struct in6_addr fw_src, fw_dst; /* Source and destination IPv6 addr */ - /* Mask for src and dest IPv6 addr */ - struct in6_addr fw_smsk, fw_dmsk; - u_short fw_number; /* Rule number */ - u_short fw_flg; /* Flags word */ -#define IPV6_FW_MAX_PORTS 10 /* A reasonable maximum */ - /* Array of port numbers to match */ - u_short fw_pts[IPV6_FW_MAX_PORTS]; - u_char fw_ip6opt,fw_ip6nopt; /* IPv6 options set/unset */ - u_char fw_tcpf,fw_tcpnf; /* TCP flags set/unset */ -#define IPV6_FW_ICMPTYPES_DIM (32 / (sizeof(unsigned) * 8)) - /* ICMP types bitmap */ - unsigned fw_icmp6types[IPV6_FW_ICMPTYPES_DIM]; - long timestamp; /* timestamp (tv_sec) of last match */ - /* Incoming and outgoing interfaces */ - union ip6_fw_if fw_in_if, fw_out_if; - union { - u_short fu_divert_port; /* Divert/tee port (options IP6DIVERT) */ - u_short fu_skipto_rule; /* SKIPTO command rule number */ - u_short fu_reject_code; /* REJECT response code */ - } fw_un; - u_char fw_prot; /* IPv6 protocol */ - u_char fw_nports; /* N'of src ports and # of dst ports */ - /* in ports array (dst ports follow */ - /* src ports; max of 10 ports in all; */ - /* count of 0 means match all ports) */ -}; - -#define IPV6_FW_GETNSRCP(rule) ((rule)->fw_nports & 0x0f) -#define IPV6_FW_SETNSRCP(rule, n) do { \ - (rule)->fw_nports &= ~0x0f; \ - (rule)->fw_nports |= (n); \ - } while (0) -#define IPV6_FW_GETNDSTP(rule) ((rule)->fw_nports >> 4) -#define IPV6_FW_SETNDSTP(rule, n) do { \ - (rule)->fw_nports &= ~0xf0; \ - (rule)->fw_nports |= (n) << 4;\ - } while (0) - -#define fw_divert_port fw_un.fu_divert_port -#define fw_skipto_rule fw_un.fu_skipto_rule -#define fw_reject_code fw_un.fu_reject_code - -struct ip6_fw_chain { - LIST_ENTRY(ip6_fw_chain) chain; - struct ip6_fw *rule; -}; - -/* - * Values for "flags" field . - */ -#define IPV6_FW_F_IN 0x0001 /* Check inbound packets */ -#define IPV6_FW_F_OUT 0x0002 /* Check outbound packets */ -#define IPV6_FW_F_IIFACE 0x0004 /* Apply inbound interface test */ -#define IPV6_FW_F_OIFACE 0x0008 /* Apply outbound interface test */ - -#define IPV6_FW_F_COMMAND 0x0070 /* Mask for type of chain entry: */ -#define IPV6_FW_F_DENY 0x0000 /* This is a deny rule */ -#define IPV6_FW_F_REJECT 0x0010 /* Deny and send a response packet */ -#define IPV6_FW_F_ACCEPT 0x0020 /* This is an accept rule */ -#define IPV6_FW_F_COUNT 0x0030 /* This is a count rule */ -#define IPV6_FW_F_DIVERT 0x0040 /* This is a divert rule */ -#define IPV6_FW_F_TEE 0x0050 /* This is a tee rule */ -#define IPV6_FW_F_SKIPTO 0x0060 /* This is a skipto rule */ - -#define IPV6_FW_F_PRN 0x0080 /* Print if this rule matches */ - -#define IPV6_FW_F_SRNG 0x0100 /* The first two src ports are a min * - * and max range (stored in host byte * - * order). */ - -#define IPV6_FW_F_DRNG 0x0200 /* The first two dst ports are a min * - * and max range (stored in host byte * - * order). */ - -/* In interface by name/unit (not IP) */ -#define IPV6_FW_F_IIFNAME 0x0400 -/* Out interface by name/unit (not IP) */ -#define IPV6_FW_F_OIFNAME 0x0800 - -#define IPV6_FW_F_INVSRC 0x1000 /* Invert sense of src check */ -#define IPV6_FW_F_INVDST 0x2000 /* Invert sense of dst check */ - -#define IPV6_FW_F_FRAG 0x4000 /* Fragment */ - -#define IPV6_FW_F_ICMPBIT 0x8000 /* ICMP type bitmap is valid */ - -#define IPV6_FW_F_MASK 0xFFFF /* All possible flag bits mask */ - -/* - * For backwards compatibility with rules specifying "via iface" but - * not restricted to only "in" or "out" packets, we define this combination - * of bits to represent this configuration. - */ - -#define IF6_FW_F_VIAHACK (IPV6_FW_F_IN|IPV6_FW_F_OUT|IPV6_FW_F_IIFACE|\ - IPV6_FW_F_OIFACE) - -/* - * Definitions for REJECT response codes. - * Values less than 256 correspond to ICMP unreachable codes. - */ -#define IPV6_FW_REJECT_RST 0x0100 /* TCP packets: send RST */ - -/* - * Definitions for IPv6 option names. - */ -#define IPV6_FW_IP6OPT_HOPOPT 0x01 -#define IPV6_FW_IP6OPT_ROUTE 0x02 -#define IPV6_FW_IP6OPT_FRAG 0x04 -#define IPV6_FW_IP6OPT_ESP 0x08 -#define IPV6_FW_IP6OPT_AH 0x10 -#define IPV6_FW_IP6OPT_NONXT 0x20 -#define IPV6_FW_IP6OPT_OPTS 0x40 - -/* - * Definitions for TCP flags. - */ -#define IPV6_FW_TCPF_FIN TH_FIN -#define IPV6_FW_TCPF_SYN TH_SYN -#define IPV6_FW_TCPF_RST TH_RST -#define IPV6_FW_TCPF_PSH TH_PUSH -#define IPV6_FW_TCPF_ACK TH_ACK -#define IPV6_FW_TCPF_URG TH_URG -#define IPV6_FW_TCPF_ESTAB 0x40 - -/* - * Main firewall chains definitions and global var's definitions. - */ -#ifdef _KERNEL - -/* - * Function definitions. - */ -void ip6_fw_init(void); - -/* Firewall hooks */ -struct ip6_hdr; -typedef int ip6_fw_chk_t __P((struct ip6_hdr**, struct ifnet*, - u_short *, struct mbuf**)); -typedef int ip6_fw_ctl_t __P((int, struct mbuf**)); -extern ip6_fw_chk_t *ip6_fw_chk_ptr; -extern ip6_fw_ctl_t *ip6_fw_ctl_ptr; - -#endif /* _KERNEL */ - -#endif /* _IP6_FW_H */ diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c index 105b4bc..42930bd 100644 --- a/sys/netinet6/ip6_input.c +++ b/sys/netinet6/ip6_input.c @@ -64,6 +64,8 @@ * @(#)ip_input.c 8.2 (Berkeley) 1/4/94 */ +#include "opt_ipsec.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/malloc.h> @@ -209,7 +211,8 @@ ip6_init2(dummy) } /* cheat */ -SYSINIT(netinet6init2, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, ip6_init2, NULL); +/* This must be after route_init(), which is now SI_ORDER_THIRD */ +SYSINIT(netinet6init2, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE, ip6_init2, NULL); /* * IP6 input interrupt handling. Just pass the packet to ip6_input. diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c index c5876f9..12f0f53 100644 --- a/sys/netinet6/ip6_output.c +++ b/sys/netinet6/ip6_output.c @@ -64,7 +64,7 @@ * @(#)ip_output.c 8.3 (Berkeley) 1/21/94 */ -#include "opt_key.h" +#include "opt_ipsec.h" #include <sys/param.h> #include <sys/malloc.h> @@ -92,13 +92,11 @@ #include <netinet6/ipsec.h> #include <netinet6/ipsec6.h> #include <netkey/key.h> -#ifdef KEY_DEBUG +#ifdef IPSEC_DEBUG #include <netkey/key_debug.h> #else -#define DPRINTF(lev,arg) -#define DDO(lev, stmt) -#define DP(x, y, z) -#endif /* KEY_DEBUG */ +#define KEYDEBUG(lev,arg) +#endif #endif /* IPSEC */ #include "loop.h" @@ -166,8 +164,11 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp) struct secpolicy *sp = NULL; /* for AH processing. stupid to have "socket" variable in IP layer... */ - so = (struct socket *)m->m_pkthdr.rcvif; - m->m_pkthdr.rcvif = NULL; + if ((flags & IPV6_SOCKINMRCVIF) != 0) { + so = (struct socket *)m->m_pkthdr.rcvif; + m->m_pkthdr.rcvif = NULL; + } else + so = NULL; ip6 = mtod(m, struct ip6_hdr *); #endif /* IPSEC */ @@ -1321,9 +1322,11 @@ ip6_ctloutput(so, sopt) caddr_t req = NULL; struct mbuf *m; - if (error = soopt_getm(sopt, &m)) /* XXX */ + if ((error = soopt_getm(sopt, &m)) + != 0) /* XXX */ break; - if (error = soopt_mcopyin(sopt, m)) /* XXX */ + if ((error = soopt_mcopyin(sopt, m)) + != 0) /* XXX */ break; if (m != 0) req = mtod(m, caddr_t); @@ -1345,9 +1348,11 @@ ip6_ctloutput(so, sopt) if (ip6_fw_ctl_ptr == NULL) return EINVAL; - if (error = soopt_getm(sopt, &m)) /* XXX */ + if ((error = soopt_getm(sopt, &m)) + != 0) /* XXX */ break; - if (error = soopt_mcopyin(sopt, m)) /* XXX */ + if ((error = soopt_mcopyin(sopt, m)) + != 0) /* XXX */ break; error = (*ip6_fw_ctl_ptr)(optname, mp); m = *mp; diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h index 21df4f7..2e70bcb 100644 --- a/sys/netinet6/ip6_var.h +++ b/sys/netinet6/ip6_var.h @@ -173,6 +173,9 @@ struct ip6stat { /* flags passed to ip6_output as last parameter */ #define IPV6_DADOUTPUT 0x01 /* DAD */ #define IPV6_FORWARDING 0x02 /* most of IPv6 header exists */ +#define IPV6_SOCKINMRCVIF 0x100 /* IPSEC hack; + * socket pointer in sending + * packet's m_pkthdr.rcvif */ extern struct ip6stat ip6stat; /* statistics */ extern u_int32_t ip6_id; /* fragment identifier */ diff --git a/sys/netinet6/ipsec.c b/sys/netinet6/ipsec.c new file mode 100644 index 0000000..9047ea3 --- /dev/null +++ b/sys/netinet6/ipsec.c @@ -0,0 +1,3061 @@ +/* + * 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$ + */ + +/* + * IPsec controller part. + */ + +#include "opt_inet.h" +#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 <sys/syslog.h> +#include <sys/sysctl.h> +#include <sys/proc.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_pcb.h> +#include <netinet/ip_var.h> +#include <netinet/in_var.h> +#include <netinet/udp.h> +#include <netinet/udp_var.h> +#include <netinet/ip_ecn.h> +#ifdef INET6 +#include <netinet6/ip6_ecn.h> +#endif + +#ifdef INET6 +#include <netinet6/ip6.h> +#include <netinet6/in6_pcb.h> +#include <netinet6/ip6_var.h> +#include <netinet6/icmp6.h> +#endif /*INET6*/ + +#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 <netkey/key.h> +#include <netkey/key_var.h> +#include <netkey/keydb.h> +#ifdef IPSEC_DEBUG +#include <netkey/key_debug.h> +#else +#define KEYDEBUG(lev,arg) +#endif + +struct ipsecstat ipsecstat; +int ip4_inbound_call_ike = 0; +int ip4_ah_cleartos = 1; +int ip4_ah_offsetmask = 0; /* maybe IP_DF? */ +int ip4_ipsec_dfbit = 0; /* DF bit on encap. 0: clear 1: set 2: copy */ +int ip4_esp_trans_deflev = IPSEC_LEVEL_USE; +int ip4_esp_net_deflev = IPSEC_LEVEL_USE; +int ip4_ah_trans_deflev = IPSEC_LEVEL_USE; +int ip4_ah_net_deflev = IPSEC_LEVEL_USE; +struct secpolicy ip4_def_policy; +int ip4_ipsec_ecn = 0; /* ECN ignore(-1)/forbidden(0)/allowed(1) */ + +/* net.inet.ipsec */ +SYSCTL_STRUCT(_net_inet_ipsec, IPSECCTL_STATS, + stats, CTLFLAG_RD, &ipsecstat, ipsecstat, ""); +SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_POLICY, + def_policy, CTLFLAG_RW, &ip4_def_policy.policy, 0, ""); +SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_ESP_TRANSLEV, esp_trans_deflev, + CTLFLAG_RW, &ip4_esp_trans_deflev, 0, ""); +SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_ESP_NETLEV, esp_net_deflev, + CTLFLAG_RW, &ip4_esp_net_deflev, 0, ""); +SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_AH_TRANSLEV, ah_trans_deflev, + CTLFLAG_RW, &ip4_ah_trans_deflev, 0, ""); +SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_AH_NETLEV, ah_net_deflev, + CTLFLAG_RW, &ip4_ah_net_deflev, 0, ""); +SYSCTL_INT(_net_inet_ipsec, IPSECCTL_INBOUND_CALL_IKE, + inbound_call_ike, CTLFLAG_RW, &ip4_inbound_call_ike, 0, ""); +SYSCTL_INT(_net_inet_ipsec, IPSECCTL_AH_CLEARTOS, + ah_cleartos, CTLFLAG_RW, &ip4_ah_cleartos, 0, ""); +SYSCTL_INT(_net_inet_ipsec, IPSECCTL_AH_OFFSETMASK, + ah_offsetmask, CTLFLAG_RW, &ip4_ah_offsetmask, 0, ""); +SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DFBIT, + dfbit, CTLFLAG_RW, &ip4_ipsec_dfbit, 0, ""); +SYSCTL_INT(_net_inet_ipsec, IPSECCTL_ECN, + ecn, CTLFLAG_RW, &ip4_ipsec_ecn, 0, ""); + +#ifdef INET6 +struct ipsecstat ipsec6stat; +int ip6_inbound_call_ike = 0; +int ip6_esp_trans_deflev = IPSEC_LEVEL_USE; +int ip6_esp_net_deflev = IPSEC_LEVEL_USE; +int ip6_ah_trans_deflev = IPSEC_LEVEL_USE; +int ip6_ah_net_deflev = IPSEC_LEVEL_USE; +struct secpolicy ip6_def_policy; +int ip6_ipsec_ecn = 0; /* ECN ignore(-1)/forbidden(0)/allowed(1) */ + +/* net.inet6.ipsec6 */ +SYSCTL_STRUCT(_net_inet6_ipsec6, IPSECCTL_STATS, + stats, CTLFLAG_RD, &ipsec6stat, ipsecstat, ""); +SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_POLICY, + def_policy, CTLFLAG_RW, &ip6_def_policy.policy, 0, ""); +SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_ESP_TRANSLEV, esp_trans_deflev, + CTLFLAG_RW, &ip6_esp_trans_deflev, 0, ""); +SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_ESP_NETLEV, esp_net_deflev, + CTLFLAG_RW, &ip6_esp_net_deflev, 0, ""); +SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_AH_TRANSLEV, ah_trans_deflev, + CTLFLAG_RW, &ip6_ah_trans_deflev, 0, ""); +SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_AH_NETLEV, ah_net_deflev, + CTLFLAG_RW, &ip6_ah_net_deflev, 0, ""); +SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_INBOUND_CALL_IKE, + inbound_call_ike, CTLFLAG_RW, &ip6_inbound_call_ike, 0, ""); +SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_ECN, + ecn, CTLFLAG_RW, &ip6_ipsec_ecn, 0, ""); +#endif /* INET6 */ + +static int ipsec_setspidx_mbuf + __P((struct secpolicyindex *, u_int, u_int, struct mbuf *)); +static void ipsec4_setspidx_inpcb __P((struct mbuf *, struct inpcb *pcb)); +static void ipsec4_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *)); +#ifdef INET6 +static u_int16_t ipsec6_get_ulp __P((struct mbuf *m)); +static void ipsec6_setspidx_in6pcb __P((struct mbuf *, struct in6pcb *pcb)); +static void ipsec6_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *)); +#endif +static struct secpolicy *ipsec_deepcopy_policy __P((struct secpolicy *src)); +static int ipsec_set_policy __P((struct secpolicy **pcb_sp, + int optname, caddr_t request, int priv)); +static int ipsec_get_policy __P((struct secpolicy *pcb_sp, struct mbuf **mp)); +static void vshiftl __P((unsigned char *, int, int)); +static int ipsec_in_reject __P((struct secpolicy *, struct mbuf *)); +static size_t ipsec_hdrsiz __P((struct secpolicy *)); +static struct mbuf *ipsec4_splithdr __P((struct mbuf *)); +#ifdef INET6 +static struct mbuf *ipsec6_splithdr __P((struct mbuf *)); +#endif +static int ipsec4_encapsulate __P((struct mbuf *, struct secasvar *)); +#ifdef INET6 +static int ipsec6_encapsulate __P((struct mbuf *, struct secasvar *)); +#endif + +#define KMALLOC(p, t, n) \ + ((p) = (t) malloc((unsigned long)(n), M_SECA, M_NOWAIT)) +#define KFREE(p) \ + free((caddr_t)(p), M_SECA); + +/* + * For OUTBOUND packet having a socket. Searching SPD for packet, + * and return a pointer to SP. + * OUT: NULL: no apropreate SP found, the following value is set to error. + * 0 : bypass + * EACCES : discard packet. + * ENOENT : ipsec_acquire() in progress, maybe. + * others : error occured. + * others: a pointer to SP + * + * NOTE: IPv6 mapped adddress concern is implemented here. + */ +struct secpolicy * +ipsec4_getpolicybysock(m, dir, so, error) + struct mbuf *m; + u_int dir; + struct socket *so; + int *error; +{ + struct inpcbpolicy *pcbsp = NULL; + struct secpolicy *currsp = NULL; /* policy on socket */ + struct secpolicy *kernsp = NULL; /* policy on kernel */ + + /* sanity check */ + if (m == NULL || so == NULL || error == NULL) + panic("ipsec4_getpolicybysock: NULL pointer was passed.\n"); + + switch (so->so_proto->pr_domain->dom_family) { + case AF_INET: + /* set spidx in pcb */ + ipsec4_setspidx_inpcb(m, sotoinpcb(so)); + pcbsp = sotoinpcb(so)->inp_sp; + break; +#ifdef INET6 + case AF_INET6: + /* set spidx in pcb */ + ipsec6_setspidx_in6pcb(m, sotoin6pcb(so)); + pcbsp = sotoin6pcb(so)->in6p_sp; + break; +#endif + default: + panic("ipsec4_getpolicybysock: unsupported address family\n"); + } + + /* sanity check */ + if (pcbsp == NULL) + panic("ipsec4_getpolicybysock: pcbsp is NULL.\n"); + + KEYDEBUG(KEYDEBUG_IPSEC_DATA, + printf("send: priv=%d ", pcbsp->priv); + if (so->so_cred) + printf("cr_uid=%d\n", so->so_cred->cr_uid); + ); + switch (dir) { + case IPSEC_DIR_INBOUND: + currsp = pcbsp->sp_in; + break; + case IPSEC_DIR_OUTBOUND: + currsp = pcbsp->sp_out; + break; + default: + panic("ipsec4_getpolicybysock: illegal direction.\n"); + } + + /* sanity check */ + if (currsp == NULL) + panic("ipsec4_getpolicybysock: currsp is NULL.\n"); + + /* when privilieged socket */ + if (pcbsp->priv) { + switch (currsp->policy) { + case IPSEC_POLICY_BYPASS: + currsp->refcnt++; + *error = 0; + return currsp; + + case IPSEC_POLICY_ENTRUST: + /* look for a policy in SPD */ + kernsp = key_allocsp(&currsp->spidx, dir); + + /* SP found */ + if (kernsp != NULL) { + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP ipsec4_getpolicybysock called " + "to allocate SP:%p\n", kernsp)); + *error = 0; + return kernsp; + } + + /* no SP found */ + if (ip4_def_policy.policy != IPSEC_POLICY_DISCARD + && ip4_def_policy.policy != IPSEC_POLICY_NONE) { + printf("fixed system default policy:%d->%d\n", + ip4_def_policy.policy, + IPSEC_POLICY_NONE); + ip4_def_policy.policy = IPSEC_POLICY_NONE; + } + ip4_def_policy.refcnt++; + *error = 0; + return &ip4_def_policy; + + case IPSEC_POLICY_IPSEC: + currsp->refcnt++; + *error = 0; + return currsp; + + default: + printf("ipsec4_getpolicybysock: " + "Invalid policy for PCB %d\n", + currsp->policy); + *error = EINVAL; + return NULL; + } + /* NOTREACHED */ + } + + /* when non-privilieged socket */ + /* look for a policy in SPD */ + kernsp = key_allocsp(&currsp->spidx, dir); + + /* SP found */ + if (kernsp != NULL) { + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP ipsec4_getpolicybysock called " + "to allocate SP:%p\n", kernsp)); + *error = 0; + return kernsp; + } + + /* no SP found */ + switch (currsp->policy) { + case IPSEC_POLICY_BYPASS: + printf("ipsec4_getpolicybysock: " + "Illegal policy for non-priviliged defined %d\n", + currsp->policy); + *error = EINVAL; + return NULL; + + case IPSEC_POLICY_ENTRUST: + if (ip4_def_policy.policy != IPSEC_POLICY_DISCARD + && ip4_def_policy.policy != IPSEC_POLICY_NONE) { + printf("fixed system default policy:%d->%d\n", + ip4_def_policy.policy, + IPSEC_POLICY_NONE); + ip4_def_policy.policy = IPSEC_POLICY_NONE; + } + ip4_def_policy.refcnt++; + *error = 0; + return &ip4_def_policy; + + case IPSEC_POLICY_IPSEC: + currsp->refcnt++; + *error = 0; + return currsp; + + default: + printf("ipsec4_getpolicybysock: " + "Invalid policy for PCB %d\n", + currsp->policy); + *error = EINVAL; + return NULL; + } + /* NOTREACHED */ +} + +/* + * For FORWADING packet or OUTBOUND without a socket. Searching SPD for packet, + * and return a pointer to SP. + * OUT: positive: a pointer to the entry for security policy leaf matched. + * NULL: no apropreate SP found, the following value is set to error. + * 0 : bypass + * EACCES : discard packet. + * ENOENT : ipsec_acquire() in progress, maybe. + * others : error occured. + */ +struct secpolicy * +ipsec4_getpolicybyaddr(m, dir, flag, error) + struct mbuf *m; + u_int dir; + int flag; + int *error; +{ + struct secpolicy *sp = NULL; + + /* sanity check */ + if (m == NULL || error == NULL) + panic("ipsec4_getpolicybyaddr: NULL pointer was passed.\n"); + + { + struct secpolicyindex spidx; + + bzero(&spidx, sizeof(spidx)); + + /* make a index to look for a policy */ + if ((flag & IP_FORWARDING) == IP_FORWARDING) { + /* Case: IP forwarding */ + *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET, m); + } else { + /* Case: ICMP echo reply */ + *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET, m); + } + + if (*error != 0) + return NULL; + + sp = key_allocsp(&spidx, dir); + } + + /* SP found */ + if (sp != NULL) { + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP ipsec4_getpolicybyaddr called " + "to allocate SP:%p\n", sp)); + *error = 0; + return sp; + } + + /* no SP found */ + if (ip4_def_policy.policy != IPSEC_POLICY_DISCARD + && ip4_def_policy.policy != IPSEC_POLICY_NONE) { + printf("fixed system default policy:%d->%d\n", + ip4_def_policy.policy, + IPSEC_POLICY_NONE); + ip4_def_policy.policy = IPSEC_POLICY_NONE; + } + ip4_def_policy.refcnt++; + *error = 0; + return &ip4_def_policy; +} + +#ifdef INET6 +/* + * For OUTBOUND packet having a socket. Searching SPD for packet, + * and return a pointer to SP. + * OUT: NULL: no apropreate SP found, the following value is set to error. + * 0 : bypass + * EACCES : discard packet. + * ENOENT : ipsec_acquire() in progress, maybe. + * others : error occured. + * others: a pointer to SP + */ +struct secpolicy * +ipsec6_getpolicybysock(m, dir, so, error) + struct mbuf *m; + u_int dir; + struct socket *so; + int *error; +{ + struct inpcbpolicy *pcbsp = NULL; + struct secpolicy *currsp = NULL; /* policy on socket */ + struct secpolicy *kernsp = NULL; /* policy on kernel */ + + /* sanity check */ + if (m == NULL || so == NULL || error == NULL) + panic("ipsec6_getpolicybysock: NULL pointer was passed.\n"); + + /* set spidx in pcb */ + ipsec6_setspidx_in6pcb(m, sotoin6pcb(so)); + + pcbsp = sotoin6pcb(so)->in6p_sp; + + /* sanity check */ + if (pcbsp == NULL) + panic("ipsec6_getpolicybysock: pcbsp is NULL.\n"); + + switch (dir) { + case IPSEC_DIR_INBOUND: + currsp = pcbsp->sp_in; + break; + case IPSEC_DIR_OUTBOUND: + currsp = pcbsp->sp_out; + break; + default: + panic("ipsec6_getpolicybysock: illegal direction.\n"); + } + + /* sanity check */ + if (currsp == NULL) + panic("ipsec6_getpolicybysock: currsp is NULL.\n"); + + /* when privilieged socket */ + if (pcbsp->priv) { + switch (currsp->policy) { + case IPSEC_POLICY_BYPASS: + currsp->refcnt++; + *error = 0; + return currsp; + + case IPSEC_POLICY_ENTRUST: + /* look for a policy in SPD */ + kernsp = key_allocsp(&currsp->spidx, dir); + + /* SP found */ + if (kernsp != NULL) { + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP ipsec6_getpolicybysock called " + "to allocate SP:%p\n", kernsp)); + *error = 0; + return kernsp; + } + + /* no SP found */ + if (ip6_def_policy.policy != IPSEC_POLICY_DISCARD + && ip6_def_policy.policy != IPSEC_POLICY_NONE) { + printf("fixed system default policy:%d->%d\n", + ip6_def_policy.policy, + IPSEC_POLICY_NONE); + ip6_def_policy.policy = IPSEC_POLICY_NONE; + } + ip6_def_policy.refcnt++; + *error = 0; + return &ip6_def_policy; + + case IPSEC_POLICY_IPSEC: + currsp->refcnt++; + *error = 0; + return currsp; + + default: + printf("ipsec6_getpolicybysock: " + "Invalid policy for PCB %d\n", + currsp->policy); + *error = EINVAL; + return NULL; + } + /* NOTREACHED */ + } + + /* when non-privilieged socket */ + /* look for a policy in SPD */ + kernsp = key_allocsp(&currsp->spidx, dir); + + /* SP found */ + if (kernsp != NULL) { + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP ipsec6_getpolicybysock called " + "to allocate SP:%p\n", kernsp)); + *error = 0; + return kernsp; + } + + /* no SP found */ + switch (currsp->policy) { + case IPSEC_POLICY_BYPASS: + printf("ipsec6_getpolicybysock: " + "Illegal policy for non-priviliged defined %d\n", + currsp->policy); + *error = EINVAL; + return NULL; + + case IPSEC_POLICY_ENTRUST: + if (ip6_def_policy.policy != IPSEC_POLICY_DISCARD + && ip6_def_policy.policy != IPSEC_POLICY_NONE) { + printf("fixed system default policy:%d->%d\n", + ip6_def_policy.policy, + IPSEC_POLICY_NONE); + ip6_def_policy.policy = IPSEC_POLICY_NONE; + } + ip6_def_policy.refcnt++; + *error = 0; + return &ip6_def_policy; + + case IPSEC_POLICY_IPSEC: + currsp->refcnt++; + *error = 0; + return currsp; + + default: + printf("ipsec6_policybysock: " + "Invalid policy for PCB %d\n", + currsp->policy); + *error = EINVAL; + return NULL; + } + /* NOTREACHED */ +} + +/* + * For FORWADING packet or OUTBOUND without a socket. Searching SPD for packet, + * and return a pointer to SP. + * `flag' means that packet is to be forwarded whether or not. + * flag = 1: forwad + * OUT: positive: a pointer to the entry for security policy leaf matched. + * NULL: no apropreate SP found, the following value is set to error. + * 0 : bypass + * EACCES : discard packet. + * ENOENT : ipsec_acquire() in progress, maybe. + * others : error occured. + */ +#ifndef IP_FORWARDING +#define IP_FORWARDING 1 +#endif + +struct secpolicy * +ipsec6_getpolicybyaddr(m, dir, flag, error) + struct mbuf *m; + u_int dir; + int flag; + int *error; +{ + struct secpolicy *sp = NULL; + + /* sanity check */ + if (m == NULL || error == NULL) + panic("ipsec6_getpolicybyaddr: NULL pointer was passed.\n"); + + { + struct secpolicyindex spidx; + + bzero(&spidx, sizeof(spidx)); + + /* make a index to look for a policy */ + if ((flag & IP_FORWARDING) == IP_FORWARDING) { + /* Case: IP forwarding */ + *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET6, m); + } else { + /* Case: ICMP echo reply */ + *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET6, m); + } + + if (*error != 0) + return NULL; + + sp = key_allocsp(&spidx, dir); + } + + /* SP found */ + if (sp != NULL) { + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP ipsec6_getpolicybyaddr called " + "to allocate SP:%p\n", sp)); + *error = 0; + return sp; + } + + /* no SP found */ + if (ip6_def_policy.policy != IPSEC_POLICY_DISCARD + && ip6_def_policy.policy != IPSEC_POLICY_NONE) { + printf("fixed system default policy:%d->%d\n", + ip6_def_policy.policy, + IPSEC_POLICY_NONE); + ip6_def_policy.policy = IPSEC_POLICY_NONE; + } + ip6_def_policy.refcnt++; + *error = 0; + return &ip6_def_policy; +} +#endif /* INET6 */ + +/* + * set IP address into spidx from mbuf. + * When Forwarding packet and ICMP echo reply, this function is used. + * + * IN: get the followings from mbuf. + * protocol family, src, dst, next protocol + * OUT: + * 0: success. + * other: failure, and set errno. + */ +int +ipsec_setspidx_mbuf(spidx, dir, family, m) + struct secpolicyindex *spidx; + u_int dir, family; + struct mbuf *m; +{ + /* sanity check */ + if (spidx == NULL || m == NULL) + panic("ipsec_setspidx_mbuf: NULL pointer was passed.\n"); + + KEYDEBUG(KEYDEBUG_IPSEC_DUMP, + printf("ipsec_setspidx_mbuf: begin\n"); kdebug_mbuf(m)); + + /* initialize */ + bzero(spidx, sizeof(*spidx)); + + spidx->dir = dir; + spidx->src.__ss_len = spidx->dst.__ss_len = _SALENBYAF(family); + spidx->src.__ss_family = spidx->dst.__ss_family = family; + spidx->prefs = spidx->prefd = _INALENBYAF(family) << 3; + + { + /* sanity check for packet length. */ + struct mbuf *n; + int tlen; + + tlen = 0; + for (n = m; n; n = n->m_next) + tlen += n->m_len; + if (m->m_pkthdr.len != tlen) { + KEYDEBUG(KEYDEBUG_IPSEC_DUMP, + printf("ipsec_setspidx_mbuf: " + "total of m_len(%d) != pkthdr.len(%d), " + "ignored.\n", + tlen, m->m_pkthdr.len)); + goto bad; + } + } + + switch (family) { + case AF_INET: + { + struct ip *ip; + struct ip ipbuf; + + /* sanity check 1 for minimum ip header length */ + if (m->m_pkthdr.len < sizeof(struct ip)) { + KEYDEBUG(KEYDEBUG_IPSEC_DUMP, + printf("ipsec_setspidx_mbuf: " + "pkthdr.len(%d) < sizeof(struct ip), " + "ignored.\n", + m->m_pkthdr.len)); + goto bad; + } + + /* + * get IPv4 header packet. usually the mbuf is contiguous + * and we need no copies. + */ + if (m->m_len >= sizeof(*ip)) + ip = mtod(m, struct ip *); + else { + m_copydata(m, 0, sizeof(ipbuf), (caddr_t)&ipbuf); + ip = &ipbuf; + } + + /* some more checks on IPv4 header. */ + bcopy(&ip->ip_src, _INADDRBYSA(&spidx->src), + sizeof(ip->ip_src)); + bcopy(&ip->ip_dst, _INADDRBYSA(&spidx->dst), + sizeof(ip->ip_dst)); + + spidx->ul_proto = ip->ip_p; + _INPORTBYSA(&spidx->src) = IPSEC_PORT_ANY; + _INPORTBYSA(&spidx->dst) = IPSEC_PORT_ANY; + break; + } + +#ifdef INET6 + case AF_INET6: + { + struct ip6_hdr *ip6_hdr; + struct ip6_hdr ip6buf; + + /* sanity check 1 for minimum ip header length */ + if (m->m_pkthdr.len < sizeof(struct ip6_hdr)) { + KEYDEBUG(KEYDEBUG_IPSEC_DUMP, + printf("ipsec_setspidx_mbuf: " + "pkthdr.len(%d) < sizeof(struct ip6_hdr), " + "ignored.\n", + m->m_pkthdr.len)); + goto bad; + } + + /* + * get IPv6 header packet. usually the mbuf is contiguous + * and we need no copies. + */ + if (m->m_len >= sizeof(*ip6_hdr)) + ip6_hdr = mtod(m, struct ip6_hdr *); + else { + m_copydata(m, 0, sizeof(ip6buf), (caddr_t)&ip6buf); + ip6_hdr = &ip6buf; + } + + /* some more checks on IPv4 header. */ + if ((ip6_hdr->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { + KEYDEBUG(KEYDEBUG_IPSEC_DUMP, + printf("ipsec_setspidx_mbuf: " + "wrong ip version on packet " + "(expected IPv6), ignored.\n")); + goto bad; + } + + bcopy(&ip6_hdr->ip6_src, _INADDRBYSA(&spidx->src), + sizeof(ip6_hdr->ip6_src)); + bcopy(&ip6_hdr->ip6_dst, _INADDRBYSA(&spidx->dst), + sizeof(ip6_hdr->ip6_dst)); + + spidx->ul_proto = ipsec6_get_ulp(m); + _INPORTBYSA(&spidx->src) = IPSEC_PORT_ANY; + _INPORTBYSA(&spidx->dst) = IPSEC_PORT_ANY; + break; + } +#endif /* INET6 */ + default: + panic("ipsec_secsecidx: no supported family passed.\n"); + } + + KEYDEBUG(KEYDEBUG_IPSEC_DUMP, + printf("ipsec_setspidx_mbuf: end\n"); + kdebug_secpolicyindex(spidx)); + + return 0; + + bad: + /* XXX initialize */ + bzero(spidx, sizeof(*spidx)); + return EINVAL; +} + +#ifdef INET6 +/* + * Get the number of upper layer protocol. + * Assumed all extension headers are in single mbuf. + */ +static u_int16_t +ipsec6_get_ulp(m) + struct mbuf *m; +{ + struct ip6_hdr *ip6; + struct ip6_ext *ip6e; + int off, nxt; + int len; + + /* sanity check */ + if (m == NULL) + panic("ipsec6_get_ulp: NULL pointer was passed.\n"); + + KEYDEBUG(KEYDEBUG_IPSEC_DUMP, + printf("ipsec6_get_ulp:\n"); kdebug_mbuf(m)); + + ip6 = mtod(m, struct ip6_hdr *); + nxt = ip6->ip6_nxt; + off = sizeof(struct ip6_hdr); + len = m->m_len; + + while (off < len) { + ip6e = (struct ip6_ext *)((caddr_t) ip6 + off); + if (m->m_len < off + sizeof(*ip6e)) { + printf("ipsec6_get_ulp: all exthdr are not " + "in single mbuf.\n"); + return IPSEC_PORT_ANY; + } + + switch(nxt) { + case IPPROTO_TCP: + case IPPROTO_UDP: + case IPPROTO_ICMPV6: + return nxt; + case IPPROTO_FRAGMENT: + off += sizeof(struct ip6_frag); + break; + case IPPROTO_AH: + off += (ip6e->ip6e_len + 2) << 2; + break; + default: + switch (nxt) { + case IPPROTO_HOPOPTS: + case IPPROTO_ROUTING: + case IPPROTO_ESP: + case IPPROTO_NONE: + case IPPROTO_DSTOPTS: + break; + default: + return nxt; /* XXX */ + } + off += (ip6e->ip6e_len + 1) << 3; + break; + } + nxt = ip6e->ip6e_nxt; + } + + return IPSEC_PORT_ANY; +} +#endif + +static void +ipsec4_setspidx_inpcb(m, pcb) + struct mbuf *m; + struct inpcb *pcb; +{ + struct secpolicyindex *spidx; + + /* sanity check */ + if (pcb == NULL) + panic("ipsec4_setspidx_inpcb: no PCB found.\n"); + if (pcb->inp_sp == NULL) + panic("ipsec4_setspidx_inpcb: no inp_sp found.\n"); + if (pcb->inp_sp->sp_out ==NULL || pcb->inp_sp->sp_in == NULL) + panic("ipsec4_setspidx_inpcb: no sp_in/out found.\n"); + + bzero(&pcb->inp_sp->sp_in->spidx, sizeof(*spidx)); + bzero(&pcb->inp_sp->sp_out->spidx, sizeof(*spidx)); + + spidx = &pcb->inp_sp->sp_in->spidx; + spidx->dir = IPSEC_DIR_INBOUND; + spidx->src.__ss_len = spidx->dst.__ss_len = _SALENBYAF(AF_INET); + spidx->src.__ss_family = spidx->dst.__ss_family = AF_INET; + spidx->prefs = _INALENBYAF(AF_INET) << 3; + spidx->prefd = _INALENBYAF(AF_INET) << 3; + spidx->ul_proto = pcb->inp_socket->so_proto->pr_protocol; + _INPORTBYSA(&spidx->src) = pcb->inp_fport; + _INPORTBYSA(&spidx->dst) = pcb->inp_lport; + ipsec4_setspidx_ipaddr(m, spidx); + + spidx = &pcb->inp_sp->sp_out->spidx; + spidx->dir = IPSEC_DIR_OUTBOUND; + spidx->src.__ss_len = spidx->dst.__ss_len = _SALENBYAF(AF_INET); + spidx->src.__ss_family = spidx->dst.__ss_family = AF_INET; + spidx->prefs = _INALENBYAF(AF_INET) << 3; + spidx->prefd = _INALENBYAF(AF_INET) << 3; + spidx->ul_proto = pcb->inp_socket->so_proto->pr_protocol; + _INPORTBYSA(&spidx->src) = pcb->inp_lport; + _INPORTBYSA(&spidx->dst) = pcb->inp_fport; + ipsec4_setspidx_ipaddr(m, spidx); + + return; +} + +static void +ipsec4_setspidx_ipaddr(m, spidx) + struct mbuf *m; + struct secpolicyindex *spidx; +{ + struct ip *ip = NULL; + + /* sanity check 1 for minimum ip header length */ + if (m == NULL) + panic("ipsec4_setspidx_in6pcb: m == 0 passed.\n"); + + if (m->m_pkthdr.len < sizeof(struct ip)) { + KEYDEBUG(KEYDEBUG_IPSEC_DUMP, + printf("ipsec4_setspidx_ipaddr: " + "pkthdr.len(%d) < sizeof(struct ip), " + "ignored.\n", + m->m_pkthdr.len)); + return; + } + + if (m->m_len >= sizeof(*ip)) + ip = mtod(m, struct ip *); + else { + struct ip ipbuf; + + m_copydata(m, 0, sizeof(ipbuf), (caddr_t)&ipbuf); + ip = &ipbuf; + } + + bcopy(&ip->ip_src, _INADDRBYSA(&spidx->src), sizeof(ip->ip_src)); + bcopy(&ip->ip_dst, _INADDRBYSA(&spidx->dst), sizeof(ip->ip_dst)); + + return; +} + +#ifdef INET6 +static void +ipsec6_setspidx_in6pcb(m, pcb) + struct mbuf *m; + struct in6pcb *pcb; +{ + struct secpolicyindex *spidx; + + /* sanity check */ + if (pcb == NULL) + panic("ipsec6_setspidx_in6pcb: no PCB found.\n"); + if (pcb->in6p_sp == NULL) + panic("ipsec6_setspidx_in6pcb: no in6p_sp found.\n"); + if (pcb->in6p_sp->sp_out ==NULL || pcb->in6p_sp->sp_in == NULL) + panic("ipsec6_setspidx_in6pcb: no sp_in/out found.\n"); + + bzero(&pcb->in6p_sp->sp_in->spidx, sizeof(*spidx)); + bzero(&pcb->in6p_sp->sp_out->spidx, sizeof(*spidx)); + + spidx = &pcb->in6p_sp->sp_in->spidx; + spidx->dir = IPSEC_DIR_INBOUND; + spidx->src.__ss_len = spidx->dst.__ss_len = _SALENBYAF(AF_INET6); + spidx->src.__ss_family = spidx->dst.__ss_family = AF_INET6; + spidx->prefs = _INALENBYAF(AF_INET6) << 3; + spidx->prefd = _INALENBYAF(AF_INET6) << 3; + spidx->ul_proto = pcb->in6p_socket->so_proto->pr_protocol; + _INPORTBYSA(&spidx->src) = pcb->in6p_fport; + _INPORTBYSA(&spidx->dst) = pcb->in6p_lport; + ipsec6_setspidx_ipaddr(m, spidx); + + spidx = &pcb->in6p_sp->sp_out->spidx; + spidx->dir = IPSEC_DIR_OUTBOUND; + spidx->src.__ss_len = spidx->dst.__ss_len = _SALENBYAF(AF_INET6); + spidx->src.__ss_family = spidx->dst.__ss_family = AF_INET6; + spidx->prefs = _INALENBYAF(AF_INET6) << 3; + spidx->prefd = _INALENBYAF(AF_INET6) << 3; + spidx->ul_proto = pcb->in6p_socket->so_proto->pr_protocol; + _INPORTBYSA(&spidx->src) = pcb->in6p_lport; + _INPORTBYSA(&spidx->dst) = pcb->in6p_fport; + ipsec6_setspidx_ipaddr(m, spidx); + + return; +} + +static void +ipsec6_setspidx_ipaddr(m, spidx) + struct mbuf *m; + struct secpolicyindex *spidx; +{ + struct ip6_hdr *ip6_hdr = NULL; + + /* sanity check 1 for minimum ip header length */ + if (m == NULL) + panic("ipsec6_setspidx_in6pcb: m == 0 passed.\n"); + + if (m->m_pkthdr.len < sizeof(struct ip6_hdr)) { + KEYDEBUG(KEYDEBUG_IPSEC_DUMP, + printf("ipsec6_setspidx_ipaddr: " + "pkthdr.len(%d) < sizeof(struct ip6_hdr), " + "ignored.\n", + m->m_pkthdr.len)); + return; + } + + if (m->m_len >= sizeof(*ip6_hdr)) + ip6_hdr = mtod(m, struct ip6_hdr *); + else { + struct ip6_hdr ip6buf; + + m_copydata(m, 0, sizeof(ip6buf), (caddr_t)&ip6buf); + ip6_hdr = &ip6buf; + } + + if ((ip6_hdr->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { + KEYDEBUG(KEYDEBUG_IPSEC_DUMP, + printf("ipsec_setspidx_mbuf: " + "wrong ip version on packet " + "(expected IPv6), ignored.\n")); + return; + } + + bcopy(&ip6_hdr->ip6_src, _INADDRBYSA(&spidx->src), + sizeof(ip6_hdr->ip6_src)); + bcopy(&ip6_hdr->ip6_dst, _INADDRBYSA(&spidx->dst), + sizeof(ip6_hdr->ip6_dst)); + + return; +} +#endif + +/* initialize policy in PCB */ +int +ipsec_init_policy(so, pcb_sp) + struct socket *so; + struct inpcbpolicy **pcb_sp; +{ + struct inpcbpolicy *new; + + /* sanity check. */ + if (so == NULL || pcb_sp == NULL) + panic("ipsec_init_policy: NULL pointer was passed.\n"); + + KMALLOC(new, struct inpcbpolicy *, sizeof(*new)); + if (new == NULL) { + printf("ipsec_init_policy: No more memory.\n"); + return ENOBUFS; + } + bzero(new, sizeof(*new)); + + if (so->so_cred != 0 && so->so_cred->cr_uid == 0) + new->priv = 1; + else + new->priv = 0; + + KEYDEBUG(KEYDEBUG_IPSEC_DATA, + printf("init: priv=%d ", new->priv); + if (so->so_cred) + printf("cr_uid=%d\n", so->so_cred->cr_uid); + else + printf("so_cred is NULL\n"); + ); + + if ((new->sp_in = key_newsp()) == NULL) { + KFREE(new); + return ENOBUFS; + } + new->sp_in->state = IPSEC_SPSTATE_ALIVE; + new->sp_in->policy = IPSEC_POLICY_ENTRUST; + + if ((new->sp_out = key_newsp()) == NULL) { + key_freesp(new->sp_in); + KFREE(new); + return ENOBUFS; + } + new->sp_out->state = IPSEC_SPSTATE_ALIVE; + new->sp_out->policy = IPSEC_POLICY_ENTRUST; + + *pcb_sp = new; + + return 0; +} + +/* copy old ipsec policy into new */ +int +ipsec_copy_policy(old, new) + struct inpcbpolicy *old, *new; +{ + struct secpolicy *sp; + + sp = ipsec_deepcopy_policy(old->sp_in); + if (sp) { + key_freesp(new->sp_in); + new->sp_in = sp; + } else + return ENOBUFS; + + sp = ipsec_deepcopy_policy(old->sp_out); + if (sp) { + key_freesp(new->sp_out); + new->sp_out = sp; + } else + return ENOBUFS; + + new->priv = old->priv; + + return 0; +} + +/* deep-copy a policy in PCB */ +static struct secpolicy * +ipsec_deepcopy_policy(src) + struct secpolicy *src; +{ + struct ipsecrequest *newchain = NULL; + struct ipsecrequest *p; + struct ipsecrequest **q; + struct ipsecrequest *r; + struct secpolicy *dst; + + dst = key_newsp(); + if (src == NULL || dst == NULL) + return NULL; + + /* + * deep-copy IPsec request chain. This is required since struct + * ipsecrequest is not reference counted. + */ + q = &newchain; + for (p = src->req; p; p = p->next) { + KMALLOC(*q, struct ipsecrequest *, sizeof(struct ipsecrequest)); + if (*q == NULL) + goto fail; + bzero(*q, sizeof(**q)); + (*q)->next = NULL; + + (*q)->saidx.proto = p->saidx.proto; + (*q)->saidx.mode = p->saidx.mode; + (*q)->level = p->level; + + bcopy(&p->saidx.src, &(*q)->saidx.src, sizeof((*q)->saidx.src)); + bcopy(&p->saidx.dst, &(*q)->saidx.dst, sizeof((*q)->saidx.dst)); + + (*q)->sav = NULL; + (*q)->sp = dst; + + q = &((*q)->next); + } + + dst->req = newchain; + dst->state = src->state; + dst->policy = src->policy; + /* do not touch the refcnt fields */ + + return dst; + +fail: + for (p = newchain; p; p = r) { + r = p->next; + KFREE(p); + p = NULL; + } + return NULL; +} + +/* set policy and ipsec request if present. */ +static int +ipsec_set_policy(pcb_sp, optname, request, priv) + struct secpolicy **pcb_sp; + int optname; + caddr_t request; + int priv; +{ + struct sadb_x_policy *xpl = (struct sadb_x_policy *)request; + struct secpolicy *newsp = NULL; + + /* sanity check. */ + if (pcb_sp == NULL || *pcb_sp == NULL || xpl == NULL) + return EINVAL; + + KEYDEBUG(KEYDEBUG_IPSEC_DUMP, + printf("ipsec_set_policy: passed policy\n"); + kdebug_sadb_x_policy((struct sadb_ext *)xpl)); + + /* check policy type */ + /* ipsec_set_policy() accepts IPSEC, ENTRUST and BYPASS. */ + if (xpl->sadb_x_policy_type == IPSEC_POLICY_DISCARD + || xpl->sadb_x_policy_type == IPSEC_POLICY_NONE) + return EINVAL; + + /* check privileged socket */ + if (priv == 0 && xpl->sadb_x_policy_type == IPSEC_POLICY_BYPASS) + return EACCES; + + /* allocation new SP entry */ + if ((newsp = key_msg2sp(xpl)) == NULL) + return EINVAL; /* maybe ENOBUFS */ + + newsp->state = IPSEC_SPSTATE_ALIVE; + + /* clear old SP and set new SP */ + key_freesp(*pcb_sp); + *pcb_sp = newsp; + KEYDEBUG(KEYDEBUG_IPSEC_DUMP, + printf("ipsec_set_policy: new policy\n"); + kdebug_secpolicy(newsp)); + + return 0; +} + +static int +ipsec_get_policy(pcb_sp, mp) + struct secpolicy *pcb_sp; + struct mbuf **mp; +{ + struct sadb_x_policy *xpl; + + /* sanity check. */ + if (pcb_sp == NULL || mp == NULL) + return EINVAL; + + if ((xpl = key_sp2msg(pcb_sp)) == NULL) { + printf("ipsec_get_policy: No more memory.\n"); + return ENOBUFS; + } + + *mp = m_get(M_WAIT, MT_DATA); + (*mp)->m_len = PFKEY_EXTLEN(xpl); + m_copyback((*mp), 0, PFKEY_EXTLEN(xpl), (caddr_t)xpl); + KEYDEBUG(KEYDEBUG_IPSEC_DUMP, + printf("ipsec_get_policy:\n"); + kdebug_mbuf(*mp)); + + KFREE(xpl); + + return 0; +} + +int +ipsec4_set_policy(inp, optname, request, priv) + struct inpcb *inp; + int optname; + caddr_t request; + int priv; +{ + struct sadb_x_policy *xpl = (struct sadb_x_policy *)request; + struct secpolicy **pcb_sp; + + /* sanity check. */ + if (inp == NULL || request == NULL) + return EINVAL; + + /* select direction */ + switch (xpl->sadb_x_policy_dir) { + case IPSEC_DIR_INBOUND: + pcb_sp = &inp->inp_sp->sp_in; + break; + case IPSEC_DIR_OUTBOUND: + pcb_sp = &inp->inp_sp->sp_out; + break; + default: + printf("ipsec4_set_policy: invalid direction=%u\n", + xpl->sadb_x_policy_dir); + return EINVAL; + } + + return ipsec_set_policy(pcb_sp, optname, request, priv); +} + +int +ipsec4_get_policy(inp, request, mp) + struct inpcb *inp; + caddr_t request; + struct mbuf **mp; +{ + struct sadb_x_policy *xpl = (struct sadb_x_policy *)request; + struct secpolicy *pcb_sp; + + /* sanity check. */ + if (inp == NULL || request == NULL || mp == NULL) + return EINVAL; + + /* select direction */ + switch (xpl->sadb_x_policy_dir) { + case IPSEC_DIR_INBOUND: + pcb_sp = inp->inp_sp->sp_in; + break; + case IPSEC_DIR_OUTBOUND: + pcb_sp = inp->inp_sp->sp_out; + break; + default: + printf("ipsec6_set_policy: invalid direction=%u\n", + xpl->sadb_x_policy_dir); + return EINVAL; + } + + return ipsec_get_policy(pcb_sp, mp); +} + +/* delete policy in PCB */ +int +ipsec4_delete_pcbpolicy(inp) + struct inpcb *inp; +{ + /* sanity check. */ + if (inp == NULL) + panic("ipsec4_delete_pcbpolicy: NULL pointer was passed.\n"); + + if (inp->inp_sp->sp_in != NULL) { + key_freesp(inp->inp_sp->sp_in); + inp->inp_sp->sp_in = NULL; + } + + if (inp->inp_sp->sp_out != NULL) { + key_freesp(inp->inp_sp->sp_out); + inp->inp_sp->sp_out = NULL; + } + + KFREE(inp->inp_sp); + inp->inp_sp = NULL; + + return 0; +} + +#ifdef INET6 +int +ipsec6_set_policy(in6p, optname, request, priv) + struct in6pcb *in6p; + int optname; + caddr_t request; + int priv; +{ + struct sadb_x_policy *xpl = (struct sadb_x_policy *)request; + struct secpolicy **pcb_sp; + + /* sanity check. */ + if (in6p == NULL || request == NULL) + return EINVAL; + + /* select direction */ + switch (xpl->sadb_x_policy_dir) { + case IPSEC_DIR_INBOUND: + pcb_sp = &in6p->in6p_sp->sp_in; + break; + case IPSEC_DIR_OUTBOUND: + pcb_sp = &in6p->in6p_sp->sp_out; + break; + default: + printf("ipsec6_set_policy: invalid direction=%u\n", + xpl->sadb_x_policy_dir); + return EINVAL; + } + + return ipsec_set_policy(pcb_sp, optname, request, priv); +} + +int +ipsec6_get_policy(in6p, request, mp) + struct in6pcb *in6p; + caddr_t request; + struct mbuf **mp; +{ + struct sadb_x_policy *xpl = (struct sadb_x_policy *)request; + struct secpolicy *pcb_sp; + + /* sanity check. */ + if (in6p == NULL || request == NULL || mp == NULL) + return EINVAL; + + /* select direction */ + switch (xpl->sadb_x_policy_dir) { + case IPSEC_DIR_INBOUND: + pcb_sp = in6p->in6p_sp->sp_in; + break; + case IPSEC_DIR_OUTBOUND: + pcb_sp = in6p->in6p_sp->sp_out; + break; + default: + printf("ipsec6_set_policy: invalid direction=%u\n", + xpl->sadb_x_policy_dir); + return EINVAL; + } + + return ipsec_get_policy(pcb_sp, mp); +} + +int +ipsec6_delete_pcbpolicy(in6p) + struct in6pcb *in6p; +{ + /* sanity check. */ + if (in6p == NULL) + panic("ipsec6_delete_pcbpolicy: NULL pointer was passed.\n"); + + if (in6p->in6p_sp->sp_in != NULL) { + key_freesp(in6p->in6p_sp->sp_in); + in6p->in6p_sp->sp_in = NULL; + } + + if (in6p->in6p_sp->sp_out != NULL) { + key_freesp(in6p->in6p_sp->sp_out); + in6p->in6p_sp->sp_out = NULL; + } + + KFREE(in6p->in6p_sp); + in6p->in6p_sp = NULL; + + return 0; +} +#endif + +/* + * return current level. + * IPSEC_LEVEL_USE or IPSEC_LEVEL_REQUIRE are always returned. + */ +u_int +ipsec_get_reqlevel(isr) + struct ipsecrequest *isr; +{ + u_int level = 0; + u_int esp_trans_deflev, esp_net_deflev, ah_trans_deflev, ah_net_deflev; + + /* sanity check */ + if (isr == NULL || isr->sp == NULL) + panic("ipsec_get_reqlevel: NULL pointer is passed.\n"); + if (isr->sp->spidx.src.__ss_family != isr->sp->spidx.dst.__ss_family) + panic("ipsec_get_reqlevel: family mismatched.\n"); + +#define IPSEC_CHECK_DEFAULT(lev) \ + (((lev) != IPSEC_LEVEL_USE && (lev) != IPSEC_LEVEL_REQUIRE) \ + ? (printf("fixed system default level " #lev ":%d->%d\n", \ + (lev), IPSEC_LEVEL_USE), \ + (lev) = IPSEC_LEVEL_USE) : (lev)) + + /* set default level */ + switch (isr->sp->spidx.src.__ss_family) { +#ifdef INET + case AF_INET: + esp_trans_deflev = IPSEC_CHECK_DEFAULT(ip4_esp_trans_deflev); + esp_net_deflev = IPSEC_CHECK_DEFAULT(ip4_esp_net_deflev); + ah_trans_deflev = IPSEC_CHECK_DEFAULT(ip4_ah_trans_deflev); + ah_net_deflev = IPSEC_CHECK_DEFAULT(ip4_ah_net_deflev); + break; +#endif +#ifdef INET6 + case AF_INET6: + esp_trans_deflev = IPSEC_CHECK_DEFAULT(ip6_esp_trans_deflev); + esp_net_deflev = IPSEC_CHECK_DEFAULT(ip6_esp_net_deflev); + ah_trans_deflev = IPSEC_CHECK_DEFAULT(ip6_ah_trans_deflev); + ah_net_deflev = IPSEC_CHECK_DEFAULT(ip6_ah_net_deflev); + break; +#endif /* INET6 */ + default: + panic("key_get_reqlevel: Unknown family. %d\n", + isr->sp->spidx.src.__ss_family); + } + +#undef IPSEC_CHECK_DEFAULT(lev) + + /* set level */ + switch (isr->level) { + case IPSEC_LEVEL_DEFAULT: + switch (isr->saidx.proto) { + case IPPROTO_ESP: + if (isr->saidx.mode == IPSEC_MODE_TUNNEL) + level = esp_net_deflev; + else + level = esp_trans_deflev; + break; + case IPPROTO_AH: + if (isr->saidx.mode == IPSEC_MODE_TUNNEL) + level = ah_net_deflev; + else + level = ah_trans_deflev; + default: + panic("ipsec_get_reqlevel: " + "Illegal protocol defined %u\n", + isr->saidx.proto); + } + break; + + case IPSEC_LEVEL_USE: + case IPSEC_LEVEL_REQUIRE: + level = isr->level; + break; + + default: + panic("ipsec_get_reqlevel: Illegal IPsec level %u\n", + isr->level); + } + + return level; +} + +/* + * Check AH/ESP integrity. + * OUT: + * 0: valid + * 1: invalid + */ +static int +ipsec_in_reject(sp, m) + struct secpolicy *sp; + struct mbuf *m; +{ + struct ipsecrequest *isr; + u_int level; + int need_auth, need_conf, need_icv; + + KEYDEBUG(KEYDEBUG_IPSEC_DATA, + printf("ipsec_in_reject: using SP\n"); + kdebug_secpolicy(sp)); + + /* check policy */ + switch (sp->policy) { + case IPSEC_POLICY_DISCARD: + return 1; + case IPSEC_POLICY_BYPASS: + case IPSEC_POLICY_NONE: + return 0; + + case IPSEC_POLICY_IPSEC: + break; + + case IPSEC_POLICY_ENTRUST: + default: + panic("ipsec_hdrsiz: Invalid policy found. %d\n", sp->policy); + } + + need_auth = 0; + need_conf = 0; + need_icv = 0; + + for (isr = sp->req; isr != NULL; isr = isr->next) { + + /* get current level */ + level = ipsec_get_reqlevel(isr); + + switch (isr->saidx.proto) { + case IPPROTO_ESP: + if (level == IPSEC_LEVEL_REQUIRE) { + need_conf++; + + if (isr->sav != NULL + && isr->sav->flags == SADB_X_EXT_NONE + && isr->sav->alg_auth != SADB_AALG_NONE) + need_icv++; + } + break; + case IPPROTO_AH: + if (level == IPSEC_LEVEL_REQUIRE) { + need_auth++; + need_icv++; + } + break; + } + } + + KEYDEBUG(KEYDEBUG_IPSEC_DUMP, + printf("ipsec_in_reject: auth:%d conf:%d icv:%d m_flags:%x\n", + need_auth, need_conf, need_icv, m->m_flags)); + + if ((need_conf && !(m->m_flags & M_DECRYPTED)) + || (!need_auth && need_icv && !(m->m_flags & M_AUTHIPDGM)) + || (need_auth && !(m->m_flags & M_AUTHIPHDR))) + return 1; + + return 0; +} + +/* + * Check AH/ESP integrity. + * This function is called from tcp_input(), udp_input(), + * and {ah,esp}4_input for tunnel mode + */ +int +ipsec4_in_reject_so(m, so) + struct mbuf *m; + struct socket *so; +{ + struct secpolicy *sp = NULL; + int error; + int result; + + /* sanity check */ + if (m == NULL) + return 0; /* XXX should be panic ? */ + + /* get SP for this packet. + * When we are called from ip_forward(), we call + * ipsec4_getpolicybyaddr() with IP_FORWARDING flag. + */ + if (so == NULL) + sp = ipsec4_getpolicybyaddr(m, IPSEC_DIR_INBOUND, IP_FORWARDING, &error); + else + sp = ipsec4_getpolicybysock(m, IPSEC_DIR_INBOUND, so, &error); + + if (sp == NULL) + return 0; /* XXX should be panic ? + * -> No, there may be error. */ + + result = ipsec_in_reject(sp, m); + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP ipsec4_in_reject_so call free SP:%p\n", sp)); + key_freesp(sp); + + return result; +} + +int +ipsec4_in_reject(m, inp) + struct mbuf *m; + struct inpcb *inp; +{ + if (inp == NULL) + return ipsec4_in_reject_so(m, NULL); + else { + if (inp->inp_socket) + return ipsec4_in_reject_so(m, inp->inp_socket); + else + panic("ipsec4_in_reject: invalid inpcb/socket"); + } +} + +#ifdef INET6 +/* + * Check AH/ESP integrity. + * This function is called from tcp6_input(), udp6_input(), + * and {ah,esp}6_input for tunnel mode + */ +int +ipsec6_in_reject_so(m, so) + struct mbuf *m; + struct socket *so; +{ + struct secpolicy *sp = NULL; + int error; + int result; + + /* sanity check */ + if (m == NULL) + return 0; /* XXX should be panic ? */ + + /* get SP for this packet. + * When we are called from ip_forward(), we call + * ipsec6_getpolicybyaddr() with IP_FORWARDING flag. + */ + if (so == NULL) + sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_INBOUND, IP_FORWARDING, &error); + else + sp = ipsec6_getpolicybysock(m, IPSEC_DIR_INBOUND, so, &error); + + if (sp == NULL) + return 0; /* XXX should be panic ? */ + + result = ipsec_in_reject(sp, m); + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP ipsec6_in_reject_so call free SP:%p\n", sp)); + key_freesp(sp); + + return result; +} + +int +ipsec6_in_reject(m, in6p) + struct mbuf *m; + struct in6pcb *in6p; +{ + if (in6p == NULL) + return ipsec6_in_reject_so(m, NULL); + else { + if (in6p->in6p_socket) + return ipsec6_in_reject_so(m, in6p->in6p_socket); + else + panic("ipsec6_in_reject: invalid in6p/socket"); + } +} +#endif + +/* + * compute the byte size to be occupied by IPsec header. + * in case it is tunneled, it includes the size of outer IP header. + * NOTE: SP passed is free in this function. + */ +static size_t +ipsec_hdrsiz(sp) + struct secpolicy *sp; +{ + struct ipsecrequest *isr; + size_t siz, clen; + + KEYDEBUG(KEYDEBUG_IPSEC_DATA, + printf("ipsec_in_reject: using SP\n"); + kdebug_secpolicy(sp)); + + /* check policy */ + switch (sp->policy) { + case IPSEC_POLICY_DISCARD: + case IPSEC_POLICY_BYPASS: + case IPSEC_POLICY_NONE: + return 0; + + case IPSEC_POLICY_IPSEC: + break; + + case IPSEC_POLICY_ENTRUST: + default: + panic("ipsec_hdrsiz: Invalid policy found. %d\n", sp->policy); + } + + siz = 0; + + for (isr = sp->req; isr != NULL; isr = isr->next) { + + clen = 0; + + switch (isr->saidx.proto) { + case IPPROTO_ESP: +#ifdef IPSEC_ESP + clen = esp_hdrsiz(isr); +#else + clen = 0; /*XXX*/ +#endif + break; + case IPPROTO_AH: + clen = ah_hdrsiz(isr); + break; + } + + if (isr->saidx.mode == IPSEC_MODE_TUNNEL) { + switch (isr->saidx.dst.__ss_family) { + case AF_INET: + clen += sizeof(struct ip); + break; +#ifdef INET6 + case AF_INET6: + clen += sizeof(struct ip6_hdr); + break; +#endif + default: + printf("ipsec_hdrsiz: unknown AF %d " + "in IPsec tunnel SA\n", + isr->saidx.dst.__ss_family); + break; + } + } + siz += clen; + } + + return siz; +} + +/* This function is called from ip_forward() and ipsec4_hdrsize_tcp(). */ +size_t +ipsec4_hdrsiz(m, dir, inp) + struct mbuf *m; + u_int dir; + struct inpcb *inp; +{ + struct secpolicy *sp = NULL; + int error; + size_t size; + + /* sanity check */ + if (m == NULL) + return 0; /* XXX should be panic ? */ + if (inp != NULL && inp->inp_socket == NULL) + panic("ipsec4_hdrsize: why is socket NULL but there is PCB."); + + /* get SP for this packet. + * When we are called from ip_forward(), we call + * ipsec4_getpolicybyaddr() with IP_FORWARDING flag. + */ + if (inp == NULL) + sp = ipsec4_getpolicybyaddr(m, dir, IP_FORWARDING, &error); + else + sp = ipsec4_getpolicybysock(m, dir, inp->inp_socket, &error); + + if (sp == NULL) + return 0; /* XXX should be panic ? */ + + size = ipsec_hdrsiz(sp); + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP ipsec4_hdrsiz call free SP:%p\n", sp)); + KEYDEBUG(KEYDEBUG_IPSEC_DATA, + printf("ipsec4_hdrsiz: size:%lu.\n", (unsigned long)size)); + key_freesp(sp); + + return size; +} + +#ifdef INET6 +/* This function is called from ipsec6_hdrsize_tcp(), + * and maybe from ip6_forward.() + */ +size_t +ipsec6_hdrsiz(m, dir, in6p) + struct mbuf *m; + u_int dir; + struct in6pcb *in6p; +{ + struct secpolicy *sp = NULL; + int error; + size_t size; + + /* sanity check */ + if (m == NULL) + return 0; /* XXX shoud be panic ? */ + if (in6p != NULL && in6p->in6p_socket == NULL) + panic("ipsec6_hdrsize: why is socket NULL but there is PCB."); + + /* get SP for this packet */ + /* XXX Is it right to call with IP_FORWARDING. */ + if (in6p == NULL) + sp = ipsec6_getpolicybyaddr(m, dir, IP_FORWARDING, &error); + else + sp = ipsec6_getpolicybysock(m, dir, in6p->in6p_socket, &error); + + if (sp == NULL) + return 0; + size = ipsec_hdrsiz(sp); + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP ipsec6_hdrsiz call free SP:%p\n", sp)); + KEYDEBUG(KEYDEBUG_IPSEC_DATA, + printf("ipsec6_hdrsiz: size:%lu.\n", (unsigned long)size)); + key_freesp(sp); + + return size; +} +#endif /*INET6*/ + +#ifdef INET +/* + * encapsulate for ipsec tunnel. + * ip->ip_src must be fixed later on. + */ +static int +ipsec4_encapsulate(m, sav) + struct mbuf *m; + struct secasvar *sav; +{ + struct ip *oip; + struct ip *ip; + size_t hlen; + size_t plen; + + /* can't tunnel between different AFs */ + if (sav->sah->saidx.src.__ss_family != sav->sah->saidx.dst.__ss_family + || sav->sah->saidx.src.__ss_family != AF_INET) { + m_freem(m); + return EINVAL; + } + + ip = mtod(m, struct ip *); +#ifdef _IP_VHL + hlen = _IP_VHL_HL(ip->ip_vhl) << 2; +#else + hlen = ip->ip_hl << 2; +#endif + + /* generate header checksum */ + ip->ip_sum = 0; +#ifdef _IP_VHL + if (ip->ip_vhl == IP_VHL_BORING) + ip->ip_sum = in_cksum_hdr(ip); + else + ip->ip_sum = in_cksum(m, hlen); +#else + ip->ip_sum = in_cksum(m, hlen); +#endif + + plen = m->m_pkthdr.len; + + /* + * grow the mbuf to accomodate the new IPv4 header. + * NOTE: IPv4 options will never be copied. + */ + if (m->m_len != hlen) + panic("ipsec4_encapsulate: assumption failed (first mbuf length)"); + if (M_LEADINGSPACE(m->m_next) < hlen) { + struct mbuf *n; + MGET(n, M_DONTWAIT, MT_DATA); + if (!n) { + m_freem(m); + return ENOBUFS; + } + n->m_len = hlen; + n->m_next = m->m_next; + m->m_next = n; + m->m_pkthdr.len += hlen; + oip = mtod(n, struct ip *); + } else { + m->m_next->m_len += hlen; + m->m_next->m_data -= hlen; + m->m_pkthdr.len += hlen; + oip = mtod(m->m_next, struct ip *); + } + ip = mtod(m, struct ip *); + ovbcopy((caddr_t)ip, (caddr_t)oip, hlen); + m->m_len = sizeof(struct ip); + m->m_pkthdr.len -= (hlen - sizeof(struct ip)); + + /* construct new IPv4 header. see RFC 2401 5.1.2.1 */ + /* ECN consideration. */ + ip_ecn_ingress(ip4_ipsec_ecn, &ip->ip_tos, &oip->ip_tos); +#ifdef _IP_VHL + ip->ip_vhl = IP_MAKE_VHL(IPVERSION, sizeof(struct ip) >> 2); +#else + ip->ip_hl = sizeof(struct ip) >> 2; +#endif + ip->ip_off &= htons(~IP_OFFMASK); + ip->ip_off &= htons(~IP_MF); + switch (ip4_ipsec_dfbit) { + case 0: /*clear DF bit*/ + ip->ip_off &= htons(~IP_DF); + break; + case 1: /*set DF bit*/ + ip->ip_off |= htons(IP_DF); + break; + default: /*copy DF bit*/ + break; + } + ip->ip_p = IPPROTO_IPIP; + if (plen + sizeof(struct ip) < IP_MAXPACKET) + ip->ip_len = htons(plen + sizeof(struct ip)); + else { + printf("IPv4 ipsec: size exceeds limit: " + "leave ip_len as is (invalid packet)\n"); + } + ip->ip_id = htons(ip_id++); + bcopy(&((struct sockaddr_in *)&sav->sah->saidx.src)->sin_addr, + &ip->ip_src, sizeof(ip->ip_src)); + bcopy(&((struct sockaddr_in *)&sav->sah->saidx.dst)->sin_addr, + &ip->ip_dst, sizeof(ip->ip_dst)); + + /* XXX Should ip_src be updated later ? */ + + return 0; +} +#endif /*INET*/ + +#ifdef INET6 +static int +ipsec6_encapsulate(m, sav) + struct mbuf *m; + struct secasvar *sav; +{ + struct ip6_hdr *oip6; + struct ip6_hdr *ip6; + size_t plen; + + /* can't tunnel between different AFs */ + if (sav->sah->saidx.src.__ss_family != sav->sah->saidx.dst.__ss_family + || sav->sah->saidx.src.__ss_family != AF_INET6) { + m_freem(m); + return EINVAL; + } + + plen = m->m_pkthdr.len; + + /* + * grow the mbuf to accomodate the new IPv6 header. + */ + if (m->m_len != sizeof(struct ip6_hdr)) + panic("ipsec6_encapsulate: assumption failed (first mbuf length)"); + if (M_LEADINGSPACE(m->m_next) < sizeof(struct ip6_hdr)) { + struct mbuf *n; + MGET(n, M_DONTWAIT, MT_DATA); + if (!n) { + m_freem(m); + return ENOBUFS; + } + n->m_len = sizeof(struct ip6_hdr); + n->m_next = m->m_next; + m->m_next = n; + m->m_pkthdr.len += sizeof(struct ip6_hdr); + oip6 = mtod(n, struct ip6_hdr *); + } else { + m->m_next->m_len += sizeof(struct ip6_hdr); + m->m_next->m_data -= sizeof(struct ip6_hdr); + m->m_pkthdr.len += sizeof(struct ip6_hdr); + oip6 = mtod(m->m_next, struct ip6_hdr *); + } + ip6 = mtod(m, struct ip6_hdr *); + ovbcopy((caddr_t)ip6, (caddr_t)oip6, sizeof(struct ip6_hdr)); + + /* Fake link-local scope-class addresses */ + if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_src)) + oip6->ip6_src.s6_addr16[1] = 0; + if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_dst)) + oip6->ip6_dst.s6_addr16[1] = 0; + + /* construct new IPv6 header. see RFC 2401 5.1.2.2 */ + /* ECN consideration. */ + ip6_ecn_ingress(ip6_ipsec_ecn, &ip6->ip6_flow, &oip6->ip6_flow); + if (plen < IPV6_MAXPACKET - sizeof(struct ip6_hdr)) + ip6->ip6_plen = htons(plen); + else { + /* ip6->ip6_plen will be updated in ip6_output() */ + } + ip6->ip6_nxt = IPPROTO_IPV6; + bcopy(&((struct sockaddr_in6 *)&sav->sah->saidx.src)->sin6_addr, + &ip6->ip6_src, sizeof(ip6->ip6_src)); + bcopy(&((struct sockaddr_in6 *)&sav->sah->saidx.dst)->sin6_addr, + &ip6->ip6_dst, sizeof(ip6->ip6_dst)); + + /* XXX Should ip6_src be updated later ? */ + + return 0; +} +#endif /*INET6*/ + +/* + * Check the variable replay window. + * ipsec_chkreplay() performs replay check before ICV verification. + * ipsec_updatereplay() updates replay bitmap. This must be called after + * ICV verification (it also performs replay check, which is usually done + * beforehand). + * 0 (zero) is returned if packet disallowed, 1 if packet permitted. + * + * based on RFC 2401. + */ +int +ipsec_chkreplay(seq, sav) + u_int32_t seq; + struct secasvar *sav; +{ + const struct secreplay *replay; + u_int32_t diff; + int fr; + u_int32_t wsizeb; /* constant: bits of window size */ + int frlast; /* constant: last frame */ + + /* sanity check */ + if (sav == NULL) + printf("ipsec_chkreplay: NULL pointer was passed.\n"); + + replay = sav->replay; + + if (replay->wsize == 0) + return 1; /* no need to check replay. */ + + /* constant */ + frlast = replay->wsize - 1; + wsizeb = replay->wsize << 3; + + /* sequence number of 0 is invalid */ + if (seq == 0) + return 0; + + /* first time is always okay */ + if (replay->count == 0) + return 1; + + if (seq > replay->lastseq) { + /* larger sequences are okay */ + return 1; + } else { + /* seq is equal or less than lastseq. */ + diff = replay->lastseq - seq; + + /* over range to check, i.e. too old or wrapped */ + if (diff >= wsizeb) + return 0; + + fr = frlast - diff / 8; + + /* this packet already seen ? */ + if ((replay->bitmap)[fr] & (1 << (diff % 8))) + return 0; + + /* out of order but good */ + return 1; + } +} + +int +ipsec_updatereplay(seq, sav) + u_int32_t seq; + struct secasvar *sav; +{ + struct secreplay *replay; + u_int32_t diff; + int fr; + u_int32_t wsizeb; /* constant: bits of window size */ + int frlast; /* constant: last frame */ + + /* sanity check */ + if (sav == NULL) + printf("ipsec_chkreplay: NULL pointer was passed.\n"); + + replay = sav->replay; + + if (replay->wsize == 0) + goto ok; /* no need to check replay. */ + + /* constant */ + frlast = replay->wsize - 1; + wsizeb = replay->wsize << 3; + + /* sequence number of 0 is invalid */ + if (seq == 0) + return 0; + + /* first time */ + if (replay->count == 0) { + replay->lastseq = seq; + bzero(replay->bitmap, replay->wsize); + (replay->bitmap)[frlast] = 1; + goto ok; + } + + if (seq > replay->lastseq) { + /* seq is larger than lastseq. */ + diff = seq - replay->lastseq; + + /* new larger sequence number */ + if (diff < wsizeb) { + /* In window */ + /* set bit for this packet */ + vshiftl(replay->bitmap, diff, replay->wsize); + (replay->bitmap)[frlast] |= 1; + } else { + /* this packet has a "way larger" */ + bzero(replay->bitmap, replay->wsize); + (replay->bitmap)[frlast] = 1; + } + replay->lastseq = seq; + + /* larger is good */ + } else { + /* seq is equal or less than lastseq. */ + diff = replay->lastseq - seq; + + /* over range to check, i.e. too old or wrapped */ + if (diff >= wsizeb) + return 0; + + fr = frlast - diff / 8; + + /* this packet already seen ? */ + if ((replay->bitmap)[fr] & (1 << (diff % 8))) + return 0; + + /* mark as seen */ + (replay->bitmap)[fr] |= (1 << (diff % 8)); + + /* out of order but good */ + } + +ok: + if (replay->count == ~0 + && (sav->flags & SADB_X_EXT_CYCSEQ) == 0) { + return 1; /* don't increment, no more packets accepted */ + } + + replay->count++; + + return 1; +} + +/* + * shift variable length bunffer to left. + * IN: bitmap: pointer to the buffer + * nbit: the number of to shift. + * wsize: buffer size (bytes). + */ +static void +vshiftl(bitmap, nbit, wsize) + unsigned char *bitmap; + int nbit, wsize; +{ + int s, j, i; + unsigned char over; + + for (j = 0; j < nbit; j += 8) { + s = (nbit - j < 8) ? (nbit - j): 8; + bitmap[0] <<= s; + for (i = 1; i < wsize; i++) { + over = (bitmap[i] >> (8 - s)); + bitmap[i] <<= s; + bitmap[i-1] |= over; + } + } + + return; +} + +const char * +ipsec4_logpacketstr(ip, spi) + struct ip *ip; + u_int32_t spi; +{ + static char buf[256]; + char *p; + u_int8_t *s, *d; + + s = (u_int8_t *)(&ip->ip_src); + d = (u_int8_t *)(&ip->ip_dst); + + snprintf(buf, sizeof(buf), "packet(SPI=%u ", (u_int32_t)ntohl(spi)); + for (p = buf; p && *p; p++) + ; + snprintf(p, sizeof(buf) - (p - buf), "src=%d.%d.%d.%d", + s[0], s[1], s[2], s[3]); + for (/*nothing*/; p && *p; p++) + ; + snprintf(p, sizeof(buf) - (p - buf), " dst=%d.%d.%d.%d", + d[0], d[1], d[2], d[3]); + for (/*nothing*/; p && *p; p++) + ; + snprintf(p, sizeof(buf) - (p - buf), ")"); + + return buf; +} + +#ifdef INET6 +const char * +ipsec6_logpacketstr(ip6, spi) + struct ip6_hdr *ip6; + u_int32_t spi; +{ + static char buf[256]; + char *p; + + snprintf(buf, sizeof(buf), "packet(SPI=%u ", (u_int32_t)ntohl(spi)); + for (p = buf; p && *p; p++) + ; + snprintf(p, sizeof(buf) - (p - buf), "src=%s", + ip6_sprintf(&ip6->ip6_src)); + for (/*nothing*/; p && *p; p++) + ; + snprintf(p, sizeof(buf) - (p - buf), " dst=%s", + ip6_sprintf(&ip6->ip6_dst)); + for (/*nothing*/; p && *p; p++) + ; + snprintf(p, sizeof(buf) - (p - buf), ")"); + + return buf; +} +#endif /*INET6*/ + +const char * +ipsec_logsastr(sav) + struct secasvar *sav; +{ + static char buf[256]; + char *p; + struct secasindex *saidx = &sav->sah->saidx; + + /* validity check */ + if (sav->sah->saidx.src.__ss_family != sav->sah->saidx.dst.__ss_family) + panic("ipsec_logsastr: family mismatched.\n"); + + snprintf(buf, sizeof(buf), "SA(SPI=%u ", (u_int32_t)ntohl(sav->spi)); + for (p = buf; p && *p; p++) + ; + if (saidx->src.__ss_family == AF_INET) { + u_int8_t *s, *d; + s = (u_int8_t *)&((struct sockaddr_in *)&saidx->src)->sin_addr; + d = (u_int8_t *)&((struct sockaddr_in *)&saidx->dst)->sin_addr; + snprintf(p, sizeof(buf) - (p - buf), + "src=%d.%d.%d.%d dst=%d.%d.%d.%d", + s[0], s[1], s[2], s[3], d[0], d[1], d[2], d[3]); + } +#ifdef INET6 + else if (saidx->src.__ss_family == AF_INET6) { + snprintf(p, sizeof(buf) - (p - buf), + "src=%s", + ip6_sprintf(&((struct sockaddr_in6 *)&saidx->src)->sin6_addr)); + for (/*nothing*/; p && *p; p++) + ; + snprintf(p, sizeof(buf) - (p - buf), + " dst=%s", + ip6_sprintf(&((struct sockaddr_in6 *)&saidx->dst)->sin6_addr)); + } +#endif + for (/*nothing*/; p && *p; p++) + ; + snprintf(p, sizeof(buf) - (p - buf), ")"); + + return buf; +} + +void +ipsec_dumpmbuf(m) + struct mbuf *m; +{ + int totlen; + int i; + u_char *p; + + totlen = 0; + printf("---\n"); + while (m) { + p = mtod(m, u_char *); + for (i = 0; i < m->m_len; i++) { + printf("%02x ", p[i]); + totlen++; + if (totlen % 16 == 0) + printf("\n"); + } + m = m->m_next; + } + if (totlen % 16 != 0) + printf("\n"); + printf("---\n"); +} + +/* + * IPsec output logic for IPv4. + */ +int +ipsec4_output(state, sp, flags) + struct ipsec_output_state *state; + struct secpolicy *sp; + int flags; +{ + struct ip *ip = NULL; + struct ipsecrequest *isr = NULL; + int s; + int error; +#ifdef IPSEC_SRCSEL + struct in_ifaddr *ia; +#endif + struct sockaddr_in *dst4; + + if (!state) + panic("state == NULL in ipsec4_output"); + if (!state->m) + panic("state->m == NULL in ipsec4_output"); + if (!state->ro) + panic("state->ro == NULL in ipsec4_output"); + if (!state->dst) + panic("state->dst == NULL in ipsec4_output"); + + ip = mtod(state->m, struct ip *); + + KEYDEBUG(KEYDEBUG_IPSEC_DATA, + printf("ipsec4_output: applyed SP\n"); + kdebug_secpolicy(sp)); + + for (isr = sp->req; isr != NULL; isr = isr->next) { + + if ((error = key_checkrequest(isr)) != 0) { + /* + * IPsec processing is required, but no SA found. + * I assume that key_acquire() had been called + * to get/establish the SA. Here I discard + * this packet because it is responsibility for + * upper layer to retransmit the packet. + */ + ipsecstat.out_nosa++; + goto bad; + } + + /* validity check */ + if (isr->sav == NULL) { + switch (ipsec_get_reqlevel(isr)) { + case IPSEC_LEVEL_USE: + continue; + case IPSEC_LEVEL_REQUIRE: + /* must be not reached here. */ + panic("ipsec4_output: no SA found, but required."); + } + } + + if (isr->sav->state != SADB_SASTATE_MATURE + && isr->sav->state != SADB_SASTATE_DYING) { + /* If there is no valid SA, we give up to process. */ + ipsecstat.out_nosa++; + error = EINVAL; + goto bad; + } + + /* + * There may be the case that SA status will be changed when + * we are refering to one. So calling splsoftnet(). + */ + s = splnet(); + + if (isr->saidx.mode == IPSEC_MODE_TUNNEL) { + /* + * build IPsec tunnel. + */ + /* XXX should be processed with other familiy */ + if (isr->sav->sah->saidx.src.__ss_family != AF_INET) { + printf("ipsec4_output: family mismatched " + "between inner and outer spi=%u\n", + (u_int32_t)ntohl(isr->sav->spi)); + splx(s); + error = EAFNOSUPPORT; + goto bad; + } + + ip = mtod(state->m, struct ip *); + + state->m = ipsec4_splithdr(state->m); + if (!state->m) { + splx(s); + error = ENOMEM; + goto bad; + } + error = ipsec4_encapsulate(state->m, isr->sav); + splx(s); + if (error) { + state->m = NULL; + goto bad; + } + ip = mtod(state->m, struct ip *); + + state->ro = &isr->sav->sah->sa_route; + state->dst = (struct sockaddr *)&state->ro->ro_dst; + dst4 = (struct sockaddr_in *)state->dst; + if (state->ro->ro_rt + && ((state->ro->ro_rt->rt_flags & RTF_UP) == 0 + || dst4->sin_addr.s_addr != ip->ip_dst.s_addr)) { + RTFREE(state->ro->ro_rt); + bzero((caddr_t)state->ro, sizeof (*state->ro)); + } + if (state->ro->ro_rt == 0) { + dst4->sin_family = AF_INET; + dst4->sin_len = sizeof(*dst4); + dst4->sin_addr = ip->ip_dst; + rtalloc(state->ro); + } + if (state->ro->ro_rt == 0) { + ipstat.ips_noroute++; + error = EHOSTUNREACH; + goto bad; + } + +#ifdef IPSEC_SRCSEL + /* + * Which address in SA or in routing table should I + * select from ? But I had set from SA at + * ipsec4_encapsulate(). + */ + ia = (struct in_ifaddr *)(state->ro->ro_rt->rt_ifa); + if (state->ro->ro_rt->rt_flags & RTF_GATEWAY) { + state->dst = (struct sockaddr *)state->ro->ro_rt->rt_gateway; + dst4 = (struct sockaddr_in *)state->dst; + } + ip->ip_src = IA_SIN(ia)->sin_addr; +#endif + } else + splx(s); + + state->m = ipsec4_splithdr(state->m); + if (!state->m) { + error = ENOMEM; + goto bad; + } + switch (isr->saidx.proto) { + case IPPROTO_ESP: +#ifdef IPSEC_ESP + if ((error = esp4_output(state->m, isr)) != 0) { + state->m = NULL; + goto bad; + } + break; +#else + m_freem(state->m); + state->m = NULL; + error = EINVAL; + goto bad; +#endif + case IPPROTO_AH: + if ((error = ah4_output(state->m, isr)) != 0) { + state->m = NULL; + goto bad; + } + break; + default: + printf("ipsec4_output: unknown ipsec protocol %d\n", + isr->saidx.proto); + m_freem(state->m); + state->m = NULL; + error = EINVAL; + goto bad; + } + + if (state->m == 0) { + error = ENOMEM; + goto bad; + } + ip = mtod(state->m, struct ip *); + } + + return 0; + +bad: + m_freem(state->m); + state->m = NULL; + return error; +} + +#ifdef INET6 +/* + * IPsec output logic for IPv6, transport mode. + */ +int +ipsec6_output_trans(state, nexthdrp, mprev, sp, flags, tun) + struct ipsec_output_state *state; + u_char *nexthdrp; + struct mbuf *mprev; + struct secpolicy *sp; + int flags; + int *tun; +{ + struct ip6_hdr *ip6; + struct ipsecrequest *isr = NULL; + int error = 0; + int plen; + + if (!state) + panic("state == NULL in ipsec6_output"); + if (!state->m) + panic("state->m == NULL in ipsec6_output"); + if (!nexthdrp) + panic("nexthdrp == NULL in ipsec6_output"); + if (!mprev) + panic("mprev == NULL in ipsec6_output"); + if (!sp) + panic("sp == NULL in ipsec6_output"); + if (!tun) + panic("tun == NULL in ipsec6_output"); + + KEYDEBUG(KEYDEBUG_IPSEC_DATA, + printf("ipsec6_output_trans: applyed SP\n"); + kdebug_secpolicy(sp)); + + *tun = 0; + for (isr = sp->req; isr; isr = isr->next) { + if (isr->saidx.mode == IPSEC_MODE_TUNNEL) { + /* the rest will be handled by ipsec6_output_tunnel() */ + break; + } + + if (key_checkrequest(isr) == ENOENT) { + /* + * IPsec processing is required, but no SA found. + * I assume that key_acquire() had been called + * to get/establish the SA. Here I discard + * this packet because it is responsibility for + * upper layer to retransmit the packet. + */ + ipsec6stat.out_nosa++; + error = ENOENT; + goto bad; + } + + /* validity check */ + if (isr->sav == NULL) { + switch (ipsec_get_reqlevel(isr)) { + case IPSEC_LEVEL_USE: + continue; + case IPSEC_LEVEL_REQUIRE: + /* must be not reached here. */ + panic("ipsec6_output_trans: no SA found, but required."); + } + } + + if (isr->sav->state != SADB_SASTATE_MATURE + && isr->sav->state != SADB_SASTATE_DYING) { + /* If there is no valid SA, we give up to process. */ + ipsec6stat.out_nosa++; + error = EINVAL; + goto bad; + } + + switch (isr->saidx.proto) { + case IPPROTO_ESP: +#ifdef IPSEC_ESP + error = esp6_output(state->m, nexthdrp, mprev->m_next, isr); +#else + m_freem(state->m); + error = EINVAL; +#endif + break; + case IPPROTO_AH: + error = ah6_output(state->m, nexthdrp, mprev->m_next, isr); + break; + default: + printf("ipsec6_output_trans: unknown ipsec protocol %d\n", isr->saidx.proto); + m_freem(state->m); + error = EINVAL; + break; + } + if (error) { + state->m = NULL; + goto bad; + } + plen = state->m->m_pkthdr.len - sizeof(struct ip6_hdr); + if (plen > IPV6_MAXPACKET) { + printf("ipsec6_output: IPsec with IPv6 jumbogram is not supported\n"); + ipsec6stat.out_inval++; + error = EINVAL; /*XXX*/ + goto bad; + } + ip6 = mtod(state->m, struct ip6_hdr *); + ip6->ip6_plen = htons(plen); + } + + /* if we have more to go, we need a tunnel mode processing */ + if (isr != NULL) + *tun = 1; + + return 0; + +bad: + m_freem(state->m); + state->m = NULL; + return error; +} + +/* + * IPsec output logic for IPv6, tunnel mode. + */ +int +ipsec6_output_tunnel(state, sp, flags) + struct ipsec_output_state *state; + struct secpolicy *sp; + int flags; +{ + struct ip6_hdr *ip6; + struct ipsecrequest *isr = NULL; + int error = 0; + int plen; +#ifdef IPSEC_SRCSEL + struct in6_addr *ia6; +#endif + struct sockaddr_in6* dst6; + int s; + + if (!state) + panic("state == NULL in ipsec6_output"); + if (!state->m) + panic("state->m == NULL in ipsec6_output"); + if (!sp) + panic("sp == NULL in ipsec6_output"); + + KEYDEBUG(KEYDEBUG_IPSEC_DATA, + printf("ipsec6_output_tunnel: applyed SP\n"); + kdebug_secpolicy(sp)); + + /* + * transport mode ipsec (before the 1st tunnel mode) is already + * processed by ipsec6_output_trans(). + */ + for (isr = sp->req; isr; isr = isr->next) { + if (isr->saidx.mode == IPSEC_MODE_TUNNEL) + break; + } + + for (/*already initialized*/; isr; isr = isr->next) { + if (key_checkrequest(isr) == ENOENT) { + /* + * IPsec processing is required, but no SA found. + * I assume that key_acquire() had been called + * to get/establish the SA. Here I discard + * this packet because it is responsibility for + * upper layer to retransmit the packet. + */ + ipsec6stat.out_nosa++; + error = ENOENT; + goto bad; + } + + /* validity check */ + if (isr->sav == NULL) { + switch (ipsec_get_reqlevel(isr)) { + case IPSEC_LEVEL_USE: + continue; + case IPSEC_LEVEL_REQUIRE: + /* must be not reached here. */ + panic("ipsec6_output_tunnel: no SA found, but required."); + } + } + + if (isr->sav->state != SADB_SASTATE_MATURE + && isr->sav->state != SADB_SASTATE_DYING) { + /* If there is no valid SA, we give up to process. */ + ipsec6stat.out_nosa++; + error = EINVAL; + goto bad; + } + + /* + * There may be the case that SA status will be changed when + * we are refering to one. So calling splsoftnet(). + */ + s = splnet(); + + if (isr->saidx.mode == IPSEC_MODE_TUNNEL) { + /* + * build IPsec tunnel. + */ + /* XXX should be processed with other familiy */ + if (isr->sav->sah->saidx.src.__ss_family != AF_INET6) { + printf("ipsec4_output: family mismatched " + "between inner and outer, spi=%u\n", + (u_int32_t)ntohl(isr->sav->spi)); + printf("ipsec6_output_tunnel: family mismatched " + "between inner and outer, spi=%u\n", + (u_int32_t)ntohl(isr->sav->spi)); + splx(s); + error = EAFNOSUPPORT; + goto bad; + } + + ip6 = mtod(state->m, struct ip6_hdr *); + + state->m = ipsec6_splithdr(state->m); + if (!state->m) { + splx(s); + error = ENOMEM; + goto bad; + } + error = ipsec6_encapsulate(state->m, isr->sav); + splx(s); + if (error) { + state->m = 0; + goto bad; + } + ip6 = mtod(state->m, struct ip6_hdr *); + + state->ro = &isr->sav->sah->sa_route; + state->dst = (struct sockaddr *)&state->ro->ro_dst; + dst6 = (struct sockaddr_in6 *)state->dst; + if (state->ro->ro_rt + && ((state->ro->ro_rt->rt_flags & RTF_UP) == 0 + || !IN6_ARE_ADDR_EQUAL(&dst6->sin6_addr, &ip6->ip6_dst))) { + RTFREE(state->ro->ro_rt); + bzero((caddr_t)state->ro, sizeof (*state->ro)); + } + if (state->ro->ro_rt == 0) { + bzero(dst6, sizeof(*dst6)); + dst6->sin6_family = AF_INET6; + dst6->sin6_len = sizeof(*dst6); + dst6->sin6_addr = ip6->ip6_dst; + rtalloc(state->ro); + } + if (state->ro->ro_rt == 0) { + ip6stat.ip6s_noroute++; + error = EHOSTUNREACH; + goto bad; + } +#ifdef IPSEC_SRCSEL + /* + * Which address in SA or in routing table should I + * select from ? But I had set from SA at + * ipsec6_encapsulate(). + */ + ia6 = in6_selectsrc(dst6, NULL, NULL, + (struct route_in6 *)state->ro, + NULL, &error); + if (ia6 == NULL) + goto bad; + ip6->ip6_src = *ia6; +#endif + } else + splx(s); + + state->m = ipsec6_splithdr(state->m); + if (!state->m) { + error = ENOMEM; + goto bad; + } + ip6 = mtod(state->m, struct ip6_hdr *); + switch (isr->saidx.proto) { + case IPPROTO_ESP: +#ifdef IPSEC_ESP + error = esp6_output(state->m, &ip6->ip6_nxt, state->m->m_next, isr); +#else + m_freem(state->m); + error = EINVAL; +#endif + break; + case IPPROTO_AH: + error = ah6_output(state->m, &ip6->ip6_nxt, state->m->m_next, isr); + break; + default: + printf("ipsec6_output_tunnel: unknown ipsec protocol %d\n", isr->saidx.proto); + m_freem(state->m); + error = EINVAL; + break; + } + if (error) { + state->m = NULL; + goto bad; + } + plen = state->m->m_pkthdr.len - sizeof(struct ip6_hdr); + if (plen > IPV6_MAXPACKET) { + printf("ipsec6_output_tunnel: IPsec with IPv6 jumbogram is not supported\n"); + ipsec6stat.out_inval++; + error = EINVAL; /*XXX*/ + goto bad; + } + ip6 = mtod(state->m, struct ip6_hdr *); + ip6->ip6_plen = htons(plen); + } + + return 0; + +bad: + m_freem(state->m); + state->m = NULL; + return error; +} +#endif /*INET6*/ + +/* + * Chop IP header and option off from the payload. + */ +static struct mbuf * +ipsec4_splithdr(m) + struct mbuf *m; +{ + struct mbuf *mh; + struct ip *ip; + int hlen; + + if (m->m_len < sizeof(struct ip)) + panic("ipsec4_splithdr: first mbuf too short"); + ip = mtod(m, struct ip *); +#ifdef _IP_VHL + hlen = _IP_VHL_HL(ip->ip_vhl) << 2; +#else + hlen = ip->ip_hl << 2; +#endif + if (m->m_len > hlen) { + MGETHDR(mh, M_DONTWAIT, MT_HEADER); + if (!mh) { + m_freem(m); + return NULL; + } + M_COPY_PKTHDR(mh, m); + MH_ALIGN(mh, hlen); + m->m_flags &= ~M_PKTHDR; + m->m_len -= hlen; + m->m_data += hlen; + mh->m_next = m; + m = mh; + m->m_len = hlen; + bcopy((caddr_t)ip, mtod(m, caddr_t), hlen); + } else if (m->m_len < hlen) { + m = m_pullup(m, hlen); + if (!m) + return NULL; + } + return m; +} + +#ifdef INET6 +static struct mbuf * +ipsec6_splithdr(m) + struct mbuf *m; +{ + struct mbuf *mh; + struct ip6_hdr *ip6; + int hlen; + + if (m->m_len < sizeof(struct ip6_hdr)) + panic("ipsec6_splithdr: first mbuf too short"); + ip6 = mtod(m, struct ip6_hdr *); + hlen = sizeof(struct ip6_hdr); + if (m->m_len > hlen) { + MGETHDR(mh, M_DONTWAIT, MT_HEADER); + if (!mh) { + m_freem(m); + return NULL; + } + M_COPY_PKTHDR(mh, m); + MH_ALIGN(mh, hlen); + m->m_flags &= ~M_PKTHDR; + m->m_len -= hlen; + m->m_data += hlen; + mh->m_next = m; + m = mh; + m->m_len = hlen; + bcopy((caddr_t)ip6, mtod(m, caddr_t), hlen); + } else if (m->m_len < hlen) { + m = m_pullup(m, hlen); + if (!m) + return NULL; + } + return m; +} +#endif + +/* validate inbound IPsec tunnel packet. */ +int +ipsec4_tunnel_validate(ip, nxt0, sav) + struct ip *ip; + u_int nxt0; + struct secasvar *sav; +{ + u_int8_t nxt = nxt0 & 0xff; + struct sockaddr_in *sin; + int hlen; + + if (nxt != IPPROTO_IPV4) + return 0; +#ifdef _IP_VHL + hlen = _IP_VHL_HL(ip->ip_vhl) << 2; +#else + hlen = ip->ip_hl << 2; +#endif + if (hlen != sizeof(struct ip)) + return 0; + switch (sav->sah->saidx.dst.__ss_family) { + case AF_INET: + sin = (struct sockaddr_in *)&sav->sah->saidx.dst; + if (bcmp(&ip->ip_dst, &sin->sin_addr, sizeof(ip->ip_dst)) != 0) + return 0; + break; +#ifdef INET6 + case AF_INET6: + /* should be supported, but at this moment we don't. */ + /*FALLTHROUGH*/ +#endif + default: + return 0; + } + + return 1; +} + +#ifdef INET6 +/* validate inbound IPsec tunnel packet. */ +int +ipsec6_tunnel_validate(ip6, nxt0, sav) + struct ip6_hdr *ip6; + u_int nxt0; + struct secasvar *sav; +{ + u_int8_t nxt = nxt0 & 0xff; + struct sockaddr_in6 *sin6; + + if (nxt != IPPROTO_IPV6) + return 0; + switch (sav->sah->saidx.dst.__ss_family) { + case AF_INET6: + sin6 = ((struct sockaddr_in6 *)&sav->sah->saidx.dst); + if (!IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &sin6->sin6_addr)) + return 0; + break; + case AF_INET: + /* should be supported, but at this moment we don't. */ + /*FALLTHROUGH*/ + default: + return 0; + } + + return 1; +} +#endif + +/* + * Make a mbuf chain for encryption. + * If the original mbuf chain contains a mbuf with a cluster, + * allocate a new cluster and copy the data to the new cluster. + * XXX: this hack is inefficient, but is necessary to handle cases + * of TCP retransmission... + */ +struct mbuf * +ipsec_copypkt(m) + struct mbuf *m; +{ + struct mbuf *n, **mpp, *mnew; + + for (n = m, mpp = &m; n; n = n->m_next) { + if (n->m_flags & M_EXT) { + /* + * Make a copy only if there are more than one references + * to the cluster. + * XXX: is this approach effective? + */ + if ( + n->m_ext.ext_free || + mclrefcnt[mtocl(n->m_ext.ext_buf)] > 1 + ) + { + int remain, copied; + struct mbuf *mm; + + if (n->m_flags & M_PKTHDR) { + MGETHDR(mnew, M_DONTWAIT, MT_HEADER); + if (mnew == NULL) + goto fail; + mnew->m_pkthdr = n->m_pkthdr; + mnew->m_flags = n->m_flags & M_COPYFLAGS; + } + else { + MGET(mnew, M_DONTWAIT, MT_DATA); + if (mnew == NULL) + goto fail; + } + mnew->m_len = 0; + mm = mnew; + + /* + * Copy data. If we don't have enough space to + * store the whole data, allocate a cluster + * or additional mbufs. + * XXX: we don't use m_copyback(), since the + * function does not use clusters and thus is + * inefficient. + */ + remain = n->m_len; + copied = 0; + while(1) { + int len; + struct mbuf *mn; + + if (remain <= (mm->m_flags & M_PKTHDR ? MHLEN : MLEN)) + len = remain; + else { /* allocate a cluster */ + MCLGET(mm, M_DONTWAIT); + if (!(mm->m_flags & M_EXT)) { + m_free(mm); + goto fail; + } + len = remain < MCLBYTES ? + remain : MCLBYTES; + } + + bcopy(n->m_data + copied, mm->m_data, + len); + + copied += len; + remain -= len; + mm->m_len = len; + + if (remain <= 0) /* completed? */ + break; + + /* need another mbuf */ + MGETHDR(mn, M_DONTWAIT, MT_HEADER); + if (mn == NULL) + goto fail; + mm->m_next = mn; + mm = mn; + } + + /* adjust chain */ + mm->m_next = m_free(n); + n = mm; + *mpp = mnew; + mpp = &n->m_next; + + continue; + } + } + *mpp = n; + mpp = &n->m_next; + } + + return(m); + fail: + m_freem(m); + return(NULL); +} diff --git a/sys/netinet6/ipsec.h b/sys/netinet6/ipsec.h index 6fdcba1..ede791b 100644 --- a/sys/netinet6/ipsec.h +++ b/sys/netinet6/ipsec.h @@ -238,6 +238,11 @@ struct ipsecstat { } #ifdef KERNEL + +#ifdef SYSCTL_DECL +SYSCTL_DECL(_net_inet_ipsec); +#endif + struct ipsec_output_state { struct mbuf *m; struct route *ro; @@ -303,6 +308,7 @@ extern struct mbuf *ipsec_copypkt __P((struct mbuf *)); #endif /*KERNEL*/ #ifndef KERNEL + extern caddr_t ipsec_set_policy __P((char *policy, int buflen)); extern int ipsec_get_policylen __P((caddr_t buf)); extern char *ipsec_dump_policy __P((caddr_t buf, char *delimiter)); diff --git a/sys/netinet6/ipsec6.h b/sys/netinet6/ipsec6.h index 61896b3..c4e9924 100644 --- a/sys/netinet6/ipsec6.h +++ b/sys/netinet6/ipsec6.h @@ -38,6 +38,10 @@ #ifdef KERNEL +#ifdef SYSCTL_DECL +SYSCTL_DECL(_net_inet6_ipsec6); +#endif + extern struct ipsecstat ipsec6stat; extern struct secpolicy ip6_def_policy; extern int ip6_esp_trans_deflev; diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c index 634f61b..f09b38a 100644 --- a/sys/netinet6/mld6.c +++ b/sys/netinet6/mld6.c @@ -68,6 +68,8 @@ * @(#)igmp.c 8.1 (Berkeley) 7/19/93 */ +#include "opt_ipsec.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/mbuf.h> @@ -391,10 +393,6 @@ mld6_sendpkt(in6m, type, dst) return; } mh->m_next = md; - -#ifdef IPSEC - mh->m_pkthdr.rcvif = NULL; -#endif mh->m_pkthdr.len = sizeof(struct ip6_hdr) + sizeof(struct mld6_hdr); mh->m_len = sizeof(struct ip6_hdr); MH_ALIGN(mh, sizeof(struct ip6_hdr)); diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c index 840f074..9c16e1e 100644 --- a/sys/netinet6/nd6_nbr.c +++ b/sys/netinet6/nd6_nbr.c @@ -29,6 +29,8 @@ * $FreeBSD$ */ +#include "opt_ipsec.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/malloc.h> @@ -423,9 +425,6 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) nd_ns->nd_ns_cksum = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len); -#ifdef IPSEC - m->m_pkthdr.rcvif = NULL; -#endif /*IPSEC*/ ip6_output(m, NULL, NULL, dad ? IPV6_DADOUTPUT : 0, &im6o, &outif); if (outif) { icmp6_ifstat_inc(outif, ifs6_out_msg); diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c index 3131e41..9027663 100644 --- a/sys/netinet6/raw_ip6.c +++ b/sys/netinet6/raw_ip6.c @@ -64,6 +64,8 @@ * @(#)raw_ip.c 8.2 (Berkeley) 1/4/94 */ +#include "opt_ipsec.h" + #include <stddef.h> #include <sys/param.h> @@ -140,7 +142,7 @@ rip6_input(mp, offp, proto) init_sin6(&rip6src, m); /* general init */ LIST_FOREACH(in6p, &ripcb, inp_list) { - if ((in6p->in6p_vflag & INP_IPV6) == NULL) + if ((in6p->in6p_vflag & INP_IPV6) == 0) continue; if (in6p->in6p_ip6_nxt && in6p->in6p_ip6_nxt != proto) @@ -328,9 +330,10 @@ rip6_output(m, va_alist) if (in6p->in6p_route.ro_rt) oifp = ifindex2ifnet[in6p->in6p_route.ro_rt->rt_ifp->if_index]; } - - ip6->ip6_flow = in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK; - ip6->ip6_vfc = IPV6_VERSION; + ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) | + (in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK); + ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) | + (IPV6_VERSION & IPV6_VERSION_MASK); /* ip6_plen will be filled in ip6_output, so not fill it here. */ ip6->ip6_nxt = in6p->in6p_ip6_nxt; ip6->ip6_hlim = in6_selecthlim(in6p, oifp); @@ -370,8 +373,8 @@ rip6_output(m, va_alist) m->m_pkthdr.rcvif = (struct ifnet *)so; #endif /*IPSEC*/ - error = ip6_output(m, optp, &in6p->in6p_route, 0, in6p->in6p_moptions, - &oifp); + error = ip6_output(m, optp, &in6p->in6p_route, IPV6_SOCKINMRCVIF, + in6p->in6p_moptions, &oifp); if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { if (oifp) icmp6_ifoutstat_inc(oifp, type, code); @@ -446,11 +449,9 @@ rip6_attach(struct socket *so, int proto, struct proc *p) if (p && (error = suser(p)) != 0) return error; - if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { - error = soreserve(so, rip_sendspace, rip_recvspace); - if (error) - return error; - } + error = soreserve(so, rip_sendspace, rip_recvspace); + if (error) + return error; s = splnet(); error = in_pcballoc(so, &ripcbinfo, p); splx(s); diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c index 137c3ee..936bac9 100644 --- a/sys/netinet6/udp6_usrreq.c +++ b/sys/netinet6/udp6_usrreq.c @@ -63,6 +63,8 @@ * $FreeBSD$ */ +#include "opt_ipsec.h" + #include <sys/param.h> #include <sys/kernel.h> #include <sys/malloc.h> @@ -98,6 +100,7 @@ #ifdef IPSEC #include <netinet6/ipsec.h> +#include <netinet6/ipsec6.h> #endif /*IPSEC*/ #include "faith.h" @@ -251,11 +254,10 @@ udp6_input(mp, offp, proto) /* * Check AH/ESP integrity. */ - if (last != NULL && - ipsec6_in_reject_so(m, last->inp_socket)) { + if (ipsec6_in_reject_so(m, last->inp_socket)) ipsec6stat.in_polvio++; /* do not inject data into pcb */ - } else + else #endif /*IPSEC*/ if ((n = m_copy(m, 0, M_COPYALL)) != NULL) { /* @@ -310,7 +312,7 @@ udp6_input(mp, offp, proto) /* * Check AH/ESP integrity. */ - if (last != NULL && ipsec6_in_reject_so(m, last->inp_socket)) { + if (ipsec6_in_reject_so(m, last->inp_socket)) { ipsec6stat.in_polvio++; goto bad; } @@ -358,7 +360,7 @@ udp6_input(mp, offp, proto) /* * Check AH/ESP integrity. */ - if (in6p != NULL && ipsec6_in_reject_so(m, in6p->in6p_socket)) { + if (ipsec6_in_reject_so(m, in6p->in6p_socket)) { ipsec6stat.in_polvio++; goto bad; } @@ -475,7 +477,7 @@ udp6_getcred SYSCTL_HANDLER_ARGS addrs[1].sin6_port, &addrs[0].sin6_addr, addrs[0].sin6_port, 1, NULL); - if (!inp || !inp->inp_socket || !inp->inp_socket->so_cred) { + if (!inp || !inp->inp_socket) { error = ENOENT; goto out; } @@ -556,8 +558,10 @@ udp6_output(in6p, m, addr6, control, p) * Stuff checksum and output datagram. */ ip6 = mtod(m, struct ip6_hdr *); - ip6->ip6_flow = in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK; - ip6->ip6_vfc = IPV6_VERSION; + ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) | + (in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK); + ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) | + (IPV6_VERSION & IPV6_VERSION_MASK); /* ip6_plen will be filled in ip6_output. */ ip6->ip6_nxt = IPPROTO_UDP; ip6->ip6_hlim = in6_selecthlim(in6p, @@ -584,7 +588,7 @@ udp6_output(in6p, m, addr6, control, p) m->m_pkthdr.rcvif = (struct ifnet *)in6p->in6p_socket; #endif /*IPSEC*/ error = ip6_output(m, in6p->in6p_outputopts, &in6p->in6p_route, - 0, in6p->in6p_moptions, NULL); + IPV6_SOCKINMRCVIF, in6p->in6p_moptions, NULL); if (addr6) { in6_pcbdisconnect(in6p); |