1 // Copyright Joyent, Inc. and other Node contributors.
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:
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
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.
22 var crypto = require('crypto');
23 var util = require('util');
24 var net = require('net');
25 var events = require('events');
26 var stream = require('stream');
28 var assert = require('assert').ok;
30 var NPN_ENABLED = process.binding('constants').NPN_ENABLED;
33 if (process.env.NODE_DEBUG && /tls/.test(process.env.NODE_DEBUG)) {
34 debug = function(a) { console.error('TLS:', a); };
36 debug = function() { };
40 var Connection = null;
42 Connection = process.binding('crypto').Connection;
43 exports.NPN_ENABLED = NPN_ENABLED;
45 throw new Error('node.js not compiled with openssl crypto support.');
48 // Convert protocols array into valid OpenSSL protocols list
49 // ("\x06spdy/2\x08http/1.1\x08http/1.0")
50 function convertNPNProtocols(NPNProtocols, out) {
51 // If NPNProtocols is Array - translate it into buffer
52 if (Array.isArray(NPNProtocols)) {
53 var buff = new Buffer(NPNProtocols.reduce(function(p, c) {
54 return p + 1 + Buffer.byteLength(c);
57 NPNProtocols.reduce(function(offset, c) {
58 var clen = Buffer.byteLength(c);
60 buff.write(c, offset + 1);
62 return offset + 1 + clen;
68 // If it's already a Buffer - store it
69 if (Buffer.isBuffer(NPNProtocols)) {
70 out.NPNProtocols = NPNProtocols;
74 // Base class of both CleartextStream and EncryptedStream
75 function CryptoStream(pair) {
76 stream.Stream.call(this);
80 this.readable = this.writable = true;
82 this._writeState = true;
84 this._pendingCallbacks = [];
85 this._pendingBytes = 0;
87 util.inherits(CryptoStream, stream.Stream);
90 CryptoStream.prototype.write = function(data /* , encoding, cb */) {
91 if (this == this.pair.cleartext) {
92 debug('cleartext.write called with ' + data.length + ' bytes');
94 debug('encrypted.write called with ' + data.length + ' bytes');
98 throw new Error('CryptoStream is not writable');
104 if (typeof arguments[1] == 'string') {
105 encoding = arguments[1];
112 // Transform strings into buffers.
113 if (typeof data == 'string') {
114 data = new Buffer(data, encoding);
117 debug('clearIn data');
119 this._pending.push(data);
120 this._pendingCallbacks.push(cb);
122 this._pendingBytes += data.length;
124 this.pair._writeCalled = true;
127 return this._pendingBytes < 128 * 1024;
131 CryptoStream.prototype.pause = function() {
132 debug('paused ' + (this == this.pair.cleartext ? 'cleartext' : 'encrypted'));
133 this._writeState = false;
137 CryptoStream.prototype.resume = function() {
138 debug('resume ' + (this == this.pair.cleartext ? 'cleartext' : 'encrypted'));
139 this._writeState = true;
144 CryptoStream.prototype.setTimeout = function(n) {
145 if (this.socket) this.socket.setTimeout(n);
149 CryptoStream.prototype.setNoDelay = function() {
150 if (this.socket) this.socket.setNoDelay();
154 CryptoStream.prototype.setEncoding = function(encoding) {
155 var StringDecoder = require('string_decoder').StringDecoder; // lazy load
156 this._decoder = new StringDecoder(encoding);
160 // EG '/C=US/ST=CA/L=SF/O=Joyent/OU=Node.js/CN=ca1/emailAddress=ry@clouds.org'
161 function parseCertString(s) {
163 var parts = s.split('/');
164 // Note: can always skip the first one.
165 for (var i = 1; i < parts.length; i++) {
166 var sepIndex = parts[i].indexOf('=');
168 var key = parts[i].slice(0, sepIndex);
169 var value = parts[i].slice(sepIndex + 1);
177 CryptoStream.prototype.getPeerCertificate = function() {
178 if (this.pair._ssl) {
179 var c = this.pair._ssl.getPeerCertificate();
182 if (c.issuer) c.issuer = parseCertString(c.issuer);
183 if (c.subject) c.subject = parseCertString(c.subject);
192 CryptoStream.prototype.getCipher = function(err) {
193 if (this.pair._ssl) {
194 return this.pair._ssl.getCurrentCipher();
201 CryptoStream.prototype.end = function(d) {
203 if (this.pair._done) return;
209 this._pending.push(END_OF_FILE);
210 this._pendingCallbacks.push(null);
212 // If this is an encrypted stream then we need to disable further 'data'
215 this.writable = false;
222 CryptoStream.prototype.destroySoon = function(err) {
231 CryptoStream.prototype.destroy = function(err) {
232 if (this.pair._done) return;
233 this.pair._destroy();
237 CryptoStream.prototype._done = function() {
238 this._doneFlag = true;
240 if (this.pair.cleartext._doneFlag &&
241 this.pair.encrypted._doneFlag &&
243 // If both streams are done:
244 this.pair._destroy();
249 CryptoStream.prototype.fd = -1;
250 CryptoStream.prototype.__defineGetter__('readyState',
251 net.Socket.prototype.__lookupGetter__('readyState'));
253 // Move decrypted, clear data out into the application.
254 // From the user's perspective this occurs as a 'data' event
255 // on the pair.cleartext.
257 // Move encrypted data to the stream. From the user's perspective this
258 // occurs as a 'data' event on the pair.encrypted. Usually the application
259 // will have some code which pipes the stream to a socket:
261 // pair.encrypted.on('data', function (d) {
265 CryptoStream.prototype._push = function() {
266 if (this == this.pair.encrypted && !this.writable) {
267 // If the encrypted side got EOF, we do not attempt
268 // to write out data anymore.
272 while (this._writeState == true) {
275 var pool = new Buffer(16 * 4096); // alloc every time?
278 chunkBytes = this._pusher(pool, bytesRead, pool.length - bytesRead);
280 if (this.pair._ssl && this.pair._ssl.error) {
285 this.pair._maybeInitFinished();
287 if (chunkBytes >= 0) {
288 bytesRead += chunkBytes;
291 } while ((chunkBytes > 0) && (bytesRead < pool.length));
293 assert(bytesRead >= 0);
295 // Bail out if we didn't read any data.
296 if (bytesRead == 0) {
297 if (this._internallyPendingBytes() == 0 && this._destroyAfterPush) {
303 var chunk = pool.slice(0, bytesRead);
305 if (this === this.pair.cleartext) {
306 debug('cleartext emit "data" with ' + bytesRead + ' bytes');
308 debug('encrypted emit "data" with ' + bytesRead + ' bytes');
312 var string = this._decoder.write(chunk);
313 if (string.length) this.emit('data', string);
315 this.emit('data', chunk);
318 // Optimization: emit the original buffer with end points
319 if (this.ondata) this.ondata(pool, 0, bytesRead);
324 // Push in any clear data coming from the application.
325 // This arrives via some code like this:
327 // pair.cleartext.write("hello world");
331 // Push in incoming encrypted data from the socket.
332 // This arrives via some code like this:
334 // socket.on('data', function (d) {
335 // pair.encrypted.write(d)
338 CryptoStream.prototype._pull = function() {
339 var havePending = this._pending.length > 0;
341 assert(havePending || this._pendingBytes == 0);
343 while (this._pending.length > 0) {
344 if (!this.pair._ssl) break;
346 var tmp = this._pending.shift();
347 var cb = this._pendingCallbacks.shift();
349 assert(this._pending.length === this._pendingCallbacks.length);
351 if (tmp === END_OF_FILE) {
353 if (this === this.pair.encrypted) {
354 debug('end encrypted ' + this.pair.fd);
355 this.pair.cleartext._destroyAfterPush = true;
358 assert(this === this.pair.cleartext);
359 debug('end cleartext');
361 this.pair._ssl.shutdown();
363 // TODO check if we get EAGAIN From shutdown, would have to do it
364 // again. should unshift END_OF_FILE back onto pending and wait for
367 this.pair.encrypted._destroyAfterPush = true;
374 if (tmp.length == 0) continue;
376 var rv = this._puller(tmp);
378 if (this.pair._ssl && this.pair._ssl.error) {
383 this.pair._maybeInitFinished();
385 if (rv === 0 || rv < 0) {
386 this._pending.unshift(tmp);
387 this._pendingCallbacks.unshift(cb);
391 this._pendingBytes -= tmp.length;
392 assert(this._pendingBytes >= 0);
396 assert(rv === tmp.length);
399 // If we've cleared all of incoming encrypted data, emit drain.
400 if (havePending && this._pending.length === 0) {
403 if (this.__destroyOnDrain) this.end();
408 function CleartextStream(pair) {
409 CryptoStream.call(this, pair);
411 util.inherits(CleartextStream, CryptoStream);
414 CleartextStream.prototype._internallyPendingBytes = function() {
415 if (this.pair._ssl) {
416 return this.pair._ssl.clearPending();
423 CleartextStream.prototype._puller = function(b) {
424 debug('clearIn ' + b.length + ' bytes');
425 return this.pair._ssl.clearIn(b, 0, b.length);
429 CleartextStream.prototype._pusher = function(pool, offset, length) {
430 debug('reading from clearOut');
431 if (!this.pair._ssl) return -1;
432 return this.pair._ssl.clearOut(pool, offset, length);
436 function EncryptedStream(pair) {
437 CryptoStream.call(this, pair);
439 util.inherits(EncryptedStream, CryptoStream);
442 EncryptedStream.prototype._internallyPendingBytes = function() {
443 if (this.pair._ssl) {
444 return this.pair._ssl.encPending();
451 EncryptedStream.prototype._puller = function(b) {
452 debug('writing from encIn');
453 return this.pair._ssl.encIn(b, 0, b.length);
457 EncryptedStream.prototype._pusher = function(pool, offset, length) {
458 debug('reading from encOut');
459 if (!this.pair._ssl) return -1;
460 return this.pair._ssl.encOut(pool, offset, length);
465 * Provides a pair of streams to do encrypted communication.
468 function SecurePair(credentials, isServer, requestCert, rejectUnauthorized,
470 if (!(this instanceof SecurePair)) {
471 return new SecurePair(credentials,
480 events.EventEmitter.call(this);
482 this._secureEstablished = false;
483 this._isServer = isServer ? true : false;
484 this._encWriteState = true;
485 this._clearWriteState = true;
488 var crypto = require('crypto');
491 this.credentials = crypto.createCredentials();
493 this.credentials = credentials;
496 if (!this._isServer) {
497 // For clients, we will always have either a given ca list or be using
502 this._secureEstablished = false;
503 this._rejectUnauthorized = rejectUnauthorized ? true : false;
504 this._requestCert = requestCert ? true : false;
506 this._ssl = new Connection(this.credentials.context,
507 this._isServer ? true : false,
509 this._rejectUnauthorized);
511 if (NPN_ENABLED && NPNProtocols) {
512 this._ssl.setNPNProtocols(NPNProtocols);
513 this.npnProtocol = null;
516 /* Acts as a r/w stream to the cleartext side of the stream. */
517 this.cleartext = new CleartextStream(this);
519 /* Acts as a r/w stream to the encrypted side of the stream. */
520 this.encrypted = new EncryptedStream(this);
522 process.nextTick(function() {
528 util.inherits(SecurePair, events.EventEmitter);
531 exports.createSecurePair = function(credentials,
534 rejectUnauthorized) {
535 var pair = new SecurePair(credentials,
545 /* Attempt to cycle OpenSSLs buffers in various directions.
547 * An SSL Connection can be viewed as four separate piplines,
548 * interacting with one has no connection to the behavoir of
549 * any of the other 3 -- This might not sound reasonable,
550 * but consider things like mid-stream renegotiation of
553 * The four pipelines, using terminology of the client (server is just
555 * (1) Encrypted Output stream (Writing encrypted data to peer)
556 * (2) Encrypted Input stream (Reading encrypted data from peer)
557 * (3) Cleartext Output stream (Decrypted content from the peer)
558 * (4) Cleartext Input stream (Cleartext content to send to the peer)
560 * This function attempts to pull any available data out of the Cleartext
561 * input stream (4), and the Encrypted input stream (2). Then it pushes any
562 * data available from the cleartext output stream (3), and finally from the
563 * Encrypted output stream (1)
565 * It is called whenever we do something with OpenSSL -- post reciving
566 * content, trying to flush, trying to change ciphers, or shutting down the
569 * Because it is also called everywhere, we also check if the connection has
570 * completed negotiation and emit 'secure' from here if it has.
572 SecurePair.prototype._cycle = function(depth) {
573 depth = depth ? depth : 0;
578 if(depth == 0) this._writeCalled = false;
580 var established = this._secureEstablished;
582 if (!this._cycleEncryptedPullLock) {
583 this._cycleEncryptedPullLock = true;
584 debug("encrypted._pull");
585 this.encrypted._pull();
586 this._cycleEncryptedPullLock = false;
589 if (!this._cycleCleartextPullLock) {
590 this._cycleCleartextPullLock = true;
591 debug("cleartext._pull");
592 this.cleartext._pull();
593 this._cycleCleartextPullLock = false;
596 if (!this._cycleCleartextPushLock) {
597 this._cycleCleartextPushLock = true;
598 debug("cleartext._push");
599 this.cleartext._push();
600 this._cycleCleartextPushLock = false;
603 if (!this._cycleEncryptedPushLock) {
604 this._cycleEncryptedPushLock = true;
605 debug("encrypted._push");
606 this.encrypted._push();
607 this._cycleEncryptedPushLock = false;
614 if ((!established && this._secureEstablished) ||
615 (depth == 0 && this._writeCalled)) {
616 // If we were not established but now we are, let's cycle again.
617 // Or if there is some data to write...
618 this._cycle(depth + 1);
623 SecurePair.prototype._maybeInitFinished = function() {
624 if (this._ssl && !this._secureEstablished && this._ssl.isInitFinished()) {
626 this.npnProtocol = this._ssl.getNegotiatedProtocol();
629 this._secureEstablished = true;
630 debug('secure established');
636 SecurePair.prototype._destroy = function() {
641 this._ssl.error = null;
645 self.encrypted.writable = self.encrypted.readable = false;
646 self.cleartext.writable = self.cleartext.readable = false;
648 process.nextTick(function() {
649 self.encrypted.emit('end');
650 if (self.encrypted.onend) self.encrypted.onend();
651 self.encrypted.emit('close');
653 self.cleartext.emit('end');
654 if (self.cleartext.onend) self.cleartext.onend();
655 self.cleartext.emit('close');
663 SecurePair.prototype._error = function() {
664 if (!this._secureEstablished) {
667 var err = this._ssl.error;
668 this._ssl.error = null;
670 if (this._isServer &&
671 this._rejectUnauthorized &&
672 /peer did not return a certificate/.test(err.message)) {
673 // Not really an error.
676 this.cleartext.emit('error', err);
681 // TODO: support anonymous (nocert) and PSK
684 // AUTHENTICATION MODES
686 // There are several levels of authentication that TLS/SSL supports.
687 // Read more about this in "man SSL_set_verify".
689 // 1. The server sends a certificate to the client but does not request a
690 // cert from the client. This is common for most HTTPS servers. The browser
691 // can verify the identity of the server, but the server does not know who
692 // the client is. Authenticating the client is usually done over HTTP using
693 // login boxes and cookies and stuff.
695 // 2. The server sends a cert to the client and requests that the client
696 // also send it a cert. The client knows who the server is and the server is
697 // requesting the client also identify themselves. There are several
700 // A) verifyError returns null meaning the client's certificate is signed
701 // by one of the server's CAs. The server know's the client idenity now
702 // and the client is authorized.
704 // B) For some reason the client's certificate is not acceptable -
705 // verifyError returns a string indicating the problem. The server can
706 // either (i) reject the client or (ii) allow the client to connect as an
707 // unauthorized connection.
709 // The mode is controlled by two boolean variables.
712 // If true the server requests a certificate from client connections. For
713 // the common HTTPS case, users will want this to be false, which is what
716 // rejectUnauthorized
717 // If true clients whose certificates are invalid for any reason will not
718 // be allowed to make connections. If false, they will simply be marked as
719 // unauthorized but secure communication will continue. By default this is
725 // - requestCert. Send verify request. Default to false.
726 // - rejectUnauthorized. Boolean, default to false.
729 // - ca: string or array of strings.
731 // emit 'secureConnection'
732 // function (cleartextStream, encryptedStream) { }
734 // 'cleartextStream' has the boolean property 'authorized' to determine if
735 // it was verified by the CA. If 'authorized' is false, a property
736 // 'authorizationError' is set on cleartextStream and has the possible
739 // "UNABLE_TO_GET_ISSUER_CERT", "UNABLE_TO_GET_CRL",
740 // "UNABLE_TO_DECRYPT_CERT_SIGNATURE", "UNABLE_TO_DECRYPT_CRL_SIGNATURE",
741 // "UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY", "CERT_SIGNATURE_FAILURE",
742 // "CRL_SIGNATURE_FAILURE", "CERT_NOT_YET_VALID" "CERT_HAS_EXPIRED",
743 // "CRL_NOT_YET_VALID", "CRL_HAS_EXPIRED" "ERROR_IN_CERT_NOT_BEFORE_FIELD",
744 // "ERROR_IN_CERT_NOT_AFTER_FIELD", "ERROR_IN_CRL_LAST_UPDATE_FIELD",
745 // "ERROR_IN_CRL_NEXT_UPDATE_FIELD", "OUT_OF_MEM",
746 // "DEPTH_ZERO_SELF_SIGNED_CERT", "SELF_SIGNED_CERT_IN_CHAIN",
747 // "UNABLE_TO_GET_ISSUER_CERT_LOCALLY", "UNABLE_TO_VERIFY_LEAF_SIGNATURE",
748 // "CERT_CHAIN_TOO_LONG", "CERT_REVOKED" "INVALID_CA",
749 // "PATH_LENGTH_EXCEEDED", "INVALID_PURPOSE" "CERT_UNTRUSTED",
754 // cleartext.credentials (by mirroring from pair object)
755 // cleartext.getCertificate() (by mirroring from pair.credentials.context)
756 function Server(/* [options], listener */) {
757 var options, listener;
758 if (typeof arguments[0] == 'object') {
759 options = arguments[0];
760 listener = arguments[1];
761 } else if (typeof arguments[0] == 'function') {
763 listener = arguments[0];
766 if (!(this instanceof Server)) return new Server(options, listener);
771 net.Server.call(this, function(socket) {
772 var creds = crypto.createCredentials({
776 ciphers: self.ciphers,
777 secureProtocol: self.secureProtocol,
778 secureOptions: self.secureOptions,
781 creds.context.setCiphers('RC4-SHA:AES128-SHA:AES256-SHA');
783 var pair = new SecurePair(creds,
786 self.rejectUnauthorized,
789 var cleartext = pipe(pair, socket);
790 cleartext._controlReleased = false;
792 pair.on('secure', function() {
793 pair.cleartext.authorized = false;
794 pair.cleartext.npnProtocol = pair.npnProtocol;
795 if (!self.requestCert) {
796 cleartext._controlReleased = true;
797 self.emit('secureConnection', pair.cleartext, pair.encrypted);
799 var verifyError = pair._ssl.verifyError();
801 pair.cleartext.authorizationError = verifyError;
803 if (self.rejectUnauthorized) {
807 cleartext._controlReleased = true;
808 self.emit('secureConnection', pair.cleartext, pair.encrypted);
811 pair.cleartext.authorized = true;
812 cleartext._controlReleased = true;
813 self.emit('secureConnection', pair.cleartext, pair.encrypted);
820 this.on('secureConnection', listener);
823 // Handle option defaults:
824 this.setOptions(options);
827 util.inherits(Server, net.Server);
828 exports.Server = Server;
829 exports.createServer = function(options, listener) {
830 return new Server(options, listener);
834 Server.prototype.setOptions = function(options) {
835 if (typeof options.requestCert == 'boolean') {
836 this.requestCert = options.requestCert;
838 this.requestCert = false;
841 if (typeof options.rejectUnauthorized == 'boolean') {
842 this.rejectUnauthorized = options.rejectUnauthorized;
844 this.rejectUnauthorized = false;
847 if (options.key) this.key = options.key;
848 if (options.cert) this.cert = options.cert;
849 if (options.ca) this.ca = options.ca;
850 if (options.secureProtocol) this.secureProtocol = options.secureProtocol;
851 if (options.crl) this.crl = options.crl;
852 if (options.ciphers) this.ciphers = options.ciphers;
853 if (options.secureProtocol) this.secureProtocol = options.secureProtocol;
854 if (options.secureOptions) this.secureOptions = options.secureOptions;
855 if (options.NPNProtocols) convertNPNProtocols(options.NPNProtocols, this);
861 // var s = tls.connect(8000, "google.com", options, function() {
862 // if (!s.authorized) {
869 // s.end("hello world\n");
873 // TODO: make port, host part of options!
874 exports.connect = function(port /* host, options, cb */) {
876 var host, options = {}, cb;
877 for (var i = 1; i < arguments.length; i++) {
878 switch (typeof arguments[i]) {
884 options = arguments[i];
893 var socket = new net.Stream();
895 var sslcontext = crypto.createCredentials(options);
896 //sslcontext.context.setCiphers('RC4-SHA:AES128-SHA:AES256-SHA');
898 convertNPNProtocols(options.NPNProtocols, this);
899 var pair = new SecurePair(sslcontext, false, true, false,
902 var cleartext = pipe(pair, socket);
904 socket.connect(port, host);
906 pair.on('secure', function() {
907 var verifyError = pair._ssl.verifyError();
909 cleartext.npnProtocol = pair.npnProtocol;
912 cleartext.authorized = false;
913 cleartext.authorizationError = verifyError;
915 cleartext.authorized = true;
921 cleartext._controlReleased = true;
926 function pipe(pair, socket) {
927 pair.encrypted.pipe(socket);
928 socket.pipe(pair.encrypted);
931 var cleartext = pair.cleartext;
932 cleartext.socket = socket;
933 cleartext.encrypted = pair.encrypted;
934 cleartext.authorized = false;
936 function onerror(e) {
937 if (cleartext._controlReleased) {
938 cleartext.emit('error', e);
943 socket.removeListener('error', onerror);
944 socket.removeListener('close', onclose);
947 socket.on('error', onerror);
948 socket.on('close', onclose);