|
|
@ -1,14 +1,58 @@ |
|
|
|
var crypto = require('crypto'); |
|
|
|
var securepair = require('securepair'); |
|
|
|
var net = require('net'); |
|
|
|
var events = require('events'); |
|
|
|
var inherits = require('util').inherits; |
|
|
|
|
|
|
|
var assert = process.assert; |
|
|
|
|
|
|
|
// TODO: support anonymous (nocert) and PSK
|
|
|
|
// TODO: how to proxy maxConnections?
|
|
|
|
|
|
|
|
|
|
|
|
// AUTHENTICATION MODES
|
|
|
|
//
|
|
|
|
// There are several levels of authentication that TLS/SSL supports.
|
|
|
|
// Read more about this in "man SSL_set_verify".
|
|
|
|
//
|
|
|
|
// 1. The server sends a certificate to the client but does not request a
|
|
|
|
// cert from the client. This is common for most HTTPS servers. The browser
|
|
|
|
// can verify the identity of the server, but the server does not know who
|
|
|
|
// the client is. Authenticating the client is usually done over HTTP using
|
|
|
|
// login boxes and cookies and stuff.
|
|
|
|
//
|
|
|
|
// 2. The server sends a cert to the client and requests that the client
|
|
|
|
// also send it a cert. The client knows who the server is and the server is
|
|
|
|
// requesting the client also identify themselves. There are several
|
|
|
|
// outcomes:
|
|
|
|
//
|
|
|
|
// A) verifyError returns null meaning the client's certificate is signed
|
|
|
|
// by one of the server's CAs. The server know's the client idenity now
|
|
|
|
// and the client is authorized.
|
|
|
|
//
|
|
|
|
// B) For some reason the client's certificate is not acceptable -
|
|
|
|
// verifyError returns a string indicating the problem. The server can
|
|
|
|
// either (i) reject the client or (ii) allow the client to connect as an
|
|
|
|
// unauthorized connection.
|
|
|
|
//
|
|
|
|
// The mode is controlled by two boolean variables.
|
|
|
|
//
|
|
|
|
// requestCert
|
|
|
|
// If true the server requests a certificate from client connections. For
|
|
|
|
// the common HTTPS case, users will want this to be false, which is what
|
|
|
|
// it defaults to.
|
|
|
|
//
|
|
|
|
// rejectUnauthorized
|
|
|
|
// If true clients whose certificates are invalid for any reason will not
|
|
|
|
// be allowed to make connections. If false, they will simply be marked as
|
|
|
|
// unauthorized but secure communication will continue. By default this is
|
|
|
|
// false.
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// Options:
|
|
|
|
// - unauthorizedPeers. Boolean, default to false.
|
|
|
|
// - requestCert. Send verify request. Default to false.
|
|
|
|
// - rejectUnauthorized. Boolean, default to false.
|
|
|
|
// - key. string.
|
|
|
|
// - cert: string.
|
|
|
|
// - ca: string or array of strings.
|
|
|
@ -56,25 +100,24 @@ function Server(/* [options], listener */) { |
|
|
|
{ key: self.key, cert: self.cert, ca: self.ca }); |
|
|
|
creds.context.setCiphers('RC4-SHA:AES128-SHA:AES256-SHA'); |
|
|
|
|
|
|
|
var pair = crypto.createPair(creds, |
|
|
|
var pair = securepair.createSecurePair(creds, |
|
|
|
true, |
|
|
|
!self.unauthorizedPeers); |
|
|
|
self.requestCert, |
|
|
|
self.rejectUnauthorized); |
|
|
|
pair.encrypted.pipe(socket); |
|
|
|
socket.pipe(pair.encrypted); |
|
|
|
|
|
|
|
pair.on('secure', function() { |
|
|
|
pair.on('secure', function(verifyError) { |
|
|
|
if (!self.requestCert) { |
|
|
|
self.emit('unauthorized', pair.cleartext); |
|
|
|
} else { |
|
|
|
var verifyError = pair._ssl.verifyError(); |
|
|
|
|
|
|
|
if (verifyError) { |
|
|
|
if (self.unauthorizedPeers) { |
|
|
|
self.emit('unauthorized', pair.cleartext, verifyError); |
|
|
|
} else { |
|
|
|
console.error('REJECT PEER. verify error: %s', verifyError); |
|
|
|
socket.destroy(); |
|
|
|
} |
|
|
|
} else { |
|
|
|
self.emit('authorized', pair.cleartext); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
pair.on('error', function(e) { |
|
|
@ -97,7 +140,6 @@ function Server(/* [options], listener */) { |
|
|
|
} |
|
|
|
|
|
|
|
// Handle option defaults:
|
|
|
|
|
|
|
|
this.setOptions(options); |
|
|
|
} |
|
|
|
|
|
|
@ -109,10 +151,16 @@ exports.createServer = function(options, listener) { |
|
|
|
|
|
|
|
|
|
|
|
Server.prototype.setOptions = function(options) { |
|
|
|
if (typeof options.unauthorizedPeers == 'boolean') { |
|
|
|
this.unauthorizedPeers = options.unauthorizedPeers; |
|
|
|
if (typeof options.requestCert == 'boolean') { |
|
|
|
this.requestCert = options.requestCert; |
|
|
|
} else { |
|
|
|
this.requestCert = false; |
|
|
|
} |
|
|
|
|
|
|
|
if (typeof options.rejectUnauthorized == 'boolean') { |
|
|
|
this.rejectUnauthorized = options.rejectUnauthorized; |
|
|
|
} else { |
|
|
|
this.unauthorizedPeers = false; |
|
|
|
this.rejectUnauthorized = false; |
|
|
|
} |
|
|
|
|
|
|
|
if (options.key) this.key = options.key; |
|
|
|