Beispiel #1
0
Server.prototype.setOptions = function(options) {
  this.requestCert = options.requestCert === true;
  this.rejectUnauthorized = options.rejectUnauthorized !== false;

  if (options.pfx) this.pfx = options.pfx;
  if (options.key) this.key = options.key;
  if (options.passphrase) this.passphrase = options.passphrase;
  if (options.cert) this.cert = options.cert;
  if (options.ca) this.ca = options.ca;
  if (options.secureProtocol) this.secureProtocol = options.secureProtocol;
  if (options.crl) this.crl = options.crl;
  if (options.ciphers) this.ciphers = options.ciphers;
  if (options.ecdhCurve !== undefined)
    this.ecdhCurve = options.ecdhCurve;
  if (options.dhparam) this.dhparam = options.dhparam;
  if (options.sessionTimeout) this.sessionTimeout = options.sessionTimeout;
  if (options.ticketKeys) this.ticketKeys = options.ticketKeys;
  var secureOptions = options.secureOptions || 0;
  if (options.honorCipherOrder !== undefined)
    this.honorCipherOrder = !!options.honorCipherOrder;
  else
    this.honorCipherOrder = true;
  if (secureOptions) this.secureOptions = secureOptions;
  if (options.NPNProtocols) tls.convertNPNProtocols(options.NPNProtocols, this);
  if (options.ALPNProtocols)
    tls.convertALPNProtocols(options.ALPNProtocols, this);
  if (options.sessionIdContext) {
    this.sessionIdContext = options.sessionIdContext;
  } else {
    this.sessionIdContext = crypto.createHash('sha1')
                                  .update(process.argv.join(' '))
                                  .digest('hex')
                                  .slice(0, 32);
  }
};
Beispiel #2
0
function TLSSocket(socket, opts) {
  const tlsOptions = { ...opts };

  if (tlsOptions.ALPNProtocols)
    tls.convertALPNProtocols(tlsOptions.ALPNProtocols, tlsOptions);

  this._tlsOptions = tlsOptions;
  this._secureEstablished = false;
  this._securePending = false;
  this._newSessionPending = false;
  this._controlReleased = false;
  this._SNICallback = null;
  this.servername = null;
  this.alpnProtocol = null;
  this.authorized = false;
  this.authorizationError = null;
  this[kRes] = null;

  var wrap;
  if ((socket instanceof net.Socket && socket._handle) || !socket) {
    // 1. connected socket
    // 2. no socket, one will be created with net.Socket().connect
    wrap = socket;
  } else {
    // 3. socket has no handle so it is js not c++
    // 4. unconnected sockets are wrapped
    // TLS expects to interact from C++ with a net.Socket that has a C++ stream
    // handle, but a JS stream doesn't have one. Wrap it up to make it look like
    // a socket.
    wrap = new JSStreamSocket(socket);
    wrap.once('close', () => this.destroy());
  }

  // Just a documented property to make secure sockets
  // distinguishable from regular ones.
  this.encrypted = true;

  net.Socket.call(this, {
    handle: this._wrapHandle(wrap),
    allowHalfOpen: socket && socket.allowHalfOpen,
    readable: false,
    writable: false
  });

  // Proxy for API compatibility
  this.ssl = this._handle;  // C++ TLSWrap object

  this.on('error', this._tlsError);

  this._init(socket, wrap);

  // Make sure to setup all required properties like: `connecting` before
  // starting the flow of the data
  this.readable = true;
  this.writable = true;

  // Read on next tick so the caller has a chance to setup listeners
  process.nextTick(initRead, this, socket);
}
Beispiel #3
0
function TLSSocket(socket, opts) {
  const tlsOptions = Object.assign({}, opts);

  if (tlsOptions.NPNProtocols)
    tls.convertNPNProtocols(tlsOptions.NPNProtocols, tlsOptions);
  if (tlsOptions.ALPNProtocols)
    tls.convertALPNProtocols(tlsOptions.ALPNProtocols, tlsOptions);

  this._tlsOptions = tlsOptions;
  this._secureEstablished = false;
  this._securePending = false;
  this._newSessionPending = false;
  this._controlReleased = false;
  this._SNICallback = null;
  this.servername = null;
  this.npnProtocol = null;
  this.alpnProtocol = null;
  this.authorized = false;
  this.authorizationError = null;
  this[kRes] = null;

  // Wrap plain JS Stream into StreamWrap
  var wrap;
  if ((socket instanceof net.Socket && socket._handle) || !socket)
    wrap = socket;
  else
    wrap = new StreamWrap(socket);

  // Just a documented property to make secure sockets
  // distinguishable from regular ones.
  this.encrypted = true;

  net.Socket.call(this, {
    handle: this._wrapHandle(wrap),
    allowHalfOpen: socket && socket.allowHalfOpen,
    readable: false,
    writable: false
  });

  // Proxy for API compatibility
  this.ssl = this._handle;

  this.on('error', this._tlsError);

  this._init(socket, wrap);

  // Make sure to setup all required properties like: `connecting` before
  // starting the flow of the data
  this.readable = true;
  this.writable = true;

  // Read on next tick so the caller has a chance to setup listeners
  process.nextTick(initRead, this, socket);
}
Beispiel #4
0
// 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 now knows the client's identity
//   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
//   true.
//
//
//
// Options:
// - requestCert. Send verify request. Default to false.
// - rejectUnauthorized. Boolean, default to true.
// - key. string.
// - cert: string.
// - clientCertEngine: string.
// - ca: string or array of strings.
// - sessionTimeout: integer.
//
// emit 'secureConnection'
//   function (tlsSocket) { }
//
//   "UNABLE_TO_GET_ISSUER_CERT", "UNABLE_TO_GET_CRL",
//   "UNABLE_TO_DECRYPT_CERT_SIGNATURE", "UNABLE_TO_DECRYPT_CRL_SIGNATURE",
//   "UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY", "CERT_SIGNATURE_FAILURE",
//   "CRL_SIGNATURE_FAILURE", "CERT_NOT_YET_VALID" "CERT_HAS_EXPIRED",
//   "CRL_NOT_YET_VALID", "CRL_HAS_EXPIRED" "ERROR_IN_CERT_NOT_BEFORE_FIELD",
//   "ERROR_IN_CERT_NOT_AFTER_FIELD", "ERROR_IN_CRL_LAST_UPDATE_FIELD",
//   "ERROR_IN_CRL_NEXT_UPDATE_FIELD", "OUT_OF_MEM",
//   "DEPTH_ZERO_SELF_SIGNED_CERT", "SELF_SIGNED_CERT_IN_CHAIN",
//   "UNABLE_TO_GET_ISSUER_CERT_LOCALLY", "UNABLE_TO_VERIFY_LEAF_SIGNATURE",
//   "CERT_CHAIN_TOO_LONG", "CERT_REVOKED" "INVALID_CA",
//   "PATH_LENGTH_EXCEEDED", "INVALID_PURPOSE" "CERT_UNTRUSTED",
//   "CERT_REJECTED"
//
function Server(options, listener) {
  if (!(this instanceof Server))
    return new Server(options, listener);

  if (typeof options === 'function') {
    listener = options;
    options = {};
  } else if (options == null || typeof options === 'object') {
    options = options || {};
  } else {
    throw new ERR_INVALID_ARG_TYPE('options', 'Object', options);
  }

  this._contexts = [];
  this.requestCert = options.requestCert === true;
  this.rejectUnauthorized = options.rejectUnauthorized !== false;

  if (options.sessionTimeout)
    this.sessionTimeout = options.sessionTimeout;

  if (options.ticketKeys)
    this.ticketKeys = options.ticketKeys;

  if (options.ALPNProtocols)
    tls.convertALPNProtocols(options.ALPNProtocols, this);

  this.setSecureContext(options);

  this[kHandshakeTimeout] = options.handshakeTimeout || (120 * 1000);
  this[kSNICallback] = options.SNICallback;

  if (typeof this[kHandshakeTimeout] !== 'number') {
    throw new ERR_INVALID_ARG_TYPE(
      'options.handshakeTimeout', 'number', options.handshakeTimeout);
  }

  if (this[kSNICallback] && typeof this[kSNICallback] !== 'function') {
    throw new ERR_INVALID_ARG_TYPE(
      'options.SNICallback', 'function', options.SNICallback);
  }

  // constructor call
  net.Server.call(this, tlsConnectionListener);

  if (listener) {
    this.on('secureConnection', listener);
  }
}
    type: TypeError,
    message: 'Ticket keys must be a buffer'
  });

assert.throws(() => tls.createServer({ ticketKeys: Buffer.alloc(0) }),
              /TypeError: Ticket keys length must be 48 bytes/);

common.expectsError(
  () => tls.createSecurePair({}),
  {
    code: 'ERR_ASSERTION',
    message: 'context.context must be a NativeSecureContext'
  }
);

{
  const buffer = Buffer.from('abcd');
  const out = {};
  tls.convertALPNProtocols(buffer, out);
  out.ALPNProtocols.write('efgh');
  assert(buffer.equals(Buffer.from('abcd')));
  assert(out.ALPNProtocols.equals(Buffer.from('efgh')));
}

{
  const buffer = new Uint8Array(Buffer.from('abcd'));
  const out = {};
  tls.convertALPNProtocols(buffer, out);
  assert(out.ALPNProtocols.equals(Buffer.from('abcd')));
}
Beispiel #6
0
exports.connect = function(/* [port, host], options, cb */) {
  const argsLen = arguments.length;
  var args = new Array(argsLen);
  for (var i = 0; i < argsLen; i++)
    args[i] = arguments[i];
  args = normalizeConnectArgs(args);
  var options = args[0];
  var cb = args[1];

  var defaults = {
    rejectUnauthorized: '0' !== process.env.NODE_TLS_REJECT_UNAUTHORIZED,
    ciphers: tls.DEFAULT_CIPHERS,
    checkServerIdentity: tls.checkServerIdentity,
    minDHSize: 1024
  };

  options = util._extend(defaults, options || {});
  if (!options.keepAlive)
    options.singleUse = true;

  assert(typeof options.checkServerIdentity === 'function');
  assert(typeof options.minDHSize === 'number',
         'options.minDHSize is not a number: ' + options.minDHSize);
  assert(options.minDHSize > 0,
         'options.minDHSize is not a positive number: ' +
         options.minDHSize);

  var hostname = options.servername ||
                 options.host ||
                 (options.socket && options.socket._host) ||
                 'localhost';
  const NPN = {};
  const ALPN = {};
  const context = options.secureContext || tls.createSecureContext(options);
  tls.convertNPNProtocols(options.NPNProtocols, NPN);
  tls.convertALPNProtocols(options.ALPNProtocols, ALPN);

  var socket = new TLSSocket(options.socket, {
    pipe: options.path && !options.port,
    secureContext: context,
    isServer: false,
    requestCert: true,
    rejectUnauthorized: options.rejectUnauthorized,
    session: options.session,
    NPNProtocols: NPN.NPNProtocols,
    ALPNProtocols: ALPN.ALPNProtocols,
    requestOCSP: options.requestOCSP
  });

  if (cb)
    socket.once('secureConnect', cb);

  if (!options.socket) {
    var connect_opt;
    if (options.path && !options.port) {
      connect_opt = { path: options.path };
    } else {
      connect_opt = {
        port: options.port,
        host: options.host,
        localAddress: options.localAddress
      };
    }
    socket.connect(connect_opt, function() {
      socket._start();
    });
  }

  socket._releaseControl();

  if (options.session)
    socket.setSession(options.session);

  if (options.servername)
    socket.setServername(options.servername);

  if (options.socket)
    socket._start();

  socket.on('secure', function() {
    // Check the size of DHE parameter above minimum requirement
    // specified in options.
    var ekeyinfo = socket.getEphemeralKeyInfo();
    if (ekeyinfo.type === 'DH' && ekeyinfo.size < options.minDHSize) {
      var err = new Error('DH parameter size ' + ekeyinfo.size +
                          ' is less than ' + options.minDHSize);
      socket.emit('error', err);
      socket.destroy();
      return;
    }

    var verifyError = socket._handle.verifyError();

    // Verify that server's identity matches it's certificate's names
    // Unless server has resumed our existing session
    if (!verifyError && !socket.isSessionReused()) {
      var cert = socket.getPeerCertificate();
      verifyError = options.checkServerIdentity(hostname, cert);
    }

    if (verifyError) {
      socket.authorized = false;
      socket.authorizationError = verifyError.code || verifyError.message;

      if (options.rejectUnauthorized) {
        socket.destroy(verifyError);
        return;
      } else {
        socket.emit('secureConnect');
      }
    } else {
      socket.authorized = true;
      socket.emit('secureConnect');
    }

    // Uncork incoming data
    socket.removeListener('end', onHangUp);
  });

  function onHangUp() {
    // NOTE: This logic is shared with _http_client.js
    if (!socket._hadError) {
      socket._hadError = true;
      var error = new Error('socket hang up');
      error.code = 'ECONNRESET';
      socket.destroy(error);
    }
  }
  socket.once('end', onHangUp);

  return socket;
};
 () => tls.convertALPNProtocols(protocols, out),
    type: TypeError,
    message: 'Ticket keys must be a buffer'
  });

assert.throws(() => tls.createServer({ ticketKeys: Buffer.alloc(0) }),
              /TypeError: Ticket keys length must be 48 bytes/);

common.expectsInternalAssertion(
  () => tls.createSecurePair({}),
  'context.context must be a NativeSecureContext'
);

{
  const buffer = Buffer.from('abcd');
  const out = {};
  tls.convertALPNProtocols(buffer, out);
  out.ALPNProtocols.write('efgh');
  assert(buffer.equals(Buffer.from('abcd')));
  assert(out.ALPNProtocols.equals(Buffer.from('efgh')));
}

{
  const arrayBufferViewStr = 'abcd';
  const inputBuffer = Buffer.from(arrayBufferViewStr.repeat(8), 'utf8');
  for (const expectView of common.getArrayBufferViews(inputBuffer)) {
    const out = {};
    tls.convertALPNProtocols(expectView, out);
    assert(out.ALPNProtocols.equals(Buffer.from(expectView)));
  }
}