1 // Copyright Joyent, Inc. and other Node contributors.
3 // // Emit `beforeExit` if the loop became alive either after emitting
4 // event, or after running some callbacks.
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the
8 // "Software"), to deal in the Software without restriction, including
9 // without limitation the rights to use, copy, modify, merge, publish,
10 // distribute, sublicense, and/or sell copies of the Software, and to permit
11 // persons to whom the Software is furnished to do so, subject to the
12 // following conditions:
14 // The above copyright notice and this permission notice shall be included
15 // in all copies or substantial portions of the Software.
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
20 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
21 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
22 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
23 // USE OR OTHER DEALINGS IN THE SOFTWARE.
27 var assert = require('assert');
28 var crypto = require('crypto');
29 var net = require('net');
30 var tls = require('tls');
31 var util = require('util');
32 var listenerCount = require('events').listenerCount;
33 var common = require('_tls_common');
35 var Timer = process.binding('timer_wrap').Timer;
36 var tls_wrap = process.binding('tls_wrap');
41 var debug = util.debuglog('tls');
43 function onhandshakestart() {
44 debug('onhandshakestart');
48 var now = Timer.now();
50 assert(now >= ssl.lastHandshakeTime);
52 if ((now - ssl.lastHandshakeTime) >= tls.CLIENT_RENEG_WINDOW * 1000) {
56 var first = (ssl.lastHandshakeTime === 0);
57 ssl.lastHandshakeTime = now;
60 if (++ssl.handshakes > tls.CLIENT_RENEG_LIMIT) {
61 // Defer the error event to the next tick. We're being called from OpenSSL's
62 // state machine and OpenSSL is not re-entrant. We cannot allow the user's
63 // callback to destroy the connection right now, it would crash and burn.
64 setImmediate(function() {
65 var err = new Error('TLS session renegotiation attack detected.');
72 function onhandshakedone() {
74 debug('onhandshakedone');
79 function loadSession(self, hello, cb) {
81 function onSession(err, session) {
83 return cb(new Error('TLS session callback was called 2 times'));
89 // NOTE: That we have disabled OpenSSL's internal session storage in
90 // `node_crypto.cc` and hence its safe to rely on getting servername only
91 // from clienthello or this place.
92 var ret = self.ssl.loadSession(session);
97 if (hello.sessionId.length <= 0 ||
100 !self.server.emit('resumeSession', hello.sessionId, onSession)) {
106 function loadSNI(self, servername, cb) {
107 if (!servername || !self._SNICallback)
111 self._SNICallback(servername, function(err, context) {
113 return cb(new Error('TLS SNI callback was called 2 times'));
119 // TODO(indutny): eventually disallow raw `SecureContext`
121 self.ssl.sni_context = context.context || context;
123 cb(null, self.ssl.sni_context);
128 function requestOCSP(self, hello, ctx, cb) {
129 if (!hello.OCSPRequest || !self.server)
133 ctx = self.server._sharedCreds;
137 if (listenerCount(self.server, 'OCSPRequest') === 0) {
140 self.server.emit('OCSPRequest',
141 ctx.getCertificate(),
147 function onOCSP(err, response) {
149 return cb(new Error('TLS OCSP callback was called 2 times'));
156 self.ssl.setOCSPResponse(response);
162 function onclienthello(hello) {
165 loadSession(self, hello, function(err, session) {
167 return self.destroy(err);
169 // Servername came from SSL session
170 // NOTE: TLS Session ticket doesn't include servername information
172 // Another note, From RFC3546:
174 // If, on the other hand, the older
175 // session is resumed, then the server MUST ignore extensions appearing
176 // in the client hello, and send a server hello containing no
177 // extensions; in this case the extension functionality negotiated
178 // during the original session initiation is applied to the resumed
181 // Therefore we should account session loading when dealing with servername
182 var servername = session && session.servername || hello.servername;
183 loadSNI(self, servername, function(err, ctx) {
185 return self.destroy(err);
186 requestOCSP(self, hello, ctx, function(err) {
188 return self.destroy(err);
190 self.ssl.endParser();
197 function onnewsession(key, session) {
204 this._newSessionPending = true;
205 if (!this.server.emit('newSession', key, session, done))
213 self.ssl.newSessionDone();
215 self._newSessionPending = false;
216 if (self._securePending)
218 self._securePending = false;
223 function onocspresponse(resp) {
224 this.emit('OCSPResponse', resp);
229 * Provides a wrap of socket stream to do encrypted communication.
232 function TLSSocket(socket, options) {
233 // Disallow wrapping TLSSocket in TLSSocket
234 assert(!(socket instanceof TLSSocket));
236 net.Socket.call(this, {
237 handle: socket && socket._handle,
238 allowHalfOpen: socket && socket.allowHalfOpen,
243 // To prevent assertion in afterConnect()
245 this._connecting = socket._connecting;
247 this._tlsOptions = options;
248 this._secureEstablished = false;
249 this._securePending = false;
250 this._newSessionPending = false;
251 this._controlReleased = false;
252 this._SNICallback = null;
254 this.servername = null;
255 this.npnProtocol = null;
256 this.authorized = false;
257 this.authorizationError = null;
259 // Just a documented property to make secure sockets
260 // distinguishable from regular ones.
261 this.encrypted = true;
263 this.on('error', this._tlsError);
266 this.once('connect', function() {
273 // Make sure to setup all required properties like: `_connecting` before
274 // starting the flow of the data
275 this.readable = true;
276 this.writable = true;
279 util.inherits(TLSSocket, net.Socket);
280 exports.TLSSocket = TLSSocket;
282 TLSSocket.prototype._init = function(socket) {
283 assert(this._handle);
285 // lib/net.js expect this value to be non-zero if write hasn't been flushed
287 // TODO(indutny): rewise this solution, it might be 1 before handshake and
288 // represent real writeQueueSize during regular writes.
289 this._handle.writeQueueSize = 1;
292 var options = this._tlsOptions;
294 // Wrap socket's handle
295 var context = options.secureContext ||
296 options.credentials ||
297 tls.createSecureContext();
298 this.ssl = tls_wrap.wrap(this._handle, context.context, options.isServer);
299 this.server = options.server || null;
301 // For clients, we will always have either a given ca list or be using
303 var requestCert = !!options.requestCert || !options.isServer,
304 rejectUnauthorized = !!options.rejectUnauthorized;
306 this._requestCert = requestCert;
307 this._rejectUnauthorized = rejectUnauthorized;
308 if (requestCert || rejectUnauthorized)
309 this.ssl.setVerifyMode(requestCert, rejectUnauthorized);
311 if (options.isServer) {
312 this.ssl.onhandshakestart = onhandshakestart.bind(this);
313 this.ssl.onhandshakedone = onhandshakedone.bind(this);
314 this.ssl.onclienthello = onclienthello.bind(this);
315 this.ssl.onnewsession = onnewsession.bind(this);
316 this.ssl.lastHandshakeTime = 0;
317 this.ssl.handshakes = 0;
320 (listenerCount(this.server, 'resumeSession') > 0 ||
321 listenerCount(this.server, 'newSession') > 0 ||
322 listenerCount(this.server, 'OCSPRequest') > 0)) {
323 this.ssl.enableSessionCallbacks();
326 this.ssl.onhandshakestart = function() {};
327 this.ssl.onhandshakedone = this._finishInit.bind(this);
328 this.ssl.onocspresponse = onocspresponse.bind(this);
331 this.ssl.setSession(options.session);
334 this.ssl.onerror = function(err) {
335 if (self._writableState.errorEmitted)
337 self._writableState.errorEmitted = true;
339 // Destroy socket if error happened before handshake's finish
340 if (!this._secureEstablished) {
343 } else if (options.isServer &&
344 rejectUnauthorized &&
345 /peer did not return a certificate/.test(err.message)) {
346 // Ignore server's authorization errors
354 // If custom SNICallback was given, or if
355 // there're SNI contexts to perform match against -
356 // set `.onsniselect` callback.
357 if (process.features.tls_sni &&
360 (options.SNICallback !== SNICallback ||
361 options.server._contexts.length)) {
362 assert(typeof options.SNICallback === 'function');
363 this._SNICallback = options.SNICallback;
364 this.ssl.enableHelloParser();
367 if (process.features.tls_npn && options.NPNProtocols)
368 this.ssl.setNPNProtocols(options.NPNProtocols);
370 if (options.handshakeTimeout > 0)
371 this.setTimeout(options.handshakeTimeout, this._handleTimeout);
373 // Socket already has some buffered data - emulate receiving it
374 if (socket && socket._readableState.length) {
376 while ((buf = socket.read()) !== null)
377 this.ssl.receive(buf);
381 TLSSocket.prototype.renegotiate = function(options, callback) {
382 var requestCert = this._requestCert,
383 rejectUnauthorized = this._rejectUnauthorized;
385 if (typeof options.requestCert !== 'undefined')
386 requestCert = !!options.requestCert;
387 if (typeof options.rejectUnauthorized !== 'undefined')
388 rejectUnauthorized = !!options.rejectUnauthorized;
390 if (requestCert !== this._requestCert ||
391 rejectUnauthorized !== this._rejectUnauthorized) {
392 this.ssl.setVerifyMode(requestCert, rejectUnauthorized);
393 this._requestCert = requestCert;
394 this._rejectUnauthorized = rejectUnauthorized;
396 if (!this.ssl.renegotiate()) {
398 process.nextTick(function() {
399 callback(new Error('Failed to renegotiate'));
405 // Ensure that we'll cycle through internal openssl's state
409 this.once('secure', function() {
417 TLSSocket.prototype.setMaxSendFragment = function setMaxSendFragment(size) {
418 return this.ssl.setMaxSendFragment(size) == 1;
421 TLSSocket.prototype.getTLSTicket = function getTLSTicket() {
422 return this.ssl.getTLSTicket();
425 TLSSocket.prototype._handleTimeout = function() {
426 this._tlsError(new Error('TLS handshake timeout'));
429 TLSSocket.prototype._tlsError = function(err) {
430 this.emit('_tlsError', err);
431 if (this._controlReleased)
432 this.emit('error', err);
435 TLSSocket.prototype._releaseControl = function() {
436 if (this._controlReleased)
438 this._controlReleased = true;
439 this.removeListener('error', this._tlsError);
443 TLSSocket.prototype._finishInit = function() {
444 // `newSession` callback wasn't called yet
445 if (this._newSessionPending) {
446 this._securePending = true;
450 if (process.features.tls_npn) {
451 this.npnProtocol = this.ssl.getNegotiatedProtocol();
454 if (process.features.tls_sni && this._tlsOptions.isServer) {
455 this.servername = this.ssl.getServername();
458 debug('secure established');
459 this._secureEstablished = true;
460 if (this._tlsOptions.handshakeTimeout > 0)
461 this.setTimeout(0, this._handleTimeout);
465 TLSSocket.prototype._start = function() {
466 if (this._tlsOptions.requestOCSP)
467 this.ssl.requestOCSP();
471 TLSSocket.prototype.setServername = function(name) {
472 this.ssl.setServername(name);
475 TLSSocket.prototype.setSession = function(session) {
476 if (util.isString(session))
477 session = new Buffer(session, 'binary');
478 this.ssl.setSession(session);
481 TLSSocket.prototype.getPeerCertificate = function(detailed) {
483 return common.translatePeerCertificate(
484 this.ssl.getPeerCertificate(detailed));
490 TLSSocket.prototype.getSession = function() {
492 return this.ssl.getSession();
498 TLSSocket.prototype.isSessionReused = function() {
500 return this.ssl.isSessionReused();
506 TLSSocket.prototype.getCipher = function(err) {
508 return this.ssl.getCurrentCipher();
514 // TODO: support anonymous (nocert) and PSK
517 // AUTHENTICATION MODES
519 // There are several levels of authentication that TLS/SSL supports.
520 // Read more about this in "man SSL_set_verify".
522 // 1. The server sends a certificate to the client but does not request a
523 // cert from the client. This is common for most HTTPS servers. The browser
524 // can verify the identity of the server, but the server does not know who
525 // the client is. Authenticating the client is usually done over HTTP using
526 // login boxes and cookies and stuff.
528 // 2. The server sends a cert to the client and requests that the client
529 // also send it a cert. The client knows who the server is and the server is
530 // requesting the client also identify themselves. There are several
533 // A) verifyError returns null meaning the client's certificate is signed
534 // by one of the server's CAs. The server know's the client idenity now
535 // and the client is authorized.
537 // B) For some reason the client's certificate is not acceptable -
538 // verifyError returns a string indicating the problem. The server can
539 // either (i) reject the client or (ii) allow the client to connect as an
540 // unauthorized connection.
542 // The mode is controlled by two boolean variables.
545 // If true the server requests a certificate from client connections. For
546 // the common HTTPS case, users will want this to be false, which is what
549 // rejectUnauthorized
550 // If true clients whose certificates are invalid for any reason will not
551 // be allowed to make connections. If false, they will simply be marked as
552 // unauthorized but secure communication will continue. By default this is
558 // - requestCert. Send verify request. Default to false.
559 // - rejectUnauthorized. Boolean, default to true.
562 // - ca: string or array of strings.
563 // - sessionTimeout: integer.
565 // emit 'secureConnection'
566 // function (tlsSocket) { }
568 // "UNABLE_TO_GET_ISSUER_CERT", "UNABLE_TO_GET_CRL",
569 // "UNABLE_TO_DECRYPT_CERT_SIGNATURE", "UNABLE_TO_DECRYPT_CRL_SIGNATURE",
570 // "UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY", "CERT_SIGNATURE_FAILURE",
571 // "CRL_SIGNATURE_FAILURE", "CERT_NOT_YET_VALID" "CERT_HAS_EXPIRED",
572 // "CRL_NOT_YET_VALID", "CRL_HAS_EXPIRED" "ERROR_IN_CERT_NOT_BEFORE_FIELD",
573 // "ERROR_IN_CERT_NOT_AFTER_FIELD", "ERROR_IN_CRL_LAST_UPDATE_FIELD",
574 // "ERROR_IN_CRL_NEXT_UPDATE_FIELD", "OUT_OF_MEM",
575 // "DEPTH_ZERO_SELF_SIGNED_CERT", "SELF_SIGNED_CERT_IN_CHAIN",
576 // "UNABLE_TO_GET_ISSUER_CERT_LOCALLY", "UNABLE_TO_VERIFY_LEAF_SIGNATURE",
577 // "CERT_CHAIN_TOO_LONG", "CERT_REVOKED" "INVALID_CA",
578 // "PATH_LENGTH_EXCEEDED", "INVALID_PURPOSE" "CERT_UNTRUSTED",
581 function Server(/* [options], listener */) {
582 var options, listener;
583 if (util.isObject(arguments[0])) {
584 options = arguments[0];
585 listener = arguments[1];
586 } else if (util.isFunction(arguments[0])) {
588 listener = arguments[0];
591 if (!(this instanceof Server)) return new Server(options, listener);
597 // Handle option defaults:
598 this.setOptions(options);
600 var sharedCreds = tls.createSecureContext({
603 passphrase: self.passphrase,
606 ciphers: self.ciphers,
607 ecdhCurve: self.ecdhCurve,
608 dhparam: self.dhparam,
609 secureProtocol: self.secureProtocol,
610 secureOptions: self.secureOptions,
611 honorCipherOrder: self.honorCipherOrder,
613 sessionIdContext: self.sessionIdContext
615 this._sharedCreds = sharedCreds;
617 var timeout = options.handshakeTimeout || (120 * 1000);
619 if (!util.isNumber(timeout)) {
620 throw new TypeError('handshakeTimeout must be a number');
623 if (self.sessionTimeout) {
624 sharedCreds.context.setSessionTimeout(self.sessionTimeout);
627 if (self.ticketKeys) {
628 sharedCreds.context.setTicketKeys(self.ticketKeys);
632 net.Server.call(this, function(raw_socket) {
633 var socket = new TLSSocket(raw_socket, {
634 secureContext: sharedCreds,
637 requestCert: self.requestCert,
638 rejectUnauthorized: self.rejectUnauthorized,
639 handshakeTimeout: timeout,
640 NPNProtocols: self.NPNProtocols,
641 SNICallback: options.SNICallback || SNICallback
644 socket.on('secure', function() {
645 if (socket._requestCert) {
646 var verifyError = socket.ssl.verifyError();
648 socket.authorizationError = verifyError.code;
650 if (socket._rejectUnauthorized)
653 socket.authorized = true;
657 if (!socket.destroyed && socket._releaseControl())
658 self.emit('secureConnection', socket);
661 var errorEmitted = false;
662 socket.on('close', function() {
664 if (!socket._controlReleased && !errorEmitted) {
666 var connReset = new Error('socket hang up');
667 connReset.code = 'ECONNRESET';
668 self.emit('clientError', connReset, socket);
672 socket.on('_tlsError', function(err) {
673 if (!socket._controlReleased && !errorEmitted) {
675 self.emit('clientError', err, socket);
681 this.on('secureConnection', listener);
685 util.inherits(Server, net.Server);
686 exports.Server = Server;
687 exports.createServer = function(options, listener) {
688 return new Server(options, listener);
692 Server.prototype._getServerData = function() {
694 ticketKeys: this._sharedCreds.context.getTicketKeys().toString('hex')
699 Server.prototype._setServerData = function(data) {
700 this._sharedCreds.context.setTicketKeys(new Buffer(data.ticketKeys, 'hex'));
704 Server.prototype.setOptions = function(options) {
705 if (util.isBoolean(options.requestCert)) {
706 this.requestCert = options.requestCert;
708 this.requestCert = false;
711 if (util.isBoolean(options.rejectUnauthorized)) {
712 this.rejectUnauthorized = options.rejectUnauthorized;
714 this.rejectUnauthorized = false;
717 if (options.pfx) this.pfx = options.pfx;
718 if (options.key) this.key = options.key;
719 if (options.passphrase) this.passphrase = options.passphrase;
720 if (options.cert) this.cert = options.cert;
721 if (options.ca) this.ca = options.ca;
722 if (options.secureProtocol) this.secureProtocol = options.secureProtocol;
723 if (options.crl) this.crl = options.crl;
724 if (options.ciphers) this.ciphers = options.ciphers;
725 if (!util.isUndefined(options.ecdhCurve))
726 this.ecdhCurve = options.ecdhCurve;
727 if (options.dhparam) this.dhparam = options.dhparam;
728 if (options.sessionTimeout) this.sessionTimeout = options.sessionTimeout;
729 if (options.ticketKeys) this.ticketKeys = options.ticketKeys;
730 var secureOptions = options.secureOptions || 0;
731 if (options.honorCipherOrder)
732 this.honorCipherOrder = true;
734 this.honorCipherOrder = false;
735 if (secureOptions) this.secureOptions = secureOptions;
736 if (options.NPNProtocols) tls.convertNPNProtocols(options.NPNProtocols, this);
737 if (options.sessionIdContext) {
738 this.sessionIdContext = options.sessionIdContext;
740 this.sessionIdContext = crypto.createHash('md5')
741 .update(process.argv.join(' '))
746 // SNI Contexts High-Level API
747 Server.prototype.addContext = function(servername, context) {
749 throw new Error('Servername is required parameter for Server.addContext');
752 var re = new RegExp('^' +
753 servername.replace(/([\.^$+?\-\\[\]{}])/g, '\\$1')
754 .replace(/\*/g, '[^\.]*') +
756 this._contexts.push([re, tls.createSecureContext(context).context]);
759 function SNICallback(servername, callback) {
762 this.server._contexts.some(function(elem) {
763 if (!util.isNull(servername.match(elem[0]))) {
775 // var s = tls.connect({port: 8000, host: "google.com"}, function() {
776 // if (!s.authorized) {
783 // s.end("hello world\n");
787 function normalizeConnectArgs(listArgs) {
788 var args = net._normalizeConnectArgs(listArgs);
789 var options = args[0];
792 if (util.isObject(listArgs[1])) {
793 options = util._extend(options, listArgs[1]);
794 } else if (util.isObject(listArgs[2])) {
795 options = util._extend(options, listArgs[2]);
798 return (cb) ? [options, cb] : [options];
801 function legacyConnect(hostname, options, NPN, context) {
802 assert(options.socket);
804 tls_legacy = require('_tls_legacy');
806 var pair = tls_legacy.createSecurePair(context,
809 !!options.rejectUnauthorized,
811 NPNProtocols: NPN.NPNProtocols,
814 tls_legacy.pipe(pair, options.socket);
815 pair.cleartext._controlReleased = true;
816 pair.on('error', function(err) {
817 pair.cleartext.emit('error', err);
823 exports.connect = function(/* [port, host], options, cb */) {
824 var args = normalizeConnectArgs(arguments);
825 var options = args[0];
829 rejectUnauthorized: '0' !== process.env.NODE_TLS_REJECT_UNAUTHORIZED,
830 ciphers: tls.DEFAULT_CIPHERS,
831 checkServerIdentity: tls.checkServerIdentity
834 options = util._extend(defaults, options || {});
836 assert(typeof options.checkServerIdentity === 'function');
838 var hostname = options.servername ||
840 options.socket && options.socket._host,
842 context = tls.createSecureContext(options);
843 tls.convertNPNProtocols(options.NPNProtocols, NPN);
845 // Wrapping TLS socket inside another TLS socket was requested -
846 // create legacy secure pair
850 if (options.socket instanceof TLSSocket) {
851 debug('legacy connect');
853 socket = legacyConnect(hostname, options, NPN, context);
854 result = socket.cleartext;
857 socket = new TLSSocket(options.socket, {
858 secureContext: context,
861 rejectUnauthorized: options.rejectUnauthorized,
862 session: options.session,
863 NPNProtocols: NPN.NPNProtocols,
864 requestOCSP: options.requestOCSP
869 if (socket._handle && !socket._connecting) {
872 // Not even started connecting yet (or probably resolving dns address),
873 // catch socket errors and assign handle.
874 if (!legacy && options.socket) {
875 options.socket.once('connect', function() {
876 assert(options.socket._handle);
877 socket._handle = options.socket._handle;
878 socket._handle.owner = socket;
879 socket.emit('connect');
882 socket.once('connect', onHandle);
886 result.once('secureConnect', cb);
888 if (!options.socket) {
891 if (options.path && !options.port) {
892 connect_opt = { path: options.path };
897 localAddress: options.localAddress
900 socket.connect(connect_opt);
905 function onHandle() {
907 socket._releaseControl();
910 socket.setSession(options.session);
913 if (options.servername)
914 socket.setServername(options.servername);
918 socket.on('secure', function() {
919 var verifyError = socket.ssl.verifyError();
921 // Verify that server's identity matches it's certificate's names
923 var cert = result.getPeerCertificate();
924 verifyError = options.checkServerIdentity(hostname, cert);
928 result.authorized = false;
929 result.authorizationError = verifyError.code || verifyError.message;
931 if (options.rejectUnauthorized) {
932 result.emit('error', verifyError);
936 result.emit('secureConnect');
939 result.authorized = true;
940 result.emit('secureConnect');
943 // Uncork incoming data
944 result.removeListener('end', onHangUp);
947 function onHangUp() {
948 // NOTE: This logic is shared with _http_client.js
949 if (!socket._hadError) {
950 socket._hadError = true;
951 var error = new Error('socket hang up');
952 error.code = 'ECONNRESET';
954 socket.emit('error', error);
957 result.once('end', onHangUp);