@@ -101,11 +101,11 @@ static thread_local X509_STORE* root_cert_store = nullptr;
101101// from this set.
102102static thread_local std::unique_ptr<X509Set> root_certs_from_users;
103103
104- X509_STORE* GetOrCreateRootCertStore () {
104+ X509_STORE* GetOrCreateRootCertStore (Environment* env ) {
105105 if (root_cert_store != nullptr ) {
106106 return root_cert_store;
107107 }
108- root_cert_store = NewRootCertStore ();
108+ root_cert_store = NewRootCertStore (env );
109109 return root_cert_store;
110110}
111111
@@ -870,23 +870,21 @@ static void LoadCACertificates(void* data) {
870870 " Started loading extra root certificates off-thread\n " );
871871 GetExtraCACertificates ();
872872 }
873+ }
873874
874- {
875- Mutex::ScopedLock cli_lock (node::per_process::cli_options_mutex);
876- if (!per_process::cli_options->use_system_ca ) {
877- return ;
878- }
879- }
880-
875+ static void LoadSystemCACertificates (void * data) {
881876 per_process::Debug (DebugCategory::CRYPTO,
882877 " Started loading system root certificates off-thread\n " );
883878 GetSystemStoreCACertificates ();
884879}
885880
886881static std::atomic<bool > tried_cert_loading_off_thread = false ;
887882static std::atomic<bool > cert_loading_thread_started = false ;
883+ static std::atomic<bool > tried_system_cert_loading_off_thread = false ;
884+ static std::atomic<bool > system_cert_loading_thread_started = false ;
888885static Mutex start_cert_loading_thread_mutex;
889886static uv_thread_t cert_loading_thread;
887+ static uv_thread_t system_cert_loading_thread;
890888
891889void StartLoadingCertificatesOffThread (
892890 const FunctionCallbackInfo<Value>& args) {
@@ -906,16 +904,13 @@ void StartLoadingCertificatesOffThread(
906904 }
907905 }
908906
907+ Environment* env = Environment::GetCurrent (args);
908+ const bool use_system_ca = env != nullptr && env->options ()->use_system_ca ;
909+
910+ Mutex::ScopedLock lock (start_cert_loading_thread_mutex);
911+
909912 // Only try to start the thread once. If it ever fails, we won't try again.
910- if (tried_cert_loading_off_thread.load ()) {
911- return ;
912- }
913- {
914- Mutex::ScopedLock lock (start_cert_loading_thread_mutex);
915- // Re-check under the lock.
916- if (tried_cert_loading_off_thread.load ()) {
917- return ;
918- }
913+ if (!tried_cert_loading_off_thread.load ()) {
919914 tried_cert_loading_off_thread.store (true );
920915 int r = uv_thread_create (&cert_loading_thread, LoadCACertificates, nullptr );
921916 cert_loading_thread_started.store (r == 0 );
@@ -925,6 +920,23 @@ void StartLoadingCertificatesOffThread(
925920 uv_strerror (r));
926921 }
927922 }
923+
924+ // If the system CA list hasn't been loaded off-thread yet, allow a worker
925+ // enabling --use-system-ca to trigger its off-thread loading.
926+ if (use_system_ca &&
927+ !has_cached_system_root_certs.load () &&
928+ !tried_system_cert_loading_off_thread.load ()) {
929+ tried_system_cert_loading_off_thread.store (true );
930+ int r = uv_thread_create (&system_cert_loading_thread,
931+ LoadSystemCACertificates,
932+ nullptr );
933+ system_cert_loading_thread_started.store (r == 0 );
934+ if (r != 0 ) {
935+ FPrintF (stderr,
936+ " Warning: Failed to load system CA certificates off thread: %s\n " ,
937+ uv_strerror (r));
938+ }
939+ }
928940}
929941
930942// Due to historical reasons the various options of CA certificates
@@ -947,13 +959,13 @@ void StartLoadingCertificatesOffThread(
947959// with all the other flags.
948960// 7. Certificates from --use-bundled-ca, --use-system-ca and
949961// NODE_EXTRA_CA_CERTS are cached after first load. Certificates
950- // from --use-system -ca are not cached and always reloaded from
962+ // from --use-openssl -ca are not cached and always reloaded from
951963// disk.
952964// 8. If users have reset the root cert store by calling
953965// tls.setDefaultCACertificates(), the store will be populated with
954966// the certificates provided by users.
955967// TODO(joyeecheung): maybe these rules need a bit of consolidation?
956- X509_STORE* NewRootCertStore () {
968+ X509_STORE* NewRootCertStore (Environment* env ) {
957969 X509_STORE* store = X509_STORE_new ();
958970 CHECK_NOT_NULL (store);
959971
@@ -975,14 +987,24 @@ X509_STORE* NewRootCertStore() {
975987 }
976988#endif
977989
978- Mutex::ScopedLock cli_lock (node::per_process::cli_options_mutex);
990+ bool use_system_ca = false ;
991+ {
992+ Mutex::ScopedLock cli_lock (node::per_process::cli_options_mutex);
993+ if (env != nullptr ) {
994+ use_system_ca = env->options ()->use_system_ca ;
995+ } else if (per_process::cli_options->per_isolate != nullptr &&
996+ per_process::cli_options->per_isolate ->per_env != nullptr ) {
997+ use_system_ca =
998+ per_process::cli_options->per_isolate ->per_env ->use_system_ca ;
999+ }
1000+ }
9791001 if (per_process::cli_options->ssl_openssl_cert_store ) {
9801002 CHECK_EQ (1 , X509_STORE_set_default_paths (store));
9811003 } else {
9821004 for (X509* cert : GetBundledRootCertificates ()) {
9831005 CHECK_EQ (1 , X509_STORE_add_cert (store, cert));
9841006 }
985- if (per_process::cli_options-> use_system_ca ) {
1007+ if (use_system_ca) {
9861008 for (X509* cert : GetSystemStoreCACertificates ()) {
9871009 CHECK_EQ (1 , X509_STORE_add_cert (store, cert));
9881010 }
@@ -1022,6 +1044,10 @@ void CleanupCachedRootCertificates() {
10221044 cert_loading_thread_started.load ()) {
10231045 uv_thread_join (&cert_loading_thread);
10241046 }
1047+ if (tried_system_cert_loading_off_thread.load () &&
1048+ system_cert_loading_thread_started.load ()) {
1049+ uv_thread_join (&system_cert_loading_thread);
1050+ }
10251051}
10261052
10271053void GetBundledRootCertificates (const FunctionCallbackInfo<Value>& args) {
@@ -1187,9 +1213,7 @@ void ResetRootCertStore(const FunctionCallbackInfo<Value>& args) {
11871213 X509_STORE_free (root_cert_store);
11881214 }
11891215
1190- // TODO(joyeecheung): we can probably just reset it to nullptr
1191- // and let the next call to NewRootCertStore() create a new one.
1192- root_cert_store = NewRootCertStore ();
1216+ root_cert_store = nullptr ;
11931217}
11941218
11951219void GetSystemCACertificates (const FunctionCallbackInfo<Value>& args) {
@@ -1700,11 +1724,12 @@ void SecureContext::SetX509StoreFlag(unsigned long flags) {
17001724}
17011725
17021726X509_STORE* SecureContext::GetCertStoreOwnedByThisSecureContext () {
1727+ Environment* env = this ->env ();
17031728 if (own_cert_store_cache_ != nullptr ) return own_cert_store_cache_;
17041729
17051730 X509_STORE* cert_store = SSL_CTX_get_cert_store (ctx_.get ());
1706- if (cert_store == GetOrCreateRootCertStore ()) {
1707- cert_store = NewRootCertStore ();
1731+ if (cert_store == GetOrCreateRootCertStore (env )) {
1732+ cert_store = NewRootCertStore (env );
17081733 SSL_CTX_set_cert_store (ctx_.get (), cert_store);
17091734 }
17101735
@@ -1777,7 +1802,8 @@ void SecureContext::AddCRL(const FunctionCallbackInfo<Value>& args) {
17771802
17781803void SecureContext::SetRootCerts () {
17791804 ClearErrorOnReturn clear_error_on_return;
1780- auto store = GetOrCreateRootCertStore ();
1805+ Environment* env = this ->env ();
1806+ auto store = GetOrCreateRootCertStore (env);
17811807
17821808 // Increment reference count so global store is not deleted along with CTX.
17831809 X509_STORE_up_ref (store);
0 commit comments