Skip to content

Commit ca3e55f

Browse files
committed
src: fix tls certificate root store data race
OpenSSL internally synchronizes access to the X509_STORE. Creation of the global root store in Node was not properly synchronized, however, introducing the possibility of data races when multiple threads try to create it concurrently. This commit coincidentally removes the last call to the thread-unsafe ERR_error_string() function. Fixes: #45743
1 parent 3bef549 commit ca3e55f

File tree

1 file changed

+23
-29
lines changed

1 file changed

+23
-29
lines changed

src/crypto/crypto_context.cc

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,15 @@ static const char* const root_certs[] = {
4747

4848
static const char system_cert_path[] = NODE_OPENSSL_SYSTEM_CERT_PATH;
4949

50-
static X509_STORE* root_cert_store;
51-
5250
static bool extra_root_certs_loaded = false;
5351

52+
inline X509_STORE* GetOrCreateRootCertStore() {
53+
static X509_STORE* store;
54+
static uv_once_t once = UV_ONCE_INIT;
55+
uv_once(&once, [] { store = NewRootCertStore(); });
56+
return store;
57+
}
58+
5459
// Takes a string or buffer and loads it into a BIO.
5560
// Caller responsible for BIO_free_all-ing the returned object.
5661
BIOPointer LoadBIO(Environment* env, Local<Value> v) {
@@ -701,7 +706,7 @@ void SecureContext::AddCACert(const FunctionCallbackInfo<Value>& args) {
701706
X509_STORE* cert_store = SSL_CTX_get_cert_store(sc->ctx_.get());
702707
while (X509Pointer x509 = X509Pointer(PEM_read_bio_X509_AUX(
703708
bio.get(), nullptr, NoPasswordCallback, nullptr))) {
704-
if (cert_store == root_cert_store) {
709+
if (cert_store == GetOrCreateRootCertStore()) {
705710
cert_store = NewRootCertStore();
706711
SSL_CTX_set_cert_store(sc->ctx_.get(), cert_store);
707712
}
@@ -731,7 +736,7 @@ void SecureContext::AddCRL(const FunctionCallbackInfo<Value>& args) {
731736
return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to parse CRL");
732737

733738
X509_STORE* cert_store = SSL_CTX_get_cert_store(sc->ctx_.get());
734-
if (cert_store == root_cert_store) {
739+
if (cert_store == GetOrCreateRootCertStore()) {
735740
cert_store = NewRootCertStore();
736741
SSL_CTX_set_cert_store(sc->ctx_.get(), cert_store);
737742
}
@@ -745,14 +750,10 @@ void SecureContext::AddRootCerts(const FunctionCallbackInfo<Value>& args) {
745750
SecureContext* sc;
746751
ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder());
747752
ClearErrorOnReturn clear_error_on_return;
748-
749-
if (root_cert_store == nullptr) {
750-
root_cert_store = NewRootCertStore();
751-
}
752-
753+
X509_STORE* store = GetOrCreateRootCertStore();
753754
// Increment reference count so global store is not deleted along with CTX.
754-
X509_STORE_up_ref(root_cert_store);
755-
SSL_CTX_set_cert_store(sc->ctx_.get(), root_cert_store);
755+
X509_STORE_up_ref(store);
756+
SSL_CTX_set_cert_store(sc->ctx_.get(), store);
756757
}
757758

758759
void SecureContext::SetCipherSuites(const FunctionCallbackInfo<Value>& args) {
@@ -1025,7 +1026,7 @@ void SecureContext::LoadPKCS12(const FunctionCallbackInfo<Value>& args) {
10251026
for (int i = 0; i < sk_X509_num(extra_certs.get()); i++) {
10261027
X509* ca = sk_X509_value(extra_certs.get(), i);
10271028

1028-
if (cert_store == root_cert_store) {
1029+
if (cert_store == GetOrCreateRootCertStore()) {
10291030
cert_store = NewRootCertStore();
10301031
SSL_CTX_set_cert_store(sc->ctx_.get(), cert_store);
10311032
}
@@ -1328,24 +1329,17 @@ unsigned long AddCertsFromFile( // NOLINT(runtime/int)
13281329

13291330
// UseExtraCaCerts is called only once at the start of the Node.js process.
13301331
void UseExtraCaCerts(const std::string& file) {
1332+
if (file.empty()) return;
13311333
ClearErrorOnReturn clear_error_on_return;
1332-
1333-
if (root_cert_store == nullptr) {
1334-
root_cert_store = NewRootCertStore();
1335-
1336-
if (!file.empty()) {
1337-
unsigned long err = AddCertsFromFile( // NOLINT(runtime/int)
1338-
root_cert_store,
1339-
file.c_str());
1340-
if (err) {
1341-
fprintf(stderr,
1342-
"Warning: Ignoring extra certs from `%s`, load failed: %s\n",
1343-
file.c_str(),
1344-
ERR_error_string(err, nullptr));
1345-
} else {
1346-
extra_root_certs_loaded = true;
1347-
}
1348-
}
1334+
X509_STORE* store = GetOrCreateRootCertStore();
1335+
if (auto err = AddCertsFromFile(store, file.c_str())) {
1336+
char buf[256];
1337+
ERR_error_string_n(err, buf, sizeof(buf));
1338+
fprintf(stderr,
1339+
"Warning: Ignoring extra certs from `%s`, load failed: %s\n",
1340+
file.c_str(), buf);
1341+
} else {
1342+
extra_root_certs_loaded = true;
13491343
}
13501344
}
13511345

0 commit comments

Comments
 (0)