Merge remote-tracking branch 'public/bug15760_hard_026_v2'

Conflicts:
	src/common/tortls.c
This commit is contained in:
Nick Mathewson 2015-06-02 13:45:27 -04:00
commit 0030765e04
2 changed files with 109 additions and 198 deletions

View File

@ -610,10 +610,28 @@ else
fi fi
AC_SUBST(TOR_OPENSSL_LIBS) AC_SUBST(TOR_OPENSSL_LIBS)
dnl Now check for particular openssl functions.
save_LIBS="$LIBS"
save_LDFLAGS="$LDFLAGS"
save_CPPFLAGS="$CPPFLAGS"
LIBS="$TOR_OPENSSL_LIBS $LIBS"
LDFLAGS="$TOR_LDFLAGS_openssl $LDFLAGS"
CPPFLAGS="$TOR_CPPFLAGS_openssl $CPPFLAGS"
AC_CHECK_MEMBERS([struct ssl_method_st.get_cipher_by_char], , , AC_CHECK_MEMBERS([struct ssl_method_st.get_cipher_by_char], , ,
[#include <openssl/ssl.h> [#include <openssl/ssl.h>
]) ])
AC_CHECK_FUNCS([ \
SSL_SESSION_get_master_key \
SSL_get_server_random \
SSL_get_client_ciphers \
SSL_get_client_random \
SSL_CIPHER_find \
])
LIBS="$save_LIBS"
LDFLAGS="$save_LDFLAGS"
CPPFLAGS="$save_CPPFLAGS"
dnl ------------------------------------------------------ dnl ------------------------------------------------------
dnl Where do you live, zlib? And how do we call you? dnl Where do you live, zlib? And how do we call you?

View File

@ -205,16 +205,6 @@ struct tor_tls_t {
void *callback_arg; void *callback_arg;
}; };
#ifdef V2_HANDSHAKE_CLIENT
/** 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. */
static SSL_CIPHER *CLIENT_CIPHER_DUMMIES = NULL;
/** A stack of SSL_CIPHER objects, some real, some fake.
* See rectify_client_ciphers() for details. */
static STACK_OF(SSL_CIPHER) *CLIENT_CIPHER_STACK = NULL;
#endif
/** The ex_data index in which we store a pointer to an SSL object's /** The ex_data index in which we store a pointer to an SSL object's
* corresponding tor_tls_t object. */ * corresponding tor_tls_t object. */
static int tor_tls_object_ex_data_index = -1; static int tor_tls_object_ex_data_index = -1;
@ -531,12 +521,6 @@ tor_tls_free_all(void)
client_tls_context = NULL; client_tls_context = NULL;
tor_tls_context_decref(ctx); tor_tls_context_decref(ctx);
} }
#ifdef V2_HANDSHAKE_CLIENT
if (CLIENT_CIPHER_DUMMIES)
tor_free(CLIENT_CIPHER_DUMMIES);
if (CLIENT_CIPHER_STACK)
sk_SSL_CIPHER_free(CLIENT_CIPHER_STACK);
#endif
} }
/** We need to give OpenSSL a callback to verify certificates. This is /** We need to give OpenSSL a callback to verify certificates. This is
@ -727,7 +711,6 @@ const char UNRESTRICTED_SERVER_CIPHER_LIST[] =
* (SSL3_TXT_RSA_NULL_SHA). If you do this, you won't be able to communicate * (SSL3_TXT_RSA_NULL_SHA). If you do this, you won't be able to communicate
* with any of the "real" Tors, though. */ * with any of the "real" Tors, though. */
#ifdef V2_HANDSHAKE_CLIENT
#define CIPHER(id, name) name ":" #define CIPHER(id, name) name ":"
#define XCIPHER(id, name) #define XCIPHER(id, name)
/** List of ciphers that clients should advertise, omitting items that /** List of ciphers that clients should advertise, omitting items that
@ -741,28 +724,6 @@ static const char CLIENT_CIPHER_LIST[] =
#undef CIPHER #undef CIPHER
#undef XCIPHER #undef XCIPHER
/** Holds a cipher that we want to advertise, and its 2-byte ID. */
typedef struct cipher_info_t { unsigned id; const char *name; } cipher_info_t;
/** A list of all the ciphers that clients should advertise, including items
* that OpenSSL might not know about. */
static const cipher_info_t CLIENT_CIPHER_INFO_LIST[] = {
#define CIPHER(id, name) { id, name },
#define XCIPHER(id, name) { id, #name },
#include "ciphers.inc"
#undef CIPHER
#undef XCIPHER
};
/** The length of CLIENT_CIPHER_INFO_LIST and CLIENT_CIPHER_DUMMIES. */
static const int N_CLIENT_CIPHERS = ARRAY_LENGTH(CLIENT_CIPHER_INFO_LIST);
#endif
#ifndef V2_HANDSHAKE_CLIENT
#undef CLIENT_CIPHER_LIST
#define CLIENT_CIPHER_LIST (TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":" \
SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA)
#endif
/** Free all storage held in <b>cert</b> */ /** Free all storage held in <b>cert</b> */
void void
tor_x509_cert_free(tor_x509_cert_t *cert) tor_x509_cert_free(tor_x509_cert_t *cert)
@ -1440,7 +1401,7 @@ static int
find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, uint16_t cipher) find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, uint16_t cipher)
{ {
const SSL_CIPHER *c; const SSL_CIPHER *c;
#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,2) #ifdef HAVE_SSL_CIPHER_FIND
{ {
unsigned char cipherid[3]; unsigned char cipherid[3];
tor_assert(ssl); tor_assert(ssl);
@ -1597,13 +1558,19 @@ tor_tls_classify_client_ciphers(const SSL *ssl,
static int static int
tor_tls_client_is_using_v2_ciphers(const SSL *ssl) tor_tls_client_is_using_v2_ciphers(const SSL *ssl)
{ {
STACK_OF(SSL_CIPHER) *ciphers;
#ifdef HAVE_SSL_GET_CLIENT_CIPHERS
ciphers = SSL_get_ciphers(ssl);
#else
SSL_SESSION *session; SSL_SESSION *session;
if (!(session = SSL_get_session((SSL *)ssl))) { if (!(session = SSL_get_session((SSL *)ssl))) {
log_info(LD_NET, "No session on TLS?"); log_info(LD_NET, "No session on TLS?");
return CIPHERS_ERR; return CIPHERS_ERR;
} }
ciphers = session->ciphers;
#endif
return tor_tls_classify_client_ciphers(ssl, session->ciphers) >= CIPHERS_V2; return tor_tls_classify_client_ciphers(ssl, ciphers) >= CIPHERS_V2;
} }
/** Invoked when we're accepting a connection on <b>ssl</b>, and the connection /** Invoked when we're accepting a connection on <b>ssl</b>, and the connection
@ -1702,150 +1669,6 @@ tor_tls_setup_session_secret_cb(tor_tls_t *tls)
SSL_set_session_secret_cb(tls->ssl, tor_tls_session_secret_cb, NULL); SSL_set_session_secret_cb(tls->ssl, tor_tls_session_secret_cb, NULL);
} }
/** Explain which ciphers we're missing. */
static void
log_unsupported_ciphers(smartlist_t *unsupported)
{
char *joined;
log_notice(LD_NET, "We weren't able to find support for all of the "
"TLS ciphersuites that we wanted to advertise. This won't "
"hurt security, but it might make your Tor (if run as a client) "
"more easy for censors to block.");
if (SSLeay() < 0x10000000L) {
log_notice(LD_NET, "To correct this, use a more recent OpenSSL, "
"built without disabling any secure ciphers or features.");
} else {
log_notice(LD_NET, "To correct this, use a version of OpenSSL "
"built with none of its ciphers disabled.");
}
joined = smartlist_join_strings(unsupported, ":", 0, NULL);
log_info(LD_NET, "The unsupported ciphers were: %s", joined);
tor_free(joined);
}
static void
set_ssl_ciphers_to_list(SSL *ssl, STACK_OF(SSL_CIPHER) *stack)
{
STACK_OF(SSL_CIPHER) *ciphers;
int r, i;
/* #1: ensure that the ssl object has its own list of ciphers. Otherwise we
* might be about to stomp the SSL_CTX ciphers list. */
r = SSL_set_cipher_list(ssl, "HIGH");
tor_assert(r);
/* #2: Grab ssl_ciphers and clear it. */
ciphers = SSL_get_ciphers(ssl);
tor_assert(ciphers);
sk_SSL_CIPHER_zero(ciphers);
/* #3: Copy the elements from stack. */
for (i = 0; i < sk_SSL_CIPHER_num(stack); ++i) {
SSL_CIPHER *c = sk_SSL_CIPHER_value(stack, i);
sk_SSL_CIPHER_push(ciphers, c);
}
}
/** Replace the ciphers on <b>ssl</b> with a new list of SSL ciphersuites:
* specifically, a list designed to mimic a common web browser. We might not
* be able to do that if OpenSSL doesn't support all the ciphers we want.
* 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.
*
* [If the server <b>does</b> 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.]
*/
static void
rectify_client_ciphers(SSL *ssl)
{
#ifdef V2_HANDSHAKE_CLIENT
if (PREDICT_UNLIKELY(!CLIENT_CIPHER_STACK)) {
STACK_OF(SSL_CIPHER) *ciphers = SSL_get_ciphers(ssl);
/* We need to set CLIENT_CIPHER_STACK to an array of the ciphers
* we want to use/advertise. */
int i = 0, j = 0;
smartlist_t *unsupported = smartlist_new();
/* First, create a dummy SSL_CIPHER for every cipher. */
CLIENT_CIPHER_DUMMIES =
tor_malloc_zero(sizeof(SSL_CIPHER)*N_CLIENT_CIPHERS);
for (i=0; i < N_CLIENT_CIPHERS; ++i) {
CLIENT_CIPHER_DUMMIES[i].valid = 1;
/* The "3<<24" here signifies that the cipher is supposed to work with
* SSL3 and TLS1. */
CLIENT_CIPHER_DUMMIES[i].id = CLIENT_CIPHER_INFO_LIST[i].id | (3<<24);
CLIENT_CIPHER_DUMMIES[i].name = CLIENT_CIPHER_INFO_LIST[i].name;
}
CLIENT_CIPHER_STACK = sk_SSL_CIPHER_new_null();
tor_assert(CLIENT_CIPHER_STACK);
log_debug(LD_NET, "List was: %s", CLIENT_CIPHER_LIST);
for (j = 0; j < sk_SSL_CIPHER_num(ciphers); ++j) {
SSL_CIPHER *cipher = sk_SSL_CIPHER_value(ciphers, j);
log_debug(LD_NET, "Cipher %d: %lx %s", j,
SSL_CIPHER_get_id(cipher), SSL_CIPHER_get_name(cipher));
}
/* Then copy as many ciphers as we can from the good list, inserting
* dummies as needed. Let j be an index into list of ciphers we have
* (ciphers) and let i be an index into the ciphers we want
* (CLIENT_INFO_CIPHER_LIST). We are building a list of ciphers in
* CLIENT_CIPHER_STACK.
*/
for (i = j = 0; i < N_CLIENT_CIPHERS; ) {
SSL_CIPHER *cipher = NULL;
if (j < sk_SSL_CIPHER_num(ciphers))
cipher = sk_SSL_CIPHER_value(ciphers, j);
if (cipher && ((SSL_CIPHER_get_id(cipher) >> 24) & 0xff) != 3) {
/* Skip over non-v3 ciphers entirely. (This should no longer be
* needed, thanks to saying !SSLv2 above.) */
log_debug(LD_NET, "Skipping v%d cipher %s",
(int)((SSL_CIPHER_get_id(cipher)>>24) & 0xff),
SSL_CIPHER_get_name(cipher));
++j;
} else if (cipher &&
(SSL_CIPHER_get_id(cipher) & 0xffff) ==
CLIENT_CIPHER_INFO_LIST[i].id) {
/* "cipher" is the cipher we expect. Put it on the list. */
log_debug(LD_NET, "Found cipher %s", SSL_CIPHER_get_name(cipher));
sk_SSL_CIPHER_push(CLIENT_CIPHER_STACK, cipher);
++j;
++i;
} else if (!strcmp(CLIENT_CIPHER_DUMMIES[i].name,
"SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA")) {
/* We found bogus cipher 0xfeff, which OpenSSL doesn't support and
* never has. For this one, we need a dummy. */
log_debug(LD_NET, "Inserting fake %s", CLIENT_CIPHER_DUMMIES[i].name);
sk_SSL_CIPHER_push(CLIENT_CIPHER_STACK, &CLIENT_CIPHER_DUMMIES[i]);
++i;
} else {
/* OpenSSL doesn't have this one. */
log_debug(LD_NET, "Completely omitting unsupported cipher %s",
CLIENT_CIPHER_INFO_LIST[i].name);
smartlist_add(unsupported, (char*) CLIENT_CIPHER_INFO_LIST[i].name);
++i;
}
}
if (smartlist_len(unsupported))
log_unsupported_ciphers(unsupported);
smartlist_free(unsupported);
}
set_ssl_ciphers_to_list(ssl, CLIENT_CIPHER_STACK);
#else
(void)ciphers;
#endif
}
/** Create a new TLS object from a file descriptor, and a flag to /** Create a new TLS object from a file descriptor, and a flag to
* determine whether it is functioning as a server. * determine whether it is functioning as a server.
*/ */
@ -1885,8 +1708,6 @@ tor_tls_new(int sock, int isServer)
tor_free(result); tor_free(result);
goto err; goto err;
} }
if (!isServer)
rectify_client_ciphers(result->ssl);
result->socket = sock; result->socket = sock;
bio = BIO_new_socket(sock, BIO_NOCLOSE); bio = BIO_new_socket(sock, BIO_NOCLOSE);
if (! bio) { if (! bio) {
@ -2769,6 +2590,46 @@ tor_tls_server_got_renegotiate(tor_tls_t *tls)
return tls->got_renegotiate; return tls->got_renegotiate;
} }
#ifndef HAVE_SSL_GET_CLIENT_RANDOM
static size_t
SSL_get_client_random(SSL *s, uint8_t *out, size_t len)
{
if (len == 0)
return SSL3_RANDOM_SIZE;
tor_assert(len == SSL3_RANDOM_SIZE);
tor_assert(s->s3);
memcpy(out, s->s3->client_random, len);
return len;
}
#endif
#ifndef HAVE_SSL_GET_SERVER_RANDOM
static size_t
SSL_get_server_random(SSL *s, uint8_t *out, size_t len)
{
if (len == 0)
return SSL3_RANDOM_SIZE;
tor_assert(len == SSL3_RANDOM_SIZE);
tor_assert(s->s3);
memcpy(out, s->s3->server_random, len);
return len;
}
#endif
#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY
static size_t
SSL_SESSION_get_master_key(SSL_SESSION *s, uint8_t *out, size_t len)
{
if (len == 0)
return s->master_key_length;
tor_assert(len == (size_t)s->master_key_length);
tor_assert(s->master_key);
memcpy(out, s->master_key, len);
return len;
}
#endif
/** Set the DIGEST256_LEN buffer at <b>secrets_out</b> to the value used in /** Set the DIGEST256_LEN buffer at <b>secrets_out</b> to the value used in
* the v3 handshake to prove that the client knows the TLS secrets for the * the v3 handshake to prove that the client knows the TLS secrets for the
* connection <b>tls</b>. Return 0 on success, -1 on failure. * connection <b>tls</b>. Return 0 on success, -1 on failure.
@ -2777,25 +2638,57 @@ MOCK_IMPL(int,
tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out)) tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out))
{ {
#define TLSSECRET_MAGIC "Tor V3 handshake TLS cross-certification" #define TLSSECRET_MAGIC "Tor V3 handshake TLS cross-certification"
char buf[128]; uint8_t buf[128];
size_t len; size_t len;
tor_assert(tls); tor_assert(tls);
tor_assert(tls->ssl);
tor_assert(tls->ssl->s3); SSL *const ssl = tls->ssl;
tor_assert(tls->ssl->session); SSL_SESSION *const session = SSL_get_session(ssl);
tor_assert(ssl);
tor_assert(session);
const size_t server_random_len = SSL_get_server_random(ssl, NULL, 0);
const size_t client_random_len = SSL_get_client_random(ssl, NULL, 0);
const size_t master_key_len = SSL_SESSION_get_master_key(session, NULL, 0);
tor_assert(server_random_len);
tor_assert(client_random_len);
tor_assert(master_key_len);
len = client_random_len + server_random_len + strlen(TLSSECRET_MAGIC) + 1;
tor_assert(len <= sizeof(buf));
{
size_t r = SSL_get_client_random(ssl, buf, client_random_len);
tor_assert(r == client_random_len);
}
{
size_t r = SSL_get_server_random(ssl, buf+client_random_len, server_random_len);
tor_assert(r == server_random_len);
}
uint8_t *master_key = tor_malloc_zero(master_key_len);
{
size_t r = SSL_SESSION_get_master_key(session, master_key, master_key_len);
tor_assert(r == master_key_len);
}
uint8_t *nextbuf = buf + client_random_len + server_random_len;
memcpy(nextbuf, TLSSECRET_MAGIC, strlen(TLSSECRET_MAGIC) + 1);
/* /*
The value is an HMAC, using the TLS master key as the HMAC key, of The value is an HMAC, using the TLS master key as the HMAC key, of
client_random | server_random | TLSSECRET_MAGIC client_random | server_random | TLSSECRET_MAGIC
*/ */
memcpy(buf + 0, tls->ssl->s3->client_random, 32);
memcpy(buf + 32, tls->ssl->s3->server_random, 32);
memcpy(buf + 64, TLSSECRET_MAGIC, strlen(TLSSECRET_MAGIC) + 1);
len = 64 + strlen(TLSSECRET_MAGIC) + 1;
crypto_hmac_sha256((char*)secrets_out, crypto_hmac_sha256((char*)secrets_out,
(char*)tls->ssl->session->master_key, (char*)master_key,
tls->ssl->session->master_key_length, master_key_len,
buf, len); (char*)buf, len);
memwipe(buf, 0, sizeof(buf)); memwipe(buf, 0, sizeof(buf));
memwipe(master_key, 0, master_key_len);
tor_free(master_key);
return 0; return 0;
} }