r16215@tombo: nickm | 2008-06-12 18:39:03 -0400

Implement code to manually force the OpenSSL client cipher list to match the one recommended in proposal 124, *even if* we do not know all those ciphers.  This is a bit of a kludge, but it is at least decently well commented.


svn:r15173
This commit is contained in:
Nick Mathewson 2008-06-12 22:39:13 +00:00
parent 555450ba73
commit 617843988c
6 changed files with 292 additions and 52 deletions

View File

@ -19,6 +19,12 @@ Changes in version 0.2.1.1-alpha - 2008-??-??
service, with the 30 seconds being the current voodoo saying that
a descriptor is stable.
o Major features:
- Modify the list of ciphers advertised by OpenSSL in client mode
to even more closely resemble a common web browser. We cheat a
little so that we can advertise ciphers that the locally
installed OpenSSL doesn't know about.
o Minor features:
- Allow separate log levels to be configured for different logging
domains. For example, this allows one to log all notices, warnings,
@ -68,6 +74,8 @@ Changes in version 0.2.1.1-alpha - 2008-??-??
- Never use OpenSSL compression: it wastes RAM and CPU trying to
compress cells, which are basically all encrypted, compressed, or
both.
- Use the TLS1 hostname extension to more closely resemble browser
behavior.
o Code simplifications and refactoring:
- Refactor code using connection_ap_handshake_attach_circuit() to

View File

@ -105,10 +105,10 @@ N - Take our draft research proposal for how to safely collect and
. Test
- More back-end work:
N - Additional TLS-camouflage work (spoofing FF cipher suite, etc.)
- spoof the cipher suites
- spoof the extensions list
o spoof the cipher suites
o spoof the extensions list
- red-team testing (a.k.a, look at a packet dump and compare),
- investigate the feasibility of handing connections off to a
. investigate the feasibility of handing connections off to a
local apache if they don't look like Tor or if they don't
portknock or whatever.
- Get closer to downloading far fewer descriptors

View File

@ -174,7 +174,10 @@ see tor-design.pdf.
handshake is complete, the initiator renegotiates the handshake, with each
parties sending a two-certificate chain as in "certificates up-front".
The initiator's ClientHello MUST include at least once ciphersuite not in
the list above.
the list above. The responder SHOULD NOT select any ciphersuite besides
those in the list above.
[The above "should not" is because some of the ciphers that
clients list may be fake.]
In "backwards-compatible renegotiation", the connection initiator's
ClientHello MUST include at least one ciphersuite other than those listed

View File

@ -13,4 +13,4 @@ libor_a_SOURCES = log.c util.c compat.c container.c mempool.c memarea.c \
$(libor_extra_source)
libor_crypto_a_SOURCES = crypto.c aes.c tortls.c torgzip.c
noinst_HEADERS = log.h crypto.h test.h util.h compat.h aes.h torint.h tortls.h strlcpy.c strlcat.c torgzip.h container.h ht.h mempool.h memarea.h
noinst_HEADERS = log.h crypto.h test.h util.h compat.h aes.h torint.h tortls.h strlcpy.c strlcat.c torgzip.h container.h ht.h mempool.h memarea.h ciphers.inc

143
src/common/ciphers.inc Normal file
View File

@ -0,0 +1,143 @@
/* This is an include file used to define the list of ciphers clients should
* advertise. Before including it, you should define the CIPHER and XCPIHER
* macros. */
#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
CIPHER(0xc00a, TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA)
#else
XCIPHER(0xc00a, TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA)
#endif
#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA
CIPHER(0xc014, TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA)
#else
XCIPHER(0xc014, TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA)
#endif
#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_SHA
CIPHER(0x0039, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA)
#else
XCIPHER(0x0039, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA)
#endif
#ifdef TLS1_TXT_DHE_DSS_WITH_AES_256_SHA
CIPHER(0x0038, TLS1_TXT_DHE_DSS_WITH_AES_256_SHA)
#else
XCIPHER(0x0038, TLS1_TXT_DHE_DSS_WITH_AES_256_SHA)
#endif
#ifdef TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA
CIPHER(0xc00f, TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA)
#else
XCIPHER(0xc00f, TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA)
#endif
#ifdef TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA
CIPHER(0xc005, TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA)
#else
XCIPHER(0xc005, TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA)
#endif
#ifdef TLS1_TXT_RSA_WITH_AES_256_SHA
CIPHER(0x0035, TLS1_TXT_RSA_WITH_AES_256_SHA)
#else
XCIPHER(0x0035, TLS1_TXT_RSA_WITH_AES_256_SHA)
#endif
#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA
CIPHER(0xc007, TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA)
#else
XCIPHER(0xc007, TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA)
#endif
#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
CIPHER(0xc009, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA)
#else
XCIPHER(0xc009, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA)
#endif
#ifdef TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA
CIPHER(0xc011, TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA)
#else
XCIPHER(0xc011, TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA)
#endif
#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA
CIPHER(0xc013, TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA)
#else
XCIPHER(0xc013, TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA)
#endif
#ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_SHA
CIPHER(0x0033, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA)
#else
XCIPHER(0x0033, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA)
#endif
#ifdef TLS1_TXT_DHE_DSS_WITH_AES_128_SHA
CIPHER(0x0032, TLS1_TXT_DHE_DSS_WITH_AES_128_SHA)
#else
XCIPHER(0x0032, TLS1_TXT_DHE_DSS_WITH_AES_128_SHA)
#endif
#ifdef TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA
CIPHER(0xc00c, TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA)
#else
XCIPHER(0xc00c, TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA)
#endif
#ifdef TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA
CIPHER(0xc00e, TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA)
#else
XCIPHER(0xc00e, TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA)
#endif
#ifdef TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA
CIPHER(0xc002, TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA)
#else
XCIPHER(0xc002, TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA)
#endif
#ifdef TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA
CIPHER(0xc004, TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA)
#else
XCIPHER(0xc004, TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA)
#endif
#ifdef SSL3_TXT_RSA_RC4_128_MD5
CIPHER(0x0004, SSL3_TXT_RSA_RC4_128_MD5)
#else
XCIPHER(0x0004, SSL3_TXT_RSA_RC4_128_MD5)
#endif
#ifdef SSL3_TXT_RSA_RC4_128_SHA
CIPHER(0x0005, SSL3_TXT_RSA_RC4_128_SHA)
#else
XCIPHER(0x0005, SSL3_TXT_RSA_RC4_128_SHA)
#endif
#ifdef TLS1_TXT_RSA_WITH_AES_128_SHA
CIPHER(0x002f, TLS1_TXT_RSA_WITH_AES_128_SHA)
#else
XCIPHER(0x002f, TLS1_TXT_RSA_WITH_AES_128_SHA)
#endif
#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA
CIPHER(0xc008, TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA)
#else
XCIPHER(0xc008, TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA)
#endif
#ifdef TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA
CIPHER(0xc012, TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA)
#else
XCIPHER(0xc012, TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA)
#endif
#ifdef SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA
CIPHER(0x0016, SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA)
#else
XCIPHER(0x0016, SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA)
#endif
#ifdef SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA
CIPHER(0x0013, SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA)
#else
XCIPHER(0x0013, SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA)
#endif
#ifdef TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA
CIPHER(0xc00d, TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA)
#else
XCIPHER(0xc00d, TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA)
#endif
#ifdef TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA
CIPHER(0xc003, TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA)
#else
XCIPHER(0xc003, TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA)
#endif
#ifdef SSL3_TXT_RSA_FIPS_WITH_3DES_EDE_CBC_SHA
CIPHER(0xfeff, SSL3_TXT_RSA_FIPS_WITH_3DES_EDE_CBC_SHA)
#else
XCIPHER(0xfeff, SSL3_TXT_RSA_FIPS_WITH_3DES_EDE_CBC_SHA)
#endif
#ifdef SSL3_TXT_RSA_DES_192_CBC3_SHA
CIPHER(0x000a, SSL3_TXT_RSA_DES_192_CBC3_SHA)
#else
XCIPHER(0x000a, SSL3_TXT_RSA_DES_192_CBC3_SHA)
#endif

View File

@ -101,6 +101,16 @@ struct tor_tls_t {
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
/** Helper: compare tor_tls_t objects by its SSL. */
static INLINE int
tor_tls_entries_eq(const tor_tls_t *a, const tor_tls_t *b)
@ -318,6 +328,12 @@ tor_tls_free_all(void)
log_warn(LD_MM, "Still have entries in the tlsmap at shutdown.");
}
HT_CLEAR(tlsmap, &tlsmap_root);
#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
@ -427,58 +443,42 @@ tor_tls_create_certificate(crypto_pk_env_t *rsa,
return x509;
}
#define SERVER_CIPHER_LIST \
(TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":" \
TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":" \
/** List of ciphers that servers should select from.*/
#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)
/* Note: for setting up your own private testing network with link crypto
* disabled, set the cipher lists to your cipher list to
* SSL3_TXT_RSA_NULL_SHA. If you do this, you won't be able to communicate
* with any of the "real" Tors, though. */
#if OPENSSL_VERSION_NUMBER >= 0x00908020l
#define CLIENT_CIPHER_LIST \
(TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA ":" \
TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA ":" \
TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":" \
TLS1_TXT_DHE_DSS_WITH_AES_256_SHA ":" \
TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA ":" \
TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA ":" \
TLS1_TXT_RSA_WITH_AES_256_SHA ":" \
TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA ":" \
TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA ":" \
TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA ":" \
TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA ":" \
TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":" \
TLS1_TXT_DHE_DSS_WITH_AES_128_SHA ":" \
TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA":" \
TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA":" \
TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA ":" \
TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA ":" \
SSL3_TXT_RSA_RC4_128_MD5 ":" \
SSL3_TXT_RSA_RC4_128_SHA ":" \
TLS1_TXT_RSA_WITH_AES_128_SHA ":" \
TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA ":" \
TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA ":" \
SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA ":" \
SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA ":" \
TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA ":" \
TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA ":" \
/*SSL3_TXT_RSA_FIPS_WITH_3DES_EDE_CBC_SHA ":"*/ \
SSL3_TXT_RSA_DES_192_CBC3_SHA)
/* SSL3_TXT_RSA_FIPS_WITH_3DES_EDE_CBC_SHA is commented out because it doesn't
* really exist; if I understand correctly, it's a bit of silliness that
* netscape did on its own before any standard for what they wanted was
* formally approved. Nonetheless, Firefox still uses it, so we need to
* fake it at some point soon. XXXX021 -NM */
#else
/* Ug. We don't have as many ciphers with openssl 0.9.7 as we'd like. Fix
* this list into something that sucks less. */
#define CLIENT_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 ":" \
SSL3_TXT_RSA_RC4_128_SHA)
#ifdef V2_HANDSHAKE_CLIENT
#define CIPHER(id, name) name ":"
#define XCIPHER(id, name)
/** List of ciphers that clients should advertise, omitting items that
* our openssl doesn't know about. */
static const char CLIENT_CIPHER_LIST[] =
#include "./ciphers.inc"
;
#undef CIPHER
#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 =
sizeof(CLIENT_CIPHER_INFO_LIST)/sizeof(CLIENT_CIPHER_INFO_LIST[0]);
#endif
#ifndef V2_HANDSHAKE_CLIENT
@ -730,6 +730,76 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
}
#endif
/** Replace *<b>ciphers</b> with a new list of SSL ciphersuites: specifically,
* a list designed to mimic a common web browser. Some of the cipher 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 <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(STACK_OF(SSL_CIPHER) **ciphers)
{
#ifdef V2_HANDSHAKE_CLIENT
if (PREDICT_UNLIKELY(!CLIENT_CIPHER_STACK)) {
/* We need to set CLIENT_CIPHER_STACK to an array of the ciphers
* we want.*/
int i = 0, j = 0;
/* 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;
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, cipher->id, cipher->name);
}
/* Then copy as many ciphers as we can from the good list, inserting
* dummies as needed. */
j=0;
for (i = 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 && ((cipher->id >> 24) & 0xff) != 3) {
log_debug(LD_NET, "Skipping v2 cipher %s", cipher->name);
++j;
} else if (cipher &&
(cipher->id & 0xffff) == CLIENT_CIPHER_INFO_LIST[i].id) {
log_debug(LD_NET, "Found cipher %s", cipher->name);
sk_SSL_CIPHER_push(CLIENT_CIPHER_STACK, cipher);
++j;
++i;
} else {
log_debug(LD_NET, "Inserting fake %s", CLIENT_CIPHER_DUMMIES[i].name);
sk_SSL_CIPHER_push(CLIENT_CIPHER_STACK, &CLIENT_CIPHER_DUMMIES[i]);
++i;
}
}
}
sk_SSL_CIPHER_free(*ciphers);
*ciphers = sk_SSL_CIPHER_dup(CLIENT_CIPHER_STACK);
tor_assert(*ciphers);
#else
(void)ciphers;
#endif
}
/** Create a new TLS object from a file descriptor, and a flag to
* determine whether it is functioning as a server.
*/
@ -745,12 +815,25 @@ tor_tls_new(int sock, int isServer)
tor_free(result);
return NULL;
}
#ifdef SSL_set_tlsext_host_name
/* Browsers use the TLS hostname extension, so we should too. */
{
char *fake_hostname = crypto_random_hostname(4,25, "www.",".com");
SSL_set_tlsext_host_name(result->ssl, fake_hostname);
}
#endif
if (!SSL_set_cipher_list(result->ssl,
isServer ? SERVER_CIPHER_LIST : CLIENT_CIPHER_LIST)) {
tls_log_errors(NULL, LOG_WARN, "setting ciphers");
log_warn(LD_NET, "WTF?");
SSL_free(result->ssl);
tor_free(result);
return NULL;
}
if (!isServer)
rectify_client_ciphers(&result->ssl->cipher_list);
result->socket = sock;
#ifdef USE_BSOCKETS
bio = BIO_new_bsocket(sock, BIO_NOCLOSE);
@ -982,7 +1065,10 @@ tor_tls_handshake(tor_tls_t *tls)
if (cert)
X509_free(cert);
#endif
SSL_set_cipher_list(tls->ssl, SERVER_CIPHER_LIST);
if (SSL_set_cipher_list(tls->ssl, SERVER_CIPHER_LIST) == 0) {
tls_log_errors(NULL, LOG_WARN, "re-setting ciphers");
r = TOR_TLS_ERROR_MISC;
}
}
}
return r;