From: Remi Gacogne Date: Fri, 4 Oct 2019 15:57:04 +0000 (+0200) Subject: dnsdist: Merge the setup of TLS contexts in Doh and DoT X-Git-Tag: dnsdist-1.4.0-rc4~21^2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b54e94dc8cfe11a36602fc0f4465c1bbb076f7f0;p=pdns dnsdist: Merge the setup of TLS contexts in Doh and DoT --- diff --git a/pdns/dnsdist-lua.cc b/pdns/dnsdist-lua.cc index ca1f6a31e..7f6ef05ec 100644 --- a/pdns/dnsdist-lua.cc +++ b/pdns/dnsdist-lua.cc @@ -146,6 +146,60 @@ static bool loadTLSCertificateAndKeys(const std::string& context, std::vector vars) +{ + if (vars->count("ciphers")) { + config.d_ciphers = boost::get((*vars)["ciphers"]); + } + + if (vars->count("ciphersTLS13")) { + config.d_ciphers13 = boost::get((*vars)["ciphersTLS13"]); + } + +#ifdef HAVE_LIBSSL + if (vars->count("minTLSVersion")) { + config.d_minTLSVersion = libssl_tls_version_from_string(boost::get((*vars)["minTLSVersion"])); + } +#endif /* HAVE_LIBSSL */ + + if (vars->count("ticketKeyFile")) { + config.d_ticketKeyFile = boost::get((*vars)["ticketKeyFile"]); + } + + if (vars->count("ticketsKeysRotationDelay")) { + config.d_ticketsKeyRotationDelay = boost::get((*vars)["ticketsKeysRotationDelay"]); + } + + if (vars->count("numberOfTicketsKeys")) { + config.d_numberOfTicketsKeys = boost::get((*vars)["numberOfTicketsKeys"]); + } + + if (vars->count("preferServerCiphers")) { + config.d_preferServerCiphers = boost::get((*vars)["preferServerCiphers"]); + } + + if (vars->count("sessionTickets")) { + config.d_enableTickets = boost::get((*vars)["sessionTickets"]); + } + + if (vars->count("numberOfStoredSessions")) { + auto value = boost::get((*vars)["numberOfStoredSessions"]); + if (value < 0) { + errlog("Invalid value '%d' for %s() parameter 'numberOfStoredSessions', should be >= 0, dismissing", value, context); + g_outputBuffer="Invalid value '" + std::to_string(value) + "' for " + context + "() parameter 'numberOfStoredSessions', should be >= 0, dimissing"; + } + config.d_maxStoredSessions = value; + } + + if (vars->count("ocspResponses")) { + auto files = boost::get>>((*vars)["ocspResponses"]); + for (const auto& file : files) { + config.d_ocspFiles.push_back(file.second); + } + } +} + #endif // defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS) void setupLuaConfig(bool client) @@ -1700,7 +1754,7 @@ void setupLuaConfig(bool client) auto frontend = std::make_shared(); if (certFiles && !certFiles->empty() && keyFiles && !keyFiles->empty()) { - if (!loadTLSCertificateAndKeys("addDOHLocal", frontend->d_certKeyPairs, *certFiles, *keyFiles)) { + if (!loadTLSCertificateAndKeys("addDOHLocal", frontend->d_tlsConfig.d_certKeyPairs, *certFiles, *keyFiles)) { return; } @@ -1737,18 +1791,11 @@ void setupLuaConfig(bool client) if (vars->count("idleTimeout")) { frontend->d_idleTimeout = boost::get((*vars)["idleTimeout"]); } - if (vars->count("ciphers")) { - frontend->d_ciphers = boost::get((*vars)["ciphers"]); - } - if (vars->count("ciphersTLS13")) { - frontend->d_ciphers13 = boost::get((*vars)["ciphersTLS13"]); - } - if (vars->count("minTLSVersion")) { - frontend->d_minTLSVersion = libssl_tls_version_from_string(boost::get((*vars)["minTLSVersion"])); - } + if (vars->count("serverTokens")) { frontend->d_serverTokens = boost::get((*vars)["serverTokens"]); } + if (vars->count("customResponseHeaders")) { for (auto const& headerMap : boost::get>((*vars)["customResponseHeaders"])) { std::pair headerResponse = std::make_pair(boost::to_lower_copy(headerMap.first), headerMap.second); @@ -1756,42 +1803,7 @@ void setupLuaConfig(bool client) } } - if (vars->count("ticketKeyFile")) { - frontend->d_ticketKeyFile = boost::get((*vars)["ticketKeyFile"]); - } - - if (vars->count("ticketsKeysRotationDelay")) { - frontend->d_ticketsKeyRotationDelay = boost::get((*vars)["ticketsKeysRotationDelay"]); - } - - if (vars->count("numberOfTicketsKeys")) { - frontend->d_numberOfTicketsKeys = boost::get((*vars)["numberOfTicketsKeys"]); - } - - if (vars->count("sessionTickets")) { - frontend->d_enableTickets = boost::get((*vars)["sessionTickets"]); - } - - if (vars->count("preferServerCiphers")) { - frontend->d_preferServerCiphers = boost::get((*vars)["preferServerCiphers"]); - } - - if (vars->count("numberOfStoredSessions")) { - auto value = boost::get((*vars)["numberOfStoredSessions"]); - if (value < 0) { - errlog("Invalid value '%d' for addDOHLocal() parameter 'numberOfStoredSessions', should be >= 0, dismissing", value); - g_outputBuffer="Invalid value '" + std::to_string(value) + "' for addDOHLocal() parameter 'numberOfStoredSessions', should be >= 0, dimissing"; - return; - } - frontend->d_maxStoredSessions = value; - } - - if (vars->count("ocspResponses")) { - auto files = boost::get>>((*vars)["ocspResponses"]); - for (const auto& file : files) { - frontend->d_ocspFiles.push_back(file.second); - } - } + parseTLSConfig(frontend->d_tlsConfig, "addDOHLocal", vars); } g_dohlocals.push_back(frontend); auto cs = std::unique_ptr(new ClientState(frontend->d_local, true, reusePort, tcpFastOpenQueueSize, interface, cpus)); @@ -1927,7 +1939,7 @@ void setupLuaConfig(bool client) } shared_ptr frontend = std::make_shared(); - if (!loadTLSCertificateAndKeys("addTLSLocal", frontend->d_certKeyPairs, certFiles, keyFiles)) { + if (!loadTLSCertificateAndKeys("addTLSLocal", frontend->d_tlsConfig.d_certKeyPairs, certFiles, keyFiles)) { return; } @@ -1943,56 +1955,7 @@ void setupLuaConfig(bool client) frontend->d_provider = boost::get((*vars)["provider"]); } - if (vars->count("ciphers")) { - frontend->d_ciphers = boost::get((*vars)["ciphers"]); - } - - if (vars->count("ciphersTLS13")) { - frontend->d_ciphers13 = boost::get((*vars)["ciphersTLS13"]); - } - -#ifdef HAVE_LIBSSL - if (vars->count("minTLSVersion")) { - frontend->d_minTLSVersion = libssl_tls_version_from_string(boost::get((*vars)["minTLSVersion"])); - } -#endif /* HAVE_LIBSSL */ - - if (vars->count("ticketKeyFile")) { - frontend->d_ticketKeyFile = boost::get((*vars)["ticketKeyFile"]); - } - - if (vars->count("ticketsKeysRotationDelay")) { - frontend->d_ticketsKeyRotationDelay = boost::get((*vars)["ticketsKeysRotationDelay"]); - } - - if (vars->count("numberOfTicketsKeys")) { - frontend->d_numberOfTicketsKeys = boost::get((*vars)["numberOfTicketsKeys"]); - } - - if (vars->count("sessionTickets")) { - frontend->d_enableTickets = boost::get((*vars)["sessionTickets"]); - } - - if (vars->count("preferServerCiphers")) { - frontend->d_preferServerCiphers = boost::get((*vars)["preferServerCiphers"]); - } - - if (vars->count("numberOfStoredSessions")) { - auto value = boost::get((*vars)["numberOfStoredSessions"]); - if (value < 0) { - errlog("Invalid value '%d' for addTLSLocal() parameter 'numberOfStoredSessions', should be >= 0, dismissing", value); - g_outputBuffer="Invalid value '" + std::to_string(value) + "' for addTLSLocal() parameter 'numberOfStoredSessions', should be >= 0, dimissing"; - return; - } - frontend->d_maxStoredSessions = value; - } - - if (vars->count("ocspResponses")) { - auto files = boost::get>>((*vars)["ocspResponses"]); - for (const auto& file : files) { - frontend->d_ocspFiles.push_back(file.second); - } - } + parseTLSConfig(frontend->d_tlsConfig, "addTLSLocal", vars); } try { @@ -2096,7 +2059,7 @@ void setupLuaConfig(bool client) g_lua.registerFunction::*)(boost::variant>> certFiles, boost::variant>> keyFiles)>("loadNewCertificatesAndKeys", [](std::shared_ptr& frontend, boost::variant>> certFiles, boost::variant>> keyFiles) { #ifdef HAVE_DNS_OVER_TLS - if (loadTLSCertificateAndKeys("loadNewCertificatesAndKeys", frontend->d_certKeyPairs, certFiles, keyFiles)) { + if (loadTLSCertificateAndKeys("loadNewCertificatesAndKeys", frontend->d_tlsConfig.d_certKeyPairs, certFiles, keyFiles)) { frontend->setupTLS(); } #endif diff --git a/pdns/dnsdistdist/doh.cc b/pdns/dnsdistdist/doh.cc index 8973bf4d1..aef94c4d5 100644 --- a/pdns/dnsdistdist/doh.cc +++ b/pdns/dnsdistdist/doh.cc @@ -929,114 +929,38 @@ static int ticket_key_callback(SSL *s, unsigned char keyName[TLS_TICKETS_KEY_NAM static std::unique_ptr getTLSContext(DOHFrontend& df, std::map& ocspResponses) { - auto ctx = std::unique_ptr(SSL_CTX_new(SSLv23_server_method()), SSL_CTX_free); - - int sslOptions = - SSL_OP_NO_SSLv2 | - SSL_OP_NO_SSLv3 | - SSL_OP_NO_COMPRESSION | - SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | - SSL_OP_SINGLE_DH_USE | - SSL_OP_SINGLE_ECDH_USE; - - if (!df.d_enableTickets || df.d_numberOfTicketsKeys == 0) { - /* for TLS 1.3 this means no stateless tickets, but stateful tickets might still be issued, - which is something we don't want. */ - sslOptions |= SSL_OP_NO_TICKET; - /* really disable all tickets */ -#ifdef HAVE_SSL_CTX_SET_NUM_TICKETS - SSL_CTX_set_num_tickets(ctx.get(), 0); -#endif /* HAVE_SSL_CTX_SET_NUM_TICKETS */ - } - else { - df.d_ticketKeys = std::unique_ptr(new OpenSSLTLSTicketKeysRing(df.d_numberOfTicketsKeys)); - SSL_CTX_set_tlsext_ticket_key_cb(ctx.get(), &ticket_key_callback); - libssl_set_ticket_key_callback_data(ctx.get(), &df); - } - - if (df.d_preferServerCiphers) { - sslOptions |= SSL_OP_CIPHER_SERVER_PREFERENCE; - } - - SSL_CTX_set_options(ctx.get(), sslOptions); - if (!libssl_set_min_tls_version(ctx, df.d_minTLSVersion)) { - throw std::runtime_error("Failed to set the minimum version to '" + libssl_tls_version_to_string(df.d_minTLSVersion) + "' for DoH listener"); - } - -#ifdef SSL_CTX_set_ecdh_auto - SSL_CTX_set_ecdh_auto(ctx.get(), 1); -#endif + try { + if (df.d_tlsConfig.d_ciphers.empty()) { + df.d_tlsConfig.d_ciphers = DOH_DEFAULT_CIPHERS; + } - if (df.d_maxStoredSessions == 0) { - /* disable stored sessions entirely */ - SSL_CTX_set_session_cache_mode(ctx.get(), SSL_SESS_CACHE_OFF); - } - else { - /* use the internal built-in cache to store sessions */ - SSL_CTX_set_session_cache_mode(ctx.get(), SSL_SESS_CACHE_SERVER); - SSL_CTX_sess_set_cache_size(ctx.get(), df.d_maxStoredSessions); - } + auto ctx = libssl_init_server_context(df.d_tlsConfig, ocspResponses); - std::vector keyTypes; - /* load certificate and private key */ - for (const auto& pair : df.d_certKeyPairs) { - if (SSL_CTX_use_certificate_chain_file(ctx.get(), pair.first.c_str()) != 1) { - ERR_print_errors_fp(stderr); - throw std::runtime_error("Failed to setup SSL/TLS for DoH listener, an error occurred while trying to load the DOH server certificate file: " + pair.first); - } - if (SSL_CTX_use_PrivateKey_file(ctx.get(), pair.second.c_str(), SSL_FILETYPE_PEM) != 1) { - ERR_print_errors_fp(stderr); - throw std::runtime_error("Failed to setup SSL/TLS for DoH listener, an error occurred while trying to load the DOH server private key file: " + pair.second); - } - if (SSL_CTX_check_private_key(ctx.get()) != 1) { - ERR_print_errors_fp(stderr); - throw std::runtime_error("Failed to setup SSL/TLS for DoH listener, the key from '" + pair.second + "' does not match the certificate from '" + pair.first + "'"); + if (df.d_tlsConfig.d_enableTickets && df.d_tlsConfig.d_numberOfTicketsKeys > 0) { + df.d_ticketKeys = std::unique_ptr(new OpenSSLTLSTicketKeysRing(df.d_tlsConfig.d_numberOfTicketsKeys)); + SSL_CTX_set_tlsext_ticket_key_cb(ctx.get(), &ticket_key_callback); + libssl_set_ticket_key_callback_data(ctx.get(), &df); } - /* store the type of the new key, we might need it later to select the right OCSP stapling response */ - auto keyType = libssl_get_last_key_type(ctx); - if (keyType < 0) { - throw std::runtime_error("Failed to setup SSL/TLS for DoH listener, the key from '" + pair.second + "' has an unknown type"); - } - keyTypes.push_back(keyType); - } - - if (!df.d_ocspFiles.empty()) { - try { - ocspResponses = libssl_load_ocsp_responses(df.d_ocspFiles, keyTypes); + if (!ocspResponses.empty()) { SSL_CTX_set_tlsext_status_cb(ctx.get(), &ocsp_stapling_callback); SSL_CTX_set_tlsext_status_arg(ctx.get(), &ocspResponses); } - catch(const std::exception& e) { - throw std::runtime_error("Unable to load OCSP responses for the SSL/TLS DoH listener: " + std::string(e.what())); - } - } - if (SSL_CTX_set_cipher_list(ctx.get(), df.d_ciphers.empty() == false ? df.d_ciphers.c_str() : DOH_DEFAULT_CIPHERS) != 1) { - throw std::runtime_error("Failed to setup SSL/TLS for DoH listener, DOH ciphers could not be set: " + df.d_ciphers); - } - -#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES - if (!df.d_ciphers13.empty() && SSL_CTX_set_ciphersuites(ctx.get(), df.d_ciphers13.c_str()) != 1) { - throw std::runtime_error("Failed to setup SSL/TLS for DoH listener, DOH TLS 1.3 ciphers could not be set: " + df.d_ciphers13); - } -#endif /* HAVE_SSL_CTX_SET_CIPHERSUITES */ - - h2o_ssl_register_alpn_protocols(ctx.get(), h2o_http2_alpn_protocols); + h2o_ssl_register_alpn_protocols(ctx.get(), h2o_http2_alpn_protocols); - try { - if (df.d_ticketKeyFile.empty()) { + if (df.d_tlsConfig.d_ticketKeyFile.empty()) { df.handleTicketsKeyRotation(); } else { - df.loadTicketsKeys(df.d_ticketKeyFile); + df.loadTicketsKeys(df.d_tlsConfig.d_ticketKeyFile); } + + return ctx; } - catch (const std::exception& e) { - throw; + catch (const std::runtime_error& e) { + throw std::runtime_error("Error setting up TLS context for DoH listener on '" + df.d_local.toStringWithPort() + "': " + e.what()); } - - return ctx; } static void setupAcceptContext(DOHAcceptContext& ctx, DOHServerConfig& dsc, bool setupTLS) @@ -1044,7 +968,7 @@ static void setupAcceptContext(DOHAcceptContext& ctx, DOHServerConfig& dsc, bool auto nativeCtx = ctx.get(); nativeCtx->ctx = &dsc.h2o_ctx; nativeCtx->hosts = dsc.h2o_config.hosts; - if (setupTLS && !dsc.df->d_certKeyPairs.empty()) { + if (setupTLS && !dsc.df->d_tlsConfig.d_certKeyPairs.empty()) { auto tlsCtx = getTLSContext(*dsc.df, ctx.d_ocspResponses); @@ -1062,8 +986,8 @@ void DOHFrontend::rotateTicketsKey(time_t now) d_ticketKeys->rotateTicketsKey(now); - if (d_ticketsKeyRotationDelay > 0) { - d_ticketsKeyNextRotation = now + d_ticketsKeyRotationDelay; + if (d_tlsConfig.d_ticketsKeyRotationDelay > 0) { + d_ticketsKeyNextRotation = now + d_tlsConfig.d_ticketsKeyRotationDelay; } } @@ -1074,14 +998,14 @@ void DOHFrontend::loadTicketsKeys(const std::string& keyFile) } d_ticketKeys->loadTicketsKeys(keyFile); - if (d_ticketsKeyRotationDelay > 0) { - d_ticketsKeyNextRotation = time(nullptr) + d_ticketsKeyRotationDelay; + if (d_tlsConfig.d_ticketsKeyRotationDelay > 0) { + d_ticketsKeyNextRotation = time(nullptr) + d_tlsConfig.d_ticketsKeyRotationDelay; } } void DOHFrontend::handleTicketsKeyRotation() { - if (d_ticketsKeyRotationDelay == 0) { + if (d_tlsConfig.d_ticketsKeyRotationDelay == 0) { return; } @@ -1122,7 +1046,7 @@ void DOHFrontend::setup() d_dsc = std::make_shared(d_idleTimeout); - if (!d_certKeyPairs.empty()) { + if (!d_tlsConfig.d_certKeyPairs.empty()) { auto tlsCtx = getTLSContext(*this, d_dsc->accept_ctx->d_ocspResponses); diff --git a/pdns/dnsdistdist/libssl.cc b/pdns/dnsdistdist/libssl.cc index f900365ba..1c78e48c3 100644 --- a/pdns/dnsdistdist/libssl.cc +++ b/pdns/dnsdistdist/libssl.cc @@ -563,4 +563,95 @@ bool OpenSSLTLSTicketKey::decrypt(const unsigned char* iv, EVP_CIPHER_CTX *ectx, return true; } +std::unique_ptr libssl_init_server_context(const TLSConfig& config, + std::map& ocspResponses) +{ + auto ctx = std::unique_ptr(SSL_CTX_new(SSLv23_server_method()), SSL_CTX_free); + + int sslOptions = + SSL_OP_NO_SSLv2 | + SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | + SSL_OP_SINGLE_DH_USE | + SSL_OP_SINGLE_ECDH_USE; + + if (!config.d_enableTickets || config.d_numberOfTicketsKeys == 0) { + /* for TLS 1.3 this means no stateless tickets, but stateful tickets might still be issued, + which is something we don't want. */ + sslOptions |= SSL_OP_NO_TICKET; + /* really disable all tickets */ +#ifdef HAVE_SSL_CTX_SET_NUM_TICKETS + SSL_CTX_set_num_tickets(ctx.get(), 0); +#endif /* HAVE_SSL_CTX_SET_NUM_TICKETS */ + } + + if (config.d_preferServerCiphers) { + sslOptions |= SSL_OP_CIPHER_SERVER_PREFERENCE; + } + + SSL_CTX_set_options(ctx.get(), sslOptions); + if (!libssl_set_min_tls_version(ctx, config.d_minTLSVersion)) { + throw std::runtime_error("Failed to set the minimum version to '" + libssl_tls_version_to_string(config.d_minTLSVersion)); + } + +#ifdef SSL_CTX_set_ecdh_auto + SSL_CTX_set_ecdh_auto(ctx.get(), 1); +#endif + + if (config.d_maxStoredSessions == 0) { + /* disable stored sessions entirely */ + SSL_CTX_set_session_cache_mode(ctx.get(), SSL_SESS_CACHE_OFF); + } + else { + /* use the internal built-in cache to store sessions */ + SSL_CTX_set_session_cache_mode(ctx.get(), SSL_SESS_CACHE_SERVER); + SSL_CTX_sess_set_cache_size(ctx.get(), config.d_maxStoredSessions); + } + + std::vector keyTypes; + /* load certificate and private key */ + for (const auto& pair : config.d_certKeyPairs) { + if (SSL_CTX_use_certificate_chain_file(ctx.get(), pair.first.c_str()) != 1) { + ERR_print_errors_fp(stderr); + throw std::runtime_error("An error occurred while trying to load the TLS server certificate file: " + pair.first); + } + if (SSL_CTX_use_PrivateKey_file(ctx.get(), pair.second.c_str(), SSL_FILETYPE_PEM) != 1) { + ERR_print_errors_fp(stderr); + throw std::runtime_error("An error occurred while trying to load the TLS server private key file: " + pair.second); + } + if (SSL_CTX_check_private_key(ctx.get()) != 1) { + ERR_print_errors_fp(stderr); + throw std::runtime_error("The key from '" + pair.second + "' does not match the certificate from '" + pair.first + "'"); + } + /* store the type of the new key, we might need it later to select the right OCSP stapling response */ + auto keyType = libssl_get_last_key_type(ctx); + if (keyType < 0) { + throw std::runtime_error("The key from '" + pair.second + "' has an unknown type"); + } + keyTypes.push_back(keyType); + } + + if (!config.d_ocspFiles.empty()) { + try { + ocspResponses = libssl_load_ocsp_responses(config.d_ocspFiles, keyTypes); + } + catch(const std::exception& e) { + throw std::runtime_error("Unable to load OCSP responses: " + std::string(e.what())); + } + } + + if (!config.d_ciphers.empty() && SSL_CTX_set_cipher_list(ctx.get(), config.d_ciphers.c_str()) != 1) { + throw std::runtime_error("The TLS ciphers could not be set: " + config.d_ciphers); + } + +#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES + if (!config.d_ciphers13.empty() && SSL_CTX_set_ciphersuites(ctx.get(), config.d_ciphers13.c_str()) != 1) { + throw std::runtime_error("The TLS 1.3 ciphers could not be set: " + config.d_ciphers13); + } +#endif /* HAVE_SSL_CTX_SET_CIPHERSUITES */ + + return ctx; +} + #endif /* HAVE_LIBSSL */ diff --git a/pdns/dnsdistdist/tcpiohandler.cc b/pdns/dnsdistdist/tcpiohandler.cc index dd98fc217..801f1d494 100644 --- a/pdns/dnsdistdist/tcpiohandler.cc +++ b/pdns/dnsdistdist/tcpiohandler.cc @@ -241,120 +241,34 @@ private: class OpenSSLTLSIOCtx: public TLSCtx { public: - OpenSSLTLSIOCtx(const TLSFrontend& fe): d_ticketKeys(fe.d_numberOfTicketsKeys), d_tlsCtx(std::unique_ptr(nullptr, SSL_CTX_free)) + OpenSSLTLSIOCtx(const TLSFrontend& fe): d_ticketKeys(fe.d_tlsConfig.d_numberOfTicketsKeys) { - d_ticketsKeyRotationDelay = fe.d_ticketsKeyRotationDelay; - - int sslOptions = - SSL_OP_NO_SSLv2 | - SSL_OP_NO_SSLv3 | - SSL_OP_NO_COMPRESSION | - SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | - SSL_OP_SINGLE_DH_USE | - SSL_OP_SINGLE_ECDH_USE; - registerOpenSSLUser(); + d_ticketsKeyRotationDelay = fe.d_tlsConfig.d_ticketsKeyRotationDelay; - d_tlsCtx = std::unique_ptr(SSL_CTX_new(SSLv23_server_method()), SSL_CTX_free); + d_tlsCtx = libssl_init_server_context(fe.d_tlsConfig, d_ocspResponses); if (!d_tlsCtx) { ERR_print_errors_fp(stderr); throw std::runtime_error("Error creating TLS context on " + fe.d_addr.toStringWithPort()); } - if (!fe.d_enableTickets || fe.d_numberOfTicketsKeys == 0) { - /* for TLS 1.3 this means no stateless tickets, but stateful tickets might still be issued, - which is something we don't want. */ - sslOptions |= SSL_OP_NO_TICKET; - /* really disable all tickets */ -#ifdef HAVE_SSL_CTX_SET_NUM_TICKETS - SSL_CTX_set_num_tickets(d_tlsCtx.get(), 0); -#endif /* HAVE_SSL_CTX_SET_NUM_TICKETS */ - } - else { + if (fe.d_tlsConfig.d_enableTickets && fe.d_tlsConfig.d_numberOfTicketsKeys > 0) { /* use our own ticket keys handler so we can rotate them */ SSL_CTX_set_tlsext_ticket_key_cb(d_tlsCtx.get(), &OpenSSLTLSIOCtx::ticketKeyCb); libssl_set_ticket_key_callback_data(d_tlsCtx.get(), this); } - if (fe.d_preferServerCiphers) { - sslOptions |= SSL_OP_CIPHER_SERVER_PREFERENCE; - } - - SSL_CTX_set_options(d_tlsCtx.get(), sslOptions); - if (!libssl_set_min_tls_version(d_tlsCtx, fe.d_minTLSVersion)) { - throw std::runtime_error("Failed to set the minimum version to '" + libssl_tls_version_to_string(fe.d_minTLSVersion) + "' for ths TLS context on " + fe.d_addr.toStringWithPort()); - } - -#if defined(SSL_CTX_set_ecdh_auto) - SSL_CTX_set_ecdh_auto(d_tlsCtx.get(), 1); -#endif - if (fe.d_maxStoredSessions == 0) { - /* disable stored sessions entirely */ - SSL_CTX_set_session_cache_mode(d_tlsCtx.get(), SSL_SESS_CACHE_OFF); - } - else { - /* use the internal built-in cache to store sessions */ - SSL_CTX_set_session_cache_mode(d_tlsCtx.get(), SSL_SESS_CACHE_SERVER); - SSL_CTX_sess_set_cache_size(d_tlsCtx.get(), fe.d_maxStoredSessions); - } - - std::vector keyTypes; - for (const auto& pair : fe.d_certKeyPairs) { - if (SSL_CTX_use_certificate_chain_file(d_tlsCtx.get(), pair.first.c_str()) != 1) { - ERR_print_errors_fp(stderr); - throw std::runtime_error("Error loading certificate from " + pair.first + " for the TLS context on " + fe.d_addr.toStringWithPort()); - } - if (SSL_CTX_use_PrivateKey_file(d_tlsCtx.get(), pair.second.c_str(), SSL_FILETYPE_PEM) != 1) { - ERR_print_errors_fp(stderr); - throw std::runtime_error("Error loading key from " + pair.second + " for the TLS context on " + fe.d_addr.toStringWithPort()); - } - if (SSL_CTX_check_private_key(d_tlsCtx.get()) != 1) { - ERR_print_errors_fp(stderr); - throw std::runtime_error("Key from '" + pair.second + "' does not match the certificate from '" + pair.first + "' for the TLS context on " + fe.d_addr.toStringWithPort()); - } - - /* store the type of the new key, we might need it later to select the right OCSP stapling response */ - auto keyType = libssl_get_last_key_type(d_tlsCtx); - if (keyType < 0) { - throw std::runtime_error("Key from '" + pair.second + "' has an unknown type"); - } - keyTypes.push_back(keyType); - } - - if (!fe.d_ocspFiles.empty()) { - try { - d_ocspResponses = libssl_load_ocsp_responses(fe.d_ocspFiles, keyTypes); - } - catch(const std::exception& e) { - throw std::runtime_error("Error loading responses for the TLS context on " + fe.d_addr.toStringWithPort() + ": " + e.what()); - } - + if (!d_ocspResponses.empty()) { SSL_CTX_set_tlsext_status_cb(d_tlsCtx.get(), &OpenSSLTLSIOCtx::ocspStaplingCb); SSL_CTX_set_tlsext_status_arg(d_tlsCtx.get(), &d_ocspResponses); } - if (!fe.d_ciphers.empty()) { - if (SSL_CTX_set_cipher_list(d_tlsCtx.get(), fe.d_ciphers.c_str()) != 1) { - ERR_print_errors_fp(stderr); - throw std::runtime_error("Error setting the cipher list to '" + fe.d_ciphers + "' for the TLS context on " + fe.d_addr.toStringWithPort()); - } - } - -#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES - if (!fe.d_ciphers13.empty()) { - if (SSL_CTX_set_ciphersuites(d_tlsCtx.get(), fe.d_ciphers13.c_str()) != 1) { - ERR_print_errors_fp(stderr); - throw std::runtime_error("Error setting the TLS 1.3 cipher list to '" + fe.d_ciphers13 + "' for the TLS context on " + fe.d_addr.toStringWithPort()); - } - } -#endif /* HAVE_SSL_CTX_SET_CIPHERSUITES */ - try { - if (fe.d_ticketKeyFile.empty()) { + if (fe.d_tlsConfig.d_ticketKeyFile.empty()) { handleTicketsKeyRotation(time(nullptr)); } else { - loadTicketsKeys(fe.d_ticketKeyFile); + loadTicketsKeys(fe.d_tlsConfig.d_ticketKeyFile); } } catch (const std::exception& e) { @@ -421,7 +335,7 @@ public: private: OpenSSLTLSTicketKeysRing d_ticketKeys; std::map d_ocspResponses; - std::unique_ptr d_tlsCtx; + std::unique_ptr d_tlsCtx{nullptr, SSL_CTX_free}; }; #endif /* HAVE_LIBSSL */ @@ -780,10 +694,10 @@ private: class GnuTLSIOCtx: public TLSCtx { public: - GnuTLSIOCtx(const TLSFrontend& fe): d_creds(std::unique_ptr(nullptr, gnutls_certificate_free_credentials)), d_enableTickets(fe.d_enableTickets) + GnuTLSIOCtx(const TLSFrontend& fe): d_creds(std::unique_ptr(nullptr, gnutls_certificate_free_credentials)), d_enableTickets(fe.d_tlsConfig.d_enableTickets) { int rc = 0; - d_ticketsKeyRotationDelay = fe.d_ticketsKeyRotationDelay; + d_ticketsKeyRotationDelay = fe.d_tlsConfig.d_ticketsKeyRotationDelay; gnutls_certificate_credentials_t creds; rc = gnutls_certificate_allocate_credentials(&creds); @@ -794,7 +708,7 @@ public: d_creds = std::unique_ptr(creds, gnutls_certificate_free_credentials); creds = nullptr; - for (const auto& pair : fe.d_certKeyPairs) { + for (const auto& pair : fe.d_tlsConfig.d_certKeyPairs) { rc = gnutls_certificate_set_x509_key_file(d_creds.get(), pair.first.c_str(), pair.second.c_str(), GNUTLS_X509_FMT_PEM); if (rc != GNUTLS_E_SUCCESS) { throw std::runtime_error("Error loading certificate ('" + pair.first + "') and key ('" + pair.second + "') for TLS context on " + fe.d_addr.toStringWithPort() + ": " + gnutls_strerror(rc)); @@ -802,10 +716,10 @@ public: } size_t count = 0; - for (const auto& file : fe.d_ocspFiles) { + for (const auto& file : fe.d_tlsConfig.d_ocspFiles) { rc = gnutls_certificate_set_ocsp_status_request_file(d_creds.get(), file.c_str(), count); if (rc != GNUTLS_E_SUCCESS) { - throw std::runtime_error("Error loading OCSP response from file '" + file + "' for certificate ('" + fe.d_certKeyPairs.at(count).first + "') and key ('" + fe.d_certKeyPairs.at(count).second + "') for TLS context on " + fe.d_addr.toStringWithPort() + ": " + gnutls_strerror(rc)); + throw std::runtime_error("Error loading OCSP response from file '" + file + "' for certificate ('" + fe.d_tlsConfig.d_certKeyPairs.at(count).first + "') and key ('" + fe.d_tlsConfig.d_certKeyPairs.at(count).second + "') for TLS context on " + fe.d_addr.toStringWithPort() + ": " + gnutls_strerror(rc)); } ++count; } @@ -817,19 +731,19 @@ public: } #endif - rc = gnutls_priority_init(&d_priorityCache, fe.d_ciphers.empty() ? "NORMAL" : fe.d_ciphers.c_str(), nullptr); + rc = gnutls_priority_init(&d_priorityCache, fe.d_tlsConfig.d_ciphers.empty() ? "NORMAL" : fe.d_tlsConfig.d_ciphers.c_str(), nullptr); if (rc != GNUTLS_E_SUCCESS) { - throw std::runtime_error("Error setting up TLS cipher preferences to '" + fe.d_ciphers + "' (" + gnutls_strerror(rc) + ") on " + fe.d_addr.toStringWithPort()); + throw std::runtime_error("Error setting up TLS cipher preferences to '" + fe.d_tlsConfig.d_ciphers + "' (" + gnutls_strerror(rc) + ") on " + fe.d_addr.toStringWithPort()); } pthread_rwlock_init(&d_lock, nullptr); try { - if (fe.d_ticketKeyFile.empty()) { + if (fe.d_tlsConfig.d_ticketKeyFile.empty()) { handleTicketsKeyRotation(time(nullptr)); } else { - loadTicketsKeys(fe.d_ticketKeyFile); + loadTicketsKeys(fe.d_tlsConfig.d_ticketKeyFile); } } catch(const std::runtime_error& e) { diff --git a/pdns/doh.hh b/pdns/doh.hh index 289b88361..fe432d585 100644 --- a/pdns/doh.hh +++ b/pdns/doh.hh @@ -46,13 +46,9 @@ struct DOHFrontend } std::shared_ptr d_dsc{nullptr}; - std::vector> d_certKeyPairs; - std::vector d_ocspFiles; std::vector> d_responsesMap; - std::string d_ciphers; - std::string d_ciphers13; + TLSConfig d_tlsConfig; std::string d_serverTokens{"h2o/dnsdist"}; - LibsslTLSVersion d_minTLSVersion{LibsslTLSVersion::TLS10}; #ifdef HAVE_DNS_OVER_HTTPS std::unique_ptr d_ticketKeys{nullptr}; #endif @@ -61,13 +57,6 @@ struct DOHFrontend uint32_t d_idleTimeout{30}; // HTTP idle timeout in seconds std::vector d_urls; - std::string d_ticketKeyFile; - - time_t d_ticketsKeyRotationDelay{43200}; - size_t d_maxStoredSessions{20480}; - uint8_t d_numberOfTicketsKeys{5}; - bool d_enableTickets{true}; - bool d_preferServerCiphers{false}; std::atomic d_httpconnects{0}; // number of TCP/IP connections established std::atomic d_getqueries{0}; // valid DNS queries received via GET diff --git a/pdns/libssl.hh b/pdns/libssl.hh index 7fd52bcfb..9276b74e6 100644 --- a/pdns/libssl.hh +++ b/pdns/libssl.hh @@ -12,6 +12,25 @@ enum class LibsslTLSVersion { Unknown, TLS10, TLS11, TLS12, TLS13 }; +class TLSConfig +{ +public: + std::vector> d_certKeyPairs; + std::vector d_ocspFiles; + + std::string d_ciphers; + std::string d_ciphers13; + std::string d_ticketKeyFile; + + size_t d_maxStoredSessions{20480}; + time_t d_ticketsKeyRotationDelay{43200}; + uint8_t d_numberOfTicketsKeys{5}; + LibsslTLSVersion d_minTLSVersion{LibsslTLSVersion::TLS10}; + + bool d_preferServerCiphers{false}; + bool d_enableTickets{true}; +}; + #ifdef HAVE_LIBSSL #include @@ -80,4 +99,7 @@ LibsslTLSVersion libssl_tls_version_from_string(const std::string& str); const std::string& libssl_tls_version_to_string(LibsslTLSVersion version); bool libssl_set_min_tls_version(std::unique_ptr& ctx, LibsslTLSVersion version); +std::unique_ptr libssl_init_server_context(const TLSConfig& config, + std::map& ocspResponses); + #endif /* HAVE_LIBSSL */ diff --git a/pdns/tcpiohandler.hh b/pdns/tcpiohandler.hh index c5735729a..3bf3f5d2b 100644 --- a/pdns/tcpiohandler.hh +++ b/pdns/tcpiohandler.hh @@ -127,7 +127,7 @@ public: time_t getTicketsKeyRotationDelay() const { - return d_ticketsKeyRotationDelay; + return d_tlsConfig.d_ticketsKeyRotationDelay; } std::string getNextTicketsKeyRotation() const @@ -141,21 +141,9 @@ public: return res; } - std::vector> d_certKeyPairs; - std::vector d_ocspFiles; + TLSConfig d_tlsConfig; ComboAddress d_addr; - std::string d_ciphers; - std::string d_ciphers13; std::string d_provider; - std::string d_ticketKeyFile; - - size_t d_maxStoredSessions{20480}; - time_t d_ticketsKeyRotationDelay{43200}; - uint8_t d_numberOfTicketsKeys{5}; - LibsslTLSVersion d_minTLSVersion{LibsslTLSVersion::TLS10}; - - bool d_enableTickets{true}; - bool d_preferServerCiphers{false}; private: std::shared_ptr d_ctx{nullptr};