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