Skip to content

Commit a7b04f4

Browse files
solarispikaBLesnau
authored andcommitted
Default using Windows Schannel for SSL/TLS on Windows
Follow https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certgetcertificatechain for related flags. Closes #1978
1 parent 27ee115 commit a7b04f4

File tree

2 files changed

+98
-0
lines changed

2 files changed

+98
-0
lines changed

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
* HTTPLIB_REQUIRE_ZSTD (default off)
1212
* HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN (default on)
1313
* HTTPLIB_USE_NON_BLOCKING_GETADDRINFO (default on)
14+
* HTTPLIB_USE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE (default on)
1415
* HTTPLIB_COMPILE (default off)
1516
* HTTPLIB_INSTALL (default on)
1617
* HTTPLIB_SHARED (default off) builds as a shared library (if HTTPLIB_COMPILE is ON)
@@ -110,6 +111,7 @@ option(HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN "Enable feature to load system cer
110111
option(HTTPLIB_USE_NON_BLOCKING_GETADDRINFO "Enables the non-blocking alternatives for getaddrinfo." ON)
111112
option(HTTPLIB_REQUIRE_ZSTD "Requires ZSTD to be found & linked, or fails build." OFF)
112113
option(HTTPLIB_USE_ZSTD_IF_AVAILABLE "Uses ZSTD (if available) to enable zstd support." ON)
114+
option(HTTPLIB_USE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE "Enable automatic root certificates update on Windows." ON)
113115
# Defaults to static library but respects standard BUILD_SHARED_LIBS if set
114116
include(CMakeDependentOption)
115117
cmake_dependent_option(HTTPLIB_SHARED "Build the library as a shared library instead of static. Has no effect if using header-only."
@@ -296,6 +298,7 @@ target_compile_definitions(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
296298
$<$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>:CPPHTTPLIB_OPENSSL_SUPPORT>
297299
$<$<AND:$<PLATFORM_ID:Darwin>,$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>,$<BOOL:${HTTPLIB_IS_USING_CERTS_FROM_MACOSX_KEYCHAIN}>>:CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN>
298300
$<$<BOOL:${HTTPLIB_USE_NON_BLOCKING_GETADDRINFO}>:CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO>
301+
$<$<AND:$<PLATFORM_ID:Windows>,$<NOT:$<BOOL:${HTTPLIB_USE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE}>>>:CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE>
299302
)
300303

301304
# CMake configuration files installation directory

httplib.h

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,10 @@ using ssize_t = __int64;
221221
#endif // NOMINMAX
222222

223223
#include <io.h>
224+
#if defined(CPPHTTPLIB_OPENSSL_SUPPORT) && \
225+
!defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
226+
#define CERT_CHAIN_PARA_HAS_EXTRA_FIELDS
227+
#endif
224228
#include <winsock2.h>
225229
#include <ws2tcpip.h>
226230

@@ -6309,6 +6313,7 @@ inline bool is_ssl_peer_could_be_closed(SSL *ssl, socket_t sock) {
63096313
}
63106314

63116315
#ifdef _WIN32
6316+
#ifdef CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
63126317
// NOTE: This code came up with the following stackoverflow post:
63136318
// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
63146319
inline bool load_system_certs_on_windows(X509_STORE *store) {
@@ -6335,6 +6340,7 @@ inline bool load_system_certs_on_windows(X509_STORE *store) {
63356340

63366341
return result;
63376342
}
6343+
#endif // CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
63386344
#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && TARGET_OS_MAC
63396345
template <typename T>
63406346
using CFObjectPtr =
@@ -11343,8 +11349,10 @@ inline bool SSLClient::load_certs() {
1134311349
} else {
1134411350
auto loaded = false;
1134511351
#ifdef _WIN32
11352+
#ifdef CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
1134611353
loaded =
1134711354
detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
11355+
#endif // CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
1134811356
#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && TARGET_OS_MAC
1134911357
loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));
1135011358
#endif // _WIN32
@@ -11391,6 +11399,8 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
1139111399
}
1139211400

1139311401
if (verification_status == SSLVerifierResponse::NoDecisionMade) {
11402+
#if !defined(_WIN32) || \
11403+
defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
1139411404
verify_result_ = SSL_get_verify_result(ssl2);
1139511405

1139611406
if (verify_result_ != X509_V_OK) {
@@ -11399,6 +11409,8 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
1139911409
output_error_log(error, nullptr);
1140011410
return false;
1140111411
}
11412+
#endif // not _WIN32 ||
11413+
// CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
1140211414

1140311415
auto server_cert = SSL_get1_peer_certificate(ssl2);
1140411416
auto se = detail::scope_exit([&] { X509_free(server_cert); });
@@ -11410,6 +11422,8 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
1141011422
return false;
1141111423
}
1141211424

11425+
#if !defined(_WIN32) || \
11426+
defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
1141311427
if (server_hostname_verification_) {
1141411428
if (!verify_host(server_cert)) {
1141511429
last_openssl_error_ = X509_V_ERR_HOSTNAME_MISMATCH;
@@ -11418,6 +11432,87 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
1141811432
return false;
1141911433
}
1142011434
}
11435+
#else // _WIN32 && !
11436+
// CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE Convert
11437+
// OpenSSL certificate to DER format
11438+
auto der_cert =
11439+
std::vector<unsigned char>(i2d_X509(server_cert, nullptr));
11440+
auto der_cert_data = der_cert.data();
11441+
if (i2d_X509(server_cert, &der_cert_data) < 0) {
11442+
error = Error::SSLServerVerification;
11443+
return false;
11444+
}
11445+
11446+
// Create a certificate context from the DER-encoded certificate
11447+
auto cert_context = CertCreateCertificateContext(
11448+
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, der_cert.data(),
11449+
static_cast<DWORD>(der_cert.size()));
11450+
11451+
if (cert_context == nullptr) {
11452+
error = Error::SSLServerVerification;
11453+
return false;
11454+
}
11455+
11456+
auto chain_para = CERT_CHAIN_PARA{};
11457+
chain_para.cbSize = sizeof(chain_para);
11458+
chain_para.dwUrlRetrievalTimeout = 10 * 1000;
11459+
11460+
auto chain_context = PCCERT_CHAIN_CONTEXT{};
11461+
auto result = CertGetCertificateChain(
11462+
nullptr, cert_context, nullptr, cert_context->hCertStore,
11463+
&chain_para,
11464+
CERT_CHAIN_CACHE_END_CERT |
11465+
CERT_CHAIN_REVOCATION_CHECK_END_CERT |
11466+
CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT,
11467+
nullptr, &chain_context);
11468+
11469+
CertFreeCertificateContext(cert_context);
11470+
11471+
if (!result || chain_context == nullptr) {
11472+
error = Error::SSLServerVerification;
11473+
return false;
11474+
}
11475+
11476+
// Verify chain policy
11477+
auto extra_policy_para = SSL_EXTRA_CERT_CHAIN_POLICY_PARA{};
11478+
extra_policy_para.cbSize = sizeof(extra_policy_para);
11479+
extra_policy_para.dwAuthType = AUTHTYPE_SERVER;
11480+
auto whost = detail::u8string_to_wstring(host_.c_str());
11481+
if (server_hostname_verification_) {
11482+
extra_policy_para.pwszServerName =
11483+
const_cast<wchar_t *>(whost.c_str());
11484+
}
11485+
11486+
auto policy_para = CERT_CHAIN_POLICY_PARA{};
11487+
policy_para.cbSize = sizeof(policy_para);
11488+
policy_para.dwFlags =
11489+
CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS;
11490+
policy_para.pvExtraPolicyPara = &extra_policy_para;
11491+
11492+
auto policy_status = CERT_CHAIN_POLICY_STATUS{};
11493+
policy_status.cbSize = sizeof(policy_status);
11494+
11495+
result = CertVerifyCertificateChainPolicy(
11496+
CERT_CHAIN_POLICY_SSL, chain_context, &policy_para,
11497+
&policy_status);
11498+
11499+
CertFreeCertificateChain(chain_context);
11500+
11501+
if (!result) {
11502+
error = Error::SSLServerVerification;
11503+
return false;
11504+
}
11505+
11506+
if (policy_status.dwError != 0) {
11507+
if (policy_status.dwError == CERT_E_CN_NO_MATCH) {
11508+
error = Error::SSLServerHostnameVerification;
11509+
} else {
11510+
error = Error::SSLServerVerification;
11511+
}
11512+
return false;
11513+
}
11514+
#endif // not _WIN32 ||
11515+
// CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
1142111516
}
1142211517
}
1142311518

0 commit comments

Comments
 (0)