summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorreyk <reyk@openbsd.org>2010-06-03 16:41:12 +0000
committerreyk <reyk@openbsd.org>2010-06-03 16:41:12 +0000
commit45ae9d61c58ed01739444dfabb70017cfe21192b (patch)
tree88115785c36b98a40f000bd458a542325b7141cd
parenttypo in a comment (diff)
downloadwireguard-openbsd-45ae9d61c58ed01739444dfabb70017cfe21192b.tar.xz
wireguard-openbsd-45ae9d61c58ed01739444dfabb70017cfe21192b.zip
Import iked, a new implementation of the IKEv2 protocol.
iked(8) is an automatic keying daemon for IPsec, like isakmpd(8), that IPsec creates flows and SAs automatically. Unlike isakmpd, iked(8) implements the newer IKEv2 protocol instead of IKEv1/ISAKMP. The daemon is still work-in-progress and not enabled in the builds, but is already able to establish IKEv2 sessions with some other IKEv2 implementations as a responder. with lots of help and debugging by jsg@ ok deraadt@
-rw-r--r--sbin/iked/Makefile35
-rw-r--r--sbin/iked/ca.c840
-rw-r--r--sbin/iked/chap_ms.c412
-rw-r--r--sbin/iked/chap_ms.h52
-rw-r--r--sbin/iked/config.c643
-rw-r--r--sbin/iked/control.c295
-rw-r--r--sbin/iked/crypto.c686
-rw-r--r--sbin/iked/dh.c657
-rw-r--r--sbin/iked/dh.h62
-rw-r--r--sbin/iked/eap.c492
-rw-r--r--sbin/iked/eap.h172
-rw-r--r--sbin/iked/genmap.sh75
-rw-r--r--sbin/iked/iked.8129
-rw-r--r--sbin/iked/iked.c367
-rw-r--r--sbin/iked/iked.conf.5718
-rw-r--r--sbin/iked/iked.h670
-rw-r--r--sbin/iked/ikev1.c183
-rw-r--r--sbin/iked/ikev2.c4419
-rw-r--r--sbin/iked/ikev2.h515
-rw-r--r--sbin/iked/log.c193
-rw-r--r--sbin/iked/parse.y2316
-rw-r--r--sbin/iked/pfkey.c1082
-rw-r--r--sbin/iked/policy.c289
-rw-r--r--sbin/iked/proc.c332
-rw-r--r--sbin/iked/types.h121
-rw-r--r--sbin/iked/util.c943
-rw-r--r--usr.sbin/ikectl/Makefile23
27 files changed, 16721 insertions, 0 deletions
diff --git a/sbin/iked/Makefile b/sbin/iked/Makefile
new file mode 100644
index 00000000000..ecb7a725488
--- /dev/null
+++ b/sbin/iked/Makefile
@@ -0,0 +1,35 @@
+# $OpenBSD: Makefile,v 1.1 2010/06/03 16:41:12 reyk Exp $
+# $vantronix: Makefile,v 1.22 2010/06/02 12:22:58 reyk Exp $
+
+PROG= iked
+SRCS= dh.c iked.c ikev1.c ikev2.c log.c \
+ proc.c util.c config.c policy.c crypto.c ca.c pfkey.c \
+ control.c chap_ms.c eap.c
+SRCS+= parse.y
+SRCS+= ${.OBJDIR}/ikev2_map.c ${.OBJDIR}/eap_map.c
+MAN= iked.conf.5 iked.8
+#NOMAN= yes
+
+BINDIR= /sbin
+
+LDADD= -lutil -levent -lssl -lcrypto
+DPADD= ${LIBUTIL} ${LIBEVENT} ${LIBSSL} ${LIBCRYPTO}
+CFLAGS+= -Wall -I${.CURDIR}
+CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
+CFLAGS+= -Wmissing-declarations
+CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
+CFLAGS+= -Wsign-compare -Wbounded
+CLEANFILES+= y.tab.h ikev2_map.c eap_map.c
+GENERATED= ikev2_map.c eap_map.c
+
+ikev2_map.c: genmap.sh ikev2.h
+ /bin/sh ${.CURDIR}/genmap.sh ${.CURDIR}/ikev2.h ikev2 > $@
+ @touch $@
+
+eap_map.c: genmap.sh eap.h
+ /bin/sh ${.CURDIR}/genmap.sh ${.CURDIR}/eap.h eap > $@
+ @touch $@
+
+${PROG} beforedepend: ${GENERATED}
+
+.include <bsd.prog.mk>
diff --git a/sbin/iked/ca.c b/sbin/iked/ca.c
new file mode 100644
index 00000000000..8d34e8d0b3e
--- /dev/null
+++ b/sbin/iked/ca.c
@@ -0,0 +1,840 @@
+/* $OpenBSD: ca.c,v 1.1 2010/06/03 16:41:12 reyk Exp $ */
+/* $vantronix: ca.c,v 1.29 2010/06/02 12:22:58 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/uio.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <string.h>
+#include <getopt.h>
+#include <signal.h>
+#include <errno.h>
+#include <err.h>
+#include <pwd.h>
+#include <event.h>
+
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <openssl/engine.h>
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/pem.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+
+#include "iked.h"
+#include "ikev2.h"
+
+void ca_reset(struct iked *, void *);
+int ca_reload(struct iked *);
+
+int ca_getreq(struct iked *, struct imsg *);
+int ca_getcert(struct iked *, struct imsg *);
+int ca_getauth(struct iked *, struct imsg *);
+X509 *ca_by_subjectpubkey(X509_STORE *, u_int8_t *, size_t);
+X509 *ca_by_issuer(X509_STORE *, X509_NAME *);
+int ca_subjectpubkey_digest(X509 *, u_int8_t *, u_int *);
+int ca_validate_cert(struct iked *, struct iked_static_id *,
+ void *, size_t);
+struct ibuf *
+ ca_x509_serialize(X509 *);
+int ca_key_serialize(EVP_PKEY *, struct iked_id *);
+
+int ca_dispatch_parent(int, struct iked_proc *, struct imsg *);
+int ca_dispatch_ikev2(int, struct iked_proc *, struct imsg *);
+
+static struct iked_proc procs[] = {
+ { "parent", PROC_PARENT, ca_dispatch_parent },
+ { "ikev2", PROC_IKEV2, ca_dispatch_ikev2 }
+};
+
+struct ca_store {
+ X509_STORE *ca_cas;
+ X509_LOOKUP *ca_calookup;
+
+ X509_STORE *ca_certs;
+ X509_LOOKUP *ca_certlookup;
+
+ struct iked_id ca_privkey;
+};
+
+pid_t
+caproc(struct iked *env, struct iked_proc *p)
+{
+ struct ca_store *store;
+ FILE *fp = NULL;
+ EVP_PKEY *key;
+
+ /*
+ * This function runs code before privsep
+ */
+ if ((store = calloc(1, sizeof(*store))) == NULL)
+ fatal("ca: failed to allocate cert store");
+
+ if ((fp = fopen(IKED_PRIVKEY, "r")) == NULL)
+ fatal("ca: failed to open private key");
+
+ if ((key = PEM_read_PrivateKey(fp, NULL, NULL, NULL)) == NULL)
+ fatalx("ca: failed to read private key");
+ fclose(fp);
+
+ if (ca_key_serialize(key, &store->ca_privkey) != 0)
+ fatalx("ca: failed to serialize private key");
+
+ return (run_proc(env, p, procs, nitems(procs), ca_reset, store));
+}
+
+void
+ca_reset(struct iked *env, void *arg)
+{
+ struct ca_store *store = arg;
+
+ if (store->ca_cas != NULL)
+ X509_STORE_free(store->ca_cas);
+ if (store->ca_certs != NULL)
+ X509_STORE_free(store->ca_certs);
+
+ if ((store->ca_cas = X509_STORE_new()) == NULL)
+ fatalx("ca_reset: failed to get ca store");
+ if ((store->ca_calookup = X509_STORE_add_lookup(store->ca_cas,
+ X509_LOOKUP_file())) == NULL)
+ fatalx("ca_reset: failed to add ca lookup");
+
+ if ((store->ca_certs = X509_STORE_new()) == NULL)
+ fatalx("ca_reset: failed to get cert store");
+ if ((store->ca_certlookup = X509_STORE_add_lookup(store->ca_certs,
+ X509_LOOKUP_file())) == NULL)
+ fatalx("ca_reset: failed to add cert lookup");
+
+ env->sc_priv = store;
+
+ if (ca_reload(env) != 0)
+ fatal("ca_reset: reload");
+}
+
+int
+ca_dispatch_parent(int fd, struct iked_proc *p, struct imsg *imsg)
+{
+ struct iked *env = p->env;
+ struct ca_store *store = env->sc_priv;
+ u_int mode;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_RESET:
+ IMSG_SIZE_CHECK(imsg, &mode);
+ memcpy(&mode, imsg->data, sizeof(mode));
+ if (mode == RESET_ALL || mode == RESET_CA) {
+ log_debug("%s: config reload", __func__);
+ ca_reset(env, store);
+ }
+ return (0);
+ default:
+ break;
+ }
+
+ return (-1);
+}
+
+int
+ca_dispatch_ikev2(int fd, struct iked_proc *p, struct imsg *imsg)
+{
+ struct iked *env = p->env;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CERTREQ:
+ ca_getreq(env, imsg);
+ break;
+ case IMSG_CERT:
+ ca_getcert(env, imsg);
+ break;
+ case IMSG_AUTH:
+ ca_getauth(env, imsg);
+ break;
+ default:
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+ca_setcert(struct iked *env, struct iked_sahdr *sh, struct iked_id *id,
+ u_int8_t type, u_int8_t *data, size_t len, enum iked_procid procid)
+{
+ struct iovec iov[4];
+ int iovcnt = 0;
+ struct iked_static_id idb;
+
+ /* Must send the cert and a valid Id to the ca process */
+ if (procid == PROC_CERT) {
+ if (id == NULL || id->id_type == IKEV2_ID_NONE ||
+ ibuf_length(id->id_buf) > IKED_ID_SIZE)
+ return (-1);
+ bzero(&idb, sizeof(idb));
+
+ /* Convert to a static Id */
+ idb.id_type = id->id_type;
+ idb.id_length = ibuf_length(id->id_buf);
+ memcpy(&idb.id_data, ibuf_data(id->id_buf),
+ ibuf_length(id->id_buf));
+
+ iov[iovcnt].iov_base = &idb;
+ iov[iovcnt].iov_len = sizeof(idb);
+ iovcnt++;
+ }
+
+ iov[iovcnt].iov_base = sh;
+ iov[iovcnt].iov_len = sizeof(*sh);
+ iovcnt++;
+ iov[iovcnt].iov_base = &type;
+ iov[iovcnt].iov_len = sizeof(type);
+ iovcnt++;
+ iov[iovcnt].iov_base = data;
+ iov[iovcnt].iov_len = len;
+ iovcnt++;
+
+ if (imsg_composev_proc(env, procid, IMSG_CERT, -1, iov, iovcnt) == -1)
+ return (-1);
+ return (0);
+}
+
+int
+ca_setreq(struct iked *env, struct iked_sahdr *sh, u_int8_t type,
+ u_int8_t *data, size_t len, enum iked_procid id)
+{
+ struct iovec iov[3];
+ int iovcnt = 3;
+
+ iov[0].iov_base = sh;
+ iov[0].iov_len = sizeof(*sh);
+ iov[1].iov_base = &type;
+ iov[1].iov_len = sizeof(type);
+ iov[2].iov_base = data;
+ iov[2].iov_len = len;
+
+ if (imsg_composev_proc(env, id, IMSG_CERTREQ, -1, iov, iovcnt) == -1)
+ return (-1);
+ return (0);
+}
+
+int
+ca_setauth(struct iked *env, struct iked_sa *sa,
+ struct ibuf *authmsg, enum iked_procid id)
+{
+ struct iovec iov[3];
+ int iovcnt = 3;
+ struct iked_policy *policy = sa->sa_policy;
+ u_int8_t type = policy->pol_auth.auth_method;
+
+ if (type == IKEV2_AUTH_SHARED_KEY_MIC) {
+ sa->sa_stateflags |= IKED_REQ_AUTH;
+ return (ikev2_message_authsign(env, sa,
+ &policy->pol_auth, authmsg));
+ }
+
+ iov[0].iov_base = &sa->sa_hdr;
+ iov[0].iov_len = sizeof(sa->sa_hdr);
+ iov[1].iov_base = &type;
+ iov[1].iov_len = sizeof(type);
+ if (type == IKEV2_AUTH_NONE)
+ iovcnt--;
+ else {
+ iov[2].iov_base = ibuf_data(authmsg);
+ iov[2].iov_len = ibuf_size(authmsg);
+ log_debug("%s: auth length %d", __func__, ibuf_size(authmsg));
+ }
+
+ if (imsg_composev_proc(env, id, IMSG_AUTH, -1, iov, iovcnt) == -1)
+ return (-1);
+ return (0);
+}
+
+int
+ca_getcert(struct iked *env, struct imsg *imsg)
+{
+ struct iked_sahdr sh;
+ u_int8_t type;
+ u_int8_t *ptr;
+ size_t len;
+ struct iked_static_id id;
+ u_int i;
+ struct iovec iov[2];
+ int iovcnt = 2, cmd;
+
+ ptr = (u_int8_t *)imsg->data;
+ len = IMSG_DATA_SIZE(imsg);
+ i = sizeof(id) + sizeof(sh) + sizeof(type);
+ if (len <= i)
+ return (-1);
+
+ memcpy(&id, ptr, sizeof(id));
+ if (id.id_type == IKEV2_ID_NONE)
+ return (-1);
+ memcpy(&sh, ptr + sizeof(id), sizeof(sh));
+ memcpy(&type, ptr + sizeof(id) + sizeof(sh), sizeof(u_int8_t));
+ if (type != IKEV2_CERT_X509_CERT)
+ return (-1);
+
+ ptr += i;
+ len -= i;
+
+ if (ca_validate_cert(env, &id, ptr, len) == 0)
+ cmd = IMSG_CERTVALID;
+ else
+ cmd = IMSG_CERTINVALID;
+
+ iov[0].iov_base = &sh;
+ iov[0].iov_len = sizeof(sh);
+ iov[1].iov_base = &type;
+ iov[1].iov_len = sizeof(type);
+
+ if (imsg_composev_proc(env, PROC_IKEV2, cmd, -1, iov, iovcnt) == -1)
+ return (-1);
+ return (0);
+}
+
+int
+ca_getreq(struct iked *env, struct imsg *imsg)
+{
+ struct ca_store *store = env->sc_priv;
+ struct iked_sahdr sh;
+ u_int8_t type;
+ u_int8_t *ptr;
+ size_t len;
+ u_int i, n;
+ X509 *ca = NULL, *cert = NULL;
+ struct ibuf *buf;
+
+ ptr = (u_int8_t *)imsg->data;
+ len = IMSG_DATA_SIZE(imsg);
+ i = sizeof(u_int8_t) + sizeof(sh);
+ if (len < i || ((len - i) % SHA_DIGEST_LENGTH) != 0)
+ return (-1);
+
+ memcpy(&sh, ptr, sizeof(sh));
+ memcpy(&type, ptr + sizeof(sh), sizeof(u_int8_t));
+ if (type != IKEV2_CERT_X509_CERT)
+ return (-1);
+
+ for (n = 1; i < len; n++, i += SHA_DIGEST_LENGTH) {
+ if ((ca = ca_by_subjectpubkey(store->ca_cas,
+ ptr + i, SHA_DIGEST_LENGTH)) == NULL) {
+ log_debug("%s: CA %d not found", __func__, n);
+ print_hex(ptr, i, SHA_DIGEST_LENGTH);
+ continue;
+ }
+
+ log_debug("%s: found CA %s", __func__, ca->name);
+
+ if ((cert = ca_by_issuer(store->ca_certs,
+ X509_get_subject_name(ca))) != NULL) {
+ /* XXX should we re-validate our own cert here? */
+ break;
+ }
+
+ log_debug("%s: no valid certificate for this CA", __func__);
+ }
+ if (ca == NULL || cert == NULL) {
+ log_warnx("%s: no valid local certificate found", __func__);
+ type = IKEV2_CERT_NONE;
+ ca_setcert(env, &sh, NULL, type, NULL, 0, PROC_IKEV2);
+ return (0);
+ }
+
+ log_debug("%s: found local certificate %s", __func__, cert->name);
+
+ if ((buf = ca_x509_serialize(cert)) == NULL)
+ return (-1);
+
+ type = IKEV2_CERT_X509_CERT;
+ ca_setcert(env, &sh, NULL, type,
+ ibuf_data(buf), ibuf_size(buf), PROC_IKEV2);
+
+ return (0);
+}
+
+int
+ca_getauth(struct iked *env, struct imsg *imsg)
+{
+ struct ca_store *store = env->sc_priv;
+ struct iked_sahdr sh;
+ u_int8_t method;
+ u_int8_t *ptr;
+ size_t len;
+ u_int i;
+ int ret = -1;
+ struct iked_sa sa;
+ struct iked_policy policy;
+ struct iked_id *id;
+ struct ibuf *authmsg;
+
+ ptr = (u_int8_t *)imsg->data;
+ len = IMSG_DATA_SIZE(imsg);
+ i = sizeof(method) + sizeof(sh);
+ if (len <= i)
+ return (-1);
+
+ memcpy(&sh, ptr, sizeof(sh));
+ memcpy(&method, ptr + sizeof(sh), sizeof(u_int8_t));
+ if (method == IKEV2_AUTH_SHARED_KEY_MIC)
+ return (-1);
+
+ ptr += i;
+ len -= i;
+
+ if ((authmsg = ibuf_new(ptr, len)) == NULL)
+ return (-1);
+
+ /*
+ * Create fake SA and policy
+ */
+ bzero(&sa, sizeof(sa));
+ bzero(&policy, sizeof(policy));
+ memcpy(&sa.sa_hdr, &sh, sizeof(sh));
+ sa.sa_policy = &policy;
+ policy.pol_auth.auth_method = method;
+ if (sh.sh_initiator)
+ id = &sa.sa_icert;
+ else
+ id = &sa.sa_rcert;
+ memcpy(id, &store->ca_privkey, sizeof(*id));
+
+ if (ikev2_message_authsign(env, &sa, &policy.pol_auth, authmsg) != 0) {
+ log_debug("%s: AUTH sign failed", __func__);
+ policy.pol_auth.auth_method = IKEV2_AUTH_NONE;
+ }
+
+ ret = ca_setauth(env, &sa, sa.sa_localauth.id_buf, PROC_IKEV2);
+
+ ibuf_release(sa.sa_localauth.id_buf);
+ ibuf_release(authmsg);
+
+ return (ret);
+}
+
+int
+ca_reload(struct iked *env)
+{
+ struct ca_store *store = env->sc_priv;
+ DIR *dir;
+ struct dirent *entry;
+ char file[PATH_MAX];
+ STACK_OF(X509_OBJECT) *h;
+ X509_OBJECT *xo;
+ X509 *x509;
+ int i, len;
+ u_int8_t md[EVP_MAX_MD_SIZE];
+ struct iovec iov[2];
+ int iovcnt = 2;
+
+ /*
+ * Load CAs
+ */
+ if ((dir = opendir(IKED_CA_DIR)) == NULL)
+ return (-1);
+
+ while ((entry = readdir(dir)) != NULL) {
+ if ((entry->d_type != DT_REG) &&
+ (entry->d_type != DT_LNK))
+ continue;
+
+ if (snprintf(file, sizeof(file), "%s%s",
+ IKED_CA_DIR, entry->d_name) == -1)
+ continue;
+
+ if (!X509_load_cert_file(store->ca_calookup, file,
+ X509_FILETYPE_PEM)) {
+ log_debug("%s: failed to load ca file %s", __func__,
+ entry->d_name);
+ ca_sslerror();
+ continue;
+ }
+ log_debug("%s: loaded ca file %s", __func__, entry->d_name);
+ }
+ closedir(dir);
+
+ /*
+ * Load CRLs for the CAs
+ */
+ if ((dir = opendir(IKED_CRL_DIR)) == NULL)
+ return (-1);
+
+ while ((entry = readdir(dir)) != NULL) {
+ if ((entry->d_type != DT_REG) &&
+ (entry->d_type != DT_LNK))
+ continue;
+
+ if (snprintf(file, sizeof(file), "%s%s",
+ IKED_CRL_DIR, entry->d_name) == -1)
+ continue;
+
+ if (!X509_load_crl_file(store->ca_calookup, file,
+ X509_FILETYPE_PEM)) {
+ log_debug("%s: failed to load crl file %s", __func__,
+ entry->d_name);
+ ca_sslerror();
+ continue;
+ }
+
+ /* Only enable CRL checks if we actually loaded a CRL */
+ X509_STORE_set_flags(store->ca_cas, X509_V_FLAG_CRL_CHECK);
+
+ log_debug("%s: loaded crl file %s", __func__, entry->d_name);
+ }
+ closedir(dir);
+
+ /*
+ * Save CAs signatures for the IKEv2 CERTREQ
+ */
+ ibuf_release(env->sc_certreq);
+ if ((env->sc_certreq = ibuf_new(NULL, 0)) == NULL)
+ return (-1);
+
+ h = store->ca_cas->objs;
+ for (i = 0; i < sk_X509_OBJECT_num(h); i++) {
+ xo = sk_X509_OBJECT_value(h, i);
+ if (xo->type != X509_LU_X509)
+ continue;
+
+ x509 = xo->data.x509;
+ len = sizeof(md);
+ ca_subjectpubkey_digest(x509, md, &len);
+ log_debug("%s: %s", __func__, x509->name);
+
+ if (ibuf_add(env->sc_certreq, md, len) != 0) {
+ ibuf_release(env->sc_certreq);
+ return (-1);
+ }
+ }
+
+ if (ibuf_length(env->sc_certreq)) {
+ env->sc_certreqtype = IKEV2_CERT_X509_CERT;
+ iov[0].iov_base = &env->sc_certreqtype;
+ iov[0].iov_len = sizeof(env->sc_certreqtype);
+ iov[1].iov_base = ibuf_data(env->sc_certreq);
+ iov[1].iov_len = ibuf_length(env->sc_certreq);
+
+ log_debug("%s: loaded %d ca certificate%s", __func__,
+ ibuf_length(env->sc_certreq) / SHA_DIGEST_LENGTH,
+ ibuf_length(env->sc_certreq) == SHA_DIGEST_LENGTH ?
+ "" : "s");
+
+ (void)imsg_composev_proc(env, PROC_IKEV2, IMSG_CERTREQ, -1,
+ iov, iovcnt);
+ }
+
+ /*
+ * Load certificates
+ */
+ if ((dir = opendir(IKED_CERT_DIR)) == NULL)
+ return (-1);
+
+ while ((entry = readdir(dir)) != NULL) {
+ if ((entry->d_type != DT_REG) &&
+ (entry->d_type != DT_LNK))
+ continue;
+
+ if (snprintf(file, sizeof(file), "%s%s",
+ IKED_CERT_DIR, entry->d_name) == -1)
+ continue;
+
+ if (!X509_load_cert_file(store->ca_certlookup, file,
+ X509_FILETYPE_PEM)) {
+ log_debug("%s: failed to load cert file %s", __func__,
+ entry->d_name);
+ ca_sslerror();
+ continue;
+ }
+ log_debug("%s: loaded cert file %s", __func__, entry->d_name);
+ }
+ closedir(dir);
+
+ h = store->ca_certs->objs;
+ for (i = 0; i < sk_X509_OBJECT_num(h); i++) {
+ xo = sk_X509_OBJECT_value(h, i);
+ if (xo->type != X509_LU_X509)
+ continue;
+
+ x509 = xo->data.x509;
+
+ (void)ca_validate_cert(env, NULL, x509, 0);
+ }
+
+ return (0);
+}
+
+X509 *
+ca_by_subjectpubkey(X509_STORE *ctx, u_int8_t *sig, size_t siglen)
+{
+ STACK_OF(X509_OBJECT) *h;
+ X509_OBJECT *xo;
+ X509 *ca;
+ int i;
+ u_int len;
+ u_int8_t md[EVP_MAX_MD_SIZE];
+
+ h = ctx->objs;
+
+ for (i = 0; i < sk_X509_OBJECT_num(h); i++) {
+ xo = sk_X509_OBJECT_value(h, i);
+ if (xo->type != X509_LU_X509)
+ continue;
+
+ ca = xo->data.x509;
+ len = sizeof(md);
+ ca_subjectpubkey_digest(ca, md, &len);
+
+ if (len == siglen && memcmp(md, sig, len) == 0)
+ return (ca);
+ }
+
+ return (NULL);
+}
+
+X509 *
+ca_by_issuer(X509_STORE *ctx, X509_NAME *subject)
+{
+ STACK_OF(X509_OBJECT) *h;
+ X509_OBJECT *xo;
+ X509 *cert;
+ int i;
+ X509_NAME *issuer;
+
+ if (subject == NULL)
+ return (NULL);
+
+ h = ctx->objs;
+ for (i = 0; i < sk_X509_OBJECT_num(h); i++) {
+ xo = sk_X509_OBJECT_value(h, i);
+ if (xo->type != X509_LU_X509)
+ continue;
+
+ cert = xo->data.x509;
+ if ((issuer = X509_get_issuer_name(cert)) == NULL)
+ continue;
+ else if (X509_NAME_cmp(subject, issuer) == 0)
+ return (cert);
+ }
+
+ return (NULL);
+}
+
+int
+ca_subjectpubkey_digest(X509 *x509, u_int8_t *md, u_int *size)
+{
+ u_int8_t *buf = NULL;
+ int buflen;
+
+ if (*size < SHA_DIGEST_LENGTH)
+ return (-1);
+
+ /*
+ * Generate a SHA-1 digest of the Subject Public Key Info
+ * element in the X.509 certificate, an ASN.1 sequence
+ * that includes the public key type (eg. RSA) and the
+ * public key value (see 3.7 of RFC4306).
+ */
+ buflen = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(x509), &buf);
+ if (!buflen)
+ return (-1);
+ if (!EVP_Digest(buf, buflen, md, size, EVP_sha1(), NULL)) {
+ free(buf);
+ return (-1);
+ }
+ free(buf);
+
+ return (0);
+}
+
+struct ibuf *
+ca_x509_serialize(X509 *x509)
+{
+ long len;
+ struct ibuf *buf;
+ u_int8_t *d = NULL;
+ BIO *out;
+
+ if ((out = BIO_new(BIO_s_mem())) == NULL)
+ return (NULL);
+ if (!i2d_X509_bio(out, x509)) {
+ BIO_free(out);
+ return (NULL);
+ }
+
+ len = BIO_get_mem_data(out, &d);
+ buf = ibuf_new(d, len);
+
+ return (buf);
+}
+
+int
+ca_key_serialize(EVP_PKEY *key, struct iked_id *id)
+{
+ int len;
+ u_int8_t *d;
+ RSA *rsa;
+
+ switch (key->type) {
+ case EVP_PKEY_RSA:
+ id->id_type = 0;
+ ibuf_release(id->id_buf);
+
+ if ((rsa = EVP_PKEY_get1_RSA(key)) == NULL)
+ return (-1);
+ if ((len = i2d_RSAPrivateKey(rsa, NULL)) <= 0)
+ return (-1);
+ if ((id->id_buf = ibuf_new(NULL, len)) == NULL)
+ return (-1);
+
+ d = ibuf_data(id->id_buf);
+ if (i2d_RSAPrivateKey(rsa, &d) != len) {
+ ibuf_release(id->id_buf);
+ return (-1);
+ }
+
+ id->id_type = IKEV2_CERT_RSA_KEY;
+ break;
+ default:
+ log_debug("%s: unsupported key type %d", __func__, key->type);
+ return (-1);
+ }
+
+ return (0);
+}
+
+char *
+ca_asn1_name(u_int8_t *asn1, size_t len)
+{
+ X509_NAME *name = NULL;
+ char *str = NULL;
+ const u_int8_t *p;
+
+ p = asn1;
+ if ((name = d2i_X509_NAME(NULL, &p, len)) == NULL)
+ return (NULL);
+ str = ca_x509_name(name);
+ X509_NAME_free(name);
+
+ return (str);
+}
+
+char *
+ca_x509_name(void *ptr)
+{
+ char buf[BUFSIZ];
+ X509_NAME *name = ptr;
+
+ bzero(buf, sizeof(buf));
+ if (!X509_NAME_oneline(name, buf, sizeof(buf) - 1))
+ return (NULL);
+
+ return (strdup(buf));
+}
+
+int
+ca_validate_cert(struct iked *env, struct iked_static_id *id,
+ void *data, size_t len)
+{
+ struct ca_store *store = env->sc_priv;
+ X509_STORE_CTX csc;
+ BIO *rawcert = NULL;
+ X509 *cert = NULL;
+ int ret = -1, result, error;
+
+ if (len == 0) {
+ /* Data is already an X509 certificate */
+ cert = (X509 *)data;
+ } else {
+ /* Convert data to X509 certificate */
+ if ((rawcert = BIO_new_mem_buf(data, len)) == NULL)
+ goto done;
+ if ((cert = d2i_X509_bio(rawcert, NULL)) == NULL)
+ goto done;
+ }
+
+ X509_STORE_CTX_init(&csc, store->ca_cas, cert, NULL);
+ if (store->ca_cas->param->flags & X509_V_FLAG_CRL_CHECK) {
+ X509_STORE_CTX_set_flags(&csc, X509_V_FLAG_CRL_CHECK);
+ X509_STORE_CTX_set_flags(&csc, X509_V_FLAG_CRL_CHECK_ALL);
+ }
+
+ bzero(&csc, sizeof(csc));
+ result = X509_verify_cert(&csc);
+ error = csc.error;
+ X509_STORE_CTX_cleanup(&csc);
+
+ log_debug("%s: %s %.100s", __func__, cert->name,
+ error == 0 ? "ok" : X509_verify_cert_error_string(error));
+
+ if (!result) {
+ /* XXX should we accept self-signed certificates? */
+ ret = -1;
+ goto done;
+ }
+
+ if (id != NULL) {
+ /* compare the id with the certificate CN or subjectAltName */
+ /* XXX */
+ }
+
+ /* Success */
+ ret = 0;
+
+ done:
+ if (rawcert != NULL) {
+ BIO_free(rawcert);
+ if (cert != NULL)
+ X509_free(cert);
+ }
+
+ return (ret);
+}
+
+void
+ca_sslinit(void)
+{
+ OpenSSL_add_all_algorithms();
+ SSL_load_error_strings();
+
+ /* Init hardware crypto engines. */
+ ENGINE_load_builtin_engines();
+ ENGINE_register_all_complete();
+}
+
+void
+ca_sslerror(void)
+{
+ u_long error;
+ extern int verbose;
+
+ if (verbose < 3)
+ return;
+
+ while ((error = ERR_get_error()) != 0)
+ log_debug("%s: %.100s", __func__,
+ ERR_error_string(error, NULL));
+}
diff --git a/sbin/iked/chap_ms.c b/sbin/iked/chap_ms.c
new file mode 100644
index 00000000000..50975c62864
--- /dev/null
+++ b/sbin/iked/chap_ms.c
@@ -0,0 +1,412 @@
+/* $OpenBSD: chap_ms.c,v 1.1 2010/06/03 16:41:12 reyk Exp $ */
+/* $vantronix: chap_ms.c,v 1.7 2010/06/02 12:22:58 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
+ * Copyright (c) 1997 - 2001 Brian Somers <brian@Awfulhak.org>
+ * Copyright (c) 1997 Gabor Kincses <gabor@acm.org>
+ * Copyright (c) 1995 Eric Rosenquist
+ *
+ * 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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <openssl/evp.h>
+#include <openssl/des.h>
+#include <openssl/md4.h>
+#include <openssl/md5.h>
+#include <openssl/sha.h>
+
+#include "chap_ms.h"
+
+/*
+ * Documentation & specifications:
+ *
+ * MS-CHAP (CHAP80) RFC2433
+ * MS-CHAP-V2 (CHAP81) RFC2759
+ * MPPE key management RFC3079
+ *
+ * Security analysis:
+ * Schneier/Mudge/Wagner, "MS-CHAP-v2", Oct 99
+ * "It is unclear to us why this protocol is so complicated."
+ */
+
+static u_int8_t sha1_pad1[40] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static u_int8_t sha1_pad2[40] = {
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2
+};
+
+u_int8_t get7bits(u_int8_t *, int);
+void mschap_des_addparity(u_int8_t *, u_int8_t *);
+void mschap_des_encrypt(u_int8_t *, u_int8_t *, u_int8_t *);
+void mschap_challenge_response(u_int8_t *, u_int8_t *, u_int8_t *);
+
+u_int8_t
+get7bits(u_int8_t *in, int start)
+{
+ u_int word;
+
+ word = (u_int)in[start / 8] << 8;
+ word |= (u_int)in[start / 8 + 1];
+ word >>= 15 - (start % 8 + 7);
+
+ return (word & 0xfe);
+}
+
+/* IN 56 bit DES key missing parity bits
+ OUT 64 bit DES key with parity bits added */
+void
+mschap_des_addparity(u_int8_t *key, u_int8_t *des_key)
+{
+ des_key[0] = get7bits(key, 0);
+ des_key[1] = get7bits(key, 7);
+ des_key[2] = get7bits(key, 14);
+ des_key[3] = get7bits(key, 21);
+ des_key[4] = get7bits(key, 28);
+ des_key[5] = get7bits(key, 35);
+ des_key[6] = get7bits(key, 42);
+ des_key[7] = get7bits(key, 49);
+
+ DES_set_odd_parity((des_cblock *)des_key);
+}
+
+void
+mschap_des_encrypt(u_int8_t *clear, u_int8_t *key, u_int8_t *cipher)
+{
+ des_cblock des_key;
+ des_key_schedule key_schedule;
+
+ mschap_des_addparity(key, des_key);
+
+ DES_set_key(&des_key, &key_schedule);
+ DES_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher,
+ &key_schedule, 1);
+}
+
+void
+mschap_challenge_response(u_int8_t *challenge, u_int8_t *pwhash,
+ u_int8_t *response)
+{
+ u_int8_t padpwhash[21];
+
+ bzero(&padpwhash, sizeof(padpwhash));
+ memcpy(padpwhash, pwhash, MSCHAP_HASH_SZ);
+
+ mschap_des_encrypt(challenge, padpwhash + 0, response + 0);
+ mschap_des_encrypt(challenge, padpwhash + 7, response + 8);
+ mschap_des_encrypt(challenge, padpwhash + 14, response + 16);
+}
+
+void
+mschap_ntpassword_hash(u_int8_t *in, int inlen, u_int8_t *hash)
+{
+ EVP_MD_CTX ctx;
+ u_int mdlen;
+
+ EVP_DigestInit(&ctx, EVP_md4());
+ EVP_DigestUpdate(&ctx, in, inlen);
+ EVP_DigestFinal(&ctx, hash, &mdlen);
+}
+
+void
+mschap_challenge_hash(u_int8_t *peer_challenge, u_int8_t *auth_challenge,
+ u_int8_t *username, int usernamelen, u_int8_t *challenge)
+{
+ EVP_MD_CTX ctx;
+ u_int8_t md[SHA_DIGEST_LENGTH];
+ u_int mdlen;
+ u_int8_t *name;
+
+ if ((name = strrchr(username, '\\')) == NULL)
+ name = username;
+ else
+ name++;
+
+ EVP_DigestInit(&ctx, EVP_sha1());
+ EVP_DigestUpdate(&ctx, peer_challenge, MSCHAPV2_CHALLENGE_SZ);
+ EVP_DigestUpdate(&ctx, auth_challenge, MSCHAPV2_CHALLENGE_SZ);
+ EVP_DigestUpdate(&ctx, name, strlen(name));
+ EVP_DigestFinal(&ctx, md, &mdlen);
+
+ memcpy(challenge, md, MSCHAP_CHALLENGE_SZ);
+}
+
+void
+mschap_nt_response(u_int8_t *auth_challenge, u_int8_t *peer_challenge,
+ u_int8_t *username, int usernamelen, u_int8_t *password, int passwordlen,
+ u_int8_t *response)
+{
+ u_int8_t challenge[MSCHAP_CHALLENGE_SZ];
+ u_int8_t password_hash[MSCHAP_HASH_SZ];
+
+ mschap_challenge_hash(peer_challenge, auth_challenge,
+ username, usernamelen, challenge);
+
+ mschap_ntpassword_hash(password, passwordlen, password_hash);
+ mschap_challenge_response(challenge, password_hash, response);
+}
+
+void
+mschap_auth_response(u_int8_t *password, int passwordlen,
+ u_int8_t *ntresponse, u_int8_t *auth_challenge, u_int8_t *peer_challenge,
+ u_int8_t *username, int usernamelen, u_int8_t *auth_response)
+{
+ EVP_MD_CTX ctx;
+ u_int8_t password_hash[MSCHAP_HASH_SZ];
+ u_int8_t password_hash2[MSCHAP_HASH_SZ];
+ u_int8_t challenge[MSCHAP_CHALLENGE_SZ];
+ u_int8_t md[SHA_DIGEST_LENGTH], *ptr;
+ u_int mdlen;
+ int i;
+ const u_int8_t hex[] = "0123456789ABCDEF";
+ static u_int8_t magic1[39] = {
+ 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
+ 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
+ 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
+ 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74
+ };
+ static u_int8_t magic2[41] = {
+ 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
+ 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
+ 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
+ 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
+ 0x6E
+ };
+
+ mschap_ntpassword_hash(password, passwordlen, password_hash);
+ mschap_ntpassword_hash(password_hash, MSCHAP_HASH_SZ, password_hash2);
+
+ EVP_DigestInit(&ctx, EVP_sha1());
+ EVP_DigestUpdate(&ctx, password_hash2, sizeof(password_hash2));
+ EVP_DigestUpdate(&ctx, ntresponse, 24);
+ EVP_DigestUpdate(&ctx, magic1, 39);
+ EVP_DigestFinal(&ctx, md, &mdlen);
+
+ mschap_challenge_hash(peer_challenge, auth_challenge,
+ username, usernamelen, challenge);
+
+ EVP_DigestInit(&ctx, EVP_sha1());
+ EVP_DigestUpdate(&ctx, md, sizeof(md));
+ EVP_DigestUpdate(&ctx, challenge, sizeof(challenge));
+ EVP_DigestUpdate(&ctx, magic2, 41);
+ EVP_DigestFinal(&ctx, md, &mdlen);
+
+ /*
+ * Encode the value of 'Digest' as "S=" followed by
+ * 40 ASCII hexadecimal digits and return it in
+ * AuthenticatorResponse.
+ * For example,
+ * "S=0123456789ABCDEF0123456789ABCDEF01234567"
+ */
+ ptr = auth_response;
+ *ptr++ = 'S';
+ *ptr++ = '=';
+ for (i = 0; i < SHA_DIGEST_LENGTH; i++) {
+ *ptr++ = hex[md[i] >> 4];
+ *ptr++ = hex[md[i] & 0x0f];
+ }
+}
+
+void
+mschap_masterkey(u_int8_t *password_hash2, u_int8_t *ntresponse,
+ u_int8_t *masterkey)
+{
+ u_int8_t md[SHA_DIGEST_LENGTH];
+ u_int mdlen;
+ EVP_MD_CTX ctx;
+ static u_int8_t magic1[27] = {
+ 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
+ 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79
+ };
+
+ EVP_DigestInit(&ctx, EVP_sha1());
+ EVP_DigestUpdate(&ctx, password_hash2, MSCHAP_HASH_SZ);
+ EVP_DigestUpdate(&ctx, ntresponse, 24);
+ EVP_DigestUpdate(&ctx, magic1, 27);
+ EVP_DigestFinal(&ctx, md, &mdlen);
+
+ memcpy(masterkey, md, 16);
+}
+
+void
+mschap_asymetric_startkey(u_int8_t *masterkey, u_int8_t *sessionkey,
+ int sessionkeylen, int issend, int isserver)
+{
+ EVP_MD_CTX ctx;
+ u_int8_t md[SHA_DIGEST_LENGTH];
+ u_int mdlen;
+ u_int8_t *s;
+ static u_int8_t magic2[84] = {
+ 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+ 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
+ 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
+ 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
+ 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+ 0x6b, 0x65, 0x79, 0x2e
+ };
+ static u_int8_t magic3[84] = {
+ 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+ 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+ 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
+ 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
+ 0x6b, 0x65, 0x79, 0x2e
+ };
+
+ if (issend)
+ s = isserver ? magic3 : magic2;
+ else
+ s = isserver ? magic2 : magic3;
+
+ EVP_DigestInit(&ctx, EVP_sha1());
+ EVP_DigestUpdate(&ctx, masterkey, 16);
+ EVP_DigestUpdate(&ctx, sha1_pad1, 40);
+ EVP_DigestUpdate(&ctx, s, 84);
+ EVP_DigestUpdate(&ctx, sha1_pad2, 40);
+ EVP_DigestFinal(&ctx, md, &mdlen);
+
+ memcpy(sessionkey, md, sessionkeylen);
+}
+
+void
+mschap_msk(u_int8_t *password, int passwordlen,
+ u_int8_t *ntresponse, u_int8_t *msk)
+{
+ u_int8_t password_hash[MSCHAP_HASH_SZ];
+ u_int8_t password_hash2[MSCHAP_HASH_SZ];
+ u_int8_t masterkey[MSCHAP_MASTERKEY_SZ];
+ u_int8_t sendkey[MSCHAP_MASTERKEY_SZ];
+ u_int8_t recvkey[MSCHAP_MASTERKEY_SZ];
+
+ mschap_ntpassword_hash(password, passwordlen, password_hash);
+ mschap_ntpassword_hash(password_hash, MSCHAP_HASH_SZ, password_hash2);
+
+ mschap_masterkey(password_hash2, ntresponse, masterkey);
+ mschap_asymetric_startkey(masterkey, recvkey, sizeof(recvkey), 0, 1);
+ mschap_asymetric_startkey(masterkey, sendkey, sizeof(sendkey), 1, 1);
+
+ /* 16 bytes receive key + 16 bytes send key + 32 bytes 0 padding */
+ bzero(msk, MSCHAP_MSK_SZ);
+ memcpy(msk, &recvkey, sizeof(recvkey));
+ memcpy(msk + sizeof(recvkey), &sendkey, sizeof(sendkey));
+}
+
+void
+mschap_newkey(u_int8_t *startkey, u_int8_t *sessionkey,
+ long sessionkeylen, u_int8_t *key)
+{
+ EVP_MD_CTX ctx;
+ u_int8_t md[SHA_DIGEST_LENGTH];
+ u_int mdlen;
+
+ EVP_DigestInit(&ctx, EVP_sha1());
+ EVP_DigestUpdate(&ctx, startkey, sessionkeylen);
+ EVP_DigestUpdate(&ctx, sha1_pad1, sizeof(sha1_pad1));
+ EVP_DigestUpdate(&ctx, sessionkey, sessionkeylen);
+ EVP_DigestUpdate(&ctx, sha1_pad2, sizeof(sha1_pad2));
+ EVP_DigestFinal(&ctx, md, &mdlen);
+
+ memcpy(key, md, sessionkeylen);
+}
+
+void
+mschap_nt(u_int8_t *password_hash, u_int8_t *challenge)
+{
+ u_int8_t response[24];
+
+ mschap_challenge_response(challenge, password_hash, response);
+ memcpy(password_hash, response, sizeof(response));
+ password_hash[24] = 1; /* NT-style response */
+}
+
+void
+mschap_lanman(u_int8_t *digest, u_int8_t *challenge, u_int8_t *secret)
+{
+ static u_int8_t salt[] = "KGS!@#$%"; /* RASAPI32.dll */
+ u_int8_t SECRET[14], *ptr, *end;
+ u_int8_t hash[MSCHAP_HASH_SZ];
+
+ end = SECRET + sizeof(SECRET);
+ for (ptr = SECRET; *secret && ptr < end; ptr++, secret++)
+ *ptr = toupper(*secret);
+ if (ptr < end)
+ memset(ptr, '\0', end - ptr);
+
+ mschap_des_encrypt(salt, SECRET, hash);
+ mschap_des_encrypt(salt, SECRET + 7, hash + 8);
+
+ mschap_challenge_response(challenge, hash, digest);
+}
+
+void
+mschap_radiuskey(u_int8_t *plain, const u_int8_t *crypted,
+ const u_int8_t *authenticator, const u_int8_t *secret)
+{
+ EVP_MD_CTX ctx;
+ u_int8_t b[MD5_DIGEST_LENGTH], p[32];
+ u_int i, mdlen;
+
+ EVP_DigestInit(&ctx, EVP_md5());
+ EVP_DigestUpdate(&ctx, secret, strlen(secret));
+ EVP_DigestUpdate(&ctx, authenticator, 16);
+ EVP_DigestUpdate(&ctx, crypted, 2);
+ EVP_DigestFinal(&ctx, b, &mdlen);
+
+ for(i = 0; i < mdlen; i++) {
+ p[i] = b[i] ^ crypted[i+2];
+ }
+
+ EVP_DigestInit(&ctx, EVP_md5());
+ EVP_DigestUpdate(&ctx, secret, strlen(secret));
+ EVP_DigestUpdate(&ctx, crypted + 2, mdlen);
+ EVP_DigestFinal(&ctx, b, &mdlen);
+
+ for(i = 0; i < mdlen; i++) {
+ p[i+16] = b[i] ^ crypted[i+18];
+ }
+
+ memcpy(plain, p+1, 16);
+}
diff --git a/sbin/iked/chap_ms.h b/sbin/iked/chap_ms.h
new file mode 100644
index 00000000000..95a216c0335
--- /dev/null
+++ b/sbin/iked/chap_ms.h
@@ -0,0 +1,52 @@
+/* $OpenBSD: chap_ms.h,v 1.1 2010/06/03 16:41:12 reyk Exp $ */
+/* $vantronix: chap_ms.h,v 1.6 2010/05/19 09:37:00 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _CHAP_MS_H
+#define _CHAP_MS_H
+
+#define MSCHAP_CHALLENGE_SZ 8
+#define MSCHAPV2_CHALLENGE_SZ 16
+#define MSCHAP_HASH_SZ 16
+#define MSCHAP_MASTERKEY_SZ 16
+#define MSCHAP_MSK_KEY_SZ 32
+#define MSCHAP_MSK_PADDING_SZ 32
+#define MSCHAP_MSK_SZ 64
+
+#define MSCHAP_MAXNTPASSWORD_SZ 255 /* unicode chars */
+
+void mschap_nt_response(u_int8_t *, u_int8_t *, u_int8_t *, int,
+ u_int8_t *, int , u_int8_t *);
+void mschap_auth_response(u_int8_t *, int, u_int8_t *, u_int8_t *,
+ u_int8_t *, u_int8_t *, int, u_int8_t *);
+
+void mschap_nt(u_int8_t *, u_int8_t *);
+void mschap_lanman(u_int8_t *, u_int8_t *, u_int8_t *);
+
+void mschap_ntpassword_hash(u_int8_t *, int, u_int8_t *);
+void mschap_challenge_hash(u_int8_t *, u_int8_t *, u_int8_t *,
+ int, u_int8_t *);
+
+void mschap_asymetric_startkey(u_int8_t *, u_int8_t *, int, int, int);
+void mschap_masterkey(u_int8_t *, u_int8_t *, u_int8_t *);
+void mschap_newkey(u_int8_t *, u_int8_t *, long, u_int8_t *);
+void mschap_radiuskey(u_int8_t *, const u_int8_t *, const u_int8_t *,
+ const u_int8_t *);
+void mschap_msk(u_int8_t *, int, u_int8_t *, u_int8_t *);
+
+#endif /* _CHAP_MS_H */
diff --git a/sbin/iked/config.c b/sbin/iked/config.c
new file mode 100644
index 00000000000..5af6dac6dd8
--- /dev/null
+++ b/sbin/iked/config.c
@@ -0,0 +1,643 @@
+/* $OpenBSD: config.c,v 1.1 2010/06/03 16:41:12 reyk Exp $ */
+/* $vantronix: config.c,v 1.30 2010/05/28 15:34:35 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+#include <signal.h>
+#include <errno.h>
+#include <err.h>
+#include <pwd.h>
+#include <event.h>
+
+#include "iked.h"
+#include "ikev2.h"
+
+struct iked_sa *
+config_new_sa(struct iked *env, int initiator)
+{
+ struct iked_sa *sa;
+
+ if ((sa = calloc(1, sizeof(*sa))) == NULL)
+ return (NULL);
+
+ TAILQ_INIT(&sa->sa_proposals);
+ TAILQ_INIT(&sa->sa_childsas);
+ TAILQ_INIT(&sa->sa_flows);
+ sa->sa_hdr.sh_initiator = initiator;
+
+ if (initiator)
+ sa->sa_hdr.sh_ispi = config_getspi();
+ else
+ sa->sa_hdr.sh_rspi = config_getspi();
+
+ gettimeofday(&sa->sa_timecreated, NULL);
+ memcpy(&sa->sa_timeused, &sa->sa_timecreated, sizeof(sa->sa_timeused));
+
+ return (sa);
+}
+
+u_int64_t
+config_getspi(void)
+{
+ u_int64_t spi;
+
+ spi = ((u_int64_t)arc4random() << 32) | arc4random();
+ if (spi == 0)
+ return (config_getspi());
+
+ return (spi);
+}
+
+void
+config_free_sa(struct iked *env, struct iked_sa *sa)
+{
+ (void)RB_REMOVE(iked_sas, &env->sc_sas, sa);
+
+ config_free_proposals(&sa->sa_proposals, 0);
+ config_free_childsas(env, &sa->sa_childsas, NULL, NULL);
+ config_free_flows(env, &sa->sa_flows, NULL);
+
+ policy_unref(env, sa->sa_policy);
+
+ ibuf_release(sa->sa_inonce);
+ ibuf_release(sa->sa_rnonce);
+
+ if (sa->sa_dhgroup != NULL)
+ group_free(sa->sa_dhgroup);
+ ibuf_release(sa->sa_dhiexchange);
+ ibuf_release(sa->sa_dhrexchange);
+
+ hash_free(sa->sa_prf);
+ hash_free(sa->sa_integr);
+ cipher_free(sa->sa_encr);
+
+ ibuf_release(sa->sa_key_d);
+ ibuf_release(sa->sa_key_iauth);
+ ibuf_release(sa->sa_key_rauth);
+ ibuf_release(sa->sa_key_iencr);
+ ibuf_release(sa->sa_key_rencr);
+ ibuf_release(sa->sa_key_iprf);
+ ibuf_release(sa->sa_key_rprf);
+
+ ibuf_release(sa->sa_1stmsg);
+ ibuf_release(sa->sa_2ndmsg);
+
+ ibuf_release(sa->sa_iid.id_buf);
+ ibuf_release(sa->sa_rid.id_buf);
+ ibuf_release(sa->sa_icert.id_buf);
+ ibuf_release(sa->sa_rcert.id_buf);
+
+ ibuf_release(sa->sa_eap.id_buf);
+ if (sa->sa_eapid != NULL)
+ free(sa->sa_eapid);
+ ibuf_release(sa->sa_eapmsk);
+
+ free(sa);
+}
+
+struct iked_policy *
+config_new_policy(struct iked *env)
+{
+ struct iked_policy *pol;
+
+ if ((pol = calloc(1, sizeof(*pol))) == NULL)
+ return (NULL);
+
+ TAILQ_INIT(&pol->pol_proposals);
+
+ if (env != NULL)
+ RB_INSERT(iked_policies, &env->sc_policies, pol);
+
+ return (pol);
+}
+
+void
+config_free_policy(struct iked *env, struct iked_policy *pol)
+{
+ struct iked_sa *sa;
+
+ if (pol->pol_flags & IKED_POLICY_REFCNT)
+ goto remove;
+
+ (void)RB_REMOVE(iked_policies, &env->sc_policies, pol);
+
+ RB_FOREACH(sa, iked_sas, &env->sc_sas) {
+ /* Remove from the policy tree, but keep for existing SAs */
+ if (sa->sa_policy == pol)
+ policy_ref(env, pol);
+ }
+
+ if (pol->pol_refcnt)
+ return;
+
+ remove:
+ config_free_proposals(&pol->pol_proposals, 0);
+ config_free_flows(env, &pol->pol_flows, NULL);
+ free(pol);
+}
+
+struct iked_proposal *
+config_add_proposal(struct iked_proposals *head, u_int id, u_int proto)
+{
+ struct iked_proposal *pp;
+
+ TAILQ_FOREACH(pp, head, prop_entry) {
+ if (pp->prop_protoid == proto &&
+ pp->prop_id == id)
+ return (pp);
+ }
+
+ if ((pp = calloc(1, sizeof(*pp))) == NULL)
+ return (NULL);
+
+ pp->prop_protoid = proto;
+ pp->prop_id = id;
+
+ TAILQ_INSERT_TAIL(head, pp, prop_entry);
+
+ return (pp);
+}
+
+void
+config_free_proposals(struct iked_proposals *head, u_int proto)
+{
+ struct iked_proposal *prop, *next;
+
+ for (prop = TAILQ_FIRST(head); prop != NULL; prop = next) {
+ next = TAILQ_NEXT(prop, prop_entry);
+
+ /* Free any proposal or only selected SA proto */
+ if (proto != 0 && prop->prop_protoid != proto)
+ continue;
+
+ log_debug("%s: free %p", __func__, prop);
+
+ TAILQ_REMOVE(head, prop, prop_entry);
+ if (prop->prop_nxforms)
+ free(prop->prop_xforms);
+ free(prop);
+ }
+}
+
+void
+config_free_flows(struct iked *env, struct iked_flows *head,
+ struct iked_spi *spi)
+{
+ struct iked_flow *flow, *next;
+
+ for (flow = TAILQ_FIRST(head); flow != NULL; flow = next) {
+ next = TAILQ_NEXT(flow, flow_entry);
+
+ if (spi != NULL && spi->spi != flow->flow_peerspi)
+ continue;
+
+ log_debug("%s: free %p", __func__, flow);
+
+ TAILQ_REMOVE(head, flow, flow_entry);
+ (void)pfkey_flow_delete(env->sc_pfkey, flow);
+ flow_free(flow);
+ }
+}
+
+void
+config_free_childsas(struct iked *env, struct iked_childsas *head,
+ struct iked_spi *peerspi, struct iked_spi *localspi)
+{
+ struct iked_childsa *csa, *nextcsa;
+
+ if (localspi != NULL)
+ bzero(localspi, sizeof(*localspi));
+
+ for (csa = TAILQ_FIRST(head); csa != NULL; csa = nextcsa) {
+ nextcsa = TAILQ_NEXT(csa, csa_entry);
+
+ if (peerspi != NULL) {
+ /* Only delete matching peer SPIs */
+ if (peerspi->spi != csa->csa_peerspi)
+ continue;
+
+ /* Store assigned local SPI */
+ if (localspi != NULL && localspi->spi == 0)
+ memcpy(localspi, &csa->csa_spi,
+ sizeof(*localspi));
+ }
+ log_debug("%s: free %p", __func__, csa);
+
+ TAILQ_REMOVE(head, csa, csa_entry);
+ (void)pfkey_sa_delete(env->sc_pfkey, csa);
+ childsa_free(csa);
+ }
+}
+
+struct iked_transform *
+config_add_transform(struct iked_proposal *prop, u_int type,
+ u_int id, u_int length, u_int keylength)
+{
+ struct iked_transform *xform;
+ struct iked_constmap *map = NULL;
+ int score = 1;
+ u_int i;
+
+ switch (type) {
+ case IKEV2_XFORMTYPE_ENCR:
+ map = ikev2_xformencr_map;
+ break;
+ case IKEV2_XFORMTYPE_PRF:
+ map = ikev2_xformprf_map;
+ break;
+ case IKEV2_XFORMTYPE_INTEGR:
+ map = ikev2_xformauth_map;
+ break;
+ case IKEV2_XFORMTYPE_DH:
+ map = ikev2_xformdh_map;
+ break;
+ case IKEV2_XFORMTYPE_ESN:
+ map = ikev2_xformesn_map;
+ break;
+ default:
+ log_debug("%s: invalid transform type %d", __func__, type);
+ return (NULL);
+ }
+
+ for (i = 0; i < prop->prop_nxforms; i++) {
+ xform = prop->prop_xforms + i;
+ if (xform->xform_type == type &&
+ xform->xform_id == id &&
+ xform->xform_length == length)
+ return (xform);
+ }
+
+ for (i = 0; i < prop->prop_nxforms; i++) {
+ xform = prop->prop_xforms + i;
+ if (xform->xform_type == type) {
+ switch (type) {
+ case IKEV2_XFORMTYPE_ENCR:
+ case IKEV2_XFORMTYPE_INTEGR:
+ score += 3;
+ break;
+ case IKEV2_XFORMTYPE_DH:
+ score += 2;
+ break;
+ default:
+ score += 1;
+ break;
+ }
+ }
+ }
+
+ if ((xform = realloc(prop->prop_xforms,
+ (prop->prop_nxforms + 1) * sizeof(*xform))) == NULL) {
+ return (NULL);
+ }
+
+ prop->prop_xforms = xform;
+ xform = prop->prop_xforms + prop->prop_nxforms++;
+ bzero(xform, sizeof(*xform));
+
+ xform->xform_type = type;
+ xform->xform_id = id;
+ xform->xform_length = length;
+ xform->xform_keylength = keylength;
+ xform->xform_score = score;
+ xform->xform_map = map;
+
+ return (xform);
+}
+
+struct iked_transform *
+config_findtransform(struct iked_proposals *props, u_int8_t type)
+{
+ struct iked_proposal *prop;
+ struct iked_transform *xform;
+ u_int i;
+
+ /* Search of the first transform with the desired type */
+ TAILQ_FOREACH(prop, props, prop_entry) {
+ for (i = 0; i < prop->prop_nxforms; i++) {
+ xform = prop->prop_xforms + i;
+ if (xform->xform_type == type)
+ return (xform);
+ }
+ }
+
+ return (NULL);
+}
+
+struct iked_user *
+config_new_user(struct iked *env, struct iked_user *new)
+{
+ struct iked_user *usr, *old;
+
+ if ((usr = calloc(1, sizeof(*usr))) == NULL)
+ return (NULL);
+
+ memcpy(usr, new, sizeof(*usr));
+
+ if ((old = RB_INSERT(iked_users, &env->sc_users, usr)) != NULL) {
+ /* Update the password of an existing user*/
+ memcpy(old, new, sizeof(old));
+
+ log_debug("%s: updating user %s", __func__, usr->usr_name);
+ free(usr);
+
+ return (old);
+ }
+
+ log_debug("%s: inserting new user %s", __func__, usr->usr_name);
+ return (usr);
+}
+
+/*
+ * Inter-process communication of configuration items.
+ */
+
+int
+config_setreset(struct iked *env, u_int mode, enum iked_procid id)
+{
+ imsg_compose_proc(env, id, IMSG_CTL_RESET, -1, &mode, sizeof(mode));
+ return (0);
+}
+
+int
+config_getreset(struct iked *env, struct imsg *imsg)
+{
+ struct iked_policy *pol, *nextpol;
+ struct iked_sa *sa, *nextsa;
+ struct iked_user *usr, *nextusr;
+ u_int mode;
+
+ IMSG_SIZE_CHECK(imsg, &mode);
+ memcpy(&mode, imsg->data, sizeof(mode));
+
+ if (mode == RESET_ALL || mode == RESET_POLICY) {
+ log_debug("%s: flushing policies", __func__);
+ for (pol = RB_MIN(iked_policies, &env->sc_policies);
+ pol != NULL; pol = nextpol) {
+ nextpol =
+ RB_NEXT(iked_policies, &env->sc_policies, pol);
+ config_free_policy(env, pol);
+ }
+ }
+
+ if (mode == RESET_ALL || mode == RESET_SA) {
+ log_debug("%s: flushing SAs", __func__);
+ for (sa = RB_MIN(iked_sas, &env->sc_sas);
+ sa != NULL; sa = nextsa) {
+ nextsa = RB_NEXT(iked_sas, &env->sc_sas, sa);
+ config_free_sa(env, sa);
+ }
+ }
+
+ if (mode == RESET_ALL || mode == RESET_USER) {
+ log_debug("%s: flushing users", __func__);
+ for (usr = RB_MIN(iked_users, &env->sc_users);
+ usr != NULL; usr = nextusr) {
+ nextusr = RB_NEXT(iked_users, &env->sc_users, usr);
+ RB_REMOVE(iked_users, &env->sc_users, usr);
+ free(usr);
+ }
+ }
+
+ return (0);
+}
+
+int
+config_setsocket(struct iked *env, struct sockaddr_storage *ss,
+ in_port_t port, enum iked_procid id)
+{
+ int s;
+
+ if ((s = udp_bind((struct sockaddr *)ss, port)) == -1)
+ return (-1);
+ imsg_compose_proc(env, id, IMSG_UDP_SOCKET, s,
+ ss, sizeof(*ss));
+ return (0);
+}
+
+int
+config_getsocket(struct iked *env, struct imsg *imsg,
+ void (*cb)(int, short, void *))
+{
+ struct iked_socket *sock;
+
+ log_debug("%s: received socket fd %d", __func__, imsg->fd);
+
+ if ((sock = calloc(1, sizeof(*sock))) == NULL)
+ fatal("config_getsocket: calloc");
+
+ IMSG_SIZE_CHECK(imsg, &sock->sock_addr);
+
+ memcpy(&sock->sock_addr, imsg->data, sizeof(sock->sock_addr));
+ sock->sock_fd = imsg->fd;
+ sock->sock_env = env;
+
+ event_set(&sock->sock_ev, sock->sock_fd,
+ EV_READ|EV_PERSIST, cb, sock);
+ event_add(&sock->sock_ev, NULL);
+
+ return (0);
+}
+
+int
+config_setpfkey(struct iked *env, enum iked_procid id)
+{
+ int s;
+
+ if ((s = pfkey_init()) == -1)
+ return (-1);
+ imsg_compose_proc(env, id, IMSG_PFKEY_SOCKET, s, NULL, 0);
+ return (0);
+}
+
+int
+config_getpfkey(struct iked *env, struct imsg *imsg)
+{
+ log_debug("%s: received pfkey fd %d", __func__, imsg->fd);
+ env->sc_pfkey = imsg->fd;
+ return (0);
+}
+
+int
+config_setuser(struct iked *env, struct iked_user *usr, enum iked_procid id)
+{
+ if (env->sc_opts & IKED_OPT_NOACTION) {
+ print_user(usr);
+ return (0);
+ }
+
+ imsg_compose_proc(env, id, IMSG_CFG_USER, -1, usr, sizeof(*usr));
+ return (0);
+}
+
+int
+config_getuser(struct iked *env, struct imsg *imsg)
+{
+ struct iked_user usr;
+
+ IMSG_SIZE_CHECK(imsg, &usr);
+ memcpy(&usr, imsg->data, sizeof(usr));
+
+ if (config_new_user(env, &usr) == NULL)
+ return (-1);
+
+ print_user(&usr);
+
+ return (0);
+}
+
+int
+config_setpolicy(struct iked *env, struct iked_policy *pol,
+ enum iked_procid id)
+{
+ struct iked_proposal *prop;
+ struct iked_flow *flow;
+ struct iked_transform *xform;
+ size_t size, iovcnt, j, c = 0;
+ struct iovec iov[IOV_MAX];
+
+ iovcnt = 1;
+ size = sizeof(*pol);
+ TAILQ_FOREACH(prop, &pol->pol_proposals, prop_entry) {
+ size += (prop->prop_nxforms * sizeof(*xform)) +
+ (sizeof(*prop));
+ iovcnt += prop->prop_nxforms + 1;
+ }
+
+ size += pol->pol_nflows * sizeof(*flow);
+ iovcnt += pol->pol_nflows;
+
+ if (iovcnt > IOV_MAX) {
+ log_warn("%s: too many proposals/flows", __func__);
+ return (-1);
+ }
+
+ iov[c].iov_base = pol;
+ iov[c++].iov_len = sizeof(*pol);
+
+ TAILQ_FOREACH(prop, &pol->pol_proposals, prop_entry) {
+ iov[c].iov_base = prop;
+ iov[c++].iov_len = sizeof(*prop);
+
+ for (j = 0; j < prop->prop_nxforms; j++) {
+ xform = prop->prop_xforms + j;
+
+ iov[c].iov_base = xform;
+ iov[c++].iov_len = sizeof(*xform);
+ }
+ }
+
+ TAILQ_FOREACH(flow, &pol->pol_flows, flow_entry) {
+ iov[c].iov_base = flow;
+ iov[c++].iov_len = sizeof(*flow);
+ }
+
+ if (env->sc_opts & IKED_OPT_NOACTION) {
+ print_policy(pol);
+ return (0);
+ }
+
+ if (imsg_composev_proc(env, id, IMSG_CFG_POLICY, -1,
+ iov, iovcnt) == -1)
+ return (-1);
+
+ return (0);
+}
+
+int
+config_getpolicy(struct iked *env, struct imsg *imsg)
+{
+ struct iked_policy *pol, *old;
+ struct iked_proposal pp, *prop;
+ struct iked_transform xf, *xform;
+ struct iked_flow *flow;
+ off_t offset = 0;
+ u_int i, j;
+ u_int8_t *buf = (u_int8_t *)imsg->data;
+
+ IMSG_SIZE_CHECK(imsg, pol);
+ log_debug("%s: received policy", __func__);
+
+ if ((pol = config_new_policy(NULL)) == NULL)
+ fatal("config_getpolicy: new policy");
+
+ memcpy(pol, buf, sizeof(*pol));
+ offset += sizeof(*pol);
+
+ TAILQ_INIT(&pol->pol_proposals);
+ TAILQ_INIT(&pol->pol_flows);
+
+ for (i = 0; i < pol->pol_nproposals; i++) {
+ memcpy(&pp, buf + offset, sizeof(pp));
+ offset += sizeof(pp);
+
+ if ((prop = config_add_proposal(&pol->pol_proposals,
+ pp.prop_id, pp.prop_protoid)) == NULL)
+ fatal("config_getpolicy: add proposal");
+
+ for (j = 0; j < pp.prop_nxforms; j++) {
+ memcpy(&xf, buf + offset, sizeof(xf));
+ offset += sizeof(xf);
+
+ if ((xform = config_add_transform(prop, xf.xform_type,
+ xf.xform_id, xf.xform_length,
+ xf.xform_keylength)) == NULL)
+ fatal("config_getpolicy: add transform");
+ }
+ }
+
+ for (i = 0; i < pol->pol_nflows; i++) {
+ if ((flow = calloc(1, sizeof(*flow))) == NULL)
+ fatal("config_getpolicy: new flow");
+
+ memcpy(flow, buf + offset, sizeof(*flow));
+ offset += sizeof(*flow);
+
+ TAILQ_INSERT_TAIL(&pol->pol_flows, flow, flow_entry);
+ }
+
+ if ((old = RB_INSERT(iked_policies,
+ &env->sc_policies, pol)) != NULL) {
+ config_free_policy(env, old);
+ RB_INSERT(iked_policies, &env->sc_policies, pol);
+ }
+
+ if (pol->pol_flags & IKED_POLICY_DEFAULT)
+ env->sc_defaultcon = pol;
+
+ print_policy(pol);
+
+ return (0);
+}
diff --git a/sbin/iked/control.c b/sbin/iked/control.c
new file mode 100644
index 00000000000..01ab6de048e
--- /dev/null
+++ b/sbin/iked/control.c
@@ -0,0 +1,295 @@
+/* $OpenBSD: control.c,v 1.1 2010/06/03 16:41:12 reyk Exp $ */
+/* $vantronix: control.c,v 1.4 2010/05/14 07:35:52 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/queue.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/tree.h>
+
+#include <net/if.h>
+
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include "iked.h"
+
+#define CONTROL_BACKLOG 5
+
+struct ctl_connlist ctl_conns;
+
+void
+ control_accept(int, short, void *);
+struct ctl_conn
+ *control_connbyfd(int);
+void control_close(int);
+void control_dispatch_imsg(int, short, void *);
+void control_imsg_forward(struct imsg *);
+
+int
+control_init(struct iked *env, struct control_sock *cs)
+{
+ struct sockaddr_un sun;
+ int fd;
+ mode_t old_umask, mode;
+
+ if (cs->cs_name == NULL)
+ return (0);
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+ log_warn("%s: socket", __func__);
+ return (-1);
+ }
+
+ sun.sun_family = AF_UNIX;
+ if (strlcpy(sun.sun_path, cs->cs_name,
+ sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) {
+ log_warn("%s: %s name too long", __func__, cs->cs_name);
+ close(fd);
+ return (-1);
+ }
+
+ if (unlink(cs->cs_name) == -1)
+ if (errno != ENOENT) {
+ log_warn("%s: unlink %s", __func__, cs->cs_name);
+ close(fd);
+ return (-1);
+ }
+
+ if (cs->cs_restricted) {
+ old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH);
+ mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
+ } else {
+ old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
+ mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP;
+ }
+
+ if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
+ log_warn("%s: bind: %s", __func__, cs->cs_name);
+ close(fd);
+ (void)umask(old_umask);
+ return (-1);
+ }
+ (void)umask(old_umask);
+
+ if (chmod(cs->cs_name, mode) == -1) {
+ log_warn("%s: chmod", __func__);
+ close(fd);
+ (void)unlink(cs->cs_name);
+ return (-1);
+ }
+
+ socket_set_blockmode(fd, BM_NONBLOCK);
+ cs->cs_fd = fd;
+ cs->cs_env = env;
+
+ return (0);
+}
+
+int
+control_listen(struct control_sock *cs)
+{
+ if (cs->cs_name == NULL)
+ return (0);
+
+ if (listen(cs->cs_fd, CONTROL_BACKLOG) == -1) {
+ log_warn("%s: listen");
+ return (-1);
+ }
+
+ event_set(&cs->cs_ev, cs->cs_fd, EV_READ | EV_PERSIST,
+ control_accept, cs->cs_env);
+ event_add(&cs->cs_ev, NULL);
+
+ return (0);
+}
+
+void
+control_cleanup(struct control_sock *cs)
+{
+ if (cs->cs_name == NULL)
+ return;
+ (void)unlink(cs->cs_name);
+}
+
+/* ARGSUSED */
+void
+control_accept(int listenfd, short event, void *arg)
+{
+ struct iked *env = arg;
+ int connfd;
+ socklen_t len;
+ struct sockaddr_un sun;
+ struct ctl_conn *c;
+
+ len = sizeof(sun);
+ if ((connfd = accept(listenfd,
+ (struct sockaddr *)&sun, &len)) == -1) {
+ if (errno != EWOULDBLOCK && errno != EINTR)
+ log_warn("%s: accept");
+ return;
+ }
+
+ socket_set_blockmode(connfd, BM_NONBLOCK);
+
+ if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) {
+ log_warn("%s", __func__);
+ close(connfd);
+ return;
+ }
+
+ imsg_init(&c->iev.ibuf, connfd);
+ c->iev.handler = control_dispatch_imsg;
+ c->iev.events = EV_READ;
+ event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events,
+ c->iev.handler, env);
+ event_add(&c->iev.ev, NULL);
+
+ TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
+}
+
+struct ctl_conn *
+control_connbyfd(int fd)
+{
+ struct ctl_conn *c;
+
+ for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->iev.ibuf.fd != fd;
+ c = TAILQ_NEXT(c, entry))
+ ; /* nothing */
+
+ return (c);
+}
+
+void
+control_close(int fd)
+{
+ struct ctl_conn *c;
+
+ if ((c = control_connbyfd(fd)) == NULL) {
+ log_warn("%s: fd %d: not found", __func__, fd);
+ return;
+ }
+
+ msgbuf_clear(&c->iev.ibuf.w);
+ TAILQ_REMOVE(&ctl_conns, c, entry);
+
+ event_del(&c->iev.ev);
+ close(c->iev.ibuf.fd);
+ free(c);
+}
+
+/* ARGSUSED */
+void
+control_dispatch_imsg(int fd, short event, void *arg)
+{
+ struct iked *env = arg;
+ struct ctl_conn *c;
+ struct imsg imsg;
+ int n, v;
+
+ if ((c = control_connbyfd(fd)) == NULL) {
+ log_warn("%s: fd %d: not found", __func__, fd);
+ return;
+ }
+
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(&c->iev.ibuf)) == -1 || n == 0) {
+ control_close(fd);
+ return;
+ }
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&c->iev.ibuf.w) < 0) {
+ control_close(fd);
+ return;
+ }
+ imsg_event_add(&c->iev);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ for (;;) {
+ if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) {
+ control_close(fd);
+ return;
+ }
+
+ if (n == 0)
+ break;
+
+ control_imsg_forward(&imsg);
+
+ switch (imsg.hdr.type) {
+ case IMSG_CTL_NOTIFY:
+ if (c->flags & CTL_CONN_NOTIFY) {
+ log_debug("%s: "
+ "client requested notify more than once",
+ __func__);
+ imsg_compose_event(&c->iev, IMSG_CTL_FAIL,
+ 0, 0, -1, NULL, 0);
+ break;
+ }
+ c->flags |= CTL_CONN_NOTIFY;
+ break;
+ case IMSG_CTL_VERBOSE:
+ IMSG_SIZE_CHECK(&imsg, &v);
+
+ memcpy(&v, imsg.data, sizeof(v));
+ log_verbose(v);
+
+ imsg_forward_proc(env, &imsg, PROC_PARENT);
+ imsg_forward_proc(env, &imsg, PROC_IKEV1);
+ imsg_forward_proc(env, &imsg, PROC_IKEV2);
+ break;
+ case IMSG_CTL_RELOAD:
+ case IMSG_CTL_RESET:
+ imsg_forward_proc(env, &imsg, PROC_PARENT);
+ break;
+ default:
+ log_debug("%s: error handling imsg %d",
+ __func__, imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+
+ imsg_event_add(&c->iev);
+}
+
+void
+control_imsg_forward(struct imsg *imsg)
+{
+ struct ctl_conn *c;
+
+ TAILQ_FOREACH(c, &ctl_conns, entry)
+ if (c->flags & CTL_CONN_NOTIFY)
+ imsg_compose(&c->iev.ibuf, imsg->hdr.type,
+ 0, imsg->hdr.pid, -1, imsg->data,
+ imsg->hdr.len - IMSG_HEADER_SIZE);
+}
diff --git a/sbin/iked/crypto.c b/sbin/iked/crypto.c
new file mode 100644
index 00000000000..29a0137894b
--- /dev/null
+++ b/sbin/iked/crypto.c
@@ -0,0 +1,686 @@
+/* $OpenBSD: crypto.c,v 1.1 2010/06/03 16:41:12 reyk Exp $ */
+/* $vantronix: crypto.c,v 1.18 2010/05/28 15:34:35 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <event.h>
+
+#include <openssl/hmac.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+#include <openssl/md5.h>
+#include <openssl/x509.h>
+
+#include "iked.h"
+#include "ikev2.h"
+
+struct iked_hash *
+hash_new(u_int8_t type, u_int16_t id)
+{
+ struct iked_hash *hash;
+ const EVP_MD *md = NULL;
+ HMAC_CTX *ctx = NULL;
+ int length = 0, fixedkey = 0, trunc = 0;
+
+ switch (type) {
+ case IKEV2_XFORMTYPE_PRF:
+ switch (id) {
+ case IKEV2_XFORMPRF_HMAC_MD5:
+ md = EVP_md5();
+ length = MD5_DIGEST_LENGTH;
+ break;
+ case IKEV2_XFORMPRF_HMAC_SHA1:
+ md = EVP_sha1();
+ length = SHA_DIGEST_LENGTH;
+ break;
+ case IKEV2_XFORMPRF_HMAC_SHA2_256:
+ md = EVP_sha256();
+ length = SHA256_DIGEST_LENGTH;
+ break;
+ case IKEV2_XFORMPRF_HMAC_SHA2_384:
+ md = EVP_sha384();
+ length = SHA384_DIGEST_LENGTH;
+ break;
+ case IKEV2_XFORMPRF_HMAC_SHA2_512:
+ md = EVP_sha512();
+ length = SHA512_DIGEST_LENGTH;
+ break;
+ case IKEV2_XFORMPRF_AES128_XCBC:
+ fixedkey = 128 / 8;
+ length = fixedkey;
+ /* FALLTHROUGH */
+ case IKEV2_XFORMPRF_HMAC_TIGER:
+ case IKEV2_XFORMPRF_AES128_CMAC:
+ default:
+ log_debug("%s: prf %s not supported",
+ print_map(id, ikev2_xformprf_map));
+ break;
+ }
+ break;
+ case IKEV2_XFORMTYPE_INTEGR:
+ switch (id) {
+ case IKEV2_XFORMAUTH_HMAC_MD5_96:
+ md = EVP_md5();
+ length = MD5_DIGEST_LENGTH;
+ trunc = 12;
+ break;
+ case IKEV2_XFORMAUTH_HMAC_SHA1_96:
+ md = EVP_sha1();
+ length = SHA_DIGEST_LENGTH;
+ trunc = 12;
+ break;
+ case IKEV2_XFORMAUTH_HMAC_SHA2_256_128:
+ md = EVP_sha256();
+ length = SHA256_DIGEST_LENGTH;
+ trunc = 16;
+ break;
+ case IKEV2_XFORMAUTH_HMAC_SHA2_384_192:
+ md = EVP_sha384();
+ length = SHA384_DIGEST_LENGTH;
+ trunc = 24;
+ break;
+ case IKEV2_XFORMAUTH_HMAC_SHA2_512_256:
+ md = EVP_sha512();
+ length = SHA512_DIGEST_LENGTH;
+ trunc = 32;
+ break;
+ case IKEV2_XFORMAUTH_NONE:
+ case IKEV2_XFORMAUTH_DES_MAC:
+ case IKEV2_XFORMAUTH_KPDK_MD5:
+ case IKEV2_XFORMAUTH_AES_XCBC_96:
+ case IKEV2_XFORMAUTH_HMAC_MD5_128:
+ case IKEV2_XFORMAUTH_HMAC_SHA1_160:
+ case IKEV2_XFORMAUTH_AES_CMAC_96:
+ case IKEV2_XFORMAUTH_AES_128_GMAC:
+ case IKEV2_XFORMAUTH_AES_192_GMAC:
+ case IKEV2_XFORMAUTH_AES_256_GMAC:
+ default:
+ log_debug("%s: auth %s not supported",
+ print_map(id, ikev2_xformauth_map));
+ break;
+ }
+ break;
+ default:
+ log_debug("%s: hash type %s not supported",
+ print_map(id, ikev2_xformtype_map));
+ break;
+ }
+ if (md == NULL)
+ return (NULL);
+
+ if ((hash = calloc(1, sizeof(*hash))) == NULL) {
+ log_debug("%s: alloc hash", __func__);
+ return (NULL);
+ }
+
+ hash->hash_type = type;
+ hash->hash_id = id;
+ hash->hash_priv = md;
+ hash->hash_ctx = NULL;
+ hash->hash_trunc = trunc;
+ hash->hash_length = length;
+ hash->hash_fixedkey = fixedkey;
+
+ if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
+ log_debug("%s: alloc hash ctx", __func__);
+ hash_free(hash);
+ return (NULL);
+ }
+
+ HMAC_CTX_init(ctx);
+ hash->hash_ctx = ctx;
+
+ return (hash);
+}
+
+struct ibuf *
+hash_setkey(struct iked_hash *hash, void *key, size_t keylen)
+{
+ ibuf_release(hash->hash_key);
+ if ((hash->hash_key = ibuf_new(key, keylen)) == NULL) {
+ log_debug("%s: alloc hash key", __func__);
+ return (NULL);
+ }
+ return (hash->hash_key);
+}
+
+void
+hash_free(struct iked_hash *hash)
+{
+ if (hash == NULL)
+ return;
+ if (hash->hash_ctx != NULL) {
+ HMAC_CTX_cleanup(hash->hash_ctx);
+ free(hash->hash_ctx);
+ }
+ ibuf_release(hash->hash_key);
+ free(hash);
+}
+
+void
+hash_init(struct iked_hash *hash)
+{
+ HMAC_Init_ex(hash->hash_ctx, hash->hash_key->buf,
+ ibuf_length(hash->hash_key), hash->hash_priv, NULL);
+}
+
+void
+hash_update(struct iked_hash *hash, void *buf, size_t len)
+{
+ HMAC_Update(hash->hash_ctx, buf, len);
+}
+
+void
+hash_final(struct iked_hash *hash, void *buf, size_t *len)
+{
+ u_int length = 0;
+
+ HMAC_Final(hash->hash_ctx, buf, &length);
+ *len = (size_t)length;
+
+ /* Truncate the result if required by the alg */
+ if (hash->hash_trunc && *len > hash->hash_trunc)
+ *len = hash->hash_trunc;
+}
+
+size_t
+hash_length(struct iked_hash *hash)
+{
+ if (hash->hash_trunc)
+ return (hash->hash_trunc);
+ return (hash->hash_length);
+}
+
+size_t
+hash_keylength(struct iked_hash *hash)
+{
+ return (hash->hash_length);
+}
+
+struct iked_cipher *
+cipher_new(u_int8_t type, u_int16_t id, u_int16_t id_length)
+{
+ struct iked_cipher *encr;
+ const EVP_CIPHER *cipher = NULL;
+ EVP_CIPHER_CTX *ctx = NULL;
+ int length = 0, fixedkey = 0, ivlength = 0;
+
+ switch (type) {
+ case IKEV2_XFORMTYPE_ENCR:
+ switch (id) {
+ case IKEV2_XFORMENCR_3DES:
+ cipher = EVP_des_ede3_cbc();
+ length = EVP_CIPHER_block_size(cipher);
+ fixedkey = EVP_CIPHER_key_length(cipher);
+ ivlength = EVP_CIPHER_iv_length(cipher);
+ break;
+ case IKEV2_XFORMENCR_AES_CBC:
+ switch (id_length) {
+ case 128:
+ cipher = EVP_aes_128_cbc();
+ break;
+ case 192:
+ cipher = EVP_aes_192_cbc();
+ break;
+ case 256:
+ cipher = EVP_aes_256_cbc();
+ break;
+ default:
+ log_debug("%s: invalid key length %d"
+ " for cipher %s", __func__, id_length,
+ print_map(id, ikev2_xformencr_map));
+ break;
+ }
+ if (cipher == NULL)
+ break;
+ length = id_length / 8;
+ ivlength = EVP_CIPHER_iv_length(cipher);
+ fixedkey = EVP_CIPHER_key_length(cipher);
+ break;
+ case IKEV2_XFORMENCR_DES_IV64:
+ case IKEV2_XFORMENCR_DES:
+ case IKEV2_XFORMENCR_RC5:
+ case IKEV2_XFORMENCR_IDEA:
+ case IKEV2_XFORMENCR_CAST:
+ case IKEV2_XFORMENCR_BLOWFISH:
+ case IKEV2_XFORMENCR_3IDEA:
+ case IKEV2_XFORMENCR_DES_IV32:
+ case IKEV2_XFORMENCR_NULL:
+ case IKEV2_XFORMENCR_AES_CTR:
+ /* FALLTHROUGH */
+ default:
+ log_debug("%s: cipher %s not supported",
+ print_map(id, ikev2_xformencr_map));
+ cipher = NULL;
+ break;
+ }
+ break;
+ default:
+ log_debug("%s: cipher type %s not supported",
+ print_map(id, ikev2_xformtype_map));
+ break;
+ }
+ if (cipher == NULL)
+ return (NULL);
+
+ if ((encr = calloc(1, sizeof(*encr))) == NULL) {
+ log_debug("%s: alloc cipher", __func__);
+ return (NULL);
+ }
+
+ encr->encr_id = id;
+ encr->encr_priv = cipher;
+ encr->encr_ctx = NULL;
+ encr->encr_length = length;
+ encr->encr_fixedkey = fixedkey;
+ encr->encr_ivlength = ivlength ? ivlength : length;
+
+ if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
+ log_debug("%s: alloc cipher ctx", __func__);
+ cipher_free(encr);
+ return (NULL);
+ }
+
+ EVP_CIPHER_CTX_init(ctx);
+ EVP_CIPHER_CTX_set_padding(ctx, 0);
+ encr->encr_ctx = ctx;
+
+ return (encr);
+}
+
+struct ibuf *
+cipher_setkey(struct iked_cipher *encr, void *key, size_t keylen)
+{
+ ibuf_release(encr->encr_key);
+ if ((encr->encr_key = ibuf_new(key, keylen)) == NULL) {
+ log_debug("%s: alloc cipher key", __func__);
+ return (NULL);
+ }
+ return (encr->encr_key);
+}
+
+struct ibuf *
+cipher_setiv(struct iked_cipher *encr, void *iv, size_t len)
+{
+ ibuf_release(encr->encr_iv);
+ if (iv != NULL) {
+ if (len < encr->encr_ivlength) {
+ log_debug("%s: invalid IV length %d", len);
+ return (NULL);
+ }
+ encr->encr_iv = ibuf_new(iv, encr->encr_ivlength);
+ } else {
+ /* Get new random IV */
+ encr->encr_iv = ibuf_random(encr->encr_ivlength);
+ }
+ if (encr->encr_iv == NULL) {
+ log_debug("%s: failed to set IV", __func__);
+ return (NULL);
+ }
+ return (encr->encr_iv);
+}
+
+void
+cipher_free(struct iked_cipher *encr)
+{
+ if (encr == NULL)
+ return;
+ if (encr->encr_ctx != NULL) {
+ EVP_CIPHER_CTX_cleanup(encr->encr_ctx);
+ free(encr->encr_ctx);
+ }
+ ibuf_release(encr->encr_key);
+ free(encr);
+}
+
+void
+cipher_init(struct iked_cipher *encr, int enc)
+{
+ EVP_CipherInit_ex(encr->encr_ctx, encr->encr_priv, NULL,
+ ibuf_data(encr->encr_key), ibuf_data(encr->encr_iv), enc);
+}
+
+void
+cipher_init_encrypt(struct iked_cipher *encr)
+{
+ cipher_init(encr, 1);
+}
+
+void
+cipher_init_decrypt(struct iked_cipher *encr)
+{
+ cipher_init(encr, 0);
+}
+
+void
+cipher_update(struct iked_cipher *encr, void *in, size_t inlen,
+ void *out, size_t *outlen)
+{
+ int olen;
+
+ olen = 0;
+ EVP_CipherUpdate(encr->encr_ctx, out, &olen, in, inlen);
+ *outlen = (size_t)olen;
+}
+
+void
+cipher_final(struct iked_cipher *encr, void *out, size_t *outlen)
+{
+ int olen;
+
+ olen = 0;
+ if (!EVP_CipherFinal_ex(encr->encr_ctx, out, &olen)) {
+ ca_sslerror();
+ *outlen = 0;
+ return;
+ }
+ *outlen = (size_t)olen;
+}
+
+size_t
+cipher_length(struct iked_cipher *encr)
+{
+ return (encr->encr_length);
+}
+
+size_t
+cipher_keylength(struct iked_cipher *encr)
+{
+ if (encr->encr_fixedkey)
+ return (encr->encr_fixedkey);
+
+ /* Might return zero */
+ return (ibuf_length(encr->encr_key));
+}
+
+size_t
+cipher_ivlength(struct iked_cipher *encr)
+{
+ return (encr->encr_ivlength);
+}
+
+size_t
+cipher_outlength(struct iked_cipher *encr, size_t inlen)
+{
+ return (inlen + encr->encr_length);
+}
+
+struct iked_dsa *
+dsa_new(u_int16_t id, struct iked_hash *prf, int sign)
+{
+ struct iked_dsa *dsap = NULL, dsa;
+
+ bzero(&dsa, sizeof(dsa));
+
+ switch (id) {
+ case IKEV2_AUTH_RSA_SIG:
+ /*
+ * XXX RFC4306 is not very clear about this and the
+ * XXX informational RFC4718 says that we should use
+ * XXX SHA1 here, but shouldn't we use the negotiated PRF
+ * XXX alg instead?
+ */
+ if ((dsa.dsa_priv =
+ EVP_get_digestbyname("sha1WithRSAEncryption")) == NULL)
+ fatalx("dsa_new: cipher not available");
+ break;
+ case IKEV2_AUTH_SHARED_KEY_MIC:
+ if (prf == NULL || prf->hash_priv == NULL)
+ fatalx("dsa_new: invalid PRF");
+ dsa.dsa_priv = prf->hash_priv;
+ dsa.dsa_hmac = 1;
+ break;
+ case IKEV2_AUTH_DSS_SIG:
+ dsa.dsa_priv = EVP_dss1();
+ break;
+ case IKEV2_AUTH_ECDSA_256:
+ dsa.dsa_priv = EVP_sha256();
+ break;
+ case IKEV2_AUTH_ECDSA_384:
+ dsa.dsa_priv = EVP_sha384();
+ break;
+ case IKEV2_AUTH_ECDSA_512:
+ dsa.dsa_priv = EVP_sha512();
+ break;
+ default:
+ log_debug("%s: auth method %s not supported",
+ print_map(id, ikev2_auth_map));
+ break;
+ }
+
+ if ((dsap = calloc(1, sizeof(*dsap))) == NULL) {
+ log_debug("%s: alloc dsa ctx", __func__);
+
+ return (NULL);
+ }
+ memcpy(dsap, &dsa, sizeof(*dsap));
+
+ dsap->dsa_method = id;
+ dsap->dsa_sign = sign;
+
+ if (dsap->dsa_hmac) {
+ if ((dsap->dsa_ctx = calloc(1, sizeof(HMAC_CTX))) == NULL) {
+ log_debug("%s: alloc hash ctx", __func__);
+ dsa_free(dsap);
+ return (NULL);
+ }
+ HMAC_CTX_init((HMAC_CTX *)dsap->dsa_ctx);
+ } else {
+ if ((dsap->dsa_ctx = EVP_MD_CTX_create()) == NULL) {
+ log_debug("%s: alloc digest ctx", __func__);
+ dsa_free(dsap);
+ return (NULL);
+ }
+ }
+
+ return (dsap);
+}
+
+struct iked_dsa *
+dsa_sign_new(u_int16_t id, struct iked_hash *prf)
+{
+ return (dsa_new(id, prf, 1));
+}
+
+struct iked_dsa *
+dsa_verify_new(u_int16_t id, struct iked_hash *prf)
+{
+ return (dsa_new(id, prf, 0));
+}
+
+void
+dsa_free(struct iked_dsa *dsa)
+{
+ if (dsa == NULL)
+ return;
+ if (dsa->dsa_hmac) {
+ HMAC_CTX_cleanup((HMAC_CTX *)dsa->dsa_ctx);
+ free(dsa->dsa_ctx);
+ } else {
+ EVP_MD_CTX_destroy((EVP_MD_CTX *)dsa->dsa_ctx);
+ if (dsa->dsa_key)
+ EVP_PKEY_free(dsa->dsa_key);
+ if (dsa->dsa_cert)
+ X509_free(dsa->dsa_cert);
+ }
+
+ ibuf_release(dsa->dsa_keydata);
+}
+
+struct ibuf *
+dsa_setkey(struct iked_dsa *dsa, void *key, size_t keylen, u_int8_t type)
+{
+ BIO *rawcert = NULL;
+ X509 *cert = NULL;
+ RSA *rsa = NULL;
+ EVP_PKEY *pkey = NULL;
+
+ ibuf_release(dsa->dsa_keydata);
+ if ((dsa->dsa_keydata = ibuf_new(key, keylen)) == NULL) {
+ log_debug("%s: alloc signature key", __func__);
+ return (NULL);
+ }
+
+ if ((rawcert = BIO_new_mem_buf(key, keylen)) == NULL)
+ goto err;
+
+ switch (type) {
+ case IKEV2_CERT_X509_CERT:
+ if ((cert = d2i_X509_bio(rawcert, NULL)) == NULL)
+ goto sslerr;
+ if ((pkey = X509_get_pubkey(cert)) == NULL)
+ goto sslerr;
+ dsa->dsa_cert = cert;
+ dsa->dsa_key = pkey;
+ break;
+ case IKEV2_CERT_RSA_KEY:
+ if (dsa->dsa_sign) {
+ if ((rsa = d2i_RSAPrivateKey_bio(rawcert,
+ NULL)) == NULL)
+ goto sslerr;
+ } else {
+ if ((rsa = d2i_RSAPublicKey_bio(rawcert,
+ NULL)) == NULL)
+ goto sslerr;
+ }
+
+ if ((pkey = EVP_PKEY_new()) == NULL)
+ goto sslerr;
+ if (!EVP_PKEY_set1_RSA(pkey, rsa))
+ goto sslerr;
+
+ dsa->dsa_cert = NULL;
+ dsa->dsa_key = pkey;
+ break;
+ default:
+ if (dsa->dsa_hmac)
+ break;
+ log_debug("%s: unsupported key type", __func__);
+ goto err;
+ }
+
+ return (dsa->dsa_keydata);
+
+ sslerr:
+ ca_sslerror();
+ err:
+ log_debug("%s: error", __func__);
+
+ if (rsa != NULL)
+ RSA_free(rsa);
+ if (pkey != NULL)
+ EVP_PKEY_free(pkey);
+ if (cert != NULL)
+ X509_free(cert);
+ if (rawcert != NULL)
+ BIO_free(rawcert);
+ ibuf_release(dsa->dsa_keydata);
+ return (NULL);
+}
+
+int
+dsa_init(struct iked_dsa *dsa)
+{
+ int ret;
+
+ if (dsa->dsa_hmac) {
+ HMAC_Init_ex(dsa->dsa_ctx, ibuf_data(dsa->dsa_keydata),
+ ibuf_length(dsa->dsa_keydata), dsa->dsa_priv, NULL);
+ return (0);
+ }
+
+ if (dsa->dsa_sign)
+ ret = EVP_SignInit_ex(dsa->dsa_ctx, dsa->dsa_priv, NULL);
+ else
+ ret = EVP_VerifyInit_ex(dsa->dsa_ctx, dsa->dsa_priv, NULL);
+
+ return (ret ? 0 : -1);
+}
+
+int
+dsa_update(struct iked_dsa *dsa, const void *buf, size_t len)
+{
+ int ret = 1;
+
+ if (dsa->dsa_hmac)
+ HMAC_Update(dsa->dsa_ctx, buf, len);
+ else if (dsa->dsa_sign)
+ ret = EVP_SignUpdate(dsa->dsa_ctx, buf, len);
+ else
+ ret = EVP_VerifyUpdate(dsa->dsa_ctx, buf, len);
+
+ return (ret ? 0 : -1);
+}
+
+size_t
+dsa_length(struct iked_dsa *dsa)
+{
+ if (dsa->dsa_hmac)
+ return (EVP_MD_size(dsa->dsa_priv));
+ return (EVP_PKEY_size(dsa->dsa_key));
+}
+
+ssize_t
+dsa_sign_final(struct iked_dsa *dsa, void *buf, size_t len)
+{
+ u_int siglen;
+
+ if (len < dsa_length(dsa))
+ return (-1);
+
+ if (dsa->dsa_hmac)
+ HMAC_Final(dsa->dsa_ctx, buf, &siglen);
+ else {
+ if (!EVP_SignFinal(dsa->dsa_ctx, buf, &siglen,
+ dsa->dsa_key))
+ return (-1);
+ }
+
+ return (siglen);
+}
+
+ssize_t
+dsa_verify_final(struct iked_dsa *dsa, void *buf, size_t len)
+{
+ u_int8_t sig[EVP_MAX_MD_SIZE];
+ u_int siglen = sizeof(sig);
+
+ if (dsa->dsa_hmac) {
+ HMAC_Final(dsa->dsa_ctx, sig, &siglen);
+ if (siglen != len || memcmp(buf, sig, siglen) != 0)
+ return (-1);
+ } else {
+ if (!EVP_VerifyFinal(dsa->dsa_ctx, buf, len,
+ dsa->dsa_key)) {
+ ca_sslerror();
+ return (-1);
+ }
+ }
+
+ return (0);
+}
diff --git a/sbin/iked/dh.c b/sbin/iked/dh.c
new file mode 100644
index 00000000000..497760ff4ba
--- /dev/null
+++ b/sbin/iked/dh.c
@@ -0,0 +1,657 @@
+/* $OpenBSD: dh.c,v 1.1 2010/06/03 16:41:12 reyk Exp $ */
+/* $vantronix: dh.c,v 1.13 2010/05/28 15:34:35 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <string.h>
+#include <event.h>
+
+#include <openssl/obj_mac.h>
+#include <openssl/dh.h>
+#include <openssl/ec.h>
+#include <openssl/ec.h>
+#include <openssl/ecdh.h>
+
+#include "iked.h"
+#include "dh.h"
+#include "ikev2.h"
+
+int modp_init(struct group *);
+int modp_getlen(struct group *);
+int modp_create_exchange(struct group *, u_int8_t *);
+int modp_create_shared(struct group *, u_int8_t *, u_int8_t *);
+
+int ec_init(struct group *);
+int ec_getlen(struct group *);
+int ec_create_exchange(struct group *, u_int8_t *);
+int ec_create_shared(struct group *, u_int8_t *, u_int8_t *);
+
+int ec_point2raw(struct group *, const EC_POINT *, u_int8_t *, size_t);
+EC_POINT *
+ ec_raw2point(struct group *, u_int8_t *, size_t);
+
+struct group_id ike_groups[] = {
+ { GROUP_MODP, 1, 768,
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+ "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ "E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF",
+ "02"
+ },
+ { GROUP_MODP, 2, 1024,
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+ "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+ "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381"
+ "FFFFFFFFFFFFFFFF",
+ "02"
+ },
+ { GROUP_EC, 3, 155, NULL, NULL, NID_ipsec3 },
+ { GROUP_EC, 4, 185, NULL, NULL, NID_ipsec4 },
+ { GROUP_MODP, 5, 1536,
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+ "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+ "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
+ "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF",
+ "02"
+ },
+ { GROUP_MODP, 14, 2048,
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+ "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+ "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
+ "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
+ "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+ "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"
+ "15728E5A8AACAA68FFFFFFFFFFFFFFFF",
+ "02"
+ },
+ { GROUP_MODP, 15, 3072,
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+ "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+ "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
+ "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
+ "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+ "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"
+ "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64"
+ "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7"
+ "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B"
+ "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31"
+ "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF",
+ "02"
+ },
+ { GROUP_MODP, 16, 4096,
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+ "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+ "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
+ "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
+ "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+ "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"
+ "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64"
+ "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7"
+ "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B"
+ "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31"
+ "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7"
+ "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA"
+ "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6"
+ "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED"
+ "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9"
+ "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199"
+ "FFFFFFFFFFFFFFFF",
+ "02"
+ },
+ { GROUP_MODP, 17, 6144,
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+ "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+ "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
+ "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
+ "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+ "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"
+ "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64"
+ "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7"
+ "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B"
+ "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31"
+ "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7"
+ "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA"
+ "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6"
+ "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED"
+ "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9"
+ "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"
+ "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD"
+ "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831"
+ "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B"
+ "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF"
+ "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6"
+ "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3"
+ "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA"
+ "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328"
+ "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C"
+ "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE"
+ "12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF",
+ "02"
+ },
+ { GROUP_MODP, 18, 8192,
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+ "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+ "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
+ "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
+ "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+ "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"
+ "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64"
+ "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7"
+ "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B"
+ "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31"
+ "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7"
+ "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA"
+ "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6"
+ "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED"
+ "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9"
+ "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"
+ "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD"
+ "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831"
+ "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B"
+ "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF"
+ "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6"
+ "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3"
+ "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA"
+ "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328"
+ "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C"
+ "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE"
+ "12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4"
+ "38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300"
+ "741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568"
+ "3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9"
+ "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B"
+ "4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A"
+ "062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36"
+ "4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1"
+ "B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92"
+ "4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47"
+ "9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71"
+ "60C980DD98EDD3DFFFFFFFFFFFFFFFFF",
+ "02"
+ },
+ { GROUP_EC, 19, 256, NULL, NULL, NID_X9_62_prime256v1 },
+ { GROUP_EC, 20, 384, NULL, NULL, NID_secp384r1 },
+ { GROUP_EC, 21, 521, NULL, NULL, NID_secp521r1 },
+ { GROUP_MODP, 22, 1024,
+ "B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C6"
+ "9A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C0"
+ "13ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD70"
+ "98488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0"
+ "A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708"
+ "DF1FB2BC2E4A4371",
+ "A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507F"
+ "D6406CFF14266D31266FEA1E5C41564B777E690F5504F213"
+ "160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1"
+ "909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28A"
+ "D662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24"
+ "855E6EEB22B3B2E5"
+ },
+ { GROUP_MODP, 23, 2048,
+ "AD107E1E9123A9D0D660FAA79559C51FA20D64E5683B9FD1"
+ "B54B1597B61D0A75E6FA141DF95A56DBAF9A3C407BA1DF15"
+ "EB3D688A309C180E1DE6B85A1274A0A66D3F8152AD6AC212"
+ "9037C9EDEFDA4DF8D91E8FEF55B7394B7AD5B7D0B6C12207"
+ "C9F98D11ED34DBF6C6BA0B2C8BBC27BE6A00E0A0B9C49708"
+ "B3BF8A317091883681286130BC8985DB1602E714415D9330"
+ "278273C7DE31EFDC7310F7121FD5A07415987D9ADC0A486D"
+ "CDF93ACC44328387315D75E198C641A480CD86A1B9E587E8"
+ "BE60E69CC928B2B9C52172E413042E9B23F10B0E16E79763"
+ "C9B53DCF4BA80A29E3FB73C16B8E75B97EF363E2FFA31F71"
+ "CF9DE5384E71B81C0AC4DFFE0C10E64F",
+ "AC4032EF4F2D9AE39DF30B5C8FFDAC506CDEBE7B89998CAF"
+ "74866A08CFE4FFE3A6824A4E10B9A6F0DD921F01A70C4AFA"
+ "AB739D7700C29F52C57DB17C620A8652BE5E9001A8D66AD7"
+ "C17669101999024AF4D027275AC1348BB8A762D0521BC98A"
+ "E247150422EA1ED409939D54DA7460CDB5F6C6B250717CBE"
+ "F180EB34118E98D119529A45D6F834566E3025E316A330EF"
+ "BB77A86F0C1AB15B051AE3D428C8F8ACB70A8137150B8EEB"
+ "10E183EDD19963DDD9E263E4770589EF6AA21E7F5F2FF381"
+ "B539CCE3409D13CD566AFBB48D6C019181E1BCFE94B30269"
+ "EDFE72FE9B6AA4BD7B5A0F1C71CFFF4C19C418E1F6EC0179"
+ "81BC087F2A7065B384B890D3191F2BFA"
+ },
+ { GROUP_MODP, 24, 2048,
+ "87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F2"
+ "5D2CEED4435E3B00E00DF8F1D61957D4FAF7DF4561B2AA30"
+ "16C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD"
+ "5A8A9D306BCF67ED91F9E6725B4758C022E0B1EF4275BF7B"
+ "6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C"
+ "4FDB70C581B23F76B63ACAE1CAA6B7902D52526735488A0E"
+ "F13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D9"
+ "67E144E5140564251CCACB83E6B486F6B3CA3F7971506026"
+ "C0B857F689962856DED4010ABD0BE621C3A3960A54E710C3"
+ "75F26375D7014103A4B54330C198AF126116D2276E11715F"
+ "693877FAD7EF09CADB094AE91E1A1597",
+ "3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF2054"
+ "07F4793A1A0BA12510DBC15077BE463FFF4FED4AAC0BB555"
+ "BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18"
+ "A55AE31341000A650196F931C77A57F2DDF463E5E9EC144B"
+ "777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC83"
+ "1D14348F6F2F9193B5045AF2767164E1DFC967C1FB3F2E55"
+ "A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14"
+ "C8484B1E052588B9B7D2BBD2DF016199ECD06E1557CD0915"
+ "B3353BBB64E0EC377FD028370DF92B52C7891428CDC67EB6"
+ "184B523D1DB246C32F63078490F00EF8D647D148D4795451"
+ "5E2327CFEF98C582664B4C0F6CC41659"
+ },
+ { GROUP_EC, 25, 192, NULL, NULL, NID_X9_62_prime192v1 },
+ { GROUP_EC, 26, 224, NULL, NULL, NID_secp224r1 }
+};
+
+void
+group_init(void)
+{
+ extern int debug;
+
+ if (debug > 2)
+ dh_selftest();
+
+ /* not used */
+ return;
+}
+
+void
+group_free(struct group *group)
+{
+ if (group == NULL)
+ return;
+ if (group->dh != NULL)
+ DH_free(group->dh);
+ if (group->ec != NULL)
+ EC_KEY_free(group->ec);
+ group->spec = NULL;
+}
+
+struct group *
+group_get(u_int32_t id)
+{
+ struct group_id *p = NULL;
+ struct group *group;
+ u_int i, items;
+
+ items = sizeof(ike_groups) / sizeof(ike_groups[0]);
+ for (i = 0; i < items; i++) {
+ if (id == ike_groups[i].id) {
+ p = &ike_groups[i];
+ break;
+ }
+ }
+ if (p == NULL)
+ return (NULL);
+
+ if ((group = calloc(1, sizeof(*group))) == NULL)
+ return (NULL);
+
+ group->id = id;
+ group->spec = p;
+
+ switch (p->type) {
+ case GROUP_MODP:
+ group->init = modp_init;
+ group->getlen = modp_getlen;
+ group->exchange = modp_create_exchange;
+ group->shared = modp_create_shared;
+ break;
+ case GROUP_EC:
+ group->init = ec_init;
+ group->getlen = ec_getlen;
+ group->exchange = ec_create_exchange;
+ group->shared = ec_create_shared;
+ break;
+ default:
+ log_debug("%s: unsupported dh type %d", __func__, p->type);
+ group_free(group);
+ return (NULL);
+ }
+
+ if (dh_init(group) != 0) {
+ group_free(group);
+ return (NULL);
+ }
+
+ return (group);
+}
+
+int
+dh_init(struct group *group)
+{
+ return (group->init(group));
+}
+
+int
+dh_getlen(struct group *group)
+{
+ return (group->getlen(group));
+}
+
+int
+dh_create_exchange(struct group *group, u_int8_t *buf)
+{
+ return (group->exchange(group, buf));
+}
+
+int
+dh_create_shared(struct group *group, u_int8_t *secret, u_int8_t *exchange)
+{
+ return (group->shared(group, secret, exchange));
+}
+
+int
+modp_init(struct group *group)
+{
+ DH *dh;
+
+ if ((dh = DH_new()) == NULL)
+ return (-1);
+
+ if (!BN_hex2bn(&dh->p, group->spec->prime) ||
+ !BN_hex2bn(&dh->g, group->spec->generator))
+ return (-1);
+
+ group->dh = dh;
+
+ return (0);
+}
+
+int
+modp_getlen(struct group *group)
+{
+ if (group->spec == NULL)
+ return (0);
+ return (roundup(group->spec->bits, 8) / 8);
+}
+
+int
+modp_create_exchange(struct group *group, u_int8_t *buf)
+{
+ int codes;
+ DH *dh = group->dh;
+
+ if (!DH_generate_key(dh))
+ return (-1);
+ if (!DH_check(dh, &codes))
+ return (-1);
+ if (!BN_bn2bin(dh->pub_key, buf))
+ return (-1);
+
+ return 0;
+}
+
+int
+modp_create_shared(struct group *group, u_int8_t *secret, u_int8_t *exchange)
+{
+ BIGNUM *ex;
+
+ if ((ex = BN_bin2bn(exchange, dh_getlen(group), NULL)) == NULL)
+ return (-1);
+ if (!DH_compute_key(secret, ex, group->dh))
+ return (-1);
+
+ return 0;
+}
+
+int
+ec_init(struct group *group)
+{
+ if ((group->ec = EC_KEY_new_by_curve_name(group->spec->nid)) == NULL)
+ return (-1);
+ if (!EC_KEY_generate_key(group->ec))
+ return (-1);
+ return (0);
+}
+
+int
+ec_getlen(struct group *group)
+{
+ if (group->spec == NULL)
+ return (0);
+ return ((roundup(group->spec->bits, 8) * 2) / 8);
+}
+
+int
+ec_create_exchange(struct group *group, u_int8_t *buf)
+{
+ return (ec_point2raw(group, EC_KEY_get0_public_key(group->ec),
+ buf, ec_getlen(group)));
+}
+
+int
+ec_create_shared(struct group *group, u_int8_t *secret, u_int8_t *exchange)
+{
+ const EC_GROUP *ecgroup = NULL;
+ const BIGNUM *privkey;
+ EC_POINT *exchangep = NULL, *secretp = NULL;
+ int ret = -1;
+
+ if ((ecgroup = EC_KEY_get0_group(group->ec)) == NULL ||
+ (privkey = EC_KEY_get0_private_key(group->ec)) == NULL)
+ goto done;
+
+ if ((exchangep =
+ ec_raw2point(group, exchange, ec_getlen(group))) == NULL)
+ goto done;
+
+ if ((secretp = EC_POINT_new(ecgroup)) == NULL)
+ goto done;
+
+ if (!EC_POINT_mul(ecgroup, secretp, NULL, exchangep, privkey, NULL))
+ goto done;
+
+ ret = ec_point2raw(group, secretp, secret, ec_getlen(group));
+
+ done:
+ if (exchangep != NULL)
+ EC_POINT_clear_free(exchangep);
+ if (secretp != NULL)
+ EC_POINT_clear_free(secretp);
+
+ return (ret);
+}
+
+int
+ec_point2raw(struct group *group, const EC_POINT *point,
+ u_int8_t *buf, size_t len)
+{
+ const EC_GROUP *ecgroup = NULL;
+ BN_CTX *bnctx = NULL;
+ BIGNUM *x = NULL, *y = NULL;
+ int ret = -1;
+ size_t xlen, ylen;
+ off_t xoff, yoff;
+
+ if ((bnctx = BN_CTX_new()) == NULL)
+ goto done;
+ BN_CTX_start(bnctx);
+ if ((x = BN_CTX_get(bnctx)) == NULL ||
+ (y = BN_CTX_get(bnctx)) == NULL)
+ goto done;
+
+ if ((ecgroup = EC_KEY_get0_group(group->ec)) == NULL)
+ goto done;
+
+ if (EC_METHOD_get_field_type(EC_GROUP_method_of(ecgroup)) ==
+ NID_X9_62_prime_field) {
+ if (!EC_POINT_get_affine_coordinates_GFp(ecgroup,
+ point, x, y, bnctx))
+ goto done;
+ } else {
+ if (!EC_POINT_get_affine_coordinates_GF2m(ecgroup,
+ point, x, y, bnctx))
+ goto done;
+ }
+
+ xlen = roundup(BN_num_bytes(x), 2);
+ xoff = xlen - BN_num_bytes(x);
+ if (!BN_bn2bin(x, buf + xoff))
+ goto done;
+
+ ylen = roundup(BN_num_bytes(y), 2);
+ yoff = (ylen - BN_num_bytes(y)) + xlen;
+ if (!BN_bn2bin(y, buf + yoff))
+ goto done;
+
+ ret = 0;
+ done:
+ BN_CTX_end(bnctx);
+ BN_CTX_free(bnctx);
+
+ return (ret);
+}
+
+EC_POINT *
+ec_raw2point(struct group *group, u_int8_t *buf, size_t len)
+{
+ const EC_GROUP *ecgroup = NULL;
+ EC_POINT *point = NULL;
+ BN_CTX *bnctx = NULL;
+ BIGNUM *x = NULL, *y = NULL;
+ int ret = -1;
+ size_t eclen;
+ size_t xlen, ylen;
+
+ if ((bnctx = BN_CTX_new()) == NULL)
+ goto done;
+ BN_CTX_start(bnctx);
+ if ((x = BN_CTX_get(bnctx)) == NULL ||
+ (y = BN_CTX_get(bnctx)) == NULL)
+ goto done;
+
+ eclen = ec_getlen(group);
+ if (len < eclen)
+ goto done;
+ xlen = ylen = eclen / 2;
+ if ((x = BN_bin2bn(buf, xlen, x)) == NULL ||
+ (y = BN_bin2bn(buf + xlen, ylen, y)) == NULL)
+ goto done;
+
+ if ((ecgroup = EC_KEY_get0_group(group->ec)) == NULL)
+ goto done;
+
+ if ((point = EC_POINT_new(ecgroup)) == NULL)
+ goto done;
+
+ if (EC_METHOD_get_field_type(EC_GROUP_method_of(ecgroup)) ==
+ NID_X9_62_prime_field) {
+ if (!EC_POINT_set_affine_coordinates_GFp(ecgroup,
+ point, x, y, bnctx))
+ goto done;
+ } else {
+ if (!EC_POINT_set_affine_coordinates_GF2m(ecgroup,
+ point, x, y, bnctx))
+ goto done;
+ }
+
+ ret = 0;
+ done:
+ if (ret != 0 && point != NULL)
+ EC_POINT_clear_free(point);
+ BN_CTX_end(bnctx);
+ BN_CTX_free(bnctx);
+
+ return (point);
+}
+
+void
+dh_selftest(void)
+{
+ struct group *adh = NULL, *bdh = NULL;
+ struct ibuf *asec = NULL, *aexc = NULL;
+ struct ibuf *bsec = NULL, *bexc = NULL;
+ size_t len;
+ int id;
+ struct timeval start, stop, tv;
+
+ /* Test DH */
+ for (id = 0; id < IKEV2_XFORMDH_MAX; id++) {
+ if ((adh = group_get(id)) == NULL ||
+ (bdh = group_get(id)) == NULL)
+ continue;
+
+ gettimeofday(&start, NULL);
+
+ print_debug("%s: DH group %d (%s)", __func__,
+ id, print_map(id, ikev2_xformdh_map));
+
+ if ((len = dh_getlen(adh)) != 0 &&
+ (aexc = ibuf_new(NULL, len)) != NULL &&
+ (dh_create_exchange(adh, aexc->buf)) != -1 &&
+ (bexc = ibuf_new(NULL, len)) != NULL &&
+ (dh_create_exchange(bdh, bexc->buf)) != -1 &&
+ (asec = ibuf_new(NULL, len)) != NULL &&
+ (dh_create_shared(adh, asec->buf, bexc->buf)) != -1 &&
+ (bsec = ibuf_new(NULL, len)) != NULL &&
+ (dh_create_shared(bdh, bsec->buf, aexc->buf)) != -1 &&
+ (ibuf_length(asec) == ibuf_length(bsec)) &&
+ (ibuf_length(asec) == len) &&
+ (memcmp(asec->buf, bsec->buf, len)) == 0)
+ print_debug(": success");
+ else
+ print_debug(": fail");
+
+ gettimeofday(&stop, NULL);
+ timersub(&stop, &start, &tv);
+
+ print_debug(" (%lu.%lu seconds)\n", tv.tv_sec, tv.tv_usec);
+
+ ibuf_release(aexc);
+ ibuf_release(bexc);
+ ibuf_release(asec);
+ ibuf_release(bsec);
+ aexc = bexc = asec = bsec = NULL;
+
+ group_free(adh);
+ group_free(bdh);
+ }
+}
diff --git a/sbin/iked/dh.h b/sbin/iked/dh.h
new file mode 100644
index 00000000000..52982d064a0
--- /dev/null
+++ b/sbin/iked/dh.h
@@ -0,0 +1,62 @@
+/* $OpenBSD: dh.h,v 1.1 2010/06/03 16:41:12 reyk Exp $ */
+/* $vantronix: dh.h,v 1.8 2010/06/02 12:22:58 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _DH_H_
+#define _DH_H_
+
+#include <sys/types.h>
+
+enum group_type {
+ GROUP_MODP = 0,
+ GROUP_EC = 1
+};
+
+struct group_id {
+ enum group_type type;
+ u_int id;
+ int bits;
+ char *prime;
+ char *generator;
+ int nid;
+};
+
+struct group {
+ int id;
+ struct group_id *spec;
+
+ void *dh;
+ void *ec;
+
+ int (*init)(struct group *);
+ int (*getlen)(struct group *);
+ int (*exchange)(struct group *, u_int8_t *);
+ int (*shared)(struct group *, u_int8_t *, u_int8_t *);
+};
+
+void group_init(void);
+void group_free(struct group *);
+struct group *group_get(u_int32_t);
+
+int dh_init(struct group *);
+int dh_getlen(struct group *);
+int dh_create_exchange(struct group *, u_int8_t *);
+int dh_create_shared(struct group *, u_int8_t *, u_int8_t *);
+void dh_selftest(void);
+
+#endif /* _DH_H_ */
diff --git a/sbin/iked/eap.c b/sbin/iked/eap.c
new file mode 100644
index 00000000000..55d87166941
--- /dev/null
+++ b/sbin/iked/eap.c
@@ -0,0 +1,492 @@
+/* $OpenBSD: eap.c,v 1.1 2010/06/03 16:41:12 reyk Exp $ */
+/* $vantronix: eap.c,v 1.12 2010/05/31 11:30:08 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/uio.h>
+
+#include <netinet/in.h>
+#include <netinet/ip_ipsp.h>
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+#include <signal.h>
+#include <errno.h>
+#include <err.h>
+#include <pwd.h>
+#include <event.h>
+
+#include <openssl/sha.h>
+#include <openssl/evp.h>
+
+#include "iked.h"
+#include "ikev2.h"
+#include "eap.h"
+#include "chap_ms.h"
+
+char *eap_identity_response(struct eap_message *);
+int eap_challenge_request(struct iked *env, struct iked_sa *,
+ struct eap_header *);
+int eap_success(struct iked *, struct iked_sa *, struct eap_header *);
+int eap_mschap(struct iked *, struct iked_sa *, struct eap_message *);
+
+ssize_t
+eap_identity_request(struct ibuf *e)
+{
+ struct eap_message *eap;
+
+ if ((eap = ibuf_advance(e, sizeof(*eap))) == NULL)
+ return (-1);
+ eap->eap_code = EAP_CODE_REQUEST;
+ eap->eap_id = 0;
+ eap->eap_length = htobe16(sizeof(*eap));
+ eap->eap_type = EAP_TYPE_IDENTITY;
+
+ return (sizeof(*eap));
+}
+
+char *
+eap_identity_response(struct eap_message *eap)
+{
+ size_t len;
+ char *str;
+ u_int8_t *ptr = (u_int8_t *)eap;
+
+ len = betoh16(eap->eap_length - sizeof(*eap));
+ ptr += sizeof(*eap);
+
+ if (len == 0 || (str = get_string(ptr, len)) == NULL) {
+ log_info("%s: invalid identity response, length %d",
+ __func__, len);
+ return (NULL);
+ }
+ log_debug("%s: identity '%s' length %d", __func__, str, len);
+ return (str);
+}
+
+int
+eap_challenge_request(struct iked *env, struct iked_sa *sa,
+ struct eap_header *hdr)
+{
+ struct eap_message *eap;
+ struct eap_mschap_challenge *ms;
+ ssize_t len = 0;
+ const char *name;
+ int ret = -1;
+ struct ibuf *e;
+
+ if ((e = ibuf_static()) == NULL)
+ return (-1);
+
+ if ((eap = ibuf_advance(e, sizeof(*eap))) == NULL)
+ goto done;
+ eap->eap_code = EAP_CODE_REQUEST;
+ eap->eap_id = hdr->eap_id + 1;
+ eap->eap_type = sa->sa_policy->pol_auth.auth_eap;
+
+ switch (sa->sa_policy->pol_auth.auth_eap) {
+ case EAP_TYPE_MSCHAP_V2:
+ name = IKED_USER; /* XXX should be user-configurable */
+ eap->eap_length = htobe16(sizeof(*eap) +
+ sizeof(*ms) + strlen(name));
+
+ if ((ms = ibuf_advance(e, sizeof(*ms))) == NULL)
+ return (-1);
+ ms->msc_opcode = EAP_MSOPCODE_CHALLENGE;
+ ms->msc_id = eap->eap_id;
+ ms->msc_length = htobe16(sizeof(*ms) + strlen(name));
+ ms->msc_valuesize = sizeof(ms->msc_challenge);
+ arc4random_buf(ms->msc_challenge, sizeof(ms->msc_challenge));
+ if (ibuf_add(e, name, strlen(name)) == -1)
+ goto done;
+ len = betoh16(eap->eap_length);
+
+ /* Store the EAP challenge value */
+ sa->sa_eap.id_type = eap->eap_type;
+ if ((sa->sa_eap.id_buf = ibuf_new(ms->msc_challenge,
+ sizeof(ms->msc_challenge))) == NULL)
+ goto done;
+ break;
+ default:
+ log_debug("%s: unsupported EAP type %s", __func__,
+ print_map(eap->eap_type, eap_type_map));
+ goto done;
+ }
+
+ ret = ikev2_send_ike_e(env, sa, e,
+ IKEV2_PAYLOAD_EAP, IKEV2_EXCHANGE_IKE_AUTH, 1);
+
+ done:
+ ibuf_release(e);
+
+ return (ret);
+}
+
+int
+eap_success(struct iked *env, struct iked_sa *sa, struct eap_header *hdr)
+{
+ struct eap_header *resp;
+ int ret = -1;
+ struct ibuf *e;
+
+ if ((e = ibuf_static()) == NULL)
+ return (-1);
+
+ if ((resp = ibuf_advance(e, sizeof(*resp))) == NULL)
+ goto done;
+ resp->eap_code = EAP_CODE_SUCCESS;
+ resp->eap_id = hdr->eap_id;
+ resp->eap_length = htobe16(sizeof(*resp));
+
+ ret = ikev2_send_ike_e(env, sa, e,
+ IKEV2_PAYLOAD_EAP, IKEV2_EXCHANGE_IKE_AUTH, 1);
+
+ done:
+ ibuf_release(e);
+
+ return (ret);
+}
+
+int
+eap_mschap(struct iked *env, struct iked_sa *sa, struct eap_message *eap)
+{
+ struct iked_user *usr;
+ struct eap_message *resp;
+ struct eap_mschap_response *msr;
+ struct eap_mschap_peer *msp;
+ struct eap_mschap *ms;
+ struct eap_mschap_success *mss;
+ u_int8_t *ptr, *pass;
+ size_t len, passlen;
+ char *name, *msg;
+ u_int8_t ntresponse[EAP_MSCHAP_NTRESPONSE_SZ];
+ u_int8_t successmsg[EAP_MSCHAP_SUCCESS_SZ];
+ struct ibuf *eapmsg = NULL;
+ int ret = -1;
+
+ if (!sa_stateok(sa, IKEV2_STATE_EAP)) {
+ log_debug("%s: unexpected EAP", __func__);
+ return (0); /* ignore */
+ }
+
+ if (sa->sa_hdr.sh_initiator) {
+ log_debug("%s: initiator EAP not supported", __func__);
+ return (-1);
+ }
+
+ /* Only MSCHAP-V2 */
+ if (eap->eap_type != EAP_TYPE_MSCHAP_V2) {
+ log_debug("%s: unsupported type EAP-%s", __func__,
+ print_map(eap->eap_type, eap_type_map));
+ return (-1);
+ }
+
+ if (betoh16(eap->eap_length) < (sizeof(*eap) + sizeof(*ms))) {
+ log_debug("%s: short message", __func__);
+ return (-1);
+ }
+
+ ms = (struct eap_mschap *)(eap + 1);
+ ptr = (u_int8_t *)(eap + 1);
+
+ switch (ms->ms_opcode) {
+ case EAP_MSOPCODE_RESPONSE:
+ msr = (struct eap_mschap_response *)ms;
+ if (betoh16(eap->eap_length) < (sizeof(*eap) + sizeof(*msr))) {
+ log_debug("%s: short response", __func__);
+ return (-1);
+ }
+ ptr += sizeof(*msr);
+ len = betoh16(eap->eap_length) -
+ sizeof(*eap) - sizeof(*msr);
+ if (len == 0 && sa->sa_eapid != NULL)
+ name = strdup(sa->sa_eapid);
+ else
+ name = get_string(ptr, len);
+ if (name == NULL) {
+ log_debug("%s: invalid response name");
+ return (-1);
+ }
+ if ((usr = user_lookup(env, name)) == NULL) {
+ log_debug("%s: unknown user '%s'", __func__, name);
+ free(name);
+ return (-1);
+ }
+ free(name);
+
+ if ((pass = string2unicode(usr->usr_pass, &passlen)) == NULL)
+ return (-1);
+
+ msp = &msr->msr_response.resp_peer;
+ mschap_nt_response(ibuf_data(sa->sa_eap.id_buf),
+ msp->msp_challenge, usr->usr_name, strlen(usr->usr_name),
+ pass, passlen, ntresponse);
+
+ if (memcmp(ntresponse, msp->msp_ntresponse,
+ sizeof(ntresponse)) != 0) {
+ log_debug("%s: '%s' authentication failed", __func__,
+ usr->usr_name);
+ free(pass);
+
+ /* XXX should we send an EAP failure packet? */
+ return (-1);
+ }
+
+ bzero(&successmsg, sizeof(successmsg));
+ mschap_auth_response(pass, passlen,
+ ntresponse, ibuf_data(sa->sa_eap.id_buf), msp->msp_challenge,
+ usr->usr_name, strlen(usr->usr_name), successmsg);
+ if ((sa->sa_eapmsk = ibuf_new(NULL, MSCHAP_MSK_SZ)) == NULL) {
+ log_debug("%s: failed to get MSK", __func__);
+ free(pass);
+ return (-1);
+ }
+ mschap_msk(pass, passlen, ntresponse,
+ ibuf_data(sa->sa_eapmsk));
+ free(pass);
+
+ log_info("%s: '%s' authenticated", __func__, usr->usr_name);
+
+
+ if ((eapmsg = ibuf_static()) == NULL)
+ return (-1);
+
+ msg = " M=Welcome";
+
+ if ((resp = ibuf_advance(eapmsg, sizeof(*resp))) == NULL)
+ goto done;
+ resp->eap_code = EAP_CODE_REQUEST;
+ resp->eap_id = eap->eap_id + 1;
+ resp->eap_length = htobe16(sizeof(*resp) + sizeof(*mss) +
+ sizeof(successmsg) + strlen(msg));
+ resp->eap_type = EAP_TYPE_MSCHAP_V2;
+
+ if ((mss = ibuf_advance(eapmsg, sizeof(*mss))) == NULL)
+ goto done;
+ mss->mss_opcode = EAP_MSOPCODE_SUCCESS;
+ mss->mss_id = msr->msr_id;
+ mss->mss_length = htobe16(sizeof(*mss) +
+ sizeof(successmsg) + strlen(msg));
+ if (ibuf_add(eapmsg, successmsg, sizeof(successmsg)) != 0)
+ goto done;
+ if (ibuf_add(eapmsg, msg, strlen(msg)) != 0)
+ goto done;
+ break;
+ case EAP_MSOPCODE_SUCCESS:
+ if ((eapmsg = ibuf_static()) == NULL)
+ return (-1);
+ if ((resp = ibuf_advance(eapmsg, sizeof(*resp))) == NULL)
+ goto done;
+ resp->eap_code = EAP_CODE_RESPONSE;
+ resp->eap_id = eap->eap_id;
+ resp->eap_length = htobe16(sizeof(*resp) + sizeof(*ms));
+ resp->eap_type = EAP_TYPE_MSCHAP_V2;
+ if ((ms = ibuf_advance(eapmsg, sizeof(*ms))) == NULL)
+ goto done;
+ ms->ms_opcode = EAP_MSOPCODE_SUCCESS;
+ break;
+ case EAP_MSOPCODE_FAILURE:
+ case EAP_MSOPCODE_CHANGE_PASSWORD:
+ case EAP_MSOPCODE_CHALLENGE:
+ default:
+ log_debug("%s: EAP-%s unsupported "
+ "responder operation %s", __func__,
+ print_map(eap->eap_type, eap_type_map),
+ print_map(ms->ms_opcode, eap_msopcode_map));
+ return (-1);
+ }
+
+ if (eapmsg != NULL)
+ ret = ikev2_send_ike_e(env, sa, eapmsg,
+ IKEV2_PAYLOAD_EAP, IKEV2_EXCHANGE_IKE_AUTH, 1);
+
+ if (ret == 0)
+ sa_state(env, sa, IKEV2_STATE_AUTH_SUCCESS);
+
+ done:
+ ibuf_release(eapmsg);
+ return (ret);
+}
+
+int
+eap_parse(struct iked *env, struct iked_sa *sa, void *data, int response)
+{
+ struct eap_header *hdr = data;
+ struct eap_message *eap = data;
+ size_t len;
+ u_int8_t *ptr;
+ struct eap_mschap *ms;
+ struct eap_mschap_challenge *msc;
+ struct eap_mschap_response *msr;
+ struct eap_mschap_success *mss;
+ struct eap_mschap_failure *msf;
+ char *str;
+
+ /* length is already verified by the caller */
+ len = betoh16(hdr->eap_length);
+ ptr = (u_int8_t *)(eap + 1);
+
+ switch (hdr->eap_code) {
+ case EAP_CODE_REQUEST:
+ case EAP_CODE_RESPONSE:
+ if (len < sizeof(*eap)) {
+ log_debug("%s: short message", __func__);
+ return (-1);
+ }
+ break;
+ case EAP_CODE_SUCCESS:
+ return (0);
+ case EAP_CODE_FAILURE:
+ if (response)
+ return (0);
+ return (-1);
+ default:
+ log_debug("%s: unsupported EAP code %s", __func__,
+ print_map(hdr->eap_code, eap_code_map));
+ return (-1);
+ }
+
+ switch (eap->eap_type) {
+ case EAP_TYPE_IDENTITY:
+ if (eap->eap_code == EAP_CODE_REQUEST)
+ break;
+ if ((str = eap_identity_response(eap)) == NULL)
+ return (-1);
+ if (response)
+ break;
+ if (sa->sa_eapid != NULL) {
+ log_debug("%s: EAP identity already known", __func__);
+ return (0);
+ }
+ sa->sa_eapid = str;
+ return (eap_challenge_request(env, sa, hdr));
+ case EAP_TYPE_MSCHAP_V2:
+ ms = (struct eap_mschap *)ptr;
+ switch (ms->ms_opcode) {
+ case EAP_MSOPCODE_CHALLENGE:
+ msc = (struct eap_mschap_challenge *)ptr;
+ ptr += sizeof(*msc);
+ len = betoh16(eap->eap_length) -
+ sizeof(*eap) - sizeof(*msc);
+ if ((str = get_string(ptr, len)) == NULL) {
+ log_debug("%s: invalid challenge name");
+ return (-1);
+ }
+ log_info("%s: %s %s id %d "
+ "length %d valuesize %d name '%s' length %d",
+ __func__,
+ print_map(eap->eap_type, eap_type_map),
+ print_map(ms->ms_opcode, eap_msopcode_map),
+ msc->msc_id, betoh16(msc->msc_length),
+ msc->msc_valuesize, str, len);
+ free(str);
+ print_hex(msc->msc_challenge, 0,
+ sizeof(msc->msc_challenge));
+ break;
+ case EAP_MSOPCODE_RESPONSE:
+ msr = (struct eap_mschap_response *)ptr;
+ ptr += sizeof(*msr);
+ len = betoh16(eap->eap_length) -
+ sizeof(*eap) - sizeof(*msr);
+ if ((str = get_string(ptr, len)) == NULL) {
+ log_debug("%s: invalid response name");
+ return (-1);
+ }
+ log_info("%s: %s %s id %d "
+ "length %d valuesize %d name '%s' name-length %d",
+ __func__,
+ print_map(eap->eap_type, eap_type_map),
+ print_map(ms->ms_opcode, eap_msopcode_map),
+ msr->msr_id, betoh16(msr->msr_length),
+ msr->msr_valuesize, str, len);
+ free(str);
+ print_hex(msr->msr_response.resp_data, 0,
+ sizeof(msr->msr_response.resp_data));
+ break;
+ case EAP_MSOPCODE_SUCCESS:
+ if (eap->eap_code == EAP_CODE_REQUEST) {
+ mss = (struct eap_mschap_success *)ptr;
+ ptr += sizeof(*mss);
+ len = betoh16(eap->eap_length) -
+ sizeof(*eap) - sizeof(*mss);
+ if ((str = get_string(ptr, len)) == NULL) {
+ log_debug("%s: invalid response name");
+ return (-1);
+ }
+ log_info("%s: %s %s request id %d "
+ "length %d message '%s' message-len %d",
+ __func__,
+ print_map(eap->eap_type, eap_type_map),
+ print_map(ms->ms_opcode, eap_msopcode_map),
+ mss->mss_id, betoh16(mss->mss_length),
+ str, len);
+ free(str);
+ } else {
+ ms = (struct eap_mschap *)ptr;
+ log_info("%s: %s %s response", __func__,
+ print_map(eap->eap_type, eap_type_map),
+ print_map(ms->ms_opcode, eap_msopcode_map));
+ if (response)
+ break;
+ if (!sa_stateok(sa, IKEV2_STATE_AUTH_SUCCESS))
+ return (-1);
+
+ return (eap_success(env, sa, hdr));
+ }
+ break;
+ case EAP_MSOPCODE_FAILURE:
+ msf = (struct eap_mschap_failure *)ptr;
+ ptr += sizeof(*msf);
+ len = betoh16(eap->eap_length) -
+ sizeof(*eap) - sizeof(*msf);
+ if ((str = get_string(ptr, len)) == NULL) {
+ log_debug("%s: invalid failure message");
+ return (-1);
+ }
+ log_info("%s: %s %s id %d "
+ "length %d message '%s'", __func__,
+ print_map(eap->eap_type, eap_type_map),
+ print_map(ms->ms_opcode, eap_msopcode_map),
+ msf->msf_id, betoh16(msf->msf_length), str);
+ free(str);
+ break;
+ default:
+ log_info("%s: unknown ms opcode %d", __func__,
+ ms->ms_opcode);
+ return (-1);
+ }
+ if (response)
+ break;
+
+ return (eap_mschap(env, sa, eap));
+ default:
+ log_debug("%s: unsupported EAP type %s", __func__,
+ print_map(eap->eap_type, eap_type_map));
+ return (-1);
+ }
+
+ return (0);
+}
diff --git a/sbin/iked/eap.h b/sbin/iked/eap.h
new file mode 100644
index 00000000000..138eb809e42
--- /dev/null
+++ b/sbin/iked/eap.h
@@ -0,0 +1,172 @@
+/* $OpenBSD: eap.h,v 1.1 2010/06/03 16:41:12 reyk Exp $ */
+/* $vantronix: eap.h,v 1.6 2010/05/18 17:56:16 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IKEV2_EAP_H
+#define _IKEV2_EAP_H
+
+struct eap_header {
+ u_int8_t eap_code;
+ u_int8_t eap_id;
+ u_int16_t eap_length;
+} __packed;
+
+struct eap_message {
+ u_int8_t eap_code;
+ u_int8_t eap_id;
+ u_int16_t eap_length;
+ u_int8_t eap_type;
+ /* Followed by type-specific data */
+} __packed;
+
+#define EAP_CODE_REQUEST 1 /* Request */
+#define EAP_CODE_RESPONSE 2 /* Response */
+#define EAP_CODE_SUCCESS 3 /* Success */
+#define EAP_CODE_FAILURE 4 /* Failure */
+
+extern struct iked_constmap eap_code_map[];
+
+/* http://www.iana.org/assignments/eap-numbers */
+#define EAP_TYPE_NONE 0 /* NONE */
+#define EAP_TYPE_IDENTITY 1 /* RFC3748 */
+#define EAP_TYPE_NOTIFICATION 2 /* RFC3748 */
+#define EAP_TYPE_NAK 3 /* RFC3748 */
+#define EAP_TYPE_MD5 4 /* RFC3748 */
+#define EAP_TYPE_OTP 5 /* RFC3748 */
+#define EAP_TYPE_GTC 6 /* RFC3748 */
+#define EAP_TYPE_RSA 9 /* Whelan */
+#define EAP_TYPE_DSS 10 /* Nace */
+#define EAP_TYPE_KEA 11 /* Nace */
+#define EAP_TYPE_KEA_VALIDATE 12 /* Nace */
+#define EAP_TYPE_TLS 13 /* RFC-simon-emu-rfc2716bis-13.txt */
+#define EAP_TYPE_AXENT 14 /* Rosselli */
+#define EAP_TYPE_SECURID 15 /* Nystrm */
+#define EAP_TYPE_ARCOT 16 /* Jerdonek */
+#define EAP_TYPE_CISCO 17 /* Norman */
+#define EAP_TYPE_SIM 18 /* RFC4186 */
+#define EAP_TYPE_SRP_SHA1 19 /* Carlson */
+#define EAP_TYPE_TTLS 21 /* Funk */
+#define EAP_TYPE_RAS 22 /* Fields */
+#define EAP_TYPE_OAAKA 23 /* RFC4187 */
+#define EAP_TYPE_3COM 24 /* Young */
+#define EAP_TYPE_PEAP 25 /* Palekar */
+#define EAP_TYPE_MSCHAP_V2 26 /* Palekar */
+#define EAP_TYPE_MAKE 27 /* Berrendonner */
+#define EAP_TYPE_CRYPTOCARD 28 /* Webb */
+#define EAP_TYPE_MSCHAP_V2_2 29 /* Potter */
+#define EAP_TYPE_DYNAMID 30 /* Merlin */
+#define EAP_TYPE_ROB 31 /* Ullah */
+#define EAP_TYPE_POTP 32 /* RFC4794 */
+#define EAP_TYPE_MS_TLV 33 /* Palekar */
+#define EAP_TYPE_SENTRINET 34 /* Kelleher */
+#define EAP_TYPE_ACTIONTEC 35 /* Chang */
+#define EAP_TYPE_BIOMETRICS 36 /* Xiong */
+#define EAP_TYPE_AIRFORTRESS 37 /* Hibbard */
+#define EAP_TYPE_HTTP_DIGEST 38 /* Tavakoli */
+#define EAP_TYPE_SECURESUITE 39 /* Clements */
+#define EAP_TYPE_DEVICECONNECT 40 /* Pitard */
+#define EAP_TYPE_SPEKE 41 /* Zick */
+#define EAP_TYPE_MOBAC 42 /* Rixom */
+#define EAP_TYPE_FAST 43 /* Cam-Winget */
+#define EAP_TYPE_ZLX 44 /* Bogue */
+#define EAP_TYPE_LINK 45 /* Zick */
+#define EAP_TYPE_PAX 46 /* Clancy */
+#define EAP_TYPE_PSK 47 /* RFC-bersani-eap-psk-11.txt */
+#define EAP_TYPE_SAKE 48 /* RFC-vanderveen-eap-sake-02.txt */
+#define EAP_TYPE_IKEV2 49 /* RFC5106 */
+#define EAP_TYPE_AKA2 50 /* RFC5448 */
+#define EAP_TYPE_GPSK 51 /* RFC5106 */
+#define EAP_TYPE_PWD 52 /* RFC-harkins-emu-eap-pwd-12.txt */
+#define EAP_TYPE_EXPANDED_TYPE 254 /* RFC3748 */
+#define EAP_TYPE_EXPERIMENTAL 255 /* RFC3748 */
+
+extern struct iked_constmap eap_type_map[];
+
+/*
+ * EAP MSCHAP-V2
+ */
+
+#define EAP_MSCHAP_CHALLENGE_SZ 16
+#define EAP_MSCHAP_RESPONSE_SZ 49
+#define EAP_MSCHAP_NTRESPONSE_SZ 24
+#define EAP_MSCHAP_SUCCESS_SZ 42
+
+#define EAP_MSOPCODE_CHALLENGE 1 /* Challenge */
+#define EAP_MSOPCODE_RESPONSE 2 /* Response */
+#define EAP_MSOPCODE_SUCCESS 3 /* Success */
+#define EAP_MSOPCODE_FAILURE 4 /* Failure */
+#define EAP_MSOPCODE_CHANGE_PASSWORD 7 /* Change Password */
+
+extern struct iked_constmap eap_msopcode_map[];
+
+struct eap_mschap {
+ u_int8_t ms_opcode;
+} __packed;
+
+struct eap_mschap_challenge {
+ u_int8_t msc_opcode;
+ u_int8_t msc_id;
+ u_int16_t msc_length;
+ u_int8_t msc_valuesize;
+ u_int8_t msc_challenge[EAP_MSCHAP_CHALLENGE_SZ];
+ /* Followed by variable-size name field */
+} __packed;
+
+struct eap_mschap_peer {
+ u_int8_t msp_challenge[EAP_MSCHAP_CHALLENGE_SZ];
+ u_int8_t msp_reserved[8];
+ u_int8_t msp_ntresponse[EAP_MSCHAP_NTRESPONSE_SZ];
+ u_int8_t msp_flags;
+};
+
+struct eap_mschap_response {
+ u_int8_t msr_opcode;
+ u_int8_t msr_id;
+ u_int16_t msr_length;
+ u_int8_t msr_valuesize;
+ union {
+ u_int8_t resp_data[EAP_MSCHAP_RESPONSE_SZ];
+ struct eap_mschap_peer resp_peer;
+ } msr_response;
+ /* Followed by variable-size name field */
+} __packed;
+
+struct eap_mschap_success {
+ u_int8_t mss_opcode;
+ u_int8_t mss_id;
+ u_int16_t mss_length;
+ /* Followed by variable-size success message */
+} __packed;
+
+struct eap_mschap_failure {
+ u_int8_t msf_opcode;
+ u_int8_t msf_id;
+ u_int16_t msf_length;
+ /* Followed by variable-size message field */
+} __packed;
+
+#define EAP_MSERROR_RESTRICTED_LOGON_HOURS 646 /* eap-mschapv2 */
+#define EAP_MSERROR_ACCT_DISABLED 647 /* eap-mschapv2 */
+#define EAP_MSERROR_PASSWD_EXPIRED 648 /* eap-mschapv2 */
+#define EAP_MSERROR_NO_DIALIN_PERMISSION 649 /* eap-mschapv2 */
+#define EAP_MSERROR_AUTHENTICATION_FAILURE 691 /* eap-mschapv2 */
+#define EAP_MSERROR_CHANGING_PASSWORD 709 /* eap-mschapv2 */
+
+extern struct iked_constmap eap_mserror_map[];
+
+#endif /* _IKEV2_EAP_H */
diff --git a/sbin/iked/genmap.sh b/sbin/iked/genmap.sh
new file mode 100644
index 00000000000..4b512bb4543
--- /dev/null
+++ b/sbin/iked/genmap.sh
@@ -0,0 +1,75 @@
+#!/bin/sh
+# $OpenBSD: genmap.sh,v 1.1 2010/06/03 16:41:12 reyk Exp $
+
+# Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+TOK=$(echo ${2} | tr "[:lower:]" "[:upper:]")
+tok=$(echo ${2} | tr "[:upper:]" "[:lower:]")
+
+MAP=$(grep "struct iked_constmap" $1 |
+ sed -Ee "s/.*${tok}_([^_]+)_map.*/\1/g")
+DFLT=$(grep -E "#define ([^_]+)_DEFAULT_" $1 |
+ sed -Ee "s/.*${TOK}_DEFAULT_([^[:space:]]+).*/\1/g")
+
+cat <<EOF
+/* Automatically generated, do not edit */
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include "types.h"
+#include "${tok}.h"
+
+EOF
+
+for i in $MAP; do
+ lower=$(echo $i | tr "[:upper:]" "[:lower:]")
+ upper=$(echo $i | tr "[:lower:]" "[:upper:]")
+
+ echo "struct iked_constmap ${tok}_${lower}_map[] = {"
+
+ X="${TOK}_${upper}"
+ grep "$X" $1 | grep -v '\\' | sed -Ee \
+ "s/#define.*${X}_([^[:blank:]]+).*\/\* (.+) \*\/$\
+/ { ${X}_\1, \"\1\", \"\2\" },/"
+
+ echo " { 0 }"
+ echo "};"
+done
+
+for i in $DFLT; do
+ lower=$(echo $i | tr "[:upper:]" "[:lower:]")
+ upper=$(echo $i | tr "[:lower:]" "[:upper:]")
+ type=$(echo $lower | sed "s/[^_]*_//")
+
+ sed -ne "{
+ /${TOK}_DEFAULT_${i}/ {
+ /^$/ { H; d; q; };
+ /[^\\\\]$/ { H; d; q; };
+ };
+ /${TOK}_DEFAULT_${i}/,/[^\\\\]$/{ H; d; };
+ };
+ $ {
+ g;
+ s/#define ${TOK}_DEFAULT_${upper}/\
+struct iked_${type} ${tok}_default_${lower}s[] =/;
+ s/\\\\//g;
+ s/}$/{ 0 } };/;
+ p;
+ };" $1
+
+ echo "size_t ${tok}_default_n${lower}s = \
+ ((sizeof(${tok}_default_${lower}s) / \
+ sizeof(${tok}_default_${lower}s[0])) - 1);"
+done
diff --git a/sbin/iked/iked.8 b/sbin/iked/iked.8
new file mode 100644
index 00000000000..4d964a12812
--- /dev/null
+++ b/sbin/iked/iked.8
@@ -0,0 +1,129 @@
+.\" $OpenBSD: iked.8,v 1.1 2010/06/03 16:41:12 reyk Exp $
+.\" $vantronix: iked.8,v 1.5 2010/06/02 14:38:08 reyk Exp $
+.\"
+.\" Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: June 3 2010 $
+.Dt IKED 8
+.Os
+.Sh NAME
+.Nm iked
+.Nd Internet Key Exchange version 2 (IKEv2) daemon
+.Sh SYNOPSIS
+.Nm iked
+.Op Fl dnvT
+.Oo
+.Fl D Ar macro Ns = Ns Ar value
+.Oc
+.Op Fl f Ar file
+.Sh DESCRIPTION
+.Nm
+is an Internet Key Exchange (IKEv2) daemon which performs mutual
+authentication and is establishing and maintaining IPsec flows and
+security associations (SAs) between the two peers.
+.Pp
+The IKEv2 protocol is defined in RFC 4306 which combines and updates
+the previous standards ISAKMP/Oakley (RFC 2408), IKE (RFC 2409), and
+the Internet DOI (RFC 2407).
+.Nm
+only supports the IKEv2 protocol;
+have a look at
+.Xr isakmpd 8
+for ISAKMP/Oakley or IKEv1 support.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl D Ar macro Ns = Ns Ar value
+Define
+.Ar macro
+to be set to
+.Ar value
+on the command line.
+Overrides the definition of
+.Ar macro
+in the configuration file.
+.It Fl d
+Do not daemonize and log to
+.Em stderr .
+.It Fl f Ar file
+Use
+.Ar file
+as the configuration file, instead of the default
+.Pa /etc/iked.conf .
+.It Fl n
+Configtest mode.
+Only check the configuration file for validity.
+.It Fl v
+Produce more verbose output.
+.It Fl T
+Disable NAT-Traversal and do not propose NAT-Traversal support to the peers.
+.El
+.Sh FILES
+.Bl -tag -width "/etc/isakmpd/private/" -compact
+.It /etc/iked.conf
+The default
+.Nm
+configuration file.
+.It /etc/isakmpd/ca/
+The directory where CA certificates are kept.
+.It /etc/isakmpd/certs/
+The directory where IKE certificates are kept, both the local
+certificate(s) and those of the peers, if a choice to have them kept
+permanently has been made.
+.It /etc/isakmpd/crls/
+The directory where CRLs are kept.
+.It /etc/isakmpd/private/
+The directory where local private keys used for public key authentication
+are kept.
+The file
+.Pa local.key
+is used to store the local private key.
+.It /etc/isakmpd/pubkeys/
+The directory in which trusted public keys are kept.
+The keys must be named in the fashion described above.
+.It Pa /var/run/iked.sock
+The default
+.Nm
+control socket.
+.El
+.Sh SEE ALSO
+.Xr iked.conf 5 ,
+.Xr ikectl 8 ,
+.Xr isakmpd 8
+.Rs
+.%R RFC 4306
+.%T Internet Key Exchange (IKEv2) Protocol
+.%D December 2005
+.Re
+.Sh HISTORY
+The
+.Nm
+program first appeared in
+.Ox 4.8 .
+.Sh AUTHORS
+The
+.Nm
+program was written by
+.An Reyk Floeter Aq reyk@vantronix.net .
+.Sh CAVEATS
+.Nm
+does not provide backwards compatibility for the ISAKMP/IKEv1 protocol.
+Please use
+.Xr isakmpd 8
+instead.
+.Pp
+.Nm
+is not yet finished and misses some important security features.
+Please don't use it in production networks yet.
diff --git a/sbin/iked/iked.c b/sbin/iked/iked.c
new file mode 100644
index 00000000000..168ff2be3c3
--- /dev/null
+++ b/sbin/iked/iked.c
@@ -0,0 +1,367 @@
+/* $OpenBSD: iked.c,v 1.1 2010/06/03 16:41:12 reyk Exp $ */
+/* $vantronix: iked.c,v 1.22 2010/06/02 14:43:30 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+#include <signal.h>
+#include <errno.h>
+#include <err.h>
+#include <pwd.h>
+#include <event.h>
+
+#include "iked.h"
+#include "ikev2.h"
+
+__dead void usage(void);
+
+void parent_shutdown(struct iked *);
+void parent_sig_handler(int, short, void *);
+int parent_dispatch_ikev1(int, struct iked_proc *, struct imsg *);
+int parent_dispatch_ikev2(int, struct iked_proc *, struct imsg *);
+int parent_dispatch_ca(int, struct iked_proc *, struct imsg *);
+int parent_configure(struct iked *);
+
+static struct iked_proc procs[] = {
+ { "ikev1", PROC_IKEV1, parent_dispatch_ikev1, ikev1 },
+ { "ikev2", PROC_IKEV2, parent_dispatch_ikev2, ikev2 },
+ { "ca", PROC_CERT, parent_dispatch_ca, caproc, IKED_CA }
+};
+
+__dead void
+usage(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "usage: %s [-dnvT] [-D macro=value] "
+ "[-f file]\n", __progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+ int debug = 0, verbose = 0;
+ int opts = 0;
+ const char *conffile = IKED_CONFIG;
+ struct iked *env = NULL;
+
+ log_init(1);
+
+ while ((c = getopt(argc, argv, "dD:nf:vT")) != -1) {
+ switch (c) {
+ case 'd':
+ debug++;
+ break;
+ case 'D':
+#if 0
+ if (cmdline_symset(optarg) < 0)
+ log_warnx("could not parse macro definition %s",
+ optarg);
+#endif
+ break;
+ case 'n':
+ debug = 1;
+ opts |= IKED_OPT_NOACTION;
+ break;
+ case 'f':
+ conffile = optarg;
+ break;
+ case 'v':
+ verbose++;
+ opts |= IKED_OPT_VERBOSE;
+ break;
+ case 'T':
+ opts |= IKED_OPT_NONATT;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ argv += optind;
+ argc -= optind;
+
+ if ((env = calloc(1, sizeof(*env))) == NULL)
+ fatal("calloc: env");
+
+ env->sc_opts = opts;
+
+ if (strlcpy(env->sc_conffile, conffile, MAXPATHLEN) >= MAXPATHLEN)
+ errx(1, "config file exceeds MAXPATHLEN");
+
+ ca_sslinit();
+ policy_init(env);
+
+ /* check for root privileges */
+ if (geteuid())
+ errx(1, "need root privileges");
+
+ if ((env->sc_pw = getpwnam(IKED_USER)) == NULL)
+ errx(1, "unknown user %s", IKED_USER);
+
+ /* Configure the control socket */
+ env->sc_csock.cs_name = IKED_SOCKET;
+
+ log_init(debug);
+ log_verbose(verbose);
+
+ if (!debug && daemon(0, 0) == -1)
+ err(1, "failed to daemonize");
+
+ group_init();
+ init_procs(env, procs, nitems(procs));
+
+ setproctitle("parent");
+
+ event_init();
+
+ signal_set(&env->sc_evsigint, SIGINT, parent_sig_handler, env);
+ signal_set(&env->sc_evsigterm, SIGTERM, parent_sig_handler, env);
+ signal_set(&env->sc_evsigchld, SIGCHLD, parent_sig_handler, env);
+ signal_set(&env->sc_evsighup, SIGHUP, parent_sig_handler, env);
+ signal_set(&env->sc_evsigpipe, SIGPIPE, parent_sig_handler, env);
+
+ signal_add(&env->sc_evsigint, NULL);
+ signal_add(&env->sc_evsigterm, NULL);
+ signal_add(&env->sc_evsigchld, NULL);
+ signal_add(&env->sc_evsighup, NULL);
+ signal_add(&env->sc_evsigpipe, NULL);
+
+ config_pipes(env, procs, nitems(procs));
+ config_procs(env, procs, nitems(procs));
+
+ if (parent_configure(env) == -1)
+ fatalx("configuration failed");
+
+ event_dispatch();
+
+ log_debug("%d parent exiting", getpid());
+
+ return (0);
+}
+
+int
+parent_configure(struct iked *env)
+{
+ struct sockaddr_storage ss;
+
+ if (parse_config(env->sc_conffile, env) == -1) {
+ kill_procs(env);
+ exit(1);
+ }
+
+ if (env->sc_opts & IKED_OPT_NOACTION) {
+ fprintf(stderr, "configuration OK\n");
+ kill_procs(env);
+ exit(0);
+ }
+
+ env->sc_pfkey = -1;
+ config_setpfkey(env, PROC_IKEV2);
+
+ bzero(&ss, sizeof(ss));
+ ss.ss_family = AF_INET;
+
+ config_setsocket(env, &ss, ntohs(IKED_IKE_PORT), PROC_IKEV2);
+ config_setsocket(env, &ss, ntohs(IKED_NATT_PORT), PROC_IKEV2);
+
+ bzero(&ss, sizeof(ss));
+ ss.ss_family = AF_INET6;
+
+ config_setsocket(env, &ss, ntohs(IKED_IKE_PORT), PROC_IKEV2);
+ config_setsocket(env, &ss, ntohs(IKED_NATT_PORT), PROC_IKEV2);
+
+ return (0);
+}
+
+void
+parent_reload(struct iked *env, int reset, const char *filename)
+{
+ /* Switch back to the default config file */
+ if (filename == NULL || *filename == '\0')
+ filename = env->sc_conffile;
+
+ log_debug("%s: level %d config file %s", __func__, reset, filename);
+
+ if (reset == RESET_RELOAD) {
+ config_setreset(env, RESET_POLICY, PROC_IKEV1);
+ config_setreset(env, RESET_POLICY, PROC_IKEV2);
+ config_setreset(env, RESET_CA, PROC_CERT);
+
+ if (parse_config(filename, env) == -1) {
+ log_debug("%s: failed to load config file %s",
+ __func__, filename);
+ }
+ } else {
+ config_setreset(env, reset, PROC_IKEV1);
+ config_setreset(env, reset, PROC_IKEV2);
+ config_setreset(env, reset, PROC_CERT);
+ }
+}
+
+void
+parent_sig_handler(int sig, short event, void *p)
+{
+ struct iked *env = p;
+ int die = 0, status, fail, id;
+ pid_t pid;
+ char *cause;
+
+ switch (sig) {
+ case SIGHUP:
+ log_info("%s: reload requested with SIGHUP", __func__);
+
+ /*
+ * This is safe because libevent uses async signal handlers
+ * that run in the event loop and not in signal context.
+ */
+ parent_reload(env, 0, NULL);
+ break;
+ case SIGPIPE:
+ log_info("%s: ignoring SIGPIPE", __func__);
+ break;
+ case SIGTERM:
+ case SIGINT:
+ die = 1;
+ /* FALLTHROUGH */
+ case SIGCHLD:
+ do {
+ pid = waitpid(-1, &status, WNOHANG);
+ if (pid <= 0)
+ continue;
+
+ fail = 0;
+ if (WIFSIGNALED(status)) {
+ fail = 1;
+ asprintf(&cause, "terminated; signal %d",
+ WTERMSIG(status));
+ } else if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) != 0) {
+ fail = 1;
+ asprintf(&cause, "exited abnormally");
+ } else
+ asprintf(&cause, "exited okay");
+ } else
+ fatalx("unexpected cause of SIGCHLD");
+
+ die = 1;
+
+ for (id = 0; id < PROC_MAX; id++)
+ if (pid == env->sc_pid[id]) {
+ log_warnx("lost child: %s %s",
+ env->sc_title[id], cause);
+ break;
+ }
+
+ free(cause);
+ } while (pid > 0 || (pid == -1 && errno == EINTR));
+
+ if (die)
+ parent_shutdown(env);
+ break;
+ default:
+ fatalx("unexpected signal");
+ }
+}
+
+int
+parent_dispatch_ikev1(int fd, struct iked_proc *p, struct imsg *imsg)
+{
+ switch (imsg->hdr.type) {
+ default:
+ break;
+ }
+
+ return (-1);
+}
+
+int
+parent_dispatch_ikev2(int fd, struct iked_proc *p, struct imsg *imsg)
+{
+ switch (imsg->hdr.type) {
+ default:
+ break;
+ }
+
+ return (-1);
+}
+
+int
+parent_dispatch_ca(int fd, struct iked_proc *p, struct imsg *imsg)
+{
+ struct iked *env = p->env;
+ int v;
+ char *str = NULL;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_RESET:
+ IMSG_SIZE_CHECK(imsg, &v);
+ memcpy(&v, imsg->data, sizeof(v));
+ parent_reload(env, v, NULL);
+ return (0);
+ case IMSG_CTL_RELOAD:
+ if (IMSG_DATA_SIZE(imsg) > 0)
+ str = get_string(imsg->data, IMSG_DATA_SIZE(imsg));
+ parent_reload(env, 0, str);
+ if (str != NULL)
+ free(str);
+ return (0);
+ default:
+ break;
+ }
+
+ return (-1);
+}
+
+void
+parent_shutdown(struct iked *env)
+{
+ pid_t pid;
+ u_int i;
+
+ for (i = 0; i < PROC_MAX; i++)
+ kill(env->sc_pid[i], SIGTERM);
+
+ do {
+ pid = waitpid(WAIT_MYPGRP, NULL, 0);
+ } while (pid != -1 || (pid == -1 && errno == EINTR));
+
+ free(env);
+
+ log_warnx("parent terminating");
+ exit(0);
+}
diff --git a/sbin/iked/iked.conf.5 b/sbin/iked/iked.conf.5
new file mode 100644
index 00000000000..a4915f89680
--- /dev/null
+++ b/sbin/iked/iked.conf.5
@@ -0,0 +1,718 @@
+.\" $OpenBSD: iked.conf.5,v 1.1 2010/06/03 16:41:12 reyk Exp $
+.\" $vantronix: iked.conf.5,v 1.10 2010/06/03 16:13:40 reyk Exp $
+.\"
+.\" Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
+.\" Copyright (c) 2004 Mathieu Sauve-Frankel All rights reserved.
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: June 3 2010 $
+.Dt IKED.CONF 5
+.Os
+.Sh NAME
+.Nm iked.conf
+.Nd IKEv2 configuration file
+.Sh DESCRIPTION
+.Nm
+is the configuration file for
+.Xr iked 8 ,
+the Internet Key Exchange version 2 (IKEv2) daemon for IPsec.
+IPsec itself is a pair of protocols:
+Encapsulating Security Payload (ESP),
+which provides integrity and confidentiality;
+and Authentication Header (AH),
+which provides integrity.
+The IPsec protocol itself is described in
+.Xr ipsec 4 .
+.Pp
+In its most basic form, a flow is established between hosts and/or
+networks, and then Security Associations (SA) are established,
+which detail how the desired protection will be achieved.
+IPsec uses flows to determine whether to apply security services to an
+IP packet or not.
+.Xr iked 8
+is used to set up flows and establish SAs automatically,
+by specifying
+.Sq ikev2
+policies in
+.Nm
+(see
+.Sx AUTOMATIC KEYING POLICIES,
+below).
+.Pp
+Alternative methods of setting up flows and SAs are also possible
+using manual keying or automatic keying using the older ISAKMP/Oakley
+a.k.a. IKEv1 protocol.
+Manual keying is not recommended, but can be convenient for quick
+setups and testing.
+Those procedures are not documented within this page,
+see
+.Xr ipsec.conf 5
+and
+.Xr isakmpd 8
+for more information about manual keying and ISAKMP support.
+.Sh IKED.CONF FILE FORMAT
+.Nm
+is divided into three main sections:
+.Bl -tag -width xxxx
+.It Sy Macros
+User-defined variables may be defined and used later, simplifying the
+configuration file.
+.It Sy Global Configuration
+Global settings for
+.Xr iked 8 .
+.It Sy Automatic Keying Policies
+Policies to set up IPsec flows and SAs automatically.
+.El
+.Pp
+Lines beginning with
+.Sq #
+and empty lines are regarded as comments,
+and ignored.
+Lines may be split using the
+.Sq \e
+character.
+.Pp
+Addresses can be specified in CIDR notation (matching netblocks),
+as symbolic host names, interface names, or interface group names.
+.Pp
+Additional configuration files can be included with the
+.Ic include
+keyword, for example:
+.Bd -literal -offset indent
+include "/etc/macros.conf"
+.Ed
+.Sh MACROS
+Macros can be defined that will later be expanded in context.
+Macro names must start with a letter, and may contain letters, digits
+and underscores.
+Macro names may not be reserved words (for example
+.Ic flow ,
+.Ic from ,
+.Ic esp ) .
+Macros are not expanded inside quotes.
+.Pp
+For example:
+.Bd -literal -offset indent
+remote_gw = "192.168.3.12"
+ikev2 esp from 192.168.7.0/24 to 192.168.8.0/24 peer $remote_gw
+.Ed
+.Sh GLOBAL CONFIGURATION
+Here are the settings that can be set globally:
+The commands are as follows:
+.Bl -tag -width xxxx
+.It Ic user Ar name Ar password
+.Xr iked 8
+supports user-based authentication by tunneling the Extensible
+Authentication Protocol (EAP) over IKEv2.
+In its most basic form, the users will be authenticated against a
+local, integrated password database that is configured with the
+.Ic user
+lines in
+.Nm
+and the
+.Ar username
+and
+.Ar password
+arguments.
+Note that the password has to be specified in plain text which is
+required to support different challenge-based EAP methods like
+EAP-MD5 or EAP-MSCHAPv2.
+.El
+.Sh AUTOMATIC KEYING POLICIES
+This section is used to configure policies that will be used by
+.Xr iked 8
+to set up flows and SAs automatically.
+Some examples of setting up automatic keying:
+.Bd -literal -offset 3n
+# Set up a VPN:
+# First between the gateway machines 192.168.3.1 and 192.168.3.2
+# Second between the networks 10.1.1.0/24 and 10.1.2.0/24
+ikev2 esp from 192.168.3.1 to 192.168.3.2
+ikev2 esp from 10.1.1.0/24 to 10.1.2.0/24 peer 192.168.3.2
+.Ed
+.Pp
+The commands are as follows:
+.Bl -tag -width xxxx
+.It Xo
+.Ic ikev2
+.Op Ar name
+.Op Ar mode
+.Op Ar encap
+.\" .Op Ar tmode
+.Xc
+.Ar name
+is an optional arbitrary string identifying the policy.
+The name should only occur once in
+.Nm
+or any incluced files.
+If omitted,
+a name will be generated automatically for the policy.
+.Pp
+.Ar mode
+specifies the IKE mode to use:
+one of
+.Ar passive
+or
+.Ar active .
+When
+.Ar passive
+is specified,
+.Xr iked 8
+will not immediately start negotiation of this tunnel, but wait for an incoming
+request from the remote peer.
+When
+.Ar active
+is specified, negotiation will be started at once.
+If omitted,
+.Ar passive
+mode will be used.
+.Pp
+.Ar encap
+specifies the encapsulation protocol to be used.
+Possible protocols are
+.Ar esp
+and
+.Ar ah ;
+the default is
+.Ar esp .
+.\" .Pp
+.\" .Ar tmode
+.\" describes the encapsulation mode to be used.
+.\" Possible modes are
+.\" .Ar tunnel
+.\" and
+.\" .Ar transport ;
+.\" the default is
+.\" .Ar tunnel .
+.It Ic proto Ar protocol
+The optional
+.Ic proto
+parameter restricts the flow to a specific IP protocol.
+Common protocols are
+.Xr icmp 4 ,
+.Xr tcp 4 ,
+and
+.Xr udp 4 .
+For a list of all the protocol name to number mappings used by
+.Xr iked 8 ,
+see the file
+.Pa /etc/protocols .
+.It Xo
+.Ic from Ar src
+.Op Ic port Ar sport
+.Oo ( Ar srcnat ) Oc
+.Ic to Ar dst
+.Op Ic port Ar dport
+.Xc
+Specify one or more traffic selectors for this policy which will be
+used to negotiate the IPsec flows between the IKEv2 peers.
+During the negotiation, the peers may decide to narrow a flow to a
+subset of the configured traffic selector networks to match the
+policies on each side.
+.Pp
+Each traffic selector will apply for packets with source address
+.Ar src
+and destination address
+.Ar dst .
+The keyword
+.Ar any
+will match any address (i.e. 0.0.0.0/0).
+If the
+.Ar src
+argument specifies a fictional source ID,
+the
+.Ar srcnat
+parameter can be used to specify the actual source address.
+This can be used in outgoing NAT/BINAT scenarios as described below.
+Host addresses are parsed as type
+.Dq IPV4_ADDR ;
+adding the suffix /32 will change the type to
+.Dq IPV4_ADDR_SUBNET ,
+which can improve interoperability with some IKE implementations.
+.Pp
+The optional
+.Ic port
+modifiers restrict the traffic selectors to the specified ports.
+They are only valid in conjunction with the
+.Xr tcp 4
+and
+.Xr udp 4
+protocols.
+Ports can be specified by number or by name.
+For a list of all port name to number mappings used by
+.Xr ipsecctl 8 ,
+see the file
+.Pa /etc/services .
+.It Ic local Ar localip Ic peer Ar remote
+The
+.Ic local
+parameter specifies the address or FQDN of the local endpoint.
+Unless we are multi-homed or have aliases,
+this option is generally not needed.
+.Pp
+The
+.Ic peer
+parameter specifies the address or FQDN of the remote endpoint.
+For host-to-host connections where
+.Ar dst
+is identical to
+.Ar remote ,
+this option is generally not needed as it will be set to
+.Ar dst
+automatically.
+If it is not specified or if the keyword
+.Ar any
+is given, the default peer is used.
+.It Xo
+.Ic ikesa
+.Ic auth Ar algorithm
+.Ic enc Ar algorithm
+.Ic prf Ar algorithm
+.Ic group Ar group
+.Xc
+These parameters define the mode and cryptographic transforms to be
+used for the IKE SA negotiation, also known as phase 1.
+The IKE SA will be used to authenticate the machines and to set up an
+encrypted channel for the IKEv2 protocol.
+.Pp
+Possible values for
+.Ic auth ,
+.Ic enc ,
+.Ic prf ,
+.Ic group ,
+and the default proposals are described below in
+.Sx CRYPTO TRANSFORMS .
+If omitted,
+.Xr iked 8
+will use the default proposals for the IKEv2 protocol.
+.Pp
+.It Xo
+.Ic childsa
+.Ic auth Ar algorithm
+.Ic enc Ar algorithm
+.Ic group Ar group
+.Xc
+These parameters define the cryptographic transforms to be used for
+the Child SA negotiation, also known as phase 2.
+Each Child SA will be used to negotiate the actual IPsec SAs.
+The initial Child SA is always negotiated with the initial IKEv2 key
+exchange, additional Child SAs may be negotiated with additional
+Child SA key exchanges for an established IKE SA.
+.Pp
+Possible values for
+.Ic auth ,
+.Ic enc ,
+.Ic group ,
+and the default proposals are described below in
+.Sx CRYPTO TRANSFORMS .
+If omitted,
+.Xr iked 8
+will use the default proposals for the ESP or AH protocol.
+The
+.Ic group
+option will only be used to enable Perfect Forwarding Security (PFS)
+for additional Child SAs exchanges that are not part of the initial
+key exchange.
+.It Ic srcid Ar string Ic dstid Ar string
+.Ic srcid
+defines an ID of type
+.Dq FQDN ,
+.Dq DER_ASN1_DN ,
+.Dq IPV4_ADDR ,
+.Dq IPV6_ADDR ,
+or
+.Dq RFC822_ADDR
+that will be used by
+.Xr iked 8
+as the identity of the local peer.
+If the argument is an email address (reyk@example.com),
+.Xr iked 8
+will use RFC822_ADDR as the ID type.
+The DER_ASN1_DN type will be used if the string starts with a slash
+.Sq /
+(/C=DE/../CN=10.0.0.1/emailAddress=reyk@example.com).
+If the argument is an IPv4 address or a compressed IPv6 address,
+the ID types IPV4_ADDR or IPV6_ADDR will be used.
+Anything else is considered to be an FQDN.
+.Pp
+If
+.Ic srcid
+is omitted,
+the default is to use the hostname of the local machine,
+see
+.Xr hostname 1
+to set or print the hostname.
+.Pp
+.Ic dstid
+is similar to
+.Ic srcid ,
+but instead specifies the ID to be used
+by the remote peer.
+.It Op Ar ikeauth
+Specify the mode to mutually authenticate the peers.
+Non-psk modes will require to set up certificates and RSA public keys,
+see
+.Xr iked 8
+for more information.
+.Pp
+.Bl -tag -width $domain -compact -offset indent
+.It Ic eap Ar type
+Use EAP to authenticate the initiator.
+The only supported EAP
+.Ar type
+is currently
+.Ar MSCHAP_V2 .
+The responder will use RSA public key authentication.
+.It Ic psk Ar string
+Use a pre-shared key
+.Ar string
+or hex value (starting with 0x) for authentication.
+.It Ic rsa
+Use RSA public key authentication.
+This is the default mode if no option is specified.
+.El
+.It Ic tag Ar string
+Add a
+.Xr pf 4
+tag to all packets of IPsec SAs created for this connection.
+This will allow matching packets for this connection by defining
+rules in
+.Xr pf.conf 5
+using the
+.Cm tagged
+keyword.
+.Pp
+The following variables can be used in tags to include information
+from the remote peer on runtime:
+.Pp
+.Bl -tag -width $domain -compact -offset indent
+.It Ar $id
+The
+.Ic dstid
+that was proposed by the remote peer to identify itself.
+It will be expanded to
+.Ar id-value ,
+e.g.\&
+.Ar foo.example.com .
+To limit the size of the derived tag,
+.Xr iked 8
+will extract the common name
+.Sq CN=
+from DER_ASN1_DN IDs, for example
+.Ar /C=DE/../CN=10.1.1.1/..
+will be expanded to
+.Ar 10.1.1.1 .
+.It Ar $domain
+Extract the domain from IDs of type FQDN, RFC822_ADDR or DER_ASN1_DN.
+.It Ar $name
+The name of the IKEv2 policy that was configured in
+.Nm
+or automatically generated by
+.Xr iked 8 .
+.El
+.Pp
+For example, if the ID is
+.Ar foo.example.com
+or
+.Ar user@example.com ,
+.Dq ipsec-$domain
+expands to
+.Dq ipsec-example.com .
+The variable expansion for the
+.Ar tag
+directive occurs only at runtime, not during configuration file parse time.
+.El
+.Sh PACKET FILTERING
+IPsec traffic appears unencrypted on the
+.Xr enc 4
+interface
+and can be filtered accordingly using the
+.Ox
+packet filter,
+.Xr pf 4 .
+The grammar for the packet filter is described in
+.Xr pf.conf 5 .
+.Pp
+The following components are relevant to filtering IPsec traffic:
+.Bl -ohang -offset indent
+.It external interface
+Interface for ISAKMP traffic and encapsulated IPsec traffic.
+.It proto udp port 500
+ISAKMP traffic on the external interface.
+.It proto udp port 4500
+ISAKMP NAT-Traversal traffic on the external interface.
+.It proto ah \*(Ba esp
+Encapsulated IPsec traffic
+on the external interface.
+.It enc0
+Interface for outgoing traffic before it's been encapsulated,
+and incoming traffic after it's been decapsulated.
+State on this interface should be interface bound;
+see
+.Xr enc 4
+for further information.
+.It proto ipencap
+[tunnel mode only]
+IP-in-IP traffic flowing between gateways
+on the enc0 interface.
+.It tagged ipsec-example.org
+Match traffic of IPsec SAs using the
+.Ic tag
+keyword.
+.El
+.Pp
+If the filtering rules specify to block everything by default,
+the following rule
+would ensure that IPsec traffic never hits the packet filtering engine,
+and is therefore passed:
+.Bd -literal -offset indent
+set skip on enc0
+.Ed
+.Pp
+In the following example, all traffic is blocked by default.
+IPsec-related traffic from gateways {192.168.3.1, 192.168.3.2} and
+networks {10.0.1.0/24, 10.0.2.0/24} is permitted.
+.Bd -literal -offset indent
+block on ix0
+block on enc0
+
+pass in on ix0 proto udp from 192.168.3.2 to 192.168.3.1 \e
+ port {500, 4500}
+pass out on ix0 proto udp from 192.168.3.1 to 192.168.3.2 \e
+ port {500, 4500}
+
+pass in on ix0 proto esp from 192.168.3.2 to 192.168.3.1
+pass out on ix0 proto esp from 192.168.3.1 to 192.168.3.2
+
+pass in on enc0 proto ipencap from 192.168.3.2 to 192.168.3.1 \e
+ keep state (if-bound)
+pass out on enc0 proto ipencap from 192.168.3.1 to 192.168.3.2 \e
+ keep state (if-bound)
+pass in on enc0 from 10.0.2.0/24 to 10.0.1.0/24 \e
+ keep state (if-bound)
+pass out on enc0 from 10.0.1.0/24 to 10.0.2.0/24 \e
+ keep state (if-bound)
+.Ed
+.Pp
+.Xr pf 4
+has the ability to filter IPsec-related packets
+based on an arbitrary
+.Em tag
+specified within a ruleset.
+The tag is used as an internal marker
+which can be used to identify the packets later on.
+This could be helpful,
+for example,
+in scenarios where users are connecting in from differing IP addresses,
+or to support queue-based bandwidth control,
+since the enc0 interface does not support it.
+.Pp
+The following
+.Xr pf.conf 5
+fragment uses queues for all IPsec traffic with special
+handling for developers and employees:
+.Bd -literal -offset indent
+altq on ix0 cbq bandwidth 1000Mb \e
+ queue { deflt, developers, employees, ipsec }
+ queue deflt bandwidth 10% priority 0 cbq(default ecn)
+ queue developers bandwidth 75% priority 7 cbq(borrow red)
+ queue employees bandwidth 5% cbq(red)
+ queue ipsec bandwidth 10% cbq(red)
+
+pass out on ix0 proto esp queue ipsec
+
+pass out on ix0 tagged ipsec-developers.example.com queue developers
+pass out on ix0 tagged ipsec-employees.example.com queue employees
+.Ed
+.Pp
+The tags will be assigned by the following
+.Nm
+example:
+.Bd -literal -offset indent
+ikev2 esp from 10.1.1.0/24 to 10.1.2.0/24 peer 192.168.3.2 \e
+ tag ipsec-$domain
+.Ed
+.Sh OUTGOING NETWORK ADDRESS TRANSLATION
+In some network topologies it is desirable to perform NAT on traffic leaving
+through the VPN tunnel.
+In order to achieve that,
+the
+.Ar src
+argument is used to negotiate the desired network ID with the peer
+and the
+.Ar srcnat
+parameter defines the true local subnet,
+so that a correct SA can be installed on the local side.
+.Pp
+For example,
+if the local subnet is 192.168.1.0/24 and all the traffic
+for a specific VPN peer should appear as coming from 10.10.10.1,
+the following configuration is used:
+.Bd -literal -offset indent
+ikev2 esp from 10.10.10.1 (192.168.1.0/24) to 192.168.2.0/24 \e
+ peer 10.10.20.1
+.Ed
+.Pp
+Naturally,
+a relevant NAT rule is required in
+.Xr pf.conf 5 .
+For the example above,
+this would be:
+.Bd -literal -offset indent
+match on enc0 from 192.168.1.0/24 to 192.168.2.0/24 nat-to 10.10.10.1
+.Ed
+.Pp
+From the peer's point of view,
+the local end of the VPN tunnel is declared to be 10.10.10.1
+and all the traffic arrives with that source address.
+.Sh CRYPTO TRANSFORMS
+The following authentication types are permitted with the
+.Ic auth
+keyword:
+.Pp
+.Bl -column "authenticationXX" "Key Length" -offset indent -compact
+.It Em Authentication Key Length Truncated Length
+.It Li hmac-md5 Ta "128 bits" Ta "96 bits"
+.It Li hmac-sha1 Ta "160 bits" Ta "96 bits"
+.It Li hmac-sha2-256 Ta "256 bits" Ta "128 bits"
+.It Li hmac-sha2-384 Ta "384 bits" Ta "192 bits"
+.It Li hmac-sha2-512 Ta "512 bits" Ta "256 bits"
+.El
+.Pp
+The following pseudo-random function types are permitted with the
+.Ic prf
+keyword:
+.Pp
+.Bl -column "authenticationXX" "Key Length" -offset indent -compact
+.It Em Authentication Key Length
+.It Li hmac-md5 Ta "128 bits" Ta "[IKE only]"
+.It Li hmac-sha1 Ta "160 bits" Ta "[IKE only]"
+.It Li hmac-sha2-256 Ta "256 bits" Ta "[IKE only]"
+.It Li hmac-sha2-384 Ta "384 bits" Ta "[IKE only]"
+.It Li hmac-sha2-512 Ta "512 bits" Ta "[IKE only]"
+.El
+.Pp
+The following cipher types are permitted with the
+.Ic enc
+keyword:
+.Pp
+.Bl -column "authenticationXX" "Key Length" -offset indent -compact
+.It Em Cipher Key Length
+.It Li des Ta "56 bits" Ta "[ESP only]"
+.It Li 3des Ta "168 bits"
+.It Li aes-128 Ta "128 bits"
+.It Li aes-192 Ta "192 bits"
+.It Li aes-256 Ta "256 bits"
+.It Li aes-ctr Ta "160 bits" Ta "[ESP only]"
+.It Li blowfish Ta "160 bits" Ta "[ESP only]"
+.It Li cast Ta "128 bits" Ta "[ESP only]"
+.It Li null Ta "" Ta "[ESP only]"
+.El
+.Pp
+Use of DES as an encryption algorithm is not recommended (except for
+backwards compatibility) due to the short key length.
+.Pp
+DES requires 8 bytes to form a 56-bit key and 3DES requires 24 bytes
+to form its 168-bit key.
+This is because the most significant bit of each byte is used for parity.
+.Pp
+The keysize of AES-CTR is actually 128-bit.
+However as well as the key, a 32-bit nonce has to be supplied.
+Thus 160 bits of key material have to be supplied.
+.Pp
+Using NULL with ESP will only provide authentication.
+This is useful in setups where AH can not be used, e.g. when NAT is involved.
+.Pp
+The following group types are permitted with the
+.Ic group
+keyword:
+.Pp
+.Bl -column "modpXXXX-XXX" "grpXX" "XXXX" "Alias" -offset indent -compact
+.It Em Name Group Size Type
+.It Li modp768 Ta grp1 Ta 768 Ta "MODP"
+.It Li modp1024 Ta grp2 Ta 1024 Ta "MODP"
+.It Li ec155 Ta grp3 Ta 155 Ta "EC2N [insecure]"
+.It Li ec185 Ta grp4 Ta 185 Ta "EC2N [insecure]"
+.It Li modp1536 Ta grp5 Ta 1536 Ta "MODP"
+.It Li modp2048 Ta grp14 Ta 2048 Ta "MODP"
+.It Li modp3072 Ta grp15 Ta 3072 Ta "MODP"
+.It Li modp4096 Ta grp16 Ta 4096 Ta "MODP"
+.It Li modp6144 Ta grp17 Ta 6144 Ta "MODP"
+.It Li modp8192 Ta grp18 Ta 8192 Ta "MODP"
+.It Li ec256 Ta grp19 Ta 256 Ta "ECP"
+.It Li ec384 Ta grp20 Ta 384 Ta "ECP"
+.It Li ec521 Ta grp21 Ta 521 Ta "ECP"
+.It Li modp1024-160 Ta grp22 Ta 2048 Ta "MODP, 160 bit Prime Order Subgroup"
+.It Li modp2048-224 Ta grp23 Ta 2048 Ta "MODP, 224 bit Prime Order Subgroup"
+.It Li modp2048-256 Ta grp24 Ta 2048 Ta "MODP, 256 bit Prime Order Subgroup"
+.It Li ec192 Ta grp25 Ta 192 Ta "ECP"
+.It Li ec224 Ta grp26 Ta 224 Ta "ECP"
+.El
+.Pp
+The currently supported group types are either
+MODP (exponentiation groups modulo a prime),
+EC2N (elliptic curve groups over GF[2^N]),
+or
+ECP (elliptic curve groups modulo a prime).
+Please note that the EC2N groups are considered as insecure and only
+provided for backwards compatibility.
+.Sh EXAMPLES
+The first example is intended for clients connecting to
+.Xr iked 8
+as an IPsec gateway, or IKEv2 responder, using mutual public key
+authentication and additional challenge-based EAP-MSCHAPv2 password
+authentication:
+.Bd -literal -offset indent
+user "test" "password123"
+
+ikev2 "win7" esp \e
+ from 172.16.2.0/24 to 0.0.0.0/0 \e
+ peer 10.0.0.0/8 local 192.168.56.0/24 \e
+ eap "mschap-v2" \e
+ config address 172.16.2.1 \e
+ tag "$name-$id"
+.Ed
+.Pp
+The next example allows peers to authenticate using a pre-shared key
+.Sq foobar :
+.Bd -literal -offset indent
+ikev2 "big test" \e
+ esp proto tcp \e
+ from 10.0.0.0/8 port 23 to 20.0.0.0/8 port 40 \e
+ from 192.168.1.1 to 192.168.2.2 \e
+ peer any local any \e
+ ikesa enc 3des auth hmac-sha1 group modp1024 \e
+ childsa enc aes-128 auth hmac-sha1 \e
+ srcid host.example.com \e
+ dstid 192.168.0.254 \e
+ psk "foobar"
+.Ed
+.Sh SEE ALSO
+.Xr enc 4 ,
+.Xr ikectl 8 ,
+.Xr iked 8 ,
+.Xr ipsec 4 ,
+.Xr ipsec.conf 5
+.\".Xr ipcomp 4 ,
+.Xr pf.conf 5
+.Sh HISTORY
+The
+.Nm
+file format first appeared in
+.Ox 4.8 .
+.Sh AUTHORS
+The
+.Nm
+program was written by
+.An Reyk Floeter Aq reyk@vantronix.net .
diff --git a/sbin/iked/iked.h b/sbin/iked/iked.h
new file mode 100644
index 00000000000..7ef52708ea0
--- /dev/null
+++ b/sbin/iked/iked.h
@@ -0,0 +1,670 @@
+/* $OpenBSD: iked.h,v 1.1 2010/06/03 16:41:12 reyk Exp $ */
+/* $vantronix: iked.h,v 1.61 2010/06/03 07:57:33 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/tree.h>
+#include <sys/queue.h>
+#include <imsg.h>
+
+#include "types.h"
+#include "dh.h"
+
+#ifndef _IKED_H
+#define _IKED_H
+
+/*
+ * Common daemon infrastructure, local imsg etc.
+ */
+
+struct imsgev {
+ struct imsgbuf ibuf;
+ void (*handler)(int, short, void *);
+ struct event ev;
+ void *data;
+ short events;
+ const char *name;
+};
+
+#define IMSG_SIZE_CHECK(imsg, p) do { \
+ if (IMSG_DATA_SIZE(imsg) < sizeof(*p)) \
+ fatalx("bad length imsg received"); \
+} while (0)
+#define IMSG_DATA_SIZE(imsg) ((imsg)->hdr.len - IMSG_HEADER_SIZE)
+
+/* initially control.h */
+struct control_sock {
+ const char *cs_name;
+ struct event cs_ev;
+ int cs_fd;
+ int cs_restricted;
+ void *cs_env;
+};
+
+struct ctl_conn {
+ TAILQ_ENTRY(ctl_conn) entry;
+ u_int8_t flags;
+#define CTL_CONN_NOTIFY 0x01
+ struct imsgev iev;
+};
+TAILQ_HEAD(ctl_connlist, ctl_conn);
+extern struct ctl_connlist ctl_conns;
+
+enum iked_procid iked_process;
+
+/*
+ * Runtime structures
+ */
+
+struct iked_spi {
+ u_int64_t spi;
+ u_int8_t spi_size;
+ u_int8_t spi_protoid;
+};
+
+struct iked_proposal {
+ u_int8_t prop_id;
+ u_int8_t prop_protoid;
+
+ struct iked_spi prop_localspi;
+ struct iked_spi prop_peerspi;
+
+ struct iked_transform *prop_xforms;
+ u_int prop_nxforms;
+
+ TAILQ_ENTRY(iked_proposal) prop_entry;
+};
+TAILQ_HEAD(iked_proposals, iked_proposal);
+
+struct iked_addr {
+ int addr_af;
+ struct sockaddr_storage addr;
+ u_int8_t addr_mask;
+ int addr_net;
+ in_port_t addr_port;
+};
+
+struct iked_flow {
+ struct iked_addr flow_src;
+ struct iked_addr flow_dst;
+ u_int flow_dir; /* in/out */
+
+ u_int64_t flow_peerspi; /* peer relation */
+ u_int flow_loaded; /* pfkey done */
+ u_int flow_rekey; /* will be deleted */
+
+ u_int8_t flow_saproto;
+ u_int8_t flow_ipproto;
+
+ struct iked_id *flow_srcid;
+ struct iked_id *flow_dstid;
+
+ struct iked_addr *flow_local; /* outer source */
+ struct iked_addr *flow_peer; /* outer dest */
+ struct iked_sa *flow_ikesa; /* parent SA */
+
+ TAILQ_ENTRY(iked_flow) flow_entry;
+};
+TAILQ_HEAD(iked_flows, iked_flow);
+
+struct iked_childsa {
+ u_int8_t csa_saproto; /* IPSec protocol */
+ u_int csa_dir; /* in/out */
+
+ u_int64_t csa_peerspi; /* peer relation */
+ u_int csa_loaded; /* pfkey done */
+ u_int csa_rekey; /* will be deleted */
+
+ struct iked_spi csa_spi;
+
+ struct ibuf *csa_encrkey; /* encryption key */
+ struct iked_transform *csa_encrxf; /* encryption xform */
+
+ struct ibuf *csa_integrkey; /* auth key */
+ struct iked_transform *csa_integrxf; /* auth xform */
+
+ struct iked_id *csa_srcid;
+ struct iked_id *csa_dstid;
+
+ struct iked_addr *csa_local; /* outer source */
+ struct iked_addr *csa_peer; /* outer dest */
+ struct iked_sa *csa_ikesa; /* parent SA */
+
+ TAILQ_ENTRY(iked_childsa) csa_entry;
+};
+TAILQ_HEAD(iked_childsas, iked_childsa);
+
+
+struct iked_static_id {
+ u_int8_t id_type;
+ u_int8_t id_length;
+ u_int8_t id_data[IKED_ID_SIZE];
+};
+
+struct iked_auth {
+ u_int8_t auth_method;
+ u_int8_t auth_eap; /* optional EAP */
+ u_int8_t auth_length; /* zero if EAP */
+ u_int8_t auth_data[IKED_PSK_SIZE];
+};
+
+struct iked_cfg {
+ u_int8_t cfg_action;
+ u_int16_t cfg_type;
+ union {
+ struct iked_addr address;
+ } cfg;
+};
+
+struct iked_policy {
+ u_int pol_id;
+ char pol_name[IKED_ID_SIZE];
+
+ u_int8_t pol_flags;
+#define IKED_POLICY_PASSIVE 0x00
+#define IKED_POLICY_DEFAULT 0x01
+#define IKED_POLICY_ACTIVE 0x02
+#define IKED_POLICY_REFCNT 0x04
+
+ int pol_refcnt;
+
+ u_int8_t pol_saproto;
+ u_int pol_ipproto;
+
+ struct sockaddr_storage pol_peer;
+ u_int8_t pol_peermask;
+ int pol_peernet;
+
+ struct sockaddr_storage pol_local;
+ u_int8_t pol_localmask;
+ int pol_localnet;
+
+ struct iked_static_id pol_peerid;
+ struct iked_static_id pol_localid;
+
+ struct iked_auth pol_auth;
+
+ char pol_tag[IKED_TAG_SIZE];
+
+ struct iked_proposals pol_proposals;
+ size_t pol_nproposals;
+
+ struct iked_flows pol_flows;
+ size_t pol_nflows;
+
+ struct iked_cfg pol_cfg[IKED_CFG_MAX];
+ u_int pol_ncfg;
+
+ RB_ENTRY(iked_policy) pol_entry;
+};
+RB_HEAD(iked_policies, iked_policy);
+
+struct iked_hash {
+ u_int8_t hash_type; /* PRF or INTEGR */
+ u_int16_t hash_id; /* IKE PRF/INTEGR hash id */
+ const void *hash_priv; /* Identifying the hash alg */
+ void *hash_ctx; /* Context of the current invocation */
+ int hash_fixedkey; /* Requires fixed key length */
+ struct ibuf *hash_key; /* MAC key derived from key seed */
+ size_t hash_length; /* Output length */
+ size_t hash_trunc; /* Truncate the output length */
+ struct iked_hash *hash_prf; /* PRF pointer */
+};
+
+struct iked_cipher {
+ u_int8_t encr_type; /* ENCR */
+ u_int16_t encr_id; /* IKE ENCR hash id */
+ const void *encr_priv; /* Identifying the hash alg */
+ void *encr_ctx; /* Context of the current invocation */
+ int encr_fixedkey; /* Requires fixed key length */
+ struct ibuf *encr_key; /* MAC key derived from key seed */
+ struct ibuf *encr_iv; /* Initialization Vector */
+ size_t encr_ivlength; /* IV length */
+ size_t encr_length; /* Block length */
+};
+
+struct iked_dsa {
+ u_int8_t dsa_method; /* AUTH method */
+ const void *dsa_priv; /* PRF or signature hash function */
+ void *dsa_ctx; /* PRF or signature hash ctx */
+ struct ibuf *dsa_keydata; /* public, private or shared key */
+ void *dsa_key; /* parsed public or private key */
+ void *dsa_cert; /* parsed certificate */
+ int dsa_hmac; /* HMAC or public/private key */
+ int dsa_sign; /* Sign or verify operation */
+};
+
+struct iked_id {
+ u_int8_t id_type;
+ struct ibuf *id_buf;
+};
+
+#define IKED_REQ_CERT 0x01 /* get local certificate (if required) */
+#define IKED_REQ_VALID 0x02 /* validate the peer cert */
+#define IKED_REQ_AUTH 0x04 /* AUTH payload */
+#define IKED_REQ_SA 0x08 /* SA available */
+#define IKED_REQ_CHILDSA 0x10 /* Child SA initiated */
+
+#define IKED_REQ_BITS \
+ "\10\01CERT\02VALID\03AUTH\04SA\05CHILDSA"
+
+struct iked_sahdr {
+ u_int64_t sh_ispi; /* Initiator SPI */
+ u_int64_t sh_rspi; /* Responder SPI */
+ u_int sh_initiator; /* Is initiator? */
+} __packed;
+
+struct iked_sa {
+ struct iked_sahdr sa_hdr;
+ u_int32_t sa_msgid;
+
+ struct iked_addr sa_peer;
+ struct iked_addr sa_local;
+ int sa_fd;
+
+ int sa_natt; /* for IKE messages */
+ int sa_udpencap; /* for pfkey */
+
+ int sa_state;
+ u_int sa_stateflags;
+ u_int sa_staterequire;
+
+ int sa_cp; /* XXX */
+
+ struct iked_policy *sa_policy;
+ struct timeval sa_timecreated;
+ struct timeval sa_timeused;
+
+ char *sa_tag;
+
+ struct ibuf *sa_inonce; /* Ni */
+ struct ibuf *sa_rnonce; /* Nr */
+
+ struct group *sa_dhgroup; /* DH group */
+ struct ibuf *sa_dhiexchange;
+ struct ibuf *sa_dhrexchange;
+
+ struct iked_hash *sa_prf; /* PRF alg */
+ struct iked_hash *sa_integr; /* integrity alg */
+ struct iked_cipher *sa_encr; /* encryption alg */
+
+ struct ibuf *sa_key_d; /* SK_d */
+ struct ibuf *sa_key_iauth; /* SK_ai */
+ struct ibuf *sa_key_rauth; /* SK_ar */
+ struct ibuf *sa_key_iencr; /* SK_ei */
+ struct ibuf *sa_key_rencr; /* SK_er */
+ struct ibuf *sa_key_iprf; /* SK_pi */
+ struct ibuf *sa_key_rprf; /* SK_pr */
+
+ struct ibuf *sa_1stmsg; /* for initiator AUTH */
+ struct ibuf *sa_2ndmsg; /* for responder AUTH */
+ struct iked_id sa_localauth; /* local AUTH message */
+
+ struct iked_id sa_iid; /* initiator id */
+ struct iked_id sa_rid; /* responder id */
+ struct iked_id sa_icert; /* initiator cert */
+ struct iked_id sa_rcert; /* responder cert */
+
+ char *sa_eapid; /* EAP identity */
+ struct iked_id sa_eap; /* EAP challenge */
+ struct ibuf *sa_eapmsk; /* EAK session key */
+
+ struct iked_proposals sa_proposals; /* SA proposals */
+ struct iked_childsas sa_childsas; /* IPSec Child SAs */
+ struct iked_flows sa_flows; /* IPSec flows */
+ u_int8_t sa_flowhash[20]; /* SHA1 */
+
+ RB_ENTRY(iked_sa) sa_entry;
+};
+RB_HEAD(iked_sas, iked_sa);
+
+struct iked_message {
+ struct ibuf *msg_data;
+ off_t msg_offset;
+
+ struct sockaddr_storage msg_local;
+ socklen_t msg_locallen;
+
+ struct sockaddr_storage msg_peer;
+ socklen_t msg_peerlen;
+
+ int msg_fd;
+ int msg_response;
+ int msg_natt;
+ struct iked_message *msg_decrypted;
+ int msg_error;
+
+ /* Associated policy and SA */
+ struct iked_policy *msg_policy;
+ struct iked_sa *msg_sa;
+
+ /* Parsed information */
+ struct iked_proposals msg_proposals;
+ struct iked_spi msg_rekey;
+
+ /* Parse stack */
+ struct iked_proposal *msg_prop;
+ u_int16_t msg_attrlength;
+};
+
+struct iked_user {
+ char usr_name[MAXLOGNAME];
+ char usr_pass[IKED_PASSWORD_SIZE];
+ RB_ENTRY(iked_user) usr_entry;
+};
+RB_HEAD(iked_users, iked_user);
+
+/*
+ * Daemon configuration
+ */
+
+struct iked {
+ char sc_conffile[MAXPATHLEN];
+
+ u_int32_t sc_opts;
+
+ int sc_pipes[PROC_MAX][PROC_MAX];
+ struct imsgev sc_ievs[PROC_MAX];
+ const char *sc_title[PROC_MAX];
+ pid_t sc_pid[PROC_MAX];
+ struct passwd *sc_pw;
+
+ struct iked_policies sc_policies;
+ struct iked_policy *sc_defaultcon;
+
+ struct iked_sas sc_sas;
+ struct iked_users sc_users;
+
+ void *sc_priv; /* per-process */
+
+ int sc_pfkey; /* ike process */
+ u_int8_t sc_certreqtype;
+ struct ibuf *sc_certreq;
+
+ struct control_sock sc_csock;
+
+ /* Event and signal handlers */
+ struct event sc_evsigint;
+ struct event sc_evsigterm;
+ struct event sc_evsigchld;
+ struct event sc_evsighup;
+ struct event sc_evsigpipe;
+};
+
+struct iked_proc {
+ const char *title;
+ enum iked_procid id;
+ int (*cb)(int, struct iked_proc *, struct imsg *);
+ pid_t (*init)(struct iked *, struct iked_proc *);
+ const char *chroot;
+ struct iked *env;
+};
+
+struct iked_socket {
+ int sock_fd;
+ struct event sock_ev;
+ struct iked *sock_env;
+ struct sockaddr_storage sock_addr;
+};
+
+/* iked.c */
+void parent_reload(struct iked *, int, const char *);
+
+/* control.c */
+int control_init(struct iked *, struct control_sock *);
+int control_listen(struct control_sock *);
+void control_cleanup(struct control_sock *);
+
+/* config.c */
+struct iked_policy *
+ config_new_policy(struct iked *);
+void config_free_sa(struct iked *, struct iked_sa *);
+struct iked_sa *
+ config_new_sa(struct iked *, int);
+struct iked_user *
+ config_new_user(struct iked *, struct iked_user *);
+u_int64_t
+ config_getspi(void);
+struct iked_transform *
+ config_findtransform(struct iked_proposals *, u_int8_t);
+void config_free_policy(struct iked *, struct iked_policy *);
+struct iked_proposal *
+ config_add_proposal(struct iked_proposals *, u_int, u_int);
+void config_free_proposals(struct iked_proposals *, u_int);
+void config_free_flows(struct iked *, struct iked_flows *,
+ struct iked_spi *);
+void config_free_childsas(struct iked *, struct iked_childsas *,
+ struct iked_spi *, struct iked_spi *);
+struct iked_transform *
+ config_add_transform(struct iked_proposal *,
+ u_int, u_int, u_int, u_int);
+int config_setreset(struct iked *, u_int, enum iked_procid);
+int config_getreset(struct iked *, struct imsg *);
+int config_setpolicy(struct iked *, struct iked_policy *,
+ enum iked_procid);
+int config_getpolicy(struct iked *, struct imsg *);
+int config_setsocket(struct iked *, struct sockaddr_storage *, in_port_t,
+ enum iked_procid);
+int config_getsocket(struct iked *env, struct imsg *,
+ void (*cb)(int, short, void *));
+int config_setpfkey(struct iked *, enum iked_procid);
+int config_getpfkey(struct iked *, struct imsg *);
+int config_setuser(struct iked *, struct iked_user *, enum iked_procid);
+int config_getuser(struct iked *, struct imsg *);
+
+/* policy.c */
+void policy_init(struct iked *);
+int policy_lookup(struct iked *, struct iked_message *);
+void policy_ref(struct iked *, struct iked_policy *);
+void policy_unref(struct iked *, struct iked_policy *);
+void sa_state(struct iked *, struct iked_sa *, int);
+void sa_stateflags(struct iked_sa *, u_int);
+int sa_stateok(struct iked_sa *, int);
+struct iked_sa *
+ sa_new(struct iked *, u_int64_t, u_int64_t, u_int,
+ struct iked_policy *);
+void sa_free(struct iked *, struct iked_sa *);
+void childsa_free(struct iked_childsa *);
+void flow_free(struct iked_flow *);
+struct iked_sa *
+ sa_lookup(struct iked *, u_int64_t, u_int64_t, u_int);
+struct iked_user *
+ user_lookup(struct iked *, const char *);
+RB_PROTOTYPE(iked_policies, iked_policy, pol_entry, policy_cmp);
+RB_PROTOTYPE(iked_sas, iked_sa, sa_entry, sa_cmp);
+RB_PROTOTYPE(iked_users, iked_user, user_entry, user_cmp);
+
+/* crypto.c */
+struct iked_hash *
+ hash_new(u_int8_t, u_int16_t);
+struct ibuf *
+ hash_setkey(struct iked_hash *, void *, size_t);
+void hash_free(struct iked_hash *);
+void hash_init(struct iked_hash *);
+void hash_update(struct iked_hash *, void *, size_t);
+void hash_final(struct iked_hash *, void *, size_t *);
+size_t hash_keylength(struct iked_hash *);
+size_t hash_length(struct iked_hash *);
+
+struct iked_cipher *
+ cipher_new(u_int8_t, u_int16_t, u_int16_t);
+struct ibuf *
+ cipher_setkey(struct iked_cipher *, void *, size_t);
+struct ibuf *
+ cipher_setiv(struct iked_cipher *, void *, size_t);
+void cipher_free(struct iked_cipher *);
+void cipher_init(struct iked_cipher *, int);
+void cipher_init_encrypt(struct iked_cipher *);
+void cipher_init_decrypt(struct iked_cipher *);
+void cipher_update(struct iked_cipher *, void *, size_t, void *, size_t *);
+void cipher_final(struct iked_cipher *, void *, size_t *);
+size_t cipher_length(struct iked_cipher *);
+size_t cipher_keylength(struct iked_cipher *);
+size_t cipher_ivlength(struct iked_cipher *);
+size_t cipher_outlength(struct iked_cipher *, size_t);
+
+struct iked_dsa *
+ dsa_new(u_int16_t, struct iked_hash *, int);
+struct iked_dsa *
+ dsa_sign_new(u_int16_t, struct iked_hash *);
+struct iked_dsa *
+ dsa_verify_new(u_int16_t, struct iked_hash *);
+struct ibuf *
+ dsa_setkey(struct iked_dsa *, void *, size_t, u_int8_t);
+void dsa_free(struct iked_dsa *);
+int dsa_init(struct iked_dsa *);
+size_t dsa_length(struct iked_dsa *);
+int dsa_update(struct iked_dsa *, const void *, size_t);
+ssize_t dsa_sign_final(struct iked_dsa *, void *, size_t);
+ssize_t dsa_verify_final(struct iked_dsa *, void *, size_t);
+
+/* ikev1.c */
+pid_t ikev1(struct iked *, struct iked_proc *);
+
+/* ikev2.c */
+pid_t ikev2(struct iked *, struct iked_proc *);
+int ikev2_send_ike_e(struct iked *, struct iked_sa *, struct ibuf *,
+ u_int8_t, u_int8_t, int);
+int ikev2_message_authsign(struct iked *, struct iked_sa *,
+ struct iked_auth *, struct ibuf *);
+struct ibuf *
+ ikev2_prfplus(struct iked_hash *, struct ibuf *, struct ibuf *,
+ size_t);
+ssize_t ikev2_psk(struct iked_sa *, u_int8_t *, size_t, u_int8_t **);
+
+/* eap.c */
+ssize_t eap_identity_request(struct ibuf *);
+int eap_parse(struct iked *, struct iked_sa *, void *, int);
+
+/* pfkey.c */
+int pfkey_flow_add(int fd, struct iked_flow *);
+int pfkey_flow_delete(int fd, struct iked_flow *);
+int pfkey_sa_init(int, struct iked_childsa *, u_int32_t *);
+int pfkey_sa_add(int, struct iked_childsa *, struct iked_childsa *);
+int pfkey_sa_delete(int, struct iked_childsa *);
+int pfkey_flush(int);
+int pfkey_init(void);
+
+/* ca.c */
+pid_t caproc(struct iked *, struct iked_proc *);
+int ca_setreq(struct iked *, struct iked_sahdr *, u_int8_t,
+ u_int8_t *, size_t, enum iked_procid);
+int ca_setcert(struct iked *, struct iked_sahdr *, struct iked_id *,
+ u_int8_t, u_int8_t *, size_t, enum iked_procid);
+int ca_setauth(struct iked *, struct iked_sa *,
+ struct ibuf *, enum iked_procid);
+void ca_sslinit(void);
+void ca_sslerror(void);
+char *ca_asn1_name(u_int8_t *, size_t);
+char *ca_x509_name(void *);
+
+/* proc.c */
+void init_procs(struct iked *, struct iked_proc *, u_int);
+void kill_procs(struct iked *);
+void init_pipes(struct iked *);
+void config_pipes(struct iked *, struct iked_proc *, u_int);
+void config_procs(struct iked *, struct iked_proc *, u_int);
+void purge_config(struct iked *, u_int8_t);
+void dispatch_proc(int, short event, void *);
+pid_t run_proc(struct iked *, struct iked_proc *, struct iked_proc *,
+ u_int, void (*)(struct iked *, void *), void *);
+
+/* util.c */
+void socket_set_blockmode(int, enum blockmodes);
+void imsg_event_add(struct imsgev *);
+int imsg_compose_event(struct imsgev *, u_int16_t, u_int32_t,
+ pid_t, int, void *, u_int16_t);
+int imsg_composev_event(struct imsgev *, u_int16_t, u_int32_t,
+ pid_t, int, const struct iovec *, int);
+int imsg_compose_proc(struct iked *, enum iked_procid,
+ u_int16_t, int, void *, u_int16_t);
+int imsg_composev_proc(struct iked *, enum iked_procid,
+ u_int16_t, int, const struct iovec *, int);
+int imsg_forward_proc(struct iked *, struct imsg *,
+ enum iked_procid);
+void imsg_flush_proc(struct iked *, enum iked_procid);
+int socket_af(struct sockaddr *, in_port_t);
+in_port_t
+ socket_getport(struct sockaddr_storage *);
+int socket_bypass(int, struct sockaddr *);
+int udp_bind(struct sockaddr *, in_port_t);
+ssize_t recvfromto(int, void *, size_t, int, struct sockaddr *,
+ socklen_t *, struct sockaddr *, socklen_t *);
+const char *
+ print_spi(u_int64_t, int);
+const char *
+ print_map(u_int, struct iked_constmap *);
+void print_hex(u_int8_t *, off_t, size_t);
+void print_hexval(u_int8_t *, off_t, size_t);
+const char *
+ print_bits(u_short, char *);
+int sockaddr_cmp(struct sockaddr *, struct sockaddr *, int);
+struct in6_addr *
+ prefixlen2mask6(u_int8_t, u_int32_t *);
+u_int32_t
+ prefixlen2mask(u_int8_t);
+const char *
+ print_host(struct sockaddr_storage *, char *, size_t);
+char *get_string(u_int8_t *, size_t);
+int print_id(struct iked_id *, off_t, char *, size_t);
+const char *
+ print_proto(u_int8_t);
+void message_cleanup(struct iked *, struct iked_message *);
+int expand_string(char *, size_t, const char *, const char *);
+u_int8_t *string2unicode(const char *, size_t *);
+
+struct ibuf *
+ ibuf_new(void *, size_t);
+struct ibuf *
+ ibuf_static(void);
+int ibuf_cat(struct ibuf *, struct ibuf *);
+void ibuf_release(struct ibuf *);
+size_t ibuf_length(struct ibuf *);
+int ibuf_setsize(struct ibuf *, size_t);
+u_int8_t *
+ ibuf_data(struct ibuf *);
+void *ibuf_get(struct ibuf *, size_t);
+struct ibuf *
+ ibuf_copy(struct ibuf *, size_t);
+struct ibuf *
+ ibuf_dup(struct ibuf *);
+struct ibuf *
+ ibuf_random(size_t);
+int ibuf_prepend(struct ibuf *, void *, size_t);
+void *ibuf_advance(struct ibuf *, size_t);
+void ibuf_zero(struct ibuf *);
+
+/* log.c */
+void log_init(int);
+void log_verbose(int);
+void log_warn(const char *, ...);
+void log_warnx(const char *, ...);
+void log_info(const char *, ...);
+void log_debug(const char *, ...);
+void print_debug(const char *, ...);
+void print_verbose(const char *, ...);
+__dead void fatal(const char *);
+__dead void fatalx(const char *);
+
+/* parse.y */
+int parse_config(const char *, struct iked *);
+void print_user(struct iked_user *);
+void print_policy(struct iked_policy *);
+size_t keylength_xf(u_int, u_int, u_int);
+
+#endif /* _IKED_H */
diff --git a/sbin/iked/ikev1.c b/sbin/iked/ikev1.c
new file mode 100644
index 00000000000..ba4c4523a58
--- /dev/null
+++ b/sbin/iked/ikev1.c
@@ -0,0 +1,183 @@
+/* $OpenBSD: ikev1.c,v 1.1 2010/06/03 16:41:12 reyk Exp $ */
+/* $vantronix: ikev1.c,v 1.13 2010/05/28 15:34:35 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * XXX Either implement IKEv1,
+ * XXX or find a way to pass IKEv1 messages to isakmpd,
+ * XXX or remove this file and ikev1 from the iked tree.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/uio.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+#include <signal.h>
+#include <errno.h>
+#include <err.h>
+#include <pwd.h>
+#include <event.h>
+
+#include "iked.h"
+#include "ikev2.h"
+
+int ikev1_dispatch_parent(int, struct iked_proc *, struct imsg *);
+int ikev1_dispatch_ikev2(int, struct iked_proc *, struct imsg *);
+
+void ikev1_message_cb(int, short, void *);
+void ikev1_message_recv(struct iked *, struct iked_message *);
+
+static struct iked_proc procs[] = {
+ { "parent", PROC_PARENT, ikev1_dispatch_parent },
+ { "ikev2", PROC_IKEV2, ikev1_dispatch_ikev2 }
+};
+
+pid_t
+ikev1(struct iked *env, struct iked_proc *p)
+{
+ return (run_proc(env, p, procs, nitems(procs), NULL, NULL));
+}
+
+int
+ikev1_dispatch_parent(int fd, struct iked_proc *p, struct imsg *imsg)
+{
+ struct iked *env = p->env;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_RESET:
+ log_debug("%s: config reload", __func__);
+ return (0);
+ case IMSG_UDP_SOCKET:
+ return (config_getsocket(env, imsg, ikev1_message_cb));
+ default:
+ break;
+ }
+
+ return (-1);
+}
+
+int
+ikev1_dispatch_ikev2(int fd, struct iked_proc *p, struct imsg *imsg)
+{
+ struct iked *env = p->env;
+ struct iked_message msg;
+ u_int8_t *buf;
+ ssize_t len;
+
+ switch (imsg->hdr.type) {
+ case IMSG_IKE_MESSAGE:
+ log_debug("%s: message", __func__);
+ IMSG_SIZE_CHECK(imsg, &msg);
+ memcpy(&msg, imsg->data, sizeof(msg));
+
+ len = IMSG_DATA_SIZE(imsg) - sizeof(msg);
+ buf = (u_int8_t *)imsg->data + sizeof(msg);
+ if (len <= 0 || (msg.msg_data = ibuf_new(buf, len)) == NULL) {
+ log_debug("%s: short message", __func__);
+ return (0);
+ }
+
+ log_debug("%s: message length %d", __func__, len);
+
+ ikev1_message_recv(env, &msg);
+ message_cleanup(env, &msg);
+ return (0);
+ default:
+ break;
+ }
+
+ return (-1);
+}
+
+void
+ikev1_message_cb(int fd, short event, void *arg)
+{
+ struct iked_socket *sock = arg;
+ struct iked *env = sock->sock_env;
+ struct iked_message msg;
+ struct ike_header hdr;
+ u_int8_t buf[IKED_MSGBUF_MAX];
+ size_t len;
+ struct iovec iov[2];
+
+ msg.msg_peerlen = sizeof(msg.msg_peer);
+ msg.msg_locallen = sizeof(msg.msg_local);
+
+ if ((len = recvfromto(fd, buf, sizeof(buf), 0,
+ (struct sockaddr*)&msg.msg_peer, &msg.msg_peerlen,
+ (struct sockaddr*)&msg.msg_local, &msg.msg_locallen)) < 1)
+ return;
+
+ if ((size_t)len <= sizeof(hdr))
+ return;
+ memcpy(&hdr, buf, sizeof(hdr));
+
+ if ((msg.msg_data = ibuf_new(buf, len)) == NULL)
+ return;
+
+ if (hdr.ike_version == IKEV2_VERSION) {
+ iov[0].iov_base = &msg;
+ iov[0].iov_len = sizeof(msg);
+ iov[1].iov_base = buf;
+ iov[1].iov_len = len;
+
+ imsg_composev_proc(env, PROC_IKEV2, IMSG_IKE_MESSAGE, -1,
+ iov, 2);
+ goto done;
+ }
+
+ ikev1_message_recv(env, &msg);
+
+ done:
+ message_cleanup(env, &msg);
+}
+
+void
+ikev1_message_recv(struct iked *env, struct iked_message *msg)
+{
+ struct ike_header *hdr;
+
+ if (ibuf_size(msg->msg_data) <= sizeof(*hdr)) {
+ log_debug("%s: short message", __func__);
+ return;
+ }
+
+ hdr = (struct ike_header *)ibuf_data(msg->msg_data);
+
+ log_debug("%s: header ispi %s rspi %s"
+ " nextpayload %u version 0x%02x exchange %u flags 0x%02x"
+ " msgid %u length %u", __func__,
+ print_spi(betoh64(hdr->ike_ispi), 8),
+ print_spi(betoh64(hdr->ike_rspi), 8),
+ hdr->ike_nextpayload,
+ hdr->ike_version,
+ hdr->ike_exchange,
+ hdr->ike_flags,
+ betoh32(hdr->ike_msgid),
+ betoh32(hdr->ike_length));
+
+ log_debug("%s: IKEv1 not supported", __func__);
+}
diff --git a/sbin/iked/ikev2.c b/sbin/iked/ikev2.c
new file mode 100644
index 00000000000..b5872edaad8
--- /dev/null
+++ b/sbin/iked/ikev2.c
@@ -0,0 +1,4419 @@
+/* $OpenBSD: ikev2.c,v 1.1 2010/06/03 16:41:12 reyk Exp $ */
+/* $vantronix: ikev2.c,v 1.101 2010/06/03 07:57:33 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/uio.h>
+
+#include <netinet/in.h>
+#include <netinet/ip_ipsp.h>
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+#include <signal.h>
+#include <errno.h>
+#include <err.h>
+#include <pwd.h>
+#include <event.h>
+
+#include <openssl/sha.h>
+#include <openssl/evp.h>
+
+#include "iked.h"
+#include "ikev2.h"
+#include "eap.h"
+#include "dh.h"
+
+int ikev2_dispatch_parent(int, struct iked_proc *, struct imsg *);
+int ikev2_dispatch_ikev1(int, struct iked_proc *, struct imsg *);
+int ikev2_dispatch_cert(int, struct iked_proc *, struct imsg *);
+
+struct iked_sa *
+ ikev2_getimsgdata(struct iked *, struct imsg *, struct iked_sahdr *,
+ u_int8_t *, u_int8_t **, size_t *);
+
+void ikev2_message_cb(int, short, void *);
+void ikev2_message_recv(struct iked *, struct iked_message *);
+int ikev2_message_valid_ike_sa(struct iked *, struct ike_header *,
+ struct iked_message *);
+int ikev2_message_send(struct iked *, int, struct iked_message *);
+int ikev2_message_send_encrypt(struct iked *, struct iked_sa *,
+ struct ibuf **, u_int8_t, u_int8_t, int);
+u_int32_t
+ ikev2_message_id(struct iked *, struct iked_sa *, int);
+struct ibuf *
+ ikev2_message_init(struct iked *, struct iked_message *,
+ struct sockaddr_storage *, socklen_t,
+ struct sockaddr_storage *, socklen_t, int);
+struct ibuf
+ *ikev2_message_auth(struct iked *, struct iked_sa *, int);
+int ikev2_message_authverify(struct iked *, struct iked_sa *,
+ struct iked_auth *, u_int8_t *, size_t, struct ibuf *);
+
+struct ibuf
+ *ikev2_message_encrypt(struct iked *, struct iked_sa *, struct ibuf *);
+struct ibuf *
+ ikev2_message_decrypt(struct iked *, struct iked_sa *,
+ struct ibuf *, struct ibuf *);
+int ikev2_message_integr(struct iked *, struct iked_sa *, struct ibuf *);
+
+int ikev2_resp_ike_sa_init(struct iked *, struct iked_message *);
+int ikev2_resp_ike_auth(struct iked *, struct iked_sa *);
+int ikev2_resp_ike_eap(struct iked *, struct iked_sa *, struct ibuf *);
+int ikev2_resp_create_child_sa(struct iked *, struct iked_message *);
+int ikev2_send_informational(struct iked *, struct iked_message *);
+
+int ikev2_parse_message(struct iked *, struct ike_header *,
+ struct iked_message *, off_t);
+int ikev2_parse_payloads(struct iked *, struct iked_message *,
+ off_t, size_t, u_int, int);
+int ikev2_parse_sa(struct iked *, struct ikev2_payload *,
+ struct iked_message *, off_t);
+int ikev2_parse_xform(struct iked *, struct ikev2_sa_proposal *,
+ struct iked_message *, off_t);
+int ikev2_parse_attr(struct iked *, struct ikev2_transform *,
+ struct iked_message *, off_t, int);
+int ikev2_parse_ke(struct iked *, struct ikev2_payload *,
+ struct iked_message *, off_t);
+int ikev2_parse_id(struct iked *, struct ikev2_payload *,
+ struct iked_message *, off_t, u_int);
+int ikev2_parse_cert(struct iked *, struct ikev2_payload *,
+ struct iked_message *, off_t);
+int ikev2_parse_certreq(struct iked *, struct ikev2_payload *,
+ struct iked_message *, off_t);
+int ikev2_parse_nonce(struct iked *, struct ikev2_payload *,
+ struct iked_message *, off_t);
+int ikev2_parse_notify(struct iked *, struct ikev2_payload *,
+ struct iked_message *, off_t);
+int ikev2_parse_delete(struct iked *, struct ikev2_payload *,
+ struct iked_message *, off_t);
+int ikev2_parse_ts(struct iked *, struct ikev2_payload *,
+ struct iked_message *, off_t, u_int);
+int ikev2_parse_auth(struct iked *, struct ikev2_payload *,
+ struct iked_message *, off_t);
+int ikev2_parse_e(struct iked *, struct ikev2_payload *,
+ struct iked_message *, off_t);
+int ikev2_parse_cp(struct iked *, struct ikev2_payload *,
+ struct iked_message *, off_t);
+int ikev2_parse_eap(struct iked *, struct ikev2_payload *,
+ struct iked_message *, off_t);
+
+int ikev2_policy2id(struct iked_static_id *, struct iked_id *, int);
+int ikev2_sa_negotiate(struct iked_sa *, struct iked_proposals *,
+ struct iked_proposals *, u_int8_t);
+int ikev2_sa_keys(struct iked_sa *);
+int ikev2_sa_tag(struct iked_sa *, struct iked_id *);
+int ikev2_childsa_negotiate(struct iked *, struct iked_sa *,
+ struct iked_spi *);
+int ikev2_childsa_enable(struct iked *, struct iked_sa *);
+int ikev2_childsa_delete(struct iked *, struct iked_sa *,
+ u_int8_t, u_int64_t, u_int64_t *, int);
+int ikev2_valid_proposal(struct iked_proposal *,
+ struct iked_transform **, struct iked_transform **);
+
+struct ike_header *
+ ikev2_add_header(struct ibuf *, struct iked_sa *,
+ u_int32_t, u_int8_t, u_int8_t, u_int8_t);
+int ikev2_set_header(struct ike_header *, size_t);
+struct ikev2_payload *
+ ikev2_add_payload(struct ibuf *);
+int ikev2_next_payload(struct ikev2_payload *, size_t,
+ u_int8_t);
+ssize_t ikev2_add_proposals(struct ibuf *, struct iked_proposals *, u_int8_t);
+ssize_t ikev2_nat_detection(struct iked_message *, void *, size_t,
+ u_int, int);
+ssize_t ikev2_add_cp(struct iked *, struct iked_sa *, struct ibuf *);
+ssize_t ikev2_add_transform(struct ibuf *,
+ u_int8_t, u_int8_t, u_int16_t, u_int16_t);
+ssize_t ikev2_add_ts(struct ibuf *, u_int, struct iked_sa *);
+int ikev2_add_data(struct ibuf *, void *, size_t);
+int ikev2_add_buf(struct ibuf *buf, struct ibuf *);
+
+static struct iked_proc procs[] = {
+ { "parent", PROC_PARENT, ikev2_dispatch_parent },
+ { "ikev1", PROC_IKEV1, ikev2_dispatch_ikev1 },
+ { "certstore", PROC_CERT, ikev2_dispatch_cert }
+};
+
+pid_t
+ikev2(struct iked *env, struct iked_proc *p)
+{
+ return (run_proc(env, p, procs, nitems(procs), NULL, NULL));
+}
+
+int
+ikev2_dispatch_parent(int fd, struct iked_proc *p, struct imsg *imsg)
+{
+ struct iked *env = p->env;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_RESET:
+ return (config_getreset(env, imsg));
+ case IMSG_UDP_SOCKET:
+ return (config_getsocket(env, imsg, ikev2_message_cb));
+ case IMSG_PFKEY_SOCKET:
+ return (config_getpfkey(env, imsg));
+ case IMSG_CFG_POLICY:
+ return (config_getpolicy(env, imsg));
+ case IMSG_CFG_USER:
+ return (config_getuser(env, imsg));
+ default:
+ break;
+ }
+
+ return (-1);
+}
+
+int
+ikev2_dispatch_ikev1(int fd, struct iked_proc *p, struct imsg *imsg)
+{
+ struct iked *env = p->env;
+ struct iked_message msg;
+ u_int8_t *buf;
+ ssize_t len;
+
+ switch (imsg->hdr.type) {
+ case IMSG_IKE_MESSAGE:
+ log_debug("%s: message", __func__);
+ IMSG_SIZE_CHECK(imsg, &msg);
+ memcpy(&msg, imsg->data, sizeof(msg));
+
+ len = IMSG_DATA_SIZE(imsg) - sizeof(msg);
+ buf = (u_int8_t *)imsg->data + sizeof(msg);
+ if (len <= 0 || (msg.msg_data = ibuf_new(buf, len)) == NULL) {
+ log_debug("%s: short message", __func__);
+ return (0);
+ }
+
+ log_debug("%s: message length %d", __func__, len);
+
+ ikev2_message_recv(env, &msg);
+ message_cleanup(env, &msg);
+ return (0);
+ default:
+ break;
+ }
+
+ return (-1);
+}
+
+int
+ikev2_dispatch_cert(int fd, struct iked_proc *p, struct imsg *imsg)
+{
+ struct iked *env = p->env;
+ struct iked_sahdr sh;
+ struct iked_sa *sa;
+ u_int8_t type;
+ u_int8_t *ptr;
+ size_t len;
+ struct iked_id *id = NULL;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CERTREQ:
+ IMSG_SIZE_CHECK(imsg, &type);
+
+ ptr = imsg->data;
+ memcpy(&type, ptr, sizeof(type));
+ ptr += sizeof(type);
+
+ ibuf_release(env->sc_certreq);
+ env->sc_certreqtype = type;
+ env->sc_certreq = ibuf_new(ptr,
+ IMSG_DATA_SIZE(imsg) - sizeof(type));
+
+ log_debug("%s: updated local CERTREQ signatures length %d",
+ __func__, ibuf_length(env->sc_certreq));
+
+ break;
+ case IMSG_CERTVALID:
+ case IMSG_CERTINVALID:
+ memcpy(&sh, imsg->data, sizeof(sh));
+ memcpy(&type, (u_int8_t *)imsg->data + sizeof(sh),
+ sizeof(type));
+
+ /* Ignore invalid or unauthenticated SAs */
+ if ((sa = sa_lookup(env,
+ sh.sh_ispi, sh.sh_rspi, sh.sh_initiator)) == NULL ||
+ sa->sa_state < IKEV2_STATE_EAP)
+ break;
+
+ if (imsg->hdr.type == IMSG_CERTVALID) {
+ log_debug("%s: peer certificate is valid", __func__);
+ sa_state(env, sa, IKEV2_STATE_VALID);
+ } else
+ log_debug("%s: peer certificate is invalid", __func__);
+
+ sa_stateflags(sa, IKED_REQ_VALID);
+
+ if (ikev2_resp_ike_auth(env, sa) != 0)
+ log_debug("%s: failed to send auth response", __func__);
+ break;
+ case IMSG_CERT:
+ if ((sa = ikev2_getimsgdata(env, imsg,
+ &sh, &type, &ptr, &len)) == NULL) {
+ log_debug("%s: invalid cert reply");
+ break;
+ }
+
+ if (sh.sh_initiator)
+ id = &sa->sa_icert;
+ else
+ id = &sa->sa_rcert;
+
+ id->id_type = type;
+ ibuf_release(id->id_buf);
+ id->id_buf = NULL;
+
+ if (type != IKEV2_CERT_NONE) {
+ if (len <= 0 ||
+ (id->id_buf = ibuf_new(ptr, len)) == NULL) {
+ log_debug("%s: failed to get cert payload",
+ __func__);
+ break;
+ }
+ }
+
+ log_debug("%s: cert type %d length %d", __func__,
+ id->id_type, ibuf_length(id->id_buf));
+
+ sa_stateflags(sa, IKED_REQ_CERT);
+
+ if (ikev2_resp_ike_auth(env, sa) != 0)
+ log_debug("%s: failed to send auth response", __func__);
+ break;
+ case IMSG_AUTH:
+ if ((sa = ikev2_getimsgdata(env, imsg,
+ &sh, &type, &ptr, &len)) == NULL) {
+ log_debug("%s: invalid auth reply", __func__);
+ break;
+ }
+
+ log_debug("%s: AUTH type %d len %d", __func__, type, len);
+
+ id = &sa->sa_localauth;
+ id->id_type = type;
+ ibuf_release(id->id_buf);
+
+ if (type != IKEV2_AUTH_NONE) {
+ if (len <= 0 ||
+ (id->id_buf = ibuf_new(ptr, len)) == NULL) {
+ log_debug("%s: failed to get auth payload",
+ __func__);
+ break;
+ }
+ }
+
+ sa_stateflags(sa, IKED_REQ_AUTH);
+
+ if (ikev2_resp_ike_auth(env, sa) != 0)
+ log_debug("%s: failed to send auth response", __func__);
+ break;
+ default:
+ return (-1);
+ }
+
+ return (0);
+}
+
+struct iked_sa *
+ikev2_getimsgdata(struct iked *env, struct imsg *imsg, struct iked_sahdr *sh,
+ u_int8_t *type, u_int8_t **buf, size_t *size)
+{
+ u_int8_t *ptr;
+ size_t len;
+ struct iked_sa *sa;
+
+ IMSG_SIZE_CHECK(imsg, sh);
+
+ ptr = imsg->data;
+ len = IMSG_DATA_SIZE(imsg) - sizeof(*sh) - sizeof(*type);
+ memcpy(sh, ptr, sizeof(*sh));
+ memcpy(type, ptr + sizeof(*sh), sizeof(*type));
+
+ sa = sa_lookup(env, sh->sh_ispi, sh->sh_rspi, sh->sh_initiator);
+
+ log_debug("%s: imsg %d rspi %s ispi %s initiator %d sa %s"
+ " type %d data length %d",
+ __func__, imsg->hdr.type,
+ print_spi(sh->sh_rspi, 8),
+ print_spi(sh->sh_ispi, 8),
+ sh->sh_initiator,
+ sa == NULL ? "invalid" : "valid", *type, len);
+
+ if (sa == NULL)
+ return (NULL);
+
+ *buf = ptr + sizeof(*sh) + sizeof(*type);
+ *size = len;
+
+ return (sa);
+}
+
+void
+ikev2_message_cb(int fd, short event, void *arg)
+{
+ struct iked_socket *sock = arg;
+ struct iked *env = sock->sock_env;
+ struct iked_message msg;
+ struct ike_header hdr;
+ u_int32_t natt = 0x00000000;
+ u_int8_t buf[IKED_MSGBUF_MAX];
+ ssize_t len;
+ off_t off;
+ struct iovec iov[2];
+
+ bzero(&msg, sizeof(msg));
+ bzero(buf, sizeof(buf));
+
+ msg.msg_peerlen = sizeof(msg.msg_peer);
+ msg.msg_locallen = sizeof(msg.msg_local);
+ memcpy(&msg.msg_local, &sock->sock_addr, sizeof(sock->sock_addr));
+
+ if ((len = recvfromto(fd, buf, sizeof(buf), 0,
+ (struct sockaddr*)&msg.msg_peer, &msg.msg_peerlen,
+ (struct sockaddr*)&msg.msg_local, &msg.msg_locallen)) <
+ (ssize_t)sizeof(natt))
+ return;
+
+ if (socket_getport(&msg.msg_local) == IKED_NATT_PORT) {
+ if (bcmp(&natt, buf, sizeof(natt)) != 0)
+ return;
+ msg.msg_natt = 1;
+ off = sizeof(natt);
+ } else
+ off = 0;
+
+ if ((size_t)(len - off) <= sizeof(hdr))
+ return;
+ memcpy(&hdr, buf + off, sizeof(hdr));
+
+ if ((msg.msg_data = ibuf_new(buf + off, len - off)) == NULL)
+ return;
+
+ if (hdr.ike_version == IKEV1_VERSION) {
+ iov[0].iov_base = &msg;
+ iov[0].iov_len = sizeof(msg);
+ iov[1].iov_base = buf;
+ iov[1].iov_len = len;
+
+ imsg_composev_proc(env, PROC_IKEV1, IMSG_IKE_MESSAGE, -1,
+ iov, 2);
+ goto done;
+ }
+ TAILQ_INIT(&msg.msg_proposals);
+
+ msg.msg_fd = fd;
+ ikev2_message_recv(env, &msg);
+
+ done:
+ message_cleanup(env, &msg);
+}
+
+void
+ikev2_message_recv(struct iked *env, struct iked_message *msg)
+{
+ u_int initiator;
+ struct iked_sa *sa;
+ struct ike_header *hdr;
+
+ hdr = ibuf_seek(msg->msg_data, msg->msg_offset, sizeof(*hdr));
+
+ if (hdr == NULL || (ssize_t)ibuf_size(msg->msg_data) <
+ (betoh32(hdr->ike_length) - msg->msg_offset))
+ return;
+
+ initiator = (hdr->ike_flags & IKEV2_FLAG_INITIATOR) ? 0 : 1;
+ msg->msg_sa = sa_lookup(env,
+ betoh64(hdr->ike_ispi), betoh64(hdr->ike_rspi),
+ initiator);
+ if (policy_lookup(env, msg) != 0)
+ return;
+
+ log_info("%s: %s from %s to %s policy '%s', %ld bytes", __func__,
+ print_map(hdr->ike_exchange, ikev2_exchange_map),
+ print_host(&msg->msg_peer, NULL, 0),
+ print_host(&msg->msg_local, NULL, 0),
+ msg->msg_policy->pol_name,
+ ibuf_size(msg->msg_data));
+
+ switch (hdr->ike_exchange) {
+ case IKEV2_EXCHANGE_IKE_SA_INIT:
+ if (msg->msg_sa != NULL) {
+ log_debug("%s: SA already exists", __func__);
+ return;
+ }
+ if ((msg->msg_sa = sa_new(env,
+ betoh64(hdr->ike_ispi), betoh64(hdr->ike_rspi),
+ initiator, msg->msg_policy)) == NULL) {
+ log_debug("%s: failed to get new SA", __func__);
+ return;
+ }
+ break;
+ case IKEV2_EXCHANGE_IKE_AUTH:
+ if (ikev2_message_valid_ike_sa(env, hdr, msg) == -1)
+ return;
+ if (sa_stateok(msg->msg_sa, IKEV2_STATE_VALID)) {
+ log_debug("%s: already authenticated", __func__);
+ return;
+ }
+ break;
+ case IKEV2_EXCHANGE_CREATE_CHILD_SA:
+ if (ikev2_message_valid_ike_sa(env, hdr, msg) == -1)
+ return;
+ break;
+ case IKEV2_EXCHANGE_INFORMATIONAL:
+ if (ikev2_message_valid_ike_sa(env, hdr, msg) == -1)
+ return;
+ break;
+ default:
+ log_debug("%s: unsupported exchange: %s", __func__,
+ print_map(hdr->ike_exchange, ikev2_exchange_map));
+ return;
+ }
+
+ if (msg->msg_sa != NULL) {
+ sa = msg->msg_sa;
+
+ log_debug("%s: updating msg, natt %d", __func__, msg->msg_natt);
+
+ if (msg->msg_natt)
+ sa->sa_natt = 1;
+
+ if (betoh32(hdr->ike_msgid) < sa->sa_msgid) {
+ log_debug("%s: invalid sequence number", __func__);
+ return;
+ } else
+ sa->sa_msgid = betoh32(hdr->ike_msgid);
+
+ if (msg->msg_peerlen > sizeof(sa->sa_peer)) {
+ log_debug("%s: invalid peer address",
+ __func__);
+ return;
+ }
+ sa->sa_peer.addr_af = msg->msg_peer.ss_family;
+ sa->sa_peer.addr.ss_len = msg->msg_peerlen;
+ sa->sa_peer.addr_port =
+ htons(socket_getport(&msg->msg_peer));
+ socket_af((struct sockaddr *)&sa->sa_peer.addr,
+ sa->sa_peer.addr_port);
+ memcpy(&sa->sa_peer.addr, &msg->msg_peer,
+ sizeof(sa->sa_peer.addr));
+
+ if (msg->msg_locallen > sizeof(sa->sa_local)) {
+ log_debug("%s: invalid local address",
+ __func__);
+ return;
+ }
+ sa->sa_local.addr_af = msg->msg_local.ss_family;
+ sa->sa_local.addr.ss_len = msg->msg_locallen;
+ sa->sa_local.addr_port =
+ htons(socket_getport(&msg->msg_local));
+ socket_af((struct sockaddr *)&sa->sa_local.addr,
+ sa->sa_local.addr_port);
+ memcpy(&sa->sa_local.addr, &msg->msg_local,
+ sizeof(sa->sa_local.addr));
+ sa->sa_fd = msg->msg_fd;
+
+ log_debug("%s: updated SA peer %s local %s", __func__,
+ print_host(&sa->sa_peer.addr, NULL, 0),
+ print_host(&sa->sa_local.addr, NULL, 0));
+ }
+
+ if (ikev2_parse_message(env, hdr, msg, msg->msg_offset) != 0) {
+ log_debug("%s: failed to parse message", __func__);
+ return;
+ }
+
+ if (msg->msg_response)
+ return;
+
+ switch (hdr->ike_exchange) {
+ case IKEV2_EXCHANGE_IKE_SA_INIT:
+ sa = msg->msg_sa;
+
+ if (sa == NULL) {
+ log_debug("%s: invalid sa", __func__);
+ return;
+ }
+
+ sa_state(env, sa, IKEV2_STATE_SA_INIT);
+
+ if ((ibuf_length(sa->sa_inonce) < IKED_NONCE_MIN) ||
+ (ibuf_length(sa->sa_rnonce) < IKED_NONCE_MIN)) {
+ log_debug("%s: invalid nonce", __func__);
+ sa_free(env, sa);
+ return;
+ }
+ if (ikev2_sa_keys(sa) != 0) {
+ log_debug("%s: failed to get IKE SA keys", __func__);
+ sa_free(env, sa);
+ return;
+ }
+
+ if (ikev2_policy2id(&msg->msg_policy->pol_localid,
+ &sa->sa_rid, 1) != 0) {
+ log_debug("%s: failed to get responder id", __func__);
+ return;
+ }
+ ibuf_release(sa->sa_1stmsg);
+ if ((sa->sa_1stmsg = ibuf_new(ibuf_data(msg->msg_data),
+ ibuf_size(msg->msg_data))) == NULL) {
+ log_debug("%s: failed to copy 1st message", __func__);
+ return;
+ }
+
+ if (ikev2_resp_ike_sa_init(env, msg) != 0) {
+ log_debug("%s: failed to send init response", __func__);
+ return;
+ }
+ break;
+ case IKEV2_EXCHANGE_IKE_AUTH:
+ sa = msg->msg_sa;
+
+ if (!sa_stateok(sa, IKEV2_STATE_AUTH_REQUEST) &&
+ sa->sa_policy->pol_auth.auth_eap)
+ sa_state(env, sa, IKEV2_STATE_EAP);
+
+ if (ikev2_resp_ike_auth(env, sa) != 0) {
+ log_debug("%s: failed to send auth response", __func__);
+ return;
+ }
+ break;
+ case IKEV2_EXCHANGE_CREATE_CHILD_SA:
+ (void)ikev2_resp_create_child_sa(env, msg);
+ break;
+ default:
+ break;
+ }
+
+ if (msg->msg_sa && msg->msg_sa->sa_state == IKEV2_STATE_DELETE)
+ sa_free(env, msg->msg_sa);
+}
+
+int
+ikev2_message_valid_ike_sa(struct iked *env, struct ike_header *oldhdr,
+ struct iked_message *msg)
+{
+#if 0
+ /* XXX Disabled, see comment below */
+ struct iked_message resp;
+ struct ike_header *hdr;
+ struct ikev2_payload *pld;
+ struct ikev2_notify *n;
+ struct ibuf *buf;
+ struct iked_sa sa;
+#endif
+
+ if (msg->msg_sa != NULL && msg->msg_policy != NULL)
+ return (0);
+
+#if 0
+ /*
+ * XXX Sending INVALID_IKE_SPIs notifications is disabled
+ * XXX because it is not mandatory and ignored by most
+ * XXX implementations. We might want to enable it in
+ * XXX combination with a rate-limitation to avoid DoS situations.
+ */
+
+ /* Fail without error message */
+ if (msg->msg_response || msg->msg_policy == NULL)
+ return (-1);
+
+ /* Invalid IKE SA, return notification */
+ if ((buf = ikev2_message_init(env, &resp,
+ &msg->msg_peer, msg->msg_peerlen,
+ &msg->msg_local, msg->msg_locallen, 1)) == NULL)
+ goto done;
+
+ bzero(&sa, sizeof(sa));
+ if ((oldhdr->ike_flags & IKEV2_FLAG_INITIATOR) == 0)
+ sa.sa_hdr.sh_initiator = 1;
+ sa.sa_hdr.sh_ispi = betoh64(oldhdr->ike_ispi);
+ sa.sa_hdr.sh_rspi = betoh64(oldhdr->ike_rspi);
+
+ /* IKE header */
+ if ((hdr = ikev2_add_header(buf, &sa, betoh32(oldhdr->ike_msgid),
+ IKEV2_PAYLOAD_NOTIFY, IKEV2_EXCHANGE_INFORMATIONAL,
+ IKEV2_FLAG_RESPONSE)) == NULL)
+ goto done;
+
+ /* SA payload */
+ if ((pld = ikev2_add_payload(buf)) == NULL)
+ goto done;
+ if ((n = ibuf_advance(buf, sizeof(*n))) == NULL)
+ goto done;
+ n->n_protoid = IKEV2_SAPROTO_IKE;
+ n->n_spisize = 0;
+ n->n_type = htobe16(IKEV2_N_INVALID_IKE_SPI);
+
+ if (ikev2_next_payload(pld, sizeof(*n), IKEV2_PAYLOAD_NONE) == -1)
+ goto done;
+
+ if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1)
+ goto done;
+
+ (void)ikev2_parse_message(env, hdr, &resp, 0);
+ (void)ikev2_message_send(env, msg->msg_fd, &resp);
+
+ done:
+ message_cleanup(env, &resp);
+#endif
+
+ /* Always fail */
+ return (-1);
+}
+
+int
+ikev2_policy2id(struct iked_static_id *polid, struct iked_id *id, int srcid)
+{
+ struct ikev2_id hdr;
+ struct iked_static_id localpid;
+ char idstr[IKED_ID_SIZE];
+ struct in_addr in4;
+ struct in6_addr in6;
+
+ /* Fixup the local Id if not specified */
+ if (srcid && polid->id_type == 0) {
+ polid = &localpid;
+ bzero(polid, sizeof(*polid));
+
+ /* Create a default local ID based on our FQDN */
+ polid->id_type = IKEV2_ID_FQDN;
+ if (gethostname((char *)polid->id_data,
+ sizeof(polid->id_data)) != 0)
+ return (-1);
+ polid->id_length =
+ strlen((char *)polid->id_data); /* excluding NUL */
+ }
+
+ if (!polid->id_length)
+ return (-1);
+
+ /* Create an IKEv2 ID payload */
+ bzero(&hdr, sizeof(hdr));
+ hdr.id_type = id->id_type = polid->id_type;
+
+ if ((id->id_buf = ibuf_new(&hdr, sizeof(hdr))) == NULL)
+ return (-1);
+
+ switch (id->id_type) {
+ case IKEV2_ID_IPV4_ADDR:
+ if (inet_pton(AF_INET, polid->id_data, &in4) != 1 ||
+ ibuf_add(id->id_buf, &in4, sizeof(in4)) != 0) {
+ ibuf_release(id->id_buf);
+ return (-1);
+ }
+ break;
+ case IKEV2_ID_IPV6_ADDR:
+ if (inet_pton(AF_INET6, polid->id_data, &in6) != 1 ||
+ ibuf_add(id->id_buf, &in6, sizeof(in6)) != 0) {
+ ibuf_release(id->id_buf);
+ return (-1);
+ }
+ break;
+ default:
+ if (ibuf_add(id->id_buf,
+ polid->id_data, polid->id_length) != 0) {
+ ibuf_release(id->id_buf);
+ return (-1);
+ }
+ break;
+ }
+
+ if (print_id(id, sizeof(hdr), idstr, sizeof(idstr)) == -1)
+ return (-1);
+
+ log_debug("%s: %s %s/%s length %d", __func__,
+ srcid ? "srcid" : "dstid",
+ print_map(id->id_type, ikev2_id_map),
+ idstr, ibuf_size(id->id_buf));
+
+ return (0);
+}
+
+struct ike_header *
+ikev2_add_header(struct ibuf *buf, struct iked_sa *sa,
+ u_int32_t msgid, u_int8_t nextpayload,
+ u_int8_t exchange, u_int8_t flags)
+{
+ struct ike_header *hdr;
+
+ if ((hdr = ibuf_advance(buf, sizeof(*hdr))) == NULL) {
+ log_debug("%s: failed to add header", __func__);
+ return (NULL);
+ }
+
+ hdr->ike_ispi = htobe64(sa->sa_hdr.sh_ispi);
+ hdr->ike_rspi = htobe64(sa->sa_hdr.sh_rspi);
+ hdr->ike_nextpayload = nextpayload;
+ hdr->ike_version = IKEV2_VERSION;
+ hdr->ike_exchange = exchange;
+ hdr->ike_msgid = htobe32(msgid);
+ hdr->ike_length = htobe32(sizeof(*hdr));
+ hdr->ike_flags = flags;
+
+ if (sa->sa_hdr.sh_initiator)
+ hdr->ike_flags |= IKEV2_FLAG_INITIATOR;
+
+ return (hdr);
+}
+
+int
+ikev2_set_header(struct ike_header *hdr, size_t length)
+{
+ u_int32_t hdrlength = sizeof(*hdr) + length;
+
+ if (hdrlength > UINT32_MAX) {
+ log_debug("%s: message too long", __func__);
+ return (-1);
+ }
+
+ hdr->ike_length = htobe32(sizeof(*hdr) + length);
+
+ return (0);
+}
+
+struct ikev2_payload *
+ikev2_add_payload(struct ibuf *buf)
+{
+ struct ikev2_payload *pld;
+
+ if ((pld = ibuf_advance(buf, sizeof(*pld))) == NULL) {
+ log_debug("%s: failed to add payload", __func__);
+ return (NULL);
+ }
+
+ pld->pld_nextpayload = IKEV2_PAYLOAD_NONE;
+ pld->pld_length = sizeof(*pld);
+
+ return (pld);
+}
+
+ssize_t
+ikev2_add_ts(struct ibuf *buf, u_int type, struct iked_sa *sa)
+{
+ struct iked_policy *pol = sa->sa_policy;
+ struct ikev2_tsp *tsp;
+ struct ikev2_ts *ts;
+ struct iked_flow *flow;
+ struct iked_addr *addr;
+ u_int8_t *ptr;
+ size_t len = 0;
+ u_int32_t av[4], bv[4], mv[4];
+ struct sockaddr_in *in4;
+ struct sockaddr_in6 *in6;
+
+ if ((tsp = ibuf_advance(buf, sizeof(*tsp))) == NULL)
+ return (-1);
+ tsp->tsp_count = pol->pol_nflows;
+ len = sizeof(*tsp);
+
+ TAILQ_FOREACH(flow, &pol->pol_flows, flow_entry) {
+ if ((ts = ibuf_advance(buf, sizeof(*ts))) == NULL)
+ return (-1);
+
+ if (type == IKEV2_PAYLOAD_TSi)
+ addr = &flow->flow_src;
+ else if (type == IKEV2_PAYLOAD_TSr)
+ addr = &flow->flow_dst;
+ else
+ return (-1);
+
+ ts->ts_protoid = flow->flow_ipproto;
+
+ if (addr->addr_port) {
+ ts->ts_startport = addr->addr_port;
+ ts->ts_endport = addr->addr_port;
+ } else {
+ ts->ts_startport = 0;
+ ts->ts_endport = 0xffff;
+ }
+
+ switch (addr->addr_af) {
+ case AF_INET:
+ ts->ts_type = IKEV2_TS_IPV4_ADDR_RANGE;
+ ts->ts_length = htobe16(sizeof(*ts) + 8);
+
+ if ((ptr = ibuf_advance(buf, 8)) == NULL)
+ return (-1);
+
+ in4 = (struct sockaddr_in *)&addr->addr;
+ if (addr->addr_net) {
+ /* Convert IPv4 network to address range */
+ mv[0] = prefixlen2mask(addr->addr_mask);
+ av[0] = in4->sin_addr.s_addr & mv[0];
+ bv[0] = in4->sin_addr.s_addr | ~mv[0];
+ } else
+ av[0] = bv[0] = in4->sin_addr.s_addr;
+
+ memcpy(ptr, &av[0], 4);
+ memcpy(ptr + 4, &bv[0], 4);
+ break;
+ case AF_INET6:
+ ts->ts_type = IKEV2_TS_IPV6_ADDR_RANGE;
+ ts->ts_length = htobe16(sizeof(*ts) + 32);
+
+ if ((ptr = ibuf_advance(buf, 32)) == NULL)
+ return (-1);
+
+ in6 = (struct sockaddr_in6 *)&addr->addr;
+
+ memcpy(&av, &in6->sin6_addr.s6_addr, 16);
+ memcpy(&bv, &in6->sin6_addr.s6_addr, 16);
+ if (addr->addr_net) {
+ /* Convert IPv6 network to address range */
+ prefixlen2mask6(addr->addr_mask, mv);
+ av[0] &= mv[0];
+ av[1] &= mv[1];
+ av[2] &= mv[2];
+ av[3] &= mv[3];
+ bv[0] |= ~mv[0];
+ bv[1] |= ~mv[1];
+ bv[2] |= ~mv[2];
+ bv[3] |= ~mv[3];
+ }
+
+ memcpy(ptr, &av, 16);
+ memcpy(ptr + 16, &bv, 16);
+ break;
+ }
+
+ len += betoh16(ts->ts_length);
+ }
+
+ return (len);
+}
+
+int
+ikev2_next_payload(struct ikev2_payload *pld, size_t length,
+ u_int8_t nextpayload)
+{
+ size_t pldlength = sizeof(*pld) + length;
+
+ if (pldlength > UINT16_MAX) {
+ log_debug("%s: payload too long", __func__);
+ return (-1);
+ }
+
+ log_debug("%s: length %d nextpayload %s",
+ __func__, pldlength, print_map(nextpayload, ikev2_payload_map));
+
+ pld->pld_length = htobe16(pldlength);
+ pld->pld_nextpayload = nextpayload;
+
+ return (0);
+}
+
+ssize_t
+ikev2_nat_detection(struct iked_message *msg, void *ptr, size_t len,
+ u_int type, int response)
+{
+ EVP_MD_CTX ctx;
+ struct ike_header *hdr;
+ u_int8_t md[SHA_DIGEST_LENGTH];
+ u_int mdlen = sizeof(md);
+ struct iked_sa *sa = msg->msg_sa;
+ struct sockaddr_in *in4;
+ struct sockaddr_in6 *in6;
+ ssize_t ret = -1;
+ struct sockaddr_storage *ss;
+ u_int64_t rspi, ispi;
+
+ if (ptr == NULL)
+ return (mdlen);
+
+ if (!response || sa == NULL) {
+ if ((hdr = ibuf_seek(msg->msg_data, 0, sizeof(*hdr))) == NULL)
+ return (-1);
+ ispi = hdr->ike_ispi;
+ rspi = hdr->ike_rspi;
+ } else {
+ ispi = htobe64(sa->sa_hdr.sh_ispi);
+ rspi = htobe64(sa->sa_hdr.sh_rspi);
+ }
+
+ EVP_MD_CTX_init(&ctx);
+ EVP_DigestInit_ex(&ctx, EVP_sha1(), NULL);
+
+ switch (type) {
+ case IKEV2_N_NAT_DETECTION_SOURCE_IP:
+ if (response)
+ ss = &msg->msg_local;
+ else
+ ss = &msg->msg_peer;
+
+ log_debug("%s: source %s %s %s", __func__,
+ print_spi(betoh64(ispi), 8),
+ print_spi(betoh64(rspi), 8),
+ print_host(ss, NULL, 0));
+ break;
+ case IKEV2_N_NAT_DETECTION_DESTINATION_IP:
+ if (response)
+ ss = &msg->msg_peer;
+ else
+ ss = &msg->msg_local;
+
+ log_debug("%s: destination %s %s %s", __func__,
+ print_spi(betoh64(ispi), 8),
+ print_spi(betoh64(rspi), 8),
+ print_host(ss, NULL, 0));
+ break;
+ default:
+ goto done;
+ }
+
+ EVP_DigestUpdate(&ctx, &ispi, sizeof(ispi));
+ EVP_DigestUpdate(&ctx, &rspi, sizeof(rspi));
+
+ switch (ss->ss_family) {
+ case AF_INET:
+ in4 = (struct sockaddr_in *)ss;
+ EVP_DigestUpdate(&ctx, &in4->sin_addr.s_addr,
+ sizeof(in4->sin_addr.s_addr));
+ EVP_DigestUpdate(&ctx, &in4->sin_port,
+ sizeof(in4->sin_port));
+ break;
+ case AF_INET6:
+ in6 = (struct sockaddr_in6 *)ss;
+ EVP_DigestUpdate(&ctx, &in6->sin6_addr.s6_addr,
+ sizeof(in6->sin6_addr.s6_addr));
+ EVP_DigestUpdate(&ctx, &in6->sin6_port,
+ sizeof(in6->sin6_port));
+ break;
+ default:
+ goto done;
+ }
+
+ EVP_DigestFinal_ex(&ctx, md, &mdlen);
+
+ if (len < mdlen)
+ goto done;
+
+ memcpy(ptr, md, mdlen);
+ ret = mdlen;
+ done:
+ EVP_MD_CTX_cleanup(&ctx);
+
+ return (ret);
+}
+
+ssize_t
+ikev2_add_cp(struct iked *env, struct iked_sa *sa, struct ibuf *buf)
+{
+ struct iked_policy *pol = sa->sa_policy;
+ struct ikev2_cp *cp;
+ struct ikev2_cfg *cfg;
+ struct iked_cfg *ikecfg;
+ u_int i;
+ size_t len;
+ struct sockaddr_in *in4;
+ struct sockaddr_in6 *in6;
+ u_int8_t prefixlen;
+
+ if ((cp = ibuf_advance(buf, sizeof(*cp))) == NULL)
+ return (-1);
+ len = sizeof(*cp);
+
+ switch (sa->sa_cp) {
+ case IKEV2_CP_REQUEST:
+ cp->cp_type = IKEV2_CP_REPLY;
+ break;
+ case IKEV2_CP_REPLY:
+ case IKEV2_CP_SET:
+ case IKEV2_CP_ACK:
+ /* Not yet supported */
+ return (-1);
+ }
+
+ for (i = 0; i < pol->pol_ncfg; i++) {
+ ikecfg = &pol->pol_cfg[i];
+ if (ikecfg->cfg_action != cp->cp_type)
+ continue;
+
+ if ((cfg = ibuf_advance(buf, sizeof(*cfg))) == NULL)
+ return (-1);
+
+ cfg->cfg_type = htobe16(ikecfg->cfg_type);
+ len += sizeof(*cfg);
+
+ switch (ikecfg->cfg_type) {
+ case IKEV2_CFG_INTERNAL_IP4_ADDRESS:
+ case IKEV2_CFG_INTERNAL_IP4_NETMASK:
+ case IKEV2_CFG_INTERNAL_IP4_DNS:
+ case IKEV2_CFG_INTERNAL_IP4_NBNS:
+ case IKEV2_CFG_INTERNAL_IP4_DHCP:
+ case IKEV2_CFG_INTERNAL_IP4_SERVER:
+ /* 4 bytes IPv4 address */
+ in4 = (struct sockaddr_in *)&ikecfg->cfg.address.addr;
+ cfg->cfg_length = htobe16(4);
+ if (ibuf_add(buf, &in4->sin_addr.s_addr, 4) == -1)
+ return (-1);
+ len += 4;
+ break;
+ case IKEV2_CFG_INTERNAL_IP6_DNS:
+ case IKEV2_CFG_INTERNAL_IP6_NBNS:
+ case IKEV2_CFG_INTERNAL_IP6_DHCP:
+ case IKEV2_CFG_INTERNAL_IP6_SERVER:
+ /* 16 bytes IPv6 address */
+ in6 = (struct sockaddr_in6 *)&ikecfg->cfg.address;
+ cfg->cfg_length = htobe16(16);
+ if (ibuf_add(buf, &in6->sin6_addr.s6_addr, 16) == -1)
+ return (-1);
+ len += 16;
+ break;
+ case IKEV2_CFG_INTERNAL_IP6_ADDRESS:
+ /* 16 bytes IPv6 address + 1 byte prefix length */
+ in6 = (struct sockaddr_in6 *)&ikecfg->cfg.address.addr;
+ cfg->cfg_length = htobe16(17);
+ if (ibuf_add(buf, &in6->sin6_addr.s6_addr, 16) == -1)
+ return (-1);
+ if (ikecfg->cfg.address.addr_net)
+ prefixlen = ikecfg->cfg.address.addr_mask;
+ else
+ prefixlen = 128;
+ if (ibuf_add(buf, &prefixlen, 1) == -1)
+ return (-1);
+ len += 16 + 1;
+ break;
+ case IKEV2_CFG_APPLICATION_VERSION:
+ /* Reply with an empty string (non-NUL terminated) */
+ cfg->cfg_length = 0;
+ break;
+ }
+ }
+
+ return (len);
+}
+
+ssize_t
+ikev2_add_proposals(struct ibuf *buf, struct iked_proposals *proposals,
+ u_int8_t protoid)
+{
+ struct ikev2_sa_proposal *sap;
+ struct iked_transform *xform;
+ struct iked_proposal *prop;
+ ssize_t i, length = 0, saplength, ret, n;
+ u_int64_t spi64;
+ u_int32_t spi32;
+
+ n = 0;
+ TAILQ_FOREACH(prop, proposals, prop_entry) {
+ if (prop->prop_protoid != protoid)
+ continue;
+
+ if ((sap = ibuf_advance(buf, sizeof(*sap))) == NULL) {
+ log_debug("%s: failed to add proposal", __func__);
+ return (-1);
+ }
+ sap->sap_proposalnr = prop->prop_id;
+ sap->sap_protoid = prop->prop_protoid;
+ sap->sap_spisize = prop->prop_localspi.spi_size;
+ sap->sap_transforms = prop->prop_nxforms;
+ saplength = sizeof(*sap);
+
+ switch (prop->prop_localspi.spi_size) {
+ case 4:
+ spi32 = htobe32(prop->prop_localspi.spi);
+ if (ibuf_add(buf, &spi32, sizeof(spi32)) != 0)
+ return (-1);
+ saplength += 4;
+ break;
+ case 8:
+ spi64 = htobe64(prop->prop_localspi.spi);
+ if (ibuf_add(buf, &spi64, sizeof(spi64)) != 0)
+ return (-1);
+ saplength += 8;
+ break;
+ default:
+ break;
+ }
+
+ for (i = 0; i < prop->prop_nxforms; i++) {
+ xform = prop->prop_xforms + i;
+
+ if ((ret = ikev2_add_transform(buf,
+ i == prop->prop_nxforms - 1 ?
+ IKEV2_XFORM_LAST : IKEV2_XFORM_MORE,
+ xform->xform_type, xform->xform_id,
+ xform->xform_length)) == -1)
+ return (-1);
+
+ saplength += ret;
+ }
+
+ sap->sap_length = htobe16(saplength);
+ length += saplength;
+ }
+
+ log_debug("%s: length %d", __func__, length);
+
+ return (length);
+}
+
+ssize_t
+ikev2_add_transform(struct ibuf *buf,
+ u_int8_t more, u_int8_t type, u_int16_t id, u_int16_t length)
+{
+ struct ikev2_transform *xfrm;
+ struct ikev2_attribute *attr;
+
+ if ((xfrm = ibuf_advance(buf, sizeof(*xfrm))) == NULL) {
+ log_debug("%s: failed to add transform", __func__);
+ return (-1);
+ }
+ xfrm->xfrm_more = more;
+ xfrm->xfrm_type = type;
+ xfrm->xfrm_id = htobe16(id);
+
+ if (length) {
+ xfrm->xfrm_length = htobe16(sizeof(*xfrm) + sizeof(*attr));
+
+ if ((attr = ibuf_advance(buf, sizeof(*attr))) == NULL) {
+ log_debug("%s: failed to add attribute", __func__);
+ return (-1);
+ }
+ attr->attr_type = htobe16(IKEV2_ATTRAF_TV |
+ IKEV2_ATTRTYPE_KEY_LENGTH);
+ attr->attr_length = htobe16(length);
+ } else
+ xfrm->xfrm_length = htobe16(sizeof(*xfrm));
+
+ return (betoh16(xfrm->xfrm_length));
+}
+
+int
+ikev2_add_data(struct ibuf *buf, void *data, size_t length)
+{
+ void *msgbuf;
+
+ if ((msgbuf = ibuf_advance(buf, length)) == NULL) {
+ log_debug("%s: failed", __func__);
+ return (-1);
+ }
+ memcpy(msgbuf, data, length);
+
+ return (0);
+}
+
+int
+ikev2_add_buf(struct ibuf *buf, struct ibuf *data)
+{
+ void *msgbuf;
+
+ if ((msgbuf = ibuf_advance(buf, ibuf_size(data))) == NULL) {
+ log_debug("%s: failed", __func__);
+ return (-1);
+ }
+ memcpy(msgbuf, ibuf_data(data), ibuf_size(data));
+
+ return (0);
+}
+
+struct ibuf *
+ikev2_message_init(struct iked *env, struct iked_message *msg,
+ struct sockaddr_storage *peer, socklen_t peerlen,
+ struct sockaddr_storage *local, socklen_t locallen, int response)
+{
+ bzero(msg, sizeof(*msg));
+ memcpy(&msg->msg_peer, peer, peerlen);
+ msg->msg_peerlen = peerlen;
+ memcpy(&msg->msg_local, local, locallen);
+ msg->msg_locallen = locallen;
+ msg->msg_response = response ? 1 : 0;
+ msg->msg_fd = -1;
+ msg->msg_data = ibuf_static();
+
+ return (msg->msg_data);
+}
+
+int
+ikev2_message_send(struct iked *env, int fd, struct iked_message *msg)
+{
+ struct ibuf *buf = msg->msg_data;
+ u_int32_t natt = 0x00000000;
+ struct ike_header *hdr;
+
+ if (buf == NULL || (hdr = ibuf_seek(msg->msg_data,
+ msg->msg_offset, sizeof(*hdr))) == NULL)
+ return (-1);
+
+ log_info("%s: %s to %s from %s", __func__,
+ print_map(hdr->ike_exchange, ikev2_exchange_map),
+ print_host(&msg->msg_peer, NULL, 0),
+ print_host(&msg->msg_local, NULL, 0));
+
+ if (msg->msg_natt || (msg->msg_sa && msg->msg_sa->sa_natt)) {
+ if (ibuf_prepend(buf, &natt, sizeof(natt)) == -1) {
+ log_debug("%s: failed to set NAT-T", __func__);
+ return (-1);
+ }
+ }
+ if ((sendto(fd, ibuf_data(buf), ibuf_size(buf), 0,
+ (struct sockaddr *)&msg->msg_peer, msg->msg_peerlen)) == -1) {
+ log_warn("%s: sendto", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+u_int32_t
+ikev2_message_id(struct iked *env, struct iked_sa *sa, int response)
+{
+ if (response)
+ return (sa->sa_msgid);
+
+ if (++sa->sa_msgid == UINT32_MAX) {
+ /* XXX we should close and renegotiate the connection now */
+ log_debug("%s: IKEv2 message sequence overflow", __func__);
+ }
+
+ return (sa->sa_msgid);
+}
+
+struct ibuf *
+ikev2_message_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src)
+{
+ size_t len, ivlen, encrlen, integrlen, blocklen,
+ outlen;
+ u_int8_t *buf, pad = 0, *ptr;
+ struct ibuf *integr, *encr, *dst = NULL, *out = NULL;
+
+ buf = ibuf_data(src);
+ len = ibuf_size(src);
+
+ log_debug("%s: decrypted length %d", __func__, len);
+ print_hex(buf, 0, len);
+
+ if (sa == NULL ||
+ sa->sa_encr == NULL ||
+ sa->sa_integr == NULL) {
+ log_debug("%s: invalid SA", __func__);
+ goto done;
+ }
+
+ if (sa->sa_hdr.sh_initiator) {
+ encr = sa->sa_key_iencr;
+ integr = sa->sa_key_iauth;
+ } else {
+ encr = sa->sa_key_rencr;
+ integr = sa->sa_key_rauth;
+ }
+
+ blocklen = cipher_length(sa->sa_encr);
+ ivlen = cipher_ivlength(sa->sa_encr);
+ integrlen = hash_length(sa->sa_integr);
+ encrlen = roundup(len + sizeof(pad), blocklen);
+ pad = encrlen - (len + sizeof(pad));
+
+ /*
+ * Pad the payload and encrypt it
+ */
+ if (pad) {
+ if ((ptr = ibuf_advance(src, pad)) == NULL)
+ goto done;
+ arc4random_buf(ptr, pad);
+ }
+ if (ibuf_add(src, &pad, sizeof(pad)) != 0)
+ goto done;
+
+ log_debug("%s: padded length %d", __func__, ibuf_size(src));
+ print_hex(ibuf_data(src), 0, ibuf_size(src));
+
+ cipher_setkey(sa->sa_encr, encr->buf, ibuf_length(encr));
+ cipher_setiv(sa->sa_encr, NULL, 0); /* new IV */
+ cipher_init_encrypt(sa->sa_encr);
+
+ if ((dst = ibuf_dup(sa->sa_encr->encr_iv)) == NULL)
+ goto done;
+
+ if ((out = ibuf_new(NULL,
+ cipher_outlength(sa->sa_encr, encrlen))) == NULL)
+ goto done;
+
+ outlen = ibuf_size(out);
+ cipher_update(sa->sa_encr,
+ ibuf_data(src), encrlen, ibuf_data(out), &outlen);
+
+ if (outlen && ibuf_add(dst, ibuf_data(out), outlen) != 0)
+ goto done;
+
+ outlen = cipher_outlength(sa->sa_encr, 0);
+ cipher_final(sa->sa_encr, out->buf, &outlen);
+ if (outlen)
+ ibuf_add(dst, out->buf, outlen);
+
+ if ((ptr = ibuf_advance(dst, integrlen)) == NULL)
+ goto done;
+ bzero(ptr, integrlen);
+
+ log_debug("%s: length %d, padding %d, output length %d",
+ __func__, len + sizeof(pad), pad, ibuf_size(dst));
+ print_hex(ibuf_data(dst), 0, ibuf_size(dst));
+
+ ibuf_release(src);
+ ibuf_release(out);
+ return (dst);
+ done:
+ ibuf_release(src);
+ ibuf_release(out);
+ ibuf_release(dst);
+ return (NULL);
+}
+
+int
+ikev2_message_integr(struct iked *env, struct iked_sa *sa, struct ibuf *src)
+{
+ int ret = -1;
+ size_t integrlen, tmplen;
+ struct ibuf *integr, *prf, *tmp = NULL;
+ u_int8_t *ptr;
+
+ log_debug("%s: message length %d", __func__, ibuf_size(src));
+ print_hex(ibuf_data(src), 0, ibuf_size(src));
+
+ if (sa == NULL ||
+ sa->sa_integr == NULL) {
+ log_debug("%s: invalid SA", __func__);
+ return (-1);
+ }
+
+ if (sa->sa_hdr.sh_initiator) {
+ integr = sa->sa_key_iauth;
+ prf = sa->sa_key_iprf;
+ } else {
+ integr = sa->sa_key_rauth;
+ prf = sa->sa_key_rprf;
+ }
+
+ integrlen = hash_length(sa->sa_integr);
+
+ log_debug("%s: integrity checksum length %d", __func__,
+ integrlen);
+
+ /*
+ * Validate packet checksum
+ */
+ if ((tmp = ibuf_new(NULL, hash_keylength(sa->sa_integr))) == NULL)
+ goto done;
+
+ hash_setkey(sa->sa_integr, ibuf_data(integr), ibuf_size(integr));
+ hash_init(sa->sa_integr);
+ hash_update(sa->sa_integr, ibuf_data(src),
+ ibuf_size(src) - integrlen);
+ hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen);
+
+ if (tmplen != integrlen) {
+ log_debug("%s: hash failure", __func__);
+ goto done;
+ }
+
+ if ((ptr = ibuf_seek(src,
+ ibuf_size(src) - integrlen, integrlen)) == NULL)
+ goto done;
+ memcpy(ptr, ibuf_data(tmp), tmplen);
+
+ print_hex(ibuf_data(tmp), 0, ibuf_size(tmp));
+
+ ret = 0;
+ done:
+ ibuf_release(tmp);
+
+ return (ret);
+}
+
+struct ibuf *
+ikev2_message_decrypt(struct iked *env, struct iked_sa *sa,
+ struct ibuf *msg, struct ibuf *src)
+{
+ size_t ivlen, encrlen, integrlen, blocklen,
+ outlen, tmplen;
+ u_int8_t pad, *ptr;
+ struct ibuf *integr, *encr, *tmp = NULL, *out = NULL;
+ off_t ivoff, encroff, integroff;
+
+ if (sa == NULL ||
+ sa->sa_encr == NULL ||
+ sa->sa_integr == NULL) {
+ log_debug("%s: invalid SA", __func__);
+ print_hex(ibuf_data(src), 0, ibuf_size(src));
+ goto done;
+ }
+
+ if (!sa->sa_hdr.sh_initiator) {
+ encr = sa->sa_key_iencr;
+ integr = sa->sa_key_iauth;
+ } else {
+ encr = sa->sa_key_rencr;
+ integr = sa->sa_key_rauth;
+ }
+
+ blocklen = cipher_length(sa->sa_encr);
+ ivlen = cipher_ivlength(sa->sa_encr);
+ ivoff = 0;
+ integrlen = hash_length(sa->sa_integr);
+ integroff = ibuf_size(src) - integrlen;
+ encroff = ivlen;
+ encrlen = ibuf_size(src) - integrlen - ivlen;
+
+ log_debug("%s: IV length %d", __func__, ivlen);
+ print_hex(ibuf_data(src), 0, ivlen);
+ log_debug("%s: encrypted payload length %d", __func__, encrlen);
+ print_hex(ibuf_data(src), encroff, encrlen);
+ log_debug("%s: integrity checksum length %d", __func__, integrlen);
+ print_hex(ibuf_data(src), integroff, integrlen);
+
+ /*
+ * Validate packet checksum
+ */
+ if ((tmp = ibuf_new(NULL, ibuf_length(integr))) == NULL)
+ goto done;
+
+ hash_setkey(sa->sa_integr, integr->buf, ibuf_length(integr));
+ hash_init(sa->sa_integr);
+ hash_update(sa->sa_integr, ibuf_data(msg),
+ ibuf_size(msg) - integrlen);
+ hash_final(sa->sa_integr, tmp->buf, &tmplen);
+
+ if (memcmp(tmp->buf, ibuf_data(src) + integroff, integrlen) != 0) {
+ log_debug("%s: integrity check failed", __func__);
+ goto done;
+ }
+
+ log_debug("%s: integrity check succeeded", __func__, tmplen);
+ print_hex(tmp->buf, 0, tmplen);
+
+ ibuf_release(tmp);
+ tmp = NULL;
+
+ /*
+ * Decrypt the payload and strip any padding
+ */
+ if ((encrlen % blocklen) != 0) {
+ log_debug("%s: unaligned encrypted payload", __func__);
+ goto done;
+ }
+
+ cipher_setkey(sa->sa_encr, encr->buf, ibuf_length(encr));
+ cipher_setiv(sa->sa_encr, ibuf_data(src) + ivoff, ivlen);
+ cipher_init_decrypt(sa->sa_encr);
+
+ if ((out = ibuf_new(NULL, cipher_outlength(sa->sa_encr,
+ encrlen))) == NULL)
+ goto done;
+
+ outlen = ibuf_length(out);
+ /* XXX why does it need encrlen + blocklen to work correctly? */
+ cipher_update(sa->sa_encr,
+ ibuf_data(src) + encroff, encrlen + blocklen,
+ ibuf_data(out), &outlen);
+ cipher_final(sa->sa_encr, ibuf_seek(out, outlen, blocklen), &tmplen);
+ if (tmplen)
+ outlen += tmplen;
+
+ /*
+ * XXX
+ * XXX the padding is wrong
+ * XXX
+ */
+ ptr = ibuf_seek(out, outlen - 1, 1);
+ pad = *ptr;
+
+ log_debug("%s: decrypted payload length %d/%d padding %d",
+ __func__, outlen, encrlen, pad);
+ print_hex(ibuf_data(out), 0, ibuf_size(out));
+
+ if (ibuf_setsize(out, outlen) != 0)
+ goto done;
+
+ ibuf_release(src);
+ return (out);
+ done:
+ ibuf_release(tmp);
+ ibuf_release(out);
+ ibuf_release(src);
+ return (NULL);
+}
+
+int
+ikev2_resp_ike_sa_init(struct iked *env, struct iked_message *msg)
+{
+ struct iked_message resp;
+ struct ike_header *hdr;
+ struct ikev2_payload *pld;
+ struct ikev2_cert *cert;
+ struct ikev2_keyexchange *ke;
+ struct ikev2_notify *n;
+ struct iked_sa *sa = msg->msg_sa;
+ struct ibuf *buf;
+ struct group *group;
+ u_int8_t *ptr;
+ ssize_t len;
+ int ret = -1;
+
+ if ((buf = ikev2_message_init(env, &resp,
+ &msg->msg_peer, msg->msg_peerlen,
+ &msg->msg_local, msg->msg_locallen, 1)) == NULL)
+ goto done;
+
+ /* IKE header */
+ if ((hdr = ikev2_add_header(buf, sa, 0,
+ IKEV2_PAYLOAD_SA, IKEV2_EXCHANGE_IKE_SA_INIT,
+ IKEV2_FLAG_RESPONSE)) == NULL)
+ goto done;
+
+ /* SA payload */
+ if ((pld = ikev2_add_payload(buf)) == NULL)
+ goto done;
+ if ((len = ikev2_add_proposals(buf, &sa->sa_proposals,
+ IKEV2_SAPROTO_IKE)) == -1)
+ goto done;
+
+ if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_KE) == -1)
+ goto done;
+
+ /* KE payload */
+ if ((pld = ikev2_add_payload(buf)) == NULL)
+ goto done;
+ if ((ke = ibuf_advance(buf, sizeof(*ke))) == NULL)
+ goto done;
+ if ((group = sa->sa_dhgroup) == NULL) {
+ log_debug("%s: invalid dh", __func__);
+ goto done;
+ }
+ ke->kex_dhgroup = htobe16(group->id);
+ if (ikev2_add_buf(buf, sa->sa_dhrexchange) == -1)
+ goto done;
+ len = sizeof(*ke) + dh_getlen(group);
+
+ if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONCE) == -1)
+ goto done;
+
+ /* NONCE payload */
+ if ((pld = ikev2_add_payload(buf)) == NULL)
+ goto done;
+ if (ikev2_add_buf(buf, sa->sa_rnonce) == -1)
+ goto done;
+ len = ibuf_size(sa->sa_rnonce);
+
+ if ((env->sc_opts & IKED_OPT_NONATT) == 0 &&
+ msg->msg_local.ss_family != AF_UNSPEC) {
+ if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NOTIFY) == -1)
+ goto done;
+
+ /* NAT-T notify payloads */
+ if ((pld = ikev2_add_payload(buf)) == NULL)
+ goto done;
+ if ((n = ibuf_advance(buf, sizeof(*n))) == NULL)
+ goto done;
+ n->n_type = htobe16(IKEV2_N_NAT_DETECTION_SOURCE_IP);
+ len = ikev2_nat_detection(msg, NULL, 0, 0, 1);
+ if ((ptr = ibuf_advance(buf, len)) == NULL)
+ goto done;
+ if ((len = ikev2_nat_detection(msg, ptr, len,
+ betoh16(n->n_type), 1)) == -1)
+ goto done;
+ len += sizeof(*n);
+
+ if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NOTIFY) == -1)
+ goto done;
+
+ if ((pld = ikev2_add_payload(buf)) == NULL)
+ goto done;
+ if ((n = ibuf_advance(buf, sizeof(*n))) == NULL)
+ goto done;
+ n->n_type = htobe16(IKEV2_N_NAT_DETECTION_DESTINATION_IP);
+ len = ikev2_nat_detection(msg, NULL, 0, 0, 1);
+ if ((ptr = ibuf_advance(buf, len)) == NULL)
+ goto done;
+ if ((len = ikev2_nat_detection(msg, ptr, len,
+ betoh16(n->n_type), 1)) == -1)
+ goto done;
+ len += sizeof(*n);
+ }
+
+ if (env->sc_certreqtype) {
+ if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_CERTREQ) == -1)
+ goto done;
+
+ /* CERTREQ payload */
+ if ((pld = ikev2_add_payload(buf)) == NULL)
+ goto done;
+ if ((cert = ibuf_advance(buf, sizeof(*cert))) == NULL)
+ goto done;
+ cert->cert_type = env->sc_certreqtype;
+ if (ikev2_add_buf(buf, env->sc_certreq) == -1)
+ goto done;
+ len = ibuf_size(env->sc_certreq) + sizeof(*cert);
+ }
+
+ if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONE) == -1)
+ goto done;
+
+ if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1)
+ goto done;
+
+ (void)ikev2_parse_message(env, hdr, &resp, 0);
+
+ ibuf_release(sa->sa_2ndmsg);
+ if ((sa->sa_2ndmsg = ibuf_dup(buf)) == NULL) {
+ log_debug("%s: failed to copy 2nd message", __func__);
+ goto done;
+ }
+
+ ret = ikev2_message_send(env, msg->msg_fd, &resp);
+
+ done:
+ message_cleanup(env, &resp);
+
+ return (ret);
+}
+
+int
+ikev2_resp_ike_auth(struct iked *env, struct iked_sa *sa)
+{
+ struct ikev2_payload *pld;
+ struct ikev2_notify *n;
+ struct ikev2_cert *cert;
+ struct ikev2_auth *auth;
+ struct iked_id *id, *certid;
+ struct ibuf *e = NULL;
+ u_int8_t firstpayload;
+ int ret = -1;
+ ssize_t len;
+
+ if (sa == NULL)
+ return (-1);
+
+ if (sa->sa_state == IKEV2_STATE_EAP)
+ return (ikev2_resp_ike_eap(env, sa, NULL));
+ else if (!sa_stateok(sa, IKEV2_STATE_VALID))
+ return (0); /* ignore */
+
+ if (ikev2_childsa_negotiate(env, sa, NULL) == -1)
+ return (-1);
+
+ /* New encrypted message buffer */
+ if ((e = ibuf_static()) == NULL)
+ goto done;
+
+ if (!sa->sa_localauth.id_type) {
+ /* Downgrade the state */
+ sa_state(env, sa, IKEV2_STATE_AUTH_SUCCESS);
+ }
+
+ if (!sa_stateok(sa, IKEV2_STATE_VALID)) {
+ /* Notify payload */
+ if ((pld = ikev2_add_payload(e)) == NULL)
+ goto done;
+ firstpayload = IKEV2_PAYLOAD_NOTIFY;
+
+ if ((n = ibuf_advance(e, sizeof(*n))) == NULL)
+ goto done;
+ n->n_protoid = IKEV2_SAPROTO_IKE; /* XXX ESP etc. */
+ n->n_spisize = 0;
+ n->n_type = htobe16(IKEV2_N_AUTHENTICATION_FAILED);
+ len = sizeof(*n);
+
+ goto send;
+ }
+
+ if (sa->sa_hdr.sh_initiator) {
+ id = &sa->sa_iid;
+ certid = &sa->sa_icert;
+ } else {
+ id = &sa->sa_rid;
+ certid = &sa->sa_rcert;
+ }
+
+ if (sa->sa_state != IKEV2_STATE_EAP_VALID) {
+ /* ID payload */
+ if ((pld = ikev2_add_payload(e)) == NULL)
+ goto done;
+ firstpayload = IKEV2_PAYLOAD_IDr;
+ if (ibuf_cat(e, id->id_buf) != 0)
+ goto done;
+ len = ibuf_size(id->id_buf);
+
+ /* CERT payload */
+ if ((sa->sa_staterequire & IKED_REQ_CERT) &&
+ (certid->id_type != IKEV2_CERT_NONE)) {
+ if (ikev2_next_payload(pld, len,
+ IKEV2_PAYLOAD_CERT) == -1)
+ goto done;
+
+ if ((pld = ikev2_add_payload(e)) == NULL)
+ goto done;
+ if ((cert = ibuf_advance(e, sizeof(*cert))) == NULL)
+ goto done;
+ cert->cert_type = certid->id_type;
+ if (ibuf_cat(e, certid->id_buf) != 0)
+ goto done;
+ len = ibuf_size(certid->id_buf) + sizeof(*cert);
+ }
+
+ if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_AUTH) == -1)
+ goto done;
+ } else
+ firstpayload = IKEV2_PAYLOAD_AUTH;
+
+ /* AUTH payload */
+ if ((pld = ikev2_add_payload(e)) == NULL)
+ goto done;
+ if ((auth = ibuf_advance(e, sizeof(*auth))) == NULL)
+ goto done;
+ auth->auth_method = sa->sa_localauth.id_type;
+ if (ibuf_cat(e, sa->sa_localauth.id_buf) != 0)
+ goto done;
+ len = ibuf_size(sa->sa_localauth.id_buf) + sizeof(*auth);
+
+ /* CP payload */
+ if (sa->sa_cp) {
+ if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_CP) == -1)
+ goto done;
+
+ if ((pld = ikev2_add_payload(e)) == NULL)
+ goto done;
+ if ((len = ikev2_add_cp(env, sa, e)) == -1)
+ goto done;
+ }
+
+ if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_SA) == -1)
+ goto done;
+
+ /* SA payload */
+ if ((pld = ikev2_add_payload(e)) == NULL)
+ goto done;
+ if ((len = ikev2_add_proposals(e, &sa->sa_proposals,
+ IKEV2_SAPROTO_ESP)) == -1)
+ goto done;
+
+ if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_TSi) == -1)
+ goto done;
+
+ /* TSi payload */
+ if ((pld = ikev2_add_payload(e)) == NULL)
+ goto done;
+ if ((len = ikev2_add_ts(e, IKEV2_PAYLOAD_TSi, sa)) == -1)
+ goto done;
+
+ if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_TSr) == -1)
+ goto done;
+
+ /* TSr payload */
+ if ((pld = ikev2_add_payload(e)) == NULL)
+ goto done;
+ if ((len = ikev2_add_ts(e, IKEV2_PAYLOAD_TSr, sa)) == -1)
+ goto done;
+
+ send:
+ if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONE) == -1)
+ goto done;
+
+ ret = ikev2_message_send_encrypt(env, sa, &e,
+ IKEV2_EXCHANGE_IKE_AUTH, firstpayload, 1);
+ if (ret == 0)
+ ret = ikev2_childsa_enable(env, sa);
+ if (ret == 0)
+ sa_state(env, sa, IKEV2_STATE_RUNNING);
+
+ done:
+ ibuf_release(e);
+
+ return (ret);
+}
+
+int
+ikev2_resp_ike_eap(struct iked *env, struct iked_sa *sa, struct ibuf *eapmsg)
+{
+ struct ikev2_payload *pld;
+ struct ikev2_cert *cert;
+ struct ikev2_auth *auth;
+ struct iked_id *id, *certid;
+ struct ibuf *e = NULL;
+ u_int8_t firstpayload;
+ int ret = -1;
+ ssize_t len = 0;
+
+ if (!sa_stateok(sa, IKEV2_STATE_EAP))
+ return (0);
+
+ /* Responder only */
+ if (sa->sa_hdr.sh_initiator)
+ return (-1);
+
+ /* New encrypted message buffer */
+ if ((e = ibuf_static()) == NULL)
+ goto done;
+
+ id = &sa->sa_rid;
+ certid = &sa->sa_rcert;
+
+ /* ID payload */
+ if ((pld = ikev2_add_payload(e)) == NULL)
+ goto done;
+ firstpayload = IKEV2_PAYLOAD_IDr;
+ if (ibuf_cat(e, id->id_buf) != 0)
+ goto done;
+ len = ibuf_size(id->id_buf);
+
+ if ((sa->sa_staterequire & IKED_REQ_CERT) &&
+ (certid->id_type != IKEV2_CERT_NONE)) {
+ if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_CERT) == -1)
+ goto done;
+
+ /* CERT payload */
+ if ((pld = ikev2_add_payload(e)) == NULL)
+ goto done;
+ if ((cert = ibuf_advance(e, sizeof(*cert))) == NULL)
+ goto done;
+ cert->cert_type = certid->id_type;
+ if (ibuf_cat(e, certid->id_buf) != 0)
+ goto done;
+ len = ibuf_size(certid->id_buf) + sizeof(*cert);
+ }
+
+ if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_AUTH) == -1)
+ goto done;
+
+ /* AUTH payload */
+ if ((pld = ikev2_add_payload(e)) == NULL)
+ goto done;
+ if ((auth = ibuf_advance(e, sizeof(*auth))) == NULL)
+ goto done;
+ auth->auth_method = sa->sa_localauth.id_type;
+ if (ibuf_cat(e, sa->sa_localauth.id_buf) != 0)
+ goto done;
+ len = ibuf_size(sa->sa_localauth.id_buf) + sizeof(*auth);
+
+ if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_EAP) == -1)
+ goto done;
+
+ /* EAP payload */
+ if ((pld = ikev2_add_payload(e)) == NULL)
+ goto done;
+ if ((len = eap_identity_request(e)) == -1)
+ goto done;
+
+ if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONE) == -1)
+ goto done;
+
+ ret = ikev2_message_send_encrypt(env, sa, &e,
+ IKEV2_EXCHANGE_IKE_AUTH, firstpayload, 1);
+
+ done:
+ ibuf_release(e);
+
+ return (ret);
+}
+
+int
+ikev2_send_ike_e(struct iked *env, struct iked_sa *sa, struct ibuf *buf,
+ u_int8_t firstpayload, u_int8_t exchange, int response)
+{
+ struct ikev2_payload *pld;
+ struct ibuf *e = NULL;
+ int ret = -1;
+
+ /* New encrypted message buffer */
+ if ((e = ibuf_static()) == NULL)
+ goto done;
+
+ if ((pld = ikev2_add_payload(e)) == NULL)
+ goto done;
+ if (ibuf_cat(e, buf) != 0)
+ goto done;
+
+ if (ikev2_next_payload(pld, ibuf_size(buf), IKEV2_PAYLOAD_NONE) == -1)
+ goto done;
+
+ ret = ikev2_message_send_encrypt(env, sa, &e,
+ exchange, firstpayload, response);
+
+ done:
+ ibuf_release(e);
+
+ return (ret);
+}
+
+int
+ikev2_resp_create_child_sa(struct iked *env, struct iked_message *msg)
+{
+ struct ikev2_payload *pld;
+ struct ibuf *e = NULL, *nonce = NULL;
+ u_int firstpayload;
+ int ret = -1;
+ ssize_t len = 0;
+ struct iked_sa *sa = msg->msg_sa;
+ struct iked_spi *rekey = &msg->msg_rekey;
+
+ if (msg->msg_response)
+ return (0);
+
+ log_debug("%s: rekey %s spi %s", __func__,
+ print_map(rekey->spi_protoid, ikev2_saproto_map),
+ print_spi(rekey->spi, rekey->spi_size));
+
+ if (rekey->spi_protoid == 0) {
+ /* Default to IKE_SA if REKEY_SA was not notified */
+ rekey->spi_protoid = IKEV2_SAPROTO_IKE;
+ if (sa->sa_hdr.sh_initiator)
+ rekey->spi = sa->sa_hdr.sh_rspi;
+ else
+ rekey->spi = sa->sa_hdr.sh_ispi;
+ rekey->spi_size = 8;
+ }
+
+ if (ikev2_childsa_negotiate(env, sa, rekey) == -1)
+ return (-1);
+
+ if (sa->sa_hdr.sh_initiator)
+ nonce = sa->sa_inonce;
+ else
+ nonce = sa->sa_rnonce;
+
+ if ((e = ibuf_static()) == NULL)
+ return (-1);
+
+ /* SA payload */
+ if ((pld = ikev2_add_payload(e)) == NULL)
+ goto done;
+ firstpayload = IKEV2_PAYLOAD_SA;
+ if ((len = ikev2_add_proposals(e, &sa->sa_proposals,
+ IKEV2_SAPROTO_ESP)) == -1)
+ goto done;
+
+ if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONCE) == -1)
+ goto done;
+
+ /* NONCE payload */
+ if ((pld = ikev2_add_payload(e)) == NULL)
+ goto done;
+ if (ikev2_add_buf(e, nonce) == -1)
+ goto done;
+ len = ibuf_size(nonce);
+
+ if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_TSi) == -1)
+ goto done;
+
+ /* TSi payload */
+ if ((pld = ikev2_add_payload(e)) == NULL)
+ goto done;
+ if ((len = ikev2_add_ts(e, IKEV2_PAYLOAD_TSi, sa)) == -1)
+ goto done;
+
+ if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_TSr) == -1)
+ goto done;
+
+ /* TSr payload */
+ if ((pld = ikev2_add_payload(e)) == NULL)
+ goto done;
+ if ((len = ikev2_add_ts(e, IKEV2_PAYLOAD_TSr, sa)) == -1)
+ goto done;
+
+ if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONE) == -1)
+ goto done;
+
+ ret = ikev2_message_send_encrypt(env, sa, &e,
+ IKEV2_EXCHANGE_CREATE_CHILD_SA, firstpayload, 1);
+ if (ret == 0)
+ ret = ikev2_childsa_enable(env, sa);
+
+ done:
+ ibuf_release(e);
+ return (ret);
+}
+
+int
+ikev2_send_informational(struct iked *env, struct iked_message *msg)
+{
+ struct iked_message resp;
+ struct ike_header *hdr;
+ struct ikev2_payload *pld;
+ struct ikev2_notify *n;
+ struct iked_sa *sa = msg->msg_sa, sah;
+ struct ibuf *buf, *e = NULL;
+ int ret = -1;
+
+ if (msg->msg_error == 0)
+ return (0);
+
+ if ((buf = ikev2_message_init(env, &resp,
+ &msg->msg_peer, msg->msg_peerlen,
+ &msg->msg_local, msg->msg_locallen, 0)) == NULL)
+ goto done;
+
+ /* New encrypted message buffer */
+ if ((e = ibuf_static()) == NULL)
+ goto done;
+
+ /* NOTIFY payload */
+ if ((pld = ikev2_add_payload(e)) == NULL)
+ goto done;
+
+ if ((n = ibuf_advance(e, sizeof(*n))) == NULL)
+ goto done;
+ n->n_protoid = IKEV2_SAPROTO_IKE; /* XXX ESP etc. */
+ n->n_spisize = 0;
+ n->n_type = htobe16(msg->msg_error);
+
+ switch (msg->msg_error) {
+ case IKEV2_N_INVALID_IKE_SPI:
+ case IKEV2_N_NO_PROPOSAL_CHOSEN:
+ break;
+ default:
+ log_debug("%s: unsupported notification %s", __func__,
+ print_map(msg->msg_error, ikev2_n_map));
+ goto done;
+ }
+
+ if (ikev2_next_payload(pld, sizeof(*n), IKEV2_PAYLOAD_NONE) == -1)
+ goto done;
+
+ if (sa != NULL && msg->msg_decrypted) {
+ /* IKE header */
+ if ((hdr = ikev2_add_header(buf, sa,
+ ikev2_message_id(env, sa, 0),
+ IKEV2_PAYLOAD_E, IKEV2_EXCHANGE_INFORMATIONAL,
+ 0)) == NULL)
+ goto done;
+
+ if ((pld = ikev2_add_payload(buf)) == NULL)
+ goto done;
+
+ /* Encrypt message and add as an E payload */
+ if ((e = ikev2_message_encrypt(env, sa, e)) == NULL) {
+ log_debug("%s: encryption failed", __func__);
+ goto done;
+ }
+ if (ibuf_cat(buf, e) != 0)
+ goto done;
+ if (ikev2_next_payload(pld, ibuf_size(e),
+ IKEV2_PAYLOAD_NOTIFY) == -1)
+ goto done;
+
+ if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1)
+ goto done;
+
+ /* Add integrity checksum (HMAC) */
+ if (ikev2_message_integr(env, sa, buf) != 0) {
+ log_debug("%s: integrity checksum failed", __func__);
+ goto done;
+ }
+ } else {
+ if ((hdr = ibuf_seek(msg->msg_data, 0, sizeof(*hdr))) == NULL)
+ goto done;
+
+ bzero(&sah, sizeof(sah));
+ sah.sa_hdr.sh_rspi = betoh64(hdr->ike_rspi);
+ sah.sa_hdr.sh_ispi = betoh64(hdr->ike_ispi);
+ sah.sa_hdr.sh_initiator =
+ hdr->ike_flags & IKEV2_FLAG_INITIATOR ? 0 : 1;
+ sa = &sah;
+
+ /* IKE header */
+ if ((hdr = ikev2_add_header(buf, &sah,
+ ikev2_message_id(env, &sah, 0),
+ IKEV2_PAYLOAD_NOTIFY, IKEV2_EXCHANGE_INFORMATIONAL,
+ 0)) == NULL)
+ goto done;
+ if (ibuf_cat(buf, e) != 0)
+ goto done;
+ if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1)
+ goto done;
+ }
+
+ resp.msg_data = buf;
+ resp.msg_sa = sa;
+ TAILQ_INIT(&resp.msg_proposals);
+
+ sa->sa_hdr.sh_initiator = sa->sa_hdr.sh_initiator ? 0 : 1;
+ (void)ikev2_parse_message(env, hdr, &resp, 0);
+ sa->sa_hdr.sh_initiator = sa->sa_hdr.sh_initiator ? 0 : 1;
+
+ ret = ikev2_message_send(env, msg->msg_fd, &resp);
+
+ done:
+ ibuf_release(e);
+ message_cleanup(env, &resp);
+
+ return (ret);
+}
+
+int
+ikev2_message_send_encrypt(struct iked *env, struct iked_sa *sa,
+ struct ibuf **ep, u_int8_t exchange, u_int8_t firstpayload, int response)
+{
+ struct iked_message resp;
+ struct ike_header *hdr;
+ struct ikev2_payload *pld;
+ struct ibuf *buf, *e = *ep;
+ int ret = -1;
+
+ if ((buf = ikev2_message_init(env, &resp,
+ &sa->sa_peer.addr, sa->sa_peer.addr.ss_len,
+ &sa->sa_local.addr, sa->sa_local.addr.ss_len, 1)) == NULL)
+ goto done;
+
+ /* IKE header */
+ if ((hdr = ikev2_add_header(buf, sa,
+ ikev2_message_id(env, sa, response),
+ IKEV2_PAYLOAD_E, exchange,
+ response ? IKEV2_FLAG_RESPONSE : 0)) == NULL)
+ goto done;
+
+ if ((pld = ikev2_add_payload(buf)) == NULL)
+ goto done;
+
+ /* Encrypt message and add as an E payload */
+ if ((e = ikev2_message_encrypt(env, sa, e)) == NULL) {
+ log_debug("%s: encryption failed", __func__);
+ goto done;
+ }
+ if (ibuf_cat(buf, e) != 0)
+ goto done;
+ if (ikev2_next_payload(pld, ibuf_size(e), firstpayload) == -1)
+ goto done;
+
+ if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1)
+ goto done;
+
+ /* Add integrity checksum (HMAC) */
+ if (ikev2_message_integr(env, sa, buf) != 0) {
+ log_debug("%s: integrity checksum failed", __func__);
+ goto done;
+ }
+
+ resp.msg_data = buf;
+ resp.msg_sa = sa;
+ TAILQ_INIT(&resp.msg_proposals);
+
+ sa->sa_hdr.sh_initiator = sa->sa_hdr.sh_initiator ? 0 : 1;
+ (void)ikev2_parse_message(env, hdr, &resp, 0);
+ sa->sa_hdr.sh_initiator = sa->sa_hdr.sh_initiator ? 0 : 1;
+
+ ret = ikev2_message_send(env, sa->sa_fd, &resp);
+
+ done:
+ /* e is cleaned up by the calling function */
+ *ep = e;
+ message_cleanup(env, &resp);
+
+ return (ret);
+}
+
+struct ibuf *
+ikev2_message_auth(struct iked *env, struct iked_sa *sa, int response)
+{
+ struct ibuf *authmsg = NULL, *nonce, *prfkey, *buf;
+ u_int8_t *ptr;
+ struct iked_id *id;
+ size_t tmplen;
+
+ /*
+ * Create the payload to be signed/MAC'ed for AUTH
+ */
+
+ if (!response) {
+ if ((nonce = sa->sa_rnonce) == NULL ||
+ (sa->sa_iid.id_type == 0) ||
+ (prfkey = sa->sa_key_iprf) == NULL ||
+ (buf = sa->sa_1stmsg) == NULL)
+ return (NULL);
+ id = &sa->sa_iid;
+ } else {
+ if ((nonce = sa->sa_inonce) == NULL ||
+ (sa->sa_rid.id_type == 0) ||
+ (prfkey = sa->sa_key_rprf) == NULL ||
+ (buf = sa->sa_2ndmsg) == NULL)
+ return (NULL);
+ id = &sa->sa_rid;
+ }
+
+ if ((authmsg = ibuf_dup(buf)) == NULL)
+ return (NULL);
+ if (ibuf_cat(authmsg, nonce) != 0)
+ goto fail;
+
+ if ((hash_setkey(sa->sa_prf, ibuf_data(prfkey),
+ ibuf_size(prfkey))) == NULL)
+ goto fail;
+
+ if ((ptr = ibuf_advance(authmsg,
+ hash_length(sa->sa_prf))) == NULL)
+ goto fail;
+
+ hash_init(sa->sa_prf);
+ hash_update(sa->sa_prf, ibuf_data(id->id_buf), ibuf_size(id->id_buf));
+ hash_final(sa->sa_prf, ptr, &tmplen);
+
+ if (tmplen != hash_length(sa->sa_prf))
+ goto fail;
+
+ log_debug("%s: %s auth data length %d",
+ __func__, response ? "responder" : "initiator",
+ ibuf_size(authmsg));
+ print_hex(ibuf_data(authmsg), 0, ibuf_size(authmsg));
+
+ return (authmsg);
+
+ fail:
+ ibuf_release(authmsg);
+ return (NULL);
+}
+
+int
+ikev2_parse_message(struct iked *env, struct ike_header *hdr,
+ struct iked_message *msg, off_t offset)
+{
+ log_debug("%s: header ispi %s rspi %s"
+ " nextpayload %s version 0x%02x exchange %s flags 0x%02x"
+ " msgid %d length %d response %d", __func__,
+ print_spi(betoh64(hdr->ike_ispi), 8),
+ print_spi(betoh64(hdr->ike_rspi), 8),
+ print_map(hdr->ike_nextpayload, ikev2_payload_map),
+ hdr->ike_version,
+ print_map(hdr->ike_exchange, ikev2_exchange_map),
+ hdr->ike_flags,
+ betoh32(hdr->ike_msgid),
+ betoh32(hdr->ike_length),
+ msg->msg_response);
+
+ if (ibuf_size(msg->msg_data) < betoh32(hdr->ike_length)) {
+ log_debug("%s: short message", __func__);
+ return (-1);
+ }
+
+ offset += sizeof(*hdr);
+
+ return (ikev2_parse_payloads(env, msg, offset,
+ betoh32(hdr->ike_length), hdr->ike_nextpayload, 0));
+}
+
+int
+ikev2_parse_payloads(struct iked *env, struct iked_message *msg,
+ off_t offset, size_t length, u_int payload, int quick)
+{
+ struct ikev2_payload pld;
+ u_int e;
+ int ret;
+ u_int8_t *msgbuf = ibuf_data(msg->msg_data);
+
+ /* Check if message was decrypted in an E payload */
+ e = msg->msg_decrypted ? IKED_E : 0;
+
+ if (quick)
+ print_debug("%s: %spayloads", __func__,
+ e ? "decrypted " : "");
+ else
+ ikev2_parse_payloads(env, msg, offset, length, payload, 1);
+
+ while (payload != 0 && offset < (off_t)length) {
+ memcpy(&pld, msgbuf + offset, sizeof(pld));
+
+ if (quick)
+ print_debug(" %s",
+ print_map(payload, ikev2_payload_map));
+ else
+ log_debug("%s: %spayload %s"
+ " nextpayload %s critical 0x%02x length %d",
+ __func__, e ? "decrypted " : "",
+ print_map(payload, ikev2_payload_map),
+ print_map(pld.pld_nextpayload, ikev2_payload_map),
+ pld.pld_reserved & IKEV2_CRITICAL_PAYLOAD,
+ betoh16(pld.pld_length));
+
+ offset += sizeof(pld);
+ ret = 0;
+
+ if (quick)
+ goto next;
+
+ switch (payload | e) {
+ case IKEV2_PAYLOAD_SA:
+ case IKEV2_PAYLOAD_SA | IKED_E:
+ ret = ikev2_parse_sa(env, &pld, msg, offset);
+ break;
+ case IKEV2_PAYLOAD_KE:
+ ret = ikev2_parse_ke(env, &pld, msg, offset);
+ break;
+ case IKEV2_PAYLOAD_IDi | IKED_E:
+ case IKEV2_PAYLOAD_IDr | IKED_E:
+ ret = ikev2_parse_id(env, &pld, msg, offset, payload);
+ break;
+ case IKEV2_PAYLOAD_CERT | IKED_E:
+ ret = ikev2_parse_cert(env, &pld, msg, offset);
+ break;
+ case IKEV2_PAYLOAD_CERTREQ:
+ case IKEV2_PAYLOAD_CERTREQ | IKED_E:
+ ret = ikev2_parse_certreq(env, &pld, msg, offset);
+ break;
+ case IKEV2_PAYLOAD_AUTH | IKED_E:
+ ret = ikev2_parse_auth(env, &pld, msg, offset);
+ break;
+ case IKEV2_PAYLOAD_NONCE:
+ case IKEV2_PAYLOAD_NONCE | IKED_E:
+ ret = ikev2_parse_nonce(env, &pld, msg, offset);
+ break;
+ case IKEV2_PAYLOAD_NOTIFY:
+ case IKEV2_PAYLOAD_NOTIFY | IKED_E:
+ ret = ikev2_parse_notify(env, &pld, msg, offset);
+ break;
+ case IKEV2_PAYLOAD_DELETE | IKED_E:
+ ret = ikev2_parse_delete(env, &pld, msg, offset);
+ break;
+ case IKEV2_PAYLOAD_TSi | IKED_E:
+ case IKEV2_PAYLOAD_TSr | IKED_E:
+ ret = ikev2_parse_ts(env, &pld, msg, offset, payload);
+ break;
+ case IKEV2_PAYLOAD_E:
+ ret = ikev2_parse_e(env, &pld, msg, offset);
+ break;
+ case IKEV2_PAYLOAD_CP | IKED_E:
+ ret = ikev2_parse_cp(env, &pld, msg, offset);
+ break;
+ case IKEV2_PAYLOAD_EAP | IKED_E:
+ ret = ikev2_parse_eap(env, &pld, msg, offset);
+ break;
+ default:
+ print_hex(msgbuf, offset,
+ betoh16(pld.pld_length) - sizeof(pld));
+ break;
+ }
+
+ if (ret != 0 && !msg->msg_response) {
+ (void)ikev2_send_informational(env, msg);
+ return (-1);
+ }
+
+ /* Encrypted payload must appear last */
+ if (payload == IKEV2_PAYLOAD_E)
+ return (0);
+
+ next:
+ payload = pld.pld_nextpayload;
+ offset += betoh16(pld.pld_length) - sizeof(pld);
+ }
+
+ if (quick)
+ print_debug("\n");
+
+ return (0);
+}
+
+int
+ikev2_parse_sa(struct iked *env, struct ikev2_payload *pld,
+ struct iked_message *msg, off_t offset)
+{
+ struct ikev2_sa_proposal sap;
+ struct iked_proposal *prop = NULL;
+ u_int32_t spi32;
+ u_int64_t spi = 0, spi64;
+ u_int8_t *msgbuf = ibuf_data(msg->msg_data);
+ struct iked_sa *sa = msg->msg_sa;
+
+ memcpy(&sap, msgbuf + offset, sizeof(sap));
+ offset += sizeof(sap);
+
+ if (sap.sap_spisize) {
+ switch (sap.sap_spisize) {
+ case 4:
+ memcpy(&spi32, msgbuf + offset, 4);
+ spi = betoh32(spi32);
+ break;
+ case 8:
+ memcpy(&spi64, msgbuf + offset, 8);
+ spi = betoh64(spi64);
+ break;
+ default:
+ log_debug("%s: unsupported SPI size %d",
+ __func__, sap.sap_spisize);
+ return (-1);
+ }
+
+ offset += sap.sap_spisize;
+ }
+
+ log_debug("%s: more %d reserved %d length %d"
+ " proposal #%d protoid %s spisize %d xforms %d spi %s",
+ __func__, sap.sap_more, sap.sap_reserved,
+ betoh16(sap.sap_length), sap.sap_proposalnr,
+ print_map(sap.sap_protoid, ikev2_saproto_map), sap.sap_spisize,
+ sap.sap_transforms, print_spi(spi, sap.sap_spisize));
+
+ if (!msg->msg_response) {
+ if ((msg->msg_prop = config_add_proposal(&msg->msg_proposals,
+ sap.sap_proposalnr, sap.sap_protoid)) == NULL) {
+ log_debug("%s: invalid proposal", __func__);
+ return (-1);
+ }
+ prop = msg->msg_prop;
+ prop->prop_localspi.spi_size = sap.sap_spisize;
+ prop->prop_peerspi.spi = spi;
+ }
+
+ /*
+ * Parse the attached transforms
+ */
+ if (ikev2_parse_xform(env, &sap, msg, offset) != 0) {
+ log_debug("%s: invalid proposal transforms", __func__);
+ return (-1);
+ }
+
+ if (msg->msg_response)
+ return (0);
+
+ /* XXX we need a better way to get this */
+ if (ikev2_sa_negotiate(sa,
+ &msg->msg_policy->pol_proposals,
+ &msg->msg_proposals, msg->msg_decrypted ?
+ IKEV2_SAPROTO_ESP : IKEV2_SAPROTO_IKE) != 0) {
+ log_debug("%s: no proposal chosen", __func__);
+ msg->msg_error = IKEV2_N_NO_PROPOSAL_CHOSEN;
+ return (-1);
+ } else if (sa_stateok(sa, IKEV2_STATE_SA_INIT))
+ sa_stateflags(sa, IKED_REQ_SA);
+
+ return (0);
+}
+
+int
+ikev2_parse_xform(struct iked *env, struct ikev2_sa_proposal *sap,
+ struct iked_message *msg, off_t offset)
+{
+ struct ikev2_transform xfrm;
+ char id[BUFSIZ];
+ u_int8_t *msgbuf = ibuf_data(msg->msg_data);
+
+ memcpy(&xfrm, msgbuf + offset, sizeof(xfrm));
+
+ switch (xfrm.xfrm_type) {
+ case IKEV2_XFORMTYPE_ENCR:
+ strlcpy(id, print_map(betoh16(xfrm.xfrm_id),
+ ikev2_xformencr_map), sizeof(id));
+ break;
+ case IKEV2_XFORMTYPE_PRF:
+ strlcpy(id, print_map(betoh16(xfrm.xfrm_id),
+ ikev2_xformprf_map), sizeof(id));
+ break;
+ case IKEV2_XFORMTYPE_INTEGR:
+ strlcpy(id, print_map(betoh16(xfrm.xfrm_id),
+ ikev2_xformauth_map), sizeof(id));
+ break;
+ case IKEV2_XFORMTYPE_DH:
+ strlcpy(id, print_map(betoh16(xfrm.xfrm_id),
+ ikev2_xformdh_map), sizeof(id));
+ break;
+ case IKEV2_XFORMTYPE_ESN:
+ strlcpy(id, print_map(betoh16(xfrm.xfrm_id),
+ ikev2_xformesn_map), sizeof(id));
+ break;
+ default:
+ snprintf(id, sizeof(id), "<%d>", betoh16(xfrm.xfrm_id));
+ break;
+ }
+
+ log_debug("%s: more %d reserved %d length %d"
+ " type %s id %s",
+ __func__, xfrm.xfrm_more, xfrm.xfrm_reserved,
+ betoh16(xfrm.xfrm_length),
+ print_map(xfrm.xfrm_type, ikev2_xformtype_map), id);
+
+ /*
+ * Parse transform attributes, if available
+ */
+ msg->msg_attrlength = 0;
+ if ((u_int)betoh16(xfrm.xfrm_length) > sizeof(xfrm))
+ ikev2_parse_attr(env, &xfrm, msg, offset + sizeof(xfrm),
+ betoh16(xfrm.xfrm_length) - sizeof(xfrm));
+
+ if (!msg->msg_response) {
+ if (config_add_transform(msg->msg_prop, xfrm.xfrm_type,
+ betoh16(xfrm.xfrm_id), msg->msg_attrlength,
+ msg->msg_attrlength) == NULL) {
+ log_debug("%s: failed to add transform", __func__);
+ return (-1);
+ }
+ }
+
+ /* Next transform */
+ offset += betoh16(xfrm.xfrm_length);
+ if (xfrm.xfrm_more == IKEV2_XFORM_MORE)
+ ikev2_parse_xform(env, sap, msg, offset);
+
+ return (0);
+}
+
+int
+ikev2_parse_attr(struct iked *env, struct ikev2_transform *xfrm,
+ struct iked_message *msg, off_t offset, int total)
+{
+ struct ikev2_attribute attr;
+ u_int type;
+ u_int8_t *msgbuf = ibuf_data(msg->msg_data);
+
+ memcpy(&attr, msgbuf + offset, sizeof(attr));
+
+ type = betoh16(attr.attr_type) & ~IKEV2_ATTRAF_TV;
+
+ log_debug("%s: attribute type %s length %d total %d",
+ __func__, print_map(type, ikev2_attrtype_map),
+ betoh16(attr.attr_length), total);
+
+ if (betoh16(attr.attr_type) & IKEV2_ATTRAF_TV) {
+ /* Type-Value attribute */
+ offset += sizeof(attr);
+ total -= sizeof(attr);
+
+ if (type == IKEV2_ATTRTYPE_KEY_LENGTH)
+ msg->msg_attrlength = betoh16(attr.attr_length);
+ } else {
+ /* Type-Length-Value attribute */
+ print_hex(msgbuf, offset + sizeof(attr),
+ betoh16(attr.attr_length) - sizeof(attr));
+ offset += betoh16(attr.attr_length);
+ total -= betoh16(attr.attr_length);
+ }
+
+ if (total > 0) {
+ /* Next attribute */
+ ikev2_parse_attr(env, xfrm, msg, offset, total);
+ }
+
+ return (0);
+}
+
+int
+ikev2_parse_ke(struct iked *env, struct ikev2_payload *pld,
+ struct iked_message *msg, off_t offset)
+{
+ struct ikev2_keyexchange kex;
+ u_int8_t *buf;
+ size_t len;
+ u_int8_t *msgbuf = ibuf_data(msg->msg_data);
+
+ memcpy(&kex, msgbuf + offset, sizeof(kex));
+
+ log_debug("%s: dh group %s reserved %d",
+ __func__,
+ print_map(betoh16(kex.kex_dhgroup), ikev2_xformdh_map),
+ betoh16(kex.kex_reserved));
+
+ buf = msgbuf + offset + sizeof(kex);
+ len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(kex);
+
+ print_hex(buf, 0, len);
+
+ if (!msg->msg_response) {
+ if ((msg->msg_sa->sa_dhiexchange =
+ ibuf_new(buf, len)) == NULL) {
+ log_debug("%s: failed to get exchange", __func__);
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+int
+ikev2_parse_id(struct iked *env, struct ikev2_payload *pld,
+ struct iked_message *msg, off_t offset, u_int payload)
+{
+ u_int8_t *ptr;
+ struct ikev2_id id;
+ size_t len;
+ struct iked_id *idp, idb;
+ struct iked_sa *sa = msg->msg_sa;
+ u_int8_t *msgbuf = ibuf_data(msg->msg_data);
+ struct ibuf *authmsg;
+ char idstr[IKED_ID_SIZE];
+
+ memcpy(&id, msgbuf + offset, sizeof(id));
+ bzero(&idb, sizeof(idb));
+
+ /* Don't strip the Id payload header */
+ ptr = msgbuf + offset;
+ len = betoh16(pld->pld_length) - sizeof(*pld);
+
+ idb.id_type = id.id_type;
+ if ((idb.id_buf = ibuf_new(ptr, len)) == NULL)
+ return (-1);
+
+ if (print_id(&idb, sizeof(id), idstr, sizeof(idstr)) == -1) {
+ log_debug("%s: malformed id", __func__);
+ return (-1);
+ }
+
+ log_debug("%s: id %s/%s length %d",
+ __func__, print_map(id.id_type, ikev2_id_map), idstr, len);
+
+ if (msg->msg_response) {
+ ibuf_release(idb.id_buf);
+ return (0);
+ }
+
+ if (sa->sa_hdr.sh_initiator && payload == IKEV2_PAYLOAD_IDr) {
+ idp = &sa->sa_rid;
+ } else if (!sa->sa_hdr.sh_initiator && payload == IKEV2_PAYLOAD_IDi) {
+ idp = &sa->sa_iid;
+ } else {
+ log_debug("%s: unexpected id payload", __func__);
+ return (0);
+ }
+
+ ibuf_release(idp->id_buf);
+ idp->id_buf = idb.id_buf;
+ idp->id_type = idb.id_type;
+
+ if ((authmsg = ikev2_message_auth(env, sa,
+ !sa->sa_hdr.sh_initiator)) == NULL) {
+ log_debug("%s: failed to get response auth data", __func__);
+ return (-1);
+ }
+
+ ca_setauth(env, sa, authmsg, PROC_CERT);
+
+ return (0);
+}
+
+int
+ikev2_parse_cert(struct iked *env, struct ikev2_payload *pld,
+ struct iked_message *msg, off_t offset)
+{
+ struct ikev2_cert cert;
+ u_int8_t *buf;
+ size_t len;
+ struct iked_sa *sa = msg->msg_sa;
+ struct iked_id *certid, *id;
+ u_int8_t *msgbuf = ibuf_data(msg->msg_data);
+
+ memcpy(&cert, msgbuf + offset, sizeof(cert));
+ offset += sizeof(cert);
+
+ buf = msgbuf + offset;
+ len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(cert);
+
+ log_debug("%s: type %s length %d",
+ __func__, print_map(cert.cert_type, ikev2_cert_map), len);
+
+ print_hex(buf, 0, len);
+
+ if (msg->msg_response)
+ return (0);
+
+ if (!sa->sa_hdr.sh_initiator && !msg->msg_response) {
+ certid = &sa->sa_icert;
+ id = &sa->sa_iid;
+ } else if (sa->sa_hdr.sh_initiator && msg->msg_response) {
+ certid = &sa->sa_rcert;
+ id = &sa->sa_rid;
+ } else
+ return (0); /* ignore */
+
+ if ((certid->id_buf = ibuf_new(buf, len)) == NULL) {
+ log_debug("%s: failed to save cert", __func__);
+ return (-1);
+ }
+ certid->id_type = cert.cert_type;
+
+ ca_setcert(env, &msg->msg_sa->sa_hdr, id, cert.cert_type,
+ buf, len, PROC_CERT);
+
+ return (0);
+}
+
+int
+ikev2_parse_certreq(struct iked *env, struct ikev2_payload *pld,
+ struct iked_message *msg, off_t offset)
+{
+ struct ikev2_cert cert;
+ u_int8_t *buf;
+ size_t len;
+ u_int8_t *msgbuf = ibuf_data(msg->msg_data);
+
+ memcpy(&cert, msgbuf + offset, sizeof(cert));
+ offset += sizeof(cert);
+
+ buf = msgbuf + offset;
+ len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(cert);
+
+ log_debug("%s: type %s signatures length %d",
+ __func__, print_map(cert.cert_type, ikev2_cert_map), len);
+ print_hex(buf, 0, len);
+
+ if (msg->msg_response)
+ return (0);
+
+ if (!len || (len % SHA_DIGEST_LENGTH) != 0) {
+ log_debug("%s: invalid certificate request", __func__);
+ return (-1);
+ }
+
+ if (msg->msg_sa == NULL)
+ return (-1);
+
+ /* Optional certreq for PSK */
+ msg->msg_sa->sa_staterequire |= IKED_REQ_CERT;
+
+ ca_setreq(env, &msg->msg_sa->sa_hdr, cert.cert_type,
+ buf, len, PROC_CERT);
+
+ return (0);
+}
+
+int
+ikev2_parse_auth(struct iked *env, struct ikev2_payload *pld,
+ struct iked_message *msg, off_t offset)
+{
+ struct ikev2_auth auth;
+ struct iked_auth ikeauth;
+ u_int8_t *buf;
+ size_t len;
+ struct ibuf *authmsg;
+ struct iked_sa *sa = msg->msg_sa;
+ struct iked_policy *policy = sa->sa_policy;
+ int ret = -1;
+ u_int8_t *msgbuf = ibuf_data(msg->msg_data);
+
+ memcpy(&auth, msgbuf + offset, sizeof(auth));
+ offset += sizeof(auth);
+
+ buf = msgbuf + offset;
+ len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(auth);
+
+ log_debug("%s: method %s length %d",
+ __func__, print_map(auth.auth_method, ikev2_auth_map), len);
+
+ print_hex(buf, 0, len);
+
+ if (msg->msg_response)
+ return (0);
+
+ memcpy(&ikeauth, &policy->pol_auth, sizeof(ikeauth));
+
+ if (policy->pol_auth.auth_eap && sa->sa_eapmsk != NULL) {
+ /* The initiator EAP auth is a PSK derived from the MSK */
+ ikeauth.auth_method = IKEV2_AUTH_SHARED_KEY_MIC;
+
+ /* Copy session key as PSK */
+ memcpy(ikeauth.auth_data, ibuf_data(sa->sa_eapmsk),
+ ibuf_size(sa->sa_eapmsk));
+ ikeauth.auth_length = ibuf_size(sa->sa_eapmsk);
+ }
+ if (auth.auth_method != ikeauth.auth_method) {
+ log_debug("%s: method %s required", __func__,
+ print_map(ikeauth.auth_method, ikev2_auth_map));
+ return (-1);
+ }
+
+ /* The AUTH payload indicates if the responder wants EAP or not */
+ if (!sa_stateok(sa, IKEV2_STATE_EAP))
+ sa_state(env, sa, IKEV2_STATE_AUTH_REQUEST);
+
+ if ((authmsg = ikev2_message_auth(env, sa,
+ sa->sa_hdr.sh_initiator)) == NULL) {
+ log_debug("%s: failed to get auth data", __func__);
+ return (-1);
+ }
+
+ ret = ikev2_message_authverify(env, sa, &ikeauth, buf, len,
+ authmsg);
+
+ ibuf_release(authmsg);
+ authmsg = NULL;
+
+ if (ret != 0)
+ goto done;
+
+ if (sa->sa_eapmsk != NULL) {
+ if ((authmsg = ikev2_message_auth(env, sa,
+ !sa->sa_hdr.sh_initiator)) == NULL) {
+ log_debug("%s: failed to get auth data", __func__);
+ return (-1);
+ }
+
+ /* 2nd AUTH for EAP messages */
+ if ((ret = ikev2_message_authsign(env, sa,
+ &ikeauth, authmsg)) != 0)
+ goto done;
+
+ sa_state(env, sa, IKEV2_STATE_EAP_VALID);
+ }
+
+ done:
+ ibuf_release(authmsg);
+
+ return (ret);
+}
+
+ssize_t
+ikev2_psk(struct iked_sa *sa, u_int8_t *data, size_t length,
+ u_int8_t **pskptr)
+{
+ u_int8_t *psk;
+ size_t psklen = -1;
+
+ if (hash_setkey(sa->sa_prf, data, length) == NULL)
+ return (-1);
+
+ if ((psk = calloc(1, hash_keylength(sa->sa_prf))) == NULL)
+ return (-1);
+
+ hash_init(sa->sa_prf);
+ hash_update(sa->sa_prf, IKEV2_KEYPAD, strlen(IKEV2_KEYPAD));
+ hash_final(sa->sa_prf, psk, &psklen);
+
+ *pskptr = psk;
+ return (psklen);
+}
+
+int
+ikev2_message_authverify(struct iked *env, struct iked_sa *sa,
+ struct iked_auth *auth, u_int8_t *buf, size_t len, struct ibuf *authmsg)
+{
+ u_int8_t *key, *psk = NULL;
+ ssize_t keylen;
+ struct iked_id *id;
+ struct iked_dsa *dsa = NULL;
+ int ret = -1;
+ u_int8_t keytype;
+
+ if (sa->sa_hdr.sh_initiator)
+ id = &sa->sa_rcert;
+ else
+ id = &sa->sa_icert;
+
+ if ((dsa = dsa_verify_new(auth->auth_method, sa->sa_prf)) == NULL) {
+ log_debug("%s: invalid auth method", __func__);
+ return (-1);
+ }
+
+ switch (auth->auth_method) {
+ case IKEV2_AUTH_SHARED_KEY_MIC:
+ if (!auth->auth_length) {
+ log_debug("%s: no pre-shared key found", __func__);
+ goto done;
+ }
+ if ((keylen = ikev2_psk(sa, auth->auth_data,
+ auth->auth_length, &psk)) == -1) {
+ log_debug("%s: failed to get PSK", __func__);
+ goto done;
+ }
+ key = psk;
+ keytype = 0;
+ break;
+ default:
+ if (id == NULL) {
+ log_debug("%s: no cert found", __func__);
+ goto done;
+ }
+ key = ibuf_data(id->id_buf);
+ keylen = ibuf_size(id->id_buf);
+ keytype = id->id_type;
+ break;
+ }
+
+ log_debug("%s: method %s keylen %d type %s", __func__,
+ print_map(auth->auth_method, ikev2_auth_map), keylen,
+ print_map(id->id_type, ikev2_cert_map));
+
+ if (dsa_setkey(dsa, key, keylen, keytype) == NULL ||
+ dsa_init(dsa) != 0 ||
+ dsa_update(dsa, ibuf_data(authmsg), ibuf_size(authmsg))) {
+ log_debug("%s: failed to compute digital signature", __func__);
+ goto done;
+ }
+
+ if ((ret = dsa_verify_final(dsa, buf, len)) == 0) {
+ log_debug("%s: authentication successful", __func__);
+ sa_state(env, sa, IKEV2_STATE_AUTH_SUCCESS);
+
+ if (!sa->sa_policy->pol_auth.auth_eap &&
+ auth->auth_method == IKEV2_AUTH_SHARED_KEY_MIC)
+ sa_state(env, sa, IKEV2_STATE_VALID);
+ } else {
+ log_debug("%s: authentication failed", __func__);
+ sa_state(env, sa, IKEV2_STATE_AUTH_REQUEST);
+ }
+
+ done:
+ if (psk != NULL)
+ free(psk);
+ dsa_free(dsa);
+
+ return (ret);
+}
+
+int
+ikev2_message_authsign(struct iked *env, struct iked_sa *sa,
+ struct iked_auth *auth, struct ibuf *authmsg)
+{
+ u_int8_t *key, *psk = NULL;
+ ssize_t keylen;
+ struct iked_hash *prf = sa->sa_prf;
+ struct iked_id *id;
+ struct iked_dsa *dsa = NULL;
+ struct ibuf *buf;
+ int ret = -1;
+ u_int8_t keytype;
+
+ if (sa->sa_hdr.sh_initiator)
+ id = &sa->sa_icert;
+ else
+ id = &sa->sa_rcert;
+
+ if ((dsa = dsa_sign_new(auth->auth_method, prf)) == NULL) {
+ log_debug("%s: invalid auth method", __func__);
+ return (-1);
+ }
+
+ switch (auth->auth_method) {
+ case IKEV2_AUTH_SHARED_KEY_MIC:
+ if (!auth->auth_length) {
+ log_debug("%s: no pre-shared key found", __func__);
+ goto done;
+ }
+ if ((keylen = ikev2_psk(sa, auth->auth_data,
+ auth->auth_length, &psk)) == -1) {
+ log_debug("%s: failed to get PSK", __func__);
+ goto done;
+ }
+ key = psk;
+ keytype = 0;
+ break;
+ default:
+ if (id == NULL) {
+ log_debug("%s: no cert found", __func__);
+ goto done;
+ }
+ key = ibuf_data(id->id_buf);
+ keylen = ibuf_size(id->id_buf);
+ keytype = id->id_type;
+ break;
+ }
+
+ if (dsa_setkey(dsa, key, keylen, keytype) == NULL ||
+ dsa_init(dsa) != 0 ||
+ dsa_update(dsa, ibuf_data(authmsg), ibuf_size(authmsg))) {
+ log_debug("%s: failed to compute digital signature", __func__);
+ goto done;
+ }
+
+ ibuf_release(sa->sa_localauth.id_buf);
+ sa->sa_localauth.id_buf = NULL;
+
+ if ((buf = ibuf_new(NULL, dsa_length(dsa))) == NULL) {
+ log_debug("%s: failed to get auth buffer", __func__);
+ goto done;
+ }
+
+ if ((ret = dsa_sign_final(dsa,
+ ibuf_data(buf), ibuf_size(buf))) == -1) {
+ log_debug("%s: failed to create auth signature", __func__);
+ ibuf_release(buf);
+ goto done;
+ }
+
+ sa->sa_localauth.id_type = auth->auth_method;
+ sa->sa_localauth.id_buf = buf;
+
+ ret = 0;
+ done:
+ if (psk != NULL)
+ free(psk);
+ dsa_free(dsa);
+
+ return (ret);
+}
+
+int
+ikev2_parse_nonce(struct iked *env, struct ikev2_payload *pld,
+ struct iked_message *msg, off_t offset)
+{
+ size_t len;
+ u_int8_t *buf;
+ u_int8_t *msgbuf = ibuf_data(msg->msg_data);
+ struct iked_sa *sa = msg->msg_sa;
+ struct ibuf *localnonce, *peernonce;
+
+ buf = msgbuf + offset;
+ len = betoh16(pld->pld_length) - sizeof(*pld);
+ print_hex(buf, 0, len);
+
+ if (!msg->msg_response) {
+ if ((peernonce = ibuf_new(buf, len)) == NULL) {
+ log_debug("%s: failed to get peer nonce", __func__);
+ return (-1);
+ }
+ if ((localnonce =
+ ibuf_random(IKED_NONCE_SIZE)) == NULL) {
+ log_debug("%s: failed to get local nonce", __func__);
+ ibuf_release(peernonce);
+ return (-1);
+ }
+
+ ibuf_release(sa->sa_inonce);
+ ibuf_release(sa->sa_rnonce);
+
+ log_debug("%s: updating nonces", __func__);
+
+ if (sa->sa_hdr.sh_initiator) {
+ sa->sa_inonce = localnonce;
+ sa->sa_rnonce = peernonce;
+ } else {
+ sa->sa_inonce = peernonce;
+ sa->sa_rnonce = localnonce;
+ }
+ }
+
+ return (0);
+}
+
+int
+ikev2_parse_notify(struct iked *env, struct ikev2_payload *pld,
+ struct iked_message *msg, off_t offset)
+{
+ struct ikev2_notify *n;
+ u_int8_t *buf, md[SHA_DIGEST_LENGTH];
+ size_t len;
+ u_int16_t type;
+ u_int32_t spi32;
+ u_int64_t spi64;
+ struct iked_spi *rekey;
+
+ if ((n = ibuf_seek(msg->msg_data, offset, sizeof(*n))) == NULL)
+ return (-1);
+ type = betoh16(n->n_type);
+
+ log_debug("%s: protoid %s spisize %d type %s",
+ __func__,
+ print_map(n->n_protoid, ikev2_saproto_map), n->n_spisize,
+ print_map(type, ikev2_n_map));
+
+ len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(*n);
+ if ((buf = ibuf_seek(msg->msg_data, offset + sizeof(*n), len)) == NULL)
+ return (-1);
+
+ print_hex(buf, 0, len);
+
+ switch (type) {
+ case IKEV2_N_NAT_DETECTION_SOURCE_IP:
+ case IKEV2_N_NAT_DETECTION_DESTINATION_IP:
+ if (ikev2_nat_detection(msg, md, sizeof(md), type,
+ msg->msg_response) == -1)
+ return (-1);
+ if (len != sizeof(md) || memcmp(buf, md, len) != 0) {
+ log_debug("%s: %s detected NAT, enabling "
+ "UDP encapsulation", __func__,
+ print_map(type, ikev2_n_map));
+
+ /*
+ * Enable UDP encapsulation of ESP packages if
+ * the check detected NAT and and if we received
+ * the IKE message on the NAT-T port.
+ */
+ if (msg->msg_sa != NULL && msg->msg_sa->sa_natt)
+ msg->msg_sa->sa_udpencap = 1;
+ }
+ print_hex(md, 0, sizeof(md));
+ break;
+ case IKEV2_N_REKEY_SA:
+ if (len != n->n_spisize) {
+ log_debug("%s: malformed notification", __func__);
+ return (-1);
+ }
+ if (msg->msg_decrypted)
+ rekey = &msg->msg_decrypted->msg_rekey;
+ else
+ rekey = &msg->msg_rekey;
+ if (rekey->spi != 0) {
+ log_debug("%s: rekeying of multiple SAs not supported",
+ __func__);
+ return (-1);
+ }
+ switch (n->n_spisize) {
+ case 4:
+ memcpy(&spi32, buf, len);
+ rekey->spi = betoh32(spi32);
+ break;
+ case 8:
+ memcpy(&spi64, buf, len);
+ rekey->spi = betoh64(spi64);
+ break;
+ default:
+ log_debug("%s: invalid spi size %d", __func__,
+ n->n_spisize);
+ return (-1);
+ }
+ rekey->spi_size = n->n_spisize;
+ rekey->spi_protoid = n->n_protoid;
+
+ log_debug("%s: rekey %s spi %s", __func__,
+ print_map(n->n_protoid, ikev2_saproto_map),
+ print_spi(rekey->spi, n->n_spisize));
+ break;
+ }
+
+ return (0);
+}
+
+int
+ikev2_parse_delete(struct iked *env, struct ikev2_payload *pld,
+ struct iked_message *msg, off_t offset)
+{
+ struct ikev2_delete *del, *localdel;
+ u_int64_t spi64, spi = 0, *localspi = NULL;
+ u_int32_t spi32;
+ size_t len, i, cnt, sz, found = 0, failed = 0;
+ u_int8_t *buf, firstpayload = 0;
+ u_int8_t *msgbuf = ibuf_data(msg->msg_data);
+ struct iked_sa *sa = msg->msg_sa;
+ struct ibuf *resp = NULL;
+ int ret = -1;
+
+ if ((del = ibuf_seek(msg->msg_data, offset, sizeof(*del))) == NULL)
+ return (-1);
+ cnt = betoh16(del->del_nspi);
+ sz = del->del_spisize;
+
+ log_debug("%s: protoid %s spisize %d nspi %d",
+ __func__, print_map(del->del_protoid, ikev2_saproto_map),
+ sz, cnt);
+
+ buf = msgbuf + offset + sizeof(*del);
+ len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(*del);
+
+ print_hex(buf, 0, len);
+
+ switch (sz) {
+ case 4:
+ case 8:
+ break;
+ default:
+ if (!msg->msg_response &&
+ del->del_protoid == IKEV2_SAPROTO_IKE) {
+ sa_state(env, sa, IKEV2_STATE_DELETE);
+ return (0);
+ }
+ log_debug("%s: invalid SPI size", __func__);
+ return (-1);
+ }
+
+ if ((len / sz) != cnt) {
+ log_debug("%s: invalid payload length %d/%d != %d",
+ __func__, len, sz, cnt);
+ return (-1);
+ }
+
+ if (!msg->msg_response &&
+ (localspi = calloc(cnt, sizeof(u_int64_t))) == NULL) {
+ log_warn("%s", __func__);
+ return (-1);
+ }
+
+ for (i = 0; i < cnt; i++) {
+ /* XXX delete SAs */
+ switch (sz) {
+ case 4:
+ memcpy(&spi32, buf + (i * sz), sizeof(spi32));
+ spi = betoh32(spi32);
+ break;
+ case 8:
+ memcpy(&spi64, buf + (i * sz), sizeof(spi64));
+ spi = betoh64(spi64);
+ break;
+ }
+ if (msg->msg_response) {
+ log_debug("%s: spi %s", __func__, print_spi(spi, sz));
+ continue;
+ }
+
+ if (ikev2_childsa_delete(env, sa,
+ del->del_protoid, spi, &localspi[i], 0) == -1)
+ failed++;
+ else
+ found++;
+ }
+
+ if (msg->msg_response)
+ return (0);
+
+ if ((resp = ibuf_static()) == NULL)
+ goto done;
+
+ if (found) {
+ if ((localdel = ibuf_advance(resp, sizeof(*localdel))) == NULL)
+ goto done;
+
+ firstpayload = IKEV2_PAYLOAD_DELETE;
+ localdel->del_protoid = del->del_protoid;
+ localdel->del_spisize = del->del_spisize;
+ localdel->del_nspi = htobe16(found);
+
+ for (i = 0; i < cnt; i++) {
+ if (!localspi[i])
+ continue;
+
+ switch (sz) {
+ case 4:
+ spi32 = htobe32(localspi[i]);
+ if (ibuf_add(resp, &spi32, sizeof(spi32)) != 0)
+ goto done;
+ break;
+ case 8:
+ spi64 = htobe64(localspi[i]);
+ if (ibuf_add(resp, &spi64, sizeof(spi64)) != 0)
+ goto done;
+ break;
+ }
+ }
+
+ }
+
+ if (found) {
+ ret = ikev2_send_ike_e(env, sa, resp,
+ firstpayload, IKEV2_EXCHANGE_INFORMATIONAL, 1);
+ } else {
+ /* XXX should we send an INVALID_SPI notification? */
+ ret = 0;
+ }
+
+ done:
+ if (localspi != NULL)
+ free(localspi);
+ ibuf_release(resp);
+ return (ret);
+}
+
+int
+ikev2_parse_ts(struct iked *env, struct ikev2_payload *pld,
+ struct iked_message *msg, off_t offset, u_int payload)
+{
+ u_int8_t *ptr;
+ struct ikev2_tsp tsp;
+ struct ikev2_ts ts;
+ size_t len, i;
+ struct sockaddr_in s4;
+ struct sockaddr_in6 s6;
+ u_int8_t buf[2][128];
+ u_int8_t *msgbuf = ibuf_data(msg->msg_data);
+
+ memcpy(&tsp, msgbuf + offset, sizeof(tsp));
+ offset += sizeof(tsp);
+
+ ptr = msgbuf + offset;
+ len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(tsp);
+
+ log_debug("%s: count %d length %d", __func__,
+ tsp.tsp_count, len);
+
+ for (i = 0; i < tsp.tsp_count; i++) {
+ memcpy(&ts, msgbuf + offset, sizeof(ts));
+
+ log_debug("%s: type %s protoid %u length %d "
+ "startport %u endport %u", __func__,
+ print_map(ts.ts_type, ikev2_ts_map),
+ ts.ts_protoid, betoh16(ts.ts_length),
+ betoh16(ts.ts_startport),
+ betoh16(ts.ts_endport));
+
+ switch (ts.ts_type) {
+ case IKEV2_TS_IPV4_ADDR_RANGE:
+ bzero(&s4, sizeof(s4));
+ s4.sin_family = AF_INET;
+ s4.sin_len = sizeof(s4);
+ memcpy(&s4.sin_addr.s_addr,
+ msgbuf + offset + sizeof(ts), 4);
+ print_host((struct sockaddr_storage *)&s4,
+ (char *)buf[0], sizeof(buf[0]));
+ memcpy(&s4.sin_addr.s_addr,
+ msgbuf + offset + sizeof(ts) + 4, 4);
+ print_host((struct sockaddr_storage *)&s4,
+ (char *)buf[1], sizeof(buf[1]));
+ log_debug("%s: start %s end %s", __func__,
+ buf[0], buf[1]);
+ break;
+ case IKEV2_TS_IPV6_ADDR_RANGE:
+ bzero(&s6, sizeof(s6));
+ s6.sin6_family = AF_INET6;
+ s6.sin6_len = sizeof(s6);
+ memcpy(&s6.sin6_addr,
+ msgbuf + offset + sizeof(ts), 16);
+ print_host((struct sockaddr_storage *)&s6,
+ (char *)buf[0], sizeof(buf[0]));
+ memcpy(&s6.sin6_addr,
+ msgbuf + offset + sizeof(ts) + 16, 16);
+ print_host((struct sockaddr_storage *)&s6,
+ (char *)buf[1], sizeof(buf[1]));
+ log_debug("%s: start %s end %s", __func__,
+ buf[0], buf[1]);
+ break;
+ default:
+ break;
+ }
+
+ offset += betoh16(ts.ts_length);
+ }
+
+ return (0);
+}
+
+int
+ikev2_parse_e(struct iked *env, struct ikev2_payload *pld,
+ struct iked_message *msg, off_t offset)
+{
+ struct ibuf *e = NULL;
+ u_int8_t *msgbuf = ibuf_data(msg->msg_data);
+ struct iked_message emsg;
+ u_int8_t *buf;
+ size_t len;
+ int ret = -1;
+
+ buf = msgbuf + offset;
+ len = betoh16(pld->pld_length) - sizeof(*pld);
+
+ if ((e = ibuf_new(buf, len)) == NULL)
+ goto done;
+
+ if ((e = ikev2_message_decrypt(env, msg->msg_sa,
+ msg->msg_data, e)) == NULL)
+ goto done;
+
+ /*
+ * Parse decrypted payload
+ */
+ bzero(&emsg, sizeof(emsg));
+ memcpy(&emsg, msg, sizeof(*msg));
+ emsg.msg_data = e;
+ emsg.msg_decrypted = msg;
+ TAILQ_INIT(&emsg.msg_proposals);
+
+ ret = ikev2_parse_payloads(env, &emsg, 0, ibuf_size(e),
+ pld->pld_nextpayload, 0);
+
+ done:
+ ibuf_release(e);
+
+ return (ret);
+}
+
+int
+ikev2_parse_cp(struct iked *env, struct ikev2_payload *pld,
+ struct iked_message *msg, off_t offset)
+{
+ struct ikev2_cp cp;
+ struct ikev2_cfg *cfg;
+ u_int8_t *buf;
+ size_t len, i;
+ u_int8_t *msgbuf = ibuf_data(msg->msg_data);
+ struct iked_sa *sa = msg->msg_sa;
+
+ memcpy(&cp, msgbuf + offset, sizeof(cp));
+ offset += sizeof(cp);
+
+ buf = msgbuf + offset;
+ len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(cp);
+
+ log_debug("%s: type %s",
+ __func__, print_map(cp.cp_type, ikev2_cp_map), len);
+ print_hex(buf, 0, len);
+
+ for (i = 0; i < len;) {
+ cfg = (struct ikev2_cfg *)(buf + i);
+
+ log_debug("%s: %s 0x%04x length %d", __func__,
+ print_map(betoh16(cfg->cfg_type), ikev2_cfg_map),
+ betoh16(cfg->cfg_type),
+ betoh16(cfg->cfg_length));
+
+ i += betoh16(cfg->cfg_length) + sizeof(*cfg);
+ }
+
+ if (msg->msg_response)
+ return (0);
+
+ if (sa)
+ sa->sa_cp = cp.cp_type;
+
+ return (0);
+}
+
+int
+ikev2_parse_eap(struct iked *env, struct ikev2_payload *pld,
+ struct iked_message *msg, off_t offset)
+{
+ struct eap_header *hdr;
+ struct eap_message *eap = NULL;
+ struct iked_sa *sa = msg->msg_sa;
+ size_t len;
+
+ if ((hdr = ibuf_seek(msg->msg_data, offset, sizeof(*hdr))) == NULL) {
+ log_debug("%s: failed to get EAP header", __func__);
+ return (-1);
+ }
+
+ len = betoh16(hdr->eap_length);
+
+ if (len < sizeof(*eap)) {
+ log_info("%s: %s id %d length %d", __func__,
+ print_map(hdr->eap_code, eap_code_map),
+ hdr->eap_id, betoh16(hdr->eap_length));
+ } else {
+ /* Now try to get the indicated length */
+ if ((eap = ibuf_seek(msg->msg_data, offset, len)) == NULL) {
+ log_debug("%s: invalid EAP length", __func__);
+ return (-1);
+ }
+
+ log_info("%s: %s id %d length %d EAP-%s", __func__,
+ print_map(eap->eap_code, eap_code_map),
+ eap->eap_id, betoh16(eap->eap_length),
+ print_map(eap->eap_type, eap_type_map));
+ }
+
+ if (eap_parse(env, sa, hdr, msg->msg_response) == -1)
+ return (-1);
+
+ return (0);
+}
+
+int
+ikev2_sa_negotiate(struct iked_sa *sa, struct iked_proposals *local,
+ struct iked_proposals *peer, u_int8_t protoid)
+{
+ struct iked_proposal *ppeer = NULL, *plocal, *prop, values;
+ struct iked_transform *tpeer, *tlocal;
+ struct iked_transform *match[IKEV2_XFORMTYPE_MAX];
+ struct iked_transform *chosen[IKEV2_XFORMTYPE_MAX];
+ u_int type, i, j, match_score, chosen_score = 0;
+
+ bzero(chosen, sizeof(chosen));
+ bzero(&values, sizeof(values));
+
+ TAILQ_FOREACH(plocal, local, prop_entry) {
+ if (plocal->prop_protoid != protoid)
+ continue;
+
+ TAILQ_FOREACH(ppeer, peer, prop_entry) {
+ bzero(match, sizeof(match));
+ match_score = 0;
+
+ if (ppeer->prop_protoid != plocal->prop_protoid)
+ continue;
+
+ for (i = 0; i < ppeer->prop_nxforms; i++) {
+ tpeer = ppeer->prop_xforms + i;
+ for (j = 0; j < plocal->prop_nxforms; j++) {
+ tlocal = plocal->prop_xforms + j;
+ if (!(tpeer->xform_type ==
+ tlocal->xform_type &&
+ tpeer->xform_id ==
+ tlocal->xform_id &&
+ tpeer->xform_length ==
+ tlocal->xform_length))
+ continue;
+ if (tpeer->xform_type >
+ IKEV2_XFORMTYPE_MAX)
+ continue;
+ type = tpeer->xform_type;
+
+ if (match[type] == NULL ||
+ tlocal->xform_score <
+ match[type]->xform_score) {
+ match[type] = tlocal;
+ } else
+ continue;
+
+ print_debug("%s: xform %d "
+ "<-> %d (%d): %s %s (keylength %d <-> %d)", __func__,
+ ppeer->prop_id, plocal->prop_id,
+ tlocal->xform_score,
+ print_map(type,
+ ikev2_xformtype_map),
+ print_map(tpeer->xform_id,
+ tpeer->xform_map),
+ tpeer->xform_keylength,
+ tlocal->xform_keylength);
+ if (tpeer->xform_length)
+ print_debug(" %d",
+ tpeer->xform_length);
+ print_debug("\n");
+ }
+ }
+
+ for (i = match_score = 0;
+ i < IKEV2_XFORMTYPE_MAX; i++) {
+ if (protoid == IKEV2_SAPROTO_IKE &&
+ (match[i] == NULL) && (
+ (i == IKEV2_XFORMTYPE_ENCR) ||
+ (i == IKEV2_XFORMTYPE_PRF) ||
+ (i == IKEV2_XFORMTYPE_INTEGR) ||
+ (i == IKEV2_XFORMTYPE_DH))) {
+ match_score = 0;
+ break;
+ } else if (protoid != IKEV2_SAPROTO_IKE &&
+ (match[i] == NULL) && (
+ (i == IKEV2_XFORMTYPE_ENCR) ||
+ (i == IKEV2_XFORMTYPE_INTEGR) ||
+ (i == IKEV2_XFORMTYPE_ESN))) {
+ match_score = 0;
+ break;
+ } else if (match[i] == NULL)
+ continue;
+
+ match_score += match[i]->xform_score;
+ }
+
+ log_debug("%s: score %d", __func__, match_score);
+ if (match_score != 0 &&
+ (chosen_score == 0 || match_score < chosen_score)) {
+ chosen_score = match_score;
+ for (i = 0; i < IKEV2_XFORMTYPE_MAX; i++)
+ chosen[i] = match[i];
+ memcpy(&values, ppeer, sizeof(values));
+ }
+ }
+ if (chosen_score != 0)
+ break;
+ }
+
+ if (chosen_score == 0)
+ return (-1);
+ else if (sa == NULL)
+ return (0);
+
+ (void)config_free_proposals(&sa->sa_proposals, protoid);
+ prop = config_add_proposal(&sa->sa_proposals, values.prop_id, protoid);
+
+ if (values.prop_localspi.spi_size) {
+ prop->prop_peerspi = values.prop_peerspi;
+ prop->prop_localspi.spi_size = values.prop_localspi.spi_size;
+ prop->prop_localspi.spi = 0;
+ }
+
+ for (i = 0; i < IKEV2_XFORMTYPE_MAX; i++) {
+ if (chosen[i] == NULL)
+ continue;
+ print_debug("%s: score %d: %s %s",
+ __func__, chosen[i]->xform_score,
+ print_map(i, ikev2_xformtype_map),
+ print_map(chosen[i]->xform_id, chosen[i]->xform_map));
+ if (chosen[i]->xform_length)
+ print_debug(" %d", chosen[i]->xform_length);
+ print_debug("\n");
+
+ if (config_add_transform(prop, chosen[i]->xform_type,
+ chosen[i]->xform_id, chosen[i]->xform_length,
+ chosen[i]->xform_keylength) == NULL)
+ break;
+ }
+
+ return (0);
+}
+
+int
+ikev2_sa_keys(struct iked_sa *sa)
+{
+ struct iked_transform *xform;
+ struct iked_hash *prf, *integr;
+ struct iked_cipher *encr;
+ struct group *group;
+ struct ibuf *ninr, *dhsecret, *skeyseed, *s, *t;
+ size_t nonceminlen, ilen, rlen, tmplen;
+ u_int64_t ispi, rspi;
+ int ret = -1;
+
+ ninr = dhsecret = skeyseed = s = t = NULL;
+
+ if (sa->sa_encr == NULL) {
+ if ((xform = config_findtransform(&sa->sa_proposals,
+ IKEV2_XFORMTYPE_ENCR)) == NULL) {
+ log_debug("%s: did not find encr transform", __func__);
+ return (-1);
+ }
+ if ((sa->sa_encr = cipher_new(xform->xform_type,
+ xform->xform_id, xform->xform_length)) == NULL) {
+ log_debug("%s: failed to get encr", __func__);
+ return (-1);
+ }
+ }
+ encr = sa->sa_encr;
+
+ if (sa->sa_prf == NULL) {
+ if ((xform = config_findtransform(&sa->sa_proposals,
+ IKEV2_XFORMTYPE_PRF)) == NULL) {
+ log_debug("%s: did not find prf transform", __func__);
+ return (-1);
+ }
+ if ((sa->sa_prf =
+ hash_new(xform->xform_type, xform->xform_id)) == NULL) {
+ log_debug("%s: failed to get prf", __func__);
+ return (-1);
+ }
+ }
+ prf = sa->sa_prf;
+
+ if (sa->sa_integr == NULL) {
+ if ((xform = config_findtransform(&sa->sa_proposals,
+ IKEV2_XFORMTYPE_INTEGR)) == NULL) {
+ log_debug("%s: did not find integr transform",
+ __func__);
+ return (-1);
+ }
+ if ((sa->sa_integr =
+ hash_new(xform->xform_type, xform->xform_id)) == NULL) {
+ log_debug("%s: failed to get integr", __func__);
+ return (-1);
+ }
+ }
+ integr = sa->sa_integr;
+
+ if (sa->sa_dhgroup == NULL) {
+ if ((xform = config_findtransform(&sa->sa_proposals,
+ IKEV2_XFORMTYPE_DH)) == NULL) {
+ log_debug("%s: did not find dh transform", __func__);
+ return (-1);
+ }
+ if ((sa->sa_dhgroup =
+ group_get(xform->xform_id)) == NULL) {
+ log_debug("%s: invalid dh", __func__);
+ return (-1);
+ }
+ if ((ssize_t)ibuf_length(sa->sa_dhiexchange) !=
+ dh_getlen(sa->sa_dhgroup)) {
+ /* XXX send notification to peer */
+ log_debug("%s: dh mismatch %d", __func__,
+ dh_getlen(sa->sa_dhgroup) * 8);
+ return (-1);
+ }
+ }
+ group = sa->sa_dhgroup;
+
+ if (!ibuf_length(sa->sa_dhrexchange)) {
+ if ((sa->sa_dhrexchange = ibuf_new(NULL,
+ dh_getlen(group))) == NULL) {
+ log_debug("%s: failed to alloc dh exchange", __func__);
+ return (-1);
+ }
+ if (dh_create_exchange(group, sa->sa_dhrexchange->buf) == -1) {
+ log_debug("%s: failed to get dh exchange", __func__);
+ return (-1);
+ }
+ }
+
+ if (prf->hash_fixedkey)
+ nonceminlen = prf->hash_fixedkey;
+ else
+ nonceminlen = IKED_NONCE_MIN;
+
+ /* Nonces need a minimal size and should have an even length */
+ if (ibuf_length(sa->sa_inonce) < nonceminlen ||
+ (ibuf_length(sa->sa_inonce) % 2) != 0 ||
+ ibuf_length(sa->sa_rnonce) < nonceminlen ||
+ (ibuf_length(sa->sa_rnonce) % 2) != 0) {
+ log_debug("%s: invalid nonces", __func__);
+ return (-1);
+ }
+
+ /*
+ * First generate SKEEYSEED = prf(Ni | Nr, g^ir)
+ */
+ if (prf->hash_fixedkey) {
+ /* Half of the key bits must come from Ni, and half from Nr */
+ ilen = prf->hash_fixedkey / 2;
+ rlen = prf->hash_fixedkey / 2;
+ } else {
+ /* Most PRF functions accept a variable-length key */
+ ilen = ibuf_length(sa->sa_inonce);
+ rlen = ibuf_length(sa->sa_rnonce);
+ }
+
+ if ((ninr = ibuf_new(sa->sa_inonce->buf, ilen)) == NULL ||
+ ibuf_add(ninr, sa->sa_rnonce->buf, rlen) != 0) {
+ log_debug("%s: failed to get nonce key buffer", __func__);
+ goto done;
+ }
+ if ((hash_setkey(prf, ninr->buf, ibuf_length(ninr))) == NULL) {
+ log_debug("%s: failed to set prf key", __func__);
+ goto done;
+ }
+
+ if ((dhsecret = ibuf_new(NULL, dh_getlen(group))) == NULL) {
+ log_debug("%s: failed to alloc dh secret", __func__);
+ goto done;
+ }
+ if (dh_create_shared(group, dhsecret->buf,
+ sa->sa_dhiexchange->buf) == -1) {
+ log_debug("%s: failed to get dh secret"
+ " group %d len %d secret %d exchange %d", __func__,
+ group->id, dh_getlen(group), ibuf_length(dhsecret),
+ ibuf_length(sa->sa_dhiexchange));
+ goto done;
+ }
+
+ if ((skeyseed = ibuf_new(NULL, hash_length(prf))) == NULL) {
+ log_debug("%s: failed to get SKEYSEED buffer", __func__);
+ goto done;
+ }
+
+ tmplen = 0;
+ hash_init(prf);
+ hash_update(prf, dhsecret->buf, ibuf_length(dhsecret));
+ hash_final(prf, skeyseed->buf, &tmplen);
+
+ log_debug("%s: SKEYSEED with %d bytes", __func__, tmplen);
+ print_hex(skeyseed->buf, 0, tmplen);
+
+ if (ibuf_setsize(skeyseed, tmplen) == -1) {
+ log_debug("%s: failed to set keymaterial length", __func__);
+ goto done;
+ }
+
+ /*
+ * Now generate the key material
+ *
+ * S = Ni | Nr | SPIi | SPIr
+ */
+
+ /* S = Ni | Nr | SPIi | SPIr */
+ ilen = ibuf_length(sa->sa_inonce);
+ rlen = ibuf_length(sa->sa_rnonce);
+ ispi = htobe64(sa->sa_hdr.sh_ispi);
+ rspi = htobe64(sa->sa_hdr.sh_rspi);
+
+ if ((s = ibuf_new(sa->sa_inonce->buf, ilen)) == NULL ||
+ ibuf_add(s, sa->sa_rnonce->buf, rlen) != 0 ||
+ ibuf_add(s, &ispi, sizeof(ispi)) != 0 ||
+ ibuf_add(s, &rspi, sizeof(rspi)) != 0) {
+ log_debug("%s: failed to set S buffer", __func__);
+ goto done;
+ }
+
+ log_debug("%s: S with %d bytes", __func__, ibuf_length(s));
+ print_hex(s->buf, 0, ibuf_length(s));
+
+ /*
+ * Get the size of the key material we need and the number
+ * of rounds we need to run the prf+ function.
+ */
+ ilen = hash_length(prf) + /* SK_d */
+ hash_keylength(integr) + /* SK_ai */
+ hash_keylength(integr) + /* SK_ar */
+ cipher_keylength(encr) + /* SK_ei */
+ cipher_keylength(encr) + /* SK_er */
+ hash_keylength(prf) + /* SK_pi */
+ hash_keylength(prf); /* SK_pr */
+
+ if ((t = ikev2_prfplus(prf, skeyseed, s, ilen)) == NULL) {
+ log_debug("%s: failed to get IKE SA key material", __func__);
+ goto done;
+ }
+
+ if ((sa->sa_key_d = ibuf_copy(t, hash_length(prf))) == NULL ||
+ (sa->sa_key_iauth = ibuf_copy(t, hash_keylength(integr))) == NULL ||
+ (sa->sa_key_rauth = ibuf_copy(t, hash_keylength(integr))) == NULL ||
+ (sa->sa_key_iencr = ibuf_copy(t, cipher_keylength(encr))) == NULL ||
+ (sa->sa_key_rencr = ibuf_copy(t, cipher_keylength(encr))) == NULL ||
+ (sa->sa_key_iprf = ibuf_copy(t, hash_length(prf))) == NULL ||
+ (sa->sa_key_rprf = ibuf_copy(t, hash_length(prf))) == NULL) {
+ log_debug("%s: failed to get SA keys", __func__);
+ goto done;
+ }
+
+ log_debug("%s: SK_d with %d bytes", __func__,
+ ibuf_length(sa->sa_key_d));
+ print_hex(sa->sa_key_d->buf, 0, ibuf_length(sa->sa_key_d));
+ log_debug("%s: SK_ai with %d bytes", __func__,
+ ibuf_length(sa->sa_key_iauth));
+ print_hex(sa->sa_key_iauth->buf, 0, ibuf_length(sa->sa_key_iauth));
+ log_debug("%s: SK_ar with %d bytes", __func__,
+ ibuf_length(sa->sa_key_rauth));
+ print_hex(sa->sa_key_rauth->buf, 0, ibuf_length(sa->sa_key_rauth));
+ log_debug("%s: SK_ei with %d bytes", __func__,
+ ibuf_length(sa->sa_key_iencr));
+ print_hex(sa->sa_key_iencr->buf, 0, ibuf_length(sa->sa_key_iencr));
+ log_debug("%s: SK_er with %d bytes", __func__,
+ ibuf_length(sa->sa_key_rencr));
+ print_hex(sa->sa_key_rencr->buf, 0, ibuf_length(sa->sa_key_rencr));
+ log_debug("%s: SK_pi with %d bytes", __func__,
+ ibuf_length(sa->sa_key_iprf));
+ print_hex(sa->sa_key_iprf->buf, 0, ibuf_length(sa->sa_key_iprf));
+ log_debug("%s: SK_pr with %d bytes", __func__,
+ ibuf_length(sa->sa_key_rprf));
+ print_hex(sa->sa_key_rprf->buf, 0, ibuf_length(sa->sa_key_rprf));
+
+ ret = 0;
+
+ done:
+ ibuf_release(ninr);
+ ibuf_release(dhsecret);
+ ibuf_release(skeyseed);
+ ibuf_release(s);
+ ibuf_release(t);
+
+ return (ret);
+}
+
+struct ibuf *
+ikev2_prfplus(struct iked_hash *prf, struct ibuf *key, struct ibuf *seed,
+ size_t keymatlen)
+{
+ struct ibuf *t = NULL, *t1 = NULL, *t2 = NULL;
+ size_t rlen, i, hashlen = 0;
+ u_int8_t pad = 0;
+
+ /*
+ * prf+ (K, S) = T1 | T2 | T3 | T4 | ...
+ *
+ * T1 = prf (K, S | 0x01)
+ * T2 = prf (K, T1 | S | 0x02)
+ * T3 = prf (K, T2 | S | 0x03)
+ * T4 = prf (K, T3 | S | 0x04)
+ */
+
+ if ((hash_setkey(prf, ibuf_data(key), ibuf_size(key))) == NULL) {
+ log_debug("%s: failed to set prf+ key", __func__);
+ goto fail;
+ }
+
+ if ((t = ibuf_new(NULL, 0)) == NULL) {
+ log_debug("%s: failed to get T buffer", __func__);
+ goto fail;
+ }
+
+ rlen = roundup(keymatlen, hash_length(prf)) / hash_length(prf);
+ if (rlen > 255)
+ fatalx("ikev2_prfplus: key material too large");
+
+ for (i = 0; i < rlen; i++) {
+ if (t1 != NULL) {
+ t2 = ibuf_new(t1->buf, ibuf_length(t1));
+ ibuf_release(t1);
+ } else
+ t2 = ibuf_new(NULL, 0);
+ t1 = ibuf_new(NULL, hash_length(prf));
+
+ ibuf_add(t2, seed->buf, ibuf_length(seed));
+ pad = i + 1;
+ ibuf_add(t2, &pad, 1);
+
+ hash_init(prf);
+ hash_update(prf, t2->buf, ibuf_length(t2));
+ hash_final(prf, t1->buf, &hashlen);
+
+ if (hashlen != hash_length(prf))
+ fatalx("ikev2_prfplus: hash length mismatch");
+
+ ibuf_release(t2);
+ ibuf_add(t, t1->buf, ibuf_length(t1));
+
+ log_debug("%s: T%d with %d bytes", __func__,
+ pad, ibuf_length(t1));
+ print_hex(t1->buf, 0, ibuf_length(t1));
+ }
+
+ log_debug("%s: Tn with %d bytes", __func__, ibuf_length(t));
+ print_hex(t->buf, 0, ibuf_length(t));
+
+ ibuf_release(t1);
+
+ return (t);
+
+ fail:
+ ibuf_release(t1);
+ ibuf_release(t);
+
+ return (NULL);
+}
+
+int
+ikev2_sa_tag(struct iked_sa *sa, struct iked_id *id)
+{
+ char *format, *domain = NULL, *idrepl = NULL;
+ char idstr[IKED_ID_SIZE];
+ int ret = -1;
+ size_t len;
+
+ if (sa->sa_tag != NULL)
+ free(sa->sa_tag);
+ sa->sa_tag = NULL;
+ format = sa->sa_policy->pol_tag;
+
+ len = IKED_TAG_SIZE;
+ if ((sa->sa_tag = calloc(1, len)) == NULL) {
+ log_debug("%s: calloc", __func__);
+ goto fail;
+ }
+ if (strlcpy(sa->sa_tag, format, len) >= len) {
+ log_debug("%s: tag too long", __func__);
+ goto fail;
+ }
+
+ if (print_id(id, sizeof(struct ikev2_id),
+ idstr, sizeof(idstr)) == -1) {
+ log_debug("%s: invalid id", __func__);
+ goto fail;
+ }
+
+ /* ASN.1 DER IDs are too long, use the CN part instead */
+ if (*idstr == '/' && (idrepl = strstr(idstr, "CN=")) != NULL) {
+ domain = strstr(idrepl, "emailAddress=");
+ idrepl[strcspn(idrepl, "/")] = '\0';
+ } else
+ idrepl = idstr;
+
+ if (strstr(format, "$id") != NULL) {
+ if (expand_string(sa->sa_tag, len, "$id", idrepl) != 0) {
+ log_debug("%s: failed to expand tag", __func__);
+ goto fail;
+ }
+ }
+
+ if (strstr(format, "$name") != NULL) {
+ if (expand_string(sa->sa_tag, len, "$name",
+ sa->sa_policy->pol_name) != 0) {
+ log_debug("%s: failed to expand tag", __func__);
+ goto fail;
+ }
+ }
+
+ if (strstr(format, "$domain") != NULL) {
+ if (id->id_type == IKEV2_ID_FQDN)
+ domain = strchr(idrepl, '.');
+ else if (id->id_type == IKEV2_ID_RFC822_ADDR)
+ domain = strchr(idrepl, '@');
+ else if (*idstr == '/' && domain != NULL)
+ domain = strchr(domain, '@');
+ else
+ domain = NULL;
+ if (domain == NULL || strlen(domain) < 2) {
+ log_debug("%s: no valid domain in ID %s",
+ __func__, idstr);
+ goto fail;
+ }
+ domain++;
+ if (expand_string(sa->sa_tag, len, "$domain", domain) != 0) {
+ log_debug("%s: failed to expand tag", __func__);
+ goto fail;
+ }
+ }
+
+ log_debug("%s: %s (%d)", __func__, sa->sa_tag, strlen(sa->sa_tag));
+
+ ret = 0;
+ fail:
+ if (ret != 0) {
+ free(sa->sa_tag);
+ sa->sa_tag = NULL;
+ }
+
+ return (ret);
+}
+
+int
+ikev2_childsa_negotiate(struct iked *env, struct iked_sa *sa,
+ struct iked_spi *rekey)
+{
+ struct iked_proposal *prop;
+ struct iked_transform *xform, *encrxf = NULL, *integrxf = NULL;
+ struct iked_childsa *csa, *csb;
+ struct iked_flow *flow, *flowa, *flowb;
+ struct ibuf *keymat = NULL, *seed = NULL;
+ u_int i, mdlen;
+ size_t ilen = 0;
+ int ret = -1;
+ u_int32_t spi = 0, flowloaded = 0;
+ struct iked_id *peerid, *localid;
+ EVP_MD_CTX ctx;
+ u_int8_t md[SHA_DIGEST_LENGTH];
+
+ if (!sa_stateok(sa, IKEV2_STATE_VALID))
+ return (-1);
+
+ if (sa->sa_hdr.sh_initiator) {
+ peerid = &sa->sa_rid;
+ localid = &sa->sa_iid;
+ } else {
+ localid = &sa->sa_rid;
+ peerid = &sa->sa_iid;
+ }
+
+ if (ikev2_sa_tag(sa, peerid) == -1)
+ return (-1);
+
+ sa_stateflags(sa, IKED_REQ_CHILDSA);
+
+ /* We need to determinate the key material length first */
+ TAILQ_FOREACH(prop, &sa->sa_proposals, prop_entry) {
+ if (prop->prop_protoid == IKEV2_SAPROTO_IKE)
+ continue;
+ log_debug("%s: proposal %d", __func__, prop->prop_id);
+ for (i = 0; i < prop->prop_nxforms; i++) {
+ xform = prop->prop_xforms + i;
+ xform->xform_keylength =
+ keylength_xf(prop->prop_protoid,
+ xform->xform_type, xform->xform_id);
+
+ switch (xform->xform_type) {
+ case IKEV2_XFORMTYPE_ENCR:
+ case IKEV2_XFORMTYPE_INTEGR:
+ if (xform->xform_length)
+ xform->xform_keylength =
+ xform->xform_length;
+ ilen += xform->xform_keylength / 8;
+ break;
+ }
+ }
+ }
+
+ /* double key material length for inbound/outbound */
+ ilen *= 2;
+
+ log_debug("%s: key material length %d", __func__, ilen);
+
+ if ((seed = ibuf_dup(sa->sa_inonce)) == NULL ||
+ ibuf_cat(seed, sa->sa_rnonce) != 0 ||
+ (keymat = ikev2_prfplus(sa->sa_prf,
+ sa->sa_key_d, seed, ilen)) == NULL) {
+ log_debug("%s: failed to get IKE SA key material", __func__);
+ goto done;
+ }
+
+ /*
+ * Generate a hash of the negotiated flows to detect a possible
+ * IKEv2 traffic selector re-negotiation.
+ */
+ EVP_DigestInit(&ctx, EVP_sha1());
+ TAILQ_FOREACH(prop, &sa->sa_proposals, prop_entry) {
+ if (ikev2_valid_proposal(prop, NULL, NULL) != 0)
+ continue;
+
+ TAILQ_FOREACH(flow, &sa->sa_policy->pol_flows, flow_entry) {
+ /* Use the inbound flow to generate the hash */
+ i = IPSP_DIRECTION_IN;
+ EVP_DigestUpdate(&ctx, &i, sizeof(i));
+ EVP_DigestUpdate(&ctx, &flow->flow_src,
+ sizeof(flow->flow_src));
+ EVP_DigestUpdate(&ctx, &flow->flow_dst,
+ sizeof(flow->flow_dst));
+ }
+ }
+ mdlen = sizeof(sa->sa_flowhash);
+ EVP_DigestFinal(&ctx, md, &mdlen);
+
+ /* Check the existing flows */
+ if (rekey != NULL) {
+ if (memcmp(&sa->sa_flowhash, &md, sizeof(md)) == 0) {
+ flowloaded = 0;
+ TAILQ_FOREACH(flow, &sa->sa_flows,
+ flow_entry) {
+ /* Mark the flow as unloaded */
+ if (flow->flow_peerspi == rekey->spi) {
+ flow->flow_loaded = 0;
+ flowloaded++;
+ }
+ }
+ log_debug("%s: keeping %d flows",
+ __func__, flowloaded);
+ }
+
+ /* XXX Check ESP/AH/IKE */
+ if (ikev2_childsa_delete(env, sa,
+ rekey->spi_protoid, rekey->spi, NULL, 1) == -1) {
+ log_debug("%s: failed to disable old SA %s",
+ __func__, print_spi(rekey->spi, rekey->spi_size));
+ return (-1);
+ }
+ }
+
+ memcpy(&sa->sa_flowhash, &md, sizeof(md));
+
+ /* Create the new flows */
+ TAILQ_FOREACH(prop, &sa->sa_proposals, prop_entry) {
+ if (ikev2_valid_proposal(prop, NULL, NULL) != 0)
+ continue;
+
+ TAILQ_FOREACH(flow, &sa->sa_policy->pol_flows, flow_entry) {
+ if ((flowa = calloc(1, sizeof(*flowa))) == NULL) {
+ log_debug("%s: failed to get flow", __func__);
+ goto done;
+ }
+
+ memcpy(flowa, flow, sizeof(*flow));
+ flowa->flow_dir = IPSP_DIRECTION_IN;
+ flowa->flow_saproto = prop->prop_protoid;
+ flowa->flow_srcid = localid;
+ flowa->flow_dstid = peerid;
+ flowa->flow_local = &sa->sa_local;
+ flowa->flow_peer = &sa->sa_peer;
+ flowa->flow_ikesa = sa;
+ flowa->flow_peerspi = prop->prop_peerspi.spi;
+ if (flowloaded)
+ flowa->flow_loaded = 1;
+
+ TAILQ_INSERT_TAIL(&sa->sa_flows, flowa, flow_entry);
+
+ if ((flowb = calloc(1, sizeof(*flowb))) == NULL) {
+ log_debug("%s: failed to get flow", __func__);
+ goto done;
+ }
+
+ memcpy(flowb, flowa, sizeof(*flow));
+
+ flowb->flow_dir = IPSP_DIRECTION_OUT;
+ memcpy(&flowb->flow_src, &flow->flow_dst,
+ sizeof(flow->flow_dst));
+ memcpy(&flowb->flow_dst, &flow->flow_src,
+ sizeof(flow->flow_src));
+
+ TAILQ_INSERT_TAIL(&sa->sa_flows, flowb, flow_entry);
+ }
+ }
+
+ /* Create the CHILD SAs using the key material */
+ TAILQ_FOREACH(prop, &sa->sa_proposals, prop_entry) {
+ if (ikev2_valid_proposal(prop, &encrxf, &integrxf) != 0)
+ continue;
+
+ spi = 0;
+
+ if ((csa = calloc(1, sizeof(*csa))) == NULL) {
+ log_debug("%s: failed to get CHILD SA", __func__);
+ goto done;
+ }
+
+ csa->csa_saproto = prop->prop_protoid;
+ csa->csa_ikesa = sa;
+ csa->csa_srcid = localid;
+ csa->csa_dstid = peerid;
+ csa->csa_peerspi = prop->prop_peerspi.spi;
+ if (sa->sa_hdr.sh_initiator) {
+ /* Initiator -> Responder */
+ csa->csa_dir = IPSP_DIRECTION_OUT;
+ csa->csa_local = &sa->sa_local;
+ csa->csa_peer = &sa->sa_peer;
+
+ if ((ret = pfkey_sa_init(env->sc_pfkey,
+ csa, &spi)) != 0)
+ goto done;
+ csa->csa_spi.spi = prop->prop_localspi.spi = spi;
+ } else {
+ /* Responder <- Initiator */
+ csa->csa_dir = IPSP_DIRECTION_IN;
+ csa->csa_spi.spi = prop->prop_peerspi.spi;
+ csa->csa_local = &sa->sa_peer;
+ csa->csa_peer = &sa->sa_local;
+ }
+
+ if ((csa->csa_encrkey = ibuf_copy(keymat,
+ encrxf->xform_keylength / 8)) == NULL ||
+ (csa->csa_integrkey = ibuf_copy(keymat,
+ integrxf->xform_keylength / 8)) == NULL) {
+ log_debug("%s: failed to get CHILD SA keys", __func__);
+ childsa_free(csa);
+ goto done;
+ }
+ csa->csa_encrxf = encrxf;
+ csa->csa_integrxf = integrxf;
+
+ TAILQ_INSERT_TAIL(&sa->sa_childsas, csa, csa_entry);
+
+ if ((csb = calloc(1, sizeof(*csb))) == NULL) {
+ log_debug("%s: failed to get CHILD SA", __func__);
+ goto done;
+ }
+
+ memcpy(csb, csa, sizeof(*csb));
+ csb->csa_dir = csa->csa_dir == IPSP_DIRECTION_IN ?
+ IPSP_DIRECTION_OUT : IPSP_DIRECTION_IN;
+ if (spi == 0) {
+ if ((ret = pfkey_sa_init(env->sc_pfkey,
+ csa, &spi)) != 0)
+ goto done;
+ csa->csa_spi.spi = prop->prop_localspi.spi = spi;
+ }
+ csb->csa_local = csa->csa_peer;
+ csb->csa_peer = csa->csa_local;
+
+ if ((csb->csa_encrkey = ibuf_copy(keymat,
+ encrxf->xform_keylength / 8)) == NULL ||
+ (csb->csa_integrkey = ibuf_copy(keymat,
+ integrxf->xform_keylength / 8)) == NULL) {
+ log_debug("%s: failed to get CHILD SA keys", __func__);
+ childsa_free(csb);
+ goto done;
+ }
+
+ TAILQ_INSERT_TAIL(&sa->sa_childsas, csb, csa_entry);
+ }
+
+ ret = 0;
+ done:
+ ibuf_release(keymat);
+ ibuf_release(seed);
+
+ return (ret);
+}
+
+int
+ikev2_childsa_enable(struct iked *env, struct iked_sa *sa)
+{
+ struct iked_childsa *csa;
+ struct iked_flow *flow;
+
+ TAILQ_FOREACH(csa, &sa->sa_childsas, csa_entry) {
+ if (csa->csa_rekey)
+ continue;
+
+ if (pfkey_sa_add(env->sc_pfkey, csa, NULL) != 0) {
+ log_debug("%s: failed to load CHILD SA spi %s",
+ __func__, print_spi(csa->csa_spi.spi, 4));
+ return (-1);
+ }
+
+ log_debug("%s: loaded CHILD SA spi %s", __func__,
+ print_spi(csa->csa_spi.spi, 4));
+ }
+
+ TAILQ_FOREACH(flow, &sa->sa_flows, flow_entry) {
+ if (flow->flow_rekey)
+ continue;
+
+ if (pfkey_flow_add(env->sc_pfkey, flow) != 0) {
+ log_debug("%s: failed to load flow", __func__);
+ return (-1);
+ }
+
+ log_debug("%s: loaded flow %p", __func__, flow);
+ }
+
+ return (0);
+}
+
+int
+ikev2_childsa_delete(struct iked *env, struct iked_sa *sa, u_int8_t saproto,
+ u_int64_t spi, u_int64_t *spiptr, int rekey)
+{
+ struct iked_childsa *csa, *nextcsa;
+ struct iked_flow *flow, *nextflow;
+ int found = 0;
+ u_int64_t localspi = 0;
+ const char *action;
+
+ if (spiptr)
+ *spiptr = 0;
+
+ action = rekey ? "disable" : "delete";
+
+ for (csa = TAILQ_FIRST(&sa->sa_childsas); csa != NULL; csa = nextcsa) {
+ nextcsa = TAILQ_NEXT(csa, csa_entry);
+
+ if (csa->csa_saproto != saproto ||
+ csa->csa_peerspi != spi)
+ continue;
+
+ if (csa->csa_spi.spi != spi)
+ localspi = csa->csa_spi.spi;
+ if (pfkey_sa_delete(env->sc_pfkey, csa) != 0)
+ log_debug("%s: failed to %s CHILD SA spi %s",
+ __func__, action, print_spi(csa->csa_spi.spi, 4));
+ else
+ log_debug("%s: %sd CHILD SA spi %s", __func__,
+ action, print_spi(csa->csa_spi.spi, 4));
+ found++;
+
+ if (rekey) {
+ csa->csa_rekey = 1;
+ continue;
+ }
+ TAILQ_REMOVE(&sa->sa_childsas, csa, csa_entry);
+ childsa_free(csa);
+ }
+
+ for (flow = TAILQ_FIRST(&sa->sa_flows); flow != NULL; flow = nextflow) {
+ nextflow = TAILQ_NEXT(flow, flow_entry);
+
+ if (flow->flow_saproto != saproto ||
+ flow->flow_peerspi != spi)
+ continue;
+ if (pfkey_flow_delete(env->sc_pfkey, flow) != 0)
+ log_debug("%s: failed to %s flow %p",
+ __func__, action, flow);
+ else
+ log_debug("%s: %sd flow %p",
+ __func__, action, flow);
+ found++;
+
+ if (rekey) {
+ flow->flow_rekey = 1;
+ continue;
+ }
+ TAILQ_REMOVE(&sa->sa_flows, flow, flow_entry);
+ flow_free(flow);
+ }
+
+ if (spiptr)
+ *spiptr = localspi;
+
+ return (found ? 0 : -1);
+}
+
+int
+ikev2_valid_proposal(struct iked_proposal *prop,
+ struct iked_transform **exf, struct iked_transform **ixf)
+{
+ struct iked_transform *xform, *encrxf, *integrxf;
+ size_t i;
+
+ switch (prop->prop_protoid) {
+ case IKEV2_SAPROTO_ESP:
+ case IKEV2_SAPROTO_AH:
+ break;
+ default:
+ return (-1);
+ }
+
+ encrxf = integrxf = NULL;
+ for (i = 0; i < prop->prop_nxforms; i++) {
+ xform = prop->prop_xforms + i;
+ if (xform->xform_type ==
+ IKEV2_XFORMTYPE_ENCR)
+ encrxf = xform;
+ else if (xform->xform_type ==
+ IKEV2_XFORMTYPE_INTEGR)
+ integrxf = xform;
+ }
+ /* XXX support non-auth / non-enc proposals */
+ if (encrxf == NULL || integrxf == NULL)
+ return (-1);
+
+ if (exf)
+ *exf = encrxf;
+ if (ixf)
+ *ixf = integrxf;
+
+ return (0);
+}
diff --git a/sbin/iked/ikev2.h b/sbin/iked/ikev2.h
new file mode 100644
index 00000000000..13278811f1a
--- /dev/null
+++ b/sbin/iked/ikev2.h
@@ -0,0 +1,515 @@
+/* $OpenBSD: ikev2.h,v 1.1 2010/06/03 16:41:12 reyk Exp $ */
+/* $vantronix: ikev2.h,v 1.27 2010/05/19 12:20:30 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IKEV2_H
+#define _IKEV2_H
+
+#define IKEV2_VERSION 0x20 /* IKE version 2.0 */
+#define IKEV1_VERSION 0x10 /* IKE version 1.0 */
+
+#define IKEV2_KEYPAD "Key Pad for IKEv2" /* don't change! */
+
+#define IKEV2_DEFAULT_IKE_TRANSFORM { \
+ { IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_CBC, 256 }, \
+ { IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_CBC, 192 }, \
+ { IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_CBC, 128 }, \
+ { IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_3DES }, \
+ { IKEV2_XFORMTYPE_PRF, IKEV2_XFORMPRF_HMAC_SHA2_256 }, \
+ { IKEV2_XFORMTYPE_PRF, IKEV2_XFORMPRF_HMAC_SHA1 }, \
+ { IKEV2_XFORMTYPE_PRF, IKEV2_XFORMPRF_HMAC_MD5 }, \
+ { IKEV2_XFORMTYPE_INTEGR, IKEV2_XFORMAUTH_HMAC_SHA2_256_128 },\
+ { IKEV2_XFORMTYPE_INTEGR, IKEV2_XFORMAUTH_HMAC_SHA1_96 },\
+ { IKEV2_XFORMTYPE_INTEGR, IKEV2_XFORMAUTH_HMAC_MD5_96 },\
+ { IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_MODP_2048_256 }, \
+ { IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_MODP_2048 }, \
+ { IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_MODP_1536 }, \
+ { IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_MODP_1024 }, \
+}
+
+extern struct iked_transform ikev2_default_ike_transforms[];
+extern size_t ikev2_default_nike_transforms;
+
+#define IKEV2_DEFAULT_ESP_TRANSFORM { \
+ { IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_CBC, 256 }, \
+ { IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_CBC, 192 }, \
+ { IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_CBC, 128 }, \
+ { IKEV2_XFORMTYPE_INTEGR, IKEV2_XFORMAUTH_HMAC_SHA2_256_128 },\
+ { IKEV2_XFORMTYPE_INTEGR, IKEV2_XFORMAUTH_HMAC_SHA1_96 },\
+ { IKEV2_XFORMTYPE_ESN, IKEV2_XFORMESN_NONE }, \
+}
+
+extern struct iked_transform ikev2_default_esp_transforms[];
+extern size_t ikev2_default_nesp_transforms;
+
+/*
+ * IKEv2 pseudo states
+ */
+
+#define IKEV2_STATE_INIT 0 /* new IKE SA */
+#define IKEV2_STATE_COOKIE 1 /* cookie requested */
+#define IKEV2_STATE_SA_INIT 2 /* init IKE SA */
+#define IKEV2_STATE_EAP 3 /* EAP requested */
+#define IKEV2_STATE_AUTH_REQUEST 4 /* auth received */
+#define IKEV2_STATE_AUTH_SUCCESS 5 /* authenticated */
+#define IKEV2_STATE_VALID 6 /* validated peer certs */
+#define IKEV2_STATE_EAP_VALID 7 /* EAP validated */
+#define IKEV2_STATE_RUNNING 8 /* active IKE SA */
+#define IKEV2_STATE_DELETE 9 /* delete this SA */
+
+extern struct iked_constmap ikev2_state_map[];
+
+/*
+ * IKE header (partially compatible with IKEv1)
+ */
+
+struct ike_header {
+ u_int64_t ike_ispi; /* IKE_SA Initiator SPI */
+ u_int64_t ike_rspi; /* IKE_SA Responder SPI */
+ u_int8_t ike_nextpayload; /* Next payload type */
+ u_int8_t ike_version; /* Major/Minor version number */
+ u_int8_t ike_exchange; /* Exchange type */
+ u_int8_t ike_flags; /* Message options */
+ u_int32_t ike_msgid; /* Message identifier */
+ u_int32_t ike_length; /* Total message length */
+} __packed;
+
+/* IKEv2 exchange types */
+#define IKEV2_EXCHANGE_IKE_SA_INIT 34 /* Initial Exchange */
+#define IKEV2_EXCHANGE_IKE_AUTH 35 /* Authentication */
+#define IKEV2_EXCHANGE_CREATE_CHILD_SA 36 /* Create Child SA */
+#define IKEV2_EXCHANGE_INFORMATIONAL 37 /* Informational */
+
+extern struct iked_constmap ikev2_exchange_map[];
+
+/* IKEv2 message flags */
+#define IKEV2_FLAG_INITIATOR 0x08 /* Sent by the initiator */
+#define IKEV2_FLAG_OLDVERSION 0x10 /* Supports a higher IKE version */
+#define IKEV2_FLAG_RESPONSE 0x20 /* Message is a response */
+
+extern struct iked_constmap ikev2_flag_map[];
+
+/*
+ * IKEv2 payloads
+ */
+
+struct ikev2_payload {
+ u_int8_t pld_nextpayload; /* Next payload type */
+ u_int8_t pld_reserved; /* Contains the critical bit */
+ u_int16_t pld_length; /* Payload length with header */
+} __packed;
+
+#define IKEV2_CRITICAL_PAYLOAD 0x01 /* First bit in the reserved field */
+
+/* IKEv2 payload types */
+#define IKEV2_PAYLOAD_NONE 0 /* No payload */
+#define IKEV2_PAYLOAD_SA 33 /* Security Association */
+#define IKEV2_PAYLOAD_KE 34 /* Key Exchange */
+#define IKEV2_PAYLOAD_IDi 35 /* Identification - Initiator */
+#define IKEV2_PAYLOAD_IDr 36 /* Identification - Responder */
+#define IKEV2_PAYLOAD_CERT 37 /* Certificate */
+#define IKEV2_PAYLOAD_CERTREQ 38 /* Certificate Request */
+#define IKEV2_PAYLOAD_AUTH 39 /* Authentication */
+#define IKEV2_PAYLOAD_NONCE 40 /* Nonce */
+#define IKEV2_PAYLOAD_NOTIFY 41 /* Notify */
+#define IKEV2_PAYLOAD_DELETE 42 /* Delete */
+#define IKEV2_PAYLOAD_VENDOR 43 /* Vendor ID */
+#define IKEV2_PAYLOAD_TSi 44 /* Traffic Selector - Initiator */
+#define IKEV2_PAYLOAD_TSr 45 /* Traffic Selector - Responder */
+#define IKEV2_PAYLOAD_E 46 /* Encrypted */
+#define IKEV2_PAYLOAD_CP 47 /* Configuration Payload */
+#define IKEV2_PAYLOAD_EAP 48 /* Extensible Authentication */
+
+extern struct iked_constmap ikev2_payload_map[];
+
+/*
+ * SA payload
+ */
+
+struct ikev2_sa_proposal {
+ u_int8_t sap_more; /* Last proposal or more */
+ u_int8_t sap_reserved; /* Must be set to zero */
+ u_int16_t sap_length; /* Proposal length */
+ u_int8_t sap_proposalnr; /* Proposal number */
+ u_int8_t sap_protoid; /* Protocol Id */
+ u_int8_t sap_spisize; /* SPI size */
+ u_int8_t sap_transforms; /* Number of transforms */
+ /* Followed by variable-length SPI */
+ /* Followed by variable-length transforms */
+} __packed;
+
+#define IKEV2_SAP_LAST 0
+#define IKEV2_SAP_MORE 2
+
+#define IKEV2_SAPROTO_IKE 1 /* IKEv2 */
+#define IKEV2_SAPROTO_AH 2 /* AH */
+#define IKEV2_SAPROTO_ESP 3 /* ESP */
+
+extern struct iked_constmap ikev2_saproto_map[];
+
+struct ikev2_transform {
+ u_int8_t xfrm_more; /* Last transform or more */
+ u_int8_t xfrm_reserved; /* Must be set to zero */
+ u_int16_t xfrm_length; /* Transform length */
+ u_int8_t xfrm_type; /* Transform type */
+ u_int8_t xfrm_reserved1; /* Must be set to zero */
+ u_int16_t xfrm_id; /* Transform Id */
+ /* Followed by variable-length transform attributes */
+} __packed;
+
+#define IKEV2_XFORM_LAST 0
+#define IKEV2_XFORM_MORE 3
+
+#define IKEV2_XFORMTYPE_ENCR 1 /* Encryption */
+#define IKEV2_XFORMTYPE_PRF 2 /* Pseudo-Random Function */
+#define IKEV2_XFORMTYPE_INTEGR 3 /* Integrity Algorithm */
+#define IKEV2_XFORMTYPE_DH 4 /* Diffie-Hellman Group */
+#define IKEV2_XFORMTYPE_ESN 5 /* Extended Sequence Numbers */
+#define IKEV2_XFORMTYPE_MAX 6
+
+extern struct iked_constmap ikev2_xformtype_map[];
+
+#define IKEV2_XFORMENCR_NONE 0 /* None */
+#define IKEV2_XFORMENCR_DES_IV64 1 /* RFC1827 */
+#define IKEV2_XFORMENCR_DES 2 /* RFC2405 */
+#define IKEV2_XFORMENCR_3DES 3 /* RFC2451 */
+#define IKEV2_XFORMENCR_RC5 4 /* RFC2451 */
+#define IKEV2_XFORMENCR_IDEA 5 /* RFC2451 */
+#define IKEV2_XFORMENCR_CAST 6 /* RFC2451 */
+#define IKEV2_XFORMENCR_BLOWFISH 7 /* RFC2451 */
+#define IKEV2_XFORMENCR_3IDEA 8 /* RFC2451 */
+#define IKEV2_XFORMENCR_DES_IV32 9 /* DESIV32 */
+#define IKEV2_XFORMENCR_RC4 10 /* RFC2451 */
+#define IKEV2_XFORMENCR_NULL 11 /* RFC2410 */
+#define IKEV2_XFORMENCR_AES_CBC 12 /* RFC3602 */
+#define IKEV2_XFORMENCR_AES_CTR 13 /* RFC3664 */
+#define IKEV2_XFORMENCR_AES_CCM_8 14 /* RFC5282 */
+#define IKEV2_XFORMENCR_AES_CCM_12 15 /* RFC5282 */
+#define IKEV2_XFORMENCR_AES_CCM_16 16 /* RFC5282 */
+#define IKEV2_XFORMENCR_AES_GCM_8 18 /* RFC5282 */
+#define IKEV2_XFORMENCR_AES_GCM_12 19 /* RFC5282 */
+#define IKEV2_XFORMENCR_AES_GCM_16 20 /* RFC5282 */
+#define IKEV2_XFORMENCR_NULL_AES_GMAC 21 /* RFC4543 */
+#define IKEV2_XFORMENCR_XTS_AES 22 /* IEEE P1619 */
+#define IKEV2_XFORMENCR_CAMELLIA_CBC 23 /* RFC5529 */
+#define IKEV2_XFORMENCR_CAMELLIA_CTR 24 /* RFC5529 */
+#define IKEV2_XFORMENCR_CAMELLIA_CCM_8 25 /* RFC5529 */
+#define IKEV2_XFORMENCR_CAMELLIA_CCM_12 26 /* RFC5529 */
+#define IKEV2_XFORMENCR_CAMELLIA_CCM_16 27 /* RFC5529 */
+
+extern struct iked_constmap ikev2_xformencr_map[];
+
+#define IKEV2_XFORMPRF_HMAC_MD5 1 /* RFC2104 */
+#define IKEV2_XFORMPRF_HMAC_SHA1 2 /* RFC2104 */
+#define IKEV2_XFORMPRF_HMAC_TIGER 3 /* RFC2104 */
+#define IKEV2_XFORMPRF_AES128_XCBC 4 /* RFC3664 */
+#define IKEV2_XFORMPRF_HMAC_SHA2_256 5 /* RFC4868 */
+#define IKEV2_XFORMPRF_HMAC_SHA2_384 6 /* RFC4868 */
+#define IKEV2_XFORMPRF_HMAC_SHA2_512 7 /* RFC4868 */
+#define IKEV2_XFORMPRF_AES128_CMAC 8 /* RFC4615 */
+
+extern struct iked_constmap ikev2_xformprf_map[];
+
+#define IKEV2_XFORMAUTH_NONE 0 /* No Authentication */
+#define IKEV2_XFORMAUTH_HMAC_MD5_96 1 /* RFC2403 */
+#define IKEV2_XFORMAUTH_HMAC_SHA1_96 2 /* RFC2404 */
+#define IKEV2_XFORMAUTH_DES_MAC 3 /* DES-MAC */
+#define IKEV2_XFORMAUTH_KPDK_MD5 4 /* RFC1826 */
+#define IKEV2_XFORMAUTH_AES_XCBC_96 5 /* RFC3566 */
+#define IKEV2_XFORMAUTH_HMAC_MD5_128 6 /* RFC4595 */
+#define IKEV2_XFORMAUTH_HMAC_SHA1_160 7 /* RFC4595 */
+#define IKEV2_XFORMAUTH_AES_CMAC_96 8 /* RFC4494 */
+#define IKEV2_XFORMAUTH_AES_128_GMAC 9 /* RFC4543 */
+#define IKEV2_XFORMAUTH_AES_192_GMAC 10 /* RFC4543 */
+#define IKEV2_XFORMAUTH_AES_256_GMAC 11 /* RFC4543 */
+#define IKEV2_XFORMAUTH_HMAC_SHA2_256_128 12 /* RFC4868 */
+#define IKEV2_XFORMAUTH_HMAC_SHA2_384_192 13 /* RFC4868 */
+#define IKEV2_XFORMAUTH_HMAC_SHA2_512_256 14 /* RFC4868 */
+
+extern struct iked_constmap ikev2_xformauth_map[];
+
+#define IKEV2_XFORMDH_NONE 0 /* No DH */
+#define IKEV2_XFORMDH_MODP_768 1 /* DH Group 1 */
+#define IKEV2_XFORMDH_MODP_1024 2 /* DH Group 2 */
+#define IKEV2_XFORMDH_EC_155 3 /* DH Group 3 */
+#define IKEV2_XFORMDH_EC_185 4 /* DH Group 3 */
+#define IKEV2_XFORMDH_MODP_1536 5 /* DH Group 5 */
+#define IKEV2_XFORMDH_MODP_2048 14 /* DH Group 14 */
+#define IKEV2_XFORMDH_MODP_3072 15 /* DH Group 15 */
+#define IKEV2_XFORMDH_MODP_4096 16 /* DH Group 16 */
+#define IKEV2_XFORMDH_MODP_6144 17 /* DH Group 17 */
+#define IKEV2_XFORMDH_MODP_8192 18 /* DH Group 18 */
+#define IKEV2_XFORMDH_EC_256 19 /* DH Group 19 */
+#define IKEV2_XFORMDH_EC_384 20 /* DH Group 20 */
+#define IKEV2_XFORMDH_EC_521 21 /* DH Group 21 */
+#define IKEV2_XFORMDH_MODP_1024_160 22 /* DH Group 22 */
+#define IKEV2_XFORMDH_MODP_2048_224 23 /* DH Group 23 */
+#define IKEV2_XFORMDH_MODP_2048_256 24 /* DH Group 24 */
+#define IKEV2_XFORMDH_EC_192 25 /* DH Group 25 */
+#define IKEV2_XFORMDH_EC_224 26 /* DH Group 26 */
+#define IKEV2_XFORMDH_MAX 27
+
+extern struct iked_constmap ikev2_xformdh_map[];
+
+#define IKEV2_XFORMESN_NONE 0 /* No ESN */
+#define IKEV2_XFORMESN_ESN 1 /* ESN */
+
+extern struct iked_constmap ikev2_xformesn_map[];
+
+struct ikev2_attribute {
+ u_int16_t attr_type; /* Attribute type */
+ u_int16_t attr_length; /* Attribute length or value */
+ /* Followed by variable length (TLV) */
+} __packed;
+
+#define IKEV2_ATTRAF_TLV 0x0000 /* Type-Length-Value format */
+#define IKEV2_ATTRAF_TV 0x8000 /* Type-Value format */
+
+#define IKEV2_ATTRTYPE_KEY_LENGTH 14 /* Key length */
+
+extern struct iked_constmap ikev2_attrtype_map[];
+
+/*
+ * KE Payload
+ */
+
+struct ikev2_keyexchange {
+ u_int16_t kex_dhgroup; /* DH Group # */
+ u_int16_t kex_reserved; /* Reserved */
+} __packed;
+
+/*
+ * N payload
+ */
+
+struct ikev2_notify {
+ u_int8_t n_protoid; /* Protocol Id */
+ u_int8_t n_spisize; /* SPI size */
+ u_int16_t n_type; /* Notify message type */
+ /* Followed by variable length SPI */
+ /* Followed by variable length notification data */
+} __packed;
+
+#define IKEV2_N_UNSUPPORTED_CRITICAL_PAYLOAD 1 /* RFC4306 */
+#define IKEV2_N_INVALID_IKE_SPI 4 /* RFC4306 */
+#define IKEV2_N_INVALID_MAJOR_VERSION 5 /* RFC4306 */
+#define IKEV2_N_INVALID_SYNTAX 7 /* RFC4306 */
+#define IKEV2_N_INVALID_MESSAGE_ID 9 /* RFC4306 */
+#define IKEV2_N_INVALID_SPI 11 /* RFC4306 */
+#define IKEV2_N_NO_PROPOSAL_CHOSEN 14 /* RFC4306 */
+#define IKEV2_N_INVALID_KE_PAYLOAD 17 /* RFC4306 */
+#define IKEV2_N_AUTHENTICATION_FAILED 24 /* RFC4306 */
+#define IKEV2_N_SINGLE_PAIR_REQUIRED 34 /* RFC4306 */
+#define IKEV2_N_NO_ADDITIONAL_SAS 35 /* RFC4306 */
+#define IKEV2_N_INTERNAL_ADDRESS_FAILURE 36 /* RFC4306 */
+#define IKEV2_N_FAILED_CP_REQUIRED 37 /* RFC4306 */
+#define IKEV2_N_TS_UNACCEPTABLE 38 /* RFC4306 */
+#define IKEV2_N_INVALID_SELECTORS 39 /* RFC4306 */
+#define IKEV2_N_UNACCEPTABLE_ADDRESSES 40 /* RFC4555 */
+#define IKEV2_N_UNEXPECTED_NAT_DETECTED 41 /* RFC4555 */
+#define IKEV2_N_USE_ASSIGNED_HoA 42 /* RFC5026 */
+#define IKEV2_N_INITIAL_CONTACT 16384 /* RFC4306 */
+#define IKEV2_N_SET_WINDOW_SIZE 16385 /* RFC4306 */
+#define IKEV2_N_ADDITIONAL_TS_POSSIBLE 16386 /* RFC4306 */
+#define IKEV2_N_IPCOMP_SUPPORTED 16387 /* RFC4306 */
+#define IKEV2_N_NAT_DETECTION_SOURCE_IP 16388 /* RFC4306 */
+#define IKEV2_N_NAT_DETECTION_DESTINATION_IP 16389 /* RFC4306 */
+#define IKEV2_N_COOKIE 16390 /* RFC4306 */
+#define IKEV2_N_USE_TRANSPORT_MODE 16391 /* RFC4306 */
+#define IKEV2_N_HTTP_CERT_LOOKUP_SUPPORTED 16392 /* RFC4306 */
+#define IKEV2_N_REKEY_SA 16393 /* RFC4306 */
+#define IKEV2_N_ESP_TFC_PADDING_NOT_SUPPORTED 16394 /* RFC4306 */
+#define IKEV2_N_NON_FIRST_FRAGMENTS_ALSO 16395 /* RFC4306 */
+#define IKEV2_N_MOBIKE_SUPPORTED 16396 /* RFC4555 */
+#define IKEV2_N_ADDITIONAL_IP4_ADDRESS 16397 /* RFC4555 */
+#define IKEV2_N_ADDITIONAL_IP6_ADDRESS 16398 /* RFC4555 */
+#define IKEV2_N_NO_ADDITIONAL_ADDRESSES 16399 /* RFC4555 */
+#define IKEV2_N_UPDATE_SA_ADDRESSES 16400 /* RFC4555 */
+#define IKEV2_N_COOKIE2 16401 /* RFC4555 */
+#define IKEV2_N_NO_NATS_ALLOWED 16402 /* RFC4555 */
+#define IKEV2_N_AUTH_LIFETIME 16403 /* RFC4478 */
+#define IKEV2_N_MULTIPLE_AUTH_SUPPORTED 16404 /* RFC4739 */
+#define IKEV2_N_ANOTHER_AUTH_FOLLOWS 16405 /* RFC4739 */
+#define IKEV2_N_REDIRECT_SUPPORTED 16406 /* RFC5685 */
+#define IKEV2_N_REDIRECT 16407 /* RFC5685 */
+#define IKEV2_N_REDIRECTED_FROM 16408 /* RFC5685 */
+#define IKEV2_N_TICKET_LT_OPAQUE 16409 /* RFC5723 */
+#define IKEV2_N_TICKET_REQUEST 16410 /* RFC5723 */
+#define IKEV2_N_TICKET_ACK 16411 /* RFC5723 */
+#define IKEV2_N_TICKET_NACK 16412 /* RFC5723 */
+#define IKEV2_N_TICKET_OPAQUE 16413 /* RFC5723 */
+#define IKEV2_N_LINK_ID 16414 /* RFC5739 */
+#define IKEV2_N_USE_WESP_MODE 16415 /* RFC-ietf-ipsecme-traffic-visibility-12.txt */
+#define IKEV2_N_ROHC_SUPPORTED 16416 /* RFC-ietf-rohc-ikev2-extensions-hcoipsec-12.txt */
+
+extern struct iked_constmap ikev2_n_map[];
+
+/*
+ * DELETE payload
+ */
+
+struct ikev2_delete {
+ u_int8_t del_protoid; /* Protocol Id */
+ u_int8_t del_spisize; /* SPI size */
+ u_int16_t del_nspi; /* Number of SPIs */
+ /* Followed by variable length SPIs */
+} __packed;
+
+/*
+ * ID payload
+ */
+
+struct ikev2_id {
+ u_int8_t id_type; /* Id type */
+ u_int8_t id_reserved[3]; /* Reserved */
+ /* Followed by the identification data */
+} __packed;
+
+#define IKEV2_ID_NONE 0 /* No ID */
+#define IKEV2_ID_IPV4_ADDR 1 /* RFC4306 */
+#define IKEV2_ID_FQDN 2 /* RFC4306 */
+#define IKEV2_ID_RFC822_ADDR 3 /* RFC4306 */
+#define IKEV2_ID_IPV6_ADDR 5 /* RFC4306 */
+#define IKEV2_ID_DER_ASN1_DN 9 /* RFC4306 */
+#define IKEV2_ID_DER_ASN1_GN 10 /* RFC4306 */
+#define IKEV2_ID_KEY_ID 11 /* RFC4306 */
+#define IKEV2_ID_FC_NAME 12 /* RFC4595 */
+
+extern struct iked_constmap ikev2_id_map[];
+
+/*
+ * CERT/CERTREQ payloads
+ */
+
+struct ikev2_cert {
+ u_int8_t cert_type; /* Encoding */
+ /* Followed by the certificate data */
+} __packed;
+
+#define IKEV2_CERT_NONE 0 /* None */
+#define IKEV2_CERT_X509_PKCS7 1 /* RFC4306 */
+#define IKEV2_CERT_PGP 2 /* RFC4306 */
+#define IKEV2_CERT_DNS_SIGNED_KEY 3 /* RFC4306 */
+#define IKEV2_CERT_X509_CERT 4 /* RFC4306 */
+#define IKEV2_CERT_KERBEROS_TOKEN 6 /* RFC4306 */
+#define IKEV2_CERT_CRL 7 /* RFC4306 */
+#define IKEV2_CERT_ARL 8 /* RFC4306 */
+#define IKEV2_CERT_SPKI 9 /* RFC4306 */
+#define IKEV2_CERT_X509_ATTR 10 /* RFC4306 */
+#define IKEV2_CERT_RSA_KEY 11 /* RFC4306 */
+#define IKEV2_CERT_HASHURL_X509 12 /* RFC4306 */
+#define IKEV2_CERT_HASHURL_X509_BUNDLE 13 /* RFC4306 */
+#define IKEV2_CERT_OCSP 14 /* RFC4806 */
+
+extern struct iked_constmap ikev2_cert_map[];
+
+/*
+ * TSi/TSr payloads
+ */
+
+struct ikev2_tsp {
+ u_int8_t tsp_count; /* Number of TSs */
+ u_int8_t tsp_reserved[3]; /* Reserved */
+ /* Followed by the traffic selectors */
+} __packed;
+
+struct ikev2_ts {
+ u_int8_t ts_type; /* TS type */
+ u_int8_t ts_protoid; /* Protocol Id */
+ u_int16_t ts_length; /* Length */
+ u_int16_t ts_startport; /* Start port */
+ u_int16_t ts_endport; /* End port */
+} __packed;
+
+#define IKEV2_TS_IPV4_ADDR_RANGE 7 /* RFC4306 */
+#define IKEV2_TS_IPV6_ADDR_RANGE 8 /* RFC4306 */
+#define IKEV2_TS_FC_ADDR_RANGE 9 /* RFC4595 */
+
+extern struct iked_constmap ikev2_ts_map[];
+
+/*
+ * AUTH payload
+ */
+
+struct ikev2_auth {
+ u_int8_t auth_method; /* Signature type */
+ u_int8_t auth_reserved[3]; /* Reserved */
+ /* Followed by the signature */
+} __packed;
+
+#define IKEV2_AUTH_NONE 0 /* None */
+#define IKEV2_AUTH_RSA_SIG 1 /* RFC4306 */
+#define IKEV2_AUTH_SHARED_KEY_MIC 2 /* RFC4306 */
+#define IKEV2_AUTH_DSS_SIG 3 /* RFC4306 */
+#define IKEV2_AUTH_ECDSA_256 9 /* RFC4754 */
+#define IKEV2_AUTH_ECDSA_384 10 /* RFC4754 */
+#define IKEV2_AUTH_ECDSA_512 11 /* RFC4754 */
+
+extern struct iked_constmap ikev2_auth_map[];
+
+/*
+ * CP payload
+ */
+
+struct ikev2_cp {
+ u_int8_t cp_type;
+ u_int8_t cp_reserved[3];
+ /* Followed by the attributes */
+} __packed;
+
+#define IKEV2_CP_REQUEST 1 /* CFG-Request */
+#define IKEV2_CP_REPLY 2 /* CFG-Reply */
+#define IKEV2_CP_SET 3 /* CFG-SET */
+#define IKEV2_CP_ACK 4 /* CFG-ACK */
+
+extern struct iked_constmap ikev2_cp_map[];
+
+struct ikev2_cfg {
+ u_int16_t cfg_type; /* first bit must be set to zero */
+ u_int16_t cfg_length;
+ /* Followed by variable-length data */
+} __packed;
+
+#define IKEV2_CFG_INTERNAL_IP4_ADDRESS 1 /* RFC4306 */
+#define IKEV2_CFG_INTERNAL_IP4_NETMASK 2 /* RFC4306 */
+#define IKEV2_CFG_INTERNAL_IP4_DNS 3 /* RFC4306 */
+#define IKEV2_CFG_INTERNAL_IP4_NBNS 4 /* RFC4306 */
+#define IKEV2_CFG_INTERNAL_ADDRESS_EXPIRY 5 /* RFC4306 */
+#define IKEV2_CFG_INTERNAL_IP4_DHCP 6 /* RFC4306 */
+#define IKEV2_CFG_APPLICATION_VERSION 7 /* RFC4306 */
+#define IKEV2_CFG_INTERNAL_IP6_ADDRESS 8 /* RFC4306 */
+#define IKEV2_CFG_INTERNAL_IP6_DNS 10 /* RFC4306 */
+#define IKEV2_CFG_INTERNAL_IP6_NBNS 11 /* RFC4306 */
+#define IKEV2_CFG_INTERNAL_IP6_DHCP 12 /* RFC4306 */
+#define IKEV2_CFG_INTERNAL_IP4_SUBNET 13 /* RFC4306 */
+#define IKEV2_CFG_SUPPORTED_ATTRIBUTES 14 /* RFC4306 */
+#define IKEV2_CFG_INTERNAL_IP6_SUBNET 15 /* RFC4306 */
+#define IKEV2_CFG_INTERNAL_IP4_SERVER 23456 /* MS-IKEE */
+#define IKEV2_CFG_INTERNAL_IP6_SERVER 23457 /* MS-IKEE */
+
+extern struct iked_constmap ikev2_cfg_map[];
+
+/*
+ * Internal structures
+ */
+
+struct ikev2_message {
+ struct ike_header im_header;
+};
+
+#endif /* _IKEV2_H */
diff --git a/sbin/iked/log.c b/sbin/iked/log.c
new file mode 100644
index 00000000000..5fc42dd6fac
--- /dev/null
+++ b/sbin/iked/log.c
@@ -0,0 +1,193 @@
+/* $OpenBSD: log.c,v 1.1 2010/06/03 16:41:12 reyk Exp $ */
+/* $vantronix: log.c,v 1.4 2010/04/21 19:29:09 reyk Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/tree.h>
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <event.h>
+
+#include "iked.h"
+
+int debug;
+int verbose;
+
+void vlog(int, const char *, va_list);
+void logit(int, const char *, ...);
+
+void
+log_init(int n_debug)
+{
+ extern char *__progname;
+
+ debug = n_debug;
+ verbose = n_debug;
+
+ if (!debug)
+ openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
+
+ tzset();
+}
+
+void
+log_verbose(int v)
+{
+ if (v > debug)
+ verbose = v;
+}
+
+void
+logit(int pri, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vlog(pri, fmt, ap);
+ va_end(ap);
+}
+
+void
+vlog(int pri, const char *fmt, va_list ap)
+{
+ char *nfmt;
+
+ if (debug) {
+ /* best effort in out of mem situations */
+ if (asprintf(&nfmt, "%s\n", fmt) == -1) {
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ } else {
+ vfprintf(stderr, nfmt, ap);
+ free(nfmt);
+ }
+ fflush(stderr);
+ } else
+ vsyslog(pri, fmt, ap);
+}
+
+
+void
+log_warn(const char *emsg, ...)
+{
+ char *nfmt;
+ va_list ap;
+
+ /* best effort to even work in out of memory situations */
+ if (emsg == NULL)
+ logit(LOG_CRIT, "%s", strerror(errno));
+ else {
+ va_start(ap, emsg);
+
+ if (asprintf(&nfmt, "%s: %s", emsg, strerror(errno)) == -1) {
+ /* we tried it... */
+ vlog(LOG_CRIT, emsg, ap);
+ logit(LOG_CRIT, "%s", strerror(errno));
+ } else {
+ vlog(LOG_CRIT, nfmt, ap);
+ free(nfmt);
+ }
+ va_end(ap);
+ }
+}
+
+void
+log_warnx(const char *emsg, ...)
+{
+ va_list ap;
+
+ va_start(ap, emsg);
+ vlog(LOG_CRIT, emsg, ap);
+ va_end(ap);
+}
+
+void
+log_info(const char *emsg, ...)
+{
+ va_list ap;
+
+ va_start(ap, emsg);
+ vlog(LOG_INFO, emsg, ap);
+ va_end(ap);
+}
+
+void
+log_debug(const char *emsg, ...)
+{
+ va_list ap;
+
+ if (verbose > 1) {
+ va_start(ap, emsg);
+ vlog(LOG_DEBUG, emsg, ap);
+ va_end(ap);
+ }
+}
+
+void
+print_debug(const char *emsg, ...)
+{
+ va_list ap;
+
+ if (debug && verbose > 2) {
+ va_start(ap, emsg);
+ vfprintf(stderr, emsg, ap);
+ va_end(ap);
+ }
+}
+
+void
+print_verbose(const char *emsg, ...)
+{
+ va_list ap;
+
+ if (verbose) {
+ va_start(ap, emsg);
+ vfprintf(stderr, emsg, ap);
+ va_end(ap);
+ }
+}
+
+void
+fatal(const char *emsg)
+{
+ if (emsg == NULL)
+ logit(LOG_CRIT, "fatal: %s", strerror(errno));
+ else {
+ if (errno)
+ logit(LOG_CRIT, "fatal: %s: %s",
+ emsg, strerror(errno));
+ else
+ logit(LOG_CRIT, "fatal: %s", emsg);
+ }
+
+ exit(1);
+}
+
+void
+fatalx(const char *emsg)
+{
+ errno = 0;
+ fatal(emsg);
+}
diff --git a/sbin/iked/parse.y b/sbin/iked/parse.y
new file mode 100644
index 00000000000..9cc3aa3c8fa
--- /dev/null
+++ b/sbin/iked/parse.y
@@ -0,0 +1,2316 @@
+/* $OpenBSD: parse.y,v 1.1 2010/06/03 16:41:12 reyk Exp $ */
+/* $vantronix: parse.y,v 1.22 2010/06/03 11:08:34 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
+ * Copyright (c) 2004, 2005 Hans-Joerg Hoexer <hshoexer@openbsd.org>
+ * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2001 Markus Friedl. All rights reserved.
+ * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
+ * Copyright (c) 2001 Theo de Raadt. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+%{
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/ip_ipsp.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ifaddrs.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <event.h>
+
+#include "iked.h"
+#include "ikev2.h"
+#include "eap.h"
+
+TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
+static struct file {
+ TAILQ_ENTRY(file) entry;
+ FILE *stream;
+ char *name;
+ int lineno;
+ int errors;
+} *file;
+struct file *pushfile(const char *, int);
+int popfile(void);
+int check_file_secrecy(int, const char *);
+int yyparse(void);
+int yylex(void);
+int yyerror(const char *, ...);
+int yywarn(const char *, ...);
+int kw_cmp(const void *, const void *);
+int lookup(char *);
+int lgetc(int);
+int lungetc(int);
+int findeol(void);
+
+TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
+struct sym {
+ TAILQ_ENTRY(sym) entry;
+ int used;
+ int persist;
+ char *nam;
+ char *val;
+};
+int symset(const char *, const char *, int);
+char *symget(const char *);
+int cmdline_symset(char *);
+
+#define KEYSIZE_LIMIT 1024
+
+static struct iked *env = NULL;
+static int debug = 0;
+static int rules = 0;
+
+struct ipsec_xf {
+ const char *name;
+ u_int id;
+ u_int length;
+ u_int keylength;
+};
+
+struct ipsec_transforms {
+ const struct ipsec_xf *authxf;
+ const struct ipsec_xf *prfxf;
+ const struct ipsec_xf *encxf;
+ const struct ipsec_xf *groupxf;
+};
+
+struct ipsec_mode {
+ struct ipsec_transforms *xfs;
+ u_int8_t ike_exch;
+};
+
+const struct ipsec_xf authxfs[] = {
+ { "hmac-md5", IKEV2_XFORMAUTH_HMAC_MD5_96, 16 },
+ { "hmac-sha1", IKEV2_XFORMAUTH_HMAC_SHA1_96, 20 },
+ { "hmac-sha2-256", IKEV2_XFORMAUTH_HMAC_SHA2_256_128, 32 },
+ { "hmac-sha2-384", IKEV2_XFORMAUTH_HMAC_SHA2_384_192, 48 },
+ { "hmac-sha2-512", IKEV2_XFORMAUTH_HMAC_SHA2_512_256, 64 },
+ { NULL }
+};
+
+const struct ipsec_xf prfxfs[] = {
+ { "hmac-md5", IKEV2_XFORMPRF_HMAC_MD5, 16 },
+ { "hmac-sha1", IKEV2_XFORMPRF_HMAC_SHA1, 20 },
+ { "hmac-sha2-256", IKEV2_XFORMPRF_HMAC_SHA2_256, 32 },
+ { "hmac-sha2-384", IKEV2_XFORMPRF_HMAC_SHA2_384, 48 },
+ { "hmac-sha2-512", IKEV2_XFORMPRF_HMAC_SHA2_512, 64 },
+ { NULL }
+};
+
+const struct ipsec_xf *encxfs = NULL;
+
+const struct ipsec_xf ikeencxfs[] = {
+ { "3des", IKEV2_XFORMENCR_3DES, 24 },
+ { "3des-cbc", IKEV2_XFORMENCR_3DES, 24 },
+ { "aes-128", IKEV2_XFORMENCR_AES_CBC, 16, 16 },
+ { "aes-192", IKEV2_XFORMENCR_AES_CBC, 24, 24 },
+ { "aes-256", IKEV2_XFORMENCR_AES_CBC, 32, 32 },
+ { NULL }
+};
+
+const struct ipsec_xf ipsecencxfs[] = {
+ { "des", IKEV2_XFORMENCR_DES, 8 },
+ { "3des", IKEV2_XFORMENCR_3DES, 24 },
+ { "3des-cbc", IKEV2_XFORMENCR_3DES, 24 },
+ { "aes-128", IKEV2_XFORMENCR_AES_CBC, 16, 16 },
+ { "aes-192", IKEV2_XFORMENCR_AES_CBC, 24, 24 },
+ { "aes-256", IKEV2_XFORMENCR_AES_CBC, 32, 32 },
+ { "aes-ctr", IKEV2_XFORMENCR_AES_CTR, 20, 20 },
+ { "blowfish", IKEV2_XFORMENCR_BLOWFISH, 20, 20 },
+ { "cast", IKEV2_XFORMENCR_CAST, 16, 16 },
+ { "null", IKEV2_XFORMENCR_NULL, 0, 0 },
+ { NULL }
+};
+
+const struct ipsec_xf groupxfs[] = {
+ { "modp768", IKEV2_XFORMDH_MODP_768 },
+ { "grp1", IKEV2_XFORMDH_MODP_768 },
+ { "modp1024", IKEV2_XFORMDH_MODP_1024 },
+ { "grp2", IKEV2_XFORMDH_MODP_1024 },
+ { "ec155", IKEV2_XFORMDH_EC_155 },
+ { "grp3", IKEV2_XFORMDH_EC_155 },
+ { "ec185", IKEV2_XFORMDH_EC_185 },
+ { "grp4", IKEV2_XFORMDH_EC_185 },
+ { "modp1536", IKEV2_XFORMDH_MODP_1536 },
+ { "grp5", IKEV2_XFORMDH_MODP_1536 },
+ { "modp2048", IKEV2_XFORMDH_MODP_2048 },
+ { "grp14", IKEV2_XFORMDH_MODP_2048 },
+ { "modp3072", IKEV2_XFORMDH_MODP_3072 },
+ { "grp15", IKEV2_XFORMDH_MODP_3072 },
+ { "modp4096", IKEV2_XFORMDH_MODP_4096 },
+ { "grp16", IKEV2_XFORMDH_MODP_4096 },
+ { "modp6144", IKEV2_XFORMDH_MODP_6144 },
+ { "grp17", IKEV2_XFORMDH_MODP_6144 },
+ { "modp8192", IKEV2_XFORMDH_MODP_8192 },
+ { "grp18", IKEV2_XFORMDH_MODP_8192 },
+ { "ec256", IKEV2_XFORMDH_EC_256 },
+ { "grp19", IKEV2_XFORMDH_EC_256 },
+ { "ec384", IKEV2_XFORMDH_EC_384 },
+ { "grp20", IKEV2_XFORMDH_EC_384 },
+ { "ec521", IKEV2_XFORMDH_EC_521 },
+ { "grp21", IKEV2_XFORMDH_EC_521 },
+ { "modp1024-160", IKEV2_XFORMDH_MODP_1024_160 },
+ { "grp22", IKEV2_XFORMDH_MODP_1024_160 },
+ { "modp2048-224", IKEV2_XFORMDH_MODP_2048_224 },
+ { "grp23", IKEV2_XFORMDH_MODP_2048_224 },
+ { "modp2048-256", IKEV2_XFORMDH_MODP_2048_256 },
+ { "grp24", IKEV2_XFORMDH_MODP_2048_256 },
+ { "ec192", IKEV2_XFORMDH_EC_192 },
+ { "grp25", IKEV2_XFORMDH_EC_192 },
+ { "ec224", IKEV2_XFORMDH_EC_224 },
+ { "grp26", IKEV2_XFORMDH_EC_224 },
+ { NULL }
+};
+
+const struct ipsec_xf methodxfs[] = {
+ { "rsa", IKEV2_AUTH_RSA_SIG },
+ { "dss", IKEV2_AUTH_DSS_SIG },
+ { "ecdsa-256", IKEV2_AUTH_ECDSA_256 },
+ { "ecdsa-384", IKEV2_AUTH_ECDSA_384 },
+ { "ecdsa-512", IKEV2_AUTH_ECDSA_512 },
+ { NULL }
+};
+
+const struct ipsec_xf saxfs[] = {
+ { "esp", IKEV2_SAPROTO_ESP },
+ { "ah", IKEV2_SAPROTO_AH },
+ { NULL }
+};
+
+const struct ipsec_xf cpxfs[] = {
+ { "address", IKEV2_CFG_INTERNAL_IP4_ADDRESS, AF_INET },
+ { "netmask", IKEV2_CFG_INTERNAL_IP4_NETMASK, AF_INET },
+ { "name-server", IKEV2_CFG_INTERNAL_IP4_DNS, AF_INET },
+ { "netbios-server", IKEV2_CFG_INTERNAL_IP4_NBNS, AF_INET },
+ { "dhcp-server", IKEV2_CFG_INTERNAL_IP4_DHCP, AF_INET },
+ { "address", IKEV2_CFG_INTERNAL_IP6_ADDRESS, AF_INET6 },
+ { "name-server", IKEV2_CFG_INTERNAL_IP6_DNS, AF_INET6 },
+ { "netbios-server", IKEV2_CFG_INTERNAL_IP6_NBNS, AF_INET6 },
+ { "dhcp-server", IKEV2_CFG_INTERNAL_IP6_DHCP, AF_INET6 },
+ { "protected-subnet", IKEV2_CFG_INTERNAL_IP4_SUBNET, AF_INET },
+ { "protected-subnet", IKEV2_CFG_INTERNAL_IP6_SUBNET, AF_INET6 },
+ { "access-server", IKEV2_CFG_INTERNAL_IP4_SERVER, AF_INET },
+ { "access-server", IKEV2_CFG_INTERNAL_IP6_SERVER, AF_INET6 }
+};
+
+struct ipsec_addr_wrap {
+ struct sockaddr_storage address;
+ u_int8_t mask;
+ int netaddress;
+ sa_family_t af;
+ u_int type;
+ u_int action;
+ char *name;
+ struct ipsec_addr_wrap *next;
+ struct ipsec_addr_wrap *tail;
+ struct ipsec_addr_wrap *srcnat;
+};
+
+struct ipsec_hosts {
+ struct ipsec_addr_wrap *src;
+ struct ipsec_addr_wrap *dst;
+ u_int16_t sport;
+ u_int16_t dport;
+};
+
+struct ipsec_addr_wrap *host(const char *);
+struct ipsec_addr_wrap *host_v6(const char *, int);
+struct ipsec_addr_wrap *host_v4(const char *, int);
+struct ipsec_addr_wrap *host_dns(const char *, int);
+struct ipsec_addr_wrap *host_if(const char *, int);
+struct ipsec_addr_wrap *host_any(void);
+void ifa_load(void);
+int ifa_exists(const char *);
+struct ipsec_addr_wrap *ifa_lookup(const char *ifa_name);
+struct ipsec_addr_wrap *ifa_grouplookup(const char *);
+void set_ipmask(struct ipsec_addr_wrap *, u_int8_t);
+const struct ipsec_xf *parse_xf(const char *, u_int, const struct ipsec_xf *);
+const char *print_xf(u_int, u_int, const struct ipsec_xf *);
+void copy_transforms(u_int, const struct ipsec_xf *,
+ const struct ipsec_xf *,
+ struct iked_transform *, size_t,
+ u_int *, struct iked_transform *, size_t);
+int create_ike(char *, u_int8_t, struct ipsec_hosts *,
+ struct ipsec_hosts *, struct ipsec_mode *,
+ struct ipsec_mode *, u_int8_t,
+ u_int8_t, char *, char *, struct iked_auth *,
+ char *, struct ipsec_addr_wrap *);
+int create_user(const char *, const char *);
+int get_id_type(char *);
+u_int8_t x2i(unsigned char *);
+int parsekey(unsigned char *, size_t, struct iked_auth *);
+int parsekeyfile(char *, struct iked_auth *);
+
+struct ipsec_transforms *ipsec_transforms;
+
+typedef struct {
+ union {
+ int64_t number;
+ u_int8_t ikemode;
+ u_int8_t dir;
+ u_int8_t satype;
+ u_int8_t proto;
+ char *string;
+ u_int16_t port;
+ struct ipsec_hosts *hosts;
+ struct ipsec_hosts peers;
+ struct ipsec_addr_wrap *anyhost;
+ struct ipsec_addr_wrap *host;
+ struct ipsec_addr_wrap *cfg;
+ struct {
+ char *srcid;
+ char *dstid;
+ } ids;
+ char *id;
+ u_int8_t type;
+ struct iked_auth ikeauth;
+ struct iked_auth ikekey;
+ struct ipsec_transforms *transforms;
+ struct ipsec_mode *mode;
+ } v;
+ int lineno;
+} YYSTYPE;
+
+%}
+
+%token FROM ESP AH IN PEER ON OUT TO SRCID DSTID RSA PSK PORT
+%token FILENAME AUTHXF PRFXF ENCXF ERROR IKEV2 IKESA CHILDSA
+%token PASSIVE ACTIVE ANY TAG PROTO LOCAL GROUP NAME CONFIG EAP USER
+%token IKEV1 FLOW SA TCPMD5 TUNNEL TRANSPORT
+%token INCLUDE
+%token <v.string> STRING
+%token <v.number> NUMBER
+%type <v.string> string
+%type <v.satype> satype
+%type <v.proto> proto
+%type <v.number> protoval
+%type <v.hosts> hosts hosts_list
+%type <v.port> port
+%type <v.number> portval
+%type <v.peers> peers
+%type <v.anyhost> anyhost
+%type <v.host> host host_spec
+%type <v.ids> ids
+%type <v.id> id
+%type <v.transforms> transforms
+%type <v.ikemode> ikemode
+%type <v.ikeauth> ikeauth
+%type <v.ikekey> keyspec
+%type <v.mode> ike_sa child_sa
+%type <v.string> tag
+%type <v.string> name
+%type <v.cfg> cfg ikecfg ikecfgvals
+%%
+
+grammar : /* empty */
+ | grammar include '\n'
+ | grammar '\n'
+ | grammar user '\n'
+ | grammar ikev2rule '\n'
+ | grammar varset '\n'
+ | grammar otherrule skipline '\n'
+ | grammar error '\n' { file->errors++; }
+ ;
+
+comma : ','
+ | /* empty */
+ ;
+
+include : INCLUDE STRING {
+ struct file *nfile;
+
+ if ((nfile = pushfile($2, 0)) == NULL) {
+ yyerror("failed to include file %s", $2);
+ free($2);
+ YYERROR;
+ }
+ free($2);
+
+ file = nfile;
+ lungetc('\n');
+ }
+ ;
+
+user : USER STRING STRING {
+ if (create_user($2, $3) == -1)
+ YYERROR;
+ }
+ ;
+
+ikev2rule : IKEV2 name ikemode satype proto hosts_list peers
+ ike_sa child_sa ids ikeauth ikecfg tag {
+ if (create_ike($2, $5, $6, &$7, $8, $9, $4, $3,
+ $10.srcid, $10.dstid, &$11, $13, $12) == -1)
+ YYERROR;
+ }
+ ;
+
+ikecfg : /* empty */ { $$ = NULL; }
+ | ikecfgvals { $$ = $1; }
+ ;
+
+ikecfgvals : cfg { $$ = $1; }
+ | ikecfgvals cfg {
+ if ($2 == NULL)
+ $$ = $1;
+ else if ($1 == NULL)
+ $$ = $2;
+ else {
+ $1->tail->next = $2;
+ $1->tail = $2->tail;
+ $$ = $1;
+ }
+ }
+ ;
+
+cfg : CONFIG STRING host_spec {
+ const struct ipsec_xf *xf;
+
+ if ((xf = parse_xf($2, $3->af, cpxfs)) == NULL) {
+ yyerror("not a valid ikecfg option");
+ free($2);
+ free($3);
+ YYERROR;
+ }
+ $$ = $3;
+ $$->type = xf->id;
+ $$->action = IKEV2_CP_REPLY; /* XXX */
+ }
+ ;
+
+name : /* empty */ { $$ = NULL; }
+ | STRING {
+ $$ = $1;
+ }
+
+satype : /* empty */ { $$ = IKEV2_SAPROTO_ESP; }
+ | ESP { $$ = IKEV2_SAPROTO_ESP; }
+ | AH { $$ = IKEV2_SAPROTO_AH; }
+ ;
+
+proto : /* empty */ { $$ = 0; }
+ | PROTO protoval { $$ = $2; }
+ | PROTO ESP { $$ = IPPROTO_ESP; }
+ | PROTO AH { $$ = IPPROTO_AH; }
+ ;
+
+protoval : STRING {
+ struct protoent *p;
+
+ p = getprotobyname($1);
+ if (p == NULL) {
+ yyerror("unknown protocol: %s", $1);
+ YYERROR;
+ }
+ $$ = p->p_proto;
+ free($1);
+ }
+ | NUMBER {
+ if ($1 > 255 || $1 < 0) {
+ yyerror("protocol outside range");
+ YYERROR;
+ }
+ }
+ ;
+
+hosts_list : hosts { $$ = $1; }
+ | hosts_list comma hosts {
+ if ($3 == NULL)
+ $$ = $1;
+ else if ($1 == NULL)
+ $$ = $3;
+ else {
+ $1->src->tail->next = $3->src;
+ $1->src->tail = $3->src->tail;
+ $1->dst->tail->next = $3->dst;
+ $1->dst->tail = $3->dst->tail;
+ $$ = $1;
+ }
+ }
+ ;
+
+hosts : FROM host port TO host port {
+ struct ipsec_addr_wrap *ipa;
+ for (ipa = $5; ipa; ipa = ipa->next) {
+ if (ipa->srcnat) {
+ yyerror("no flow NAT support for"
+ " destination network: %s",
+ ipa->name);
+ YYERROR;
+ }
+ }
+
+ if (($$ = calloc(1, sizeof(*$$))) == NULL)
+ err(1, "hosts: calloc");
+
+ $$->src = $2;
+ $$->sport = $3;
+ $$->dst = $5;
+ $$->dport = $6;
+ }
+ | TO host port FROM host port {
+ struct ipsec_addr_wrap *ipa;
+ for (ipa = $2; ipa; ipa = ipa->next) {
+ if (ipa->srcnat) {
+ yyerror("no flow NAT support for"
+ " destination network: %s",
+ ipa->name);
+ YYERROR;
+ }
+ }
+ if (($$ = calloc(1, sizeof(*$$))) == NULL)
+ err(1, "hosts: calloc");
+
+ $$->src = $5;
+ $$->sport = $6;
+ $$->dst = $2;
+ $$->dport = $3;
+ }
+ ;
+
+port : /* empty */ { $$ = 0; }
+ | PORT portval { $$ = $2; }
+ ;
+
+portval : STRING {
+ struct servent *s;
+
+ if ((s = getservbyname($1, "tcp")) != NULL ||
+ (s = getservbyname($1, "udp")) != NULL) {
+ $$ = s->s_port;
+ } else {
+ yyerror("unknown port: %s", $1);
+ YYERROR;
+ }
+ }
+ | NUMBER {
+ if ($1 > USHRT_MAX || $1 < 0) {
+ yyerror("port outside range");
+ YYERROR;
+ }
+ $$ = htons($1);
+ }
+ ;
+
+peers : /* empty */ {
+ $$.dst = NULL;
+ $$.src = NULL;
+ }
+ | PEER anyhost LOCAL anyhost {
+ $$.dst = $2;
+ $$.src = $4;
+ }
+ | LOCAL anyhost PEER anyhost {
+ $$.dst = $4;
+ $$.src = $2;
+ }
+ | PEER anyhost {
+ $$.dst = $2;
+ $$.src = NULL;
+ }
+ | LOCAL anyhost {
+ $$.dst = NULL;
+ $$.src = $2;
+ }
+ ;
+
+anyhost : host_spec { $$ = $1; }
+ | ANY {
+ $$ = host_any();
+ }
+
+host_spec : STRING {
+ if (($$ = host($1)) == NULL) {
+ free($1);
+ yyerror("could not parse host specification");
+ YYERROR;
+ }
+ free($1);
+ }
+ | STRING '/' NUMBER {
+ char *buf;
+
+ if (asprintf(&buf, "%s/%lld", $1, $3) == -1)
+ err(1, "host: asprintf");
+ free($1);
+ if (($$ = host(buf)) == NULL) {
+ free(buf);
+ yyerror("could not parse host specification");
+ YYERROR;
+ }
+ free(buf);
+ }
+ ;
+
+host : host_spec { $$ = $1; }
+ | host_spec '(' host_spec ')' {
+ if ($3->af != $1->af) {
+ yyerror("Flow NAT address family mismatch");
+ YYERROR;
+ }
+ $$ = $1;
+ $$->srcnat = $3;
+ }
+ | ANY {
+ $$ = host_any();
+ }
+ ;
+
+ids : /* empty */ {
+ $$.srcid = NULL;
+ $$.dstid = NULL;
+ }
+ | SRCID id DSTID id {
+ $$.srcid = $2;
+ $$.dstid = $4;
+ }
+ | SRCID id {
+ $$.srcid = $2;
+ $$.dstid = NULL;
+ }
+ | DSTID id {
+ $$.srcid = NULL;
+ $$.dstid = $2;
+ }
+ ;
+
+id : STRING { $$ = $1; }
+ ;
+
+transforms : {
+ if ((ipsec_transforms = calloc(1,
+ sizeof(struct ipsec_transforms))) == NULL)
+ err(1, "transforms: calloc");
+ }
+ transforms_l {
+ $$ = ipsec_transforms;
+ }
+ | /* empty */ {
+ $$ = NULL;
+ }
+ ;
+
+transforms_l : transforms_l transform
+ | transform
+ ;
+
+transform : AUTHXF STRING {
+ if (ipsec_transforms->authxf)
+ yyerror("auth already set");
+ else {
+ ipsec_transforms->authxf = parse_xf($2, 0,
+ authxfs);
+ if (!ipsec_transforms->authxf)
+ yyerror("%s not a valid transform", $2);
+ }
+ }
+ | ENCXF STRING {
+ if (ipsec_transforms->encxf)
+ yyerror("enc already set");
+ else {
+ ipsec_transforms->encxf = parse_xf($2, 0,
+ encxfs);
+ if (!ipsec_transforms->encxf)
+ yyerror("%s not a valid transform",
+ $2);
+ }
+ }
+ | PRFXF STRING {
+ if (ipsec_transforms->prfxf)
+ yyerror("prf already set");
+ else {
+ ipsec_transforms->prfxf = parse_xf($2, 0,
+ prfxfs);
+ if (!ipsec_transforms->prfxf)
+ yyerror("%s not a valid transform",
+ $2);
+ }
+ }
+ | GROUP STRING {
+ if (ipsec_transforms->groupxf)
+ yyerror("group already set");
+ else {
+ ipsec_transforms->groupxf = parse_xf($2, 0,
+ groupxfs);
+ if (!ipsec_transforms->groupxf)
+ yyerror("%s not a valid transform",
+ $2);
+ }
+ }
+ ;
+
+ike_sa : /* empty */ {
+ $$ = NULL;
+ }
+ | IKESA {
+ encxfs = ikeencxfs;
+ } transforms {
+ if (($$ = calloc(1, sizeof(*$$))) == NULL)
+ err(1, "child_sa: calloc");
+ $$->xfs = $3;
+ }
+ ;
+
+child_sa : /* empty */ {
+ $$ = NULL;
+ }
+ | CHILDSA {
+ encxfs = ipsecencxfs;
+ } transforms {
+ if (($$ = calloc(1, sizeof(*$$))) == NULL)
+ err(1, "child_sa: calloc");
+ $$->xfs = $3;
+ }
+ ;
+
+ikemode : /* empty */ { $$ = IKED_POLICY_PASSIVE; }
+ | PASSIVE { $$ = IKED_POLICY_PASSIVE; }
+ | ACTIVE { $$ = IKED_POLICY_ACTIVE; }
+ ;
+
+ikeauth : /* empty */ {
+ $$.auth_method = IKEV2_AUTH_RSA_SIG;
+ $$.auth_length = 0;
+ }
+ | RSA {
+ $$.auth_method = IKEV2_AUTH_RSA_SIG;
+ $$.auth_length = 0;
+ }
+ | PSK keyspec {
+ memcpy(&$$, &$2, sizeof($$));
+ $$.auth_method = IKEV2_AUTH_SHARED_KEY_MIC;
+ }
+ | EAP STRING {
+ u_int i;
+
+ for (i = 0; i < strlen($2); i++)
+ if ($2[i] == '-')
+ $2[i] = '_';
+
+ if (strcasecmp("mschap_v2", $2) != 0) {
+ yyerror("unsupported EAP method: %s", $2);
+ free($2);
+ YYERROR;
+ }
+ free($2);
+
+ $$.auth_method = IKEV2_AUTH_RSA_SIG;
+ $$.auth_eap = EAP_TYPE_MSCHAP_V2;
+ $$.auth_length = 0;
+ }
+ ;
+
+keyspec : STRING {
+ u_int8_t *hex;
+
+ bzero(&$$, sizeof($$));
+
+ hex = $1;
+ if (strncmp(hex, "0x", 2) == 0) {
+ hex += 2;
+ if (parsekey(hex, strlen(hex), &$$) != 0) {
+ free($1);
+ YYERROR;
+ }
+ } else {
+ if (strlen($1) > sizeof($$.auth_data)) {
+ yyerror("psk too long");
+ free($1);
+ YYERROR;
+ }
+ strlcpy($$.auth_data, $1,
+ sizeof($$.auth_data));
+ $$.auth_length = strlen($1);
+ }
+ free($1);
+ }
+ | FILENAME STRING {
+ if (parsekeyfile($2, &$$) != 0) {
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ }
+ ;
+
+tag : /* empty */
+ {
+ $$ = NULL;
+ }
+ | TAG STRING
+ {
+ $$ = $2;
+ }
+ ;
+
+string : string STRING
+ {
+ if (asprintf(&$$, "%s %s", $1, $2) == -1)
+ err(1, "string: asprintf");
+ free($1);
+ free($2);
+ }
+ | STRING
+ ;
+
+varset : STRING '=' string
+ {
+ log_debug("%s = \"%s\"\n", $1, $3);
+ if (symset($1, $3, 0) == -1)
+ err(1, "cannot store variable");
+ free($1);
+ free($3);
+ }
+ ;
+
+/*
+ * ignore IKEv1/manual keying rules in ipsec.conf
+ */
+otherrule : IKEV1
+ | sarule
+ | FLOW
+ | TCPMD5
+ ;
+
+/* manual keying SAs might start with the following keywords */
+sarule : SA
+ | FROM
+ | TO
+ | TUNNEL
+ | TRANSPORT
+ ;
+
+/* ignore everything to the end of the line */
+skipline :
+ {
+ int c;
+
+ while ((c = lgetc(0)) != '\n' && c != EOF)
+ ; /* nothing */
+ if (c == '\n')
+ lungetc(c);
+ }
+ ;
+%%
+
+struct keywords {
+ const char *k_name;
+ int k_val;
+};
+
+int
+yyerror(const char *fmt, ...)
+{
+ va_list ap;
+
+ file->errors++;
+ va_start(ap, fmt);
+ fprintf(stderr, "%s: %d: ", file->name, yylval.lineno);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ return (0);
+}
+
+int
+yywarn(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "%s: %d: ", file->name, yylval.lineno);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ return (0);
+}
+
+int
+kw_cmp(const void *k, const void *e)
+{
+ return (strcmp(k, ((const struct keywords *)e)->k_name));
+}
+
+int
+lookup(char *s)
+{
+ /* this has to be sorted always */
+ static const struct keywords keywords[] = {
+ { "active", ACTIVE },
+ { "ah", AH },
+ { "any", ANY },
+ { "auth", AUTHXF },
+ { "childsa", CHILDSA },
+ { "config", CONFIG },
+ { "dstid", DSTID },
+ { "eap", EAP },
+ { "enc", ENCXF },
+ { "esp", ESP },
+ { "file", FILENAME },
+ { "flow", FLOW },
+ { "from", FROM },
+ { "group", GROUP },
+ { "ikesa", IKESA },
+ { "ike", IKEV1 },
+ { "ikev2", IKEV2 },
+ { "include", INCLUDE },
+ { "local", LOCAL },
+ { "name", NAME },
+ { "passive", PASSIVE },
+ { "peer", PEER },
+ { "port", PORT },
+ { "proto", PROTO },
+ { "psk", PSK },
+ { "rsa", RSA },
+ { "sa", SA },
+ { "srcid", SRCID },
+ { "tag", TAG },
+ { "tcpmd5", TCPMD5 },
+ { "to", TO },
+ { "transport", TRANSPORT },
+ { "tunnel", TUNNEL },
+ { "user", USER }
+ };
+ const struct keywords *p;
+
+ p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
+ sizeof(keywords[0]), kw_cmp);
+
+ if (p) {
+ if (debug > 1)
+ fprintf(stderr, "%s: %d\n", s, p->k_val);
+ return (p->k_val);
+ } else {
+ if (debug > 1)
+ fprintf(stderr, "string: %s\n", s);
+ return (STRING);
+ }
+}
+
+#define MAXPUSHBACK 128
+
+char *parsebuf;
+int parseindex;
+char pushback_buffer[MAXPUSHBACK];
+int pushback_index = 0;
+
+int
+lgetc(int quotec)
+{
+ int c, next;
+
+ if (parsebuf) {
+ /* Read character from the parsebuffer instead of input. */
+ if (parseindex >= 0) {
+ c = parsebuf[parseindex++];
+ if (c != '\0')
+ return (c);
+ parsebuf = NULL;
+ } else
+ parseindex++;
+ }
+
+ if (pushback_index)
+ return (pushback_buffer[--pushback_index]);
+
+ if (quotec) {
+ if ((c = getc(file->stream)) == EOF) {
+ yyerror("reached end of file while parsing quoted string");
+ if (popfile() == EOF)
+ return (EOF);
+ return (quotec);
+ }
+ return (c);
+ }
+
+ while ((c = getc(file->stream)) == '\\') {
+ next = getc(file->stream);
+ if (next != '\n') {
+ c = next;
+ break;
+ }
+ yylval.lineno = file->lineno;
+ file->lineno++;
+ }
+
+ while (c == EOF) {
+ if (popfile() == EOF)
+ return (EOF);
+ c = getc(file->stream);
+ }
+ return (c);
+}
+
+int
+lungetc(int c)
+{
+ if (c == EOF)
+ return (EOF);
+ if (parsebuf) {
+ parseindex--;
+ if (parseindex >= 0)
+ return (c);
+ }
+ if (pushback_index < MAXPUSHBACK-1)
+ return (pushback_buffer[pushback_index++] = c);
+ else
+ return (EOF);
+}
+
+int
+findeol(void)
+{
+ int c;
+
+ parsebuf = NULL;
+
+ /* skip to either EOF or the first real EOL */
+ while (1) {
+ if (pushback_index)
+ c = pushback_buffer[--pushback_index];
+ else
+ c = lgetc(0);
+ if (c == '\n') {
+ file->lineno++;
+ break;
+ }
+ if (c == EOF)
+ break;
+ }
+ return (ERROR);
+}
+
+int
+yylex(void)
+{
+ char buf[8096];
+ char *p, *val;
+ int quotec, next, c;
+ int token;
+
+top:
+ p = buf;
+ while ((c = lgetc(0)) == ' ' || c == '\t')
+ ; /* nothing */
+
+ yylval.lineno = file->lineno;
+ if (c == '#')
+ while ((c = lgetc(0)) != '\n' && c != EOF)
+ ; /* nothing */
+ if (c == '$' && parsebuf == NULL) {
+ while (1) {
+ if ((c = lgetc(0)) == EOF)
+ return (0);
+
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ if (isalnum(c) || c == '_') {
+ *p++ = (char)c;
+ continue;
+ }
+ *p = '\0';
+ lungetc(c);
+ break;
+ }
+ val = symget(buf);
+ if (val == NULL) {
+ yyerror("macro '%s' not defined", buf);
+ return (findeol());
+ }
+ parsebuf = val;
+ parseindex = 0;
+ goto top;
+ }
+
+ switch (c) {
+ case '\'':
+ case '"':
+ quotec = c;
+ while (1) {
+ if ((c = lgetc(quotec)) == EOF)
+ return (0);
+ if (c == '\n') {
+ file->lineno++;
+ continue;
+ } else if (c == '\\') {
+ if ((next = lgetc(quotec)) == EOF)
+ return (0);
+ if (next == quotec || c == ' ' || c == '\t')
+ c = next;
+ else if (next == '\n')
+ continue;
+ else
+ lungetc(next);
+ } else if (c == quotec) {
+ *p = '\0';
+ break;
+ }
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ *p++ = (char)c;
+ }
+ yylval.v.string = strdup(buf);
+ if (yylval.v.string == NULL)
+ err(1, "yylex: strdup");
+ return (STRING);
+ }
+
+#define allowed_to_end_number(x) \
+ (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
+
+ if (c == '-' || isdigit(c)) {
+ do {
+ *p++ = c;
+ if ((unsigned)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ } while ((c = lgetc(0)) != EOF && isdigit(c));
+ lungetc(c);
+ if (p == buf + 1 && buf[0] == '-')
+ goto nodigits;
+ if (c == EOF || allowed_to_end_number(c)) {
+ const char *errstr = NULL;
+
+ *p = '\0';
+ yylval.v.number = strtonum(buf, LLONG_MIN,
+ LLONG_MAX, &errstr);
+ if (errstr) {
+ yyerror("\"%s\" invalid number: %s",
+ buf, errstr);
+ return (findeol());
+ }
+ return (NUMBER);
+ } else {
+nodigits:
+ while (p > buf + 1)
+ lungetc(*--p);
+ c = *--p;
+ if (c == '-')
+ return (c);
+ }
+ }
+
+#define allowed_in_string(x) \
+ (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
+ x != '{' && x != '}' && x != '<' && x != '>' && \
+ x != '!' && x != '=' && x != '/' && x != '#' && \
+ x != ','))
+
+ if (isalnum(c) || c == ':' || c == '_' || c == '*') {
+ do {
+ *p++ = c;
+ if ((unsigned)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
+ lungetc(c);
+ *p = '\0';
+ if ((token = lookup(buf)) == STRING)
+ if ((yylval.v.string = strdup(buf)) == NULL)
+ err(1, "yylex: strdup");
+ return (token);
+ }
+ if (c == '\n') {
+ yylval.lineno = file->lineno;
+ file->lineno++;
+ }
+ if (c == EOF)
+ return (0);
+ return (c);
+}
+
+int
+check_file_secrecy(int fd, const char *fname)
+{
+ struct stat st;
+
+ if (fstat(fd, &st)) {
+ warn("cannot stat %s", fname);
+ return (-1);
+ }
+ if (st.st_uid != 0 && st.st_uid != getuid()) {
+ warnx("%s: owner not root or current user", fname);
+ return (-1);
+ }
+ if (st.st_mode & (S_IRWXG | S_IRWXO)) {
+ warnx("%s: group/world readable/writeable", fname);
+ return (-1);
+ }
+ return (0);
+}
+
+struct file *
+pushfile(const char *name, int secret)
+{
+ struct file *nfile;
+
+ if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
+ warn("malloc");
+ return (NULL);
+ }
+ if ((nfile->name = strdup(name)) == NULL) {
+ warn("malloc");
+ free(nfile);
+ return (NULL);
+ }
+ if (TAILQ_FIRST(&files) == NULL && strcmp(nfile->name, "-") == 0) {
+ nfile->stream = stdin;
+ free(nfile->name);
+ if ((nfile->name = strdup("stdin")) == NULL) {
+ warn("strdup");
+ free(nfile);
+ return (NULL);
+ }
+ } else if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
+ warn("%s", nfile->name);
+ free(nfile->name);
+ free(nfile);
+ return (NULL);
+ } else if (secret &&
+ check_file_secrecy(fileno(nfile->stream), nfile->name)) {
+ fclose(nfile->stream);
+ free(nfile->name);
+ free(nfile);
+ return (NULL);
+ }
+ nfile->lineno = 1;
+ TAILQ_INSERT_TAIL(&files, nfile, entry);
+ return (nfile);
+}
+
+int
+popfile(void)
+{
+ struct file *prev;
+
+ if ((prev = TAILQ_PREV(file, files, entry)) != NULL) {
+ prev->errors += file->errors;
+ TAILQ_REMOVE(&files, file, entry);
+ fclose(file->stream);
+ free(file->name);
+ free(file);
+ file = prev;
+ return (0);
+ }
+ return (EOF);
+}
+
+int
+parse_config(const char *filename, struct iked *x_env)
+{
+ struct sym *sym;
+ int errors = 0;
+
+ env = x_env;
+ rules = 0;
+
+ if ((file = pushfile(filename, 1)) == NULL)
+ return (-1);
+
+ yyparse();
+ errors = file->errors;
+ popfile();
+
+ if (!rules)
+ log_warnx("%s: no valid configuration rules found",
+ filename);
+ else
+ log_debug("%s: loaded %d configuration rules",
+ filename, rules);
+
+ /* Free macros and check which have not been used. */
+ while ((sym = TAILQ_FIRST(&symhead))) {
+ if (!sym->used)
+ log_debug("warning: macro '%s' not "
+ "used\n", sym->nam);
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entry);
+ free(sym);
+ }
+
+ return (errors ? -1 : 0);
+}
+
+int
+symset(const char *nam, const char *val, int persist)
+{
+ struct sym *sym;
+
+ for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
+ sym = TAILQ_NEXT(sym, entry))
+ ; /* nothing */
+
+ if (sym != NULL) {
+ if (sym->persist == 1)
+ return (0);
+ else {
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entry);
+ free(sym);
+ }
+ }
+ if ((sym = calloc(1, sizeof(*sym))) == NULL)
+ return (-1);
+
+ sym->nam = strdup(nam);
+ if (sym->nam == NULL) {
+ free(sym);
+ return (-1);
+ }
+ sym->val = strdup(val);
+ if (sym->val == NULL) {
+ free(sym->nam);
+ free(sym);
+ return (-1);
+ }
+ sym->used = 0;
+ sym->persist = persist;
+ TAILQ_INSERT_TAIL(&symhead, sym, entry);
+ return (0);
+}
+
+int
+cmdline_symset(char *s)
+{
+ char *sym, *val;
+ int ret;
+ size_t len;
+
+ if ((val = strrchr(s, '=')) == NULL)
+ return (-1);
+
+ len = strlen(s) - strlen(val) + 1;
+ if ((sym = malloc(len)) == NULL)
+ err(1, "cmdline_symset: malloc");
+
+ strlcpy(sym, s, len);
+
+ ret = symset(sym, val + 1, 1);
+ free(sym);
+
+ return (ret);
+}
+
+char *
+symget(const char *nam)
+{
+ struct sym *sym;
+
+ TAILQ_FOREACH(sym, &symhead, entry)
+ if (strcmp(nam, sym->nam) == 0) {
+ sym->used = 1;
+ return (sym->val);
+ }
+ return (NULL);
+}
+
+u_int8_t
+x2i(unsigned char *s)
+{
+ char ss[3];
+
+ ss[0] = s[0];
+ ss[1] = s[1];
+ ss[2] = 0;
+
+ if (!isxdigit(s[0]) || !isxdigit(s[1])) {
+ yyerror("keys need to be specified in hex digits");
+ return (-1);
+ }
+ return ((u_int8_t)strtoul(ss, NULL, 16));
+}
+
+int
+parsekey(unsigned char *hexkey, size_t len, struct iked_auth *auth)
+{
+ u_int i;
+
+ bzero(auth, sizeof(*auth));
+ if ((len / 2) > sizeof(auth->auth_data))
+ return (-1);
+ auth->auth_length = len / 2;
+
+ for (i = 0; i < auth->auth_length; i++)
+ auth->auth_data[i] = x2i(hexkey + 2 * i);
+
+ return (0);
+}
+
+int
+parsekeyfile(char *filename, struct iked_auth *auth)
+{
+ struct stat sb;
+ int fd;
+ unsigned char *hex;
+
+ if ((fd = open(filename, O_RDONLY)) < 0)
+ err(1, "open %s", filename);
+ if (fstat(fd, &sb) < 0)
+ err(1, "parsekeyfile: stat %s", filename);
+ if ((sb.st_size > KEYSIZE_LIMIT) || (sb.st_size == 0))
+ errx(1, "%s: key too %s", filename, sb.st_size ? "large" :
+ "small");
+ if ((hex = calloc(sb.st_size, sizeof(unsigned char))) == NULL)
+ err(1, "parsekeyfile: calloc");
+ if (read(fd, hex, sb.st_size) < sb.st_size)
+ err(1, "parsekeyfile: read");
+ close(fd);
+ return (parsekey(hex, sb.st_size, auth));
+}
+
+int
+get_id_type(char *string)
+{
+ struct in6_addr ia;
+
+ if (string == NULL)
+ return (IKEV2_ID_NONE);
+
+ if (*string == '/')
+ return (IKEV2_ID_DER_ASN1_DN);
+ else if (inet_pton(AF_INET, string, &ia) == 1)
+ return (IKEV2_ID_IPV4_ADDR);
+ else if (inet_pton(AF_INET6, string, &ia) == 1)
+ return (IKEV2_ID_IPV6_ADDR);
+ else if (strchr(string, '@'))
+ return (IKEV2_ID_RFC822_ADDR);
+ else
+ return (IKEV2_ID_FQDN);
+}
+
+struct ipsec_addr_wrap *
+host(const char *s)
+{
+ struct ipsec_addr_wrap *ipa = NULL;
+ int mask, cont = 1;
+ char *p, *q, *ps;
+
+ if ((p = strrchr(s, '/')) != NULL) {
+ errno = 0;
+ mask = strtol(p + 1, &q, 0);
+ if (errno == ERANGE || !q || *q || mask > 128 || q == (p + 1))
+ errx(1, "host: invalid netmask '%s'", p);
+ if ((ps = malloc(strlen(s) - strlen(p) + 1)) == NULL)
+ err(1, "host: calloc");
+ strlcpy(ps, s, strlen(s) - strlen(p) + 1);
+ } else {
+ if ((ps = strdup(s)) == NULL)
+ err(1, "host: strdup");
+ mask = -1;
+ }
+
+ /* Does interface with this name exist? */
+ if (cont && (ipa = host_if(ps, mask)) != NULL)
+ cont = 0;
+
+ /* IPv4 address? */
+ if (cont && (ipa = host_v4(s, mask == -1 ? 32 : mask)) != NULL)
+ cont = 0;
+
+ /* IPv6 address? */
+ if (cont && (ipa = host_v6(ps, mask == -1 ? 128 : mask)) != NULL)
+ cont = 0;
+
+ /* dns lookup */
+ if (cont && mask == -1 && (ipa = host_dns(s, mask)) != NULL)
+ cont = 0;
+ free(ps);
+
+ if (ipa == NULL || cont == 1) {
+ fprintf(stderr, "no IP address found for %s\n", s);
+ return (NULL);
+ }
+ return (ipa);
+}
+
+struct ipsec_addr_wrap *
+host_v6(const char *s, int prefixlen)
+{
+ struct ipsec_addr_wrap *ipa = NULL;
+ struct addrinfo hints, *res;
+ char hbuf[NI_MAXHOST];
+
+ bzero(&hints, sizeof(struct addrinfo));
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICHOST;
+ if (getaddrinfo(s, NULL, &hints, &res))
+ return (NULL);
+ if (res->ai_next)
+ err(1, "host_v6: numeric hostname expanded to multiple item");
+
+ ipa = calloc(1, sizeof(struct ipsec_addr_wrap));
+ if (ipa == NULL)
+ err(1, "host_v6: calloc");
+ ipa->af = res->ai_family;
+ memcpy(&ipa->address, res->ai_addr, sizeof(struct sockaddr_in6));
+ if (prefixlen > 128)
+ prefixlen = 128;
+ ipa->next = NULL;
+ ipa->tail = ipa;
+
+ set_ipmask(ipa, prefixlen);
+ if (getnameinfo(res->ai_addr, res->ai_addrlen,
+ hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST)) {
+ errx(1, "could not get a numeric hostname");
+ }
+
+ if (prefixlen != 128) {
+ ipa->netaddress = 1;
+ asprintf(&ipa->name, "%s/%d", hbuf, prefixlen);
+ } else
+ ipa->name = strdup(hbuf);
+ if (ipa->name == NULL)
+ err(1, "host_v6: strdup");
+
+ freeaddrinfo(res);
+
+ return (ipa);
+}
+
+struct ipsec_addr_wrap *
+host_v4(const char *s, int mask)
+{
+ struct ipsec_addr_wrap *ipa = NULL;
+ struct sockaddr_in ina;
+ int bits = 32;
+
+ bzero(&ina, sizeof(ina));
+ if (strrchr(s, '/') != NULL) {
+ if ((bits = inet_net_pton(AF_INET, s, &ina.sin_addr,
+ sizeof(ina.sin_addr))) == -1)
+ return (NULL);
+ } else {
+ if (inet_pton(AF_INET, s, &ina.sin_addr) != 1)
+ return (NULL);
+ }
+
+ ipa = calloc(1, sizeof(struct ipsec_addr_wrap));
+ if (ipa == NULL)
+ err(1, "host_v4: calloc");
+
+ ina.sin_family = AF_INET;
+ ina.sin_len = sizeof(ina);
+ memcpy(&ipa->address, &ina, sizeof(ina));
+
+ ipa->name = strdup(s);
+ if (ipa->name == NULL)
+ err(1, "host_v4: strdup");
+ ipa->af = AF_INET;
+ ipa->next = NULL;
+ ipa->tail = ipa;
+
+ set_ipmask(ipa, bits);
+ if (strrchr(s, '/') != NULL)
+ ipa->netaddress = 1;
+
+ return (ipa);
+}
+
+struct ipsec_addr_wrap *
+host_dns(const char *s, int mask)
+{
+ struct ipsec_addr_wrap *ipa = NULL, *head = NULL;
+ struct addrinfo hints, *res0, *res;
+ int error;
+ char hbuf[NI_MAXHOST];
+
+ bzero(&hints, sizeof(struct addrinfo));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ error = getaddrinfo(s, NULL, &hints, &res0);
+ if (error)
+ return (NULL);
+
+ for (res = res0; res; res = res->ai_next) {
+ if (res->ai_family != AF_INET && res->ai_family != AF_INET6)
+ continue;
+
+ ipa = calloc(1, sizeof(struct ipsec_addr_wrap));
+ if (ipa == NULL)
+ err(1, "host_dns: calloc");
+ switch (res->ai_family) {
+ case AF_INET:
+ memcpy(&ipa->address, res->ai_addr,
+ sizeof(struct sockaddr_in));
+ break;
+ case AF_INET6:
+ memcpy(&ipa->address, res->ai_addr,
+ sizeof(struct sockaddr_in6));
+ break;
+ }
+ error = getnameinfo(res->ai_addr, res->ai_addrlen, hbuf,
+ sizeof(hbuf), NULL, 0, NI_NUMERICHOST);
+ if (error)
+ err(1, "host_dns: getnameinfo");
+ ipa->name = strdup(hbuf);
+ if (ipa->name == NULL)
+ err(1, "host_dns: strdup");
+ ipa->af = res->ai_family;
+ ipa->next = NULL;
+ ipa->tail = ipa;
+ if (head == NULL)
+ head = ipa;
+ else {
+ head->tail->next = ipa;
+ head->tail = ipa;
+ }
+
+ /*
+ * XXX for now, no netmask support for IPv6.
+ * but since there's no way to specify address family, once you
+ * have IPv6 address on a host, you cannot use dns/netmask
+ * syntax.
+ */
+ if (ipa->af == AF_INET)
+ set_ipmask(ipa, mask == -1 ? 32 : mask);
+ else
+ if (mask != -1)
+ err(1, "host_dns: cannot apply netmask "
+ "on non-IPv4 address");
+ }
+ freeaddrinfo(res0);
+
+ return (head);
+}
+
+struct ipsec_addr_wrap *
+host_if(const char *s, int mask)
+{
+ struct ipsec_addr_wrap *ipa = NULL;
+
+ if (ifa_exists(s))
+ ipa = ifa_lookup(s);
+
+ return (ipa);
+}
+
+struct ipsec_addr_wrap *
+host_any(void)
+{
+ struct ipsec_addr_wrap *ipa;
+
+ ipa = calloc(1, sizeof(struct ipsec_addr_wrap));
+ if (ipa == NULL)
+ err(1, "host_any: calloc");
+ ipa->af = AF_UNSPEC;
+ ipa->netaddress = 1;
+ ipa->tail = ipa;
+ return (ipa);
+}
+
+/* interface lookup routintes */
+
+struct ipsec_addr_wrap *iftab;
+
+void
+ifa_load(void)
+{
+ struct ifaddrs *ifap, *ifa;
+ struct ipsec_addr_wrap *n = NULL, *h = NULL;
+
+ if (getifaddrs(&ifap) < 0)
+ err(1, "ifa_load: getifaddrs");
+
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if (!(ifa->ifa_addr->sa_family == AF_INET ||
+ ifa->ifa_addr->sa_family == AF_INET6 ||
+ ifa->ifa_addr->sa_family == AF_LINK))
+ continue;
+ n = calloc(1, sizeof(struct ipsec_addr_wrap));
+ if (n == NULL)
+ err(1, "ifa_load: calloc");
+ n->af = ifa->ifa_addr->sa_family;
+ if ((n->name = strdup(ifa->ifa_name)) == NULL)
+ err(1, "ifa_load: strdup");
+ if (n->af == AF_INET) {
+ n->af = AF_INET;
+ memcpy(&n->address, ifa->ifa_addr,
+ sizeof(struct sockaddr_in));
+ memcpy(&n->mask, ifa->ifa_addr,
+ sizeof(struct sockaddr_in));
+ } else if (n->af == AF_INET6) {
+ n->af = AF_INET6;
+ memcpy(&n->address, ifa->ifa_addr,
+ sizeof(struct sockaddr_in6));
+ memcpy(&n->mask, ifa->ifa_addr,
+ sizeof(struct sockaddr_in6));
+ }
+ n->next = NULL;
+ n->tail = n;
+ if (h == NULL)
+ h = n;
+ else {
+ h->tail->next = n;
+ h->tail = n;
+ }
+ }
+
+ iftab = h;
+ freeifaddrs(ifap);
+}
+
+int
+ifa_exists(const char *ifa_name)
+{
+ struct ipsec_addr_wrap *n;
+ struct ifgroupreq ifgr;
+ int s;
+
+ if (iftab == NULL)
+ ifa_load();
+
+ /* check wether this is a group */
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ err(1, "ifa_exists: socket");
+ bzero(&ifgr, sizeof(ifgr));
+ strlcpy(ifgr.ifgr_name, ifa_name, sizeof(ifgr.ifgr_name));
+ if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == 0) {
+ close(s);
+ return (1);
+ }
+ close(s);
+
+ for (n = iftab; n; n = n->next) {
+ if (n->af == AF_LINK && !strncmp(n->name, ifa_name,
+ IFNAMSIZ))
+ return (1);
+ }
+
+ return (0);
+}
+
+struct ipsec_addr_wrap *
+ifa_grouplookup(const char *ifa_name)
+{
+ struct ifg_req *ifg;
+ struct ifgroupreq ifgr;
+ int s;
+ size_t len;
+ struct ipsec_addr_wrap *n, *h = NULL, *hn;
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ err(1, "socket");
+ bzero(&ifgr, sizeof(ifgr));
+ strlcpy(ifgr.ifgr_name, ifa_name, sizeof(ifgr.ifgr_name));
+ if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1) {
+ close(s);
+ return (NULL);
+ }
+
+ len = ifgr.ifgr_len;
+ if ((ifgr.ifgr_groups = calloc(1, len)) == NULL)
+ err(1, "calloc");
+ if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1)
+ err(1, "ioctl");
+
+ for (ifg = ifgr.ifgr_groups; ifg && len >= sizeof(struct ifg_req);
+ ifg++) {
+ len -= sizeof(struct ifg_req);
+ if ((n = ifa_lookup(ifg->ifgrq_member)) == NULL)
+ continue;
+ if (h == NULL)
+ h = n;
+ else {
+ for (hn = h; hn->next != NULL; hn = hn->next)
+ ; /* nothing */
+ hn->next = n;
+ n->tail = hn;
+ }
+ }
+ free(ifgr.ifgr_groups);
+ close(s);
+
+ return (h);
+}
+
+struct ipsec_addr_wrap *
+ifa_lookup(const char *ifa_name)
+{
+ struct ipsec_addr_wrap *p = NULL, *h = NULL, *n = NULL;
+ struct sockaddr_in6 *in6;
+ u_int8_t *s6;
+
+ if (iftab == NULL)
+ ifa_load();
+
+ if ((n = ifa_grouplookup(ifa_name)) != NULL)
+ return (n);
+
+ for (p = iftab; p; p = p->next) {
+ if (p->af != AF_INET && p->af != AF_INET6)
+ continue;
+ if (strncmp(p->name, ifa_name, IFNAMSIZ))
+ continue;
+ n = calloc(1, sizeof(struct ipsec_addr_wrap));
+ if (n == NULL)
+ err(1, "ifa_lookup: calloc");
+ memcpy(n, p, sizeof(struct ipsec_addr_wrap));
+ if ((n->name = strdup(p->name)) == NULL)
+ err(1, "ifa_lookup: strdup");
+ switch (n->af) {
+ case AF_INET:
+ set_ipmask(n, 32);
+ break;
+ case AF_INET6:
+ in6 = (struct sockaddr_in6 *)&n->address;
+ s6 = (u_int8_t *)&in6->sin6_addr.s6_addr;
+
+ /* route/show.c and bgpd/util.c give KAME credit */
+ if (IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) {
+ u_int16_t tmp16;
+
+ /* for now we can not handle link local,
+ * therefore bail for now
+ */
+ free(n);
+ continue;
+
+ memcpy(&tmp16, &s6[2], sizeof(tmp16));
+ /* use this when we support link-local
+ * n->??.scopeid = ntohs(tmp16);
+ */
+ s6[2] = 0;
+ s6[3] = 0;
+ }
+ set_ipmask(n, 128);
+ break;
+ }
+
+ n->next = NULL;
+ n->tail = n;
+ if (h == NULL)
+ h = n;
+ else {
+ h->tail->next = n;
+ h->tail = n;
+ }
+ }
+
+ return (h);
+}
+
+void
+set_ipmask(struct ipsec_addr_wrap *address, u_int8_t b)
+{
+ address->mask = b;
+}
+
+const struct ipsec_xf *
+parse_xf(const char *name, u_int length, const struct ipsec_xf xfs[])
+{
+ int i;
+
+ for (i = 0; xfs[i].name != NULL; i++) {
+ if (strncmp(name, xfs[i].name, strlen(name)))
+ continue;
+ if (length == 0 || length == xfs[i].length)
+ return &xfs[i];
+ }
+ return (NULL);
+}
+
+const char *
+print_xf(u_int id, u_int length, const struct ipsec_xf xfs[])
+{
+ int i;
+
+ for (i = 0; xfs[i].name != NULL; i++) {
+ if (xfs[i].id == id) {
+ if (length == 0 || length == xfs[i].length)
+ return (xfs[i].name);
+ }
+ }
+ return ("unknown");
+}
+
+size_t
+keylength_xf(u_int saproto, u_int type, u_int id)
+{
+ int i;
+ const struct ipsec_xf *xfs;
+
+ switch (type) {
+ case IKEV2_XFORMTYPE_ENCR:
+ if (saproto == IKEV2_SAPROTO_IKE)
+ xfs = ikeencxfs;
+ else
+ xfs = ipsecencxfs;
+ break;
+ case IKEV2_XFORMTYPE_INTEGR:
+ xfs = authxfs;
+ break;
+ default:
+ return (0);
+ }
+
+ for (i = 0; xfs[i].name != NULL; i++) {
+ if (xfs[i].id == id)
+ return (xfs[i].length * 8);
+ }
+ return (0);
+}
+
+void
+print_user(struct iked_user *usr)
+{
+ print_verbose("user \"%s\" \"%s\"\n", usr->usr_name, usr->usr_pass);
+}
+
+void
+print_policy(struct iked_policy *pol)
+{
+ struct iked_proposal *pp;
+ struct iked_transform *xform;
+ struct iked_flow *flow;
+ struct iked_cfg *cfg;
+ u_int i, j;
+ const struct ipsec_xf *xfs = NULL;
+
+ print_verbose("ikev2");
+
+ if (pol->pol_name[0] != '\0')
+ print_verbose(" \"%s\"", pol->pol_name);
+
+ if (pol->pol_flags & IKED_POLICY_DEFAULT)
+ print_verbose(" default");
+ if (pol->pol_flags & IKED_POLICY_ACTIVE)
+ print_verbose(" active");
+ else
+ print_verbose(" passive");
+
+ print_verbose(" %s", print_xf(pol->pol_saproto, 0, saxfs));
+
+ if (pol->pol_ipproto)
+ print_verbose(" proto %s", print_proto(pol->pol_ipproto));
+
+ TAILQ_FOREACH(flow, &pol->pol_flows, flow_entry) {
+ print_verbose(" from %s",
+ print_host(&flow->flow_src.addr, NULL, 0));
+ if (flow->flow_src.addr_af != AF_UNSPEC &&
+ flow->flow_src.addr_net)
+ print_verbose("/%d", flow->flow_src.addr_mask);
+ if (flow->flow_src.addr_port)
+ print_verbose(" port %d",
+ ntohs(flow->flow_src.addr_port));
+
+ print_verbose(" to %s",
+ print_host(&flow->flow_dst.addr, NULL, 0));
+ if (flow->flow_dst.addr_af != AF_UNSPEC &&
+ flow->flow_dst.addr_net)
+ print_verbose("/%d", flow->flow_dst.addr_mask);
+ if (flow->flow_dst.addr_port)
+ print_verbose(" port %d",
+ ntohs(flow->flow_dst.addr_port));
+ }
+
+ if ((pol->pol_flags & IKED_POLICY_DEFAULT) == 0) {
+ print_verbose(" local %s",
+ print_host(&pol->pol_local, NULL, 0));
+ if (pol->pol_local.ss_family != AF_UNSPEC &&
+ pol->pol_localnet)
+ print_verbose("/%d", pol->pol_localmask);
+
+ print_verbose(" peer %s",
+ print_host(&pol->pol_peer, NULL, 0));
+ if (pol->pol_peer.ss_family != AF_UNSPEC &&
+ pol->pol_peernet)
+ print_verbose("/%d", pol->pol_peermask);
+ }
+
+ TAILQ_FOREACH(pp, &pol->pol_proposals, prop_entry) {
+ if (!pp->prop_nxforms)
+ continue;
+ if (pp->prop_protoid == IKEV2_SAPROTO_IKE)
+ print_verbose(" ikesa");
+ else
+ print_verbose(" childsa");
+
+ for (j = 0; ikev2_xformtype_map[j].cm_type != 0; j++) {
+ xfs = NULL;
+
+ for (i = 0; i < pp->prop_nxforms; i++) {
+ xform = pp->prop_xforms + i;
+
+ if (xform->xform_type !=
+ ikev2_xformtype_map[j].cm_type)
+ continue;
+
+ if (xfs != NULL) {
+ print_verbose(",");
+ } else {
+ switch (xform->xform_type) {
+ case IKEV2_XFORMTYPE_INTEGR:
+ print_verbose(" auth ");
+ xfs = authxfs;
+ break;
+ case IKEV2_XFORMTYPE_ENCR:
+ print_verbose(" enc ");
+ if (pp->prop_protoid ==
+ IKEV2_SAPROTO_IKE)
+ xfs = ikeencxfs;
+ else
+ xfs = ipsecencxfs;
+ break;
+ case IKEV2_XFORMTYPE_PRF:
+ print_verbose(" prf ");
+ xfs = prfxfs;
+ break;
+ case IKEV2_XFORMTYPE_DH:
+ print_verbose(" group ");
+ xfs = groupxfs;
+ break;
+ default:
+ continue;
+ }
+ }
+
+ print_verbose("%s", print_xf(xform->xform_id,
+ xform->xform_length / 8, xfs));
+ }
+ }
+ }
+
+ if (pol->pol_localid.id_length != 0)
+ print_verbose(" srcid %s", pol->pol_localid.id_data);
+ if (pol->pol_peerid.id_length != 0)
+ print_verbose(" dstid %s", pol->pol_peerid.id_data);
+
+ if (pol->pol_auth.auth_method == IKEV2_AUTH_SHARED_KEY_MIC) {
+ print_verbose(" psk 0x");
+ for (i = 0; i < pol->pol_auth.auth_length; i++)
+ print_verbose("%02x",
+ pol->pol_auth.auth_data[i]);
+ } else {
+ if (pol->pol_auth.auth_eap)
+ print_verbose(" eap \"%s\"",
+ print_map(pol->pol_auth.auth_eap, eap_type_map));
+ else
+ print_verbose(" %s",
+ print_xf(pol->pol_auth.auth_method, 0, methodxfs));
+ }
+
+ for (i = 0; i < pol->pol_ncfg; i++) {
+ cfg = &pol->pol_cfg[i];
+ print_verbose(" config %s %s", print_xf(cfg->cfg_type,
+ cfg->cfg.address.addr_af, cpxfs),
+ print_host(&cfg->cfg.address.addr, NULL, 0));
+ }
+
+ if (pol->pol_tag[0] != '\0')
+ print_verbose(" tag \"%s\"", pol->pol_tag);
+
+ print_verbose("\n");
+}
+
+void
+copy_transforms(u_int type, const struct ipsec_xf *xf,
+ const struct ipsec_xf *xfs,
+ struct iked_transform *dst, size_t ndst,
+ u_int *n, struct iked_transform *src, size_t nsrc)
+{
+ u_int i;
+ struct iked_transform *a, *b;
+
+ if (xf != NULL) {
+ if (*n >= ndst)
+ return;
+ b = dst + (*n)++;
+
+ b->xform_type = type;
+ b->xform_id = xf->id;
+ b->xform_keylength = xf->length * 8;
+ b->xform_length = xf->keylength * 8;
+ return;
+ }
+
+ for (i = 0; i < nsrc; i++) {
+ a = src + i;
+ if (a->xform_type != type)
+ continue;
+ if (*n >= ndst)
+ return;
+ b = dst + (*n)++;
+ memcpy(b, a, sizeof(*b));
+ }
+}
+
+int
+create_ike(char *name, u_int8_t ipproto, struct ipsec_hosts *hosts,
+ struct ipsec_hosts *peers, struct ipsec_mode *ike_sa,
+ struct ipsec_mode *ipsec_sa, u_int8_t saproto,
+ u_int8_t mode, char *srcid, char *dstid,
+ struct iked_auth *authtype, char *tag, struct ipsec_addr_wrap *ikecfg)
+{
+ struct ipsec_addr_wrap *ipa, *ipb;
+ struct iked_policy pol;
+ struct iked_proposal prop[2];
+ u_int j;
+ struct iked_transform ikexforms[64], espxforms[64];
+ struct iked_flow flows[64];
+ static u_int policy_id = 0;
+ struct iked_cfg *cfg;
+
+ bzero(&pol, sizeof(pol));
+ bzero(&prop, sizeof(prop));
+
+ pol.pol_id = ++policy_id;
+ pol.pol_saproto = saproto;
+ pol.pol_ipproto = ipproto;
+ pol.pol_flags = mode;
+ memcpy(&pol.pol_auth, authtype, sizeof(struct iked_auth));
+
+ if (name != NULL) {
+ if (strlcpy(pol.pol_name, name,
+ sizeof(pol.pol_name)) >= sizeof(pol.pol_name)) {
+ yyerror("name too long");
+ return (-1);
+ }
+ } else {
+ snprintf(pol.pol_name, sizeof(pol.pol_name),
+ "policy%d", policy_id);
+ }
+
+ if (srcid) {
+ pol.pol_localid.id_type = get_id_type(srcid);
+ pol.pol_localid.id_length = strlen(srcid);
+ if (strlcpy((char *)pol.pol_localid.id_data,
+ srcid, IKED_ID_SIZE) >= IKED_ID_SIZE) {
+ yyerror("srcid too long");
+ return (-1);
+ }
+ }
+ if (dstid) {
+ pol.pol_peerid.id_type = get_id_type(dstid);
+ pol.pol_peerid.id_length = strlen(dstid);
+ if (strlcpy((char *)pol.pol_peerid.id_data,
+ dstid, IKED_ID_SIZE) >= IKED_ID_SIZE) {
+ yyerror("dstid too long");
+ return (-1);
+ }
+ }
+ if (tag)
+ strlcpy(pol.pol_tag, tag, sizeof(pol.pol_tag));
+
+ if (peers == NULL) {
+ if (pol.pol_flags & IKED_POLICY_ACTIVE) {
+ yyerror("active mode requires peer specification");
+ return (-1);
+ }
+ pol.pol_flags |= IKED_POLICY_DEFAULT;
+ }
+
+ if (peers && peers->src && peers->dst &&
+ peers->src->af != peers->dst->af)
+ fatalx("create_ike: address family mismatch");
+
+ ipa = ipb = NULL;
+ if (peers) {
+ if (peers->src)
+ ipa = peers->src;
+ if (peers->dst)
+ ipb = peers->dst;
+ if (ipa == NULL && ipb == NULL) {
+ if (hosts->src && hosts->src->next == NULL)
+ ipa = hosts->src;
+ if (hosts->dst && hosts->dst->next == NULL)
+ ipb = hosts->dst;
+ }
+ }
+ if (ipa == NULL && ipb == NULL) {
+ yyerror("could not get local/peer specification");
+ return (-1);
+ }
+ if (pol.pol_flags & IKED_POLICY_ACTIVE) {
+ if (ipb == NULL || ipb->netaddress ||
+ (ipa != NULL && ipa->netaddress)) {
+ yyerror("active mode requires local/peer address");
+ return (-1);
+ }
+ }
+ if (ipa) {
+ memcpy(&pol.pol_local, &ipa->address, sizeof(pol.pol_local));
+ pol.pol_localmask = ipa->mask;
+ pol.pol_localnet = ipa->netaddress;
+ }
+ if (ipb) {
+ memcpy(&pol.pol_peer, &ipb->address, sizeof(pol.pol_peer));
+ pol.pol_peermask = ipb->mask;
+ pol.pol_peernet = ipb->netaddress;
+ }
+
+ TAILQ_INIT(&pol.pol_proposals);
+ TAILQ_INIT(&pol.pol_flows);
+
+ prop[0].prop_id = ++pol.pol_nproposals;
+ prop[0].prop_protoid = IKEV2_SAPROTO_IKE;
+ if (ike_sa == NULL || ike_sa->xfs == NULL) {
+ prop[0].prop_nxforms = ikev2_default_nike_transforms;
+ prop[0].prop_xforms = ikev2_default_ike_transforms;
+ } else {
+ j = 0;
+ copy_transforms(IKEV2_XFORMTYPE_INTEGR,
+ ike_sa->xfs->authxf, authxfs,
+ ikexforms, nitems(ikexforms), &j,
+ ikev2_default_ike_transforms,
+ ikev2_default_nike_transforms);
+ copy_transforms(IKEV2_XFORMTYPE_ENCR,
+ ike_sa->xfs->encxf, ikeencxfs,
+ ikexforms, nitems(ikexforms), &j,
+ ikev2_default_ike_transforms,
+ ikev2_default_nike_transforms);
+ copy_transforms(IKEV2_XFORMTYPE_DH,
+ ike_sa->xfs->groupxf, groupxfs,
+ ikexforms, nitems(ikexforms), &j,
+ ikev2_default_ike_transforms,
+ ikev2_default_nike_transforms);
+ copy_transforms(IKEV2_XFORMTYPE_PRF,
+ ike_sa->xfs->prfxf, prfxfs,
+ ikexforms, nitems(ikexforms), &j,
+ ikev2_default_ike_transforms,
+ ikev2_default_nike_transforms);
+ prop[0].prop_nxforms = j;
+ prop[0].prop_xforms = ikexforms;
+ }
+ TAILQ_INSERT_TAIL(&pol.pol_proposals, &prop[0], prop_entry);
+
+ prop[1].prop_id = ++pol.pol_nproposals;
+ prop[1].prop_protoid = saproto;
+ if (ipsec_sa == NULL || ipsec_sa->xfs == NULL) {
+ prop[1].prop_nxforms = ikev2_default_nesp_transforms;
+ prop[1].prop_xforms = ikev2_default_esp_transforms;
+ } else {
+ j = 0;
+ copy_transforms(IKEV2_XFORMTYPE_INTEGR,
+ ipsec_sa->xfs->authxf, authxfs,
+ espxforms, nitems(espxforms), &j,
+ ikev2_default_esp_transforms,
+ ikev2_default_nesp_transforms);
+ copy_transforms(IKEV2_XFORMTYPE_ENCR,
+ ipsec_sa->xfs->encxf, ipsecencxfs,
+ espxforms, nitems(espxforms), &j,
+ ikev2_default_esp_transforms,
+ ikev2_default_nesp_transforms);
+ copy_transforms(IKEV2_XFORMTYPE_DH,
+ ipsec_sa->xfs->groupxf, groupxfs,
+ espxforms, nitems(espxforms), &j,
+ ikev2_default_esp_transforms,
+ ikev2_default_nesp_transforms);
+ copy_transforms(IKEV2_XFORMTYPE_ESN,
+ NULL, NULL,
+ espxforms, nitems(espxforms), &j,
+ ikev2_default_esp_transforms,
+ ikev2_default_nesp_transforms);
+ prop[1].prop_nxforms = j;
+ prop[1].prop_xforms = espxforms;
+ }
+ TAILQ_INSERT_TAIL(&pol.pol_proposals, &prop[1], prop_entry);
+
+ if (hosts == NULL || hosts->src == NULL || hosts->dst == NULL)
+ fatalx("create_ike: no traffic selectors/flows");
+
+ for (j = 0, ipa = hosts->src, ipb = hosts->dst; ipa && ipb;
+ ipa = ipa->next, ipb = ipb->next, j++) {
+ memcpy(&flows[j].flow_src.addr, &ipa->address,
+ sizeof(ipa->address));
+ flows[j].flow_src.addr_af = ipa->af;
+ flows[j].flow_src.addr_mask = ipa->mask;
+ flows[j].flow_src.addr_net = ipa->netaddress;
+ flows[j].flow_src.addr_port = hosts->sport;
+
+ memcpy(&flows[j].flow_dst.addr, &ipb->address,
+ sizeof(ipb->address));
+ flows[j].flow_dst.addr_af = ipb->af;
+ flows[j].flow_dst.addr_mask = ipb->mask;
+ flows[j].flow_dst.addr_net = ipb->netaddress;
+ flows[j].flow_dst.addr_port = hosts->dport;
+
+ pol.pol_nflows++;
+ TAILQ_INSERT_TAIL(&pol.pol_flows, &flows[j], flow_entry);
+ }
+
+ for (j = 0, ipa = ikecfg; ipa; ipa = ipa->next, j++) {
+ if (j >= IKED_CFG_MAX)
+ break;
+ cfg = &pol.pol_cfg[j];
+ pol.pol_ncfg++;
+
+ cfg->cfg_action = ipa->action;
+ cfg->cfg_type = ipa->type;
+ memcpy(&cfg->cfg.address.addr, &ipa->address,
+ sizeof(ipa->address));
+ cfg->cfg.address.addr_mask = ipa->mask;
+ cfg->cfg.address.addr_net = ipa->netaddress;
+ cfg->cfg.address.addr_af = ipa->af;
+ }
+
+ config_setpolicy(env, &pol, PROC_IKEV2);
+
+ rules++;
+ return (0);
+}
+
+int
+create_user(const char *user, const char *pass)
+{
+ struct iked_user usr;
+
+ bzero(&usr, sizeof(usr));
+
+ if (*user == '\0' || (strlcpy(usr.usr_name, user,
+ sizeof(usr.usr_name)) >= sizeof(usr.usr_name))) {
+ yyerror("invalid user name");
+ return (-1);
+ }
+ if (*pass == '\0' || (strlcpy(usr.usr_pass, pass,
+ sizeof(usr.usr_pass)) >= sizeof(usr.usr_pass))) {
+ yyerror("invalid password");
+ return (-1);
+ }
+
+ config_setuser(env, &usr, PROC_IKEV2);
+
+ rules++;
+ return (0);
+}
diff --git a/sbin/iked/pfkey.c b/sbin/iked/pfkey.c
new file mode 100644
index 00000000000..42dd40623bb
--- /dev/null
+++ b/sbin/iked/pfkey.c
@@ -0,0 +1,1082 @@
+/* $OpenBSD: pfkey.c,v 1.1 2010/06/03 16:41:12 reyk Exp $ */
+/* $vantronix: pfkey.c,v 1.11 2010/06/03 07:57:33 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
+ * Copyright (c) 2004, 2005 Hans-Joerg Hoexer <hshoexer@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2003, 2004 Markus Friedl <markus@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/uio.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <netinet/ip_ipsp.h>
+#include <net/pfkeyv2.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <event.h>
+
+#include "iked.h"
+#include "ikev2.h"
+
+#define ROUNDUP(x) (((x) + (PFKEYV2_CHUNK - 1)) & ~(PFKEYV2_CHUNK - 1))
+#define IOV_CNT 20
+
+#define PFKEYV2_CHUNK sizeof(u_int64_t)
+
+static u_int32_t sadb_msg_seq = 1;
+
+struct pfkey_constmap {
+ u_int8_t pfkey_id;
+ u_int pfkey_ikeid;
+ u_int pfkey_fixedkey;
+};
+
+static const struct pfkey_constmap pfkey_encr[] = {
+ { SADB_X_EALG_DES_IV64, IKEV2_XFORMENCR_DES_IV64 },
+ { SADB_EALG_DESCBC, IKEV2_XFORMENCR_DES },
+ { SADB_EALG_3DESCBC, IKEV2_XFORMENCR_3DES },
+ { SADB_X_EALG_RC5, IKEV2_XFORMENCR_RC5 },
+ { SADB_X_EALG_IDEA, IKEV2_XFORMENCR_IDEA },
+ { SADB_X_EALG_CAST, IKEV2_XFORMENCR_CAST },
+ { SADB_X_EALG_BLF, IKEV2_XFORMENCR_BLOWFISH },
+ { SADB_X_EALG_3IDEA, IKEV2_XFORMENCR_3IDEA },
+ { SADB_X_EALG_DES_IV32, IKEV2_XFORMENCR_DES_IV32 },
+ { SADB_X_EALG_RC4, IKEV2_XFORMENCR_RC4 },
+ { SADB_EALG_NULL, IKEV2_XFORMENCR_NULL },
+ { SADB_X_EALG_AES, IKEV2_XFORMENCR_AES_CBC },
+ { SADB_X_EALG_AESCTR, IKEV2_XFORMENCR_AES_CTR },
+ { 0 }
+};
+
+static const struct pfkey_constmap pfkey_integr[] = {
+ { SADB_AALG_MD5HMAC, IKEV2_XFORMAUTH_HMAC_MD5_96 },
+ { SADB_AALG_SHA1HMAC, IKEV2_XFORMAUTH_HMAC_SHA1_96 },
+ { SADB_X_AALG_DES, IKEV2_XFORMAUTH_DES_MAC },
+ { SADB_X_AALG_SHA2_256, IKEV2_XFORMAUTH_HMAC_SHA2_256_128 },
+ { SADB_X_AALG_SHA2_384, IKEV2_XFORMAUTH_HMAC_SHA2_384_192 },
+ { SADB_X_AALG_SHA2_512, IKEV2_XFORMAUTH_HMAC_SHA2_512_256 },
+ { 0 }
+};
+
+static const struct pfkey_constmap pfkey_satype[] = {
+ { SADB_SATYPE_AH, IKEV2_SAPROTO_AH },
+ { SADB_SATYPE_ESP, IKEV2_SAPROTO_ESP },
+ { 0 }
+};
+
+int pfkey_map(const struct pfkey_constmap *, u_int16_t, u_int8_t *);
+int pfkey_flow(int, u_int8_t, u_int8_t, struct iked_flow *);
+int pfkey_sa(int, u_int8_t, u_int8_t, struct iked_childsa *);
+int pfkey_sa_getspi(int, u_int8_t, struct iked_childsa *, u_int32_t *);
+int pfkey_sagroup(int, u_int8_t, u_int8_t,
+ struct iked_childsa *, struct iked_childsa *);
+int pfkey_reply(int, u_int8_t **, ssize_t *);
+struct sadb_ident *
+ pfkey_id2ident(struct iked_id *, u_int);
+
+int
+pfkey_map(const struct pfkey_constmap *map, u_int16_t alg, u_int8_t *pfkalg)
+{
+ int i;
+
+ for (i = 0; map[i].pfkey_id != 0; i++)
+ if (map[i].pfkey_ikeid == alg) {
+ *pfkalg = map[i].pfkey_id;
+ return (0);
+ }
+ return (-1);
+}
+
+int
+pfkey_flow(int sd, u_int8_t satype, u_int8_t action, struct iked_flow *flow)
+{
+ struct sadb_msg smsg;
+ struct sadb_address sa_src, sa_dst, sa_local, sa_peer, sa_smask,
+ sa_dmask;
+ struct sadb_protocol sa_flowtype, sa_protocol;
+ struct sadb_ident *sa_srcid, *sa_dstid;
+ struct sockaddr_storage ssrc, sdst, slocal, speer, smask, dmask;
+ struct iovec iov[IOV_CNT];
+ ssize_t n;
+ int iov_cnt, len, ret = -1;
+ in_port_t sport, dport;
+
+ sport = dport = 0;
+ sa_srcid = sa_dstid = NULL;
+
+ bzero(&ssrc, sizeof(ssrc));
+ bzero(&smask, sizeof(smask));
+ memcpy(&ssrc, &flow->flow_src.addr, sizeof(ssrc));
+ memcpy(&smask, &flow->flow_src.addr, sizeof(smask));
+ if ((sport = flow->flow_src.addr_port) != 0)
+ dport = 0xffff;
+ socket_af((struct sockaddr *)&ssrc, sport);
+ socket_af((struct sockaddr *)&smask, dport);
+
+ switch (flow->flow_src.addr_af) {
+ case AF_INET:
+ ((struct sockaddr_in *)&smask)->sin_addr.s_addr =
+ prefixlen2mask(flow->flow_src.addr_net ?
+ flow->flow_src.addr_mask : 32);
+ break;
+ case AF_INET6:
+ prefixlen2mask6(flow->flow_src.addr_net ?
+ flow->flow_src.addr_mask : 128,
+ (u_int32_t *)((struct sockaddr_in6 *)
+ &smask)->sin6_addr.s6_addr);
+ break;
+ default:
+ log_warnx("%s: unsupported address family %d",
+ __func__, flow->flow_src.addr_af);
+ return (-1);
+ }
+ smask.ss_len = ssrc.ss_len;
+
+ bzero(&sdst, sizeof(sdst));
+ bzero(&dmask, sizeof(dmask));
+ memcpy(&sdst, &flow->flow_dst.addr, sizeof(sdst));
+ memcpy(&dmask, &flow->flow_dst.addr, sizeof(dmask));
+ if ((sport = flow->flow_dst.addr_port) != 0)
+ dport = 0xffff;
+ socket_af((struct sockaddr *)&sdst, sport);
+ socket_af((struct sockaddr *)&dmask, dport);
+
+ switch (flow->flow_dst.addr_af) {
+ case AF_INET:
+ ((struct sockaddr_in *)&dmask)->sin_addr.s_addr =
+ prefixlen2mask(flow->flow_dst.addr_net ?
+ flow->flow_dst.addr_mask : 32);
+ break;
+ case AF_INET6:
+ prefixlen2mask6(flow->flow_dst.addr_net ?
+ flow->flow_dst.addr_mask : 128,
+ (u_int32_t *)((struct sockaddr_in6 *)
+ &dmask)->sin6_addr.s6_addr);
+ break;
+ default:
+ log_warnx("%s: unsupported address family %d",
+ __func__, flow->flow_dst.addr_af);
+ return (-1);
+ }
+ dmask.ss_len = sdst.ss_len;
+
+ bzero(&slocal, sizeof(slocal));
+ bzero(&speer, sizeof(speer));
+ if (action != SADB_X_DELFLOW) {
+ memcpy(&slocal, &flow->flow_local->addr, sizeof(slocal));
+ socket_af((struct sockaddr *)&slocal, 0);
+
+ memcpy(&speer, &flow->flow_peer->addr, sizeof(speer));
+ socket_af((struct sockaddr *)&speer, 0);
+ }
+
+ bzero(&smsg, sizeof(smsg));
+ smsg.sadb_msg_version = PF_KEY_V2;
+ smsg.sadb_msg_seq = sadb_msg_seq++;
+ smsg.sadb_msg_pid = getpid();
+ smsg.sadb_msg_len = sizeof(smsg) / 8;
+ smsg.sadb_msg_type = action;
+ smsg.sadb_msg_satype = satype;
+
+ bzero(&sa_flowtype, sizeof(sa_flowtype));
+ sa_flowtype.sadb_protocol_exttype = SADB_X_EXT_FLOW_TYPE;
+ sa_flowtype.sadb_protocol_len = sizeof(sa_flowtype) / 8;
+ sa_flowtype.sadb_protocol_direction = flow->flow_dir;
+ sa_flowtype.sadb_protocol_proto =
+ flow->flow_dir == IPSP_DIRECTION_IN ?
+ SADB_X_FLOW_TYPE_USE : SADB_X_FLOW_TYPE_REQUIRE;
+
+ bzero(&sa_protocol, sizeof(sa_protocol));
+ sa_protocol.sadb_protocol_exttype = SADB_X_EXT_PROTOCOL;
+ sa_protocol.sadb_protocol_len = sizeof(sa_protocol) / 8;
+ sa_protocol.sadb_protocol_direction = 0;
+ sa_protocol.sadb_protocol_proto = flow->flow_ipproto;
+
+ bzero(&sa_src, sizeof(sa_src));
+ sa_src.sadb_address_exttype = SADB_X_EXT_SRC_FLOW;
+ sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8;
+
+ bzero(&sa_smask, sizeof(sa_smask));
+ sa_smask.sadb_address_exttype = SADB_X_EXT_SRC_MASK;
+ sa_smask.sadb_address_len =
+ (sizeof(sa_smask) + ROUNDUP(smask.ss_len)) / 8;
+
+ bzero(&sa_dst, sizeof(sa_dst));
+ sa_dst.sadb_address_exttype = SADB_X_EXT_DST_FLOW;
+ sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
+
+ bzero(&sa_dmask, sizeof(sa_dmask));
+ sa_dmask.sadb_address_exttype = SADB_X_EXT_DST_MASK;
+ sa_dmask.sadb_address_len =
+ (sizeof(sa_dmask) + ROUNDUP(dmask.ss_len)) / 8;
+
+ if (action != SADB_X_DELFLOW) {
+ /* local address */
+ bzero(&sa_local, sizeof(sa_local));
+ sa_local.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+ sa_local.sadb_address_len =
+ (sizeof(sa_local) + ROUNDUP(slocal.ss_len)) / 8;
+
+ /* peer address */
+ bzero(&sa_peer, sizeof(sa_peer));
+ sa_peer.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+ sa_peer.sadb_address_len =
+ (sizeof(sa_peer) + ROUNDUP(speer.ss_len)) / 8;
+
+ /* local id */
+ sa_srcid = pfkey_id2ident(flow->flow_srcid,
+ SADB_EXT_IDENTITY_SRC);
+
+ /* peer id */
+ sa_dstid = pfkey_id2ident(flow->flow_dstid,
+ SADB_EXT_IDENTITY_DST);
+ }
+
+ iov_cnt = 0;
+
+ /* header */
+ iov[iov_cnt].iov_base = &smsg;
+ iov[iov_cnt].iov_len = sizeof(smsg);
+ iov_cnt++;
+
+ /* add flow type */
+ iov[iov_cnt].iov_base = &sa_flowtype;
+ iov[iov_cnt].iov_len = sizeof(sa_flowtype);
+ smsg.sadb_msg_len += sa_flowtype.sadb_protocol_len;
+ iov_cnt++;
+
+ if (action != SADB_X_DELFLOW) {
+#if 0
+ /* local ip */
+ iov[iov_cnt].iov_base = &sa_local;
+ iov[iov_cnt].iov_len = sizeof(sa_local);
+ iov_cnt++;
+ iov[iov_cnt].iov_base = &slocal;
+ iov[iov_cnt].iov_len = ROUNDUP(slocal.ss_len);
+ smsg.sadb_msg_len += sa_local.sadb_address_len;
+ iov_cnt++;
+#endif
+
+ /* remote peer */
+ iov[iov_cnt].iov_base = &sa_peer;
+ iov[iov_cnt].iov_len = sizeof(sa_peer);
+ iov_cnt++;
+ iov[iov_cnt].iov_base = &speer;
+ iov[iov_cnt].iov_len = ROUNDUP(speer.ss_len);
+ smsg.sadb_msg_len += sa_peer.sadb_address_len;
+ iov_cnt++;
+ }
+
+ /* src addr */
+ iov[iov_cnt].iov_base = &sa_src;
+ iov[iov_cnt].iov_len = sizeof(sa_src);
+ iov_cnt++;
+ iov[iov_cnt].iov_base = &ssrc;
+ iov[iov_cnt].iov_len = ROUNDUP(ssrc.ss_len);
+ smsg.sadb_msg_len += sa_src.sadb_address_len;
+ iov_cnt++;
+
+ /* src mask */
+ iov[iov_cnt].iov_base = &sa_smask;
+ iov[iov_cnt].iov_len = sizeof(sa_smask);
+ iov_cnt++;
+ iov[iov_cnt].iov_base = &smask;
+ iov[iov_cnt].iov_len = ROUNDUP(smask.ss_len);
+ smsg.sadb_msg_len += sa_smask.sadb_address_len;
+ iov_cnt++;
+
+ /* dest addr */
+ iov[iov_cnt].iov_base = &sa_dst;
+ iov[iov_cnt].iov_len = sizeof(sa_dst);
+ iov_cnt++;
+ iov[iov_cnt].iov_base = &sdst;
+ iov[iov_cnt].iov_len = ROUNDUP(sdst.ss_len);
+ smsg.sadb_msg_len += sa_dst.sadb_address_len;
+ iov_cnt++;
+
+ /* dst mask */
+ iov[iov_cnt].iov_base = &sa_dmask;
+ iov[iov_cnt].iov_len = sizeof(sa_dmask);
+ iov_cnt++;
+ iov[iov_cnt].iov_base = &dmask;
+ iov[iov_cnt].iov_len = ROUNDUP(dmask.ss_len);
+ smsg.sadb_msg_len += sa_dmask.sadb_address_len;
+ iov_cnt++;
+
+ /* add protocol */
+ iov[iov_cnt].iov_base = &sa_protocol;
+ iov[iov_cnt].iov_len = sizeof(sa_protocol);
+ smsg.sadb_msg_len += sa_protocol.sadb_protocol_len;
+ iov_cnt++;
+
+ if (sa_srcid) {
+ /* src identity */
+ iov[iov_cnt].iov_base = sa_srcid;
+ iov[iov_cnt].iov_len = sa_srcid->sadb_ident_len * 8;
+ smsg.sadb_msg_len += sa_srcid->sadb_ident_len;
+ iov_cnt++;
+ }
+ if (sa_dstid) {
+ /* dst identity */
+ iov[iov_cnt].iov_base = sa_dstid;
+ iov[iov_cnt].iov_len = sa_dstid->sadb_ident_len * 8;
+ smsg.sadb_msg_len += sa_dstid->sadb_ident_len;
+ iov_cnt++;
+ }
+
+ len = smsg.sadb_msg_len * 8;
+ if ((n = writev(sd, iov, iov_cnt)) == -1) {
+ log_warn("%s: writev failed", __func__);
+ goto done;
+ } else if (n != len) {
+ log_warnx("%s: short write", __func__);
+ goto done;
+ }
+
+ ret = pfkey_reply(sd, NULL, NULL);
+
+ done:
+ if (sa_srcid)
+ free(sa_srcid);
+ if (sa_dstid)
+ free(sa_dstid);
+
+ return (ret);
+}
+
+int
+pfkey_sa(int sd, u_int8_t satype, u_int8_t action, struct iked_childsa *sa)
+{
+ struct sadb_msg smsg;
+ struct sadb_sa sadb;
+ struct sadb_address sa_src, sa_dst;
+ struct sadb_key sa_authkey, sa_enckey;
+ struct sadb_x_udpencap udpencap;
+ struct sadb_x_tag sa_tag;
+ struct sockaddr_storage ssrc, sdst;
+ struct sadb_ident *sa_srcid, *sa_dstid;
+ struct iovec iov[IOV_CNT];
+ ssize_t n;
+ int iov_cnt, len;
+ char *tag = NULL;
+
+ sa_srcid = sa_dstid = NULL;
+
+ bzero(&ssrc, sizeof(ssrc));
+ memcpy(&ssrc, &sa->csa_local->addr, sizeof(ssrc));
+ if (socket_af((struct sockaddr *)&ssrc, 0) == -1) {
+ log_warn("%s: invalid address", __func__);
+ return (-1);
+ }
+
+ bzero(&sdst, sizeof(sdst));
+ memcpy(&sdst, &sa->csa_peer->addr, sizeof(sdst));
+ if (socket_af((struct sockaddr *)&sdst, 0) == -1) {
+ log_warn("%s: invalid address", __func__);
+ return (-1);
+ }
+
+ bzero(&smsg, sizeof(smsg));
+ smsg.sadb_msg_version = PF_KEY_V2;
+ smsg.sadb_msg_seq = sadb_msg_seq++;
+ smsg.sadb_msg_pid = getpid();
+ smsg.sadb_msg_len = sizeof(smsg) / 8;
+ smsg.sadb_msg_type = action;
+ smsg.sadb_msg_satype = satype;
+
+ bzero(&sadb, sizeof(sadb));
+ sadb.sadb_sa_len = sizeof(sadb) / 8;
+ sadb.sadb_sa_exttype = SADB_EXT_SA;
+ sadb.sadb_sa_spi = htonl(sa->csa_spi.spi);
+ sadb.sadb_sa_state = SADB_SASTATE_MATURE;
+ sadb.sadb_sa_replay = 16;
+
+ /* XXX we don't support transport mode, yet */
+ sadb.sadb_sa_flags |= SADB_X_SAFLAGS_TUNNEL;
+
+ bzero(&sa_src, sizeof(sa_src));
+ sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8;
+ sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+
+ bzero(&sa_dst, sizeof(sa_dst));
+ sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
+ sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+
+ bzero(&sa_authkey, sizeof(sa_authkey));
+ bzero(&sa_enckey, sizeof(sa_enckey));
+ bzero(&udpencap, sizeof udpencap);
+
+ if (action == SADB_DELETE)
+ goto send;
+
+ /* XXX handle NULL encryption or NULL auth or combined encr/auth */
+ if (action == SADB_ADD &&
+ !ibuf_length(sa->csa_integrkey) && !ibuf_length(sa->csa_encrkey) &&
+ satype != SADB_X_SATYPE_IPCOMP && satype != SADB_X_SATYPE_IPIP) {
+ log_warnx("%s: no key specified", __func__);
+ return (-1);
+ }
+
+ if (sa->csa_ikesa->sa_udpencap) {
+ sadb.sadb_sa_flags |= SADB_X_SAFLAGS_UDPENCAP;
+ udpencap.sadb_x_udpencap_exttype = SADB_X_EXT_UDPENCAP;
+ udpencap.sadb_x_udpencap_len = sizeof(udpencap) / 8;
+ udpencap.sadb_x_udpencap_port =
+ sa->csa_ikesa->sa_peer.addr_port;
+
+ log_debug("%s: udpencap port %d", __func__,
+ ntohs(udpencap.sadb_x_udpencap_port),
+ udpencap.sadb_x_udpencap_port);
+ }
+
+ if (sa->csa_integrxf)
+ if (pfkey_map(pfkey_integr,
+ sa->csa_integrxf->xform_id, &sadb.sadb_sa_auth) == -1) {
+ log_warnx("%s: unsupported integrity algorithm %s",
+ __func__, print_map(sa->csa_integrxf->xform_id,
+ ikev2_xformauth_map));
+ return (-1);
+ }
+
+ if (sa->csa_encrxf)
+ if (pfkey_map(pfkey_encr,
+ sa->csa_encrxf->xform_id, &sadb.sadb_sa_encrypt) == -1) {
+ log_warnx("%s: unsupported encryption algorithm %s",
+ __func__, print_map(sa->csa_encrxf->xform_id,
+ ikev2_xformencr_map));
+ return (-1);
+ }
+
+ if (ibuf_length(sa->csa_integrkey)) {
+ sa_authkey.sadb_key_len = (sizeof(sa_authkey) +
+ ((ibuf_size(sa->csa_integrkey) + 7) / 8) * 8) / 8;
+ sa_authkey.sadb_key_exttype = SADB_EXT_KEY_AUTH;
+ sa_authkey.sadb_key_bits =
+ 8 * ibuf_size(sa->csa_integrkey);
+ }
+
+ if (ibuf_length(sa->csa_encrkey)) {
+ sa_enckey.sadb_key_len = (sizeof(sa_enckey) +
+ ((ibuf_size(sa->csa_encrkey) + 7) / 8) * 8) / 8;
+ sa_enckey.sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
+ sa_enckey.sadb_key_bits =
+ 8 * ibuf_size(sa->csa_encrkey);
+ }
+
+ /* local id */
+ sa_srcid = pfkey_id2ident(sa->csa_srcid, SADB_EXT_IDENTITY_SRC);
+
+ /* peer id */
+ sa_dstid = pfkey_id2ident(sa->csa_dstid, SADB_EXT_IDENTITY_DST);
+
+ tag = sa->csa_ikesa->sa_tag;
+ if (tag != NULL && *tag != '\0') {
+ bzero(&sa_tag, sizeof(sa_tag));
+ sa_tag.sadb_x_tag_exttype = SADB_X_EXT_TAG;
+ sa_tag.sadb_x_tag_len =
+ (ROUNDUP(strlen(tag) + 1) + sizeof(sa_tag)) / 8;
+ sa_tag.sadb_x_tag_taglen = strlen(tag) + 1;
+ } else
+ tag = NULL;
+
+ send:
+ iov_cnt = 0;
+
+ /* header */
+ iov[iov_cnt].iov_base = &smsg;
+ iov[iov_cnt].iov_len = sizeof(smsg);
+ iov_cnt++;
+
+ /* sa */
+ iov[iov_cnt].iov_base = &sadb;
+ iov[iov_cnt].iov_len = sizeof(sadb);
+ smsg.sadb_msg_len += sadb.sadb_sa_len;
+ iov_cnt++;
+
+ /* src addr */
+ iov[iov_cnt].iov_base = &sa_src;
+ iov[iov_cnt].iov_len = sizeof(sa_src);
+ iov_cnt++;
+ iov[iov_cnt].iov_base = &ssrc;
+ iov[iov_cnt].iov_len = ROUNDUP(ssrc.ss_len);
+ smsg.sadb_msg_len += sa_src.sadb_address_len;
+ iov_cnt++;
+
+ /* dst addr */
+ iov[iov_cnt].iov_base = &sa_dst;
+ iov[iov_cnt].iov_len = sizeof(sa_dst);
+ iov_cnt++;
+ iov[iov_cnt].iov_base = &sdst;
+ iov[iov_cnt].iov_len = ROUNDUP(sdst.ss_len);
+ smsg.sadb_msg_len += sa_dst.sadb_address_len;
+ iov_cnt++;
+
+ if (udpencap.sadb_x_udpencap_len) {
+ iov[iov_cnt].iov_base = &udpencap;
+ iov[iov_cnt].iov_len = sizeof(udpencap);
+ smsg.sadb_msg_len += udpencap.sadb_x_udpencap_len;
+ iov_cnt++;
+ }
+
+ if (sa_enckey.sadb_key_len) {
+ /* encryption key */
+ iov[iov_cnt].iov_base = &sa_enckey;
+ iov[iov_cnt].iov_len = sizeof(sa_enckey);
+ iov_cnt++;
+ iov[iov_cnt].iov_base = ibuf_data(sa->csa_encrkey);
+ iov[iov_cnt].iov_len =
+ ((ibuf_size(sa->csa_encrkey) + 7) / 8) * 8;
+ smsg.sadb_msg_len += sa_enckey.sadb_key_len;
+ iov_cnt++;
+ }
+ if (sa_authkey.sadb_key_len) {
+ /* authentication key */
+ iov[iov_cnt].iov_base = &sa_authkey;
+ iov[iov_cnt].iov_len = sizeof(sa_authkey);
+ iov_cnt++;
+ iov[iov_cnt].iov_base = ibuf_data(sa->csa_integrkey);
+ iov[iov_cnt].iov_len =
+ ((ibuf_size(sa->csa_integrkey) + 7) / 8) * 8;
+ smsg.sadb_msg_len += sa_authkey.sadb_key_len;
+ iov_cnt++;
+ }
+
+ if (sa_srcid) {
+ /* src identity */
+ iov[iov_cnt].iov_base = sa_srcid;
+ iov[iov_cnt].iov_len = sa_srcid->sadb_ident_len * 8;
+ smsg.sadb_msg_len += sa_srcid->sadb_ident_len;
+ iov_cnt++;
+ }
+ if (sa_dstid) {
+ /* dst identity */
+ iov[iov_cnt].iov_base = sa_dstid;
+ iov[iov_cnt].iov_len = sa_dstid->sadb_ident_len * 8;
+ smsg.sadb_msg_len += sa_dstid->sadb_ident_len;
+ iov_cnt++;
+ }
+
+ if (tag != NULL) {
+ /* tag identity */
+ iov[iov_cnt].iov_base = &sa_tag;
+ iov[iov_cnt].iov_len = sizeof(sa_tag);
+ iov_cnt++;
+ iov[iov_cnt].iov_base = tag;
+ iov[iov_cnt].iov_len = ROUNDUP(strlen(tag) + 1);
+ smsg.sadb_msg_len += sa_tag.sadb_x_tag_len;
+ iov_cnt++;
+ }
+
+ len = smsg.sadb_msg_len * 8;
+ if ((n = writev(sd, iov, iov_cnt)) == -1) {
+ log_warn("%s: writev failed", __func__);
+ return (-1);
+ } else if (n != len) {
+ log_warnx("%s: short write", __func__);
+ return (-1);
+ }
+
+ return (pfkey_reply(sd, NULL, NULL));
+}
+
+int
+pfkey_sa_getspi(int sd, u_int8_t satype, struct iked_childsa *sa,
+ u_int32_t *spip)
+{
+ struct sadb_msg *msg, smsg;
+ struct sadb_address sa_src, sa_dst;
+ struct sadb_sa *sa_ext;
+ struct sadb_ext *ext;
+ struct sadb_spirange sa_spirange;
+ struct sockaddr_storage ssrc, sdst;
+ struct iovec iov[IOV_CNT];
+ u_int8_t *data;
+ ssize_t n;
+ int iov_cnt, len, ret = 0;
+
+ bzero(&ssrc, sizeof(ssrc));
+ memcpy(&ssrc, &sa->csa_local->addr, sizeof(ssrc));
+ if (socket_af((struct sockaddr *)&ssrc, 0) == -1) {
+ log_warn("%s: invalid address", __func__);
+ return (-1);
+ }
+
+ bzero(&sdst, sizeof(sdst));
+ memcpy(&sdst, &sa->csa_peer->addr, sizeof(sdst));
+ if (socket_af((struct sockaddr *)&sdst, 0) == -1) {
+ log_warn("%s: invalid address", __func__);
+ return (-1);
+ }
+
+ bzero(&smsg, sizeof(smsg));
+ smsg.sadb_msg_version = PF_KEY_V2;
+ smsg.sadb_msg_seq = sadb_msg_seq++;
+ smsg.sadb_msg_pid = getpid();
+ smsg.sadb_msg_len = sizeof(smsg) / 8;
+ smsg.sadb_msg_type = SADB_GETSPI;
+ smsg.sadb_msg_satype = satype;
+
+ bzero(&sa_spirange, sizeof(sa_spirange));
+ sa_spirange.sadb_spirange_exttype = SADB_EXT_SPIRANGE;
+ sa_spirange.sadb_spirange_len = sizeof(sa_spirange) / 8;
+ sa_spirange.sadb_spirange_min = 0x100;
+ sa_spirange.sadb_spirange_max = 0xffffffff;
+ sa_spirange.sadb_spirange_reserved = 0;
+
+ bzero(&sa_src, sizeof(sa_src));
+ sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8;
+ sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+
+ bzero(&sa_dst, sizeof(sa_dst));
+ sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
+ sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+
+ iov_cnt = 0;
+
+ /* header */
+ iov[iov_cnt].iov_base = &smsg;
+ iov[iov_cnt].iov_len = sizeof(smsg);
+ iov_cnt++;
+
+ /* SPI range */
+ iov[iov_cnt].iov_base = &sa_spirange;
+ iov[iov_cnt].iov_len = sizeof(sa_spirange);
+ smsg.sadb_msg_len += sa_spirange.sadb_spirange_len;
+ iov_cnt++;
+
+ /* src addr */
+ iov[iov_cnt].iov_base = &sa_src;
+ iov[iov_cnt].iov_len = sizeof(sa_src);
+ iov_cnt++;
+ iov[iov_cnt].iov_base = &ssrc;
+ iov[iov_cnt].iov_len = ROUNDUP(ssrc.ss_len);
+ smsg.sadb_msg_len += sa_src.sadb_address_len;
+ iov_cnt++;
+
+ /* dst addr */
+ iov[iov_cnt].iov_base = &sa_dst;
+ iov[iov_cnt].iov_len = sizeof(sa_dst);
+ iov_cnt++;
+ iov[iov_cnt].iov_base = &sdst;
+ iov[iov_cnt].iov_len = ROUNDUP(sdst.ss_len);
+ smsg.sadb_msg_len += sa_dst.sadb_address_len;
+ iov_cnt++;
+
+ *spip = 0;
+
+ len = smsg.sadb_msg_len * 8;
+ if ((n = writev(sd, iov, iov_cnt)) == -1) {
+ log_warn("%s: writev failed", __func__);
+ return (-1);
+ } else if (n != len) {
+ log_warnx("%s: short write", __func__);
+ return (-1);
+ }
+
+ if ((ret = pfkey_reply(sd, &data, &n)) != 0)
+ return (-1);
+
+ msg = (struct sadb_msg *)data;
+ for (ext = (struct sadb_ext *)(msg + 1);
+ (size_t)((u_int8_t *)ext - (u_int8_t *)msg) <
+ msg->sadb_msg_len * PFKEYV2_CHUNK;
+ ext = (struct sadb_ext *)((u_int8_t *)ext +
+ ext->sadb_ext_len * PFKEYV2_CHUNK)) {
+ if (ext->sadb_ext_type == SADB_EXT_SA) {
+ sa_ext = (struct sadb_sa *)ext;
+ *spip = ntohl(sa_ext->sadb_sa_spi);
+ break;
+ }
+ }
+
+ bzero(data, len);
+ free(data);
+
+ log_debug("%s: spi 0x%08x", __func__, *spip);
+
+ return (ret);
+}
+
+int
+pfkey_sagroup(int sd, u_int8_t satype1, u_int8_t action,
+ struct iked_childsa *sa1, struct iked_childsa *sa2)
+{
+ struct sadb_msg smsg;
+ struct sadb_sa sadb1, sadb2;
+ struct sadb_address sa_dst1, sa_dst2;
+ struct sockaddr_storage sdst1, sdst2;
+ struct sadb_protocol sa_proto;
+ struct iovec iov[IOV_CNT];
+ ssize_t n;
+ int iov_cnt, len;
+ u_int8_t satype2;
+
+ if (pfkey_map(pfkey_satype, sa2->csa_saproto, &satype2) == -1)
+ return (-1);
+
+ bzero(&sdst1, sizeof(sdst1));
+ memcpy(&sdst1, &sa1->csa_peer->addr, sizeof(sdst1));
+ if (socket_af((struct sockaddr *)&sdst1, 0) == -1) {
+ log_warnx("%s: unsupported address family %d",
+ __func__, sdst1.ss_family);
+ return (-1);
+ }
+
+ bzero(&sdst2, sizeof(sdst2));
+ memcpy(&sdst2, &sa2->csa_peer->addr, sizeof(sdst2));
+ if (socket_af((struct sockaddr *)&sdst2, 0) == -1) {
+ log_warnx("%s: unsupported address family %d",
+ __func__, sdst2.ss_family);
+ return (-1);
+ }
+
+ bzero(&smsg, sizeof(smsg));
+ smsg.sadb_msg_version = PF_KEY_V2;
+ smsg.sadb_msg_seq = sadb_msg_seq++;
+ smsg.sadb_msg_pid = getpid();
+ smsg.sadb_msg_len = sizeof(smsg) / 8;
+ smsg.sadb_msg_type = action;
+ smsg.sadb_msg_satype = satype1;
+
+ bzero(&sadb1, sizeof(sadb1));
+ sadb1.sadb_sa_len = sizeof(sadb1) / 8;
+ sadb1.sadb_sa_exttype = SADB_EXT_SA;
+ sadb1.sadb_sa_spi = htonl(sa1->csa_spi.spi);
+ sadb1.sadb_sa_state = SADB_SASTATE_MATURE;
+
+ bzero(&sadb2, sizeof(sadb2));
+ sadb2.sadb_sa_len = sizeof(sadb2) / 8;
+ sadb2.sadb_sa_exttype = SADB_X_EXT_SA2;
+ sadb2.sadb_sa_spi = htonl(sa2->csa_spi.spi);
+ sadb2.sadb_sa_state = SADB_SASTATE_MATURE;
+ iov_cnt = 0;
+
+ bzero(&sa_dst1, sizeof(sa_dst1));
+ sa_dst1.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+ sa_dst1.sadb_address_len =
+ (sizeof(sa_dst1) + ROUNDUP(sdst1.ss_len)) / 8;
+
+ bzero(&sa_dst2, sizeof(sa_dst2));
+ sa_dst2.sadb_address_exttype = SADB_X_EXT_DST2;
+ sa_dst2.sadb_address_len =
+ (sizeof(sa_dst2) + ROUNDUP(sdst2.ss_len)) / 8;
+
+ bzero(&sa_proto, sizeof(sa_proto));
+ sa_proto.sadb_protocol_exttype = SADB_X_EXT_PROTOCOL;
+ sa_proto.sadb_protocol_len = sizeof(sa_proto) / 8;
+ sa_proto.sadb_protocol_direction = 0;
+ sa_proto.sadb_protocol_proto = satype2;
+
+ /* header */
+ iov[iov_cnt].iov_base = &smsg;
+ iov[iov_cnt].iov_len = sizeof(smsg);
+ iov_cnt++;
+
+ /* sa */
+ iov[iov_cnt].iov_base = &sadb1;
+ iov[iov_cnt].iov_len = sizeof(sadb1);
+ smsg.sadb_msg_len += sadb1.sadb_sa_len;
+ iov_cnt++;
+
+ /* dst addr */
+ iov[iov_cnt].iov_base = &sa_dst1;
+ iov[iov_cnt].iov_len = sizeof(sa_dst1);
+ iov_cnt++;
+ iov[iov_cnt].iov_base = &sdst1;
+ iov[iov_cnt].iov_len = ROUNDUP(sdst1.ss_len);
+ smsg.sadb_msg_len += sa_dst1.sadb_address_len;
+ iov_cnt++;
+
+ /* second sa */
+ iov[iov_cnt].iov_base = &sadb2;
+ iov[iov_cnt].iov_len = sizeof(sadb2);
+ smsg.sadb_msg_len += sadb2.sadb_sa_len;
+ iov_cnt++;
+
+ /* second dst addr */
+ iov[iov_cnt].iov_base = &sa_dst2;
+ iov[iov_cnt].iov_len = sizeof(sa_dst2);
+ iov_cnt++;
+ iov[iov_cnt].iov_base = &sdst2;
+ iov[iov_cnt].iov_len = ROUNDUP(sdst2.ss_len);
+ smsg.sadb_msg_len += sa_dst2.sadb_address_len;
+ iov_cnt++;
+
+ /* SA type */
+ iov[iov_cnt].iov_base = &sa_proto;
+ iov[iov_cnt].iov_len = sizeof(sa_proto);
+ smsg.sadb_msg_len += sa_proto.sadb_protocol_len;
+ iov_cnt++;
+
+ len = smsg.sadb_msg_len * 8;
+ if ((n = writev(sd, iov, iov_cnt)) == -1) {
+ log_warn("%s: writev failed", __func__);
+ return (-1);
+ } else if (n != len) {
+ log_warnx("%s: short write", __func__);
+ return (-1);
+ }
+
+ return (pfkey_reply(sd, NULL, NULL));
+}
+
+int
+pfkey_reply(int sd, u_int8_t **datap, ssize_t *lenp)
+{
+ struct sadb_msg hdr;
+ ssize_t len;
+ u_int8_t *data;
+
+ if (recv(sd, &hdr, sizeof(hdr), MSG_PEEK) != sizeof(hdr)) {
+ warnx("short read");
+ return -1;
+ }
+ len = hdr.sadb_msg_len * PFKEYV2_CHUNK;
+ if ((data = malloc(len)) == NULL)
+ err(1, "pfkey_reply: malloc");
+ if (read(sd, data, len) != len) {
+ warn("PF_KEY short read");
+ bzero(data, len);
+ free(data);
+ return -1;
+ }
+ if (datap) {
+ *datap = data;
+ if (lenp)
+ *lenp = len;
+ } else {
+ bzero(data, len);
+ free(data);
+ }
+ if (datap == NULL && hdr.sadb_msg_errno != 0) {
+ errno = hdr.sadb_msg_errno;
+ if (errno != EEXIST) {
+ warn("PF_KEY failed");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int
+pfkey_flow_add(int fd, struct iked_flow *flow)
+{
+ u_int8_t satype;
+
+ if (flow->flow_loaded)
+ return (0);
+
+ if (pfkey_map(pfkey_satype, flow->flow_saproto, &satype) == -1)
+ return (-1);
+
+ if (pfkey_flow(fd, satype, SADB_X_ADDFLOW, flow) == -1)
+ return (-1);
+
+ flow->flow_loaded = 1;
+ return (0);
+}
+
+int
+pfkey_flow_delete(int fd, struct iked_flow *flow)
+{
+ u_int8_t satype;
+
+ if (!flow->flow_loaded)
+ return (0);
+
+ if (pfkey_map(pfkey_satype, flow->flow_saproto, &satype) == -1)
+ return (-1);
+
+ if (pfkey_flow(fd, satype, SADB_X_DELFLOW, flow) == -1)
+ return (-1);
+
+ flow->flow_loaded = 0;
+ return (0);
+}
+
+int
+pfkey_sa_init(int fd, struct iked_childsa *sa, u_int32_t *spi)
+{
+ u_int8_t satype;
+
+ if (pfkey_map(pfkey_satype, sa->csa_saproto, &satype) == -1)
+ return (-1);
+
+ if (pfkey_sa_getspi(fd, satype, sa, spi) == -1)
+ return (-1);
+
+ log_debug("%s: new spi 0x%08x", __func__, *spi);
+
+ return (0);
+}
+
+int
+pfkey_sa_add(int fd, struct iked_childsa *sa, struct iked_childsa *last)
+{
+ u_int8_t satype;
+ u_int cmd;
+
+ if (pfkey_map(pfkey_satype, sa->csa_saproto, &satype) == -1)
+ return (-1);
+
+ if (sa->csa_dir == IPSP_DIRECTION_IN || sa->csa_loaded)
+ cmd = SADB_UPDATE;
+ else
+ cmd = SADB_ADD;
+
+ log_debug("%s: %s spi %s", __func__, cmd == SADB_ADD ? "add": "update",
+ print_spi(sa->csa_spi.spi, 4));
+
+ if (pfkey_sa(fd, satype, cmd, sa) == -1) {
+ if (cmd == SADB_ADD)
+ (void)pfkey_sa_delete(fd, sa);
+ return (-1);
+ }
+
+ if (last && cmd == SADB_ADD) {
+ if (pfkey_sagroup(fd, satype,
+ SADB_X_GRPSPIS, sa, last) == -1) {
+ (void)pfkey_sa_delete(fd, sa);
+ return (-1);
+ }
+ }
+
+ sa->csa_loaded = 1;
+ return (0);
+}
+
+int
+pfkey_sa_delete(int fd, struct iked_childsa *sa)
+{
+ u_int8_t satype;
+
+ if (!sa->csa_loaded || sa->csa_spi.spi == 0)
+ return (0);
+
+ if (pfkey_map(pfkey_satype, sa->csa_saproto, &satype) == -1)
+ return (-1);
+
+ if (pfkey_sa(fd, satype, SADB_DELETE, sa) == -1)
+ return (-1);
+
+ sa->csa_loaded = 0;
+ return (0);
+}
+
+int
+pfkey_flush(int sd)
+{
+ struct sadb_msg smsg;
+ struct iovec iov[IOV_CNT];
+ ssize_t n;
+ int iov_cnt, len;
+
+ bzero(&smsg, sizeof(smsg));
+ smsg.sadb_msg_version = PF_KEY_V2;
+ smsg.sadb_msg_seq = sadb_msg_seq++;
+ smsg.sadb_msg_pid = getpid();
+ smsg.sadb_msg_len = sizeof(smsg) / 8;
+ smsg.sadb_msg_type = SADB_FLUSH;
+ smsg.sadb_msg_satype = SADB_SATYPE_UNSPEC;
+
+ iov_cnt = 0;
+
+ iov[iov_cnt].iov_base = &smsg;
+ iov[iov_cnt].iov_len = sizeof(smsg);
+ iov_cnt++;
+
+ len = smsg.sadb_msg_len * 8;
+ if ((n = writev(sd, iov, iov_cnt)) == -1) {
+ log_warn("%s: writev failed", __func__);
+ return (-1);
+ }
+ if (n != len) {
+ log_warnx("%s: short write", __func__);
+ return (-1);
+ }
+ if (pfkey_reply(sd, NULL, NULL) < 0)
+ return (-1);
+
+ return (0);
+}
+
+struct sadb_ident *
+pfkey_id2ident(struct iked_id *id, u_int exttype)
+{
+ char idstr[IKED_ID_SIZE];
+ u_int type;
+ size_t len;
+ struct sadb_ident *sa_id;
+
+ switch (id->id_type) {
+ case IKEV2_ID_FQDN:
+ type = SADB_IDENTTYPE_FQDN;
+ break;
+ case IKEV2_ID_RFC822_ADDR:
+ type = SADB_IDENTTYPE_USERFQDN;
+ break;
+ case IKEV2_ID_IPV4_ADDR:
+ case IKEV2_ID_IPV6_ADDR:
+ type = SADB_IDENTTYPE_PREFIX;
+ break;
+ case IKEV2_ID_DER_ASN1_DN:
+ case IKEV2_ID_DER_ASN1_GN:
+ case IKEV2_ID_KEY_ID:
+ case IKEV2_ID_NONE:
+ default:
+ /* XXX not implemented/supported by PFKEY */
+ return (NULL);
+ }
+
+ bzero(&idstr, sizeof(idstr));
+
+ if (print_id(id, sizeof(struct ikev2_id), idstr, sizeof(idstr)) == -1)
+ return (NULL);
+
+ len = ROUNDUP(strlen(idstr) + 1) + sizeof(*sa_id);
+ if ((sa_id = calloc(1, len)) == NULL)
+ return (NULL);
+
+ strlcpy((char *)(sa_id + 1), idstr, ROUNDUP(strlen(idstr) + 1));
+ sa_id->sadb_ident_type = type;
+ sa_id->sadb_ident_len = len / 8;
+ sa_id->sadb_ident_exttype = exttype;
+
+ return (sa_id);
+}
+
+int
+pfkey_init(void)
+{
+ int fd;
+
+ if ((fd = socket(PF_KEY, SOCK_RAW, PF_KEY_V2)) == -1)
+ fatal("pfkey_init: failed to open PF_KEY socket");
+
+ pfkey_flush(fd);
+
+ return (fd);
+}
diff --git a/sbin/iked/policy.c b/sbin/iked/policy.c
new file mode 100644
index 00000000000..62c30d90362
--- /dev/null
+++ b/sbin/iked/policy.c
@@ -0,0 +1,289 @@
+/* $OpenBSD: policy.c,v 1.1 2010/06/03 16:41:12 reyk Exp $ */
+/* $vantronix: policy.c,v 1.29 2010/05/28 15:34:35 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/tree.h>
+
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <event.h>
+
+#include "iked.h"
+#include "ikev2.h"
+
+static __inline int
+ policy_cmp(struct iked_policy *, struct iked_policy *);
+static __inline int
+ sa_cmp(struct iked_sa *, struct iked_sa *);
+static __inline int
+ user_cmp(struct iked_user *, struct iked_user *);
+
+void
+policy_init(struct iked *env)
+{
+ RB_INIT(&env->sc_policies);
+ RB_INIT(&env->sc_users);
+ RB_INIT(&env->sc_sas);
+}
+
+int
+policy_lookup(struct iked *env, struct iked_message *msg)
+{
+ struct iked_policy pol;
+
+ if (msg->msg_sa != NULL && msg->msg_sa->sa_policy != NULL) {
+ /* Existing SA with policy */
+ msg->msg_policy = msg->msg_sa->sa_policy;
+ goto found;
+ }
+
+ bzero(&pol, sizeof(pol));
+ memcpy(&pol.pol_peer, &msg->msg_peer, sizeof(pol.pol_peer));
+ memcpy(&pol.pol_local, &msg->msg_local, sizeof(pol.pol_local));
+
+ /* Try to find a matching policy for this message */
+ if ((msg->msg_policy =
+ RB_FIND(iked_policies, &env->sc_policies, &pol)) != NULL)
+ goto found;
+
+ /* No matching policy found, try the default */
+ if ((msg->msg_policy = env->sc_defaultcon) != NULL)
+ goto found;
+
+ /* No policy found */
+ return (-1);
+
+ found:
+ return (0);
+}
+
+static __inline int
+policy_cmp(struct iked_policy *a, struct iked_policy *b)
+{
+ int ret;
+
+ if (b->pol_flags & IKED_POLICY_DEFAULT)
+ return (-2);
+
+ if ((ret = sockaddr_cmp((struct sockaddr *)&a->pol_peer,
+ (struct sockaddr *)&b->pol_peer, b->pol_peermask)) != 0)
+ return (ret);
+ if ((ret = sockaddr_cmp((struct sockaddr *)&a->pol_local,
+ (struct sockaddr *)&b->pol_local, b->pol_localmask)) != 0)
+ return (ret);
+
+ return (0);
+}
+
+void
+policy_ref(struct iked *env, struct iked_policy *pol)
+{
+ pol->pol_refcnt++;
+ pol->pol_flags |= IKED_POLICY_REFCNT;
+}
+
+void
+policy_unref(struct iked *env, struct iked_policy *pol)
+{
+ if (pol == NULL || (pol->pol_flags & IKED_POLICY_REFCNT) == 0)
+ return;
+ if (--(pol->pol_refcnt) <= 0)
+ config_free_policy(env, pol);
+}
+
+RB_GENERATE(iked_policies, iked_policy, pol_entry, policy_cmp);
+
+void
+sa_state(struct iked *env, struct iked_sa *sa, int state)
+{
+ const char *a;
+ const char *b;
+
+ a = print_map(sa->sa_state, ikev2_state_map);
+ b = print_map(state, ikev2_state_map);
+
+ log_info("%s: %s -> %s", __func__, a, b);
+ sa->sa_state = state;
+}
+
+void
+sa_stateflags(struct iked_sa *sa, u_int flags)
+{
+ log_debug("%s: 0x%02x -> 0x%02x %s (required 0x%02x)", __func__,
+ sa->sa_stateflags, sa->sa_stateflags | flags,
+ print_bits(sa->sa_stateflags | flags,
+ IKED_REQ_BITS), sa->sa_staterequire);
+
+ sa->sa_stateflags |= flags;
+}
+
+int
+sa_stateok(struct iked_sa *sa, int state)
+{
+ if (sa->sa_state < state)
+ return (0);
+ if (state == IKEV2_STATE_VALID ||
+ state == IKEV2_STATE_EAP) {
+ log_debug("%s: flags 0x%02x require 0x%02x %s", __func__,
+ (sa->sa_stateflags & sa->sa_staterequire),
+ sa->sa_staterequire,
+ print_bits(sa->sa_staterequire,
+ IKED_REQ_BITS));
+
+ if ((sa->sa_stateflags & sa->sa_staterequire) !=
+ sa->sa_staterequire)
+ return (0); /* not ready, ignore */
+ }
+ return (1);
+}
+
+struct iked_sa *
+sa_new(struct iked *env, u_int64_t ispi, u_int64_t rspi,
+ u_int initiator, struct iked_policy *policy)
+{
+ struct iked_sa *sa;
+
+ if ((sa = sa_lookup(env, ispi, rspi, initiator)) == NULL) {
+ /* Create new SA */
+ sa = config_new_sa(env, initiator);
+ }
+ if (sa == NULL) {
+ log_debug("%s: failed to get sa", __func__);
+ return (NULL);
+ }
+ if (sa->sa_policy == NULL) {
+ sa->sa_policy = policy;
+ }
+ sa->sa_staterequire = IKED_REQ_AUTH|IKED_REQ_SA;
+ if (policy != NULL && policy->pol_auth.auth_eap) {
+ sa->sa_staterequire |= IKED_REQ_CERT;
+ } else if (policy != NULL &&
+ policy->pol_auth.auth_method != IKEV2_AUTH_SHARED_KEY_MIC) {
+ sa->sa_staterequire |= IKED_REQ_VALID|IKED_REQ_CERT;
+ }
+ if (sa->sa_hdr.sh_ispi == 0)
+ sa->sa_hdr.sh_ispi = ispi;
+ if (sa->sa_hdr.sh_rspi == 0)
+ sa->sa_hdr.sh_rspi = rspi;
+
+ /* Re-insert node into the tree */
+ (void)RB_REMOVE(iked_sas, &env->sc_sas, sa);
+ RB_INSERT(iked_sas, &env->sc_sas, sa);
+
+ return (sa);
+}
+
+void
+sa_free(struct iked *env, struct iked_sa *sa)
+{
+ log_debug("%s: ispi %s rspi %s", __func__,
+ print_spi(sa->sa_hdr.sh_ispi, 8),
+ print_spi(sa->sa_hdr.sh_rspi, 8));
+
+ (void)RB_REMOVE(iked_sas, &env->sc_sas, sa);
+ config_free_sa(env, sa);
+}
+
+void
+childsa_free(struct iked_childsa *sa)
+{
+ ibuf_release(sa->csa_encrkey);
+ ibuf_release(sa->csa_integrkey);
+ free(sa);
+}
+
+void
+flow_free(struct iked_flow *flow)
+{
+ free(flow);
+}
+
+struct iked_sa *
+sa_lookup(struct iked *env, u_int64_t ispi, u_int64_t rspi,
+ u_int initiator)
+{
+ struct iked_sa *sa, key;
+
+ key.sa_hdr.sh_ispi = ispi;
+ key.sa_hdr.sh_rspi = rspi;
+ key.sa_hdr.sh_initiator = initiator;
+
+ if ((sa = RB_FIND(iked_sas, &env->sc_sas, &key)) != NULL)
+ gettimeofday(&sa->sa_timeused, NULL);
+ return (sa);
+}
+
+static __inline int
+sa_cmp(struct iked_sa *a, struct iked_sa *b)
+{
+ if (a->sa_hdr.sh_initiator != b->sa_hdr.sh_initiator)
+ return (-2);
+
+ if (a->sa_hdr.sh_ispi > b->sa_hdr.sh_ispi)
+ return (-1);
+ if (a->sa_hdr.sh_ispi < b->sa_hdr.sh_ispi)
+ return (1);
+
+ /* Responder SPI is not yet set */
+ if (a->sa_hdr.sh_rspi == 0)
+ return (0);
+
+ if (a->sa_hdr.sh_rspi > b->sa_hdr.sh_rspi)
+ return (-1);
+ if (a->sa_hdr.sh_rspi < b->sa_hdr.sh_rspi)
+ return (1);
+
+ return (0);
+}
+
+RB_GENERATE(iked_sas, iked_sa, sa_entry, sa_cmp);
+
+struct iked_user *
+user_lookup(struct iked *env, const char *user)
+{
+ struct iked_user key;
+
+ if (strlcpy(key.usr_name, user,
+ sizeof(key.usr_name)) >= sizeof(key.usr_name))
+ return (NULL);
+
+ return (RB_FIND(iked_users, &env->sc_users, &key));
+}
+
+static __inline int
+user_cmp(struct iked_user *a, struct iked_user *b)
+{
+ return (strcmp(a->usr_name, b->usr_name));
+}
+
+RB_GENERATE(iked_users, iked_user, usr_entry, user_cmp);
diff --git a/sbin/iked/proc.c b/sbin/iked/proc.c
new file mode 100644
index 00000000000..05213b1e083
--- /dev/null
+++ b/sbin/iked/proc.c
@@ -0,0 +1,332 @@
+/* $OpenBSD: proc.c,v 1.1 2010/06/03 16:41:12 reyk Exp $ */
+/* $vantronix: proc.c,v 1.11 2010/06/01 16:45:56 jsg Exp $ */
+
+/*
+ * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
+ * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <pwd.h>
+#include <event.h>
+
+#include <openssl/rand.h>
+
+#include "iked.h"
+
+void proc_shutdown(struct iked_proc *);
+void proc_sig_handler(int, short, void *);
+
+void
+init_procs(struct iked *env, struct iked_proc *p, u_int nproc)
+{
+ u_int i;
+
+ iked_process = PROC_PARENT;
+ init_pipes(env);
+
+ for (i = 0; i < nproc; i++, p++) {
+ env->sc_title[p->id] = p->title;
+ env->sc_pid[p->id] = (*p->init)(env, p);
+ }
+}
+
+void
+kill_procs(struct iked *env)
+{
+ u_int i;
+
+ if (iked_process != PROC_PARENT)
+ return;
+
+ for (i = 0; i < PROC_MAX; i++) {
+ if (env->sc_pid[i] == 0)
+ continue;
+ kill(env->sc_pid[i], SIGTERM);
+ }
+}
+
+void
+init_pipes(struct iked *env)
+{
+ int i, j, fds[2];
+
+ for (i = 0; i < PROC_MAX; i++)
+ for (j = 0; j < PROC_MAX; j++) {
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC,
+ fds) == -1)
+ fatal("socketpair");
+ env->sc_pipes[i][j] = fds[0];
+ env->sc_pipes[j][i] = fds[1];
+ socket_set_blockmode(env->sc_pipes[i][j],
+ BM_NONBLOCK);
+ socket_set_blockmode(env->sc_pipes[j][i],
+ BM_NONBLOCK);
+ }
+}
+
+void
+config_pipes(struct iked *env, struct iked_proc *p, u_int nproc)
+{
+ u_int i, j, k, found;
+
+ for (i = 0; i < PROC_MAX; i++) {
+ if (i != iked_process) {
+ for (j = 0; j < PROC_MAX; j++) {
+ close(env->sc_pipes[i][j]);
+ env->sc_pipes[i][j] = -1;
+ }
+ } else {
+ for (j = found = 0; j < PROC_MAX; j++, found = 0) {
+ for (k = 0; k < nproc; k++) {
+ if (p[k].id == j)
+ found++;
+ }
+ if (!found) {
+ close(env->sc_pipes[i][j]);
+ env->sc_pipes[i][j] = -1;
+ }
+ }
+ }
+ }
+}
+
+void
+config_procs(struct iked *env, struct iked_proc *p, u_int nproc)
+{
+ u_int src, dst, i;
+
+ /*
+ * listen on appropriate pipes
+ */
+ for (i = 0; i < nproc; i++, p++) {
+ src = iked_process;
+ dst = p->id;
+ p->env = env;
+
+ imsg_init(&env->sc_ievs[dst].ibuf,
+ env->sc_pipes[src][dst]);
+ env->sc_ievs[dst].handler = dispatch_proc;
+ env->sc_ievs[dst].events = EV_READ;
+ env->sc_ievs[dst].data = p;
+ env->sc_ievs[dst].name = p->title;
+ event_set(&env->sc_ievs[dst].ev,
+ env->sc_ievs[dst].ibuf.fd,
+ env->sc_ievs[dst].events,
+ env->sc_ievs[dst].handler,
+ env->sc_ievs[dst].data);
+ event_add(&env->sc_ievs[dst].ev, NULL);
+ }
+}
+
+void
+proc_shutdown(struct iked_proc *p)
+{
+ struct iked *env = p->env;
+
+ if (p->id == PROC_CONTROL)
+ control_cleanup(&env->sc_csock);
+
+ log_info("%s exiting", p->title);
+ _exit(0);
+}
+
+void
+proc_sig_handler(int sig, short event, void *arg)
+{
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ proc_shutdown((struct iked_proc *)arg);
+ break;
+ case SIGCHLD:
+ case SIGHUP:
+ case SIGPIPE:
+ /* ignore */
+ break;
+ default:
+ fatalx("proc_sig_handler: unexpected signal");
+ /* NOTREACHED */
+ }
+}
+
+pid_t
+run_proc(struct iked *env, struct iked_proc *p,
+ struct iked_proc *procs, u_int nproc,
+ void (*init)(struct iked *, void *), void *arg)
+{
+ pid_t pid;
+ struct passwd *pw;
+ const char *root;
+ u_int32_t seed[256];
+
+ switch (pid = fork()) {
+ case -1:
+ fatal("run_proc: cannot fork");
+ case 0:
+ break;
+ default:
+ return (pid);
+ }
+
+ pw = env->sc_pw;
+
+ if (p->id == PROC_CONTROL) {
+ if (control_init(env, &env->sc_csock) == -1)
+ fatalx(p->title);
+ }
+
+ /* Change root directory */
+ if (p->chroot != NULL)
+ root = p->chroot;
+ else
+ root = pw->pw_dir;
+
+#ifndef DEBUG
+ if (chroot(root) == -1)
+ fatal("run_proc: chroot");
+ if (chdir("/") == -1)
+ fatal("run_proc: chdir(\"/\")");
+#else
+#warning disabling privilege revocation and chroot in DEBUG MODE
+ if (p->chroot != NULL) {
+ if (chroot(root) == -1)
+ fatal("run_proc: chroot");
+ if (chdir("/") == -1)
+ fatal("run_proc: chdir(\"/\")");
+ }
+#endif
+
+ iked_process = p->id;
+ setproctitle("%s", p->title);
+
+#ifndef DEBUG
+ if (setgroups(1, &pw->pw_gid) ||
+ setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
+ setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
+ fatal("run_proc: cannot drop privileges");
+#endif
+
+ event_init();
+
+ signal_set(&env->sc_evsigint, SIGINT, proc_sig_handler, env);
+ signal_set(&env->sc_evsigterm, SIGTERM, proc_sig_handler, env);
+ signal_set(&env->sc_evsigchld, SIGCHLD, proc_sig_handler, env);
+ signal_set(&env->sc_evsighup, SIGHUP, proc_sig_handler, env);
+ signal_set(&env->sc_evsigpipe, SIGPIPE, proc_sig_handler, env);
+
+ signal_add(&env->sc_evsigint, NULL);
+ signal_add(&env->sc_evsigterm, NULL);
+ signal_add(&env->sc_evsigchld, NULL);
+ signal_add(&env->sc_evsighup, NULL);
+ signal_add(&env->sc_evsigpipe, NULL);
+
+ config_pipes(env, procs, nproc);
+ config_procs(env, procs, nproc);
+
+ arc4random_buf(seed, sizeof(seed));
+ RAND_seed(seed, sizeof(seed));
+
+ if (p->id == PROC_CONTROL) {
+ TAILQ_INIT(&ctl_conns);
+ if (control_listen(&env->sc_csock) == -1)
+ fatalx(p->title);
+ }
+
+ if (init != NULL)
+ init(env, arg);
+
+ event_dispatch();
+
+ proc_shutdown(p);
+
+ return (0);
+}
+
+void
+dispatch_proc(int fd, short event, void *arg)
+{
+ struct iked_proc *p = (struct iked_proc *)arg;
+ struct iked *env = p->env;
+ struct imsgev *iev;
+ struct imsgbuf *ibuf;
+ struct imsg imsg;
+ ssize_t n;
+ int verbose;
+
+ iev = &env->sc_ievs[p->id];
+ ibuf = &iev->ibuf;
+
+ if (event & EV_READ) {
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal(p->title);
+ if (n == 0) {
+ /* this pipe is dead, so remove the event handler */
+ event_del(&iev->ev);
+ event_loopexit(NULL);
+ return;
+ }
+ }
+
+ if (event & EV_WRITE) {
+ if (msgbuf_write(&ibuf->w) == -1)
+ fatal(p->title);
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal(p->title);
+ if (n == 0)
+ break;
+
+ /*
+ * Check the message with the program callback
+ */
+ if ((p->cb)(fd, p, &imsg) == 0) {
+ /* Message was handled by the callback, continue */
+ imsg_free(&imsg);
+ continue;
+ }
+
+ /*
+ * Generic message handling
+ */
+ switch (imsg.hdr.type) {
+ case IMSG_CTL_VERBOSE:
+ IMSG_SIZE_CHECK(&imsg, &verbose);
+
+ memcpy(&verbose, imsg.data, sizeof(verbose));
+ log_verbose(verbose);
+ break;
+ default:
+ log_warnx("%s: %s got imsg %d", __func__, p->title,
+ imsg.hdr.type);
+ fatalx(p->title);
+ }
+ imsg_free(&imsg);
+ }
+ imsg_event_add(iev);
+}
diff --git a/sbin/iked/types.h b/sbin/iked/types.h
new file mode 100644
index 00000000000..fe2b8862375
--- /dev/null
+++ b/sbin/iked/types.h
@@ -0,0 +1,121 @@
+/* $OpenBSD: types.h,v 1.1 2010/06/03 16:41:12 reyk Exp $ */
+/* $vantronix: types.h,v 1.24 2010/05/11 12:05:56 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IKED_TYPES_H
+#define _IKED_TYPES_H
+
+#ifndef IKED_USER
+#define IKED_USER "_isakmpd"
+#endif
+
+#ifndef IKED_CONFIG
+#define IKED_CONFIG "/etc/iked.conf"
+#endif
+#define IKED_SOCKET "/var/run/iked.sock"
+
+#ifndef IKED_CA
+#define IKED_CA "/etc/isakmpd/"
+#endif
+#define IKED_CA_DIR "ca/"
+#define IKED_CRL_DIR "crls/"
+#define IKED_CERT_DIR "certs/"
+#define IKED_PRIVKEY IKED_CA "private/local.key"
+#define IKED_PUBKEY "local.pub"
+
+#define IKED_OPT_VERBOSE 0x00000001
+#define IKED_OPT_NOACTION 0x00000002
+#define IKED_OPT_NONATT 0x00000004
+
+#define IKED_IKE_PORT 500
+#define IKED_NATT_PORT 4500
+
+#define IKED_NONCE_MIN 16 /* XXX 128 bits */
+#define IKED_NONCE_SIZE 32 /* XXX 256 bits */
+
+#define IKED_ID_SIZE 1024 /* XXX should be dynanic */
+#define IKED_PSK_SIZE 1024 /* XXX should be dynamic */
+#define IKED_MSGBUF_MAX 8192
+#define IKED_CFG_MAX 16 /* maximum CP attributes */
+#define IKED_TAG_SIZE 64
+#define IKED_CYCLE_BUFFERS 4 /* # of static buffers for mapping */
+#define IKED_PASSWORD_SIZE 256 /* limited by most EAP types */
+
+#define IKED_E 0x1000 /* Decrypted flag */
+
+struct iked_constmap {
+ u_int cm_type;
+ const char *cm_name;
+ const char *cm_descr;
+};
+
+struct iked_transform {
+ u_int8_t xform_type;
+ u_int16_t xform_id;
+ u_int16_t xform_length;
+ u_int16_t xform_keylength;
+ u_int xform_score;
+ struct iked_constmap *xform_map;
+};
+
+enum imsg_type {
+ IMSG_NONE,
+ IMSG_CTL_OK,
+ IMSG_CTL_FAIL,
+ IMSG_CTL_VERBOSE,
+ IMSG_CTL_NOTIFY,
+ IMSG_CTL_RELOAD,
+ IMSG_CTL_RESET,
+ IMSG_UDP_SOCKET,
+ IMSG_PFKEY_SOCKET,
+ IMSG_IKE_MESSAGE,
+ IMSG_CFG_POLICY,
+ IMSG_CFG_USER,
+ IMSG_CERTREQ,
+ IMSG_CERT,
+ IMSG_CERTVALID,
+ IMSG_CERTINVALID,
+ IMSG_AUTH
+};
+
+enum iked_procid {
+ PROC_PARENT = 0,
+ PROC_IKEV1,
+ PROC_IKEV2,
+ PROC_CERT,
+ PROC_MAX
+};
+
+/* Attach the control socket to the following process */
+#define PROC_CONTROL PROC_CERT
+
+enum blockmodes {
+ BM_NORMAL,
+ BM_NONBLOCK
+};
+
+enum flushmode {
+ RESET_RELOAD = 0,
+ RESET_ALL,
+ RESET_CA,
+ RESET_POLICY,
+ RESET_SA,
+ RESET_USER
+};
+
+#endif /* _IKED_TYPES_H */
diff --git a/sbin/iked/util.c b/sbin/iked/util.c
new file mode 100644
index 00000000000..7644d9f5004
--- /dev/null
+++ b/sbin/iked/util.c
@@ -0,0 +1,943 @@
+/* $OpenBSD: util.c,v 1.1 2010/06/03 16:41:12 reyk Exp $ */
+/* $vantronix: util.c,v 1.39 2010/06/02 12:22:58 reyk Exp $ */
+
+/*
+ * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <event.h>
+
+#include "iked.h"
+#include "ikev2.h"
+
+void
+socket_set_blockmode(int fd, enum blockmodes bm)
+{
+ int flags;
+
+ if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
+ fatal("fcntl F_GETFL");
+
+ if (bm == BM_NONBLOCK)
+ flags |= O_NONBLOCK;
+ else
+ flags &= ~O_NONBLOCK;
+
+ if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
+ fatal("fcntl F_SETFL");
+}
+
+void
+imsg_event_add(struct imsgev *iev)
+{
+ if (iev->handler == NULL) {
+ imsg_flush(&iev->ibuf);
+ return;
+ }
+
+ iev->events = EV_READ;
+ if (iev->ibuf.w.queued)
+ iev->events |= EV_WRITE;
+
+ event_del(&iev->ev);
+ event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
+ event_add(&iev->ev, NULL);
+}
+
+int
+imsg_compose_event(struct imsgev *iev, u_int16_t type, u_int32_t peerid,
+ pid_t pid, int fd, void *data, u_int16_t datalen)
+{
+ int ret;
+
+ if ((ret = imsg_compose(&iev->ibuf, type, peerid,
+ pid, fd, data, datalen)) == -1)
+ return (ret);
+ imsg_event_add(iev);
+ return (ret);
+}
+
+int
+imsg_composev_event(struct imsgev *iev, u_int16_t type, u_int32_t peerid,
+ pid_t pid, int fd, const struct iovec *iov, int iovcnt)
+{
+ int ret;
+
+ if ((ret = imsg_composev(&iev->ibuf, type, peerid,
+ pid, fd, iov, iovcnt)) == -1)
+ return (ret);
+ imsg_event_add(iev);
+ return (ret);
+}
+
+int
+imsg_compose_proc(struct iked *env, enum iked_procid id,
+ u_int16_t type, int fd, void *data, u_int16_t datalen)
+{
+ return (imsg_compose_event(&env->sc_ievs[id],
+ type, -1, 0, fd, data, datalen));
+}
+
+int
+imsg_composev_proc(struct iked *env, enum iked_procid id,
+ u_int16_t type, int fd, const struct iovec *iov, int iovcnt)
+{
+ return (imsg_composev_event(&env->sc_ievs[id],
+ type, -1, 0, fd, iov, iovcnt));
+}
+
+int
+imsg_forward_proc(struct iked *env, struct imsg *imsg,
+ enum iked_procid id)
+{
+ return (imsg_compose_proc(env, id, imsg->hdr.type,
+ imsg->fd, imsg->data, IMSG_DATA_SIZE(imsg)));
+}
+
+void
+imsg_flush_proc(struct iked *env, enum iked_procid id)
+{
+ imsg_flush(&env->sc_ievs[id].ibuf);
+}
+
+int
+socket_af(struct sockaddr *sa, in_port_t port)
+{
+ errno = 0;
+ switch (sa->sa_family) {
+ case AF_INET:
+ ((struct sockaddr_in *)sa)->sin_port = port;
+ ((struct sockaddr_in *)sa)->sin_len =
+ sizeof(struct sockaddr_in);
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *)sa)->sin6_port = port;
+ ((struct sockaddr_in6 *)sa)->sin6_len =
+ sizeof(struct sockaddr_in6);
+ break;
+ default:
+ errno = EPFNOSUPPORT;
+ return (-1);
+ }
+
+ return (0);
+}
+
+in_port_t
+socket_getport(struct sockaddr_storage *ss)
+{
+ switch (ss->ss_family) {
+ case AF_INET:
+ return (ntohs(((struct sockaddr_in *)ss)->sin_port));
+ case AF_INET6:
+ return (ntohs(((struct sockaddr_in6 *)ss)->sin6_port));
+ default:
+ return (0);
+ }
+
+ /* NOTREACHED */
+ return (0);
+}
+
+int
+socket_bypass(int s, struct sockaddr *sa)
+{
+ int v, *a;
+ int a4[] = {
+ IPPROTO_IP,
+ IP_AUTH_LEVEL,
+ IP_ESP_TRANS_LEVEL,
+ IP_ESP_NETWORK_LEVEL,
+#ifdef IPV6_IPCOMP_LEVEL
+ IP_IPCOMP_LEVEL
+#endif
+ };
+ int a6[] = {
+ IPPROTO_IPV6,
+ IPV6_AUTH_LEVEL,
+ IPV6_ESP_TRANS_LEVEL,
+ IPV6_ESP_NETWORK_LEVEL,
+#ifdef IPV6_IPCOMP_LEVEL
+ IPV6_IPCOMP_LEVEL
+#endif
+ };
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ a = a4;
+ break;
+ case AF_INET6:
+ a = a6;
+ break;
+ default:
+ log_warn("%s: invalid address family", __func__);
+ return (-1);
+ }
+
+ v = IPSEC_LEVEL_BYPASS;
+ if (setsockopt(s, a[0], a[1], &v, sizeof(v)) == -1) {
+ log_warn("%s: AUTH_LEVEL", __func__);
+ return (-1);
+ }
+ if (setsockopt(s, a[0], a[2], &v, sizeof(v)) == -1) {
+ log_warn("%s: ESP_TRANS_LEVEL", __func__);
+ return (-1);
+ }
+ if (setsockopt(s, a[0], a[3], &v, sizeof(v)) == -1) {
+ log_warn("%s: ESP_NETWORK_LEVEL", __func__);
+ return (-1);
+ }
+#ifdef IP_IPCOMP_LEVEL
+ if (setsockopt(s, a[0], a[4], &v, sizeof(v)) == -1) {
+ log_warn("%s: IPCOMP_LEVEL", __func__);
+ return (-1);
+ }
+#endif
+
+ return (0);
+}
+
+int
+udp_bind(struct sockaddr *sa, in_port_t port)
+{
+ int s, val;
+
+ if (socket_af(sa, port) == -1) {
+ log_warn("%s: failed to set UDP port", __func__);
+ return (-1);
+ }
+
+ if ((s = socket(sa->sa_family, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
+ log_warn("%s: failed to get UDP socket", __func__);
+ return (-1);
+ }
+
+ /* Skip IPsec processing (don't encrypt) for IKE messages */
+ if (socket_bypass(s, sa) == -1) {
+ log_warn("%s: failed to bypass IPsec on IKE socket",
+ __func__);
+ goto bad;
+ }
+
+ val = 1;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(int)) == -1) {
+ log_warn("%s: failed to set reuseport", __func__);
+ goto bad;
+ }
+ val = 1;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(int)) == -1) {
+ log_warn("%s: failed to set reuseaddr", __func__);
+ goto bad;
+ }
+
+ if (sa->sa_family == AF_INET) {
+ val = 1;
+ if (setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR,
+ &val, sizeof(int)) == -1) {
+ log_warn("%s: failed to set IPv4 packet info",
+ __func__);
+ goto bad;
+ }
+ } else {
+ val = 1;
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO,
+ &val, sizeof(int)) == -1) {
+ log_warn("%s: failed to set IPv6 packet info",
+ __func__);
+ goto bad;
+ }
+ }
+
+ if (bind(s, sa, sa->sa_len) == -1) {
+ log_warn("%s: failed to bind UDP socket", __func__);
+ goto bad;
+ }
+
+ return (s);
+ bad:
+ close(s);
+ return (-1);
+}
+
+int
+sockaddr_cmp(struct sockaddr *a, struct sockaddr *b, int prefixlen)
+{
+ struct sockaddr_in *a4, *b4;
+ struct sockaddr_in6 *a6, *b6;
+ u_int32_t av[4], bv[4], mv[4];
+
+ if (b->sa_family != AF_UNSPEC && (a->sa_family > b->sa_family))
+ return (1);
+ if (b->sa_family != AF_UNSPEC && (a->sa_family < b->sa_family))
+ return (-1);
+
+ switch (a->sa_family) {
+ case AF_INET:
+ a4 = (struct sockaddr_in *)a;
+ b4 = (struct sockaddr_in *)b;
+
+ av[0] = a4->sin_addr.s_addr;
+ bv[0] = b4->sin_addr.s_addr;
+ mv[0] = prefixlen2mask(prefixlen);
+
+ if ((av[0] & mv[0]) > (bv[0] & mv[0]))
+ return (1);
+ if ((bv[0] & mv[0]) < (bv[0] & mv[0]))
+ return (-1);
+ break;
+ case AF_INET6:
+ a6 = (struct sockaddr_in6 *)a;
+ b6 = (struct sockaddr_in6 *)b;
+
+ memcpy(&av, &a6->sin6_addr.s6_addr, 16);
+ memcpy(&bv, &b6->sin6_addr.s6_addr, 16);
+ prefixlen2mask6(prefixlen, mv);
+
+ if ((av[3] & mv[3]) > (bv[3] & mv[3]))
+ return (1);
+ if ((av[3] & mv[3]) < (bv[3] & mv[3]))
+ return (-1);
+ if ((av[2] & mv[2]) > (bv[2] & mv[2]))
+ return (1);
+ if ((av[2] & mv[2]) < (bv[2] & mv[2]))
+ return (-1);
+ if ((av[1] & mv[1]) > (bv[1] & mv[1]))
+ return (1);
+ if ((av[1] & mv[1]) < (bv[1] & mv[1]))
+ return (-1);
+ if ((av[0] & mv[0]) > (bv[0] & mv[0]))
+ return (1);
+ if ((av[0] & mv[0]) < (bv[0] & mv[0]))
+ return (-1);
+ break;
+ }
+
+ return (0);
+}
+
+ssize_t
+recvfromto(int s, void *buf, size_t len, int flags, struct sockaddr *from,
+ socklen_t *fromlen, struct sockaddr *to, socklen_t *tolen)
+{
+ struct iovec iov;
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ struct in6_pktinfo *pkt6;
+ struct sockaddr_in *in;
+ struct sockaddr_in6 *in6;
+ ssize_t ret;
+ union {
+ struct cmsghdr hdr;
+ char buf[CMSG_SPACE(sizeof(struct sockaddr_storage))];
+ } cmsgbuf;
+
+ bzero(&msg, sizeof(msg));
+ bzero(&cmsgbuf.buf, sizeof(cmsgbuf.buf));
+
+ iov.iov_base = buf;
+ iov.iov_len = len;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_name = from;
+ msg.msg_namelen = *fromlen;
+ msg.msg_control = &cmsgbuf.buf;
+ msg.msg_controllen = sizeof(cmsgbuf.buf);
+
+ if ((ret = recvmsg(s, &msg, 0)) == -1)
+ return (-1);
+
+ *fromlen = from->sa_len;
+ *tolen = 0;
+
+ if (getsockname(s, to, tolen) != 0)
+ *tolen = 0;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ switch (from->sa_family) {
+ case AF_INET:
+ if (cmsg->cmsg_level == IPPROTO_IP &&
+ cmsg->cmsg_type == IP_RECVDSTADDR) {
+ in = (struct sockaddr_in *)to;
+ in->sin_family = AF_INET;
+ in->sin_len = *tolen = sizeof(*in);
+ memcpy(&in->sin_addr, CMSG_DATA(cmsg),
+ sizeof(struct in_addr));
+ }
+ break;
+ case AF_INET6:
+ if (cmsg->cmsg_level == IPPROTO_IPV6 &&
+ cmsg->cmsg_type == IPV6_PKTINFO) {
+ in6 = (struct sockaddr_in6 *)to;
+ in6->sin6_family = AF_INET6;
+ in6->sin6_len = *tolen = sizeof(*in6);
+ pkt6 = (struct in6_pktinfo *)CMSG_DATA(cmsg);
+ memcpy(&in6->sin6_addr, &pkt6->ipi6_addr,
+ sizeof(struct in6_addr));
+ if (IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr))
+ in6->sin6_scope_id =
+ pkt6->ipi6_ifindex;
+ }
+ break;
+ }
+ }
+
+ return (ret);
+}
+
+const char *
+print_spi(u_int64_t spi, int size)
+{
+ static char buf[IKED_CYCLE_BUFFERS][32];
+ static int i = 0;
+ char *ptr;
+
+ ptr = buf[i];
+
+ switch (size) {
+ case 4:
+ snprintf(ptr, 32, "0x%08x", (u_int32_t)spi);
+ break;
+ case 8:
+ snprintf(ptr, 32, "0x%016llx", spi);
+ break;
+ default:
+ snprintf(ptr, 32, "%llu", spi);
+ break;
+ }
+
+ if (++i >= IKED_CYCLE_BUFFERS)
+ i = 0;
+
+ return (ptr);
+}
+
+const char *
+print_map(u_int type, struct iked_constmap *map)
+{
+ u_int i;
+ static char buf[IKED_CYCLE_BUFFERS][32];
+ static int idx = 0;
+ const char *name = NULL;
+
+ if (idx >= IKED_CYCLE_BUFFERS)
+ idx = 0;
+ bzero(buf[idx], sizeof(buf[idx]));
+
+ for (i = 0; map[i].cm_name != NULL; i++) {
+ if (map[i].cm_type == type)
+ name = map[i].cm_name;
+ }
+
+ if (name == NULL)
+ snprintf(buf[idx], sizeof(buf[idx]), "<UNKNOWN:%u>", type);
+ else
+ strlcpy(buf[idx], name, sizeof(buf[idx]));
+
+ return (buf[idx++]);
+}
+
+void
+print_hex(u_int8_t *buf, off_t offset, size_t length)
+{
+ u_int i;
+ extern int verbose;
+
+ if (verbose < 2 || !length)
+ return;
+
+ for (i = 0; i < length; i++) {
+ if (i && (i % 4) == 0) {
+ if ((i % 32) == 0)
+ print_debug("\n");
+ else
+ print_debug(" ");
+ }
+ print_debug("%02x", buf[offset + i]);
+ }
+ print_debug("\n");
+}
+
+void
+print_hexval(u_int8_t *buf, off_t offset, size_t length)
+{
+ u_int i;
+ extern int verbose;
+
+ if (verbose < 2 || !length)
+ return;
+
+ print_debug("0x");
+ for (i = 0; i < length; i++)
+ print_debug("%02x", buf[offset + i]);
+ print_debug("\n");
+}
+
+const char *
+print_bits(u_short v, char *bits)
+{
+ static char buf[BUFSIZ];
+ u_int i, any = 0, j = 0;
+ char c;
+
+ if (!bits)
+ return ("");
+
+ bzero(buf, sizeof(buf));
+
+ bits++;
+ while ((i = *bits++)) {
+ if (v & (1 << (i-1))) {
+ if (any) {
+ buf[j++] = ',';
+ if (j >= sizeof(buf))
+ return (buf);
+ }
+ any = 1;
+ for (; (c = *bits) > 32; bits++) {
+ buf[j++] = tolower(c);
+ if (j >= sizeof(buf))
+ return (buf);
+ }
+ } else
+ for (; *bits > 32; bits++)
+ ;
+ }
+
+ return (buf);
+}
+
+u_int32_t
+prefixlen2mask(u_int8_t prefixlen)
+{
+ if (prefixlen == 0)
+ return (0);
+
+ if (prefixlen > 32)
+ prefixlen = 32;
+
+ return (htonl(0xffffffff << (32 - prefixlen)));
+}
+
+struct in6_addr *
+prefixlen2mask6(u_int8_t prefixlen, u_int32_t *mask)
+{
+ static struct in6_addr s6;
+ int i;
+
+ if (prefixlen > 128)
+ prefixlen = 128;
+
+ bzero(&s6, sizeof(s6));
+ for (i = 0; i < prefixlen / 8; i++)
+ s6.s6_addr[i] = 0xff;
+ i = prefixlen % 8;
+ if (i)
+ s6.s6_addr[prefixlen / 8] = 0xff00 >> i;
+
+ memcpy(mask, &s6, sizeof(s6));
+
+ return (&s6);
+}
+
+const char *
+print_host(struct sockaddr_storage *ss, char *buf, size_t len)
+{
+ static char sbuf[IKED_CYCLE_BUFFERS][NI_MAXHOST + 7];
+ static int idx = 0;
+ char pbuf[7];
+ in_port_t port;
+
+ if (buf == NULL) {
+ buf = sbuf[idx];
+ len = sizeof(sbuf[idx]);
+ if (++idx >= IKED_CYCLE_BUFFERS)
+ idx = 0;
+ }
+
+ if (ss->ss_family == AF_UNSPEC) {
+ strlcpy(buf, "any", len);
+ return (buf);
+ }
+
+ if (getnameinfo((struct sockaddr *)ss, ss->ss_len,
+ buf, len, NULL, 0, NI_NUMERICHOST) != 0) {
+ buf[0] = '\0';
+ return (NULL);
+ }
+
+ if ((port = socket_getport(ss)) != 0) {
+ snprintf(pbuf, sizeof(pbuf), ":%d", port);
+ (void)strlcat(buf, pbuf, len);
+ }
+
+ return (buf);
+}
+
+char *
+get_string(u_int8_t *ptr, size_t len)
+{
+ size_t i;
+ char *str;
+
+ for (i = 0; i < len; i++)
+ if (!isprint((char)ptr[i]))
+ break;
+
+ if ((str = calloc(1, i + 1)) == NULL)
+ return (NULL);
+ memcpy(str, ptr, i);
+
+ return (str);
+}
+
+int
+print_id(struct iked_id *id, off_t offset, char *idstr, size_t idstrlen)
+{
+ u_int8_t buf[BUFSIZ], *ptr;
+ struct sockaddr_in *s4;
+ struct sockaddr_in6 *s6;
+ char *str;
+ ssize_t len;
+ int i;
+
+ bzero(buf, sizeof(buf));
+
+ if (id->id_buf == NULL)
+ return (-1);
+
+ len = ibuf_size(id->id_buf);
+ ptr = ibuf_data(id->id_buf);
+
+ if (len <= offset)
+ return (-1);
+
+ len -= offset;
+ ptr += offset;
+
+ switch (id->id_type) {
+ case IKEV2_ID_IPV4_ADDR:
+ s4 = (struct sockaddr_in *)buf;
+ s4->sin_family = AF_INET;
+ s4->sin_len = sizeof(*s4);
+ memcpy(&s4->sin_addr.s_addr, ptr, len);
+
+ if (print_host((struct sockaddr_storage *)s4,
+ idstr, idstrlen) == NULL)
+ return (-1);
+ break;
+ case IKEV2_ID_FQDN:
+ case IKEV2_ID_RFC822_ADDR:
+ if (len >= (ssize_t)sizeof(buf))
+ return (-1);
+
+ if ((str = get_string(ptr, len)) == NULL)
+ return (-1);
+
+ if (strlcpy(idstr, str, idstrlen) >= idstrlen) {
+ free(str);
+ return (-1);
+ }
+ free(str);
+ break;
+ case IKEV2_ID_IPV6_ADDR:
+ s6 = (struct sockaddr_in6 *)buf;
+ s6->sin6_family = AF_INET6;
+ s6->sin6_len = sizeof(*s6);
+ memcpy(&s6->sin6_addr, ptr, len);
+
+ if (print_host((struct sockaddr_storage *)s6,
+ idstr, idstrlen) == NULL)
+ return (-1);
+ break;
+ case IKEV2_ID_DER_ASN1_DN:
+ if ((str = ca_asn1_name(ptr, len)) == NULL)
+ return (-1);
+ if (strlcpy(idstr, str, idstrlen) >= idstrlen) {
+ free(str);
+ return (-1);
+ }
+ free(str);
+ break;
+ default:
+ /* XXX test */
+ for (i = 0; i < ((ssize_t)idstrlen - 1) && i < len; i++)
+ snprintf(idstr + i, idstrlen - i,
+ "%02x", ptr[i]);
+ break;
+ }
+
+ return (0);
+}
+
+const char *
+print_proto(u_int8_t proto)
+{
+ struct protoent *p;
+ static char buf[IKED_CYCLE_BUFFERS][BUFSIZ];
+ static int idx = 0;
+
+ if (idx >= IKED_CYCLE_BUFFERS)
+ idx = 0;
+
+ if ((p = getprotobynumber(proto)) != NULL)
+ strlcpy(buf[idx], p->p_name, sizeof(buf[idx]));
+ else
+ snprintf(buf[idx], sizeof(buf), "%u", proto);
+
+
+ return (buf[idx++]);
+}
+
+void
+message_cleanup(struct iked *env, struct iked_message *msg)
+{
+ if (msg->msg_data != NULL) {
+ ibuf_release(msg->msg_data);
+ msg->msg_data = NULL;
+ }
+}
+
+int
+expand_string(char *label, size_t len, const char *srch, const char *repl)
+{
+ char *tmp;
+ char *p, *q;
+
+ if ((tmp = calloc(1, len)) == NULL) {
+ log_debug("expand_string: calloc");
+ return (-1);
+ }
+ p = q = label;
+ while ((q = strstr(p, srch)) != NULL) {
+ *q = '\0';
+ if ((strlcat(tmp, p, len) >= len) ||
+ (strlcat(tmp, repl, len) >= len)) {
+ log_debug("expand_string: string too long");
+ return (-1);
+ }
+ q += strlen(srch);
+ p = q;
+ }
+ if (strlcat(tmp, p, len) >= len) {
+ log_debug("expand_string: string too long");
+ return (-1);
+ }
+ strlcpy(label, tmp, len); /* always fits */
+ free(tmp);
+
+ return (0);
+}
+
+u_int8_t *
+string2unicode(const char *ascii, size_t *outlen)
+{
+ u_int8_t *uc = NULL;
+ size_t i, len = strlen(ascii);
+
+ if ((uc = calloc(1, (len * 2) + 2)) == NULL)
+ return (NULL);
+
+ for (i = 0; i < len; i++) {
+ /* XXX what about the byte order? */
+ uc[i * 2] = ascii[i];
+ }
+ *outlen = len * 2;
+
+ return (uc);
+}
+
+/*
+ * Extending the imsg buffer API for internal use
+ */
+
+int
+ibuf_cat(struct ibuf *dst, struct ibuf *src)
+{
+ return (ibuf_add(dst, src->buf, ibuf_size(src)));
+}
+
+void
+ibuf_zero(struct ibuf *buf)
+{
+ memset(buf->buf, 0, buf->wpos);
+}
+
+struct ibuf *
+ibuf_new(void *data, size_t len)
+{
+ struct ibuf *buf;
+
+ if ((buf = ibuf_dynamic(len,
+ IKED_MSGBUF_MAX)) == NULL)
+ return (NULL);
+
+ ibuf_zero(buf);
+
+ if (data == NULL && len) {
+ if (ibuf_advance(buf, len) == NULL) {
+ ibuf_free(buf);
+ return (NULL);
+ }
+ } else {
+ if (ibuf_add(buf, data, len) != 0) {
+ ibuf_free(buf);
+ return (NULL);
+ }
+ }
+
+ return (buf);
+}
+
+struct ibuf *
+ibuf_static(void)
+{
+ struct ibuf *buf;
+
+ if ((buf = ibuf_open(IKED_MSGBUF_MAX)) == NULL)
+ return (NULL);
+
+ ibuf_zero(buf);
+
+ return (buf);
+}
+
+void *
+ibuf_advance(struct ibuf *buf, size_t len)
+{
+ void *ptr;
+
+ if ((ptr = ibuf_reserve(buf, len)) != NULL)
+ memset(ptr, 0, len);
+
+ return (ptr);
+}
+
+void
+ibuf_release(struct ibuf *buf)
+{
+ if (buf == NULL)
+ return;
+ if (buf->buf != NULL)
+ free(buf->buf);
+ free(buf);
+}
+
+size_t
+ibuf_length(struct ibuf *buf)
+{
+ if (buf == NULL || buf->buf == NULL)
+ return (0);
+ return (ibuf_size(buf));
+}
+
+u_int8_t *
+ibuf_data(struct ibuf *buf)
+{
+ return (ibuf_seek(buf, 0, 0));
+}
+
+void *
+ibuf_get(struct ibuf *buf, size_t len)
+{
+ void *data;
+
+ if ((data = ibuf_seek(buf, buf->rpos, len)) == NULL)
+ return (NULL);
+ buf->rpos += len;
+
+ return (data);
+}
+
+struct ibuf *
+ibuf_copy(struct ibuf *buf, size_t len)
+{
+ void *data;
+
+ if ((data = ibuf_get(buf, len)) == NULL)
+ return (NULL);
+
+ return (ibuf_new(data, len));
+}
+
+struct ibuf *
+ibuf_dup(struct ibuf *buf)
+{
+ return (ibuf_new(ibuf_data(buf), ibuf_size(buf)));
+}
+
+struct ibuf *
+ibuf_random(size_t len)
+{
+ struct ibuf *buf;
+ void *ptr;
+
+ if ((buf = ibuf_open(len)) == NULL)
+ return (NULL);
+ if ((ptr = ibuf_reserve(buf, len)) == NULL) {
+ ibuf_free(buf);
+ return (NULL);
+ }
+ arc4random_buf(ptr, len);
+ return (buf);
+}
+
+int
+ibuf_setsize(struct ibuf *buf, size_t len)
+{
+ if (len > buf->size)
+ return (-1);
+ buf->wpos = len;
+ return (0);
+}
+
+int
+ibuf_prepend(struct ibuf *buf, void *data, size_t len)
+{
+ struct ibuf *new;
+
+ /* Swap buffers (we could also use memmove here) */
+ if ((new = ibuf_new(data, len)) == NULL)
+ return (-1);
+ if (ibuf_cat(new, buf) == -1) {
+ ibuf_release(new);
+ return (-1);
+ }
+ free(buf->buf);
+ memcpy(buf, new, sizeof(*buf));
+ free(new);
+
+ return (0);
+}
diff --git a/usr.sbin/ikectl/Makefile b/usr.sbin/ikectl/Makefile
new file mode 100644
index 00000000000..09c2ed5c175
--- /dev/null
+++ b/usr.sbin/ikectl/Makefile
@@ -0,0 +1,23 @@
+# $OpenBSD: Makefile,v 1.1 2010/06/03 16:41:12 reyk Exp $
+# $vantronix: Makefile,v 1.9 2010/06/03 15:57:59 reyk Exp $
+
+.PATH: ${.CURDIR}/../../sbin/iked
+
+PROG= ikectl
+SRCS= log.c ikeca.c ikectl.c parser.c
+
+MAN= ikectl.8
+
+LDADD= -lutil -lcrypto
+DPADD= ${LIBUTIL}
+CFLAGS+= -Wall -I${.CURDIR} -I${.CURDIR}/../../sbin/iked
+CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
+CFLAGS+= -Wmissing-declarations
+CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
+CFLAGS+= -Wsign-compare -Wbounded
+
+#distribution:
+# ${INSTALL} -C -g wheel -m 0644 ${.CURDIR}/ikeca.cnf \
+# ${DESTDIR}/etc/ssl/ikeca.cnf
+
+.include <bsd.prog.mk>