/*
 * Copyright (c) 2022 Joris Vink <joris@coders.se>
 *
 * 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.
 */

/*
 * This TLS backend is the original TLS code used in Kore.
 */

#include <sys/types.h>

#include <openssl/bio.h>
#include <openssl/dh.h>
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>

#include <poll.h>

#include "kore.h"
#include "http.h"

/*
 * Disable deprecated declaration warnings if we're building against
 * OpenSSL 3 as they marked all low-level APIs as deprecated.
 *
 * Work is being done to replace these, but for now let things build.
 */
#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif

#define TLS_SESSION_ID		"kore_tls_sessionid"

/* Helper for weird API designs (looking at you OpenSSL). */
union deconst {
	void		*p;
	const void	*cp;
};

static int	tls_domain_x509_verify(int, X509_STORE_CTX *);
static X509	*tls_domain_load_certificate_chain(SSL_CTX *,
		    const void *, size_t);
static EVP_PKEY	*tls_privsep_private_key(EVP_PKEY *, struct kore_domain *);

static int	tls_sni_cb(SSL *, int *, void *);
static void	tls_info_callback(const SSL *, int, int);

#if defined(KORE_USE_ACME)
static void	tls_acme_challenge_set_cert(SSL *, struct kore_domain *);
static int	tls_acme_alpn(SSL *, const unsigned char **, unsigned char *,
		    const unsigned char *, unsigned int, void *);
#endif

static void	tls_keymgr_await_data(void);
static void	tls_keymgr_msg_response(struct kore_msg *, const void *);

static int	tls_keymgr_rsa_init(RSA *);
static int	tls_keymgr_rsa_finish(RSA *);
static int	tls_keymgr_rsa_privenc(int, const unsigned char *,
		    unsigned char *, RSA *, int);

static DH		*dh_params = NULL;
static RSA_METHOD	*keymgr_rsa_meth = NULL;
static int		tls_version = KORE_TLS_VERSION_BOTH;
static char		*tls_cipher_list = KORE_DEFAULT_CIPHER_LIST;

static u_int8_t		keymgr_buf[2048];
static size_t		keymgr_buflen = 0;
static int		keymgr_response = 0;

#if defined(KORE_USE_ACME)
static u_int8_t acme_alpn_name[] =
    { 0xa, 'a', 'c', 'm', 'e', '-', 't', 'l', 's', '/', '1' };
#endif

struct kore_privsep	keymgr_privsep;
int			kore_keymgr_active = 0;

int
kore_tls_supported(void)
{
	return (KORE_RESULT_OK);
}

void
kore_tls_init(void)
{
	SSL_library_init();
	SSL_load_error_strings();
	ERR_load_crypto_strings();

}

void
kore_tls_log_version(void)
{
	kore_log(LOG_NOTICE, "TLS backend %s", OPENSSL_VERSION_TEXT);

#if !defined(TLS1_3_VERSION)
	if (!kore_quiet) {
		kore_log(LOG_NOTICE,
		    "%s has no TLS 1.3 - will only use TLS 1.2",
		    OPENSSL_VERSION_TEXT);
	}
#endif
}

void
kore_tls_cleanup(void)
{
	RSA_meth_free(keymgr_rsa_meth);
}

void
kore_tls_version_set(int version)
{
	tls_version = version;
}

void
kore_tls_dh_check(void)
{
	if (dh_params != NULL)
		return;

	if (!kore_tls_dh_load(KORE_DHPARAM_PATH))
		fatal("failed to load default DH parameters");
}

int
kore_tls_dh_load(const char *path)
{
	BIO	*bio;

	if (dh_params != NULL) {
		kore_log(LOG_ERR, "tls_dhparam already specified");
		return (KORE_RESULT_ERROR);
	}

	if ((bio = BIO_new_file(path, "r")) == NULL) {
		kore_log(LOG_ERR, "tls_dhparam file '%s' not accessible", path);
		return (KORE_RESULT_ERROR);
	}

	dh_params = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
	BIO_free(bio);

	if (dh_params == NULL) {
		kore_log(LOG_ERR, "PEM_read_bio_DHparams(): %s", ssl_errno_s);
		return (KORE_RESULT_ERROR);
	}

	return (KORE_RESULT_OK);
}

int
kore_tls_ciphersuite_set(const char *list)
{
	if (strcmp(tls_cipher_list, KORE_DEFAULT_CIPHER_LIST)) {
		kore_log(LOG_ERR, "tls_cipher specified twice");
		return (KORE_RESULT_ERROR);
	}

	tls_cipher_list = kore_strdup(list);

	return (KORE_RESULT_OK);
}

void
kore_tls_keymgr_init(void)
{
	const RSA_METHOD	*meth;

	if ((meth = RSA_get_default_method()) == NULL)
		fatal("failed to obtain RSA method");

	if ((keymgr_rsa_meth = RSA_meth_new("kore RSA keymgr method",
	    RSA_METHOD_FLAG_NO_CHECK)) == NULL)
		fatal("failed to setup RSA method");

	RSA_meth_set_init(keymgr_rsa_meth, tls_keymgr_rsa_init);
	RSA_meth_set_finish(keymgr_rsa_meth, tls_keymgr_rsa_finish);
	RSA_meth_set_priv_enc(keymgr_rsa_meth, tls_keymgr_rsa_privenc);

	RSA_meth_set_pub_enc(keymgr_rsa_meth, RSA_meth_get_pub_enc(meth));
	RSA_meth_set_pub_dec(keymgr_rsa_meth, RSA_meth_get_pub_dec(meth));
	RSA_meth_set_bn_mod_exp(keymgr_rsa_meth, RSA_meth_get_bn_mod_exp(meth));

	kore_msg_register(KORE_MSG_KEYMGR_RESP, tls_keymgr_msg_response);
}

void
kore_tls_domain_setup(struct kore_domain *dom, int type,
    const void *data, size_t datalen)
{
	const u_int8_t		*ptr;
	EVP_PKEY		*pkey;
	X509			*x509;
	STACK_OF(X509_NAME)	*certs;
	const SSL_METHOD	*method;

	if (dom->tls_ctx != NULL)
		SSL_CTX_free(dom->tls_ctx);

	if ((method = TLS_method()) == NULL)
		fatalx("TLS_method(): %s", ssl_errno_s);

	if ((dom->tls_ctx = SSL_CTX_new(method)) == NULL)
		fatalx("SSL_ctx_new(): %s", ssl_errno_s);

	if (!SSL_CTX_set_min_proto_version(dom->tls_ctx, TLS1_2_VERSION))
		fatalx("SSL_CTX_set_min_proto_version: %s", ssl_errno_s);

#if defined(TLS1_3_VERSION)
	if (!SSL_CTX_set_max_proto_version(dom->tls_ctx, TLS1_3_VERSION))
		fatalx("SSL_CTX_set_max_proto_version: %s", ssl_errno_s);
#else
	if (!SSL_CTX_set_max_proto_version(dom->tls_ctx, TLS1_2_VERSION))
		fatalx("SSL_CTX_set_min_proto_version: %s", ssl_errno_s);
#endif

	switch (tls_version) {
	case KORE_TLS_VERSION_1_3:
#if defined(TLS1_3_VERSION)
		if (!SSL_CTX_set_min_proto_version(dom->tls_ctx,
		    TLS1_3_VERSION)) {
			fatalx("SSL_CTX_set_min_proto_version: %s",
			    ssl_errno_s);
		}
		break;
#endif
	case KORE_TLS_VERSION_1_2:
		if (!SSL_CTX_set_max_proto_version(dom->tls_ctx,
		    TLS1_2_VERSION)) {
			fatalx("SSL_CTX_set_min_proto_version: %s",
			    ssl_errno_s);
		}
		break;
	case KORE_TLS_VERSION_BOTH:
		break;
	default:
		fatalx("unknown tls_version: %d", tls_version);
		return;
	}

	switch (type) {
	case KORE_PEM_CERT_CHAIN:
		x509 = tls_domain_load_certificate_chain(dom->tls_ctx,
		    data, datalen);
		break;
	case KORE_DER_CERT_DATA:
		ptr = data;
		if ((x509 = d2i_X509(NULL, &ptr, datalen)) == NULL)
			fatalx("d2i_X509: %s", ssl_errno_s);
		if (SSL_CTX_use_certificate(dom->tls_ctx, x509) == 0)
			fatalx("SSL_CTX_use_certificate: %s", ssl_errno_s);
		break;
	default:
		fatalx("%s: unknown type %d", __func__, type);
	}

	if (x509 == NULL) {
		kore_log(LOG_NOTICE, "failed to load certificate for '%s': %s",
		    dom->domain, ssl_errno_s);
		SSL_CTX_free(dom->tls_ctx);
		dom->tls_ctx = NULL;
		return;
	}

	if ((pkey = X509_get_pubkey(x509)) == NULL)
		fatalx("certificate has no public key");

	switch (EVP_PKEY_id(pkey)) {
	case EVP_PKEY_RSA:
		pkey = tls_privsep_private_key(pkey, dom);
		break;
	default:
		fatalx("unknown public key in certificate");
	}

	if (!SSL_CTX_use_PrivateKey(dom->tls_ctx, pkey))
		fatalx("SSL_CTX_use_PrivateKey(): %s", ssl_errno_s);

	if (!SSL_CTX_check_private_key(dom->tls_ctx)) {
		fatalx("Public/Private key for %s do not match (%s)",
		    dom->domain, ssl_errno_s);
	}

	if (dh_params == NULL)
		fatal("no DH parameters specified");

	SSL_CTX_set_tmp_dh(dom->tls_ctx, dh_params);
	SSL_CTX_set_options(dom->tls_ctx, SSL_OP_SINGLE_DH_USE);

	if (!SSL_CTX_set_ecdh_auto(dom->tls_ctx, 1))
		fatalx("SSL_CTX_set_ecdh_auto: %s", ssl_errno_s);

	SSL_CTX_set_options(dom->tls_ctx, SSL_OP_SINGLE_ECDH_USE);
	SSL_CTX_set_options(dom->tls_ctx, SSL_OP_NO_COMPRESSION);

	if (dom->cafile != NULL) {
		if ((certs = SSL_load_client_CA_file(dom->cafile)) == NULL) {
			fatalx("SSL_load_client_CA_file(%s): %s",
			    dom->cafile, ssl_errno_s);
		}

		SSL_CTX_load_verify_locations(dom->tls_ctx, dom->cafile, NULL);
		SSL_CTX_set_verify_depth(dom->tls_ctx, dom->x509_verify_depth);
		SSL_CTX_set_client_CA_list(dom->tls_ctx, certs);
		SSL_CTX_set_verify(dom->tls_ctx, SSL_VERIFY_PEER |
		    SSL_VERIFY_FAIL_IF_NO_PEER_CERT, tls_domain_x509_verify);
	}

	SSL_CTX_set_session_id_context(dom->tls_ctx,
	    (unsigned char *)TLS_SESSION_ID, strlen(TLS_SESSION_ID));
	SSL_CTX_set_mode(dom->tls_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);

	if (tls_version == KORE_TLS_VERSION_BOTH) {
		SSL_CTX_set_options(dom->tls_ctx, SSL_OP_NO_SSLv2);
		SSL_CTX_set_options(dom->tls_ctx, SSL_OP_NO_SSLv3);
		SSL_CTX_set_options(dom->tls_ctx, SSL_OP_NO_TLSv1);
		SSL_CTX_set_options(dom->tls_ctx, SSL_OP_NO_TLSv1_1);
	}

	SSL_CTX_set_options(dom->tls_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
	SSL_CTX_set_cipher_list(dom->tls_ctx, tls_cipher_list);

	SSL_CTX_set_info_callback(dom->tls_ctx, tls_info_callback);
	SSL_CTX_set_tlsext_servername_callback(dom->tls_ctx, tls_sni_cb);

#if defined(KORE_USE_ACME)
	SSL_CTX_set_alpn_select_cb(dom->tls_ctx, tls_acme_alpn, dom);
#endif

	X509_free(x509);
}

void
kore_tls_domain_crl(struct kore_domain *dom, const void *pem, size_t pemlen)
{
	int			err;
	BIO			*in;
	X509_CRL		*crl;
	X509_REVOKED		*rev;
	X509_STORE		*store;
	struct connection	*c, *next;

	ERR_clear_error();
	in = BIO_new_mem_buf(pem, pemlen);

	if ((store = SSL_CTX_get_cert_store(dom->tls_ctx)) == NULL) {
		BIO_free(in);
		kore_log(LOG_ERR, "SSL_CTX_get_cert_store(): %s", ssl_errno_s);
		return;
	}

	X509_STORE_set_flags(store,
	    X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);

	for (;;) {
		crl = PEM_read_bio_X509_CRL(in, NULL, NULL, NULL);
		if (crl == NULL) {
			err = ERR_GET_REASON(ERR_peek_last_error());
			if (err == PEM_R_NO_START_LINE) {
				ERR_clear_error();
				break;
			}

			kore_log(LOG_WARNING, "failed to read CRL %s: %s",
			    dom->crlfile, ssl_errno_s);
			continue;
		}

		if (!X509_STORE_add_crl(store, crl)) {
			err = ERR_GET_REASON(ERR_peek_last_error());
			if (err == X509_R_CERT_ALREADY_IN_HASH_TABLE) {
				X509_CRL_free(crl);
				continue;
			}

			kore_log(LOG_WARNING, "failed to add CRL %s: %s",
			    dom->crlfile, ssl_errno_s);
			X509_CRL_free(crl);
			continue;
		}

		/*
		 * Check if any accepted connection authenticated themselves
		 * with a now revoked certificate.
		 */
		for (c = TAILQ_FIRST(&connections); c != NULL; c = next) {
			next = TAILQ_NEXT(c, list);
			if (c->proto != CONN_PROTO_HTTP)
				continue;

			/*
			 * Prune any connection that is currently not yet done
			 * with the TLS handshake. This is to prevent a race
			 * where a handshake could not yet be complete but
			 * did pass the x509 verification step and their cert
			 * was revoked in this CRL update.
			 */
			if (c->state == CONN_STATE_TLS_SHAKE) {
				kore_connection_disconnect(c);
				continue;
			}

			if (c->tls_cert == NULL)
				continue;

			if (X509_CRL_get0_by_cert(crl, &rev, c->tls_cert) != 1)
				continue;

			kore_connection_log(c,
			    "connection removed, its certificate is revoked");

			kore_connection_disconnect(c);
		}
	}

	BIO_free(in);
}

void
kore_tls_domain_cleanup(struct kore_domain *dom)
{
	if (dom->tls_ctx != NULL)
		SSL_CTX_free(dom->tls_ctx);
}

int
kore_tls_connection_accept(struct connection *c)
{
	int		r;

	if (primary_dom == NULL) {
		kore_connection_log(c,
		    "TLS handshake but no TLS configured on server");
		return (KORE_RESULT_ERROR);
	}

	if (primary_dom->tls_ctx == NULL) {
		kore_connection_log(c,
		    "TLS configuration for %s not yet complete",
		    primary_dom->domain);
		return (KORE_RESULT_ERROR);
	}

	if (c->tls == NULL) {
		c->tls = SSL_new(primary_dom->tls_ctx);
		if (c->tls == NULL)
			return (KORE_RESULT_ERROR);

		SSL_set_fd(c->tls, c->fd);
		SSL_set_accept_state(c->tls);

		if (!SSL_set_ex_data(c->tls, 0, c)) {
			kore_connection_log(c,
			    "SSL_set_ex_data: %s", ssl_errno_s);
			return (KORE_RESULT_ERROR);
		}

		if (primary_dom->cafile != NULL)
			c->flags |= CONN_LOG_TLS_FAILURE;
	}

	ERR_clear_error();
	r = SSL_accept(c->tls);
	if (r <= 0) {
		r = SSL_get_error(c->tls, r);
		switch (r) {
		case SSL_ERROR_WANT_READ:
		case SSL_ERROR_WANT_WRITE:
			kore_connection_start_idletimer(c);
			return (KORE_RESULT_RETRY);
		default:
			if (c->flags & CONN_LOG_TLS_FAILURE) {
				kore_connection_log(c,
				    "SSL_accept: %s", ssl_errno_s);
			}
			return (KORE_RESULT_ERROR);
		}
	}

#if defined(KORE_USE_ACME)
	if (c->proto == CONN_PROTO_ACME_ALPN) {
		kore_connection_log(c, "disconnecting ACME client");
		kore_connection_disconnect(c);
		return (KORE_RESULT_ERROR);
	}
#endif

	if (SSL_get_verify_mode(c->tls) & SSL_VERIFY_PEER) {
		c->tls_cert = SSL_get_peer_certificate(c->tls);
		if (c->tls_cert == NULL) {
			kore_connection_log(c, "no peer certificate returned");
			return (KORE_RESULT_ERROR);
		}
	} else {
		c->tls_cert = NULL;
	}

	return (KORE_RESULT_OK);
}

int
kore_tls_read(struct connection *c, size_t *bytes)
{
	int		r;

	ERR_clear_error();
	r = SSL_read(c->tls, (c->rnb->buf + c->rnb->s_off),
	    (c->rnb->b_len - c->rnb->s_off));

	if (c->tls_reneg > 1)
		return (KORE_RESULT_ERROR);

	if (r <= 0) {
		r = SSL_get_error(c->tls, r);
		switch (r) {
		case SSL_ERROR_WANT_READ:
		case SSL_ERROR_WANT_WRITE:
			c->evt.flags &= ~KORE_EVENT_READ;
			return (KORE_RESULT_OK);
		case SSL_ERROR_ZERO_RETURN:
			return (KORE_RESULT_ERROR);
		case SSL_ERROR_SYSCALL:
			switch (errno) {
			case EINTR:
				*bytes = 0;
				return (KORE_RESULT_OK);
			case EAGAIN:
				c->evt.flags &= ~KORE_EVENT_READ;
				c->snb->flags |= NETBUF_MUST_RESEND;
				return (KORE_RESULT_OK);
			default:
				break;
			}
			/* FALLTHROUGH */
		default:
			if (c->flags & CONN_LOG_TLS_FAILURE) {
				kore_connection_log(c, "SSL_read: %s",
				    ssl_errno_s);
			}
			return (KORE_RESULT_ERROR);
		}
	}

	*bytes = (size_t)r;

	return (KORE_RESULT_OK);
}

int
kore_tls_write(struct connection *c, size_t len, size_t *written)
{
	int		r;

	if (len > INT_MAX)
		return (KORE_RESULT_ERROR);

	ERR_clear_error();
	r = SSL_write(c->tls, (c->snb->buf + c->snb->s_off), len);
	if (c->tls_reneg > 1)
		return (KORE_RESULT_ERROR);

	if (r <= 0) {
		r = SSL_get_error(c->tls, r);
		switch (r) {
		case SSL_ERROR_WANT_READ:
		case SSL_ERROR_WANT_WRITE:
			c->evt.flags &= ~KORE_EVENT_WRITE;
			c->snb->flags |= NETBUF_MUST_RESEND;
			return (KORE_RESULT_OK);
		case SSL_ERROR_SYSCALL:
			switch (errno) {
			case EINTR:
				*written = 0;
				return (KORE_RESULT_OK);
			case EAGAIN:
				c->evt.flags &= ~KORE_EVENT_WRITE;
				c->snb->flags |= NETBUF_MUST_RESEND;
				return (KORE_RESULT_OK);
			default:
				break;
			}
			/* FALLTHROUGH */
		default:
			if (c->flags & CONN_LOG_TLS_FAILURE) {
				kore_connection_log(c,
				    "SSL_write: %s", ssl_errno_s);
			}
			return (KORE_RESULT_ERROR);
		}
	}

	*written = (size_t)r;

	return (KORE_RESULT_OK);
}

void
kore_tls_connection_cleanup(struct connection *c)
{
	if (c->tls != NULL) {
		SSL_shutdown(c->tls);
		SSL_free(c->tls);
	}

	if (c->tls_cert != NULL)
		X509_free(c->tls_cert);

	if (c->tls_sni != NULL)
		kore_free(c->tls_sni);
}


KORE_PRIVATE_KEY *
kore_tls_rsakey_load(const char *path)
{
	FILE			*fp;
	KORE_PRIVATE_KEY	*pkey;

	if (access(path, R_OK) == -1)
		return (NULL);

	if ((fp = fopen(path, "r")) == NULL)
		fatalx("%s(%s): %s", __func__, path, errno_s);

	if ((pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL)) == NULL)
		fatalx("PEM_read_PrivateKey: %s", ssl_errno_s);

	fclose(fp);

	return (pkey);
}

KORE_PRIVATE_KEY *
kore_tls_rsakey_generate(const char *path)
{
	FILE			*fp;
	EVP_PKEY_CTX		*ctx;
	KORE_PRIVATE_KEY	*pkey;

	if ((ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL)) == NULL)
		fatalx("EVP_PKEY_CTX_new_id: %s", ssl_errno_s);

	if (EVP_PKEY_keygen_init(ctx) <= 0)
		fatalx("EVP_PKEY_keygen_init: %s", ssl_errno_s);

	if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, KORE_RSAKEY_BITS) <= 0)
		fatalx("EVP_PKEY_CTX_set_rsa_keygen_bits: %s", ssl_errno_s);

	pkey = NULL;
	if (EVP_PKEY_keygen(ctx, &pkey) <= 0)
		fatalx("EVP_PKEY_keygen: %s", ssl_errno_s);

	if (path != NULL) {
		if ((fp = fopen(path, "w")) == NULL)
			fatalx("fopen(%s): %s", path, errno_s);

		if (!PEM_write_PrivateKey(fp, pkey, NULL, NULL, 0, NULL, NULL))
			fatalx("PEM_write_PrivateKey: %s", ssl_errno_s);

		fclose(fp);
	}

	return (pkey);
}

KORE_X509_NAMES *
kore_tls_x509_subject_name(struct connection *c)
{
	X509_NAME	*name;

	if ((name = X509_get_subject_name(c->tls_cert)) == NULL) {
		kore_connection_log(c,
		    "X509_get_subject_name: %s", ssl_errno_s);
	}

	return (name);
}

KORE_X509_NAMES *
kore_tls_x509_issuer_name(struct connection *c)
{
	X509_NAME	*name;

	if ((name = X509_get_issuer_name(c->tls_cert)) == NULL)
		kore_connection_log(c, "X509_get_issuer_name: %s", ssl_errno_s);

	return (name);
}

int
kore_tls_x509name_foreach(KORE_X509_NAMES *name, int flags, void *udata,
    int (*cb)(void *, int, int, const char *, const void *, size_t, int))
{
	u_int8_t		*data;
	ASN1_STRING		*astr;
	X509_NAME_ENTRY		*entry;
	const char		*field;
	int			islast, ret, idx, namelen, nid, len;

	data = NULL;
	ret = KORE_RESULT_ERROR;

	if ((namelen = X509_NAME_entry_count(name)) == 0)
		goto cleanup;

	for (idx = 0; idx < namelen; idx++) {
		if ((entry = X509_NAME_get_entry(name, idx)) == NULL)
			goto cleanup;

		nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(entry));
		if ((field = OBJ_nid2sn(nid)) == NULL)
			goto cleanup;

		switch (nid) {
		case NID_commonName:
			nid = KORE_X509_NAME_COMMON_NAME;
			break;
		default:
			nid = -1;
			break;
		}

		if ((astr = X509_NAME_ENTRY_get_data(entry)) == NULL)
			goto cleanup;

		data = NULL;
		if ((len = ASN1_STRING_to_UTF8(&data, astr)) < 0)
			goto cleanup;

		if (idx != (namelen - 1))
			islast = 0;
		else
			islast = 1;

		if (!cb(udata, islast, nid, field, data, len, flags))
			goto cleanup;

		OPENSSL_free(data);
		data = NULL;
	}

	ret = KORE_RESULT_OK;

cleanup:
	if (data != NULL)
		OPENSSL_free(data);

	return (ret);
}

int
kore_tls_x509_data(struct connection *c, u_int8_t **ptr, size_t *olen)
{
	int		len;
	u_int8_t	*der, *pp;

	if ((len = i2d_X509(c->tls_cert, NULL)) <= 0) {
		kore_connection_log(c, "i2d_X509: %s", ssl_errno_s);
		return (KORE_RESULT_ERROR);
	}

	der = kore_calloc(1, len);
	pp = der;

	if (i2d_X509(c->tls_cert, &pp) <= 0) {
		kore_free(der);
		kore_connection_log(c, "i2d_X509: %s", ssl_errno_s);
		return (KORE_RESULT_ERROR);
	}

	*ptr = der;
	*olen = len;

	return (KORE_RESULT_OK);
}

void
kore_tls_seed(const void *data, size_t len)
{
	RAND_poll();
	RAND_seed(data, len);
}

static void
tls_info_callback(const SSL *ssl, int flags, int ret)
{
	struct connection	*c;

	if (flags & SSL_CB_HANDSHAKE_START) {
		if ((c = SSL_get_app_data(ssl)) == NULL)
			fatal("no SSL_get_app_data");

#if defined(TLS1_3_VERSION)
		if (SSL_version(ssl) != TLS1_3_VERSION)
#endif
			c->tls_reneg++;
	}
}

static int
tls_sni_cb(SSL *ssl, int *ad, void *arg)
{
	struct connection	*c;
	struct kore_domain	*dom;
	const char		*sname;

	if ((c = SSL_get_ex_data(ssl, 0)) == NULL)
		fatal("no connection data in %s", __func__);

	sname = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);

	if (sname != NULL)
		c->tls_sni = kore_strdup(sname);

	if (sname != NULL &&
	    (dom = kore_domain_lookup(c->owner->server, sname)) != NULL) {
		if (dom->tls_ctx == NULL) {
			kore_log(LOG_NOTICE,
			    "TLS configuration for %s not complete",
			    dom->domain);
			return (SSL_TLSEXT_ERR_NOACK);
		}

		SSL_set_SSL_CTX(ssl, dom->tls_ctx);

		if (dom->cafile != NULL) {
			SSL_set_verify(ssl, SSL_VERIFY_PEER |
			    SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
			c->flags |= CONN_LOG_TLS_FAILURE;
		} else {
			SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL);
		}

#if defined(KORE_USE_ACME)
		/*
		 * If ALPN callback was called before SNI was parsed we
		 * must make sure we swap to the correct certificate now.
		 */
		if (c->flags & CONN_TLS_ALPN_ACME_SEEN)
			tls_acme_challenge_set_cert(ssl, dom);

		c->flags |= CONN_TLS_SNI_SEEN;
#endif
		return (SSL_TLSEXT_ERR_OK);
	}

	return (SSL_TLSEXT_ERR_NOACK);
}

static int
tls_keymgr_rsa_init(RSA *rsa)
{
	if (rsa != NULL) {
		RSA_set_flags(rsa, RSA_flags(rsa) |
		    RSA_FLAG_EXT_PKEY | RSA_METHOD_FLAG_NO_CHECK);
		return (1);
	}

	return (0);
}

static int
tls_keymgr_rsa_privenc(int flen, const unsigned char *from, unsigned char *to,
    RSA *rsa, int padding)
{
	int			ret;
	size_t			len;
	struct kore_keyreq	*req;
	struct kore_domain	*dom;

	len = sizeof(*req) + flen;
	if (len > sizeof(keymgr_buf))
		fatal("keymgr_buf too small");

	if ((dom = RSA_get_app_data(rsa)) == NULL)
		fatal("RSA key has no domain attached");

	memset(keymgr_buf, 0, sizeof(keymgr_buf));

	req = (struct kore_keyreq *)keymgr_buf;

	if (kore_strlcpy(req->domain, dom->domain, sizeof(req->domain)) >=
	    sizeof(req->domain))
		fatal("%s: domain truncated", __func__);

	req->data_len = flen;
	req->padding = padding;
	memcpy(&req->data[0], from, req->data_len);

	kore_msg_send(KORE_WORKER_KEYMGR, KORE_MSG_KEYMGR_REQ, keymgr_buf, len);
	tls_keymgr_await_data();

	ret = -1;
	if (keymgr_response) {
		if (keymgr_buflen < INT_MAX &&
		    (int)keymgr_buflen == RSA_size(rsa)) {
			ret = RSA_size(rsa);
			memcpy(to, keymgr_buf, RSA_size(rsa));
		}
	}

	keymgr_buflen = 0;
	keymgr_response = 0;
	kore_platform_event_all(worker->msg[1]->fd, worker->msg[1]);

	return (ret);
}

static int
tls_keymgr_rsa_finish(RSA *rsa)
{
	return (1);
}

static void
tls_keymgr_await_data(void)
{
	int			ret;
	struct pollfd		pfd[1];
	u_int64_t		start, cur;
#if !defined(KORE_NO_HTTP)
	int			process_requests;
#endif

	/*
	 * We need to wait until the keymgr responds to us, so keep doing
	 * net_recv_flush() until our callback for KORE_MSG_KEYMGR_RESP
	 * tells us that we have obtained the response.
	 *
	 * This means other internal messages can still be delivered by
	 * this worker process to the appropriate callbacks but we do not
	 * drop out until we've either received an answer from the keymgr
	 * or until the timeout has been reached (1 second currently).
	 *
	 * If we end up waiting for the keymgr process we will call
	 * http_process (if not built with NOHTTP=1) to further existing
	 * requests so those do not block too much.
	 *
	 * This means that all incoming data will stop being processed
	 * while existing requests will get processed until we return
	 * from this call.
	 */
	start = kore_time_ms();
	kore_platform_disable_read(worker->msg[1]->fd);

	keymgr_response = 0;
	memset(keymgr_buf, 0, sizeof(keymgr_buf));

#if !defined(KORE_NO_HTTP)
	process_requests = 0;
#endif

	for (;;) {
#if !defined(KORE_NO_HTTP)
		if (process_requests) {
			http_process();
			process_requests = 0;
		}
#endif
		pfd[0].fd = worker->msg[1]->fd;
		pfd[0].events = POLLIN;
		pfd[0].revents = 0;

		ret = poll(pfd, 1, 100);
		if (ret == -1) {
			if (errno == EINTR)
				continue;
			fatal("poll: %s", errno_s);
		}

		cur = kore_time_ms();
		if ((cur - start) > 1000)
			break;
		if (ret == 0) {
#if !defined(KORE_NO_HTTP)
			/* No activity on channel, process HTTP requests. */
			process_requests = 1;
#endif
			continue;
		}

		if (pfd[0].revents & (POLLERR | POLLHUP))
			break;
		if (!(pfd[0].revents & POLLIN))
			break;

		worker->msg[1]->evt.flags |= KORE_EVENT_READ;
		if (!net_recv_flush(worker->msg[1]))
			break;

		if (keymgr_response)
			break;

#if !defined(KORE_NO_HTTP)
		/* If we've spent 100ms already, process HTTP requests. */
		if ((cur - start) > 100) {
			process_requests = 1;
		}
#endif
	}
}

static void
tls_keymgr_msg_response(struct kore_msg *msg, const void *data)
{
	keymgr_response = 1;
	keymgr_buflen = msg->length;

	if (keymgr_buflen > sizeof(keymgr_buf))
		return;

	memcpy(keymgr_buf, data, keymgr_buflen);
}

static int
tls_domain_x509_verify(int ok, X509_STORE_CTX *ctx)
{
	struct connection	*c;
	SSL			*tls;
	X509			*cert;
	const char		*text;
	int			error, depth;

	if ((tls = X509_STORE_CTX_get_ex_data(ctx,
	    SSL_get_ex_data_X509_STORE_CTX_idx())) == NULL)
		fatal("X509_STORE_CTX_get_ex_data: no data");

	if ((c = SSL_get_ex_data(tls, 0)) == NULL)
		fatal("no connection data in %s", __func__);

	error = X509_STORE_CTX_get_error(ctx);
	cert = X509_STORE_CTX_get_current_cert(ctx);

	if (ok == 0 && cert != NULL) {
		text = X509_verify_cert_error_string(error);
		depth = X509_STORE_CTX_get_error_depth(ctx);

		kore_connection_log(c,
		    "X509 verification error depth:%d - %s", depth, text);

		/* Continue on CRL validity errors. */
		switch (error) {
		case X509_V_ERR_CRL_HAS_EXPIRED:
		case X509_V_ERR_CRL_NOT_YET_VALID:
		case X509_V_ERR_UNABLE_TO_GET_CRL:
			ok = 1;
			break;
		}
	}

	return (ok);
}

/*
 * What follows is basically a reimplementation of
 * SSL_CTX_use_certificate_chain_file() from OpenSSL but with our
 * BIO set to the pem data that we received.
 */
static X509 *
tls_domain_load_certificate_chain(SSL_CTX *ctx, const void *data, size_t len)
{
	unsigned long	err;
	BIO		*in;
	X509		*x, *ca;

	ERR_clear_error();
	in = BIO_new_mem_buf(data, len);

	if ((x = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL)) == NULL)
		return (NULL);

	/* refcount for x509 will go up one. */
	if (SSL_CTX_use_certificate(ctx, x) == 0)
		return (NULL);

	SSL_CTX_clear_chain_certs(ctx);

	ERR_clear_error();
	while ((ca = PEM_read_bio_X509(in, NULL, NULL, NULL)) != NULL) {
		/* ca its reference count won't be increased. */
		if (SSL_CTX_add0_chain_cert(ctx, ca) == 0)
			return (NULL);
	}

	err = ERR_peek_last_error();

	if (ERR_GET_LIB(err) != ERR_LIB_PEM ||
	    ERR_GET_REASON(err) != PEM_R_NO_START_LINE)
		return (NULL);

	BIO_free(in);

	return (x);
}

static EVP_PKEY *
tls_privsep_private_key(EVP_PKEY *pub, struct kore_domain *dom)
{
	EVP_PKEY		*pkey;
	RSA			*rsa_new;
	const RSA		*rsa_cur;
	const BIGNUM		*e_cur, *n_cur;
	BIGNUM			*e_new, *n_new;

	if ((pkey = EVP_PKEY_new()) == NULL)
		fatalx("failed to create new EVP_PKEY data structure");

	if ((rsa_new = RSA_new()) == NULL)
		fatalx("failed to create new RSA data structure");

	if ((rsa_cur = EVP_PKEY_get0_RSA(pub)) == NULL)
		fatalx("no RSA public key present");

	RSA_get0_key(rsa_cur, &n_cur, &e_cur, NULL);

	if ((n_new = BN_dup(n_cur)) == NULL)
		fatalx("BN_dup failed");

	if ((e_new = BN_dup(e_cur)) == NULL)
		fatalx("BN_dup failed");

	RSA_set_app_data(rsa_new, dom);
	RSA_set_method(rsa_new, keymgr_rsa_meth);
	RSA_set0_key(rsa_new, n_new, e_new, NULL);

	EVP_PKEY_set1_RSA(pkey, rsa_new);

	return (pkey);
}

#if defined(KORE_USE_ACME)
static int
tls_acme_alpn(SSL *ssl, const unsigned char **out, unsigned char *outlen,
    const unsigned char *in, unsigned int inlen, void *udata)
{
	struct connection	*c;

	if ((c = SSL_get_ex_data(ssl, 0)) == NULL)
		fatal("%s: no connection data present", __func__);

	if (inlen != sizeof(acme_alpn_name))
		return (SSL_TLSEXT_ERR_NOACK);

	if (memcmp(acme_alpn_name, in, sizeof(acme_alpn_name)))
		return (SSL_TLSEXT_ERR_NOACK);

	*out = in + 1;
	*outlen = inlen - 1;

	c->flags |= CONN_TLS_ALPN_ACME_SEEN;

	/*
	 * If SNI was already done, we can continue, otherwise we mark
	 * that we saw the right ALPN negotiation on this connection
	 * and wait for the SNI extension to be parsed.
	 */
	if (c->flags & CONN_TLS_SNI_SEEN) {
		/* SNI was seen, we are on the right domain. */
		tls_acme_challenge_set_cert(ssl, udata);
	}

	return (SSL_TLSEXT_ERR_OK);
}

static void
tls_acme_challenge_set_cert(SSL *ssl, struct kore_domain *dom)
{
	struct connection	*c;
	const unsigned char	*ptr;
	X509			*x509;

	if (dom->acme == 0) {
		kore_log(LOG_NOTICE, "[%s] ACME not active", dom->domain);
		return;
	}

	if (dom->acme_challenge == 0) {
		kore_log(LOG_NOTICE,
		    "[%s] ACME auth challenge not active", dom->domain);
		return;
	}

	kore_log(LOG_INFO, "[%s] acme-tls/1 challenge requested",
	    dom->domain);

	if ((c = SSL_get_ex_data(ssl, 0)) == NULL)
		fatal("%s: no connection data present", __func__);

	ptr = dom->acme_cert;
	if ((x509 = d2i_X509(NULL, &ptr, dom->acme_cert_len)) == NULL)
		fatal("d2i_X509: %s", ssl_errno_s);

	if (SSL_use_certificate(ssl, x509) == 0)
		fatal("SSL_use_certificate: %s", ssl_errno_s);

	SSL_clear_chain_certs(ssl);
	SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL);

	c->proto = CONN_PROTO_ACME_ALPN;
}
#endif /* KORE_USE_ACME */
