Merge branch 'v0.10'
[platform/upstream/nodejs.git] / lib / tls.js
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 var crypto = require('crypto');
23 var util = require('util');
24 var net = require('net');
25 var url = require('url');
26 var events = require('events');
27 var stream = require('stream');
28 var assert = require('assert').ok;
29 var constants = require('constants');
30
31 var DEFAULT_CIPHERS = 'ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:' + // TLS 1.2
32                       'RC4:HIGH:!MD5:!aNULL:!EDH';                   // TLS 1.0
33
34 // Allow {CLIENT_RENEG_LIMIT} client-initiated session renegotiations
35 // every {CLIENT_RENEG_WINDOW} seconds. An error event is emitted if more
36 // renegotations are seen. The settings are applied to all remote client
37 // connections.
38 exports.CLIENT_RENEG_LIMIT = 3;
39 exports.CLIENT_RENEG_WINDOW = 600;
40
41 exports.SLAB_BUFFER_SIZE = 10 * 1024 * 1024;
42
43 exports.getCiphers = function() {
44   var names = process.binding('crypto').getSSLCiphers();
45   // Drop all-caps names in favor of their lowercase aliases,
46   var ctx = {};
47   names.forEach(function(name) {
48     if (/^[0-9A-Z\-]+$/.test(name)) name = name.toLowerCase();
49     ctx[name] = true;
50   });
51   return Object.getOwnPropertyNames(ctx).sort();
52 };
53
54
55 var debug;
56 if (process.env.NODE_DEBUG && /tls/.test(process.env.NODE_DEBUG)) {
57   debug = function(a) { console.error('TLS:', a); };
58 } else {
59   debug = function() { };
60 }
61
62
63 var Connection = null;
64 try {
65   Connection = process.binding('crypto').Connection;
66 } catch (e) {
67   throw new Error('node.js not compiled with openssl crypto support.');
68 }
69
70 // Convert protocols array into valid OpenSSL protocols list
71 // ("\x06spdy/2\x08http/1.1\x08http/1.0")
72 function convertNPNProtocols(NPNProtocols, out) {
73   // If NPNProtocols is Array - translate it into buffer
74   if (Array.isArray(NPNProtocols)) {
75     var buff = new Buffer(NPNProtocols.reduce(function(p, c) {
76       return p + 1 + Buffer.byteLength(c);
77     }, 0));
78
79     NPNProtocols.reduce(function(offset, c) {
80       var clen = Buffer.byteLength(c);
81       buff[offset] = clen;
82       buff.write(c, offset + 1);
83
84       return offset + 1 + clen;
85     }, 0);
86
87     NPNProtocols = buff;
88   }
89
90   // If it's already a Buffer - store it
91   if (Buffer.isBuffer(NPNProtocols)) {
92     out.NPNProtocols = NPNProtocols;
93   }
94 }
95
96
97 function checkServerIdentity(host, cert) {
98   // Create regexp to much hostnames
99   function regexpify(host, wildcards) {
100     // Add trailing dot (make hostnames uniform)
101     if (!/\.$/.test(host)) host += '.';
102
103     // The same applies to hostname with more than one wildcard,
104     // if hostname has wildcard when wildcards are not allowed,
105     // or if there are less than two dots after wildcard (i.e. *.com or *d.com)
106     //
107     // also
108     //
109     // "The client SHOULD NOT attempt to match a presented identifier in
110     // which the wildcard character comprises a label other than the
111     // left-most label (e.g., do not match bar.*.example.net)."
112     // RFC6125
113     if (!wildcards && /\*/.test(host) || /[\.\*].*\*/.test(host) ||
114         /\*/.test(host) && !/\*.*\..+\..+/.test(host)) {
115       return /$./;
116     }
117
118     // Replace wildcard chars with regexp's wildcard and
119     // escape all characters that have special meaning in regexps
120     // (i.e. '.', '[', '{', '*', and others)
121     var re = host.replace(
122         /\*([a-z0-9\\-_\.])|[\.,\-\\\^\$+?*\[\]\(\):!\|{}]/g,
123         function(all, sub) {
124           if (sub) return '[a-z0-9\\-_]*' + (sub === '-' ? '\\-' : sub);
125           return '\\' + all;
126         });
127
128     return new RegExp('^' + re + '$', 'i');
129   }
130
131   var dnsNames = [],
132       uriNames = [],
133       ips = [],
134       matchCN = true,
135       valid = false;
136
137   // There're several names to perform check against:
138   // CN and altnames in certificate extension
139   // (DNS names, IP addresses, and URIs)
140   //
141   // Walk through altnames and generate lists of those names
142   if (cert.subjectaltname) {
143     cert.subjectaltname.split(/, /g).forEach(function(altname) {
144       if (/^DNS:/.test(altname)) {
145         dnsNames.push(altname.slice(4));
146       } else if (/^IP Address:/.test(altname)) {
147         ips.push(altname.slice(11));
148       } else if (/^URI:/.test(altname)) {
149         var uri = url.parse(altname.slice(4));
150         if (uri) uriNames.push(uri.hostname);
151       }
152     });
153   }
154
155   // If hostname is an IP address, it should be present in the list of IP
156   // addresses.
157   if (net.isIP(host)) {
158     valid = ips.some(function(ip) {
159       return ip === host;
160     });
161   } else {
162     // Transform hostname to canonical form
163     if (!/\.$/.test(host)) host += '.';
164
165     // Otherwise check all DNS/URI records from certificate
166     // (with allowed wildcards)
167     dnsNames = dnsNames.map(function(name) {
168       return regexpify(name, true);
169     });
170
171     // Wildcards ain't allowed in URI names
172     uriNames = uriNames.map(function(name) {
173       return regexpify(name, false);
174     });
175
176     dnsNames = dnsNames.concat(uriNames);
177
178     if (dnsNames.length > 0) matchCN = false;
179
180     // Match against Common Name (CN) only if no supported identifiers are
181     // present.
182     //
183     // "As noted, a client MUST NOT seek a match for a reference identifier
184     //  of CN-ID if the presented identifiers include a DNS-ID, SRV-ID,
185     //  URI-ID, or any application-specific identifier types supported by the
186     //  client."
187     // RFC6125
188     if (matchCN) {
189       var commonNames = cert.subject.CN;
190       if (Array.isArray(commonNames)) {
191         for (var i = 0, k = commonNames.length; i < k; ++i) {
192           dnsNames.push(regexpify(commonNames[i], true));
193         }
194       } else {
195         dnsNames.push(regexpify(commonNames, true));
196       }
197     }
198
199     valid = dnsNames.some(function(re) {
200       return re.test(host);
201     });
202   }
203
204   return valid;
205 }
206 exports.checkServerIdentity = checkServerIdentity;
207
208
209 function SlabBuffer() {
210   this.create();
211 }
212
213
214 SlabBuffer.prototype.create = function create() {
215   this.isFull = false;
216   this.pool = new Buffer(exports.SLAB_BUFFER_SIZE);
217   this.offset = 0;
218   this.remaining = this.pool.length;
219 };
220
221
222 SlabBuffer.prototype.use = function use(context, fn, size) {
223   if (this.remaining === 0) {
224     this.isFull = true;
225     return 0;
226   }
227
228   var actualSize = this.remaining;
229
230   if (size !== null) actualSize = Math.min(size, actualSize);
231
232   var bytes = fn.call(context, this.pool, this.offset, actualSize);
233   if (bytes > 0) {
234     this.offset += bytes;
235     this.remaining -= bytes;
236   }
237
238   assert(this.remaining >= 0);
239
240   return bytes;
241 };
242
243
244 var slabBuffer = null;
245
246
247 // Base class of both CleartextStream and EncryptedStream
248 function CryptoStream(pair, options) {
249   stream.Duplex.call(this, options);
250
251   this.pair = pair;
252   this._pending = null;
253   this._pendingEncoding = '';
254   this._pendingCallback = null;
255   this._doneFlag = false;
256   this._resumingSession = false;
257   this._reading = true;
258   this._destroyed = false;
259   this._ended = false;
260   this._finished = false;
261   this._opposite = null;
262
263   if (slabBuffer === null) slabBuffer = new SlabBuffer();
264   this._buffer = slabBuffer;
265
266   this.once('finish', onCryptoStreamFinish);
267
268   // net.Socket calls .onend too
269   this.once('end', onCryptoStreamEnd);
270 }
271 util.inherits(CryptoStream, stream.Duplex);
272
273
274 function onCryptoStreamFinish() {
275   this._finished = true;
276
277   if (this === this.pair.cleartext) {
278     debug('cleartext.onfinish');
279     if (this.pair.ssl) {
280       // Generate close notify
281       // NOTE: first call checks if client has sent us shutdown,
282       // second call enqueues shutdown into the BIO.
283       if (this.pair.ssl.shutdown() !== 1) {
284         this.pair.ssl.shutdown();
285       }
286     }
287   } else {
288     debug('encrypted.onfinish');
289   }
290
291   // Try to read just to get sure that we won't miss EOF
292   if (this._opposite.readable) this._opposite.read(0);
293
294   if (this._opposite._ended) {
295     this._done();
296
297     // No half-close, sorry
298     if (this === this.pair.cleartext) this._opposite._done();
299   }
300 }
301
302
303 function onCryptoStreamEnd() {
304   this._ended = true;
305   if (this === this.pair.cleartext) {
306     debug('cleartext.onend');
307   } else {
308     debug('encrypted.onend');
309   }
310
311   if (this.onend) this.onend();
312 }
313
314
315 CryptoStream.prototype._write = function write(data, encoding, cb) {
316   assert(this._pending === null);
317
318   // Black-hole data
319   if (!this.pair.ssl) return cb(null);
320
321   // When resuming session don't accept any new data.
322   // And do not put too much data into openssl, before writing it from encrypted
323   // side.
324   //
325   // TODO(indutny): Remove magic number, use watermark based limits
326   if (!this._resumingSession &&
327       this._opposite._internallyPendingBytes() < 128 * 1024) {
328     // Write current buffer now
329     var written;
330     if (this === this.pair.cleartext) {
331       debug('cleartext.write called with ' + data.length + ' bytes');
332       written = this.pair.ssl.clearIn(data, 0, data.length);
333     } else {
334       debug('encrypted.write called with ' + data.length + ' bytes');
335       written = this.pair.ssl.encIn(data, 0, data.length);
336     }
337
338     // Handle and report errors
339     if (this.pair.ssl && this.pair.ssl.error) {
340       return cb(this.pair.error(true));
341     }
342
343     // Force SSL_read call to cycle some states/data inside OpenSSL
344     this.pair.cleartext.read(0);
345
346     // Cycle encrypted data
347     if (this.pair.encrypted._internallyPendingBytes()) {
348       this.pair.encrypted.read(0);
349     }
350
351     // Get NPN and Server name when ready
352     this.pair.maybeInitFinished();
353
354     // Whole buffer was written
355     if (written === data.length) {
356       if (this === this.pair.cleartext) {
357         debug('cleartext.write succeed with ' + data.length + ' bytes');
358       } else {
359         debug('encrypted.write succeed with ' + data.length + ' bytes');
360       }
361
362       return cb(null);
363     }
364     assert(written === 0 || written === -1);
365   } else {
366     debug('cleartext.write queue is full');
367
368     // Force SSL_read call to cycle some states/data inside OpenSSL
369     this.pair.cleartext.read(0);
370   }
371
372   // No write has happened
373   this._pending = data;
374   this._pendingEncoding = encoding;
375   this._pendingCallback = cb;
376
377   if (this === this.pair.cleartext) {
378     debug('cleartext.write queued with ' + data.length + ' bytes');
379   } else {
380     debug('encrypted.write queued with ' + data.length + ' bytes');
381   }
382 };
383
384
385 CryptoStream.prototype._writePending = function writePending() {
386   var data = this._pending,
387       encoding = this._pendingEncoding,
388       cb = this._pendingCallback;
389
390   this._pending = null;
391   this._pendingEncoding = '';
392   this._pendingCallback = null;
393   this._write(data, encoding, cb);
394 };
395
396
397 CryptoStream.prototype._read = function read(size) {
398   // XXX: EOF?!
399   if (!this.pair.ssl) return this.push(null);
400
401   // Wait for session to be resumed
402   // Mark that we're done reading, but don't provide data or EOF
403   if (this._resumingSession || !this._reading) return this.push('');
404
405   var out;
406   if (this === this.pair.cleartext) {
407     debug('cleartext.read called with ' + size + ' bytes');
408     out = this.pair.ssl.clearOut;
409   } else {
410     debug('encrypted.read called with ' + size + ' bytes');
411     out = this.pair.ssl.encOut;
412   }
413
414   var bytesRead = 0,
415       start = this._buffer.offset;
416   do {
417     var read = this._buffer.use(this.pair.ssl, out, size);
418     if (read > 0) {
419       bytesRead += read;
420       size -= read;
421     }
422
423     // Handle and report errors
424     if (this.pair.ssl && this.pair.ssl.error) {
425       this.pair.error();
426       break;
427     }
428
429     // Get NPN and Server name when ready
430     this.pair.maybeInitFinished();
431   } while (read > 0 && !this._buffer.isFull && bytesRead < size);
432
433   // Create new buffer if previous was filled up
434   var pool = this._buffer.pool;
435   if (this._buffer.isFull) this._buffer.create();
436
437   assert(bytesRead >= 0);
438
439   if (this === this.pair.cleartext) {
440     debug('cleartext.read succeed with ' + bytesRead + ' bytes');
441   } else {
442     debug('encrypted.read succeed with ' + bytesRead + ' bytes');
443   }
444
445   // Try writing pending data
446   if (this._pending !== null) this._writePending();
447   if (this._opposite._pending !== null) this._opposite._writePending();
448
449   if (bytesRead === 0) {
450     // EOF when cleartext has finished and we have nothing to read
451     if (this._opposite._finished && this._internallyPendingBytes() === 0) {
452       // Perform graceful shutdown
453       this._done();
454
455       // No half-open, sorry!
456       if (this === this.pair.cleartext)
457         this._opposite._done();
458
459       // EOF
460       return this.push(null);
461     }
462
463     // Bail out
464     return this.push('');
465   }
466
467   // Give them requested data
468   if (this.ondata) {
469     var self = this;
470     this.ondata(pool, start, start + bytesRead);
471
472     // Consume data automatically
473     // simple/test-https-drain fails without it
474     process.nextTick(function() {
475       self.read(bytesRead);
476     });
477   }
478   return this.push(pool.slice(start, start + bytesRead));
479 };
480
481
482 CryptoStream.prototype.setTimeout = function(timeout, callback) {
483   if (this.socket) this.socket.setTimeout(timeout, callback);
484 };
485
486
487 CryptoStream.prototype.setNoDelay = function(noDelay) {
488   if (this.socket) this.socket.setNoDelay(noDelay);
489 };
490
491
492 CryptoStream.prototype.setKeepAlive = function(enable, initialDelay) {
493   if (this.socket) this.socket.setKeepAlive(enable, initialDelay);
494 };
495
496 CryptoStream.prototype.__defineGetter__('bytesWritten', function() {
497   return this.socket ? this.socket.bytesWritten : 0;
498 });
499
500
501 // Example:
502 // C=US\nST=CA\nL=SF\nO=Joyent\nOU=Node.js\nCN=ca1\nemailAddress=ry@clouds.org
503 function parseCertString(s) {
504   var out = {};
505   var parts = s.split('\n');
506   for (var i = 0, len = parts.length; i < len; i++) {
507     var sepIndex = parts[i].indexOf('=');
508     if (sepIndex > 0) {
509       var key = parts[i].slice(0, sepIndex);
510       var value = parts[i].slice(sepIndex + 1);
511       if (key in out) {
512         if (!Array.isArray(out[key])) {
513           out[key] = [out[key]];
514         }
515         out[key].push(value);
516       } else {
517         out[key] = value;
518       }
519     }
520   }
521   return out;
522 }
523
524
525 CryptoStream.prototype.getPeerCertificate = function() {
526   if (this.pair.ssl) {
527     var c = this.pair.ssl.getPeerCertificate();
528
529     if (c) {
530       if (c.issuer) c.issuer = parseCertString(c.issuer);
531       if (c.subject) c.subject = parseCertString(c.subject);
532       return c;
533     }
534   }
535
536   return null;
537 };
538
539 CryptoStream.prototype.getSession = function() {
540   if (this.pair.ssl) {
541     return this.pair.ssl.getSession();
542   }
543
544   return null;
545 };
546
547 CryptoStream.prototype.isSessionReused = function() {
548   if (this.pair.ssl) {
549     return this.pair.ssl.isSessionReused();
550   }
551
552   return null;
553 };
554
555 CryptoStream.prototype.getCipher = function(err) {
556   if (this.pair.ssl) {
557     return this.pair.ssl.getCurrentCipher();
558   } else {
559     return null;
560   }
561 };
562
563
564 CryptoStream.prototype.end = function(chunk, encoding) {
565   if (this === this.pair.cleartext) {
566     debug('cleartext.end');
567   } else {
568     debug('encrypted.end');
569   }
570
571   // Write pending data first
572   if (this._pending !== null) this._writePending();
573
574   this.writable = false;
575
576   stream.Duplex.prototype.end.call(this, chunk, encoding);
577 };
578
579
580 CryptoStream.prototype.destroySoon = function(err) {
581   if (this === this.pair.cleartext) {
582     debug('cleartext.destroySoon');
583   } else {
584     debug('encrypted.destroySoon');
585   }
586
587   if (this.writable)
588     this.end();
589
590   if (this._writableState.finished)
591     this.destroy();
592   else
593     this.once('finish', this.destroy);
594 };
595
596
597 CryptoStream.prototype.destroy = function(err) {
598   if (this._destroyed) return;
599   this._destroyed = true;
600   this.readable = this.writable = false;
601
602   // Destroy both ends
603   if (this === this.pair.cleartext) {
604     debug('cleartext.destroy');
605   } else {
606     debug('encrypted.destroy');
607   }
608   this._opposite.destroy();
609
610   var self = this;
611   process.nextTick(function() {
612     // Force EOF
613     self.push(null);
614
615     // Emit 'close' event
616     self.emit('close', err ? true : false);
617   });
618 };
619
620
621 CryptoStream.prototype._done = function() {
622   this._doneFlag = true;
623
624   if (this === this.pair.encrypted && !this.pair._secureEstablished)
625     return this.pair.error();
626
627   if (this.pair.cleartext._doneFlag &&
628       this.pair.encrypted._doneFlag &&
629       !this.pair._doneFlag) {
630     // If both streams are done:
631     this.pair.destroy();
632   }
633 };
634
635
636 // readyState is deprecated. Don't use it.
637 Object.defineProperty(CryptoStream.prototype, 'readyState', {
638   get: function() {
639     if (this._connecting) {
640       return 'opening';
641     } else if (this.readable && this.writable) {
642       return 'open';
643     } else if (this.readable && !this.writable) {
644       return 'readOnly';
645     } else if (!this.readable && this.writable) {
646       return 'writeOnly';
647     } else {
648       return 'closed';
649     }
650   }
651 });
652
653
654 function CleartextStream(pair, options) {
655   CryptoStream.call(this, pair, options);
656
657   // This is a fake kludge to support how the http impl sits
658   // on top of net Sockets
659   var self = this;
660   this._handle = {
661     readStop: function() {
662       self._reading = false;
663     },
664     readStart: function() {
665       if (self._reading && self._readableState.length > 0) return;
666       self._reading = true;
667       self.read(0);
668       if (self._opposite.readable) self._opposite.read(0);
669     }
670   };
671 }
672 util.inherits(CleartextStream, CryptoStream);
673
674
675 CleartextStream.prototype._internallyPendingBytes = function() {
676   if (this.pair.ssl) {
677     return this.pair.ssl.clearPending();
678   } else {
679     return 0;
680   }
681 };
682
683
684 CleartextStream.prototype.address = function() {
685   return this.socket && this.socket.address();
686 };
687
688
689 CleartextStream.prototype.__defineGetter__('remoteAddress', function() {
690   return this.socket && this.socket.remoteAddress;
691 });
692
693
694 CleartextStream.prototype.__defineGetter__('remotePort', function() {
695   return this.socket && this.socket.remotePort;
696 });
697
698 function EncryptedStream(pair, options) {
699   CryptoStream.call(this, pair, options);
700 }
701 util.inherits(EncryptedStream, CryptoStream);
702
703
704 EncryptedStream.prototype._internallyPendingBytes = function() {
705   if (this.pair.ssl) {
706     return this.pair.ssl.encPending();
707   } else {
708     return 0;
709   }
710 };
711
712
713 function onhandshakestart() {
714   debug('onhandshakestart');
715
716   var self = this;
717   var ssl = self.ssl;
718   var now = Date.now();
719
720   assert(now >= ssl.lastHandshakeTime);
721
722   if ((now - ssl.lastHandshakeTime) >= exports.CLIENT_RENEG_WINDOW * 1000) {
723     ssl.handshakes = 0;
724   }
725
726   var first = (ssl.lastHandshakeTime === 0);
727   ssl.lastHandshakeTime = now;
728   if (first) return;
729
730   if (++ssl.handshakes > exports.CLIENT_RENEG_LIMIT) {
731     // Defer the error event to the next tick. We're being called from OpenSSL's
732     // state machine and OpenSSL is not re-entrant. We cannot allow the user's
733     // callback to destroy the connection right now, it would crash and burn.
734     setImmediate(function() {
735       var err = new Error('TLS session renegotiation attack detected.');
736       if (self.cleartext) self.cleartext.emit('error', err);
737     });
738   }
739 }
740
741
742 function onhandshakedone() {
743   // for future use
744   debug('onhandshakedone');
745 }
746
747
748 function onclienthello(hello) {
749   var self = this,
750       once = false;
751
752   this._resumingSession = true;
753   function callback(err, session) {
754     if (once) return;
755     once = true;
756
757     if (err) return self.socket.destroy(err);
758
759     self.ssl.loadSession(session);
760
761     // Cycle data
762     self._resumingSession = false;
763     self.cleartext.read(0);
764     self.encrypted.read(0);
765   }
766
767   if (hello.sessionId.length <= 0 ||
768       !this.server ||
769       !this.server.emit('resumeSession', hello.sessionId, callback)) {
770     callback(null, null);
771   }
772 }
773
774
775 function onnewsession(key, session) {
776   if (!this.server) return;
777   this.server.emit('newSession', key, session);
778 }
779
780
781 /**
782  * Provides a pair of streams to do encrypted communication.
783  */
784
785 function SecurePair(credentials, isServer, requestCert, rejectUnauthorized,
786                     options) {
787   if (!(this instanceof SecurePair)) {
788     return new SecurePair(credentials,
789                           isServer,
790                           requestCert,
791                           rejectUnauthorized,
792                           options);
793   }
794
795   var self = this;
796
797   options || (options = {});
798
799   events.EventEmitter.call(this);
800
801   this.server = options.server;
802   this._secureEstablished = false;
803   this._isServer = isServer ? true : false;
804   this._encWriteState = true;
805   this._clearWriteState = true;
806   this._doneFlag = false;
807   this._destroying = false;
808
809   if (!credentials) {
810     this.credentials = crypto.createCredentials();
811   } else {
812     this.credentials = credentials;
813   }
814
815   if (!this._isServer) {
816     // For clients, we will always have either a given ca list or be using
817     // default one
818     requestCert = true;
819   }
820
821   this._rejectUnauthorized = rejectUnauthorized ? true : false;
822   this._requestCert = requestCert ? true : false;
823
824   this.ssl = new Connection(this.credentials.context,
825                             this._isServer ? true : false,
826                             this._isServer ? this._requestCert :
827                                              options.servername,
828                             this._rejectUnauthorized);
829
830   if (this._isServer) {
831     this.ssl.onhandshakestart = onhandshakestart.bind(this);
832     this.ssl.onhandshakedone = onhandshakedone.bind(this);
833     this.ssl.onclienthello = onclienthello.bind(this);
834     this.ssl.onnewsession = onnewsession.bind(this);
835     this.ssl.lastHandshakeTime = 0;
836     this.ssl.handshakes = 0;
837   }
838
839   if (process.features.tls_sni) {
840     if (this._isServer && options.SNICallback) {
841       this.ssl.setSNICallback(options.SNICallback);
842     }
843     this.servername = null;
844   }
845
846   if (process.features.tls_npn && options.NPNProtocols) {
847     this.ssl.setNPNProtocols(options.NPNProtocols);
848     this.npnProtocol = null;
849   }
850
851   /* Acts as a r/w stream to the cleartext side of the stream. */
852   this.cleartext = new CleartextStream(this, options.cleartext);
853
854   /* Acts as a r/w stream to the encrypted side of the stream. */
855   this.encrypted = new EncryptedStream(this, options.encrypted);
856
857   /* Let streams know about each other */
858   this.cleartext._opposite = this.encrypted;
859   this.encrypted._opposite = this.cleartext;
860
861   process.nextTick(function() {
862     /* The Connection may be destroyed by an abort call */
863     if (self.ssl) {
864       self.ssl.start();
865     }
866   });
867 }
868
869 util.inherits(SecurePair, events.EventEmitter);
870
871
872 exports.createSecurePair = function(credentials,
873                                     isServer,
874                                     requestCert,
875                                     rejectUnauthorized) {
876   var pair = new SecurePair(credentials,
877                             isServer,
878                             requestCert,
879                             rejectUnauthorized);
880   return pair;
881 };
882
883
884 SecurePair.prototype.maybeInitFinished = function() {
885   if (this.ssl && !this._secureEstablished && this.ssl.isInitFinished()) {
886     if (process.features.tls_npn) {
887       this.npnProtocol = this.ssl.getNegotiatedProtocol();
888     }
889
890     if (process.features.tls_sni) {
891       this.servername = this.ssl.getServername();
892     }
893
894     this._secureEstablished = true;
895     debug('secure established');
896     this.emit('secure');
897   }
898 };
899
900
901 SecurePair.prototype.destroy = function() {
902   if (this._destroying) return;
903
904   if (!this._doneFlag) {
905     debug('SecurePair.destroy');
906     this._destroying = true;
907
908     // SecurePair should be destroyed only after it's streams
909     this.cleartext.destroy();
910     this.encrypted.destroy();
911
912     this._doneFlag = true;
913     this.ssl.error = null;
914     this.ssl.close();
915     this.ssl = null;
916   }
917 };
918
919
920 SecurePair.prototype.error = function(returnOnly) {
921   var err = this.ssl.error;
922   this.ssl.error = null;
923
924   if (!this._secureEstablished) {
925     // Emit ECONNRESET instead of zero return
926     if (!err || err.message === 'ZERO_RETURN') {
927       var connReset = new Error('socket hang up');
928       connReset.code = 'ECONNRESET';
929       connReset.sslError = err && err.message;
930
931       err = connReset;
932     }
933     this.destroy();
934     if (!returnOnly) this.emit('error', err);
935   } else if (this._isServer &&
936              this._rejectUnauthorized &&
937              /peer did not return a certificate/.test(err.message)) {
938     // Not really an error.
939     this.destroy();
940   } else {
941     if (!returnOnly) this.cleartext.emit('error', err);
942   }
943   return err;
944 };
945
946 // TODO: support anonymous (nocert) and PSK
947
948
949 // AUTHENTICATION MODES
950 //
951 // There are several levels of authentication that TLS/SSL supports.
952 // Read more about this in "man SSL_set_verify".
953 //
954 // 1. The server sends a certificate to the client but does not request a
955 // cert from the client. This is common for most HTTPS servers. The browser
956 // can verify the identity of the server, but the server does not know who
957 // the client is. Authenticating the client is usually done over HTTP using
958 // login boxes and cookies and stuff.
959 //
960 // 2. The server sends a cert to the client and requests that the client
961 // also send it a cert. The client knows who the server is and the server is
962 // requesting the client also identify themselves. There are several
963 // outcomes:
964 //
965 //   A) verifyError returns null meaning the client's certificate is signed
966 //   by one of the server's CAs. The server know's the client idenity now
967 //   and the client is authorized.
968 //
969 //   B) For some reason the client's certificate is not acceptable -
970 //   verifyError returns a string indicating the problem. The server can
971 //   either (i) reject the client or (ii) allow the client to connect as an
972 //   unauthorized connection.
973 //
974 // The mode is controlled by two boolean variables.
975 //
976 // requestCert
977 //   If true the server requests a certificate from client connections. For
978 //   the common HTTPS case, users will want this to be false, which is what
979 //   it defaults to.
980 //
981 // rejectUnauthorized
982 //   If true clients whose certificates are invalid for any reason will not
983 //   be allowed to make connections. If false, they will simply be marked as
984 //   unauthorized but secure communication will continue. By default this is
985 //   true.
986 //
987 //
988 //
989 // Options:
990 // - requestCert. Send verify request. Default to false.
991 // - rejectUnauthorized. Boolean, default to true.
992 // - key. string.
993 // - cert: string.
994 // - ca: string or array of strings.
995 // - sessionTimeout: integer.
996 //
997 // emit 'secureConnection'
998 //   function (cleartextStream, encryptedStream) { }
999 //
1000 //   'cleartextStream' has the boolean property 'authorized' to determine if
1001 //   it was verified by the CA. If 'authorized' is false, a property
1002 //   'authorizationError' is set on cleartextStream and has the possible
1003 //   values:
1004 //
1005 //   "UNABLE_TO_GET_ISSUER_CERT", "UNABLE_TO_GET_CRL",
1006 //   "UNABLE_TO_DECRYPT_CERT_SIGNATURE", "UNABLE_TO_DECRYPT_CRL_SIGNATURE",
1007 //   "UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY", "CERT_SIGNATURE_FAILURE",
1008 //   "CRL_SIGNATURE_FAILURE", "CERT_NOT_YET_VALID" "CERT_HAS_EXPIRED",
1009 //   "CRL_NOT_YET_VALID", "CRL_HAS_EXPIRED" "ERROR_IN_CERT_NOT_BEFORE_FIELD",
1010 //   "ERROR_IN_CERT_NOT_AFTER_FIELD", "ERROR_IN_CRL_LAST_UPDATE_FIELD",
1011 //   "ERROR_IN_CRL_NEXT_UPDATE_FIELD", "OUT_OF_MEM",
1012 //   "DEPTH_ZERO_SELF_SIGNED_CERT", "SELF_SIGNED_CERT_IN_CHAIN",
1013 //   "UNABLE_TO_GET_ISSUER_CERT_LOCALLY", "UNABLE_TO_VERIFY_LEAF_SIGNATURE",
1014 //   "CERT_CHAIN_TOO_LONG", "CERT_REVOKED" "INVALID_CA",
1015 //   "PATH_LENGTH_EXCEEDED", "INVALID_PURPOSE" "CERT_UNTRUSTED",
1016 //   "CERT_REJECTED"
1017 //
1018 //
1019 // TODO:
1020 // cleartext.credentials (by mirroring from pair object)
1021 // cleartext.getCertificate() (by mirroring from pair.credentials.context)
1022 function Server(/* [options], listener */) {
1023   var options, listener;
1024   if (typeof arguments[0] == 'object') {
1025     options = arguments[0];
1026     listener = arguments[1];
1027   } else if (typeof arguments[0] == 'function') {
1028     options = {};
1029     listener = arguments[0];
1030   }
1031
1032   if (!(this instanceof Server)) return new Server(options, listener);
1033
1034   this._contexts = [];
1035
1036   var self = this;
1037
1038   // Handle option defaults:
1039   this.setOptions(options);
1040
1041   if (!self.pfx && (!self.cert || !self.key)) {
1042     throw new Error('Missing PFX or certificate + private key.');
1043   }
1044
1045   var sharedCreds = crypto.createCredentials({
1046     pfx: self.pfx,
1047     key: self.key,
1048     passphrase: self.passphrase,
1049     cert: self.cert,
1050     ca: self.ca,
1051     ciphers: self.ciphers || DEFAULT_CIPHERS,
1052     secureProtocol: self.secureProtocol,
1053     secureOptions: self.secureOptions,
1054     crl: self.crl,
1055     sessionIdContext: self.sessionIdContext
1056   });
1057
1058   var timeout = options.handshakeTimeout || (120 * 1000);
1059
1060   if (typeof timeout !== 'number') {
1061     throw new TypeError('handshakeTimeout must be a number');
1062   }
1063
1064   if (self.sessionTimeout) {
1065     sharedCreds.context.setSessionTimeout(self.sessionTimeout);
1066   }
1067
1068   // constructor call
1069   net.Server.call(this, function(socket) {
1070     var creds = crypto.createCredentials(null, sharedCreds.context);
1071
1072     var pair = new SecurePair(creds,
1073                               true,
1074                               self.requestCert,
1075                               self.rejectUnauthorized,
1076                               {
1077                                 server: self,
1078                                 NPNProtocols: self.NPNProtocols,
1079                                 SNICallback: self.SNICallback,
1080
1081                                 // Stream options
1082                                 cleartext: self._cleartext,
1083                                 encrypted: self._encrypted
1084                               });
1085
1086     var cleartext = pipe(pair, socket);
1087     cleartext._controlReleased = false;
1088
1089     function listener() {
1090       pair.emit('error', new Error('TLS handshake timeout'));
1091     }
1092
1093     if (timeout > 0) {
1094       socket.setTimeout(timeout, listener);
1095     }
1096
1097     pair.once('secure', function() {
1098       socket.setTimeout(0, listener);
1099
1100       pair.cleartext.authorized = false;
1101       pair.cleartext.npnProtocol = pair.npnProtocol;
1102       pair.cleartext.servername = pair.servername;
1103
1104       if (!self.requestCert) {
1105         cleartext._controlReleased = true;
1106         self.emit('secureConnection', pair.cleartext, pair.encrypted);
1107       } else {
1108         var verifyError = pair.ssl.verifyError();
1109         if (verifyError) {
1110           pair.cleartext.authorizationError = verifyError.message;
1111
1112           if (self.rejectUnauthorized) {
1113             socket.destroy();
1114             pair.destroy();
1115           } else {
1116             cleartext._controlReleased = true;
1117             self.emit('secureConnection', pair.cleartext, pair.encrypted);
1118           }
1119         } else {
1120           pair.cleartext.authorized = true;
1121           cleartext._controlReleased = true;
1122           self.emit('secureConnection', pair.cleartext, pair.encrypted);
1123         }
1124       }
1125     });
1126     pair.on('error', function(err) {
1127       self.emit('clientError', err, this);
1128     });
1129   });
1130
1131   if (listener) {
1132     this.on('secureConnection', listener);
1133   }
1134 }
1135
1136 util.inherits(Server, net.Server);
1137 exports.Server = Server;
1138 exports.createServer = function(options, listener) {
1139   return new Server(options, listener);
1140 };
1141
1142
1143 Server.prototype.setOptions = function(options) {
1144   if (typeof options.requestCert == 'boolean') {
1145     this.requestCert = options.requestCert;
1146   } else {
1147     this.requestCert = false;
1148   }
1149
1150   if (typeof options.rejectUnauthorized == 'boolean') {
1151     this.rejectUnauthorized = options.rejectUnauthorized;
1152   } else {
1153     this.rejectUnauthorized = false;
1154   }
1155
1156   if (options.pfx) this.pfx = options.pfx;
1157   if (options.key) this.key = options.key;
1158   if (options.passphrase) this.passphrase = options.passphrase;
1159   if (options.cert) this.cert = options.cert;
1160   if (options.ca) this.ca = options.ca;
1161   if (options.secureProtocol) this.secureProtocol = options.secureProtocol;
1162   if (options.crl) this.crl = options.crl;
1163   if (options.ciphers) this.ciphers = options.ciphers;
1164   if (options.sessionTimeout) this.sessionTimeout = options.sessionTimeout;
1165   var secureOptions = options.secureOptions || 0;
1166   if (options.honorCipherOrder) {
1167     secureOptions |= constants.SSL_OP_CIPHER_SERVER_PREFERENCE;
1168   }
1169   if (secureOptions) this.secureOptions = secureOptions;
1170   if (options.NPNProtocols) convertNPNProtocols(options.NPNProtocols, this);
1171   if (options.SNICallback) {
1172     this.SNICallback = options.SNICallback;
1173   } else {
1174     this.SNICallback = this.SNICallback.bind(this);
1175   }
1176   if (options.sessionIdContext) {
1177     this.sessionIdContext = options.sessionIdContext;
1178   } else if (this.requestCert) {
1179     this.sessionIdContext = crypto.createHash('md5')
1180                                   .update(process.argv.join(' '))
1181                                   .digest('hex');
1182   }
1183   if (options.cleartext) this.cleartext = options.cleartext;
1184   if (options.encrypted) this.encrypted = options.encrypted;
1185 };
1186
1187 // SNI Contexts High-Level API
1188 Server.prototype.addContext = function(servername, credentials) {
1189   if (!servername) {
1190     throw 'Servername is required parameter for Server.addContext';
1191   }
1192
1193   var re = new RegExp('^' +
1194                       servername.replace(/([\.^$+?\-\\[\]{}])/g, '\\$1')
1195                                 .replace(/\*/g, '.*') +
1196                       '$');
1197   this._contexts.push([re, crypto.createCredentials(credentials).context]);
1198 };
1199
1200 Server.prototype.SNICallback = function(servername) {
1201   var ctx;
1202
1203   this._contexts.some(function(elem) {
1204     if (servername.match(elem[0]) !== null) {
1205       ctx = elem[1];
1206       return true;
1207     }
1208   });
1209
1210   return ctx;
1211 };
1212
1213
1214 // Target API:
1215 //
1216 //  var s = tls.connect({port: 8000, host: "google.com"}, function() {
1217 //    if (!s.authorized) {
1218 //      s.destroy();
1219 //      return;
1220 //    }
1221 //
1222 //    // s.socket;
1223 //
1224 //    s.end("hello world\n");
1225 //  });
1226 //
1227 //
1228 function normalizeConnectArgs(listArgs) {
1229   var args = net._normalizeConnectArgs(listArgs);
1230   var options = args[0];
1231   var cb = args[1];
1232
1233   if (typeof listArgs[1] === 'object') {
1234     options = util._extend(options, listArgs[1]);
1235   } else if (typeof listArgs[2] === 'object') {
1236     options = util._extend(options, listArgs[2]);
1237   }
1238
1239   return (cb) ? [options, cb] : [options];
1240 }
1241
1242 exports.connect = function(/* [port, host], options, cb */) {
1243   var args = normalizeConnectArgs(arguments);
1244   var options = args[0];
1245   var cb = args[1];
1246
1247   var defaults = {
1248     rejectUnauthorized: '0' !== process.env.NODE_TLS_REJECT_UNAUTHORIZED
1249   };
1250   options = util._extend(defaults, options || {});
1251
1252   var socket = options.socket ? options.socket : new net.Stream();
1253
1254   var sslcontext = crypto.createCredentials(options);
1255
1256   convertNPNProtocols(options.NPNProtocols, this);
1257   var hostname = options.servername || options.host || 'localhost',
1258       pair = new SecurePair(sslcontext, false, true,
1259                             options.rejectUnauthorized === true ? true : false,
1260                             {
1261                               NPNProtocols: this.NPNProtocols,
1262                               servername: hostname,
1263                               cleartext: options.cleartext,
1264                               encrypted: options.encrypted
1265                             });
1266
1267   if (options.session) {
1268     var session = options.session;
1269     if (typeof session === 'string')
1270       session = new Buffer(session, 'binary');
1271     pair.ssl.setSession(session);
1272   }
1273
1274   var cleartext = pipe(pair, socket);
1275   if (cb) {
1276     cleartext.once('secureConnect', cb);
1277   }
1278
1279   if (!options.socket) {
1280     var connect_opt = (options.path && !options.port) ? {path: options.path} : {
1281       port: options.port,
1282       host: options.host,
1283       localAddress: options.localAddress
1284     };
1285     socket.connect(connect_opt);
1286   }
1287
1288   pair.on('secure', function() {
1289     var verifyError = pair.ssl.verifyError();
1290
1291     cleartext.npnProtocol = pair.npnProtocol;
1292
1293     // Verify that server's identity matches it's certificate's names
1294     if (!verifyError) {
1295       var validCert = checkServerIdentity(hostname,
1296                                           pair.cleartext.getPeerCertificate());
1297       if (!validCert) {
1298         verifyError = new Error('Hostname/IP doesn\'t match certificate\'s ' +
1299                                 'altnames');
1300       }
1301     }
1302
1303     if (verifyError) {
1304       cleartext.authorized = false;
1305       cleartext.authorizationError = verifyError.message;
1306
1307       if (pair._rejectUnauthorized) {
1308         cleartext.emit('error', verifyError);
1309         pair.destroy();
1310       } else {
1311         cleartext.emit('secureConnect');
1312       }
1313     } else {
1314       cleartext.authorized = true;
1315       cleartext.emit('secureConnect');
1316     }
1317   });
1318   pair.on('error', function(err) {
1319     cleartext.emit('error', err);
1320   });
1321
1322   cleartext._controlReleased = true;
1323   return cleartext;
1324 };
1325
1326
1327 function pipe(pair, socket) {
1328   pair.encrypted.pipe(socket);
1329   socket.pipe(pair.encrypted);
1330
1331   pair.encrypted.on('close', function() {
1332     process.nextTick(function() {
1333       socket.destroy();
1334     });
1335   });
1336
1337   pair.fd = socket.fd;
1338   var cleartext = pair.cleartext;
1339   cleartext.socket = socket;
1340   cleartext.encrypted = pair.encrypted;
1341   cleartext.authorized = false;
1342
1343   // cycle the data whenever the socket drains, so that
1344   // we can pull some more into it.  normally this would
1345   // be handled by the fact that pipe() triggers read() calls
1346   // on writable.drain, but CryptoStreams are a bit more
1347   // complicated.  Since the encrypted side actually gets
1348   // its data from the cleartext side, we have to give it a
1349   // light kick to get in motion again.
1350   socket.on('drain', function() {
1351     if (pair.encrypted._pending)
1352       pair.encrypted._writePending();
1353     if (pair.cleartext._pending)
1354       pair.cleartext._writePending();
1355     pair.encrypted.read(0);
1356     pair.cleartext.read(0);
1357   });
1358
1359   function onerror(e) {
1360     if (cleartext._controlReleased) {
1361       cleartext.emit('error', e);
1362     }
1363   }
1364
1365   function onclose() {
1366     socket.removeListener('error', onerror);
1367     socket.removeListener('timeout', ontimeout);
1368   }
1369
1370   function ontimeout() {
1371     cleartext.emit('timeout');
1372   }
1373
1374   socket.on('error', onerror);
1375   socket.on('close', onclose);
1376   socket.on('timeout', ontimeout);
1377
1378   return cleartext;
1379 }