@@ -111,9 +111,11 @@ static const char* const root_certs[] = {
111111
112112static const char system_cert_path[] = NODE_OPENSSL_SYSTEM_CERT_PATH;
113113
114- static X509_STORE* root_cert_store;
115- static std::vector<X509*> root_certs_vector;
114+ static X509StorePointer root_cert_store;
115+
116+ static std::vector<X509Pointer> root_certs_vector;
116117static Mutex root_certs_vector_mutex;
118+ static bool root_certs_vector_loaded = false ;
117119
118120static bool extra_root_certs_loaded = false ;
119121
@@ -973,37 +975,39 @@ void SecureContext::SetCert(const FunctionCallbackInfo<Value>& args) {
973975static void EnsureRootCerts () {
974976 Mutex::ScopedLock lock (root_certs_vector_mutex);
975977
976- if (root_certs_vector. empty () ) {
978+ if (!root_certs_vector_loaded ) {
977979 for (size_t i = 0 ; i < arraysize (root_certs); i++) {
978- X509* x509 =
980+ X509Pointer x509 = X509Pointer (
979981 PEM_read_bio_X509 (NodeBIO::NewFixed (root_certs[i],
980982 strlen (root_certs[i])).get (),
981983 nullptr , // no re-use of X509 structure
982984 NoPasswordCallback,
983- nullptr ); // no callback data
985+ nullptr )) ; // no callback data
984986
985987 // Parse errors from the built-in roots are fatal.
986988 CHECK_NOT_NULL (x509);
987989
988- root_certs_vector.push_back (x509);
990+ root_certs_vector.push_back (std::move ( x509) );
989991 }
992+
993+ root_certs_vector_loaded = true ;
990994 }
991995}
992996
993997
994- static X509_STORE* NewRootCertStore () {
998+ static X509StorePointer NewRootCertStore () {
995999 EnsureRootCerts ();
9961000
997- X509_STORE* store = X509_STORE_new ();
1001+ X509StorePointer store = X509StorePointer ( X509_STORE_new () );
9981002 if (*system_cert_path != ' \0 ' ) {
999- X509_STORE_load_locations (store, system_cert_path, nullptr );
1003+ X509_STORE_load_locations (store. get () , system_cert_path, nullptr );
10001004 }
10011005 if (per_process::cli_options->ssl_openssl_cert_store ) {
1002- X509_STORE_set_default_paths (store);
1006+ X509_STORE_set_default_paths (store. get () );
10031007 } else {
10041008 Mutex::ScopedLock lock (root_certs_vector_mutex);
1005- for (X509* cert : root_certs_vector) {
1006- X509_STORE_add_cert (store, cert);
1009+ for (X509Pointer& cert : root_certs_vector) {
1010+ X509_STORE_add_cert (store. get () , cert. get () );
10071011 }
10081012 }
10091013
@@ -1027,15 +1031,14 @@ void SecureContext::AddCACert(const FunctionCallbackInfo<Value>& args) {
10271031 return ;
10281032
10291033 X509_STORE* cert_store = SSL_CTX_get_cert_store (sc->ctx_ .get ());
1030- while (X509* x509 = PEM_read_bio_X509_AUX (
1031- bio.get (), nullptr , NoPasswordCallback, nullptr )) {
1032- if (cert_store == root_cert_store) {
1033- cert_store = NewRootCertStore ();
1034+ while (X509Pointer x509 = X509Pointer ( PEM_read_bio_X509_AUX (
1035+ bio.get (), nullptr , NoPasswordCallback, nullptr ))) {
1036+ if (cert_store == root_cert_store. get () ) {
1037+ cert_store = NewRootCertStore (). release () ;
10341038 SSL_CTX_set_cert_store (sc->ctx_ .get (), cert_store);
10351039 }
1036- X509_STORE_add_cert (cert_store, x509);
1037- SSL_CTX_add_client_CA (sc->ctx_ .get (), x509);
1038- X509_free (x509);
1040+ X509_STORE_add_cert (cert_store, x509.get ());
1041+ SSL_CTX_add_client_CA (sc->ctx_ .get (), x509.get ());
10391042 }
10401043}
10411044
@@ -1063,8 +1066,8 @@ void SecureContext::AddCRL(const FunctionCallbackInfo<Value>& args) {
10631066 return env->ThrowError (" Failed to parse CRL" );
10641067
10651068 X509_STORE* cert_store = SSL_CTX_get_cert_store (sc->ctx_ .get ());
1066- if (cert_store == root_cert_store) {
1067- cert_store = NewRootCertStore ();
1069+ if (cert_store == root_cert_store. get () ) {
1070+ cert_store = NewRootCertStore (). release () ;
10681071 SSL_CTX_set_cert_store (sc->ctx_ .get (), cert_store);
10691072 }
10701073
@@ -1086,10 +1089,10 @@ static unsigned long AddRootCertsFromFile( // NOLINT(runtime/int)
10861089 // Scope for root_certs_vector lock
10871090 {
10881091 Mutex::ScopedLock lock (root_certs_vector_mutex);
1089- while (X509* x509 =
1090- PEM_read_bio_X509 (bio.get (), nullptr , NoPasswordCallback, nullptr )) {
1091- X509_STORE_add_cert (root_cert_store, x509);
1092- root_certs_vector.push_back (x509);
1092+ while (X509Pointer x509 = X509Pointer (
1093+ PEM_read_bio_X509 (bio.get (), nullptr , NoPasswordCallback, nullptr ))) {
1094+ X509_STORE_add_cert (root_cert_store. get () , x509. get () );
1095+ root_certs_vector.push_back (std::move ( x509) );
10931096 }
10941097 }
10951098
@@ -1142,8 +1145,8 @@ void SecureContext::AddRootCerts(const FunctionCallbackInfo<Value>& args) {
11421145 }
11431146
11441147 // Increment reference count so global store is not deleted along with CTX.
1145- X509_STORE_up_ref (root_cert_store);
1146- SSL_CTX_set_cert_store (sc->ctx_ .get (), root_cert_store);
1148+ X509_STORE_up_ref (root_cert_store. get () );
1149+ SSL_CTX_set_cert_store (sc->ctx_ .get (), root_cert_store. get () );
11471150}
11481151
11491152
@@ -1442,8 +1445,8 @@ void SecureContext::LoadPKCS12(const FunctionCallbackInfo<Value>& args) {
14421445 for (int i = 0 ; i < sk_X509_num (extra_certs.get ()); i++) {
14431446 X509* ca = sk_X509_value (extra_certs.get (), i);
14441447
1445- if (cert_store == root_cert_store) {
1446- cert_store = NewRootCertStore ();
1448+ if (cert_store == root_cert_store. get () ) {
1449+ cert_store = NewRootCertStore (). release () ;
14471450 SSL_CTX_set_cert_store (sc->ctx_ .get (), cert_store);
14481451 }
14491452 X509_STORE_add_cert (cert_store, ca);
@@ -6648,9 +6651,9 @@ void GetRootCertificates(const FunctionCallbackInfo<Value>& args) {
66486651
66496652 result.reserve (root_certs_vector.size ());
66506653
6651- for (X509* cert : root_certs_vector) {
6654+ for (X509Pointer& cert : root_certs_vector) {
66526655 Local<Value> value;
6653- if (!X509ToPEM (env, cert).ToLocal (&value))
6656+ if (!X509ToPEM (env, cert. get () ).ToLocal (&value))
66546657 return ;
66556658
66566659 result.push_back (value);
@@ -6662,6 +6665,55 @@ void GetRootCertificates(const FunctionCallbackInfo<Value>& args) {
66626665}
66636666
66646667
6668+ void SetRootCertificates (const FunctionCallbackInfo<Value>& args) {
6669+ Environment* env = Environment::GetCurrent (args);
6670+
6671+ // Single argument must be an array of PEM-formatted strings.
6672+ CHECK_EQ (args.Length (), 1 );
6673+ CHECK (args[0 ]->IsArray ());
6674+ Local<Array> arr = args[0 ].As <Array>();
6675+ size_t len = arr->Length ();
6676+
6677+ ERR_clear_error ();
6678+ ClearErrorOnReturn clear_error_on_return;
6679+
6680+ std::vector<X509Pointer> certs;
6681+ certs.reserve (len);
6682+
6683+ for (size_t i = 0 ; i < len; i++) {
6684+ // All array values must be strings.
6685+ Local<Value> value;
6686+ if (!arr->Get (env->context (), i).ToLocal (&value)) return ;
6687+ CHECK (value->IsString ());
6688+
6689+ BIOPointer bio (LoadBIO (env, value));
6690+ if (!bio) return ThrowCryptoError (env, ERR_get_error (), " LoadBIO" );
6691+
6692+ // Each string can contain multiple PEM-formatted certificates.
6693+ while (X509Pointer x509 = X509Pointer (
6694+ PEM_read_bio_X509 (bio.get (), nullptr , NoPasswordCallback, nullptr ))) {
6695+ certs.push_back (std::move (x509));
6696+ }
6697+
6698+ // Ignore error if its EOF/no start line found.
6699+ unsigned long err = ERR_peek_last_error (); // NOLINT(runtime/int)
6700+ if (ERR_GET_LIB (err) != ERR_LIB_PEM ||
6701+ ERR_GET_REASON (err) != PEM_R_NO_START_LINE) {
6702+ return ThrowCryptoError (env, err, " PEM_read_bio_X509" );
6703+ }
6704+ }
6705+
6706+ // Scope for root_certs_vector lock
6707+ {
6708+ Mutex::ScopedLock lock (root_certs_vector_mutex);
6709+ root_certs_vector = std::move (certs);
6710+ }
6711+
6712+ // A new root_cert_store will be built on next use.
6713+ root_cert_store = nullptr ;
6714+ }
6715+
6716+
66656717// Convert the input public key to compressed, uncompressed, or hybrid formats.
66666718void ConvertKey (const FunctionCallbackInfo<Value>& args) {
66676719 MarkPopErrorOnReturn mark_pop_error_on_return;
@@ -6878,6 +6930,7 @@ void Initialize(Local<Object> target,
68786930 env->SetMethodNoSideEffect (target, " certExportChallenge" , ExportChallenge);
68796931 env->SetMethodNoSideEffect (target, " getRootCertificates" ,
68806932 GetRootCertificates);
6933+ env->SetMethod (target, " setRootCertificates" , SetRootCertificates);
68816934 // Exposed for testing purposes only.
68826935 env->SetMethodNoSideEffect (target, " isExtraRootCertsFileLoaded" ,
68836936 IsExtraRootCertsFileLoaded);
0 commit comments