diff options
author | Nick Mathewson <nickm@torproject.org> | 2003-09-04 16:05:08 +0000 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2003-09-04 16:05:08 +0000 |
commit | fd20011c263df7fa843d32a2323bff81357b5a20 (patch) | |
tree | 162072e508db0eea1be73e580752178449f66b94 /src/common | |
parent | 4fb92e5bf7144ec14f664913cfefb045fe813dfa (diff) | |
download | tor-fd20011c263df7fa843d32a2323bff81357b5a20.tar.gz tor-fd20011c263df7fa843d32a2323bff81357b5a20.zip |
Add initial interfaces and code for TLS support. Interfaces are right; code needs work and testing.
svn:r424
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/Makefile.am | 4 | ||||
-rw-r--r-- | src/common/crypto.c | 6 | ||||
-rw-r--r-- | src/common/crypto.h | 6 | ||||
-rw-r--r-- | src/common/tortls.c | 323 | ||||
-rw-r--r-- | src/common/tortls.h | 29 |
5 files changed, 360 insertions, 8 deletions
diff --git a/src/common/Makefile.am b/src/common/Makefile.am index cdcc367d9b..7bd90b0bf4 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -3,7 +3,7 @@ noinst_LIBRARIES = libor.a #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 diff --git a/src/common/crypto.c b/src/common/crypto.c index da78d06ce3..06e66857e2 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -696,11 +696,6 @@ int crypto_SHA_digest(unsigned char *m, int len, unsigned char *digest) 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 @@ static void init_dh_param() { supposedly it equals: 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }. */ + /* See also rfc 3536 */ r = BN_hex2bn(&p, "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" diff --git a/src/common/crypto.h b/src/common/crypto.h index 181101b592..051251329a 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -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_encode(char *dest, int destlen, char *src, int srclen); 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(); diff --git a/src/common/tortls.c b/src/common/tortls.c new file mode 100644 index 0000000000..9e964de6e6 --- /dev/null +++ b/src/common/tortls.c @@ -0,0 +1,323 @@ +/* Copyright 2003 Roger Dingledine. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/* 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; + } +} diff --git a/src/common/tortls.h b/src/common/tortls.h new file mode 100644 index 0000000000..fa1b72f205 --- /dev/null +++ b/src/common/tortls.h @@ -0,0 +1,29 @@ +/* Copyright 2003 Roger Dingledine */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#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 |