]> granicus.if.org Git - pdns/commitdiff
dnsdist: Merge the setup of TLS contexts in Doh and DoT
authorRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 4 Oct 2019 15:57:04 +0000 (17:57 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 10 Oct 2019 15:07:19 +0000 (17:07 +0200)
pdns/dnsdist-lua.cc
pdns/dnsdistdist/doh.cc
pdns/dnsdistdist/libssl.cc
pdns/dnsdistdist/tcpiohandler.cc
pdns/doh.hh
pdns/libssl.hh
pdns/tcpiohandler.hh

index ca1f6a31e8603f571b8fe5d0d12ce5d27bc59323..7f6ef05ec54b1ad9cbecbeb310530d1059067804 100644 (file)
@@ -146,6 +146,60 @@ static bool loadTLSCertificateAndKeys(const std::string& context, std::vector<st
 
   return true;
 }
+
+static void parseTLSConfig(TLSConfig& config, const std::string& context, boost::optional<localbind_t> vars)
+{
+  if (vars->count("ciphers")) {
+    config.d_ciphers = boost::get<const string>((*vars)["ciphers"]);
+  }
+
+  if (vars->count("ciphersTLS13")) {
+    config.d_ciphers13 = boost::get<const string>((*vars)["ciphersTLS13"]);
+  }
+
+#ifdef HAVE_LIBSSL
+  if (vars->count("minTLSVersion")) {
+    config.d_minTLSVersion = libssl_tls_version_from_string(boost::get<const string>((*vars)["minTLSVersion"]));
+  }
+#endif /* HAVE_LIBSSL */
+
+  if (vars->count("ticketKeyFile")) {
+    config.d_ticketKeyFile = boost::get<const string>((*vars)["ticketKeyFile"]);
+  }
+
+  if (vars->count("ticketsKeysRotationDelay")) {
+    config.d_ticketsKeyRotationDelay = boost::get<int>((*vars)["ticketsKeysRotationDelay"]);
+  }
+
+  if (vars->count("numberOfTicketsKeys")) {
+    config.d_numberOfTicketsKeys = boost::get<int>((*vars)["numberOfTicketsKeys"]);
+  }
+
+  if (vars->count("preferServerCiphers")) {
+    config.d_preferServerCiphers = boost::get<bool>((*vars)["preferServerCiphers"]);
+  }
+
+  if (vars->count("sessionTickets")) {
+    config.d_enableTickets = boost::get<bool>((*vars)["sessionTickets"]);
+  }
+
+  if (vars->count("numberOfStoredSessions")) {
+    auto value = boost::get<int>((*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<std::vector<std::pair<int, std::string>>>((*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<DOHFrontend>();
 
     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<int>((*vars)["idleTimeout"]);
       }
-      if (vars->count("ciphers")) {
-        frontend->d_ciphers = boost::get<const string>((*vars)["ciphers"]);
-      }
-      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"]);
       }
+
       if (vars->count("customResponseHeaders")) {
         for (auto const& headerMap : boost::get<std::map<std::string,std::string>>((*vars)["customResponseHeaders"])) {
           std::pair<std::string,std::string> 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<const string>((*vars)["ticketKeyFile"]);
-      }
-
-      if (vars->count("ticketsKeysRotationDelay")) {
-        frontend->d_ticketsKeyRotationDelay = boost::get<int>((*vars)["ticketsKeysRotationDelay"]);
-      }
-
-      if (vars->count("numberOfTicketsKeys")) {
-        frontend->d_numberOfTicketsKeys = boost::get<int>((*vars)["numberOfTicketsKeys"]);
-      }
-
-      if (vars->count("sessionTickets")) {
-        frontend->d_enableTickets = boost::get<bool>((*vars)["sessionTickets"]);
-      }
-
-      if (vars->count("preferServerCiphers")) {
-        frontend->d_preferServerCiphers = boost::get<bool>((*vars)["preferServerCiphers"]);
-      }
-
-      if (vars->count("numberOfStoredSessions")) {
-        auto value = boost::get<int>((*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<std::vector<std::pair<int, std::string>>>((*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<ClientState>(new ClientState(frontend->d_local, true, reusePort, tcpFastOpenQueueSize, interface, cpus));
@@ -1927,7 +1939,7 @@ void setupLuaConfig(bool client)
         }
         shared_ptr<TLSFrontend> frontend = std::make_shared<TLSFrontend>();
 
-        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<const string>((*vars)["provider"]);
           }
 
-          if (vars->count("ciphers")) {
-            frontend->d_ciphers = boost::get<const string>((*vars)["ciphers"]);
-          }
-
-          if (vars->count("ciphersTLS13")) {
-            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"]);
-          }
-
-          if (vars->count("ticketsKeysRotationDelay")) {
-            frontend->d_ticketsKeyRotationDelay = boost::get<int>((*vars)["ticketsKeysRotationDelay"]);
-          }
-
-          if (vars->count("numberOfTicketsKeys")) {
-            frontend->d_numberOfTicketsKeys = boost::get<int>((*vars)["numberOfTicketsKeys"]);
-          }
-
-          if (vars->count("sessionTickets")) {
-            frontend->d_enableTickets = boost::get<bool>((*vars)["sessionTickets"]);
-          }
-
-          if (vars->count("preferServerCiphers")) {
-            frontend->d_preferServerCiphers = boost::get<bool>((*vars)["preferServerCiphers"]);
-          }
-
-          if (vars->count("numberOfStoredSessions")) {
-            auto value = boost::get<int>((*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<std::vector<std::pair<int, std::string>>>((*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<void(std::shared_ptr<TLSFrontend>::*)(boost::variant<std::string, std::vector<std::pair<int,std::string>>> certFiles, boost::variant<std::string, std::vector<std::pair<int,std::string>>> keyFiles)>("loadNewCertificatesAndKeys", [](std::shared_ptr<TLSFrontend>& frontend, boost::variant<std::string, std::vector<std::pair<int,std::string>>> certFiles, boost::variant<std::string, std::vector<std::pair<int,std::string>>> 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
index 8973bf4d18344314feba68231a44ae92809590a8..aef94c4d53573710107e7d9a0181fa6ff0ffb1cf 100644 (file)
@@ -929,114 +929,38 @@ static int ticket_key_callback(SSL *s, unsigned char keyName[TLS_TICKETS_KEY_NAM
 static std::unique_ptr<SSL_CTX, void(*)(SSL_CTX*)> getTLSContext(DOHFrontend& df,
                                                                  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);
-
-  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<OpenSSLTLSTicketKeysRing>(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<int> 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<OpenSSLTLSTicketKeysRing>(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<DOHServerConfig>(d_idleTimeout);
 
-  if  (!d_certKeyPairs.empty()) {
+  if  (!d_tlsConfig.d_certKeyPairs.empty()) {
     auto tlsCtx = getTLSContext(*this,
                                 d_dsc->accept_ctx->d_ocspResponses);
 
index f900365bafc85f68ac541684e6b0b430bad0ddea..1c78e48c3591807f1f510243b8d039dd216ba65f 100644 (file)
@@ -563,4 +563,95 @@ bool OpenSSLTLSTicketKey::decrypt(const unsigned char* iv, EVP_CIPHER_CTX *ectx,
   return true;
 }
 
+std::unique_ptr<SSL_CTX, void(*)(SSL_CTX*)> libssl_init_server_context(const TLSConfig& config,
+                                                                       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);
+
+  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<int> 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 */
index dd98fc2176e839214c8d62244df01e1b382497dd..801f1d494cc08a84658ecb9990141e0199f58f59 100644 (file)
@@ -241,120 +241,34 @@ private:
 class OpenSSLTLSIOCtx: public TLSCtx
 {
 public:
-  OpenSSLTLSIOCtx(const TLSFrontend& fe): d_ticketKeys(fe.d_numberOfTicketsKeys), d_tlsCtx(std::unique_ptr<SSL_CTX, void(*)(SSL_CTX*)>(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, void(*)(SSL_CTX*)>(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<int> 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<int, std::string> d_ocspResponses;
-  std::unique_ptr<SSL_CTX, void(*)(SSL_CTX*)> d_tlsCtx;
+  std::unique_ptr<SSL_CTX, void(*)(SSL_CTX*)> 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<gnutls_certificate_credentials_st, void(*)(gnutls_certificate_credentials_t)>(nullptr, gnutls_certificate_free_credentials)), d_enableTickets(fe.d_enableTickets)
+  GnuTLSIOCtx(const TLSFrontend& fe): d_creds(std::unique_ptr<gnutls_certificate_credentials_st, void(*)(gnutls_certificate_credentials_t)>(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<gnutls_certificate_credentials_st, void(*)(gnutls_certificate_credentials_t)>(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) {
index 289b883614fc874e9fe0e1cab9eab8a75ef2ea86..fe432d585f2bbe3462949eb7edb07dd021b172bd 100644 (file)
@@ -46,13 +46,9 @@ struct DOHFrontend
   }
 
   std::shared_ptr<DOHServerConfig> d_dsc{nullptr};
-  std::vector<std::pair<std::string, std::string>> d_certKeyPairs;
-  std::vector<std::string> d_ocspFiles;
   std::vector<std::shared_ptr<DOHResponseMapEntry>> 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<OpenSSLTLSTicketKeysRing> d_ticketKeys{nullptr};
 #endif
@@ -61,13 +57,6 @@ struct DOHFrontend
 
   uint32_t d_idleTimeout{30};             // HTTP idle timeout in seconds
   std::vector<std::string> 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<uint64_t> d_httpconnects{0};   // number of TCP/IP connections established
   std::atomic<uint64_t> d_getqueries{0};     // valid DNS queries received via GET
index 7fd52bcfb77ea916fefca2789e5d097bcfb0f802..9276b74e6fbbb9e52872b64bf9f0d91859434c95 100644 (file)
 
 enum class LibsslTLSVersion { Unknown, TLS10, TLS11, TLS12, TLS13 };
 
+class TLSConfig
+{
+public:
+  std::vector<std::pair<std::string, std::string>> d_certKeyPairs;
+  std::vector<std::string> 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 <openssl/ssl.h>
 
@@ -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<SSL_CTX, void(*)(SSL_CTX*)>& ctx, LibsslTLSVersion version);
 
+std::unique_ptr<SSL_CTX, void(*)(SSL_CTX*)> libssl_init_server_context(const TLSConfig& config,
+                                                                       std::map<int, std::string>& ocspResponses);
+
 #endif /* HAVE_LIBSSL */
index c5735729a96502229aa4db708f2ff9a64a6b6962..3bf3f5d2b509d8b2056289e46fb88f85ad33c50e 100644 (file)
@@ -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<std::pair<std::string, std::string>> d_certKeyPairs;
-  std::vector<std::string> 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<TLSCtx> d_ctx{nullptr};