diff options
Diffstat (limited to 'ipsec-tools/src/racoon/pfkey.c')
-rw-r--r-- | ipsec-tools/src/racoon/pfkey.c | 3993 |
1 files changed, 3993 insertions, 0 deletions
diff --git a/ipsec-tools/src/racoon/pfkey.c b/ipsec-tools/src/racoon/pfkey.c new file mode 100644 index 00000000..d00b166d --- /dev/null +++ b/ipsec-tools/src/racoon/pfkey.c @@ -0,0 +1,3993 @@ +/* $NetBSD: pfkey.c,v 1.57 2011/03/15 13:20:14 vanhu Exp $ */ + +/* $Id: pfkey.c,v 1.57 2011/03/15 13:20:14 vanhu Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <netdb.h> +#include <errno.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <netdb.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#ifdef ENABLE_NATT +# ifdef __linux__ +# include <linux/udp.h> +# endif +# if defined(__NetBSD__) || defined(__FreeBSD__) || \ + (defined(__APPLE__) && defined(__MACH__)) +# include <netinet/udp.h> +# endif +#endif + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <sys/sysctl.h> + +#include <net/route.h> +#include <net/pfkeyv2.h> + +#include <netinet/in.h> +#include PATH_IPSEC_H +#include <fcntl.h> + +#include "libpfkey.h" + +#include "var.h" +#include "misc.h" +#include "vmbuf.h" +#include "plog.h" +#include "sockmisc.h" +#include "session.h" +#include "debug.h" + +#include "schedule.h" +#include "localconf.h" +#include "remoteconf.h" +#include "handler.h" +#include "policy.h" +#include "proposal.h" +#include "isakmp_var.h" +#include "isakmp.h" +#include "isakmp_inf.h" +#include "ipsec_doi.h" +#include "oakley.h" +#include "pfkey.h" +#include "algorithm.h" +#include "sainfo.h" +#include "admin.h" +#include "evt.h" +#include "privsep.h" +#include "strnames.h" +#include "backupsa.h" +#include "gcmalloc.h" +#include "nattraversal.h" +#include "crypto_openssl.h" +#include "grabmyaddr.h" + +#if defined(SADB_X_EALG_RIJNDAELCBC) && !defined(SADB_X_EALG_AESCBC) +#define SADB_X_EALG_AESCBC SADB_X_EALG_RIJNDAELCBC +#endif + +/* prototype */ +static u_int ipsecdoi2pfkey_aalg __P((u_int)); +static u_int ipsecdoi2pfkey_ealg __P((u_int)); +static u_int ipsecdoi2pfkey_calg __P((u_int)); +static u_int ipsecdoi2pfkey_alg __P((u_int, u_int)); +static u_int keylen_aalg __P((u_int)); +static u_int keylen_ealg __P((u_int, int)); + +static int pk_recvgetspi __P((caddr_t *)); +static int pk_recvupdate __P((caddr_t *)); +static int pk_recvadd __P((caddr_t *)); +static int pk_recvdelete __P((caddr_t *)); +static int pk_recvacquire __P((caddr_t *)); +static int pk_recvexpire __P((caddr_t *)); +static int pk_recvflush __P((caddr_t *)); +static int getsadbpolicy __P((caddr_t *, int *, int, struct ph2handle *)); +static int pk_recvspdupdate __P((caddr_t *)); +static int pk_recvspdadd __P((caddr_t *)); +static int pk_recvspddelete __P((caddr_t *)); +static int pk_recvspdexpire __P((caddr_t *)); +static int pk_recvspdget __P((caddr_t *)); +static int pk_recvspddump __P((caddr_t *)); +static int pk_recvspdflush __P((caddr_t *)); +#if defined(SADB_X_MIGRATE) && defined(SADB_X_EXT_KMADDRESS) +static int pk_recvmigrate __P((caddr_t *)); +#endif +static struct sadb_msg *pk_recv __P((int, int *)); + +static int (*pkrecvf[]) __P((caddr_t *)) = { +NULL, +pk_recvgetspi, +pk_recvupdate, +pk_recvadd, +pk_recvdelete, +NULL, /* SADB_GET */ +pk_recvacquire, +NULL, /* SABD_REGISTER */ +pk_recvexpire, +pk_recvflush, +NULL, /* SADB_DUMP */ +NULL, /* SADB_X_PROMISC */ +NULL, /* SADB_X_PCHANGE */ +pk_recvspdupdate, +pk_recvspdadd, +pk_recvspddelete, +pk_recvspdget, +NULL, /* SADB_X_SPDACQUIRE */ +pk_recvspddump, +pk_recvspdflush, +NULL, /* SADB_X_SPDSETIDX */ +pk_recvspdexpire, +NULL, /* SADB_X_SPDDELETE2 */ +NULL, /* SADB_X_NAT_T_NEW_MAPPING */ +#if defined(SADB_X_MIGRATE) && defined(SADB_X_EXT_KMADDRESS) +pk_recvmigrate, +#else +NULL, /* SADB_X_MIGRATE */ +#endif +#if (SADB_MAX > 24) +#error "SADB extra message?" +#endif +}; + +static int addnewsp __P((caddr_t *, struct sockaddr *, struct sockaddr *)); + +/* cope with old kame headers - ugly */ +#ifndef SADB_X_AALG_MD5 +#define SADB_X_AALG_MD5 SADB_AALG_MD5 +#endif +#ifndef SADB_X_AALG_SHA +#define SADB_X_AALG_SHA SADB_AALG_SHA +#endif +#ifndef SADB_X_AALG_NULL +#define SADB_X_AALG_NULL SADB_AALG_NULL +#endif + +#ifndef SADB_X_EALG_BLOWFISHCBC +#define SADB_X_EALG_BLOWFISHCBC SADB_EALG_BLOWFISHCBC +#endif +#ifndef SADB_X_EALG_CAST128CBC +#define SADB_X_EALG_CAST128CBC SADB_EALG_CAST128CBC +#endif +#ifndef SADB_X_EALG_RC5CBC +#ifdef SADB_EALG_RC5CBC +#define SADB_X_EALG_RC5CBC SADB_EALG_RC5CBC +#endif +#endif + +/* + * PF_KEY packet handler + * 0: success + * -1: fail + */ +static int +pfkey_handler(ctx, fd) + void *ctx; + int fd; +{ + struct sadb_msg *msg; + int len; + caddr_t mhp[SADB_EXT_MAX + 1]; + int error = -1; + + /* receive pfkey message. */ + len = 0; + msg = (struct sadb_msg *) pk_recv(fd, &len); + if (msg == NULL) { + if (len < 0) { + /* do not report EAGAIN as error; well get + * called from main loop later. and it's normal + * when spd dump is received during reload and + * this function is called in loop. */ + if (errno == EAGAIN) + goto end; + + plog(LLV_ERROR, LOCATION, NULL, + "failed to recv from pfkey (%s)\n", + strerror(errno)); + goto end; + } else { + /* short message - msg not ready */ + return 0; + } + } + + plog(LLV_DEBUG, LOCATION, NULL, "got pfkey %s message\n", + s_pfkey_type(msg->sadb_msg_type)); + plogdump(LLV_DEBUG2, msg, msg->sadb_msg_len << 3); + + /* validity check */ + if (msg->sadb_msg_errno) { + int pri; + + /* when SPD is empty, treat the state as no error. */ + if (msg->sadb_msg_type == SADB_X_SPDDUMP && + msg->sadb_msg_errno == ENOENT) + pri = LLV_DEBUG; + else + pri = LLV_ERROR; + + plog(pri, LOCATION, NULL, + "pfkey %s failed: %s\n", + s_pfkey_type(msg->sadb_msg_type), + strerror(msg->sadb_msg_errno)); + + goto end; + } + + /* check pfkey message. */ + if (pfkey_align(msg, mhp)) { + plog(LLV_ERROR, LOCATION, NULL, + "libipsec failed pfkey align (%s)\n", + ipsec_strerror()); + goto end; + } + if (pfkey_check(mhp)) { + plog(LLV_ERROR, LOCATION, NULL, + "libipsec failed pfkey check (%s)\n", + ipsec_strerror()); + goto end; + } + msg = (struct sadb_msg *)mhp[0]; + + /* safety check */ + if (msg->sadb_msg_type >= ARRAYLEN(pkrecvf)) { + plog(LLV_ERROR, LOCATION, NULL, + "unknown PF_KEY message type=%u\n", + msg->sadb_msg_type); + goto end; + } + + if (pkrecvf[msg->sadb_msg_type] == NULL) { + plog(LLV_INFO, LOCATION, NULL, + "unsupported PF_KEY message %s\n", + s_pfkey_type(msg->sadb_msg_type)); + goto end; + } + + if ((pkrecvf[msg->sadb_msg_type])(mhp) < 0) + goto end; + + error = 1; +end: + if (msg) + racoon_free(msg); + return(error); +} + +/* + * dump SADB + */ +vchar_t * +pfkey_dump_sadb(satype) + int satype; +{ + int s; + vchar_t *buf = NULL; + pid_t pid = getpid(); + struct sadb_msg *msg = NULL; + size_t bl, ml; + int len; + int bufsiz; + + if ((s = privsep_socket(PF_KEY, SOCK_RAW, PF_KEY_V2)) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "libipsec failed pfkey open: %s\n", + ipsec_strerror()); + return NULL; + } + + if ((bufsiz = pfkey_set_buffer_size(s, lcconf->pfkey_buffer_size)) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "libipsec failed pfkey set buffer size to %d: %s\n", + lcconf->pfkey_buffer_size, ipsec_strerror()); + return NULL; + } else if (bufsiz < lcconf->pfkey_buffer_size) { + plog(LLV_WARNING, LOCATION, NULL, + "pfkey socket receive buffer set to %dKB, instead of %d\n", + bufsiz, lcconf->pfkey_buffer_size); + } + + plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_dump\n"); + if (pfkey_send_dump(s, satype) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "libipsec failed dump: %s\n", ipsec_strerror()); + goto fail; + } + + while (1) { + if (msg) + racoon_free(msg); + msg = pk_recv(s, &len); + if (msg == NULL) { + if (len < 0) + goto done; + else + continue; + } + + if (msg->sadb_msg_type != SADB_DUMP || msg->sadb_msg_pid != pid) + { + plog(LLV_DEBUG, LOCATION, NULL, + "discarding non-sadb dump msg %p, our pid=%i\n", msg, pid); + plog(LLV_DEBUG, LOCATION, NULL, + "type %i, pid %i\n", msg->sadb_msg_type, msg->sadb_msg_pid); + continue; + } + + + ml = msg->sadb_msg_len << 3; + bl = buf ? buf->l : 0; + buf = vrealloc(buf, bl + ml); + if (buf == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "failed to reallocate buffer to dump.\n"); + goto fail; + } + memcpy(buf->v + bl, msg, ml); + + if (msg->sadb_msg_seq == 0) + break; + } + goto done; + +fail: + if (buf) + vfree(buf); + buf = NULL; +done: + if (msg) + racoon_free(msg); + close(s); + return buf; +} + +#ifdef ENABLE_ADMINPORT +/* + * flush SADB + */ +void +pfkey_flush_sadb(proto) + u_int proto; +{ + int satype; + + /* convert to SADB_SATYPE */ + if ((satype = admin2pfkey_proto(proto)) < 0) + return; + + plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_flush\n"); + if (pfkey_send_flush(lcconf->sock_pfkey, satype) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "libipsec failed send flush (%s)\n", ipsec_strerror()); + return; + } + + return; +} +#endif + +/* + * These are the SATYPEs that we manage. We register to get + * PF_KEY messages related to these SATYPEs, and we also use + * this list to determine which SATYPEs to delete SAs for when + * we receive an INITIAL-CONTACT. + */ +const struct pfkey_satype pfkey_satypes[] = { + { SADB_SATYPE_AH, "AH" }, + { SADB_SATYPE_ESP, "ESP" }, + { SADB_X_SATYPE_IPCOMP, "IPCOMP" }, +}; +const int pfkey_nsatypes = + sizeof(pfkey_satypes) / sizeof(pfkey_satypes[0]); + +/* + * PF_KEY initialization + */ +int +pfkey_init() +{ + int i, reg_fail; + int bufsiz; + + if ((lcconf->sock_pfkey = pfkey_open()) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "libipsec failed pfkey open (%s)\n", ipsec_strerror()); + return -1; + } + if ((bufsiz = pfkey_set_buffer_size(lcconf->sock_pfkey, + lcconf->pfkey_buffer_size)) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "libipsec failed to set pfkey buffer size to %d (%s)\n", + lcconf->pfkey_buffer_size, ipsec_strerror()); + return -1; + } else if (bufsiz < lcconf->pfkey_buffer_size) { + plog(LLV_WARNING, LOCATION, NULL, + "pfkey socket receive buffer set to %dKB, instead of %d\n", + bufsiz, lcconf->pfkey_buffer_size); + } + + if (fcntl(lcconf->sock_pfkey, F_SETFL, O_NONBLOCK) == -1) + plog(LLV_WARNING, LOCATION, NULL, + "failed to set the pfkey socket to NONBLOCK\n"); + + for (i = 0, reg_fail = 0; i < pfkey_nsatypes; i++) { + plog(LLV_DEBUG, LOCATION, NULL, + "call pfkey_send_register for %s\n", + pfkey_satypes[i].ps_name); + if (pfkey_send_register(lcconf->sock_pfkey, + pfkey_satypes[i].ps_satype) < 0 || + pfkey_recv_register(lcconf->sock_pfkey) < 0) { + plog(LLV_WARNING, LOCATION, NULL, + "failed to register %s (%s)\n", + pfkey_satypes[i].ps_name, + ipsec_strerror()); + reg_fail++; + } + } + + if (reg_fail == pfkey_nsatypes) { + plog(LLV_ERROR, LOCATION, NULL, + "failed to regist any protocol.\n"); + pfkey_close(lcconf->sock_pfkey); + return -1; + } + + initsp(); + + if (pfkey_send_spddump(lcconf->sock_pfkey) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "libipsec sending spddump failed: %s\n", + ipsec_strerror()); + pfkey_close(lcconf->sock_pfkey); + return -1; + } +#if 0 + if (pfkey_promisc_toggle(1) < 0) { + pfkey_close(lcconf->sock_pfkey); + return -1; + } +#endif + monitor_fd(lcconf->sock_pfkey, pfkey_handler, NULL, 0); + return 0; +} + +int +pfkey_reload() +{ + flushsp(); + + if (pfkey_send_spddump(lcconf->sock_pfkey) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "libipsec sending spddump failed: %s\n", + ipsec_strerror()); + return -1; + } + + while (pfkey_handler(NULL, lcconf->sock_pfkey) > 0) + continue; + + return 0; +} + +/* %%% for conversion */ +/* IPSECDOI_ATTR_AUTH -> SADB_AALG */ +static u_int +ipsecdoi2pfkey_aalg(hashtype) + u_int hashtype; +{ + switch (hashtype) { + case IPSECDOI_ATTR_AUTH_HMAC_MD5: + return SADB_AALG_MD5HMAC; + case IPSECDOI_ATTR_AUTH_HMAC_SHA1: + return SADB_AALG_SHA1HMAC; + case IPSECDOI_ATTR_AUTH_HMAC_SHA2_256: +#if (defined SADB_X_AALG_SHA2_256) && !defined(SADB_X_AALG_SHA2_256HMAC) + return SADB_X_AALG_SHA2_256; +#else + return SADB_X_AALG_SHA2_256HMAC; +#endif + case IPSECDOI_ATTR_AUTH_HMAC_SHA2_384: +#if (defined SADB_X_AALG_SHA2_384) && !defined(SADB_X_AALG_SHA2_384HMAC) + return SADB_X_AALG_SHA2_384; +#else + return SADB_X_AALG_SHA2_384HMAC; +#endif + case IPSECDOI_ATTR_AUTH_HMAC_SHA2_512: +#if (defined SADB_X_AALG_SHA2_512) && !defined(SADB_X_AALG_SHA2_512HMAC) + return SADB_X_AALG_SHA2_512; +#else + return SADB_X_AALG_SHA2_512HMAC; +#endif + case IPSECDOI_ATTR_AUTH_KPDK: /* need special care */ + return SADB_AALG_NONE; + + /* not supported */ + case IPSECDOI_ATTR_AUTH_DES_MAC: + plog(LLV_ERROR, LOCATION, NULL, + "Not supported hash type: %u\n", hashtype); + return ~0; + + case 0: /* reserved */ + default: + return SADB_AALG_NONE; + + plog(LLV_ERROR, LOCATION, NULL, + "Invalid hash type: %u\n", hashtype); + return ~0; + } + /*NOTREACHED*/ +} + +/* IPSECDOI_ESP -> SADB_EALG */ +static u_int +ipsecdoi2pfkey_ealg(t_id) + u_int t_id; +{ + switch (t_id) { + case IPSECDOI_ESP_DES_IV64: /* sa_flags |= SADB_X_EXT_OLD */ + return SADB_EALG_DESCBC; + case IPSECDOI_ESP_DES: + return SADB_EALG_DESCBC; + case IPSECDOI_ESP_3DES: + return SADB_EALG_3DESCBC; +#ifdef SADB_X_EALG_RC5CBC + case IPSECDOI_ESP_RC5: + return SADB_X_EALG_RC5CBC; +#endif + case IPSECDOI_ESP_CAST: + return SADB_X_EALG_CAST128CBC; + case IPSECDOI_ESP_BLOWFISH: + return SADB_X_EALG_BLOWFISHCBC; + case IPSECDOI_ESP_DES_IV32: /* flags |= (SADB_X_EXT_OLD| + SADB_X_EXT_IV4B)*/ + return SADB_EALG_DESCBC; + case IPSECDOI_ESP_NULL: + return SADB_EALG_NULL; +#ifdef SADB_X_EALG_AESCBC + case IPSECDOI_ESP_AES: + return SADB_X_EALG_AESCBC; +#endif +#ifdef SADB_X_EALG_TWOFISHCBC + case IPSECDOI_ESP_TWOFISH: + return SADB_X_EALG_TWOFISHCBC; +#endif +#ifdef SADB_X_EALG_CAMELLIACBC + case IPSECDOI_ESP_CAMELLIA: + return SADB_X_EALG_CAMELLIACBC; +#endif + + /* not supported */ + case IPSECDOI_ESP_3IDEA: + case IPSECDOI_ESP_IDEA: + case IPSECDOI_ESP_RC4: + plog(LLV_ERROR, LOCATION, NULL, + "Not supported transform: %u\n", t_id); + return ~0; + + case 0: /* reserved */ + default: + plog(LLV_ERROR, LOCATION, NULL, + "Invalid transform id: %u\n", t_id); + return ~0; + } + /*NOTREACHED*/ +} + +/* IPCOMP -> SADB_CALG */ +static u_int +ipsecdoi2pfkey_calg(t_id) + u_int t_id; +{ + switch (t_id) { + case IPSECDOI_IPCOMP_OUI: + return SADB_X_CALG_OUI; + case IPSECDOI_IPCOMP_DEFLATE: + return SADB_X_CALG_DEFLATE; + case IPSECDOI_IPCOMP_LZS: + return SADB_X_CALG_LZS; + + case 0: /* reserved */ + default: + plog(LLV_ERROR, LOCATION, NULL, + "Invalid transform id: %u\n", t_id); + return ~0; + } + /*NOTREACHED*/ +} + +/* IPSECDOI_PROTO -> SADB_SATYPE */ +u_int +ipsecdoi2pfkey_proto(proto) + u_int proto; +{ + switch (proto) { + case IPSECDOI_PROTO_IPSEC_AH: + return SADB_SATYPE_AH; + case IPSECDOI_PROTO_IPSEC_ESP: + return SADB_SATYPE_ESP; + case IPSECDOI_PROTO_IPCOMP: + return SADB_X_SATYPE_IPCOMP; + + default: + plog(LLV_ERROR, LOCATION, NULL, + "Invalid ipsec_doi proto: %u\n", proto); + return ~0; + } + /*NOTREACHED*/ +} + +static u_int +ipsecdoi2pfkey_alg(algclass, type) + u_int algclass, type; +{ + switch (algclass) { + case IPSECDOI_ATTR_AUTH: + return ipsecdoi2pfkey_aalg(type); + case IPSECDOI_PROTO_IPSEC_ESP: + return ipsecdoi2pfkey_ealg(type); + case IPSECDOI_PROTO_IPCOMP: + return ipsecdoi2pfkey_calg(type); + default: + plog(LLV_ERROR, LOCATION, NULL, + "Invalid ipsec_doi algclass: %u\n", algclass); + return ~0; + } + /*NOTREACHED*/ +} + +/* SADB_SATYPE -> IPSECDOI_PROTO */ +u_int +pfkey2ipsecdoi_proto(satype) + u_int satype; +{ + switch (satype) { + case SADB_SATYPE_AH: + return IPSECDOI_PROTO_IPSEC_AH; + case SADB_SATYPE_ESP: + return IPSECDOI_PROTO_IPSEC_ESP; + case SADB_X_SATYPE_IPCOMP: + return IPSECDOI_PROTO_IPCOMP; + + default: + plog(LLV_ERROR, LOCATION, NULL, + "Invalid pfkey proto: %u\n", satype); + return ~0; + } + /*NOTREACHED*/ +} + +/* IPSECDOI_ATTR_ENC_MODE -> IPSEC_MODE */ +u_int +ipsecdoi2pfkey_mode(mode) + u_int mode; +{ + switch (mode) { + case IPSECDOI_ATTR_ENC_MODE_TUNNEL: +#ifdef ENABLE_NATT + case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_RFC: + case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_DRAFT: +#endif + return IPSEC_MODE_TUNNEL; + case IPSECDOI_ATTR_ENC_MODE_TRNS: +#ifdef ENABLE_NATT + case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_RFC: + case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_DRAFT: +#endif + return IPSEC_MODE_TRANSPORT; + default: + plog(LLV_ERROR, LOCATION, NULL, "Invalid mode type: %u\n", mode); + return ~0; + } + /*NOTREACHED*/ +} + +/* IPSECDOI_ATTR_ENC_MODE -> IPSEC_MODE */ +u_int +pfkey2ipsecdoi_mode(mode) + u_int mode; +{ + switch (mode) { + case IPSEC_MODE_TUNNEL: + return IPSECDOI_ATTR_ENC_MODE_TUNNEL; + case IPSEC_MODE_TRANSPORT: + return IPSECDOI_ATTR_ENC_MODE_TRNS; + case IPSEC_MODE_ANY: + return IPSECDOI_ATTR_ENC_MODE_ANY; + default: + plog(LLV_ERROR, LOCATION, NULL, "Invalid mode type: %u\n", mode); + return ~0; + } + /*NOTREACHED*/ +} + +/* default key length for encryption algorithm */ +static u_int +keylen_aalg(hashtype) + u_int hashtype; +{ + int res; + + if (hashtype == 0) + return SADB_AALG_NONE; + + res = alg_ipsec_hmacdef_hashlen(hashtype); + if (res == -1) { + plog(LLV_ERROR, LOCATION, NULL, + "invalid hmac algorithm %u.\n", hashtype); + return ~0; + } + return res; +} + +/* default key length for encryption algorithm */ +static u_int +keylen_ealg(enctype, encklen) + u_int enctype; + int encklen; +{ + int res; + + res = alg_ipsec_encdef_keylen(enctype, encklen); + if (res == -1) { + plog(LLV_ERROR, LOCATION, NULL, + "invalid encryption algorithm %u.\n", enctype); + return ~0; + } + return res; +} + +void +pk_fixup_sa_addresses(mhp) + caddr_t *mhp; +{ + struct sockaddr *src, *dst; + + src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); + dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]); + set_port(src, PORT_ISAKMP); + set_port(dst, PORT_ISAKMP); + +#ifdef ENABLE_NATT + if (PFKEY_ADDR_X_NATTYPE(mhp[SADB_X_EXT_NAT_T_TYPE])) { + /* NAT-T is enabled for this SADB entry; copy + * the ports from NAT-T extensions */ + if(mhp[SADB_X_EXT_NAT_T_SPORT] != NULL) + set_port(src, PFKEY_ADDR_X_PORT(mhp[SADB_X_EXT_NAT_T_SPORT])); + if(mhp[SADB_X_EXT_NAT_T_DPORT] != NULL) + set_port(dst, PFKEY_ADDR_X_PORT(mhp[SADB_X_EXT_NAT_T_DPORT])); + } +#endif +} + +int +pfkey_convertfromipsecdoi(proto_id, t_id, hashtype, + e_type, e_keylen, a_type, a_keylen, flags) + u_int proto_id; + u_int t_id; + u_int hashtype; + u_int *e_type; + u_int *e_keylen; + u_int *a_type; + u_int *a_keylen; + u_int *flags; +{ + *flags = 0; + switch (proto_id) { + case IPSECDOI_PROTO_IPSEC_ESP: + if ((*e_type = ipsecdoi2pfkey_ealg(t_id)) == ~0) + goto bad; + if ((*e_keylen = keylen_ealg(t_id, *e_keylen)) == ~0) + goto bad; + *e_keylen >>= 3; + + if ((*a_type = ipsecdoi2pfkey_aalg(hashtype)) == ~0) + goto bad; + if ((*a_keylen = keylen_aalg(hashtype)) == ~0) + goto bad; + *a_keylen >>= 3; + + if (*e_type == SADB_EALG_NONE) { + plog(LLV_ERROR, LOCATION, NULL, "no ESP algorithm.\n"); + goto bad; + } + break; + + case IPSECDOI_PROTO_IPSEC_AH: + if ((*a_type = ipsecdoi2pfkey_aalg(hashtype)) == ~0) + goto bad; + if ((*a_keylen = keylen_aalg(hashtype)) == ~0) + goto bad; + *a_keylen >>= 3; + + if (t_id == IPSECDOI_ATTR_AUTH_HMAC_MD5 + && hashtype == IPSECDOI_ATTR_AUTH_KPDK) { + /* AH_MD5 + Auth(KPDK) = RFC1826 keyed-MD5 */ + *a_type = SADB_X_AALG_MD5; + *flags |= SADB_X_EXT_OLD; + } + *e_type = SADB_EALG_NONE; + *e_keylen = 0; + if (*a_type == SADB_AALG_NONE) { + plog(LLV_ERROR, LOCATION, NULL, "no AH algorithm.\n"); + goto bad; + } + break; + + case IPSECDOI_PROTO_IPCOMP: + if ((*e_type = ipsecdoi2pfkey_calg(t_id)) == ~0) + goto bad; + *e_keylen = 0; + + *flags = SADB_X_EXT_RAWCPI; + + *a_type = SADB_AALG_NONE; + *a_keylen = 0; + if (*e_type == SADB_X_CALG_NONE) { + plog(LLV_ERROR, LOCATION, NULL, "no IPCOMP algorithm.\n"); + goto bad; + } + break; + + default: + plog(LLV_ERROR, LOCATION, NULL, "unknown IPsec protocol.\n"); + goto bad; + } + + return 0; + + bad: + errno = EINVAL; + return -1; +} + +/*%%%*/ +/* send getspi message per ipsec protocol per remote address */ +/* + * the local address and remote address in ph1handle are dealed + * with destination address and source address respectively. + * Because SPI is decided by responder. + */ +int +pk_sendgetspi(iph2) + struct ph2handle *iph2; +{ + struct sockaddr *src = NULL, *dst = NULL; + u_int satype, mode; + struct saprop *pp; + struct saproto *pr; + u_int32_t minspi, maxspi; + u_int8_t natt_type = 0; + u_int16_t sport = 0, dport = 0; + + if (iph2->side == INITIATOR) + pp = iph2->proposal; + else + pp = iph2->approval; + + if (iph2->sa_src && iph2->sa_dst) { + /* MIPv6: Use SA addresses, not IKE ones */ + src = dupsaddr(iph2->sa_src); + dst = dupsaddr(iph2->sa_dst); + } else { + /* Common case: SA addresses and IKE ones are the same */ + src = dupsaddr(iph2->src); + dst = dupsaddr(iph2->dst); + } + + if (src == NULL || dst == NULL) { + racoon_free(src); + racoon_free(dst); + return -1; + } + + for (pr = pp->head; pr != NULL; pr = pr->next) { + + /* validity check */ + satype = ipsecdoi2pfkey_proto(pr->proto_id); + if (satype == ~0) { + plog(LLV_ERROR, LOCATION, NULL, + "invalid proto_id %d\n", pr->proto_id); + racoon_free(src); + racoon_free(dst); + return -1; + } + /* this works around a bug in Linux kernel where it allocates 4 byte + spi's for IPCOMP */ + else if (satype == SADB_X_SATYPE_IPCOMP) { + minspi = 0x100; + maxspi = 0xffff; + } + else { + minspi = 0; + maxspi = 0; + } + mode = ipsecdoi2pfkey_mode(pr->encmode); + if (mode == ~0) { + plog(LLV_ERROR, LOCATION, NULL, + "invalid encmode %d\n", pr->encmode); + racoon_free(src); + racoon_free(dst); + return -1; + } + +#ifdef ENABLE_NATT + if (pr->udp_encap) { + natt_type = iph2->ph1->natt_options->encaps_type; + sport=extract_port(src); + dport=extract_port(dst); + } +#endif + + plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_getspi\n"); + if (pfkey_send_getspi_nat( + lcconf->sock_pfkey, + satype, + mode, + dst, /* src of SA */ + src, /* dst of SA */ + natt_type, + dport, + sport, + minspi, maxspi, + pr->reqid_in, iph2->seq) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "ipseclib failed send getspi (%s)\n", + ipsec_strerror()); + racoon_free(src); + racoon_free(dst); + return -1; + } + plog(LLV_DEBUG, LOCATION, NULL, + "pfkey GETSPI sent: %s\n", + sadbsecas2str(dst, src, satype, 0, mode)); + } + + racoon_free(src); + racoon_free(dst); + return 0; +} + +/* + * receive GETSPI from kernel. + */ +static int +pk_recvgetspi(mhp) + caddr_t *mhp; +{ + struct sadb_msg *msg; + struct sadb_sa *sa; + struct ph2handle *iph2; + struct sockaddr *src, *dst; + int proto_id; + int allspiok, notfound; + struct saprop *pp; + struct saproto *pr; + + /* validity check */ + if (mhp[SADB_EXT_SA] == NULL + || mhp[SADB_EXT_ADDRESS_DST] == NULL + || mhp[SADB_EXT_ADDRESS_SRC] == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "inappropriate sadb getspi message passed.\n"); + return -1; + } + msg = (struct sadb_msg *)mhp[0]; + sa = (struct sadb_sa *)mhp[SADB_EXT_SA]; + pk_fixup_sa_addresses(mhp); + dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); /* note SA dir */ + src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]); + + /* the message has to be processed or not ? */ + if (msg->sadb_msg_pid != getpid()) { + plog(LLV_DEBUG, LOCATION, NULL, + "%s message is not interesting " + "because pid %d is not mine.\n", + s_pfkey_type(msg->sadb_msg_type), + msg->sadb_msg_pid); + return -1; + } + + iph2 = getph2byseq(msg->sadb_msg_seq); + if (iph2 == NULL) { + plog(LLV_DEBUG, LOCATION, NULL, + "seq %d of %s message not interesting.\n", + msg->sadb_msg_seq, + s_pfkey_type(msg->sadb_msg_type)); + return -1; + } + + if (iph2->status != PHASE2ST_GETSPISENT) { + plog(LLV_ERROR, LOCATION, NULL, + "status mismatch (db:%d msg:%d)\n", + iph2->status, PHASE2ST_GETSPISENT); + return -1; + } + + /* set SPI, and check to get all spi whether or not */ + allspiok = 1; + notfound = 1; + proto_id = pfkey2ipsecdoi_proto(msg->sadb_msg_satype); + pp = iph2->side == INITIATOR ? iph2->proposal : iph2->approval; + + for (pr = pp->head; pr != NULL; pr = pr->next) { + if (pr->proto_id == proto_id && pr->spi == 0) { + pr->spi = sa->sadb_sa_spi; + notfound = 0; + plog(LLV_DEBUG, LOCATION, NULL, + "pfkey GETSPI succeeded: %s\n", + sadbsecas2str(dst, src, + msg->sadb_msg_satype, + sa->sadb_sa_spi, + ipsecdoi2pfkey_mode(pr->encmode))); + } + if (pr->spi == 0) + allspiok = 0; /* not get all spi */ + } + + if (notfound) { + plog(LLV_ERROR, LOCATION, NULL, + "get spi for unknown address %s\n", + saddrwop2str(dst)); + return -1; + } + + if (allspiok) { + /* update status */ + iph2->status = PHASE2ST_GETSPIDONE; + if (isakmp_post_getspi(iph2) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "failed to start post getspi.\n"); + remph2(iph2); + delph2(iph2); + iph2 = NULL; + return -1; + } + } + + return 0; +} + +/* + * set inbound SA + */ +int +pk_sendupdate(iph2) + struct ph2handle *iph2; +{ + struct saproto *pr; + struct pfkey_send_sa_args sa_args; + + /* sanity check */ + if (iph2->approval == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "no approvaled SAs found.\n"); + return -1; + } + + /* fill in some needed for pfkey_send_update2 */ + memset (&sa_args, 0, sizeof (sa_args)); + sa_args.so = lcconf->sock_pfkey; + if (iph2->lifetime_secs) + sa_args.l_addtime = iph2->lifetime_secs; + else + sa_args.l_addtime = iph2->approval->lifetime; + sa_args.seq = iph2->seq; + sa_args.wsize = 4; + + if (iph2->sa_src && iph2->sa_dst) { + /* MIPv6: Use SA addresses, not IKE ones */ + sa_args.dst = dupsaddr(iph2->sa_src); + sa_args.src = dupsaddr(iph2->sa_dst); + } else { + /* Common case: SA addresses and IKE ones are the same */ + sa_args.dst = dupsaddr(iph2->src); + sa_args.src = dupsaddr(iph2->dst); + } + + if (sa_args.src == NULL || sa_args.dst == NULL) { + racoon_free(sa_args.src); + racoon_free(sa_args.dst); + return -1; + } + + for (pr = iph2->approval->head; pr != NULL; pr = pr->next) { + /* validity check */ + sa_args.satype = ipsecdoi2pfkey_proto(pr->proto_id); + if (sa_args.satype == ~0) { + plog(LLV_ERROR, LOCATION, NULL, + "invalid proto_id %d\n", pr->proto_id); + racoon_free(sa_args.src); + racoon_free(sa_args.dst); + return -1; + } + else if (sa_args.satype == SADB_X_SATYPE_IPCOMP) { + /* IPCOMP has no replay window */ + sa_args.wsize = 0; + } +#ifdef ENABLE_SAMODE_UNSPECIFIED + sa_args.mode = IPSEC_MODE_ANY; +#else + sa_args.mode = ipsecdoi2pfkey_mode(pr->encmode); + if (sa_args.mode == ~0) { + plog(LLV_ERROR, LOCATION, NULL, + "invalid encmode %d\n", pr->encmode); + racoon_free(sa_args.src); + racoon_free(sa_args.dst); + return -1; + } +#endif + /* set algorithm type and key length */ + sa_args.e_keylen = pr->head->encklen; + if (pfkey_convertfromipsecdoi( + pr->proto_id, + pr->head->trns_id, + pr->head->authtype, + &sa_args.e_type, &sa_args.e_keylen, + &sa_args.a_type, &sa_args.a_keylen, + &sa_args.flags) < 0){ + racoon_free(sa_args.src); + racoon_free(sa_args.dst); + return -1; + } + +#if 0 + sa_args.l_bytes = iph2->approval->lifebyte * 1024, +#else + sa_args.l_bytes = 0; +#endif + +#ifdef HAVE_SECCTX + if (*iph2->approval->sctx.ctx_str) { + sa_args.ctxdoi = iph2->approval->sctx.ctx_doi; + sa_args.ctxalg = iph2->approval->sctx.ctx_alg; + sa_args.ctxstrlen = iph2->approval->sctx.ctx_strlen; + sa_args.ctxstr = iph2->approval->sctx.ctx_str; + } +#endif /* HAVE_SECCTX */ + +#ifdef ENABLE_NATT + if (pr->udp_encap) { + sa_args.l_natt_type = iph2->ph1->natt_options->encaps_type; + sa_args.l_natt_sport = extract_port(iph2->ph1->remote); + sa_args.l_natt_dport = extract_port(iph2->ph1->local); + sa_args.l_natt_oa = iph2->natoa_src; +#ifdef SADB_X_EXT_NAT_T_FRAG + sa_args.l_natt_frag = iph2->ph1->rmconf->esp_frag; +#endif + } +#endif + + /* more info to fill in */ + sa_args.spi = pr->spi; + sa_args.reqid = pr->reqid_in; + sa_args.keymat = pr->keymat->v; + + plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_update2\n"); + if (pfkey_send_update2(&sa_args) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "libipsec failed send update (%s)\n", + ipsec_strerror()); + racoon_free(sa_args.src); + racoon_free(sa_args.dst); + return -1; + } + + if (!lcconf->pathinfo[LC_PATHTYPE_BACKUPSA]) + continue; + + /* + * It maybe good idea to call backupsa_to_file() after + * racoon will receive the sadb_update messages. + * But it is impossible because there is not key in the + * information from the kernel. + */ + + /* change some things before backing up */ + sa_args.wsize = 4; + sa_args.l_bytes = iph2->approval->lifebyte * 1024; + + if (backupsa_to_file(&sa_args) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "backuped SA failed: %s\n", + sadbsecas2str(sa_args.src, sa_args.dst, + sa_args.satype, sa_args.spi, sa_args.mode)); + } + plog(LLV_DEBUG, LOCATION, NULL, + "backuped SA: %s\n", + sadbsecas2str(sa_args.src, sa_args.dst, + sa_args.satype, sa_args.spi, sa_args.mode)); + } + + racoon_free(sa_args.src); + racoon_free(sa_args.dst); + return 0; +} + +static int +pk_recvupdate(mhp) + caddr_t *mhp; +{ + struct sadb_msg *msg; + struct sadb_sa *sa; + struct sockaddr *src, *dst; + struct ph2handle *iph2; + u_int proto_id, encmode, sa_mode; + int incomplete = 0; + struct saproto *pr; + + /* ignore this message because of local test mode. */ + if (f_local) + return 0; + + /* sanity check */ + if (mhp[0] == NULL + || mhp[SADB_EXT_SA] == NULL + || mhp[SADB_EXT_ADDRESS_SRC] == NULL + || mhp[SADB_EXT_ADDRESS_DST] == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "inappropriate sadb update message passed.\n"); + return -1; + } + msg = (struct sadb_msg *)mhp[0]; + pk_fixup_sa_addresses(mhp); + src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); + dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]); + sa = (struct sadb_sa *)mhp[SADB_EXT_SA]; + + sa_mode = mhp[SADB_X_EXT_SA2] == NULL + ? IPSEC_MODE_ANY + : ((struct sadb_x_sa2 *)mhp[SADB_X_EXT_SA2])->sadb_x_sa2_mode; + + /* the message has to be processed or not ? */ + if (msg->sadb_msg_pid != getpid()) { + plog(LLV_DEBUG, LOCATION, NULL, + "%s message is not interesting " + "because pid %d is not mine.\n", + s_pfkey_type(msg->sadb_msg_type), + msg->sadb_msg_pid); + return -1; + } + + iph2 = getph2byseq(msg->sadb_msg_seq); + if (iph2 == NULL) { + plog(LLV_DEBUG, LOCATION, NULL, + "seq %d of %s message not interesting.\n", + msg->sadb_msg_seq, + s_pfkey_type(msg->sadb_msg_type)); + return -1; + } + + if (iph2->status != PHASE2ST_ADDSA) { + plog(LLV_ERROR, LOCATION, NULL, + "status mismatch (db:%d msg:%d)\n", + iph2->status, PHASE2ST_ADDSA); + return -1; + } + + /* check to complete all keys ? */ + for (pr = iph2->approval->head; pr != NULL; pr = pr->next) { + proto_id = pfkey2ipsecdoi_proto(msg->sadb_msg_satype); + if (proto_id == ~0) { + plog(LLV_ERROR, LOCATION, NULL, + "invalid proto_id %d\n", msg->sadb_msg_satype); + return -1; + } + encmode = pfkey2ipsecdoi_mode(sa_mode); + if (encmode == ~0) { + plog(LLV_ERROR, LOCATION, NULL, + "invalid encmode %d\n", sa_mode); + return -1; + } + + if (pr->proto_id == proto_id + && pr->spi == sa->sadb_sa_spi) { + pr->ok = 1; + plog(LLV_DEBUG, LOCATION, NULL, + "pfkey UPDATE succeeded: %s\n", + sadbsecas2str(dst, src, + msg->sadb_msg_satype, + sa->sadb_sa_spi, + sa_mode)); + + plog(LLV_INFO, LOCATION, NULL, + "IPsec-SA established: %s\n", + sadbsecas2str(dst, src, + msg->sadb_msg_satype, sa->sadb_sa_spi, + sa_mode)); + } + + if (pr->ok == 0) + incomplete = 1; + } + + if (incomplete) + return 0; + + /* turn off the timer for calling pfkey_timeover() */ + sched_cancel(&iph2->sce); + + /* update status */ + iph2->status = PHASE2ST_ESTABLISHED; + evt_phase2(iph2, EVT_PHASE2_UP, NULL); + +#ifdef ENABLE_STATS + gettimeofday(&iph2->end, NULL); + syslog(LOG_NOTICE, "%s(%s): %8.6f", + "phase2", "quick", timedelta(&iph2->start, &iph2->end)); +#endif + + /* turn off schedule */ + sched_cancel(&iph2->scr); + + /* + * since we are going to reuse the phase2 handler, we need to + * remain it and refresh all the references between ph1 and ph2 to use. + */ + sched_schedule(&iph2->sce, iph2->approval->lifetime, + isakmp_ph2expire_stub); + + plog(LLV_DEBUG, LOCATION, NULL, "===\n"); + return 0; +} + +/* + * set outbound SA + */ +int +pk_sendadd(iph2) + struct ph2handle *iph2; +{ + struct saproto *pr; + struct pfkey_send_sa_args sa_args; + + /* sanity check */ + if (iph2->approval == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "no approvaled SAs found.\n"); + return -1; + } + + /* fill in some needed for pfkey_send_update2 */ + memset (&sa_args, 0, sizeof (sa_args)); + sa_args.so = lcconf->sock_pfkey; + if (iph2->lifetime_secs) + sa_args.l_addtime = iph2->lifetime_secs; + else + sa_args.l_addtime = iph2->approval->lifetime; + sa_args.seq = iph2->seq; + sa_args.wsize = 4; + + if (iph2->sa_src && iph2->sa_dst) { + /* MIPv6: Use SA addresses, not IKE ones */ + sa_args.src = dupsaddr(iph2->sa_src); + sa_args.dst = dupsaddr(iph2->sa_dst); + } else { + /* Common case: SA addresses and IKE ones are the same */ + sa_args.src = dupsaddr(iph2->src); + sa_args.dst = dupsaddr(iph2->dst); + } + + if (sa_args.src == NULL || sa_args.dst == NULL) { + racoon_free(sa_args.src); + racoon_free(sa_args.dst); + return -1; + } + + for (pr = iph2->approval->head; pr != NULL; pr = pr->next) { + /* validity check */ + sa_args.satype = ipsecdoi2pfkey_proto(pr->proto_id); + if (sa_args.satype == ~0) { + plog(LLV_ERROR, LOCATION, NULL, + "invalid proto_id %d\n", pr->proto_id); + racoon_free(sa_args.src); + racoon_free(sa_args.dst); + return -1; + } + else if (sa_args.satype == SADB_X_SATYPE_IPCOMP) { + /* no replay window for IPCOMP */ + sa_args.wsize = 0; + } +#ifdef ENABLE_SAMODE_UNSPECIFIED + sa_args.mode = IPSEC_MODE_ANY; +#else + sa_args.mode = ipsecdoi2pfkey_mode(pr->encmode); + if (sa_args.mode == ~0) { + plog(LLV_ERROR, LOCATION, NULL, + "invalid encmode %d\n", pr->encmode); + racoon_free(sa_args.src); + racoon_free(sa_args.dst); + return -1; + } +#endif + + /* set algorithm type and key length */ + sa_args.e_keylen = pr->head->encklen; + if (pfkey_convertfromipsecdoi( + pr->proto_id, + pr->head->trns_id, + pr->head->authtype, + &sa_args.e_type, &sa_args.e_keylen, + &sa_args.a_type, &sa_args.a_keylen, + &sa_args.flags) < 0){ + racoon_free(sa_args.src); + racoon_free(sa_args.dst); + return -1; + } + +#if 0 + sa_args.l_bytes = iph2->approval->lifebyte * 1024, +#else + sa_args.l_bytes = 0; +#endif + +#ifdef HAVE_SECCTX + if (*iph2->approval->sctx.ctx_str) { + sa_args.ctxdoi = iph2->approval->sctx.ctx_doi; + sa_args.ctxalg = iph2->approval->sctx.ctx_alg; + sa_args.ctxstrlen = iph2->approval->sctx.ctx_strlen; + sa_args.ctxstr = iph2->approval->sctx.ctx_str; + } +#endif /* HAVE_SECCTX */ + +#ifdef ENABLE_NATT + plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_add2 " + "(NAT flavor)\n"); + + if (pr->udp_encap) { + sa_args.l_natt_type = UDP_ENCAP_ESPINUDP; + sa_args.l_natt_sport = extract_port(iph2->ph1->local); + sa_args.l_natt_dport = extract_port(iph2->ph1->remote); + sa_args.l_natt_oa = iph2->natoa_dst; +#ifdef SADB_X_EXT_NAT_T_FRAG + sa_args.l_natt_frag = iph2->ph1->rmconf->esp_frag; +#endif + } +#endif + /* more info to fill in */ + sa_args.spi = pr->spi_p; + sa_args.reqid = pr->reqid_out; + sa_args.keymat = pr->keymat_p->v; + + plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_add2\n"); + if (pfkey_send_add2(&sa_args) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "libipsec failed send add (%s)\n", + ipsec_strerror()); + racoon_free(sa_args.src); + racoon_free(sa_args.dst); + return -1; + } + + if (!lcconf->pathinfo[LC_PATHTYPE_BACKUPSA]) + continue; + + /* + * It maybe good idea to call backupsa_to_file() after + * racoon will receive the sadb_update messages. + * But it is impossible because there is not key in the + * information from the kernel. + */ + if (backupsa_to_file(&sa_args) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "backuped SA failed: %s\n", + sadbsecas2str(sa_args.src, sa_args.dst, + sa_args.satype, sa_args.spi, sa_args.mode)); + } + plog(LLV_DEBUG, LOCATION, NULL, + "backuped SA: %s\n", + sadbsecas2str(sa_args.src, sa_args.dst, + sa_args.satype, sa_args.spi, sa_args.mode)); + } + racoon_free(sa_args.src); + racoon_free(sa_args.dst); + return 0; +} + +static int +pk_recvadd(mhp) + caddr_t *mhp; +{ + struct sadb_msg *msg; + struct sadb_sa *sa; + struct sockaddr *src, *dst; + struct ph2handle *iph2; + u_int sa_mode; + + /* ignore this message because of local test mode. */ + if (f_local) + return 0; + + /* sanity check */ + if (mhp[0] == NULL + || mhp[SADB_EXT_SA] == NULL + || mhp[SADB_EXT_ADDRESS_SRC] == NULL + || mhp[SADB_EXT_ADDRESS_DST] == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "inappropriate sadb add message passed.\n"); + return -1; + } + msg = (struct sadb_msg *)mhp[0]; + pk_fixup_sa_addresses(mhp); + src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); + dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]); + sa = (struct sadb_sa *)mhp[SADB_EXT_SA]; + + sa_mode = mhp[SADB_X_EXT_SA2] == NULL + ? IPSEC_MODE_ANY + : ((struct sadb_x_sa2 *)mhp[SADB_X_EXT_SA2])->sadb_x_sa2_mode; + + /* the message has to be processed or not ? */ + if (msg->sadb_msg_pid != getpid()) { + plog(LLV_DEBUG, LOCATION, NULL, + "%s message is not interesting " + "because pid %d is not mine.\n", + s_pfkey_type(msg->sadb_msg_type), + msg->sadb_msg_pid); + return -1; + } + + iph2 = getph2byseq(msg->sadb_msg_seq); + if (iph2 == NULL) { + plog(LLV_DEBUG, LOCATION, NULL, + "seq %d of %s message not interesting.\n", + msg->sadb_msg_seq, + s_pfkey_type(msg->sadb_msg_type)); + return -1; + } + + /* + * NOTE don't update any status of phase2 handle + * because they must be updated by SADB_UPDATE message + */ + + plog(LLV_INFO, LOCATION, NULL, + "IPsec-SA established: %s\n", + sadbsecas2str(src, dst, + msg->sadb_msg_satype, sa->sadb_sa_spi, sa_mode)); + + plog(LLV_DEBUG, LOCATION, NULL, "===\n"); + return 0; +} + +static int +pk_recvexpire(mhp) + caddr_t *mhp; +{ + struct sadb_msg *msg; + struct sadb_sa *sa; + struct sockaddr *src, *dst; + struct ph2handle *iph2; + u_int proto_id, sa_mode; + + /* sanity check */ + if (mhp[0] == NULL + || mhp[SADB_EXT_SA] == NULL + || mhp[SADB_EXT_ADDRESS_SRC] == NULL + || mhp[SADB_EXT_ADDRESS_DST] == NULL + || (mhp[SADB_EXT_LIFETIME_HARD] != NULL + && mhp[SADB_EXT_LIFETIME_SOFT] != NULL)) { + plog(LLV_ERROR, LOCATION, NULL, + "inappropriate sadb expire message passed.\n"); + return -1; + } + msg = (struct sadb_msg *)mhp[0]; + sa = (struct sadb_sa *)mhp[SADB_EXT_SA]; + pk_fixup_sa_addresses(mhp); + src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); + dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]); + + sa_mode = mhp[SADB_X_EXT_SA2] == NULL + ? IPSEC_MODE_ANY + : ((struct sadb_x_sa2 *)mhp[SADB_X_EXT_SA2])->sadb_x_sa2_mode; + + proto_id = pfkey2ipsecdoi_proto(msg->sadb_msg_satype); + if (proto_id == ~0) { + plog(LLV_ERROR, LOCATION, NULL, + "invalid proto_id %d\n", msg->sadb_msg_satype); + return -1; + } + + plog(LLV_INFO, LOCATION, NULL, + "IPsec-SA expired: %s\n", + sadbsecas2str(src, dst, + msg->sadb_msg_satype, sa->sadb_sa_spi, sa_mode)); + + iph2 = getph2bysaidx(src, dst, proto_id, sa->sadb_sa_spi); + if (iph2 == NULL) { + /* + * Ignore it because two expire messages are come up. + * phase2 handler has been deleted already when 2nd message + * is received. + */ + plog(LLV_DEBUG, LOCATION, NULL, + "no such a SA found: %s\n", + sadbsecas2str(src, dst, + msg->sadb_msg_satype, sa->sadb_sa_spi, + sa_mode)); + return 0; + } + + /* resent expiry message? */ + if (iph2->status > PHASE2ST_ESTABLISHED) + return 0; + + /* still negotiating? */ + if (iph2->status < PHASE2ST_ESTABLISHED) { + /* not a hard timeout? */ + if (mhp[SADB_EXT_LIFETIME_HARD] == NULL) + return 0; + + /* + * We were negotiating for that SA (w/o much success + * from current status) and kernel has decided our time + * is over trying (xfrm_larval_drop controls that and + * is enabled by default on Linux >= 2.6.28 kernels). + */ + plog(LLV_WARNING, LOCATION, NULL, + "PF_KEY EXPIRE message received from kernel for SA" + " being negotiated. Stopping negotiation.\n"); + } + + /* turn off the timer for calling isakmp_ph2expire() */ + sched_cancel(&iph2->sce); + + if (iph2->status == PHASE2ST_ESTABLISHED && + iph2->side == INITIATOR) { + struct ph1handle *iph1hint; + /* + * Active phase 2 expired and we were initiator. + * Begin new phase 2 exchange, so we can keep on sending + * traffic. + */ + + /* update status for re-use */ + iph1hint = iph2->ph1; + initph2(iph2); + iph2->status = PHASE2ST_STATUS2; + + /* start quick exchange */ + if (isakmp_post_acquire(iph2, iph1hint, FALSE) < 0) { + plog(LLV_ERROR, LOCATION, iph2->dst, + "failed to begin ipsec sa " + "re-negotication.\n"); + remph2(iph2); + delph2(iph2); + return -1; + } + + return 0; + } + + /* + * We are responder or the phase 2 was not established. + * Just remove the ph2handle to reflect SADB. + */ + iph2->status = PHASE2ST_EXPIRED; + remph2(iph2); + delph2(iph2); + + return 0; +} + +static int +pk_recvacquire(mhp) + caddr_t *mhp; +{ + struct sadb_msg *msg; + struct sadb_x_policy *xpl; + struct secpolicy *sp_out = NULL, *sp_in = NULL; + struct ph2handle *iph2; + struct sockaddr *src, *dst; /* IKE addresses (for exchanges) */ + struct sockaddr *sp_src, *sp_dst; /* SP addresses (selectors). */ + struct sockaddr *sa_src = NULL, *sa_dst = NULL ; /* SA addresses */ +#ifdef HAVE_SECCTX + struct sadb_x_sec_ctx *m_sec_ctx; +#endif /* HAVE_SECCTX */ + struct policyindex spidx; + + /* ignore this message because of local test mode. */ + if (f_local) + return 0; + + /* sanity check */ + if (mhp[0] == NULL + || mhp[SADB_EXT_ADDRESS_SRC] == NULL + || mhp[SADB_EXT_ADDRESS_DST] == NULL + || mhp[SADB_X_EXT_POLICY] == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "inappropriate sadb acquire message passed.\n"); + return -1; + } + msg = (struct sadb_msg *)mhp[0]; + xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY]; + /* acquire does not have nat-t ports; so do not bother setting + * the default port 500; just use the port zero for wildcard + * matching the get a valid natted destination */ + sp_src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); + sp_dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]); + +#ifdef HAVE_SECCTX + m_sec_ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX]; + + if (m_sec_ctx != NULL) { + plog(LLV_INFO, LOCATION, NULL, "security context doi: %u\n", + m_sec_ctx->sadb_x_ctx_doi); + plog(LLV_INFO, LOCATION, NULL, + "security context algorithm: %u\n", + m_sec_ctx->sadb_x_ctx_alg); + plog(LLV_INFO, LOCATION, NULL, "security context length: %u\n", + m_sec_ctx->sadb_x_ctx_len); + plog(LLV_INFO, LOCATION, NULL, "security context: %s\n", + ((char *)m_sec_ctx + sizeof(struct sadb_x_sec_ctx))); + } +#endif /* HAVE_SECCTX */ + + /* ignore if type is not IPSEC_POLICY_IPSEC */ + if (xpl->sadb_x_policy_type != IPSEC_POLICY_IPSEC) { + plog(LLV_DEBUG, LOCATION, NULL, + "ignore ACQUIRE message. type is not IPsec.\n"); + return 0; + } + + /* ignore it if src or dst are multicast addresses. */ + if ((sp_dst->sa_family == AF_INET + && IN_MULTICAST(ntohl(((struct sockaddr_in *)sp_dst)->sin_addr.s_addr))) +#ifdef INET6 + || (sp_dst->sa_family == AF_INET6 + && IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)sp_dst)->sin6_addr)) +#endif + ) { + plog(LLV_DEBUG, LOCATION, NULL, + "ignore due to multicast destination address: %s.\n", + saddrwop2str(sp_dst)); + return 0; + } + + if ((sp_src->sa_family == AF_INET + && IN_MULTICAST(ntohl(((struct sockaddr_in *)sp_src)->sin_addr.s_addr))) +#ifdef INET6 + || (sp_src->sa_family == AF_INET6 + && IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)sp_src)->sin6_addr)) +#endif + ) { + plog(LLV_DEBUG, LOCATION, NULL, + "ignore due to multicast source address: %s.\n", + saddrwop2str(sp_src)); + return 0; + } + + /* search for proper policyindex */ + sp_out = getspbyspid(xpl->sadb_x_policy_id); + if (sp_out == NULL) { + plog(LLV_ERROR, LOCATION, NULL, "no policy found: id:%d.\n", + xpl->sadb_x_policy_id); + return -1; + } + plog(LLV_DEBUG, LOCATION, NULL, + "suitable outbound SP found: %s.\n", spidx2str(&sp_out->spidx)); + + /* Before going further, let first get the source and destination + * address that would be used for IKE negotiation. The logic is: + * - if SP from SPD image contains local and remote hints, we + * use them (provided by MIGRATE). + * - otherwise, we use the ones from the ipsecrequest, which means: + * - the addresses from the request for transport mode + * - the endpoints addresses for tunnel mode + * + * Note that: + * 1) racoon does not support negotiation of bundles which + * simplifies the lookup for the addresses in the ipsecrequest + * list, as we expect only one. + * 2) We do source and destination parts all together and do not + * accept semi-defined information. This is just a decision, + * there might be needs. + * + * --arno + */ + if (sp_out->req && sp_out->req->saidx.mode == IPSEC_MODE_TUNNEL) { + /* For Tunnel mode, SA addresses are the endpoints */ + src = (struct sockaddr *) &sp_out->req->saidx.src; + dst = (struct sockaddr *) &sp_out->req->saidx.dst; + } else { + /* Otherwise use requested addresses. + * + * We need to explicitly setup sa_src and sa_dst too, + * since the SA ports are different from IKE port. And + * src/dst ports will be overwritten when the matching + * phase1 is found. */ + src = sa_src = sp_src; + dst = sa_dst = sp_dst; + } + if (sp_out->local && sp_out->remote) { + /* hints available, let's use them */ + sa_src = src; + sa_dst = dst; + src = (struct sockaddr *) sp_out->local; + dst = (struct sockaddr *) sp_out->remote; + } + + /* + * If there is a phase 2 handler against the policy identifier in + * the acquire message, and if + * 1. its state is less than PHASE2ST_ESTABLISHED, then racoon + * should ignore such a acquire message because the phase 2 + * is just negotiating. + * 2. its state is equal to PHASE2ST_ESTABLISHED, then racoon + * has to prcesss such a acquire message because racoon may + * lost the expire message. + */ + iph2 = getph2byid(src, dst, xpl->sadb_x_policy_id); + if (iph2 != NULL) { + if (iph2->status < PHASE2ST_ESTABLISHED) { + plog(LLV_DEBUG, LOCATION, NULL, + "ignore the acquire because ph2 found\n"); + return -1; + } + if (iph2->status == PHASE2ST_EXPIRED) + iph2 = NULL; + /*FALLTHROUGH*/ + } + + /* Check we are listening on source address. If not, ignore. */ + if (myaddr_getsport(src) == -1) { + plog(LLV_DEBUG, LOCATION, NULL, + "Not listening on source address %s. Ignoring ACQUIRE.\n", + saddrwop2str(src)); + return 0; + } + + /* get inbound policy */ + { + + memset(&spidx, 0, sizeof(spidx)); + spidx.dir = IPSEC_DIR_INBOUND; + memcpy(&spidx.src, &sp_out->spidx.dst, sizeof(spidx.src)); + memcpy(&spidx.dst, &sp_out->spidx.src, sizeof(spidx.dst)); + spidx.prefs = sp_out->spidx.prefd; + spidx.prefd = sp_out->spidx.prefs; + spidx.ul_proto = sp_out->spidx.ul_proto; + +#ifdef HAVE_SECCTX + if (m_sec_ctx) { + spidx.sec_ctx.ctx_doi = m_sec_ctx->sadb_x_ctx_doi; + spidx.sec_ctx.ctx_alg = m_sec_ctx->sadb_x_ctx_alg; + spidx.sec_ctx.ctx_strlen = m_sec_ctx->sadb_x_ctx_len; + memcpy(spidx.sec_ctx.ctx_str, + ((char *)m_sec_ctx + sizeof(struct sadb_x_sec_ctx)), + spidx.sec_ctx.ctx_strlen); + } +#endif /* HAVE_SECCTX */ + + sp_in = getsp(&spidx); + if (sp_in) { + plog(LLV_DEBUG, LOCATION, NULL, + "suitable inbound SP found: %s.\n", + spidx2str(&sp_in->spidx)); + } else { + plog(LLV_NOTIFY, LOCATION, NULL, + "no in-bound policy found: %s\n", + spidx2str(&spidx)); + } + } + + /* allocate a phase 2 */ + iph2 = newph2(); + if (iph2 == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "failed to allocate phase2 entry.\n"); + return -1; + } + iph2->side = INITIATOR; + iph2->spid = xpl->sadb_x_policy_id; + iph2->satype = msg->sadb_msg_satype; + iph2->seq = msg->sadb_msg_seq; + iph2->status = PHASE2ST_STATUS2; + + /* set address used by IKE for the negotiation (might differ from + * SA address, i.e. might not be tunnel endpoints or addresses + * of transport mode SA) */ + iph2->dst = dupsaddr(dst); + if (iph2->dst == NULL) { + delph2(iph2); + return -1; + } + iph2->src = dupsaddr(src); + if (iph2->src == NULL) { + delph2(iph2); + return -1; + } + + /* If sa_src and sa_dst have been set, this mean we have to + * set iph2->sa_src and iph2->sa_dst to provide the addresses + * of the SA because iph2->src and iph2->dst are only the ones + * used for the IKE exchanges. Those that need these addresses + * are for instance pk_sendupdate() or pk_sendgetspi() */ + if (sa_src) { + iph2->sa_src = dupsaddr(sa_src); + iph2->sa_dst = dupsaddr(sa_dst); + } + + if (isakmp_get_sainfo(iph2, sp_out, sp_in) < 0) { + delph2(iph2); + return -1; + } + +#ifdef HAVE_SECCTX + if (m_sec_ctx) { + set_secctx_in_proposal(iph2, spidx); + } +#endif /* HAVE_SECCTX */ + + insph2(iph2); + + /* start isakmp initiation by using ident exchange */ + /* XXX should be looped if there are multiple phase 2 handler. */ + if (isakmp_post_acquire(iph2, NULL, TRUE) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "failed to begin ipsec sa negotication.\n"); + remph2(iph2); + delph2(iph2); + return -1; + } + + return 0; +} + +static int +pk_recvdelete(mhp) + caddr_t *mhp; +{ + struct sadb_msg *msg; + struct sadb_sa *sa; + struct sockaddr *src, *dst; + struct ph2handle *iph2 = NULL; + u_int proto_id; + + /* ignore this message because of local test mode. */ + if (f_local) + return 0; + + /* sanity check */ + if (mhp[0] == NULL + || mhp[SADB_EXT_SA] == NULL + || mhp[SADB_EXT_ADDRESS_SRC] == NULL + || mhp[SADB_EXT_ADDRESS_DST] == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "inappropriate sadb delete message passed.\n"); + return -1; + } + msg = (struct sadb_msg *)mhp[0]; + sa = (struct sadb_sa *)mhp[SADB_EXT_SA]; + pk_fixup_sa_addresses(mhp); + src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); + dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]); + + /* the message has to be processed or not ? */ + if (msg->sadb_msg_pid == getpid()) { + plog(LLV_DEBUG, LOCATION, NULL, + "%s message is not interesting " + "because the message was originated by me.\n", + s_pfkey_type(msg->sadb_msg_type)); + return -1; + } + + proto_id = pfkey2ipsecdoi_proto(msg->sadb_msg_satype); + if (proto_id == ~0) { + plog(LLV_ERROR, LOCATION, NULL, + "invalid proto_id %d\n", msg->sadb_msg_satype); + return -1; + } + + iph2 = getph2bysaidx(src, dst, proto_id, sa->sadb_sa_spi); + if (iph2 == NULL) { + /* ignore */ + plog(LLV_ERROR, LOCATION, NULL, + "no iph2 found: %s\n", + sadbsecas2str(src, dst, msg->sadb_msg_satype, + sa->sadb_sa_spi, IPSEC_MODE_ANY)); + return 0; + } + + plog(LLV_ERROR, LOCATION, NULL, + "pfkey DELETE received: %s\n", + sadbsecas2str(src, dst, + msg->sadb_msg_satype, sa->sadb_sa_spi, IPSEC_MODE_ANY)); + + /* send delete information */ + if (iph2->status == PHASE2ST_ESTABLISHED) + isakmp_info_send_d2(iph2); + + remph2(iph2); + delph2(iph2); + + return 0; +} + +static int +pk_recvflush(mhp) + caddr_t *mhp; +{ + /* ignore this message because of local test mode. */ + if (f_local) + return 0; + + /* sanity check */ + if (mhp[0] == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "inappropriate sadb flush message passed.\n"); + return -1; + } + + flushph2(); + + return 0; +} + +static int +getsadbpolicy(policy0, policylen0, type, iph2) + caddr_t *policy0; + int *policylen0, type; + struct ph2handle *iph2; +{ + struct policyindex *spidx = (struct policyindex *)iph2->spidx_gen; + struct sockaddr *src = NULL, *dst = NULL; + struct sadb_x_policy *xpl; + struct sadb_x_ipsecrequest *xisr; + struct saproto *pr; + struct saproto **pr_rlist; + int rlist_len = 0; + caddr_t policy, p; + int policylen; + int xisrlen; + u_int satype, mode; + int len = 0; +#ifdef HAVE_SECCTX + int ctxlen = 0; +#endif /* HAVE_SECCTX */ + + + /* get policy buffer size */ + policylen = sizeof(struct sadb_x_policy); + if (type != SADB_X_SPDDELETE) { + if (iph2->sa_src && iph2->sa_dst) { + src = iph2->sa_src; /* MIPv6: Use SA addresses, */ + dst = iph2->sa_dst; /* not IKE ones */ + } else { + src = iph2->src; /* Common case: SA addresses */ + dst = iph2->dst; /* and IKE ones are the same */ + } + + for (pr = iph2->approval->head; pr; pr = pr->next) { + xisrlen = sizeof(*xisr); + if (pr->encmode == IPSECDOI_ATTR_ENC_MODE_TUNNEL) { + xisrlen += (sysdep_sa_len(src) + + sysdep_sa_len(dst)); + } + + policylen += PFKEY_ALIGN8(xisrlen); + } + } + +#ifdef HAVE_SECCTX + if (*spidx->sec_ctx.ctx_str) { + ctxlen = sizeof(struct sadb_x_sec_ctx) + + PFKEY_ALIGN8(spidx->sec_ctx.ctx_strlen); + policylen += ctxlen; + } +#endif /* HAVE_SECCTX */ + + /* make policy structure */ + policy = racoon_malloc(policylen); + memset((void*)policy, 0xcd, policylen); + if (!policy) { + plog(LLV_ERROR, LOCATION, NULL, + "buffer allocation failed.\n"); + return -1; + } + + xpl = (struct sadb_x_policy *)policy; + xpl->sadb_x_policy_len = PFKEY_UNIT64(policylen); + xpl->sadb_x_policy_exttype = SADB_X_EXT_POLICY; + xpl->sadb_x_policy_type = IPSEC_POLICY_IPSEC; + xpl->sadb_x_policy_dir = spidx->dir; + xpl->sadb_x_policy_id = 0; +#ifdef HAVE_PFKEY_POLICY_PRIORITY + xpl->sadb_x_policy_priority = PRIORITY_DEFAULT; +#endif + len++; + +#ifdef HAVE_SECCTX + if (*spidx->sec_ctx.ctx_str) { + struct sadb_x_sec_ctx *p; + + p = (struct sadb_x_sec_ctx *)(xpl + len); + memset(p, 0, ctxlen); + p->sadb_x_sec_len = PFKEY_UNIT64(ctxlen); + p->sadb_x_sec_exttype = SADB_X_EXT_SEC_CTX; + p->sadb_x_ctx_len = spidx->sec_ctx.ctx_strlen; + p->sadb_x_ctx_doi = spidx->sec_ctx.ctx_doi; + p->sadb_x_ctx_alg = spidx->sec_ctx.ctx_alg; + + memcpy(p + 1,spidx->sec_ctx.ctx_str,spidx->sec_ctx.ctx_strlen); + len += ctxlen; + } +#endif /* HAVE_SECCTX */ + + /* no need to append policy information any more if type is SPDDELETE */ + if (type == SADB_X_SPDDELETE) + goto end; + + xisr = (struct sadb_x_ipsecrequest *)(xpl + len); + + /* The order of things is reversed for use in add policy messages */ + for (pr = iph2->approval->head; pr; pr = pr->next) rlist_len++; + pr_rlist = racoon_malloc((rlist_len+1)*sizeof(struct saproto*)); + if (!pr_rlist) { + plog(LLV_ERROR, LOCATION, NULL, + "buffer allocation failed.\n"); + return -1; + } + pr_rlist[rlist_len--] = NULL; + for (pr = iph2->approval->head; pr; pr = pr->next) pr_rlist[rlist_len--] = pr; + rlist_len = 0; + + for (pr = pr_rlist[rlist_len++]; pr; pr = pr_rlist[rlist_len++]) { + + satype = doi2ipproto(pr->proto_id); + if (satype == ~0) { + plog(LLV_ERROR, LOCATION, NULL, + "invalid proto_id %d\n", pr->proto_id); + goto err; + } + mode = ipsecdoi2pfkey_mode(pr->encmode); + if (mode == ~0) { + plog(LLV_ERROR, LOCATION, NULL, + "invalid encmode %d\n", pr->encmode); + goto err; + } + + /* + * the policy level cannot be unique because the policy + * is defined later than SA, so req_id cannot be bound to SA. + */ + xisr->sadb_x_ipsecrequest_proto = satype; + xisr->sadb_x_ipsecrequest_mode = mode; + if(iph2->proposal->head->reqid_in > 0){ + xisr->sadb_x_ipsecrequest_level = IPSEC_LEVEL_UNIQUE; + xisr->sadb_x_ipsecrequest_reqid = iph2->proposal->head->reqid_in; + }else{ + xisr->sadb_x_ipsecrequest_level = IPSEC_LEVEL_REQUIRE; + xisr->sadb_x_ipsecrequest_reqid = 0; + } + p = (caddr_t)(xisr + 1); + + xisrlen = sizeof(*xisr); + + if (pr->encmode == IPSECDOI_ATTR_ENC_MODE_TUNNEL) { + int src_len, dst_len; + + src_len = sysdep_sa_len(src); + dst_len = sysdep_sa_len(dst); + xisrlen += src_len + dst_len; + + memcpy(p, src, src_len); + p += src_len; + + memcpy(p, dst, dst_len); + p += dst_len; + } + + xisr->sadb_x_ipsecrequest_len = PFKEY_ALIGN8(xisrlen); + xisr = (struct sadb_x_ipsecrequest *)p; + + } + racoon_free(pr_rlist); + +end: + *policy0 = policy; + *policylen0 = policylen; + + return 0; + +err: + if (policy) + racoon_free(policy); + if (pr_rlist) racoon_free(pr_rlist); + + return -1; +} + +int +pk_sendspdupdate2(iph2) + struct ph2handle *iph2; +{ + struct policyindex *spidx = (struct policyindex *)iph2->spidx_gen; + caddr_t policy = NULL; + int policylen = 0; + u_int64_t ltime, vtime; + + ltime = iph2->approval->lifetime; + vtime = 0; + + if (getsadbpolicy(&policy, &policylen, SADB_X_SPDUPDATE, iph2)) { + plog(LLV_ERROR, LOCATION, NULL, + "getting sadb policy failed.\n"); + return -1; + } + + if (pfkey_send_spdupdate2( + lcconf->sock_pfkey, + (struct sockaddr *)&spidx->src, + spidx->prefs, + (struct sockaddr *)&spidx->dst, + spidx->prefd, + spidx->ul_proto, + ltime, vtime, + policy, policylen, 0) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "libipsec failed send spdupdate2 (%s)\n", + ipsec_strerror()); + goto end; + } + plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_spdupdate2\n"); + +end: + if (policy) + racoon_free(policy); + + return 0; +} + +static int +pk_recvspdupdate(mhp) + caddr_t *mhp; +{ + struct sadb_address *saddr, *daddr; + struct sadb_x_policy *xpl; + struct sadb_lifetime *lt; + struct policyindex spidx; + struct secpolicy *sp; + struct sockaddr *local=NULL, *remote=NULL; + u_int64_t created; + int ret; + + /* sanity check */ + if (mhp[0] == NULL + || mhp[SADB_EXT_ADDRESS_SRC] == NULL + || mhp[SADB_EXT_ADDRESS_DST] == NULL + || mhp[SADB_X_EXT_POLICY] == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "inappropriate sadb spdupdate message passed.\n"); + return -1; + } + saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC]; + daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST]; + xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY]; + lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD]; + if(lt != NULL) + created = lt->sadb_lifetime_addtime; + else + created = 0; + +#ifdef HAVE_PFKEY_POLICY_PRIORITY + KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, + saddr + 1, + daddr + 1, + saddr->sadb_address_prefixlen, + daddr->sadb_address_prefixlen, + saddr->sadb_address_proto, + xpl->sadb_x_policy_priority, + created, + &spidx); +#else + KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, + saddr + 1, + daddr + 1, + saddr->sadb_address_prefixlen, + daddr->sadb_address_prefixlen, + saddr->sadb_address_proto, + created, + &spidx); +#endif + +#ifdef HAVE_SECCTX + if (mhp[SADB_X_EXT_SEC_CTX] != NULL) { + struct sadb_x_sec_ctx *ctx; + + ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX]; + spidx.sec_ctx.ctx_alg = ctx->sadb_x_ctx_alg; + spidx.sec_ctx.ctx_doi = ctx->sadb_x_ctx_doi; + spidx.sec_ctx.ctx_strlen = ctx->sadb_x_ctx_len; + memcpy(spidx.sec_ctx.ctx_str, ctx + 1, ctx->sadb_x_ctx_len); + } +#endif /* HAVE_SECCTX */ + + sp = getsp(&spidx); + if (sp == NULL) { + plog(LLV_DEBUG, LOCATION, NULL, + "this policy did not exist for removal: \"%s\"\n", + spidx2str(&spidx)); + } else { + /* preserve hints before deleting the SP */ + local = sp->local; + remote = sp->remote; + sp->local = NULL; + sp->remote = NULL; + + remsp(sp); + delsp(sp); + } + + /* Add new SP (with old hints) */ + ret = addnewsp(mhp, local, remote); + + if (local != NULL) + racoon_free(local); + if (remote != NULL) + racoon_free(remote); + + if (ret < 0) + return -1; + + return 0; +} + +/* + * this function has to be used by responder side. + */ +int +pk_sendspdadd2(iph2) + struct ph2handle *iph2; +{ + struct policyindex *spidx = (struct policyindex *)iph2->spidx_gen; + caddr_t policy = NULL; + int policylen = 0; + u_int64_t ltime, vtime; + + ltime = iph2->approval->lifetime; + vtime = 0; + + if (getsadbpolicy(&policy, &policylen, SADB_X_SPDADD, iph2)) { + plog(LLV_ERROR, LOCATION, NULL, + "getting sadb policy failed.\n"); + return -1; + } + + if (pfkey_send_spdadd2( + lcconf->sock_pfkey, + (struct sockaddr *)&spidx->src, + spidx->prefs, + (struct sockaddr *)&spidx->dst, + spidx->prefd, + spidx->ul_proto, + ltime, vtime, + policy, policylen, 0) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "libipsec failed send spdadd2 (%s)\n", + ipsec_strerror()); + goto end; + } + plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_spdadd2\n"); + +end: + if (policy) + racoon_free(policy); + + return 0; +} + +static int +pk_recvspdadd(mhp) + caddr_t *mhp; +{ + struct sadb_address *saddr, *daddr; + struct sadb_x_policy *xpl; + struct sadb_lifetime *lt; + struct policyindex spidx; + struct secpolicy *sp; + struct sockaddr *local = NULL, *remote = NULL; + u_int64_t created; + int ret; + + /* sanity check */ + if (mhp[0] == NULL + || mhp[SADB_EXT_ADDRESS_SRC] == NULL + || mhp[SADB_EXT_ADDRESS_DST] == NULL + || mhp[SADB_X_EXT_POLICY] == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "inappropriate sadb spdadd message passed.\n"); + return -1; + } + saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC]; + daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST]; + xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY]; + lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD]; + if(lt != NULL) + created = lt->sadb_lifetime_addtime; + else + created = 0; + +#ifdef HAVE_PFKEY_POLICY_PRIORITY + KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, + saddr + 1, + daddr + 1, + saddr->sadb_address_prefixlen, + daddr->sadb_address_prefixlen, + saddr->sadb_address_proto, + xpl->sadb_x_policy_priority, + created, + &spidx); +#else + KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, + saddr + 1, + daddr + 1, + saddr->sadb_address_prefixlen, + daddr->sadb_address_prefixlen, + saddr->sadb_address_proto, + created, + &spidx); +#endif + +#ifdef HAVE_SECCTX + if (mhp[SADB_X_EXT_SEC_CTX] != NULL) { + struct sadb_x_sec_ctx *ctx; + + ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX]; + spidx.sec_ctx.ctx_alg = ctx->sadb_x_ctx_alg; + spidx.sec_ctx.ctx_doi = ctx->sadb_x_ctx_doi; + spidx.sec_ctx.ctx_strlen = ctx->sadb_x_ctx_len; + memcpy(spidx.sec_ctx.ctx_str, ctx + 1, ctx->sadb_x_ctx_len); + } +#endif /* HAVE_SECCTX */ + + sp = getsp(&spidx); + if (sp != NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "such policy already exists. " + "anyway replace it: %s\n", + spidx2str(&spidx)); + + /* preserve hints before deleting the SP */ + local = sp->local; + remote = sp->remote; + sp->local = NULL; + sp->remote = NULL; + + remsp(sp); + delsp(sp); + } + + /* Add new SP (with old hints) */ + ret = addnewsp(mhp, local, remote); + + if (local != NULL) + racoon_free(local); + if (remote != NULL) + racoon_free(remote); + + if (ret < 0) + return -1; + + return 0; +} + +/* + * this function has to be used by responder side. + */ +int +pk_sendspddelete(iph2) + struct ph2handle *iph2; +{ + struct policyindex *spidx = (struct policyindex *)iph2->spidx_gen; + caddr_t policy = NULL; + int policylen; + + if (getsadbpolicy(&policy, &policylen, SADB_X_SPDDELETE, iph2)) { + plog(LLV_ERROR, LOCATION, NULL, + "getting sadb policy failed.\n"); + return -1; + } + + if (pfkey_send_spddelete( + lcconf->sock_pfkey, + (struct sockaddr *)&spidx->src, + spidx->prefs, + (struct sockaddr *)&spidx->dst, + spidx->prefd, + spidx->ul_proto, + policy, policylen, 0) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "libipsec failed send spddelete (%s)\n", + ipsec_strerror()); + goto end; + } + plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_spddelete\n"); + +end: + if (policy) + racoon_free(policy); + + return 0; +} + +static int +pk_recvspddelete(mhp) + caddr_t *mhp; +{ + struct sadb_address *saddr, *daddr; + struct sadb_x_policy *xpl; + struct sadb_lifetime *lt; + struct policyindex spidx; + struct secpolicy *sp; + u_int64_t created; + + /* sanity check */ + if (mhp[0] == NULL + || mhp[SADB_EXT_ADDRESS_SRC] == NULL + || mhp[SADB_EXT_ADDRESS_DST] == NULL + || mhp[SADB_X_EXT_POLICY] == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "inappropriate sadb spddelete message passed.\n"); + return -1; + } + saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC]; + daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST]; + xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY]; + lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD]; + if(lt != NULL) + created = lt->sadb_lifetime_addtime; + else + created = 0; + +#ifdef HAVE_PFKEY_POLICY_PRIORITY + KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, + saddr + 1, + daddr + 1, + saddr->sadb_address_prefixlen, + daddr->sadb_address_prefixlen, + saddr->sadb_address_proto, + xpl->sadb_x_policy_priority, + created, + &spidx); +#else + KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, + saddr + 1, + daddr + 1, + saddr->sadb_address_prefixlen, + daddr->sadb_address_prefixlen, + saddr->sadb_address_proto, + created, + &spidx); +#endif + +#ifdef HAVE_SECCTX + if (mhp[SADB_X_EXT_SEC_CTX] != NULL) { + struct sadb_x_sec_ctx *ctx; + + ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX]; + spidx.sec_ctx.ctx_alg = ctx->sadb_x_ctx_alg; + spidx.sec_ctx.ctx_doi = ctx->sadb_x_ctx_doi; + spidx.sec_ctx.ctx_strlen = ctx->sadb_x_ctx_len; + memcpy(spidx.sec_ctx.ctx_str, ctx + 1, ctx->sadb_x_ctx_len); + } +#endif /* HAVE_SECCTX */ + + sp = getsp(&spidx); + if (sp == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "no policy found: %s\n", + spidx2str(&spidx)); + return -1; + } + + remsp(sp); + delsp(sp); + + return 0; +} + +static int +pk_recvspdexpire(mhp) + caddr_t *mhp; +{ + struct sadb_address *saddr, *daddr; + struct sadb_x_policy *xpl; + struct sadb_lifetime *lt; + struct policyindex spidx; + struct secpolicy *sp; + u_int64_t created; + + /* sanity check */ + if (mhp[0] == NULL + || mhp[SADB_EXT_ADDRESS_SRC] == NULL + || mhp[SADB_EXT_ADDRESS_DST] == NULL + || mhp[SADB_X_EXT_POLICY] == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "inappropriate sadb spdexpire message passed.\n"); + return -1; + } + saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC]; + daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST]; + xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY]; + lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD]; + if(lt != NULL) + created = lt->sadb_lifetime_addtime; + else + created = 0; + +#ifdef HAVE_PFKEY_POLICY_PRIORITY + KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, + saddr + 1, + daddr + 1, + saddr->sadb_address_prefixlen, + daddr->sadb_address_prefixlen, + saddr->sadb_address_proto, + xpl->sadb_x_policy_priority, + created, + &spidx); +#else + KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, + saddr + 1, + daddr + 1, + saddr->sadb_address_prefixlen, + daddr->sadb_address_prefixlen, + saddr->sadb_address_proto, + created, + &spidx); +#endif + +#ifdef HAVE_SECCTX + if (mhp[SADB_X_EXT_SEC_CTX] != NULL) { + struct sadb_x_sec_ctx *ctx; + + ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX]; + spidx.sec_ctx.ctx_alg = ctx->sadb_x_ctx_alg; + spidx.sec_ctx.ctx_doi = ctx->sadb_x_ctx_doi; + spidx.sec_ctx.ctx_strlen = ctx->sadb_x_ctx_len; + memcpy(spidx.sec_ctx.ctx_str, ctx + 1, ctx->sadb_x_ctx_len); + } +#endif /* HAVE_SECCTX */ + + sp = getsp(&spidx); + if (sp == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "no policy found: %s\n", + spidx2str(&spidx)); + return -1; + } + + remsp(sp); + delsp(sp); + + return 0; +} + +static int +pk_recvspdget(mhp) + caddr_t *mhp; +{ + /* sanity check */ + if (mhp[0] == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "inappropriate sadb spdget message passed.\n"); + return -1; + } + + return 0; +} + +static int +pk_recvspddump(mhp) + caddr_t *mhp; +{ + struct sadb_msg *msg; + struct sadb_address *saddr, *daddr; + struct sadb_x_policy *xpl; + struct sadb_lifetime *lt; + struct policyindex spidx; + struct secpolicy *sp; + struct sockaddr *local=NULL, *remote=NULL; + u_int64_t created; + int ret; + + /* sanity check */ + if (mhp[0] == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "inappropriate sadb spddump message passed.\n"); + return -1; + } + msg = (struct sadb_msg *)mhp[0]; + saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC]; + daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST]; + xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY]; + lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD]; + if(lt != NULL) + created = lt->sadb_lifetime_addtime; + else + created = 0; + + if (saddr == NULL || daddr == NULL || xpl == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "inappropriate sadb spddump message passed.\n"); + return -1; + } + +#ifdef HAVE_PFKEY_POLICY_PRIORITY + KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, + saddr + 1, + daddr + 1, + saddr->sadb_address_prefixlen, + daddr->sadb_address_prefixlen, + saddr->sadb_address_proto, + xpl->sadb_x_policy_priority, + created, + &spidx); +#else + KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, + saddr + 1, + daddr + 1, + saddr->sadb_address_prefixlen, + daddr->sadb_address_prefixlen, + saddr->sadb_address_proto, + created, + &spidx); +#endif + +#ifdef HAVE_SECCTX + if (mhp[SADB_X_EXT_SEC_CTX] != NULL) { + struct sadb_x_sec_ctx *ctx; + + ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX]; + spidx.sec_ctx.ctx_alg = ctx->sadb_x_ctx_alg; + spidx.sec_ctx.ctx_doi = ctx->sadb_x_ctx_doi; + spidx.sec_ctx.ctx_strlen = ctx->sadb_x_ctx_len; + memcpy(spidx.sec_ctx.ctx_str, ctx + 1, ctx->sadb_x_ctx_len); + } +#endif /* HAVE_SECCTX */ + + sp = getsp(&spidx); + if (sp != NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "such policy already exists. " + "anyway replace it: %s\n", + spidx2str(&spidx)); + + /* preserve hints before deleting the SP */ + local = sp->local; + remote = sp->remote; + sp->local = NULL; + sp->remote = NULL; + + remsp(sp); + delsp(sp); + } + + /* Add new SP (with old hints) */ + ret = addnewsp(mhp, local, remote); + + if (local != NULL) + racoon_free(local); + if (remote != NULL) + racoon_free(remote); + + if (ret < 0) + return -1; + + return 0; +} + +static int +pk_recvspdflush(mhp) + caddr_t *mhp; +{ + /* sanity check */ + if (mhp[0] == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "inappropriate sadb spdflush message passed.\n"); + return -1; + } + + flushsp(); + + return 0; +} + +#if defined(SADB_X_MIGRATE) && defined(SADB_X_EXT_KMADDRESS) + +/* MIGRATE support (pk_recvmigrate() is the handler of MIGRATE message). + * + * pk_recvmigrate() + * 1) some preprocessing and checks + * 2) parsing of sadb_x_kmaddress extension + * 3) SP lookup using selectors and content of policy extension from MIGRATE + * 4) resolution of current local and remote IKE addresses + * 5) Use of addresses to get Phase 1 handler if any + * 6) Update of IKE addresses in Phase 1 (iph1->local and iph1->remote) + * 7) Update of IKE addresses in Phase 2 (iph2->src and iph2->dst) + * 8) Update of IKE addresses in SP (sp->local and sp->remote) + * 9) Loop on sadb_x_ipsecrequests pairs from MIGRATE + * - update of associated ipsecrequests entries in sp->req (should be + * only one as racoon does not support bundles), i.e. update of + * tunnel endpoints when required. + * - If tunnel mode endpoints have been updated, lookup of associated + * Phase 2 handle to also update sa_src and sa_dst entries + * + * XXX Note that we do not support yet the update of SA addresses for transport + * mode, but only the update of SA addresses for tunnel mode (endpoints). + * Reasons are: + * - there is no initial need for MIPv6 + * - racoon does not support bundles + * - this would imply more work to deal with sainfo update (if feasible). + */ + +/* Generic argument structure for migration callbacks */ +struct migrate_args { + struct sockaddr *local; + struct sockaddr *remote; +}; + +/* + * Update local and remote addresses of given Phase 1. Schedule removal + * if negotiation was going on and restart a one from updated address. + * + * -1 is returned on error. 0 if everything went right. + */ +static int +migrate_ph1_ike_addresses(iph1, arg) + struct ph1handle *iph1; + void *arg; +{ + struct migrate_args *ma = (struct migrate_args *) arg; + struct remoteconf *rmconf; + u_int16_t port; + + /* Already up-to-date? */ + if (cmpsaddr(iph1->local, ma->local) == CMPSADDR_MATCH && + cmpsaddr(iph1->remote, ma->remote) == CMPSADDR_MATCH) + return 0; + + if (iph1->status < PHASE1ST_ESTABLISHED) { + /* Bad luck! We received a MIGRATE *while* negotiating + * Phase 1 (i.e. it was not established yet). If we act as + * initiator we need to restart the negotiation. As + * responder, our best bet is to update our addresses + * and wait for the initiator to do something */ + plog(LLV_WARNING, LOCATION, NULL, "MIGRATE received *during* " + "Phase 1 negotiation (%s).\n", + saddr2str_fromto("%s => %s", ma->local, ma->remote)); + + /* If we are not acting as initiator, let's just leave and + * let the remote peer handle the restart */ + rmconf = getrmconf(ma->remote, 0); + if (rmconf == NULL || !rmconf->passive) { + iph1->status = PHASE1ST_EXPIRED; + isakmp_ph1delete(iph1); + + /* This is unlikely, but let's just check if a Phase 1 + * for the new addresses already exist */ + if (getph1byaddr(ma->local, ma->remote, 0)) { + plog(LLV_WARNING, LOCATION, NULL, "No need " + "to start a new Phase 1 negotiation. One " + "already exists.\n"); + return 0; + } + + plog(LLV_WARNING, LOCATION, NULL, "As initiator, " + "restarting it.\n"); + /* Note that the insertion of the new Phase 1 will not + * interfere with the fact we are called from enumph1, + * because it is inserted as first element. --arno */ + isakmp_ph1begin_i(rmconf, ma->local, ma->remote); + + return 0; + } + } + + if (iph1->local != NULL) { + plog(LLV_DEBUG, LOCATION, NULL, "Migrating Phase 1 local " + "address from %s\n", + saddr2str_fromto("%s to %s", iph1->local, ma->local)); + port = extract_port(iph1->local); + racoon_free(iph1->local); + } else + port = 0; + + iph1->local = dupsaddr(ma->local); + if (iph1->local == NULL) { + plog(LLV_ERROR, LOCATION, NULL, "unable to allocate " + "Phase 1 local address.\n"); + return -1; + } + set_port(iph1->local, port); + + if (iph1->remote != NULL) { + plog(LLV_DEBUG, LOCATION, NULL, "Migrating Phase 1 remote " + "address from %s\n", + saddr2str_fromto("%s to %s", iph1->remote, ma->remote)); + port = extract_port(iph1->remote); + racoon_free(iph1->remote); + } else + port = 0; + + iph1->remote = dupsaddr(ma->remote); + if (iph1->remote == NULL) { + plog(LLV_ERROR, LOCATION, NULL, "unable to allocate " + "Phase 1 remote address.\n"); + return -1; + } + set_port(iph1->remote, port); + + return 0; +} + +/* Update src and dst of all current Phase 2 handles. + * with provided local and remote addresses. + * Our intent is NOT to modify IPsec SA endpoints but IKE + * addresses so we need to take care to separate those if + * they are different. -1 is returned on error. 0 if everything + * went right. + * + * Note: we do not maintain port information as it is not + * expected to be meaningful --arno + */ +static int +migrate_ph2_ike_addresses(iph2, arg) + struct ph2handle *iph2; + void *arg; +{ + struct migrate_args *ma = (struct migrate_args *) arg; + struct ph1handle *iph1; + + /* If Phase 2 has an associated Phase 1, migrate addresses */ + if (iph2->ph1) + migrate_ph1_ike_addresses(iph2->ph1, arg); + + /* Already up-to-date? */ + if (cmpsaddr(iph2->src, ma->local) == CMPSADDR_MATCH && + cmpsaddr(iph2->dst, ma->remote) == CMPSADDR_MATCH) + return 0; + + /* save src/dst as sa_src/sa_dst before rewriting */ + if (iph2->sa_src == NULL && iph2->sa_dst == NULL) { + iph2->sa_src = iph2->src; + iph2->sa_dst = iph2->dst; + iph2->src = NULL; + iph2->dst = NULL; + } + + if (iph2->src != NULL) + racoon_free(iph2->src); + iph2->src = dupsaddr(ma->local); + if (iph2->src == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "unable to allocate Phase 2 src address.\n"); + return -1; + } + + if (iph2->dst != NULL) + racoon_free(iph2->dst); + iph2->dst = dupsaddr(ma->remote); + if (iph2->dst == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "unable to allocate Phase 2 dst address.\n"); + return -1; + } + + return 0; +} + +/* Consider existing Phase 2 handles with given spid and update their source + * and destination addresses for SA. As racoon does not support bundles, if + * we modify multiple occurrences, this probably imply rekeying has happened. + * + * Both addresses passed to the function are expected not to be NULL and of + * same family. -1 is returned on error. 0 if everything went right. + * + * Specific care is needed to support Phase 2 for which negotiation has + * already started but are which not yet established. + */ +static int +migrate_ph2_sa_addresses(iph2, args) + struct ph2handle *iph2; + void *args; +{ + struct migrate_args *ma = (struct migrate_args *) args; + + if (iph2->sa_src != NULL) { + racoon_free(iph2->sa_src); + iph2->sa_src = NULL; + } + + if (iph2->sa_dst != NULL) { + racoon_free(iph2->sa_dst); + iph2->sa_dst = NULL; + } + + iph2->sa_src = dupsaddr(ma->local); + if (iph2->sa_src == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "unable to allocate Phase 2 sa_src address.\n"); + return -1; + } + + iph2->sa_dst = dupsaddr(ma->remote); + if (iph2->sa_dst == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "unable to allocate Phase 2 sa_dst address.\n"); + return -1; + } + + if (iph2->status < PHASE2ST_ESTABLISHED) { + struct remoteconf *rmconf; + /* We were negotiating for that SA when we received the MIGRATE. + * We cannot simply update the addresses and let the exchange + * go on. We have to restart the whole negotiation if we are + * the initiator. Otherwise (acting as responder), we just need + * to delete our ph2handle and wait for the initiator to start + * a new negotiation. */ + + if (iph2->ph1 && iph2->ph1->rmconf) + rmconf = iph2->ph1->rmconf; + else + rmconf = getrmconf(iph2->dst, 0); + + if (rmconf && !rmconf->passive) { + struct ph1handle *iph1hint; + + plog(LLV_WARNING, LOCATION, iph2->dst, "MIGRATE received " + "*during* IPsec SA negotiation. As initiator, " + "restarting it.\n"); + + /* Turn off expiration timer ...*/ + sched_cancel(&iph2->sce); + iph2->status = PHASE2ST_EXPIRED; + + /* ... clean Phase 2 handle ... */ + iph1hint = iph2->ph1; + initph2(iph2); + iph2->status = PHASE2ST_STATUS2; + + /* and start a new negotiation */ + if (isakmp_post_acquire(iph2, iph1hint, FALSE) < 0) { + plog(LLV_ERROR, LOCATION, iph2->dst, "failed " + "to begin IPsec SA renegotiation after " + "MIGRATE reception.\n"); + remph2(iph2); + delph2(iph2); + return -1; + } + } else { + plog(LLV_WARNING, LOCATION, iph2->dst, "MIGRATE received " + "*during* IPsec SA negotiation. As responder, let's" + "wait for the initiator to act.\n"); + + /* Simply schedule deletion */ + isakmp_ph2expire(iph2); + } + } + + return 0; +} + +/* Update SP hints (local and remote addresses) for future IKE + * negotiations of SA associated with that SP. -1 is returned + * on error. 0 if everything went right. + * + * Note: we do not maintain port information as it is not + * expected to be meaningful --arno + */ +static int +migrate_sp_ike_addresses(sp, local, remote) + struct secpolicy *sp; + struct sockaddr *local, *remote; +{ + if (sp == NULL || local == NULL || remote == NULL) + return -1; + + if (sp->local != NULL) + racoon_free(sp->local); + + sp->local = dupsaddr(local); + if (sp->local == NULL) { + plog(LLV_ERROR, LOCATION, NULL, "unable to allocate " + "local hint for SP.\n"); + return -1; + } + + if (sp->remote != NULL) + racoon_free(sp->remote); + + sp->remote = dupsaddr(remote); + if (sp->remote == NULL) { + plog(LLV_ERROR, LOCATION, NULL, "unable to allocate " + "remote hint for SP.\n"); + return -1; + } + + return 0; +} + +/* Given current ipsecrequest (isr_cur) to be migrated in considered + tree, the function first checks that it matches the expected one + (xisr_old) provided in MIGRATE message and then updates the addresses + if it is tunnel mode (with content of xisr_new). Various other checks + are performed. For transport mode, structures are not modified, only + the checks are done. -1 is returned on error. */ +static int +migrate_ph2_one_isr(spid, isr_cur, xisr_old, xisr_new) + u_int32_t spid; + struct ipsecrequest *isr_cur; + struct sadb_x_ipsecrequest *xisr_old, *xisr_new; +{ + struct secasindex *saidx = &isr_cur->saidx; + struct sockaddr *osaddr, *odaddr, *nsaddr, *ndaddr; + struct ph2selector ph2sel; + struct migrate_args ma; + + /* First, check that mode and proto do match */ + if (xisr_old->sadb_x_ipsecrequest_proto != saidx->proto || + xisr_old->sadb_x_ipsecrequest_mode != saidx->mode || + xisr_new->sadb_x_ipsecrequest_proto != saidx->proto || + xisr_new->sadb_x_ipsecrequest_mode != saidx->mode) + return -1; + + /* Then, verify reqid if necessary */ + if (isr_cur->saidx.reqid && + (xisr_old->sadb_x_ipsecrequest_reqid != IPSEC_LEVEL_UNIQUE || + xisr_new->sadb_x_ipsecrequest_reqid != IPSEC_LEVEL_UNIQUE || + isr_cur->saidx.reqid != xisr_old->sadb_x_ipsecrequest_reqid || + isr_cur->saidx.reqid != xisr_new->sadb_x_ipsecrequest_reqid)) + return -1; + + /* If not tunnel mode, our work is over */ + if (saidx->mode != IPSEC_MODE_TUNNEL) { + plog(LLV_DEBUG, LOCATION, NULL, "SADB_X_MIGRATE: " + "non tunnel mode isr, skipping SA address migration.\n"); + return 0; + } + + /* Tunnel mode: let's check addresses do match and then update them. */ + osaddr = (struct sockaddr *)(xisr_old + 1); + odaddr = (struct sockaddr *)(((u_int8_t *)osaddr) + sysdep_sa_len(osaddr)); + nsaddr = (struct sockaddr *)(xisr_new + 1); + ndaddr = (struct sockaddr *)(((u_int8_t *)nsaddr) + sysdep_sa_len(nsaddr)); + + /* Check family does match */ + if (osaddr->sa_family != odaddr->sa_family || + nsaddr->sa_family != ndaddr->sa_family) + return -1; + + /* Check family does match */ + if (saidx->src.ss_family != osaddr->sa_family) + return -1; + + /* We log IPv4 to IPv6 and IPv6 to IPv4 switches */ + if (nsaddr->sa_family != osaddr->sa_family) + plog(LLV_INFO, LOCATION, NULL, "SADB_X_MIGRATE: " + "changing address families (%d to %d) for endpoints.\n", + osaddr->sa_family, nsaddr->sa_family); + + if (cmpsaddr(osaddr, (struct sockaddr *) &saidx->src) != CMPSADDR_MATCH || + cmpsaddr(odaddr, (struct sockaddr *) &saidx->dst) != CMPSADDR_MATCH) { + plog(LLV_DEBUG, LOCATION, NULL, "SADB_X_MIGRATE: " + "mismatch of addresses in saidx and xisr.\n"); + return -1; + } + + /* Excellent. Let's grab associated Phase 2 handle (if any) + * and update its sa_src and sa_dst entries. Note that we + * make the assumption that racoon does not support bundles + * and make the lookup using spid: we blindly update + * sa_src and sa_dst for _all_ found Phase 2 handles */ + memset(&ph2sel, 0, sizeof(ph2sel)); + ph2sel.spid = spid; + + memset(&ma, 0, sizeof(ma)); + ma.local = nsaddr; + ma.remote = ndaddr; + + if (enumph2(&ph2sel, migrate_ph2_sa_addresses, &ma) < 0) + return -1; + + /* Now we can do the update of endpoints in secasindex */ + memcpy(&saidx->src, nsaddr, sysdep_sa_len(nsaddr)); + memcpy(&saidx->dst, ndaddr, sysdep_sa_len(ndaddr)); + + return 0; +} + +/* Process the raw (unparsed yet) list of sadb_x_ipsecrequests of MIGRATE + * message. For each sadb_x_ipsecrequest pair (old followed by new), + * the corresponding ipsecrequest entry in the SP is updated. Associated + * existing Phase 2 handle is also updated (if any) */ +static int +migrate_sp_isr_list(sp, xisr_list, xisr_list_len) + struct secpolicy *sp; + struct sadb_x_ipsecrequest *xisr_list; + int xisr_list_len; +{ + struct sadb_x_ipsecrequest *xisr_new, *xisr_old = xisr_list; + int xisr_old_len, xisr_new_len; + struct ipsecrequest *isr_cur; + + isr_cur = sp->req; /* ipsecrequest list from from sp */ + + while (xisr_list_len > 0 && isr_cur != NULL) { + /* Get old xisr (length field is in bytes) */ + xisr_old_len = xisr_old->sadb_x_ipsecrequest_len; + if (xisr_old_len < sizeof(*xisr_old) || + xisr_old_len + sizeof(*xisr_new) > xisr_list_len) { + plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: " + "invalid ipsecrequest length. Exiting.\n"); + return -1; + } + + /* Get new xisr with updated info */ + xisr_new = (struct sadb_x_ipsecrequest *)(((u_int8_t *)xisr_old) + xisr_old_len); + xisr_new_len = xisr_new->sadb_x_ipsecrequest_len; + if (xisr_new_len < sizeof(*xisr_new) || + xisr_new_len + xisr_old_len > xisr_list_len) { + plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: " + "invalid ipsecrequest length. Exiting.\n"); + return -1; + } + + /* Start by migrating current ipsecrequest from SP */ + if (migrate_ph2_one_isr(sp->id, isr_cur, xisr_old, xisr_new) == -1) { + plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: " + "Unable to match and migrate isr. Exiting.\n"); + return -1; + } + + /* Update pointers for next round */ + xisr_list_len -= xisr_old_len + xisr_new_len; + xisr_old = (struct sadb_x_ipsecrequest *)(((u_int8_t *)xisr_new) + + xisr_new_len); + + isr_cur = isr_cur->next; /* Get next ipsecrequest from SP */ + } + + /* Check we had the same amount of pairs in the MIGRATE + as the number of ipsecrequests in the SP */ + if ((xisr_list_len != 0) || isr_cur != NULL) { + plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: " + "number of ipsecrequest does not match the one in SP.\n"); + return -1; + } + + return 0; +} + +/* Parse sadb_x_kmaddress extension and make local and remote + * parameters point to the new addresses (zero copy). -1 is + * returned on error, meaning that addresses are not usable */ +static int +parse_kmaddress(kmaddr, local, remote) + struct sadb_x_kmaddress *kmaddr; + struct sockaddr **local, **remote; +{ + int addrslen, local_len=0; + struct ph1handle *iph1; + + if (kmaddr == NULL) + return -1; + + /* Grab addresses in sadb_x_kmaddress extension */ + addrslen = PFKEY_EXTLEN(kmaddr) - sizeof(*kmaddr); + if (addrslen < sizeof(struct sockaddr)) + return -1; + + *local = (struct sockaddr *)(kmaddr + 1); + + switch ((*local)->sa_family) { + case AF_INET: + local_len = sizeof(struct sockaddr_in); + break; +#ifdef INET6 + case AF_INET6: + local_len = sizeof(struct sockaddr_in6); + break; +#endif + default: + return -1; + } + + if (addrslen != PFKEY_ALIGN8(2*local_len)) + return -1; + + *remote = (struct sockaddr *)(((u_int8_t *)(*local)) + local_len); + + if ((*local)->sa_family != (*remote)->sa_family) + return -1; + + return 0; +} + +/* Handler of PF_KEY MIGRATE message. Helpers are above */ +static int +pk_recvmigrate(mhp) + caddr_t *mhp; +{ + struct sadb_address *saddr, *daddr; + struct sockaddr *old_saddr, *new_saddr; + struct sockaddr *old_daddr, *new_daddr; + struct sockaddr *old_local, *old_remote; + struct sockaddr *local, *remote; + struct sadb_x_kmaddress *kmaddr; + struct sadb_x_policy *xpl; + struct sadb_x_ipsecrequest *xisr_list; + struct sadb_lifetime *lt; + struct policyindex spidx; + struct secpolicy *sp; + struct ipsecrequest *isr_cur; + struct secasindex *oldsaidx; + struct ph2handle *iph2; + struct ph1handle *iph1; + struct ph2selector ph2sel; + struct ph1selector ph1sel; + u_int32_t spid; + u_int64_t created; + int xisr_list_len; + int ulproto; + struct migrate_args ma; + + /* Some sanity checks */ + + if (mhp[0] == NULL + || mhp[SADB_EXT_ADDRESS_SRC] == NULL + || mhp[SADB_EXT_ADDRESS_DST] == NULL + || mhp[SADB_X_EXT_KMADDRESS] == NULL + || mhp[SADB_X_EXT_POLICY] == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "SADB_X_MIGRATE: invalid MIGRATE message received.\n"); + return -1; + } + kmaddr = (struct sadb_x_kmaddress *)mhp[SADB_X_EXT_KMADDRESS]; + saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC]; + daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST]; + xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY]; + lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD]; + if (lt != NULL) + created = lt->sadb_lifetime_addtime; + else + created = 0; + + if (xpl->sadb_x_policy_type != IPSEC_POLICY_IPSEC) { + plog(LLV_WARNING, LOCATION, NULL,"SADB_X_MIGRATE: " + "found non IPsec policy in MIGRATE message. Exiting.\n"); + return -1; + } + + if (PFKEY_EXTLEN(xpl) < sizeof(*xpl)) { + plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: " + "invalid size for sadb_x_policy. Exiting.\n"); + return -1; + } + + /* Some logging to help debbugging */ + if (xpl->sadb_x_policy_dir == IPSEC_DIR_OUTBOUND) + plog(LLV_DEBUG, LOCATION, NULL, + "SADB_X_MIGRATE: Outbound SA being migrated.\n"); + else + plog(LLV_DEBUG, LOCATION, NULL, + "SADB_X_MIGRATE: Inbound SA being migrated.\n"); + + /* validity check */ + xisr_list = (struct sadb_x_ipsecrequest *)(xpl + 1); + xisr_list_len = PFKEY_EXTLEN(xpl) - sizeof(*xpl); + if (xisr_list_len < sizeof(*xisr_list)) { + plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: " + "invalid sadb_x_policy message length. Exiting.\n"); + return -1; + } + + if (parse_kmaddress(kmaddr, &local, &remote) == -1) { + plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: " + "invalid sadb_x_kmaddress extension. Exiting.\n"); + return -1; + } + + /* 0 means ANY */ + if (saddr->sadb_address_proto == 0) + ulproto = IPSEC_ULPROTO_ANY; + else + ulproto = saddr->sadb_address_proto; + +#ifdef HAVE_PFKEY_POLICY_PRIORITY + KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, + saddr + 1, + daddr + 1, + saddr->sadb_address_prefixlen, + daddr->sadb_address_prefixlen, + ulproto, + xpl->sadb_x_policy_priority, + created, + &spidx); +#else + KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, + saddr + 1, + daddr + 1, + saddr->sadb_address_prefixlen, + daddr->sadb_address_prefixlen, + ulproto, + created, + &spidx); +#endif + + /* Everything seems ok, let's get the SP. + * + * XXX We could also do the lookup using the spid from xpl. + * I don't know which one is better. --arno */ + sp = getsp(&spidx); + if (sp == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "SADB_X_MIGRATE: Passed policy does not exist: %s\n", + spidx2str(&spidx)); + return -1; + } + + /* Get the best source and destination addresses used for IKE + * negotiation, to find and migrate existing Phase 1 */ + if (sp->local && sp->remote) { + /* hints available, let's use them */ + old_local = (struct sockaddr *)sp->local; + old_remote = (struct sockaddr *)sp->remote; + } else if (sp->req && sp->req->saidx.mode == IPSEC_MODE_TUNNEL) { + /* Tunnel mode and no hint, use endpoints */ + old_local = (struct sockaddr *)&sp->req->saidx.src; + old_remote = (struct sockaddr *)&sp->req->saidx.dst; + } else { + /* default, use selectors as fallback */ + old_local = (struct sockaddr *)&sp->spidx.src; + old_remote = (struct sockaddr *)&sp->spidx.dst; + } + + /* We migrate all Phase 1 that match our old local and remote + * addresses (no matter their state). + * + * XXX In fact, we should probably havea special treatment for + * Phase 1 that are being established when we receive a MIGRATE. + * This can happen if a movement occurs during the initial IKE + * negotiation. In that case, I wonder if should restart the + * negotiation from the new address or just update things like + * we do it now. + * + * XXX while looking at getph1byaddr(), the comment at the + * beginning of the function expects comparison to happen + * without ports considerations but it uses CMPSADDR() which + * relies either on cmpsaddrstrict() or cmpsaddrwop() based + * on NAT-T support being activated. That make me wonder if I + * should force ports to 0 (ANY) in local and remote values + * used below. + * + * -- arno */ + + /* Apply callback data ...*/ + memset(&ma, 0, sizeof(ma)); + ma.local = local; + ma.remote = remote; + + /* Fill phase1 match criteria ... */ + memset(&ph1sel, 0, sizeof(ph1sel)); + ph1sel.local = old_local; + ph1sel.remote = old_remote; + + + /* Have matching Phase 1 found and addresses updated. As this is a + * time consuming task on a busy responder, and MIGRATE messages + * are always sent for *both* inbound and outbound (and possibly + * forward), we only do that for outbound SP. */ + if (xpl->sadb_x_policy_dir == IPSEC_DIR_OUTBOUND && + enumph1(&ph1sel, migrate_ph1_ike_addresses, &ma) < 0) { + plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: Unable " + "to migrate Phase 1 addresses.\n"); + return -1; + } + + /* We can now update IKE addresses in Phase 2 handle. */ + memset(&ph2sel, 0, sizeof(ph2sel)); + ph2sel.spid = sp->id; + if (enumph2(&ph2sel, migrate_ph2_ike_addresses, &ma) < 0) { + plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: Unable " + "to migrate Phase 2 IKE addresses.\n"); + return -1; + } + + /* and _then_ in SP. */ + if (migrate_sp_ike_addresses(sp, local, remote) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "SADB_X_MIGRATE: Unable to migrate SP IKE addresses.\n"); + return -1; + } + + /* Loop on sadb_x_ipsecrequest list to possibly update sp->req + * entries and associated live Phase 2 handles (their sa_src + * and sa_dst) */ + if (migrate_sp_isr_list(sp, xisr_list, xisr_list_len) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "SADB_X_MIGRATE: Unable to migrate isr list.\n"); + return -1; + } + + return 0; +} +#endif + +/* + * send error against acquire message to kernel. + */ +int +pk_sendeacquire(iph2) + struct ph2handle *iph2; +{ + struct sadb_msg *newmsg; + int len; + + len = sizeof(struct sadb_msg); + newmsg = racoon_calloc(1, len); + if (newmsg == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "failed to get buffer to send acquire.\n"); + return -1; + } + + memset(newmsg, 0, len); + newmsg->sadb_msg_version = PF_KEY_V2; + newmsg->sadb_msg_type = SADB_ACQUIRE; + newmsg->sadb_msg_errno = ENOENT; /* XXX */ + newmsg->sadb_msg_satype = iph2->satype; + newmsg->sadb_msg_len = PFKEY_UNIT64(len); + newmsg->sadb_msg_reserved = 0; + newmsg->sadb_msg_seq = iph2->seq; + newmsg->sadb_msg_pid = (u_int32_t)getpid(); + + /* send message */ + len = pfkey_send(lcconf->sock_pfkey, newmsg, len); + + racoon_free(newmsg); + + return 0; +} + +/* + * check if the algorithm is supported or not. + * OUT 0: ok + * -1: ng + */ +int +pk_checkalg(class, calg, keylen) + int class, calg, keylen; +{ + int sup, error; + u_int alg; + struct sadb_alg alg0; + + switch (algclass2doi(class)) { + case IPSECDOI_PROTO_IPSEC_ESP: + sup = SADB_EXT_SUPPORTED_ENCRYPT; + break; + case IPSECDOI_ATTR_AUTH: + sup = SADB_EXT_SUPPORTED_AUTH; + break; + case IPSECDOI_PROTO_IPCOMP: + plog(LLV_DEBUG, LOCATION, NULL, + "no check of compression algorithm; " + "not supported in sadb message.\n"); + return 0; + default: + plog(LLV_ERROR, LOCATION, NULL, + "invalid algorithm class.\n"); + return -1; + } + alg = ipsecdoi2pfkey_alg(algclass2doi(class), algtype2doi(class, calg)); + if (alg == ~0) + return -1; + + if (keylen == 0) { + if (ipsec_get_keylen(sup, alg, &alg0)) { + plog(LLV_ERROR, LOCATION, NULL, + "%s.\n", ipsec_strerror()); + return -1; + } + keylen = alg0.sadb_alg_minbits; + } + + error = ipsec_check_keylen(sup, alg, keylen); + if (error) + plog(LLV_ERROR, LOCATION, NULL, + "%s.\n", ipsec_strerror()); + + return error; +} + +/* + * differences with pfkey_recv() in libipsec/pfkey.c: + * - never performs busy wait loop. + * - returns NULL and set *lenp to negative on fatal failures + * - returns NULL and set *lenp to non-negative on non-fatal failures + * - returns non-NULL on success + */ +static struct sadb_msg * +pk_recv(so, lenp) + int so; + int *lenp; +{ + struct sadb_msg buf, *newmsg; + int reallen; + int retry = 0; + + *lenp = -1; + do + { + plog(LLV_DEBUG, LOCATION, NULL, "pk_recv: retry[%d] recv() \n", retry ); + *lenp = recv(so, (caddr_t)&buf, sizeof(buf), MSG_PEEK | MSG_DONTWAIT); + retry++; + } + while (*lenp < 0 && errno == EAGAIN && retry < 3); + + if (*lenp < 0) + return NULL; /*fatal*/ + + else if (*lenp < sizeof(buf)) + return NULL; + + reallen = PFKEY_UNUNIT64(buf.sadb_msg_len); + if (reallen < sizeof(buf)) { + *lenp = -1; + errno = EIO; + return NULL; /*fatal*/ + } + if ((newmsg = racoon_calloc(1, reallen)) == NULL) + return NULL; + + *lenp = recv(so, (caddr_t)newmsg, reallen, MSG_PEEK); + if (*lenp < 0) { + racoon_free(newmsg); + return NULL; /*fatal*/ + } else if (*lenp != reallen) { + racoon_free(newmsg); + return NULL; + } + + *lenp = recv(so, (caddr_t)newmsg, reallen, 0); + if (*lenp < 0) { + racoon_free(newmsg); + return NULL; /*fatal*/ + } else if (*lenp != reallen) { + racoon_free(newmsg); + return NULL; + } + + return newmsg; +} + +/* see handler.h */ +u_int32_t +pk_getseq() +{ + return eay_random(); +} + +static int +addnewsp(mhp, local, remote) + caddr_t *mhp; + struct sockaddr *local, *remote; +{ + struct secpolicy *new = NULL; + struct sadb_address *saddr, *daddr; + struct sadb_x_policy *xpl; + struct sadb_lifetime *lt; + u_int64_t created; + + /* sanity check */ + if (mhp[SADB_EXT_ADDRESS_SRC] == NULL + || mhp[SADB_EXT_ADDRESS_DST] == NULL + || mhp[SADB_X_EXT_POLICY] == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "inappropriate sadb spd management message passed.\n"); + goto bad; + } + + saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC]; + daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST]; + xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY]; + lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD]; + if(lt != NULL) + created = lt->sadb_lifetime_addtime; + else + created = 0; + lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD]; + if(lt != NULL) + created = lt->sadb_lifetime_addtime; + else + created = 0; + +#ifdef __linux__ + /* bsd skips over per-socket policies because there will be no + * src and dst extensions in spddump messages. On Linux the only + * way to achieve the same is check for policy id. + */ + if (xpl->sadb_x_policy_id % 8 >= 3) return 0; +#endif + + new = newsp(); + if (new == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "failed to allocate buffer\n"); + goto bad; + } + + new->spidx.dir = xpl->sadb_x_policy_dir; + new->id = xpl->sadb_x_policy_id; + new->policy = xpl->sadb_x_policy_type; + new->req = NULL; + + /* check policy */ + switch (xpl->sadb_x_policy_type) { + case IPSEC_POLICY_DISCARD: + case IPSEC_POLICY_NONE: + case IPSEC_POLICY_ENTRUST: + case IPSEC_POLICY_BYPASS: + break; + + case IPSEC_POLICY_IPSEC: + { + int tlen; + struct sadb_x_ipsecrequest *xisr; + struct ipsecrequest **p_isr = &new->req; + + /* validity check */ + if (PFKEY_EXTLEN(xpl) < sizeof(*xpl)) { + plog(LLV_ERROR, LOCATION, NULL, + "invalid msg length.\n"); + goto bad; + } + + tlen = PFKEY_EXTLEN(xpl) - sizeof(*xpl); + xisr = (struct sadb_x_ipsecrequest *)(xpl + 1); + + while (tlen > 0) { + + /* length check */ + if (xisr->sadb_x_ipsecrequest_len < sizeof(*xisr)) { + plog(LLV_ERROR, LOCATION, NULL, + "invalid msg length.\n"); + goto bad; + } + + /* allocate request buffer */ + *p_isr = newipsecreq(); + if (*p_isr == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "failed to get new ipsecreq.\n"); + goto bad; + } + + /* set values */ + (*p_isr)->next = NULL; + + switch (xisr->sadb_x_ipsecrequest_proto) { + case IPPROTO_ESP: + case IPPROTO_AH: + case IPPROTO_IPCOMP: + break; + default: + plog(LLV_ERROR, LOCATION, NULL, + "invalid proto type: %u\n", + xisr->sadb_x_ipsecrequest_proto); + goto bad; + } + (*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: + plog(LLV_ERROR, LOCATION, NULL, + "invalid mode: %u\n", + xisr->sadb_x_ipsecrequest_mode); + goto bad; + } + (*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; + case IPSEC_LEVEL_UNIQUE: + (*p_isr)->saidx.reqid = + xisr->sadb_x_ipsecrequest_reqid; + break; + + default: + plog(LLV_ERROR, LOCATION, NULL, + "invalid level: %u\n", + xisr->sadb_x_ipsecrequest_level); + goto bad; + } + (*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); + bcopy(paddr, &(*p_isr)->saidx.src, + sysdep_sa_len(paddr)); + + paddr = (struct sockaddr *)((caddr_t)paddr + + sysdep_sa_len(paddr)); + bcopy(paddr, &(*p_isr)->saidx.dst, + sysdep_sa_len(paddr)); + } + + (*p_isr)->sp = new; + + /* initialization for the next. */ + p_isr = &(*p_isr)->next; + tlen -= xisr->sadb_x_ipsecrequest_len; + + /* validity check */ + if (tlen < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "becoming tlen < 0\n"); + } + + xisr = (struct sadb_x_ipsecrequest *)((caddr_t)xisr + + xisr->sadb_x_ipsecrequest_len); + } + } + break; + default: + plog(LLV_ERROR, LOCATION, NULL, + "invalid policy type.\n"); + goto bad; + } + +#ifdef HAVE_PFKEY_POLICY_PRIORITY + KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, + saddr + 1, + daddr + 1, + saddr->sadb_address_prefixlen, + daddr->sadb_address_prefixlen, + saddr->sadb_address_proto, + xpl->sadb_x_policy_priority, + created, + &new->spidx); +#else + KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, + saddr + 1, + daddr + 1, + saddr->sadb_address_prefixlen, + daddr->sadb_address_prefixlen, + saddr->sadb_address_proto, + created, + &new->spidx); +#endif + +#ifdef HAVE_SECCTX + if (mhp[SADB_X_EXT_SEC_CTX] != NULL) { + struct sadb_x_sec_ctx *ctx; + + ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX]; + new->spidx.sec_ctx.ctx_alg = ctx->sadb_x_ctx_alg; + new->spidx.sec_ctx.ctx_doi = ctx->sadb_x_ctx_doi; + new->spidx.sec_ctx.ctx_strlen = ctx->sadb_x_ctx_len; + memcpy(new->spidx.sec_ctx.ctx_str,ctx + 1,ctx->sadb_x_ctx_len); + } +#endif /* HAVE_SECCTX */ + + /* Set local and remote hints for that SP, if available */ + if (local && remote) { + new->local = dupsaddr(local); + new->remote = dupsaddr(remote); + } + + inssp(new); + + return 0; +bad: + if (new != NULL) { + if (new->req != NULL) + racoon_free(new->req); + racoon_free(new); + } + return -1; +} + +/* proto/mode/src->dst spi */ +const char * +sadbsecas2str(src, dst, proto, spi, mode) + struct sockaddr *src, *dst; + int proto; + u_int32_t spi; + int mode; +{ + static char buf[256]; + u_int doi_proto, doi_mode = 0; + char *p; + int blen, i; + + doi_proto = pfkey2ipsecdoi_proto(proto); + if (doi_proto == ~0) + return NULL; + if (mode) { + doi_mode = pfkey2ipsecdoi_mode(mode); + if (doi_mode == ~0) + return NULL; + } + + blen = sizeof(buf) - 1; + p = buf; + + i = snprintf(p, blen, "%s%s%s ", + s_ipsecdoi_proto(doi_proto), + mode ? "/" : "", + mode ? s_ipsecdoi_encmode(doi_mode) : ""); + if (i < 0 || i >= blen) + return NULL; + p += i; + blen -= i; + + i = snprintf(p, blen, "%s->", saddr2str(src)); + if (i < 0 || i >= blen) + return NULL; + p += i; + blen -= i; + + i = snprintf(p, blen, "%s ", saddr2str(dst)); + if (i < 0 || i >= blen) + return NULL; + p += i; + blen -= i; + + if (spi) { + snprintf(p, blen, "spi=%lu(0x%lx)", (unsigned long)ntohl(spi), + (unsigned long)ntohl(spi)); + } + + return buf; +} |