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/netkey/key.c | |
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/netkey/key.c')
-rw-r--r-- | sys/netkey/key.c | 7264 |
1 files changed, 5011 insertions, 2253 deletions
diff --git a/sys/netkey/key.c b/sys/netkey/key.c index 0116b2a..d9ec961 100644 --- a/sys/netkey/key.c +++ b/sys/netkey/key.c @@ -1,2558 +1,5316 @@ /* - * modified by Jun-ichiro itojun Itoh <itojun@itojun.org>, 1997 - */ -/*---------------------------------------------------------------------- - key.c : Key Management Engine for BSD - - Copyright 1995 by Bao Phan, Randall Atkinson, & Dan McDonald, - All Rights Reserved. All Rights have been assigned to the US - Naval Research Laboratory (NRL). The NRL Copyright Notice and - License governs distribution and use of this software. - - Patents are pending on this technology. NRL grants a license - to use this technology at no cost under the terms below with - the additional requirement that software, hardware, and - documentation relating to use of this technology must include - the note that: - This product includes technology developed at and - licensed from the Information Technology Division, - US Naval Research Laboratory. - -----------------------------------------------------------------------*/ -/*---------------------------------------------------------------------- -# @(#)COPYRIGHT 1.1a (NRL) 17 August 1995 - -COPYRIGHT NOTICE - -All of the documentation and software included in this software -distribution from the US Naval Research Laboratory (NRL) are -copyrighted by their respective developers. - -This software and documentation were developed at NRL by various -people. Those developers have each copyrighted the portions that they -developed at NRL and have assigned All Rights for those portions to -NRL. Outside the USA, NRL also has copyright on the software -developed at NRL. The affected files all contain specific copyright -notices and those notices must be retained in any derived work. - -NRL LICENSE - -NRL grants permission for redistribution and use in source and binary -forms, with or without modification, of the software and documentation -created at NRL provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. -3. All advertising materials mentioning features or use of this software - must display the following acknowledgement: - - This product includes software developed at the Information - Technology Division, US Naval Research Laboratory. - -4. Neither the name of the NRL nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL 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 NRL 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. - -The views and conclusions contained in the software and documentation -are those of the authors and should not be interpreted as representing -official policies, either expressed or implied, of the US Naval -Research Laboratory (NRL). - -----------------------------------------------------------------------*/ - -#include "opt_key.h" - -#ifdef KEY + * 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$ + */ + +/* KAME $Id: key.c,v 1.1.6.5.2.19 1999/07/22 14:09:24 itojun Exp $ */ + +/* + * This code is referd to RFC 2367 + */ +#include "opt_inet.h" +#include "opt_inet6.h" +#include "opt_ipsec.h" + +#include <sys/types.h> #include <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> -#include <sys/domain.h> -#include <sys/malloc.h> #include <sys/mbuf.h> -#include <sys/proc.h> +#include <sys/domain.h> #include <sys/protosw.h> +#include <sys/malloc.h> #include <sys/socket.h> #include <sys/socketvar.h> +#include <sys/sysctl.h> +#include <sys/errno.h> +#include <sys/proc.h> +#include <sys/queue.h> -#include <net/raw_cb.h> #include <net/if.h> -#include <net/if_dl.h> #include <net/route.h> +#include <net/raw_cb.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/in6.h> +#include <netinet6/ip6.h> #include <netinet6/in6_var.h> +#include <netinet6/in6_pcb.h> #endif /* INET6 */ +#include <net/pfkeyv2.h> +#include <netkey/key_var.h> +#include <netkey/keydb.h> #include <netkey/key.h> +#include <netkey/keysock.h> +#ifdef IPSEC_DEBUG #include <netkey/key_debug.h> +#else +#define KEYDEBUG(lev,arg) +#endif -static MALLOC_DEFINE(M_SECA, "key mgmt", "security associations, key management"); +#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 + +MALLOC_DEFINE(M_SECA, "key mgmt", "security associations, key management"); + +#if defined(IPSEC_DEBUG) +u_int32_t key_debug_level = 0; +#endif /* defined(IPSEC_DEBUG) */ +static u_int key_spi_trycnt = 1000; +static u_int32_t key_spi_minval = 0x100; +static u_int32_t key_spi_maxval = 0x0fffffff; /* XXX */ +static u_int key_int_random = 60; /*interval to initialize randseed,1(m)*/ +static u_int key_larval_lifetime = 30; /* interval to expire acquiring, 30(s)*/ +static int key_blockacq_count = 10; /* counter for blocking SADB_ACQUIRE.*/ +static int key_blockacq_lifetime = 20; /* lifetime for blocking SADB_ACQUIRE.*/ + +static u_int32_t acq_seq = 0; +static int key_tick_init_random = 0; + +static LIST_HEAD(_sptree, secpolicy) sptree[IPSEC_DIR_MAX]; /* SPD */ +static LIST_HEAD(_sahtree, secashead) sahtree; /* SAD */ +static LIST_HEAD(_regtree, secreg) regtree[SADB_SATYPE_MAX + 1]; + /* registed list */ +#ifndef IPSEC_NONBLOCK_ACQUIRE +static LIST_HEAD(_acqtree, secacq) acqtree; /* acquiring list */ +#endif -#define KMALLOC(p, t, n) (p = (t) malloc((unsigned long)(n), M_SECA, M_DONTWAIT)) -#define KFREE(p) free((caddr_t)p, M_SECA); +struct key_cb key_cb; -#define CRITICAL_DCL int critical_s; -#define CRITICAL_START critical_s = splnet() -#define CRITICAL_END splx(critical_s) +/* search order for SAs */ +static u_int saorder_state_valid[] = { + SADB_SASTATE_MATURE, SADB_SASTATE_DYING +}; +static u_int saorder_state_alive[] = { + /* except DEAD */ + SADB_SASTATE_MATURE, SADB_SASTATE_DYING, SADB_SASTATE_LARVAL +}; +static u_int saorder_state_any[] = { + SADB_SASTATE_MATURE, SADB_SASTATE_DYING, + SADB_SASTATE_LARVAL, SADB_SASTATE_DEAD +}; +#if defined(IPSEC_DEBUG) +SYSCTL_INT(_net_key, KEYCTL_DEBUG_LEVEL, debug, CTLFLAG_RW, \ + &key_debug_level, 0, ""); +#endif /* defined(IPSEC_DEBUG) */ + +/* max count of trial for the decision of spi value */ +SYSCTL_INT(_net_key, KEYCTL_SPI_TRY, spi_trycnt, CTLFLAG_RW, \ + &key_spi_trycnt, 0, ""); + +/* minimum spi value to allocate automatically. */ +SYSCTL_INT(_net_key, KEYCTL_SPI_MIN_VALUE, spi_minval, CTLFLAG_RW, \ + &key_spi_minval, 0, ""); + +/* maximun spi value to allocate automatically. */ +SYSCTL_INT(_net_key, KEYCTL_SPI_MAX_VALUE, spi_maxval, CTLFLAG_RW, \ + &key_spi_maxval, 0, ""); + +/* interval to initialize randseed */ +SYSCTL_INT(_net_key, KEYCTL_RANDOM_INT, int_random, CTLFLAG_RW, \ + &key_int_random, 0, ""); + +/* lifetime for larval SA */ +SYSCTL_INT(_net_key, KEYCTL_LARVAL_LIFETIME, larval_lifetime, CTLFLAG_RW, \ + &key_larval_lifetime, 0, ""); + +/* counter for blocking to send SADB_ACQUIRE to IKEd */ +SYSCTL_INT(_net_key, KEYCTL_BLOCKACQ_COUNT, blockacq_count, CTLFLAG_RW, \ + &key_blockacq_count, 0, ""); + +/* lifetime for blocking to send SADB_ACQUIRE to IKEd */ +SYSCTL_INT(_net_key, KEYCTL_BLOCKACQ_LIFETIME, blockacq_lifetime, CTLFLAG_RW, \ + &key_blockacq_lifetime, 0, ""); + +#define __LIST_FOREACH(elm, head, field) \ + for (elm = LIST_FIRST(head); elm; elm = LIST_NEXT(elm, field)) +#define __LIST_CHAINED(elm) \ + (!((elm)->chain.le_next == NULL && (elm)->chain.le_prev == NULL)) + +#define KEY_CHKSASTATE(head, sav, name) { \ + if ((head) != (sav)) { \ + printf("%s: state mismatched (TREE=%d SA=%d)\n", \ + (name), (head), (sav)); \ + continue; \ + } \ +} + +#define KEY_CHKSPDIR(head, sp, name) { \ + if ((head) != (sp)) { \ + printf("%s: direction mismatched (TREE=%d SP=%d), " \ + "anyway continue.\n", \ + (name), (head), (sp)); \ + } \ +} + +#define KMALLOC(p, t, n) \ + ((p) = (t) malloc((unsigned long)(n), M_SECA, M_NOWAIT)) +#define KFREE(p) \ + free((caddr_t)(p), M_SECA); + +#define KEY_NEWBUF(dst, t, src, len) \ + ((dst) = (t)key_newbuf((src), (len))) + +/* + * set parameters into secpolicyindex buffer. + * Must allocate secpolicyindex buffer passed to this function. + */ +#define KEY_SETSECSPIDX(_dir, s, d, ps, pd, ulp, idx) do { \ + bzero((idx), sizeof(struct secpolicyindex)); \ + (idx)->dir = (_dir); \ + (idx)->prefs = (ps); \ + (idx)->prefd = (pd); \ + (idx)->ul_proto = (ulp); \ + bcopy((s), &(idx)->src, ((struct sockaddr *)(s))->sa_len); \ + bcopy((d), &(idx)->dst, ((struct sockaddr *)(d))->sa_len); \ +} while (0) + +/* + * set parameters into secasindex buffer. + * Must allocate secasindex buffer before calling this function. + */ +#define KEY_SETSECASIDX(p, m, s, d, idx) do { \ + bzero((idx), sizeof(struct secasindex)); \ + (idx)->proto = (p); \ + (idx)->mode = (m); \ + bcopy((s), &(idx)->src, ((struct sockaddr *)(s))->sa_len); \ + bcopy((d), &(idx)->dst, ((struct sockaddr *)(d))->sa_len); \ +} while (0) + +/* key statistics */ +struct _keystat { + u_long getspi_count; /* the avarage of count to try to get new SPI */ +} keystat; + +static struct secasvar *key_allocsa_policy __P((struct ipsecrequest *isr)); +static void key_freesp_so __P((struct secpolicy **sp)); +static struct secasvar *key_do_allocsa_policy __P((struct secashead *sah, + u_int state)); +static void key_delsp __P((struct secpolicy *sp)); +static struct secpolicy *key_getsp __P((struct secpolicyindex *spidx)); +static struct sadb_msg *key_spdadd __P((caddr_t *mhp)); +static struct sadb_msg *key_spddelete __P((caddr_t *mhp)); +static struct sadb_msg *key_spdflush __P((caddr_t *mhp)); +static int key_spddump __P((caddr_t *mhp, struct socket *so, int target)); +static u_int key_setdumpsp __P((struct sadb_msg *newmsg, struct secpolicy *sp, + u_int8_t type, u_int32_t seq, u_int32_t pid)); +static u_int key_getspmsglen __P((struct secpolicy *sp)); +static u_int key_getspreqmsglen __P((struct secpolicy *sp)); +static struct secashead *key_newsah __P((struct secasindex *saidx)); +static void key_delsah __P((struct secashead *sah)); +static struct secasvar *key_newsav __P((caddr_t *mhp, struct secashead *sah)); +static void key_delsav __P((struct secasvar *sav)); +static struct secashead *key_getsah __P((struct secasindex *saidx)); +static struct secasvar *key_checkspidup __P((struct secasindex *saidx, + u_int32_t spi)); +static struct secasvar *key_getsavbyspi __P((struct secashead *sah, + u_int32_t spi)); +static int key_setsaval __P((struct secasvar *sav, caddr_t *mhp)); +static u_int key_getmsglen __P((struct secasvar *sav)); +static int key_mature __P((struct secasvar *sav)); +static u_int key_setdumpsa __P((struct sadb_msg *newmsg, struct secasvar *sav, + u_int8_t type, u_int8_t satype, + u_int32_t seq, u_int32_t pid)); +static caddr_t key_setsadbmsg __P((caddr_t buf, u_int8_t type, int tlen, + u_int8_t satype, u_int32_t seq, pid_t pid, + u_int8_t reserved1, u_int8_t reserved2)); +static caddr_t key_setsadbsa __P((caddr_t buf, struct secasvar *sav)); +static caddr_t key_setsadbaddr __P((caddr_t buf, u_int16_t exttype, + struct sockaddr *saddr, u_int8_t prefixlen, u_int16_t ul_proto)); +static caddr_t key_setsadbident + __P((caddr_t buf, u_int16_t exttype, u_int16_t idtype, + caddr_t string, int stringlen, u_int64_t id)); +static caddr_t key_setsadbext __P((caddr_t p, caddr_t ext)); +static void *key_newbuf __P((void *src, u_int len)); #ifdef INET6 -#define MAXHASHKEYLEN (2 * sizeof(int) + 2 * sizeof(struct sockaddr_in6)) -#else -#define MAXHASHKEYLEN (2 * sizeof(int) + 2 * sizeof(struct sockaddr_in)) +static int key_ismyaddr6 __P((caddr_t addr)); #endif +static int key_cmpsaidx_exactly + __P((struct secasindex *saidx0, struct secasindex *saidx1)); +static int key_cmpsaidx_withmode + __P((struct secasindex *saidx0, struct secasindex *saidx1)); +static int key_cmpspidx_exactly + __P((struct secpolicyindex *spidx0, struct secpolicyindex *spidx1)); +static int key_cmpspidx_withmask + __P((struct secpolicyindex *spidx0, struct secpolicyindex *spidx1)); +static int key_bbcmp __P((caddr_t p1, caddr_t p2, u_int bits)); +static u_int16_t key_satype2proto __P((u_int8_t satype)); +static u_int8_t key_proto2satype __P((u_int16_t proto)); + +static struct sadb_msg *key_getspi __P((caddr_t *mhp)); +static u_int32_t key_do_getnewspi __P((struct sadb_spirange *spirange, + struct secasindex *saidx)); +static struct sadb_msg *key_update __P((caddr_t *mhp)); +static struct secasvar *key_getsavbyseq __P((struct secashead *sah, + u_int32_t seq)); +static struct sadb_msg *key_add __P((caddr_t *mhp)); +static struct sadb_msg *key_getmsgbuf_x1 __P((caddr_t *mhp)); +static struct sadb_msg *key_delete __P((caddr_t *mhp)); +static struct sadb_msg *key_get __P((caddr_t *mhp)); +static int key_acquire __P((struct secasindex *saidx, + struct secpolicyindex *spidx)); +static struct secacq *key_newacq __P((struct secasindex *saidx)); +static struct secacq *key_getacq __P((struct secasindex *saidx)); +static struct secacq *key_getacqbyseq __P((u_int32_t seq)); +static struct sadb_msg *key_acquire2 __P((caddr_t *mhp)); +static struct sadb_msg *key_register __P((caddr_t *mhp, struct socket *so)); +static int key_expire __P((struct secasvar *sav)); +static struct sadb_msg *key_flush __P((caddr_t *mhp)); +static int key_dump __P((caddr_t *mhp, struct socket *so, int target)); +static void key_promisc __P((caddr_t *mhp, struct socket *so)); +static int key_sendall __P((struct sadb_msg *msg, u_int len)); +static int key_align __P((struct sadb_msg *msg, caddr_t *mhp)); +static void key_sa_chgstate __P((struct secasvar *sav, u_int8_t state)); +/* %%% IPsec policy management */ +/* + * allocating a SP for OUTBOUND or INBOUND packet. + * Must call key_freesp() later. + * OUT: NULL: not found + * others: found and return the pointer. + */ +struct secpolicy * +key_allocsp(spidx, dir) + struct secpolicyindex *spidx; + u_int dir; +{ + struct secpolicy *sp; + int s; + + /* sanity check */ + if (spidx == NULL) + panic("key_allocsp: NULL pointer is passed.\n"); + + /* check direction */ + switch (dir) { + case IPSEC_DIR_INBOUND: + case IPSEC_DIR_OUTBOUND: + break; + default: + panic("key_allocsp: Invalid direction is passed.\n"); + } + + /* get a SP entry */ + s = splnet(); /*called from softclock()*/ + KEYDEBUG(KEYDEBUG_IPSEC_DATA, + printf("*** objects\n"); + kdebug_secpolicyindex(spidx)); + + __LIST_FOREACH(sp, &sptree[dir], chain) { + KEYDEBUG(KEYDEBUG_IPSEC_DATA, + printf("*** in SPD\n"); + kdebug_secpolicyindex(&sp->spidx)); + + if (sp->state == IPSEC_SPSTATE_DEAD) + continue; + if (key_cmpspidx_withmask(&sp->spidx, spidx)) + goto found; + } + + splx(s); + return NULL; + +found: + /* sanity check */ + KEY_CHKSPDIR(sp->spidx.dir, dir, "key_allocsp"); + + /* found a SPD entry */ + sp->refcnt++; + splx(s); + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP key_allocsp cause refcnt++:%d SP:%p\n", + sp->refcnt, sp)); + + return sp; +} /* - * Not clear whether these values should be - * tweakable at kernel config time. - */ -#define KEYTBLSIZE 61 -#define KEYALLOCTBLSIZE 61 -#define SO2SPITBLSIZE 61 - -/* - * These values should be tweakable... - * perhaps by using sysctl - */ - -#define MAXLARVALTIME 240; /* Lifetime of a larval key table entry */ -#define MAXKEYACQUIRE 1; /* Max number of key acquire messages sent */ - /* per destination address */ -#define MAXACQUIRETIME 15; /* Lifetime of acquire message */ - -/* - * Key engine tables and global variables - */ - -struct key_tblnode keytable[KEYTBLSIZE]; -struct key_allocnode keyalloctbl[KEYALLOCTBLSIZE]; -struct key_so2spinode so2spitbl[SO2SPITBLSIZE]; - -struct keyso_cb keyso_cb; -struct key_tblnode nullkeynode; -struct key_registry *keyregtable; -struct key_acquirelist *key_acquirelist; -u_long maxlarvallifetime = MAXLARVALTIME; -int maxkeyacquire = MAXKEYACQUIRE; -u_long maxacquiretime = MAXACQUIRETIME; - -extern struct sockaddr key_addr; - -#define ROUNDUP(a) \ - ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) -#define ADVANCE(x, n) \ - { x += ROUNDUP(n); } - -static int addrpart_equal __P((struct sockaddr *, struct sockaddr *)); -static int key_gethashval __P((char *, int, int)); -static int key_createkey __P((char *, u_int, struct sockaddr *, - struct sockaddr *, u_int32_t, u_int)); -static struct key_so2spinode *key_sosearch __P((u_int, struct sockaddr *, - struct sockaddr *, struct socket *)); -static void key_deleteacquire __P((u_int, struct sockaddr *)); -static struct key_tblnode *key_search __P((u_int, struct sockaddr *, - struct sockaddr *, u_int32_t, int, struct key_tblnode **)); -static struct key_tblnode *key_addnode __P((int, struct key_secassoc *)); -static int key_alloc __P((u_int, struct sockaddr *, struct sockaddr *, - struct socket *, u_int, struct key_tblnode **)); -static int key_xdata __P((struct key_msghdr *, struct key_msgdata *, int)); -static int key_sendup __P((struct socket *, struct key_msghdr *)); -static void key_init __P((void)); -static int my_addr __P((struct sockaddr *)); -static int key_output __P((struct mbuf *, struct socket *)); -static int key_attach __P((struct socket *, int, struct proc *)); -static int key_detach __P((struct socket *)); -static void key_cbinit __P((void)); - -/*---------------------------------------------------------------------- - * key_secassoc2msghdr(): - * Copy info from a security association into a key message buffer. - * Assume message buffer is sufficiently large to hold all security - * association information including src, dst, from, key and iv. - ----------------------------------------------------------------------*/ + * checking each request entries in SP, and acquire SA if need. + * OUT: 0: there are valid requests. + * ENOENT: policy may be valid, but SA with REQUIRE is on acquiring. + */ int -key_secassoc2msghdr(secassoc, km, keyinfo) - struct key_secassoc *secassoc; - struct key_msghdr *km; - struct key_msgdata *keyinfo; -{ - char *cp; - DPRINTF(IDL_FINISHED, ("Entering key_secassoc2msghdr\n")); - - if ((km == 0) || (keyinfo == 0) || (secassoc == 0)) - return(-1); - - km->type = secassoc->type; - km->vers = secassoc->vers; - km->state = secassoc->state; - km->label = secassoc->label; - km->spi = secassoc->spi; - km->keylen = secassoc->keylen; - km->ekeylen = secassoc->ekeylen; - km->ivlen = secassoc->ivlen; - km->algorithm = secassoc->algorithm; - km->lifetype = secassoc->lifetype; - km->lifetime1 = secassoc->lifetime1; - km->lifetime2 = secassoc->lifetime2; - km->antireplay = secassoc->antireplay; - - /* - * Stuff src/dst/from/key/iv/ekey in buffer after - * the message header. - */ - cp = (char *)(km + 1); - - DPRINTF(IDL_FINISHED, ("sa2msghdr: 1\n")); - keyinfo->src = (struct sockaddr *)cp; - if (secassoc->src->sa_len) { - bcopy(secassoc->src, cp, secassoc->src->sa_len); - ADVANCE(cp, secassoc->src->sa_len); - } else { - bzero(cp, MAX_SOCKADDR_SZ); - ADVANCE(cp, MAX_SOCKADDR_SZ); - } - - DPRINTF(IDL_FINISHED, ("sa2msghdr: 2\n")); - keyinfo->dst = (struct sockaddr *)cp; - if (secassoc->dst->sa_len) { - bcopy(secassoc->dst, cp, secassoc->dst->sa_len); - ADVANCE(cp, secassoc->dst->sa_len); - } else { - bzero(cp, MAX_SOCKADDR_SZ); - ADVANCE(cp, MAX_SOCKADDR_SZ); - } +key_checkrequest(isr) + struct ipsecrequest *isr; +{ + u_int level; + int error; + + /* sanity check */ + if (isr == NULL) + panic("key_checkrequest: NULL pointer is passed.\n"); + + /* check mode */ + switch (isr->saidx.mode) { + case IPSEC_MODE_TRANSPORT: + case IPSEC_MODE_TUNNEL: + break; + case IPSEC_MODE_ANY: + default: + panic("key_checkrequest: Invalid policy defined.\n"); + } - DPRINTF(IDL_FINISHED, ("sa2msghdr: 3\n")); - keyinfo->from = (struct sockaddr *)cp; - if (secassoc->from->sa_len) { - bcopy(secassoc->from, cp, secassoc->from->sa_len); - ADVANCE(cp, secassoc->from->sa_len); - } else { - bzero(cp, MAX_SOCKADDR_SZ); - ADVANCE(cp, MAX_SOCKADDR_SZ); - } + /* get current level */ + level = ipsec_get_reqlevel(isr); - DPRINTF(IDL_FINISHED, ("sa2msghdr: 4\n")); + /* + * We don't allocate new SA if the state of SA in the holder is + * SADB_SASTATE_MATURE, and if this is newer one. + */ + if (isr->sav != NULL) { + /* + * XXX While SA is hanging on policy request(isr), its refcnt + * can not be zero. So isr->sav->sah is valid pointer if + * isr->sav != NULL. But that may not be true in fact. + * There may be missunderstanding by myself. Anyway I set + * zero to isr->sav->sah when isr->sav is flushed. + * I must check to have conviction this issue. + */ + if (isr->sav->sah != NULL + && isr->sav != (struct secasvar *)LIST_FIRST( + &isr->sav->sah->savtree[SADB_SASTATE_MATURE])) { + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP checkrequest calls free SA:%p\n", + isr->sav)); + key_freesav(isr->sav); + } + isr->sav = NULL; + } - keyinfo->key = cp; - keyinfo->keylen = secassoc->keylen; - if (secassoc->keylen) { - bcopy((char *)(secassoc->key), cp, secassoc->keylen); - ADVANCE(cp, secassoc->keylen); - } + /* new SA allocation if no SA found. */ + if (isr->sav == NULL) + isr->sav = key_allocsa_policy(isr); - DPRINTF(IDL_FINISHED, ("sa2msghdr: 5\n")); - keyinfo->iv = cp; - keyinfo->ivlen = secassoc->ivlen; - if (secassoc->ivlen) { - bcopy((char *)(secassoc->iv), cp, secassoc->ivlen); - ADVANCE(cp, secassoc->ivlen); - } + /* When there is SA. */ + if (isr->sav != NULL) + return 0; - DPRINTF(IDL_FINISHED, ("sa2msghdr: 6\n")); - keyinfo->ekey = cp; - keyinfo->ekeylen = secassoc->ekeylen; - if (secassoc->ekeylen) { - bcopy((char *)(secassoc->ekey), cp, secassoc->ekeylen); - ADVANCE(cp, secassoc->ekeylen); - } + /* there is no SA */ + if ((error = key_acquire(&isr->saidx, &isr->sp->spidx)) != 0) { + /* XXX What I do ? */ + printf("key_checkrequest: error %d returned " + "from key_acquire.\n", error); + return error; + } - DDO(IDL_FINISHED,printf("msgbuf(len=%d):\n",(char *)cp - (char *)km)); - DDO(IDL_FINISHED,dump_buf((char *)km, (char *)cp - (char *)km)); - DPRINTF(IDL_FINISHED, ("sa2msghdr: 6\n")); - return(0); + return level == IPSEC_LEVEL_REQUIRE ? ENOENT : 0; } +/* + * allocating a SA for policy entry from SAD. + * NOTE: searching SAD of aliving state. + * OUT: NULL: not found. + * others: found and return the pointer. + */ +static struct secasvar * +key_allocsa_policy(isr) + struct ipsecrequest *isr; +{ + struct secashead *sah; + struct secasvar *sav; + u_int stateidx, state; + + __LIST_FOREACH(sah, &sahtree, chain) { + if (sah->state == SADB_SASTATE_DEAD) + continue; + if (key_cmpsaidx_withmode(&sah->saidx, &isr->saidx)) + goto found; + } -/*---------------------------------------------------------------------- - * key_msghdr2secassoc(): - * Copy info from a key message buffer into a key_secassoc - * structure - ----------------------------------------------------------------------*/ -int -key_msghdr2secassoc(secassoc, km, keyinfo) - struct key_secassoc *secassoc; - struct key_msghdr *km; - struct key_msgdata *keyinfo; -{ - DPRINTF(IDL_FINISHED, ("Entering key_msghdr2secassoc\n")); - - if ((km == 0) || (keyinfo == 0) || (secassoc == 0)) - return(-1); - - secassoc->len = sizeof(*secassoc); - secassoc->type = km->type; - secassoc->vers = km->vers; - secassoc->state = km->state; - secassoc->label = km->label; - secassoc->spi = km->spi; - secassoc->keylen = km->keylen; - secassoc->ekeylen = km->ekeylen; - secassoc->ivlen = km->ivlen; - secassoc->algorithm = km->algorithm; - secassoc->lifetype = km->lifetype; - secassoc->lifetime1 = km->lifetime1; - secassoc->lifetime2 = km->lifetime2; - secassoc->antireplay = km->antireplay; - - if (keyinfo->src) { - KMALLOC(secassoc->src, struct sockaddr *, keyinfo->src->sa_len); - if (!secassoc->src) { - DPRINTF(IDL_ERROR,("msghdr2secassoc: can't allocate mem for src\n")); - return(-1); - } - bcopy((char *)keyinfo->src, (char *)secassoc->src, - keyinfo->src->sa_len); - } else - secassoc->src = NULL; - - if (keyinfo->dst) { - KMALLOC(secassoc->dst, struct sockaddr *, keyinfo->dst->sa_len); - if (!secassoc->dst) { - DPRINTF(IDL_ERROR,("msghdr2secassoc: can't allocate mem for dst\n")); - return(-1); - } - bcopy((char *)keyinfo->dst, (char *)secassoc->dst, - keyinfo->dst->sa_len); - } else - secassoc->dst = NULL; - - if (keyinfo->from) { - KMALLOC(secassoc->from, struct sockaddr *, keyinfo->from->sa_len); - if (!secassoc->from) { - DPRINTF(IDL_ERROR,("msghdr2secassoc: can't allocate mem for from\n")); - return(-1); - } - bcopy((char *)keyinfo->from, (char *)secassoc->from, - keyinfo->from->sa_len); - } else - secassoc->from = NULL; - - /* - * Make copies of key and iv - */ - if (secassoc->ivlen) { - KMALLOC(secassoc->iv, caddr_t, secassoc->ivlen); - if (secassoc->iv == 0) { - DPRINTF(IDL_ERROR,("msghdr2secassoc: can't allocate mem for iv\n")); - return(-1); - } - bcopy((char *)keyinfo->iv, (char *)secassoc->iv, secassoc->ivlen); - } else - secassoc->iv = NULL; - - if (secassoc->keylen) { - KMALLOC(secassoc->key, caddr_t, secassoc->keylen); - if (secassoc->key == 0) { - DPRINTF(IDL_ERROR,("msghdr2secassoc: can't allocate mem for key\n")); - if (secassoc->iv) - KFREE(secassoc->iv); - return(-1); - } - bcopy((char *)keyinfo->key, (char *)secassoc->key, secassoc->keylen); - } else - secassoc->key = NULL; - - if (secassoc->ekeylen) { - KMALLOC(secassoc->ekey, caddr_t, secassoc->ekeylen); - if (secassoc->ekey == 0) { - DPRINTF(IDL_ERROR,("msghdr2secassoc: can't allocate mem for ekey\n")); - if (secassoc->iv) - KFREE(secassoc->iv); - if (secassoc->key) - KFREE(secassoc->key); - return(-1); - } - bcopy((char *)keyinfo->ekey, (char *)secassoc->ekey, secassoc->ekeylen); - } else - secassoc->ekey = NULL; + return NULL; + + found: + + /* search valid state */ + for (stateidx = 0; + stateidx < _ARRAYLEN(saorder_state_valid); + stateidx++) { + + state = saorder_state_valid[stateidx]; + + sav = key_do_allocsa_policy(sah, state); + if (sav != NULL) + return sav; + } - return(0); + return NULL; } +/* + * searching SAD with direction, protocol, mode and state. + * called by key_allocsa_policy(). + * OUT: + * NULL : not found + * others : found, pointer to a SA. + */ +static struct secasvar * +key_do_allocsa_policy(sah, state) + struct secashead *sah; + u_int state; +{ + struct secasvar *sav, *candidate; + + /* initilize */ + candidate = NULL; + + __LIST_FOREACH(sav, &sah->savtree[state], chain) { + + /* sanity check */ + KEY_CHKSASTATE(sav->state, state, "key_do_allocsa_policy"); + + /* initialize */ + if (candidate == NULL) { + candidate = sav; + continue; + } + + /* Which SA is the better ? */ + + /* sanity check 2 */ + if (candidate->lft_c == NULL || sav->lft_c == NULL) { + /*XXX do panic ? */ + printf("key_do_allocsa_policy: " + "lifetime_current is NULL.\n"); + continue; + } + + /* XXX What the best method is to compare ? */ + if (candidate->lft_c->sadb_lifetime_addtime < + sav->lft_c->sadb_lifetime_addtime) { + candidate = sav; + continue; + } + } -/*---------------------------------------------------------------------- - * addrpart_equal(): - * Determine if the address portion of two sockaddrs are equal. - * Currently handles only AF_INET and AF_INET6 address families. - ----------------------------------------------------------------------*/ -static int -addrpart_equal(sa1, sa2) - struct sockaddr *sa1; - struct sockaddr *sa2; -{ - if ((sa1->sa_family != sa2->sa_family) || - (sa1->sa_len != sa2->sa_len)) - return 0; - - switch(sa1->sa_family) { - case AF_INET: - return (((struct sockaddr_in *)sa1)->sin_addr.s_addr == - ((struct sockaddr_in *)sa2)->sin_addr.s_addr); -#ifdef INET6 - case AF_INET6: - return (IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)sa1)->sin6_addr, - &((struct sockaddr_in6 *)sa2)->sin6_addr)); -#endif /* INET6 */ - } - return(0); + if (candidate) { + candidate->refcnt++; + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP allocsa_policy cause " + "refcnt++:%d SA:%p\n", + candidate->refcnt, candidate)); + } + return candidate; } -/*---------------------------------------------------------------------- - * key_inittables(): - * Allocate space and initialize key engine tables - ----------------------------------------------------------------------*/ -int -key_inittables() -{ - int i; - - KMALLOC(keyregtable, struct key_registry *, sizeof(struct key_registry)); - if (!keyregtable) - return -1; - bzero((char *)keyregtable, sizeof(struct key_registry)); - KMALLOC(key_acquirelist, struct key_acquirelist *, - sizeof(struct key_acquirelist)); - if (!key_acquirelist) - return -1; - bzero((char *)key_acquirelist, sizeof(struct key_acquirelist)); - for (i = 0; i < KEYTBLSIZE; i++) - bzero((char *)&keytable[i], sizeof(struct key_tblnode)); - for (i = 0; i < KEYALLOCTBLSIZE; i++) - bzero((char *)&keyalloctbl[i], sizeof(struct key_allocnode)); - for (i = 0; i < SO2SPITBLSIZE; i++) - bzero((char *)&so2spitbl[i], sizeof(struct key_so2spinode)); - - return 0; -} - -#ifdef notyet -static int -key_freetables() +/* + * allocating a SA entry for a *INBOUND* packet. + * Must call key_freesav() later. + * OUT: positive: pointer to a sav. + * NULL: not found, or error occured. + */ +struct secasvar * +key_allocsa(family, src, dst, proto, spi) + u_int family, proto; + caddr_t src, dst; + u_int32_t spi; { - KFREE(keyregtable); - keyregtable = NULL; - KFREE(key_acquirelist); - key_acquirelist = NULL; - return 0; + struct secashead *sah; + struct secasvar *sav; + u_int stateidx, state; + int s; + + /* sanity check */ + if (src == NULL || dst == NULL) + panic("key_allocsa: NULL pointer is passed.\n"); + + /* + * searching SAD. + * XXX: to be checked internal IP header somewhere. Also when + * IPsec tunnel packet is received. But ESP tunnel mode is + * encrypted so we can't check internal IP header. + */ + s = splnet(); /*called from softclock()*/ + __LIST_FOREACH(sah, &sahtree, chain) { + + /* search valid state */ + for (stateidx = 0; + stateidx < _ARRAYLEN(saorder_state_valid); + stateidx++) { + + state = saorder_state_valid[stateidx]; + __LIST_FOREACH(sav, &sah->savtree[state], chain) { + + /* sanity check */ + KEY_CHKSASTATE(sav->state, state, "key_allocsav"); + if (proto != sav->sah->saidx.proto) + continue; + if (spi != sav->spi) + continue; + + if (key_bbcmp(src, + _INADDRBYSA(&sav->sah->saidx.src), + _INALENBYAF(sav->sah->saidx.src.__ss_family) << 3) + && key_bbcmp(dst, + _INADDRBYSA(&sav->sah->saidx.dst), + _INALENBYAF(sav->sah->saidx.dst.__ss_family) << 3)) + goto found; + } + } + } + + /* not found */ + splx(s); + return NULL; + +found: + sav->refcnt++; + splx(s); + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP allocsa cause refcnt++:%d SA:%p\n", + sav->refcnt, sav)); + return sav; } + +/* + * Must be called after calling key_allocsp(). + * For both the packet without socket and key_freeso(). + */ +void +key_freesp(sp) + struct secpolicy *sp; +{ + /* sanity check */ + if (sp == NULL) + panic("key_freesp: NULL pointer is passed.\n"); + + sp->refcnt--; + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP freesp cause refcnt--:%d SP:%p\n", + sp->refcnt, sp)); + + if (sp->refcnt == 0) + key_delsp(sp); + + return; +} + +/* + * Must be called after calling key_allocsp(). + * For the packet with socket. + */ +void +key_freeso(so) + struct socket *so; +{ + /* sanity check */ + if (so == NULL) + panic("key_freeso: NULL pointer is passed.\n"); + + switch (so->so_proto->pr_domain->dom_family) { +#ifdef INET + case PF_INET: + { + struct inpcb *pcb = sotoinpcb(so); + + /* Does it have a PCB ? */ + if (pcb == NULL) + return; + key_freesp_so(&pcb->inp_sp->sp_in); + key_freesp_so(&pcb->inp_sp->sp_out); + } + break; #endif +#ifdef INET6 + case PF_INET6: + { + struct in6pcb *pcb = sotoin6pcb(so); + + /* Does it have a PCB ? */ + if (pcb == NULL) + return; + key_freesp_so(&pcb->in6p_sp->sp_in); + key_freesp_so(&pcb->in6p_sp->sp_out); + } + break; +#endif /* INET6 */ + default: + printf("key_freeso: unknown address family=%d.\n", + so->so_proto->pr_domain->dom_family); + return; + } -/*---------------------------------------------------------------------- - * key_gethashval(): - * Determine keytable hash value. - ----------------------------------------------------------------------*/ -static int -key_gethashval(buf, len, tblsize) - char *buf; - int len; - int tblsize; -{ - int i, j = 0; - - /* - * Todo: Use word size xor and check for alignment - * and zero pad if necessary. Need to also pick - * a good hash function and table size. - */ - if (len <= 0) { - DPRINTF(IDL_ERROR,("key_gethashval got bogus len!\n")); - return(-1); - } - for(i = 0; i < len; i++) { - j ^= (u_int8_t)(*(buf + i)); - } - return (j % tblsize); + return; } +static void +key_freesp_so(sp) + struct secpolicy **sp; +{ + /* sanity check */ + if (sp == NULL || *sp == NULL) + panic("key_freesp_so: sp == NULL\n"); + + switch ((*sp)->policy) { + case IPSEC_POLICY_IPSEC: + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP freeso calls free SP:%p\n", *sp)); + key_freesp(*sp); + *sp = NULL; + break; + case IPSEC_POLICY_ENTRUST: + case IPSEC_POLICY_BYPASS: + return; + default: + panic("key_freesp_so: Invalid policy found %d", (*sp)->policy); + } -/*---------------------------------------------------------------------- - * key_createkey(): - * Create hash key for hash function - * key is: type+src+dst if keytype = 1 - * type+src+dst+spi if keytype = 0 - * Uses only the address portion of the src and dst sockaddrs to - * form key. Currently handles only AF_INET and AF_INET6 sockaddrs - ----------------------------------------------------------------------*/ -static int -key_createkey(buf, type, src, dst, spi, keytype) - char *buf; - u_int type; - struct sockaddr *src; - struct sockaddr *dst; - u_int32_t spi; - u_int keytype; + return; +} + +/* + * Must be called after calling key_allocsa(). + * This function is called by key_freesp() to free some SA allocated + * for a policy. + */ +void +key_freesav(sav) + struct secasvar *sav; { - char *cp, *p; + /* sanity check */ + if (sav == NULL) + panic("key_freesav: NULL pointer is passed.\n"); - DPRINTF(IDL_FINISHED,("Entering key_createkey\n")); + sav->refcnt--; + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP freesav cause refcnt--:%d SA:%p SPI %d\n", + sav->refcnt, sav, (u_int32_t)ntohl(sav->spi))); - if (!buf || !src || !dst) - return(-1); + if (sav->refcnt == 0) + key_delsav(sav); - cp = buf; - bcopy((char *)&type, cp, sizeof(type)); - cp += sizeof(type); + return; +} -#ifdef INET6 - /* - * Assume only IPv4 and IPv6 addresses. - */ -#define ADDRPART(a) \ - ((a)->sa_family == AF_INET6) ? \ - (char *)&(((struct sockaddr_in6 *)(a))->sin6_addr) : \ - (char *)&(((struct sockaddr_in *)(a))->sin_addr) - -#define ADDRSIZE(a) \ - ((a)->sa_family == AF_INET6) ? sizeof(struct in6_addr) : \ - sizeof(struct in_addr) -#else /* INET6 */ -#define ADDRPART(a) (char *)&(((struct sockaddr_in *)(a))->sin_addr) -#define ADDRSIZE(a) sizeof(struct in_addr) -#endif /* INET6 */ +/* %%% SPD management */ +/* + * free security policy entry. + */ +static void +key_delsp(sp) + struct secpolicy *sp; +{ + int s; - DPRINTF(IDL_FINISHED,("src addr:\n")); - DDO(IDL_FINISHED,dump_smart_sockaddr(src)); - DPRINTF(IDL_FINISHED,("dst addr:\n")); - DDO(IDL_FINISHED,dump_smart_sockaddr(dst)); + /* sanity check */ + if (sp == NULL) + panic("key_delsp: NULL pointer is passed.\n"); - p = ADDRPART(src); - bcopy(p, cp, ADDRSIZE(src)); - cp += ADDRSIZE(src); + sp->state = IPSEC_SPSTATE_DEAD; - p = ADDRPART(dst); - bcopy(p, cp, ADDRSIZE(dst)); - cp += ADDRSIZE(dst); + if (sp->refcnt > 0) + return; /* can't free */ -#undef ADDRPART -#undef ADDRSIZE + s = splnet(); /*called from softclock()*/ + /* remove from SP index */ + if (__LIST_CHAINED(sp)) + LIST_REMOVE(sp, chain); - if (keytype == 0) { - bcopy((char *)&spi, cp, sizeof(spi)); - cp += sizeof(spi); - } + { + struct ipsecrequest *isr = sp->req, *nextisr; + + while (isr != NULL) { + if (isr->sav != NULL) { + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP delsp calls free SA:%p\n", + isr->sav)); + key_freesav(isr->sav); + isr->sav = NULL; + } + + nextisr = isr->next; + KFREE(isr); + isr = nextisr; + } + } + + KFREE(sp); - DPRINTF(IDL_FINISHED,("hash key:\n")); - DDO(IDL_FINISHED, dump_buf(buf, cp - buf)); - return(cp - buf); + splx(s); + + return; } +/* + * search SPD + * OUT: NULL : not found + * others : found, pointer to a SP. + */ +static struct secpolicy * +key_getsp(spidx) + struct secpolicyindex *spidx; +{ + struct secpolicy *sp; + + /* sanity check */ + if (spidx == NULL) + panic("key_getsp: NULL pointer is passed.\n"); + + __LIST_FOREACH(sp, &sptree[spidx->dir], chain) { + if (sp->state == IPSEC_SPSTATE_DEAD) + continue; + if (key_cmpspidx_exactly(spidx, &sp->spidx)) { + sp->refcnt++; + return sp; + } + } -/*---------------------------------------------------------------------- - * key_sosearch(): - * Search the so2spi table for the security association allocated to - * the socket. Returns pointer to a struct key_so2spinode which can - * be used to locate the security association entry in the keytable. - ----------------------------------------------------------------------*/ -static struct key_so2spinode * -key_sosearch(type, src, dst, so) - u_int type; - struct sockaddr *src; - struct sockaddr *dst; - struct socket *so; + return NULL; +} + +struct secpolicy * +key_newsp() { - struct key_so2spinode *np = 0; + struct secpolicy *newsp = NULL; - if (!(src && dst)) { - DPRINTF(IDL_ERROR,("key_sosearch: got null src or dst pointer!\n")); - return(NULL); - } + KMALLOC(newsp, struct secpolicy *, sizeof(*newsp)); + if (newsp == NULL) { + printf("key_newsp: No more memory.\n"); + return NULL; + } + bzero(newsp, sizeof(*newsp)); - for (np = so2spitbl[((u_int32_t)so) % SO2SPITBLSIZE].next; np; np = np->next) { - if ((so == np->socket) && (type == np->keynode->secassoc->type) - && addrpart_equal(src, np->keynode->secassoc->src) - && addrpart_equal(dst, np->keynode->secassoc->dst)) - return(np); - } - return(NULL); + newsp->refcnt = 1; + newsp->req = NULL; + + return newsp; } +/* + * create secpolicy structure from sadb_x_policy structure. + * NOTE: `state', `secpolicyindex' in secpolicy structure are not set, + * so must be set properly later. + */ +struct secpolicy * +key_msg2sp(xpl0) + struct sadb_x_policy *xpl0; +{ + struct secpolicy *newsp; + + /* sanity check */ + if (xpl0 == NULL) + panic("key_msg2sp: NULL pointer was passed.\n"); + + if ((newsp = key_newsp()) == NULL) + return NULL; + + newsp->spidx.dir = xpl0->sadb_x_policy_dir; + newsp->policy = xpl0->sadb_x_policy_type; + + /* check policy */ + switch (xpl0->sadb_x_policy_type) { + case IPSEC_POLICY_DISCARD: + case IPSEC_POLICY_NONE: + case IPSEC_POLICY_ENTRUST: + case IPSEC_POLICY_BYPASS: + newsp->req = NULL; + break; + + case IPSEC_POLICY_IPSEC: + { + int tlen; + struct sadb_x_ipsecrequest *xisr; + struct ipsecrequest **p_isr = &newsp->req; + + /* validity check */ + if (PFKEY_EXTLEN(xpl0) <= sizeof(*xpl0)) { + printf("key_msg2sp: Invalid msg length.\n"); + key_freesp(newsp); + return NULL; + } + + tlen = PFKEY_EXTLEN(xpl0) - sizeof(*xpl0); + xisr = (struct sadb_x_ipsecrequest *)(xpl0 + 1); + + while (tlen > 0) { + + /* length check */ + if (xisr->sadb_x_ipsecrequest_len < sizeof(*xisr)) { + printf("key_msg2sp: " + "invalid ipsecrequest length.\n"); + key_freesp(newsp); + return NULL; + } + + /* allocate request buffer */ + KMALLOC(*p_isr, struct ipsecrequest *, sizeof(**p_isr)); + if ((*p_isr) == NULL) { + printf("key_msg2sp: No more memory.\n"); + key_freesp(newsp); + return NULL; + } + bzero(*p_isr, sizeof(**p_isr)); + + /* set values */ + (*p_isr)->next = NULL; + + switch (xisr->sadb_x_ipsecrequest_proto) { + case IPPROTO_ESP: + case IPPROTO_AH: + break; + default: + printf("key_msg2sp: invalid proto type=%u\n", + xisr->sadb_x_ipsecrequest_proto); + key_freesp(newsp); + return NULL; + } + (*p_isr)->saidx.proto = xisr->sadb_x_ipsecrequest_proto; + + switch (xisr->sadb_x_ipsecrequest_mode) { + case IPSEC_MODE_TRANSPORT: + case IPSEC_MODE_TUNNEL: + break; + case IPSEC_MODE_ANY: + default: + printf("key_msg2sp: invalid mode=%u\n", + xisr->sadb_x_ipsecrequest_mode); + key_freesp(newsp); + return NULL; + } + (*p_isr)->saidx.mode = xisr->sadb_x_ipsecrequest_mode; + + switch (xisr->sadb_x_ipsecrequest_level) { + case IPSEC_LEVEL_DEFAULT: + case IPSEC_LEVEL_USE: + case IPSEC_LEVEL_REQUIRE: + break; + default: + printf("key_msg2sp: invalid level=%u\n", + xisr->sadb_x_ipsecrequest_level); + key_freesp(newsp); + return NULL; + } + (*p_isr)->level = xisr->sadb_x_ipsecrequest_level; + + /* set IP addresses if there */ + if (xisr->sadb_x_ipsecrequest_len > sizeof(*xisr)) { + struct sockaddr *paddr; + + paddr = (struct sockaddr *)(xisr + 1); + + /* validity check */ + if (paddr->sa_len + > sizeof((*p_isr)->saidx.src)) { + printf("key_msg2sp: invalid request " + "address length.\n"); + key_freesp(newsp); + return NULL; + } + bcopy(paddr, &(*p_isr)->saidx.src, + paddr->sa_len); + + paddr = (struct sockaddr *)((caddr_t)paddr + + paddr->sa_len); + + /* validity check */ + if (paddr->sa_len + > sizeof((*p_isr)->saidx.dst)) { + printf("key_msg2sp: invalid request " + "address length.\n"); + key_freesp(newsp); + return NULL; + } + bcopy(paddr, &(*p_isr)->saidx.dst, + paddr->sa_len); + } + + (*p_isr)->sav = NULL; + (*p_isr)->sp = newsp; + + /* initialization for the next. */ + p_isr = &(*p_isr)->next; + tlen -= xisr->sadb_x_ipsecrequest_len; + + /* validity check */ + if (tlen < 0) { + printf("key_msg2sp: becoming tlen < 0.\n"); + key_freesp(newsp); + return NULL; + } + + xisr = (struct sadb_x_ipsecrequest *)((caddr_t)xisr + + xisr->sadb_x_ipsecrequest_len); + } + } + break; + default: + printf("key_msg2sp: invalid policy type.\n"); + key_freesp(newsp); + return NULL; + } + + return newsp; +} -/*---------------------------------------------------------------------- - * key_sodelete(): - * Delete entries from the so2spi table. - * flag = 1 purge all entries - * flag = 0 delete entries with socket pointer matching socket - ----------------------------------------------------------------------*/ -void -key_sodelete(socket, flag) - struct socket *socket; - int flag; +/* + * copy secpolicy struct to sadb_x_policy structure indicated. + */ +struct sadb_x_policy * +key_sp2msg(sp) + struct secpolicy *sp; { - struct key_so2spinode *prevnp, *np; - CRITICAL_DCL + struct sadb_x_policy *xpl; + int tlen; + caddr_t p; - CRITICAL_START; + /* sanity check. */ + if (sp == NULL) + panic("key_sp2msg: NULL pointer was passed.\n"); - DPRINTF(IDL_EVENT,("Entering keysodelete w/so=0x%x flag=%d\n", - (unsigned int)socket,flag)); + tlen = key_getspreqmsglen(sp); - if (flag) { - int i; + KMALLOC(xpl, struct sadb_x_policy *, tlen); + if (xpl == NULL) { + printf("key_sp2msg: No more memory.\n"); + return NULL; + } + bzero(xpl, tlen); + + xpl->sadb_x_policy_len = PFKEY_UNIT64(tlen); + xpl->sadb_x_policy_exttype = SADB_X_EXT_POLICY; + xpl->sadb_x_policy_type = sp->policy; + xpl->sadb_x_policy_dir = sp->spidx.dir; + p = (caddr_t)xpl + sizeof(*xpl); + + /* if is the policy for ipsec ? */ + if (sp->policy == IPSEC_POLICY_IPSEC) { + struct sadb_x_ipsecrequest *xisr; + struct ipsecrequest *isr; + + for (isr = sp->req; isr != NULL; isr = isr->next) { + + xisr = (struct sadb_x_ipsecrequest *)p; + + xisr->sadb_x_ipsecrequest_proto = isr->saidx.proto; + xisr->sadb_x_ipsecrequest_mode = isr->saidx.mode; + xisr->sadb_x_ipsecrequest_level = isr->level; + + p += sizeof(*xisr); + bcopy(&isr->saidx.src, p, isr->saidx.src.__ss_len); + p += isr->saidx.src.__ss_len; + bcopy(&isr->saidx.dst, p, isr->saidx.dst.__ss_len); + p += isr->saidx.src.__ss_len; + + xisr->sadb_x_ipsecrequest_len = + PFKEY_ALIGN8(sizeof(*xisr) + + isr->saidx.src.__ss_len + + isr->saidx.dst.__ss_len); + } + } - for (i = 0; i < SO2SPITBLSIZE; i++) - for(np = so2spitbl[i].next; np; np = np->next) { - KFREE(np); - } - CRITICAL_END; - return; - } + return xpl; +} - prevnp = &so2spitbl[((u_int32_t)socket) % SO2SPITBLSIZE]; - for(np = prevnp->next; np; np = np->next) { - if (np->socket == socket) { - struct socketlist *socklp, *prevsocklp; - - (np->keynode->alloc_count)--; - - /* - * If this socket maps to a unique secassoc, - * we go ahead and delete the secassoc, since it - * can no longer be allocated or used by any other - * socket. - */ - if (np->keynode->secassoc->state & K_UNIQUE) { - if (key_delete(np->keynode->secassoc) != 0) - panic("key_sodelete"); - np = prevnp; - continue; - } +/* + * SADB_SPDADD processing + * add a entry to SP database, when received + * <base, address(SD), policy> + * from the user(?). + * Adding to SP database, + * and send + * <base, address(SD), policy> + * to the socket which was send. + * + * IN: mhp: pointer to the pointer to each header. + * OUT: NULL if fail. + * other if success, return pointer to the message to send. + * + */ +static struct sadb_msg * +key_spdadd(mhp) + caddr_t *mhp; +{ + struct sadb_msg *msg0; + struct sadb_address *src0, *dst0; + struct sadb_x_policy *xpl0; + struct secpolicyindex spidx; + struct secpolicy *newsp; + + /* sanity check */ + if (mhp == NULL || mhp[0] == NULL) + panic("key_spdadd: NULL pointer is passed.\n"); + + msg0 = (struct sadb_msg *)mhp[0]; + + if (mhp[SADB_EXT_ADDRESS_SRC] == NULL + || mhp[SADB_EXT_ADDRESS_DST] == NULL + || mhp[SADB_X_EXT_POLICY] == NULL) { + printf("key_spdadd: invalid message is passed.\n"); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } - /* - * We traverse the socketlist and remove the entry - * for this socket - */ - DPRINTF(IDL_FINISHED,("keysodelete: deleting from socklist...")); - prevsocklp = np->keynode->solist; - for (socklp = prevsocklp->next; socklp; socklp = socklp->next) { - if (socklp->socket == socket) { - prevsocklp->next = socklp->next; - KFREE(socklp); - break; - } - prevsocklp = socklp; - } - DPRINTF(IDL_FINISHED,("done\n")); - prevnp->next = np->next; - KFREE(np); - np = prevnp; + src0 = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC]; + dst0 = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST]; + xpl0 = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY]; + + /* make secindex */ + KEY_SETSECSPIDX(xpl0->sadb_x_policy_dir, + src0 + 1, + dst0 + 1, + src0->sadb_address_prefixlen, + dst0->sadb_address_prefixlen, + src0->sadb_address_proto, + &spidx); + + /* checking the direciton. */ + switch (xpl0->sadb_x_policy_dir) { + case IPSEC_DIR_INBOUND: + case IPSEC_DIR_OUTBOUND: + break; + default: + printf("key_spdadd: Invalid SP direction.\n"); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } + + /* Is there SP in SPD ? */ + newsp = key_getsp(&spidx); + if (newsp != NULL) { + key_freesp(newsp); + printf("key_spdadd: a SP entry exists already.\n"); + msg0->sadb_msg_errno = EEXIST; + return NULL; + } + + /* check policy */ + /* key_spdadd() accepts DISCARD, NONE and IPSEC. */ + if (xpl0->sadb_x_policy_type == IPSEC_POLICY_ENTRUST + || xpl0->sadb_x_policy_type == IPSEC_POLICY_BYPASS) { + printf("key_spdadd: Invalid policy type.\n"); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } + + /* allocation new SP entry */ + if ((newsp = key_msg2sp(xpl0)) == NULL) { + msg0->sadb_msg_errno = ENOBUFS; + return NULL; + } + + KEY_SETSECSPIDX(xpl0->sadb_x_policy_dir, + src0 + 1, + dst0 + 1, + src0->sadb_address_prefixlen, + dst0->sadb_address_prefixlen, + src0->sadb_address_proto, + &newsp->spidx); + + newsp->refcnt = 1; /* do not reclaim until I say I do */ + newsp->state = IPSEC_SPSTATE_ALIVE; + LIST_INSERT_HEAD(&sptree[newsp->spidx.dir], newsp, chain); + + { + struct sadb_msg *newmsg; + u_int len; + caddr_t p; + + /* create new sadb_msg to reply. */ + len = sizeof(struct sadb_msg) + + PFKEY_EXTLEN(mhp[SADB_X_EXT_POLICY]) + + PFKEY_EXTLEN(mhp[SADB_EXT_ADDRESS_SRC]) + + PFKEY_EXTLEN(mhp[SADB_EXT_ADDRESS_DST]); + + KMALLOC(newmsg, struct sadb_msg *, len); + if (newmsg == NULL) { + printf("key_spdadd: No more memory.\n"); + msg0->sadb_msg_errno = ENOBUFS; + return NULL; + } + bzero((caddr_t)newmsg, len); + + bcopy((caddr_t)msg0, (caddr_t)newmsg, sizeof(*msg0)); + newmsg->sadb_msg_errno = 0; + newmsg->sadb_msg_len = PFKEY_UNIT64(len); + p = (caddr_t)newmsg + sizeof(*msg0); + + p = key_setsadbext(p, mhp[SADB_X_EXT_POLICY]); + p = key_setsadbext(p, mhp[SADB_EXT_ADDRESS_SRC]); + p = key_setsadbext(p, mhp[SADB_EXT_ADDRESS_DST]); + + return newmsg; } - prevnp = np; - } - CRITICAL_END; } +/* + * SADB_SPDDELETE processing + * receive + * <base, address(SD), policy(*)> + * from the user(?), and set SADB_SASTATE_DEAD, + * and send, + * <base, address(SD), policy(*)> + * to the ikmpd. + * policy(*) including direction of policy. + * + * IN: mhp: pointer to the pointer to each header. + * OUT: other if success, return pointer to the message to send. + * 0 if fail. + */ +static struct sadb_msg * +key_spddelete(mhp) + caddr_t *mhp; +{ + struct sadb_msg *msg0; + struct sadb_address *src0, *dst0; + struct sadb_x_policy *xpl0; + struct secpolicyindex spidx; + struct secpolicy *sp; + + /* sanity check */ + if (mhp == NULL || mhp[0] == NULL) + panic("key_spddelete: NULL pointer is passed.\n"); + + msg0 = (struct sadb_msg *)mhp[0]; + + if (mhp[SADB_EXT_ADDRESS_SRC] == NULL + || mhp[SADB_EXT_ADDRESS_DST] == NULL + || mhp[SADB_X_EXT_POLICY] == NULL) { + printf("key_spddelete: invalid message is passed.\n"); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } + + src0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_SRC]); + dst0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_DST]); + xpl0 = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY]; + + /* make secindex */ + KEY_SETSECSPIDX(xpl0->sadb_x_policy_dir, + src0 + 1, + dst0 + 1, + src0->sadb_address_prefixlen, + dst0->sadb_address_prefixlen, + src0->sadb_address_proto, + &spidx); + + /* checking the direciton. */ + switch (xpl0->sadb_x_policy_dir) { + case IPSEC_DIR_INBOUND: + case IPSEC_DIR_OUTBOUND: + break; + default: + printf("key_spddelete: Invalid SP direction.\n"); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } -/*---------------------------------------------------------------------- - * key_deleteacquire(): - * Delete an entry from the key_acquirelist - ----------------------------------------------------------------------*/ -static void -key_deleteacquire(type, target) - u_int type; - struct sockaddr *target; -{ - struct key_acquirelist *ap, *prev; - - prev = key_acquirelist; - for(ap = key_acquirelist->next; ap; ap = ap->next) { - if (addrpart_equal(target, (struct sockaddr *)&(ap->target)) && - (type == ap->type)) { - DPRINTF(IDL_EVENT,("Deleting entry from acquire list!\n")); - prev->next = ap->next; - KFREE(ap); - ap = prev; + /* Is there SP in SPD ? */ + if ((sp = key_getsp(&spidx)) == NULL) { + printf("key_spddelete: no SP found.\n"); + msg0->sadb_msg_errno = ENOENT; + return NULL; + } + + sp->state = IPSEC_SPSTATE_DEAD; + key_freesp(sp); + + { + struct sadb_msg *newmsg; + u_int len; + caddr_t p; + + /* create new sadb_msg to reply. */ + len = sizeof(struct sadb_msg) + + PFKEY_EXTLEN(mhp[SADB_X_EXT_POLICY]) + + PFKEY_EXTLEN(mhp[SADB_EXT_ADDRESS_SRC]) + + PFKEY_EXTLEN(mhp[SADB_EXT_ADDRESS_DST]); + + KMALLOC(newmsg, struct sadb_msg *, len); + if (newmsg == NULL) { + printf("key_spddelete: No more memory.\n"); + msg0->sadb_msg_errno = ENOBUFS; + return NULL; + } + bzero((caddr_t)newmsg, len); + + bcopy((caddr_t)mhp[0], (caddr_t)newmsg, sizeof(*msg0)); + newmsg->sadb_msg_errno = 0; + newmsg->sadb_msg_len = PFKEY_UNIT64(len); + p = (caddr_t)newmsg + sizeof(*msg0); + + p = key_setsadbext(p, mhp[SADB_X_EXT_POLICY]); + p = key_setsadbext(p, mhp[SADB_EXT_ADDRESS_SRC]); + p = key_setsadbext(p, mhp[SADB_EXT_ADDRESS_DST]); + + return newmsg; } - prev = ap; - } } +/* + * SADB_SPDFLUSH processing + * receive + * <base> + * from the user, and free all entries in secpctree. + * and send, + * <base> + * to the user. + * NOTE: what to do is only marking SADB_SASTATE_DEAD. + * + * IN: mhp: pointer to the pointer to each header. + * OUT: other if success, return pointer to the message to send. + * 0 if fail. + */ +static struct sadb_msg * +key_spdflush(mhp) + caddr_t *mhp; +{ + struct sadb_msg *msg0; + struct secpolicy *sp; + u_int dir; -/*---------------------------------------------------------------------- - * key_search(): - * Search the key table for an entry with same type, src addr, dest - * addr, and spi. Returns a pointer to struct key_tblnode if found - * else returns null. - ----------------------------------------------------------------------*/ -static struct key_tblnode * -key_search(type, src, dst, spi, indx, prevkeynode) - u_int type; - struct sockaddr *src; - struct sockaddr *dst; - u_int32_t spi; - int indx; - struct key_tblnode **prevkeynode; -{ - struct key_tblnode *keynode, *prevnode; - - if (indx > KEYTBLSIZE || indx < 0) - return (NULL); - if (!(&keytable[indx])) - return (NULL); - -#define sec_type keynode->secassoc->type -#define sec_spi keynode->secassoc->spi -#define sec_src keynode->secassoc->src -#define sec_dst keynode->secassoc->dst - - prevnode = &keytable[indx]; - for (keynode = keytable[indx].next; keynode; keynode = keynode->next) { - if ((type == sec_type) && (spi == sec_spi) && - addrpart_equal(src, sec_src) - && addrpart_equal(dst, sec_dst)) - break; - prevnode = keynode; - } - *prevkeynode = prevnode; - return(keynode); + /* sanity check */ + if (mhp == NULL || mhp[0] == NULL) + panic("key_spdflush: NULL pointer is passed.\n"); + + msg0 = (struct sadb_msg *)mhp[0]; + + for (dir = 0; dir < IPSEC_DIR_MAX; dir++) { + __LIST_FOREACH(sp, &sptree[dir], chain) { + sp->state = IPSEC_SPSTATE_DEAD; + } + } + + { + struct sadb_msg *newmsg; + u_int len; + + /* create new sadb_msg to reply. */ + len = sizeof(struct sadb_msg); + + KMALLOC(newmsg, struct sadb_msg *, len); + if (newmsg == NULL) { + printf("key_spdflush: No more memory.\n"); + msg0->sadb_msg_errno = ENOBUFS; + return NULL; + } + bzero((caddr_t)newmsg, len); + + bcopy((caddr_t)mhp[0], (caddr_t)newmsg, sizeof(*msg0)); + newmsg->sadb_msg_errno = 0; + newmsg->sadb_msg_len = PFKEY_UNIT64(len); + + return(newmsg); + } } +/* + * SADB_SPDDUMP processing + * receive + * <base> + * from the user, and dump all SP leaves + * and send, + * <base> ..... + * to the ikmpd. + * + * IN: mhp: pointer to the pointer to each header. + * OUT: other if success, return pointer to the message to send. + * 0 if fail. + */ +static int +key_spddump(mhp, so, target) + caddr_t *mhp; + struct socket *so; + int target; +{ + struct sadb_msg *msg0; + struct secpolicy *sp; + int len, cnt, cnt_sanity; + struct sadb_msg *newmsg; + u_int dir; + + /* sanity check */ + if (mhp == NULL || mhp[0] == NULL) + panic("key_spddump: NULL pointer is passed.\n"); + + msg0 = (struct sadb_msg *)mhp[0]; + + /* search SPD entry and get buffer size. */ + cnt = cnt_sanity = 0; + for (dir = 0; dir < IPSEC_DIR_MAX; dir++) { + __LIST_FOREACH(sp, &sptree[dir], chain) { + cnt++; + } + } + + if (cnt == 0) + return ENOENT; + + newmsg = NULL; + for (dir = 0; dir < IPSEC_DIR_MAX; dir++) { + __LIST_FOREACH(sp, &sptree[dir], chain) { + len = key_getspmsglen(sp); + + /* making buffer */ + KMALLOC(newmsg, struct sadb_msg *, len); + if (newmsg == NULL) { + printf("key_spddump: No more memory.\n"); + return ENOBUFS; + } + bzero((caddr_t)newmsg, len); + + --cnt; + (void)key_setdumpsp(newmsg, sp, SADB_X_SPDDUMP, + cnt, msg0->sadb_msg_pid); + + key_sendup(so, newmsg, len, target); + KFREE(newmsg); + newmsg = NULL; + } + } + + return 0; +} -/*---------------------------------------------------------------------- - * key_addnode(): - * Insert a key_tblnode entry into the key table. Returns a pointer - * to the newly created key_tblnode. - ----------------------------------------------------------------------*/ -static struct key_tblnode * -key_addnode(indx, secassoc) - int indx; - struct key_secassoc *secassoc; +static u_int +key_setdumpsp(newmsg, sp, type, seq, pid) + struct sadb_msg *newmsg; + struct secpolicy *sp; + u_int8_t type; + u_int32_t seq, pid; { - struct key_tblnode *keynode; + u_int tlen; + caddr_t p; + + tlen = key_getspmsglen(sp); + + p = key_setsadbmsg((caddr_t)newmsg, type, tlen, + SADB_SATYPE_UNSPEC, seq, pid, + IPSEC_MODE_ANY, sp->refcnt); + + p = key_setsadbaddr(p, + SADB_EXT_ADDRESS_SRC, + (struct sockaddr *)&sp->spidx.src, + sp->spidx.prefs, + sp->spidx.ul_proto); + p = key_setsadbaddr(p, + SADB_EXT_ADDRESS_DST, + (struct sockaddr *)&sp->spidx.dst, + sp->spidx.prefd, + sp->spidx.ul_proto); - DPRINTF(IDL_FINISHED,("Entering key_addnode w/indx=%d secassoc=0x%x\n", - indx, (unsigned int)secassoc)); + { + struct sadb_x_policy *tmp; - if (!(&keytable[indx])) - return(NULL); - if (!secassoc) { - panic("key_addnode: Someone passed in a null secassoc!\n"); - } + if ((tmp = key_sp2msg(sp)) == NULL) { + printf("key_setdumpsp: No more memory.\n"); + return ENOBUFS; + } - KMALLOC(keynode, struct key_tblnode *, sizeof(struct key_tblnode)); - if (keynode == 0) - return(NULL); - bzero((char *)keynode, sizeof(struct key_tblnode)); + /* validity check */ + if (key_getspreqmsglen(sp) != PFKEY_UNUNIT64(tmp->sadb_x_policy_len)) + panic("key_setdumpsp: length mismatch." + "sp:%d msg:%d\n", + key_getspreqmsglen(sp), + PFKEY_UNUNIT64(tmp->sadb_x_policy_len)); - KMALLOC(keynode->solist, struct socketlist *, sizeof(struct socketlist)); - if (keynode->solist == 0) { - KFREE(keynode); - return(NULL); - } - bzero((char *)(keynode->solist), sizeof(struct socketlist)); + bcopy(tmp, p, PFKEY_UNUNIT64(tmp->sadb_x_policy_len)); + KFREE(tmp); + } - keynode->secassoc = secassoc; - keynode->solist->next = NULL; - keynode->next = keytable[indx].next; - keytable[indx].next = keynode; - return(keynode); + return tlen; } +/* get sadb message length for a SP. */ +static u_int +key_getspmsglen(sp) + struct secpolicy *sp; +{ + u_int tlen; -/*---------------------------------------------------------------------- - * key_add(): - * Add a new security association to the key table. Caller is - * responsible for allocating memory for the key_secassoc as - * well as the buffer space for the key, iv. Assumes the security - * association passed in is well-formed. - ----------------------------------------------------------------------*/ -int -key_add(secassoc) - struct key_secassoc *secassoc; -{ - char buf[MAXHASHKEYLEN]; - int len, indx; - int inbound = 0; - int outbound = 0; - struct key_tblnode *keynode, *prevkeynode; - struct key_allocnode *np = NULL; - CRITICAL_DCL - - DPRINTF(IDL_FINISHED, ("Entering key_add w/secassoc=0x%x\n", - (unsigned int)secassoc)); - - if (!secassoc) { - panic("key_add: who the hell is passing me a null pointer"); - } + /* sanity check */ + if (sp == NULL) + panic("key_getspmsglen: NULL pointer is passed.\n"); - /* - * Should we allow a null key to be inserted into the table ? - * or can we use null key to indicate some policy action... - */ + tlen = (sizeof(struct sadb_msg) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(_SALENBYAF(sp->spidx.src.__ss_family)) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(_SALENBYAF(sp->spidx.dst.__ss_family))); -#if 0 - /* - * For esp using des-cbc or tripple-des we call - * des_set_odd_parity. - */ - if (secassoc->key && (secassoc->type == KEY_TYPE_ESP) && - ((secassoc->algorithm == IPSEC_ALGTYPE_ESP_DES_CBC) || - (secassoc->algorithm == IPSEC_ALGTYPE_ESP_3DES))) - des_set_odd_parity(secassoc->key); -#endif /* 0 */ - - /* - * initialization for anti-replay services. - */ - secassoc->sequence = 0; - secassoc->replayright = 0; - secassoc->replaywindow = 0; - - /* - * Check if secassoc with same spi exists before adding - */ - bzero((char *)&buf, sizeof(buf)); - len = key_createkey((char *)&buf, secassoc->type, secassoc->src, - secassoc->dst, secassoc->spi, 0); - indx = key_gethashval((char *)&buf, len, KEYTBLSIZE); - DPRINTF(IDL_FINISHED,("keyadd: keytbl hash position=%d\n", indx)); - keynode = key_search(secassoc->type, secassoc->src, secassoc->dst, - secassoc->spi, indx, &prevkeynode); - if (keynode) { - DPRINTF(IDL_EVENT,("keyadd: secassoc already exists!\n")); - return(-2); - } + tlen += key_getspreqmsglen(sp); + + return tlen; +} + +/* + * get PFKEY message length for security policy and request. + */ +static u_int +key_getspreqmsglen(sp) + struct secpolicy *sp; +{ + u_int tlen; + + tlen = sizeof(struct sadb_x_policy); + + /* if is the policy for ipsec ? */ + if (sp->policy != IPSEC_POLICY_IPSEC) + return tlen; + + /* get length of ipsec requests */ + { + struct ipsecrequest *isr; + int len; + + for (isr = sp->req; isr != NULL; isr = isr->next) { + len = sizeof(struct sadb_x_ipsecrequest) + + isr->saidx.src.__ss_len + + isr->saidx.dst.__ss_len; - inbound = my_addr(secassoc->dst); - outbound = my_addr(secassoc->src); - DPRINTF(IDL_FINISHED,("inbound=%d outbound=%d\n", inbound, outbound)); - - /* - * We allocate mem for an allocation entry if needed. - * This is done here instead of in the allocaton code - * segment so that we can easily recover/cleanup from a - * memory allocation error. - */ - if (outbound || (!inbound && !outbound)) { - KMALLOC(np, struct key_allocnode *, sizeof(struct key_allocnode)); - if (np == 0) { - DPRINTF(IDL_ERROR,("keyadd: can't allocate allocnode!\n")); - return(-1); + tlen += PFKEY_ALIGN8(len); + } } - } - CRITICAL_START; + return tlen; +} - if ((keynode = key_addnode(indx, secassoc)) == NULL) { - DPRINTF(IDL_ERROR,("keyadd: key_addnode failed!\n")); - if (np) - KFREE(np); - CRITICAL_END; - return(-1); - } - DPRINTF(IDL_GROSS_EVENT,("Added new keynode:\n")); - DDO(IDL_FINISHED, dump_keytblnode(keynode)); - DDO(IDL_FINISHED, dump_secassoc(keynode->secassoc)); - - /* - * We add an entry to the allocation table for - * this secassoc if the interfaces are up, - * the secassoc is outbound. In the case - * where the interfaces are not up, we go ahead - * , do it anyways. This wastes an allocation - * entry if the secassoc later turned out to be - * inbound when the interfaces are ifconfig up. - */ - if (outbound || (!inbound && !outbound)) { - len = key_createkey((char *)&buf, secassoc->type, secassoc->src, - secassoc->dst, 0, 1); - indx = key_gethashval((char *)&buf, len, KEYALLOCTBLSIZE); - DPRINTF(IDL_FINISHED,("keyadd: keyalloc hash position=%d\n", indx)); - np->keynode = keynode; - np->next = keyalloctbl[indx].next; - keyalloctbl[indx].next = np; - } - if (inbound) - secassoc->state |= K_INBOUND; - if (outbound) - secassoc->state |= K_OUTBOUND; +/* %%% SAD management */ +/* + * allocating a memory for new SA head, and copy from the values of mhp. + * OUT: NULL : failure due to the lack of memory. + * others : pointer to new SA head. + */ +static struct secashead * +key_newsah(saidx) + struct secasindex *saidx; +{ + struct secashead *newsah; + u_int stateidx; - key_deleteacquire(secassoc->type, secassoc->dst); + /* sanity check */ + if (saidx == NULL) + panic("key_newsaidx: NULL pointer is passed.\n"); - CRITICAL_END; - return 0; + KMALLOC(newsah, struct secashead *, sizeof(struct secashead)); + if (newsah == NULL) { + return NULL; + } + bzero((caddr_t)newsah, sizeof(struct secashead)); + + bcopy(saidx, &newsah->saidx, sizeof(newsah->saidx)); + + for (stateidx = 0; + stateidx < _ARRAYLEN(saorder_state_any); + stateidx++) { + LIST_INIT(&newsah->savtree[saorder_state_any[stateidx]]); + } + + /* add to saidxtree */ + newsah->state = SADB_SASTATE_MATURE; + LIST_INSERT_HEAD(&sahtree, newsah, chain); + + return(newsah); } +/* + * delete SA index and all SA registerd. + */ +static void +key_delsah(sah) + struct secashead *sah; +{ + struct secasvar *sav, *nextsav; + u_int stateidx, state; + int s; + + /* sanity check */ + if (sah == NULL) + panic("key_delsah: NULL pointer is passed.\n"); + + s = splnet(); /*called from softclock()*/ + + /* remove from tree of SA index */ + if (__LIST_CHAINED(sah)) + LIST_REMOVE(sah, chain); + + /* searching all SA registerd in the secindex. */ + for (stateidx = 0; + stateidx < _ARRAYLEN(saorder_state_any); + stateidx++) { + + state = saorder_state_any[stateidx]; + for (sav = (struct secasvar *)LIST_FIRST(&sah->savtree[state]); + sav != NULL; + sav = nextsav) { + + nextsav = LIST_NEXT(sav, chain); + + /* sanity check */ + KEY_CHKSASTATE(state, sav->state, "key_delsah"); + + /* remove back pointer */ + sav->sah = NULL; + + if (sav->refcnt < 0) { + printf("key_delsah: why refcnt < 0 ?, " + "sav->refcnt=%d\n", + sav->refcnt); + } + key_freesav(sav); + sav = NULL; + } + } -/*---------------------------------------------------------------------- - * key_get(): - * Get a security association from the key table. - ----------------------------------------------------------------------*/ -int -key_get(type, src, dst, spi, secassoc) - u_int type; - struct sockaddr *src; - struct sockaddr *dst; - u_int32_t spi; - struct key_secassoc **secassoc; -{ - char buf[MAXHASHKEYLEN]; - struct key_tblnode *keynode, *prevkeynode; - int len, indx; - - bzero(&buf, sizeof(buf)); - *secassoc = NULL; - len = key_createkey((char *)&buf, type, src, dst, spi, 0); - indx = key_gethashval((char *)&buf, len, KEYTBLSIZE); - DPRINTF(IDL_FINISHED,("keyget: indx=%d\n",indx)); - keynode = key_search(type, src, dst, spi, indx, &prevkeynode); - if (keynode) { - DPRINTF(IDL_GROSS_EVENT,("keyget: found it! keynode=0x%x", - (unsigned int)keynode)); - *secassoc = keynode->secassoc; - return(0); - } else - return(-1); /* Not found */ -} - - -/*---------------------------------------------------------------------- - * key_dump(): - * Dump all valid entries in the keytable to a pf_key socket. Each - * security associaiton is sent one at a time in a pf_key message. A - * message with seqno = 0 signifies the end of the dump transaction. - ----------------------------------------------------------------------*/ -int -key_dump(so) - struct socket *so; -{ - int len, i; - int seq = 1; - struct key_msgdata keyinfo; - struct key_msghdr *km; - struct key_tblnode *keynode; - int kmlen; - - /* - * Routine to dump the key table to a routing socket - * Use for debugging only! - */ - - kmlen = sizeof(struct key_msghdr) + 3 * MAX_SOCKADDR_SZ + MAX_KEY_SZ - + MAX_IV_SZ; - KMALLOC(km, struct key_msghdr *, kmlen); - if (!km) - return(ENOBUFS); - - DPRINTF(IDL_FINISHED,("Entering key_dump()")); - /* - * We need to speed this up later. Fortunately, key_dump - * messages are not sent often. - */ - for (i = 0; i < KEYTBLSIZE; i++) { - for (keynode = keytable[i].next; keynode; keynode = keynode->next) { - /* - * We exclude dead/larval/zombie security associations for now - * but it may be useful to also send these up for debugging purposes - */ - if (keynode->secassoc->state & (K_DEAD | K_LARVAL | K_ZOMBIE)) - continue; - - len = (sizeof(struct key_msghdr) + - ROUNDUP(keynode->secassoc->src->sa_len) + - ROUNDUP(keynode->secassoc->dst->sa_len) + - ROUNDUP(keynode->secassoc->from->sa_len) + - ROUNDUP(keynode->secassoc->keylen) + - ROUNDUP(keynode->secassoc->ivlen) + - ROUNDUP(keynode->secassoc->ekeylen)); - - if (kmlen < len) { - KFREE(km); - kmlen = len; - KMALLOC(km, struct key_msghdr *, kmlen); - if (!km) - return(ENOBUFS); - } + if (sah->sa_route.ro_rt) { + RTFREE(sah->sa_route.ro_rt); + sah->sa_route.ro_rt = (struct rtentry *)NULL; + } - if (key_secassoc2msghdr(keynode->secassoc, km, &keyinfo) != 0) - panic("key_dump"); + KFREE(sah); - km->key_msglen = len; - km->key_msgvers = KEY_VERSION; - km->key_msgtype = KEY_DUMP; - km->key_pid = curproc->p_pid; - km->key_seq = seq++; - km->key_errno = 0; + splx(s); + return; +} - key_sendup(so, km); - } - } - bzero((char *)km, sizeof(struct key_msghdr)); - km->key_msglen = sizeof(struct key_msghdr); - km->key_msgvers = KEY_VERSION; - km->key_msgtype = KEY_DUMP; - km->key_pid = curproc->p_pid; - km->key_seq = 0; - km->key_errno = 0; - - key_sendup(so, km); - KFREE(km); - DPRINTF(IDL_FINISHED,("Leaving key_dump()\n")); - return(0); -} - -/*---------------------------------------------------------------------- - * key_delete(): - * Delete a security association from the key table. - ----------------------------------------------------------------------*/ -int -key_delete(secassoc) - struct key_secassoc *secassoc; -{ - char buf[MAXHASHKEYLEN]; - int len, indx; - struct key_tblnode *keynode = 0; - struct key_tblnode *prevkeynode = 0; - struct socketlist *socklp, *deadsocklp; - struct key_so2spinode *np, *prevnp; - struct key_allocnode *ap, *prevap; - CRITICAL_DCL - - DPRINTF(IDL_FINISHED,("Entering key_delete w/secassoc=0x%x\n", - (unsigned int)secassoc)); - - bzero((char *)&buf, sizeof(buf)); - len = key_createkey((char *)&buf, secassoc->type, secassoc->src, - secassoc->dst, secassoc->spi, 0); - indx = key_gethashval((char *)&buf, len, KEYTBLSIZE); - DPRINTF(IDL_FINISHED,("keydelete: keytbl hash position=%d\n", indx)); - keynode = key_search(secassoc->type, secassoc->src, secassoc->dst, - secassoc->spi, indx, &prevkeynode); - - if (keynode) { - CRITICAL_START; - DPRINTF(IDL_GROSS_EVENT,("keydelete: found keynode to delete\n")); - keynode->secassoc->state |= K_DEAD; - - if (keynode->ref_count > 0) { - DPRINTF(IDL_EVENT,("keydelete: secassoc still held, marking for deletion only!\n")); - CRITICAL_END; - return(0); - } +/* + * allocating a new SA with LARVAL state. key_add() and key_getspi() call, + * and copy the values of mhp into new buffer. + * When SAD message type is GETSPI: + * to set sequence number from acq_seq++, + * to set zero to SPI. + * not to call key_setsava(). + * OUT: NULL : fail + * others : pointer to new secasvar. + */ +static struct secasvar * +key_newsav(mhp, sah) + caddr_t *mhp; + struct secashead *sah; +{ + struct secasvar *newsav; + struct sadb_msg *msg0; - prevkeynode->next = keynode->next; - - /* - * Walk the socketlist, delete the - * entries mapping sockets to this secassoc - * from the so2spi table. - */ - DPRINTF(IDL_FINISHED,("keydelete: deleting socklist...")); - for(socklp = keynode->solist->next; socklp; ) { - prevnp = &so2spitbl[((u_int32_t)(socklp->socket)) % SO2SPITBLSIZE]; - for(np = prevnp->next; np; np = np->next) { - if ((np->socket == socklp->socket) && (np->keynode == keynode)) { - prevnp->next = np->next; - KFREE(np); - break; - } - prevnp = np; - } - deadsocklp = socklp; - socklp = socklp->next; - KFREE(deadsocklp); - } - DPRINTF(IDL_FINISHED,("done\n")); - /* - * If an allocation entry exist for this - * secassoc, delete it. - */ - bzero((char *)&buf, sizeof(buf)); - len = key_createkey((char *)&buf, secassoc->type, secassoc->src, - secassoc->dst, 0, 1); - indx = key_gethashval((char *)&buf, len, KEYALLOCTBLSIZE); - DPRINTF(IDL_FINISHED,("keydelete: alloctbl hash position=%d\n", indx)); - prevap = &keyalloctbl[indx]; - for (ap = prevap->next; ap; ap = ap->next) { - if (ap->keynode == keynode) { - prevap->next = ap->next; - KFREE(ap); - break; - } - prevap = ap; - } - - if (keynode->secassoc->iv) - KFREE(keynode->secassoc->iv); - if (keynode->secassoc->key) - KFREE(keynode->secassoc->key); - if (keynode->secassoc->ekey) - KFREE(keynode->secassoc->ekey); - KFREE(keynode->secassoc); - if (keynode->solist) - KFREE(keynode->solist); - KFREE(keynode); - CRITICAL_END; - return(0); - } - return(-1); + /* sanity check */ + if (mhp == NULL || mhp[0] == NULL || sah == NULL) + panic("key_newsa: NULL pointer is passed.\n"); + + msg0 = (struct sadb_msg *)mhp[0]; + + KMALLOC(newsav, struct secasvar *, sizeof(struct secasvar)); + if (newsav == NULL) { + printf("key_newsa: No more memory.\n"); + msg0->sadb_msg_errno = ENOBUFS; + return NULL; + } + bzero((caddr_t)newsav, sizeof(struct secasvar)); + + switch (msg0->sadb_msg_type) { + case SADB_GETSPI: + newsav->spi = 0; + + /* sync sequence number */ + if (msg0->sadb_msg_seq == 0) + newsav->seq = + (acq_seq = (acq_seq == ~0 ? 1 : ++acq_seq)); + else + newsav->seq = msg0->sadb_msg_seq; + break; + + case SADB_ADD: + /* sanity check */ + if (mhp[SADB_EXT_SA] == NULL) { + KFREE(newsav); + printf("key_newsa: invalid message is passed.\n"); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } + newsav->spi = ((struct sadb_sa *)mhp[SADB_EXT_SA])->sadb_sa_spi; + newsav->seq = msg0->sadb_msg_seq; + break; + default: + KFREE(newsav); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } + + /* copy sav values */ + if (msg0->sadb_msg_type != SADB_GETSPI && key_setsaval(newsav, mhp)) { + KFREE(newsav); + /* msg0->sadb_msg_errno is set at key_setsaval. */ + return NULL; + } + + /* reset tick */ + newsav->tick = 0; + + newsav->pid = msg0->sadb_msg_pid; + + /* add to satree */ + newsav->sah = sah; + newsav->refcnt = 1; + newsav->state = SADB_SASTATE_LARVAL; + LIST_INSERT_HEAD(&sah->savtree[SADB_SASTATE_LARVAL], newsav, chain); + + return newsav; } +/* + * free() SA variable entry. + */ +static void +key_delsav(sav) + struct secasvar *sav; +{ + /* sanity check */ + if (sav == NULL) + panic("key_delsav: NULL pointer is passed.\n"); + + if (sav->refcnt > 0) return; /* can't free */ + + /* remove from SA header */ + if (__LIST_CHAINED(sav)) + LIST_REMOVE(sav, chain); + + if (sav->key_auth != NULL) + KFREE(sav->key_auth); + if (sav->key_enc != NULL) + KFREE(sav->key_enc); + if (sav->replay != NULL) { + if (sav->replay->bitmap != NULL) + KFREE(sav->replay->bitmap); + KFREE(sav->replay); + } + if (sav->lft_c != NULL) + KFREE(sav->lft_c); + if (sav->lft_h != NULL) + KFREE(sav->lft_h); + if (sav->lft_s != NULL) + KFREE(sav->lft_s); + if (sav->iv != NULL) + KFREE(sav->iv); +#if notyet + if (sav->misc1 != NULL) + KFREE(sav->misc1); + if (sav->misc2 != NULL) + KFREE(sav->misc2); + if (sav->misc3 != NULL) + KFREE(sav->misc3); +#endif -/*---------------------------------------------------------------------- - * key_flush(): - * Delete all entries from the key table. - ----------------------------------------------------------------------*/ -void -key_flush() + sav->sah = NULL; + /* XXX for making sure. See key_checkrequest(), + * Refcnt may be suspicious. */ + + KFREE(sav); + + return; +} + +/* + * search SAD. + * OUT: + * NULL : not found + * others : found, pointer to a SA. + */ +static struct secashead * +key_getsah(saidx) + struct secasindex *saidx; +{ + struct secashead *sah; + + __LIST_FOREACH(sah, &sahtree, chain) { + if (sah->state == SADB_SASTATE_DEAD) + continue; + if (key_cmpsaidx_exactly(&sah->saidx, saidx)) + return(sah); + } + + return NULL; +} + +/* + * check not to be duplicated SPI. + * NOTE: this function is too slow due to searching all SAD. + * OUT: + * NULL : not found + * others : found, pointer to a SA. + */ +static struct secasvar * +key_checkspidup(saidx, spi) + struct secasindex *saidx; + u_int32_t spi; { - struct key_tblnode *keynode; - int i; -#if 1 - int timo; + struct secashead *sah; + struct secasvar *sav; + + /* check address family */ + if (saidx->src.__ss_family != saidx->src.__ss_family) { + printf("key_checkspidup: address family mismatched.\n"); + return NULL; + } + + /* check all SAD */ + __LIST_FOREACH(sah, &sahtree, chain) { + if (!key_ismyaddr(sah->saidx.dst.__ss_family, + _INADDRBYSA(&sah->saidx.dst))) + continue; + sav = key_getsavbyspi(sah, spi); + if (sav != NULL) + return sav; + } + + return NULL; +} + +/* + * search SAD litmited alive SA, protocol, SPI. + * OUT: + * NULL : not found + * others : found, pointer to a SA. + */ +static struct secasvar * +key_getsavbyspi(sah, spi) + struct secashead *sah; + u_int32_t spi; +{ + struct secasvar *sav; + u_int stateidx, state; + + /* search all status */ + for (stateidx = 0; + stateidx < _ARRAYLEN(saorder_state_alive); + stateidx++) { + + state = saorder_state_alive[stateidx]; + __LIST_FOREACH(sav, &sah->savtree[state], chain) { + + /* sanity check */ + if (sav->state != state) { + printf("key_getsavbyspi: " + "invalid sav->state " + "(queue: %d SA: %d)\n", + state, sav->state); + continue; + } + + if (sav->spi == spi) + return sav; + } + } + + return NULL; +} + +/* + * copy SA values from PF_KEY message except *SPI, SEQ, PID, STATE and TYPE*. + * You must update these if need. + * OUT: 0: success. + * 1: failure. set errno to (mhp[0])->sadb_msg_errno. + */ +static int +key_setsaval(sav, mhp) + struct secasvar *sav; + caddr_t *mhp; +{ + struct sadb_msg *msg0; + int error = 0; + + /* sanity check */ + if (mhp == NULL || mhp[0] == NULL) + panic("key_setsaval: NULL pointer is passed.\n"); + + msg0 = (struct sadb_msg *)mhp[0]; + + /* initialization */ + sav->replay = NULL; + sav->key_auth = NULL; + sav->key_enc = NULL; + sav->iv = NULL; + sav->lft_c = NULL; + sav->lft_h = NULL; + sav->lft_s = NULL; + + /* SA */ + if (mhp[SADB_EXT_SA] != NULL) { + struct sadb_sa *sa0 = (struct sadb_sa *)mhp[SADB_EXT_SA]; + + sav->alg_auth = sa0->sadb_sa_auth; + sav->alg_enc = sa0->sadb_sa_encrypt; + sav->flags = sa0->sadb_sa_flags; + + /* replay window */ + if ((sa0->sadb_sa_flags & SADB_X_EXT_OLD) == 0) { + KMALLOC(sav->replay, struct secreplay *, + sizeof(struct secreplay)); + if (sav->replay == NULL) { + printf("key_setsaval: No more memory.\n"); + error = ENOBUFS; + goto err; + } + bzero(sav->replay, sizeof(struct secreplay)); + + if ((sav->replay->wsize = sa0->sadb_sa_replay) != 0) { + KMALLOC(sav->replay->bitmap, caddr_t, + sav->replay->wsize); + if (sav->replay->bitmap == NULL) { + printf("key_setsaval: " + "No more memory.\n"); + error = ENOBUFS; + goto err; + } + bzero(sav->replay->bitmap, sa0->sadb_sa_replay); + } + } + } + + /* Authentication keys */ + if (mhp[SADB_EXT_KEY_AUTH] != NULL) { + struct sadb_key *key0; + u_int len; + + key0 = (struct sadb_key *)mhp[SADB_EXT_KEY_AUTH]; + len = PFKEY_UNUNIT64(key0->sadb_key_len); + + error = 0; + if (len < sizeof(struct sadb_key)) + error = EINVAL; + switch (msg0->sadb_msg_satype) { + case SADB_SATYPE_AH: + case SADB_SATYPE_ESP: + if (len == sizeof(struct sadb_key) + && sav->alg_auth != SADB_AALG_NULL) { + error = EINVAL; + } + break; + case SADB_X_SATYPE_IPCOMP: + error = EINVAL; + break; + default: + error = EINVAL; + break; + } + if (error) { + printf("key_setsaval: invalid key_auth values.\n"); + goto err; + } + + KEY_NEWBUF(sav->key_auth, struct sadb_key *, key0, len); + if (sav->key_auth == NULL) { + printf("key_setsaval: No more memory.\n"); + error = ENOBUFS; + goto err; + } + + /* make length shift up for kernel*/ + sav->key_auth->sadb_key_len = len; + } + + /* Encryption key */ + if (mhp[SADB_EXT_KEY_ENCRYPT] != NULL) { + struct sadb_key *key0; + u_int len; + + key0 = (struct sadb_key *)mhp[SADB_EXT_KEY_ENCRYPT]; + len = PFKEY_UNUNIT64(key0->sadb_key_len); + + error = 0; + if (len < sizeof(struct sadb_key)) + error = EINVAL; + switch (msg0->sadb_msg_satype) { + case SADB_SATYPE_ESP: + if (len == sizeof(struct sadb_key) + && sav->alg_enc != SADB_EALG_NULL) { + error = EINVAL; + } + break; + case SADB_SATYPE_AH: + error = EINVAL; + break; + case SADB_X_SATYPE_IPCOMP: + break; + default: + error = EINVAL; + break; + } + if (error) { + printf("key_setsatval: invalid key_enc value.\n"); + goto err; + } + + KEY_NEWBUF(sav->key_enc, struct sadb_key *, key0, len); + if (sav->key_enc == NULL) { + printf("key_setsaval: No more memory.\n"); + error = ENOBUFS; + goto err; + } + + /* make length shift up for kernel*/ + sav->key_enc->sadb_key_len = len; + } + + /* set iv */ + sav->ivlen = 0; + + switch (msg0->sadb_msg_satype) { + case SADB_SATYPE_ESP: +#ifdef IPSEC_ESP + { + struct esp_algorithm *algo; + + algo = &esp_algorithms[sav->alg_enc]; + if (algo && algo->ivlen) + sav->ivlen = (*algo->ivlen)(sav); + KMALLOC(sav->iv, caddr_t, sav->ivlen); + if (sav->iv == 0) { + printf("key_setsaval: No more memory.\n"); + error = ENOBUFS; + goto err; + } + /* initialize ? */ + break; + } +#else + break; #endif + case SADB_SATYPE_AH: + break; + default: + printf("key_setsaval: invalid SA type.\n"); + error = EINVAL; + goto err; + } - /* - * This is slow, but simple. - */ - DPRINTF(IDL_FINISHED,("Flushing key table...")); - for (i = 0; i < KEYTBLSIZE; i++) { - timo = 0; - while ((keynode = keytable[i].next)) { - if (key_delete(keynode->secassoc) != 0) - panic("key_flush"); - timo++; - if (10000 < timo) { -printf("key_flush: timo exceeds limit; terminate the loop to prevent hangup\n"); - break; - } + /* reset tick */ + sav->tick = 0; + + /* make lifetime for CURRENT */ + { + struct timeval tv; + + KMALLOC(sav->lft_c, struct sadb_lifetime *, + sizeof(struct sadb_lifetime)); + if (sav->lft_c == NULL) { + printf("key_setsaval: No more memory.\n"); + error = ENOBUFS; + goto err; + } + + microtime(&tv); + + sav->lft_c->sadb_lifetime_len = + PFKEY_UNIT64(sizeof(struct sadb_lifetime)); + sav->lft_c->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT; + sav->lft_c->sadb_lifetime_allocations = 0; + sav->lft_c->sadb_lifetime_bytes = 0; + sav->lft_c->sadb_lifetime_addtime = tv.tv_sec; + sav->lft_c->sadb_lifetime_usetime = 0; } - } - DPRINTF(IDL_FINISHED,("done\n")); + + /* lifetimes for HARD and SOFT */ + { + struct sadb_lifetime *lft0; + + lft0 = (struct sadb_lifetime *)mhp[SADB_EXT_LIFETIME_HARD]; + if (lft0 != NULL) { + KEY_NEWBUF(sav->lft_h, struct sadb_lifetime *, + lft0, sizeof(*lft0)); + if (sav->lft_h == NULL) { + printf("key_setsaval: No more memory.\n"); + error = ENOBUFS; + goto err; + } + /* to be initialize ? */ + } + + lft0 = (struct sadb_lifetime *)mhp[SADB_EXT_LIFETIME_SOFT]; + if (lft0 != NULL) { + KEY_NEWBUF(sav->lft_s, struct sadb_lifetime *, + lft0, sizeof(*lft0)); + if (sav->lft_s == NULL) { + printf("key_setsaval: No more memory.\n"); + error = ENOBUFS; + goto err; + } + /* to be initialize ? */ + } + } + + msg0->sadb_msg_errno = 0; + return 0; + + err: + /* initialization */ + if (sav->replay != NULL) { + if (sav->replay->bitmap != NULL) + KFREE(sav->replay->bitmap); + KFREE(sav->replay); + } + if (sav->key_auth != NULL) + KFREE(sav->key_auth); + if (sav->key_enc != NULL) + KFREE(sav->key_enc); + if (sav->iv != NULL) + KFREE(sav->iv); + if (sav->lft_c != NULL) + KFREE(sav->lft_c); + if (sav->lft_h != NULL) + KFREE(sav->lft_h); + if (sav->lft_s != NULL) + KFREE(sav->lft_s); + + msg0->sadb_msg_errno = error; + return 1; } +/* + * get message buffer length. + */ +static u_int +key_getmsglen(sav) + struct secasvar *sav; +{ + int len = sizeof(struct sadb_msg); + + len += sizeof(struct sadb_sa); + len += (sizeof(struct sadb_address) + + PFKEY_ALIGN8(_SALENBYAF(sav->sah->saidx.src.__ss_family))); + len += (sizeof(struct sadb_address) + + PFKEY_ALIGN8(_SALENBYAF(sav->sah->saidx.dst.__ss_family))); + + if (sav->key_auth != NULL) + len += sav->key_auth->sadb_key_len; + if (sav->key_enc != NULL) + len += sav->key_enc->sadb_key_len; + + if (sav->lft_c != NULL) + len += sizeof(struct sadb_lifetime); + if (sav->lft_h != NULL) + len += sizeof(struct sadb_lifetime); + if (sav->lft_s != NULL) + len += sizeof(struct sadb_lifetime); + + return len; +} -/*---------------------------------------------------------------------- - * key_getspi(): - * Get a unique spi value for a key management daemon/program. The - * spi value, once assigned, cannot be assigned again (as long as the - * entry with that same spi value remains in the table). - ----------------------------------------------------------------------*/ -int -key_getspi(type, vers, src, dst, lowval, highval, spi) - u_int type; - u_int vers; - struct sockaddr *src; - struct sockaddr *dst; - u_int32_t lowval; - u_int32_t highval; - u_int32_t *spi; -{ - struct key_secassoc *secassoc; - struct key_tblnode *keynode, *prevkeynode; - int count, done, len, indx; - int maxcount = 1000; - u_int32_t val; - char buf[MAXHASHKEYLEN]; - CRITICAL_DCL - - DPRINTF(IDL_EVENT,("Entering getspi w/type=%d,low=%u,high=%u\n", - type, lowval, highval)); - if (!(src && dst)) - return(EINVAL); - - if ((lowval == 0) || (highval == 0)) - return(EINVAL); - - if (lowval > highval) { - u_int32_t temp; - temp = lowval; - lowval = highval; - highval = lowval; - } +/* + * validation with a secasvar entry, and set SADB_SATYPE_MATURE. + * OUT: 0: valid + * other: errno + */ +static int +key_mature(sav) + struct secasvar *sav; +{ + int mature; + int checkmask = 0; /* 2^0: ealg 2^1: aalg 2^2: calg */ + int mustmask = 0; /* 2^0: ealg 2^1: aalg 2^2: calg */ - done = count = 0; - do { - count++; - /* - * This may not be "random enough". - */ - val = lowval + (random() % (highval - lowval + 1)); - - if (lowval == highval) - count = maxcount; - DPRINTF(IDL_FINISHED,("%u ",val)); - if (val) { - DPRINTF(IDL_FINISHED,("\n")); - bzero(&buf, sizeof(buf)); - len = key_createkey((char *)&buf, type, src, dst, val, 0); - indx = key_gethashval((char *)&buf, len, KEYTBLSIZE); - if (!key_search(type, src, dst, val, indx, &prevkeynode)) { - CRITICAL_START; - KMALLOC(secassoc, struct key_secassoc *, sizeof(struct key_secassoc)); - if (secassoc == 0) { - DPRINTF(IDL_ERROR,("key_getspi: can't allocate memory\n")); - CRITICAL_END; - return(ENOBUFS); - } - bzero((char *)secassoc, sizeof(*secassoc)); - - DPRINTF(IDL_FINISHED,("getspi: indx=%d\n",indx)); - secassoc->len = sizeof(struct key_secassoc); - secassoc->type = type; - secassoc->vers = vers; - secassoc->spi = val; - secassoc->state |= K_LARVAL; - if (my_addr(dst)) - secassoc->state |= K_INBOUND; - if (my_addr(src)) - secassoc->state |= K_OUTBOUND; - - KMALLOC(secassoc->src, struct sockaddr *, src->sa_len); - if (!secassoc->src) { - DPRINTF(IDL_ERROR,("key_getspi: can't allocate memory\n")); - KFREE(secassoc); - CRITICAL_END; - return(ENOBUFS); - } - bcopy((char *)src, (char *)secassoc->src, src->sa_len); - KMALLOC(secassoc->dst, struct sockaddr *, dst->sa_len); - if (!secassoc->dst) { - DPRINTF(IDL_ERROR,("key_getspi: can't allocate memory\n")); - KFREE(secassoc->src); - KFREE(secassoc); - CRITICAL_END; - return(ENOBUFS); - } - bcopy((char *)dst, (char *)secassoc->dst, dst->sa_len); - - /* We fill this in with a plausable value now to insure - that other routines don't break. These will get - overwritten later with the correct values. */ -#if 0 -#ifdef INET6 - secassoc->from->sa_family = AF_INET6; - secassoc->from->sa_len = sizeof(struct sockaddr_in6); -#else /* INET6 */ - secassoc->from->sa_family = AF_INET; - secassoc->from->sa_len = sizeof(struct sockaddr_in); -#endif /* INET6 */ + mature = 0; + + /* check SPI value */ + if (ntohl(sav->spi) >= 0 && ntohl(sav->spi) <= 255) { + printf("key_mature: illegal range of SPI %d.\n", sav->spi); + return EINVAL; + } + + /* check satype */ + switch (sav->sah->saidx.proto) { + case IPPROTO_ESP: + /* check flags */ + if ((sav->flags & SADB_X_EXT_OLD) + && (sav->flags & SADB_X_EXT_DERIV)) { + printf("key_mature: " + "invalid flag (derived) given to old-esp.\n"); + return EINVAL; + } + checkmask = 3; + mustmask = 1; + break; + case IPPROTO_AH: + /* check flags */ + if (sav->flags & SADB_X_EXT_DERIV) { + printf("key_mature: " + "invalid flag (derived) given to AH SA.\n"); + return EINVAL; + } + if (sav->alg_enc != SADB_EALG_NONE) { + printf("key_mature: " + "protocol and algorithm mismated.\n"); + return(EINVAL); + } + checkmask = 2; + mustmask = 2; + break; + default: + printf("key_mature: Invalid satype.\n"); + return EPROTONOSUPPORT; + } + + /* check authentication algorithm */ + if ((checkmask & 2) != 0) { + struct ah_algorithm *algo; + int keylen; + + /* XXX: should use algorithm map to check. */ + switch (sav->alg_auth) { + case SADB_AALG_NONE: + case SADB_AALG_MD5HMAC: + case SADB_AALG_SHA1HMAC: + case SADB_AALG_MD5: + case SADB_AALG_SHA: + case SADB_AALG_NULL: + break; + default: + printf("key_mature: " + "unknown authentication algorithm.\n"); + return EINVAL; + } + + /* algorithm-dependent check */ + algo = &ah_algorithms[sav->alg_auth]; + + if (sav->key_auth) + keylen = sav->key_auth->sadb_key_bits; + else + keylen = 0; + if (keylen < algo->keymin || algo->keymax < keylen) { + printf("key_mature: invalid AH key length %d " + "(%d-%d allowed)\n", keylen, + algo->keymin, algo->keymax); + return EINVAL; + } + + if (algo->mature) { + if ((*algo->mature)(sav)) { + /* message generated in per-algorithm function*/ + return EINVAL; + } else + mature = SADB_SATYPE_AH; + } + + if ((mustmask & 2) != 0 && mature != SADB_SATYPE_AH) + return EINVAL; + } + + /* check encryption algorithm */ + if ((checkmask & 1) != 0) { +#ifdef IPSEC_ESP + struct esp_algorithm *algo; + int keylen; + + switch (sav->alg_enc) { + case SADB_EALG_NONE: + case SADB_EALG_DESCBC: + case SADB_EALG_3DESCBC: + case SADB_EALG_NULL: + case SADB_EALG_BLOWFISHCBC: + case SADB_EALG_CAST128CBC: + case SADB_EALG_RC5CBC: + break; + default: + printf("key_mature: unknown encryption algorithm.\n"); + return(EINVAL); + } + + /* algorithm-dependent check */ + algo = &esp_algorithms[sav->alg_enc]; + + if (sav->key_enc) + keylen = sav->key_enc->sadb_key_bits; + else + keylen = 0; + if (keylen < algo->keymin || algo->keymax < keylen) { + printf("key_mature: invalid ESP key length %d " + "(%d-%d allowed)\n", keylen, + algo->keymin, algo->keymax); + return EINVAL; + } + + if (algo->mature) { + if ((*algo->mature)(sav)) { + /* message generated in per-algorithm function*/ + return EINVAL; + } else + mature = SADB_SATYPE_ESP; + } + + if ((mustmask & 1) != 0 && mature != SADB_SATYPE_ESP) + return EINVAL; +#else + printf("key_mature: ESP not supported in this configuration\n"); + return EINVAL; #endif + } - /* - * We need to add code to age these larval key table - * entries so they don't linger forever waiting for - * a KEY_UPDATE message that may not come for various - * reasons. This is another task that key_reaper can - * do once we have it coded. - */ - secassoc->lifetime1 += time_second + maxlarvallifetime; - - if (!(keynode = key_addnode(indx, secassoc))) { - DPRINTF(IDL_ERROR,("key_getspi: can't add node\n")); - CRITICAL_END; - return(ENOBUFS); - } - DPRINTF(IDL_FINISHED,("key_getspi: added node 0x%x\n", - (unsigned int)keynode)); - done++; - CRITICAL_END; - } - } - } while ((count < maxcount) && !done); - DPRINTF(IDL_EVENT,("getspi returns w/spi=%u,count=%d\n",val,count)); - if (done) { - *spi = val; - return(0); - } else { - *spi = 0; - return(EADDRNOTAVAIL); - } + key_sa_chgstate(sav, SADB_SASTATE_MATURE); + + return 0; } +/* + * subroutine for SADB_GET and SADB_DUMP. + * the buf must be allocated sufficent space. + */ +static u_int +key_setdumpsa(newmsg, sav, type, satype, seq, pid) + struct sadb_msg *newmsg; + struct secasvar *sav; + u_int8_t type, satype; + u_int32_t seq, pid; +{ + u_int tlen; + caddr_t p; + int i; + + tlen = key_getmsglen(sav); + + p = key_setsadbmsg((caddr_t)newmsg, type, tlen, + satype, seq, pid, + sav->sah->saidx.mode, sav->refcnt); + + for (i = 1; i <= SADB_EXT_MAX; i++) { + switch (i) { + case SADB_EXT_SA: + p = key_setsadbsa(p, sav); + break; + + case SADB_EXT_ADDRESS_SRC: + p = key_setsadbaddr(p, + SADB_EXT_ADDRESS_SRC, + (struct sockaddr *)&sav->sah->saidx.src, + _INALENBYAF(sav->sah->saidx.src.__ss_family) << 3, + IPSEC_ULPROTO_ANY); + break; + + case SADB_EXT_ADDRESS_DST: + p = key_setsadbaddr(p, + SADB_EXT_ADDRESS_DST, + (struct sockaddr *)&sav->sah->saidx.dst, + _INALENBYAF(sav->sah->saidx.dst.__ss_family) << 3, + IPSEC_ULPROTO_ANY); + break; + + case SADB_EXT_KEY_AUTH: + { + u_int len; + if (sav->key_auth == NULL) break; + len = sav->key_auth->sadb_key_len; /* real length */ + bcopy((caddr_t)sav->key_auth, p, len); + ((struct sadb_ext *)p)->sadb_ext_len = PFKEY_UNIT64(len); + p += len; + } + break; + + case SADB_EXT_KEY_ENCRYPT: + { + u_int len; + if (sav->key_enc == NULL) break; + len = sav->key_enc->sadb_key_len; /* real length */ + bcopy((caddr_t)sav->key_enc, p, len); + ((struct sadb_ext *)p)->sadb_ext_len = PFKEY_UNIT64(len); + p += len; + } + break;; + + case SADB_EXT_LIFETIME_CURRENT: + if (sav->lft_c == NULL) break; + p = key_setsadbext(p, (caddr_t)sav->lft_c); + break; + + case SADB_EXT_LIFETIME_HARD: + if (sav->lft_h == NULL) break; + p = key_setsadbext(p, (caddr_t)sav->lft_h); + break; + + case SADB_EXT_LIFETIME_SOFT: + if (sav->lft_s == NULL) break; + p = key_setsadbext(p, (caddr_t)sav->lft_s); + break; + + case SADB_EXT_IDENTITY_SRC: + case SADB_EXT_IDENTITY_DST: + /* XXX: should we brought from SPD ? */ + case SADB_EXT_SENSITIVITY: + default: + break; + } + } -/*---------------------------------------------------------------------- - * key_update(): - * Update a keytable entry that has an spi value assigned but is - * incomplete (e.g. no key/iv). - ----------------------------------------------------------------------*/ -int -key_update(secassoc) - struct key_secassoc *secassoc; -{ - struct key_tblnode *keynode, *prevkeynode; - struct key_allocnode *np = 0; - u_int8_t newstate; - int len, indx, inbound, outbound; - char buf[MAXHASHKEYLEN]; - CRITICAL_DCL - - bzero(&buf, sizeof(buf)); - len = key_createkey((char *)&buf, secassoc->type, secassoc->src, - secassoc->dst, secassoc->spi, 0); - indx = key_gethashval((char *)&buf, len, KEYTBLSIZE); - if(!(keynode = key_search(secassoc->type, secassoc->src, secassoc->dst, - secassoc->spi, indx, &prevkeynode))) { - return(ESRCH); - } - if (keynode->secassoc->state & K_DEAD) - return(ESRCH); + return tlen; +} - /* Should we also restrict updating of only LARVAL entries ? */ +/* + * set data into sadb_msg. + * `buf' must has been allocated sufficiently. + */ +static caddr_t +key_setsadbmsg(buf, type, tlen, satype, seq, pid, reserved1, reserved2) + caddr_t buf; + u_int8_t type, satype; + u_int16_t tlen; + u_int32_t seq; + pid_t pid; + u_int8_t reserved1; + u_int8_t reserved2; +{ + struct sadb_msg *p; + u_int len; + + p = (struct sadb_msg *)buf; + len = sizeof(struct sadb_msg); + + bzero(p, len); + p->sadb_msg_version = PF_KEY_V2; + p->sadb_msg_type = type; + p->sadb_msg_errno = 0; + p->sadb_msg_satype = satype; + p->sadb_msg_len = PFKEY_UNIT64(tlen); + p->sadb_msg_mode = reserved1; + p->sadb_msg_reserved = reserved2; + p->sadb_msg_seq = seq; + p->sadb_msg_pid = (u_int32_t)pid; + + return(buf + len); +} - CRITICAL_START; +/* + * copy secasvar data into sadb_address. + * `buf' must has been allocated sufficiently. + */ +static caddr_t +key_setsadbsa(buf, sav) + caddr_t buf; + struct secasvar *sav; +{ + struct sadb_sa *p; + u_int len; + + p = (struct sadb_sa *)buf; + len = sizeof(struct sadb_sa); + + bzero(p, len); + p->sadb_sa_len = PFKEY_UNIT64(len); + p->sadb_sa_exttype = SADB_EXT_SA; + p->sadb_sa_spi = sav->spi; + p->sadb_sa_replay = (sav->replay != NULL ? sav->replay->wsize : 0); + p->sadb_sa_state = sav->state; + p->sadb_sa_auth = sav->alg_auth; + p->sadb_sa_encrypt = sav->alg_enc; + p->sadb_sa_flags = sav->flags; + + return(buf + len); +} - inbound = my_addr(secassoc->dst); - outbound = my_addr(secassoc->src); +/* + * set data into sadb_address. + * `buf' must has been allocated sufficiently. + */ +static caddr_t +key_setsadbaddr(buf, exttype, saddr, prefixlen, ul_proto) + caddr_t buf; + u_int16_t exttype; + struct sockaddr *saddr; + u_int8_t prefixlen; + u_int16_t ul_proto; +{ + struct sadb_address *p; + u_int len; - newstate = keynode->secassoc->state; - newstate &= ~K_LARVAL; + p = (struct sadb_address *)buf; + len = sizeof(struct sadb_address) + PFKEY_ALIGN8(saddr->sa_len); - if (inbound) - newstate |= K_INBOUND; - if (outbound) - newstate |= K_OUTBOUND; + bzero(p, len); + p->sadb_address_len = PFKEY_UNIT64(len); + p->sadb_address_exttype = exttype; + p->sadb_address_proto = ul_proto; + p->sadb_address_prefixlen = prefixlen; + p->sadb_address_reserved = 0; - if (outbound || (!inbound && !outbound)) { - KMALLOC(np, struct key_allocnode *, sizeof(struct key_allocnode)); - if (np == 0) { - DPRINTF(IDL_ERROR,("keyupdate: can't allocate allocnode!\n")); - CRITICAL_END; - return(ENOBUFS); - } - } + bcopy(saddr, p + 1, saddr->sa_len); - /* - * Free the old key, iv if they're there. - */ - if (keynode->secassoc->key) - KFREE(keynode->secassoc->key); - if (keynode->secassoc->iv) - KFREE(keynode->secassoc->iv); - if (keynode->secassoc->ekey) - KFREE(keynode->secassoc->ekey); - - /* - * We now copy the secassoc over. We don't need to copy - * the key, iv into new buffers since the calling routine - * does that already. - */ - - *(keynode->secassoc) = *secassoc; - keynode->secassoc->state = newstate; - - /* - * Should we allow a null key to be inserted into the table ? - * or can we use null key to indicate some policy action... - */ - -#if 0 - if (keynode->secassoc->key && - (keynode->secassoc->type == KEY_TYPE_ESP) && - ((keynode->secassoc->algorithm == IPSEC_ALGTYPE_ESP_DES_CBC) || - (keynode->secassoc->algorithm == IPSEC_ALGTYPE_ESP_3DES))) - des_set_odd_parity(keynode->secassoc->key); -#endif /* 0 */ - - /* - * We now add an entry to the allocation table for this - * updated key table entry. - */ - if (outbound || (!inbound && !outbound)) { - len = key_createkey((char *)&buf, secassoc->type, secassoc->src, - secassoc->dst, 0, 1); - indx = key_gethashval((char *)&buf, len, KEYALLOCTBLSIZE); - DPRINTF(IDL_FINISHED,("keyupdate: keyalloc hash position=%d\n", indx)); - np->keynode = keynode; - np->next = keyalloctbl[indx].next; - keyalloctbl[indx].next = np; - } + return(buf + len); +} + +/* + * set data into sadb_ident. + * `buf' must has been allocated sufficiently. + */ +static caddr_t +key_setsadbident(buf, exttype, idtype, string, stringlen, id) + caddr_t buf; + u_int16_t exttype, idtype; + caddr_t string; + int stringlen; + u_int64_t id; +{ + struct sadb_ident *p; + u_int len; + + p = (struct sadb_ident *)buf; + len = sizeof(struct sadb_ident) + PFKEY_ALIGN8(stringlen); - key_deleteacquire(secassoc->type, (struct sockaddr *)&(secassoc->dst)); + bzero(p, len); + p->sadb_ident_len = PFKEY_UNIT64(len); + p->sadb_ident_exttype = exttype; + p->sadb_ident_type = idtype; + p->sadb_ident_reserved = 0; + p->sadb_ident_id = id; - CRITICAL_END; - return(0); + bcopy(string, p + 1, stringlen); + + return(buf + len); } -/*---------------------------------------------------------------------- - * key_register(): - * Register a socket as one capable of acquiring security associations - * for the kernel. - ----------------------------------------------------------------------*/ -int -key_register(socket, type) - struct socket *socket; - u_int type; -{ - struct key_registry *p, *new; - CRITICAL_DCL - - CRITICAL_START; - - DPRINTF(IDL_EVENT,("Entering key_register w/so=0x%x,type=%d\n", - (unsigned int)socket,type)); - - if (!(keyregtable && socket)) - panic("key_register"); - - /* - * Make sure entry is not already in table - */ - for(p = keyregtable->next; p; p = p->next) { - if ((p->type == type) && (p->socket == socket)) { - CRITICAL_END; - return(EEXIST); - } - } +/* + * copy buffer of any sadb extension type into sadb_ext. + * assume that sadb_ext_len shifted down >> 3. + * i.e. shift length up when setting length of extension. + */ +static caddr_t +key_setsadbext(p, ext) + caddr_t p, ext; +{ + u_int len; - KMALLOC(new, struct key_registry *, sizeof(struct key_registry)); - if (new == 0) { - CRITICAL_END; - return(ENOBUFS); - } - new->type = type; - new->socket = socket; - new->next = keyregtable->next; - keyregtable->next = new; - CRITICAL_END; - return(0); -} - -/*---------------------------------------------------------------------- - * key_unregister(): - * Delete entries from the registry list. - * allflag = 1 : delete all entries with matching socket - * allflag = 0 : delete only the entry matching socket, type - ----------------------------------------------------------------------*/ -void -key_unregister(socket, type, allflag) - struct socket *socket; - u_int type; - int allflag; -{ - struct key_registry *p, *prev; - CRITICAL_DCL - - CRITICAL_START; - - DPRINTF(IDL_EVENT,("Entering key_unregister w/so=0x%x,type=%d,flag=%d\n", - (unsigned int)socket, type, allflag)); - - if (!(keyregtable && socket)) - panic("key_register"); - prev = keyregtable; - for(p = keyregtable->next; p; p = p->next) { - if ((allflag && (p->socket == socket)) || - ((p->type == type) && (p->socket == socket))) { - prev->next = p->next; - KFREE(p); - p = prev; - } - prev = p; - } - CRITICAL_END; + len = PFKEY_UNUNIT64(((struct sadb_ext *)ext)->sadb_ext_len); + + bcopy(ext, p, len); + + return(p + len); } +/* %%% utilities */ +/* + * copy a buffer into the new buffer allocated. + */ +static void * +key_newbuf(src, len) + void *src; + u_int len; +{ + caddr_t new; -/*---------------------------------------------------------------------- - * key_acquire(): - * Send a key_acquire message to all registered key mgnt daemons - * capable of acquire security association of type type. - * - * Return: 0 if succesfully called key mgnt. daemon(s) - * -1 if not successfull. - ----------------------------------------------------------------------*/ + KMALLOC(new, caddr_t, len); + if (new == NULL) { + printf("key_newbuf: No more memory.\n"); + return NULL; + } + bcopy((caddr_t)src, new, len); + + return new; +} + +/* compare my own address + * OUT: 1: true, i.e. my address. + * 0: false + */ int -key_acquire(type, src, dst) - u_int type; - struct sockaddr *src; - struct sockaddr *dst; -{ - struct key_registry *p; - struct key_acquirelist *ap, *prevap; - int success = 0, created = 0; - u_int etype; - struct key_msghdr *km = NULL; - int len; - - DPRINTF(IDL_EVENT,("Entering key_acquire()\n")); - - if (!keyregtable || !src || !dst) - return (-1); - - /* - * We first check the acquirelist to see if a key_acquire - * message has been sent for this destination. - */ - etype = type; - prevap = key_acquirelist; - for(ap = key_acquirelist->next; ap; ap = ap->next) { - if (addrpart_equal(dst, ap->target) && - (etype == ap->type)) { - DPRINTF(IDL_EVENT,("acquire message previously sent!\n")); - if (ap->expiretime < time_second) { - DPRINTF(IDL_EVENT,("acquire message has expired!\n")); - ap->count = 0; - break; - } - if (ap->count < maxkeyacquire) { - DPRINTF(IDL_EVENT,("max acquire messages not yet exceeded!\n")); - break; - } - return(0); - } else if (ap->expiretime < time_second) { - /* - * Since we're already looking at the list, we may as - * well delete expired entries as we scan through the list. - * This should really be done by a function like key_reaper() - * but until we code key_reaper(), this is a quick, dirty - * hack. - */ - DPRINTF(IDL_EVENT,("found an expired entry...deleting it!\n")); - prevap->next = ap->next; - KFREE(ap); - ap = prevap; - } - prevap = ap; - } +key_ismyaddr(family, addr) + u_int family; + caddr_t addr; +{ + /* sanity check */ + if (addr == NULL) + panic("key_ismyaddr: NULL pointer is passed.\n"); + + switch (family) { + case AF_INET: + { + struct in_ifaddr *ia; + + for (ia = in_ifaddrhead.tqh_first; ia; + ia = ia->ia_link.tqe_next) + if (bcmp(addr, + (caddr_t)&ia->ia_addr.sin_addr, + _INALENBYAF(family)) == 0) + return 1; + } + break; +#ifdef INET6 + case AF_INET6: + return key_ismyaddr6(addr); +#endif + } - /* - * Scan registry, send KEY_ACQUIRE message to - * appropriate key management daemons. - */ - for(p = keyregtable->next; p; p = p->next) { - if (p->type != type) - continue; - - if (!created) { - len = sizeof(struct key_msghdr) + ROUNDUP(src->sa_len) + - ROUNDUP(dst->sa_len); - KMALLOC(km, struct key_msghdr *, len); - if (!km) { - DPRINTF(IDL_ERROR,("key_acquire: no memory\n")); - return(-1); - } - DPRINTF(IDL_FINISHED,("key_acquire/created: 1\n")); - bzero((char *)km, len); - km->key_msglen = len; - km->key_msgvers = KEY_VERSION; - km->key_msgtype = KEY_ACQUIRE; - km->type = type; - DPRINTF(IDL_FINISHED,("key_acquire/created: 2\n")); - /* - * This is inefficient, slow. - */ - - /* - * We zero out sin_zero here for AF_INET addresses because - * ip_output() currently does not do it for performance reasons. - */ - if (src->sa_family == AF_INET) - bzero((char *)(((struct sockaddr_in *)src)->sin_zero), - sizeof(((struct sockaddr_in *)src)->sin_zero)); - if (dst->sa_family == AF_INET) - bzero((char *)(((struct sockaddr_in *)dst)->sin_zero), - sizeof(((struct sockaddr_in *)dst)->sin_zero)); - - bcopy((char *)src, (char *)(km + 1), src->sa_len); - bcopy((char *)dst, (char *)((int)(km + 1) + ROUNDUP(src->sa_len)), - dst->sa_len); - DPRINTF(IDL_FINISHED,("key_acquire/created: 3\n")); - created++; - } - if (key_sendup(p->socket, km)) - success++; - } + return 0; +} + +#ifdef INET6 +/* + * compare my own address for IPv6. + * 1: ours + * 0: other + * NOTE: derived ip6_input() in KAME. This is necessary to modify more. + */ +#include <netinet6/in6.h> +#include <netinet6/in6_var.h> - if (km) - KFREE(km); - - /* - * Update the acquirelist - */ - if (success) { - if (!ap) { - DPRINTF(IDL_EVENT,("Adding new entry in acquirelist\n")); - KMALLOC(ap, struct key_acquirelist *, - sizeof(struct key_acquirelist) + dst->sa_len); - if (ap == 0) - return(success ? 0 : -1); - bzero((char *)ap, sizeof(struct key_acquirelist)); - ap->target = (struct sockaddr *)(ap + 1); - bcopy((char *)dst, (char *)ap->target, dst->sa_len); - ap->type = etype; - ap->next = key_acquirelist->next; - key_acquirelist->next = ap; - } - DPRINTF(IDL_GROSS_EVENT,("Updating acquire counter, expiration time\n")); - ap->count++; - ap->expiretime = time_second + maxacquiretime; - } - DPRINTF(IDL_EVENT,("key_acquire: done! success=%d\n",success)); - return(success ? 0 : -1); -} - -/*---------------------------------------------------------------------- - * key_alloc(): - * Allocate a security association to a socket. A socket requesting - * unique keying (per-socket keying) is assigned a security assocation - * exclusively for its use. Sockets not requiring unique keying are - * assigned the first security association which may or may not be - * used by another socket. - ----------------------------------------------------------------------*/ static int -key_alloc(type, src, dst, socket, unique_key, keynodep) - u_int type; - struct sockaddr *src; - struct sockaddr *dst; - struct socket *socket; - u_int unique_key; - struct key_tblnode **keynodep; -{ - struct key_tblnode *keynode; - char buf[MAXHASHKEYLEN]; - struct key_allocnode *np, *prevnp; - struct key_so2spinode *newnp; - int len; - int indx; - - DPRINTF(IDL_FINISHED,("Entering key_alloc w/type=%u!\n",type)); - if (!(src && dst)) { - DPRINTF(IDL_ERROR,("key_alloc: received null src or dst!\n")); - return(-1); - } +key_ismyaddr6(addr) + caddr_t addr; +{ + struct in6_addr *a = (struct in6_addr *)addr; + struct in6_ifaddr *ia; + + for (ia = in6_ifaddr; ia; ia = ia->ia_next) { + if (bcmp(addr, (caddr_t)&ia->ia_addr.sin6_addr, + _INALENBYAF(AF_INET6)) == 0) { + return 1; + } + + /* XXX Multicast */ + { + struct in6_multi *in6m = 0; + + IN6_LOOKUP_MULTI(*(struct in6_addr *)addr, ia->ia_ifp, in6m); + if (in6m) + return 1; + } + } - /* - * Search key allocation table - */ - bzero((char *)&buf, sizeof(buf)); - len = key_createkey((char *)&buf, type, src, dst, 0, 1); - indx = key_gethashval((char *)&buf, len, KEYALLOCTBLSIZE); - -#define np_type np->keynode->secassoc->type -#define np_state np->keynode->secassoc->state -#define np_src np->keynode->secassoc->src -#define np_dst np->keynode->secassoc->dst - - prevnp = &keyalloctbl[indx]; - for (np = keyalloctbl[indx].next; np; np = np->next) { - if ((type == np_type) && addrpart_equal(src, np_src) && - addrpart_equal(dst, np_dst) && - !(np_state & (K_LARVAL | K_DEAD | K_UNIQUE))) { - if (!(unique_key)) - break; - if (!(np_state & K_USED)) - break; - } - prevnp = np; - } + /* loopback, just for safety */ + if (IN6_IS_ADDR_LOOPBACK(a)) + return 1; - if (np) { - struct socketlist *newsp; - CRITICAL_DCL + /* XXX anycast */ - CRITICAL_START; + return 0; +} +#endif /*INET6*/ - DPRINTF(IDL_EVENT,("key_alloc: found node to allocate\n")); - keynode = np->keynode; +/* + * compare two secasindex structure exactly. + * IN: + * saidx0: source, it can be in SAD. + * saidx1: object, it can be from SPD. + * OUT: + * 1 : equal + * 0 : not equal + */ +static int +key_cmpsaidx_exactly(saidx0, saidx1) + struct secasindex *saidx0, *saidx1; +{ + /* sanity */ + if (saidx0 == NULL && saidx1 == NULL) + return 1; - KMALLOC(newnp, struct key_so2spinode *, sizeof(struct key_so2spinode)); - if (newnp == 0) { - DPRINTF(IDL_ERROR,("key_alloc: Can't alloc mem for so2spi node!\n")); - CRITICAL_END; - return(ENOBUFS); - } - KMALLOC(newsp, struct socketlist *, sizeof(struct socketlist)); - if (newsp == 0) { - DPRINTF(IDL_ERROR,("key_alloc: Can't alloc mem for socketlist!\n")); - if (newnp) - KFREE(newnp); - CRITICAL_END; - return(ENOBUFS); - } + if (saidx0 == NULL || saidx1 == NULL) + return 0; - /* - * Add a hash entry into the so2spi table to - * map socket to allocated secassoc. - */ - DPRINTF(IDL_FINISHED,("key_alloc: adding entry to so2spi table...")); - newnp->keynode = keynode; - newnp->socket = socket; - newnp->next = so2spitbl[((u_int32_t)socket) % SO2SPITBLSIZE].next; - so2spitbl[((u_int32_t)socket) % SO2SPITBLSIZE].next = newnp; - DPRINTF(IDL_FINISHED,("done\n")); - - if (unique_key) { - /* - * Need to remove the allocation entry - * since the secassoc is now unique, - * can't be allocated to any other socket - */ - DPRINTF(IDL_EVENT,("key_alloc: making keynode unique...")); - keynode->secassoc->state |= K_UNIQUE; - prevnp->next = np->next; - KFREE(np); - DPRINTF(IDL_EVENT,("done\n")); - } - keynode->secassoc->state |= K_USED; - keynode->secassoc->state |= K_OUTBOUND; - keynode->alloc_count++; - - /* - * Add socket to list of socket using secassoc. - */ - DPRINTF(IDL_FINISHED,("key_alloc: adding so to solist...")); - newsp->socket = socket; - newsp->next = keynode->solist->next; - keynode->solist->next = newsp; - DPRINTF(IDL_FINISHED,("done\n")); - *keynodep = keynode; - CRITICAL_END; - return(0); - } - *keynodep = NULL; - return(0); -} - - -/*---------------------------------------------------------------------- - * key_free(): - * Decrement the refcount for a key table entry. If the entry is - * marked dead,, the refcount is zero, we go ahead, delete it. - ----------------------------------------------------------------------*/ -void -key_free(keynode) - struct key_tblnode *keynode; -{ - DPRINTF(IDL_GROSS_EVENT,("Entering key_free w/keynode=0x%x\n", - (unsigned int)keynode)); - if (!keynode) { - DPRINTF(IDL_ERROR,("Warning: key_free got null pointer\n")); - return; - } - (keynode->ref_count)--; - if (keynode->ref_count < 0) { - DPRINTF(IDL_ERROR,("Warning: key_free decremented refcount to %d\n",keynode->ref_count)); - } - if ((keynode->secassoc->state & K_DEAD) && (keynode->ref_count <= 0)) { - DPRINTF(IDL_GROSS_EVENT,("key_free: calling key_delete\n")); - key_delete(keynode->secassoc); - } -} + if (saidx0->proto != saidx1->proto + || saidx0->mode != saidx1->mode) + return 0; -/*---------------------------------------------------------------------- - * getassocbyspi(): - * Get a security association for a given type, src, dst,, spi. - * - * Returns: 0 if sucessfull - * -1 if error/not found - * - * Caller must convert spi to host order. Function assumes spi is - * in host order! - ----------------------------------------------------------------------*/ -int -getassocbyspi(type, src, dst, spi, keyentry) - u_int type; - struct sockaddr *src; - struct sockaddr *dst; - u_int32_t spi; - struct key_tblnode **keyentry; -{ - char buf[MAXHASHKEYLEN]; - int len, indx; - struct key_tblnode *keynode, *prevkeynode = 0; - - DPRINTF(IDL_FINISHED,("Entering getassocbyspi w/type=%u spi=%u\n",type,spi)); - - *keyentry = NULL; - bzero(&buf, sizeof(buf)); - len = key_createkey((char *)&buf, type, src, dst, spi, 0); - indx = key_gethashval((char *)&buf, len, KEYTBLSIZE); - DPRINTF(IDL_FINISHED,("getassocbyspi: indx=%d\n",indx)); - DDO(IDL_FINISHED,dump_sockaddr(src);dump_sockaddr(dst)); - keynode = key_search(type, src, dst, spi, indx, &prevkeynode); - DPRINTF(IDL_FINISHED,("getassocbyspi: keysearch ret=0x%x\n", - (unsigned int)keynode)); - if (keynode && !(keynode->secassoc->state & (K_DEAD | K_LARVAL))) { - DPRINTF(IDL_GROSS_EVENT,("getassocbyspi: found secassoc!\n")); - (keynode->ref_count)++; - keynode->secassoc->state |= K_USED; - *keyentry = keynode; - } else { - DPRINTF(IDL_EVENT,("getassocbyspi: secassoc not found!\n")); - return (-1); - } - return(0); + if (bcmp(&saidx0->src, &saidx1->src, saidx0->src.__ss_len) != 0 + || bcmp(&saidx0->dst, &saidx1->dst, saidx0->dst.__ss_len) != 0) + return 0; + + return 1; } +/* + * compare two secasindex structure with consideration mode. + * don't compare port. + * IN: + * saidx0: source, it is often in SAD. + * saidx1: object, it is often from SPD. + * OUT: + * 1 : equal + * 0 : not equal + */ +static int +key_cmpsaidx_withmode(saidx0, saidx1) + struct secasindex *saidx0, *saidx1; +{ + /* sanity */ + if (saidx0 == NULL && saidx1 == NULL) + return 1; -/*---------------------------------------------------------------------- - * getassocbysocket(): - * Get a security association for a given type, src, dst,, socket. - * If not found, try to allocate one. - * Returns: 0 if successfull - * -1 if error condition/secassoc not found (*keyentry = NULL) - * 1 if secassoc temporarily unavailable (*keynetry = NULL) - * (e.g., key mgnt. daemon(s) called) - ----------------------------------------------------------------------*/ -int -getassocbysocket(type, src, dst, socket, unique_key, keyentry) - u_int type; - struct sockaddr *src; - struct sockaddr *dst; - struct socket *socket; - u_int unique_key; - struct key_tblnode **keyentry; -{ - struct key_tblnode *keynode = 0; - struct key_so2spinode *np; - u_int realtype; - - DPRINTF(IDL_FINISHED,("Entering getassocbysocket w/type=%u so=0x%x\n", - type,(unsigned int)socket)); - - /* - * We treat esp-transport mode, esp-tunnel mode - * as a single type in the keytable. This has a side - * effect that socket using both esp-transport, - * esp-tunnel will use the same security association - * for both modes. Is this a problem? - */ - realtype = type; - if ((np = key_sosearch(type, src, dst, socket))) { - if (np->keynode && np->keynode->secassoc && - !(np->keynode->secassoc->state & (K_DEAD | K_LARVAL))) { - DPRINTF(IDL_FINISHED,("getassocbysocket: found secassoc!\n")); - (np->keynode->ref_count)++; - *keyentry = np->keynode; - return(0); - } - } + if (saidx0 == NULL || saidx1 == NULL) + return 0; - /* - * No secassoc has been allocated to socket, - * so allocate one, if available - */ - DPRINTF(IDL_GROSS_EVENT,("getassocbyso: can't find it, trying to allocate!\n")); - if (key_alloc(realtype, src, dst, socket, unique_key, &keynode) == 0) { - if (keynode) { - DPRINTF(IDL_GROSS_EVENT,("getassocbyso: key_alloc found secassoc!\n")); - keynode->ref_count++; - *keyentry = keynode; - return(0); - } else { - /* - * Kick key mgnt. daemon(s) - * (this should be done in ipsec_output_policy() instead or - * selectively called based on a flag value) - */ - DPRINTF(IDL_FINISHED,("getassocbyso: calling key mgnt daemons!\n")); - *keyentry = NULL; - if (key_acquire(realtype, src, dst) == 0) - return (1); - else - return(-1); + if (saidx0->proto != saidx1->proto + || saidx0->src.__ss_family != saidx1->src.__ss_family + || saidx0->dst.__ss_family != saidx1->dst.__ss_family) + return 0; + + if (saidx0->mode != IPSEC_MODE_ANY + && saidx0->mode != saidx1->mode) + return 0; + + { + int sa_len = _INALENBYAF(saidx0->src.__ss_family); + + if (bcmp(_INADDRBYSA(&saidx0->src), _INADDRBYSA(&saidx1->src), sa_len) + || bcmp(_INADDRBYSA(&saidx0->dst), _INADDRBYSA(&saidx1->dst), sa_len)) + return 0; } - } - *keyentry = NULL; - return(-1); + + return 1; } -/*---------------------------------------------------------------------- - * key_xdata(): - * Parse message buffer for src/dst/from/iv/key if parseflag = 0 - * else parse for src/dst only. - ----------------------------------------------------------------------*/ +/* + * compare two secindex structure exactly. + * IN: + * spidx0: source, it is often in SPD. + * spidx1: object, it is often from PFKEY message. + * OUT: + * 1 : equal + * 0 : not equal + */ static int -key_xdata(km, kip, parseflag) - struct key_msghdr *km; - struct key_msgdata *kip; - int parseflag; +key_cmpspidx_exactly(spidx0, spidx1) + struct secpolicyindex *spidx0, *spidx1; { - char *cp, *cpmax; + /* sanity */ + if (spidx0 == NULL && spidx1 == NULL) + return 1; - if (!km || (km->key_msglen <= 0)) - return (-1); + if (spidx0 == NULL || spidx1 == NULL) + return 0; - cp = (caddr_t)(km + 1); - cpmax = (caddr_t)km + km->key_msglen; + if (spidx0->prefs != spidx1->prefs + || spidx0->prefd != spidx1->prefd + || spidx0->ul_proto != spidx1->ul_proto) + return 0; - /* - * Assumes user process passes message with - * correct word alignment. - */ + if (bcmp(&spidx0->src, &spidx1->src, spidx0->src.__ss_len) != 0 + || bcmp(&spidx0->dst, &spidx1->dst, spidx0->dst.__ss_len) != 0) + return 0; - /* - * Need to clean up this code later. - */ + return 1; +} - /* Grab src addr */ - kip->src = (struct sockaddr *)cp; - if (!kip->src->sa_len) { - DPRINTF(IDL_MAJOR_EVENT,("key_xdata couldn't parse src addr\n")); - return(-1); - } +/* + * compare two secindex structure with mask. + * IN: + * spidx0: source, it is often in SPD. + * spidx1: object, it is often from IP header. + * OUT: + * 1 : equal + * 0 : not equal + */ +static int +key_cmpspidx_withmask(spidx0, spidx1) + struct secpolicyindex *spidx0, *spidx1; +{ + /* sanity */ + if (spidx0 == NULL && spidx1 == NULL) + return 1; - ADVANCE(cp, kip->src->sa_len); + if (spidx0 == NULL || spidx1 == NULL) + return 0; - /* Grab dest addr */ - kip->dst = (struct sockaddr *)cp; - if (!kip->dst->sa_len) { - DPRINTF(IDL_MAJOR_EVENT,("key_xdata couldn't parse dest addr\n")); - return(-1); - } + if (spidx0->src.__ss_family != spidx1->src.__ss_family + || spidx0->dst.__ss_family != spidx1->dst.__ss_family) + return 0; - ADVANCE(cp, kip->dst->sa_len); - if (parseflag == 1) { - kip->from = 0; - kip->key = kip->iv = kip->ekey = 0; - kip->keylen = kip->ivlen = kip->ekeylen = 0; - return(0); - } - - /* Grab from addr */ - kip->from = (struct sockaddr *)cp; - if (!kip->from->sa_len) { - DPRINTF(IDL_MAJOR_EVENT,("key_xdata couldn't parse from addr\n")); - return(-1); - } + /* if spidx.ul_proto == IPSEC_ULPROTO_ANY, ignore. */ + if (spidx0->ul_proto != (u_int16_t)IPSEC_ULPROTO_ANY + && spidx0->ul_proto != spidx1->ul_proto) + return 0; - ADVANCE(cp, kip->from->sa_len); - - /* Grab key */ - if ((kip->keylen = km->keylen)) { - kip->key = cp; - ADVANCE(cp, km->keylen); - } else - kip->key = 0; + if (_INPORTBYSA(&spidx0->src) != IPSEC_PORT_ANY + && _INPORTBYSA(&spidx0->src) != _INPORTBYSA(&spidx1->src)) + return 0; - /* Grab iv */ - if ((kip->ivlen = km->ivlen)) { - kip->iv = cp; - ADVANCE(cp, km->ivlen); - } else - kip->iv = 0; + if (_INPORTBYSA(&spidx0->dst) != IPSEC_PORT_ANY + && _INPORTBYSA(&spidx0->dst) != _INPORTBYSA(&spidx1->dst)) + return 0; - /* Grab ekey */ - if ((kip->ekeylen = km->ekeylen)) { - kip->ekey = cp; - ADVANCE(cp, km->ekeylen); - } else - kip->ekey = 0; + if (!key_bbcmp(_INADDRBYSA(&spidx0->src), + _INADDRBYSA(&spidx1->src), + spidx0->prefs)) + return 0; - return (0); -} + if (!key_bbcmp(_INADDRBYSA(&spidx0->dst), + _INADDRBYSA(&spidx1->dst), + spidx0->prefd)) + return 0; + /* XXX Do we check other field ? e.g. flowinfo, scope_id. */ -int -key_parse(kmp, so, dstfamily) - struct key_msghdr **kmp; - struct socket *so; - int *dstfamily; + return 1; +} + +/* + * compare two buffers with mask. + * IN: + * addr1: source + * addr2: object + * bits: Number of bits to compare + * OUT: + * 1 : equal + * 0 : not equal + */ +static int +key_bbcmp(p1, p2, bits) + register caddr_t p1, p2; + register u_int bits; { - int error = 0, keyerror = 0; - struct key_msgdata keyinfo; - struct key_secassoc *secassoc = NULL; - struct key_msghdr *km = *kmp; + u_int8_t mask; - DPRINTF(IDL_MAJOR_EVENT, ("Entering key_parse\n")); + /* XXX: This could be considerably faster if we compare a word + * at a time, but it is complicated on LSB Endian machines */ -#define senderr(e) \ - { error = (e); goto flush; } + /* Handle null pointers */ + if (p1 == NULL || p2 == NULL) + return (p1 == p2); - if (km->key_msgvers != KEY_VERSION) { - DPRINTF(IDL_CRITICAL,("keyoutput: Unsupported key message version!\n")); - senderr(EPROTONOSUPPORT); - } + while (bits >= 8) { + if (*p1++ != *p2++) + return 0; + bits -= 8; + } - km->key_pid = curproc->p_pid; + if (bits > 0) { + mask = ~((1<<(8-bits))-1); + if ((*p1 & mask) != (*p2 & mask)) + return 0; + } + return 1; /* Match! */ +} - DDO(IDL_MAJOR_EVENT, printf("keymsghdr:\n"); dump_keymsghdr(km)); +/* + * time handler. + * scanning SPD and SAD to check status for each entries, + * and do to remove or to expire. + */ +void +key_timehandler(void) +{ + u_int dir; + int s; - /* - * Parse buffer for src addr, dest addr, from addr, key, iv - */ - bzero((char *)&keyinfo, sizeof(keyinfo)); + s = splnet(); /*called from softclock()*/ - switch (km->key_msgtype) { - case KEY_ADD: - DPRINTF(IDL_MAJOR_EVENT,("key_output got KEY_ADD msg\n")); + /* SPD */ + { + struct secpolicy *sp, *nextsp; - if (key_xdata(km, &keyinfo, 0) < 0) - goto parsefail; + for (dir = 0; dir < IPSEC_DIR_MAX; dir++) { + for (sp = LIST_FIRST(&sptree[dir]); + sp != NULL; + sp = nextsp) { - /* - * Allocate the secassoc structure to insert - * into key table here. - */ - KMALLOC(secassoc, struct key_secassoc *, sizeof(struct key_secassoc)); - if (secassoc == 0) { - DPRINTF(IDL_CRITICAL,("keyoutput: No more memory!\n")); - senderr(ENOBUFS); - } + nextsp = LIST_NEXT(sp, chain); - if (key_msghdr2secassoc(secassoc, km, &keyinfo) < 0) { - DPRINTF(IDL_CRITICAL,("keyoutput: key_msghdr2secassoc failed!\n")); - KFREE(secassoc); - senderr(EINVAL); + if (sp->state == IPSEC_SPSTATE_DEAD) + key_freesp(sp); + } + } } - DPRINTF(IDL_MAJOR_EVENT,("secassoc to add:\n")); - DDO(IDL_MAJOR_EVENT,dump_secassoc(secassoc)); - - if ((keyerror = key_add(secassoc)) != 0) { - DPRINTF(IDL_CRITICAL,("keyoutput: key_add failed\n")); - if (secassoc->key) - KFREE(secassoc->key); - if (secassoc->iv) - KFREE(secassoc->iv); - if (secassoc->ekey) - KFREE(secassoc->ekey); - KFREE(secassoc); - if (keyerror == -2) { - senderr(EEXIST); - } else { - senderr(ENOBUFS); - } + + /* SAD */ + { + struct secashead *sah, *nextsah; + struct secasvar *sav, *nextsav; + + for (sah = LIST_FIRST(&sahtree); + sah != NULL; + sah = nextsah) { + + nextsah = LIST_NEXT(sah, chain); + + /* if sah has been dead, then delete it and process next sah. */ + if (sah->state == SADB_SASTATE_DEAD) { + key_delsah(sah); + continue; + } + + /* if LARVAL entry doesn't become MATURE, delete it. */ + for (sav = LIST_FIRST(&sah->savtree[SADB_SASTATE_LARVAL]); + sav != NULL; + sav = nextsav) { + + nextsav = LIST_NEXT(sav, chain); + + sav->tick++; + + if (key_larval_lifetime < sav->tick) { + key_freesav(sav); + } + } + + /* + * check MATURE entry to start to send expire message + * whether or not. + */ + for (sav = LIST_FIRST(&sah->savtree[SADB_SASTATE_MATURE]); + sav != NULL; + sav = nextsav) { + + nextsav = LIST_NEXT(sav, chain); + + sav->tick++; + + /* we don't need to check. */ + if (sav->lft_s == NULL) + continue; + + /* sanity check */ + if (sav->lft_c == NULL) { + printf("key_timehandler: " + "There is no CURRENT time, why?\n"); + continue; + } + + /* compare SOFT lifetime and tick */ + if (sav->lft_s->sadb_lifetime_addtime != 0 + && sav->lft_s->sadb_lifetime_addtime < sav->tick) { + /* + * check SA to be used whether or not. + * when SA hasn't been used, delete it. + */ + if (sav->lft_c->sadb_lifetime_usetime == 0) { + key_sa_chgstate(sav, SADB_SASTATE_DEAD); + key_freesav(sav); + sav = NULL; + } else { + key_sa_chgstate(sav, SADB_SASTATE_DYING); + /* + * XXX If we keep to send expire + * message in the status of + * DYING. Do remove below code. + */ + key_expire(sav); + } + } + /* check SOFT lifetime by bytes */ + /* + * XXX I don't know the way to delete this SA + * when new SA is installed. Caution when it's + * installed too big lifetime by time. + */ + else if (sav->lft_s->sadb_lifetime_bytes != 0 + && sav->lft_s->sadb_lifetime_bytes < sav->lft_c->sadb_lifetime_bytes) { + + key_sa_chgstate(sav, SADB_SASTATE_DYING); + /* + * XXX If we keep to send expire + * message in the status of + * DYING. Do remove below code. + */ + key_expire(sav); + } + } + + /* check DYING entry to change status to DEAD. */ + for (sav = LIST_FIRST(&sah->savtree[SADB_SASTATE_DYING]); + sav != NULL; + sav = nextsav) { + + nextsav = LIST_NEXT(sav, chain); + + sav->tick++; + + /* we don't need to check. */ + if (sav->lft_h == NULL) + continue; + + /* sanity check */ + if (sav->lft_c == NULL) { + printf("key_timehandler: " + "There is no CURRENT time, why?\n"); + continue; + } + + /* compare HARD lifetime and tick */ + if (sav->lft_h->sadb_lifetime_addtime != 0 + && sav->lft_h->sadb_lifetime_addtime < sav->tick) { + key_sa_chgstate(sav, SADB_SASTATE_DEAD); + key_freesav(sav); + sav = NULL; + } + /* check HARD lifetime by bytes */ + else if (sav->lft_h->sadb_lifetime_bytes != 0 + && sav->lft_h->sadb_lifetime_bytes < sav->lft_c->sadb_lifetime_bytes) { + key_sa_chgstate(sav, SADB_SASTATE_DEAD); + key_freesav(sav); + sav = NULL; + } + } + + /* delete entry in DEAD */ + for (sav = LIST_FIRST(&sah->savtree[SADB_SASTATE_DEAD]); + sav != NULL; + sav = nextsav) { + + nextsav = LIST_NEXT(sav, chain); + + /* sanity check */ + if (sav->state != SADB_SASTATE_DEAD) { + printf("key_timehandler: " + "invalid sav->state " + "(queue: %d SA: %d): " + "kill it anyway\n", + SADB_SASTATE_DEAD, sav->state); + } + + /* + * do not call key_freesav() here. + * sav should already be freed, and sav->refcnt + * shows other references to sav + * (such as from SPD). + */ + } + } } - break; - case KEY_DELETE: - DPRINTF(IDL_MAJOR_EVENT,("key_output got KEY_DELETE msg\n")); - if (key_xdata(km, &keyinfo, 1) < 0) - goto parsefail; +#ifndef IPSEC_NONBLOCK_ACQUIRE + /* ACQ tree */ + { + struct secacq *acq, *nextacq; - KMALLOC(secassoc, struct key_secassoc *, sizeof(struct key_secassoc)); - if (secassoc == 0) { - senderr(ENOBUFS); - } - if (key_msghdr2secassoc(secassoc, km, &keyinfo) < 0) { - KFREE(secassoc); - senderr(EINVAL); - } - if (key_delete(secassoc) != 0) { - if (secassoc->iv) - KFREE(secassoc->iv); - if (secassoc->key) - KFREE(secassoc->key); - if (secassoc->ekey) - KFREE(secassoc->ekey); - KFREE(secassoc); - senderr(ESRCH); - } - if (secassoc->iv) - KFREE(secassoc->iv); - if (secassoc->key) - KFREE(secassoc->key); - if (secassoc->ekey) - KFREE(secassoc->ekey); - KFREE(secassoc); - break; - case KEY_UPDATE: - DPRINTF(IDL_EVENT,("key_output got KEY_UPDATE msg\n")); - - if (key_xdata(km, &keyinfo, 0) < 0) - goto parsefail; - - KMALLOC(secassoc, struct key_secassoc *, sizeof(struct key_secassoc)); - if (secassoc == 0) { - senderr(ENOBUFS); - } - if (key_msghdr2secassoc(secassoc, km, &keyinfo) < 0) { - KFREE(secassoc); - senderr(EINVAL); - } - if ((keyerror = key_update(secassoc)) != 0) { - DPRINTF(IDL_CRITICAL,("Error updating key entry\n")); - if (secassoc->iv) - KFREE(secassoc->iv); - if (secassoc->key) - KFREE(secassoc->key); - if (secassoc->ekey) - KFREE(secassoc->ekey); - KFREE(secassoc); - senderr(keyerror); - } - KFREE(secassoc); - break; - case KEY_GET: - DPRINTF(IDL_EVENT,("key_output got KEY_GET msg\n")); - - if (key_xdata(km, &keyinfo, 1) < 0) - goto parsefail; - - if (key_get(km->type, (struct sockaddr *)keyinfo.src, - (struct sockaddr *)keyinfo.dst, - km->spi, &secassoc) != 0) { - DPRINTF(IDL_EVENT,("keyoutput: can't get key\n")); - senderr(ESRCH); + for (acq = LIST_FIRST(&acqtree); + acq != NULL; + acq = nextacq) { + + nextacq = LIST_NEXT(acq, chain); + + acq->tick++; + + if (key_blockacq_lifetime < acq->tick && __LIST_CHAINED(acq)) { + LIST_REMOVE(acq, chain); + KFREE(acq); + } + } } +#endif - if (secassoc) { - int newlen; - - DPRINTF(IDL_EVENT,("keyoutput: Found secassoc!\n")); - newlen = sizeof(struct key_msghdr) + ROUNDUP(secassoc->src->sa_len) + - ROUNDUP(secassoc->dst->sa_len) + ROUNDUP(secassoc->from->sa_len) + - ROUNDUP(secassoc->keylen) + ROUNDUP(secassoc->ivlen) + - ROUNDUP(secassoc->ekeylen); - DPRINTF(IDL_EVENT,("keyoutput: newlen=%d\n", newlen)); - if (newlen > km->key_msglen) { - struct key_msghdr *newkm; - - DPRINTF(IDL_EVENT,("keyoutput: Allocating new buffer!\n")); - KMALLOC(newkm, struct key_msghdr *, newlen); - if (newkm == 0) { - senderr(ENOBUFS); - } - bcopy((char *)km, (char *)newkm, km->key_msglen); - DPRINTF(IDL_FINISHED,("keyoutput: 1\n")); - KFREE(km); - *kmp = km = newkm; - DPRINTF(IDL_CRITICAL, ("km->key_msglen = %d, newlen = %d\n", - km->key_msglen, newlen)); - km->key_msglen = newlen; - } - DPRINTF(IDL_FINISHED,("keyoutput: 2\n")); - if (key_secassoc2msghdr(secassoc, km, &keyinfo)) { - DPRINTF(IDL_CRITICAL,("keyoutput: Can't create msghdr!\n")); - senderr(EINVAL); + /* initialize random seed */ + if (key_tick_init_random++ > key_int_random) { + key_tick_init_random = 0; + key_srandom(); + } + +#ifndef IPSEC_DEBUG2 + /* do exchange to tick time !! */ + (void)timeout((void *)key_timehandler, (void *)0, 100); +#endif /* IPSEC_DEBUG2 */ + + splx(s); + return; +} + +/* + * to initialize a seed for random() + */ +void +key_srandom() +{ + struct timeval tv; + + microtime(&tv); + srandom(tv.tv_usec); + + return; +} + +/* + * map SADB_SATYPE_* to IPPROTO_*. + * if satype == SADB_SATYPE then satype is mapped to ~0. + * OUT: + * 0: invalid satype. + */ +static u_int16_t +key_satype2proto(satype) + u_int8_t satype; +{ + switch (satype) { + case SADB_SATYPE_UNSPEC: + return IPSEC_PROTO_ANY; + case SADB_SATYPE_AH: + return IPPROTO_AH; + case SADB_SATYPE_ESP: + return IPPROTO_ESP; + default: + return 0; + } + /* NOTREACHED */ +} + +/* + * map IPPROTO_* to SADB_SATYPE_* + * OUT: + * 0: invalid protocol type. + */ +static u_int8_t +key_proto2satype(proto) + u_int16_t proto; +{ + switch (proto) { + case IPPROTO_AH: + return SADB_SATYPE_AH; + case IPPROTO_ESP: + return SADB_SATYPE_ESP; + default: + return 0; + } + /* NOTREACHED */ +} + +/* %%% PF_KEY */ +/* + * SADB_GETSPI processing is to receive + * <base, src address, dst address, (SPI range)> + * from the IKMPd, to assign a unique spi value, to hang on the INBOUND + * tree with the status of LARVAL, and send + * <base, SA(*), address(SD)> + * to the IKMPd. + * + * IN: mhp: pointer to the pointer to each header. + * OUT: NULL if fail. + * other if success, return pointer to the message to send. + */ +static struct sadb_msg * +key_getspi(mhp) + caddr_t *mhp; +{ + struct sadb_msg *msg0; + struct sadb_address *src0, *dst0; + struct secasindex saidx; + struct secashead *newsah; + struct secasvar *newsav; + u_int8_t proto; + u_int32_t spi; + + /* sanity check */ + if (mhp == NULL || mhp[0] == NULL) + panic("key_getspi: NULL pointer is passed.\n"); + + msg0 = (struct sadb_msg *)mhp[0]; + + if (mhp[SADB_EXT_ADDRESS_SRC] == NULL + || mhp[SADB_EXT_ADDRESS_DST] == NULL) { + printf("key_getspi: invalid message is passed.\n"); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } + + src0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_SRC]); + dst0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_DST]); + + /* map satype to proto */ + if ((proto = key_satype2proto(msg0->sadb_msg_satype)) == 0) { + printf("key_getspi: invalid satype is passed.\n"); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } + + KEY_SETSECASIDX(proto, msg0->sadb_msg_mode, src0+1, dst0+1, &saidx); + + /* SPI allocation */ + spi = key_do_getnewspi((struct sadb_spirange *)mhp[SADB_EXT_SPIRANGE], + &saidx); + if (spi == 0) { + msg0->sadb_msg_errno = EEXIST; + return NULL; + } + + /* get a SA index */ + if ((newsah = key_getsah(&saidx)) == NULL) { + + /* create a new SA index */ + if ((newsah = key_newsah(&saidx)) == NULL) { + printf("key_getspi: No more memory.\n"); + msg0->sadb_msg_errno = ENOBUFS; + return NULL; + } + } + + /* get a new SA */ + if ((newsav = key_newsav(mhp, newsah)) == NULL) { + msg0->sadb_msg_errno = ENOBUFS; + /* XXX don't free new SA index allocated in above. */ + return NULL; + } + + /* set spi */ + newsav->spi = htonl(spi); + +#ifndef IPSEC_NONBLOCK_ACQUIRE + /* delete the entry in acqtree */ + if (msg0->sadb_msg_seq != 0) { + struct secacq *acq; + if ((acq = key_getacqbyseq(msg0->sadb_msg_seq)) != NULL) { + /* reset counter in order to deletion by timehander. */ + acq->tick = key_blockacq_lifetime; + acq->count = 0; + } + } +#endif + + { + struct sadb_msg *newmsg; + u_int len; + caddr_t p; + + /* create new sadb_msg to reply. */ + len = sizeof(struct sadb_msg) + + sizeof(struct sadb_sa) + + PFKEY_EXTLEN(mhp[SADB_EXT_ADDRESS_SRC]) + + PFKEY_EXTLEN(mhp[SADB_EXT_ADDRESS_DST]); + + KMALLOC(newmsg, struct sadb_msg *, len); + if (newmsg == NULL) { + printf("key_getspi: No more memory.\n"); + msg0->sadb_msg_errno = ENOBUFS; + return NULL; + } + bzero((caddr_t)newmsg, len); + + bcopy((caddr_t)mhp[0], (caddr_t)newmsg, sizeof(*msg0)); + newmsg->sadb_msg_seq = newsav->seq; + newmsg->sadb_msg_errno = 0; + newmsg->sadb_msg_len = PFKEY_UNIT64(len); + p = (caddr_t)newmsg + sizeof(*msg0); + + { + struct sadb_sa *m_sa; + m_sa = (struct sadb_sa *)p; + m_sa->sadb_sa_len = PFKEY_UNIT64(sizeof(struct sadb_sa)); + m_sa->sadb_sa_exttype = SADB_EXT_SA; + m_sa->sadb_sa_spi = htonl(spi); + p += sizeof(struct sadb_sa); } - DPRINTF(IDL_FINISHED,("keyoutput: 3\n")); - } - break; - case KEY_GETSPI: - DPRINTF(IDL_EVENT,("key_output got KEY_GETSPI msg\n")); - - if (key_xdata(km, &keyinfo, 1) < 0) - goto parsefail; - - if ((keyerror = key_getspi(km->type, km->vers, keyinfo.src, keyinfo.dst, - km->lifetime1, km->lifetime2, - &(km->spi))) != 0) { - DPRINTF(IDL_CRITICAL,("keyoutput: getspi failed error=%d\n", keyerror)); - senderr(keyerror); + + p = key_setsadbext(p, mhp[SADB_EXT_ADDRESS_SRC]); + p = key_setsadbext(p, mhp[SADB_EXT_ADDRESS_DST]); + + return newmsg; } - break; - case KEY_REGISTER: - DPRINTF(IDL_EVENT,("key_output got KEY_REGISTER msg\n")); - key_register(so, km->type); - break; - case KEY_DUMP: - DPRINTF(IDL_EVENT,("key_output got KEY_DUMP msg\n")); - error = key_dump(so); - return(error); - break; - case KEY_FLUSH: - DPRINTF(IDL_EVENT,("key_output got KEY_FLUSH msg\n")); - key_flush(); - break; - default: - DPRINTF(IDL_CRITICAL,("key_output got unsupported msg type=%d\n", - km->key_msgtype)); - senderr(EOPNOTSUPP); - } +} - goto flush; +/* + * allocating new SPI + * called by key_getspi(). + * OUT: + * 0: failure. + * others: success. + */ +static u_int32_t +key_do_getnewspi(spirange, saidx) + struct sadb_spirange *spirange; + struct secasindex *saidx; +{ + u_int32_t newspi; + u_int32_t min, max; + int count = key_spi_trycnt; + + /* set spi range to allocate */ + if (spirange != NULL) { + min = spirange->sadb_spirange_min; + max = spirange->sadb_spirange_max; + } else { + min = key_spi_minval; + max = key_spi_maxval; + } -parsefail: - keyinfo.dst = NULL; - error = EINVAL; + if (min == max) { + if (key_checkspidup(saidx, min) != NULL) { + printf("key_do_getnewspi: SPI %u exists already.\n", min); + return 0; + } -flush: - if (km) - km->key_errno = error; + count--; /* taking one cost. */ + newspi = min; - if (dstfamily) - *dstfamily = keyinfo.dst ? keyinfo.dst->sa_family : 0; + } else { - DPRINTF(IDL_MAJOR_EVENT, ("key_parse exiting with error=%d\n", error)); - return error; + /* init SPI */ + newspi = 0; + + /* when requesting to allocate spi ranged */ + while (count--) { + /* generate pseudo-random SPI value ranged. */ + newspi = min + (random() % ( max - min + 1 )); + + if (key_checkspidup(saidx, newspi) == NULL) + break; + } + + if (count == 0 || newspi == 0) { + printf("key_do_getnewspi: to allocate spi is failed.\n"); + return 0; + } + } + + /* statistics */ + keystat.getspi_count = + (keystat.getspi_count + key_spi_trycnt - count) / 2; + + return newspi; } /* - * Definitions of protocols supported in the KEY domain. + * SADB_UPDATE processing + * receive + * <base, SA, (lifetime(HSC),) address(SD), (address(P),) + * key(AE), (identity(SD),) (sensitivity)> + * from the ikmpd, and update a secasvar entry whose status is SADB_SASTATE_LARVAL. + * and send + * <base, SA, (lifetime(HSC),) address(SD), (address(P),) + * (identity(SD),) (sensitivity)> + * to the ikmpd. + * + * IN: mhp: pointer to the pointer to each header. + * OUT: NULL if fail. + * other if success, return pointer to the message to send. */ +static struct sadb_msg * +key_update(mhp) + caddr_t *mhp; +{ + struct sadb_msg *msg0; + struct sadb_sa *sa0; + struct sadb_address *src0, *dst0; + struct secasindex saidx; + struct secashead *sah; + struct secasvar *sav; + u_int16_t proto; + + /* sanity check */ + if (mhp == NULL || mhp[0] == NULL) + panic("key_update: NULL pointer is passed.\n"); + + msg0 = (struct sadb_msg *)mhp[0]; + + /* map satype to proto */ + if ((proto = key_satype2proto(msg0->sadb_msg_satype)) == 0) { + printf("key_update: invalid satype is passed.\n"); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } -struct sockaddr key_addr = { 2, PF_KEY, }; -struct sockproto key_proto = { PF_KEY, }; + if (mhp[SADB_EXT_SA] == NULL + || mhp[SADB_EXT_ADDRESS_SRC] == NULL + || mhp[SADB_EXT_ADDRESS_DST] == NULL + || (msg0->sadb_msg_satype == SADB_SATYPE_ESP + && mhp[SADB_EXT_KEY_ENCRYPT] == NULL) + || (msg0->sadb_msg_satype == SADB_SATYPE_AH + && mhp[SADB_EXT_KEY_AUTH] == NULL) + || (mhp[SADB_EXT_LIFETIME_HARD] != NULL + && mhp[SADB_EXT_LIFETIME_SOFT] == NULL) + || (mhp[SADB_EXT_LIFETIME_HARD] == NULL + && mhp[SADB_EXT_LIFETIME_SOFT] != NULL)) { + printf("key_update: invalid message is passed.\n"); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } -#define KEYREAPERINT 120 + sa0 = (struct sadb_sa *)mhp[SADB_EXT_SA]; + src0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_SRC]); + dst0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_DST]); -static int -key_sendup(s, km) - struct socket *s; - struct key_msghdr *km; -{ - struct mbuf *m; - - if (!km) - panic("km == NULL in key_sendup\n"); - MGETHDR(m, M_WAIT, MT_DATA); - m->m_len = m->m_pkthdr.len = 0; - m->m_next = 0; - m->m_nextpkt = 0; - m->m_pkthdr.rcvif = 0; - if (km) - m_copyback(m, 0, km->key_msglen, (caddr_t)km); - - if (sbappendaddr(&(s->so_rcv), &key_addr, m, NULL)) { - sorwakeup(s); - return 1; - } else { - if (m) m_freem(m); - } + KEY_SETSECASIDX(proto, msg0->sadb_msg_mode, src0+1, dst0+1, &saidx); + + /* get a SA header */ + if ((sah = key_getsah(&saidx)) == NULL) { + printf("key_update: no SA index found.\n"); + msg0->sadb_msg_errno = ENOENT; + return NULL; + } + + /* find a SA with sequence number. */ + if ((sav = key_getsavbyseq(sah, msg0->sadb_msg_seq)) == NULL) { + printf("key_update: no larval SA with sequence %u exists.\n", + msg0->sadb_msg_seq); + msg0->sadb_msg_errno = ENOENT; + return NULL; + } + + /* validity check */ + if (sav->sah->saidx.proto != proto) { + printf("key_update: protocol mismatched (DB=%u param=%u)\n", + sav->sah->saidx.proto, proto); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } + if (sav->spi != sa0->sadb_sa_spi) { + printf("key_update: SPI mismatched (DB:%u param:%u)\n", + (u_int32_t)ntohl(sav->spi), + (u_int32_t)ntohl(sa0->sadb_sa_spi)); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } + if (sav->pid != msg0->sadb_msg_pid) { + printf("key_update: pid mismatched (DB:%u param:%u)\n", + sav->pid, msg0->sadb_msg_pid); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } + + /* copy sav values */ + if (key_setsaval(sav, mhp)) { + key_freesav(sav); + return NULL; + } + + /* check SA values to be mature. */ + if ((msg0->sadb_msg_errno = key_mature(sav)) != 0) { + key_freesav(sav); + return NULL; + } + + /* + * we must call key_freesav() whenever we leave a function context, + * as we did not allocated a new sav (we updated existing sav). + */ + key_freesav(sav); + sav = NULL; - return(0); + { + struct sadb_msg *newmsg; + + /* set msg buf from mhp */ + if ((newmsg = key_getmsgbuf_x1(mhp)) == NULL) { + printf("key_update: No more memory.\n"); + msg0->sadb_msg_errno = ENOBUFS; + return NULL; + } + return newmsg; + } } -#ifdef notyet -/*---------------------------------------------------------------------- - * key_reaper(): - * Scan key table, nuke unwanted entries - ----------------------------------------------------------------------*/ -static void -key_reaper(whocares) - void *whocares; +/* + * search SAD with sequence for a SA which state is SADB_SASTATE_LARVAL. + * only called by key_update(). + * OUT: + * NULL : not found + * others : found, pointer to a SA. + */ +static struct secasvar * +key_getsavbyseq(sah, seq) + struct secashead *sah; + u_int32_t seq; { - DPRINTF(IDL_GROSS_EVENT,("Entering key_reaper()\n")); + struct secasvar *sav; + u_int state; + + state = SADB_SASTATE_LARVAL; - timeout(key_reaper, NULL, KEYREAPERINT * HZ); + /* search SAD with sequence number ? */ + __LIST_FOREACH(sav, &sah->savtree[state], chain) { + + KEY_CHKSASTATE(state, sav->state, "key_getsabyseq"); + + if (sav->seq == seq) { + sav->refcnt++; + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP key_getsavbyseq cause " + "refcnt++:%d SA:%p\n", + sav->refcnt, sav)); + return sav; + } + } + + return NULL; } -#endif /* notyet */ -/*---------------------------------------------------------------------- - * key_init(): - * Init routine for key socket, key engine - ----------------------------------------------------------------------*/ -static void -key_init() +/* + * SADB_ADD processing + * add a entry to SA database, when received + * <base, SA, (lifetime(HSC),) address(SD), (address(P),) + * key(AE), (identity(SD),) (sensitivity)> + * from the ikmpd, + * and send + * <base, SA, (lifetime(HSC),) address(SD), (address(P),) + * (identity(SD),) (sensitivity)> + * to the ikmpd. + * + * IGNORE identity and sensitivity messages. + * + * IN: mhp: pointer to the pointer to each header. + * OUT: NULL if fail. + * other if success, return pointer to the message to send. + */ +static struct sadb_msg * +key_add(mhp) + caddr_t *mhp; { - DPRINTF(IDL_EVENT,("Called key_init().\n")); - if (key_inittables()) - panic("key_inittables failed!\n"); -#ifdef notyet - timeout(key_reaper, NULL, HZ); -#endif /* notyet */ - bzero((char *)&keyso_cb, sizeof(keyso_cb)); + struct sadb_msg *msg0; + struct sadb_sa *sa0; + struct sadb_address *src0, *dst0; + struct secasindex saidx; + struct secashead *newsah; + struct secasvar *newsav; + u_int16_t proto; + + /* sanity check */ + if (mhp == NULL || mhp[0] == NULL) + panic("key_add: NULL pointer is passed.\n"); + + msg0 = (struct sadb_msg *)mhp[0]; + + /* map satype to proto */ + if ((proto = key_satype2proto(msg0->sadb_msg_satype)) == 0) { + printf("key_add: invalid satype is passed.\n"); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } + + if (mhp[SADB_EXT_SA] == NULL + || mhp[SADB_EXT_ADDRESS_SRC] == NULL + || mhp[SADB_EXT_ADDRESS_DST] == NULL + || (msg0->sadb_msg_satype == SADB_SATYPE_ESP + && mhp[SADB_EXT_KEY_ENCRYPT] == NULL) + || (msg0->sadb_msg_satype == SADB_SATYPE_AH + && mhp[SADB_EXT_KEY_AUTH] == NULL) + || (mhp[SADB_EXT_LIFETIME_HARD] != NULL + && mhp[SADB_EXT_LIFETIME_SOFT] == NULL) + || (mhp[SADB_EXT_LIFETIME_HARD] == NULL + && mhp[SADB_EXT_LIFETIME_SOFT] != NULL)) { + printf("key_add: invalid message is passed.\n"); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } + + sa0 = (struct sadb_sa *)mhp[SADB_EXT_SA]; + src0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_SRC]); + dst0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_DST]); + + KEY_SETSECASIDX(proto, msg0->sadb_msg_mode, src0+1, dst0+1, &saidx); + + /* get a SA header */ + if ((newsah = key_getsah(&saidx)) == NULL) { + + /* create a new SA header */ + if ((newsah = key_newsah(&saidx)) == NULL) { + printf("key_add: No more memory.\n"); + msg0->sadb_msg_errno = ENOBUFS; + return NULL; + } + } + + /* create new SA entry. */ + /* We can create new SA only if SPI is differenct. */ + if (key_getsavbyspi(newsah, sa0->sadb_sa_spi)) { + printf("key_add: SA already exists.\n"); + msg0->sadb_msg_errno = EEXIST; + return NULL; + } + if ((newsav = key_newsav(mhp, newsah)) == NULL) + return NULL; + + /* check SA values to be mature. */ + if ((msg0->sadb_msg_errno = key_mature(newsav)) != NULL) { + key_freesav(newsav); + return NULL; + } + + /* + * don't call key_freesav() here, as we would like to keep the SA + * in the database on success. + */ + + { + struct sadb_msg *newmsg; + + /* set msg buf from mhp */ + if ((newmsg = key_getmsgbuf_x1(mhp)) == NULL) { + printf("key_add: No more memory.\n"); + msg0->sadb_msg_errno = ENOBUFS; + return NULL; + } + + return newmsg; + } } -/*---------------------------------------------------------------------- - * my_addr(): - * Determine if an address belongs to one of my configured interfaces. - * Currently handles only AF_INET, AF_INET6 addresses. - ----------------------------------------------------------------------*/ -static int -my_addr(sa) - struct sockaddr *sa; +static struct sadb_msg * +key_getmsgbuf_x1(mhp) + caddr_t *mhp; { - switch(sa->sa_family) { -#ifdef INET6 - case AF_INET6: { - struct in6_ifaddr *i6a = 0; + struct sadb_msg *msg0; + struct sadb_msg *newmsg; + u_int len; + caddr_t p; + + /* sanity check */ + if (mhp == NULL || mhp[0] == NULL) + panic("key_getmsgbuf_x1: NULL pointer is passed.\n"); + + msg0 = (struct sadb_msg *)mhp[0]; + + /* create new sadb_msg to reply. */ + len = sizeof(struct sadb_msg) + + sizeof(struct sadb_sa) + + PFKEY_EXTLEN(mhp[SADB_EXT_ADDRESS_SRC]) + + PFKEY_EXTLEN(mhp[SADB_EXT_ADDRESS_DST]) + + (mhp[SADB_EXT_LIFETIME_HARD] == NULL + ? 0 : sizeof(struct sadb_lifetime)) + + (mhp[SADB_EXT_LIFETIME_SOFT] == NULL + ? 0 : sizeof(struct sadb_lifetime)); + + KMALLOC(newmsg, struct sadb_msg *, len); + if (newmsg == NULL) + return NULL; + bzero((caddr_t)newmsg, len); + + bcopy((caddr_t)mhp[0], (caddr_t)newmsg, sizeof(*msg0)); + newmsg->sadb_msg_errno = 0; + newmsg->sadb_msg_len = PFKEY_UNIT64(len); + p = (caddr_t)newmsg + sizeof(*msg0); + + p = key_setsadbext(p, mhp[SADB_EXT_SA]); + p = key_setsadbext(p, mhp[SADB_EXT_ADDRESS_SRC]); + p = key_setsadbext(p, mhp[SADB_EXT_ADDRESS_DST]); + + if (mhp[SADB_EXT_LIFETIME_HARD] != NULL) + p = key_setsadbext(p, mhp[SADB_EXT_LIFETIME_HARD]); + + if (mhp[SADB_EXT_LIFETIME_SOFT] != NULL) + p = key_setsadbext(p, mhp[SADB_EXT_LIFETIME_SOFT]); + + return newmsg; +} - for (i6a = in6_ifaddr; i6a; i6a = i6a->ia_next) { /*XXX*/ - if (IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)sa)->sin6_addr, - &i6a->ia_addr.sin6_addr)) - return(1); - } +/* + * SADB_DELETE processing + * receive + * <base, SA(*), address(SD)> + * from the ikmpd, and set SADB_SASTATE_DEAD, + * and send, + * <base, SA(*), address(SD)> + * to the ikmpd. + * + * IN: mhp: pointer to the pointer to each header. + * OUT: NULL if fail. + * other if success, return pointer to the message to send. + */ +static struct sadb_msg * +key_delete(mhp) + caddr_t *mhp; +{ + struct sadb_msg *msg0; + struct sadb_sa *sa0; + struct sadb_address *src0, *dst0; + struct secasindex saidx; + struct secashead *sah; + struct secasvar *sav; + u_int16_t proto; + + /* sanity check */ + if (mhp == NULL || mhp[0] == NULL) + panic("key_delete: NULL pointer is passed.\n"); + + msg0 = (struct sadb_msg *)mhp[0]; + + /* map satype to proto */ + if ((proto = key_satype2proto(msg0->sadb_msg_satype)) == 0) { + printf("key_delete: invalid satype is passed.\n"); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } + + if (mhp[SADB_EXT_SA] == NULL + || mhp[SADB_EXT_ADDRESS_SRC] == NULL + || mhp[SADB_EXT_ADDRESS_DST] == NULL) { + printf("key_delete: invalid message is passed.\n"); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } + sa0 = (struct sadb_sa *)mhp[SADB_EXT_SA]; + src0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_SRC]); + dst0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_DST]); + + KEY_SETSECASIDX(proto, msg0->sadb_msg_mode, src0+1, dst0+1, &saidx); + + /* get a SA header */ + if ((sah = key_getsah(&saidx)) == NULL) { + printf("key_delete: no SA found.\n"); + msg0->sadb_msg_errno = ENOENT; + return NULL; + } + + /* get a SA with SPI. */ + sav = key_getsavbyspi(sah, sa0->sadb_sa_spi); + if (sav == NULL) { + printf("key_delete: no alive SA found.\n"); + msg0->sadb_msg_errno = ENOENT; + return NULL; + } + + key_sa_chgstate(sav, SADB_SASTATE_DEAD); + key_freesav(sav); + sav = NULL; + + { + struct sadb_msg *newmsg; + u_int len; + caddr_t p; + + /* create new sadb_msg to reply. */ + len = sizeof(struct sadb_msg) + + sizeof(struct sadb_sa) + + PFKEY_EXTLEN(mhp[SADB_EXT_ADDRESS_SRC]) + + PFKEY_EXTLEN(mhp[SADB_EXT_ADDRESS_DST]); + + KMALLOC(newmsg, struct sadb_msg *, len); + if (newmsg == NULL) { + printf("key_delete: No more memory.\n"); + msg0->sadb_msg_errno = ENOBUFS; + return NULL; + } + bzero((caddr_t)newmsg, len); + + bcopy((caddr_t)mhp[0], (caddr_t)newmsg, sizeof(*msg0)); + newmsg->sadb_msg_errno = 0; + newmsg->sadb_msg_len = PFKEY_UNIT64(len); + p = (caddr_t)newmsg + sizeof(*msg0); + + p = key_setsadbext(p, mhp[SADB_EXT_SA]); + p = key_setsadbext(p, mhp[SADB_EXT_ADDRESS_SRC]); + p = key_setsadbext(p, mhp[SADB_EXT_ADDRESS_DST]); + + return newmsg; } - break; -#endif /* INET6 */ - case AF_INET: { - struct in_ifaddr *ia = 0; +} - for (ia = in_ifaddrhead.tqh_first; ia; ia = ia->ia_link.tqe_next) { - if (((struct sockaddr_in *)sa)->sin_addr.s_addr == - ia->ia_addr.sin_addr.s_addr) - return(1); - } +/* + * SADB_GET processing + * receive + * <base, SA(*), address(SD)> + * from the ikmpd, and get a SP and a SA to respond, + * and send, + * <base, SA, (lifetime(HSC),) address(SD), (address(P),) key(AE), + * (identity(SD),) (sensitivity)> + * to the ikmpd. + * + * IN: mhp: pointer to the pointer to each header. + * OUT: NULL if fail. + * other if success, return pointer to the message to send. + */ +static struct sadb_msg * +key_get(mhp) + caddr_t *mhp; +{ + struct sadb_msg *msg0; + struct sadb_sa *sa0; + struct sadb_address *src0, *dst0; + struct secasindex saidx; + struct secashead *sah; + struct secasvar *sav; + u_int16_t proto; + + /* sanity check */ + if (mhp == NULL || mhp[0] == NULL) + panic("key_get: NULL pointer is passed.\n"); + + msg0 = (struct sadb_msg *)mhp[0]; + + /* map satype to proto */ + if ((proto = key_satype2proto(msg0->sadb_msg_satype)) == 0) { + printf("key_get: invalid satype is passed.\n"); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } + + if (mhp[SADB_EXT_SA] == NULL + || mhp[SADB_EXT_ADDRESS_SRC] == NULL + || mhp[SADB_EXT_ADDRESS_DST] == NULL) { + printf("key_get: invalid message is passed.\n"); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } + sa0 = (struct sadb_sa *)mhp[SADB_EXT_SA]; + src0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_SRC]); + dst0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_DST]); + + KEY_SETSECASIDX(proto, msg0->sadb_msg_mode, src0+1, dst0+1, &saidx); + + /* get a SA header */ + if ((sah = key_getsah(&saidx)) == NULL) { + printf("key_get: no SA found.\n"); + msg0->sadb_msg_errno = ENOENT; + return NULL; + } + + /* get a SA with SPI. */ + sav = key_getsavbyspi(sah, sa0->sadb_sa_spi); + if (sav == NULL) { + printf("key_get: no SA with state of mature found.\n"); + msg0->sadb_msg_errno = ENOENT; + return NULL; + } + + { + struct sadb_msg *newmsg; + u_int len; + u_int8_t satype; + + /* map proto to satype */ + if ((satype = key_proto2satype(sah->saidx.proto)) == 0) { + printf("key_get: there was invalid proto in SAD.\n"); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } + + /* calculate a length of message buffer */ + len = key_getmsglen(sav); + + KMALLOC(newmsg, struct sadb_msg *, len); + if (newmsg == NULL) { + printf("key_get: No more memory.\n"); + msg0->sadb_msg_errno = ENOBUFS; + return NULL; + } + + /* create new sadb_msg to reply. */ + (void)key_setdumpsa(newmsg, sav, SADB_GET, + satype, msg0->sadb_msg_seq, msg0->sadb_msg_pid); + + return newmsg; } - break; - } - return(0); } -/*---------------------------------------------------------------------- - * key_output(): - * Process outbound pf_key message. - ----------------------------------------------------------------------*/ +/* + * SADB_ACQUIRE processing called by key_checkrequest() and key_acquire2(). + * send + * <base, SA, address(SD), (address(P)), + * (identity(SD),) (sensitivity,) proposal> + * to KMD, and expect to receive + * <base> with SADB_ACQUIRE if error occured, + * or + * <base, src address, dst address, (SPI range)> with SADB_GETSPI + * from KMD by PF_KEY. + * + * sensitivity is not supported. + * + * OUT: + * 0 : succeed + * others: error number + */ static int -key_output(m, so) - struct mbuf *m; - struct socket *so; +key_acquire(saidx, spidx) + struct secasindex *saidx; + struct secpolicyindex *spidx; { - struct key_msghdr *km = 0; - int len; - int error = 0; - int dstfamily = 0; +#ifndef IPSEC_NONBLOCK_ACQUIRE + struct secacq *newacq; +#endif + u_int8_t satype; + int error; + + /* sanity check */ + if (saidx == NULL || spidx == NULL) + panic("key_acquire: NULL pointer is passed.\n"); + if ((satype = key_proto2satype(saidx->proto)) == 0) + panic("key_acquire: invalid proto is passed.\n"); + +#ifndef IPSEC_NONBLOCK_ACQUIRE + /* + * We never do anything about acquirng SA. There is anather + * solution that kernel blocks to send SADB_ACQUIRE message until + * getting something message from IKEd. In later case, to be + * managed with ACQUIRING list. + */ + /* get a entry to check whether sending message or not. */ + if ((newacq = key_getacq(saidx)) != NULL) { + if (key_blockacq_count < newacq->count) { + /* reset counter and do send message. */ + newacq->count = 0; + } else { + /* increment counter and do nothing. */ + newacq->count++; + return 0; + } + } else { + /* make new entry for blocking to send SADB_ACQUIRE. */ + if ((newacq = key_newacq(saidx)) == NULL) + return ENOBUFS; + + /* add to acqtree */ + LIST_INSERT_HEAD(&acqtree, newacq, chain); + } +#endif - DPRINTF(IDL_EVENT,("key_output() got a message len=%d.\n", m->m_pkthdr.len)); + { + struct sadb_msg *newmsg = NULL; + union sadb_x_ident_id id; + u_int len; + caddr_t p; + + /* create new sadb_msg to reply. */ + len = sizeof(struct sadb_msg) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(saidx->src.__ss_len) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(saidx->dst.__ss_len) + + sizeof(struct sadb_ident) + + PFKEY_ALIGN8(spidx->src.__ss_len) + + sizeof(struct sadb_ident) + + PFKEY_ALIGN8(spidx->dst.__ss_len) + + sizeof(struct sadb_prop) + + sizeof(struct sadb_comb); /* XXX to be multiple */ + + KMALLOC(newmsg, struct sadb_msg *, len); + if (newmsg == 0) { + printf("key_acquire: No more memory.\n"); + return ENOBUFS; + } + bzero((caddr_t)newmsg, len); -#undef senderr -#define senderr(e) \ - { error = (e); if (km) km->key_errno = error; goto flush; } + newmsg->sadb_msg_version = PF_KEY_V2; + newmsg->sadb_msg_type = SADB_ACQUIRE; + newmsg->sadb_msg_errno = 0; + newmsg->sadb_msg_satype = satype; + newmsg->sadb_msg_len = PFKEY_UNIT64(len); - if (m == 0 || ((m->m_len < sizeof(long)) && - (m = m_pullup(m, sizeof(long))) == 0)) { - DPRINTF(IDL_CRITICAL,("key_output can't pullup mbuf\n")); - return (ENOBUFS); - } - if ((m->m_flags & M_PKTHDR) == 0) - panic("key_output"); - - DDO(IDL_FINISHED,dump_mbuf(m)); - - len = m->m_pkthdr.len; - if (len < sizeof(*km) || len != mtod(m, struct key_msghdr *)->key_msglen) { - DPRINTF(IDL_CRITICAL,("keyout: Invalid length field/length mismatch!\n")); - senderr(EINVAL); - } - KMALLOC(km, struct key_msghdr *, len); - if (km == 0) { - DPRINTF(IDL_CRITICAL,("keyoutput: Can't malloc memory!\n")); - senderr(ENOBUFS); - } +#ifndef IPSEC_NONBLOCK_ACQUIRE + newmsg->sadb_msg_seq = newacq->seq; +#else + newmsg->sadb_msg_seq = (acq_seq = (acq_seq == ~0 ? 1 : ++acq_seq)); +#endif - m_copydata(m, 0, len, (caddr_t)km); + newmsg->sadb_msg_pid = 0; + p = (caddr_t)newmsg + sizeof(struct sadb_msg); + + /* set sadb_address for saidx's. */ + p = key_setsadbaddr(p, + SADB_EXT_ADDRESS_SRC, + (struct sockaddr *)&saidx->src, + _INALENBYAF(saidx->src.__ss_family) << 3, + IPSEC_ULPROTO_ANY); + p = key_setsadbaddr(p, + SADB_EXT_ADDRESS_DST, + (struct sockaddr *)&saidx->dst, + _INALENBYAF(saidx->dst.__ss_family) << 3, + IPSEC_ULPROTO_ANY); + + /* set sadb_address for spidx's. */ + id.sadb_x_ident_id_addr.prefix = spidx->prefs; + id.sadb_x_ident_id_addr.ul_proto = spidx->ul_proto; + p = key_setsadbident(p, + SADB_EXT_IDENTITY_SRC, + SADB_X_IDENTTYPE_ADDR, + (caddr_t)&spidx->src, + spidx->src.__ss_len, + *(u_int64_t *)&id); + + id.sadb_x_ident_id_addr.prefix = spidx->prefd; + id.sadb_x_ident_id_addr.ul_proto = spidx->ul_proto; + p = key_setsadbident(p, + SADB_EXT_IDENTITY_DST, + SADB_X_IDENTTYPE_ADDR, + (caddr_t)&spidx->dst, + spidx->dst.__ss_len, + *(u_int64_t *)&id); + + /* create proposal extension */ + /* set combination extension */ + /* XXX: to be defined by proposal database */ + { + struct sadb_prop *prop; + struct sadb_comb *comb; + + prop = (struct sadb_prop *)p; + prop->sadb_prop_len = PFKEY_UNIT64(sizeof(*prop) + sizeof(*comb)); + /* XXX to be multiple */ + prop->sadb_prop_exttype = SADB_EXT_PROPOSAL; + prop->sadb_prop_replay = 32; /* XXX be variable ? */ + p += sizeof(struct sadb_prop); + + comb = (struct sadb_comb *)p; + comb->sadb_comb_auth = SADB_AALG_SHA1HMAC; /* XXX ??? */ + comb->sadb_comb_encrypt = SADB_EALG_DESCBC; /* XXX ??? */ + comb->sadb_comb_flags = 0; + comb->sadb_comb_auth_minbits = 8; /* XXX */ + comb->sadb_comb_auth_maxbits = 1024; /* XXX */ + comb->sadb_comb_encrypt_minbits = 64; /* XXX */ + comb->sadb_comb_encrypt_maxbits = 64; /* XXX */ + comb->sadb_comb_soft_allocations = 0; + comb->sadb_comb_hard_allocations = 0; + comb->sadb_comb_soft_bytes = 0; + comb->sadb_comb_hard_bytes = 0; + comb->sadb_comb_soft_addtime = 0; + comb->sadb_comb_hard_addtime = 0; + comb->sadb_comb_soft_usetime = 0; + comb->sadb_comb_hard_usetime = 0; + + p += sizeof(*comb); + } - km->key_errno = error = key_parse(&km, so, &dstfamily); - DPRINTF(IDL_MAJOR_EVENT, ("Back from key_parse\n")); -flush: - if (km) - key_sendup(so, km); -#if 0 - { - struct rawcb *rp = 0; - struct mbuf *m; - - if ((so->so_options & SO_USELOOPBACK) == 0) { - if (keyso_cb.any_count <= 1) { - if (km) - KFREE(km); - return (error); - } - rp = sotorawcb(so); + error = key_sendall(newmsg, len); + if (error != 0) + printf("key_acquire: key_sendall returned %d\n", error); + return error; } - DPRINTF(IDL_MAJOR_EVENT, ("key_output: foo\n")); - key_proto.sp_protocol = dstfamily; + return 0; +} - if (km) { - m = m_devget(km, len, 0, NULL, NULL); - KFREE(km); - } +#ifndef IPSEC_NONBLOCK_ACQUIRE +static struct secacq * +key_newacq(saidx) + struct secasindex *saidx; +{ + struct secacq *newacq; - DPRINTF(IDL_MAJOR_EVENT, ("key_output: bar\n")); - if (rp) - rp->rcb_proto.sp_family = 0; /* Prevent us from receiving message */ + /* get new entry */ + KMALLOC(newacq, struct secacq *, sizeof(struct secacq)); + if (newacq == NULL) { + printf("key_newacq: No more memory.\n"); + return NULL; + } + bzero(newacq, sizeof(*newacq)); - raw_input(m, &key_proto, &key_addr, &key_addr); + /* copy secindex */ + bcopy(saidx, &newacq->saidx, sizeof(newacq->saidx)); + newacq->seq = (acq_seq == ~0 ? 1 : ++acq_seq); + newacq->tick = 0; + newacq->count = 0; - if (rp) - rp->rcb_proto.sp_family = PF_KEY; - } - DPRINTF(IDL_MAJOR_EVENT, ("key_output: baz\n")); -#endif /* 0 */ - return (error); + return newacq; } +static struct secacq * +key_getacq(saidx) + struct secasindex *saidx; +{ + struct secacq *acq; -/*---------------------------------------------------------------------- - * key_*(): - * Handles protocol requests for pf_key sockets. - ----------------------------------------------------------------------*/ + __LIST_FOREACH(acq, &acqtree, chain) { + if (key_cmpsaidx_exactly(saidx, &acq->saidx)) + return acq; + } -static int -key_attach(struct socket *so, int proto, struct proc *p) + return NULL; +} + +static struct secacq * +key_getacqbyseq(seq) + u_int32_t seq; { - register int error = 0; - register struct rawcb *rp; - int s; + struct secacq *acq; - DPRINTF(IDL_EVENT,("Entering key_attach\n")); + __LIST_FOREACH(acq, &acqtree, chain) { + if (acq->seq == seq) + return acq; + } - MALLOC(rp, struct rawcb *, sizeof(*rp), M_PCB, M_WAITOK); - if (rp) { - bzero(rp, sizeof(*rp)); - so->so_pcb = (caddr_t)rp; - } - s = splnet(); - error = (raw_usrreqs.pru_attach)(so, proto, p); - if (!error) { /* XXX was: if (!so) which didn't make sense */ - splx(s); - return error; - } + return NULL; +} +#endif + +/* + * SADB_ACQUIRE processing, + * in first situation, is receiving + * <base> + * from the ikmpd, and clear sequence of its secasvar entry. + * + * In second situation, is receiving + * <base, address(SD), (address(P),) (identity(SD),) (sensitivity,) proposal> + * from a user land process, and return + * <base, address(SD), (address(P),) (identity(SD),) (sensitivity,) proposal> + * to the socket. + * + * IN: mhp: pointer to the pointer to each header. + * OUT: NULL if fail. + * other if success, return pointer to the message to send. + */ +static struct sadb_msg * +key_acquire2(mhp) + caddr_t *mhp; +{ + struct sadb_msg *msg0; + struct sadb_address *src0, *dst0; + struct secasindex saidx; + struct secashead *sah; + u_int16_t proto; + + /* sanity check */ + if (mhp == NULL || mhp[0] == NULL) + panic("key_acquire2: NULL pointer is passed.\n"); + + msg0 = (struct sadb_msg *)mhp[0]; + + /* + * Error message from KMd. + * We assume that if error was occured in IKEd, the length of PFKEY + * message is equal to the size of sadb_msg structure. + * We return ~0 even if error occured in this function. + */ + if (msg0->sadb_msg_len == PFKEY_UNIT64(sizeof(struct sadb_msg))) { + +#ifndef IPSEC_NONBLOCK_ACQUIRE + struct secacq *acq; + + /* check sequence number */ + if (msg0->sadb_msg_seq == 0) { + printf("key_acquire2: must specify sequence number.\n"); + return (struct sadb_msg *)~0; + } + + if ((acq = key_getacqbyseq(msg0->sadb_msg_seq)) == NULL) { + printf("key_acquire2: " + "invalid sequence number is passed.\n"); + return (struct sadb_msg *)~0; + } + + /* reset acq counter in order to deletion by timehander. */ + acq->tick = key_blockacq_lifetime; + acq->count = 0; +#endif + return (struct sadb_msg *)~0; + /* NOTREACHED */ + } + + /* + * This message is from user land. + */ + + /* map satype to proto */ + if ((proto = key_satype2proto(msg0->sadb_msg_satype)) == 0) { + printf("key_acquire2: invalid satype is passed.\n"); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } + + if (mhp[SADB_EXT_ADDRESS_SRC] == NULL + || mhp[SADB_EXT_ADDRESS_DST] == NULL + || mhp[SADB_EXT_PROPOSAL] == NULL) { + /* error */ + printf("key_acquire2: invalid message is passed.\n"); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } + src0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_SRC]); + dst0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_DST]); + + KEY_SETSECASIDX(proto, msg0->sadb_msg_mode, src0+1, dst0+1, &saidx); + + /* get a SA index */ + if ((sah = key_getsah(&saidx)) != NULL) { + printf("key_acquire2: a SA exists already.\n"); + msg0->sadb_msg_errno = EEXIST; + return NULL; + } + + msg0->sadb_msg_errno = key_acquire(&saidx, NULL); + if (msg0->sadb_msg_errno != 0) { + /* XXX What I do ? */ + printf("key_acquire2: error %d returned " + "from key_acquire.\n", msg0->sadb_msg_errno); + return NULL; + } + + { + struct sadb_msg *newmsg; + u_int len; + + /* create new sadb_msg to reply. */ + len = PFKEY_UNUNIT64(msg0->sadb_msg_len); + + KMALLOC(newmsg, struct sadb_msg *, len); + if (newmsg == NULL) { + printf("key_acquire2: No more memory.\n"); + msg0->sadb_msg_errno = ENOBUFS; + return NULL; + } + bzero((caddr_t)newmsg, len); + + bcopy(mhp[0], (caddr_t)newmsg, len); - rp = sotorawcb(so); /* isn't this redundant? */ - if (rp) { - int af = rp->rcb_proto.sp_protocol; - if (error) { - free((caddr_t)rp, M_PCB); - splx(s); - return error; + return newmsg; } - if (af == AF_INET) - keyso_cb.ip4_count++; -#ifdef INET6 - else if (af == AF_INET6) - keyso_cb.ip6_count++; -#endif /* INET6 */ - keyso_cb.any_count++; -#if 0 /*itojun*/ - rp->rcb_faddr = &key_addr; +} + +/* + * SADB_REGISTER processing. + * If SATYPE_UNSPEC has been passed as satype, only return sabd_supported. + * receive + * <base> + * from the ikmpd, and register a socket to send PF_KEY messages, + * and send + * <base, supported> + * to KMD by PF_KEY. + * If socket is detached, must free from regnode. + * OUT: + * 0 : succeed + * others: error number + */ +static struct sadb_msg * +key_register(mhp, so) + caddr_t *mhp; + struct socket *so; +{ + struct sadb_msg *msg0; + struct secreg *reg, *newreg = 0; + + /* sanity check */ + if (mhp == NULL || so == NULL || mhp[0] == NULL) + panic("key_register: NULL pointer is passed.\n"); + + msg0 = (struct sadb_msg *)mhp[0]; + + /* When SATYPE_UNSPEC is specified, only return sabd_supported. */ + if (msg0->sadb_msg_satype == SADB_SATYPE_UNSPEC) + goto setmsg; + + /* check whether existing or not */ + __LIST_FOREACH(reg, ®tree[msg0->sadb_msg_satype], chain) { + if (reg->so == so) { + printf("key_register: socket exists already.\n"); + msg0->sadb_msg_errno = EEXIST; + return NULL; + } + } + + /* create regnode */ + KMALLOC(newreg, struct secreg *, sizeof(struct secreg)); + if (newreg == NULL) { + printf("key_register: No more memory.\n"); + msg0->sadb_msg_errno = ENOBUFS; + return NULL; + } + bzero((caddr_t)newreg, sizeof(struct secreg)); + + newreg->so = so; + ((struct keycb *)sotorawcb(so))->kp_registered++; + + /* add regnode to regtree. */ + LIST_INSERT_HEAD(®tree[msg0->sadb_msg_satype], newreg, chain); + + setmsg: + { + struct sadb_msg *newmsg; + struct sadb_supported *sup; + u_int len, alen, elen; + caddr_t p; + + /* create new sadb_msg to reply. */ + alen = sizeof(struct sadb_supported) + + ((SADB_AALG_MAX - 1) * sizeof(struct sadb_alg)); + +#ifdef IPSEC_ESP + elen = sizeof(struct sadb_supported) + + ((SADB_EALG_MAX - 1) * sizeof(struct sadb_alg)); #else + elen = 0; +#endif + + len = sizeof(struct sadb_msg) + + alen + + elen; + + KMALLOC(newmsg, struct sadb_msg *, len); + if (newmsg == NULL) { + printf("key_register: No more memory.\n"); + msg0->sadb_msg_errno = ENOBUFS; + return NULL; + } + bzero((caddr_t)newmsg, len); + + bcopy((caddr_t)mhp[0], (caddr_t)newmsg, sizeof(*msg0)); + newmsg->sadb_msg_errno = 0; + newmsg->sadb_msg_len = PFKEY_UNIT64(len); + p = (caddr_t)newmsg + sizeof(*msg0); + + /* for authentication algorithm */ + sup = (struct sadb_supported *)p; + sup->sadb_supported_len = PFKEY_UNIT64(alen); + sup->sadb_supported_exttype = SADB_EXT_SUPPORTED_AUTH; + p += sizeof(*sup); + { - struct mbuf *m; - MGET(m, M_DONTWAIT, MT_DATA); - if (m) { /* XXX but what about sin_len here? -PW */ - rp->rcb_faddr = mtod(m, struct sockaddr *); - bcopy(&key_addr, rp->rcb_faddr, sizeof(struct sockaddr)); - } else - rp->rcb_faddr = NULL; + int i; + struct sadb_alg *alg; + struct ah_algorithm *algo; + + for (i = 1; i < SADB_AALG_MAX; i++) { + algo = &ah_algorithms[i]; + alg = (struct sadb_alg *)p; + alg->sadb_alg_id = i; + alg->sadb_alg_ivlen = 0; + alg->sadb_alg_minbits = algo->keymin; + alg->sadb_alg_maxbits = algo->keymax; + p += sizeof(struct sadb_alg); + } + } + +#ifdef IPSEC_ESP + /* for encryption algorithm */ + sup = (struct sadb_supported *)p; + sup->sadb_supported_len = PFKEY_UNIT64(elen); + sup->sadb_supported_exttype = SADB_EXT_SUPPORTED_ENCRYPT; + p += sizeof(*sup); + + { + int i; + struct sadb_alg *alg; + struct esp_algorithm *algo; + + for (i = 1; i < SADB_EALG_MAX; i++) { + algo = &esp_algorithms[i]; + + alg = (struct sadb_alg *)p; + alg->sadb_alg_id = i; + if (algo && algo->ivlen) { + /* + * give NULL to get the value preferred by algorithm + * XXX SADB_X_EXT_DERIV ? + */ + alg->sadb_alg_ivlen = (*algo->ivlen)(NULL); + } else + alg->sadb_alg_ivlen = 0; + alg->sadb_alg_minbits = algo->keymin; + alg->sadb_alg_maxbits = algo->keymax; + p += sizeof(struct sadb_alg); + } } #endif - soisconnected(so); /* Key socket, like routing socket, must be - connected. */ - /* Possibly set other needed flags/options at creation time in here. */ - so->so_options |= SO_USELOOPBACK; /* Like routing socket, we turn this */ - /* on by default */ + return newmsg; } - splx(s); - return error; } -static int -key_detach(struct socket *so) +/* + * free secreg entry registered. + * XXX: I want to do free a socket marked done SADB_RESIGER to socket. + */ +void +key_freereg(so) + struct socket *so; { - register int error = 0; - register struct rawcb *rp; - int s; + struct secreg *reg; + int i; - DPRINTF(IDL_EVENT,("Entering key_detach\n")); + /* sanity check */ + if (so == NULL) + panic("key_freereg: NULL pointer is passed.\n"); - rp = sotorawcb(so); - if (rp) { - int af = rp->rcb_proto.sp_protocol; - if (af == AF_INET) - keyso_cb.ip4_count--; -#ifdef INET6 - else if (af == AF_INET6) - keyso_cb.ip6_count--; -#endif /* INET6 */ - keyso_cb.any_count--; - } - s = splnet(); - error = (raw_usrreqs.pru_detach)(so); - splx(s); - return error; + /* + * check whether existing or not. + * check all type of SA, because there is a potential that + * one socket is registered to multiple type of SA. + */ + for (i = 0; i <= SADB_SATYPE_MAX; i++) { + __LIST_FOREACH(reg, ®tree[i], chain) { + if (reg->so == so + && __LIST_CHAINED(reg)) { + LIST_REMOVE(reg, chain); + KFREE(reg); + break; + } + } + } + return; } +/* + * SADB_EXPIRE processing + * send + * <base, SA, lifetime(C and one of HS), address(SD)> + * to KMD by PF_KEY. + * NOTE: We send only soft lifetime extension. + * + * OUT: 0 : succeed + * others : error number + */ static int -key_abort(struct socket *so) +key_expire(sav) + struct secasvar *sav; { - DPRINTF(IDL_EVENT,("Entering key_abort\n")); + int s; + int satype; + + /* XXX: Why do we lock ? */ + s = splnet(); /*called from softclock()*/ + + /* sanity check */ + if (sav == NULL) + panic("key_expire: NULL pointer is passed.\n"); + if (sav->sah == NULL) + panic("key_expire: Why was SA index in SA NULL.\n"); + if ((satype = key_proto2satype(sav->sah->saidx.proto)) == 0) + panic("key_expire: invalid proto is passed.\n"); + + { + struct sadb_msg *newmsg = NULL; + u_int len; + caddr_t p; + int error; + + /* create new sadb_msg to reply. */ + len = sizeof(struct sadb_msg) + + sizeof(struct sadb_sa) + + sizeof(struct sadb_lifetime) + + sizeof(struct sadb_lifetime) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(sav->sah->saidx.src.__ss_len) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(sav->sah->saidx.dst.__ss_len); + + KMALLOC(newmsg, struct sadb_msg *, len); + if (newmsg == NULL) { + printf("key_expire: No more memory.\n"); + splx(s); + return ENOBUFS; + } + bzero((caddr_t)newmsg, len); + + /* set msg header */ + p = key_setsadbmsg((caddr_t)newmsg, SADB_EXPIRE, len, + satype, sav->seq, 0, + sav->sah->saidx.mode, sav->refcnt); - return (raw_usrreqs.pru_abort)(so); + /* create SA extension */ + p = key_setsadbsa(p, sav); + + /* create lifetime extension */ + { + struct sadb_lifetime *m_lt = (struct sadb_lifetime *)p; + + m_lt->sadb_lifetime_len = PFKEY_UNIT64(sizeof(struct sadb_lifetime)); + m_lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT; + m_lt->sadb_lifetime_allocations = sav->lft_c->sadb_lifetime_allocations; + m_lt->sadb_lifetime_bytes = sav->lft_c->sadb_lifetime_bytes; + m_lt->sadb_lifetime_addtime = sav->lft_c->sadb_lifetime_addtime; + m_lt->sadb_lifetime_usetime = sav->lft_c->sadb_lifetime_usetime; + p += sizeof(struct sadb_lifetime); + + /* copy SOFT lifetime extension. */ + bcopy(sav->lft_s, p, sizeof(struct sadb_lifetime)); + p += sizeof(struct sadb_lifetime); + } + + /* set sadb_address for source */ + p = key_setsadbaddr(p, + SADB_EXT_ADDRESS_SRC, + (struct sockaddr *)&sav->sah->saidx.src, + _INALENBYAF(sav->sah->saidx.src.__ss_family) << 3, + IPSEC_ULPROTO_ANY); + + /* set sadb_address for destination */ + p = key_setsadbaddr(p, + SADB_EXT_ADDRESS_DST, + (struct sockaddr *)&sav->sah->saidx.dst, + _INALENBYAF(sav->sah->saidx.dst.__ss_family) << 3, + IPSEC_ULPROTO_ANY); + + error = key_sendall(newmsg, len); + splx(s); + return error; + } } -static int -key_bind(struct socket *so, struct sockaddr *nam, struct proc *p) +/* + * SADB_FLUSH processing + * receive + * <base> + * from the ikmpd, and free all entries in secastree. + * and send, + * <base> + * to the ikmpd. + * NOTE: to do is only marking SADB_SASTATE_DEAD. + * + * IN: mhp: pointer to the pointer to each header. + * OUT: NULL if fail. + * other if success, return pointer to the message to send. + */ +static struct sadb_msg * +key_flush(mhp) + caddr_t *mhp; { - DPRINTF(IDL_EVENT,("Entering key_bind\n")); + struct sadb_msg *msg0; + struct secashead *sah, *nextsah; + struct secasvar *sav, *nextsav; + u_int16_t proto; + u_int8_t state; + u_int stateidx; + + /* sanity check */ + if (mhp == NULL || mhp[0] == NULL) + panic("key_flush: NULL pointer is passed.\n"); + + msg0 = (struct sadb_msg *)mhp[0]; + + /* map satype to proto */ + if ((proto = key_satype2proto(msg0->sadb_msg_satype)) == 0) { + printf("key_flush: invalid satype is passed.\n"); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } + + /* no SATYPE specified, i.e. flushing all SA. */ + for (sah = LIST_FIRST(&sahtree); + sah != NULL; + sah = nextsah) { + + nextsah = LIST_NEXT(sah, chain); + + if (msg0->sadb_msg_satype != SADB_SATYPE_UNSPEC + && proto != sah->saidx.proto) + continue; + + for (stateidx = 0; + stateidx < _ARRAYLEN(saorder_state_alive); + stateidx++) { - return (raw_usrreqs.pru_bind)(so, nam, p); + state = saorder_state_any[stateidx]; + for (sav = LIST_FIRST(&sah->savtree[state]); + sav != NULL; + sav = nextsav) { + + nextsav = LIST_NEXT(sav, chain); + + key_sa_chgstate(sav, SADB_SASTATE_DEAD); + } + } + + sah->state = SADB_SASTATE_DEAD; + } + + { + struct sadb_msg *newmsg; + u_int len; + + /* create new sadb_msg to reply. */ + len = sizeof(struct sadb_msg); + + KMALLOC(newmsg, struct sadb_msg *, len); + if (newmsg == NULL) { + printf("key_flush: No more memory.\n"); + msg0->sadb_msg_errno = ENOBUFS; + return NULL; + } + bzero((caddr_t)newmsg, len); + + bcopy((caddr_t)mhp[0], (caddr_t)newmsg, sizeof(*msg0)); + newmsg->sadb_msg_errno = 0; + newmsg->sadb_msg_len = PFKEY_UNIT64(len); + + return newmsg; + } } +/* + * SADB_DUMP processing + * dump all entries including status of DEAD in SAD. + * receive + * <base> + * from the ikmpd, and dump all secasvar leaves + * and send, + * <base> ..... + * to the ikmpd. + * + * IN: mhp: pointer to the pointer to each header. + * OUT: error code. 0 on success. + */ static int -key_connect(struct socket *so, struct sockaddr *nam, struct proc *p) +key_dump(mhp, so, target) + caddr_t *mhp; + struct socket *so; + int target; { - DPRINTF(IDL_EVENT,("Entering key_connect\n")); + struct sadb_msg *msg0; + struct secashead *sah; + struct secasvar *sav; + u_int16_t proto; + u_int stateidx; + u_int8_t satype; + u_int8_t state; + int len, cnt; + struct sadb_msg *newmsg; + + /* sanity check */ + if (mhp == NULL || mhp[0] == NULL) + panic("key_dump: NULL pointer is passed.\n"); + + msg0 = (struct sadb_msg *)mhp[0]; + + /* map satype to proto */ + if ((proto = key_satype2proto(msg0->sadb_msg_satype)) == 0) { + printf("key_dump: invalid satype is passed.\n"); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } + + /* count sav entries to be sent to the userland. */ + cnt = 0; + __LIST_FOREACH(sah, &sahtree, chain) { + + if (msg0->sadb_msg_satype != SADB_SATYPE_UNSPEC + && proto != sah->saidx.proto) + continue; + + for (stateidx = 0; + stateidx < _ARRAYLEN(saorder_state_any); + stateidx++) { + + state = saorder_state_any[stateidx]; + __LIST_FOREACH(sav, &sah->savtree[state], chain) { + cnt++; + } + } + } + + if (cnt == 0) + return ENOENT; + + /* send this to the userland, one at a time. */ + newmsg = NULL; + __LIST_FOREACH(sah, &sahtree, chain) { + + if (msg0->sadb_msg_satype != SADB_SATYPE_UNSPEC + && proto != sah->saidx.proto) + continue; + + /* map proto to satype */ + if ((satype = key_proto2satype(sah->saidx.proto)) == 0) { + printf("key_dump: there was invalid proto in SAD.\n"); + msg0->sadb_msg_errno = EINVAL; + return NULL; + } + + for (stateidx = 0; + stateidx < _ARRAYLEN(saorder_state_any); + stateidx++) { + + state = saorder_state_any[stateidx]; + __LIST_FOREACH(sav, &sah->savtree[state], chain) { + + len = key_getmsglen(sav); + KMALLOC(newmsg, struct sadb_msg *, len); + if (newmsg == NULL) { + printf("key_dump: No more memory.\n"); + return ENOBUFS; + } + bzero((caddr_t)newmsg, len); + + --cnt; + (void)key_setdumpsa(newmsg, sav, SADB_DUMP, + satype, cnt, msg0->sadb_msg_pid); + + key_sendup(so, newmsg, len, target); + KFREE(newmsg); + newmsg = NULL; + } + } + } - return (raw_usrreqs.pru_connect)(so, nam, p); + return 0; +} + +/* + * SADB_X_PROMISC processing + */ +static void +key_promisc(mhp, so) + caddr_t *mhp; + struct socket *so; +{ + struct sadb_msg *msg0; + int olen; + + /* sanity check */ + if (mhp == NULL || mhp[0] == NULL) + panic("key_promisc: NULL pointer is passed.\n"); + + msg0 = (struct sadb_msg *)mhp[0]; + olen = PFKEY_UNUNIT64(msg0->sadb_msg_len); + + if (olen < sizeof(struct sadb_msg)) { + return; + } else if (olen == sizeof(struct sadb_msg)) { + /* enable/disable promisc mode */ + struct keycb *kp; + int target = 0; + + target = KEY_SENDUP_ONE; + + if (so == NULL) { + return; + } + if ((kp = (struct keycb *)sotorawcb(so)) == NULL) { + msg0->sadb_msg_errno = EINVAL; + goto sendorig; + } + msg0->sadb_msg_errno = 0; + if (msg0->sadb_msg_satype == 1 || msg0->sadb_msg_satype == 0) { + kp->kp_promisc = msg0->sadb_msg_satype; + } else { + msg0->sadb_msg_errno = EINVAL; + goto sendorig; + } + + /* send the original message back to everyone */ + msg0->sadb_msg_errno = 0; + target = KEY_SENDUP_ALL; +sendorig: + key_sendup(so, msg0, PFKEY_UNUNIT64(msg0->sadb_msg_len), target); + } else { + /* send packet as is */ + struct sadb_msg *msg; + int len; + + len = olen - sizeof(struct sadb_msg); + KMALLOC(msg, struct sadb_msg *, len); + if (msg == NULL) { + msg0->sadb_msg_errno = ENOBUFS; + key_sendup(so, msg0, PFKEY_UNUNIT64(msg0->sadb_msg_len), + KEY_SENDUP_ONE); /*XXX*/ + } + + /* XXX if sadb_msg_seq is specified, send to specific pid */ + key_sendup(so, msg, len, KEY_SENDUP_ALL); + KFREE(msg); + } } +/* + * send message to the socket. + * OUT: + * 0 : success + * others : fail + */ static int -key_disconnect(struct socket *so) +key_sendall(msg, len) + struct sadb_msg *msg; + u_int len; { - DPRINTF(IDL_EVENT,("Entering key_disconnect\n")); + struct secreg *reg; + int error = 0; + + /* sanity check */ + if (msg == NULL) + panic("key_sendall: NULL pointer is passed.\n"); + + /* search table registerd socket to send a message. */ + __LIST_FOREACH(reg, ®tree[msg->sadb_msg_satype], chain) { + error = key_sendup(reg->so, msg, len, KEY_SENDUP_ONE); + if (error != 0) { + if (error == ENOBUFS) + printf("key_sendall: No more memory.\n"); + else { + printf("key_sendall: key_sendup returned %d\n", + error); + } + KFREE(msg); + return error; + } + } - return (raw_usrreqs.pru_disconnect)(so); + KFREE(msg); + return 0; } -static int -key_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, - struct mbuf *control, struct proc *p) +/* + * parse sadb_msg buffer to process PFKEYv2, + * and create a data to response if needed. + * I think to be dealed with mbuf directly. + * IN: + * msgp : pointer to pointer to a received buffer pulluped. + * This is rewrited to response. + * so : pointer to socket. + * OUT: + * length for buffer to send to user process. + */ +int +key_parse(msgp, so, targetp) + struct sadb_msg **msgp; + struct socket *so; + int *targetp; { - DPRINTF(IDL_EVENT,("Entering key_send\n")); + struct sadb_msg *msg = *msgp, *newmsg = NULL; + caddr_t mhp[SADB_EXT_MAX + 1]; + u_int orglen; + int error; + + /* sanity check */ + if (msg == NULL || so == NULL) + panic("key_parse: NULL pointer is passed.\n"); + + KEYDEBUG(KEYDEBUG_KEY_DUMP, + printf("key_parse: passed sadb_msg\n"); + kdebug_sadb(msg)); + + orglen = PFKEY_UNUNIT64(msg->sadb_msg_len); + + if (targetp) + *targetp = KEY_SENDUP_ONE; + + /* check version */ + if (msg->sadb_msg_version != PF_KEY_V2) { + printf("key_parse: PF_KEY version %u is mismatched.\n", + msg->sadb_msg_version); + return EINVAL; + } + + /* check type */ + if (msg->sadb_msg_type > SADB_MAX) { + printf("key_parse: invalid type %u is passed.\n", + msg->sadb_msg_type); + msg->sadb_msg_errno = EINVAL; + return orglen; + } - return (raw_usrreqs.pru_send)(so, flags, m, nam, control, p); + /* align message. */ + if (key_align(msg, mhp) != 0) { + msg->sadb_msg_errno = EINVAL; + return orglen; + } + + /* check SA type */ + switch (msg->sadb_msg_satype) { + case SADB_SATYPE_UNSPEC: + switch (msg->sadb_msg_type) { + case SADB_GETSPI: + case SADB_UPDATE: + case SADB_ADD: + case SADB_DELETE: + case SADB_GET: + case SADB_ACQUIRE: + case SADB_EXPIRE: + printf("key_parse: must specify satype " + "when msg type=%u.\n", + msg->sadb_msg_type); + msg->sadb_msg_errno = EINVAL; + return orglen; + } + break; + case SADB_SATYPE_AH: + case SADB_SATYPE_ESP: + switch (msg->sadb_msg_type) { + case SADB_X_SPDADD: + case SADB_X_SPDDELETE: + case SADB_X_SPDGET: + case SADB_X_SPDDUMP: + case SADB_X_SPDFLUSH: + printf("key_parse: illegal satype=%u\n", msg->sadb_msg_type); + msg->sadb_msg_errno = EINVAL; + return orglen; + } + break; + case SADB_SATYPE_RSVP: + case SADB_SATYPE_OSPFV2: + case SADB_SATYPE_RIPV2: + case SADB_SATYPE_MIP: + printf("key_parse: type %u isn't supported.\n", + msg->sadb_msg_satype); + msg->sadb_msg_errno = EOPNOTSUPP; + return orglen; + case 1: /* XXX: What does it do ? */ + if (msg->sadb_msg_type == SADB_X_PROMISC) + break; + /*FALLTHROUGH*/ + default: + printf("key_parse: invalid type %u is passed.\n", + msg->sadb_msg_satype); + msg->sadb_msg_errno = EINVAL; + return orglen; + } + + /* check field of upper layer protocol and address family */ + if (mhp[SADB_EXT_ADDRESS_SRC] != NULL + && mhp[SADB_EXT_ADDRESS_DST] != NULL) { + struct sadb_address *src0, *dst0; + u_int prefix; + + src0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_SRC]); + dst0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_DST]); + + /* check upper layer protocol */ + if (src0->sadb_address_proto != dst0->sadb_address_proto) { + printf("key_parse: upper layer protocol mismatched.\n"); + msg->sadb_msg_errno = EINVAL; + return orglen; + } + + /* check family */ + if (PFKEY_ADDR_SADDR(src0)->sa_family + != PFKEY_ADDR_SADDR(dst0)->sa_family) { + printf("key_parse: address family mismatched.\n"); + msg->sadb_msg_errno = EINVAL; + return orglen; + } + + prefix = _INALENBYAF(PFKEY_ADDR_SADDR(src0)->sa_family) << 3; + + /* check max prefixlen */ + if (prefix < src0->sadb_address_prefixlen + || prefix < dst0->sadb_address_prefixlen) { + printf("key_parse: illegal prefixlen.\n"); + msg->sadb_msg_errno = EINVAL; + return orglen; + } + + switch (PFKEY_ADDR_SADDR(src0)->sa_family) { + case AF_INET: + case AF_INET6: + break; + default: + printf("key_parse: invalid address family.\n"); + msg->sadb_msg_errno = EINVAL; + return orglen; + } + + /* + * prefixlen == 0 is valid because there can be a case when + * all addresses are matched. + */ + } + + switch (msg->sadb_msg_type) { + case SADB_GETSPI: + if ((newmsg = key_getspi(mhp)) == NULL) + return orglen; + if (targetp) + *targetp = KEY_SENDUP_ALL; + break; + + case SADB_UPDATE: + if ((newmsg = key_update(mhp)) == NULL) + return orglen; + if (targetp) + *targetp = KEY_SENDUP_ALL; + break; + + case SADB_ADD: + if ((newmsg = key_add(mhp)) == NULL) + return orglen; + if (targetp) + *targetp = KEY_SENDUP_ALL; + break; + + case SADB_DELETE: + if ((newmsg = key_delete(mhp)) == NULL) + return orglen; + if (targetp) + *targetp = KEY_SENDUP_ALL; + break; + + case SADB_GET: + if ((newmsg = key_get(mhp)) == NULL) + return orglen; + break; + + case SADB_ACQUIRE: + if ((newmsg = key_acquire2(mhp)) == NULL) + return orglen; + + if (newmsg == (struct sadb_msg *)~0) { + /* + * It's not need to reply because of the message + * that was reporting an error occured from the KMd. + */ + KFREE(msg); + return 0; + } + break; + + case SADB_REGISTER: + if ((newmsg = key_register(mhp, so)) == NULL) + return orglen; + if (targetp) + *targetp = KEY_SENDUP_REGISTERED; + break; + + case SADB_EXPIRE: + printf("key_parse: why is SADB_EXPIRE received ?\n"); + msg->sadb_msg_errno = EINVAL; + if (targetp) + *targetp = KEY_SENDUP_ALL; + return orglen; + + case SADB_FLUSH: + if ((newmsg = key_flush(mhp)) == NULL) + return orglen; + if (targetp) + *targetp = KEY_SENDUP_ALL; + break; + + case SADB_DUMP: + /* key_dump will call key_sendup() on her own */ + error = key_dump(mhp, so, KEY_SENDUP_ONE); + if (error) { + msg->sadb_msg_errno = error; + return orglen; + } else { + KFREE(msg); + return 0; + } + break; + + case SADB_X_PROMISC: + /* everything is handled in key_promisc() */ + key_promisc(mhp, so); + KFREE(msg); + return 0; /*nothing to reply*/ + + case SADB_X_PCHANGE: + printf("key_parse: SADB_X_PCHANGE isn't supported.\n"); + msg->sadb_msg_errno = EINVAL; + return orglen; + + case SADB_X_SPDADD: + if ((newmsg = key_spdadd(mhp)) == NULL) + return orglen; + if (targetp) + *targetp = KEY_SENDUP_ALL; + break; + + case SADB_X_SPDDELETE: + if ((newmsg = key_spddelete(mhp)) == NULL) + return orglen; + if (targetp) + *targetp = KEY_SENDUP_ALL; + break; + + case SADB_X_SPDDUMP: + /* key_spddump will call key_sendup() on her own */ + error = key_spddump(mhp, so, KEY_SENDUP_ONE); + if (error) { + msg->sadb_msg_errno = error; + return orglen; + } else { + KFREE(msg); + return 0; + } + break; + + + case SADB_X_SPDFLUSH: + if ((newmsg = key_spdflush(mhp)) == NULL) + return orglen; + if (targetp) + *targetp = KEY_SENDUP_ALL; + break; + + default: + msg->sadb_msg_errno = EOPNOTSUPP; + return orglen; + } + + /* switch from old sadb_msg to new one if success. */ + KFREE(msg); + *msgp = newmsg; + + return PFKEY_UNUNIT64((*msgp)->sadb_msg_len); } +/* + * set the pointer to each header into message buffer. + * IN: msg: pointer to message buffer. + * mhp: pointer to the buffer allocated like below: + * caddr_t mhp[SADB_EXT_MAX + 1]; + * OUT: 0: + * EINVAL: + */ static int -key_shutdown(struct socket *so) +key_align(msg, mhp) + struct sadb_msg *msg; + caddr_t *mhp; { - DPRINTF(IDL_EVENT,("Entering key_shutdown\n")); + struct sadb_ext *ext; + int tlen, extlen; + int i; + + /* sanity check */ + if (msg == NULL || mhp == NULL) + panic("key_align: NULL pointer is passed.\n"); + + /* initialize */ + for (i = 0; i < SADB_EXT_MAX + 1; i++) + mhp[i] = NULL; + + mhp[0] = (caddr_t)msg; + + tlen = PFKEY_UNUNIT64(msg->sadb_msg_len) - sizeof(struct sadb_msg); + ext = (struct sadb_ext *)((caddr_t)msg + sizeof(struct sadb_msg)); + + while (tlen > 0) { + /* duplicate check */ + /* XXX Are there duplication either KEY_AUTH or KEY_ENCRYPT ?*/ + if (mhp[ext->sadb_ext_type] != NULL) { + printf("key_align: duplicate ext_type %u is passed.\n", + ext->sadb_ext_type); + return EINVAL; + } + + /* set pointer */ + switch (ext->sadb_ext_type) { + case SADB_EXT_SA: + case SADB_EXT_LIFETIME_CURRENT: + case SADB_EXT_LIFETIME_HARD: + case SADB_EXT_LIFETIME_SOFT: + case SADB_EXT_ADDRESS_SRC: + case SADB_EXT_ADDRESS_DST: + case SADB_EXT_ADDRESS_PROXY: + case SADB_EXT_KEY_AUTH: + /* must to be chek weak keys. */ + case SADB_EXT_KEY_ENCRYPT: + /* must to be chek weak keys. */ + case SADB_EXT_IDENTITY_SRC: + case SADB_EXT_IDENTITY_DST: + case SADB_EXT_SENSITIVITY: + case SADB_EXT_PROPOSAL: + case SADB_EXT_SUPPORTED_AUTH: + case SADB_EXT_SUPPORTED_ENCRYPT: + case SADB_EXT_SPIRANGE: + case SADB_X_EXT_POLICY: + mhp[ext->sadb_ext_type] = (caddr_t)ext; + break; + default: + printf("key_align: invalid ext_type %u is passed.\n", + ext->sadb_ext_type); + return EINVAL; + } + + extlen = PFKEY_UNUNIT64(ext->sadb_ext_len); + tlen -= extlen; + ext = (struct sadb_ext *)((caddr_t)ext + extlen); + } - return (raw_usrreqs.pru_shutdown)(so); + return 0; } -/*---------------------------------------------------------------------- - * key_cbinit(): - * Control block init routine for key socket - ----------------------------------------------------------------------*/ -static void -key_cbinit() +void +key_init() { - /* - * This is equivalent to raw_init for the routing socket. - * The key socket uses the same control block as the routing - * socket. - */ - DPRINTF(IDL_EVENT,("Called key_cbinit().\n")); + int i; + + bzero((caddr_t)&key_cb, sizeof(key_cb)); + + for (i = 0; i < IPSEC_DIR_MAX; i++) { + LIST_INIT(&sptree[i]); + } + + LIST_INIT(&sahtree); + + for (i = 0; i <= SADB_SATYPE_MAX; i++) { + LIST_INIT(®tree[i]); + } + +#ifndef IPSEC_NONBLOCK_ACQUIRE + LIST_INIT(&acqtree); +#endif + + /* system default */ + ip4_def_policy.policy = IPSEC_POLICY_NONE; + ip4_def_policy.refcnt++; /*never reclaim this*/ +#ifdef INET6 + ip6_def_policy.policy = IPSEC_POLICY_NONE; + ip6_def_policy.refcnt++; /*never reclaim this*/ +#endif + +#ifndef IPSEC_DEBUG2 + timeout((void *)key_timehandler, (void *)0, 100); +#endif /*IPSEC_DEBUG2*/ + + /* initialize key statistics */ + keystat.getspi_count = 1; + + printf("IPsec: Initialized Security Association Processing.\n"); + + return; } /* - * Protoswitch entry for pf_key + * XXX: maybe This function is called after INBOUND IPsec processing. + * + * Special check for tunnel-mode packets. + * We must make some checks for consistency between inner and outer IP header. + * + * xxx more checks to be provided */ +int +key_checktunnelsanity(sav, family, src, dst) + struct secasvar *sav; + u_int family; + caddr_t src; + caddr_t dst; +{ + /* sanity check */ + if (sav->sah == NULL) + panic("sav->sah == NULL at key_checktunnelsanity"); -extern struct domain keydomain; /* or at least forward */ - -struct pr_usrreqs key_usrreqs = { - key_abort, pru_accept_notsupp, key_attach, key_bind, key_connect, - pru_connect2_notsupp, in_control, key_detach, key_disconnect, - pru_listen_notsupp, in_setpeeraddr, pru_rcvd_notsupp, - pru_rcvoob_notsupp, key_send, pru_sense_null, key_shutdown, - in_setsockaddr, sosend, soreceive, sopoll -}; + /* XXX: check inner IP header */ + return 1; +} -struct protosw keysw[] = { -{ SOCK_RAW, &keydomain, 0, PR_ATOMIC|PR_ADDR, - 0, key_output, raw_ctlinput, 0, - 0, - key_cbinit, 0, 0, 0, - &key_usrreqs, -}, -}; +#if 0 +#ifdef __FreeBSD__ +#define hostnamelen strlen(hostname) +#endif -struct domain keydomain = - { PF_KEY, "key", key_init, 0, 0, - keysw, &keysw[sizeof(keysw)/sizeof(keysw[0])] }; +/* + * Get FQDN for the host. + * If the administrator configured hostname (by hostname(1)) without + * domain name, returns nothing. + */ +static const char * +key_getfqdn() +{ + int i; + int hasdot; + static char fqdn[MAXHOSTNAMELEN + 1]; + + if (!hostnamelen) + return NULL; + + /* check if it comes with domain name. */ + hasdot = 0; + for (i = 0; i < hostnamelen; i++) { + if (hostname[i] == '.') + hasdot++; + } + if (!hasdot) + return NULL; + + /* NOTE: hostname may not be NUL-terminated. */ + bzero(fqdn, sizeof(fqdn)); + bcopy(hostname, fqdn, hostnamelen); + fqdn[hostnamelen] = '\0'; + return fqdn; +} -#ifdef __FreeBSD__ -DOMAIN_SET(key); +/* + * get username@FQDN for the host/user. + */ +static const char * +key_getuserfqdn() +{ + const char *host; + static char userfqdn[MAXHOSTNAMELEN + MAXLOGNAME + 2]; + struct proc *p = curproc; + char *q; + + if (!p || !p->p_pgrp || !p->p_pgrp->pg_session) + return NULL; + if (!(host = key_getfqdn())) + return NULL; + + /* NOTE: s_login may not be-NUL terminated. */ + bzero(userfqdn, sizeof(userfqdn)); + bcopy(p->p_pgrp->pg_session->s_login, userfqdn, MAXLOGNAME); + userfqdn[MAXLOGNAME] = '\0'; /* safeguard */ + q = userfqdn + strlen(userfqdn); + *q++ = '@'; + bcopy(host, q, strlen(host)); + q += strlen(host); + *q++ = '\0'; + + return userfqdn; +} #endif -#endif /*KEY*/ +/* record data transfer on SA, and update timestamps */ +void +key_sa_recordxfer(sav, m) + struct secasvar *sav; + struct mbuf *m; +{ + if (!sav) + panic("key_sa_recordxfer called with sav == NULL"); + if (!m) + panic("key_sa_recordxfer called with m == NULL"); + if (!sav->lft_c) + return; + + sav->lft_c->sadb_lifetime_bytes += m->m_pkthdr.len; + /* to check bytes lifetime is done in key_timehandler(). */ + + /* + * We use the number of packets as the unit of + * sadb_lifetime_allocations. We increment the variable + * whenever {esp,ah}_{in,out}put is called. + */ + sav->lft_c->sadb_lifetime_allocations++; + /* XXX check for expires? */ + + /* + * NOTE: We record CURRENT sadb_lifetime_usetime by using wall clock, + * in seconds. HARD and SOFT lifetime are measured by the time + * difference (again in seconds) from sadb_lifetime_usetime. + * + * usetime + * v expire expire + * -----+-----+--------+---> t + * <--------------> HARD + * <-----> SOFT + */ + { + struct timeval tv; + microtime(&tv); + sav->lft_c->sadb_lifetime_usetime = tv.tv_sec; + /* XXX check for expires? */ + } + + return; +} + +/* dumb version */ +void +key_sa_routechange(dst) + struct sockaddr *dst; +{ + struct secashead *sah; + struct route *ro; + + __LIST_FOREACH(sah, &sahtree, chain) { + ro = &sah->sa_route; + if (ro->ro_rt && dst->sa_len == ro->ro_dst.sa_len + && bcmp(dst, &ro->ro_dst, dst->sa_len) == 0) { + RTFREE(ro->ro_rt); + ro->ro_rt = (struct rtentry *)NULL; + } + } + + return; +} + +static void +key_sa_chgstate(sav, state) + struct secasvar *sav; + u_int8_t state; +{ + if (sav == NULL) + panic("key_sa_chgstate called with sav == NULL"); + + if (sav->state == state) + return; + + if (__LIST_CHAINED(sav)) + LIST_REMOVE(sav, chain); + + sav->state = state; + LIST_INSERT_HEAD(&sav->sah->savtree[state], sav, chain); +} |