Skip to content

Commit

Permalink
src: fix missing extra ca in secure contexts
Browse files Browse the repository at this point in the history
Fixes SecureContext missing NODE_EXTRA_CA_CERTS when
SecureContext::AddCACert, SecureContext::AddCRL,
or SecureContext::LoadPKCS12 are called.

Fixes: nodejs#32010
  • Loading branch information
ebickle committed Feb 28, 2020
1 parent 3d894d0 commit d47a7ab
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 3 deletions.
38 changes: 35 additions & 3 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1027,6 +1027,38 @@ static X509_STORE* NewRootCertStore() {
}


X509_STORE* CloneRootCertStore() {
if (root_cert_store == nullptr) {
root_cert_store = NewRootCertStore();
}

X509_STORE* store_clone = X509_STORE_new();
X509_STORE_set1_param(store_clone, X509_STORE_get0_param(root_cert_store));

STACK_OF(X509_OBJECT)* objs = X509_STORE_get0_objects(root_cert_store);
for (int i = 0; i < sk_X509_OBJECT_num(objs); i++) {
X509_OBJECT* obj = sk_X509_OBJECT_value(objs, i);

switch (X509_OBJECT_get_type(obj)) {
case X509_LU_X509:
{
X509* cert = X509_OBJECT_get0_X509(obj);
X509_STORE_add_cert(store_clone, cert);
}
break;
case X509_LU_CRL:
{
X509_CRL* crl = X509_OBJECT_get0_X509_CRL(obj);
X509_STORE_add_crl(store_clone, crl);
}
break;
}
}

return store_clone;
}


void GetRootCertificates(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Local<Value> result[arraysize(root_certs)];
Expand Down Expand Up @@ -1064,7 +1096,7 @@ void SecureContext::AddCACert(const FunctionCallbackInfo<Value>& args) {
while (X509* x509 = PEM_read_bio_X509_AUX(
bio.get(), nullptr, NoPasswordCallback, nullptr)) {
if (cert_store == root_cert_store) {
cert_store = NewRootCertStore();
cert_store = CloneRootCertStore();
SSL_CTX_set_cert_store(sc->ctx_.get(), cert_store);
}
X509_STORE_add_cert(cert_store, x509);
Expand Down Expand Up @@ -1098,7 +1130,7 @@ void SecureContext::AddCRL(const FunctionCallbackInfo<Value>& args) {

X509_STORE* cert_store = SSL_CTX_get_cert_store(sc->ctx_.get());
if (cert_store == root_cert_store) {
cert_store = NewRootCertStore();
cert_store = CloneRootCertStore();
SSL_CTX_set_cert_store(sc->ctx_.get(), cert_store);
}

Expand Down Expand Up @@ -1475,7 +1507,7 @@ void SecureContext::LoadPKCS12(const FunctionCallbackInfo<Value>& args) {
X509* ca = sk_X509_value(extra_certs.get(), i);

if (cert_store == root_cert_store) {
cert_store = NewRootCertStore();
cert_store = CloneRootCertStore();
SSL_CTX_set_cert_store(sc->ctx_.get(), cert_store);
}
X509_STORE_add_cert(cert_store, ca);
Expand Down
55 changes: 55 additions & 0 deletions test/parallel/test-tls-env-extra-ca-with-ca.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Certs in NODE_EXTRA_CA_CERTS are used for TLS peer validation

'use strict';
const common = require('../common');

if (!common.hasCrypto)
common.skip('missing crypto');

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

const { fork } = require('child_process');

if (process.env.CHILD) {
const copts = {
port: process.env.PORT,
checkServerIdentity: common.mustCall()
};

// New secure contexts have the well-known root CAs.
copts.secureContext = tls.createSecureContext();

// Explicit calls to addCACert() add to the root certificates,
// instead of replacing, so connection still succeeds.
copts.secureContext.context.addCACert(
fixtures.readKey('ca1-cert.pem')
);

const client = tls.connect(copts, common.mustCall(function() {
client.end('hi');
}));
return;
}

const options = {
key: fixtures.readKey('agent3-key.pem'),
cert: fixtures.readKey('agent3-cert.pem')
};

const server = tls.createServer(options, common.mustCall(function(s) {
s.end('bye');
server.close();
})).listen(0, common.mustCall(function() {
const env = Object.assign({}, process.env, {
CHILD: 'yes',
PORT: this.address().port,
NODE_EXTRA_CA_CERTS: fixtures.path('keys', 'ca2-cert.pem')
});

fork(__filename, { env }).on('exit', common.mustCall(function(status) {
// Client did not succeed in connecting
assert.strictEqual(status, 0);
}));
}));
46 changes: 46 additions & 0 deletions test/parallel/test-tls-env-extra-ca-with-crl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Certs in NODE_EXTRA_CA_CERTS are used for TLS peer validation

'use strict';
const common = require('../common');

if (!common.hasCrypto)
common.skip('missing crypto');

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

const { fork } = require('child_process');

if (process.env.CHILD) {
const copts = {
port: process.env.PORT,
checkServerIdentity: common.mustCall(),
crl: fixtures.readKey('ca2-crl.pem')
};
const client = tls.connect(copts, common.mustCall(function() {
client.end('hi');
}));
return;
}

const options = {
key: fixtures.readKey('agent3-key.pem'),
cert: fixtures.readKey('agent3-cert.pem')
};

const server = tls.createServer(options, common.mustCall(function(s) {
s.end('bye');
server.close();
})).listen(0, common.mustCall(function() {
const env = Object.assign({}, process.env, {
CHILD: 'yes',
PORT: this.address().port,
NODE_EXTRA_CA_CERTS: fixtures.path('keys', 'ca2-cert.pem')
});

fork(__filename, { env }).on('exit', common.mustCall(function(status) {
// Client did not succeed in connecting
assert.strictEqual(status, 0);
}));
}));
47 changes: 47 additions & 0 deletions test/parallel/test-tls-env-extra-ca-with-pfx.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Certs in NODE_EXTRA_CA_CERTS are used for TLS peer validation

'use strict';
const common = require('../common');

if (!common.hasCrypto)
common.skip('missing crypto');

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

const { fork } = require('child_process');

if (process.env.CHILD) {
const copts = {
port: process.env.PORT,
checkServerIdentity: common.mustCall(),
pfx: fixtures.readKey('agent1.pfx'),
passphrase: 'sample'
};
const client = tls.connect(copts, common.mustCall(function() {
client.end('hi');
}));
return;
}

const options = {
key: fixtures.readKey('agent3-key.pem'),
cert: fixtures.readKey('agent3-cert.pem')
};

const server = tls.createServer(options, common.mustCall(function(s) {
s.end('bye');
server.close();
})).listen(0, common.mustCall(function() {
const env = Object.assign({}, process.env, {
CHILD: 'yes',
PORT: this.address().port,
NODE_EXTRA_CA_CERTS: fixtures.path('keys', 'ca2-cert.pem')
});

fork(__filename, { env }).on('exit', common.mustCall(function(status) {
// Client did not succeed in connecting
assert.strictEqual(status, 0);
}));
}));

0 comments on commit d47a7ab

Please sign in to comment.