iamthechad/sslinfo

View on GitHub
lib/method.js

Summary

Maintainability
B
6 hrs
Test Coverage
var Q = require('q'),
  _ = require('lodash'),
  tls = require('tls');

module.exports = {
  /**
   * Return an object containing information about each protocol method.
   * @returns {{SSLv2_method: {name: string, opensslAbbrev: string, order: number}, SSLv3_method: {name: string, opensslAbbrev: string, order: number}, TLSv1_method: {name: string, opensslAbbrev: string, order: number}, TLSv1_1_method: {name: string, opensslAbbrev: string, order: number}, TLSv1_2_method: {name: string, opensslAbbrev: string, order: number}}}
   */
  methods: function() {
    return methods;
  },
  /**
   * Determine which SSL/TLS methods a host supports.
   * @param hostData {{ host: string, port: number }}
   * @return {promise}
   */
  getSSLResults: function(hostData) {
    var tasks = _.values(methods).map(function(d) {
      return _trySSLMethod({host: hostData.host, port: hostData.port, servername: hostData.servername, protocol: d});
    });
    return Q.all(tasks).then(function(results) {
      hostData.protocols = [];
      results.forEach(function(item) {
        hostData.protocols.push(item);
      });
      return hostData;
    });
  },
  /**
   * See if a host supports a specific SSL/TLS method
   * @param options {{ host: string, port: number, protocol: { name: string, commonName: string } }}
   * @returns {promise}
   */
  trySSLMethod: function(options) {
    return _trySSLMethod(options);
  }
};

var methods = {
  SSLv2_method: {
    name: 'SSLv2_method',
    commonName: 'SSLv2'
  },
  SSLv3_method: {
    name: 'SSLv3_method',
    commonName: 'SSLv3'
  },
  TLSv1_method: {
    name: 'TLSv1_method',
    commonName: 'TLSv1'
  },
  TLSv1_1_method: {
    name: 'TLSv1_1_method',
    commonName: 'TLSv1.1'
  },
  TLSv1_2_method: {
    name: 'TLSv1_2_method',
    commonName: 'TLSv1.2'
  }
};

/**
 * See if a host supports a specific SSL/TLS method
 * @param options {{ host: string, port: number, protocol: { name: string, commonName: string} }}
 * @returns {promise}
 * @private
 */
function _trySSLMethod(options) {
  var fullOptions = {
    rejectUnauthorized: false,
    secureProtocol: options.protocol.name
  };
  fullOptions = _.extend(fullOptions, options);

  var deferred = Q.defer();
  try {
    var socket = tls.connect(fullOptions, function() {
      deferred.resolve({protocol: options.protocol.name, name: options.protocol.commonName, enabled: true});
      socket.end();
    });
    socket.setEncoding('utf8');
    socket.on('error', function(error) {
      if (error.code && error.code === 'ECONNRESET') {
        deferred.resolve({protocol: options.protocol.name, name: options.protocol.commonName, enabled: false});
      } else {
        var msg = error.toString();
        if (msg.indexOf('no ciphers available') !== -1) {
          deferred.resolve({
            protocol: options.protocol.name,
            name: options.protocol.commonName,
            enabled: false,
            error: 'The installed openssl library does not support "' + options.protocol.commonName + '"'
          });
        } else if (msg.indexOf('SSL3_GET_RECORD:wrong version number' !== -1)) {
          deferred.resolve({
            protocol: options.protocol.name,
            name: options.protocol.commonName,
            enabled: false
          });
        } else {
          deferred.reject(error);
        }
      }
      socket.end();
    });
  } catch (e) {
    var msg = e.toString();
    if (msg.indexOf('methods disabled') !== -1) {
      deferred.resolve({
        protocol: options.protocol.name,
        name: options.protocol.commonName,
        enabled: false,
        error: 'This version of NodeJS does not support "' + options.protocol.commonName + '"'
      });
    } else {
      deferred.reject(e);
    }
  }
  return deferred.promise;
}