Fix set_ca_cert_store() breaking CA exclusivity and redirect CA transfer

Since the TLS abstraction layer was introduced, SSLClient::set_ca_cert_store()
handed the store to the TLS context without leaving any trace on the client.
As a result:

- load_certs() merged system CA certs into the user-provided store,
  silently broadening the trust set (a custom store used to suppress
  system CA loading)
- Client::load_ca_cert_store() went through the native store path,
  bypassing the PEM retention used for redirect transfer, so CA certs
  were not carried over to clients created for HTTPS redirects
- The Windows Schannel verification skip for custom CA certs did not
  trigger

Track custom store assignment with a flag checked by load_certs() and
the Schannel path, and route Client::load_ca_cert_store() through the
PEM-based SSLClient path so the CA data survives redirects.
This commit is contained in:
yhirose committed 2026-06-09 21:03:19 -04:00
1 parent 78ff94e604
commit e7e7bf7b44
2 files changed
+116 -3

No files matched your search

+13 -3
View File
@@ -2804,6 +2804,11 @@ private:
std::mutex ctx_mutex_;
std::once_flag initialize_cert_;
// Tracks whether a custom CA store was applied via set_ca_cert_store(),
// since the store handle itself is owned by ctx_ and leaves no other trace.
// Used to keep custom CA configuration exclusive with system CA loading.
bool ca_cert_store_set_ = false;
long verify_result_ = 0;
std::function<SSLVerifierResponse(tls::session_t)> session_verifier_;
@@ -16092,6 +16097,7 @@ inline void SSLClient::set_ca_cert_store(tls::ca_store_t ca_cert_store) {
if (ca_cert_store && ctx_) {
// set_ca_store takes ownership of ca_cert_store
tls::set_ca_store(ctx_, ca_cert_store);
ca_cert_store_set_ = true;
} else if (ca_cert_store) {
tls::free_ca_store(ca_cert_store);
}
@@ -16138,7 +16144,7 @@ inline bool SSLClient::load_certs() {
last_backend_error_ = tls::get_error();
ret = false;
}
} else if (ca_cert_pem_.empty()) {
} else if (ca_cert_pem_.empty() && !ca_cert_store_set_) {
if (!tls::load_system_certs(ctx_)) {
last_backend_error_ = tls::get_error();
}
@@ -16273,7 +16279,8 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
// Skip when a custom CA cert is specified, as the Windows certificate
// store would not know about user-provided CA certificates.
if (enable_windows_cert_verification_ && ca_cert_file_path_.empty() &&
ca_cert_dir_path_.empty() && ca_cert_pem_.empty()) {
ca_cert_dir_path_.empty() && ca_cert_pem_.empty() &&
!ca_cert_store_set_) {
std::vector<unsigned char> der;
if (get_cert_der(server_cert, der)) {
uint64_t wincrypt_error = 0;
@@ -16335,7 +16342,10 @@ inline void Client::set_ca_cert_store(tls::ca_store_t ca_cert_store) {
}
inline void Client::load_ca_cert_store(const char *ca_cert, std::size_t size) {
set_ca_cert_store(tls::create_ca_store(ca_cert, size));
if (is_ssl_) {
// Use the PEM-based path so the CA data is retained for redirect transfer
static_cast<SSLClient &>(*cli_).load_ca_cert_store(ca_cert, size);
}
}
inline void