Browse Source

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

Nick Mathewson 16 years ago
parent
commit
617843988c
6 changed files with 292 additions and 52 deletions
  1. 8 0
      ChangeLog
  2. 3 3
      doc/TODO
  3. 4 1
      doc/spec/tor-spec.txt
  4. 1 1
      src/common/Makefile.am
  5. 143 0
      src/common/ciphers.inc
  6. 133 47
      src/common/tortls.c

+ 8 - 0
ChangeLog

@@ -19,6 +19,12 @@ Changes in version 0.2.1.1-alpha - 2008-??-??
       service, with the 30 seconds being the current voodoo saying that
       service, with the 30 seconds being the current voodoo saying that
       a descriptor is stable.
       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:
   o Minor features:
     - Allow separate log levels to be configured for different logging
     - Allow separate log levels to be configured for different logging
       domains.  For example, this allows one to log all notices, warnings,
       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
     - Never use OpenSSL compression: it wastes RAM and CPU trying to
       compress cells, which are basically all encrypted, compressed, or
       compress cells, which are basically all encrypted, compressed, or
       both.
       both.
+    - Use the TLS1 hostname extension to more closely resemble browser
+      behavior.
 
 
   o Code simplifications and refactoring:
   o Code simplifications and refactoring:
     - Refactor code using connection_ap_handshake_attach_circuit() to
     - Refactor code using connection_ap_handshake_attach_circuit() to

+ 3 - 3
doc/TODO

@@ -105,10 +105,10 @@ N   - Take our draft research proposal for how to safely collect and
       . Test
       . Test
     - More back-end work:
     - More back-end work:
 N     - Additional TLS-camouflage work (spoofing FF cipher suite, etc.)
 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),
         - 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
           local apache if they don't look like Tor or if they don't
           portknock or whatever.
           portknock or whatever.
       - Get closer to downloading far fewer descriptors
       - Get closer to downloading far fewer descriptors

+ 4 - 1
doc/spec/tor-spec.txt

@@ -174,7 +174,10 @@ see tor-design.pdf.
    handshake is complete, the initiator renegotiates the handshake, with each
    handshake is complete, the initiator renegotiates the handshake, with each
    parties sending a two-certificate chain as in "certificates up-front".
    parties sending a two-certificate chain as in "certificates up-front".
    The initiator's ClientHello MUST include at least once ciphersuite not in
    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
    In "backwards-compatible renegotiation", the connection initiator's
    ClientHello MUST include at least one ciphersuite other than those listed
    ClientHello MUST include at least one ciphersuite other than those listed

+ 1 - 1
src/common/Makefile.am

@@ -13,4 +13,4 @@ libor_a_SOURCES = log.c util.c compat.c container.c mempool.c memarea.c \
 	$(libor_extra_source)
 	$(libor_extra_source)
 libor_crypto_a_SOURCES = crypto.c aes.c tortls.c torgzip.c
 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 - 0
src/common/ciphers.inc

@@ -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

+ 133 - 47
src/common/tortls.c

@@ -101,6 +101,16 @@ 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
+
 /** Helper: compare tor_tls_t objects by its SSL. */
 /** Helper: compare tor_tls_t objects by its SSL. */
 static INLINE int
 static INLINE int
 tor_tls_entries_eq(const tor_tls_t *a, const tor_tls_t *b)
 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.");
     log_warn(LD_MM, "Still have entries in the tlsmap at shutdown.");
   }
   }
   HT_CLEAR(tlsmap, &tlsmap_root);
   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
 /** 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;
   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)
    SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA)
 /* Note: for setting up your own private testing network with link crypto
 /* Note: for setting up your own private testing network with link crypto
  * disabled, set the cipher lists to your cipher list to
  * 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
  * 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. */
 
 
-#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
 #endif
 
 
 #ifndef V2_HANDSHAKE_CLIENT
 #ifndef V2_HANDSHAKE_CLIENT
@@ -730,6 +730,76 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
 }
 }
 #endif
 #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
 /** 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.
  */
  */
@@ -745,12 +815,25 @@ tor_tls_new(int sock, int isServer)
     tor_free(result);
     tor_free(result);
     return NULL;
     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,
   if (!SSL_set_cipher_list(result->ssl,
                      isServer ? SERVER_CIPHER_LIST : CLIENT_CIPHER_LIST)) {
                      isServer ? SERVER_CIPHER_LIST : CLIENT_CIPHER_LIST)) {
+    tls_log_errors(NULL, LOG_WARN, "setting ciphers");
+    log_warn(LD_NET, "WTF?");
     SSL_free(result->ssl);
     SSL_free(result->ssl);
     tor_free(result);
     tor_free(result);
     return NULL;
     return NULL;
   }
   }
+  if (!isServer)
+    rectify_client_ciphers(&result->ssl->cipher_list);
   result->socket = sock;
   result->socket = sock;
 #ifdef USE_BSOCKETS
 #ifdef USE_BSOCKETS
   bio = BIO_new_bsocket(sock, BIO_NOCLOSE);
   bio = BIO_new_bsocket(sock, BIO_NOCLOSE);
@@ -982,7 +1065,10 @@ tor_tls_handshake(tor_tls_t *tls)
       if (cert)
       if (cert)
         X509_free(cert);
         X509_free(cert);
 #endif
 #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;
   return r;