Quantcast

[openssl.org #3068] [PATCH] Safari broken ECDHE-ECDSA workaround

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

[openssl.org #3068] [PATCH] Safari broken ECDHE-ECDSA workaround

Nick Mathewson via RT
The Safari browser on OSX versions 10.8 to 10.8.3 advertises support for
several ECDHE-ECDSA ciphers but fails to negotiate them.

When a Safari client connects to an OpenSSL-based server that has the
attached patch (against the "master" branch) applied, the server will
prefer other mutually supported ciphers above ECDHE-ECDSA ciphers.
This patch enables a webserver to have an ECC certificate together with
an RSA and/or DSA certificate, and to offer ECDHE-ECDSA ciphers without
fear of breaking compatibility with Safari clients.

The ssl_check_for_safari() function, which fingerprints Safari clients
based on the TLS Extensions used, was written by Adam Langley.

A representative from Apple has told me that the Safari bug will be
fixed in OSX 10.8.4.  However, since OSX users won't be forced to
upgrade, I believe that a significant number of users will still be
using affected Safari versions a few years from now.

--
Rob Stradling
Senior Research & Development Scientist
COMODO - Creating Trust Online



diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index 7ad8a54..fff73eb 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -3076,7 +3076,10 @@ void ssl3_clear(SSL *s)
  OPENSSL_free(s->s3->tlsext_authz_client_types);
  s->s3->tlsext_authz_client_types = NULL;
  }
-#endif
+#ifndef OPENSSL_NO_EC
+ s->s3->is_probably_safari = 0;
+#endif /* OPENSSL_NO_EC */
+#endif /* OPENSSL_NO_TLSEXT */
 
  rp = s->s3->rbuf.buf;
  wp = s->s3->wbuf.buf;
@@ -4145,8 +4148,15 @@ SSL_CIPHER *ssl3_choose_cipher(SSL *s, STACK_OF(SSL_CIPHER) *clnt,
  ii=sk_SSL_CIPHER_find(allow,c);
  if (ii >= 0)
  {
- ret=sk_SSL_CIPHER_value(allow,ii);
- break;
+ if ((alg_k & SSL_kEECDH) && (alg_a & SSL_aECDSA) && s->s3->is_probably_safari)
+ {
+ if (!ret) ret=sk_SSL_CIPHER_value(allow,ii);
+ }
+ else
+ {
+ ret=sk_SSL_CIPHER_value(allow,ii);
+ break;
+ }
  }
  }
  return(ret);
diff --git a/ssl/ssl.h b/ssl/ssl.h
index e14a8d4..dd62578 100644
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -568,6 +568,7 @@ struct ssl_session_st
 #define SSL_OP_SSLEAY_080_CLIENT_DH_BUG 0x00000080L
 #define SSL_OP_TLS_D5_BUG 0x00000100L
 #define SSL_OP_TLS_BLOCK_PADDING_BUG 0x00000200L
+#define SSL_OP_SAFARI_ECDHE_ECDSA_BUG 0x00000400L
 
 /* Disable SSL 3.0/TLS 1.0 CBC vulnerability workaround that was added
  * in OpenSSL 0.9.6d.  Usually (depending on the application protocol)
@@ -578,7 +579,7 @@ struct ssl_session_st
 
 /* SSL_OP_ALL: various bug workarounds that should be rather harmless.
  *             This used to be 0x000FFFFFL before 0.9.7. */
-#define SSL_OP_ALL 0x80000BFFL
+#define SSL_OP_ALL 0x80000FFFL
 
 /* DTLS options */
 #define SSL_OP_NO_QUERY_MTU                 0x00001000L
diff --git a/ssl/ssl3.h b/ssl/ssl3.h
index d8ed725..0b900b5 100644
--- a/ssl/ssl3.h
+++ b/ssl/ssl3.h
@@ -577,7 +577,14 @@ typedef struct ssl3_state_st
  * server echoed our server_authz extension and therefore must send us
  * a supplemental data handshake message. */
  char tlsext_authz_server_promised;
-#endif
+
+#ifndef OPENSSL_NO_EC
+ /* This is set to true if we believe that this is a version of Safari
+ * running on OS X 10.6 .. 10.8. We wish to know this because Safari
+ * on 10.8 has broken ECDHE-ECDSA support. */
+ char is_probably_safari;
+#endif /* OPENSSL_NO_EC */
+#endif /* OPENSSL_NO_TLSEXT */
  } SSL3_STATE;
 
 #endif
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 31daa50..c5e2b85 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -1716,6 +1716,89 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha
  return ret;
  }
 
+#ifndef OPENSSL_NO_EC
+/* ssl_check_for_safari attempts to fingerprint Safari using OS X
+ * SecureTransport using the TLS extension block in |d|, of length |n|.
+ * Safari, since 10.6, sends exactly these extensions, in this order:
+ *   SNI,
+ *   elliptic_curves
+ *   ec_point_formats
+ *
+ * We wish to fingerprint Safari because they broke ECDHE-ECDSA support in 10.8,
+ * but they advertise support. So enabling ECDHE-ECDSA ciphers breaks them.
+ * Sadly we cannot differentiate 10.6 and 10.7 (which work), from 10.8 (which
+ * doesn't).
+ */
+static void ssl_check_for_safari(SSL *s, const unsigned char *data, const unsigned char *d, int n) {
+ unsigned short type, size;
+ static const unsigned char kSafariExtensionsBlock[] = {
+ 0x00, 0x0a,  /* elliptic_curves extension */
+ 0x00, 0x08,  /* 8 bytes */
+ 0x00, 0x06,  /* 6 bytes of curve ids */
+ 0x00, 0x17,  /* P-256 */
+ 0x00, 0x18,  /* P-384 */
+ 0x00, 0x19,  /* P-521 */
+
+ 0x00, 0x0b,  /* ec_point_formats */
+ 0x00, 0x02,  /* 2 bytes */
+ 0x01,        /* 1 point format */
+ 0x00,        /* uncompressed */
+ };
+
+ /* The following is only present in TLS 1.2 */
+ static const unsigned char kSafariTLS12ExtensionsBlock[] = {
+ 0x00, 0x0d,  /* signature_algorithms */
+ 0x00, 0x0c,  /* 12 bytes */
+ 0x00, 0x0a,  /* 10 bytes */
+ 0x05, 0x01,  /* SHA-384/RSA */
+ 0x04, 0x01,  /* SHA-256/RSA */
+ 0x02, 0x01,  /* SHA-1/RSA */
+ 0x04, 0x03,  /* SHA-256/ECDSA */
+ 0x02, 0x03,  /* SHA-1/ECDSA */
+ };
+
+ if (data >= (d+n-2))
+ return;
+ data += 2;
+
+ if (data > (d+n-4))
+ return;
+ n2s(data,type);
+ n2s(data,size);
+
+ if (type != TLSEXT_TYPE_server_name)
+ return;
+
+ if (data+size > d+n)
+ return;
+ data += size;
+
+ if (TLS1_get_version(s) >= TLS1_2_VERSION)
+ {
+ const size_t len1 = sizeof(kSafariExtensionsBlock);
+ const size_t len2 = sizeof(kSafariTLS12ExtensionsBlock);
+
+ if (data + len1 + len2 != d+n)
+ return;
+ if (memcmp(data, kSafariExtensionsBlock, len1) != 0)
+ return;
+ if (memcmp(data + len1, kSafariTLS12ExtensionsBlock, len2) != 0)
+ return;
+ }
+ else
+ {
+ const size_t len = sizeof(kSafariExtensionsBlock);
+
+ if (data + len != d+n)
+ return;
+ if (memcmp(data, kSafariExtensionsBlock, len) != 0)
+ return;
+ }
+
+ s->s3->is_probably_safari = 1;
+}
+#endif /* OPENSSL_NO_EC */
+
 static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, int n, int *al)
  {
  unsigned short type;
@@ -1735,6 +1818,12 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char
  s->tlsext_heartbeat &= ~(SSL_TLSEXT_HB_ENABLED |
                        SSL_TLSEXT_HB_DONT_SEND_REQUESTS);
 #endif
+
+#ifndef OPENSSL_NO_EC
+ if (s->options & SSL_OP_SAFARI_ECDHE_ECDSA_BUG)
+ ssl_check_for_safari(s, data, d, n);
+#endif /* OPENSSL_NO_EC */
+
  /* Clear any signature algorithms extension received */
  if (s->cert->peer_sigalgs)
  {
Loading...