|
@@ -101,6 +101,16 @@ struct tor_tls_t {
|
|
|
void *callback_arg;
|
|
|
};
|
|
|
|
|
|
+#ifdef V2_HANDSHAKE_CLIENT
|
|
|
+
|
|
|
+ * in client mode into advertising the ciphers we want. See
|
|
|
+ * rectify_client_ciphers for details. */
|
|
|
+static SSL_CIPHER *CLIENT_CIPHER_DUMMIES = NULL;
|
|
|
+
|
|
|
+ * See rectify_client_ciphers for details. */
|
|
|
+static STACK_OF(SSL_CIPHER) *CLIENT_CIPHER_STACK = NULL;
|
|
|
+#endif
|
|
|
+
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
@@ -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 ":" \
|
|
|
+
|
|
|
+#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)
|
|
|
|
|
|
* 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_DES_192_CBC3_SHA)
|
|
|
-
|
|
|
- * 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
|
|
|
-
|
|
|
- * 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)
|
|
|
+
|
|
|
+ * our openssl doesn't know about. */
|
|
|
+static const char CLIENT_CIPHER_LIST[] =
|
|
|
+#include "./ciphers.inc"
|
|
|
+ ;
|
|
|
+#undef CIPHER
|
|
|
+#undef XCIPHER
|
|
|
+
|
|
|
+
|
|
|
+typedef struct cipher_info_t { unsigned id; const char *name; } cipher_info_t;
|
|
|
+
|
|
|
+ * 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
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+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
|
|
|
|
|
|
+
|
|
|
+ * 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 want.*/
|
|
|
+ int i = 0, j = 0;
|
|
|
+
|
|
|
+
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 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
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
* 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
|
|
|
+
|
|
|
+ {
|
|
|
+ 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;
|