summaryrefslogtreecommitdiffstats
path: root/sys/netkey/key.c
diff options
context:
space:
mode:
authorwollman <wollman@FreeBSD.org>1996-06-14 17:22:18 +0000
committerwollman <wollman@FreeBSD.org>1996-06-14 17:22:18 +0000
commit845782b7e052a1136c206b3e7ae58942a6a725bd (patch)
tree0dd5de95373d80204fd553830859671bd3bad40f /sys/netkey/key.c
parent99066eaf6d9a560cdc3e6b0de8c328e34cb45696 (diff)
downloadFreeBSD-src-845782b7e052a1136c206b3e7ae58942a6a725bd.zip
FreeBSD-src-845782b7e052a1136c206b3e7ae58942a6a725bd.tar.gz
This is the `netkey' kernel key-management service (the PF_KEY analogue
to PF_ROUTE) from NRL's IPv6 distribution, heavily modified by me for better source layout, formatting, and textual conventions. I am told that this code is no longer under active development, but it's a useful hack for those interested in doing work on network security, key management, etc. This code has only been tested twice, so it should be considered highly experimental. Obtained from: ftp.ripe.net
Diffstat (limited to 'sys/netkey/key.c')
-rw-r--r--sys/netkey/key.c2270
1 files changed, 2270 insertions, 0 deletions
diff --git a/sys/netkey/key.c b/sys/netkey/key.c
new file mode 100644
index 0000000..2edd29a
--- /dev/null
+++ b/sys/netkey/key.c
@@ -0,0 +1,2270 @@
+/*----------------------------------------------------------------------
+ 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/domain.h>
+#include <sys/mbuf.h>
+#include <sys/proc.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/time.h>
+
+#include <net/raw_cb.h>
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/if_ether.h>
+
+#ifdef INET6
+#include <netinet6/in6.h>
+#include <netinet6/in6_var.h>
+#endif /* INET6 */
+
+#include <netkey/key.h>
+#include <netkey/key_debug.h>
+
+#define SOCKADDR struct sockaddr
+
+#define KMALLOC(p, t, n) (p = (t) malloc((unsigned long)(n), M_SECA, M_DONTWAIT))
+#define KFREE(p) free((caddr_t)p, M_SECA);
+
+#define CRITICAL_DCL int critical_s;
+#define CRITICAL_START critical_s = splnet()
+#define CRITICAL_END splx(critical_s)
+
+#define TIME_SECONDS time.tv_sec
+#define CURRENT_PID curproc->p_pid
+
+#define DEFARGS(arglist, args) arglist args;
+#define AND ;
+
+#ifdef INET6
+#define MAXHASHKEYLEN (2 * sizeof(int) + 2 * sizeof(struct sockaddr_in6))
+#else
+#define MAXHASHKEYLEN (2 * sizeof(int) + 2 * sizeof(struct sockaddr_in))
+#endif
+
+
+/*
+ * 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 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 my_addr __P((SOCKADDR *));
+static int key_sendup __P((struct socket *, struct key_msghdr *));
+
+/*----------------------------------------------------------------------
+ * 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.
+ ----------------------------------------------------------------------*/
+int
+key_secassoc2msghdr(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->state = secassoc->state;
+ km->label = secassoc->label;
+ km->spi = secassoc->spi;
+ km->keylen = secassoc->keylen;
+ km->ivlen = secassoc->ivlen;
+ km->algorithm = secassoc->algorithm;
+ km->lifetype = secassoc->lifetype;
+ km->lifetime1 = secassoc->lifetime1;
+ km->lifetime2 = secassoc->lifetime2;
+
+ /*
+ * Stuff src/dst/from/key/iv in buffer after
+ * the message header.
+ */
+ cp = (char *)(km + 1);
+
+ DPRINTF(IDL_FINISHED, ("sa2msghdr: 1\n"));
+ keyinfo->src = (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 = (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);
+ }
+
+ DPRINTF(IDL_FINISHED, ("sa2msghdr: 3\n"));
+ keyinfo->from = (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);
+ }
+
+ DPRINTF(IDL_FINISHED, ("sa2msghdr: 4\n"));
+
+ keyinfo->key = cp;
+ keyinfo->keylen = secassoc->keylen;
+ if (secassoc->keylen) {
+ bcopy((char *)(secassoc->key), cp, secassoc->keylen);
+ ADVANCE(cp, secassoc->keylen);
+ }
+
+ 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);
+ }
+
+ 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);
+}
+
+
+/*----------------------------------------------------------------------
+ * key_msghdr2secassoc():
+ * Copy info from a key message buffer into a key_secassoc
+ * structure
+ ----------------------------------------------------------------------*/
+int
+key_msghdr2secassoc(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->state = km->state;
+ secassoc->label = km->label;
+ secassoc->spi = km->spi;
+ secassoc->keylen = km->keylen;
+ secassoc->ivlen = km->ivlen;
+ secassoc->algorithm = km->algorithm;
+ secassoc->lifetype = km->lifetype;
+ secassoc->lifetime1 = km->lifetime1;
+ secassoc->lifetime2 = km->lifetime2;
+
+ if (keyinfo->src) {
+ KMALLOC(secassoc->src, 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, 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, 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;
+ return(0);
+}
+
+
+/*----------------------------------------------------------------------
+ * 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(SOCKADDR *sa1, 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_ADDR_EQUAL(((struct sockaddr_in6 *)sa1)->sin6_addr,
+ ((struct sockaddr_in6 *)sa2)->sin6_addr));
+#endif /* INET6 */
+ }
+ return(0);
+}
+
+/*----------------------------------------------------------------------
+ * key_inittables():
+ * Allocate space and initialize key engine tables
+ ----------------------------------------------------------------------*/
+int
+key_inittables(void)
+{
+ 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;
+}
+
+static int
+key_freetables(void)
+{
+ KFREE(keyregtable);
+ keyregtable = NULL;
+ KFREE(key_acquirelist);
+ key_acquirelist = NULL;
+ return 0;
+}
+
+/*----------------------------------------------------------------------
+ * key_gethashval():
+ * Determine keytable hash value.
+ ----------------------------------------------------------------------*/
+static int
+key_gethashval(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);
+}
+
+
+/*----------------------------------------------------------------------
+ * 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(char *buf, u_int type, SOCKADDR *src, SOCKADDR *dst,
+ u_int32_t spi, u_int keytype)
+{
+ char *cp, *p;
+
+ DPRINTF(IDL_FINISHED,("Entering key_createkey\n"));
+
+ if (!buf || !src || !dst)
+ return(-1);
+
+ cp = buf;
+ bcopy((char *)&type, cp, sizeof(type));
+ cp += sizeof(type);
+
+#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 in_addr6) : \
+ 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 */
+
+ 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));
+
+ p = ADDRPART(src);
+ bcopy(p, cp, ADDRSIZE(src));
+ cp += ADDRSIZE(src);
+
+ p = ADDRPART(dst);
+ bcopy(p, cp, ADDRSIZE(dst));
+ cp += ADDRSIZE(dst);
+
+#undef ADDRPART
+#undef ADDRSIZE
+
+ if (keytype == 0) {
+ bcopy((char *)&spi, cp, sizeof(spi));
+ cp += sizeof(spi);
+ }
+
+ DPRINTF(IDL_FINISHED,("hash key:\n"));
+ DDO(IDL_FINISHED, dump_buf(buf, cp - buf));
+ return(cp - buf);
+}
+
+
+/*----------------------------------------------------------------------
+ * 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(u_int type, SOCKADDR *src, SOCKADDR *dst, struct socket *so)
+{
+ struct key_so2spinode *np = 0;
+
+ if (!(src && dst)) {
+ DPRINTF(IDL_ERROR,("key_sosearch: got null src or dst pointer!\n"));
+ return(NULL);
+ }
+
+ 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);
+}
+
+
+/*----------------------------------------------------------------------
+ * key_sodelete():
+ * Delete entries from the so2spi table.
+ * flag = 1 purge all entries
+ * flag = 0 delete entries with socket pointer matching socket
+ ----------------------------------------------------------------------*/
+static void
+key_sodelete(struct socket *socket, int flag)
+{
+ struct key_so2spinode *prevnp, *np;
+ CRITICAL_DCL
+
+ CRITICAL_START;
+
+ DPRINTF(IDL_EVENT,("Entering keysodelete w/so=0x%x flag=%d\n",
+ (unsigned int)socket,flag));
+
+ if (flag) {
+ int i;
+
+ for (i = 0; i < SO2SPITBLSIZE; i++)
+ for(np = so2spitbl[i].next; np; np = np->next) {
+ KFREE(np);
+ }
+ CRITICAL_END;
+ return;
+ }
+
+ 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;
+ }
+
+ /*
+ * 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;
+ }
+ prevnp = np;
+ }
+ CRITICAL_END;
+}
+
+
+/*----------------------------------------------------------------------
+ * key_deleteacquire():
+ * Delete an entry from the key_acquirelist
+ ----------------------------------------------------------------------*/
+static void
+key_deleteacquire(u_int type, SOCKADDR *target)
+{
+ struct key_acquirelist *ap, *prev;
+
+ prev = key_acquirelist;
+ for(ap = key_acquirelist->next; ap; ap = ap->next) {
+ if (addrpart_equal(target, (SOCKADDR *)&(ap->target)) &&
+ (type == ap->type)) {
+ DPRINTF(IDL_EVENT,("Deleting entry from acquire list!\n"));
+ prev->next = ap->next;
+ KFREE(ap);
+ ap = prev;
+ }
+ prev = ap;
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ * 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(u_int type, SOCKADDR *src, 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);
+}
+
+
+/*----------------------------------------------------------------------
+ * 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(int indx, struct key_secassoc *secassoc)
+{
+ struct key_tblnode *keynode;
+
+ DPRINTF(IDL_FINISHED,("Entering key_addnode w/indx=%d secassoc=0x%x\n",
+ indx, (unsigned int)secassoc));
+
+ if (!(&keytable[indx]))
+ return(NULL);
+ if (!secassoc) {
+ panic("key_addnode: Someone passed in a null secassoc!\n");
+ }
+
+ KMALLOC(keynode, struct key_tblnode *, sizeof(struct key_tblnode));
+ if (keynode == 0)
+ return(NULL);
+ bzero((char *)keynode, sizeof(struct key_tblnode));
+
+ KMALLOC(keynode->solist, struct socketlist *, sizeof(struct socketlist));
+ if (keynode->solist == 0) {
+ KFREE(keynode);
+ return(NULL);
+ }
+ bzero((char *)(keynode->solist), sizeof(struct socketlist));
+
+ keynode->secassoc = secassoc;
+ keynode->solist->next = NULL;
+ keynode->next = keytable[indx].next;
+ keytable[indx].next = keynode;
+ return(keynode);
+}
+
+
+/*----------------------------------------------------------------------
+ * 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(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");
+ }
+
+ /*
+ * 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
+ /*
+ * 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 */
+
+ /*
+ * 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);
+ }
+
+ 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);
+ }
+ }
+
+ CRITICAL_START;
+
+ 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;
+
+ key_deleteacquire(secassoc->type, secassoc->dst);
+
+ CRITICAL_END;
+ return 0;
+}
+
+
+/*----------------------------------------------------------------------
+ * key_get():
+ * Get a security association from the key table.
+ ----------------------------------------------------------------------*/
+int
+key_get(u_int type, SOCKADDR *src, 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(struct socket *so)
+{
+ int len, i;
+ int seq = 1;
+ struct key_msgdata keyinfo;
+ struct key_msghdr *km;
+ struct key_tblnode *keynode;
+
+ /*
+ * Routine to dump the key table to a routing socket
+ * Use for debugging only!
+ */
+
+ KMALLOC(km, struct key_msghdr *, sizeof(struct key_msghdr) +
+ 3 * MAX_SOCKADDR_SZ + MAX_KEY_SZ + MAX_IV_SZ);
+ 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));
+
+ if (key_secassoc2msghdr(keynode->secassoc, km, &keyinfo) != 0)
+ panic("key_dump");
+
+ km->key_msglen = len;
+ km->key_msgvers = KEY_VERSION;
+ km->key_msgtype = KEY_DUMP;
+ km->key_pid = CURRENT_PID;
+ km->key_seq = seq++;
+ km->key_errno = 0;
+
+ 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 = CURRENT_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(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);
+ }
+
+ 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);
+ KFREE(keynode->secassoc);
+ if (keynode->solist)
+ KFREE(keynode->solist);
+ KFREE(keynode);
+ CRITICAL_END;
+ return(0);
+ }
+ return(-1);
+}
+
+
+/*----------------------------------------------------------------------
+ * key_flush():
+ * Delete all entries from the key table.
+ ----------------------------------------------------------------------*/
+void
+key_flush(void)
+{
+ struct key_tblnode *keynode;
+ int i;
+
+ /*
+ * This is slow, but simple.
+ */
+ DPRINTF(IDL_FINISHED,("Flushing key table..."));
+ for (i = 0; i < KEYTBLSIZE; i++) {
+ while ((keynode = keytable[i].next))
+ if (key_delete(keynode->secassoc) != 0)
+ panic("key_flush");
+ }
+ DPRINTF(IDL_FINISHED,("done\n"));
+}
+
+
+/*----------------------------------------------------------------------
+ * 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(u_int type, SOCKADDR *src, 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;
+ }
+
+ 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->spi = val;
+ secassoc->state |= K_LARVAL;
+ if (my_addr(secassoc->dst))
+ secassoc->state |= K_INBOUND;
+ if (my_addr(secassoc->src))
+ secassoc->state |= K_OUTBOUND;
+
+ bcopy((char *)src, (char *)secassoc->src, src->sa_len);
+ 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. */
+#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 */
+
+ /*
+ * 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_SECONDS + 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_update():
+ * Update a keytable entry that has an spi value assigned but is
+ * incomplete (e.g. no key/iv).
+ ----------------------------------------------------------------------*/
+int
+key_update(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);
+
+ /* Should we also restrict updating of only LARVAL entries ? */
+
+ CRITICAL_START;
+
+ inbound = my_addr(secassoc->dst);
+ outbound = my_addr(secassoc->src);
+
+ newstate = keynode->secassoc->state;
+ newstate &= ~K_LARVAL;
+
+ if (inbound)
+ newstate |= K_INBOUND;
+ if (outbound)
+ newstate |= K_OUTBOUND;
+
+ 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);
+ }
+ }
+
+ /*
+ * 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);
+
+ /*
+ * 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;
+ }
+
+ key_deleteacquire(secassoc->type, (SOCKADDR *)&(secassoc->dst));
+
+ CRITICAL_END;
+ return(0);
+}
+
+/*----------------------------------------------------------------------
+ * key_register():
+ * Register a socket as one capable of acquiring security associations
+ * for the kernel.
+ ----------------------------------------------------------------------*/
+int
+key_register(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);
+ }
+ }
+
+ 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(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;
+}
+
+
+/*----------------------------------------------------------------------
+ * 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.
+ ----------------------------------------------------------------------*/
+int
+key_acquire(u_int type, SOCKADDR *src, 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_SECONDS) {
+ 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_SECONDS) {
+ /*
+ * 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;
+ }
+
+ /*
+ * 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++;
+ }
+
+ 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));
+ if (ap == 0)
+ return(success ? 0 : -1);
+ bzero((char *)ap, sizeof(struct key_acquirelist));
+ 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_SECONDS + 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(u_int type, SOCKADDR *src, 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);
+ }
+
+ /*
+ * 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;
+ }
+
+ if (np) {
+ struct socketlist *newsp;
+ CRITICAL_DCL
+
+ CRITICAL_START;
+
+ DPRINTF(IDL_EVENT,("key_alloc: found node to allocate\n"));
+ keynode = np->keynode;
+
+ 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);
+ }
+
+ /*
+ * 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(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);
+ }
+}
+
+/*----------------------------------------------------------------------
+ * 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(u_int type, SOCKADDR *src, 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);
+}
+
+
+/*----------------------------------------------------------------------
+ * 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(u_int type, SOCKADDR *src, 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);
+ }
+ }
+
+ /*
+ * 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);
+ }
+ }
+ *keyentry = NULL;
+ return(-1);
+}
+
+/*----------------------------------------------------------------------
+ * key_xdata():
+ * Parse message buffer for src/dst/from/iv/key if parseflag = 0
+ * else parse for src/dst only.
+ ----------------------------------------------------------------------*/
+static int
+key_xdata(struct key_msghdr *km, struct key_msgdata *kip, int parseflag)
+{
+ char *cp, *cpmax;
+
+ if (!km || (km->key_msglen <= 0))
+ return (-1);
+
+ cp = (caddr_t)(km + 1);
+ cpmax = (caddr_t)km + km->key_msglen;
+
+ /*
+ * Assumes user process passes message with
+ * correct word alignment.
+ */
+
+ /*
+ * Need to clean up this code later.
+ */
+
+ /* Grab src addr */
+ kip->src = (SOCKADDR *)cp;
+ if (!kip->src->sa_len) {
+ DPRINTF(IDL_MAJOR_EVENT,("key_xdata couldn't parse src addr\n"));
+ return(-1);
+ }
+
+ ADVANCE(cp, kip->src->sa_len);
+
+ /* Grab dest addr */
+ kip->dst = (SOCKADDR *)cp;
+ if (!kip->dst->sa_len) {
+ DPRINTF(IDL_MAJOR_EVENT,("key_xdata couldn't parse dest addr\n"));
+ return(-1);
+ }
+
+ ADVANCE(cp, kip->dst->sa_len);
+ if (parseflag == 1) {
+ kip->from = 0;
+ kip->key = kip->iv = 0;
+ kip->keylen = kip->ivlen = 0;
+ return(0);
+ }
+
+ /* Grab from addr */
+ kip->from = (SOCKADDR *)cp;
+ if (!kip->from->sa_len) {
+ DPRINTF(IDL_MAJOR_EVENT,("key_xdata couldn't parse from addr\n"));
+ return(-1);
+ }
+
+ ADVANCE(cp, kip->from->sa_len);
+
+ /* Grab key */
+ if ((kip->keylen = km->keylen)) {
+ kip->key = cp;
+ ADVANCE(cp, km->keylen);
+ } else
+ kip->key = 0;
+
+ /* Grab iv */
+ if ((kip->ivlen = km->ivlen))
+ kip->iv = cp;
+ else
+ kip->iv = 0;
+
+ return (0);
+}
+
+
+int
+key_parse(struct key_msghdr **kmp, struct socket *so, int *dstfamily)
+{
+ int error = 0, keyerror = 0;
+ struct key_msgdata keyinfo;
+ struct key_secassoc *secassoc = NULL;
+ struct key_msghdr *km = *kmp;
+
+ DPRINTF(IDL_MAJOR_EVENT, ("Entering key_parse\n"));
+
+#define senderr(e) \
+ { error = (e); goto flush; }
+
+ if (km->key_msgvers != KEY_VERSION) {
+ DPRINTF(IDL_CRITICAL,("keyoutput: Unsupported key message version!\n"));
+ senderr(EPROTONOSUPPORT);
+ }
+
+ km->key_pid = CURRENT_PID;
+
+ DDO(IDL_MAJOR_EVENT, printf("keymsghdr:\n"); dump_keymsghdr(km));
+
+ /*
+ * Parse buffer for src addr, dest addr, from addr, key, iv
+ */
+ bzero((char *)&keyinfo, sizeof(keyinfo));
+
+ switch (km->key_msgtype) {
+ case KEY_ADD:
+ DPRINTF(IDL_MAJOR_EVENT,("key_output got KEY_ADD msg\n"));
+
+ if (key_xdata(km, &keyinfo, 0) < 0)
+ goto parsefail;
+
+ /*
+ * 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);
+ }
+
+ if (key_msghdr2secassoc(secassoc, km, &keyinfo) < 0) {
+ DPRINTF(IDL_CRITICAL,("keyoutput: key_msghdr2secassoc failed!\n"));
+ KFREE(secassoc);
+ senderr(EINVAL);
+ }
+ 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);
+ KFREE(secassoc);
+ if (keyerror == -2) {
+ senderr(EEXIST);
+ } else {
+ senderr(ENOBUFS);
+ }
+ }
+ break;
+ case KEY_DELETE:
+ DPRINTF(IDL_MAJOR_EVENT,("key_output got KEY_DELETE msg\n"));
+
+ if (key_xdata(km, &keyinfo, 1) < 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 (key_delete(secassoc) != 0) {
+ if (secassoc->iv)
+ KFREE(secassoc->iv);
+ if (secassoc->key)
+ KFREE(secassoc->key);
+ KFREE(secassoc);
+ senderr(ESRCH);
+ }
+ if (secassoc->iv)
+ KFREE(secassoc->iv);
+ if (secassoc->key)
+ KFREE(secassoc->key);
+ 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);
+ 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, (SOCKADDR *)keyinfo.src,
+ (SOCKADDR *)keyinfo.dst,
+ km->spi, &secassoc) != 0) {
+ DPRINTF(IDL_EVENT,("keyoutput: can't get key\n"));
+ senderr(ESRCH);
+ }
+
+ 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);
+ 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);
+ }
+ 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, keyinfo.src, keyinfo.dst,
+ km->lifetime1, km->lifetime2,
+ &(km->spi))) != 0) {
+ DPRINTF(IDL_CRITICAL,("keyoutput: getspi failed error=%d\n", keyerror));
+ senderr(keyerror);
+ }
+ 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;
+
+parsefail:
+ keyinfo.dst = NULL;
+ error = EINVAL;
+
+flush:
+ if (km)
+ km->key_errno = error;
+
+ if (dstfamily)
+ *dstfamily = keyinfo.dst ? keyinfo.dst->sa_family : 0;
+
+ DPRINTF(IDL_MAJOR_EVENT, ("key_parse exiting with error=%d\n", error));
+ return error;
+}
+
+/*
+ * Definitions of protocols supported in the KEY domain.
+ */
+
+struct sockaddr key_addr = { 2, PF_KEY, };
+struct sockproto key_proto = { PF_KEY, };
+
+#define KEYREAPERINT 120
+
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+
+static int
+key_sendup(s, km)
+ struct socket *s;
+ struct key_msghdr *km;
+{
+ struct mbuf *m;
+ 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;
+ m_copyback(m, 0, km->key_msglen, (caddr_t)km);
+
+ if (sbappendaddr(&(s->so_rcv), &key_addr, m, NULL)) {
+ sorwakeup(s);
+ return 1;
+ } else
+ m_freem(m);
+
+ return(0);
+}
+
+#ifdef notyet
+/*----------------------------------------------------------------------
+ * key_reaper():
+ * Scan key table, nuke unwanted entries
+ ----------------------------------------------------------------------*/
+static void
+key_reaper(whocares)
+ void *whocares;
+{
+ DPRINTF(IDL_GROSS_EVENT,("Entering key_reaper()\n"));
+
+ timeout(key_reaper, NULL, KEYREAPERINT * HZ);
+}
+#endif /* notyet */
+
+/*----------------------------------------------------------------------
+ * key_init():
+ * Init routine for key socket, key engine
+ ----------------------------------------------------------------------*/
+static void
+key_init(void)
+{
+ 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));
+}
+
+/*----------------------------------------------------------------------
+ * 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)
+ SOCKADDR *sa;
+{
+ struct in6_ifaddr *i6a = 0;
+ struct in_ifaddr *ia = 0;
+
+ switch(sa->sa_family) {
+#ifdef INET6
+ case AF_INET6:
+ for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next) {
+ if (IN6_ADDR_EQUAL(((struct sockaddr_in6 *)sa)->sin6_addr,
+ i6a->i6a_addr.sin6_addr))
+ return(1);
+ }
+ break;
+#endif /* INET6 */
+ case AF_INET:
+ for (ia = in_ifaddr; ia; ia = ia->ia_next) {
+ if (((struct sockaddr_in *)sa)->sin_addr.s_addr ==
+ ia->ia_addr.sin_addr.s_addr)
+ return(1);
+ }
+ break;
+ }
+ return(0);
+}
+
+/*----------------------------------------------------------------------
+ * key_output():
+ * Process outbound pf_key message.
+ ----------------------------------------------------------------------*/
+static int
+key_output(struct mbuf *m, struct socket *so)
+{
+ struct key_msghdr *km = 0;
+ caddr_t cp, cplimit;
+ int len;
+ int error = 0;
+ int dstfamily = 0;
+
+ DPRINTF(IDL_EVENT,("key_output() got a message len=%d.\n", m->m_pkthdr.len));
+
+#undef senderr
+#define senderr(e) \
+ { error = (e); if (km) km->key_errno = error; goto flush; }
+
+ 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);
+ }
+
+ m_copydata(m, 0, len, (caddr_t)km);
+
+ km->key_errno = error = key_parse(&km, so, &dstfamily);
+ DPRINTF(IDL_MAJOR_EVENT, ("Back from key_parse\n"));
+flush:
+ 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);
+ }
+
+ DPRINTF(IDL_MAJOR_EVENT, ("key_output: foo\n"));
+ key_proto.sp_protocol = dstfamily;
+
+ if (km) {
+ m = m_devget(km, len, 0, NULL, NULL);
+ KFREE(km);
+ }
+
+ DPRINTF(IDL_MAJOR_EVENT, ("key_output: bar\n"));
+ if (rp)
+ rp->rcb_proto.sp_family = 0; /* Prevent us from receiving message */
+
+ raw_input(m, &key_proto, &key_addr, &key_addr);
+
+ if (rp)
+ rp->rcb_proto.sp_family = PF_KEY;
+ }
+ DPRINTF(IDL_MAJOR_EVENT, ("key_output: baz\n"));
+#endif /* 0 */
+ return (error);
+}
+
+
+/*----------------------------------------------------------------------
+ * key_usrreq():
+ * Handles PRU_* for pf_key sockets.
+ ----------------------------------------------------------------------*/
+static int
+key_usrreq(struct socket *so, int req, struct mbuf *m, struct mbuf *nam,
+ struct mbuf *control)
+{
+ register int error = 0;
+ register struct rawcb *rp = sotorawcb(so);
+ int s;
+
+ DPRINTF(IDL_EVENT,("Entering key_usrreq, req = %d.\n",req));
+
+ if (req == PRU_ATTACH) {
+ MALLOC(rp, struct rawcb *, sizeof(*rp), M_PCB, M_WAITOK);
+ if (so->so_pcb = (caddr_t)rp)
+ bzero(so->so_pcb, sizeof(*rp));
+ }
+
+ if (req == PRU_DETACH && 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_usrreq(so, req, m, nam, control);
+ rp = sotorawcb(so);
+
+ if (req == PRU_ATTACH && rp) {
+ int af = rp->rcb_proto.sp_protocol;
+ if (error) {
+ free((caddr_t)rp, M_PCB);
+ splx(s);
+ return error;
+ }
+ 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++;
+ rp->rcb_faddr = &key_addr;
+ 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 */
+ }
+ splx(s);
+ return error;
+}
+
+/*----------------------------------------------------------------------
+ * key_cbinit():
+ * Control block init routine for key socket
+ ----------------------------------------------------------------------*/
+static void
+key_cbinit(void)
+{
+ /*
+ * 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"));
+}
+
+/*
+ * Protoswitch entry for pf_key
+ */
+
+extern struct domain keydomain; /* or at least forward */
+
+struct protosw keysw[] = {
+{ SOCK_RAW, &keydomain, 0, PR_ATOMIC|PR_ADDR,
+ raw_input, key_output, raw_ctlinput, 0,
+ key_usrreq,
+ key_cbinit
+}
+};
+
+struct domain keydomain =
+ { PF_KEY, "key", key_init, 0, 0,
+ keysw, &keysw[sizeof(keysw)/sizeof(keysw[0])] };
+
+DOMAIN_SET(key)
OpenPOWER on IntegriCloud