Browse Source

tls: enable rejectUnauthorized option to client

Fiexes #2247.
v0.7.4-release
koichik 13 years ago
parent
commit
f8c335d0ca
  1. 8
      doc/api/https.markdown
  2. 4
      doc/api/tls.markdown
  3. 13
      lib/tls.js
  4. 95
      test/simple/test-https-client-reject.js
  5. 92
      test/simple/test-tls-client-reject.js

8
doc/api/https.markdown

@ -11,8 +11,8 @@ This class is a subclass of `tls.Server` and emits events same as
## https.createServer(options, [requestListener]) ## https.createServer(options, [requestListener])
Returns a new HTTPS web server object. The `options` is similar to Returns a new HTTPS web server object. The `options` is similar to
`tls.createServer()`. The `requestListener` is a function which is [tls.createServer()](tls.html#tls.createServer). The `requestListener` is
automatically added to the `'request'` event. a function which is automatically added to the `'request'` event.
Example: Example:
@ -94,6 +94,10 @@ specified. However, a [globalAgent](#https.globalAgent) silently ignores these.
- `cert`: Public x509 certificate to use. Default `null`. - `cert`: Public x509 certificate to use. Default `null`.
- `ca`: An authority certificate or array of authority certificates to check - `ca`: An authority certificate or array of authority certificates to check
the remote host against. the remote host against.
- `rejectUnauthorized`: If `true`, the server certificate is verified against
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 `false`.
In order to specify these options, use a custom `Agent`. In order to specify these options, use a custom `Agent`.

4
doc/api/tls.markdown

@ -119,6 +119,10 @@ defaults to `localhost`.) `options` should be an object which specifies
omitted several well known "root" CAs will be used, like VeriSign. omitted several well known "root" CAs will be used, like VeriSign.
These are used to authorize connections. These are used to authorize connections.
- `rejectUnauthorized`: If `true`, the server certificate is verified against
the list of supplied CAs. An `'error'` event is emitted if verification
fails. Default: `false`.
- `NPNProtocols`: An array of string or `Buffer` containing supported NPN - `NPNProtocols`: An array of string or `Buffer` containing supported NPN
protocols. `Buffer` should have following format: `0x05hello0x05world`, protocols. `Buffer` should have following format: `0x05hello0x05world`,
where first byte is next protocol name's length. (Passing array should where first byte is next protocol name's length. (Passing array should

13
lib/tls.js

@ -1023,7 +1023,8 @@ exports.connect = function(port /* host, options, cb */) {
var sslcontext = crypto.createCredentials(options); var sslcontext = crypto.createCredentials(options);
convertNPNProtocols(options.NPNProtocols, this); convertNPNProtocols(options.NPNProtocols, this);
var pair = new SecurePair(sslcontext, false, true, false, var pair = new SecurePair(sslcontext, false, true,
options.rejectUnauthorized === true ? true : false,
{ {
NPNProtocols: this.NPNProtocols, NPNProtocols: this.NPNProtocols,
servername: options.servername || host servername: options.servername || host
@ -1048,11 +1049,17 @@ exports.connect = function(port /* host, options, cb */) {
if (verifyError) { if (verifyError) {
cleartext.authorized = false; cleartext.authorized = false;
cleartext.authorizationError = verifyError; cleartext.authorizationError = verifyError;
if (pair._rejectUnauthorized) {
cleartext.emit('error', verifyError);
pair.destroy();
} else { } else {
cleartext.authorized = true; cleartext.emit('secureConnect');
} }
} else {
cleartext.authorized = true;
cleartext.emit('secureConnect'); cleartext.emit('secureConnect');
}
}); });
cleartext._controlReleased = true; cleartext._controlReleased = true;

95
test/simple/test-https-client-reject.js

@ -0,0 +1,95 @@
// 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.
if (!process.versions.openssl) {
console.error('Skipping because node compiled without OpenSSL.');
process.exit(0);
}
var common = require('../common');
var assert = require('assert');
var https = require('https');
var fs = require('fs');
var path = require('path');
var options = {
key: fs.readFileSync(path.join(common.fixturesDir, 'test_key.pem')),
cert: fs.readFileSync(path.join(common.fixturesDir, 'test_cert.pem'))
};
var reqCount = 0;
var server = https.createServer(options, function(req, res) {
++reqCount;
res.writeHead(200);
res.end();
}).listen(common.PORT, function() {
unauthorized();
});
function unauthorized() {
var req = https.request({
port: common.PORT
}, function(res) {
assert(!req.socket.authorized);
rejectUnauthorized();
});
req.on('error', function(err) {
assert(false);
});
req.end();
}
function rejectUnauthorized() {
var options = {
port: common.PORT,
rejectUnauthorized: true
};
options.agent = new https.Agent(options);
var req = https.request(options, function(res) {
assert(false);
});
req.on('error', function(err) {
authorized();
});
req.end();
}
function authorized() {
var options = {
port: common.PORT,
rejectUnauthorized: true,
ca: [ fs.readFileSync(path.join(common.fixturesDir, 'test_cert.pem')) ]
};
options.agent = new https.Agent(options);
var req = https.request(options, function(res) {
assert(req.socket.authorized);
server.close();
});
req.on('error', function(err) {
assert(false);
});
req.end();
}
process.on('exit', function() {
assert.equal(reqCount, 2);
});

92
test/simple/test-tls-client-reject.js

@ -0,0 +1,92 @@
// 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.
if (!process.versions.openssl) {
console.error('Skipping because node compiled without OpenSSL.');
process.exit(0);
}
var common = require('../common');
var assert = require('assert');
var tls = require('tls');
var fs = require('fs');
var path = require('path');
var options = {
key: fs.readFileSync(path.join(common.fixturesDir, 'test_key.pem')),
cert: fs.readFileSync(path.join(common.fixturesDir, 'test_cert.pem'))
};
var connectCount = 0;
var server = tls.createServer(options, function(socket) {
++connectCount;
socket.on('data', function(data) {
common.debug(data.toString());
assert.equal(data, 'ok');
});
}).listen(common.PORT, function() {
unauthorized();
});
function unauthorized() {
var socket = tls.connect(common.PORT, function() {
assert(!socket.authorized);
socket.end();
rejectUnauthorized();
});
socket.on('error', function(err) {
assert(false);
});
socket.write('ok');
}
function rejectUnauthorized() {
var socket = tls.connect(common.PORT, {
rejectUnauthorized: true
}, function() {
assert(false);
});
socket.on('error', function(err) {
common.debug(err);
authorized();
});
socket.write('ng');
}
function authorized() {
var socket = tls.connect(common.PORT, {
rejectUnauthorized: true,
ca: [ fs.readFileSync(path.join(common.fixturesDir, 'test_cert.pem')) ]
}, function() {
assert(socket.authorized);
socket.end();
server.close();
});
socket.on('error', function(err) {
assert(false);
});
socket.write('ok');
}
process.on('exit', function() {
assert.equal(connectCount, 3);
});
Loading…
Cancel
Save