Skip to content

Commit

Permalink
crypto: initial LibreSSL support
Browse files Browse the repository at this point in the history
  • Loading branch information
rvagg committed Mar 3, 2017
1 parent 1824bbb commit 341b8f7
Show file tree
Hide file tree
Showing 11 changed files with 108 additions and 36 deletions.
8 changes: 7 additions & 1 deletion lib/_tls_common.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ var crypto = null;

const binding = process.binding('crypto');
const NativeSecureContext = binding.SecureContext;
const isLibreSSL = process.versions.openssl &&
/LibreSSL$/.test(process.versions.openssl);


function SecureContext(secureProtocol, secureOptions, context) {
if (!(this instanceof SecureContext)) {
Expand Down Expand Up @@ -138,7 +141,10 @@ exports.createSecureContext = function createSecureContext(options, context) {
// freelist.)
if (options.singleUse) {
c.singleUse = true;
c.context.setFreeListLength(0);
if (!isLibreSSL) {
c.context.setFreeListLength(0);
} // else TODO check for possible leak
// https://github.com/nodejs/node/issues/1522
}

return c;
Expand Down
44 changes: 29 additions & 15 deletions lib/_tls_wrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ const Timer = process.binding('timer_wrap').Timer;
const tls_wrap = process.binding('tls_wrap');
const TCP = process.binding('tcp_wrap').TCP;
const Pipe = process.binding('pipe_wrap').Pipe;
const isLibreSSL = process.versions.openssl &&
/LibreSSL$/.test(process.versions.openssl);

function onhandshakestart() {
debug('onhandshakestart');
Expand Down Expand Up @@ -155,14 +157,20 @@ function onclienthello(hello) {
if (err)
return self.destroy(err);

self._handle.endParser();
if (isLibreSSL) {
oncertcb(hello, self, session && session.servername || hello.servername);
} else {
self._handle.endParser();
}
});
}


function oncertcb(info) {
var self = this;
var servername = info.servername;
function oncertcb(info, self, servername) {
if (!self)
self = this;
if (!servername)
servername = info.servername;

loadSNI(self, servername, function(err, ctx) {
if (err)
Expand All @@ -174,10 +182,14 @@ function oncertcb(info) {
if (!self._handle)
return self.destroy(new Error('Socket is closed'));

try {
self._handle.certCbDone();
} catch (e) {
self.destroy(e);
if (isLibreSSL) {
self._handle.endParser();
} else {
try {
self._handle.certCbDone();
} catch (e) {
self.destroy(e);
}
}
});
});
Expand Down Expand Up @@ -1072,13 +1084,15 @@ exports.connect = function(...args /* [port,] [host,] [options,] [cb] */) {
socket.on('secure', function() {
// Check the size of DHE parameter above minimum requirement
// specified in options.
var ekeyinfo = socket.getEphemeralKeyInfo();
if (ekeyinfo.type === 'DH' && ekeyinfo.size < options.minDHSize) {
var err = new Error('DH parameter size ' + ekeyinfo.size +
' is less than ' + options.minDHSize);
socket.emit('error', err);
socket.destroy();
return;
if (!isLibreSSL) {
var ekeyinfo = socket.getEphemeralKeyInfo();
if (ekeyinfo.type === 'DH' && ekeyinfo.size < options.minDHSize) {
var err = new Error('DH parameter size ' + ekeyinfo.size +
' is less than ' + options.minDHSize);
socket.emit('error', err);
socket.destroy();
return;
}
}

var verifyError = socket._handle.verifyError();
Expand Down
2 changes: 1 addition & 1 deletion node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@
'conditions': [
# -force_load or --whole-archive are not applicable for
# the static library
[ 'node_target_type!="static_library"', {
[ 'node_target_type!="static_library" and node_shared_openssl=="false"', {
'xcode_settings': {
'OTHER_LDFLAGS': [
'-Wl,-force_load,<(PRODUCT_DIR)/<(OPENSSL_PRODUCT)',
Expand Down
11 changes: 7 additions & 4 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3126,10 +3126,13 @@ void SetupProcessObject(Environment* env,
break;
}
}
READONLY_PROPERTY(
versions,
"openssl",
OneByteString(env->isolate(), &OPENSSL_VERSION_TEXT[i], j - i));
Local<String> sslversion =
OneByteString(env->isolate(), &OPENSSL_VERSION_TEXT[i], j - i);
# ifdef LIBRESSL_VERSION_NUMBER
sslversion = String::Concat(sslversion,
OneByteString(env->isolate(), "-LibreSSL"));
# endif
READONLY_PROPERTY(versions, "openssl", sslversion);
}
#endif

Expand Down
38 changes: 27 additions & 11 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -523,8 +523,12 @@ int SSL_CTX_use_certificate_chain(SSL_CTX* ctx,
for (int i = 0; i < sk_X509_num(extra_certs); i++) {
X509* ca = sk_X509_value(extra_certs, i);

#ifdef LIBRESSL_VERSION_NUMBER
r = SSL_CTX_add_extra_chain_cert(ctx, ca);
#else
// NOTE: Increments reference count on `ca`
r = SSL_CTX_add1_chain_cert(ctx, ca);
#endif // LIBRESSL_VERSION_NUMBER

if (!r) {
ret = 0;
Expand Down Expand Up @@ -680,7 +684,7 @@ void SecureContext::SetCert(const FunctionCallbackInfo<Value>& args) {
}


#if OPENSSL_VERSION_NUMBER < 0x10100000L && !defined(OPENSSL_IS_BORINGSSL)
#if (OPENSSL_VERSION_NUMBER < 0x10100000L && !defined(OPENSSL_IS_BORINGSSL)) || defined(LIBRESSL_VERSION_NUMBER)
// This section contains OpenSSL 1.1.0 functions reimplemented for OpenSSL
// 1.0.2 so that the following code can be written without lots of #if lines.

Expand All @@ -693,7 +697,7 @@ static int X509_up_ref(X509* cert) {
CRYPTO_add(&cert->references, 1, CRYPTO_LOCK_X509);
return 1;
}
#endif // OPENSSL_VERSION_NUMBER < 0x10100000L && !OPENSSL_IS_BORINGSSL
#endif // (OPENSSL_VERSION_NUMBER < 0x10100000L && !OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER)


static X509_STORE* NewRootCertStore() {
Expand Down Expand Up @@ -1153,7 +1157,7 @@ void SecureContext::SetTicketKeys(const FunctionCallbackInfo<Value>& args) {


void SecureContext::SetFreeListLength(const FunctionCallbackInfo<Value>& args) {
#if OPENSSL_VERSION_NUMBER < 0x10100000L && !defined(OPENSSL_IS_BORINGSSL)
#if OPENSSL_VERSION_NUMBER < 0x10100000L && !defined(OPENSSL_IS_BORINGSSL) && !defined(LIBRESSL_VERSION_NUMBER)
// |freelist_max_len| was removed in OpenSSL 1.1.0. In that version OpenSSL
// mallocs and frees buffers directly, without the use of a freelist.
SecureContext* wrap;
Expand Down Expand Up @@ -1930,6 +1934,10 @@ void SSLWrap<Base>::RequestOCSP(
template <class Base>
void SSLWrap<Base>::GetEphemeralKeyInfo(
const v8::FunctionCallbackInfo<v8::Value>& args) {
#ifdef LIBRESSL_VERSION_NUMBER
Environment* env = Environment::GetCurrent(args);
env->ThrowError("getEphemeralKeyInfo() not supported when using LibreSSL");
#else
Base* w;
ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder());
Environment* env = Environment::GetCurrent(args);
Expand Down Expand Up @@ -1968,7 +1976,8 @@ void SSLWrap<Base>::GetEphemeralKeyInfo(
EVP_PKEY_free(key);
}

return args.GetReturnValue().Set(info);
args.GetReturnValue().Set(info);
#endif // LIBRESSL_VERSION_NUMBER
}


Expand Down Expand Up @@ -2449,8 +2458,9 @@ void SSLWrap<Base>::CertCbDone(const FunctionCallbackInfo<Value>& args) {
w->sni_context_.Reset();
w->sni_context_.Reset(env->isolate(), ctx);

int rv;
int rv = 1;

#ifndef LIBRESSL_VERSION_NUMBER
// NOTE: reference count is not increased by this API methods
X509* x509 = SSL_CTX_get0_certificate(sc->ctx_);
EVP_PKEY* pkey = SSL_CTX_get0_privatekey(sc->ctx_);
Expand All @@ -2463,6 +2473,8 @@ void SSLWrap<Base>::CertCbDone(const FunctionCallbackInfo<Value>& args) {
rv = SSL_use_PrivateKey(w->ssl_, pkey);
if (rv && chain != nullptr)
rv = SSL_set1_chain(w->ssl_, chain);
#endif // LIBRESSL_VERSION_NUMBER

if (rv)
rv = w->SetCACerts(sc);
if (!rv) {
Expand Down Expand Up @@ -2526,9 +2538,11 @@ void SSLWrap<Base>::SetSNIContext(SecureContext* sc) {

template <class Base>
int SSLWrap<Base>::SetCACerts(SecureContext* sc) {
#ifndef LIBRESSL_VERSION_NUMBER
int err = SSL_set1_verify_cert_store(ssl_, SSL_CTX_get_cert_store(sc->ctx_));
if (err != 1)
return err;
#endif // LIBRESSL_VERSION_NUMBER

STACK_OF(X509_NAME)* list = SSL_dup_CA_list(
SSL_CTX_get_client_CA_list(sc->ctx_));
Expand Down Expand Up @@ -2841,7 +2855,7 @@ inline int VerifyCallback(int preverify_ok, X509_STORE_CTX* ctx) {
SSL* ssl = static_cast<SSL*>(
X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));

if (SSL_is_server(ssl))
if (ssl->server)
return 1;

// Client needs to check if the server cert is listed in the
Expand Down Expand Up @@ -2924,7 +2938,9 @@ void Connection::New(const FunctionCallbackInfo<Value>& args) {

InitNPN(sc);

#ifndef LIBRESSL_VERSION_NUMBER
SSL_set_cert_cb(conn->ssl_, SSLWrap<Connection>::SSLCertCallback, conn);
#endif // LIBRESSL_VERSION_NUMBER

#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
if (is_server) {
Expand Down Expand Up @@ -5976,11 +5992,11 @@ void SetEngine(const FunctionCallbackInfo<Value>& args) {
#endif // !OPENSSL_NO_ENGINE

void GetFipsCrypto(const FunctionCallbackInfo<Value>& args) {
if (FIPS_mode()) {
args.GetReturnValue().Set(1);
} else {
args.GetReturnValue().Set(0);
}
#ifdef NODE_FIPS_MODE
args.GetReturnValue().Set(FIPS_mode());
#else
args.GetReturnValue().Set(0);
#endif
}

void SetFipsCrypto(const FunctionCallbackInfo<Value>& args) {
Expand Down
2 changes: 2 additions & 0 deletions src/tls_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,9 @@ void TLSWrap::InitSSL() {

InitNPN(sc_);

#ifndef LIBRESSL_VERSION_NUMBER
SSL_set_cert_cb(ssl_, SSLWrap<TLSWrap>::SSLCertCallback, this);
#endif // LIBRESSL_VERSION_NUMBER

if (is_server()) {
SSL_set_accept_state(ssl_);
Expand Down
5 changes: 4 additions & 1 deletion test/parallel/test-crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const assert = require('assert');
const crypto = require('crypto');
const fs = require('fs');
const tls = require('tls');
const isLibreSSL = /LibreSSL$/.test(process.versions.openssl);

crypto.DEFAULT_ENCODING = 'buffer';

Expand Down Expand Up @@ -82,9 +83,11 @@ assert(tlsCiphers.every((value) => /^[^A-Z]+$/.test(value)));
validateList(tlsCiphers);

// Assert that we have sha and sha1 but not SHA and SHA1.
<<<<<<< 1824bbbff1341e253a891a804651b6338f8008e4
assert.notStrictEqual(0, crypto.getHashes().length);
assert(crypto.getHashes().includes('sha1'));
assert(crypto.getHashes().includes('sha'));
if (!isLibreSSL)
assert(crypto.getHashes().includes('sha'));
assert(!crypto.getHashes().includes('SHA1'));
assert(!crypto.getHashes().includes('SHA'));
assert(crypto.getHashes().includes('RSA-SHA1'));
Expand Down
8 changes: 7 additions & 1 deletion test/parallel/test-tls-client-getephemeralkeyinfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@ if (!common.hasCrypto) {
common.skip('missing crypto');
process.exit();
}
const tls = require('tls');

var isLibreSSL = /LibreSSL$/.test(process.versions.openssl);
if (isLibreSSL) {
common.skip('LibreSSL does not support getEphemeralKeyInfo()');
process.exit();
}

const tls = require('tls');
const fs = require('fs');
const key = fs.readFileSync(common.fixturesDir + '/keys/agent2-key.pem');
const cert = fs.readFileSync(common.fixturesDir + '/keys/agent2-cert.pem');
Expand Down
9 changes: 8 additions & 1 deletion test/parallel/test-tls-client-mindhsize.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,15 @@ if (!common.hasCrypto) {
common.skip('missing crypto');
process.exit();
}
const tls = require('tls');

const isLibreSSL = /LibreSSL$/.test(process.versions.openssl);

if (isLibreSSL) {
common.skip('LibreSSL does not support DH key size limiting');
process.exit();
}

const tls = require('tls');
const fs = require('fs');
const key = fs.readFileSync(common.fixturesDir + '/keys/agent2-key.pem');
const cert = fs.readFileSync(common.fixturesDir + '/keys/agent2-cert.pem');
Expand Down
5 changes: 4 additions & 1 deletion test/parallel/test-tls-cnnic-whitelist.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ const assert = require('assert');
const tls = require('tls');
const fs = require('fs');
const path = require('path');
const finished = 0;
const isLibreSSL = /LibreSSL$/.test(process.versions.openssl);

function filenamePEM(n) {
return path.join(common.fixturesDir, 'keys', n + '.pem');
Expand Down Expand Up @@ -52,7 +54,8 @@ const testCases = [
port: undefined,
rejectUnauthorized: true
},
errorCode: 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY'
errorCode: isLibreSSL ? 'CERT_UNTRUSTED' :
'UNABLE_TO_GET_ISSUER_CERT_LOCALLY'
}
];

Expand Down
12 changes: 12 additions & 0 deletions test/parallel/test-tls-empty-sni-context.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/*eslint max-len: ["error", { "ignoreComments": true }]*/

'use strict';

const common = require('../common');
Expand All @@ -7,6 +9,12 @@ if (!process.features.tls_sni) {
return;
}

var isLibreSSL = /LibreSSL$/.test(process.versions.openssl);
if (isLibreSSL) {
common.skip('Test not yet supported with LibreSSL');
process.exit();
}

const assert = require('assert');

if (!common.hasCrypto) {
Expand All @@ -24,6 +32,8 @@ const options = {
const server = tls.createServer(options, (c) => {
common.fail('Should not be called');
}).on('tlsClientError', common.mustCall((err, c) => {
//TODO: LibreSSL gives:
// 140735295963904:error:1408A0C1:SSL routines:SSL3_GET_CLIENT_HELLO:no shared cipher:s3_srvr.c:1038:
assert(/SSL_use_certificate:passed a null parameter/i.test(err.message));
server.close();
})).listen(0, common.mustCall(() => {
Expand All @@ -34,6 +44,8 @@ const server = tls.createServer(options, (c) => {
}, common.mustNotCall());

c.on('error', common.mustCall((err) => {
//TODO: LibreSSL gives:
// 140735295963904:error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure:s23_clnt.c:441:
assert(/socket hang up/.test(err.message));
}));
}));

0 comments on commit 341b8f7

Please sign in to comment.