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 @ 4108c312

History | View | Annotate | Download (37.5 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 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
//
996
// emit 'secureConnection'
997
//   function (cleartextStream, encryptedStream) { }
998
//
999
//   'cleartextStream' has the boolean property 'authorized' to determine if
1000
//   it was verified by the CA. If 'authorized' is false, a property
1001
//   'authorizationError' is set on cleartextStream and has the possible
1002
//   values:
1003
//
1004
//   "UNABLE_TO_GET_ISSUER_CERT", "UNABLE_TO_GET_CRL",
1005
//   "UNABLE_TO_DECRYPT_CERT_SIGNATURE", "UNABLE_TO_DECRYPT_CRL_SIGNATURE",
1006
//   "UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY", "CERT_SIGNATURE_FAILURE",
1007
//   "CRL_SIGNATURE_FAILURE", "CERT_NOT_YET_VALID" "CERT_HAS_EXPIRED",
1008
//   "CRL_NOT_YET_VALID", "CRL_HAS_EXPIRED" "ERROR_IN_CERT_NOT_BEFORE_FIELD",
1009
//   "ERROR_IN_CERT_NOT_AFTER_FIELD", "ERROR_IN_CRL_LAST_UPDATE_FIELD",
1010
//   "ERROR_IN_CRL_NEXT_UPDATE_FIELD", "OUT_OF_MEM",
1011
//   "DEPTH_ZERO_SELF_SIGNED_CERT", "SELF_SIGNED_CERT_IN_CHAIN",
1012
//   "UNABLE_TO_GET_ISSUER_CERT_LOCALLY", "UNABLE_TO_VERIFY_LEAF_SIGNATURE",
1013
//   "CERT_CHAIN_TOO_LONG", "CERT_REVOKED" "INVALID_CA",
1014
//   "PATH_LENGTH_EXCEEDED", "INVALID_PURPOSE" "CERT_UNTRUSTED",
1015
//   "CERT_REJECTED"
1016
//
1017
//
1018
// TODO:
1019
// cleartext.credentials (by mirroring from pair object)
1020
// cleartext.getCertificate() (by mirroring from pair.credentials.context)
1021
function Server(/* [options], listener */) {
1022
  var options, listener;
1023
  if (typeof arguments[0] == 'object') {
1024
    options = arguments[0];
1025
    listener = arguments[1];
1026
  } else if (typeof arguments[0] == 'function') {
1027
    options = {};
1028
    listener = arguments[0];
1029
  }
1030

    
1031
  if (!(this instanceof Server)) return new Server(options, listener);
1032

    
1033
  this._contexts = [];
1034

    
1035
  var self = this;
1036

    
1037
  // Handle option defaults:
1038
  this.setOptions(options);
1039

    
1040
  if (!self.pfx && (!self.cert || !self.key)) {
1041
    throw new Error('Missing PFX or certificate + private key.');
1042
  }
1043

    
1044
  var sharedCreds = crypto.createCredentials({
1045
    pfx: self.pfx,
1046
    key: self.key,
1047
    passphrase: self.passphrase,
1048
    cert: self.cert,
1049
    ca: self.ca,
1050
    ciphers: self.ciphers || DEFAULT_CIPHERS,
1051
    secureProtocol: self.secureProtocol,
1052
    secureOptions: self.secureOptions,
1053
    crl: self.crl,
1054
    sessionIdContext: self.sessionIdContext
1055
  });
1056

    
1057
  var timeout = options.handshakeTimeout || (120 * 1000);
1058

    
1059
  if (typeof timeout !== 'number') {
1060
    throw new TypeError('handshakeTimeout must be a number');
1061
  }
1062

    
1063
  // constructor call
1064
  net.Server.call(this, function(socket) {
1065
    var creds = crypto.createCredentials(null, sharedCreds.context);
1066

    
1067
    var pair = new SecurePair(creds,
1068
                              true,
1069
                              self.requestCert,
1070
                              self.rejectUnauthorized,
1071
                              {
1072
                                server: self,
1073
                                NPNProtocols: self.NPNProtocols,
1074
                                SNICallback: self.SNICallback,
1075

    
1076
                                // Stream options
1077
                                cleartext: self._cleartext,
1078
                                encrypted: self._encrypted
1079
                              });
1080

    
1081
    var cleartext = pipe(pair, socket);
1082
    cleartext._controlReleased = false;
1083

    
1084
    function listener() {
1085
      pair.emit('error', new Error('TLS handshake timeout'));
1086
    }
1087

    
1088
    if (timeout > 0) {
1089
      socket.setTimeout(timeout, listener);
1090
    }
1091

    
1092
    pair.once('secure', function() {
1093
      socket.setTimeout(0, listener);
1094

    
1095
      pair.cleartext.authorized = false;
1096
      pair.cleartext.npnProtocol = pair.npnProtocol;
1097
      pair.cleartext.servername = pair.servername;
1098

    
1099
      if (!self.requestCert) {
1100
        cleartext._controlReleased = true;
1101
        self.emit('secureConnection', pair.cleartext, pair.encrypted);
1102
      } else {
1103
        var verifyError = pair.ssl.verifyError();
1104
        if (verifyError) {
1105
          pair.cleartext.authorizationError = verifyError.message;
1106

    
1107
          if (self.rejectUnauthorized) {
1108
            socket.destroy();
1109
            pair.destroy();
1110
          } else {
1111
            cleartext._controlReleased = true;
1112
            self.emit('secureConnection', pair.cleartext, pair.encrypted);
1113
          }
1114
        } else {
1115
          pair.cleartext.authorized = true;
1116
          cleartext._controlReleased = true;
1117
          self.emit('secureConnection', pair.cleartext, pair.encrypted);
1118
        }
1119
      }
1120
    });
1121
    pair.on('error', function(err) {
1122
      self.emit('clientError', err, this);
1123
    });
1124
  });
1125

    
1126
  if (listener) {
1127
    this.on('secureConnection', listener);
1128
  }
1129
}
1130

    
1131
util.inherits(Server, net.Server);
1132
exports.Server = Server;
1133
exports.createServer = function(options, listener) {
1134
  return new Server(options, listener);
1135
};
1136

    
1137

    
1138
Server.prototype.setOptions = function(options) {
1139
  if (typeof options.requestCert == 'boolean') {
1140
    this.requestCert = options.requestCert;
1141
  } else {
1142
    this.requestCert = false;
1143
  }
1144

    
1145
  if (typeof options.rejectUnauthorized == 'boolean') {
1146
    this.rejectUnauthorized = options.rejectUnauthorized;
1147
  } else {
1148
    this.rejectUnauthorized = false;
1149
  }
1150

    
1151
  if (options.pfx) this.pfx = options.pfx;
1152
  if (options.key) this.key = options.key;
1153
  if (options.passphrase) this.passphrase = options.passphrase;
1154
  if (options.cert) this.cert = options.cert;
1155
  if (options.ca) this.ca = options.ca;
1156
  if (options.secureProtocol) this.secureProtocol = options.secureProtocol;
1157
  if (options.crl) this.crl = options.crl;
1158
  if (options.ciphers) this.ciphers = options.ciphers;
1159
  var secureOptions = options.secureOptions || 0;
1160
  if (options.honorCipherOrder) {
1161
    secureOptions |= constants.SSL_OP_CIPHER_SERVER_PREFERENCE;
1162
  }
1163
  if (secureOptions) this.secureOptions = secureOptions;
1164
  if (options.NPNProtocols) convertNPNProtocols(options.NPNProtocols, this);
1165
  if (options.SNICallback) {
1166
    this.SNICallback = options.SNICallback;
1167
  } else {
1168
    this.SNICallback = this.SNICallback.bind(this);
1169
  }
1170
  if (options.sessionIdContext) {
1171
    this.sessionIdContext = options.sessionIdContext;
1172
  } else if (this.requestCert) {
1173
    this.sessionIdContext = crypto.createHash('md5')
1174
                                  .update(process.argv.join(' '))
1175
                                  .digest('hex');
1176
  }
1177
  if (options.cleartext) this.cleartext = options.cleartext;
1178
  if (options.encrypted) this.encrypted = options.encrypted;
1179
};
1180

    
1181
// SNI Contexts High-Level API
1182
Server.prototype.addContext = function(servername, credentials) {
1183
  if (!servername) {
1184
    throw 'Servername is required parameter for Server.addContext';
1185
  }
1186

    
1187
  var re = new RegExp('^' +
1188
                      servername.replace(/([\.^$+?\-\\[\]{}])/g, '\\$1')
1189
                                .replace(/\*/g, '.*') +
1190
                      '$');
1191
  this._contexts.push([re, crypto.createCredentials(credentials).context]);
1192
};
1193

    
1194
Server.prototype.SNICallback = function(servername) {
1195
  var ctx;
1196

    
1197
  this._contexts.some(function(elem) {
1198
    if (servername.match(elem[0]) !== null) {
1199
      ctx = elem[1];
1200
      return true;
1201
    }
1202
  });
1203

    
1204
  return ctx;
1205
};
1206

    
1207

    
1208
// Target API:
1209
//
1210
//  var s = tls.connect({port: 8000, host: "google.com"}, function() {
1211
//    if (!s.authorized) {
1212
//      s.destroy();
1213
//      return;
1214
//    }
1215
//
1216
//    // s.socket;
1217
//
1218
//    s.end("hello world\n");
1219
//  });
1220
//
1221
//
1222
function normalizeConnectArgs(listArgs) {
1223
  var args = net._normalizeConnectArgs(listArgs);
1224
  var options = args[0];
1225
  var cb = args[1];
1226

    
1227
  if (typeof listArgs[1] === 'object') {
1228
    options = util._extend(options, listArgs[1]);
1229
  } else if (typeof listArgs[2] === 'object') {
1230
    options = util._extend(options, listArgs[2]);
1231
  }
1232

    
1233
  return (cb) ? [options, cb] : [options];
1234
}
1235

    
1236
exports.connect = function(/* [port, host], options, cb */) {
1237
  var args = normalizeConnectArgs(arguments);
1238
  var options = args[0];
1239
  var cb = args[1];
1240

    
1241
  var defaults = {
1242
    rejectUnauthorized: '0' !== process.env.NODE_TLS_REJECT_UNAUTHORIZED
1243
  };
1244
  options = util._extend(defaults, options || {});
1245

    
1246
  var socket = options.socket ? options.socket : new net.Stream();
1247

    
1248
  var sslcontext = crypto.createCredentials(options);
1249

    
1250
  convertNPNProtocols(options.NPNProtocols, this);
1251
  var hostname = options.servername || options.host || 'localhost',
1252
      pair = new SecurePair(sslcontext, false, true,
1253
                            options.rejectUnauthorized === true ? true : false,
1254
                            {
1255
                              NPNProtocols: this.NPNProtocols,
1256
                              servername: hostname,
1257
                              cleartext: options.cleartext,
1258
                              encrypted: options.encrypted
1259
                            });
1260

    
1261
  if (options.session) {
1262
    var session = options.session;
1263
    if (typeof session === 'string')
1264
      session = new Buffer(session, 'binary');
1265
    pair.ssl.setSession(session);
1266
  }
1267

    
1268
  var cleartext = pipe(pair, socket);
1269
  if (cb) {
1270
    cleartext.once('secureConnect', cb);
1271
  }
1272

    
1273
  if (!options.socket) {
1274
    var connect_opt = (options.path && !options.port) ? {path: options.path} : {
1275
      port: options.port,
1276
      host: options.host,
1277
      localAddress: options.localAddress
1278
    };
1279
    socket.connect(connect_opt);
1280
  }
1281

    
1282
  pair.on('secure', function() {
1283
    var verifyError = pair.ssl.verifyError();
1284

    
1285
    cleartext.npnProtocol = pair.npnProtocol;
1286

    
1287
    // Verify that server's identity matches it's certificate's names
1288
    if (!verifyError) {
1289
      var validCert = checkServerIdentity(hostname,
1290
                                          pair.cleartext.getPeerCertificate());
1291
      if (!validCert) {
1292
        verifyError = new Error('Hostname/IP doesn\'t match certificate\'s ' +
1293
                                'altnames');
1294
      }
1295
    }
1296

    
1297
    if (verifyError) {
1298
      cleartext.authorized = false;
1299
      cleartext.authorizationError = verifyError.message;
1300

    
1301
      if (pair._rejectUnauthorized) {
1302
        cleartext.emit('error', verifyError);
1303
        pair.destroy();
1304
      } else {
1305
        cleartext.emit('secureConnect');
1306
      }
1307
    } else {
1308
      cleartext.authorized = true;
1309
      cleartext.emit('secureConnect');
1310
    }
1311
  });
1312
  pair.on('error', function(err) {
1313
    cleartext.emit('error', err);
1314
  });
1315

    
1316
  cleartext._controlReleased = true;
1317
  return cleartext;
1318
};
1319

    
1320

    
1321
function pipe(pair, socket) {
1322
  pair.encrypted.pipe(socket);
1323
  socket.pipe(pair.encrypted);
1324

    
1325
  pair.encrypted.on('close', function() {
1326
    process.nextTick(function() {
1327
      socket.destroy();
1328
    });
1329
  });
1330

    
1331
  pair.fd = socket.fd;
1332
  var cleartext = pair.cleartext;
1333
  cleartext.socket = socket;
1334
  cleartext.encrypted = pair.encrypted;
1335
  cleartext.authorized = false;
1336

    
1337
  // cycle the data whenever the socket drains, so that
1338
  // we can pull some more into it.  normally this would
1339
  // be handled by the fact that pipe() triggers read() calls
1340
  // on writable.drain, but CryptoStreams are a bit more
1341
  // complicated.  Since the encrypted side actually gets
1342
  // its data from the cleartext side, we have to give it a
1343
  // light kick to get in motion again.
1344
  socket.on('drain', function() {
1345
    if (pair.encrypted._pending)
1346
      pair.encrypted._writePending();
1347
    if (pair.cleartext._pending)
1348
      pair.cleartext._writePending();
1349
    pair.encrypted.read(0);
1350
    pair.cleartext.read(0);
1351
  });
1352

    
1353
  function onerror(e) {
1354
    if (cleartext._controlReleased) {
1355
      cleartext.emit('error', e);
1356
    }
1357
  }
1358

    
1359
  function onclose() {
1360
    socket.removeListener('error', onerror);
1361
    socket.removeListener('timeout', ontimeout);
1362
  }
1363

    
1364
  function ontimeout() {
1365
    cleartext.emit('timeout');
1366
  }
1367

    
1368
  socket.on('error', onerror);
1369
  socket.on('close', onclose);
1370
  socket.on('timeout', ontimeout);
1371

    
1372
  return cleartext;
1373
}