diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 999d144..0000000 --- a/.gitignore +++ /dev/null @@ -1,34 +0,0 @@ -*.o -*.a -*.d -*.in -*.out -*.log -*~ -*.cat? -.#* -.deps -.DS_Store -m4 -aclocal.m4 -Makefile -autom4te.cache -compile -config.guess -config.h -config.status -config.sub -configure -depcomp -install-sh -libtool -ltmain.sh -missing -parse.c -stamp-h1 -ylwrap -iked -ikectl -*_map.c -clang_* -*-scan-build diff --git a/README.md b/README.md index 81e2b14..6a51544 100644 --- a/README.md +++ b/README.md @@ -53,11 +53,11 @@ also need GNU automake, autoconf (version 2.69 or newer) and libtool. `sh bootstrap` to generate the required build files. 4. Run `./configure` in this directory to generate the Makefiles. - `./configure --help` will show you some available build options. - - For example, you can run the following when building on Apple OSX + - For example, you can run the following when building on Apple OS X with MacPorts: ``` - ./configure --with-ssl-dir=/opt/local/ - --with-libevent-dir=/opt/local/lib/libevent1/ + ./configure --with-ssl-dir=/opt/local/ \ + --with-libevent-dir=/opt/local/lib/libevent1/ \ --prefix=/opt/local/ ``` 5. Type `make` to build all parts of OpenIKED including iked and ikectl. @@ -76,6 +76,23 @@ on Linux. # useradd -g _iked -c 'iked privsep' -d /var/empty -s /sbin/nologin _iked ``` +For creating system users on newer versions of Apple OS X, refer to +the `dscl` command line utility, eg. +``` + # dscl . -list /Groups gid | sort -n -k 2 + # dscl . -create /Groups/_iked gid 300 + # dscl . -create /Groups/_iked RealName "OpenIKED" + # dscl . -create /Groups/_iked passwd "*" + # dscl . -list /Users UniqueID | sort -n -k 2 + # dscl . -create /Users/_iked + # dscl . -create /Users/_iked NFSHomeDirectory /var/empty + # dscl . -create /Users/_iked uid 300 + # dscl . -create /Users/_iked gid 300 + # dscl . -create /Users/_iked UserShell /usr/bin/false + # dscl . -create /Users/_iked RealName "OpenIKED" + # dscl . -create /Users/_iked passwd "*" +``` + 8. On FreeBSD, NetBSD and maybe other BSD-variants IPsec is not enabled in the default GENERIC kernel. You have to compile a custom kernel and enable options like `IPSEC`. Please refer to the @@ -89,7 +106,7 @@ Supported Platforms Original version: -* OpenBSD 4.8 or newer +* OpenBSD 6.0 or newer Portable version: diff --git a/bootstrap b/bootstrap index 6cfcf9d..b8d8fe5 100755 --- a/bootstrap +++ b/bootstrap @@ -36,7 +36,7 @@ aclocalflags="`sed -ne 's/^[ \t]*ACLOCAL_AMFLAGS[ \t]*=//p' Makefile.am 2>/dev/n # Check for automake amvers="no" -for v in 12 11 10 9 8 7 6 5; do +for v in 15 14 13 12 11 10 9 8 7 6 5; do if automake-1.${v} --version >/dev/null 2>&1; then amvers="-1.${v}" break diff --git a/configure.ac b/configure.ac index 1705862..4c0d2f9 100644 --- a/configure.ac +++ b/configure.ac @@ -22,7 +22,7 @@ AC_CONFIG_HEADER([config.h]) AC_CANONICAL_HOST AC_C_BIGENDIAN -AM_INIT_AUTOMAKE +AM_INIT_AUTOMAKE() # Checks for programs. AC_PROG_CC @@ -73,16 +73,18 @@ AC_CHECK_DECL([LLONG_MAX], [have_llong_max=1], , [#include ]) if test "$GCC" = "yes" || test "$GCC" = "egcs"; then CFLAGS="$CFLAGS -Wall -Wpointer-arith -Wuninitialized" - GCC_VER=`$CC -v 2>&1 | $AWK '/gcc version /{print $3}'` + GCC_VER=`$CC -v 2>&1 | grep ' version '` case $GCC_VER in - 1.*) no_attrib_nonnull=1 ;; - 2.8* | 2.9*) - CFLAGS="$CFLAGS -Wsign-compare" - no_attrib_nonnull=1 - ;; - 2.*) no_attrib_nonnull=1 ;; - 3.*) CFLAGS="$CFLAGS -Wsign-compare" ;; - 4.*) CFLAGS="$CFLAGS -Wsign-compare -Wno-pointer-sign" ;; + gcc*2.*) no_attrib_nonnull=1 ;; + gcc*3.*) CFLAGS="$CFLAGS -Wsign-compare" ;; + gcc*4.8*) + CFLAGS="$CFLAGS -Wsign-compare -Wno-pointer-sign" + CFLAGS="$CFLAGS -Wno-unused-result" + ;; + gcc*4.*) CFLAGS="$CFLAGS -Wsign-compare -Wno-pointer-sign" ;; + *LLVM*|*clang*) + CFLAGS="$CFLAGS -Wsign-compare -Wno-pointer-sign" + ;; *) ;; esac @@ -139,6 +141,18 @@ AC_CHECK_HEADERS([\ getopt.h \ libkern/OSByteOrder.h \ ndir.h \ + sys/types.h \ + sys/cdefs.h \ + sys/endian.h \ + sys/ndir.h \ + sys/param.h \ + sys/pstat.h \ + sys/socket.h \ + sys/stat.h \ + sys/sysctl.h \ + sys/uio.h \ + sys/un.h \ + sys/wait.h \ net/pfkeyv2.h \ linux/pfkeyv2.h \ linux/in.h \ @@ -158,22 +172,17 @@ AC_CHECK_HEADERS([\ stdio.h \ stdlib.h \ string.h \ - sys/cdefs.h \ - sys/endian.h \ - sys/ndir.h \ - sys/param.h \ - sys/pstat.h \ - sys/socket.h \ - sys/stat.h \ - sys/sysctl.h \ - sys/types.h \ - sys/uio.h \ - sys/un.h \ - sys/wait.h \ syslog.h \ ucred.h \ unistd.h \ vis.h \ +], , , [ +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_SOCKET_H +# include +#endif ]) # login_cap.h requires sys/types.h on NetBSD @@ -428,6 +437,7 @@ else fi AC_CHECK_FUNCS([ \ + accept4 \ arc4random \ arc4random_buf \ arc4random_uniform \ @@ -437,6 +447,8 @@ AC_CHECK_FUNCS([ \ closefrom \ dirfd \ dirname \ + errc \ + explicit_bzero \ fgetln \ freeaddrinfo \ getaddrinfo \ @@ -444,6 +456,8 @@ AC_CHECK_FUNCS([ \ getopt \ getpeereid \ memmove \ + pledge \ + realloc_array \ setproctitle \ setregid \ setreuid \ @@ -454,6 +468,7 @@ AC_CHECK_FUNCS([ \ strmode \ sysconf \ waitpid \ + warnc \ ]) # IRIX has a const char return value for gai_strerror() @@ -492,6 +507,19 @@ AC_CHECK_DECLS([O_NONBLOCK], , , #endif ]) +AC_CHECK_DECLS([SOCK_NONBLOCK], , , [#include ]) +AC_CHECK_DECLS([SOCK_CLOEXEC], , , [#include ]) +AC_CHECK_DECLS([IP_RECVDSTADDR], , , [ +#include +#include +#include +]) +AC_CHECK_DECLS([IP_RECVORIGDSTADDR], , , [ +#include +#include +#include +]) + AC_CHECK_DECLS([writev], , , [ #include #include @@ -1699,10 +1727,15 @@ AC_DEFINE_UNQUOTED([IKED_USER], ["$IKED_USER"], [non-privileged user for privilege separation]) AC_SUBST([IKED_USER]) +_prefix=`eval echo ${prefix}` +if test x$_prefix = xNONE; then + _prefix=$ac_default_prefix +fi + _sysconfdir=`eval echo ${sysconfdir}` case $_sysconfdir in NONE/*) - _sysconfdir=`echo $_sysconfdir | sed "s~NONE~$ac_default_prefix~"` ;; + _sysconfdir=`echo $_sysconfdir | sed "s~NONE~$_prefix~"` ;; esac ikedconf=`eval echo ${_sysconfdir}/iked.conf` @@ -2458,7 +2491,8 @@ if test "x$ZIP" != "x"; then AC_DEFINE_UNQUOTED([PATH_ZIP], ["$ZIP"], [Path to ZIP binary]) fi -AC_DEFINE_UNQUOTED([PREFIX], ["$prefix"], [Root directory prefix]) + +AC_DEFINE_UNQUOTED([PREFIX], ["$_prefix"], [Root directory prefix]) AC_DEFINE([_GNU_SOURCE], [1], [Enable GNU Extensions]) diff --git a/ikectl/.cvsignore b/ikectl/.cvsignore deleted file mode 100644 index 536efe1..0000000 --- a/ikectl/.cvsignore +++ /dev/null @@ -1,5 +0,0 @@ -.cvsignore -.deps -Makefile -Makefile.am -Makefile.in diff --git a/ikectl/CVS/Entries b/ikectl/CVS/Entries deleted file mode 100644 index 76336d8..0000000 --- a/ikectl/CVS/Entries +++ /dev/null @@ -1,8 +0,0 @@ -/Makefile/1.2/Result of merge+Fri Jan 11 08:23:23 2013// -/ikeca.c/1.25/Result of merge// -/ikectl.c/1.16/Result of merge// -/parser.c/1.12/Result of merge// -/ikeca.cnf/1.5/Sat Dec 15 14:53:03 2012// -/ikectl.8/1.20/Mon Jan 14 14:11:49 2013// -/parser.h/1.12/Mon Jan 14 14:11:49 2013// -D diff --git a/ikectl/CVS/Repository b/ikectl/CVS/Repository deleted file mode 100644 index 2a96acb..0000000 --- a/ikectl/CVS/Repository +++ /dev/null @@ -1 +0,0 @@ -src/usr.sbin/ikectl diff --git a/ikectl/CVS/Root b/ikectl/CVS/Root deleted file mode 100644 index 13631a1..0000000 --- a/ikectl/CVS/Root +++ /dev/null @@ -1 +0,0 @@ -anoncvs@anoncvs.openbsd.org:/cvs diff --git a/ikectl/Makefile.am b/ikectl/Makefile.am index 4f44e8b..4d70694 100644 --- a/ikectl/Makefile.am +++ b/ikectl/Makefile.am @@ -1,6 +1,8 @@ sbin_PROGRAMS= ikectl -ikectl_SOURCES= ikeca.c ikectl.c parser.c $(top_srcdir)/iked/log.c +ikectl_SOURCES= ikeca.c ikectl.c parser.c \ + $(top_srcdir)/iked/log.c \ + $(top_srcdir)/iked/util.c ikectl_CFLAGS= LIBCOMPAT= $(top_builddir)/openbsd-compat/libopenbsd-compat.a diff --git a/ikectl/ikeca.c b/ikectl/ikeca.c index 4b84090..13a255b 100644 --- a/ikectl/ikeca.c +++ b/ikectl/ikeca.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ikeca.c,v 1.25 2013/01/08 10:38:19 reyk Exp $ */ +/* $OpenBSD: ikeca.c,v 1.40 2015/11/02 12:21:27 jsg Exp $ */ /* * Copyright (c) 2010 Jonathan Gray @@ -16,7 +16,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include #include #include #include @@ -29,6 +28,7 @@ #include #include #include +#include #include #include @@ -52,7 +52,7 @@ #endif #ifndef PATH_OPENSSL -#define PATH_OPENSSL "/usr/sbin/openssl" +#define PATH_OPENSSL "/usr/bin/openssl" #endif #ifndef PATH_ZIP #define PATH_ZIP "/usr/local/bin/zip" @@ -64,6 +64,8 @@ struct ca { char sslpath[PATH_MAX]; char passfile[PATH_MAX]; + char index[PATH_MAX]; + char serial[PATH_MAX]; char sslcnf[PATH_MAX]; char extcnf[PATH_MAX]; char batch[PATH_MAX]; @@ -82,13 +84,41 @@ struct { { "/private", 0700 } }; -int ca_sign(struct ca *, char *, int, char *); +/* explicitly list allowed variables */ +const char *ca_env[][2] = { + { "$ENV::CADB", NULL }, + { "$ENV::CASERIAL", NULL }, + { "$ENV::CERTFQDN", NULL }, + { "$ENV::CERTIP", NULL }, + { "$ENV::CERTPATHLEN", NULL }, + { "$ENV::CERTUSAGE", NULL }, + { "$ENV::CERT_C", NULL }, + { "$ENV::CERT_CN", NULL }, + { "$ENV::CERT_EMAIL", NULL }, + { "$ENV::CERT_L", NULL }, + { "$ENV::CERT_O", NULL }, + { "$ENV::CERT_OU", NULL }, + { "$ENV::CERT_ST", NULL }, + { "$ENV::EXTCERTUSAGE", NULL }, + { "$ENV::NSCERTTYPE", NULL }, + { NULL } +}; + +int ca_sign(struct ca *, char *, int); int ca_request(struct ca *, char *); -int ca_newpass(char *, char *); -char * ca_readpass(char *, size_t *); +void ca_newpass(char *, char *); +char *ca_readpass(char *, size_t *); int fcopy(char *, char *, mode_t); +void fcopy_env(const char *, const char *, mode_t); int rm_dir(char *); -int ca_hier(char *); +void ca_hier(char *); +void ca_setenv(const char *, const char *); +void ca_clrenv(void); +void ca_setcnf(struct ca *, const char *); +void ca_create_index(struct ca *); + +/* util.c */ +int expand_string(char *, size_t, const char *, const char *); int ca_delete(struct ca *ca) @@ -173,10 +203,13 @@ ca_request(struct ca *ca, char *keyname) char cmd[PATH_MAX * 2]; char path[PATH_MAX]; + ca_setenv("$ENV::CERT_CN", keyname); + ca_setcnf(ca, keyname); + snprintf(path, sizeof(path), "%s/private/%s.csr", ca->sslpath, keyname); - snprintf(cmd, sizeof(cmd), "env CERT_CN=%s %s req %s-new" + snprintf(cmd, sizeof(cmd), "%s req %s-new" " -key %s/private/%s.key -out %s -config %s", - keyname, PATH_OPENSSL, ca->batch, ca->sslpath, keyname, + PATH_OPENSSL, ca->batch, ca->sslpath, keyname, path, ca->sslcnf); system(cmd); @@ -186,40 +219,47 @@ ca_request(struct ca *ca, char *keyname) } int -ca_sign(struct ca *ca, char *keyname, int type, char *envargs) +ca_sign(struct ca *ca, char *keyname, int type) { char cmd[PATH_MAX * 2]; - char hostname[MAXHOSTNAMELEN]; + char hostname[_POSIX_HOST_NAME_MAX+1]; char name[128]; + const char *extensions = NULL; strlcpy(name, keyname, sizeof(name)); - if (envargs == NULL) - envargs = ""; - if (type == HOST_IPADDR) { - snprintf(cmd, sizeof(cmd), "env CERTIP=%s%s %s x509 -req" - " -days 365 -in %s/private/%s.csr" - " -CA %s/ca.crt -CAkey %s/private/ca.key -CAcreateserial" - " -extfile %s -extensions x509v3_IPAddr -out %s/%s.crt" - " -passin file:%s", name, envargs, PATH_OPENSSL, - ca->sslpath, keyname, ca->sslpath, ca->sslpath, - ca->extcnf, ca->sslpath, keyname, ca->passfile); + ca_setenv("$ENV::CERTIP", name); + extensions = "x509v3_IPAddr"; } else if (type == HOST_FQDN) { if (!strcmp(keyname, "local")) { if (gethostname(hostname, sizeof(hostname))) err(1, "gethostname"); strlcpy(name, hostname, sizeof(name)); } - snprintf(cmd, sizeof(cmd), "env CERTFQDN=%s%s %s x509 -req" - " -days 365 -in %s/private/%s.csr" - " -CA %s/ca.crt -CAkey %s/private/ca.key -CAcreateserial" - " -extfile %s -extensions x509v3_FQDN -out %s/%s.crt" - " -passin file:%s", name, envargs, PATH_OPENSSL, - ca->sslpath, keyname, ca->sslpath, ca->sslpath, - ca->extcnf, ca->sslpath, keyname, ca->passfile); - } else - err(1, "unknown host type %d", type); + ca_setenv("$ENV::CERTFQDN", name); + extensions = "x509v3_FQDN"; + } else { + errx(1, "unknown host type %d", type); + } + + ca_create_index(ca); + + ca_setenv("$ENV::CADB", ca->index); + ca_setenv("$ENV::CASERIAL", ca->serial); + ca_setcnf(ca, keyname); + + snprintf(cmd, sizeof(cmd), + "%s ca -config %s -keyfile %s/private/ca.key" + " -cert %s/ca.crt" + " -extfile %s -extensions %s -out %s/%s.crt" + " -in %s/private/%s.csr" + " -passin file:%s -outdir %s -batch", + PATH_OPENSSL, ca->sslcnf, ca->sslpath, + ca->sslpath, + ca->extcnf, extensions, ca->sslpath, keyname, + ca->sslpath, keyname, + ca->passfile, ca->sslpath); system(cmd); @@ -229,16 +269,25 @@ ca_sign(struct ca *ca, char *keyname, int type, char *envargs) int ca_certificate(struct ca *ca, char *keyname, int type, int action) { - char *envargs = ""; + ca_clrenv(); switch (action) { case CA_SERVER: - envargs = " EXTCERTUSAGE=serverAuth NSCERTTYPE=server" - " CERTUSAGE=digitalSignature,keyEncipherment"; + ca_setenv("$ENV::EXTCERTUSAGE", "serverAuth"); + ca_setenv("$ENV::NSCERTTYPE", "server"); + ca_setenv("$ENV::CERTUSAGE", + "digitalSignature,keyEncipherment"); break; case CA_CLIENT: - envargs = " EXTCERTUSAGE=clientAuth NSCERTTYPE=client" - " CERTUSAGE=digitalSignature,keyAgreement"; + ca_setenv("$ENV::EXTCERTUSAGE", "clientAuth"); + ca_setenv("$ENV::NSCERTTYPE", "client"); + ca_setenv("$ENV::CERTUSAGE", + "digitalSignature,keyAgreement"); + break; + case CA_OCSP: + ca_setenv("$ENV::EXTCERTUSAGE", "OCSPSigning"); + ca_setenv("$ENV::CERTUSAGE", + "nonRepudiation,digitalSignature,keyEncipherment"); break; default: break; @@ -246,7 +295,7 @@ ca_certificate(struct ca *ca, char *keyname, int type, int action) ca_key_create(ca, keyname); ca_request(ca, keyname); - ca_sign(ca, keyname, type, envargs); + ca_sign(ca, keyname, type); return (0); } @@ -313,7 +362,7 @@ ca_cert_install(struct ca *ca, char *keyname, char *dir) return (0); } -int +void ca_newpass(char *passfile, char *password) { FILE *f; @@ -342,8 +391,6 @@ ca_newpass(char *passfile, char *password) fprintf(f, "%s\n%s\n", pass, pass); fclose(f); - - return (0); } int @@ -352,6 +399,8 @@ ca_create(struct ca *ca) char cmd[PATH_MAX * 2]; char path[PATH_MAX]; + ca_clrenv(); + snprintf(path, sizeof(path), "%s/private/ca.key", ca->sslpath); snprintf(cmd, sizeof(cmd), "%s genrsa -aes256 -out" " %s -passout file:%s 2048", PATH_OPENSSL, @@ -359,8 +408,11 @@ ca_create(struct ca *ca) system(cmd); chmod(path, 0600); + ca_setenv("$ENV::CERT_CN", "VPN CA"); + ca_setcnf(ca, "ca"); + snprintf(path, sizeof(path), "%s/private/ca.csr", ca->sslpath); - snprintf(cmd, sizeof(cmd), "env CERT_CN='VPN CA' %s req %s-new" + snprintf(cmd, sizeof(cmd), "%s req %s-new" " -key %s/private/ca.key" " -config %s -out %s -passin file:%s", PATH_OPENSSL, ca->batch, ca->sslpath, ca->sslcnf, path, ca->passfile); @@ -369,6 +421,7 @@ ca_create(struct ca *ca) snprintf(cmd, sizeof(cmd), "%s x509 -req -days 365" " -in %s/private/ca.csr -signkey %s/private/ca.key" + " -sha256" " -extfile %s -extensions x509v3_CA -out %s/ca.crt -passin file:%s", PATH_OPENSSL, ca->sslpath, ca->sslpath, ca->extcnf, ca->sslpath, ca->passfile); @@ -443,8 +496,8 @@ ca_show_certs(struct ca *ca, char *name) err(1, "could not open directory %s", ca->sslpath); while ((de = readdir(dir)) != NULL) { - if (NAMLEN(de) > 4) { - p = de->d_name + NAMLEN(de) - 4; + if (strlen(de->d_name) > 4) { + p = de->d_name + strlen(de->d_name) - 4; if (strcmp(".crt", p) != 0) continue; snprintf(path, sizeof(path), "%s/%s", ca->sslpath, @@ -466,15 +519,16 @@ int fcopy(char *src, char *dst, mode_t mode) { int ifd, ofd; - u_int8_t buf[BUFSIZ]; + uint8_t buf[BUFSIZ]; ssize_t r; if ((ifd = open(src, O_RDONLY)) == -1) err(1, "open %s", src); if ((ofd = open(dst, O_WRONLY|O_CREAT|O_TRUNC, mode)) == -1) { + int saved_errno = errno; close(ifd); - err(1, "open %s", dst); + errc(1, saved_errno, "open %s", dst); } while ((r = read(ifd, buf, sizeof(buf))) > 0) { @@ -487,6 +541,45 @@ fcopy(char *src, char *dst, mode_t mode) return (r == -1); } +void +fcopy_env(const char *src, const char *dst, mode_t mode) +{ + int ofd = -1, i; + uint8_t buf[BUFSIZ]; + ssize_t r = -1, len; + FILE *ifp = NULL; + int saved_errno; + + if ((ifp = fopen(src, "r")) == NULL) + err(1, "fopen %s", src); + + if ((ofd = open(dst, O_WRONLY|O_CREAT|O_TRUNC, mode)) == -1) + goto done; + + while (fgets(buf, sizeof(buf), ifp) != NULL) { + for (i = 0; ca_env[i][0] != NULL; i++) { + if (ca_env[i][1] == NULL) + continue; + if (expand_string(buf, sizeof(buf), + ca_env[i][0], ca_env[i][1]) == -1) + errx(1, "env %s value too long", ca_env[i][0]); + } + len = strlen(buf); + if (write(ofd, buf, len) != len) + goto done; + } + + r = 0; + + done: + saved_errno = errno; + close(ofd); + if (ifp != NULL) + fclose(ifp); + if (r == -1) + errc(1, saved_errno, "open %s", dst); +} + int rm_dir(char *path) { @@ -522,13 +615,12 @@ rm_dir(char *path) return (0); } - -int +void ca_hier(char *path) { struct stat st; char dst[PATH_MAX]; - u_int i; + unsigned int i; for (i = 0; i < nitems(hier); i++) { strlcpy(dst, path, sizeof(dst)); @@ -537,8 +629,6 @@ ca_hier(char *path) mkdir(dst, hier[i].mode) != 0) err(1, "failed to create dir %s", dst); } - - return (0); } int @@ -555,12 +645,12 @@ ca_export(struct ca *ca, char *keyname, char *myname, char *password) char dst[PATH_MAX]; char *p; char tpl[] = "/tmp/ikectl.XXXXXXXXXX"; - u_int i; + unsigned int i; int fd; if (keyname != NULL) { if (strlcpy(oname, keyname, sizeof(oname)) >= sizeof(oname)) - err(1, "name too long"); + errx(1, "name too long"); } else { strlcpy(oname, "ca", sizeof(oname)); } @@ -682,7 +772,7 @@ ca_export(struct ca *ca, char *keyname, char *myname, char *password) de->d_name); snprintf(dst, sizeof(dst), "%s/export/%s", p, de->d_name); - fcopy(src, dst, 644); + fcopy(src, dst, 0644); } closedir(dexp); } @@ -735,13 +825,49 @@ ca_readpass(char *path, size_t *len) return (r); } +/* create index if it doesn't already exist */ +void +ca_create_index(struct ca *ca) +{ + struct stat st; + int fd; + + if (snprintf(ca->index, sizeof(ca->index), "%s/index.txt", + ca->sslpath) < 0) + err(1, "snprintf"); + if (stat(ca->index, &st) != 0) { + if (errno == ENOENT) { + if ((fd = open(ca->index, O_WRONLY | O_CREAT, 0644)) + == -1) + err(1, "could not create file %s", ca->index); + close(fd); + } else + err(1, "could not access %s", ca->index); + } + + if (snprintf(ca->serial, sizeof(ca->serial), "%s/serial.txt", + ca->sslpath) < 0) + err(1, "snprintf"); + if (stat(ca->serial, &st) != 0) { + if (errno == ENOENT) { + if ((fd = open(ca->serial, O_WRONLY | O_CREAT, 0644)) + == -1) + err(1, "could not create file %s", ca->serial); + /* serial file must be created with a number */ + if (write(fd, "01\n", 3) != 3) + err(1, "write %s", ca->serial); + close(fd); + } else + err(1, "could not access %s", ca->serial); + } +} + int ca_revoke(struct ca *ca, char *keyname) { struct stat st; char cmd[PATH_MAX * 2]; char path[PATH_MAX]; - int fd; char *pass; size_t len; @@ -757,56 +883,95 @@ ca_revoke(struct ca *ca, char *keyname) snprintf(path, sizeof(path), "%s/ikeca.passwd", ca->sslpath); pass = ca_readpass(path, &len); if (pass == NULL) - err(1, "could not open passphrase file"); + errx(1, "could not open passphrase file"); - /* create index if it doesn't already exist */ - snprintf(path, sizeof(path), "%s/index.txt", ca->sslpath); - if (stat(path, &st) != 0) { - if (errno == ENOENT) { - if ((fd = open(path, O_WRONLY | O_CREAT, 0644)) == -1) - err(1, "could not create file %s", path); - close(fd); - } else - err(1, "could not access %s", path); - } + ca_create_index(ca); + + ca_setenv("$ENV::CADB", ca->index); + ca_setenv("$ENV::CASERIAL", ca->serial); + ca_setcnf(ca, "ca-revoke"); if (keyname) { - snprintf(cmd, sizeof(cmd), "env CADB='%s/index.txt' " - " %s ca %s-config %s -keyfile %s/private/ca.key" + snprintf(cmd, sizeof(cmd), + "%s ca %s-config %s -keyfile %s/private/ca.key" " -key %s" " -cert %s/ca.crt" - " -md sha1" " -revoke %s/%s.crt", - ca->sslpath, PATH_OPENSSL, ca->batch, ca->sslcnf, + PATH_OPENSSL, ca->batch, ca->sslcnf, ca->sslpath, pass, ca->sslpath, ca->sslpath, keyname); system(cmd); } - snprintf(cmd, sizeof(cmd), "env CADB='%s/index.txt' " - " %s ca %s-config %s -keyfile %s/private/ca.key" + snprintf(cmd, sizeof(cmd), + "%s ca %s-config %s -keyfile %s/private/ca.key" " -key %s" " -gencrl" " -cert %s/ca.crt" - " -md sha1" " -crldays 365" " -out %s/ca.crl", - ca->sslpath, PATH_OPENSSL, ca->batch, ca->sslcnf, ca->sslpath, + PATH_OPENSSL, ca->batch, ca->sslcnf, ca->sslpath, pass, ca->sslpath, ca->sslpath); system(cmd); - bzero(pass, len); + explicit_bzero(pass, len); free(pass); return (0); } +void +ca_clrenv(void) +{ + int i; + for (i = 0; ca_env[i][0] != NULL; i++) + ca_env[i][1] = NULL; +} + +void +ca_setenv(const char *key, const char *value) +{ + int i; + + for (i = 0; ca_env[i][0] != NULL; i++) { + if (strcmp(ca_env[i][0], key) == 0) { + if (ca_env[i][1] != NULL) + errx(1, "env %s already set: %s", key, value); + ca_env[i][1] = value; + return; + } + } + errx(1, "env %s invalid", key); +} + +void +ca_setcnf(struct ca *ca, const char *keyname) +{ + struct stat st; + const char *extcnf, *sslcnf; + + if (stat(IKECA_CNF, &st) == 0) { + extcnf = IKECA_CNF; + sslcnf = IKECA_CNF; + } else { + extcnf = X509_CNF; + sslcnf = SSL_CNF; + } + + snprintf(ca->extcnf, sizeof(ca->extcnf), "%s/%s-ext.cnf", + ca->sslpath, keyname); + snprintf(ca->sslcnf, sizeof(ca->sslcnf), "%s/%s-ssl.cnf", + ca->sslpath, keyname); + + fcopy_env(extcnf, ca->extcnf, 0400); + fcopy_env(sslcnf, ca->sslcnf, 0400); +} + struct ca * ca_setup(char *caname, int create, int quiet, char *pass) { struct stat st; struct ca *ca; char path[PATH_MAX]; - u_int32_t rnd[256]; if (stat(PATH_OPENSSL, &st) == -1) err(1, "openssl binary not available"); @@ -822,14 +987,6 @@ ca_setup(char *caname, int create, int quiet, char *pass) if (quiet) strlcpy(ca->batch, "-batch ", sizeof(ca->batch)); - if (stat(IKECA_CNF, &st) == 0) { - strlcpy(ca->extcnf, IKECA_CNF, sizeof(ca->extcnf)); - strlcpy(ca->sslcnf, IKECA_CNF, sizeof(ca->sslcnf)); - } else { - strlcpy(ca->extcnf, X509_CNF, sizeof(ca->extcnf)); - strlcpy(ca->sslcnf, SSL_CNF, sizeof(ca->sslcnf)); - } - if (create == 0 && stat(ca->sslpath, &st) == -1) { free(ca->caname); free(ca); @@ -846,8 +1003,5 @@ ca_setup(char *caname, int create, int quiet, char *pass) if (create && stat(ca->passfile, &st) == -1 && errno == ENOENT) ca_newpass(ca->passfile, pass); - arc4random_buf(rnd, sizeof(rnd)); - RAND_seed(rnd, sizeof(rnd)); - return (ca); } diff --git a/ikectl/ikeca.cnf b/ikectl/ikeca.cnf index d71662c..e884090 100644 --- a/ikectl/ikeca.cnf +++ b/ikectl/ikeca.cnf @@ -1,6 +1,4 @@ -# $OpenBSD: ikeca.cnf,v 1.5 2012/10/25 12:35:55 reyk Exp $ - -RANDFILE = /dev/arandom +# $OpenBSD: ikeca.cnf,v 1.8 2015/11/02 12:21:27 jsg Exp $ CERT_C = DE CERT_ST = Lower Saxony @@ -17,11 +15,13 @@ EXTCERTUSAGE = serverAuth,clientAuth CERTIP = 0.0.0.0 CERTFQDN = nohost.nodomain CADB = index.txt +CASERIAL = serial.txt NSCERTTYPE = server,client [ req ] -default_bits = 2048 -default_keyfile = privkey.pem +#default_bits = 2048 +#default_md = sha256 +#default_keyfile = privkey.pem distinguished_name = req_distinguished_name #attributes = req_attributes @@ -88,8 +88,21 @@ extendedKeyUsage=$ENV::EXTCERTUSAGE [ca] default_ca = CA_default +[CA_sign_policy] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + [CA_default] database = $ENV::CADB -default_md = sha1 +serial = $ENV::CASERIAL +default_md = sha256 +default_days = 365 default_crl_days = 365 - +unique_subject = yes +email_in_dn = yes +policy = CA_sign_policy diff --git a/ikectl/ikectl.8 b/ikectl/ikectl.8 index 19caee9..40d30ac 100644 --- a/ikectl/ikectl.8 +++ b/ikectl/ikectl.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ikectl.8,v 1.20 2013/01/08 10:38:19 reyk Exp $ +.\" $OpenBSD: ikectl.8,v 1.25 2015/11/02 10:27:44 jsg Exp $ .\" .\" Copyright (c) 2007-2013 Reyk Floeter .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: January 8 2013 $ +.Dd $Mdocdate: November 2 2015 $ .Dt IKECTL 8 .Os .Sh NAME @@ -149,7 +149,7 @@ as the currently active CA or into the specified .It Xo .Cm ca Ar name Cm certificate Ar host .Cm create -.Op Ic server \*(Ba client +.Op Ic server | client | ocsp .Xc Create a private key and certificate for .Ar host @@ -163,6 +163,9 @@ this can be restricted using the optional or .Ic client argument. +If the +.Ic ocsp +argument is specified the extended key usage will be set for OCSP signing. .It Xo .Cm ca Ar name Cm certificate Ar host .Cm delete @@ -250,21 +253,21 @@ from the named .El .Sh FILES .Bl -tag -width "/var/run/iked.sockXX" -compact -.It /etc/iked/ +.It Pa /etc/iked/ Active configuration. -.It /etc/ssl/ +.It Pa /etc/ssl/ Directory to store the CA files. -.It /usr/share/iked +.It Pa /usr/share/iked/ If this optional directory exists, .Nm will include the contents with the .Cm ca export commands. -.It /var/run/iked.sock -default +.It Pa /var/run/iked.sock +Default .Ux Ns -domain socket used for communication with -.Xr iked 8 +.Xr iked 8 . .El .Sh EXAMPLES First create a new certificate authority: @@ -346,9 +349,9 @@ program first appeared in The .Nm program was written by -.An Reyk Floeter Aq reyk@openbsd.org +.An Reyk Floeter Aq Mt reyk@openbsd.org and -.An Jonathan Gray Aq jsg@openbsd.org . +.An Jonathan Gray Aq Mt jsg@openbsd.org . .Sh CAVEATS For ease of use, the .Ic ca diff --git a/ikectl/ikectl.c b/ikectl/ikectl.c index 5c591fc..749c924 100644 --- a/ikectl/ikectl.c +++ b/ikectl/ikectl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ikectl.c,v 1.16 2013/01/08 10:38:19 reyk Exp $ */ +/* $OpenBSD: ikectl.c,v 1.23 2015/12/05 13:11:18 claudio Exp $ */ /* * Copyright (c) 2007-2013 Reyk Floeter @@ -19,12 +19,11 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include #include #include -#include "openbsd-compat/sys-queue.h" + #include -#include "openbsd-compat/sys-tree.h" + #include #include @@ -45,7 +44,7 @@ struct imsgname { void (*func)(struct imsg *); }; -struct imsgname *monitor_lookup(u_int8_t); +struct imsgname *monitor_lookup(uint8_t); void monitor_id(struct imsg *); int monitor(struct imsg *); @@ -114,6 +113,7 @@ ca_opt(struct parse_result *res) case CA_CERT_CREATE: case CA_SERVER: case CA_CLIENT: + case CA_OCSP: ca_certificate(ca, res->host, res->htype, res->action); break; case CA_CERT_DELETE: @@ -194,6 +194,7 @@ main(int argc, char *argv[]) case CA_CERT_CREATE: case CA_CLIENT: case CA_SERVER: + case CA_OCSP: case CA_CERT_DELETE: case CA_CERT_INSTALL: case CA_CERT_EXPORT: @@ -204,6 +205,9 @@ main(int argc, char *argv[]) case CA_KEY_DELETE: case CA_KEY_INSTALL: case CA_KEY_IMPORT: + if (pledge("stdio proc exec rpath wpath cpath fattr tty", NULL) + == -1) + err(1, "pledge"); ca_opt(res); break; case NONE: @@ -234,6 +238,9 @@ main(int argc, char *argv[]) err(1, "connect: %s", sock); } + if (pledge("stdio", NULL) == -1) + err(1, "pledge"); + if (res->ibuf != NULL) ibuf = res->ibuf; else @@ -313,11 +320,11 @@ main(int argc, char *argv[]) } while (ibuf->w.queued) - if (msgbuf_write(&ibuf->w) < 0) + if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) err(1, "write error"); while (!done) { - if ((n = imsg_read(ibuf)) == -1) + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) errx(1, "imsg_read error"); if (n == 0) errx(1, "pipe closed"); @@ -344,7 +351,7 @@ main(int argc, char *argv[]) } struct imsgname * -monitor_lookup(u_int8_t type) +monitor_lookup(uint8_t type) { int i; @@ -366,7 +373,7 @@ monitor(struct imsg *imsg) imn = monitor_lookup(imsg->hdr.type); printf("%s: imsg type %u len %u peerid %u pid %d\n", imn->name, imsg->hdr.type, imsg->hdr.len, imsg->hdr.peerid, imsg->hdr.pid); - printf("\ttimestamp: %u, %s", now, ctime(&now)); + printf("\ttimestamp: %lld, %s", (long long)now, ctime(&now)); if (imn->type == -1) done = 1; if (imn->func != NULL) diff --git a/ikectl/parser.c b/ikectl/parser.c index c4a7cb4..13685a8 100644 --- a/ikectl/parser.c +++ b/ikectl/parser.c @@ -1,4 +1,4 @@ -/* $OpenBSD: parser.c,v 1.12 2013/01/08 10:38:19 reyk Exp $ */ +/* $OpenBSD: parser.c,v 1.16 2016/06/14 13:45:40 reyk Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter @@ -18,11 +18,10 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include #include #include -#include "openbsd-compat/sys-queue.h" -#include "openbsd-compat/sys-tree.h" + + #include #include @@ -68,7 +67,6 @@ static const struct token t_ca_ex_pass[]; static const struct token t_ca_modifiers[]; static const struct token t_ca_cert[]; static const struct token t_ca_cert_extusage[]; -static const struct token t_ca_cert_ex_peer[]; static const struct token t_ca_cert_modifiers[]; static const struct token t_ca_key[]; static const struct token t_ca_key_modifiers[]; @@ -182,6 +180,7 @@ static const struct token t_ca_cert_extusage[] = { { NOTOKEN, "", NONE, NULL}, { KEYWORD, "server", CA_SERVER, NULL }, { KEYWORD, "client", CA_CLIENT, NULL }, + { KEYWORD, "ocsp", CA_OCSP, NULL }, { ENDTOKEN, "", NONE, NULL }, }; @@ -284,7 +283,7 @@ parse_addr(const char *word) const struct token * match_token(char *word, const struct token table[]) { - u_int i, match = 0; + unsigned int i, match = 0; const struct token *t = NULL; for (i = 0; table[i].type != ENDTOKEN; i++) { diff --git a/ikectl/parser.h b/ikectl/parser.h index 4281400..f87ee36 100644 --- a/ikectl/parser.h +++ b/ikectl/parser.h @@ -1,4 +1,4 @@ -/* $OpenBSD: parser.h,v 1.12 2013/01/08 10:38:19 reyk Exp $ */ +/* $OpenBSD: parser.h,v 1.14 2015/11/02 10:27:44 jsg Exp $ */ /* * Copyright (c) 2007-2013 Reyk Floeter @@ -16,8 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef _IKECTL_PARSER_H -#define _IKECTL_PARSER_H +#ifndef IKECTL_PARSER_H +#define IKECTL_PARSER_H enum actions { NONE, @@ -44,6 +44,7 @@ enum actions { CA_CERT_CREATE, CA_SERVER, CA_CLIENT, + CA_OCSP, CA_CERT_DELETE, CA_CERT_INSTALL, CA_CERT_EXPORT, @@ -88,4 +89,4 @@ int ca_key_delete(struct ca *, char *); int ca_key_install(struct ca *, char *, char *); int ca_key_import(struct ca *, char *, char *); -#endif /* _IKECTL_PARSER_H */ +#endif /* IKECTL_PARSER_H */ diff --git a/iked/.cvsignore b/iked/.cvsignore deleted file mode 100644 index d822aec..0000000 --- a/iked/.cvsignore +++ /dev/null @@ -1,7 +0,0 @@ -.cvsignore -.deps -Makefile -Makefile.am -Makefile.in -iked.conf -parse.c diff --git a/iked/CVS/Entries b/iked/CVS/Entries deleted file mode 100644 index ac67746..0000000 --- a/iked/CVS/Entries +++ /dev/null @@ -1,31 +0,0 @@ -/Makefile/1.8/Result of merge+Fri Jan 11 08:22:53 2013// -/chap_ms.c/1.6/Result of merge// -/crypto.c/1.9/Result of merge// -/iked.8/1.13/Result of merge// -/iked.h/1.56/Result of merge// -/log.c/1.3/Result of merge// -/policy.c/1.23/Result of merge+Fri Jan 11 08:23:08 2013// -/timer.c/1.10/Result of merge// -/util.c/1.18/Result of merge// -/ca.c/1.22/Result of merge+Tue Apr 2 09:13:18 2013// -/chap_ms.h/1.3/Mon Jan 14 14:11:49 2013// -/config.c/1.20/Result of merge+Tue Apr 2 09:13:19 2013// -/control.c/1.12/Result of merge// -/dh.c/1.10/Mon Jan 14 14:11:49 2013// -/dh.h/1.5/Mon Jan 14 14:11:49 2013// -/eap.c/1.9/Result of merge+Tue Apr 2 09:13:20 2013// -/eap.h/1.3/Mon Jan 14 14:11:49 2013// -/genmap.sh/1.6/Mon Jan 14 14:11:49 2013// -/iked.c/1.17/Result of merge+Tue Apr 2 09:13:21 2013// -/iked.conf.5/1.23/Tue Apr 2 09:13:22 2013// -/ikev1.c/1.13/Result of merge+Tue Apr 2 09:13:23 2013// -/ikev2.c/1.82/Result of merge+Tue Apr 2 09:13:24 2013// -/ikev2.h/1.12/Tue Apr 2 09:13:26 2013// -/ikev2_msg.c/1.25/Result of merge+Tue Apr 2 09:13:26 2013// -/ikev2_pld.c/1.31/Result of merge+Tue Apr 2 09:13:27 2013// -/imsg_util.c/1.7/Result of merge+Tue Apr 2 09:13:28 2013// -/parse.y/1.30/Result of merge// -/pfkey.c/1.23/Result of merge// -/proc.c/1.9/Result of merge+Tue Apr 2 09:13:31 2013// -/types.h/1.17/Mon Jan 14 14:11:49 2013// -D diff --git a/iked/CVS/Repository b/iked/CVS/Repository deleted file mode 100644 index 6a68f45..0000000 --- a/iked/CVS/Repository +++ /dev/null @@ -1 +0,0 @@ -src/sbin/iked diff --git a/iked/CVS/Root b/iked/CVS/Root deleted file mode 100644 index 13631a1..0000000 --- a/iked/CVS/Root +++ /dev/null @@ -1 +0,0 @@ -anoncvs@anoncvs.openbsd.org:/cvs diff --git a/iked/Makefile.am b/iked/Makefile.am index a1dc4a7..006e2ad 100644 --- a/iked/Makefile.am +++ b/iked/Makefile.am @@ -3,20 +3,23 @@ sbin_PROGRAMS= iked iked_MAPS= eap_map.c ikev2_map.c iked_MAPS_IN= eap.h ikev2.h iked_SOURCES= ca.c chap_ms.c config.c control.c crypto.c dh.c \ - eap.c iked.c ikev1.c ikev2.c ikev2_msg.c ikev2_pld.c \ - log.c policy.c proc.c timer.c util.c imsg_util.c \ + eap.c iked.c ikev2.c ikev2_msg.c ikev2_pld.c \ + log.c ocsp.c policy.c proc.c \ + timer.c util.c imsg_util.c \ parse.y $(iked_MAPS) -iked_CFLAGS= +iked_CPPFLAGS= -include $(top_srcdir)/openbsd-compat/includes.h libpfkey_a_SOURCES= pfkey.c -libpfkey_a_CPPFLAGS= @PFKEY_CPPFLAGS@ +libpfkey_a_CPPFLAGS= @PFKEY_CPPFLAGS@ $(iked_CPPFLAGS) -noinst_LIBRARIES= libpfkey.a +libcurve25519_a_SOURCES=smult_curve25519_ref.c +libcurve25519_a_CPPFLAGS= + +noinst_LIBRARIES= libpfkey.a libcurve25519.a LIBCOMPAT= $(top_builddir)/openbsd-compat/libopenbsd-compat.a -LDADD= $(LIBCOMPAT) libpfkey.a -CPPFLAGS= -include $(top_srcdir)/openbsd-compat/includes.h \ - -I$(top_srcdir)/openbsd-compat \ +LDADD= $(LIBCOMPAT) libpfkey.a libcurve25519.a +CPPFLAGS= -I$(top_srcdir)/openbsd-compat \ -I$(srcdir) @CPPFLAGS@ @DEFS@ MANPAGES= iked.8.out iked.conf.5.out MANPAGES_IN= iked.8 iked.conf.5 diff --git a/iked/ca.c b/iked/ca.c index 5f55b1f..3c811df 100644 --- a/iked/ca.c +++ b/iked/ca.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ca.c,v 1.22 2013/03/21 04:30:14 deraadt Exp $ */ +/* $OpenBSD: ca.c,v 1.40 2015/12/07 12:46:37 reyk Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter @@ -16,8 +16,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include "openbsd-compat/sys-queue.h" + #include #include #include @@ -27,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -43,19 +41,22 @@ #include #include #include +#include #include "iked.h" #include "ikev2.h" -void ca_reset(struct privsep *, void *); +void ca_run(struct privsep *, struct privsep_proc *, void *); +void ca_reset(struct privsep *, struct privsep_proc *, 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_subjectpubkey(X509_STORE *, uint8_t *, size_t); X509 *ca_by_issuer(X509_STORE *, X509_NAME *, struct iked_static_id *); -int ca_subjectpubkey_digest(X509 *, u_int8_t *, u_int *); +int ca_subjectpubkey_digest(X509 *, uint8_t *, unsigned int *); +int ca_x509_subject_cmp(X509 *, struct iked_static_id *); int ca_validate_pubkey(struct iked *, struct iked_static_id *, void *, size_t); int ca_validate_cert(struct iked *, struct iked_static_id *, @@ -64,14 +65,13 @@ struct ibuf * ca_x509_serialize(X509 *); int ca_x509_subjectaltname_cmp(X509 *, struct iked_static_id *); int ca_x509_subjectaltname(X509 *cert, struct iked_id *); -int ca_key_serialize(EVP_PKEY *, struct iked_id *); +int ca_privkey_serialize(EVP_PKEY *, struct iked_id *); +int ca_pubkey_serialize(EVP_PKEY *, struct iked_id *); int ca_dispatch_parent(int, struct privsep_proc *, struct imsg *); -int ca_dispatch_ikev1(int, struct privsep_proc *, struct imsg *); int ca_dispatch_ikev2(int, struct privsep_proc *, struct imsg *); static struct privsep_proc procs[] = { { "parent", PROC_PARENT, ca_dispatch_parent }, - { "ikev1", PROC_IKEV1, ca_dispatch_ikev1 }, { "ikev2", PROC_IKEV2, ca_dispatch_ikev2 } }; @@ -83,6 +83,7 @@ struct ca_store { X509_LOOKUP *ca_certlookup; struct iked_id ca_privkey; + struct iked_id ca_pubkey; }; pid_t @@ -98,6 +99,7 @@ caproc(struct privsep *ps, struct privsep_proc *p) if ((store = calloc(1, sizeof(*store))) == NULL) fatal("ca: failed to allocate cert store"); + /* Read private key */ if ((fp = fopen(IKED_PRIVKEY, "r")) == NULL) fatal("ca: failed to open private key"); @@ -105,14 +107,33 @@ caproc(struct privsep *ps, struct privsep_proc *p) fatalx("ca: failed to read private key"); fclose(fp); - if (ca_key_serialize(key, &store->ca_privkey) != 0) + if (ca_privkey_serialize(key, &store->ca_privkey) != 0) fatalx("ca: failed to serialize private key"); + if (ca_pubkey_serialize(key, &store->ca_pubkey) != 0) + fatalx("ca: failed to serialize public key"); + + EVP_PKEY_free(key); return (proc_run(ps, p, procs, nitems(procs), ca_reset, store)); } void -ca_reset(struct privsep *ps, void *arg) +ca_run(struct privsep *ps, struct privsep_proc *p, void *arg) +{ + /* + * pledge in the ca process: + * stdio - for malloc and basic I/O including events. + * rpath - for certificate files. + * recvfd - for ocsp sockets. + */ + if (pledge("stdio rpath recvfd", NULL) == -1) + fatal("pledge"); + + ca_reset(ps, p, arg); +} + +void +ca_reset(struct privsep *ps, struct privsep_proc *p, void *arg) { struct iked *env = ps->ps_env; struct ca_store *store = arg; @@ -144,8 +165,8 @@ int ca_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) { struct iked *env = p->p_env; - struct ca_store *store = env->sc_priv; - u_int mode; + struct ca_store *store = env->sc_priv; + unsigned int mode; switch (imsg->hdr.type) { case IMSG_CTL_RESET: @@ -153,9 +174,15 @@ ca_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) memcpy(&mode, imsg->data, sizeof(mode)); if (mode == RESET_ALL || mode == RESET_CA) { log_debug("%s: config reload", __func__); - ca_reset(&env->sc_ps, store); + ca_reset(&env->sc_ps, p, store); } break; + case IMSG_OCSP_FD: + ocsp_receive_fd(env, imsg); + break; + case IMSG_OCSP_URL: + config_getocsp(env, imsg); + break; default: return (-1); } @@ -163,12 +190,6 @@ ca_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) return (0); } -int -ca_dispatch_ikev1(int fd, struct privsep_proc *p, struct imsg *imsg) -{ - return (-1); -} - int ca_dispatch_ikev2(int fd, struct privsep_proc *p, struct imsg *imsg) { @@ -193,7 +214,7 @@ ca_dispatch_ikev2(int fd, struct privsep_proc *p, struct imsg *imsg) 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 privsep_procid procid) + uint8_t type, uint8_t *data, size_t len, enum privsep_procid procid) { struct iovec iov[4]; int iovcnt = 0; @@ -228,14 +249,14 @@ ca_setcert(struct iked *env, struct iked_sahdr *sh, struct iked_id *id, iov[iovcnt].iov_len = len; iovcnt++; - if (proc_composev_imsg(env, procid, IMSG_CERT, -1, iov, iovcnt) == -1) + if (proc_composev(&env->sc_ps, procid, IMSG_CERT, iov, iovcnt) == -1) return (-1); return (0); } int -ca_setreq(struct iked *env, struct iked_sahdr *sh, - struct iked_static_id *localid, u_int8_t type, u_int8_t *data, +ca_setreq(struct iked *env, struct iked_sa *sa, + struct iked_static_id *localid, uint8_t type, uint8_t *data, size_t len, enum privsep_procid procid) { struct iovec iov[4]; @@ -259,8 +280,8 @@ ca_setreq(struct iked *env, struct iked_sahdr *sh, iov[iovcnt].iov_len = sizeof(idb); iovcnt++; - iov[iovcnt].iov_base = sh; - iov[iovcnt].iov_len = sizeof(*sh); + iov[iovcnt].iov_base = &sa->sa_hdr; + iov[iovcnt].iov_len = sizeof(sa->sa_hdr); iovcnt++; iov[iovcnt].iov_base = &type; iov[iovcnt].iov_len = sizeof(type); @@ -269,10 +290,11 @@ ca_setreq(struct iked *env, struct iked_sahdr *sh, iov[iovcnt].iov_len = len; iovcnt++; - if (proc_composev_imsg(env, procid, - IMSG_CERTREQ, -1, iov, iovcnt) == -1) + if (proc_composev(&env->sc_ps, procid, IMSG_CERTREQ, iov, iovcnt) == -1) goto done; + sa_stateflags(sa, IKED_REQ_CERTREQ); + ret = 0; done: ibuf_release(id.id_buf); @@ -286,7 +308,13 @@ ca_setauth(struct iked *env, struct iked_sa *sa, struct iovec iov[3]; int iovcnt = 3; struct iked_policy *policy = sa->sa_policy; - u_int8_t type = policy->pol_auth.auth_method; + uint8_t type = policy->pol_auth.auth_method; + + /* switch encoding to IKEV2_AUTH_SIG if SHA2 is supported */ + if (sa->sa_sigsha2 && type == IKEV2_AUTH_RSA_SIG) { + log_debug("%s: switching from RSA_SIG to SIG", __func__); + type = IKEV2_AUTH_SIG; + } if (type == IKEV2_AUTH_SHARED_KEY_MIC) { sa->sa_stateflags |= IKED_REQ_AUTH; @@ -303,10 +331,10 @@ ca_setauth(struct iked *env, struct iked_sa *sa, 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)); + log_debug("%s: auth length %zu", __func__, ibuf_size(authmsg)); } - if (proc_composev_imsg(env, id, IMSG_AUTH, -1, iov, iovcnt) == -1) + if (proc_composev(&env->sc_ps, id, IMSG_AUTH, iov, iovcnt) == -1) return (-1); return (0); } @@ -315,15 +343,15 @@ int ca_getcert(struct iked *env, struct imsg *imsg) { struct iked_sahdr sh; - u_int8_t type; - u_int8_t *ptr; + uint8_t type; + uint8_t *ptr; size_t len; struct iked_static_id id; - u_int i; + unsigned int i; struct iovec iov[2]; int iovcnt = 2, cmd, ret = 0; - ptr = (u_int8_t *)imsg->data; + ptr = (uint8_t *)imsg->data; len = IMSG_DATA_SIZE(imsg); i = sizeof(id) + sizeof(sh) + sizeof(type); if (len <= i) @@ -333,7 +361,7 @@ ca_getcert(struct iked *env, struct imsg *imsg) 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)); + memcpy(&type, ptr + sizeof(id) + sizeof(sh), sizeof(uint8_t)); ptr += i; len -= i; @@ -341,6 +369,11 @@ ca_getcert(struct iked *env, struct imsg *imsg) switch (type) { case IKEV2_CERT_X509_CERT: ret = ca_validate_cert(env, &id, ptr, len); + if (ret == 0 && env->sc_ocsp_url) { + ret = ocsp_validate_cert(env, &id, ptr, len, sh, type); + if (ret == 0) + return (0); + } break; case IKEV2_CERT_RSA_KEY: ret = ca_validate_pubkey(env, &id, ptr, len); @@ -361,7 +394,7 @@ ca_getcert(struct iked *env, struct imsg *imsg) iov[1].iov_base = &type; iov[1].iov_len = sizeof(type); - if (proc_composev_imsg(env, PROC_IKEV2, cmd, -1, iov, iovcnt) == -1) + if (proc_composev(&env->sc_ps, PROC_IKEV2, cmd, iov, iovcnt) == -1) return (-1); return (0); } @@ -369,19 +402,19 @@ ca_getcert(struct iked *env, struct imsg *imsg) int ca_getreq(struct iked *env, struct imsg *imsg) { - struct ca_store *store = env->sc_priv; + struct ca_store *store = env->sc_priv; struct iked_sahdr sh; - u_int8_t type; - u_int8_t *ptr; + uint8_t type; + uint8_t *ptr; size_t len; - u_int i, n; + unsigned int i, n; X509 *ca = NULL, *cert = NULL; struct ibuf *buf; struct iked_static_id id; - ptr = (u_int8_t *)imsg->data; + ptr = (uint8_t *)imsg->data; len = IMSG_DATA_SIZE(imsg); - i = sizeof(id) + sizeof(u_int8_t) + sizeof(sh); + i = sizeof(id) + sizeof(uint8_t) + sizeof(sh); if (len < i || ((len - i) % SHA_DIGEST_LENGTH) != 0) return (-1); @@ -389,41 +422,51 @@ ca_getreq(struct iked *env, struct imsg *imsg) 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); - - 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; - } + memcpy(&type, ptr + sizeof(id) + sizeof(sh), sizeof(uint8_t)); - log_debug("%s: found CA %s", __func__, ca->name); + switch (type) { + case IKEV2_CERT_RSA_KEY: + if (store->ca_pubkey.id_type != type || + (buf = store->ca_pubkey.id_buf) == NULL) + return (-1); - if ((cert = ca_by_issuer(store->ca_certs, - X509_get_subject_name(ca), &id)) != NULL) { - /* XXX should we re-validate our own cert here? */ - break; - } + log_debug("%s: using local public key of type %s", __func__, + print_map(type, ikev2_cert_map)); + break; + case IKEV2_CERT_X509_CERT: + for (n = 1; i < len; n++, i += SHA_DIGEST_LENGTH) { + if ((ca = ca_by_subjectpubkey(store->ca_cas, ptr + i, + SHA_DIGEST_LENGTH)) == NULL) + continue; - 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 CA %s", __func__, ca->name); - log_debug("%s: found local certificate %s", __func__, cert->name); + if ((cert = ca_by_issuer(store->ca_certs, + X509_get_subject_name(ca), &id)) != NULL) { + /* XXX + * should we re-validate our own cert here? + */ + break; + } + } + 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) + if ((buf = ca_x509_serialize(cert)) == NULL) + return (-1); + break; + default: + log_warnx("%s: unknown cert type requested", __func__); return (-1); + } - type = IKEV2_CERT_X509_CERT; ca_setcert(env, &sh, NULL, type, ibuf_data(buf), ibuf_size(buf), PROC_IKEV2); @@ -433,26 +476,26 @@ ca_getreq(struct iked *env, struct imsg *imsg) int ca_getauth(struct iked *env, struct imsg *imsg) { - struct ca_store *store = env->sc_priv; + struct ca_store *store = env->sc_priv; struct iked_sahdr sh; - u_int8_t method; - u_int8_t *ptr; + uint8_t method; + uint8_t *ptr; size_t len; - u_int i; + unsigned 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; + ptr = (uint8_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)); + memcpy(&method, ptr + sizeof(sh), sizeof(uint8_t)); if (method == IKEV2_AUTH_SHARED_KEY_MIC) return (-1); @@ -492,17 +535,17 @@ ca_getauth(struct iked *env, struct imsg *imsg) int ca_reload(struct iked *env) { - struct ca_store *store = env->sc_priv; - DIR *dir; - struct dirent *entry; + struct ca_store *store = env->sc_priv; + uint8_t md[EVP_MAX_MD_SIZE]; char file[PATH_MAX]; + struct iovec iov[2]; + struct dirent *entry; 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; + DIR *dir; + int i, iovcnt = 0; + unsigned int len; /* * Load CAs @@ -523,7 +566,7 @@ ca_reload(struct iked *env) X509_FILETYPE_PEM)) { log_warn("%s: failed to load ca file %s", __func__, entry->d_name); - ca_sslerror(); + ca_sslerror(__func__); continue; } log_debug("%s: loaded ca file %s", __func__, entry->d_name); @@ -549,7 +592,7 @@ ca_reload(struct iked *env) X509_FILETYPE_PEM)) { log_warn("%s: failed to load crl file %s", __func__, entry->d_name); - ca_sslerror(); + ca_sslerror(__func__); continue; } @@ -588,15 +631,17 @@ ca_reload(struct iked *env) env->sc_certreqtype = IKEV2_CERT_X509_CERT; iov[0].iov_base = &env->sc_certreqtype; iov[0].iov_len = sizeof(env->sc_certreqtype); + iovcnt++; iov[1].iov_base = ibuf_data(env->sc_certreq); iov[1].iov_len = ibuf_length(env->sc_certreq); + iovcnt++; - log_debug("%s: loaded %d ca certificate%s", __func__, + log_debug("%s: loaded %zu ca certificate%s", __func__, ibuf_length(env->sc_certreq) / SHA_DIGEST_LENGTH, ibuf_length(env->sc_certreq) == SHA_DIGEST_LENGTH ? "" : "s"); - (void)proc_composev_imsg(env, PROC_IKEV2, IMSG_CERTREQ, -1, + (void)proc_composev(&env->sc_ps, PROC_IKEV2, IMSG_CERTREQ, iov, iovcnt); } @@ -619,7 +664,7 @@ ca_reload(struct iked *env) X509_FILETYPE_PEM)) { log_warn("%s: failed to load cert file %s", __func__, entry->d_name); - ca_sslerror(); + ca_sslerror(__func__); continue; } log_debug("%s: loaded cert file %s", __func__, entry->d_name); @@ -637,18 +682,30 @@ ca_reload(struct iked *env) (void)ca_validate_cert(env, NULL, x509, 0); } + if (!env->sc_certreqtype) + env->sc_certreqtype = store->ca_pubkey.id_type; + + log_debug("%s: local cert type %s", __func__, + print_map(env->sc_certreqtype, ikev2_cert_map)); + + iov[0].iov_base = &env->sc_certreqtype; + iov[0].iov_len = sizeof(env->sc_certreqtype); + if (iovcnt == 0) + iovcnt++; + (void)proc_composev(&env->sc_ps, PROC_IKEV2, IMSG_CERTREQ, iov, iovcnt); + return (0); } X509 * -ca_by_subjectpubkey(X509_STORE *ctx, u_int8_t *sig, size_t siglen) +ca_by_subjectpubkey(X509_STORE *ctx, uint8_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]; + unsigned int len; + uint8_t md[EVP_MAX_MD_SIZE]; h = ctx->objs; @@ -690,9 +747,16 @@ ca_by_issuer(X509_STORE *ctx, X509_NAME *subject, struct iked_static_id *id) if ((issuer = X509_get_issuer_name(cert)) == NULL) continue; else if (X509_NAME_cmp(subject, issuer) == 0) { - if (ca_x509_subjectaltname_cmp(cert, id) != 0) - continue; - return (cert); + switch (id->id_type) { + case IKEV2_ID_ASN1_DN: + if (ca_x509_subject_cmp(cert, id) == 0) + return (cert); + break; + default: + if (ca_x509_subjectaltname_cmp(cert, id) == 0) + return (cert); + break; + } } } @@ -700,9 +764,9 @@ ca_by_issuer(X509_STORE *ctx, X509_NAME *subject, struct iked_static_id *id) } int -ca_subjectpubkey_digest(X509 *x509, u_int8_t *md, u_int *size) +ca_subjectpubkey_digest(X509 *x509, uint8_t *md, unsigned int *size) { - u_int8_t *buf = NULL; + uint8_t *buf = NULL; int buflen; if (*size < SHA_DIGEST_LENGTH) @@ -731,7 +795,7 @@ ca_x509_serialize(X509 *x509) { long len; struct ibuf *buf; - u_int8_t *d = NULL; + uint8_t *d = NULL; BIO *out; if ((out = BIO_new(BIO_s_mem())) == NULL) @@ -743,16 +807,18 @@ ca_x509_serialize(X509 *x509) len = BIO_get_mem_data(out, &d); buf = ibuf_new(d, len); + BIO_free(out); return (buf); } int -ca_key_serialize(EVP_PKEY *key, struct iked_id *id) +ca_pubkey_serialize(EVP_PKEY *key, struct iked_id *id) { - int len; - u_int8_t *d; - RSA *rsa; + RSA *rsa = NULL; + uint8_t *d; + int len = 0; + int ret = -1; switch (key->type) { case EVP_PKEY_RSA: @@ -761,16 +827,60 @@ ca_key_serialize(EVP_PKEY *key, struct iked_id *id) ibuf_release(id->id_buf); if ((rsa = EVP_PKEY_get1_RSA(key)) == NULL) - return (-1); + goto done; + if ((len = i2d_RSAPublicKey(rsa, NULL)) <= 0) + goto done; + if ((id->id_buf = ibuf_new(NULL, len)) == NULL) + goto done; + + d = ibuf_data(id->id_buf); + if (i2d_RSAPublicKey(rsa, &d) != len) { + ibuf_release(id->id_buf); + goto done; + } + + id->id_type = IKEV2_CERT_RSA_KEY; + break; + default: + log_debug("%s: unsupported key type %d", __func__, key->type); + return (-1); + } + + log_debug("%s: type %s length %d", __func__, + print_map(id->id_type, ikev2_cert_map), len); + + ret = 0; + done: + if (rsa != NULL) + RSA_free(rsa); + return (ret); +} + +int +ca_privkey_serialize(EVP_PKEY *key, struct iked_id *id) +{ + RSA *rsa = NULL; + uint8_t *d; + int len = 0; + int ret = -1; + + switch (key->type) { + case EVP_PKEY_RSA: + id->id_type = 0; + id->id_offset = 0; + ibuf_release(id->id_buf); + + if ((rsa = EVP_PKEY_get1_RSA(key)) == NULL) + goto done; if ((len = i2d_RSAPrivateKey(rsa, NULL)) <= 0) - return (-1); + goto done; if ((id->id_buf = ibuf_new(NULL, len)) == NULL) - return (-1); + goto done; d = ibuf_data(id->id_buf); if (i2d_RSAPrivateKey(rsa, &d) != len) { ibuf_release(id->id_buf); - return (-1); + goto done; } id->id_type = IKEV2_CERT_RSA_KEY; @@ -780,15 +890,22 @@ ca_key_serialize(EVP_PKEY *key, struct iked_id *id) return (-1); } - return (0); + log_debug("%s: type %s length %d", __func__, + print_map(id->id_type, ikev2_cert_map), len); + + ret = 0; + done: + if (rsa != NULL) + RSA_free(rsa); + return (ret); } char * -ca_asn1_name(u_int8_t *asn1, size_t len) +ca_asn1_name(uint8_t *asn1, size_t len) { X509_NAME *name = NULL; char *str = NULL; - const u_int8_t *p; + const uint8_t *p; p = asn1; if ((name = d2i_X509_NAME(NULL, &p, len)) == NULL) @@ -812,17 +929,112 @@ ca_x509_name(void *ptr) return (strdup(buf)); } +/* + * Copy 'src' to 'dst' until 'marker' is found while unescaping '\' + * characters. The return value tells the caller where to continue + * parsing (might be the end of the string) or NULL on error. + */ +static char * +ca_x509_name_unescape(char *src, char *dst, char marker) +{ + while (*src) { + if (*src == marker) { + src++; + break; + } + if (*src == '\\') { + src++; + if (!*src) { + log_warnx("%s: '\\' at end of string", + __func__); + *dst = '\0'; + return (NULL); + } + } + *dst++ = *src++; + } + *dst = '\0'; + return (src); +} +/* + * Parse an X509 subject name where 'subject' is in the format + * /type0=value0/type1=value1/type2=... + * where characters may be escaped by '\'. + * See lib/libssl/src/apps/apps.c:parse_name() + */ +void * +ca_x509_name_parse(char *subject) +{ + char *cp, *value = NULL, *type = NULL; + size_t maxlen; + X509_NAME *name = NULL; + + if (*subject != '/') { + log_warnx("%s: leading '/' missing in '%s'", __func__, subject); + goto err; + } + + /* length of subject is upper bound for unescaped type/value */ + maxlen = strlen(subject) + 1; + + if ((type = calloc(1, maxlen)) == NULL || + (value = calloc(1, maxlen)) == NULL || + (name = X509_NAME_new()) == NULL) + goto err; + + cp = subject + 1; + while (*cp) { + /* unescape type, terminated by '=' */ + cp = ca_x509_name_unescape(cp, type, '='); + if (cp == NULL) { + log_warnx("%s: could not parse type", __func__); + goto err; + } + if (!*cp) { + log_warnx("%s: missing value", __func__); + goto err; + } + /* unescape value, terminated by '/' */ + cp = ca_x509_name_unescape(cp, value, '/'); + if (cp == NULL) { + log_warnx("%s: could not parse value", __func__); + goto err; + } + if (!*type || !*value) { + log_warnx("%s: empty type or value", __func__); + goto err; + } + log_debug("%s: setting '%s' to '%s'", __func__, type, value); + if (!X509_NAME_add_entry_by_txt(name, type, MBSTRING_ASC, + value, -1, -1, 0)) { + log_warnx("%s: setting '%s' to '%s' failed", __func__, + type, value); + ca_sslerror(__func__); + goto err; + } + } + free(type); + free(value); + return (name); + +err: + X509_NAME_free(name); + free(type); + free(value); + return (NULL); +} + int ca_validate_pubkey(struct iked *env, struct iked_static_id *id, void *data, size_t len) { BIO *rawcert = NULL; - RSA *rsa = NULL; + RSA *peerrsa = NULL, *localrsa = NULL; EVP_PKEY *peerkey = NULL, *localkey = NULL; int ret = -1; FILE *fp = NULL; char idstr[IKED_ID_SIZE]; - char file[MAXPATHLEN]; + char file[PATH_MAX]; struct iked_id idp; if (len == 0 && data == NULL) @@ -855,42 +1067,58 @@ ca_validate_pubkey(struct iked *env, struct iked_static_id *id, if ((rawcert = BIO_new_mem_buf(data, len)) == NULL) goto done; - if ((rsa = d2i_RSAPublicKey_bio(rawcert, NULL)) == NULL) + if ((peerrsa = d2i_RSAPublicKey_bio(rawcert, NULL)) == NULL) goto sslerr; if ((peerkey = EVP_PKEY_new()) == NULL) goto sslerr; - if (!EVP_PKEY_set1_RSA(peerkey, rsa)) + if (!EVP_PKEY_set1_RSA(peerkey, peerrsa)) goto sslerr; } lc_string(idstr); if (strlcpy(file, IKED_PUBKEY_DIR, sizeof(file)) >= sizeof(file) || - strlcpy(file, idstr, sizeof(file)) >= sizeof(file)) + strlcat(file, idstr, sizeof(file)) >= sizeof(file)) goto done; - log_debug("%s: looking up %s", __func__, file); - if ((fp = fopen(file, "r")) == NULL) goto done; - localkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL); - fclose(fp); + if (localkey == NULL) { + /* reading PKCS #8 failed, try PEM */ + rewind(fp); + localrsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL); + fclose(fp); + if (localrsa == NULL) + goto sslerr; + if ((localkey = EVP_PKEY_new()) == NULL) + goto sslerr; + if (!EVP_PKEY_set1_RSA(localkey, localrsa)) + goto sslerr; + } else { + fclose(fp); + } if (localkey == NULL) goto sslerr; if (!EVP_PKEY_cmp(peerkey, localkey)) goto done; + log_debug("%s: valid public key in file %s", __func__, file); + ret = 0; sslerr: if (ret != 0) - ca_sslerror(); + ca_sslerror(__func__); done: ibuf_release(idp.id_buf); if (peerkey != NULL) EVP_PKEY_free(peerkey); - if (rsa != NULL) - RSA_free(rsa); + if (localkey != NULL) + EVP_PKEY_free(localkey); + if (peerrsa != NULL) + RSA_free(peerrsa); + if (localrsa != NULL) + RSA_free(localrsa); if (rawcert != NULL) BIO_free(rawcert); @@ -906,9 +1134,7 @@ ca_validate_cert(struct iked *env, struct iked_static_id *id, BIO *rawcert = NULL; X509 *cert = NULL; int ret = -1, result, error; - size_t idlen, idoff; - const u_int8_t *idptr; - X509_NAME *idname = NULL, *subject; + X509_NAME *subject; const char *errstr = "failed"; if (len == 0) { @@ -931,23 +1157,13 @@ ca_validate_cert(struct iked *env, struct iked_static_id *id, if (id != NULL) { if ((ret = ca_validate_pubkey(env, id, X509_get_pubkey(cert), 0)) == 0) { - errstr = "public key found, ok"; + errstr = "in public key file, ok"; goto done; } switch (id->id_type) { case IKEV2_ID_ASN1_DN: - idoff = id->id_offset; - if (id->id_length <= idoff) { - errstr = "invalid ASN1_DN id length"; - goto done; - } - idlen = id->id_length - idoff; - idptr = id->id_data + idoff; - - if ((idname = d2i_X509_NAME(NULL, - &idptr, idlen)) == NULL || - X509_NAME_cmp(subject, idname) != 0) { + if (ca_x509_subject_cmp(cert, id) < 0) { errstr = "ASN1_DN identifier mismatch"; goto done; } @@ -984,14 +1200,12 @@ ca_validate_cert(struct iked *env, struct iked_static_id *id, /* Success */ ret = 0; + errstr = "ok"; done: if (cert != NULL) - log_debug("%s: %s %.100s", __func__, cert->name, - ret == 0 ? "ok" : errstr); + log_debug("%s: %s %.100s", __func__, cert->name, errstr); - if (idname != NULL) - X509_NAME_free(idname); if (rawcert != NULL) { BIO_free(rawcert); if (cert != NULL) @@ -1001,6 +1215,31 @@ ca_validate_cert(struct iked *env, struct iked_static_id *id, return (ret); } +/* check if subject from cert matches the id */ +int +ca_x509_subject_cmp(X509 *cert, struct iked_static_id *id) +{ + X509_NAME *subject, *idname = NULL; + const uint8_t *idptr; + size_t idlen; + int ret = -1; + + if (id->id_type != IKEV2_ID_ASN1_DN) + return (-1); + if ((subject = X509_get_subject_name(cert)) == NULL) + return (-1); + if (id->id_length <= id->id_offset) + return (-1); + idlen = id->id_length - id->id_offset; + idptr = id->id_data + id->id_offset; + if ((idname = d2i_X509_NAME(NULL, &idptr, idlen)) == NULL) + return (-1); + if (X509_NAME_cmp(subject, idname) == 0) + ret = 0; + X509_NAME_free(idname); + return (ret); +} + int ca_x509_subjectaltname_cmp(X509 *cert, struct iked_static_id *id) { @@ -1036,7 +1275,7 @@ int ca_x509_subjectaltname(X509 *cert, struct iked_id *id) { X509_EXTENSION *san; - u_int8_t sanhdr[4], *data; + uint8_t sanhdr[4], *data; int ext, santype, sanlen; char idstr[IKED_ID_SIZE]; @@ -1056,7 +1295,7 @@ ca_x509_subjectaltname(X509 *cert, struct iked_id *id) } /* This is partially based on isakmpd's x509 subjectaltname code */ - data = (u_int8_t *)san->value->data; + data = (uint8_t *)san->value->data; memcpy(&sanhdr, data, sizeof(sanhdr)); santype = sanhdr[2] & 0x3f; sanlen = sanhdr[3]; @@ -1112,16 +1351,14 @@ ca_sslinit(void) /* Init hardware crypto engines. */ ENGINE_load_builtin_engines(); ENGINE_register_all_complete(); - - seed_rng(); } void -ca_sslerror(void) +ca_sslerror(const char *caller) { - u_long error; + unsigned long error; while ((error = ERR_get_error()) != 0) - log_warn("%s: %.100s", __func__, + log_warn("%s: %s: %.100s", __func__, caller, ERR_error_string(error, NULL)); } diff --git a/iked/chap_ms.c b/iked/chap_ms.c index dfaff2f..1e03f1d 100644 --- a/iked/chap_ms.c +++ b/iked/chap_ms.c @@ -1,4 +1,4 @@ -/* $OpenBSD: chap_ms.c,v 1.6 2013/01/08 10:38:19 reyk Exp $ */ +/* $OpenBSD: chap_ms.c,v 1.9 2015/08/21 11:59:27 reyk Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter @@ -56,32 +56,32 @@ * "It is unclear to us why this protocol is so complicated." */ -static u_int8_t sha1_pad1[40] = { +static uint8_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] = { +static uint8_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 *); +uint8_t get7bits(uint8_t *, int); +void mschap_des_addparity(uint8_t *, uint8_t *); +void mschap_des_encrypt(uint8_t *, uint8_t *, uint8_t *); +void mschap_challenge_response(uint8_t *, uint8_t *, uint8_t *); -u_int8_t -get7bits(u_int8_t *in, int start) +uint8_t +get7bits(uint8_t *in, int start) { - u_int word; + unsigned int word; - word = (u_int)in[start / 8] << 8; - word |= (u_int)in[start / 8 + 1]; + word = (unsigned int)in[start / 8] << 8; + word |= (unsigned int)in[start / 8 + 1]; word >>= 15 - (start % 8 + 7); return (word & 0xfe); @@ -90,7 +90,7 @@ get7bits(u_int8_t *in, int start) /* 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) +mschap_des_addparity(uint8_t *key, uint8_t *des_key) { des_key[0] = get7bits(key, 0); des_key[1] = get7bits(key, 7); @@ -105,7 +105,7 @@ mschap_des_addparity(u_int8_t *key, u_int8_t *des_key) } void -mschap_des_encrypt(u_int8_t *clear, u_int8_t *key, u_int8_t *cipher) +mschap_des_encrypt(uint8_t *clear, uint8_t *key, uint8_t *cipher) { DES_cblock des_key; DES_key_schedule key_schedule; @@ -118,10 +118,10 @@ mschap_des_encrypt(u_int8_t *clear, u_int8_t *key, u_int8_t *cipher) } void -mschap_challenge_response(u_int8_t *challenge, u_int8_t *pwhash, - u_int8_t *response) +mschap_challenge_response(uint8_t *challenge, uint8_t *pwhash, + uint8_t *response) { - u_int8_t padpwhash[21 + 1]; + uint8_t padpwhash[21 + 1]; bzero(&padpwhash, sizeof(padpwhash)); memcpy(padpwhash, pwhash, MSCHAP_HASH_SZ); @@ -132,10 +132,10 @@ mschap_challenge_response(u_int8_t *challenge, u_int8_t *pwhash, } void -mschap_ntpassword_hash(u_int8_t *in, int inlen, u_int8_t *hash) +mschap_ntpassword_hash(uint8_t *in, int inlen, uint8_t *hash) { EVP_MD_CTX ctx; - u_int mdlen; + unsigned int mdlen; EVP_DigestInit(&ctx, EVP_md4()); EVP_DigestUpdate(&ctx, in, inlen); @@ -143,13 +143,13 @@ mschap_ntpassword_hash(u_int8_t *in, int inlen, u_int8_t *hash) } void -mschap_challenge_hash(u_int8_t *peer_challenge, u_int8_t *auth_challenge, - u_int8_t *username, int usernamelen, u_int8_t *challenge) +mschap_challenge_hash(uint8_t *peer_challenge, uint8_t *auth_challenge, + uint8_t *username, int usernamelen, uint8_t *challenge) { EVP_MD_CTX ctx; - u_int8_t md[SHA_DIGEST_LENGTH]; - u_int mdlen; - u_int8_t *name; + uint8_t md[SHA_DIGEST_LENGTH]; + unsigned int mdlen; + uint8_t *name; if ((name = strrchr(username, '\\')) == NULL) name = username; @@ -166,12 +166,12 @@ mschap_challenge_hash(u_int8_t *peer_challenge, u_int8_t *auth_challenge, } 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) +mschap_nt_response(uint8_t *auth_challenge, uint8_t *peer_challenge, + uint8_t *username, int usernamelen, uint8_t *password, int passwordlen, + uint8_t *response) { - u_int8_t challenge[MSCHAP_CHALLENGE_SZ]; - u_int8_t password_hash[MSCHAP_HASH_SZ]; + uint8_t challenge[MSCHAP_CHALLENGE_SZ]; + uint8_t password_hash[MSCHAP_HASH_SZ]; mschap_challenge_hash(peer_challenge, auth_challenge, username, usernamelen, challenge); @@ -181,25 +181,25 @@ mschap_nt_response(u_int8_t *auth_challenge, u_int8_t *peer_challenge, } 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) +mschap_auth_response(uint8_t *password, int passwordlen, + uint8_t *ntresponse, uint8_t *auth_challenge, uint8_t *peer_challenge, + uint8_t *username, int usernamelen, uint8_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; + uint8_t password_hash[MSCHAP_HASH_SZ]; + uint8_t password_hash2[MSCHAP_HASH_SZ]; + uint8_t challenge[MSCHAP_CHALLENGE_SZ]; + uint8_t md[SHA_DIGEST_LENGTH], *ptr; + unsigned int mdlen; int i; - const u_int8_t hex[] = "0123456789ABCDEF"; - static u_int8_t magic1[39] = { + const uint8_t hex[] = "0123456789ABCDEF"; + static uint8_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] = { + static uint8_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, @@ -242,13 +242,13 @@ mschap_auth_response(u_int8_t *password, int passwordlen, } void -mschap_masterkey(u_int8_t *password_hash2, u_int8_t *ntresponse, - u_int8_t *masterkey) +mschap_masterkey(uint8_t *password_hash2, uint8_t *ntresponse, + uint8_t *masterkey) { - u_int8_t md[SHA_DIGEST_LENGTH]; - u_int mdlen; + uint8_t md[SHA_DIGEST_LENGTH]; + unsigned int mdlen; EVP_MD_CTX ctx; - static u_int8_t magic1[27] = { + static uint8_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 @@ -264,14 +264,14 @@ mschap_masterkey(u_int8_t *password_hash2, u_int8_t *ntresponse, } void -mschap_asymetric_startkey(u_int8_t *masterkey, u_int8_t *sessionkey, +mschap_asymetric_startkey(uint8_t *masterkey, uint8_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] = { + uint8_t md[SHA_DIGEST_LENGTH]; + unsigned int mdlen; + uint8_t *s; + static uint8_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, @@ -282,7 +282,7 @@ mschap_asymetric_startkey(u_int8_t *masterkey, u_int8_t *sessionkey, 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x2e }; - static u_int8_t magic3[84] = { + static uint8_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, @@ -310,14 +310,14 @@ mschap_asymetric_startkey(u_int8_t *masterkey, u_int8_t *sessionkey, } void -mschap_msk(u_int8_t *password, int passwordlen, - u_int8_t *ntresponse, u_int8_t *msk) +mschap_msk(uint8_t *password, int passwordlen, + uint8_t *ntresponse, uint8_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]; + uint8_t password_hash[MSCHAP_HASH_SZ]; + uint8_t password_hash2[MSCHAP_HASH_SZ]; + uint8_t masterkey[MSCHAP_MASTERKEY_SZ]; + uint8_t sendkey[MSCHAP_MASTERKEY_SZ]; + uint8_t recvkey[MSCHAP_MASTERKEY_SZ]; mschap_ntpassword_hash(password, passwordlen, password_hash); mschap_ntpassword_hash(password_hash, MSCHAP_HASH_SZ, password_hash2); @@ -333,58 +333,12 @@ mschap_msk(u_int8_t *password, int passwordlen, } void -mschap_newkey(u_int8_t *startkey, u_int8_t *sessionkey, - long sessionkeylen, u_int8_t *key) +mschap_radiuskey(uint8_t *plain, const uint8_t *crypted, + const uint8_t *authenticator, const uint8_t *secret) { 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 + 1], *ptr, *end; - u_int8_t hash[MSCHAP_HASH_SZ]; - - bzero(&SECRET, sizeof(SECRET)); - end = SECRET + (sizeof(SECRET) - 1); - for (ptr = SECRET; *secret && ptr < end; ptr++, secret++) - *ptr = toupper(*secret); - - 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; + uint8_t b[MD5_DIGEST_LENGTH], p[32]; + unsigned int i, mdlen; EVP_DigestInit(&ctx, EVP_md5()); EVP_DigestUpdate(&ctx, secret, strlen(secret)); diff --git a/iked/chap_ms.h b/iked/chap_ms.h index f06940c..53b3adb 100644 --- a/iked/chap_ms.h +++ b/iked/chap_ms.h @@ -1,4 +1,4 @@ -/* $OpenBSD: chap_ms.h,v 1.3 2013/01/08 10:38:19 reyk Exp $ */ +/* $OpenBSD: chap_ms.h,v 1.6 2015/08/21 11:59:27 reyk Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter @@ -16,8 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef _CHAP_MS_H -#define _CHAP_MS_H +#ifndef CHAP_MS_H +#define CHAP_MS_H #define MSCHAP_CHALLENGE_SZ 8 #define MSCHAPV2_CHALLENGE_SZ 16 @@ -29,23 +29,19 @@ #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_response(uint8_t *, uint8_t *, uint8_t *, int, + uint8_t *, int , uint8_t *); +void mschap_auth_response(uint8_t *, int, uint8_t *, uint8_t *, + uint8_t *, uint8_t *, int, uint8_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(uint8_t *, int, uint8_t *); +void mschap_challenge_hash(uint8_t *, uint8_t *, uint8_t *, + int, uint8_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(uint8_t *, uint8_t *, int, int, int); +void mschap_masterkey(uint8_t *, uint8_t *, uint8_t *); +void mschap_radiuskey(uint8_t *, const uint8_t *, const uint8_t *, + const uint8_t *); +void mschap_msk(uint8_t *, int, uint8_t *, uint8_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 */ +#endif /* CHAP_MS_H */ diff --git a/iked/config.c b/iked/config.c index 397191d..cc4e060 100644 --- a/iked/config.c +++ b/iked/config.c @@ -1,4 +1,4 @@ -/* $OpenBSD: config.c,v 1.20 2013/03/21 04:30:14 deraadt Exp $ */ +/* $OpenBSD: config.c,v 1.42 2016/06/01 11:16:41 patrick Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter @@ -16,30 +16,22 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include "openbsd-compat/sys-queue.h" + #include #include #include -#include -#include -#include -#include -#include -#include - #include #include #include #include -#include #include #include #include #include #include +#include "defines.h" #include "iked.h" #include "ikev2.h" @@ -70,31 +62,56 @@ config_new_sa(struct iked *env, int initiator) return (sa); } -u_int64_t +uint64_t config_getspi(void) { - u_int64_t spi; + uint64_t spi; - spi = ((u_int64_t)arc4random() << 32) | arc4random(); - if (spi == 0) - return (config_getspi()); + do { + arc4random_buf(&spi, sizeof spi); + } while (spi == 0); return (spi); } void -config_free_sa(struct iked *env, struct iked_sa *sa) +config_free_kex(struct iked_kex *kex) { - (void)RB_REMOVE(iked_sas, &env->sc_sas, sa); + if (kex == NULL) + return; + + ibuf_release(kex->kex_inonce); + ibuf_release(kex->kex_rnonce); - timer_deregister(env, &sa->sa_timer); + if (kex->kex_dhgroup != NULL) + group_free(kex->kex_dhgroup); + ibuf_release(kex->kex_dhiexchange); + ibuf_release(kex->kex_dhrexchange); + + free(kex); +} + +void +config_free_sa(struct iked *env, struct iked_sa *sa) +{ + timer_del(env, &sa->sa_timer); + timer_del(env, &sa->sa_rekey); config_free_proposals(&sa->sa_proposals, 0); config_free_childsas(env, &sa->sa_childsas, NULL, NULL); sa_free_flows(env, &sa->sa_flows); + if (sa->sa_addrpool) { + (void)RB_REMOVE(iked_addrpool, &env->sc_addrpool, sa); + free(sa->sa_addrpool); + } + if (sa->sa_addrpool6) { + (void)RB_REMOVE(iked_addrpool6, &env->sc_addrpool6, sa); + free(sa->sa_addrpool6); + } + if (sa->sa_policy) { - (void)RB_REMOVE(iked_sapeers, &sa->sa_policy->pol_sapeers, sa); + TAILQ_REMOVE(&sa->sa_policy->pol_sapeers, sa, sa_peer_entry); policy_unref(env, sa->sa_policy); } @@ -130,8 +147,7 @@ config_free_sa(struct iked *env, struct iked_sa *sa) ibuf_release(sa->sa_rcert.id_buf); ibuf_release(sa->sa_eap.id_buf); - if (sa->sa_eapid != NULL) - free(sa->sa_eapid); + free(sa->sa_eapid); ibuf_release(sa->sa_eapmsk); free(sa); @@ -145,8 +161,10 @@ config_new_policy(struct iked *env) if ((pol = calloc(1, sizeof(*pol))) == NULL) return (NULL); + /* XXX caller does this again */ TAILQ_INIT(&pol->pol_proposals); - RB_INIT(&pol->pol_sapeers); + TAILQ_INIT(&pol->pol_sapeers); + RB_INIT(&pol->pol_flows); return (pol); } @@ -161,10 +179,13 @@ config_free_policy(struct iked *env, struct iked_policy *pol) TAILQ_REMOVE(&env->sc_policies, pol, pol_entry); - RB_FOREACH(sa, iked_sapeers, &pol->pol_sapeers) { - /* Remove from the policy tree, but keep for existing SAs */ + TAILQ_FOREACH(sa, &pol->pol_sapeers, sa_peer_entry) { + /* Remove from the policy list, but keep for existing SAs */ if (sa->sa_policy == pol) policy_ref(env, pol); + else + log_warnx("%s: ERROR: sa_policy %p != pol %p", + __func__, sa->sa_policy, pol); } if (pol->pol_refcnt) @@ -177,7 +198,8 @@ config_free_policy(struct iked *env, struct iked_policy *pol) } struct iked_proposal * -config_add_proposal(struct iked_proposals *head, u_int id, u_int proto) +config_add_proposal(struct iked_proposals *head, unsigned int id, + unsigned int proto) { struct iked_proposal *pp; @@ -199,7 +221,7 @@ config_add_proposal(struct iked_proposals *head, u_int id, u_int proto) } void -config_free_proposals(struct iked_proposals *head, u_int proto) +config_free_proposals(struct iked_proposals *head, unsigned int proto) { struct iked_proposal *prop, *next; @@ -266,13 +288,13 @@ config_free_childsas(struct iked *env, struct iked_childsas *head, } struct iked_transform * -config_add_transform(struct iked_proposal *prop, u_int type, - u_int id, u_int length, u_int keylength) +config_add_transform(struct iked_proposal *prop, unsigned int type, + unsigned int id, unsigned int length, unsigned int keylength) { struct iked_transform *xform; struct iked_constmap *map = NULL; int score = 1; - u_int i; + unsigned int i; switch (type) { case IKEV2_XFORMTYPE_ENCR: @@ -321,8 +343,8 @@ config_add_transform(struct iked_proposal *prop, u_int type, } } - if ((xform = realloc(prop->prop_xforms, - (prop->prop_nxforms + 1) * sizeof(*xform))) == NULL) { + if ((xform = reallocarray(prop->prop_xforms, + prop->prop_nxforms + 1, sizeof(*xform))) == NULL) { return (NULL); } @@ -341,14 +363,18 @@ config_add_transform(struct iked_proposal *prop, u_int type, } struct iked_transform * -config_findtransform(struct iked_proposals *props, u_int8_t type) +config_findtransform(struct iked_proposals *props, uint8_t type, + unsigned int proto) { struct iked_proposal *prop; struct iked_transform *xform; - u_int i; + unsigned int i; /* Search of the first transform with the desired type */ TAILQ_FOREACH(prop, props, prop_entry) { + /* Find any proposal or only selected SA proto */ + if (proto != 0 && prop->prop_protoid != proto) + continue; for (i = 0; i < prop->prop_nxforms; i++) { xform = prop->prop_xforms + i; if (xform->xform_type == type) @@ -388,41 +414,39 @@ config_new_user(struct iked *env, struct iked_user *new) */ int -config_setcoupled(struct iked *env, u_int couple) +config_setcoupled(struct iked *env, unsigned int couple) { - u_int type; + unsigned int type; type = couple ? IMSG_CTL_COUPLE : IMSG_CTL_DECOUPLE; - proc_compose_imsg(env, PROC_IKEV1, type, -1, NULL, 0); - proc_compose_imsg(env, PROC_IKEV2, type, -1, NULL, 0); + proc_compose(&env->sc_ps, PROC_IKEV2, type, NULL, 0); return (0); } int -config_getcoupled(struct iked *env, u_int type) +config_getcoupled(struct iked *env, unsigned int type) { return (pfkey_couple(env->sc_pfkey, &env->sc_sas, type == IMSG_CTL_COUPLE ? 1 : 0)); } int -config_setmode(struct iked *env, u_int passive) +config_setmode(struct iked *env, unsigned int passive) { - u_int type; + unsigned int type; type = passive ? IMSG_CTL_PASSIVE : IMSG_CTL_ACTIVE; - proc_compose_imsg(env, PROC_IKEV1, type, -1, NULL, 0); - proc_compose_imsg(env, PROC_IKEV2, type, -1, NULL, 0); + proc_compose(&env->sc_ps, PROC_IKEV2, type, NULL, 0); return (0); } int -config_getmode(struct iked *env, u_int type) +config_getmode(struct iked *env, unsigned int type) { - u_int8_t old; - u_char *mode[] = { "active", "passive" }; + uint8_t old; + unsigned char *mode[] = { "active", "passive" }; old = env->sc_passive ? 1 : 0; env->sc_passive = type == IMSG_CTL_PASSIVE ? 1 : 0; @@ -437,9 +461,9 @@ config_getmode(struct iked *env, u_int type) } int -config_setreset(struct iked *env, u_int mode, enum privsep_procid id) +config_setreset(struct iked *env, unsigned int mode, enum privsep_procid id) { - proc_compose_imsg(env, id, IMSG_CTL_RESET, -1, &mode, sizeof(mode)); + proc_compose(&env->sc_ps, id, IMSG_CTL_RESET, &mode, sizeof(mode)); return (0); } @@ -449,7 +473,7 @@ 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; + unsigned int mode; IMSG_SIZE_CHECK(imsg, &mode); memcpy(&mode, imsg->data, sizeof(mode)); @@ -468,6 +492,7 @@ config_getreset(struct iked *env, struct imsg *imsg) for (sa = RB_MIN(iked_sas, &env->sc_sas); sa != NULL; sa = nextsa) { nextsa = RB_NEXT(iked_sas, &env->sc_sas, sa); + RB_REMOVE(iked_sas, &env->sc_sas, sa); config_free_sa(env, sa); } } @@ -493,8 +518,8 @@ config_setsocket(struct iked *env, struct sockaddr_storage *ss, if ((s = udp_bind((struct sockaddr *)ss, port)) == -1) return (-1); - proc_compose_imsg(env, id, IMSG_UDP_SOCKET, s, - ss, sizeof(*ss)); + proc_compose_imsg(&env->sc_ps, id, -1, + IMSG_UDP_SOCKET, -1, s, ss, sizeof(*ss)); return (0); } @@ -531,7 +556,8 @@ config_getsocket(struct iked *env, struct imsg *imsg, if (*sptr == NULL) *sptr = sock; if (*nptr == NULL && - socket_getport(&sock->sock_addr) == IKED_NATT_PORT) + socket_getport((struct sockaddr *)&sock->sock_addr) == + IKED_NATT_PORT) *nptr = sock; event_set(&sock->sock_ev, sock->sock_fd, @@ -548,7 +574,8 @@ config_setpfkey(struct iked *env, enum privsep_procid id) if ((s = pfkey_socket()) == -1) return (-1); - proc_compose_imsg(env, id, IMSG_PFKEY_SOCKET, s, NULL, 0); + proc_compose_imsg(&env->sc_ps, id, -1, + IMSG_PFKEY_SOCKET, -1, s, NULL, 0); return (0); } @@ -568,7 +595,7 @@ config_setuser(struct iked *env, struct iked_user *usr, enum privsep_procid id) return (0); } - proc_compose_imsg(env, id, IMSG_CFG_USER, -1, usr, sizeof(*usr)); + proc_compose(&env->sc_ps, id, IMSG_CFG_USER, usr, sizeof(*usr)); return (0); } @@ -633,13 +660,12 @@ config_setpolicy(struct iked *env, struct iked_policy *pol, iov[c++].iov_len = sizeof(*flow); } - if (env->sc_opts & IKED_OPT_NOACTION) { - print_policy(pol); + print_policy(pol); + + if (env->sc_opts & IKED_OPT_NOACTION) return (0); - } - if (proc_composev_imsg(env, id, IMSG_CFG_POLICY, -1, - iov, iovcnt) == -1) + if (proc_composev(&env->sc_ps, id, IMSG_CFG_POLICY, iov, iovcnt) == -1) return (-1); return (0); @@ -653,8 +679,8 @@ config_getpolicy(struct iked *env, struct imsg *imsg) 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; + unsigned int i, j; + uint8_t *buf = (uint8_t *)imsg->data; IMSG_SIZE_CHECK(imsg, pol); log_debug("%s: received policy", __func__); @@ -666,6 +692,7 @@ config_getpolicy(struct iked *env, struct imsg *imsg) offset += sizeof(*pol); TAILQ_INIT(&pol->pol_proposals); + TAILQ_INIT(&pol->pol_sapeers); RB_INIT(&pol->pol_flows); for (i = 0; i < pol->pol_nproposals; i++) { @@ -694,7 +721,8 @@ config_getpolicy(struct iked *env, struct imsg *imsg) memcpy(flow, buf + offset, sizeof(*flow)); offset += sizeof(*flow); - RB_INSERT(iked_flows, &pol->pol_flows, flow); + if (RB_INSERT(iked_flows, &pol->pol_flows, flow)) + free(flow); } TAILQ_INSERT_TAIL(&env->sc_policies, pol, pol_entry); @@ -706,8 +734,6 @@ config_getpolicy(struct iked *env, struct imsg *imsg) env->sc_defaultcon = pol; } - print_policy(pol); - return (0); } @@ -717,7 +743,7 @@ config_setcompile(struct iked *env, enum privsep_procid id) if (env->sc_opts & IKED_OPT_NOACTION) return (0); - proc_compose_imsg(env, id, IMSG_COMPILE, -1, NULL, 0); + proc_compose(&env->sc_ps, id, IMSG_COMPILE, NULL, 0); return (0); } @@ -733,3 +759,28 @@ config_getcompile(struct iked *env, struct imsg *imsg) log_debug("%s: compilation done", __func__); return (0); } + +int +config_setocsp(struct iked *env) +{ + if (env->sc_opts & IKED_OPT_NOACTION) + return (0); + proc_compose(&env->sc_ps, PROC_CERT, + IMSG_OCSP_URL, env->sc_ocsp_url, + env->sc_ocsp_url ? strlen(env->sc_ocsp_url) : 0); + + return (0); +} + +int +config_getocsp(struct iked *env, struct imsg *imsg) +{ + free(env->sc_ocsp_url); + if (IMSG_DATA_SIZE(imsg) > 0) + env->sc_ocsp_url = get_string(imsg->data, IMSG_DATA_SIZE(imsg)); + else + env->sc_ocsp_url = NULL; + log_debug("%s: ocsp_url %s", __func__, + env->sc_ocsp_url ? env->sc_ocsp_url : "none"); + return (0); +} diff --git a/iked/control.c b/iked/control.c index dfa48b3..2043c3f 100644 --- a/iked/control.c +++ b/iked/control.c @@ -1,4 +1,4 @@ -/* $OpenBSD: control.c,v 1.12 2013/03/21 04:30:14 deraadt Exp $ */ +/* $OpenBSD: control.c,v 1.21 2015/12/05 13:09:46 claudio Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter @@ -17,12 +17,11 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "openbsd-compat/sys-queue.h" -#include + #include #include #include -#include "openbsd-compat/sys-tree.h" + #include @@ -46,7 +45,32 @@ struct ctl_conn *control_connbyfd(int); void control_close(int, struct control_sock *); void control_dispatch_imsg(int, short, void *); +void control_dispatch_parent(int, short, void *); void control_imsg_forward(struct imsg *); +void control_run(struct privsep *, struct privsep_proc *, void *); + +static struct privsep_proc procs[] = { + { "parent", PROC_PARENT, NULL } +}; + +pid_t +control(struct privsep *ps, struct privsep_proc *p) +{ + return (proc_run(ps, p, procs, nitems(procs), control_run, NULL)); +} + +void +control_run(struct privsep *ps, struct privsep_proc *p, void *arg) +{ + /* + * pledge in the control process: + * stdio - for malloc and basic I/O including events. + * cpath - for unlinking the control socket. + * unix - for the control socket. + */ + if (pledge("stdio cpath unix", NULL) == -1) + fatal("pledge"); +} int control_init(struct privsep *ps, struct control_sock *cs) @@ -59,7 +83,7 @@ control_init(struct privsep *ps, struct control_sock *cs) if (cs->cs_name == NULL) return (0); - if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + if ((fd = bsd_socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1) { log_warn("%s: socket", __func__); return (-1); } @@ -102,7 +126,6 @@ control_init(struct privsep *ps, struct control_sock *cs) return (-1); } - socket_set_blockmode(fd, BM_NONBLOCK); cs->cs_fd = fd; cs->cs_env = env; @@ -153,8 +176,8 @@ control_accept(int listenfd, short event, void *arg) return; len = sizeof(sun); - if ((connfd = accept(listenfd, - (struct sockaddr *)&sun, &len)) == -1) { + if ((connfd = bsd_accept4(listenfd, + (struct sockaddr *)&sun, &len, SOCK_NONBLOCK)) == -1) { /* * Pause accept if we are out of file descriptors, or * libevent will haunt us here too. @@ -170,8 +193,6 @@ control_accept(int listenfd, short event, void *arg) return; } - socket_set_blockmode(connfd, BM_NONBLOCK); - if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) { log_warn("%s", __func__); close(connfd); @@ -241,22 +262,18 @@ control_dispatch_imsg(int fd, short event, void *arg) return; } - switch (event) { - case EV_READ: - if ((n = imsg_read(&c->iev.ibuf)) == -1 || n == 0) { + if (event & EV_READ) { + if (((n = imsg_read(&c->iev.ibuf)) == -1 && errno != EAGAIN) || + n == 0) { control_close(fd, cs); return; } - break; - case EV_WRITE: - if (msgbuf_write(&c->iev.ibuf.w) < 0) { + } + if (event & EV_WRITE) { + if (msgbuf_write(&c->iev.ibuf.w) <= 0 && errno != EAGAIN) { control_close(fd, cs); return; } - imsg_event_add(&c->iev); - return; - default: - fatalx("unknown event"); } for (;;) { @@ -288,9 +305,8 @@ control_dispatch_imsg(int fd, short event, void *arg) memcpy(&v, imsg.data, sizeof(v)); log_verbose(v); - proc_forward_imsg(env, &imsg, PROC_PARENT); - proc_forward_imsg(env, &imsg, PROC_IKEV2); - proc_forward_imsg(env, &imsg, PROC_IKEV1); + proc_forward_imsg(&env->sc_ps, &imsg, PROC_PARENT, -1); + proc_forward_imsg(&env->sc_ps, &imsg, PROC_IKEV2, -1); break; case IMSG_CTL_RELOAD: case IMSG_CTL_RESET: @@ -298,7 +314,7 @@ control_dispatch_imsg(int fd, short event, void *arg) case IMSG_CTL_DECOUPLE: case IMSG_CTL_ACTIVE: case IMSG_CTL_PASSIVE: - proc_forward_imsg(env, &imsg, PROC_PARENT); + proc_forward_imsg(&env->sc_ps, &imsg, PROC_PARENT, -1); break; default: log_debug("%s: error handling imsg %d", @@ -318,7 +334,7 @@ control_imsg_forward(struct imsg *imsg) TAILQ_FOREACH(c, &ctl_conns, entry) if (c->flags & CTL_CONN_NOTIFY) - imsg_compose(&c->iev.ibuf, imsg->hdr.type, + imsg_compose_event(&c->iev, imsg->hdr.type, 0, imsg->hdr.pid, -1, imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE); } diff --git a/iked/crypto.c b/iked/crypto.c index 6959c37..4931107 100644 --- a/iked/crypto.c +++ b/iked/crypto.c @@ -1,4 +1,4 @@ -/* $OpenBSD: crypto.c,v 1.9 2013/01/08 10:38:19 reyk Exp $ */ +/* $OpenBSD: crypto.c,v 1.19 2015/10/31 19:28:19 naddy Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter @@ -16,8 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include "openbsd-compat/sys-queue.h" +#include /* roundup */ + #include #include @@ -34,12 +34,44 @@ #include #include #include +#include #include "iked.h" #include "ikev2.h" +/* RFC 7427, A.1 */ +static const uint8_t sha256WithRSAEncryption[] = { + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00 +}; +static const uint8_t sha384WithRSAEncryption[] = { + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0c, 0x05, 0x00 +}; +static const uint8_t sha512WithRSAEncryption[] = { + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0d, 0x05, 0x00 +}; + +struct { + uint8_t sc_len; + const uint8_t *sc_oid; + const EVP_MD *(*sc_md)(void); +} schemes[] = { + { sizeof(sha256WithRSAEncryption), + sha256WithRSAEncryption, EVP_sha256 }, + { sizeof(sha384WithRSAEncryption), + sha384WithRSAEncryption, EVP_sha384 }, + { sizeof(sha512WithRSAEncryption), + sha512WithRSAEncryption, EVP_sha512 }, +}; + +int _dsa_verify_init(struct iked_dsa *, const uint8_t *, size_t); +size_t _dsa_verify_offset(struct iked_dsa *, uint8_t *); +int _dsa_sign_encode(struct iked_dsa *, uint8_t *, size_t *); + struct iked_hash * -hash_new(u_int8_t type, u_int16_t id) +hash_new(uint8_t type, uint16_t id) { struct iked_hash *hash; const EVP_MD *md = NULL; @@ -197,7 +229,7 @@ hash_update(struct iked_hash *hash, void *buf, size_t len) void hash_final(struct iked_hash *hash, void *buf, size_t *len) { - u_int length = 0; + unsigned int length = 0; HMAC_Final(hash->hash_ctx, buf, &length); *len = (size_t)length; @@ -222,7 +254,7 @@ hash_keylength(struct iked_hash *hash) } struct iked_cipher * -cipher_new(u_int8_t type, u_int16_t id, u_int16_t id_length) +cipher_new(uint8_t type, uint16_t id, uint16_t id_length) { struct iked_cipher *encr; const EVP_CIPHER *cipher = NULL; @@ -328,7 +360,7 @@ 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", __func__, len); + log_debug("%s: invalid IV length %zu", __func__, len); return (NULL); } encr->encr_iv = ibuf_new(iv, encr->encr_ivlength); @@ -384,7 +416,7 @@ cipher_update(struct iked_cipher *encr, void *in, size_t inlen, olen = 0; if (!EVP_CipherUpdate(encr->encr_ctx, out, &olen, in, inlen)) { - ca_sslerror(); + ca_sslerror(__func__); *outlen = 0; return; } @@ -398,7 +430,7 @@ cipher_final(struct iked_cipher *encr, void *out, size_t *outlen) olen = 0; if (!EVP_CipherFinal_ex(encr->encr_ctx, out, &olen)) { - ca_sslerror(); + ca_sslerror(__func__); *outlen = 0; return; } @@ -434,23 +466,22 @@ cipher_outlength(struct iked_cipher *encr, size_t inlen) } struct iked_dsa * -dsa_new(u_int16_t id, struct iked_hash *prf, int sign) +dsa_new(uint16_t id, struct iked_hash *prf, int sign) { struct iked_dsa *dsap = NULL, dsa; bzero(&dsa, sizeof(dsa)); switch (id) { + case IKEV2_AUTH_SIG: + if (sign) + dsa.dsa_priv = EVP_sha256(); /* XXX should be passed */ + else + dsa.dsa_priv = NULL; /* set later by dsa_init() */ + break; 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"); + /* RFC5996 says we SHOULD use SHA1 here */ + dsa.dsa_priv = EVP_sha1(); break; case IKEV2_AUTH_SHARED_KEY_MIC: if (prf == NULL || prf->hash_priv == NULL) @@ -467,7 +498,7 @@ dsa_new(u_int16_t id, struct iked_hash *prf, int sign) case IKEV2_AUTH_ECDSA_384: dsa.dsa_priv = EVP_sha384(); break; - case IKEV2_AUTH_ECDSA_512: + case IKEV2_AUTH_ECDSA_521: dsa.dsa_priv = EVP_sha512(); break; default: @@ -505,13 +536,13 @@ dsa_new(u_int16_t id, struct iked_hash *prf, int sign) } struct iked_dsa * -dsa_sign_new(u_int16_t id, struct iked_hash *prf) +dsa_sign_new(uint16_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) +dsa_verify_new(uint16_t id, struct iked_hash *prf) { return (dsa_new(id, prf, 0)); } @@ -537,7 +568,7 @@ dsa_free(struct iked_dsa *dsa) } struct ibuf * -dsa_setkey(struct iked_dsa *dsa, void *key, size_t keylen, u_int8_t type) +dsa_setkey(struct iked_dsa *dsa, void *key, size_t keylen, uint8_t type) { BIO *rawcert = NULL; X509 *cert = NULL; @@ -578,6 +609,7 @@ dsa_setkey(struct iked_dsa *dsa, void *key, size_t keylen, u_int8_t type) if (!EVP_PKEY_set1_RSA(pkey, rsa)) goto sslerr; + RSA_free(rsa); /* pkey now has the reference */ dsa->dsa_cert = NULL; dsa->dsa_key = pkey; break; @@ -591,7 +623,7 @@ dsa_setkey(struct iked_dsa *dsa, void *key, size_t keylen, u_int8_t type) return (dsa->dsa_keydata); sslerr: - ca_sslerror(); + ca_sslerror(__func__); err: log_debug("%s: error", __func__); @@ -608,7 +640,53 @@ dsa_setkey(struct iked_dsa *dsa, void *key, size_t keylen, u_int8_t type) } int -dsa_init(struct iked_dsa *dsa) +_dsa_verify_init(struct iked_dsa *dsa, const uint8_t *sig, size_t len) +{ + uint8_t oidlen; + size_t i; + + if (dsa->dsa_priv != NULL) + return (0); + /* + * For IKEV2_AUTH_SIG the oid of the authentication signature + * is encoded in the first bytes of the auth message. + */ + if (dsa->dsa_method != IKEV2_AUTH_SIG) { + log_debug("%s: dsa_priv not set for %s", __func__, + print_map(dsa->dsa_method, ikev2_auth_map)); + return (-1); + } + if (sig == NULL) { + log_debug("%s: signature missing", __func__); + return (-1); + } + if (len < 1) { + log_debug("%s: signature (%zu) too small for oid length", + __func__, len); + return (-1); + } + memcpy(&oidlen, sig, sizeof(oidlen)); + if (len < (size_t)oidlen + 1) { + log_debug("%s: signature (%zu) too small for oid (%u)", + __func__, len, oidlen); + return (-1); + } + for (i = 0; i < nitems(schemes); i++) { + if (oidlen == schemes[i].sc_len && + memcmp(sig + 1, schemes[i].sc_oid, + schemes[i].sc_len) == 0) { + dsa->dsa_priv = (*schemes[i].sc_md)(); + log_debug("%s: signature scheme %zd selected", + __func__, i); + return (0); + } + } + log_debug("%s: unsupported signature (%d)", __func__, oidlen); + return (-1); +} + +int +dsa_init(struct iked_dsa *dsa, const void *buf, size_t len) { int ret; @@ -621,8 +699,11 @@ dsa_init(struct iked_dsa *dsa) if (dsa->dsa_sign) ret = EVP_SignInit_ex(dsa->dsa_ctx, dsa->dsa_priv, NULL); - else + else { + if ((ret = _dsa_verify_init(dsa, buf, len)) != 0) + return (ret); ret = EVP_VerifyInit_ex(dsa->dsa_ctx, dsa->dsa_priv, NULL); + } return (ret ? 0 : -1); } @@ -642,18 +723,44 @@ dsa_update(struct iked_dsa *dsa, const void *buf, size_t len) return (ret ? 0 : -1); } +/* Prefix signature hash with encoded type */ +int +_dsa_sign_encode(struct iked_dsa *dsa, uint8_t *ptr, size_t *offp) +{ + if (offp) + *offp = 0; + if (dsa->dsa_method != IKEV2_AUTH_SIG) + return (0); + if (dsa->dsa_priv != EVP_sha256()) + return (-1); + if (ptr) { + ptr[0] = sizeof(sha256WithRSAEncryption); + memcpy(ptr + 1, sha256WithRSAEncryption, + sizeof(sha256WithRSAEncryption)); + } + if (offp) + *offp = 1 + sizeof(sha256WithRSAEncryption); + return (0); +} + size_t dsa_length(struct iked_dsa *dsa) { + size_t off = 0; + if (dsa->dsa_hmac) return (EVP_MD_size(dsa->dsa_priv)); - return (EVP_PKEY_size(dsa->dsa_key)); + if (_dsa_sign_encode(dsa, NULL, &off) < 0) + fatal("dsa_length: internal error"); + return (EVP_PKEY_size(dsa->dsa_key) + off); } ssize_t dsa_sign_final(struct iked_dsa *dsa, void *buf, size_t len) { - u_int siglen; + unsigned int siglen; + size_t off = 0; + uint8_t *ptr = buf; if (len < dsa_length(dsa)) return (-1); @@ -662,19 +769,36 @@ dsa_sign_final(struct iked_dsa *dsa, void *buf, size_t len) if (!HMAC_Final(dsa->dsa_ctx, buf, &siglen)) return (-1); } else { - if (!EVP_SignFinal(dsa->dsa_ctx, buf, &siglen, + if (_dsa_sign_encode(dsa, ptr, &off) < 0) + return (-1); + if (!EVP_SignFinal(dsa->dsa_ctx, ptr + off, &siglen, dsa->dsa_key)) return (-1); + siglen += off; } return (siglen); } +size_t +_dsa_verify_offset(struct iked_dsa *dsa, uint8_t *ptr) +{ + /* + * XXX assumes that _dsa_verify_init() has already checked + * the encoded method. + */ + if (dsa->dsa_method == IKEV2_AUTH_SIG) + return (ptr[0] + 1); + return (0); +} + 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); + uint8_t sig[EVP_MAX_MD_SIZE]; + unsigned int siglen = sizeof(sig); + uint8_t *ptr = buf; + size_t off = 0; if (dsa->dsa_hmac) { if (!HMAC_Final(dsa->dsa_ctx, sig, &siglen)) @@ -682,9 +806,11 @@ dsa_verify_final(struct iked_dsa *dsa, void *buf, size_t len) if (siglen != len || memcmp(buf, sig, siglen) != 0) return (-1); } else { - if (EVP_VerifyFinal(dsa->dsa_ctx, buf, len, + if ((off = _dsa_verify_offset(dsa, ptr)) >= len) + return (-1); + if (EVP_VerifyFinal(dsa->dsa_ctx, ptr + off, len - off, dsa->dsa_key) != 1) { - ca_sslerror(); + ca_sslerror(__func__); return (-1); } } diff --git a/iked/dh.c b/iked/dh.c index de33d96..ba4e2cb 100644 --- a/iked/dh.c +++ b/iked/dh.c @@ -1,7 +1,7 @@ -/* $OpenBSD: dh.c,v 1.10 2013/01/08 10:38:19 reyk Exp $ */ +/* $OpenBSD: dh.c,v 1.17 2015/08/21 11:59:27 reyk Exp $ */ /* - * Copyright (c) 2010-2013 Reyk Floeter + * Copyright (c) 2010-2014 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -16,31 +16,53 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include +#include /* roundup */ #include #include #include #include #include +#include +#include "defines.h" #include "dh.h" int dh_init(struct group *); +/* MODP */ 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 modp_create_exchange(struct group *, uint8_t *); +int modp_create_shared(struct group *, uint8_t *, uint8_t *); +/* EC2N/ECP */ 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_create_exchange(struct group *, uint8_t *); +int ec_create_shared(struct group *, uint8_t *, uint8_t *); -int ec_point2raw(struct group *, const EC_POINT *, u_int8_t *, size_t); +int ec_point2raw(struct group *, const EC_POINT *, uint8_t *, size_t); EC_POINT * - ec_raw2point(struct group *, u_int8_t *, size_t); + ec_raw2point(struct group *, uint8_t *, size_t); + +/* curve25519 */ +int ec25519_init(struct group *); +int ec25519_getlen(struct group *); +int ec25519_create_exchange(struct group *, uint8_t *); +int ec25519_create_shared(struct group *, uint8_t *, uint8_t *); + +#define CURVE25519_SIZE 32 /* 256 bits */ +struct curve25519_key { + uint8_t secret[CURVE25519_SIZE]; + uint8_t public[CURVE25519_SIZE]; +}; +extern int crypto_scalarmult_curve25519(unsigned char a[CURVE25519_SIZE], + const unsigned char b[CURVE25519_SIZE], + const unsigned char c[CURVE25519_SIZE]) + __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE))) + __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE))) + __attribute__((__bounded__(__minbytes__, 3, CURVE25519_SIZE))); struct group_id ike_groups[] = { { GROUP_MODP, 1, 768, @@ -277,7 +299,22 @@ struct group_id ike_groups[] = { "5E2327CFEF98C582664B4C0F6CC41659" }, { GROUP_ECP, 25, 192, NULL, NULL, NID_X9_62_prime192v1 }, - { GROUP_ECP, 26, 224, NULL, NULL, NID_secp224r1 } + { GROUP_ECP, 26, 224, NULL, NULL, NID_secp224r1 }, +#ifdef NID_brainpoolP224r1 + { GROUP_ECP, 27, 224, NULL, NULL, NID_brainpoolP224r1 }, +#endif +#ifdef NID_brainpoolP256r1 + { GROUP_ECP, 28, 256, NULL, NULL, NID_brainpoolP256r1 }, +#endif +#ifdef NID_brainpoolP384r1 + { GROUP_ECP, 29, 384, NULL, NULL, NID_brainpoolP384r1 }, +#endif +#ifdef NID_brainpoolP512r1 + { GROUP_ECP, 30, 512, NULL, NULL, NID_brainpoolP512r1 }, +#endif + + /* "Private use" extensions */ + { GROUP_CURVE25519, 1034, CURVE25519_SIZE * 8 } }; void @@ -296,16 +333,21 @@ group_free(struct group *group) DH_free(group->dh); if (group->ec != NULL) EC_KEY_free(group->ec); + if (group->curve25519 != NULL) { + explicit_bzero(group->curve25519, + sizeof(struct curve25519_key)); + free(group->curve25519); + } group->spec = NULL; free(group); } struct group * -group_get(u_int32_t id) +group_get(uint32_t id) { struct group_id *p = NULL; struct group *group; - u_int i, items; + unsigned int i, items; items = sizeof(ike_groups) / sizeof(ike_groups[0]); for (i = 0; i < items; i++) { @@ -337,6 +379,12 @@ group_get(u_int32_t id) group->exchange = ec_create_exchange; group->shared = ec_create_shared; break; + case GROUP_CURVE25519: + group->init = ec25519_init; + group->getlen = ec25519_getlen; + group->exchange = ec25519_create_exchange; + group->shared = ec25519_create_shared; + break; default: group_free(group); return (NULL); @@ -363,13 +411,13 @@ dh_getlen(struct group *group) } int -dh_create_exchange(struct group *group, u_int8_t *buf) +dh_create_exchange(struct group *group, uint8_t *buf) { return (group->exchange(group, buf)); } int -dh_create_shared(struct group *group, u_int8_t *secret, u_int8_t *exchange) +dh_create_shared(struct group *group, uint8_t *secret, uint8_t *exchange) { return (group->shared(group, secret, exchange)); } @@ -399,7 +447,7 @@ modp_getlen(struct group *group) } int -modp_create_exchange(struct group *group, u_int8_t *buf) +modp_create_exchange(struct group *group, uint8_t *buf) { DH *dh = group->dh; int len, ret; @@ -422,7 +470,7 @@ modp_create_exchange(struct group *group, u_int8_t *buf) } int -modp_create_shared(struct group *group, u_int8_t *secret, u_int8_t *exchange) +modp_create_shared(struct group *group, uint8_t *secret, uint8_t *exchange) { BIGNUM *ex; int len, ret; @@ -434,7 +482,7 @@ modp_create_shared(struct group *group, u_int8_t *secret, u_int8_t *exchange) ret = DH_compute_key(secret, ex, group->dh); BN_clear_free(ex); - if (!ret) + if (ret <= 0) return (-1); /* add zero padding */ @@ -453,6 +501,10 @@ ec_init(struct group *group) return (-1); if (!EC_KEY_generate_key(group->ec)) return (-1); + if (!EC_KEY_check_key(group->ec)) { + EC_KEY_free(group->ec); + return (-1); + } return (0); } @@ -466,7 +518,7 @@ ec_getlen(struct group *group) } int -ec_create_exchange(struct group *group, u_int8_t *buf) +ec_create_exchange(struct group *group, uint8_t *buf) { size_t len; @@ -478,10 +530,11 @@ ec_create_exchange(struct group *group, u_int8_t *buf) } int -ec_create_shared(struct group *group, u_int8_t *secret, u_int8_t *exchange) +ec_create_shared(struct group *group, uint8_t *secret, uint8_t *exchange) { const EC_GROUP *ecgroup = NULL; const BIGNUM *privkey; + EC_KEY *exkey = NULL; EC_POINT *exchangep = NULL, *secretp = NULL; int ret = -1; @@ -493,6 +546,17 @@ ec_create_shared(struct group *group, u_int8_t *secret, u_int8_t *exchange) ec_raw2point(group, exchange, ec_getlen(group))) == NULL) goto done; + if ((exkey = EC_KEY_new()) == NULL) + goto done; + if (!EC_KEY_set_group(exkey, ecgroup)) + goto done; + if (!EC_KEY_set_public_key(exkey, exchangep)) + goto done; + + /* validate exchangep */ + if (!EC_KEY_check_key(exkey)) + goto done; + if ((secretp = EC_POINT_new(ecgroup)) == NULL) goto done; @@ -502,6 +566,8 @@ ec_create_shared(struct group *group, u_int8_t *secret, u_int8_t *exchange) ret = ec_point2raw(group, secretp, secret, ec_getlen(group)); done: + if (exkey != NULL) + EC_KEY_free(exkey); if (exchangep != NULL) EC_POINT_clear_free(exchangep); if (secretp != NULL) @@ -512,7 +578,7 @@ ec_create_shared(struct group *group, u_int8_t *secret, u_int8_t *exchange) int ec_point2raw(struct group *group, const EC_POINT *point, - u_int8_t *buf, size_t len) + uint8_t *buf, size_t len) { const EC_GROUP *ecgroup = NULL; BN_CTX *bnctx = NULL; @@ -559,6 +625,11 @@ ec_point2raw(struct group *group, const EC_POINT *point, ret = 0; done: + /* Make sure to erase sensitive data */ + if (x != NULL) + BN_clear(x); + if (y != NULL) + BN_clear(y); BN_CTX_end(bnctx); BN_CTX_free(bnctx); @@ -566,7 +637,7 @@ ec_point2raw(struct group *group, const EC_POINT *point, } EC_POINT * -ec_raw2point(struct group *group, u_int8_t *buf, size_t len) +ec_raw2point(struct group *group, uint8_t *buf, size_t len) { const EC_GROUP *ecgroup = NULL; EC_POINT *point = NULL; @@ -612,8 +683,57 @@ ec_raw2point(struct group *group, u_int8_t *buf, size_t len) done: if (ret != 0 && point != NULL) EC_POINT_clear_free(point); + /* Make sure to erase sensitive data */ + if (x != NULL) + BN_clear(x); + if (y != NULL) + BN_clear(y); BN_CTX_end(bnctx); BN_CTX_free(bnctx); return (point); } + +int +ec25519_init(struct group *group) +{ + static const uint8_t basepoint[CURVE25519_SIZE] = { 9 }; + struct curve25519_key *curve25519; + + if ((curve25519 = calloc(1, sizeof(*curve25519))) == NULL) + return (-1); + + group->curve25519 = curve25519; + + arc4random_buf(curve25519->secret, CURVE25519_SIZE); + crypto_scalarmult_curve25519(curve25519->public, + curve25519->secret, basepoint); + + return (0); +} + +int +ec25519_getlen(struct group *group) +{ + if (group->spec == NULL) + return (0); + return (CURVE25519_SIZE); +} + +int +ec25519_create_exchange(struct group *group, uint8_t *buf) +{ + struct curve25519_key *curve25519 = group->curve25519; + + memcpy(buf, curve25519->public, ec25519_getlen(group)); + return (0); +} + +int +ec25519_create_shared(struct group *group, uint8_t *shared, uint8_t *public) +{ + struct curve25519_key *curve25519 = group->curve25519; + + crypto_scalarmult_curve25519(shared, curve25519->secret, public); + return (0); +} diff --git a/iked/dh.h b/iked/dh.h index d7b8ea8..e5b825c 100644 --- a/iked/dh.h +++ b/iked/dh.h @@ -1,4 +1,4 @@ -/* $OpenBSD: dh.h,v 1.5 2013/01/08 10:38:19 reyk Exp $ */ +/* $OpenBSD: dh.h,v 1.9 2015/08/21 11:59:27 reyk Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter @@ -16,18 +16,19 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef _DH_H_ -#define _DH_H_ +#ifndef DH_GROUP_H +#define DH_GROUP_H enum group_type { - GROUP_MODP = 0, - GROUP_EC2N = 1, - GROUP_ECP = 2 + GROUP_MODP = 0, + GROUP_EC2N = 1, + GROUP_ECP = 2, + GROUP_CURVE25519 = 3 }; struct group_id { enum group_type type; - u_int id; + unsigned int id; int bits; char *prime; char *generator; @@ -40,21 +41,22 @@ struct group { void *dh; void *ec; + void *curve25519; 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 *); + int (*exchange)(struct group *, uint8_t *); + int (*shared)(struct group *, uint8_t *, uint8_t *); }; #define DH_MAXSZ 1024 /* 8192 bits */ -void group_init(void); -void group_free(struct group *); -struct group *group_get(u_int32_t); +void group_init(void); +void group_free(struct group *); +struct group *group_get(uint32_t); 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 *); +int dh_create_exchange(struct group *, uint8_t *); +int dh_create_shared(struct group *, uint8_t *, uint8_t *); -#endif /* _DH_H_ */ +#endif /* DH_GROUP_H */ diff --git a/iked/eap.c b/iked/eap.c index bbb5a7c..2b8e87c 100644 --- a/iked/eap.c +++ b/iked/eap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: eap.c,v 1.9 2013/03/21 04:30:14 deraadt Exp $ */ +/* $OpenBSD: eap.c,v 1.14 2015/08/21 11:59:27 reyk Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter @@ -16,23 +16,18 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include "openbsd-compat/sys-queue.h" + #include #include #include #include -#if defined(__OpenBSD__) -#include -#endif #include #include #include #include #include -#include #include #include #include @@ -71,19 +66,19 @@ eap_identity_request(struct ibuf *e) char * eap_identity_response(struct eap_message *eap) { - size_t len; - char *str; - u_int8_t *ptr = (u_int8_t *)eap; + size_t len; + char *str; + uint8_t *ptr = (uint8_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", + log_info("%s: invalid identity response, length %zu", __func__, len); return (NULL); } - log_debug("%s: identity '%s' length %d", __func__, str, len); + log_debug("%s: identity '%s' length %zd", __func__, str, len); return (str); } @@ -177,11 +172,11 @@ eap_mschap(struct iked *env, struct iked_sa *sa, struct eap_message *eap) struct eap_mschap_peer *msp; struct eap_mschap *ms; struct eap_mschap_success *mss; - u_int8_t *ptr, *pass; + uint8_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]; + uint8_t ntresponse[EAP_MSCHAP_NTRESPONSE_SZ]; + uint8_t successmsg[EAP_MSCHAP_SUCCESS_SZ]; struct ibuf *eapmsg = NULL; int ret = -1; @@ -208,7 +203,7 @@ eap_mschap(struct iked *env, struct iked_sa *sa, struct eap_message *eap) } ms = (struct eap_mschap *)(eap + 1); - ptr = (u_int8_t *)(eap + 1); + ptr = (uint8_t *)(eap + 1); switch (ms->ms_opcode) { case EAP_MSOPCODE_RESPONSE: @@ -336,7 +331,7 @@ 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; + uint8_t *ptr; struct eap_mschap *ms; struct eap_mschap_challenge *msc; struct eap_mschap_response *msr; @@ -346,7 +341,7 @@ eap_parse(struct iked *env, struct iked_sa *sa, void *data, int response) /* length is already verified by the caller */ len = betoh16(hdr->eap_length); - ptr = (u_int8_t *)(eap + 1); + ptr = (uint8_t *)(eap + 1); switch (hdr->eap_code) { case EAP_CODE_REQUEST: @@ -399,7 +394,7 @@ eap_parse(struct iked *env, struct iked_sa *sa, void *data, int response) return (-1); } log_info("%s: %s %s id %d " - "length %d valuesize %d name '%s' length %d", + "length %d valuesize %d name '%s' length %zu", __func__, print_map(eap->eap_type, eap_type_map), print_map(ms->ms_opcode, eap_msopcode_map), @@ -420,7 +415,7 @@ eap_parse(struct iked *env, struct iked_sa *sa, void *data, int response) return (-1); } log_info("%s: %s %s id %d " - "length %d valuesize %d name '%s' name-length %d", + "length %d valuesize %d name '%s' name-length %zu", __func__, print_map(eap->eap_type, eap_type_map), print_map(ms->ms_opcode, eap_msopcode_map), @@ -442,7 +437,7 @@ eap_parse(struct iked *env, struct iked_sa *sa, void *data, int response) return (-1); } log_info("%s: %s %s request id %d " - "length %d message '%s' message-len %d", + "length %d message '%s' message-len %zu", __func__, print_map(eap->eap_type, eap_type_map), print_map(ms->ms_opcode, eap_msopcode_map), diff --git a/iked/eap.h b/iked/eap.h index a8d158e..cc567ea 100644 --- a/iked/eap.h +++ b/iked/eap.h @@ -1,4 +1,4 @@ -/* $OpenBSD: eap.h,v 1.3 2013/01/08 10:38:19 reyk Exp $ */ +/* $OpenBSD: eap.h,v 1.5 2015/08/21 11:59:27 reyk Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter @@ -16,20 +16,20 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef _IKEV2_EAP_H -#define _IKEV2_EAP_H +#ifndef IKED_EAP_H +#define IKED_EAP_H struct eap_header { - u_int8_t eap_code; - u_int8_t eap_id; - u_int16_t eap_length; + uint8_t eap_code; + uint8_t eap_id; + uint16_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; + uint8_t eap_code; + uint8_t eap_id; + uint16_t eap_length; + uint8_t eap_type; /* Followed by type-specific data */ } __packed; @@ -114,48 +114,48 @@ extern struct iked_constmap eap_type_map[]; extern struct iked_constmap eap_msopcode_map[]; struct eap_mschap { - u_int8_t ms_opcode; + uint8_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]; + uint8_t msc_opcode; + uint8_t msc_id; + uint16_t msc_length; + uint8_t msc_valuesize; + uint8_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; + uint8_t msp_challenge[EAP_MSCHAP_CHALLENGE_SZ]; + uint8_t msp_reserved[8]; + uint8_t msp_ntresponse[EAP_MSCHAP_NTRESPONSE_SZ]; + uint8_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; + uint8_t msr_opcode; + uint8_t msr_id; + uint16_t msr_length; + uint8_t msr_valuesize; union { - u_int8_t resp_data[EAP_MSCHAP_RESPONSE_SZ]; + uint8_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; + uint8_t mss_opcode; + uint8_t mss_id; + uint16_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; + uint8_t msf_opcode; + uint8_t msf_id; + uint16_t msf_length; /* Followed by variable-size message field */ } __packed; @@ -168,4 +168,4 @@ struct eap_mschap_failure { extern struct iked_constmap eap_mserror_map[]; -#endif /* _IKEV2_EAP_H */ +#endif /* IKED_EAP_H */ diff --git a/iked/genmap.sh b/iked/genmap.sh old mode 100644 new mode 100755 index 685f42e..9cd4ba0 --- a/iked/genmap.sh +++ b/iked/genmap.sh @@ -1,5 +1,5 @@ #!/bin/sh -# $OpenBSD: genmap.sh,v 1.6 2013/01/08 10:38:19 reyk Exp $ +# $OpenBSD: genmap.sh,v 1.7 2015/01/16 06:39:58 deraadt Exp $ # Copyright (c) 2010-2013 Reyk Floeter # @@ -28,7 +28,6 @@ EOF sed -n '1,/^ \*\//p' $1 cat < #include #include "types.h" diff --git a/iked/iked.8 b/iked/iked.8 index 7f2dc16..c03ec11 100644 --- a/iked/iked.8 +++ b/iked/iked.8 @@ -1,6 +1,6 @@ -.\" $OpenBSD: iked.8,v 1.13 2013/01/08 10:38:19 reyk Exp $ +.\" $OpenBSD: iked.8,v 1.19 2014/11/10 13:57:32 jmc Exp $ .\" -.\" Copyright (c) 2010-2013 Reyk Floeter +.\" Copyright (c) 2010 - 2014 Reyk Floeter .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: January 8 2013 $ +.Dd $Mdocdate: November 10 2014 $ .Dt IKED 8 .Os .Sh NAME @@ -23,9 +23,7 @@ .Sh SYNOPSIS .Nm iked .Op Fl 6dnSTtv -.Oo -.Fl D Ar macro Ns = Ns Ar value -.Oc +.Op Fl D Ar macro Ns = Ns Ar value .Op Fl f Ar file .Sh DESCRIPTION .Nm @@ -48,10 +46,8 @@ is provided by .Nm supports mutual authentication using RSA public keys and X.509 certificates. See the -.Sx FILES -section below and -.Sx PKI AND CERTIFICATE AUTHORITY COMMANDS -in +.Sx PUBLIC KEY AUTHENTICATION +section below and PKI AND CERTIFICATE AUTHORITY COMMANDS in .Xr ikectl 8 for more information about creating and maintaining the public key infrastructure. @@ -103,6 +99,54 @@ negotiate NAT-Traversal with the peers. .It Fl v Produce more verbose output. .El +.Sh PUBLIC KEY AUTHENTICATION +It is possible to store trusted public keys to make them directly +usable by +.Nm , +bypassing the need to use certificates. +The keys should be saved in PEM format (see +.Xr openssl 1 ) +and named and stored as follows: +.Pp +.Bl -tag -width "for_ufqdn_identitiesXX" -offset 3n -compact +.It For IPv4 identities: +/etc/iked/pubkeys/ipv4/A.B.C.D +.It For IPv6 identities: +/etc/iked/pubkeys/ipv6/abcd:abcd::ab:bc +.It For FQDN identities: +/etc/iked/pubkeys/fqdn/foo.bar.org +.It For UFQDN identities: +/etc/iked/pubkeys/ufqdn/user@foo.bar.org +.El +.Pp +Depending on the +.Ic srcid +and +.Ic dstid +specifications in +.Xr iked.conf 5 , +keys may be named after their IPv4 address, IPv6 address, +fully qualified domain name (FQDN) or user fully qualified domain name (UFQDN). +.Pp +For example, +.Nm +can authenticate using the pre-generated keys if the local public key, +by default +.Pa /etc/iked/local.pub , +is copied to the remote gateway as +.Pa /etc/iked/pubkeys/ipv4/local.gateway.ip.address +and the remote gateway's public key +is copied to the local gateway as +.Pa /etc/iked/pubkeys/ipv4/remote.gateway.ip.address . +Of course, new keys may also be generated +(the user is not required to use the pre-generated keys). +In this example, +.Ic srcid +and +.Ic dstid +would also have to be set to the specified addresses +in +.Xr iked.conf 5 . .Sh FILES .Bl -tag -width "/etc/iked/private/XXX" -compact .It Pa /etc/iked.conf @@ -154,38 +198,4 @@ program first appeared in The .Nm program was written by -.An Reyk Floeter Aq reyk@openbsd.org . -.Sh CAVEATS -.Nm -is not yet finished and is missing some important security features. -It should not yet be used in production networks. -.Sh BUGS -The portable version of -.Nm , -.Ic OpenIKED , -might have a few limitations on operating systems other than the most -recent versions of -.Ox . -A major difference between -.Ox -and other systems is the API and availability of the IPsec PFKEYv2 -flow implementation (SADB) and message passing between kernel and -.Nm . -Current known limitations are: -.Bl -tag -width Ds -.It Ic VPN traffic leakage prevention -Automatic blocking of IPv6 traffic is not working on KAME-bases stacks. -.It Ic NAT-T -NAT-T is only supported on OpenBSD and Darwin (OS X). -The NAT-T API is not standardized in PFKEYv2, not even on a semi-level, -and differs in most operating systems, if supported at all. -.It Ic Crypto algorithms -Some of the crypto algorithms are either not supported on other -systems or not implemented correctly. -For example, Linux still uses the broken pre-standard version of -hmac-sha2-256 by default that was specified with 96 bit truncation -instead of the standard 128 bit truncation. -The common workaround that allows to specify the truncation length -would be to use Linux' non-standard XFRM kernel API instead of -PFKEYv2. -.El +.An Reyk Floeter Aq Mt reyk@openbsd.org . diff --git a/iked/iked.c b/iked/iked.c index 7419f13..6127ee3 100644 --- a/iked/iked.c +++ b/iked/iked.c @@ -1,4 +1,4 @@ -/* $OpenBSD: iked.c,v 1.17 2013/03/21 04:30:14 deraadt Exp $ */ +/* $OpenBSD: iked.c,v 1.30 2015/12/07 12:46:37 reyk Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter @@ -16,30 +16,18 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include "openbsd-compat/sys-queue.h" + #include #include #include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_DEPRECATED_DAEMON -/* Apple deprected daemon() and prints an warning that breaks -Werror */ -#define daemon XXX_daemon -#endif - #include #include #include #include #include #include +#include #include #include #include @@ -48,32 +36,20 @@ #include "iked.h" #include "ikev2.h" -#ifdef HAVE_DEPRECATED_DAEMON -#undef daemon -extern int daemon(int, int); -#endif - __dead void usage(void); void parent_shutdown(struct iked *); void parent_sig_handler(int, short, void *); -int parent_dispatch_ikev1(int, struct privsep_proc *, struct imsg *); -int parent_dispatch_ikev2(int, struct privsep_proc *, struct imsg *); int parent_dispatch_ca(int, struct privsep_proc *, struct imsg *); +int parent_dispatch_control(int, struct privsep_proc *, struct imsg *); int parent_configure(struct iked *); static struct privsep_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 } + { "ca", PROC_CERT, parent_dispatch_ca, caproc, IKED_CA }, + { "control", PROC_CONTROL, parent_dispatch_control, control }, + { "ikev2", PROC_IKEV2, NULL, ikev2 } }; -#ifndef HAVE_SETPROCTITLE -/* Saved arguments to main(). */ -char **saved_argv; -int saved_argc; -#endif - __dead void usage(void) { @@ -94,25 +70,7 @@ main(int argc, char *argv[]) struct iked *env = NULL; struct privsep *ps; - log_init(1); - -#ifndef HAVE_SETPROCTITLE - /* Save argv. Duplicate so setproctitle emulation doesn't clobber it */ - saved_argc = argc; - saved_argv = calloc(argc + 1, sizeof(*saved_argv)); - if (saved_argv == NULL) - fatal("calloc: argv"); - for (c = 0; c < argc; c++) { - saved_argv[c] = strdup(argv[c]); - if (saved_argv[c] == NULL) - fatal("strdup: saved_argv[c]"); - } - saved_argv[c] = NULL; - - /* Prepare for later setproctitle emulation */ - compat_init_setproctitle(argc, argv); - argv = saved_argv; -#endif + log_init(1, LOG_DAEMON); while ((c = getopt(argc, argv, "6dD:nf:vSTt")) != -1) { switch (c) { @@ -159,13 +117,14 @@ main(int argc, char *argv[]) ps = &env->sc_ps; ps->ps_env = env; + TAILQ_INIT(&ps->ps_rcsocks); if ((opts & (IKED_OPT_NONATT|IKED_OPT_NATT)) == (IKED_OPT_NONATT|IKED_OPT_NATT)) errx(1, "conflicting NAT-T options"); - if (strlcpy(env->sc_conffile, conffile, MAXPATHLEN) >= MAXPATHLEN) - errx(1, "config file exceeds MAXPATHLEN"); + if (strlcpy(env->sc_conffile, conffile, PATH_MAX) >= PATH_MAX) + errx(1, "config file exceeds PATH_MAX"); ca_sslinit(); policy_init(env); @@ -180,16 +139,19 @@ main(int argc, char *argv[]) /* Configure the control socket */ ps->ps_csock.cs_name = IKED_SOCKET; - log_init(debug); + log_init(debug, LOG_DAEMON); log_verbose(verbose); if (!debug && daemon(0, 0) == -1) err(1, "failed to daemonize"); group_init(); + + ps->ps_ninstances = 1; proc_init(ps, procs, nitems(procs)); setproctitle("parent"); + log_procinit("parent"); event_init(); @@ -198,14 +160,16 @@ main(int argc, char *argv[]) signal_set(&ps->ps_evsigchld, SIGCHLD, parent_sig_handler, ps); signal_set(&ps->ps_evsighup, SIGHUP, parent_sig_handler, ps); signal_set(&ps->ps_evsigpipe, SIGPIPE, parent_sig_handler, ps); + signal_set(&ps->ps_evsigusr1, SIGUSR1, parent_sig_handler, ps); signal_add(&ps->ps_evsigint, NULL); signal_add(&ps->ps_evsigterm, NULL); signal_add(&ps->ps_evsigchld, NULL); signal_add(&ps->ps_evsighup, NULL); signal_add(&ps->ps_evsigpipe, NULL); + signal_add(&ps->ps_evsigusr1, NULL); - proc_config(ps, procs, nitems(procs)); + proc_listen(ps, procs, nitems(procs)); if (parent_configure(env) == -1) fatalx("configuration failed"); @@ -221,9 +185,6 @@ int parent_configure(struct iked *env) { struct sockaddr_storage ss; -#if defined(HAVE_APPLE_NATT) - int udpencap; -#endif if (parse_config(env->sc_conffile, env) == -1) { proc_kill(&env->sc_ps); @@ -240,20 +201,11 @@ parent_configure(struct iked *env) config_setpfkey(env, PROC_IKEV2); /* Now compile the policies and calculate skip steps */ - config_setcompile(env, PROC_IKEV1); config_setcompile(env, PROC_IKEV2); bzero(&ss, sizeof(ss)); ss.ss_family = AF_INET; - -#if defined(HAVE_APPLE_NATT) - if ((env->sc_opts & IKED_OPT_NONATT) == 0) { - udpencap = IKED_NATT_PORT; - if (sysctlbyname("net.inet.ipsec.esp_port", NULL, NULL, - &udpencap, sizeof(udpencap)) != 0) - fatalx("failed to set NAT-T port"); - } -#endif + SET_SS_LEN(&ss, sizeof(struct sockaddr_in)); if ((env->sc_opts & IKED_OPT_NATT) == 0) config_setsocket(env, &ss, ntohs(IKED_IKE_PORT), PROC_IKEV2); @@ -262,14 +214,34 @@ parent_configure(struct iked *env) bzero(&ss, sizeof(ss)); ss.ss_family = AF_INET6; + SET_SS_LEN(&ss, sizeof(struct sockaddr_in6)); if ((env->sc_opts & IKED_OPT_NATT) == 0) config_setsocket(env, &ss, ntohs(IKED_IKE_PORT), PROC_IKEV2); if ((env->sc_opts & IKED_OPT_NONATT) == 0) config_setsocket(env, &ss, ntohs(IKED_NATT_PORT), PROC_IKEV2); + /* + * pledge in the parent process: + * It has to run fairly late to allow forking the processes and + * opening the PFKEY socket and the listening UDP sockets (once) + * that need the bypass ioctls that are never allowed by pledge. + * + * Other flags: + * stdio - for malloc and basic I/O including events. + * rpath - for reload to open and read the configuration files. + * proc - run kill to terminate its children safely. + * dns - for reload and ocsp connect. + * inet - for ocsp connect. + * route - for using interfaces in iked.conf (SIOCGIFGMEMB) + * sendfd - for ocsp sockets. + */ + if (pledge("stdio rpath proc dns inet route sendfd", NULL) == -1) + fatal("pledge"); + config_setcoupled(env, env->sc_decoupled ? 0 : 1); config_setmode(env, env->sc_passive ? 1 : 0); + config_setocsp(env); return (0); } @@ -284,7 +256,6 @@ parent_reload(struct iked *env, int reset, const char *filename) 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); @@ -294,13 +265,12 @@ parent_reload(struct iked *env, int reset, const char *filename) } /* Re-compile policies and skip steps */ - config_setcompile(env, PROC_IKEV1); config_setcompile(env, PROC_IKEV2); config_setcoupled(env, env->sc_decoupled ? 0 : 1); config_setmode(env, env->sc_passive ? 1 : 0); + config_setocsp(env); } else { - config_setreset(env, reset, PROC_IKEV1); config_setreset(env, reset, PROC_IKEV2); config_setreset(env, reset, PROC_CERT); } @@ -327,12 +297,17 @@ parent_sig_handler(int sig, short event, void *arg) case SIGPIPE: log_info("%s: ignoring SIGPIPE", __func__); break; + case SIGUSR1: + log_info("%s: ignoring SIGUSR1", __func__); + break; case SIGTERM: case SIGINT: die = 1; /* FALLTHROUGH */ case SIGCHLD: do { + int len; + pid = waitpid(-1, &status, WNOHANG); if (pid <= 0) continue; @@ -340,17 +315,21 @@ parent_sig_handler(int sig, short event, void *arg) fail = 0; if (WIFSIGNALED(status)) { fail = 1; - asprintf(&cause, "terminated; signal %d", + len = asprintf(&cause, "terminated; signal %d", WTERMSIG(status)); } else if (WIFEXITED(status)) { if (WEXITSTATUS(status) != 0) { fail = 1; - asprintf(&cause, "exited abnormally"); + len = asprintf(&cause, + "exited abnormally"); } else - asprintf(&cause, "exited okay"); + len = asprintf(&cause, "exited okay"); } else fatalx("unexpected cause of SIGCHLD"); + if (len == -1) + fatal("asprintf"); + die = 1; for (id = 0; id < PROC_MAX; id++) @@ -373,34 +352,28 @@ parent_sig_handler(int sig, short event, void *arg) } int -parent_dispatch_ikev1(int fd, struct privsep_proc *p, struct imsg *imsg) +parent_dispatch_ca(int fd, struct privsep_proc *p, struct imsg *imsg) { - switch (imsg->hdr.type) { - default: - break; - } - - return (-1); -} + struct iked *env = p->p_ps->ps_env; -int -parent_dispatch_ikev2(int fd, struct privsep_proc *p, struct imsg *imsg) -{ switch (imsg->hdr.type) { - default: + case IMSG_OCSP_FD: + ocsp_connect(env); break; + default: + return (-1); } - return (-1); + return (0); } int -parent_dispatch_ca(int fd, struct privsep_proc *p, struct imsg *imsg) +parent_dispatch_control(int fd, struct privsep_proc *p, struct imsg *imsg) { struct iked *env = p->p_ps->ps_env; int v; char *str = NULL; - u_int type = imsg->hdr.type; + unsigned int type = imsg->hdr.type; switch (type) { case IMSG_CTL_RESET: @@ -412,15 +385,13 @@ parent_dispatch_ca(int fd, struct privsep_proc *p, struct imsg *imsg) case IMSG_CTL_DECOUPLE: case IMSG_CTL_ACTIVE: case IMSG_CTL_PASSIVE: - proc_compose_imsg(env, PROC_IKEV1, type, -1, NULL, 0); - proc_compose_imsg(env, PROC_IKEV2, type, -1, NULL, 0); + proc_compose(&env->sc_ps, PROC_IKEV2, type, NULL, 0); break; 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); + free(str); break; default: return (-1); diff --git a/iked/iked.conf.5 b/iked/iked.conf.5 index c915ee0..d98ced3 100644 --- a/iked/iked.conf.5 +++ b/iked/iked.conf.5 @@ -1,6 +1,6 @@ -.\" $OpenBSD: iked.conf.5,v 1.23 2013/03/05 19:16:01 sobrado Exp $ +.\" $OpenBSD: iked.conf.5,v 1.44 2015/12/09 21:41:49 naddy Exp $ .\" -.\" Copyright (c) 2010-2013 Reyk Floeter +.\" Copyright (c) 2010 - 2014 Reyk Floeter .\" Copyright (c) 2004 Mathieu Sauve-Frankel All rights reserved. .\" .\" Permission to use, copy, modify, and distribute this software for any @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: March 5 2013 $ +.Dd $Mdocdate: December 9 2015 $ .Dt IKED.CONF 5 .Os .Sh NAME @@ -136,6 +136,13 @@ This is the default. .It Ic set decouple Don't load the negotiated SAs and flows from the kernel. This mode is only useful for testing and debugging. +.It Ic set ocsp Ar URL +Enable OCSP and set the URL of the OCSP responder. +Please note that the matching responder and issuer certificates +have to be placed in +.Pa /etc/iked/ocsp/responder.crt +and +.Pa /etc/iked/ocsp/issuer.crt . .It Ic user Ar name Ar password .Xr iked 8 supports user-based authentication by tunneling the Extensible @@ -239,6 +246,10 @@ is specified, negotiation will be started at once. If omitted, .Ar passive mode will be used. +.It Ic ipcomp +Enable optional support for +.Xr ipcomp 4 , +the IP Payload Compression protocol. .It Op Ar encap .Ar encap specifies the encapsulation protocol to be used. @@ -386,7 +397,7 @@ If omitted, 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) +option will only be used to enable Perfect Forward Secrecy (PFS) for additional Child SAs exchanges that are not part of the initial key exchange. .It Ic srcid Ar string Ic dstid Ar string @@ -424,6 +435,14 @@ is similar to .Ic srcid , but instead specifies the ID to be used by the remote peer. +.It Ic ikelifetime Ar time +The optional +.Ic ikelifetime +parameter defines the IKE SA expiration timeout by the +.Ar time +SA was created. +A zero value disables active IKE SA rekeying. +This is the default. .It Ic lifetime Ar time Op Ic bytes Ar bytes The optional .Ic lifetime @@ -473,6 +492,33 @@ or hex value (starting with 0x) for authentication. Use RSA public key authentication. This is the default mode if no option is specified. .El +.It Ic config Ar option address +Send one or more optional configuration payloads (CP) to the peer. +The configuration +.Ar option +can be one of the following with the expected address format: +.Pp +.Bl -tag -width Ds -compact -offset indent +.It Ic address Ar address +Assign a static address on the internal network. +.It Ic address Ar address/prefix +Assign a dynamic address on the internal network. +The address will be assigned from an address pool with the size specified by +.Ar prefix . +.It Ic netmask Ar netmask +The IPv4 netmask of the internal network. +.It Ic name-server Ar address +The DNS server address within the internal network. +.It Ic netbios-server Ar address +The NetBIOS name server (WINS) within the internal network. +This option is provided for compatibility with legacy clients. +.It Ic dhcp-server Ar address +The address of an internal DHCP server for further configuration. +.It Ic protected-subnet Ar address/prefix +The address of the protected subnet within the internal network. +.It Ic access-server Ar address +The address of an internal remote access server. +.El .It Ic tag Ar string Add a .Xr pf 4 @@ -553,7 +599,7 @@ Interface for IKE traffic and encapsulated IPsec traffic. IKE traffic on the external interface. .It proto udp port 4500 IKE NAT-Traversal traffic on the external interface. -.It proto ah \*(Ba esp +.It proto ah | esp Encapsulated IPsec traffic on the external interface. .It enc0 @@ -624,17 +670,18 @@ The following 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) +queue std on ix0 bandwidth 100M +queue deflt parent std bandwidth 10M default +queue developers parent std bandwidth 75M +queue employees parent std bandwidth 5M +queue ipsec parent std bandwidth 10M -pass out on ix0 proto esp queue ipsec +pass out on ix0 proto esp set queue ipsec -pass out on ix0 tagged ipsec-developers.example.com queue developers -pass out on ix0 tagged ipsec-employees.example.com queue employees +pass out on ix0 tagged ipsec-developers.example.com \e + set queue developers +pass out on ix0 tagged ipsec-employees.example.com \e + set queue employees .Ed .Pp The following example assigns the tags in the @@ -673,7 +720,8 @@ a relevant NAT rule is required in 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 +match out on enc0 from 192.168.1.0/24 to 192.168.2.0/24 \e + nat-to 10.10.10.1 .Ed .Pp From the peer's point of view, @@ -695,8 +743,8 @@ keyword: The following pseudo-random function types are permitted with the .Ic prf keyword: -.Bl -column "Authentication" "Key Length" "[IKE only]" -offset indent -.It Em "Authentication" Ta Em "Key Length" Ta "" +.Bl -column "hmac-sha2-512" "Key Length" "[IKE only]" -offset indent +.It Em "PRF" Ta Em "Key Length" Ta "" .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]" @@ -707,9 +755,8 @@ keyword: The following cipher types are permitted with the .Ic enc keyword: -.Bl -column "aes-128-gmac" "Key Length" "[ESP only]" -offset indent +.Bl -column "chacha20-poly1305" "Key Length" "[ESP only]" -offset indent .It Em "Cipher" Ta Em "Key Length" Ta "" -.It Li des Ta "56 bits" Ta "[ESP only]" .It Li 3des Ta "168 bits" Ta "" .It Li aes-128 Ta "128 bits" Ta "" .It Li aes-192 Ta "192 bits" Ta "" @@ -720,28 +767,31 @@ keyword: .It Li aes-128-gcm Ta "160 bits" Ta "[ESP only]" .It Li aes-192-gcm Ta "224 bits" Ta "[ESP only]" .It Li aes-256-gcm Ta "288 bits" Ta "[ESP only]" +.It Li blowfish Ta "160 bits" Ta "[ESP only]" +.It Li cast Ta "128 bits" Ta "[ESP only]" +.It Li chacha20-poly1305 Ta "288 bits" Ta "[ESP only]" +.El +.Pp +The following cipher types provide only authentication, +not encryption: +.Bl -column "chacha20-poly1305" "Key Length" "[ESP only]" -offset indent .It Li aes-128-gmac Ta "160 bits" Ta "[ESP only]" .It Li aes-192-gmac Ta "224 bits" Ta "[ESP only]" .It Li aes-256-gmac Ta "288 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. +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. -The same applies to AES-GCM and AES-GMAC. +The same applies to AES-GCM, AES-GMAC and Chacha20-Poly1305, +however in the latter case the keysize is 256 bit. .Pp Using AES-GMAC or NULL with ESP will only provide authentication. -This is useful in setups where AH can not be used, e.g. when NAT is involved. +This is useful in setups where AH cannot be used, e.g. when NAT is involved. .Pp The following group types are permitted with the .Ic group @@ -766,17 +816,22 @@ keyword: .It Li modp2048-256 Ta grp24 Ta 2048 Ta "MODP, 256 bit Prime Order Subgroup" .It Li ecp192 Ta grp25 Ta 192 Ta "ECP" .It Li ecp224 Ta grp26 Ta 224 Ta "ECP" +.It Li brainpool224 Ta grp27 Ta 224 Ta "ECP, brainpoolP224r1" +.It Li brainpool256 Ta grp28 Ta 256 Ta "ECP, brainpoolP256r1" +.It Li brainpool384 Ta grp29 Ta 384 Ta "ECP, brainpoolP384r1" +.It Li brainpool512 Ta grp30 Ta 512 Ta "ECP, brainpoolP512r1" +.It Li curve25519 Ta - Ta 256 Ta "Curve25519" .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). +ECP (elliptic curve groups modulo a prime), +or the non-standard Curve25519. 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 +The first example is intended for a server with 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 @@ -846,4 +901,4 @@ file format first appeared in The .Xr iked 8 program was written by -.An Reyk Floeter Aq reyk@openbsd.org . +.An Reyk Floeter Aq Mt reyk@openbsd.org . diff --git a/iked/iked.h b/iked/iked.h index 0430274..9a4eb08 100644 --- a/iked/iked.h +++ b/iked/iked.h @@ -1,4 +1,4 @@ -/* $OpenBSD: iked.h,v 1.56 2013/01/08 10:38:19 reyk Exp $ */ +/* $OpenBSD: iked.h,v 1.96 2016/06/01 11:16:41 patrick Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter @@ -17,29 +17,30 @@ */ #include -#include "openbsd-compat/sys-tree.h" -#include "openbsd-compat/sys-queue.h" + + +#include #include #include "types.h" #include "dh.h" -#ifndef _IKED_H -#define _IKED_H +#ifndef IKED_H +#define IKED_H /* * Common IKEv1/IKEv2 header */ struct ike_header { - u_int64_t ike_ispi; /* Initiator cookie */ - u_int64_t ike_rspi; /* Responder cookie */ - 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 */ + uint64_t ike_ispi; /* Initiator cookie */ + uint64_t ike_rspi; /* Responder cookie */ + uint8_t ike_nextpayload; /* Next payload type */ + uint8_t ike_version; /* Major/Minor version number */ + uint8_t ike_exchange; /* Exchange type */ + uint8_t ike_flags; /* Message options */ + uint32_t ike_msgid; /* Message identifier */ + uint32_t ike_length; /* Total message length */ } __packed; /* @@ -50,6 +51,7 @@ struct imsgev { struct imsgbuf ibuf; void (*handler)(int, short, void *); struct event ev; + struct privsep_proc *proc; void *data; short events; const char *name; @@ -79,11 +81,14 @@ struct control_sock { int cs_fd; int cs_restricted; void *cs_env; + + TAILQ_ENTRY(control_sock) cs_entry; }; +TAILQ_HEAD(control_socks, control_sock); struct ctl_conn { TAILQ_ENTRY(ctl_conn) entry; - u_int8_t flags; + uint8_t flags; #define CTL_CONN_NOTIFY 0x01 struct imsgev iev; }; @@ -104,20 +109,20 @@ struct iked_timer { }; struct iked_spi { - u_int64_t spi; - u_int8_t spi_size; - u_int8_t spi_protoid; + uint64_t spi; + uint8_t spi_size; + uint8_t spi_protoid; }; struct iked_proposal { - u_int8_t prop_id; - u_int8_t prop_protoid; + uint8_t prop_id; + uint8_t prop_protoid; struct iked_spi prop_localspi; struct iked_spi prop_peerspi; struct iked_transform *prop_xforms; - u_int prop_nxforms; + unsigned int prop_nxforms; TAILQ_ENTRY(iked_proposal) prop_entry; }; @@ -126,7 +131,7 @@ TAILQ_HEAD(iked_proposals, iked_proposal); struct iked_addr { int addr_af; struct sockaddr_storage addr; - u_int8_t addr_mask; + uint8_t addr_mask; int addr_net; in_port_t addr_port; }; @@ -134,17 +139,14 @@ struct iked_addr { struct iked_flow { struct iked_addr flow_src; struct iked_addr flow_dst; - u_int flow_dir; /* in/out */ - - u_int flow_loaded; /* pfkey done */ + unsigned int flow_dir; /* in/out */ - u_int8_t flow_saproto; - u_int8_t flow_ipproto; - u_int8_t flow_type; - u_int32_t flow_id; + unsigned int flow_loaded; /* pfkey done */ - struct iked_id *flow_srcid; - struct iked_id *flow_dstid; + uint8_t flow_saproto; + uint8_t flow_ipproto; + uint8_t flow_type; + uint32_t flow_id; struct iked_addr *flow_local; /* outer source */ struct iked_addr *flow_peer; /* outer dest */ @@ -157,26 +159,24 @@ RB_HEAD(iked_flows, iked_flow); TAILQ_HEAD(iked_saflows, iked_flow); struct iked_childsa { - u_int8_t csa_saproto; /* IPSec protocol */ - u_int csa_dir; /* in/out */ + uint8_t csa_saproto; /* IPSec protocol */ + unsigned int csa_dir; /* in/out */ - u_int64_t csa_peerspi; /* peer relation */ - u_int8_t csa_loaded; /* pfkey done */ - u_int8_t csa_rekey; /* will be deleted */ - u_int8_t csa_allocated; /* from the kernel */ - u_int8_t csa_persistent;/* do not rekey */ - u_int8_t csa_esn; /* use ESN */ + uint64_t csa_peerspi; /* peer relation */ + uint8_t csa_loaded; /* pfkey done */ + uint8_t csa_rekey; /* will be deleted */ + uint8_t csa_allocated; /* from the kernel */ + uint8_t csa_persistent;/* do not rekey */ + uint8_t csa_esn; /* use ESN */ + uint8_t csa_transport; /* transport mode */ struct iked_spi csa_spi; struct ibuf *csa_encrkey; /* encryption key */ - struct iked_transform *csa_encrxf; /* encryption xform */ + uint16_t csa_encrid; /* encryption xform id */ struct ibuf *csa_integrkey; /* auth key */ - struct iked_transform *csa_integrxf; /* auth xform */ - - struct iked_id *csa_srcid; - struct iked_id *csa_dstid; + uint16_t csa_integrid; /* auth xform id */ struct iked_addr *csa_local; /* outer source */ struct iked_addr *csa_peer; /* outer dest */ @@ -184,6 +184,9 @@ struct iked_childsa { struct iked_childsa *csa_peersa; /* peer */ + struct iked_childsa *csa_parent; /* IPCOMP parent */ + unsigned int csa_children; /* IPCOMP children */ + RB_ENTRY(iked_childsa) csa_node; TAILQ_ENTRY(iked_childsa) csa_entry; }; @@ -192,36 +195,36 @@ TAILQ_HEAD(iked_childsas, iked_childsa); struct iked_static_id { - u_int8_t id_type; - u_int8_t id_length; - u_int8_t id_offset; - u_int8_t id_data[IKED_ID_SIZE]; + uint8_t id_type; + uint8_t id_length; + uint8_t id_offset; + uint8_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]; + uint8_t auth_method; + uint8_t auth_eap; /* optional EAP */ + uint8_t auth_length; /* zero if EAP */ + uint8_t auth_data[IKED_PSK_SIZE]; }; struct iked_cfg { - u_int8_t cfg_action; - u_int16_t cfg_type; + uint8_t cfg_action; + uint16_t cfg_type; union { struct iked_addr address; } cfg; }; -RB_HEAD(iked_sapeers, iked_sa); +TAILQ_HEAD(iked_sapeers, iked_sa); struct iked_lifetime { - u_int64_t lt_bytes; - u_int64_t lt_seconds; + uint64_t lt_bytes; + uint64_t lt_seconds; }; struct iked_policy { - u_int pol_id; + unsigned int pol_id; char pol_name[IKED_ID_SIZE]; #define IKED_SKIP_FLAGS 0 @@ -232,19 +235,23 @@ struct iked_policy { #define IKED_SKIP_COUNT 5 struct iked_policy *pol_skip[IKED_SKIP_COUNT]; - u_int8_t pol_flags; + uint8_t pol_flags; #define IKED_POLICY_PASSIVE 0x00 #define IKED_POLICY_DEFAULT 0x01 #define IKED_POLICY_ACTIVE 0x02 #define IKED_POLICY_REFCNT 0x04 #define IKED_POLICY_QUICK 0x08 #define IKED_POLICY_SKIP 0x10 +#define IKED_POLICY_IPCOMP 0x20 +#define IKED_POLICY_TRANSPORT 0x40 int pol_refcnt; + uint8_t pol_certreqtype; + int pol_af; - u_int8_t pol_saproto; - u_int pol_ipproto; + uint8_t pol_saproto; + unsigned int pol_ipproto; struct iked_addr pol_peer; struct group *pol_peerdh; @@ -256,7 +263,7 @@ struct iked_policy { struct iked_auth pol_auth; char pol_tag[IKED_TAG_SIZE]; - u_int pol_tap; + unsigned int pol_tap; struct iked_proposals pol_proposals; size_t pol_nproposals; @@ -265,9 +272,10 @@ struct iked_policy { size_t pol_nflows; struct iked_cfg pol_cfg[IKED_CFG_MAX]; - u_int pol_ncfg; + unsigned int pol_ncfg; - struct iked_lifetime pol_lifetime; + uint32_t pol_rekey; /* ike SA lifetime */ + struct iked_lifetime pol_lifetime; /* child SA lifetime */ struct iked_sapeers pol_sapeers; @@ -276,8 +284,8 @@ struct iked_policy { TAILQ_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 */ + uint8_t hash_type; /* PRF or INTEGR */ + uint16_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 */ @@ -288,8 +296,8 @@ struct iked_hash { }; struct iked_cipher { - u_int8_t encr_type; /* ENCR */ - u_int16_t encr_id; /* IKE ENCR hash id */ + uint8_t encr_type; /* ENCR */ + uint16_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 */ @@ -300,7 +308,7 @@ struct iked_cipher { }; struct iked_dsa { - u_int8_t dsa_method; /* AUTH method */ + uint8_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 */ @@ -311,41 +319,54 @@ struct iked_dsa { }; struct iked_id { - u_int8_t id_type; - u_int8_t id_offset; + uint8_t id_type; + uint8_t id_offset; 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_INF 0x20 /* Informational exchange initiated */ -#define IKED_REQ_DELETE 0x40 /* Rekeying continuation */ +#define IKED_REQ_CERT 0x0001 /* get local certificate (if required) */ +#define IKED_REQ_CERTVALID 0x0002 /* validated the peer cert */ +#define IKED_REQ_CERTREQ 0x0004 /* CERTREQ has been received */ +#define IKED_REQ_AUTH 0x0008 /* AUTH payload */ +#define IKED_REQ_AUTHVALID 0x0010 /* AUTH payload has been verified */ +#define IKED_REQ_SA 0x0020 /* SA available */ +#define IKED_REQ_EAPVALID 0x0040 /* EAP payload has been verified */ +#define IKED_REQ_CHILDSA 0x0080 /* Child SA initiated */ +#define IKED_REQ_INF 0x0100 /* Informational exchange initiated */ #define IKED_REQ_BITS \ - "\10\01CERT\02VALID\03AUTH\04SA" + "\20\01CERT\02CERTVALID\03CERTREQ\04AUTH\05AUTHVALID\06SA\07EAPVALID" \ + "\10CHILDSA\11INF" TAILQ_HEAD(iked_msgqueue, iked_message); struct iked_sahdr { - u_int64_t sh_ispi; /* Initiator SPI */ - u_int64_t sh_rspi; /* Responder SPI */ - u_int sh_initiator; /* Is initiator? */ + uint64_t sh_ispi; /* Initiator SPI */ + uint64_t sh_rspi; /* Responder SPI */ + unsigned int sh_initiator; /* Is initiator? */ } __packed; +struct iked_kex { + struct ibuf *kex_inonce; /* Ni */ + struct ibuf *kex_rnonce; /* Nr */ + + struct group *kex_dhgroup; /* DH group */ + struct ibuf *kex_dhiexchange; + struct ibuf *kex_dhrexchange; + struct ibuf *kex_dhpeer; /* pointer to i or r */ +}; + struct iked_sa { struct iked_sahdr sa_hdr; - u_int32_t sa_msgid; - u_int32_t sa_reqid; + uint32_t sa_msgid; /* Last request rcvd */ + int sa_msgid_set; /* msgid initialized */ + uint32_t sa_reqid; /* Next request sent */ int sa_type; #define IKED_SATYPE_LOOKUP 0 /* Used for lookup */ #define IKED_SATYPE_LOCAL 1 /* Local SA */ struct iked_addr sa_peer; - struct iked_addr sa_polpeer; struct iked_addr sa_local; int sa_fd; @@ -353,9 +374,9 @@ struct iked_sa { int sa_udpencap; /* for pfkey */ int sa_state; - u_int sa_stateflags; - u_int sa_stateinit; /* SA_INIT */ - u_int sa_statevalid; /* IKE_AUTH */ + unsigned int sa_stateflags; + unsigned int sa_stateinit; /* SA_INIT */ + unsigned int sa_statevalid; /* IKE_AUTH */ int sa_cp; /* XXX */ @@ -365,13 +386,14 @@ struct iked_sa { 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 ibuf *sa_dhpeer; /* pointer to i or r */ + struct iked_kex sa_kex; +/* XXX compat defines until everything is converted */ +#define sa_inonce sa_kex.kex_inonce +#define sa_rnonce sa_kex.kex_rnonce +#define sa_dhgroup sa_kex.kex_dhgroup +#define sa_dhiexchange sa_kex.kex_dhiexchange +#define sa_dhrexchange sa_kex.kex_dhrexchange +#define sa_dhpeer sa_kex.kex_dhpeer struct iked_hash *sa_prf; /* PRF alg */ struct iked_hash *sa_integr; /* integrity alg */ @@ -388,11 +410,14 @@ struct iked_sa { struct ibuf *sa_1stmsg; /* for initiator AUTH */ struct ibuf *sa_2ndmsg; /* for responder AUTH */ struct iked_id sa_localauth; /* local AUTH message */ + int sa_sigsha2; /* use SHA2 for signatures */ 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 */ +#define IKESA_SRCID(x) ((x)->sa_hdr.sh_initiator ? &(x)->sa_iid : &(x)->sa_rid) +#define IKESA_DSTID(x) ((x)->sa_hdr.sh_initiator ? &(x)->sa_rid : &(x)->sa_iid) char *sa_eapid; /* EAP identity */ struct iked_id sa_eap; /* EAP challenge */ @@ -402,8 +427,19 @@ struct iked_sa { struct iked_childsas sa_childsas; /* IPSec Child SAs */ struct iked_saflows sa_flows; /* IPSec flows */ + struct iked_sa *sa_next; /* IKE SA rekeying */ + uint64_t sa_rekeyspi; /* peerspi for rekey*/ + + uint8_t sa_transport; /* Transport mode */ + uint8_t sa_ipcomp; /* IPcomp transform */ + uint16_t sa_cpi_out; /* IPcomp outgoing */ + uint16_t sa_cpi_in; /* IPcomp incoming*/ + struct iked_timer sa_timer; /* SA timeouts */ -#define IKED_IKE_SA_REKEY_TIMEOUT 300 /* 5 minutes */ +#define IKED_IKE_SA_DELETE_TIMEOUT 300 /* 5 minutes */ +#define IKED_IKE_SA_ALIVE_TIMEOUT 60 /* 1 minute */ + + struct iked_timer sa_rekey; /* rekey timeout */ struct iked_msgqueue sa_requests; /* request queue */ #define IKED_RETRANSMIT_TIMEOUT 2 /* 2 seconds */ @@ -411,14 +447,22 @@ struct iked_sa { struct iked_msgqueue sa_responses; /* response queue */ #define IKED_RESPONSE_TIMEOUT 120 /* 2 minutes */ - RB_ENTRY(iked_sa) sa_peer_entry; + TAILQ_ENTRY(iked_sa) sa_peer_entry; RB_ENTRY(iked_sa) sa_entry; + + struct iked_addr *sa_addrpool; /* address from pool */ + RB_ENTRY(iked_sa) sa_addrpool_entry; /* pool entries */ + + struct iked_addr *sa_addrpool6; /* address from pool */ + RB_ENTRY(iked_sa) sa_addrpool6_entry; /* pool entries */ }; RB_HEAD(iked_sas, iked_sa); +RB_HEAD(iked_addrpool, iked_sa); +RB_HEAD(iked_addrpool6, iked_sa); struct iked_message { struct ibuf *msg_data; - off_t msg_offset; + size_t msg_offset; struct sockaddr_storage msg_local; socklen_t msg_locallen; @@ -430,6 +474,7 @@ struct iked_message { int msg_fd; int msg_response; + int msg_responded; int msg_natt; int msg_error; int msg_e; @@ -439,8 +484,8 @@ struct iked_message { struct iked_policy *msg_policy; struct iked_sa *msg_sa; - u_int32_t msg_msgid; - u_int8_t msg_exchange; + uint32_t msg_msgid; + uint8_t msg_exchange; /* Parsed information */ struct iked_proposals msg_proposals; @@ -453,7 +498,7 @@ struct iked_message { /* Parse stack */ struct iked_proposal *msg_prop; - u_int16_t msg_attrlength; + uint16_t msg_attrlength; /* Retransmit queue */ struct iked_timer msg_timer; @@ -464,20 +509,32 @@ struct iked_message { }; struct iked_user { - char usr_name[MAXLOGNAME]; + char usr_name[LOGIN_NAME_MAX]; char usr_pass[IKED_PASSWORD_SIZE]; RB_ENTRY(iked_user) usr_entry; }; RB_HEAD(iked_users, iked_user); +struct privsep_pipes { + int *pp_pipes[PROC_MAX]; +}; + struct privsep { - int ps_pipes[PROC_MAX][PROC_MAX]; - struct imsgev ps_ievs[PROC_MAX]; + struct privsep_pipes *ps_pipes[PROC_MAX]; + struct privsep_pipes *ps_pp; + + struct imsgev *ps_ievs[PROC_MAX]; const char *ps_title[PROC_MAX]; pid_t ps_pid[PROC_MAX]; struct passwd *ps_pw; + int ps_noaction; struct control_sock ps_csock; + struct control_socks ps_rcsocks; + + unsigned int ps_instances[PROC_MAX]; + unsigned int ps_ninstances; + unsigned int ps_instance; /* Event and signal handlers */ struct event ps_evsigint; @@ -485,6 +542,7 @@ struct privsep { struct event ps_evsigchld; struct event ps_evsighup; struct event ps_evsigpipe; + struct event ps_evsigusr1; struct iked *ps_env; }; @@ -499,18 +557,26 @@ struct privsep_proc { const char *p_chroot; struct privsep *p_ps; struct iked *p_env; + void (*p_shutdown)(void); + unsigned int p_instance; }; +struct iked_ocsp_entry { + TAILQ_ENTRY(iked_ocsp_entry) ioe_entry; /* next request */ + void *ioe_ocsp; /* private ocsp request data */ +}; +TAILQ_HEAD(iked_ocsp_requests, iked_ocsp_entry); + /* * Daemon configuration */ struct iked { - char sc_conffile[MAXPATHLEN]; + char sc_conffile[PATH_MAX]; - u_int32_t sc_opts; - u_int8_t sc_passive; - u_int8_t sc_decoupled; + uint32_t sc_opts; + uint8_t sc_passive; + uint8_t sc_decoupled; struct iked_policies sc_policies; struct iked_policy *sc_defaultcon; @@ -524,7 +590,7 @@ struct iked { int sc_pfkey; /* ike process */ struct event sc_pfkeyev; - u_int8_t sc_certreqtype; + uint8_t sc_certreqtype; struct ibuf *sc_certreq; struct iked_socket *sc_sock4[2]; @@ -535,6 +601,12 @@ struct iked { #define IKED_INITIATOR_INTERVAL 60 struct privsep sc_ps; + + struct iked_ocsp_requests sc_ocsp; + char *sc_ocsp_url; + + struct iked_addrpool sc_addrpool; + struct iked_addrpool6 sc_addrpool6; }; struct iked_socket { @@ -548,6 +620,7 @@ struct iked_socket { void parent_reload(struct iked *, int, const char *); /* control.c */ +pid_t control(struct privsep *, struct privsep_proc *); int control_init(struct privsep *, struct control_sock *); int control_listen(struct control_sock *); void control_cleanup(struct control_sock *); @@ -555,30 +628,32 @@ void control_cleanup(struct control_sock *); /* config.c */ struct iked_policy * config_new_policy(struct iked *); +void config_free_kex(struct iked_kex *); 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 +uint64_t config_getspi(void); struct iked_transform * - config_findtransform(struct iked_proposals *, u_int8_t); + config_findtransform(struct iked_proposals *, uint8_t, unsigned int); 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); + config_add_proposal(struct iked_proposals *, unsigned int, + unsigned int); +void config_free_proposals(struct iked_proposals *, unsigned int); void config_free_flows(struct iked *, struct iked_flows *); 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_setcoupled(struct iked *, u_int); -int config_getcoupled(struct iked *, u_int); -int config_setmode(struct iked *, u_int); -int config_getmode(struct iked *, u_int); -int config_setreset(struct iked *, u_int, enum privsep_procid); + unsigned int, unsigned int, unsigned int, unsigned int); +int config_setcoupled(struct iked *, unsigned int); +int config_getcoupled(struct iked *, unsigned int); +int config_setmode(struct iked *, unsigned int); +int config_getmode(struct iked *, unsigned int); +int config_setreset(struct iked *, unsigned int, enum privsep_procid); int config_getreset(struct iked *, struct imsg *); int config_setpolicy(struct iked *, struct iked_policy *, enum privsep_procid); @@ -593,6 +668,8 @@ int config_setuser(struct iked *, struct iked_user *, enum privsep_procid); int config_getuser(struct iked *, struct imsg *); int config_setcompile(struct iked *, enum privsep_procid); int config_getcompile(struct iked *, struct imsg *); +int config_setocsp(struct iked *); +int config_getocsp(struct iked *, struct imsg *); /* policy.c */ void policy_init(struct iked *); @@ -603,34 +680,33 @@ void policy_calc_skip_steps(struct iked_policies *); 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); +void sa_stateflags(struct iked_sa *, unsigned int); int sa_stateok(struct iked_sa *, int); struct iked_sa * - sa_new(struct iked *, u_int64_t, u_int64_t, u_int, + sa_new(struct iked *, uint64_t, uint64_t, unsigned int, struct iked_policy *); void sa_free(struct iked *, struct iked_sa *); void sa_free_flows(struct iked *, struct iked_saflows *); int sa_address(struct iked_sa *, struct iked_addr *, - struct sockaddr_storage *, int); + struct sockaddr_storage *); void childsa_free(struct iked_childsa *); struct iked_childsa * - childsa_lookup(struct iked_sa *, u_int64_t, u_int8_t); + childsa_lookup(struct iked_sa *, uint64_t, uint8_t); void flow_free(struct iked_flow *); struct iked_sa * - sa_lookup(struct iked *, u_int64_t, u_int64_t, u_int); -struct iked_sa * - sa_peer_lookup(struct iked_policy *, struct sockaddr_storage *); + sa_lookup(struct iked *, uint64_t, uint64_t, unsigned int); struct iked_user * user_lookup(struct iked *, const char *); RB_PROTOTYPE(iked_sas, iked_sa, sa_entry, sa_cmp); -RB_PROTOTYPE(iked_sapeers, iked_sa, sa_peer_entry, sa_peer_cmp); +RB_PROTOTYPE(iked_addrpool, iked_sa, sa_addrpool_entry, sa_addrpool_cmp); +RB_PROTOTYPE(iked_addrpool6, iked_sa, sa_addrpool6_entry, sa_addrpool6_cmp); RB_PROTOTYPE(iked_users, iked_user, user_entry, user_cmp); RB_PROTOTYPE(iked_activesas, iked_childsa, csa_node, childsa_cmp); RB_PROTOTYPE(iked_flows, iked_flow, flow_node, flow_cmp); /* crypto.c */ struct iked_hash * - hash_new(u_int8_t, u_int16_t); + hash_new(uint8_t, uint16_t); struct ibuf * hash_setkey(struct iked_hash *, void *, size_t); void hash_free(struct iked_hash *); @@ -641,7 +717,7 @@ 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); + cipher_new(uint8_t, uint16_t, uint16_t); struct ibuf * cipher_setkey(struct iked_cipher *, void *, size_t); struct ibuf * @@ -658,55 +734,52 @@ 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); + dsa_new(uint16_t, struct iked_hash *, int); struct iked_dsa * - dsa_sign_new(u_int16_t, struct iked_hash *); + dsa_sign_new(uint16_t, struct iked_hash *); struct iked_dsa * - dsa_verify_new(u_int16_t, struct iked_hash *); + dsa_verify_new(uint16_t, struct iked_hash *); struct ibuf * - dsa_setkey(struct iked_dsa *, void *, size_t, u_int8_t); + dsa_setkey(struct iked_dsa *, void *, size_t, uint8_t); void dsa_free(struct iked_dsa *); -int dsa_init(struct iked_dsa *); +int dsa_init(struct iked_dsa *, const void *, size_t); 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 privsep *, struct privsep_proc *); - /* ikev2.c */ pid_t ikev2(struct privsep *, struct privsep_proc *); void ikev2_recv(struct iked *, struct iked_message *); void ikev2_init_ike_sa(struct iked *, void *); -int ikev2_sa_negotiate(struct iked_sa *, struct iked_proposals *, +int ikev2_sa_negotiate(struct iked_proposals *, struct iked_proposals *, struct iked_proposals *); int ikev2_policy2id(struct iked_static_id *, struct iked_id *, int); 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); + uint8_t, uint64_t, uint64_t *, int); 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 **); +ssize_t ikev2_psk(struct iked_sa *, uint8_t *, size_t, uint8_t **); ssize_t ikev2_nat_detection(struct iked *, struct iked_message *, - void *, size_t, u_int); + void *, size_t, unsigned int); int ikev2_send_informational(struct iked *, struct iked_message *); int ikev2_send_ike_e(struct iked *, struct iked_sa *, struct ibuf *, - u_int8_t, u_int8_t, int); + uint8_t, uint8_t, int); struct ike_header * ikev2_add_header(struct ibuf *, struct iked_sa *, - u_int32_t, u_int8_t, u_int8_t, u_int8_t); + uint32_t, uint8_t, uint8_t, uint8_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); -void ikev2_acquire_sa(struct iked *, struct iked_flow *); + uint8_t); +int ikev2_acquire_sa(struct iked *, struct iked_flow *); void ikev2_disable_rekeying(struct iked *, struct iked_sa *); -void ikev2_rekey_sa(struct iked *, struct iked_spi *); -void ikev2_drop_sa(struct iked *, struct iked_spi *); +int ikev2_rekey_sa(struct iked *, struct iked_spi *); +int ikev2_drop_sa(struct iked *, struct iked_spi *); int ikev2_print_id(struct iked_id *, char *, size_t); /* ikev2_msg.c */ @@ -718,19 +791,19 @@ struct ibuf * struct iked_message * ikev2_msg_copy(struct iked *, struct iked_message *); void ikev2_msg_cleanup(struct iked *, struct iked_message *); -u_int32_t +uint32_t ikev2_msg_id(struct iked *, struct iked_sa *); struct ibuf *ikev2_msg_auth(struct iked *, struct iked_sa *, int); int ikev2_msg_authsign(struct iked *, struct iked_sa *, struct iked_auth *, struct ibuf *); int ikev2_msg_authverify(struct iked *, struct iked_sa *, - struct iked_auth *, u_int8_t *, size_t, struct ibuf *); + struct iked_auth *, uint8_t *, size_t, struct ibuf *); int ikev2_msg_valid_ike_sa(struct iked *, struct ike_header *, struct iked_message *); int ikev2_msg_send(struct iked *, struct iked_message *); int ikev2_msg_send_encrypt(struct iked *, struct iked_sa *, - struct ibuf **, u_int8_t, u_int8_t, int); + struct ibuf **, uint8_t, uint8_t, int); struct ibuf *ikev2_msg_encrypt(struct iked *, struct iked_sa *, struct ibuf *); struct ibuf * @@ -753,7 +826,7 @@ struct iked_message * /* ikev2_pld.c */ int ikev2_pld_parse(struct iked *, struct ike_header *, - struct iked_message *, off_t); + struct iked_message *, size_t); /* eap.c */ ssize_t eap_identity_request(struct ibuf *); @@ -763,88 +836,100 @@ int eap_parse(struct iked *, struct iked_sa *, void *, int); int pfkey_couple(int, struct iked_sas *, int); int pfkey_flow_add(int fd, struct iked_flow *); int pfkey_flow_delete(int fd, struct iked_flow *); -int pfkey_block(int, int, u_int); -int pfkey_sa_init(int, struct iked_childsa *, u_int32_t *); +int pfkey_block(int, int, unsigned int); +int pfkey_sa_init(int, struct iked_childsa *, uint32_t *); int pfkey_sa_add(int, struct iked_childsa *, struct iked_childsa *); int pfkey_sa_delete(int, struct iked_childsa *); -int pfkey_flush(int, u_int); +int pfkey_sa_last_used(int, struct iked_childsa *, uint64_t *); +int pfkey_flush(int); int pfkey_socket(void); void pfkey_init(struct iked *, int fd); /* ca.c */ pid_t caproc(struct privsep *, struct privsep_proc *); -int ca_setreq(struct iked *, struct iked_sahdr *, struct iked_static_id *, - u_int8_t, u_int8_t *, size_t, enum privsep_procid); +int ca_setreq(struct iked *, struct iked_sa *, struct iked_static_id *, + uint8_t, uint8_t *, size_t, enum privsep_procid); int ca_setcert(struct iked *, struct iked_sahdr *, struct iked_id *, - u_int8_t, u_int8_t *, size_t, enum privsep_procid); + uint8_t, uint8_t *, size_t, enum privsep_procid); int ca_setauth(struct iked *, struct iked_sa *, struct ibuf *, enum privsep_procid); void ca_sslinit(void); -void ca_sslerror(void); -char *ca_asn1_name(u_int8_t *, size_t); +void ca_sslerror(const char *); +char *ca_asn1_name(uint8_t *, size_t); char *ca_x509_name(void *); +void *ca_x509_name_parse(char *); /* timer.c */ -void timer_initialize(struct iked *, struct iked_timer *, +void timer_set(struct iked *, struct iked_timer *, void (*)(struct iked *, void *), void *); -int timer_initialized(struct iked *, struct iked_timer *); -void timer_register(struct iked *, struct iked_timer *, int); -void timer_deregister(struct iked *, struct iked_timer *); +void timer_add(struct iked *, struct iked_timer *, int); +void timer_del(struct iked *, struct iked_timer *); /* proc.c */ -void proc_init(struct privsep *, struct privsep_proc *, u_int); +void proc_init(struct privsep *, struct privsep_proc *, unsigned int); void proc_kill(struct privsep *); -void proc_config(struct privsep *, struct privsep_proc *, u_int); +void proc_listen(struct privsep *, struct privsep_proc *, size_t); void proc_dispatch(int, short event, void *); pid_t proc_run(struct privsep *, struct privsep_proc *, - struct privsep_proc *, u_int, - void (*)(struct privsep *, void *), void *); + struct privsep_proc *, unsigned int, + void (*)(struct privsep *, struct privsep_proc *, void *), void *); 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, +int imsg_compose_event(struct imsgev *, uint16_t, uint32_t, + pid_t, int, void *, uint16_t); +int imsg_composev_event(struct imsgev *, uint16_t, uint32_t, pid_t, int, const struct iovec *, int); -int proc_compose_imsg(struct iked *, enum privsep_procid, - u_int16_t, int, void *, u_int16_t); -int proc_composev_imsg(struct iked *, enum privsep_procid, - u_int16_t, int, const struct iovec *, int); -int proc_forward_imsg(struct iked *, struct imsg *, - enum privsep_procid); -void proc_flush_imsg(struct iked *, enum privsep_procid); +int proc_compose_imsg(struct privsep *, enum privsep_procid, int, + u_int16_t, u_int32_t, int, void *, u_int16_t); +int proc_compose(struct privsep *, enum privsep_procid, + uint16_t, void *, uint16_t); +int proc_composev_imsg(struct privsep *, enum privsep_procid, int, + u_int16_t, u_int32_t, int, const struct iovec *, int); +int proc_composev(struct privsep *, enum privsep_procid, + uint16_t, const struct iovec *, int); +int proc_forward_imsg(struct privsep *, struct imsg *, + enum privsep_procid, int); +struct imsgbuf * + proc_ibuf(struct privsep *, enum privsep_procid, int); +struct imsgev * + proc_iev(struct privsep *, enum privsep_procid, int); /* util.c */ -void socket_set_blockmode(int, enum blockmodes); int socket_af(struct sockaddr *, in_port_t); in_port_t - socket_getport(struct sockaddr_storage *); + socket_getport(struct sockaddr *); +int socket_setport(struct sockaddr *, in_port_t); int socket_getaddr(int, 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); + print_spi(uint64_t, int); const char * - print_map(u_int, struct iked_constmap *); + print_map(unsigned int, struct iked_constmap *); void lc_string(char *); -void print_hex(u_int8_t *, off_t, size_t); -void print_hexval(u_int8_t *, off_t, size_t); +void print_hex(uint8_t *, off_t, size_t); +void print_hexval(uint8_t *, off_t, size_t); const char * - print_bits(u_short, char *); + print_bits(unsigned short, unsigned char *); int sockaddr_cmp(struct sockaddr *, struct sockaddr *, int); -u_int8_t mask2prefixlen(struct sockaddr *); -u_int8_t mask2prefixlen6(struct sockaddr *); +uint8_t mask2prefixlen(struct sockaddr *); +uint8_t mask2prefixlen6(struct sockaddr *); struct in6_addr * - prefixlen2mask6(u_int8_t, u_int32_t *); -u_int32_t - prefixlen2mask(u_int8_t); + prefixlen2mask6(uint8_t, uint32_t *); +uint32_t + prefixlen2mask(uint8_t); const char * - print_host(struct sockaddr_storage *, char *, size_t); -char *get_string(u_int8_t *, size_t); + print_host(struct sockaddr *, char *, size_t); +char *get_string(uint8_t *, size_t); const char * - print_proto(u_int8_t); + print_proto(uint8_t); int expand_string(char *, size_t, const char *, const char *); -u_int8_t *string2unicode(const char *, size_t *); +uint8_t *string2unicode(const char *, size_t *); +void print_debug(const char *, ...) + __attribute__((format(printf, 1, 2))); +void print_verbose(const char *, ...) + __attribute__((format(printf, 1, 2))); /* imsg_util.c */ struct ibuf * @@ -855,7 +940,7 @@ 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 * +uint8_t * ibuf_data(struct ibuf *); void *ibuf_getdata(struct ibuf *, size_t); struct ibuf * @@ -869,23 +954,38 @@ 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 *); +void log_init(int, int); +void log_procinit(const char *); +void log_verbose(int); +void log_warn(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void log_warnx(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void log_info(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void log_debug(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void logit(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void vlog(int, const char *, va_list) + __attribute__((__format__ (printf, 2, 0))); +__dead void fatal(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +__dead void fatalx(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); + +/* ocsp.c */ +int ocsp_connect(struct iked *env); +int ocsp_receive_fd(struct iked *, struct imsg *); +int ocsp_validate_cert(struct iked *, struct iked_static_id *, + void *, size_t, struct iked_sahdr, uint8_t); /* 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); -size_t noncelength_xf(u_int, u_int); +size_t keylength_xf(unsigned int, unsigned int, unsigned int); +size_t noncelength_xf(unsigned int, unsigned int); int cmdline_symset(char *); -#endif /* _IKED_H */ +#endif /* IKED_H */ diff --git a/iked/ikev1.c b/iked/ikev1.c deleted file mode 100644 index fc4f6b2..0000000 --- a/iked/ikev1.c +++ /dev/null @@ -1,197 +0,0 @@ -/* $OpenBSD: ikev1.c,v 1.13 2013/03/21 04:30:14 deraadt Exp $ */ - -/* - * Copyright (c) 2010-2013 Reyk Floeter - * - * 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 -#include "openbsd-compat/sys-queue.h" -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "iked.h" -#include "ikev2.h" - -int ikev1_dispatch_parent(int, struct privsep_proc *, struct imsg *); -int ikev1_dispatch_ikev2(int, struct privsep_proc *, struct imsg *); -int ikev1_dispatch_cert(int, struct privsep_proc *, struct imsg *); - -void ikev1_msg_cb(int, short, void *); -void ikev1_recv(struct iked *, struct iked_message *); - -static struct privsep_proc procs[] = { - { "parent", PROC_PARENT, ikev1_dispatch_parent }, - { "ikev2", PROC_IKEV2, ikev1_dispatch_ikev2 }, - { "certstore", PROC_CERT, ikev1_dispatch_cert } -}; - -pid_t -ikev1(struct privsep *ps, struct privsep_proc *p) -{ - return (proc_run(ps, p, procs, nitems(procs), NULL, NULL)); -} - -int -ikev1_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) -{ - struct iked *env = p->p_env; - - switch (imsg->hdr.type) { - case IMSG_CTL_RESET: - log_debug("%s: config reload", __func__); - return (0); - case IMSG_CTL_COUPLE: - case IMSG_CTL_DECOUPLE: - return (0); - case IMSG_CTL_ACTIVE: - case IMSG_CTL_PASSIVE: - return (0); - case IMSG_UDP_SOCKET: - return (config_getsocket(env, imsg, ikev1_msg_cb)); - case IMSG_COMPILE: - return (0); - default: - break; - } - - return (-1); -} - -int -ikev1_dispatch_ikev2(int fd, struct privsep_proc *p, struct imsg *imsg) -{ - struct iked *env = p->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_recv(env, &msg); - ikev2_msg_cleanup(env, &msg); - return (0); - default: - break; - } - - return (-1); -} - -int -ikev1_dispatch_cert(int fd, struct privsep_proc *p, struct imsg *imsg) -{ - return (-1); -} - -void -ikev1_msg_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; - - proc_composev_imsg(env, PROC_IKEV2, IMSG_IKE_MESSAGE, -1, - iov, 2); - goto done; - } - - ikev1_recv(env, &msg); - - done: - ikev2_msg_cleanup(env, &msg); -} - -void -ikev1_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/iked/ikev2.c b/iked/ikev2.c index a24af78..87360ed 100644 --- a/iked/ikev2.c +++ b/iked/ikev2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ikev2.c,v 1.82 2013/03/21 04:30:14 deraadt Exp $ */ +/* $OpenBSD: ikev2.c,v 1.131 2016/06/02 07:14:26 patrick Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter @@ -16,15 +16,17 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include "openbsd-compat/sys-queue.h" +#include /* roundup */ + #include #include #include -#include #include -#if defined(__OpenBSD__) +#if defined(HAVE_NETIPSEC_IPSEC_H) +#include +#endif +#if defined(HAVE_NETINET_IP_IPSP_H) #include #endif #include @@ -33,7 +35,6 @@ #include #include #include -#include #include #include #include @@ -42,23 +43,26 @@ #include #include +#include #include "iked.h" #include "ikev2.h" #include "eap.h" #include "dh.h" +void ikev2_run(struct privsep *, struct privsep_proc *, void *); int ikev2_dispatch_parent(int, struct privsep_proc *, struct imsg *); -int ikev2_dispatch_ikev1(int, struct privsep_proc *, struct imsg *); int ikev2_dispatch_cert(int, struct privsep_proc *, struct imsg *); struct iked_sa * ikev2_getimsgdata(struct iked *, struct imsg *, struct iked_sahdr *, - u_int8_t *, u_int8_t **, size_t *); + uint8_t *, uint8_t **, size_t *); void ikev2_recv(struct iked *, struct iked_message *); -int ikev2_ike_auth(struct iked *, struct iked_sa *, +int ikev2_ike_auth_compatible(struct iked_sa *, uint8_t, uint8_t); +int ikev2_ike_auth_recv(struct iked *, struct iked_sa *, struct iked_message *); +int ikev2_ike_auth(struct iked *, struct iked_sa *); void ikev2_init_recv(struct iked *, struct iked_message *, struct ike_header *); @@ -75,45 +79,86 @@ int ikev2_resp_ike_auth(struct iked *, struct iked_sa *); int ikev2_resp_ike_eap(struct iked *, struct iked_sa *, struct ibuf *); int ikev2_send_create_child_sa(struct iked *, struct iked_sa *, - struct iked_spi *, u_int8_t); + struct iked_spi *, uint8_t); +int ikev2_ikesa_enable(struct iked *, struct iked_sa *, struct iked_sa *); +void ikev2_ikesa_delete(struct iked *, struct iked_sa *, int); int ikev2_init_create_child_sa(struct iked *, struct iked_message *); int ikev2_resp_create_child_sa(struct iked *, struct iked_message *); +void ikev2_ike_sa_rekey(struct iked *, void *); void ikev2_ike_sa_timeout(struct iked *env, void *); +void ikev2_ike_sa_alive(struct iked *, void *); int ikev2_sa_initiator(struct iked *, struct iked_sa *, - struct iked_message *); + struct iked_sa *, struct iked_message *); int ikev2_sa_responder(struct iked *, struct iked_sa *, struct iked_sa *, struct iked_message *); +int ikev2_sa_initiator_dh(struct iked_sa *, struct iked_message *, + unsigned int); +int ikev2_sa_responder_dh(struct iked_kex *, struct iked_proposals *, + struct iked_message *, unsigned int); +void ikev2_sa_cleanup_dh(struct iked_sa *); int ikev2_sa_keys(struct iked *, struct iked_sa *, struct ibuf *); int ikev2_sa_tag(struct iked_sa *, struct iked_id *); +int ikev2_set_sa_proposal(struct iked_sa *, struct iked_policy *, + unsigned int); -int ikev2_childsa_negotiate(struct iked *, struct iked_sa *, int); +int ikev2_childsa_negotiate(struct iked *, struct iked_sa *, + struct iked_kex *, struct iked_proposals *, int, int); int ikev2_match_proposals(struct iked_proposal *, struct iked_proposal *, struct iked_transform **); int ikev2_valid_proposal(struct iked_proposal *, struct iked_transform **, struct iked_transform **, int *); ssize_t ikev2_add_proposals(struct iked *, struct iked_sa *, struct ibuf *, - struct iked_proposals *, u_int8_t, int, int); + struct iked_proposals *, uint8_t, 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); + uint8_t, uint8_t, uint16_t, uint16_t); ssize_t ikev2_add_ts(struct ibuf *, struct ikev2_payload **, ssize_t, struct iked_sa *, int); -ssize_t ikev2_add_ts_payload(struct ibuf *, u_int, struct iked_sa *); +ssize_t ikev2_add_certreq(struct ibuf *, struct ikev2_payload **, ssize_t, + struct ibuf *, uint8_t); +ssize_t ikev2_add_transportnotify(struct iked *, struct ibuf *, + struct ikev2_payload **, ssize_t, struct iked_sa *); +ssize_t ikev2_add_ipcompnotify(struct iked *, struct ibuf *, + struct ikev2_payload **, ssize_t, struct iked_sa *); +ssize_t ikev2_add_ts_payload(struct ibuf *, unsigned int, struct iked_sa *); int ikev2_add_data(struct ibuf *, void *, size_t); int ikev2_add_buf(struct ibuf *buf, struct ibuf *); +int ikev2_ipcomp_enable(struct iked *, struct iked_sa *); +void ikev2_ipcomp_csa_free(struct iked *, struct iked_childsa *); + +int ikev2_cp_setaddr(struct iked *, struct iked_sa *, sa_family_t); +int ikev2_cp_fixaddr(struct iked_sa *, struct iked_addr *, + struct iked_addr *); + +ssize_t ikev2_add_sighashnotify(struct ibuf *, struct ikev2_payload **, + ssize_t); + static struct privsep_proc procs[] = { { "parent", PROC_PARENT, ikev2_dispatch_parent }, - { "ikev1", PROC_IKEV1, ikev2_dispatch_ikev1 }, { "certstore", PROC_CERT, ikev2_dispatch_cert } }; pid_t ikev2(struct privsep *ps, struct privsep_proc *p) { - return (proc_run(ps, p, procs, nitems(procs), NULL, NULL)); + return (proc_run(ps, p, procs, nitems(procs), ikev2_run, NULL)); +} + +void +ikev2_run(struct privsep *ps, struct privsep_proc *p, void *arg) +{ + /* + * pledge in the ikev2 process: + * stdio - for malloc and basic I/O including events. + * inet - for sendto with specified peer address. + * recvfd - for PFKEYv2 and the listening UDP sockets. + * In theory, recvfd could be dropped after getting the fds once. + */ + if (pledge("stdio inet recvfd", NULL) == -1) + fatal("pledge"); } int @@ -131,9 +176,9 @@ ikev2_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) case IMSG_CTL_PASSIVE: if (config_getmode(env, imsg->hdr.type) == -1) return (0); /* ignore error */ - timer_initialize(env, &env->sc_inittmr, ikev2_init_ike_sa, + timer_set(env, &env->sc_inittmr, ikev2_init_ike_sa, NULL); - timer_register(env, &env->sc_inittmr, IKED_INITIATOR_INITIAL); + timer_add(env, &env->sc_inittmr, IKED_INITIATOR_INITIAL); return (0); case IMSG_UDP_SOCKET: return (config_getsocket(env, imsg, ikev2_msg_cb)); @@ -152,49 +197,17 @@ ikev2_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) return (-1); } -int -ikev2_dispatch_ikev1(int fd, struct privsep_proc *p, struct imsg *imsg) -{ - struct iked *env = p->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_recv(env, &msg); - ikev2_msg_cleanup(env, &msg); - return (0); - default: - break; - } - - return (-1); -} - int ikev2_dispatch_cert(int fd, struct privsep_proc *p, struct imsg *imsg) { struct iked *env = p->p_env; struct iked_sahdr sh; struct iked_sa *sa; - u_int8_t type; - u_int8_t *ptr; + uint8_t type; + uint8_t *ptr; size_t len; struct iked_id *id = NULL; + int ignore = 0; switch (imsg->hdr.type) { case IMSG_CERTREQ: @@ -209,14 +222,15 @@ ikev2_dispatch_cert(int fd, struct privsep_proc *p, struct imsg *imsg) 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)); + log_debug("%s: updated local CERTREQ type %s length %zu", + __func__, print_map(type, ikev2_cert_map), + 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), + memcpy(&type, (uint8_t *)imsg->data + sizeof(sh), sizeof(type)); /* Ignore invalid or unauthenticated SAs */ @@ -227,13 +241,12 @@ ikev2_dispatch_cert(int fd, struct privsep_proc *p, struct imsg *imsg) if (imsg->hdr.type == IMSG_CERTVALID) { log_debug("%s: peer certificate is valid", __func__); - sa_stateflags(sa, IKED_REQ_VALID); - sa_state(env, sa, IKEV2_STATE_VALID); + sa_stateflags(sa, IKED_REQ_CERTVALID); } else { log_warnx("%s: peer certificate is invalid", __func__); } - if (ikev2_ike_auth(env, sa, NULL) != 0) + if (ikev2_ike_auth(env, sa) != 0) log_debug("%s: failed to send ike auth", __func__); break; case IMSG_CERT: @@ -243,6 +256,21 @@ ikev2_dispatch_cert(int fd, struct privsep_proc *p, struct imsg *imsg) break; } + /* + * Ignore the message if we already got a valid certificate. + * This might happen if the peer sent multiple CERTREQs. + */ + if (sa->sa_stateflags & IKED_REQ_CERT || + type == IKEV2_CERT_NONE) + ignore = 1; + + log_debug("%s: cert type %s length %zu, %s", __func__, + print_map(type, ikev2_cert_map), len, + ignore ? "ignored" : "ok"); + + if (ignore) + break; + if (sh.sh_initiator) id = &sa->sa_icert; else @@ -253,21 +281,15 @@ ikev2_dispatch_cert(int fd, struct privsep_proc *p, struct imsg *imsg) 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; - } + 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_ike_auth(env, sa, NULL) != 0) + if (ikev2_ike_auth(env, sa) != 0) log_debug("%s: failed to send ike auth", __func__); break; case IMSG_AUTH: @@ -276,8 +298,13 @@ ikev2_dispatch_cert(int fd, struct privsep_proc *p, struct imsg *imsg) log_debug("%s: invalid auth reply", __func__); break; } + if (sa_stateok(sa, IKEV2_STATE_VALID)) { + log_warnx("%s: ignoring AUTH in state %s", __func__, + print_map(sa->sa_state, ikev2_state_map)); + break; + } - log_debug("%s: AUTH type %d len %d", __func__, type, len); + log_debug("%s: AUTH type %d len %zu", __func__, type, len); id = &sa->sa_localauth; id->id_type = type; @@ -295,7 +322,7 @@ ikev2_dispatch_cert(int fd, struct privsep_proc *p, struct imsg *imsg) sa_stateflags(sa, IKED_REQ_AUTH); - if (ikev2_ike_auth(env, sa, NULL) != 0) + if (ikev2_ike_auth(env, sa) != 0) log_debug("%s: failed to send ike auth", __func__); break; default: @@ -307,9 +334,9 @@ ikev2_dispatch_cert(int fd, struct privsep_proc *p, struct imsg *imsg) 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) + uint8_t *type, uint8_t **buf, size_t *size) { - u_int8_t *ptr; + uint8_t *ptr; size_t len; struct iked_sa *sa; @@ -323,7 +350,7 @@ ikev2_getimsgdata(struct iked *env, struct imsg *imsg, struct iked_sahdr *sh, 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", + " type %d data length %zd", __func__, imsg->hdr.type, print_spi(sh->sh_rspi, 8), print_spi(sh->sh_ispi, 8), @@ -345,17 +372,16 @@ ikev2_recv(struct iked *env, struct iked_message *msg) struct ike_header *hdr; struct iked_message *m; struct iked_sa *sa; - u_int initiator, flag = 0; - int response; + unsigned int initiator, flag = 0; hdr = ibuf_seek(msg->msg_data, msg->msg_offset, sizeof(*hdr)); - if (hdr == NULL || (ssize_t)ibuf_size(msg->msg_data) < + if (hdr == NULL || ibuf_size(msg->msg_data) < (betoh32(hdr->ike_length) - msg->msg_offset)) return; initiator = (hdr->ike_flags & IKEV2_FLAG_INITIATOR) ? 0 : 1; - response = (hdr->ike_flags & IKEV2_FLAG_RESPONSE) ? 1 : 0; + msg->msg_response = (hdr->ike_flags & IKEV2_FLAG_RESPONSE) ? 1 : 0; msg->msg_sa = sa_lookup(env, betoh64(hdr->ike_ispi), betoh64(hdr->ike_rspi), initiator); @@ -363,11 +389,12 @@ ikev2_recv(struct iked *env, struct iked_message *msg) if (policy_lookup(env, msg) != 0) return; - log_info("%s: %s from %s %s to %s policy '%s' id %u, %ld bytes", + log_info("%s: %s %s from %s %s to %s policy '%s' id %u, %ld bytes", __func__, print_map(hdr->ike_exchange, ikev2_exchange_map), + msg->msg_response ? "response" : "request", initiator ? "responder" : "initiator", - print_host(&msg->msg_peer, NULL, 0), - print_host(&msg->msg_local, NULL, 0), + print_host((struct sockaddr *)&msg->msg_peer, NULL, 0), + print_host((struct sockaddr *)&msg->msg_local, NULL, 0), msg->msg_policy->pol_name, msg->msg_msgid, ibuf_length(msg->msg_data)); log_debug("%s: ispi %s rspi %s", __func__, @@ -382,7 +409,7 @@ ikev2_recv(struct iked *env, struct iked_message *msg) if (hdr->ike_exchange == IKEV2_EXCHANGE_INFORMATIONAL) flag = IKED_REQ_INF; - if (response) { + if (msg->msg_response) { if (msg->msg_msgid > sa->sa_reqid) return; if (hdr->ike_exchange != IKEV2_EXCHANGE_INFORMATIONAL && @@ -391,6 +418,10 @@ ikev2_recv(struct iked *env, struct iked_message *msg) if (flag) { if ((sa->sa_stateflags & flag) == 0) return; + /* + * We have initiated this exchange, even if + * we are not the initiator of the IKE SA. + */ initiator = 1; } /* @@ -413,24 +444,33 @@ ikev2_recv(struct iked *env, struct iked_message *msg) sa_free(env, sa); } return; + } else if (sa->sa_msgid_set && msg->msg_msgid == sa->sa_msgid) { + /* + * Response is being worked on, most likely we're + * waiting for the CA process to get back to us + */ + return; } /* * If it's a new request, make sure to update the peer's - * message ID and dispose of all previous responses + * message ID and dispose of all previous responses. + * We need to set sa_msgid_set in order to distinguish between + * "last msgid was 0" and "msgid not set yet". */ sa->sa_msgid = msg->msg_msgid; + sa->sa_msgid_set = 1; ikev2_msg_prevail(env, &sa->sa_responses, msg); } - if (sa_address(sa, &sa->sa_peer, &msg->msg_peer, initiator) == -1 || - sa_address(sa, &sa->sa_local, &msg->msg_local, initiator) == -1) + if (sa_address(sa, &sa->sa_peer, &msg->msg_peer) == -1 || + sa_address(sa, &sa->sa_local, &msg->msg_local) == -1) return; sa->sa_fd = msg->msg_fd; log_debug("%s: updated SA to peer %s local %s", __func__, - print_host(&sa->sa_peer.addr, NULL, 0), - print_host(&sa->sa_local.addr, NULL, 0)); + print_host((struct sockaddr *)&sa->sa_peer.addr, NULL, 0), + print_host((struct sockaddr *)&sa->sa_local.addr, NULL, 0)); done: if (initiator) @@ -445,7 +485,18 @@ ikev2_recv(struct iked *env, struct iked_message *msg) } int -ikev2_ike_auth(struct iked *env, struct iked_sa *sa, +ikev2_ike_auth_compatible(struct iked_sa *sa, uint8_t want, uint8_t have) +{ + if (want == have) + return (0); + if (sa->sa_sigsha2 && + have == IKEV2_AUTH_SIG && want == IKEV2_AUTH_RSA_SIG) + return (0); + return (-1); +} + +int +ikev2_ike_auth_recv(struct iked *env, struct iked_sa *sa, struct iked_message *msg) { struct iked_id *id, *certid; @@ -454,9 +505,6 @@ ikev2_ike_auth(struct iked *env, struct iked_sa *sa, struct iked_policy *policy = sa->sa_policy; int ret = -1; - if (msg == NULL) - goto done; - if (sa->sa_hdr.sh_initiator) { id = &sa->sa_rid; certid = &sa->sa_rcert; @@ -464,20 +512,39 @@ ikev2_ike_auth(struct iked *env, struct iked_sa *sa, id = &sa->sa_iid; certid = &sa->sa_icert; } + /* try to relookup the policy based on the peerid */ + if (msg->msg_id.id_type && !sa->sa_hdr.sh_initiator) { + struct iked_policy *old = sa->sa_policy; + + sa->sa_policy = NULL; + if (policy_lookup(env, msg) == 0 && msg->msg_policy && + msg->msg_policy != old) { + /* move sa to new policy */ + policy = sa->sa_policy = msg->msg_policy; + TAILQ_REMOVE(&old->pol_sapeers, sa, sa_peer_entry); + TAILQ_INSERT_TAIL(&policy->pol_sapeers, + sa, sa_peer_entry); + } else { + /* restore */ + msg->msg_policy = sa->sa_policy = old; + } + } if (msg->msg_id.id_type) { memcpy(id, &msg->msg_id, sizeof(*id)); bzero(&msg->msg_id, sizeof(msg->msg_id)); - if ((authmsg = ikev2_msg_auth(env, sa, - !sa->sa_hdr.sh_initiator)) == NULL) { - log_debug("%s: failed to get response " - "auth data", __func__); - return (-1); - } + if (!sa->sa_hdr.sh_initiator) { + if ((authmsg = ikev2_msg_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); - ibuf_release(authmsg); + ca_setauth(env, sa, authmsg, PROC_CERT); + ibuf_release(authmsg); + } } if (msg->msg_cert.id_type) { @@ -506,11 +573,13 @@ ikev2_ike_auth(struct iked *env, struct iked_sa *sa, ikeauth.auth_length = ibuf_size(sa->sa_eapmsk); } - if (msg->msg_auth.id_type != ikeauth.auth_method) { + if (ikev2_ike_auth_compatible(sa, + ikeauth.auth_method, msg->msg_auth.id_type) < 0) { log_warnx("%s: unexpected auth method %s", __func__, - print_map(ikeauth.auth_method, ikev2_auth_map)); + print_map(msg->msg_auth.id_type, ikev2_auth_map)); return (-1); } + ikeauth.auth_method = msg->msg_auth.id_type; if ((authmsg = ikev2_msg_auth(env, sa, sa->sa_hdr.sh_initiator)) == NULL) { @@ -524,8 +593,10 @@ ikev2_ike_auth(struct iked *env, struct iked_sa *sa, authmsg); ibuf_release(authmsg); - if (ret != 0) - goto done; + if (ret != 0) { + log_debug("%s: ikev2_msg_authverify failed", __func__); + return (-1); + } if (sa->sa_eapmsk != NULL) { if ((authmsg = ikev2_msg_auth(env, sa, @@ -544,13 +615,17 @@ ikev2_ike_auth(struct iked *env, struct iked_sa *sa, return (-1); } - sa_state(env, sa, IKEV2_STATE_EAP_VALID); + /* ikev2_msg_authverify verified AUTH */ + sa_stateflags(sa, IKED_REQ_AUTHVALID); + sa_stateflags(sa, IKED_REQ_EAPVALID); + + sa_state(env, sa, IKEV2_STATE_EAP_SUCCESS); } } if (!TAILQ_EMPTY(&msg->msg_proposals)) { - if (ikev2_sa_negotiate(sa, &sa->sa_policy->pol_proposals, - &msg->msg_proposals) != 0) { + if (ikev2_sa_negotiate(&sa->sa_proposals, + &sa->sa_policy->pol_proposals, &msg->msg_proposals) != 0) { log_debug("%s: no proposal chosen", __func__); msg->msg_error = IKEV2_N_NO_PROPOSAL_CHOSEN; return (-1); @@ -558,13 +633,49 @@ ikev2_ike_auth(struct iked *env, struct iked_sa *sa, sa_stateflags(sa, IKED_REQ_SA); } - done: + return ikev2_ike_auth(env, sa); +} + +int +ikev2_ike_auth(struct iked *env, struct iked_sa *sa) +{ + struct iked_policy *pol = sa->sa_policy; + uint8_t certreqtype; + + /* Attempt state transition */ + if (sa->sa_state == IKEV2_STATE_EAP_SUCCESS) + sa_state(env, sa, IKEV2_STATE_EAP_VALID); + else if (sa->sa_state == IKEV2_STATE_AUTH_SUCCESS) + sa_state(env, sa, IKEV2_STATE_VALID); + if (sa->sa_hdr.sh_initiator) { if (sa_stateok(sa, IKEV2_STATE_AUTH_SUCCESS)) return (ikev2_init_done(env, sa)); else return (ikev2_init_ike_auth(env, sa)); } + + /* + * If we have to send a local certificate but did not receive an + * optional CERTREQ, use our own certreq to find a local certificate. + * We could alternatively extract the CA from the peer certificate + * to find a matching local one. + */ + if (sa->sa_statevalid & IKED_REQ_CERT) { + if ((sa->sa_stateflags & IKED_REQ_CERTREQ) == 0) { + log_debug("%s: no CERTREQ, using default", __func__); + if (pol->pol_certreqtype) + certreqtype = pol->pol_certreqtype; + else + certreqtype = env->sc_certreqtype; + return (ca_setreq(env, sa, + &pol->pol_localid, certreqtype, + ibuf_data(env->sc_certreq), + ibuf_size(env->sc_certreq), PROC_CERT)); + } else if ((sa->sa_stateflags & IKED_REQ_CERT) == 0) + return (0); /* ignored, wait for cert */ + } + return (ikev2_resp_ike_auth(env, sa)); } @@ -589,7 +700,8 @@ ikev2_init_recv(struct iked *env, struct iked_message *msg, betoh64(hdr->ike_ispi), betoh64(hdr->ike_rspi), 1, NULL)) == NULL || sa != msg->msg_sa) { log_debug("%s: invalid new SA", __func__); - sa_free(env, sa); + if (sa) + sa_free(env, sa); } break; case IKEV2_EXCHANGE_IKE_AUTH: @@ -620,7 +732,8 @@ ikev2_init_recv(struct iked *env, struct iked_message *msg, * Update address information and use the NAT-T * port and socket, if available. */ - port = htons(socket_getport(&sock->sock_addr)); + port = htons(socket_getport( + (struct sockaddr *)&sock->sock_addr)); sa->sa_local.addr_port = port; sa->sa_peer.addr_port = port; (void)socket_af((struct sockaddr *)&sa->sa_local.addr, port); @@ -632,8 +745,8 @@ ikev2_init_recv(struct iked *env, struct iked_message *msg, log_debug("%s: NAT detected, updated SA to " "peer %s local %s", __func__, - print_host(&sa->sa_peer.addr, NULL, 0), - print_host(&sa->sa_local.addr, NULL, 0)); + print_host((struct sockaddr *)&sa->sa_peer.addr, NULL, 0), + print_host((struct sockaddr *)&sa->sa_local.addr, NULL, 0)); } switch (hdr->ike_exchange) { @@ -641,12 +754,13 @@ ikev2_init_recv(struct iked *env, struct iked_message *msg, (void)ikev2_init_auth(env, msg); break; case IKEV2_EXCHANGE_IKE_AUTH: - (void)ikev2_ike_auth(env, sa, msg); + (void)ikev2_ike_auth_recv(env, sa, msg); break; case IKEV2_EXCHANGE_CREATE_CHILD_SA: (void)ikev2_init_create_child_sa(env, msg); break; case IKEV2_EXCHANGE_INFORMATIONAL: + sa->sa_stateflags &= ~IKED_REQ_INF; break; default: log_debug("%s: exchange %s not implemented", __func__, @@ -659,24 +773,11 @@ void ikev2_init_ike_sa(struct iked *env, void *arg) { struct iked_policy *pol; -#ifdef __APPLE__ - static struct timeval waketime = { 0, 0 }; - struct timeval tv; - size_t size = sizeof(tv); - - if (sysctlbyname("kern.waketime", &tv, &size, NULL, 0) == 0) { - if (waketime.tv_sec != 0 && - memcmp(&waketime, &tv, sizeof(waketime)) != 0) { - log_debug("%s: after power resume", __func__); - } - memcpy(&waketime, &tv, sizeof(waketime)); - } -#endif TAILQ_FOREACH(pol, &env->sc_policies, pol_entry) { if ((pol->pol_flags & IKED_POLICY_ACTIVE) == 0) continue; - if (sa_peer_lookup(pol, &pol->pol_peer.addr) != NULL) { + if (!TAILQ_EMPTY(&pol->pol_sapeers)) { log_debug("%s: \"%s\" is already active", __func__, pol->pol_name); continue; @@ -686,11 +787,13 @@ ikev2_init_ike_sa(struct iked *env, void *arg) if (ikev2_init_ike_sa_peer(env, pol, &pol->pol_peer)) log_debug("%s: failed to initiate with peer %s", - __func__, print_host(&pol->pol_peer.addr, NULL, 0)); + __func__, + print_host((struct sockaddr *)&pol->pol_peer.addr, + NULL, 0)); } - timer_initialize(env, &env->sc_inittmr, ikev2_init_ike_sa, NULL); - timer_register(env, &env->sc_inittmr, IKED_INITIATOR_INTERVAL); + timer_set(env, &env->sc_inittmr, ikev2_init_ike_sa, NULL); + timer_add(env, &env->sc_inittmr, IKED_INITIATOR_INTERVAL); } int @@ -706,7 +809,7 @@ ikev2_init_ike_sa_peer(struct iked *env, struct iked_policy *pol, struct iked_sa *sa; struct ibuf *buf; struct group *group; - u_int8_t *ptr; + uint8_t *ptr; ssize_t len; int ret = -1; struct iked_socket *sock; @@ -720,9 +823,10 @@ ikev2_init_ike_sa_peer(struct iked *env, struct iked_policy *pol, return (-1); /* Pick peer's DH group if asked */ + /* XXX free old sa_dhgroup ? */ sa->sa_dhgroup = pol->pol_peerdh; - if (ikev2_sa_initiator(env, sa, NULL) == -1) + if (ikev2_sa_initiator(env, sa, NULL, NULL) == -1) goto done; if (pol->pol_local.addr.ss_family == AF_UNSPEC) { @@ -736,7 +840,7 @@ ikev2_init_ike_sa_peer(struct iked *env, struct iked_policy *pol, goto done; /* Inherit the port from the 1st send socket */ - port = htons(socket_getport(&sock->sock_addr)); + port = htons(socket_getport((struct sockaddr *)&sock->sock_addr)); (void)socket_af((struct sockaddr *)&req.msg_local, port); (void)socket_af((struct sockaddr *)&req.msg_peer, port); @@ -825,6 +929,9 @@ ikev2_init_ike_sa_peer(struct iked *env, struct iked_policy *pol, len += sizeof(*n); } + if ((len = ikev2_add_sighashnotify(buf, &pld, len)) == -1) + goto done; + if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONE) == -1) goto done; @@ -839,12 +946,6 @@ ikev2_init_ike_sa_peer(struct iked *env, struct iked_policy *pol, goto done; } - memcpy(&sa->sa_polpeer, &pol->pol_peer, sizeof(sa->sa_polpeer)); - if (RB_INSERT(iked_sapeers, &pol->pol_sapeers, sa)) { - log_debug("%s: conflicting sa", __func__); - goto done; - } - if ((ret = ikev2_msg_send(env, &req)) == 0) sa_state(env, sa, IKEV2_STATE_SA_INIT); @@ -867,7 +968,7 @@ ikev2_init_auth(struct iked *env, struct iked_message *msg) if (sa == NULL) return (-1); - if (ikev2_sa_initiator(env, sa, msg) == -1) { + if (ikev2_sa_initiator(env, sa, NULL, msg) == -1) { log_debug("%s: failed to get IKE keys", __func__); return (-1); } @@ -895,7 +996,7 @@ ikev2_init_ike_auth(struct iked *env, struct iked_sa *sa) struct ikev2_auth *auth; struct iked_id *id, *certid; struct ibuf *e = NULL; - u_int8_t firstpayload; + uint8_t firstpayload; int ret = -1; ssize_t len; @@ -937,21 +1038,15 @@ ikev2_init_ike_auth(struct iked *env, struct iked_sa *sa) goto done; len = ibuf_size(certid->id_buf) + sizeof(*cert); - if (env->sc_certreqtype) { - if (ikev2_next_payload(pld, len, - IKEV2_PAYLOAD_CERTREQ) == -1) - goto done; + /* CERTREQ payload(s) */ + if ((len = ikev2_add_certreq(e, &pld, + len, env->sc_certreq, env->sc_certreqtype)) == -1) + goto done; - /* CERTREQ payload */ - if ((pld = ikev2_add_payload(e)) == NULL) - goto done; - if ((cert = ibuf_advance(e, sizeof(*cert))) == NULL) - goto done; - cert->cert_type = env->sc_certreqtype; - if (ikev2_add_buf(e, env->sc_certreq) == -1) - goto done; - len = ibuf_size(env->sc_certreq) + sizeof(*cert); - } + if (env->sc_certreqtype != pol->pol_certreqtype && + (len = ikev2_add_certreq(e, &pld, + len, NULL, pol->pol_certreqtype)) == -1) + goto done; } if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_AUTH) == -1) @@ -978,6 +1073,16 @@ ikev2_init_ike_auth(struct iked *env, struct iked_sa *sa) goto done; } + /* compression */ + if ((pol->pol_flags & IKED_POLICY_IPCOMP) && + (len = ikev2_add_ipcompnotify(env, e, &pld, len, sa)) == -1) + goto done; + + /* Transport mode IPSec. */ + if ((pol->pol_flags & IKED_POLICY_TRANSPORT) && + (len = ikev2_add_transportnotify(env, e, &pld, len, sa)) == -1) + goto done; + if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_SA) == -1) goto done; @@ -1011,11 +1116,18 @@ ikev2_init_done(struct iked *env, struct iked_sa *sa) if (!sa_stateok(sa, IKEV2_STATE_VALID)) return (0); /* ignored */ - ret = ikev2_childsa_negotiate(env, sa, sa->sa_hdr.sh_initiator); + ret = ikev2_childsa_negotiate(env, sa, &sa->sa_kex, &sa->sa_proposals, + sa->sa_hdr.sh_initiator, 0); if (ret == 0) ret = ikev2_childsa_enable(env, sa); - if (ret == 0) + if (ret == 0) { sa_state(env, sa, IKEV2_STATE_ESTABLISHED); + timer_set(env, &sa->sa_timer, ikev2_ike_sa_alive, sa); + timer_add(env, &sa->sa_timer, IKED_IKE_SA_ALIVE_TIMEOUT); + timer_set(env, &sa->sa_rekey, ikev2_ike_sa_rekey, sa); + if (sa->sa_policy->pol_rekey) + timer_add(env, &sa->sa_rekey, sa->sa_policy->pol_rekey); + } if (ret) ikev2_childsa_delete(env, sa, 0, 0, NULL, 1); @@ -1030,6 +1142,9 @@ ikev2_policy2id(struct iked_static_id *polid, struct iked_id *id, int srcid) char idstr[IKED_ID_SIZE]; struct in_addr in4; struct in6_addr in6; + X509_NAME *name = NULL; + uint8_t *p; + int len; /* Fixup the local Id if not specified */ if (srcid && polid->id_type == 0) { @@ -1072,6 +1187,19 @@ ikev2_policy2id(struct iked_static_id *polid, struct iked_id *id, int srcid) return (-1); } break; + case IKEV2_ID_ASN1_DN: + /* policy has ID in string-format, convert to ASN1 */ + if ((name = ca_x509_name_parse(polid->id_data)) == NULL || + (len = i2d_X509_NAME(name, NULL)) < 0 || + (p = ibuf_reserve(id->id_buf, len)) == NULL || + (i2d_X509_NAME(name, &p)) < 0) { + if (name) + X509_NAME_free(name); + ibuf_release(id->id_buf); + return (-1); + } + X509_NAME_free(name); + break; default: if (ibuf_add(id->id_buf, polid->id_data, polid->id_length) != 0) { @@ -1084,7 +1212,7 @@ ikev2_policy2id(struct iked_static_id *polid, struct iked_id *id, int srcid) if (ikev2_print_id(id, idstr, sizeof(idstr)) == -1) return (-1); - log_debug("%s: %s %s length %d", __func__, + log_debug("%s: %s %s length %zu", __func__, srcid ? "srcid" : "dstid", idstr, ibuf_size(id->id_buf)); @@ -1093,8 +1221,8 @@ ikev2_policy2id(struct iked_static_id *polid, struct iked_id *id, int srcid) 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) + uint32_t msgid, uint8_t nextpayload, + uint8_t exchange, uint8_t flags) { struct ike_header *hdr; @@ -1121,7 +1249,7 @@ ikev2_add_header(struct ibuf *buf, struct iked_sa *sa, int ikev2_set_header(struct ike_header *hdr, size_t length) { - u_int32_t hdrlength = sizeof(*hdr) + length; + uint32_t hdrlength = sizeof(*hdr) + length; if (hdrlength > UINT32_MAX) { log_debug("%s: message too long", __func__); @@ -1150,16 +1278,17 @@ ikev2_add_payload(struct ibuf *buf) } ssize_t -ikev2_add_ts_payload(struct ibuf *buf, u_int type, struct iked_sa *sa) +ikev2_add_ts_payload(struct ibuf *buf, unsigned 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; + struct iked_addr pooladdr; + uint8_t *ptr; size_t len = 0; - u_int32_t av[4], bv[4], mv[4]; + uint32_t av[4], bv[4], mv[4]; struct sockaddr_in *in4; struct sockaddr_in6 *in6; @@ -1185,6 +1314,13 @@ ikev2_add_ts_payload(struct ibuf *buf, u_int type, struct iked_sa *sa) } else return (-1); + /* patch remote address (if configured to 0.0.0.0) */ + if ((type == IKEV2_PAYLOAD_TSi && !sa->sa_hdr.sh_initiator) || + (type == IKEV2_PAYLOAD_TSr && sa->sa_hdr.sh_initiator)) { + if (ikev2_cp_fixaddr(sa, addr, &pooladdr) != -1) + addr = &pooladdr; + } + ts->ts_protoid = flow->flow_ipproto; if (addr->addr_port) { @@ -1277,9 +1413,159 @@ ikev2_add_ts(struct ibuf *e, struct ikev2_payload **pld, ssize_t len, return (len); } + +ssize_t +ikev2_add_certreq(struct ibuf *e, struct ikev2_payload **pld, ssize_t len, + struct ibuf *certreq, uint8_t type) +{ + struct ikev2_cert *cert; + + if (type == IKEV2_CERT_NONE) + return (len); + + if (ikev2_next_payload(*pld, len, IKEV2_PAYLOAD_CERTREQ) == -1) + return (-1); + + /* CERTREQ payload */ + if ((*pld = ikev2_add_payload(e)) == NULL) + return (-1); + + if ((cert = ibuf_advance(e, sizeof(*cert))) == NULL) + return (-1); + + cert->cert_type = type; + len = sizeof(*cert); + + if (certreq != NULL && cert->cert_type == IKEV2_CERT_X509_CERT) { + if (ikev2_add_buf(e, certreq) == -1) + return (-1); + len += ibuf_size(certreq); + } + + log_debug("%s: type %s length %zd", __func__, + print_map(type, ikev2_cert_map), len); + + return (len); +} + +ssize_t +ikev2_add_transportnotify(struct iked *env, struct ibuf *e, + struct ikev2_payload **pld, ssize_t len, struct iked_sa *sa) +{ + struct ikev2_notify *n; + + if (*pld) + if (ikev2_next_payload(*pld, len, IKEV2_PAYLOAD_NOTIFY) == -1) + return (-1); + if ((*pld = ikev2_add_payload(e)) == NULL) + return (-1); + len = sizeof(*n); + if ((n = ibuf_advance(e, len)) == NULL) + return (-1); + n->n_protoid = 0; + n->n_spisize = 0; + n->n_type = htobe16(IKEV2_N_USE_TRANSPORT_MODE); + + log_info("%s: Notifying to use transport mode", __func__); + + return (len); +} + +ssize_t +ikev2_add_ipcompnotify(struct iked *env, struct ibuf *e, + struct ikev2_payload **pld, ssize_t len, struct iked_sa *sa) +{ + struct iked_childsa csa; + struct ikev2_notify *n; + u_int8_t *ptr; + u_int16_t cpi; + u_int32_t spi; + u_int8_t transform; + + /* we only support deflate */ + transform = IKEV2_IPCOMP_DEFLATE; + + bzero(&csa, sizeof(csa)); + csa.csa_saproto = IKEV2_SAPROTO_IPCOMP; + csa.csa_ikesa = sa; + csa.csa_local = &sa->sa_peer; + csa.csa_peer = &sa->sa_local; + + if (pfkey_sa_init(env->sc_pfkey, &csa, &spi) == -1) + return (-1); + /* + * We get spi == 0 if the kernel does not support IPcomp, + * so just return the length of the current payload. + */ + if (spi == 0) + return (len); + cpi = htobe16((uint16_t)spi); + if (*pld) + if (ikev2_next_payload(*pld, len, IKEV2_PAYLOAD_NOTIFY) == -1) + return (-1); + if ((*pld = ikev2_add_payload(e)) == NULL) + return (-1); + len = sizeof(*n) + sizeof(cpi) + sizeof(transform); + if ((ptr = ibuf_advance(e, len)) == NULL) + return (-1); + n = (struct ikev2_notify *)ptr; + n->n_protoid = 0; + n->n_spisize = 0; + n->n_type = htobe16(IKEV2_N_IPCOMP_SUPPORTED); + ptr += sizeof(*n); + memcpy(ptr, &cpi, sizeof(cpi)); + ptr += sizeof(cpi); + memcpy(ptr, &transform, sizeof(transform)); + + sa->sa_cpi_in = spi; /* already on host byte order */ + log_debug("%s: sa_cpi_in 0x%04x", __func__, sa->sa_cpi_in); + + return (len); +} + +ssize_t +ikev2_add_sighashnotify(struct ibuf *e, struct ikev2_payload **pld, + ssize_t len) +{ + struct ikev2_notify *n; + uint8_t *ptr; + size_t i; + uint16_t hash, signature_hashes[] = { + IKEV2_SIGHASH_SHA2_256, + IKEV2_SIGHASH_SHA2_384, + IKEV2_SIGHASH_SHA2_512 + }; + + if (ikev2_next_payload(*pld, len, IKEV2_PAYLOAD_NOTIFY) == -1) + return (-1); + + /* XXX signature_hashes are hardcoded for now */ + len = sizeof(*n) + nitems(signature_hashes) * sizeof(hash); + + /* NOTIFY payload */ + if ((*pld = ikev2_add_payload(e)) == NULL) + return (-1); + if ((ptr = ibuf_advance(e, len)) == NULL) + return (-1); + + n = (struct ikev2_notify *)ptr; + n->n_protoid = 0; + n->n_spisize = 0; + n->n_type = htobe16(IKEV2_N_SIGNATURE_HASH_ALGORITHMS); + ptr += sizeof(*n); + + for (i = 0; i < nitems(signature_hashes); i++) { + hash = htobe16(signature_hashes[i]); + memcpy(ptr, &hash, sizeof(hash)); + ptr += sizeof(hash); + } + + return (len); +} + int ikev2_next_payload(struct ikev2_payload *pld, size_t length, - u_int8_t nextpayload) + uint8_t nextpayload) { size_t pldlength = sizeof(*pld) + length; @@ -1288,7 +1574,7 @@ ikev2_next_payload(struct ikev2_payload *pld, size_t length, return (-1); } - log_debug("%s: length %d nextpayload %s", + log_debug("%s: length %zu nextpayload %s", __func__, pldlength, print_map(nextpayload, ikev2_payload_map)); pld->pld_length = htobe16(pldlength); @@ -1299,21 +1585,21 @@ ikev2_next_payload(struct ikev2_payload *pld, size_t length, ssize_t ikev2_nat_detection(struct iked *env, struct iked_message *msg, - void *ptr, size_t len, u_int type) + void *ptr, size_t len, unsigned int type) { EVP_MD_CTX ctx; struct ike_header *hdr; - u_int8_t md[SHA_DIGEST_LENGTH]; - u_int mdlen = sizeof(md); + uint8_t md[SHA_DIGEST_LENGTH]; + unsigned 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 *src, *dst, *ss; - u_int64_t rspi, ispi; + struct sockaddr *src, *dst, *ss; + uint64_t rspi, ispi; struct ibuf *buf; int frompeer = 0; - u_int32_t rnd; + uint32_t rnd; if (ptr == NULL) return (mdlen); @@ -1325,14 +1611,14 @@ ikev2_nat_detection(struct iked *env, struct iked_message *msg, ispi = hdr->ike_ispi; rspi = hdr->ike_rspi; frompeer = 1; - src = &msg->msg_peer; - dst = &msg->msg_local; + src = (struct sockaddr *)&msg->msg_peer; + dst = (struct sockaddr *)&msg->msg_local; } else { ispi = htobe64(sa->sa_hdr.sh_ispi); rspi = htobe64(sa->sa_hdr.sh_rspi); frompeer = 0; - src = &msg->msg_local; - dst = &msg->msg_peer; + src = (struct sockaddr *)&msg->msg_local; + dst = (struct sockaddr *)&msg->msg_peer; } EVP_MD_CTX_init(&ctx); @@ -1362,7 +1648,7 @@ ikev2_nat_detection(struct iked *env, struct iked_message *msg, EVP_DigestUpdate(&ctx, &ispi, sizeof(ispi)); EVP_DigestUpdate(&ctx, &rspi, sizeof(rspi)); - switch (ss->ss_family) { + switch (ss->sa_family) { case AF_INET: in4 = (struct sockaddr_in *)ss; EVP_DigestUpdate(&ctx, &in4->sin_addr.s_addr, @@ -1407,11 +1693,12 @@ ikev2_add_cp(struct iked *env, struct iked_sa *sa, struct ibuf *buf) struct ikev2_cp *cp; struct ikev2_cfg *cfg; struct iked_cfg *ikecfg; - u_int i; + unsigned int i; + uint32_t mask4; size_t len; struct sockaddr_in *in4; struct sockaddr_in6 *in6; - u_int8_t prefixlen; + uint8_t prefixlen; if ((cp = ibuf_advance(buf, sizeof(*cp))) == NULL) return (-1); @@ -1447,12 +1734,29 @@ ikev2_add_cp(struct iked *env, struct iked_sa *sa, struct ibuf *buf) case IKEV2_CFG_INTERNAL_IP4_DHCP: case IKEV2_CFG_INTERNAL_IP4_SERVER: /* 4 bytes IPv4 address */ - in4 = (struct sockaddr_in *)&ikecfg->cfg.address.addr; + in4 = (ikecfg->cfg.address.addr_mask != 32 && + (ikecfg->cfg_type == + IKEV2_CFG_INTERNAL_IP4_ADDRESS) && + sa->sa_addrpool && + sa->sa_addrpool->addr_af == AF_INET) ? + (struct sockaddr_in *)&sa->sa_addrpool->addr : + (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_IP4_SUBNET: + /* 4 bytes IPv4 address + 4 bytes IPv4 mask + */ + in4 = (struct sockaddr_in *)&ikecfg->cfg.address.addr; + mask4 = prefixlen2mask(ikecfg->cfg.address.addr_mask); + cfg->cfg_length = htobe16(8); + if (ibuf_add(buf, &in4->sin_addr.s_addr, 4) == -1) + return (-1); + if (ibuf_add(buf, &mask4, 4) == -1) + return (-1); + len += 8; + break; case IKEV2_CFG_INTERNAL_IP6_DNS: case IKEV2_CFG_INTERNAL_IP6_NBNS: case IKEV2_CFG_INTERNAL_IP6_DHCP: @@ -1465,8 +1769,15 @@ ikev2_add_cp(struct iked *env, struct iked_sa *sa, struct ibuf *buf) len += 16; break; case IKEV2_CFG_INTERNAL_IP6_ADDRESS: + case IKEV2_CFG_INTERNAL_IP6_SUBNET: /* 16 bytes IPv6 address + 1 byte prefix length */ - in6 = (struct sockaddr_in6 *)&ikecfg->cfg.address.addr; + in6 = (ikecfg->cfg.address.addr_mask != 128 && + (ikecfg->cfg_type == + IKEV2_CFG_INTERNAL_IP6_ADDRESS) && + sa->sa_addrpool6 && + sa->sa_addrpool6->addr_af == AF_INET6) ? + (struct sockaddr_in6 *)&sa->sa_addrpool6->addr : + (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); @@ -1490,7 +1801,7 @@ ikev2_add_cp(struct iked *env, struct iked_sa *sa, struct ibuf *buf) ssize_t ikev2_add_proposals(struct iked *env, struct iked_sa *sa, struct ibuf *buf, - struct iked_proposals *proposals, u_int8_t protoid, int initiator, + struct iked_proposals *proposals, uint8_t protoid, int initiator, int sendikespi) { struct ikev2_sa_proposal *sap; @@ -1498,9 +1809,9 @@ ikev2_add_proposals(struct iked *env, struct iked_sa *sa, struct ibuf *buf, struct iked_proposal *prop; struct iked_childsa csa; ssize_t length = 0, saplength, xflen; - u_int64_t spi64; - u_int32_t spi32, spi; - u_int i; + uint64_t spi64; + uint32_t spi32, spi; + unsigned int i; TAILQ_FOREACH(prop, proposals, prop_entry) { if ((protoid && prop->prop_protoid != protoid) || @@ -1575,14 +1886,14 @@ ikev2_add_proposals(struct iked *env, struct iked_sa *sa, struct ibuf *buf, length += saplength; } - log_debug("%s: length %d", __func__, length); + log_debug("%s: length %zd", __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) + uint8_t more, uint8_t type, uint16_t id, uint16_t length) { struct ikev2_transform *xfrm; struct ikev2_attribute *attr; @@ -1720,7 +2031,7 @@ ikev2_resp_recv(struct iked *env, struct iked_message *msg, sa->sa_policy->pol_auth.auth_eap) sa_state(env, sa, IKEV2_STATE_EAP); - if (ikev2_ike_auth(env, sa, msg) != 0) { + if (ikev2_ike_auth_recv(env, sa, msg) != 0) { log_debug("%s: failed to send auth response", __func__); sa_state(env, sa, IKEV2_STATE_CLOSED); return; @@ -1729,6 +2040,14 @@ ikev2_resp_recv(struct iked *env, struct iked_message *msg, case IKEV2_EXCHANGE_CREATE_CHILD_SA: (void)ikev2_resp_create_child_sa(env, msg); break; + case IKEV2_EXCHANGE_INFORMATIONAL: + if (!msg->msg_responded && !msg->msg_error) { + (void)ikev2_send_ike_e(env, sa, NULL, + IKEV2_PAYLOAD_NONE, IKEV2_EXCHANGE_INFORMATIONAL, + 1); + msg->msg_responded = 1; + } + break; default: break; } @@ -1740,13 +2059,12 @@ 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; + uint8_t *ptr; ssize_t len; int ret = -1; @@ -1841,21 +2159,22 @@ ikev2_resp_ike_sa_init(struct iked *env, struct iked_message *msg) len += sizeof(*n); } - if (env->sc_certreqtype && (sa->sa_statevalid & IKED_REQ_CERT)) { - if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_CERTREQ) == -1) + if (sa->sa_statevalid & IKED_REQ_CERT) { + /* CERTREQ payload(s) */ + if ((len = ikev2_add_certreq(buf, &pld, + len, env->sc_certreq, env->sc_certreqtype)) == -1) goto done; - /* CERTREQ payload */ - if ((pld = ikev2_add_payload(buf)) == NULL) - goto done; - if ((cert = ibuf_advance(buf, sizeof(*cert))) == NULL) + if (env->sc_certreqtype != sa->sa_policy->pol_certreqtype && + (len = ikev2_add_certreq(buf, &pld, + len, NULL, sa->sa_policy->pol_certreqtype)) == -1) 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 (sa->sa_sigsha2 && + (len = ikev2_add_sighashnotify(buf, &pld, len)) == -1) + goto done; + if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONE) == -1) goto done; @@ -1888,7 +2207,7 @@ ikev2_resp_ike_auth(struct iked *env, struct iked_sa *sa) struct ikev2_auth *auth; struct iked_id *id, *certid; struct ibuf *e = NULL; - u_int8_t firstpayload; + uint8_t firstpayload; int ret = -1; ssize_t len; @@ -1900,7 +2219,12 @@ ikev2_resp_ike_auth(struct iked *env, struct iked_sa *sa) else if (!sa_stateok(sa, IKEV2_STATE_VALID)) return (0); /* ignore */ - if (ikev2_childsa_negotiate(env, sa, sa->sa_hdr.sh_initiator) == -1) + if (ikev2_cp_setaddr(env, sa, AF_INET) < 0 || + ikev2_cp_setaddr(env, sa, AF_INET6) < 0) + return (-1); + + if (ikev2_childsa_negotiate(env, sa, &sa->sa_kex, &sa->sa_proposals, + sa->sa_hdr.sh_initiator, 0) < 0) return (-1); /* New encrypted message buffer */ @@ -1988,6 +2312,16 @@ ikev2_resp_ike_auth(struct iked *env, struct iked_sa *sa) goto done; } + /* compression */ + if (sa->sa_ipcomp && + (len = ikev2_add_ipcompnotify(env, e, &pld, len, sa)) == -1) + goto done; + + /* Transport mode IPSec. */ + if (sa->sa_transport && + (len = ikev2_add_transportnotify(env, e, &pld, len, sa)) == -1) + goto done; + if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_SA) == -1) goto done; @@ -2009,12 +2343,32 @@ ikev2_resp_ike_auth(struct iked *env, struct iked_sa *sa) IKEV2_EXCHANGE_IKE_AUTH, firstpayload, 1); if (ret == 0) ret = ikev2_childsa_enable(env, sa); - if (ret == 0) + if (ret == 0) { sa_state(env, sa, IKEV2_STATE_ESTABLISHED); + /** + * Logs about the secure session are tagged with LOG_SESSION + * so that it can be extracted for customer visiblity purposes + */ + log_info("LOG_SESSION: Secure session established to peer %s \ + local %s", print_host((struct sockaddr *)&sa->sa_peer.addr, + NULL, 0), print_host((struct sockaddr *)&sa->sa_local.addr, + NULL, 0)); + timer_set(env, &sa->sa_timer, ikev2_ike_sa_alive, sa); + timer_add(env, &sa->sa_timer, IKED_IKE_SA_ALIVE_TIMEOUT); + timer_set(env, &sa->sa_rekey, ikev2_ike_sa_rekey, sa); + if (sa->sa_policy->pol_rekey) + timer_add(env, &sa->sa_rekey, sa->sa_policy->pol_rekey); + } done: if (ret) ikev2_childsa_delete(env, sa, 0, 0, NULL, 1); + if (ret != 0) { + log_info("LOG_SESSION: Authentication failure in establishing \ + secure session from %s to peer %s", + print_host((struct sockaddr *)&sa->sa_local.addr, NULL, 0), + print_host((struct sockaddr *)&sa->sa_peer.addr, NULL, 0)); + } ibuf_release(e); return (ret); } @@ -2027,17 +2381,18 @@ ikev2_resp_ike_eap(struct iked *env, struct iked_sa *sa, struct ibuf *eapmsg) struct ikev2_auth *auth; struct iked_id *id, *certid; struct ibuf *e = NULL; - u_int8_t firstpayload; + uint8_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); + /* Check if "ca" has done it's job yet */ + if (!sa->sa_localauth.id_type) + return (0); + /* New encrypted message buffer */ if ((e = ibuf_static()) == NULL) goto done; @@ -2105,7 +2460,7 @@ ikev2_resp_ike_eap(struct iked *env, struct iked_sa *sa, struct ibuf *eapmsg) 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) + uint8_t firstpayload, uint8_t exchange, int response) { struct ikev2_payload *pld; struct ibuf *e = NULL; @@ -2117,11 +2472,15 @@ ikev2_send_ike_e(struct iked *env, struct iked_sa *sa, struct ibuf *buf, 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; + if (buf) { + if (ibuf_cat(e, buf) != 0) + goto done; + + if (ikev2_next_payload(pld, ibuf_size(buf), + IKEV2_PAYLOAD_NONE) == -1) + goto done; + } ret = ikev2_msg_send_encrypt(env, sa, &e, exchange, firstpayload, response); @@ -2132,16 +2491,47 @@ ikev2_send_ike_e(struct iked *env, struct iked_sa *sa, struct ibuf *buf, return (ret); } +int +ikev2_set_sa_proposal(struct iked_sa *sa, struct iked_policy *pol, + unsigned int proto) +{ + struct iked_proposal *prop, *copy; + struct iked_transform *xform; + unsigned int i; + + /* create copy of the policy proposals */ + config_free_proposals(&sa->sa_proposals, proto); + TAILQ_FOREACH(prop, &pol->pol_proposals, prop_entry) { + if (proto != 0 && prop->prop_protoid != proto) + continue; + if ((copy = config_add_proposal(&sa->sa_proposals, + prop->prop_id, prop->prop_protoid)) == NULL) + return (-1); + for (i = 0; i < prop->prop_nxforms; i++) { + xform = &prop->prop_xforms[i]; + if (config_add_transform(copy, xform->xform_type, + xform->xform_id, xform->xform_length, + xform->xform_keylength) == NULL) + return (-1); + } + } + return (0); +} + int ikev2_send_create_child_sa(struct iked *env, struct iked_sa *sa, - struct iked_spi *rekey, u_int8_t protoid) + struct iked_spi *rekey, uint8_t protoid) { + struct iked_policy *pol = sa->sa_policy; struct iked_childsa *csa = NULL, *csb = NULL; struct ikev2_notify *n; - struct ikev2_payload *pld; + struct ikev2_payload *pld = NULL; + struct ikev2_keyexchange *ke; + struct group *group; struct ibuf *e = NULL, *nonce = NULL; - u_int8_t *ptr; - u_int32_t spi; + uint8_t *ptr; + uint8_t firstpayload; + uint32_t spi; ssize_t len = 0; int initiator, ret = -1; @@ -2152,6 +2542,14 @@ ikev2_send_create_child_sa(struct iked *env, struct iked_sa *sa, else log_debug("%s: creating new CHILD SAs", __func__); + /* XXX cannot initiate multiple concurrent CREATE_CHILD_SA exchanges */ + if (sa->sa_stateflags & IKED_REQ_CHILDSA) { + log_debug("%s: another CREATE_CHILD_SA exchange already active", + __func__); + return (-1); + } + + sa->sa_rekeyspi = 0; /* clear rekey spi */ initiator = sa->sa_hdr.sh_initiator ? 1 : 0; if (rekey && @@ -2160,6 +2558,7 @@ ikev2_send_create_child_sa(struct iked *env, struct iked_sa *sa, (csb = csa->csa_peersa) == NULL)) { log_debug("%s: CHILD SA %s wasn't found", __func__, print_spi(rekey->spi, rekey->spi_size)); + goto done; } /* Generate new nonce */ @@ -2173,9 +2572,39 @@ ikev2_send_create_child_sa(struct iked *env, struct iked_sa *sa, if ((e = ibuf_static()) == NULL) goto done; + len = 0; + + /* compression */ + if ((pol->pol_flags & IKED_POLICY_IPCOMP) && + (len = ikev2_add_ipcompnotify(env, e, &pld, len, sa)) == -1) + goto done; + + /* Transport mode IPSec. */ + if ((pol->pol_flags & IKED_POLICY_TRANSPORT) && + (len = ikev2_add_transportnotify(env, e, &pld, len, sa)) == -1) + goto done; + + if (pld) { + firstpayload = IKEV2_PAYLOAD_NOTIFY; + if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_SA) == -1) + goto done; + } else + firstpayload = IKEV2_PAYLOAD_SA; + /* SA payload */ if ((pld = ikev2_add_payload(e)) == NULL) goto done; + + /* + * We need to reset the sa_proposal. Otherwise it would be + * left over from the IKE_AUTH exchange and would not contain + * any DH groups (e.g. for ESP child SAs). + */ + if (ikev2_set_sa_proposal(sa, pol, protoid) < 0) { + log_debug("%s: ikev2_set_sa_proposal failed", __func__); + goto done; + } + if ((len = ikev2_add_proposals(env, sa, e, &sa->sa_proposals, protoid, 1, 0)) == -1) goto done; @@ -2190,6 +2619,32 @@ ikev2_send_create_child_sa(struct iked *env, struct iked_sa *sa, goto done; len = ibuf_size(nonce); + if (config_findtransform(&pol->pol_proposals, IKEV2_XFORMTYPE_DH, + protoid)) { + log_debug("%s: enable PFS", __func__); + ikev2_sa_cleanup_dh(sa); + if (ikev2_sa_initiator_dh(sa, NULL, protoid) < 0) { + log_debug("%s: failed to setup DH", __func__); + goto done; + } + if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_KE) == -1) + goto done; + + /* KE payload */ + if ((pld = ikev2_add_payload(e)) == NULL) + goto done; + if ((ke = ibuf_advance(e, 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(e, sa->sa_dhiexchange) == -1) + goto done; + len = sizeof(*ke) + dh_getlen(group); + } + if ((len = ikev2_add_ts(e, &pld, len, sa, !initiator)) == -1) goto done; @@ -2208,7 +2663,7 @@ ikev2_send_create_child_sa(struct iked *env, struct iked_sa *sa, if ((ptr = ibuf_advance(e, rekey->spi_size)) == NULL) goto done; len = rekey->spi_size; - spi = htobe32((u_int32_t)csa->csa_peerspi); + spi = htobe32((uint32_t)csa->csa_peerspi); memcpy(ptr, &spi, rekey->spi_size); len += sizeof(*n); } @@ -2217,11 +2672,16 @@ ikev2_send_create_child_sa(struct iked *env, struct iked_sa *sa, goto done; ret = ikev2_msg_send_encrypt(env, sa, &e, - IKEV2_EXCHANGE_CREATE_CHILD_SA, IKEV2_PAYLOAD_SA, 0); + IKEV2_EXCHANGE_CREATE_CHILD_SA, firstpayload, 0); if (ret == 0) { if (rekey) { csa->csa_rekey = 1; csb->csa_rekey = 1; + /* + * Remember the peer spi of the rekeyed + * SA for ikev2_init_create_child_sa(). + */ + sa->sa_rekeyspi = csa->csa_peerspi; } sa->sa_stateflags |= IKED_REQ_CHILDSA; } @@ -2231,28 +2691,131 @@ ikev2_send_create_child_sa(struct iked *env, struct iked_sa *sa, return (ret); } +void +ikev2_ike_sa_rekey(struct iked *env, void *arg) +{ + struct iked_sa *sa = arg; + struct iked_sa *nsa = NULL; + struct ikev2_payload *pld = NULL; + struct ikev2_keyexchange *ke; + struct group *group; + struct ibuf *e = NULL, *nonce = NULL; + ssize_t len = 0; + int ret = -1; + + log_debug("%s: called for IKE SA %p", __func__, sa); + + if (sa->sa_stateflags & IKED_REQ_CHILDSA) { + /* + * We cannot initiate multiple concurrent CREATE_CHILD_SA + * exchanges, so retry in one minute. + */ + timer_add(env, &sa->sa_rekey, 60); + return; + } + + if ((nsa = sa_new(env, 0, 0, 1, sa->sa_policy)) == NULL) { + log_debug("%s: failed to get new SA", __func__); + goto done; + } + + if (ikev2_sa_initiator(env, nsa, sa, NULL)) { + log_debug("%s: failed to setup DH", __func__); + goto done; + } + sa_state(env, nsa, IKEV2_STATE_AUTH_SUCCESS); + nonce = nsa->sa_inonce; + + if ((e = ibuf_static()) == NULL) + goto done; + + /* SA payload */ + if ((pld = ikev2_add_payload(e)) == NULL) + goto done; + + /* just reuse the old IKE SA proposals */ + if ((len = ikev2_add_proposals(env, nsa, e, &sa->sa_proposals, + IKEV2_SAPROTO_IKE, 1, 1)) == -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_KE) == -1) + goto done; + + /* KE payload */ + if ((pld = ikev2_add_payload(e)) == NULL) + goto done; + if ((ke = ibuf_advance(e, sizeof(*ke))) == NULL) + goto done; + if ((group = nsa->sa_dhgroup) == NULL) { + log_debug("%s: invalid dh", __func__); + goto done; + } + ke->kex_dhgroup = htobe16(group->id); + if (ikev2_add_buf(e, nsa->sa_dhiexchange) == -1) + goto done; + len = sizeof(*ke) + dh_getlen(group); + + if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONE) == -1) + goto done; + + ret = ikev2_msg_send_encrypt(env, sa, &e, + IKEV2_EXCHANGE_CREATE_CHILD_SA, IKEV2_PAYLOAD_SA, 0); + if (ret == 0) { + sa->sa_stateflags |= IKED_REQ_CHILDSA; + sa->sa_next = nsa; + nsa = NULL; + } +done: + if (nsa) + sa_free(env, nsa); + ibuf_release(e); + + if (ret == 0) + log_debug("%s: create child SA sent", __func__); + else + log_debug("%s: could not send create child SA", __func__); + /* XXX should we try again in case of ret != 0 ? */ +} + int ikev2_init_create_child_sa(struct iked *env, struct iked_message *msg) { - struct iked_childsa *csa; + struct iked_childsa *csa = NULL; struct iked_proposal *prop; struct iked_sa *sa = msg->msg_sa; + struct iked_sa *nsa; + struct iked_spi *spi; struct ikev2_delete *del; struct ibuf *buf = NULL; - u_int64_t peerspi; - u_int32_t spi32; - int rekeying = 0, ret = -1; + uint32_t spi32; + int pfs = 0, ret = -1; if (!ikev2_msg_frompeer(msg) || (sa->sa_stateflags & IKED_REQ_CHILDSA) == 0) return (0); - if (msg->msg_prop == NULL) { + if (msg->msg_prop == NULL || + TAILQ_EMPTY(&msg->msg_proposals)) { log_debug("%s: no proposal specified", __func__); return (-1); } - /* Update peer SPI */ + if (ikev2_sa_negotiate(&sa->sa_proposals, &sa->sa_proposals, + &msg->msg_proposals) != 0) { + log_debug("%s: no proposal chosen", __func__); + return (-1); + } + TAILQ_FOREACH(prop, &sa->sa_proposals, prop_entry) { if (prop->prop_protoid == msg->msg_prop->prop_protoid) break; @@ -2263,13 +2826,56 @@ ikev2_init_create_child_sa(struct iked *env, struct iked_message *msg) return (-1); } - peerspi = prop->prop_peerspi.spi; - prop->prop_peerspi.spi = msg->msg_prop->prop_peerspi.spi; - - if ((csa = childsa_lookup(sa, peerspi, prop->prop_protoid)) != NULL) { - rekeying = 1; - log_debug("%s: rekeying CHILD SA %s", __func__, - print_spi(peerspi, prop->prop_peerspi.spi_size)); + /* IKE SA rekeying */ + if (prop->prop_protoid == IKEV2_SAPROTO_IKE) { + if (sa->sa_next == NULL) { + log_debug("%s: missing IKE SA for rekeying", __func__); + return (-1); + } + /* Update the responder SPI */ + spi = &msg->msg_prop->prop_peerspi; + if ((nsa = sa_new(env, sa->sa_next->sa_hdr.sh_ispi, + spi->spi, 1, NULL)) == NULL || nsa != sa->sa_next) { + log_debug("%s: invalid rekey SA", __func__); + if (nsa) + sa_free(env, nsa); + sa_free(env, sa->sa_next); + sa->sa_next = NULL; + return (-1); + } + if (ikev2_sa_initiator(env, nsa, sa, msg) == -1) { + log_debug("%s: failed to get IKE keys", __func__); + return (-1); + } + sa->sa_stateflags &= ~IKED_REQ_CHILDSA; + sa->sa_next = NULL; + return (ikev2_ikesa_enable(env, sa, nsa)); + } + + /* Child SA rekeying */ + if (sa->sa_rekeyspi && + (csa = childsa_lookup(sa, sa->sa_rekeyspi, prop->prop_protoid)) + != NULL) { + log_debug("%s: rekeying CHILD SA old %s spi %s", __func__, + print_spi(csa->csa_spi.spi, csa->csa_spi.spi_size), + print_spi(prop->prop_peerspi.spi, + prop->prop_peerspi.spi_size)); + } + + /* check KE payload for PFS */ + if (ibuf_length(msg->msg_ke)) { + log_debug("%s: using PFS", __func__); + if (ikev2_sa_initiator_dh(sa, msg, prop->prop_protoid) < 0) { + log_debug("%s: failed to setup DH", __func__); + return (ret); + } + if (sa->sa_dhpeer == NULL) { + log_debug("%s: no peer DH", __func__); + return (ret); + } + pfs = 1; + /* XXX check group against policy ? */ + /* XXX should ikev2_sa_negotiate do this? */ } /* Update responder's nonce */ @@ -2280,12 +2886,13 @@ ikev2_init_create_child_sa(struct iked *env, struct iked_message *msg) ibuf_release(sa->sa_rnonce); sa->sa_rnonce = ibuf_dup(msg->msg_nonce); - if (ikev2_childsa_negotiate(env, sa, 1)) { + if (ikev2_childsa_negotiate(env, sa, &sa->sa_kex, &sa->sa_proposals, 1, + pfs)) { log_debug("%s: failed to get CHILD SAs", __func__); return (-1); } - if (rekeying) { + if (csa) { /* Child SA rekeying */ if ((buf = ibuf_static()) == NULL) @@ -2298,7 +2905,7 @@ ikev2_init_create_child_sa(struct iked *env, struct iked_message *msg) del->del_spisize = sizeof(spi32); del->del_nspi = htobe16(1); - spi32 = htobe32(csa->csa_peerspi); + spi32 = htobe32(csa->csa_spi.spi); if (ibuf_add(buf, &spi32, sizeof(spi32))) goto done; @@ -2306,10 +2913,16 @@ ikev2_init_create_child_sa(struct iked *env, struct iked_message *msg) IKEV2_EXCHANGE_INFORMATIONAL, 0)) goto done; - sa->sa_stateflags |= IKED_REQ_INF | IKED_REQ_DELETE; + sa->sa_stateflags |= IKED_REQ_INF; } ret = ikev2_childsa_enable(env, sa); + if ((ret == 0) && (csa)) { + log_info("LOG_SESSION: Rekeying secure session key peer %s \ + local %s", print_host((struct sockaddr *)&sa->sa_peer.addr, + NULL, 0), print_host((struct sockaddr *)&sa->sa_local.addr, + NULL, 0)); + } done: sa->sa_stateflags &= ~IKED_REQ_CHILDSA; @@ -2321,29 +2934,165 @@ ikev2_init_create_child_sa(struct iked *env, struct iked_message *msg) } int -ikev2_resp_create_child_sa(struct iked *env, struct iked_message *msg) +ikev2_ikesa_enable(struct iked *env, struct iked_sa *sa, struct iked_sa *nsa) { struct iked_childsa *csa, *nextcsa; struct iked_flow *flow, *nextflow; struct iked_proposal *prop, *nextprop; + + log_debug("%s: IKE SA %p ispi %s rspi %s replaced" + " by SA %p ispi %s rspi %s ", + __func__, sa, + print_spi(sa->sa_hdr.sh_ispi, 8), + print_spi(sa->sa_hdr.sh_rspi, 8), + nsa, + print_spi(nsa->sa_hdr.sh_ispi, 8), + print_spi(nsa->sa_hdr.sh_rspi, 8)); + + /* Transfer socket and NAT information */ + nsa->sa_fd = sa->sa_fd; + nsa->sa_natt = sa->sa_natt; + nsa->sa_udpencap = sa->sa_udpencap; + + /* Transfer all Child SAs and flows from the old IKE SA */ + for (flow = TAILQ_FIRST(&sa->sa_flows); flow != NULL; + flow = nextflow) { + nextflow = TAILQ_NEXT(flow, flow_entry); + TAILQ_REMOVE(&sa->sa_flows, flow, flow_entry); + TAILQ_INSERT_TAIL(&nsa->sa_flows, flow, + flow_entry); + flow->flow_ikesa = nsa; + flow->flow_local = &nsa->sa_local; + flow->flow_peer = &nsa->sa_peer; + } + for (csa = TAILQ_FIRST(&sa->sa_childsas); csa != NULL; + csa = nextcsa) { + nextcsa = TAILQ_NEXT(csa, csa_entry); + TAILQ_REMOVE(&sa->sa_childsas, csa, csa_entry); + TAILQ_INSERT_TAIL(&nsa->sa_childsas, csa, + csa_entry); + csa->csa_ikesa = nsa; + if (csa->csa_dir == IPSP_DIRECTION_IN) { + csa->csa_local = &nsa->sa_peer; + csa->csa_peer = &nsa->sa_local; + } else { + csa->csa_local = &nsa->sa_local; + csa->csa_peer = &nsa->sa_peer; + } + } + /* Transfer all non-IKE proposals */ + for (prop = TAILQ_FIRST(&sa->sa_proposals); prop != NULL; + prop = nextprop) { + nextprop = TAILQ_NEXT(prop, prop_entry); + if (prop->prop_protoid == IKEV2_SAPROTO_IKE) + continue; + TAILQ_REMOVE(&sa->sa_proposals, prop, prop_entry); + TAILQ_INSERT_TAIL(&nsa->sa_proposals, prop, + prop_entry); + } + + /* Preserve ID information */ + if (sa->sa_hdr.sh_initiator == nsa->sa_hdr.sh_initiator) { + nsa->sa_iid = sa->sa_iid; + nsa->sa_rid = sa->sa_rid; + } else { + /* initiator and responder role swapped */ + nsa->sa_iid = sa->sa_rid; + nsa->sa_rid = sa->sa_iid; + } + /* duplicate the actual buffer */ + nsa->sa_iid.id_buf = ibuf_dup(nsa->sa_iid.id_buf); + nsa->sa_rid.id_buf = ibuf_dup(nsa->sa_rid.id_buf); + + /* Transfer sa_addrpool address */ + if (sa->sa_addrpool) { + RB_REMOVE(iked_addrpool, &env->sc_addrpool, sa); + nsa->sa_addrpool = sa->sa_addrpool; + sa->sa_addrpool = NULL; + RB_INSERT(iked_addrpool, &env->sc_addrpool, nsa); + } + if (sa->sa_addrpool6) { + RB_REMOVE(iked_addrpool6, &env->sc_addrpool6, sa); + nsa->sa_addrpool6 = sa->sa_addrpool6; + sa->sa_addrpool6 = NULL; + RB_INSERT(iked_addrpool6, &env->sc_addrpool6, nsa); + } + + log_debug("%s: activating new IKE SA", __func__); + sa_state(env, nsa, IKEV2_STATE_ESTABLISHED); + timer_set(env, &nsa->sa_timer, ikev2_ike_sa_alive, nsa); + timer_add(env, &nsa->sa_timer, IKED_IKE_SA_ALIVE_TIMEOUT); + timer_set(env, &nsa->sa_rekey, ikev2_ike_sa_rekey, nsa); + if (nsa->sa_policy->pol_rekey) + timer_add(env, &nsa->sa_rekey, nsa->sa_policy->pol_rekey); + nsa->sa_stateflags = nsa->sa_statevalid; /* XXX */ + + /* unregister DPD keep alive timer & rekey first */ + if (sa->sa_state == IKEV2_STATE_ESTABLISHED) { + timer_del(env, &sa->sa_rekey); + timer_del(env, &sa->sa_timer); + } + + ikev2_ikesa_delete(env, sa, nsa->sa_hdr.sh_initiator); + return (0); +} + +void +ikev2_ikesa_delete(struct iked *env, struct iked_sa *sa, int initiator) +{ + struct ibuf *buf = NULL; + struct ikev2_delete *del; + + if (initiator) { + /* Send PAYLOAD_DELETE */ + if ((buf = ibuf_static()) == NULL) + goto done; + if ((del = ibuf_advance(buf, sizeof(*del))) == NULL) + goto done; + del->del_protoid = IKEV2_SAPROTO_IKE; + del->del_spisize = 0; + del->del_nspi = 0; + if (ikev2_send_ike_e(env, sa, buf, IKEV2_PAYLOAD_DELETE, + IKEV2_EXCHANGE_INFORMATIONAL, 0) == -1) + goto done; + log_debug("%s: sent delete, closing SA", __func__); +done: + ibuf_release(buf); + sa_state(env, sa, IKEV2_STATE_CLOSED); + } else { + sa_state(env, sa, IKEV2_STATE_CLOSING); + } + + /* Remove IKE-SA after timeout, e.g. if we don't get a delete */ + timer_set(env, &sa->sa_timer, ikev2_ike_sa_timeout, sa); + timer_add(env, &sa->sa_timer, IKED_IKE_SA_DELETE_TIMEOUT); +} + +int +ikev2_resp_create_child_sa(struct iked *env, struct iked_message *msg) +{ + struct iked_childsa *csa; + struct iked_proposal *prop; + struct iked_proposals proposals; + struct iked_kex *kex, *kextmp = NULL; struct iked_sa *nsa = NULL, *sa = msg->msg_sa; struct iked_spi *spi, *rekey = &msg->msg_rekey; struct ikev2_keyexchange *ke; - struct ikev2_notify *n; - struct ikev2_payload *pld; - struct ibuf *buf = NULL, *e = NULL, *nonce = NULL; - struct group *group; - u_int64_t spi64; - u_int32_t spi32; + struct ikev2_payload *pld = NULL; + struct ibuf *e = NULL, *nonce = NULL; + uint8_t firstpayload; ssize_t len = 0; int initiator, protoid, rekeying = 1; int ret = -1; + int pfs = 0; initiator = sa->sa_hdr.sh_initiator ? 1 : 0; if (!ikev2_msg_frompeer(msg) || msg->msg_prop == NULL) return (0); + TAILQ_INIT(&proposals); + if ((protoid = rekey->spi_protoid) == 0) { /* * If REKEY_SA notification is not present, then it's either @@ -2389,45 +3138,42 @@ ikev2_resp_create_child_sa(struct iked *env, struct iked_message *msg) sa_state(env, nsa, IKEV2_STATE_AUTH_SUCCESS); nonce = nsa->sa_rnonce; + kex = &nsa->sa_kex; } else { /* Child SA creating/rekeying */ + if ((kex = kextmp = calloc(1, sizeof(*kextmp))) == NULL) { + log_debug("%s: calloc kex", __func__); + goto fail; + } + + if (ikev2_sa_negotiate(&proposals, + &sa->sa_policy->pol_proposals, &msg->msg_proposals) != 0) { + log_debug("%s: no proposal chosen", __func__); + goto fail; + } + + /* check KE payload for PFS */ if (ibuf_length(msg->msg_parent->msg_ke)) { - /* We do not support KE payload yet */ - if ((buf = ibuf_static()) == NULL) - return (-1); - if ((n = ibuf_advance(buf, sizeof(*n))) == NULL) { - ibuf_release(buf); - return (-1); - } - n->n_protoid = protoid; - n->n_spisize = rekey->spi_size; - n->n_type = htobe16(IKEV2_N_NO_ADDITIONAL_SAS); - switch (rekey->spi_size) { - case 4: - spi32 = htobe32(rekey->spi); - ibuf_add(buf, &spi32, rekey->spi_size); - break; - case 8: - spi64 = htobe64(rekey->spi); - ibuf_add(buf, &spi64, rekey->spi_size); - break; + log_debug("%s: using PFS", __func__); + if (ikev2_sa_responder_dh(kex, &proposals, + msg->msg_parent, protoid) < 0) { + log_debug("%s: failed to setup DH", __func__); + goto fail; } - ikev2_send_ike_e(env, sa, buf, IKEV2_PAYLOAD_NOTIFY, - IKEV2_EXCHANGE_CREATE_CHILD_SA, 1); - ibuf_release(buf); - return (0); + pfs = 1; + /* XXX check group against policy ? */ } /* Update peer SPI */ - TAILQ_FOREACH(prop, &sa->sa_proposals, prop_entry) { + TAILQ_FOREACH(prop, &proposals, prop_entry) { if (prop->prop_protoid == protoid) break; } if (prop == NULL) { log_debug("%s: failed to find %s proposals", __func__, print_map(protoid, ikev2_saproto_map)); - return (-1); + goto fail; } else prop->prop_peerspi = msg->msg_prop->prop_peerspi; @@ -2438,13 +3184,13 @@ ikev2_resp_create_child_sa(struct iked *env, struct iked_message *msg) log_debug("%s: CHILD SA %s wasn't found", __func__, print_spi(rekey->spi, rekey->spi_size)); - return (-1); + goto fail; } if (!csa->csa_loaded || !csa->csa_peersa || !csa->csa_peersa->csa_loaded) { log_debug("%s: SA is not loaded or no peer SA", __func__); - return (-1); + goto fail; } csa->csa_rekey = 1; csa->csa_peersa->csa_rekey = 1; @@ -2453,34 +3199,53 @@ ikev2_resp_create_child_sa(struct iked *env, struct iked_message *msg) /* Update initiator's nonce */ if (!ibuf_length(msg->msg_nonce)) { log_debug("%s: initiator didn't send nonce", __func__); - return (-1); + goto fail; } - ibuf_release(sa->sa_inonce); - sa->sa_inonce = ibuf_dup(msg->msg_nonce); + ibuf_release(kex->kex_inonce); + kex->kex_inonce = ibuf_dup(msg->msg_nonce); /* Generate new responder's nonce */ if ((nonce = ibuf_random(IKED_NONCE_SIZE)) == NULL) - return (-1); + goto fail; /* Update responder's nonce */ - ibuf_release(sa->sa_rnonce); - sa->sa_rnonce = nonce; + ibuf_release(kex->kex_rnonce); + kex->kex_rnonce = nonce; - if (ikev2_childsa_negotiate(env, sa, 0)) { + if (ikev2_childsa_negotiate(env, sa, kex, &proposals, 0, pfs)) { log_debug("%s: failed to get CHILD SAs", __func__); - return (-1); + goto fail; } } if ((e = ibuf_static()) == NULL) goto done; + len = 0; + + /* compression (unless IKE rekeying) */ + if (!nsa && sa->sa_ipcomp && + (len = ikev2_add_ipcompnotify(env, e, &pld, len, sa)) == -1) + goto done; + + /* Transport mode IPSec (unless IKE rekeying). */ + if (!nsa && sa->sa_transport && + (len = ikev2_add_transportnotify(env, e, &pld, len, sa)) == -1) + goto done; + + if (pld) { + firstpayload = IKEV2_PAYLOAD_NOTIFY; + if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_SA) == -1) + goto done; + } else + firstpayload = IKEV2_PAYLOAD_SA; + /* SA payload */ if ((pld = ikev2_add_payload(e)) == NULL) goto done; if ((len = ikev2_add_proposals(env, nsa ? nsa : sa, e, - nsa ? &nsa->sa_proposals : &sa->sa_proposals, + nsa ? &nsa->sa_proposals : &proposals, protoid, 0, nsa ? 1 : 0)) == -1) goto done; @@ -2494,7 +3259,8 @@ ikev2_resp_create_child_sa(struct iked *env, struct iked_message *msg) goto done; len = ibuf_size(nonce); - if (protoid == IKEV2_SAPROTO_IKE) { + if (protoid == IKEV2_SAPROTO_IKE || pfs) { + if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_KE) == -1) goto done; @@ -2503,74 +3269,44 @@ ikev2_resp_create_child_sa(struct iked *env, struct iked_message *msg) goto done; if ((ke = ibuf_advance(e, sizeof(*ke))) == NULL) goto done; - if ((group = nsa->sa_dhgroup) == NULL) { + if (kex->kex_dhgroup == NULL) { log_debug("%s: invalid dh", __func__); goto done; } - ke->kex_dhgroup = htobe16(group->id); - if (ikev2_add_buf(e, nsa->sa_dhrexchange) == -1) + ke->kex_dhgroup = htobe16(kex->kex_dhgroup->id); + if (ikev2_add_buf(e, kex->kex_dhrexchange) == -1) goto done; - len = sizeof(*ke) + dh_getlen(group); - } else { + len = sizeof(*ke) + dh_getlen(kex->kex_dhgroup); + } + + if (protoid != IKEV2_SAPROTO_IKE) if ((len = ikev2_add_ts(e, &pld, len, sa, initiator)) == -1) goto done; - } if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONE) == -1) goto done; if ((ret = ikev2_msg_send_encrypt(env, sa, &e, - IKEV2_EXCHANGE_CREATE_CHILD_SA, IKEV2_PAYLOAD_SA, 1)) == -1) + IKEV2_EXCHANGE_CREATE_CHILD_SA, firstpayload, 1)) == -1) goto done; - if (protoid == IKEV2_SAPROTO_IKE) { - /* Transfer all Child SAs and flows from the old IKE SA */ - for (flow = TAILQ_FIRST(&sa->sa_flows); flow != NULL; - flow = nextflow) { - nextflow = TAILQ_NEXT(flow, flow_entry); - TAILQ_REMOVE(&sa->sa_flows, flow, flow_entry); - TAILQ_INSERT_TAIL(&nsa->sa_flows, flow, - flow_entry); - flow->flow_ikesa = nsa; - } - for (csa = TAILQ_FIRST(&sa->sa_childsas); csa != NULL; - csa = nextcsa) { - nextcsa = TAILQ_NEXT(csa, csa_entry); - TAILQ_REMOVE(&sa->sa_childsas, csa, csa_entry); - TAILQ_INSERT_TAIL(&nsa->sa_childsas, csa, - csa_entry); - csa->csa_ikesa = nsa; - } - /* Transfer all non-IKE proposals */ - for (prop = TAILQ_FIRST(&sa->sa_proposals); prop != NULL; - prop = nextprop) { - nextprop = TAILQ_NEXT(prop, prop_entry); - if (prop->prop_protoid == IKEV2_SAPROTO_IKE) - continue; - TAILQ_REMOVE(&sa->sa_proposals, prop, prop_entry); - TAILQ_INSERT_TAIL(&nsa->sa_proposals, prop, - prop_entry); - } - /* Preserve ID information */ - nsa->sa_iid = sa->sa_iid; - nsa->sa_iid.id_buf = ibuf_dup(sa->sa_iid.id_buf); - nsa->sa_rid = sa->sa_rid; - nsa->sa_rid.id_buf = ibuf_dup(sa->sa_rid.id_buf); - - log_debug("%s: activating new IKE SA", __func__); - sa_state(env, nsa, IKEV2_STATE_ESTABLISHED); - nsa->sa_stateflags = sa->sa_statevalid; /* XXX */ - - timer_initialize(env, &sa->sa_timer, ikev2_ike_sa_timeout, sa); - timer_register(env, &sa->sa_timer, IKED_IKE_SA_REKEY_TIMEOUT); - } else + if (protoid == IKEV2_SAPROTO_IKE) + ret = ikev2_ikesa_enable(env, sa, nsa); + else ret = ikev2_childsa_enable(env, sa); done: if (ret && protoid != IKEV2_SAPROTO_IKE) ikev2_childsa_delete(env, sa, 0, 0, NULL, 1); ibuf_release(e); + config_free_proposals(&proposals, 0); + config_free_kex(kextmp); return (ret); + + fail: + config_free_proposals(&proposals, 0); + config_free_kex(kextmp); + return (-1); } void @@ -2582,6 +3318,55 @@ ikev2_ike_sa_timeout(struct iked *env, void *arg) sa_free(env, sa); } +void +ikev2_ike_sa_alive(struct iked *env, void *arg) +{ + struct iked_sa *sa = arg; + struct iked_childsa *csa = NULL; + struct timeval tv; + uint64_t last_used, diff; + int foundin = 0, foundout = 0; + + /* check for incoming traffic on any child SA */ + TAILQ_FOREACH(csa, &sa->sa_childsas, csa_entry) { + if (!csa->csa_loaded || + csa->csa_saproto == IKEV2_SAPROTO_IPCOMP) + continue; + gettimeofday(&tv, NULL); +#if defined(_OPENBSD_IPSEC_API_VERSION) + if (pfkey_sa_last_used(env->sc_pfkey, csa, &last_used) != 0) + continue; +#else + last_used = tv.tv_sec; /* XXX */ +#endif + diff = (u_int32_t)(tv.tv_sec - last_used); + log_debug("%s: %s CHILD SA spi %s last used %llu second(s) ago", + __func__, + csa->csa_dir == IPSP_DIRECTION_IN ? "incoming" : "outgoing", + print_spi(csa->csa_spi.spi, csa->csa_spi.spi_size), + (unsigned long long)diff); + if (diff < IKED_IKE_SA_ALIVE_TIMEOUT) { + if (csa->csa_dir == IPSP_DIRECTION_IN) { + foundin = 1; + break; + } else { + foundout = 1; + } + } + } + + /* send probe if any outging SA has been used, but no incoming SA */ + if (!foundin && foundout) { + log_debug("%s: sending alive check", __func__); + ikev2_send_ike_e(env, sa, NULL, IKEV2_PAYLOAD_NONE, + IKEV2_EXCHANGE_INFORMATIONAL, 0); + sa->sa_stateflags |= IKED_REQ_INF; + } + + /* re-register */ + timer_add(env, &sa->sa_timer, IKED_IKE_SA_ALIVE_TIMEOUT); +} + int ikev2_send_informational(struct iked *env, struct iked_message *msg) { @@ -2696,10 +3481,10 @@ ikev2_send_informational(struct iked *env, struct iked_message *msg) } ssize_t -ikev2_psk(struct iked_sa *sa, u_int8_t *data, size_t length, - u_int8_t **pskptr) +ikev2_psk(struct iked_sa *sa, uint8_t *data, size_t length, + uint8_t **pskptr) { - u_int8_t *psk; + uint8_t *psk; size_t psklen = -1; if (hash_setkey(sa->sa_prf, data, length) == NULL) @@ -2721,8 +3506,8 @@ ikev2_match_proposals(struct iked_proposal *local, struct iked_proposal *peer, struct iked_transform **xforms) { struct iked_transform *tpeer, *tlocal; - u_int i, j, type, score; - u_int8_t protoid = peer->prop_protoid; + unsigned int i, j, type, score; + uint8_t protoid = peer->prop_protoid; for (i = 0; i < peer->prop_nxforms; i++) { tpeer = peer->prop_xforms + i; @@ -2778,16 +3563,17 @@ ikev2_match_proposals(struct iked_proposal *local, struct iked_proposal *peer, } int -ikev2_sa_negotiate(struct iked_sa *sa, struct iked_proposals *local, +ikev2_sa_negotiate(struct iked_proposals *result, struct iked_proposals *local, struct iked_proposals *peer) { struct iked_proposal *ppeer = NULL, *plocal, *prop, vpeer, vlocal; - struct iked_transform *chosen[IKEV2_XFORMTYPE_MAX]; + struct iked_transform chosen[IKEV2_XFORMTYPE_MAX]; + struct iked_transform *valid[IKEV2_XFORMTYPE_MAX]; struct iked_transform *match[IKEV2_XFORMTYPE_MAX]; - u_int i, score, chosen_score = 0; - u_int8_t protoid = 0; + unsigned int i, score, chosen_score = 0; + uint8_t protoid = 0; - bzero(chosen, sizeof(chosen)); + bzero(valid, sizeof(valid)); bzero(&vlocal, sizeof(vlocal)); bzero(&vpeer, sizeof(vpeer)); @@ -2806,8 +3592,11 @@ ikev2_sa_negotiate(struct iked_sa *sa, struct iked_proposals *local, log_debug("%s: score %d", __func__, score); if (score && (!chosen_score || score < chosen_score)) { chosen_score = score; - for (i = 0; i < IKEV2_XFORMTYPE_MAX; i++) - chosen[i] = match[i]; + for (i = 0; i < IKEV2_XFORMTYPE_MAX; i++) { + if ((valid[i] = match[i])) + memcpy(&chosen[i], match[i], + sizeof(chosen[0])); + } memcpy(&vpeer, ppeer, sizeof(vpeer)); memcpy(&vlocal, plocal, sizeof(vlocal)); } @@ -2818,12 +3607,11 @@ ikev2_sa_negotiate(struct iked_sa *sa, struct iked_proposals *local, if (chosen_score == 0) return (-1); - else if (sa == NULL) + else if (result == NULL) return (0); - (void)config_free_proposals(&sa->sa_proposals, vpeer.prop_protoid); - prop = config_add_proposal(&sa->sa_proposals, vpeer.prop_id, - vpeer.prop_protoid); + (void)config_free_proposals(result, vpeer.prop_protoid); + prop = config_add_proposal(result, vpeer.prop_id, vpeer.prop_protoid); if (vpeer.prop_localspi.spi_size) { prop->prop_localspi.spi_size = vpeer.prop_localspi.spi_size; @@ -2835,18 +3623,18 @@ ikev2_sa_negotiate(struct iked_sa *sa, struct iked_proposals *local, } for (i = 0; i < IKEV2_XFORMTYPE_MAX; i++) { - if (chosen[i] == NULL) + if (valid[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); + 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) + if (config_add_transform(prop, chosen[i].xform_type, + chosen[i].xform_id, chosen[i].xform_length, + chosen[i].xform_keylength) == NULL) break; } @@ -2854,15 +3642,15 @@ ikev2_sa_negotiate(struct iked_sa *sa, struct iked_proposals *local, } int -ikev2_sa_initiator(struct iked *env, struct iked_sa *sa, - struct iked_message *msg) +ikev2_sa_initiator_dh(struct iked_sa *sa, struct iked_message *msg, + unsigned int proto) { struct iked_policy *pol = sa->sa_policy; struct iked_transform *xform; if (sa->sa_dhgroup == NULL) { if ((xform = config_findtransform(&pol->pol_proposals, - IKEV2_XFORMTYPE_DH)) == NULL) { + IKEV2_XFORMTYPE_DH, proto)) == NULL) { log_debug("%s: did not find dh transform", __func__); return (-1); } @@ -2887,28 +3675,10 @@ ikev2_sa_initiator(struct iked *env, struct iked_sa *sa, } } - if (!ibuf_length(sa->sa_inonce)) { - if ((sa->sa_inonce = ibuf_random(IKED_NONCE_SIZE)) == NULL) { - log_debug("%s: failed to get local nonce", __func__); - return (-1); - } - } - /* Initial message */ if (msg == NULL) return (0); - if (!ibuf_length(sa->sa_rnonce)) { - if (!ibuf_length(msg->msg_nonce)) { - log_debug("%s: invalid peer nonce", __func__); - return (-1); - } - if ((sa->sa_rnonce = ibuf_dup(msg->msg_nonce)) == NULL) { - log_debug("%s: failed to get peer nonce", __func__); - return (-1); - } - } - if (!ibuf_length(sa->sa_dhrexchange)) { if (!ibuf_length(msg->msg_ke)) { log_debug("%s: invalid peer dh exchange", __func__); @@ -2928,10 +3698,43 @@ ikev2_sa_initiator(struct iked *env, struct iked_sa *sa, /* Set a pointer to the peer exchange */ sa->sa_dhpeer = sa->sa_dhrexchange; + return (0); +} + +int +ikev2_sa_initiator(struct iked *env, struct iked_sa *sa, + struct iked_sa *osa, struct iked_message *msg) +{ + struct iked_transform *xform; + + if (ikev2_sa_initiator_dh(sa, msg, 0) < 0) + return (-1); + + if (!ibuf_length(sa->sa_inonce)) { + if ((sa->sa_inonce = ibuf_random(IKED_NONCE_SIZE)) == NULL) { + log_debug("%s: failed to get local nonce", __func__); + return (-1); + } + } + + /* Initial message */ + if (msg == NULL) + return (0); + + if (!ibuf_length(sa->sa_rnonce)) { + if (!ibuf_length(msg->msg_nonce)) { + log_debug("%s: invalid peer nonce", __func__); + return (-1); + } + if ((sa->sa_rnonce = ibuf_dup(msg->msg_nonce)) == NULL) { + log_debug("%s: failed to get peer nonce", __func__); + return (-1); + } + } /* XXX we need a better way to get this */ - if (ikev2_sa_negotiate(sa, &msg->msg_policy->pol_proposals, - &msg->msg_proposals) != 0) { + if (ikev2_sa_negotiate(&sa->sa_proposals, + &msg->msg_policy->pol_proposals, &msg->msg_proposals) != 0) { log_debug("%s: no proposal chosen", __func__); msg->msg_error = IKEV2_N_NO_PROPOSAL_CHOSEN; return (-1); @@ -2940,7 +3743,7 @@ ikev2_sa_initiator(struct iked *env, struct iked_sa *sa, if (sa->sa_encr == NULL) { if ((xform = config_findtransform(&sa->sa_proposals, - IKEV2_XFORMTYPE_ENCR)) == NULL) { + IKEV2_XFORMTYPE_ENCR, 0)) == NULL) { log_debug("%s: did not find encr transform", __func__); return (-1); } @@ -2953,7 +3756,7 @@ ikev2_sa_initiator(struct iked *env, struct iked_sa *sa, if (sa->sa_prf == NULL) { if ((xform = config_findtransform(&sa->sa_proposals, - IKEV2_XFORMTYPE_PRF)) == NULL) { + IKEV2_XFORMTYPE_PRF, 0)) == NULL) { log_debug("%s: did not find prf transform", __func__); return (-1); } @@ -2964,29 +3767,77 @@ ikev2_sa_initiator(struct iked *env, struct iked_sa *sa, } } - 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__); + if (sa->sa_integr == NULL) { + if ((xform = config_findtransform(&sa->sa_proposals, + IKEV2_XFORMTYPE_INTEGR, 0)) == 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); + } + } + + ibuf_release(sa->sa_2ndmsg); + if ((sa->sa_2ndmsg = ibuf_dup(msg->msg_data)) == NULL) { + log_debug("%s: failed to copy 2nd message", __func__); + return (-1); + } + + return (ikev2_sa_keys(env, sa, osa ? osa->sa_key_d : NULL)); +} + +int +ikev2_sa_responder_dh(struct iked_kex *kex, struct iked_proposals *proposals, + struct iked_message *msg, unsigned int proto) +{ + struct iked_transform *xform; + + if (kex->kex_dhgroup == NULL) { + if ((xform = config_findtransform(proposals, + IKEV2_XFORMTYPE_DH, proto)) == NULL) { + log_debug("%s: did not find dh transform", __func__); + return (-1); + } + if ((kex->kex_dhgroup = + group_get(xform->xform_id)) == NULL) { + log_debug("%s: invalid dh %d", __func__, + xform->xform_id); + return (-1); + } + } + + if (!ibuf_length(kex->kex_dhrexchange)) { + if ((kex->kex_dhrexchange = ibuf_new(NULL, + dh_getlen(kex->kex_dhgroup))) == NULL) { + log_debug("%s: failed to alloc dh exchange", __func__); return (-1); } - if ((sa->sa_integr = - hash_new(xform->xform_type, xform->xform_id)) == NULL) { - log_debug("%s: failed to get integr", __func__); + if (dh_create_exchange(kex->kex_dhgroup, + kex->kex_dhrexchange->buf) == -1) { + log_debug("%s: failed to get dh exchange", __func__); return (-1); } } - ibuf_release(sa->sa_2ndmsg); - if ((sa->sa_2ndmsg = ibuf_dup(msg->msg_data)) == NULL) { - log_debug("%s: failed to copy 2nd message", __func__); - return (-1); + if (!ibuf_length(kex->kex_dhiexchange)) { + if ((kex->kex_dhiexchange = ibuf_dup(msg->msg_ke)) == NULL || + ((ssize_t)ibuf_length(kex->kex_dhiexchange) != + dh_getlen(kex->kex_dhgroup))) { + /* XXX send notification to peer */ + log_debug("%s: invalid dh, size %d", __func__, + dh_getlen(kex->kex_dhgroup) * 8); + return (-1); + } } - return (ikev2_sa_keys(env, sa, NULL)); + /* Set a pointer to the peer exchange */ + kex->kex_dhpeer = kex->kex_dhiexchange; + return (0); } - int ikev2_sa_responder(struct iked *env, struct iked_sa *sa, struct iked_sa *osa, struct iked_message *msg) @@ -3015,8 +3866,8 @@ ikev2_sa_responder(struct iked *env, struct iked_sa *sa, struct iked_sa *osa, } /* XXX we need a better way to get this */ - if (ikev2_sa_negotiate(sa, &msg->msg_policy->pol_proposals, - &msg->msg_proposals) != 0) { + if (ikev2_sa_negotiate(&sa->sa_proposals, + &msg->msg_policy->pol_proposals, &msg->msg_proposals) != 0) { log_debug("%s: no proposal chosen", __func__); msg->msg_error = IKEV2_N_NO_PROPOSAL_CHOSEN; return (-1); @@ -3025,7 +3876,7 @@ ikev2_sa_responder(struct iked *env, struct iked_sa *sa, struct iked_sa *osa, if (sa->sa_encr == NULL) { if ((xform = config_findtransform(&sa->sa_proposals, - IKEV2_XFORMTYPE_ENCR)) == NULL) { + IKEV2_XFORMTYPE_ENCR, 0)) == NULL) { log_debug("%s: did not find encr transform", __func__); return (-1); } @@ -3038,7 +3889,7 @@ ikev2_sa_responder(struct iked *env, struct iked_sa *sa, struct iked_sa *osa, if (sa->sa_prf == NULL) { if ((xform = config_findtransform(&sa->sa_proposals, - IKEV2_XFORMTYPE_PRF)) == NULL) { + IKEV2_XFORMTYPE_PRF, 0)) == NULL) { log_debug("%s: did not find prf transform", __func__); return (-1); } @@ -3051,7 +3902,7 @@ ikev2_sa_responder(struct iked *env, struct iked_sa *sa, struct iked_sa *osa, if (sa->sa_integr == NULL) { if ((xform = config_findtransform(&sa->sa_proposals, - IKEV2_XFORMTYPE_INTEGR)) == NULL) { + IKEV2_XFORMTYPE_INTEGR, 0)) == NULL) { log_debug("%s: did not find integr transform", __func__); return (-1); @@ -3063,45 +3914,8 @@ ikev2_sa_responder(struct iked *env, struct iked_sa *sa, struct iked_sa *osa, } } - 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 (!ibuf_length(sa->sa_dhrexchange)) { - if ((sa->sa_dhrexchange = ibuf_new(NULL, - dh_getlen(sa->sa_dhgroup))) == NULL) { - log_debug("%s: failed to alloc dh exchange", __func__); - return (-1); - } - if (dh_create_exchange(sa->sa_dhgroup, - sa->sa_dhrexchange->buf) == -1) { - log_debug("%s: failed to get dh exchange", __func__); - return (-1); - } - } - - if (!ibuf_length(sa->sa_dhiexchange)) { - if ((sa->sa_dhiexchange = ibuf_dup(msg->msg_ke)) == NULL || - ((ssize_t)ibuf_length(sa->sa_dhiexchange) != - dh_getlen(sa->sa_dhgroup))) { - /* XXX send notification to peer */ - log_debug("%s: invalid dh, size %d", __func__, - dh_getlen(sa->sa_dhgroup) * 8); - return (-1); - } - } - - /* Set a pointer to the peer exchange */ - sa->sa_dhpeer = sa->sa_dhiexchange; + if (ikev2_sa_responder_dh(&sa->sa_kex, &sa->sa_proposals, msg, 0) < 0) + return (-1); return (ikev2_sa_keys(env, sa, osa ? osa->sa_key_d : NULL)); } @@ -3114,7 +3928,7 @@ ikev2_sa_keys(struct iked *env, struct iked_sa *sa, struct ibuf *key) struct group *group; struct ibuf *ninr, *dhsecret, *skeyseed, *s, *t; size_t nonceminlen, ilen, rlen, tmplen; - u_int64_t ispi, rspi; + uint64_t ispi, rspi; int ret = -1; ninr = dhsecret = skeyseed = s = t = NULL; @@ -3171,7 +3985,7 @@ ikev2_sa_keys(struct iked *env, struct iked_sa *sa, struct ibuf *key) if (dh_create_shared(group, dhsecret->buf, sa->sa_dhpeer->buf) == -1) { log_debug("%s: failed to get dh secret" - " group %d len %d secret %d exchange %d", __func__, + " group %d len %d secret %zu exchange %zu", __func__, group->id, dh_getlen(group), ibuf_length(dhsecret), ibuf_length(sa->sa_dhpeer)); goto done; @@ -3215,7 +4029,7 @@ ikev2_sa_keys(struct iked *env, struct iked_sa *sa, struct ibuf *key) hash_update(prf, dhsecret->buf, ibuf_length(dhsecret)); hash_final(prf, skeyseed->buf, &tmplen); - log_debug("%s: SKEYSEED with %d bytes", __func__, tmplen); + log_debug("%s: SKEYSEED with %zu bytes", __func__, tmplen); print_hex(skeyseed->buf, 0, tmplen); if (ibuf_setsize(skeyseed, tmplen) == -1) { @@ -3243,7 +4057,7 @@ ikev2_sa_keys(struct iked *env, struct iked_sa *sa, struct ibuf *key) goto done; } - log_debug("%s: S with %d bytes", __func__, ibuf_length(s)); + log_debug("%s: S with %zu bytes", __func__, ibuf_length(s)); print_hex(s->buf, 0, ibuf_length(s)); /* @@ -3275,25 +4089,25 @@ ikev2_sa_keys(struct iked *env, struct iked_sa *sa, struct ibuf *key) goto done; } - log_debug("%s: SK_d with %d bytes", __func__, + log_debug("%s: SK_d with %zu 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__, + log_debug("%s: SK_ai with %zu 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__, + log_debug("%s: SK_ar with %zu 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__, + log_debug("%s: SK_ei with %zu 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__, + log_debug("%s: SK_er with %zu 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__, + log_debug("%s: SK_pi with %zu 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__, + log_debug("%s: SK_pr with %zu bytes", __func__, ibuf_length(sa->sa_key_rprf)); print_hex(sa->sa_key_rprf->buf, 0, ibuf_length(sa->sa_key_rprf)); @@ -3309,13 +4123,24 @@ ikev2_sa_keys(struct iked *env, struct iked_sa *sa, struct ibuf *key) return (ret); } +void +ikev2_sa_cleanup_dh(struct iked_sa *sa) +{ + ibuf_release(sa->sa_dhiexchange); + ibuf_release(sa->sa_dhrexchange); + group_free(sa->sa_dhgroup); + sa->sa_dhiexchange = NULL; + sa->sa_dhrexchange = NULL; + sa->sa_dhgroup = NULL; +} + 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; + uint8_t pad = 0; /* * prf+ (K, S) = T1 | T2 | T3 | T4 | ... @@ -3362,12 +4187,12 @@ ikev2_prfplus(struct iked_hash *prf, struct ibuf *key, struct ibuf *seed, ibuf_release(t2); ibuf_add(t, t1->buf, ibuf_length(t1)); - log_debug("%s: T%d with %d bytes", __func__, + log_debug("%s: T%d with %zu 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)); + log_debug("%s: Tn with %zu bytes", __func__, ibuf_length(t)); print_hex(t->buf, 0, ibuf_length(t)); ibuf_release(t1); @@ -3389,8 +4214,7 @@ ikev2_sa_tag(struct iked_sa *sa, struct iked_id *id) int ret = -1; size_t len; - if (sa->sa_tag != NULL) - free(sa->sa_tag); + free(sa->sa_tag); sa->sa_tag = NULL; format = sa->sa_policy->pol_tag; @@ -3453,7 +4277,7 @@ ikev2_sa_tag(struct iked_sa *sa, struct iked_id *id) } } - log_debug("%s: %s (%d)", __func__, sa->sa_tag, strlen(sa->sa_tag)); + log_debug("%s: %s (%zu)", __func__, sa->sa_tag, strlen(sa->sa_tag)); ret = 0; fail: @@ -3466,35 +4290,29 @@ ikev2_sa_tag(struct iked_sa *sa, struct iked_id *id) } int -ikev2_childsa_negotiate(struct iked *env, struct iked_sa *sa, int initiator) +ikev2_childsa_negotiate(struct iked *env, struct iked_sa *sa, + struct iked_kex *kex, struct iked_proposals *proposals, int initiator, + int pfs) { struct iked_proposal *prop; struct iked_transform *xform, *encrxf = NULL, *integrxf = NULL; struct iked_childsa *csa, *csb; struct iked_flow *flow, *saflow, *flowa, *flowb; - struct iked_id *peerid, *localid; - struct ibuf *keymat = NULL, *seed = NULL; - u_int32_t spi = 0; - u_int i; + struct ibuf *keymat = NULL, *seed = NULL, *dhsecret = NULL; + struct group *group; + uint32_t spi = 0; + unsigned int i; size_t ilen = 0; int esn, skip, ret = -1; if (!sa_stateok(sa, IKEV2_STATE_VALID)) return (-1); - if (sa->sa_hdr.sh_initiator) { - peerid = &sa->sa_rid; - localid = &sa->sa_iid; - } else { - peerid = &sa->sa_iid; - localid = &sa->sa_rid; - } - - if (ikev2_sa_tag(sa, peerid) == -1) + if (ikev2_sa_tag(sa, IKESA_DSTID(sa)) == -1) return (-1); /* We need to determine the key material length first */ - TAILQ_FOREACH(prop, &sa->sa_proposals, prop_entry) { + TAILQ_FOREACH(prop, proposals, prop_entry) { if (prop->prop_protoid == IKEV2_SAPROTO_IKE) continue; log_debug("%s: proposal %d", __func__, prop->prop_id); @@ -3522,10 +4340,40 @@ ikev2_childsa_negotiate(struct iked *env, struct iked_sa *sa, int initiator) /* double key material length for inbound/outbound */ ilen *= 2; - log_debug("%s: key material length %d", __func__, ilen); + log_debug("%s: key material length %zu", __func__, ilen); - if ((seed = ibuf_dup(sa->sa_inonce)) == NULL || - ibuf_cat(seed, sa->sa_rnonce) != 0 || + if ((seed = ibuf_new(NULL, 0)) == NULL) { + log_debug("%s: failed to setup IKE SA key material", __func__); + goto done; + } + if (pfs) { + log_debug("%s: using PFS", __func__); + if (kex->kex_dhpeer == NULL || + ibuf_length(kex->kex_dhpeer) == 0 || + (group = kex->kex_dhgroup) == NULL) { + log_debug("%s: no dh group for pfs", __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, + kex->kex_dhpeer->buf) == -1) { + log_debug("%s: failed to get dh secret" + " group %d len %d secret %zu exchange %zu", + __func__, group->id, dh_getlen(group), + ibuf_length(dhsecret), + ibuf_length(kex->kex_dhpeer)); + goto done; + } + if (ibuf_cat(seed, dhsecret) != 0) { + log_debug("%s: failed to set dh secret", __func__); + goto done; + } + } + if (ibuf_cat(seed, kex->kex_inonce) != 0 || + ibuf_cat(seed, kex->kex_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__); @@ -3533,7 +4381,7 @@ ikev2_childsa_negotiate(struct iked *env, struct iked_sa *sa, int initiator) } /* Create the new flows */ - TAILQ_FOREACH(prop, &sa->sa_proposals, prop_entry) { + TAILQ_FOREACH(prop, proposals, prop_entry) { if (ikev2_valid_proposal(prop, NULL, NULL, NULL) != 0) continue; @@ -3544,8 +4392,11 @@ ikev2_childsa_negotiate(struct iked *env, struct iked_sa *sa, int initiator) &flow->flow_src) && IKED_ADDR_EQ(&saflow->flow_dst, &flow->flow_dst) && - saflow->flow_saproto == prop->prop_protoid) + saflow->flow_saproto == + prop->prop_protoid) { skip = 1; + break; + } } if (skip) continue; @@ -3558,11 +4409,10 @@ ikev2_childsa_negotiate(struct iked *env, struct iked_sa *sa, int initiator) memcpy(flowa, flow, sizeof(*flow)); flowa->flow_dir = IPSP_DIRECTION_OUT; 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; + ikev2_cp_fixaddr(sa, &flow->flow_dst, &flowa->flow_dst); if ((flowb = calloc(1, sizeof(*flowb))) == NULL) { log_debug("%s: failed to get flow", __func__); @@ -3577,6 +4427,7 @@ ikev2_childsa_negotiate(struct iked *env, struct iked_sa *sa, int initiator) sizeof(flow->flow_dst)); memcpy(&flowb->flow_dst, &flow->flow_src, sizeof(flow->flow_src)); + ikev2_cp_fixaddr(sa, &flow->flow_dst, &flowb->flow_src); TAILQ_INSERT_TAIL(&sa->sa_flows, flowa, flow_entry); TAILQ_INSERT_TAIL(&sa->sa_flows, flowb, flow_entry); @@ -3584,7 +4435,7 @@ ikev2_childsa_negotiate(struct iked *env, struct iked_sa *sa, int initiator) } /* create the CHILD SAs using the key material */ - TAILQ_FOREACH(prop, &sa->sa_proposals, prop_entry) { + TAILQ_FOREACH(prop, proposals, prop_entry) { if (ikev2_valid_proposal(prop, &encrxf, &integrxf, &esn) != 0) continue; @@ -3597,10 +4448,9 @@ ikev2_childsa_negotiate(struct iked *env, struct iked_sa *sa, int initiator) csa->csa_saproto = prop->prop_protoid; csa->csa_ikesa = sa; - csa->csa_srcid = localid; - csa->csa_dstid = peerid; csa->csa_spi.spi_protoid = prop->prop_protoid; csa->csa_esn = esn; + csa->csa_transport = sa->sa_transport; /* Set up responder's SPIs */ if (initiator) { @@ -3639,8 +4489,10 @@ ikev2_childsa_negotiate(struct iked *env, struct iked_sa *sa, int initiator) childsa_free(csa); goto done; } - csa->csa_encrxf = encrxf; - csa->csa_integrxf = integrxf; + if (encrxf) + csa->csa_encrid = encrxf->xform_id; + if (integrxf) + csa->csa_integrid = integrxf->xform_id; if ((csb = calloc(1, sizeof(*csb))) == NULL) { log_debug("%s: failed to get CHILD SA", __func__); @@ -3685,17 +4537,183 @@ ikev2_childsa_negotiate(struct iked *env, struct iked_sa *sa, int initiator) ret = 0; done: + ibuf_release(dhsecret); ibuf_release(keymat); ibuf_release(seed); return (ret); } +/* free a replaced IPCOMP SA */ +void +ikev2_ipcomp_csa_free(struct iked *env, struct iked_childsa *csa) +{ + if (csa->csa_children) + fatalx("ikev2_ipcomp_csa_free: has children"); + if (csa->csa_ikesa) + TAILQ_REMOVE(&csa->csa_ikesa->sa_childsas, csa, + csa_entry); + if (csa->csa_loaded) { + log_debug("%s: csa %p loaded: calling pfkey_sa_delete", + __func__, csa); + pfkey_sa_delete(env->sc_pfkey, csa); + RB_REMOVE(iked_activesas, &env->sc_activesas, csa); + } + childsa_free(csa); +} + +int +ikev2_ipcomp_enable(struct iked *env, struct iked_sa *sa) +{ + struct iked_childsa *other, *nother, *csa = NULL, *csb = NULL; + struct iked_flow *flow, *flowa = NULL, *flowb = NULL; + struct iked_flow *nflow, *oflow; + + if ((csa = calloc(1, sizeof(*csa))) == NULL || + (csb = calloc(1, sizeof(*csb))) == NULL || + (flowa = calloc(1, sizeof(*flowa))) == NULL || + (flowb = calloc(1, sizeof(*flowb))) == NULL) { + free(csa); + free(csb); + free(flowa); + free(flowb); + return (-1); + } + + /* switch ESP SAs to transport mode */ + TAILQ_FOREACH(other, &sa->sa_childsas, csa_entry) { + if (!other->csa_rekey && !other->csa_loaded && + other->csa_saproto == IKEV2_SAPROTO_ESP) { + other->csa_transport = 1; + if (other->csa_dir == IPSP_DIRECTION_OUT) { + other->csa_parent = csa; + csa->csa_children++; + } else { + other->csa_parent = csb; + csb->csa_children++; + } + } + } + + /* install IPCOMP SAs */ + csa->csa_ikesa = sa; + csa->csa_saproto = IKEV2_SAPROTO_IPCOMP; + csa->csa_spi.spi_size = 2; + csa->csa_spi.spi = sa->sa_cpi_out; + csa->csa_peerspi = sa->sa_cpi_in; + csa->csa_dir = IPSP_DIRECTION_OUT; + csa->csa_local = &sa->sa_local; + csa->csa_peer = &sa->sa_peer; + + memcpy(csb, csa, sizeof(*csb)); + csb->csa_spi.spi = csa->csa_peerspi; + csb->csa_peerspi = csa->csa_spi.spi; + csb->csa_dir = IPSP_DIRECTION_IN; + csb->csa_local = csa->csa_peer; + csb->csa_peer = csa->csa_local; + csb->csa_allocated = 1; + + /* remove old replaced IPCOMP SAs */ + TAILQ_FOREACH_SAFE(other, &sa->sa_childsas, csa_entry, nother) { + if (other->csa_saproto != IKEV2_SAPROTO_IPCOMP || + other->csa_children != 0) + continue; + if (other->csa_dir == csa->csa_dir && + IKED_ADDR_EQ(other->csa_local, csa->csa_local) && + IKED_ADDR_EQ(other->csa_peer, csa->csa_peer)) { + log_debug("%s: csa %p replaces %p", + __func__, csa, other); + ikev2_ipcomp_csa_free(env, other); + } else if (other->csa_dir == csb->csa_dir && + IKED_ADDR_EQ(other->csa_local, csb->csa_local) && + IKED_ADDR_EQ(other->csa_peer, csb->csa_peer)) { + log_debug("%s: csa %p replaces %p", + __func__, csb, other); + ikev2_ipcomp_csa_free(env, other); + } + } + + TAILQ_INSERT_TAIL(&sa->sa_childsas, csa, csa_entry); + TAILQ_INSERT_TAIL(&sa->sa_childsas, csb, csa_entry); + + csa->csa_peersa = csb; + csb->csa_peersa = csa; + + /* redirect flows to IPCOMP */ + /* XXX expensive? should be merged into ikev2_childsa_negotiate() */ + TAILQ_FOREACH_SAFE(flow, &sa->sa_flows, flow_entry, nflow) { + if (flow->flow_loaded || + flow->flow_saproto != IKEV2_SAPROTO_ESP) + continue; + TAILQ_FOREACH(oflow, &sa->sa_flows, flow_entry) + if (IKED_ADDR_EQ(&oflow->flow_src, &flow->flow_src) && + IKED_ADDR_EQ(&oflow->flow_dst, &flow->flow_dst) && + oflow->flow_dir == flow->flow_dir && + oflow->flow_saproto == IKEV2_SAPROTO_IPCOMP) + break; + if (oflow != NULL) { + log_debug("%s: keeping oflow %p, indentical to flow %p", + __func__, oflow, flow); + TAILQ_REMOVE(&sa->sa_flows, flow, flow_entry); + flow_free(flow); + } else { + log_debug("%s: flow %p saproto %d -> %d", __func__, + flow, flow->flow_saproto, IKEV2_SAPROTO_IPCOMP); + flow->flow_saproto = IKEV2_SAPROTO_IPCOMP; + } + } + + /* setup ESP flows for gateways */ + flowa->flow_dir = IPSP_DIRECTION_OUT; + flowa->flow_saproto = IKEV2_SAPROTO_ESP; + flowa->flow_local = &sa->sa_local; + flowa->flow_peer = &sa->sa_peer; + memcpy(&flowa->flow_src, &sa->sa_local, sizeof(sa->sa_local)); + memcpy(&flowa->flow_dst, &sa->sa_peer, sizeof(sa->sa_peer)); + socket_setport((struct sockaddr *)&flowa->flow_src.addr, 0); + socket_setport((struct sockaddr *)&flowa->flow_dst.addr, 0); + flowa->flow_src.addr_port = flowa->flow_dst.addr_port = 0; + flowa->flow_src.addr_mask = flowa->flow_dst.addr_mask = + (sa->sa_local.addr_af == AF_INET) ? 32 : 128; + flowa->flow_ikesa = sa; + + /* skip if flow already exists */ + TAILQ_FOREACH(flow, &sa->sa_flows, flow_entry) { + if (IKED_ADDR_EQ(&flow->flow_src, &flowa->flow_src) && + IKED_ADDR_EQ(&flow->flow_dst, &flowa->flow_dst) && + flow->flow_dir == flowa->flow_dir && + flow->flow_saproto == flowa->flow_saproto) { + free(flowa); + free(flowb); + goto done; + } + } + + memcpy(flowb, flowa, sizeof(*flowb)); + flowb->flow_dir = IPSP_DIRECTION_IN; + memcpy(&flowb->flow_dst, &flowa->flow_src, sizeof(flowa->flow_src)); + memcpy(&flowb->flow_src, &flowa->flow_dst, sizeof(flowa->flow_dst)); + + TAILQ_INSERT_TAIL(&sa->sa_flows, flowa, flow_entry); + TAILQ_INSERT_TAIL(&sa->sa_flows, flowb, flow_entry); + + done: + /* make sure IPCOMP CPIs are not reused */ + sa->sa_ipcomp = 0; + sa->sa_cpi_in = sa->sa_cpi_out = 0; + + return (0); +} + int ikev2_childsa_enable(struct iked *env, struct iked_sa *sa) { struct iked_childsa *csa; - struct iked_flow *flow; + struct iked_flow *flow, *oflow; + + if (sa->sa_ipcomp && sa->sa_cpi_in && sa->sa_cpi_out && + ikev2_ipcomp_enable(env, sa) == -1) + return (-1); TAILQ_FOREACH(csa, &sa->sa_childsas, csa_entry) { if (csa->csa_rekey || csa->csa_loaded) @@ -3723,6 +4741,14 @@ ikev2_childsa_enable(struct iked *env, struct iked_sa *sa) return (-1); } + if ((oflow = RB_FIND(iked_flows, &env->sc_activeflows, flow)) + != NULL) { + log_debug("%s: replaced old flow %p with %p", + __func__, oflow, flow); + oflow->flow_loaded = 0; + RB_REMOVE(iked_flows, &env->sc_activeflows, oflow); + } + RB_INSERT(iked_flows, &env->sc_activeflows, flow); log_debug("%s: loaded flow %p", __func__, flow); @@ -3779,7 +4805,7 @@ ikev2_valid_proposal(struct iked_proposal *prop, struct iked_transform **exf, struct iked_transform **ixf, int *esn) { struct iked_transform *xform, *encrxf, *integrxf; - u_int i, doesn = 0; + unsigned int i, doesn = 0; switch (prop->prop_protoid) { case IKEV2_SAPROTO_ESP: @@ -3822,19 +4848,17 @@ ikev2_valid_proposal(struct iked_proposal *prop, return (0); } -void +/* return 0 if processed, -1 if busy */ +int ikev2_acquire_sa(struct iked *env, struct iked_flow *acquire) { struct iked_flow *flow; struct iked_sa *sa; -#if defined(_OPENBSD_IPSEC_API_VERSION) struct iked_policy pol, *p = NULL; -#endif if (env->sc_passive) - return; + return (0); -#if defined(_OPENBSD_IPSEC_API_VERSION) /* First try to find an active flow with IKE SA */ flow = RB_FIND(iked_flows, &env->sc_activeflows, acquire); if (!flow) { @@ -3850,7 +4874,7 @@ ikev2_acquire_sa(struct iked *env, struct iked_flow *acquire) if ((p = policy_test(env, &pol)) == NULL) { log_warnx("%s: flow wasn't found", __func__); - return; + return (0); } log_debug("%s: found matching policy '%s'", __func__, @@ -3860,21 +4884,20 @@ ikev2_acquire_sa(struct iked *env, struct iked_flow *acquire) log_warnx("%s: failed to initiate a " "IKE_SA_INIT exchange", __func__); } else { -#else - if ((flow = acquire) != NULL) { -#endif log_debug("%s: found active flow", __func__); if ((sa = flow->flow_ikesa) == NULL) { log_warnx("%s: flow without SA", __func__); - return; + return (0); } - + if (sa->sa_stateflags & IKED_REQ_CHILDSA) + return (-1); /* busy, retry later */ if (ikev2_send_create_child_sa(env, sa, NULL, flow->flow_saproto) != 0) log_warnx("%s: failed to initiate a " "CREATE_CHILD_SA exchange", __func__); } + return (0); } void @@ -3890,7 +4913,8 @@ ikev2_disable_rekeying(struct iked *env, struct iked_sa *sa) (void)ikev2_childsa_delete(env, sa, 0, 0, NULL, 1); } -void +/* return 0 if processed, -1 if busy */ +int ikev2_rekey_sa(struct iked *env, struct iked_spi *rekey) { struct iked_childsa *csa, key; @@ -3899,51 +4923,64 @@ ikev2_rekey_sa(struct iked *env, struct iked_spi *rekey) key.csa_spi = *rekey; csa = RB_FIND(iked_activesas, &env->sc_activesas, &key); if (!csa) - return; + return (0); if (csa->csa_rekey) /* See if it's already taken care of */ - return; + return (0); + if (csa->csa_saproto == IKEV2_SAPROTO_IPCOMP) /* no rekey */ + return (0); if ((sa = csa->csa_ikesa) == NULL) { log_warnx("%s: SA %s doesn't have a parent SA", __func__, print_spi(rekey->spi, rekey->spi_size)); - return; + return (0); } if (!sa_stateok(sa, IKEV2_STATE_ESTABLISHED)) { log_warnx("%s: SA %s is not established", __func__, print_spi(rekey->spi, rekey->spi_size)); - return; + return (0); } + if (sa->sa_stateflags & IKED_REQ_CHILDSA) + return (-1); /* busy, retry later */ if (csa->csa_allocated) /* Peer SPI died first, get the local one */ rekey->spi = csa->csa_peerspi; if (ikev2_send_create_child_sa(env, sa, rekey, rekey->spi_protoid)) log_warnx("%s: failed to initiate a CREATE_CHILD_SA exchange", __func__); + return (0); } -void +/* return 0 if processed, -1 if busy */ +int ikev2_drop_sa(struct iked *env, struct iked_spi *drop) { struct ibuf *buf = NULL; struct iked_childsa *csa, key; struct iked_sa *sa; struct ikev2_delete *del; - u_int32_t spi32; + uint32_t spi32; key.csa_spi = *drop; csa = RB_FIND(iked_activesas, &env->sc_activesas, &key); if (!csa || csa->csa_rekey) - return; + return (0); + + sa = csa->csa_ikesa; + if (sa && (sa->sa_stateflags & IKED_REQ_CHILDSA)) + return (-1); /* busy, retry later */ + RB_REMOVE(iked_activesas, &env->sc_activesas, csa); csa->csa_loaded = 0; - if ((sa = csa->csa_ikesa) == NULL) { + csa->csa_rekey = 1; /* prevent re-loading */ + if (sa == NULL) { log_debug("%s: failed to find a parent SA", __func__); - return; + return (0); + } + if (csa->csa_saproto == IKEV2_SAPROTO_IPCOMP) { + /* matching Child SAs (e.g. ESP) should have expired by now */ + if (csa->csa_children == 0) + ikev2_ipcomp_csa_free(env, csa); + return (0); } - - if ((buf = ibuf_static()) == NULL) - goto done; - if ((del = ibuf_advance(buf, sizeof(*del))) == NULL) - goto done; if (csa->csa_allocated) spi32 = htobe32(csa->csa_spi.spi); @@ -3958,7 +4995,7 @@ ikev2_drop_sa(struct iked *env, struct iked_spi *drop) /* Send PAYLOAD_DELETE */ if ((buf = ibuf_static()) == NULL) - return; + return (0); if ((del = ibuf_advance(buf, sizeof(*del))) == NULL) goto done; del->del_protoid = drop->spi_protoid; @@ -3980,15 +5017,14 @@ ikev2_drop_sa(struct iked *env, struct iked_spi *drop) done: ibuf_release(buf); - return; + return (0); } int ikev2_print_id(struct iked_id *id, char *idstr, size_t idstrlen) { - u_int8_t buf[BUFSIZ], *ptr; - struct sockaddr_in *s4; - struct sockaddr_in6 *s6; + uint8_t buf[BUFSIZ], *ptr; + struct sockaddr *sa; char *str; ssize_t len; int i; @@ -4020,15 +5056,11 @@ ikev2_print_id(struct iked_id *id, char *idstr, size_t idstrlen) switch (id->id_type) { case IKEV2_ID_IPV4: - s4 = (struct sockaddr_in *)buf; - s4->sin_family = AF_INET; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - s4->sin_len = sizeof(*s4); -#endif - memcpy(&s4->sin_addr.s_addr, ptr, len); - - if (print_host((struct sockaddr_storage *)s4, - idstr, idstrlen) == NULL) + sa = (struct sockaddr *)buf; + sa->sa_family = AF_INET; + SET_SA_LEN(sa, sizeof(struct sockaddr_in)); + memcpy(&((struct sockaddr_in *)sa)->sin_addr, ptr, len); + if (print_host(sa, idstr, idstrlen) == NULL) return (-1); break; case IKEV2_ID_FQDN: @@ -4046,15 +5078,11 @@ ikev2_print_id(struct iked_id *id, char *idstr, size_t idstrlen) free(str); break; case IKEV2_ID_IPV6: - s6 = (struct sockaddr_in6 *)buf; - s6->sin6_family = AF_INET6; -#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN - s6->sin6_len = sizeof(*s6); -#endif - memcpy(&s6->sin6_addr, ptr, len); - - if (print_host((struct sockaddr_storage *)s6, - idstr, idstrlen) == NULL) + sa = (struct sockaddr *)buf; + sa->sa_family = AF_INET6; + SET_SA_LEN(sa, sizeof(struct sockaddr_in6)); + memcpy(&((struct sockaddr_in6 *)sa)->sin6_addr, ptr, len); + if (print_host(sa, idstr, idstrlen) == NULL) return (-1); break; case IKEV2_ID_ASN1_DN: @@ -4076,3 +5104,184 @@ ikev2_print_id(struct iked_id *id, char *idstr, size_t idstrlen) return (0); } + +/* + * If we have an IKEV2_CP_REQUEST for IKEV2_CFG_INTERNAL_IP4_ADDRESS and + * if a network(pool) is configured, then select an address from that pool + * and remember it in the sa_addrpool attribute. + */ +int +ikev2_cp_setaddr(struct iked *env, struct iked_sa *sa, sa_family_t family) +{ + struct iked_cfg *ikecfg = NULL; + struct iked_policy *pol = sa->sa_policy; + struct sockaddr_in *in4 = NULL, *cfg4 = NULL; + struct sockaddr_in6 *in6 = NULL, *cfg6 = NULL; + struct iked_sa key; + struct iked_addr addr; + uint32_t mask, host, lower, upper, start, nhost; + size_t i; + + switch (family) { + case AF_INET: + if (sa->sa_addrpool) + return (0); + break; + case AF_INET6: + if (sa->sa_addrpool6) + return (0); + break; + default: + return (-1); + } + if (pol->pol_ncfg == 0) + return (0); + /* check for an address pool config (address w/ prefixlen != 32) */ + bzero(&addr, sizeof(addr)); + for (i = 0; i < pol->pol_ncfg; i++) { + ikecfg = &pol->pol_cfg[i]; + if (family == AF_INET && + ikecfg->cfg_type == IKEV2_CFG_INTERNAL_IP4_ADDRESS && + ikecfg->cfg.address.addr_mask != 32) { + addr.addr_af = AF_INET; + break; + } + if (family == AF_INET6 && + ikecfg->cfg_type == IKEV2_CFG_INTERNAL_IP6_ADDRESS && + ikecfg->cfg.address.addr_mask != 128) { + addr.addr_af = AF_INET6; + break; + } + } + if (i == pol->pol_ncfg) + return (0); + + /* + * failure: pool configured, but not requested. + * If we continue, we might end up with flows where 0.0.0.0 is NOT + * replaced with an address from the pool with ikev2_cp_fixaddr(). + */ + if (sa->sa_cp != IKEV2_CP_REQUEST) { + log_debug("%s: pool configured, but IKEV2_CP_REQUEST missing", + __func__); + return (-1); + } + + switch (addr.addr_af) { + case AF_INET: + cfg4 = (struct sockaddr_in *)&ikecfg->cfg.address.addr; + addr.addr.ss_family = AF_INET; + SET_SS_LEN(&addr.addr, sizeof(struct sockaddr_in)); + mask = prefixlen2mask(ikecfg->cfg.address.addr_mask); + lower = ntohl(cfg4->sin_addr.s_addr & ~mask); + key.sa_addrpool = &addr; + break; + case AF_INET6: + cfg6 = (struct sockaddr_in6 *)&ikecfg->cfg.address.addr; + addr.addr.ss_family = AF_INET6; + SET_SS_LEN(&addr.addr, sizeof(struct sockaddr_in6)); + /* truncate prefixlen to get a 32-bit space */ + mask = (ikecfg->cfg.address.addr_mask >= 96) + ? prefixlen2mask(ikecfg->cfg.address.addr_mask - 96) + : prefixlen2mask(0); + memcpy(&lower, &cfg6->sin6_addr.s6_addr[12], sizeof(uint32_t)); + lower = ntohl(lower & ~mask); + key.sa_addrpool6 = &addr; + break; + default: + return (-1); + } + + if (lower == 0) + lower = 1; + /* Note that start, upper and host are in HOST byte order */ + upper = ntohl(~mask); + /* Randomly select start from [lower, upper-1] */ + start = arc4random_uniform(upper - lower) + lower; + + for (host = start;;) { + log_debug("%s: mask %x start %x lower %x host %x upper %x", + __func__, mask, start, lower, host, upper); + switch (addr.addr_af) { + case AF_INET: + in4 = (struct sockaddr_in *)&addr.addr; + in4->sin_addr.s_addr = + (cfg4->sin_addr.s_addr & mask) | htonl(host); + break; + case AF_INET6: + in6 = (struct sockaddr_in6 *)&addr.addr; + memcpy(&in6->sin6_addr, &cfg6->sin6_addr, + sizeof(in6->sin6_addr)); + nhost = htonl(host); + memcpy(&in6->sin6_addr.s6_addr[12], &nhost, + sizeof(uint32_t)); + break; + } + if ((addr.addr_af == AF_INET && + !RB_FIND(iked_addrpool, &env->sc_addrpool, &key)) || + (addr.addr_af == AF_INET6 && + !RB_FIND(iked_addrpool6, &env->sc_addrpool6, &key))) + break; + /* try next address */ + host++; + /* but skip broadcast and network address */ + if (host >= upper || host < lower) + host = lower; + if (host == start) + return (-1); /* exhausted */ + } + + switch (addr.addr_af) { + case AF_INET: + if (!key.sa_addrpool) + return (-1); /* cannot happen? */ + if ((sa->sa_addrpool = calloc(1, sizeof(addr))) == NULL) + return (-1); + memcpy(sa->sa_addrpool, &addr, sizeof(addr)); + RB_INSERT(iked_addrpool, &env->sc_addrpool, sa); + break; + case AF_INET6: + if (!key.sa_addrpool6) + return (-1); /* cannot happen? */ + if ((sa->sa_addrpool6 = calloc(1, sizeof(addr))) == NULL) + return (-1); + memcpy(sa->sa_addrpool6, &addr, sizeof(addr)); + RB_INSERT(iked_addrpool6, &env->sc_addrpool6, sa); + break; + default: + return (-1); + } + return (0); +} + +/* + * if 'addr' is 'UNSPECIFIED' replace it with sa_addrpool from + * the ip-pool and store the result in 'patched'. + */ +int +ikev2_cp_fixaddr(struct iked_sa *sa, struct iked_addr *addr, + struct iked_addr *patched) +{ + struct sockaddr_in *in4; + struct sockaddr_in6 *in6; + + switch (addr->addr_af) { + case AF_INET: + if (sa->sa_addrpool == NULL) + return (-1); + in4 = (struct sockaddr_in *)&addr->addr; + if (in4->sin_addr.s_addr) + return (-1); + memcpy(patched, sa->sa_addrpool, sizeof(*patched)); + break; + case AF_INET6: + if (sa->sa_addrpool6 == NULL) + return (-1); + in6 = (struct sockaddr_in6 *)&addr->addr; + if (!IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr)) + return (-1); + memcpy(patched, sa->sa_addrpool6, sizeof(*patched)); + break; + } + return (0); +} diff --git a/iked/ikev2.h b/iked/ikev2.h index 439e8ab..e2b182c 100644 --- a/iked/ikev2.h +++ b/iked/ikev2.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ikev2.h,v 1.12 2013/03/30 16:31:37 reyk Exp $ */ +/* $OpenBSD: ikev2.h,v 1.23 2015/10/31 19:28:19 naddy Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter @@ -16,8 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef _IKEV2_H -#define _IKEV2_H +#ifndef IKED_IKEV2_H +#define IKED_IKEV2_H #define IKEV2_VERSION 0x20 /* IKE version 2.0 */ #define IKEV1_VERSION 0x10 /* IKE version 1.0 */ @@ -32,12 +32,14 @@ #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_ESTABLISHED 8 /* active IKE SA */ -#define IKEV2_STATE_CLOSED 9 /* delete this SA */ +#define IKEV2_STATE_EAP_SUCCESS 4 /* EAP succeeded */ +#define IKEV2_STATE_AUTH_REQUEST 5 /* auth received */ +#define IKEV2_STATE_AUTH_SUCCESS 6 /* authenticated */ +#define IKEV2_STATE_VALID 7 /* authenticated AND validated certs */ +#define IKEV2_STATE_EAP_VALID 8 /* EAP validated */ +#define IKEV2_STATE_ESTABLISHED 9 /* active IKE SA */ +#define IKEV2_STATE_CLOSING 10 /* expect delete for this SA */ +#define IKEV2_STATE_CLOSED 11 /* delete this SA */ extern struct iked_constmap ikev2_state_map[]; @@ -71,9 +73,9 @@ extern struct iked_constmap ikev2_flag_map[]; */ 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 */ + uint8_t pld_nextpayload; /* Next payload type */ + uint8_t pld_reserved; /* Contains the critical bit */ + uint16_t pld_length; /* Payload length with header */ } __packed; #define IKEV2_CRITICAL_PAYLOAD 0x01 /* First bit in the reserved field */ @@ -105,13 +107,13 @@ extern struct iked_constmap ikev2_payload_map[]; */ 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 */ + uint8_t sap_more; /* Last proposal or more */ + uint8_t sap_reserved; /* Must be set to zero */ + uint16_t sap_length; /* Proposal length */ + uint8_t sap_proposalnr; /* Proposal number */ + uint8_t sap_protoid; /* Protocol Id */ + uint8_t sap_spisize; /* SPI size */ + uint8_t sap_transforms; /* Number of transforms */ /* Followed by variable-length SPI */ /* Followed by variable-length transforms */ } __packed; @@ -125,16 +127,17 @@ struct ikev2_sa_proposal { #define IKEV2_SAPROTO_ESP 3 /* ESP */ #define IKEV2_SAPROTO_FC_ESP_HEADER 4 /* RFC4595 */ #define IKEV2_SAPROTO_FC_CT_AUTH 5 /* RFC4595 */ +#define IKEV2_SAPROTO_IPCOMP 204 /* private, should be 4 */ 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 */ + uint8_t xfrm_more; /* Last transform or more */ + uint8_t xfrm_reserved; /* Must be set to zero */ + uint16_t xfrm_length; /* Transform length */ + uint8_t xfrm_type; /* Transform type */ + uint8_t xfrm_reserved1; /* Must be set to zero */ + uint16_t xfrm_id; /* Transform Id */ /* Followed by variable-length transform attributes */ } __packed; @@ -177,6 +180,7 @@ extern struct iked_constmap ikev2_xformtype_map[]; #define IKEV2_XFORMENCR_CAMELLIA_CCM_8 25 /* RFC5529 */ #define IKEV2_XFORMENCR_CAMELLIA_CCM_12 26 /* RFC5529 */ #define IKEV2_XFORMENCR_CAMELLIA_CCM_16 27 /* RFC5529 */ +#define IKEV2_XFORMENCR_CHACHA20_POLY1305 28 /* RFC7634 */ extern struct iked_constmap ikev2_xformencr_map[]; @@ -239,7 +243,7 @@ extern struct iked_constmap ikev2_xformauth_map[]; #define IKEV2_XFORMDH_BRAINPOOL_P256R1 28 /* DH Group 28 */ #define IKEV2_XFORMDH_BRAINPOOL_P384R1 29 /* DH Group 29 */ #define IKEV2_XFORMDH_BRAINPOOL_P512R1 30 /* DH Group 30 */ -#define IKEV2_XFORMDH_MAX 31 +#define IKEV2_XFORMDH_X_CURVE25519 1034 /* draft-ietf-ipsecme-safecurves-00 */ extern struct iked_constmap ikev2_xformdh_map[]; @@ -249,8 +253,8 @@ extern struct iked_constmap ikev2_xformdh_map[]; 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 */ + uint16_t attr_type; /* Attribute type */ + uint16_t attr_length; /* Attribute length or value */ /* Followed by variable length (TLV) */ } __packed; @@ -266,8 +270,8 @@ extern struct iked_constmap ikev2_attrtype_map[]; */ struct ikev2_keyexchange { - u_int16_t kex_dhgroup; /* DH Group # */ - u_int16_t kex_reserved; /* Reserved */ + uint16_t kex_dhgroup; /* DH Group # */ + uint16_t kex_reserved; /* Reserved */ } __packed; /* @@ -275,9 +279,9 @@ struct ikev2_keyexchange { */ 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 */ + uint8_t n_protoid; /* Protocol Id */ + uint8_t n_spisize; /* SPI size */ + uint16_t n_type; /* Notify message type */ /* Followed by variable length SPI */ /* Followed by variable length notification data */ } __packed; @@ -347,6 +351,7 @@ struct ikev2_notify { #define IKEV2_N_PSK_CONFIRM 16426 /* RFC6631 */ #define IKEV2_N_ERX_SUPPORTED 16427 /* RFC6867 */ #define IKEV2_N_IFOM_CAPABILITY 16428 /* OA3GPP */ +#define IKEV2_N_SIGNATURE_HASH_ALGORITHMS 16431 /* RFC7427 */ extern struct iked_constmap ikev2_n_map[]; @@ -355,9 +360,9 @@ extern struct iked_constmap ikev2_n_map[]; */ 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 */ + uint8_t del_protoid; /* Protocol Id */ + uint8_t del_spisize; /* SPI size */ + uint16_t del_nspi; /* Number of SPIs */ /* Followed by variable length SPIs */ } __packed; @@ -366,8 +371,8 @@ struct ikev2_delete { */ struct ikev2_id { - u_int8_t id_type; /* Id type */ - u_int8_t id_reserved[3]; /* Reserved */ + uint8_t id_type; /* Id type */ + uint8_t id_reserved[3]; /* Reserved */ /* Followed by the identification data */ } __packed; @@ -388,7 +393,7 @@ extern struct iked_constmap ikev2_id_map[]; */ struct ikev2_cert { - u_int8_t cert_type; /* Encoding */ + uint8_t cert_type; /* Encoding */ /* Followed by the certificate data */ } __packed; @@ -414,17 +419,17 @@ extern struct iked_constmap ikev2_cert_map[]; */ struct ikev2_tsp { - u_int8_t tsp_count; /* Number of TSs */ - u_int8_t tsp_reserved[3]; /* Reserved */ + uint8_t tsp_count; /* Number of TSs */ + uint8_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 */ + uint8_t ts_type; /* TS type */ + uint8_t ts_protoid; /* Protocol Id */ + uint16_t ts_length; /* Length */ + uint16_t ts_startport; /* Start port */ + uint16_t ts_endport; /* End port */ } __packed; #define IKEV2_TS_IPV4_ADDR_RANGE 7 /* RFC4306 */ @@ -438,8 +443,8 @@ extern struct iked_constmap ikev2_ts_map[]; */ struct ikev2_auth { - u_int8_t auth_method; /* Signature type */ - u_int8_t auth_reserved[3]; /* Reserved */ + uint8_t auth_method; /* Signature type */ + uint8_t auth_reserved[3]; /* Reserved */ /* Followed by the signature */ } __packed; @@ -449,18 +454,30 @@ struct ikev2_auth { #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 */ +#define IKEV2_AUTH_ECDSA_521 11 /* RFC4754 */ #define IKEV2_AUTH_GSPM 12 /* RFC6467 */ +#define IKEV2_AUTH_NULL 13 /* RFC7619 */ +#define IKEV2_AUTH_SIG 14 /* RFC7427 */ extern struct iked_constmap ikev2_auth_map[]; +/* Notifications used together with IKEV2_AUTH_SIG */ + +#define IKEV2_SIGHASH_RESERVED 0 /* RFC7427 */ +#define IKEV2_SIGHASH_SHA1 1 /* RFC7427 */ +#define IKEV2_SIGHASH_SHA2_256 2 /* RFC7427 */ +#define IKEV2_SIGHASH_SHA2_384 3 /* RFC7427 */ +#define IKEV2_SIGHASH_SHA2_512 4 /* RFC7427 */ + +extern struct iked_constmap ikev2_sighash_map[]; + /* * CP payload */ struct ikev2_cp { - u_int8_t cp_type; - u_int8_t cp_reserved[3]; + uint8_t cp_type; + uint8_t cp_reserved[3]; /* Followed by the attributes */ } __packed; @@ -472,8 +489,8 @@ struct ikev2_cp { 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; + uint16_t cfg_type; /* first bit must be set to zero */ + uint16_t cfg_length; /* Followed by variable-length data */ } __packed; @@ -500,4 +517,4 @@ struct ikev2_cfg { extern struct iked_constmap ikev2_cfg_map[]; -#endif /* _IKEV2_H */ +#endif /* IKED_IKEV2_H */ diff --git a/iked/ikev2_msg.c b/iked/ikev2_msg.c index 12016c8..c335509 100644 --- a/iked/ikev2_msg.c +++ b/iked/ikev2_msg.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ikev2_msg.c,v 1.25 2013/03/21 04:30:14 deraadt Exp $ */ +/* $OpenBSD: ikev2_msg.c,v 1.45 2015/10/19 11:25:35 reyk Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter @@ -16,23 +16,19 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include "openbsd-compat/sys-queue.h" +#include /* roundup */ + #include #include #include #include -#if defined(__OpenBSD__) -#include -#endif #include #include #include #include #include -#include #include #include #include @@ -47,6 +43,7 @@ #include "eap.h" #include "dh.h" +void ikev1_recv(struct iked *, struct iked_message *); void ikev2_msg_response_timeout(struct iked *, void *); void ikev2_msg_retransmit_timeout(struct iked *, void *); @@ -57,11 +54,10 @@ ikev2_msg_cb(int fd, short event, void *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]; + uint32_t natt = 0x00000000; + uint8_t buf[IKED_MSGBUF_MAX]; ssize_t len; off_t off; - struct iovec iov[2]; bzero(&msg, sizeof(msg)); bzero(buf, sizeof(buf)); @@ -77,8 +73,9 @@ ikev2_msg_cb(int fd, short event, void *arg) (ssize_t)sizeof(natt)) return; - if (socket_getport(&msg.msg_local) == IKED_NATT_PORT) { - if (bcmp(&natt, buf, sizeof(natt)) != 0) + if (socket_getport((struct sockaddr *)&msg.msg_local) == + IKED_NATT_PORT) { + if (memcmp(&natt, buf, sizeof(natt)) != 0) return; msg.msg_natt = 1; off = sizeof(natt); @@ -92,25 +89,44 @@ ikev2_msg_cb(int fd, short event, void *arg) 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; - - proc_composev_imsg(env, PROC_IKEV1, IMSG_IKE_MESSAGE, -1, - iov, 2); - goto done; - } TAILQ_INIT(&msg.msg_proposals); - msg.msg_fd = fd; - ikev2_recv(env, &msg); - done: + if (hdr.ike_version == IKEV1_VERSION) + ikev1_recv(env, &msg); + else + ikev2_recv(env, &msg); + ikev2_msg_cleanup(env, &msg); } +void +ikev1_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__); +} + struct ibuf * ikev2_msg_init(struct iked *env, struct iked_message *msg, struct sockaddr_storage *peer, socklen_t peerlen, @@ -136,11 +152,14 @@ ikev2_msg_copy(struct iked *env, struct iked_message *msg) { struct iked_message *m = NULL; struct ibuf *buf; - ssize_t len; + size_t len; void *ptr; - if ((len = ibuf_size(msg->msg_data) - msg->msg_offset) <= 0 || - (ptr = ibuf_seek(msg->msg_data, msg->msg_offset, len)) == NULL || + if (ibuf_size(msg->msg_data) < msg->msg_offset) + return (NULL); + len = ibuf_size(msg->msg_data) - msg->msg_offset; + + if ((ptr = ibuf_seek(msg->msg_data, msg->msg_offset, len)) == NULL || (m = malloc(sizeof(*m))) == NULL || (buf = ikev2_msg_init(env, m, &msg->msg_peer, msg->msg_peerlen, &msg->msg_local, msg->msg_locallen, msg->msg_response)) == NULL || @@ -188,8 +207,22 @@ ikev2_msg_valid_ike_sa(struct iked *env, struct ike_header *oldhdr, struct iked_sa sa; #endif - if (msg->msg_sa != NULL && msg->msg_policy != NULL) + if (msg->msg_sa != NULL && msg->msg_policy != NULL) { + /* + * Only permit informational requests from initiator + * on closing SAs (for DELETE). + */ + if (msg->msg_sa->sa_state == IKEV2_STATE_CLOSING) { + if (((oldhdr->ike_flags & + (IKEV2_FLAG_INITIATOR|IKEV2_FLAG_RESPONSE)) == + IKEV2_FLAG_INITIATOR) && + (oldhdr->ike_exchange == + IKEV2_EXCHANGE_INFORMATIONAL)) + return (0); + return (-1); + } return (0); + } #if 0 /* @@ -256,8 +289,9 @@ ikev2_msg_send(struct iked *env, struct iked_message *msg) { struct iked_sa *sa = msg->msg_sa; struct ibuf *buf = msg->msg_data; - u_int32_t natt = 0x00000000; + uint32_t natt = 0x00000000; int isnatt = 0; + uint8_t exchange, flags; struct ike_header *hdr; struct iked_message *m; @@ -267,10 +301,14 @@ ikev2_msg_send(struct iked *env, struct iked_message *msg) isnatt = (msg->msg_natt || (msg->msg_sa && msg->msg_sa->sa_natt)); - log_info("%s: %s from %s to %s, %ld bytes%s", __func__, - print_map(hdr->ike_exchange, ikev2_exchange_map), - print_host(&msg->msg_local, NULL, 0), - print_host(&msg->msg_peer, NULL, 0), + exchange = hdr->ike_exchange; + flags = hdr->ike_flags; + log_info("%s: %s %s from %s to %s msgid %u, %ld bytes%s", __func__, + print_map(exchange, ikev2_exchange_map), + (flags & IKEV2_FLAG_RESPONSE) ? "response" : "request", + print_host((struct sockaddr *)&msg->msg_local, NULL, 0), + print_host((struct sockaddr *)&msg->msg_peer, NULL, 0), + betoh32(hdr->ike_msgid), ibuf_length(buf), isnatt ? ", NAT-T" : ""); if (isnatt) { @@ -294,27 +332,25 @@ ikev2_msg_send(struct iked *env, struct iked_message *msg) log_debug("%s: failed to copy a message", __func__); return (-1); } - m->msg_exchange = hdr->ike_exchange; + m->msg_exchange = exchange; - if (hdr->ike_flags & IKEV2_FLAG_RESPONSE) { + if (flags & IKEV2_FLAG_RESPONSE) { TAILQ_INSERT_TAIL(&sa->sa_responses, m, msg_entry); - timer_initialize(env, &m->msg_timer, - ikev2_msg_response_timeout, m); - timer_register(env, &m->msg_timer, IKED_RESPONSE_TIMEOUT); + timer_set(env, &m->msg_timer, ikev2_msg_response_timeout, m); + timer_add(env, &m->msg_timer, IKED_RESPONSE_TIMEOUT); } else { TAILQ_INSERT_TAIL(&sa->sa_requests, m, msg_entry); - timer_initialize(env, &m->msg_timer, - ikev2_msg_retransmit_timeout, m); - timer_register(env, &m->msg_timer, IKED_RETRANSMIT_TIMEOUT); + timer_set(env, &m->msg_timer, ikev2_msg_retransmit_timeout, m); + timer_add(env, &m->msg_timer, IKED_RETRANSMIT_TIMEOUT); } return (0); } -u_int32_t +uint32_t ikev2_msg_id(struct iked *env, struct iked_sa *sa) { - u_int32_t id = sa->sa_reqid; + uint32_t id = sa->sa_reqid; if (++sa->sa_reqid == UINT32_MAX) { /* XXX we should close and renegotiate the connection now */ @@ -328,13 +364,13 @@ ikev2_msg_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; + uint8_t *buf, pad = 0, *ptr; struct ibuf *encr, *dst = NULL, *out = NULL; buf = ibuf_data(src); len = ibuf_size(src); - log_debug("%s: decrypted length %d", __func__, len); + log_debug("%s: decrypted length %zu", __func__, len); print_hex(buf, 0, len); if (sa == NULL || @@ -366,7 +402,7 @@ ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src) if (ibuf_add(src, &pad, sizeof(pad)) != 0) goto done; - log_debug("%s: padded length %d", __func__, ibuf_size(src)); + log_debug("%s: padded length %zu", __func__, ibuf_size(src)); print_hex(ibuf_data(src), 0, ibuf_size(src)); cipher_setkey(sa->sa_encr, encr->buf, ibuf_length(encr)); @@ -389,9 +425,9 @@ ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src) if ((ptr = ibuf_advance(dst, integrlen)) == NULL) goto done; - bzero(ptr, integrlen); + explicit_bzero(ptr, integrlen); - log_debug("%s: length %d, padding %d, output length %d", + log_debug("%s: length %zu, padding %d, output length %zu", __func__, len + sizeof(pad), pad, ibuf_size(dst)); print_hex(ibuf_data(dst), 0, ibuf_size(dst)); @@ -411,9 +447,9 @@ ikev2_msg_integr(struct iked *env, struct iked_sa *sa, struct ibuf *src) int ret = -1; size_t integrlen, tmplen; struct ibuf *integr, *tmp = NULL; - u_int8_t *ptr; + uint8_t *ptr; - log_debug("%s: message length %d", __func__, ibuf_size(src)); + log_debug("%s: message length %zu", __func__, ibuf_size(src)); print_hex(ibuf_data(src), 0, ibuf_size(src)); if (sa == NULL || @@ -429,7 +465,7 @@ ikev2_msg_integr(struct iked *env, struct iked_sa *sa, struct ibuf *src) integrlen = hash_length(sa->sa_integr); - log_debug("%s: integrity checksum length %d", __func__, + log_debug("%s: integrity checksum length %zu", __func__, integrlen); /* @@ -469,7 +505,7 @@ ikev2_msg_decrypt(struct iked *env, struct iked_sa *sa, { ssize_t ivlen, encrlen, integrlen, blocklen, outlen, tmplen; - u_int8_t pad = 0, *ptr; + uint8_t pad = 0, *ptr; struct ibuf *integr, *encr, *tmp = NULL, *out = NULL; off_t ivoff, encroff, integroff; @@ -502,11 +538,11 @@ ikev2_msg_decrypt(struct iked *env, struct iked_sa *sa, goto done; } - log_debug("%s: IV length %d", __func__, ivlen); + log_debug("%s: IV length %zd", __func__, ivlen); print_hex(ibuf_data(src), 0, ivlen); - log_debug("%s: encrypted payload length %d", __func__, encrlen); + log_debug("%s: encrypted payload length %zd", __func__, encrlen); print_hex(ibuf_data(src), encroff, encrlen); - log_debug("%s: integrity checksum length %d", __func__, integrlen); + log_debug("%s: integrity checksum length %zd", __func__, integrlen); print_hex(ibuf_data(src), integroff, integrlen); /* @@ -556,7 +592,7 @@ ikev2_msg_decrypt(struct iked *env, struct iked_sa *sa, pad = *ptr; } - log_debug("%s: decrypted payload length %d/%d padding %d", + log_debug("%s: decrypted payload length %zd/%zd padding %d", __func__, outlen, encrlen, pad); print_hex(ibuf_data(out), 0, ibuf_size(out)); @@ -574,7 +610,7 @@ ikev2_msg_decrypt(struct iked *env, struct iked_sa *sa, int ikev2_msg_send_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf **ep, - u_int8_t exchange, u_int8_t firstpayload, int response) + uint8_t exchange, uint8_t firstpayload, int response) { struct iked_message resp; struct ike_header *hdr; @@ -637,7 +673,7 @@ struct ibuf * ikev2_msg_auth(struct iked *env, struct iked_sa *sa, int response) { struct ibuf *authmsg = NULL, *nonce, *prfkey, *buf; - u_int8_t *ptr; + uint8_t *ptr; struct iked_id *id; size_t tmplen; @@ -681,7 +717,7 @@ ikev2_msg_auth(struct iked *env, struct iked_sa *sa, int response) if (tmplen != hash_length(sa->sa_prf)) goto fail; - log_debug("%s: %s auth data length %d", + log_debug("%s: %s auth data length %zu", __func__, response ? "responder" : "initiator", ibuf_size(authmsg)); print_hex(ibuf_data(authmsg), 0, ibuf_size(authmsg)); @@ -695,14 +731,14 @@ ikev2_msg_auth(struct iked *env, struct iked_sa *sa, int response) int ikev2_msg_authverify(struct iked *env, struct iked_sa *sa, - struct iked_auth *auth, u_int8_t *buf, size_t len, struct ibuf *authmsg) + struct iked_auth *auth, uint8_t *buf, size_t len, struct ibuf *authmsg) { - u_int8_t *key, *psk = NULL; + uint8_t *key, *psk = NULL; ssize_t keylen; struct iked_id *id; struct iked_dsa *dsa = NULL; int ret = -1; - u_int8_t keytype; + uint8_t keytype; if (sa->sa_hdr.sh_initiator) id = &sa->sa_rcert; @@ -739,12 +775,12 @@ ikev2_msg_authverify(struct iked *env, struct iked_sa *sa, break; } - log_debug("%s: method %s keylen %d type %s", __func__, + log_debug("%s: method %s keylen %zd 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_init(dsa, buf, len) != 0 || dsa_update(dsa, ibuf_data(authmsg), ibuf_size(authmsg))) { log_debug("%s: failed to compute digital signature", __func__); goto done; @@ -753,18 +789,14 @@ ikev2_msg_authverify(struct iked *env, struct iked_sa *sa, 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); + sa_stateflags(sa, IKED_REQ_AUTHVALID); } else { log_debug("%s: authentication failed", __func__); sa_state(env, sa, IKEV2_STATE_AUTH_REQUEST); } done: - if (psk != NULL) - free(psk); + free(psk); dsa_free(dsa); return (ret); @@ -774,14 +806,14 @@ int ikev2_msg_authsign(struct iked *env, struct iked_sa *sa, struct iked_auth *auth, struct ibuf *authmsg) { - u_int8_t *key, *psk = NULL; + uint8_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; + uint8_t keytype; if (sa->sa_hdr.sh_initiator) id = &sa->sa_icert; @@ -819,7 +851,7 @@ ikev2_msg_authsign(struct iked *env, struct iked_sa *sa, } if (dsa_setkey(dsa, key, keylen, keytype) == NULL || - dsa_init(dsa) != 0 || + dsa_init(dsa, NULL, 0) != 0 || dsa_update(dsa, ibuf_data(authmsg), ibuf_size(authmsg))) { log_debug("%s: failed to compute digital signature", __func__); goto done; @@ -845,8 +877,7 @@ ikev2_msg_authsign(struct iked *env, struct iked_sa *sa, ret = 0; done: - if (psk != NULL) - free(psk); + free(psk); dsa_free(dsa); return (ret); @@ -905,7 +936,7 @@ ikev2_msg_dispose(struct iked *env, struct iked_msgqueue *queue, struct iked_message *msg) { TAILQ_REMOVE(queue, msg, msg_entry); - timer_deregister(env, &msg->msg_timer); + timer_del(env, &msg->msg_timer); ikev2_msg_cleanup(env, msg); free(msg); } @@ -942,11 +973,10 @@ ikev2_msg_retransmit_response(struct iked *env, struct iked_sa *sa, ibuf_size(msg->msg_data), 0, (struct sockaddr *)&msg->msg_peer, msg->msg_peerlen)) == -1) { log_warn("%s: sendto", __func__); - sa_free(env, sa); return (-1); } - timer_register(env, &msg->msg_timer, IKED_RESPONSE_TIMEOUT); + timer_add(env, &msg->msg_timer, IKED_RESPONSE_TIMEOUT); return (0); } @@ -975,10 +1005,11 @@ ikev2_msg_retransmit_timeout(struct iked *env, void *arg) return; } /* Exponential timeout */ - timer_register(env, &msg->msg_timer, + timer_add(env, &msg->msg_timer, IKED_RETRANSMIT_TIMEOUT * (2 << (msg->msg_tries++))); } else { - log_debug("%s: retransmit limit reached", __func__); + log_debug("%s: retransmit limit reached for msgid %u", + __func__, msg->msg_msgid); sa_free(env, sa); } } diff --git a/iked/ikev2_pld.c b/iked/ikev2_pld.c index 9e4ed78..ef8c9aa 100644 --- a/iked/ikev2_pld.c +++ b/iked/ikev2_pld.c @@ -1,7 +1,8 @@ -/* $OpenBSD: ikev2_pld.c,v 1.31 2013/03/21 04:30:14 deraadt Exp $ */ +/* $OpenBSD: ikev2_pld.c,v 1.55 2015/10/15 18:40:38 mmcc Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter + * Copyright (c) 2014 Hans-Joerg Hoexer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -16,23 +17,18 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include "openbsd-compat/sys-queue.h" + #include #include #include #include -#if defined(__OpenBSD__) -#include -#endif #include #include #include #include #include -#include #include #include #include @@ -47,42 +43,72 @@ #include "eap.h" #include "dh.h" +int ikev2_validate_pld(struct iked_message *, size_t, size_t, + struct ikev2_payload *); int ikev2_pld_payloads(struct iked *, struct iked_message *, - off_t, size_t, u_int, int); + size_t, size_t, unsigned int); +int ikev2_validate_sa(struct iked_message *, size_t, size_t, + struct ikev2_payload *, struct ikev2_sa_proposal *); int ikev2_pld_sa(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t); + struct iked_message *, size_t, size_t); +int ikev2_validate_xform(struct iked_message *, size_t, size_t, + struct ikev2_transform *); int ikev2_pld_xform(struct iked *, struct ikev2_sa_proposal *, - struct iked_message *, off_t); + struct iked_message *, size_t, size_t); +int ikev2_validate_attr(struct iked_message *, size_t, size_t, + struct ikev2_attribute *); int ikev2_pld_attr(struct iked *, struct ikev2_transform *, - struct iked_message *, off_t, int); + struct iked_message *, size_t, size_t); +int ikev2_validate_ke(struct iked_message *, size_t, size_t, + struct ikev2_payload *, struct ikev2_keyexchange *); int ikev2_pld_ke(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t); + struct iked_message *, size_t, size_t); +int ikev2_validate_id(struct iked_message *, size_t, size_t, + struct ikev2_payload *, struct ikev2_id *); int ikev2_pld_id(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t, u_int); + struct iked_message *, size_t, size_t, unsigned int); +int ikev2_validate_cert(struct iked_message *, size_t, size_t, + struct ikev2_payload *, struct ikev2_cert *); int ikev2_pld_cert(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t); + struct iked_message *, size_t, size_t); +int ikev2_validate_certreq(struct iked_message *, size_t, size_t, + struct ikev2_payload *, struct ikev2_cert *); int ikev2_pld_certreq(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t); + struct iked_message *, size_t, size_t); +int ikev2_validate_nonce(struct iked_message *, size_t, size_t, + struct ikev2_payload *); int ikev2_pld_nonce(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t); + struct iked_message *, size_t, size_t); +int ikev2_validate_notify(struct iked_message *, size_t, size_t, + struct ikev2_payload *, struct ikev2_notify *); int ikev2_pld_notify(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t); + struct iked_message *, size_t, size_t); +int ikev2_validate_delete(struct iked_message *, size_t, size_t, + struct ikev2_payload *, struct ikev2_delete *); int ikev2_pld_delete(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t); + struct iked_message *, size_t, size_t); +int ikev2_validate_ts(struct iked_message *, size_t, size_t, + struct ikev2_payload *, struct ikev2_tsp *); int ikev2_pld_ts(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t, u_int); + struct iked_message *, size_t, size_t, unsigned int); +int ikev2_validate_auth(struct iked_message *, size_t, size_t, + struct ikev2_payload *, struct ikev2_auth *); int ikev2_pld_auth(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t); + struct iked_message *, size_t, size_t); int ikev2_pld_e(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t); + struct iked_message *, size_t); +int ikev2_validate_cp(struct iked_message *, size_t, size_t, + struct ikev2_payload *, struct ikev2_cp *); int ikev2_pld_cp(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t); + struct iked_message *, size_t, size_t); +int ikev2_validate_eap(struct iked_message *, size_t, size_t, + struct ikev2_payload *, struct eap_header *); int ikev2_pld_eap(struct iked *, struct ikev2_payload *, - struct iked_message *, off_t); + struct iked_message *, size_t, size_t); int ikev2_pld_parse(struct iked *env, struct ike_header *hdr, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset) { log_debug("%s: header ispi %s rspi %s" " nextpayload %s version 0x%02x exchange %s flags 0x%02x" @@ -105,94 +131,130 @@ ikev2_pld_parse(struct iked *env, struct ike_header *hdr, offset += sizeof(*hdr); return (ikev2_pld_payloads(env, msg, offset, - betoh32(hdr->ike_length), hdr->ike_nextpayload, 0)); + betoh32(hdr->ike_length), hdr->ike_nextpayload)); +} + +int +ikev2_validate_pld(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld) +{ + uint8_t *msgbuf = ibuf_data(msg->msg_data); + size_t pld_length; + + /* We need at least the generic header. */ + if (left < sizeof(*pld)) { + log_debug("%s: malformed payload: too short for generic " + "header (%zu < %zu)", __func__, left, sizeof(*pld)); + return (-1); + } + memcpy(pld, msgbuf + offset, sizeof(*pld)); + + /* + * We need at least the specified number of bytes. + * pld_length is the full size of the payload including + * the generic payload header. + */ + pld_length = betoh16(pld->pld_length); + if (left < pld_length) { + log_debug("%s: malformed payload: shorter than specified " + "(%zu < %zu)", __func__, left, pld_length); + return (-1); + } + /* + * Sanity check the specified payload size, it must + * be at last the size of the generic payload header. + */ + if (pld_length < sizeof(*pld)) { + log_debug("%s: malformed payload: shorter than minimum " + "header size (%zu < %zu)", __func__, pld_length, + sizeof(*pld)); + return (-1); + } + + return (0); } int ikev2_pld_payloads(struct iked *env, struct iked_message *msg, - off_t offset, size_t length, u_int payload, int quick) + size_t offset, size_t length, unsigned int payload) { struct ikev2_payload pld; - u_int e; + unsigned int e; int ret; - u_int8_t *msgbuf = ibuf_data(msg->msg_data); + uint8_t *msgbuf = ibuf_data(msg->msg_data); + size_t left; /* Check if message was decrypted in an E payload */ e = msg->msg_e ? IKED_E : 0; - if (quick) - print_debug("%s: %spayloads", __func__, - e ? "decrypted " : ""); - else - ikev2_pld_payloads(env, msg, offset, length, payload, 1); + while (payload != 0 && offset < length) { + /* Bytes left in datagram. */ + left = length - offset; - while (payload != 0 && offset < (off_t)length) { - memcpy(&pld, msgbuf + offset, sizeof(pld)); + if (ikev2_validate_pld(msg, offset, left, &pld)) + return (-1); - 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)); + 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)); + /* Skip over generic payload header. */ offset += sizeof(pld); + left -= sizeof(pld); ret = 0; - if (quick) - goto next; - switch (payload | e) { case IKEV2_PAYLOAD_SA: case IKEV2_PAYLOAD_SA | IKED_E: - ret = ikev2_pld_sa(env, &pld, msg, offset); + ret = ikev2_pld_sa(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_KE: case IKEV2_PAYLOAD_KE | IKED_E: - ret = ikev2_pld_ke(env, &pld, msg, offset); + ret = ikev2_pld_ke(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_IDi | IKED_E: case IKEV2_PAYLOAD_IDr | IKED_E: - ret = ikev2_pld_id(env, &pld, msg, offset, payload); + ret = ikev2_pld_id(env, &pld, msg, offset, left, + payload); break; case IKEV2_PAYLOAD_CERT | IKED_E: - ret = ikev2_pld_cert(env, &pld, msg, offset); + ret = ikev2_pld_cert(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_CERTREQ: case IKEV2_PAYLOAD_CERTREQ | IKED_E: - ret = ikev2_pld_certreq(env, &pld, msg, offset); + ret = ikev2_pld_certreq(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_AUTH | IKED_E: - ret = ikev2_pld_auth(env, &pld, msg, offset); + ret = ikev2_pld_auth(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_NONCE: case IKEV2_PAYLOAD_NONCE | IKED_E: - ret = ikev2_pld_nonce(env, &pld, msg, offset); + ret = ikev2_pld_nonce(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_NOTIFY: case IKEV2_PAYLOAD_NOTIFY | IKED_E: - ret = ikev2_pld_notify(env, &pld, msg, offset); + ret = ikev2_pld_notify(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_DELETE | IKED_E: - ret = ikev2_pld_delete(env, &pld, msg, offset); + ret = ikev2_pld_delete(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_TSi | IKED_E: case IKEV2_PAYLOAD_TSr | IKED_E: - ret = ikev2_pld_ts(env, &pld, msg, offset, payload); + ret = ikev2_pld_ts(env, &pld, msg, offset, left, + payload); break; case IKEV2_PAYLOAD_SK: ret = ikev2_pld_e(env, &pld, msg, offset); break; case IKEV2_PAYLOAD_CP | IKED_E: - ret = ikev2_pld_cp(env, &pld, msg, offset); + ret = ikev2_pld_cp(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_EAP | IKED_E: - ret = ikev2_pld_eap(env, &pld, msg, offset); + ret = ikev2_pld_eap(env, &pld, msg, offset, left); break; default: print_hex(msgbuf, offset, @@ -209,34 +271,128 @@ ikev2_pld_payloads(struct iked *env, struct iked_message *msg, if (payload == IKEV2_PAYLOAD_SK) return (0); - next: payload = pld.pld_nextpayload; offset += betoh16(pld.pld_length) - sizeof(pld); } - if (quick) - print_debug("\n"); + return (0); +} +int +ikev2_validate_sa(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld, struct ikev2_sa_proposal *sap) +{ + uint8_t *msgbuf = ibuf_data(msg->msg_data); + size_t pld_length, sap_length; + + pld_length = betoh16(pld->pld_length); + if (pld_length < sizeof(*pld) + sizeof(*sap)) { + log_debug("%s: malformed payload: specified length smaller " + "than minimum size (%zu < %zu)", __func__, pld_length, + sizeof(*pld) + sizeof(*sap)); + return (-1); + } + + /* This will actually be caught by earlier checks. */ + if (left < sizeof(*sap)) { + log_debug("%s: malformed payload: too short for header " + "(%zu < %zu)", __func__, left, sizeof(*sap)); + return (-1); + } + memcpy(sap, msgbuf + offset, sizeof(*sap)); + + sap_length = betoh16(sap->sap_length); + if (sap_length < sizeof(*sap)) { + log_debug("%s: malformed payload: shorter than minimum header " + "size (%zu < %zu)", __func__, sap_length, sizeof(*sap)); + return (-1); + } + if (left < sap_length) { + log_debug("%s: malformed payload: too long for actual payload " + "size (%zu < %zu)", __func__, left, sap_length); + return (-1); + } + /* + * NB: There might be more proposals, we parse only the first one. + * This condition must never be true. + */ + if (pld_length - sizeof(*pld) < sap_length) { + log_debug("%s: payload malformed: SA payload length mismatches " + "proposal substructure length (%lu < %zu)", __func__, + pld_length - sizeof(*pld), sap_length); + return (-1); + } + /* + * If there is only one proposal, sap_length must be the + * total payload size. + */ + if (!sap->sap_more && ((pld_length - sizeof(*pld)) != sap_length)) { + log_debug("%s: payload malformed: SA payload length mismatches " + "single proposal substructure length (%lu != %zu)", + __func__, pld_length - sizeof(*pld), sap_length); + return (-1); + } + /* + * If there are more than one proposal, there must be bytes + * left in the payload. + */ + if (sap->sap_more && ((pld_length - sizeof(*pld)) <= sap_length)) { + log_debug("%s: payload malformed: SA payload too small for " + "further proposals (%zu <= %zu)", __func__, + pld_length - sizeof(*pld), sap_length); + return (-1); + } return (0); } +/* + * NB: This function parses both the SA header and the first proposal. + * Additional proposals are ignored. + */ int ikev2_pld_sa(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset, size_t left) { 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); + uint32_t spi32; + uint64_t spi = 0, spi64; + uint8_t *msgbuf = ibuf_data(msg->msg_data); struct iked_proposals *props; + size_t total; + + if (ikev2_validate_sa(msg, offset, left, pld, &sap)) + return (-1); + + if (sap.sap_more) + log_debug("%s: more than one proposal specified", __func__); + + /* Assumed size of the first proposals, including SPI if present. */ + total = (betoh16(sap.sap_length) - sizeof(sap)); props = &msg->msg_parent->msg_proposals; - memcpy(&sap, msgbuf + offset, sizeof(sap)); offset += sizeof(sap); + left -= sizeof(sap); if (sap.sap_spisize) { + if (left < sap.sap_spisize) { + log_debug("%s: malformed payload: SPI larger than " + "actual payload (%zu < %d)", __func__, left, + sap.sap_spisize); + return (-1); + } + if (total < sap.sap_spisize) { + log_debug("%s: malformed payload: SPI larger than " + "proposal (%zu < %d)", __func__, total, + sap.sap_spisize); + return (-1); + } + if (total < sap.sap_spisize) { + log_debug("%s: malformed payload: SPI too large " + "(%zu < %d)", __func__, total, sap.sap_spisize); + return (-1); + } switch (sap.sap_spisize) { case 4: memcpy(&spi32, msgbuf + offset, 4); @@ -253,6 +409,20 @@ ikev2_pld_sa(struct iked *env, struct ikev2_payload *pld, } offset += sap.sap_spisize; + left -= sap.sap_spisize; + + /* Assumed size of the proposal, now without SPI. */ + total -= sap.sap_spisize; + } + + /* + * As we verified sanity of packet headers, this check will + * be always false, but just to be sure we keep it. + */ + if (left < total) { + log_debug("%s: payload malformed: too long for payload " + "(%zu < %zu)", __func__, left, total); + return (-1); } log_debug("%s: more %d reserved %d length %d" @@ -281,7 +451,7 @@ ikev2_pld_sa(struct iked *env, struct ikev2_payload *pld, * Parse the attached transforms */ if (sap.sap_transforms && - ikev2_pld_xform(env, &sap, msg, offset) != 0) { + ikev2_pld_xform(env, &sap, msg, offset, total) != 0) { log_debug("%s: invalid proposal transforms", __func__); return (-1); } @@ -289,15 +459,48 @@ ikev2_pld_sa(struct iked *env, struct ikev2_payload *pld, return (0); } +int +ikev2_validate_xform(struct iked_message *msg, size_t offset, size_t total, + struct ikev2_transform *xfrm) +{ + uint8_t *msgbuf = ibuf_data(msg->msg_data); + size_t xfrm_length; + + if (total < sizeof(*xfrm)) { + log_debug("%s: payload malformed: too short for header " + "(%zu < %zu)", __func__, total, sizeof(*xfrm)); + return (-1); + } + memcpy(xfrm, msgbuf + offset, sizeof(*xfrm)); + + xfrm_length = betoh16(xfrm->xfrm_length); + if (xfrm_length < sizeof(*xfrm)) { + log_debug("%s: payload malformed: shorter than minimal header " + "(%zu < %zu)", __func__, xfrm_length, sizeof(*xfrm)); + return (-1); + } + if (total < xfrm_length) { + log_debug("%s: malformed payload: too long for payload size " + "(%zu < %zu)", __func__, total, xfrm_length); + return (-1); + } + + return (0); +} + int ikev2_pld_xform(struct iked *env, struct ikev2_sa_proposal *sap, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset, size_t total) { struct ikev2_transform xfrm; char id[BUFSIZ]; - u_int8_t *msgbuf = ibuf_data(msg->msg_data); + int ret = 0; + size_t xfrm_length; - memcpy(&xfrm, msgbuf + offset, sizeof(xfrm)); + if (ikev2_validate_xform(msg, offset, total, &xfrm)) + return (-1); + + xfrm_length = betoh16(xfrm.xfrm_length); switch (xfrm.xfrm_type) { case IKEV2_XFORMTYPE_ENCR: @@ -325,19 +528,21 @@ ikev2_pld_xform(struct iked *env, struct ikev2_sa_proposal *sap, break; } - log_debug("%s: more %d reserved %d length %d" + log_debug("%s: more %d reserved %d length %zu" " type %s id %s", - __func__, xfrm.xfrm_more, xfrm.xfrm_reserved, - betoh16(xfrm.xfrm_length), + __func__, xfrm.xfrm_more, xfrm.xfrm_reserved, 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_pld_attr(env, &xfrm, msg, offset + sizeof(xfrm), - betoh16(xfrm.xfrm_length) - sizeof(xfrm)); + if (xfrm_length > sizeof(xfrm)) { + if (ikev2_pld_attr(env, &xfrm, msg, offset + sizeof(xfrm), + xfrm_length - sizeof(xfrm)) != 0) { + return (-1); + } + } if (ikev2_msg_frompeer(msg)) { if (config_add_transform(msg->msg_parent->msg_prop, @@ -349,26 +554,52 @@ ikev2_pld_xform(struct iked *env, struct ikev2_sa_proposal *sap, } /* Next transform */ - offset += betoh16(xfrm.xfrm_length); + offset += xfrm_length; + total -= xfrm_length; if (xfrm.xfrm_more == IKEV2_XFORM_MORE) - ikev2_pld_xform(env, sap, msg, offset); + ret = ikev2_pld_xform(env, sap, msg, offset, total); + else if (total != 0) { + /* No more transforms but still some data left. */ + log_debug("%s: less data than specified, %zu bytes left", + __func__, total); + ret = -1; + } + + return (ret); +} + +int +ikev2_validate_attr(struct iked_message *msg, size_t offset, size_t total, + struct ikev2_attribute *attr) +{ + uint8_t *msgbuf = ibuf_data(msg->msg_data); + + if (total < sizeof(*attr)) { + log_debug("%s: payload malformed: too short for header " + "(%zu < %zu)", __func__, total, sizeof(*attr)); + return (-1); + } + memcpy(attr, msgbuf + offset, sizeof(*attr)); return (0); } int ikev2_pld_attr(struct iked *env, struct ikev2_transform *xfrm, - struct iked_message *msg, off_t offset, int total) + struct iked_message *msg, size_t offset, size_t total) { struct ikev2_attribute attr; - u_int type; - u_int8_t *msgbuf = ibuf_data(msg->msg_data); + unsigned int type; + uint8_t *msgbuf = ibuf_data(msg->msg_data); + int ret = 0; + size_t attr_length; - memcpy(&attr, msgbuf + offset, sizeof(attr)); + if (ikev2_validate_attr(msg, offset, total, &attr)) + return (-1); type = betoh16(attr.attr_type) & ~IKEV2_ATTRAF_TV; - log_debug("%s: attribute type %s length %d total %d", + log_debug("%s: attribute type %s length %d total %zu", __func__, print_map(type, ikev2_attrtype_map), betoh16(attr.attr_length), total); @@ -381,30 +612,70 @@ ikev2_pld_attr(struct iked *env, struct ikev2_transform *xfrm, msg->msg_attrlength = betoh16(attr.attr_length); } else { /* Type-Length-Value attribute */ + attr_length = betoh16(attr.attr_length); + if (attr_length < sizeof(attr)) { + log_debug("%s: payload malformed: shorter than " + "minimal header (%zu < %zu)", __func__, + attr_length, sizeof(attr)); + return (-1); + } + if (total < attr_length) { + log_debug("%s: payload malformed: attribute larger " + "than actual payload (%zu < %zu)", __func__, + total, attr_length); + return (-1); + } print_hex(msgbuf, offset + sizeof(attr), - betoh16(attr.attr_length) - sizeof(attr)); - offset += betoh16(attr.attr_length); - total -= betoh16(attr.attr_length); + attr_length - sizeof(attr)); + offset += attr_length; + total -= attr_length; } if (total > 0) { /* Next attribute */ - ikev2_pld_attr(env, xfrm, msg, offset, total); + ret = ikev2_pld_attr(env, xfrm, msg, offset, total); } + return (ret); +} + +int +ikev2_validate_ke(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld, struct ikev2_keyexchange *kex) +{ + uint8_t *msgbuf = ibuf_data(msg->msg_data); + size_t pld_length; + + pld_length = betoh16(pld->pld_length); + if (pld_length < sizeof(*pld) + sizeof(*kex)) { + log_debug("%s: malformed payload: specified length smaller " + "than minimum size (%zu < %zu)", __func__, pld_length, + sizeof(*pld) + sizeof(*kex)); + return (-1); + } + + /* This will actually be caught by earlier checks. */ + if (left < sizeof(*kex)) { + log_debug("%s: malformed payload: too short for header " + "(%zu < %zu)", __func__, left, sizeof(*kex)); + return (-1); + } + memcpy(kex, msgbuf + offset, sizeof(*kex)); + return (0); } int ikev2_pld_ke(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset, size_t left) { struct ikev2_keyexchange kex; - u_int8_t *buf; + uint8_t *buf; size_t len; - u_int8_t *msgbuf = ibuf_data(msg->msg_data); + uint8_t *msgbuf = ibuf_data(msg->msg_data); - memcpy(&kex, msgbuf + offset, sizeof(kex)); + if (ikev2_validate_ke(msg, offset, left, pld, &kex)) + return (-1); log_debug("%s: dh group %s reserved %d", __func__, print_map(betoh16(kex.kex_dhgroup), ikev2_xformdh_map), @@ -413,6 +684,17 @@ ikev2_pld_ke(struct iked *env, struct ikev2_payload *pld, buf = msgbuf + offset + sizeof(kex); len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(kex); + if (len == 0) { + log_debug("%s: malformed payload: no KE data given", __func__); + return (-1); + } + /* This will actually be caught by earlier checks. */ + if (left < len) { + log_debug("%s: malformed payload: smaller than specified " + "(%zu < %zu)", __func__, left, len); + return (-1); + } + print_hex(buf, 0, len); if (ikev2_msg_frompeer(msg)) { @@ -426,19 +708,47 @@ ikev2_pld_ke(struct iked *env, struct ikev2_payload *pld, return (0); } +int +ikev2_validate_id(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld, struct ikev2_id *id) +{ + uint8_t *msgbuf = ibuf_data(msg->msg_data); + size_t pld_length; + + pld_length = betoh16(pld->pld_length); + if (pld_length < sizeof(*pld) + sizeof(*id)) { + log_debug("%s: malformed payload: specified length smaller " + "than minimum size (%zu < %zu)", __func__, pld_length, + sizeof(*pld) + sizeof(*id)); + return (-1); + } + + /* This will actually be caught by earlier checks. */ + if (left < sizeof(*id)) { + log_debug("%s: malformed payload: too short for header " + "(%zu < %zu)", __func__, left, sizeof(*id)); + return (-1); + } + memcpy(id, msgbuf + offset, sizeof(*id)); + + return (0); +} + int ikev2_pld_id(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset, u_int payload) + struct iked_message *msg, size_t offset, size_t left, unsigned int payload) { - u_int8_t *ptr; + uint8_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); + uint8_t *msgbuf = ibuf_data(msg->msg_data); char idstr[IKED_ID_SIZE]; - memcpy(&id, msgbuf + offset, sizeof(id)); + if (ikev2_validate_id(msg, offset, left, pld, &id)) + return (-1); + bzero(&idb, sizeof(idb)); /* Don't strip the Id payload header */ @@ -455,7 +765,7 @@ ikev2_pld_id(struct iked *env, struct ikev2_payload *pld, return (-1); } - log_debug("%s: id %s length %d", __func__, idstr, len); + log_debug("%s: id %s length %zu", __func__, idstr, len); if (!ikev2_msg_frompeer(msg)) { ibuf_release(idb.id_buf); @@ -481,23 +791,50 @@ ikev2_pld_id(struct iked *env, struct ikev2_payload *pld, return (0); } +int +ikev2_validate_cert(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld, struct ikev2_cert *cert) +{ + uint8_t *msgbuf = ibuf_data(msg->msg_data); + size_t pld_length; + + pld_length = betoh16(pld->pld_length); + if (pld_length < sizeof(*pld) + sizeof(*cert)) { + log_debug("%s: malformed payload: specified length smaller " + "than minimum size (%zu < %zu)", __func__, pld_length, + sizeof(*pld) + sizeof(*cert)); + return (-1); + } + + /* This will actually be caught by earlier checks. */ + if (left < sizeof(*cert)) { + log_debug("%s: malformed payload: too short for header " + "(%zu < %zu)", __func__, left, sizeof(*cert)); + return (-1); + } + memcpy(cert, msgbuf + offset, sizeof(*cert)); + + return (0); +} + int ikev2_pld_cert(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset, size_t left) { struct ikev2_cert cert; - u_int8_t *buf; + uint8_t *buf; size_t len; struct iked_id *certid; - u_int8_t *msgbuf = ibuf_data(msg->msg_data); + uint8_t *msgbuf = ibuf_data(msg->msg_data); - memcpy(&cert, msgbuf + offset, sizeof(cert)); + if (ikev2_validate_cert(msg, offset, left, pld, &cert)) + return (-1); offset += sizeof(cert); buf = msgbuf + offset; len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(cert); - log_debug("%s: type %s length %d", + log_debug("%s: type %s length %zu", __func__, print_map(cert.cert_type, ikev2_cert_map), len); print_hex(buf, 0, len); @@ -521,32 +858,70 @@ ikev2_pld_cert(struct iked *env, struct ikev2_payload *pld, return (0); } +int +ikev2_validate_certreq(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld, struct ikev2_cert *cert) +{ + uint8_t *msgbuf = ibuf_data(msg->msg_data); + size_t pld_length; + + pld_length = betoh16(pld->pld_length); + if (pld_length < sizeof(*pld) + sizeof(*cert)) { + log_debug("%s: malformed payload: specified length smaller " + "than minimum size (%zu < %zu)", __func__, pld_length, + sizeof(*pld) + sizeof(*cert)); + return (-1); + } + + /* This will actually be caught by earlier checks. */ + if (left < sizeof(*cert)) { + log_debug("%s: malformed payload: too short for header " + "(%zu < %zu)", __func__, left, sizeof(*cert)); + return (-1); + } + memcpy(cert, msgbuf + offset, sizeof(*cert)); + + return (0); +} + int ikev2_pld_certreq(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset, size_t left) { struct iked_sa *sa = msg->msg_sa; struct ikev2_cert cert; - u_int8_t *buf; - size_t len; - u_int8_t *msgbuf = ibuf_data(msg->msg_data); + uint8_t *buf; + ssize_t len; + uint8_t *msgbuf = ibuf_data(msg->msg_data); - memcpy(&cert, msgbuf + offset, sizeof(cert)); + if (ikev2_validate_certreq(msg, offset, left, pld, &cert)) + return (-1); offset += sizeof(cert); buf = msgbuf + offset; len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(cert); - log_debug("%s: type %s signatures length %d", + log_debug("%s: type %s length %zd", __func__, print_map(cert.cert_type, ikev2_cert_map), len); + + /* This will actually be caught by earlier checks. */ + if (len < 0) { + log_debug("%s: invalid certificate request length", __func__); + return (-1); + } + print_hex(buf, 0, len); if (!ikev2_msg_frompeer(msg)) return (0); - if (!len || (len % SHA_DIGEST_LENGTH) != 0) { - log_debug("%s: invalid certificate request", __func__); - return (-1); + if (cert.cert_type == IKEV2_CERT_X509_CERT) { + if (!len) + return (0); + if ((len % SHA_DIGEST_LENGTH) != 0) { + log_debug("%s: invalid certificate request", __func__); + return (-1); + } } if (msg->msg_sa == NULL) @@ -558,30 +933,57 @@ ikev2_pld_certreq(struct iked *env, struct ikev2_payload *pld, else sa->sa_statevalid |= IKED_REQ_CERT; - ca_setreq(env, &sa->sa_hdr, &sa->sa_policy->pol_localid, + ca_setreq(env, sa, &sa->sa_policy->pol_localid, cert.cert_type, buf, len, PROC_CERT); return (0); } +int +ikev2_validate_auth(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld, struct ikev2_auth *auth) +{ + uint8_t *msgbuf = ibuf_data(msg->msg_data); + size_t pld_length; + + pld_length = betoh16(pld->pld_length); + if (pld_length < sizeof(*pld) + sizeof(*auth)) { + log_debug("%s: malformed payload: specified length smaller " + "than minimum size (%zu < %zu)", __func__, pld_length, + sizeof(*pld) + sizeof(*auth)); + return (-1); + } + + /* This will actually be caught by earlier checks. */ + if (left < sizeof(*auth)) { + log_debug("%s: malformed payload: too short for header " + "(%zu < %zu)", __func__, left, sizeof(*auth)); + return (-1); + } + memcpy(auth, msgbuf + offset, sizeof(*auth)); + + return (0); +} + int ikev2_pld_auth(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset, size_t left) { struct ikev2_auth auth; struct iked_id *idp; - u_int8_t *buf; + uint8_t *buf; size_t len; struct iked_sa *sa = msg->msg_sa; - u_int8_t *msgbuf = ibuf_data(msg->msg_data); + uint8_t *msgbuf = ibuf_data(msg->msg_data); - memcpy(&auth, msgbuf + offset, sizeof(auth)); + if (ikev2_validate_auth(msg, offset, left, pld, &auth)) + return (-1); offset += sizeof(auth); buf = msgbuf + offset; len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(auth); - log_debug("%s: method %s length %d", + log_debug("%s: method %s length %zu", __func__, print_map(auth.auth_method, ikev2_auth_map), len); print_hex(buf, 0, len); @@ -608,16 +1010,49 @@ ikev2_pld_auth(struct iked *env, struct ikev2_payload *pld, return (0); } +int +ikev2_validate_nonce(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld) +{ + size_t pld_length; + + /* This will actually be caught by earlier checks. */ + pld_length = betoh16(pld->pld_length); + if (pld_length < sizeof(*pld)) { + log_debug("%s: malformed payload: specified length smaller " + "than minimum size (%zu < %zu)", __func__, pld_length, + sizeof(*pld)); + return (-1); + } + + return (0); +} + int ikev2_pld_nonce(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset, size_t left) { size_t len; - u_int8_t *buf; - u_int8_t *msgbuf = ibuf_data(msg->msg_data); + uint8_t *buf; + uint8_t *msgbuf = ibuf_data(msg->msg_data); + + if (ikev2_validate_nonce(msg, offset, left, pld)) + return (-1); buf = msgbuf + offset; len = betoh16(pld->pld_length) - sizeof(*pld); + + if (len == 0) { + log_debug("%s: malformed payload: no NONCE given", __func__); + return (-1); + } + /* This will actually be caught by earlier checks. */ + if (left < len) { + log_debug("%s: malformed payload: smaller than specified " + "(%zu < %zu)", __func__, left, len); + return (-1); + } + print_hex(buf, 0, len); if (ikev2_msg_frompeer(msg)) { @@ -632,30 +1067,59 @@ ikev2_pld_nonce(struct iked *env, struct ikev2_payload *pld, return (0); } +int +ikev2_validate_notify(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld, struct ikev2_notify *n) +{ + uint8_t *msgbuf = ibuf_data(msg->msg_data); + size_t pld_length; + + pld_length = betoh16(pld->pld_length); + if (pld_length < sizeof(*pld) + sizeof(*n)) { + log_debug("%s: malformed payload: specified length smaller " + "than minimum size (%zu < %zu)", __func__, pld_length, + sizeof(*pld) + sizeof(*n)); + return (-1); + } + + /* This will actually be caught by earlier checks. */ + if (left < sizeof(*n)) { + log_debug("%s: malformed payload: too short for header " + "(%zu < %zu)", __func__, left, sizeof(*n)); + return (-1); + } + memcpy(n, msgbuf + offset, sizeof(*n)); + + return (0); +} + int ikev2_pld_notify(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset, size_t left) { - struct ikev2_notify *n; - u_int8_t *buf, md[SHA_DIGEST_LENGTH]; + struct ikev2_notify n; + uint8_t *buf, md[SHA_DIGEST_LENGTH]; size_t len; - u_int32_t spi32; - u_int64_t spi64; + uint32_t spi32; + uint64_t spi64; struct iked_spi *rekey; - u_int16_t type; - u_int16_t group; + uint16_t type; + uint16_t group; + uint16_t cpi; + uint16_t signature_hash; + uint8_t transform; - if ((n = ibuf_seek(msg->msg_data, offset, sizeof(*n))) == NULL) + if (ikev2_validate_notify(msg, offset, left, pld, &n)) return (-1); - type = betoh16(n->n_type); + 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(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) + 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); @@ -666,9 +1130,14 @@ ikev2_pld_notify(struct iked *env, struct ikev2_payload *pld, switch (type) { case IKEV2_N_NAT_DETECTION_SOURCE_IP: case IKEV2_N_NAT_DETECTION_DESTINATION_IP: + if (len != sizeof(md)) { + log_debug("%s: malformed payload: hash size mismatch" + " (%zu != %zu)", __func__, len, sizeof(md)); + return (-1); + } if (ikev2_nat_detection(env, msg, md, sizeof(md), type) == -1) return (-1); - if (len != sizeof(md) || memcmp(buf, md, len) != 0) { + if (memcmp(buf, md, len) != 0) { log_debug("%s: %s detected NAT, enabling " "UDP encapsulation", __func__, print_map(type, ikev2_n_map)); @@ -683,13 +1152,21 @@ ikev2_pld_notify(struct iked *env, struct ikev2_payload *pld, print_hex(md, 0, sizeof(md)); break; case IKEV2_N_INVALID_KE_PAYLOAD: + if (sa_stateok(msg->msg_sa, IKEV2_STATE_VALID) && + !msg->msg_e) { + log_debug("%s: INVALID_KE_PAYLOAD not encrypted", + __func__); + return (-1); + } if (len != sizeof(group)) { - log_debug("%s: malformed notification", __func__); + log_debug("%s: malformed payload: group size mismatch" + " (%zu != %zu)", __func__, len, sizeof(group)); return (-1); } + /* XXX chould also happen for PFS */ if (!msg->msg_sa->sa_hdr.sh_initiator) { log_debug("%s: not an initiator", __func__); - sa_free(env, msg->msg_sa); + sa_state(env, msg->msg_sa, IKEV2_STATE_CLOSED); msg->msg_sa = NULL; return (-1); } @@ -703,13 +1180,21 @@ ikev2_pld_notify(struct iked *env, struct ikev2_payload *pld, } log_debug("%s: responder selected DH group %d", __func__, group); - sa_free(env, msg->msg_sa); + sa_state(env, msg->msg_sa, IKEV2_STATE_CLOSED); msg->msg_sa = NULL; - timer_initialize(env, &env->sc_inittmr, ikev2_init_ike_sa, - NULL); - timer_register(env, &env->sc_inittmr, IKED_INITIATOR_INITIAL); + + /* + * XXX should also happen for PFS so we have to check state. + */ + timer_set(env, &env->sc_inittmr, ikev2_init_ike_sa, NULL); + timer_add(env, &env->sc_inittmr, IKED_INITIATOR_INITIAL); break; case IKEV2_N_NO_ADDITIONAL_SAS: + if (!msg->msg_e) { + log_debug("%s: NO_ADDITIONAL_SAS not encrypted", + __func__); + return (-1); + } /* This makes sense for Child SAs only atm */ if (msg->msg_sa->sa_stateflags & IKED_REQ_CHILDSA) { ikev2_disable_rekeying(env, msg->msg_sa); @@ -717,7 +1202,11 @@ ikev2_pld_notify(struct iked *env, struct ikev2_payload *pld, } break; case IKEV2_N_REKEY_SA: - if (len != n->n_spisize) { + if (!msg->msg_e) { + log_debug("%s: N_REKEY_SA not encrypted", __func__); + return (-1); + } + if (len != n.n_spisize) { log_debug("%s: malformed notification", __func__); return (-1); } @@ -727,7 +1216,7 @@ ikev2_pld_notify(struct iked *env, struct ikev2_payload *pld, __func__); return (-1); } - switch (n->n_spisize) { + switch (n.n_spisize) { case 4: memcpy(&spi32, buf, len); rekey->spi = betoh32(spi32); @@ -738,55 +1227,146 @@ ikev2_pld_notify(struct iked *env, struct ikev2_payload *pld, break; default: log_debug("%s: invalid spi size %d", __func__, - n->n_spisize); + n.n_spisize); return (-1); } - rekey->spi_size = n->n_spisize; - rekey->spi_protoid = n->n_protoid; + 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)); + print_map(n.n_protoid, ikev2_saproto_map), + print_spi(rekey->spi, n.n_spisize)); break; + case IKEV2_N_IPCOMP_SUPPORTED: + if (!msg->msg_e) { + log_debug("%s: N_IPCOMP_SUPPORTED not encrypted", + __func__); + return (-1); + } + if (len < sizeof(cpi) + sizeof(transform)) { + log_debug("%s: ignoring malformed ipcomp notification", + __func__); + return (0); + } + memcpy(&cpi, buf, sizeof(cpi)); + memcpy(&transform, buf + sizeof(cpi), sizeof(transform)); + log_debug("%s: cpi 0x%x, transform %s, len %zu", __func__, + betoh16(cpi), print_map(transform, ikev2_ipcomp_map), len); + /* we only support deflate */ + if ((msg->msg_policy->pol_flags & IKED_POLICY_IPCOMP) && + (transform == IKEV2_IPCOMP_DEFLATE)) { + msg->msg_sa->sa_ipcomp = transform; + msg->msg_sa->sa_cpi_out = betoh16(cpi); + } + break; + case IKEV2_N_USE_TRANSPORT_MODE: + if (!msg->msg_e) { + log_debug("%s: USE_TRANSPORT_MODE: %s", __func__, + "notification in unencrypted payload"); + return (-1); + } + if (len != 0) { + log_debug("%s: USE_TRANSPORT_MODE: %s", __func__, + "ignoring malformed notification"); + return (0); + } + log_info("%s: USE_TRANSPORT_MODE: %s", __func__, + "notification received"); + if (msg->msg_sa->sa_policy->pol_flags & IKED_POLICY_TRANSPORT) + msg->msg_sa->sa_transport = 1; + break; + case IKEV2_N_SIGNATURE_HASH_ALGORITHMS: + if (msg->msg_e) { + log_debug("%s: SIGNATURE_HASH_ALGORITHMS: encrypted", + __func__); + return (-1); + } + if (msg->msg_sa == NULL || + msg->msg_sa->sa_sigsha2) { + log_debug("%s: SIGNATURE_HASH_ALGORITHMS: no SA or " + "duplicate notify", __func__); + return (-1); + } + if (len < sizeof(signature_hash) || + len % sizeof(signature_hash)) { + log_debug("%s: malformed signature hash notification" + "(%zu bytes)", __func__, len); + return (0); + } + while (len >= sizeof(signature_hash)) { + memcpy(&signature_hash, buf, sizeof(signature_hash)); + signature_hash = betoh16(signature_hash); + log_debug("%s: signature hash %s (%x)", __func__, + print_map(signature_hash, ikev2_sighash_map), + signature_hash); + len -= sizeof(signature_hash); + buf += sizeof(signature_hash); + if (signature_hash == IKEV2_SIGHASH_SHA2_256) + msg->msg_sa->sa_sigsha2 = 1; + } + break; + } + + return (0); +} + +int +ikev2_validate_delete(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld, struct ikev2_delete *del) +{ + uint8_t *msgbuf = ibuf_data(msg->msg_data); + size_t pld_length; + + pld_length = betoh16(pld->pld_length); + if (pld_length < sizeof(*pld) + sizeof(*del)) { + log_debug("%s: malformed payload: specified length smaller " + "than minimum size (%zu < %zu)", __func__, pld_length, + sizeof(*pld) + sizeof(*del)); + return (-1); } + /* This will actually be caught by earlier checks. */ + if (left < sizeof(*del)) { + log_debug("%s: malformed payload: too short for header " + "(%zu < %zu)", __func__, left, sizeof(*del)); + return (-1); + } + memcpy(del, msgbuf + offset, sizeof(*del)); + return (0); } int ikev2_pld_delete(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset, size_t left) { struct iked_childsa **peersas = NULL; struct iked_sa *sa = msg->msg_sa; - struct ikev2_delete *del, *localdel; + struct ikev2_delete del, *localdel; struct ibuf *resp = NULL; - u_int64_t *localspi = NULL; - u_int64_t spi64, spi = 0; - u_int32_t spi32; - u_int8_t *buf, *msgbuf = ibuf_data(msg->msg_data); + uint64_t *localspi = NULL; + uint64_t spi64, spi = 0; + uint32_t spi32; + uint8_t *buf, *msgbuf = ibuf_data(msg->msg_data); size_t found = 0, failed = 0; int cnt, i, len, sz, ret = -1; - /* Skip if it's a reply and we don't have to deal with it */ - if (ikev2_msg_frompeer(msg) && sa && - (sa->sa_stateflags & IKED_REQ_INF)) { - sa->sa_stateflags &= ~IKED_REQ_INF; - if ((sa->sa_stateflags & IKED_REQ_DELETE) == 0) - return (0); - } + /* Skip if it's a response, then we don't have to deal with it */ + if (ikev2_msg_frompeer(msg) && + msg->msg_parent->msg_response) + return (0); - if ((del = ibuf_seek(msg->msg_data, offset, sizeof(*del))) == NULL) + if (ikev2_validate_delete(msg, offset, left, pld, &del)) return (-1); - cnt = betoh16(del->del_nspi); - sz = del->del_spisize; + cnt = betoh16(del.del_nspi); + sz = del.del_spisize; log_debug("%s: proto %s spisize %d nspi %d", - __func__, print_map(del->del_protoid, ikev2_saproto_map), + __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); + buf = msgbuf + offset + sizeof(del); + len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(del); print_hex(buf, 0, len); @@ -795,20 +1375,28 @@ ikev2_pld_delete(struct iked *env, struct ikev2_payload *pld, case 8: break; default: - if (ikev2_msg_frompeer(msg) && - del->del_protoid == IKEV2_SAPROTO_IKE) { + if (del.del_protoid != IKEV2_SAPROTO_IKE) { + log_debug("%s: invalid SPI size", __func__); + return (-1); + } + if (ikev2_msg_frompeer(msg)) { /* Send an empty informational response */ if ((resp = ibuf_static()) == NULL) goto done; ret = ikev2_send_ike_e(env, sa, resp, IKEV2_PAYLOAD_NONE, IKEV2_EXCHANGE_INFORMATIONAL, 1); + msg->msg_parent->msg_responded = 1; ibuf_release(resp); sa_state(env, sa, IKEV2_STATE_CLOSED); - return (ret); + } else { + /* + * We're sending a delete message. Upper layer + * must deal with deletion of the IKE SA. + */ + ret = 0; } - log_debug("%s: invalid SPI size", __func__); - return (-1); + return (ret); } if ((len / sz) != cnt) { @@ -819,7 +1407,7 @@ ikev2_pld_delete(struct iked *env, struct ikev2_payload *pld, if (ikev2_msg_frompeer(msg) && ((peersas = calloc(cnt, sizeof(struct iked_childsa *))) == NULL || - (localspi = calloc(cnt, sizeof(u_int64_t))) == NULL)) { + (localspi = calloc(cnt, sizeof(uint64_t))) == NULL)) { log_warn("%s", __func__); goto done; } @@ -842,13 +1430,13 @@ ikev2_pld_delete(struct iked *env, struct ikev2_payload *pld, continue; if ((peersas[i] = childsa_lookup(sa, spi, - del->del_protoid)) == NULL) { + del.del_protoid)) == NULL) { log_warnx("%s: CHILD SA doesn't exist for spi %s", - __func__, print_spi(spi, del->del_spisize)); - goto done; + __func__, print_spi(spi, del.del_spisize)); + continue; } - if (ikev2_childsa_delete(env, sa, del->del_protoid, spi, + if (ikev2_childsa_delete(env, sa, del.del_protoid, spi, &localspi[i], 0) == -1) failed++; else @@ -864,9 +1452,7 @@ ikev2_pld_delete(struct iked *env, struct ikev2_payload *pld, if (!ikev2_msg_frompeer(msg)) goto done; - if (sa && (sa->sa_stateflags & IKED_REQ_DELETE)) { - /* Finish rekeying */ - sa->sa_stateflags &= ~IKED_REQ_DELETE; + if (msg->msg_parent->msg_response) { ret = 0; goto done; } @@ -880,8 +1466,8 @@ ikev2_pld_delete(struct iked *env, struct ikev2_payload *pld, if ((localdel = ibuf_advance(resp, sizeof(*localdel))) == NULL) goto done; - localdel->del_protoid = del->del_protoid; - localdel->del_spisize = del->del_spisize; + localdel->del_protoid = del.del_protoid; + localdel->del_spisize = del.del_spisize; localdel->del_nspi = htobe16(found); for (i = 0; i < cnt; i++) { @@ -899,44 +1485,69 @@ ikev2_pld_delete(struct iked *env, struct ikev2_payload *pld, } } - log_warnx("%s: deleted %d spis", __func__, found); + log_warnx("%s: deleted %zu spis", __func__, found); } if (found) { ret = ikev2_send_ike_e(env, sa, resp, IKEV2_PAYLOAD_DELETE, IKEV2_EXCHANGE_INFORMATIONAL, 1); + msg->msg_parent->msg_responded = 1; } else { /* XXX should we send an INVALID_SPI notification? */ ret = 0; } done: - if (localspi) - free(localspi); - if (peersas) - free(peersas); + free(localspi); + free(peersas); ibuf_release(resp); return (ret); } +int +ikev2_validate_ts(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld, struct ikev2_tsp *tsp) +{ + uint8_t *msgbuf = ibuf_data(msg->msg_data); + size_t pld_length; + + pld_length = betoh16(pld->pld_length); + if (pld_length < sizeof(*pld) + sizeof(*tsp)) { + log_debug("%s: malformed payload: specified length smaller " + "than minimum size (%zu < %zu)", __func__, pld_length, + sizeof(*pld) + sizeof(*tsp)); + return (-1); + } + + /* This will actually be caught by earlier checks. */ + if (left < sizeof(*tsp)) { + log_debug("%s: malformed payload: too short for header " + "(%zu < %zu)", __func__, left, sizeof(*tsp)); + return (-1); + } + memcpy(tsp, msgbuf + offset, sizeof(*tsp)); + + return (0); +} + int ikev2_pld_ts(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset, u_int payload) + struct iked_message *msg, size_t offset, size_t left, unsigned int payload) { 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); + struct sockaddr_storage ss; + uint8_t buf[2][128]; + uint8_t *msgbuf = ibuf_data(msg->msg_data); - memcpy(&tsp, msgbuf + offset, sizeof(tsp)); + if (ikev2_validate_ts(msg, offset, left, pld, &tsp)) + return (-1); offset += sizeof(tsp); len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(tsp); - log_debug("%s: count %d length %d", __func__, + log_debug("%s: count %d length %zu", __func__, tsp.tsp_count, len); for (i = 0; i < tsp.tsp_count; i++) { @@ -949,38 +1560,33 @@ ikev2_pld_ts(struct iked *env, struct ikev2_payload *pld, betoh16(ts.ts_startport), betoh16(ts.ts_endport)); + bzero(&ss, sizeof(ss)); switch (ts.ts_type) { case IKEV2_TS_IPV4_ADDR_RANGE: - bzero(&s4, sizeof(s4)); - s4.sin_family = AF_INET; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - s4.sin_len = sizeof(s4); -#endif - memcpy(&s4.sin_addr.s_addr, + ss.ss_family = AF_INET; + SET_SS_LEN(&ss, sizeof(struct sockaddr_in)); + memcpy(&((struct sockaddr_in *)&ss)->sin_addr, msgbuf + offset + sizeof(ts), 4); - print_host((struct sockaddr_storage *)&s4, - (char *)buf[0], sizeof(buf[0])); - memcpy(&s4.sin_addr.s_addr, + print_host((struct sockaddr *)&ss, (char *)buf[0], + sizeof(buf[0])); + memcpy(&((struct sockaddr_in *)&ss)->sin_addr, msgbuf + offset + sizeof(ts) + 4, 4); - print_host((struct sockaddr_storage *)&s4, - (char *)buf[1], sizeof(buf[1])); + print_host((struct sockaddr *)&ss, (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; -#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN - s6.sin6_len = sizeof(s6); -#endif - memcpy(&s6.sin6_addr, + ss.ss_family = AF_INET6; + SET_SS_LEN(&ss, sizeof(struct sockaddr_in6)); + memcpy(&((struct sockaddr_in6 *)&ss)->sin6_addr, msgbuf + offset + sizeof(ts), 16); - print_host((struct sockaddr_storage *)&s6, - (char *)buf[0], sizeof(buf[0])); - memcpy(&s6.sin6_addr, + print_host((struct sockaddr *)&ss, (char *)buf[0], + sizeof(buf[0])); + memcpy(&((struct sockaddr_in6 *)&ss)->sin6_addr, msgbuf + offset + sizeof(ts) + 16, 16); - print_host((struct sockaddr_storage *)&s6, - (char *)buf[1], sizeof(buf[1])); + print_host((struct sockaddr *)&ss, (char *)buf[1], + sizeof(buf[1])); log_debug("%s: start %s end %s", __func__, buf[0], buf[1]); break; @@ -996,13 +1602,13 @@ ikev2_pld_ts(struct iked *env, struct ikev2_payload *pld, int ikev2_pld_e(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset) { struct iked_sa *sa = msg->msg_sa; struct ibuf *e = NULL; - u_int8_t *msgbuf = ibuf_data(msg->msg_data); + uint8_t *msgbuf = ibuf_data(msg->msg_data); struct iked_message emsg; - u_int8_t *buf; + uint8_t *buf; size_t len; int ret = -1; @@ -1034,7 +1640,7 @@ ikev2_pld_e(struct iked *env, struct ikev2_payload *pld, TAILQ_INIT(&emsg.msg_proposals); ret = ikev2_pld_payloads(env, &emsg, 0, ibuf_size(e), - pld->pld_nextpayload, 0); + pld->pld_nextpayload); done: ibuf_release(e); @@ -1042,24 +1648,51 @@ ikev2_pld_e(struct iked *env, struct ikev2_payload *pld, return (ret); } +int +ikev2_validate_cp(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld, struct ikev2_cp *cp) +{ + uint8_t *msgbuf = ibuf_data(msg->msg_data); + size_t pld_length; + + pld_length = betoh16(pld->pld_length); + if (pld_length < sizeof(*pld) + sizeof(*cp)) { + log_debug("%s: malformed payload: specified length smaller " + "than minimum size (%zu < %zu)", __func__, pld_length, + sizeof(*pld) + sizeof(*cp)); + return (-1); + } + + /* This will actually be caught by earlier checks. */ + if (left < sizeof(*cp)) { + log_debug("%s: malformed payload: too short for header " + "(%zu < %zu)", __func__, left, sizeof(*cp)); + return (-1); + } + memcpy(cp, msgbuf + offset, sizeof(*cp)); + + return (0); +} + int ikev2_pld_cp(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset, size_t left) { struct ikev2_cp cp; struct ikev2_cfg *cfg; - u_int8_t *buf; + uint8_t *buf; size_t len, i; - u_int8_t *msgbuf = ibuf_data(msg->msg_data); + uint8_t *msgbuf = ibuf_data(msg->msg_data); struct iked_sa *sa = msg->msg_sa; - memcpy(&cp, msgbuf + offset, sizeof(cp)); + if (ikev2_validate_cp(msg, offset, left, pld, &cp)) + return (-1); offset += sizeof(cp); buf = msgbuf + offset; len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(cp); - log_debug("%s: type %s length %d", + log_debug("%s: type %s length %zu", __func__, print_map(cp.cp_type, ikev2_cp_map), len); print_hex(buf, 0, len); @@ -1083,26 +1716,49 @@ ikev2_pld_cp(struct iked *env, struct ikev2_payload *pld, return (0); } +int +ikev2_validate_eap(struct iked_message *msg, size_t offset, size_t left, + struct ikev2_payload *pld, struct eap_header *hdr) +{ + uint8_t *msgbuf = ibuf_data(msg->msg_data); + size_t pld_length; + + pld_length = betoh16(pld->pld_length); + if (pld_length < sizeof(*pld) + sizeof(*hdr)) { + log_debug("%s: malformed payload: specified length smaller " + "than minimum size (%zu < %zu)", __func__, pld_length, + sizeof(*pld) + sizeof(*hdr)); + return (-1); + } + + /* This will actually be caught by earlier checks. */ + if (left < sizeof(*hdr)) { + log_debug("%s: malformed payload: too short for header " + "(%zu < %zu)", __func__, left, sizeof(*hdr)); + return (-1); + } + memcpy(hdr, msgbuf + offset, sizeof(*hdr)); + + return (0); +} + int ikev2_pld_eap(struct iked *env, struct ikev2_payload *pld, - struct iked_message *msg, off_t offset) + struct iked_message *msg, size_t offset, size_t left) { - struct eap_header *hdr; + 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__); + if (ikev2_validate_eap(msg, offset, left, pld, &hdr)) return (-1); - } - - len = betoh16(hdr->eap_length); + 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)); + 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) { @@ -1114,10 +1770,10 @@ ikev2_pld_eap(struct iked *env, struct ikev2_payload *pld, 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); + if (eap_parse(env, sa, eap, msg->msg_response) == -1) + return (-1); + } return (0); } diff --git a/iked/imsg_util.c b/iked/imsg_util.c index 8dcfab2..0a7d886 100644 --- a/iked/imsg_util.c +++ b/iked/imsg_util.c @@ -1,4 +1,4 @@ -/* $OpenBSD: imsg_util.c,v 1.7 2013/03/21 04:30:14 deraadt Exp $ */ +/* $OpenBSD: imsg_util.c,v 1.10 2015/08/21 11:59:27 reyk Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter @@ -16,18 +16,11 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "openbsd-compat/sys-queue.h" + #include #include -#include -#include -#include -#include -#include -#include #include - #include #include #include @@ -52,7 +45,7 @@ ibuf_cat(struct ibuf *dst, struct ibuf *src) void ibuf_zero(struct ibuf *buf) { - memset(buf->buf, 0, buf->wpos); + explicit_bzero(buf->buf, buf->wpos); } struct ibuf * @@ -110,8 +103,10 @@ ibuf_release(struct ibuf *buf) { if (buf == NULL) return; - if (buf->buf != NULL) + if (buf->buf != NULL) { + ibuf_zero(buf); free(buf->buf); + } free(buf); } @@ -123,7 +118,7 @@ ibuf_length(struct ibuf *buf) return (ibuf_size(buf)); } -u_int8_t * +uint8_t * ibuf_data(struct ibuf *buf) { return (ibuf_seek(buf, 0, 0)); diff --git a/iked/log.c b/iked/log.c index e283024..7b1783b 100644 --- a/iked/log.c +++ b/iked/log.c @@ -1,4 +1,4 @@ -/* $OpenBSD: log.c,v 1.3 2013/01/08 10:38:19 reyk Exp $ */ +/* $OpenBSD: log.c,v 1.8 2015/12/07 12:13:51 reyk Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -11,46 +11,65 @@ * 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. + * 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 -#include "openbsd-compat/sys-queue.h" -#include -#include "openbsd-compat/sys-tree.h" - -#include -#include #include #include +#include #include #include -#include - -#include "iked.h" - -int debug; -int verbose; - -void vlog(int, const char *, va_list); -void logit(int, const char *, ...); +#include +#include + +int debug; +int verbose; +const char *log_procname; + +void log_init(int, int); +void log_procinit(const char *); +void log_verbose(int); +void log_warn(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void log_warnx(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void log_info(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void log_debug(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void logit(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void vlog(int, const char *, va_list) + __attribute__((__format__ (printf, 2, 0))); +__dead void fatal(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +__dead void fatalx(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); void -log_init(int n_debug) +log_init(int n_debug, int facility) { extern char *__progname; debug = n_debug; verbose = n_debug; + log_procinit(__progname); if (!debug) - openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); + openlog(__progname, LOG_PID | LOG_NDELAY, facility); tzset(); } +void +log_procinit(const char *procname) +{ + if (procname != NULL) + log_procname = procname; +} + void log_verbose(int v) { @@ -143,49 +162,45 @@ log_debug(const char *emsg, ...) } } -void -print_debug(const char *emsg, ...) +static void +vfatal(const char *emsg, va_list ap) { - va_list ap; - - if (debug && verbose > 2) { - va_start(ap, emsg); - vfprintf(stderr, emsg, ap); - va_end(ap); + static char s[BUFSIZ]; + const char *sep; + + if (emsg != NULL) { + (void)vsnprintf(s, sizeof(s), emsg, ap); + sep = ": "; + } else { + s[0] = '\0'; + sep = ""; } + if (errno) + logit(LOG_CRIT, "%s: %s%s%s", + log_procname, s, sep, strerror(errno)); + else + logit(LOG_CRIT, "%s%s%s", log_procname, sep, s); } void -print_verbose(const char *emsg, ...) +fatal(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); - } + va_list ap; + va_start(ap, emsg); + vfatal(emsg, ap); + va_end(ap); exit(1); } void -fatalx(const char *emsg) +fatalx(const char *emsg, ...) { + va_list ap; + errno = 0; - fatal(emsg); + va_start(ap, emsg); + vfatal(emsg, ap); + va_end(ap); + exit(1); } diff --git a/iked/ocsp.c b/iked/ocsp.c new file mode 100644 index 0000000..5efa661 --- /dev/null +++ b/iked/ocsp.c @@ -0,0 +1,511 @@ +/* $OpenBSD: ocsp.c,v 1.8 2015/12/07 12:46:37 reyk Exp $ */ + +/* + * Copyright (c) 2014 Markus Friedl + * Copyright (c) 2005 Marco Pfatschbacher + * + * 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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "iked.h" + +struct iked_ocsp { + struct iked *ocsp_env; /* back pointer to env */ + struct iked_sahdr ocsp_sh; /* ike sa */ + uint8_t ocsp_type; /* auth type */ + struct iked_socket *ocsp_sock; /* socket to ocsp responder */ + BIO *ocsp_cbio; /* matching OpenSSL obj */ + OCSP_CERTID *ocsp_id; /* ocsp-id for cert */ + OCSP_REQUEST *ocsp_req; /* ocsp-request */ + OCSP_REQ_CTX *ocsp_req_ctx; /* async ocsp-request */ +}; + +struct ocsp_connect { + struct iked_socket oc_sock; + char *oc_path; +}; + +/* priv */ +void ocsp_connect_cb(int, short, void *); +int ocsp_connect_finish(struct iked *, int, struct ocsp_connect *); + +/* unpriv */ +void ocsp_free(struct iked_ocsp *); +void ocsp_callback(int, short, void *); +void ocsp_parse_response(struct iked_ocsp *, OCSP_RESPONSE *); +STACK_OF(X509) *ocsp_load_certs(const char *); +int ocsp_validate_finish(struct iked_ocsp *, int); + + +/* priv */ + +/* async connect to configure ocsp-responder */ +int +ocsp_connect(struct iked *env) +{ + struct ocsp_connect *oc = NULL; + struct addrinfo hints, *res0 = NULL, *res; + char *host = NULL, *port = NULL, *path = NULL; + int use_ssl, fd = -1, ret = -1, error; + + if (env->sc_ocsp_url == 0) { + log_warnx("%s: no ocsp url", __func__); + goto done; + } + if (!OCSP_parse_url(env->sc_ocsp_url, &host, &port, &path, &use_ssl)) { + log_warnx("%s: error parsing OCSP-request-URL: %s", __func__, + env->sc_ocsp_url); + goto done; + } + + if ((fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0)) < 0) { + log_debug("%s: socket failed", __func__); + goto done; + } + if ((oc = calloc(1, sizeof(*oc))) == NULL) { + log_debug("%s: calloc failed", __func__); + goto done; + } + + bzero(&hints, sizeof(struct addrinfo)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(host, port, &hints, &res0); + if (error) { + log_debug("%s: getaddrinfo(%s, %s) failed", + __func__, host, port); + goto done; + } + /* XXX just pick the first answer. we could loop instead */ + for (res = res0; res; res = res->ai_next) + if (res->ai_family == AF_INET) + break; + if (res == NULL) { + log_debug("%s: no addr to connect to for %s:%s", + __func__, host, port); + goto done; + } + + oc->oc_sock.sock_fd = fd; + oc->oc_sock.sock_env = env; + oc->oc_path = path; + path = NULL; + + log_debug("%s: connect(%s, %s)", __func__, host, port); + if (connect(fd, res->ai_addr, res->ai_addrlen) == -1) { + /* register callback for ansync connect */ + if (errno == EINPROGRESS) { + event_set(&oc->oc_sock.sock_ev, fd, EV_WRITE, + ocsp_connect_cb, oc); + event_add(&oc->oc_sock.sock_ev, NULL); + ret = 0; + } else + log_debug("%s: error while connecting: %s", __func__, + strerror(errno)); + } else { + ocsp_connect_finish(env, fd, oc); + ret = 0; + } + done: + if (res0) + freeaddrinfo(res0); + free(host); + free(port); + free(path); + if (ret == -1) { + ocsp_connect_finish(env, -1, oc); + if (fd >= 0) + close(fd); + } + return (ret); +} + +/* callback triggered if connection to ocsp-responder completes/fails */ +void +ocsp_connect_cb(int fd, short event, void *arg) +{ + struct ocsp_connect *oc = arg; + int error, send_fd = -1; + socklen_t len; + + len = sizeof(error); + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + log_warn("%s: getsockopt SOL_SOCKET SO_ERROR", __func__); + } else if (error) { + log_debug("%s: error while connecting: %s", __func__, + strerror(error)); + } else { + send_fd = fd; + } + ocsp_connect_finish(oc->oc_sock.sock_env, send_fd, oc); + + /* if we did not send the fd, we need to close it ourself */ + if (send_fd == -1) + close(fd); +} + +/* send FD+path or error back to CA process */ +int +ocsp_connect_finish(struct iked *env, int fd, struct ocsp_connect *oc) +{ + struct iovec iov[1]; + int iovcnt = 1, ret; + + if (oc && fd >= 0) { + /* the imsg framework will close the FD after send */ + iov[0].iov_base = oc->oc_path; + iov[0].iov_len = strlen(oc->oc_path); + ret = proc_composev_imsg(&env->sc_ps, PROC_CERT, -1, + IMSG_OCSP_FD, -1, fd, iov, iovcnt); + } else { + ret = proc_compose_imsg(&env->sc_ps, PROC_CERT, -1, + IMSG_OCSP_FD, -1, -1, NULL, 0); + if (fd >= 0) + close(fd); + } + if (oc) { + free(oc->oc_path); + free(oc); + } + return (ret); +} + + +/* unpriv */ + +/* validate the certifcate stored in 'data' by querying the ocsp-responder */ +int +ocsp_validate_cert(struct iked *env, struct iked_static_id *id, + void *data, size_t len, struct iked_sahdr sh, uint8_t type) +{ + struct iked_ocsp_entry *ioe; + struct iked_ocsp *ocsp; + BIO *rawcert = NULL, *bissuer = NULL; + X509 *cert = NULL, *issuer = NULL; + + if ((ioe = calloc(1, sizeof(*ioe))) == NULL) + return (-1); + if ((ocsp = calloc(1, sizeof(*ocsp))) == NULL) { + free(ioe); + return (-1); + } + + ocsp->ocsp_env = env; + ocsp->ocsp_sh = sh; + ocsp->ocsp_type = type; + + if ((rawcert = BIO_new_mem_buf(data, len)) == NULL || + (cert = d2i_X509_bio(rawcert, NULL)) == NULL || + (bissuer = BIO_new_file(IKED_OCSP_ISSUER, "r")) == NULL || + (issuer = PEM_read_bio_X509(bissuer, NULL, NULL, NULL)) == NULL || + (ocsp->ocsp_cbio = BIO_new(BIO_s_socket())) == NULL || + (ocsp->ocsp_req = OCSP_REQUEST_new()) == NULL || + !(ocsp->ocsp_id = OCSP_cert_to_id(NULL, cert, issuer)) || + !OCSP_request_add0_id(ocsp->ocsp_req, ocsp->ocsp_id)) + goto err; + + BIO_free(rawcert); + BIO_free(bissuer); + X509_free(cert); + X509_free(issuer); + + ioe->ioe_ocsp = ocsp; + TAILQ_INSERT_TAIL(&env->sc_ocsp, ioe, ioe_entry); + + /* request connection to ocsp-responder */ + proc_compose(&env->sc_ps, PROC_PARENT, IMSG_OCSP_FD, NULL, 0); + return (0); + + err: + ca_sslerror(__func__); + free(ioe); + if (rawcert != NULL) + BIO_free(rawcert); + if (cert != NULL) + X509_free(cert); + if (bissuer != NULL) + BIO_free(bissuer); + if (issuer != NULL) + X509_free(issuer); + ocsp_validate_finish(ocsp, 0); /* failed */ + return (-1); +} + +/* free ocsp query context */ +void +ocsp_free(struct iked_ocsp *ocsp) +{ + if (ocsp != NULL) { + if (ocsp->ocsp_sock != NULL) { + close(ocsp->ocsp_sock->sock_fd); + free(ocsp->ocsp_sock); + } + if (ocsp->ocsp_cbio != NULL) + BIO_free_all(ocsp->ocsp_cbio); + if (ocsp->ocsp_id != NULL) + OCSP_CERTID_free(ocsp->ocsp_id); + + /* XXX not sure about ownership XXX */ + if (ocsp->ocsp_req_ctx != NULL) + OCSP_REQ_CTX_free(ocsp->ocsp_req_ctx); + else if (ocsp->ocsp_req != NULL) + OCSP_REQUEST_free(ocsp->ocsp_req); + + free(ocsp); + } +} + +/* we got a connection to the ocsp responder */ +int +ocsp_receive_fd(struct iked *env, struct imsg *imsg) +{ + struct iked_ocsp_entry *ioe = NULL; + struct iked_ocsp *ocsp = NULL; + struct iked_socket *sock; + char *path = NULL; + int ret = -1; + + log_debug("%s: received socket fd %d", __func__, imsg->fd); + if ((ioe = TAILQ_FIRST(&env->sc_ocsp)) == NULL) { + log_debug("%s: oops, no request for", __func__); + close(imsg->fd); + return (-1); + } + TAILQ_REMOVE(&env->sc_ocsp, ioe, ioe_entry); + ocsp = ioe->ioe_ocsp; + free(ioe); + + if ((sock = calloc(1, sizeof(*sock))) == NULL) + fatal("ocsp_receive_fd: calloc sock"); + + /* note that sock_addr is not set */ + sock->sock_fd = imsg->fd; + sock->sock_env = env; + ocsp->ocsp_sock = sock; + + /* fetch 'path' and 'fd' from imsg */ + if ((path = get_string(imsg->data, IMSG_DATA_SIZE(imsg))) == NULL) + goto done; + + BIO_set_fd(ocsp->ocsp_cbio, imsg->fd, BIO_NOCLOSE); + + if ((ocsp->ocsp_req_ctx = OCSP_sendreq_new(ocsp->ocsp_cbio, + path, NULL, -1)) == NULL) + goto done; + if (!OCSP_REQ_CTX_set1_req(ocsp->ocsp_req_ctx, ocsp->ocsp_req)) + goto done; + + event_set(&sock->sock_ev, sock->sock_fd, EV_WRITE, ocsp_callback, ocsp); + event_add(&sock->sock_ev, NULL); + ret = 0; + done: + if (ret == -1) + ocsp_validate_finish(ocsp, 0); /* failed */ + free(path); + return (ret); +} + +/* load a stack of x509 certificates */ +STACK_OF(X509)* +ocsp_load_certs(const char *file) +{ + BIO *bio = NULL; + STACK_OF(X509) *certs = NULL; + STACK_OF(X509_INFO) *xis = NULL; + X509_INFO *xi; + int i; + + if ((bio = BIO_new_file(file, "r")) == NULL) { + log_warn("%s: BIO_new_file failed for %s", + __func__, file); + return (NULL); + } + if ((xis = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL)) == NULL) { + ca_sslerror(__func__); + goto done; + } + if ((certs = sk_X509_new_null()) == NULL) { + log_debug("%s: sk_X509_new_null failed for %s", __func__, file); + goto done; + } + for (i = 0; i < sk_X509_INFO_num(xis); i++) { + xi = sk_X509_INFO_value(xis, i); + if (xi->x509) { + if (!sk_X509_push(certs, xi->x509)) + goto done; + xi->x509 = NULL; + } + } + + done: + if (bio) + BIO_free(bio); + if (xis) + sk_X509_INFO_pop_free(xis, X509_INFO_free); + if (certs && sk_X509_num(certs) <= 0) { + sk_X509_pop_free(certs, X509_free); + certs = NULL; + } + return (certs); +} + +/* read/write callback that sends the requests and reads the ocsp response */ +void +ocsp_callback(int fd, short event, void *arg) +{ + struct iked_ocsp *ocsp = arg; + struct iked_socket *sock = ocsp->ocsp_sock; + OCSP_RESPONSE *resp = NULL; + + /* + * Only call OCSP_sendreq_nbio() if should_read/write is + * either not requested or read/write can be called. + */ + if ((!BIO_should_read(ocsp->ocsp_cbio) || (event & EV_READ)) && + (!BIO_should_write(ocsp->ocsp_cbio) || (event & EV_WRITE)) && + OCSP_sendreq_nbio(&resp, ocsp->ocsp_req_ctx) != -1 ) { + ocsp_parse_response(ocsp, resp); + return; + } + if (BIO_should_read(ocsp->ocsp_cbio)) + event_set(&sock->sock_ev, sock->sock_fd, EV_READ, + ocsp_callback, ocsp); + else if (BIO_should_write(ocsp->ocsp_cbio)) + event_set(&sock->sock_ev, sock->sock_fd, EV_WRITE, + ocsp_callback, ocsp); + event_add(&sock->sock_ev, NULL); +} + +/* parse the actual OCSP response */ +void +ocsp_parse_response(struct iked_ocsp *ocsp, OCSP_RESPONSE *resp) +{ + int status; + X509_STORE *store = NULL; + STACK_OF(X509) *verify_other = NULL; + OCSP_BASICRESP *bs = NULL; + int verify_flags = 0; + ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; + int reason = 0; + int error = 1; + + if (!resp) { + log_warnx("%s: error querying OCSP responder", __func__); + goto done; + } + + status = OCSP_response_status(resp); + if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { + log_warnx("%s: responder error: %s (%i)\n", __func__, + OCSP_response_status_str(status), status); + goto done; + } + + verify_other = ocsp_load_certs(IKED_OCSP_RESPCERT); + verify_flags |= OCSP_TRUSTOTHER; + if (!verify_other) + goto done; + + bs = OCSP_response_get1_basic(resp); + if (!bs) { + log_warnx("%s: error parsing response", __func__); + goto done; + } + + status = OCSP_check_nonce(ocsp->ocsp_req, bs); + if (status <= 0) { + if (status == -1) + log_warnx("%s: no nonce in response", __func__); + else { + log_warnx("%s: nonce verify error", __func__); + goto done; + } + } + + store = X509_STORE_new(); + status = OCSP_basic_verify(bs, verify_other, store, verify_flags); + if (status < 0) + status = OCSP_basic_verify(bs, NULL, store, 0); + + if (status <= 0) { + ca_sslerror(__func__); + log_warnx("%s: response verify failure", __func__); + goto done; + } else + log_debug("%s: response verify ok", __func__); + + if (!OCSP_resp_find_status(bs, ocsp->ocsp_id, &status, &reason, + &rev, &thisupd, &nextupd)) { + log_warnx("%s: no status found", __func__); + goto done; + } + log_debug("%s: status: %s", __func__, OCSP_cert_status_str(status)); + + if (status == V_OCSP_CERTSTATUS_GOOD) + error = 0; + + done: + if (store) + X509_STORE_free(store); + if (verify_other) + sk_X509_pop_free(verify_other, X509_free); + if (resp) + OCSP_RESPONSE_free(resp); + if (bs) + OCSP_BASICRESP_free(bs); + + ocsp_validate_finish(ocsp, error == 0); +} + +/* + * finish the ocsp_validate_cert() RPC by sending the appropriate + * message back to the IKEv2 process + */ +int +ocsp_validate_finish(struct iked_ocsp *ocsp, int valid) +{ + struct iked *env = ocsp->ocsp_env; + struct iovec iov[2]; + int iovcnt = 2, ret, cmd; + + iov[0].iov_base = &ocsp->ocsp_sh; + iov[0].iov_len = sizeof(ocsp->ocsp_sh); + iov[1].iov_base = &ocsp->ocsp_type; + iov[1].iov_len = sizeof(ocsp->ocsp_type); + + cmd = valid ? IMSG_CERTVALID : IMSG_CERTINVALID; + ret = proc_composev(&env->sc_ps, PROC_IKEV2, cmd, iov, iovcnt); + + ocsp_free(ocsp); + return (ret); +} diff --git a/iked/parse.y b/iked/parse.y index 7c1bda2..dd4bb67 100644 --- a/iked/parse.y +++ b/iked/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.30 2013/03/21 04:30:14 deraadt Exp $ */ +/* $OpenBSD: parse.y,v 1.54 2015/12/09 21:41:49 naddy Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter @@ -24,14 +24,11 @@ %{ #include #include -#include "openbsd-compat/sys-queue.h" + #include #include #include #include -#if defined(__OpenBSD__) -#include -#endif #include #include @@ -65,10 +62,12 @@ static struct file { struct file *pushfile(const char *, int); int popfile(void); int check_file_secrecy(int, const char *); +int check_pubkey(char *, int ); int yyparse(void); int yylex(void); -int yyerror(const char *, ...); -int yywarn(const char *, ...); +int yyerror(const char *, ...) + __attribute__((__format__ (printf, 1, 2))) + __attribute__((__nonnull__ (1))); int kw_cmp(const void *, const void *); int lookup(char *); int lgetc(int); @@ -93,14 +92,15 @@ static int debug = 0; static int rules = 0; static int passive = 0; static int decouple = 0; +static char *ocsp_url = NULL; struct ipsec_xf { const char *name; - u_int id; - u_int length; - u_int keylength; - u_int nonce; - u_int noauth; + unsigned int id; + unsigned int length; + unsigned int keylength; + unsigned int nonce; + unsigned int noauth; }; struct ipsec_transforms { @@ -113,7 +113,7 @@ struct ipsec_transforms { struct ipsec_mode { struct ipsec_transforms *xfs; - u_int8_t ike_exch; + uint8_t ike_exch; }; struct iked_transform ikev2_default_ike_transforms[] = { @@ -123,10 +123,8 @@ struct iked_transform ikev2_default_ike_transforms[] = { { 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 }, @@ -140,13 +138,9 @@ struct iked_transform ikev2_default_esp_transforms[] = { { IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_CBC, 256 }, { IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_CBC, 192 }, { IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_CBC, 128 }, -#ifndef BROKEN_HMAC_SHA2_256 { IKEV2_XFORMTYPE_INTEGR, IKEV2_XFORMAUTH_HMAC_SHA2_256_128 }, -#endif { IKEV2_XFORMTYPE_INTEGR, IKEV2_XFORMAUTH_HMAC_SHA1_96 }, -#ifdef HAVE_ESN { IKEV2_XFORMTYPE_ESN, IKEV2_XFORMESN_ESN }, -#endif { IKEV2_XFORMTYPE_ESN, IKEV2_XFORMESN_NONE }, { 0 } }; @@ -183,7 +177,6 @@ const struct ipsec_xf ikeencxfs[] = { }; 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 }, @@ -200,6 +193,8 @@ const struct ipsec_xf ipsecencxfs[] = { { "aes-256-gmac", IKEV2_XFORMENCR_NULL_AES_GMAC, 32, 32, 4, 1 }, { "blowfish", IKEV2_XFORMENCR_BLOWFISH, 20, 20 }, { "cast", IKEV2_XFORMENCR_CAST, 16, 16 }, + { "chacha20-poly1305", IKEV2_XFORMENCR_CHACHA20_POLY1305, + 32, 32, 4, 1 }, { "null", IKEV2_XFORMENCR_NULL, 0, 0 }, { NULL } }; @@ -241,6 +236,15 @@ const struct ipsec_xf groupxfs[] = { { "grp25", IKEV2_XFORMDH_ECP_192 }, { "ecp224", IKEV2_XFORMDH_ECP_224 }, { "grp26", IKEV2_XFORMDH_ECP_224 }, + { "brainpool224", IKEV2_XFORMDH_BRAINPOOL_P224R1 }, + { "grp27", IKEV2_XFORMDH_BRAINPOOL_P224R1 }, + { "brainpool256", IKEV2_XFORMDH_BRAINPOOL_P256R1 }, + { "grp28", IKEV2_XFORMDH_BRAINPOOL_P256R1 }, + { "brainpool384", IKEV2_XFORMDH_BRAINPOOL_P384R1 }, + { "grp29", IKEV2_XFORMDH_BRAINPOOL_P384R1 }, + { "brainpool512", IKEV2_XFORMDH_BRAINPOOL_P512R1 }, + { "grp30", IKEV2_XFORMDH_BRAINPOOL_P512R1 }, + { "curve25519", IKEV2_XFORMDH_X_CURVE25519 }, { NULL } }; @@ -249,7 +253,7 @@ const struct ipsec_xf methodxfs[] = { { "dss", IKEV2_AUTH_DSS_SIG }, { "ecdsa-256", IKEV2_AUTH_ECDSA_256 }, { "ecdsa-384", IKEV2_AUTH_ECDSA_384 }, - { "ecdsa-512", IKEV2_AUTH_ECDSA_512 }, + { "ecdsa-521", IKEV2_AUTH_ECDSA_521 }, { NULL } }; @@ -272,7 +276,8 @@ const struct ipsec_xf cpxfs[] = { { "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 } + { "access-server", IKEV2_CFG_INTERNAL_IP6_SERVER, AF_INET6 }, + { NULL } }; const struct iked_lifetime deflifetime = { @@ -282,11 +287,10 @@ const struct iked_lifetime deflifetime = { struct ipsec_addr_wrap { struct sockaddr_storage address; - u_int8_t mask; + uint8_t mask; int netaddress; - sa_family_t af; - u_int type; - u_int action; + unsigned int type; + unsigned int action; char *name; struct ipsec_addr_wrap *next; struct ipsec_addr_wrap *tail; @@ -296,13 +300,13 @@ struct ipsec_addr_wrap { struct ipsec_hosts { struct ipsec_addr_wrap *src; struct ipsec_addr_wrap *dst; - u_int16_t sport; - u_int16_t dport; + uint16_t sport; + uint16_t dport; }; struct ipsec_filters { - char *tag; - u_int tap; + char *tag; + unsigned int tap; }; struct ipsec_addr_wrap *host(const char *); @@ -315,22 +319,25 @@ 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 *, +void set_ipmask(struct ipsec_addr_wrap *, uint8_t); +const struct ipsec_xf *parse_xf(const char *, unsigned int, + const struct ipsec_xf *); +const char *print_xf(unsigned int, unsigned int, + const struct ipsec_xf *); +void copy_transforms(unsigned 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 *, int, 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_lifetime *, - struct iked_auth *, struct ipsec_filters *, - struct ipsec_addr_wrap *); + unsigned int *, struct iked_transform *, size_t); +int create_ike(char *, int, uint8_t, struct ipsec_hosts *, + struct ipsec_hosts *, struct ipsec_mode *, + struct ipsec_mode *, uint8_t, + uint8_t, char *, char *, + uint32_t, struct iked_lifetime *, + struct iked_auth *, struct ipsec_filters *, + struct ipsec_addr_wrap *); int create_user(const char *, const char *); int get_id_type(char *); -u_int8_t x2i(unsigned char *); +uint8_t x2i(unsigned char *); int parsekey(unsigned char *, size_t, struct iked_auth *); int parsekeyfile(char *, struct iked_auth *); @@ -340,12 +347,12 @@ struct ipsec_filters *ipsec_filters; typedef struct { union { int64_t number; - u_int8_t ikemode; - u_int8_t dir; - u_int8_t satype; - u_int8_t proto; + uint8_t ikemode; + uint8_t dir; + uint8_t satype; + uint8_t proto; char *string; - u_int16_t port; + uint16_t port; struct ipsec_hosts *hosts; struct ipsec_hosts peers; struct ipsec_addr_wrap *anyhost; @@ -356,7 +363,7 @@ typedef struct { char *dstid; } ids; char *id; - u_int8_t type; + uint8_t type; struct iked_lifetime lifetime; struct iked_auth ikeauth; struct iked_auth ikekey; @@ -374,6 +381,7 @@ typedef struct { %token PASSIVE ACTIVE ANY TAG TAP PROTO LOCAL GROUP NAME CONFIG EAP USER %token IKEV1 FLOW SA TCPMD5 TUNNEL TRANSPORT COUPLE DECOUPLE SET %token INCLUDE LIFETIME BYTES INET INET6 QUICK SKIP DEFAULT +%token IPCOMP OCSP IKELIFETIME %token STRING %token NUMBER %type string @@ -390,12 +398,12 @@ typedef struct { %type id %type transforms %type filters -%type ikeflags ikematch ikemode +%type ikeflags ikematch ikemode ipcomp %type ikeauth %type keyspec %type ike_sa child_sa %type lifetime -%type byte_spec time_spec +%type byte_spec time_spec ikelifetime %type name %type cfg ikecfg ikecfgvals %% @@ -434,6 +442,12 @@ set : SET ACTIVE { passive = 0; } | SET PASSIVE { passive = 1; } | SET COUPLE { decouple = 0; } | SET DECOUPLE { decouple = 1; } + | SET OCSP STRING { + if ((ocsp_url = strdup($3)) == NULL) { + yyerror("cannot set ocsp_url"); + YYERROR; + } + } ; user : USER STRING STRING { @@ -443,9 +457,11 @@ user : USER STRING STRING { ; ikev2rule : IKEV2 name ikeflags satype af proto hosts_list peers - ike_sa child_sa ids lifetime ikeauth ikecfg filters { + ike_sa child_sa ids ikelifetime lifetime ikeauth ikecfg + filters { if (create_ike($2, $5, $6, $7, &$8, $9, $10, $4, $3, - $11.srcid, $11.dstid, &$12, &$13, $15, $14) == -1) + $11.srcid, $11.dstid, $12, &$13, &$14, + $16, $15) == -1) YYERROR; } ; @@ -471,7 +487,8 @@ ikecfgvals : cfg { $$ = $1; } cfg : CONFIG STRING host_spec { const struct ipsec_xf *xf; - if ((xf = parse_xf($2, $3->af, cpxfs)) == NULL) { + xf = parse_xf($2, $3->address.ss_family, cpxfs); + if (xf == NULL) { yyerror("not a valid ikecfg option"); free($2); free($3); @@ -640,7 +657,8 @@ host_spec : STRING { | STRING '/' NUMBER { char *buf; - if (asprintf(&buf, "%s/%lld", $1, $3) == -1) + if (asprintf(&buf, "%s/%lld", $1, + (long long int)$3) == -1) err(1, "host: asprintf"); free($1); if (($$ = host(buf)) == NULL) { @@ -654,8 +672,9 @@ host_spec : STRING { host : host_spec { $$ = $1; } | host_spec '(' host_spec ')' { - if (($1->af != AF_UNSPEC) && ($3->af != AF_UNSPEC) && - ($3->af != $1->af)) { + if (($1->address.ss_family != AF_UNSPEC) && + ($3->address.ss_family != AF_UNSPEC) && + ($3->address.ss_family != $1->address.ss_family)) { yyerror("Flow NAT address family mismatch"); YYERROR; } @@ -757,7 +776,7 @@ ike_sa : /* empty */ { encxfs = ikeencxfs; } transforms { if (($$ = calloc(1, sizeof(*$$))) == NULL) - err(1, "child_sa: calloc"); + err(1, "ike_sa: calloc"); $$->xfs = $3; } ; @@ -774,7 +793,7 @@ child_sa : /* empty */ { } ; -ikeflags : ikematch ikemode { $$ = $1 | $2; } +ikeflags : ikematch ikemode ipcomp { $$ = $1 | $2 | $3; } ; ikematch : /* empty */ { $$ = 0; } @@ -788,6 +807,10 @@ ikemode : /* empty */ { $$ = IKED_POLICY_PASSIVE; } | ACTIVE { $$ = IKED_POLICY_ACTIVE; } ; +ipcomp : /* empty */ { $$ = 0; } + | IPCOMP { $$ = IKED_POLICY_IPCOMP; } + ; + ikeauth : /* empty */ { $$.auth_method = IKEV2_AUTH_RSA_SIG; $$.auth_length = 0; @@ -801,7 +824,7 @@ ikeauth : /* empty */ { $$.auth_method = IKEV2_AUTH_SHARED_KEY_MIC; } | EAP STRING { - u_int i; + unsigned int i; for (i = 0; i < strlen($2); i++) if ($2[i] == '-') @@ -824,14 +847,14 @@ byte_spec : NUMBER { $$ = $1; } | STRING { - u_int64_t bytes = 0; + uint64_t bytes = 0; char unit = 0; if (sscanf($1, "%llu%c", &bytes, &unit) != 2) { yyerror("invalid byte specification: %s", $1); YYERROR; } - switch (toupper(unit)) { + switch (toupper((unsigned char)unit)) { case 'K': bytes *= 1024; break; @@ -853,14 +876,14 @@ time_spec : NUMBER { $$ = $1; } | STRING { - u_int64_t seconds = 0; + uint64_t seconds = 0; char unit = 0; if (sscanf($1, "%llu%c", &seconds, &unit) != 2) { yyerror("invalid time specification: %s", $1); YYERROR; } - switch (tolower(unit)) { + switch (tolower((unsigned char)unit)) { case 'm': seconds *= 60; break; @@ -888,8 +911,15 @@ lifetime : /* empty */ { } ; +ikelifetime : /* empty */ { + $$ = 0; + } + | IKELIFETIME time_spec { + $$ = $2; + } + keyspec : STRING { - u_int8_t *hex; + uint8_t *hex; bzero(&$$, sizeof($$)); @@ -1034,19 +1064,6 @@ yyerror(const char *fmt, ...) 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) { @@ -1077,14 +1094,17 @@ lookup(char *s) { "from", FROM }, { "group", GROUP }, { "ike", IKEV1 }, + { "ikelifetime", IKELIFETIME }, { "ikesa", IKESA }, { "ikev2", IKEV2 }, { "include", INCLUDE }, { "inet", INET }, { "inet6", INET6 }, + { "ipcomp", IPCOMP }, { "lifetime", LIFETIME }, { "local", LOCAL }, { "name", NAME }, + { "ocsp", OCSP }, { "passive", PASSIVE }, { "peer", PEER }, { "port", PORT }, @@ -1123,10 +1143,10 @@ lookup(char *s) #define MAXPUSHBACK 128 -char *parsebuf; -int parseindex; -char pushback_buffer[MAXPUSHBACK]; -int pushback_index = 0; +unsigned char *parsebuf; +int parseindex; +unsigned char pushback_buffer[MAXPUSHBACK]; +int pushback_index = 0; int lgetc(int quotec) @@ -1149,7 +1169,8 @@ lgetc(int quotec) if (quotec) { if ((c = getc(file->stream)) == EOF) { - yyerror("reached end of file while parsing quoted string"); + yyerror("reached end of file while parsing " + "quoted string"); if (popfile() == EOF) return (EOF); return (quotec); @@ -1217,10 +1238,10 @@ findeol(void) int yylex(void) { - char buf[8096]; - char *p, *val; - int quotec, next, c; - int token; + unsigned char buf[8096]; + unsigned char *p, *val; + int quotec, next, c; + int token; top: p = buf; @@ -1241,7 +1262,7 @@ top: return (findeol()); } if (isalnum(c) || c == '_') { - *p++ = (char)c; + *p++ = c; continue; } *p = '\0'; @@ -1281,12 +1302,15 @@ top: } else if (c == quotec) { *p = '\0'; break; + } else if (c == '\0') { + yyerror("syntax error"); + return (findeol()); } if (p + 1 >= buf + sizeof(buf) - 1) { yyerror("string too long"); return (findeol()); } - *p++ = (char)c; + *p++ = c; } yylval.v.string = strdup(buf); if (yylval.v.string == NULL) @@ -1373,8 +1397,8 @@ check_file_secrecy(int fd, const char *fname) 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); + if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) { + warnx("%s: group writable or world read/writable", fname); return (-1); } return (0); @@ -1459,6 +1483,7 @@ parse_config(const char *filename, struct iked *x_env) env->sc_passive = passive ? 1 : 0; env->sc_decoupled = decouple ? 1 : 0; + env->sc_ocsp_url = ocsp_url; if (!rules) log_warnx("%s: no valid configuration rules found", @@ -1555,7 +1580,7 @@ symget(const char *nam) return (NULL); } -u_int8_t +uint8_t x2i(unsigned char *s) { char ss[3]; @@ -1568,13 +1593,13 @@ x2i(unsigned char *s) yyerror("keys need to be specified in hex digits"); return (-1); } - return ((u_int8_t)strtoul(ss, NULL, 16)); + return ((uint8_t)strtoul(ss, NULL, 16)); } int parsekey(unsigned char *hexkey, size_t len, struct iked_auth *auth) { - u_int i; + unsigned int i; bzero(auth, sizeof(*auth)); if ((len / 2) > sizeof(auth->auth_data)) @@ -1631,6 +1656,48 @@ get_id_type(char *string) return (IKEV2_ID_FQDN); } +int +check_pubkey(char *idstr, int type) +{ + char keyfile[PATH_MAX]; + FILE *fp = NULL; + const char *suffix = NULL; + + switch (type) { + case IKEV2_ID_IPV4: + suffix = "ipv4"; + break; + case IKEV2_ID_IPV6: + suffix = "ipv6"; + break; + case IKEV2_ID_FQDN: + suffix = "fqdn"; + break; + case IKEV2_ID_UFQDN: + suffix = "ufqdn"; + break; + default: + /* Unspecified ID or public key not supported for this type */ + return (-1); + } + + lc_string(idstr); + if ((size_t)snprintf(keyfile, sizeof(keyfile), + IKED_CA IKED_PUBKEY_DIR "%s/%s", suffix, + idstr) >= sizeof(keyfile)) { + log_warnx("%s: public key path is too long", __func__); + return (-1); + } + + if ((fp = fopen(keyfile, "r")) == NULL) + return (-1); + fclose(fp); + + log_debug("%s: found public key file %s", __func__, keyfile); + + return (0); +} + struct ipsec_addr_wrap * host(const char *s) { @@ -1695,8 +1762,9 @@ host_v6(const char *s, int prefixlen) 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)); + ipa->address.ss_family = res->ai_family; + SET_SS_LEN(&ipa->address, sizeof(struct sockaddr_in6)); if (prefixlen > 128) prefixlen = 128; ipa->next = NULL; @@ -1710,11 +1778,12 @@ host_v6(const char *s, int prefixlen) 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"); + if (asprintf(&ipa->name, "%s/%d", hbuf, prefixlen) == -1) + err(1, "host_v6: asprintf"); + } else { + if ((ipa->name = strdup(hbuf)) == NULL) + err(1, "host_v6: strdup"); + } freeaddrinfo(res); @@ -1742,16 +1811,13 @@ host_v4(const char *s, int mask) if (ipa == NULL) err(1, "host_v4: calloc"); - ina.sin_family = AF_INET; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - ina.sin_len = sizeof(ina); -#endif memcpy(&ipa->address, &ina, sizeof(ina)); + ipa->address.ss_family = AF_INET; + SET_SS_LEN(&ipa->address, 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; @@ -1767,12 +1833,13 @@ host_dns(const char *s, int mask) { struct ipsec_addr_wrap *ipa = NULL, *head = NULL; struct addrinfo hints, *res0, *res; - int error; + int error, sslen; char hbuf[NI_MAXHOST]; bzero(&hints, sizeof(struct addrinfo)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG; error = getaddrinfo(s, NULL, &hints, &res0); if (error) return (NULL); @@ -1786,22 +1853,26 @@ host_dns(const char *s, int mask) err(1, "host_dns: calloc"); switch (res->ai_family) { case AF_INET: - memcpy(&ipa->address, res->ai_addr, - sizeof(struct sockaddr_in)); + sslen = sizeof(struct sockaddr_in); break; case AF_INET6: - memcpy(&ipa->address, res->ai_addr, - sizeof(struct sockaddr_in6)); + sslen = sizeof(struct sockaddr_in6); + break; + default: + sslen = 0; break; } - error = getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, - sizeof(hbuf), NULL, 0, NI_NUMERICHOST); + if (sslen > 0) + memcpy(&ipa->address, res->ai_addr, sslen); + ipa->address.ss_family = res->ai_family; + SET_SS_LEN(&ipa->address, sslen); + error = getnameinfo((struct sockaddr *)&ipa->address, sslen, + 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) @@ -1817,7 +1888,7 @@ host_dns(const char *s, int mask) * have IPv6 address on a host, you cannot use dns/netmask * syntax. */ - if (ipa->af == AF_INET) + if (ipa->address.ss_family == AF_INET) set_ipmask(ipa, mask == -1 ? 32 : mask); else if (mask != -1) @@ -1848,7 +1919,7 @@ host_any(void) ipa = calloc(1, sizeof(struct ipsec_addr_wrap)); if (ipa == NULL) err(1, "host_any: calloc"); - ipa->af = AF_UNSPEC; + ipa->address.ss_family = AF_UNSPEC; ipa->netaddress = 1; ipa->tail = ipa; return (ipa); @@ -1877,15 +1948,16 @@ ifa_load(void) n = calloc(1, sizeof(struct ipsec_addr_wrap)); if (n == NULL) err(1, "ifa_load: calloc"); - n->af = ifa->ifa_addr->sa_family; + n->address.ss_family = ifa->ifa_addr->sa_family; + SET_SS_LEN(&n->address, SA_LEN(ifa->ifa_addr)); if ((n->name = strdup(ifa->ifa_name)) == NULL) err(1, "ifa_load: strdup"); - if (n->af == AF_INET) { + if (n->address.ss_family == AF_INET) { sa_in = (struct sockaddr_in *)ifa->ifa_addr; memcpy(&n->address, sa_in, sizeof(*sa_in)); sa_in = (struct sockaddr_in *)ifa->ifa_netmask; n->mask = mask2prefixlen((struct sockaddr *)sa_in); - } else if (n->af == AF_INET6) { + } else if (n->address.ss_family == AF_INET6) { sa_in6 = (struct sockaddr_in6 *)ifa->ifa_addr; memcpy(&n->address, sa_in6, sizeof(*sa_in6)); sa_in6 = (struct sockaddr_in6 *)ifa->ifa_netmask; @@ -1931,8 +2003,8 @@ ifa_exists(const char *ifa_name) #endif for (n = iftab; n; n = n->next) { - if (n->af == AF_LINK && !strncmp(n->name, ifa_name, - IFNAMSIZ)) + if (n->address.ss_family == AF_LINK && + !strncmp(n->name, ifa_name, IFNAMSIZ)) return (1); } @@ -1980,7 +2052,6 @@ ifa_grouplookup(const char *ifa_name) } free(ifgr.ifgr_groups); close(s); - return (h); #else return (NULL); @@ -1992,7 +2063,7 @@ ifa_lookup(const char *ifa_name) { struct ipsec_addr_wrap *p = NULL, *h = NULL, *n = NULL; struct sockaddr_in6 *in6; - u_int8_t *s6; + uint8_t *s6; if (iftab == NULL) ifa_load(); @@ -2001,7 +2072,8 @@ ifa_lookup(const char *ifa_name) return (n); for (p = iftab; p; p = p->next) { - if (p->af != AF_INET && p->af != AF_INET6) + if (p->address.ss_family != AF_INET && + p->address.ss_family != AF_INET6) continue; if (strncmp(p->name, ifa_name, IFNAMSIZ)) continue; @@ -2011,17 +2083,17 @@ ifa_lookup(const char *ifa_name) memcpy(n, p, sizeof(struct ipsec_addr_wrap)); if ((n->name = strdup(p->name)) == NULL) err(1, "ifa_lookup: strdup"); - switch (n->af) { + switch (n->address.ss_family) { 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; + s6 = (uint8_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; + uint16_t tmp16; /* for now we can not handle link local, * therefore bail for now @@ -2054,13 +2126,13 @@ ifa_lookup(const char *ifa_name) } void -set_ipmask(struct ipsec_addr_wrap *address, u_int8_t b) +set_ipmask(struct ipsec_addr_wrap *address, uint8_t b) { address->mask = b; } const struct ipsec_xf * -parse_xf(const char *name, u_int length, const struct ipsec_xf xfs[]) +parse_xf(const char *name, unsigned int length, const struct ipsec_xf xfs[]) { int i; @@ -2074,7 +2146,7 @@ parse_xf(const char *name, u_int length, const struct ipsec_xf xfs[]) } const char * -print_xf(u_int id, u_int length, const struct ipsec_xf xfs[]) +print_xf(unsigned int id, unsigned int length, const struct ipsec_xf xfs[]) { int i; @@ -2088,7 +2160,7 @@ print_xf(u_int id, u_int length, const struct ipsec_xf xfs[]) } size_t -keylength_xf(u_int saproto, u_int type, u_int id) +keylength_xf(unsigned int saproto, unsigned int type, unsigned int id) { int i; const struct ipsec_xf *xfs; @@ -2115,7 +2187,7 @@ keylength_xf(u_int saproto, u_int type, u_int id) } size_t -noncelength_xf(u_int type, u_int id) +noncelength_xf(unsigned int type, unsigned int id) { const struct ipsec_xf *xfs = ipsecencxfs; int i; @@ -2142,7 +2214,7 @@ print_policy(struct iked_policy *pol) struct iked_transform *xform; struct iked_flow *flow; struct iked_cfg *cfg; - u_int i, j; + unsigned int i, j; const struct ipsec_xf *xfs = NULL; print_verbose("ikev2"); @@ -2176,7 +2248,8 @@ print_policy(struct iked_policy *pol) RB_FOREACH(flow, iked_flows, &pol->pol_flows) { print_verbose(" from %s", - print_host(&flow->flow_src.addr, NULL, 0)); + print_host((struct sockaddr *)&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); @@ -2185,7 +2258,8 @@ print_policy(struct iked_policy *pol) ntohs(flow->flow_src.addr_port)); print_verbose(" to %s", - print_host(&flow->flow_dst.addr, NULL, 0)); + print_host((struct sockaddr *)&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); @@ -2196,13 +2270,15 @@ print_policy(struct iked_policy *pol) if ((pol->pol_flags & IKED_POLICY_DEFAULT) == 0) { print_verbose(" local %s", - print_host(&pol->pol_local.addr, NULL, 0)); + print_host((struct sockaddr *)&pol->pol_local.addr, NULL, + 0)); if (pol->pol_local.addr.ss_family != AF_UNSPEC && pol->pol_local.addr_net) print_verbose("/%d", pol->pol_local.addr_mask); print_verbose(" peer %s", - print_host(&pol->pol_peer.addr, NULL, 0)); + print_host((struct sockaddr *)&pol->pol_peer.addr, NULL, + 0)); if (pol->pol_peer.addr.ss_family != AF_UNSPEC && pol->pol_peer.addr_net) print_verbose("/%d", pol->pol_peer.addr_mask); @@ -2266,6 +2342,9 @@ print_policy(struct iked_policy *pol) if (pol->pol_peerid.id_length != 0) print_verbose(" dstid %s", pol->pol_peerid.id_data); + if (pol->pol_rekey) + print_verbose(" ikelifetime %u", pol->pol_rekey); + print_verbose(" lifetime %llu bytes %llu", pol->pol_lifetime.lt_seconds, pol->pol_lifetime.lt_bytes); @@ -2287,7 +2366,8 @@ print_policy(struct iked_policy *pol) 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)); + print_host((struct sockaddr *)&cfg->cfg.address.addr, NULL, + 0)); } if (pol->pol_tag[0] != '\0') @@ -2300,12 +2380,12 @@ print_policy(struct iked_policy *pol) } void -copy_transforms(u_int type, const struct ipsec_xf *xf, +copy_transforms(unsigned 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) + unsigned int *n, struct iked_transform *src, size_t nsrc) { - u_int i; + unsigned int i; struct iked_transform *a, *b; if (xf != NULL) { @@ -2332,26 +2412,31 @@ copy_transforms(u_int type, const struct ipsec_xf *xf, } int -create_ike(char *name, int af, u_int8_t ipproto, struct ipsec_hosts *hosts, +create_ike(char *name, int af, uint8_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 flags, char *srcid, char *dstid, struct iked_lifetime *lt, + struct ipsec_mode *ipsec_sa, uint8_t saproto, + uint8_t flags, char *srcid, char *dstid, + uint32_t ikelifetime, struct iked_lifetime *lt, struct iked_auth *authtype, struct ipsec_filters *filter, struct ipsec_addr_wrap *ikecfg) { + char idstr[IKED_ID_SIZE]; + unsigned int idtype = IKEV2_ID_NONE; struct ipsec_addr_wrap *ipa, *ipb; struct iked_policy pol; struct iked_proposal prop[2]; - u_int j; + unsigned int j; struct iked_transform ikexforms[64], ipsecxforms[64]; struct iked_flow flows[64]; - static u_int policy_id = 0; + static unsigned int policy_id = 0; struct iked_cfg *cfg; bzero(&pol, sizeof(pol)); bzero(&prop, sizeof(prop)); + bzero(idstr, sizeof(idstr)); pol.pol_id = ++policy_id; + pol.pol_certreqtype = env->sc_certreqtype; pol.pol_af = af; pol.pol_saproto = saproto; pol.pol_ipproto = ipproto; @@ -2403,15 +2488,16 @@ create_ike(char *name, int af, u_int8_t ipproto, struct ipsec_hosts *hosts, } if (peers && peers->src && peers->dst && - (peers->src->af != AF_UNSPEC) && (peers->dst->af != AF_UNSPEC) && - (peers->src->af != peers->dst->af)) + (peers->src->address.ss_family != AF_UNSPEC) && + (peers->dst->address.ss_family != AF_UNSPEC) && + (peers->src->address.ss_family != peers->dst->address.ss_family)) fatalx("create_ike: peer address family mismatch"); if (peers && (pol.pol_af != AF_UNSPEC) && - ((peers->src && (peers->src->af != AF_UNSPEC) && - (peers->src->af != pol.pol_af)) || - (peers->dst && (peers->dst->af != AF_UNSPEC) && - (peers->dst->af != pol.pol_af)))) + ((peers->src && (peers->src->address.ss_family != AF_UNSPEC) && + (peers->src->address.ss_family != pol.pol_af)) || + (peers->dst && (peers->dst->address.ss_family != AF_UNSPEC) && + (peers->dst->address.ss_family != pol.pol_af)))) fatalx("create_ike: policy address family mismatch"); ipa = ipb = NULL; @@ -2421,6 +2507,11 @@ create_ike(char *name, int af, u_int8_t ipproto, struct ipsec_hosts *hosts, if (peers->dst) ipb = peers->dst; if (ipa == NULL && ipb == NULL) { + /* + * If no peers were specified, flag this policy + * as being able to use transport mode. + */ + pol.pol_flags |= IKED_POLICY_TRANSPORT; if (hosts->src && hosts->src->next == NULL) ipa = hosts->src; if (hosts->dst && hosts->dst->next == NULL) @@ -2441,22 +2532,25 @@ create_ike(char *name, int af, u_int8_t ipproto, struct ipsec_hosts *hosts, if (ipa) { memcpy(&pol.pol_local.addr, &ipa->address, sizeof(ipa->address)); - pol.pol_local.addr_af = ipa->af; + pol.pol_local.addr_af = ipa->address.ss_family; pol.pol_local.addr_mask = ipa->mask; pol.pol_local.addr_net = ipa->netaddress; if (pol.pol_af == AF_UNSPEC) - pol.pol_af = ipa->af; + pol.pol_af = ipa->address.ss_family; } if (ipb) { memcpy(&pol.pol_peer.addr, &ipb->address, sizeof(ipb->address)); - pol.pol_peer.addr_af = ipb->af; + pol.pol_peer.addr_af = ipb->address.ss_family; pol.pol_peer.addr_mask = ipb->mask; pol.pol_peer.addr_net = ipb->netaddress; if (pol.pol_af == AF_UNSPEC) - pol.pol_af = ipb->af; + pol.pol_af = ipb->address.ss_family; } + if (ikelifetime) + pol.pol_rekey = ikelifetime; + if (lt) pol.pol_lifetime = *lt; else @@ -2544,18 +2638,20 @@ create_ike(char *name, int af, u_int8_t ipproto, struct ipsec_hosts *hosts, 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_af = ipa->address.ss_family; 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_af = ipb->address.ss_family; flows[j].flow_dst.addr_mask = ipb->mask; flows[j].flow_dst.addr_net = ipb->netaddress; flows[j].flow_dst.addr_port = hosts->dport; + flows[j].flow_ipproto = ipproto; + pol.pol_nflows++; RB_INSERT(iked_flows, &pol.pol_flows, &flows[j]); } @@ -2572,9 +2668,32 @@ create_ike(char *name, int af, u_int8_t ipproto, struct ipsec_hosts *hosts, sizeof(ipa->address)); cfg->cfg.address.addr_mask = ipa->mask; cfg->cfg.address.addr_net = ipa->netaddress; - cfg->cfg.address.addr_af = ipa->af; + cfg->cfg.address.addr_af = ipa->address.ss_family; } + if (dstid) { + strlcpy(idstr, dstid, sizeof(idstr)); + idtype = pol.pol_peerid.id_type; + } else if (!pol.pol_peer.addr_net) { + print_host((struct sockaddr *)&pol.pol_peer.addr, idstr, + sizeof(idstr)); + switch (pol.pol_peer.addr.ss_family) { + case AF_INET: + idtype = IKEV2_ID_IPV4; + break; + case AF_INET6: + idtype = IKEV2_ID_IPV6; + break; + default: + log_warnx("%s: unknown address family", __func__); + break; + } + } + + /* Check if we have a raw public key for this peer */ + if (check_pubkey(idstr, idtype) != -1) + pol.pol_certreqtype = IKEV2_CERT_RSA_KEY; + config_setpolicy(env, &pol, PROC_IKEV2); rules++; diff --git a/iked/pfkey.c b/iked/pfkey.c index 343e372..3fec341 100644 --- a/iked/pfkey.c +++ b/iked/pfkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pfkey.c,v 1.23 2013/03/21 04:30:14 deraadt Exp $ */ +/* $OpenBSD: pfkey.c,v 1.51 2016/03/07 19:33:26 mmcc Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter @@ -19,20 +19,25 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include "openbsd-compat/sys-queue.h" + #include #include #include -#if defined(__OpenBSD__) +#if defined(HAVE_NETIPSEC_IPSEC_H) +#include +#endif +#if defined(HAVE_NETINET_IP_IPSP_H) #include +#endif +#if defined(HAVE_NET_PFKEYV2_H) #include #endif #include #include #include +#include #include #include #include @@ -44,23 +49,24 @@ #define ROUNDUP(x) (((x) + (PFKEYV2_CHUNK - 1)) & ~(PFKEYV2_CHUNK - 1)) #define IOV_CNT 20 -#define PFKEYV2_CHUNK sizeof(u_int64_t) +#define PFKEYV2_CHUNK sizeof(uint64_t) +#define PFKEY_REPLY_TIMEOUT 1000 -static u_int32_t sadb_msg_seq = 0; -static u_int sadb_decoupled = 0; -static u_int sadb_ipv6refcnt = 0; +static uint32_t sadb_msg_seq = 0; +static unsigned int sadb_decoupled = 0; +static unsigned int sadb_ipv6refcnt = 0; static int pfkey_blockipv6 = 0; -static struct iked_timer pfkey_timer; -static struct iked_timer pfkey_pwrtimer; +static struct event pfkey_timer_ev; +static struct timeval pfkey_timer_tv; struct pfkey_message { SIMPLEQ_ENTRY(pfkey_message) pm_entry; u_int8_t *pm_data; - ssize_t pm_lenght; + ssize_t pm_length; }; -SIMPLEQ_HEAD(, pfkey_message) pfkey_postponed = +SIMPLEQ_HEAD(, pfkey_message) pfkey_retry, pfkey_postponed = SIMPLEQ_HEAD_INITIALIZER(pfkey_postponed); struct pfkey_constmap { @@ -71,9 +77,11 @@ struct pfkey_constmap { static const struct pfkey_constmap pfkey_encr[] = { #ifdef SADB_X_EALG_DES_IV64 - { SADB_X_EALG_DES_IV64, IKEV2_XFORMENCR_DES_IV64 }, + { SADB_X_EALG_DES_IV64, IKEV2_XFORMENCR_DES_IV64 }, #endif +#ifdef SADB_X_EALG_DESCBC { SADB_EALG_DESCBC, IKEV2_XFORMENCR_DES }, +#endif { SADB_EALG_3DESCBC, IKEV2_XFORMENCR_3DES }, #ifdef SADB_X_EALG_RC5 { SADB_X_EALG_RC5, IKEV2_XFORMENCR_RC5 }, @@ -91,7 +99,7 @@ static const struct pfkey_constmap pfkey_encr[] = { { SADB_X_EALG_3IDEA, IKEV2_XFORMENCR_3IDEA }, #endif #ifdef SADB_X_EALG_DES_IV32 - { SADB_X_EALG_DES_IV32, IKEV2_XFORMENCR_DES_IV32 }, + { SADB_X_EALG_DES_IV32, IKEV2_XFORMENCR_DES_IV32 }, #endif #ifdef SADB_X_EALG_RC4 { SADB_X_EALG_RC4, IKEV2_XFORMENCR_RC4 }, @@ -102,7 +110,7 @@ static const struct pfkey_constmap pfkey_encr[] = { { SADB_X_EALG_AESCTR, IKEV2_XFORMENCR_AES_CTR }, #endif #ifdef SADB_X_EALG_AESGCM16 - { SADB_X_EALG_AESGCM16, IKEV2_XFORMENCR_AES_GCM_16 }, + { SADB_X_EALG_AESGCM16, IKEV2_XFORMENCR_AES_GCM_16 }, #endif #ifdef SADB_X_EALG_AESGMAC { SADB_X_EALG_AESGMAC, IKEV2_XFORMENCR_NULL_AES_GMAC }, @@ -117,13 +125,13 @@ static const struct pfkey_constmap pfkey_integr[] = { { SADB_X_AALG_DES, IKEV2_XFORMAUTH_DES_MAC }, #endif #if defined(SADB_X_AALG_SHA2_256) && !defined(BROKEN_HMAC_SHA2_256) - { SADB_X_AALG_SHA2_256, IKEV2_XFORMAUTH_HMAC_SHA2_256_128 }, + { SADB_X_AALG_SHA2_256, IKEV2_XFORMAUTH_HMAC_SHA2_256_128 }, #endif #ifdef SADB_X_AALG_SHA2_384 - { SADB_X_AALG_SHA2_384, IKEV2_XFORMAUTH_HMAC_SHA2_384_192 }, + { SADB_X_AALG_SHA2_384, IKEV2_XFORMAUTH_HMAC_SHA2_384_192 }, #endif #ifdef SADB_X_AALG_SHA2_512 - { SADB_X_AALG_SHA2_512, IKEV2_XFORMAUTH_HMAC_SHA2_512_256 }, + { SADB_X_AALG_SHA2_512, IKEV2_XFORMAUTH_HMAC_SHA2_512_256 }, #endif { 0 } }; @@ -131,28 +139,27 @@ static const struct pfkey_constmap pfkey_integr[] = { static const struct pfkey_constmap pfkey_satype[] = { { SADB_SATYPE_AH, IKEV2_SAPROTO_AH }, { SADB_SATYPE_ESP, IKEV2_SAPROTO_ESP }, + { SADB_X_SATYPE_IPCOMP, IKEV2_SAPROTO_IPCOMP }, { 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 *); -#if defined(_OPENBSD_IPSEC_API_VERSION) -int pfkey_sagroup(int, u_int8_t, u_int8_t, +int pfkey_map(const struct pfkey_constmap *, uint16_t, uint8_t *); +int pfkey_flow(int, uint8_t, uint8_t, struct iked_flow *); +int pfkey_sa(int, uint8_t, uint8_t, struct iked_childsa *); +int pfkey_sa_getspi(int, uint8_t, struct iked_childsa *, uint32_t *); +int pfkey_sagroup(int, uint8_t, uint8_t, struct iked_childsa *, struct iked_childsa *); -#endif int pfkey_write(int, struct sadb_msg *, struct iovec *, int, - u_int8_t **, ssize_t *); -int pfkey_reply(int, u_int8_t **, ssize_t *); + uint8_t **, ssize_t *); +int pfkey_reply(int, uint8_t **, ssize_t *); void pfkey_dispatch(int, short, void *); struct sadb_ident * - pfkey_id2ident(struct iked_id *, u_int); -void *pfkey_find_ext(u_int8_t *, ssize_t, int); + pfkey_id2ident(struct iked_id *, unsigned int); +void *pfkey_find_ext(uint8_t *, ssize_t, int); -void pfkey_timer_cb(struct iked *, void *); -void pfkey_process(struct iked *, struct pfkey_message *); +void pfkey_timer_cb(int, short, void *); +int pfkey_process(struct iked *, struct pfkey_message *); int pfkey_couple(int sd, struct iked_sas *sas, int couple) @@ -160,42 +167,43 @@ pfkey_couple(int sd, struct iked_sas *sas, int couple) struct iked_sa *sa; struct iked_flow *flow; struct iked_childsa *csa; - u_int old; const char *mode[] = { "coupled", "decoupled" }; /* Socket is not ready */ if (sd == -1) return (-1); - old = sadb_decoupled ? 1 : 0; - sadb_decoupled = couple ? 0 : 1; - - if (old == sadb_decoupled) + if (sadb_decoupled == !couple) return (0); log_debug("%s: kernel %s -> %s", __func__, - mode[old], mode[sadb_decoupled]); + mode[sadb_decoupled], mode[!sadb_decoupled]); + + /* Allow writes to the PF_KEY socket */ + sadb_decoupled = 0; RB_FOREACH(sa, iked_sas, sas) { TAILQ_FOREACH(csa, &sa->sa_childsas, csa_entry) { - if (!csa->csa_loaded && !sadb_decoupled) + if (!csa->csa_loaded && couple) (void)pfkey_sa_add(sd, csa, NULL); - else if (csa->csa_loaded && sadb_decoupled) + else if (csa->csa_loaded && !couple) (void)pfkey_sa_delete(sd, csa); } TAILQ_FOREACH(flow, &sa->sa_flows, flow_entry) { - if (!flow->flow_loaded && !sadb_decoupled) + if (!flow->flow_loaded && couple) (void)pfkey_flow_add(sd, flow); - else if (flow->flow_loaded && sadb_decoupled) + else if (flow->flow_loaded && !couple) (void)pfkey_flow_delete(sd, flow); } } + sadb_decoupled = !couple; + return (0); } int -pfkey_map(const struct pfkey_constmap *map, u_int16_t alg, u_int8_t *pfkalg) +pfkey_map(const struct pfkey_constmap *map, uint16_t alg, uint8_t *pfkalg) { int i; @@ -208,7 +216,7 @@ pfkey_map(const struct pfkey_constmap *map, u_int16_t alg, u_int8_t *pfkalg) } int -pfkey_flow(int sd, u_int8_t satype, u_int8_t action, struct iked_flow *flow) +pfkey_flow(int sd, uint8_t satype, uint8_t action, struct iked_flow *flow) { #if defined(_OPENBSD_IPSEC_API_VERSION) struct sadb_msg smsg; @@ -219,19 +227,16 @@ pfkey_flow(int sd, u_int8_t satype, u_int8_t action, struct iked_flow *flow) struct sockaddr_storage ssrc, sdst, slocal, speer, smask, dmask; struct iovec iov[IOV_CNT]; int iov_cnt, 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); + socket_af((struct sockaddr *)&ssrc, flow->flow_src.addr_port); + socket_af((struct sockaddr *)&smask, flow->flow_src.addr_port ? + 0xffff : 0); switch (flow->flow_src.addr_af) { case AF_INET: @@ -242,7 +247,7 @@ pfkey_flow(int sd, u_int8_t satype, u_int8_t action, struct iked_flow *flow) case AF_INET6: prefixlen2mask6(flow->flow_src.addr_net ? flow->flow_src.addr_mask : 128, - (u_int32_t *)((struct sockaddr_in6 *) + (uint32_t *)((struct sockaddr_in6 *) &smask)->sin6_addr.s6_addr); break; default: @@ -250,16 +255,15 @@ pfkey_flow(int sd, u_int8_t satype, u_int8_t action, struct iked_flow *flow) __func__, flow->flow_src.addr_af); return (-1); } - smask.ss_len = ssrc.ss_len; + SET_SS_LEN(&smask, SS_LEN(&ssrc)); 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); + socket_af((struct sockaddr *)&sdst, flow->flow_dst.addr_port); + socket_af((struct sockaddr *)&dmask, flow->flow_dst.addr_port ? + 0xffff : 0); switch (flow->flow_dst.addr_af) { case AF_INET: @@ -270,7 +274,7 @@ pfkey_flow(int sd, u_int8_t satype, u_int8_t action, struct iked_flow *flow) case AF_INET6: prefixlen2mask6(flow->flow_dst.addr_net ? flow->flow_dst.addr_mask : 128, - (u_int32_t *)((struct sockaddr_in6 *) + (uint32_t *)((struct sockaddr_in6 *) &dmask)->sin6_addr.s6_addr); break; default: @@ -345,11 +349,11 @@ pfkey_flow(int sd, u_int8_t satype, u_int8_t action, struct iked_flow *flow) (sizeof(sa_peer) + ROUNDUP(speer.ss_len)) / 8; /* local id */ - sa_srcid = pfkey_id2ident(flow->flow_srcid, + sa_srcid = pfkey_id2ident(IKESA_SRCID(flow->flow_ikesa), SADB_EXT_IDENTITY_SRC); /* peer id */ - sa_dstid = pfkey_id2ident(flow->flow_dstid, + sa_dstid = pfkey_id2ident(IKESA_DSTID(flow->flow_ikesa), SADB_EXT_IDENTITY_DST); } @@ -454,12 +458,13 @@ pfkey_flow(int sd, u_int8_t satype, u_int8_t action, struct iked_flow *flow) #else /* !_OPENBSD_IPSEC_API_VERSION */ + struct iked_sa *ike_sa = flow->flow_ikesa; struct sadb_msg smsg; struct sadb_address sa_src, sa_dst; struct sadb_x_ipsecrequest sa_ipsec; struct sadb_x_policy sa_policy, *sa_polid; struct sadb_x_sa2 sa_2; - struct sockaddr_storage ssrc, sdst, slocal, speer; + struct sockaddr_storage ssrc, sdst, slocal, speer; struct iovec iov[IOV_CNT]; int iov_cnt, ret = -1; in_port_t sport, dport; @@ -536,7 +541,7 @@ pfkey_flow(int sd, u_int8_t satype, u_int8_t action, struct iked_flow *flow) bzero(&sa_2, sizeof(sa_2)); sa_2.sadb_x_sa2_exttype = SADB_X_EXT_SA2; sa_2.sadb_x_sa2_len = sizeof(sa_2) / 8; - sa_2.sadb_x_sa2_mode = IPSEC_MODE_TUNNEL; + sa_2.sadb_x_sa2_mode = IPSEC_MODE_ANY; bzero(&sa_src, sizeof(sa_src)); sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC; @@ -564,19 +569,20 @@ pfkey_flow(int sd, u_int8_t satype, u_int8_t action, struct iked_flow *flow) bzero(&sa_ipsec, sizeof(sa_ipsec)); sa_ipsec.sadb_x_ipsecrequest_proto = satype == SADB_SATYPE_AH ? IPPROTO_AH : IPPROTO_ESP; - sa_ipsec.sadb_x_ipsecrequest_mode = IPSEC_MODE_TUNNEL; /* XXX */ + sa_ipsec.sadb_x_ipsecrequest_mode = (ike_sa->sa_transport) ? + IPSEC_MODE_TRANSPORT : IPSEC_MODE_TUNNEL; sa_ipsec.sadb_x_ipsecrequest_level = flow->flow_dir == IPSEC_DIR_INBOUND ? IPSEC_LEVEL_USE : IPSEC_LEVEL_REQUIRE; - sa_ipsec.sadb_x_ipsecrequest_len = sizeof(sa_ipsec) + + sa_ipsec.sadb_x_ipsecrequest_len = sizeof(sa_ipsec) + SS_LEN(&slocal) + SS_LEN(&speer); padlen = ROUNDUP(sa_ipsec.sadb_x_ipsecrequest_len) - sa_ipsec.sadb_x_ipsecrequest_len; - sa_ipsec.sadb_x_ipsecrequest_len += padlen; + sa_ipsec.sadb_x_ipsecrequest_len += padlen; sa_policy.sadb_x_policy_len = (sizeof(sa_policy) + sa_ipsec.sadb_x_ipsecrequest_len) / 8; - break; + break; } iov_cnt = 0; @@ -656,7 +662,7 @@ pfkey_flow(int sd, u_int8_t satype, u_int8_t action, struct iked_flow *flow) } int -pfkey_sa(int sd, u_int8_t satype, u_int8_t action, struct iked_childsa *sa) +pfkey_sa(int sd, uint8_t satype, uint8_t action, struct iked_childsa *sa) { struct sadb_msg smsg; struct sadb_sa sadb; @@ -674,7 +680,7 @@ pfkey_sa(int sd, u_int8_t satype, u_int8_t action, struct iked_childsa *sa) struct sadb_sa_natt natt; #endif #endif - struct sockaddr_storage ssrc, sdst; + struct sockaddr_storage ssrc, sdst; struct sadb_ident *sa_srcid, *sa_dstid; struct iked_lifetime *lt; struct iked_policy *pol; @@ -730,7 +736,8 @@ pfkey_sa(int sd, u_int8_t satype, u_int8_t action, struct iked_childsa *sa) bzero(&sa_2, sizeof(sa_2)); sa_2.sadb_x_sa2_exttype = SADB_X_EXT_SA2; sa_2.sadb_x_sa2_len = sizeof(sa_2) / 8; - sa_2.sadb_x_sa2_mode = IPSEC_MODE_TUNNEL; /* XXX */ + sa_2.sadb_x_sa2_mode = (sa->csa_transport) ? IPSEC_MODE_TRANSPORT : + IPSEC_MODE_TUNNEL; #endif bzero(&sa_src, sizeof(sa_src)); @@ -801,26 +808,27 @@ pfkey_sa(int sd, u_int8_t satype, u_int8_t action, struct iked_childsa *sa) natt.sadb_sa_natt_port = ntohs(sa->csa_ikesa->sa_peer.addr_port); #else -#warning PFKEYv2 NAT-T not supported +//#warning PFKEYv2 NAT-T not supported #endif log_debug("%s: udpencap port %u", __func__, ntohs(sa->csa_ikesa->sa_peer.addr_port)); } - if (sa->csa_integrxf) + + if (sa->csa_integrid != 0) if (pfkey_map(pfkey_integr, - sa->csa_integrxf->xform_id, &sadb.sadb_sa_auth) == -1) { + sa->csa_integrid, &sadb.sadb_sa_auth) == -1) { log_warnx("%s: unsupported integrity algorithm %s", - __func__, print_map(sa->csa_integrxf->xform_id, + __func__, print_map(sa->csa_integrid, ikev2_xformauth_map)); return (-1); } - if (sa->csa_encrxf) + if (sa->csa_encrid) if (pfkey_map(pfkey_encr, - sa->csa_encrxf->xform_id, &sadb.sadb_sa_encrypt) == -1) { + sa->csa_encrid, &sadb.sadb_sa_encrypt) == -1) { log_warnx("%s: unsupported encryption algorithm %s", - __func__, print_map(sa->csa_encrxf->xform_id, + __func__, print_map(sa->csa_encrid, ikev2_xformencr_map)); return (-1); } @@ -842,10 +850,9 @@ pfkey_sa(int sd, u_int8_t satype, u_int8_t action, struct iked_childsa *sa) } /* local id */ - sa_srcid = pfkey_id2ident(sa->csa_srcid, SADB_EXT_IDENTITY_SRC); - + sa_srcid = pfkey_id2ident(IKESA_SRCID(sa->csa_ikesa), SADB_EXT_IDENTITY_SRC); /* peer id */ - sa_dstid = pfkey_id2ident(sa->csa_dstid, SADB_EXT_IDENTITY_DST); + sa_dstid = pfkey_id2ident(IKESA_DSTID(sa->csa_ikesa), SADB_EXT_IDENTITY_DST); #if defined(_OPENBSD_IPSEC_API_VERSION) tag = sa->csa_ikesa->sa_tag; @@ -917,8 +924,7 @@ pfkey_sa(int sd, u_int8_t satype, u_int8_t action, struct iked_childsa *sa) if (sa_ltime_soft.sadb_lifetime_len) { /* soft lifetime */ iov[iov_cnt].iov_base = &sa_ltime_soft; - iov[iov_cnt].iov_len = sizeof(sa_ltime_soft); - smsg.sadb_msg_len += sa_ltime_soft.sadb_lifetime_len; + iov[iov_cnt].iov_len = sizeof(sa_ltime_soft); smsg.sadb_msg_len += sa_ltime_soft.sadb_lifetime_len; iov_cnt++; } @@ -1001,6 +1007,120 @@ pfkey_sa(int sd, u_int8_t satype, u_int8_t action, struct iked_childsa *sa) return (pfkey_write(sd, &smsg, iov, iov_cnt, NULL, NULL)); } +#if defined(_OPENBSD_IPSEC_API_VERSION) +int +pfkey_sa_last_used(int sd, struct iked_childsa *sa, u_int64_t *last_used) +{ + struct sadb_msg *msg, smsg; + struct sadb_address sa_src, sa_dst; + struct sadb_sa sadb; + struct sadb_lifetime *sa_life; + struct sockaddr_storage ssrc, sdst; + struct iovec iov[IOV_CNT]; + uint8_t *data; + ssize_t n; + int iov_cnt, ret = -1; + uint8_t satype; + + *last_used = 0; + + if (pfkey_map(pfkey_satype, sa->csa_saproto, &satype) == -1) + return (-1); + + 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_GET; + 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 = 64; + + 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++; + + /* 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 ((ret = pfkey_write(sd, &smsg, iov, iov_cnt, &data, &n)) != 0) + return (-1); + + msg = (struct sadb_msg *)data; + if (msg->sadb_msg_errno != 0) { + errno = msg->sadb_msg_errno; + ret = -1; + log_warn("%s: message", __func__); + goto done; + } + if ((sa_life = pfkey_find_ext(data, n, SADB_X_EXT_LIFETIME_LASTUSE)) + == NULL) { + /* has never been used */ + ret = -1; + goto done; + } + *last_used = sa_life->sadb_lifetime_usetime; + log_debug("%s: last_used %llu", __func__, *last_used); + +done: + bzero(data, n); + free(data); + return (ret); +} +#endif + int pfkey_sa_getspi(int sd, u_int8_t satype, struct iked_childsa *sa, u_int32_t *spip) @@ -1011,12 +1131,9 @@ pfkey_sa_getspi(int sd, u_int8_t satype, struct iked_childsa *sa, struct sadb_spirange sa_spirange; struct sockaddr_storage ssrc, sdst; struct iovec iov[IOV_CNT]; - u_int8_t *data; + uint8_t *data; ssize_t n; int iov_cnt, ret = -1; -#if !defined(_OPENBSD_IPSEC_API_VERSION) - struct sadb_x_sa2 sa_2; -#endif bzero(&ssrc, sizeof(ssrc)); memcpy(&ssrc, &sa->csa_local->addr, sizeof(ssrc)); @@ -1040,32 +1157,21 @@ pfkey_sa_getspi(int sd, u_int8_t satype, struct iked_childsa *sa, smsg.sadb_msg_type = SADB_GETSPI; smsg.sadb_msg_satype = satype; -#if !defined(_OPENBSD_IPSEC_API_VERSION) - bzero(&sa_2, sizeof(sa_2)); - sa_2.sadb_x_sa2_exttype = SADB_X_EXT_SA2; - sa_2.sadb_x_sa2_len = sizeof(sa_2) / 8; - sa_2.sadb_x_sa2_mode = IPSEC_MODE_TUNNEL; -#endif - 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_max = (satype == SADB_X_SATYPE_IPCOMP) ? + (CPI_PRIVATE_MIN - 1) : 0xffffffff; + sa_spirange.sadb_spirange_reserved = 0; bzero(&sa_src, sizeof(sa_src)); sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(SS_LEN(&ssrc))) / 8; sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC; - sa_src.sadb_address_proto = IPSEC_ULPROTO_ANY; - sa_src.sadb_address_prefixlen = (ssrc.ss_family == AF_INET ? - sizeof(struct in_addr) : sizeof(struct in6_addr)) << 3; bzero(&sa_dst, sizeof(sa_dst)); sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(SS_LEN(&sdst))) / 8; sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST; - sa_dst.sadb_address_proto = IPSEC_ULPROTO_ANY; - sa_dst.sadb_address_prefixlen = (sdst.ss_family == AF_INET ? - sizeof(struct in_addr) : sizeof(struct in6_addr)) << 3; iov_cnt = 0; @@ -1074,13 +1180,11 @@ pfkey_sa_getspi(int sd, u_int8_t satype, struct iked_childsa *sa, iov[iov_cnt].iov_len = sizeof(smsg); iov_cnt++; -#if !defined(_OPENBSD_IPSEC_API_VERSION) - /* extended SA header */ - iov[iov_cnt].iov_base = &sa_2; - iov[iov_cnt].iov_len = sizeof(sa_2); - smsg.sadb_msg_len += sa_2.sadb_x_sa2_len; + /* 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++; -#endif /* src addr */ iov[iov_cnt].iov_base = &sa_src; @@ -1100,12 +1204,6 @@ pfkey_sa_getspi(int sd, u_int8_t satype, struct iked_childsa *sa, smsg.sadb_msg_len += sa_dst.sadb_address_len; 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++; - *spip = 0; if ((ret = pfkey_write(sd, &smsg, iov, iov_cnt, &data, &n)) != 0) @@ -1143,7 +1241,7 @@ pfkey_sagroup(int sd, u_int8_t satype1, u_int8_t action, struct sadb_protocol sa_proto; struct iovec iov[IOV_CNT]; int iov_cnt; - u_int8_t satype2; + uint8_t satype2; if (pfkey_map(pfkey_satype, sa2->csa_saproto, &satype2) == -1) return (-1); @@ -1279,14 +1377,36 @@ pfkey_write(int sd, struct sadb_msg *smsg, struct iovec *iov, int iov_cnt, } int -pfkey_reply(int sd, u_int8_t **datap, ssize_t *lenp) +pfkey_reply(int sd, uint8_t **datap, ssize_t *lenp) { struct pfkey_message *pm; struct sadb_msg hdr; ssize_t len; - u_int8_t *data; + uint8_t *data; + struct pollfd pfd[1]; + int n; + + pfd[0].fd = sd; + pfd[0].events = POLLIN; for (;;) { + /* + * We should actually expect the reply to get lost + * as PF_KEY is an unreliable service per the specs. + * Currently we do this by setting a short timeout, + * and if it is not readable in that time, we fail + * the read. + */ + n = poll(pfd, 1, PFKEY_REPLY_TIMEOUT / 1000); + if (n == -1) { + log_warn("%s: poll() failed", __func__); + return (-1); + } + if (n == 0) { + log_warnx("%s: no reply from PF_KEY", __func__); + return (-1); + } + if (recv(sd, &hdr, sizeof(hdr), MSG_PEEK) != sizeof(hdr)) { log_warn("%s: short recv", __func__); return (-1); @@ -1297,11 +1417,13 @@ pfkey_reply(int sd, u_int8_t **datap, ssize_t *lenp) return (-1); } - len = hdr.sadb_msg_len * PFKEYV2_CHUNK; - if ((data = malloc(len)) == NULL) { + if ((data = reallocarray(NULL, hdr.sadb_msg_len, + PFKEYV2_CHUNK)) == NULL) { log_warn("%s: malloc", __func__); return (-1); } + len = hdr.sadb_msg_len * PFKEYV2_CHUNK; + if (read(sd, data, len) != len) { log_warnx("%s: short read", __func__); free(data); @@ -1310,19 +1432,26 @@ pfkey_reply(int sd, u_int8_t **datap, ssize_t *lenp) /* XXX: Only one message can be outstanding. */ if (hdr.sadb_msg_seq == sadb_msg_seq && - hdr.sadb_msg_pid == (u_int32_t)getpid()) + hdr.sadb_msg_pid == (uint32_t)getpid()) break; + /* ignore messages for other processes */ + if (hdr.sadb_msg_pid != 0 && + hdr.sadb_msg_pid != (uint32_t)getpid()) { + free(data); + continue; + } + /* not the reply, enqueue */ if ((pm = malloc(sizeof(*pm))) == NULL) { - log_warn("%s", __func__); + log_warn("%s: malloc", __func__); free(data); return (-1); } pm->pm_data = data; - pm->pm_lenght = len; + pm->pm_length = len; SIMPLEQ_INSERT_TAIL(&pfkey_postponed, pm, pm_entry); - timer_register(NULL, &pfkey_timer, 1); + evtimer_add(&pfkey_timer_ev, &pfkey_timer_tv); } if (datap) { @@ -1345,7 +1474,7 @@ pfkey_reply(int sd, u_int8_t **datap, ssize_t *lenp) int pfkey_flow_add(int fd, struct iked_flow *flow) { - u_int8_t satype; + uint8_t satype; if (flow->flow_loaded) return (0); @@ -1370,7 +1499,7 @@ pfkey_flow_add(int fd, struct iked_flow *flow) int pfkey_flow_delete(int fd, struct iked_flow *flow) { - u_int8_t satype; + uint8_t satype; if (!flow->flow_loaded) return (0); @@ -1393,26 +1522,17 @@ pfkey_flow_delete(int fd, struct iked_flow *flow) } int -pfkey_block(int fd, int af, u_int action) +pfkey_block(int fd, int af, unsigned int action) { +#if defined(_OPENBSD_IPSEC_API_VERSION) struct iked_flow flow; -#if !defined(_OPENBSD_IPSEC_API_VERSION) && !defined(XXX_BLOCK_IPV6) - /* - * XXX This is work in progress, I didn't have time to make - * XXX this work with PFKEYv2 on KAME-based platforms yet. - */ -#warning PFKEYv2 IPv6 blocking not supported - log_debug("%s: not yet supported on this platform, ignored", __func__); - return (0); -#endif - if (!pfkey_blockipv6) return (0); /* * Prevent VPN traffic leakages in dual-stack hosts/networks. - * http://tools.ietf.org/html/draft-gont-opsec-vpn-leakages. + * https://tools.ietf.org/html/draft-gont-opsec-vpn-leakages. * We forcibly block IPv6 traffic unless it is used in any of * the flows by tracking a sadb_ipv6refcnt reference counter. */ @@ -1428,14 +1548,18 @@ pfkey_block(int fd, int af, u_int action) if (pfkey_flow(fd, 0, action, &flow) == -1) return (-1); +#else + /* XXX the action above currently fails on KAME */ + pfkey_blockipv6 = 0; +#endif return (0); } int -pfkey_sa_init(int fd, struct iked_childsa *sa, u_int32_t *spi) +pfkey_sa_init(int fd, struct iked_childsa *sa, uint32_t *spi) { - u_int8_t satype; + uint8_t satype; if (pfkey_map(pfkey_satype, sa->csa_saproto, &satype) == -1) return (-1); @@ -1451,8 +1575,8 @@ pfkey_sa_init(int fd, struct iked_childsa *sa, u_int32_t *spi) int pfkey_sa_add(int fd, struct iked_childsa *sa, struct iked_childsa *last) { - u_int8_t satype; - u_int cmd; + uint8_t satype; + unsigned int cmd; if (pfkey_map(pfkey_satype, sa->csa_saproto, &satype) == -1) return (-1); @@ -1466,9 +1590,19 @@ pfkey_sa_add(int fd, struct iked_childsa *sa, struct iked_childsa *last) print_spi(sa->csa_spi.spi, 4)); if (pfkey_sa(fd, satype, cmd, sa) == -1) { - if (cmd == SADB_ADD) + if (cmd == SADB_ADD) { (void)pfkey_sa_delete(fd, sa); - return (-1); + return (-1); + } + if (sa->csa_allocated && !sa->csa_loaded && errno == ESRCH) { + /* Needed for recoupling local SAs */ + log_debug("%s: SADB_UPDATE on local SA returned ESRCH," + " trying SADB_ADD", __func__); + if (pfkey_sa(fd, satype, SADB_ADD, sa) == -1) + return (-1); + } else { + return (-1); + } } if (last && cmd == SADB_ADD) { @@ -1488,7 +1622,7 @@ pfkey_sa_add(int fd, struct iked_childsa *sa, struct iked_childsa *last) int pfkey_sa_delete(int fd, struct iked_childsa *sa) { - u_int8_t satype; + uint8_t satype; if (!sa->csa_loaded || sa->csa_spi.spi == 0) return (0); @@ -1504,7 +1638,7 @@ pfkey_sa_delete(int fd, struct iked_childsa *sa) } int -pfkey_flush(int sd, u_int what) +pfkey_flush(int sd) { struct sadb_msg smsg; struct iovec iov[IOV_CNT]; @@ -1515,7 +1649,7 @@ pfkey_flush(int sd, u_int what) smsg.sadb_msg_seq = ++sadb_msg_seq; smsg.sadb_msg_pid = getpid(); smsg.sadb_msg_len = sizeof(smsg) / 8; - smsg.sadb_msg_type = what; + smsg.sadb_msg_type = SADB_FLUSH; smsg.sadb_msg_satype = SADB_SATYPE_UNSPEC; iov_cnt = 0; @@ -1528,10 +1662,10 @@ pfkey_flush(int sd, u_int what) } struct sadb_ident * -pfkey_id2ident(struct iked_id *id, u_int exttype) +pfkey_id2ident(struct iked_id *id, unsigned int exttype) { char idstr[IKED_ID_SIZE]; - u_int type; + unsigned int type; size_t len; struct sadb_ident *sa_id; @@ -1572,27 +1706,6 @@ pfkey_id2ident(struct iked_id *id, u_int exttype) return (sa_id); } -void -pfkey_pwrtimer_cb(struct iked *env, void *arg) -{ -#ifdef __APPLE__ - static struct timeval waketime = { 0, 0 }; - struct timeval tv; - size_t size = sizeof(tv); - - if (sysctlbyname("kern.waketime", &tv, &size, NULL, 0) == 0) { - if (waketime.tv_sec != 0 && - memcmp(&waketime, &tv, sizeof(waketime)) != 0) { - log_info("%s: after power resume", __func__); - pfkey_flush(env->sc_pfkey, SADB_FLUSH); - } - memcpy(&waketime, &tv, sizeof(waketime)); - } -#endif - - timer_register(env, &pfkey_pwrtimer, 3); -} - int pfkey_socket(void) { @@ -1604,8 +1717,7 @@ pfkey_socket(void) if ((fd = socket(PF_KEY, SOCK_RAW, PF_KEY_V2)) == -1) fatal("pfkey_socket: failed to open PF_KEY socket"); - pfkey_flush(fd, SADB_FLUSH); - pfkey_flush(fd, SADB_X_SPDFLUSH); + pfkey_flush(fd); return (fd); } @@ -1616,6 +1728,11 @@ pfkey_init(struct iked *env, int fd) struct sadb_msg smsg; struct iovec iov; + /* Set up a timer to process messages deferred by the pfkey_reply */ + pfkey_timer_tv.tv_sec = 1; + pfkey_timer_tv.tv_usec = 0; + evtimer_set(&pfkey_timer_ev, pfkey_timer_cb, env); + /* Register the pfkey socket event handler */ env->sc_pfkey = fd; event_set(&env->sc_pfkeyev, env->sc_pfkey, @@ -1651,13 +1768,6 @@ pfkey_init(struct iked *env, int fd) if (pfkey_write(fd, &smsg, &iov, 1, NULL, NULL)) fatal("pfkey_init: failed to set up AH acquires"); - /* Set up a timer to process messages deferred by the pfkey_reply */ - timer_initialize(env, &pfkey_timer, pfkey_timer_cb, NULL); - - /* Run a timer to flush the SADB after power resume */ - timer_initialize(env, &pfkey_pwrtimer, pfkey_pwrtimer_cb, NULL); - timer_register(env, &pfkey_pwrtimer, 3); - if (env->sc_opts & IKED_OPT_NOIPV6BLOCKING) return; @@ -1668,15 +1778,15 @@ pfkey_init(struct iked *env, int fd) } void * -pfkey_find_ext(u_int8_t *data, ssize_t len, int type) +pfkey_find_ext(uint8_t *data, ssize_t len, int type) { struct sadb_ext *ext = (struct sadb_ext *)(data + sizeof(struct sadb_msg)); - while (ext && ((u_int8_t *)ext - data < len)) { + while (ext && ((uint8_t *)ext - data < len)) { if (ext->sadb_ext_type == type) return (ext); - ext = (struct sadb_ext *)((u_int8_t *)ext + + ext = (struct sadb_ext *)((uint8_t *)ext + ext->sadb_ext_len * PFKEYV2_CHUNK); } @@ -1687,10 +1797,10 @@ void pfkey_dispatch(int sd, short event, void *arg) { struct iked *env = (struct iked *)arg; - struct pfkey_message pm; + struct pfkey_message pm, *pmp; struct sadb_msg hdr; ssize_t len; - u_int8_t *data; + uint8_t *data; if (recv(sd, &hdr, sizeof(hdr), MSG_PEEK) != sizeof(hdr)) { log_warn("%s: short recv", __func__); @@ -1702,39 +1812,67 @@ pfkey_dispatch(int sd, short event, void *arg) return; } - len = hdr.sadb_msg_len * PFKEYV2_CHUNK; - if ((data = malloc(len)) == NULL) { + if ((data = reallocarray(NULL, hdr.sadb_msg_len, PFKEYV2_CHUNK)) + == NULL) { log_warn("%s: malloc", __func__); return; } + len = hdr.sadb_msg_len * PFKEYV2_CHUNK; + if (read(sd, data, len) != len) { log_warn("%s: short read", __func__); free(data); return; } - pm.pm_data = data; - pm.pm_lenght = len; - pfkey_process(env, &pm); + /* Try postponed requests first, so we do in-order processing */ + if (!SIMPLEQ_EMPTY(&pfkey_postponed)) + pfkey_timer_cb(0, 0, env); - free(data); + pm.pm_data = data; + pm.pm_length = len; + + if (pfkey_process(env, &pm) == -1 && + (pmp = calloc(1, sizeof(*pmp))) != NULL) { + pmp->pm_data = data; + pmp->pm_length = len; + log_debug("%s: pfkey_process is busy, retry later", __func__); + SIMPLEQ_INSERT_TAIL(&pfkey_postponed, pmp, pm_entry); + evtimer_add(&pfkey_timer_ev, &pfkey_timer_tv); + } else { + free(data); + } } void -pfkey_timer_cb(struct iked *env, void *arg) +pfkey_timer_cb(int unused, short event, void *arg) { + struct iked *env = arg; struct pfkey_message *pm; + SIMPLEQ_INIT(&pfkey_retry); while (!SIMPLEQ_EMPTY(&pfkey_postponed)) { pm = SIMPLEQ_FIRST(&pfkey_postponed); SIMPLEQ_REMOVE_HEAD(&pfkey_postponed, pm_entry); - pfkey_process(env, pm); - free(pm->pm_data); - free(pm); + if (pfkey_process(env, pm) == -1) { + log_debug("%s: pfkey_process is busy, retry later", + __func__); + SIMPLEQ_INSERT_TAIL(&pfkey_retry, pm, pm_entry); + } else { + free(pm->pm_data); + free(pm); + } } + /* move from retry to postponed */ + while ((pm = SIMPLEQ_FIRST(&pfkey_retry)) != NULL) { + SIMPLEQ_REMOVE_HEAD(&pfkey_retry, pm_entry); + SIMPLEQ_INSERT_TAIL(&pfkey_postponed, pm, pm_entry); + } + if (!SIMPLEQ_EMPTY(&pfkey_postponed)) + evtimer_add(&pfkey_timer_ev, &pfkey_timer_tv); } -void +int pfkey_process(struct iked *env, struct pfkey_message *pm) { struct iked_spi spi; @@ -1748,54 +1886,55 @@ pfkey_process(struct iked *env, struct pfkey_message *pm) struct sadb_address *sa_addr; struct sadb_protocol *sa_proto; struct sadb_x_policy sa_pol; - struct sadb_x_sa2 sa_2; - struct sockaddr_storage *ssrc, *sdst, *smask, *dmask, *speer; + struct sockaddr *ssrc, *sdst, *smask, *dmask, *speer; struct iovec iov[IOV_CNT]; - int iov_cnt, sd = env->sc_pfkey; - u_int8_t *reply; + int ret = 0, iov_cnt, sd; + uint8_t *reply; ssize_t rlen; const char *errmsg = NULL; + size_t slen; #else struct iked_addr peer; - struct sadb_address *sa_addr; - struct sockaddr_storage *speer; + struct sadb_address *sa_addr; + struct sockaddr *speer; struct sadb_x_policy *sa_pol; - struct iked_flow *flow; + struct iked_flow flow; + struct sockaddr *ssrc; #endif u_int8_t *data = pm->pm_data; - ssize_t len = pm->pm_lenght; + ssize_t len = pm->pm_length; if (!env || !data || !len) - return; + return 0; hdr = (struct sadb_msg *)data; switch (hdr->sadb_msg_type) { case SADB_ACQUIRE: - /* Get peer from the acquire message */ - if ((sa_addr = pfkey_find_ext(data, len, - SADB_EXT_ADDRESS_DST)) == NULL) { - log_debug("%s: no peer address", __func__); - return; - } - speer = (struct sockaddr_storage *)(sa_addr + 1); - bzero(&peer, sizeof(peer)); - peer.addr_af = speer->ss_family; - peer.addr_port = htons(socket_getport(speer)); - memcpy(&peer.addr, speer, sizeof(*speer)); - if (socket_af((struct sockaddr *)&peer.addr, - peer.addr_port) == -1) { - log_debug("%s: invalid address", __func__); - return; - } - log_debug("%s: acquire request (peer %s)", __func__, - print_host(speer, NULL, 0)); + /* Get peer from the acquire message */ + if ((sa_addr = pfkey_find_ext(data, len, + SADB_EXT_ADDRESS_DST)) == NULL) { + log_debug("%s: no peer address", __func__); + return 0; + } + speer = (struct sockaddr *)(sa_addr + 1); + //speer = (struct sockaddr_storage *)(sa_addr + 1); + bzero(&peer, sizeof(peer)); + peer.addr_af = speer->sa_family; + peer.addr_port = htons(socket_getport(speer)); + memcpy(&peer.addr, speer, sizeof(*speer)); + if (socket_af((struct sockaddr *)&peer.addr, + peer.addr_port) == -1) { + log_debug("%s: invalid address", __func__); + return 0; + } + log_debug("%s: acquire request (peer %s)", __func__, + print_host(speer, NULL, 0)); #if defined(_OPENBSD_IPSEC_API_VERSION) - /* Get the matching flow */ - bzero(&flow, sizeof(flow)); - flow.flow_peer = &peer; + sd = env->sc_pfkey; + /* get the matching flow */ bzero(&smsg, sizeof(smsg)); smsg.sadb_msg_version = PF_KEY_V2; smsg.sadb_msg_seq = ++sadb_msg_seq; @@ -1821,7 +1960,7 @@ pfkey_process(struct iked *env, struct pfkey_message *pm) if (pfkey_write(sd, &smsg, iov, iov_cnt, &reply, &rlen)) { log_warnx("%s: failed to get a policy", __func__); - return; + return (0); } if ((sa_addr = pfkey_find_ext(reply, rlen, @@ -1829,14 +1968,18 @@ pfkey_process(struct iked *env, struct pfkey_message *pm) errmsg = "flow source address"; goto out; } - ssrc = (struct sockaddr_storage *)(sa_addr + 1); - flow.flow_src.addr_af = ssrc->ss_family; + ssrc = (struct sockaddr *)(sa_addr + 1); + flow.flow_src.addr_af = ssrc->sa_family; flow.flow_src.addr_port = htons(socket_getport(ssrc)); - memcpy(&flow.flow_src.addr, ssrc, sizeof(*ssrc)); + if ((slen = ssrc->sa_len) > sizeof(flow.flow_src.addr)) { + log_debug("%s: invalid src address len", __func__); + return (0); + } + memcpy(&flow.flow_src.addr, ssrc, slen); if (socket_af((struct sockaddr *)&flow.flow_src.addr, flow.flow_src.addr_port) == -1) { log_debug("%s: invalid address", __func__); - return; + return (0); } if ((sa_addr = pfkey_find_ext(reply, rlen, @@ -1844,14 +1987,18 @@ pfkey_process(struct iked *env, struct pfkey_message *pm) errmsg = "flow destination address"; goto out; } - sdst = (struct sockaddr_storage *)(sa_addr + 1); - flow.flow_dst.addr_af = sdst->ss_family; + sdst = (struct sockaddr *)(sa_addr + 1); + flow.flow_dst.addr_af = sdst->sa_family; flow.flow_dst.addr_port = htons(socket_getport(sdst)); - memcpy(&flow.flow_dst.addr, sdst, sizeof(*sdst)); + if ((slen = sdst->sa_len) > sizeof(flow.flow_dst.addr)) { + log_debug("%s: invalid dst address len", __func__); + return (0); + } + memcpy(&flow.flow_dst.addr, sdst, slen); if (socket_af((struct sockaddr *)&flow.flow_dst.addr, flow.flow_dst.addr_port) == -1) { log_debug("%s: invalid address", __func__); - return; + return (0); } if ((sa_addr = pfkey_find_ext(reply, rlen, @@ -1859,8 +2006,8 @@ pfkey_process(struct iked *env, struct pfkey_message *pm) errmsg = "flow source mask"; goto out; } - smask = (struct sockaddr_storage *)(sa_addr + 1); - switch (smask->ss_family) { + smask = (struct sockaddr *)(sa_addr + 1); + switch (smask->sa_family) { case AF_INET: flow.flow_src.addr_mask = mask2prefixlen((struct sockaddr *)smask); @@ -1876,7 +2023,7 @@ pfkey_process(struct iked *env, struct pfkey_message *pm) default: log_debug("%s: bad address family", __func__); free(reply); - return; + return (0); } if ((sa_addr = pfkey_find_ext(reply, rlen, @@ -1884,8 +2031,8 @@ pfkey_process(struct iked *env, struct pfkey_message *pm) errmsg = "flow destination mask"; goto out; } - dmask = (struct sockaddr_storage *)(sa_addr + 1); - switch (dmask->ss_family) { + dmask = (struct sockaddr *)(sa_addr + 1); + switch (dmask->sa_family) { case AF_INET: flow.flow_dst.addr_mask = mask2prefixlen((struct sockaddr *)dmask); @@ -1901,7 +2048,7 @@ pfkey_process(struct iked *env, struct pfkey_message *pm) default: log_debug("%s: bad address family", __func__); free(reply); - return; + return (0); } if ((sa_proto = pfkey_find_ext(reply, rlen, @@ -1917,74 +2064,91 @@ pfkey_process(struct iked *env, struct pfkey_message *pm) print_host(sdst, NULL, 0), print_host(dmask, NULL, 0), print_host(speer, NULL, 0)); - ikev2_acquire_sa(env, &flow); + ret = ikev2_acquire_sa(env, &flow); out: if (errmsg) log_warnx("%s: %s wasn't found", __func__, errmsg); free(reply); #elif defined(SADB_X_EXT_POLICY) - if ((sa_pol = pfkey_find_ext(data, len, - SADB_X_EXT_POLICY)) == NULL) { - log_debug("%s: no policy extension", __func__); - return; - } + /* + * On an upcall from the kernel, reconstruct iked_flow with src + * and dest in upcall message. Then proceed onto establish SA + */ + bzero(&flow, sizeof(flow)); + flow.flow_peer = &peer; + flow.flow_dst = peer; + if (flow.flow_dst.addr_af == AF_INET) { + flow.flow_dst.addr_mask = 32; + } + if ((sa_pol = pfkey_find_ext(data, len, + SADB_X_EXT_POLICY)) == NULL) { + log_debug("%s: no policy extension", __func__); + return 0; + } - RB_FOREACH(flow, iked_flows, &env->sc_activeflows) { - /* - * Find existing active policy by policy Id. - * XXX This should use another tree that uses - * XXX the x_policy_id as lookup key. - */ - if (flow->flow_id == sa_pol->sadb_x_policy_id) { - log_debug("%s: flow %s policy id 0x%x ", - __func__, - flow->flow_dir == IPSP_DIRECTION_IN ? - "in" : "out", flow->flow_id); - ikev2_acquire_sa(env, flow); - break; - } - } + if ((sa_addr = pfkey_find_ext(data, len, + SADB_EXT_ADDRESS_SRC)) == NULL) { + log_debug("%s: no src in ext_policy upcall", __func__); + return 0; + } + ssrc = (struct sockaddr*)(sa_addr + 1); + flow.flow_src.addr_af = ssrc->sa_family; + flow.flow_src.addr_port = htons(socket_getport(ssrc)); + if (flow.flow_src.addr_af == AF_INET) { + flow.flow_src.addr_mask = 32; + } + memcpy(&flow.flow_src.addr, ssrc, sizeof(*ssrc)); + if (socket_af((struct sockaddr *)&flow.flow_src.addr, + flow.flow_src.addr_port) == -1) { + log_debug("%s: invalid address", __func__); + return 0; + } + + log_debug("%s: flow %s from to %s ", __func__, + print_host((struct sockaddr*)&(flow.flow_src.addr), NULL, 0), + print_host((struct sockaddr*)&(flow.flow_dst.addr), NULL, 0)); + ikev2_acquire_sa(env, &flow); #else #warning PFKEYv2 SADB_ACQUIRE not implemented - log_debug("%s: SADB_ACQUIRE not yet supported here, ignored", - __func__); + log_debug("%s: SADB_ACQUIRE not yet supported here, ignored", + __func__); #endif - break; + break; case SADB_EXPIRE: - if ((sa = pfkey_find_ext(data, len, SADB_EXT_SA)) == NULL) { - log_warnx("%s: SA extension wasn't found", __func__); - return; - } - if ((sa_ltime = pfkey_find_ext(data, len, - SADB_EXT_LIFETIME_SOFT)) == NULL && - (sa_ltime = pfkey_find_ext(data, len, - SADB_EXT_LIFETIME_HARD)) == NULL) { - log_warnx("%s: lifetime extension wasn't found", - __func__); - return; - } - spi.spi = ntohl(sa->sadb_sa_spi); - spi.spi_size = 4; - switch (hdr->sadb_msg_satype) { - case SADB_SATYPE_AH: - spi.spi_protoid = IKEV2_SAPROTO_AH; - break; - case SADB_SATYPE_ESP: - spi.spi_protoid = IKEV2_SAPROTO_ESP; - break; - default: - log_warnx("%s: usupported SA type %d spi %s", - __func__, hdr->sadb_msg_satype, - print_spi(spi.spi, spi.spi_size)); - return; - } + if ((sa = pfkey_find_ext(data, len, SADB_EXT_SA)) == NULL) { + log_warnx("%s: SA extension wasn't found", __func__); + return 0; + } + if ((sa_ltime = pfkey_find_ext(data, len, + SADB_EXT_LIFETIME_SOFT)) == NULL && + (sa_ltime = pfkey_find_ext(data, len, + SADB_EXT_LIFETIME_HARD)) == NULL) { + log_warnx("%s: lifetime extension wasn't found", + __func__); + return 0; + } + spi.spi = ntohl(sa->sadb_sa_spi); + spi.spi_size = 4; + switch (hdr->sadb_msg_satype) { + case SADB_SATYPE_AH: + spi.spi_protoid = IKEV2_SAPROTO_AH; + break; + case SADB_SATYPE_ESP: + spi.spi_protoid = IKEV2_SAPROTO_ESP; + break; + default: + log_warnx("%s: usupported SA type %d spi %s", + __func__, hdr->sadb_msg_satype, + print_spi(spi.spi, spi.spi_size)); + return 0; + } - log_debug("%s: SA %s is expired, pending %s", __func__, - print_spi(spi.spi, spi.spi_size), - sa_ltime->sadb_lifetime_exttype == SADB_EXT_LIFETIME_SOFT ? - "rekeying" : "deletion"); + log_debug("%s: SA %s is expired, pending %s", __func__, + print_spi(spi.spi, spi.spi_size), + sa_ltime->sadb_lifetime_exttype == SADB_EXT_LIFETIME_SOFT ? + "rekeying" : "deletion"); if (sa_ltime->sadb_lifetime_exttype == SADB_EXT_LIFETIME_SOFT) ikev2_rekey_sa(env, &spi); @@ -1992,4 +2156,5 @@ pfkey_process(struct iked *env, struct pfkey_message *pm) ikev2_drop_sa(env, &spi); break; } + return 0; } diff --git a/iked/policy.c b/iked/policy.c index 7cff708..36d0bbd 100644 --- a/iked/policy.c +++ b/iked/policy.c @@ -1,4 +1,4 @@ -/* $OpenBSD: policy.c,v 1.23 2013/01/08 10:38:19 reyk Exp $ */ +/* $OpenBSD: policy.c,v 1.42 2016/06/01 11:16:41 patrick Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter @@ -17,21 +17,14 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include "openbsd-compat/sys-queue.h" + #include #include -#include "openbsd-compat/sys-tree.h" -#include -#include -#include -#include -#include -#include #include #include +#include #include #include #include @@ -55,6 +48,7 @@ void policy_init(struct iked *env) { TAILQ_INIT(&env->sc_policies); + TAILQ_INIT(&env->sc_ocsp); RB_INIT(&env->sc_users); RB_INIT(&env->sc_sas); RB_INIT(&env->sc_activesas); @@ -65,6 +59,8 @@ int policy_lookup(struct iked *env, struct iked_message *msg) { struct iked_policy pol; + char *s, idstr[IKED_ID_SIZE]; + if (msg->msg_sa != NULL && msg->msg_sa->sa_policy != NULL) { /* Existing SA with policy */ @@ -76,6 +72,15 @@ policy_lookup(struct iked *env, struct iked_message *msg) pol.pol_af = msg->msg_peer.ss_family; memcpy(&pol.pol_peer.addr, &msg->msg_peer, sizeof(msg->msg_peer)); memcpy(&pol.pol_local.addr, &msg->msg_local, sizeof(msg->msg_local)); + if (msg->msg_id.id_type && + ikev2_print_id(&msg->msg_id, idstr, IKED_ID_SIZE) == 0 && + (s = strchr(idstr, '/')) != NULL) { + pol.pol_peerid.id_type = msg->msg_id.id_type; + pol.pol_peerid.id_length = strlen(s+1); + strlcpy(pol.pol_peerid.id_data, s+1, + sizeof(pol.pol_peerid.id_data)); + log_debug("%s: peerid '%s'", __func__, s+1); + } /* Try to find a matching policy for this message */ if ((msg->msg_policy = policy_test(env, &pol)) != NULL) @@ -92,6 +97,47 @@ policy_lookup(struct iked *env, struct iked_message *msg) return (0); } +/* + * Check if host is in a subnet + */ +bool +is_in_subnet(struct sockaddr_in* host, struct sockaddr_in* subnet, + uint8_t subnet_mask) +{ + uint32_t host_addr = ntohl(host->sin_addr.s_addr); + uint32_t subnet_addr = ntohl(subnet->sin_addr.s_addr); + unsigned int mask = 0xFFFFFFFF << (32 - subnet_mask); + + return ((host_addr & mask) == (subnet_addr & mask)); +} + +bool +isipv4_flow_and_policy(struct iked_addr fsrc, struct iked_addr fdst, + struct iked_addr psrc, struct iked_addr pdst) +{ + return (fsrc.addr_af == AF_INET) && (fdst.addr_af == AF_INET) && + (psrc.addr_af == AF_INET) && (pdst.addr_af == AF_INET); +} + +/* + * Rules/Configuration Policies in iked.conf are subnet based. + * This routinue checks if an incoming flow matches a preconfigured + * policy in the config + */ +bool +flow_matches_policy(struct iked_addr fsrc, struct iked_addr fdst, + struct iked_addr psrc, struct iked_addr pdst) +{ + struct sockaddr_in *fsrc_addr, *psrc_addr, *fdst_addr, *pdst_addr; + fsrc_addr = (struct sockaddr_in*)&(fsrc.addr); + psrc_addr = (struct sockaddr_in*)&(psrc.addr); + fdst_addr = (struct sockaddr_in*)&(fdst.addr); + pdst_addr = (struct sockaddr_in*)&(pdst.addr); + return (isipv4_flow_and_policy(fsrc, fdst, psrc, pdst) && + is_in_subnet(fsrc_addr, psrc_addr, psrc.addr_mask) && + is_in_subnet(fdst_addr, pdst_addr, pdst.addr_mask)); +} + struct iked_policy * policy_test(struct iked *env, struct iked_policy *key) { @@ -126,9 +172,36 @@ policy_test(struct iked *env, struct iked_policy *key) */ if (key->pol_nflows && (flowkey = RB_MIN(iked_flows, - &key->pol_flows)) != NULL && - (flow = RB_FIND(iked_flows, &p->pol_flows, - flowkey)) == NULL) { + &key->pol_flows)) != NULL) { + bool match = false; + struct iked_addr psrc, pdst, fsrc, fdst; + /* + * Check if incoming flow from kernel upcall + * matches any of the configuration policies + * or rules specified in iked.conf + */ + RB_FOREACH(flow, iked_flows, &p->pol_flows) { + psrc = flow->flow_src; + pdst = flow->flow_dst; + fsrc = flowkey->flow_src; + fdst = flowkey->flow_dst; + if (flow_matches_policy(fsrc, fdst, + psrc, pdst)) { + match = true; + break; + } + } + if (!match) { + p = TAILQ_NEXT(p, pol_entry); + continue; + } + } + /* make sure the peer ID matches */ + if (key->pol_peerid.id_type && + (key->pol_peerid.id_type != p->pol_peerid.id_type || + memcmp(key->pol_peerid.id_data, + p->pol_peerid.id_data, + sizeof(key->pol_peerid.id_data)) != 0)) { p = TAILQ_NEXT(p, pol_entry); continue; } @@ -202,6 +275,16 @@ policy_unref(struct iked *env, struct iked_policy *pol) return; if (--(pol->pol_refcnt) <= 0) config_free_policy(env, pol); + else { + struct iked_sa *tmp; + int count = 0; + + TAILQ_FOREACH(tmp, &pol->pol_sapeers, sa_peer_entry) + count++; + if (count != pol->pol_refcnt) + log_warnx("%s: ERROR pol %p pol_refcnt %d != count %d", + __func__, pol, pol->pol_refcnt, count); + } } void @@ -209,19 +292,28 @@ sa_state(struct iked *env, struct iked_sa *sa, int state) { const char *a; const char *b; + int ostate = sa->sa_state; - a = print_map(sa->sa_state, ikev2_state_map); + a = print_map(ostate, ikev2_state_map); b = print_map(state, ikev2_state_map); - if (state > sa->sa_state) { + sa->sa_state = state; + if (ostate != IKEV2_STATE_INIT && + !sa_stateok(sa, state)) { + log_debug("%s: cannot switch: %s -> %s", __func__, a, b); + sa->sa_state = ostate; + } else if (ostate != sa->sa_state) { switch (state) { case IKEV2_STATE_ESTABLISHED: case IKEV2_STATE_CLOSED: log_info("%s: %s -> %s from %s to %s policy '%s'", __func__, a, b, - print_host(&sa->sa_peer.addr, NULL, 0), - print_host(&sa->sa_local.addr, NULL, 0), - sa->sa_policy->pol_name); + print_host((struct sockaddr *)&sa->sa_peer.addr, + NULL, 0), + print_host((struct sockaddr *)&sa->sa_local.addr, + NULL, 0), + sa->sa_policy ? sa->sa_policy->pol_name : + ""); break; default: log_debug("%s: %s -> %s", __func__, a, b); @@ -229,20 +321,19 @@ sa_state(struct iked *env, struct iked_sa *sa, int state) } } - sa->sa_state = state; } void -sa_stateflags(struct iked_sa *sa, u_int flags) +sa_stateflags(struct iked_sa *sa, unsigned int flags) { - u_int require; + unsigned int require; if (sa->sa_state > IKEV2_STATE_SA_INIT) require = sa->sa_statevalid; else require = sa->sa_stateinit; - log_debug("%s: 0x%02x -> 0x%02x %s (required 0x%02x %s)", __func__, + log_debug("%s: 0x%04x -> 0x%04x %s (required 0x%04x %s)", __func__, sa->sa_stateflags, sa->sa_stateflags | flags, print_bits(sa->sa_stateflags | flags, IKED_REQ_BITS), require, print_bits(require, IKED_REQ_BITS)); @@ -253,7 +344,7 @@ sa_stateflags(struct iked_sa *sa, u_int flags) int sa_stateok(struct iked_sa *sa, int state) { - u_int require; + unsigned int require; if (sa->sa_state < state) return (0); @@ -265,8 +356,8 @@ sa_stateok(struct iked_sa *sa, int state) if (state == IKEV2_STATE_SA_INIT || state == IKEV2_STATE_VALID || - state == IKEV2_STATE_EAP) { - log_debug("%s: %s flags 0x%02x, require 0x%02x %s", __func__, + state == IKEV2_STATE_EAP_VALID) { + log_debug("%s: %s flags 0x%04x, require 0x%04x %s", __func__, print_map(state, ikev2_state_map), (sa->sa_stateflags & require), require, print_bits(require, IKED_REQ_BITS)); @@ -278,38 +369,58 @@ sa_stateok(struct iked_sa *sa, int state) } struct iked_sa * -sa_new(struct iked *env, u_int64_t ispi, u_int64_t rspi, - u_int initiator, struct iked_policy *pol) +sa_new(struct iked *env, uint64_t ispi, uint64_t rspi, + unsigned int initiator, struct iked_policy *pol) { struct iked_sa *sa; + struct iked_sa *old; struct iked_id *localid; - u_int diff; + unsigned int diff; if ((ispi == 0 && rspi == 0) || (sa = sa_lookup(env, ispi, rspi, initiator)) == NULL) { /* Create new SA */ + if (!initiator && ispi == 0) { + log_debug("%s: cannot create responder IKE SA w/o ispi", + __func__); + return (NULL); + } sa = config_new_sa(env, initiator); + if (sa == NULL) { + log_debug("%s: failed to allocate IKE SA", __func__); + return (NULL); + } + if (!initiator) + sa->sa_hdr.sh_ispi = ispi; + old = RB_INSERT(iked_sas, &env->sc_sas, sa); + if (old && old != sa) { + log_warnx("%s: duplicate IKE SA", __func__); + config_free_sa(env, sa); + return (NULL); + } } - if (sa == NULL) { - log_debug("%s: failed to get sa", __func__); - return (NULL); - } - if (sa->sa_policy == NULL) + /* Update rspi in the initator case */ + if (initiator && sa->sa_hdr.sh_rspi == 0 && rspi) + sa->sa_hdr.sh_rspi = rspi; + + if (sa->sa_policy == NULL) { sa->sa_policy = pol; - else + TAILQ_INSERT_TAIL(&pol->pol_sapeers, sa, sa_peer_entry); + } else pol = sa->sa_policy; - sa->sa_statevalid = IKED_REQ_AUTH|IKED_REQ_SA; + sa->sa_statevalid = IKED_REQ_AUTH|IKED_REQ_AUTHVALID|IKED_REQ_SA; if (pol != NULL && pol->pol_auth.auth_eap) { - sa->sa_statevalid |= IKED_REQ_CERT; + sa->sa_statevalid |= IKED_REQ_CERT|IKED_REQ_EAPVALID; } else if (pol != NULL && pol->pol_auth.auth_method != IKEV2_AUTH_SHARED_KEY_MIC) { - sa->sa_statevalid |= IKED_REQ_VALID|IKED_REQ_CERT; + sa->sa_statevalid |= IKED_REQ_CERTVALID|IKED_REQ_CERT; } if (initiator) { localid = &sa->sa_iid; - diff = IKED_REQ_VALID|IKED_REQ_SA; + diff = IKED_REQ_CERTVALID|IKED_REQ_AUTHVALID|IKED_REQ_SA| + IKED_REQ_EAPVALID; sa->sa_stateinit = sa->sa_statevalid & ~diff; sa->sa_statevalid = sa->sa_statevalid & diff; } else @@ -322,14 +433,6 @@ sa_new(struct iked *env, u_int64_t ispi, u_int64_t rspi, return (NULL); } - 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 */ - RB_INSERT(iked_sas, &env->sc_sas, sa); - return (sa); } @@ -340,6 +443,12 @@ sa_free(struct iked *env, struct iked_sa *sa) print_spi(sa->sa_hdr.sh_ispi, 8), print_spi(sa->sa_hdr.sh_rspi, 8)); + /* IKE rekeying running? */ + if (sa->sa_next) { + RB_REMOVE(iked_sas, &env->sc_sas, sa->sa_next); + config_free_sa(env, sa->sa_next); + } + RB_REMOVE(iked_sas, &env->sc_sas, sa); config_free_sa(env, sa); } @@ -364,45 +473,37 @@ sa_free_flows(struct iked *env, struct iked_saflows *head) int sa_address(struct iked_sa *sa, struct iked_addr *addr, - struct sockaddr_storage *peer, int initiator) + struct sockaddr_storage *peer) { - struct iked_policy *pol = sa->sa_policy; - - if (pol == NULL) { - log_debug("%s: invalid policy", __func__); - return (-1); - } - bzero(addr, sizeof(*addr)); addr->addr_af = peer->ss_family; - addr->addr_port = htons(socket_getport(peer)); + addr->addr_port = htons(socket_getport((struct sockaddr *)peer)); memcpy(&addr->addr, peer, sizeof(*peer)); if (socket_af((struct sockaddr *)&addr->addr, addr->addr_port) == -1) { log_debug("%s: invalid address", __func__); return (-1); } - - if (addr == &sa->sa_peer) { - /* XXX Re-insert node into the tree */ - RB_REMOVE(iked_sapeers, &pol->pol_sapeers, sa); - memcpy(&sa->sa_polpeer, initiator ? &pol->pol_peer : - &sa->sa_peer, sizeof(sa->sa_polpeer)); - RB_INSERT(iked_sapeers, &pol->pol_sapeers, sa); - } - return (0); } void -childsa_free(struct iked_childsa *sa) +childsa_free(struct iked_childsa *csa) { - ibuf_release(sa->csa_encrkey); - ibuf_release(sa->csa_integrkey); - free(sa); + if (csa->csa_children) { + /* XXX should not happen */ + log_warnx("%s: trying to remove CSA %p children %u", + __func__, csa, csa->csa_children); + return; + } + if (csa->csa_parent) + csa->csa_parent->csa_children--; + ibuf_release(csa->csa_encrkey); + ibuf_release(csa->csa_integrkey); + free(csa); } struct iked_childsa * -childsa_lookup(struct iked_sa *sa, u_int64_t spi, u_int8_t protoid) +childsa_lookup(struct iked_sa *sa, uint64_t spi, uint8_t protoid) { struct iked_childsa *csa; @@ -424,13 +525,13 @@ flow_free(struct iked_flow *flow) } struct iked_sa * -sa_lookup(struct iked *env, u_int64_t ispi, u_int64_t rspi, - u_int initiator) +sa_lookup(struct iked *env, uint64_t ispi, uint64_t rspi, + unsigned int initiator) { struct iked_sa *sa, key; key.sa_hdr.sh_ispi = ispi; - key.sa_hdr.sh_rspi = rspi; + /* key.sa_hdr.sh_rspi = rspi; */ key.sa_hdr.sh_initiator = initiator; if ((sa = RB_FIND(iked_sas, &env->sc_sas, &key)) != NULL) { @@ -438,6 +539,7 @@ sa_lookup(struct iked *env, u_int64_t ispi, u_int64_t rspi, /* Validate if SPIr matches */ if ((sa->sa_hdr.sh_rspi != 0) && + (rspi != 0) && (sa->sa_hdr.sh_rspi != rspi)) return (NULL); } @@ -448,8 +550,10 @@ sa_lookup(struct iked *env, u_int64_t ispi, u_int64_t rspi, 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_initiator > b->sa_hdr.sh_initiator) + return (-1); + if (a->sa_hdr.sh_initiator < b->sa_hdr.sh_initiator) + return (1); if (a->sa_hdr.sh_ispi > b->sa_hdr.sh_ispi) return (-1); @@ -471,20 +575,18 @@ sa_cmp(struct iked_sa *a, struct iked_sa *b) return (0); } -struct iked_sa * -sa_peer_lookup(struct iked_policy *pol, struct sockaddr_storage *peer) +static __inline int +sa_addrpool_cmp(struct iked_sa *a, struct iked_sa *b) { - struct iked_sa key; - - memcpy(&key.sa_polpeer.addr, peer, sizeof(*peer)); - return (RB_FIND(iked_sapeers, &pol->pol_sapeers, &key)); + return (sockaddr_cmp((struct sockaddr *)&a->sa_addrpool->addr, + (struct sockaddr *)&b->sa_addrpool->addr, -1)); } static __inline int -sa_peer_cmp(struct iked_sa *a, struct iked_sa *b) +sa_addrpool6_cmp(struct iked_sa *a, struct iked_sa *b) { - return (sockaddr_cmp((struct sockaddr *)&a->sa_polpeer.addr, - (struct sockaddr *)&b->sa_polpeer.addr, -1)); + return (sockaddr_cmp((struct sockaddr *)&a->sa_addrpool6->addr, + (struct sockaddr *)&b->sa_addrpool6->addr, -1)); } struct iked_user * @@ -547,10 +649,9 @@ flow_cmp(struct iked_flow *a, struct iked_flow *b) return (diff); } -#ifndef __clang_analyzer__ RB_GENERATE(iked_sas, iked_sa, sa_entry, sa_cmp); -RB_GENERATE(iked_sapeers, iked_sa, sa_peer_entry, sa_peer_cmp); +RB_GENERATE(iked_addrpool, iked_sa, sa_addrpool_entry, sa_addrpool_cmp); +RB_GENERATE(iked_addrpool6, iked_sa, sa_addrpool6_entry, sa_addrpool6_cmp); RB_GENERATE(iked_users, iked_user, usr_entry, user_cmp); RB_GENERATE(iked_activesas, iked_childsa, csa_node, childsa_cmp); RB_GENERATE(iked_flows, iked_flow, flow_node, flow_cmp); -#endif diff --git a/iked/proc.c b/iked/proc.c index 331d84b..67aa623 100644 --- a/iked/proc.c +++ b/iked/proc.c @@ -1,7 +1,7 @@ -/* $OpenBSD: proc.c,v 1.9 2013/03/21 04:30:14 deraadt Exp $ */ +/* $OpenBSD: proc.c,v 1.29 2015/12/07 16:05:56 reyk Exp $ */ /* - * Copyright (c) 2010-2013 Reyk Floeter + * Copyright (c) 2010 - 2014 Reyk Floeter * Copyright (c) 2008 Pierre-Yves Ritschard * * Permission to use, copy, modify, and distribute this software for any @@ -17,11 +17,10 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include "openbsd-compat/sys-queue.h" -#include "openbsd-compat/sys-tree.h" -#include +#include + #include +#include #include #include @@ -31,41 +30,101 @@ #include #include #include - +#include #include - #include "iked.h" -void proc_setup(struct privsep *); +void proc_open(struct privsep *, struct privsep_proc *, + struct privsep_proc *, size_t); +void proc_close(struct privsep *); +int proc_ispeer(struct privsep_proc *, unsigned int, enum privsep_procid); void proc_shutdown(struct privsep_proc *); void proc_sig_handler(int, short, void *); +void proc_range(struct privsep *, enum privsep_procid, int *, int *); +int proc_dispatch_null(int, struct privsep_proc *, struct imsg *); + +int +proc_ispeer(struct privsep_proc *procs, unsigned int nproc, + enum privsep_procid type) +{ + unsigned int i; + + for (i = 0; i < nproc; i++) + if (procs[i].p_id == type) + return (1); + return (0); +} void -proc_init(struct privsep *ps, struct privsep_proc *p, u_int nproc) +proc_init(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc) { - u_int i; + unsigned int i, j, src, dst; + struct privsep_pipes *pp; + + /* + * Allocate pipes for all process instances (incl. parent) + * + * - ps->ps_pipes: N:M mapping + * N source processes connected to M destination processes: + * [src][instances][dst][instances], for example + * [PROC_RELAY][3][PROC_CA][3] + * + * - ps->ps_pp: per-process 1:M part of ps->ps_pipes + * Each process instance has a destination array of socketpair fds: + * [dst][instances], for example + * [PROC_PARENT][0] + */ + for (src = 0; src < PROC_MAX; src++) { + /* Allocate destination array for each process */ + if ((ps->ps_pipes[src] = calloc(ps->ps_ninstances, + sizeof(struct privsep_pipes))) == NULL) + fatal("proc_init: calloc"); + + for (i = 0; i < ps->ps_ninstances; i++) { + pp = &ps->ps_pipes[src][i]; + + for (dst = 0; dst < PROC_MAX; dst++) { + /* Allocate maximum fd integers */ + if ((pp->pp_pipes[dst] = + calloc(ps->ps_ninstances, + sizeof(int))) == NULL) + fatal("proc_init: calloc"); + + /* Mark fd as unused */ + for (j = 0; j < ps->ps_ninstances; j++) + pp->pp_pipes[dst][j] = -1; + } + } + } /* - * Called from parent + * Setup and run the parent and its children */ privsep_process = PROC_PARENT; + ps->ps_instances[PROC_PARENT] = 1; ps->ps_title[PROC_PARENT] = "parent"; ps->ps_pid[PROC_PARENT] = getpid(); + ps->ps_pp = &ps->ps_pipes[privsep_process][0]; - proc_setup(ps); + for (i = 0; i < nproc; i++) { + /* Default to 1 process instance */ + if (ps->ps_instances[procs[i].p_id] < 1) + ps->ps_instances[procs[i].p_id] = 1; + ps->ps_title[procs[i].p_id] = procs[i].p_title; + } + + proc_open(ps, NULL, procs, nproc); /* Engage! */ - for (i = 0; i < nproc; i++, p++) { - ps->ps_title[p->p_id] = p->p_title; - ps->ps_pid[p->p_id] = (*p->p_init)(ps, p); - } + for (i = 0; i < nproc; i++) + ps->ps_pid[procs[i].p_id] = (*procs[i].p_init)(ps, &procs[i]); } void proc_kill(struct privsep *ps) { pid_t pid; - u_int i; + unsigned int i; if (privsep_process != PROC_PARENT) return; @@ -73,83 +132,157 @@ proc_kill(struct privsep *ps) for (i = 0; i < PROC_MAX; i++) { if (ps->ps_pid[i] == 0) continue; - kill(ps->ps_pid[i], SIGTERM); + killpg(ps->ps_pid[i], SIGTERM); } do { pid = waitpid(WAIT_ANY, NULL, 0); } while (pid != -1 || (pid == -1 && errno == EINTR)); + + proc_close(ps); } void -proc_setup(struct privsep *ps) +proc_open(struct privsep *ps, struct privsep_proc *p, + struct privsep_proc *procs, size_t nproc) { - int i, j, sockpair[2]; - - for (i = 0; i < PROC_MAX; i++) - for (j = 0; j < PROC_MAX; j++) { - if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, - sockpair) == -1) - fatal("sockpair"); - ps->ps_pipes[i][j] = sockpair[0]; - ps->ps_pipes[j][i] = sockpair[1]; - socket_set_blockmode(ps->ps_pipes[i][j], - BM_NONBLOCK); - socket_set_blockmode(ps->ps_pipes[j][i], - BM_NONBLOCK); + struct privsep_pipes *pa, *pb; + int fds[2]; + unsigned int i, j, src, proc; + + if (p == NULL) + src = privsep_process; /* parent */ + else + src = p->p_id; + + /* + * Open socket pairs for our peers + */ + for (proc = 0; proc < nproc; proc++) { + procs[proc].p_ps = ps; + procs[proc].p_env = ps->ps_env; + if (procs[proc].p_cb == NULL) + procs[proc].p_cb = proc_dispatch_null; + + for (i = 0; i < ps->ps_instances[src]; i++) { + for (j = 0; j < ps->ps_instances[procs[proc].p_id]; + j++) { + pa = &ps->ps_pipes[src][i]; + pb = &ps->ps_pipes[procs[proc].p_id][j]; + + /* Check if fds are already set by peer */ + if (pa->pp_pipes[procs[proc].p_id][j] != -1) + continue; + + if (bsd_socketpair(AF_UNIX, + SOCK_STREAM | SOCK_NONBLOCK, + PF_UNSPEC, fds) == -1) + fatal("socketpair"); + + pa->pp_pipes[procs[proc].p_id][j] = fds[0]; + pb->pp_pipes[src][i] = fds[1]; + } } + } } void -proc_config(struct privsep *ps, struct privsep_proc *p, u_int nproc) +proc_listen(struct privsep *ps, struct privsep_proc *procs, size_t nproc) { - u_int src, dst, i, j, k, found; - - src = privsep_process; + unsigned int i, dst, src, n, m; + struct privsep_pipes *pp; /* - * close unused pipes + * Close unused pipes */ - for (i = 0; i < PROC_MAX; i++) { - if (i != privsep_process) { - for (j = 0; j < PROC_MAX; j++) { - close(ps->ps_pipes[i][j]); - ps->ps_pipes[i][j] = -1; - } - } else { - for (j = found = 0; j < PROC_MAX; j++, found = 0) { - for (k = 0; k < nproc; k++) { - if (p[k].p_id == j) - found++; - } - if (!found) { - close(ps->ps_pipes[i][j]); - ps->ps_pipes[i][j] = -1; + for (src = 0; src < PROC_MAX; src++) { + for (n = 0; n < ps->ps_instances[src]; n++) { + /* Ingore current process */ + if (src == (unsigned int)privsep_process && + n == ps->ps_instance) + continue; + + pp = &ps->ps_pipes[src][n]; + + for (dst = 0; dst < PROC_MAX; dst++) { + if (src == dst) + continue; + for (m = 0; m < ps->ps_instances[dst]; m++) { + if (pp->pp_pipes[dst][m] == -1) + continue; + + /* Close and invalidate fd */ + close(pp->pp_pipes[dst][m]); + pp->pp_pipes[dst][m] = -1; } } } } + src = privsep_process; + ps->ps_pp = pp = &ps->ps_pipes[src][ps->ps_instance]; + /* - * listen on appropriate pipes + * Listen on appropriate pipes */ - for (i = 0; i < nproc; i++, p++) { - dst = p->p_id; - p->p_ps = ps; - p->p_env = ps->ps_env; - - imsg_init(&ps->ps_ievs[dst].ibuf, - ps->ps_pipes[src][dst]); - ps->ps_ievs[dst].handler = proc_dispatch; - ps->ps_ievs[dst].events = EV_READ; - ps->ps_ievs[dst].data = p; - ps->ps_ievs[dst].name = p->p_title; - event_set(&ps->ps_ievs[dst].ev, - ps->ps_ievs[dst].ibuf.fd, - ps->ps_ievs[dst].events, - ps->ps_ievs[dst].handler, - ps->ps_ievs[dst].data); - event_add(&ps->ps_ievs[dst].ev, NULL); + for (i = 0; i < nproc; i++) { + dst = procs[i].p_id; + + if (src == dst) + fatal("proc_listen: cannot peer with oneself"); + + if ((ps->ps_ievs[dst] = calloc(ps->ps_instances[dst], + sizeof(struct imsgev))) == NULL) + fatal("proc_open"); + + for (n = 0; n < ps->ps_instances[dst]; n++) { + if (pp->pp_pipes[dst][n] == -1) + continue; + + imsg_init(&(ps->ps_ievs[dst][n].ibuf), + pp->pp_pipes[dst][n]); + ps->ps_ievs[dst][n].handler = proc_dispatch; + ps->ps_ievs[dst][n].events = EV_READ; + ps->ps_ievs[dst][n].proc = &procs[i]; + ps->ps_ievs[dst][n].data = &ps->ps_ievs[dst][n]; + procs[i].p_instance = n; + + event_set(&(ps->ps_ievs[dst][n].ev), + ps->ps_ievs[dst][n].ibuf.fd, + ps->ps_ievs[dst][n].events, + ps->ps_ievs[dst][n].handler, + ps->ps_ievs[dst][n].data); + event_add(&(ps->ps_ievs[dst][n].ev), NULL); + } + } +} + +void +proc_close(struct privsep *ps) +{ + unsigned int dst, n; + struct privsep_pipes *pp; + + if (ps == NULL) + return; + + pp = ps->ps_pp; + + for (dst = 0; dst < PROC_MAX; dst++) { + if (ps->ps_ievs[dst] == NULL) + continue; + + for (n = 0; n < ps->ps_instances[dst]; n++) { + if (pp->pp_pipes[dst][n] == -1) + continue; + + /* Cancel the fd, close and invalidate the fd */ + event_del(&(ps->ps_ievs[dst][n].ev)); + imsg_clear(&(ps->ps_ievs[dst][n].ibuf)); + close(pp->pp_pipes[dst][n]); + pp->pp_pipes[dst][n] = -1; + } + free(ps->ps_ievs[dst]); } } @@ -161,7 +294,13 @@ proc_shutdown(struct privsep_proc *p) if (p->p_id == PROC_CONTROL && ps) control_cleanup(&ps->ps_csock); - log_info("%s exiting", p->p_title); + if (p->p_shutdown != NULL) + (*p->p_shutdown)(); + + proc_close(ps); + + log_info("%s exiting, pid %d", p->p_title, getpid()); + _exit(0); } @@ -178,6 +317,7 @@ proc_sig_handler(int sig, short event, void *arg) case SIGCHLD: case SIGHUP: case SIGPIPE: + case SIGUSR1: /* ignore */ break; default: @@ -188,18 +328,32 @@ proc_sig_handler(int sig, short event, void *arg) pid_t proc_run(struct privsep *ps, struct privsep_proc *p, - struct privsep_proc *procs, u_int nproc, - void (*init)(struct privsep *, void *), void *arg) + struct privsep_proc *procs, unsigned int nproc, + void (*run)(struct privsep *, struct privsep_proc *, void *), void *arg) { - pid_t pid; - struct passwd *pw; - const char *root; - u_int32_t seed[256]; + pid_t pid; + struct passwd *pw; + const char *root; + struct control_sock *rcs; + unsigned int n; +#ifndef LIBRESSL_VERSION_NUMBER + u_int32_t seed[256]; +#endif + + if (ps->ps_noaction) + return (0); + proc_open(ps, p, procs, nproc); + + /* Fork child handlers */ switch (pid = fork()) { case -1: fatal("proc_run: cannot fork"); case 0: + log_procinit(p->p_title); + + /* Set the process group of the current process */ + setpgid(0, 0); break; default: return (pid); @@ -207,9 +361,12 @@ proc_run(struct privsep *ps, struct privsep_proc *p, pw = ps->ps_pw; - if (p->p_id == PROC_CONTROL) { + if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) { if (control_init(ps, &ps->ps_csock) == -1) - fatalx(p->p_title); + fatalx(__func__); + TAILQ_FOREACH(rcs, &ps->ps_rcsocks, cs_entry) + if (control_init(ps, rcs) == -1) + fatalx(__func__); } /* Change root directory */ @@ -218,30 +375,31 @@ proc_run(struct privsep *ps, struct privsep_proc *p, else root = pw->pw_dir; -#ifndef DEBUG if (chroot(root) == -1) fatal("proc_run: chroot"); if (chdir("/") == -1) fatal("proc_run: chdir(\"/\")"); -#else -#warning disabling privilege revocation and chroot in DEBUG MODE - if (p->p_chroot != NULL) { - if (chroot(root) == -1) - fatal("proc_run: chroot"); - if (chdir("/") == -1) - fatal("proc_run: chdir(\"/\")"); - } -#endif privsep_process = p->p_id; setproctitle("%s", p->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("proc_run: cannot drop privileges"); + + /* Fork child handlers */ + for (n = 1; n < ps->ps_instances[p->p_id]; n++) { + if (fork() == 0) { + ps->ps_instance = p->p_instance = n; + break; + } + } + +#ifdef DEBUG + log_debug("%s: %s %d/%d, pid %d", __func__, p->p_title, + ps->ps_instance + 1, ps->ps_instances[p->p_id], getpid()); #endif event_init(); @@ -251,26 +409,32 @@ proc_run(struct privsep *ps, struct privsep_proc *p, signal_set(&ps->ps_evsigchld, SIGCHLD, proc_sig_handler, p); signal_set(&ps->ps_evsighup, SIGHUP, proc_sig_handler, p); signal_set(&ps->ps_evsigpipe, SIGPIPE, proc_sig_handler, p); + signal_set(&ps->ps_evsigusr1, SIGUSR1, proc_sig_handler, p); signal_add(&ps->ps_evsigint, NULL); signal_add(&ps->ps_evsigterm, NULL); signal_add(&ps->ps_evsigchld, NULL); signal_add(&ps->ps_evsighup, NULL); signal_add(&ps->ps_evsigpipe, NULL); + signal_add(&ps->ps_evsigusr1, NULL); - proc_config(ps, procs, nproc); - +#ifndef LIBRESSL_VERSION_NUMBER arc4random_buf(seed, sizeof(seed)); RAND_seed(seed, sizeof(seed)); +#endif + proc_listen(ps, procs, nproc); - if (p->p_id == PROC_CONTROL) { + if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) { TAILQ_INIT(&ctl_conns); if (control_listen(&ps->ps_csock) == -1) - fatalx(p->p_title); + fatalx(__func__); + TAILQ_FOREACH(rcs, &ps->ps_rcsocks, cs_entry) + if (control_listen(rcs) == -1) + fatalx(__func__); } - if (init != NULL) - init(ps, arg); + if (run != NULL) + run(ps, p, arg); event_dispatch(); @@ -282,9 +446,9 @@ proc_run(struct privsep *ps, struct privsep_proc *p, void proc_dispatch(int fd, short event, void *arg) { - struct privsep_proc *p = (struct privsep_proc *)arg; + struct imsgev *iev = arg; + struct privsep_proc *p = iev->proc; struct privsep *ps = p->p_ps; - struct imsgev *iev; struct imsgbuf *ibuf; struct imsg imsg; ssize_t n; @@ -292,12 +456,11 @@ proc_dispatch(int fd, short event, void *arg) const char *title; title = ps->ps_title[privsep_process]; - iev = &ps->ps_ievs[p->p_id]; ibuf = &iev->ibuf; if (event & EV_READ) { - if ((n = imsg_read(ibuf)) == -1) - fatal(title); + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) + fatal(__func__); if (n == 0) { /* this pipe is dead, so remove the event handler */ event_del(&iev->ev); @@ -307,16 +470,22 @@ proc_dispatch(int fd, short event, void *arg) } if (event & EV_WRITE) { - if (msgbuf_write(&ibuf->w) == -1) - fatal(title); + if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) + fatal(__func__); } for (;;) { if ((n = imsg_get(ibuf, &imsg)) == -1) - fatal(title); + fatal(__func__); if (n == 0) break; +#if DEBUG > 1 + log_debug("%s: %s %d got imsg %d peerid %d from %s %d", + __func__, title, ps->ps_instance + 1, + imsg.hdr.type, imsg.hdr.peerid, p->p_title, p->p_instance); +#endif + /* * Check the message with the program callback */ @@ -332,20 +501,32 @@ proc_dispatch(int fd, short event, void *arg) 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->p_title, - imsg.hdr.type); - fatalx(title); + log_warnx("%s: %s %d got invalid imsg %d peerid %d " + "from %s %d", + __func__, title, ps->ps_instance + 1, + imsg.hdr.type, imsg.hdr.peerid, + p->p_title, p->p_instance); + fatalx(__func__); } imsg_free(&imsg); } imsg_event_add(iev); } +int +proc_dispatch_null(int fd, struct privsep_proc *p, struct imsg *imsg) +{ + return (-1); +} + +/* + * imsg helper functions + */ + void imsg_event_add(struct imsgev *iev) { @@ -364,8 +545,8 @@ imsg_event_add(struct imsgev *iev) } 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) +imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid, + pid_t pid, int fd, void *data, uint16_t datalen) { int ret; @@ -377,7 +558,7 @@ imsg_compose_event(struct imsgev *iev, u_int16_t type, u_int32_t peerid, } int -imsg_composev_event(struct imsgev *iev, u_int16_t type, u_int32_t peerid, +imsg_composev_event(struct imsgev *iev, uint16_t type, uint32_t peerid, pid_t pid, int fd, const struct iovec *iov, int iovcnt) { int ret; @@ -389,32 +570,86 @@ imsg_composev_event(struct imsgev *iev, u_int16_t type, u_int32_t peerid, return (ret); } +void +proc_range(struct privsep *ps, enum privsep_procid id, int *n, int *m) +{ + if (*n == -1) { + /* Use a range of all target instances */ + *n = 0; + *m = ps->ps_instances[id]; + } else { + /* Use only a single slot of the specified peer process */ + *m = *n + 1; + } +} + int -proc_compose_imsg(struct iked *env, enum privsep_procid id, - u_int16_t type, int fd, void *data, u_int16_t datalen) +proc_compose_imsg(struct privsep *ps, enum privsep_procid id, int n, + uint16_t type, uint32_t peerid, int fd, void *data, uint16_t datalen) { - return (imsg_compose_event(&env->sc_ps.ps_ievs[id], - type, -1, 0, fd, data, datalen)); + int m; + + proc_range(ps, id, &n, &m); + for (; n < m; n++) { + if (imsg_compose_event(&ps->ps_ievs[id][n], + type, peerid, 0, fd, data, datalen) == -1) + return (-1); + } + + return (0); } int -proc_composev_imsg(struct iked *env, enum privsep_procid id, - u_int16_t type, int fd, const struct iovec *iov, int iovcnt) +proc_compose(struct privsep *ps, enum privsep_procid id, + uint16_t type, void *data, uint16_t datalen) { - return (imsg_composev_event(&env->sc_ps.ps_ievs[id], - type, -1, 0, fd, iov, iovcnt)); + return (proc_compose_imsg(ps, id, -1, type, -1, -1, data, datalen)); } int -proc_forward_imsg(struct iked *env, struct imsg *imsg, - enum privsep_procid id) +proc_composev_imsg(struct privsep *ps, enum privsep_procid id, int n, + uint16_t type, uint32_t peerid, int fd, const struct iovec *iov, int iovcnt) { - return (proc_compose_imsg(env, id, imsg->hdr.type, - imsg->fd, imsg->data, IMSG_DATA_SIZE(imsg))); + int m; + + proc_range(ps, id, &n, &m); + for (; n < m; n++) + if (imsg_composev_event(&ps->ps_ievs[id][n], + type, peerid, 0, fd, iov, iovcnt) == -1) + return (-1); + + return (0); } -void -proc_flush_imsg(struct iked *env, enum privsep_procid id) +int +proc_composev(struct privsep *ps, enum privsep_procid id, + uint16_t type, const struct iovec *iov, int iovcnt) { - imsg_flush(&env->sc_ps.ps_ievs[id].ibuf); + return (proc_composev_imsg(ps, id, -1, type, -1, -1, iov, iovcnt)); +} + +int +proc_forward_imsg(struct privsep *ps, struct imsg *imsg, + enum privsep_procid id, int n) +{ + return (proc_compose_imsg(ps, id, n, imsg->hdr.type, + imsg->hdr.peerid, imsg->fd, imsg->data, IMSG_DATA_SIZE(imsg))); +} + +struct imsgbuf * +proc_ibuf(struct privsep *ps, enum privsep_procid id, int n) +{ + int m; + + proc_range(ps, id, &n, &m); + return (&ps->ps_ievs[id][n].ibuf); +} + +struct imsgev * +proc_iev(struct privsep *ps, enum privsep_procid id, int n) +{ + int m; + + proc_range(ps, id, &n, &m); + return (&ps->ps_ievs[id][n]); } diff --git a/iked/smult_curve25519_ref.c b/iked/smult_curve25519_ref.c new file mode 100644 index 0000000..71c106a --- /dev/null +++ b/iked/smult_curve25519_ref.c @@ -0,0 +1,265 @@ +/* $OpenBSD: smult_curve25519_ref.c,v 1.1 2014/08/27 10:28:57 reyk Exp $ */ +/* +version 20081011 +Matthew Dempsky +Public domain. +Derived from public domain code by D. J. Bernstein. +*/ + +int crypto_scalarmult_curve25519(unsigned char *, const unsigned char *, const unsigned char *); + +static void add(unsigned int out[32],const unsigned int a[32],const unsigned int b[32]) +{ + unsigned int j; + unsigned int u; + u = 0; + for (j = 0;j < 31;++j) { u += a[j] + b[j]; out[j] = u & 255; u >>= 8; } + u += a[31] + b[31]; out[31] = u; +} + +static void sub(unsigned int out[32],const unsigned int a[32],const unsigned int b[32]) +{ + unsigned int j; + unsigned int u; + u = 218; + for (j = 0;j < 31;++j) { + u += a[j] + 65280 - b[j]; + out[j] = u & 255; + u >>= 8; + } + u += a[31] - b[31]; + out[31] = u; +} + +static void squeeze(unsigned int a[32]) +{ + unsigned int j; + unsigned int u; + u = 0; + for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; } + u += a[31]; a[31] = u & 127; + u = 19 * (u >> 7); + for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; } + u += a[31]; a[31] = u; +} + +static const unsigned int minusp[32] = { + 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128 +} ; + +static void freeze(unsigned int a[32]) +{ + unsigned int aorig[32]; + unsigned int j; + unsigned int negative; + + for (j = 0;j < 32;++j) aorig[j] = a[j]; + add(a,a,minusp); + negative = -((a[31] >> 7) & 1); + for (j = 0;j < 32;++j) a[j] ^= negative & (aorig[j] ^ a[j]); +} + +static void mult(unsigned int out[32],const unsigned int a[32],const unsigned int b[32]) +{ + unsigned int i; + unsigned int j; + unsigned int u; + + for (i = 0;i < 32;++i) { + u = 0; + for (j = 0;j <= i;++j) u += a[j] * b[i - j]; + for (j = i + 1;j < 32;++j) u += 38 * a[j] * b[i + 32 - j]; + out[i] = u; + } + squeeze(out); +} + +static void mult121665(unsigned int out[32],const unsigned int a[32]) +{ + unsigned int j; + unsigned int u; + + u = 0; + for (j = 0;j < 31;++j) { u += 121665 * a[j]; out[j] = u & 255; u >>= 8; } + u += 121665 * a[31]; out[31] = u & 127; + u = 19 * (u >> 7); + for (j = 0;j < 31;++j) { u += out[j]; out[j] = u & 255; u >>= 8; } + u += out[j]; out[j] = u; +} + +static void square(unsigned int out[32],const unsigned int a[32]) +{ + unsigned int i; + unsigned int j; + unsigned int u; + + for (i = 0;i < 32;++i) { + u = 0; + for (j = 0;j < i - j;++j) u += a[j] * a[i - j]; + for (j = i + 1;j < i + 32 - j;++j) u += 38 * a[j] * a[i + 32 - j]; + u *= 2; + if ((i & 1) == 0) { + u += a[i / 2] * a[i / 2]; + u += 38 * a[i / 2 + 16] * a[i / 2 + 16]; + } + out[i] = u; + } + squeeze(out); +} + +static void select(unsigned int p[64],unsigned int q[64],const unsigned int r[64],const unsigned int s[64],unsigned int b) +{ + unsigned int j; + unsigned int t; + unsigned int bminus1; + + bminus1 = b - 1; + for (j = 0;j < 64;++j) { + t = bminus1 & (r[j] ^ s[j]); + p[j] = s[j] ^ t; + q[j] = r[j] ^ t; + } +} + +static void mainloop(unsigned int work[64],const unsigned char e[32]) +{ + unsigned int xzm1[64]; + unsigned int xzm[64]; + unsigned int xzmb[64]; + unsigned int xzm1b[64]; + unsigned int xznb[64]; + unsigned int xzn1b[64]; + unsigned int a0[64]; + unsigned int a1[64]; + unsigned int b0[64]; + unsigned int b1[64]; + unsigned int c1[64]; + unsigned int r[32]; + unsigned int s[32]; + unsigned int t[32]; + unsigned int u[32]; + unsigned int j; + unsigned int b; + int pos; + + for (j = 0;j < 32;++j) xzm1[j] = work[j]; + xzm1[32] = 1; + for (j = 33;j < 64;++j) xzm1[j] = 0; + + xzm[0] = 1; + for (j = 1;j < 64;++j) xzm[j] = 0; + + for (pos = 254;pos >= 0;--pos) { + b = e[pos / 8] >> (pos & 7); + b &= 1; + select(xzmb,xzm1b,xzm,xzm1,b); + add(a0,xzmb,xzmb + 32); + sub(a0 + 32,xzmb,xzmb + 32); + add(a1,xzm1b,xzm1b + 32); + sub(a1 + 32,xzm1b,xzm1b + 32); + square(b0,a0); + square(b0 + 32,a0 + 32); + mult(b1,a1,a0 + 32); + mult(b1 + 32,a1 + 32,a0); + add(c1,b1,b1 + 32); + sub(c1 + 32,b1,b1 + 32); + square(r,c1 + 32); + sub(s,b0,b0 + 32); + mult121665(t,s); + add(u,t,b0); + mult(xznb,b0,b0 + 32); + mult(xznb + 32,s,u); + square(xzn1b,c1); + mult(xzn1b + 32,r,work); + select(xzm,xzm1,xznb,xzn1b,b); + } + + for (j = 0;j < 64;++j) work[j] = xzm[j]; +} + +static void recip(unsigned int out[32],const unsigned int z[32]) +{ + unsigned int z2[32]; + unsigned int z9[32]; + unsigned int z11[32]; + unsigned int z2_5_0[32]; + unsigned int z2_10_0[32]; + unsigned int z2_20_0[32]; + unsigned int z2_50_0[32]; + unsigned int z2_100_0[32]; + unsigned int t0[32]; + unsigned int t1[32]; + int i; + + /* 2 */ square(z2,z); + /* 4 */ square(t1,z2); + /* 8 */ square(t0,t1); + /* 9 */ mult(z9,t0,z); + /* 11 */ mult(z11,z9,z2); + /* 22 */ square(t0,z11); + /* 2^5 - 2^0 = 31 */ mult(z2_5_0,t0,z9); + + /* 2^6 - 2^1 */ square(t0,z2_5_0); + /* 2^7 - 2^2 */ square(t1,t0); + /* 2^8 - 2^3 */ square(t0,t1); + /* 2^9 - 2^4 */ square(t1,t0); + /* 2^10 - 2^5 */ square(t0,t1); + /* 2^10 - 2^0 */ mult(z2_10_0,t0,z2_5_0); + + /* 2^11 - 2^1 */ square(t0,z2_10_0); + /* 2^12 - 2^2 */ square(t1,t0); + /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t0,t1); square(t1,t0); } + /* 2^20 - 2^0 */ mult(z2_20_0,t1,z2_10_0); + + /* 2^21 - 2^1 */ square(t0,z2_20_0); + /* 2^22 - 2^2 */ square(t1,t0); + /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { square(t0,t1); square(t1,t0); } + /* 2^40 - 2^0 */ mult(t0,t1,z2_20_0); + + /* 2^41 - 2^1 */ square(t1,t0); + /* 2^42 - 2^2 */ square(t0,t1); + /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t1,t0); square(t0,t1); } + /* 2^50 - 2^0 */ mult(z2_50_0,t0,z2_10_0); + + /* 2^51 - 2^1 */ square(t0,z2_50_0); + /* 2^52 - 2^2 */ square(t1,t0); + /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); } + /* 2^100 - 2^0 */ mult(z2_100_0,t1,z2_50_0); + + /* 2^101 - 2^1 */ square(t1,z2_100_0); + /* 2^102 - 2^2 */ square(t0,t1); + /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { square(t1,t0); square(t0,t1); } + /* 2^200 - 2^0 */ mult(t1,t0,z2_100_0); + + /* 2^201 - 2^1 */ square(t0,t1); + /* 2^202 - 2^2 */ square(t1,t0); + /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); } + /* 2^250 - 2^0 */ mult(t0,t1,z2_50_0); + + /* 2^251 - 2^1 */ square(t1,t0); + /* 2^252 - 2^2 */ square(t0,t1); + /* 2^253 - 2^3 */ square(t1,t0); + /* 2^254 - 2^4 */ square(t0,t1); + /* 2^255 - 2^5 */ square(t1,t0); + /* 2^255 - 21 */ mult(out,t1,z11); +} + +int crypto_scalarmult_curve25519(unsigned char *q, + const unsigned char *n, + const unsigned char *p) +{ + unsigned int work[96]; + unsigned char e[32]; + unsigned int i; + for (i = 0;i < 32;++i) e[i] = n[i]; + e[0] &= 248; + e[31] &= 127; + e[31] |= 64; + for (i = 0;i < 32;++i) work[i] = p[i]; + mainloop(work,e); + recip(work + 32,work + 32); + mult(work + 64,work,work + 32); + freeze(work + 64); + for (i = 0;i < 32;++i) q[i] = work[64 + i]; + return 0; +} diff --git a/iked/timer.c b/iked/timer.c index 150673b..3d52291 100644 --- a/iked/timer.c +++ b/iked/timer.c @@ -1,4 +1,4 @@ -/* $OpenBSD: timer.c,v 1.10 2013/01/08 10:38:19 reyk Exp $ */ +/* $OpenBSD: timer.c,v 1.12 2015/01/16 06:39:58 deraadt Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter @@ -16,8 +16,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include "openbsd-compat/sys-queue.h" + #include #include @@ -35,7 +34,7 @@ void timer_callback(int, short, void *); void -timer_initialize(struct iked *env, struct iked_timer *tmr, +timer_set(struct iked *env, struct iked_timer *tmr, void (*cb)(struct iked *, void *), void *arg) { tmr->tmr_env = env; @@ -44,17 +43,8 @@ timer_initialize(struct iked *env, struct iked_timer *tmr, evtimer_set(&tmr->tmr_ev, timer_callback, tmr); } -int -timer_initialized(struct iked *env, struct iked_timer *tmr) -{ - if (tmr && tmr->tmr_env == env && tmr->tmr_cb && - evtimer_initialized(&tmr->tmr_ev)) - return (1); - return (0); -} - void -timer_register(struct iked *env, struct iked_timer *tmr, int timeout) +timer_add(struct iked *env, struct iked_timer *tmr, int timeout) { struct timeval tv = { timeout }; @@ -66,9 +56,11 @@ timer_register(struct iked *env, struct iked_timer *tmr, int timeout) } void -timer_deregister(struct iked *env, struct iked_timer *tmr) +timer_del(struct iked *env, struct iked_timer *tmr) { - evtimer_del(&tmr->tmr_ev); + if (tmr->tmr_env == env && tmr->tmr_cb && + evtimer_initialized(&tmr->tmr_ev)) + evtimer_del(&tmr->tmr_ev); } void diff --git a/iked/types.h b/iked/types.h index d0de6e5..4648809 100644 --- a/iked/types.h +++ b/iked/types.h @@ -1,4 +1,4 @@ -/* $OpenBSD: types.h,v 1.17 2013/01/08 10:38:19 reyk Exp $ */ +/* $OpenBSD: types.h,v 1.25 2016/01/27 20:20:30 gsoares Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter @@ -16,27 +16,32 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef _IKED_TYPES_H -#define _IKED_TYPES_H +#ifndef IKED_TYPES_H +#define IKED_TYPES_H #ifndef IKED_USER -#define IKED_USER "_iked" +#define IKED_USER "_iked" #endif #ifndef IKED_CONFIG -#define IKED_CONFIG "/etc/iked.conf" +#define IKED_CONFIG "/etc/iked.conf" #endif -#define IKED_SOCKET "/var/run/iked.sock" + +#define IKED_SOCKET "/var/run/iked.sock" #ifndef IKED_CA -#define IKED_CA "/etc/iked/" +#define IKED_CA "/etc/iked/" #endif -#define IKED_CA_DIR "ca/" -#define IKED_CRL_DIR "crls/" -#define IKED_CERT_DIR "certs/" -#define IKED_PUBKEY_DIR "pubkeys/" -#define IKED_PRIVKEY IKED_CA "private/local.key" -#define IKED_PUBKEY "local.pub" + +#define IKED_CA_DIR "ca/" +#define IKED_CRL_DIR "crls/" +#define IKED_CERT_DIR "certs/" +#define IKED_PUBKEY_DIR "pubkeys/" +#define IKED_PRIVKEY IKED_CA "private/local.key" +#define IKED_PUBKEY "local.pub" + +#define IKED_OCSP_RESPCERT "ocsp/responder.crt" +#define IKED_OCSP_ISSUER "ocsp/issuer.crt" #define IKED_OPT_VERBOSE 0x00000001 #define IKED_OPT_NOACTION 0x00000002 @@ -51,7 +56,7 @@ #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_ID_SIZE 1024 /* XXX should be dynamic */ #define IKED_PSK_SIZE 1024 /* XXX should be dynamic */ #define IKED_MSGBUF_MAX 8192 #define IKED_CFG_MAX 16 /* maximum CP attributes */ @@ -65,17 +70,17 @@ #define IKED_E 0x1000 /* Decrypted flag */ struct iked_constmap { - u_int cm_type; + unsigned 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; + uint8_t xform_type; + uint16_t xform_id; + uint16_t xform_length; + uint16_t xform_keylength; + unsigned int xform_score; struct iked_constmap *xform_map; }; @@ -101,25 +106,19 @@ enum imsg_type { IMSG_CERT, IMSG_CERTVALID, IMSG_CERTINVALID, + IMSG_OCSP_FD, + IMSG_OCSP_URL, IMSG_AUTH }; enum privsep_procid { PROC_PARENT = 0, - PROC_IKEV1, - PROC_IKEV2, + PROC_CONTROL, PROC_CERT, + PROC_IKEV2, 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, @@ -133,4 +132,4 @@ enum flushmode { #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) #endif -#endif /* _IKED_TYPES_H */ +#endif /* IKED_TYPES_H */ diff --git a/iked/util.c b/iked/util.c index 5b0f013..d1dba33 100644 --- a/iked/util.c +++ b/iked/util.c @@ -1,4 +1,4 @@ -/* $OpenBSD: util.c,v 1.18 2013/01/08 10:38:19 reyk Exp $ */ +/* $OpenBSD: util.c,v 1.30 2015/11/23 19:28:34 reyk Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter @@ -17,23 +17,17 @@ */ #include -#include "openbsd-compat/sys-queue.h" + #include #include -#include -#include -#include -#include -#include -#include #include - #include #include #include #include #include +#include #include #include #include @@ -41,58 +35,41 @@ #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"); -} +/* log.c */ +extern int debug; +extern int verbose; int socket_af(struct sockaddr *sa, in_port_t port) { + u_int salen; + errno = 0; switch (sa->sa_family) { case AF_INET: ((struct sockaddr_in *)sa)->sin_port = port; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - ((struct sockaddr_in *)sa)->sin_len = - sizeof(struct sockaddr_in); -#endif + salen = sizeof(struct sockaddr_in); break; case AF_INET6: ((struct sockaddr_in6 *)sa)->sin6_port = port; -#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN - ((struct sockaddr_in6 *)sa)->sin6_len = - sizeof(struct sockaddr_in6); -#endif + salen = sizeof(struct sockaddr_in6); break; default: errno = EPFNOSUPPORT; return (-1); } - + SET_SA_LEN(sa, salen); return (0); } in_port_t -socket_getport(struct sockaddr_storage *ss) +socket_getport(struct sockaddr *sa) { - switch (ss->ss_family) { + switch (sa->sa_family) { case AF_INET: - return (ntohs(((struct sockaddr_in *)ss)->sin_port)); + return (ntohs(((struct sockaddr_in *)sa)->sin_port)); case AF_INET6: - return (ntohs(((struct sockaddr_in6 *)ss)->sin6_port)); + return (ntohs(((struct sockaddr_in6 *)sa)->sin6_port)); default: return (0); } @@ -101,6 +78,22 @@ socket_getport(struct sockaddr_storage *ss) return (0); } +int +socket_setport(struct sockaddr *sa, in_port_t port) +{ + switch (sa->sa_family) { + case AF_INET: + ((struct sockaddr_in *)sa)->sin_port = htons(port); + break; + case AF_INET6: + ((struct sockaddr_in6 *)sa)->sin6_port = htons(port); + break; + default: + return (-1); + } + return (0); +} + int socket_getaddr(int s, struct sockaddr_storage *ss) { @@ -219,7 +212,8 @@ udp_bind(struct sockaddr *sa, in_port_t port) return (-1); } - if ((s = socket(sa->sa_family, SOCK_DGRAM, IPPROTO_UDP)) == -1) { + if ((s = bsd_socket(sa->sa_family, + SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP)) == -1) { log_warn("%s: failed to get UDP socket", __func__); return (-1); } @@ -231,30 +225,23 @@ udp_bind(struct sockaddr *sa, in_port_t port) goto bad; } -#ifdef SO_REUSEPORT val = 1; if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(int)) == -1) { log_warn("%s: failed to set reuseport", __func__); goto bad; } -#endif -#ifdef SO_REUSEADDR val = 1; if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(int)) == -1) { log_warn("%s: failed to set reuseaddr", __func__); goto bad; } -#endif if (sa->sa_family == AF_INET) { val = 1; - if (setsockopt(s, IPPROTO_IP, -#if defined(IP_RECVDSTADDR) - IP_RECVDSTADDR, -#elif defined(IP_PKTINFO) - IP_PKTINFO, -#else -#error IPv4 packet info not supported +#if HAVE_DECL_IP_RECVORIGDSTADDR + if (setsockopt(s, IPPROTO_IP, IP_RECVORIGDSTADDR, +#elif HAVE_DECL_IP_RECVDSTADDR + if (setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, #endif &val, sizeof(int)) == -1) { log_warn("%s: failed to set IPv4 packet info", @@ -262,15 +249,6 @@ udp_bind(struct sockaddr *sa, in_port_t port) goto bad; } } else { -#ifdef IPV6_V6ONLY - val = 1; - if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, - &val, sizeof(int)) == -1) { - log_warn("%s: failed to set IPv6-only mode", - __func__); - goto bad; - } -#endif val = 1; if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(int)) == -1) { @@ -296,7 +274,7 @@ 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]; + uint32_t av[4], bv[4], mv[4]; if (a->sa_family == AF_UNSPEC || b->sa_family == AF_UNSPEC) return (0); @@ -362,16 +340,12 @@ recvfromto(int s, void *buf, size_t len, int flags, struct sockaddr *from, 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; -#if !defined(IP_RECVDSTADDR) && defined(IP_PKTINFO) - struct in_pktinfo *pkt; -#endif bzero(&msg, sizeof(msg)); bzero(&cmsgbuf.buf, sizeof(cmsgbuf.buf)); @@ -398,41 +372,28 @@ recvfromto(int s, void *buf, size_t len, int flags, struct sockaddr *from, cmsg = CMSG_NXTHDR(&msg, cmsg)) { switch (from->sa_family) { case AF_INET: -#if defined(IP_RECVDSTADDR) +#if HAVE_DECL_IP_RECVDSTADDR 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)); + to->sa_family = AF_INET; + SET_SA_LEN(to, sizeof(struct sockaddr_in)); + memcpy(&((struct sockaddr_in *)to)->sin_addr, + CMSG_DATA(cmsg), sizeof(struct in_addr)); } -#elif defined(IP_PKTINFO) +#elif HAVE_DECL_IP_RECVORIGDSTADDR if (cmsg->cmsg_level == IPPROTO_IP && - cmsg->cmsg_type == IP_PKTINFO) { - in = (struct sockaddr_in *)to; - in->sin_family = AF_INET; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - in->sin_len = *tolen = sizeof(*in); -#else - *tolen = sizeof(*in); -#endif - pkt = (struct in_pktinfo *)CMSG_DATA(cmsg); - memcpy(&in->sin_addr, &pkt->ipi_addr, - sizeof(struct in_addr)); + cmsg->cmsg_type == IP_RECVORIGDSTADDR) { + memcpy(to, CMSG_DATA(cmsg), + sizeof(struct sockaddr_in)); } #endif break; case AF_INET6: if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { + to->sa_family = AF_INET6; + SET_SA_LEN(to, sizeof(struct sockaddr_in6)); in6 = (struct sockaddr_in6 *)to; - in6->sin6_family = AF_INET6; -#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN - in6->sin6_len = *tolen = sizeof(*in6); -#else - *tolen = sizeof(*in6); -#endif pkt6 = (struct in6_pktinfo *)CMSG_DATA(cmsg); memcpy(&in6->sin6_addr, &pkt6->ipi6_addr, sizeof(struct in6_addr)); @@ -448,7 +409,7 @@ recvfromto(int s, void *buf, size_t len, int flags, struct sockaddr *from, } const char * -print_spi(u_int64_t spi, int size) +print_spi(uint64_t spi, int size) { static char buf[IKED_CYCLE_BUFFERS][32]; static int i = 0; @@ -457,8 +418,11 @@ print_spi(u_int64_t spi, int size) ptr = buf[i]; switch (size) { + case 2: + snprintf(ptr, 32, "0x%04x", (uint16_t)spi); + break; case 4: - snprintf(ptr, 32, "0x%08x", (u_int32_t)spi); + snprintf(ptr, 32, "0x%08x", (uint32_t)spi); break; case 8: snprintf(ptr, 32, "0x%016llx", spi); @@ -475,9 +439,9 @@ print_spi(u_int64_t spi, int size) } const char * -print_map(u_int type, struct iked_constmap *map) +print_map(unsigned int type, struct iked_constmap *map) { - u_int i; + unsigned int i; static char buf[IKED_CYCLE_BUFFERS][32]; static int idx = 0; const char *name = NULL; @@ -503,16 +467,16 @@ void lc_string(char *str) { for (; *str != '\0'; str++) - *str = tolower(*str); + *str = tolower((unsigned char)*str); } void -print_hex(u_int8_t *buf, off_t offset, size_t length) +print_hex(uint8_t *buf, off_t offset, size_t length) { - u_int i; + unsigned int i; extern int verbose; - if (verbose < 2 || !length) + if (verbose < 3 || !length) return; for (i = 0; i < length; i++) { @@ -528,9 +492,9 @@ print_hex(u_int8_t *buf, off_t offset, size_t length) } void -print_hexval(u_int8_t *buf, off_t offset, size_t length) +print_hexval(uint8_t *buf, off_t offset, size_t length) { - u_int i; + unsigned int i; extern int verbose; if (verbose < 2 || !length) @@ -543,12 +507,12 @@ print_hexval(u_int8_t *buf, off_t offset, size_t length) } const char * -print_bits(u_short v, char *bits) +print_bits(unsigned short v, unsigned char *bits) { static char buf[IKED_CYCLE_BUFFERS][BUFSIZ]; static int idx = 0; - u_int i, any = 0, j = 0; - char c; + unsigned int i, any = 0, j = 0; + unsigned char c; if (!bits) return (""); @@ -568,7 +532,7 @@ print_bits(u_short v, char *bits) } any = 1; for (; (c = *bits) > 32; bits++) { - buf[idx][j++] = tolower(c); + buf[idx][j++] = tolower((unsigned char)c); if (j >= sizeof(buf[idx])) return (buf[idx]); } @@ -580,7 +544,7 @@ print_bits(u_short v, char *bits) return (buf[idx]); } -u_int8_t +uint8_t mask2prefixlen(struct sockaddr *sa) { struct sockaddr_in *sa_in = (struct sockaddr_in *)sa; @@ -592,18 +556,18 @@ mask2prefixlen(struct sockaddr *sa) return (33 - ffs(ntohl(ina))); } -u_int8_t +uint8_t mask2prefixlen6(struct sockaddr *sa) { struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)sa; - u_int8_t l = 0, *ap, *ep; + uint8_t l = 0, *ap, *ep; /* * sin6_len is the size of the sockaddr so substract the offset of * the possibly truncated sin6_addr struct. */ - ap = (u_int8_t *)&sa_in6->sin6_addr; - ep = (u_int8_t *)sa_in6 + SA_LEN(sa); + ap = (uint8_t *)&sa_in6->sin6_addr; + ep = (uint8_t *)sa_in6 + SA_LEN((struct sockaddr *)sa_in6); for (; ap < ep; ap++) { /* this "beauty" is adopted from sbin/route/show.c ... */ switch (*ap) { @@ -641,8 +605,8 @@ mask2prefixlen6(struct sockaddr *sa) return (l); } -u_int32_t -prefixlen2mask(u_int8_t prefixlen) +uint32_t +prefixlen2mask(uint8_t prefixlen) { if (prefixlen == 0) return (0); @@ -654,7 +618,7 @@ prefixlen2mask(u_int8_t prefixlen) } struct in6_addr * -prefixlen2mask6(u_int8_t prefixlen, u_int32_t *mask) +prefixlen2mask6(uint8_t prefixlen, uint32_t *mask) { static struct in6_addr s6; int i; @@ -675,7 +639,7 @@ prefixlen2mask6(u_int8_t prefixlen, u_int32_t *mask) } const char * -print_host(struct sockaddr_storage *ss, char *buf, size_t len) +print_host(struct sockaddr *sa, char *buf, size_t len) { static char sbuf[IKED_CYCLE_BUFFERS][NI_MAXHOST + 7]; static int idx = 0; @@ -689,18 +653,18 @@ print_host(struct sockaddr_storage *ss, char *buf, size_t len) idx = 0; } - if (ss->ss_family == AF_UNSPEC) { + if (sa->sa_family == AF_UNSPEC) { strlcpy(buf, "any", len); return (buf); } - if (getnameinfo((struct sockaddr *)ss, SS_LEN(ss), + if (getnameinfo(sa, SA_LEN(sa), buf, len, NULL, 0, NI_NUMERICHOST) != 0) { buf[0] = '\0'; return (NULL); } - if ((port = socket_getport(ss)) != 0) { + if ((port = socket_getport(sa)) != 0) { snprintf(pbuf, sizeof(pbuf), ":%d", port); (void)strlcat(buf, pbuf, len); } @@ -709,24 +673,19 @@ print_host(struct sockaddr_storage *ss, char *buf, size_t len) } char * -get_string(u_int8_t *ptr, size_t len) +get_string(uint8_t *ptr, size_t len) { size_t i; - char *str; for (i = 0; i < len; i++) - if (!isprint((char)ptr[i])) + if (!isprint(ptr[i])) break; - if ((str = calloc(1, i + 1)) == NULL) - return (NULL); - memcpy(str, ptr, i); - - return (str); + return strndup(ptr, i); } const char * -print_proto(u_int8_t proto) +print_proto(uint8_t proto) { struct protoent *p; static char buf[IKED_CYCLE_BUFFERS][BUFSIZ]; @@ -777,10 +736,10 @@ expand_string(char *label, size_t len, const char *srch, const char *repl) return (0); } -u_int8_t * +uint8_t * string2unicode(const char *ascii, size_t *outlen) { - u_int8_t *uc = NULL; + uint8_t *uc = NULL; size_t i, len = strlen(ascii); if ((uc = calloc(1, (len * 2) + 2)) == NULL) @@ -794,3 +753,27 @@ string2unicode(const char *ascii, size_t *outlen) return (uc); } + +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); + } +} diff --git a/openbsd-compat/Makefile.am b/openbsd-compat/Makefile.am index 80b5eba..4953479 100644 --- a/openbsd-compat/Makefile.am +++ b/openbsd-compat/Makefile.am @@ -1,9 +1,9 @@ noinst_LIBRARIES= libopenbsd-compat.a -libopenbsd_compat_a_SOURCES= \ - bsd-arc4random.c bsd-closefrom.c \ - daemon.c entropy.c fgetln.c getopt.c imsg.c imsg-buffer.c \ - setresguid.c setproctitle.c \ +libopenbsd_compat_a_SOURCES= bsd-arc4random.c bsd-err.c \ + bsd-closefrom.c bsd-pledge.c bsd-socket.c \ + daemon.c explicit-bzero.c fgetln.c getopt.c imsg.c \ + imsg-buffer.c reallocarray.c setresguid.c setproctitle.c \ strlcat.c strlcpy.c strtonum.c vis.c EXTRA_DIST= defines.h imsg.h includes.h \ diff --git a/openbsd-compat/bsd-err.c b/openbsd-compat/bsd-err.c new file mode 100644 index 0000000..0ab08fb --- /dev/null +++ b/openbsd-compat/bsd-err.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016 Reyk Floeter + * + * 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. + */ + +#if !defined(HAVE_ERRC) || !defined(HAVE_WARNC) +#include +#include +#include +#endif + +#ifndef HAVE_ERRC +void +errc(int eval, int code, const char *fmt, ...) +{ + va_list ap; + int saved_errno = errno; + + errno = code; + + va_start(ap, fmt); + verr(eval, fmt, ap); + va_end(ap); + + /* NOTREACHED */ + errno = saved_errno; +} +#endif + +#ifndef HAVE_WARNC +void +warnc(int code, const char *fmt, ...) +{ + va_list ap; + int saved_errno = errno; + + errno = code; + + va_start(ap, fmt); + vwarn(fmt, ap); + va_end(ap); + + errno = saved_errno; +} +#endif diff --git a/openbsd-compat/bsd-pledge.c b/openbsd-compat/bsd-pledge.c new file mode 100644 index 0000000..ad3fd41 --- /dev/null +++ b/openbsd-compat/bsd-pledge.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Reyk Floeter + * + * 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. + */ + +#if !defined(HAVE_PLEDGE) +#include + +extern void log_debug(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); + +/* + * XXX Implement pledge using other sandbox methods? + */ +int +pledge(const char *promises, const char *paths[]) +{ + int i; + + log_debug("%s: pid %d promises: %s", __func__, getpid(), promises); + for (i = 0; paths != NULL && paths[i] != NULL; i++) + log_debug("%s: pid %d paths[%d]: %s", + __func__, getpid(), i, paths[i]); + + return (0); +} +#endif diff --git a/openbsd-compat/bsd-socket.c b/openbsd-compat/bsd-socket.c new file mode 100644 index 0000000..828c9df --- /dev/null +++ b/openbsd-compat/bsd-socket.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2016 Reyk Floeter + * + * 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. + */ + +#if defined(SOCK_SETFLAGS) +#include +#include + +static int +bsd_socket_setflags(int s, int flags) +{ + int f; + + if (flags & SOCK_NONBLOCK) { + if (fcntl(s, F_GETFL, &f) == -1) + return (-1); + f |= O_NONBLOCK; + if (fcntl(s, F_SETFL, &f) == -1) + return (-1); + } + + if (flags & SOCK_CLOEXEC) { + if (fcntl(s, F_GETFD, &f) == -1) + return (-1); + f |= FD_CLOEXEC; + if (fcntl(s, F_SETFD, &f) == -1) + return (-1); + } + + return (0); +} +#endif + +int +bsd_socket(int domain, int type, int protocol) +{ + int s; +#if defined(SOCK_SETFLAGS) + int setfl; + + setfl = type & SOCK_SETFLAGS; + type &= ~SOCK_SETFLAGS; +#endif + + if ((s = socket(domain, type, protocol)) == -1) + return (-1); + +#if defined(SOCK_SETFLAGS) + if (bsd_socket_setflags(s, setfl) == -1) { + close(s); + return (-1); + } +#endif + + return (s); +} + +int +bsd_socketpair(int d, int type, int protocol, int sv[2]) +{ +#if defined(SOCK_SETFLAGS) + int setfl; + int i; + + setfl = type & SOCK_SETFLAGS; + type &= ~SOCK_SETFLAGS; +#endif + + if (socketpair(d, type, protocol, sv) == -1) + return (-1); + +#if defined(SOCK_SETFLAGS) + for (i = 0; i < 2; i++) { + if (bsd_socket_setflags(sv[i], setfl) == -1) { + close(sv[0]); + close(sv[1]); + return (-1); + } + } +#endif + + return (0); +} + +int +bsd_accept4(int s, struct sockaddr *addr, socklen_t *addrlen, int flags) +{ +#if !defined(SOCK_SETFLAGS) && defined(HAVE_ACCEPT4) + return (accept4(s, addr, addrlen, flags)); +#else + int c; + int setfl = flags & SOCK_SETFLAGS; + + if ((c = accept(s, addr, addrlen)) == -1) + return (-1); + + if (bsd_socket_setflags(c, setfl) == -1) { + close(c); + return (-1); + } + + return (c); +#endif +} diff --git a/openbsd-compat/defines.h b/openbsd-compat/defines.h index fa9dbdf..df4bbea 100644 --- a/openbsd-compat/defines.h +++ b/openbsd-compat/defines.h @@ -36,7 +36,7 @@ #if defined(HAVE_DECL_SHUT_RD) && HAVE_DECL_SHUT_RD == 0 enum { - SHUT_RD = 0, /* No more receptions. */ + SHUT_RD = 0, /* No more receptions. */ SHUT_WR, /* No more transmissions. */ SHUT_RDWR /* No more receptions or transmissions. */ }; @@ -46,17 +46,23 @@ enum #endif #ifndef IPTOS_LOWDELAY -# define IPTOS_LOWDELAY 0x10 -# define IPTOS_THROUGHPUT 0x08 -# define IPTOS_RELIABILITY 0x04 -# define IPTOS_LOWCOST 0x02 -# define IPTOS_MINCOST IPTOS_LOWCOST +# define IPTOS_LOWDELAY 0x10 +# define IPTOS_THROUGHPUT 0x08 +# define IPTOS_RELIABILITY 0x04 +# define IPTOS_LOWCOST 0x02 +# define IPTOS_MINCOST IPTOS_LOWCOST #endif /* IPTOS_LOWDELAY */ #if !defined(HAVE_NETINET_IP_IPSP_H) && (defined(HAVE_NETINET6_IPSEC_H) || \ defined(HAVE_NETIPSEC_IPSEC_H) || defined(HAVE_LINUX_IPSEC_H)) #define IPSP_DIRECTION_IN IPSEC_DIR_INBOUND #define IPSP_DIRECTION_OUT IPSEC_DIR_OUTBOUND /* XXX Linux: _FWD? */ +#ifndef CPI_PRIVATE_MIN +#define CPI_PRIVATE_MIN 61440 +#endif +#ifndef CPI_PRIVATE_MAX +#define CPI_PRIVATE_MAX 65535 +#endif #endif #if defined(HAVE_NET_PFKEYV2_H) || defined(HAVE_LINUX_PFKEYV2_H) @@ -83,44 +89,30 @@ enum # ifdef _POSIX_PATH_MAX # define PATH_MAX _POSIX_PATH_MAX # else -# define PATH_MAX 64 +# define PATH_MAX 256 # endif #endif -/* - * Looks like ugly, but MAX_IMSGSIZE equals 16384, - * and if we don't care it will overflow for some struct - */ -#if PATH_MAX > 1024 -# undef PATH_MAX -# define PATH_MAX 1024 -#endif - #ifndef MAXPATHLEN # define MAXPATHLEN PATH_MAX #endif -#if MAXPATHLEN > 1024 -# undef MAXPATHLEN -# define MAXPATHLEN 1024 -#endif - #if defined(HAVE_DECL_MAXSYMLINKS) && HAVE_DECL_MAXSYMLINKS == 0 # define MAXSYMLINKS 5 #endif #ifndef MAXLOGNAME -#define MAXLOGNAME LOGIN_NAME_MAX +#define MAXLOGNAME LOGIN_NAME_MAX #endif #ifndef STDIN_FILENO -# define STDIN_FILENO 0 +# define STDIN_FILENO 0 #endif #ifndef STDOUT_FILENO -# define STDOUT_FILENO 1 +# define STDOUT_FILENO 1 #endif #ifndef STDERR_FILENO -# define STDERR_FILENO 2 +# define STDERR_FILENO 2 #endif #ifndef NGROUPS_MAX /* Disable groupaccess if NGROUP_MAX is not set */ @@ -132,7 +124,20 @@ enum #endif #if defined(HAVE_DECL_O_NONBLOCK) && HAVE_DECL_O_NONBLOCK == 0 -# define O_NONBLOCK 00004 /* Non Blocking Open */ +# define O_NONBLOCK 00004 /* Non Blocking Open */ +#endif + +#if defined(HAVE_DECL_SOCK_NONBLOCK) && HAVE_DECL_SOCK_NONBLOCK == 0 +# define SOCK_NONBLOCK 0x4000 /* Set O_NONBLOCK */ +#endif + +#if defined(HAVE_DECL_SOCK_CLOEXEC) && HAVE_DECL_SOCK_CLOEXEC == 0 +# define SOCK_CLOEXEC 0x8000 /* Set FD_CLOEXEC */ +#endif + +#if (defined(HAVE_DECL_SOCK_NONBLOCK) && HAVE_DECL_SOCK_NONBLOCK == 0) || \ + (defined(HAVE_DECL_SOCK_CLOEXEC) && HAVE_DECL_SOCK_CLOEXEC == 0) +# define SOCK_SETFLAGS 0xf000 /* Set flags as checked above */ #endif #ifndef S_ISDIR @@ -353,8 +358,8 @@ typedef u_int16_t in_port_t; #if defined(BROKEN_SYS_TERMIO_H) && !defined(_STRUCT_WINSIZE) #define _STRUCT_WINSIZE struct winsize { - unsigned short ws_row; /* rows, in characters */ - unsigned short ws_col; /* columns, in character */ + unsigned short ws_row; /* rows, in characters */ + unsigned short ws_col; /* columns, in character */ unsigned short ws_xpixel; /* horizontal size, pixels */ unsigned short ws_ypixel; /* vertical size, pixels */ }; @@ -474,7 +479,7 @@ struct sadb_sa_natt { #endif #ifndef roundup -# define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) +# define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) #endif #ifndef timersub @@ -490,9 +495,9 @@ struct sadb_sa_natt { #endif #ifndef timespeccmp -#define timespeccmp(a, b, cmp) \ - (((a)->tv_sec == (b)->tv_sec) ? \ - ((a)->tv_nsec cmp (b)->tv_nsec) : \ +#define timespeccmp(a, b, cmp) \ + (((a)->tv_sec == (b)->tv_sec) ? \ + ((a)->tv_nsec cmp (b)->tv_nsec) : \ ((a)->tv_sec cmp (b)->tv_sec)) #endif @@ -555,6 +560,7 @@ struct sadb_sa_natt { #if !defined(HAVE_ATTRIBUTE__BOUNDED__) && !defined(__bounded__) # define __bounded__(x, y, z...) +# define HAVE_ATTRIBUTE__BOUNDED__ /* LibreSSL checks it again */ #endif #if !defined(HAVE_ATTRIBUTE__NONNULL__) && !defined(__nonnull__) @@ -610,7 +616,7 @@ struct sadb_sa_natt { # define LITTLE_ENDIAN 1234 # endif /* LITTLE_ENDIAN */ # ifndef BIG_ENDIAN -# define BIG_ENDIAN 4321 +# define BIG_ENDIAN 4321 # endif /* BIG_ENDIAN */ # ifdef WORDS_BIGENDIAN # define BYTE_ORDER BIG_ENDIAN diff --git a/openbsd-compat/entropy.c b/openbsd-compat/entropy.c deleted file mode 100644 index ea798a7..0000000 --- a/openbsd-compat/entropy.c +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (c) 2001 Damien Miller. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" - -#include -#include -#ifdef HAVE_SYS_UN_H -# include -#endif - -#include -#include - -#include -#include -#include -#include -#include /* for offsetof */ - -#include -#include -#include - -/* - * Portable OpenSSH PRNG seeding: - * If OpenSSL has not "internally seeded" itself (e.g. pulled data from - * /dev/random), then collect RANDOM_SEED_SIZE bytes of randomness from - * PRNGd. - */ -#ifndef OPENSSL_PRNG_ONLY - -#define RANDOM_SEED_SIZE 48 - -/* - * Collect 'len' bytes of entropy into 'buf' from PRNGD/EGD daemon - * listening either on 'tcp_port', or via Unix domain socket at * - * 'socket_path'. - * Either a non-zero tcp_port or a non-null socket_path must be - * supplied. - * Returns 0 on success, -1 on error - */ -int -get_random_bytes_prngd(unsigned char *buf, int len, - unsigned short tcp_port, char *socket_path) -{ - int fd, addr_len, rval, errors; - u_char msg[2]; - struct sockaddr_storage addr; - struct sockaddr_in *addr_in = (struct sockaddr_in *)&addr; - struct sockaddr_un *addr_un = (struct sockaddr_un *)&addr; - mysig_t old_sigpipe; - - /* Sanity checks */ - if (socket_path == NULL && tcp_port == 0) - errx(1, "You must specify a port or a socket"); - if (socket_path != NULL && - strlen(socket_path) >= sizeof(addr_un->sun_path)) - errx(1, "Random pool path is too long"); - if (len <= 0 || len > 255) - errx(1, "Too many bytes (%d) to read from PRNGD", len); - - memset(&addr, '\0', sizeof(addr)); - - if (tcp_port != 0) { - addr_in->sin_family = AF_INET; - addr_in->sin_addr.s_addr = htonl(INADDR_LOOPBACK); - addr_in->sin_port = htons(tcp_port); - addr_len = sizeof(*addr_in); - } else { - addr_un->sun_family = AF_UNIX; - strlcpy(addr_un->sun_path, socket_path, - sizeof(addr_un->sun_path)); - addr_len = offsetof(struct sockaddr_un, sun_path) + - strlen(socket_path) + 1; - } - - old_sigpipe = mysignal(SIGPIPE, SIG_IGN); - - errors = 0; - rval = -1; -reopen: - fd = socket(addr.ss_family, SOCK_STREAM, 0); - if (fd == -1) { - warn("Couldn't create socket"); - goto done; - } - - if (connect(fd, (struct sockaddr*)&addr, addr_len) == -1) { - if (tcp_port != 0) { - warn("Couldn't connect to PRNGD port %d", - tcp_port); - } else { - warn("Couldn't connect to PRNGD socket \"%s\"", - addr_un->sun_path); - } - goto done; - } - - /* Send blocking read request to PRNGD */ - msg[0] = 0x02; - msg[1] = len; - - if (atomicio(vwrite, fd, msg, sizeof(msg)) != sizeof(msg)) { - if (errno == EPIPE && errors < 10) { - close(fd); - errors++; - goto reopen; - } - warn("Couldn't write to PRNGD socket"); - goto done; - } - - if (atomicio(read, fd, buf, len) != (size_t)len) { - if (errno == EPIPE && errors < 10) { - close(fd); - errors++; - goto reopen; - } - warn("Couldn't read from PRNGD socket"); - goto done; - } - - rval = 0; -done: - mysignal(SIGPIPE, old_sigpipe); - if (fd != -1) - close(fd); - return rval; -} - -static int -seed_from_prngd(unsigned char *buf, size_t bytes) -{ -#ifdef PRNGD_PORT - debug("trying egd/prngd port %d", PRNGD_PORT); - if (get_random_bytes_prngd(buf, bytes, PRNGD_PORT, NULL) == 0) - return 0; -#endif -#ifdef PRNGD_SOCKET - debug("trying egd/prngd socket %s", PRNGD_SOCKET); - if (get_random_bytes_prngd(buf, bytes, 0, PRNGD_SOCKET) == 0) - return 0; -#endif - return -1; -} -#endif /* OPENSSL_PRNG_ONLY */ - -void -seed_rng(void) -{ -#ifndef OPENSSL_PRNG_ONLY - unsigned char buf[RANDOM_SEED_SIZE]; -#endif - /* - * OpenSSL version numbers: MNNFFPPS: major minor fix patch status - * We match major, minor, fix and status (not patch) for <1.0.0. - * After that, we acceptable compatible fix versions (so we - * allow 1.0.1 to work with 1.0.0). Going backwards is only allowed - * within a patch series. - */ - u_long version_mask = SSLeay() >= 0x1000000f ? ~0xffff0L : ~0xff0L; - if (((SSLeay() ^ OPENSSL_VERSION_NUMBER) & version_mask) || - (SSLeay() >> 12) < (OPENSSL_VERSION_NUMBER >> 12)) - errx(1, "OpenSSL version mismatch. Built against %lx, you " - "have %lx", (u_long)OPENSSL_VERSION_NUMBER, SSLeay()); - -#ifndef OPENSSL_PRNG_ONLY - if (RAND_status() == 1) { - warnx("RNG is ready, skipping seeding"); - return; - } - - if (seed_from_prngd(buf, sizeof(buf)) == -1) - errx(1, "Could not obtain seed from PRNGd"); - RAND_add(buf, sizeof(buf), sizeof(buf)); - memset(buf, '\0', sizeof(buf)); - -#endif /* OPENSSL_PRNG_ONLY */ - if (RAND_status() != 1) - errx(1, "PRNG is not seeded"); -} diff --git a/openbsd-compat/explicit-bzero.c b/openbsd-compat/explicit-bzero.c new file mode 100644 index 0000000..76b96ea --- /dev/null +++ b/openbsd-compat/explicit-bzero.c @@ -0,0 +1,21 @@ +/* $OpenBSD: explicit_bzero.c,v 1.3 2014/06/21 02:34:26 matthew Exp $ */ +/* + * Public domain. + * Written by Matthew Dempsky. + */ + +#if !defined(EXPLICIT_BZERO) +#include + +__attribute__((weak)) void +__explicit_bzero_hook(void *buf, size_t len) +{ +} + +void +explicit_bzero(void *buf, size_t len) +{ + memset(buf, 0, len); + __explicit_bzero_hook(buf, len); +} +#endif diff --git a/openbsd-compat/imsg.c b/openbsd-compat/imsg.c index a4536a2..cb06548 100644 --- a/openbsd-compat/imsg.c +++ b/openbsd-compat/imsg.c @@ -1,4 +1,4 @@ -/* $OpenBSD: imsg.c,v 1.2 2012/06/02 21:46:53 gilles Exp $ */ +/* $OpenBSD: imsg.c,v 1.13 2015/12/09 11:54:12 tb Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -16,7 +16,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include +#include #include #include #include @@ -36,7 +36,7 @@ void imsg_init(struct imsgbuf *ibuf, int fd) { msgbuf_init(&ibuf->w); - bzero(&ibuf->r, sizeof(ibuf->r)); + memset(&ibuf->r, 0, sizeof(ibuf->r)); ibuf->fd = fd; ibuf->w.fd = fd; ibuf->pid = getpid(); @@ -80,7 +80,8 @@ imsg_read(struct imsgbuf *ibuf) int fd; struct imsg_fd *ifd; - bzero(&msg, sizeof(msg)); + memset(&msg, 0, sizeof(msg)); + memset(&cmsgbuf, 0, sizeof(cmsgbuf)); iov.iov_base = ibuf->r.buf + ibuf->r.wpos; iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos; @@ -96,9 +97,10 @@ imsg_read(struct imsgbuf *ibuf) if (available_fds(imsg_fd_overhead + (CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int))) { errno = EAGAIN; + free(ifd); return (-1); } - + if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) { if (errno == EMSGSIZE) goto fail; @@ -138,8 +140,7 @@ imsg_read(struct imsgbuf *ibuf) } fail: - if (ifd) - free(ifd); + free(ifd); return (n); } @@ -163,7 +164,9 @@ imsg_get(struct imsgbuf *ibuf, struct imsg *imsg) return (0); datalen = imsg->hdr.len - IMSG_HEADER_SIZE; ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE; - if ((imsg->data = malloc(datalen)) == NULL) + if (datalen == 0) + imsg->data = NULL; + else if ((imsg->data = malloc(datalen)) == NULL) return (-1); if (imsg->hdr.flags & IMSGF_HASFD) @@ -185,7 +188,7 @@ imsg_get(struct imsgbuf *ibuf, struct imsg *imsg) int imsg_compose(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid, - pid_t pid, int fd, void *data, u_int16_t datalen) + pid_t pid, int fd, const void *data, u_int16_t datalen) { struct ibuf *wbuf; @@ -255,7 +258,7 @@ imsg_create(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid, } int -imsg_add(struct ibuf *msg, void *data, u_int16_t datalen) +imsg_add(struct ibuf *msg, const void *data, u_int16_t datalen) { if (datalen) if (ibuf_add(msg, data, datalen) == -1) { @@ -307,7 +310,7 @@ int imsg_flush(struct imsgbuf *ibuf) { while (ibuf->w.queued) - if (msgbuf_write(&ibuf->w) < 0) + if (msgbuf_write(&ibuf->w) <= 0) return (-1); return (0); } diff --git a/openbsd-compat/imsg.h b/openbsd-compat/imsg.h index d691f7d..8994657 100644 --- a/openbsd-compat/imsg.h +++ b/openbsd-compat/imsg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: imsg.h,v 1.2 2010/06/23 07:53:55 nicm Exp $ */ +/* $OpenBSD: imsg.h,v 1.3 2013/12/26 17:32:33 eric Exp $ */ /* * Copyright (c) 2006, 2007 Pierre-Yves Ritschard @@ -98,12 +98,12 @@ void imsg_init(struct imsgbuf *, int); ssize_t imsg_read(struct imsgbuf *); ssize_t imsg_get(struct imsgbuf *, struct imsg *); int imsg_compose(struct imsgbuf *, u_int32_t, u_int32_t, pid_t, - int, void *, u_int16_t); + int, const void *, u_int16_t); int imsg_composev(struct imsgbuf *, u_int32_t, u_int32_t, pid_t, int, const struct iovec *, int); struct ibuf *imsg_create(struct imsgbuf *, u_int32_t, u_int32_t, pid_t, u_int16_t); -int imsg_add(struct ibuf *, void *, u_int16_t); +int imsg_add(struct ibuf *, const void *, u_int16_t); void imsg_close(struct imsgbuf *, struct ibuf *); void imsg_free(struct imsg *); int imsg_flush(struct imsgbuf *); diff --git a/openbsd-compat/openbsd-compat.h b/openbsd-compat/openbsd-compat.h index 26bf649..7829c00 100644 --- a/openbsd-compat/openbsd-compat.h +++ b/openbsd-compat/openbsd-compat.h @@ -72,6 +72,14 @@ # endif #endif +#if !defined(SET_SA_LEN) +# if defined(HAVE_STRUCT_SOCKADDR_SA_LEN) +# define SET_SA_LEN(sa, len) ((sa)->sa_len = (len)) +# else +# define SET_SA_LEN(sa, len) (void)0 +# endif +#endif + /* From OpenBGPD portable */ #if !defined(SS_LEN) # if defined(HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN) @@ -81,18 +89,14 @@ # endif #endif -#ifdef HAVE_SS_LEN -# define STORAGE_LEN(X) ((X).ss_len) -# define SET_STORAGE_LEN(X, Y) do { STORAGE_LEN(X) = (Y); } while(0) -#elif defined(HAVE___SS_LEN) -# define STORAGE_LEN(X) ((X).__ss_len) -# define SET_STORAGE_LEN(X, Y) do { STORAGE_LEN(X) = (Y); } while(0) -#else -# define STORAGE_LEN(X) (STORAGE_FAMILY(X) == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)) -# define SET_STORAGE_LEN(X, Y) (void) 0 +#if !defined(SET_SS_LEN) +# if defined(HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN) +# define SET_SS_LEN(ss, len) ((ss)->ss_len = (len)) +# else +# define SET_SS_LEN(ss, len) SET_SA_LEN((struct sockaddr *)(ss), len) +# endif #endif - #ifndef HAVE_CLOSEFROM void closefrom(int); #endif @@ -177,6 +181,28 @@ int scan_scaled(char *scaled, long long *result); int fmt_scaled(long long number, char *result); #endif -void seed_rng(void); +#ifndef HAVE_REALLOCARRAY +void *reallocarray(void *, size_t , size_t); +#endif + +#ifndef HAVE_EXPLICIT_BZERO +void explicit_bzero(void *buf, size_t len); +#endif + +#ifndef HAVE_PLEDGE +int pledge(const char *promises, const char *paths[]); +#endif + +#ifndef HAVE_ERRC +void errc(int eval, int code, const char *fmt, ...); +#endif + +#ifndef HAVE_WARNC +void warnc(int code, const char *fmt, ...); +#endif + +int bsd_socket(int domain, int type, int protocol); +int bsd_socketpair(int d, int type, int protocol, int sv[2]); +int bsd_accept4(int s, struct sockaddr *addr, socklen_t *addrlen, int flags); #endif /* _OPENBSD_COMPAT_H */ diff --git a/openbsd-compat/reallocarray.c b/openbsd-compat/reallocarray.c new file mode 100644 index 0000000..fe748bb --- /dev/null +++ b/openbsd-compat/reallocarray.c @@ -0,0 +1,40 @@ +/* $OpenBSD: reallocarray.c,v 1.2 2014/12/08 03:45:00 bcook Exp $ */ +/* + * Copyright (c) 2008 Otto Moerbeek + * + * 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. + */ + +#if !defined(HAVE_REALLOCARRAY) +#include +#include +#include +#include + +/* + * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX + * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW + */ +#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) + +void * +reallocarray(void *optr, size_t nmemb, size_t size) +{ + if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + nmemb > 0 && SIZE_MAX / nmemb < size) { + errno = ENOMEM; + return NULL; + } + return realloc(optr, size * nmemb); +} +#endif diff --git a/regress/Makefile b/regress/Makefile new file mode 100644 index 0000000..556e851 --- /dev/null +++ b/regress/Makefile @@ -0,0 +1,5 @@ +# $OpenBSD: Makefile,v 1.1 2014/08/25 19:22:20 reyk Exp $ + +SUBDIR= dh + +.include diff --git a/regress/dh/Makefile b/regress/dh/Makefile new file mode 100644 index 0000000..9afb73d --- /dev/null +++ b/regress/dh/Makefile @@ -0,0 +1,17 @@ +# $OpenBSD: Makefile,v 1.2 2014/08/27 10:28:57 reyk Exp $ + +# Test DH: + +PROG= dhtest +SRCS= dh.c dhtest.c smult_curve25519_ref.c +TOPSRC= ${.CURDIR}/../../iked +TOPOBJ!= cd ${TOPSRC}; printf "all:\n\t@pwd\n" |${MAKE} -f- +.PATH: ${TOPSRC} ${TOPOBJ} +CFLAGS+= -I${TOPSRC} -I${TOPOBJ} -Wall + +NOMAN= +LDADD+= -lcrypto +DPADD+= ${LIBCRYPTO} +DEBUG= -g + +.include diff --git a/regress/dh/dhtest.c b/regress/dh/dhtest.c new file mode 100644 index 0000000..575109e --- /dev/null +++ b/regress/dh/dhtest.c @@ -0,0 +1,82 @@ +/* $OpenBSD: dhtest.c,v 1.2 2014/08/27 10:28:57 reyk Exp $ */ +/* $EOM: dhtest.c,v 1.1 1998/07/18 21:14:20 provos Exp $ */ + +/* + * Copyright (c) 2010 Reyk Floeter + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +/* + * This module does a Diffie-Hellman Exchange + */ + +#include +#include +#include + +#include "dh.h" + +int +main(void) +{ + int len, id; + char buf[DH_MAXSZ], buf2[DH_MAXSZ]; + char sec[DH_MAXSZ], sec2[DH_MAXSZ]; + struct group *group, *group2; + const char *name[] = { "MODP", "EC2N", "ECP", "CURVE25519" }; + + group_init(); + + for (id = 0; id < 0xffff; id++) { + if ((group = group_get(id)) == NULL || + (group2 = group_get(id)) == NULL) + continue; + + len = dh_getlen(group); + + printf ("Testing group %d (%s-%d, length %d): ", id, + name[group->spec->type], + group->spec->bits, len * 8); + + dh_create_exchange(group, buf); + dh_create_exchange(group2, buf2); + + dh_create_shared(group, sec, buf2); + dh_create_shared(group2, sec2, buf); + + if (memcmp (sec, sec2, len)) { + printf("FAILED\n"); + return (1); + } else + printf("OKAY\n"); + + group_free(group); + group_free(group2); + } + + return (0); +}