From f57a8139bb8a66d47469b66a3ee63c38616e3e67 Mon Sep 17 00:00:00 2001 From: Harshil Jain Date: Thu, 6 Oct 2022 18:57:33 +0530 Subject: [PATCH] crypto: added support for reading system store certificates in windows --- src/crypto/crypto_context.cc | 104 ++++++++++++++++++++++++++++++++--- src/node_options.cc | 4 ++ src/node_options.h | 1 + 3 files changed, 101 insertions(+), 8 deletions(-) diff --git a/src/crypto/crypto_context.cc b/src/crypto/crypto_context.cc index 7eab9de37cb3a1..1a928497e68bb5 100644 --- a/src/crypto/crypto_context.cc +++ b/src/crypto/crypto_context.cc @@ -18,6 +18,13 @@ #include #endif // !OPENSSL_NO_ENGINE +#ifdef _WIN32 +#include +#include + +#include "base64-inl.h" +#endif + namespace node { using v8::Array; @@ -190,6 +197,65 @@ int SSL_CTX_use_certificate_chain(SSL_CTX* ctx, } // namespace +void ReadSystemStoreCertificates( + std::vector* system_root_certificates) { + const HCERTSTORE hStore = CertOpenSystemStoreW(0, L"ROOT"); + CHECK_NE(hStore, NULLPTR); + + auto cleanup = + OnScopeLeave([hStore]() { CHECK_EQ(CertCloseStore(hStore, 0), TRUE); }); + + PCCERT_CONTEXT pCtx = nullptr; + + while ((pCtx = CertEnumCertificatesInStore(hStore, pCtx)) != nullptr) { + const DWORD cbSize = CertGetNameStringW( + pCtx, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, nullptr, nullptr, 0); + + CHECK_GT(cbSize, 0); + + std::vector pszName(cbSize); + + CHECK_GT(CertGetNameStringW(pCtx, + CERT_NAME_SIMPLE_DISPLAY_TYPE, + 0, + nullptr, + pszName.data(), + cbSize), + 0); + + const char* certificate_src_ptr = + reinterpret_cast(pCtx->pbCertEncoded); + const size_t slen = pCtx->cbCertEncoded; + const size_t dlen = base64_encoded_size(slen); + + char* certificate_dst_ptr = UncheckedMalloc(dlen); + + CHECK_NOT_NULL(certificate_dst_ptr); + + auto cleanup = + OnScopeLeave([certificate_dst_ptr]() { free(certificate_dst_ptr); }); + + const size_t written = + base64_encode(certificate_src_ptr, slen, certificate_dst_ptr, dlen); + CHECK_EQ(written, dlen); + + std::string base64_string_output(certificate_dst_ptr, dlen); + + constexpr size_t distance = 72; + size_t pos = distance; + + while (pos < base64_string_output.size()) { + base64_string_output.insert(pos, "\n"); + pos += distance + 1; + } + + base64_string_output = "-----BEGIN CERTIFICATE-----\n" + + base64_string_output + "\n-----END CERTIFICATE-----"; + + system_root_certificates->emplace_back(std::move(base64_string_output)); + } +} + X509_STORE* NewRootCertStore() { static std::vector root_certs_vector; static Mutex root_certs_vector_mutex; @@ -197,11 +263,22 @@ X509_STORE* NewRootCertStore() { if (root_certs_vector.empty() && per_process::cli_options->ssl_openssl_cert_store == false) { + std::vector combined_root_certs; + for (size_t i = 0; i < arraysize(root_certs); i++) { + combined_root_certs.emplace_back(root_certs[i]); + } + + if (per_process::cli_options->node_use_system_ca) { + ReadSystemStoreCertificates(&combined_root_certs); + } + + for (size_t i = 0; i < combined_root_certs.size(); i++) { X509* x509 = - PEM_read_bio_X509(NodeBIO::NewFixed(root_certs[i], - strlen(root_certs[i])).get(), - nullptr, // no re-use of X509 structure + PEM_read_bio_X509(NodeBIO::NewFixed(combined_root_certs[i].c_str(), + combined_root_certs[i].length()) + .get(), + nullptr, // no re-use of X509 structure NoPasswordCallback, nullptr); // no callback data @@ -234,19 +311,30 @@ X509_STORE* NewRootCertStore() { void GetRootCertificates(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - Local result[arraysize(root_certs)]; + + std::vector combined_root_certs; for (size_t i = 0; i < arraysize(root_certs); i++) { + combined_root_certs.emplace_back(root_certs[i]); + } + + if (per_process::cli_options->node_use_system_ca) { + ReadSystemStoreCertificates(&combined_root_certs); + } + + std::vector> result(combined_root_certs.size()); + + for (size_t i = 0; i < combined_root_certs.size(); i++) { if (!String::NewFromOneByte( - env->isolate(), - reinterpret_cast(root_certs[i])) - .ToLocal(&result[i])) { + env->isolate(), + reinterpret_cast(combined_root_certs[i].c_str())) + .ToLocal(&result[i])) { return; } } args.GetReturnValue().Set( - Array::New(env->isolate(), result, arraysize(root_certs))); + Array::New(env->isolate(), result.data(), combined_root_certs.size())); } bool SecureContext::HasInstance(Environment* env, const Local& value) { diff --git a/src/node_options.cc b/src/node_options.cc index b9ffafb7cacd6c..5ac8b21225cfcc 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -843,6 +843,10 @@ PerProcessOptionsParser::PerProcessOptionsParser( "use an alternative default TLS cipher list", &PerProcessOptions::tls_cipher_list, kAllowedInEnvironment); + AddOption("--node-use-system-ca", + "use system's store CA", + &PerProcessOptions::node_use_system_ca, + kAllowedInEnvironment); AddOption("--use-openssl-ca", "use OpenSSL's default CA store" #if defined(NODE_OPENSSL_CERT_STORE) diff --git a/src/node_options.h b/src/node_options.h index ca43192d85a4b4..a21da4e98dc20f 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -264,6 +264,7 @@ class PerProcessOptions : public Options { #else bool ssl_openssl_cert_store = false; #endif + bool node_use_system_ca = false; bool use_openssl_ca = false; bool use_bundled_ca = false; bool enable_fips_crypto = false;