浏览代码

Give a redirect client the CAs its client trusts

With set_ca_cert_store() after load_ca_cert_store(), the redirect client
trusted the earlier PEM too, though the store had replaced it, and of
several load_ca_cert_store() calls it kept only the last. Take the CAs
from the context when a store was set, and from every PEM loaded
otherwise.
yhirose 2 天之前
父节点
当前提交
489fe8e504
共有 2 个文件被更改,包括 49 次插入 和 6 次删除
  1. 10 4
      httplib.h
  2. 39 2
      test/test.cc

+ 10 - 4
httplib.h

@@ -16168,17 +16168,19 @@ inline bool ClientImpl::create_redirect_client(
     redirect_client.system_ca_mode_ = system_ca_mode_;
 
     // Transfer CA certificate to redirect client
-    redirect_client.load_ca_cert_store(ca_cert_pem_.c_str(),
-                                       ca_cert_pem_.size());
     redirect_client.set_ca_cert_path(ca_cert_file_path_, ca_cert_dir_path_);
 
     if (is_ssl()) {
       auto &self = static_cast<SSLClient &>(*this);
       // A store given to set_ca_cert_store() lives only in this client's
-      // context, so pass on the CA certificates the context holds.
+      // context, which then holds all that is trusted.
       if (self.ca_cert_store_set_) {
         auto pem = detail::ca_certs_to_pem(self.ctx_);
         redirect_client.load_ca_cert_store(pem.data(), pem.size());
+        redirect_client.ca_cert_store_set_ = true;
+      } else {
+        redirect_client.load_ca_cert_store(ca_cert_pem_.c_str(),
+                                           ca_cert_pem_.size());
       }
       redirect_client.set_session_verifier(self.session_verifier_);
       if (self.server_certificate_verifier_) {
@@ -19051,7 +19053,11 @@ inline void SSLClient::enable_windows_certificate_verification(bool enabled) {
 inline void SSLClient::load_ca_cert_store(const char *ca_cert,
                                           std::size_t size) {
   if (ctx_ && ca_cert && size > 0) {
-    ca_cert_pem_.assign(ca_cert, size); // Store for redirect transfer
+    // Every PEM loaded, for redirect transfer
+    if (!ca_cert_pem_.empty() && ca_cert_pem_.back() != '\n') {
+      ca_cert_pem_ += '\n';
+    }
+    ca_cert_pem_.append(ca_cert, size);
     tls::load_ca_pem(ctx_, ca_cert, size);
   }
 }

+ 39 - 2
test/test.cc

@@ -17226,7 +17226,8 @@ TEST(SSLClientRedirectTest, CertFile) {
 // another port, so the client follows it with a new client.
 static Result GetThroughTlsRedirectToDifferentPort(
     const std::function<void(SSLClient &)> &setup,
-    const char *target_cert = SERVER_CERT2_FILE) {
+    const char *target_cert = SERVER_CERT2_FILE,
+    const char *target_key = SERVER_PRIVATE_KEY_FILE) {
   auto ssl_port = PORT + 1;
 
   SSLServer ssl_svr1(SERVER_CERT2_FILE, SERVER_PRIVATE_KEY_FILE);
@@ -17235,7 +17236,7 @@ static Result GetThroughTlsRedirectToDifferentPort(
                      "/index");
   });
 
-  SSLServer ssl_svr2(target_cert, SERVER_PRIVATE_KEY_FILE);
+  SSLServer ssl_svr2(target_cert, target_key);
   ssl_svr2.Get("/index", [&](const Request &, Response &res) {
     res.set_content("test", "text/plain");
   });
@@ -17299,6 +17300,42 @@ TEST(SSLClientRedirectTest, KeepCaCertStore) {
   ASSERT_TRUE(res) << "Error: " << to_string(res.error());
   EXPECT_EQ(StatusCode::OK_200, res->status);
 }
+
+// The target's certificate is signed by the root CA, which the first server's
+// is not. Its name is not checked, as it carries none of 127.0.0.1.
+TEST(SSLClientRedirectTest, KeepEveryCaCertPem) {
+  auto res = GetThroughTlsRedirectToDifferentPort(
+      [](SSLClient &cli) {
+        std::string root_ca, cert;
+        read_file(CLIENT_CA_CERT_FILE, root_ca);
+        read_file(SERVER_CERT2_FILE, cert);
+        cli.load_ca_cert_store(root_ca.data(), root_ca.size());
+        cli.load_ca_cert_store(cert.data(), cert.size());
+        cli.enable_server_hostname_verification(false);
+      },
+      CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE);
+  ASSERT_TRUE(res) << "Error: " << to_string(res.error());
+  EXPECT_EQ(StatusCode::OK_200, res->status);
+}
+
+#ifndef CPPHTTPLIB_WOLFSSL_SUPPORT
+// set_ca_cert_store() replaces the root CA loaded before it, so the target is
+// not trusted after the redirect either. (wolfSSL adds the store instead.)
+TEST(SSLClientRedirectTest, CaCertStoreReplacesEarlierPem) {
+  auto res = GetThroughTlsRedirectToDifferentPort(
+      [](SSLClient &cli) {
+        std::string root_ca, cert;
+        read_file(CLIENT_CA_CERT_FILE, root_ca);
+        read_file(SERVER_CERT2_FILE, cert);
+        cli.load_ca_cert_store(root_ca.data(), root_ca.size());
+        cli.set_ca_cert_store(tls::create_ca_store(cert.c_str(), cert.size()));
+        cli.enable_server_hostname_verification(false);
+      },
+      CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE);
+  ASSERT_FALSE(res);
+  EXPECT_EQ(Error::SSLServerVerification, res.error());
+}
+#endif
 #endif
 
 #ifdef CPPHTTPLIB_SSL_ENABLED