-
-
Notifications
You must be signed in to change notification settings - Fork 32.3k
feat: added support for reading certificates from windows system store #44532
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -18,6 +18,13 @@ | |||||||||||||
#include <openssl/engine.h> | ||||||||||||||
#endif // !OPENSSL_NO_ENGINE | ||||||||||||||
|
||||||||||||||
#ifdef _WIN32 | ||||||||||||||
#include <Windows.h> | ||||||||||||||
#include <wincrypt.h> | ||||||||||||||
|
||||||||||||||
#include "base64-inl.h" | ||||||||||||||
#endif | ||||||||||||||
|
||||||||||||||
namespace node { | ||||||||||||||
|
||||||||||||||
using v8::Array; | ||||||||||||||
|
@@ -190,18 +197,93 @@ int SSL_CTX_use_certificate_chain(SSL_CTX* ctx, | |||||||||||||
|
||||||||||||||
} // namespace | ||||||||||||||
|
||||||||||||||
void ReadSystemStoreCertificates( | ||||||||||||||
std::vector<std::string>* system_root_certificates) { | ||||||||||||||
#ifdef _WIN32 | ||||||||||||||
const HCERTSTORE hStore = CertOpenSystemStoreW(0, L"ROOT"); | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Because the first arg is a pointer, right? How likely is it for this method to fail? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Worth taking a look at this change to openjdk and thinking about is the system store enough: https://bugs.openjdk.org/browse/JDK-6782021 There's also the Jetbrains implementation that accesses a number of stores and aggregates them: I've had confirmation that the jetbrains one doesn't work with intermediate certificates, awaiting confirmation with the OpenJDK one. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To make matters a bit more complex - Windows has this feature of revoking certificates through the "disallowed" store, that Chromium implements, for example https://github.com/chromium/chromium/blob/a5cf86ae718b86764946713e7abae12f1fa42d08/net/cert/internal/trust_store_win.cc#L334 Though I feel that for an initial implementation, not supporting that is fine because apparently many runtimes do not support it either (also we do not respect that when using the bundled certificates, anyway) |
||||||||||||||
CHECK_NE(hStore, nullptr); | ||||||||||||||
|
||||||||||||||
auto cleanup = | ||||||||||||||
OnScopeLeave([hStore]() { CHECK_EQ(CertCloseStore(hStore, 0), TRUE); }); | ||||||||||||||
jasnell marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
|
||||||||||||||
PCCERT_CONTEXT certificate_context_ptr = nullptr; | ||||||||||||||
|
||||||||||||||
std::vector<X509*> system_root_certificates_X509; | ||||||||||||||
|
||||||||||||||
while ((certificate_context_ptr = CertEnumCertificatesInStore( | ||||||||||||||
hStore, certificate_context_ptr)) != nullptr) { | ||||||||||||||
Comment on lines
+213
to
+214
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prefer something like this for readability reasons:
Suggested change
|
||||||||||||||
const DWORD certificate_buffer_size = | ||||||||||||||
CertGetNameStringW(certificate_context_ptr, | ||||||||||||||
CERT_NAME_SIMPLE_DISPLAY_TYPE, | ||||||||||||||
0, | ||||||||||||||
nullptr, | ||||||||||||||
nullptr, | ||||||||||||||
0); | ||||||||||||||
|
||||||||||||||
CHECK_GT(certificate_buffer_size, 0); | ||||||||||||||
|
||||||||||||||
std::vector<wchar_t> certificate_name(certificate_buffer_size); | ||||||||||||||
|
||||||||||||||
CHECK_GT(CertGetNameStringW(certificate_context_ptr, | ||||||||||||||
CERT_NAME_SIMPLE_DISPLAY_TYPE, | ||||||||||||||
0, | ||||||||||||||
nullptr, | ||||||||||||||
certificate_name.data(), | ||||||||||||||
certificate_buffer_size), | ||||||||||||||
0); | ||||||||||||||
const unsigned char* certificate_src_ptr = | ||||||||||||||
reinterpret_cast<const unsigned char*>( | ||||||||||||||
certificate_context_ptr->pbCertEncoded); | ||||||||||||||
const size_t certificate_src_length = | ||||||||||||||
certificate_context_ptr->cbCertEncoded; | ||||||||||||||
|
||||||||||||||
X509* cert = | ||||||||||||||
d2i_X509(nullptr, &certificate_src_ptr, certificate_src_length); | ||||||||||||||
|
||||||||||||||
system_root_certificates_X509.emplace_back(cert); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
for (size_t i = 0; i < system_root_certificates_X509.size(); i++) { | ||||||||||||||
int result = 0; | ||||||||||||||
|
||||||||||||||
BIOPointer bio(BIO_new(BIO_s_mem())); | ||||||||||||||
CHECK(bio); | ||||||||||||||
|
||||||||||||||
BUF_MEM* mem = nullptr; | ||||||||||||||
result = PEM_write_bio_X509(bio.get(), system_root_certificates_X509[i]); | ||||||||||||||
|
||||||||||||||
BIO_get_mem_ptr(bio.get(), &mem); | ||||||||||||||
std::string certificate_string_pem(mem->data, mem->length); | ||||||||||||||
system_root_certificates->emplace_back(certificate_string_pem); | ||||||||||||||
|
||||||||||||||
bio.reset(); | ||||||||||||||
Comment on lines
+258
to
+259
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Not necessary to reset manually, the destructor does that automatically when it goes out of scope. |
||||||||||||||
} | ||||||||||||||
#endif | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
X509_STORE* NewRootCertStore() { | ||||||||||||||
static std::vector<X509*> root_certs_vector; | ||||||||||||||
static Mutex root_certs_vector_mutex; | ||||||||||||||
Mutex::ScopedLock lock(root_certs_vector_mutex); | ||||||||||||||
|
||||||||||||||
if (root_certs_vector.empty() && | ||||||||||||||
per_process::cli_options->ssl_openssl_cert_store == false) { | ||||||||||||||
std::vector<std::string> 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(), | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
combined_root_certs[i].length()) | ||||||||||||||
.get(), | ||||||||||||||
nullptr, // no re-use of X509 structure | ||||||||||||||
NoPasswordCallback, | ||||||||||||||
nullptr); // no callback data | ||||||||||||||
|
||||||||||||||
|
@@ -234,19 +316,30 @@ X509_STORE* NewRootCertStore() { | |||||||||||||
|
||||||||||||||
void GetRootCertificates(const FunctionCallbackInfo<Value>& args) { | ||||||||||||||
Environment* env = Environment::GetCurrent(args); | ||||||||||||||
Local<Value> result[arraysize(root_certs)]; | ||||||||||||||
|
||||||||||||||
std::vector<std::string> 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<Local<Value>> result(combined_root_certs.size()); | ||||||||||||||
|
||||||||||||||
for (size_t i = 0; i < combined_root_certs.size(); i++) { | ||||||||||||||
if (!String::NewFromOneByte( | ||||||||||||||
env->isolate(), | ||||||||||||||
reinterpret_cast<const uint8_t*>(root_certs[i])) | ||||||||||||||
.ToLocal(&result[i])) { | ||||||||||||||
env->isolate(), | ||||||||||||||
reinterpret_cast<const uint8_t*>(combined_root_certs[i].c_str())) | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor optimization:
Suggested change
|
||||||||||||||
.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>& value) { | ||||||||||||||
|
Uh oh!
There was an error while loading. Please reload this page.