tortls.c File Reference

Wrapper functions to present a consistent interface to TLS, SSL, and X.509 functions from OpenSSL. More...

#include "orconfig.h"
#include <assert.h>
#include <openssl/ssl.h>
#include <openssl/ssl3.h>
#include <openssl/err.h>
#include <openssl/tls1.h>
#include <openssl/asn1.h>
#include <openssl/bio.h>
#include <openssl/opensslv.h>
#include "crypto.h"
#include "tortls.h"
#include "util.h"
#include "log.h"
#include "container.h"
#include "ht.h"
#include <string.h>
#include "tortls_states.h"
#include "./ciphers.inc"

Data Structures

struct  tor_tls_context_t
struct  tor_tls_t
struct  cipher_info_t

Defines

#define CRYPTO_PRIVATE
#define V2_HANDSHAKE_SERVER
#define V2_HANDSHAKE_CLIENT
#define LEGAL_NICKNAME_CHARACTERS   "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
#define IDENTITY_CERT_LIFETIME   (365*24*60*60)
#define ADDR(tls)   (((tls) && (tls)->address) ? tls->address : "peer")
#define SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION   0x00040000L
#define SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION   0x0010
#define _TOR_TLS_SYSCALL   (_MIN_TOR_TLS_ERROR_VAL - 2)
#define _TOR_TLS_ZERORETURN   (_MIN_TOR_TLS_ERROR_VAL - 1)
#define CATCH_SYSCALL   1
#define CATCH_ZERO   2
#define SERVER_CIPHER_LIST
#define CIPHER(id, name)   name ":"
#define XCIPHER(id, name)
#define CIPHER(id, name)   { id, name },
#define XCIPHER(id, name)   { id, #name },

Functions

static STACK_OF (SSL_CIPHER)
static INLINE unsigned int tor_tls_entry_hash (const tor_tls_t *a)
static HT_HEAD (HT_PROTOTYPE(tlsmap, HT_PROTOTYPE(tor_tls_t)
static void tor_tls_context_decref (tor_tls_context_t *ctx)
static void tor_tls_context_incref (tor_tls_context_t *ctx)
static X509 * tor_tls_create_certificate (crypto_pk_env_t *rsa, crypto_pk_env_t *rsa_sign, const char *cname, const char *cname_sign, unsigned int lifetime)
static void tor_tls_unblock_renegotiation (tor_tls_t *tls)
static const char * ssl_state_to_string (int ssl_state)
static void tls_log_errors (tor_tls_t *tls, int severity, int domain, const char *doing)
static int tor_errno_to_tls_error (int e)
const char * tor_tls_err_to_string (int err)
static int tor_tls_get_error (tor_tls_t *tls, int r, int extra, const char *doing, int severity, int domain)
static void tor_tls_init (void)
void tor_tls_free_all (void)
static int always_accept_verify_cb (int preverify_ok, X509_STORE_CTX *x509_ctx)
static X509_NAME * tor_x509_name_new (const char *cname)
int tor_tls_context_new (crypto_pk_env_t *identity, unsigned int key_lifetime)
static int tor_tls_client_is_using_v2_ciphers (const SSL *ssl, const char *address)
static void tor_tls_server_info_callback (const SSL *ssl, int type, int val)
static void rectify_client_ciphers (STACK_OF(SSL_CIPHER)**ciphers)
tor_tls_ttor_tls_new (int sock, int isServer)
void tor_tls_set_logged_address (tor_tls_t *tls, const char *address)
void tor_tls_set_renegotiate_callback (tor_tls_t *tls, void(*cb)(tor_tls_t *, void *arg), void *arg)
void tor_tls_block_renegotiation (tor_tls_t *tls)
int tor_tls_is_server (tor_tls_t *tls)
void tor_tls_free (tor_tls_t *tls)
int tor_tls_read (tor_tls_t *tls, char *cp, size_t len)
int tor_tls_write (tor_tls_t *tls, const char *cp, size_t n)
int tor_tls_handshake (tor_tls_t *tls)
int tor_tls_renegotiate (tor_tls_t *tls)
int tor_tls_shutdown (tor_tls_t *tls)
int tor_tls_peer_has_cert (tor_tls_t *tls)
static void log_cert_lifetime (X509 *cert, const char *problem)
static void try_to_extract_certs_from_tls (int severity, tor_tls_t *tls, X509 **cert_out, X509 **id_cert_out)
int tor_tls_verify (int severity, tor_tls_t *tls, crypto_pk_env_t **identity_key)
int tor_tls_check_lifetime (tor_tls_t *tls, int tolerance)
int tor_tls_get_pending_bytes (tor_tls_t *tls)
size_t tor_tls_get_forced_write_size (tor_tls_t *tls)
void tor_tls_get_n_raw_bytes (tor_tls_t *tls, size_t *n_read, size_t *n_written)
void _check_no_tls_errors (const char *fname, int line)
int tor_tls_used_v1_handshake (tor_tls_t *tls)
void tor_tls_get_buffer_sizes (tor_tls_t *tls, size_t *rbuf_capacity, size_t *rbuf_bytes, size_t *wbuf_capacity, size_t *wbuf_bytes)

Variables

static int use_unsafe_renegotiation_op = 0
static int use_unsafe_renegotiation_flag = 0
static SSL_CIPHER * CLIENT_CIPHER_DUMMIES = NULL
static tor_tls_context_tglobal_tls_context = NULL
static int tls_library_is_initialized = 0
static const char CLIENT_CIPHER_LIST []
static const cipher_info_t CLIENT_CIPHER_INFO_LIST []
static const int N_CLIENT_CIPHERS


Detailed Description

Wrapper functions to present a consistent interface to TLS, SSL, and X.509 functions from OpenSSL.


Define Documentation

#define IDENTITY_CERT_LIFETIME   (365*24*60*60)

How long do identity certificates live? (sec)

Referenced by tor_tls_context_new().

#define SERVER_CIPHER_LIST

Value:

(TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":"           \
   TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":"           \
   SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA)
List of ciphers that servers should select from.

Referenced by tor_tls_handshake(), and tor_tls_new().


Function Documentation

void _check_no_tls_errors ( const char *  fname,
int  line 
)

Implement check_no_tls_errors: If there are any pending OpenSSL errors, log an error message.

References LD_CRYPTO, LD_NET, and tls_log_errors().

static int always_accept_verify_cb ( int  preverify_ok,
X509_STORE_CTX *  x509_ctx 
) [static]

We need to give OpenSSL a callback to verify certificates. This is it: We always accept peer certs and complete the handshake. We don't validate them until later.

Referenced by tor_tls_context_new(), and tor_tls_handshake().

static HT_HEAD ( HT_PROTOTYPE(  tlsmap,
HT_PROTOTYPE(  tor_tls_t 
) [static]

Map from SSL* pointers to tor_tls_t objects using those pointers. Helper: given a SSL* pointer, return the tor_tls_t object using that pointer.

static void log_cert_lifetime ( X509 *  cert,
const char *  problem 
) [static]

Warn that a certificate lifetime extends through a certain range.

References LD_GENERAL, LD_NET, tls_log_errors(), and tor_free.

Referenced by tor_tls_check_lifetime().

static void rectify_client_ciphers ( STACK_OF(SSL_CIPHER)**  ciphers  )  [static]

Replace *ciphers with a new list of SSL ciphersuites: specifically, a list designed to mimic a common web browser. Some of the ciphers in the list won't actually be implemented by OpenSSL: that's okay so long as the server doesn't select them, and the server won't select anything besides what's in SERVER_CIPHER_LIST.

[If the server does select a bogus cipher, we won't crash or anything; we'll just fail later when we try to look up the cipher in ssl->cipher_list_by_id.]

References CLIENT_CIPHER_DUMMIES, CLIENT_CIPHER_LIST, cipher_info_t::id, LD_NET, N_CLIENT_CIPHERS, cipher_info_t::name, and tor_assert.

Referenced by tor_tls_new().

static const char* ssl_state_to_string ( int  ssl_state  )  [static]

Return the symbolic name of an OpenSSL state.

References tor_snprintf().

Referenced by tls_log_errors(), tor_tls_get_error(), and tor_tls_handshake().

static STACK_OF ( SSL_CIPHER   )  [static]

A stack of SSL_CIPHER objects, some real, some fake. See rectify_client_ciphers() for details.

Referenced by tor_tls_handshake(), and try_to_extract_certs_from_tls().

static void tls_log_errors ( tor_tls_t tls,
int  severity,
int  domain,
const char *  doing 
) [static]

static int tor_errno_to_tls_error ( int  e  )  [static]

Convert an errno (or a WSAerrno on windows) into a TOR_TLS_* error code.

Referenced by tor_tls_get_error().

void tor_tls_block_renegotiation ( tor_tls_t tls  ) 

If this version of openssl supports it, turn off renegotiation on tls. (Our protocol never requires this for security, but it's nice to use belt-and-suspenders here.)

Referenced by connection_or_tls_renegotiated_cb(), and connection_tls_finish_handshake().

int tor_tls_check_lifetime ( tor_tls_t tls,
int  tolerance 
)

Check whether the certificate set on the connection tls is expired or not-yet-valid, give or take tolerance seconds. Return 0 for valid, -1 for failure.

NOTE: you should call tor_tls_verify before tor_tls_check_lifetime.

References LD_NET, log_cert_lifetime(), and tls_log_errors().

static int tor_tls_client_is_using_v2_ciphers ( const SSL *  ssl,
const char *  address 
) [static]

Return true iff the cipher list suggested by the client for ssl is a list that indicates that the client knows how to do the v2 TLS connection handshake.

References LD_NET, smartlist_add(), smartlist_create(), smartlist_free(), smartlist_join_strings(), and tor_free.

Referenced by tor_tls_handshake(), and tor_tls_server_info_callback().

static void tor_tls_context_decref ( tor_tls_context_t ctx  )  [static]

static void tor_tls_context_incref ( tor_tls_context_t ctx  )  [static]

Increase the reference count of ctx.

References tor_tls_context_t::refcnt.

Referenced by tor_tls_new().

int tor_tls_context_new ( crypto_pk_env_t identity,
unsigned int  key_lifetime 
)

Create a new TLS context for use with Tor TLS handshakes. identity should be set to the identity key used to sign the certificate, and nickname set to the nickname to use.

You can call this function multiple times. Each time you call it, it generates new certificates; all new connections will use the new SSL context.

References _crypto_dh_env_get_dh(), _crypto_pk_env_get_evp_pkey(), always_accept_verify_cb(), crypto_dh_free(), crypto_dh_new(), crypto_free_pk_env(), crypto_new_pk_env(), crypto_pk_dup_key(), crypto_random_hostname(), tor_tls_context_t::ctx, IDENTITY_CERT_LIFETIME, tor_tls_context_t::key, LD_CRYPTO, LD_NET, tor_tls_context_t::my_cert, tor_tls_context_t::my_id_cert, tor_tls_context_t::refcnt, tls_log_errors(), tor_assert, tor_free, tor_tls_context_decref(), tor_tls_create_certificate(), tor_tls_init(), and use_unsafe_renegotiation_op.

Referenced by init_keys(), and run_scheduled_events().

static X509 * tor_tls_create_certificate ( crypto_pk_env_t rsa,
crypto_pk_env_t rsa_sign,
const char *  cname,
const char *  cname_sign,
unsigned int  cert_lifetime 
) [static]

Generate and sign an X509 certificate with the public key rsa, signed by the private key rsa_sign. The commonName of the certificate will be cname; the commonName of the issuer will be cname_sign. The cert will be valid for cert_lifetime seconds starting from now. Return a certificate on success, NULL on failure.

References _crypto_pk_env_get_evp_pkey(), LD_NET, tls_log_errors(), tor_assert, tor_tls_init(), and tor_x509_name_new().

Referenced by tor_tls_context_new().

static INLINE unsigned int tor_tls_entry_hash ( const tor_tls_t a  )  [static]

Helper: return a hash value for a tor_tls_t by its SSL.

const char* tor_tls_err_to_string ( int  err  ) 

Given a TOR_TLS_* error code, return a string equivalent.

Referenced by connection_read_to_buf(), and connection_tls_continue_handshake().

void tor_tls_free ( tor_tls_t tls  ) 

Release resources associated with a TLS object. Does not close the underlying file descriptor.

References LD_BUG, tor_tls_t::negotiated_callback, tor_assert, tor_free, and tor_tls_context_decref().

Referenced by _connection_free().

void tor_tls_free_all ( void   ) 

Free all global TLS structures.

References CLIENT_CIPHER_DUMMIES, LD_MM, tor_free, and tor_tls_context_decref().

Referenced by tor_free_all().

void tor_tls_get_buffer_sizes ( tor_tls_t tls,
size_t *  rbuf_capacity,
size_t *  rbuf_bytes,
size_t *  wbuf_capacity,
size_t *  wbuf_bytes 
)

Examine the amount of memory used and available for buffers in tls. Set *rbuf_capacity to the amount of storage allocated for the read buffer and *rbuf_bytes to the amount actually used. Set *wbuf_capacity to the amount of storage allocated for the write buffer and *wbuf_bytes to the amount actually used.

Referenced by dumpstats().

static int tor_tls_get_error ( tor_tls_t tls,
int  r,
int  extra,
const char *  doing,
int  severity,
int  domain 
) [static]

Given a TLS object and the result of an SSL_* call, use SSL_get_error to determine whether an error has occurred, and if so which one. Return one of TOR_TLS_{DONE|WANTREAD|WANTWRITE|ERROR}. If extra&CATCH_SYSCALL is true, return _TOR_TLS_SYSCALL instead of reporting syscall errors. If extra&CATCH_ZERO is true, return _TOR_TLS_ZERORETURN instead of reporting zero-return errors.

If an error has occurred, log it at level severity and describe the current action as doing.

References LD_NET, ssl_state_to_string(), tls_log_errors(), and tor_errno_to_tls_error().

Referenced by tor_tls_handshake(), tor_tls_read(), tor_tls_renegotiate(), tor_tls_shutdown(), and tor_tls_write().

size_t tor_tls_get_forced_write_size ( tor_tls_t tls  ) 

If tls requires that the next write be of a particular size, return that size. Otherwise, return 0.

References tor_tls_t::wantwrite_n.

Referenced by flush_chunk_tls().

void tor_tls_get_n_raw_bytes ( tor_tls_t tls,
size_t *  n_read,
size_t *  n_written 
)

Sets n_read and n_written to the number of bytes read and written, respectively, on the raw socket used by tls since the last time this function was called on tls.

References tor_tls_t::last_read_count, tor_tls_t::last_write_count, and LD_BUG.

Referenced by connection_handle_write_impl(), and connection_read_to_buf().

int tor_tls_get_pending_bytes ( tor_tls_t tls  ) 

Return the number of bytes available for reading from tls.

References tor_assert.

Referenced by connection_or_process_cells_from_inbuf(), and connection_read_to_buf().

int tor_tls_handshake ( tor_tls_t tls  ) 

Perform initial handshake on tls. When finished, returns TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD, or TOR_TLS_WANTWRITE.

References always_accept_verify_cb(), tor_tls_t::isServer, LD_BUG, LD_HANDSHAKE, SERVER_CIPHER_LIST, ssl_state_to_string(), STACK_OF(), tls_log_errors(), tor_assert, tor_tls_client_is_using_v2_ciphers(), tor_tls_get_error(), tor_tls_unblock_renegotiation(), and tor_tls_t::wasV2Handshake.

Referenced by connection_tls_continue_handshake().

static void tor_tls_init ( void   )  [static]

Initialize OpenSSL, unless it has already been initialized.

References LD_GENERAL, tls_library_is_initialized, use_unsafe_renegotiation_flag, and use_unsafe_renegotiation_op.

Referenced by tor_tls_context_new(), and tor_tls_create_certificate().

int tor_tls_is_server ( tor_tls_t tls  ) 

Return whether this tls initiated the connect (client) or received it (server).

References tor_tls_t::isServer, and tor_assert.

Referenced by connection_or_nonopen_was_started_here(), and connection_tls_continue_handshake().

tor_tls_t* tor_tls_new ( int  sock,
int  isServer 
)

int tor_tls_peer_has_cert ( tor_tls_t tls  ) 

Return true iff this TLS connection is authenticated.

References LD_HANDSHAKE, and tls_log_errors().

Referenced by connection_or_check_valid_tls_handshake().

int tor_tls_read ( tor_tls_t tls,
char *  cp,
size_t  len 
)

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.

References tor_tls_t::callback_arg, tor_tls_t::got_renegotiate, LD_NET, tor_tls_t::negotiated_callback, tor_assert, and tor_tls_get_error().

Referenced by read_to_chunk_tls().

int tor_tls_renegotiate ( tor_tls_t tls  ) 

Client only: Renegotiate a TLS session. When finished, returns TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD, or TOR_TLS_WANTWRITE.

References tor_tls_t::isServer, LD_HANDSHAKE, tor_assert, and tor_tls_get_error().

Referenced by connection_tls_continue_handshake().

static void tor_tls_server_info_callback ( const SSL *  ssl,
int  type,
int  val 
) [static]

Invoked when we're accepting a connection on ssl, and the connection changes state. We use this:

  • To alter the state of the handshake partway through, so we do not send or request extra certificates in v2 handshakes.
  • To detect renegotiation

References tor_tls_t::got_renegotiate, LD_BUG, tor_tls_t::negotiated_callback, tor_tls_client_is_using_v2_ciphers(), and tor_tls_t::wasV2Handshake.

Referenced by tor_tls_new(), and tor_tls_set_renegotiate_callback().

void tor_tls_set_logged_address ( tor_tls_t tls,
const char *  address 
)

Make future log messages about tls display the address address.

References tor_assert, and tor_free.

Referenced by connection_tls_start_handshake().

void tor_tls_set_renegotiate_callback ( tor_tls_t tls,
void(*)(tor_tls_t *, void *arg)  cb,
void *  arg 
)

Set cb to be called with argument arg whenever tls next gets a client-side renegotiate in the middle of a read. Do not invoke this function until after initial handshaking is done!

References tor_tls_t::callback_arg, tor_tls_t::got_renegotiate, tor_tls_t::negotiated_callback, and tor_tls_server_info_callback().

Referenced by connection_or_tls_renegotiated_cb(), and connection_tls_continue_handshake().

int tor_tls_shutdown ( tor_tls_t tls  ) 

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.

References LD_NET, tor_assert, and tor_tls_get_error().

static void tor_tls_unblock_renegotiation ( tor_tls_t tls  )  [static]

If this version of openssl requires it, turn on renegotiation on tls.

References use_unsafe_renegotiation_flag, and use_unsafe_renegotiation_op.

Referenced by tor_tls_handshake().

int tor_tls_used_v1_handshake ( tor_tls_t tls  ) 

Return true iff the initial TLS connection at tls did not use a v2 TLS handshake. Output is undefined if the handshake isn't finished.

References tor_tls_t::isServer, and tor_tls_t::wasV2Handshake.

Referenced by connection_tls_continue_handshake(), and connection_tls_finish_handshake().

int tor_tls_verify ( int  severity,
tor_tls_t tls,
crypto_pk_env_t **  identity_key 
)

If the provided tls connection is authenticated and has a certificate chain that is currently valid and signed, then set *identity_key to the identity certificate's key and return 0. Else, return -1 and log complaints with log-level severity.

References _crypto_new_pk_env_rsa(), LD_HANDSHAKE, LD_PROTOCOL, tls_log_errors(), and try_to_extract_certs_from_tls().

Referenced by connection_or_check_valid_tls_handshake().

int tor_tls_write ( tor_tls_t tls,
const char *  cp,
size_t  n 
)

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.

References LD_NET, tor_assert, tor_tls_get_error(), and tor_tls_t::wantwrite_n.

Referenced by flush_chunk_tls().

static X509_NAME* tor_x509_name_new ( const char *  cname  )  [static]

Return a newly allocated X509 name with commonName cname.

Referenced by tor_tls_create_certificate().

static void try_to_extract_certs_from_tls ( int  severity,
tor_tls_t tls,
X509 **  cert_out,
X509 **  id_cert_out 
) [static]

Helper function: try to extract a link certificate and an identity certificate from tls, and store them in *cert_out and *id_cert_out respectively. Log all messages at level severity.

Note that a reference is added to cert_out, so it needs to be freed. id_cert_out doesn't.

References LD_PROTOCOL, and STACK_OF().

Referenced by tor_tls_verify().


Variable Documentation

SSL_CIPHER* CLIENT_CIPHER_DUMMIES = NULL [static]

An array of fake SSL_CIPHER objects that we use in order to trick OpenSSL in client mode into advertising the ciphers we want. See rectify_client_ciphers() for details.

Referenced by rectify_client_ciphers(), and tor_tls_free_all().

A list of all the ciphers that clients should advertise, including items that OpenSSL might not know about.

const char CLIENT_CIPHER_LIST[] [static]

Initial value:

# 554 "/home/arma/work/onion/git/tor/src/common/tortls.c" 2
List of ciphers that clients should advertise, omitting items that our OpenSSL doesn't know about.

Referenced by rectify_client_ciphers(), and tor_tls_new().

Global tls context. We keep it here because nobody else needs to touch it.

const int N_CLIENT_CIPHERS [static]

Initial value:

The length of CLIENT_CIPHER_INFO_LIST and CLIENT_CIPHER_DUMMIES.

Referenced by rectify_client_ciphers().

int tls_library_is_initialized = 0 [static]

True iff tor_tls_init() has been called.

Referenced by tor_tls_init().

Does the run-time openssl version look like we need SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION?

Referenced by tor_tls_init(), and tor_tls_unblock_renegotiation().

int use_unsafe_renegotiation_op = 0 [static]

Does the run-time openssl version look like we need SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION?

Referenced by tor_tls_context_new(), tor_tls_init(), and tor_tls_unblock_renegotiation().


Generated on Tue May 25 00:31:00 2010 for tor by  doxygen 1.5.6