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