From: Fred Morcos Date: Tue, 7 Feb 2023 18:07:08 +0000 (+0100) Subject: OpenSSL 3.0: Add loadTLSProvider to replace loadTLSEngine X-Git-Tag: dnsdist-1.8.0-rc1~40^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=refs%2Fpull%2F12423%2Fhead;p=thirdparty%2Fpdns.git OpenSSL 3.0: Add loadTLSProvider to replace loadTLSEngine --- diff --git a/pdns/dnsdist-console.cc b/pdns/dnsdist-console.cc index 02316b0564..6b31e9f138 100644 --- a/pdns/dnsdist-console.cc +++ b/pdns/dnsdist-console.cc @@ -562,7 +562,12 @@ const std::vector g_consoleKeywords{ { "KeyValueStoreLookupRule", true, "kvs, lookupKey", "matches queries if the key is found in the specified Key Value store" }, { "KeyValueStoreRangeLookupRule", true, "kvs, lookupKey", "matches queries if the key is found in the specified Key Value store" }, { "leastOutstanding", false, "", "Send traffic to downstream server with least outstanding queries, with the lowest 'order', and within that the lowest recent latency"}, +#if defined(HAVE_LIBSSL) && !defined(HAVE_TLS_PROVIDERS) { "loadTLSEngine", true, "engineName [, defaultString]", "Load the OpenSSL engine named 'engineName', setting the engine default string to 'defaultString' if supplied"}, +#endif +#if defined(HAVE_LIBSSL) && OPENSSL_VERSION_MAJOR >= 3 && defined(HAVE_TLS_PROVIDERS) + { "loadTLSProvider", true, "providerName", "Load the OpenSSL provider named 'providerName'"}, +#endif { "LogAction", true, "[filename], [binary], [append], [buffered]", "Log a line for each query, to the specified file if any, to the console (require verbose) otherwise. When logging to a file, the `binary` optional parameter specifies whether we log in binary form (default) or in textual form, the `append` optional parameter specifies whether we open the file for appending or truncate each time (default), and the `buffered` optional parameter specifies whether writes to the file are buffered (default) or not." }, { "LogResponseAction", true, "[filename], [append], [buffered]", "Log a line for each response, to the specified file if any, to the console (require verbose) otherwise. The `append` optional parameter specifies whether we open the file for appending or truncate each time (default), and the `buffered` optional parameter specifies whether writes to the file are buffered (default) or not." }, { "LuaAction", true, "function", "Invoke a Lua function that accepts a DNSQuestion" }, @@ -777,7 +782,7 @@ const std::vector g_consoleKeywords{ { "TCPRule", true, "[tcp]", "Matches question received over TCP if tcp is true, over UDP otherwise" }, { "TeeAction", true, "remote [, addECS [, local]]", "send copy of query to remote, optionally adding ECS info, optionally set local address" }, { "testCrypto", true, "", "test of the crypto all works" }, - { "TimedIPSetRule", true, "", "Create a rule which matches a set of IP addresses which expire"}, + { "TimedIPSetRule", true, "", "Create a rule which matches a set of IP addresses which expire"}, { "topBandwidth", true, "top", "show top-`top` clients that consume the most bandwidth over length of ringbuffer" }, { "topCacheHitResponseRules", true, "[top][, vars]", "show `top` cache-hit response rules" }, { "topCacheInsertedResponseRules", true, "[top][, vars]", "show `top` cache-inserted response rules" }, diff --git a/pdns/dnsdist-lua.cc b/pdns/dnsdist-lua.cc index e5b31692f9..a0c1561838 100644 --- a/pdns/dnsdist-lua.cc +++ b/pdns/dnsdist-lua.cc @@ -3051,7 +3051,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) DownstreamState::s_randomizeIDs = randomized; }); -#if defined(HAVE_LIBSSL) +#if defined(HAVE_LIBSSL) && !defined(HAVE_TLS_PROVIDERS) luaCtx.writeFunction("loadTLSEngine", [client](const std::string& engineName, boost::optional defaultString) { if (client) { return; @@ -3063,7 +3063,21 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) errlog("Error while trying to load TLS engine '%s': %s", engineName, error); } }); -#endif /* HAVE_LIBSSL */ +#endif /* HAVE_LIBSSL && !HAVE_TLS_PROVIDERS */ + +#if defined(HAVE_LIBSSL) && OPENSSL_VERSION_MAJOR >= 3 && defined(HAVE_TLS_PROVIDERS) + luaCtx.writeFunction("loadTLSProvider", [client](const std::string& providerName) { + if (client) { + return; + } + + auto [success, error] = libssl_load_provider(providerName); + if (!success) { + g_outputBuffer = "Error while trying to load TLS provider '" + providerName + "': " + error + "\n"; + errlog("Error while trying to load TLS provider '%s': %s", providerName, error); + } + }); +#endif /* HAVE_LIBSSL && OPENSSL_VERSION_MAJOR >= 3 && HAVE_TLS_PROVIDERS */ luaCtx.writeFunction("newThread", [client, configCheck](const std::string& code) { if (client || configCheck) { diff --git a/pdns/dnsdistdist/configure.ac b/pdns/dnsdistdist/configure.ac index 18c3e157df..83b88ef0a9 100644 --- a/pdns/dnsdistdist/configure.ac +++ b/pdns/dnsdistdist/configure.ac @@ -69,6 +69,8 @@ AM_CONDITIONAL([HAVE_CDB], [false]) PDNS_CHECK_LIBCRYPTO +DNSDIST_ENABLE_TLS_PROVIDERS + PDNS_ENABLE_DNS_OVER_TLS DNSDIST_ENABLE_DNS_OVER_HTTPS diff --git a/pdns/dnsdistdist/docs/reference/config.rst b/pdns/dnsdistdist/docs/reference/config.rst index 65ca67cedd..7c2d96ccb1 100644 --- a/pdns/dnsdistdist/docs/reference/config.rst +++ b/pdns/dnsdistdist/docs/reference/config.rst @@ -211,7 +211,7 @@ Listen Sockets * ``maxConcurrentTCPConnections=0``: int - Maximum number of concurrent incoming TCP connections. The default is 0 which means unlimited. * ``releaseBuffers=true``: bool - Whether OpenSSL should release its I/O buffers when a connection goes idle, saving roughly 35 kB of memory per connection. * ``enableRenegotiation=false``: bool - Whether secure TLS renegotiation should be enabled (OpenSSL only, the GnuTLS provider does not support it). Disabled by default since it increases the attack surface and is seldom used for DNS. - * ``tlsAsyncMode=false``: bool - Whether to enable experimental asynchronous TLS I/O operations if OpenSSL is used as the TLS provider and an asynchronous capable SSL engine is loaded. See also :func:`loadTLSEngine` to load the engine. + * ``tlsAsyncMode=false``: bool - Whether to enable experimental asynchronous TLS I/O operations if OpenSSL is used as the TLS implementation and an asynchronous capable SSL engine (or provider) is loaded. See also :func:`loadTLSEngine` or :func:`loadTLSProvider` to load the engine (or provider). * ``additionalAddresses``: list - List of additional addresses (with port) to listen on. Using this option instead of creating a new frontend for each address avoids the creation of new thread and Frontend objects, reducing the memory usage. The drawback is that there will be a single set of metrics for all addresses. * ``ignoreTLSConfigurationErrors=false``: bool - Ignore TLS configuration errors (such as invalid certificate path) and just issue a warning instead of aborting the whole process @@ -1855,6 +1855,17 @@ Other functions :param string engineName: The name of the engine to load. :param string defaultString: The default string to pass to the engine. The exact value depends on the engine but represents the algorithms to register with the engine, as a list of comma-separated keywords. For example "RSA,EC,DSA,DH,PKEY,PKEY_CRYPTO,PKEY_ASN1". +.. function:: loadTLSProvider(providerName) + + .. versionadded:: 1.8.0 + + Load the OpenSSL provider named ``providerName``. Providers can be used to accelerate cryptographic operations, like for example Intel QAT. + At the moment up to a maximum of 32 loaded providers are supported, and that support is experimental. + Note that :func:`loadTLSProvider` is only available when building against OpenSSL version >= 3.0 and with the `--enable-tls-provider` configure flag on. In other cases, :func:`loadTLSEngine` should be used instead. + Some providers might actually degrade performance unless the TLS asynchronous mode of OpenSSL is enabled. To enable it see the ``tlsAsyncMode`` parameter on :func:`addTLSLocal`. + + :param string providerName: The name of the provider to load. + .. function:: newTLSCertificate(pathToCert[, options]) .. versionadded:: 1.8.0 diff --git a/pdns/dnsdistdist/m4/dnsdist_enable_tls_providers.m4 b/pdns/dnsdistdist/m4/dnsdist_enable_tls_providers.m4 new file mode 100644 index 0000000000..1e58025bf3 --- /dev/null +++ b/pdns/dnsdistdist/m4/dnsdist_enable_tls_providers.m4 @@ -0,0 +1,22 @@ +AC_DEFUN([DNSDIST_ENABLE_TLS_PROVIDERS], [ + AC_MSG_CHECKING([whether to enable OpenSSL >= 3.0 TLS providers (experimental)]) + AC_ARG_ENABLE([tls-providers], + AS_HELP_STRING([--enable-tls-providers], [enable TLS providers (experimental and requires OpenSSL >= 3.0) @<:@default=no@:>@]), + [enable_tls_providers=$enableval], + [enable_tls_providers=no] + ) + AC_MSG_RESULT([$enable_tls_providers]) + AM_CONDITIONAL([HAVE_TLS_PROVIDERS], [test "x$enable_tls_providers" != "xno"]) + + PKG_CHECK_MODULES([LIBSSL], [libssl >= 3.0], [ + [HAVE_LIBSSL_3_PLUS=1] + AC_DEFINE([HAVE_LIBSSL_3_PLUS], [1], [Define to 1 if you have OpenSSL >= 3.0]) + ], [ : ]) + + AM_COND_IF([HAVE_TLS_PROVIDERS], [ + AC_DEFINE([HAVE_TLS_PROVIDERS], [1], [Define to 1 if you enable OpenSSL >= 3.0 TLS providers]) + AS_IF([test "x$HAVE_LIBSSL_3_PLUS" != "x1"], [ + AC_MSG_ERROR([TLS providers support requires OpenSSL >= 3.0]) + ]) + ]) +]) diff --git a/pdns/libssl.cc b/pdns/libssl.cc index 6026bdba3f..33d764f494 100644 --- a/pdns/libssl.cc +++ b/pdns/libssl.cc @@ -29,6 +29,7 @@ #if OPENSSL_VERSION_MAJOR >= 3 #include +#include #include #include #endif @@ -79,9 +80,15 @@ static void openssl_thread_cleanup() #endif /* (OPENSSL_VERSION_NUMBER < 0x1010000fL || (defined LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2090100fL) */ static std::atomic s_users; + +#if OPENSSL_VERSION_MAJOR >= 3 && defined(HAVE_TLS_PROVIDERS) +static LockGuarded>> s_providers; +#else #ifndef OPENSSL_NO_ENGINE static LockGuarded>> s_engines; #endif +#endif + static int s_ticketsKeyIndex{-1}; static int s_countersIndex{-1}; static int s_keyLogIndex{-1}; @@ -143,6 +150,7 @@ void registerOpenSSLUser() void unregisterOpenSSLUser() { if (s_users.fetch_sub(1) == 1) { +#if OPENSSL_VERSION_MAJOR < 3 || !defined(HAVE_TLS_PROVIDERS) #ifndef OPENSSL_NO_ENGINE for (auto& [name, engine] : *s_engines.lock()) { ENGINE_finish(engine.get()); @@ -150,6 +158,7 @@ void unregisterOpenSSLUser() } s_engines.lock()->clear(); #endif +#endif #if (OPENSSL_VERSION_NUMBER < 0x1010000fL || (defined LIBRESSL_VERSION_NUMBER && LIBRESSL_VERSION_NUMBER < 0x2090100fL)) ERR_free_strings(); @@ -165,6 +174,32 @@ void unregisterOpenSSLUser() } } +#if defined(HAVE_LIBSSL) && OPENSSL_VERSION_MAJOR >= 3 && defined(HAVE_TLS_PROVIDERS) +std::pair libssl_load_provider(const std::string& providerName) +{ + if (s_users.load() == 0) { + /* We need to make sure that OpenSSL has been properly initialized before loading an engine. + This messes up our accounting a bit, so some memory might not be properly released when + the program exits when engines are in use. */ + registerOpenSSLUser(); + } + + auto providers = s_providers.lock(); + if (providers->count(providerName) > 0) { + return { false, "TLS provider already loaded" }; + } + + auto provider = std::unique_ptr(OSSL_PROVIDER_load(nullptr, providerName.c_str()), OSSL_PROVIDER_unload); + if (provider == nullptr) { + return { false, "unable to load TLS provider '" + providerName + "'" }; + } + + providers->insert({providerName, std::move(provider)}); + return { true, "" }; +} +#endif /* HAVE_LIBSSL && OPENSSL_VERSION_MAJOR >= 3 && HAVE_TLS_PROVIDERS */ + +#if defined(HAVE_LIBSSL) && !defined(HAVE_TLS_PROVIDERS) std::pair libssl_load_engine(const std::string& engineName, const std::optional& defaultString) { #ifdef OPENSSL_NO_ENGINE @@ -204,6 +239,7 @@ std::pair libssl_load_engine(const std::string& engineName, c return { true, "" }; #endif } +#endif /* HAVE_LIBSSL && !HAVE_TLS_PROVIDERS */ void* libssl_get_ticket_key_callback_data(SSL* s) { diff --git a/pdns/libssl.hh b/pdns/libssl.hh index d8af451112..296bd72e49 100644 --- a/pdns/libssl.hh +++ b/pdns/libssl.hh @@ -164,6 +164,12 @@ bool libssl_set_alpn_protos(SSL_CTX* ctx, const std::vector std::string libssl_get_error_string(); +#if defined(HAVE_LIBSSL) && OPENSSL_VERSION_MAJOR >= 3 && defined(HAVE_TLS_PROVIDERS) +std::pair libssl_load_provider(const std::string& engineName); +#endif /* HAVE_LIBSSL && OPENSSL_VERSION_MAJOR >= 3 && HAVE_TLS_PROVIDERS */ + +#if defined(HAVE_LIBSSL) && !defined(HAVE_TLS_PROVIDERS) std::pair libssl_load_engine(const std::string& engineName, const std::optional& defaultString); +#endif /* HAVE_LIBSSL && !HAVE_TLS_PROVIDERS */ #endif /* HAVE_LIBSSL */