diff --git a/configure b/configure index 5d8dee0e9dc..d2998c8d34c 100755 --- a/configure +++ b/configure @@ -112,15 +112,15 @@ parser.add_option("--systemtap-includes", dest="systemtap_includes", help=optparse.SUPPRESS_HELP) -parser.add_option("--ssl2", +parser.add_option("--without-ssl2", action="store_true", dest="ssl2", - help="Enable SSL v2") + help="Disable SSL v2") -parser.add_option("--ssl3", +parser.add_option("--without-ssl3", action="store_true", dest="ssl3", - help="Enable SSL v3") + help="Disable SSL v3") parser.add_option("--shared-zlib", action="store_true", @@ -625,10 +625,10 @@ def configure_openssl(o): if options.without_ssl: return - if not options.ssl2: + if options.ssl2: o['defines'] += ['OPENSSL_NO_SSL2=1'] - if not options.ssl3: + if options.ssl3: o['defines'] += ['OPENSSL_NO_SSL3=1'] if options.shared_openssl: diff --git a/doc/api/https.markdown b/doc/api/https.markdown index 8f608b1a7a7..e5023c3ecdd 100644 --- a/doc/api/https.markdown +++ b/doc/api/https.markdown @@ -126,8 +126,8 @@ The following options from [tls.connect()][] can also be specified. However, a the list of supplied CAs. An `'error'` event is emitted if verification fails. Verification happens at the connection level, *before* the HTTP request is sent. Default `true`. -- `secureProtocol`: The SSL method to use, e.g. `SSLv3_method` to force - SSL version 3. The possible values depend on your installation of +- `secureProtocol`: The SSL method to use, e.g. `TLSv1_method` to force + TLS version 1. The possible values depend on your installation of OpenSSL and are defined in the constant [SSL_METHODS][]. In order to specify these options, use a custom `Agent`. diff --git a/doc/api/tls.markdown b/doc/api/tls.markdown index 3c2fe6ea691..918ee443574 100644 --- a/doc/api/tls.markdown +++ b/doc/api/tls.markdown @@ -40,11 +40,13 @@ To create .pfx or .p12, do this: ## Protocol support -Node.js is compiled without SSL2/SSL3 protocol support by default. These -protocols are insecure and could be easily compromised as was shown by -[CVE-2014-3566][]. However, in some situations, it may cause -problems with legacy clients/servers (such as Internet Explorer 6). If you do -really wish to use them, please rebuild node.js with `./configure --with-ssl3`. +Node.js is compiled with SSLv2 and SSLv3 protocol support by default, but these +protocols are **disabled**. They are considered insecure and could be easily +compromised as was shown by [CVE-2014-3566][]. However, in some situations, it +may cause problems with legacy clients/servers (such as Internet Explorer 6). +If you wish to enable SSLv2 or SSLv3, run node with the `--enable-ssl2` or +`--enable-ssl3` flag respectively. In future versions of Node.js SSLv2 and +SSLv3 will not be compiled in by default. ## Client-initiated renegotiation attack mitigation diff --git a/doc/node.1 b/doc/node.1 index a381dcf3345..ab5fa73b271 100644 --- a/doc/node.1 +++ b/doc/node.1 @@ -62,6 +62,12 @@ and servers. --max-stack-size=val set max v8 stack size (bytes) + --enable-ssl2 enable ssl2 in crypto, tls, and https + modules + + --enable-ssl3 enable ssl3 in crypto, tls, and https + modules + .SH ENVIRONMENT VARIABLES diff --git a/src/node.cc b/src/node.cc index 42239730e39..bdd24eca716 100644 --- a/src/node.cc +++ b/src/node.cc @@ -82,6 +82,7 @@ typedef int mode_t; #include "node_script.h" #include "v8_typed_array.h" +#include "node_crypto.h" #include "util.h" using namespace v8; @@ -125,7 +126,6 @@ static Persistent enter_symbol; static Persistent exit_symbol; static Persistent disposed_symbol; - static bool print_eval = false; static bool force_repl = false; static bool trace_deprecation = false; @@ -2543,6 +2543,8 @@ static void PrintHelp() { " --trace-deprecation show stack traces on deprecations\n" " --v8-options print v8 command line options\n" " --max-stack-size=val set max v8 stack size (bytes)\n" + " --enable-ssl2 enable ssl2\n" + " --enable-ssl3 enable ssl3\n" "\n" "Environment variables:\n" #ifdef _WIN32 @@ -2576,6 +2578,12 @@ static void ParseArgs(int argc, char **argv) { p = 1 + strchr(arg, '='); max_stack_size = atoi(p); argv[i] = const_cast(""); + } else if (strcmp(arg, "--enable-ssl2") == 0) { + SSL2_ENABLE = true; + argv[i] = const_cast(""); + } else if (strcmp(arg, "--enable-ssl3") == 0) { + SSL3_ENABLE = true; + argv[i] = const_cast(""); } else if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0) { PrintHelp(); exit(0); @@ -3045,6 +3053,7 @@ static char **copy_argv(int argc, char **argv) { return argv_copy; } + int Start(int argc, char *argv[]) { const char* replaceInvalid = getenv("NODE_INVALID_UTF8"); diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 075a2e3b1b1..e56544d1ca8 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -69,10 +69,14 @@ const char* root_certs[] = { NULL }; +bool SSL2_ENABLE = false; +bool SSL3_ENABLE = false; + namespace crypto { using namespace v8; + // Forcibly clear OpenSSL's error stack on return. This stops stale errors // from popping up later in the lifecycle of crypto operations where they // would cause spurious failures. It's a rather blunt method, though. @@ -234,6 +238,24 @@ Handle SecureContext::New(const Arguments& args) { } +bool MaybeThrowSSL3() { + if (!SSL3_ENABLE) { + ThrowException(Exception::Error(String::New("SSLv3 is considered unsafe, see node --help"))); + return true; + } else { + return false; + } +} + +bool MaybeThrowSSL2() { + if (!SSL2_ENABLE) { + ThrowException(Exception::Error(String::New("SSLv2 is considered unsafe, see node --help"))); + return true; + } else { + return false; + } +} + Handle SecureContext::Init(const Arguments& args) { HandleScope scope; @@ -246,28 +268,46 @@ Handle SecureContext::Init(const Arguments& args) { if (strcmp(*sslmethod, "SSLv2_method") == 0) { #ifndef OPENSSL_NO_SSL2 + if (MaybeThrowSSL2()) return Undefined(); method = SSLv2_method(); #else return ThrowException(Exception::Error(String::New("SSLv2 methods disabled"))); #endif } else if (strcmp(*sslmethod, "SSLv2_server_method") == 0) { #ifndef OPENSSL_NO_SSL2 + if (MaybeThrowSSL2()) return Undefined(); method = SSLv2_server_method(); #else return ThrowException(Exception::Error(String::New("SSLv2 methods disabled"))); #endif } else if (strcmp(*sslmethod, "SSLv2_client_method") == 0) { #ifndef OPENSSL_NO_SSL2 + if (MaybeThrowSSL2()) return Undefined(); method = SSLv2_client_method(); #else return ThrowException(Exception::Error(String::New("SSLv2 methods disabled"))); #endif } else if (strcmp(*sslmethod, "SSLv3_method") == 0) { +#ifndef OPENSSL_NO_SSL3 + if (MaybeThrowSSL3()) return Undefined(); method = SSLv3_method(); +#else + return ThrowException(Exception::Error(String::New("SSLv3 methods disabled"))); +#endif } else if (strcmp(*sslmethod, "SSLv3_server_method") == 0) { +#ifndef OPENSSL_NO_SSL3 + if (MaybeThrowSSL3()) return Undefined(); method = SSLv3_server_method(); +#else + return ThrowException(Exception::Error(String::New("SSLv3 methods disabled"))); +#endif } else if (strcmp(*sslmethod, "SSLv3_client_method") == 0) { +#ifndef OPENSSL_NO_SSL3 + if (MaybeThrowSSL3()) return Undefined(); method = SSLv3_client_method(); +#else + return ThrowException(Exception::Error(String::New("SSLv3 methods disabled"))); +#endif } else if (strcmp(*sslmethod, "SSLv23_method") == 0) { method = SSLv23_method(); } else if (strcmp(*sslmethod, "SSLv23_server_method") == 0) { @@ -295,6 +335,20 @@ Handle SecureContext::Init(const Arguments& args) { SSL_CTX_sess_set_get_cb(sc->ctx_, GetSessionCallback); SSL_CTX_sess_set_new_cb(sc->ctx_, NewSessionCallback); + int options = 0; + +#ifndef OPENSSL_NO_SSL2 + if (!SSL2_ENABLE) + options |= SSL_OP_NO_SSLv2; +#endif + +#ifndef OPENSSL_NO_SSL3 + if (!SSL3_ENABLE) + options |= SSL_OP_NO_SSLv3; +#endif + + SSL_CTX_set_options(sc->ctx_, options); + sc->ca_store_ = NULL; return True(); } diff --git a/src/node_crypto.h b/src/node_crypto.h index b12638e007d..54b9b88e437 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -44,6 +44,10 @@ #define EVP_F_EVP_DECRYPTFINAL 101 namespace node { + +extern bool SSL2_ENABLE; +extern bool SSL3_ENABLE; + namespace crypto { static X509_STORE* root_cert_store; diff --git a/test/simple/test-tls-disable-ssl2.js b/test/simple/test-tls-disable-ssl2.js new file mode 100644 index 00000000000..4da38efe2ef --- /dev/null +++ b/test/simple/test-tls-disable-ssl2.js @@ -0,0 +1,53 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var tls = require('tls'); +var fs = require('fs'); + +var SSL_Method = 'SSLv2_method'; +var localhost = '127.0.0.1'; + +function test(honorCipherOrder, clientCipher, expectedCipher, cb) { + var soptions = { + secureProtocol: SSL_Method, + key: fs.readFileSync(common.fixturesDir + '/keys/agent2-key.pem'), + cert: fs.readFileSync(common.fixturesDir + '/keys/agent2-cert.pem'), + ciphers: 'AES256-SHA:RC4-SHA:DES-CBC-SHA', + honorCipherOrder: !!honorCipherOrder + }; + + var server; + + assert.throws(function() { + server = tls.createServer(soptions, function(cleartextStream) { + nconns++; + }); + }, /SSLv2 is considered unsafe/); +} + +test1(); + +function test1() { + // Client has the preference of cipher suites by default + test(false, 'DES-CBC-SHA:RC4-SHA:AES256-SHA','DES-CBC-SHA'); +} diff --git a/test/simple/test-tls-disable-ssl3.js b/test/simple/test-tls-disable-ssl3.js new file mode 100644 index 00000000000..9d514852c82 --- /dev/null +++ b/test/simple/test-tls-disable-ssl3.js @@ -0,0 +1,53 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var tls = require('tls'); +var fs = require('fs'); + +var SSL_Method = 'SSLv3_method'; +var localhost = '127.0.0.1'; + +function test(honorCipherOrder, clientCipher, expectedCipher, cb) { + var soptions = { + secureProtocol: SSL_Method, + key: fs.readFileSync(common.fixturesDir + '/keys/agent2-key.pem'), + cert: fs.readFileSync(common.fixturesDir + '/keys/agent2-cert.pem'), + ciphers: 'AES256-SHA:RC4-SHA:DES-CBC-SHA', + honorCipherOrder: !!honorCipherOrder + }; + + var server; + + assert.throws(function() { + server = tls.createServer(soptions, function(cleartextStream) { + nconns++; + }); + }, /SSLv3 is considered unsafe/); +} + +test1(); + +function test1() { + // Client has the preference of cipher suites by default + test(false, 'DES-CBC-SHA:RC4-SHA:AES256-SHA','DES-CBC-SHA'); +} diff --git a/test/simple/test-tls-enable-ssl2.js b/test/simple/test-tls-enable-ssl2.js new file mode 100644 index 00000000000..5d1fca4cbf5 --- /dev/null +++ b/test/simple/test-tls-enable-ssl2.js @@ -0,0 +1,55 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// Flags: --enable-ssl2 + +var common = require('../common'); +var assert = require('assert'); +var tls = require('tls'); +var fs = require('fs'); + +var SSL_Method = 'SSLv2_method'; +var localhost = '127.0.0.1'; + +function test(honorCipherOrder, clientCipher, expectedCipher, cb) { + var soptions = { + secureProtocol: SSL_Method, + key: fs.readFileSync(common.fixturesDir + '/keys/agent2-key.pem'), + cert: fs.readFileSync(common.fixturesDir + '/keys/agent2-cert.pem'), + ciphers: 'AES256-SHA:RC4-SHA:DES-CBC-SHA', + honorCipherOrder: !!honorCipherOrder + }; + + var server; + + assert.doesNotThrow(function() { + server = tls.createServer(soptions, function(cleartextStream) { + nconns++; + }); + }, /SSLv2 Disabled/); +} + +test1(); + +function test1() { + // Client has the preference of cipher suites by default + test(false, 'DES-CBC-SHA:RC4-SHA:AES256-SHA','DES-CBC-SHA'); +} diff --git a/test/simple/test-tls-enable-ssl3.js b/test/simple/test-tls-enable-ssl3.js new file mode 100644 index 00000000000..b85e65198c8 --- /dev/null +++ b/test/simple/test-tls-enable-ssl3.js @@ -0,0 +1,55 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// Flags: --enable-ssl3 + +var common = require('../common'); +var assert = require('assert'); +var tls = require('tls'); +var fs = require('fs'); + +var SSL_Method = 'SSLv3_method'; +var localhost = '127.0.0.1'; + +function test(honorCipherOrder, clientCipher, expectedCipher, cb) { + var soptions = { + secureProtocol: SSL_Method, + key: fs.readFileSync(common.fixturesDir + '/keys/agent2-key.pem'), + cert: fs.readFileSync(common.fixturesDir + '/keys/agent2-cert.pem'), + ciphers: 'AES256-SHA:RC4-SHA:DES-CBC-SHA', + honorCipherOrder: !!honorCipherOrder + }; + + var server; + + assert.doesNotThrow(function() { + server = tls.createServer(soptions, function(cleartextStream) { + nconns++; + }); + }, /SSLv3 Disabled/); +} + +test1(); + +function test1() { + // Client has the preference of cipher suites by default + test(false, 'DES-CBC-SHA:RC4-SHA:AES256-SHA','DES-CBC-SHA'); +}