Commit Diff


commit - d00c9f4eb93b02c32c1ca4411730fc1c8561502d
commit + f4a6eef32d65cd21b8ae3ff004c9e6df1da60025
blob - b61a0e5f45f1ce17893004a3a97dfd5a9ad133fa
blob + ee84780d4f695d7e2f4ac87e7b056b9481af9829
--- Makefile
+++ Makefile
@@ -1,15 +1,15 @@
-#	$OpenBSD: Makefile,v 1.30 2016/09/28 15:03:03 reyk Exp $
+#	$OpenBSD: Makefile,v 1.31 2017/05/27 08:33:25 claudio Exp $
 
 PROG=		relayd
 SRCS=		parse.y
 SRCS+=		agentx.c ca.c carp.c check_icmp.c check_script.c \
-		check_tcp.c config.c control.c hce.c log.c name2id.c \
-		pfe.c pfe_filter.c pfe_route.c proc.c \
+		check_tcp.c check_tls.c config.c control.c hce.c log.c \
+		name2id.c pfe.c pfe_filter.c pfe_route.c proc.c \
 		relay.c relay_http.c relay_udp.c relayd.c \
 		shuffle.c snmp.c ssl.c util.c
 MAN=		relayd.8 relayd.conf.5
 
-LDADD=		-levent -lssl -lcrypto -lutil
+LDADD=		-levent -ltls -lssl -lcrypto -lutil
 DPADD=		${LIBEVENT} ${LIBSSL} ${LIBCRYPTO} ${LIBUTIL}
 CFLAGS+=	-Wall -I${.CURDIR} -I${.CURDIR}/../snmpd
 CFLAGS+=	-Wstrict-prototypes -Wmissing-prototypes
blob - 45bd5f3224bb86e0222dbfd8925953c00d6c8cde
blob + f5cc3be89576381300e43b3161c73629806c698d
--- ca.c
+++ ca.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: ca.c,v 1.24 2017/04/06 12:20:48 gsoares Exp $	*/
+/*	$OpenBSD: ca.c,v 1.25 2017/05/27 08:33:25 claudio Exp $	*/
 
 /*
  * Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
@@ -82,17 +82,57 @@ ca_init(struct privsep *ps, struct privsep_proc *p, vo
 	env->sc_id = getpid() & 0xffff;
 }
 
+static void
+hash_string(char *d, size_t dlen, char *hash, size_t hashlen)
+{
+	static const char	hex[] = "0123456789abcdef";
+	size_t			off, i;
+
+	if (hashlen < 2 * dlen + sizeof("SHA256:"))
+		fatalx("%s hash buffer to small", __func__);
+
+	off = strlcpy(hash, "SHA256:", hashlen);
+
+	for (i = 0; i < dlen; i++) {
+		hash[off++] = hex[(d[i] >> 4) & 0x0f];
+		hash[off++] = hex[d[i] & 0x0f];
+	}
+	hash[off] = 0;
+}
+
 void
 ca_launch(void)
 {
+	char		 d[EVP_MAX_MD_SIZE], hash[TLS_CERT_HASH_SIZE];
 	BIO		*in = NULL;
 	EVP_PKEY	*pkey = NULL;
 	struct relay	*rlay;
+	X509		*cert = NULL;
+	int		 dlen;
 
 	TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) {
 		if ((rlay->rl_conf.flags & (F_TLS|F_TLSCLIENT)) == 0)
 			continue;
 
+		if (rlay->rl_conf.tls_cert_len) {
+			if ((in = BIO_new_mem_buf(rlay->rl_tls_cert,
+			    rlay->rl_conf.tls_cert_len)) == NULL)
+				fatalx("ca_launch: cert");
+
+			if ((cert = PEM_read_bio_X509(in, NULL,
+			    NULL, NULL)) == NULL)
+				fatalx("ca_launch: cert");
+
+		        if (X509_digest(cert, EVP_sha256(), d, &dlen) != 1)
+				fatalx("ca_launch: cert");
+
+			hash_string(d, dlen, hash, sizeof(hash));
+
+			BIO_free(in);
+			X509_free(cert);
+			purge_key(&rlay->rl_tls_cert,
+			    rlay->rl_conf.tls_cert_len);
+		}
 		if (rlay->rl_conf.tls_key_len) {
 			if ((in = BIO_new_mem_buf(rlay->rl_tls_key,
 			    rlay->rl_conf.tls_key_len)) == NULL)
@@ -105,16 +145,31 @@ ca_launch(void)
 
 			rlay->rl_tls_pkey = pkey;
 
-			if (pkey_add(env, pkey,
-			    rlay->rl_conf.tls_keyid) == NULL)
+			if (pkey_add(env, pkey, hash) == NULL)
 				fatalx("tls pkey");
 
 			purge_key(&rlay->rl_tls_key,
 			    rlay->rl_conf.tls_key_len);
 		}
-		if (rlay->rl_conf.tls_cert_len) {
-			purge_key(&rlay->rl_tls_cert,
-			    rlay->rl_conf.tls_cert_len);
+
+		if (rlay->rl_conf.tls_cacert_len) {
+			if ((in = BIO_new_mem_buf(rlay->rl_tls_cacert,
+			    rlay->rl_conf.tls_cacert_len)) == NULL)
+				fatalx("ca_launch: cacert");
+
+			if ((cert = PEM_read_bio_X509(in, NULL,
+			    NULL, NULL)) == NULL)
+				fatalx("ca_launch: cacert");
+
+		        if (X509_digest(cert, EVP_sha256(), d, &dlen) != 1)
+				fatalx("ca_launch: cacert");
+
+			hash_string(d, dlen, hash, sizeof(hash));
+
+			BIO_free(in);
+			X509_free(cert);
+			purge_key(&rlay->rl_tls_cacert,
+			    rlay->rl_conf.tls_cacert_len);
 		}
 		if (rlay->rl_conf.tls_cakey_len) {
 			if ((in = BIO_new_mem_buf(rlay->rl_tls_cakey,
@@ -128,17 +183,12 @@ ca_launch(void)
 
 			rlay->rl_tls_capkey = pkey;
 
-			if (pkey_add(env, pkey,
-			    rlay->rl_conf.tls_cakeyid) == NULL)
+			if (pkey_add(env, pkey, hash) == NULL)
 				fatalx("ca pkey");
 
 			purge_key(&rlay->rl_tls_cakey,
 			    rlay->rl_conf.tls_cakey_len);
 		}
-		if (rlay->rl_conf.tls_cacert_len) {
-			purge_key(&rlay->rl_tls_cacert,
-			    rlay->rl_conf.tls_cacert_len);
-		}
 	}
 }
 
@@ -186,12 +236,13 @@ ca_dispatch_relay(int fd, struct privsep_proc *p, stru
 		if (IMSG_DATA_SIZE(imsg) != (sizeof(cko) + cko.cko_flen))
 			fatalx("ca_dispatch_relay: "
 			    "invalid key operation");
-		if ((pkey = pkey_find(env, cko.cko_id)) == NULL ||
+		if ((pkey = pkey_find(env, cko.cko_hash)) == NULL ||
 		    (rsa = EVP_PKEY_get1_RSA(pkey)) == NULL)
 			fatalx("ca_dispatch_relay: "
 			    "invalid relay key or id");
 
-		DPRINTF("%s:%d: key id %d", __func__, __LINE__, cko.cko_id);
+		DPRINTF("%s:%d: key hash %s proc %d",
+		    __func__, __LINE__, cko.cko_hash, cko.cko_proc);
 
 		from = (u_char *)imsg->data + sizeof(cko);
 		if ((to = calloc(1, cko.cko_tlen)) == NULL)
@@ -259,7 +310,7 @@ rsae_send_imsg(int flen, const u_char *from, u_char *t
 	struct pollfd	 pfd[1];
 	struct ctl_keyop cko;
 	int		 ret = 0;
-	objid_t		*id;
+	char		*hash;
 	struct iovec	 iov[2];
 	struct imsgbuf	*ibuf;
 	struct imsgev	*iev;
@@ -267,7 +318,7 @@ rsae_send_imsg(int flen, const u_char *from, u_char *t
 	int		 n, done = 0, cnt = 0;
 	u_char		*toptr;
 
-	if ((id = RSA_get_ex_data(rsa, 0)) == NULL)
+	if ((hash = RSA_get_ex_data(rsa, 0)) == NULL)
 		return (0);
 
 	iev = proc_iev(ps, PROC_CA, ps->ps_instance);
@@ -277,7 +328,7 @@ rsae_send_imsg(int flen, const u_char *from, u_char *t
 	 * XXX this could be nicer...
 	 */
 
-	cko.cko_id = *id;
+	(void)strlcpy(cko.cko_hash, hash, sizeof(cko.cko_hash));
 	cko.cko_proc = ps->ps_instance;
 	cko.cko_flen = flen;
 	cko.cko_tlen = RSA_size(rsa);
@@ -490,6 +541,5 @@ ca_engine_init(struct relayd *x_env)
 	return;
 
  fail:
-	ssl_error(__func__, errstr);
-	fatalx("%s", errstr);
+	fatalx("%s: %s", __func__, errstr);
 }
blob - /dev/null
blob + 2e407312e852a23cfd4211035a36a7977cc2ca09 (mode 644)
--- /dev/null
+++ boguskeys.h
@@ -0,0 +1,200 @@
+/*	$OpenBSD: boguskeys.h,v 1.1 2017/05/27 08:33:25 claudio Exp $	*/
+
+/*
+ * Placed in the public domain by Claudio Jeker <claudio@openbsd.org>
+ * on March 26, 2017.
+ */
+
+/* Bogus private key since the private key is privseped away */
+const char bogus_1024[] = "-----BEGIN RSA PRIVATE KEY-----\n"
+    "MIICXQIBAAKBgQDXEA8QOA7tgvV0UN50pAf34b0vKD95svTuFNuCn7esdTUly/hF\n"
+    "wDckkEznfbGj6o1otpMVaPNwRhhwikF7x9IWPjXw7sfbgvQoa2gkMUMkUr/X49KA\n"
+    "7Uu0xqOaKn/IM4yA/ZaTuL99zdn8EBCRyrDVF8iDnVTPMrsLTyg2bE1qhwIDAQAB\n"
+    "AoGAHvv/T5TkAbAWcPWdtyxSwZHSUdL4oi34P7zdi0o7iiswxwtF77aruybXDZr8\n"
+    "VuNaEDYNps4CFLDkoIIqwQye5bWktBLL9Bv0ZDmR8u1PkQPjwRblg7jPtk46aiWQ\n"
+    "9NEVkr2V1GUrzAPDcC23R5PKx//PveTiwrfmo6j+sWkxTxECQQD+9LevrDATY1nt\n"
+    "Ce9R1KnduwueeDRRByS+8or8dyGXUR1wZjm2M4pBpigTfPQiSA9O6nixV3xpwNUN\n"
+    "G9XpWGO1AkEA1/GE9ZPBWHOut+WYSerq76gZeIaH3tF3FnnLBLzw8+ePf0qm0h4q\n"
+    "i2dl/EQV9LH7q0Rf7k2yXgHeo5dK4OkyywJBAM49+kWSvcVBTmJw8fa5WLw0bf7A\n"
+    "cFnHtJL+sy3t1O+KP41INJFOeh4HIk45e2gr8K4/AGk9QzhtNCuJg+5igS0CQQDM\n"
+    "AyW2TW2w/znmC0ehLgvfd1T5BUCARizYUyB2zXpnNDHh9Mk+YbmYEovLlReZIj2+\n"
+    "RM7M+SK2pdWNgHYBns+ZAkAT7fZsAeOxNjM7h2kA0AriUvc2IuDqVGiFKAFCVacF\n"
+    "mSQSIplSJU117YTqbVGf++SEj/WFYOTS8G+jjBuMr1d9\n"
+    "-----END RSA PRIVATE KEY-----\n";
+const char bogus_2048[] = "-----BEGIN RSA PRIVATE KEY-----\n"
+    "MIIEpAIBAAKCAQEA2qsShCATc5n25suEmB+1zaxbrVbSqaEWZ+qizKTLlybJ0TOD\n"
+    "Nl/6lo9hIZ+gTqf0GwJRTUwtkjlovrn5p8IWtZUceG0S+ijh7DybzGCVlOFN0JRx\n"
+    "z+zTr9eNPkvrJLwYavSzV4BpjelBKManE8sHA6pqXCDi5PfJ0iKfWtHQk2S5ukWA\n"
+    "WE33ANLQVW0ATPvtNpHSacNIzWEW7h/66sPJu+iNcekx/Q+1kI+0Msf7m1HFN464\n"
+    "eRrm3kqncPsJ6o3Kbu2aoJFk7oO6+HfSyXmxLywuUgyPnqW6zN8pj7jaq33fmHzo\n"
+    "3s95Nbk/cYOtYHPzaT5eQWXy120aZ/scuY1laQIDAQABAoIBAClEP6pPo1wdokrL\n"
+    "/an30geOj36W9AqvK9tQnIiiUQmleFDSt+B7HH9tb5c42Lf/WkH+nflIdxExZGMa\n"
+    "FdNi/YYnLchMTViIfppmlcBsOc5u9pB2c0QaHZkBxNYM3cOA+9qzc2UABuuRKYrY\n"
+    "co95sUkv0AKy8h7j5GKTxh8NmZ82+YRkkkMkk7bvXhGppR+jiqeQ4KsZbYWFPAG9\n"
+    "WJA+sFVn8WS0oMePfqmeIPY+BiddU0ITn02Hafn9jBhhXI5LKbiiwC8sFDICxPSm\n"
+    "moDpmexe1s7jNuSxueEM5XPQP7v2QmnH9KDxDcPEC5Lz8qFa6wkiLBpQ9CRmPlDV\n"
+    "pEfF8kECgYEA/5jiItxbt+kEDMm6GuAGy02Zq/9Eb3u1J7szjvvGrL6L0S5FcDic\n"
+    "S8M5A5hTvbxQfohr6AEzqog5IQ2EiyxghIfYOs5E+rYVnN4py1ErzR3LoC45bIiO\n"
+    "tRbgYGMqFzD+uGaePpCwz/Ptn9KqCoH4hhfCJPMgOSNUvh8EAJfh7HcCgYEA2wNK\n"
+    "Y53qfMjsd1qGYMM3J6QtTJWrteejSspouyKAlCD1RHKKzhmOxa5GPkG4NSYi1hij\n"
+    "nRywxGvFOm0eoYYMUhPdjdC4Txp646l3HNdEZMWv+NN47+vaHX+KvTyq1xis46JB\n"
+    "Y5SK+57RmS7sEQqUVwuqJiuPR1YoM2daBqiUFR8CgYEAymyLE67PGLz7TyFoOaaY\n"
+    "2uQPQ098JIqlstyofaHa+65Azx7FMZYz+jCXc8hs8cQ1P7DNPMXO5EzUad/py8sO\n"
+    "eYeYcSIxMRmJzl2IXhRgCyeAv9A7/D++PZ7rfoqqqAlOgj4LL2OqFFeMJtpRftbm\n"
+    "O1SPlnHSYE4h7BxmMA4ZiAsCgYAeG0Cxmvat+qzO52nLiWpej6oOehClq9b9o/9r\n"
+    "oh2Mv08X/qroFAlVUVSkoEIjRD/LsI1lPplqFuqA0plAWP3+lm6BXSzI6vnzq8sM\n"
+    "8uaa97Xt/ZwFVyWfonW+98UAVosFq7tTZgsI9dcYOKQI36xuntLf9mL2yngyQMXW\n"
+    "XnwkvwKBgQCCoZxoF0o6QWbEowJf/BrozjYa2D0tVokxRr7kfVXt9TTQez5LQ1u4\n"
+    "/w6oCEKldPe/6tzO12i9BITmAmoZzswO/ms7J3cRnvoLWM1tPHh3zrGZgIaMdTyv\n"
+    "n0QebhOEKeXrhwZVmyhBFgI/4bTZJWByAnr6p3jLywK2NdxJIXZ5cg==\n"
+    "-----END RSA PRIVATE KEY-----\n";
+const char bogus_4096[] = "-----BEGIN RSA PRIVATE KEY-----\n"
+    "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\n"
+    "-----END RSA PRIVATE KEY-----\n";
+const char bogus_8192[] = "-----BEGIN RSA PRIVATE KEY-----\n"
+    "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\n"
+    "-----END RSA PRIVATE KEY-----\n";
blob - bbf3258f70c08de7ce2c800a6999ac41e2140297
blob + cf3a97c3f232a3eeacd3f782d078fee3e5fc53f4
--- check_tcp.c
+++ check_tcp.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: check_tcp.c,v 1.52 2017/02/09 11:16:22 reyk Exp $	*/
+/*	$OpenBSD: check_tcp.c,v 1.53 2017/05/27 08:33:25 claudio Exp $	*/
 
 /*
  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -170,7 +170,7 @@ tcp_host_up(struct ctl_tcp_event *cte)
 	}
 
 	if (cte->table->conf.flags & F_TLS) {
-		ssl_transaction(cte);
+		check_tls(cte);
 		return;
 	}
 
blob - e9d00420804c1fdd90be9df4d15cb5620ce234f8
blob + 2d95412460b4e195a6171100989c17c033c7ec1e
--- config.c
+++ config.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: config.c,v 1.31 2016/11/24 21:01:18 reyk Exp $	*/
+/*	$OpenBSD: config.c,v 1.32 2017/05/27 08:33:25 claudio Exp $	*/
 
 /*
  * Copyright (c) 2011 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -100,8 +100,12 @@ config_init(struct relayd *env)
 		(void)strlcpy(env->sc_proto_default.tlsciphers,
 		    TLSCIPHERS_DEFAULT,
 		    sizeof(env->sc_proto_default.tlsciphers));
-		env->sc_proto_default.tlsecdhcurve = TLSECDHCURVE_DEFAULT;
-		env->sc_proto_default.tlsdhparams = TLSDHPARAMS_DEFAULT;
+		(void)strlcpy(env->sc_proto_default.tlsecdhcurve,
+		    TLSECDHCURVE_DEFAULT,
+		    sizeof(env->sc_proto_default.tlsecdhcurve));
+		(void)strlcpy(env->sc_proto_default.tlsdhparams,
+		    TLSDHPARAM_DEFAULT,
+		    sizeof(env->sc_proto_default.tlsdhparams));
 		env->sc_proto_default.type = RELAY_PROTO_TCP;
 		(void)strlcpy(env->sc_proto_default.name, "default",
 		    sizeof(env->sc_proto_default.name));
@@ -269,6 +273,8 @@ config_getcfg(struct relayd *env, struct imsg *imsg)
 		ssl_init(env);
 		if (what & CONFIG_CA_ENGINE)
 			ca_engine_init(env);
+		if (tls_init() == -1)
+			fatalx("unable to initialize TLS");
 	}
 
 	if (privsep_process != PROC_PARENT)
blob - /dev/null
blob + eb453507c8232d26e8dd17464b647a8b0b1f7c56 (mode 644)
--- /dev/null
+++ check_tls.c
@@ -0,0 +1,226 @@
+/*	$OpenBSD: check_tls.c,v 1.1 2017/05/27 08:33:25 claudio Exp $	*/
+
+/*
+ * Copyright (c) 2017 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/uio.h>
+
+#include <limits.h>
+#include <event.h>
+#include <unistd.h>
+#include <string.h>
+#include <imsg.h>
+
+#include "relayd.h"
+
+void	check_tls_read(int, short, void *);
+void	check_tls_write(int, short, void *);
+void	check_tls_handshake(int, short, void *);
+void	check_tls_cleanup(struct ctl_tcp_event *);
+void	check_tls_error(struct ctl_tcp_event *, const char *, const char *);
+
+void
+check_tls_read(int s, short event, void *arg)
+{
+	char			 rbuf[SMALL_READ_BUF_SIZE];
+	struct ctl_tcp_event	*cte = arg;
+	int			 retry_flag = EV_READ;
+	int			 ret;
+
+	if (event == EV_TIMEOUT) {
+		cte->host->up = HOST_DOWN;
+		check_tls_cleanup(cte);
+		hce_notify_done(cte->host, HCE_TLS_READ_TIMEOUT);
+		return;
+	}
+
+	bzero(rbuf, sizeof(rbuf));
+
+	ret = tls_read(cte->tls, rbuf, sizeof(rbuf));
+	if (ret > 0) {
+		if (ibuf_add(cte->buf, rbuf, ret) == -1)
+			fatal("check_tls_read: buf_add error");
+		if (cte->validate_read != NULL &&
+		    cte->validate_read(cte) == 0) {
+			check_tls_cleanup(cte);
+			hce_notify_done(cte->host, cte->host->he);
+			return;
+		}
+	} else if (ret == 0) {
+		cte->host->up = HOST_DOWN;
+		(void)cte->validate_close(cte);
+		check_tls_cleanup(cte);
+		hce_notify_done(cte->host, cte->host->he);
+		return;
+	} else if (ret == TLS_WANT_POLLIN) {
+		retry_flag = EV_READ;
+	} else if (ret == TLS_WANT_POLLOUT) {
+		retry_flag = EV_WRITE;
+	} else {
+		cte->host->up = HOST_DOWN;
+		check_tls_error(cte, cte->host->conf.name, "cannot read");
+		check_tls_cleanup(cte);
+		hce_notify_done(cte->host, HCE_TLS_READ_ERROR);
+		return;
+	}
+
+	event_again(&cte->ev, s, EV_TIMEOUT|retry_flag, check_tls_read,
+	    &cte->tv_start, &cte->table->conf.timeout, cte);
+	return;
+}
+
+void
+check_tls_write(int s, short event, void *arg)
+{
+	struct ctl_tcp_event	*cte = arg;
+	int			 retry_flag = EV_WRITE;
+	int			 len;
+	int			 ret;
+
+	if (event == EV_TIMEOUT) {
+		cte->host->up = HOST_DOWN;
+		check_tls_cleanup(cte);
+		hce_notify_done(cte->host, HCE_TLS_WRITE_TIMEOUT);
+		return;
+	}
+
+	len = strlen(cte->table->sendbuf);
+
+	ret = tls_write(cte->tls, cte->table->sendbuf, len);
+	if (ret > 0) {
+		if ((cte->buf = ibuf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) ==
+		    NULL)
+			fatalx("ssl_write: cannot create dynamic buffer");
+
+		event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, check_tls_read,
+		    &cte->tv_start, &cte->table->conf.timeout, cte);
+		return;
+	} else if (ret == TLS_WANT_POLLIN) {
+		retry_flag = EV_READ;
+	} else if (ret == TLS_WANT_POLLOUT) {
+		retry_flag = EV_WRITE;
+	} else {
+		cte->host->up = HOST_DOWN;
+		check_tls_error(cte, cte->host->conf.name, "cannot write");
+		check_tls_cleanup(cte);
+		hce_notify_done(cte->host, HCE_TLS_WRITE_ERROR);
+		return;
+	}
+
+	event_again(&cte->ev, s, EV_TIMEOUT|retry_flag, check_tls_write,
+	    &cte->tv_start, &cte->table->conf.timeout, cte);
+}
+
+void
+check_tls_handshake(int fd, short event, void *arg)
+{
+	struct ctl_tcp_event	*cte = arg;
+	int			 retry_flag = 0;
+	int			 ret;
+
+	if (event == EV_TIMEOUT) {
+		cte->host->up = HOST_DOWN;
+		hce_notify_done(cte->host, HCE_TLS_CONNECT_TIMEOUT);
+		check_tls_cleanup(cte);
+		return;
+	}
+
+	ret = tls_handshake(cte->tls);
+	if (ret == 0) {
+		if (cte->table->conf.check == CHECK_TCP) {
+			cte->host->up = HOST_UP;
+			hce_notify_done(cte->host, HCE_TLS_CONNECT_OK);
+			check_tls_cleanup(cte);
+			return;
+		}
+		if (cte->table->sendbuf != NULL) {
+			event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE,
+			    check_tls_write, &cte->tv_start,
+			    &cte->table->conf.timeout, cte);
+			return;
+		}
+		if ((cte->buf = ibuf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) ==
+		    NULL)
+			fatalx("ssl_connect: cannot create dynamic buffer");
+		event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_READ,
+		    check_tls_read, &cte->tv_start, &cte->table->conf.timeout,
+		    cte);
+		return;
+	} else if (ret == TLS_WANT_POLLIN) {
+		retry_flag = EV_READ;
+	} else if (ret == TLS_WANT_POLLOUT) {
+		retry_flag = EV_WRITE;
+	} else {
+		cte->host->up = HOST_DOWN;
+		check_tls_error(cte, cte->host->conf.name,
+		   "cannot connect");
+		hce_notify_done(cte->host, HCE_TLS_CONNECT_FAIL);
+		check_tls_cleanup(cte);
+		return;
+	}
+
+	event_again(&cte->ev, cte->s, EV_TIMEOUT|retry_flag,
+	    check_tls_handshake,
+	    &cte->tv_start, &cte->table->conf.timeout, cte);
+}
+
+void
+check_tls_cleanup(struct ctl_tcp_event *cte)
+{
+	tls_close(cte->tls);
+	tls_reset(cte->tls);
+	close(cte->s);
+	ibuf_free(cte->buf);
+	cte->buf = NULL;
+}
+
+void
+check_tls_error(struct ctl_tcp_event *cte, const char *where, const char *what)
+{
+	if (log_getverbose() < 2)
+		return;
+	log_debug("TLS error: %s: %s: %s", where, what, tls_error(cte->tls));
+}
+
+void
+check_tls(struct ctl_tcp_event *cte)
+{
+	if (cte->tls == NULL) {
+		cte->tls = tls_client();
+		if (cte->tls == NULL)
+			fatal("cannot create TLS connection");
+	}
+	/* need to re-configure because of tls_reset */
+	if (tls_configure(cte->tls, cte->table->tls_cfg) == -1)
+		fatal("cannot configure TLS connection");
+
+	if (tls_connect_socket(cte->tls, cte->s, NULL) == -1) {
+		check_tls_error(cte, cte->host->conf.name,
+		    "cannot connect");
+		tls_close(cte->tls);
+		tls_reset(cte->tls);
+		cte->host->up = HOST_UNKNOWN;
+		hce_notify_done(cte->host, HCE_TLS_CONNECT_ERROR);
+		return;
+	}
+
+	event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE, check_tls_handshake,
+	    &cte->tv_start, &cte->table->conf.timeout, cte);
+}
blob - 33cc9ee2a452f317ff0add29370e3de1e74082c9
blob + 6162c2008eb81b71b084a24534b69fb294f253d4
--- hce.c
+++ hce.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: hce.c,v 1.75 2016/09/03 14:09:04 reyk Exp $	*/
+/*	$OpenBSD: hce.c,v 1.76 2017/05/27 08:33:25 claudio Exp $	*/
 
 /*
  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -90,9 +90,11 @@ hce_setup_events(void)
 	if (env->sc_conf.flags & F_TLS) {
 		TAILQ_FOREACH(table, env->sc_tables, entry) {
 			if (!(table->conf.flags & F_TLS) ||
-			    table->ssl_ctx != NULL)
+			    table->tls_cfg != NULL)
 				continue;
-			table->ssl_ctx = ssl_ctx_create(env);
+			table->tls_cfg = tls_config_new();
+			tls_config_insecure_noverifycert(table->tls_cfg);
+			tls_config_insecure_noverifyname(table->tls_cfg);
 		}
 	}
 }
blob - 57ab789db8ffd8965e22e81d2ad5be3764161e09
blob + 5e318601f2615c6f437add1ce729c1f6bf427329
--- parse.y
+++ parse.y
@@ -1,4 +1,4 @@
-/*	$OpenBSD: parse.y,v 1.214 2017/01/05 13:53:09 krw Exp $	*/
+/*	$OpenBSD: parse.y,v 1.215 2017/05/27 08:33:25 claudio Exp $	*/
 
 /*
  * Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -181,7 +181,6 @@ typedef struct {
 %type	<v.number>	opttls opttlsclient
 %type	<v.number>	redirect_proto relay_proto match
 %type	<v.number>	action ruleaf key_option
-%type	<v.number>	tlsdhparams tlsecdhcurve
 %type	<v.port>	port
 %type	<v.host>	host
 %type	<v.addr>	address
@@ -1000,8 +999,10 @@ proto		: relay_proto PROTO STRING	{
 			TAILQ_INIT(&p->rules);
 			(void)strlcpy(p->tlsciphers, TLSCIPHERS_DEFAULT,
 			    sizeof(p->tlsciphers));
-			p->tlsdhparams = TLSDHPARAMS_DEFAULT;
-			p->tlsecdhcurve = TLSECDHCURVE_DEFAULT;
+			(void)strlcpy(p->tlsecdhcurve, TLSECDHCURVE_DEFAULT,
+			    sizeof(p->tlsecdhcurve));
+			(void)strlcpy(p->tlsdhparams, TLSDHPARAM_DEFAULT,
+			    sizeof(p->tlsdhparams));
 			if (last_proto_id == INT_MAX) {
 				yyerror("too many protocols defined");
 				free(p);
@@ -1088,8 +1089,8 @@ tlsflags_l	: tlsflags comma tlsflags_l
 		| tlsflags
 		;
 
-tlsflags	: SESSION TICKETS { proto->tickets = 0; }
-		| NO SESSION TICKETS { proto->tickets = -1; }
+tlsflags	: SESSION TICKETS { proto->tickets = 1; }
+		| NO SESSION TICKETS { proto->tickets = 0; }
 		| CIPHERS STRING		{
 			if (strlcpy(proto->tlsciphers, $2,
 			    sizeof(proto->tlsciphers)) >=
@@ -1101,17 +1102,69 @@ tlsflags	: SESSION TICKETS { proto->tickets = 0; }
 			free($2);
 		}
 		| NO EDH			{
-			proto->tlsdhparams = TLSDHPARAMS_NONE;
+			(void)strlcpy(proto->tlsdhparams, "none",
+			    sizeof(proto->tlsdhparams));
 		}
-		| EDH tlsdhparams		{
-			proto->tlsdhparams = $2;
+		| EDH			{
+			(void)strlcpy(proto->tlsdhparams, "auto",
+			    sizeof(proto->tlsdhparams));
 		}
+		| EDH PARAMS STRING		{
+			struct tls_config	*tls_cfg;
+			if ((tls_cfg = tls_config_new()) == NULL) {
+				yyerror("tls_config_new failed");
+				free($3);
+				YYERROR;
+			}
+			if (tls_config_set_dheparams(tls_cfg, $3) != 0) {
+				yyerror("tls edh params %s: %s", $3,
+				    tls_config_error(tls_cfg));
+				tls_config_free(tls_cfg);
+				free($3);
+				YYERROR;
+			}
+			tls_config_free(tls_cfg);
+			if (strlcpy(proto->tlsdhparams, $3,
+			    sizeof(proto->tlsdhparams)) >=
+			    sizeof(proto->tlsdhparams)) {
+				yyerror("tls edh truncated");
+				free($3);
+				YYERROR;
+			}
+			free($3);
+		}
 		| NO ECDH			{
-			proto->tlsecdhcurve = 0;
+			(void)strlcpy(proto->tlsecdhcurve, "none",
+			    sizeof(proto->tlsecdhcurve));
 		}
-		| ECDH tlsecdhcurve		{
-			proto->tlsecdhcurve = $2;
+		| ECDH			{
+			(void)strlcpy(proto->tlsecdhcurve, "auto",
+			    sizeof(proto->tlsecdhcurve));
 		}
+		| ECDH CURVE STRING			{
+			struct tls_config	*tls_cfg;
+			if ((tls_cfg = tls_config_new()) == NULL) {
+				yyerror("tls_config_new failed");
+				free($3);
+				YYERROR;
+			}
+			if (tls_config_set_ecdhecurve(tls_cfg, $3) != 0) {
+				yyerror("tls ecdh curve %s: %s", $3,
+				    tls_config_error(tls_cfg));
+				tls_config_free(tls_cfg);
+				free($3);
+				YYERROR;
+			}
+			tls_config_free(tls_cfg);
+			if (strlcpy(proto->tlsecdhcurve, $3,
+			    sizeof(proto->tlsecdhcurve)) >=
+			    sizeof(proto->tlsecdhcurve)) {
+				yyerror("tls ecdh truncated");
+				free($3);
+				YYERROR;
+			}
+			free($3);
+		}
 		| CA FILENAME STRING		{
 			if (strlcpy(proto->tlsca, $3,
 			    sizeof(proto->tlsca)) >=
@@ -1549,29 +1602,6 @@ key_option	: /* empty */		{ $$ = KEY_OPTION_NONE; }
 		| LOG			{ $$ = KEY_OPTION_LOG; }
 		;
 
-tlsdhparams	: /* empty */		{ $$ = TLSDHPARAMS_MIN; }
-		| PARAMS NUMBER		{
-			if ($2 < TLSDHPARAMS_MIN) {
-				yyerror("EDH params not supported: %d", $2);
-				YYERROR;
-			}
-			$$ = $2;
-		}
-		;
-
-tlsecdhcurve	: /* empty */		{ $$ = TLSECDHCURVE_DEFAULT; }
-		| CURVE STRING		{
-			if (strcmp("none", $2) == 0)
-				$$ = 0;
-			else if ((proto->tlsecdhcurve = OBJ_sn2nid($2)) == 0) {
-				yyerror("ECDH curve not supported");
-				free($2);
-				YYERROR;
-			}
-			free($2);
-		}
-		;
-
 relay		: RELAY STRING	{
 			struct relay *r;
 
blob - 3ed127553b952ffc5aeb98206ba5c17a29bddc98
blob + 4b36e4a8a31402f6b8b803fafbf48e699663d943
--- relay.c
+++ relay.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: relay.c,v 1.219 2017/02/02 08:24:16 reyk Exp $	*/
+/*	$OpenBSD: relay.c,v 1.220 2017/05/27 08:33:25 claudio Exp $	*/
 
 /*
  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -39,8 +39,7 @@
 #include <siphash.h>
 #include <imsg.h>
 
-#include <openssl/dh.h>
-#include <openssl/ssl.h>
+#include <tls.h>
 
 #include "relayd.h"
 
@@ -73,22 +72,15 @@ void		 relay_input(struct rsession *);
 
 void		 relay_hash_addr(SIPHASH_CTX *, struct sockaddr_storage *, int);
 
-DH *		 relay_tls_get_dhparams(int);
-DH		*relay_tls_callback_dh(SSL *, int, int);
-SSL_CTX		*relay_tls_ctx_create(struct relay *);
+int		 relay_tls_ctx_create(struct relay *);
 void		 relay_tls_transaction(struct rsession *,
 		    struct ctl_relay_event *);
-void		 relay_tls_accept(int, short, void *);
+void		 relay_tls_handshake(int, short, void *);
 void		 relay_connect_retry(int, short, void *);
-void		 relay_tls_connect(int, short, void *);
 void		 relay_tls_connected(struct ctl_relay_event *);
 void		 relay_tls_readcb(int, short, void *);
 void		 relay_tls_writecb(int, short, void *);
 
-struct tls_ticket	*relay_get_ticket_key(unsigned char *);
-int			 relay_tls_session_ticket(SSL *, unsigned char *,
-			    unsigned char *, EVP_CIPHER_CTX *, HMAC_CTX *, int);
-
 char		*relay_load_file(const char *, off_t *);
 extern void	 bufferevent_read_pressure_cb(struct evbuffer *, size_t,
 		    size_t, void *);
@@ -262,7 +254,7 @@ relay_protodebug(struct relay *rlay)
 		fprintf(stderr, "\ttls flags: %s\n",
 		    printb_flags(proto->tlsflags, TLSFLAG_BITS));
 	fprintf(stderr, "\ttls session tickets: %s\n",
-	    (proto->tickets > -1) ? "enabled" : "disabled");
+	    (proto->tickets == 1) ? "enabled" : "disabled");
 	fprintf(stderr, "\ttype: ");
 	switch (proto->type) {
 	case RELAY_PROTO_TCP:
@@ -426,8 +418,8 @@ relay_launch(void)
 
 	TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) {
 		if ((rlay->rl_conf.flags & (F_TLS|F_TLSCLIENT)) &&
-		    (rlay->rl_ssl_ctx = relay_tls_ctx_create(rlay)) == NULL)
-			fatal("relay_init: failed to create TLS context");
+		    relay_tls_ctx_create(rlay) == -1)
+			fatalx("relay_launch: failed to create TLS context");
 
 		TAILQ_FOREACH(rlt, &rlay->rl_tables, rlt_entry) {
 			/*
@@ -687,7 +679,7 @@ relay_connected(int fd, short sig, void *arg)
 		return;
 	}
 
-	if ((rlay->rl_conf.flags & F_TLSCLIENT) && (out->ssl == NULL)) {
+	if ((rlay->rl_conf.flags & F_TLSCLIENT) && (out->tls == NULL)) {
 		relay_tls_transaction(con, out);
 		return;
 	}
@@ -727,7 +719,7 @@ relay_connected(int fd, short sig, void *arg)
 	con->se_out.bev = bev;
 
 	/* Initialize the TLS wrapper */
-	if ((rlay->rl_conf.flags & F_TLSCLIENT) && (out->ssl != NULL))
+	if ((rlay->rl_conf.flags & F_TLSCLIENT) && (out->tls != NULL))
 		relay_tls_connected(out);
 
 	bufferevent_settimeout(bev,
@@ -778,7 +770,7 @@ relay_input(struct rsession *con)
 	}
 
 	/* Initialize the TLS wrapper */
-	if ((rlay->rl_conf.flags & F_TLS) && con->se_in.ssl != NULL)
+	if ((rlay->rl_conf.flags & F_TLS) && con->se_in.tls != NULL)
 		relay_tls_connected(&con->se_in);
 
 	bufferevent_settimeout(con->se_in.bev,
@@ -826,8 +818,8 @@ relay_dump(struct ctl_relay_event *cre, const void *bu
 	 * of non-blocking events etc. This is useful to print an
 	 * error message before gracefully closing the session.
 	 */
-	if (cre->ssl != NULL)
-		(void)SSL_write(cre->ssl, buf, len);
+	if (cre->tls != NULL)
+		(void)tls_write(cre->tls, buf, len);
 	else
 		(void)write(cre->s, buf, len);
 }
@@ -1086,9 +1078,9 @@ relay_accept(int fd, short event, void *arg)
 		goto err;
 
 	con->se_in.s = s;
-	con->se_in.ssl = NULL;
+	con->se_in.tls = NULL;
 	con->se_out.s = -1;
-	con->se_out.ssl = NULL;
+	con->se_out.tls = NULL;
 	con->se_in.dst = &con->se_out;
 	con->se_out.dst = &con->se_in;
 	con->se_in.con = con;
@@ -1401,7 +1393,7 @@ relay_session(struct rsession *con)
 		return;
 	}
 
-	if ((rlay->rl_conf.flags & F_TLS) && (in->ssl == NULL)) {
+	if ((rlay->rl_conf.flags & F_TLS) && (in->tls == NULL)) {
 		relay_tls_transaction(con, in);
 		return;
 	}
@@ -1570,7 +1562,7 @@ relay_connect(struct rsession *con)
 	/* Connection is already established but session not active */
 	if ((rlay->rl_conf.flags & F_TLSINSPECT) &&
 	    con->se_out.state == STATE_PRECONNECT) {
-		if (con->se_out.ssl == NULL) {
+		if (con->se_out.tls == NULL) {
 			log_debug("%s: tls connect failed", __func__);
 			return (-1);
 		}
@@ -1706,14 +1698,11 @@ relay_close(struct rsession *con, const char *msg)
 		bufferevent_free(con->se_in.bev);
 	else if (con->se_in.output != NULL)
 		evbuffer_free(con->se_in.output);
-	if (con->se_in.ssl != NULL) {
-		/* XXX handle non-blocking shutdown */
-		if (SSL_shutdown(con->se_in.ssl) == 0)
-			SSL_shutdown(con->se_in.ssl);
-		SSL_free(con->se_in.ssl);
-	}
-	if (con->se_in.tlscert != NULL)
-		X509_free(con->se_in.tlscert);
+	if (con->se_in.tls != NULL)
+		tls_close(con->se_in.tls);
+	tls_free(con->se_in.tls);
+	tls_config_free(con->se_in.tls_cfg);
+	free(con->se_in.tlscert);
 	if (con->se_in.s != -1) {
 		close(con->se_in.s);
 		if (con->se_out.s == -1) {
@@ -1726,20 +1715,16 @@ relay_close(struct rsession *con, const char *msg)
 			    __func__, relay_inflight);
 		}
 	}
-	free(con->se_in.buf);
 
 	if (con->se_out.bev != NULL)
 		bufferevent_free(con->se_out.bev);
 	else if (con->se_out.output != NULL)
 		evbuffer_free(con->se_out.output);
-	if (con->se_out.ssl != NULL) {
-		/* XXX handle non-blocking shutdown */
-		if (SSL_shutdown(con->se_out.ssl) == 0)
-			SSL_shutdown(con->se_out.ssl);
-		SSL_free(con->se_out.ssl);
-	}
-	if (con->se_out.tlscert != NULL)
-		X509_free(con->se_out.tlscert);
+	if (con->se_out.tls != NULL)
+		tls_close(con->se_out.tls);
+	tls_free(con->se_out.tls);
+	tls_config_free(con->se_out.tls_cfg);
+	free(con->se_out.tlscert);
 	if (con->se_out.s != -1) {
 		close(con->se_out.s);
 
@@ -1751,8 +1736,6 @@ relay_close(struct rsession *con, const char *msg)
 	}
 	con->se_out.state = STATE_INIT;
 
-	free(con->se_out.buf);
-
 	if (con->se_log != NULL)
 		evbuffer_free(con->se_log);
 
@@ -1896,6 +1879,8 @@ relay_dispatch_ca(int fd, struct privsep_proc *p, stru
 int
 relay_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
 {
+	struct relay_ticket_key	 ticket;
+	struct relay		*rlay;
 	struct rsession		*con;
 	struct timeval		 tv;
 	objid_t			 id;
@@ -1945,13 +1930,15 @@ relay_dispatch_parent(int fd, struct privsep_proc *p, 
 		config_getreset(env, imsg);
 		break;
 	case IMSG_TLSTICKET_REKEY:
-		IMSG_SIZE_CHECK(imsg, (&env->sc_tls_ticket));
-		/* rotate keys */
-		memcpy(&env->sc_tls_ticket_bak, &env->sc_tls_ticket,
-		    sizeof(env->sc_tls_ticket));
-		env->sc_tls_ticket_bak.tt_backup = 1;
-		memcpy(&env->sc_tls_ticket, imsg->data,
-		    sizeof(env->sc_tls_ticket));
+		IMSG_SIZE_CHECK(imsg, (&ticket));
+		memcpy(&env->sc_ticket, imsg->data, sizeof(env->sc_ticket));
+		TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) {
+			if (rlay->rl_conf.flags & F_TLS)
+				tls_config_add_ticket_key(rlay->rl_tls_cfg,
+				    env->sc_ticket.tt_keyrev,
+				    env->sc_ticket.tt_key,
+				    sizeof(env->sc_ticket.tt_key));
+		}
 		break;
 	default:
 		return (-1);
@@ -1960,69 +1947,6 @@ relay_dispatch_parent(int fd, struct privsep_proc *p, 
 	return (0);
 }
 
-DH *
-relay_tls_get_dhparams(int keylen)
-{
-	DH		*dh;
-	BIGNUM		*(*prime)(BIGNUM *);
-	const char	*gen;
-
-	gen = "2";
-	if (keylen >= 8192)
-		prime = get_rfc3526_prime_8192;
-	else if (keylen >= 4096)
-		prime = get_rfc3526_prime_4096;
-	else if (keylen >= 3072)
-		prime = get_rfc3526_prime_3072;
-	else if (keylen >= 2048)
-		prime = get_rfc3526_prime_2048;
-	else if (keylen >= 1536)
-		prime = get_rfc3526_prime_1536;
-	else
-		prime = get_rfc2409_prime_1024;
-
-	if ((dh = DH_new()) == NULL)
-		return (NULL);
-
-	dh->p = (*prime)(NULL);
-	BN_dec2bn(&dh->g, gen);
-
-	if (dh->p == NULL || dh->g == NULL) {
-		DH_free(dh);
-		return (NULL);
-	}
-
-	return (dh);
-}
-
-DH *
-relay_tls_callback_dh(SSL *ssl, int export, int keylen)
-{
-	struct ctl_relay_event	*cre;
-	EVP_PKEY		*pkey;
-	int			 keytype, maxlen;
-	DH			*dh = NULL;
-
-	/* Get maximum key length from config */
-	if ((cre = (struct ctl_relay_event *)SSL_get_app_data(ssl)) == NULL)
-		return (NULL);
-	maxlen = cre->con->se_relay->rl_proto->tlsdhparams;
-
-	/* Get the private key length from the cert */
-	if ((pkey = SSL_get_privatekey(ssl))) {
-		keytype = EVP_PKEY_type(pkey->type);
-		if (keytype == EVP_PKEY_RSA || keytype == EVP_PKEY_DSA)
-			keylen = EVP_PKEY_bits(pkey);
-		else
-			return (NULL);
-	}
-
-	/* get built-in params based on the shorter key length */
-	dh = relay_tls_get_dhparams(MINIMUM(keylen, maxlen));
-
-	return (dh);
-}
-
 int
 relay_dispatch_hce(int fd, struct privsep_proc *p, struct imsg *imsg)
 {
@@ -2034,129 +1958,239 @@ relay_dispatch_hce(int fd, struct privsep_proc *p, str
 	return (-1);
 }
 
-SSL_CTX *
-relay_tls_ctx_create(struct relay *rlay)
+static int
+relay_tls_ctx_create_proto(struct protocol *proto, struct tls_config *tls_cfg)
 {
-	struct protocol	*proto = rlay->rl_proto;
-	SSL_CTX		*ctx;
-	EC_KEY		*ecdhkey;
+	uint32_t		 protocols = 0;
 
-	ctx = SSL_CTX_new(SSLv23_method());
-	if (ctx == NULL)
-		goto err;
-
-	/*
-	 * Disable the session cache by default.
-	 * Everything modern uses tickets
-	 */
-	SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
-
-	/* Set callback for TLS session tickets if enabled */
-	if (proto->tickets == -1)
-		SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET);
-	else {
-		if (!SSL_CTX_set_tlsext_ticket_key_cb(ctx,
-		    relay_tls_session_ticket))
-			log_warnx("could not set the TLS ticket callback");
-		/* set timeout to the ticket rekey time */
-		SSL_CTX_set_timeout(ctx, TLS_TICKET_REKEY_TIME);
-	}
-
-	/* Enable all workarounds and set SSL options */
-	SSL_CTX_set_options(ctx, SSL_OP_ALL);
-	SSL_CTX_set_options(ctx,
-	    SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
-	if (proto->tlsflags & TLSFLAG_CIPHER_SERVER_PREF)
-		SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
-	if ((proto->tlsflags & TLSFLAG_CLIENT_RENEG) == 0)
-		SSL_CTX_set_options(ctx, SSL_OP_NO_CLIENT_RENEGOTIATION);
-
 	/* Set the allowed SSL protocols */
-	SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
-	SSL_CTX_clear_options(ctx, SSL_OP_NO_SSLv3);
-	if ((proto->tlsflags & TLSFLAG_SSLV3) == 0)
-		SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
-	SSL_CTX_clear_options(ctx, SSL_OP_NO_TLSv1);
-	if ((proto->tlsflags & TLSFLAG_TLSV1_0) == 0)
-		SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);
-	SSL_CTX_clear_options(ctx, SSL_OP_NO_TLSv1_1);
-	if ((proto->tlsflags & TLSFLAG_TLSV1_1) == 0)
-		SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_1);
-	SSL_CTX_clear_options(ctx, SSL_OP_NO_TLSv1_2);
-	if ((proto->tlsflags & TLSFLAG_TLSV1_2) == 0)
-		SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_2);
-
-	if (proto->tlsecdhcurve > 0) {
-		/* Enable ECDHE support for TLS perfect forward secrecy */
-		if ((ecdhkey =
-		    EC_KEY_new_by_curve_name(proto->tlsecdhcurve)) == NULL)
-			goto err;
-		SSL_CTX_set_tmp_ecdh(ctx, ecdhkey);
-		SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE);
-		EC_KEY_free(ecdhkey);
+	if (proto->tlsflags & TLSFLAG_TLSV1_0)
+		protocols |= TLS_PROTOCOL_TLSv1_0;
+	if (proto->tlsflags & TLSFLAG_TLSV1_1)
+		protocols |= TLS_PROTOCOL_TLSv1_1;
+	if (proto->tlsflags & TLSFLAG_TLSV1_2)
+		protocols |= TLS_PROTOCOL_TLSv1_2;
+	if (tls_config_set_protocols(tls_cfg, protocols) == -1) {
+		log_warnx("could not set the TLS protocol: %s",
+		    tls_config_error(tls_cfg));
+		return (-1);
 	}
 
-	if (proto->tlsdhparams > 0) {
-		/* Enable EDH params (forward secrecy for older clients) */
-		SSL_CTX_set_tmp_dh_callback(ctx, relay_tls_callback_dh);
+	if (tls_config_set_ciphers(tls_cfg, proto->tlsciphers)) {
+		log_warnx("could not set the TLS cypers: %s",
+		    tls_config_error(tls_cfg));
+		return (-1);
 	}
 
-	if (!SSL_CTX_set_cipher_list(ctx, proto->tlsciphers))
-		goto err;
+	if ((proto->tlsflags & TLSFLAG_CIPHER_SERVER_PREF) == 0)
+		tls_config_prefer_ciphers_client(tls_cfg);
 
-	/* Verify the server certificate if we have a CA chain */
-	if ((rlay->rl_conf.flags & F_TLSCLIENT) &&
-	    (rlay->rl_tls_ca != NULL)) {
-		if (!SSL_CTX_load_verify_mem(ctx,
-		    rlay->rl_tls_ca, rlay->rl_conf.tls_ca_len))
-			goto err;
-		SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
-	}
-
-	if ((rlay->rl_conf.flags & F_TLS) == 0)
-		return (ctx);
-
-	log_debug("%s: loading certificate", __func__);
-	if (!SSL_CTX_use_certificate_chain_mem(ctx,
-	    rlay->rl_tls_cert, rlay->rl_conf.tls_cert_len))
-		goto err;
-
-	log_debug("%s: loading private key", __func__);
-	if (!ssl_ctx_fake_private_key(ctx,
-	    &rlay->rl_conf.tls_keyid, sizeof(rlay->rl_conf.tls_keyid),
-	    rlay->rl_tls_cert, rlay->rl_conf.tls_cert_len,
-	    &rlay->rl_tls_x509, &rlay->rl_tls_pkey))
-		goto err;
-
-	if (!SSL_CTX_check_private_key(ctx))
-		goto err;
-
-	if (rlay->rl_conf.tls_cacert_len) {
-		log_debug("%s: loading CA private key", __func__);
-		if (!ssl_load_pkey(&rlay->rl_conf.tls_cakeyid,
-		    sizeof(rlay->rl_conf.tls_cakeyid),
-		    rlay->rl_tls_cacert, rlay->rl_conf.tls_cacert_len,
-		    &rlay->rl_tls_cacertx509, &rlay->rl_tls_capkey))
-			goto err;
-	}
-
 	/*
 	 * Set session ID context to a random value. It needs to be the
 	 * same accross all relay processes or session caching will fail.
 	 */
-	if (!SSL_CTX_set_session_id_context(ctx, env->sc_conf.tls_sid,
-	    sizeof(env->sc_conf.tls_sid)))
+	if (tls_config_set_session_id(tls_cfg, env->sc_conf.tls_sid,
+	    sizeof(env->sc_conf.tls_sid)) == -1) {
+		log_warnx("could not set the TLS session ID: %s",
+		    tls_config_error(tls_cfg));
+		return (-1);
+	}
+
+	/* Set callback for TLS session tickets if enabled */
+	if (proto->tickets == 1) {
+		/* set timeout to the ticket rekey time */
+		tls_config_set_session_lifetime(tls_cfg, TLS_SESSION_LIFETIME);
+
+		tls_config_add_ticket_key(tls_cfg,
+		    env->sc_ticket.tt_keyrev, env->sc_ticket.tt_key,
+		    sizeof(env->sc_ticket.tt_key));
+	}
+
+	if (tls_config_set_ecdhecurve(tls_cfg, proto->tlsecdhcurve) != 0) {
+		log_warnx("failed to set ecdh curve %s: %s",
+		    proto->tlsecdhcurve, tls_config_error(tls_cfg));
+		return (-1);
+	}
+
+	if (tls_config_set_dheparams(tls_cfg, proto->tlsdhparams) != 0) {
+		log_warnx("failed to set dh params %s: %s",
+		    proto->tlsdhparams, tls_config_error(tls_cfg));
+		return (-1);
+	}
+
+	return (0);
+}
+
+/*
+ * This function is not publicy exported because it is a hack until libtls
+ * has a proper privsep setup 
+ */
+void tls_config_skip_private_key_check(struct tls_config *config);
+
+int
+relay_tls_ctx_create(struct relay *rlay)
+{
+	struct tls_config	*tls_cfg, *tls_client_cfg;
+	struct tls		*tls = NULL;
+	const char		*fake_key;
+	int			 fake_keylen;
+
+	if ((tls_cfg = tls_config_new()) == NULL) {
+		log_warnx("unable to allocate TLS config");
+		return (-1);
+	}
+	if ((tls_client_cfg = tls_config_new()) == NULL) {
+		log_warnx("unable to allocate TLS config");
+		return (-1);
+	}
+
+	if (relay_tls_ctx_create_proto(rlay->rl_proto, tls_cfg) == -1)
 		goto err;
+	if (relay_tls_ctx_create_proto(rlay->rl_proto, tls_client_cfg) == -1)
+		goto err;
 
+	/* Verify the server certificate if we have a CA chain */
+	if (rlay->rl_conf.flags & F_TLSCLIENT) {
+		/*
+		 * Currently relayd can't verify the name of certs and changing
+		 * this is non trivial. For now just disable name verification.
+		 */
+		tls_config_insecure_noverifyname(tls_client_cfg);
+
+		if (rlay->rl_tls_ca != NULL) {
+			if (tls_config_set_ca_mem(tls_client_cfg,
+			    rlay->rl_tls_ca, rlay->rl_conf.tls_ca_len) != 0) {
+				log_warnx("failed to set root certificates: %s",
+				    tls_config_error(tls_client_cfg));
+				goto err;
+			}
+		} else {
+			/* No root cert available so disable the checking */
+			tls_config_insecure_noverifycert(tls_client_cfg);
+		}
+
+		rlay->rl_tls_client_cfg = tls_client_cfg;
+	}
+
+	if (rlay->rl_conf.flags & F_TLS) {
+		log_debug("%s: loading certificate", __func__);
+		/*
+		 * Use the public key as the "private" key - the secret key
+		 * parameters are hidden in an extra process that will be
+		 * contacted by the RSA engine.  The SSL/TLS library needs at
+		 * least the public key parameters in the current process.
+		 * For this we need to skip the private key check done by
+		 * libtls.
+		 */
+		tls_config_skip_private_key_check(tls_cfg);
+
+		if ((fake_keylen = ssl_ctx_fake_private_key(rlay->rl_tls_cert,
+		    rlay->rl_conf.tls_cert_len, &fake_key)) == -1) {
+			/* error already printed */
+			goto err;
+		}
+
+		if (tls_config_set_keypair_ocsp_mem(tls_cfg,
+		    rlay->rl_tls_cert, rlay->rl_conf.tls_cert_len,
+		    fake_key, fake_keylen, NULL, 0) != 0) {
+			log_warnx("failed to set tls certificate: %s",
+			    tls_config_error(tls_cfg));
+			goto err;
+		}
+
+		if (rlay->rl_conf.tls_cacert_len) {
+			log_debug("%s: loading CA certificate", __func__);
+			if (!ssl_load_pkey(&rlay->rl_conf.tls_cakeyid,
+			    rlay->rl_tls_cacert, rlay->rl_conf.tls_cacert_len,
+			    &rlay->rl_tls_cacertx509, &rlay->rl_tls_capkey))
+				goto err;
+			/* loading certificate public key */
+			if (!ssl_load_pkey(NULL,
+			    rlay->rl_tls_cert, rlay->rl_conf.tls_cert_len,
+			    NULL, &rlay->rl_tls_pkey))
+				goto err;
+		}
+
+		tls = tls_server();
+		if (tls == NULL) {
+			log_warnx("unable to allocate TLS context");
+			goto err;
+		}
+		if (tls_configure(tls, tls_cfg) == -1) {
+			log_warnx("could not configure the TLS context: %s",
+			    tls_error(tls));
+			tls_free(tls);
+			goto err;
+		}
+		rlay->rl_tls_cfg = tls_cfg;
+		rlay->rl_tls_ctx = tls;
+	}
+
 	/* The text versions of the keys/certs are not needed anymore */
 	purge_key(&rlay->rl_tls_cert, rlay->rl_conf.tls_cert_len);
 	purge_key(&rlay->rl_tls_cacert, rlay->rl_conf.tls_cacert_len);
 
-	return (ctx);
+	if (rlay->rl_tls_client_cfg == NULL)
+		tls_config_free(tls_client_cfg);
+	if (rlay->rl_tls_cfg == NULL)
+		tls_config_free(tls_cfg);
 
+	return (0);
  err:
-	SSL_CTX_free(ctx);
-	ssl_error(rlay->rl_conf.name, "relay_tls_ctx_create");
+	tls_config_free(tls_client_cfg);
+	tls_config_free(tls_cfg);
+	return (-1);
+}
+
+static struct tls *
+relay_tls_inspect_create(struct relay *rlay, struct ctl_relay_event *cre)
+{
+	struct tls_config	*tls_cfg;
+	struct tls		*tls;
+	const char		*fake_key;
+	int			 fake_keylen;
+
+	/* TLS inspection: use session-specific certificate */
+	if ((tls_cfg = tls_config_new()) == NULL) {
+		log_warnx("unable to allocate TLS config");
+		goto err;
+	}
+	if (relay_tls_ctx_create_proto(rlay->rl_proto,
+	    tls_cfg) == -1) {
+		tls_config_free(tls_cfg);
+		goto err;
+	}
+
+	log_debug("%s: loading intercepted certificate", __func__);
+	if ((fake_keylen = ssl_ctx_fake_private_key(cre->tlscert,
+	    cre->tlscert_len, &fake_key)) == -1) {
+		/* error already printed */
+		goto err;
+	}
+	if (tls_config_set_keypair_ocsp_mem(tls_cfg,
+	    cre->tlscert, cre->tlscert_len, fake_key, fake_keylen,
+	    NULL, 0) != 0) {
+		log_warnx("failed to set tls certificate: %s",
+		    tls_config_error(tls_cfg));
+		goto err;
+	}
+
+	tls = tls_server();
+	if (tls == NULL) {
+		log_warnx("unable to allocate TLS context");
+		goto err;
+	}
+	if (tls_configure(tls, tls_cfg) == -1) {
+		log_warnx("could not configure the TLS context: %s",
+		    tls_error(tls));
+		tls_free(tls);
+		goto err;
+	}
+
+	cre->tls_cfg = tls_cfg;
+	return (tls);
+ err:
+	tls_config_free(tls_cfg);
 	return (NULL);
 }
 
@@ -2164,186 +2198,117 @@ void
 relay_tls_transaction(struct rsession *con, struct ctl_relay_event *cre)
 {
 	struct relay		*rlay = con->se_relay;
-	SSL			*ssl;
-	const SSL_METHOD	*method;
-	void			(*cb)(int, short, void *);
+	struct tls		*tls_server;
+	const char		*errstr;
 	u_int			 flag;
 
-	ssl = SSL_new(rlay->rl_ssl_ctx);
-	if (ssl == NULL)
-		goto err;
-
 	if (cre->dir == RELAY_DIR_REQUEST) {
-		cb = relay_tls_accept;
-		method = SSLv23_server_method();
+		if (cre->tlscert != NULL)
+			tls_server = relay_tls_inspect_create(rlay, cre);
+		else
+			tls_server = rlay->rl_tls_ctx;
+
+		if (tls_accept_socket(tls_server, &cre->tls, cre->s) == -1) {
+			errstr = "could not accept the TLS connection";
+			goto err;
+		}
+		if (cre->tlscert != NULL)
+			tls_free(tls_server);
 		flag = EV_READ;
-
-		/* Use session-specific certificate for TLS inspection. */
-		if (cre->tlscert != NULL)
-			SSL_use_certificate(ssl, cre->tlscert);
 	} else {
-		cb = relay_tls_connect;
-		method = SSLv23_client_method();
+		cre->tls = tls_client();
+		if (cre->tls == NULL ||
+		    tls_configure(cre->tls, rlay->rl_tls_client_cfg) == -1) {
+			errstr = "could not configure the TLS client context";
+			goto err;
+		}
+		if (tls_connect_socket(cre->tls, cre->s, NULL) == -1) {
+			errstr = "could not connect the TLS connection";
+			goto err;
+		}
 		flag = EV_WRITE;
 	}
 
-	if (!SSL_set_ssl_method(ssl, method))
-		goto err;
-	if (!SSL_set_fd(ssl, cre->s))
-		goto err;
-
-	if (cre->dir == RELAY_DIR_REQUEST)
-		SSL_set_accept_state(ssl);
-	else
-		SSL_set_connect_state(ssl);
-
-	SSL_set_app_data(ssl, cre);
-	cre->ssl = ssl;
-
-	DPRINTF("%s: session %d: scheduling on %s", __func__, con->se_id,
+	log_debug("%s: session %d: scheduling on %s", __func__, con->se_id,
 	    (flag == EV_READ) ? "EV_READ" : "EV_WRITE");
-	event_again(&con->se_ev, cre->s, EV_TIMEOUT|flag, cb,
-	    &con->se_tv_start, &rlay->rl_conf.timeout, con);
+	event_again(&con->se_ev, cre->s, EV_TIMEOUT|flag, relay_tls_handshake,
+	    &con->se_tv_start, &rlay->rl_conf.timeout, cre);
 	return;
 
  err:
-	SSL_free(ssl);
-	ssl_error(rlay->rl_conf.name, "relay_tls_transaction");
-	relay_close(con, "session tls failed");
+	relay_close(con, errstr);
 }
 
 void
-relay_tls_accept(int fd, short event, void *arg)
+relay_tls_handshake(int fd, short event, void *arg)
 {
-	struct rsession	*con = arg;
-	struct relay	*rlay = con->se_relay;
-	int		 retry_flag = 0;
-	int		 tls_err = 0;
-	int		 ret;
+	struct ctl_relay_event	*cre = arg;
+	struct rsession		*con = cre->con;
+	struct relay		*rlay = con->se_relay;
+	int			 retry_flag = 0;
+	int			 ret;
 
 	if (event == EV_TIMEOUT) {
-		relay_close(con, "TLS accept timeout");
+		relay_close(con, "TLS handshake timeout");
 		return;
 	}
 
-	ret = SSL_accept(con->se_in.ssl);
-	if (ret <= 0) {
-		tls_err = SSL_get_error(con->se_in.ssl, ret);
-
-		switch (tls_err) {
-		case SSL_ERROR_WANT_READ:
-			retry_flag = EV_READ;
-			goto retry;
-		case SSL_ERROR_WANT_WRITE:
-			retry_flag = EV_WRITE;
-			goto retry;
-		case SSL_ERROR_ZERO_RETURN:
-		case SSL_ERROR_SYSCALL:
-			if (ret == 0) {
-				relay_close(con, "closed");
-				return;
-			}
-			/* FALLTHROUGH */
-		default:
-			ssl_error(rlay->rl_conf.name, "relay_tls_accept");
-			relay_close(con, "TLS accept error");
-			return;
-		}
-	}
-
-
+	ret = tls_handshake(cre->tls);
+	if (ret == 0) {
 #ifdef DEBUG
-	log_info(
+		log_info(
 #else
-	log_debug(
+		log_debug(
 #endif
-	    "relay %s, session %d established (%d active)",
-	    rlay->rl_conf.name, con->se_id, relay_sessions);
+		    "relay %s, tls session %d %s (%d active)",
+		    rlay->rl_conf.name, con->se_id,
+		    cre->dir == RELAY_DIR_REQUEST ? "established" : "connected",
+		    relay_sessions);
 
-	relay_session(con);
-	return;
-
-retry:
-	DPRINTF("%s: session %d: scheduling on %s", __func__, con->se_id,
-	    (retry_flag == EV_READ) ? "EV_READ" : "EV_WRITE");
-	event_again(&con->se_ev, fd, EV_TIMEOUT|retry_flag, relay_tls_accept,
-	    &con->se_tv_start, &rlay->rl_conf.timeout, con);
-}
-
-void
-relay_tls_connect(int fd, short event, void *arg)
-{
-	struct rsession	*con = arg;
-	struct relay	*rlay = con->se_relay;
-	int		 retry_flag = 0;
-	int		 tls_err = 0;
-	int		 ret;
-	X509		*servercert = NULL;
-
-	if (event == EV_TIMEOUT) {
-		relay_close(con, "TLS connect timeout");
-		return;
-	}
-
-	ret = SSL_connect(con->se_out.ssl);
-	if (ret <= 0) {
-		tls_err = SSL_get_error(con->se_out.ssl, ret);
-
-		switch (tls_err) {
-		case SSL_ERROR_WANT_READ:
-			retry_flag = EV_READ;
-			goto retry;
-		case SSL_ERROR_WANT_WRITE:
-			retry_flag = EV_WRITE;
-			goto retry;
-		case SSL_ERROR_ZERO_RETURN:
-		case SSL_ERROR_SYSCALL:
-			if (ret == 0) {
-				relay_close(con, "closed");
-				return;
-			}
-			/* FALLTHROUGH */
-		default:
-			ssl_error(rlay->rl_conf.name, "relay_tls_connect");
-			relay_close(con, "TLS connect error");
-			return;
-		}
-	}
-
-#ifdef DEBUG
-	log_info(
-#else
-	log_debug(
-#endif
-	    "relay %s, tls session %d connected (%d active)",
-	    rlay->rl_conf.name, con->se_id, relay_sessions);
-
-	if (rlay->rl_conf.flags & F_TLSINSPECT) {
-		if ((servercert =
-		    SSL_get_peer_certificate(con->se_out.ssl)) != NULL) {
-			con->se_in.tlscert =
-			    ssl_update_certificate(servercert,
-			    rlay->rl_tls_pkey, rlay->rl_tls_capkey,
-			    rlay->rl_tls_cacertx509);
-		} else
-			con->se_in.tlscert = NULL;
-		if (servercert != NULL)
-			X509_free(servercert);
-		if (con->se_in.tlscert == NULL)
-			relay_close(con, "could not create certificate");
-		else
+		if (cre->dir == RELAY_DIR_REQUEST) {
 			relay_session(con);
+			return;
+		}
+
+		if (rlay->rl_conf.flags & F_TLSINSPECT) {
+			const uint8_t	*servercert;
+			size_t		 len;
+
+			servercert = tls_peer_cert_chain_pem(con->se_out.tls,
+			    &len);
+			if (servercert != NULL) {
+				con->se_in.tlscert = ssl_update_certificate(
+				    servercert, len,
+				    rlay->rl_tls_pkey, rlay->rl_tls_capkey,
+				    rlay->rl_tls_cacertx509,
+				    &con->se_in.tlscert_len);
+			} else
+				con->se_in.tlscert = NULL;
+			if (con->se_in.tlscert == NULL)
+				relay_close(con,
+				    "could not create certificate");
+			else
+				relay_session(con);
+			return;
+		}
+		relay_connected(fd, EV_WRITE, con);
 		return;
+	} else if (ret == TLS_WANT_POLLIN) {
+		retry_flag = EV_READ;
+	} else if (ret == TLS_WANT_POLLOUT) {
+		retry_flag = EV_WRITE;
+	} else {
+		log_debug("TLS handshake failed: %s: %s: %s",
+		    rlay->rl_conf.name, __func__,
+		    tls_error(cre->tls));
+		relay_close(con, "TLS handshake error");
+		return;
 	}
 
-	relay_connected(fd, EV_WRITE, con);
-	return;
-
-retry:
 	DPRINTF("%s: session %d: scheduling on %s", __func__, con->se_id,
 	    (retry_flag == EV_READ) ? "EV_READ" : "EV_WRITE");
-	event_again(&con->se_ev, fd, EV_TIMEOUT|retry_flag, relay_tls_connect,
-	    &con->se_tv_start, &rlay->rl_conf.timeout, con);
+	event_again(&con->se_ev, fd, EV_TIMEOUT|retry_flag, relay_tls_handshake,
+	    &con->se_tv_start, &rlay->rl_conf.timeout, cre);
 }
 
 void
@@ -2365,11 +2330,9 @@ relay_tls_readcb(int fd, short event, void *arg)
 	char			 rbuf[IBUF_READ_SIZE];
 	struct bufferevent	*bufev = arg;
 	struct ctl_relay_event	*cre = bufev->cbarg;
-	struct rsession		*con = cre->con;
-	struct relay		*rlay = con->se_relay;
-	int			 ret = 0, tls_err = 0;
 	short			 what = EVBUFFER_READ;
 	int			 howmuch = IBUF_READ_SIZE;
+	ssize_t			 ret;
 	size_t			 len;
 
 	if (event == EV_TIMEOUT) {
@@ -2380,29 +2343,18 @@ relay_tls_readcb(int fd, short event, void *arg)
 	if (bufev->wm_read.high != 0)
 		howmuch = MINIMUM(sizeof(rbuf), bufev->wm_read.high);
 
-	ret = SSL_read(cre->ssl, rbuf, howmuch);
-	if (ret <= 0) {
-		tls_err = SSL_get_error(cre->ssl, ret);
+	ret = tls_read(cre->tls, rbuf, howmuch);
+	if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) {
+		goto retry;
+	} else if (ret < 0) {
+		what |= EVBUFFER_ERROR;
+		goto err;
+	}
+	len = ret;
 
-		switch (tls_err) {
-		case SSL_ERROR_WANT_READ:
-			DPRINTF("%s: session %d: want read",
-			    __func__, con->se_id);
-			goto retry;
-		case SSL_ERROR_WANT_WRITE:
-			DPRINTF("%s: session %d: want write",
-			    __func__, con->se_id);
-			goto retry;
-		default:
-			if (ret == 0)
-				what |= EVBUFFER_EOF;
-			else {
-				ssl_error(rlay->rl_conf.name,
-				    "relay_tls_readcb");
-				what |= EVBUFFER_ERROR;
-			}
-			goto err;
-		}
+	if (len == 0) {
+		what |= EVBUFFER_EOF;
+		goto err;
 	}
 
 	if (evbuffer_add(bufev->input, rbuf, ret) == -1) {
@@ -2439,9 +2391,8 @@ relay_tls_writecb(int fd, short event, void *arg)
 {
 	struct bufferevent	*bufev = arg;
 	struct ctl_relay_event	*cre = bufev->cbarg;
-	struct rsession		*con = cre->con;
-	struct relay		*rlay = con->se_relay;
-	int			 ret = 0, tls_err;
+	ssize_t			 ret;
+	size_t			 len;
 	short			 what = EVBUFFER_WRITE;
 
 	if (event == EV_TIMEOUT) {
@@ -2450,47 +2401,17 @@ relay_tls_writecb(int fd, short event, void *arg)
 	}
 
 	if (EVBUFFER_LENGTH(bufev->output)) {
-		if (cre->buf == NULL) {
-			cre->buflen = EVBUFFER_LENGTH(bufev->output);
-			if ((cre->buf = malloc(cre->buflen)) == NULL) {
-				what |= EVBUFFER_ERROR;
-				goto err;
-			}
-			bcopy(EVBUFFER_DATA(bufev->output),
-			    cre->buf, cre->buflen);
+		ret = tls_write(cre->tls, EVBUFFER_DATA(bufev->output),
+		    EVBUFFER_LENGTH(bufev->output));
+		if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) {
+			goto retry;
+		} else if (ret < 0) {
+			what |= EVBUFFER_ERROR;
+			goto err;
 		}
-
-		ret = SSL_write(cre->ssl, cre->buf, cre->buflen);
-		if (ret <= 0) {
-			tls_err = SSL_get_error(cre->ssl, ret);
-
-			switch (tls_err) {
-			case SSL_ERROR_WANT_READ:
-				DPRINTF("%s: session %d: want read",
-				    __func__, con->se_id);
-				goto retry;
-			case SSL_ERROR_WANT_WRITE:
-				DPRINTF("%s: session %d: want write",
-				    __func__, con->se_id);
-				goto retry;
-			default:
-				if (ret == 0)
-					what |= EVBUFFER_EOF;
-				else {
-					ssl_error(rlay->rl_conf.name,
-					    "relay_tls_writecb");
-					what |= EVBUFFER_ERROR;
-				}
-				goto err;
-			}
-		}
-		evbuffer_drain(bufev->output, ret);
+		len = ret;
+		evbuffer_drain(bufev->output, len);
 	}
-	if (cre->buf != NULL) {
-		free(cre->buf);
-		cre->buf = NULL;
-		cre->buflen = 0;
-	}
 
 	if (EVBUFFER_LENGTH(bufev->output) != 0)
 		relay_bufferevent_add(&bufev->ev_write, bufev->timeout_write);
@@ -2501,72 +2422,14 @@ relay_tls_writecb(int fd, short event, void *arg)
 	return;
 
  retry:
-	if (cre->buflen != 0)
-		relay_bufferevent_add(&bufev->ev_write, bufev->timeout_write);
+	relay_bufferevent_add(&bufev->ev_write, bufev->timeout_write);
 	return;
 
  err:
-	if (cre->buf != NULL) {
-		free(cre->buf);
-		cre->buf = NULL;
-		cre->buflen = 0;
-	}
 	(*bufev->errorcb)(bufev, what, bufev->cbarg);
 }
 
-struct tls_ticket *
-relay_get_ticket_key(unsigned char *keyname)
-{
-	if (keyname) {
-		if (timingsafe_memcmp(keyname,
-		    env->sc_tls_ticket_bak.tt_key_name,
-		    sizeof(env->sc_tls_ticket_bak.tt_key_name)) == 0)
-			return &env->sc_tls_ticket_bak;
-		if (timingsafe_memcmp(keyname,
-		    env->sc_tls_ticket.tt_key_name,
-		    sizeof(env->sc_tls_ticket.tt_key_name)) == 0)
-			return &env->sc_tls_ticket;
-		return NULL;
-	}
-	return &env->sc_tls_ticket;
-}
-
 int
-relay_tls_session_ticket(SSL *ssl, unsigned char *keyname, unsigned char *iv,
-    EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int mode)
-{
-	struct tls_ticket	*key;
-
-	if (mode == 1) {
-		/* create new session */
-		key = relay_get_ticket_key(NULL);
-		memcpy(keyname, key->tt_key_name, sizeof(key->tt_key_name));
-		arc4random_buf(iv, EVP_MAX_IV_LENGTH);
-		EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL,
-		    key->tt_aes_key, iv);
-		HMAC_Init_ex(hctx, key->tt_hmac_key, sizeof(key->tt_hmac_key),
-		    EVP_sha256(), NULL);
-		return 0;
-	} else {
-		/* get key by name */
-		key = relay_get_ticket_key(keyname);
-		if  (!key)
-			return 0;
-
-		EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL,
-		    key->tt_aes_key, iv);
-		HMAC_Init_ex(hctx, key->tt_hmac_key, sizeof(key->tt_hmac_key),
-		    EVP_sha256(), NULL);
-
-		/* time to renew the ticket? */
-		if (key->tt_backup) {
-			return 2;
-		}
-		return 1;
-	}
-}
-
-int
 relay_bufferevent_add(struct event *ev, int timeout)
 {
 	struct timeval tv, *ptv = NULL;
blob - 4082ab35af0833b8c7ff79ac6b427b3dd377d4f0
blob + 7141fcf4bcc816d341481e2bae7df0794015f672
--- relay_http.c
+++ relay_http.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: relay_http.c,v 1.64 2017/03/10 21:04:35 reyk Exp $	*/
+/*	$OpenBSD: relay_http.c,v 1.65 2017/05/27 08:33:25 claudio Exp $	*/
 
 /*
  * Copyright (c) 2006 - 2016 Reyk Floeter <reyk@openbsd.org>
@@ -715,11 +715,6 @@ relay_reset_http(struct ctl_relay_event *cre)
 	struct http_descriptor	*desc = cre->desc;
 
 	relay_httpdesc_free(desc);
-	if (cre->buf != NULL) {
-		free(cre->buf);
-		cre->buf = NULL;
-		cre->buflen = 0;
-	}
 	desc->http_method = 0;
 	desc->http_chunked = 0;
 	cre->headerlen = 0;
blob - 53996b21b30db9c649e3e8c6e610d7ad5ac4eea3
blob + e3b443eb42d86a649e880c4c7a0acb4ab4ffd799
--- relayd.c
+++ relayd.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: relayd.c,v 1.166 2017/05/06 19:44:53 fcambus Exp $	*/
+/*	$OpenBSD: relayd.c,v 1.167 2017/05/27 08:33:25 claudio Exp $	*/
 
 /*
  * Copyright (c) 2007 - 2016 Reyk Floeter <reyk@openbsd.org>
@@ -43,7 +43,7 @@
 #include <sha1.h>
 #include <md5.h>
 
-#include <openssl/ssl.h>
+#include <tls.h>
 
 #include "relayd.h"
 
@@ -530,12 +530,11 @@ purge_table(struct relayd *env, struct tablelist *head
 			close(host->cte.s);
 		}
 		ibuf_free(host->cte.buf);
-		SSL_free(host->cte.ssl);
+		tls_free(host->cte.tls);
 		free(host);
 	}
 	free(table->sendbuf);
-	if (table->conf.flags & F_TLS)
-		SSL_CTX_free(table->ssl_ctx);
+	tls_config_free(table->tls_cfg);
 
 	if (head != NULL)
 		TAILQ_REMOVE(head, table, entry);
@@ -578,10 +577,6 @@ purge_relay(struct relayd *env, struct relay *rlay)
 	purge_key(&rlay->rl_tls_ca, rlay->rl_conf.tls_ca_len);
 	purge_key(&rlay->rl_tls_cakey, rlay->rl_conf.tls_cakey_len);
 
-	if (rlay->rl_tls_x509 != NULL) {
-		X509_free(rlay->rl_tls_x509);
-		rlay->rl_tls_x509 = NULL;
-	}
 	if (rlay->rl_tls_pkey != NULL) {
 		EVP_PKEY_free(rlay->rl_tls_pkey);
 		rlay->rl_tls_pkey = NULL;
@@ -595,7 +590,9 @@ purge_relay(struct relayd *env, struct relay *rlay)
 		rlay->rl_tls_capkey = NULL;
 	}
 
-	SSL_CTX_free(rlay->rl_ssl_ctx);
+	tls_free(rlay->rl_tls_ctx);
+	tls_config_free(rlay->rl_tls_cfg);
+	tls_config_free(rlay->rl_tls_client_cfg);
 
 	while ((rlt = TAILQ_FIRST(&rlay->rl_tables))) {
 		TAILQ_REMOVE(&rlay->rl_tables, rlt, rlt_entry);
@@ -1166,18 +1163,18 @@ relay_findbyaddr(struct relayd *env, struct relay_conf
 }
 
 EVP_PKEY *
-pkey_find(struct relayd *env, objid_t id)
+pkey_find(struct relayd *env, char * hash)
 {
 	struct ca_pkey	*pkey;
 
 	TAILQ_FOREACH(pkey, env->sc_pkeys, pkey_entry)
-		if (pkey->pkey_id == id)
+		if (strcmp(hash, pkey->pkey_hash) == 0)
 			return (pkey->pkey);
 	return (NULL);
 }
 
 struct ca_pkey *
-pkey_add(struct relayd *env, EVP_PKEY *pkey, objid_t id)
+pkey_add(struct relayd *env, EVP_PKEY *pkey, char *hash)
 {
 	struct ca_pkey	*ca_pkey;
 
@@ -1188,8 +1185,10 @@ pkey_add(struct relayd *env, EVP_PKEY *pkey, objid_t i
 		return (NULL);
 
 	ca_pkey->pkey = pkey;
-	ca_pkey->pkey_id = id;
-
+	if (strlcpy(ca_pkey->pkey_hash, hash, sizeof(ca_pkey->pkey_hash)) >=
+	    sizeof(ca_pkey->pkey_hash))
+		return (NULL);
+		
 	TAILQ_INSERT_TAIL(env->sc_pkeys, ca_pkey, pkey_entry);
 
 	return (ca_pkey);
@@ -1638,20 +1637,18 @@ parent_tls_ticket_rekey(int fd, short events, void *ar
 	static struct event	 rekeyev;
 	struct relayd		*env = arg;
 	struct timeval		 tv;
-	struct tls_ticket	 key;
+	struct relay_ticket_key	 key;
 
 	log_debug("relayd_tls_ticket_rekey: rekeying tickets");
 
-	arc4random_buf(key.tt_key_name, sizeof(key.tt_key_name));
-	arc4random_buf(key.tt_hmac_key, sizeof(key.tt_hmac_key));
-	arc4random_buf(key.tt_aes_key, sizeof(key.tt_aes_key));
-	key.tt_backup = 0;
+	key.tt_keyrev = arc4random();
+	arc4random_buf(key.tt_key, sizeof(key.tt_key));
 
 	proc_compose_imsg(env->sc_ps, PROC_RELAY, -1, IMSG_TLSTICKET_REKEY,
 	    -1, -1, &key, sizeof(key));
 
 	evtimer_set(&rekeyev, parent_tls_ticket_rekey, env);
 	timerclear(&tv);
-	tv.tv_sec = TLS_TICKET_REKEY_TIME;
+	tv.tv_sec = TLS_SESSION_LIFETIME / 4;
 	evtimer_add(&rekeyev, &tv);
 }
blob - fc24c9759507ea7b3eb542c1db310e9fb16f7cf0
blob + 58f5a7079631b1d45e2aee2587cf7b0dadfc8973
--- relayd.h
+++ relayd.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: relayd.h,v 1.239 2017/02/02 08:24:16 reyk Exp $	*/
+/*	$OpenBSD: relayd.h,v 1.240 2017/05/27 08:33:25 claudio Exp $	*/
 
 /*
  * Copyright (c) 2006 - 2016 Reyk Floeter <reyk@openbsd.org>
@@ -37,6 +37,7 @@
 #include <imsg.h>
 
 #include <openssl/ssl.h>
+#include <tls.h>
 
 #ifndef nitems
 #define	nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
@@ -61,6 +62,7 @@
 #define MAX_NAME_SIZE		64
 #define SRV_MAX_VIRTS		16
 #define TLS_NAME_SIZE		512
+#define TLS_CERT_HASH_SIZE	128
 #define RELAY_MAX_PREFETCH	256
 #define RELAY_MIN_PREFETCHED	32
 
@@ -166,7 +168,7 @@ struct ctl_tcp_event {
 	int			(*validate_read)(struct ctl_tcp_event *);
 	int			(*validate_close)(struct ctl_tcp_event *);
 
-	SSL			*ssl;	/* libssl object */
+	struct tls		*tls;
 };
 
 enum direction {
@@ -192,9 +194,11 @@ struct ctl_relay_event {
 	struct ctl_relay_event	*dst;
 	struct rsession		*con;
 
-	SSL			*ssl;	/* libssl object */
+	struct tls		*tls;
+	struct tls_config	*tls_cfg;
 
-	X509			*tlscert;
+	uint8_t			*tlscert;
+	size_t			 tlscert_len;
 
 	off_t			 splicelen;
 	off_t			 toread;
@@ -205,9 +209,6 @@ struct ctl_relay_event {
 	enum relay_state	 state;
 	enum direction		 dir;
 
-	u_int8_t		*buf;
-	int			 buflen;
-
 	/* protocol-specific descriptor */
 	void			*desc;
 };
@@ -243,7 +244,7 @@ struct ctl_bindany {
 };
 
 struct ctl_keyop {
-	objid_t			 cko_id;
+	char			 cko_hash[TLS_CERT_HASH_SIZE];
 	int			 cko_proc;
 	int			 cko_flen;
 	int			 cko_tlen;
@@ -496,7 +497,7 @@ struct table {
 	int			 up;
 	int			 skipped;
 	struct hostlist		 hosts;
-	SSL_CTX			*ssl_ctx;	/* libssl context */
+	struct tls_config	*tls_cfg;
 	char			*sendbuf;
 };
 TAILQ_HEAD(tablelist, table);
@@ -680,20 +681,14 @@ TAILQ_HEAD(relay_rules, relay_rule);
 	"\06cipher-server-preference\07client-renegotiation"
 
 #define TLSCIPHERS_DEFAULT	"HIGH:!aNULL"
-#define TLSECDHCURVE_DEFAULT	NID_X9_62_prime256v1
+#define TLSECDHCURVE_DEFAULT	"auto"
+#define TLSDHPARAM_DEFAULT	"none"
 
-#define TLSDHPARAMS_NONE	0
-#define TLSDHPARAMS_DEFAULT	0
-#define TLSDHPARAMS_MIN		1024
-
-struct tls_ticket {
-	/* The key, aes key and hmac key must be 16 bytes / 128bits */
-	unsigned char	tt_key_name[16];
-	unsigned char	tt_aes_key[16];
-	unsigned char	tt_hmac_key[16];
-	int		tt_backup;
+struct relay_ticket_key {
+	uint32_t	tt_keyrev;
+	unsigned char	tt_key[TLS_TICKET_KEY_SIZE];
 };
-#define	TLS_TICKET_REKEY_TIME	(2 * 3600)
+#define	TLS_SESSION_LIFETIME	(2 * 3600)
 
 struct protocol {
 	objid_t			 id;
@@ -705,8 +700,8 @@ struct protocol {
 	u_int8_t		 tcpipminttl;
 	u_int8_t		 tlsflags;
 	char			 tlsciphers[768];
-	int			 tlsdhparams;
-	int			 tlsecdhcurve;
+	char			 tlsdhparams[128];
+	char			 tlsecdhcurve[128];
 	char			 tlsca[PATH_MAX];
 	char			 tlscacert[PATH_MAX];
 	char			 tlscakey[PATH_MAX];
@@ -742,7 +737,7 @@ struct relay_table {
 TAILQ_HEAD(relaytables, relay_table);
 
 struct ca_pkey {
-	objid_t			 pkey_id;
+	char			 pkey_hash[TLS_CERT_HASH_SIZE];
 	EVP_PKEY		*pkey;
 	TAILQ_ENTRY(ca_pkey)	 pkey_entry;
 };
@@ -788,10 +783,11 @@ struct relay {
 	struct event		 rl_ev;
 	struct event		 rl_evt;
 
-	SSL_CTX			*rl_ssl_ctx;	/* libssl context */
+	struct tls_config	*rl_tls_cfg;
+	struct tls_config	*rl_tls_client_cfg;
+	struct tls		*rl_tls_ctx;
 
 	char			*rl_tls_cert;
-	X509			*rl_tls_x509;
 	char			*rl_tls_key;
 	EVP_PKEY		*rl_tls_pkey;
 	char			*rl_tls_ca;
@@ -1092,11 +1088,10 @@ struct relayd {
 	struct ctl_icmp_event	 sc_icmp6_send;
 	struct ctl_icmp_event	 sc_icmp6_recv;
 
+	struct relay_ticket_key	 sc_ticket;
+
 	struct privsep		*sc_ps;
 	int			 sc_reload;
-
-	struct tls_ticket	 sc_tls_ticket;
-	struct tls_ticket	 sc_tls_ticket_bak;
 };
 
 #define RELAYD_OPT_VERBOSE		0x01
@@ -1237,6 +1232,9 @@ void	 check_icmp(struct relayd *, struct timeval *);
 /* check_tcp.c */
 void	 check_tcp(struct ctl_tcp_event *);
 
+/* check_tls.c */
+void	 check_tls(struct ctl_tcp_event *);
+
 /* check_script.c */
 void	 check_script(struct relayd *, struct host *);
 void	 script_done(struct relayd *, struct ctl_script *);
@@ -1244,15 +1242,11 @@ int	 script_exec(struct relayd *, struct ctl_script *)
 
 /* ssl.c */
 void	 ssl_init(struct relayd *);
-void	 ssl_transaction(struct ctl_tcp_event *);
-SSL_CTX	*ssl_ctx_create(struct relayd *);
-void	 ssl_error(const char *, const char *);
 char	*ssl_load_key(struct relayd *, const char *, off_t *, char *);
-X509	*ssl_update_certificate(X509 *, EVP_PKEY *, EVP_PKEY *, X509 *);
-int	 ssl_load_pkey(const void *, size_t, char *, off_t,
-	    X509 **, EVP_PKEY **);
-int	 ssl_ctx_fake_private_key(SSL_CTX *, const void *, size_t,
-	    char *, off_t, X509 **, EVP_PKEY **);
+uint8_t *ssl_update_certificate(const uint8_t *, size_t, EVP_PKEY *,
+	    EVP_PKEY *, X509 *, size_t *);
+int	 ssl_load_pkey(void *, char *, off_t, X509 **, EVP_PKEY **);
+int	 ssl_ctx_fake_private_key(char *, off_t, const char **);
 
 /* ca.c */
 void	 ca(struct privsep *, struct privsep_proc *);
@@ -1276,8 +1270,8 @@ struct protocol	*proto_find(struct relayd *, objid_t);
 struct rsession	*session_find(struct relayd *, objid_t);
 struct relay	*relay_findbyname(struct relayd *, const char *);
 struct relay	*relay_findbyaddr(struct relayd *, struct relay_config *);
-EVP_PKEY	*pkey_find(struct relayd *, objid_t);
-struct ca_pkey	*pkey_add(struct relayd *, EVP_PKEY *, objid_t);
+EVP_PKEY	*pkey_find(struct relayd *, char *hash);
+struct ca_pkey	*pkey_add(struct relayd *, EVP_PKEY *, char *hash);
 int		 expand_string(char *, size_t, const char *, const char *);
 void		 translate_string(char *);
 void		 purge_key(char **, off_t);
blob - f5dad7e36f0166be4c10bba5d2bba1853e8a7e1b
blob + 1ff423f58661b29f728944756423f6751b7567c1
--- ssl.c
+++ ssl.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: ssl.c,v 1.31 2017/01/09 14:49:21 reyk Exp $	*/
+/*	$OpenBSD: ssl.c,v 1.32 2017/05/27 08:33:25 claudio Exp $	*/
 
 /*
  * Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -18,12 +18,9 @@
  */
 
 #include <sys/types.h>
-#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/uio.h>
 
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <event.h>
 #include <unistd.h>
 #include <string.h>
 #include <imsg.h>
@@ -33,208 +30,11 @@
 #include <openssl/engine.h>
 
 #include "relayd.h"
+#include "boguskeys.h"
 
-void	ssl_read(int, short, void *);
-void	ssl_write(int, short, void *);
-void	ssl_connect(int, short, void *);
-void	ssl_cleanup(struct ctl_tcp_event *);
 int	ssl_password_cb(char *, int, int, void *);
 
 void
-ssl_read(int s, short event, void *arg)
-{
-	char			 rbuf[SMALL_READ_BUF_SIZE];
-	struct ctl_tcp_event	*cte = arg;
-	int			 retry_flag = EV_READ;
-	int			 tls_err = 0;
-	int			 ret;
-
-	if (event == EV_TIMEOUT) {
-		cte->host->up = HOST_DOWN;
-		ssl_cleanup(cte);
-		hce_notify_done(cte->host, HCE_TLS_READ_TIMEOUT);
-		return;
-	}
-
-	bzero(rbuf, sizeof(rbuf));
-
-	ret = SSL_read(cte->ssl, rbuf, sizeof(rbuf));
-	if (ret <= 0) {
-		tls_err = SSL_get_error(cte->ssl, ret);
-		switch (tls_err) {
-		case SSL_ERROR_WANT_READ:
-			retry_flag = EV_READ;
-			goto retry;
-		case SSL_ERROR_WANT_WRITE:
-			retry_flag = EV_WRITE;
-			goto retry;
-		case SSL_ERROR_ZERO_RETURN: /* FALLTHROUGH */
-		case SSL_ERROR_SYSCALL:
-			if (ret == 0) {
-				cte->host->up = HOST_DOWN;
-				(void)cte->validate_close(cte);
-				ssl_cleanup(cte);
-				hce_notify_done(cte->host, cte->host->he);
-				return;
-			}
-			/* FALLTHROUGH */
-		default:
-			cte->host->up = HOST_DOWN;
-			ssl_error(cte->host->conf.name, "cannot read");
-			ssl_cleanup(cte);
-			hce_notify_done(cte->host, HCE_TLS_READ_ERROR);
-			break;
-		}
-		return;
-	}
-	if (ibuf_add(cte->buf, rbuf, ret) == -1)
-		fatal("ssl_read: buf_add error");
-	if (cte->validate_read != NULL) {
-		if (cte->validate_read(cte) != 0)
-			goto retry;
-
-		ssl_cleanup(cte);
-		hce_notify_done(cte->host, cte->host->he);
-		return;
-	}
-
-retry:
-	event_again(&cte->ev, s, EV_TIMEOUT|retry_flag, ssl_read,
-	    &cte->tv_start, &cte->table->conf.timeout, cte);
-	return;
-}
-
-void
-ssl_write(int s, short event, void *arg)
-{
-	struct ctl_tcp_event	*cte = arg;
-	int			 retry_flag = EV_WRITE;
-	int			 tls_err = 0;
-	int			 len;
-	int			 ret;
-
-	if (event == EV_TIMEOUT) {
-		cte->host->up = HOST_DOWN;
-		ssl_cleanup(cte);
-		hce_notify_done(cte->host, HCE_TLS_WRITE_TIMEOUT);
-		return;
-	}
-
-	len = strlen(cte->table->sendbuf);
-
-	ret = SSL_write(cte->ssl, cte->table->sendbuf, len);
-	if (ret <= 0) {
-		tls_err = SSL_get_error(cte->ssl, ret);
-		switch (tls_err) {
-		case SSL_ERROR_WANT_READ:
-			retry_flag = EV_READ;
-			goto retry;
-		case SSL_ERROR_WANT_WRITE:
-			retry_flag = EV_WRITE;
-			goto retry;
-		default:
-			cte->host->up = HOST_DOWN;
-			ssl_error(cte->host->conf.name, "cannot write");
-			ssl_cleanup(cte);
-			hce_notify_done(cte->host, HCE_TLS_WRITE_ERROR);
-			return;
-		}
-	}
-	if ((cte->buf = ibuf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL)
-		fatalx("ssl_write: cannot create dynamic buffer");
-
-	event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, ssl_read,
-	    &cte->tv_start, &cte->table->conf.timeout, cte);
-	return;
-retry:
-	event_again(&cte->ev, s, EV_TIMEOUT|retry_flag, ssl_write,
-	    &cte->tv_start, &cte->table->conf.timeout, cte);
-}
-
-void
-ssl_connect(int s, short event, void *arg)
-{
-	struct ctl_tcp_event	*cte = arg;
-	int			 retry_flag = 0;
-	int			 tls_err = 0;
-	int			 ret;
-
-	if (event == EV_TIMEOUT) {
-		cte->host->up = HOST_DOWN;
-		hce_notify_done(cte->host, HCE_TLS_CONNECT_TIMEOUT);
-		ssl_cleanup(cte);
-		return;
-	}
-
-	ret = SSL_connect(cte->ssl);
-	if (ret <= 0) {
-		tls_err = SSL_get_error(cte->ssl, ret);
-		switch (tls_err) {
-		case SSL_ERROR_WANT_READ:
-			retry_flag = EV_READ;
-			goto retry;
-		case SSL_ERROR_WANT_WRITE:
-			retry_flag = EV_WRITE;
-			goto retry;
-		default:
-			cte->host->up = HOST_DOWN;
-			ssl_error(cte->host->conf.name, "cannot connect");
-			hce_notify_done(cte->host, HCE_TLS_CONNECT_FAIL);
-			ssl_cleanup(cte);
-			return;
-		}
-	}
-
-	if (cte->table->conf.check == CHECK_TCP) {
-		cte->host->up = HOST_UP;
-		hce_notify_done(cte->host, HCE_TLS_CONNECT_OK);
-		ssl_cleanup(cte);
-		return;
-	}
-	if (cte->table->sendbuf != NULL) {
-		event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE, ssl_write,
-		    &cte->tv_start, &cte->table->conf.timeout, cte);
-		return;
-	}
-
-	if ((cte->buf = ibuf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL)
-		fatalx("ssl_connect: cannot create dynamic buffer");
-	event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_READ, ssl_read,
-	    &cte->tv_start, &cte->table->conf.timeout, cte);
-	return;
-
-retry:
-	event_again(&cte->ev, s, EV_TIMEOUT|retry_flag, ssl_connect,
-	    &cte->tv_start, &cte->table->conf.timeout, cte);
-}
-
-void
-ssl_cleanup(struct ctl_tcp_event *cte)
-{
-	close(cte->s);
-	if (cte->ssl != NULL) {
-		SSL_shutdown(cte->ssl);
-		SSL_clear(cte->ssl);
-	}
-	ibuf_free(cte->buf);
-	cte->buf = NULL;
-}
-
-void
-ssl_error(const char *where, const char *what)
-{
-	char		 errbuf[128];
-	unsigned long	 code;
-
-	if (log_getverbose() < 2)
-		return;
-	for (; (code = ERR_get_error()) != 0 ;) {
-		ERR_error_string_n(code, errbuf, sizeof(errbuf));
-		log_debug("SSL library error: %s: %s: %s", where, what, errbuf);
-	}
-}
-
-void
 ssl_init(struct relayd *env)
 {
 	static int	 initialized = 0;
@@ -252,43 +52,6 @@ ssl_init(struct relayd *env)
 	initialized = 1;
 }
 
-void
-ssl_transaction(struct ctl_tcp_event *cte)
-{
-	if (cte->ssl == NULL) {
-		cte->ssl = SSL_new(cte->table->ssl_ctx);
-		if (cte->ssl == NULL) {
-			ssl_error(cte->host->conf.name, "cannot create object");
-			fatal("cannot create SSL object");
-		}
-	}
-
-	if (SSL_set_fd(cte->ssl, cte->s) == 0) {
-		cte->host->up = HOST_UNKNOWN;
-		ssl_error(cte->host->conf.name, "cannot set fd");
-		ssl_cleanup(cte);
-		hce_notify_done(cte->host, HCE_TLS_CONNECT_ERROR);
-		return;
-	}
-	SSL_set_connect_state(cte->ssl);
-
-	event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE, ssl_connect,
-	    &cte->tv_start, &cte->table->conf.timeout, cte);
-}
-
-SSL_CTX *
-ssl_ctx_create(struct relayd *env)
-{
-	SSL_CTX	*ctx;
-
-	ctx = SSL_CTX_new(SSLv23_client_method());
-	if (ctx == NULL) {
-		ssl_error("ssl_ctx_create", "cannot create context");
-		fatal("could not create SSL context");
-	}
-	return (ctx);
-}
-
 int
 ssl_password_cb(char *buf, int size, int rwflag, void *u)
 {
@@ -345,8 +108,6 @@ ssl_load_key(struct relayd *env, const char *name, off
 	return (buf);
 
  fail:
-	ssl_error(__func__, name);
-
 	free(buf);
 	if (bio != NULL)
 		BIO_free_all(bio);
@@ -355,21 +116,45 @@ ssl_load_key(struct relayd *env, const char *name, off
 	return (NULL);
 }
 
-X509 *
-ssl_update_certificate(X509 *oldcert, EVP_PKEY *pkey, EVP_PKEY *capkey,
-    X509 *cacert)
+uint8_t *
+ssl_update_certificate(const uint8_t *oldcert, size_t oldlen, EVP_PKEY *pkey,
+    EVP_PKEY *capkey, X509 *cacert, size_t *newlen)
 {
 	char		 name[2][TLS_NAME_SIZE];
+	BIO		*in, *out = NULL;
+	BUF_MEM		*bptr = NULL;
 	X509		*cert = NULL;
+	uint8_t		*newcert = NULL, *foo = NULL;
 
+	/* XXX BIO_new_mem_buf is not using const so work around this */
+	if ((foo = malloc(oldlen)) == NULL) {	
+		log_warn("%s: malloc", __func__);
+		return (NULL);
+	}
+	memcpy(foo, oldcert, oldlen);
+
+	if ((in = BIO_new_mem_buf(foo, oldlen)) == NULL) {
+		log_warnx("%s: BIO_new_mem_buf failed", __func__);
+		goto done;
+	}
+
+	if ((cert = PEM_read_bio_X509(in, NULL,
+	    ssl_password_cb, NULL)) == NULL) {
+		log_warnx("%s: PEM_read_bio_X509 failed", __func__);
+		goto done;
+	}
+
+	BIO_free(in);
+	in = NULL;
+
 	name[0][0] = name[1][0] = '\0';
-	if (!X509_NAME_oneline(X509_get_subject_name(oldcert),
+	if (!X509_NAME_oneline(X509_get_subject_name(cert),
 	    name[0], sizeof(name[0])) ||
-	    !X509_NAME_oneline(X509_get_issuer_name(oldcert),
+	    !X509_NAME_oneline(X509_get_issuer_name(cert),
 	    name[1], sizeof(name[1])))
 		goto done;
 
-	if ((cert = X509_dup(oldcert)) == NULL)
+	if ((cert = X509_dup(cert)) == NULL)
 		goto done;
 
 	/* Update certificate key and use our CA as the issuer */
@@ -377,9 +162,9 @@ ssl_update_certificate(X509 *oldcert, EVP_PKEY *pkey, 
 	X509_set_issuer_name(cert, X509_get_subject_name(cacert));
 
 	/* Sign with our CA */
-	if (!X509_sign(cert, capkey, EVP_sha1())) {
-		X509_free(cert);
-		cert = NULL;
+	if (!X509_sign(cert, capkey, EVP_sha256())) {
+		log_warnx("%s: X509_sign failed", __func__);
+		goto done;
 	}
 
 #if DEBUG_CERT
@@ -390,15 +175,37 @@ ssl_update_certificate(X509 *oldcert, EVP_PKEY *pkey, 
 #endif
 #endif
 
- done:
-	if (cert == NULL)
-		ssl_error(__func__, name[0]);
+	/* write cert as PEM file */
+	out = BIO_new(BIO_s_mem());
+	if (out == NULL) {
+		log_warnx("%s: BIO_new failed", __func__);
+		goto done;
+	}
+	if (!PEM_write_bio_X509(out, cert)) {
+		log_warnx("%s: PEM_write_bio_X509 failed", __func__);
+		goto done;
+	}
+	BIO_get_mem_ptr(out, &bptr);
+	if ((newcert = malloc(bptr->length)) == NULL) {
+		log_warn("%s: malloc", __func__);
+		goto done;
+	}
+	memcpy(newcert, bptr->data, bptr->length);
+	*newlen = bptr->length;
 
-	return (cert);
+done:
+	free(foo);
+	if (in)
+		BIO_free(in);
+	if (out)
+		BIO_free(out);
+	if (cert)
+		X509_free(cert);
+	return (newcert);
 }
 
 int
-ssl_load_pkey(const void *data, size_t datalen, char *buf, off_t len,
+ssl_load_pkey(void *data, char *buf, off_t len,
     X509 **x509ptr, EVP_PKEY **pkeyptr)
 {
 	BIO		*in;
@@ -408,36 +215,38 @@ ssl_load_pkey(const void *data, size_t datalen, char *
 	void		*exdata = NULL;
 
 	if ((in = BIO_new_mem_buf(buf, len)) == NULL) {
-		SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_R_BUF_LIB);
+		log_warnx("%s: BIO_new_mem_buf failed", __func__);
 		return (0);
 	}
 
 	if ((x509 = PEM_read_bio_X509(in, NULL,
 	    ssl_password_cb, NULL)) == NULL) {
-		SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_R_PEM_LIB);
+		log_warnx("%s: PEM_read_bio_X509 failed", __func__);
 		goto fail;
 	}
 
 	if ((pkey = X509_get_pubkey(x509)) == NULL) {
-		SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_R_X509_LIB);
+		log_warnx("%s: X509_get_pubkey failed", __func__);
 		goto fail;
 	}
 
 	BIO_free(in);
 
-	if (data != NULL && datalen) {
-		if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL ||
-		    (exdata = malloc(datalen)) == NULL) {
-			SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_R_EVP_LIB);
+	if (data != NULL) {
+		if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) {
+			log_warnx("%s: failed to extract RSA", __func__);
 			goto fail;
 		}
 
-		memcpy(exdata, data, datalen);
-		RSA_set_ex_data(rsa, 0, exdata);
+		RSA_set_ex_data(rsa, 0, data);
 		RSA_free(rsa); /* dereference, will be cleaned up with pkey */
 	}
 
-	*x509ptr = x509;
+	if (x509ptr != NULL)
+		*x509ptr = x509;
+	else
+		X509_free(x509);
+
 	*pkeyptr = pkey;
 
 	return (1);
@@ -456,37 +265,63 @@ ssl_load_pkey(const void *data, size_t datalen, char *
 	return (0);
 }
 
+/*
+ * This function is a horrible hack but for RSA privsep to work a private key
+ * with correct size needs to be loaded into the tls config.
+ */
 int
-ssl_ctx_fake_private_key(SSL_CTX *ctx, const void *data, size_t datalen,
-    char *buf, off_t len, X509 **x509ptr, EVP_PKEY **pkeyptr)
+ssl_ctx_fake_private_key(char *buf, off_t len, const char **fake_key)
 {
-	int		 ret = 0;
+	BIO		*in;
 	EVP_PKEY	*pkey = NULL;
 	X509		*x509 = NULL;
+	int		 ret = -1, keylen;
 
-	if (!ssl_load_pkey(data, datalen, buf, len, &x509, &pkey))
+	if ((in = BIO_new_mem_buf(buf, len)) == NULL) {
+		log_warnx("%s: BIO_new_mem_buf failed", __func__);
 		return (0);
+	}
 
-	/*
-	 * Use the public key as the "private" key - the secret key
-	 * parameters are hidden in an extra process that will be
-	 * contacted by the RSA engine.  The SSL/TLS library needs at
-	 * least the public key parameters in the current process.
-	 */
-	ret = SSL_CTX_use_PrivateKey(ctx, pkey);
-	if (!ret)
-		SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_R_SSL_LIB);
+	if ((x509 = PEM_read_bio_X509(in, NULL, NULL, NULL)) == NULL) {
+		log_warnx("%s: PEM_read_bio_X509 failed", __func__);
+		goto fail;
+	}
 
-	if (pkeyptr != NULL)
-		*pkeyptr = pkey;
-	else if (pkey != NULL)
+	if ((pkey = X509_get_pubkey(x509)) == NULL) {
+		log_warnx("%s: X509_get_pubkey failed", __func__);
+		goto fail;
+	}
+
+	keylen = EVP_PKEY_size(pkey) * 8;
+	switch(keylen) {
+	case 1024:
+		*fake_key = bogus_1024;
+		ret = sizeof(bogus_1024);
+		break;
+	case 2048:
+		*fake_key = bogus_2048;
+		ret = sizeof(bogus_2048);
+		break;
+	case 4096:
+		*fake_key = bogus_4096;
+		ret = sizeof(bogus_4096);
+		break;
+	case 8192:
+		*fake_key = bogus_8192;
+		ret = sizeof(bogus_8192);
+		break;
+	default:
+		log_warnx("%s: key size %d not support", __func__, keylen);
+		ret = -1;
+		break;
+	}
+fail:
+	BIO_free(in);
+
+	if (pkey != NULL)
 		EVP_PKEY_free(pkey);
-
-	if (x509ptr != NULL)
-		*x509ptr = x509;
-	else if (x509 != NULL)
+	if (x509 != NULL)
 		X509_free(x509);
-
+	
 	return (ret);
 }
-