]> granicus.if.org Git - pdns/commitdiff
dnsdist: Add minTLSVersion for DoH and DoH
authorRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 16 Aug 2019 12:31:51 +0000 (14:31 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 16 Aug 2019 12:31:51 +0000 (14:31 +0200)
pdns/dnsdist-lua.cc
pdns/dnsdistdist/docs/reference/config.rst
pdns/dnsdistdist/doh.cc
pdns/dnsdistdist/libssl.cc
pdns/dnsdistdist/libssl.hh
pdns/dnsdistdist/tcpiohandler.cc
pdns/doh.hh
pdns/tcpiohandler.hh

index 5ee4eedf05fdc6e717ed4b1b085c268972df7ac1..f448e6c6fddcf797a89eef906db85dc4812ee314 100644 (file)
@@ -1727,6 +1727,9 @@ void setupLuaConfig(bool client)
       if (vars->count("ciphersTLS13")) {
         frontend->d_ciphers13 = boost::get<const string>((*vars)["ciphersTLS13"]);
       }
+      if (vars->count("minTLSVersion")) {
+        frontend->d_minTLSVersion = libssl_tls_version_from_string(boost::get<const string>((*vars)["minTLSVersion"]));
+      }
       if (vars->count("serverTokens")) {
         frontend->d_serverTokens = boost::get<const string>((*vars)["serverTokens"]);
       }
@@ -1873,6 +1876,12 @@ void setupLuaConfig(bool client)
             frontend->d_ciphers13 = boost::get<const string>((*vars)["ciphersTLS13"]);
           }
 
+#ifdef HAVE_LIBSSL
+          if (vars->count("minTLSVersion")) {
+            frontend->d_minTLSVersion = libssl_tls_version_from_string(boost::get<const string>((*vars)["minTLSVersion"]));
+          }
+#endif /* HAVE_LIBSSL */
+
           if (vars->count("ticketKeyFile")) {
             frontend->d_ticketKeyFile = boost::get<const string>((*vars)["ticketKeyFile"]);
           }
index 29f50609d033d1aceadfa47978a2c431c61ae4ff..a6be0c4b2f68126722e2f98578a01ccfaff8b4ad 100644 (file)
@@ -128,6 +128,7 @@ Listen Sockets
   * ``serverTokens``: str - The content of the Server: HTTP header returned by dnsdist. The default is "h2o/dnsdist".
   * ``customResponseHeaders={}``: table - Set custom HTTP header(s) returned by dnsdist.
   * ``ocspResponses``: list - List of files containing OCSP responses, in the same order than the certificates and keys, that will be used to provide OCSP stapling responses.
+  * ``minTLSVersion``: str - Minimum version of the TLS protocol to support. Possible values are 'tls-1.0', 'tls-1.1', 'tls-1.2' and 'tls-1.3'.
 
 .. function:: addTLSLocal(address, certFile(s), keyFile(s) [, options])
 
@@ -138,7 +139,7 @@ Listen Sockets
   .. versionchanged:: 1.3.3
     ``numberOfStoredSessions`` option added.
   .. versionchanged:: 1.4.0
-    ``ciphersTLS13`` and ``ocspResponses`` options added.
+    ``ciphersTLS13``, ``minTLSVersion`` and ``ocspResponses`` options added.
 
   Listen on the specified address and TCP port for incoming DNS over TLS connections, presenting the specified X.509 certificate.
 
@@ -163,6 +164,7 @@ Listen Sockets
   * ``sessionTickets``: bool - Whether session resumption via session tickets is enabled. Default is true, meaning tickets are enabled.
   * ``numberOfStoredSessions``: int - The maximum number of sessions kept in memory at the same time. At this time this is only supported by the OpenSSL provider, as stored sessions are not supported with the GnuTLS one. Default is 20480. Setting this value to 0 disables stored session entirely.
   * ``ocspResponses``: list - List of files containing OCSP responses, in the same order than the certificates and keys, that will be used to provide OCSP stapling responses.
+  * ``minTLSVersion``: str - Minimum version of the TLS protocol to support. Possible values are 'tls-1.0', 'tls-1.1', 'tls-1.2' and 'tls-1.3'. Note that this value is ignored when the GnuTLS provider is in use, and the ``ciphers`` option should be set accordingly instead. For example, 'NORMAL:!VERS-TLS1.0:!VERS-TLS1.1' will disable TLS 1.0 and 1.1.
 
 .. function:: setLocal(address[, options])
 
index a5e187567764b2ebcdd8f0322ae9a4b251b0edd3..1f8f64e10286f6df806909d5de7d2a3685e55976 100644 (file)
@@ -884,7 +884,7 @@ static int ocsp_stapling_callback(SSL* ssl, void* arg)
   return libssl_ocsp_stapling_callback(ssl, *ocspMap);
 }
 
-static std::unique_ptr<SSL_CTX, void(*)(SSL_CTX*)> getTLSContext(const std::vector<std::pair<std::string, std::string>>& pairs, const std::string& ciphers, const std::string& ciphers13, const std::vector<std::string>& ocspFiles, std::map<int, std::string>& ocspResponses)
+static std::unique_ptr<SSL_CTX, void(*)(SSL_CTX*)> getTLSContext(const std::vector<std::pair<std::string, std::string>>& pairs, const std::string& ciphers, const std::string& ciphers13, LibsslTLSVersion minTLSVersion, const std::vector<std::string>& ocspFiles, std::map<int, std::string>& ocspResponses)
 {
   auto ctx = std::unique_ptr<SSL_CTX, void(*)(SSL_CTX*)>(SSL_CTX_new(SSLv23_server_method()), SSL_CTX_free);
 
@@ -897,6 +897,7 @@ static std::unique_ptr<SSL_CTX, void(*)(SSL_CTX*)> getTLSContext(const std::vect
     SSL_OP_SINGLE_ECDH_USE;
 
   SSL_CTX_set_options(ctx.get(), sslOptions);
+  libssl_set_min_tls_version(ctx, minTLSVersion);
 
 #ifdef SSL_CTX_set_ecdh_auto
   SSL_CTX_set_ecdh_auto(ctx.get(), 1);
@@ -961,6 +962,7 @@ static void setupAcceptContext(DOHAcceptContext& ctx, DOHServerConfig& dsc, bool
     auto tlsCtx = getTLSContext(dsc.df->d_certKeyPairs,
                                 dsc.df->d_ciphers,
                                 dsc.df->d_ciphers13,
+                                dsc.df->d_minTLSVersion,
                                 dsc.df->d_ocspFiles,
                                 ctx.d_ocspResponses);
 
@@ -988,6 +990,7 @@ void DOHFrontend::setup()
   auto tlsCtx = getTLSContext(d_certKeyPairs,
                               d_ciphers,
                               d_ciphers13,
+                              d_minTLSVersion,
                               d_ocspFiles,
                               d_dsc->accept_ctx->d_ocspResponses);
 
index d0a2da24b07dbf8aaa6910d67c5c4548393570a6..d7561578eab8d455b5563dfed3d9e50e038289e4 100644 (file)
@@ -264,4 +264,71 @@ bool libssl_generate_ocsp_response(const std::string& certFile, const std::strin
 }
 #endif /* HAVE_OCSP_BASIC_SIGN */
 
+LibsslTLSVersion libssl_tls_version_from_string(const std::string& str)
+{
+  if (str == "tls1.0") {
+    return LibsslTLSVersion::TLS10;
+  }
+  if (str == "tls1.1") {
+    return LibsslTLSVersion::TLS11;
+  }
+  if (str == "tls1.2") {
+    return LibsslTLSVersion::TLS12;
+  }
+  if (str == "tls1.3") {
+    return LibsslTLSVersion::TLS13;
+  }
+  throw std::runtime_error("Unknown TLS version '" + str);
+}
+
+bool libssl_set_min_tls_version(std::unique_ptr<SSL_CTX, void(*)(SSL_CTX*)>& ctx, LibsslTLSVersion version)
+{
+#if (OPENSSL_VERSION_NUMBER >= 0x1010000fL && !defined LIBRESSL_VERSION_NUMBER)
+  /* these functions have been introduced in 1.1.0, and the use of SSL_OP_NO_* is deprecated */
+  int vers;
+  switch(version) {
+  case LibsslTLSVersion::TLS10:
+    vers = TLS1_VERSION;
+    break;
+  case LibsslTLSVersion::TLS11:
+    vers = TLS1_1_VERSION;
+    break;
+  case LibsslTLSVersion::TLS12:
+    vers = TLS1_2_VERSION;
+    break;
+  case LibsslTLSVersion::TLS13:
+    vers = TLS1_3_VERSION;
+    break;
+  default:
+    return false;
+  }
+
+  if (SSL_CTX_set_min_proto_version(ctx.get(), vers) != 1) {
+    return false;
+  }
+  return true;
+#else
+  long vers = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
+  switch(version) {
+  case LibsslTLSVersion::TLS10:
+    break;
+  case LibsslTLSVersion::TLS11:
+    vers |= SSL_OP_NO_TLSv1;
+    break;
+  case LibsslTLSVersion::TLS12:
+    vers |= SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1;
+    break;
+  case LibsslTLSVersion::TLS13:
+    vers |= SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2;
+    break;
+  default:
+    return false;
+  }
+
+  long options = SSL_CTX_get_options(ctx.get());
+  SSL_CTX_set_options(ctx.get(), options | vers);
+  return true;
+#endif
+}
+
 #endif /* HAVE_LIBSSL */
index f722e5cddea6bdec0f062bf6aa0c3bd1b22f8589..5d2d66fc6dc394999ed1f95289a5a4ebf9d92062 100644 (file)
@@ -22,4 +22,9 @@ int libssl_get_last_key_type(std::unique_ptr<SSL_CTX, void(*)(SSL_CTX*)>& ctx);
 bool libssl_generate_ocsp_response(const std::string& certFile, const std::string& caCert, const std::string& caKey, const std::string& outFile, int ndays, int nmin);
 #endif
 
+enum class LibsslTLSVersion { TLS10, TLS11, TLS12, TLS13 };
+
+LibsslTLSVersion libssl_tls_version_from_string(const std::string& str);
+bool libssl_set_min_tls_version(std::unique_ptr<SSL_CTX, void(*)(SSL_CTX*)>& ctx, LibsslTLSVersion version);
+
 #endif /* HAVE_LIBSSL */
index fab7815debae23ce193b09dbe62b55d8b1418a69..c5aa691002be4865b835542a3c554090aa54af35 100644 (file)
@@ -409,6 +409,7 @@ public:
     SSL_CTX_set_tlsext_ticket_key_cb(d_tlsCtx.get(), &OpenSSLTLSIOCtx::ticketKeyCb);
     SSL_CTX_set_ex_data(d_tlsCtx.get(), s_ticketsKeyIndex, this);
     SSL_CTX_set_options(d_tlsCtx.get(), sslOptions);
+    libssl_set_min_tls_version(d_tlsCtx, fe.d_minTLSVersion);
 #if defined(SSL_CTX_set_ecdh_auto)
     SSL_CTX_set_ecdh_auto(d_tlsCtx.get(), 1);
 #endif
index fc8b539b6ed99e60e54348ffdacc8b98654f05ae..ddcb2437806e56c0832ba5e6a99bbf32c95d8ecb 100644 (file)
@@ -1,5 +1,6 @@
 #pragma once
 #include "iputils.hh"
+#include "libssl.hh"
 
 struct DOHServerConfig;
 
@@ -11,6 +12,7 @@ struct DOHFrontend
   std::string d_ciphers;
   std::string d_ciphers13;
   std::string d_serverTokens{"h2o/dnsdist"};
+  LibsslTLSVersion d_minTLSVersion{LibsslTLSVersion::TLS10};
   std::vector<std::pair<std::string, std::string>> d_customResponseHeaders;
   ComboAddress d_local;
 
index 05ad306e62cccfee801ef8e8da57dc1e45f1be02..5356e59054a88d367c498c4c0fc750a3702ae5e3 100644 (file)
@@ -2,6 +2,7 @@
 #pragma once
 #include <memory>
 
+#include "libssl.hh"
 #include "misc.hh"
 
 enum class IOState { Done, NeedRead, NeedWrite };
@@ -149,6 +150,8 @@ public:
   size_t d_maxStoredSessions{20480};
   time_t d_ticketsKeyRotationDelay{43200};
   uint8_t d_numberOfTicketsKeys{5};
+  LibsslTLSVersion d_minTLSVersion{LibsslTLSVersion::TLS10};
+
   bool d_enableTickets{true};
 
 private: