@@ -52,7 +52,7 @@ static const char* const root_certs[] = {
5252
5353static const char system_cert_path[] = NODE_OPENSSL_SYSTEM_CERT_PATH;
5454
55- static bool extra_root_certs_loaded = false ;
55+ static std::string extra_root_certs_file; // NOLINT(runtime/string)
5656
5757X509_STORE* GetOrCreateRootCertStore () {
5858 // Guaranteed thread-safe by standard, just don't use -fno-threadsafe-statics.
@@ -197,26 +197,66 @@ int SSL_CTX_use_certificate_chain(SSL_CTX* ctx,
197197 issuer);
198198}
199199
200+ unsigned long LoadCertsFromFile ( // NOLINT(runtime/int)
201+ std::vector<X509*>* certs,
202+ const char * file) {
203+ MarkPopErrorOnReturn mark_pop_error_on_return;
204+
205+ BIOPointer bio (BIO_new_file (file, " r" ));
206+ if (!bio) return ERR_get_error ();
207+
208+ while (X509* x509 = PEM_read_bio_X509 (
209+ bio.get (), nullptr , NoPasswordCallback, nullptr )) {
210+ certs->push_back (x509);
211+ }
212+
213+ unsigned long err = ERR_peek_last_error (); // NOLINT(runtime/int)
214+ // Ignore error if its EOF/no start line found.
215+ if (ERR_GET_LIB (err) == ERR_LIB_PEM &&
216+ ERR_GET_REASON (err) == PEM_R_NO_START_LINE) {
217+ return 0 ;
218+ } else {
219+ return err;
220+ }
221+ }
222+
200223X509_STORE* NewRootCertStore () {
201224 static std::vector<X509*> root_certs_vector;
225+ static bool root_certs_vector_loaded = false ;
202226 static Mutex root_certs_vector_mutex;
203227 Mutex::ScopedLock lock (root_certs_vector_mutex);
204228
205- if (root_certs_vector.empty () &&
206- per_process::cli_options->ssl_openssl_cert_store == false ) {
207- for (size_t i = 0 ; i < arraysize (root_certs); i++) {
208- X509* x509 =
209- PEM_read_bio_X509 (NodeBIO::NewFixed (root_certs[i],
210- strlen (root_certs[i])).get (),
211- nullptr , // no re-use of X509 structure
212- NoPasswordCallback,
213- nullptr ); // no callback data
229+ if (!root_certs_vector_loaded) {
230+ if (per_process::cli_options->ssl_openssl_cert_store == false ) {
231+ for (size_t i = 0 ; i < arraysize (root_certs); i++) {
232+ X509* x509 = PEM_read_bio_X509 (
233+ NodeBIO::NewFixed (root_certs[i], strlen (root_certs[i])).get (),
234+ nullptr , // no re-use of X509 structure
235+ NoPasswordCallback,
236+ nullptr ); // no callback data
237+
238+ // Parse errors from the built-in roots are fatal.
239+ CHECK_NOT_NULL (x509);
214240
215- // Parse errors from the built-in roots are fatal.
216- CHECK_NOT_NULL (x509);
241+ root_certs_vector.push_back (x509);
242+ }
243+ }
217244
218- root_certs_vector.push_back (x509);
245+ if (!extra_root_certs_file.empty ()) {
246+ unsigned long err = LoadCertsFromFile ( // NOLINT(runtime/int)
247+ &root_certs_vector,
248+ extra_root_certs_file.c_str ());
249+ if (err) {
250+ char buf[256 ];
251+ ERR_error_string_n (err, buf, sizeof (buf));
252+ fprintf (stderr,
253+ " Warning: Ignoring extra certs from `%s`, load failed: %s\n " ,
254+ extra_root_certs_file.c_str (),
255+ buf);
256+ }
219257 }
258+
259+ root_certs_vector_loaded = true ;
220260 }
221261
222262 X509_STORE* store = X509_STORE_new ();
@@ -228,12 +268,11 @@ X509_STORE* NewRootCertStore() {
228268
229269 Mutex::ScopedLock cli_lock (node::per_process::cli_options_mutex);
230270 if (per_process::cli_options->ssl_openssl_cert_store ) {
231- X509_STORE_set_default_paths (store);
232- } else {
233- for (X509* cert : root_certs_vector) {
234- X509_up_ref (cert);
235- X509_STORE_add_cert (store, cert);
236- }
271+ CHECK_EQ (1 , X509_STORE_set_default_paths (store));
272+ }
273+
274+ for (X509* cert : root_certs_vector) {
275+ CHECK_EQ (1 , X509_STORE_add_cert (store, cert));
237276 }
238277
239278 return store;
@@ -339,11 +378,6 @@ void SecureContext::Initialize(Environment* env, Local<Object> target) {
339378
340379 SetMethodNoSideEffect (
341380 context, target, " getRootCertificates" , GetRootCertificates);
342- // Exposed for testing purposes only.
343- SetMethodNoSideEffect (context,
344- target,
345- " isExtraRootCertsFileLoaded" ,
346- IsExtraRootCertsFileLoaded);
347381}
348382
349383void SecureContext::RegisterExternalReferences (
@@ -383,7 +417,6 @@ void SecureContext::RegisterExternalReferences(
383417 registry->Register (CtxGetter);
384418
385419 registry->Register (GetRootCertificates);
386- registry->Register (IsExtraRootCertsFileLoaded);
387420}
388421
389422SecureContext* SecureContext::Create (Environment* env) {
@@ -1383,54 +1416,9 @@ void SecureContext::GetCertificate(const FunctionCallbackInfo<Value>& args) {
13831416 args.GetReturnValue ().Set (buff);
13841417}
13851418
1386- namespace {
1387- unsigned long AddCertsFromFile ( // NOLINT(runtime/int)
1388- X509_STORE* store,
1389- const char * file) {
1390- ERR_clear_error ();
1391- MarkPopErrorOnReturn mark_pop_error_on_return;
1392-
1393- BIOPointer bio (BIO_new_file (file, " r" ));
1394- if (!bio)
1395- return ERR_get_error ();
1396-
1397- while (X509Pointer x509 = X509Pointer (PEM_read_bio_X509 (
1398- bio.get (), nullptr , NoPasswordCallback, nullptr ))) {
1399- X509_STORE_add_cert (store, x509.get ());
1400- }
1401-
1402- unsigned long err = ERR_peek_error (); // NOLINT(runtime/int)
1403- // Ignore error if its EOF/no start line found.
1404- if (ERR_GET_LIB (err) == ERR_LIB_PEM &&
1405- ERR_GET_REASON (err) == PEM_R_NO_START_LINE) {
1406- return 0 ;
1407- }
1408-
1409- return err;
1410- }
1411- } // namespace
1412-
14131419// UseExtraCaCerts is called only once at the start of the Node.js process.
14141420void UseExtraCaCerts (const std::string& file) {
1415- if (file.empty ()) return ;
1416- ClearErrorOnReturn clear_error_on_return;
1417- X509_STORE* store = GetOrCreateRootCertStore ();
1418- if (auto err = AddCertsFromFile (store, file.c_str ())) {
1419- char buf[256 ];
1420- ERR_error_string_n (err, buf, sizeof (buf));
1421- fprintf (stderr,
1422- " Warning: Ignoring extra certs from `%s`, load failed: %s\n " ,
1423- file.c_str (),
1424- buf);
1425- } else {
1426- extra_root_certs_loaded = true ;
1427- }
1428- }
1429-
1430- // Exposed to JavaScript strictly for testing purposes.
1431- void IsExtraRootCertsFileLoaded (
1432- const FunctionCallbackInfo<Value>& args) {
1433- return args.GetReturnValue ().Set (extra_root_certs_loaded);
1421+ extra_root_certs_file = file;
14341422}
14351423
14361424} // namespace crypto
0 commit comments