This repository has been archived by the owner. It is now read-only.
Permalink
Browse files

tls: enable rejectUnauthorized option to client

Fiexes #2247.
  • Loading branch information...
1 parent e0a207c commit f8c335d0caf47f16d31413f89aa28eda3878e3aa @koichik koichik committed Dec 7, 2011
Showing with 207 additions and 5 deletions.
  1. +6 −2 doc/api/https.markdown
  2. +4 −0 doc/api/tls.markdown
  3. +10 −3 lib/tls.js
  4. +95 −0 test/simple/test-https-client-reject.js
  5. +92 −0 test/simple/test-tls-client-reject.js
View
@@ -11,8 +11,8 @@ This class is a subclass of `tls.Server` and emits events same as
## https.createServer(options, [requestListener])
Returns a new HTTPS web server object. The `options` is similar to
-`tls.createServer()`. The `requestListener` is a function which is
-automatically added to the `'request'` event.
+[tls.createServer()](tls.html#tls.createServer). The `requestListener` is
+a function which is automatically added to the `'request'` event.
Example:
@@ -94,6 +94,10 @@ specified. However, a [globalAgent](#https.globalAgent) silently ignores these.
- `cert`: Public x509 certificate to use. Default `null`.
- `ca`: An authority certificate or array of authority certificates to check
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`.
View
@@ -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.
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
protocols. `Buffer` should have following format: `0x05hello0x05world`,
where first byte is next protocol name's length. (Passing array should
View
@@ -1023,7 +1023,8 @@ exports.connect = function(port /* host, options, cb */) {
var sslcontext = crypto.createCredentials(options);
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,
servername: options.servername || host
@@ -1048,11 +1049,17 @@ exports.connect = function(port /* host, options, cb */) {
if (verifyError) {
cleartext.authorized = false;
cleartext.authorizationError = verifyError;
+
+ if (pair._rejectUnauthorized) {
+ cleartext.emit('error', verifyError);
+ pair.destroy();
+ } else {
+ cleartext.emit('secureConnect');
+ }
} else {
cleartext.authorized = true;
+ cleartext.emit('secureConnect');
}
-
- cleartext.emit('secureConnect');
});
cleartext._controlReleased = true;
@@ -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);
+});
@@ -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);
+});

0 comments on commit f8c335d

Please sign in to comment.