Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

tls: Use OpenSSL default trusted CA list and allow configuration of the trusted CA path #25363

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions doc/api/crypto.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,21 @@ dictionary with keys:
* `cert` : A string holding the PEM encoded certificate
* `ca` : Either a string or list of strings of PEM encoded CA
certificates to trust.
* `caFile` : A string holding the path to a file containing PEM
encoded CA certificates to trust.
* `caPath` : A string holding the path to a directory containing PEM
encoded CA certificates to trust. This directory must be prepared
using the OpenSSL c\_rehash utility.
* `crl` : Either a string or list of strings of PEM encoded CRLs
(Certificate Revocation List)
* `ciphers`: A string describing the ciphers to use or exclude.
Consult
<http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT>
for details on the format.

If no 'ca' details are given, then node.js will use the default
publicly trusted list of CAs as given in
If no `ca`, `caFile`, or `caPath` is provided, the OpenSSL default trusted CA
list will be used. If OpenSSL is not configured with a default trusted CA list,
then node.js will use a hard-coded list of publicly trusted CAs based on
<http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt>.


Expand Down
9 changes: 7 additions & 2 deletions doc/api/https.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,13 @@ The following options from [tls.connect()][] can also be specified. However, a
- `key`: Private key to use for SSL. Default `null`.
- `passphrase`: A string of passphrase for the private key or pfx. Default `null`.
- `cert`: Public x509 certificate to use. Default `null`.
- `ca`: An authority certificate or array of authority certificates to check
the remote host against.
- `ca`: A string or array of strings containing PEM encoded CA certificates to
check the remote host against.
- `caFile`: A string containing the path to a file containing PEM encoded CA
certificates to check the remote host against.
- `caPath`: A string containing the path to a directory containing PEM encoded
CA certificates to check the remote host against. This directory must be
prepared using the OpenSSL c\_rehash utility.
- `ciphers`: A string describing the ciphers to use or exclude. Consult
<http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT> for
details on the format.
Expand Down
16 changes: 14 additions & 2 deletions doc/api/tls.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,14 @@ automatically set as a listener for the [secureConnection][] event. The
PEM format. (Could be an array of certs). (Required)

- `ca`: An array of strings or `Buffer`s of trusted certificates in PEM
format. If this is omitted several well known "root" CAs will be used,
like VeriSign. These are used to authorize connections.
format.

- `caFile`: A string containing the path to a file that contains trusted
certificates in PEM format.

- `caPath`: A string containing the path to a directory that contains trusted
certificates. This directory must be prepared using the OpenSSL c\_rehash
utility.

- `crl` : Either a string or list of strings of PEM encoded CRLs (Certificate
Revocation List)
Expand Down Expand Up @@ -339,6 +345,12 @@ automatically set as a listener for the [secureConnection][] event. The
protocol set the `SSL_OP_NO_SSLv3` flag. See [SSL_CTX_set_options]
for all available options.

If no `ca`, `caFile`, or `caPath` is specified, the OpenSSL default trusted CA
list will be used. If OpenSSL is not configured with a default trusted CA list,
then a hard-coded list of publicly trusted CAs based on
<http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt>
will be used.

Here is a simple example echo server:

var tls = require('tls');
Expand Down
4 changes: 3 additions & 1 deletion lib/_tls_common.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,9 @@ exports.createSecureContext = function createSecureContext(options, context) {

// NOTE: It's important to add CA before the cert to be able to load
// cert's issuer in C++ code.
if (options.ca) {
if (options.caFile || options.caPath) {
c.context.addRootCerts(options.caFile, options.caPath);
} else if (options.ca) {
if (util.isArray(options.ca)) {
for (var i = 0, len = options.ca.length; i < len; i++) {
c.context.addCACert(options.ca[i]);
Expand Down
4 changes: 4 additions & 0 deletions lib/_tls_wrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,8 @@ function Server(/* [options], listener */) {
passphrase: self.passphrase,
cert: self.cert,
ca: self.ca,
caFile: self.caFile,
caPath: self.caPath,
ciphers: self.ciphers,
ecdhCurve: self.ecdhCurve,
dhparam: self.dhparam,
Expand Down Expand Up @@ -719,6 +721,8 @@ Server.prototype.setOptions = function(options) {
if (options.passphrase) this.passphrase = options.passphrase;
if (options.cert) this.cert = options.cert;
if (options.ca) this.ca = options.ca;
if (options.caFile) this.caFile = options.caFile;
if (options.caPath) this.caPath = options.caPath;
if (options.secureProtocol) this.secureProtocol = options.secureProtocol;
if (options.crl) this.crl = options.crl;
if (options.ciphers) this.ciphers = options.ciphers;
Expand Down
8 changes: 8 additions & 0 deletions lib/https.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@ Agent.prototype.getName = function(options) {
if (options.ca)
name += options.ca;

name += ':';
if (options.caFile)
name += options.caFile;

name += ':';
if (options.caPath)
name += options.caPath;

name += ':';
if (options.cert)
name += options.cert;
Expand Down
26 changes: 26 additions & 0 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,32 @@ void SecureContext::AddRootCerts(const FunctionCallbackInfo<Value>& args) {

assert(sc->ca_store_ == NULL);

if (args.Length() == 2) {
char* caFile = NULL;
char* caPath = NULL;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use nullptr here and snake_case the variable names?

if (args[0]->IsString()) {
String::Utf8Value file(args[0]);
caFile = *file;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unsafe; you're assigning a pointer to memory that goes out of scope immediately afterwards. Happens a few lines below as well.

} else if (!args[0]->IsUndefined() && !args[0]->IsNull()) {
return sc->env()->ThrowTypeError("Bad parameter");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd move the type checking to JS land and just CHECK(args[0]->IsString() || args[0]->IsUndefined()) here.

}
if (args[1]->IsString()) {
String::Utf8Value path(args[1]);
caPath = *path;
} else if (!args[1]->IsUndefined() && !args[1]->IsNull()) {
return sc->env()->ThrowTypeError("Bad parameter");
}
if (!SSL_CTX_load_verify_locations(sc->ctx_, caFile, caPath))
return sc->env()->ThrowTypeError("Error loading CA certificates from caFile or caPath");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Long line.

return;
}

if (args.Length() != 0)
return sc->env()->ThrowTypeError("Bad parameter");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd drop this and put a CHECK(args.Length() == 0 || args.Length() == 2) at the top.


if (SSL_CTX_set_default_verify_paths(sc->ctx_))
return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand how this works but it's very implicit. Can you add a comment explaining it and maybe move it into the if block a few lines up?


if (!root_cert_store) {
root_cert_store = X509_STORE_new();

Expand Down