27
27
#include < wincrypt.h>
28
28
#endif
29
29
30
+ #include < set>
31
+
30
32
namespace node {
31
33
32
34
using ncrypto::BignumPointer;
@@ -81,10 +83,28 @@ static std::atomic<bool> has_cached_bundled_root_certs{false};
81
83
static std::atomic<bool > has_cached_system_root_certs{false };
82
84
static std::atomic<bool > has_cached_extra_root_certs{false };
83
85
86
+ // Used for sets of X509.
87
+ struct X509Less {
88
+ bool operator ()(const X509* lhs, const X509* rhs) const noexcept {
89
+ return X509_cmp (const_cast <X509*>(lhs), const_cast <X509*>(rhs)) < 0 ;
90
+ }
91
+ };
92
+ using X509Set = std::set<X509*, X509Less>;
93
+
94
+ // Per-thread root cert store. See NewRootCertStore() on what it contains.
95
+ static thread_local X509_STORE* root_cert_store = nullptr ;
96
+ // If the user calls tls.setDefaultCACertificates() this will be used
97
+ // to hold the user-provided certificates, the root_cert_store and any new
98
+ // copy generated by NewRootCertStore() will then contain the certificates
99
+ // from this set.
100
+ static thread_local std::unique_ptr<X509Set> root_certs_from_users;
101
+
84
102
X509_STORE* GetOrCreateRootCertStore () {
85
- // Guaranteed thread-safe by standard, just don't use -fno-threadsafe-statics.
86
- static X509_STORE* store = NewRootCertStore ();
87
- return store;
103
+ if (root_cert_store != nullptr ) {
104
+ return root_cert_store;
105
+ }
106
+ root_cert_store = NewRootCertStore ();
107
+ return root_cert_store;
88
108
}
89
109
90
110
// Takes a string or buffer and loads it into a BIO.
@@ -225,14 +245,11 @@ int SSL_CTX_use_certificate_chain(SSL_CTX* ctx,
225
245
issuer);
226
246
}
227
247
228
- static unsigned long LoadCertsFromFile ( // NOLINT(runtime/int)
248
+ static unsigned long LoadCertsFromBIO ( // NOLINT(runtime/int)
229
249
std::vector<X509*>* certs,
230
- const char * file ) {
250
+ BIOPointer bio ) {
231
251
MarkPopErrorOnReturn mark_pop_error_on_return;
232
252
233
- auto bio = BIOPointer::NewFile (file, " r" );
234
- if (!bio) return ERR_get_error ();
235
-
236
253
while (X509* x509 = PEM_read_bio_X509 (
237
254
bio.get (), nullptr , NoPasswordCallback, nullptr )) {
238
255
certs->push_back (x509);
@@ -248,6 +265,17 @@ static unsigned long LoadCertsFromFile( // NOLINT(runtime/int)
248
265
}
249
266
}
250
267
268
+ static unsigned long LoadCertsFromFile ( // NOLINT(runtime/int)
269
+ std::vector<X509*>* certs,
270
+ const char * file) {
271
+ MarkPopErrorOnReturn mark_pop_error_on_return;
272
+
273
+ auto bio = BIOPointer::NewFile (file, " r" );
274
+ if (!bio) return ERR_get_error ();
275
+
276
+ return LoadCertsFromBIO (certs, std::move (bio));
277
+ }
278
+
251
279
// Indicates the trust status of a certificate.
252
280
enum class TrustStatus {
253
281
// Trust status is unknown / uninitialized.
@@ -829,11 +857,24 @@ static std::vector<X509*>& GetExtraCACertificates() {
829
857
// NODE_EXTRA_CA_CERTS are cached after first load. Certificates
830
858
// from --use-system-ca are not cached and always reloaded from
831
859
// disk.
860
+ // 8. If users have reset the root cert store by calling
861
+ // tls.setDefaultCACertificates(), the store will be populated with
862
+ // the certificates provided by users.
832
863
// TODO(joyeecheung): maybe these rules need a bit of consolidation?
833
864
X509_STORE* NewRootCertStore () {
834
865
X509_STORE* store = X509_STORE_new ();
835
866
CHECK_NOT_NULL (store);
836
867
868
+ // If the root cert store is already reset by users through
869
+ // tls.setDefaultCACertificates(), just create a copy from the
870
+ // user-provided certificates.
871
+ if (root_certs_from_users != nullptr ) {
872
+ for (X509* cert : *root_certs_from_users) {
873
+ CHECK_EQ (1 , X509_STORE_add_cert (store, cert));
874
+ }
875
+ return store;
876
+ }
877
+
837
878
#ifdef NODE_OPENSSL_SYSTEM_CERT_PATH
838
879
if constexpr (sizeof (NODE_OPENSSL_SYSTEM_CERT_PATH) > 1 ) {
839
880
ERR_set_mark ();
@@ -901,14 +942,57 @@ void GetBundledRootCertificates(const FunctionCallbackInfo<Value>& args) {
901
942
Array::New (env->isolate (), result, arraysize (root_certs)));
902
943
}
903
944
945
+ bool ArrayOfStringsToX509s (Local<Context> context,
946
+ Local<Array> cert_array,
947
+ std::vector<X509*>* certs) {
948
+ ClearErrorOnReturn clear_error_on_return;
949
+ Isolate* isolate = context->GetIsolate ();
950
+ Environment* env = Environment::GetCurrent (context);
951
+ uint32_t array_length = cert_array->Length ();
952
+
953
+ std::vector<v8::Global<Value>> cert_items;
954
+ if (FromV8Array (context, cert_array, &cert_items).IsNothing ()) {
955
+ return false ;
956
+ }
957
+
958
+ for (uint32_t i = 0 ; i < array_length; i++) {
959
+ Local<Value> cert_val = cert_items[i].Get (isolate);
960
+ // Parse the PEM certificate.
961
+ BIOPointer bio (LoadBIO (env, cert_val));
962
+ if (!bio) {
963
+ ThrowCryptoError (env, ERR_get_error (), " Failed to load certificate data" );
964
+ return false ;
965
+ }
966
+
967
+ // Read all certificates from this PEM string
968
+ size_t start = certs->size ();
969
+ auto err = LoadCertsFromBIO (certs, std::move (bio));
970
+ if (err != 0 ) {
971
+ size_t end = certs->size ();
972
+ // Clean up any certificates we've already parsed upon failure.
973
+ for (size_t j = start; j < end; ++j) {
974
+ X509_free ((*certs)[j]);
975
+ }
976
+ ThrowCryptoError (env, err, " Failed to parse certificate" );
977
+ return false ;
978
+ }
979
+ }
980
+
981
+ return true ;
982
+ }
983
+
984
+ template <typename It>
904
985
MaybeLocal<Array> X509sToArrayOfStrings (Environment* env,
905
- const std::vector<X509*>& certs) {
986
+ It first,
987
+ It last,
988
+ size_t size) {
906
989
ClearErrorOnReturn clear_error_on_return;
907
990
EscapableHandleScope scope (env->isolate ());
908
991
909
- LocalVector<Value> result (env->isolate (), certs.size ());
910
- for (size_t i = 0 ; i < certs.size (); ++i) {
911
- X509View view (certs[i]);
992
+ LocalVector<Value> result (env->isolate (), size);
993
+ size_t i = 0 ;
994
+ for (It cur = first; cur != last; ++cur, ++i) {
995
+ X509View view (*cur);
912
996
auto pem_bio = view.toPEM ();
913
997
if (!pem_bio) {
914
998
ThrowCryptoError (env, ERR_get_error (), " X509 to PEM conversion" );
@@ -933,10 +1017,87 @@ MaybeLocal<Array> X509sToArrayOfStrings(Environment* env,
933
1017
return scope.Escape (Array::New (env->isolate (), result.data (), result.size ()));
934
1018
}
935
1019
1020
+ void GetUserRootCertificates (const FunctionCallbackInfo<Value>& args) {
1021
+ Environment* env = Environment::GetCurrent (args);
1022
+ CHECK_NOT_NULL (root_certs_from_users);
1023
+ Local<Array> results;
1024
+ if (X509sToArrayOfStrings (env,
1025
+ root_certs_from_users->begin (),
1026
+ root_certs_from_users->end (),
1027
+ root_certs_from_users->size ())
1028
+ .ToLocal (&results)) {
1029
+ args.GetReturnValue ().Set (results);
1030
+ }
1031
+ }
1032
+
1033
+ void ResetRootCertStore (const FunctionCallbackInfo<Value>& args) {
1034
+ Local<Context> context = args.GetIsolate ()->GetCurrentContext ();
1035
+ CHECK (args[0 ]->IsArray ());
1036
+ Local<Array> cert_array = args[0 ].As <Array>();
1037
+
1038
+ if (cert_array->Length () == 0 ) {
1039
+ // If the array is empty, just clear the user certs and reset the store.
1040
+ if (root_cert_store != nullptr ) {
1041
+ X509_STORE_free (root_cert_store);
1042
+ root_cert_store = nullptr ;
1043
+ }
1044
+
1045
+ // Free any existing certificates in the old set.
1046
+ if (root_certs_from_users != nullptr ) {
1047
+ for (X509* cert : *root_certs_from_users) {
1048
+ X509_free (cert);
1049
+ }
1050
+ }
1051
+ root_certs_from_users = std::make_unique<X509Set>();
1052
+ return ;
1053
+ }
1054
+
1055
+ // Parse certificates from the array
1056
+ std::unique_ptr<std::vector<X509*>> certs =
1057
+ std::make_unique<std::vector<X509*>>();
1058
+ if (!ArrayOfStringsToX509s (context, cert_array, certs.get ())) {
1059
+ // Error already thrown by ArrayOfStringsToX509s
1060
+ return ;
1061
+ }
1062
+
1063
+ if (certs->empty ()) {
1064
+ Environment* env = Environment::GetCurrent (context);
1065
+ return THROW_ERR_CRYPTO_OPERATION_FAILED (
1066
+ env, " No valid certificates found in the provided array" );
1067
+ }
1068
+
1069
+ auto new_set = std::make_unique<X509Set>();
1070
+ for (X509* cert : *certs) {
1071
+ auto [it, inserted] = new_set->insert (cert);
1072
+ if (!inserted) { // Free duplicate certificates from the vector.
1073
+ X509_free (cert);
1074
+ }
1075
+ }
1076
+
1077
+ // Free any existing certificates in the old set.
1078
+ if (root_certs_from_users != nullptr ) {
1079
+ for (X509* cert : *root_certs_from_users) {
1080
+ X509_free (cert);
1081
+ }
1082
+ }
1083
+ std::swap (root_certs_from_users, new_set);
1084
+
1085
+ // Reset the global root cert store and create a new one with the
1086
+ // certificates.
1087
+ if (root_cert_store != nullptr ) {
1088
+ X509_STORE_free (root_cert_store);
1089
+ }
1090
+
1091
+ // TODO(joyeecheung): we can probably just reset it to nullptr
1092
+ // and let the next call to NewRootCertStore() create a new one.
1093
+ root_cert_store = NewRootCertStore ();
1094
+ }
1095
+
936
1096
void GetSystemCACertificates (const FunctionCallbackInfo<Value>& args) {
937
1097
Environment* env = Environment::GetCurrent (args);
938
1098
Local<Array> results;
939
- if (X509sToArrayOfStrings (env, GetSystemStoreCACertificates ())
1099
+ std::vector<X509*>& certs = GetSystemStoreCACertificates ();
1100
+ if (X509sToArrayOfStrings (env, certs.begin (), certs.end (), certs.size ())
940
1101
.ToLocal (&results)) {
941
1102
args.GetReturnValue ().Set (results);
942
1103
}
@@ -948,7 +1109,9 @@ void GetExtraCACertificates(const FunctionCallbackInfo<Value>& args) {
948
1109
return args.GetReturnValue ().Set (Array::New (env->isolate ()));
949
1110
}
950
1111
Local<Array> results;
951
- if (X509sToArrayOfStrings (env, GetExtraCACertificates ()).ToLocal (&results)) {
1112
+ std::vector<X509*>& certs = GetExtraCACertificates ();
1113
+ if (X509sToArrayOfStrings (env, certs.begin (), certs.end (), certs.size ())
1114
+ .ToLocal (&results)) {
952
1115
args.GetReturnValue ().Set (results);
953
1116
}
954
1117
}
@@ -1044,6 +1207,9 @@ void SecureContext::Initialize(Environment* env, Local<Object> target) {
1044
1207
context, target, " getSystemCACertificates" , GetSystemCACertificates);
1045
1208
SetMethodNoSideEffect (
1046
1209
context, target, " getExtraCACertificates" , GetExtraCACertificates);
1210
+ SetMethod (context, target, " resetRootCertStore" , ResetRootCertStore);
1211
+ SetMethodNoSideEffect (
1212
+ context, target, " getUserRootCertificates" , GetUserRootCertificates);
1047
1213
}
1048
1214
1049
1215
void SecureContext::RegisterExternalReferences (
@@ -1086,6 +1252,8 @@ void SecureContext::RegisterExternalReferences(
1086
1252
registry->Register (GetBundledRootCertificates);
1087
1253
registry->Register (GetSystemCACertificates);
1088
1254
registry->Register (GetExtraCACertificates);
1255
+ registry->Register (ResetRootCertStore);
1256
+ registry->Register (GetUserRootCertificates);
1089
1257
}
1090
1258
1091
1259
SecureContext* SecureContext::Create (Environment* env) {
0 commit comments