/***************************************************************************
 * nsock_ssl.c -- This contains functions that relate somewhat exclusively *
 * to SSL (over TCP) support in nsock.  Where SSL support is incidental,   *
 * it is often in other files where code can be more easily shared between *
 * the SSL and NonSSL paths.                                               *
 *                                                                         *
 ***********************IMPORTANT NSOCK LICENSE TERMS***********************
 *                                                                         *
 * The nsock parallel socket event library is (C) 1999-2012 Insecure.Com   *
 * LLC This library is free software; you may redistribute and/or          *
 * modify it under the terms of the GNU General Public License as          *
 * published by the Free Software Foundation; Version 2.  This guarantees  *
 * your right to use, modify, and redistribute this software under certain *
 * conditions.  If this license is unacceptable to you, Insecure.Com LLC   *
 * may be willing to sell alternative licenses (contact                    *
 * sales@insecure.com ).                                                   *
 *                                                                         *
 * As a special exception to the GPL terms, Insecure.Com LLC grants        *
 * permission to link the code of this program with any version of the     *
 * OpenSSL library which is distributed under a license identical to that  *
 * listed in the included docs/licenses/OpenSSL.txt file, and distribute   *
 * linked combinations including the two. You must obey the GNU GPL in all *
 * respects for all of the code used other than OpenSSL.  If you modify    *
 * this file, you may extend this exception to your version of the file,   *
 * but you are not obligated to do so.                                     *
 *                                                                         *
 * If you received these files with a written license agreement stating    *
 * terms other than the (GPL) terms above, then that alternative license   *
 * agreement takes precedence over this comment.                           *
 *                                                                         *
 * Source is provided to this software because we believe users have a     *
 * right to know exactly what a program is going to do before they run it. *
 * This also allows you to audit the software for security holes (none     *
 * have been found so far).                                                *
 *                                                                         *
 * Source code also allows you to port Nmap to new platforms, fix bugs,    *
 * and add new features.  You are highly encouraged to send your changes   *
 * to nmap-dev@insecure.org for possible incorporation into the main       *
 * distribution.  By sending these changes to Fyodor or one of the         *
 * Insecure.Org development mailing lists, it is assumed that you are      *
 * offering the Nmap Project (Insecure.Com LLC) the unlimited,             *
 * non-exclusive right to reuse, modify, and relicense the code.  Nmap     *
 * will always be available Open Source, but this is important because the *
 * inability to relicense code has caused devastating problems for other   *
 * Free Software projects (such as KDE and NASM).  We also occasionally    *
 * relicense the code to third parties as discussed above.  If you wish to *
 * specify special license conditions of your contributions, just say so   *
 * when you send them.                                                     *
 *                                                                         *
 * This program is distributed in the hope that it will be useful, but     *
 * WITHOUT ANY WARRANTY; without even the implied warranty of              *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       *
 * General Public License v2.0 for more details                            *
 * (http://www.gnu.org/licenses/gpl-2.0.html).                             *
 *                                                                         *
 ***************************************************************************/

/* $Id$ */


#include "nsock.h"
#include "nsock_internal.h"
#include "nsock_ssl.h"
#include "netutils.h"

#if HAVE_OPENSSL

/* Disallow anonymous ciphers (Diffie-Hellman key agreement), low bit-strength
 * ciphers, export-crippled ciphers, and MD5. Prefer ciphers in decreasing order
 * of key size. The cipher list is taken from the book Network Security with
 *  OpenSSL. To see exactly what ciphers are enabled, use the command
 *   openssl ciphers -v '...'
 * where ... is the string below. */
#define CIPHERS_SECURE "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"

/* This list of ciphers is for speed and compatibility, not security. Any cipher
 *  is accepted, and the list is sorted by speed based on Brian Hatch's
 *  (bri@ifokr.org) tests on an Pentium 686 against the ciphers listed. */
#define CIPHERS_FAST "RC4-SHA:RC4-MD5:NULL-SHA:EXP-DES-CBC-SHA:EXP-EDH-RSA-DES-CBC-SHA:EXP-RC4-MD5:NULL-MD5:EDH-RSA-DES-CBC-SHA:EXP-RC2-CBC-MD5:EDH-RSA-DES-CBC3-SHA:EXP-ADH-RC4-MD5:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:EXP-ADH-DES-CBC-SHA:ADH-AES256-SHA:ADH-DES-CBC-SHA:ADH-RC4-MD5:AES256-SHA:DES-CBC-SHA:DES-CBC3-SHA:ADH-DES-CBC3-SHA:AES128-SHA:ADH-AES128-SHA:eNULL:ALL"

extern struct timeval nsock_tod;

/* Create an SSL_CTX and do initialization that is common to nsp_ssl_init and
 * nsp_ssl_init_max_speed. */
static SSL_CTX *ssl_init_common() {
  SSL_CTX *ctx;

  SSL_load_error_strings();
  SSL_library_init();

  ctx = SSL_CTX_new(SSLv23_client_method());
  if (!ctx) {
    fatal("OpenSSL failed to create a new SSL_CTX: %s",
          ERR_error_string(ERR_get_error(), NULL));
  }

  /* Our SSL* will always have the SSL_SESSION* inside it, so we neither need to
   * use nor waste memory for the session cache.  (Use '1' because '0' means
   * 'infinite'.)   */
  SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF|SSL_SESS_CACHE_NO_AUTO_CLEAR);
  SSL_CTX_sess_set_cache_size(ctx, 1);
  SSL_CTX_set_timeout(ctx, 3600); /* pretty unnecessary */

  return ctx;
}
#endif /* HAVE_OPENSSL */

/* Initializes an Nsock pool to create SSL connections. This sets an internal
 * SSL_CTX, which is like a template that sets options for all connections that
 * are made from it. The connections made from this context will use only secure
 * ciphers but no server certificate verification is done. Returns the SSL_CTX
 * so you can set your own options. */
nsock_ssl_ctx nsp_ssl_init(nsock_pool ms_pool) {
#if HAVE_OPENSSL
  mspool *ms = (mspool *)ms_pool;
  char rndbuf[128];

  if (ms->sslctx == NULL)
    ms->sslctx = ssl_init_common();

  /* get_random_bytes may or may not provide high-quality randomness. Add it to
   * the entropy pool without increasing the entropy estimate (third argument of
   * RAND_add is 0). We rely on OpenSSL's entropy gathering, called implicitly
   * by RAND_status, to give us what we need, or else bail out if it fails. */
  get_random_bytes(rndbuf, sizeof(rndbuf));
  RAND_add(rndbuf, sizeof(rndbuf), 0);
  if (!RAND_status())
    fatal("nsp_ssl_init: Failed to seed OpenSSL PRNG (RAND_status returned false).");

  /* By default, do no server certificate verification. To enable it, do
   * something like:
   *    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
   *
   *  on the SSL_CTX returned. If you do, it is then up to the application to
   *  load trusted certificates with SSL_CTX_load_verify_locations or
   *  SSL_CTX_set_default_verify_paths, or else every connection will fail. It
   *  is also up to the application to do any further checks such as domain name
   *  validation. */
  SSL_CTX_set_verify(ms->sslctx, SSL_VERIFY_NONE, NULL);

  /* SSL_OP_ALL sets bug-compatibility for pretty much everything.
   * SSL_OP_NO_SSLv2 disables the less-secure SSLv2 while allowing us to use the
   * SSLv2-compatible SSLv23_client_method. */
  SSL_CTX_set_options(ms->sslctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);

  if (!SSL_CTX_set_cipher_list(ms->sslctx, CIPHERS_SECURE)) {
    fatal("Unable to set OpenSSL cipher list: %s",
          ERR_error_string(ERR_get_error(), NULL));
  }
  return ms->sslctx;
#else
  fatal("%s called with no OpenSSL support", __func__);
#endif
}

/* Initializes an Nsock pool to create SSL connections that emphasize speed over
 * security. Insecure ciphers are used when they are faster and no certificate
 * verification is done. Returns the SSL_CTX so you can set your own options. */
nsock_ssl_ctx nsp_ssl_init_max_speed(nsock_pool ms_pool) {
#if HAVE_OPENSSL
  mspool *ms = (mspool *)ms_pool;
  char rndbuf[128];

  if (ms->sslctx == NULL)
    ms->sslctx = ssl_init_common();

  /* get_random_bytes may or may not provide high-quality randomness. */
  get_random_bytes(rndbuf, sizeof(rndbuf));
  RAND_seed(rndbuf, sizeof(rndbuf));

  SSL_CTX_set_verify(ms->sslctx, SSL_VERIFY_NONE, NULL);
  SSL_CTX_set_options(ms->sslctx, SSL_OP_ALL);
  if (!SSL_CTX_set_cipher_list(ms->sslctx, CIPHERS_FAST)) {
    fatal("Unable to set OpenSSL cipher list: %s",
          ERR_error_string(ERR_get_error(), NULL));
  }
  return ms->sslctx;
#else
  fatal("%s called with no OpenSSL support", __func__);
#endif
}

/* Check server certificate verification, after a connection is established. We
 * check first that a certificate was even offered, then call
 * SSL_get_verify_result to get the overall status of verification. (Just
 * calling SSL_get_verify_result is not enough because that function returns
 * X509_V_OK when 0 certificates are presented.) If the verification mode of the
 * SSL object is SSL_VERIFY_NONE, or if OpenSSL is disabled, this function
 * always returns true. */
int nsi_ssl_post_connect_verify(const nsock_iod nsockiod) {
#if HAVE_OPENSSL
  msiod *iod = (msiod *)nsockiod;

  assert(iod->ssl != NULL);
  if (SSL_get_verify_mode(iod->ssl) != SSL_VERIFY_NONE) {
    X509 *cert;

    cert = SSL_get_peer_certificate(iod->ssl);
    if (cert == NULL)
      /* No certificate presented. */
      return 0;

    X509_free(cert);

    if (SSL_get_verify_result(iod->ssl) != X509_V_OK)
      /* Something wrong with verification. */
      return 0;
  }
#endif
  return 1;
}