summaryrefslogtreecommitdiff
path: root/keyexchange/isakmpd-20041012/pf_key_v2.c
diff options
context:
space:
mode:
Diffstat (limited to 'keyexchange/isakmpd-20041012/pf_key_v2.c')
-rw-r--r--keyexchange/isakmpd-20041012/pf_key_v2.c4442
1 files changed, 4442 insertions, 0 deletions
diff --git a/keyexchange/isakmpd-20041012/pf_key_v2.c b/keyexchange/isakmpd-20041012/pf_key_v2.c
new file mode 100644
index 0000000..d8cbc35
--- /dev/null
+++ b/keyexchange/isakmpd-20041012/pf_key_v2.c
@@ -0,0 +1,4442 @@
+/* $OpenBSD: pf_key_v2.c,v 1.150 2004/09/17 13:53:08 ho Exp $ */
+/* $EOM: pf_key_v2.c,v 1.79 2000/12/12 00:33:19 niklas Exp $ */
+
+/*
+ * Copyright (c) 1999, 2000, 2001 Niklas Hallqvist. All rights reserved.
+ * Copyright (c) 1999, 2000, 2001 Angelos D. Keromytis. All rights reserved.
+ * Copyright (c) 2001 Håkan Olsson. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/*
+ * This code was written under funding by Ericsson Radio Systems.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+
+#include "sysdep.h"
+
+#if !defined (LINUX_IPSEC)
+#include <net/pfkeyv2.h>
+#endif
+#include <netinet/in.h>
+#ifdef SADB_X_EXT_FLOW_TYPE
+#include <sys/mbuf.h>
+#include <netinet/ip_ipsp.h>
+#endif
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <errno.h>
+#include <bitstring.h>
+
+#include "cert.h"
+#include "conf.h"
+#include "exchange.h"
+#include "ipsec.h"
+#include "ipsec_num.h"
+#include "key.h"
+#include "log.h"
+#include "pf_key_v2.h"
+#include "sa.h"
+#include "timer.h"
+#include "transport.h"
+#include "util.h"
+
+#if defined (USE_KEYNOTE)
+#include "policy.h"
+#endif
+
+#if defined (USE_NAT_TRAVERSAL)
+#include "udp_encap.h"
+#endif
+
+#define IN6_IS_ADDR_FULL(a) \
+ ((*(u_int32_t *)(void *)(&(a)->s6_addr[0]) == 0xffff) && \
+ (*(u_int32_t *)(void *)(&(a)->s6_addr[4]) == 0xffff) && \
+ (*(u_int32_t *)(void *)(&(a)->s6_addr[8]) == 0xffff) && \
+ (*(u_int32_t *)(void *)(&(a)->s6_addr[12]) == 0xffff))
+
+#define ADDRESS_MAX sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"
+
+/*
+ * PF_KEY v2 always work with 64-bit entities and aligns on 64-bit boundaries.
+ */
+#define PF_KEY_V2_CHUNK 8
+#define PF_KEY_V2_ROUND(x) \
+ (((x) + PF_KEY_V2_CHUNK - 1) & ~(PF_KEY_V2_CHUNK - 1))
+
+/* How many microseconds we will wait for a reply from the PF_KEY socket. */
+#define PF_KEY_REPLY_TIMEOUT 1000
+
+struct pf_key_v2_node {
+ TAILQ_ENTRY(pf_key_v2_node) link;
+ void *seg;
+ size_t sz;
+ int cnt;
+ u_int16_t type;
+ u_int8_t flags;
+};
+
+TAILQ_HEAD(pf_key_v2_msg, pf_key_v2_node);
+
+#define PF_KEY_V2_NODE_MALLOCED 1
+#define PF_KEY_V2_NODE_MARK 2
+
+/* Used to derive "unique" connection identifiers. */
+int connection_seq = 0;
+
+#ifdef KAME
+/*
+ * KAME requires the sadb_msg_seq of an UPDATE be the same of that of the
+ * GETSPI creating the larval SA.
+ */
+struct pf_key_v2_sa_seq {
+ TAILQ_ENTRY(pf_key_v2_sa_seq) link;
+ u_int8_t *spi;
+ size_t sz;
+ u_int8_t proto;
+ struct sockaddr *dst;
+ int dstlen;
+ u_int32_t seq;
+};
+
+TAILQ_HEAD(, pf_key_v2_sa_seq) pf_key_v2_sa_seq_map;
+#endif
+
+#ifndef KAME
+static u_int8_t *pf_key_v2_convert_id(u_int8_t *, int, size_t *, int *);
+#endif
+static struct pf_key_v2_msg *pf_key_v2_call(struct pf_key_v2_msg *);
+static struct pf_key_v2_node *pf_key_v2_find_ext(struct pf_key_v2_msg *,
+ u_int16_t);
+static void pf_key_v2_notify(struct pf_key_v2_msg *);
+static struct pf_key_v2_msg *pf_key_v2_read(u_int32_t);
+static u_int32_t pf_key_v2_seq(void);
+static u_int32_t pf_key_v2_write(struct pf_key_v2_msg *);
+static int pf_key_v2_remove_conf(char *);
+static int pf_key_v2_conf_refhandle(int, char *);
+
+#ifdef SADB_X_ASKPOLICY
+static int pf_key_v2_conf_refinc(int, char *);
+#endif
+
+/* The socket to use for PF_KEY interactions. */
+int pf_key_v2_socket;
+
+#ifdef KAME
+static int
+pf_key_v2_register_sa_seq(u_int8_t *spi, size_t sz, u_int8_t proto,
+ struct sockaddr *dst, int dstlen, u_int32_t seq)
+{
+ struct pf_key_v2_sa_seq *node = 0;
+
+ node = malloc(sizeof *node);
+ if (!node)
+ goto cleanup;
+ memset(node, '0', sizeof *node);
+ node->spi = malloc(sz);
+ if (!node->spi)
+ goto cleanup;
+ node->dst = malloc(sysdep_sa_len(dst));
+ if (!node->dst)
+ goto cleanup;
+ memcpy(node->dst, dst, sysdep_sa_len(dst));
+ node->dstlen = sysdep_sa_len(dst);
+ memcpy(node->spi, spi, sz);
+ node->sz = sz;
+ node->proto = proto;
+ node->seq = seq;
+ TAILQ_INSERT_TAIL(&pf_key_v2_sa_seq_map, node, link);
+ return 1;
+
+cleanup:
+ if (node->dst)
+ free(node->dst);
+ if (node)
+ free(node);
+ return 0;
+}
+
+static u_int32_t
+pf_key_v2_seq_by_sa(u_int8_t *spi, size_t sz, u_int8_t proto,
+ struct sockaddr *dst, int dstlen)
+{
+ struct pf_key_v2_sa_seq *node;
+
+ for (node = TAILQ_FIRST(&pf_key_v2_sa_seq_map); node;
+ node = TAILQ_NEXT(node, link))
+ if (node->proto == proto &&
+ node->sz == sz && memcmp(node->spi, spi, sz) == 0 &&
+ node->dstlen == sysdep_sa_len(dst) &&
+ memcmp(node->dst, dst, sysdep_sa_len(dst)) == 0)
+ return node->seq;
+ return 0;
+}
+#endif
+
+static struct pf_key_v2_msg *
+pf_key_v2_msg_new(struct sadb_msg *msg, int flags)
+{
+ struct pf_key_v2_node *node = 0;
+ struct pf_key_v2_msg *ret;
+
+ node = malloc(sizeof *node);
+ if (!node)
+ goto cleanup;
+ ret = malloc(sizeof *ret);
+ if (!ret)
+ goto cleanup;
+ TAILQ_INIT(ret);
+ node->seg = msg;
+ node->sz = sizeof *msg;
+ node->type = 0;
+ node->cnt = 1;
+ node->flags = flags;
+ TAILQ_INSERT_HEAD(ret, node, link);
+ return ret;
+
+cleanup:
+ if (node)
+ free(node);
+ return 0;
+}
+
+/* Add a SZ sized segment SEG to the PF_KEY message MSG. */
+static int
+pf_key_v2_msg_add(struct pf_key_v2_msg *msg, struct sadb_ext *ext, int flags)
+{
+ struct pf_key_v2_node *node;
+
+ node = malloc(sizeof *node);
+ if (!node)
+ return -1;
+ node->seg = ext;
+ node->sz = ext->sadb_ext_len * PF_KEY_V2_CHUNK;
+ node->type = ext->sadb_ext_type;
+ node->flags = flags;
+ TAILQ_FIRST(msg)->cnt++;
+ TAILQ_INSERT_TAIL(msg, node, link);
+ return 0;
+}
+
+/* Deallocate the PF_KEY message MSG. */
+static void
+pf_key_v2_msg_free(struct pf_key_v2_msg *msg)
+{
+ struct pf_key_v2_node *np;
+
+ np = TAILQ_FIRST(msg);
+ while (np) {
+ TAILQ_REMOVE(msg, np, link);
+ if (np->flags & PF_KEY_V2_NODE_MALLOCED)
+ free(np->seg);
+ free(np);
+ np = TAILQ_FIRST(msg);
+ }
+ free(msg);
+}
+
+/* Just return a new sequence number. */
+static u_int32_t
+pf_key_v2_seq(void)
+{
+ static u_int32_t seq = 0;
+
+ return ++seq;
+}
+
+/*
+ * Read a PF_KEY packet with SEQ as the sequence number, looping if necessary.
+ * If SEQ is zero just read the first message we see, otherwise we queue
+ * messages up until both the PID and the sequence number match.
+ */
+static struct pf_key_v2_msg *
+pf_key_v2_read(u_int32_t seq)
+{
+ ssize_t n;
+ u_int8_t *buf = 0;
+ struct pf_key_v2_msg *ret = 0;
+ struct sadb_msg *msg;
+ struct sadb_msg hdr;
+ struct sadb_ext *ext;
+ struct timeval tv;
+ fd_set *fds;
+
+ while (1) {
+ /*
+ * If this is a read of a reply we should actually expect the
+ * reply to get lost as PF_KEY is an unreliable service per
+ * the specs. Currently we do this by setting a short timeout,
+ * and if it is not readable in that time, we fail the read.
+ */
+ if (seq) {
+ fds = calloc(howmany(pf_key_v2_socket + 1, NFDBITS),
+ sizeof(fd_mask));
+ if (!fds) {
+ log_error("pf_key_v2_read: "
+ "calloc (%lu, %lu) failed",
+ (unsigned long) howmany(pf_key_v2_socket + 1,
+ NFDBITS),
+ (unsigned long) sizeof(fd_mask));
+ goto cleanup;
+ }
+ FD_SET(pf_key_v2_socket, fds);
+ tv.tv_sec = 0;
+ tv.tv_usec = PF_KEY_REPLY_TIMEOUT;
+ n = select(pf_key_v2_socket + 1, fds, 0, 0, &tv);
+ free(fds);
+ if (n == -1) {
+ log_error("pf_key_v2_read: "
+ "select (%d, fds, 0, 0, &tv) failed",
+ pf_key_v2_socket + 1);
+ goto cleanup;
+ }
+ if (!n) {
+ log_print("pf_key_v2_read: "
+ "no reply from PF_KEY");
+ goto cleanup;
+ }
+ }
+ n = recv(pf_key_v2_socket, &hdr, sizeof hdr, MSG_PEEK);
+ if (n == -1) {
+ log_error("pf_key_v2_read: recv (%d, ...) failed",
+ pf_key_v2_socket);
+ goto cleanup;
+ }
+ if (n != sizeof hdr) {
+ log_error("pf_key_v2_read: recv (%d, ...) "
+ "returned short packet (%lu bytes)",
+ pf_key_v2_socket, (unsigned long) n);
+ goto cleanup;
+ }
+ n = hdr.sadb_msg_len * PF_KEY_V2_CHUNK;
+ buf = malloc(n);
+ if (!buf) {
+ log_error("pf_key_v2_read: malloc (%lu) failed",
+ (unsigned long) n);
+ goto cleanup;
+ }
+ n = read(pf_key_v2_socket, buf, n);
+ if (n == -1) {
+ log_error("pf_key_v2_read: read (%d, ...) failed",
+ pf_key_v2_socket);
+ goto cleanup;
+ }
+ if (n != hdr.sadb_msg_len * PF_KEY_V2_CHUNK) {
+ log_print("pf_key_v2_read: read (%d, ...) "
+ "returned short packet (%lu bytes)",
+ pf_key_v2_socket, (unsigned long) n);
+ goto cleanup;
+ }
+ LOG_DBG_BUF((LOG_SYSDEP, 80, "pf_key_v2_read: msg", buf, n));
+
+ /* We drop all messages that is not what we expect. */
+ msg = (struct sadb_msg *) buf;
+ if (msg->sadb_msg_version != PF_KEY_V2 ||
+ (msg->sadb_msg_pid != 0 &&
+ msg->sadb_msg_pid != (u_int32_t) getpid())) {
+ if (seq) {
+ free(buf);
+ buf = 0;
+ continue;
+ } else {
+ LOG_DBG((LOG_SYSDEP, 90, "pf_key_v2_read:"
+ "bad version (%d) or PID (%d, mine is "
+ "%ld), ignored", msg->sadb_msg_version,
+ msg->sadb_msg_pid, (long) getpid()));
+ goto cleanup;
+ }
+ }
+ /* Parse the message. */
+ ret = pf_key_v2_msg_new(msg, PF_KEY_V2_NODE_MALLOCED);
+ if (!ret)
+ goto cleanup;
+ buf = 0;
+ for (ext = (struct sadb_ext *) (msg + 1);
+ (u_int8_t *) ext - (u_int8_t *) msg <
+ msg->sadb_msg_len * PF_KEY_V2_CHUNK;
+ ext = (struct sadb_ext *) ((u_int8_t *) ext +
+ ext->sadb_ext_len * PF_KEY_V2_CHUNK))
+ pf_key_v2_msg_add(ret, ext, 0);
+
+ /*
+ * If the message is not the one we are waiting for, queue it
+ * up.
+ */
+ if (seq && (msg->sadb_msg_pid != (u_int32_t) getpid() ||
+ msg->sadb_msg_seq != seq)) {
+ gettimeofday(&tv, 0);
+ timer_add_event("pf_key_v2_notify",
+ (void (*) (void *)) pf_key_v2_notify, ret, &tv);
+ ret = 0;
+ continue;
+ }
+ return ret;
+ }
+
+cleanup:
+ if (buf)
+ free(buf);
+ if (ret)
+ pf_key_v2_msg_free(ret);
+ return 0;
+}
+
+/* Write the message in PMSG to the PF_KEY socket. */
+u_int32_t
+pf_key_v2_write(struct pf_key_v2_msg *pmsg)
+{
+ struct iovec *iov = 0;
+ ssize_t n;
+ size_t len;
+ int i, cnt = TAILQ_FIRST(pmsg)->cnt;
+ char header[80];
+ struct sadb_msg *msg = TAILQ_FIRST(pmsg)->seg;
+ struct pf_key_v2_node *np = TAILQ_FIRST(pmsg);
+
+ iov = (struct iovec *) malloc(cnt * sizeof *iov);
+ if (!iov) {
+ log_error("pf_key_v2_write: malloc (%lu) failed",
+ cnt * (unsigned long) sizeof *iov);
+ return 0;
+ }
+ msg->sadb_msg_version = PF_KEY_V2;
+ msg->sadb_msg_errno = 0;
+ msg->sadb_msg_reserved = 0;
+ msg->sadb_msg_pid = getpid();
+ if (!msg->sadb_msg_seq)
+ msg->sadb_msg_seq = pf_key_v2_seq();
+
+ /* Compute the iovec segments as well as the message length. */
+ len = 0;
+ for (i = 0; i < cnt; i++) {
+ iov[i].iov_base = np->seg;
+ len += iov[i].iov_len = np->sz;
+
+ /*
+ * XXX One can envision setting specific extension fields, like
+ * *_reserved ones here. For now we require them to be set by the
+ * caller.
+ */
+
+ np = TAILQ_NEXT(np, link);
+ }
+ msg->sadb_msg_len = len / PF_KEY_V2_CHUNK;
+
+ for (i = 0; i < cnt; i++) {
+ snprintf(header, sizeof header, "pf_key_v2_write: iov[%d]", i);
+ LOG_DBG_BUF((LOG_SYSDEP, 80, header,
+ (u_int8_t *) iov[i].iov_base, iov[i].iov_len));
+ }
+
+ n = writev(pf_key_v2_socket, iov, cnt);
+ if (n == -1) {
+ log_error("pf_key_v2_write: writev (%d, %p, %d) failed",
+ pf_key_v2_socket, iov, cnt);
+ goto cleanup;
+ }
+ if ((size_t) n != len) {
+ log_error("pf_key_v2_write: "
+ "writev (%d, ...) returned prematurely (%lu)",
+ pf_key_v2_socket, (unsigned long) n);
+ goto cleanup;
+ }
+ free(iov);
+ return msg->sadb_msg_seq;
+
+cleanup:
+ if (iov)
+ free(iov);
+ return 0;
+}
+
+/*
+ * Do a PF_KEY "call", i.e. write a message MSG, read the reply and return
+ * it to the caller.
+ */
+static struct pf_key_v2_msg *
+pf_key_v2_call(struct pf_key_v2_msg *msg)
+{
+ u_int32_t seq;
+
+ seq = pf_key_v2_write(msg);
+ if (!seq)
+ return 0;
+ return pf_key_v2_read(seq);
+}
+
+/* Find the TYPE extension in MSG. Return zero if none found. */
+static struct pf_key_v2_node *
+pf_key_v2_find_ext(struct pf_key_v2_msg *msg, u_int16_t type)
+{
+ struct pf_key_v2_node *ext;
+
+ for (ext = TAILQ_NEXT(TAILQ_FIRST(msg), link); ext;
+ ext = TAILQ_NEXT(ext, link))
+ if (ext->type == type)
+ return ext;
+ return 0;
+}
+
+/*
+ * Open the PF_KEYv2 sockets and return the descriptor used for notifies.
+ * Return -1 for failure and -2 if no notifies will show up.
+ */
+int
+pf_key_v2_open(void)
+{
+ int fd = -1, err;
+ struct sadb_msg msg;
+ struct pf_key_v2_msg *regmsg = 0, *ret = 0;
+
+ /* Open the socket we use to speak to IPsec. */
+ pf_key_v2_socket = -1;
+ fd = socket(PF_KEY, SOCK_RAW, PF_KEY_V2);
+ if (fd == -1) {
+ log_error("pf_key_v2_open: "
+ "socket (PF_KEY, SOCK_RAW, PF_KEY_V2) failed");
+ goto cleanup;
+ }
+ pf_key_v2_socket = fd;
+
+ /* Register it to get ESP and AH acquires from the kernel. */
+ msg.sadb_msg_seq = 0;
+ msg.sadb_msg_type = SADB_REGISTER;
+ msg.sadb_msg_satype = SADB_SATYPE_ESP;
+ regmsg = pf_key_v2_msg_new(&msg, 0);
+ if (!regmsg)
+ goto cleanup;
+ ret = pf_key_v2_call(regmsg);
+ pf_key_v2_msg_free(regmsg);
+ if (!ret)
+ goto cleanup;
+ err = ((struct sadb_msg *)TAILQ_FIRST(ret)->seg)->sadb_msg_errno;
+ if (err) {
+ log_print("pf_key_v2_open: REGISTER: %s", strerror(err));
+ goto cleanup;
+ }
+ /* XXX Register the accepted transforms. */
+
+ pf_key_v2_msg_free(ret);
+ ret = 0;
+
+ msg.sadb_msg_seq = 0;
+ msg.sadb_msg_type = SADB_REGISTER;
+ msg.sadb_msg_satype = SADB_SATYPE_AH;
+ regmsg = pf_key_v2_msg_new(&msg, 0);
+ if (!regmsg)
+ goto cleanup;
+ ret = pf_key_v2_call(regmsg);
+ pf_key_v2_msg_free(regmsg);
+ if (!ret)
+ goto cleanup;
+ err = ((struct sadb_msg *)TAILQ_FIRST(ret)->seg)->sadb_msg_errno;
+ if (err) {
+ log_print("pf_key_v2_open: REGISTER: %s", strerror(err));
+ goto cleanup;
+ }
+ /* XXX Register the accepted transforms. */
+
+ pf_key_v2_msg_free(ret);
+ ret = 0;
+
+#ifdef SADB_X_SATYPE_IPCOMP
+ msg.sadb_msg_seq = 0;
+ msg.sadb_msg_type = SADB_REGISTER;
+ msg.sadb_msg_satype = SADB_X_SATYPE_IPCOMP;
+ regmsg = pf_key_v2_msg_new(&msg, 0);
+ if (!regmsg)
+ goto cleanup;
+ ret = pf_key_v2_call(regmsg);
+ pf_key_v2_msg_free(regmsg);
+ if (!ret)
+ goto cleanup;
+ err = ((struct sadb_msg *)TAILQ_FIRST(ret)->seg)->sadb_msg_errno;
+ if (err) {
+ log_print("pf_key_v2_open: REGISTER: %s", strerror(err));
+ goto cleanup;
+ }
+ /* XXX Register the accepted transforms. */
+
+ pf_key_v2_msg_free(ret);
+#endif /* SADB_X_SATYPE_IPCOMP */
+
+#ifdef KAME
+ TAILQ_INIT(&pf_key_v2_sa_seq_map);
+#endif
+
+ return fd;
+
+cleanup:
+ if (pf_key_v2_socket != -1) {
+ close(pf_key_v2_socket);
+ pf_key_v2_socket = -1;
+ }
+ if (ret)
+ pf_key_v2_msg_free(ret);
+ return -1;
+}
+
+/*
+ * Generate a SPI for protocol PROTO and the source/destination pair given by
+ * SRC, SRCLEN, DST & DSTLEN. Stash the SPI size in SZ.
+ */
+u_int8_t *
+pf_key_v2_get_spi(size_t *sz, u_int8_t proto, struct sockaddr *src,
+ struct sockaddr *dst, u_int32_t seq)
+{
+ struct sadb_msg msg;
+ struct sadb_sa *sa;
+ struct sadb_address *addr = 0;
+ struct sadb_spirange spirange;
+ struct pf_key_v2_msg *getspi = 0, *ret = 0;
+ struct pf_key_v2_node *ext;
+ u_int8_t *spi = 0;
+ int len, err;
+#ifdef KAME
+ struct sadb_x_sa2 ssa2;
+#endif
+
+ msg.sadb_msg_type = SADB_GETSPI;
+ switch (proto) {
+ case IPSEC_PROTO_IPSEC_ESP:
+ msg.sadb_msg_satype = SADB_SATYPE_ESP;
+ break;
+ case IPSEC_PROTO_IPSEC_AH:
+ msg.sadb_msg_satype = SADB_SATYPE_AH;
+ break;
+#ifdef SADB_X_SATYPE_IPCOMP
+ case IPSEC_PROTO_IPCOMP:
+ msg.sadb_msg_satype = SADB_X_SATYPE_IPCOMP;
+ break;
+#endif
+ default:
+ log_print("pf_key_v2_get_spi: invalid proto %d", proto);
+ goto cleanup;
+ }
+
+ /* Set the sequence number from the ACQUIRE message. */
+ msg.sadb_msg_seq = seq;
+ getspi = pf_key_v2_msg_new(&msg, 0);
+ if (!getspi)
+ goto cleanup;
+
+#ifdef KAME
+ memset(&ssa2, 0, sizeof ssa2);
+ ssa2.sadb_x_sa2_exttype = SADB_X_EXT_SA2;
+ ssa2.sadb_x_sa2_len = sizeof ssa2 / PF_KEY_V2_CHUNK;
+ ssa2.sadb_x_sa2_mode = 0;
+ if (pf_key_v2_msg_add(getspi, (struct sadb_ext *)&ssa2, 0) == -1)
+ goto cleanup;
+#endif
+
+ /* Setup the ADDRESS extensions. */
+ len =
+ sizeof(struct sadb_address) + PF_KEY_V2_ROUND(sysdep_sa_len(src));
+ addr = calloc(1, len);
+ if (!addr)
+ goto cleanup;
+ addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+ addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
+#ifndef __OpenBSD__
+ addr->sadb_address_proto = 0;
+ addr->sadb_address_prefixlen = 0;
+#endif
+ addr->sadb_address_reserved = 0;
+ memcpy(addr + 1, src, sysdep_sa_len(src));
+ switch (((struct sockaddr *) (addr + 1))->sa_family) {
+ case AF_INET:
+ ((struct sockaddr_in *) (addr + 1))->sin_port = 0;
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *) (addr + 1))->sin6_port = 0;
+ break;
+ }
+ if (pf_key_v2_msg_add(getspi, (struct sadb_ext *) addr,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ addr = 0;
+
+ len =
+ sizeof(struct sadb_address) + PF_KEY_V2_ROUND(sysdep_sa_len(dst));
+ addr = calloc(1, len);
+ if (!addr)
+ goto cleanup;
+ addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+ addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
+#ifndef __OpenBSD__
+ addr->sadb_address_proto = 0;
+ addr->sadb_address_prefixlen = 0;
+#endif
+ addr->sadb_address_reserved = 0;
+ memcpy(addr + 1, dst, sysdep_sa_len(dst));
+ switch (((struct sockaddr *) (addr + 1))->sa_family) {
+ case AF_INET:
+ ((struct sockaddr_in *) (addr + 1))->sin_port = 0;
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *) (addr + 1))->sin6_port = 0;
+ break;
+ }
+ if (pf_key_v2_msg_add(getspi, (struct sadb_ext *) addr,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ addr = 0;
+
+ /* Setup the SPIRANGE extension. */
+ spirange.sadb_spirange_exttype = SADB_EXT_SPIRANGE;
+ spirange.sadb_spirange_len = sizeof spirange / PF_KEY_V2_CHUNK;
+ if (proto == IPSEC_PROTO_IPCOMP) {
+ spirange.sadb_spirange_min = CPI_RESERVED_MAX + 1;
+ spirange.sadb_spirange_max = CPI_PRIVATE_MIN - 1;
+ } else {
+ spirange.sadb_spirange_min = IPSEC_SPI_LOW;
+ spirange.sadb_spirange_max = 0xffffffff;
+ }
+ spirange.sadb_spirange_reserved = 0;
+ if (pf_key_v2_msg_add(getspi, (struct sadb_ext *)&spirange, 0) == -1)
+ goto cleanup;
+
+ ret = pf_key_v2_call(getspi);
+ pf_key_v2_msg_free(getspi);
+ getspi = 0;
+ if (!ret)
+ goto cleanup;
+ err = ((struct sadb_msg *)TAILQ_FIRST(ret)->seg)->sadb_msg_errno;
+ if (err) {
+ log_print("pf_key_v2_get_spi: GETSPI: %s", strerror(err));
+ goto cleanup;
+ }
+ ext = pf_key_v2_find_ext(ret, SADB_EXT_SA);
+ if (!ext) {
+ log_print("pf_key_v2_get_spi: no SA extension found");
+ goto cleanup;
+ }
+ sa = ext->seg;
+
+ /* IPCOMP CPIs are only 16 bits long. */
+ *sz = (proto == IPSEC_PROTO_IPCOMP) ? sizeof(u_int16_t)
+ : sizeof sa->sadb_sa_spi;
+ spi = malloc(*sz);
+ if (!spi)
+ goto cleanup;
+ /* XXX This is ugly. */
+ if (proto == IPSEC_PROTO_IPCOMP) {
+ u_int32_t tspi = ntohl(sa->sadb_sa_spi);
+ *(u_int16_t *) spi = htons((u_int16_t) tspi);
+ } else
+ memcpy(spi, &sa->sadb_sa_spi, *sz);
+
+#ifdef KAME
+ if (!pf_key_v2_register_sa_seq(spi, *sz, proto, dst,
+ sysdep_sa_len(dst),
+ ((struct sadb_msg *) (TAILQ_FIRST(ret)->seg))->sadb_msg_seq))
+ goto cleanup;
+#endif
+ pf_key_v2_msg_free(ret);
+
+ LOG_DBG_BUF((LOG_SYSDEP, 50, "pf_key_v2_get_spi: spi", spi, *sz));
+ return spi;
+
+cleanup:
+ if (spi)
+ free(spi);
+ if (addr)
+ free(addr);
+ if (getspi)
+ pf_key_v2_msg_free(getspi);
+ if (ret)
+ pf_key_v2_msg_free(ret);
+ return 0;
+}
+
+/* Fetch SA information from the kernel. XXX OpenBSD only? */
+struct sa_kinfo *
+pf_key_v2_get_kernel_sa(u_int8_t *spi, size_t spi_sz, u_int8_t proto,
+ struct sockaddr *dst)
+{
+ struct sadb_msg msg;
+ struct sadb_sa *ssa;
+ struct sadb_address *addr = 0;
+ struct sockaddr *sa;
+ struct sadb_lifetime *life;
+ struct pf_key_v2_msg *gettdb = 0, *ret = 0;
+ struct pf_key_v2_node *ext;
+ static struct sa_kinfo ksa;
+#if defined (SADB_X_EXT_UDPENCAP)
+ struct sadb_x_udpencap *udpencap;
+#endif
+ int len, err;
+
+ if (spi_sz != sizeof (ssa->sadb_sa_spi))
+ return 0;
+
+ msg.sadb_msg_type = SADB_GET;
+ switch (proto) {
+ case IPSEC_PROTO_IPSEC_ESP:
+ msg.sadb_msg_satype = SADB_SATYPE_ESP;
+ break;
+ case IPSEC_PROTO_IPSEC_AH:
+ msg.sadb_msg_satype = SADB_SATYPE_AH;
+ break;
+#ifdef SADB_X_SATYPE_IPCOMP
+ case IPSEC_PROTO_IPCOMP:
+ msg.sadb_msg_satype = SADB_X_SATYPE_IPCOMP;
+ break;
+#endif
+ default:
+ log_print("pf_key_v2_get_kernel_sa: invalid proto %d", proto);
+ goto cleanup;
+ }
+
+ gettdb = pf_key_v2_msg_new(&msg, 0);
+ if (!gettdb)
+ goto cleanup;
+
+ /* SPI */
+ ssa = (struct sadb_sa *)calloc(1, sizeof *ssa);
+ if (!ssa) {
+ log_print("pf_key_v2_get_kernel_sa: calloc(1, %lu) failed",
+ (unsigned long)sizeof *ssa);
+ goto cleanup;
+ }
+
+ ssa->sadb_sa_exttype = SADB_EXT_SA;
+ ssa->sadb_sa_len = sizeof *ssa / PF_KEY_V2_CHUNK;
+ memcpy(&ssa->sadb_sa_spi, spi, sizeof ssa->sadb_sa_spi);
+ ssa->sadb_sa_state = SADB_SASTATE_MATURE;
+ if (pf_key_v2_msg_add(gettdb, (struct sadb_ext *)ssa,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ ssa = 0;
+
+ /* XXX KAME SADB_X_EXT_xyz here? */
+
+ /* Address */
+ len =
+ sizeof(struct sadb_address) + PF_KEY_V2_ROUND(sysdep_sa_len(dst));
+ addr = calloc(1, len);
+ if (!addr)
+ goto cleanup;
+ addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+ addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
+#ifndef __OpenBSD__
+ addr->sadb_address_proto = 0;
+ addr->sadb_address_prefixlen = 0;
+#endif
+ addr->sadb_address_reserved = 0;
+ memcpy(addr + 1, dst, sysdep_sa_len(dst));
+ switch (((struct sockaddr *) (addr + 1))->sa_family) {
+ case AF_INET:
+ ((struct sockaddr_in *) (addr + 1))->sin_port = 0;
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *) (addr + 1))->sin6_port = 0;
+ break;
+ }
+ if (pf_key_v2_msg_add(gettdb, (struct sadb_ext *)addr,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ addr = 0;
+
+ ret = pf_key_v2_call(gettdb);
+ pf_key_v2_msg_free(gettdb);
+ gettdb = 0;
+ if (!ret)
+ goto cleanup;
+ err = ((struct sadb_msg *)TAILQ_FIRST(ret)->seg)->sadb_msg_errno;
+ if (err) {
+ log_print("pf_key_v2_get_kernel_sa: SADB_GET: %s",
+ strerror(err));
+ goto cleanup;
+ }
+
+ /* Extract the data. */
+ memset(&ksa, 0, sizeof ksa);
+
+ ext = pf_key_v2_find_ext(ret, SADB_EXT_SA);
+ if (!ext)
+ goto cleanup;
+
+ ssa = (struct sadb_sa *)ext;
+ ksa.spi = ssa->sadb_sa_spi;
+ ksa.wnd = ssa->sadb_sa_replay;
+ ksa.flags = ssa->sadb_sa_flags;
+
+ ext = pf_key_v2_find_ext(ret, SADB_EXT_LIFETIME_CURRENT);
+ if (ext) {
+ life = (struct sadb_lifetime *)ext->seg;
+ ksa.cur_allocations = life->sadb_lifetime_allocations;
+ ksa.cur_bytes = life->sadb_lifetime_bytes;
+ ksa.first_use = life->sadb_lifetime_usetime;
+ ksa.established = life->sadb_lifetime_addtime;
+ }
+
+ ext = pf_key_v2_find_ext(ret, SADB_EXT_LIFETIME_SOFT);
+ if (ext) {
+ life = (struct sadb_lifetime *)ext->seg;
+ ksa.soft_allocations = life->sadb_lifetime_allocations;
+ ksa.soft_bytes = life->sadb_lifetime_bytes;
+ ksa.soft_timeout = life->sadb_lifetime_addtime;
+ ksa.soft_first_use = life->sadb_lifetime_usetime;
+ }
+
+ ext = pf_key_v2_find_ext(ret, SADB_EXT_LIFETIME_HARD);
+ if (ext) {
+ life = (struct sadb_lifetime *)ext->seg;
+ ksa.exp_allocations = life->sadb_lifetime_allocations;
+ ksa.exp_bytes = life->sadb_lifetime_bytes;
+ ksa.exp_timeout = life->sadb_lifetime_addtime;
+ ksa.exp_first_use = life->sadb_lifetime_usetime;
+ }
+
+#if defined (SADB_X_EXT_LIFETIME_LASTUSE)
+ ext = pf_key_v2_find_ext(ret, SADB_X_EXT_LIFETIME_LASTUSE);
+ if (ext) {
+ life = (struct sadb_lifetime *)ext->seg;
+ ksa.last_used = life->sadb_lifetime_usetime;
+ }
+#endif
+
+ ext = pf_key_v2_find_ext(ret, SADB_EXT_ADDRESS_SRC);
+ if (ext) {
+ sa = (struct sockaddr *)ext->seg;
+ memcpy(&ksa.src, sa,
+ sa->sa_family == AF_INET ? sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6));
+ }
+
+ ext = pf_key_v2_find_ext(ret, SADB_EXT_ADDRESS_DST);
+ if (ext) {
+ sa = (struct sockaddr *)ext->seg;
+ memcpy(&ksa.dst, sa,
+ sa->sa_family == AF_INET ? sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6));
+ }
+
+ ext = pf_key_v2_find_ext(ret, SADB_EXT_ADDRESS_PROXY);
+ if (ext) {
+ sa = (struct sockaddr *)ext->seg;
+ memcpy(sa, &ksa.proxy,
+ sa->sa_family == AF_INET ? sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6));
+ }
+
+#if defined (SADB_X_EXT_UDPENCAP)
+ ext = pf_key_v2_find_ext(ret, SADB_X_EXT_UDPENCAP);
+ if (ext) {
+ udpencap = (struct sadb_x_udpencap *)ext->seg;
+ ksa.udpencap_port = udpencap->sadb_x_udpencap_port;
+ }
+#endif
+
+ pf_key_v2_msg_free(ret);
+
+ LOG_DBG_BUF((LOG_SYSDEP, 50, "pf_key_v2_get_kernel_sa: spi", spi,
+ spi_sz));
+
+ return &ksa;
+
+ cleanup:
+ if (addr)
+ free (addr);
+ if (gettdb)
+ pf_key_v2_msg_free(gettdb);
+ if (ret)
+ pf_key_v2_msg_free(ret);
+ return 0;
+}
+
+static void
+pf_key_v2_setup_sockaddr(void *res, struct sockaddr *src,
+ struct sockaddr *dst, in_port_t port, int ingress)
+{
+ struct sockaddr_in *ip4_sa;
+ struct sockaddr_in6 *ip6_sa;
+ u_int8_t *p;
+
+ switch (src->sa_family) {
+ case AF_INET:
+ ip4_sa = (struct sockaddr_in *) res;
+ ip4_sa->sin_family = AF_INET;
+#ifndef USE_OLD_SOCKADDR
+ ip4_sa->sin_len = sizeof *ip4_sa;
+#endif
+ ip4_sa->sin_port = port;
+ if (dst)
+ p = (u_int8_t *) (ingress ?
+ &((struct sockaddr_in *)src)->sin_addr.s_addr :
+ &((struct sockaddr_in *)dst)->sin_addr.s_addr);
+ else
+ p = (u_int8_t *)&((struct sockaddr_in *)src)->sin_addr.s_addr;
+ ip4_sa->sin_addr.s_addr = *((in_addr_t *) p);
+ break;
+
+ case AF_INET6:
+ ip6_sa = (struct sockaddr_in6 *) res;
+ ip6_sa->sin6_family = AF_INET6;
+#ifndef USE_OLD_SOCKADDR
+ ip6_sa->sin6_len = sizeof *ip6_sa;
+#endif
+ ip6_sa->sin6_port = port;
+ if (dst)
+ p = (u_int8_t *) (ingress ?
+ &((struct sockaddr_in6 *)src)->sin6_addr.s6_addr :
+ &((struct sockaddr_in6 *)dst)->sin6_addr.s6_addr);
+ else
+ p = (u_int8_t *)&((struct sockaddr_in6 *)src)->sin6_addr.s6_addr;
+ memcpy(ip6_sa->sin6_addr.s6_addr, p, sizeof(struct in6_addr));
+ break;
+
+ default:
+ log_print("pf_key_v2_setup_sockaddr: unknown family %d\n",
+ src->sa_family);
+ break;
+ }
+}
+
+/*
+ * Store/update a PF_KEY_V2 security association with full information from the
+ * IKE SA and PROTO into the kernel. INCOMING is set if we are setting the
+ * parameters for the incoming SA, and cleared otherwise.
+ */
+int
+pf_key_v2_set_spi(struct sa *sa, struct proto *proto, int incoming,
+ struct sa *isakmp_sa)
+{
+ struct sadb_msg msg;
+ struct sadb_sa ssa;
+ struct sadb_lifetime *life = 0;
+ struct sadb_address *addr = 0;
+ struct sadb_key *key = 0;
+ struct sadb_ident *sid = 0;
+ struct sockaddr *src, *dst;
+ struct pf_key_v2_msg *update = 0, *ret = 0;
+ struct ipsec_proto *iproto = proto->data;
+ size_t len;
+ int keylen, hashlen, err;
+#ifndef KAME
+ u_int8_t *pp;
+ int idtype;
+#else /* KAME */
+ struct sadb_x_sa2 ssa2;
+#endif
+#if defined (SADB_X_CREDTYPE_NONE) || defined (SADB_X_AUTHTYPE_NONE)
+ struct ipsec_sa *isa = sa->data;
+ struct sadb_x_cred *cred;
+ struct sadb_protocol flowtype, tprotocol;
+#endif
+#if defined (USE_NAT_TRAVERSAL) && defined (SADB_X_EXT_UDPENCAP)
+ struct sadb_x_udpencap udpencap;
+#elif defined (USE_NAT_TRAVERSAL) && defined (SADB_X_EXT_NAT_T_TYPE)
+ struct sadb_x_nat_t_type nat_t_type;
+ struct sadb_x_nat_t_port nat_t_sport;
+ struct sadb_x_nat_t_port nat_t_dport;
+#endif
+#ifdef USE_DEBUG
+ char *addr_str;
+#endif
+
+ msg.sadb_msg_type = incoming ? SADB_UPDATE : SADB_ADD;
+ switch (proto->proto) {
+ case IPSEC_PROTO_IPSEC_ESP:
+ msg.sadb_msg_satype = SADB_SATYPE_ESP;
+ keylen = ipsec_esp_enckeylength(proto);
+ hashlen = ipsec_esp_authkeylength(proto);
+
+ switch (proto->id) {
+ case IPSEC_ESP_DES:
+ case IPSEC_ESP_DES_IV32:
+ case IPSEC_ESP_DES_IV64:
+ ssa.sadb_sa_encrypt = SADB_EALG_DESCBC;
+ break;
+
+ case IPSEC_ESP_3DES:
+ ssa.sadb_sa_encrypt = SADB_EALG_3DESCBC;
+ break;
+
+#ifdef SADB_X_EALG_AES
+ case IPSEC_ESP_AES:
+ /* case IPSEC_ESP_AES_128_CTR: */
+ ssa.sadb_sa_encrypt = SADB_X_EALG_AES;
+ break;
+#endif
+
+#ifdef SADB_X_EALG_CAST
+ case IPSEC_ESP_CAST:
+ ssa.sadb_sa_encrypt = SADB_X_EALG_CAST;
+ break;
+#endif
+
+#ifdef SADB_X_EALG_BLF
+ case IPSEC_ESP_BLOWFISH:
+ ssa.sadb_sa_encrypt = SADB_X_EALG_BLF;
+ break;
+#endif
+
+ default:
+ LOG_DBG((LOG_SYSDEP, 50, "pf_key_v2_set_spi: "
+ "unknown encryption algorithm %d", proto->id));
+ return -1;
+ }
+
+ switch (iproto->auth) {
+ case IPSEC_AUTH_HMAC_MD5:
+#ifdef SADB_AALG_MD5HMAC96
+ ssa.sadb_sa_auth = SADB_AALG_MD5HMAC96;
+#else
+ ssa.sadb_sa_auth = SADB_AALG_MD5HMAC;
+#endif
+ break;
+
+ case IPSEC_AUTH_HMAC_SHA:
+#ifdef SADB_AALG_SHA1HMAC96
+ ssa.sadb_sa_auth = SADB_AALG_SHA1HMAC96;
+#else
+ ssa.sadb_sa_auth = SADB_AALG_SHA1HMAC;
+#endif
+ break;
+
+#ifndef KAME
+ case IPSEC_AUTH_HMAC_RIPEMD:
+#ifdef SADB_X_AALG_RIPEMD160HMAC96
+ ssa.sadb_sa_auth = SADB_X_AALG_RIPEMD160HMAC96;
+#elif defined (SADB_X_AALG_RIPEMD160HMAC)
+ ssa.sadb_sa_auth = SADB_X_AALG_RIPEMD160HMAC;
+#elif defined (SADB_X_AALG_RIPEMD160)
+ ssa.sadb_sa_auth = SADB_X_AALG_RIPEMD160;
+#else
+ ssa.sadb_sa_auth = SADB_AALG_RIPEMD160HMAC;
+#endif
+ break;
+#endif
+
+#ifdef SADB_X_AALG_SHA2_256
+ case IPSEC_AUTH_HMAC_SHA2_256:
+ ssa.sadb_sa_auth = SADB_X_AALG_SHA2_256;
+ break;
+#endif
+
+#ifdef SADB_X_AALG_SHA2_384
+ case IPSEC_AUTH_HMAC_SHA2_384:
+ ssa.sadb_sa_auth = SADB_X_AALG_SHA2_384;
+ break;
+#endif
+
+#ifdef SADB_X_AALG_SHA2_512
+ case IPSEC_AUTH_HMAC_SHA2_512:
+ ssa.sadb_sa_auth = SADB_X_AALG_SHA2_512;
+ break;
+#endif
+
+ case IPSEC_AUTH_DES_MAC:
+ case IPSEC_AUTH_KPDK:
+ /* XXX We should be supporting KPDK */
+ LOG_DBG((LOG_SYSDEP, 50, "pf_key_v2_set_spi: "
+ "unknown authentication algorithm %d",
+ iproto->auth));
+ return -1;
+
+ default:
+ ssa.sadb_sa_auth = SADB_AALG_NONE;
+ }
+ break;
+
+ case IPSEC_PROTO_IPSEC_AH:
+ msg.sadb_msg_satype = SADB_SATYPE_AH;
+ hashlen = ipsec_ah_keylength(proto);
+ keylen = 0;
+
+ ssa.sadb_sa_encrypt = SADB_EALG_NONE;
+ switch (proto->id) {
+ case IPSEC_AH_MD5:
+#ifdef SADB_AALG_MD5HMAC96
+ ssa.sadb_sa_auth = SADB_AALG_MD5HMAC96;
+#else
+ ssa.sadb_sa_auth = SADB_AALG_MD5HMAC;
+#endif
+ break;
+
+ case IPSEC_AH_SHA:
+#ifdef SADB_AALG_SHA1HMAC96
+ ssa.sadb_sa_auth = SADB_AALG_SHA1HMAC96;
+#else
+ ssa.sadb_sa_auth = SADB_AALG_SHA1HMAC;
+#endif
+ break;
+
+#ifndef KAME
+ case IPSEC_AH_RIPEMD:
+#ifdef SADB_X_AALG_RIPEMD160HMAC96
+ ssa.sadb_sa_auth = SADB_X_AALG_RIPEMD160HMAC96;
+#elif defined (SADB_X_AALG_RIPEMD160HMAC)
+ ssa.sadb_sa_auth = SADB_X_AALG_RIPEMD160HMAC;
+#elif defined (SADB_X_AALG_RIPEMD160)
+ ssa.sadb_sa_auth = SADB_X_AALG_RIPEMD160;
+#else
+ ssa.sadb_sa_auth = SADB_AALG_RIPEMD160HMAC;
+#endif
+ break;
+#endif
+
+#ifdef SADB_X_AALG_SHA2_256
+ case IPSEC_AH_SHA2_256:
+ ssa.sadb_sa_auth = SADB_X_AALG_SHA2_256;
+ break;
+#endif
+
+#ifdef SADB_X_AALG_SHA2_384
+ case IPSEC_AH_SHA2_384:
+ ssa.sadb_sa_auth = SADB_X_AALG_SHA2_384;
+ break;
+#endif
+
+#ifdef SADB_X_AALG_SHA2_512
+ case IPSEC_AH_SHA2_512:
+ ssa.sadb_sa_auth = SADB_X_AALG_SHA2_512;
+ break;
+#endif
+
+ default:
+ LOG_DBG((LOG_SYSDEP, 50, "pf_key_v2_set_spi: "
+ "unknown authentication algorithm %d", proto->id));
+ goto cleanup;
+ }
+ break;
+
+#ifdef SADB_X_SATYPE_IPCOMP
+ case IPSEC_PROTO_IPCOMP:
+ msg.sadb_msg_satype = SADB_X_SATYPE_IPCOMP;
+ ssa.sadb_sa_auth = SADB_AALG_NONE;
+ keylen = 0;
+ hashlen = 0;
+
+ /*
+ * Put compression algorithm type in the sadb_sa_encrypt
+ * field.
+ */
+ switch (proto->id) {
+#ifdef SADB_X_CALG_OUI
+ case IPSEC_IPCOMP_OUI:
+ ssa.sadb_sa_encrypt = SADB_X_CALG_OUI;
+ break;
+#endif
+
+#ifdef SADB_X_CALG_DEFLATE
+ case IPSEC_IPCOMP_DEFLATE:
+ ssa.sadb_sa_encrypt = SADB_X_CALG_DEFLATE;
+ break;
+#endif
+
+#ifdef SADB_X_CALG_LZS
+ case IPSEC_IPCOMP_LZS:
+ ssa.sadb_sa_encrypt = SADB_X_CALG_LZS;
+ break;
+#endif
+
+#ifdef SADB_X_CALG_V42BIS
+ case IPSEC_IPCOMP_V42BIS:
+ ssa.sadb_sa_encrypt = SADB_X_CALG_V42BIS;
+ break;
+#endif
+
+ default:
+ break;
+ }
+ break;
+#endif /* SADB_X_SATYPE_IPCOMP */
+
+ default:
+ log_print("pf_key_v2_set_spi: invalid proto %d", proto->proto);
+ goto cleanup;
+ }
+ if (incoming) {
+ sa->transport->vtbl->get_src(sa->transport, &dst);
+ sa->transport->vtbl->get_dst(sa->transport, &src);
+ }
+ else {
+ sa->transport->vtbl->get_dst(sa->transport, &dst);
+ sa->transport->vtbl->get_src(sa->transport, &src);
+ }
+
+#ifdef KAME
+ msg.sadb_msg_seq = (incoming ?
+ pf_key_v2_seq_by_sa(proto->spi[incoming], sizeof ssa.sadb_sa_spi,
+ proto->proto, dst, sysdep_sa_len(dst)) : 0);
+#else
+ msg.sadb_msg_seq = sa->seq;
+#endif
+ update = pf_key_v2_msg_new(&msg, 0);
+ if (!update)
+ goto cleanup;
+
+#ifdef KAME
+ memset(&ssa2, 0, sizeof ssa2);
+ ssa2.sadb_x_sa2_exttype = SADB_X_EXT_SA2;
+ ssa2.sadb_x_sa2_len = sizeof ssa2 / PF_KEY_V2_CHUNK;
+#if defined (LINUX_IPSEC)
+ if (iproto->encap_mode == IPSEC_ENCAP_TUNNEL)
+ ssa2.sadb_x_sa2_mode = IPSEC_MODE_TUNNEL;
+ else
+ ssa2.sadb_x_sa2_mode = IPSEC_MODE_TRANSPORT;
+#else
+ ssa2.sadb_x_sa2_mode = 0;
+#endif
+ if (pf_key_v2_msg_add(update, (struct sadb_ext *)&ssa2, 0) == -1)
+ goto cleanup;
+#endif
+
+ /* Setup the rest of the SA extension. */
+ ssa.sadb_sa_exttype = SADB_EXT_SA;
+ ssa.sadb_sa_len = sizeof ssa / PF_KEY_V2_CHUNK;
+ if (proto->spi_sz[incoming] == 2) /* IPCOMP uses 16bit CPIs. */
+ ssa.sadb_sa_spi = htonl(proto->spi[incoming][0] << 8 |
+ proto->spi[incoming][1]);
+ else
+ memcpy(&ssa.sadb_sa_spi, proto->spi[incoming],
+ sizeof ssa.sadb_sa_spi);
+ ssa.sadb_sa_replay = conf_get_str("General", "Shared-SADB") ? 0 :
+ iproto->replay_window;
+ ssa.sadb_sa_state = SADB_SASTATE_MATURE;
+ ssa.sadb_sa_flags = 0;
+#ifdef SADB_X_SAFLAGS_TUNNEL
+ if (iproto->encap_mode == IPSEC_ENCAP_TUNNEL ||
+ iproto->encap_mode == IPSEC_ENCAP_UDP_ENCAP_TUNNEL ||
+ iproto->encap_mode == IPSEC_ENCAP_UDP_ENCAP_TUNNEL_DRAFT)
+ ssa.sadb_sa_flags = SADB_X_SAFLAGS_TUNNEL;
+#endif
+
+ if (isakmp_sa->flags & SA_FLAG_NAT_T_ENABLE) {
+#if defined (USE_NAT_TRAVERSAL) && defined (SADB_X_EXT_UDPENCAP)
+ memset(&udpencap, 0, sizeof udpencap);
+ ssa.sadb_sa_flags |= SADB_X_SAFLAGS_UDPENCAP;
+ udpencap.sadb_x_udpencap_exttype = SADB_X_EXT_UDPENCAP;
+ udpencap.sadb_x_udpencap_len =
+ sizeof udpencap / PF_KEY_V2_CHUNK;
+ udpencap.sadb_x_udpencap_port = sockaddr_port(dst);
+ if (pf_key_v2_msg_add(update, (struct sadb_ext *)&udpencap, 0)
+ == -1)
+ goto cleanup;
+#elif defined (USE_NAT_TRAVERSAL) && defined (SADB_X_EXT_NAT_T_TYPE)
+#ifndef UDP_ENCAP_ESPINUDP
+#define UDP_ENCAP_ESPINUDP 2
+#endif
+ memset(&nat_t_type, 0, sizeof nat_t_type);
+ memset(&nat_t_sport, 0, sizeof nat_t_sport);
+ memset(&nat_t_dport, 0, sizeof nat_t_dport);
+
+ /* type = draft-udp-encap-06 */
+ nat_t_type.sadb_x_nat_t_type_len = sizeof nat_t_type / PF_KEY_V2_CHUNK;
+ nat_t_type.sadb_x_nat_t_type_exttype = SADB_X_EXT_NAT_T_TYPE;
+ nat_t_type.sadb_x_nat_t_type_type = UDP_ENCAP_ESPINUDP;
+ if(pf_key_v2_msg_add(update, (struct sadb_ext *)&nat_t_type, 0) == -1)
+ goto cleanup;
+
+ /* source port */
+ nat_t_sport.sadb_x_nat_t_port_len = sizeof nat_t_sport /
+ PF_KEY_V2_CHUNK;
+ nat_t_sport.sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_SPORT;
+ nat_t_sport.sadb_x_nat_t_port_port = sockaddr_port(src);
+ if(pf_key_v2_msg_add(update, (struct sadb_ext *)&nat_t_sport, 0) == -1)
+ goto cleanup;
+
+ /* destination port */
+ nat_t_dport.sadb_x_nat_t_port_len = sizeof nat_t_dport /
+ PF_KEY_V2_CHUNK;
+ nat_t_dport.sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_DPORT;
+ nat_t_dport.sadb_x_nat_t_port_port = sockaddr_port(dst);
+ if(pf_key_v2_msg_add(update, (struct sadb_ext *)&nat_t_dport, 0) == -1)
+ goto cleanup;
+
+ /* original address (transport mode checksum missing info) goes here */
+#endif
+ }
+
+ if (pf_key_v2_msg_add(update, (struct sadb_ext *)&ssa, 0) == -1)
+ goto cleanup;
+
+ if (sa->seconds || sa->kilobytes) {
+ /* Setup the hard limits. */
+ life = malloc(sizeof *life);
+ if (!life)
+ goto cleanup;
+ life->sadb_lifetime_len = sizeof *life / PF_KEY_V2_CHUNK;
+ life->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
+ life->sadb_lifetime_allocations = 0;
+ life->sadb_lifetime_bytes = sa->kilobytes * 1024;
+ /*
+ * XXX I am not sure which one is best in security respect.
+ * Maybe the RFCs actually mandate what a lifetime really is.
+ */
+#if 0
+ life->sadb_lifetime_addtime = 0;
+ life->sadb_lifetime_usetime = sa->seconds;
+#else
+ life->sadb_lifetime_addtime = sa->seconds;
+ life->sadb_lifetime_usetime = 0;
+#endif
+ if (pf_key_v2_msg_add(update, (struct sadb_ext *) life,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ life = 0;
+
+ /*
+ * Setup the soft limits, we use 90 % of the hard ones.
+ * XXX A configurable ratio would be better.
+ */
+ life = malloc(sizeof *life);
+ if (!life)
+ goto cleanup;
+ life->sadb_lifetime_len = sizeof *life / PF_KEY_V2_CHUNK;
+ life->sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT;
+ life->sadb_lifetime_allocations = 0;
+ life->sadb_lifetime_bytes = sa->kilobytes * 1024 * 9 / 10;
+ /*
+ * XXX I am not sure which one is best in security respect.
+ * Maybe the RFCs actually mandate what a lifetime really is.
+ */
+#if 0
+ life->sadb_lifetime_addtime = 0;
+ life->sadb_lifetime_usetime = sa->seconds * 9 / 10;
+#else
+ life->sadb_lifetime_addtime = sa->seconds * 9 / 10;
+ life->sadb_lifetime_usetime = 0;
+#endif
+ if (pf_key_v2_msg_add(update, (struct sadb_ext *) life,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ life = 0;
+ }
+ /*
+ * Setup the ADDRESS extensions.
+ */
+ len = sizeof *addr + PF_KEY_V2_ROUND(sysdep_sa_len(src));
+ addr = calloc(1, len);
+ if (!addr)
+ goto cleanup;
+ addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+ addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
+#ifndef __OpenBSD__
+ addr->sadb_address_proto = 0;
+ addr->sadb_address_prefixlen = 0;
+#endif
+ addr->sadb_address_reserved = 0;
+ memcpy(addr + 1, src, sysdep_sa_len(src));
+ switch (((struct sockaddr *) (addr + 1))->sa_family) {
+ case AF_INET:
+ ((struct sockaddr_in *) (addr + 1))->sin_port = 0;
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *) (addr + 1))->sin6_port = 0;
+ break;
+ }
+ if (pf_key_v2_msg_add(update, (struct sadb_ext *) addr,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ addr = 0;
+
+ len = sizeof *addr + PF_KEY_V2_ROUND(sysdep_sa_len(dst));
+ addr = calloc(1, len);
+ if (!addr)
+ goto cleanup;
+ addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+ addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
+#ifndef __OpenBSD__
+ addr->sadb_address_proto = 0;
+ addr->sadb_address_prefixlen = 0;
+#endif
+ addr->sadb_address_reserved = 0;
+ memcpy(addr + 1, dst, sysdep_sa_len(dst));
+ switch (((struct sockaddr *) (addr + 1))->sa_family) {
+ case AF_INET:
+ ((struct sockaddr_in *) (addr + 1))->sin_port = 0;
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *) (addr + 1))->sin6_port = 0;
+ break;
+ }
+ if (pf_key_v2_msg_add(update, (struct sadb_ext *) addr,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ addr = 0;
+
+#if 0
+ /* XXX I am not sure about what to do here just yet. */
+ if (iproto->encap_mode == IPSEC_ENCAP_TUNNEL) {
+ len = sizeof *addr + PF_KEY_V2_ROUND(sysdep_sa_len(dst));
+ addr = calloc(1, len);
+ if (!addr)
+ goto cleanup;
+ addr->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
+ addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
+#ifndef __OpenBSD__
+ addr->sadb_address_proto = 0;
+ addr->sadb_address_prefixlen = 0;
+#endif
+ addr->sadb_address_reserved = 0;
+ memcpy(addr + 1, dst, sysdep_sa_len(dst));
+ switch (((struct sockaddr *) (addr + 1))->sa_family) {
+ case AF_INET:
+ ((struct sockaddr_in *) (addr + 1))->sin_port = 0;
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *) (addr + 1))->sin6_port = 0;
+ break;
+ }
+ if (pf_key_v2_msg_add(update, (struct sadb_ext *) addr,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ addr = 0;
+#if 0
+ msg->em_odst = msg->em_dst;
+ msg->em_osrc = msg->em_src;
+#endif
+ }
+#endif
+
+ if (proto->proto != IPSEC_PROTO_IPCOMP) {
+ /* Setup the KEY extensions. */
+ if (hashlen) {
+ len = sizeof *key + PF_KEY_V2_ROUND(hashlen);
+ key = malloc(len);
+ if (!key)
+ goto cleanup;
+ key->sadb_key_exttype = SADB_EXT_KEY_AUTH;
+ key->sadb_key_len = len / PF_KEY_V2_CHUNK;
+ key->sadb_key_bits = hashlen * 8;
+ key->sadb_key_reserved = 0;
+ memcpy(key + 1,
+ iproto->keymat[incoming] +
+ (proto->proto ==
+ IPSEC_PROTO_IPSEC_ESP ? keylen : 0),
+ hashlen);
+ if (pf_key_v2_msg_add(update, (struct sadb_ext *) key,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ key = 0;
+ }
+ if (keylen) {
+ len = sizeof *key + PF_KEY_V2_ROUND(keylen);
+ key = malloc(len);
+ if (!key)
+ goto cleanup;
+ key->sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
+ key->sadb_key_len = len / PF_KEY_V2_CHUNK;
+ key->sadb_key_bits = keylen * 8;
+ key->sadb_key_reserved = 0;
+ memcpy(key + 1, iproto->keymat[incoming], keylen);
+ if (pf_key_v2_msg_add(update, (struct sadb_ext *) key,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ key = 0;
+ }
+ }
+#ifndef KAME
+ /* Setup identity extensions. */
+ if (isakmp_sa->id_i) {
+ pp = pf_key_v2_convert_id(isakmp_sa->id_i, isakmp_sa->id_i_len,
+ &len, &idtype);
+ if (!pp)
+ goto nosid;
+
+ sid = calloc(PF_KEY_V2_ROUND(len + 1) + sizeof *sid,
+ sizeof(u_int8_t));
+ if (!sid) {
+ free(pp);
+ goto cleanup;
+ }
+ sid->sadb_ident_type = idtype;
+ sid->sadb_ident_len = ((sizeof *sid) / PF_KEY_V2_CHUNK) +
+ PF_KEY_V2_ROUND(len + 1) / PF_KEY_V2_CHUNK;
+ if ((isakmp_sa->initiator && !incoming) ||
+ (!isakmp_sa->initiator && incoming))
+ sid->sadb_ident_exttype = SADB_EXT_IDENTITY_SRC;
+ else
+ sid->sadb_ident_exttype = SADB_EXT_IDENTITY_DST;
+
+ memcpy(sid + 1, pp, len);
+ free(pp);
+
+ if (pf_key_v2_msg_add(update, (struct sadb_ext *) sid,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ sid = 0;
+
+nosid:
+ if (sid)
+ free(sid);
+ sid = 0;
+ }
+ if (isakmp_sa->id_r) {
+ pp = pf_key_v2_convert_id(isakmp_sa->id_r, isakmp_sa->id_r_len,
+ &len, &idtype);
+ if (!pp)
+ goto nodid;
+
+ sid = calloc(PF_KEY_V2_ROUND(len + 1) + sizeof *sid,
+ sizeof(u_int8_t));
+ if (!sid) {
+ free(pp);
+ goto cleanup;
+ }
+ sid->sadb_ident_type = idtype;
+ sid->sadb_ident_len = ((sizeof *sid) / PF_KEY_V2_CHUNK) +
+ PF_KEY_V2_ROUND(len + 1) / PF_KEY_V2_CHUNK;
+ if ((isakmp_sa->initiator && !incoming) ||
+ (!isakmp_sa->initiator && incoming))
+ sid->sadb_ident_exttype = SADB_EXT_IDENTITY_DST;
+ else
+ sid->sadb_ident_exttype = SADB_EXT_IDENTITY_SRC;
+
+ memcpy(sid + 1, pp, len);
+ free(pp);
+
+ if (pf_key_v2_msg_add(update, (struct sadb_ext *) sid,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ sid = 0;
+
+nodid:
+ if (sid)
+ free(sid);
+ sid = 0;
+ }
+#endif /* KAME */
+
+#ifdef SADB_X_CREDTYPE_NONE
+ /*
+ * Send received credentials to the kernel. We don't bother with
+ * our credentials, since the process either knows them (if it
+ * specified them with setsockopt()), or has no business looking at
+ * them (e.g., system wide certs).
+ */
+ if (isakmp_sa->recv_cert) {
+ switch (isakmp_sa->recv_certtype) {
+ case ISAKMP_CERTENC_NONE:
+ /* Nothing to be done here. */
+ break;
+
+#if defined (USE_KEYNOTE) && defined (SADB_X_EXT_REMOTE_CREDENTIALS)
+ case ISAKMP_CERTENC_KEYNOTE:
+ len = strlen(isakmp_sa->recv_cert);
+ cred = calloc(PF_KEY_V2_ROUND(len) + sizeof *cred,
+ sizeof(u_int8_t));
+ if (!cred)
+ goto cleanup;
+
+ cred->sadb_x_cred_len =
+ ((sizeof *cred) / PF_KEY_V2_CHUNK) +
+ PF_KEY_V2_ROUND(len) / PF_KEY_V2_CHUNK;
+ cred->sadb_x_cred_exttype =
+ SADB_X_EXT_REMOTE_CREDENTIALS;
+ cred->sadb_x_cred_type = SADB_X_CREDTYPE_KEYNOTE;
+ memcpy(cred + 1, isakmp_sa->recv_cert, len);
+
+ if (pf_key_v2_msg_add(update, (struct sadb_ext *) cred,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ break;
+#endif /* USE_KEYNOTE */
+
+#if defined (USE_X509) && defined (SADB_X_EXT_REMOTE_CREDENTIALS)
+ case ISAKMP_CERTENC_X509_SIG:
+ {
+ u_int8_t *data;
+ u_int32_t datalen;
+ struct cert_handler *handler;
+
+ /* We do it this way to avoid weird includes.*/
+ handler = cert_get(ISAKMP_CERTENC_X509_SIG);
+ if (!handler)
+ break;
+ handler->cert_serialize(isakmp_sa->recv_cert,
+ &data, &datalen);
+ if (!data)
+ break;
+
+ len = datalen;
+ cred =
+ calloc(PF_KEY_V2_ROUND(len) + sizeof *cred,
+ sizeof(u_int8_t));
+ if (!cred) {
+ free(data);
+ goto cleanup;
+ }
+ cred->sadb_x_cred_len =
+ ((sizeof *cred) / PF_KEY_V2_CHUNK) +
+ PF_KEY_V2_ROUND(len) / PF_KEY_V2_CHUNK;
+ cred->sadb_x_cred_exttype =
+ SADB_X_EXT_REMOTE_CREDENTIALS;
+ cred->sadb_x_cred_type = SADB_X_CREDTYPE_X509;
+ memcpy(cred + 1, data, len);
+ free(data);
+
+ if (pf_key_v2_msg_add(update,
+ (struct sadb_ext *) cred,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ }
+ break;
+#endif /* USE_X509 */
+ }
+ }
+#endif /* SADB_X_CREDTYPE_NONE */
+
+#ifdef SADB_X_AUTHTYPE_NONE
+ /*
+ * Tell the kernel what the peer used to authenticate, unless it was a
+ * passphrase.
+ */
+ if (isakmp_sa->recv_key) {
+ u_int8_t *data;
+
+ /*
+ * If it's a private key, we shouldn't pass it to the kernel
+ * for processes to see; successful authentication of Phase 1
+ * implies that the process already knew the passphrase. On
+ * the other hand, we don't want to reveal to processes any
+ * system-wide passphrases used for authentication with remote
+ * systems. Same reason we don't send up the key (private or
+ * passphrase) we used to authenticate with the peer.
+ */
+ if (isakmp_sa->recv_keytype == ISAKMP_KEY_PASSPHRASE)
+ goto doneauth;
+
+ key_serialize(isakmp_sa->recv_keytype, ISAKMP_KEYTYPE_PUBLIC,
+ isakmp_sa->recv_key, &data, &len);
+ if (!data)
+ goto cleanup;
+
+ cred = calloc(PF_KEY_V2_ROUND(len) + sizeof *cred,
+ sizeof(u_int8_t));
+ if (!cred) {
+ free(data);
+ goto cleanup;
+ }
+ cred->sadb_x_cred_len = ((sizeof *cred) / PF_KEY_V2_CHUNK) +
+ PF_KEY_V2_ROUND(len) / PF_KEY_V2_CHUNK;
+ cred->sadb_x_cred_exttype = SADB_X_EXT_REMOTE_AUTH;
+ memcpy(cred + 1, data, len);
+ free(data);
+
+ switch (isakmp_sa->recv_keytype) {
+ case ISAKMP_KEY_RSA:
+ cred->sadb_x_cred_type = SADB_X_AUTHTYPE_RSA;
+ break;
+
+ default:
+ log_print("pf_key_v2_set_spi: "
+ "unknown received key type %d",
+ isakmp_sa->recv_keytype);
+ free(cred);
+ goto cleanup;
+ }
+
+ if (pf_key_v2_msg_add(update, (struct sadb_ext *) cred,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ }
+doneauth:
+#endif /* SADB_X_AUTHTYPE_NONE */
+
+#ifdef SADB_X_EXT_FLOW_TYPE
+ /* Setup the flow type extension. */
+ bzero(&flowtype, sizeof flowtype);
+ flowtype.sadb_protocol_exttype = SADB_X_EXT_FLOW_TYPE;
+ flowtype.sadb_protocol_len = sizeof flowtype / PF_KEY_V2_CHUNK;
+ flowtype.sadb_protocol_direction = incoming ?
+ IPSP_DIRECTION_IN : IPSP_DIRECTION_OUT;
+
+ if (pf_key_v2_msg_add(update, (struct sadb_ext *)&flowtype, 0) == -1)
+ goto cleanup;
+
+ bzero(&tprotocol, sizeof tprotocol);
+ tprotocol.sadb_protocol_exttype = SADB_X_EXT_PROTOCOL;
+ tprotocol.sadb_protocol_len = sizeof tprotocol / PF_KEY_V2_CHUNK;
+ tprotocol.sadb_protocol_proto = isa->tproto;
+
+ if (pf_key_v2_msg_add(update, (struct sadb_ext *)&tprotocol,
+ 0) == -1)
+ goto cleanup;
+
+ len = sizeof *addr + PF_KEY_V2_ROUND(sysdep_sa_len(isa->src_net));
+ addr = calloc(1, len);
+ if (!addr)
+ goto cleanup;
+ addr->sadb_address_exttype = incoming ?
+ SADB_X_EXT_DST_FLOW : SADB_X_EXT_SRC_FLOW;
+ addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
+ addr->sadb_address_reserved = 0;
+ pf_key_v2_setup_sockaddr(addr + 1, isa->src_net, 0, isa->sport, 0);
+ if (pf_key_v2_msg_add(update, (struct sadb_ext *) addr,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ addr = 0;
+
+ addr = calloc(1, len);
+ if (!addr)
+ goto cleanup;
+ addr->sadb_address_exttype =
+ incoming ? SADB_X_EXT_DST_MASK : SADB_X_EXT_SRC_MASK;
+ addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
+ addr->sadb_address_reserved = 0;
+ pf_key_v2_setup_sockaddr(addr + 1, isa->src_mask, 0,
+ isa->sport ? 0xffff : 0, 0);
+ if (pf_key_v2_msg_add(update, (struct sadb_ext *) addr,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ addr = 0;
+
+ addr = calloc(1, len);
+ if (!addr)
+ goto cleanup;
+ addr->sadb_address_exttype = incoming ?
+ SADB_X_EXT_SRC_FLOW : SADB_X_EXT_DST_FLOW;
+ addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
+ addr->sadb_address_reserved = 0;
+ pf_key_v2_setup_sockaddr(addr + 1, isa->dst_net, 0, isa->dport, 0);
+ if (pf_key_v2_msg_add(update, (struct sadb_ext *) addr,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ addr = 0;
+
+ addr = calloc(1, len);
+ if (!addr)
+ goto cleanup;
+ addr->sadb_address_exttype =
+ incoming ? SADB_X_EXT_SRC_MASK : SADB_X_EXT_DST_MASK;
+ addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
+ addr->sadb_address_reserved = 0;
+ pf_key_v2_setup_sockaddr(addr + 1, isa->dst_mask, 0,
+ isa->dport ? 0xffff : 0, 0);
+ if (pf_key_v2_msg_add(update, (struct sadb_ext *) addr,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ addr = 0;
+#endif /* SADB_X_EXT_FLOW_TYPE */
+
+ /* XXX Here can sensitivity extensions be setup. */
+
+#ifdef USE_DEBUG
+ if (sockaddr2text(dst, &addr_str, 0))
+ addr_str = 0;
+
+ LOG_DBG((LOG_SYSDEP, 10, "pf_key_v2_set_spi: "
+ "satype %d dst %s SPI 0x%x", msg.sadb_msg_satype,
+ addr_str ? addr_str : "unknown", ntohl(ssa.sadb_sa_spi)));
+
+ if (addr_str)
+ free(addr_str);
+#endif /* USE_DEBUG */
+
+ /*
+ * Although PF_KEY knows about expirations, it is unreliable per the
+ * specs thus we need to do them inside isakmpd as well.
+ */
+ if (sa->seconds)
+ if (sa_setup_expirations(sa))
+ goto cleanup;
+
+ ret = pf_key_v2_call(update);
+ pf_key_v2_msg_free(update);
+ update = 0;
+ if (!ret)
+ goto cleanup;
+ err = ((struct sadb_msg *)TAILQ_FIRST(ret)->seg)->sadb_msg_errno;
+ pf_key_v2_msg_free(ret);
+ ret = 0;
+
+ /*
+ * If we are doing an addition into an SADB shared with our peer,
+ * errors here are to be expected as the peer will already have
+ * created the SA, and can thus be ignored.
+ */
+ if (err && !(msg.sadb_msg_type == SADB_ADD &&
+ conf_get_str("General", "Shared-SADB"))) {
+ log_print("pf_key_v2_set_spi: %s: %s",
+ msg.sadb_msg_type == SADB_ADD ? "ADD" : "UPDATE",
+ strerror(err));
+ goto cleanup;
+ }
+ LOG_DBG((LOG_SYSDEP, 50, "pf_key_v2_set_spi: done"));
+
+ return 0;
+
+cleanup:
+ if (sid)
+ free(sid);
+ if (addr)
+ free(addr);
+ if (life)
+ free(life);
+ if (key)
+ free(key);
+ if (update)
+ pf_key_v2_msg_free(update);
+ if (ret)
+ pf_key_v2_msg_free(ret);
+ return -1;
+}
+
+static __inline__ int
+pf_key_v2_mask_to_bits(u_int32_t mask)
+{
+ u_int32_t hmask = ntohl(mask);
+
+ return (33 - ffs(~hmask + 1)) % 33;
+}
+
+static int
+pf_key_v2_mask6_to_bits(u_int8_t * mask)
+{
+ int n;
+
+ bit_ffc(mask, 128, &n);
+ return n == -1 ? 128 : n;
+}
+
+/*
+ * Enable/disable a flow.
+ * XXX Assumes OpenBSD {ADD,DEL}FLOW extensions.
+ * Should probably be moved to sysdep.c
+ */
+static int
+pf_key_v2_flow(struct sockaddr *laddr, struct sockaddr *lmask,
+ struct sockaddr *raddr, struct sockaddr *rmask,
+ u_int8_t tproto, u_int16_t sport, u_int16_t dport,
+ u_int8_t *spi, u_int8_t proto, struct sockaddr *dst,
+ struct sockaddr *src, int delete, int ingress,
+ u_int8_t srcid_type, u_int8_t *srcid, int srcid_len,
+ u_int8_t dstid_type, u_int8_t *dstid, int dstid_len,
+ struct ipsec_proto *iproto)
+{
+#ifdef USE_DEBUG
+ char *laddr_str, *lmask_str, *raddr_str, *rmask_str;
+#endif
+
+#if defined (SADB_X_ADDFLOW) && defined (SADB_X_DELFLOW)
+ struct sadb_msg msg;
+#if defined (SADB_X_EXT_FLOW_TYPE)
+ struct sadb_protocol flowtype;
+ struct sadb_ident *sid = 0;
+#else
+ struct sadb_sa ssa;
+#endif
+ struct sadb_address *addr = 0;
+ struct sadb_protocol tprotocol;
+ struct pf_key_v2_msg *flow = 0, *ret = 0;
+ size_t len;
+ int err;
+
+#if !defined (SADB_X_SAFLAGS_INGRESS_FLOW) && !defined (SADB_X_EXT_FLOW_TYPE)
+ if (ingress)
+ return 0;
+#endif
+
+ msg.sadb_msg_type = delete ? SADB_X_DELFLOW : SADB_X_ADDFLOW;
+ switch (proto) {
+ case IPSEC_PROTO_IPSEC_ESP:
+ msg.sadb_msg_satype = SADB_SATYPE_ESP;
+ break;
+ case IPSEC_PROTO_IPSEC_AH:
+ msg.sadb_msg_satype = SADB_SATYPE_AH;
+ break;
+ case IPSEC_PROTO_IPCOMP:
+ msg.sadb_msg_satype = SADB_X_SATYPE_IPCOMP;
+ break;
+ default:
+ log_print("pf_key_v2_flow: invalid proto %d", proto);
+ goto cleanup;
+ }
+ msg.sadb_msg_seq = 0;
+ flow = pf_key_v2_msg_new(&msg, 0);
+ if (!flow)
+ goto cleanup;
+
+#if defined (SADB_X_EXT_FLOW_TYPE)
+ if (!delete) {
+ /* Setup the source ID, if provided. */
+ if (srcid) {
+ sid = calloc(
+ PF_KEY_V2_ROUND(srcid_len + 1) + sizeof *sid,
+ sizeof(u_int8_t));
+ if (!sid)
+ goto cleanup;
+
+ sid->sadb_ident_len = ((sizeof *sid) / PF_KEY_V2_CHUNK)
+ + PF_KEY_V2_ROUND(srcid_len + 1) / PF_KEY_V2_CHUNK;
+ sid->sadb_ident_exttype = SADB_EXT_IDENTITY_SRC;
+ sid->sadb_ident_type = srcid_type;
+
+ memcpy(sid + 1, srcid, srcid_len);
+
+ if (pf_key_v2_msg_add(flow, (struct sadb_ext *) sid,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+
+ sid = 0;
+ }
+ /* Setup the destination ID, if provided. */
+ if (dstid) {
+ sid = calloc(
+ PF_KEY_V2_ROUND(dstid_len + 1) + sizeof *sid,
+ sizeof(u_int8_t));
+ if (!sid)
+ goto cleanup;
+
+ sid->sadb_ident_len = ((sizeof *sid) / PF_KEY_V2_CHUNK)
+ + PF_KEY_V2_ROUND(dstid_len + 1) / PF_KEY_V2_CHUNK;
+ sid->sadb_ident_exttype = SADB_EXT_IDENTITY_DST;
+ sid->sadb_ident_type = dstid_type;
+
+ memcpy(sid + 1, dstid, dstid_len);
+
+ if (pf_key_v2_msg_add(flow, (struct sadb_ext *) sid,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+
+ sid = 0;
+ }
+ }
+ /* Setup the flow type extension. */
+ bzero(&flowtype, sizeof flowtype);
+ flowtype.sadb_protocol_exttype = SADB_X_EXT_FLOW_TYPE;
+ flowtype.sadb_protocol_len = sizeof flowtype / PF_KEY_V2_CHUNK;
+ flowtype.sadb_protocol_direction =
+ ingress ? IPSP_DIRECTION_IN : IPSP_DIRECTION_OUT;
+ flowtype.sadb_protocol_proto =
+ ingress ? SADB_X_FLOW_TYPE_USE : SADB_X_FLOW_TYPE_REQUIRE;
+
+ if (pf_key_v2_msg_add(flow, (struct sadb_ext *)&flowtype, 0) == -1)
+ goto cleanup;
+#else /* SADB_X_EXT_FLOW_TYPE */
+ /* Setup the SA extension. */
+ ssa.sadb_sa_exttype = SADB_EXT_SA;
+ ssa.sadb_sa_len = sizeof ssa / PF_KEY_V2_CHUNK;
+ memcpy(&ssa.sadb_sa_spi, spi, sizeof ssa.sadb_sa_spi);
+ ssa.sadb_sa_replay = 0;
+ ssa.sadb_sa_state = 0;
+ ssa.sadb_sa_auth = 0;
+ ssa.sadb_sa_encrypt = 0;
+ ssa.sadb_sa_flags = 0;
+#if defined (SADB_X_SAFLAGS_INGRESS_FLOW)
+ if (ingress)
+ ssa.sadb_sa_flags |= SADB_X_SAFLAGS_INGRESS_FLOW;
+#endif
+#if defined (SADB_X_SAFLAGS_REPLACEFLOW)
+ if (!delete && !ingress)
+ ssa.sadb_sa_flags |= SADB_X_SAFLAGS_REPLACEFLOW;
+#endif
+
+ if (pf_key_v2_msg_add(flow, (struct sadb_ext *)&ssa, 0) == -1)
+ goto cleanup;
+#endif /* SADB_X_EXT_FLOW_TYPE */
+
+ /*
+ * Setup the ADDRESS extensions.
+ */
+ len = sizeof *addr + PF_KEY_V2_ROUND(sysdep_sa_len(src));
+#if !defined (SADB_X_EXT_FLOW_TYPE)
+ if (!delete || ingress)
+#else
+ if (!delete)
+#endif /* SADB_X_EXT_FLOW_TYPE */
+ {
+ addr = calloc(1, len);
+ if (!addr)
+ goto cleanup;
+ addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+ addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
+ addr->sadb_address_reserved = 0;
+#if defined (SADB_X_EXT_FLOW_TYPE)
+ pf_key_v2_setup_sockaddr(addr + 1, src, dst, 0, ingress);
+#else
+ pf_key_v2_setup_sockaddr(addr + 1, dst, 0, 0, 0);
+#endif
+ if (pf_key_v2_msg_add(flow, (struct sadb_ext *) addr,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ addr = 0;
+ }
+ len = sizeof *addr + PF_KEY_V2_ROUND(sysdep_sa_len(laddr));
+ addr = calloc(1, len);
+ if (!addr)
+ goto cleanup;
+ addr->sadb_address_exttype = SADB_X_EXT_SRC_FLOW;
+ addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
+ addr->sadb_address_reserved = 0;
+ pf_key_v2_setup_sockaddr(addr + 1, laddr, 0, sport, 0);
+ if (pf_key_v2_msg_add(flow, (struct sadb_ext *) addr,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ addr = 0;
+
+ addr = calloc(1, len);
+ if (!addr)
+ goto cleanup;
+ addr->sadb_address_exttype = SADB_X_EXT_SRC_MASK;
+ addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
+ addr->sadb_address_reserved = 0;
+ pf_key_v2_setup_sockaddr(addr + 1, lmask, 0, sport ? 0xffff : 0, 0);
+ if (pf_key_v2_msg_add(flow, (struct sadb_ext *) addr,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ addr = 0;
+
+ addr = calloc(1, len);
+ if (!addr)
+ goto cleanup;
+ addr->sadb_address_exttype = SADB_X_EXT_DST_FLOW;
+ addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
+ addr->sadb_address_reserved = 0;
+ pf_key_v2_setup_sockaddr(addr + 1, raddr, 0, dport, 0);
+ if (pf_key_v2_msg_add(flow, (struct sadb_ext *) addr,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ addr = 0;
+
+ addr = calloc(1, len);
+ if (!addr)
+ goto cleanup;
+ addr->sadb_address_exttype = SADB_X_EXT_DST_MASK;
+ addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
+ addr->sadb_address_reserved = 0;
+ pf_key_v2_setup_sockaddr(addr + 1, rmask, 0, dport ? 0xffff : 0, 0);
+ if (pf_key_v2_msg_add(flow, (struct sadb_ext *) addr,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ addr = 0;
+
+ /* Setup the protocol extension. */
+ bzero(&tprotocol, sizeof tprotocol);
+ tprotocol.sadb_protocol_exttype = SADB_X_EXT_PROTOCOL;
+ tprotocol.sadb_protocol_len = sizeof tprotocol / PF_KEY_V2_CHUNK;
+ tprotocol.sadb_protocol_proto = tproto;
+
+ if (pf_key_v2_msg_add(flow, (struct sadb_ext *)&tprotocol, 0) == -1)
+ goto cleanup;
+
+#ifdef USE_DEBUG
+ if (sockaddr2text(laddr, &laddr_str, 0))
+ laddr_str = 0;
+ if (sockaddr2text(lmask, &lmask_str, 0))
+ lmask_str = 0;
+ if (sockaddr2text(raddr, &raddr_str, 0))
+ raddr_str = 0;
+ if (sockaddr2text(rmask, &rmask_str, 0))
+ rmask_str = 0;
+
+ LOG_DBG((LOG_SYSDEP, 50,
+ "pf_key_v2_flow: src %s %s dst %s %s proto %u sport %u dport %u",
+ laddr_str ? laddr_str : "<??\?>", lmask_str ? lmask_str : "<??\?>",
+ raddr_str ? raddr_str : "<??\?>", rmask_str ? rmask_str : "<??\?>",
+ tproto, ntohs(sport), ntohs(dport)));
+
+ if (laddr_str)
+ free(laddr_str);
+ if (lmask_str)
+ free(lmask_str);
+ if (raddr_str)
+ free(raddr_str);
+ if (rmask_str)
+ free(rmask_str);
+#endif /* USE_DEBUG */
+
+ ret = pf_key_v2_call(flow);
+ pf_key_v2_msg_free(flow);
+ flow = 0;
+ if (!ret)
+ goto cleanup;
+ err = ((struct sadb_msg *)TAILQ_FIRST(ret)->seg)->sadb_msg_errno;
+ if (err) {
+ if (err == ESRCH) /* These are common and usually
+ * harmless. */
+ LOG_DBG((LOG_SYSDEP, 10, "pf_key_v2_flow: %sFLOW: %s",
+ delete ? "DEL" : "ADD", strerror(err)));
+ else
+ log_print("pf_key_v2_flow: %sFLOW: %s",
+ delete ? "DEL" : "ADD", strerror(err));
+ goto cleanup;
+ }
+ pf_key_v2_msg_free(ret);
+
+ LOG_DBG((LOG_MISC, 50, "pf_key_v2_flow: %sFLOW: done",
+ delete ? "DEL" : "ADD"));
+
+ return 0;
+
+cleanup:
+#if defined (SADB_X_EXT_FLOW_TYPE)
+ if (sid)
+ free(sid);
+#endif /* SADB_X_EXT_FLOW_TYPE */
+ if (addr)
+ free(addr);
+ if (flow)
+ pf_key_v2_msg_free(flow);
+ if (ret)
+ pf_key_v2_msg_free(ret);
+ return -1;
+
+#elif defined (SADB_X_SPDUPDATE) && defined (SADB_X_SPDDELETE)
+ struct sadb_msg msg;
+ struct sadb_x_policy *policy = 0;
+ struct sadb_x_ipsecrequest *ipsecrequest;
+ struct sadb_x_sa2 ssa2;
+ struct sadb_address *addr = 0;
+ struct sockaddr *saddr;
+ struct pf_key_v2_msg *flow = 0, *ret = 0;
+ u_int8_t *policy_buf;
+ size_t len;
+ int err;
+ struct sockaddr_in *ip4_sa;
+ struct sockaddr_in6 *ip6_sa;
+
+ msg.sadb_msg_type = delete ? SADB_X_SPDDELETE : SADB_X_SPDUPDATE;
+ msg.sadb_msg_satype = SADB_SATYPE_UNSPEC;
+ msg.sadb_msg_seq = 0;
+ flow = pf_key_v2_msg_new(&msg, 0);
+ if (!flow)
+ goto cleanup;
+
+ memset(&ssa2, 0, sizeof ssa2);
+ ssa2.sadb_x_sa2_exttype = SADB_X_EXT_SA2;
+ ssa2.sadb_x_sa2_len = sizeof ssa2 / PF_KEY_V2_CHUNK;
+ ssa2.sadb_x_sa2_mode = 0;
+ if (pf_key_v2_msg_add(flow, (struct sadb_ext *)&ssa2, 0) == -1)
+ goto cleanup;
+
+ /*
+ * Setup the ADDRESS extensions.
+ */
+ len = sizeof *addr + PF_KEY_V2_ROUND(sysdep_sa_len(src));
+ addr = calloc(1, len);
+ if (!addr)
+ goto cleanup;
+ addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+ addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
+#ifdef LINUX_IPSEC
+ addr->sadb_address_proto = tproto;
+#else
+ addr->sadb_address_proto = IPSEC_ULPROTO_ANY;
+#endif
+ addr->sadb_address_reserved = 0;
+#ifdef LINUX_IPSEC
+ pf_key_v2_setup_sockaddr(addr + 1, laddr, 0, sport, 0);
+#else
+ pf_key_v2_setup_sockaddr(addr + 1, laddr, 0, IPSEC_PORT_ANY, 0);
+#endif
+ switch (laddr->sa_family) {
+ case AF_INET:
+ ip4_sa = (struct sockaddr_in *) lmask;
+ addr->sadb_address_prefixlen
+ = pf_key_v2_mask_to_bits(ip4_sa->sin_addr.s_addr);
+ break;
+ case AF_INET6:
+ ip6_sa = (struct sockaddr_in6 *) lmask;
+ addr->sadb_address_prefixlen =
+ pf_key_v2_mask6_to_bits(&ip6_sa->sin6_addr.s6_addr[0]);
+ break;
+ }
+ if (pf_key_v2_msg_add(flow, (struct sadb_ext *) addr,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ addr = 0;
+
+ len = sizeof *addr + PF_KEY_V2_ROUND(sysdep_sa_len(raddr));
+ addr = calloc(1, len);
+ if (!addr)
+ goto cleanup;
+ addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+ addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
+#ifdef LINUX_IPSEC
+ addr->sadb_address_proto = tproto;
+#else
+ addr->sadb_address_proto = IPSEC_ULPROTO_ANY;
+#endif
+ addr->sadb_address_reserved = 0;
+#ifdef LINUX_IPSEC
+ pf_key_v2_setup_sockaddr(addr + 1, raddr, 0, dport, 0);
+#else
+ pf_key_v2_setup_sockaddr(addr + 1, raddr, 0, IPSEC_PORT_ANY, 0);
+#endif
+ switch (raddr->sa_family) {
+ case AF_INET:
+ ip4_sa = (struct sockaddr_in *) rmask;
+ addr->sadb_address_prefixlen
+ = pf_key_v2_mask_to_bits(ip4_sa->sin_addr.s_addr);
+ break;
+ case AF_INET6:
+ ip6_sa = (struct sockaddr_in6 *) rmask;
+ addr->sadb_address_prefixlen =
+ pf_key_v2_mask6_to_bits(&ip6_sa->sin6_addr.s6_addr[0]);
+ break;
+ }
+ if (pf_key_v2_msg_add(flow, (struct sadb_ext *) addr,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ addr = 0;
+
+ /* Setup the POLICY extension. */
+ len = sizeof *policy + sizeof *ipsecrequest +
+ 2 * PF_KEY_V2_ROUND(sysdep_sa_len(src));
+ policy_buf = (u_int8_t *) calloc(1, len);
+ if (!policy_buf) {
+ log_error("pf_key_v2_flow: calloc %lu failed",
+ (unsigned long) len);
+ goto cleanup;
+ }
+ policy = (struct sadb_x_policy *) policy_buf;
+ policy->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
+ policy->sadb_x_policy_len = len / PF_KEY_V2_CHUNK;
+ policy->sadb_x_policy_type = IPSEC_POLICY_IPSEC;
+ if (ingress)
+ policy->sadb_x_policy_dir = IPSEC_DIR_INBOUND;
+ else
+ policy->sadb_x_policy_dir = IPSEC_DIR_OUTBOUND;
+ policy->sadb_x_policy_reserved = 0;
+
+ /* Setup the IPSECREQUEST extension part. */
+ ipsecrequest = (struct sadb_x_ipsecrequest *) (policy + 1);
+ ipsecrequest->sadb_x_ipsecrequest_len = len - sizeof *policy;
+ switch (proto) {
+ case IPSEC_PROTO_IPSEC_ESP:
+ ipsecrequest->sadb_x_ipsecrequest_proto = IPPROTO_ESP;
+ break;
+ case IPSEC_PROTO_IPSEC_AH:
+ ipsecrequest->sadb_x_ipsecrequest_proto = IPPROTO_AH;
+ break;
+ default:
+ log_print("pf_key_v2_flow: invalid proto %d", proto);
+ goto cleanup;
+ }
+#if defined (LINUX_IPSEC)
+ if (iproto->encap_mode == IPSEC_ENCAP_TUNNEL)
+ ipsecrequest->sadb_x_ipsecrequest_mode = IPSEC_MODE_TUNNEL;
+ else
+ ipsecrequest->sadb_x_ipsecrequest_mode = IPSEC_MODE_TRANSPORT;
+#else
+ ipsecrequest->sadb_x_ipsecrequest_mode = IPSEC_MODE_TUNNEL; /* XXX */
+#endif
+ ipsecrequest->sadb_x_ipsecrequest_level
+ = ingress ? IPSEC_LEVEL_USE : IPSEC_LEVEL_REQUIRE;
+ ipsecrequest->sadb_x_ipsecrequest_reqid = 0; /* XXX */
+
+ /* Add source and destination addresses. */
+ saddr = (struct sockaddr *)(ipsecrequest + 1);
+ pf_key_v2_setup_sockaddr(saddr, src, 0, 0, 0);
+ switch (src->sa_family) {
+ case AF_INET:
+ saddr = (struct sockaddr *)((struct sockaddr_in *)saddr + 1);
+ break;
+ case AF_INET6:
+ saddr = (struct sockaddr *)((struct sockaddr_in6 *)saddr + 1);
+ break;
+ }
+ pf_key_v2_setup_sockaddr(saddr, dst, 0, 0, 0);
+ if (pf_key_v2_msg_add(flow, (struct sadb_ext *)policy,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ policy = 0;
+
+#ifdef USE_DEBUG
+ if (sockaddr2text(laddr, &laddr_str, 0))
+ laddr_str = 0;
+ if (sockaddr2text(lmask, &lmask_str, 0))
+ lmask_str = 0;
+ if (sockaddr2text(raddr, &raddr_str, 0))
+ raddr_str = 0;
+ if (sockaddr2text(rmask, &rmask_str, 0))
+ rmask_str = 0;
+
+ LOG_DBG((LOG_SYSDEP, 50, "pf_key_v2_flow: src %s %s dst %s %s",
+ laddr_str ? laddr_str : "<??\?>", lmask_str ? lmask_str : "<??\?>",
+ raddr_str ? raddr_str : "<??\?>",
+ rmask_str ? rmask_str : "<??\?>"));
+
+ if (laddr_str)
+ free(laddr_str);
+ if (lmask_str)
+ free(lmask_str);
+ if (raddr_str)
+ free(raddr_str);
+ if (rmask_str)
+ free(rmask_str);
+#endif
+
+ ret = pf_key_v2_call(flow);
+ pf_key_v2_msg_free(flow);
+ flow = 0;
+ if (!ret)
+ goto cleanup;
+ err = ((struct sadb_msg *)TAILQ_FIRST(ret)->seg)->sadb_msg_errno;
+ if (!delete && err == EEXIST) {
+ LOG_DBG((LOG_SYSDEP, 50, "pf_key_v2_flow: "
+ "SPDADD returns EEXIST"));
+ } else if (err) {
+ log_print("pf_key_v2_flow: SPD%s: %s",
+ delete ? "DELETE" : "ADD", strerror(err));
+ goto cleanup;
+ }
+ pf_key_v2_msg_free(ret);
+
+ LOG_DBG((LOG_SYSDEP, 50, "pf_key_v2_flow: SPD%s: done",
+ delete ? "DELETE" : "ADD"));
+
+ return 0;
+
+cleanup:
+ if (addr)
+ free(addr);
+ if (policy)
+ free(policy);
+ if (flow)
+ pf_key_v2_msg_free(flow);
+ if (ret)
+ pf_key_v2_msg_free(ret);
+ return -1;
+
+#else
+ log_print("pf_key_v2_flow: not supported in pure PF_KEYv2");
+ return -1;
+#endif
+}
+
+#ifndef KAME
+static u_int8_t *
+pf_key_v2_convert_id(u_int8_t * id, int idlen, size_t * reslen, int *idtype)
+{
+ u_int8_t *addr, *res = 0;
+ char addrbuf[ADDRESS_MAX + 5];
+
+ switch (id[0]) {
+ case IPSEC_ID_FQDN:
+ res = calloc(idlen - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ,
+ sizeof(u_int8_t));
+ if (!res)
+ return 0;
+
+ *reslen = idlen - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ;
+ memcpy(res, id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ, *reslen);
+ *idtype = SADB_IDENTTYPE_FQDN;
+ LOG_DBG((LOG_SYSDEP, 40, "pf_key_v2_convert_id: FQDN %.*s",
+ (int) *reslen, res));
+ return res;
+
+ case IPSEC_ID_USER_FQDN:
+ res = calloc(idlen - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ,
+ sizeof(u_int8_t));
+ if (!res)
+ return 0;
+
+ *reslen = idlen - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ;
+ memcpy(res, id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ, *reslen);
+ *idtype = SADB_IDENTTYPE_USERFQDN;
+ LOG_DBG((LOG_SYSDEP, 40, "pf_key_v2_convert_id: UFQDN %.*s",
+ (int) *reslen, res));
+ return res;
+
+ case IPSEC_ID_IPV4_ADDR: /* XXX CONNECTION ? */
+ if (inet_ntop(AF_INET, id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ,
+ addrbuf, ADDRESS_MAX) == NULL)
+ return 0;
+ *reslen = strlen(addrbuf) + 3;
+ strlcat(addrbuf, "/32", ADDRESS_MAX + 5);
+ res = (u_int8_t *) strdup(addrbuf);
+ if (!res)
+ return 0;
+ *idtype = SADB_IDENTTYPE_PREFIX;
+ LOG_DBG((LOG_SYSDEP, 40, "pf_key_v2_convert_id: "
+ "IPv4 address %s", res));
+ return res;
+
+ case IPSEC_ID_IPV6_ADDR: /* XXX CONNECTION ? */
+ if (inet_ntop(AF_INET6,
+ id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ,
+ addrbuf, ADDRESS_MAX) == NULL)
+ return 0;
+ *reslen = strlen(addrbuf) + 4;
+ strlcat(addrbuf, "/128", ADDRESS_MAX + 5);
+ res = (u_int8_t *) strdup(addrbuf);
+ if (!res)
+ return 0;
+ LOG_DBG((LOG_SYSDEP, 40, "pf_key_v2_convert_id: "
+ "IPv6 address %s", res));
+ *idtype = SADB_IDENTTYPE_PREFIX;
+ return res;
+
+ case IPSEC_ID_IPV4_ADDR_SUBNET: /* XXX PREFIX */
+ addr = id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ;
+ if (inet_ntop(AF_INET, addr, addrbuf, ADDRESS_MAX) == NULL)
+ return 0;
+ snprintf(addrbuf + strlen(addrbuf),
+ ADDRESS_MAX - strlen(addrbuf), "/%d",
+ pf_key_v2_mask_to_bits(*(u_int32_t *)(addr +
+ sizeof(struct in_addr))));
+ *reslen = strlen(addrbuf);
+ res = (u_int8_t *) strdup(addrbuf);
+ if (!res)
+ return 0;
+ *idtype = SADB_IDENTTYPE_PREFIX;
+ LOG_DBG((LOG_SYSDEP, 40, "pf_key_v2_convert_id: "
+ "IPv4 subnet %s", res));
+ return res;
+
+ case IPSEC_ID_IPV6_ADDR_SUBNET: /* XXX PREFIX */
+ addr = id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ;
+ if (inet_ntop(AF_INET6, addr, addrbuf, ADDRESS_MAX) == NULL)
+ return 0;
+ snprintf(addrbuf + strlen(addrbuf),
+ ADDRESS_MAX - strlen(addrbuf), "/%d",
+ pf_key_v2_mask6_to_bits(addr +
+ sizeof(struct in6_addr)));
+ *reslen = strlen(addrbuf);
+ res = (u_int8_t *) strdup(addrbuf);
+ if (!res)
+ return 0;
+ LOG_DBG((LOG_SYSDEP, 40, "pf_key_v2_convert_id: "
+ "IPv6 subnet %s", res));
+ *idtype = SADB_IDENTTYPE_PREFIX;
+ return res;
+
+ case IPSEC_ID_IPV4_RANGE:
+ case IPSEC_ID_IPV6_RANGE:
+ case IPSEC_ID_DER_ASN1_DN:
+ case IPSEC_ID_DER_ASN1_GN:
+ case IPSEC_ID_KEY_ID:
+ /* XXX Not implemented yet. */
+ return 0;
+ }
+
+ return 0;
+}
+#endif
+
+/* Enable a flow given an SA. */
+int
+pf_key_v2_enable_sa(struct sa *sa, struct sa *isakmp_sa)
+{
+ struct ipsec_sa *isa = sa->data;
+ struct sockaddr *dst, *src;
+ int error;
+ struct proto *proto = TAILQ_FIRST(&sa->protos);
+ int sidtype = 0, didtype = 0;
+ size_t sidlen = 0, didlen = 0;
+ u_int8_t *sid = 0, *did = 0;
+#if !defined (SADB_X_EXT_FLOW_TYPE)
+ struct sockaddr_storage hostmask_storage;
+ struct sockaddr *hostmask = (struct sockaddr *)&hostmask_storage;
+#endif /* SADB_X_EXT_FLOW_TYPE */
+
+ sa->transport->vtbl->get_dst(sa->transport, &dst);
+ sa->transport->vtbl->get_src(sa->transport, &src);
+
+#if defined (SADB_X_EXT_FLOW_TYPE)
+ if (isakmp_sa->id_i) {
+ if (isakmp_sa->initiator)
+ sid = pf_key_v2_convert_id(isakmp_sa->id_i,
+ isakmp_sa->id_i_len, &sidlen, &sidtype);
+ else
+ did = pf_key_v2_convert_id(isakmp_sa->id_i,
+ isakmp_sa->id_i_len, &didlen, &didtype);
+ }
+ if (isakmp_sa->id_r) {
+ if (isakmp_sa->initiator)
+ did = pf_key_v2_convert_id(isakmp_sa->id_r,
+ isakmp_sa->id_r_len, &didlen, &didtype);
+ else
+ sid = pf_key_v2_convert_id(isakmp_sa->id_r,
+ isakmp_sa->id_r_len, &sidlen, &sidtype);
+ }
+#endif /* SADB_X_EXT_FLOW_TYPE */
+
+ error = pf_key_v2_flow(isa->src_net, isa->src_mask, isa->dst_net,
+ isa->dst_mask, isa->tproto, isa->sport, isa->dport,
+ proto->spi[0], proto->proto, dst, src, 0, 0,
+ sidtype, sid, sidlen, didtype, did, didlen,
+ proto->data);
+ if (error)
+ goto cleanup;
+
+#if !defined (SADB_X_EXT_FLOW_TYPE)
+ /* Set hostmask to '-1'. */
+ switch (dst->sa_family) {
+ case AF_INET:
+ ((struct sockaddr_in *) hostmask)->sin_family = AF_INET;
+#ifndef USE_OLD_SOCKADDR
+ ((struct sockaddr_in *) hostmask)->sin_len =
+ sizeof(struct in_addr);
+#endif
+ memset(&((struct sockaddr_in *) hostmask)->sin_addr.s_addr,
+ 0xff, sizeof(struct in_addr));
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *) hostmask)->sin6_family = AF_INET6;
+#ifndef USE_OLD_SOCKADDR
+ ((struct sockaddr_in6 *) hostmask)->sin6_len =
+ sizeof(struct in6_addr);
+#endif
+ memset(&((struct sockaddr_in6 *) hostmask)->sin6_addr.s6_addr,
+ 0xff, sizeof(struct in6_addr));
+ break;
+ }
+
+ /* Ingress flows, handling SA bundles. */
+ while (TAILQ_NEXT(proto, link)) {
+ error = pf_key_v2_flow(dst, hostmask, src, hostmask, 0, 0, 0,
+ proto->spi[1], proto->proto, src, dst,
+ 0, 1, 0, 0, 0, 0, 0, 0, proto->data);
+ if (error)
+ goto cleanup;
+ proto = TAILQ_NEXT(proto, link);
+ }
+#endif /* SADB_X_EXT_FLOW_TYPE */
+
+ error = pf_key_v2_flow(isa->dst_net, isa->dst_mask, isa->src_net,
+ isa->src_mask, isa->tproto, isa->dport, isa->sport,
+ proto->spi[1], proto->proto, src, dst, 0, 1,
+ sidtype, sid, sidlen, didtype, did, didlen,
+ proto->data);
+
+cleanup:
+#if defined (SADB_X_EXT_FLOW_TYPE)
+ if (sid)
+ free(sid);
+ if (did)
+ free(did);
+#endif /* SADB_X_EXT_FLOW_TYPE */
+
+ return error;
+}
+
+#if defined (SADB_X_ASKPOLICY)
+/* Increase reference count of refcounted sections. */
+static int
+pf_key_v2_conf_refinc(int af, char *section)
+{
+ char conn[22];
+ int num;
+
+ if (!section)
+ return 0;
+
+ num = conf_get_num(section, "Refcount", 0);
+ if (num == 0)
+ return 0;
+
+ snprintf(conn, sizeof conn, "%d", num + 1);
+ conf_set(af, section, "Refcount", conn, 1, 0);
+ return 0;
+}
+#endif
+
+/*
+ * Return 0 if the section didn't exist or was removed, non-zero otherwise.
+ * Don't touch non-refcounted (statically defined) sections.
+ */
+static int
+pf_key_v2_conf_refhandle(int af, char *section)
+{
+ char conn[22];
+ int num;
+
+ if (!section)
+ return 0;
+
+ num = conf_get_num(section, "Refcount", 0);
+ if (num == 1) {
+ conf_remove_section(af, section);
+ num--;
+ } else if (num != 0) {
+ snprintf(conn, sizeof conn, "%d", num - 1);
+ conf_set(af, section, "Refcount", conn, 1, 0);
+ }
+ return num;
+}
+
+/* Remove all dynamically-established configuration entries. */
+static int
+pf_key_v2_remove_conf(char *section)
+{
+ char *ikepeer, *localid, *remoteid, *configname;
+ struct conf_list_node *attr;
+ struct conf_list *attrs;
+ int af;
+
+ if (!section)
+ return 0;
+
+ if (!conf_get_str(section, "Phase"))
+ return 0;
+
+ /* Only remove dynamically-established entries. */
+ attrs = conf_get_list(section, "Flags");
+ if (attrs) {
+ for (attr = TAILQ_FIRST(&attrs->fields); attr;
+ attr = TAILQ_NEXT(attr, link))
+ if (!strcasecmp(attr->field, "__ondemand"))
+ goto passed;
+
+ conf_free_list(attrs);
+ }
+ return 0;
+
+passed:
+ conf_free_list(attrs);
+
+ af = conf_begin();
+
+ configname = conf_get_str(section, "Configuration");
+ conf_remove_section(af, configname);
+
+ /* These are the Phase 2 Local/Remote IDs. */
+ localid = conf_get_str(section, "Local-ID");
+ pf_key_v2_conf_refhandle(af, localid);
+
+ remoteid = conf_get_str(section, "Remote-ID");
+ pf_key_v2_conf_refhandle(af, remoteid);
+
+ ikepeer = conf_get_str(section, "ISAKMP-peer");
+
+ pf_key_v2_conf_refhandle(af, section);
+
+ if (ikepeer) {
+ remoteid = conf_get_str(ikepeer, "Remote-ID");
+ localid = conf_get_str(ikepeer, "ID");
+ configname = conf_get_str(ikepeer, "Configuration");
+
+ pf_key_v2_conf_refhandle(af, ikepeer);
+ pf_key_v2_conf_refhandle(af, configname);
+
+ /* Phase 1 IDs */
+ pf_key_v2_conf_refhandle(af, localid);
+ pf_key_v2_conf_refhandle(af, remoteid);
+ }
+ conf_end(af, 1);
+ return 0;
+}
+
+/* Disable a flow given a SA. */
+static int
+pf_key_v2_disable_sa(struct sa *sa, int incoming)
+{
+ struct ipsec_sa *isa = sa->data;
+ struct sockaddr *dst, *src;
+ struct proto *proto = TAILQ_FIRST(&sa->protos);
+#if !defined (SADB_X_EXT_FLOW_TYPE)
+ struct sockaddr_storage hostmask_storage;
+ struct sockaddr *hostmask = (struct sockaddr *)&hostmask_storage;
+ int error;
+#endif /* SADB_X_EXT_FLOW_TYPE */
+
+ sa->transport->vtbl->get_dst(sa->transport, &dst);
+ sa->transport->vtbl->get_src(sa->transport, &src);
+
+ if (!incoming)
+ return pf_key_v2_flow(isa->src_net, isa->src_mask,
+ isa->dst_net, isa->dst_mask, isa->tproto, isa->sport,
+ isa->dport, proto->spi[0], proto->proto, src, dst, 1, 0,
+ 0, 0, 0, 0, 0, 0, proto->data);
+ else {
+#if !defined (SADB_X_EXT_FLOW_TYPE)
+ /* Set hostmask to '-1'. */
+ switch (dst->sa_family) {
+ case AF_INET:
+ ((struct sockaddr_in *) hostmask)->sin_family =
+ AF_INET;
+#ifndef USE_OLD_SOCKADDR
+ ((struct sockaddr_in *) hostmask)->sin_len =
+ sizeof(struct in_addr);
+#endif
+ memset(&((struct sockaddr_in *) hostmask)->sin_addr.s_addr,
+ 0xff, sizeof(struct in_addr));
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *) hostmask)->sin6_family =
+ AF_INET6;
+#ifndef USE_OLD_SOCKADDR
+ ((struct sockaddr_in6 *) hostmask)->sin6_len =
+ sizeof(struct in6_addr);
+#endif
+ memset(&((struct sockaddr_in6 *) hostmask)->sin6_addr.s6_addr,
+ 0xff, sizeof(struct in6_addr));
+ break;
+ }
+
+ /* Ingress flow --- SA bundles */
+ while (TAILQ_NEXT(proto, link)) {
+ error = pf_key_v2_flow(dst, hostmask, src, hostmask,
+ 0, 0, 0, proto->spi[1], proto->proto, src, dst,
+ 1, 1, 0, 0, 0, 0, 0, 0, proto->data);
+ if (error)
+ return error;
+ proto = TAILQ_NEXT(proto, link);
+ }
+#endif /* SADB_X_EXT_FLOW_TYPE */
+
+ return pf_key_v2_flow(isa->dst_net, isa->dst_mask,
+ isa->src_net, isa->src_mask, isa->tproto, isa->dport,
+ isa->sport, proto->spi[1], proto->proto, src, dst, 1, 1,
+ 0, 0, 0, 0, 0, 0, proto->data);
+ }
+}
+
+/*
+ * Delete the IPsec SA represented by the INCOMING direction in protocol PROTO
+ * of the IKE security association SA. Also delete potential flows tied to it.
+ */
+int
+pf_key_v2_delete_spi(struct sa *sa, struct proto *proto, int incoming)
+{
+ struct sadb_msg msg;
+ struct sadb_sa ssa;
+ struct sadb_address *addr = 0;
+ struct sockaddr *saddr;
+ int len, err;
+ struct pf_key_v2_msg *delete = 0, *ret = 0;
+#ifdef KAME
+ struct sadb_x_sa2 ssa2;
+#endif
+
+ /* If it's not an established SA, don't proceed. */
+ if (!(sa->flags & SA_FLAG_READY))
+ return 0;
+
+ /*
+ * If the SA was not replaced and was not one acquired through the
+ * kernel (ACQUIRE message), remove the flow associated with it.
+ * We ignore any errors from the disabling of the flow.
+ */
+ if (!(sa->flags & SA_FLAG_REPLACED)
+ && !(sa->flags & SA_FLAG_ONDEMAND))
+ pf_key_v2_disable_sa(sa, incoming);
+
+ if (sa->name && !(sa->flags & SA_FLAG_REPLACED)) {
+ LOG_DBG((LOG_SYSDEP, 50,
+ "pf_key_v2_delete_spi: removing configuration %s",
+ sa->name));
+ pf_key_v2_remove_conf(sa->name);
+ }
+ msg.sadb_msg_type = SADB_DELETE;
+ switch (proto->proto) {
+ case IPSEC_PROTO_IPSEC_ESP:
+ msg.sadb_msg_satype = SADB_SATYPE_ESP;
+ break;
+ case IPSEC_PROTO_IPSEC_AH:
+ msg.sadb_msg_satype = SADB_SATYPE_AH;
+ break;
+#if defined (SADB_X_SATYPE_IPCOMP)
+ case IPSEC_PROTO_IPCOMP:
+ msg.sadb_msg_satype = SADB_X_SATYPE_IPCOMP;
+ break;
+#endif
+ default:
+ log_print("pf_key_v2_delete_spi: invalid proto %d",
+ proto->proto);
+ goto cleanup;
+ }
+ msg.sadb_msg_seq = 0;
+ delete = pf_key_v2_msg_new(&msg, 0);
+ if (!delete)
+ goto cleanup;
+
+ /* Setup the SA extension. */
+ ssa.sadb_sa_exttype = SADB_EXT_SA;
+ ssa.sadb_sa_len = sizeof ssa / PF_KEY_V2_CHUNK;
+ memcpy(&ssa.sadb_sa_spi, proto->spi[incoming], sizeof ssa.sadb_sa_spi);
+ ssa.sadb_sa_replay = 0;
+ ssa.sadb_sa_state = 0;
+ ssa.sadb_sa_auth = 0;
+ ssa.sadb_sa_encrypt = 0;
+ ssa.sadb_sa_flags = 0;
+ if (pf_key_v2_msg_add(delete, (struct sadb_ext *)&ssa, 0) == -1)
+ goto cleanup;
+
+#ifdef KAME
+ memset(&ssa2, 0, sizeof ssa2);
+ ssa2.sadb_x_sa2_exttype = SADB_X_EXT_SA2;
+ ssa2.sadb_x_sa2_len = sizeof ssa2 / PF_KEY_V2_CHUNK;
+ ssa2.sadb_x_sa2_mode = 0;
+ if (pf_key_v2_msg_add(delete, (struct sadb_ext *)&ssa2, 0) == -1)
+ goto cleanup;
+#endif
+
+ /*
+ * Setup the ADDRESS extensions.
+ */
+ if (incoming)
+ sa->transport->vtbl->get_dst(sa->transport, &saddr);
+ else
+ sa->transport->vtbl->get_src(sa->transport, &saddr);
+ len = sizeof *addr + PF_KEY_V2_ROUND(sysdep_sa_len(saddr));
+ addr = calloc(1, len);
+ if (!addr)
+ goto cleanup;
+ addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+ addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
+#ifndef __OpenBSD__
+ addr->sadb_address_proto = 0;
+ addr->sadb_address_prefixlen = 0;
+#endif
+ addr->sadb_address_reserved = 0;
+ memcpy(addr + 1, saddr, sysdep_sa_len(saddr));
+ switch (saddr->sa_family) {
+ case AF_INET:
+ ((struct sockaddr_in *) (addr + 1))->sin_port = 0;
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *) (addr + 1))->sin6_port = 0;
+ break;
+ }
+ if (pf_key_v2_msg_add(delete, (struct sadb_ext *) addr,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ addr = 0;
+
+ if (incoming)
+ sa->transport->vtbl->get_src(sa->transport, &saddr);
+ else
+ sa->transport->vtbl->get_dst(sa->transport, &saddr);
+ len = sizeof *addr + PF_KEY_V2_ROUND(sysdep_sa_len(saddr));
+ addr = calloc(1, len);
+ if (!addr)
+ goto cleanup;
+ addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+ addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
+#ifndef __OpenBSD__
+ addr->sadb_address_proto = 0;
+ addr->sadb_address_prefixlen = 0;
+#endif
+ addr->sadb_address_reserved = 0;
+ memcpy(addr + 1, saddr, sysdep_sa_len(saddr));
+ switch (saddr->sa_family) {
+ case AF_INET:
+ ((struct sockaddr_in *) (addr + 1))->sin_port = 0;
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *) (addr + 1))->sin6_port = 0;
+ break;
+ }
+ if (pf_key_v2_msg_add(delete, (struct sadb_ext *) addr,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ addr = 0;
+
+ ret = pf_key_v2_call(delete);
+ pf_key_v2_msg_free(delete);
+ delete = 0;
+ if (!ret)
+ goto cleanup;
+ err = ((struct sadb_msg *)TAILQ_FIRST(ret)->seg)->sadb_msg_errno;
+ if (err) {
+ LOG_DBG((LOG_SYSDEP, 10, "pf_key_v2_delete_spi: DELETE: %s",
+ strerror(err)));
+ goto cleanup;
+ }
+ pf_key_v2_msg_free(ret);
+
+ LOG_DBG((LOG_SYSDEP, 50, "pf_key_v2_delete_spi: done"));
+
+ return 0;
+
+cleanup:
+ if (addr)
+ free(addr);
+ if (delete)
+ pf_key_v2_msg_free(delete);
+ if (ret)
+ pf_key_v2_msg_free(ret);
+ return -1;
+}
+
+static void
+pf_key_v2_stayalive(struct exchange *exchange, void *vconn, int fail)
+{
+ char *conn = vconn;
+ struct sa *sa;
+
+ /* XXX What if it is phase 1 ? */
+ sa = sa_lookup_by_name(conn, 2);
+ if (sa)
+ sa->flags |= SA_FLAG_STAYALIVE;
+
+ /*
+ * Remove failed configuration entry -- call twice because it is
+ * created with a Refcount of 2.
+ */
+ if (fail && (!exchange || exchange->name)) {
+ pf_key_v2_remove_conf(conn);
+ pf_key_v2_remove_conf(conn);
+ }
+}
+
+/* Check if a connection CONN exists, otherwise establish it. */
+void
+pf_key_v2_connection_check(char *conn)
+{
+ if (!sa_lookup_by_name(conn, 2)) {
+ LOG_DBG((LOG_SYSDEP, 70,
+ "pf_key_v2_connection_check: SA for %s missing", conn));
+ exchange_establish(conn, pf_key_v2_stayalive, conn);
+ } else
+ LOG_DBG((LOG_SYSDEP, 70, "pf_key_v2_connection_check: "
+ "SA for %s exists", conn));
+}
+
+/* Handle a PF_KEY lifetime expiration message PMSG. */
+static void
+pf_key_v2_expire(struct pf_key_v2_msg *pmsg)
+{
+ struct sadb_msg *msg;
+ struct sadb_sa *ssa;
+ struct sadb_address *dst;
+ struct sockaddr *dstaddr;
+ struct sadb_lifetime *life, *lifecurrent;
+ struct sa *sa;
+ struct pf_key_v2_node *lifenode, *ext;
+#ifdef USE_DEBUG
+ char *dst_str;
+#endif
+
+ msg = (struct sadb_msg *)TAILQ_FIRST(pmsg)->seg;
+ ext = pf_key_v2_find_ext(pmsg, SADB_EXT_SA);
+ if (!ext) {
+ log_print("pf_key_v2_expire: no SA extension found");
+ return;
+ }
+ ssa = ext->seg;
+ ext = pf_key_v2_find_ext(pmsg, SADB_EXT_ADDRESS_DST);
+ if (!ext) {
+ log_print("pf_key_v2_expire: "
+ "no destination address extension found");
+ return;
+ }
+ dst = ext->seg;
+ dstaddr = (struct sockaddr *) (dst + 1);
+ lifenode = pf_key_v2_find_ext(pmsg, SADB_EXT_LIFETIME_HARD);
+ if (!lifenode)
+ lifenode = pf_key_v2_find_ext(pmsg, SADB_EXT_LIFETIME_SOFT);
+ if (!lifenode) {
+ log_print("pf_key_v2_expire: no lifetime extension found");
+ return;
+ }
+ life = lifenode->seg;
+
+ lifenode = pf_key_v2_find_ext(pmsg, SADB_EXT_LIFETIME_CURRENT);
+ if (!lifenode) {
+ log_print("pf_key_v2_expire: "
+ "no current lifetime extension found");
+ return;
+ }
+ lifecurrent = lifenode->seg;
+
+#ifdef USE_DEBUG
+
+ if (sockaddr2text(dstaddr, &dst_str, 0))
+ dst_str = 0;
+
+ LOG_DBG((LOG_SYSDEP, 20, "pf_key_v2_expire: "
+ "%s dst %s SPI %x sproto %d",
+ life->sadb_lifetime_exttype == SADB_EXT_LIFETIME_SOFT ? "SOFT"
+ : "HARD", dst_str ? dst_str : "<unknown>",
+ ntohl(ssa->sadb_sa_spi), msg->sadb_msg_satype));
+
+ if (dst_str)
+ free(dst_str);
+
+#endif /* USE_DEBUG */
+
+ /*
+ * Find the IPsec SA. The IPsec stack has two SAs for every IKE SA,
+ * one outgoing and one incoming, we regard expirations for any of
+ * them as an expiration of the full IKE SA. Likewise, in
+ * protection suites consisting of more than one protocol, any
+ * expired individual IPsec stack SA will be seen as an expiration
+ * of the full suite.
+ */
+ switch (msg->sadb_msg_satype) {
+ case SADB_SATYPE_ESP:
+ sa = ipsec_sa_lookup(dstaddr, ssa->sadb_sa_spi,
+ IPSEC_PROTO_IPSEC_ESP);
+ break;
+
+ case SADB_SATYPE_AH:
+ sa = ipsec_sa_lookup(dstaddr, ssa->sadb_sa_spi,
+ IPSEC_PROTO_IPSEC_AH);
+ break;
+
+#ifdef SADB_X_SATYPE_IPCOMP
+ case SADB_X_SATYPE_IPCOMP:
+ sa = ipsec_sa_lookup(dstaddr, ssa->sadb_sa_spi,
+ IPSEC_PROTO_IPCOMP);
+ break;
+#endif
+
+ default:
+ /* XXX Log? */
+ sa = 0;
+ break;
+ }
+
+ /* If the SA is already gone, don't do anything. */
+ if (!sa)
+ return;
+
+ /*
+ * If we got a notification, try to renegotiate the SA -- unless of
+ * course it has already been replaced by another.
+ * Also, ignore SAs that were not dynamically established, or that
+ * did not see any use.
+ */
+ if (!(sa->flags & SA_FLAG_REPLACED) &&
+ (sa->flags & SA_FLAG_ONDEMAND) &&
+ lifecurrent->sadb_lifetime_bytes)
+ exchange_establish(sa->name, 0, 0);
+
+ if (life->sadb_lifetime_exttype == SADB_EXT_LIFETIME_HARD) {
+ /* Remove the old SA, it isn't useful anymore. */
+ sa_free(sa);
+ }
+}
+
+/* Handle a PF_KEY SA ACQUIRE message PMSG. */
+static void
+pf_key_v2_acquire(struct pf_key_v2_msg *pmsg)
+{
+#if defined (SADB_X_ASKPOLICY)
+ struct sadb_msg *msg, askpolicy_msg;
+ struct pf_key_v2_msg *askpolicy = 0, *ret = 0;
+ struct sadb_x_policy policy;
+ struct sadb_address *dst = 0, *src = 0;
+ struct sockaddr *dstaddr, *srcaddr = 0;
+ struct sadb_comb *scmb = 0;
+ struct sadb_prop *sprp = 0;
+ struct sadb_ident *srcident = 0, *dstident = 0;
+ char dstbuf[ADDRESS_MAX], srcbuf[ADDRESS_MAX], *peer = 0,
+ *conn = 0;
+ char confname[120];
+ char *srcid = 0, *dstid = 0, *prefstring = 0;
+ int slen, af, afamily, masklen, buflen;
+ struct sockaddr *smask, *sflow, *dmask, *dflow;
+ struct sadb_protocol *sproto;
+ char ssflow[ADDRESS_MAX], sdflow[ADDRESS_MAX];
+ char sdmask[ADDRESS_MAX], ssmask[ADDRESS_MAX];
+ char *sidtype = 0, *didtype = 0;
+ char lname[100], dname[100], configname[30];
+ int shostflag = 0, dhostflag = 0;
+ struct pf_key_v2_node *ext;
+ struct passwd *pwd = 0;
+ u_int16_t sport = 0, dport = 0;
+ u_int8_t tproto = 0;
+ char tmbuf[sizeof sport * 3 + 1], *xform;
+ int connlen;
+#if defined (SADB_X_CREDTYPE_NONE)
+ struct sadb_x_cred *cred = 0, *sauth = 0;
+#endif
+
+ /* This needs to be dynamically allocated. */
+ connlen = 22;
+ conn = malloc(connlen);
+ if (!conn) {
+ log_error("pf_key_v2_acquire: malloc (%d) failed", connlen);
+ return;
+ }
+ msg = (struct sadb_msg *)TAILQ_FIRST(pmsg)->seg;
+
+ ext = pf_key_v2_find_ext(pmsg, SADB_EXT_ADDRESS_DST);
+ if (!ext) {
+ log_print("pf_key_v2_acquire: "
+ "no destination address specified");
+ return;
+ }
+ dst = ext->seg;
+
+ ext = pf_key_v2_find_ext(pmsg, SADB_EXT_ADDRESS_SRC);
+ if (ext)
+ src = ext->seg;
+
+ ext = pf_key_v2_find_ext(pmsg, SADB_EXT_PROPOSAL);
+ if (ext) {
+ sprp = ext->seg;
+ scmb = (struct sadb_comb *) (sprp + 1);
+ }
+ ext = pf_key_v2_find_ext(pmsg, SADB_EXT_IDENTITY_SRC);
+ if (ext)
+ srcident = ext->seg;
+
+ ext = pf_key_v2_find_ext(pmsg, SADB_EXT_IDENTITY_DST);
+ if (ext)
+ dstident = ext->seg;
+
+ /* Ask the kernel for the matching policy. */
+ bzero(&askpolicy_msg, sizeof askpolicy_msg);
+ askpolicy_msg.sadb_msg_type = SADB_X_ASKPOLICY;
+ askpolicy = pf_key_v2_msg_new(&askpolicy_msg, 0);
+ if (!askpolicy)
+ goto fail;
+
+ policy.sadb_x_policy_exttype = SADB_X_EXT_POLICY;
+ policy.sadb_x_policy_len = sizeof policy / PF_KEY_V2_CHUNK;
+ policy.sadb_x_policy_seq = msg->sadb_msg_seq;
+ if (pf_key_v2_msg_add(askpolicy, (struct sadb_ext *)&policy, 0) == -1)
+ goto fail;
+
+ ret = pf_key_v2_call(askpolicy);
+ if (!ret)
+ goto fail;
+
+ /* Now we have all the information needed. */
+
+ ext = pf_key_v2_find_ext(ret, SADB_X_EXT_SRC_FLOW);
+ if (!ext) {
+ log_print("pf_key_v2_acquire: no source flow extension found");
+ goto fail;
+ }
+ sflow = (struct sockaddr *) (((struct sadb_address *) ext->seg) + 1);
+
+ ext = pf_key_v2_find_ext(ret, SADB_X_EXT_DST_FLOW);
+ if (!ext) {
+ log_print("pf_key_v2_acquire: "
+ "no destination flow extension found");
+ goto fail;
+ }
+ dflow = (struct sockaddr *) (((struct sadb_address *) ext->seg) + 1);
+ ext = pf_key_v2_find_ext(ret, SADB_X_EXT_SRC_MASK);
+ if (!ext) {
+ log_print("pf_key_v2_acquire: no source mask extension found");
+ goto fail;
+ }
+ smask = (struct sockaddr *) (((struct sadb_address *) ext->seg) + 1);
+
+ ext = pf_key_v2_find_ext(ret, SADB_X_EXT_DST_MASK);
+ if (!ext) {
+ log_print("pf_key_v2_acquire: "
+ "no destination mask extension found");
+ goto fail;
+ }
+ dmask = (struct sockaddr *) (((struct sadb_address *) ext->seg) + 1);
+
+ ext = pf_key_v2_find_ext(ret, SADB_X_EXT_FLOW_TYPE);
+ if (!ext) {
+ log_print("pf_key_v2_acquire: no flow type extension found");
+ goto fail;
+ }
+ sproto = ext->seg;
+ tproto = sproto->sadb_protocol_proto;
+
+#if defined (SADB_X_EXT_LOCAL_CREDENTIALS)
+ ext = pf_key_v2_find_ext(pmsg, SADB_X_EXT_LOCAL_CREDENTIALS);
+ if (ext)
+ cred = (struct sadb_x_cred *) ext->seg;
+ else
+ cred = 0;
+#endif
+
+#if defined (SADB_X_EXT_LOCAL_AUTH)
+ ext = pf_key_v2_find_ext(pmsg, SADB_X_EXT_LOCAL_AUTH);
+ if (ext)
+ sauth = (struct sadb_x_cred *) ext->seg;
+ else
+ sauth = 0;
+#endif
+
+ bzero(ssflow, sizeof ssflow);
+ bzero(sdflow, sizeof sdflow);
+ bzero(ssmask, sizeof ssmask);
+ bzero(sdmask, sizeof sdmask);
+
+ sidtype = didtype = "IPV4_ADDR_SUBNET"; /* default */
+
+ switch (sflow->sa_family) {
+ case AF_INET:
+ if (inet_ntop(AF_INET,
+ &((struct sockaddr_in *) sflow)->sin_addr, ssflow,
+ ADDRESS_MAX) == NULL) {
+ log_print("pf_key_v2_acquire: inet_ntop failed");
+ goto fail;
+ }
+ sport = ((struct sockaddr_in *) sflow)->sin_port;
+ if (inet_ntop(AF_INET,
+ &((struct sockaddr_in *) dflow)->sin_addr, sdflow,
+ ADDRESS_MAX) == NULL) {
+ log_print("pf_key_v2_acquire: inet_ntop failed");
+ goto fail;
+ }
+ dport = ((struct sockaddr_in *) dflow)->sin_port;
+ if (inet_ntop(AF_INET,
+ &((struct sockaddr_in *) smask)->sin_addr, ssmask,
+ ADDRESS_MAX) == NULL) {
+ log_print("pf_key_v2_acquire: inet_ntop failed");
+ goto fail;
+ }
+ if (inet_ntop(AF_INET,
+ &((struct sockaddr_in *) dmask)->sin_addr, sdmask,
+ ADDRESS_MAX) == NULL) {
+ log_print("pf_key_v2_acquire: inet_ntop failed");
+ goto fail;
+ }
+ if (((struct sockaddr_in *) smask)->sin_addr.s_addr ==
+ INADDR_BROADCAST) {
+ shostflag = 1;
+ sidtype = "IPV4_ADDR";
+ }
+ if (((struct sockaddr_in *) dmask)->sin_addr.s_addr ==
+ INADDR_BROADCAST) {
+ dhostflag = 1;
+ didtype = "IPV4_ADDR";
+ }
+ break;
+
+ case AF_INET6:
+ if (inet_ntop(AF_INET6,
+ &((struct sockaddr_in6 *) sflow)->sin6_addr,
+ ssflow, ADDRESS_MAX) == NULL) {
+ log_print("pf_key_v2_acquire: inet_ntop failed");
+ goto fail;
+ }
+ sport = ((struct sockaddr_in6 *) sflow)->sin6_port;
+ if (inet_ntop(AF_INET6,
+ &((struct sockaddr_in6 *) dflow)->sin6_addr,
+ sdflow, ADDRESS_MAX) == NULL) {
+ log_print("pf_key_v2_acquire: inet_ntop failed");
+ goto fail;
+ }
+ dport = ((struct sockaddr_in6 *) dflow)->sin6_port;
+ if (inet_ntop(AF_INET6,
+ &((struct sockaddr_in6 *) smask)->sin6_addr,
+ ssmask, ADDRESS_MAX) == NULL) {
+ log_print("pf_key_v2_acquire: inet_ntop failed");
+ goto fail;
+ }
+ if (inet_ntop(AF_INET6,
+ &((struct sockaddr_in6 *) dmask)->sin6_addr,
+ sdmask, ADDRESS_MAX) == NULL) {
+ log_print("pf_key_v2_acquire: inet_ntop failed");
+ goto fail;
+ }
+ sidtype = didtype = "IPV6_ADDR_SUBNET";
+ if (IN6_IS_ADDR_FULL(&((struct sockaddr_in6 *)smask)->sin6_addr)) {
+ shostflag = 1;
+ sidtype = "IPV6_ADDR";
+ }
+ if (IN6_IS_ADDR_FULL(&((struct sockaddr_in6 *)dmask)->sin6_addr)) {
+ dhostflag = 1;
+ didtype = "IPV6_ADDR";
+ }
+ break;
+ }
+
+ dstaddr = (struct sockaddr *)(dst + 1);
+ bzero(dstbuf, sizeof dstbuf);
+ bzero(srcbuf, sizeof srcbuf);
+
+ if (dstaddr->sa_family == 0) {
+ /*
+ * Destination was not specified in the flow -- can we derive
+ * it?
+ */
+ if (dhostflag == 0) {
+ log_print("pf_key_v2_acquire: "
+ "Cannot determine precise destination");
+ goto fail;
+ }
+ dstaddr = dflow;
+ }
+ switch (dstaddr->sa_family) {
+ case AF_INET:
+ if (inet_ntop(AF_INET,
+ &((struct sockaddr_in *) dstaddr)->sin_addr,
+ dstbuf, ADDRESS_MAX) == NULL) {
+ log_print("pf_key_v2_acquire: inet_ntop failed");
+ goto fail;
+ }
+ LOG_DBG((LOG_SYSDEP, 20,
+ "pf_key_v2_acquire: dst=%s sproto %d", dstbuf,
+ msg->sadb_msg_satype));
+ break;
+
+ case AF_INET6:
+ if (inet_ntop(AF_INET6,
+ &((struct sockaddr_in6 *) dstaddr)->sin6_addr,
+ dstbuf, ADDRESS_MAX) == NULL) {
+ log_print("pf_key_v2_acquire: inet_ntop failed");
+ goto fail;
+ }
+ LOG_DBG((LOG_SYSDEP, 20,
+ "pf_key_v2_acquire: dst=%s sproto %d", dstbuf,
+ msg->sadb_msg_satype));
+ break;
+ }
+
+ if (src) {
+ srcaddr = (struct sockaddr *) (src + 1);
+
+ switch (srcaddr->sa_family) {
+ case AF_INET:
+ if (inet_ntop(AF_INET,
+ &((struct sockaddr_in *) srcaddr)->sin_addr,
+ srcbuf, ADDRESS_MAX) == NULL) {
+ log_print("pf_key_v2_acquire: "
+ "inet_ntop failed");
+ goto fail;
+ }
+ break;
+
+ case AF_INET6:
+ if (inet_ntop(AF_INET6,
+ &((struct sockaddr_in6 *)srcaddr)->sin6_addr,
+ srcbuf, ADDRESS_MAX) == NULL) {
+ log_print("pf_key_v2_acquire: "
+ "inet_ntop failed");
+ goto fail;
+ }
+ break;
+
+ default:
+ /*
+ * The kernel will pass an all '0' EXT_ADDRESS_SRC if
+ * it wasn't specified for the flow. In that case, do
+ * NOT specify the srcaddr in the Peer-name below
+ */
+ srcbuf[0] = 0;
+ srcaddr = NULL;
+ break;
+ }
+ }
+ /* Insert source ID. */
+ if (srcident) {
+ slen = (srcident->sadb_ident_len * sizeof(u_int64_t))
+ - sizeof(struct sadb_ident);
+ if (((unsigned char *) (srcident + 1))[slen - 1] != '\0') {
+ log_print("pf_key_v2_acquire: "
+ "source identity not NUL-terminated");
+ goto fail;
+ }
+ /* Check for valid type. */
+ switch (srcident->sadb_ident_type) {
+#if defined (SADB_X_IDENTTYPE_CONNECTION)
+ case SADB_X_IDENTTYPE_CONNECTION:
+ /* XXX */
+ break;
+#endif
+
+ case SADB_IDENTTYPE_PREFIX:
+ /* Determine what the address family is. */
+ srcid = memchr(srcident + 1, ':', slen);
+ if (srcid)
+ afamily = AF_INET6;
+ else
+ afamily = AF_INET;
+
+ srcid = memchr(srcident + 1, '/', slen);
+ if (!srcid) {
+ log_print("pf_key_v2_acquire: "
+ "badly formatted PREFIX identity");
+ goto fail;
+ }
+ masklen = atoi(srcid + 1);
+
+ /* XXX We only support host addresses. */
+ if ((afamily == AF_INET6 && masklen != 128)
+ || (afamily == AF_INET && masklen != 32)) {
+ log_print("pf_key_v2_acquire: "
+ "non-host address specified in source "
+ "identity (mask length %d), ignoring "
+ "request", masklen);
+ goto fail;
+ }
+ /*
+ * NUL-terminate the PREFIX string at the separator,
+ * then dup.
+ */
+ *srcid = '\0';
+ slen = strlen((char *) (srcident + 1)) +
+ sizeof "ID:Address/";
+ srcid = malloc(slen);
+ if (!srcid) {
+ log_error("pf_key_v2_acquire: "
+ "malloc (%d) failed", slen);
+ goto fail;
+ }
+ snprintf(srcid, slen, "ID:Address/%s",
+ (char *) (srcident + 1));
+
+ /* Set the section if it doesn't already exist. */
+ af = conf_begin();
+ if (!conf_get_str(srcid, "ID-type")) {
+ if (conf_set(af, srcid, "ID-type",
+ afamily == AF_INET ? "IPV4_ADDR" :
+ "IPV6_ADDR", 1, 0)
+ || conf_set(af, srcid, "Refcount", "1", 1,
+ 0)
+ || conf_set(af, srcid, "Address",
+ (char *) (srcident + 1), 1, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ } else
+ pf_key_v2_conf_refinc(af, srcid);
+ conf_end(af, 1);
+ break;
+
+ case SADB_IDENTTYPE_FQDN:
+ prefstring = "FQDN";
+ /* Fall through */
+ case SADB_IDENTTYPE_USERFQDN:
+ if (!prefstring) {
+ prefstring = "USER_FQDN";
+
+ /*
+ * Check whether there is a string following
+ * the header; if no, that there is a user ID
+ * (and acquire the login name). If there is
+ * both a string and a user ID, check that
+ * they match.
+ */
+ if ((slen == 0) &&
+ (srcident->sadb_ident_id == 0)) {
+ log_print("pf_key_v2_acquire: "
+ "no user FQDN or ID provided");
+ goto fail;
+ }
+ if (srcident->sadb_ident_id) {
+ pwd =
+ getpwuid(srcident->sadb_ident_id);
+ if (!pwd) {
+ log_error("pf_key_v2_acquire: "
+ "could not acquire "
+ "username from provided "
+ "ID %llu",
+ srcident->sadb_ident_id);
+ goto fail;
+ }
+ if (slen != 0)
+ if (strcmp(pwd->pw_name,
+ (char *) (srcident + 1))
+ != 0) {
+ log_print("pf_key_v2_acquire: "
+ "provided user "
+ "name and ID do "
+ "not match (%s != "
+ "%s)",
+ (char *) (srcident + 1),
+ pwd->pw_name);
+ /*
+ * String has
+ * precedence, per
+ * RFC 2367.
+ */
+ }
+ }
+ }
+ buflen = (slen ? slen : strlen(pwd->pw_name)) +
+ strlen(prefstring) + sizeof "ID:/";
+ srcid = malloc(buflen);
+ if (!srcid) {
+ log_error("pf_key_v2_acquire: "
+ "malloc (%d) failed", buflen);
+ goto fail;
+ }
+ snprintf(srcid, buflen, "ID:%s/", prefstring);
+ if (slen != 0)
+ strlcat(srcid,
+ (char *) (srcident + 1), buflen);
+ else
+ strlcat(srcid, pwd->pw_name, buflen);
+ pwd = 0;
+
+ /* Set the section if it doesn't already exist. */
+ af = conf_begin();
+ if (!conf_get_str(srcid, "ID-type")) {
+ if (conf_set(af, srcid, "ID-type", prefstring,
+ 1, 0)
+ || conf_set(af, srcid, "Refcount", "1", 1,
+ 0)
+ || conf_set(af, srcid, "Name",
+ srcid + sizeof "ID:/" - 1 +
+ strlen(prefstring), 1, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ } else
+ pf_key_v2_conf_refinc(af, srcid);
+ conf_end(af, 1);
+ break;
+
+ default:
+ LOG_DBG((LOG_SYSDEP, 20,
+ "pf_key_v2_acquire: invalid source ID type %d",
+ srcident->sadb_ident_type));
+ goto fail;
+ }
+
+ LOG_DBG((LOG_SYSDEP, 50,
+ "pf_key_v2_acquire: constructed source ID \"%s\"", srcid));
+ prefstring = 0;
+ }
+ /* Insert destination ID. */
+ if (dstident) {
+ slen = (dstident->sadb_ident_len * sizeof(u_int64_t))
+ - sizeof(struct sadb_ident);
+
+ /* Check for valid type. */
+ switch (dstident->sadb_ident_type) {
+#if defined (SADB_X_IDENTTYPE_CONNECTION)
+ case SADB_X_IDENTTYPE_CONNECTION:
+ /* XXX */
+ break;
+#endif
+
+ case SADB_IDENTTYPE_PREFIX:
+ /* Determine what the address family is. */
+ dstid = memchr(dstident + 1, ':', slen);
+ if (dstid)
+ afamily = AF_INET6;
+ else
+ afamily = AF_INET;
+
+ dstid = memchr(dstident + 1, '/', slen);
+ if (!dstid) {
+ log_print("pf_key_v2_acquire: "
+ "badly formatted PREFIX identity");
+ goto fail;
+ }
+ masklen = atoi(dstid + 1);
+
+ /* XXX We only support host addresses. */
+ if ((afamily == AF_INET6 && masklen != 128)
+ || (afamily == AF_INET && masklen != 32)) {
+ log_print("pf_key_v2_acquire: "
+ "non-host address specified in "
+ "destination identity (mask length %d), "
+ "ignoring request", masklen);
+ goto fail;
+ }
+ /*
+ * NUL-terminate the PREFIX string at the separator,
+ * then dup.
+ */
+ *dstid = '\0';
+ slen = strlen((char *) (dstident + 1)) +
+ sizeof "ID:Address/";
+ dstid = malloc(slen);
+ if (!dstid) {
+ log_error("pf_key_v2_acquire: "
+ "malloc (%d) failed", slen);
+ goto fail;
+ }
+ snprintf(dstid, slen, "ID:Address/%s",
+ (char *) (dstident + 1));
+
+ /* Set the section if it doesn't already exist. */
+ af = conf_begin();
+ if (!conf_get_str(dstid, "ID-type")) {
+ if (conf_set(af, dstid, "ID-type",
+ afamily == AF_INET ? "IPV4_ADDR" :
+ "IPV6_ADDR", 1, 0)
+ || conf_set(af, dstid, "Refcount", "1", 1,
+ 0)
+ || conf_set(af, dstid, "Address",
+ (char *) (dstident + 1), 1, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ } else
+ pf_key_v2_conf_refinc(af, dstid);
+ conf_end(af, 1);
+ break;
+
+ case SADB_IDENTTYPE_FQDN:
+ prefstring = "FQDN";
+ /* Fall through */
+
+ case SADB_IDENTTYPE_USERFQDN:
+ if (!prefstring) {
+ prefstring = "USER_FQDN";
+
+ /*
+ * Check whether there is a string following
+ * the header; if no, that there is a user ID
+ * (and acquire the login name). If there is
+ * both a string and a user ID, check that
+ * they match.
+ */
+ if (slen == 0 &&
+ dstident->sadb_ident_id == 0) {
+ log_print("pf_key_v2_acquire: "
+ "no user FQDN or ID provided");
+ goto fail;
+ }
+ if (dstident->sadb_ident_id) {
+ pwd = getpwuid(dstident->sadb_ident_id);
+ if (!pwd) {
+ log_error("pf_key_v2_acquire: "
+ "could not acquire "
+ "username from provided "
+ "ID %llu",
+ dstident->sadb_ident_id);
+ goto fail;
+ }
+ if (slen != 0)
+ if (strcmp(pwd->pw_name,
+ (char *) (dstident + 1))
+ != 0) {
+ log_print("pf_key_v2_acquire: "
+ "provided user "
+ "name and ID do "
+ "not match (%s != "
+ "%s)",
+ (char *) (dstident + 1),
+ pwd->pw_name);
+ /*
+ * String has
+ * precedence, per RF
+ * 2367.
+ */
+ }
+ }
+ }
+ buflen = (slen ? slen : strlen(pwd->pw_name)) +
+ strlen(prefstring) + sizeof "ID:/";
+ dstid = malloc(buflen);
+ if (!dstid) {
+ log_error("pf_key_v2_acquire: "
+ "malloc (%d) failed", buflen);
+ goto fail;
+ }
+ snprintf(dstid, buflen, "ID:%s/", prefstring);
+ if (slen != 0)
+ strlcat(dstid, (char *) (dstident + 1),
+ buflen);
+ else
+ strlcat(dstid, pwd->pw_name, buflen);
+ pwd = 0;
+
+ /* Set the section if it doesn't already exist. */
+ af = conf_begin();
+ if (!conf_get_str(dstid, "ID-type")) {
+ if (conf_set(af, dstid, "ID-type", prefstring,
+ 1, 0)
+ || conf_set(af, dstid, "Refcount", "1", 1,
+ 0)
+ || conf_set(af, dstid, "Name",
+ dstid + sizeof "ID:/" - 1 +
+ strlen(prefstring), 1, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ } else
+ pf_key_v2_conf_refinc(af, dstid);
+ conf_end(af, 1);
+ break;
+
+ default:
+ LOG_DBG((LOG_SYSDEP, 20, "pf_key_v2_acquire: "
+ "invalid destination ID type %d",
+ dstident->sadb_ident_type));
+ goto fail;
+ }
+
+ LOG_DBG((LOG_SYSDEP, 50,
+ "pf_key_v2_acquire: constructed destination ID \"%s\"",
+ dstid));
+ }
+ /* Now we've placed the necessary IDs in the configuration space. */
+
+ /* Get a new connection sequence number. */
+ for (;; connection_seq++) {
+ snprintf(conn, connlen, "Connection-%u", connection_seq);
+ snprintf(configname, sizeof configname, "Config-Phase2-%u",
+ connection_seq);
+
+ /* Does it exist ? */
+ if (!conf_get_str(conn, "Phase")
+ && !conf_get_str(configname, "Suites"))
+ break;
+ }
+
+ /*
+ * Set the IPsec connection entry. In particular, the following fields:
+ * - Phase
+ * - ISAKMP-peer
+ * - Local-ID/Remote-ID (if provided)
+ * - Acquire-ID (sequence number of kernel message, e.g., PF_KEYv2)
+ * - Configuration
+ *
+ * Also set the following section:
+ * [Peer-dstaddr(/srcaddr)(-srcid)(/dstid)]
+ * with these fields:
+ * - Phase
+ * - ID (if provided)
+ * - Remote-ID (if provided)
+ * - Local-address (if provided)
+ * - Address
+ * - Configuration (if an entry ISAKMP-configuration-dstaddr(/srcaddr)
+ * exists -- otherwise use the defaults)
+ */
+
+ slen = strlen(dstbuf) + strlen(srcbuf) + (srcid ? strlen(srcid) : 0)
+ + (dstid ? strlen(dstid) : 0) + sizeof "Peer-/-/";
+ peer = malloc(slen);
+ if (!peer)
+ goto fail;
+
+ /*
+ * The various cases:
+ * - Peer-dstaddr
+ * - Peer-dstaddr/srcaddr
+ * - Peer-dstaddr/srcaddr-srcid
+ * - Peer-dstaddr/srcaddr-srcid/dstid
+ * - Peer-dstaddr/srcaddr-/dstid
+ * - Peer-dstaddr-srcid/dstid
+ * - Peer-dstaddr-/dstid
+ * - Peer-dstaddr-srcid
+ */
+ snprintf(peer, slen, "Peer-%s%s%s%s%s%s%s", dstbuf, srcaddr ? "/" : "",
+ srcaddr ? srcbuf : "", srcid ? "-" : "", srcid ? srcid : "",
+ dstid ? (srcid ? "/" : "-/") : "", dstid ? dstid : "");
+
+ /*
+ * Set the IPsec connection section. Refcount is set to 2, because
+ * it will be linked both to the incoming and the outgoing SA.
+ */
+ af = conf_begin();
+ if (conf_set(af, conn, "Phase", "2", 0, 0)
+ || conf_set(af, conn, "Flags", "__ondemand", 0, 0)
+ || conf_set(af, conn, "Refcount", "2", 0, 0)
+ || conf_set(af, conn, "ISAKMP-peer", peer, 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ /* Set the sequence number. */
+ snprintf(lname, sizeof lname, "%u", msg->sadb_msg_seq);
+ if (conf_set(af, conn, "Acquire-ID", lname, 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ /* Set Phase 2 IDs -- this is the Local-ID section. */
+ snprintf(lname, sizeof lname, "Phase2-ID:%s/%s/%u/%u", ssflow, ssmask,
+ tproto, sport);
+ if (conf_set(af, conn, "Local-ID", lname, 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ if (!conf_get_str(lname, "ID-type")) {
+ if (conf_set(af, lname, "Refcount", "1", 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ if (shostflag) {
+ if (conf_set(af, lname, "ID-type", sidtype, 0, 0)
+ || conf_set(af, lname, "Address", ssflow, 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ } else {
+ if (conf_set(af, lname, "ID-type", sidtype, 0, 0)
+ || conf_set(af, lname, "Network", ssflow, 0, 0)
+ || conf_set(af, lname, "Netmask", ssmask, 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ }
+ if (tproto) {
+ snprintf(tmbuf, sizeof sport * 3 + 1, "%u", tproto);
+ if (conf_set(af, lname, "Protocol", tmbuf, 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ if (sport) {
+ snprintf(tmbuf, sizeof sport * 3 + 1, "%u",
+ ntohs(sport));
+ if (conf_set(af, lname, "Port", tmbuf, 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ }
+ }
+ } else
+ pf_key_v2_conf_refinc(af, lname);
+
+ /* Set Remote-ID section. */
+ snprintf(dname, sizeof dname, "Phase2-ID:%s/%s/%u/%u", sdflow, sdmask,
+ tproto, dport);
+ if (conf_set(af, conn, "Remote-ID", dname, 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ if (!conf_get_str(dname, "ID-type")) {
+ if (conf_set(af, dname, "Refcount", "1", 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ if (dhostflag) {
+ if (conf_set(af, dname, "ID-type", didtype, 0, 0)
+ || conf_set(af, dname, "Address", sdflow, 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ } else {
+ if (conf_set(af, dname, "ID-type", didtype, 0, 0)
+ || conf_set(af, dname, "Network", sdflow, 0, 0)
+ || conf_set(af, dname, "Netmask", sdmask, 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ }
+
+ if (tproto) {
+ snprintf(tmbuf, sizeof dport * 3 + 1, "%u", tproto);
+ if (conf_set(af, dname, "Protocol", tmbuf, 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ if (dport) {
+ snprintf(tmbuf, sizeof dport * 3 + 1, "%u",
+ ntohs(dport));
+ if (conf_set(af, dname, "Port", tmbuf, 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ }
+ }
+ } else
+ pf_key_v2_conf_refinc(af, dname);
+
+ /*
+ * XXX
+ * We should be using information from the proposal to set this up.
+ * At least, we should make this selectable.
+ */
+
+ /* Phase 2 configuration. */
+ if (conf_set(af, conn, "Configuration", configname, 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ if (conf_set(af, configname, "Exchange_type", "Quick_mode", 0, 0)
+ || conf_set(af, configname, "DOI", "IPSEC", 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ if (conf_get_str("General", "Default-phase-2-suites")) {
+ if (conf_set(af, configname, "Suites",
+ conf_get_str("General", "Default-phase-2-suites"), 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ } else {
+ if (conf_set(af, configname, "Suites",
+ "QM-ESP-3DES-SHA-PFS-SUITE", 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ }
+
+ /* Set the ISAKMP-peer section. */
+ if (!conf_get_str(peer, "Phase")) {
+ if (conf_set(af, peer, "Phase", "1", 0, 0)
+ || conf_set(af, peer, "Refcount", "1", 0, 0)
+ || conf_set(af, peer, "Address", dstbuf, 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ if (srcaddr && conf_set(af, peer, "Local-address", srcbuf, 0,
+ 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ snprintf(confname, sizeof confname, "ISAKMP-Configuration-%s",
+ peer);
+ if (conf_set(af, peer, "Configuration", confname, 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+#if defined (SADB_X_CREDTYPE_NONE)
+ /* Store any credentials passed to us. */
+ if (cred) {
+ struct cert_handler *handler = 0;
+ void *cert;
+ char num[12], *certprint;
+
+ /* Convert to bytes in-place. */
+ cred->sadb_x_cred_len *= PF_KEY_V2_CHUNK;
+
+ if (cred->sadb_x_cred_len <= sizeof *cred) {
+ log_print("pf_key_v2_acquire: "
+ "zero-length credentials, aborting SA "
+ "acquisition");
+ conf_end(af, 0);
+ goto fail;
+ }
+ switch (cred->sadb_x_cred_type) {
+ case SADB_X_CREDTYPE_X509:
+ snprintf(num, sizeof num, "%d",
+ ISAKMP_CERTENC_X509_SIG);
+ handler = cert_get(ISAKMP_CERTENC_X509_SIG);
+ break;
+ case SADB_X_CREDTYPE_KEYNOTE:
+ snprintf(num, sizeof num, "%d",
+ ISAKMP_CERTENC_KEYNOTE);
+ handler = cert_get(ISAKMP_CERTENC_KEYNOTE);
+ break;
+ default:
+ log_print("pf_key_v2_acquire: "
+ "unknown credential type %d",
+ cred->sadb_x_cred_type);
+ conf_end(af, 0);
+ goto fail;
+ }
+
+ if (!handler) {
+ log_print("pf_key_v2_acquire: "
+ "cert_get (%s) failed", num);
+ conf_end(af, 0);
+ goto fail;
+ }
+ /* Set the credential type as a number. */
+ if (conf_set(af, peer, "Credential_type", num, 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ /* Get the certificate. */
+ cert = handler->cert_get((u_int8_t *) (cred + 1),
+ cred->sadb_x_cred_len - sizeof *cred);
+
+ /* Now convert to printable format. */
+ certprint = handler->cert_printable(cert);
+ handler->cert_free(cert);
+ if (!certprint
+ || conf_set(af, peer, "Credentials", certprint, 0,
+ 0)) {
+ if (certprint)
+ free(certprint);
+ conf_end(af, 0);
+ goto fail;
+ }
+ free(certprint);
+ }
+#endif /* SADB_X_CREDTYPE_NONE */
+
+ /* Phase 1 configuration. */
+ if (!conf_get_str(confname, "exchange_type")) {
+#if defined (SADB_X_EXT_LOCAL_AUTH)
+ /*
+ * We may have been provided with authentication
+ * material.
+ */
+ if (sauth) {
+ char *authm;
+
+ /* Convert to bytes in-place. */
+ sauth->sadb_x_cred_len *= PF_KEY_V2_CHUNK;
+
+ switch (sauth->sadb_x_cred_type) {
+ case SADB_X_AUTHTYPE_PASSPHRASE:
+ if (conf_set(af, confname,
+ "Transforms", "3DES-SHA", 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ if (sauth->sadb_x_cred_len <=
+ sizeof *sauth) {
+ log_print("pf_key_v2_acquire: "
+ "zero-length passphrase, "
+ "aborting SA acquisition");
+ conf_end(af, 0);
+ goto fail;
+ }
+ authm = malloc(sauth->sadb_x_cred_len -
+ sizeof *sauth + 1);
+ if (!authm) {
+ log_error("pf_key_v2_acquire: "
+ "malloc (%lu) failed",
+ sauth->sadb_x_cred_len -
+ (unsigned long) sizeof *sauth + 1);
+ conf_end(af, 0);
+ goto fail;
+ }
+ memcpy(authm, sauth + 1,
+ sauth->sadb_x_cred_len -
+ sizeof *sauth + 1);
+
+ /* Set the passphrase in the peer. */
+ if (conf_set(af, peer,
+ "Authentication", authm, 0, 0)) {
+ free(authm);
+ conf_end(af, 0);
+ goto fail;
+ }
+ free(authm);
+ break;
+
+ case SADB_X_AUTHTYPE_RSA:
+ if (conf_set(af, confname,
+ "Transforms", "3DES-SHA-RSA_SIG",
+ 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ if (sauth->sadb_x_cred_len <=
+ sizeof *sauth) {
+ log_print("pf_key_v2_acquire: "
+ "zero-length RSA key, "
+ "aborting SA acquisition");
+ conf_end(af, 0);
+ goto fail;
+ }
+ authm = key_printable(ISAKMP_KEY_RSA,
+ ISAKMP_KEYTYPE_PRIVATE,
+ (u_int8_t *) sauth + 1,
+ sauth->sadb_x_cred_len -
+ sizeof *sauth);
+ if (!authm) {
+ log_print("pf_key_v2_acquire: "
+ "failed to convert "
+ "private key to printable "
+ "format (size %lu)",
+ sauth->sadb_x_cred_len -
+ (unsigned long) sizeof *sauth);
+ conf_end(af, 0);
+ goto fail;
+ }
+ /*
+ * Set the key in the peer. We don't
+ * use "Authentication" to avoid
+ * potential conflicts with file-based
+ * configurations that use public key
+ * authentication but still specify
+ * an "Authentication" tag (typically
+ * as a remnant of passphrase-based
+ * testing).
+ */
+ if (conf_set(af, peer,
+ "PKAuthentication", authm, 0, 0)) {
+ free(authm);
+ conf_end(af, 0);
+ goto fail;
+ }
+ free(authm);
+ break;
+
+ default:
+ log_print("pf_key_v2_acquire: "
+ "unknown authentication "
+ "material type %d received from "
+ "kernel", sauth->sadb_x_cred_type);
+ conf_end(af, 0);
+ goto fail;
+ }
+ } else /* Fall through */
+#endif /* SADB_X_EXT_LOCAL_AUTH */
+ {
+ xform = conf_get_str(
+ "Default-phase-1-configuration",
+ "Transforms");
+ if (conf_set(af, confname, "Transforms",
+ xform ? xform : "3DES-SHA-RSA_SIG", 0,
+ 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ }
+
+ if (conf_set(af, confname, "Exchange_Type", "ID_PROT",
+ 0, 0)
+ || conf_set(af, confname, "DOI", "IPSEC", 0, 0)
+ || conf_set(af, confname, "Refcount", "1", 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ } else
+ pf_key_v2_conf_refinc(af, confname);
+
+ /* The ID we should use in Phase 1. */
+ if (srcid && conf_set(af, peer, "ID", srcid, 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ /* The ID the other side should use in Phase 1. */
+ if (dstid && conf_set(af, peer, "Remote-ID", dstid, 0, 0)) {
+ conf_end(af, 0);
+ goto fail;
+ }
+ } else
+ pf_key_v2_conf_refinc(af, peer);
+
+ /* All done. */
+ conf_end(af, 1);
+
+ /* Let's rock 'n roll. */
+ pf_key_v2_connection_check(conn);
+ conn = 0;
+
+ /* Fall-through to cleanup. */
+fail:
+ if (ret)
+ pf_key_v2_msg_free(ret);
+ if (askpolicy)
+ pf_key_v2_msg_free(askpolicy);
+ if (srcid)
+ free(srcid);
+ if (dstid)
+ free(dstid);
+ if (peer)
+ free(peer);
+ if (conn)
+ free(conn);
+ return;
+#else
+ /* acquire not supported */
+ return;
+#endif /* SADB_X_ASKPOLICY */
+}
+
+static void
+pf_key_v2_notify(struct pf_key_v2_msg *msg)
+{
+ switch (((struct sadb_msg *)TAILQ_FIRST(msg)->seg)->sadb_msg_type) {
+ case SADB_EXPIRE:
+ pf_key_v2_expire(msg);
+ break;
+
+ case SADB_ACQUIRE:
+ pf_key_v2_acquire(msg);
+ break;
+
+ default:
+ log_print("pf_key_v2_notify: unexpected message type (%d)",
+ ((struct sadb_msg *)TAILQ_FIRST(msg)->seg)->sadb_msg_type);
+ }
+ pf_key_v2_msg_free(msg);
+}
+
+void
+pf_key_v2_handler(int fd)
+{
+ struct pf_key_v2_msg *msg;
+#if !defined (LINUX_IPSEC)
+ int n;
+
+ /*
+ * As synchronous read/writes to the socket can have taken place
+ * between the select(2) call of the main loop and this handler, we
+ * need to recheck the readability.
+ */
+ if (ioctl(pf_key_v2_socket, FIONREAD, &n) == -1) {
+ log_error("pf_key_v2_handler: ioctl (%d, FIONREAD, &n) failed",
+ pf_key_v2_socket);
+ return;
+ }
+ if (!n)
+ return;
+#endif /* LINUX_IPSEC */
+
+ msg = pf_key_v2_read(0);
+ if (msg)
+ pf_key_v2_notify(msg);
+}
+
+/*
+ * Group 2 IPsec SAs given by the PROTO1 and PROTO2 protocols of the SA IKE
+ * security association in a chain.
+ * XXX Assumes OpenBSD GRPSPIS extension. Should probably be moved to sysdep.c
+ */
+int
+pf_key_v2_group_spis(struct sa *sa, struct proto *proto1,
+ struct proto *proto2, int incoming)
+{
+#if defined (SADB_X_GRPSPIS)
+ struct sadb_msg msg;
+ struct sadb_sa sa1, sa2;
+ struct sadb_address *addr = 0;
+ struct sadb_protocol protocol;
+ struct pf_key_v2_msg *grpspis = 0, *ret = 0;
+ struct sockaddr *saddr;
+ int err;
+ size_t len;
+#ifdef KAME
+ struct sadb_x_sa2 kamesa2;
+#endif
+
+ msg.sadb_msg_type = SADB_X_GRPSPIS;
+ switch (proto1->proto) {
+ case IPSEC_PROTO_IPSEC_ESP:
+ msg.sadb_msg_satype = SADB_SATYPE_ESP;
+ break;
+ case IPSEC_PROTO_IPSEC_AH:
+ msg.sadb_msg_satype = SADB_SATYPE_AH;
+ break;
+#if defined (SADB_X_SATYPE_IPCOMP)
+ case IPSEC_PROTO_IPCOMP:
+ msg.sadb_msg_satype = SADB_X_SATYPE_IPCOMP;
+ break;
+#endif
+ default:
+ log_print("pf_key_v2_group_spis: invalid proto %d",
+ proto1->proto);
+ goto cleanup;
+ }
+ msg.sadb_msg_seq = 0;
+ grpspis = pf_key_v2_msg_new(&msg, 0);
+ if (!grpspis)
+ goto cleanup;
+
+ /* Setup the SA extensions. */
+ sa1.sadb_sa_exttype = SADB_EXT_SA;
+ sa1.sadb_sa_len = sizeof sa1 / PF_KEY_V2_CHUNK;
+ memcpy(&sa1.sadb_sa_spi, proto1->spi[incoming],
+ sizeof sa1.sadb_sa_spi);
+ sa1.sadb_sa_replay = 0;
+ sa1.sadb_sa_state = 0;
+ sa1.sadb_sa_auth = 0;
+ sa1.sadb_sa_encrypt = 0;
+ sa1.sadb_sa_flags = 0;
+ if (pf_key_v2_msg_add(grpspis, (struct sadb_ext *)&sa1, 0) == -1)
+ goto cleanup;
+
+#ifndef KAME
+ sa2.sadb_sa_exttype = SADB_X_EXT_SA2;
+ sa2.sadb_sa_len = sizeof sa2 / PF_KEY_V2_CHUNK;
+ memcpy(&sa2.sadb_sa_spi, proto2->spi[incoming],
+ sizeof sa2.sadb_sa_spi);
+ sa2.sadb_sa_replay = 0;
+ sa2.sadb_sa_state = 0;
+ sa2.sadb_sa_auth = 0;
+ sa2.sadb_sa_encrypt = 0;
+ sa2.sadb_sa_flags = 0;
+ if (pf_key_v2_msg_add(grpspis, (struct sadb_ext *)&sa2, 0) == -1)
+ goto cleanup;
+#else
+ memset(&kamesa2, 0, sizeof kamesa2);
+ kamesa2.sadb_x_sa2_exttype = SADB_X_EXT_SA2;
+ kamesa2.sadb_x_sa2_len = sizeof kamesa2 / PF_KEY_V2_CHUNK;
+ kamesa2.sadb_x_sa2_mode = 0;
+ if (pf_key_v2_msg_add(grpspis, (struct sadb_ext *)&kamesa2, 0) == -1)
+ goto cleanup;
+#endif
+
+ /*
+ * Setup the ADDRESS extensions.
+ */
+ if (incoming)
+ sa->transport->vtbl->get_src(sa->transport, &saddr);
+ else
+ sa->transport->vtbl->get_dst(sa->transport, &saddr);
+ len = sizeof *addr + PF_KEY_V2_ROUND(sysdep_sa_len(saddr));
+ addr = calloc(1, len);
+ if (!addr)
+ goto cleanup;
+ addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+ addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
+#ifndef __OpenBSD__
+ addr->sadb_address_proto = 0;
+ addr->sadb_address_prefixlen = 0;
+#endif
+ addr->sadb_address_reserved = 0;
+ memcpy(addr + 1, saddr, sysdep_sa_len(saddr));
+ ((struct sockaddr_in *) (addr + 1))->sin_port = 0;
+ if (pf_key_v2_msg_add(grpspis, (struct sadb_ext *) addr,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ addr = 0;
+
+ addr = calloc(1, len);
+ if (!addr)
+ goto cleanup;
+ addr->sadb_address_exttype = SADB_X_EXT_DST2;
+ addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
+#ifndef __OpenBSD__
+ addr->sadb_address_proto = 0;
+ addr->sadb_address_prefixlen = 0;
+#endif
+ addr->sadb_address_reserved = 0;
+ memcpy(addr + 1, saddr, sysdep_sa_len(saddr));
+ ((struct sockaddr_in *) (addr + 1))->sin_port = 0;
+ if (pf_key_v2_msg_add(grpspis, (struct sadb_ext *) addr,
+ PF_KEY_V2_NODE_MALLOCED) == -1)
+ goto cleanup;
+ addr = 0;
+
+ /* Setup the PROTOCOL extension. */
+ protocol.sadb_protocol_exttype = SADB_X_EXT_PROTOCOL;
+ protocol.sadb_protocol_len = sizeof protocol / PF_KEY_V2_CHUNK;
+ switch (proto2->proto) {
+ case IPSEC_PROTO_IPSEC_ESP:
+ protocol.sadb_protocol_proto = SADB_SATYPE_ESP;
+ break;
+ case IPSEC_PROTO_IPSEC_AH:
+ protocol.sadb_protocol_proto = SADB_SATYPE_AH;
+ break;
+#if defined (SADB_X_SATYPE_IPCOMP)
+ case IPSEC_PROTO_IPCOMP:
+ protocol.sadb_protocol_proto = SADB_X_SATYPE_IPCOMP;
+ break;
+#endif
+ default:
+ log_print("pf_key_v2_group_spis: invalid proto %d",
+ proto2->proto);
+ goto cleanup;
+ }
+ protocol.sadb_protocol_reserved2 = 0;
+ if (pf_key_v2_msg_add(grpspis,
+ (struct sadb_ext *)&protocol, 0) == -1)
+ goto cleanup;
+
+ ret = pf_key_v2_call(grpspis);
+ pf_key_v2_msg_free(grpspis);
+ grpspis = 0;
+ if (!ret)
+ goto cleanup;
+ err = ((struct sadb_msg *)TAILQ_FIRST(ret)->seg)->sadb_msg_errno;
+ if (err) {
+ log_print("pf_key_v2_group_spis: GRPSPIS: %s", strerror(err));
+ goto cleanup;
+ }
+ pf_key_v2_msg_free(ret);
+
+ LOG_DBG((LOG_SYSDEP, 50, "pf_key_v2_group_spis: done"));
+
+ return 0;
+
+cleanup:
+ if (addr)
+ free(addr);
+ if (grpspis)
+ pf_key_v2_msg_free(grpspis);
+ if (ret)
+ pf_key_v2_msg_free(ret);
+ return -1;
+
+#else /* SADB_X_GRPSPIS */
+ log_print("pf_key_v2_group_spis: not supported in pure PF_KEYv2");
+ return -1;
+#endif
+}