aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile8
-rw-r--r--compat/Makefile6
-rw-r--r--compat/arc4random.c252
-rw-r--r--compat/arc4random.h89
-rw-r--r--compat/chacha_private.h224
-rw-r--r--compat/getentropy.c96
-rw-r--r--compat/libtls/Makefile27
-rw-r--r--compat/libtls/asn.c177
-rw-r--r--compat/libtls/by_mem.c141
-rw-r--r--compat/libtls/openssl.c212
-rw-r--r--compat/libtls/tls.c903
-rw-r--r--compat/libtls/tls.h219
-rw-r--r--compat/libtls/tls_bio_cb.c169
-rw-r--r--compat/libtls/tls_client.c487
-rw-r--r--compat/libtls/tls_config.c915
-rw-r--r--compat/libtls/tls_conninfo.c346
-rw-r--r--compat/libtls/tls_internal.h300
-rw-r--r--compat/libtls/tls_keypair.c171
-rw-r--r--compat/libtls/tls_ocsp.c465
-rw-r--r--compat/libtls/tls_peer.c101
-rw-r--r--compat/libtls/tls_server.c471
-rw-r--r--compat/libtls/tls_util.c228
-rw-r--r--compat/libtls/tls_verify.c286
-rw-r--r--compat/timingsafe_memcmp.c48
-rwxr-xr-xconfigure147
-rw-r--r--have/ASN1_time_parse.c26
-rw-r--r--have/ASN1_time_tm_cmp.c26
-rw-r--r--have/Makefile11
-rw-r--r--have/SSL_CTX_load_verify_mem.c24
-rw-r--r--have/SSL_CTX_use_certificate_chain_mem.c24
-rw-r--r--have/X509_LOOKUP_mem.c24
-rw-r--r--have/arc4random.c23
-rw-r--r--have/arc4random_buf.c26
-rw-r--r--have/getentropy.c26
-rw-r--r--have/timingsafe_memcmp.c26
-rw-r--r--regress/Makefile4
36 files changed, 6715 insertions, 13 deletions
diff --git a/Makefile b/Makefile
index e4e5dbc..dd6e6d4 100644
--- a/Makefile
+++ b/Makefile
@@ -36,11 +36,11 @@ GEMEXP_SRCS = ge.c config.c crypto.c dirs.c fcgi.c iri.c log.c mime.c \
GEMEXP_OBJS = ${GEMEXP_SRCS:.c=.o} ${COBJS}
-GG_SRCS = gg.c iri.c utf8.c
+GG_SRCS = gg.c iri.c log.c utf8.c
GG_OBJS = ${GG_SRCS:.c=.o} ${COBJS}
-TITAN_SRCS = titan.c iri.c utf8.c
+TITAN_SRCS = titan.c iri.c log.c utf8.c
TITAN_OBJS = ${TITAN_SRCS:.c=.o} ${COBJS}
SRCS = gmid.h log.h parse.y proc.h \
@@ -60,7 +60,9 @@ config.mk config.h: configure
include config.mk
clean:
- rm -f *.[do] compat/*.[do] y.tab.c y.tab.h y.output gmid gemexp gg
+ rm -f gmid gemexp gg
+ rm -f *.[do] compat/*.[do] compat/libtls/*.[do]
+ rm -f y.tab.c y.tab.h y.output
rm -f compile_flags.txt
${MAKE} -C regress clean
diff --git a/compat/Makefile b/compat/Makefile
index 65ec3ef..39fe642 100644
--- a/compat/Makefile
+++ b/compat/Makefile
@@ -1,9 +1,13 @@
DISTFILES = Makefile \
+ arc4random.c \
+ arc4random.h \
+ chacha_private.h \
err.c \
explicit_bzero.c \
freezero.c \
getdtablecount.c \
getdtablesize.c \
+ getentropy.c \
getprogname.c \
imsg-buffer.c \
imsg.c \
@@ -18,6 +22,7 @@ DISTFILES = Makefile \
strlcat.c \
strlcpy.c \
strtonum.c \
+ timingsafe_memcmp.c \
tree.h \
vasprintf.c \
vis.c
@@ -30,6 +35,7 @@ dist: ${DISTFILES}
${INSTALL} -m 0644 ${DISTFILES} ${DESTDIR}/
mkdir -p ${DESTDIR}/vis
${INSTALL} -m 0644 vis/vis.h ${DESTDIR}/vis
+ ${MAKE} -C libtls DESTDIR=${DESTDIR}/libtls dist
.PHONY: all dist
include ../config.mk
diff --git a/compat/arc4random.c b/compat/arc4random.c
new file mode 100644
index 0000000..8ee61b4
--- /dev/null
+++ b/compat/arc4random.c
@@ -0,0 +1,252 @@
+/* $OpenBSD: arc4random.c,v 1.58 2022/07/31 13:41:45 tb Exp $ */
+
+/*
+ * Copyright (c) 1996, David Mazieres <dm@uun.org>
+ * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
+ * Copyright (c) 2013, Markus Friedl <markus@openbsd.org>
+ * Copyright (c) 2014, Theo de Raadt <deraadt@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * ChaCha based random number generator for OpenBSD.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/crypt/arc4random.c */
+
+#include "../config.h"
+
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#ifndef HAVE_ARC4RANDOM
+
+/*
+ * Always use the getentropy implementation from bsd-getentropy.c, which
+ * will call a native getentropy if available then fall back as required.
+ * We use a different name so that OpenSSL cannot call the wrong getentropy.
+ */
+int _ssh_compat_getentropy(void *, size_t);
+#ifdef getentropy
+# undef getentropy
+#endif
+#define getentropy(x, y) (_ssh_compat_getentropy((x), (y)))
+
+#include "log.h"
+
+#define KEYSTREAM_ONLY
+#include "chacha_private.h"
+
+#define minimum(a, b) ((a) < (b) ? (a) : (b))
+
+#if defined(__GNUC__) || defined(_MSC_VER)
+#define inline __inline
+#else /* __GNUC__ || _MSC_VER */
+#define inline
+#endif /* !__GNUC__ && !_MSC_VER */
+
+#define KEYSZ 32
+#define IVSZ 8
+#define BLOCKSZ 64
+#define RSBUFSZ (16*BLOCKSZ)
+
+#define REKEY_BASE (1024*1024) /* NB. should be a power of 2 */
+
+/* Marked MAP_INHERIT_ZERO, so zero'd out in fork children. */
+static struct _rs {
+ size_t rs_have; /* valid bytes at end of rs_buf */
+ size_t rs_count; /* bytes till reseed */
+} *rs;
+
+/* Maybe be preserved in fork children, if _rs_allocate() decides. */
+static struct _rsx {
+ chacha_ctx rs_chacha; /* chacha context for random keystream */
+ u_char rs_buf[RSBUFSZ]; /* keystream blocks */
+} *rsx;
+
+static inline int _rs_allocate(struct _rs **, struct _rsx **);
+static inline void _rs_forkdetect(void);
+#include "arc4random.h"
+
+static inline void _rs_rekey(u_char *dat, size_t datlen);
+
+static inline void
+_rs_init(u_char *buf, size_t n)
+{
+ if (n < KEYSZ + IVSZ)
+ return;
+
+ if (rs == NULL) {
+ if (_rs_allocate(&rs, &rsx) == -1)
+ _exit(1);
+ }
+
+ chacha_keysetup(&rsx->rs_chacha, buf, KEYSZ * 8);
+ chacha_ivsetup(&rsx->rs_chacha, buf + KEYSZ);
+}
+
+static void
+_rs_stir(void)
+{
+ u_char rnd[KEYSZ + IVSZ];
+ uint32_t rekey_fuzz = 0;
+
+ if (getentropy(rnd, sizeof rnd) == -1)
+ _getentropy_fail();
+
+ if (!rs)
+ _rs_init(rnd, sizeof(rnd));
+ else
+ _rs_rekey(rnd, sizeof(rnd));
+ explicit_bzero(rnd, sizeof(rnd)); /* discard source seed */
+
+ /* invalidate rs_buf */
+ rs->rs_have = 0;
+ memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
+
+ /* rekey interval should not be predictable */
+ chacha_encrypt_bytes(&rsx->rs_chacha, (uint8_t *)&rekey_fuzz,
+ (uint8_t *)&rekey_fuzz, sizeof(rekey_fuzz));
+ rs->rs_count = REKEY_BASE + (rekey_fuzz % REKEY_BASE);
+}
+
+static inline void
+_rs_stir_if_needed(size_t len)
+{
+ _rs_forkdetect();
+ if (!rs || rs->rs_count <= len)
+ _rs_stir();
+ if (rs->rs_count <= len)
+ rs->rs_count = 0;
+ else
+ rs->rs_count -= len;
+}
+
+static inline void
+_rs_rekey(u_char *dat, size_t datlen)
+{
+#ifndef KEYSTREAM_ONLY
+ memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
+#endif
+ /* fill rs_buf with the keystream */
+ chacha_encrypt_bytes(&rsx->rs_chacha, rsx->rs_buf,
+ rsx->rs_buf, sizeof(rsx->rs_buf));
+ /* mix in optional user provided data */
+ if (dat) {
+ size_t i, m;
+
+ m = minimum(datlen, KEYSZ + IVSZ);
+ for (i = 0; i < m; i++)
+ rsx->rs_buf[i] ^= dat[i];
+ }
+ /* immediately reinit for backtracking resistance */
+ _rs_init(rsx->rs_buf, KEYSZ + IVSZ);
+ memset(rsx->rs_buf, 0, KEYSZ + IVSZ);
+ rs->rs_have = sizeof(rsx->rs_buf) - KEYSZ - IVSZ;
+}
+
+static inline void
+_rs_random_buf(void *_buf, size_t n)
+{
+ u_char *buf = (u_char *)_buf;
+ u_char *keystream;
+ size_t m;
+
+ _rs_stir_if_needed(n);
+ while (n > 0) {
+ if (rs->rs_have > 0) {
+ m = minimum(n, rs->rs_have);
+ keystream = rsx->rs_buf + sizeof(rsx->rs_buf)
+ - rs->rs_have;
+ memcpy(buf, keystream, m);
+ memset(keystream, 0, m);
+ buf += m;
+ n -= m;
+ rs->rs_have -= m;
+ }
+ if (rs->rs_have == 0)
+ _rs_rekey(NULL, 0);
+ }
+}
+
+static inline void
+_rs_random_u32(uint32_t *val)
+{
+ u_char *keystream;
+
+ _rs_stir_if_needed(sizeof(*val));
+ if (rs->rs_have < sizeof(*val))
+ _rs_rekey(NULL, 0);
+ keystream = rsx->rs_buf + sizeof(rsx->rs_buf) - rs->rs_have;
+ memcpy(val, keystream, sizeof(*val));
+ memset(keystream, 0, sizeof(*val));
+ rs->rs_have -= sizeof(*val);
+}
+
+uint32_t
+arc4random(void)
+{
+ uint32_t val;
+
+ _ARC4_LOCK();
+ _rs_random_u32(&val);
+ _ARC4_UNLOCK();
+ return val;
+}
+
+/*
+ * If we are providing arc4random, then we can provide a more efficient
+ * arc4random_buf().
+ */
+# ifndef HAVE_ARC4RANDOM_BUF
+void
+arc4random_buf(void *buf, size_t n)
+{
+ _ARC4_LOCK();
+ _rs_random_buf(buf, n);
+ _ARC4_UNLOCK();
+}
+# endif /* !HAVE_ARC4RANDOM_BUF */
+#endif /* !HAVE_ARC4RANDOM */
+
+/* arc4random_buf() that uses platform arc4random() */
+#if !defined(HAVE_ARC4RANDOM_BUF) && defined(HAVE_ARC4RANDOM)
+void
+arc4random_buf(void *_buf, size_t n)
+{
+ size_t i;
+ u_int32_t r = 0;
+ char *buf = (char *)_buf;
+
+ for (i = 0; i < n; i++) {
+ if (i % 4 == 0)
+ r = arc4random();
+ buf[i] = r & 0xff;
+ r >>= 8;
+ }
+ explicit_bzero(&r, sizeof(r));
+}
+#endif /* !defined(HAVE_ARC4RANDOM_BUF) && defined(HAVE_ARC4RANDOM) */
+
diff --git a/compat/arc4random.h b/compat/arc4random.h
new file mode 100644
index 0000000..5af3a44
--- /dev/null
+++ b/compat/arc4random.h
@@ -0,0 +1,89 @@
+/* $OpenBSD: arc4random_linux.h,v 1.12 2019/07/11 10:37:28 inoguchi Exp $ */
+
+/*
+ * Copyright (c) 1996, David Mazieres <dm@uun.org>
+ * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
+ * Copyright (c) 2013, Markus Friedl <markus@openbsd.org>
+ * Copyright (c) 2014, Theo de Raadt <deraadt@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Stub functions for portability. From LibreSSL with some adaptations.
+ */
+
+#include <sys/mman.h>
+
+#include <signal.h>
+
+/* OpenSSH isn't multithreaded */
+#define _ARC4_LOCK()
+#define _ARC4_UNLOCK()
+#define _ARC4_ATFORK(f)
+
+static inline void
+_getentropy_fail(void)
+{
+ fatal("getentropy failed");
+}
+
+static volatile sig_atomic_t _rs_forked;
+
+static inline void
+_rs_forkhandler(void)
+{
+ _rs_forked = 1;
+}
+
+static inline void
+_rs_forkdetect(void)
+{
+ static pid_t _rs_pid = 0;
+ pid_t pid = getpid();
+
+ if (_rs_pid == 0 || _rs_pid == 1 || _rs_pid != pid || _rs_forked) {
+ _rs_pid = pid;
+ _rs_forked = 0;
+ if (rs)
+ memset(rs, 0, sizeof(*rs));
+ }
+}
+
+static inline int
+_rs_allocate(struct _rs **rsp, struct _rsx **rsxp)
+{
+#if defined(MAP_ANON) && defined(MAP_PRIVATE)
+ if ((*rsp = mmap(NULL, sizeof(**rsp), PROT_READ|PROT_WRITE,
+ MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED)
+ return (-1);
+
+ if ((*rsxp = mmap(NULL, sizeof(**rsxp), PROT_READ|PROT_WRITE,
+ MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
+ munmap(*rsp, sizeof(**rsp));
+ *rsp = NULL;
+ return (-1);
+ }
+#else
+ if ((*rsp = calloc(1, sizeof(**rsp))) == NULL)
+ return (-1);
+ if ((*rsxp = calloc(1, sizeof(**rsxp))) == NULL) {
+ free(*rsp);
+ *rsp = NULL;
+ return (-1);
+ }
+#endif
+
+ _ARC4_ATFORK(_rs_forkhandler);
+ return (0);
+}
diff --git a/compat/chacha_private.h b/compat/chacha_private.h
new file mode 100644
index 0000000..cdcb785
--- /dev/null
+++ b/compat/chacha_private.h
@@ -0,0 +1,224 @@
+/* OPENBSD ORIGINAL: lib/libc/crypt/chacha_private.h */
+
+/*
+chacha-merged.c version 20080118
+D. J. Bernstein
+Public domain.
+*/
+
+/* $OpenBSD: chacha_private.h,v 1.3 2022/02/28 21:56:29 dtucker Exp $ */
+
+typedef unsigned char u8;
+typedef unsigned int u32;
+
+typedef struct
+{
+ u32 input[16]; /* could be compressed */
+} chacha_ctx;
+
+#define U8C(v) (v##U)
+#define U32C(v) (v##U)
+
+#define U8V(v) ((u8)(v) & U8C(0xFF))
+#define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF))
+
+#define ROTL32(v, n) \
+ (U32V((v) << (n)) | ((v) >> (32 - (n))))
+
+#define U8TO32_LITTLE(p) \
+ (((u32)((p)[0]) ) | \
+ ((u32)((p)[1]) << 8) | \
+ ((u32)((p)[2]) << 16) | \
+ ((u32)((p)[3]) << 24))
+
+#define U32TO8_LITTLE(p, v) \
+ do { \
+ (p)[0] = U8V((v) ); \
+ (p)[1] = U8V((v) >> 8); \
+ (p)[2] = U8V((v) >> 16); \
+ (p)[3] = U8V((v) >> 24); \
+ } while (0)
+
+#define ROTATE(v,c) (ROTL32(v,c))
+#define XOR(v,w) ((v) ^ (w))
+#define PLUS(v,w) (U32V((v) + (w)))
+#define PLUSONE(v) (PLUS((v),1))
+
+#define QUARTERROUND(a,b,c,d) \
+ a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \
+ c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \
+ a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \
+ c = PLUS(c,d); b = ROTATE(XOR(b,c), 7);
+
+static const char sigma[16] = "expand 32-byte k";
+static const char tau[16] = "expand 16-byte k";
+
+static void
+chacha_keysetup(chacha_ctx *x,const u8 *k,u32 kbits)
+{
+ const char *constants;
+
+ x->input[4] = U8TO32_LITTLE(k + 0);
+ x->input[5] = U8TO32_LITTLE(k + 4);
+ x->input[6] = U8TO32_LITTLE(k + 8);
+ x->input[7] = U8TO32_LITTLE(k + 12);
+ if (kbits == 256) { /* recommended */
+ k += 16;
+ constants = sigma;
+ } else { /* kbits == 128 */
+ constants = tau;
+ }
+ x->input[8] = U8TO32_LITTLE(k + 0);
+ x->input[9] = U8TO32_LITTLE(k + 4);
+ x->input[10] = U8TO32_LITTLE(k + 8);
+ x->input[11] = U8TO32_LITTLE(k + 12);
+ x->input[0] = U8TO32_LITTLE(constants + 0);
+ x->input[1] = U8TO32_LITTLE(constants + 4);
+ x->input[2] = U8TO32_LITTLE(constants + 8);
+ x->input[3] = U8TO32_LITTLE(constants + 12);
+}
+
+static void
+chacha_ivsetup(chacha_ctx *x,const u8 *iv)
+{
+ x->input[12] = 0;
+ x->input[13] = 0;
+ x->input[14] = U8TO32_LITTLE(iv + 0);
+ x->input[15] = U8TO32_LITTLE(iv + 4);
+}
+
+static void
+chacha_encrypt_bytes(chacha_ctx *x,const u8 *m,u8 *c,u32 bytes)
+{
+ u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
+ u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
+ u8 *ctarget = NULL;
+ u8 tmp[64];
+ u_int i;
+
+ if (!bytes) return;
+
+ j0 = x->input[0];
+ j1 = x->input[1];
+ j2 = x->input[2];
+ j3 = x->input[3];
+ j4 = x->input[4];
+ j5 = x->input[5];
+ j6 = x->input[6];
+ j7 = x->input[7];
+ j8 = x->input[8];
+ j9 = x->input[9];
+ j10 = x->input[10];
+ j11 = x->input[11];
+ j12 = x->input[12];
+ j13 = x->input[13];
+ j14 = x->input[14];
+ j15 = x->input[15];
+
+ for (;;) {
+ if (bytes < 64) {
+ for (i = 0;i < bytes;++i) tmp[i] = m[i];
+ m = tmp;
+ ctarget = c;
+ c = tmp;
+ }
+ x0 = j0;
+ x1 = j1;
+ x2 = j2;
+ x3 = j3;
+ x4 = j4;
+ x5 = j5;
+ x6 = j6;
+ x7 = j7;
+ x8 = j8;
+ x9 = j9;
+ x10 = j10;
+ x11 = j11;
+ x12 = j12;
+ x13 = j13;
+ x14 = j14;
+ x15 = j15;
+ for (i = 20;i > 0;i -= 2) {
+ QUARTERROUND( x0, x4, x8,x12)
+ QUARTERROUND( x1, x5, x9,x13)
+ QUARTERROUND( x2, x6,x10,x14)
+ QUARTERROUND( x3, x7,x11,x15)
+ QUARTERROUND( x0, x5,x10,x15)
+ QUARTERROUND( x1, x6,x11,x12)
+ QUARTERROUND( x2, x7, x8,x13)
+ QUARTERROUND( x3, x4, x9,x14)
+ }
+ x0 = PLUS(x0,j0);
+ x1 = PLUS(x1,j1);
+ x2 = PLUS(x2,j2);
+ x3 = PLUS(x3,j3);
+ x4 = PLUS(x4,j4);
+ x5 = PLUS(x5,j5);
+ x6 = PLUS(x6,j6);
+ x7 = PLUS(x7,j7);
+ x8 = PLUS(x8,j8);
+ x9 = PLUS(x9,j9);
+ x10 = PLUS(x10,j10);
+ x11 = PLUS(x11,j11);
+ x12 = PLUS(x12,j12);
+ x13 = PLUS(x13,j13);
+ x14 = PLUS(x14,j14);
+ x15 = PLUS(x15,j15);
+
+#ifndef KEYSTREAM_ONLY
+ x0 = XOR(x0,U8TO32_LITTLE(m + 0));
+ x1 = XOR(x1,U8TO32_LITTLE(m + 4));
+ x2 = XOR(x2,U8TO32_LITTLE(m + 8));
+ x3 = XOR(x3,U8TO32_LITTLE(m + 12));
+ x4 = XOR(x4,U8TO32_LITTLE(m + 16));
+ x5 = XOR(x5,U8TO32_LITTLE(m + 20));
+ x6 = XOR(x6,U8TO32_LITTLE(m + 24));
+ x7 = XOR(x7,U8TO32_LITTLE(m + 28));
+ x8 = XOR(x8,U8TO32_LITTLE(m + 32));
+ x9 = XOR(x9,U8TO32_LITTLE(m + 36));
+ x10 = XOR(x10,U8TO32_LITTLE(m + 40));
+ x11 = XOR(x11,U8TO32_LITTLE(m + 44));
+ x12 = XOR(x12,U8TO32_LITTLE(m + 48));
+ x13 = XOR(x13,U8TO32_LITTLE(m + 52));
+ x14 = XOR(x14,U8TO32_LITTLE(m + 56));
+ x15 = XOR(x15,U8TO32_LITTLE(m + 60));
+#endif
+
+ j12 = PLUSONE(j12);
+ if (!j12) {
+ j13 = PLUSONE(j13);
+ /* stopping at 2^70 bytes per nonce is user's responsibility */
+ }
+
+ U32TO8_LITTLE(c + 0,x0);
+ U32TO8_LITTLE(c + 4,x1);
+ U32TO8_LITTLE(c + 8,x2);
+ U32TO8_LITTLE(c + 12,x3);
+ U32TO8_LITTLE(c + 16,x4);
+ U32TO8_LITTLE(c + 20,x5);
+ U32TO8_LITTLE(c + 24,x6);
+ U32TO8_LITTLE(c + 28,x7);
+ U32TO8_LITTLE(c + 32,x8);
+ U32TO8_LITTLE(c + 36,x9);
+ U32TO8_LITTLE(c + 40,x10);
+ U32TO8_LITTLE(c + 44,x11);
+ U32TO8_LITTLE(c + 48,x12);
+ U32TO8_LITTLE(c + 52,x13);
+ U32TO8_LITTLE(c + 56,x14);
+ U32TO8_LITTLE(c + 60,x15);
+
+ if (bytes <= 64) {
+ if (bytes < 64) {
+ for (i = 0;i < bytes;++i) ctarget[i] = c[i];
+ }
+ x->input[12] = j12;
+ x->input[13] = j13;
+ return;
+ }
+ bytes -= 64;
+ c += 64;
+#ifndef KEYSTREAM_ONLY
+ m += 64;
+#endif
+ }
+}
diff --git a/compat/getentropy.c b/compat/getentropy.c
new file mode 100644
index 0000000..dc33884
--- /dev/null
+++ b/compat/getentropy.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 1996, David Mazieres <dm@uun.org>
+ * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
+ * Copyright (c) 2013, Markus Friedl <markus@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "../config.h"
+
+#ifndef SSH_RANDOM_DEV
+# define SSH_RANDOM_DEV "/dev/urandom"
+#endif /* SSH_RANDOM_DEV */
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_RANDOM_H
+# include <sys/random.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef WITH_OPENSSL
+#include <openssl/rand.h>
+#include <openssl/err.h>
+#endif
+
+#include "log.h"
+
+int _ssh_compat_getentropy(void *, size_t);
+
+static int
+seed_from_prngd(unsigned char *buf, size_t bytes)
+{
+ return -1;
+}
+
+int
+_ssh_compat_getentropy(void *s, size_t len)
+{
+#if defined(WITH_OPENSSL) && defined(OPENSSL_PRNG_ONLY)
+ if (RAND_bytes(s, len) <= 0)
+ fatal("Couldn't obtain random bytes (error 0x%lx)",
+ (unsigned long)ERR_get_error());
+#else
+ int fd, save_errno;
+ ssize_t r;
+ size_t o = 0;
+
+#ifdef WITH_OPENSSL
+ if (RAND_bytes(s, len) == 1)
+ return 0;
+#endif
+#ifdef HAVE_GETENTROPY
+ if ((r = getentropy(s, len)) == 0)
+ return 0;
+#endif /* HAVE_GETENTROPY */
+#ifdef HAVE_GETRANDOM
+ if ((r = getrandom(s, len, 0)) > 0 && (size_t)r == len)
+ return 0;
+#endif /* HAVE_GETRANDOM */
+
+ if ((fd = open(SSH_RANDOM_DEV, O_RDONLY)) == -1) {
+ save_errno = errno;
+ /* Try egd/prngd before giving up. */
+ if (seed_from_prngd(s, len) == 0)
+ return 0;
+ fatal("Couldn't open %s: %s", SSH_RANDOM_DEV,
+ strerror(save_errno));
+ }
+ while (o < len) {
+ r = read(fd, (u_char *)s + o, len - o);
+ if (r < 0) {
+ if (errno == EAGAIN || errno == EINTR ||
+ errno == EWOULDBLOCK)
+ continue;
+ fatal("read %s: %s", SSH_RANDOM_DEV, strerror(errno));
+ }
+ o += r;
+ }
+ close(fd);
+#endif /* WITH_OPENSSL */
+ return 0;
+}
diff --git a/compat/libtls/Makefile b/compat/libtls/Makefile
new file mode 100644
index 0000000..4af6e93
--- /dev/null
+++ b/compat/libtls/Makefile
@@ -0,0 +1,27 @@
+DISTFILES = Makefile \
+ asn.c \
+ by_mem.c \
+ openssl.c \
+ tls.c \
+ tls.h \
+ tls_bio_cb.c \
+ tls_client.c \
+ tls_config.c \
+ tls_conninfo.c \
+ tls_internal.h \
+ tls_keypair.c \
+ tls_ocsp.c \
+ tls_peer.c \
+ tls_server.c \
+ tls_util.c \
+ tls_verify.c
+
+all:
+ false
+
+dist: ${DISTFILES}
+ mkdir -p ${DESTDIR}/
+ ${INSTALL} -m 0644 ${DISTFILES} ${DESTDIR}/
+
+.PHONY: all dist
+include ../../config.mk
diff --git a/compat/libtls/asn.c b/compat/libtls/asn.c
new file mode 100644
index 0000000..4bc428e
--- /dev/null
+++ b/compat/libtls/asn.c
@@ -0,0 +1,177 @@
+/* $OpenBSD: a_time_tm.c,v 1.15 2018/04/25 11:48:21 tb Exp $ */
+/*
+ * Copyright (c) 2015 Bob Beck <beck@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#define GENTIME_LENGTH 15
+#define UTCTIME_LENGTH 13
+
+#define V_ASN1_UTCTIME 23
+#define V_ASN1_GENERALIZEDTIME 24
+
+#ifndef HAVE_ASN1_TIME_TM_CMP
+int
+ASN1_time_tm_cmp(struct tm *tm1, struct tm *tm2)
+{
+ if (tm1->tm_year < tm2->tm_year)
+ return (-1);
+ if (tm1->tm_year > tm2->tm_year)
+ return (1);
+ if (tm1->tm_mon < tm2->tm_mon)
+ return (-1);
+ if (tm1->tm_mon > tm2->tm_mon)
+ return (1);
+ if (tm1->tm_mday < tm2->tm_mday)
+ return (-1);
+ if (tm1->tm_mday > tm2->tm_mday)
+ return (1);
+ if (tm1->tm_hour < tm2->tm_hour)
+ return (-1);
+ if (tm1->tm_hour > tm2->tm_hour)
+ return (1);
+ if (tm1->tm_min < tm2->tm_min)
+ return (-1);
+ if (tm1->tm_min > tm2->tm_min)
+ return (1);
+ if (tm1->tm_sec < tm2->tm_sec)
+ return (-1);
+ if (tm1->tm_sec > tm2->tm_sec)
+ return (1);
+ return 0;
+}
+#endif
+
+#ifndef HAVE_ASN1_TIME_TM_CLAMP_NOTAFTER
+int
+ASN1_time_tm_clamp_notafter(struct tm *tm)
+{
+#ifdef SMALL_TIME_T
+ struct tm broken_os_epoch_tm;
+ time_t broken_os_epoch_time = INT_MAX;
+
+ if (gmtime_r(&broken_os_epoch_time, &broken_os_epoch_tm) == NULL)
+ return 0;
+
+ if (ASN1_time_tm_cmp(tm, &broken_os_epoch_tm) == 1)
+ memcpy(tm, &broken_os_epoch_tm, sizeof(*tm));
+#endif
+ return 1;
+}
+#endif
+
+/*
+ * Parse an RFC 5280 format ASN.1 time string.
+ *
+ * mode must be:
+ * 0 if we expect to parse a time as specified in RFC 5280 for an X509 object.
+ * V_ASN1_UTCTIME if we wish to parse an RFC5280 format UTC time.
+ * V_ASN1_GENERALIZEDTIME if we wish to parse an RFC5280 format Generalized time.
+ *
+ * Returns:
+ * -1 if the string was invalid.
+ * V_ASN1_UTCTIME if the string validated as a UTC time string.
+ * V_ASN1_GENERALIZEDTIME if the string validated as a Generalized time string.
+ *
+ * Fills in *tm with the corresponding time if tm is non NULL.
+ */
+#ifndef HAVE_ASN1_TIME_PARSE
+#define ATOI2(ar) ((ar) += 2, ((ar)[-2] - '0') * 10 + ((ar)[-1] - '0'))
+int
+ASN1_time_parse(const char *bytes, size_t len, struct tm *tm, int mode)
+{
+ size_t i;
+ int type = 0;
+ struct tm ltm;
+ struct tm *lt;
+ const char *p;
+
+ if (bytes == NULL)
+ return (-1);
+
+ /* Constrain to valid lengths. */
+ if (len != UTCTIME_LENGTH && len != GENTIME_LENGTH)
+ return (-1);
+
+ lt = tm;
+ if (lt == NULL) {
+ memset(&ltm, 0, sizeof(ltm));
+ lt = &ltm;
+ }
+
+ /* Timezone is required and must be GMT (Zulu). */
+ if (bytes[len - 1] != 'Z')
+ return (-1);
+
+ /* Make sure everything else is digits. */
+ for (i = 0; i < len - 1; i++) {
+ if (isdigit((unsigned char)bytes[i]))
+ continue;
+ return (-1);
+ }
+
+ /*
+ * Validate and convert the time
+ */
+ p = bytes;
+ switch (len) {
+ case GENTIME_LENGTH:
+ if (mode == V_ASN1_UTCTIME)
+ return (-1);
+ lt->tm_year = (ATOI2(p) * 100) - 1900; /* cc */
+ type = V_ASN1_GENERALIZEDTIME;
+ /* FALLTHROUGH */
+ case UTCTIME_LENGTH:
+ if (type == 0) {
+ if (mode == V_ASN1_GENERALIZEDTIME)
+ return (-1);
+ type = V_ASN1_UTCTIME;
+ }
+ lt->tm_year += ATOI2(p); /* yy */
+ if (type == V_ASN1_UTCTIME) {
+ if (lt->tm_year < 50)
+ lt->tm_year += 100;
+ }
+ lt->tm_mon = ATOI2(p) - 1; /* mm */
+ if (lt->tm_mon < 0 || lt->tm_mon > 11)
+ return (-1);
+ lt->tm_mday = ATOI2(p); /* dd */
+ if (lt->tm_mday < 1 || lt->tm_mday > 31)
+ return (-1);
+ lt->tm_hour = ATOI2(p); /* HH */
+ if (lt->tm_hour < 0 || lt->tm_hour > 23)
+ return (-1);
+ lt->tm_min = ATOI2(p); /* MM */
+ if (lt->tm_min < 0 || lt->tm_min > 59)
+ return (-1);
+ lt->tm_sec = ATOI2(p); /* SS */
+ /* Leap second 60 is not accepted. Reconsider later? */
+ if (lt->tm_sec < 0 || lt->tm_sec > 59)
+ return (-1);
+ break;
+ default:
+ return (-1);
+ }
+
+ return (type);
+}
+#endif
diff --git a/compat/libtls/by_mem.c b/compat/libtls/by_mem.c
new file mode 100644
index 0000000..e290a0c
--- /dev/null
+++ b/compat/libtls/by_mem.c
@@ -0,0 +1,141 @@
+/* $OpenBSD: by_mem.c,v 1.4 2017/01/29 17:49:23 beck Exp $ */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * 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 copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+#include "config.h"
+
+#ifndef HAVE_X509_LOOKUP_MEM
+
+#include <sys/uio.h>
+#include <errno.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <openssl/buffer.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/lhash.h>
+#include <openssl/x509.h>
+
+#define X509error(r) ERR_PUT_error(ERR_LIB_X509,(0xfff),(r),__FILE__,__LINE__)
+
+#define X509_L_MEM 3
+
+static int by_mem_ctrl(X509_LOOKUP *, int, const char *, long, char **);
+
+static X509_LOOKUP_METHOD *x509_mem_lookup;
+
+X509_LOOKUP_METHOD *
+X509_LOOKUP_mem(void)
+{
+ if (x509_mem_lookup == NULL) {
+ x509_mem_lookup = X509_LOOKUP_meth_new("Load cert from memory");
+ X509_LOOKUP_meth_set_ctrl(x509_mem_lookup, by_mem_ctrl);
+ }
+ return (x509_mem_lookup);
+}
+
+static int
+by_mem_ctrl(X509_LOOKUP *lu, int cmd, const char *buf,
+ long type, char **ret)
+{
+ STACK_OF(X509_INFO) *inf = NULL;
+ const struct iovec *iov;
+ X509_INFO *itmp;
+ BIO *in = NULL;
+ int i, count = 0, ok = 0;
+
+ iov = (const struct iovec *)buf;
+
+ if (!(cmd == X509_L_MEM && type == X509_FILETYPE_PEM))
+ goto done;
+
+ if ((in = BIO_new_mem_buf(iov->iov_base, iov->iov_len)) == NULL)
+ goto done;
+
+ if ((inf = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL)) == NULL)
+ goto done;
+
+ for (i = 0; i < sk_X509_INFO_num(inf); i++) {
+ itmp = sk_X509_INFO_value(inf, i);
+ if (itmp->x509) {
+ ok = X509_STORE_add_cert(X509_LOOKUP_get_store(lu), itmp->x509);
+ if (!ok)
+ goto done;
+ count++;
+ }
+ if (itmp->crl) {
+ ok = X509_STORE_add_crl(X509_LOOKUP_get_store(lu), itmp->crl);
+ if (!ok)
+ goto done;
+ count++;
+ }
+ }
+
+ ok = count != 0;
+ done:
+ if (count == 0)
+ X509error(ERR_R_PEM_LIB);
+ if (inf != NULL)
+ sk_X509_INFO_pop_free(inf, X509_INFO_free);
+ if (in != NULL)
+ BIO_free(in);
+ return (ok);
+}
+
+#endif /* HAVE_X509_LOOKUP_MEM */
diff --git a/compat/libtls/openssl.c b/compat/libtls/openssl.c
new file mode 100644
index 0000000..3528887
--- /dev/null
+++ b/compat/libtls/openssl.c
@@ -0,0 +1,212 @@
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <limits.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include <openssl/err.h>
+#include <openssl/bio.h>
+#include <openssl/objects.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include <openssl/x509_vfy.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+
+#include "log.h"
+
+#ifndef HAVE_SSL_CTX_LOAD_VERIFY_MEM
+
+#define X509_LOOKUP_add_mem(x,iov,type) \
+ X509_LOOKUP_ctrl((x),X509_L_MEM,(const char *)(iov),\
+ (long)(type),NULL)
+
+#define X509_L_MEM 3
+
+#define SSL_ECDH_CURVE "prime256v1"
+
+X509_LOOKUP_METHOD *
+X509_LOOKUP_mem(void);
+
+static int
+X509_STORE_load_mem(X509_STORE *ctx, void *buf, int len)
+{
+ X509_LOOKUP *lookup;
+ struct iovec iov;
+ lookup = X509_STORE_add_lookup(ctx, X509_LOOKUP_mem());
+ if (lookup == NULL)
+ return (0);
+ iov.iov_base = buf;
+ iov.iov_len = len;
+ if (X509_LOOKUP_add_mem(lookup, &iov, X509_FILETYPE_PEM) != 1)
+ return (0);
+ return (1);
+}
+
+int
+SSL_CTX_load_verify_mem(SSL_CTX *ctx, void *buf, int len)
+{
+ return (X509_STORE_load_mem(SSL_CTX_get_cert_store(ctx), buf, len));
+}
+
+#endif /* HAVE_SSL_CTX_LOAD_VERIFY_MEM */
+
+#ifndef HAVE_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * 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 copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+/*
+ * SSL operations needed when running in a privilege separated environment.
+ * Adapted from openssl's ssl_rsa.c by Pierre-Yves Ritschard .
+ */
+
+/*
+ * Read a bio that contains our certificate in "PEM" format,
+ * possibly followed by a sequence of CA certificates that should be
+ * sent to the peer in the Certificate message.
+ */
+static int
+ssl_ctx_use_certificate_chain_bio(SSL_CTX *ctx, BIO *in)
+{
+ int ret = 0;
+ X509 *x = NULL;
+
+ ERR_clear_error(); /* clear error stack for SSL_CTX_use_certificate() */
+
+ x = PEM_read_bio_X509_AUX(in, NULL, SSL_CTX_get_default_passwd_cb(ctx),
+ SSL_CTX_get_default_passwd_cb_userdata(ctx));
+ if (x == NULL) {
+ SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_PEM_LIB);
+ goto end;
+ }
+
+ ret = SSL_CTX_use_certificate(ctx, x);
+
+ if (ERR_peek_error() != 0)
+ ret = 0;
+ /* Key/certificate mismatch doesn't imply ret==0 ... */
+ if (ret) {
+ /*
+ * If we could set up our certificate, now proceed to
+ * the CA certificates.
+ */
+ X509 *ca;
+ STACK_OF(X509) *chain;
+ int r;
+ unsigned long err;
+
+ SSL_CTX_get_extra_chain_certs_only(ctx, &chain);
+ if (chain != NULL) {
+ sk_X509_pop_free(chain, X509_free);
+ SSL_CTX_clear_extra_chain_certs(ctx);
+ }
+
+ while ((ca = PEM_read_bio_X509(in, NULL,
+ SSL_CTX_get_default_passwd_cb(ctx),
+ SSL_CTX_get_default_passwd_cb_userdata(ctx))) != NULL) {
+ r = SSL_CTX_add_extra_chain_cert(ctx, ca);
+ if (!r) {
+ X509_free(ca);
+ ret = 0;
+ goto end;
+ }
+ /*
+ * Note that we must not free r if it was successfully
+ * added to the chain (while we must free the main
+ * certificate, since its reference count is increased
+ * by SSL_CTX_use_certificate).
+ */
+ }
+
+ /* When the while loop ends, it's usually just EOF. */
+ err = ERR_peek_last_error();
+ if (ERR_GET_LIB(err) == ERR_LIB_PEM &&
+ ERR_GET_REASON(err) == PEM_R_NO_START_LINE)
+ ERR_clear_error();
+ else
+ ret = 0; /* some real error */
+ }
+
+end:
+ if (x != NULL)
+ X509_free(x);
+ return (ret);
+}
+
+int
+SSL_CTX_use_certificate_chain_mem(SSL_CTX *ctx, void *buf, int len)
+{
+ BIO *in;
+ int ret = 0;
+
+ in = BIO_new_mem_buf(buf, len);
+ if (in == NULL) {
+ SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_BUF_LIB);
+ goto end;
+ }
+
+ ret = ssl_ctx_use_certificate_chain_bio(ctx, in);
+
+end:
+ BIO_free(in);
+ return (ret);
+}
+
+#endif /* HAVE_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM */
diff --git a/compat/libtls/tls.c b/compat/libtls/tls.c
new file mode 100644
index 0000000..0daabf5
--- /dev/null
+++ b/compat/libtls/tls.c
@@ -0,0 +1,903 @@
+/* $OpenBSD: tls.c,v 1.97 2023/06/18 11:43:03 op Exp $ */
+/*
+ * Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <sys/socket.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/safestack.h>
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+
+#include <tls.h>
+#include "tls_internal.h"
+
+static struct tls_config *tls_config_default;
+
+static int tls_init_rv = -1;
+
+static void
+tls_do_init(void)
+{
+ OPENSSL_init_ssl(OPENSSL_INIT_NO_LOAD_CONFIG, NULL);
+
+ if (BIO_sock_init() != 1)
+ return;
+
+ if ((tls_config_default = tls_config_new_internal()) == NULL)
+ return;
+
+ tls_config_default->refcount++;
+
+ tls_init_rv = 0;
+}
+
+int
+tls_init(void)
+{
+ if (tls_init_rv == -1)
+ tls_do_init();
+ return tls_init_rv;
+}
+
+const char *
+tls_error(struct tls *ctx)
+{
+ return ctx->error.msg;
+}
+
+void
+tls_error_clear(struct tls_error *error)
+{
+ free(error->msg);
+ error->msg = NULL;
+ error->num = 0;
+ error->tls = 0;
+}
+
+static int
+tls_error_vset(struct tls_error *error, int errnum, const char *fmt, va_list ap)
+{
+ char *errmsg = NULL;
+ int rv = -1;
+
+ tls_error_clear(error);
+
+ error->num = errnum;
+ error->tls = 1;
+
+ if (vasprintf(&errmsg, fmt, ap) == -1) {
+ errmsg = NULL;
+ goto err;
+ }
+
+ if (errnum == -1) {
+ error->msg = errmsg;
+ return (0);
+ }
+
+ if (asprintf(&error->msg, "%s: %s", errmsg, strerror(errnum)) == -1) {
+ error->msg = NULL;
+ goto err;
+ }
+ rv = 0;
+
+ err:
+ free(errmsg);
+
+ return (rv);
+}
+
+int
+tls_error_set(struct tls_error *error, const char *fmt, ...)
+{
+ va_list ap;
+ int errnum, rv;
+
+ errnum = errno;
+
+ va_start(ap, fmt);
+ rv = tls_error_vset(error, errnum, fmt, ap);
+ va_end(ap);
+
+ return (rv);
+}
+
+int
+tls_error_setx(struct tls_error *error, const char *fmt, ...)
+{
+ va_list ap;
+ int rv;
+
+ va_start(ap, fmt);
+ rv = tls_error_vset(error, -1, fmt, ap);
+ va_end(ap);
+
+ return (rv);
+}
+
+int
+tls_config_set_error(struct tls_config *config, const char *fmt, ...)
+{
+ va_list ap;
+ int errnum, rv;
+
+ errnum = errno;
+
+ va_start(ap, fmt);
+ rv = tls_error_vset(&config->error, errnum, fmt, ap);
+ va_end(ap);
+
+ return (rv);
+}
+
+int
+tls_config_set_errorx(struct tls_config *config, const char *fmt, ...)
+{
+ va_list ap;
+ int rv;
+
+ va_start(ap, fmt);
+ rv = tls_error_vset(&config->error, -1, fmt, ap);
+ va_end(ap);
+
+ return (rv);
+}
+
+int
+tls_set_error(struct tls *ctx, const char *fmt, ...)
+{
+ va_list ap;
+ int errnum, rv;
+
+ errnum = errno;
+
+ va_start(ap, fmt);
+ rv = tls_error_vset(&ctx->error, errnum, fmt, ap);
+ va_end(ap);
+
+ return (rv);
+}
+
+int
+tls_set_errorx(struct tls *ctx, const char *fmt, ...)
+{
+ va_list ap;
+ int rv;
+
+ va_start(ap, fmt);
+ rv = tls_error_vset(&ctx->error, -1, fmt, ap);
+ va_end(ap);
+
+ return (rv);
+}
+
+int
+tls_set_ssl_errorx(struct tls *ctx, const char *fmt, ...)
+{
+ va_list ap;
+ int rv;
+
+ /* Only set an error if a more specific one does not already exist. */
+ if (ctx->error.tls != 0)
+ return (0);
+
+ va_start(ap, fmt);
+ rv = tls_error_vset(&ctx->error, -1, fmt, ap);
+ va_end(ap);
+
+ return (rv);
+}
+
+struct tls_sni_ctx *
+tls_sni_ctx_new(void)
+{
+ return (calloc(1, sizeof(struct tls_sni_ctx)));
+}
+
+void
+tls_sni_ctx_free(struct tls_sni_ctx *sni_ctx)
+{
+ if (sni_ctx == NULL)
+ return;
+
+ SSL_CTX_free(sni_ctx->ssl_ctx);
+ X509_free(sni_ctx->ssl_cert);
+
+ free(sni_ctx);
+}
+
+struct tls *
+tls_new(void)
+{
+ struct tls *ctx;
+
+ if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
+ return (NULL);
+
+ tls_reset(ctx);
+
+ if (tls_configure(ctx, tls_config_default) == -1) {
+ free(ctx);
+ return NULL;
+ }
+
+ return (ctx);
+}
+
+int
+tls_configure(struct tls *ctx, struct tls_config *config)
+{
+ if (config == NULL)
+ config = tls_config_default;
+
+ config->refcount++;
+
+ tls_config_free(ctx->config);
+
+ ctx->config = config;
+ ctx->keypair = config->keypair;
+
+ if ((ctx->flags & TLS_SERVER) != 0)
+ return (tls_configure_server(ctx));
+
+ return (0);
+}
+
+int
+tls_cert_hash(X509 *cert, char **hash)
+{
+ char d[EVP_MAX_MD_SIZE], *dhex = NULL;
+ int dlen, rv = -1;
+
+ free(*hash);
+ *hash = NULL;
+
+ if (X509_digest(cert, EVP_sha256(), d, &dlen) != 1)
+ goto err;
+
+ if (tls_hex_string(d, dlen, &dhex, NULL) != 0)
+ goto err;
+
+ if (asprintf(hash, "SHA256:%s", dhex) == -1) {
+ *hash = NULL;
+ goto err;
+ }
+
+ rv = 0;
+ err:
+ free(dhex);
+
+ return (rv);
+}
+
+int
+tls_cert_pubkey_hash(X509 *cert, char **hash)
+{
+ char d[EVP_MAX_MD_SIZE], *dhex = NULL;
+ int dlen, rv = -1;
+
+ free(*hash);
+ *hash = NULL;
+
+ if (X509_pubkey_digest(cert, EVP_sha256(), d, &dlen) != 1)
+ goto err;
+
+ if (tls_hex_string(d, dlen, &dhex, NULL) != 0)
+ goto err;
+
+ if (asprintf(hash, "SHA256:%s", dhex) == -1) {
+ *hash = NULL;
+ goto err;
+ }
+
+ rv = 0;
+
+ err:
+ free(dhex);
+
+ return (rv);
+}
+
+static int
+tls_keypair_to_pkey(struct tls *ctx, struct tls_keypair *keypair, EVP_PKEY **pkey)
+{
+ BIO *bio = NULL;
+ X509 *x509 = NULL;
+ char *mem;
+ size_t len;
+ int ret = -1;
+
+ *pkey = NULL;
+
+ if (ctx->config->use_fake_private_key) {
+ mem = keypair->cert_mem;
+ len = keypair->cert_len;
+ } else {
+ mem = keypair->key_mem;
+ len = keypair->key_len;
+ }
+
+ if (mem == NULL)
+ return (0);
+
+ if (len > INT_MAX) {
+ tls_set_errorx(ctx, ctx->config->use_fake_private_key ?
+ "cert too long" : "key too long");
+ goto err;
+ }
+
+ if ((bio = BIO_new_mem_buf(mem, len)) == NULL) {
+ tls_set_errorx(ctx, "failed to create buffer");
+ goto err;
+ }
+
+ if (ctx->config->use_fake_private_key) {
+ if ((x509 = PEM_read_bio_X509(bio, NULL, tls_password_cb,
+ NULL)) == NULL) {
+ tls_set_errorx(ctx, "failed to read X509 certificate");
+ goto err;
+ }
+ if ((*pkey = X509_get_pubkey(x509)) == NULL) {
+ tls_set_errorx(ctx, "failed to retrieve pubkey");
+ goto err;
+ }
+ } else {
+ if ((*pkey = PEM_read_bio_PrivateKey(bio, NULL, tls_password_cb,
+ NULL)) == NULL) {
+ tls_set_errorx(ctx, "failed to read private key");
+ goto err;
+ }
+ }
+
+ ret = 0;
+ err:
+ BIO_free(bio);
+ X509_free(x509);
+ return (ret);
+}
+
+static int
+tls_keypair_setup_pkey(struct tls *ctx, struct tls_keypair *keypair, EVP_PKEY *pkey)
+{
+ RSA *rsa = NULL;
+ EC_KEY *eckey = NULL;
+ int ret = -1;
+
+ /* Only install the pubkey hash if fake private keys are used. */
+ if (!ctx->config->skip_private_key_check)
+ return (0);
+
+ if (keypair->pubkey_hash == NULL) {
+ tls_set_errorx(ctx, "public key hash not set");
+ goto err;
+ }
+
+ switch (EVP_PKEY_id(pkey)) {
+ case EVP_PKEY_RSA:
+ if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL ||
+ RSA_set_ex_data(rsa, 0, keypair->pubkey_hash) == 0 ||
+ EVP_PKEY_set1_RSA(pkey, rsa) == 0) {
+ tls_set_errorx(ctx, "RSA key setup failure");
+ goto err;
+ }
+ break;
+ case EVP_PKEY_EC:
+ if ((eckey = EVP_PKEY_get1_EC_KEY(pkey)) == NULL ||
+ EC_KEY_set_ex_data(eckey, 0, keypair->pubkey_hash) == 0 ||
+ EVP_PKEY_set1_EC_KEY(pkey, eckey) == 0) {
+ tls_set_errorx(ctx, "EC key setup failure");
+ goto err;
+ }
+ break;
+ default:
+ tls_set_errorx(ctx, "incorrect key type");
+ goto err;
+ }
+
+ ret = 0;
+
+ err:
+ RSA_free(rsa);
+ EC_KEY_free(eckey);
+ return (ret);
+}
+
+int
+tls_configure_ssl_keypair(struct tls *ctx, SSL_CTX *ssl_ctx,
+ struct tls_keypair *keypair, int required)
+{
+ EVP_PKEY *pkey = NULL;
+
+ if (!required &&
+ keypair->cert_mem == NULL &&
+ keypair->key_mem == NULL)
+ return(0);
+
+ if (keypair->cert_mem != NULL) {
+ if (keypair->cert_len > INT_MAX) {
+ tls_set_errorx(ctx, "certificate too long");
+ goto err;
+ }
+
+ if (SSL_CTX_use_certificate_chain_mem(ssl_ctx,
+ keypair->cert_mem, keypair->cert_len) != 1) {
+ tls_set_errorx(ctx, "failed to load certificate");
+ goto err;
+ }
+ }
+
+ if (tls_keypair_to_pkey(ctx, keypair, &pkey) == -1)
+ goto err;
+ if (pkey != NULL) {
+ if (tls_keypair_setup_pkey(ctx, keypair, pkey) == -1)
+ goto err;
+ if (SSL_CTX_use_PrivateKey(ssl_ctx, pkey) != 1) {
+ tls_set_errorx(ctx, "failed to load private key");
+ goto err;
+ }
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ }
+
+ if (!ctx->config->skip_private_key_check &&
+ SSL_CTX_check_private_key(ssl_ctx) != 1) {
+ tls_set_errorx(ctx, "private/public key mismatch");
+ goto err;
+ }
+
+ return (0);
+
+ err:
+ EVP_PKEY_free(pkey);
+
+ return (-1);
+}
+
+int
+tls_configure_ssl(struct tls *ctx, SSL_CTX *ssl_ctx)
+{
+ SSL_CTX_clear_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
+
+ SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
+ SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+ SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2);
+ SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv3);
+
+ SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TLSv1);
+ SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TLSv1_1);
+ SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TLSv1_2);
+ SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TLSv1_3);
+
+ if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_0) == 0)
+ SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1);
+ if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_1) == 0)
+ SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_1);
+ if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_2) == 0)
+ SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_2);
+ if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_3) == 0)
+ SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_3);
+
+ if (ctx->config->alpn != NULL) {
+ if (SSL_CTX_set_alpn_protos(ssl_ctx, ctx->config->alpn,
+ ctx->config->alpn_len) != 0) {
+ tls_set_errorx(ctx, "failed to set alpn");
+ goto err;
+ }
+ }
+
+ if (ctx->config->ciphers != NULL) {
+ if (SSL_CTX_set_cipher_list(ssl_ctx,
+ ctx->config->ciphers) != 1) {
+ tls_set_errorx(ctx, "failed to set ciphers");
+ goto err;
+ }
+ }
+
+ if (ctx->config->verify_time == 0) {
+ X509_VERIFY_PARAM_set_flags(SSL_CTX_get0_param(ssl_ctx),
+ X509_V_FLAG_NO_CHECK_TIME);
+ }
+
+ /* Disable any form of session caching by default */
+ SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_OFF);
+ SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET);
+
+ return (0);
+
+ err:
+ return (-1);
+}
+
+static int
+tls_ssl_cert_verify_cb(X509_STORE_CTX *x509_ctx, void *arg)
+{
+ struct tls *ctx = arg;
+ int x509_err;
+
+ if (ctx->config->verify_cert == 0)
+ return (1);
+
+ if ((X509_verify_cert(x509_ctx)) < 0) {
+ tls_set_errorx(ctx, "X509 verify cert failed");
+ return (0);
+ }
+
+ x509_err = X509_STORE_CTX_get_error(x509_ctx);
+ if (x509_err == X509_V_OK)
+ return (1);
+
+ tls_set_errorx(ctx, "certificate verification failed: %s",
+ X509_verify_cert_error_string(x509_err));
+
+ return (0);
+}
+
+int
+tls_configure_ssl_verify(struct tls *ctx, SSL_CTX *ssl_ctx, int verify)
+{
+ size_t ca_len = ctx->config->ca_len;
+ char *ca_mem = ctx->config->ca_mem;
+ char *crl_mem = ctx->config->crl_mem;
+ size_t crl_len = ctx->config->crl_len;
+ char *ca_free = NULL;
+ STACK_OF(X509_INFO) *xis = NULL;
+ X509_STORE *store;
+ X509_INFO *xi;
+ BIO *bio = NULL;
+ int rv = -1;
+ int i;
+
+ SSL_CTX_set_verify(ssl_ctx, verify, NULL);
+ SSL_CTX_set_cert_verify_callback(ssl_ctx, tls_ssl_cert_verify_cb, ctx);
+
+ if (ctx->config->verify_depth >= 0)
+ SSL_CTX_set_verify_depth(ssl_ctx, ctx->config->verify_depth);
+
+ if (ctx->config->verify_cert == 0)
+ goto done;
+
+ /* If no CA has been specified, attempt to load the default. */
+ if (ctx->config->ca_mem == NULL && ctx->config->ca_path == NULL) {
+ if (tls_config_load_file(&ctx->error, "CA", tls_default_ca_cert_file(),
+ &ca_mem, &ca_len) != 0)
+ goto err;
+ ca_free = ca_mem;
+ }
+
+ if (ca_mem != NULL) {
+ if (ca_len > INT_MAX) {
+ tls_set_errorx(ctx, "ca too long");
+ goto err;
+ }
+ if (SSL_CTX_load_verify_mem(ssl_ctx, ca_mem, ca_len) != 1) {
+ tls_set_errorx(ctx, "ssl verify memory setup failure");
+ goto err;
+ }
+ } else if (SSL_CTX_load_verify_locations(ssl_ctx, NULL,
+ ctx->config->ca_path) != 1) {
+ tls_set_errorx(ctx, "ssl verify locations failure");
+ goto err;
+ }
+
+ if (crl_mem != NULL) {
+ if (crl_len > INT_MAX) {
+ tls_set_errorx(ctx, "crl too long");
+ goto err;
+ }
+ if ((bio = BIO_new_mem_buf(crl_mem, crl_len)) == NULL) {
+ tls_set_errorx(ctx, "failed to create buffer");
+ goto err;
+ }
+ if ((xis = PEM_X509_INFO_read_bio(bio, NULL, tls_password_cb,
+ NULL)) == NULL) {
+ tls_set_errorx(ctx, "failed to parse crl");
+ goto err;
+ }
+ store = SSL_CTX_get_cert_store(ssl_ctx);
+ for (i = 0; i < sk_X509_INFO_num(xis); i++) {
+ xi = sk_X509_INFO_value(xis, i);
+ if (xi->crl == NULL)
+ continue;
+ if (!X509_STORE_add_crl(store, xi->crl)) {
+ tls_set_error(ctx, "failed to add crl");
+ goto err;
+ }
+ }
+ X509_STORE_set_flags(store,
+ X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
+ }
+
+ done:
+ rv = 0;
+
+ err:
+ sk_X509_INFO_pop_free(xis, X509_INFO_free);
+ BIO_free(bio);
+ free(ca_free);
+
+ return (rv);
+}
+
+void
+tls_free(struct tls *ctx)
+{
+ if (ctx == NULL)
+ return;
+
+ tls_reset(ctx);
+
+ free(ctx);
+}
+
+void
+tls_reset(struct tls *ctx)
+{
+ struct tls_sni_ctx *sni, *nsni;
+
+ tls_config_free(ctx->config);
+ ctx->config = NULL;
+
+ SSL_CTX_free(ctx->ssl_ctx);
+ SSL_free(ctx->ssl_conn);
+ X509_free(ctx->ssl_peer_cert);
+
+ ctx->ssl_conn = NULL;
+ ctx->ssl_ctx = NULL;
+ ctx->ssl_peer_cert = NULL;
+ /* X509 objects in chain are freed with the SSL */
+ ctx->ssl_peer_chain = NULL;
+
+ ctx->socket = -1;
+ ctx->state = 0;
+
+ free(ctx->servername);
+ ctx->servername = NULL;
+
+ free(ctx->error.msg);
+ ctx->error.msg = NULL;
+ ctx->error.num = -1;
+
+ tls_conninfo_free(ctx->conninfo);
+ ctx->conninfo = NULL;
+
+ tls_ocsp_free(ctx->ocsp);
+ ctx->ocsp = NULL;
+
+ for (sni = ctx->sni_ctx; sni != NULL; sni = nsni) {
+ nsni = sni->next;
+ tls_sni_ctx_free(sni);
+ }
+ ctx->sni_ctx = NULL;
+
+ ctx->read_cb = NULL;
+ ctx->write_cb = NULL;
+ ctx->cb_arg = NULL;
+}
+
+int
+tls_ssl_error(struct tls *ctx, SSL *ssl_conn, int ssl_ret, const char *prefix)
+{
+ const char *errstr = "unknown error";
+ unsigned long err;
+ int ssl_err;
+
+ ssl_err = SSL_get_error(ssl_conn, ssl_ret);
+ switch (ssl_err) {
+ case SSL_ERROR_NONE:
+ case SSL_ERROR_ZERO_RETURN:
+ return (0);
+
+ case SSL_ERROR_WANT_READ:
+ return (TLS_WANT_POLLIN);
+
+ case SSL_ERROR_WANT_WRITE:
+ return (TLS_WANT_POLLOUT);
+
+ case SSL_ERROR_SYSCALL:
+ if ((err = ERR_peek_error()) != 0) {
+ errstr = ERR_error_string(err, NULL);
+ } else if (ssl_ret == 0) {
+ if ((ctx->state & TLS_HANDSHAKE_COMPLETE) != 0) {
+ ctx->state |= TLS_EOF_NO_CLOSE_NOTIFY;
+ return (0);
+ }
+ errstr = "unexpected EOF";
+ } else if (ssl_ret == -1) {
+ errstr = strerror(errno);
+ }
+ tls_set_ssl_errorx(ctx, "%s failed: %s", prefix, errstr);
+ return (-1);
+
+ case SSL_ERROR_SSL:
+ if ((err = ERR_peek_error()) != 0) {
+ errstr = ERR_error_string(err, NULL);
+ }
+ tls_set_ssl_errorx(ctx, "%s failed: %s", prefix, errstr);
+ return (-1);
+
+ case SSL_ERROR_WANT_CONNECT:
+ case SSL_ERROR_WANT_ACCEPT:
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ default:
+ tls_set_ssl_errorx(ctx, "%s failed (%d)", prefix, ssl_err);
+ return (-1);
+ }
+}
+
+int
+tls_handshake(struct tls *ctx)
+{
+ int rv = -1;
+
+ tls_error_clear(&ctx->error);
+
+ if ((ctx->flags & (TLS_CLIENT | TLS_SERVER_CONN)) == 0) {
+ tls_set_errorx(ctx, "invalid operation for context");
+ goto out;
+ }
+
+ if ((ctx->state & TLS_HANDSHAKE_COMPLETE) != 0) {
+ tls_set_errorx(ctx, "handshake already completed");
+ goto out;
+ }
+
+ if ((ctx->flags & TLS_CLIENT) != 0)
+ rv = tls_handshake_client(ctx);
+ else if ((ctx->flags & TLS_SERVER_CONN) != 0)
+ rv = tls_handshake_server(ctx);
+
+ if (rv == 0) {
+ ctx->ssl_peer_cert = SSL_get_peer_certificate(ctx->ssl_conn);
+ ctx->ssl_peer_chain = SSL_get_peer_cert_chain(ctx->ssl_conn);
+ if (tls_conninfo_populate(ctx) == -1)
+ rv = -1;
+ if (ctx->ocsp == NULL)
+ ctx->ocsp = tls_ocsp_setup_from_peer(ctx);
+ }
+ out:
+ /* Prevent callers from performing incorrect error handling */
+ errno = 0;
+ return (rv);
+}
+
+ssize_t
+tls_read(struct tls *ctx, void *buf, size_t buflen)
+{
+ ssize_t rv = -1;
+ int ssl_ret;
+
+ tls_error_clear(&ctx->error);
+
+ if ((ctx->state & TLS_HANDSHAKE_COMPLETE) == 0) {
+ if ((rv = tls_handshake(ctx)) != 0)
+ goto out;
+ }
+
+ if (buflen > INT_MAX) {
+ tls_set_errorx(ctx, "buflen too long");
+ goto out;
+ }
+
+ ERR_clear_error();
+ if ((ssl_ret = SSL_read(ctx->ssl_conn, buf, buflen)) > 0) {
+ rv = (ssize_t)ssl_ret;
+ goto out;
+ }
+ rv = (ssize_t)tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "read");
+
+ out:
+ /* Prevent callers from performing incorrect error handling */
+ errno = 0;
+ return (rv);
+}
+
+ssize_t
+tls_write(struct tls *ctx, const void *buf, size_t buflen)
+{
+ ssize_t rv = -1;
+ int ssl_ret;
+
+ tls_error_clear(&ctx->error);
+
+ if ((ctx->state & TLS_HANDSHAKE_COMPLETE) == 0) {
+ if ((rv = tls_handshake(ctx)) != 0)
+ goto out;
+ }
+
+ if (buflen > INT_MAX) {
+ tls_set_errorx(ctx, "buflen too long");
+ goto out;
+ }
+
+ ERR_clear_error();
+ if ((ssl_ret = SSL_write(ctx->ssl_conn, buf, buflen)) > 0) {
+ rv = (ssize_t)ssl_ret;
+ goto out;
+ }
+ rv = (ssize_t)tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "write");
+
+ out:
+ /* Prevent callers from performing incorrect error handling */
+ errno = 0;
+ return (rv);
+}
+
+int
+tls_close(struct tls *ctx)
+{
+ int ssl_ret;
+ int rv = 0;
+
+ tls_error_clear(&ctx->error);
+
+ if ((ctx->flags & (TLS_CLIENT | TLS_SERVER_CONN)) == 0) {
+ tls_set_errorx(ctx, "invalid operation for context");
+ rv = -1;
+ goto out;
+ }
+
+ if (ctx->state & TLS_SSL_NEEDS_SHUTDOWN) {
+ ERR_clear_error();
+ ssl_ret = SSL_shutdown(ctx->ssl_conn);
+ if (ssl_ret < 0) {
+ rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret,
+ "shutdown");
+ if (rv == TLS_WANT_POLLIN || rv == TLS_WANT_POLLOUT)
+ goto out;
+ }
+ ctx->state &= ~TLS_SSL_NEEDS_SHUTDOWN;
+ }
+
+ if (ctx->socket != -1) {
+ if (shutdown(ctx->socket, SHUT_RDWR) != 0) {
+ if (rv == 0 &&
+ errno != ENOTCONN && errno != ECONNRESET) {
+ tls_set_error(ctx, "shutdown");
+ rv = -1;
+ }
+ }
+ if (close(ctx->socket) != 0) {
+ if (rv == 0) {
+ tls_set_error(ctx, "close");
+ rv = -1;
+ }
+ }
+ ctx->socket = -1;
+ }
+
+ if ((ctx->state & TLS_EOF_NO_CLOSE_NOTIFY) != 0) {
+ tls_set_errorx(ctx, "EOF without close notify");
+ rv = -1;
+ }
+
+ out:
+ /* Prevent callers from performing incorrect error handling */
+ errno = 0;
+ return (rv);
+}
diff --git a/compat/libtls/tls.h b/compat/libtls/tls.h
new file mode 100644
index 0000000..b94a6fa
--- /dev/null
+++ b/compat/libtls/tls.h
@@ -0,0 +1,219 @@
+/* $OpenBSD: tls.h,v 1.62 2022/03/24 15:56:34 tb Exp $ */
+/*
+ * Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef HEADER_TLS_H
+#define HEADER_TLS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+#include <stddef.h>
+#include <stdint.h>
+
+#define TLS_API 20200120
+
+#define TLS_PROTOCOL_TLSv1_0 (1 << 1)
+#define TLS_PROTOCOL_TLSv1_1 (1 << 2)
+#define TLS_PROTOCOL_TLSv1_2 (1 << 3)
+#define TLS_PROTOCOL_TLSv1_3 (1 << 4)
+
+#define TLS_PROTOCOL_TLSv1 \
+ (TLS_PROTOCOL_TLSv1_0|TLS_PROTOCOL_TLSv1_1|\
+ TLS_PROTOCOL_TLSv1_2|TLS_PROTOCOL_TLSv1_3)
+
+#define TLS_PROTOCOLS_ALL TLS_PROTOCOL_TLSv1
+#define TLS_PROTOCOLS_DEFAULT (TLS_PROTOCOL_TLSv1_2|TLS_PROTOCOL_TLSv1_3)
+
+#define TLS_WANT_POLLIN -2
+#define TLS_WANT_POLLOUT -3
+
+/* RFC 6960 Section 2.3 */
+#define TLS_OCSP_RESPONSE_SUCCESSFUL 0
+#define TLS_OCSP_RESPONSE_MALFORMED 1
+#define TLS_OCSP_RESPONSE_INTERNALERROR 2
+#define TLS_OCSP_RESPONSE_TRYLATER 3
+#define TLS_OCSP_RESPONSE_SIGREQUIRED 4
+#define TLS_OCSP_RESPONSE_UNAUTHORIZED 5
+
+/* RFC 6960 Section 2.2 */
+#define TLS_OCSP_CERT_GOOD 0
+#define TLS_OCSP_CERT_REVOKED 1
+#define TLS_OCSP_CERT_UNKNOWN 2
+
+/* RFC 5280 Section 5.3.1 */
+#define TLS_CRL_REASON_UNSPECIFIED 0
+#define TLS_CRL_REASON_KEY_COMPROMISE 1
+#define TLS_CRL_REASON_CA_COMPROMISE 2
+#define TLS_CRL_REASON_AFFILIATION_CHANGED 3
+#define TLS_CRL_REASON_SUPERSEDED 4
+#define TLS_CRL_REASON_CESSATION_OF_OPERATION 5
+#define TLS_CRL_REASON_CERTIFICATE_HOLD 6
+#define TLS_CRL_REASON_REMOVE_FROM_CRL 8
+#define TLS_CRL_REASON_PRIVILEGE_WITHDRAWN 9
+#define TLS_CRL_REASON_AA_COMPROMISE 10
+
+#define TLS_MAX_SESSION_ID_LENGTH 32
+#define TLS_TICKET_KEY_SIZE 48
+
+struct tls;
+struct tls_config;
+
+typedef ssize_t (*tls_read_cb)(struct tls *_ctx, void *_buf, size_t _buflen,
+ void *_cb_arg);
+typedef ssize_t (*tls_write_cb)(struct tls *_ctx, const void *_buf,
+ size_t _buflen, void *_cb_arg);
+
+int tls_init(void);
+
+const char *tls_config_error(struct tls_config *_config);
+const char *tls_error(struct tls *_ctx);
+
+struct tls_config *tls_config_new(void);
+void tls_config_free(struct tls_config *_config);
+
+const char *tls_default_ca_cert_file(void);
+
+int tls_config_add_keypair_file(struct tls_config *_config,
+ const char *_cert_file, const char *_key_file);
+int tls_config_add_keypair_mem(struct tls_config *_config, const uint8_t *_cert,
+ size_t _cert_len, const uint8_t *_key, size_t _key_len);
+int tls_config_add_keypair_ocsp_file(struct tls_config *_config,
+ const char *_cert_file, const char *_key_file,
+ const char *_ocsp_staple_file);
+int tls_config_add_keypair_ocsp_mem(struct tls_config *_config, const uint8_t *_cert,
+ size_t _cert_len, const uint8_t *_key, size_t _key_len,
+ const uint8_t *_staple, size_t _staple_len);
+int tls_config_set_alpn(struct tls_config *_config, const char *_alpn);
+int tls_config_set_ca_file(struct tls_config *_config, const char *_ca_file);
+int tls_config_set_ca_path(struct tls_config *_config, const char *_ca_path);
+int tls_config_set_ca_mem(struct tls_config *_config, const uint8_t *_ca,
+ size_t _len);
+int tls_config_set_cert_file(struct tls_config *_config,
+ const char *_cert_file);
+int tls_config_set_cert_mem(struct tls_config *_config, const uint8_t *_cert,
+ size_t _len);
+int tls_config_set_ciphers(struct tls_config *_config, const char *_ciphers);
+int tls_config_set_crl_file(struct tls_config *_config, const char *_crl_file);
+int tls_config_set_crl_mem(struct tls_config *_config, const uint8_t *_crl,
+ size_t _len);
+int tls_config_set_dheparams(struct tls_config *_config, const char *_params);
+int tls_config_set_ecdhecurve(struct tls_config *_config, const char *_curve);
+int tls_config_set_ecdhecurves(struct tls_config *_config, const char *_curves);
+int tls_config_set_key_file(struct tls_config *_config, const char *_key_file);
+int tls_config_set_key_mem(struct tls_config *_config, const uint8_t *_key,
+ size_t _len);
+int tls_config_set_keypair_file(struct tls_config *_config,
+ const char *_cert_file, const char *_key_file);
+int tls_config_set_keypair_mem(struct tls_config *_config, const uint8_t *_cert,
+ size_t _cert_len, const uint8_t *_key, size_t _key_len);
+int tls_config_set_keypair_ocsp_file(struct tls_config *_config,
+ const char *_cert_file, const char *_key_file, const char *_staple_file);
+int tls_config_set_keypair_ocsp_mem(struct tls_config *_config, const uint8_t *_cert,
+ size_t _cert_len, const uint8_t *_key, size_t _key_len,
+ const uint8_t *_staple, size_t staple_len);
+int tls_config_set_ocsp_staple_mem(struct tls_config *_config,
+ const uint8_t *_staple, size_t _len);
+int tls_config_set_ocsp_staple_file(struct tls_config *_config,
+ const char *_staple_file);
+int tls_config_set_protocols(struct tls_config *_config, uint32_t _protocols);
+int tls_config_set_session_fd(struct tls_config *_config, int _session_fd);
+int tls_config_set_verify_depth(struct tls_config *_config, int _verify_depth);
+
+void tls_config_prefer_ciphers_client(struct tls_config *_config);
+void tls_config_prefer_ciphers_server(struct tls_config *_config);
+
+void tls_config_insecure_noverifycert(struct tls_config *_config);
+void tls_config_insecure_noverifyname(struct tls_config *_config);
+void tls_config_insecure_noverifytime(struct tls_config *_config);
+void tls_config_verify(struct tls_config *_config);
+
+void tls_config_ocsp_require_stapling(struct tls_config *_config);
+void tls_config_verify_client(struct tls_config *_config);
+void tls_config_verify_client_optional(struct tls_config *_config);
+
+void tls_config_clear_keys(struct tls_config *_config);
+int tls_config_parse_protocols(uint32_t *_protocols, const char *_protostr);
+
+int tls_config_set_session_id(struct tls_config *_config,
+ const unsigned char *_session_id, size_t _len);
+int tls_config_set_session_lifetime(struct tls_config *_config, int _lifetime);
+int tls_config_add_ticket_key(struct tls_config *_config, uint32_t _keyrev,
+ unsigned char *_key, size_t _keylen);
+
+struct tls *tls_client(void);
+struct tls *tls_server(void);
+int tls_configure(struct tls *_ctx, struct tls_config *_config);
+void tls_reset(struct tls *_ctx);
+void tls_free(struct tls *_ctx);
+
+int tls_accept_fds(struct tls *_ctx, struct tls **_cctx, int _fd_read,
+ int _fd_write);
+int tls_accept_socket(struct tls *_ctx, struct tls **_cctx, int _socket);
+int tls_accept_cbs(struct tls *_ctx, struct tls **_cctx,
+ tls_read_cb _read_cb, tls_write_cb _write_cb, void *_cb_arg);
+int tls_connect(struct tls *_ctx, const char *_host, const char *_port);
+int tls_connect_fds(struct tls *_ctx, int _fd_read, int _fd_write,
+ const char *_servername);
+int tls_connect_servername(struct tls *_ctx, const char *_host,
+ const char *_port, const char *_servername);
+int tls_connect_socket(struct tls *_ctx, int _s, const char *_servername);
+int tls_connect_cbs(struct tls *_ctx, tls_read_cb _read_cb,
+ tls_write_cb _write_cb, void *_cb_arg, const char *_servername);
+int tls_handshake(struct tls *_ctx);
+ssize_t tls_read(struct tls *_ctx, void *_buf, size_t _buflen);
+ssize_t tls_write(struct tls *_ctx, const void *_buf, size_t _buflen);
+int tls_close(struct tls *_ctx);
+
+int tls_peer_cert_provided(struct tls *_ctx);
+int tls_peer_cert_contains_name(struct tls *_ctx, const char *_name);
+
+const char *tls_peer_cert_hash(struct tls *_ctx);
+const char *tls_peer_cert_issuer(struct tls *_ctx);
+const char *tls_peer_cert_subject(struct tls *_ctx);
+time_t tls_peer_cert_notbefore(struct tls *_ctx);
+time_t tls_peer_cert_notafter(struct tls *_ctx);
+const uint8_t *tls_peer_cert_chain_pem(struct tls *_ctx, size_t *_len);
+
+const char *tls_conn_alpn_selected(struct tls *_ctx);
+const char *tls_conn_cipher(struct tls *_ctx);
+int tls_conn_cipher_strength(struct tls *_ctx);
+const char *tls_conn_servername(struct tls *_ctx);
+int tls_conn_session_resumed(struct tls *_ctx);
+const char *tls_conn_version(struct tls *_ctx);
+
+uint8_t *tls_load_file(const char *_file, size_t *_len, char *_password);
+void tls_unload_file(uint8_t *_buf, size_t len);
+
+int tls_ocsp_process_response(struct tls *_ctx, const unsigned char *_response,
+ size_t _size);
+int tls_peer_ocsp_cert_status(struct tls *_ctx);
+int tls_peer_ocsp_crl_reason(struct tls *_ctx);
+time_t tls_peer_ocsp_next_update(struct tls *_ctx);
+int tls_peer_ocsp_response_status(struct tls *_ctx);
+const char *tls_peer_ocsp_result(struct tls *_ctx);
+time_t tls_peer_ocsp_revocation_time(struct tls *_ctx);
+time_t tls_peer_ocsp_this_update(struct tls *_ctx);
+const char *tls_peer_ocsp_url(struct tls *_ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HEADER_TLS_H */
diff --git a/compat/libtls/tls_bio_cb.c b/compat/libtls/tls_bio_cb.c
new file mode 100644
index 0000000..0bab70d
--- /dev/null
+++ b/compat/libtls/tls_bio_cb.c
@@ -0,0 +1,169 @@
+/* $OpenBSD: tls_bio_cb.c,v 1.21 2023/05/14 07:26:25 op Exp $ */
+/*
+ * Copyright (c) 2016 Tobias Pape <tobias@netshed.de>
+ *
+ * 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 "config.h"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openssl/bio.h>
+
+#include <tls.h>
+#include "tls_internal.h"
+
+static int bio_cb_write(BIO *bio, const char *buf, int num);
+static int bio_cb_read(BIO *bio, char *buf, int size);
+static int bio_cb_puts(BIO *bio, const char *str);
+static long bio_cb_ctrl(BIO *bio, int cmd, long num, void *ptr);
+
+static BIO_METHOD *bio_cb_method;
+
+static void
+bio_cb_method_init(void)
+{
+ BIO_METHOD *bio_method;
+
+ if (bio_cb_method != NULL)
+ return;
+
+ bio_method = BIO_meth_new(BIO_TYPE_MEM, "libtls_callbacks");
+ if (bio_method == NULL)
+ return;
+
+ BIO_meth_set_write(bio_method, bio_cb_write);
+ BIO_meth_set_read(bio_method, bio_cb_read);
+ BIO_meth_set_puts(bio_method, bio_cb_puts);
+ BIO_meth_set_ctrl(bio_method, bio_cb_ctrl);
+
+ bio_cb_method = bio_method;
+}
+
+static BIO_METHOD *
+bio_s_cb(void)
+{
+ if (bio_cb_method != NULL)
+ return (bio_cb_method);
+
+ bio_cb_method_init();
+
+ return (bio_cb_method);
+}
+
+static int
+bio_cb_puts(BIO *bio, const char *str)
+{
+ return (bio_cb_write(bio, str, strlen(str)));
+}
+
+static long
+bio_cb_ctrl(BIO *bio, int cmd, long num, void *ptr)
+{
+ long ret = 1;
+
+ switch (cmd) {
+ case BIO_CTRL_GET_CLOSE:
+ ret = (long)BIO_get_shutdown(bio);
+ break;
+ case BIO_CTRL_SET_CLOSE:
+ BIO_set_shutdown(bio, (int)num);
+ break;
+ case BIO_CTRL_DUP:
+ case BIO_CTRL_FLUSH:
+ break;
+ case BIO_CTRL_INFO:
+ case BIO_CTRL_GET:
+ case BIO_CTRL_SET:
+ default:
+ ret = BIO_ctrl(BIO_next(bio), cmd, num, ptr);
+ }
+
+ return (ret);
+}
+
+static int
+bio_cb_write(BIO *bio, const char *buf, int num)
+{
+ struct tls *ctx = BIO_get_data(bio);
+ int rv;
+
+ BIO_clear_retry_flags(bio);
+ rv = (ctx->write_cb)(ctx, buf, num, ctx->cb_arg);
+ if (rv == TLS_WANT_POLLIN) {
+ BIO_set_retry_read(bio);
+ rv = -1;
+ } else if (rv == TLS_WANT_POLLOUT) {
+ BIO_set_retry_write(bio);
+ rv = -1;
+ }
+ return (rv);
+}
+
+static int
+bio_cb_read(BIO *bio, char *buf, int size)
+{
+ struct tls *ctx = BIO_get_data(bio);
+ int rv;
+
+ BIO_clear_retry_flags(bio);
+ rv = (ctx->read_cb)(ctx, buf, size, ctx->cb_arg);
+ if (rv == TLS_WANT_POLLIN) {
+ BIO_set_retry_read(bio);
+ rv = -1;
+ } else if (rv == TLS_WANT_POLLOUT) {
+ BIO_set_retry_write(bio);
+ rv = -1;
+ }
+ return (rv);
+}
+
+int
+tls_set_cbs(struct tls *ctx, tls_read_cb read_cb, tls_write_cb write_cb,
+ void *cb_arg)
+{
+ const BIO_METHOD *bio_cb;
+ BIO *bio;
+ int rv = -1;
+
+ if (read_cb == NULL || write_cb == NULL) {
+ tls_set_errorx(ctx, "no callbacks provided");
+ goto err;
+ }
+
+ ctx->read_cb = read_cb;
+ ctx->write_cb = write_cb;
+ ctx->cb_arg = cb_arg;
+
+ if ((bio_cb = bio_s_cb()) == NULL) {
+ tls_set_errorx(ctx, "failed to create callback method");
+ goto err;
+ }
+ if ((bio = BIO_new(bio_cb)) == NULL) {
+ tls_set_errorx(ctx, "failed to create callback i/o");
+ goto err;
+ }
+ BIO_set_data(bio, ctx);
+ BIO_set_init(bio, 1);
+
+ SSL_set_bio(ctx->ssl_conn, bio, bio);
+
+ rv = 0;
+
+ err:
+ return (rv);
+}
diff --git a/compat/libtls/tls_client.c b/compat/libtls/tls_client.c
new file mode 100644
index 0000000..4a7018d
--- /dev/null
+++ b/compat/libtls/tls_client.c
@@ -0,0 +1,487 @@
+/* $OpenBSD: tls_client.c,v 1.49 2023/05/14 07:26:25 op Exp $ */
+/*
+ * Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <limits.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openssl/err.h>
+#include <openssl/x509.h>
+
+#include <tls.h>
+#include "tls_internal.h"
+
+struct tls *
+tls_client(void)
+{
+ struct tls *ctx;
+
+ if (tls_init() == -1)
+ return (NULL);
+
+ if ((ctx = tls_new()) == NULL)
+ return (NULL);
+
+ ctx->flags |= TLS_CLIENT;
+
+ return (ctx);
+}
+
+int
+tls_connect(struct tls *ctx, const char *host, const char *port)
+{
+ return tls_connect_servername(ctx, host, port, NULL);
+}
+
+int
+tls_connect_servername(struct tls *ctx, const char *host, const char *port,
+ const char *servername)
+{
+ struct addrinfo hints, *res, *res0;
+ const char *h = NULL, *p = NULL;
+ char *hs = NULL, *ps = NULL;
+ int rv = -1, s = -1, ret;
+
+ if ((ctx->flags & TLS_CLIENT) == 0) {
+ tls_set_errorx(ctx, "not a client context");
+ goto err;
+ }
+
+ if (host == NULL) {
+ tls_set_errorx(ctx, "host not specified");
+ goto err;
+ }
+
+ /* If port is NULL, try to extract a port from the specified host. */
+ if (port == NULL) {
+ ret = tls_host_port(host, &hs, &ps);
+ if (ret == -1) {
+ tls_set_errorx(ctx, "memory allocation failure");
+ goto err;
+ }
+ if (ret != 0) {
+ tls_set_errorx(ctx, "no port provided");
+ goto err;
+ }
+ }
+
+ h = (hs != NULL) ? hs : host;
+ p = (ps != NULL) ? ps : port;
+
+ /*
+ * First check if the host is specified as a numeric IP address,
+ * either IPv4 or IPv6, before trying to resolve the host.
+ * The AI_ADDRCONFIG resolver option will not return IPv4 or IPv6
+ * records if it is not configured on an interface; not considering
+ * loopback addresses. Checking the numeric addresses first makes
+ * sure that connection attempts to numeric addresses and especially
+ * 127.0.0.1 or ::1 loopback addresses are always possible.
+ */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = SOCK_STREAM;
+
+ /* try as an IPv4 literal */
+ hints.ai_family = AF_INET;
+ hints.ai_flags = AI_NUMERICHOST;
+ if (getaddrinfo(h, p, &hints, &res0) != 0) {
+ /* try again as an IPv6 literal */
+ hints.ai_family = AF_INET6;
+ if (getaddrinfo(h, p, &hints, &res0) != 0) {
+ /* last try, with name resolution and save the error */
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = AI_ADDRCONFIG;
+ if ((s = getaddrinfo(h, p, &hints, &res0)) != 0) {
+ tls_set_error(ctx, "%s", gai_strerror(s));
+ goto err;
+ }
+ }
+ }
+
+ /* It was resolved somehow; now try connecting to what we got */
+ s = -1;
+ for (res = res0; res; res = res->ai_next) {
+ s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (s == -1) {
+ tls_set_error(ctx, "socket");
+ continue;
+ }
+ if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
+ tls_set_error(ctx, "connect");
+ close(s);
+ s = -1;
+ continue;
+ }
+
+ break; /* Connected. */
+ }
+ freeaddrinfo(res0);
+
+ if (s == -1)
+ goto err;
+
+ if (servername == NULL)
+ servername = h;
+
+ if (tls_connect_socket(ctx, s, servername) != 0) {
+ close(s);
+ goto err;
+ }
+
+ ctx->socket = s;
+
+ rv = 0;
+
+ err:
+ free(hs);
+ free(ps);
+
+ return (rv);
+}
+
+static int
+tls_client_read_session(struct tls *ctx)
+{
+ int sfd = ctx->config->session_fd;
+ uint8_t *session = NULL;
+ size_t session_len = 0;
+ SSL_SESSION *ss = NULL;
+ BIO *bio = NULL;
+ struct stat sb;
+ ssize_t n;
+ int rv = -1;
+
+ if (fstat(sfd, &sb) == -1) {
+ tls_set_error(ctx, "failed to stat session file");
+ goto err;
+ }
+ if (sb.st_size < 0 || sb.st_size > INT_MAX) {
+ tls_set_errorx(ctx, "invalid session file size");
+ goto err;
+ }
+ session_len = (size_t)sb.st_size;
+
+ /* A zero size file means that we do not yet have a valid session. */
+ if (session_len == 0)
+ goto done;
+
+ if ((session = malloc(session_len)) == NULL)
+ goto err;
+
+ n = pread(sfd, session, session_len, 0);
+ if (n < 0 || (size_t)n != session_len) {
+ tls_set_error(ctx, "failed to read session file");
+ goto err;
+ }
+ if ((bio = BIO_new_mem_buf(session, session_len)) == NULL)
+ goto err;
+ if ((ss = PEM_read_bio_SSL_SESSION(bio, NULL, tls_password_cb,
+ NULL)) == NULL) {
+ tls_set_errorx(ctx, "failed to parse session");
+ goto err;
+ }
+
+ if (SSL_set_session(ctx->ssl_conn, ss) != 1) {
+ tls_set_errorx(ctx, "failed to set session");
+ goto err;
+ }
+
+ done:
+ rv = 0;
+
+ err:
+ freezero(session, session_len);
+ SSL_SESSION_free(ss);
+ BIO_free(bio);
+
+ return rv;
+}
+
+static int
+tls_client_write_session(struct tls *ctx)
+{
+ int sfd = ctx->config->session_fd;
+ SSL_SESSION *ss = NULL;
+ BIO *bio = NULL;
+ long data_len;
+ char *data;
+ off_t offset;
+ size_t len;
+ ssize_t n;
+ int rv = -1;
+
+ if ((ss = SSL_get1_session(ctx->ssl_conn)) == NULL) {
+ if (ftruncate(sfd, 0) == -1) {
+ tls_set_error(ctx, "failed to truncate session file");
+ goto err;
+ }
+ goto done;
+ }
+
+ if ((bio = BIO_new(BIO_s_mem())) == NULL)
+ goto err;
+ if (PEM_write_bio_SSL_SESSION(bio, ss) == 0)
+ goto err;
+ if ((data_len = BIO_get_mem_data(bio, &data)) <= 0)
+ goto err;
+
+ len = (size_t)data_len;
+ offset = 0;
+
+ if (ftruncate(sfd, len) == -1) {
+ tls_set_error(ctx, "failed to truncate session file");
+ goto err;
+ }
+ while (len > 0) {
+ if ((n = pwrite(sfd, data + offset, len, offset)) == -1) {
+ tls_set_error(ctx, "failed to write session file");
+ goto err;
+ }
+ offset += n;
+ len -= n;
+ }
+
+ done:
+ rv = 0;
+
+ err:
+ SSL_SESSION_free(ss);
+ BIO_free_all(bio);
+
+ return (rv);
+}
+
+static int
+tls_connect_common(struct tls *ctx, const char *servername)
+{
+ union tls_addr addrbuf;
+ size_t servername_len;
+ int rv = -1;
+
+ if ((ctx->flags & TLS_CLIENT) == 0) {
+ tls_set_errorx(ctx, "not a client context");
+ goto err;
+ }
+
+ if (servername != NULL) {
+ if ((ctx->servername = strdup(servername)) == NULL) {
+ tls_set_errorx(ctx, "out of memory");
+ goto err;
+ }
+
+ /*
+ * If there's a trailing dot, remove it. While an FQDN includes
+ * the terminating dot representing the zero-length label of
+ * the root (RFC 8499, section 2), the SNI explicitly does not
+ * include it (RFC 6066, section 3).
+ */
+ servername_len = strlen(ctx->servername);
+ if (servername_len > 0 &&
+ ctx->servername[servername_len - 1] == '.')
+ ctx->servername[servername_len - 1] = '\0';
+ }
+
+ if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) {
+ tls_set_errorx(ctx, "ssl context failure");
+ goto err;
+ }
+
+ if (tls_configure_ssl(ctx, ctx->ssl_ctx) != 0)
+ goto err;
+
+ if (tls_configure_ssl_keypair(ctx, ctx->ssl_ctx,
+ ctx->config->keypair, 0) != 0)
+ goto err;
+
+ if (ctx->config->verify_name) {
+ if (ctx->servername == NULL) {
+ tls_set_errorx(ctx, "server name not specified");
+ goto err;
+ }
+ }
+
+ if (tls_configure_ssl_verify(ctx, ctx->ssl_ctx, SSL_VERIFY_PEER) == -1)
+ goto err;
+
+ if (ctx->config->ecdhecurves != NULL) {
+ if (SSL_CTX_set1_groups(ctx->ssl_ctx, ctx->config->ecdhecurves,
+ ctx->config->ecdhecurves_len) != 1) {
+ tls_set_errorx(ctx, "failed to set ecdhe curves");
+ goto err;
+ }
+ }
+
+ if (SSL_CTX_set_tlsext_status_cb(ctx->ssl_ctx, tls_ocsp_verify_cb) != 1) {
+ tls_set_errorx(ctx, "ssl OCSP verification setup failure");
+ goto err;
+ }
+
+ if ((ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) {
+ tls_set_errorx(ctx, "ssl connection failure");
+ goto err;
+ }
+
+ if (SSL_set_app_data(ctx->ssl_conn, ctx) != 1) {
+ tls_set_errorx(ctx, "ssl application data failure");
+ goto err;
+ }
+
+ if (ctx->config->session_fd != -1) {
+ SSL_clear_options(ctx->ssl_conn, SSL_OP_NO_TICKET);
+ if (tls_client_read_session(ctx) == -1)
+ goto err;
+ }
+
+ if (SSL_set_tlsext_status_type(ctx->ssl_conn, TLSEXT_STATUSTYPE_ocsp) != 1) {
+ tls_set_errorx(ctx, "ssl OCSP extension setup failure");
+ goto err;
+ }
+
+ /*
+ * RFC 6066 (SNI): Literal IPv4 and IPv6 addresses are not
+ * permitted in "HostName".
+ */
+ if (ctx->servername != NULL &&
+ inet_pton(AF_INET, ctx->servername, &addrbuf) != 1 &&
+ inet_pton(AF_INET6, ctx->servername, &addrbuf) != 1) {
+ if (SSL_set_tlsext_host_name(ctx->ssl_conn,
+ ctx->servername) == 0) {
+ tls_set_errorx(ctx, "server name indication failure");
+ goto err;
+ }
+ }
+
+ ctx->state |= TLS_CONNECTED;
+ rv = 0;
+
+ err:
+ return (rv);
+}
+
+int
+tls_connect_socket(struct tls *ctx, int s, const char *servername)
+{
+ return tls_connect_fds(ctx, s, s, servername);
+}
+
+int
+tls_connect_fds(struct tls *ctx, int fd_read, int fd_write,
+ const char *servername)
+{
+ int rv = -1;
+
+ if (fd_read < 0 || fd_write < 0) {
+ tls_set_errorx(ctx, "invalid file descriptors");
+ goto err;
+ }
+
+ if (tls_connect_common(ctx, servername) != 0)
+ goto err;
+
+ if (SSL_set_rfd(ctx->ssl_conn, fd_read) != 1 ||
+ SSL_set_wfd(ctx->ssl_conn, fd_write) != 1) {
+ tls_set_errorx(ctx, "ssl file descriptor failure");
+ goto err;
+ }
+
+ rv = 0;
+ err:
+ return (rv);
+}
+
+int
+tls_connect_cbs(struct tls *ctx, tls_read_cb read_cb,
+ tls_write_cb write_cb, void *cb_arg, const char *servername)
+{
+ int rv = -1;
+
+ if (tls_connect_common(ctx, servername) != 0)
+ goto err;
+
+ if (tls_set_cbs(ctx, read_cb, write_cb, cb_arg) != 0)
+ goto err;
+
+ rv = 0;
+
+ err:
+ return (rv);
+}
+
+int
+tls_handshake_client(struct tls *ctx)
+{
+ X509 *cert = NULL;
+ int match, ssl_ret;
+ int rv = -1;
+
+ if ((ctx->flags & TLS_CLIENT) == 0) {
+ tls_set_errorx(ctx, "not a client context");
+ goto err;
+ }
+
+ if ((ctx->state & TLS_CONNECTED) == 0) {
+ tls_set_errorx(ctx, "context not connected");
+ goto err;
+ }
+
+ ctx->state |= TLS_SSL_NEEDS_SHUTDOWN;
+
+ ERR_clear_error();
+ if ((ssl_ret = SSL_connect(ctx->ssl_conn)) != 1) {
+ rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "handshake");
+ goto err;
+ }
+
+ if (ctx->config->verify_name) {
+ cert = SSL_get_peer_certificate(ctx->ssl_conn);
+ if (cert == NULL) {
+ tls_set_errorx(ctx, "no server certificate");
+ goto err;
+ }
+ if (tls_check_name(ctx, cert, ctx->servername, &match) == -1)
+ goto err;
+ if (!match) {
+ tls_set_errorx(ctx, "name `%s' not present in"
+ " server certificate", ctx->servername);
+ goto err;
+ }
+ }
+
+ ctx->state |= TLS_HANDSHAKE_COMPLETE;
+
+ if (ctx->config->session_fd != -1) {
+ if (tls_client_write_session(ctx) == -1)
+ goto err;
+ }
+
+ rv = 0;
+
+ err:
+ X509_free(cert);
+
+ return (rv);
+}
diff --git a/compat/libtls/tls_config.c b/compat/libtls/tls_config.c
new file mode 100644
index 0000000..3f4306a
--- /dev/null
+++ b/compat/libtls/tls_config.c
@@ -0,0 +1,915 @@
+/* $OpenBSD: tls_config.c,v 1.66 2023/05/14 07:26:25 op Exp $ */
+/*
+ * Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <tls.h>
+
+#include "tls_internal.h"
+
+const char *
+tls_default_ca_cert_file(void)
+{
+#ifdef OPENSMTPD_CA_FILE
+ return OPENSMTPD_CA_FILE;
+#else
+ return X509_get_default_cert_file();
+#endif
+}
+
+int
+tls_config_load_file(struct tls_error *error, const char *filetype,
+ const char *filename, char **buf, size_t *len)
+{
+ struct stat st;
+ int fd = -1;
+ ssize_t n;
+
+ free(*buf);
+ *buf = NULL;
+ *len = 0;
+
+ if ((fd = open(filename, O_RDONLY)) == -1) {
+ tls_error_set(error, "failed to open %s file '%s'",
+ filetype, filename);
+ goto err;
+ }
+ if (fstat(fd, &st) != 0) {
+ tls_error_set(error, "failed to stat %s file '%s'",
+ filetype, filename);
+ goto err;
+ }
+ if (st.st_size < 0)
+ goto err;
+ *len = (size_t)st.st_size;
+ if ((*buf = malloc(*len)) == NULL) {
+ tls_error_set(error, "failed to allocate buffer for "
+ "%s file", filetype);
+ goto err;
+ }
+ n = read(fd, *buf, *len);
+ if (n < 0 || (size_t)n != *len) {
+ tls_error_set(error, "failed to read %s file '%s'",
+ filetype, filename);
+ goto err;
+ }
+ close(fd);
+ return 0;
+
+ err:
+ if (fd != -1)
+ close(fd);
+ freezero(*buf, *len);
+ *buf = NULL;
+ *len = 0;
+
+ return -1;
+}
+
+struct tls_config *
+tls_config_new_internal(void)
+{
+ struct tls_config *config;
+ unsigned char sid[TLS_MAX_SESSION_ID_LENGTH];
+
+ if ((config = calloc(1, sizeof(*config))) == NULL)
+ return (NULL);
+
+ config->refcount = 1;
+ config->session_fd = -1;
+
+ if ((config->keypair = tls_keypair_new()) == NULL)
+ goto err;
+
+ /*
+ * Default configuration.
+ */
+ if (tls_config_set_dheparams(config, "none") != 0)
+ goto err;
+ if (tls_config_set_ecdhecurves(config, "default") != 0)
+ goto err;
+ if (tls_config_set_ciphers(config, "secure") != 0)
+ goto err;
+
+ if (tls_config_set_protocols(config, TLS_PROTOCOLS_DEFAULT) != 0)
+ goto err;
+ if (tls_config_set_verify_depth(config, 6) != 0)
+ goto err;
+
+ /*
+ * Set session ID context to a random value. For the simple case
+ * of a single process server this is good enough. For multiprocess
+ * servers the session ID needs to be set by the caller.
+ */
+ arc4random_buf(sid, sizeof(sid));
+ if (tls_config_set_session_id(config, sid, sizeof(sid)) != 0)
+ goto err;
+ config->ticket_keyrev = arc4random();
+ config->ticket_autorekey = 1;
+
+ tls_config_prefer_ciphers_server(config);
+
+ tls_config_verify(config);
+
+ return (config);
+
+ err:
+ tls_config_free(config);
+ return (NULL);
+}
+
+struct tls_config *
+tls_config_new(void)
+{
+ if (tls_init() == -1)
+ return (NULL);
+
+ return tls_config_new_internal();
+}
+
+void
+tls_config_free(struct tls_config *config)
+{
+ struct tls_keypair *kp, *nkp;
+ int refcount;
+
+ if (config == NULL)
+ return;
+
+ refcount = --config->refcount;
+
+ if (refcount > 0)
+ return;
+
+ for (kp = config->keypair; kp != NULL; kp = nkp) {
+ nkp = kp->next;
+ tls_keypair_free(kp);
+ }
+
+ free(config->error.msg);
+
+ free(config->alpn);
+ free((char *)config->ca_mem);
+ free((char *)config->ca_path);
+ free((char *)config->ciphers);
+ free((char *)config->crl_mem);
+ free(config->ecdhecurves);
+
+ free(config);
+}
+
+static void
+tls_config_keypair_add(struct tls_config *config, struct tls_keypair *keypair)
+{
+ struct tls_keypair *kp;
+
+ kp = config->keypair;
+ while (kp->next != NULL)
+ kp = kp->next;
+
+ kp->next = keypair;
+}
+
+const char *
+tls_config_error(struct tls_config *config)
+{
+ return config->error.msg;
+}
+
+void
+tls_config_clear_keys(struct tls_config *config)
+{
+ struct tls_keypair *kp;
+
+ for (kp = config->keypair; kp != NULL; kp = kp->next)
+ tls_keypair_clear_key(kp);
+}
+
+int
+tls_config_parse_protocols(uint32_t *protocols, const char *protostr)
+{
+ uint32_t proto, protos = 0;
+ char *s, *p, *q;
+ int negate;
+
+ if (protostr == NULL) {
+ *protocols = TLS_PROTOCOLS_DEFAULT;
+ return (0);
+ }
+
+ if ((s = strdup(protostr)) == NULL)
+ return (-1);
+
+ q = s;
+ while ((p = strsep(&q, ",:")) != NULL) {
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ negate = 0;
+ if (*p == '!') {
+ negate = 1;
+ p++;
+ }
+
+ if (negate && protos == 0)
+ protos = TLS_PROTOCOLS_ALL;
+
+ proto = 0;
+ if (strcasecmp(p, "all") == 0 ||
+ strcasecmp(p, "legacy") == 0)
+ proto = TLS_PROTOCOLS_ALL;
+ else if (strcasecmp(p, "default") == 0 ||
+ strcasecmp(p, "secure") == 0)
+ proto = TLS_PROTOCOLS_DEFAULT;
+ if (strcasecmp(p, "tlsv1") == 0)
+ proto = TLS_PROTOCOL_TLSv1;
+ else if (strcasecmp(p, "tlsv1.0") == 0)
+ proto = TLS_PROTOCOL_TLSv1_0;
+ else if (strcasecmp(p, "tlsv1.1") == 0)
+ proto = TLS_PROTOCOL_TLSv1_1;
+ else if (strcasecmp(p, "tlsv1.2") == 0)
+ proto = TLS_PROTOCOL_TLSv1_2;
+ else if (strcasecmp(p, "tlsv1.3") == 0)
+ proto = TLS_PROTOCOL_TLSv1_3;
+
+ if (proto == 0) {
+ free(s);
+ return (-1);
+ }
+
+ if (negate)
+ protos &= ~proto;
+ else
+ protos |= proto;
+ }
+
+ *protocols = protos;
+
+ free(s);
+
+ return (0);
+}
+
+static int
+tls_config_parse_alpn(struct tls_config *config, const char *alpn,
+ char **alpn_data, size_t *alpn_len)
+{
+ size_t buf_len, i, len;
+ char *buf = NULL;
+ char *s = NULL;
+ char *p, *q;
+
+ free(*alpn_data);
+ *alpn_data = NULL;
+ *alpn_len = 0;
+
+ if ((buf_len = strlen(alpn) + 1) > 65535) {
+ tls_config_set_errorx(config, "alpn too large");
+ goto err;
+ }
+
+ if ((buf = malloc(buf_len)) == NULL) {
+ tls_config_set_errorx(config, "out of memory");
+ goto err;
+ }
+
+ if ((s = strdup(alpn)) == NULL) {
+ tls_config_set_errorx(config, "out of memory");
+ goto err;
+ }
+
+ i = 0;
+ q = s;
+ while ((p = strsep(&q, ",")) != NULL) {
+ if ((len = strlen(p)) == 0) {
+ tls_config_set_errorx(config,
+ "alpn protocol with zero length");
+ goto err;
+ }
+ if (len > 255) {
+ tls_config_set_errorx(config,
+ "alpn protocol too long");
+ goto err;
+ }
+ buf[i++] = len & 0xff;
+ memcpy(&buf[i], p, len);
+ i += len;
+ }
+
+ free(s);
+
+ *alpn_data = buf;
+ *alpn_len = buf_len;
+
+ return (0);
+
+ err:
+ free(buf);
+ free(s);
+
+ return (-1);
+}
+
+int
+tls_config_set_alpn(struct tls_config *config, const char *alpn)
+{
+ return tls_config_parse_alpn(config, alpn, &config->alpn,
+ &config->alpn_len);
+}
+
+static int
+tls_config_add_keypair_file_internal(struct tls_config *config,
+ const char *cert_file, const char *key_file, const char *ocsp_file)
+{
+ struct tls_keypair *keypair;
+
+ if ((keypair = tls_keypair_new()) == NULL)
+ return (-1);
+ if (tls_keypair_set_cert_file(keypair, &config->error, cert_file) != 0)
+ goto err;
+ if (key_file != NULL &&
+ tls_keypair_set_key_file(keypair, &config->error, key_file) != 0)
+ goto err;
+ if (ocsp_file != NULL &&
+ tls_keypair_set_ocsp_staple_file(keypair, &config->error,
+ ocsp_file) != 0)
+ goto err;
+
+ tls_config_keypair_add(config, keypair);
+
+ return (0);
+
+ err:
+ tls_keypair_free(keypair);
+ return (-1);
+}
+
+static int
+tls_config_add_keypair_mem_internal(struct tls_config *config, const uint8_t *cert,
+ size_t cert_len, const uint8_t *key, size_t key_len,
+ const uint8_t *staple, size_t staple_len)
+{
+ struct tls_keypair *keypair;
+
+ if ((keypair = tls_keypair_new()) == NULL)
+ return (-1);
+ if (tls_keypair_set_cert_mem(keypair, &config->error, cert, cert_len) != 0)
+ goto err;
+ if (key != NULL &&
+ tls_keypair_set_key_mem(keypair, &config->error, key, key_len) != 0)
+ goto err;
+ if (staple != NULL &&
+ tls_keypair_set_ocsp_staple_mem(keypair, &config->error, staple,
+ staple_len) != 0)
+ goto err;
+
+ tls_config_keypair_add(config, keypair);
+
+ return (0);
+
+ err:
+ tls_keypair_free(keypair);
+ return (-1);
+}
+
+int
+tls_config_add_keypair_mem(struct tls_config *config, const uint8_t *cert,
+ size_t cert_len, const uint8_t *key, size_t key_len)
+{
+ return tls_config_add_keypair_mem_internal(config, cert, cert_len, key,
+ key_len, NULL, 0);
+}
+
+int
+tls_config_add_keypair_file(struct tls_config *config,
+ const char *cert_file, const char *key_file)
+{
+ return tls_config_add_keypair_file_internal(config, cert_file,
+ key_file, NULL);
+}
+
+int
+tls_config_add_keypair_ocsp_mem(struct tls_config *config, const uint8_t *cert,
+ size_t cert_len, const uint8_t *key, size_t key_len, const uint8_t *staple,
+ size_t staple_len)
+{
+ return tls_config_add_keypair_mem_internal(config, cert, cert_len, key,
+ key_len, staple, staple_len);
+}
+
+int
+tls_config_add_keypair_ocsp_file(struct tls_config *config,
+ const char *cert_file, const char *key_file, const char *ocsp_file)
+{
+ return tls_config_add_keypair_file_internal(config, cert_file,
+ key_file, ocsp_file);
+}
+
+int
+tls_config_set_ca_file(struct tls_config *config, const char *ca_file)
+{
+ return tls_config_load_file(&config->error, "CA", ca_file,
+ &config->ca_mem, &config->ca_len);
+}
+
+int
+tls_config_set_ca_path(struct tls_config *config, const char *ca_path)
+{
+ return tls_set_string(&config->ca_path, ca_path);
+}
+
+int
+tls_config_set_ca_mem(struct tls_config *config, const uint8_t *ca, size_t len)
+{
+ return tls_set_mem(&config->ca_mem, &config->ca_len, ca, len);
+}
+
+int
+tls_config_set_cert_file(struct tls_config *config, const char *cert_file)
+{
+ return tls_keypair_set_cert_file(config->keypair, &config->error,
+ cert_file);
+}
+
+int
+tls_config_set_cert_mem(struct tls_config *config, const uint8_t *cert,
+ size_t len)
+{
+ return tls_keypair_set_cert_mem(config->keypair, &config->error,
+ cert, len);
+}
+
+int
+tls_config_set_ciphers(struct tls_config *config, const char *ciphers)
+{
+ SSL_CTX *ssl_ctx = NULL;
+
+ if (ciphers == NULL ||
+ strcasecmp(ciphers, "default") == 0 ||
+ strcasecmp(ciphers, "secure") == 0)
+ ciphers = TLS_CIPHERS_DEFAULT;
+ else if (strcasecmp(ciphers, "compat") == 0)
+ ciphers = TLS_CIPHERS_COMPAT;
+ else if (strcasecmp(ciphers, "legacy") == 0)
+ ciphers = TLS_CIPHERS_LEGACY;
+ else if (strcasecmp(ciphers, "all") == 0 ||
+ strcasecmp(ciphers, "insecure") == 0)
+ ciphers = TLS_CIPHERS_ALL;
+
+ if ((ssl_ctx = SSL_CTX_new(SSLv23_method())) == NULL) {
+ tls_config_set_errorx(config, "out of memory");
+ goto err;
+ }
+ if (SSL_CTX_set_cipher_list(ssl_ctx, ciphers) != 1) {
+ tls_config_set_errorx(config, "no ciphers for '%s'", ciphers);
+ goto err;
+ }
+
+ SSL_CTX_free(ssl_ctx);
+ return tls_set_string(&config->ciphers, ciphers);
+
+ err:
+ SSL_CTX_free(ssl_ctx);
+ return -1;
+}
+
+int
+tls_config_set_crl_file(struct tls_config *config, const char *crl_file)
+{
+ return tls_config_load_file(&config->error, "CRL", crl_file,
+ &config->crl_mem, &config->crl_len);
+}
+
+int
+tls_config_set_crl_mem(struct tls_config *config, const uint8_t *crl,
+ size_t len)
+{
+ return tls_set_mem(&config->crl_mem, &config->crl_len, crl, len);
+}
+
+int
+tls_config_set_dheparams(struct tls_config *config, const char *params)
+{
+ int keylen;
+
+ if (params == NULL || strcasecmp(params, "none") == 0)
+ keylen = 0;
+ else if (strcasecmp(params, "auto") == 0)
+ keylen = -1;
+ else if (strcasecmp(params, "legacy") == 0)
+ keylen = 1024;
+ else {
+ tls_config_set_errorx(config, "invalid dhe param '%s'", params);
+ return (-1);
+ }
+
+ config->dheparams = keylen;
+
+ return (0);
+}
+
+int
+tls_config_set_ecdhecurve(struct tls_config *config, const char *curve)
+{
+ if (curve == NULL ||
+ strcasecmp(curve, "none") == 0 ||
+ strcasecmp(curve, "auto") == 0) {
+ curve = TLS_ECDHE_CURVES;
+ } else if (strchr(curve, ',') != NULL || strchr(curve, ':') != NULL) {
+ tls_config_set_errorx(config, "invalid ecdhe curve '%s'",
+ curve);
+ return (-1);
+ }
+
+ return tls_config_set_ecdhecurves(config, curve);
+}
+
+int
+tls_config_set_ecdhecurves(struct tls_config *config, const char *curves)
+{
+ int *curves_list = NULL, *curves_new;
+ size_t curves_num = 0;
+ char *cs = NULL;
+ char *p, *q;
+ int rv = -1;
+ int nid;
+
+ free(config->ecdhecurves);
+ config->ecdhecurves = NULL;
+ config->ecdhecurves_len = 0;
+
+ if (curves == NULL || strcasecmp(curves, "default") == 0)
+ curves = TLS_ECDHE_CURVES;
+
+ if ((cs = strdup(curves)) == NULL) {
+ tls_config_set_errorx(config, "out of memory");
+ goto err;
+ }
+
+ q = cs;
+ while ((p = strsep(&q, ",:")) != NULL) {
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ nid = OBJ_sn2nid(p);
+ if (nid == NID_undef)
+ nid = OBJ_ln2nid(p);
+ if (nid == NID_undef)
+ nid = EC_curve_nist2nid(p);
+ if (nid == NID_undef) {
+ tls_config_set_errorx(config,
+ "invalid ecdhe curve '%s'", p);
+ goto err;
+ }
+
+ if ((curves_new = reallocarray(curves_list, curves_num + 1,
+ sizeof(int))) == NULL) {
+ tls_config_set_errorx(config, "out of memory");
+ goto err;
+ }
+ curves_list = curves_new;
+ curves_list[curves_num] = nid;
+ curves_num++;
+ }
+
+ config->ecdhecurves = curves_list;
+ config->ecdhecurves_len = curves_num;
+ curves_list = NULL;
+
+ rv = 0;
+
+ err:
+ free(cs);
+ free(curves_list);
+
+ return (rv);
+}
+
+int
+tls_config_set_key_file(struct tls_config *config, const char *key_file)
+{
+ return tls_keypair_set_key_file(config->keypair, &config->error,
+ key_file);
+}
+
+int
+tls_config_set_key_mem(struct tls_config *config, const uint8_t *key,
+ size_t len)
+{
+ return tls_keypair_set_key_mem(config->keypair, &config->error,
+ key, len);
+}
+
+static int
+tls_config_set_keypair_file_internal(struct tls_config *config,
+ const char *cert_file, const char *key_file, const char *ocsp_file)
+{
+ if (tls_config_set_cert_file(config, cert_file) != 0)
+ return (-1);
+ if (tls_config_set_key_file(config, key_file) != 0)
+ return (-1);
+ if (ocsp_file != NULL &&
+ tls_config_set_ocsp_staple_file(config, ocsp_file) != 0)
+ return (-1);
+
+ return (0);
+}
+
+static int
+tls_config_set_keypair_mem_internal(struct tls_config *config, const uint8_t *cert,
+ size_t cert_len, const uint8_t *key, size_t key_len,
+ const uint8_t *staple, size_t staple_len)
+{
+ if (tls_config_set_cert_mem(config, cert, cert_len) != 0)
+ return (-1);
+ if (tls_config_set_key_mem(config, key, key_len) != 0)
+ return (-1);
+ if ((staple != NULL) &&
+ (tls_config_set_ocsp_staple_mem(config, staple, staple_len) != 0))
+ return (-1);
+
+ return (0);
+}
+
+int
+tls_config_set_keypair_file(struct tls_config *config,
+ const char *cert_file, const char *key_file)
+{
+ return tls_config_set_keypair_file_internal(config, cert_file, key_file,
+ NULL);
+}
+
+int
+tls_config_set_keypair_mem(struct tls_config *config, const uint8_t *cert,
+ size_t cert_len, const uint8_t *key, size_t key_len)
+{
+ return tls_config_set_keypair_mem_internal(config, cert, cert_len,
+ key, key_len, NULL, 0);
+}
+
+int
+tls_config_set_keypair_ocsp_file(struct tls_config *config,
+ const char *cert_file, const char *key_file, const char *ocsp_file)
+{
+ return tls_config_set_keypair_file_internal(config, cert_file, key_file,
+ ocsp_file);
+}
+
+int
+tls_config_set_keypair_ocsp_mem(struct tls_config *config, const uint8_t *cert,
+ size_t cert_len, const uint8_t *key, size_t key_len,
+ const uint8_t *staple, size_t staple_len)
+{
+ return tls_config_set_keypair_mem_internal(config, cert, cert_len,
+ key, key_len, staple, staple_len);
+}
+
+
+int
+tls_config_set_protocols(struct tls_config *config, uint32_t protocols)
+{
+ config->protocols = protocols;
+
+ return (0);
+}
+
+int
+tls_config_set_session_fd(struct tls_config *config, int session_fd)
+{
+ struct stat sb;
+ mode_t mugo;
+
+ if (session_fd == -1) {
+ config->session_fd = session_fd;
+ return (0);
+ }
+
+ if (fstat(session_fd, &sb) == -1) {
+ tls_config_set_error(config, "failed to stat session file");
+ return (-1);
+ }
+ if (!S_ISREG(sb.st_mode)) {
+ tls_config_set_errorx(config,
+ "session file is not a regular file");
+ return (-1);
+ }
+
+ if (sb.st_uid != getuid()) {
+ tls_config_set_errorx(config, "session file has incorrect "
+ "owner (uid %u != %u)", sb.st_uid, getuid());
+ return (-1);
+ }
+ mugo = sb.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO);
+ if (mugo != (S_IRUSR|S_IWUSR)) {
+ tls_config_set_errorx(config, "session file has incorrect "
+ "permissions (%o != 600)", mugo);
+ return (-1);
+ }
+
+ config->session_fd = session_fd;
+
+ return (0);
+}
+
+int
+tls_config_set_verify_depth(struct tls_config *config, int verify_depth)
+{
+ config->verify_depth = verify_depth;
+
+ return (0);
+}
+
+void
+tls_config_prefer_ciphers_client(struct tls_config *config)
+{
+ config->ciphers_server = 0;
+}
+
+void
+tls_config_prefer_ciphers_server(struct tls_config *config)
+{
+ config->ciphers_server = 1;
+}
+
+void
+tls_config_insecure_noverifycert(struct tls_config *config)
+{
+ config->verify_cert = 0;
+}
+
+void
+tls_config_insecure_noverifyname(struct tls_config *config)
+{
+ config->verify_name = 0;
+}
+
+void
+tls_config_insecure_noverifytime(struct tls_config *config)
+{
+ config->verify_time = 0;
+}
+
+void
+tls_config_verify(struct tls_config *config)
+{
+ config->verify_cert = 1;
+ config->verify_name = 1;
+ config->verify_time = 1;
+}
+
+void
+tls_config_ocsp_require_stapling(struct tls_config *config)
+{
+ config->ocsp_require_stapling = 1;
+}
+
+void
+tls_config_verify_client(struct tls_config *config)
+{
+ config->verify_client = 1;
+}
+
+void
+tls_config_verify_client_optional(struct tls_config *config)
+{
+ config->verify_client = 2;
+}
+
+void
+tls_config_skip_private_key_check(struct tls_config *config)
+{
+ config->skip_private_key_check = 1;
+}
+
+void
+tls_config_use_fake_private_key(struct tls_config *config)
+{
+ config->use_fake_private_key = 1;
+ config->skip_private_key_check = 1;
+}
+
+int
+tls_config_set_ocsp_staple_file(struct tls_config *config, const char *staple_file)
+{
+ return tls_keypair_set_ocsp_staple_file(config->keypair, &config->error,
+ staple_file);
+}
+
+int
+tls_config_set_ocsp_staple_mem(struct tls_config *config, const uint8_t *staple,
+ size_t len)
+{
+ return tls_keypair_set_ocsp_staple_mem(config->keypair, &config->error,
+ staple, len);
+}
+
+int
+tls_config_set_session_id(struct tls_config *config,
+ const unsigned char *session_id, size_t len)
+{
+ if (len > TLS_MAX_SESSION_ID_LENGTH) {
+ tls_config_set_errorx(config, "session ID too large");
+ return (-1);
+ }
+ memset(config->session_id, 0, sizeof(config->session_id));
+ memcpy(config->session_id, session_id, len);
+ return (0);
+}
+
+int
+tls_config_set_session_lifetime(struct tls_config *config, int lifetime)
+{
+ if (lifetime > TLS_MAX_SESSION_TIMEOUT) {
+ tls_config_set_errorx(config, "session lifetime too large");
+ return (-1);
+ }
+ if (lifetime != 0 && lifetime < TLS_MIN_SESSION_TIMEOUT) {
+ tls_config_set_errorx(config, "session lifetime too small");
+ return (-1);
+ }
+
+ config->session_lifetime = lifetime;
+ return (0);
+}
+
+int
+tls_config_add_ticket_key(struct tls_config *config, uint32_t keyrev,
+ unsigned char *key, size_t keylen)
+{
+ struct tls_ticket_key newkey;
+ int i;
+
+ if (TLS_TICKET_KEY_SIZE != keylen ||
+ sizeof(newkey.aes_key) + sizeof(newkey.hmac_key) > keylen) {
+ tls_config_set_errorx(config,
+ "wrong amount of ticket key data");
+ return (-1);
+ }
+
+ keyrev = htonl(keyrev);
+ memset(&newkey, 0, sizeof(newkey));
+ memcpy(newkey.key_name, &keyrev, sizeof(keyrev));
+ memcpy(newkey.aes_key, key, sizeof(newkey.aes_key));
+ memcpy(newkey.hmac_key, key + sizeof(newkey.aes_key),
+ sizeof(newkey.hmac_key));
+ newkey.time = time(NULL);
+
+ for (i = 0; i < TLS_NUM_TICKETS; i++) {
+ struct tls_ticket_key *tk = &config->ticket_keys[i];
+ if (memcmp(newkey.key_name, tk->key_name,
+ sizeof(tk->key_name)) != 0)
+ continue;
+
+ /* allow re-entry of most recent key */
+ if (i == 0 && memcmp(newkey.aes_key, tk->aes_key,
+ sizeof(tk->aes_key)) == 0 && memcmp(newkey.hmac_key,
+ tk->hmac_key, sizeof(tk->hmac_key)) == 0)
+ return (0);
+ tls_config_set_errorx(config, "ticket key already present");
+ return (-1);
+ }
+
+ memmove(&config->ticket_keys[1], &config->ticket_keys[0],
+ sizeof(config->ticket_keys) - sizeof(config->ticket_keys[0]));
+ config->ticket_keys[0] = newkey;
+
+ config->ticket_autorekey = 0;
+
+ return (0);
+}
+
+int
+tls_config_ticket_autorekey(struct tls_config *config)
+{
+ unsigned char key[TLS_TICKET_KEY_SIZE];
+ int rv;
+
+ arc4random_buf(key, sizeof(key));
+ rv = tls_config_add_ticket_key(config, config->ticket_keyrev++, key,
+ sizeof(key));
+ config->ticket_autorekey = 1;
+ return (rv);
+}
diff --git a/compat/libtls/tls_conninfo.c b/compat/libtls/tls_conninfo.c
new file mode 100644
index 0000000..ac6df2f
--- /dev/null
+++ b/compat/libtls/tls_conninfo.c
@@ -0,0 +1,346 @@
+/* $OpenBSD: tls_conninfo.c,v 1.23 2023/05/14 07:26:25 op Exp $ */
+/*
+ * Copyright (c) 2015 Joel Sing <jsing@openbsd.org>
+ * Copyright (c) 2015 Bob Beck <beck@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <openssl/x509.h>
+
+#include <tls.h>
+#include "tls_internal.h"
+
+int ASN1_time_tm_clamp_notafter(struct tm *tm);
+
+int
+tls_hex_string(const unsigned char *in, size_t inlen, char **out,
+ size_t *outlen)
+{
+ static const char hex[] = "0123456789abcdef";
+ size_t i, len;
+ char *p;
+
+ if (outlen != NULL)
+ *outlen = 0;
+
+ if (inlen >= SIZE_MAX)
+ return (-1);
+ if ((*out = reallocarray(NULL, inlen + 1, 2)) == NULL)
+ return (-1);
+
+ p = *out;
+ len = 0;
+ for (i = 0; i < inlen; i++) {
+ p[len++] = hex[(in[i] >> 4) & 0x0f];
+ p[len++] = hex[in[i] & 0x0f];
+ }
+ p[len++] = 0;
+
+ if (outlen != NULL)
+ *outlen = len;
+
+ return (0);
+}
+
+static int
+tls_get_peer_cert_hash(struct tls *ctx, char **hash)
+{
+ *hash = NULL;
+ if (ctx->ssl_peer_cert == NULL)
+ return (0);
+
+ if (tls_cert_hash(ctx->ssl_peer_cert, hash) == -1) {
+ tls_set_errorx(ctx, "unable to compute peer certificate hash - out of memory");
+ *hash = NULL;
+ return -1;
+ }
+ return 0;
+}
+
+static int
+tls_get_peer_cert_issuer(struct tls *ctx, char **issuer)
+{
+ X509_NAME *name = NULL;
+
+ *issuer = NULL;
+ if (ctx->ssl_peer_cert == NULL)
+ return (-1);
+ if ((name = X509_get_issuer_name(ctx->ssl_peer_cert)) == NULL)
+ return (-1);
+ *issuer = X509_NAME_oneline(name, 0, 0);
+ if (*issuer == NULL)
+ return (-1);
+ return (0);
+}
+
+static int
+tls_get_peer_cert_subject(struct tls *ctx, char **subject)
+{
+ X509_NAME *name = NULL;
+
+ *subject = NULL;
+ if (ctx->ssl_peer_cert == NULL)
+ return (-1);
+ if ((name = X509_get_subject_name(ctx->ssl_peer_cert)) == NULL)
+ return (-1);
+ *subject = X509_NAME_oneline(name, 0, 0);
+ if (*subject == NULL)
+ return (-1);
+ return (0);
+}
+
+static int
+tls_get_peer_cert_times(struct tls *ctx, time_t *notbefore,
+ time_t *notafter)
+{
+ struct tm before_tm, after_tm;
+ ASN1_TIME *before, *after;
+
+ if (ctx->ssl_peer_cert == NULL)
+ return (-1);
+
+ if ((before = X509_get_notBefore(ctx->ssl_peer_cert)) == NULL)
+ goto err;
+ if ((after = X509_get_notAfter(ctx->ssl_peer_cert)) == NULL)
+ goto err;
+ if (ASN1_time_parse(before->data, before->length, &before_tm, 0) == -1)
+ goto err;
+ if (ASN1_time_parse(after->data, after->length, &after_tm, 0) == -1)
+ goto err;
+ if (!ASN1_time_tm_clamp_notafter(&after_tm))
+ goto err;
+ if ((*notbefore = timegm(&before_tm)) == -1)
+ goto err;
+ if ((*notafter = timegm(&after_tm)) == -1)
+ goto err;
+
+ return (0);
+
+ err:
+ return (-1);
+}
+
+static int
+tls_get_peer_cert_info(struct tls *ctx)
+{
+ if (ctx->ssl_peer_cert == NULL)
+ return (0);
+
+ if (tls_get_peer_cert_hash(ctx, &ctx->conninfo->hash) == -1)
+ goto err;
+ if (tls_get_peer_cert_subject(ctx, &ctx->conninfo->subject) == -1)
+ goto err;
+ if (tls_get_peer_cert_issuer(ctx, &ctx->conninfo->issuer) == -1)
+ goto err;
+ if (tls_get_peer_cert_times(ctx, &ctx->conninfo->notbefore,
+ &ctx->conninfo->notafter) == -1)
+ goto err;
+
+ return (0);
+
+ err:
+ return (-1);
+}
+
+static int
+tls_conninfo_alpn_proto(struct tls *ctx)
+{
+ const unsigned char *p;
+ unsigned int len;
+
+ free(ctx->conninfo->alpn);
+ ctx->conninfo->alpn = NULL;
+
+ SSL_get0_alpn_selected(ctx->ssl_conn, &p, &len);
+ if (len > 0) {
+ if ((ctx->conninfo->alpn = malloc(len + 1)) == NULL)
+ return (-1);
+ memcpy(ctx->conninfo->alpn, p, len);
+ ctx->conninfo->alpn[len] = '\0';
+ }
+
+ return (0);
+}
+
+static int
+tls_conninfo_cert_pem(struct tls *ctx)
+{
+ int i, rv = -1;
+ BIO *membio = NULL;
+ BUF_MEM *bptr = NULL;
+
+ if (ctx->ssl_peer_cert == NULL)
+ return 0;
+ if ((membio = BIO_new(BIO_s_mem()))== NULL)
+ goto err;
+
+ /*
+ * We have to write the peer cert out separately, because
+ * the certificate chain may or may not contain it.
+ */
+ if (!PEM_write_bio_X509(membio, ctx->ssl_peer_cert))
+ goto err;
+ for (i = 0; i < sk_X509_num(ctx->ssl_peer_chain); i++) {
+ X509 *chaincert = sk_X509_value(ctx->ssl_peer_chain, i);
+ if (chaincert != ctx->ssl_peer_cert &&
+ !PEM_write_bio_X509(membio, chaincert))
+ goto err;
+ }
+
+ BIO_get_mem_ptr(membio, &bptr);
+ free(ctx->conninfo->peer_cert);
+ ctx->conninfo->peer_cert_len = 0;
+ if ((ctx->conninfo->peer_cert = malloc(bptr->length)) == NULL)
+ goto err;
+ ctx->conninfo->peer_cert_len = bptr->length;
+ memcpy(ctx->conninfo->peer_cert, bptr->data,
+ ctx->conninfo->peer_cert_len);
+
+ /* BIO_free() will kill BUF_MEM - because we have not set BIO_NOCLOSE */
+ rv = 0;
+ err:
+ BIO_free(membio);
+ return rv;
+}
+
+static int
+tls_conninfo_session(struct tls *ctx)
+{
+ ctx->conninfo->session_resumed = SSL_session_reused(ctx->ssl_conn);
+
+ return 0;
+}
+
+int
+tls_conninfo_populate(struct tls *ctx)
+{
+ const char *tmp;
+
+ tls_conninfo_free(ctx->conninfo);
+
+ if ((ctx->conninfo = calloc(1, sizeof(struct tls_conninfo))) == NULL) {
+ tls_set_errorx(ctx, "out of memory");
+ goto err;
+ }
+
+ if (tls_conninfo_alpn_proto(ctx) == -1)
+ goto err;
+
+ if ((tmp = SSL_get_cipher(ctx->ssl_conn)) == NULL)
+ goto err;
+ if ((ctx->conninfo->cipher = strdup(tmp)) == NULL)
+ goto err;
+ ctx->conninfo->cipher_strength = SSL_get_cipher_bits(ctx->ssl_conn, NULL);
+
+ if (ctx->servername != NULL) {
+ if ((ctx->conninfo->servername =
+ strdup(ctx->servername)) == NULL)
+ goto err;
+ }
+
+ if ((tmp = SSL_get_version(ctx->ssl_conn)) == NULL)
+ goto err;
+ if ((ctx->conninfo->version = strdup(tmp)) == NULL)
+ goto err;
+
+ if (tls_get_peer_cert_info(ctx) == -1)
+ goto err;
+
+ if (tls_conninfo_cert_pem(ctx) == -1)
+ goto err;
+
+ if (tls_conninfo_session(ctx) == -1)
+ goto err;
+
+ return (0);
+
+ err:
+ tls_conninfo_free(ctx->conninfo);
+ ctx->conninfo = NULL;
+
+ return (-1);
+}
+
+void
+tls_conninfo_free(struct tls_conninfo *conninfo)
+{
+ if (conninfo == NULL)
+ return;
+
+ free(conninfo->alpn);
+ free(conninfo->cipher);
+ free(conninfo->servername);
+ free(conninfo->version);
+
+ free(conninfo->hash);
+ free(conninfo->issuer);
+ free(conninfo->subject);
+
+ free(conninfo->peer_cert);
+
+ free(conninfo);
+}
+
+const char *
+tls_conn_alpn_selected(struct tls *ctx)
+{
+ if (ctx->conninfo == NULL)
+ return (NULL);
+ return (ctx->conninfo->alpn);
+}
+
+const char *
+tls_conn_cipher(struct tls *ctx)
+{
+ if (ctx->conninfo == NULL)
+ return (NULL);
+ return (ctx->conninfo->cipher);
+}
+
+int
+tls_conn_cipher_strength(struct tls *ctx)
+{
+ if (ctx->conninfo == NULL)
+ return (0);
+ return (ctx->conninfo->cipher_strength);
+}
+
+const char *
+tls_conn_servername(struct tls *ctx)
+{
+ if (ctx->conninfo == NULL)
+ return (NULL);
+ return (ctx->conninfo->servername);
+}
+
+int
+tls_conn_session_resumed(struct tls *ctx)
+{
+ if (ctx->conninfo == NULL)
+ return (0);
+ return (ctx->conninfo->session_resumed);
+}
+
+const char *
+tls_conn_version(struct tls *ctx)
+{
+ if (ctx->conninfo == NULL)
+ return (NULL);
+ return (ctx->conninfo->version);
+}
diff --git a/compat/libtls/tls_internal.h b/compat/libtls/tls_internal.h
new file mode 100644
index 0000000..4846a88
--- /dev/null
+++ b/compat/libtls/tls_internal.h
@@ -0,0 +1,300 @@
+/* $OpenBSD: tls_internal.h,v 1.82 2023/06/18 11:43:03 op Exp $ */
+/*
+ * Copyright (c) 2014 Jeremie Courreges-Anglas <jca@openbsd.org>
+ * Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef HEADER_TLS_INTERNAL_H
+#define HEADER_TLS_INTERNAL_H
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <openssl/ssl.h>
+
+#define TLS_CIPHERS_DEFAULT TLS_CIPHERS_COMPAT
+#define TLS_CIPHERS_COMPAT "HIGH:!aNULL"
+#define TLS_CIPHERS_LEGACY "HIGH:MEDIUM:!aNULL"
+#define TLS_CIPHERS_ALL "ALL:!aNULL:!eNULL"
+
+#define TLS_ECDHE_CURVES "X25519,P-256,P-384"
+
+union tls_addr {
+ struct in_addr ip4;
+ struct in6_addr ip6;
+};
+
+struct tls_error {
+ char *msg;
+ int num;
+ int tls;
+};
+
+struct tls_keypair {
+ struct tls_keypair *next;
+
+ char *cert_mem;
+ size_t cert_len;
+ char *key_mem;
+ size_t key_len;
+ char *ocsp_staple;
+ size_t ocsp_staple_len;
+ char *pubkey_hash;
+};
+
+#define TLS_MIN_SESSION_TIMEOUT (4)
+#define TLS_MAX_SESSION_TIMEOUT (24 * 60 * 60)
+
+#define TLS_NUM_TICKETS 4
+#define TLS_TICKET_NAME_SIZE 16
+#define TLS_TICKET_AES_SIZE 32
+#define TLS_TICKET_HMAC_SIZE 16
+
+struct tls_ticket_key {
+ /* The key_name must be 16 bytes according to -lssl */
+ unsigned char key_name[TLS_TICKET_NAME_SIZE];
+ unsigned char aes_key[TLS_TICKET_AES_SIZE];
+ unsigned char hmac_key[TLS_TICKET_HMAC_SIZE];
+ time_t time;
+};
+
+struct tls_config {
+ struct tls_error error;
+
+ int refcount;
+
+ char *alpn;
+ size_t alpn_len;
+ const char *ca_path;
+ char *ca_mem;
+ size_t ca_len;
+ const char *ciphers;
+ int ciphers_server;
+ char *crl_mem;
+ size_t crl_len;
+ int dheparams;
+ int *ecdhecurves;
+ size_t ecdhecurves_len;
+ struct tls_keypair *keypair;
+ int ocsp_require_stapling;
+ uint32_t protocols;
+ unsigned char session_id[TLS_MAX_SESSION_ID_LENGTH];
+ int session_fd;
+ int session_lifetime;
+ struct tls_ticket_key ticket_keys[TLS_NUM_TICKETS];
+ uint32_t ticket_keyrev;
+ int ticket_autorekey;
+ int verify_cert;
+ int verify_client;
+ int verify_depth;
+ int verify_name;
+ int verify_time;
+ int skip_private_key_check;
+ int use_fake_private_key;
+};
+
+struct tls_conninfo {
+ char *alpn;
+ char *cipher;
+ int cipher_strength;
+ char *servername;
+ int session_resumed;
+ char *version;
+
+ char *hash;
+ char *issuer;
+ char *subject;
+
+ uint8_t *peer_cert;
+ size_t peer_cert_len;
+
+ time_t notbefore;
+ time_t notafter;
+};
+
+#define TLS_CLIENT (1 << 0)
+#define TLS_SERVER (1 << 1)
+#define TLS_SERVER_CONN (1 << 2)
+
+#define TLS_EOF_NO_CLOSE_NOTIFY (1 << 0)
+#define TLS_CONNECTED (1 << 1)
+#define TLS_HANDSHAKE_COMPLETE (1 << 2)
+#define TLS_SSL_NEEDS_SHUTDOWN (1 << 3)
+
+struct tls_ocsp_result {
+ const char *result_msg;
+ int response_status;
+ int cert_status;
+ int crl_reason;
+ time_t this_update;
+ time_t next_update;
+ time_t revocation_time;
+};
+
+struct tls_ocsp {
+ /* responder location */
+ char *ocsp_url;
+
+ /* cert data, this struct does not own these */
+ X509 *main_cert;
+ STACK_OF(X509) *extra_certs;
+
+ struct tls_ocsp_result *ocsp_result;
+};
+
+struct tls_sni_ctx {
+ struct tls_sni_ctx *next;
+
+ struct tls_keypair *keypair;
+
+ SSL_CTX *ssl_ctx;
+ X509 *ssl_cert;
+};
+
+struct tls {
+ struct tls_config *config;
+ struct tls_keypair *keypair;
+
+ struct tls_error error;
+
+ uint32_t flags;
+ uint32_t state;
+
+ char *servername;
+ int socket;
+
+ SSL *ssl_conn;
+ SSL_CTX *ssl_ctx;
+
+ struct tls_sni_ctx *sni_ctx;
+
+ X509 *ssl_peer_cert;
+ STACK_OF(X509) *ssl_peer_chain;
+
+ struct tls_conninfo *conninfo;
+
+ struct tls_ocsp *ocsp;
+
+ tls_read_cb read_cb;
+ tls_write_cb write_cb;
+ void *cb_arg;
+};
+
+int tls_set_mem(char **_dest, size_t *_destlen, const void *_src,
+ size_t _srclen);
+int tls_set_string(const char **_dest, const char *_src);
+
+struct tls_keypair *tls_keypair_new(void);
+void tls_keypair_clear_key(struct tls_keypair *_keypair);
+void tls_keypair_free(struct tls_keypair *_keypair);
+int tls_keypair_set_cert_file(struct tls_keypair *_keypair,
+ struct tls_error *_error, const char *_cert_file);
+int tls_keypair_set_cert_mem(struct tls_keypair *_keypair,
+ struct tls_error *_error, const uint8_t *_cert, size_t _len);
+int tls_keypair_set_key_file(struct tls_keypair *_keypair,
+ struct tls_error *_error, const char *_key_file);
+int tls_keypair_set_key_mem(struct tls_keypair *_keypair,
+ struct tls_error *_error, const uint8_t *_key, size_t _len);
+int tls_keypair_set_ocsp_staple_file(struct tls_keypair *_keypair,
+ struct tls_error *_error, const char *_ocsp_file);
+int tls_keypair_set_ocsp_staple_mem(struct tls_keypair *_keypair,
+ struct tls_error *_error, const uint8_t *_staple, size_t _len);
+int tls_keypair_load_cert(struct tls_keypair *_keypair,
+ struct tls_error *_error, X509 **_cert);
+
+struct tls_sni_ctx *tls_sni_ctx_new(void);
+void tls_sni_ctx_free(struct tls_sni_ctx *sni_ctx);
+
+struct tls_config *tls_config_new_internal(void);
+
+struct tls *tls_new(void);
+struct tls *tls_server_conn(struct tls *ctx);
+
+int tls_check_name(struct tls *ctx, X509 *cert, const char *servername,
+ int *match);
+int tls_configure_server(struct tls *ctx);
+
+int tls_configure_ssl(struct tls *ctx, SSL_CTX *ssl_ctx);
+int tls_configure_ssl_keypair(struct tls *ctx, SSL_CTX *ssl_ctx,
+ struct tls_keypair *keypair, int required);
+int tls_configure_ssl_verify(struct tls *ctx, SSL_CTX *ssl_ctx, int verify);
+
+int tls_handshake_client(struct tls *ctx);
+int tls_handshake_server(struct tls *ctx);
+
+int tls_config_load_file(struct tls_error *error, const char *filetype,
+ const char *filename, char **buf, size_t *len);
+int tls_config_ticket_autorekey(struct tls_config *config);
+int tls_host_port(const char *hostport, char **host, char **port);
+
+int tls_set_cbs(struct tls *ctx,
+ tls_read_cb read_cb, tls_write_cb write_cb, void *cb_arg);
+
+void tls_error_clear(struct tls_error *error);
+int tls_error_set(struct tls_error *error, const char *fmt, ...)
+ __attribute__((__format__ (printf, 2, 3)))
+ __attribute__((__nonnull__ (2)));
+int tls_error_setx(struct tls_error *error, const char *fmt, ...)
+ __attribute__((__format__ (printf, 2, 3)))
+ __attribute__((__nonnull__ (2)));
+int tls_config_set_error(struct tls_config *cfg, const char *fmt, ...)
+ __attribute__((__format__ (printf, 2, 3)))
+ __attribute__((__nonnull__ (2)));
+int tls_config_set_errorx(struct tls_config *cfg, const char *fmt, ...)
+ __attribute__((__format__ (printf, 2, 3)))
+ __attribute__((__nonnull__ (2)));
+int tls_set_error(struct tls *ctx, const char *fmt, ...)
+ __attribute__((__format__ (printf, 2, 3)))
+ __attribute__((__nonnull__ (2)));
+int tls_set_errorx(struct tls *ctx, const char *fmt, ...)
+ __attribute__((__format__ (printf, 2, 3)))
+ __attribute__((__nonnull__ (2)));
+int tls_set_ssl_errorx(struct tls *ctx, const char *fmt, ...)
+ __attribute__((__format__ (printf, 2, 3)))
+ __attribute__((__nonnull__ (2)));
+
+int tls_ssl_error(struct tls *ctx, SSL *ssl_conn, int ssl_ret,
+ const char *prefix);
+
+int tls_conninfo_populate(struct tls *ctx);
+void tls_conninfo_free(struct tls_conninfo *conninfo);
+
+int tls_ocsp_verify_cb(SSL *ssl, void *arg);
+int tls_ocsp_stapling_cb(SSL *ssl, void *arg);
+void tls_ocsp_free(struct tls_ocsp *ctx);
+struct tls_ocsp *tls_ocsp_setup_from_peer(struct tls *ctx);
+int tls_hex_string(const unsigned char *_in, size_t _inlen, char **_out,
+ size_t *_outlen);
+int tls_cert_hash(X509 *_cert, char **_hash);
+int tls_cert_pubkey_hash(X509 *_cert, char **_hash);
+
+int tls_password_cb(char *_buf, int _size, int _rwflag, void *_u);
+
+/* XXX this function is not fully hidden so relayd can use it */
+void tls_config_skip_private_key_check(struct tls_config *config);
+void tls_config_use_fake_private_key(struct tls_config *config);
+
+/* XXX prototypes brought for OpenSMTPD libtls wrapper to OpenSSL */
+int ASN1_time_parse(const char *bytes, size_t len, struct tm *tm, int mode);
+
+#ifndef HAVE_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM
+int SSL_CTX_use_certificate_chain_mem(SSL_CTX *, void *, int);
+#endif
+
+#ifndef HAVE_SSL_CTX_LOAD_VERIFY_MEM
+int SSL_CTX_load_verify_mem(SSL_CTX *, void *, int);
+#endif
+
+#endif /* HEADER_TLS_INTERNAL_H */
diff --git a/compat/libtls/tls_keypair.c b/compat/libtls/tls_keypair.c
new file mode 100644
index 0000000..8f62f53
--- /dev/null
+++ b/compat/libtls/tls_keypair.c
@@ -0,0 +1,171 @@
+/* $OpenBSD: tls_keypair.c,v 1.8 2021/01/05 17:37:12 jsing Exp $ */
+/*
+ * Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+
+#include <tls.h>
+
+#include "tls_internal.h"
+
+struct tls_keypair *
+tls_keypair_new(void)
+{
+ return calloc(1, sizeof(struct tls_keypair));
+}
+
+static int
+tls_keypair_pubkey_hash(struct tls_keypair *keypair, struct tls_error *error)
+{
+ X509 *cert = NULL;
+ int rv = -1;
+
+ free(keypair->pubkey_hash);
+ keypair->pubkey_hash = NULL;
+
+ if (keypair->cert_mem == NULL) {
+ rv = 0;
+ goto done;
+ }
+
+ if (tls_keypair_load_cert(keypair, error, &cert) == -1)
+ goto err;
+ if (tls_cert_pubkey_hash(cert, &keypair->pubkey_hash) == -1)
+ goto err;
+
+ rv = 0;
+
+ err:
+ X509_free(cert);
+ done:
+ return (rv);
+}
+
+void
+tls_keypair_clear_key(struct tls_keypair *keypair)
+{
+ freezero(keypair->key_mem, keypair->key_len);
+ keypair->key_mem = NULL;
+ keypair->key_len = 0;
+}
+
+int
+tls_keypair_set_cert_file(struct tls_keypair *keypair, struct tls_error *error,
+ const char *cert_file)
+{
+ if (tls_config_load_file(error, "certificate", cert_file,
+ &keypair->cert_mem, &keypair->cert_len) == -1)
+ return -1;
+ return tls_keypair_pubkey_hash(keypair, error);
+}
+
+int
+tls_keypair_set_cert_mem(struct tls_keypair *keypair, struct tls_error *error,
+ const uint8_t *cert, size_t len)
+{
+ if (tls_set_mem(&keypair->cert_mem, &keypair->cert_len, cert, len) == -1)
+ return -1;
+ return tls_keypair_pubkey_hash(keypair, error);
+}
+
+int
+tls_keypair_set_key_file(struct tls_keypair *keypair, struct tls_error *error,
+ const char *key_file)
+{
+ tls_keypair_clear_key(keypair);
+ return tls_config_load_file(error, "key", key_file,
+ &keypair->key_mem, &keypair->key_len);
+}
+
+int
+tls_keypair_set_key_mem(struct tls_keypair *keypair, struct tls_error *error,
+ const uint8_t *key, size_t len)
+{
+ tls_keypair_clear_key(keypair);
+ return tls_set_mem(&keypair->key_mem, &keypair->key_len, key, len);
+}
+
+int
+tls_keypair_set_ocsp_staple_file(struct tls_keypair *keypair,
+ struct tls_error *error, const char *ocsp_file)
+{
+ return tls_config_load_file(error, "ocsp", ocsp_file,
+ &keypair->ocsp_staple, &keypair->ocsp_staple_len);
+}
+
+int
+tls_keypair_set_ocsp_staple_mem(struct tls_keypair *keypair,
+ struct tls_error *error, const uint8_t *staple, size_t len)
+{
+ return tls_set_mem(&keypair->ocsp_staple, &keypair->ocsp_staple_len,
+ staple, len);
+}
+
+void
+tls_keypair_free(struct tls_keypair *keypair)
+{
+ if (keypair == NULL)
+ return;
+
+ tls_keypair_clear_key(keypair);
+
+ free(keypair->cert_mem);
+ free(keypair->ocsp_staple);
+ free(keypair->pubkey_hash);
+
+ free(keypair);
+}
+
+int
+tls_keypair_load_cert(struct tls_keypair *keypair, struct tls_error *error,
+ X509 **cert)
+{
+ char *errstr = "unknown";
+ BIO *cert_bio = NULL;
+ unsigned long ssl_err;
+ int rv = -1;
+
+ X509_free(*cert);
+ *cert = NULL;
+
+ if (keypair->cert_mem == NULL) {
+ tls_error_set(error, "keypair has no certificate");
+ goto err;
+ }
+ if ((cert_bio = BIO_new_mem_buf(keypair->cert_mem,
+ keypair->cert_len)) == NULL) {
+ tls_error_set(error, "failed to create certificate bio");
+ goto err;
+ }
+ if ((*cert = PEM_read_bio_X509(cert_bio, NULL, tls_password_cb,
+ NULL)) == NULL) {
+ if ((ssl_err = ERR_peek_error()) != 0)
+ errstr = ERR_error_string(ssl_err, NULL);
+ tls_error_set(error, "failed to load certificate: %s", errstr);
+ goto err;
+ }
+
+ rv = 0;
+
+ err:
+ BIO_free(cert_bio);
+
+ return (rv);
+}
diff --git a/compat/libtls/tls_ocsp.c b/compat/libtls/tls_ocsp.c
new file mode 100644
index 0000000..ada3362
--- /dev/null
+++ b/compat/libtls/tls_ocsp.c
@@ -0,0 +1,465 @@
+/* $OpenBSD: tls_ocsp.c,v 1.23 2023/05/14 07:26:25 op Exp $ */
+/*
+ * Copyright (c) 2015 Marko Kreen <markokr@gmail.com>
+ * Copyright (c) 2016 Bob Beck <beck@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <string.h>
+
+#include <openssl/err.h>
+#include <openssl/ocsp.h>
+#include <openssl/x509.h>
+
+#include <tls.h>
+#include "tls_internal.h"
+
+#define MAXAGE_SEC (14*24*60*60)
+#define JITTER_SEC (60)
+
+/*
+ * State for request.
+ */
+
+static struct tls_ocsp *
+tls_ocsp_new(void)
+{
+ return (calloc(1, sizeof(struct tls_ocsp)));
+}
+
+void
+tls_ocsp_free(struct tls_ocsp *ocsp)
+{
+ if (ocsp == NULL)
+ return;
+
+ X509_free(ocsp->main_cert);
+ free(ocsp->ocsp_result);
+ free(ocsp->ocsp_url);
+
+ free(ocsp);
+}
+
+static int
+tls_ocsp_asn1_parse_time(struct tls *ctx, ASN1_GENERALIZEDTIME *gt, time_t *gt_time)
+{
+ struct tm tm;
+
+ if (gt == NULL)
+ return -1;
+ /* RFC 6960 specifies that all times in OCSP must be GENERALIZEDTIME */
+ if (ASN1_time_parse(gt->data, gt->length, &tm,
+ V_ASN1_GENERALIZEDTIME) == -1)
+ return -1;
+ if ((*gt_time = timegm(&tm)) == -1)
+ return -1;
+ return 0;
+}
+
+static int
+tls_ocsp_fill_info(struct tls *ctx, int response_status, int cert_status,
+ int crl_reason, ASN1_GENERALIZEDTIME *revtime,
+ ASN1_GENERALIZEDTIME *thisupd, ASN1_GENERALIZEDTIME *nextupd)
+{
+ struct tls_ocsp_result *info = NULL;
+
+ free(ctx->ocsp->ocsp_result);
+ ctx->ocsp->ocsp_result = NULL;
+
+ if ((info = calloc(1, sizeof (struct tls_ocsp_result))) == NULL) {
+ tls_set_error(ctx, "calloc");
+ return -1;
+ }
+ info->response_status = response_status;
+ info->cert_status = cert_status;
+ info->crl_reason = crl_reason;
+ if (info->response_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+ info->result_msg =
+ OCSP_response_status_str(info->response_status);
+ } else if (info->cert_status != V_OCSP_CERTSTATUS_REVOKED) {
+ info->result_msg = OCSP_cert_status_str(info->cert_status);
+ } else {
+ info->result_msg = OCSP_crl_reason_str(info->crl_reason);
+ }
+ info->revocation_time = info->this_update = info->next_update = -1;
+ if (revtime != NULL &&
+ tls_ocsp_asn1_parse_time(ctx, revtime, &info->revocation_time) != 0) {
+ tls_set_error(ctx,
+ "unable to parse revocation time in OCSP reply");
+ goto err;
+ }
+ if (thisupd != NULL &&
+ tls_ocsp_asn1_parse_time(ctx, thisupd, &info->this_update) != 0) {
+ tls_set_error(ctx,
+ "unable to parse this update time in OCSP reply");
+ goto err;
+ }
+ if (nextupd != NULL &&
+ tls_ocsp_asn1_parse_time(ctx, nextupd, &info->next_update) != 0) {
+ tls_set_error(ctx,
+ "unable to parse next update time in OCSP reply");
+ goto err;
+ }
+ ctx->ocsp->ocsp_result = info;
+ return 0;
+
+ err:
+ free(info);
+ return -1;
+}
+
+static OCSP_CERTID *
+tls_ocsp_get_certid(X509 *main_cert, STACK_OF(X509) *extra_certs,
+ SSL_CTX *ssl_ctx)
+{
+ X509_NAME *issuer_name;
+ X509 *issuer;
+ X509_STORE_CTX *storectx = NULL;
+ X509_OBJECT *obj = NULL;
+ OCSP_CERTID *cid = NULL;
+ X509_STORE *store;
+
+ if ((issuer_name = X509_get_issuer_name(main_cert)) == NULL)
+ goto out;
+
+ if (extra_certs != NULL) {
+ issuer = X509_find_by_subject(extra_certs, issuer_name);
+ if (issuer != NULL) {
+ cid = OCSP_cert_to_id(NULL, main_cert, issuer);
+ goto out;
+ }
+ }
+
+ if ((store = SSL_CTX_get_cert_store(ssl_ctx)) == NULL)
+ goto out;
+ if ((storectx = X509_STORE_CTX_new()) == NULL)
+ goto out;
+ if (X509_STORE_CTX_init(storectx, store, main_cert, extra_certs) != 1)
+ goto out;
+ if ((obj = X509_STORE_CTX_get_obj_by_subject(storectx, X509_LU_X509,
+ issuer_name)) == NULL)
+ goto out;
+
+ cid = OCSP_cert_to_id(NULL, main_cert, X509_OBJECT_get0_X509(obj));
+
+ out:
+ X509_STORE_CTX_free(storectx);
+ X509_OBJECT_free(obj);
+
+ return cid;
+}
+
+struct tls_ocsp *
+tls_ocsp_setup_from_peer(struct tls *ctx)
+{
+ struct tls_ocsp *ocsp = NULL;
+ STACK_OF(OPENSSL_STRING) *ocsp_urls = NULL;
+
+ if ((ocsp = tls_ocsp_new()) == NULL)
+ goto err;
+
+ /* steal state from ctx struct */
+ ocsp->main_cert = SSL_get_peer_certificate(ctx->ssl_conn);
+ ocsp->extra_certs = SSL_get_peer_cert_chain(ctx->ssl_conn);
+ if (ocsp->main_cert == NULL) {
+ tls_set_errorx(ctx, "no peer certificate for OCSP");
+ goto err;
+ }
+
+ ocsp_urls = X509_get1_ocsp(ocsp->main_cert);
+ if (ocsp_urls == NULL) {
+ tls_set_errorx(ctx, "no OCSP URLs in peer certificate");
+ goto err;
+ }
+
+ ocsp->ocsp_url = strdup(sk_OPENSSL_STRING_value(ocsp_urls, 0));
+ if (ocsp->ocsp_url == NULL) {
+ tls_set_errorx(ctx, "out of memory");
+ goto err;
+ }
+
+ X509_email_free(ocsp_urls);
+ return ocsp;
+
+ err:
+ tls_ocsp_free(ocsp);
+ X509_email_free(ocsp_urls);
+ return NULL;
+}
+
+static int
+tls_ocsp_verify_response(struct tls *ctx, OCSP_RESPONSE *resp)
+{
+ OCSP_BASICRESP *br = NULL;
+ ASN1_GENERALIZEDTIME *revtime = NULL, *thisupd = NULL, *nextupd = NULL;
+ OCSP_CERTID *cid = NULL;
+ STACK_OF(X509) *combined = NULL;
+ int response_status=0, cert_status=0, crl_reason=0;
+ int ret = -1;
+ unsigned long flags;
+
+ if ((br = OCSP_response_get1_basic(resp)) == NULL) {
+ tls_set_errorx(ctx, "cannot load ocsp reply");
+ goto err;
+ }
+
+ /*
+ * Skip validation of 'extra_certs' as this should be done
+ * already as part of main handshake.
+ */
+ flags = OCSP_TRUSTOTHER;
+
+ /* now verify */
+ if (OCSP_basic_verify(br, ctx->ocsp->extra_certs,
+ SSL_CTX_get_cert_store(ctx->ssl_ctx), flags) != 1) {
+ tls_set_errorx(ctx, "ocsp verify failed");
+ goto err;
+ }
+
+ /* signature OK, look inside */
+ response_status = OCSP_response_status(resp);
+ if (response_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+ tls_set_errorx(ctx, "ocsp verify failed: response - %s",
+ OCSP_response_status_str(response_status));
+ goto err;
+ }
+
+ cid = tls_ocsp_get_certid(ctx->ocsp->main_cert,
+ ctx->ocsp->extra_certs, ctx->ssl_ctx);
+ if (cid == NULL) {
+ tls_set_errorx(ctx, "ocsp verify failed: no issuer cert");
+ goto err;
+ }
+
+ if (OCSP_resp_find_status(br, cid, &cert_status, &crl_reason,
+ &revtime, &thisupd, &nextupd) != 1) {
+ tls_set_errorx(ctx, "ocsp verify failed: no result for cert");
+ goto err;
+ }
+
+ if (OCSP_check_validity(thisupd, nextupd, JITTER_SEC,
+ MAXAGE_SEC) != 1) {
+ tls_set_errorx(ctx,
+ "ocsp verify failed: ocsp response not current");
+ goto err;
+ }
+
+ if (tls_ocsp_fill_info(ctx, response_status, cert_status,
+ crl_reason, revtime, thisupd, nextupd) != 0)
+ goto err;
+
+ /* finally can look at status */
+ if (cert_status != V_OCSP_CERTSTATUS_GOOD && cert_status !=
+ V_OCSP_CERTSTATUS_UNKNOWN) {
+ tls_set_errorx(ctx, "ocsp verify failed: revoked cert - %s",
+ OCSP_crl_reason_str(crl_reason));
+ goto err;
+ }
+ ret = 0;
+
+ err:
+ sk_X509_free(combined);
+ OCSP_CERTID_free(cid);
+ OCSP_BASICRESP_free(br);
+ return ret;
+}
+
+/*
+ * Process a raw OCSP response from an OCSP server request.
+ * OCSP details can then be retrieved with tls_peer_ocsp_* functions.
+ * returns 0 if certificate ok, -1 otherwise.
+ */
+static int
+tls_ocsp_process_response_internal(struct tls *ctx, const unsigned char *response,
+ size_t size)
+{
+ int ret;
+ OCSP_RESPONSE *resp;
+
+ resp = d2i_OCSP_RESPONSE(NULL, &response, size);
+ if (resp == NULL) {
+ tls_ocsp_free(ctx->ocsp);
+ ctx->ocsp = NULL;
+ tls_set_error(ctx, "unable to parse OCSP response");
+ return -1;
+ }
+ ret = tls_ocsp_verify_response(ctx, resp);
+ OCSP_RESPONSE_free(resp);
+ return ret;
+}
+
+/* TLS handshake verification callback for stapled requests */
+int
+tls_ocsp_verify_cb(SSL *ssl, void *arg)
+{
+ const unsigned char *raw = NULL;
+ int size, res = -1;
+ struct tls *ctx;
+
+ if ((ctx = SSL_get_app_data(ssl)) == NULL)
+ return -1;
+
+ size = SSL_get_tlsext_status_ocsp_resp(ssl, &raw);
+ if (size <= 0) {
+ if (ctx->config->ocsp_require_stapling) {
+ tls_set_errorx(ctx, "no stapled OCSP response provided");
+ return 0;
+ }
+ return 1;
+ }
+
+ tls_ocsp_free(ctx->ocsp);
+ if ((ctx->ocsp = tls_ocsp_setup_from_peer(ctx)) == NULL)
+ return 0;
+
+ if (ctx->config->verify_cert == 0 || ctx->config->verify_time == 0)
+ return 1;
+
+ res = tls_ocsp_process_response_internal(ctx, raw, size);
+
+ return (res == 0) ? 1 : 0;
+}
+
+
+/* Staple the OCSP information in ctx->ocsp to the server handshake. */
+int
+tls_ocsp_stapling_cb(SSL *ssl, void *arg)
+{
+ int ret = SSL_TLSEXT_ERR_ALERT_FATAL;
+ unsigned char *ocsp_staple = NULL;
+ struct tls *ctx;
+
+ if ((ctx = SSL_get_app_data(ssl)) == NULL)
+ goto err;
+
+ if (ctx->keypair == NULL || ctx->keypair->ocsp_staple == NULL ||
+ ctx->keypair->ocsp_staple_len == 0)
+ return SSL_TLSEXT_ERR_NOACK;
+
+ if ((ocsp_staple = malloc(ctx->keypair->ocsp_staple_len)) == NULL)
+ goto err;
+
+ memcpy(ocsp_staple, ctx->keypair->ocsp_staple,
+ ctx->keypair->ocsp_staple_len);
+
+ if (SSL_set_tlsext_status_ocsp_resp(ctx->ssl_conn, ocsp_staple,
+ ctx->keypair->ocsp_staple_len) != 1)
+ goto err;
+
+ ret = SSL_TLSEXT_ERR_OK;
+ err:
+ if (ret != SSL_TLSEXT_ERR_OK)
+ free(ocsp_staple);
+
+ return ret;
+}
+
+/*
+ * Public API
+ */
+
+/* Retrieve OCSP URL from peer certificate, if present. */
+const char *
+tls_peer_ocsp_url(struct tls *ctx)
+{
+ if (ctx->ocsp == NULL)
+ return NULL;
+ return ctx->ocsp->ocsp_url;
+}
+
+const char *
+tls_peer_ocsp_result(struct tls *ctx)
+{
+ if (ctx->ocsp == NULL)
+ return NULL;
+ if (ctx->ocsp->ocsp_result == NULL)
+ return NULL;
+ return ctx->ocsp->ocsp_result->result_msg;
+}
+
+int
+tls_peer_ocsp_response_status(struct tls *ctx)
+{
+ if (ctx->ocsp == NULL)
+ return -1;
+ if (ctx->ocsp->ocsp_result == NULL)
+ return -1;
+ return ctx->ocsp->ocsp_result->response_status;
+}
+
+int
+tls_peer_ocsp_cert_status(struct tls *ctx)
+{
+ if (ctx->ocsp == NULL)
+ return -1;
+ if (ctx->ocsp->ocsp_result == NULL)
+ return -1;
+ return ctx->ocsp->ocsp_result->cert_status;
+}
+
+int
+tls_peer_ocsp_crl_reason(struct tls *ctx)
+{
+ if (ctx->ocsp == NULL)
+ return -1;
+ if (ctx->ocsp->ocsp_result == NULL)
+ return -1;
+ return ctx->ocsp->ocsp_result->crl_reason;
+}
+
+time_t
+tls_peer_ocsp_this_update(struct tls *ctx)
+{
+ if (ctx->ocsp == NULL)
+ return -1;
+ if (ctx->ocsp->ocsp_result == NULL)
+ return -1;
+ return ctx->ocsp->ocsp_result->this_update;
+}
+
+time_t
+tls_peer_ocsp_next_update(struct tls *ctx)
+{
+ if (ctx->ocsp == NULL)
+ return -1;
+ if (ctx->ocsp->ocsp_result == NULL)
+ return -1;
+ return ctx->ocsp->ocsp_result->next_update;
+}
+
+time_t
+tls_peer_ocsp_revocation_time(struct tls *ctx)
+{
+ if (ctx->ocsp == NULL)
+ return -1;
+ if (ctx->ocsp->ocsp_result == NULL)
+ return -1;
+ return ctx->ocsp->ocsp_result->revocation_time;
+}
+
+int
+tls_ocsp_process_response(struct tls *ctx, const unsigned char *response,
+ size_t size)
+{
+ if ((ctx->state & TLS_HANDSHAKE_COMPLETE) == 0)
+ return -1;
+ return tls_ocsp_process_response_internal(ctx, response, size);
+}
diff --git a/compat/libtls/tls_peer.c b/compat/libtls/tls_peer.c
new file mode 100644
index 0000000..d5c550a
--- /dev/null
+++ b/compat/libtls/tls_peer.c
@@ -0,0 +1,101 @@
+/* $OpenBSD: tls_peer.c,v 1.8 2017/04/10 17:11:13 jsing Exp $ */
+/*
+ * Copyright (c) 2015 Joel Sing <jsing@openbsd.org>
+ * Copyright (c) 2015 Bob Beck <beck@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+
+#include <openssl/x509.h>
+
+#include <tls.h>
+#include "tls_internal.h"
+
+const char *
+tls_peer_cert_hash(struct tls *ctx)
+{
+ if (ctx->conninfo == NULL)
+ return (NULL);
+ return (ctx->conninfo->hash);
+}
+const char *
+tls_peer_cert_issuer(struct tls *ctx)
+{
+ if (ctx->conninfo == NULL)
+ return (NULL);
+ return (ctx->conninfo->issuer);
+}
+
+const char *
+tls_peer_cert_subject(struct tls *ctx)
+{
+ if (ctx->conninfo == NULL)
+ return (NULL);
+ return (ctx->conninfo->subject);
+}
+
+int
+tls_peer_cert_provided(struct tls *ctx)
+{
+ return (ctx->ssl_peer_cert != NULL);
+}
+
+int
+tls_peer_cert_contains_name(struct tls *ctx, const char *name)
+{
+ int match;
+
+ if (ctx->ssl_peer_cert == NULL)
+ return (0);
+
+ if (tls_check_name(ctx, ctx->ssl_peer_cert, name, &match) == -1)
+ return (0);
+
+ return (match);
+}
+
+time_t
+tls_peer_cert_notbefore(struct tls *ctx)
+{
+ if (ctx->ssl_peer_cert == NULL)
+ return (-1);
+ if (ctx->conninfo == NULL)
+ return (-1);
+ return (ctx->conninfo->notbefore);
+}
+
+time_t
+tls_peer_cert_notafter(struct tls *ctx)
+{
+ if (ctx->ssl_peer_cert == NULL)
+ return (-1);
+ if (ctx->conninfo == NULL)
+ return (-1);
+ return (ctx->conninfo->notafter);
+}
+
+const uint8_t *
+tls_peer_cert_chain_pem(struct tls *ctx, size_t *size)
+{
+ if (ctx->ssl_peer_cert == NULL)
+ return (NULL);
+ if (ctx->conninfo == NULL)
+ return (NULL);
+ *size = ctx->conninfo->peer_cert_len;
+ return (ctx->conninfo->peer_cert);
+}
+
diff --git a/compat/libtls/tls_server.c b/compat/libtls/tls_server.c
new file mode 100644
index 0000000..b374012
--- /dev/null
+++ b/compat/libtls/tls_server.c
@@ -0,0 +1,471 @@
+/* $OpenBSD: tls_server.c,v 1.49 2023/05/14 07:26:25 op Exp $ */
+/*
+ * Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <sys/socket.h>
+
+#include <arpa/inet.h>
+
+#include <string.h>
+
+#include <openssl/asn1.h>
+#include <openssl/ec.h>
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+
+#include <tls.h>
+#include "tls_internal.h"
+
+struct tls *
+tls_server(void)
+{
+ struct tls *ctx;
+
+ if (tls_init() == -1)
+ return (NULL);
+
+ if ((ctx = tls_new()) == NULL)
+ return (NULL);
+
+ ctx->flags |= TLS_SERVER;
+
+ return (ctx);
+}
+
+struct tls *
+tls_server_conn(struct tls *ctx)
+{
+ struct tls *conn_ctx;
+
+ if ((conn_ctx = tls_new()) == NULL)
+ return (NULL);
+
+ conn_ctx->flags |= TLS_SERVER_CONN;
+
+ ctx->config->refcount++;
+
+ conn_ctx->config = ctx->config;
+ conn_ctx->keypair = ctx->config->keypair;
+
+ return (conn_ctx);
+}
+
+static int
+tls_server_alpn_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen,
+ const unsigned char *in, unsigned int inlen, void *arg)
+{
+ struct tls *ctx = arg;
+
+ if (SSL_select_next_proto((unsigned char**)out, outlen,
+ ctx->config->alpn, ctx->config->alpn_len, in, inlen) ==
+ OPENSSL_NPN_NEGOTIATED)
+ return (SSL_TLSEXT_ERR_OK);
+
+ return (SSL_TLSEXT_ERR_NOACK);
+}
+
+static int
+tls_servername_cb(SSL *ssl, int *al, void *arg)
+{
+ struct tls *ctx = (struct tls *)arg;
+ struct tls_sni_ctx *sni_ctx;
+ union tls_addr addrbuf;
+ struct tls *conn_ctx;
+ const char *name;
+ int match;
+
+ if ((conn_ctx = SSL_get_app_data(ssl)) == NULL)
+ goto err;
+
+ if ((name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name)) ==
+ NULL) {
+ /*
+ * The servername callback gets called even when there is no
+ * TLS servername extension provided by the client. Sigh!
+ */
+ return (SSL_TLSEXT_ERR_NOACK);
+ }
+
+ /*
+ * Per RFC 6066 section 3: ensure that name is not an IP literal.
+ *
+ * While we should treat this as an error, a number of clients
+ * (Python, Ruby and Safari) are not RFC compliant. To avoid handshake
+ * failures, pretend that we did not receive the extension.
+ */
+ if (inet_pton(AF_INET, name, &addrbuf) == 1 ||
+ inet_pton(AF_INET6, name, &addrbuf) == 1)
+ return (SSL_TLSEXT_ERR_NOACK);
+
+ free(conn_ctx->servername);
+ if ((conn_ctx->servername = strdup(name)) == NULL)
+ goto err;
+
+ /* Find appropriate SSL context for requested servername. */
+ for (sni_ctx = ctx->sni_ctx; sni_ctx != NULL; sni_ctx = sni_ctx->next) {
+ if (tls_check_name(ctx, sni_ctx->ssl_cert, name,
+ &match) == -1)
+ goto err;
+ if (match) {
+ conn_ctx->keypair = sni_ctx->keypair;
+ SSL_set_SSL_CTX(conn_ctx->ssl_conn, sni_ctx->ssl_ctx);
+ return (SSL_TLSEXT_ERR_OK);
+ }
+ }
+
+ /* No match, use the existing context/certificate. */
+ return (SSL_TLSEXT_ERR_OK);
+
+ err:
+ /*
+ * There is no way to tell libssl that an internal failure occurred.
+ * The only option we have is to return a fatal alert.
+ */
+ *al = SSL_AD_INTERNAL_ERROR;
+ return (SSL_TLSEXT_ERR_ALERT_FATAL);
+}
+
+static struct tls_ticket_key *
+tls_server_ticket_key(struct tls_config *config, unsigned char *keyname)
+{
+ struct tls_ticket_key *key = NULL;
+ time_t now;
+ int i;
+
+ now = time(NULL);
+ if (config->ticket_autorekey == 1) {
+ if (now - 3 * (config->session_lifetime / 4) >
+ config->ticket_keys[0].time) {
+ if (tls_config_ticket_autorekey(config) == -1)
+ return (NULL);
+ }
+ }
+ for (i = 0; i < TLS_NUM_TICKETS; i++) {
+ struct tls_ticket_key *tk = &config->ticket_keys[i];
+ if (now - config->session_lifetime > tk->time)
+ continue;
+ if (keyname == NULL || timingsafe_memcmp(keyname,
+ tk->key_name, sizeof(tk->key_name)) == 0) {
+ key = tk;
+ break;
+ }
+ }
+ return (key);
+}
+
+static int
+tls_server_ticket_cb(SSL *ssl, unsigned char *keyname, unsigned char *iv,
+ EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int mode)
+{
+ struct tls_ticket_key *key;
+ struct tls *tls_ctx;
+
+ if ((tls_ctx = SSL_get_app_data(ssl)) == NULL)
+ return (-1);
+
+ if (mode == 1) {
+ /* create new session */
+ key = tls_server_ticket_key(tls_ctx->config, NULL);
+ if (key == NULL) {
+ tls_set_errorx(tls_ctx, "no valid ticket key found");
+ return (-1);
+ }
+
+ memcpy(keyname, key->key_name, sizeof(key->key_name));
+ arc4random_buf(iv, EVP_MAX_IV_LENGTH);
+ if (!EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL,
+ key->aes_key, iv)) {
+ tls_set_errorx(tls_ctx, "failed to init encrypt");
+ return (-1);
+ }
+ if (!HMAC_Init_ex(hctx, key->hmac_key, sizeof(key->hmac_key),
+ EVP_sha256(), NULL)) {
+ tls_set_errorx(tls_ctx, "failed to init hmac");
+ return (-1);
+ }
+ return (0);
+ } else {
+ /* get key by name */
+ key = tls_server_ticket_key(tls_ctx->config, keyname);
+ if (key == NULL)
+ return (0);
+
+ if (!EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL,
+ key->aes_key, iv)) {
+ tls_set_errorx(tls_ctx, "failed to init decrypt");
+ return (-1);
+ }
+ if (!HMAC_Init_ex(hctx, key->hmac_key, sizeof(key->hmac_key),
+ EVP_sha256(), NULL)) {
+ tls_set_errorx(tls_ctx, "failed to init hmac");
+ return (-1);
+ }
+
+ /* time to renew the ticket? is it the primary key? */
+ if (key != &tls_ctx->config->ticket_keys[0])
+ return (2);
+ return (1);
+ }
+}
+
+static int
+tls_configure_server_ssl(struct tls *ctx, SSL_CTX **ssl_ctx,
+ struct tls_keypair *keypair)
+{
+ SSL_CTX_free(*ssl_ctx);
+
+ if ((*ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
+ tls_set_errorx(ctx, "ssl context failure");
+ goto err;
+ }
+
+#ifdef SSL_OP_NO_CLIENT_RENEGOTIATION
+ SSL_CTX_set_options(*ssl_ctx, SSL_OP_NO_CLIENT_RENEGOTIATION);
+#endif
+
+ if (SSL_CTX_set_tlsext_servername_callback(*ssl_ctx,
+ tls_servername_cb) != 1) {
+ tls_set_error(ctx, "failed to set servername callback");
+ goto err;
+ }
+ if (SSL_CTX_set_tlsext_servername_arg(*ssl_ctx, ctx) != 1) {
+ tls_set_error(ctx, "failed to set servername callback arg");
+ goto err;
+ }
+
+ if (tls_configure_ssl(ctx, *ssl_ctx) != 0)
+ goto err;
+ if (tls_configure_ssl_keypair(ctx, *ssl_ctx, keypair, 1) != 0)
+ goto err;
+ if (ctx->config->verify_client != 0) {
+ int verify = SSL_VERIFY_PEER;
+ if (ctx->config->verify_client == 1)
+ verify |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+ if (tls_configure_ssl_verify(ctx, *ssl_ctx, verify) == -1)
+ goto err;
+ }
+
+ if (ctx->config->alpn != NULL)
+ SSL_CTX_set_alpn_select_cb(*ssl_ctx, tls_server_alpn_cb,
+ ctx);
+
+ if (ctx->config->dheparams == -1)
+ SSL_CTX_set_dh_auto(*ssl_ctx, 1);
+ else if (ctx->config->dheparams == 1024)
+ SSL_CTX_set_dh_auto(*ssl_ctx, 2);
+
+ if (ctx->config->ecdhecurves != NULL) {
+ SSL_CTX_set_ecdh_auto(*ssl_ctx, 1);
+ if (SSL_CTX_set1_groups(*ssl_ctx, ctx->config->ecdhecurves,
+ ctx->config->ecdhecurves_len) != 1) {
+ tls_set_errorx(ctx, "failed to set ecdhe curves");
+ goto err;
+ }
+ }
+
+ if (ctx->config->ciphers_server == 1)
+ SSL_CTX_set_options(*ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
+
+ if (SSL_CTX_set_tlsext_status_cb(*ssl_ctx, tls_ocsp_stapling_cb) != 1) {
+ tls_set_errorx(ctx, "failed to add OCSP stapling callback");
+ goto err;
+ }
+
+ if (ctx->config->session_lifetime > 0) {
+ /* set the session lifetime and enable tickets */
+ SSL_CTX_set_timeout(*ssl_ctx, ctx->config->session_lifetime);
+ SSL_CTX_clear_options(*ssl_ctx, SSL_OP_NO_TICKET);
+ if (!SSL_CTX_set_tlsext_ticket_key_cb(*ssl_ctx,
+ tls_server_ticket_cb)) {
+ tls_set_error(ctx,
+ "failed to set the TLS ticket callback");
+ goto err;
+ }
+ }
+
+ if (SSL_CTX_set_session_id_context(*ssl_ctx, ctx->config->session_id,
+ sizeof(ctx->config->session_id)) != 1) {
+ tls_set_error(ctx, "failed to set session id context");
+ goto err;
+ }
+
+ return (0);
+
+ err:
+ SSL_CTX_free(*ssl_ctx);
+ *ssl_ctx = NULL;
+
+ return (-1);
+}
+
+static int
+tls_configure_server_sni(struct tls *ctx)
+{
+ struct tls_sni_ctx **sni_ctx;
+ struct tls_keypair *kp;
+
+ if (ctx->config->keypair->next == NULL)
+ return (0);
+
+ /* Set up additional SSL contexts for SNI. */
+ sni_ctx = &ctx->sni_ctx;
+ for (kp = ctx->config->keypair->next; kp != NULL; kp = kp->next) {
+ if ((*sni_ctx = tls_sni_ctx_new()) == NULL) {
+ tls_set_errorx(ctx, "out of memory");
+ goto err;
+ }
+ (*sni_ctx)->keypair = kp;
+ if (tls_configure_server_ssl(ctx, &(*sni_ctx)->ssl_ctx, kp) == -1)
+ goto err;
+ if (tls_keypair_load_cert(kp, &ctx->error,
+ &(*sni_ctx)->ssl_cert) == -1)
+ goto err;
+ sni_ctx = &(*sni_ctx)->next;
+ }
+
+ return (0);
+
+ err:
+ return (-1);
+}
+
+int
+tls_configure_server(struct tls *ctx)
+{
+ if (tls_configure_server_ssl(ctx, &ctx->ssl_ctx,
+ ctx->config->keypair) == -1)
+ goto err;
+ if (tls_configure_server_sni(ctx) == -1)
+ goto err;
+
+ return (0);
+
+ err:
+ return (-1);
+}
+
+static struct tls *
+tls_accept_common(struct tls *ctx)
+{
+ struct tls *conn_ctx = NULL;
+
+ if ((ctx->flags & TLS_SERVER) == 0) {
+ tls_set_errorx(ctx, "not a server context");
+ goto err;
+ }
+
+ if ((conn_ctx = tls_server_conn(ctx)) == NULL) {
+ tls_set_errorx(ctx, "connection context failure");
+ goto err;
+ }
+
+ if ((conn_ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) {
+ tls_set_errorx(ctx, "ssl failure");
+ goto err;
+ }
+
+ if (SSL_set_app_data(conn_ctx->ssl_conn, conn_ctx) != 1) {
+ tls_set_errorx(ctx, "ssl application data failure");
+ goto err;
+ }
+
+ return conn_ctx;
+
+ err:
+ tls_free(conn_ctx);
+
+ return (NULL);
+}
+
+int
+tls_accept_socket(struct tls *ctx, struct tls **cctx, int s)
+{
+ return (tls_accept_fds(ctx, cctx, s, s));
+}
+
+int
+tls_accept_fds(struct tls *ctx, struct tls **cctx, int fd_read, int fd_write)
+{
+ struct tls *conn_ctx;
+
+ if ((conn_ctx = tls_accept_common(ctx)) == NULL)
+ goto err;
+
+ if (SSL_set_rfd(conn_ctx->ssl_conn, fd_read) != 1 ||
+ SSL_set_wfd(conn_ctx->ssl_conn, fd_write) != 1) {
+ tls_set_errorx(ctx, "ssl file descriptor failure");
+ goto err;
+ }
+
+ *cctx = conn_ctx;
+
+ return (0);
+ err:
+ tls_free(conn_ctx);
+ *cctx = NULL;
+
+ return (-1);
+}
+
+int
+tls_accept_cbs(struct tls *ctx, struct tls **cctx,
+ tls_read_cb read_cb, tls_write_cb write_cb, void *cb_arg)
+{
+ struct tls *conn_ctx;
+
+ if ((conn_ctx = tls_accept_common(ctx)) == NULL)
+ goto err;
+
+ if (tls_set_cbs(conn_ctx, read_cb, write_cb, cb_arg) != 0)
+ goto err;
+
+ *cctx = conn_ctx;
+
+ return (0);
+ err:
+ tls_free(conn_ctx);
+ *cctx = NULL;
+
+ return (-1);
+}
+
+int
+tls_handshake_server(struct tls *ctx)
+{
+ int ssl_ret;
+ int rv = -1;
+
+ if ((ctx->flags & TLS_SERVER_CONN) == 0) {
+ tls_set_errorx(ctx, "not a server connection context");
+ goto err;
+ }
+
+ ctx->state |= TLS_SSL_NEEDS_SHUTDOWN;
+
+ ERR_clear_error();
+ if ((ssl_ret = SSL_accept(ctx->ssl_conn)) != 1) {
+ rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "handshake");
+ goto err;
+ }
+
+ ctx->state |= TLS_HANDSHAKE_COMPLETE;
+ rv = 0;
+
+ err:
+ return (rv);
+}
diff --git a/compat/libtls/tls_util.c b/compat/libtls/tls_util.c
new file mode 100644
index 0000000..dc54af8
--- /dev/null
+++ b/compat/libtls/tls_util.c
@@ -0,0 +1,228 @@
+/* $OpenBSD: tls_util.c,v 1.16 2023/05/14 07:26:25 op Exp $ */
+/*
+ * Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
+ * Copyright (c) 2014 Ted Unangst <tedu@openbsd.org>
+ * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <sys/stat.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "tls.h"
+#include "tls_internal.h"
+
+static void *
+memdup(const void *in, size_t len)
+{
+ void *out;
+
+ if ((out = malloc(len)) == NULL)
+ return NULL;
+ memcpy(out, in, len);
+ return out;
+}
+
+int
+tls_set_mem(char **dest, size_t *destlen, const void *src, size_t srclen)
+{
+ free(*dest);
+ *dest = NULL;
+ *destlen = 0;
+ if (src != NULL) {
+ if ((*dest = memdup(src, srclen)) == NULL)
+ return -1;
+ *destlen = srclen;
+ }
+ return 0;
+}
+
+int
+tls_set_string(const char **dest, const char *src)
+{
+ free((char *)*dest);
+ *dest = NULL;
+ if (src != NULL)
+ if ((*dest = strdup(src)) == NULL)
+ return -1;
+ return 0;
+}
+
+/*
+ * Extract the host and port from a colon separated value. For a literal IPv6
+ * address the address must be contained with square braces. If a host and
+ * port are successfully extracted, the function will return 0 and the
+ * caller is responsible for freeing the host and port. If no port is found
+ * then the function will return 1, with both host and port being NULL.
+ * On memory allocation failure -1 will be returned.
+ */
+int
+tls_host_port(const char *hostport, char **host, char **port)
+{
+ char *h, *p, *s;
+ int rv = 1;
+
+ *host = NULL;
+ *port = NULL;
+
+ if ((s = strdup(hostport)) == NULL)
+ goto err;
+
+ h = p = s;
+
+ /* See if this is an IPv6 literal with square braces. */
+ if (p[0] == '[') {
+ h++;
+ if ((p = strchr(s, ']')) == NULL)
+ goto done;
+ *p++ = '\0';
+ }
+
+ /* Find the port separator. */
+ if ((p = strchr(p, ':')) == NULL)
+ goto done;
+
+ /* If there is another separator then we have issues. */
+ if (strchr(p + 1, ':') != NULL)
+ goto done;
+
+ *p++ = '\0';
+
+ if (asprintf(host, "%s", h) == -1) {
+ *host = NULL;
+ goto err;
+ }
+ if (asprintf(port, "%s", p) == -1) {
+ *port = NULL;
+ goto err;
+ }
+
+ rv = 0;
+ goto done;
+
+ err:
+ free(*host);
+ *host = NULL;
+ free(*port);
+ *port = NULL;
+ rv = -1;
+
+ done:
+ free(s);
+
+ return (rv);
+}
+
+int
+tls_password_cb(char *buf, int size, int rwflag, void *u)
+{
+ size_t len;
+
+ if (size < 0)
+ return (0);
+
+ if (u == NULL) {
+ memset(buf, 0, size);
+ return (0);
+ }
+
+ if ((len = strlcpy(buf, u, size)) >= (size_t)size)
+ return (0);
+
+ return (len);
+}
+
+uint8_t *
+tls_load_file(const char *name, size_t *len, char *password)
+{
+ FILE *fp;
+ EVP_PKEY *key = NULL;
+ BIO *bio = NULL;
+ char *data;
+ uint8_t *buf = NULL;
+ struct stat st;
+ size_t size = 0;
+ int fd = -1;
+ ssize_t n;
+
+ *len = 0;
+
+ if ((fd = open(name, O_RDONLY)) == -1)
+ return (NULL);
+
+ /* Just load the file into memory without decryption */
+ if (password == NULL) {
+ if (fstat(fd, &st) != 0)
+ goto err;
+ if (st.st_size < 0)
+ goto err;
+ size = (size_t)st.st_size;
+ if ((buf = malloc(size)) == NULL)
+ goto err;
+ n = read(fd, buf, size);
+ if (n < 0 || (size_t)n != size)
+ goto err;
+ close(fd);
+ goto done;
+ }
+
+ /* Or read the (possibly) encrypted key from file */
+ if ((fp = fdopen(fd, "r")) == NULL)
+ goto err;
+ fd = -1;
+
+ key = PEM_read_PrivateKey(fp, NULL, tls_password_cb, password);
+ fclose(fp);
+ if (key == NULL)
+ goto err;
+
+ /* Write unencrypted key to memory buffer */
+ if ((bio = BIO_new(BIO_s_mem())) == NULL)
+ goto err;
+ if (!PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, NULL, NULL))
+ goto err;
+ if ((size = BIO_get_mem_data(bio, &data)) <= 0)
+ goto err;
+ if ((buf = malloc(size)) == NULL)
+ goto err;
+ memcpy(buf, data, size);
+
+ BIO_free_all(bio);
+ EVP_PKEY_free(key);
+
+ done:
+ *len = size;
+ return (buf);
+
+ err:
+ if (fd != -1)
+ close(fd);
+ freezero(buf, size);
+ BIO_free_all(bio);
+ EVP_PKEY_free(key);
+
+ return (NULL);
+}
+
+void
+tls_unload_file(uint8_t *buf, size_t len)
+{
+ freezero(buf, len);
+}
diff --git a/compat/libtls/tls_verify.c b/compat/libtls/tls_verify.c
new file mode 100644
index 0000000..053dc2f
--- /dev/null
+++ b/compat/libtls/tls_verify.c
@@ -0,0 +1,286 @@
+/* $OpenBSD: tls_verify.c,v 1.23 2023/05/11 07:35:27 tb Exp $ */
+/*
+ * Copyright (c) 2014 Jeremie Courreges-Anglas <jca@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <sys/socket.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <string.h>
+
+#include <openssl/x509v3.h>
+
+#include <tls.h>
+#include "tls_internal.h"
+
+static int
+tls_match_name(const char *cert_name, const char *name)
+{
+ const char *cert_domain, *domain, *next_dot;
+
+ if (strcasecmp(cert_name, name) == 0)
+ return 0;
+
+ /* Wildcard match? */
+ if (cert_name[0] == '*') {
+ /*
+ * Valid wildcards:
+ * - "*.domain.tld"
+ * - "*.sub.domain.tld"
+ * - etc.
+ * Reject "*.tld".
+ * No attempt to prevent the use of eg. "*.co.uk".
+ */
+ cert_domain = &cert_name[1];
+ /* Disallow "*" */
+ if (cert_domain[0] == '\0')
+ return -1;
+ /* Disallow "*foo" */
+ if (cert_domain[0] != '.')
+ return -1;
+ /* Disallow "*.." */
+ if (cert_domain[1] == '.')
+ return -1;
+ next_dot = strchr(&cert_domain[1], '.');
+ /* Disallow "*.bar" */
+ if (next_dot == NULL)
+ return -1;
+ /* Disallow "*.bar.." */
+ if (next_dot[1] == '.')
+ return -1;
+
+ domain = strchr(name, '.');
+
+ /* No wildcard match against a name with no host part. */
+ if (name[0] == '.')
+ return -1;
+ /* No wildcard match against a name with no domain part. */
+ if (domain == NULL || strlen(domain) == 1)
+ return -1;
+
+ if (strcasecmp(cert_domain, domain) == 0)
+ return 0;
+ }
+
+ return -1;
+}
+
+/*
+ * See RFC 5280 section 4.2.1.6 for SubjectAltName details.
+ * alt_match is set to 1 if a matching alternate name is found.
+ * alt_exists is set to 1 if any known alternate name exists in the certificate.
+ */
+static int
+tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name,
+ int *alt_match, int *alt_exists)
+{
+ STACK_OF(GENERAL_NAME) *altname_stack = NULL;
+ union tls_addr addrbuf;
+ int addrlen, type;
+ int count, i;
+ int rv = 0;
+
+ *alt_match = 0;
+ *alt_exists = 0;
+
+ altname_stack = X509_get_ext_d2i(cert, NID_subject_alt_name,
+ NULL, NULL);
+ if (altname_stack == NULL)
+ return 0;
+
+ if (inet_pton(AF_INET, name, &addrbuf) == 1) {
+ type = GEN_IPADD;
+ addrlen = 4;
+ } else if (inet_pton(AF_INET6, name, &addrbuf) == 1) {
+ type = GEN_IPADD;
+ addrlen = 16;
+ } else {
+ type = GEN_DNS;
+ addrlen = 0;
+ }
+
+ count = sk_GENERAL_NAME_num(altname_stack);
+ for (i = 0; i < count; i++) {
+ GENERAL_NAME *altname;
+
+ altname = sk_GENERAL_NAME_value(altname_stack, i);
+
+ if (altname->type == GEN_DNS || altname->type == GEN_IPADD)
+ *alt_exists = 1;
+
+ if (altname->type != type)
+ continue;
+
+ if (type == GEN_DNS) {
+ const unsigned char *data;
+ int format, len;
+
+ format = ASN1_STRING_type(altname->d.dNSName);
+ if (format == V_ASN1_IA5STRING) {
+ data = ASN1_STRING_get0_data(altname->d.dNSName);
+ len = ASN1_STRING_length(altname->d.dNSName);
+
+ if (len < 0 || (size_t)len != strlen(data)) {
+ tls_set_errorx(ctx,
+ "error verifying name '%s': "
+ "NUL byte in subjectAltName, "
+ "probably a malicious certificate",
+ name);
+ rv = -1;
+ break;
+ }
+
+ /*
+ * Per RFC 5280 section 4.2.1.6:
+ * " " is a legal domain name, but that
+ * dNSName must be rejected.
+ */
+ if (strcmp(data, " ") == 0) {
+ tls_set_errorx(ctx,
+ "error verifying name '%s': "
+ "a dNSName of \" \" must not be "
+ "used", name);
+ rv = -1;
+ break;
+ }
+
+ if (tls_match_name(data, name) == 0) {
+ *alt_match = 1;
+ break;
+ }
+ } else {
+#ifdef DEBUG
+ fprintf(stdout, "%s: unhandled subjectAltName "
+ "dNSName encoding (%d)\n", getprogname(),
+ format);
+#endif
+ }
+
+ } else if (type == GEN_IPADD) {
+ const unsigned char *data;
+ int datalen;
+
+ datalen = ASN1_STRING_length(altname->d.iPAddress);
+ data = ASN1_STRING_get0_data(altname->d.iPAddress);
+
+ if (datalen < 0) {
+ tls_set_errorx(ctx,
+ "Unexpected negative length for an "
+ "IP address: %d", datalen);
+ rv = -1;
+ break;
+ }
+
+ /*
+ * Per RFC 5280 section 4.2.1.6:
+ * IPv4 must use 4 octets and IPv6 must use 16 octets.
+ */
+ if (datalen == addrlen &&
+ memcmp(data, &addrbuf, addrlen) == 0) {
+ *alt_match = 1;
+ break;
+ }
+ }
+ }
+
+ sk_GENERAL_NAME_pop_free(altname_stack, GENERAL_NAME_free);
+ return rv;
+}
+
+static int
+tls_check_common_name(struct tls *ctx, X509 *cert, const char *name,
+ int *cn_match)
+{
+ X509_NAME *subject_name;
+ char *common_name = NULL;
+ union tls_addr addrbuf;
+ int common_name_len;
+ int rv = -1;
+
+ *cn_match = 0;
+
+ subject_name = X509_get_subject_name(cert);
+ if (subject_name == NULL)
+ goto done;
+
+ common_name_len = X509_NAME_get_text_by_NID(subject_name,
+ NID_commonName, NULL, 0);
+ if (common_name_len < 0)
+ goto done;
+
+ common_name = calloc(common_name_len + 1, 1);
+ if (common_name == NULL) {
+ tls_set_error(ctx, "out of memory");
+ goto err;
+ }
+
+ X509_NAME_get_text_by_NID(subject_name, NID_commonName, common_name,
+ common_name_len + 1);
+
+ /* NUL bytes in CN? */
+ if (common_name_len < 0 ||
+ (size_t)common_name_len != strlen(common_name)) {
+ tls_set_errorx(ctx, "error verifying name '%s': "
+ "NUL byte in Common Name field, "
+ "probably a malicious certificate", name);
+ goto err;
+ }
+
+ /*
+ * We don't want to attempt wildcard matching against IP addresses,
+ * so perform a simple comparison here.
+ */
+ if (inet_pton(AF_INET, name, &addrbuf) == 1 ||
+ inet_pton(AF_INET6, name, &addrbuf) == 1) {
+ if (strcmp(common_name, name) == 0)
+ *cn_match = 1;
+ goto done;
+ }
+
+ if (tls_match_name(common_name, name) == 0)
+ *cn_match = 1;
+
+ done:
+ rv = 0;
+
+ err:
+ free(common_name);
+ return rv;
+}
+
+int
+tls_check_name(struct tls *ctx, X509 *cert, const char *name, int *match)
+{
+ int alt_exists;
+
+ *match = 0;
+
+ if (tls_check_subject_altname(ctx, cert, name, match,
+ &alt_exists) == -1)
+ return -1;
+
+ /*
+ * As per RFC 6125 section 6.4.4, if any known alternate name existed
+ * in the certificate, we do not attempt to match on the CN.
+ */
+ if (*match || alt_exists)
+ return 0;
+
+ return tls_check_common_name(ctx, cert, name, match);
+}
diff --git a/compat/timingsafe_memcmp.c b/compat/timingsafe_memcmp.c
new file mode 100644
index 0000000..927208b
--- /dev/null
+++ b/compat/timingsafe_memcmp.c
@@ -0,0 +1,48 @@
+/* $OpenBSD: timingsafe_memcmp.c,v 1.2 2015/08/31 02:53:57 guenther Exp $ */
+/*
+ * Copyright (c) 2014 Google Inc.
+ *
+ * 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 "../config.h"
+
+#include <limits.h>
+#include <string.h>
+
+int
+timingsafe_memcmp(const void *b1, const void *b2, size_t len)
+{
+ const unsigned char *p1 = b1, *p2 = b2;
+ size_t i;
+ int res = 0, done = 0;
+
+ for (i = 0; i < len; i++) {
+ /* lt is -1 if p1[i] < p2[i]; else 0. */
+ int lt = (p1[i] - p2[i]) >> CHAR_BIT;
+
+ /* gt is -1 if p1[i] > p2[i]; else 0. */
+ int gt = (p2[i] - p1[i]) >> CHAR_BIT;
+
+ /* cmp is 1 if p1[i] > p2[i]; -1 if p1[i] < p2[i]; else 0. */
+ int cmp = lt - gt;
+
+ /* set res = cmp if !done. */
+ res |= cmp & ~done;
+
+ /* set done if p1[i] != p2[i]. */
+ done |= lt | gt;
+ }
+
+ return (res);
+}
diff --git a/configure b/configure
index c7bdc53..bda2585 100755
--- a/configure
+++ b/configure
@@ -56,7 +56,12 @@ CDIAGFLAGS="${CDIAGFLAGS} -W -Wall -Wextra -Wpointer-arith -Wuninitialized"
CDIAGFLAGS="${CDIAGFLAGS} -Wstrict-prototypes -Wmissing-prototypes -Wunused"
CDIAGFLAGS="${CDIAGFLAGS} -Wsign-compare -Wno-unused-parameter" # -Wshadow
CDIAGFLAGS="${CDIAGFLAGS} -Wno-missing-field-initializers"
-CDIAGFLAGS="${CDIAGFLAGS} -Wpointer-sign"
+CDIAGFLAGS="${CDIAGFLAGS} -Wno-pointer-sign"
+
+LIBTLS=bundled # or system
+if [ "$(uname || true)" = OpenBSD ]; then
+ LIBTLS=system
+fi
while [ $# -gt 0 ]; do
key="${1%%=*}"
@@ -95,9 +100,17 @@ while [ $# -gt 0 ]; do
--bindir) key=BINDIR ;;
--mandir) key=MANDIR ;;
--prefix) key=PREFIX ;;
+ --with-libtls) key=LIBTLS ;;
esac
case "$key" in
+ LIBTLS)
+ case "$val" in
+ bundled) LIBTLS=bundled ;;
+ system) LIBTLS=system ;;
+ *) usage ;;
+ esac
+ ;;
BINDIR) BINDIR="$val" ;;
CC) CC="$val" ;;
CFLAGS) CFLAGS="$val" ;;
@@ -267,15 +280,17 @@ if [ ${HAVE_ENDIAN_H} -eq 0 -a \
exit 1
fi
+runtest arc4random ARC4RANDOM || true
+runtest arc4random_buf ARC4RANDOM_BUF || true
runtest err ERR || true
runtest explicit_bzero EXPLICIT_BZERO || true
runtest freezero FREEZERO || true
runtest getdtablecount GETDTABLECOUNT || true
runtest getdtablesize GETDTABLESIZE || true
+runtest getentropy GETENTROPY || true
runtest getprogname GETPROGNAME || true
runtest imsg IMSG "" -lutil libimsg || true
runtest libevent LIBEVENT "" -levent libevent_core|| true
-runtest libtls LIBTLS "" -ltls libtls || true
runtest memmem MEMMEM -D_GNU_SOURCE || true
runtest openssl OPENSSL "" '-lcrypto -lssl' 'libcrypto libssl' || true
runtest pr_set_name PR_SET_NAME || true
@@ -289,15 +304,51 @@ runtest setresuid SETRESUID -D_GNU_SOURCE || true
runtest strlcat STRLCAT || true
runtest strlcpy STRLCPY || true
runtest strtonum STRTONUM -D_OPENBSD_SOURCE || true
+runtest timingsafe_memcmp TIMINGSAFE_MEMCMP || true
runtest tree_h TREE_H || true
runtest vasprintf VASPRINTF -D_GNU_SOURCE || true
runtest vis VIS -DLIBBSD_OPENBSD_VIS || true
+if [ ${HAVE_ARC4RANDOM} -eq 1 -a ${HAVE_ARC4RANDOM_BUF} -eq 0 ]; then
+ COMPATS="compat/arc4random.c ${COMPATS}"
+fi
+
+if [ ${HAVE_ARC4RANDOM} -eq 0 -a ${HAVE_GETENTROPY} -eq 1 ]; then
+ COMPATS="compat/getentropy.c ${COMPATS}"
+fi
+
+if [ "${LIBTLS}" = system ]; then
+ runtest libtls LIBTLS "" -ltls libtls || true
+
+ # not actually needed
+ HAVE_ASN1_TIME_TM_CMP=1
+ HAVE_ASN1_TIME_TM_CLAMP_NOTAFTER=1
+ HAVE_ASN1_TIME_PARSE=1
+ HAVE_SSL_CTX_UCCM=1
+ HAVE_SSL_CTX_LVM=1
+ HAVE_X509_LOOKUP_MEM=1
+else
+ # use bundled one
+ HAVE_LIBTLS=1
+ for f in compat/libtls/*.c; do
+ COMPATS="$f ${COMPATS}"
+ done
+
+ CFLAGS="-Icompat/libtls ${CFLAGS}"
+
+ deptest ASN1_time_tm_cmp ASN1_TIME_TM_CMP || true
+ deptest ASN1_time_tm_clamp_notafter ASN1_TIME_TM_CLAMP_NOTAFTER || true
+ deptest ASN1_time_parse ASN1_TIME_PARSE || true
+ deptest SSL_CTX_use_certificate_chain_mem SSL_CTX_UCCM || true
+ deptest SSL_CTX_load_verify_mem SSL_CTX_LVM || true
+ deptest X509_LOOKUP_mem X509_LOOKUP_MEM || true
+fi
+
deptest libevent2 LIBEVENT2 || true
if [ ${HAVE_LIBTLS} -eq 0 ]; then
- echo "FATAL: libtls not found" 1>&2
- echo "FATAL: libtls not found" 1>&3
+ echo "FATAL: openssl not found" 1>&2
+ echo "FATAL: openssl not found" 1>&3
exit 1
fi
@@ -340,7 +391,7 @@ if [ $NEED_LIBBSD_OPENBSD_VIS = 1 ]; then
CFLAGS="$CFLAGS -DLIBBSD_OPENBSD_VIS"
fi
-CFLAGS="${CFLAGS} ${CDIAGFLAGS}"
+CFLAGS="-I. ${CFLAGS} ${CDIAGFLAGS}"
exec > config.h
echo "config.h: writing.." >&2
@@ -394,17 +445,62 @@ elif [ ${HAVE_MACHINE_ENDIAN} -eq 1 ]; then
__HEREDOC__
fi
-[ ${HAVE_EXPLICIT_BZERO} -eq 0 -o \
+[ ${HAVE_ARC4RANDOM_BUF} -eq 0 -o \
+ ${HAVE_ASN1_TIME_PARSE} -eq 0 -o \
+ ${HAVE_EXPLICIT_BZERO} -eq 0 -o \
${HAVE_FREEZERO} -eq 0 -o \
+ ${HAVE_GETENTROPY} -eq 0 -o \
${HAVE_REALLOCARRAY} -eq 0 -o \
${HAVE_RECALLOCARRAY} -eq 0 -o \
${HAVE_STRLCAT} -eq 0 -o \
${HAVE_STRLCPY} -eq 0 -o \
- ${HAVE_STRTONUM} -eq 0 ] && echo "#include <stddef.h>"
+ ${HAVE_STRTONUM} -eq 0 -o \
+ ${HAVE_TIMINGSAFE_MEMCMP} -eq 0 ] && echo "#include <stddef.h>"
+
+[ ${HAVE_ARC4RANDOM} -eq 0 ] && echo "#include <stdint.h>"
[ ${HAVE_SETRESGID} -eq 0 -o \
${HAVE_SETRESUID} -eq 0 ] && echo "#include <unistd.h>"
+if [ ${HAVE_GETENTROPY} -eq 1 ]; then
+ echo "#define HAVE_GETENTROPY 1"
+else
+ echo "#define WITH_OPENSSL 1"
+ echo "#define OPENSSL_PRNG_ONLY 1"
+fi
+
+if [ ${HAVE_ARC4RANDOM} -eq 0 ]; then
+ echo "extern uint32_t arc4random(void);"
+else
+ echo "#define HAVE_ARC4RANDOM 1"
+fi
+if [ ${HAVE_ARC4RANDOM_BUF} -eq 0 ]; then
+ echo "extern void arc4random_buf(void *, size_t);"
+else
+ echo "#define HAVE_ARC4RANDOM_BUF 1"
+fi
+
+if [ ${HAVE_ASN1_TIME_TM_CMP} -eq 0 ]; then
+ echo "struct tm;"
+ echo "extern int ASN1_time_tm_cmp(struct tm *, struct tm *);"
+else
+ echo "#define HAVE_ASN1_TIME_TM_CMP 1"
+fi
+
+if [ ${HAVE_ASN1_TIME_TM_CLAMP_NOTAFTER} -eq 0 ]; then
+ echo "struct tm;"
+ echo "extern int ASN1_time_tm_clamp_notafter(struct tm *);"
+else
+ echo "#define HAVE_ASN1_TIME_TM_CLAMP_NOTAFTER 1"
+fi
+
+if [ ${HAVE_ASN1_TIME_PARSE} -eq 0 ]; then
+ echo "struct tm;"
+ echo "extern int ASN1_time_parse(const char *, size_t, struct tm *, int);"
+else
+ echo "#define HAVE_ASN1_TIME_PARSE 1"
+fi
+
if [ ${HAVE_ERR} -eq 0 ]; then
echo "extern void err(int, const char*, ...);"
echo "extern void errx(int, const char*, ...);"
@@ -425,6 +521,9 @@ fi
if [ ${HAVE_GETDTABLESIZE} -eq 0 ]; then
echo "extern int getdtablesize(void);"
fi
+if [ ${HAVE_GETENTROPY} -eq 0 ]; then
+ echo "extern int getentropy(void *, size_t)";
+fi
if [ ${HAVE_GETPROGNAME} -eq 0 ]; then
echo "extern const char *getprogname(void);"
fi
@@ -455,10 +554,44 @@ fi
if [ ${HAVE_STRTONUM} -eq 0 ]; then
echo "extern long long strtonum(const char*, long long, long long, const char**);"
fi
+if [ ${HAVE_TIMINGSAFE_MEMCMP} -eq 0 ]; then
+ echo "extern int timingsafe_memcmp(const void *, const void *, size_t);"
+fi
if [ ${HAVE_VASPRINTF} -eq 0 ]; then
echo "extern int vasprintf(char**, const char*, va_list);"
fi
+if [ ${HAVE_ASN1_TIME_TM_CMP} -eq 0 ]; then
+ echo "#include <openssl/asn1.h>"
+ echo "struct tm;"
+ echo "int ASN1_time_tm_cmp(struct tm *, struct tm *);"
+else
+ echo "#define HAVE_ASN1_TIME_TM_CMP 1"
+fi
+
+if [ ${HAVE_SSL_CTX_UCCM} -eq 0 -o ${HAVE_SSL_CTX_LVM} -eq 0 ]; then
+ echo "#include <openssl/ssl.h>"
+fi
+
+if [ ${HAVE_SSL_CTX_UCCM} -eq 0 ]; then
+ echo "int SSL_CTX_use_certificate_chain_mem(SSL_CTX *, void *, int);"
+else
+ echo "#define HAVE_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM 1"
+fi
+
+if [ ${HAVE_SSL_CTX_LVM} -eq 0 ]; then
+ echo "int SSL_CTX_load_verify_mem(SSL_CTX *, void *, int);"
+else
+ echo "#define HAVE_SSL_CTX_LOAD_VERIFY_MEM 1"
+fi
+
+if [ ${HAVE_X509_LOOKUP_MEM} -eq 0 ]; then
+ echo "#include <openssl/x509_vfy.h>"
+ echo "X509_LOOKUP_METHOD *X509_LOOKUP_mem(void);"
+else
+ echo "#define HAVE_X509_LOOKUP_MEM 1"
+fi
+
cat <<__HEREDOC__
#ifndef __dead
diff --git a/have/ASN1_time_parse.c b/have/ASN1_time_parse.c
new file mode 100644
index 0000000..abc7d5b
--- /dev/null
+++ b/have/ASN1_time_parse.c
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2023 Omar Polo <op@omarpolo.com>
+ *
+ * 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 <stdlib.h>
+
+#include <openssl/asn1.h>
+
+int
+main(void)
+{
+ return ASN1_time_parse("", 0, NULL, 0);
+}
+
diff --git a/have/ASN1_time_tm_cmp.c b/have/ASN1_time_tm_cmp.c
new file mode 100644
index 0000000..807c97d
--- /dev/null
+++ b/have/ASN1_time_tm_cmp.c
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2023 Omar Polo <op@omarpolo.com>
+ *
+ * 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 <stdlib.h>
+
+#include <openssl/asn1.h>
+
+int
+main(void)
+{
+ return ASN1_time_tm_cmp(NULL, NULL);
+}
+
diff --git a/have/Makefile b/have/Makefile
index 21e75c4..d2a1dfa 100644
--- a/have/Makefile
+++ b/have/Makefile
@@ -1,10 +1,18 @@
-DISTFILES = Makefile \
+DISTFILES = ASN1_time_parse.c \
+ ASN1_time_tm_clamp_notafter.c \
+ ASN1_time_tm_cmp.c \
+ Makefile \
+ SSL_CTX_load_verify_mem.c \
+ SSL_CTX_use_certificate_chain_mem.c \
+ X509_LOOKUP_mem.c \
+ arc4random.c \
endian_h.c \
err.c \
explicit_bzero.c \
freezero.c \
getdtablecount.c \
getdtablesize.c \
+ getentropy.c \
getprogname.c \
imsg.c \
libevent.c \
@@ -24,6 +32,7 @@ DISTFILES = Makefile \
strlcat.c \
strlcpy.c \
strtonum.c \
+ timingsafe_memcmp.c \
tree_h.c \
vasprintf.c \
vis.c \
diff --git a/have/SSL_CTX_load_verify_mem.c b/have/SSL_CTX_load_verify_mem.c
new file mode 100644
index 0000000..32ef15b
--- /dev/null
+++ b/have/SSL_CTX_load_verify_mem.c
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2023 Omar Polo <op@omarpolo.com>
+ *
+ * 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 <stdio.h>
+#include <openssl/ssl.h>
+
+int
+main(void)
+{
+ return SSL_CTX_load_verify_mem(NULL, NULL, 0);
+}
diff --git a/have/SSL_CTX_use_certificate_chain_mem.c b/have/SSL_CTX_use_certificate_chain_mem.c
new file mode 100644
index 0000000..0e45c31
--- /dev/null
+++ b/have/SSL_CTX_use_certificate_chain_mem.c
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2023 Omar Polo <op@omarpolo.com>
+ *
+ * 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 <stdio.h>
+#include <openssl/ssl.h>
+
+int
+main(void)
+{
+ return SSL_CTX_use_certificate_chain_mem(NULL, NULL, 0);
+}
diff --git a/have/X509_LOOKUP_mem.c b/have/X509_LOOKUP_mem.c
new file mode 100644
index 0000000..3df485e
--- /dev/null
+++ b/have/X509_LOOKUP_mem.c
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2023 Omar Polo <op@omarpolo.com>
+ *
+ * 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 <stdio.h>
+#include <openssl/x509_vfy.h>
+
+int
+main(void)
+{
+ return X509_LOOKUP_mem() != NULL;
+}
diff --git a/have/arc4random.c b/have/arc4random.c
new file mode 100644
index 0000000..4d3d48b
--- /dev/null
+++ b/have/arc4random.c
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2023 Omar Polo <op@omarpolo.com>
+ *
+ * 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 <stdlib.h>
+
+int
+main(void)
+{
+ return arc4random();
+}
diff --git a/have/arc4random_buf.c b/have/arc4random_buf.c
new file mode 100644
index 0000000..450ab9c
--- /dev/null
+++ b/have/arc4random_buf.c
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2023 Omar Polo <op@omarpolo.com>
+ *
+ * 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 <stdlib.h>
+
+int
+main(void)
+{
+ char buf[128];
+
+ arc4random_buf(buf, sizeof(buf));
+ return 0;
+}
diff --git a/have/getentropy.c b/have/getentropy.c
new file mode 100644
index 0000000..46cd74d
--- /dev/null
+++ b/have/getentropy.c
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2023 Omar Polo <op@omarpolo.com>
+ *
+ * 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 <stdio.h>
+#include <unistd.h>
+
+int
+main(void)
+{
+ char buf[1024];
+
+ return getentropy(buf, sizeof(buf));
+}
diff --git a/have/timingsafe_memcmp.c b/have/timingsafe_memcmp.c
new file mode 100644
index 0000000..0922d4b
--- /dev/null
+++ b/have/timingsafe_memcmp.c
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2023 Omar Polo <op@omarpolo.com>
+ *
+ * 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 <string.h>
+
+int
+main(void)
+{
+ const char *a = "foo";
+ const char *b = "bar";
+
+ return timingsafe_memcmp(a, b, 3);
+}
diff --git a/regress/Makefile b/regress/Makefile
index ed79b78..89b87ef 100644
--- a/regress/Makefile
+++ b/regress/Makefile
@@ -31,7 +31,7 @@ REG_COMPATS = ${COBJS:%=../%}
PUNY_SRCS = puny-test.c ../puny.c ../utf8.c ../utils.c ../log.c
PUNY_OBJS = ${PUNY_SRCS:.c=.o} ${REG_COMPATS}
-IRI_SRCS = iri_test.c ../iri.c ../utf8.c
+IRI_SRCS = iri_test.c ../iri.c ../utf8.c ../log.c
IRI_OBJS = ${IRI_SRCS:.c=.o} ${REG_COMPATS}
.PHONY: all data clean dist
@@ -51,7 +51,7 @@ fill-file: fill-file.o
${CC} fill-file.o -o $@ ${LIBS} ${LDFLAGS}
fcgi-test: fcgi-test.o
- ${CC} fcgi-test.o ${REG_COMPATS} -o fcgi-test ${LIBS} ${LDFLAGS}
+ ${CC} fcgi-test.o ../log.o ${REG_COMPATS} -o fcgi-test ${LIBS} ${LDFLAGS}
key.pem: cert.pem