mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-02 21:43:27 +07:00
Let CryptoAPI decide on a root missing from the OpenSSL store
Windows adds a root it trusts to its store only when CryptoAPI needs it to build a chain. The OpenSSL store is loaded from the Windows store, so OpenSSL fails with "unable to get local issuer certificate" before the CryptoAPI check runs, and Windows never gets to fetch the root. When Windows certificate verification is enabled, leave that error to the CryptoAPI check. Since OpenSSL then skips its purpose check, request the server authentication usage from CryptoAPI, and fail instead of skipping the CryptoAPI check when the leaf cannot be encoded. Refs #2596
This commit is contained in:
@@ -10736,9 +10736,12 @@ inline bool verify_cert_with_windows_schannel(
|
||||
auto store = cert_context->hCertStore;
|
||||
#endif
|
||||
|
||||
// Setup chain parameters
|
||||
// Setup chain parameters. The SSL policy does not check the key usage.
|
||||
LPSTR server_auth = const_cast<LPSTR>(szOID_PKIX_KP_SERVER_AUTH);
|
||||
CERT_CHAIN_PARA chain_para = {};
|
||||
chain_para.cbSize = sizeof(chain_para);
|
||||
chain_para.RequestedUsage.Usage.cUsageIdentifier = 1;
|
||||
chain_para.RequestedUsage.Usage.rgpszUsageIdentifier = &server_auth;
|
||||
|
||||
// Build certificate chain with revocation checking
|
||||
PCCERT_CHAIN_CONTEXT chain_context = nullptr;
|
||||
@@ -10936,6 +10939,15 @@ inline bool setup_client_tls_session(
|
||||
if (verification_status == SSLVerifierResponse::NoDecisionMade &&
|
||||
server_certificate_verification) {
|
||||
auto verify_result = get_verify_result(session);
|
||||
#if defined(CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE) && \
|
||||
defined(CPPHTTPLIB_OPENSSL_SUPPORT)
|
||||
// Windows adds a root it trusts to its store only when CryptoAPI needs it,
|
||||
// so leave a root missing from the store to the CryptoAPI check below
|
||||
if (options.windows_cert_verification &&
|
||||
verify_result == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) {
|
||||
verify_result = X509_V_OK;
|
||||
}
|
||||
#endif
|
||||
if (verify_result != 0) {
|
||||
return fail(Error::SSLServerVerification, 0,
|
||||
static_cast<uint64_t>(verify_result));
|
||||
@@ -10970,6 +10982,8 @@ inline bool setup_client_tls_session(
|
||||
session)) {
|
||||
return fail(Error::SSLServerVerification, 0, wincrypt_error);
|
||||
}
|
||||
} else {
|
||||
return fail(Error::SSLServerVerification, 0, get_error());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -13967,6 +13967,34 @@ TEST(SSLClientTest, WindowsCertificateVerification_ServerIntermediates_Online) {
|
||||
ASSERT_TRUE(res) << "Error: " << to_string(res.error())
|
||||
<< " ssl_backend_error=" << res.ssl_backend_error();
|
||||
}
|
||||
|
||||
// A root missing from the trust store is left to CryptoAPI, which must still
|
||||
// reject a chain whose root Windows does not trust either.
|
||||
TEST(SSLClientTest, WindowsCertificateVerification_UnknownIssuerRejected) {
|
||||
// Issued by the test root CA, which is not in the Windows root store
|
||||
SSLServer svr(CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE);
|
||||
ASSERT_TRUE(svr.is_valid());
|
||||
svr.Get("/", [](const Request &, Response &res) {
|
||||
res.set_content("ok", "text/plain");
|
||||
});
|
||||
|
||||
thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); });
|
||||
auto se = detail::scope_exit([&] {
|
||||
svr.stop();
|
||||
t.join();
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
});
|
||||
|
||||
svr.wait_until_ready();
|
||||
|
||||
SSLClient cli(HOST, PORT);
|
||||
// Keep the hostname check from failing first
|
||||
cli.enable_server_hostname_verification(false);
|
||||
|
||||
auto res = cli.Get("/");
|
||||
ASSERT_FALSE(res);
|
||||
EXPECT_EQ(Error::SSLServerVerification, res.error());
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(SSLClientTest, ServerCertificateVerification1_Online) {
|
||||
|
||||
Reference in New Issue
Block a user