2011-03-10 08:54:52 +00:00
|
|
|
// 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.
|
|
|
|
|
2010-12-01 21:00:04 +00:00
|
|
|
var net = require('net');
|
2012-07-11 19:54:20 +00:00
|
|
|
var url = require('url');
|
2013-06-13 13:36:00 +00:00
|
|
|
var util = require('util');
|
2013-05-22 18:32:54 +00:00
|
|
|
|
2012-02-15 18:26:43 +00:00
|
|
|
// Allow {CLIENT_RENEG_LIMIT} client-initiated session renegotiations
|
|
|
|
// every {CLIENT_RENEG_WINDOW} seconds. An error event is emitted if more
|
|
|
|
// renegotations are seen. The settings are applied to all remote client
|
|
|
|
// connections.
|
2012-02-18 23:01:35 +00:00
|
|
|
exports.CLIENT_RENEG_LIMIT = 3;
|
2012-02-15 18:26:43 +00:00
|
|
|
exports.CLIENT_RENEG_WINDOW = 600;
|
|
|
|
|
2013-01-22 13:27:05 +00:00
|
|
|
exports.SLAB_BUFFER_SIZE = 10 * 1024 * 1024;
|
|
|
|
|
2014-03-06 23:27:01 +00:00
|
|
|
exports.DEFAULT_CIPHERS =
|
2014-08-28 07:51:42 +00:00
|
|
|
// TLS 1.2
|
|
|
|
'ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:AES128-GCM-SHA256:' +
|
|
|
|
// TLS 1.0
|
|
|
|
'RC4:HIGH:!MD5:!aNULL';
|
2014-03-06 23:27:01 +00:00
|
|
|
|
|
|
|
exports.DEFAULT_ECDH_CURVE = 'prime256v1';
|
|
|
|
|
2013-03-18 23:16:55 +00:00
|
|
|
exports.getCiphers = function() {
|
|
|
|
var names = process.binding('crypto').getSSLCiphers();
|
|
|
|
// Drop all-caps names in favor of their lowercase aliases,
|
|
|
|
var ctx = {};
|
|
|
|
names.forEach(function(name) {
|
|
|
|
if (/^[0-9A-Z\-]+$/.test(name)) name = name.toLowerCase();
|
|
|
|
ctx[name] = true;
|
|
|
|
});
|
|
|
|
return Object.getOwnPropertyNames(ctx).sort();
|
|
|
|
};
|
|
|
|
|
2011-04-14 03:53:39 +00:00
|
|
|
// Convert protocols array into valid OpenSSL protocols list
|
|
|
|
// ("\x06spdy/2\x08http/1.1\x08http/1.0")
|
2013-06-13 13:36:00 +00:00
|
|
|
exports.convertNPNProtocols = function convertNPNProtocols(NPNProtocols, out) {
|
2011-04-14 03:53:39 +00:00
|
|
|
// If NPNProtocols is Array - translate it into buffer
|
2013-07-26 21:38:08 +00:00
|
|
|
if (util.isArray(NPNProtocols)) {
|
2011-04-14 03:53:39 +00:00
|
|
|
var buff = new Buffer(NPNProtocols.reduce(function(p, c) {
|
|
|
|
return p + 1 + Buffer.byteLength(c);
|
|
|
|
}, 0));
|
|
|
|
|
|
|
|
NPNProtocols.reduce(function(offset, c) {
|
|
|
|
var clen = Buffer.byteLength(c);
|
|
|
|
buff[offset] = clen;
|
|
|
|
buff.write(c, offset + 1);
|
|
|
|
|
|
|
|
return offset + 1 + clen;
|
|
|
|
}, 0);
|
|
|
|
|
|
|
|
NPNProtocols = buff;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If it's already a Buffer - store it
|
2013-07-26 21:38:08 +00:00
|
|
|
if (util.isBuffer(NPNProtocols)) {
|
2011-04-14 03:53:39 +00:00
|
|
|
out.NPNProtocols = NPNProtocols;
|
|
|
|
}
|
2013-06-13 13:36:00 +00:00
|
|
|
};
|
2012-07-05 19:50:21 +00:00
|
|
|
|
2013-06-13 13:36:00 +00:00
|
|
|
exports.checkServerIdentity = function checkServerIdentity(host, cert) {
|
2012-07-11 19:54:20 +00:00
|
|
|
// Create regexp to much hostnames
|
|
|
|
function regexpify(host, wildcards) {
|
|
|
|
// Add trailing dot (make hostnames uniform)
|
|
|
|
if (!/\.$/.test(host)) host += '.';
|
|
|
|
|
|
|
|
// The same applies to hostname with more than one wildcard,
|
|
|
|
// if hostname has wildcard when wildcards are not allowed,
|
|
|
|
// or if there are less than two dots after wildcard (i.e. *.com or *d.com)
|
2013-01-14 23:29:46 +00:00
|
|
|
//
|
|
|
|
// also
|
|
|
|
//
|
|
|
|
// "The client SHOULD NOT attempt to match a presented identifier in
|
|
|
|
// which the wildcard character comprises a label other than the
|
|
|
|
// left-most label (e.g., do not match bar.*.example.net)."
|
|
|
|
// RFC6125
|
|
|
|
if (!wildcards && /\*/.test(host) || /[\.\*].*\*/.test(host) ||
|
2012-07-11 19:54:20 +00:00
|
|
|
/\*/.test(host) && !/\*.*\..+\..+/.test(host)) {
|
|
|
|
return /$./;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Replace wildcard chars with regexp's wildcard and
|
|
|
|
// escape all characters that have special meaning in regexps
|
|
|
|
// (i.e. '.', '[', '{', '*', and others)
|
|
|
|
var re = host.replace(
|
2012-07-20 18:07:16 +00:00
|
|
|
/\*([a-z0-9\\-_\.])|[\.,\-\\\^\$+?*\[\]\(\):!\|{}]/g,
|
|
|
|
function(all, sub) {
|
|
|
|
if (sub) return '[a-z0-9\\-_]*' + (sub === '-' ? '\\-' : sub);
|
|
|
|
return '\\' + all;
|
|
|
|
});
|
2012-07-11 19:54:20 +00:00
|
|
|
|
|
|
|
return new RegExp('^' + re + '$', 'i');
|
|
|
|
}
|
|
|
|
|
|
|
|
var dnsNames = [],
|
|
|
|
uriNames = [],
|
|
|
|
ips = [],
|
2013-01-14 23:29:46 +00:00
|
|
|
matchCN = true,
|
2014-04-14 16:08:38 +00:00
|
|
|
valid = false,
|
|
|
|
reason = 'Unknown reason';
|
2012-07-11 19:54:20 +00:00
|
|
|
|
|
|
|
// There're several names to perform check against:
|
|
|
|
// CN and altnames in certificate extension
|
|
|
|
// (DNS names, IP addresses, and URIs)
|
|
|
|
//
|
|
|
|
// Walk through altnames and generate lists of those names
|
|
|
|
if (cert.subjectaltname) {
|
|
|
|
cert.subjectaltname.split(/, /g).forEach(function(altname) {
|
2014-06-22 15:34:47 +00:00
|
|
|
var option = altname.match(/^(DNS|IP Address|URI):(.*)$/);
|
|
|
|
if (!option)
|
|
|
|
return;
|
|
|
|
if (option[1] === 'DNS') {
|
|
|
|
dnsNames.push(option[2]);
|
|
|
|
} else if (option[1] === 'IP Address') {
|
|
|
|
ips.push(option[2]);
|
|
|
|
} else if (option[1] === 'URI') {
|
|
|
|
var uri = url.parse(option[2]);
|
2012-07-11 19:54:20 +00:00
|
|
|
if (uri) uriNames.push(uri.hostname);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// If hostname is an IP address, it should be present in the list of IP
|
|
|
|
// addresses.
|
|
|
|
if (net.isIP(host)) {
|
|
|
|
valid = ips.some(function(ip) {
|
|
|
|
return ip === host;
|
|
|
|
});
|
2014-04-14 16:08:38 +00:00
|
|
|
if (!valid) {
|
|
|
|
reason = util.format('IP: %s is not in the cert\'s list: %s',
|
|
|
|
host,
|
|
|
|
ips.join(', '));
|
|
|
|
}
|
2012-07-11 19:54:20 +00:00
|
|
|
} else {
|
|
|
|
// Transform hostname to canonical form
|
|
|
|
if (!/\.$/.test(host)) host += '.';
|
|
|
|
|
|
|
|
// Otherwise check all DNS/URI records from certificate
|
|
|
|
// (with allowed wildcards)
|
|
|
|
dnsNames = dnsNames.map(function(name) {
|
|
|
|
return regexpify(name, true);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Wildcards ain't allowed in URI names
|
|
|
|
uriNames = uriNames.map(function(name) {
|
|
|
|
return regexpify(name, false);
|
|
|
|
});
|
|
|
|
|
|
|
|
dnsNames = dnsNames.concat(uriNames);
|
|
|
|
|
2013-01-14 23:29:46 +00:00
|
|
|
if (dnsNames.length > 0) matchCN = false;
|
|
|
|
|
2013-04-04 19:10:53 +00:00
|
|
|
// Match against Common Name (CN) only if no supported identifiers are
|
|
|
|
// present.
|
2013-01-14 23:29:46 +00:00
|
|
|
//
|
|
|
|
// "As noted, a client MUST NOT seek a match for a reference identifier
|
|
|
|
// of CN-ID if the presented identifiers include a DNS-ID, SRV-ID,
|
|
|
|
// URI-ID, or any application-specific identifier types supported by the
|
|
|
|
// client."
|
|
|
|
// RFC6125
|
|
|
|
if (matchCN) {
|
|
|
|
var commonNames = cert.subject.CN;
|
2013-07-26 21:38:08 +00:00
|
|
|
if (util.isArray(commonNames)) {
|
2013-01-14 23:29:46 +00:00
|
|
|
for (var i = 0, k = commonNames.length; i < k; ++i) {
|
|
|
|
dnsNames.push(regexpify(commonNames[i], true));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
dnsNames.push(regexpify(commonNames, true));
|
2012-08-12 19:46:56 +00:00
|
|
|
}
|
|
|
|
}
|
2012-07-11 19:54:20 +00:00
|
|
|
|
|
|
|
valid = dnsNames.some(function(re) {
|
|
|
|
return re.test(host);
|
|
|
|
});
|
2014-04-14 16:08:38 +00:00
|
|
|
|
|
|
|
if (!valid) {
|
|
|
|
if (cert.subjectaltname) {
|
|
|
|
reason = util.format('Host: %s is not in the cert\'s altnames: %s',
|
|
|
|
host,
|
|
|
|
cert.subjectaltname);
|
|
|
|
} else {
|
|
|
|
reason = util.format('Host: %s is not cert\'s CN: %s',
|
|
|
|
host,
|
|
|
|
cert.subject.CN);
|
|
|
|
}
|
|
|
|
}
|
2012-07-11 19:54:20 +00:00
|
|
|
}
|
|
|
|
|
2014-04-14 16:08:38 +00:00
|
|
|
if (!valid) {
|
|
|
|
var err = new Error(
|
2014-07-03 04:02:36 +00:00
|
|
|
util.format('Hostname/IP doesn\'t match certificate\'s altnames: %j',
|
|
|
|
reason));
|
2014-04-14 16:08:38 +00:00
|
|
|
err.reason = reason;
|
|
|
|
err.host = host;
|
|
|
|
err.cert = cert;
|
|
|
|
return err;
|
|
|
|
}
|
2010-12-09 08:10:16 +00:00
|
|
|
};
|
|
|
|
|
2011-08-26 11:00:40 +00:00
|
|
|
// Example:
|
|
|
|
// C=US\nST=CA\nL=SF\nO=Joyent\nOU=Node.js\nCN=ca1\nemailAddress=ry@clouds.org
|
2013-06-13 13:36:00 +00:00
|
|
|
exports.parseCertString = function parseCertString(s) {
|
2010-12-18 19:18:33 +00:00
|
|
|
var out = {};
|
2011-08-26 11:00:40 +00:00
|
|
|
var parts = s.split('\n');
|
|
|
|
for (var i = 0, len = parts.length; i < len; i++) {
|
2010-12-18 19:18:33 +00:00
|
|
|
var sepIndex = parts[i].indexOf('=');
|
|
|
|
if (sepIndex > 0) {
|
|
|
|
var key = parts[i].slice(0, sepIndex);
|
|
|
|
var value = parts[i].slice(sepIndex + 1);
|
2012-03-03 09:46:45 +00:00
|
|
|
if (key in out) {
|
2013-07-26 21:38:08 +00:00
|
|
|
if (!util.isArray(out[key])) {
|
2012-03-03 09:46:45 +00:00
|
|
|
out[key] = [out[key]];
|
|
|
|
}
|
|
|
|
out[key].push(value);
|
|
|
|
} else {
|
|
|
|
out[key] = value;
|
|
|
|
}
|
2010-12-18 19:18:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return out;
|
2010-12-09 08:35:16 +00:00
|
|
|
};
|
2011-01-07 18:57:39 +00:00
|
|
|
|
2013-06-13 13:36:00 +00:00
|
|
|
// Public API
|
2014-03-06 23:27:01 +00:00
|
|
|
exports.createSecureContext = require('_tls_common').createSecureContext;
|
|
|
|
exports.SecureContext = require('_tls_common').SecureContext;
|
2013-07-03 07:46:01 +00:00
|
|
|
exports.TLSSocket = require('_tls_wrap').TLSSocket;
|
2013-06-13 13:36:00 +00:00
|
|
|
exports.Server = require('_tls_wrap').Server;
|
|
|
|
exports.createServer = require('_tls_wrap').createServer;
|
|
|
|
exports.connect = require('_tls_wrap').connect;
|
2011-01-07 18:57:39 +00:00
|
|
|
|
2013-06-13 13:36:00 +00:00
|
|
|
// Legacy API
|
|
|
|
exports.__defineGetter__('createSecurePair', util.deprecate(function() {
|
|
|
|
return require('_tls_legacy').createSecurePair;
|
|
|
|
}, 'createSecurePair() is deprecated, use TLSSocket instead'));
|