Skip to content

Commit

Permalink
src: fix missing extra ca in tls.rootCertificates
Browse files Browse the repository at this point in the history
Fixes tls.rootCertificates missing certificates loaded from NODE_EXTRA_CA_CERTS.

Fixes: nodejs#32074
  • Loading branch information
ebickle committed Mar 5, 2020
1 parent 0d8a84a commit 14f3a86
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 33 deletions.
30 changes: 22 additions & 8 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -988,19 +988,33 @@ static X509_STORE* NewRootCertStore() {

void GetRootCertificates(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Local<Value> result[arraysize(root_certs)];

for (size_t i = 0; i < arraysize(root_certs); i++) {
if (!String::NewFromOneByte(
env->isolate(),
reinterpret_cast<const uint8_t*>(root_certs[i]),
NewStringType::kNormal).ToLocal(&result[i])) {
return;
if (root_cert_store == nullptr) {
root_cert_store = NewRootCertStore();
}

stack_st_X509_OBJECT* objs = X509_STORE_get0_objects(root_cert_store);
int num_objs = sk_X509_OBJECT_num(objs);

std::vector<Local<Value>> result;
result.reserve(num_objs);

for (size_t i = 0; i < num_objs; i++) {
X509_OBJECT* obj = sk_X509_OBJECT_value(objs, i);
if (X509_OBJECT_get_type(obj) == X509_LU_X509) {
X509* cert = X509_OBJECT_get0_X509(obj);

Local<Value> value;
if (!X509ToPEM(env, cert).ToLocal(&value)) {
return;
}

result.push_back(value);
}
}

args.GetReturnValue().Set(
Array::New(env->isolate(), result, arraysize(root_certs)));
Array::New(env->isolate(), result.data(), result.size()));
}


Expand Down
10 changes: 10 additions & 0 deletions src/node_crypto_common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -968,6 +968,16 @@ MaybeLocal<Value> GetPeerCert(
return result;
}

MaybeLocal<Value> X509ToPEM(Environment* env, X509* cert) {
BIOPointer bio(BIO_new(BIO_s_mem()));

if (PEM_write_bio_X509(bio.get(), cert) == 0) {
return Undefined(env->isolate());
}

return ToV8Value(env, bio);
}

MaybeLocal<Object> X509ToObject(Environment* env, X509* cert) {
EscapableHandleScope scope(env->isolate());
Local<Context> context = env->context();
Expand Down
4 changes: 4 additions & 0 deletions src/node_crypto_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ v8::MaybeLocal<v8::Object> X509ToObject(
Environment* env,
X509* cert);

v8::MaybeLocal<v8::Value> X509ToPEM(
Environment* env,
X509* cert);

} // namespace crypto
} // namespace node

Expand Down
69 changes: 44 additions & 25 deletions test/parallel/test-tls-root-certificates.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,49 @@
const common = require('../common');
if (!common.hasCrypto) common.skip('missing crypto');

const fixtures = require('../common/fixtures');
const assert = require('assert');
const tls = require('tls');

assert(Array.isArray(tls.rootCertificates));
assert(tls.rootCertificates.length > 0);

// Getter should return the same object.
assert.strictEqual(tls.rootCertificates, tls.rootCertificates);

// Array is immutable...
assert.throws(() => tls.rootCertificates[0] = 0, /TypeError/);
assert.throws(() => tls.rootCertificates.sort(), /TypeError/);

// ...and so is the property.
assert.throws(() => tls.rootCertificates = 0, /TypeError/);

// Does not contain duplicates.
assert.strictEqual(tls.rootCertificates.length,
new Set(tls.rootCertificates).size);

assert(tls.rootCertificates.every((s) => {
return s.startsWith('-----BEGIN CERTIFICATE-----\n');
}));

assert(tls.rootCertificates.every((s) => {
return s.endsWith('\n-----END CERTIFICATE-----');
}));
const { fork } = require('child_process');

if (process.argv[2] !== 'child') {
// Parent
const NODE_EXTRA_CA_CERTS = fixtures.path('keys', 'ca1-cert.pem');

fork(
__filename,
['child'],
{ env: { ...process.env, NODE_EXTRA_CA_CERTS } }
).on('exit', common.mustCall(function(status) {
assert.strictEqual(status, 0);
}));
} else {
// Child
assert(Array.isArray(tls.rootCertificates));
assert(tls.rootCertificates.length > 0);

// Getter should return the same object.
assert.strictEqual(tls.rootCertificates, tls.rootCertificates);

// Array is immutable...
assert.throws(() => tls.rootCertificates[0] = 0, /TypeError/);
assert.throws(() => tls.rootCertificates.sort(), /TypeError/);

// ...and so is the property.
assert.throws(() => tls.rootCertificates = 0, /TypeError/);

// Does not contain duplicates.
assert.strictEqual(tls.rootCertificates.length,
new Set(tls.rootCertificates).size);

assert(tls.rootCertificates.every((s) => {
return s.startsWith('-----BEGIN CERTIFICATE-----\n');
}));

assert(tls.rootCertificates.every((s) => {
return s.endsWith('\n-----END CERTIFICATE-----\n');
}));

const extraCert = fixtures.readKey('ca1-cert.pem', 'utf8');
assert(tls.rootCertificates.includes(extraCert));
}

0 comments on commit 14f3a86

Please sign in to comment.