@@ -52,7 +52,7 @@ static const char* const root_certs[] = {
52
52
53
53
static const char system_cert_path[] = NODE_OPENSSL_SYSTEM_CERT_PATH;
54
54
55
- static bool extra_root_certs_loaded = false ;
55
+ static std::string extra_root_certs_file; // NOLINT(runtime/string)
56
56
57
57
X509_STORE* GetOrCreateRootCertStore () {
58
58
// 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,
197
197
issuer);
198
198
}
199
199
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
+
200
223
X509_STORE* NewRootCertStore () {
201
224
static std::vector<X509*> root_certs_vector;
225
+ static bool root_certs_vector_loaded = false ;
202
226
static Mutex root_certs_vector_mutex;
203
227
Mutex::ScopedLock lock (root_certs_vector_mutex);
204
228
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);
214
240
215
- // Parse errors from the built-in roots are fatal.
216
- CHECK_NOT_NULL (x509);
241
+ root_certs_vector.push_back (x509);
242
+ }
243
+ }
217
244
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
+ }
219
257
}
258
+
259
+ root_certs_vector_loaded = true ;
220
260
}
221
261
222
262
X509_STORE* store = X509_STORE_new ();
@@ -228,12 +268,11 @@ X509_STORE* NewRootCertStore() {
228
268
229
269
Mutex::ScopedLock cli_lock (node::per_process::cli_options_mutex);
230
270
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));
237
276
}
238
277
239
278
return store;
@@ -339,11 +378,6 @@ void SecureContext::Initialize(Environment* env, Local<Object> target) {
339
378
340
379
SetMethodNoSideEffect (
341
380
context, target, " getRootCertificates" , GetRootCertificates);
342
- // Exposed for testing purposes only.
343
- SetMethodNoSideEffect (context,
344
- target,
345
- " isExtraRootCertsFileLoaded" ,
346
- IsExtraRootCertsFileLoaded);
347
381
}
348
382
349
383
void SecureContext::RegisterExternalReferences (
@@ -383,7 +417,6 @@ void SecureContext::RegisterExternalReferences(
383
417
registry->Register (CtxGetter);
384
418
385
419
registry->Register (GetRootCertificates);
386
- registry->Register (IsExtraRootCertsFileLoaded);
387
420
}
388
421
389
422
SecureContext* SecureContext::Create (Environment* env) {
@@ -1383,54 +1416,9 @@ void SecureContext::GetCertificate(const FunctionCallbackInfo<Value>& args) {
1383
1416
args.GetReturnValue ().Set (buff);
1384
1417
}
1385
1418
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
-
1413
1419
// UseExtraCaCerts is called only once at the start of the Node.js process.
1414
1420
void 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;
1434
1422
}
1435
1423
1436
1424
} // namespace crypto
0 commit comments