[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[or-cvs] Add initial interfaces and code for TLS support. Interface...



Update of /home/or/cvsroot/src/common
In directory moria.mit.edu:/tmp/cvs-serv15538/src/common

Modified Files:
	Makefile.am crypto.c crypto.h 
Added Files:
	tortls.c tortls.h 
Log Message:
Add initial interfaces and code for TLS support.  Interfaces are right; code needs work and testing.

--- NEW FILE: tortls.c ---
/* Copyright 2003 Roger Dingledine. */
/* See LICENSE for licensing information */
/* $Id: tortls.c,v 1.1 2003/09/04 16:05:08 nickm Exp $ */

/* TLS wrappers for The Onion Router.  (Unlike other tor functions, these
 * are prefixed with tor_ in order to avoid conflicting with OpenSSL
 * functions and variables.)
 */

#include "./crypto.h"
#include "./tortls.h"
#include "./util.h"

#include <assert.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/tls1.h>
#include <openssl/asn1.h>
#include <openssl/bio.h>

struct tor_tls_context_st {
  SSL_CTX *ctx;
};

struct tor_tls_st {
  SSL *ssl;
  int socket;
  enum { 
    TOR_TLS_ST_HANDSHAKE, TOR_TLS_ST_OPEN, TOR_TLS_ST_GOTCLOSE, 
    TOR_TLS_ST_SENTCLOSE, TOR_TLS_ST_CLOSED
  } state;
  int isServer;
};

#define _TOR_TLS_SYSCALL    -6
#define _TOR_TLS_ZERORETURN -5

static int
tor_tls_get_error(tor_tls *tls, int r, int extra)
{
  int err = SSL_get_error(tls->ssl, r);
  switch (err) {
    case SSL_ERROR_NONE:
      return TOR_TLS_DONE;
    case SSL_ERROR_WANT_READ:
      return TOR_TLS_WANTREAD;
    case SSL_ERROR_WANT_WRITE:
      return TOR_TLS_WANTWRITE;
    case SSL_ERROR_SYSCALL:
      return extra ? _TOR_TLS_SYSCALL : TOR_TLS_ERROR;
    case SSL_ERROR_ZERO_RETURN:
      return extra ? _TOR_TLS_ZERORETURN : TOR_TLS_ERROR;
    default:
      return TOR_TLS_ERROR;
  }
}

static int always_accept_verify_cb(int preverify_ok, 
                                   X509_STORE_CTX *x509_ctx)
{
  /* XXXX Actually, this needs to get more complicated.  But for now,
     XXXX always accept peer certs. */
  return 1;
}

/* Generate a self-signed certificate with the private key 'rsa' and
 * commonName 'nickname', and write it, PEM-encoded, to the file named
 * by 'certfile'.  Return 0 on success, -1 for failure.
 */
int
tor_tls_write_certificate(char *certfile, crypto_pk_env_t *rsa, char *nickname)
{
  RSA *_rsa = NULL;
  time_t start_time, end_time;
  EVP_PKEY *pkey = NULL;
  X509 *x509 = NULL;
  X509_NAME *name = NULL;
  BIO *out = NULL;
  int nid;
  
  start_time = time(NULL);

  assert(rsa && rsa->type == CRYPTO_PK_RSA);
  if (!(_rsa = RSAPrivateKey_dup((RSA*)rsa->key)))
    return -1;
  if (!(pkey = EVP_PKEY_new()))
    return -1;
  if (!(EVP_PKEY_assign_RSA(pkey, _rsa)))
    return -1;
  if (!(x509 = X509_new()))
    return -1;
  if (!(X509_set_version(x509, 2)))
    return -1;
  if (!(ASN1_INTEGER_set(X509_get_serialNumber(x509), (long)start_time)))
    return -1;
  
  if (!(name = X509_NAME_new()))
    return -1;
  if ((nid = OBJ_txt2nid("organizationName")) != NID_undef) return -1;
  if (!(X509_NAME_add_entry_by_NID(name, nid, MBSTRING_ASC,
                                   "TOR", -1, -1, 0))) return -1;
  if ((nid = OBJ_txt2nid("commonName")) != NID_undef) return -1;
  if (!(X509_NAME_add_entry_by_NID(name, nid, MBSTRING_ASC,
                                   nickname, -1, -1, 0))) return -1;
  
  if (!(X509_set_issuer_name(x509, name)))
    return -1;
  if (!(X509_set_subject_name(x509, name)))
    return -1;
  if (!X509_time_adj(X509_get_notBefore(x509),0,&start_time))
    return -1;
  end_time = start_time + 24*60*60*365;
  if (!X509_time_adj(X509_get_notAfter(x509),0,&end_time))
    return -1;
  if (!X509_set_pubkey(x509, pkey))
    return -1;
  if (!X509_sign(x509, pkey, EVP_sha1()))
    return -1;
  if (!(out = BIO_new_file(certfile, "w")))
    return -1;
  if (!(PEM_write_bio_X509(out, x509)))
    return -1;
  BIO_free(out);
  X509_free(x509);
  EVP_PKEY_free(pkey);
  X509_NAME_free(name);
  return 0;
}

/* Create a new TLS context.  If we are going to be using it as a
 * server, it must have isServer set to true, certfile set to a
 * filename for a certificate file, and RSA set to the private key
 * used for that certificate.
 */
tor_tls_context *
tor_tls_context_new(char *certfile, crypto_pk_env_t *rsa, int isServer)
{
  assert(!rsa || rsa->type == CRYPTO_PK_RSA);
  assert((certfile && rsa) || (!certfile && !rsa));
  crypto_dh_env_t *dh = NULL;
  RSA *_rsa = NULL;
  EVP_PKEY *pkey = NULL;
  tor_tls_context *result;

  result = tor_malloc(sizeof(tor_tls_context));
  if (!(result->ctx = SSL_CTX_new(TLSv1_method())))
    return NULL;
  /* XXXX This should use AES, but we'll need to require OpenSSL 0.9.7 first */
  if (!SSL_CTX_set_cipher_list(result->ctx, TLS1_TXT_DHE_DSS_WITH_RC4_128_SHA))
                               /* TLS1_TXT_DHE_RSA_WITH_AES_128_SHA)) */
    return NULL;
  if (certfile && !SSL_CTX_use_certificate_file(result->ctx,certfile,
                                                SSL_FILETYPE_PEM))
    return NULL;
  SSL_CTX_set_session_cache_mode(result->ctx, SSL_SESS_CACHE_OFF);
  if (rsa) {
    if (!(_rsa = RSAPrivateKey_dup((RSA*)rsa->key)))
      return NULL;
    if (!(pkey = EVP_PKEY_new()))
      return NULL;
    if (!EVP_PKEY_assign_RSA(pkey, _rsa))
      return NULL;
    if (!SSL_CTX_use_PrivateKey(result->ctx, pkey))
      return NULL;
    EVP_PKEY_free(pkey);
    if (certfile) {
      if (!SSL_CTX_check_private_key(result->ctx))
        return NULL;
    }
  }
  dh = crypto_dh_new();
  SSL_CTX_set_tmp_dh(result->ctx, dh->dh);
  crypto_dh_free(dh);
  SSL_CTX_set_verify(result->ctx, SSL_VERIFY_PEER, 
                     always_accept_verify_cb);
  
  return result;
}

/* Create a new TLS object from a TLS context, a filedescriptor, and 
 * a flag to determine whether it is functioning as a server.
 */
tor_tls *
tor_tls_new(tor_tls_context *ctx, int sock, int isServer)
{
  tor_tls *result = tor_malloc(sizeof(tor_tls));
  if (!(result->ssl = SSL_new(ctx->ctx)))
    return NULL;
  result->socket = sock;
  SSL_set_fd(result->ssl, sock);
  result->state = TOR_TLS_ST_HANDSHAKE;
  result->isServer = isServer;
  return result;
}

/* Release resources associated with a TLS object.  Does not close the
 * underlying file descriptor.
 */
void
tor_tls_free(tor_tls *tls)
{
  SSL_free(tls->ssl);
  free(tls);
}

/* Underlying function for TLS reading.  Reads up to 'len' characters
 * from 'tls' into 'cp'.  On success, returns the number of characters
 * read.  On failure, returns TOR_TLS_ERROR, TOR_TLS_CLOSE,
 * TOR_TLS_WANTREAD, or TOR_TLS_WANTWRITE.
 */
int
tor_tls_read(tor_tls *tls, char *cp, int len)
{
  int r, err;
  assert(tls && tls->ssl);
  assert(tls->state == TOR_TLS_ST_OPEN);
  r = SSL_read(tls->ssl, cp, len);
  if (r > 0)
    return r;
  err = tor_tls_get_error(tls, r, 1);
  if (err == _TOR_TLS_SYSCALL)
    return TOR_TLS_ERROR;
  else if (err == _TOR_TLS_ZERORETURN) {
    tls->state = TOR_TLS_ST_CLOSED;
    return TOR_TLS_CLOSE;
  } else {
    /*  XXXX Make sure it's not TOR_TLS_DONE. */
    return err;
  }
}

/* Underlying function for TLS writing.  Write up to 'n' characters
 * from 'cp' onto 'tls'.  On success, returns the number of characters
 * written.  On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD, 
 * or TOR_TLS_WANTWRITE.
 */
int
tor_tls_write(tor_tls *tls, char *cp, int n)
{
  int r, err;
  assert(tls && tls->ssl);
  assert(tls->state == TOR_TLS_ST_OPEN);
  r = SSL_write(tls->ssl, cp, n);
  err = tor_tls_get_error(tls, r, 1);
  if (err == _TOR_TLS_ZERORETURN) {
    /* should never happen XXXX */
    return 0;
  } else if (err == TOR_TLS_DONE) {
    return r;
  } else {
    return err;
  }  
}

/* Perform initial handshake on 'tls'.  When finished, returns
 * TOR_TLS_DONE.  On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD,
 * or TOR_TLS_WANNTWRITE.
 */
int
tor_tls_handshake(tor_tls *tls)
{
  int r;
  assert(tls && tls->ssl);
  assert(tls->state == TOR_TLS_ST_HANDSHAKE);
  if (tls->isServer) {
    r = SSL_accept(tls->ssl);
  } else {
    r = SSL_connect(tls->ssl);
  }
  r = tor_tls_get_error(tls,r,0);
  if (r == TOR_TLS_DONE) {
    tls->state = TOR_TLS_ST_OPEN; 
  }
  return r;
}

/* Shut down an open tls connection 'tls'.  When finished, returns
 * TOR_TLS_DONE.  On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD,
 * or TOR_TLS_WANTWRITE.
 */
int
tor_tls_shutdown(tor_tls *tls)
{
  int r, err;
  char buf[128];
  assert(tls && tls->ssl);

  if (tls->state == TOR_TLS_ST_SENTCLOSE) {
    do {
      r = SSL_read(tls->ssl, buf, 128);
    } while (r>0);
    err = tor_tls_get_error(tls, r, 1);
    if (err == _TOR_TLS_ZERORETURN) {
      tls->state = TOR_TLS_ST_GOTCLOSE;
      /* fall through */
    } else {
      if (err == _TOR_TLS_SYSCALL)
        err = TOR_TLS_ERROR;
      return err;
    }
  }

  r = SSL_shutdown(tls->ssl);
  if (r == 1) {
    tls->state = TOR_TLS_ST_CLOSED;
    return TOR_TLS_DONE;
  }
  err = tor_tls_get_error(tls, r, 1);
  if (err == _TOR_TLS_SYSCALL)
    return TOR_TLS_ST_CLOSED; /* XXXX is this right? */
  else if (err == _TOR_TLS_ZERORETURN) {
    if (tls->state == TOR_TLS_ST_GOTCLOSE || 
        tls->state == TOR_TLS_ST_SENTCLOSE) {
      /* XXXX log; unexpected. */
      return TOR_TLS_ERROR;
    }
    tls->state = TOR_TLS_ST_SENTCLOSE;
    return tor_tls_shutdown(tls);
  } else {
    /* XXXX log if not error. */
    return err;
  }
}

--- NEW FILE: tortls.h ---
/* Copyright 2003 Roger Dingledine */
/* See LICENSE for licensing information */
/* $Id: tortls.h,v 1.1 2003/09/04 16:05:08 nickm Exp $ */

#ifndef _TORTLS_H
#define _TORTLS_H

#include "../common/crypto.h"

typedef struct tor_tls_context_st tor_tls_context;
typedef struct tor_tls_st tor_tls;

#define TOR_TLS_ERROR       -4
#define TOR_TLS_CLOSE       -3
#define TOR_TLS_WANTREAD    -2
#define TOR_TLS_WANTWRITE   -1
#define TOR_TLS_DONE         0

int tor_tls_write_certificate(char *certfile, crypto_pk_env_t *rsa, char *nickname);
tor_tls_context *tor_tls_context_new(char *certfile, crypto_pk_env_t *rsa, int isServer);
tor_tls *tor_tls_new(tor_tls_context *ctx, int sock, int isServer);
void tor_tls_free(tor_tls *tls);
int tor_tls_read(tor_tls *tls, char *cp, int len);
int tor_tls_write(tor_tls *tls, char *cp, int n);
int tor_tls_handshake(tor_tls *tls);
/* XXXX we need a function to check for validated, verified peer certs. */
int tor_tls_shutdown(tor_tls *tls);

#endif

Index: Makefile.am
===================================================================
RCS file: /home/or/cvsroot/src/common/Makefile.am,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -d -r1.14 -r1.15
--- Makefile.am	14 Aug 2003 21:46:24 -0000	1.14
+++ Makefile.am	4 Sep 2003 16:05:08 -0000	1.15
@@ -3,7 +3,7 @@
 
 #CFLAGS  = -Wall -Wpointer-arith -O2
 
-libor_a_SOURCES = log.c crypto.c fakepoll.c util.c aes.c
+libor_a_SOURCES = log.c crypto.c fakepoll.c util.c aes.c tortls.c
 
-noinst_HEADERS = log.h crypto.h fakepoll.h test.h util.h aes.h torint.h
+noinst_HEADERS = log.h crypto.h fakepoll.h test.h util.h aes.h torint.h tortls.h
 

Index: crypto.c
===================================================================
RCS file: /home/or/cvsroot/src/common/crypto.c,v
retrieving revision 1.30
retrieving revision 1.31
diff -u -d -r1.30 -r1.31
--- crypto.c	25 Aug 2003 07:06:12 -0000	1.30
+++ crypto.c	4 Sep 2003 16:05:08 -0000	1.31
@@ -696,11 +696,6 @@
   return (SHA1(m,len,digest) == NULL);
 }
 
-
-struct crypto_dh_env_st {
-  DH *dh;
-};
-
 static BIGNUM *dh_param_p = NULL;
 static BIGNUM *dh_param_g = NULL;
 
@@ -735,6 +730,7 @@
      supposedly it equals:
         2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }.
   */
+  /* See also rfc 3536 */
   r = BN_hex2bn(&p,
 		"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
 		"8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"

Index: crypto.h
===================================================================
RCS file: /home/or/cvsroot/src/common/crypto.h,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -d -r1.14 -r1.15
--- crypto.h	30 Jul 2003 19:10:20 -0000	1.14
+++ crypto.h	4 Sep 2003 16:05:08 -0000	1.15
@@ -7,6 +7,7 @@
 
 #include <stdio.h>
 #include <openssl/rsa.h>
+#include <openssl/dh.h>
 
 /* available encryption primitives */
 #define CRYPTO_CIPHER_IDENTITY 0
@@ -72,7 +73,10 @@
 int base64_decode(char *dest, int destlen, char *src, int srclen);
 
 /* Key negotiation */
-typedef struct crypto_dh_env_st crypto_dh_env_t;
+typedef struct crypto_dh_env_st {
+  DH *dh;
+} crypto_dh_env_t;
+
 /* #define CRYPTO_DH_SIZE (1536 / 8) */
 #define CRYPTO_DH_SIZE (1024 / 8)
 crypto_dh_env_t *crypto_dh_new();