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, ¶ms_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