diff options
author | Nick Mathewson <nickm@torproject.org> | 2012-11-28 13:31:17 -0500 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2012-12-25 20:14:07 -0500 |
commit | 175b2678d7dd0ff8b00b597169e4a9a0d8e86f12 (patch) | |
tree | 3d7531b0316a389bee440521e608b7a9ffc72735 /src/common | |
parent | 63208aa1e53f5ffc3ecbe47402cc2736bbce6af0 (diff) | |
download | tor-175b2678d7dd0ff8b00b597169e4a9a0d8e86f12.tar.gz tor-175b2678d7dd0ff8b00b597169e4a9a0d8e86f12.zip |
Let servers choose better ciphersuites when clients support them
This implements the server-side of proposal 198 by detecting when
clients lack the magic list of ciphersuites that indicates that
they're lying faking some ciphers they don't really have. When
clients lack this list, we can choose any cipher that we'd actually
like. The newly allowed ciphersuites are, currently, "All ECDHE-RSA
ciphers that openssl supports, except for ECDHE-RSA-RC4".
The code to detect the cipher list relies on on (ab)use of
SSL_set_session_secret_cb.
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/tortls.c | 116 | ||||
-rw-r--r-- | src/common/tortls.h | 1 |
2 files changed, 101 insertions, 16 deletions
diff --git a/src/common/tortls.c b/src/common/tortls.c index 4513689cf9..bba89268e1 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -678,11 +678,42 @@ tor_tls_create_certificate(crypto_pk_t *rsa, #undef SERIAL_NUMBER_SIZE } -/** List of ciphers that servers should select from.*/ +/** List of ciphers that servers should select from when the client might be + * claiming extra unsupported ciphers in order to avoid fingerprinting. */ #define SERVER_CIPHER_LIST \ (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 when we actually have + * our choice of what cipher to use. */ +const char UNRESTRICTED_SERVER_CIPHER_LIST[] = +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_CHC_SHA + TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA ":" +#endif +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ":" +#endif +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256 + TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256 ":" +#endif +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA + TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA ":" +#endif +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256 +#endif +//#if TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA +// TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA ":" +//#endif + /* These next two are mandatory. */ + TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":" + TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":" +#ifdef TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA + TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA ":" +#endif + SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA; + /* Note: to set up your own private testing network with link crypto * disabled, set your Tors' cipher list to * (SSL3_TXT_RSA_NULL_SHA). If you do this, you won't be able to communicate @@ -1410,15 +1441,22 @@ prune_v2_cipher_list(void) v2_cipher_list_pruned = 1; } +/* Return the name of the negotiated ciphersuite in use on <b>tls</b> */ +const char * +tor_tls_get_ciphersuite_name(tor_tls_t *tls) +{ + return SSL_get_cipher(tls->ssl); +} + /** Examine the client cipher list in <b>ssl</b>, and determine what kind of * client it is. Return one of CIPHERS_ERR, CIPHERS_V1, CIPHERS_V2, * CIPHERS_UNRESTRICTED. **/ static int -tor_tls_classify_client_ciphers(const SSL *ssl) +tor_tls_classify_client_ciphers(const SSL *ssl, + STACK_OF(SSL_CIPHER) *peer_ciphers) { int i, res; - SSL_SESSION *session; tor_tls_t *tor_tls; if (PREDICT_UNLIKELY(!v2_cipher_list_pruned)) prune_v2_cipher_list(); @@ -1429,20 +1467,15 @@ tor_tls_classify_client_ciphers(const SSL *ssl) /* If we reached this point, we just got a client hello. See if there is * a cipher list. */ - if (!(session = SSL_get_session((SSL *)ssl))) { - log_info(LD_NET, "No session on TLS?"); - res = CIPHERS_ERR; - goto done; - } - if (!session->ciphers) { + if (!peer_ciphers) { log_info(LD_NET, "No ciphers on session"); res = CIPHERS_ERR; goto done; } /* Now we need to see if there are any ciphers whose presence means we're * dealing with an updated Tor. */ - for (i = 0; i < sk_SSL_CIPHER_num(session->ciphers); ++i) { - SSL_CIPHER *cipher = sk_SSL_CIPHER_value(session->ciphers, i); + for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) { + SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i); const char *ciphername = SSL_CIPHER_get_name(cipher); if (strcmp(ciphername, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA) && strcmp(ciphername, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA) && @@ -1458,8 +1491,8 @@ tor_tls_classify_client_ciphers(const SSL *ssl) v2_or_higher: { const uint16_t *v2_cipher = v2_cipher_list; - for (i = 0; i < sk_SSL_CIPHER_num(session->ciphers); ++i) { - SSL_CIPHER *cipher = sk_SSL_CIPHER_value(session->ciphers, i); + for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) { + SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i); uint16_t id = cipher->id & 0xffff; if (id == 0x00ff) /* extended renegotiation indicator. */ continue; @@ -1480,8 +1513,8 @@ tor_tls_classify_client_ciphers(const SSL *ssl) { smartlist_t *elts = smartlist_new(); char *s; - for (i = 0; i < sk_SSL_CIPHER_num(session->ciphers); ++i) { - SSL_CIPHER *cipher = sk_SSL_CIPHER_value(session->ciphers, i); + for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) { + SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i); const char *ciphername = SSL_CIPHER_get_name(cipher); smartlist_add(elts, (char*)ciphername); } @@ -1504,8 +1537,56 @@ tor_tls_classify_client_ciphers(const SSL *ssl) static int tor_tls_client_is_using_v2_ciphers(const SSL *ssl) { - return tor_tls_classify_client_ciphers(ssl) >= CIPHERS_V2; + SSL_SESSION *session; + if (!(session = SSL_get_session((SSL *)ssl))) { + log_info(LD_NET, "No session on TLS?"); + return CIPHERS_ERR; + } + + return tor_tls_classify_client_ciphers(ssl, session->ciphers) >= CIPHERS_V2; +} + +#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0) +/** Callback to get invoked on a server after we've read the list of ciphers + * the client supports, but before we pick our own ciphersuite. + * + * We can't abuse an info_cb for this, since by the time one of the + * client_hello info_cbs is called, we've already picked which ciphersuite to + * use. + * + * Technically, this function is an abuse of this callback, since the point of + * a session_secret_cb is to try to set up and/or verify a shared-secret for + * authentication on the fly. But as long as we return 0, we won't actually be + * setting up a shared secret, and all will be fine. + */ +static int +tor_tls_session_secret_cb(SSL *ssl, void *secret, int *secret_len, + STACK_OF(SSL_CIPHER) *peer_ciphers, + SSL_CIPHER **cipher, void *arg) +{ + (void) secret; + (void) secret_len; + (void) peer_ciphers; + (void) cipher; + (void) arg; + + if (tor_tls_classify_client_ciphers(ssl, peer_ciphers) == + CIPHERS_UNRESTRICTED) { + SSL_set_cipher_list(ssl, UNRESTRICTED_SERVER_CIPHER_LIST); + } + + SSL_set_session_secret_cb(ssl, NULL, NULL); + + return 0; +} +static void +tor_tls_setup_session_secret_cb(tor_tls_t *tls) +{ + SSL_set_session_secret_cb(tls->ssl, tor_tls_session_secret_cb, NULL); } +#else +#define tor_tls_setup_session_secret_cb(tls) STMT_NIL +#endif /** Invoked when a TLS state changes: log the change at severity 'debug' */ static void @@ -1773,6 +1854,9 @@ tor_tls_new(int sock, int isServer) SSL_set_info_callback(result->ssl, tor_tls_debug_state_callback); } + if (isServer) + tor_tls_setup_session_secret_cb(result); + /* Not expected to get called. */ tls_log_errors(NULL, LOG_WARN, LD_NET, "creating tor_tls_t object"); return result; diff --git a/src/common/tortls.h b/src/common/tortls.h index 7bc6c8e76b..8881827cef 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -129,6 +129,7 @@ int tor_tls_cert_is_valid(int severity, const tor_cert_t *cert, const tor_cert_t *signing_cert, int check_rsa_1024); +const char *tor_tls_get_ciphersuite_name(tor_tls_t *tls); #endif |