Skip to content

Commit f8c335d

Browse files
committed
tls: enable rejectUnauthorized option to client
Fiexes #2247.
1 parent e0a207c commit f8c335d

File tree

5 files changed

+207
-5
lines changed

5 files changed

+207
-5
lines changed

doc/api/https.markdown

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ This class is a subclass of `tls.Server` and emits events same as
1111
## https.createServer(options, [requestListener])
1212

1313
Returns a new HTTPS web server object. The `options` is similar to
14-
`tls.createServer()`. The `requestListener` is a function which is
15-
automatically added to the `'request'` event.
14+
[tls.createServer()](tls.html#tls.createServer). The `requestListener` is
15+
a function which is automatically added to the `'request'` event.
1616

1717
Example:
1818

@@ -94,6 +94,10 @@ specified. However, a [globalAgent](#https.globalAgent) silently ignores these.
9494
- `cert`: Public x509 certificate to use. Default `null`.
9595
- `ca`: An authority certificate or array of authority certificates to check
9696
the remote host against.
97+
- `rejectUnauthorized`: If `true`, the server certificate is verified against
98+
the list of supplied CAs. An `'error'` event is emitted if verification
99+
fails. Verification happens at the connection level, *before* the HTTP
100+
request is sent. Default `false`.
97101

98102
In order to specify these options, use a custom `Agent`.
99103

doc/api/tls.markdown

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ defaults to `localhost`.) `options` should be an object which specifies
119119
omitted several well known "root" CAs will be used, like VeriSign.
120120
These are used to authorize connections.
121121

122+
- `rejectUnauthorized`: If `true`, the server certificate is verified against
123+
the list of supplied CAs. An `'error'` event is emitted if verification
124+
fails. Default: `false`.
125+
122126
- `NPNProtocols`: An array of string or `Buffer` containing supported NPN
123127
protocols. `Buffer` should have following format: `0x05hello0x05world`,
124128
where first byte is next protocol name's length. (Passing array should

lib/tls.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,7 +1023,8 @@ exports.connect = function(port /* host, options, cb */) {
10231023
var sslcontext = crypto.createCredentials(options);
10241024

10251025
convertNPNProtocols(options.NPNProtocols, this);
1026-
var pair = new SecurePair(sslcontext, false, true, false,
1026+
var pair = new SecurePair(sslcontext, false, true,
1027+
options.rejectUnauthorized === true ? true : false,
10271028
{
10281029
NPNProtocols: this.NPNProtocols,
10291030
servername: options.servername || host
@@ -1048,11 +1049,17 @@ exports.connect = function(port /* host, options, cb */) {
10481049
if (verifyError) {
10491050
cleartext.authorized = false;
10501051
cleartext.authorizationError = verifyError;
1052+
1053+
if (pair._rejectUnauthorized) {
1054+
cleartext.emit('error', verifyError);
1055+
pair.destroy();
1056+
} else {
1057+
cleartext.emit('secureConnect');
1058+
}
10511059
} else {
10521060
cleartext.authorized = true;
1061+
cleartext.emit('secureConnect');
10531062
}
1054-
1055-
cleartext.emit('secureConnect');
10561063
});
10571064

10581065
cleartext._controlReleased = true;
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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+
if (!process.versions.openssl) {
23+
console.error('Skipping because node compiled without OpenSSL.');
24+
process.exit(0);
25+
}
26+
27+
var common = require('../common');
28+
var assert = require('assert');
29+
var https = require('https');
30+
var fs = require('fs');
31+
var path = require('path');
32+
33+
var options = {
34+
key: fs.readFileSync(path.join(common.fixturesDir, 'test_key.pem')),
35+
cert: fs.readFileSync(path.join(common.fixturesDir, 'test_cert.pem'))
36+
};
37+
38+
var reqCount = 0;
39+
40+
var server = https.createServer(options, function(req, res) {
41+
++reqCount;
42+
res.writeHead(200);
43+
res.end();
44+
}).listen(common.PORT, function() {
45+
unauthorized();
46+
});
47+
48+
function unauthorized() {
49+
var req = https.request({
50+
port: common.PORT
51+
}, function(res) {
52+
assert(!req.socket.authorized);
53+
rejectUnauthorized();
54+
});
55+
req.on('error', function(err) {
56+
assert(false);
57+
});
58+
req.end();
59+
}
60+
61+
function rejectUnauthorized() {
62+
var options = {
63+
port: common.PORT,
64+
rejectUnauthorized: true
65+
};
66+
options.agent = new https.Agent(options);
67+
var req = https.request(options, function(res) {
68+
assert(false);
69+
});
70+
req.on('error', function(err) {
71+
authorized();
72+
});
73+
req.end();
74+
}
75+
76+
function authorized() {
77+
var options = {
78+
port: common.PORT,
79+
rejectUnauthorized: true,
80+
ca: [ fs.readFileSync(path.join(common.fixturesDir, 'test_cert.pem')) ]
81+
};
82+
options.agent = new https.Agent(options);
83+
var req = https.request(options, function(res) {
84+
assert(req.socket.authorized);
85+
server.close();
86+
});
87+
req.on('error', function(err) {
88+
assert(false);
89+
});
90+
req.end();
91+
}
92+
93+
process.on('exit', function() {
94+
assert.equal(reqCount, 2);
95+
});
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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+
if (!process.versions.openssl) {
23+
console.error('Skipping because node compiled without OpenSSL.');
24+
process.exit(0);
25+
}
26+
27+
var common = require('../common');
28+
var assert = require('assert');
29+
var tls = require('tls');
30+
var fs = require('fs');
31+
var path = require('path');
32+
33+
var options = {
34+
key: fs.readFileSync(path.join(common.fixturesDir, 'test_key.pem')),
35+
cert: fs.readFileSync(path.join(common.fixturesDir, 'test_cert.pem'))
36+
};
37+
38+
var connectCount = 0;
39+
40+
var server = tls.createServer(options, function(socket) {
41+
++connectCount;
42+
socket.on('data', function(data) {
43+
common.debug(data.toString());
44+
assert.equal(data, 'ok');
45+
});
46+
}).listen(common.PORT, function() {
47+
unauthorized();
48+
});
49+
50+
function unauthorized() {
51+
var socket = tls.connect(common.PORT, function() {
52+
assert(!socket.authorized);
53+
socket.end();
54+
rejectUnauthorized();
55+
});
56+
socket.on('error', function(err) {
57+
assert(false);
58+
});
59+
socket.write('ok');
60+
}
61+
62+
function rejectUnauthorized() {
63+
var socket = tls.connect(common.PORT, {
64+
rejectUnauthorized: true
65+
}, function() {
66+
assert(false);
67+
});
68+
socket.on('error', function(err) {
69+
common.debug(err);
70+
authorized();
71+
});
72+
socket.write('ng');
73+
}
74+
75+
function authorized() {
76+
var socket = tls.connect(common.PORT, {
77+
rejectUnauthorized: true,
78+
ca: [ fs.readFileSync(path.join(common.fixturesDir, 'test_cert.pem')) ]
79+
}, function() {
80+
assert(socket.authorized);
81+
socket.end();
82+
server.close();
83+
});
84+
socket.on('error', function(err) {
85+
assert(false);
86+
});
87+
socket.write('ok');
88+
}
89+
90+
process.on('exit', function() {
91+
assert.equal(connectCount, 3);
92+
});

0 commit comments

Comments
 (0)