mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-19 11:19:36 +00:00
Carry the server certificate verifier over to the redirect client (#2634)
* Carry the server certificate verifier over to the redirect client create_redirect_client() now copies the CA store and the session verifier to the SSLClient it builds for a cross-host https redirect (#2632), but a verifier given to set_server_certificate_verifier() was still dropped: the redirect client's ctx never had the verify callback wired in, so a pinning verifier never ran on the host the server redirects to. SSLClient now keeps its own copy of the verifier, since ctx_ cannot report it back and the backends hold it in a slot shared by every client, and installs it on the redirect client. The copy also replaces the Windows-only server_certificate_verifier_set_ flag. SSLClientRedirectTest.ServerCertificateVerifier fails before this change and passes after (OpenSSL 3, Mbed TLS 2.28, wolfSSL 5.6). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGAYpQgXt95LdDQcJYgjB8 * Clarify the server_certificate_verifier_ comment The backends keep the verify callback in a per-thread slot, not one shared by every client. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGAYpQgXt95LdDQcJYgjB8 --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
1 parent
d78a3cbb82
commit
9f6dcf1230
2 files changed
+69
-8
No files matched your search
@@ -3454,11 +3454,13 @@ private:
|
|||||||
|
|
||||||
std::function<SSLVerifierResponse(tls::session_t)> session_verifier_;
|
std::function<SSLVerifierResponse(tls::session_t)> session_verifier_;
|
||||||
|
|
||||||
|
// The verifier given to set_server_certificate_verifier(), kept for the
|
||||||
|
// redirect client: ctx_ cannot report it back, and the backends keep it in
|
||||||
|
// a per-thread slot that any other client may overwrite.
|
||||||
|
tls::VerifyCallback server_certificate_verifier_;
|
||||||
|
|
||||||
#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
|
#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
|
||||||
bool enable_windows_cert_verification_ = true;
|
bool enable_windows_cert_verification_ = true;
|
||||||
// Like ca_cert_store_set_, tracks what ctx_ cannot report back: whether
|
|
||||||
// set_server_certificate_verifier() installed a verifier.
|
|
||||||
bool server_certificate_verifier_set_ = false;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
friend class ClientImpl;
|
friend class ClientImpl;
|
||||||
@@ -15944,6 +15946,11 @@ inline bool ClientImpl::create_redirect_client(
|
|||||||
}
|
}
|
||||||
|
|
||||||
redirect_client.session_verifier_ = self.session_verifier_;
|
redirect_client.session_verifier_ = self.session_verifier_;
|
||||||
|
|
||||||
|
if (self.server_certificate_verifier_) {
|
||||||
|
redirect_client.set_server_certificate_verifier(
|
||||||
|
self.server_certificate_verifier_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client certificates are set through constructor for SSLClient
|
// Client certificates are set through constructor for SSLClient
|
||||||
@@ -18751,10 +18758,8 @@ inline void SSLClient::set_ca_cert_store(tls::ca_store_t ca_cert_store) {
|
|||||||
inline void
|
inline void
|
||||||
SSLClient::set_server_certificate_verifier(tls::VerifyCallback verifier) {
|
SSLClient::set_server_certificate_verifier(tls::VerifyCallback verifier) {
|
||||||
if (!ctx_) { return; }
|
if (!ctx_) { return; }
|
||||||
#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
|
server_certificate_verifier_ = verifier;
|
||||||
server_certificate_verifier_set_ = static_cast<bool>(verifier);
|
tls::set_verify_callback(ctx_, std::move(verifier));
|
||||||
#endif
|
|
||||||
tls::set_verify_callback(ctx_, verifier);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void SSLClient::set_session_verifier(
|
inline void SSLClient::set_session_verifier(
|
||||||
@@ -18817,7 +18822,8 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
|
|||||||
ca_cert_dir_path_.empty() && ca_cert_pem_.empty() && !ca_cert_store_set_;
|
ca_cert_dir_path_.empty() && ca_cert_pem_.empty() && !ca_cert_store_set_;
|
||||||
// Only a verifier set through set_server_certificate_verifier() is seen
|
// Only a verifier set through set_server_certificate_verifier() is seen
|
||||||
// here, not one installed with tls::set_verify_callback() directly.
|
// here, not one installed with tls::set_verify_callback() directly.
|
||||||
options.server_certificate_verifier_set = server_certificate_verifier_set_;
|
options.server_certificate_verifier_set =
|
||||||
|
static_cast<bool>(server_certificate_verifier_);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
tls::session_t session = nullptr;
|
tls::session_t session = nullptr;
|
||||||
|
|||||||
@@ -16016,6 +16016,61 @@ TEST(SSLClientRedirectTest, SessionVerifier) {
|
|||||||
EXPECT_EQ(Error::SSLServerVerification, res.error());
|
EXPECT_EQ(Error::SSLServerVerification, res.error());
|
||||||
EXPECT_EQ(2, sessions.load());
|
EXPECT_EQ(2, sessions.load());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// So must the verifier given to set_server_certificate_verifier().
|
||||||
|
TEST(SSLClientRedirectTest, ServerCertificateVerifier) {
|
||||||
|
auto ssl_port = PORT + 1;
|
||||||
|
std::atomic<bool> redirected{false};
|
||||||
|
|
||||||
|
SSLServer ssl_svr1(SERVER_CERT2_FILE, SERVER_PRIVATE_KEY_FILE);
|
||||||
|
ASSERT_TRUE(ssl_svr1.is_valid());
|
||||||
|
ssl_svr1.Get("/index", [&](const Request &, Response &res) {
|
||||||
|
redirected = true;
|
||||||
|
res.set_redirect("https://127.0.0.1:" + std::to_string(ssl_port) +
|
||||||
|
"/index");
|
||||||
|
});
|
||||||
|
|
||||||
|
SSLServer ssl_svr2(SERVER_CERT2_FILE, SERVER_PRIVATE_KEY_FILE);
|
||||||
|
ASSERT_TRUE(ssl_svr2.is_valid());
|
||||||
|
ssl_svr2.Get("/index", [&](const Request &, Response &res) {
|
||||||
|
res.set_content("test", "text/plain");
|
||||||
|
});
|
||||||
|
|
||||||
|
thread t = thread([&]() { ASSERT_TRUE(ssl_svr1.listen("127.0.0.1", PORT)); });
|
||||||
|
thread t2 =
|
||||||
|
thread([&]() { ASSERT_TRUE(ssl_svr2.listen("127.0.0.1", ssl_port)); });
|
||||||
|
auto se = detail::scope_exit([&] {
|
||||||
|
ssl_svr2.stop();
|
||||||
|
ssl_svr1.stop();
|
||||||
|
t2.join();
|
||||||
|
t.join();
|
||||||
|
ASSERT_FALSE(ssl_svr1.is_running());
|
||||||
|
});
|
||||||
|
|
||||||
|
ssl_svr1.wait_until_ready();
|
||||||
|
ssl_svr2.wait_until_ready();
|
||||||
|
|
||||||
|
SSLClient cli("127.0.0.1", PORT);
|
||||||
|
std::string cert;
|
||||||
|
read_file(SERVER_CERT2_FILE, cert);
|
||||||
|
cli.load_ca_cert_store(cert.c_str(), cert.size());
|
||||||
|
cli.enable_server_certificate_verification(true);
|
||||||
|
cli.set_follow_location(true);
|
||||||
|
cli.set_connection_timeout(30);
|
||||||
|
|
||||||
|
// Accept the host that redirects only; the verifier runs once per
|
||||||
|
// certificate in the chain, so count the calls made after the redirect.
|
||||||
|
std::atomic<int> calls_after_redirect{0};
|
||||||
|
cli.set_server_certificate_verifier([&](const tls::VerifyContext &) {
|
||||||
|
if (!redirected) { return true; }
|
||||||
|
calls_after_redirect++;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
auto res = cli.Get("/index");
|
||||||
|
ASSERT_FALSE(res);
|
||||||
|
EXPECT_LT(0, calls_after_redirect.load());
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CPPHTTPLIB_SSL_ENABLED
|
#ifdef CPPHTTPLIB_SSL_ENABLED
|
||||||
|
|||||||
Reference in new issue
Block a user