@@ -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,21 @@ 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 && !has_cached_system_root_certs.load () &&
927+ !tried_system_cert_loading_off_thread.load ()) {
928+ tried_system_cert_loading_off_thread.store (true );
929+ int r = uv_thread_create (
930+ &system_cert_loading_thread, LoadSystemCACertificates, nullptr );
931+ system_cert_loading_thread_started.store (r == 0 );
932+ if (r != 0 ) {
933+ FPrintF (stderr,
934+ " Warning: Failed to load system CA certificates off thread: %s\n " ,
935+ uv_strerror (r));
936+ }
937+ }
928938}
929939
930940// Due to historical reasons the various options of CA certificates
@@ -947,13 +957,13 @@ void StartLoadingCertificatesOffThread(
947957// with all the other flags.
948958// 7. Certificates from --use-bundled-ca, --use-system-ca and
949959// NODE_EXTRA_CA_CERTS are cached after first load. Certificates
950- // from --use-system -ca are not cached and always reloaded from
960+ // from --use-openssl -ca are not cached and always reloaded from
951961// disk.
952962// 8. If users have reset the root cert store by calling
953963// tls.setDefaultCACertificates(), the store will be populated with
954964// the certificates provided by users.
955965// TODO(joyeecheung): maybe these rules need a bit of consolidation?
956- X509_STORE* NewRootCertStore () {
966+ X509_STORE* NewRootCertStore (Environment* env ) {
957967 X509_STORE* store = X509_STORE_new ();
958968 CHECK_NOT_NULL (store);
959969
@@ -975,14 +985,24 @@ X509_STORE* NewRootCertStore() {
975985 }
976986#endif
977987
978- Mutex::ScopedLock cli_lock (node::per_process::cli_options_mutex);
988+ bool use_system_ca = false ;
989+ {
990+ Mutex::ScopedLock cli_lock (node::per_process::cli_options_mutex);
991+ if (env != nullptr ) {
992+ use_system_ca = env->options ()->use_system_ca ;
993+ } else if (per_process::cli_options->per_isolate != nullptr &&
994+ per_process::cli_options->per_isolate ->per_env != nullptr ) {
995+ use_system_ca =
996+ per_process::cli_options->per_isolate ->per_env ->use_system_ca ;
997+ }
998+ }
979999 if (per_process::cli_options->ssl_openssl_cert_store ) {
9801000 CHECK_EQ (1 , X509_STORE_set_default_paths (store));
9811001 } else {
9821002 for (X509* cert : GetBundledRootCertificates ()) {
9831003 CHECK_EQ (1 , X509_STORE_add_cert (store, cert));
9841004 }
985- if (per_process::cli_options-> use_system_ca ) {
1005+ if (use_system_ca) {
9861006 for (X509* cert : GetSystemStoreCACertificates ()) {
9871007 CHECK_EQ (1 , X509_STORE_add_cert (store, cert));
9881008 }
@@ -1022,6 +1042,10 @@ void CleanupCachedRootCertificates() {
10221042 cert_loading_thread_started.load ()) {
10231043 uv_thread_join (&cert_loading_thread);
10241044 }
1045+ if (tried_system_cert_loading_off_thread.load () &&
1046+ system_cert_loading_thread_started.load ()) {
1047+ uv_thread_join (&system_cert_loading_thread);
1048+ }
10251049}
10261050
10271051void GetBundledRootCertificates (const FunctionCallbackInfo<Value>& args) {
@@ -1187,9 +1211,7 @@ void ResetRootCertStore(const FunctionCallbackInfo<Value>& args) {
11871211 X509_STORE_free (root_cert_store);
11881212 }
11891213
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 ();
1214+ root_cert_store = nullptr ;
11931215}
11941216
11951217void GetSystemCACertificates (const FunctionCallbackInfo<Value>& args) {
@@ -1700,11 +1722,12 @@ void SecureContext::SetX509StoreFlag(unsigned long flags) {
17001722}
17011723
17021724X509_STORE* SecureContext::GetCertStoreOwnedByThisSecureContext () {
1725+ Environment* env = this ->env ();
17031726 if (own_cert_store_cache_ != nullptr ) return own_cert_store_cache_;
17041727
17051728 X509_STORE* cert_store = SSL_CTX_get_cert_store (ctx_.get ());
1706- if (cert_store == GetOrCreateRootCertStore ()) {
1707- cert_store = NewRootCertStore ();
1729+ if (cert_store == GetOrCreateRootCertStore (env )) {
1730+ cert_store = NewRootCertStore (env );
17081731 SSL_CTX_set_cert_store (ctx_.get (), cert_store);
17091732 }
17101733
@@ -1777,7 +1800,8 @@ void SecureContext::AddCRL(const FunctionCallbackInfo<Value>& args) {
17771800
17781801void SecureContext::SetRootCerts () {
17791802 ClearErrorOnReturn clear_error_on_return;
1780- auto store = GetOrCreateRootCertStore ();
1803+ Environment* env = this ->env ();
1804+ auto store = GetOrCreateRootCertStore (env);
17811805
17821806 // Increment reference count so global store is not deleted along with CTX.
17831807 X509_STORE_up_ref (store);
0 commit comments