160484Sobrien/*	$NetBSD: tls.c,v 1.4 2024/02/21 22:52:29 christos Exp $	*/
278828Sobrien
3218822Sdim/*
460484Sobrien * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
560484Sobrien *
660484Sobrien * SPDX-License-Identifier: MPL-2.0
760484Sobrien *
860484Sobrien * This Source Code Form is subject to the terms of the Mozilla Public
977298Sobrien * License, v. 2.0. If a copy of the MPL was not distributed with this
1060484Sobrien * file, you can obtain one at https://mozilla.org/MPL/2.0/.
1160484Sobrien *
12130561Sobrien * See the COPYRIGHT file distributed with this work for additional
1360484Sobrien * information regarding copyright ownership.
14130561Sobrien */
15130561Sobrien
16130561Sobrien#include <inttypes.h>
17130561Sobrien#include <netinet/in.h>
1860484Sobrien#include <stdlib.h>
19130561Sobrien#include <string.h>
20130561Sobrien#include <sys/socket.h>
21130561Sobrien#if HAVE_LIBNGHTTP2
22130561Sobrien#include <nghttp2/nghttp2.h>
2360484Sobrien#endif /* HAVE_LIBNGHTTP2 */
24130561Sobrien#include <arpa/inet.h>
25130561Sobrien
26218822Sdim#include <openssl/bn.h>
27218822Sdim#include <openssl/conf.h>
2860484Sobrien#include <openssl/crypto.h>
2960484Sobrien#include <openssl/dh.h>
3060484Sobrien#include <openssl/err.h>
3160484Sobrien#include <openssl/evp.h>
3260484Sobrien#include <openssl/opensslv.h>
3377298Sobrien#include <openssl/rand.h>
34130561Sobrien#include <openssl/rsa.h>
3560484Sobrien#include <openssl/x509_vfy.h>
36218822Sdim#include <openssl/x509v3.h>
3760484Sobrien
3860484Sobrien#include <isc/atomic.h>
3960484Sobrien#include <isc/ht.h>
4060484Sobrien#include <isc/log.h>
4160484Sobrien#include <isc/magic.h>
4260484Sobrien#include <isc/mutex.h>
4360484Sobrien#include <isc/mutexblock.h>
4460484Sobrien#include <isc/once.h>
4560484Sobrien#include <isc/random.h>
4660484Sobrien#include <isc/refcount.h>
4760484Sobrien#include <isc/rwlock.h>
4860484Sobrien#include <isc/sockaddr.h>
4960484Sobrien#include <isc/thread.h>
5060484Sobrien#include <isc/tls.h>
5160484Sobrien#include <isc/util.h>
5260484Sobrien
5360484Sobrien#include "openssl_shim.h"
5460484Sobrien#include "tls_p.h"
55130561Sobrien
5660484Sobrien#define COMMON_SSL_OPTIONS \
5760484Sobrien	(SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION)
5860484Sobrien
59130561Sobrienstatic isc_once_t init_once = ISC_ONCE_INIT;
60130561Sobrienstatic isc_once_t shut_once = ISC_ONCE_INIT;
61130561Sobrienstatic atomic_bool init_done = false;
62130561Sobrienstatic atomic_bool shut_done = false;
63130561Sobrien
64130561Sobrien#if OPENSSL_VERSION_NUMBER < 0x10100000L
65130561Sobrienstatic isc_mutex_t *locks = NULL;
66130561Sobrienstatic int nlocks;
67130561Sobrien
6860484Sobrienstatic void
6960484Sobrienisc__tls_lock_callback(int mode, int type, const char *file, int line) {
7060484Sobrien	UNUSED(file);
7160484Sobrien	UNUSED(line);
7260484Sobrien	if ((mode & CRYPTO_LOCK) != 0) {
7360484Sobrien		LOCK(&locks[type]);
7460484Sobrien	} else {
7560484Sobrien		UNLOCK(&locks[type]);
76130561Sobrien	}
7760484Sobrien}
7860484Sobrien
79130561Sobrienstatic void
8060484Sobrienisc__tls_set_thread_id(CRYPTO_THREADID *id) {
8160484Sobrien	CRYPTO_THREADID_set_numeric(id, (unsigned long)isc_thread_self());
8260484Sobrien}
8360484Sobrien#endif
8460484Sobrien
8560484Sobrienstatic void
8660484Sobrientls_initialize(void) {
8777298Sobrien	REQUIRE(!atomic_load(&init_done));
8860484Sobrien
8960484Sobrien#if OPENSSL_VERSION_NUMBER >= 0x10100000L
9077298Sobrien	RUNTIME_CHECK(OPENSSL_init_ssl(OPENSSL_INIT_ENGINE_ALL_BUILTIN |
9160484Sobrien					       OPENSSL_INIT_LOAD_CONFIG,
9260484Sobrien				       NULL) == 1);
9360484Sobrien#else
9460484Sobrien	nlocks = CRYPTO_num_locks();
9560484Sobrien	/*
9660484Sobrien	 * We can't use isc_mem API here, because it's called too
9760484Sobrien	 * early and when the isc_mem_debugging flags are changed
9860484Sobrien	 * later.
9960484Sobrien	 *
10060484Sobrien	 * Actually, since this is a single allocation at library load
10160484Sobrien	 * and deallocation at library unload, using the standard
102130561Sobrien	 * allocator without the tracking is fine for this purpose.
10360484Sobrien	 */
10477298Sobrien	locks = calloc(nlocks, sizeof(locks[0]));
10560484Sobrien	isc_mutexblock_init(locks, nlocks);
10660484Sobrien	CRYPTO_set_locking_callback(isc__tls_lock_callback);
10760484Sobrien	CRYPTO_THREADID_set_callback(isc__tls_set_thread_id);
108130561Sobrien
10960484Sobrien	CRYPTO_malloc_init();
11060484Sobrien	ERR_load_crypto_strings();
111130561Sobrien	SSL_load_error_strings();
112130561Sobrien	SSL_library_init();
11360484Sobrien
11460484Sobrien#if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000
11560484Sobrien	ENGINE_load_builtin_engines();
11660484Sobrien#endif
11760484Sobrien	OpenSSL_add_all_algorithms();
11860484Sobrien	OPENSSL_load_builtin_modules();
11960484Sobrien
12060484Sobrien	CONF_modules_load_file(NULL, NULL,
12160484Sobrien			       CONF_MFLAGS_DEFAULT_SECTION |
12260484Sobrien				       CONF_MFLAGS_IGNORE_MISSING_FILE);
123130561Sobrien#endif
12460484Sobrien
12560484Sobrien	/* Protect ourselves against unseeded PRNG */
12660484Sobrien	if (RAND_status() != 1) {
12760484Sobrien		FATAL_ERROR("OpenSSL pseudorandom number generator "
12860484Sobrien			    "cannot be initialized (see the `PRNG not "
12960484Sobrien			    "seeded' message in the OpenSSL FAQ)");
13060484Sobrien	}
131130561Sobrien
13260484Sobrien	atomic_compare_exchange_enforced(&init_done, &(bool){ false }, true);
13360484Sobrien}
13460484Sobrien
13560484Sobrienvoid
13660484Sobrienisc__tls_initialize(void) {
13760484Sobrien	isc_result_t result = isc_once_do(&init_once, tls_initialize);
13860484Sobrien	REQUIRE(result == ISC_R_SUCCESS);
13960484Sobrien	REQUIRE(atomic_load(&init_done));
14060484Sobrien}
14160484Sobrien
14260484Sobrienstatic void
14360484Sobrientls_shutdown(void) {
14460484Sobrien	REQUIRE(atomic_load(&init_done));
14560484Sobrien	REQUIRE(!atomic_load(&shut_done));
14660484Sobrien
14777298Sobrien#if OPENSSL_VERSION_NUMBER >= 0x10100000L
14860484Sobrien	OPENSSL_cleanup();
14960484Sobrien#else
15060484Sobrien	CONF_modules_unload(1);
15160484Sobrien	OBJ_cleanup();
15260484Sobrien	EVP_cleanup();
15360484Sobrien#if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000
15477298Sobrien	ENGINE_cleanup();
15560484Sobrien#endif
15660484Sobrien	CRYPTO_cleanup_all_ex_data();
15760484Sobrien	ERR_remove_thread_state(NULL);
15860484Sobrien	RAND_cleanup();
15960484Sobrien	ERR_free_strings();
16060484Sobrien
16160484Sobrien	CRYPTO_set_locking_callback(NULL);
16277298Sobrien
16360484Sobrien	if (locks != NULL) {
16460484Sobrien		isc_mutexblock_destroy(locks, nlocks);
16560484Sobrien		free(locks);
16660484Sobrien		locks = NULL;
16760484Sobrien	}
168130561Sobrien#endif
16960484Sobrien
17060484Sobrien	atomic_compare_exchange_enforced(&shut_done, &(bool){ false }, true);
17160484Sobrien}
172130561Sobrien
17360484Sobrienvoid
17460484Sobrienisc__tls_shutdown(void) {
17560484Sobrien	isc_result_t result = isc_once_do(&shut_once, tls_shutdown);
17660484Sobrien	REQUIRE(result == ISC_R_SUCCESS);
17760484Sobrien	REQUIRE(atomic_load(&shut_done));
17860484Sobrien}
17960484Sobrien
180130561Sobrienvoid
181218822Sdimisc_tlsctx_free(isc_tlsctx_t **ctxp) {
182218822Sdim	SSL_CTX *ctx = NULL;
183218822Sdim	REQUIRE(ctxp != NULL && *ctxp != NULL);
184218822Sdim
185218822Sdim	ctx = *ctxp;
186218822Sdim	*ctxp = NULL;
18760484Sobrien
188218822Sdim	SSL_CTX_free(ctx);
18960484Sobrien}
19060484Sobrien
19160484Sobrienvoid
19260484Sobrienisc_tlsctx_attach(isc_tlsctx_t *src, isc_tlsctx_t **ptarget) {
19360484Sobrien	REQUIRE(src != NULL);
19460484Sobrien	REQUIRE(ptarget != NULL && *ptarget == NULL);
19560484Sobrien
19660484Sobrien	RUNTIME_CHECK(SSL_CTX_up_ref(src) == 1);
19760484Sobrien
19889857Sobrien	*ptarget = src;
19960484Sobrien}
200104834Sobrien
20160484Sobrien#if HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
20260484Sobrien/*
203218822Sdim * Callback invoked by the SSL library whenever a new TLS pre-master secret
204218822Sdim * needs to be logged.
205218822Sdim */
20660484Sobrienstatic void
207104834Sobriensslkeylogfile_append(const SSL *ssl, const char *line) {
20860484Sobrien	UNUSED(ssl);
20960484Sobrien
21060484Sobrien	isc_log_write(isc_lctx, ISC_LOGCATEGORY_SSLKEYLOG, ISC_LOGMODULE_NETMGR,
21160484Sobrien		      ISC_LOG_INFO, "%s", line);
21260484Sobrien}
213130561Sobrien
21460484Sobrien/*
21560484Sobrien * Enable TLS pre-master secret logging if the SSLKEYLOGFILE environment
21660484Sobrien * variable is set.  This needs to be done on a per-context basis as that is
217130561Sobrien * how SSL_CTX_set_keylog_callback() works.
218130561Sobrien */
219130561Sobrienstatic void
22060484Sobriensslkeylogfile_init(isc_tlsctx_t *ctx) {
22160484Sobrien	if (getenv("SSLKEYLOGFILE") != NULL) {
22260484Sobrien		SSL_CTX_set_keylog_callback(ctx, sslkeylogfile_append);
22360484Sobrien	}
22460484Sobrien}
22560484Sobrien#else /* HAVE_SSL_CTX_SET_KEYLOG_CALLBACK */
22660484Sobrien#define sslkeylogfile_init(ctx)
22760484Sobrien#endif /* HAVE_SSL_CTX_SET_KEYLOG_CALLBACK */
22860484Sobrien
22960484Sobrienisc_result_t
23060484Sobrienisc_tlsctx_createclient(isc_tlsctx_t **ctxp) {
231130561Sobrien	unsigned long err;
23260484Sobrien	char errbuf[256];
23360484Sobrien	SSL_CTX *ctx = NULL;
23460484Sobrien	const SSL_METHOD *method = NULL;
23560484Sobrien
23660484Sobrien	REQUIRE(ctxp != NULL && *ctxp == NULL);
23760484Sobrien
23860484Sobrien	method = TLS_client_method();
23960484Sobrien	if (method == NULL) {
24060484Sobrien		goto ssl_error;
24160484Sobrien	}
24260484Sobrien	ctx = SSL_CTX_new(method);
24360484Sobrien	if (ctx == NULL) {
24460484Sobrien		goto ssl_error;
24560484Sobrien	}
24660484Sobrien
24760484Sobrien	SSL_CTX_set_options(ctx, COMMON_SSL_OPTIONS);
24860484Sobrien
24960484Sobrien#if HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
25060484Sobrien	SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
25160484Sobrien#else
25260484Sobrien	SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
25360484Sobrien					 SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);
25460484Sobrien#endif
25560484Sobrien
25660484Sobrien	sslkeylogfile_init(ctx);
25760484Sobrien
25860484Sobrien	*ctxp = ctx;
25960484Sobrien
26060484Sobrien	return (ISC_R_SUCCESS);
26160484Sobrien
26260484Sobrienssl_error:
26360484Sobrien	err = ERR_get_error();
26460484Sobrien	ERR_error_string_n(err, errbuf, sizeof(errbuf));
26560484Sobrien	isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR,
26660484Sobrien		      ISC_LOG_ERROR, "Error initializing TLS context: %s",
26760484Sobrien		      errbuf);
26860484Sobrien
26960484Sobrien	return (ISC_R_TLSERROR);
27060484Sobrien}
27160484Sobrien
27260484Sobrienisc_result_t
27360484Sobrienisc_tlsctx_load_certificate(isc_tlsctx_t *ctx, const char *keyfile,
27460484Sobrien			    const char *certfile) {
27560484Sobrien	int rv;
27660484Sobrien	REQUIRE(ctx != NULL);
27760484Sobrien	REQUIRE(keyfile != NULL);
27860484Sobrien	REQUIRE(certfile != NULL);
27960484Sobrien
28060484Sobrien	rv = SSL_CTX_use_certificate_chain_file(ctx, certfile);
281130561Sobrien	if (rv != 1) {
282130561Sobrien		return (ISC_R_TLSERROR);
283130561Sobrien	}
28460484Sobrien	rv = SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM);
28560484Sobrien	if (rv != 1) {
28660484Sobrien		return (ISC_R_TLSERROR);
28760484Sobrien	}
288130561Sobrien
28960484Sobrien	return (ISC_R_SUCCESS);
29060484Sobrien}
291130561Sobrien
29260484Sobrienisc_result_t
293130561Sobrienisc_tlsctx_createserver(const char *keyfile, const char *certfile,
29460484Sobrien			isc_tlsctx_t **ctxp) {
29560484Sobrien	int rv;
296130561Sobrien	unsigned long err;
29760484Sobrien	bool ephemeral = (keyfile == NULL && certfile == NULL);
29860484Sobrien	X509 *cert = NULL;
299130561Sobrien	EVP_PKEY *pkey = NULL;
30060484Sobrien	SSL_CTX *ctx = NULL;
30160484Sobrien#if OPENSSL_VERSION_NUMBER < 0x30000000L
302130561Sobrien	EC_KEY *eckey = NULL;
30360484Sobrien#else
30460484Sobrien	EVP_PKEY_CTX *pkey_ctx = NULL;
305130561Sobrien	EVP_PKEY *params_pkey = NULL;
30660484Sobrien#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
30760484Sobrien	char errbuf[256];
30860484Sobrien	const SSL_METHOD *method = NULL;
30960484Sobrien
31060484Sobrien	REQUIRE(ctxp != NULL && *ctxp == NULL);
31160484Sobrien	REQUIRE((keyfile == NULL) == (certfile == NULL));
31260484Sobrien
31360484Sobrien	method = TLS_server_method();
314130561Sobrien	if (method == NULL) {
31560484Sobrien		goto ssl_error;
31660484Sobrien	}
31760484Sobrien	ctx = SSL_CTX_new(method);
31860484Sobrien	if (ctx == NULL) {
31960484Sobrien		goto ssl_error;
32060484Sobrien	}
32160484Sobrien	RUNTIME_CHECK(ctx != NULL);
32260484Sobrien
32360484Sobrien	SSL_CTX_set_options(ctx, COMMON_SSL_OPTIONS);
32460484Sobrien
32560484Sobrien#if HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
32660484Sobrien	SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
32760484Sobrien#else
32860484Sobrien	SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
32960484Sobrien					 SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);
33060484Sobrien#endif
33160484Sobrien
33260484Sobrien	if (ephemeral) {
33360484Sobrien		const int group_nid = NID_X9_62_prime256v1;
33460484Sobrien
33560484Sobrien#if OPENSSL_VERSION_NUMBER < 0x30000000L
33660484Sobrien		eckey = EC_KEY_new_by_curve_name(group_nid);
33760484Sobrien		if (eckey == NULL) {
33860484Sobrien			goto ssl_error;
33960484Sobrien		}
34060484Sobrien
34160484Sobrien		/* Generate the key. */
34260484Sobrien		rv = EC_KEY_generate_key(eckey);
34360484Sobrien		if (rv != 1) {
34460484Sobrien			goto ssl_error;
34560484Sobrien		}
34660484Sobrien		pkey = EVP_PKEY_new();
34760484Sobrien		if (pkey == NULL) {
34860484Sobrien			goto ssl_error;
34960484Sobrien		}
35060484Sobrien		rv = EVP_PKEY_set1_EC_KEY(pkey, eckey);
35160484Sobrien		if (rv != 1) {
35260484Sobrien			goto ssl_error;
35360484Sobrien		}
35460484Sobrien
35560484Sobrien		/* Use a named curve and uncompressed point conversion form. */
35660484Sobrien#if HAVE_EVP_PKEY_GET0_EC_KEY
35760484Sobrien		EC_KEY_set_asn1_flag(EVP_PKEY_get0_EC_KEY(pkey),
35860484Sobrien				     OPENSSL_EC_NAMED_CURVE);
35960484Sobrien		EC_KEY_set_conv_form(EVP_PKEY_get0_EC_KEY(pkey),
36060484Sobrien				     POINT_CONVERSION_UNCOMPRESSED);
36160484Sobrien#else
36260484Sobrien		EC_KEY_set_asn1_flag(pkey->pkey.ec, OPENSSL_EC_NAMED_CURVE);
36360484Sobrien		EC_KEY_set_conv_form(pkey->pkey.ec,
364130561Sobrien				     POINT_CONVERSION_UNCOMPRESSED);
36560484Sobrien#endif /* HAVE_EVP_PKEY_GET0_EC_KEY */
36660484Sobrien
36760484Sobrien#if defined(SSL_CTX_set_ecdh_auto)
368130561Sobrien		/*
36960484Sobrien		 * Using this macro is required for older versions of OpenSSL to
370130561Sobrien		 * automatically enable ECDH support.
37160484Sobrien		 *
372130561Sobrien		 * On later versions this function is no longer needed and is
373130561Sobrien		 * deprecated.
374130561Sobrien		 */
375130561Sobrien		(void)SSL_CTX_set_ecdh_auto(ctx, 1);
376130561Sobrien#endif /* defined(SSL_CTX_set_ecdh_auto) */
377130561Sobrien
378130561Sobrien		/* Cleanup */
379130561Sobrien		EC_KEY_free(eckey);
380130561Sobrien		eckey = NULL;
381130561Sobrien#else
382130561Sobrien		/* Generate the key's parameters. */
383130561Sobrien		pkey_ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
384130561Sobrien		if (pkey_ctx == NULL) {
385130561Sobrien			goto ssl_error;
386130561Sobrien		}
387130561Sobrien		rv = EVP_PKEY_paramgen_init(pkey_ctx);
388130561Sobrien		if (rv != 1) {
389130561Sobrien			goto ssl_error;
390130561Sobrien		}
391130561Sobrien		rv = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pkey_ctx,
392130561Sobrien							    group_nid);
393130561Sobrien		if (rv != 1) {
394130561Sobrien			goto ssl_error;
395130561Sobrien		}
396130561Sobrien		rv = EVP_PKEY_paramgen(pkey_ctx, &params_pkey);
397130561Sobrien		if (rv != 1 || params_pkey == NULL) {
398130561Sobrien			goto ssl_error;
39960484Sobrien		}
40060484Sobrien		EVP_PKEY_CTX_free(pkey_ctx);
40160484Sobrien
40260484Sobrien		/* Generate the key. */
40377298Sobrien		pkey_ctx = EVP_PKEY_CTX_new(params_pkey, NULL);
40477298Sobrien		if (pkey_ctx == NULL) {
40577298Sobrien			goto ssl_error;
40677298Sobrien		}
407130561Sobrien		rv = EVP_PKEY_keygen_init(pkey_ctx);
40877298Sobrien		if (rv != 1) {
40960484Sobrien			goto ssl_error;
41077298Sobrien		}
41160484Sobrien		rv = EVP_PKEY_keygen(pkey_ctx, &pkey);
412130561Sobrien		if (rv != 1 || pkey == NULL) {
41377298Sobrien			goto ssl_error;
41477298Sobrien		}
415130561Sobrien
41677298Sobrien		/* Cleanup */
41760484Sobrien		EVP_PKEY_free(params_pkey);
41860484Sobrien		params_pkey = NULL;
41977298Sobrien		EVP_PKEY_CTX_free(pkey_ctx);
42077298Sobrien		pkey_ctx = NULL;
42177298Sobrien#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
42277298Sobrien
423130561Sobrien		cert = X509_new();
42477298Sobrien		if (cert == NULL) {
42560484Sobrien			goto ssl_error;
42677298Sobrien		}
42760484Sobrien
428130561Sobrien		ASN1_INTEGER_set(X509_get_serialNumber(cert),
42960484Sobrien				 (long)isc_random32());
43060484Sobrien
431130561Sobrien		/*
43260484Sobrien		 * Set the "not before" property 5 minutes into the past to
43360484Sobrien		 * accommodate with some possible clock skew across systems.
43460484Sobrien		 */
43560484Sobrien#if OPENSSL_VERSION_NUMBER < 0x10101000L
43677298Sobrien		X509_gmtime_adj(X509_get_notBefore(cert), -300);
43777298Sobrien#else
43877298Sobrien		X509_gmtime_adj(X509_getm_notBefore(cert), -300);
439130561Sobrien#endif
44077298Sobrien
44160484Sobrien		/*
44277298Sobrien		 * We set the vailidy for 10 years.
44360484Sobrien		 */
444130561Sobrien#if OPENSSL_VERSION_NUMBER < 0x10101000L
44577298Sobrien		X509_gmtime_adj(X509_get_notAfter(cert), 3650 * 24 * 3600);
44677298Sobrien#else
447130561Sobrien		X509_gmtime_adj(X509_getm_notAfter(cert), 3650 * 24 * 3600);
44877298Sobrien#endif
44960484Sobrien
45060484Sobrien		X509_set_pubkey(cert, pkey);
45160484Sobrien
45277298Sobrien		X509_NAME *name = X509_get_subject_name(cert);
45360484Sobrien
45460484Sobrien		X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
45577298Sobrien					   (const unsigned char *)"AQ", -1, -1,
45677298Sobrien					   0);
45760484Sobrien		X509_NAME_add_entry_by_txt(
458130561Sobrien			name, "O", MBSTRING_ASC,
45977298Sobrien			(const unsigned char *)"BIND9 ephemeral "
46060484Sobrien					       "certificate",
46177298Sobrien			-1, -1, 0);
46260484Sobrien		X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
463130561Sobrien					   (const unsigned char *)"bind9.local",
46477298Sobrien					   -1, -1, 0);
46577298Sobrien
466130561Sobrien		X509_set_issuer_name(cert, name);
46777298Sobrien		X509_sign(cert, pkey, EVP_sha256());
46860484Sobrien		rv = SSL_CTX_use_certificate(ctx, cert);
46960484Sobrien		if (rv != 1) {
47077298Sobrien			goto ssl_error;
47177298Sobrien		}
47277298Sobrien		rv = SSL_CTX_use_PrivateKey(ctx, pkey);
47377298Sobrien		if (rv != 1) {
474130561Sobrien			goto ssl_error;
47577298Sobrien		}
47660484Sobrien
47777298Sobrien		X509_free(cert);
47860484Sobrien		EVP_PKEY_free(pkey);
479130561Sobrien	} else {
48077298Sobrien		isc_result_t result;
48177298Sobrien		result = isc_tlsctx_load_certificate(ctx, keyfile, certfile);
482130561Sobrien		if (result != ISC_R_SUCCESS) {
48377298Sobrien			goto ssl_error;
48460484Sobrien		}
48560484Sobrien	}
48660484Sobrien
48760484Sobrien	sslkeylogfile_init(ctx);
48877298Sobrien
48977298Sobrien	*ctxp = ctx;
49077298Sobrien	return (ISC_R_SUCCESS);
49177298Sobrien
492130561Sobrienssl_error:
49377298Sobrien	err = ERR_get_error();
49460484Sobrien	ERR_error_string_n(err, errbuf, sizeof(errbuf));
49577298Sobrien	isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR,
49660484Sobrien		      ISC_LOG_ERROR, "Error initializing TLS context: %s",
497130561Sobrien		      errbuf);
49877298Sobrien
49977298Sobrien	if (ctx != NULL) {
500130561Sobrien		SSL_CTX_free(ctx);
50177298Sobrien	}
50260484Sobrien	if (cert != NULL) {
50360484Sobrien		X509_free(cert);
50460484Sobrien	}
50560484Sobrien	if (pkey != NULL) {
50677298Sobrien		EVP_PKEY_free(pkey);
50777298Sobrien	}
50877298Sobrien#if OPENSSL_VERSION_NUMBER < 0x30000000L
509130561Sobrien	if (eckey != NULL) {
51077298Sobrien		EC_KEY_free(eckey);
51160484Sobrien	}
51277298Sobrien#else
51360484Sobrien	if (params_pkey != NULL) {
514130561Sobrien		EVP_PKEY_free(params_pkey);
51577298Sobrien	}
51677298Sobrien	if (pkey_ctx != NULL) {
517130561Sobrien		EVP_PKEY_CTX_free(pkey_ctx);
51877298Sobrien	}
51960484Sobrien#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
52060484Sobrien
52160484Sobrien	return (ISC_R_TLSERROR);
52260484Sobrien}
52360484Sobrien
52477298Sobrienstatic long
52577298Sobrienget_tls_version_disable_bit(const isc_tls_protocol_version_t tls_ver) {
52677298Sobrien	long bit = 0;
52777298Sobrien
528130561Sobrien	switch (tls_ver) {
52977298Sobrien	case ISC_TLS_PROTO_VER_1_2:
53060484Sobrien#ifdef SSL_OP_NO_TLSv1_2
53177298Sobrien		bit = SSL_OP_NO_TLSv1_2;
53260484Sobrien#else
533130561Sobrien		bit = 0;
53477298Sobrien#endif
53577298Sobrien		break;
536130561Sobrien	case ISC_TLS_PROTO_VER_1_3:
53777298Sobrien#ifdef SSL_OP_NO_TLSv1_3
53860484Sobrien		bit = SSL_OP_NO_TLSv1_3;
53960484Sobrien#else
54060484Sobrien		bit = 0;
54177298Sobrien#endif
54277298Sobrien		break;
54377298Sobrien	default:
54477298Sobrien		UNREACHABLE();
545130561Sobrien		break;
54677298Sobrien	};
54760484Sobrien
54877298Sobrien	return (bit);
54960484Sobrien}
550130561Sobrien
55177298Sobrienbool
55277298Sobrienisc_tls_protocol_supported(const isc_tls_protocol_version_t tls_ver) {
553130561Sobrien	return (get_tls_version_disable_bit(tls_ver) != 0);
55477298Sobrien}
55560484Sobrien
55660484Sobrienisc_tls_protocol_version_t
55760484Sobrienisc_tls_protocol_name_to_version(const char *name) {
55877298Sobrien	REQUIRE(name != NULL);
55977298Sobrien
56077298Sobrien	if (strcasecmp(name, "TLSv1.2") == 0) {
56177298Sobrien		return (ISC_TLS_PROTO_VER_1_2);
562130561Sobrien	} else if (strcasecmp(name, "TLSv1.3") == 0) {
56377298Sobrien		return (ISC_TLS_PROTO_VER_1_3);
56460484Sobrien	}
56577298Sobrien
56660484Sobrien	return (ISC_TLS_PROTO_VER_UNDEFINED);
567130561Sobrien}
56877298Sobrien
56977298Sobrienvoid
570130561Sobrienisc_tlsctx_set_protocols(isc_tlsctx_t *ctx, const uint32_t tls_versions) {
57177298Sobrien	REQUIRE(ctx != NULL);
57260484Sobrien	REQUIRE(tls_versions != 0);
57360484Sobrien	long set_options = 0;
57460484Sobrien	long clear_options = 0;
57577298Sobrien	uint32_t versions = tls_versions;
57677298Sobrien
57777298Sobrien	/*
57877298Sobrien	 * The code below might be initially hard to follow because of the
579130561Sobrien	 * double negation that OpenSSL enforces.
58077298Sobrien	 *
58160484Sobrien	 * Taking into account that OpenSSL provides bits to *disable*
58277298Sobrien	 * specific protocol versions, like SSL_OP_NO_TLSv1_2,
58360484Sobrien	 * SSL_OP_NO_TLSv1_3, etc., the code has the following logic:
584130561Sobrien	 *
58577298Sobrien	 * If a protocol version is not specified in the bitmask, get the
58677298Sobrien	 * bit that disables it and add it to the set of TLS options to
587130561Sobrien	 * set ('set_options'). Otherwise, if a protocol version is set,
58877298Sobrien	 * add the bit to the set of options to clear ('clear_options').
58960484Sobrien	 */
59060484Sobrien
59160484Sobrien	/* TLS protocol versions are defined as powers of two. */
59277298Sobrien	for (uint32_t tls_ver = ISC_TLS_PROTO_VER_1_2;
59377298Sobrien	     tls_ver < ISC_TLS_PROTO_VER_UNDEFINED; tls_ver <<= 1)
59477298Sobrien	{
59577298Sobrien		if ((tls_versions & tls_ver) == 0) {
596130561Sobrien			set_options |= get_tls_version_disable_bit(tls_ver);
59777298Sobrien		} else {
59860484Sobrien			/*
59977298Sobrien			 * Only supported versions should ever be passed to the
60060484Sobrien			 * function SSL_CTX_clear_options. For example, in order
601130561Sobrien			 * to enable TLS v1.2, we have to clear
60277298Sobrien			 * SSL_OP_NO_TLSv1_2. Insist that the configuration file
60377298Sobrien			 * was verified properly, so we are not trying to enable
604130561Sobrien			 * an unsupported TLS version.
60560484Sobrien			 */
60660484Sobrien			INSIST(isc_tls_protocol_supported(tls_ver));
60760484Sobrien			clear_options |= get_tls_version_disable_bit(tls_ver);
60860484Sobrien		}
60977298Sobrien		versions &= ~(tls_ver);
61077298Sobrien	}
61177298Sobrien
61277298Sobrien	/* All versions should be processed at this point, thus the value
613130561Sobrien	 * must equal zero. If it is not, then some garbage has been
61477298Sobrien	 * passed to the function; this situation is worth
61560484Sobrien	 * investigation. */
61677298Sobrien	INSIST(versions == 0);
61760484Sobrien
618130561Sobrien	(void)SSL_CTX_set_options(ctx, set_options);
61977298Sobrien	(void)SSL_CTX_clear_options(ctx, clear_options);
62077298Sobrien}
621130561Sobrien
62260484Sobrienbool
62360484Sobrienisc_tlsctx_load_dhparams(isc_tlsctx_t *ctx, const char *dhparams_file) {
62460484Sobrien	REQUIRE(ctx != NULL);
62560484Sobrien	REQUIRE(dhparams_file != NULL);
62677298Sobrien	REQUIRE(*dhparams_file != '\0');
62777298Sobrien
62877298Sobrien#if OPENSSL_VERSION_NUMBER < 0x30000000L
62977298Sobrien	/* OpenSSL < 3.0 */
630130561Sobrien	DH *dh = NULL;
63177298Sobrien	FILE *paramfile;
63260484Sobrien
63377298Sobrien	paramfile = fopen(dhparams_file, "r");
63460484Sobrien
635130561Sobrien	if (paramfile) {
63677298Sobrien		int check = 0;
63777298Sobrien		dh = PEM_read_DHparams(paramfile, NULL, NULL, NULL);
638130561Sobrien		fclose(paramfile);
63960484Sobrien
64060484Sobrien		if (dh == NULL) {
64160484Sobrien			return (false);
64260484Sobrien		} else if (DH_check(dh, &check) != 1 || check != 0) {
64377298Sobrien			DH_free(dh);
64477298Sobrien			return (false);
64577298Sobrien		}
64677298Sobrien	} else {
647130561Sobrien		return (false);
64877298Sobrien	}
64960484Sobrien
65077298Sobrien	if (SSL_CTX_set_tmp_dh(ctx, dh) != 1) {
65160484Sobrien		DH_free(dh);
652130561Sobrien		return (false);
65377298Sobrien	}
65477298Sobrien
655130561Sobrien	DH_free(dh);
65660484Sobrien#else
65760484Sobrien	/* OpenSSL >= 3.0: low level DH APIs are deprecated in OpenSSL 3.0 */
65860484Sobrien	EVP_PKEY *dh = NULL;
65960484Sobrien	BIO *bio = NULL;
66077298Sobrien
66177298Sobrien	bio = BIO_new_file(dhparams_file, "r");
66277298Sobrien	if (bio == NULL) {
66377298Sobrien		return (false);
664130561Sobrien	}
66577298Sobrien
66660484Sobrien	dh = PEM_read_bio_Parameters(bio, NULL);
66777298Sobrien	if (dh == NULL) {
66860484Sobrien		BIO_free(bio);
669130561Sobrien		return (false);
67077298Sobrien	}
67177298Sobrien
672130561Sobrien	if (SSL_CTX_set0_tmp_dh_pkey(ctx, dh) != 1) {
67360484Sobrien		BIO_free(bio);
67460484Sobrien		EVP_PKEY_free(dh);
67560484Sobrien		return (false);
67677298Sobrien	}
67777298Sobrien
67877298Sobrien	/* No need to call EVP_PKEY_free(dh) as the "dh" is owned by the
67977298Sobrien	 * SSL context at this point. */
680130561Sobrien
68177298Sobrien	BIO_free(bio);
68260484Sobrien#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
68377298Sobrien
68460484Sobrien	return (true);
685130561Sobrien}
68677298Sobrien
68777298Sobrienbool
688130561Sobrienisc_tls_cipherlist_valid(const char *cipherlist) {
68960484Sobrien	isc_tlsctx_t *tmp_ctx = NULL;
69060484Sobrien	const SSL_METHOD *method = NULL;
69160484Sobrien	bool result;
69277298Sobrien	REQUIRE(cipherlist != NULL);
69377298Sobrien
69477298Sobrien	if (*cipherlist == '\0') {
69577298Sobrien		return (false);
696130561Sobrien	}
69777298Sobrien
69860484Sobrien	method = TLS_server_method();
69977298Sobrien	if (method == NULL) {
70060484Sobrien		return (false);
701130561Sobrien	}
70277298Sobrien	tmp_ctx = SSL_CTX_new(method);
70377298Sobrien	if (tmp_ctx == NULL) {
704130561Sobrien		return (false);
70560484Sobrien	}
70660484Sobrien
70760484Sobrien	result = SSL_CTX_set_cipher_list(tmp_ctx, cipherlist) == 1;
70877298Sobrien
70977298Sobrien	isc_tlsctx_free(&tmp_ctx);
71077298Sobrien
71177298Sobrien	return (result);
712130561Sobrien}
71377298Sobrien
71460484Sobrienvoid
71577298Sobrienisc_tlsctx_set_cipherlist(isc_tlsctx_t *ctx, const char *cipherlist) {
71660484Sobrien	REQUIRE(ctx != NULL);
717130561Sobrien	REQUIRE(cipherlist != NULL);
71877298Sobrien	REQUIRE(*cipherlist != '\0');
71977298Sobrien
720130561Sobrien	RUNTIME_CHECK(SSL_CTX_set_cipher_list(ctx, cipherlist) == 1);
72160484Sobrien}
72260484Sobrien
72360484Sobrienvoid
72460484Sobrienisc_tlsctx_prefer_server_ciphers(isc_tlsctx_t *ctx, const bool prefer) {
72577298Sobrien	REQUIRE(ctx != NULL);
72677298Sobrien
72777298Sobrien	if (prefer) {
72877298Sobrien		(void)SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
729130561Sobrien	} else {
73077298Sobrien		(void)SSL_CTX_clear_options(ctx,
73160484Sobrien					    SSL_OP_CIPHER_SERVER_PREFERENCE);
73277298Sobrien	}
73360484Sobrien}
734130561Sobrien
73577298Sobrienvoid
73677298Sobrienisc_tlsctx_session_tickets(isc_tlsctx_t *ctx, const bool use) {
737130561Sobrien	REQUIRE(ctx != NULL);
73860484Sobrien
73960484Sobrien	if (!use) {
74060484Sobrien		(void)SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET);
741130561Sobrien	} else {
74260484Sobrien		(void)SSL_CTX_clear_options(ctx, SSL_OP_NO_TICKET);
74360484Sobrien	}
74460484Sobrien}
74560484Sobrien
74660484Sobrienisc_tls_t *
74760484Sobrienisc_tls_create(isc_tlsctx_t *ctx) {
74860484Sobrien	isc_tls_t *newctx = NULL;
74960484Sobrien
75077298Sobrien	REQUIRE(ctx != NULL);
75160484Sobrien
75260484Sobrien	newctx = SSL_new(ctx);
75360484Sobrien	if (newctx == NULL) {
75460484Sobrien		char errbuf[256];
75560484Sobrien		unsigned long err = ERR_get_error();
75677298Sobrien
75760484Sobrien		ERR_error_string_n(err, errbuf, sizeof(errbuf));
75860484Sobrien		fprintf(stderr, "%s:SSL_new(%p) -> %s\n", __func__, ctx,
75960484Sobrien			errbuf);
76060484Sobrien	}
76177298Sobrien
76277298Sobrien	return (newctx);
76360484Sobrien}
76460484Sobrien
76560484Sobrienvoid
766130561Sobrienisc_tls_free(isc_tls_t **tlsp) {
76760484Sobrien	isc_tls_t *tls = NULL;
768130561Sobrien	REQUIRE(tlsp != NULL && *tlsp != NULL);
769130561Sobrien
770130561Sobrien	tls = *tlsp;
771130561Sobrien	*tlsp = NULL;
77260484Sobrien	SSL_free(tls);
773130561Sobrien}
77460484Sobrien
77560484Sobrienconst char *
77660484Sobrienisc_tls_verify_peer_result_string(isc_tls_t *tls) {
77760484Sobrien	REQUIRE(tls != NULL);
77860484Sobrien
77960484Sobrien	return (X509_verify_cert_error_string(SSL_get_verify_result(tls)));
78060484Sobrien}
78160484Sobrien
782130561Sobrien#if HAVE_LIBNGHTTP2
78360484Sobrien#ifndef OPENSSL_NO_NEXTPROTONEG
78460484Sobrien/*
78560484Sobrien * NPN TLS extension client callback.
78660484Sobrien */
787130561Sobrienstatic int
78860484Sobrienselect_next_proto_cb(SSL *ssl, unsigned char **out, unsigned char *outlen,
78960484Sobrien		     const unsigned char *in, unsigned int inlen, void *arg) {
79060484Sobrien	UNUSED(ssl);
79160484Sobrien	UNUSED(arg);
79260484Sobrien
79360484Sobrien	if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
79460484Sobrien		return (SSL_TLSEXT_ERR_NOACK);
79560484Sobrien	}
79660484Sobrien	return (SSL_TLSEXT_ERR_OK);
79760484Sobrien}
79860484Sobrien#endif /* !OPENSSL_NO_NEXTPROTONEG */
79960484Sobrien
80060484Sobrienvoid
80160484Sobrienisc_tlsctx_enable_http2client_alpn(isc_tlsctx_t *ctx) {
80260484Sobrien	REQUIRE(ctx != NULL);
80360484Sobrien
80489857Sobrien#ifndef OPENSSL_NO_NEXTPROTONEG
80560484Sobrien	SSL_CTX_set_next_proto_select_cb(ctx, select_next_proto_cb, NULL);
80660484Sobrien#endif /* !OPENSSL_NO_NEXTPROTONEG */
80760484Sobrien
80860484Sobrien#if OPENSSL_VERSION_NUMBER >= 0x10002000L
80960484Sobrien	SSL_CTX_set_alpn_protos(ctx, (const unsigned char *)NGHTTP2_PROTO_ALPN,
81060484Sobrien				NGHTTP2_PROTO_ALPN_LEN);
81160484Sobrien#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
81260484Sobrien}
81360484Sobrien
81460484Sobrien#ifndef OPENSSL_NO_NEXTPROTONEG
81560484Sobrienstatic int
81660484Sobriennext_proto_cb(isc_tls_t *ssl, const unsigned char **data, unsigned int *len,
81760484Sobrien	      void *arg) {
81860484Sobrien	UNUSED(ssl);
81960484Sobrien	UNUSED(arg);
82089857Sobrien
82160484Sobrien	*data = (const unsigned char *)NGHTTP2_PROTO_ALPN;
82260484Sobrien	*len = (unsigned int)NGHTTP2_PROTO_ALPN_LEN;
82360484Sobrien	return (SSL_TLSEXT_ERR_OK);
82460484Sobrien}
82589857Sobrien#endif /* !OPENSSL_NO_NEXTPROTONEG */
82660484Sobrien
82760484Sobrien#if OPENSSL_VERSION_NUMBER >= 0x10002000L
82860484Sobrienstatic int
829130561Sobrienalpn_select_proto_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen,
83089857Sobrien		     const unsigned char *in, unsigned int inlen, void *arg) {
83189857Sobrien	int ret;
83289857Sobrien
83360484Sobrien	UNUSED(ssl);
83460484Sobrien	UNUSED(arg);
83560484Sobrien
83660484Sobrien	ret = nghttp2_select_next_protocol((unsigned char **)(uintptr_t)out,
83760484Sobrien					   outlen, in, inlen);
83860484Sobrien
83960484Sobrien	if (ret != 1) {
84060484Sobrien		return (SSL_TLSEXT_ERR_NOACK);
84160484Sobrien	}
84260484Sobrien
84360484Sobrien	return (SSL_TLSEXT_ERR_OK);
84460484Sobrien}
84560484Sobrien#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
84660484Sobrien
84760484Sobrienvoid
84860484Sobrienisc_tlsctx_enable_http2server_alpn(isc_tlsctx_t *tls) {
84960484Sobrien	REQUIRE(tls != NULL);
85060484Sobrien
85160484Sobrien#ifndef OPENSSL_NO_NEXTPROTONEG
85260484Sobrien	SSL_CTX_set_next_protos_advertised_cb(tls, next_proto_cb, NULL);
85360484Sobrien#endif // OPENSSL_NO_NEXTPROTONEG
85460484Sobrien#if OPENSSL_VERSION_NUMBER >= 0x10002000L
855130561Sobrien	SSL_CTX_set_alpn_select_cb(tls, alpn_select_proto_cb, NULL);
85660484Sobrien#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
85760484Sobrien}
85860484Sobrien#endif /* HAVE_LIBNGHTTP2 */
85960484Sobrien
860130561Sobrienvoid
861130561Sobrienisc_tls_get_selected_alpn(isc_tls_t *tls, const unsigned char **alpn,
86260484Sobrien			  unsigned int *alpnlen) {
86360484Sobrien	REQUIRE(tls != NULL);
86460484Sobrien	REQUIRE(alpn != NULL);
86560484Sobrien	REQUIRE(alpnlen != NULL);
86660484Sobrien
86760484Sobrien#ifndef OPENSSL_NO_NEXTPROTONEG
86860484Sobrien	SSL_get0_next_proto_negotiated(tls, alpn, alpnlen);
86960484Sobrien#endif
87060484Sobrien#if OPENSSL_VERSION_NUMBER >= 0x10002000L
87160484Sobrien	if (*alpn == NULL) {
87260484Sobrien		SSL_get0_alpn_selected(tls, alpn, alpnlen);
87360484Sobrien	}
87460484Sobrien#endif
87560484Sobrien}
87660484Sobrien
87760484Sobrienstatic bool
87860484Sobrienprotoneg_check_protocol(const uint8_t **pout, uint8_t *pout_len,
87960484Sobrien			const uint8_t *in, size_t in_len, const uint8_t *key,
88060484Sobrien			size_t key_len) {
88160484Sobrien	for (size_t i = 0; i + key_len <= in_len; i += (size_t)(in[i] + 1)) {
88277298Sobrien		if (memcmp(&in[i], key, key_len) == 0) {
88377298Sobrien			*pout = (const uint8_t *)(&in[i + 1]);
88460484Sobrien			*pout_len = in[i];
885130561Sobrien			return (true);
88660484Sobrien		}
88760484Sobrien	}
88860484Sobrien	return (false);
88989857Sobrien}
890130561Sobrien
89160484Sobrien/* dot prepended by its length (3 bytes) */
89289857Sobrien#define DOT_PROTO_ALPN	   "\x3" ISC_TLS_DOT_PROTO_ALPN_ID
89389857Sobrien#define DOT_PROTO_ALPN_LEN (sizeof(DOT_PROTO_ALPN) - 1)
89460484Sobrien
895130561Sobrienstatic bool
89689857Sobriendot_select_next_protocol(const uint8_t **pout, uint8_t *pout_len,
897130561Sobrien			 const uint8_t *in, size_t in_len) {
89889857Sobrien	return (protoneg_check_protocol(pout, pout_len, in, in_len,
89960484Sobrien					(const uint8_t *)DOT_PROTO_ALPN,
90089857Sobrien					DOT_PROTO_ALPN_LEN));
90160484Sobrien}
90260484Sobrien
90360484Sobrienvoid
90477298Sobrienisc_tlsctx_enable_dot_client_alpn(isc_tlsctx_t *ctx) {
90560484Sobrien	REQUIRE(ctx != NULL);
90660484Sobrien
90760484Sobrien#if OPENSSL_VERSION_NUMBER >= 0x10002000L
90860484Sobrien	SSL_CTX_set_alpn_protos(ctx, (const uint8_t *)DOT_PROTO_ALPN,
909130561Sobrien				DOT_PROTO_ALPN_LEN);
91060484Sobrien#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
91160484Sobrien}
91260484Sobrien
91360484Sobrien#if OPENSSL_VERSION_NUMBER >= 0x10002000L
914130561Sobrienstatic int
91560484Sobriendot_alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
91660484Sobrien			 unsigned char *outlen, const unsigned char *in,
91760484Sobrien			 unsigned int inlen, void *arg) {
91860484Sobrien	bool ret;
91960484Sobrien
92060484Sobrien	UNUSED(ssl);
92160484Sobrien	UNUSED(arg);
922130561Sobrien
923130561Sobrien	ret = dot_select_next_protocol(out, outlen, in, inlen);
92460484Sobrien
92560484Sobrien	if (!ret) {
92660484Sobrien		return (SSL_TLSEXT_ERR_NOACK);
92760484Sobrien	}
92860484Sobrien
929130561Sobrien	return (SSL_TLSEXT_ERR_OK);
93060484Sobrien}
93160484Sobrien#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
93260484Sobrien
93360484Sobrienvoid
934130561Sobrienisc_tlsctx_enable_dot_server_alpn(isc_tlsctx_t *tls) {
93560484Sobrien	REQUIRE(tls != NULL);
93660484Sobrien
93760484Sobrien#if OPENSSL_VERSION_NUMBER >= 0x10002000L
93860484Sobrien	SSL_CTX_set_alpn_select_cb(tls, dot_alpn_select_proto_cb, NULL);
939130561Sobrien#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
94060484Sobrien}
94160484Sobrien
942130561Sobrienisc_result_t
94360484Sobrienisc_tlsctx_enable_peer_verification(isc_tlsctx_t *tlsctx, const bool is_server,
94460484Sobrien				    isc_tls_cert_store_t *store,
94560484Sobrien				    const char *hostname,
94660484Sobrien				    bool hostname_ignore_subject) {
94760484Sobrien	int ret = 0;
94860484Sobrien	REQUIRE(tlsctx != NULL);
94960484Sobrien	REQUIRE(store != NULL);
95060484Sobrien
95160484Sobrien	/* Set the hostname/IP address. */
95260484Sobrien	if (!is_server && hostname != NULL && *hostname != '\0') {
95360484Sobrien		struct in6_addr sa6;
95460484Sobrien		struct in_addr sa;
95560484Sobrien		X509_VERIFY_PARAM *param = SSL_CTX_get0_param(tlsctx);
95660484Sobrien		unsigned int hostflags = X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS;
95760484Sobrien
95860484Sobrien		/* It might be an IP address. */
95960484Sobrien		if (inet_pton(AF_INET6, hostname, &sa6) == 1 ||
96060484Sobrien		    inet_pton(AF_INET, hostname, &sa) == 1)
96160484Sobrien		{
96260484Sobrien			ret = X509_VERIFY_PARAM_set1_ip_asc(param, hostname);
963130561Sobrien		} else {
96477298Sobrien			/* It seems that it is a host name. Let's set it. */
96560484Sobrien			ret = X509_VERIFY_PARAM_set1_host(param, hostname, 0);
966130561Sobrien		}
96760484Sobrien		if (ret != 1) {
96860484Sobrien			ERR_clear_error();
96960484Sobrien			return (ISC_R_FAILURE);
97077298Sobrien		}
97177298Sobrien
97260484Sobrien#ifdef X509_CHECK_FLAG_NEVER_CHECK_SUBJECT
97360484Sobrien		/*
97460484Sobrien		 * According to the RFC 8310, Section 8.1, Subject field MUST
97577298Sobrien		 * NOT be inspected when verifying a hostname when using
97660484Sobrien		 * DoT. Only SubjectAltName must be checked instead. That is
97760484Sobrien		 * not the case for HTTPS, though.
97860484Sobrien		 *
97960484Sobrien		 * Unfortunately, some quite old versions of OpenSSL (< 1.1.1)
98060484Sobrien		 * might lack the functionality to implement that. It should
98160484Sobrien		 * have very little real-world consequences, as most of the
98260484Sobrien		 * production-ready certificates issued by real CAs will have
98377298Sobrien		 * SubjectAltName set. In such a case, the Subject field is
98460484Sobrien		 * ignored.
98560484Sobrien		 */
98660484Sobrien		if (hostname_ignore_subject) {
987130561Sobrien			hostflags |= X509_CHECK_FLAG_NEVER_CHECK_SUBJECT;
98860484Sobrien		}
98960484Sobrien#else
99060484Sobrien		UNUSED(hostname_ignore_subject);
99160484Sobrien#endif
99260484Sobrien		X509_VERIFY_PARAM_set_hostflags(param, hostflags);
99360484Sobrien	}
99460484Sobrien
99560484Sobrien	/* "Attach" the cert store to the context */
99660484Sobrien	SSL_CTX_set1_cert_store(tlsctx, store);
99760484Sobrien
99860484Sobrien	/* enable verification */
99960484Sobrien	if (is_server) {
100060484Sobrien		SSL_CTX_set_verify(tlsctx,
1001130561Sobrien				   SSL_VERIFY_PEER |
100260484Sobrien					   SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
100360484Sobrien				   NULL);
100460484Sobrien	} else {
100560484Sobrien		SSL_CTX_set_verify(tlsctx, SSL_VERIFY_PEER, NULL);
100677298Sobrien	}
1007130561Sobrien
100860484Sobrien	return (ISC_R_SUCCESS);
100960484Sobrien}
1010130561Sobrien
1011130561Sobrienisc_result_t
101277298Sobrienisc_tlsctx_load_client_ca_names(isc_tlsctx_t *ctx, const char *ca_bundle_file) {
1013130561Sobrien	STACK_OF(X509_NAME) * cert_names;
101460484Sobrien	REQUIRE(ctx != NULL);
101560484Sobrien	REQUIRE(ca_bundle_file != NULL);
101660484Sobrien
101760484Sobrien	cert_names = SSL_load_client_CA_file(ca_bundle_file);
101860484Sobrien	if (cert_names == NULL) {
101960484Sobrien		ERR_clear_error();
102060484Sobrien		return (ISC_R_FAILURE);
102160484Sobrien	}
102260484Sobrien
102360484Sobrien	SSL_CTX_set_client_CA_list(ctx, cert_names);
102460484Sobrien
102560484Sobrien	return (ISC_R_SUCCESS);
102660484Sobrien}
102760484Sobrien
102860484Sobrienisc_result_t
102960484Sobrienisc_tls_cert_store_create(const char *ca_bundle_filename,
103060484Sobrien			  isc_tls_cert_store_t **pstore) {
103177298Sobrien	int ret = 0;
103260484Sobrien	isc_tls_cert_store_t *store = NULL;
103360484Sobrien	REQUIRE(pstore != NULL && *pstore == NULL);
103460484Sobrien
103560484Sobrien	store = X509_STORE_new();
103660484Sobrien	if (store == NULL) {
103760484Sobrien		goto error;
103860484Sobrien	}
103960484Sobrien
104060484Sobrien	/* Let's treat empty string as the default (system wide) store */
104160484Sobrien	if (ca_bundle_filename != NULL && *ca_bundle_filename == '\0') {
104260484Sobrien		ca_bundle_filename = NULL;
104360484Sobrien	}
104460484Sobrien
104577298Sobrien	if (ca_bundle_filename == NULL) {
104660484Sobrien		ret = X509_STORE_set_default_paths(store);
104777298Sobrien	} else {
104860484Sobrien		ret = X509_STORE_load_locations(store, ca_bundle_filename,
104960484Sobrien						NULL);
105060484Sobrien	}
105160484Sobrien
105260484Sobrien	if (ret == 0) {
105360484Sobrien		goto error;
105460484Sobrien	}
105560484Sobrien
105660484Sobrien	*pstore = store;
105777298Sobrien	return (ISC_R_SUCCESS);
105877298Sobrien
105960484Sobrienerror:
106060484Sobrien	ERR_clear_error();
106160484Sobrien	if (store != NULL) {
106260484Sobrien		X509_STORE_free(store);
106360484Sobrien	}
1064130561Sobrien	return (ISC_R_FAILURE);
106560484Sobrien}
106660484Sobrien
106760484Sobrienvoid
106860484Sobrienisc_tls_cert_store_free(isc_tls_cert_store_t **pstore) {
106960484Sobrien	isc_tls_cert_store_t *store;
107060484Sobrien	REQUIRE(pstore != NULL && *pstore != NULL);
107160484Sobrien
107260484Sobrien	store = *pstore;
107360484Sobrien
107460484Sobrien	X509_STORE_free(store);
107560484Sobrien
107660484Sobrien	*pstore = NULL;
107760484Sobrien}
107860484Sobrien
107960484Sobrien#define TLSCTX_CACHE_MAGIC    ISC_MAGIC('T', 'l', 'S', 'c')
108060484Sobrien#define VALID_TLSCTX_CACHE(t) ISC_MAGIC_VALID(t, TLSCTX_CACHE_MAGIC)
108160484Sobrien
108260484Sobrien#define TLSCTX_CLIENT_SESSION_CACHE_MAGIC ISC_MAGIC('T', 'l', 'C', 'c')
108360484Sobrien#define VALID_TLSCTX_CLIENT_SESSION_CACHE(t) \
108460484Sobrien	ISC_MAGIC_VALID(t, TLSCTX_CLIENT_SESSION_CACHE_MAGIC)
108560484Sobrien
108660484Sobrientypedef struct isc_tlsctx_cache_entry {
108760484Sobrien	/*
108860484Sobrien	 * We need a TLS context entry for each transport on both IPv4 and
108960484Sobrien	 * IPv6 in order to avoid cluttering a context-specific
109060484Sobrien	 * session-resumption cache.
109160484Sobrien	 */
109260484Sobrien	isc_tlsctx_t *ctx[isc_tlsctx_cache_count - 1][2];
109360484Sobrien	isc_tlsctx_client_session_cache_t
109460484Sobrien		*client_sess_cache[isc_tlsctx_cache_count - 1][2];
1095130561Sobrien	/*
1096130561Sobrien	 * One certificate store is enough for all the contexts defined
109760484Sobrien	 * above. We need that for peer validation.
109860484Sobrien	 */
109960484Sobrien	isc_tls_cert_store_t *ca_store;
110060484Sobrien} isc_tlsctx_cache_entry_t;
110177298Sobrien
1102130561Sobrienstruct isc_tlsctx_cache {
110360484Sobrien	uint32_t magic;
110460484Sobrien	isc_refcount_t references;
110560484Sobrien	isc_mem_t *mctx;
110660484Sobrien
1107218822Sdim	isc_rwlock_t rwlock;
110860484Sobrien	isc_ht_t *data;
1109130561Sobrien};
111060484Sobrien
111160484Sobrienvoid
111289857Sobrienisc_tlsctx_cache_create(isc_mem_t *mctx, isc_tlsctx_cache_t **cachep) {
111360484Sobrien	isc_tlsctx_cache_t *nc;
111460484Sobrien
111560484Sobrien	REQUIRE(cachep != NULL && *cachep == NULL);
111660484Sobrien	nc = isc_mem_get(mctx, sizeof(*nc));
111777298Sobrien
111860484Sobrien	*nc = (isc_tlsctx_cache_t){ .magic = TLSCTX_CACHE_MAGIC };
111977298Sobrien	isc_refcount_init(&nc->references, 1);
112060484Sobrien	isc_mem_attach(mctx, &nc->mctx);
112160484Sobrien
112277298Sobrien	isc_ht_init(&nc->data, mctx, 5, ISC_HT_CASE_SENSITIVE);
112360484Sobrien	isc_rwlock_init(&nc->rwlock, 0, 0);
112477298Sobrien
112577298Sobrien	*cachep = nc;
112660484Sobrien}
112760484Sobrien
112860484Sobrienvoid
1129130561Sobrienisc_tlsctx_cache_attach(isc_tlsctx_cache_t *source,
1130130561Sobrien			isc_tlsctx_cache_t **targetp) {
1131130561Sobrien	REQUIRE(VALID_TLSCTX_CACHE(source));
1132130561Sobrien	REQUIRE(targetp != NULL && *targetp == NULL);
1133130561Sobrien
1134130561Sobrien	isc_refcount_increment(&source->references);
113560484Sobrien
1136130561Sobrien	*targetp = source;
1137130561Sobrien}
113860484Sobrien
113960484Sobrienstatic void
114060484Sobrientlsctx_cache_entry_destroy(isc_mem_t *mctx, isc_tlsctx_cache_entry_t *entry) {
114160484Sobrien	size_t i, k;
114260484Sobrien
114360484Sobrien	for (i = 0; i < (isc_tlsctx_cache_count - 1); i++) {
114460484Sobrien		for (k = 0; k < 2; k++) {
114560484Sobrien			if (entry->ctx[i][k] != NULL) {
114660484Sobrien				isc_tlsctx_free(&entry->ctx[i][k]);
114760484Sobrien			}
114860484Sobrien
1149130561Sobrien			if (entry->client_sess_cache[i][k] != NULL) {
115060484Sobrien				isc_tlsctx_client_session_cache_detach(
1151130561Sobrien					&entry->client_sess_cache[i][k]);
115260484Sobrien			}
115360484Sobrien		}
115460484Sobrien	}
115560484Sobrien	if (entry->ca_store != NULL) {
1156130561Sobrien		isc_tls_cert_store_free(&entry->ca_store);
115789857Sobrien	}
115889857Sobrien	isc_mem_put(mctx, entry, sizeof(*entry));
115960484Sobrien}
116089857Sobrien
116160484Sobrienstatic void
116260484Sobrientlsctx_cache_destroy(isc_tlsctx_cache_t *cache) {
116360484Sobrien	isc_ht_iter_t *it = NULL;
1164130561Sobrien	isc_result_t result;
116560484Sobrien
116660484Sobrien	cache->magic = 0;
116760484Sobrien
116860484Sobrien	isc_refcount_destroy(&cache->references);
116960484Sobrien
117060484Sobrien	isc_ht_iter_create(cache->data, &it);
117160484Sobrien	for (result = isc_ht_iter_first(it); result == ISC_R_SUCCESS;
117277298Sobrien	     result = isc_ht_iter_delcurrent_next(it))
117360484Sobrien	{
117460484Sobrien		isc_tlsctx_cache_entry_t *entry = NULL;
117577298Sobrien		isc_ht_iter_current(it, (void **)&entry);
117677298Sobrien		tlsctx_cache_entry_destroy(cache->mctx, entry);
117777298Sobrien	}
117877298Sobrien
117977298Sobrien	isc_ht_iter_destroy(&it);
118077298Sobrien	isc_ht_destroy(&cache->data);
118177298Sobrien	isc_rwlock_destroy(&cache->rwlock);
118260484Sobrien	isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
118360484Sobrien}
118460484Sobrien
118560484Sobrienvoid
118677298Sobrienisc_tlsctx_cache_detach(isc_tlsctx_cache_t **cachep) {
1187130561Sobrien	isc_tlsctx_cache_t *cache = NULL;
118860484Sobrien
118989857Sobrien	REQUIRE(cachep != NULL);
119089857Sobrien
119160484Sobrien	cache = *cachep;
1192130561Sobrien	*cachep = NULL;
119389857Sobrien
119460484Sobrien	REQUIRE(VALID_TLSCTX_CACHE(cache));
119560484Sobrien
1196218822Sdim	if (isc_refcount_decrement(&cache->references) == 1) {
1197218822Sdim		tlsctx_cache_destroy(cache);
119889857Sobrien	}
119960484Sobrien}
1200130561Sobrien
120160484Sobrienisc_result_t
120260484Sobrienisc_tlsctx_cache_add(
120389857Sobrien	isc_tlsctx_cache_t *cache, const char *name,
120489857Sobrien	const isc_tlsctx_cache_transport_t transport, const uint16_t family,
120560484Sobrien	isc_tlsctx_t *ctx, isc_tls_cert_store_t *store,
1206130561Sobrien	isc_tlsctx_client_session_cache_t *client_sess_cache,
120760484Sobrien	isc_tlsctx_t **pfound, isc_tls_cert_store_t **pfound_store,
1208130561Sobrien	isc_tlsctx_client_session_cache_t **pfound_client_sess_cache) {
120960484Sobrien	isc_result_t result = ISC_R_FAILURE;
1210130561Sobrien	size_t name_len, tr_offset;
121160484Sobrien	isc_tlsctx_cache_entry_t *entry = NULL;
121260484Sobrien	bool ipv6;
121360484Sobrien
121460484Sobrien	REQUIRE(VALID_TLSCTX_CACHE(cache));
121589857Sobrien	REQUIRE(client_sess_cache == NULL ||
121689857Sobrien		VALID_TLSCTX_CLIENT_SESSION_CACHE(client_sess_cache));
121760484Sobrien	REQUIRE(name != NULL && *name != '\0');
1218130561Sobrien	REQUIRE(transport > isc_tlsctx_cache_none &&
121989857Sobrien		transport < isc_tlsctx_cache_count);
122060484Sobrien	REQUIRE(family == AF_INET || family == AF_INET6);
122160484Sobrien	REQUIRE(ctx != NULL);
122260484Sobrien
1223130561Sobrien	tr_offset = (transport - 1);
1224130561Sobrien	ipv6 = (family == AF_INET6);
1225130561Sobrien
122660484Sobrien	RWLOCK(&cache->rwlock, isc_rwlocktype_write);
122760484Sobrien
122860484Sobrien	name_len = strlen(name);
122977298Sobrien	result = isc_ht_find(cache->data, (const uint8_t *)name, name_len,
123060484Sobrien			     (void **)&entry);
123160484Sobrien	if (result == ISC_R_SUCCESS && entry->ctx[tr_offset][ipv6] != NULL) {
1232130561Sobrien		isc_tlsctx_client_session_cache_t *found_client_sess_cache;
123360484Sobrien		/* The entry exists. */
1234130561Sobrien		if (pfound != NULL) {
123577298Sobrien			INSIST(*pfound == NULL);
1236130561Sobrien			*pfound = entry->ctx[tr_offset][ipv6];
123789857Sobrien		}
123889857Sobrien
123977298Sobrien		if (pfound_store != NULL && entry->ca_store != NULL) {
1240130561Sobrien			INSIST(*pfound_store == NULL);
124160484Sobrien			*pfound_store = entry->ca_store;
124260484Sobrien		}
124360484Sobrien
124460484Sobrien		found_client_sess_cache =
124560484Sobrien			entry->client_sess_cache[tr_offset][ipv6];
1246130561Sobrien		if (pfound_client_sess_cache != NULL &&
124789857Sobrien		    found_client_sess_cache != NULL)
1248218822Sdim		{
124960484Sobrien			INSIST(*pfound_client_sess_cache == NULL);
125060484Sobrien			*pfound_client_sess_cache = found_client_sess_cache;
1251218822Sdim		}
1252218822Sdim		result = ISC_R_EXISTS;
125360484Sobrien	} else if (result == ISC_R_SUCCESS &&
1254130561Sobrien		   entry->ctx[tr_offset][ipv6] == NULL)
125560484Sobrien	{
125660484Sobrien		/*
1257130561Sobrien		 * The hash table entry exists, but is not filled for this
125860484Sobrien		 * particular transport/IP type combination.
125989857Sobrien		 */
126060484Sobrien		entry->ctx[tr_offset][ipv6] = ctx;
126160484Sobrien		entry->client_sess_cache[tr_offset][ipv6] = client_sess_cache;
126260484Sobrien		/*
126360484Sobrien		 * As the passed certificates store object is supposed
1264130561Sobrien		 * to be internally managed by the cache object anyway,
1265130561Sobrien		 * we might destroy the unneeded store object right now.
1266130561Sobrien		 */
1267130561Sobrien		if (store != NULL && store != entry->ca_store) {
126860484Sobrien			isc_tls_cert_store_free(&store);
126960484Sobrien		}
1270130561Sobrien		result = ISC_R_SUCCESS;
1271130561Sobrien	} else {
127260484Sobrien		/*
127360484Sobrien		 * The hash table entry does not exist, let's create one.
127460484Sobrien		 */
127560484Sobrien		INSIST(result != ISC_R_SUCCESS);
127677298Sobrien		entry = isc_mem_get(cache->mctx, sizeof(*entry));
127760484Sobrien		/* Oracle/Red Hat Linux, GCC bug #53119 */
127877298Sobrien		memset(entry, 0, sizeof(*entry));
127989857Sobrien		entry->ctx[tr_offset][ipv6] = ctx;
128060484Sobrien		entry->client_sess_cache[tr_offset][ipv6] = client_sess_cache;
128160484Sobrien		entry->ca_store = store;
128260484Sobrien		RUNTIME_CHECK(isc_ht_add(cache->data, (const uint8_t *)name,
128360484Sobrien					 name_len,
128460484Sobrien					 (void *)entry) == ISC_R_SUCCESS);
1285130561Sobrien		result = ISC_R_SUCCESS;
128677298Sobrien	}
128777298Sobrien
128860484Sobrien	RWUNLOCK(&cache->rwlock, isc_rwlocktype_write);
128960484Sobrien
1290130561Sobrien	return (result);
129160484Sobrien}
129260484Sobrien
1293130561Sobrienisc_result_t
129460484Sobrienisc_tlsctx_cache_find(
129560484Sobrien	isc_tlsctx_cache_t *cache, const char *name,
129660484Sobrien	const isc_tlsctx_cache_transport_t transport, const uint16_t family,
129760484Sobrien	isc_tlsctx_t **pctx, isc_tls_cert_store_t **pstore,
1298130561Sobrien	isc_tlsctx_client_session_cache_t **pfound_client_sess_cache) {
129960484Sobrien	isc_result_t result = ISC_R_FAILURE;
1300130561Sobrien	size_t tr_offset;
130160484Sobrien	isc_tlsctx_cache_entry_t *entry = NULL;
1302130561Sobrien	bool ipv6;
130360484Sobrien
1304218822Sdim	REQUIRE(VALID_TLSCTX_CACHE(cache));
1305218822Sdim	REQUIRE(name != NULL && *name != '\0');
1306218822Sdim	REQUIRE(transport > isc_tlsctx_cache_none &&
1307218822Sdim		transport < isc_tlsctx_cache_count);
1308218822Sdim	REQUIRE(family == AF_INET || family == AF_INET6);
1309218822Sdim	REQUIRE(pctx != NULL && *pctx == NULL);
131060484Sobrien
131160484Sobrien	tr_offset = (transport - 1);
131260484Sobrien	ipv6 = (family == AF_INET6);
131360484Sobrien
131460484Sobrien	RWLOCK(&cache->rwlock, isc_rwlocktype_read);
1315130561Sobrien
131660484Sobrien	result = isc_ht_find(cache->data, (const uint8_t *)name, strlen(name),
1317130561Sobrien			     (void **)&entry);
131877298Sobrien
131960484Sobrien	if (result == ISC_R_SUCCESS && pstore != NULL &&
132060484Sobrien	    entry->ca_store != NULL)
132160484Sobrien	{
132260484Sobrien		*pstore = entry->ca_store;
1323218822Sdim	}
132460484Sobrien
1325130561Sobrien	if (result == ISC_R_SUCCESS && entry->ctx[tr_offset][ipv6] != NULL) {
132660484Sobrien		isc_tlsctx_client_session_cache_t *found_client_sess_cache =
132760484Sobrien			entry->client_sess_cache[tr_offset][ipv6];
132860484Sobrien
132960484Sobrien		*pctx = entry->ctx[tr_offset][ipv6];
133060484Sobrien
133160484Sobrien		if (pfound_client_sess_cache != NULL &&
1332130561Sobrien		    found_client_sess_cache != NULL)
1333130561Sobrien		{
1334218822Sdim			INSIST(*pfound_client_sess_cache == NULL);
133560484Sobrien			*pfound_client_sess_cache = found_client_sess_cache;
1336130561Sobrien		}
133760484Sobrien	} else if (result == ISC_R_SUCCESS &&
133889857Sobrien		   entry->ctx[tr_offset][ipv6] == NULL)
1339130561Sobrien	{
134060484Sobrien		result = ISC_R_NOTFOUND;
134160484Sobrien	} else {
1342130561Sobrien		INSIST(result != ISC_R_SUCCESS);
134377298Sobrien	}
134477298Sobrien
134577298Sobrien	RWUNLOCK(&cache->rwlock, isc_rwlocktype_read);
134660484Sobrien
134777298Sobrien	return (result);
134860484Sobrien}
134960484Sobrien
135060484Sobrientypedef struct client_session_cache_entry client_session_cache_entry_t;
1351130561Sobrien
135277298Sobrientypedef struct client_session_cache_bucket {
135360484Sobrien	char *bucket_key;
135460484Sobrien	size_t bucket_key_len;
135560484Sobrien	/* Cache entries within the bucket (from the oldest to the newest). */
1356130561Sobrien	ISC_LIST(client_session_cache_entry_t) entries;
135760484Sobrien} client_session_cache_bucket_t;
135860484Sobrien
135960484Sobrienstruct client_session_cache_entry {
136060484Sobrien	SSL_SESSION *session;
1361130561Sobrien	client_session_cache_bucket_t *bucket; /* "Parent" bucket pointer. */
1362130561Sobrien	ISC_LINK(client_session_cache_entry_t) bucket_link;
136360484Sobrien	ISC_LINK(client_session_cache_entry_t) cache_link;
136460484Sobrien};
136560484Sobrien
136660484Sobrienstruct isc_tlsctx_client_session_cache {
136760484Sobrien	uint32_t magic;
136860484Sobrien	isc_refcount_t references;
136960484Sobrien	isc_mem_t *mctx;
137060484Sobrien
1371130561Sobrien	/*
137260484Sobrien	 * We need to keep a reference to the related TLS context in order
1373130561Sobrien	 * to ensure that it remains valid while the TLS client sessions
137460484Sobrien	 * cache object is valid, as every TLS session object
1375130561Sobrien	 * (SSL_SESSION) is "tied" to a particular context.
137660484Sobrien	 */
137760484Sobrien	isc_tlsctx_t *ctx;
137860484Sobrien
137960484Sobrien	/*
138089857Sobrien	 * The idea is to have one bucket per remote server. Each bucket,
138160484Sobrien	 * can maintain multiple TLS sessions to that server, as BIND
138260484Sobrien	 * might want to establish multiple TLS connections to the remote
138360484Sobrien	 * server at once.
1384130561Sobrien	 */
138577298Sobrien	isc_ht_t *buckets;
138660484Sobrien
138777298Sobrien	/*
138877298Sobrien	 * The list of all current entries within the cache maintained in
138960484Sobrien	 * LRU-manner, so that the oldest entry might be efficiently
139077298Sobrien	 * removed.
139177298Sobrien	 */
139260484Sobrien	ISC_LIST(client_session_cache_entry_t) lru_entries;
139360484Sobrien	/* Number of the entries within the cache. */
139460484Sobrien	size_t nentries;
139560484Sobrien	/* Maximum number of the entries within the cache. */
139660484Sobrien	size_t max_entries;
139760484Sobrien
1398130561Sobrien	isc_mutex_t lock;
139977298Sobrien};
140077298Sobrien
140177298Sobrienvoid
140260484Sobrienisc_tlsctx_client_session_cache_create(
140377298Sobrien	isc_mem_t *mctx, isc_tlsctx_t *ctx, const size_t max_entries,
140460484Sobrien	isc_tlsctx_client_session_cache_t **cachep) {
140560484Sobrien	isc_tlsctx_client_session_cache_t *nc;
140660484Sobrien
1407130561Sobrien	REQUIRE(ctx != NULL);
140877298Sobrien	REQUIRE(max_entries > 0);
140960484Sobrien	REQUIRE(cachep != NULL && *cachep == NULL);
141060484Sobrien
141160484Sobrien	nc = isc_mem_get(mctx, sizeof(*nc));
1412130561Sobrien
141360484Sobrien	*nc = (isc_tlsctx_client_session_cache_t){ .max_entries = max_entries };
141460484Sobrien	isc_refcount_init(&nc->references, 1);
141560484Sobrien	isc_mem_attach(mctx, &nc->mctx);
141660484Sobrien	isc_tlsctx_attach(ctx, &nc->ctx);
141760484Sobrien
141860484Sobrien	isc_ht_init(&nc->buckets, mctx, 5, ISC_HT_CASE_SENSITIVE);
1419130561Sobrien	ISC_LIST_INIT(nc->lru_entries);
1420130561Sobrien	isc_mutex_init(&nc->lock);
1421130561Sobrien
1422130561Sobrien	nc->magic = TLSCTX_CLIENT_SESSION_CACHE_MAGIC;
142360484Sobrien
142460484Sobrien	*cachep = nc;
142560484Sobrien}
142660484Sobrien
142760484Sobrienvoid
142860484Sobrienisc_tlsctx_client_session_cache_attach(
142960484Sobrien	isc_tlsctx_client_session_cache_t *source,
143060484Sobrien	isc_tlsctx_client_session_cache_t **targetp) {
143160484Sobrien	REQUIRE(VALID_TLSCTX_CLIENT_SESSION_CACHE(source));
143277298Sobrien	REQUIRE(targetp != NULL && *targetp == NULL);
143377298Sobrien
143460484Sobrien	isc_refcount_increment(&source->references);
143560484Sobrien
143660484Sobrien	*targetp = source;
143760484Sobrien}
143860484Sobrien
143960484Sobrienstatic void
144060484Sobrienclient_cache_entry_delete(isc_tlsctx_client_session_cache_t *restrict cache,
144177298Sobrien			  client_session_cache_entry_t *restrict entry) {
144277298Sobrien	client_session_cache_bucket_t *restrict bucket = entry->bucket;
144360484Sobrien
144460484Sobrien	/* Unlink and free the cache entry */
144560484Sobrien	ISC_LIST_UNLINK(bucket->entries, entry, bucket_link);
144660484Sobrien	ISC_LIST_UNLINK(cache->lru_entries, entry, cache_link);
1447130561Sobrien	cache->nentries--;
144860484Sobrien	(void)SSL_SESSION_free(entry->session);
144960484Sobrien	isc_mem_put(cache->mctx, entry, sizeof(*entry));
1450130561Sobrien
1451130561Sobrien	/* The bucket is empty - let's remove it */
145260484Sobrien	if (ISC_LIST_EMPTY(bucket->entries)) {
1453130561Sobrien		RUNTIME_CHECK(isc_ht_delete(cache->buckets,
145477298Sobrien					    (const uint8_t *)bucket->bucket_key,
1455130561Sobrien					    bucket->bucket_key_len) ==
145677298Sobrien			      ISC_R_SUCCESS);
145777298Sobrien
145877298Sobrien		isc_mem_free(cache->mctx, bucket->bucket_key);
145960484Sobrien		isc_mem_put(cache->mctx, bucket, sizeof(*bucket));
146060484Sobrien	}
1461130561Sobrien}
1462130561Sobrien
1463130561Sobrienvoid
146460484Sobrienisc_tlsctx_client_session_cache_detach(
146560484Sobrien	isc_tlsctx_client_session_cache_t **cachep) {
146660484Sobrien	isc_tlsctx_client_session_cache_t *cache = NULL;
146760484Sobrien	client_session_cache_entry_t *entry = NULL, *next = NULL;
146860484Sobrien
146960484Sobrien	REQUIRE(cachep != NULL);
147060484Sobrien
147160484Sobrien	cache = *cachep;
147260484Sobrien	*cachep = NULL;
147360484Sobrien
147460484Sobrien	REQUIRE(VALID_TLSCTX_CLIENT_SESSION_CACHE(cache));
147560484Sobrien
147660484Sobrien	if (isc_refcount_decrement(&cache->references) != 1) {
147760484Sobrien		return;
147860484Sobrien	}
147960484Sobrien
148060484Sobrien	cache->magic = 0;
148160484Sobrien
1482218822Sdim	isc_refcount_destroy(&cache->references);
148360484Sobrien
148460484Sobrien	entry = ISC_LIST_HEAD(cache->lru_entries);
148560484Sobrien	while (entry != NULL) {
148660484Sobrien		next = ISC_LIST_NEXT(entry, cache_link);
148760484Sobrien		client_cache_entry_delete(cache, entry);
148860484Sobrien		entry = next;
148960484Sobrien	}
149060484Sobrien
149160484Sobrien	RUNTIME_CHECK(isc_ht_count(cache->buckets) == 0);
149260484Sobrien	isc_ht_destroy(&cache->buckets);
149360484Sobrien
149460484Sobrien	isc_mutex_destroy(&cache->lock);
149560484Sobrien	isc_tlsctx_free(&cache->ctx);
1496218822Sdim	isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
149760484Sobrien}
149860484Sobrien
1499130561Sobrienstatic bool
150060484Sobrienssl_session_seems_resumable(const SSL_SESSION *sess) {
150160484Sobrien#ifdef HAVE_SSL_SESSION_IS_RESUMABLE
150277298Sobrien	/*
150360484Sobrien	 * If SSL_SESSION_is_resumable() is available, let's use that. It
1504130561Sobrien	 * is expected to be available on OpenSSL >= 1.1.1 and its modern
150560484Sobrien	 * siblings.
150660484Sobrien	 */
150760484Sobrien	return (SSL_SESSION_is_resumable(sess) != 0);
150860484Sobrien#elif (OPENSSL_VERSION_NUMBER >= 0x10100000L)
150960484Sobrien	/*
151060484Sobrien	 * Taking into consideration that OpenSSL 1.1.0 uses opaque
151160484Sobrien	 * pointers for SSL_SESSION, we cannot implement a replacement for
151260484Sobrien	 * SSL_SESSION_is_resumable() manually. Let's use a sensible
151360484Sobrien	 * approximation for that, then: if there is an associated session
151460484Sobrien	 * ticket or session ID, then, most likely, the session is
151560484Sobrien	 * resumable.
151660484Sobrien	 */
151760484Sobrien	unsigned int session_id_len = 0;
151860484Sobrien	(void)SSL_SESSION_get_id(sess, &session_id_len);
151960484Sobrien	return (SSL_SESSION_has_ticket(sess) || session_id_len > 0);
152060484Sobrien#else
152160484Sobrien	return (!sess->not_resumable &&
152260484Sobrien		(sess->session_id_length > 0 || sess->tlsext_ticklen > 0));
152360484Sobrien#endif
152460484Sobrien}
152560484Sobrien
152660484Sobrienvoid
152760484Sobrienisc_tlsctx_client_session_cache_keep(isc_tlsctx_client_session_cache_t *cache,
152860484Sobrien				     char *remote_peer_name, isc_tls_t *tls) {
152960484Sobrien	size_t name_len;
153060484Sobrien	isc_result_t result;
153160484Sobrien	SSL_SESSION *sess;
153260484Sobrien	client_session_cache_bucket_t *restrict bucket = NULL;
153360484Sobrien	client_session_cache_entry_t *restrict entry = NULL;
153460484Sobrien
153560484Sobrien	REQUIRE(VALID_TLSCTX_CLIENT_SESSION_CACHE(cache));
153660484Sobrien	REQUIRE(remote_peer_name != NULL && *remote_peer_name != '\0');
153760484Sobrien	REQUIRE(tls != NULL);
153877298Sobrien
153977298Sobrien	sess = SSL_get1_session(tls);
154077298Sobrien	if (sess == NULL) {
154160484Sobrien		ERR_clear_error();
154277298Sobrien		return;
154360484Sobrien	} else if (!ssl_session_seems_resumable(sess)) {
154460484Sobrien		SSL_SESSION_free(sess);
154560484Sobrien		return;
154660484Sobrien	}
154760484Sobrien
154860484Sobrien	isc_mutex_lock(&cache->lock);
154960484Sobrien
155089857Sobrien	name_len = strlen(remote_peer_name);
155160484Sobrien	result = isc_ht_find(cache->buckets, (const uint8_t *)remote_peer_name,
155260484Sobrien			     name_len, (void **)&bucket);
155360484Sobrien
155460484Sobrien	if (result != ISC_R_SUCCESS) {
155560484Sobrien		/* Let's create a new bucket */
155660484Sobrien		INSIST(bucket == NULL);
155760484Sobrien		bucket = isc_mem_get(cache->mctx, sizeof(*bucket));
155860484Sobrien		*bucket = (client_session_cache_bucket_t){
155977298Sobrien			.bucket_key = isc_mem_strdup(cache->mctx,
156060484Sobrien						     remote_peer_name),
156189857Sobrien			.bucket_key_len = name_len
156289857Sobrien		};
156360484Sobrien		ISC_LIST_INIT(bucket->entries);
156460484Sobrien		RUNTIME_CHECK(isc_ht_add(cache->buckets,
156560484Sobrien					 (const uint8_t *)remote_peer_name,
156660484Sobrien					 name_len,
156777298Sobrien					 (void *)bucket) == ISC_R_SUCCESS);
156860484Sobrien	}
156977298Sobrien
157060484Sobrien	/* Let's add a new cache entry to the new/found bucket */
157160484Sobrien	entry = isc_mem_get(cache->mctx, sizeof(*entry));
157260484Sobrien	*entry = (client_session_cache_entry_t){ .session = sess,
157360484Sobrien						 .bucket = bucket };
157460484Sobrien	ISC_LINK_INIT(entry, bucket_link);
157577298Sobrien	ISC_LINK_INIT(entry, cache_link);
157660484Sobrien
157760484Sobrien	ISC_LIST_APPEND(bucket->entries, entry, bucket_link);
1578130561Sobrien
157977298Sobrien	ISC_LIST_APPEND(cache->lru_entries, entry, cache_link);
158060484Sobrien	cache->nentries++;
158160484Sobrien
158260484Sobrien	if (cache->nentries > cache->max_entries) {
158360484Sobrien		/*
158489857Sobrien		 * Cache overrun. We need to remove the oldest entry from the
158560484Sobrien		 * cache
158660484Sobrien		 */
1587130561Sobrien		client_session_cache_entry_t *restrict oldest;
1588130561Sobrien		INSIST((cache->nentries - 1) == cache->max_entries);
158960484Sobrien
159060484Sobrien		oldest = ISC_LIST_HEAD(cache->lru_entries);
1591130561Sobrien		client_cache_entry_delete(cache, oldest);
1592130561Sobrien	}
159360484Sobrien
159460484Sobrien	isc_mutex_unlock(&cache->lock);
159577298Sobrien}
1596130561Sobrien
1597130561Sobrienvoid
159860484Sobrienisc_tlsctx_client_session_cache_reuse(isc_tlsctx_client_session_cache_t *cache,
159989857Sobrien				      char *remote_peer_name, isc_tls_t *tls) {
160089857Sobrien	client_session_cache_bucket_t *restrict bucket = NULL;
160189857Sobrien	client_session_cache_entry_t *restrict entry;
160260484Sobrien	size_t name_len;
1603218822Sdim	isc_result_t result;
160460484Sobrien
160560484Sobrien	REQUIRE(VALID_TLSCTX_CLIENT_SESSION_CACHE(cache));
1606130561Sobrien	REQUIRE(remote_peer_name != NULL && *remote_peer_name != '\0');
160760484Sobrien	REQUIRE(tls != NULL);
160860484Sobrien
1609130561Sobrien	isc_mutex_lock(&cache->lock);
161060484Sobrien
161160484Sobrien	/* Let's find the bucket */
161260484Sobrien	name_len = strlen(remote_peer_name);
161360484Sobrien	result = isc_ht_find(cache->buckets, (const uint8_t *)remote_peer_name,
161460484Sobrien			     name_len, (void **)&bucket);
161560484Sobrien
161660484Sobrien	if (result != ISC_R_SUCCESS) {
1617130561Sobrien		goto exit;
161877298Sobrien	}
1619130561Sobrien
162060484Sobrien	INSIST(bucket != NULL);
1621130561Sobrien
162260484Sobrien	/*
162360484Sobrien	 * If the bucket has been found, let's use the newest session from
162460484Sobrien	 * the bucket, as it has the highest chance to be successfully
1625130561Sobrien	 * resumed.
162660484Sobrien	 */
162760484Sobrien	INSIST(!ISC_LIST_EMPTY(bucket->entries));
1628130561Sobrien	entry = ISC_LIST_TAIL(bucket->entries);
1629130561Sobrien	RUNTIME_CHECK(SSL_set_session(tls, entry->session) == 1);
1630130561Sobrien	client_cache_entry_delete(cache, entry);
163160484Sobrien
1632130561Sobrienexit:
1633130561Sobrien	isc_mutex_unlock(&cache->lock);
1634130561Sobrien}
163577298Sobrien
1636130561Sobrienvoid
1637130561Sobrienisc_tlsctx_client_session_cache_keep_sockaddr(
163860484Sobrien	isc_tlsctx_client_session_cache_t *cache, isc_sockaddr_t *remote_peer,
1639130561Sobrien	isc_tls_t *tls) {
1640130561Sobrien	char peername[ISC_SOCKADDR_FORMATSIZE] = { 0 };
1641130561Sobrien
1642130561Sobrien	REQUIRE(remote_peer != NULL);
1643130561Sobrien
164460484Sobrien	isc_sockaddr_format(remote_peer, peername, sizeof(peername));
1645130561Sobrien
164660484Sobrien	isc_tlsctx_client_session_cache_keep(cache, peername, tls);
1647130561Sobrien}
1648130561Sobrien
1649130561Sobrienvoid
1650130561Sobrienisc_tlsctx_client_session_cache_reuse_sockaddr(
1651130561Sobrien	isc_tlsctx_client_session_cache_t *cache, isc_sockaddr_t *remote_peer,
1652130561Sobrien	isc_tls_t *tls) {
1653130561Sobrien	char peername[ISC_SOCKADDR_FORMATSIZE] = { 0 };
1654130561Sobrien
1655130561Sobrien	REQUIRE(remote_peer != NULL);
1656130561Sobrien
1657130561Sobrien	isc_sockaddr_format(remote_peer, peername, sizeof(peername));
1658130561Sobrien
1659130561Sobrien	isc_tlsctx_client_session_cache_reuse(cache, peername, tls);
1660130561Sobrien}
1661130561Sobrien
1662130561Sobrienconst isc_tlsctx_t *
1663130561Sobrienisc_tlsctx_client_session_cache_getctx(
1664130561Sobrien	isc_tlsctx_client_session_cache_t *cache) {
1665130561Sobrien	REQUIRE(VALID_TLSCTX_CLIENT_SESSION_CACHE(cache));
166660484Sobrien	return (cache->ctx);
1667130561Sobrien}
166860484Sobrien
166960484Sobrienvoid
167060484Sobrienisc_tlsctx_set_random_session_id_context(isc_tlsctx_t *ctx) {
167160484Sobrien	uint8_t session_id_ctx[SSL_MAX_SID_CTX_LENGTH] = { 0 };
167260484Sobrien	const size_t len = ISC_MIN(20, sizeof(session_id_ctx));
1673130561Sobrien
1674130561Sobrien	REQUIRE(ctx != NULL);
167560484Sobrien
167660484Sobrien	RUNTIME_CHECK(RAND_bytes(session_id_ctx, len) == 1);
167760484Sobrien
167860484Sobrien	RUNTIME_CHECK(
167960484Sobrien		SSL_CTX_set_session_id_context(ctx, session_id_ctx, len) == 1);
168060484Sobrien}
168160484Sobrien