2727#include  < wincrypt.h> 
2828#endif 
2929
30+ #include  < set> 
31+ 
3032namespace  node  {
3133
3234using  ncrypto::BignumPointer;
@@ -83,10 +85,28 @@ static std::atomic<bool> has_cached_bundled_root_certs{false};
8385static  std::atomic<bool > has_cached_system_root_certs{false };
8486static  std::atomic<bool > has_cached_extra_root_certs{false };
8587
88+ //  Used for sets of X509.
89+ struct  X509Less  {
90+   bool  operator ()(const  X509* lhs, const  X509* rhs) const  noexcept  {
91+     return  X509_cmp (const_cast <X509*>(lhs), const_cast <X509*>(rhs)) < 0 ;
92+   }
93+ };
94+ using  X509Set = std::set<X509*, X509Less>;
95+ 
96+ //  Per-thread root cert store. See NewRootCertStore() on what it contains.
97+ static  thread_local  X509_STORE* root_cert_store = nullptr ;
98+ //  If the user calls tls.setDefaultCACertificates() this will be used
99+ //  to hold the user-provided certificates, the root_cert_store and any new
100+ //  copy generated by NewRootCertStore() will then contain the certificates
101+ //  from this set.
102+ static  thread_local  std::unique_ptr<X509Set> root_certs_from_users;
103+ 
86104X509_STORE* GetOrCreateRootCertStore () {
87-   //  Guaranteed thread-safe by standard, just don't use -fno-threadsafe-statics.
88-   static  X509_STORE* store = NewRootCertStore ();
89-   return  store;
105+   if  (root_cert_store != nullptr ) {
106+     return  root_cert_store;
107+   }
108+   root_cert_store = NewRootCertStore ();
109+   return  root_cert_store;
90110}
91111
92112//  Takes a string or buffer and loads it into a BIO.
@@ -227,14 +247,11 @@ int SSL_CTX_use_certificate_chain(SSL_CTX* ctx,
227247                                       issuer);
228248}
229249
230- static  unsigned  long  LoadCertsFromFile (  //  NOLINT(runtime/int)
250+ static  unsigned  long  LoadCertsFromBIO (  //  NOLINT(runtime/int)
231251    std::vector<X509*>* certs,
232-     const   char * file ) {
252+     BIOPointer bio ) {
233253  MarkPopErrorOnReturn mark_pop_error_on_return;
234254
235-   auto  bio = BIOPointer::NewFile (file, " r" 
236-   if  (!bio) return  ERR_get_error ();
237- 
238255  while  (X509* x509 = PEM_read_bio_X509 (
239256             bio.get (), nullptr , NoPasswordCallback, nullptr )) {
240257    certs->push_back (x509);
@@ -250,6 +267,17 @@ static unsigned long LoadCertsFromFile(  // NOLINT(runtime/int)
250267  }
251268}
252269
270+ static  unsigned  long  LoadCertsFromFile (  //  NOLINT(runtime/int)
271+     std::vector<X509*>* certs,
272+     const  char * file) {
273+   MarkPopErrorOnReturn mark_pop_error_on_return;
274+ 
275+   auto  bio = BIOPointer::NewFile (file, " r" 
276+   if  (!bio) return  ERR_get_error ();
277+ 
278+   return  LoadCertsFromBIO (certs, std::move (bio));
279+ }
280+ 
253281//  Indicates the trust status of a certificate.
254282enum  class  TrustStatus  {
255283  //  Trust status is unknown / uninitialized.
@@ -831,11 +859,24 @@ static std::vector<X509*>& GetExtraCACertificates() {
831859//     NODE_EXTRA_CA_CERTS are cached after first load. Certificates
832860//     from --use-system-ca are not cached and always reloaded from
833861//     disk.
862+ //  8. If users have reset the root cert store by calling
863+ //     tls.setDefaultCACertificates(), the store will be populated with
864+ //     the certificates provided by users.
834865//  TODO(joyeecheung): maybe these rules need a bit of consolidation?
835866X509_STORE* NewRootCertStore () {
836867  X509_STORE* store = X509_STORE_new ();
837868  CHECK_NOT_NULL (store);
838869
870+   //  If the root cert store is already reset by users through
871+   //  tls.setDefaultCACertificates(), just create a copy from the
872+   //  user-provided certificates.
873+   if  (root_certs_from_users != nullptr ) {
874+     for  (X509* cert : *root_certs_from_users) {
875+       CHECK_EQ (1 , X509_STORE_add_cert (store, cert));
876+     }
877+     return  store;
878+   }
879+ 
839880#ifdef  NODE_OPENSSL_SYSTEM_CERT_PATH
840881  if  constexpr  (sizeof (NODE_OPENSSL_SYSTEM_CERT_PATH) > 1 ) {
841882    ERR_set_mark ();
@@ -903,14 +944,57 @@ void GetBundledRootCertificates(const FunctionCallbackInfo<Value>& args) {
903944      Array::New (env->isolate (), result, arraysize (root_certs)));
904945}
905946
947+ bool  ArrayOfStringsToX509s (Local<Context> context,
948+                            Local<Array> cert_array,
949+                            std::vector<X509*>* certs) {
950+   ClearErrorOnReturn clear_error_on_return;
951+   Isolate* isolate = context->GetIsolate ();
952+   Environment* env = Environment::GetCurrent (context);
953+   uint32_t  array_length = cert_array->Length ();
954+ 
955+   std::vector<v8::Global<Value>> cert_items;
956+   if  (FromV8Array (context, cert_array, &cert_items).IsNothing ()) {
957+     return  false ;
958+   }
959+ 
960+   for  (uint32_t  i = 0 ; i < array_length; i++) {
961+     Local<Value> cert_val = cert_items[i].Get (isolate);
962+     //  Parse the PEM certificate.
963+     BIOPointer bio (LoadBIO (env, cert_val));
964+     if  (!bio) {
965+       ThrowCryptoError (env, ERR_get_error (), " Failed to load certificate data" 
966+       return  false ;
967+     }
968+ 
969+     //  Read all certificates from this PEM string
970+     size_t  start = certs->size ();
971+     auto  err = LoadCertsFromBIO (certs, std::move (bio));
972+     if  (err != 0 ) {
973+       size_t  end = certs->size ();
974+       //  Clean up any certificates we've already parsed upon failure.
975+       for  (size_t  j = start; j < end; ++j) {
976+         X509_free ((*certs)[j]);
977+       }
978+       ThrowCryptoError (env, err, " Failed to parse certificate" 
979+       return  false ;
980+     }
981+   }
982+ 
983+   return  true ;
984+ }
985+ 
986+ template  <typename  It>
906987MaybeLocal<Array> X509sToArrayOfStrings (Environment* env,
907-                                         const  std::vector<X509*>& certs) {
988+                                         It first,
989+                                         It last,
990+                                         size_t  size) {
908991  ClearErrorOnReturn clear_error_on_return;
909992  EscapableHandleScope scope (env->isolate ());
910993
911-   LocalVector<Value> result (env->isolate (), certs.size ());
912-   for  (size_t  i = 0 ; i < certs.size (); ++i) {
913-     X509View view (certs[i]);
994+   LocalVector<Value> result (env->isolate (), size);
995+   size_t  i = 0 ;
996+   for  (It cur = first; cur != last; ++cur, ++i) {
997+     X509View view (*cur);
914998    auto  pem_bio = view.toPEM ();
915999    if  (!pem_bio) {
9161000      ThrowCryptoError (env, ERR_get_error (), " X509 to PEM conversion" 
@@ -935,10 +1019,87 @@ MaybeLocal<Array> X509sToArrayOfStrings(Environment* env,
9351019  return  scope.Escape (Array::New (env->isolate (), result.data (), result.size ()));
9361020}
9371021
1022+ void  GetUserRootCertificates (const  FunctionCallbackInfo<Value>& args) {
1023+   Environment* env = Environment::GetCurrent (args);
1024+   CHECK_NOT_NULL (root_certs_from_users);
1025+   Local<Array> results;
1026+   if  (X509sToArrayOfStrings (env,
1027+                             root_certs_from_users->begin (),
1028+                             root_certs_from_users->end (),
1029+                             root_certs_from_users->size ())
1030+           .ToLocal (&results)) {
1031+     args.GetReturnValue ().Set (results);
1032+   }
1033+ }
1034+ 
1035+ void  ResetRootCertStore (const  FunctionCallbackInfo<Value>& args) {
1036+   Local<Context> context = args.GetIsolate ()->GetCurrentContext ();
1037+   CHECK (args[0 ]->IsArray ());
1038+   Local<Array> cert_array = args[0 ].As <Array>();
1039+ 
1040+   if  (cert_array->Length () == 0 ) {
1041+     //  If the array is empty, just clear the user certs and reset the store.
1042+     if  (root_cert_store != nullptr ) {
1043+       X509_STORE_free (root_cert_store);
1044+       root_cert_store = nullptr ;
1045+     }
1046+ 
1047+     //  Free any existing certificates in the old set.
1048+     if  (root_certs_from_users != nullptr ) {
1049+       for  (X509* cert : *root_certs_from_users) {
1050+         X509_free (cert);
1051+       }
1052+     }
1053+     root_certs_from_users = std::make_unique<X509Set>();
1054+     return ;
1055+   }
1056+ 
1057+   //  Parse certificates from the array
1058+   std::unique_ptr<std::vector<X509*>> certs =
1059+       std::make_unique<std::vector<X509*>>();
1060+   if  (!ArrayOfStringsToX509s (context, cert_array, certs.get ())) {
1061+     //  Error already thrown by ArrayOfStringsToX509s
1062+     return ;
1063+   }
1064+ 
1065+   if  (certs->empty ()) {
1066+     Environment* env = Environment::GetCurrent (context);
1067+     return  THROW_ERR_CRYPTO_OPERATION_FAILED (
1068+         env, " No valid certificates found in the provided array" 
1069+   }
1070+ 
1071+   auto  new_set = std::make_unique<X509Set>();
1072+   for  (X509* cert : *certs) {
1073+     auto  [it, inserted] = new_set->insert (cert);
1074+     if  (!inserted) {  //  Free duplicate certificates from the vector.
1075+       X509_free (cert);
1076+     }
1077+   }
1078+ 
1079+   //  Free any existing certificates in the old set.
1080+   if  (root_certs_from_users != nullptr ) {
1081+     for  (X509* cert : *root_certs_from_users) {
1082+       X509_free (cert);
1083+     }
1084+   }
1085+   std::swap (root_certs_from_users, new_set);
1086+ 
1087+   //  Reset the global root cert store and create a new one with the
1088+   //  certificates.
1089+   if  (root_cert_store != nullptr ) {
1090+     X509_STORE_free (root_cert_store);
1091+   }
1092+ 
1093+   //  TODO(joyeecheung): we can probably just reset it to nullptr
1094+   //  and let the next call to NewRootCertStore() create a new one.
1095+   root_cert_store = NewRootCertStore ();
1096+ }
1097+ 
9381098void  GetSystemCACertificates (const  FunctionCallbackInfo<Value>& args) {
9391099  Environment* env = Environment::GetCurrent (args);
9401100  Local<Array> results;
941-   if  (X509sToArrayOfStrings (env, GetSystemStoreCACertificates ())
1101+   std::vector<X509*>& certs = GetSystemStoreCACertificates ();
1102+   if  (X509sToArrayOfStrings (env, certs.begin (), certs.end (), certs.size ())
9421103          .ToLocal (&results)) {
9431104    args.GetReturnValue ().Set (results);
9441105  }
@@ -950,7 +1111,9 @@ void GetExtraCACertificates(const FunctionCallbackInfo<Value>& args) {
9501111    return  args.GetReturnValue ().Set (Array::New (env->isolate ()));
9511112  }
9521113  Local<Array> results;
953-   if  (X509sToArrayOfStrings (env, GetExtraCACertificates ()).ToLocal (&results)) {
1114+   std::vector<X509*>& certs = GetExtraCACertificates ();
1115+   if  (X509sToArrayOfStrings (env, certs.begin (), certs.end (), certs.size ())
1116+           .ToLocal (&results)) {
9541117    args.GetReturnValue ().Set (results);
9551118  }
9561119}
@@ -1046,6 +1209,9 @@ void SecureContext::Initialize(Environment* env, Local<Object> target) {
10461209      context, target, " getSystemCACertificates" 
10471210  SetMethodNoSideEffect (
10481211      context, target, " getExtraCACertificates" 
1212+   SetMethod (context, target, " resetRootCertStore" 
1213+   SetMethodNoSideEffect (
1214+       context, target, " getUserRootCertificates" 
10491215}
10501216
10511217void  SecureContext::RegisterExternalReferences (
@@ -1088,6 +1254,8 @@ void SecureContext::RegisterExternalReferences(
10881254  registry->Register (GetBundledRootCertificates);
10891255  registry->Register (GetSystemCACertificates);
10901256  registry->Register (GetExtraCACertificates);
1257+   registry->Register (ResetRootCertStore);
1258+   registry->Register (GetUserRootCertificates);
10911259}
10921260
10931261SecureContext* SecureContext::Create (Environment* env) {
0 commit comments