]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
OpenSSL 3.0: Add loadTLSProvider to replace loadTLSEngine 12423/head
authorFred Morcos <fred.morcos@open-xchange.com>
Tue, 7 Feb 2023 18:07:08 +0000 (19:07 +0100)
committerFred Morcos <fred.morcos@open-xchange.com>
Wed, 8 Feb 2023 17:05:52 +0000 (18:05 +0100)
pdns/dnsdist-console.cc
pdns/dnsdist-lua.cc
pdns/dnsdistdist/configure.ac
pdns/dnsdistdist/docs/reference/config.rst
pdns/dnsdistdist/m4/dnsdist_enable_tls_providers.m4 [new file with mode: 0644]
pdns/libssl.cc
pdns/libssl.hh

index 02316b0564b3741accdf0cf630670792e0a7d1e3..6b31e9f138da8696ada9cb67d1de07bdccaf9f90 100644 (file)
@@ -562,7 +562,12 @@ const std::vector<ConsoleKeyword> 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<ConsoleKeyword> 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" },
index e5b31692f980f067a8270cdae7d3b4a64aee9a2d..a0c156183878d49c1cc653a98da00da0e64d3db3 100644 (file)
@@ -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<std::string> 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) {
index 18c3e157df5b4f7a1ab52928b4e2e2bf74be9027..83b88ef0a9a843ffafac1e6ed72b5b98495e06f3 100644 (file)
@@ -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
 
index 65ca67cedd1e23bf93dd209e58e80ff641e8cb75..7c2d96ccb197e4ff8fe36659e022a8ee70178cc4 100644 (file)
@@ -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 (file)
index 0000000..1e58025
--- /dev/null
@@ -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])
+    ])
+  ])
+])
index 6026bdba3f8b6b9cc0e01234eb35aae4a671f907..33d764f4946892f2abd0550063d09eda57a8f0f7 100644 (file)
@@ -29,6 +29,7 @@
 
 #if OPENSSL_VERSION_MAJOR >= 3
 #include <openssl/param_build.h>
+#include <openssl/core.h>
 #include <openssl/core_names.h>
 #include <openssl/evp.h>
 #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<uint64_t> s_users;
+
+#if OPENSSL_VERSION_MAJOR >= 3 && defined(HAVE_TLS_PROVIDERS)
+static LockGuarded<std::unordered_map<std::string, std::unique_ptr<OSSL_PROVIDER, decltype(&OSSL_PROVIDER_unload)>>> s_providers;
+#else
 #ifndef OPENSSL_NO_ENGINE
 static LockGuarded<std::unordered_map<std::string, std::unique_ptr<ENGINE, int(*)(ENGINE*)>>> 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<bool, std::string> 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, decltype(&OSSL_PROVIDER_unload)>(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<bool, std::string> libssl_load_engine(const std::string& engineName, const std::optional<std::string>& defaultString)
 {
 #ifdef OPENSSL_NO_ENGINE
@@ -204,6 +239,7 @@ std::pair<bool, std::string> 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)
 {
index d8af451112029fc9058b63e50bc31ccd98834618..296bd72e49ec7c345e8e4fd9666b652170d15a8e 100644 (file)
@@ -164,6 +164,12 @@ bool libssl_set_alpn_protos(SSL_CTX* ctx, const std::vector<std::vector<uint8_t>
 
 std::string libssl_get_error_string();
 
+#if defined(HAVE_LIBSSL) && OPENSSL_VERSION_MAJOR >= 3 && defined(HAVE_TLS_PROVIDERS)
+std::pair<bool, std::string> 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<bool, std::string> libssl_load_engine(const std::string& engineName, const std::optional<std::string>& defaultString);
+#endif /* HAVE_LIBSSL && !HAVE_TLS_PROVIDERS */
 
 #endif /* HAVE_LIBSSL */