]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Basic infra for client cert
authorOtto Moerbeek <otto.moerbeek@open-xchange.com>
Wed, 4 Feb 2026 13:42:20 +0000 (14:42 +0100)
committerOtto Moerbeek <otto.moerbeek@open-xchange.com>
Mon, 16 Feb 2026 07:51:41 +0000 (08:51 +0100)
Signed-off-by: Otto Moerbeek <otto.moerbeek@open-xchange.com>
pdns/libssl.cc
pdns/libssl.hh
pdns/recursordist/rec-rust-lib/rust-bridge-in.rs
pdns/recursordist/rec-rust-lib/rust/src/bridge.rs
pdns/recursordist/rec-tcpout.cc
pdns/tcpiohandler.cc
pdns/tcpiohandler.hh

index 6058e8269058ae37887ca68ea59d6715dc60ab41..f8c6e2e6363d668bc2804480b6230434da83883c 100644 (file)
@@ -1081,7 +1081,7 @@ static void mergeNewCertificateAndKey(pdns::libssl::ServerContext& serverContext
   }
 }
 
-void libssl_setup_context_no_sni(std::unique_ptr<SSL_CTX, decltype(&SSL_CTX_free)>& ctx, const TLSCertKeyPair& pair, std::vector<int>& keyTypes)
+void libssl_setup_context_no_sni(SSL_CTX* ctx, const TLSCertKeyPair& pair, std::vector<int>& keyTypes)
 {
   if (!pair.d_key) {
 #if defined(HAVE_SSL_CTX_USE_CERT_AND_KEY)
@@ -1126,7 +1126,7 @@ void libssl_setup_context_no_sni(std::unique_ptr<SSL_CTX, decltype(&SSL_CTX_free
     auto cert = std::unique_ptr<X509, void(*)(X509*)>(certptr, X509_free);
     auto ca = std::unique_ptr<STACK_OF(X509), void(*)(STACK_OF(X509)*)>(captr, [](STACK_OF(X509)* st){ sk_X509_free(st); });
 
-    if (SSL_CTX_use_cert_and_key(ctx.get(), cert.get(), key.get(), ca.get(), 1) != 1) {
+    if (SSL_CTX_use_cert_and_key(ctx, cert.get(), key.get(), ca.get(), 1) != 1) {
       ERR_print_errors_fp(stderr);
       throw std::runtime_error("An error occurred while trying to load the TLS certificate and key from PKCS12 file " + pair.d_cert);
     }
@@ -1134,17 +1134,17 @@ void libssl_setup_context_no_sni(std::unique_ptr<SSL_CTX, decltype(&SSL_CTX_free
     throw std::runtime_error("PKCS12 files are not supported by your openssl version");
 #endif /* HAVE_SSL_CTX_USE_CERT_AND_KEY */
   } else {
-    if (SSL_CTX_use_certificate_chain_file(ctx.get(), pair.d_cert.c_str()) != 1) {
+    if (SSL_CTX_use_certificate_chain_file(ctx, pair.d_cert.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.d_cert);
     }
-    if (SSL_CTX_use_PrivateKey_file(ctx.get(), pair.d_key->c_str(), SSL_FILETYPE_PEM) != 1) {
+    if (SSL_CTX_use_PrivateKey_file(ctx, pair.d_key->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.d_key.value());
     }
   }
 
-  if (SSL_CTX_check_private_key(ctx.get()) != 1) {
+  if (SSL_CTX_check_private_key(ctx) != 1) {
     ERR_print_errors_fp(stderr);
     throw std::runtime_error("The key from '" + pair.d_key.value() + "' does not match the certificate from '" + pair.d_cert + "'");
   }
@@ -1165,7 +1165,7 @@ std::pair<std::unique_ptr<SSL_CTX, decltype(&SSL_CTX_free)>, std::vector<std::st
   std::vector<int> keyTypes;
   /* load certificate and private key */
   for (const auto& pair : config.d_certKeyPairs) {
-    libssl_setup_context_no_sni(ctx, pair, keyTypes);
+    libssl_setup_context_no_sni(ctx.get(), pair, keyTypes);
   }
 
 #ifndef DISABLE_OCSP_STAPLING
index f4a7bc0e12c3836acb41eb336bd7acf836d48dbb..3a0a3c0df834dc570a5e886b0f66153ddee0f6cc 100644 (file)
@@ -167,7 +167,7 @@ public:
 }
 
 /* add a keypair to an SSL context */
-void libssl_setup_context_no_sni(std::unique_ptr<SSL_CTX, decltype(&SSL_CTX_free)>& ctx, const TLSCertKeyPair& pair, std::vector<int>& keyTypes);
+void libssl_setup_context_no_sni(SSL_CTX* ctx, const TLSCertKeyPair& pair, std::vector<int>& keyTypes);
 
 /* return the created context, and a list of warning messages for issues not severe enough
    to trigger raising an exception, like failing to load an OCSP response file */
index ed9e0e5cd45c45d1100dcd24bb9e6fabee0e247f..b31b3316f3a3e88f76ac28fa7ac8079c7a85522b 100644 (file)
@@ -348,6 +348,13 @@ struct OutgoingTLSConfiguration {
     ciphers: String,
     #[serde(default, skip_serializing_if = "crate::is_default")]
     ciphers_tls_13: String,
+    #[serde(default, skip_serializing_if = "crate::is_default")]
+    // Fields below added in 5.5.0
+    client_certificate: String,
+    #[serde(default, skip_serializing_if = "crate::is_default")]
+    client_certificate_key: String,
+    #[serde(default, skip_serializing_if = "crate::is_default")]
+    client_certificate_password: String,
 }
 
 #[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
index 524b7a2c1930ca7afb03056f7e4807592a6fb73f..036aa93156a689293b8cc1d5ca3c67591bd082d5 100644 (file)
@@ -818,7 +818,12 @@ impl OutgoingTLSConfiguration {
         inserts(&mut map, "subject_name", &self.subject_name);
         inserts(&mut map, "subject_address", &self.subject_address);
         inserts(&mut map, "ciphers", &self.ciphers);
-        inserts(&mut map, "ciphers_tls", &self.ciphers_tls_13);
+        inserts(&mut map, "ciphers_tls_13", &self.ciphers_tls_13);
+
+        inserts(&mut map, "client_certificate", &self.client_certificate);
+        inserts(&mut map, "client_certificate_key", &self.client_certificate_key);
+        inserts(&mut map, "client_certificate_password", &self.client_certificate_password);
+
         serde_yaml::Value::Mapping(map)
     }
 
index 762178478dcdad3cd0fd4a392a113163849491fe..20bbd8627961eb612d0ae031eac67f6289bc62a7 100644 (file)
@@ -142,6 +142,10 @@ std::shared_ptr<TLSCtx> TCPOutConnectionManager::getTLSContext(const std::string
       verboseLogging = config->verbose_logging;
       tlsParams.d_ciphers = std::string(config->ciphers);
       tlsParams.d_ciphers13 = std::string(config->ciphers_tls_13);
+
+      tlsParams.d_client_certificate = std::string(config->client_certificate);
+      tlsParams.d_client_certificate_key = std::string(config->client_certificate_key);
+      tlsParams.d_client_certificate_password = std::string(config->client_certificate_password);
     }
   }
   return ::getTLSContext(tlsParams);
index dfd770b83c3c9bdee3b925ad0e3cd082d7e4b1e0..b9ef3ac65e94a0fab7a7b72ffb14ac36ecb3ba9d 100644 (file)
@@ -857,6 +857,20 @@ public:
 
     libssl_set_alpn_protos(d_tlsCtx.get(), getALPNVector(params.d_alpn, true));
 
+    if (!params.d_client_certificate.empty()) {
+      std::optional<std::string> key = std::nullopt;
+      if (!params.d_client_certificate_key.empty()) {
+        key = params.d_client_certificate_key;
+      }
+      std::optional<std::string> password = std::nullopt;
+      if (!params.d_client_certificate_password.empty()) {
+        password = params.d_client_certificate_password;
+      }
+      TLSCertKeyPair pair{params.d_client_certificate, key, password};
+      std::vector<int> keyTypes;
+      libssl_setup_context_no_sni(d_tlsCtx.get(), pair, keyTypes);
+    }
+
 #ifdef SSL_MODE_RELEASE_BUFFERS
     if (params.d_releaseBuffers) {
       SSL_CTX_set_mode(d_tlsCtx.get(), SSL_MODE_RELEASE_BUFFERS);
index f031987a47c1a67052805960ea31da4928cfa71e..a3f7b52d0bf1762601e390ec56b5c51a68eb42dd 100644 (file)
@@ -598,6 +598,9 @@ struct TLSContextParameters
   std::string d_ciphers13;
   std::string d_caStore;
   std::string d_keyLogFile;
+  std::string d_client_certificate;
+  std::string d_client_certificate_key;
+  std::string d_client_certificate_password;
   TLSFrontend::ALPN d_alpn{TLSFrontend::ALPN::Unset};
   bool d_validateCertificates{true};
   bool d_releaseBuffers{true};