mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-13 06:33:44 +01:00
prop198: Detect the list of ciphersuites we used to lie about having
This is less easy than you might think; we can't just look at the client ciphers list, since openssl doesn't remember client ciphers if it doesn't know about them. So we have to keep a list of the "v2" ciphers, with the ones we don't know about removed.
This commit is contained in:
parent
bbaf4d9643
commit
2a26e1d45f
@ -127,6 +127,24 @@ typedef struct tor_tls_context_t {
|
|||||||
crypto_pk_t *auth_key;
|
crypto_pk_t *auth_key;
|
||||||
} tor_tls_context_t;
|
} tor_tls_context_t;
|
||||||
|
|
||||||
|
/** Return values for tor_tls_classify_client_ciphers.
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
/** An error occurred when examining the client ciphers */
|
||||||
|
#define CIPHERS_ERR -1
|
||||||
|
/** The client cipher list indicates that a v1 handshake was in use. */
|
||||||
|
#define CIPHERS_V1 1
|
||||||
|
/** The client cipher list indicates that the client is using the v2 or the
|
||||||
|
* v3 handshake, but that it is (probably!) lying about what ciphers it
|
||||||
|
* supports */
|
||||||
|
#define CIPHERS_V2 2
|
||||||
|
/** The client cipher list indicates that the client is using the v2 or the
|
||||||
|
* v3 handshake, and that it is telling the truth about what ciphers it
|
||||||
|
* supports */
|
||||||
|
#define CIPHERS_UNRESTRICTED 3
|
||||||
|
/** @} */
|
||||||
|
|
||||||
#define TOR_TLS_MAGIC 0x71571571
|
#define TOR_TLS_MAGIC 0x71571571
|
||||||
|
|
||||||
/** Holds a SSL object and its associated data. Members are only
|
/** Holds a SSL object and its associated data. Members are only
|
||||||
@ -1322,23 +1340,94 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef V2_HANDSHAKE_SERVER
|
#ifdef V2_HANDSHAKE_SERVER
|
||||||
/** Return true iff the cipher list suggested by the client for <b>ssl</b> is
|
|
||||||
* a list that indicates that the client knows how to do the v2 TLS connection
|
/* Here's the old V2 cipher list we sent from 0.2.1.1-alpha up to
|
||||||
* handshake. */
|
* 0.2.3.17-beta. If a client is using this list, we can't believe the ciphers
|
||||||
static int
|
* that it claims to support. We'll prune this list to remove the ciphers
|
||||||
tor_tls_client_is_using_v2_ciphers(const SSL *ssl, const char *address)
|
* *we* don't recognize. */
|
||||||
|
static uint16_t v2_cipher_list[] = {
|
||||||
|
0xc00a, /* TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA */
|
||||||
|
0xc014, /* TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA */
|
||||||
|
0x0039, /* TLS1_TXT_DHE_RSA_WITH_AES_256_SHA */
|
||||||
|
0x0038, /* TLS1_TXT_DHE_DSS_WITH_AES_256_SHA */
|
||||||
|
0xc00f, /* TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA */
|
||||||
|
0xc005, /* TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA */
|
||||||
|
0x0035, /* TLS1_TXT_RSA_WITH_AES_256_SHA */
|
||||||
|
0xc007, /* TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA */
|
||||||
|
0xc009, /* TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA */
|
||||||
|
0xc011, /* TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA */
|
||||||
|
0xc013, /* TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA */
|
||||||
|
0x0033, /* TLS1_TXT_DHE_RSA_WITH_AES_128_SHA */
|
||||||
|
0x0032, /* TLS1_TXT_DHE_DSS_WITH_AES_128_SHA */
|
||||||
|
0xc00c, /* TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA */
|
||||||
|
0xc00e, /* TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA */
|
||||||
|
0xc002, /* TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA */
|
||||||
|
0xc004, /* TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA */
|
||||||
|
0x0004, /* SSL3_TXT_RSA_RC4_128_MD5 */
|
||||||
|
0x0005, /* SSL3_TXT_RSA_RC4_128_SHA */
|
||||||
|
0x002f, /* TLS1_TXT_RSA_WITH_AES_128_SHA */
|
||||||
|
0xc008, /* TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA */
|
||||||
|
0xc012, /* TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA */
|
||||||
|
0x0016, /* SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA */
|
||||||
|
0x0013, /* SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA */
|
||||||
|
0xc00d, /* TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA */
|
||||||
|
0xc003, /* TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA */
|
||||||
|
0xfeff, /* SSL3_TXT_RSA_FIPS_WITH_3DES_EDE_CBC_SHA */
|
||||||
|
0x000a, /* SSL3_TXT_RSA_DES_192_CBC3_SHA */
|
||||||
|
0
|
||||||
|
};
|
||||||
|
/** Have we removed the unrecognized ciphers from v2_cipher_list yet? */
|
||||||
|
static int v2_cipher_list_pruned = 0;
|
||||||
|
|
||||||
|
/** Remove from v2_cipher_list every cipher that we don't support, so that
|
||||||
|
* comparing v2_cipher_list to a client's cipher list will give a sensible
|
||||||
|
* result. */
|
||||||
|
static void
|
||||||
|
prune_v2_cipher_list(void)
|
||||||
{
|
{
|
||||||
int i;
|
uint16_t *inp, *outp;
|
||||||
|
const SSL_METHOD *m = SSLv23_method();
|
||||||
|
|
||||||
|
inp = outp = v2_cipher_list;
|
||||||
|
while (*inp) {
|
||||||
|
unsigned char cipherid[2];
|
||||||
|
const SSL_CIPHER *cipher;
|
||||||
|
/* Is there no better way to do this? */
|
||||||
|
set_uint16(cipherid, htons(*inp));
|
||||||
|
cipher = m->get_cipher_by_char(cipherid);
|
||||||
|
if (cipher) {
|
||||||
|
tor_assert((cipher->id & 0xffff) == *inp);
|
||||||
|
*outp++ = *inp++;
|
||||||
|
} else {
|
||||||
|
inp++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*outp = 0;
|
||||||
|
|
||||||
|
v2_cipher_list_pruned = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 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, const char *address)
|
||||||
|
{
|
||||||
|
int i, res;
|
||||||
SSL_SESSION *session;
|
SSL_SESSION *session;
|
||||||
|
if (PREDICT_UNLIKELY(!v2_cipher_list_pruned))
|
||||||
|
prune_v2_cipher_list();
|
||||||
|
|
||||||
/* If we reached this point, we just got a client hello. See if there is
|
/* If we reached this point, we just got a client hello. See if there is
|
||||||
* a cipher list. */
|
* a cipher list. */
|
||||||
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 0;
|
return CIPHERS_ERR;
|
||||||
}
|
}
|
||||||
if (!session->ciphers) {
|
if (!session->ciphers) {
|
||||||
log_info(LD_NET, "No ciphers on session");
|
log_info(LD_NET, "No ciphers on session");
|
||||||
return 0;
|
return CIPHERS_ERR;
|
||||||
}
|
}
|
||||||
/* Now we need to see if there are any ciphers whose presence means we're
|
/* Now we need to see if there are any ciphers whose presence means we're
|
||||||
* dealing with an updated Tor. */
|
* dealing with an updated Tor. */
|
||||||
@ -1351,11 +1440,32 @@ tor_tls_client_is_using_v2_ciphers(const SSL *ssl, const char *address)
|
|||||||
strcmp(ciphername, "(NONE)")) {
|
strcmp(ciphername, "(NONE)")) {
|
||||||
log_debug(LD_NET, "Got a non-version-1 cipher called '%s'", ciphername);
|
log_debug(LD_NET, "Got a non-version-1 cipher called '%s'", ciphername);
|
||||||
// return 1;
|
// return 1;
|
||||||
goto dump_list;
|
goto v2_or_higher;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return CIPHERS_V1;
|
||||||
dump_list:
|
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);
|
||||||
|
uint16_t id = cipher->id & 0xffff;
|
||||||
|
if (id == 0x00ff) /* extended renegotiation indicator. */
|
||||||
|
continue;
|
||||||
|
if (!id || id != *v2_cipher) {
|
||||||
|
res = CIPHERS_UNRESTRICTED;
|
||||||
|
goto dump_ciphers;
|
||||||
|
}
|
||||||
|
++v2_cipher;
|
||||||
|
}
|
||||||
|
if (*v2_cipher != 0) {
|
||||||
|
res = CIPHERS_UNRESTRICTED;
|
||||||
|
goto dump_ciphers;
|
||||||
|
}
|
||||||
|
res = CIPHERS_V2;
|
||||||
|
}
|
||||||
|
|
||||||
|
dump_ciphers:
|
||||||
{
|
{
|
||||||
smartlist_t *elts = smartlist_new();
|
smartlist_t *elts = smartlist_new();
|
||||||
char *s;
|
char *s;
|
||||||
@ -1365,12 +1475,21 @@ tor_tls_client_is_using_v2_ciphers(const SSL *ssl, const char *address)
|
|||||||
smartlist_add(elts, (char*)ciphername);
|
smartlist_add(elts, (char*)ciphername);
|
||||||
}
|
}
|
||||||
s = smartlist_join_strings(elts, ":", 0, NULL);
|
s = smartlist_join_strings(elts, ":", 0, NULL);
|
||||||
log_debug(LD_NET, "Got a non-version-1 cipher list from %s. It is: '%s'",
|
log_debug(LD_NET, "Got a %s V2/V3 cipher list from %s. It is: '%s'",
|
||||||
address, s);
|
(res == CIPHERS_V2) ? "fictitious" : "real", address, s);
|
||||||
tor_free(s);
|
tor_free(s);
|
||||||
smartlist_free(elts);
|
smartlist_free(elts);
|
||||||
}
|
}
|
||||||
return 1;
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return true iff the cipher list suggested by the client for <b>ssl</b> is
|
||||||
|
* a list that indicates that the client knows how to do the v2 TLS connection
|
||||||
|
* handshake. */
|
||||||
|
static int
|
||||||
|
tor_tls_client_is_using_v2_ciphers(const SSL *ssl, const char *address)
|
||||||
|
{
|
||||||
|
return tor_tls_classify_client_ciphers(ssl, address) >= CIPHERS_V2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Invoked when a TLS state changes: log the change at severity 'debug' */
|
/** Invoked when a TLS state changes: log the change at severity 'debug' */
|
||||||
|
Loading…
Reference in New Issue
Block a user