The data contained in this repository can be downloaded to your computer using one of several clients.
Please see the documentation of your version control software client for more information.

Please select the desired protocol below to get the URL.

This URL has Read-Only access.

Statistics
| Branch: | Revision:

main_repo / lib / tls.js @ 595b5974

History | View | Annotate | Download (38.7 KB)

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 END_OF_FILE = 42;
29
var assert = require('assert').ok;
30
var constants = require('constants');
31

    
32
var DEFAULT_CIPHERS = 'ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:' + // TLS 1.2
33
                      'RC4:HIGH:!MD5:!aNULL:!EDH';                   // TLS 1.0
34

    
35
// Allow {CLIENT_RENEG_LIMIT} client-initiated session renegotiations
36
// every {CLIENT_RENEG_WINDOW} seconds. An error event is emitted if more
37
// renegotations are seen. The settings are applied to all remote client
38
// connections.
39
exports.CLIENT_RENEG_LIMIT = 3;
40
exports.CLIENT_RENEG_WINDOW = 600;
41

    
42
exports.SLAB_BUFFER_SIZE = 10 * 1024 * 1024;
43

    
44

    
45
var debug;
46
if (process.env.NODE_DEBUG && /tls/.test(process.env.NODE_DEBUG)) {
47
  debug = function(a) { console.error('TLS:', a); };
48
} else {
49
  debug = function() { };
50
}
51

    
52

    
53
var Connection = null;
54
try {
55
  Connection = process.binding('crypto').Connection;
56
} catch (e) {
57
  throw new Error('node.js not compiled with openssl crypto support.');
58
}
59

    
60
// Convert protocols array into valid OpenSSL protocols list
61
// ("\x06spdy/2\x08http/1.1\x08http/1.0")
62
function convertNPNProtocols(NPNProtocols, out) {
63
  // If NPNProtocols is Array - translate it into buffer
64
  if (Array.isArray(NPNProtocols)) {
65
    var buff = new Buffer(NPNProtocols.reduce(function(p, c) {
66
      return p + 1 + Buffer.byteLength(c);
67
    }, 0));
68

    
69
    NPNProtocols.reduce(function(offset, c) {
70
      var clen = Buffer.byteLength(c);
71
      buff[offset] = clen;
72
      buff.write(c, offset + 1);
73

    
74
      return offset + 1 + clen;
75
    }, 0);
76

    
77
    NPNProtocols = buff;
78
  }
79

    
80
  // If it's already a Buffer - store it
81
  if (Buffer.isBuffer(NPNProtocols)) {
82
    out.NPNProtocols = NPNProtocols;
83
  }
84
}
85

    
86

    
87
function checkServerIdentity(host, cert) {
88
  // Create regexp to much hostnames
89
  function regexpify(host, wildcards) {
90
    // Add trailing dot (make hostnames uniform)
91
    if (!/\.$/.test(host)) host += '.';
92

    
93
    // The same applies to hostname with more than one wildcard,
94
    // if hostname has wildcard when wildcards are not allowed,
95
    // or if there are less than two dots after wildcard (i.e. *.com or *d.com)
96
    //
97
    // also
98
    //
99
    // "The client SHOULD NOT attempt to match a presented identifier in
100
    // which the wildcard character comprises a label other than the
101
    // left-most label (e.g., do not match bar.*.example.net)."
102
    // RFC6125
103
    if (!wildcards && /\*/.test(host) || /[\.\*].*\*/.test(host) ||
104
        /\*/.test(host) && !/\*.*\..+\..+/.test(host)) {
105
      return /$./;
106
    }
107

    
108
    // Replace wildcard chars with regexp's wildcard and
109
    // escape all characters that have special meaning in regexps
110
    // (i.e. '.', '[', '{', '*', and others)
111
    var re = host.replace(
112
        /\*([a-z0-9\\-_\.])|[\.,\-\\\^\$+?*\[\]\(\):!\|{}]/g,
113
        function(all, sub) {
114
          if (sub) return '[a-z0-9\\-_]*' + (sub === '-' ? '\\-' : sub);
115
          return '\\' + all;
116
        });
117

    
118
    return new RegExp('^' + re + '$', 'i');
119
  }
120

    
121
  var dnsNames = [],
122
      uriNames = [],
123
      ips = [],
124
      matchCN = true,
125
      valid = false;
126

    
127
  // There're several names to perform check against:
128
  // CN and altnames in certificate extension
129
  // (DNS names, IP addresses, and URIs)
130
  //
131
  // Walk through altnames and generate lists of those names
132
  if (cert.subjectaltname) {
133
    matchCN = false;
134
    cert.subjectaltname.split(/, /g).forEach(function(altname) {
135
      if (/^DNS:/.test(altname)) {
136
        dnsNames.push(altname.slice(4));
137
      } else if (/^IP Address:/.test(altname)) {
138
        ips.push(altname.slice(11));
139
      } else if (/^URI:/.test(altname)) {
140
        var uri = url.parse(altname.slice(4));
141
        if (uri) uriNames.push(uri.hostname);
142
      }
143
    });
144
  }
145

    
146
  // If hostname is an IP address, it should be present in the list of IP
147
  // addresses.
148
  if (net.isIP(host)) {
149
    valid = ips.some(function(ip) {
150
      return ip === host;
151
    });
152
  } else {
153
    // Transform hostname to canonical form
154
    if (!/\.$/.test(host)) host += '.';
155

    
156
    // Otherwise check all DNS/URI records from certificate
157
    // (with allowed wildcards)
158
    dnsNames = dnsNames.map(function(name) {
159
      return regexpify(name, true);
160
    });
161

    
162
    // Wildcards ain't allowed in URI names
163
    uriNames = uriNames.map(function(name) {
164
      return regexpify(name, false);
165
    });
166

    
167
    dnsNames = dnsNames.concat(uriNames);
168

    
169
    if (dnsNames.length > 0) matchCN = false;
170

    
171
    // Match against Common Name (CN) only if altnames are not present.
172
    //
173
    // "As noted, a client MUST NOT seek a match for a reference identifier
174
    //  of CN-ID if the presented identifiers include a DNS-ID, SRV-ID,
175
    //  URI-ID, or any application-specific identifier types supported by the
176
    //  client."
177
    // RFC6125
178
    if (matchCN) {
179
      var commonNames = cert.subject.CN;
180
      if (Array.isArray(commonNames)) {
181
        for (var i = 0, k = commonNames.length; i < k; ++i) {
182
          dnsNames.push(regexpify(commonNames[i], true));
183
        }
184
      } else {
185
        dnsNames.push(regexpify(commonNames, true));
186
      }
187
    }
188

    
189
    valid = dnsNames.some(function(re) {
190
      return re.test(host);
191
    });
192
  }
193

    
194
  return valid;
195
}
196
exports.checkServerIdentity = checkServerIdentity;
197

    
198

    
199
function SlabBuffer() {
200
  this.create();
201
}
202

    
203

    
204
SlabBuffer.prototype.create = function create() {
205
  this.isFull = false;
206
  this.pool = new Buffer(exports.SLAB_BUFFER_SIZE);
207
  this.offset = 0;
208
  this.remaining = this.pool.length;
209
};
210

    
211

    
212
SlabBuffer.prototype.use = function use(context, fn) {
213
  if (this.remaining === 0) {
214
    this.isFull = true;
215
    return 0;
216
  }
217

    
218
  var bytes = fn.call(context, this.pool, this.offset, this.remaining);
219

    
220
  if (bytes > 0) {
221
    this.offset += bytes;
222
    this.remaining -= bytes;
223
  }
224

    
225
  assert(this.remaining >= 0);
226

    
227
  return bytes;
228
};
229

    
230

    
231
var slabBuffer = null;
232

    
233

    
234
// Base class of both CleartextStream and EncryptedStream
235
function CryptoStream(pair) {
236
  Stream.call(this);
237

    
238
  this.pair = pair;
239

    
240
  this.readable = this.writable = true;
241

    
242
  this._paused = false;
243
  this._needDrain = false;
244
  this._pending = [];
245
  this._pendingCallbacks = [];
246
  this._pendingBytes = 0;
247
  if (slabBuffer === null) slabBuffer = new SlabBuffer();
248
  this._buffer = slabBuffer;
249
}
250
util.inherits(CryptoStream, Stream);
251

    
252

    
253
CryptoStream.prototype.write = function(data /* , encoding, cb */) {
254
  if (this == this.pair.cleartext) {
255
    debug('cleartext.write called with ' + data.length + ' bytes');
256
  } else {
257
    debug('encrypted.write called with ' + data.length + ' bytes');
258
  }
259

    
260
  if (!this.writable) {
261
    throw new Error('CryptoStream is not writable');
262
  }
263

    
264
  var encoding, cb;
265

    
266
  // parse arguments
267
  if (typeof arguments[1] == 'string') {
268
    encoding = arguments[1];
269
    cb = arguments[2];
270
  } else {
271
    cb = arguments[1];
272
  }
273

    
274

    
275
  // Transform strings into buffers.
276
  if (typeof data == 'string') {
277
    data = new Buffer(data, encoding);
278
  }
279

    
280
  debug((this === this.pair.cleartext ? 'clear' : 'encrypted') + 'In data');
281

    
282
  this._pending.push(data);
283
  this._pendingCallbacks.push(cb);
284
  this._pendingBytes += data.length;
285

    
286
  this.pair._writeCalled = true;
287
  this.pair.cycle();
288

    
289
  // In the following cases, write() should return a false,
290
  // then this stream should eventually emit 'drain' event.
291
  //
292
  // 1. There are pending data more than 128k bytes.
293
  // 2. A forward stream shown below is paused.
294
  //    A) EncryptedStream for CleartextStream.write().
295
  //    B) CleartextStream for EncryptedStream.write().
296
  //
297
  if (!this._needDrain) {
298
    if (this._pendingBytes >= 128 * 1024) {
299
      this._needDrain = true;
300
    } else {
301
      if (this === this.pair.cleartext) {
302
        this._needDrain = this.pair.encrypted._paused;
303
      } else {
304
        this._needDrain = this.pair.cleartext._paused;
305
      }
306
    }
307
  }
308
  return !this._needDrain;
309
};
310

    
311

    
312
CryptoStream.prototype.pause = function() {
313
  debug('paused ' + (this == this.pair.cleartext ? 'cleartext' : 'encrypted'));
314
  this._paused = true;
315
};
316

    
317

    
318
CryptoStream.prototype.resume = function() {
319
  debug('resume ' + (this == this.pair.cleartext ? 'cleartext' : 'encrypted'));
320
  this._paused = false;
321
  this.pair.cycle();
322
};
323

    
324

    
325
CryptoStream.prototype.setTimeout = function(timeout, callback) {
326
  if (this.socket) this.socket.setTimeout(timeout, callback);
327
};
328

    
329

    
330
CryptoStream.prototype.setNoDelay = function(noDelay) {
331
  if (this.socket) this.socket.setNoDelay(noDelay);
332
};
333

    
334

    
335
CryptoStream.prototype.setKeepAlive = function(enable, initialDelay) {
336
  if (this.socket) this.socket.setKeepAlive(enable, initialDelay);
337
};
338

    
339
CryptoStream.prototype.__defineGetter__('bytesWritten', function() {
340
  return this.socket ? this.socket.bytesWritten : 0;
341
});
342

    
343
CryptoStream.prototype.setEncoding = function(encoding) {
344
  var StringDecoder = require('string_decoder').StringDecoder; // lazy load
345
  this._decoder = new StringDecoder(encoding);
346
};
347

    
348

    
349
// Example:
350
// C=US\nST=CA\nL=SF\nO=Joyent\nOU=Node.js\nCN=ca1\nemailAddress=ry@clouds.org
351
function parseCertString(s) {
352
  var out = {};
353
  var parts = s.split('\n');
354
  for (var i = 0, len = parts.length; i < len; i++) {
355
    var sepIndex = parts[i].indexOf('=');
356
    if (sepIndex > 0) {
357
      var key = parts[i].slice(0, sepIndex);
358
      var value = parts[i].slice(sepIndex + 1);
359
      if (key in out) {
360
        if (!Array.isArray(out[key])) {
361
          out[key] = [out[key]];
362
        }
363
        out[key].push(value);
364
      } else {
365
        out[key] = value;
366
      }
367
    }
368
  }
369
  return out;
370
}
371

    
372

    
373
CryptoStream.prototype.getPeerCertificate = function() {
374
  if (this.pair.ssl) {
375
    var c = this.pair.ssl.getPeerCertificate();
376

    
377
    if (c) {
378
      if (c.issuer) c.issuer = parseCertString(c.issuer);
379
      if (c.subject) c.subject = parseCertString(c.subject);
380
      return c;
381
    }
382
  }
383

    
384
  return null;
385
};
386

    
387
CryptoStream.prototype.getSession = function() {
388
  if (this.pair.ssl) {
389
    return this.pair.ssl.getSession();
390
  }
391

    
392
  return null;
393
};
394

    
395
CryptoStream.prototype.isSessionReused = function() {
396
  if (this.pair.ssl) {
397
    return this.pair.ssl.isSessionReused();
398
  }
399

    
400
  return null;
401
};
402

    
403
CryptoStream.prototype.getCipher = function(err) {
404
  if (this.pair.ssl) {
405
    return this.pair.ssl.getCurrentCipher();
406
  } else {
407
    return null;
408
  }
409
};
410

    
411

    
412
CryptoStream.prototype.end = function(d) {
413
  if (this.pair._doneFlag) return;
414
  if (!this.writable) return;
415

    
416
  if (d) {
417
    this.write(d);
418
  }
419

    
420
  this._pending.push(END_OF_FILE);
421
  this._pendingCallbacks.push(null);
422

    
423
  // If this is an encrypted stream then we need to disable further 'data'
424
  // events.
425

    
426
  this.writable = false;
427

    
428
  this.pair.cycle();
429
};
430

    
431

    
432
CryptoStream.prototype.destroySoon = function(err) {
433
  if (this.writable) {
434
    this.end();
435
  } else {
436
    this.destroy();
437
  }
438
};
439

    
440

    
441
CryptoStream.prototype.destroy = function(err) {
442
  if (this.pair._doneFlag) return;
443
  this.pair.destroy();
444
};
445

    
446

    
447
CryptoStream.prototype._done = function() {
448
  this._doneFlag = true;
449

    
450
  if (this.pair.cleartext._doneFlag &&
451
      this.pair.encrypted._doneFlag &&
452
      !this.pair._doneFlag) {
453
    // If both streams are done:
454
    if (!this.pair._secureEstablished) {
455
      this.pair.error();
456
    } else {
457
      this.pair.destroy();
458
    }
459
  }
460
};
461

    
462

    
463
// readyState is deprecated. Don't use it.
464
Object.defineProperty(CryptoStream.prototype, 'readyState', {
465
  get: function() {
466
    if (this._connecting) {
467
      return 'opening';
468
    } else if (this.readable && this.writable) {
469
      return 'open';
470
    } else if (this.readable && !this.writable) {
471
      return 'readOnly';
472
    } else if (!this.readable && this.writable) {
473
      return 'writeOnly';
474
    } else {
475
      return 'closed';
476
    }
477
  }
478
});
479

    
480

    
481
// Move decrypted, clear data out into the application.
482
// From the user's perspective this occurs as a 'data' event
483
// on the pair.cleartext.
484
// also
485
// Move encrypted data to the stream. From the user's perspective this
486
// occurs as a 'data' event on the pair.encrypted. Usually the application
487
// will have some code which pipes the stream to a socket:
488
//
489
//   pair.encrypted.on('data', function (d) {
490
//     socket.write(d);
491
//   });
492
//
493
CryptoStream.prototype._push = function() {
494
  if (this == this.pair.encrypted && !this.writable) {
495
    // If the encrypted side got EOF, we do not attempt
496
    // to write out data anymore.
497
    return;
498
  }
499

    
500
  while (!this._paused) {
501
    var chunkBytes = 0,
502
        bytesRead = 0,
503
        start = this._buffer.offset;
504

    
505
    do {
506
      chunkBytes = this._buffer.use(this, this._pusher);
507
      if (chunkBytes > 0) bytesRead += chunkBytes;
508

    
509
      if (this.pair.ssl && this.pair.ssl.error) {
510
        this.pair.error();
511
        return;
512
      }
513

    
514
      this.pair.maybeInitFinished();
515

    
516
    } while (chunkBytes > 0 && !this._buffer.isFull);
517

    
518
    var pool = this._buffer.pool;
519

    
520
    // Create new buffer if previous was filled up
521
    if (this._buffer.isFull) this._buffer.create();
522

    
523
    assert(bytesRead >= 0);
524

    
525
    // Bail out if we didn't read any data.
526
    if (bytesRead == 0) {
527
      if (this._internallyPendingBytes() == 0 && this._destroyAfterPush) {
528
        this._done();
529
      }
530
      return;
531
    }
532

    
533
    var chunk = pool.slice(start, start + bytesRead);
534

    
535
    if (this === this.pair.cleartext) {
536
      debug('cleartext emit "data" with ' + bytesRead + ' bytes');
537
    } else {
538
      debug('encrypted emit "data" with ' + bytesRead + ' bytes');
539
    }
540

    
541
    if (this._decoder) {
542
      var string = this._decoder.write(chunk);
543
      if (string.length) this.emit('data', string);
544
    } else {
545
      this.emit('data', chunk);
546
    }
547

    
548
    // Optimization: emit the original buffer with end points
549
    if (this.ondata) this.ondata(pool, start, start + bytesRead);
550
  }
551
};
552

    
553

    
554
// Push in any clear data coming from the application.
555
// This arrives via some code like this:
556
//
557
//   pair.cleartext.write("hello world");
558
//
559
// also
560
//
561
// Push in incoming encrypted data from the socket.
562
// This arrives via some code like this:
563
//
564
//   socket.on('data', function (d) {
565
//     pair.encrypted.write(d)
566
//   });
567
//
568
CryptoStream.prototype._pull = function() {
569
  var havePending = this._pending.length > 0;
570

    
571
  assert(havePending || this._pendingBytes == 0);
572

    
573
  while (this._pending.length > 0) {
574
    if (!this.pair.ssl) break;
575

    
576
    var tmp = this._pending.shift();
577
    var cb = this._pendingCallbacks.shift();
578

    
579
    assert(this._pending.length === this._pendingCallbacks.length);
580

    
581
    if (tmp === END_OF_FILE) {
582
      // Sending EOF
583
      if (this === this.pair.encrypted) {
584
        debug('end encrypted ' + this.pair.fd);
585
        this.pair.cleartext._destroyAfterPush = true;
586
      } else {
587
        // CleartextStream
588
        assert(this === this.pair.cleartext);
589
        debug('end cleartext');
590

    
591
        this.pair.ssl.shutdown();
592

    
593
        // TODO check if we get EAGAIN From shutdown, would have to do it
594
        // again. should unshift END_OF_FILE back onto pending and wait for
595
        // next cycle.
596

    
597
        this.pair.encrypted._destroyAfterPush = true;
598
      }
599
      this.pair.cycle();
600
      this._done();
601
      return;
602
    }
603

    
604
    if (tmp.length == 0) continue;
605

    
606
    var rv = this._puller(tmp);
607

    
608
    if (this.pair.ssl && this.pair.ssl.error) {
609
      this.pair.error();
610
      return;
611
    }
612

    
613
    this.pair.maybeInitFinished();
614

    
615
    if (rv === 0 || rv < 0) {
616
      this._pending.unshift(tmp);
617
      this._pendingCallbacks.unshift(cb);
618
      break;
619
    }
620

    
621
    this._pendingBytes -= tmp.length;
622
    assert(this._pendingBytes >= 0);
623

    
624
    if (cb) cb();
625

    
626
    assert(rv === tmp.length);
627
  }
628

    
629
  // If pending data has cleared, 'drain' event should be emitted
630
  // after write() returns a false.
631
  // Except when a forward stream shown below is paused.
632
  //   A) EncryptedStream for CleartextStream._pull().
633
  //   B) CleartextStream for EncryptedStream._pull().
634
  //
635
  if (this._needDrain && this._pending.length === 0) {
636
    var paused;
637
    if (this === this.pair.cleartext) {
638
      paused = this.pair.encrypted._paused;
639
    } else {
640
      paused = this.pair.cleartext._paused;
641
    }
642
    if (!paused) {
643
      debug('drain ' + (this === this.pair.cleartext ? 'clear' : 'encrypted'));
644
      var self = this;
645
      process.nextTick(function() {
646
        self.emit('drain');
647
      });
648
      this._needDrain = false;
649
      if (this.__destroyOnDrain) this.end();
650
    }
651
  }
652
};
653

    
654

    
655
function CleartextStream(pair) {
656
  CryptoStream.call(this, pair);
657
}
658
util.inherits(CleartextStream, CryptoStream);
659

    
660

    
661
CleartextStream.prototype._internallyPendingBytes = function() {
662
  if (this.pair.ssl) {
663
    return this.pair.ssl.clearPending();
664
  } else {
665
    return 0;
666
  }
667
};
668

    
669

    
670
CleartextStream.prototype._puller = function(b) {
671
  debug('clearIn ' + b.length + ' bytes');
672
  return this.pair.ssl.clearIn(b, 0, b.length);
673
};
674

    
675

    
676
CleartextStream.prototype._pusher = function(pool, offset, length) {
677
  debug('reading from clearOut');
678
  if (!this.pair.ssl) return -1;
679
  return this.pair.ssl.clearOut(pool, offset, length);
680
};
681

    
682
CleartextStream.prototype.address = function() {
683
  return this.socket && this.socket.address();
684
};
685

    
686
CleartextStream.prototype.__defineGetter__('remoteAddress', function() {
687
  return this.socket && this.socket.remoteAddress;
688
});
689

    
690

    
691
CleartextStream.prototype.__defineGetter__('remotePort', function() {
692
  return this.socket && this.socket.remotePort;
693
});
694

    
695
function EncryptedStream(pair) {
696
  CryptoStream.call(this, pair);
697
}
698
util.inherits(EncryptedStream, CryptoStream);
699

    
700

    
701
EncryptedStream.prototype._internallyPendingBytes = function() {
702
  if (this.pair.ssl) {
703
    return this.pair.ssl.encPending();
704
  } else {
705
    return 0;
706
  }
707
};
708

    
709

    
710
EncryptedStream.prototype._puller = function(b) {
711
  debug('writing from encIn');
712
  return this.pair.ssl.encIn(b, 0, b.length);
713
};
714

    
715

    
716
EncryptedStream.prototype._pusher = function(pool, offset, length) {
717
  debug('reading from encOut');
718
  if (!this.pair.ssl) return -1;
719
  return this.pair.ssl.encOut(pool, offset, length);
720
};
721

    
722

    
723
function onhandshakestart() {
724
  debug('onhandshakestart');
725

    
726
  var self = this;
727
  var ssl = self.ssl;
728
  var now = Date.now();
729

    
730
  assert(now >= ssl.lastHandshakeTime);
731

    
732
  if ((now - ssl.lastHandshakeTime) >= exports.CLIENT_RENEG_WINDOW * 1000) {
733
    ssl.handshakes = 0;
734
  }
735

    
736
  var first = (ssl.lastHandshakeTime === 0);
737
  ssl.lastHandshakeTime = now;
738
  if (first) return;
739

    
740
  if (++ssl.handshakes > exports.CLIENT_RENEG_LIMIT) {
741
    // Defer the error event to the next tick. We're being called from OpenSSL's
742
    // state machine and OpenSSL is not re-entrant. We cannot allow the user's
743
    // callback to destroy the connection right now, it would crash and burn.
744
    setImmediate(function() {
745
      var err = new Error('TLS session renegotiation attack detected.');
746
      if (self.cleartext) self.cleartext.emit('error', err);
747
    });
748
  }
749
}
750

    
751

    
752
function onhandshakedone() {
753
  // for future use
754
  debug('onhandshakedone');
755
}
756

    
757
function onclienthello(hello) {
758
  var self = this,
759
      once = false;
760

    
761
  this.encrypted.pause();
762
  this.cleartext.pause();
763
  function callback(err, session) {
764
    if (once) return;
765
    once = true;
766

    
767
    if (err) return self.socket.destroy(err);
768

    
769
    self.ssl.loadSession(session);
770

    
771
    self.encrypted.resume();
772
    self.cleartext.resume();
773
  }
774

    
775
  if (hello.sessionId.length <= 0 ||
776
      !this.server ||
777
      !this.server.emit('resumeSession', hello.sessionId, callback)) {
778
    callback(null, null);
779
  }
780
}
781

    
782

    
783
function onnewsession(key, session) {
784
  if (!this.server) return;
785
  this.server.emit('newSession', key, session);
786
}
787

    
788

    
789
/**
790
 * Provides a pair of streams to do encrypted communication.
791
 */
792

    
793
function SecurePair(credentials, isServer, requestCert, rejectUnauthorized,
794
                    options) {
795
  if (!(this instanceof SecurePair)) {
796
    return new SecurePair(credentials,
797
                          isServer,
798
                          requestCert,
799
                          rejectUnauthorized,
800
                          options);
801
  }
802

    
803
  var self = this;
804

    
805
  options || (options = {});
806

    
807
  events.EventEmitter.call(this);
808

    
809
  this.server = options.server;
810
  this._secureEstablished = false;
811
  this._isServer = isServer ? true : false;
812
  this._encWriteState = true;
813
  this._clearWriteState = true;
814
  this._doneFlag = false;
815

    
816
  if (!credentials) {
817
    this.credentials = crypto.createCredentials();
818
  } else {
819
    this.credentials = credentials;
820
  }
821

    
822
  if (!this._isServer) {
823
    // For clients, we will always have either a given ca list or be using
824
    // default one
825
    requestCert = true;
826
  }
827

    
828
  this._rejectUnauthorized = rejectUnauthorized ? true : false;
829
  this._requestCert = requestCert ? true : false;
830

    
831
  this.ssl = new Connection(this.credentials.context,
832
                            this._isServer ? true : false,
833
                            this._isServer ? this._requestCert :
834
                                             options.servername,
835
                            this._rejectUnauthorized);
836

    
837
  if (this._isServer) {
838
    this.ssl.onhandshakestart = onhandshakestart.bind(this);
839
    this.ssl.onhandshakedone = onhandshakedone.bind(this);
840
    this.ssl.onclienthello = onclienthello.bind(this);
841
    this.ssl.onnewsession = onnewsession.bind(this);
842
    this.ssl.lastHandshakeTime = 0;
843
    this.ssl.handshakes = 0;
844
  }
845

    
846
  if (process.features.tls_sni) {
847
    if (this._isServer && options.SNICallback) {
848
      this.ssl.setSNICallback(options.SNICallback);
849
    }
850
    this.servername = null;
851
  }
852

    
853
  if (process.features.tls_npn && options.NPNProtocols) {
854
    this.ssl.setNPNProtocols(options.NPNProtocols);
855
    this.npnProtocol = null;
856
  }
857

    
858
  /* Acts as a r/w stream to the cleartext side of the stream. */
859
  this.cleartext = new CleartextStream(this);
860

    
861
  /* Acts as a r/w stream to the encrypted side of the stream. */
862
  this.encrypted = new EncryptedStream(this);
863

    
864
  process.nextTick(function() {
865
    /* The Connection may be destroyed by an abort call */
866
    if (self.ssl) {
867
      self.ssl.start();
868
    }
869
    self.cycle();
870
  });
871
}
872

    
873
util.inherits(SecurePair, events.EventEmitter);
874

    
875

    
876
exports.createSecurePair = function(credentials,
877
                                    isServer,
878
                                    requestCert,
879
                                    rejectUnauthorized) {
880
  var pair = new SecurePair(credentials,
881
                            isServer,
882
                            requestCert,
883
                            rejectUnauthorized);
884
  return pair;
885
};
886

    
887

    
888

    
889

    
890
/* Attempt to cycle OpenSSLs buffers in various directions.
891
 *
892
 * An SSL Connection can be viewed as four separate piplines,
893
 * interacting with one has no connection to the behavoir of
894
 * any of the other 3 -- This might not sound reasonable,
895
 * but consider things like mid-stream renegotiation of
896
 * the ciphers.
897
 *
898
 * The four pipelines, using terminology of the client (server is just
899
 * reversed):
900
 *  (1) Encrypted Output stream (Writing encrypted data to peer)
901
 *  (2) Encrypted Input stream (Reading encrypted data from peer)
902
 *  (3) Cleartext Output stream (Decrypted content from the peer)
903
 *  (4) Cleartext Input stream (Cleartext content to send to the peer)
904
 *
905
 * This function attempts to pull any available data out of the Cleartext
906
 * input stream (4), and the Encrypted input stream (2).  Then it pushes any
907
 * data available from the cleartext output stream (3), and finally from the
908
 * Encrypted output stream (1)
909
 *
910
 * It is called whenever we do something with OpenSSL -- post reciving
911
 * content, trying to flush, trying to change ciphers, or shutting down the
912
 * connection.
913
 *
914
 * Because it is also called everywhere, we also check if the connection has
915
 * completed negotiation and emit 'secure' from here if it has.
916
 */
917
SecurePair.prototype.cycle = function(depth) {
918
  if (this._doneFlag) return;
919

    
920
  depth = depth ? depth : 0;
921

    
922
  if (depth == 0) this._writeCalled = false;
923

    
924
  var established = this._secureEstablished;
925

    
926
  if (!this.cycleEncryptedPullLock) {
927
    this.cycleEncryptedPullLock = true;
928
    debug('encrypted._pull');
929
    this.encrypted._pull();
930
    this.cycleEncryptedPullLock = false;
931
  }
932

    
933
  if (!this.cycleCleartextPullLock) {
934
    this.cycleCleartextPullLock = true;
935
    debug('cleartext._pull');
936
    this.cleartext._pull();
937
    this.cycleCleartextPullLock = false;
938
  }
939

    
940
  if (!this.cycleCleartextPushLock) {
941
    this.cycleCleartextPushLock = true;
942
    debug('cleartext._push');
943
    this.cleartext._push();
944
    this.cycleCleartextPushLock = false;
945
  }
946

    
947
  if (!this.cycleEncryptedPushLock) {
948
    this.cycleEncryptedPushLock = true;
949
    debug('encrypted._push');
950
    this.encrypted._push();
951
    this.cycleEncryptedPushLock = false;
952
  }
953

    
954
  if ((!established && this._secureEstablished) ||
955
      (depth == 0 && this._writeCalled)) {
956
    // If we were not established but now we are, let's cycle again.
957
    // Or if there is some data to write...
958
    this.cycle(depth + 1);
959
  }
960
};
961

    
962

    
963
SecurePair.prototype.maybeInitFinished = function() {
964
  if (this.ssl && !this._secureEstablished && this.ssl.isInitFinished()) {
965
    if (process.features.tls_npn) {
966
      this.npnProtocol = this.ssl.getNegotiatedProtocol();
967
    }
968

    
969
    if (process.features.tls_sni) {
970
      this.servername = this.ssl.getServername();
971
    }
972

    
973
    this._secureEstablished = true;
974
    debug('secure established');
975
    this.emit('secure');
976
  }
977
};
978

    
979

    
980
SecurePair.prototype.destroy = function() {
981
  var self = this;
982

    
983
  if (!this._doneFlag) {
984
    this._doneFlag = true;
985
    this.ssl.error = null;
986
    this.ssl.close();
987
    this.ssl = null;
988

    
989
    self.encrypted.writable = self.encrypted.readable = false;
990
    self.cleartext.writable = self.cleartext.readable = false;
991

    
992
    process.nextTick(function() {
993
      if (self.cleartext._decoder) {
994
        var ret = self.cleartext._decoder.end();
995
        if (ret)
996
          self.cleartext.emit('data', ret);
997
      }
998
      self.cleartext.emit('end');
999
      self.encrypted.emit('close');
1000
      self.cleartext.emit('close');
1001
    });
1002
  }
1003
};
1004

    
1005

    
1006
SecurePair.prototype.error = function() {
1007
  if (!this._secureEstablished) {
1008
    var error = this.ssl.error;
1009
    if (!error) {
1010
      error = new Error('socket hang up');
1011
      error.code = 'ECONNRESET';
1012
    }
1013
    this.destroy();
1014
    this.emit('error', error);
1015
  } else {
1016
    var err = this.ssl.error;
1017
    this.ssl.error = null;
1018

    
1019
    if (this._isServer &&
1020
        this._rejectUnauthorized &&
1021
        /peer did not return a certificate/.test(err.message)) {
1022
      // Not really an error.
1023
      this.destroy();
1024
    } else {
1025
      this.cleartext.emit('error', err);
1026
    }
1027
  }
1028
};
1029

    
1030
// TODO: support anonymous (nocert) and PSK
1031

    
1032

    
1033
// AUTHENTICATION MODES
1034
//
1035
// There are several levels of authentication that TLS/SSL supports.
1036
// Read more about this in "man SSL_set_verify".
1037
//
1038
// 1. The server sends a certificate to the client but does not request a
1039
// cert from the client. This is common for most HTTPS servers. The browser
1040
// can verify the identity of the server, but the server does not know who
1041
// the client is. Authenticating the client is usually done over HTTP using
1042
// login boxes and cookies and stuff.
1043
//
1044
// 2. The server sends a cert to the client and requests that the client
1045
// also send it a cert. The client knows who the server is and the server is
1046
// requesting the client also identify themselves. There are several
1047
// outcomes:
1048
//
1049
//   A) verifyError returns null meaning the client's certificate is signed
1050
//   by one of the server's CAs. The server know's the client idenity now
1051
//   and the client is authorized.
1052
//
1053
//   B) For some reason the client's certificate is not acceptable -
1054
//   verifyError returns a string indicating the problem. The server can
1055
//   either (i) reject the client or (ii) allow the client to connect as an
1056
//   unauthorized connection.
1057
//
1058
// The mode is controlled by two boolean variables.
1059
//
1060
// requestCert
1061
//   If true the server requests a certificate from client connections. For
1062
//   the common HTTPS case, users will want this to be false, which is what
1063
//   it defaults to.
1064
//
1065
// rejectUnauthorized
1066
//   If true clients whose certificates are invalid for any reason will not
1067
//   be allowed to make connections. If false, they will simply be marked as
1068
//   unauthorized but secure communication will continue. By default this is
1069
//   true.
1070
//
1071
//
1072
//
1073
// Options:
1074
// - requestCert. Send verify request. Default to false.
1075
// - rejectUnauthorized. Boolean, default to true.
1076
// - key. string.
1077
// - cert: string.
1078
// - ca: string or array of strings.
1079
//
1080
// emit 'secureConnection'
1081
//   function (cleartextStream, encryptedStream) { }
1082
//
1083
//   'cleartextStream' has the boolean property 'authorized' to determine if
1084
//   it was verified by the CA. If 'authorized' is false, a property
1085
//   'authorizationError' is set on cleartextStream and has the possible
1086
//   values:
1087
//
1088
//   "UNABLE_TO_GET_ISSUER_CERT", "UNABLE_TO_GET_CRL",
1089
//   "UNABLE_TO_DECRYPT_CERT_SIGNATURE", "UNABLE_TO_DECRYPT_CRL_SIGNATURE",
1090
//   "UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY", "CERT_SIGNATURE_FAILURE",
1091
//   "CRL_SIGNATURE_FAILURE", "CERT_NOT_YET_VALID" "CERT_HAS_EXPIRED",
1092
//   "CRL_NOT_YET_VALID", "CRL_HAS_EXPIRED" "ERROR_IN_CERT_NOT_BEFORE_FIELD",
1093
//   "ERROR_IN_CERT_NOT_AFTER_FIELD", "ERROR_IN_CRL_LAST_UPDATE_FIELD",
1094
//   "ERROR_IN_CRL_NEXT_UPDATE_FIELD", "OUT_OF_MEM",
1095
//   "DEPTH_ZERO_SELF_SIGNED_CERT", "SELF_SIGNED_CERT_IN_CHAIN",
1096
//   "UNABLE_TO_GET_ISSUER_CERT_LOCALLY", "UNABLE_TO_VERIFY_LEAF_SIGNATURE",
1097
//   "CERT_CHAIN_TOO_LONG", "CERT_REVOKED" "INVALID_CA",
1098
//   "PATH_LENGTH_EXCEEDED", "INVALID_PURPOSE" "CERT_UNTRUSTED",
1099
//   "CERT_REJECTED"
1100
//
1101
//
1102
// TODO:
1103
// cleartext.credentials (by mirroring from pair object)
1104
// cleartext.getCertificate() (by mirroring from pair.credentials.context)
1105
function Server(/* [options], listener */) {
1106
  var options, listener;
1107
  if (typeof arguments[0] == 'object') {
1108
    options = arguments[0];
1109
    listener = arguments[1];
1110
  } else if (typeof arguments[0] == 'function') {
1111
    options = {};
1112
    listener = arguments[0];
1113
  }
1114

    
1115
  if (!(this instanceof Server)) return new Server(options, listener);
1116

    
1117
  this._contexts = [];
1118

    
1119
  var self = this;
1120

    
1121
  // Handle option defaults:
1122
  this.setOptions(options);
1123

    
1124
  if (!self.pfx && (!self.cert || !self.key)) {
1125
    throw new Error('Missing PFX or certificate + private key.');
1126
  }
1127

    
1128
  var sharedCreds = crypto.createCredentials({
1129
    pfx: self.pfx,
1130
    key: self.key,
1131
    passphrase: self.passphrase,
1132
    cert: self.cert,
1133
    ca: self.ca,
1134
    ciphers: self.ciphers || DEFAULT_CIPHERS,
1135
    secureProtocol: self.secureProtocol,
1136
    secureOptions: self.secureOptions,
1137
    crl: self.crl,
1138
    sessionIdContext: self.sessionIdContext
1139
  });
1140

    
1141
  var timeout = options.handshakeTimeout || (120 * 1000);
1142

    
1143
  if (typeof timeout !== 'number') {
1144
    throw new TypeError('handshakeTimeout must be a number');
1145
  }
1146

    
1147
  // constructor call
1148
  net.Server.call(this, function(socket) {
1149
    var creds = crypto.createCredentials(null, sharedCreds.context);
1150

    
1151
    var pair = new SecurePair(creds,
1152
                              true,
1153
                              self.requestCert,
1154
                              self.rejectUnauthorized,
1155
                              {
1156
                                server: self,
1157
                                NPNProtocols: self.NPNProtocols,
1158
                                SNICallback: self.SNICallback
1159
                              });
1160

    
1161
    var cleartext = pipe(pair, socket);
1162
    cleartext._controlReleased = false;
1163

    
1164
    function listener() {
1165
      pair.emit('error', new Error('TLS handshake timeout'));
1166
    }
1167

    
1168
    if (timeout > 0) {
1169
      socket.setTimeout(timeout, listener);
1170
    }
1171

    
1172
    pair.once('secure', function() {
1173
      socket.setTimeout(0, listener);
1174

    
1175
      pair.cleartext.authorized = false;
1176
      pair.cleartext.npnProtocol = pair.npnProtocol;
1177
      pair.cleartext.servername = pair.servername;
1178

    
1179
      if (!self.requestCert) {
1180
        cleartext._controlReleased = true;
1181
        self.emit('secureConnection', pair.cleartext, pair.encrypted);
1182
      } else {
1183
        var verifyError = pair.ssl.verifyError();
1184
        if (verifyError) {
1185
          pair.cleartext.authorizationError = verifyError.message;
1186

    
1187
          if (self.rejectUnauthorized) {
1188
            socket.destroy();
1189
            pair.destroy();
1190
          } else {
1191
            cleartext._controlReleased = true;
1192
            self.emit('secureConnection', pair.cleartext, pair.encrypted);
1193
          }
1194
        } else {
1195
          pair.cleartext.authorized = true;
1196
          cleartext._controlReleased = true;
1197
          self.emit('secureConnection', pair.cleartext, pair.encrypted);
1198
        }
1199
      }
1200
    });
1201
    pair.on('error', function(err) {
1202
      self.emit('clientError', err, this);
1203
    });
1204
  });
1205

    
1206
  if (listener) {
1207
    this.on('secureConnection', listener);
1208
  }
1209
}
1210

    
1211
util.inherits(Server, net.Server);
1212
exports.Server = Server;
1213
exports.createServer = function(options, listener) {
1214
  return new Server(options, listener);
1215
};
1216

    
1217

    
1218
Server.prototype.setOptions = function(options) {
1219
  if (typeof options.requestCert == 'boolean') {
1220
    this.requestCert = options.requestCert;
1221
  } else {
1222
    this.requestCert = false;
1223
  }
1224

    
1225
  if (typeof options.rejectUnauthorized == 'boolean') {
1226
    this.rejectUnauthorized = options.rejectUnauthorized;
1227
  } else {
1228
    this.rejectUnauthorized = false;
1229
  }
1230

    
1231
  if (options.pfx) this.pfx = options.pfx;
1232
  if (options.key) this.key = options.key;
1233
  if (options.passphrase) this.passphrase = options.passphrase;
1234
  if (options.cert) this.cert = options.cert;
1235
  if (options.ca) this.ca = options.ca;
1236
  if (options.secureProtocol) this.secureProtocol = options.secureProtocol;
1237
  if (options.crl) this.crl = options.crl;
1238
  if (options.ciphers) this.ciphers = options.ciphers;
1239
  var secureOptions = options.secureOptions || 0;
1240
  if (options.honorCipherOrder) {
1241
    secureOptions |= constants.SSL_OP_CIPHER_SERVER_PREFERENCE;
1242
  }
1243
  if (secureOptions) this.secureOptions = secureOptions;
1244
  if (options.NPNProtocols) convertNPNProtocols(options.NPNProtocols, this);
1245
  if (options.SNICallback) {
1246
    this.SNICallback = options.SNICallback;
1247
  } else {
1248
    this.SNICallback = this.SNICallback.bind(this);
1249
  }
1250
  if (options.sessionIdContext) {
1251
    this.sessionIdContext = options.sessionIdContext;
1252
  } else if (this.requestCert) {
1253
    this.sessionIdContext = crypto.createHash('md5')
1254
                                  .update(process.argv.join(' '))
1255
                                  .digest('hex');
1256
  }
1257
};
1258

    
1259
// SNI Contexts High-Level API
1260
Server.prototype.addContext = function(servername, credentials) {
1261
  if (!servername) {
1262
    throw 'Servername is required parameter for Server.addContext';
1263
  }
1264

    
1265
  var re = new RegExp('^' +
1266
                      servername.replace(/([\.^$+?\-\\[\]{}])/g, '\\$1')
1267
                                .replace(/\*/g, '.*') +
1268
                      '$');
1269
  this._contexts.push([re, crypto.createCredentials(credentials).context]);
1270
};
1271

    
1272
Server.prototype.SNICallback = function(servername) {
1273
  var ctx;
1274

    
1275
  this._contexts.some(function(elem) {
1276
    if (servername.match(elem[0]) !== null) {
1277
      ctx = elem[1];
1278
      return true;
1279
    }
1280
  });
1281

    
1282
  return ctx;
1283
};
1284

    
1285

    
1286
// Target API:
1287
//
1288
//  var s = tls.connect({port: 8000, host: "google.com"}, function() {
1289
//    if (!s.authorized) {
1290
//      s.destroy();
1291
//      return;
1292
//    }
1293
//
1294
//    // s.socket;
1295
//
1296
//    s.end("hello world\n");
1297
//  });
1298
//
1299
//
1300
function normalizeConnectArgs(listArgs) {
1301
  var args = net._normalizeConnectArgs(listArgs);
1302
  var options = args[0];
1303
  var cb = args[1];
1304

    
1305
  if (typeof listArgs[1] === 'object') {
1306
    options = util._extend(options, listArgs[1]);
1307
  } else if (typeof listArgs[2] === 'object') {
1308
    options = util._extend(options, listArgs[2]);
1309
  }
1310

    
1311
  return (cb) ? [options, cb] : [options];
1312
}
1313

    
1314
exports.connect = function(/* [port, host], options, cb */) {
1315
  var args = normalizeConnectArgs(arguments);
1316
  var options = args[0];
1317
  var cb = args[1];
1318

    
1319
  var defaults = {
1320
    rejectUnauthorized: '0' !== process.env.NODE_TLS_REJECT_UNAUTHORIZED
1321
  };
1322
  options = util._extend(defaults, options || {});
1323

    
1324
  var socket = options.socket ? options.socket : new net.Stream();
1325

    
1326
  var sslcontext = crypto.createCredentials(options);
1327

    
1328
  convertNPNProtocols(options.NPNProtocols, this);
1329
  var hostname = options.servername || options.host || 'localhost',
1330
      pair = new SecurePair(sslcontext, false, true,
1331
                            options.rejectUnauthorized === true ? true : false,
1332
                            {
1333
                              NPNProtocols: this.NPNProtocols,
1334
                              servername: hostname
1335
                            });
1336

    
1337
  if (options.session) {
1338
    var session = options.session;
1339
    if (typeof session === 'string')
1340
      session = new Buffer(session, 'binary');
1341
    pair.ssl.setSession(session);
1342
  }
1343

    
1344
  var cleartext = pipe(pair, socket);
1345
  if (cb) {
1346
    cleartext.once('secureConnect', cb);
1347
  }
1348

    
1349
  if (!options.socket) {
1350
    var connect_opt = (options.path && !options.port) ? {path: options.path} : {
1351
      port: options.port,
1352
      host: options.host,
1353
      localAddress: options.localAddress
1354
    };
1355
    socket.connect(connect_opt);
1356
  }
1357

    
1358
  pair.on('secure', function() {
1359
    var verifyError = pair.ssl.verifyError();
1360

    
1361
    cleartext.npnProtocol = pair.npnProtocol;
1362

    
1363
    // Verify that server's identity matches it's certificate's names
1364
    if (!verifyError) {
1365
      var validCert = checkServerIdentity(hostname,
1366
                                          pair.cleartext.getPeerCertificate());
1367
      if (!validCert) {
1368
        verifyError = new Error('Hostname/IP doesn\'t match certificate\'s ' +
1369
                                'altnames');
1370
      }
1371
    }
1372

    
1373
    if (verifyError) {
1374
      cleartext.authorized = false;
1375
      cleartext.authorizationError = verifyError.message;
1376

    
1377
      if (pair._rejectUnauthorized) {
1378
        cleartext.emit('error', verifyError);
1379
        pair.destroy();
1380
      } else {
1381
        cleartext.emit('secureConnect');
1382
      }
1383
    } else {
1384
      cleartext.authorized = true;
1385
      cleartext.emit('secureConnect');
1386
    }
1387
  });
1388
  pair.on('error', function(err) {
1389
    cleartext.emit('error', err);
1390
  });
1391

    
1392
  cleartext._controlReleased = true;
1393
  return cleartext;
1394
};
1395

    
1396

    
1397
function pipe(pair, socket) {
1398
  pair.encrypted.pipe(socket);
1399
  socket.pipe(pair.encrypted);
1400

    
1401
  pair.fd = socket.fd;
1402
  var cleartext = pair.cleartext;
1403
  cleartext.socket = socket;
1404
  cleartext.encrypted = pair.encrypted;
1405
  cleartext.authorized = false;
1406

    
1407
  function onerror(e) {
1408
    if (cleartext._controlReleased) {
1409
      cleartext.emit('error', e);
1410
    }
1411
  }
1412

    
1413
  function onclose() {
1414
    socket.removeListener('error', onerror);
1415
    socket.removeListener('timeout', ontimeout);
1416
  }
1417

    
1418
  function ontimeout() {
1419
    cleartext.emit('timeout');
1420
  }
1421

    
1422
  socket.on('error', onerror);
1423
  socket.on('close', onclose);
1424
  socket.on('timeout', ontimeout);
1425

    
1426
  return cleartext;
1427
}