mirror of https://github.com/lukechilds/node.git
9 changed files with 944 additions and 23 deletions
@ -0,0 +1 @@ |
|||||
|
node_modules/ |
@ -0,0 +1,15 @@ |
|||||
|
{ |
||||
|
"name": "ssl-options-tests", |
||||
|
"version": "1.0.0", |
||||
|
"description": "", |
||||
|
"main": "test.js", |
||||
|
"scripts": { |
||||
|
"test": "node test.js" |
||||
|
}, |
||||
|
"author": "", |
||||
|
"license": "MIT", |
||||
|
"dependencies": { |
||||
|
"async": "^0.9.0", |
||||
|
"debug": "^2.1.0" |
||||
|
} |
||||
|
} |
@ -0,0 +1,729 @@ |
|||||
|
var tls = require('tls'); |
||||
|
var fs = require('fs'); |
||||
|
var path = require('path'); |
||||
|
var fork = require('child_process').fork; |
||||
|
var assert = require('assert'); |
||||
|
var constants = require('constants'); |
||||
|
var os = require('os'); |
||||
|
|
||||
|
var async = require('async'); |
||||
|
var debug = require('debug')('test-node-ssl'); |
||||
|
|
||||
|
var common = require('../../common'); |
||||
|
|
||||
|
var SSL2_COMPATIBLE_CIPHERS = 'RC4-MD5'; |
||||
|
|
||||
|
var CMD_LINE_OPTIONS = [ null, "--enable-ssl2", "--enable-ssl3" ]; |
||||
|
|
||||
|
var SERVER_SSL_PROTOCOLS = [ |
||||
|
null, |
||||
|
'SSLv2_method', 'SSLv2_server_method', |
||||
|
'SSLv3_method', 'SSLv3_server_method', |
||||
|
'TLSv1_method', 'TLSv1_server_method', |
||||
|
'SSLv23_method','SSLv23_server_method' |
||||
|
]; |
||||
|
|
||||
|
var CLIENT_SSL_PROTOCOLS = [ |
||||
|
null, |
||||
|
'SSLv2_method', 'SSLv2_client_method', |
||||
|
'SSLv3_method', 'SSLv3_client_method', |
||||
|
'TLSv1_method', 'TLSv1_client_method', |
||||
|
'SSLv23_method','SSLv23_client_method' |
||||
|
]; |
||||
|
|
||||
|
var SECURE_OPTIONS = [ |
||||
|
null, |
||||
|
0, |
||||
|
constants.SSL_OP_NO_SSLv2, |
||||
|
constants.SSL_OP_NO_SSLv3, |
||||
|
constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 |
||||
|
]; |
||||
|
|
||||
|
function xtend(source) { |
||||
|
var clone = {}; |
||||
|
|
||||
|
for (var property in source) { |
||||
|
if (source.hasOwnProperty(property)) { |
||||
|
clone[property] = source[property]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return clone; |
||||
|
} |
||||
|
|
||||
|
function isAutoNegotiationProtocol(sslProtocol) { |
||||
|
assert(sslProtocol === null || typeof sslProtocol === 'string'); |
||||
|
|
||||
|
return sslProtocol == null || |
||||
|
sslProtocol === 'SSLv23_method' || |
||||
|
sslProtocol === 'SSLv23_client_method' || |
||||
|
sslProtocol === 'SSLv23_server_method'; |
||||
|
} |
||||
|
|
||||
|
function isSameSslProtocolVersion(serverSecureProtocol, clientSecureProtocol) { |
||||
|
assert(serverSecureProtocol === null || typeof serverSecureProtocol === 'string'); |
||||
|
assert(clientSecureProtocol === null || typeof clientSecureProtocol === 'string'); |
||||
|
|
||||
|
if (serverSecureProtocol === clientSecureProtocol) { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
var serverProtocolPrefix = ''; |
||||
|
if (serverSecureProtocol) |
||||
|
serverProtocolPrefix = serverSecureProtocol.split('_')[0]; |
||||
|
|
||||
|
var clientProtocolPrefix = ''; |
||||
|
if (clientSecureProtocol) |
||||
|
clientProtocolPrefix = clientSecureProtocol.split('_')[0]; |
||||
|
|
||||
|
if (serverProtocolPrefix === clientProtocolPrefix) { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
function secureProtocolsCompatible(serverSecureProtocol, clientSecureProtocol) { |
||||
|
if (isAutoNegotiationProtocol(serverSecureProtocol) || |
||||
|
isAutoNegotiationProtocol(clientSecureProtocol)) { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
if (isSameSslProtocolVersion(serverSecureProtocol, |
||||
|
clientSecureProtocol)) { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
function isSsl3Protocol(secureProtocol) { |
||||
|
assert(secureProtocol === null || typeof secureProtocol === 'string'); |
||||
|
|
||||
|
return secureProtocol === 'SSLv3_method' || |
||||
|
secureProtocol === 'SSLv3_client_method' || |
||||
|
secureProtocol === 'SSLv3_server_method'; |
||||
|
} |
||||
|
|
||||
|
function isSsl2Protocol(secureProtocol) { |
||||
|
assert(secureProtocol === null || typeof secureProtocol === 'string'); |
||||
|
|
||||
|
return secureProtocol === 'SSLv2_method' || |
||||
|
secureProtocol === 'SSLv2_client_method' || |
||||
|
secureProtocol === 'SSLv2_server_method'; |
||||
|
} |
||||
|
|
||||
|
function secureProtocolCompatibleWithSecureOptions(secureProtocol, secureOptions, cmdLineOption) { |
||||
|
if (secureOptions == null) { |
||||
|
if (isSsl2Protocol(secureProtocol) && |
||||
|
(!cmdLineOption || cmdLineOption.indexOf('--enable-ssl2') === -1)) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (isSsl3Protocol(secureProtocol) && |
||||
|
(!cmdLineOption || cmdLineOption.indexOf('--enable-ssl3') === -1)) { |
||||
|
return false; |
||||
|
} |
||||
|
} else { |
||||
|
if (secureOptions & constants.SSL_OP_NO_SSLv2 && isSsl2Protocol(secureProtocol)) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (secureOptions & constants.SSL_OP_NO_SSLv3 && isSsl3Protocol(secureProtocol)) { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
function testSetupsCompatible(serverSetup, clientSetup) { |
||||
|
debug('Determing test result for:'); |
||||
|
debug(serverSetup); |
||||
|
debug(clientSetup); |
||||
|
|
||||
|
/* |
||||
|
* If the protocols specified by the client and server are |
||||
|
* not compatible (e.g SSLv2 vs SSLv3), then the test should fail. |
||||
|
*/ |
||||
|
if (!secureProtocolsCompatible(serverSetup.secureProtocol, |
||||
|
clientSetup.secureProtocol)) { |
||||
|
debug('secureProtocols not compatible! server secureProtocol: ' + |
||||
|
serverSetup.secureProtocol + ', client secureProtocol: ' + |
||||
|
clientSetup.secureProtocol); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
* If the client's options are not compatible with the server's protocol, |
||||
|
* then the test should fail. Same if server's options are not compatible |
||||
|
* with the client's protocol. |
||||
|
*/ |
||||
|
if (!secureProtocolCompatibleWithSecureOptions(serverSetup.secureProtocol, |
||||
|
clientSetup.secureOptions, |
||||
|
clientSetup.cmdLine) || |
||||
|
!secureProtocolCompatibleWithSecureOptions(clientSetup.secureProtocol, |
||||
|
serverSetup.secureOptions, |
||||
|
serverSetup.cmdLine)) { |
||||
|
debug('Secure protocol not compatible with secure options!'); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (isSsl2Protocol(serverSetup.secureProtocol) || |
||||
|
isSsl2Protocol(clientSetup.secureProtocol)) { |
||||
|
|
||||
|
/* |
||||
|
* It seems that in order to be able to use SSLv2, at least the server |
||||
|
* *needs* to advertise at least one cipher compatible with it. |
||||
|
*/ |
||||
|
if (serverSetup.ciphers !== SSL2_COMPATIBLE_CIPHERS) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
* If only either one of the client or server specify SSLv2 as their |
||||
|
* protocol, then *both* of them *need* to advertise at least one cipher |
||||
|
* that is compatible with SSLv2. |
||||
|
*/ |
||||
|
if ((!isSsl2Protocol(serverSetup.secureProtocol) || !isSsl2Protocol(clientSetup.secureProtocol)) && |
||||
|
(clientSetup.ciphers !== SSL2_COMPATIBLE_CIPHERS || serverSetup.ciphers !== SSL2_COMPATIBLE_CIPHERS)) { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
function sslSetupMakesSense(cmdLineOption, secureProtocol, secureOption) { |
||||
|
if (isSsl2Protocol(secureProtocol)) { |
||||
|
if (secureOption & constants.SSL_OP_NO_SSLv2 || |
||||
|
(secureOption == null && (!cmdLineOption || cmdLineOption.indexOf('--enable-ssl2') === -1))) { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (isSsl3Protocol(secureProtocol)) { |
||||
|
if (secureOption & constants.SSL_OP_NO_SSLv3 || |
||||
|
(secureOption == null && (!cmdLineOption || cmdLineOption.indexOf('--enable-ssl3') === -1))) { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
function createTestsSetups() { |
||||
|
|
||||
|
var serversSetup = []; |
||||
|
var clientsSetup = []; |
||||
|
|
||||
|
CMD_LINE_OPTIONS.forEach(function (cmdLineOption) { |
||||
|
SERVER_SSL_PROTOCOLS.forEach(function (serverSecureProtocol) { |
||||
|
SECURE_OPTIONS.forEach(function (secureOption) { |
||||
|
if (sslSetupMakesSense(cmdLineOption, |
||||
|
serverSecureProtocol, |
||||
|
secureOption)) { |
||||
|
var serverSetup = { |
||||
|
cmdLine: cmdLineOption, |
||||
|
secureProtocol: serverSecureProtocol, |
||||
|
secureOptions: secureOption |
||||
|
}; |
||||
|
|
||||
|
serversSetup.push(serverSetup); |
||||
|
|
||||
|
if (isSsl2Protocol(serverSecureProtocol)) { |
||||
|
var setupWithSsl2Ciphers = xtend(serverSetup); |
||||
|
setupWithSsl2Ciphers.ciphers = SSL2_COMPATIBLE_CIPHERS; |
||||
|
serversSetup.push(setupWithSsl2Ciphers); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
CLIENT_SSL_PROTOCOLS.forEach(function (clientSecureProtocol) { |
||||
|
SECURE_OPTIONS.forEach(function (secureOption) { |
||||
|
if (sslSetupMakesSense(cmdLineOption, |
||||
|
clientSecureProtocol, |
||||
|
secureOption)) { |
||||
|
var clientSetup = { |
||||
|
cmdLine: cmdLineOption, |
||||
|
secureProtocol: clientSecureProtocol, |
||||
|
secureOptions: secureOption |
||||
|
}; |
||||
|
|
||||
|
clientsSetup.push(clientSetup); |
||||
|
|
||||
|
if (isSsl2Protocol(clientSecureProtocol)) { |
||||
|
var setupWithSsl2Ciphers = xtend(clientSetup); |
||||
|
setupWithSsl2Ciphers.ciphers = SSL2_COMPATIBLE_CIPHERS; |
||||
|
clientsSetup.push(setupWithSsl2Ciphers); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
var testSetups = []; |
||||
|
var testId = 0; |
||||
|
serversSetup.forEach(function (serverSetup) { |
||||
|
clientsSetup.forEach(function (clientSetup) { |
||||
|
var testSetup = { |
||||
|
server: serverSetup, |
||||
|
client: clientSetup, |
||||
|
ID: testId++ |
||||
|
}; |
||||
|
|
||||
|
var successExpected = false; |
||||
|
if (testSetupsCompatible(serverSetup, clientSetup)) { |
||||
|
successExpected = true; |
||||
|
} |
||||
|
testSetup.successExpected = successExpected; |
||||
|
|
||||
|
testSetups.push(testSetup); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
return testSetups; |
||||
|
} |
||||
|
|
||||
|
function runServer(port, secureProtocol, secureOptions, ciphers) { |
||||
|
debug('Running server!'); |
||||
|
debug('port: ' + port); |
||||
|
debug('secureProtocol: ' + secureProtocol); |
||||
|
debug('secureOptions: ' + secureOptions); |
||||
|
debug('ciphers: ' + ciphers); |
||||
|
|
||||
|
var keyPath = path.join(common.fixturesDir, 'agent.key'); |
||||
|
var certPath = path.join(common.fixturesDir, 'agent.crt'); |
||||
|
|
||||
|
var key = fs.readFileSync(keyPath).toString(); |
||||
|
var cert = fs.readFileSync(certPath).toString(); |
||||
|
|
||||
|
var server = new tls.Server({ key: key, |
||||
|
cert: cert, |
||||
|
ca: [], |
||||
|
ciphers: ciphers, |
||||
|
secureProtocol: secureProtocol, |
||||
|
secureOptions: secureOptions |
||||
|
}); |
||||
|
|
||||
|
server.listen(port, function() { |
||||
|
process.on('message', function onChildMsg(msg) { |
||||
|
if (msg === 'close') { |
||||
|
server.close(); |
||||
|
process.exit(0); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
process.send('server_listening'); |
||||
|
}); |
||||
|
|
||||
|
server.on('error', function onServerError(err) { |
||||
|
debug('Server error: ' + err); |
||||
|
process.exit(1); |
||||
|
}); |
||||
|
|
||||
|
server.on('clientError', function onClientError(err) { |
||||
|
debug('Client error on server: ' + err); |
||||
|
process.exit(1); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function runClient(port, secureProtocol, secureOptions, ciphers) { |
||||
|
debug('Running client!'); |
||||
|
debug('port: ' + port); |
||||
|
debug('secureProtocol: ' + secureProtocol); |
||||
|
debug('secureOptions: ' + secureOptions); |
||||
|
debug('ciphers: ' + ciphers); |
||||
|
|
||||
|
var con = tls.connect(port, |
||||
|
{ |
||||
|
rejectUnauthorized: false, |
||||
|
secureProtocol: secureProtocol, |
||||
|
secureOptions: secureOptions |
||||
|
}, |
||||
|
function() { |
||||
|
|
||||
|
// TODO jgilli: test that sslProtocolUsed is at least as "secure" as
|
||||
|
// "secureProtocol"
|
||||
|
/* |
||||
|
* var sslProtocolUsed = con.getVersion(); |
||||
|
* debug('Protocol used: ' + sslProtocolUsed); |
||||
|
*/ |
||||
|
|
||||
|
process.send('client_done'); |
||||
|
}); |
||||
|
|
||||
|
con.on('error', function(err) { |
||||
|
debug('Client could not connect:' + err); |
||||
|
process.exit(1); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function stringToSecureOptions(secureOptionsString) { |
||||
|
assert(typeof secureOptionsString === 'string'); |
||||
|
|
||||
|
var secureOptions; |
||||
|
|
||||
|
var optionStrings = secureOptionsString.split('|'); |
||||
|
optionStrings.forEach(function (option) { |
||||
|
if (option === 'SSL_OP_NO_SSLv2') { |
||||
|
secureOptions |= constants.SSL_OP_NO_SSLv2; |
||||
|
} |
||||
|
|
||||
|
if (option === 'SSL_OP_NO_SSLv3') { |
||||
|
secureOptions |= constants.SSL_OP_NO_SSLv3; |
||||
|
} |
||||
|
|
||||
|
if (option === '0') { |
||||
|
secureOptions = 0; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
return secureOptions; |
||||
|
} |
||||
|
|
||||
|
function processTestCmdLineOptions(argv){ |
||||
|
var options = {}; |
||||
|
|
||||
|
argv.forEach(function (arg) { |
||||
|
var key; |
||||
|
var value; |
||||
|
|
||||
|
var keyValue = arg.split(':'); |
||||
|
var key = keyValue[0]; |
||||
|
|
||||
|
if (keyValue.length == 2 && keyValue[1].length > 0) { |
||||
|
value = keyValue[1]; |
||||
|
|
||||
|
if (key === 'secureOptions') { |
||||
|
value = stringToSecureOptions(value); |
||||
|
} |
||||
|
|
||||
|
if (key === 'port') { |
||||
|
value = +value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
options[key] = value; |
||||
|
}); |
||||
|
|
||||
|
return options; |
||||
|
} |
||||
|
|
||||
|
function checkTestExitCode(testSetup, serverExitCode, clientExitCode) { |
||||
|
if (testSetup.successExpected) { |
||||
|
if (serverExitCode === 0 && clientExitCode === 0) { |
||||
|
debug('Test succeeded as expected!'); |
||||
|
return true; |
||||
|
} |
||||
|
} else { |
||||
|
if (serverExitCode !== 0 || clientExitCode !== 0) { |
||||
|
debug('Test failed as expected!'); |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
function secureOptionsToString(secureOptions) { |
||||
|
var secureOptsString = ''; |
||||
|
|
||||
|
if (secureOptions & constants.SSL_OP_NO_SSLv2) { |
||||
|
secureOptsString += 'SSL_OP_NO_SSLv2'; |
||||
|
} |
||||
|
|
||||
|
if (secureOptions & constants.SSL_OP_NO_SSLv3) { |
||||
|
secureOptsString += '|SSL_OP_NO_SSLv3'; |
||||
|
} |
||||
|
|
||||
|
if (secureOptions === 0) { |
||||
|
secureOptsString = '0'; |
||||
|
} |
||||
|
|
||||
|
return secureOptsString; |
||||
|
} |
||||
|
|
||||
|
function forkTestProcess(processType, testSetup, port) { |
||||
|
var argv = [ processType ]; |
||||
|
|
||||
|
if (testSetup.secureProtocol) { |
||||
|
argv.push('secureProtocol:' + testSetup.secureProtocol); |
||||
|
} else { |
||||
|
argv.push('secureProtocol:'); |
||||
|
} |
||||
|
|
||||
|
argv.push('secureOptions:' + secureOptionsToString(testSetup.secureOptions)); |
||||
|
|
||||
|
if (testSetup.ciphers) { |
||||
|
argv.push('ciphers:' + testSetup.ciphers); |
||||
|
} else { |
||||
|
argv.push('ciphers:'); |
||||
|
} |
||||
|
|
||||
|
argv.push('port:' + port); |
||||
|
|
||||
|
var forkOptions; |
||||
|
if (testSetup.cmdLine) { |
||||
|
forkOptions = { |
||||
|
execArgv: [ testSetup.cmdLine ] |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return fork(process.argv[1], |
||||
|
argv, |
||||
|
forkOptions); |
||||
|
} |
||||
|
|
||||
|
function runTest(testSetup, testDone) { |
||||
|
var clientSetup = testSetup.client; |
||||
|
var serverSetup = testSetup.server; |
||||
|
|
||||
|
assert(clientSetup); |
||||
|
assert(serverSetup); |
||||
|
|
||||
|
debug('Starting new test on port: ' + testSetup.port); |
||||
|
|
||||
|
debug('client setup:'); |
||||
|
debug(clientSetup); |
||||
|
|
||||
|
debug('server setup:'); |
||||
|
debug(serverSetup); |
||||
|
|
||||
|
debug('Success expected:' + testSetup.successExpected); |
||||
|
|
||||
|
var serverExitCode; |
||||
|
|
||||
|
var clientStarted = false; |
||||
|
var clientExitCode; |
||||
|
|
||||
|
var serverChild = forkTestProcess('server', serverSetup, testSetup.port); |
||||
|
assert(serverChild); |
||||
|
|
||||
|
serverChild.on('message', function onServerMsg(msg) { |
||||
|
if (msg === 'server_listening') { |
||||
|
debug('Starting client!'); |
||||
|
clientStarted = true; |
||||
|
|
||||
|
var clientChild = forkTestProcess('client', clientSetup, testSetup.port); |
||||
|
assert(clientChild); |
||||
|
|
||||
|
clientChild.on('exit', function onClientExited(exitCode) { |
||||
|
debug('Client exited with code:' + exitCode); |
||||
|
|
||||
|
clientExitCode = exitCode; |
||||
|
if (serverExitCode != null) { |
||||
|
var err; |
||||
|
if (!checkTestExitCode(testSetup, serverExitCode, clientExitCode)) |
||||
|
err = new Error("Test failed!"); |
||||
|
|
||||
|
return testDone(err); |
||||
|
} else { |
||||
|
if (serverChild.connected) { |
||||
|
serverChild.send('close'); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
clientChild.on('message', function onClientMsg(msg) { |
||||
|
if (msg === 'client_done' && serverChild.connected) { |
||||
|
serverChild.send('close'); |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
serverChild.on('exit', function onServerExited(exitCode) { |
||||
|
debug('Server exited with code:' + exitCode); |
||||
|
|
||||
|
serverExitCode = exitCode; |
||||
|
if (clientExitCode != null || !clientStarted) { |
||||
|
var err; |
||||
|
if (!checkTestExitCode(testSetup, serverExitCode, clientExitCode)) |
||||
|
err = new Error("Test failed!"); |
||||
|
|
||||
|
return testDone(err); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function usage() { |
||||
|
console.log('Usage: test-node-ssl [-j N] [--list-tests] [-s startIndex] ' + |
||||
|
'[-e endIndex] [-o outputFile]'); |
||||
|
process.exit(1); |
||||
|
} |
||||
|
|
||||
|
function processDriverCmdLineOptions(argv) { |
||||
|
var options = { |
||||
|
parallelTests: 1 |
||||
|
}; |
||||
|
|
||||
|
for (var i = 1; i < argv.length; ++i) { |
||||
|
if (argv[i] === '-j') { |
||||
|
|
||||
|
var nbParallelTests = +argv[i + 1]; |
||||
|
if (!nbParallelTests) { |
||||
|
usage(); |
||||
|
} else { |
||||
|
options.parallelTests = argv[++i]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (argv[i] === '-s') { |
||||
|
var start = +argv[i + 1]; |
||||
|
if (!start) { |
||||
|
usage(); |
||||
|
} else { |
||||
|
options.start = argv[++i]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (argv[i] === '-e') { |
||||
|
var end = +argv[i + 1]; |
||||
|
if (!end) { |
||||
|
usage(); |
||||
|
} else { |
||||
|
options.end = argv[++i]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (argv[i] === '--list-tests') { |
||||
|
options.listTests = true; |
||||
|
} |
||||
|
|
||||
|
if (argv[i] === '-o') { |
||||
|
var outputFile = argv[i + 1]; |
||||
|
if (!outputFile) { |
||||
|
usage(); |
||||
|
} else { |
||||
|
options.outputFile = argv[++i]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return options; |
||||
|
} |
||||
|
|
||||
|
function outputTestResult(test, err, output) { |
||||
|
output.write(os.EOL); |
||||
|
output.write('Test:' + os.EOL); |
||||
|
output.write(JSON.stringify(test, null, " ")); |
||||
|
output.write(os.EOL); |
||||
|
output.write('Result:'); |
||||
|
output.write(err ? 'failure' : 'success'); |
||||
|
output.write(os.EOL); |
||||
|
} |
||||
|
|
||||
|
var agentType = process.argv[2]; |
||||
|
if (agentType === 'client' || agentType === 'server') { |
||||
|
var options = processTestCmdLineOptions(process.argv); |
||||
|
debug('secureProtocol: ' + options.secureProtocol); |
||||
|
debug('secureOptions: ' + options.secureOptions); |
||||
|
debug('ciphers:' + options.ciphers); |
||||
|
debug('port:' + options.port); |
||||
|
|
||||
|
if (agentType === 'client') { |
||||
|
runClient(options.port, |
||||
|
options.secureProtocol, |
||||
|
options.secureOptions, |
||||
|
options.ciphers); |
||||
|
} else if (agentType === 'server') { |
||||
|
runServer(options.port, |
||||
|
options.secureProtocol, |
||||
|
options.secureOptions, |
||||
|
options.ciphers); |
||||
|
} |
||||
|
} else { |
||||
|
var driverOptions = processDriverCmdLineOptions(process.argv); |
||||
|
debug('Tests driver options:'); |
||||
|
debug(driverOptions); |
||||
|
/* |
||||
|
* This is the tests driver process. |
||||
|
* |
||||
|
* It forks itself twice for each test. Each of the two forked processees are |
||||
|
* respectfully used as an SSL client and an SSL server. The client and |
||||
|
* server setup their SSL connection as generated by the "createTestsSetups" |
||||
|
* function. Once both processes have exited, the tests driver process |
||||
|
* compare both client and server exit codes with the expected test result |
||||
|
* of the test setup. If they match, the test is successful, otherwise it |
||||
|
* failed. |
||||
|
*/ |
||||
|
|
||||
|
var testSetups = createTestsSetups(); |
||||
|
|
||||
|
if (driverOptions.listTests) { |
||||
|
console.log(testSetups); |
||||
|
process.exit(0); |
||||
|
} |
||||
|
|
||||
|
var testOutput = process.stdout; |
||||
|
if (driverOptions.outputFile) { |
||||
|
testOutput = fs.createWriteStream(driverOptions.outputFile) |
||||
|
.on('error', function onError(err) { |
||||
|
console.error(err); |
||||
|
process.exit(1); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
debug('Tests setups:'); |
||||
|
debug('Number of tests: ' + testSetups.length); |
||||
|
debug(JSON.stringify(testSetups, null, " ")); |
||||
|
debug(); |
||||
|
|
||||
|
var nbTestsStarted = 0; |
||||
|
|
||||
|
function runTests(tests, callback) { |
||||
|
var nbTests = tests.length; |
||||
|
if (nbTests === 0) { |
||||
|
return callback(); |
||||
|
} |
||||
|
var error; |
||||
|
var nbTestsDone = 0; |
||||
|
|
||||
|
debug('Starting new batch of tests...'); |
||||
|
|
||||
|
var port = common.PORT; |
||||
|
async.each(tests, function (test, testDone) { |
||||
|
test.port = port++; |
||||
|
|
||||
|
++nbTestsStarted; |
||||
|
debug('Starting test nb: ' + nbTestsStarted); |
||||
|
|
||||
|
runTest(test, function onTestDone(err) { |
||||
|
++nbTestsDone; |
||||
|
if (err && error === undefined) { |
||||
|
error = new Error('Test with ID ' + test.ID + ' failed: ' + err); |
||||
|
} |
||||
|
|
||||
|
outputTestResult(test, err, testOutput); |
||||
|
|
||||
|
if (nbTestsDone === nbTests) |
||||
|
return testDone(error); |
||||
|
return testDone(); |
||||
|
}); |
||||
|
|
||||
|
}, function testsDone(err, results) { |
||||
|
if (err) { |
||||
|
assert(false, |
||||
|
"At least one test in the most recent batch failed: " + err); |
||||
|
} |
||||
|
|
||||
|
return callback(err); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function runAllTests(allTests, allTestsDone) { |
||||
|
if (allTests.length === 0) { |
||||
|
return allTestsDone(); |
||||
|
} |
||||
|
|
||||
|
return runTests(allTests.splice(0, driverOptions.parallelTests), |
||||
|
runAllTests.bind(global, allTests, allTestsDone)); |
||||
|
} |
||||
|
|
||||
|
runAllTests(testSetups.slice(driverOptions.start, driverOptions.end), |
||||
|
function allDone(err) { |
||||
|
console.log('All tests done!'); |
||||
|
}); |
||||
|
} |
@ -0,0 +1,131 @@ |
|||||
|
// 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 nconns = 0; |
||||
|
var SSL_Method = 'SSLv23_method'; |
||||
|
var localhost = '127.0.0.1'; |
||||
|
var opCipher = process.binding('constants').SSL_OP_CIPHER_SERVER_PREFERENCE; |
||||
|
|
||||
|
/* |
||||
|
* This test is to make sure we are preserving secureOptions that are passed |
||||
|
* to the server. |
||||
|
* |
||||
|
* Also that if honorCipherOrder is passed we are preserving that in the |
||||
|
* options. |
||||
|
* |
||||
|
* And that if we are passing in secureOptions no new options (aside from the |
||||
|
* honorCipherOrder case) are added to the secureOptions |
||||
|
*/ |
||||
|
|
||||
|
|
||||
|
process.on('exit', function() { |
||||
|
assert.equal(nconns, 6); |
||||
|
}); |
||||
|
|
||||
|
function test(honorCipherOrder, clientCipher, expectedCipher, secureOptions, 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', |
||||
|
secureOptions: secureOptions, |
||||
|
honorCipherOrder: !!honorCipherOrder |
||||
|
}; |
||||
|
|
||||
|
var server = tls.createServer(soptions, function(cleartextStream) { |
||||
|
nconns++; |
||||
|
}); |
||||
|
|
||||
|
if (!!honorCipherOrder) { |
||||
|
assert.strictEqual(server.secureOptions & opCipher, opCipher, 'we should preserve cipher preference'); |
||||
|
} |
||||
|
|
||||
|
if (secureOptions) { |
||||
|
var expectedSecureOpts = secureOptions; |
||||
|
if (!!honorCipherOrder) expectedSecureOpts |= opCipher; |
||||
|
|
||||
|
assert.strictEqual(server.secureOptions & expectedSecureOpts, |
||||
|
expectedSecureOpts, 'we should preserve secureOptions'); |
||||
|
assert.strictEqual(server.secureOptions & ~expectedSecureOpts, |
||||
|
0, |
||||
|
'we should not add extra options'); |
||||
|
} |
||||
|
|
||||
|
server.listen(common.PORT, localhost, function() { |
||||
|
var coptions = { |
||||
|
rejectUnauthorized: false, |
||||
|
secureProtocol: SSL_Method |
||||
|
}; |
||||
|
if (clientCipher) { |
||||
|
coptions.ciphers = clientCipher; |
||||
|
} |
||||
|
var client = tls.connect(common.PORT, localhost, coptions, function() { |
||||
|
var cipher = client.getCipher(); |
||||
|
client.end(); |
||||
|
server.close(); |
||||
|
assert.equal(cipher.name, expectedCipher); |
||||
|
if (cb) cb(); |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
test1(); |
||||
|
|
||||
|
function test1() { |
||||
|
// Client has the preference of cipher suites by default
|
||||
|
test(false, 'DES-CBC-SHA:RC4-SHA:AES256-SHA','DES-CBC-SHA', 0, test2); |
||||
|
} |
||||
|
|
||||
|
function test2() { |
||||
|
// Server has the preference of cipher suites where AES256-SHA is in
|
||||
|
// the first.
|
||||
|
test(true, 'DES-CBC-SHA:RC4-SHA:AES256-SHA', 'AES256-SHA', 0, test3); |
||||
|
} |
||||
|
|
||||
|
function test3() { |
||||
|
// Server has the preference of cipher suites. RC4-SHA is given
|
||||
|
// higher priority over DES-CBC-SHA among client cipher suites.
|
||||
|
test(true, 'DES-CBC-SHA:RC4-SHA', 'RC4-SHA', 0, test4); |
||||
|
} |
||||
|
|
||||
|
function test4() { |
||||
|
// As client has only one cipher, server has no choice in regardless
|
||||
|
// of honorCipherOrder.
|
||||
|
test(true, 'DES-CBC-SHA', 'DES-CBC-SHA', 0, test5); |
||||
|
} |
||||
|
|
||||
|
function test5() { |
||||
|
test(false, |
||||
|
'DES-CBC-SHA', |
||||
|
'DES-CBC-SHA', |
||||
|
process.binding('constants').SSL_OP_SINGLE_DH_USE, test6); |
||||
|
} |
||||
|
|
||||
|
function test6() { |
||||
|
test(true, |
||||
|
'DES-CBC-SHA', |
||||
|
'DES-CBC-SHA', |
||||
|
process.binding('constants').SSL_OP_SINGLE_DH_USE); |
||||
|
} |
Loading…
Reference in new issue