]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
TCPIOHandler: Handle validation of IP addresses in certificates
authorRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 8 Feb 2022 09:35:19 +0000 (10:35 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 22 Feb 2022 09:00:59 +0000 (10:00 +0100)
14 files changed:
pdns/dnsdist-lua.cc
pdns/dnsdist.hh
pdns/dnsdistdist/dnsdist-discovery.cc
pdns/dnsdistdist/dnsdist-healthchecks.cc
pdns/dnsdistdist/dnsdist-tcp-downstream.cc
pdns/dnsdistdist/docs/reference/config.rst
pdns/dnsdistdist/test-dnsdistnghttp2_cc.cc
pdns/dnsdistdist/test-dnsdisttcp_cc.cc
pdns/lwres.cc
pdns/rec-carbon.cc
pdns/sdig.cc
pdns/tcpiohandler.cc
pdns/tcpiohandler.hh
pdns/ws-recursor.cc

index 6c3d66dc6a86620b9e430b80c567f47af45b554b..23f549e13d3faa8d4555d94b6cbec14f540e99de 100644 (file)
@@ -565,6 +565,17 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
                          if (vars.count("subjectName")) {
                            config.d_tlsSubjectName = boost::get<string>(vars.at("subjectName"));
                          }
+                         else if (vars.count("subjectAddr")) {
+                           try {
+                             ComboAddress ca(boost::get<string>(vars.at("subjectAddr")));
+                             config.d_tlsSubjectName = ca.toString();
+                             config.d_tlsSubjectIsAddr = true;
+                           }
+                           catch (const std::exception& e) {
+                             errlog("Error creating new server: downstream subjectAddr value must be a valid IP address");
+                             return std::shared_ptr<DownstreamState>();
+                           }
+                         }
 
                          if (vars.count("tls")) {
                            config.d_tlsParams.d_provider = boost::get<string>(vars.at("tls"));
index 4c51a6842b86d80be1d06baa2aa8f43b69aa5d2a..f03522db86ba0faf2e2f10c8f357ac59314280b8 100644 (file)
@@ -739,6 +739,7 @@ struct DownstreamState: public std::enable_shared_from_this<DownstreamState>
     uint8_t maxCheckFailures{1};
     uint8_t minRiseSuccesses{1};
     Availability availability{Availability::Auto};
+    bool d_tlsSubjectIsAddr{false};
     bool mustResolve{false};
     bool useECS{false};
     bool useProxyProtocol{false};
index 58a6b025bfbb7837ae697547cceaa6d6185d5d15..8821f4ea819255da1b419d7c007dfb822bd3c9dc 100644 (file)
@@ -369,7 +369,7 @@ static bool checkBackendUsability(std::shared_ptr<DownstreamState>& ds)
     }
 
     time_t now = time(nullptr);
-    auto handler = std::make_unique<TCPIOHandler>(ds->d_config.d_tlsSubjectName, sock.releaseHandle(), timeval{ds->d_config.checkTimeout, 0}, ds->d_tlsCtx, now);
+    auto handler = std::make_unique<TCPIOHandler>(ds->d_config.d_tlsSubjectName, ds->d_config.d_tlsSubjectIsAddr, sock.releaseHandle(), timeval{ds->d_config.checkTimeout, 0}, ds->d_tlsCtx, now);
     handler->connect(ds->d_config.tcpFastOpen, ds->d_config.remote, timeval{ds->d_config.checkTimeout, 0});
     return true;
   }
@@ -424,6 +424,7 @@ bool ServiceDiscovery::tryToUpgradeBackend(const UpgradeableBackend& backend)
        extension."
     */
     config.d_tlsSubjectName = backend.d_ds->d_config.remote.toString();
+    config.d_tlsSubjectIsAddr = true;
   }
 
   if (!backend.d_poolAfterUpgrade.empty()) {
index d46ec532b30991e8d08e0f17c23ead86297cd1b9..a68e1ef0ae7819b565d6464074f8fc219dd51154 100644 (file)
@@ -420,7 +420,7 @@ bool queueHealthCheck(std::unique_ptr<FDMultiplexer>& mplexer, const std::shared
     }
     else {
       time_t now = time(nullptr);
-      data->d_tcpHandler = std::make_unique<TCPIOHandler>(ds->d_config.d_tlsSubjectName, sock.releaseHandle(), timeval{ds->d_config.checkTimeout,0}, ds->d_tlsCtx, now);
+      data->d_tcpHandler = std::make_unique<TCPIOHandler>(ds->d_config.d_tlsSubjectName, ds->d_config.d_tlsSubjectIsAddr, sock.releaseHandle(), timeval{ds->d_config.checkTimeout,0}, ds->d_tlsCtx, now);
       data->d_ioState = std::make_unique<IOStateHandler>(*mplexer, data->d_tcpHandler->getDescriptor());
       if (ds->d_tlsCtx) {
         try {
index ebb63a99c86301165c639d1679d4cc611d0c3da2..511ed30f32f2ee3e86b94f5ce8151af25d0eb9bb 100644 (file)
@@ -95,7 +95,7 @@ bool ConnectionToBackend::reconnect()
       socket->setNonBlocking();
 
       gettimeofday(&d_connectionStartTime, nullptr);
-      auto handler = std::make_unique<TCPIOHandler>(d_ds->d_config.d_tlsSubjectName, socket->releaseHandle(), timeval{0,0}, d_ds->d_tlsCtx, d_connectionStartTime.tv_sec);
+      auto handler = std::make_unique<TCPIOHandler>(d_ds->d_config.d_tlsSubjectName, d_ds->d_config.d_tlsSubjectIsAddr, socket->releaseHandle(), timeval{0,0}, d_ds->d_tlsCtx, d_connectionStartTime.tv_sec);
       if (!tlsSession && d_ds->d_tlsCtx) {
         tlsSession = g_sessionCache.getSession(d_ds->getID(), d_connectionStartTime.tv_sec);
       }
index 62b7b3cf85b42de97158561baae55b1fbbb82141..ede565291ef06ebd804b976bfe096235d8e29777 100644 (file)
@@ -533,7 +533,7 @@ Servers
     Added ``addXForwardedHeaders``, ``caStore``, ``checkTCP``, ``ciphers``, ``ciphers13``, ``dohPath``, ``enableRenegotiation``, ``releaseBuffers``, ``subjectName``, ``tcpOnly``, ``tls`` and ``validateCertificates`` to server_table.
 
   .. versionchanged:: 1.8.0
-    Added ``autoUpgrade``, ``autoUpgradeDoHKey``, ``autoUpgradeInterval``, ``autoUpgradeKeep`` and ``autoUpgradePool`` to server_table.
+    Added ``autoUpgrade``, ``autoUpgradeDoHKey``, ``autoUpgradeInterval``, ``autoUpgradeKeep``, ``autoUpgradePool`` and ``subjectAddr`` to server_table.
 
   Add a new backend server. Call this function with either a string::
 
@@ -587,7 +587,8 @@ Servers
       caStore=STRING,           -- Specifies the path to the CA certificate file, in PEM format, to use to check the certificate presented by the backend. Default is an empty string, which means to use the system CA store. Note that this directive is only used if ``validateCertificates`` is set.
       ciphers=STRING,           -- The TLS ciphers to use. The exact format depends on the provider used. When the OpenSSL provider is used, ciphers for TLS 1.3 must be specified via ``ciphersTLS13``.
       ciphersTLS13=STRING,      -- The ciphers to use for TLS 1.3, when the OpenSSL provider is used. When the GnuTLS provider is used, ``ciphers`` applies regardless of the TLS protocol and this setting is not used.
-      subjectName=STRING,       -- The subject name passed in the SNI value of the TLS handshake, and against which to validate the certificate presented by the backend. Default is empty.
+      subjectName=STRING,       -- The subject name passed in the SNI value of the TLS handshake, and against which to validate the certificate presented by the backend. Default is empty. If set this value supersedes any ``subjectAddr`` one.
+      subjectAddr=STRING,       -- The subject IP address passed in the SNI value of the TLS handshake, and against which to validate the certificate presented by the backend. Default is empty.
       validateCertificates=BOOL,-- Whether the certificate presented by the backend should be validated against the CA store (see ``caStore``). Default is true.
       dohPath=STRING,           -- Enable DNS over HTTPS communication for this backend, using POST queries to the HTTP host supplied as ``subjectName`` and the HTTP path supplied in this parameter.
       addXForwardedHeaders=BOOL,-- Whether to add X-Forwarded-For, X-Forwarded-Port and X-Forwarded-Proto headers to a DNS over HTTPS backend.
index 8e1bd912c7e8e7b417d804d0ff601e5a01a630cb..7bf4e2c8ef4820df0619c1004fa73e5570b41726 100644 (file)
@@ -498,7 +498,7 @@ public:
     return std::make_unique<MockupTLSConnection>(socket);
   }
 
-  std::unique_ptr<TLSConnection> getClientConnection(const std::string& host, int socket, const struct timeval& timeout) override
+  std::unique_ptr<TLSConnection> getClientConnection(const std::string& host, bool hostIsAddr, int socket, const struct timeval& timeout) override
   {
     return std::make_unique<MockupTLSConnection>(socket, true, d_needProxyProtocol);
   }
index 9f24135848969bf0a51dea0b1b65876c1daa6ac8..79c5cec57b69f67183c632bdc32af97d10a362c1 100644 (file)
@@ -306,7 +306,7 @@ public:
     return std::make_unique<MockupTLSConnection>(socket);
   }
 
-  std::unique_ptr<TLSConnection> getClientConnection(const std::string& host, int socket, const struct timeval& timeout) override
+  std::unique_ptr<TLSConnection> getClientConnection(const std::string& host, bool hostIsAddr, int socket, const struct timeval& timeout) override
   {
     return std::make_unique<MockupTLSConnection>(socket, true);
   }
index 0e42dc1dc34560815ad38c9914e2347000375484..933b469b8762433ef2d15456a6aa3abc1125a035 100644 (file)
@@ -275,7 +275,7 @@ static bool tcpconnect(const struct timeval& now, const ComboAddress& ip, TCPOut
       dnsOverTLS = false;
     }
   }
-  connection.d_handler = std::make_shared<TCPIOHandler>(nsName, s.releaseHandle(), timeout, tlsCtx, now.tv_sec);
+  connection.d_handler = std::make_shared<TCPIOHandler>(nsName, false, s.releaseHandle(), timeout, tlsCtx, now.tv_sec);
   // Returned state ignored
   // This can throw an exception, retry will need to happen at higher level
   connection.d_handler->tryConnect(SyncRes::s_tcp_fast_open_connect, ip);
index 723e447296f0de0af86e1e947b2ff3b10209a488..a8e67a6bae64e3f33a7c173dcc2c550bcfa89a78 100644 (file)
@@ -48,7 +48,7 @@ void doCarbonDump(void*)
       {
         g_networkTimeoutMsec / 1000, static_cast<suseconds_t>(g_networkTimeoutMsec) % 1000 * 1000
       };
-      auto handler = std::make_shared<TCPIOHandler>("", s.releaseHandle(), timeout, tlsCtx, time(nullptr));
+      auto handler = std::make_shared<TCPIOHandler>("", false, s.releaseHandle(), timeout, tlsCtx, time(nullptr));
       handler->tryConnect(SyncRes::s_tcp_fast_open_connect, remote); // we do the connect so the first attempt happens while we gather stats
 
       if (msg.empty()) {
index 6e9d77635b61536fc2226d3599b76648e80dd181..917f75038eee1ddaed121bb09a4428bf97bc4296 100644 (file)
@@ -418,7 +418,7 @@ try {
     Socket sock(dest.sin4.sin_family, SOCK_STREAM);
     sock.setNonBlocking();
     setTCPNoDelay(sock.getHandle()); // disable NAGLE, which does not play nicely with delayed ACKs
-    TCPIOHandler handler(subjectName, sock.releaseHandle(), timeout, tlsCtx, time(nullptr));
+    TCPIOHandler handler(subjectName, false, sock.releaseHandle(), timeout, tlsCtx, time(nullptr));
     handler.connect(fastOpen, dest, timeout);
     // we are writing the proxyheader inside the TLS connection. Is that right?
     if (proxyheader.size() > 0 && handler.write(proxyheader.data(), proxyheader.size(), timeout) != proxyheader.size()) {
index c85ef7d77c328aad6919cab512e871d13076fe38..84ac3aef76d56465e88e79d7ca5568aec4a7e21a 100644 (file)
@@ -102,7 +102,7 @@ public:
   }
 
   /* client-side connection */
-  OpenSSLTLSConnection(const std::string& hostname, int socket, const struct timeval& timeout, std::shared_ptr<SSL_CTX>& tlsCtx): d_tlsCtx(tlsCtx), d_conn(std::unique_ptr<SSL, void(*)(SSL*)>(SSL_new(tlsCtx.get()), SSL_free)), d_hostname(hostname), d_timeout(timeout)
+  OpenSSLTLSConnection(const std::string& hostname, bool hostIsAddr, int socket, const struct timeval& timeout, std::shared_ptr<SSL_CTX>& tlsCtx): d_tlsCtx(tlsCtx), d_conn(std::unique_ptr<SSL, void(*)(SSL*)>(SSL_new(tlsCtx.get()), SSL_free)), d_hostname(hostname), d_timeout(timeout)
   {
     d_socket = socket;
 
@@ -131,21 +131,36 @@ public:
       throw std::runtime_error("Error setting TLS SNI to " + d_hostname);
     }
 
-#if (OPENSSL_VERSION_NUMBER >= 0x1010000fL) && HAVE_SSL_SET_HOSTFLAGS // grrr libressl
-    SSL_set_hostflags(d_conn.get(), X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
-    if (SSL_set1_host(d_conn.get(), d_hostname.c_str()) != 1) {
-      throw std::runtime_error("Error setting TLS hostname for certificate validation");
+    if (hostIsAddr) {
+#if (OPENSSL_VERSION_NUMBER >= 0x10002000L)
+      X509_VERIFY_PARAM *param = SSL_get0_param(d_conn.get());
+      /* Enable automatic IP checks */
+      X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
+      if (X509_VERIFY_PARAM_set1_ip_asc(param, d_hostname.c_str()) != 1) {
+        throw std::runtime_error("Error setting TLS IP for certificate validation");
+      }
+#else
+      /* no validation for you, see https://wiki.openssl.org/index.php/Hostname_validation */
+#endif
     }
+    else {
+#if (OPENSSL_VERSION_NUMBER >= 0x1010000fL) && HAVE_SSL_SET_HOSTFLAGS // grrr libressl
+      SSL_set_hostflags(d_conn.get(), X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
+      if (SSL_set1_host(d_conn.get(), d_hostname.c_str()) != 1) {
+        throw std::runtime_error("Error setting TLS hostname for certificate validation");
+      }
 #elif (OPENSSL_VERSION_NUMBER >= 0x10002000L)
-    X509_VERIFY_PARAM *param = SSL_get0_param(d_conn.get());
-    /* Enable automatic hostname checks */
-    X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
-    if (X509_VERIFY_PARAM_set1_host(param, d_hostname.c_str(), d_hostname.size()) != 1) {
-      throw std::runtime_error("Error setting TLS hostname for certificate validation");
-    }
+      X509_VERIFY_PARAM *param = SSL_get0_param(d_conn.get());
+      /* Enable automatic hostname checks */
+      X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
+      if (X509_VERIFY_PARAM_set1_host(param, d_hostname.c_str(), d_hostname.size()) != 1) {
+        throw std::runtime_error("Error setting TLS hostname for certificate validation");
+      }
 #else
-    /* no hostname validation for you, see https://wiki.openssl.org/index.php/Hostname_validation */
+      /* no hostname validation for you, see https://wiki.openssl.org/index.php/Hostname_validation */
 #endif
+    }
+
     SSL_set_ex_data(d_conn.get(), s_tlsConnIndex, this);
   }
 
@@ -734,9 +749,9 @@ public:
     return std::make_unique<OpenSSLTLSConnection>(socket, timeout, d_feContext);
   }
 
-  std::unique_ptr<TLSConnection> getClientConnection(const std::string& host, int socket, const struct timeval& timeout) override
+  std::unique_ptr<TLSConnection> getClientConnection(const std::string& host, bool hostIsAddr, int socket, const struct timeval& timeout) override
   {
-    return std::make_unique<OpenSSLTLSConnection>(host, socket, timeout, d_tlsCtx);
+    return std::make_unique<OpenSSLTLSConnection>(host, hostIsAddr, socket, timeout, d_tlsCtx);
   }
 
   void rotateTicketsKey(time_t now) override
@@ -1654,7 +1669,7 @@ public:
     return entry;
   }
 
-  std::unique_ptr<TLSConnection> getClientConnection(const std::string& host, int socket, const struct timeval& timeout) override
+  std::unique_ptr<TLSConnection> getClientConnection(const std::string& host, bool, int socket, const struct timeval& timeout) override
   {
     auto creds = getPerThreadCredentials(d_contextParameters->d_validateCertificates, d_contextParameters->d_caStore);
     auto connection = std::make_unique<GnuTLSConnection>(host, socket, timeout, creds, d_priorityCache, d_validateCerts);
index 48968e41bce490130855228ef1e8679f7c41083e..92faa6909a55423fba4e74f601ecadc0874d92e6 100644 (file)
@@ -78,7 +78,7 @@ public:
   }
   virtual ~TLSCtx() {}
   virtual std::unique_ptr<TLSConnection> getConnection(int socket, const struct timeval& timeout, time_t now) = 0;
-  virtual std::unique_ptr<TLSConnection> getClientConnection(const std::string& host, int socket, const struct timeval& timeout) = 0;
+  virtual std::unique_ptr<TLSConnection> getClientConnection(const std::string& host, bool hostIsAddr, int socket, const struct timeval& timeout) = 0;
   virtual void rotateTicketsKey(time_t now) = 0;
   virtual void loadTicketsKeys(const std::string& file)
   {
@@ -233,10 +233,10 @@ class TCPIOHandler
 public:
   enum class Type : uint8_t { Client, Server };
 
-  TCPIOHandler(const std::string& host, int socket, const struct timeval& timeout, std::shared_ptr<TLSCtx> ctx, time_t now): d_socket(socket)
+  TCPIOHandler(const std::string& host, bool hostIsAddr, int socket, const struct timeval& timeout, std::shared_ptr<TLSCtx> ctx, time_t now): d_socket(socket)
   {
     if (ctx) {
-      d_conn = ctx->getClientConnection(host, d_socket, timeout);
+      d_conn = ctx->getClientConnection(host, hostIsAddr, d_socket, timeout);
     }
   }
 
index 17f0b85369e2f50f1fafbdf607a527169b1f37c4..f3372e9c388358f9350a5fe7aeeb2160d90a3606 100644 (file)
@@ -1374,7 +1374,7 @@ void AsyncWebServer::serveConnection(std::shared_ptr<Socket> client) const
       g_networkTimeoutMsec / 1000, static_cast<suseconds_t>(g_networkTimeoutMsec) % 1000 * 1000
     };
     std::shared_ptr<TLSCtx> tlsCtx{nullptr};
-    auto handler = std::make_shared<TCPIOHandler>("", client->releaseHandle(), timeout, tlsCtx, time(nullptr));
+    auto handler = std::make_shared<TCPIOHandler>("", false, client->releaseHandle(), timeout, tlsCtx, time(nullptr));
 
     PacketBuffer data;
     try {