From 0ab510b79c062277b63c81509a3b9aed27aad442 Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Tue, 6 Oct 2020 13:25:23 +0200 Subject: [PATCH 1/3] src: mark/pop OpenSSL errors in NewRootCertStore This commit sets the OpenSSL error mark before calling X509_STORE_load_locations and pops the error mark afterwards. The motivation for this is that it is possible that X509_STORE_load_locations can produce errors if the configuration option --openssl-system-ca-path file does not exist. Later if a different function is called which calls an OpenSSL function it could fail because these errors might still be on the OpenSSL error stack. Currently, all functions that call NewRootCertStore clear the OpenSSL error queue upon returning, but this was not the case for example in v12.18.0. Fixes: https://github.com/nodejs/node/issues/35456 --- node.gyp | 8 +++++++- src/crypto/crypto_context.cc | 5 ++++- test/cctest/test_node_crypto.cc | 23 +++++++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 test/cctest/test_node_crypto.cc diff --git a/node.gyp b/node.gyp index 264a2cb408ed8b..dcbcca87aa8859 100644 --- a/node.gyp +++ b/node.gyp @@ -1361,6 +1361,9 @@ 'defines': [ 'HAVE_OPENSSL=1', ], + 'sources': [ + 'test/cctest/test_node_crypto.cc', + ] }], [ 'node_use_openssl=="true" and experimental_quic==1', { 'defines': [ @@ -1385,7 +1388,10 @@ ] }], ['OS=="solaris"', { - 'ldflags': [ '-I<(SHARED_INTERMEDIATE_DIR)' ] + 'ldflags': [ '-I<(SHARED_INTERMEDIATE_DIR)' ], + 'sources!': [ + 'test/cctest/test_node_crypto.cc', + ] }], # Skip cctest while building shared lib node for Windows [ 'OS=="win" and node_shared=="true"', { diff --git a/src/crypto/crypto_context.cc b/src/crypto/crypto_context.cc index 16a14ffcc89591..54ea3404b55218 100644 --- a/src/crypto/crypto_context.cc +++ b/src/crypto/crypto_context.cc @@ -192,7 +192,8 @@ static X509_STORE* NewRootCertStore() { static Mutex root_certs_vector_mutex; Mutex::ScopedLock lock(root_certs_vector_mutex); - if (root_certs_vector.empty()) { + if (root_certs_vector.empty() && + per_process::cli_options->ssl_openssl_cert_store == false) { for (size_t i = 0; i < arraysize(root_certs); i++) { X509* x509 = PEM_read_bio_X509(NodeBIO::NewFixed(root_certs[i], @@ -210,7 +211,9 @@ static X509_STORE* NewRootCertStore() { X509_STORE* store = X509_STORE_new(); if (*system_cert_path != '\0') { + ERR_set_mark(); X509_STORE_load_locations(store, system_cert_path, nullptr); + ERR_pop_to_mark(); } Mutex::ScopedLock cli_lock(node::per_process::cli_options_mutex); diff --git a/test/cctest/test_node_crypto.cc b/test/cctest/test_node_crypto.cc new file mode 100644 index 00000000000000..c8bb0577d4def2 --- /dev/null +++ b/test/cctest/test_node_crypto.cc @@ -0,0 +1,23 @@ +// This simulates specifying the configuration option --openssl-system-ca-path +// and settting it to a file that does not exist. +#define NODE_OPENSSL_SYSTEM_CERT_PATH "/missing/ca.pem" + +#include "../../src/crypto/crypto_context.cc" // NOLINT(build/include) +#include "node_options.h" +#include "openssl/err.h" +#include "gtest/gtest.h" + +/* + * This test verifies that a call to NewRootCertDir with the build time + * configuration option --openssl-system-ca-path set to an missing file, will + * not leave any OpenSSL errors on the OpenSSL error stack. + * See https://github.com/nodejs/node/issues/35456 for details. + */ +TEST(NodeCrypto, NewRootCertStore) { + node::per_process::cli_options->ssl_openssl_cert_store = true; + X509_STORE* store = node::crypto::NewRootCertStore(); + ASSERT_TRUE(store); + ASSERT_EQ(ERR_peek_error(), 0UL) << "NewRootCertStore should not have left " + "any errors on the OpenSSL error stack\n"; + X509_STORE_free(store); +} From f7fd2ecaf5b179ebd47821c484854296c1d6dd2c Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Sun, 18 Oct 2020 11:04:13 +0200 Subject: [PATCH 2/3] squash: move NewRootCertStore to crypto_context.h --- src/crypto/crypto_context.cc | 5 +++-- src/crypto/crypto_context.h | 2 ++ test/cctest/test_node_crypto.cc | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/crypto/crypto_context.cc b/src/crypto/crypto_context.cc index 54ea3404b55218..5afb120c612bbc 100644 --- a/src/crypto/crypto_context.cc +++ b/src/crypto/crypto_context.cc @@ -187,7 +187,9 @@ int SSL_CTX_use_certificate_chain(SSL_CTX* ctx, issuer); } -static X509_STORE* NewRootCertStore() { +} // namespace + +X509_STORE* NewRootCertStore() { static std::vector root_certs_vector; static Mutex root_certs_vector_mutex; Mutex::ScopedLock lock(root_certs_vector_mutex); @@ -228,7 +230,6 @@ static X509_STORE* NewRootCertStore() { return store; } -} // namespace void GetRootCertificates(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); diff --git a/src/crypto/crypto_context.h b/src/crypto/crypto_context.h index f6c911f1fad03f..d7c94bd952b88a 100644 --- a/src/crypto/crypto_context.h +++ b/src/crypto/crypto_context.h @@ -21,6 +21,8 @@ void GetRootCertificates( void IsExtraRootCertsFileLoaded( const v8::FunctionCallbackInfo& args); +X509_STORE* NewRootCertStore(); + class SecureContext final : public BaseObject { public: ~SecureContext() override; diff --git a/test/cctest/test_node_crypto.cc b/test/cctest/test_node_crypto.cc index c8bb0577d4def2..c0ce5f25496cd3 100644 --- a/test/cctest/test_node_crypto.cc +++ b/test/cctest/test_node_crypto.cc @@ -2,7 +2,7 @@ // and settting it to a file that does not exist. #define NODE_OPENSSL_SYSTEM_CERT_PATH "/missing/ca.pem" -#include "../../src/crypto/crypto_context.cc" // NOLINT(build/include) +#include "crypto/crypto_context.h" #include "node_options.h" #include "openssl/err.h" #include "gtest/gtest.h" From 9117d4f5481083c27edecf3156331c2850bc44a5 Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Sun, 18 Oct 2020 12:40:37 +0200 Subject: [PATCH 3/3] squash: remove smartos ignore --- node.gyp | 3 --- 1 file changed, 3 deletions(-) diff --git a/node.gyp b/node.gyp index dcbcca87aa8859..4eaf943b563e5b 100644 --- a/node.gyp +++ b/node.gyp @@ -1389,9 +1389,6 @@ }], ['OS=="solaris"', { 'ldflags': [ '-I<(SHARED_INTERMEDIATE_DIR)' ], - 'sources!': [ - 'test/cctest/test_node_crypto.cc', - ] }], # Skip cctest while building shared lib node for Windows [ 'OS=="win" and node_shared=="true"', {