mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-11-03 13:38:36 +00:00
Compare commits
2
Commits
latest
...
39489ee376
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39489ee376 | ||
|
|
75cc18b966 |
No files matched your search
@@ -5120,6 +5120,9 @@ bool is_peer_closed(session_t session, socket_t sock);
|
|||||||
|
|
||||||
// Certificate verification
|
// Certificate verification
|
||||||
cert_t get_peer_cert(const_session_t session);
|
cert_t get_peer_cert(const_session_t session);
|
||||||
|
// The certificates the peer sent, leaf first. Free each with free_cert(), and
|
||||||
|
// do not use them after free_session(), as with get_peer_cert().
|
||||||
|
size_t get_peer_certs(const_session_t session, std::vector<cert_t> &certs);
|
||||||
void free_cert(cert_t cert);
|
void free_cert(cert_t cert);
|
||||||
bool verify_hostname(cert_t cert, const char *hostname);
|
bool verify_hostname(cert_t cert, const char *hostname);
|
||||||
uint64_t hostname_mismatch_code();
|
uint64_t hostname_mismatch_code();
|
||||||
@@ -10698,10 +10701,9 @@ inline bool match_hostname(const std::string &pattern,
|
|||||||
// Verify certificate using Windows CertGetCertificateChain API.
|
// Verify certificate using Windows CertGetCertificateChain API.
|
||||||
// This provides real-time certificate validation with Windows Update
|
// This provides real-time certificate validation with Windows Update
|
||||||
// integration, independent of the TLS backend (OpenSSL or MbedTLS).
|
// integration, independent of the TLS backend (OpenSSL or MbedTLS).
|
||||||
inline bool
|
inline bool verify_cert_with_windows_schannel(
|
||||||
verify_cert_with_windows_schannel(const std::vector<unsigned char> &der_cert,
|
const std::vector<unsigned char> &der_cert, const std::string &hostname,
|
||||||
const std::string &hostname,
|
bool verify_hostname, uint64_t &out_error, tls::const_session_t session) {
|
||||||
bool verify_hostname, uint64_t &out_error) {
|
|
||||||
if (der_cert.empty()) { return false; }
|
if (der_cert.empty()) { return false; }
|
||||||
|
|
||||||
out_error = 0;
|
out_error = 0;
|
||||||
@@ -10719,6 +10721,26 @@ verify_cert_with_windows_schannel(const std::vector<unsigned char> &der_cert,
|
|||||||
auto cert_guard =
|
auto cert_guard =
|
||||||
scope_exit([&] { CertFreeCertificateContext(cert_context); });
|
scope_exit([&] { CertFreeCertificateContext(cert_context); });
|
||||||
|
|
||||||
|
// Give CryptoAPI the certificates the server sent. Without them it follows
|
||||||
|
// the leaf's AIA URL, which may lead to an issuer under an untrusted root.
|
||||||
|
std::vector<tls::cert_t> peer_certs;
|
||||||
|
tls::get_peer_certs(session, peer_certs);
|
||||||
|
auto store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, 0, nullptr);
|
||||||
|
auto store_guard = scope_exit([&] {
|
||||||
|
for (auto cert : peer_certs) {
|
||||||
|
tls::free_cert(cert);
|
||||||
|
}
|
||||||
|
if (store) { CertCloseStore(store, 0); }
|
||||||
|
});
|
||||||
|
for (auto cert : peer_certs) {
|
||||||
|
std::vector<unsigned char> der;
|
||||||
|
if (store && tls::get_cert_der(cert, der)) {
|
||||||
|
CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING, der.data(),
|
||||||
|
static_cast<DWORD>(der.size()),
|
||||||
|
CERT_STORE_ADD_USE_EXISTING, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Setup chain parameters
|
// Setup chain parameters
|
||||||
CERT_CHAIN_PARA chain_para = {};
|
CERT_CHAIN_PARA chain_para = {};
|
||||||
chain_para.cbSize = sizeof(chain_para);
|
chain_para.cbSize = sizeof(chain_para);
|
||||||
@@ -10726,7 +10748,7 @@ verify_cert_with_windows_schannel(const std::vector<unsigned char> &der_cert,
|
|||||||
// Build certificate chain with revocation checking
|
// Build certificate chain with revocation checking
|
||||||
PCCERT_CHAIN_CONTEXT chain_context = nullptr;
|
PCCERT_CHAIN_CONTEXT chain_context = nullptr;
|
||||||
auto chain_result = CertGetCertificateChain(
|
auto chain_result = CertGetCertificateChain(
|
||||||
nullptr, cert_context, nullptr, cert_context->hCertStore, &chain_para,
|
nullptr, cert_context, nullptr, store, &chain_para,
|
||||||
CERT_CHAIN_CACHE_END_CERT | CERT_CHAIN_REVOCATION_CHECK_END_CERT |
|
CERT_CHAIN_CACHE_END_CERT | CERT_CHAIN_REVOCATION_CHECK_END_CERT |
|
||||||
CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT,
|
CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT,
|
||||||
nullptr, &chain_context);
|
nullptr, &chain_context);
|
||||||
@@ -10949,8 +10971,8 @@ inline bool setup_client_tls_session(
|
|||||||
if (get_cert_der(server_cert, der)) {
|
if (get_cert_der(server_cert, der)) {
|
||||||
uint64_t wincrypt_error = 0;
|
uint64_t wincrypt_error = 0;
|
||||||
if (!verify_cert_with_windows_schannel(
|
if (!verify_cert_with_windows_schannel(
|
||||||
der, host, options.server_hostname_verification,
|
der, host, options.server_hostname_verification, wincrypt_error,
|
||||||
wincrypt_error)) {
|
session)) {
|
||||||
return fail(Error::SSLServerVerification, 0, wincrypt_error);
|
return fail(Error::SSLServerVerification, 0, wincrypt_error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -19523,6 +19545,24 @@ inline cert_t get_peer_cert(const_session_t session) {
|
|||||||
static_cast<SSL *>(const_cast<void *>(session))));
|
static_cast<SSL *>(const_cast<void *>(session))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline size_t get_peer_certs(const_session_t session,
|
||||||
|
std::vector<cert_t> &certs) {
|
||||||
|
certs.clear();
|
||||||
|
if (!session) { return 0; }
|
||||||
|
auto ssl = static_cast<const SSL *>(session);
|
||||||
|
// On the server side, the chain leaves out the peer's own certificate
|
||||||
|
if (SSL_is_server(ssl)) {
|
||||||
|
if (auto leaf = get_peer_cert(session)) { certs.push_back(leaf); }
|
||||||
|
}
|
||||||
|
auto sk = SSL_get_peer_cert_chain(ssl);
|
||||||
|
for (int i = 0; sk && i < sk_X509_num(sk); i++) {
|
||||||
|
auto x509 = sk_X509_value(sk, i);
|
||||||
|
X509_up_ref(x509);
|
||||||
|
certs.push_back(static_cast<cert_t>(x509));
|
||||||
|
}
|
||||||
|
return certs.size();
|
||||||
|
}
|
||||||
|
|
||||||
inline void free_cert(cert_t cert) {
|
inline void free_cert(cert_t cert) {
|
||||||
if (cert) { X509_free(static_cast<X509 *>(cert)); }
|
if (cert) { X509_free(static_cast<X509 *>(cert)); }
|
||||||
}
|
}
|
||||||
@@ -20867,6 +20907,18 @@ inline cert_t get_peer_cert(const_session_t session) {
|
|||||||
return const_cast<mbedtls_x509_crt *>(cert);
|
return const_cast<mbedtls_x509_crt *>(cert);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline size_t get_peer_certs(const_session_t session,
|
||||||
|
std::vector<cert_t> &certs) {
|
||||||
|
certs.clear();
|
||||||
|
// Mbed TLS parses the whole received chain into a list headed by the peer
|
||||||
|
// certificate, owned by the session like get_peer_cert()'s result
|
||||||
|
for (auto crt = static_cast<mbedtls_x509_crt *>(get_peer_cert(session));
|
||||||
|
crt && crt->raw.len > 0; crt = crt->next) {
|
||||||
|
certs.push_back(static_cast<cert_t>(crt));
|
||||||
|
}
|
||||||
|
return certs.size();
|
||||||
|
}
|
||||||
|
|
||||||
inline void free_cert(cert_t cert) {
|
inline void free_cert(cert_t cert) {
|
||||||
// Mbed TLS: peer certificate is owned by the SSL context.
|
// Mbed TLS: peer certificate is owned by the SSL context.
|
||||||
// No-op here, but callers should still call this for cross-backend
|
// No-op here, but callers should still call this for cross-backend
|
||||||
@@ -22024,6 +22076,24 @@ inline cert_t get_peer_cert(const_session_t session) {
|
|||||||
return static_cast<cert_t>(cert);
|
return static_cast<cert_t>(cert);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline size_t get_peer_certs(const_session_t session,
|
||||||
|
std::vector<cert_t> &certs) {
|
||||||
|
certs.clear();
|
||||||
|
if (!session) { return 0; }
|
||||||
|
// wolfSSL keeps the received chain only when built with SESSION_CERTS
|
||||||
|
#ifdef SESSION_CERTS
|
||||||
|
auto wsession =
|
||||||
|
static_cast<impl::WolfSSLSession *>(const_cast<void *>(session));
|
||||||
|
auto chain = wolfSSL_get_peer_chain(wsession->ssl);
|
||||||
|
auto count = chain ? wolfSSL_get_chain_count(chain) : 0;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
auto x509 = wolfSSL_get_chain_X509(chain, i);
|
||||||
|
if (x509) { certs.push_back(static_cast<cert_t>(x509)); }
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return certs.size();
|
||||||
|
}
|
||||||
|
|
||||||
inline void free_cert(cert_t cert) {
|
inline void free_cert(cert_t cert) {
|
||||||
if (cert) { wolfSSL_X509_free(static_cast<WOLFSSL_X509 *>(cert)); }
|
if (cert) { wolfSSL_X509_free(static_cast<WOLFSSL_X509 *>(cert)); }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13958,6 +13958,15 @@ TEST(SSLClientTest, WindowsCertificateVerification_Disabled) {
|
|||||||
auto res = cli.Get("/");
|
auto res = cli.Get("/");
|
||||||
if (res) { EXPECT_NE(StatusCode::InternalServerError_500, res->status); }
|
if (res) { EXPECT_NE(StatusCode::InternalServerError_500, res->status); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The server sends an intermediate cross-signed by a root Windows trusts,
|
||||||
|
// while the leaf's AIA URL leads to one under a root Windows does not trust.
|
||||||
|
TEST(SSLClientTest, WindowsCertificateVerification_ServerIntermediates_Online) {
|
||||||
|
SSLClient cli("accounts.spotify.com", 443);
|
||||||
|
auto res = cli.Get("/");
|
||||||
|
ASSERT_TRUE(res) << "Error: " << to_string(res.error())
|
||||||
|
<< " ssl_backend_error=" << res.ssl_backend_error();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
TEST(SSLClientTest, ServerCertificateVerification1_Online) {
|
TEST(SSLClientTest, ServerCertificateVerification1_Online) {
|
||||||
|
|||||||
Reference in new issue
Block a user