@@ -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,22 @@ 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;
886+ static Mutex start_system_cert_loading_thread_mutex;
889887static uv_thread_t cert_loading_thread;
888+ static uv_thread_t system_cert_loading_thread;
890889
891890void StartLoadingCertificatesOffThread (
892891 const FunctionCallbackInfo<Value>& args) {
@@ -906,23 +905,45 @@ void StartLoadingCertificatesOffThread(
906905 }
907906 }
908907
908+ Environment* env = Environment::GetCurrent (args);
909+ const bool use_system_ca = env != nullptr && env->options ()->use_system_ca ;
910+
909911 // 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- {
912+ // Quick check, if it's already tried, no need to lock.
913+ if (!tried_cert_loading_off_thread.load ()) {
914914 Mutex::ScopedLock lock (start_cert_loading_thread_mutex);
915- // Re-check under the lock.
916- if (tried_cert_loading_off_thread.load ()) {
917- return ;
915+ // Check again under the lock.
916+ if (!tried_cert_loading_off_thread.load ()) {
917+ tried_cert_loading_off_thread.store (true );
918+ int r =
919+ uv_thread_create (&cert_loading_thread, LoadCACertificates, nullptr );
920+ cert_loading_thread_started.store (r == 0 );
921+ if (r != 0 ) {
922+ FPrintF (stderr,
923+ " Warning: Failed to load CA certificates off thread: %s\n " ,
924+ uv_strerror (r));
925+ }
918926 }
919- tried_cert_loading_off_thread.store (true );
920- int r = uv_thread_create (&cert_loading_thread, LoadCACertificates, nullptr );
921- cert_loading_thread_started.store (r == 0 );
922- if (r != 0 ) {
923- FPrintF (stderr,
924- " Warning: Failed to load CA certificates off thread: %s\n " ,
925- uv_strerror (r));
927+ }
928+
929+ // If the system CA list hasn't been loaded off-thread yet, allow a worker
930+ // enabling --use-system-ca to trigger its off-thread loading.
931+ // Quick check, if it's already tried, no need to lock.
932+ if (use_system_ca && !has_cached_system_root_certs.load () &&
933+ !tried_system_cert_loading_off_thread.load ()) {
934+ Mutex::ScopedLock lock (start_system_cert_loading_thread_mutex);
935+ if (!has_cached_system_root_certs.load () &&
936+ !tried_system_cert_loading_off_thread.load ()) {
937+ tried_system_cert_loading_off_thread.store (true );
938+ int r = uv_thread_create (
939+ &system_cert_loading_thread, LoadSystemCACertificates, nullptr );
940+ system_cert_loading_thread_started.store (r == 0 );
941+ if (r != 0 ) {
942+ FPrintF (
943+ stderr,
944+ " Warning: Failed to load system CA certificates off thread: %s\n " ,
945+ uv_strerror (r));
946+ }
926947 }
927948 }
928949}
@@ -947,13 +968,13 @@ void StartLoadingCertificatesOffThread(
947968// with all the other flags.
948969// 7. Certificates from --use-bundled-ca, --use-system-ca and
949970// NODE_EXTRA_CA_CERTS are cached after first load. Certificates
950- // from --use-system -ca are not cached and always reloaded from
971+ // from --use-openssl -ca are not cached and always reloaded from
951972// disk.
952973// 8. If users have reset the root cert store by calling
953974// tls.setDefaultCACertificates(), the store will be populated with
954975// the certificates provided by users.
955976// TODO(joyeecheung): maybe these rules need a bit of consolidation?
956- X509_STORE* NewRootCertStore () {
977+ X509_STORE* NewRootCertStore (Environment* env ) {
957978 X509_STORE* store = X509_STORE_new ();
958979 CHECK_NOT_NULL (store);
959980
@@ -975,14 +996,26 @@ X509_STORE* NewRootCertStore() {
975996 }
976997#endif
977998
978- Mutex::ScopedLock cli_lock (node::per_process::cli_options_mutex);
979- if (per_process::cli_options->ssl_openssl_cert_store ) {
999+ bool use_system_ca = false ;
1000+ bool ssl_openssl_cert_store = false ;
1001+ {
1002+ Mutex::ScopedLock cli_lock (node::per_process::cli_options_mutex);
1003+ ssl_openssl_cert_store = per_process::cli_options->ssl_openssl_cert_store ;
1004+ if (env != nullptr ) {
1005+ use_system_ca = env->options ()->use_system_ca ;
1006+ } else if (per_process::cli_options->per_isolate != nullptr &&
1007+ per_process::cli_options->per_isolate ->per_env != nullptr ) {
1008+ use_system_ca =
1009+ per_process::cli_options->per_isolate ->per_env ->use_system_ca ;
1010+ }
1011+ }
1012+ if (ssl_openssl_cert_store) {
9801013 CHECK_EQ (1 , X509_STORE_set_default_paths (store));
9811014 } else {
9821015 for (X509* cert : GetBundledRootCertificates ()) {
9831016 CHECK_EQ (1 , X509_STORE_add_cert (store, cert));
9841017 }
985- if (per_process::cli_options-> use_system_ca ) {
1018+ if (use_system_ca) {
9861019 for (X509* cert : GetSystemStoreCACertificates ()) {
9871020 CHECK_EQ (1 , X509_STORE_add_cert (store, cert));
9881021 }
@@ -1016,11 +1049,20 @@ void CleanupCachedRootCertificates() {
10161049 }
10171050 }
10181051
1019- // Serialize with starter to avoid the race window.
1020- Mutex::ScopedLock lock (start_cert_loading_thread_mutex);
1021- if (tried_cert_loading_off_thread.load () &&
1022- cert_loading_thread_started.load ()) {
1023- uv_thread_join (&cert_loading_thread);
1052+ // Serialize with starters to avoid the race window.
1053+ {
1054+ Mutex::ScopedLock lock (start_cert_loading_thread_mutex);
1055+ if (tried_cert_loading_off_thread.load () &&
1056+ cert_loading_thread_started.load ()) {
1057+ uv_thread_join (&cert_loading_thread);
1058+ }
1059+ }
1060+ {
1061+ Mutex::ScopedLock lock (start_system_cert_loading_thread_mutex);
1062+ if (tried_system_cert_loading_off_thread.load () &&
1063+ system_cert_loading_thread_started.load ()) {
1064+ uv_thread_join (&system_cert_loading_thread);
1065+ }
10241066 }
10251067}
10261068
@@ -1187,9 +1229,7 @@ void ResetRootCertStore(const FunctionCallbackInfo<Value>& args) {
11871229 X509_STORE_free (root_cert_store);
11881230 }
11891231
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 ();
1232+ root_cert_store = nullptr ;
11931233}
11941234
11951235void GetSystemCACertificates (const FunctionCallbackInfo<Value>& args) {
@@ -1700,11 +1740,12 @@ void SecureContext::SetX509StoreFlag(unsigned long flags) {
17001740}
17011741
17021742X509_STORE* SecureContext::GetCertStoreOwnedByThisSecureContext () {
1743+ Environment* env = this ->env ();
17031744 if (own_cert_store_cache_ != nullptr ) return own_cert_store_cache_;
17041745
17051746 X509_STORE* cert_store = SSL_CTX_get_cert_store (ctx_.get ());
1706- if (cert_store == GetOrCreateRootCertStore ()) {
1707- cert_store = NewRootCertStore ();
1747+ if (cert_store == GetOrCreateRootCertStore (env )) {
1748+ cert_store = NewRootCertStore (env );
17081749 SSL_CTX_set_cert_store (ctx_.get (), cert_store);
17091750 }
17101751
@@ -1777,7 +1818,8 @@ void SecureContext::AddCRL(const FunctionCallbackInfo<Value>& args) {
17771818
17781819void SecureContext::SetRootCerts () {
17791820 ClearErrorOnReturn clear_error_on_return;
1780- auto store = GetOrCreateRootCertStore ();
1821+ Environment* env = this ->env ();
1822+ auto store = GetOrCreateRootCertStore (env);
17811823
17821824 // Increment reference count so global store is not deleted along with CTX.
17831825 X509_STORE_up_ref (store);
0 commit comments