Skip to content

Commit 87719d7

Browse files
oyydrefack
authored andcommitted
tls: load NODE_EXTRA_CA_CERTS at startup
This commit makes node load extra certificates at startup instead of first use. PR-URL: #23354 Fixes: #20434 Refs: #20432 Reviewed-By: Sam Roberts <[email protected]> Reviewed-By: Sakthipriyan Vairamani <[email protected]> Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
1 parent 3e3ce22 commit 87719d7

File tree

3 files changed

+96
-21
lines changed

3 files changed

+96
-21
lines changed

src/node_crypto.cc

+34-21
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,10 @@ static const char* const root_certs[] = {
116116

117117
static const char system_cert_path[] = NODE_OPENSSL_SYSTEM_CERT_PATH;
118118

119-
static std::string extra_root_certs_file; // NOLINT(runtime/string)
120-
121119
static X509_STORE* root_cert_store;
122120

121+
static bool extra_root_certs_loaded = false;
122+
123123
// Just to generate static methods
124124
template void SSLWrap<TLSWrap>::AddMethods(Environment* env,
125125
Local<FunctionTemplate> t);
@@ -832,11 +832,6 @@ void SecureContext::AddCRL(const FunctionCallbackInfo<Value>& args) {
832832
}
833833

834834

835-
void UseExtraCaCerts(const std::string& file) {
836-
extra_root_certs_file = file;
837-
}
838-
839-
840835
static unsigned long AddCertsFromFile( // NOLINT(runtime/int)
841836
X509_STORE* store,
842837
const char* file) {
@@ -863,30 +858,44 @@ static unsigned long AddCertsFromFile( // NOLINT(runtime/int)
863858
return err;
864859
}
865860

866-
void SecureContext::AddRootCerts(const FunctionCallbackInfo<Value>& args) {
867-
SecureContext* sc;
868-
ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder());
861+
862+
void UseExtraCaCerts(const std::string& file) {
869863
ClearErrorOnReturn clear_error_on_return;
870864

871-
if (!root_cert_store) {
865+
if (root_cert_store == nullptr) {
872866
root_cert_store = NewRootCertStore();
873867

874-
if (!extra_root_certs_file.empty()) {
868+
if (!file.empty()) {
875869
unsigned long err = AddCertsFromFile( // NOLINT(runtime/int)
876870
root_cert_store,
877-
extra_root_certs_file.c_str());
871+
file.c_str());
878872
if (err) {
879-
// We do not call back into JS after this line anyway, so ignoring
880-
// the return value of ProcessEmitWarning does not affect how a
881-
// possible exception would be propagated.
882-
ProcessEmitWarning(sc->env(),
883-
"Ignoring extra certs from `%s`, "
884-
"load failed: %s\n",
885-
extra_root_certs_file.c_str(),
886-
ERR_error_string(err, nullptr));
873+
fprintf(stderr,
874+
"Warning: Ignoring extra certs from `%s`, load failed: %s\n",
875+
file.c_str(),
876+
ERR_error_string(err, nullptr));
877+
} else {
878+
extra_root_certs_loaded = true;
887879
}
888880
}
889881
}
882+
}
883+
884+
885+
static void IsExtraRootCertsFileLoaded(
886+
const FunctionCallbackInfo<Value>& args) {
887+
return args.GetReturnValue().Set(extra_root_certs_loaded);
888+
}
889+
890+
891+
void SecureContext::AddRootCerts(const FunctionCallbackInfo<Value>& args) {
892+
SecureContext* sc;
893+
ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder());
894+
ClearErrorOnReturn clear_error_on_return;
895+
896+
if (root_cert_store == nullptr) {
897+
root_cert_store = NewRootCertStore();
898+
}
890899

891900
// Increment reference count so global store is not deleted along with CTX.
892901
X509_STORE_up_ref(root_cert_store);
@@ -5624,6 +5633,7 @@ void SetFipsCrypto(const FunctionCallbackInfo<Value>& args) {
56245633
}
56255634
#endif /* NODE_FIPS_MODE */
56265635

5636+
56275637
void Initialize(Local<Object> target,
56285638
Local<Value> unused,
56295639
Local<Context> context,
@@ -5644,6 +5654,9 @@ void Initialize(Local<Object> target,
56445654
env->SetMethodNoSideEffect(target, "certVerifySpkac", VerifySpkac);
56455655
env->SetMethodNoSideEffect(target, "certExportPublicKey", ExportPublicKey);
56465656
env->SetMethodNoSideEffect(target, "certExportChallenge", ExportChallenge);
5657+
// Exposed for testing purposes only.
5658+
env->SetMethodNoSideEffect(target, "isExtraRootCertsFileLoaded",
5659+
IsExtraRootCertsFileLoaded);
56475660

56485661
env->SetMethodNoSideEffect(target, "ECDHConvertKey", ConvertKey);
56495662
#ifndef OPENSSL_NO_ENGINE
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
'use strict';
2+
// Flags: --expose-internals
3+
4+
const common = require('../common');
5+
6+
if (!common.hasCrypto)
7+
common.skip('missing crypto');
8+
9+
const assert = require('assert');
10+
const tls = require('tls');
11+
const fixtures = require('../common/fixtures');
12+
const { internalBinding } = require('internal/test/binding');
13+
const binding = internalBinding('crypto');
14+
15+
const { fork } = require('child_process');
16+
17+
// This test ensures that extra certificates are loaded at startup.
18+
if (process.argv[2] !== 'child') {
19+
if (process.env.CHILD_USE_EXTRA_CA_CERTS === 'yes') {
20+
assert.strictEqual(binding.isExtraRootCertsFileLoaded(), true);
21+
} else if (process.env.CHILD_USE_EXTRA_CA_CERTS === 'no') {
22+
assert.strictEqual(binding.isExtraRootCertsFileLoaded(), false);
23+
tls.createServer({});
24+
assert.strictEqual(binding.isExtraRootCertsFileLoaded(), false);
25+
}
26+
} else {
27+
const NODE_EXTRA_CA_CERTS = fixtures.path('keys', 'ca1-cert.pem');
28+
const extendsEnv = (obj) => Object.assign({}, process.env, obj);
29+
30+
[
31+
extendsEnv({ CHILD_USE_EXTRA_CA_CERTS: 'yes', NODE_EXTRA_CA_CERTS }),
32+
extendsEnv({ CHILD_USE_EXTRA_CA_CERTS: 'no' }),
33+
].forEach((processEnv) => {
34+
fork(__filename, ['child'], { env: processEnv })
35+
.on('exit', common.mustCall((status) => {
36+
// client did not succeed in connecting
37+
assert.strictEqual(status, 0);
38+
}));
39+
});
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use strict';
2+
const common = require('../common');
3+
const fixtures = require('../common/fixtures');
4+
const assert = require('assert');
5+
const { fork } = require('child_process');
6+
7+
// This test ensures that trying to load extra certs won't throw even when
8+
// there is no crypto support, i.e., built with "./configure --without-ssl".
9+
if (process.argv[2] === 'child') {
10+
// exit
11+
} else {
12+
const NODE_EXTRA_CA_CERTS = fixtures.path('keys', 'ca1-cert.pem');
13+
14+
fork(
15+
__filename,
16+
['child'],
17+
{ env: Object.assign({}, process.env, { NODE_EXTRA_CA_CERTS }) },
18+
).on('exit', common.mustCall(function(status) {
19+
// client did not succeed in connecting
20+
assert.strictEqual(status, 0);
21+
}));
22+
}

0 commit comments

Comments
 (0)