pennmush/game/data/
pennmush/game/log/
pennmush/game/save/
pennmush/game/txt/evt/
pennmush/game/txt/nws/
pennmush/os2/
pennmush/po/
pennmush/win32/msvc.net/
pennmush/win32/msvc6/
/**
 * \file myssl.c
 *
 * \brief Code to support SSL connections in PennMUSH.
 *
 * This file contains nearly all of the code that interacts with the
 * OpenSSL libraries to suppose SSL connections in PennMUSH.
 *
 * Lots of stuff here taken from Eric Rescorla's 2001 Linux Journal
 * articles "An Introduction to OpenSSL Programming"
 */

#include "copyrite.h"
#include "config.h"

#ifdef HAS_OPENSSL
#include <stdio.h>
#include <stdarg.h>
#ifdef I_SYS_TYPES
#include <sys/types.h>
#endif
#ifdef WIN32
#define FD_SETSIZE 256
#include <windows.h>
#include <winsock.h>
#include <io.h>
#define EWOULDBLOCK WSAEWOULDBLOCK
#define MAXHOSTNAMELEN 32
#define LC_MESSAGES 6
void shutdown_checkpoint(void);
#else				/* !WIN32 */
#ifdef I_SYS_FILE
#include <sys/file.h>
#endif
#ifdef I_SYS_TIME
#include <sys/time.h>
#endif
#include <sys/ioctl.h>
#include <errno.h>
#ifdef I_SYS_SOCKET
#include <sys/socket.h>
#endif
#ifdef I_NETINET_IN
#include <netinet/in.h>
#endif
#ifdef I_NETDB
#include <netdb.h>
#endif
#ifdef I_SYS_PARAM
#include <sys/param.h>
#endif
#ifdef I_SYS_STAT
#include <sys/stat.h>
#endif
#endif				/* !WIN32 */
#include <time.h>
#ifdef I_SYS_WAIT
#include <sys/wait.h>
#endif
#include <fcntl.h>
#include <ctype.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#ifdef I_SYS_SELECT
#include <sys/select.h>
#endif
#ifdef I_UNISTD
#include <unistd.h>
#endif

#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/dh.h>
#include <openssl/evp.h>

#include "conf.h"
#include "mysocket.h"
#include "externs.h"
#include "myssl.h"
#include "log.h"
#include "parse.h"
#include "confmagic.h"

#define MYSSL_RB        0x1	/**< Read blocked (on read) */
#define MYSSL_WB        0x2	/**< Write blocked (on write) */
#define MYSSL_RBOW      0x4	/**< Read blocked (on write) */
#define MYSSL_WBOR      0x8	/**< Write blocked (on read) */
#define MYSSL_ACCEPT    0x10	/**< We need to call SSL_accept (again) */
#define MYSSL_VERIFIED  0x20	/**< This is an authenticated connection */
#define MYSSL_HANDSHAKE 0x40	/**< We need to call SSL_do_handshake */

#undef MYSSL_DEBUG
#ifdef MYSSL_DEBUG
#define ssl_debugdump(x) ssl_errordump(x)
#else
#define ssl_debugdump(x)
#endif

static void ssl_errordump(const char *msg);
static int client_verify_callback(int preverify_ok, X509_STORE_CTX * x509_ctx);
static DH *get_dh1024(void);


static BIO *bio_err = NULL;
static SSL_CTX *ctx = NULL;

/** Initialize the SSL context.
 * \return pointer to SSL context object.
 */
SSL_CTX *
ssl_init(void)
{
  SSL_METHOD *meth;
  unsigned char context[128];

  if (!bio_err) {
    if (!SSL_library_init())
      return NULL;
    SSL_load_error_strings();
    /* Error write context */
    bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
  }
#ifndef HAS_DEV_URANDOM
  /* We need to seed the RNG with RAND_seed() or RAND_egd() here.
   * Where are we going to get an unpredictable seed?
   */
#endif

  /* Set up SIGPIPE handler here? */

  /* Create context */
  meth = SSLv23_server_method();
  ctx = SSL_CTX_new(meth);

  /* Load keys/certs */
  if (*options.ssl_private_key_file) {
    if (!SSL_CTX_use_certificate_chain_file(ctx, options.ssl_private_key_file)) {
      ssl_errordump
	("Unable to load server certificate - only anonymous ciphers supported.");
    }
    if (!SSL_CTX_use_PrivateKey_file
	(ctx, options.ssl_private_key_file, SSL_FILETYPE_PEM)) {
      ssl_errordump
	("Unable to load private key - only anonymous ciphers supported.");
    }
  }

  /* Load trusted CAs */
  if (*options.ssl_ca_file) {
    if (!SSL_CTX_load_verify_locations(ctx, options.ssl_ca_file, NULL)) {
      ssl_errordump("Unable to load CA certificates");
    } else {
      if (options.ssl_require_client_cert)
	SSL_CTX_set_verify(ctx,
			   SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
			   client_verify_callback);
      else
	SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, client_verify_callback);
#if (OPENSSL_VERSION_NUMBER < 0x0090600fL)
      SSL_CTX_set_verify_depth(ctx, 1);
#endif
    }
  }

  SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE | SSL_OP_ALL);
  SSL_CTX_set_mode(ctx,
		   SSL_MODE_ENABLE_PARTIAL_WRITE |
		   SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);

  /* Set up DH callback */
  SSL_CTX_set_tmp_dh(ctx, get_dh1024());

  /* Set the cipher list to the usual default list, except that
   * we'll allow anonymous diffie-hellman, too.
   */
  SSL_CTX_set_cipher_list(ctx, "ALL:ADH:RC4+RSA:+SSLv2:@STRENGTH");

  /* Set up session cache if we can */
  strncpy(context, MUDNAME, 128);
  SSL_CTX_set_session_id_context(ctx, context, u_strlen(context));

  /* Load hash algorithms */
  OpenSSL_add_all_digests();

  return ctx;
}

static int
client_verify_callback(int preverify_ok, X509_STORE_CTX * x509_ctx)
{
  char buf[256];
  X509 *err_cert;
  int err, depth;

  err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
  err = X509_STORE_CTX_get_error(x509_ctx);
  depth = X509_STORE_CTX_get_error_depth(x509_ctx);

  X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);
  if (!preverify_ok) {
    do_log(LT_ERR, 0, 0, "verify error:num=%d:%s:depth=%d:%s\n", err,
	   X509_verify_cert_error_string(err), depth, buf);
    if (err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT) {
      X509_NAME_oneline(X509_get_issuer_name(x509_ctx->current_cert), buf, 256);
      do_log(LT_ERR, 0, 0, "issuer= %s\n", buf);
    }
    return preverify_ok;
  }
  /* They've passed the preverification */
  /* if there are contents of the cert we wanted to verify, we'd do it here. 
   */
  return preverify_ok;
}

static DH *
get_dh1024(void)
{
  static unsigned char dh1024_p[] = {
    0xB6, 0xBC, 0x30, 0x5B, 0xB4, 0xE5, 0x96, 0x62, 0x3F, 0x85, 0x5B, 0x1F,
    0x88, 0xD1, 0x12, 0xE1, 0x1D, 0x27, 0x69, 0x63, 0xAD, 0xB3, 0x4D, 0x23,
    0xB8, 0x4B, 0x1A, 0x90, 0xA6, 0x89, 0xD8, 0x5D, 0xFA, 0xF5, 0x8F, 0xFF,
    0xFF, 0xF4, 0x54, 0x3B, 0xCD, 0x5C, 0xAA, 0x79, 0x8B, 0x14, 0xBB, 0x84,
    0xAC, 0xEE, 0x94, 0x47, 0x76, 0xEC, 0x46, 0x75, 0x26, 0x48, 0x8C, 0x06,
    0x55, 0x27, 0x7F, 0xC0, 0xF1, 0xE8, 0x1F, 0xD2, 0xE4, 0x55, 0xAE, 0x78,
    0x11, 0x6E, 0xF1, 0x3B, 0xCD, 0x55, 0xE8, 0x17, 0xE9, 0x15, 0x7B, 0x05,
    0x91, 0x28, 0x9D, 0xD3, 0x40, 0x2E, 0x34, 0x03, 0x04, 0x2B, 0x2D, 0xC5,
    0x5C, 0x67, 0xC5, 0xF4, 0x28, 0x8E, 0x16, 0xAC, 0xDD, 0x68, 0x43, 0x66,
    0x51, 0xC1, 0x6F, 0x54, 0xB9, 0x22, 0xD8, 0x1A, 0x39, 0x6B, 0x0A, 0xC1,
    0x20, 0x5A, 0x9D, 0x31, 0x30, 0xE4, 0x0B, 0xC3,
  };
  static unsigned char dh1024_g[] = {
    0x02,
  };
  DH *dh;
  if ((dh = DH_new()) == NULL)
    return (NULL);
  dh->p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL);
  dh->g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), NULL);
  if ((dh->p == NULL) || (dh->g == NULL)) {
    DH_free(dh);
    return (NULL);
  }
  return (dh);
}


/** Associate an SSL object with a socket and return it.
 * \param sock socket descriptor to associate with an SSL object.
 * \return pointer to SSL object.
 */
SSL *
ssl_setup_socket(int sock)
{
  SSL *ssl;
  BIO *bio;

  ssl = SSL_new(ctx);
  bio = BIO_new_socket(sock, BIO_NOCLOSE);
  BIO_set_nbio(bio, 1);
  SSL_set_bio(ssl, bio, bio);
  return ssl;
}

/** Close down an SSL connection and free the object.
 * \param ssl pointer to SSL object to close down.
 * Technically, this function sends a shutdown notification
 * and then frees the object without waiting for acknowledgement
 * of the shutdown. If there were a good way to do that, it would
 * be better.
 */
void
ssl_close_connection(SSL * ssl)
{
  SSL_shutdown(ssl);
  SSL_free(ssl);
}



/** Given an accepted connection on the listening socket, set up SSL.
 * \param sock an accepted socket (returned by accept())
 * \param state pointer to place to return connection state.
 * \return an SSL object to associate with the listen end of this connection.
 */
SSL *
ssl_listen(int sock, int *state)
{
  SSL *ssl;
  ssl = ssl_setup_socket(sock);
  *state = ssl_accept(ssl);
  return ssl;
}

/** Given an accepted connection on the listening socket, resume SSL.
 * \param sock an accepted socket (returned by accept())
 * \param state pointer to place to return connection state.
 * \return an SSL object to associate with the listen end of this connection.
 */
SSL *
ssl_resume(int sock, int *state)
{
  SSL *ssl;

  ssl = ssl_setup_socket(sock);
  SSL_set_accept_state(ssl);
  *state = ssl_handshake(ssl);
  return ssl;
}


/** Perform an SSL handshake.
 * In some cases, a handshake may block, so we may have to call this
 * function again. Accordingly, we return state information that
 * tells us if we need to do that.
 * \param ssl pointer to an SSL object.
 * \return resulting state of the object.
 */
int
ssl_handshake(SSL * ssl)
{
  int ret;
  int state = 0;
  int err;

  if ((ret = SSL_do_handshake(ssl)) <= 0) {
    switch (err = SSL_get_error(ssl, ret)) {
    case SSL_ERROR_WANT_READ:
      /* We must want for the socket to be readable, and then repeat
       * the call.
       */
      ssl_debugdump("SSL_do_handshake wants read");
      state = MYSSL_RB | MYSSL_HANDSHAKE;
      break;
    case SSL_ERROR_WANT_WRITE:
      /* We must want for the socket to be writable, and then repeat
       * the call.
       */
      ssl_debugdump("SSL_do_handshake wants write");
      state = MYSSL_WB | MYSSL_HANDSHAKE;
      break;
    default:
      /* Oops, don't know what's wrong */
      ssl_errordump("Error in ssl_handshake");
      state = -1;
    }
  } else {
    state = ssl_accept(ssl);
  }
  return state;
}

/** Given connection state, determine if an SSL_accept needs to be 
 * performed. This is a just a wrapper so we don't have to expose
 * our internal state management stuff.
 * \param state an ssl connection state.
 * \return 0 if no ssl_accept is needed, non-zero otherwise.
 */
int
ssl_need_accept(int state)
{
  return (state & MYSSL_ACCEPT);
}

/** Given connection state, determine if an SSL_handshake needs to be 
 * performed. This is a just a wrapper so we don't have to expose
 * our internal state management stuff.
 * \param state an ssl connection state.
 * \return 0 if no ssl_handshake is needed, non-zero otherwise.
 */
int
ssl_need_handshake(int state)
{
  return (state & MYSSL_HANDSHAKE);
}

/** Given connection state, determine if it's blocked on write.
 * This is a just a wrapper so we don't have to expose
 * our internal state management stuff.
 * \param state an ssl connection state.
 * \return 0 if no ssl_handshake is needed, non-zero otherwise.
 */
int
ssl_want_write(int state)
{
  return (state & MYSSL_WB);
}

/** Call SSL_accept and return the connection state.
 * \param ssl pointer to an SSL object.
 * \return ssl state flags indicating success, pending, or failure.
 */
int
ssl_accept(SSL * ssl)
{
  int ret;
  int state = 0;
  X509 *peer;
  char buf[256];

  if ((ret = SSL_accept(ssl)) <= 0) {
    switch (SSL_get_error(ssl, ret)) {
    case SSL_ERROR_WANT_READ:
      /* We must want for the socket to be readable, and then repeat
       * the call.
       */
      ssl_debugdump("SSL_accept wants read");
      state = MYSSL_RB | MYSSL_ACCEPT;
      break;
    case SSL_ERROR_WANT_WRITE:
      /* We must want for the socket to be writable, and then repeat
       * the call.
       */
      ssl_debugdump("SSL_accept wants write");
      state = MYSSL_WB | MYSSL_ACCEPT;
      break;
    default:
      /* Oops, don't know what's wrong */
      ssl_errordump("Error accepting connection");
      return -1;
    }
  } else {
    /* Successful accept - report it */
    if ((peer = SSL_get_peer_certificate(ssl))) {
      if (SSL_get_verify_result(ssl) == X509_V_OK) {
	/* The client sent a certificate which verified OK */
	X509_NAME_oneline(X509_get_subject_name(peer), buf, 256);
	do_log(LT_CONN, 0, 0, "SSL client certificate accepted: %s", buf);
	state |= MYSSL_VERIFIED;
      }
    }
  }

  return state;
}


/** Given an SSL object and its last known state, attempt to read from it.
 * \param ssl pointer to SSL object.
 * \param state saved state of SSL object.
 * \param net_read_ready 1 if the underlying socket is ready for read.
 * \param net_write_ready 1 if the underlying socket is ready for write.
 * \param buf buffer to read into.
 * \param bufsize number of bytes to read.
 * \param bytes_read pointer to return the number of bytes read.
 * \return new state of SSL object, or -1 if the connection closed.
 */
int
ssl_read(SSL * ssl, int state, int net_read_ready, int net_write_ready,
	 char *buf, int bufsize, int *bytes_read)
{
  if ((net_read_ready && !(state & MYSSL_WBOR)) ||
      (net_write_ready && (state & MYSSL_RBOW))) {
    do {
      state &= ~(MYSSL_RB | MYSSL_RBOW);
      *bytes_read = SSL_read(ssl, buf, bufsize);
      switch (SSL_get_error(ssl, *bytes_read)) {
      case SSL_ERROR_NONE:
	/* Yay */
	return state;
      case SSL_ERROR_ZERO_RETURN:
	/* End of data on this socket */
	return -1;
      case SSL_ERROR_WANT_READ:
	/* More needs to be read from the underlying socket */
	ssl_debugdump("SSL_read wants read");
	state |= MYSSL_RB;
	break;
      case SSL_ERROR_WANT_WRITE:
	/* More needs to be written to the underlying socket.
	 * This can happen during a rehandshake.
	 */
	ssl_debugdump("SSL_read wants write");
	state |= MYSSL_RBOW;
	break;
      default:
	/* Should never happen */
	ssl_errordump("Unknown ssl_read failure!");
	return -1;
      }
    } while (SSL_pending(ssl) && !(state & MYSSL_RB));
  }
  return state;
}

/** Given an SSL object and its last known state, attempt to write to it.
 * \param ssl pointer to SSL object.
 * \param state saved state of SSL object.
 * \param net_read_ready 1 if the underlying socket is ready for read.
 * \param net_write_ready 1 if the underlying socket is ready for write.
 * \param buf buffer to write.
 * \param bufsize length of buffer to write.
 * \param offset pointer to offset into buffer marking where to write next.
 * \return new state of SSL object, or -1 if the connection closed.
 */
int
ssl_write(SSL * ssl, int state, int net_read_ready, int net_write_ready,
	  unsigned char *buf, int bufsize, int *offset)
{
  int r;
  if ((net_write_ready && bufsize) || (net_read_ready && !(state & MYSSL_WBOR))) {
    state &= ~(MYSSL_WBOR | MYSSL_WB);
    r = SSL_write(ssl, buf + *offset, bufsize);
    switch (SSL_get_error(ssl, r)) {
    case SSL_ERROR_NONE:
      /* We wrote something, but maybe not all */
      bufsize -= r;
      *offset += r;
      break;
    case SSL_ERROR_WANT_WRITE:
      /* Underlying socket isn't ready to be written to. */
      ssl_debugdump("SSL_write wants write");
      state |= MYSSL_WB;
      break;
    case SSL_ERROR_WANT_READ:
      /* More needs to be read from the underlying socket first.
       * This can happen during a rehandshake.
       */
      ssl_debugdump("SSL_write wants read");
      state |= MYSSL_WBOR;
      break;
    default:
      /* Should never happen */
      ssl_errordump("Unknown ssl_write failure!");
    }
  }
  return state;
}


static void
ssl_errordump(const char *msg)
{
  fprintf(stderr, "%s\n", msg);
  ERR_print_errors(bio_err);
}

#ifdef BROKEN

/* The below were various attempts to serialize and save/restore 
 * SSL objects. It ain't pretty, and it don't work.
 */

void
ssl_write_session(FILE * fp, SSL * ssl)
{
  SSL_SESSION *s;
  s = SSL_get_session(ssl);
  PEM_write_SSL_SESSION(fp, s);
}

void
ssl_read_session(FILE * fp)
{
  SSL_SESSION s;
  PEM_read_SSL_SESSION(fp, &s, NULL, NULL);
  SSL_CTX_add_session(ctx, &s);
}

void
ssl_write_ssl(FILE * fp, SSL * ssl)
{
  BIO *bio;
  SSL_CIPHER *cipher;
  bio = SSL_get_rbio(ssl);
  cipher = SSL_CIPHER_get_current_cipher(ssl);
  fwrite(bio, sizeof(BIO), 1, fp);
  fwrite(ssl->version, sizeof(ssl->version), 1, fp);
  fwrite(ssl->type, sizeof(ssl->type), 1, fp);
  fwrite(ssl->rwstate, sizeof(ssl->type), 1, fp);
  fwrite(ssl->rstate, sizeof(ssl->type), 1, fp);
  fwrite(ssl->state, sizeof(ssl->type), 1, fp);
  fwrite(cipher, sizeof(cipher), 1, fp);
}

SSL *
ssl_read_ssl(FILE * fp, int sock)
{
  SSL *ssl;
  BIO *bio;

  bio = BIO_new(BIO_s_socket());
  fread(bio, sizeof(BIO), 1, fp);
  ssl = SSL_new(ctx);
  fread(ssl, sizeof(SSL), 1, fp);
  SSL_set_ssl_method(ssl, SSLv23_server_method());
  SSL_set_bio(ssl, bio, bio);
  return ssl;
}
#endif				/* BROKEN */


#endif				/* HAS_OPENSSL */