Commit Diff


commit - 9019e55e7ef1369c37f5a7d4c7b0e441d55d6b44
commit + f9ab77a898ec008a445b3842afc21bb4eac60657
blob - e4e5dbc29a6fe2d943fdd948ec2c005d5962ef6e
blob + dd6e6d4684904bc10aeb05d7304a6484d4aed081
--- Makefile
+++ Makefile
@@ -36,11 +36,11 @@ GEMEXP_SRCS =	ge.c config.c crypto.c dirs.c fcgi.c iri
 
 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
 
blob - 65ec3ef2b2dce2eda370c789d62e7c02b6eadb74
blob + 39fe6426506258d9be4d13cd280659e5e30890f6
--- compat/Makefile
+++ 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
blob - /dev/null
blob + 8ee61b4ec68449d07d6a05dac90800fde0ba0ce2 (mode 644)
--- /dev/null
+++ 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) */
+
blob - /dev/null
blob + 5af3a4492a826a1304b66c9e967a1d6611222e16 (mode 644)
--- /dev/null
+++ 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);
+}
blob - /dev/null
blob + cdcb785608254d80bdf2c59dea05b251982ff358 (mode 644)
--- /dev/null
+++ 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
+  }
+}
blob - /dev/null
blob + dc338844076d86d1fc83c8f5e10e340f11548d0e (mode 644)
--- /dev/null
+++ 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;
+}
blob - /dev/null
blob + 4af6e93e2c6c1a75dc01c7285904c07f25e6bcf4 (mode 644)
--- /dev/null
+++ 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
blob - /dev/null
blob + 4bc428e5bc57ec2f739fdad198ec9d657aa68f99 (mode 644)
--- /dev/null
+++ 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
blob - /dev/null
blob + e290a0c931e797baf89a9ec03158768bbaad3222 (mode 644)
--- /dev/null
+++ 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 */
blob - /dev/null
blob + 35288871efac833cce0b52e56944ff1516ef358c (mode 644)
--- /dev/null
+++ 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 */
blob - /dev/null
blob + 0daabf5a311326a1099c2e7b217cc56a79a22dff (mode 644)
--- /dev/null
+++ 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);
+}
blob - /dev/null
blob + b94a6fa6d0569cd8cd336409c48689df7898bf0d (mode 644)
--- /dev/null
+++ 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 */
blob - /dev/null
blob + 0bab70d329d7fc706123f790b15e27c14f22a374 (mode 644)
--- /dev/null
+++ 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);
+}
blob - /dev/null
blob + 4a7018dff560117b8d75c9ed6d6243e643e5b00c (mode 644)
--- /dev/null
+++ 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);
+}
blob - /dev/null
blob + 3f4306ab46c84e1a108c73e1ef1911ac4034e264 (mode 644)
--- /dev/null
+++ 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);
+}
blob - /dev/null
blob + ac6df2fce8e2964058e3f64a5662648a8b9d1970 (mode 644)
--- /dev/null
+++ 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);
+}
blob - /dev/null
blob + 4846a88aa665bad11c8459db029074f0d53b309b (mode 644)
--- /dev/null
+++ 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 */
blob - /dev/null
blob + 8f62f53972e8f97e5beeb0ed1711b34681636e6a (mode 644)
--- /dev/null
+++ 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);
+}
blob - /dev/null
blob + ada33622df3f98ef299e945aa055982fb6fbaf27 (mode 644)
--- /dev/null
+++ 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);
+}
blob - /dev/null
blob + d5c550af20e4f9fc3b82f7b141dc58034c9e363f (mode 644)
--- /dev/null
+++ 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);
+}
+
blob - /dev/null
blob + b374012b1363f70340621f6862d33386cf06e356 (mode 644)
--- /dev/null
+++ 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);
+}
blob - /dev/null
blob + dc54af8d42e90a40e2280bbbb1fd8a63f272ccba (mode 644)
--- /dev/null
+++ 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);
+}
blob - /dev/null
blob + 053dc2f9f2570cd9c02f3073403d133237201b24 (mode 644)
--- /dev/null
+++ 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);
+}
blob - /dev/null
blob + 927208bfa59f0f70379fac683483f5528fbd4c8f (mode 644)
--- /dev/null
+++ 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);
+}
blob - c7bdc5373ecae978b8bd26b58679c8d4b1da865a
blob + bda25855867d373fae88c7ce17747bb08690b173
--- configure
+++ configure
@@ -56,8 +56,13 @@ CDIAGFLAGS="${CDIAGFLAGS} -W -Wall -Wextra -Wpointer-a
 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%%=*}"
 	val="${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
blob - 21e75c4f6e3d0ce1b9826006cae1ae9dc54e80cd
blob + d2a1dfa7860a0922228e753a972ad2787d2ba1fd
--- have/Makefile
+++ 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 \
blob - /dev/null
blob + abc7d5b2bbe3423b7dc54bd2779498d9d5b5dcb8 (mode 644)
--- /dev/null
+++ 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);
+}
+
blob - /dev/null
blob + 807c97d0fa05693aa1984673fbf76b46ad83aa65 (mode 644)
--- /dev/null
+++ 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);
+}
+
blob - /dev/null
blob + 32ef15bf602abee708e38693bf5309c2ebab928f (mode 644)
--- /dev/null
+++ 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);
+}
blob - /dev/null
blob + 0e45c31199b139b416bc7c8ccf05afc96ed52ffc (mode 644)
--- /dev/null
+++ 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);
+}
blob - /dev/null
blob + 3df485e45525f40a25acb48b2698fc5949a9688a (mode 644)
--- /dev/null
+++ 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;
+}
blob - /dev/null
blob + 4d3d48b23929893c8ece3ea7f780e5e090b7b068 (mode 644)
--- /dev/null
+++ 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();
+}
blob - /dev/null
blob + 450ab9c3035d6cdb16a2a86fd16e69c1a83fa0f2 (mode 644)
--- /dev/null
+++ 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;
+}
blob - /dev/null
blob + 46cd74d795c9b208d37a4159ed55c40bd9a5fba0 (mode 644)
--- /dev/null
+++ 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));
+}
blob - /dev/null
blob + 0922d4b6ef185f031e53d41e7f656fbdba89d497 (mode 644)
--- /dev/null
+++ 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);
+}
blob - ed79b78cce1bed4a060b932dc300da3eb80a9fd4
blob + 89b87ef4c5fcc8ff545d5bad9c9efae7230a08e0
--- regress/Makefile
+++ 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