◐ Shell
clean mode source ↗

crypto: add tls.setDefaultCACertificates() · nodejs/node@eeeb40e

@@ -27,6 +27,8 @@

2727

#include <wincrypt.h>

2828

#endif

292930+

#include <set>

31+3032

namespace node {

31333234

using ncrypto::BignumPointer;

@@ -83,10 +85,28 @@ static std::atomic<bool> has_cached_bundled_root_certs{false};

8385

static std::atomic<bool> has_cached_system_root_certs{false};

8486

static std::atomic<bool> has_cached_extra_root_certs{false};

858788+

// 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+86104

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;

90110

}

9111192112

// Takes a string or buffer and loads it into a BIO.

@@ -227,14 +247,11 @@ int SSL_CTX_use_certificate_chain(SSL_CTX* ctx,

227247

issuer);

228248

}

229249230-

static unsigned long LoadCertsFromFile( // NOLINT(runtime/int)

250+

static unsigned long LoadCertsFromBIO( // NOLINT(runtime/int)

231251

std::vector<X509*>* certs,

232-

const char* file) {

252+

BIOPointer bio) {

233253

MarkPopErrorOnReturn mark_pop_error_on_return;

234254235-

auto bio = BIOPointer::NewFile(file, "r");

236-

if (!bio) return ERR_get_error();

237-238255

while (X509* x509 = PEM_read_bio_X509(

239256

bio.get(), nullptr, NoPasswordCallback, nullptr)) {

240257

certs->push_back(x509);

@@ -250,6 +267,17 @@ static unsigned long LoadCertsFromFile( // NOLINT(runtime/int)

250267

}

251268

}

252269270+

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+253281

// Indicates the trust status of a certificate.

254282

enum class TrustStatus {

255283

// Trust status is unknown / uninitialized.

@@ -831,11 +859,24 @@ static std::vector<X509*>& GetExtraCACertificates() {

831859

// NODE_EXTRA_CA_CERTS are cached after first load. Certificates

832860

// from --use-system-ca are not cached and always reloaded from

833861

// 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.

834865

// TODO(joyeecheung): maybe these rules need a bit of consolidation?

835866

X509_STORE* NewRootCertStore() {

836867

X509_STORE* store = X509_STORE_new();

837868

CHECK_NOT_NULL(store);

838869870+

// 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+839880

#ifdef NODE_OPENSSL_SYSTEM_CERT_PATH

840881

if constexpr (sizeof(NODE_OPENSSL_SYSTEM_CERT_PATH) > 1) {

841882

ERR_set_mark();

@@ -903,14 +944,57 @@ void GetBundledRootCertificates(const FunctionCallbackInfo<Value>& args) {

903944

Array::New(env->isolate(), result, arraysize(root_certs)));

904945

}

905946947+

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>

906987

MaybeLocal<Array> X509sToArrayOfStrings(Environment* env,

907-

const std::vector<X509*>& certs) {

988+

It first,

989+

It last,

990+

size_t size) {

908991

ClearErrorOnReturn clear_error_on_return;

909992

EscapableHandleScope scope(env->isolate());

910993911-

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);

914998

auto pem_bio = view.toPEM();

915999

if (!pem_bio) {

9161000

ThrowCryptoError(env, ERR_get_error(), "X509 to PEM conversion");

@@ -935,10 +1019,87 @@ MaybeLocal<Array> X509sToArrayOfStrings(Environment* env,

9351019

return scope.Escape(Array::New(env->isolate(), result.data(), result.size()));

9361020

}

93710211022+

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+9381098

void GetSystemCACertificates(const FunctionCallbackInfo<Value>& args) {

9391099

Environment* env = Environment::GetCurrent(args);

9401100

Local<Array> results;

941-

if (X509sToArrayOfStrings(env, GetSystemStoreCACertificates())

1101+

std::vector<X509*>& certs = GetSystemStoreCACertificates();

1102+

if (X509sToArrayOfStrings(env, certs.begin(), certs.end(), certs.size())

9421103

.ToLocal(&results)) {

9431104

args.GetReturnValue().Set(results);

9441105

}

@@ -950,7 +1111,9 @@ void GetExtraCACertificates(const FunctionCallbackInfo<Value>& args) {

9501111

return args.GetReturnValue().Set(Array::New(env->isolate()));

9511112

}

9521113

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)) {

9541117

args.GetReturnValue().Set(results);

9551118

}

9561119

}

@@ -1046,6 +1209,9 @@ void SecureContext::Initialize(Environment* env, Local<Object> target) {

10461209

context, target, "getSystemCACertificates", GetSystemCACertificates);

10471210

SetMethodNoSideEffect(

10481211

context, target, "getExtraCACertificates", GetExtraCACertificates);

1212+

SetMethod(context, target, "resetRootCertStore", ResetRootCertStore);

1213+

SetMethodNoSideEffect(

1214+

context, target, "getUserRootCertificates", GetUserRootCertificates);

10491215

}

1050121610511217

void SecureContext::RegisterExternalReferences(

@@ -1088,6 +1254,8 @@ void SecureContext::RegisterExternalReferences(

10881254

registry->Register(GetBundledRootCertificates);

10891255

registry->Register(GetSystemCACertificates);

10901256

registry->Register(GetExtraCACertificates);

1257+

registry->Register(ResetRootCertStore);

1258+

registry->Register(GetUserRootCertificates);

10911259

}

1092126010931261

SecureContext* SecureContext::Create(Environment* env) {