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

Commit 6f24649

Browse files
author
Julien Gilli
committed
tls: fix default ciphers not used consistently
Passing null or undefined for the ciphers value of the options parameter of tls.connect and https.get/request makes node *not* use the default ciphers list. This problem had been fixed in node v0.12 with commit 5d2aef1, but for some reason the fix hasn't been backported to v0.10. This change also comes with a test that makes sure that tls/https clients that don't specify a ciphers suite (or a null or undefined one) cannot connect to a server that specifies only RC4-MD5 as the available ciphers suite. This test relies on the fact that RC4-MD5 is not available in the default ciphers suite, which is the case currently in the v0.10 branch.
1 parent 4028669 commit 6f24649

File tree

2 files changed

+127
-1
lines changed

2 files changed

+127
-1
lines changed

lib/crypto.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,11 @@ exports.createCredentials = function(options, context) {
134134

135135
if (options.cert) c.context.setCert(options.cert);
136136

137-
if (options.ciphers) c.context.setCiphers(options.ciphers);
137+
if (options.ciphers) {
138+
c.context.setCiphers(options.ciphers);
139+
} else {
140+
c.context.setCiphers(binding.DEFAULT_CIPHER_LIST);
141+
}
138142

139143
if (options.ca) {
140144
if (Array.isArray(options.ca)) {
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// Copyright Joyent, Inc. and other Node contributors.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a
4+
// copy of this software and associated documentation files (the
5+
// "Software"), to deal in the Software without restriction, including
6+
// without limitation the rights to use, copy, modify, merge, publish,
7+
// distribute, sublicense, and/or sell copies of the Software, and to permit
8+
// persons to whom the Software is furnished to do so, subject to the
9+
// following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included
12+
// in all copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17+
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20+
// USE OR OTHER DEALINGS IN THE SOFTWARE.
21+
22+
/*
23+
* This is a regression test that highlights a problem where tls.connect would
24+
* not use the default ciphers suite when no ciphers was passed.
25+
*
26+
* This test makes sure that when connecting to a server that uses only the RC4
27+
* cipher, which was removed recently from the default ciphers suite, calls to
28+
* tls.connect that don't pass any ciphers fail to connect properly.
29+
*/
30+
31+
var common = require('../common');
32+
var assert = require('assert');
33+
var tls = require('tls');
34+
var https = require('https');
35+
var path = require('path');
36+
var fs = require('fs');
37+
38+
var KEYPATH = path.join(common.fixturesDir, 'keys', 'agent2-key.pem');
39+
var KEY = fs.readFileSync(KEYPATH).toString();
40+
41+
var CERTPATH = path.join(common.fixturesDir, 'keys', 'agent2-cert.pem');
42+
var CERT = fs.readFileSync(CERTPATH).toString();
43+
44+
var SERVER_OPTIONS = {
45+
key: KEY,
46+
cert: CERT,
47+
// RC4 was removed recently from the default ciphers list
48+
// due to security vulnerabilities. Set the server to only use this
49+
// cipher so that this test can make sure that tls clients
50+
// can't connect to it when they use default ciphers.
51+
ciphers: 'RC4-MD5',
52+
};
53+
54+
/*
55+
* Uses "clientFn" with default ciphers to connect to TLS/HTTPS server
56+
* on port "port". Calls "callback" with an object as parameter
57+
* that has one property "allClientsFailed" that is true
58+
* if all clients connections/requests failed, false otherwise.
59+
*/
60+
function testClientWithDefaultCiphers(clientFn, port, callback) {
61+
// Use different forms of passing default ciphers to
62+
// tls connect: no 'ciphers' property, 'ciphers' with value 'null' and
63+
// 'ciphers' with value 'undefined'. Although we would think that these
64+
// semantically similar ways of passing no ciphers would trigger the same
65+
// behavior, in reality the implementation behaves differently, and
66+
// thus we need to keep all these forms in the test.
67+
var CLIENT_OPTIONS = [
68+
{
69+
rejectUnauthorized: false,
70+
ciphers: null
71+
},
72+
{
73+
rejectUnauthorized: false,
74+
ciphers: undefined
75+
},
76+
{
77+
rejectUnauthorized: false,
78+
}
79+
];
80+
81+
var nbClientErrors = 0;
82+
var callbackCalled = false;
83+
84+
CLIENT_OPTIONS.forEach(function(clientOptionsObject) {
85+
clientOptionsObject.port = port;
86+
87+
var conn = clientFn(clientOptionsObject, function onConnect() {
88+
callbackCalled = true;
89+
return callback({ allClientsFailed: false });
90+
});
91+
92+
conn.on('error', function onClientError(err) {
93+
++nbClientErrors;
94+
if (nbClientErrors === CLIENT_OPTIONS.length && !callbackCalled) {
95+
callbackCalled = true;
96+
return callback({ allClientsFailed: true });
97+
}
98+
});
99+
});
100+
}
101+
102+
// Test that tls.connect uses default ciphers on the client properly
103+
var tlsServer = new tls.Server(SERVER_OPTIONS);
104+
tlsServer.listen(common.PORT, function () {
105+
testClientWithDefaultCiphers(tls.connect, common.PORT, function onTestsDone(results) {
106+
assert(results.allClientsFailed,
107+
'All TLS clients using default ciphers to server only ' +
108+
'supporting RC4 cipher should have failed');
109+
tlsServer.close();
110+
});
111+
});
112+
113+
// Test that https.get uses default ciphers on the client properly
114+
var httpsServer = new https.Server(SERVER_OPTIONS);
115+
httpsServer.listen(common.PORT + 1, function () {
116+
testClientWithDefaultCiphers(https.get, common.PORT + 1, function onTestsDone(results) {
117+
assert(results.allClientsFailed,
118+
'All HTTPS clients using default ciphers to server only ' +
119+
'supporting RC4 cipher should have failed');
120+
httpsServer.close();
121+
});
122+
});

0 commit comments

Comments
 (0)