diff --git a/lib/_tls_common.js b/lib/_tls_common.js index d8f6afed0bd8fb..de7cff4ee7b6a1 100644 --- a/lib/_tls_common.js +++ b/lib/_tls_common.js @@ -36,9 +36,11 @@ var crypto = null; const { SecureContext: NativeSecureContext } = process.binding('crypto'); -function SecureContext(secureProtocol, secureOptions, context) { +function SecureContext(min_version, max_version, secureProtocol, + secureOptions, context) { if (!(this instanceof SecureContext)) { - return new SecureContext(secureProtocol, secureOptions, context); + return new SecureContext(min_version, max_version, + secureProtocol, secureOptions, context); } if (context) { @@ -47,9 +49,9 @@ function SecureContext(secureProtocol, secureOptions, context) { this.context = new NativeSecureContext(); if (secureProtocol) { - this.context.init(secureProtocol); + this.context.init(min_version, max_version, secureProtocol); } else { - this.context.init(); + this.context.init(min_version, max_version); } } @@ -86,7 +88,14 @@ exports.createSecureContext = function createSecureContext(options, context) { if (options.honorCipherOrder) secureOptions |= SSL_OP_CIPHER_SERVER_PREFERENCE; - const c = new SecureContext(options.secureProtocol, secureOptions, context); + if (!options.min_version) + options.min_version = tls.DEFAULT_MIN_VERSION; + + if (!options.max_version) + options.max_version = tls.DEFAULT_MAX_VERSION; + + const c = new SecureContext(options.min_version, options.max_version, + options.secureProtocol, secureOptions, context); var i; var val; diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index 2e6b2e8da559db..fc0fd4282007a2 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -885,6 +885,8 @@ function Server(options, listener) { ciphers: this.ciphers, ecdhCurve: this.ecdhCurve, dhparam: this.dhparam, + min_version: this.min_version, + max_version: this.max_version, secureProtocol: this.secureProtocol, secureOptions: this.secureOptions, honorCipherOrder: this.honorCipherOrder, @@ -957,6 +959,8 @@ Server.prototype.setOptions = function(options) { if (options.clientCertEngine) this.clientCertEngine = options.clientCertEngine; if (options.ca) this.ca = options.ca; + if (options.min_version) this.min_version = options.min_version; + if (options.max_version) this.max_version = options.max_version; if (options.secureProtocol) this.secureProtocol = options.secureProtocol; if (options.crl) this.crl = options.crl; if (options.ciphers) this.ciphers = options.ciphers; diff --git a/lib/https.js b/lib/https.js index 169c61900cff1c..1bf734c5f24ab2 100644 --- a/lib/https.js +++ b/lib/https.js @@ -186,6 +186,14 @@ Agent.prototype.getName = function getName(options) { if (options.servername && options.servername !== options.host) name += options.servername; + name += ':'; + if (options.min_version) + name += options.min_version; + + name += ':'; + if (options.max_version) + name += options.max_version; + name += ':'; if (options.secureProtocol) name += options.secureProtocol; diff --git a/lib/tls.js b/lib/tls.js index 28cd302674b04c..914041c4fcd4ca 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -52,6 +52,11 @@ exports.DEFAULT_CIPHERS = exports.DEFAULT_ECDH_CURVE = 'auto'; +// disable TLS1.3 by default for cipher suite incompatibilities with TLS1.2 +exports.DEFAULT_MAX_VERSION = 'TLSv1.2'; + +exports.DEFAULT_MIN_VERSION = 'TLSv1'; + exports.getCiphers = internalUtil.cachedResult( () => internalUtil.filterDuplicateStrings(binding.getSSLCiphers(), true) ); diff --git a/src/node_crypto.cc b/src/node_crypto.cc index ce9c41deaebcd2..40e3eadfeaff3d 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -363,6 +363,24 @@ void SecureContext::New(const FunctionCallbackInfo& args) { } +int string_to_tls_protocol(const char* version_str) { + int version; + + if (strcmp(version_str, "TLSv1.3") == 0) { + version = TLS1_3_VERSION; + } else if (strcmp(version_str, "TLSv1.2") == 0) { + version = TLS1_2_VERSION; + } else if (strcmp(version_str, "TLSv1.1") == 0) { + version = TLS1_1_VERSION; + } else if (strcmp(version_str, "TLSv1") == 0) { + version = TLS1_VERSION; + } else { + version = 0; + } + return version; +} + + void SecureContext::Init(const FunctionCallbackInfo& args) { SecureContext* sc; ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder()); @@ -370,10 +388,21 @@ void SecureContext::Init(const FunctionCallbackInfo& args) { int min_version = 0; int max_version = 0; + + if (args[0]->IsString()) { + const node::Utf8Value min(env->isolate(), args[0]); + min_version = string_to_tls_protocol(*min); + } + + if (args[1]->IsString()) { + const node::Utf8Value max(env->isolate(), args[1]); + max_version = string_to_tls_protocol(*max); + } + const SSL_METHOD* method = TLS_method(); - if (args.Length() == 1 && args[0]->IsString()) { - const node::Utf8Value sslmethod(env->isolate(), args[0]); + if (args.Length() == 3 && args[2]->IsString()) { + const node::Utf8Value sslmethod(env->isolate(), args[2]); // Note that SSLv2 and SSLv3 are disallowed but SSLv23_method and friends // are still accepted. They are OpenSSL's way of saying that all known diff --git a/test/parallel/test-https-agent-getname.js b/test/parallel/test-https-agent-getname.js index c29e09731df0b2..d27763c215c540 100644 --- a/test/parallel/test-https-agent-getname.js +++ b/test/parallel/test-https-agent-getname.js @@ -12,7 +12,7 @@ const agent = new https.Agent(); // empty options assert.strictEqual( agent.getName({}), - 'localhost:::::::::::::::::' + 'localhost:::::::::::::::::::' ); // pass all options arguments @@ -39,5 +39,5 @@ const options = { assert.strictEqual( agent.getName(options), '0.0.0.0:443:192.168.1.1:ca:cert::ciphers:key:pfx:false:localhost:' + - 'secureProtocol:c,r,l:false:ecdhCurve:dhparam:0:sessionIdContext' + '::secureProtocol:c,r,l:false:ecdhCurve:dhparam:0:sessionIdContext' );