mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-28 06:13:31 +01:00
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.
This commit is contained in:
parent
63208aa1e5
commit
175b2678d7
24
changes/tls_ecdhe
Normal file
24
changes/tls_ecdhe
Normal file
@ -0,0 +1,24 @@
|
||||
o Major features:
|
||||
|
||||
- Servers can now enable the ECDHE TLS ciphersuites when
|
||||
available and appropriate. These ciphersuites, when used with
|
||||
the P-256 elliptic curve, let us negotiate forward-secure TLS
|
||||
secret keys more safely and more efficiently than with our
|
||||
previous use of Diffie Hellman modulo a 1024-bit prime.
|
||||
|
||||
Enabling these ciphers was a little tricky, since for a long
|
||||
time, clients had been claiming to support them without
|
||||
actually doing so, in order to foil fingerprinting. But with
|
||||
the client-side implementation of proposal 198 in
|
||||
0.2.3.17-beta, clients can now match the ciphers from recent
|
||||
firefox versions *and* list the ciphers they actually mean, so
|
||||
servers can believe such clients when they advertise ECDHE
|
||||
support in their TLS ClientHello messages.
|
||||
|
||||
This feature requires clients running 0.2.3.17-beta or later,
|
||||
and requires both sides to be running OpenSSL 1.0.0 or later
|
||||
with ECC support. OpenSSL 1.0.1, with the compile-time option
|
||||
"enable-ec_nistp_64_gcc_128", is highly recommended.
|
||||
Implements the server side of proposal 198; closes ticket
|
||||
7200.
|
||||
|
@ -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,9 +1537,57 @@ 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
|
||||
tor_tls_debug_state_callback(const SSL *ssl, int type, int val)
|
||||
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -1317,7 +1317,8 @@ connection_tls_continue_handshake(or_connection_t *conn)
|
||||
if (conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING) {
|
||||
if (tor_tls_received_v3_certificate(conn->tls)) {
|
||||
log_info(LD_OR, "Client got a v3 cert! Moving on to v3 "
|
||||
"handshake.");
|
||||
"handshake with ciphersuite %s",
|
||||
tor_tls_get_ciphersuite_name(conn->tls));
|
||||
return connection_or_launch_v3_or_handshake(conn);
|
||||
} else {
|
||||
log_debug(LD_OR, "Done with initial SSL handshake (client-side)."
|
||||
@ -1641,10 +1642,12 @@ connection_tls_finish_handshake(or_connection_t *conn)
|
||||
char digest_rcvd[DIGEST_LEN];
|
||||
int started_here = connection_or_nonopen_was_started_here(conn);
|
||||
|
||||
log_debug(LD_HANDSHAKE,"%s tls handshake on %p with %s done. verifying.",
|
||||
log_debug(LD_HANDSHAKE,"%s tls handshake on %p with %s done, using "
|
||||
"ciphersuite %s. verifying.",
|
||||
started_here?"outgoing":"incoming",
|
||||
conn,
|
||||
safe_str_client(conn->base_.address));
|
||||
safe_str_client(conn->base_.address),
|
||||
tor_tls_get_ciphersuite_name(conn->tls));
|
||||
|
||||
directory_set_dirty();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user