@@ -40,7 +40,6 @@ using ncrypto::MarkPopErrorOnReturn;
40
40
using ncrypto::SSLPointer;
41
41
using ncrypto::StackOfX509;
42
42
using ncrypto::X509Pointer;
43
- using ncrypto::X509View;
44
43
using v8::Array;
45
44
using v8::ArrayBufferView;
46
45
using v8::Boolean ;
@@ -74,6 +73,10 @@ static const char system_cert_path[] = NODE_OPENSSL_SYSTEM_CERT_PATH;
74
73
75
74
static std::string extra_root_certs_file; // NOLINT(runtime/string)
76
75
76
+ static std::atomic<bool > has_cached_bundled_root_certs{false };
77
+ static std::atomic<bool > has_cached_system_root_certs{false };
78
+ static std::atomic<bool > has_cached_extra_root_certs{false };
79
+
77
80
X509_STORE* GetOrCreateRootCertStore () {
78
81
// Guaranteed thread-safe by standard, just don't use -fno-threadsafe-statics.
79
82
static X509_STORE* store = NewRootCertStore ();
@@ -261,35 +264,6 @@ bool isSelfIssued(X509* cert) {
261
264
return X509_NAME_cmp (subject, issuer) == 0 ;
262
265
}
263
266
264
- // TODO(joyeecheung): it is a bit excessive to do this X509 -> PEM -> X509
265
- // dance when we could've just pass everything around in binary. Change the
266
- // root_certs to be embedded as DER so that we can save the serialization
267
- // and deserialization.
268
- void X509VectorToPEMVector (const std::vector<X509Pointer>& src,
269
- std::vector<std::string>* dest) {
270
- for (size_t i = 0 ; i < src.size (); i++) {
271
- X509View x509_view (src[i].get ());
272
-
273
- auto pem_bio = x509_view.toPEM ();
274
- if (!pem_bio) {
275
- fprintf (stderr,
276
- " Warning: converting system certificate to PEM format failed\n " );
277
- continue ;
278
- }
279
-
280
- char * pem_data = nullptr ;
281
- auto pem_size = BIO_get_mem_data (pem_bio.get (), &pem_data);
282
- if (pem_size <= 0 || !pem_data) {
283
- fprintf (
284
- stderr,
285
- " Warning: cannot read PEM-encoded data from system certificate\n " );
286
- continue ;
287
- }
288
-
289
- dest->emplace_back (pem_data, pem_size);
290
- }
291
- }
292
-
293
267
// The following code is loosely based on
294
268
// https://github.com/chromium/chromium/blob/54bd8e3/net/cert/internal/trust_store_mac.cc
295
269
// and
@@ -482,7 +456,7 @@ bool IsCertificateTrustedForPolicy(X509* cert, SecCertificateRef ref) {
482
456
}
483
457
484
458
void ReadMacOSKeychainCertificates (
485
- std::vector<std::string >* system_root_certificates ) {
459
+ std::vector<X509* >* system_root_certificates_X509 ) {
486
460
CFTypeRef search_keys[] = {kSecClass , kSecMatchLimit , kSecReturnRef };
487
461
CFTypeRef search_values[] = {
488
462
kSecClassCertificate , kSecMatchLimitAll , kCFBooleanTrue };
@@ -504,7 +478,6 @@ void ReadMacOSKeychainCertificates(
504
478
505
479
CFIndex count = CFArrayGetCount (curr_anchors);
506
480
507
- std::vector<X509Pointer> system_root_certificates_X509;
508
481
for (int i = 0 ; i < count; ++i) {
509
482
SecCertificateRef cert_ref = reinterpret_cast <SecCertificateRef>(
510
483
const_cast <void *>(CFArrayGetValueAtIndex (curr_anchors, i)));
@@ -521,13 +494,10 @@ void ReadMacOSKeychainCertificates(
521
494
CFRelease (der_data);
522
495
bool is_valid = IsCertificateTrustedForPolicy (cert, cert_ref);
523
496
if (is_valid) {
524
- system_root_certificates_X509. emplace_back (cert);
497
+ system_root_certificates_X509-> emplace_back (cert);
525
498
}
526
499
}
527
500
CFRelease (curr_anchors);
528
-
529
- X509VectorToPEMVector (system_root_certificates_X509,
530
- system_root_certificates);
531
501
}
532
502
#endif // __APPLE__
533
503
@@ -596,7 +566,7 @@ bool IsCertTrustedForServerAuth(PCCERT_CONTEXT cert) {
596
566
return false ;
597
567
}
598
568
599
- void GatherCertsForLocation (std::vector<X509Pointer >* vector,
569
+ void GatherCertsForLocation (std::vector<X509* >* vector,
600
570
DWORD location,
601
571
LPCWSTR store_name) {
602
572
if (!(location == CERT_SYSTEM_STORE_LOCAL_MACHINE ||
@@ -639,114 +609,142 @@ void GatherCertsForLocation(std::vector<X509Pointer>* vector,
639
609
}
640
610
641
611
void ReadWindowsCertificates (
642
- std::vector<std::string>* system_root_certificates) {
643
- std::vector<X509Pointer> system_root_certificates_X509;
612
+ std::vector<X509*>* system_root_certificates_X509) {
644
613
// TODO(joyeecheung): match Chromium's policy, collect more certificates
645
614
// from user-added CAs and support disallowed (revoked) certificates.
646
615
647
616
// Grab the user-added roots.
648
617
GatherCertsForLocation (
649
- & system_root_certificates_X509, CERT_SYSTEM_STORE_LOCAL_MACHINE, L" ROOT" );
650
- GatherCertsForLocation (& system_root_certificates_X509,
618
+ system_root_certificates_X509, CERT_SYSTEM_STORE_LOCAL_MACHINE, L" ROOT" );
619
+ GatherCertsForLocation (system_root_certificates_X509,
651
620
CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY,
652
621
L" ROOT" );
653
- GatherCertsForLocation (& system_root_certificates_X509,
622
+ GatherCertsForLocation (system_root_certificates_X509,
654
623
CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE,
655
624
L" ROOT" );
656
625
GatherCertsForLocation (
657
- & system_root_certificates_X509, CERT_SYSTEM_STORE_CURRENT_USER, L" ROOT" );
658
- GatherCertsForLocation (& system_root_certificates_X509,
626
+ system_root_certificates_X509, CERT_SYSTEM_STORE_CURRENT_USER, L" ROOT" );
627
+ GatherCertsForLocation (system_root_certificates_X509,
659
628
CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY,
660
629
L" ROOT" );
661
630
662
631
// Grab the user-added trusted server certs. Trusted end-entity certs are
663
632
// only allowed for server auth in the "local machine" store, but not in the
664
633
// "current user" store.
665
- GatherCertsForLocation (& system_root_certificates_X509,
634
+ GatherCertsForLocation (system_root_certificates_X509,
666
635
CERT_SYSTEM_STORE_LOCAL_MACHINE,
667
636
L" TrustedPeople" );
668
- GatherCertsForLocation (& system_root_certificates_X509,
637
+ GatherCertsForLocation (system_root_certificates_X509,
669
638
CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY,
670
639
L" TrustedPeople" );
671
- GatherCertsForLocation (& system_root_certificates_X509,
640
+ GatherCertsForLocation (system_root_certificates_X509,
672
641
CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE,
673
642
L" TrustedPeople" );
674
-
675
- X509VectorToPEMVector (system_root_certificates_X509,
676
- system_root_certificates);
677
643
}
678
644
#endif
679
645
680
- void ReadSystemStoreCertificates (
681
- std::vector<std::string>* system_root_certificates) {
682
- # ifdef __APPLE__
683
- ReadMacOSKeychainCertificates (system_root_certificates) ;
684
- # endif
685
- # ifdef _WIN32
686
- ReadWindowsCertificates (system_root_certificates);
687
- # endif
688
- }
689
-
690
- std::vector<std::string> getCombinedRootCertificates () {
691
- std::vector<std::string> combined_root_certs;
646
+ static std::vector<X509*> InitializeBundledRootCertificates () {
647
+ // Read the bundled certificates in node_root_certs.h into
648
+ // bundled_root_certs_vector.
649
+ std::vector<X509*> bundled_root_certs ;
650
+ size_t bundled_root_cert_count = arraysize (root_certs);
651
+ bundled_root_certs. reserve (bundled_root_cert_count);
652
+ for ( size_t i = 0 ; i < bundled_root_cert_count; i++) {
653
+ X509* x509 = PEM_read_bio_X509 (
654
+ NodeBIO::NewFixed (root_certs[i], strlen (root_certs[i])). get (),
655
+ nullptr , // no re-use of X509 structure
656
+ NoPasswordCallback,
657
+ nullptr ); // no callback data
692
658
693
- for (size_t i = 0 ; i < arraysize (root_certs); i++) {
694
- combined_root_certs.emplace_back (root_certs[i]);
695
- }
659
+ // Parse errors from the built-in roots are fatal.
660
+ CHECK_NOT_NULL (x509);
696
661
697
- if (per_process::cli_options->use_system_ca ) {
698
- ReadSystemStoreCertificates (&combined_root_certs);
662
+ bundled_root_certs.push_back (x509);
699
663
}
664
+ return bundled_root_certs;
665
+ }
700
666
701
- return combined_root_certs;
667
+ // TODO(joyeecheung): it is a bit excessive to do this PEM -> X509
668
+ // dance when we could've just pass everything around in binary. Change the
669
+ // root_certs to be embedded as DER so that we can save the serialization
670
+ // and deserialization.
671
+ static std::vector<X509*>& GetBundledRootCertificates () {
672
+ // Use function-local static to guarantee thread safety.
673
+ static std::vector<X509*> bundled_root_certs =
674
+ InitializeBundledRootCertificates ();
675
+ has_cached_bundled_root_certs.store (true );
676
+ return bundled_root_certs;
702
677
}
703
678
679
+ static std::vector<X509*> InitializeSystemStoreCertificates () {
680
+ std::vector<X509*> system_store_certs;
681
+ #ifdef __APPLE__
682
+ ReadMacOSKeychainCertificates (&system_store_certs);
683
+ #endif
684
+ #ifdef _WIN32
685
+ ReadWindowsCertificates (&system_store_certs);
686
+ #endif
687
+ return system_store_certs;
688
+ }
689
+
690
+ static std::vector<X509*>& GetSystemStoreRootCertificates () {
691
+ // Use function-local static to guarantee thread safety.
692
+ static std::vector<X509*> system_store_certs =
693
+ InitializeSystemStoreCertificates ();
694
+ has_cached_system_root_certs.store (true );
695
+ return system_store_certs;
696
+ }
697
+
698
+ static std::vector<X509*> InitializeExtraCACertificates () {
699
+ std::vector<X509*> extra_certs;
700
+ unsigned long err = LoadCertsFromFile ( // NOLINT(runtime/int)
701
+ &extra_certs,
702
+ extra_root_certs_file.c_str ());
703
+ if (err) {
704
+ char buf[256 ];
705
+ ERR_error_string_n (err, buf, sizeof (buf));
706
+ fprintf (stderr,
707
+ " Warning: Ignoring extra certs from `%s`, load failed: %s\n " ,
708
+ extra_root_certs_file.c_str (),
709
+ buf);
710
+ }
711
+ return extra_certs;
712
+ }
713
+
714
+ static std::vector<X509*>& GetExtraCACertificates () {
715
+ // Use function-local static to guarantee thread safety.
716
+ static std::vector<X509*> extra_certs = InitializeExtraCACertificates ();
717
+ has_cached_extra_root_certs.store (true );
718
+ return extra_certs;
719
+ }
720
+
721
+ // Due to historical reasons the various options of CA certificates
722
+ // may invalid one another. The current rule is:
723
+ // 1. If the configure-time option --openssl-use-def-ca-store is NOT used
724
+ // (default):
725
+ // a. If the runtime option --use-openssl-ca is used, load the
726
+ // CA certificates from the default locations respected by OpenSSL.
727
+ // b. Otherwise, --use-bundled-ca is assumed to be the default, and we
728
+ // use the bundled CA certificates.
729
+ // 2. If the configure-time option --openssl-use-def-ca-store IS used,
730
+ // --use-openssl-ca is assumed to be the default, with the default
731
+ // location set to the path specified by the configure-time option.
732
+ // 3. --use-openssl-ca and --use-bundled-ca are mutually exclusive.
733
+ // 4. --use-openssl-ca and --use-system-ca are mutually exclusive.
734
+ // 5. --use-bundled-ca and --use-system-ca can be used together.
735
+ // The certificates can be combined.
736
+ // 6. Independent of all other flags, NODE_EXTRA_CA_CERTS always
737
+ // adds extra certificates from the specified path, so it works
738
+ // with all the other flags.
739
+ // 7. Certificates from --use-bundled-ca, --use-system-ca and
740
+ // NODE_EXTRA_CA_CERTS are cached after first load. Certificates
741
+ // from --use-system-ca are not cached and always reloaded from
742
+ // disk.
743
+ // TODO(joyeecheung): maybe these rules need a bit of consolidation?
704
744
X509_STORE* NewRootCertStore () {
705
- static std::vector<X509*> root_certs_vector;
706
- static bool root_certs_vector_loaded = false ;
707
- static Mutex root_certs_vector_mutex;
708
- Mutex::ScopedLock lock (root_certs_vector_mutex);
709
-
710
- if (!root_certs_vector_loaded) {
711
- if (per_process::cli_options->ssl_openssl_cert_store == false ) {
712
- std::vector<std::string> combined_root_certs =
713
- getCombinedRootCertificates ();
714
-
715
- for (size_t i = 0 ; i < combined_root_certs.size (); i++) {
716
- X509* x509 =
717
- PEM_read_bio_X509 (NodeBIO::NewFixed (combined_root_certs[i].data (),
718
- combined_root_certs[i].length ())
719
- .get (),
720
- nullptr , // no re-use of X509 structure
721
- NoPasswordCallback,
722
- nullptr ); // no callback data
723
-
724
- // Parse errors from the built-in roots are fatal.
725
- CHECK_NOT_NULL (x509);
726
-
727
- root_certs_vector.push_back (x509);
728
- }
729
- }
730
-
731
- if (!extra_root_certs_file.empty ()) {
732
- unsigned long err = LoadCertsFromFile ( // NOLINT(runtime/int)
733
- &root_certs_vector,
734
- extra_root_certs_file.c_str ());
735
- if (err) {
736
- char buf[256 ];
737
- ERR_error_string_n (err, buf, sizeof (buf));
738
- fprintf (stderr,
739
- " Warning: Ignoring extra certs from `%s`, load failed: %s\n " ,
740
- extra_root_certs_file.c_str (),
741
- buf);
742
- }
743
- }
744
-
745
- root_certs_vector_loaded = true ;
746
- }
747
-
748
745
X509_STORE* store = X509_STORE_new ();
749
746
CHECK_NOT_NULL (store);
747
+
750
748
if (*system_cert_path != ' \0 ' ) {
751
749
ERR_set_mark ();
752
750
X509_STORE_load_locations (store, system_cert_path, nullptr );
@@ -756,15 +754,45 @@ X509_STORE* NewRootCertStore() {
756
754
Mutex::ScopedLock cli_lock (node::per_process::cli_options_mutex);
757
755
if (per_process::cli_options->ssl_openssl_cert_store ) {
758
756
CHECK_EQ (1 , X509_STORE_set_default_paths (store));
757
+ } else {
758
+ for (X509* cert : GetBundledRootCertificates ()) {
759
+ CHECK_EQ (1 , X509_STORE_add_cert (store, cert));
760
+ }
761
+ if (per_process::cli_options->use_system_ca ) {
762
+ for (X509* cert : GetSystemStoreRootCertificates ()) {
763
+ CHECK_EQ (1 , X509_STORE_add_cert (store, cert));
764
+ }
765
+ }
759
766
}
760
767
761
- for (X509* cert : root_certs_vector) {
762
- CHECK_EQ (1 , X509_STORE_add_cert (store, cert));
768
+ if (!extra_root_certs_file.empty ()) {
769
+ for (X509* cert : GetExtraCACertificates ()) {
770
+ CHECK_EQ (1 , X509_STORE_add_cert (store, cert));
771
+ }
763
772
}
764
773
765
774
return store;
766
775
}
767
776
777
+ void CleanupCachedRootCertificates () {
778
+ if (has_cached_bundled_root_certs.load ()) {
779
+ for (X509* cert : GetBundledRootCertificates ()) {
780
+ X509_free (cert);
781
+ }
782
+ }
783
+ if (has_cached_system_root_certs.load ()) {
784
+ for (X509* cert : GetSystemStoreRootCertificates ()) {
785
+ X509_free (cert);
786
+ }
787
+ }
788
+
789
+ if (has_cached_extra_root_certs.load ()) {
790
+ for (X509* cert : GetExtraCACertificates ()) {
791
+ X509_free (cert);
792
+ }
793
+ }
794
+ }
795
+
768
796
void GetRootCertificates (const FunctionCallbackInfo<Value>& args) {
769
797
Environment* env = Environment::GetCurrent (args);
770
798
Local<Value> result[arraysize (root_certs)];
0 commit comments