#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
static u_int ipsecdoi2pfkey_aalg(u_int);
static u_int ipsecdoi2pfkey_ealg(u_int);
static u_int ipsecdoi2pfkey_calg(u_int);
static u_int ipsecdoi2pfkey_alg(u_int, u_int);
static u_int keylen_aalg(u_int);
static u_int keylen_ealg(u_int, int);
static int pk_recvgetspi(caddr_t *);
static int pk_recvupdate(caddr_t *);
static int pk_recvadd(caddr_t *);
static int pk_recvdelete(caddr_t *);
static int pk_recvacquire(caddr_t *);
static int pk_recvexpire(caddr_t *);
static int pk_recvflush(caddr_t *);
static int getsadbpolicy(caddr_t *, int *, int, struct ph2handle *);
static int pk_recvspdupdate(caddr_t *);
static int pk_recvspdadd(caddr_t *);
static int pk_recvspddelete(caddr_t *);
static int pk_recvspdexpire(caddr_t *);
static int pk_recvspdget(caddr_t *);
static int pk_recvspddump(caddr_t *);
static int pk_recvspdflush(caddr_t *);
#if defined(SADB_X_MIGRATE) && defined(SADB_X_EXT_KMADDRESS)
static int pk_recvmigrate(caddr_t *);
#endif
static struct sadb_msg *pk_recv(int, int *);
static int (*pkrecvf[])(caddr_t *) = {
NULL,
pk_recvgetspi,
pk_recvupdate,
pk_recvadd,
pk_recvdelete,
NULL,
pk_recvacquire,
NULL,
pk_recvexpire,
pk_recvflush,
NULL,
NULL,
NULL,
pk_recvspdupdate,
pk_recvspdadd,
pk_recvspddelete,
pk_recvspdget,
NULL,
pk_recvspddump,
pk_recvspdflush,
NULL,
pk_recvspdexpire,
NULL,
NULL,
#if defined(SADB_X_MIGRATE) && defined(SADB_X_EXT_KMADDRESS)
pk_recvmigrate,
#else
NULL,
#endif
#if (SADB_MAX > 24)
#error "SADB extra message?"
#endif
};
static int addnewsp(caddr_t *, struct sockaddr *, struct sockaddr *);
#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
static int
pfkey_handler(void *ctx __unused, int fd)
{
struct sadb_msg *msg;
int len;
caddr_t mhp[SADB_EXT_MAX + 1];
int error = -1;
len = 0;
msg = (struct sadb_msg *) pk_recv(fd, &len);
if (msg == NULL) {
if (len < 0) {
if (errno == EAGAIN)
goto end;
plog(LLV_ERROR, LOCATION, NULL,
"failed to recv from pfkey (%s)\n",
strerror(errno));
goto end;
} else {
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);
if (msg->sadb_msg_errno) {
int pri;
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;
}
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];
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);
}
vchar_t *
pfkey_dump_sadb(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;
}
for (;;) {
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
void
pfkey_flush_sadb(u_int proto)
{
int 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
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]);
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;
}
static u_int
ipsecdoi2pfkey_aalg(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:
return SADB_AALG_NONE;
case IPSECDOI_ATTR_AUTH_DES_MAC:
plog(LLV_ERROR, LOCATION, NULL,
"Not supported hash type: %u\n", hashtype);
return ~0u;
case 0:
default:
return SADB_AALG_NONE;
plog(LLV_ERROR, LOCATION, NULL,
"Invalid hash type: %u\n", hashtype);
return ~0u;
}
}
static u_int
ipsecdoi2pfkey_ealg(u_int t_id)
{
switch (t_id) {
case IPSECDOI_ESP_DES_IV64:
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:
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_AESGCM16
case IPSECDOI_ESP_AESGCM16:
return SADB_X_EALG_AESGCM16;
#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
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 ~0u;
case 0:
default:
plog(LLV_ERROR, LOCATION, NULL,
"Invalid transform id: %u\n", t_id);
return ~0u;
}
}
static u_int
ipsecdoi2pfkey_calg(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:
default:
plog(LLV_ERROR, LOCATION, NULL,
"Invalid transform id: %u\n", t_id);
return ~0u;
}
}
u_int
ipsecdoi2pfkey_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 ~0u;
}
}
static u_int
ipsecdoi2pfkey_alg(u_int algclass, u_int 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 ~0u;
}
}
u_int
pfkey2ipsecdoi_proto(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 ~0u;
}
}
u_int
ipsecdoi2pfkey_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 ~0u;
}
}
u_int
pfkey2ipsecdoi_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 ~0u;
}
}
static u_int
keylen_aalg(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 ~0u;
}
return res;
}
static u_int
keylen_ealg(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 ~0u;
}
return res;
}
void
pk_fixup_sa_addresses(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])) {
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(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)) == ~0u)
goto bad;
if ((*e_keylen = keylen_ealg(t_id, *e_keylen)) == ~0u)
goto bad;
*e_keylen >>= 3;
if ((*a_type = ipsecdoi2pfkey_aalg(hashtype)) == ~0u)
goto bad;
if ((*a_keylen = keylen_aalg(hashtype)) == ~0u)
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)) == ~0u)
goto bad;
if ((*a_keylen = keylen_aalg(hashtype)) == ~0u)
goto bad;
*a_keylen >>= 3;
if (t_id == IPSECDOI_ATTR_AUTH_HMAC_MD5
&& hashtype == IPSECDOI_ATTR_AUTH_KPDK) {
*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)) == ~0u)
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;
}
int
pk_sendgetspi(struct ph2handle *iph2)
{
struct sockaddr *src = NULL, *dst = NULL;
u_int satype, mode;
struct saprop *pp;
struct saproto *pr;
uint32_t minspi, maxspi;
uint8_t natt_type = 0;
uint16_t sport = 0, dport = 0;
if (iph2->side == INITIATOR)
pp = iph2->proposal;
else
pp = iph2->approval;
if (iph2->sa_src && iph2->sa_dst) {
src = dupsaddr(iph2->sa_src);
dst = dupsaddr(iph2->sa_dst);
} else {
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) {
satype = ipsecdoi2pfkey_proto(pr->proto_id);
if (satype == ~0u) {
plog(LLV_ERROR, LOCATION, NULL,
"invalid proto_id %d\n", pr->proto_id);
racoon_free(src);
racoon_free(dst);
return -1;
}
else if (satype == SADB_X_SATYPE_IPCOMP) {
minspi = 0x100;
maxspi = 0xffff;
}
else {
minspi = 0;
maxspi = 0;
}
mode = ipsecdoi2pfkey_mode(pr->encmode);
if (mode == ~0u) {
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,
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;
}
static int
pk_recvgetspi(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;
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]);
src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]);
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;
}
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;
}
if (notfound) {
plog(LLV_ERROR, LOCATION, NULL,
"get spi for unknown address %s\n",
saddrwop2str(dst));
return -1;
}
if (allspiok) {
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;
}
int
pk_sendupdate(struct ph2handle *iph2)
{
struct saproto *pr;
struct pfkey_send_sa_args sa_args;
if (iph2->approval == NULL) {
plog(LLV_ERROR, LOCATION, NULL,
"no approvaled SAs found.\n");
return -1;
}
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) {
sa_args.dst = dupsaddr(iph2->sa_src);
sa_args.src = dupsaddr(iph2->sa_dst);
} else {
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) {
sa_args.satype = ipsecdoi2pfkey_proto(pr->proto_id);
if (sa_args.satype == ~0u) {
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) {
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 == ~0u) {
plog(LLV_ERROR, LOCATION, NULL,
"invalid encmode %d\n", pr->encmode);
racoon_free(sa_args.src);
racoon_free(sa_args.dst);
return -1;
}
#endif
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
#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
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;
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(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;
if (f_local)
return 0;
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;
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;
}
for (pr = iph2->approval->head; pr != NULL; pr = pr->next) {
proto_id = pfkey2ipsecdoi_proto(msg->sadb_msg_satype);
if (proto_id == ~0u) {
plog(LLV_ERROR, LOCATION, NULL,
"invalid proto_id %d\n", msg->sadb_msg_satype);
return -1;
}
encmode = pfkey2ipsecdoi_mode(sa_mode);
if (encmode == ~0u) {
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;
sched_cancel(&iph2->sce);
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
sched_cancel(&iph2->scr);
sched_schedule(&iph2->sce, iph2->approval->lifetime,
isakmp_ph2expire_stub);
plog(LLV_DEBUG, LOCATION, NULL, "===\n");
return 0;
}
int
pk_sendadd(struct ph2handle *iph2)
{
struct saproto *pr;
struct pfkey_send_sa_args sa_args;
if (iph2->approval == NULL) {
plog(LLV_ERROR, LOCATION, NULL,
"no approvaled SAs found.\n");
return -1;
}
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) {
sa_args.src = dupsaddr(iph2->sa_src);
sa_args.dst = dupsaddr(iph2->sa_dst);
} else {
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) {
sa_args.satype = ipsecdoi2pfkey_proto(pr->proto_id);
if (sa_args.satype == ~0u) {
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) {
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 == ~0u) {
plog(LLV_ERROR, LOCATION, NULL,
"invalid encmode %d\n", pr->encmode);
racoon_free(sa_args.src);
racoon_free(sa_args.dst);
return -1;
}
#endif
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
#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
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;
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(caddr_t *mhp)
{
struct sadb_msg *msg;
struct sadb_sa *sa;
struct sockaddr *src, *dst;
struct ph2handle *iph2;
u_int sa_mode;
if (f_local)
return 0;
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;
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;
}
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(caddr_t *mhp)
{
struct sadb_msg *msg;
struct sadb_sa *sa;
struct sockaddr *src, *dst;
struct ph2handle *iph2;
u_int proto_id, sa_mode;
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 == ~0u) {
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) {
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;
}
if (iph2->status > PHASE2ST_ESTABLISHED)
return 0;
if (iph2->status < PHASE2ST_ESTABLISHED) {
if (mhp[SADB_EXT_LIFETIME_HARD] == NULL)
return 0;
plog(LLV_WARNING, LOCATION, NULL,
"PF_KEY EXPIRE message received from kernel for SA"
" being negotiated. Stopping negotiation.\n");
}
sched_cancel(&iph2->sce);
if (iph2->status == PHASE2ST_ESTABLISHED &&
iph2->side == INITIATOR) {
struct ph1handle *iph1hint;
iph1hint = iph2->ph1;
initph2(iph2);
iph2->status = PHASE2ST_STATUS2;
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;
}
iph2->status = PHASE2ST_EXPIRED;
remph2(iph2);
delph2(iph2);
return 0;
}
static int
pk_recvacquire(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;
struct sockaddr *sp_src, *sp_dst;
struct sockaddr *sa_src = NULL, *sa_dst = NULL ;
#ifdef HAVE_SECCTX
struct sadb_x_sec_ctx *m_sec_ctx;
#endif
struct policyindex spidx;
if (f_local)
return 0;
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];
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
if (xpl->sadb_x_policy_type != IPSEC_POLICY_IPSEC) {
plog(LLV_DEBUG, LOCATION, NULL,
"ignore ACQUIRE message. type is not IPsec.\n");
return 0;
}
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;
}
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));
if (sp_out->req && sp_out->req->saidx.mode == IPSEC_MODE_TUNNEL) {
src = (struct sockaddr *) &sp_out->req->saidx.src;
dst = (struct sockaddr *) &sp_out->req->saidx.dst;
} else {
src = sa_src = sp_src;
dst = sa_dst = sp_dst;
}
if (sp_out->local && sp_out->remote) {
sa_src = src;
sa_dst = dst;
src = (struct sockaddr *) sp_out->local;
dst = (struct sockaddr *) sp_out->remote;
}
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;
}
if (myaddr_getsport(src) == -1) {
plog(LLV_DEBUG, LOCATION, NULL,
"Not listening on source address %s. Ignoring ACQUIRE.\n",
saddrwop2str(src));
return 0;
}
{
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
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));
}
}
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;
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) {
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
insph2(iph2);
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(caddr_t *mhp)
{
struct sadb_msg *msg;
struct sadb_sa *sa;
struct sockaddr *src, *dst;
struct ph2handle *iph2 = NULL;
u_int proto_id;
if (f_local)
return 0;
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]);
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 == ~0u) {
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) {
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));
if (iph2->status == PHASE2ST_ESTABLISHED)
isakmp_info_send_d2(iph2);
remph2(iph2);
delph2(iph2);
return 0;
}
static int
pk_recvflush(caddr_t *mhp)
{
if (f_local)
return 0;
if (mhp[0] == NULL) {
plog(LLV_ERROR, LOCATION, NULL,
"inappropriate sadb flush message passed.\n");
return -1;
}
flushph2();
return 0;
}
static int
getsadbpolicy(caddr_t *policy0, int *policylen0, int 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
policylen = sizeof(struct sadb_x_policy);
if (type != SADB_X_SPDDELETE) {
if (iph2->sa_src && iph2->sa_dst) {
src = iph2->sa_src;
dst = iph2->sa_dst;
} else {
src = iph2->src;
dst = iph2->dst;
}
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
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
if (type == SADB_X_SPDDELETE)
goto end;
xisr = (struct sadb_x_ipsecrequest *)(xpl + len);
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 == ~0u) {
plog(LLV_ERROR, LOCATION, NULL,
"invalid proto_id %d\n", pr->proto_id);
goto err;
}
mode = ipsecdoi2pfkey_mode(pr->encmode);
if (mode == ~0u) {
plog(LLV_ERROR, LOCATION, NULL,
"invalid encmode %d\n", pr->encmode);
goto err;
}
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(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(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;
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
sp = getsp(&spidx);
if (sp == NULL) {
plog(LLV_DEBUG, LOCATION, NULL,
"this policy did not exist for removal: \"%s\"\n",
spidx2str(&spidx));
} else {
local = sp->local;
remote = sp->remote;
sp->local = NULL;
sp->remote = NULL;
remsp(sp);
delsp(sp);
}
ret = addnewsp(mhp, local, remote);
if (local != NULL)
racoon_free(local);
if (remote != NULL)
racoon_free(remote);
if (ret < 0)
return -1;
return 0;
}
int
pk_sendspdadd2(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(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;
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
sp = getsp(&spidx);
if (sp != NULL) {
plog(LLV_ERROR, LOCATION, NULL,
"such policy already exists. "
"anyway replace it: %s\n",
spidx2str(&spidx));
local = sp->local;
remote = sp->remote;
sp->local = NULL;
sp->remote = NULL;
remsp(sp);
delsp(sp);
}
ret = addnewsp(mhp, local, remote);
if (local != NULL)
racoon_free(local);
if (remote != NULL)
racoon_free(remote);
if (ret < 0)
return -1;
return 0;
}
int
pk_sendspddelete(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(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;
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
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(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;
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
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(caddr_t *mhp)
{
if (mhp[0] == NULL) {
plog(LLV_ERROR, LOCATION, NULL,
"inappropriate sadb spdget message passed.\n");
return -1;
}
return 0;
}
static int
pk_recvspddump(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;
if (mhp[0] == NULL) {
plog(LLV_ERROR, LOCATION, NULL,
"inappropriate sadb spddump 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;
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
sp = getsp(&spidx);
if (sp != NULL) {
plog(LLV_ERROR, LOCATION, NULL,
"such policy already exists. "
"anyway replace it: %s\n",
spidx2str(&spidx));
local = sp->local;
remote = sp->remote;
sp->local = NULL;
sp->remote = NULL;
remsp(sp);
delsp(sp);
}
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(caddr_t *mhp)
{
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)
struct migrate_args {
struct sockaddr *local;
struct sockaddr *remote;
};
static int
migrate_ph1_ike_addresses(iph1, arg)
struct ph1handle *iph1;
void *arg;
{
struct migrate_args *ma = (struct migrate_args *) arg;
struct remoteconf *rmconf;
uint16_t port;
if (cmpsaddr(iph1->local, ma->local) == CMPSADDR_MATCH &&
cmpsaddr(iph1->remote, ma->remote) == CMPSADDR_MATCH)
return 0;
if (iph1->status < PHASE1ST_ESTABLISHED) {
plog(LLV_WARNING, LOCATION, NULL, "MIGRATE received *during* "
"Phase 1 negotiation (%s).\n",
saddr2str_fromto("%s => %s", ma->local, ma->remote));
rmconf = getrmconf(ma->remote, 0);
if (rmconf == NULL || !rmconf->passive) {
iph1->status = PHASE1ST_EXPIRED;
isakmp_ph1delete(iph1);
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");
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;
}
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 (iph2->ph1)
migrate_ph1_ike_addresses(iph2->ph1, arg);
if (cmpsaddr(iph2->src, ma->local) == CMPSADDR_MATCH &&
cmpsaddr(iph2->dst, ma->remote) == CMPSADDR_MATCH)
return 0;
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;
}
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;
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");
sched_cancel(&iph2->sce);
iph2->status = PHASE2ST_EXPIRED;
iph1hint = iph2->ph1;
initph2(iph2);
iph2->status = PHASE2ST_STATUS2;
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");
isakmp_ph2expire(iph2);
}
}
return 0;
}
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;
}
static int
migrate_ph2_one_isr(spid, isr_cur, xisr_old, xisr_new)
uint32_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;
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;
if (isr_cur->saidx.reqid &&
(xisr_old->sadb_x_ipsecrequest_level != IPSEC_LEVEL_UNIQUE ||
xisr_new->sadb_x_ipsecrequest_level != 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 (saidx->mode != IPSEC_MODE_TUNNEL) {
plog(LLV_DEBUG, LOCATION, NULL, "SADB_X_MIGRATE: "
"non tunnel mode isr, skipping SA address migration.\n");
return 0;
}
osaddr = (struct sockaddr *)(xisr_old + 1);
odaddr = (struct sockaddr *)(((uint8_t *)osaddr) + sysdep_sa_len(osaddr));
nsaddr = (struct sockaddr *)(xisr_new + 1);
ndaddr = (struct sockaddr *)(((uint8_t *)nsaddr) + sysdep_sa_len(nsaddr));
if (osaddr->sa_family != odaddr->sa_family ||
nsaddr->sa_family != ndaddr->sa_family)
return -1;
if (saidx->src.ss_family != osaddr->sa_family)
return -1;
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;
}
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;
memcpy(&saidx->src, nsaddr, sysdep_sa_len(nsaddr));
memcpy(&saidx->dst, ndaddr, sysdep_sa_len(ndaddr));
return 0;
}
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;
while (xisr_list_len > 0 && isr_cur != NULL) {
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;
}
xisr_new = (struct sadb_x_ipsecrequest *)(((uint8_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;
}
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;
}
xisr_list_len -= xisr_old_len + xisr_new_len;
xisr_old = (struct sadb_x_ipsecrequest *)(((uint8_t *)xisr_new) +
xisr_new_len);
isr_cur = isr_cur->next;
}
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;
}
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;
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 *)(((uint8_t *)(*local)) + local_len);
if ((*local)->sa_family != (*remote)->sa_family)
return -1;
return 0;
}
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;
uint32_t spid;
u_int64_t created;
int xisr_list_len;
int ulproto;
struct migrate_args ma;
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;
}
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");
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;
}
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
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;
}
if (sp->local && sp->remote) {
old_local = (struct sockaddr *)sp->local;
old_remote = (struct sockaddr *)sp->remote;
} else if (sp->req && sp->req->saidx.mode == IPSEC_MODE_TUNNEL) {
old_local = (struct sockaddr *)&sp->req->saidx.src;
old_remote = (struct sockaddr *)&sp->req->saidx.dst;
} else {
old_local = (struct sockaddr *)&sp->spidx.src;
old_remote = (struct sockaddr *)&sp->spidx.dst;
}
memset(&ma, 0, sizeof(ma));
ma.local = local;
ma.remote = remote;
memset(&ph1sel, 0, sizeof(ph1sel));
ph1sel.local = old_local;
ph1sel.remote = old_remote;
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;
}
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;
}
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;
}
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
int
pk_sendeacquire(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;
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 = (uint32_t)getpid();
len = pfkey_send(lcconf->sock_pfkey, newmsg, len);
racoon_free(newmsg);
return 0;
}
int
pk_checkalg(int class, int calg, int 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 == ~0u)
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;
}
static struct sadb_msg *
pk_recv(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;
else if (*lenp < sizeof(buf))
return NULL;
reallen = PFKEY_UNUNIT64(buf.sadb_msg_len);
if (reallen < sizeof(buf)) {
*lenp = -1;
errno = EIO;
return NULL;
}
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;
} 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;
} else if (*lenp != reallen) {
racoon_free(newmsg);
return NULL;
}
return newmsg;
}
uint32_t
pk_getseq(void)
{
return eay_random();
}
static int
addnewsp(caddr_t *mhp, struct sockaddr *local, struct sockaddr *remote)
{
struct secpolicy *new = NULL;
struct sadb_address *saddr, *daddr;
struct sadb_x_policy *xpl;
struct sadb_lifetime *lt;
u_int64_t created;
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__
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;
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;
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) {
if (xisr->sadb_x_ipsecrequest_len < sizeof(*xisr)) {
plog(LLV_ERROR, LOCATION, NULL,
"invalid msg length.\n");
goto bad;
}
*p_isr = newipsecreq();
if (*p_isr == NULL) {
plog(LLV_ERROR, LOCATION, NULL,
"failed to get new ipsecreq.\n");
goto bad;
}
(*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;
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;
p_isr = &(*p_isr)->next;
tlen -= xisr->sadb_x_ipsecrequest_len;
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
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;
}
const char *
sadbsecas2str(struct sockaddr *src, struct sockaddr *dst, int proto,
uint32_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 == ~0u)
return NULL;
if (mode) {
doi_mode = pfkey2ipsecdoi_mode(mode);
if (doi_mode == ~0u)
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;
}