Skip to content

Default using Windows Schannel for SSL/TLS on Windows #2116

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 89 additions & 34 deletions httplib.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@ using ssize_t = long;
#endif // NOMINMAX

#include <io.h>
#if defined(CPPHTTPLIB_OPENSSL_SUPPORT)
#define CERT_CHAIN_PARA_HAS_EXTRA_FIELDS
#endif
#include <winsock2.h>
#include <ws2tcpip.h>

Expand Down Expand Up @@ -5580,34 +5583,7 @@ inline bool is_ssl_peer_could_be_closed(SSL *ssl, socket_t sock) {
SSL_get_error(ssl, 0) == SSL_ERROR_ZERO_RETURN;
}

#ifdef _WIN32
// NOTE: This code came up with the following stackoverflow post:
// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
inline bool load_system_certs_on_windows(X509_STORE *store) {
auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT");
if (!hStore) { return false; }

auto result = false;
PCCERT_CONTEXT pContext = NULL;
while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
nullptr) {
auto encoded_cert =
static_cast<const unsigned char *>(pContext->pbCertEncoded);

auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
if (x509) {
X509_STORE_add_cert(store, x509);
X509_free(x509);
result = true;
}
}

CertFreeCertificateContext(pContext);
CertCloseStore(hStore, 0);

return result;
}
#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
#if defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
#if TARGET_OS_OSX
template <typename T>
using CFObjectPtr =
Expand Down Expand Up @@ -5697,7 +5673,7 @@ inline bool load_system_certs_on_macos(X509_STORE *store) {
return result;
}
#endif // TARGET_OS_OSX
#endif // _WIN32
#endif // CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN && __APPLE__
#endif // CPPHTTPLIB_OPENSSL_SUPPORT

#ifdef _WIN32
Expand Down Expand Up @@ -9643,14 +9619,11 @@ inline bool SSLClient::load_certs() {
}
} else {
auto loaded = false;
#ifdef _WIN32
loaded =
detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
#if defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
#if TARGET_OS_OSX
loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));
#endif // TARGET_OS_OSX
#endif // _WIN32
#endif // CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN && __APPLE__
if (!loaded) { SSL_CTX_set_default_verify_paths(ctx_); }
}
});
Expand Down Expand Up @@ -9690,12 +9663,14 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
}

if (verification_status == SSLVerifierResponse::NoDecisionMade) {
#ifndef _WIN32
verify_result_ = SSL_get_verify_result(ssl2);

if (verify_result_ != X509_V_OK) {
error = Error::SSLServerVerification;
return false;
}
#endif

auto server_cert = SSL_get1_peer_certificate(ssl2);
auto se = detail::scope_exit([&] { X509_free(server_cert); });
Expand All @@ -9705,12 +9680,92 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
return false;
}

#ifdef _WIN32
// Convert OpenSSL certificate to DER format
auto der_cert =
std::vector<unsigned char>(i2d_X509(server_cert, nullptr));
auto der_cert_data = der_cert.data();
if (i2d_X509(server_cert, &der_cert_data) < 0) {
error = Error::SSLServerVerification;
return false;
}

// Create a certificate context from the DER-encoded certificate
auto cert_context = CertCreateCertificateContext(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, der_cert.data(),
static_cast<DWORD>(der_cert.size()));

if (cert_context == nullptr) {
error = Error::SSLServerVerification;
return false;
}

auto chain_para = CERT_CHAIN_PARA{};
chain_para.cbSize = sizeof(chain_para);
chain_para.dwUrlRetrievalTimeout = 10 * 1000;

auto chain_context = PCCERT_CHAIN_CONTEXT{};
auto result = CertGetCertificateChain(
nullptr, cert_context, nullptr, cert_context->hCertStore,
&chain_para,
CERT_CHAIN_CACHE_END_CERT |
CERT_CHAIN_REVOCATION_CHECK_END_CERT |
CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT,
nullptr, &chain_context);

CertFreeCertificateContext(cert_context);

if (!result || chain_context == nullptr) {
error = Error::SSLServerVerification;
return false;
}

// Verify chain policy
auto extra_policy_para = SSL_EXTRA_CERT_CHAIN_POLICY_PARA{};
extra_policy_para.cbSize = sizeof(extra_policy_para);
extra_policy_para.dwAuthType = AUTHTYPE_SERVER;
auto whost = detail::u8string_to_wstring(host_.c_str());
if (server_hostname_verification_) {
extra_policy_para.pwszServerName =
const_cast<wchar_t *>(whost.c_str());
}

auto policy_para = CERT_CHAIN_POLICY_PARA{};
policy_para.cbSize = sizeof(policy_para);
policy_para.dwFlags =
CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS;
policy_para.pvExtraPolicyPara = &extra_policy_para;

auto policy_status = CERT_CHAIN_POLICY_STATUS{};
policy_status.cbSize = sizeof(policy_status);

result = CertVerifyCertificateChainPolicy(
CERT_CHAIN_POLICY_SSL, chain_context, &policy_para,
&policy_status);

CertFreeCertificateChain(chain_context);

if (!result) {
error = Error::SSLServerVerification;
return false;
}

if (policy_status.dwError != 0) {
if (policy_status.dwError == CERT_E_CN_NO_MATCH) {
error = Error::SSLServerHostnameVerification;
} else {
error = Error::SSLServerVerification;
}
return false;
}
#else
if (server_hostname_verification_) {
if (!verify_host(server_cert)) {
error = Error::SSLServerHostnameVerification;
return false;
}
}
#endif
}
}

Expand Down
Loading