/* $NetBSD: pfkey.c,v 1.57 2011/03/15 13:20:14 vanhu Exp $ */
/* $Id: pfkey.c,v 1.1.1.1 2012/02/21 22:39:10 misho 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;
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>