The branch master has been updated
       via  902aca09f3cfdf124dc92c7338635b8515eb8f39 (commit)
       via  3d33f3bbe4e6dfa5ae36a2ced644b623b345bd9e (commit)
       via  fba7b84ca30dc809652e9f35f65e1d55c5b3c6e4 (commit)
       via  035b1e69d2b0ece62069aeafa47ed34bf9e707f5 (commit)
       via  e2994cf09969166e9596a07eca91bcbe61524b30 (commit)
       via  df7ce507fcc147d8319bcb55f07197a22f6acf59 (commit)
       via  58c9e32a3a4b187b9a4c14448edcf182e6754b64 (commit)
       via  6438632420cee9821409221ef6717edc5ee408c1 (commit)
       via  801cb720ade8a8fd312bc36f09f92c026e9340df (commit)
       via  de7d61d5c264fd6883a1563d3d159d2591d9037b (commit)
       via  b1b4b543ee531606cddb5df9d56b17b27d4ac60d (commit)
       via  6f8db4e669ffa178ec2a0ed1e367aaf2b94d4ec6 (commit)
       via  9529419d943c9c4cedd2397f78902c53b3091be1 (commit)
       via  4bfe1432c8d82ffaa99c01085da0520b6090567d (commit)
       via  1ab3836b3bb8ccfa4da7ce529d420e750cd56b32 (commit)
      from  e3fb4d3d52e188b83ccb8506aa2f16cb686f4d6c (commit)


- Log -----------------------------------------------------------------
commit 902aca09f3cfdf124dc92c7338635b8515eb8f39
Author: Matt Caswell <m...@openssl.org>
Date:   Tue Nov 8 13:43:12 2016 +0000

    Make some CLIENTHELLO_MSG function arguments const
    
    There were a few places where they could be declared const so this commit
    does that.
    
    Reviewed-by: Kurt Roeckx <k...@openssl.org>
    Reviewed-by: Rich Salz <rs...@openssl.org>

commit 3d33f3bbe4e6dfa5ae36a2ced644b623b345bd9e
Author: Matt Caswell <m...@openssl.org>
Date:   Mon Nov 7 15:15:06 2016 +0000

    Update a comment
    
    The name and type of the argument to ssl_check_for_safari() has changed.
    
    Reviewed-by: Kurt Roeckx <k...@openssl.org>
    Reviewed-by: Rich Salz <rs...@openssl.org>

commit fba7b84ca30dc809652e9f35f65e1d55c5b3c6e4
Author: Matt Caswell <m...@openssl.org>
Date:   Mon Nov 7 15:13:04 2016 +0000

    Swap back to using SSL3_RANDOM_SIZE instead of sizeof(clienthello.random)
    
    The size if fixed by the protocol and won't change even if
    sizeof(clienthello.random) does.
    
    Reviewed-by: Kurt Roeckx <k...@openssl.org>
    Reviewed-by: Rich Salz <rs...@openssl.org>

commit 035b1e69d2b0ece62069aeafa47ed34bf9e707f5
Author: Matt Caswell <m...@openssl.org>
Date:   Mon Nov 7 15:09:19 2016 +0000

    Move setting the session_id_len until after we filled the session_id
    
    Reviewed-by: Kurt Roeckx <k...@openssl.org>
    Reviewed-by: Rich Salz <rs...@openssl.org>

commit e2994cf09969166e9596a07eca91bcbe61524b30
Author: Matt Caswell <m...@openssl.org>
Date:   Mon Nov 7 15:07:56 2016 +0000

    Load the sessionid directly in SSLv2 compat ClientHello
    
    Don't use a sub-packet, just load it.
    
    Reviewed-by: Kurt Roeckx <k...@openssl.org>
    Reviewed-by: Rich Salz <rs...@openssl.org>

commit df7ce507fcc147d8319bcb55f07197a22f6acf59
Author: Matt Caswell <m...@openssl.org>
Date:   Mon Nov 7 14:59:12 2016 +0000

    Rename clienthello.version to clienthello.legacy_version
    
    For consistency with the TLSv1.3 spec.
    
    Reviewed-by: Kurt Roeckx <k...@openssl.org>
    Reviewed-by: Rich Salz <rs...@openssl.org>

commit 58c9e32a3a4b187b9a4c14448edcf182e6754b64
Author: Matt Caswell <m...@openssl.org>
Date:   Wed Nov 2 14:14:23 2016 +0000

    Fix some minor style issues
    
    Add a blank line, take one away - due to feedback received during review.
    
    Reviewed-by: Kurt Roeckx <k...@openssl.org>
    Reviewed-by: Rich Salz <rs...@openssl.org>

commit 6438632420cee9821409221ef6717edc5ee408c1
Author: Matt Caswell <m...@openssl.org>
Date:   Mon Oct 31 16:36:30 2016 +0000

    Add some function documentation and update some existing comments
    
    Reviewed-by: Kurt Roeckx <k...@openssl.org>
    Reviewed-by: Rich Salz <rs...@openssl.org>

commit 801cb720ade8a8fd312bc36f09f92c026e9340df
Author: Matt Caswell <m...@openssl.org>
Date:   Mon Oct 31 14:52:22 2016 +0000

    Fix make update following extensions refactor
    
    Reviewed-by: Kurt Roeckx <k...@openssl.org>
    Reviewed-by: Rich Salz <rs...@openssl.org>

commit de7d61d5c264fd6883a1563d3d159d2591d9037b
Author: Matt Caswell <m...@openssl.org>
Date:   Mon Oct 31 13:20:03 2016 +0000

    Improve some comment documentation following the extensions refactor
    
    Reviewed-by: Kurt Roeckx <k...@openssl.org>
    Reviewed-by: Rich Salz <rs...@openssl.org>

commit b1b4b543ee531606cddb5df9d56b17b27d4ac60d
Author: Matt Caswell <m...@openssl.org>
Date:   Mon Oct 31 13:11:17 2016 +0000

    Fix various style issues in the extension parsing refactor
    
    Based on review feedback received.
    
    Reviewed-by: Kurt Roeckx <k...@openssl.org>
    Reviewed-by: Rich Salz <rs...@openssl.org>

commit 6f8db4e669ffa178ec2a0ed1e367aaf2b94d4ec6
Author: Matt Caswell <m...@openssl.org>
Date:   Mon Oct 31 12:50:05 2016 +0000

    Use an explicit name for the struct for definition of RAW_EXTENSION
    
    Reviewed-by: Kurt Roeckx <k...@openssl.org>
    Reviewed-by: Rich Salz <rs...@openssl.org>

commit 9529419d943c9c4cedd2397f78902c53b3091be1
Author: Matt Caswell <m...@openssl.org>
Date:   Mon Oct 31 12:48:37 2016 +0000

    Fix a memory leak in the ClientHello extension parsing
    
    We should be freeing up the raw extension data after we've finished with it.
    
    Reviewed-by: Kurt Roeckx <k...@openssl.org>
    Reviewed-by: Rich Salz <rs...@openssl.org>

commit 4bfe1432c8d82ffaa99c01085da0520b6090567d
Author: Matt Caswell <m...@openssl.org>
Date:   Mon Oct 31 12:47:20 2016 +0000

    Handle compression methods correctly with SSLv2 compat ClientHello
    
    In the case of an SSLv2 compat ClientHello we weren't setting up the
    compression methods correctly, which could lead to uninit reads or crashes.
    
    Reviewed-by: Kurt Roeckx <k...@openssl.org>
    Reviewed-by: Rich Salz <rs...@openssl.org>

commit 1ab3836b3bb8ccfa4da7ce529d420e750cd56b32
Author: Matt Caswell <m...@openssl.org>
Date:   Sat Oct 22 17:24:37 2016 +0100

    Refactor ClientHello processing so that extensions get parsed earlier
    
    Reviewed-by: Kurt Roeckx <k...@openssl.org>
    Reviewed-by: Rich Salz <rs...@openssl.org>

-----------------------------------------------------------------------

Summary of changes:
 include/openssl/ssl.h    |   1 +
 ssl/ssl_err.c            |   1 +
 ssl/ssl_locl.h           |  36 ++++-
 ssl/ssl_sess.c           |  26 ++--
 ssl/statem/statem_lib.c  | 111 ++++++++++++++-
 ssl/statem/statem_locl.h |   3 +
 ssl/statem/statem_srvr.c | 262 +++++++++++++++++++++---------------
 ssl/t1_lib.c             | 343 +++++++++++++++++++++++------------------------
 8 files changed, 479 insertions(+), 304 deletions(-)

diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index c781323..1bb93fe 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -2233,6 +2233,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_F_TLS1_SETUP_KEY_BLOCK                       211
 # define SSL_F_TLS1_SET_SERVER_SIGALGS                    335
 # define SSL_F_TLS_CLIENT_KEY_EXCHANGE_POST_WORK          354
+# define SSL_F_TLS_COLLECT_EXTENSIONS                     435
 # define SSL_F_TLS_CONSTRUCT_CERTIFICATE_REQUEST          372
 # define SSL_F_TLS_CONSTRUCT_CERT_STATUS                  429
 # define SSL_F_TLS_CONSTRUCT_CHANGE_CIPHER_SPEC           427
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index 5c2e961..6c438fe 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -244,6 +244,7 @@ static ERR_STRING_DATA SSL_str_functs[] = {
     {ERR_FUNC(SSL_F_TLS1_SET_SERVER_SIGALGS), "tls1_set_server_sigalgs"},
     {ERR_FUNC(SSL_F_TLS_CLIENT_KEY_EXCHANGE_POST_WORK),
      "tls_client_key_exchange_post_work"},
+    {ERR_FUNC(SSL_F_TLS_COLLECT_EXTENSIONS), "tls_collect_extensions"},
     {ERR_FUNC(SSL_F_TLS_CONSTRUCT_CERTIFICATE_REQUEST),
      "tls_construct_certificate_request"},
     {ERR_FUNC(SSL_F_TLS_CONSTRUCT_CERT_STATUS), "tls_construct_cert_status"},
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 0c6bd31..dcc2336 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -1624,6 +1624,29 @@ typedef struct ssl3_comp_st {
 } SSL3_COMP;
 # endif
 
+typedef struct raw_extension_st {
+    unsigned int type;
+    PACKET data;
+} RAW_EXTENSION;
+
+#define MAX_COMPRESSIONS_SIZE   255
+
+typedef struct {
+    unsigned int isv2;
+    unsigned int legacy_version;
+    unsigned char random[SSL3_RANDOM_SIZE];
+    size_t session_id_len;
+    unsigned char session_id[SSL_MAX_SSL_SESSION_ID_LENGTH];
+    size_t dtls_cookie_len;
+    unsigned char dtls_cookie[DTLS1_COOKIE_LENGTH];
+    PACKET ciphersuites;
+    size_t compressions_len;
+    unsigned char compressions[MAX_COMPRESSIONS_SIZE];
+    PACKET extensions;
+    size_t num_extensions;
+    RAW_EXTENSION *pre_proc_exts;
+} CLIENTHELLO_MSG;
+
 extern SSL3_ENC_METHOD ssl3_undef_enc_method;
 
 __owur const SSL_METHOD *ssl_bad_method(int ver);
@@ -1797,8 +1820,7 @@ __owur CERT *ssl_cert_dup(CERT *cert);
 void ssl_cert_clear_certs(CERT *c);
 void ssl_cert_free(CERT *c);
 __owur int ssl_get_new_session(SSL *s, int session);
-__owur int ssl_get_prev_session(SSL *s, const PACKET *ext,
-                                const PACKET *session_id);
+__owur int ssl_get_prev_session(SSL *s, CLIENTHELLO_MSG *hello);
 __owur SSL_SESSION *ssl_session_dup(SSL_SESSION *src, int ticket);
 __owur int ssl_cipher_id_cmp(const SSL_CIPHER *a, const SSL_CIPHER *b);
 DECLARE_OBJ_BSEARCH_GLOBAL_CMP_FN(SSL_CIPHER, SSL_CIPHER, ssl_cipher_id);
@@ -1919,7 +1941,7 @@ __owur int ssl_version_supported(const SSL *s, int 
version);
 __owur int ssl_set_client_hello_version(SSL *s);
 __owur int ssl_check_version_downgrade(SSL *s);
 __owur int ssl_set_version_bound(int method_version, int version, int *bound);
-__owur int ssl_choose_server_version(SSL *s);
+__owur int ssl_choose_server_version(SSL *s, CLIENTHELLO_MSG *hello);
 __owur int ssl_choose_client_version(SSL *s, int version);
 int ssl_get_client_min_max_version(const SSL *s, int *min_version,
                                    int *max_version);
@@ -2020,7 +2042,7 @@ __owur int tls1_shared_list(SSL *s,
                             const unsigned char *l2, size_t l2len, int nmatch);
 __owur int ssl_add_clienthello_tlsext(SSL *s, WPACKET *pkt, int *al);
 __owur int ssl_add_serverhello_tlsext(SSL *s, WPACKET *pkt, int *al);
-__owur int ssl_parse_clienthello_tlsext(SSL *s, PACKET *pkt);
+__owur int ssl_parse_clienthello_tlsext(SSL *s, CLIENTHELLO_MSG *hello);
 void ssl_set_default_md(SSL *s);
 __owur int tls1_set_server_sigalgs(SSL *s);
 __owur int ssl_check_clienthello_tlsext_late(SSL *s, int *al);
@@ -2034,9 +2056,9 @@ __owur int dtls1_process_heartbeat(SSL *s, unsigned char 
*p,
                                    size_t length);
 #  endif
 
-__owur int tls_check_serverhello_tlsext_early(SSL *s, const PACKET *ext,
-                                              const PACKET *session_id,
-                                              SSL_SESSION **ret);
+__owur int tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello,
+                                      SSL_SESSION **ret);
+__owur int tls_check_client_ems_support(SSL *s, const CLIENTHELLO_MSG *hello);
 
 __owur int tls12_get_sigandhash(WPACKET *pkt, const EVP_PKEY *pk,
                                 const EVP_MD *md);
diff --git a/ssl/ssl_sess.c b/ssl/ssl_sess.c
index 44101cb..291796e 100644
--- a/ssl/ssl_sess.c
+++ b/ssl/ssl_sess.c
@@ -432,8 +432,7 @@ int ssl_get_new_session(SSL *s, int session)
  * ssl_get_prev attempts to find an SSL_SESSION to be used to resume this
  * connection. It is only called by servers.
  *
- *   ext: ClientHello extensions (including length prefix)
- *   session_id: ClientHello session ID.
+ *   hello: The parsed ClientHello data
  *
  * Returns:
  *   -1: error
@@ -445,7 +444,7 @@ int ssl_get_new_session(SSL *s, int session)
  *   - Both for new and resumed sessions, s->tlsext_ticket_expected is set to 1
  *     if the server should issue a new session ticket (to 0 otherwise).
  */
-int ssl_get_prev_session(SSL *s, const PACKET *ext, const PACKET *session_id)
+int ssl_get_prev_session(SSL *s, CLIENTHELLO_MSG *hello)
 {
     /* This is used only by servers. */
 
@@ -454,11 +453,11 @@ int ssl_get_prev_session(SSL *s, const PACKET *ext, const 
PACKET *session_id)
     int try_session_cache = 1;
     int r;
 
-    if (PACKET_remaining(session_id) == 0)
+    if (hello->session_id_len == 0)
         try_session_cache = 0;
 
-    /* sets s->tlsext_ticket_expected and extended master secret flag */
-    r = tls_check_serverhello_tlsext_early(s, ext, session_id, &ret);
+    /* sets s->tlsext_ticket_expected */
+    r = tls_get_ticket_from_client(s, hello, &ret);
     switch (r) {
     case -1:                   /* Error during processing */
         fatal = 1;
@@ -479,14 +478,12 @@ int ssl_get_prev_session(SSL *s, const PACKET *ext, const 
PACKET *session_id)
         !(s->session_ctx->session_cache_mode &
           SSL_SESS_CACHE_NO_INTERNAL_LOOKUP)) {
         SSL_SESSION data;
-        size_t local_len;
+
         data.ssl_version = s->version;
         memset(data.session_id, 0, sizeof(data.session_id));
-        if (!PACKET_copy_all(session_id, data.session_id,
-                             sizeof(data.session_id), &local_len)) {
-            goto err;
-        }
-        data.session_id_length = local_len;
+        memcpy(data.session_id, hello->session_id, hello->session_id_len);
+        data.session_id_length = hello->session_id_len;
+
         CRYPTO_THREAD_read_lock(s->session_ctx->lock);
         ret = lh_SSL_SESSION_retrieve(s->session_ctx->sessions, &data);
         if (ret != NULL) {
@@ -501,8 +498,9 @@ int ssl_get_prev_session(SSL *s, const PACKET *ext, const 
PACKET *session_id)
     if (try_session_cache &&
         ret == NULL && s->session_ctx->get_session_cb != NULL) {
         int copy = 1;
-        ret = s->session_ctx->get_session_cb(s, PACKET_data(session_id),
-                                             (int)PACKET_remaining(session_id),
+
+        ret = s->session_ctx->get_session_cb(s, hello->session_id,
+                                             hello->session_id_len,
                                              &copy);
 
         if (ret != NULL) {
diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c
index 24159da..a3a31bc 100644
--- a/ssl/statem/statem_lib.c
+++ b/ssl/statem/statem_lib.c
@@ -152,6 +152,111 @@ static void ssl3_take_mac(SSL *s)
 }
 #endif
 
+/*
+ * Comparison function used in a call to qsort (see tls_collect_extensions()
+ * below.)
+ * The two arguments |p1| and |p2| are expected to be pointers to 
RAW_EXTENSIONs
+ *
+ * Returns:
+ *  1 if the type for p1 is greater than p2
+ *  0 if the type for p1 and p2 are the same
+ * -1 if the type for p1 is less than p2
+ */
+static int compare_extensions(const void *p1, const void *p2)
+{
+    const RAW_EXTENSION *e1 = (const RAW_EXTENSION *)p1;
+    const RAW_EXTENSION *e2 = (const RAW_EXTENSION *)p2;
+
+    if (e1->type < e2->type)
+        return -1;
+    else if (e1->type > e2->type)
+        return 1;
+
+    return 0;
+}
+
+/*
+ * Gather a list of all the extensions. We don't actually process the content
+ * of the extensions yet, except to check their types.
+ *
+ * Per http://tools.ietf.org/html/rfc5246#section-7.4.1.4, there may not be
+ * more than one extension of the same type in a ClientHello or ServerHello.
+ * This function returns 1 if all extensions are unique and we have parsed 
their
+ * types, and 0 if the extensions contain duplicates, could not be successfully
+ * parsed, or an internal error occurred.
+ */
+/*
+ * TODO(TLS1.3): Refactor ServerHello extension parsing to use this and then
+ * remove tls1_check_duplicate_extensions()
+ */
+int tls_collect_extensions(PACKET *packet, RAW_EXTENSION **res,
+                             size_t *numfound, int *ad)
+{
+    PACKET extensions = *packet;
+    size_t num_extensions = 0, i = 0;
+    RAW_EXTENSION *raw_extensions = NULL;
+
+    /* First pass: count the extensions. */
+    while (PACKET_remaining(&extensions) > 0) {
+        unsigned int type;
+        PACKET extension;
+
+        if (!PACKET_get_net_2(&extensions, &type) ||
+            !PACKET_get_length_prefixed_2(&extensions, &extension)) {
+            *ad = SSL_AD_DECODE_ERROR;
+            goto err;
+        }
+        num_extensions++;
+    }
+
+    if (num_extensions > 0) {
+        raw_extensions = OPENSSL_malloc(sizeof(*raw_extensions)
+                                        * num_extensions);
+        if (raw_extensions == NULL) {
+            *ad = SSL_AD_INTERNAL_ERROR;
+            SSLerr(SSL_F_TLS_COLLECT_EXTENSIONS, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+
+        /* Second pass: collect the extensions. */
+        for (i = 0; i < num_extensions; i++) {
+            if (!PACKET_get_net_2(packet, &raw_extensions[i].type) ||
+                !PACKET_get_length_prefixed_2(packet,
+                                              &raw_extensions[i].data)) {
+                /* This should not happen. */
+                *ad = SSL_AD_INTERNAL_ERROR;
+                SSLerr(SSL_F_TLS_COLLECT_EXTENSIONS, ERR_R_INTERNAL_ERROR);
+                goto err;
+            }
+        }
+
+        if (PACKET_remaining(packet) != 0) {
+            *ad = SSL_AD_DECODE_ERROR;
+            SSLerr(SSL_F_TLS_COLLECT_EXTENSIONS, SSL_R_LENGTH_MISMATCH);
+            goto err;
+        }
+        /* Sort the extensions and make sure there are no duplicates. */
+        qsort(raw_extensions, num_extensions, sizeof(*raw_extensions),
+              compare_extensions);
+        for (i = 1; i < num_extensions; i++) {
+            if (raw_extensions[i - 1].type == raw_extensions[i].type) {
+                *ad = SSL_AD_DECODE_ERROR;
+                goto err;
+            }
+        }
+    }
+
+    *res = raw_extensions;
+    *numfound = num_extensions;
+    return 1;
+
+ err:
+    OPENSSL_free(raw_extensions);
+    return 0;
+}
+
+
+
 MSG_PROCESS_RETURN tls_process_change_cipher_spec(SSL *s, PACKET *pkt)
 {
     int al;
@@ -875,7 +980,7 @@ int ssl_set_version_bound(int method_version, int version, 
int *bound)
  *
  * Returns 0 on success or an SSL error reason number on failure.
  */
-int ssl_choose_server_version(SSL *s)
+int ssl_choose_server_version(SSL *s, CLIENTHELLO_MSG *hello)
 {
     /*-
      * With version-flexible methods we have an initial state with:
@@ -887,11 +992,13 @@ int ssl_choose_server_version(SSL *s)
      * handle version.
      */
     int server_version = s->method->version;
-    int client_version = s->client_version;
+    int client_version = hello->legacy_version;
     const version_info *vent;
     const version_info *table;
     int disabled = 0;
 
+    s->client_version = client_version;
+
     switch (server_version) {
     default:
         if (version_cmp(s, client_version, s->version) < 0)
diff --git a/ssl/statem/statem_locl.h b/ssl/statem/statem_locl.h
index a360fc9..740595b 100644
--- a/ssl/statem/statem_locl.h
+++ b/ssl/statem/statem_locl.h
@@ -86,6 +86,9 @@ __owur int tls_construct_finished(SSL *s, WPACKET *pkt);
 __owur WORK_STATE tls_finish_handshake(SSL *s, WORK_STATE wst);
 __owur WORK_STATE dtls_wait_for_dry(SSL *s);
 
+int tls_collect_extensions(PACKET *packet, RAW_EXTENSION **res,
+                             size_t *numfound, int *ad);
+
 /* some client-only functions */
 __owur int tls_construct_client_hello(SSL *s, WPACKET *pkt);
 __owur MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt);
diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c
index 6aa897b..6bd16b8 100644
--- a/ssl/statem/statem_srvr.c
+++ b/ssl/statem/statem_srvr.c
@@ -889,7 +889,7 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET 
*pkt)
 {
     int i, al = SSL_AD_INTERNAL_ERROR;
     unsigned int j;
-    size_t loop, complen = 0;
+    size_t loop;
     unsigned long id;
     const SSL_CIPHER *c;
 #ifndef OPENSSL_NO_COMP
@@ -898,17 +898,20 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, 
PACKET *pkt)
     STACK_OF(SSL_CIPHER) *ciphers = NULL;
     int protverr;
     /* |cookie| will only be initialized for DTLS. */
-    PACKET session_id, cipher_suites, compression, extensions, cookie;
-    int is_v2_record;
+    PACKET session_id, compression, extensions, cookie;
     static const unsigned char null_compression = 0;
+    CLIENTHELLO_MSG clienthello;
 
-    is_v2_record = RECORD_LAYER_is_sslv2_record(&s->rlayer);
-
+    /*
+     * First, parse the raw ClientHello data into the CLIENTHELLO_MSG 
structure.
+     */
+    memset(&clienthello, 0, sizeof(clienthello));
+    clienthello.isv2 = RECORD_LAYER_is_sslv2_record(&s->rlayer);
     PACKET_null_init(&cookie);
-    /* First lets get s->client_version set correctly */
-    if (is_v2_record) {
-        unsigned int version;
+
+    if (clienthello.isv2) {
         unsigned int mt;
+
         /*-
          * An SSLv3/TLSv1 backwards-compatible CLIENT-HELLO in an SSLv2
          * header is sent directly on the wire, not wrapped as a TLS
@@ -934,73 +937,25 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, 
PACKET *pkt)
             SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, ERR_R_INTERNAL_ERROR);
             goto err;
         }
-
-        if (!PACKET_get_net_2(pkt, &version)) {
-            /* No protocol version supplied! */
-            SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_UNKNOWN_PROTOCOL);
-            goto err;
-        }
-        if (version == 0x0002) {
-            /* This is real SSLv2. We don't support it. */
-            SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_UNKNOWN_PROTOCOL);
-            goto err;
-        } else if ((version & 0xff00) == (SSL3_VERSION_MAJOR << 8)) {
-            /* SSLv3/TLS */
-            s->client_version = version;
-        } else {
-            /* No idea what protocol this is */
-            SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_UNKNOWN_PROTOCOL);
-            goto err;
-        }
-    } else {
-        /*
-         * use version from inside client hello, not from record header (may
-         * differ: see RFC 2246, Appendix E, second paragraph)
-         */
-        if (!PACKET_get_net_2(pkt, (unsigned int *)&s->client_version)) {
-            al = SSL_AD_DECODE_ERROR;
-            SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_LENGTH_TOO_SHORT);
-            goto f_err;
-        }
-    }
-
-    /*
-     * Do SSL/TLS version negotiation if applicable. For DTLS we just check
-     * versions are potentially compatible. Version negotiation comes later.
-     */
-    if (!SSL_IS_DTLS(s)) {
-        protverr = ssl_choose_server_version(s);
-    } else if (s->method->version != DTLS_ANY_VERSION &&
-               DTLS_VERSION_LT(s->client_version, s->version)) {
-        protverr = SSL_R_VERSION_TOO_LOW;
-    } else {
-        protverr = 0;
     }
 
-    if (protverr) {
-        SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, protverr);
-        if ((!s->enc_write_ctx && !s->write_hash)) {
-            /*
-             * similar to ssl3_get_record, send alert using remote version
-             * number
-             */
-            s->version = s->client_version;
-        }
-        al = SSL_AD_PROTOCOL_VERSION;
-        goto f_err;
+    if (!PACKET_get_net_2(pkt, &clienthello.legacy_version)) {
+        al = SSL_AD_DECODE_ERROR;
+        SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_LENGTH_TOO_SHORT);
+        goto err;
     }
 
     /* Parse the message and load client random. */
-    if (is_v2_record) {
+    if (clienthello.isv2) {
         /*
          * Handle an SSLv2 backwards compatible ClientHello
          * Note, this is only for SSLv3+ using the backward compatible format.
-         * Real SSLv2 is not supported, and is rejected above.
+         * Real SSLv2 is not supported, and is rejected below.
          */
-        unsigned int cipher_len, session_id_len, challenge_len;
+        unsigned int ciphersuite_len, session_id_len, challenge_len;
         PACKET challenge;
 
-        if (!PACKET_get_net_2(pkt, &cipher_len)
+        if (!PACKET_get_net_2(pkt, &ciphersuite_len)
             || !PACKET_get_net_2(pkt, &session_id_len)
             || !PACKET_get_net_2(pkt, &challenge_len)) {
             SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO,
@@ -1015,8 +970,9 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET 
*pkt)
             goto f_err;
         }
 
-        if (!PACKET_get_sub_packet(pkt, &cipher_suites, cipher_len)
-            || !PACKET_get_sub_packet(pkt, &session_id, session_id_len)
+        if (!PACKET_get_sub_packet(pkt, &clienthello.ciphersuites,
+                                   ciphersuite_len)
+            || !PACKET_copy_bytes(pkt, clienthello.session_id, session_id_len)
             || !PACKET_get_sub_packet(pkt, &challenge, challenge_len)
             /* No extensions. */
             || PACKET_remaining(pkt) != 0) {
@@ -1025,13 +981,18 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, 
PACKET *pkt)
             al = SSL_AD_DECODE_ERROR;
             goto f_err;
         }
+        clienthello.session_id_len = session_id_len;
 
-        /* Load the client random and compression list. */
-        challenge_len = challenge_len > SSL3_RANDOM_SIZE ? SSL3_RANDOM_SIZE :
-            challenge_len;
-        memset(s->s3->client_random, 0, SSL3_RANDOM_SIZE);
+        /* Load the client random and compression list. We use SSL3_RANDOM_SIZE
+         * here rather than sizeof(clienthello.random) because that is the 
limit
+         * for SSLv3 and it is fixed. It won't change even if
+         * sizeof(clienthello.random) does.
+         */
+        challenge_len = challenge_len > SSL3_RANDOM_SIZE
+                        ? SSL3_RANDOM_SIZE : challenge_len;
+        memset(clienthello.random, 0, SSL3_RANDOM_SIZE);
         if (!PACKET_copy_bytes(&challenge,
-                               s->s3->client_random + SSL3_RANDOM_SIZE -
+                               clienthello.random + SSL3_RANDOM_SIZE -
                                challenge_len, challenge_len)
             /* Advertise only null compression. */
             || !PACKET_buf_init(&compression, &null_compression, 1)) {
@@ -1040,17 +1001,14 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, 
PACKET *pkt)
             goto f_err;
         }
 
-        PACKET_null_init(&extensions);
+        PACKET_null_init(&clienthello.extensions);
     } else {
         /* Regular ClientHello. */
-        if (!PACKET_copy_bytes(pkt, s->s3->client_random, SSL3_RANDOM_SIZE)
-            || !PACKET_get_length_prefixed_1(pkt, &session_id)) {
-            al = SSL_AD_DECODE_ERROR;
-            SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_LENGTH_MISMATCH);
-            goto f_err;
-        }
-
-        if (PACKET_remaining(&session_id) > SSL_MAX_SSL_SESSION_ID_LENGTH) {
+        if (!PACKET_copy_bytes(pkt, clienthello.random, SSL3_RANDOM_SIZE)
+            || !PACKET_get_length_prefixed_1(pkt, &session_id)
+            || !PACKET_copy_all(&session_id, clienthello.session_id,
+                    SSL_MAX_SSL_SESSION_ID_LENGTH,
+                    &clienthello.session_id_len)) {
             al = SSL_AD_DECODE_ERROR;
             SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_LENGTH_MISMATCH);
             goto f_err;
@@ -1062,33 +1020,114 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, 
PACKET *pkt)
                 SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_LENGTH_MISMATCH);
                 goto f_err;
             }
+            if (!PACKET_copy_all(&cookie, clienthello.dtls_cookie,
+                                 DTLS1_COOKIE_LENGTH,
+                                 &clienthello.dtls_cookie_len)) {
+                al = SSL_AD_DECODE_ERROR;
+                SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_LENGTH_MISMATCH);
+                goto f_err;
+            }
             /*
              * If we require cookies and this ClientHello doesn't contain one,
              * just return since we do not want to allocate any memory yet.
              * So check cookie length...
              */
             if (SSL_get_options(s) & SSL_OP_COOKIE_EXCHANGE) {
-                if (PACKET_remaining(&cookie) == 0)
+                if (clienthello.dtls_cookie_len == 0)
                     return 1;
             }
         }
 
-        if (!PACKET_get_length_prefixed_2(pkt, &cipher_suites)
-            || !PACKET_get_length_prefixed_1(pkt, &compression)) {
+        if (!PACKET_get_length_prefixed_2(pkt, &clienthello.ciphersuites)) {
+            al = SSL_AD_DECODE_ERROR;
+            SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_LENGTH_MISMATCH);
+            goto f_err;
+        }
+
+        if (!PACKET_get_length_prefixed_1(pkt, &compression)) {
             al = SSL_AD_DECODE_ERROR;
             SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_LENGTH_MISMATCH);
             goto f_err;
         }
+
         /* Could be empty. */
-        extensions = *pkt;
+        if (PACKET_remaining(pkt) == 0) {
+            PACKET_null_init(&clienthello.extensions);
+        } else {
+            if (!PACKET_get_length_prefixed_2(pkt, &clienthello.extensions)) {
+                al = SSL_AD_DECODE_ERROR;
+                SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_LENGTH_MISMATCH);
+                goto f_err;
+            }
+        }
+    }
+
+    if (!PACKET_copy_all(&compression, clienthello.compressions,
+                         MAX_COMPRESSIONS_SIZE,
+                         &clienthello.compressions_len)) {
+        al = SSL_AD_DECODE_ERROR;
+        SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_LENGTH_MISMATCH);
+        goto f_err;
+    }
+
+    /* Preserve the raw extensions PACKET for later use */
+    extensions = clienthello.extensions;
+    if (!tls_collect_extensions(&extensions, &clienthello.pre_proc_exts,
+                                  &clienthello.num_extensions, &al)) {
+        /* SSLerr already been called */
+        goto f_err;
+    }
+
+    /* Finished parsing the ClientHello, now we can start processing it */
+
+    /* Set up the client_random */
+    memcpy(s->s3->client_random, clienthello.random, SSL3_RANDOM_SIZE);
+
+    /* Choose the version */
+
+    if (clienthello.isv2) {
+        if (clienthello.legacy_version == SSL2_VERSION
+                || (clienthello.legacy_version & 0xff00)
+                   != (SSL3_VERSION_MAJOR << 8)) {
+            /*
+             * This is real SSLv2 or something complete unknown. We don't
+             * support it.
+             */
+            SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_UNKNOWN_PROTOCOL);
+            goto err;
+        }
+        /* SSLv3/TLS */
+        s->client_version = clienthello.legacy_version;
+    }
+    /*
+     * Do SSL/TLS version negotiation if applicable. For DTLS we just check
+     * versions are potentially compatible. Version negotiation comes later.
+     */
+    if (!SSL_IS_DTLS(s)) {
+        protverr = ssl_choose_server_version(s, &clienthello);
+    } else if (s->method->version != DTLS_ANY_VERSION &&
+               DTLS_VERSION_LT((int)clienthello.legacy_version, s->version)) {
+        protverr = SSL_R_VERSION_TOO_LOW;
+    } else {
+        protverr = 0;
+    }
+
+    if (protverr) {
+        SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, protverr);
+        if ((!s->enc_write_ctx && !s->write_hash)) {
+            /* like ssl3_get_record, send alert using remote version number */
+            s->version = s->client_version = clienthello.legacy_version;
+        }
+        al = SSL_AD_PROTOCOL_VERSION;
+        goto f_err;
     }
 
     if (SSL_IS_DTLS(s)) {
         /* Empty cookie was already handled above by returning early. */
         if (SSL_get_options(s) & SSL_OP_COOKIE_EXCHANGE) {
             if (s->ctx->app_verify_cookie_cb != NULL) {
-                if (s->ctx->app_verify_cookie_cb(s, PACKET_data(&cookie),
-                        (unsigned int)PACKET_remaining(&cookie)) == 0) {
+                if (s->ctx->app_verify_cookie_cb(s, clienthello.dtls_cookie,
+                        clienthello.dtls_cookie_len) == 0) {
                     al = SSL_AD_HANDSHAKE_FAILURE;
                     SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO,
                            SSL_R_COOKIE_MISMATCH);
@@ -1096,7 +1135,9 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, 
PACKET *pkt)
                     /* else cookie verification succeeded */
                 }
                 /* default verification */
-            } else if (!PACKET_equal(&cookie, s->d1->cookie, 
s->d1->cookie_len)) {
+            } else if (s->d1->cookie_len != clienthello.dtls_cookie_len
+                    || memcmp(clienthello.dtls_cookie, s->d1->cookie,
+                              s->d1->cookie_len) != 0) {
                 al = SSL_AD_HANDSHAKE_FAILURE;
                 SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_COOKIE_MISMATCH);
                 goto f_err;
@@ -1104,7 +1145,7 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, 
PACKET *pkt)
             s->d1->cookie_verified = 1;
         }
         if (s->method->version == DTLS_ANY_VERSION) {
-            protverr = ssl_choose_server_version(s);
+            protverr = ssl_choose_server_version(s, &clienthello);
             if (protverr != 0) {
                 SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, protverr);
                 s->version = s->client_version;
@@ -1116,6 +1157,14 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, 
PACKET *pkt)
 
     s->hit = 0;
 
+    /* We need to do this before getting the session */
+    if (!tls_check_client_ems_support(s, &clienthello)) {
+        /* Only fails if the extension is malformed */
+        al = SSL_AD_DECODE_ERROR;
+        SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_CLIENTHELLO_TLSEXT);
+        goto f_err;
+    }
+
     /*
      * We don't allow resumption in a backwards compatible ClientHello.
      * TODO(openssl-team): in TLS1.1+, session_id MUST be empty.
@@ -1132,13 +1181,13 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, 
PACKET *pkt)
      * SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION setting will be
      * ignored.
      */
-    if (is_v2_record ||
+    if (clienthello.isv2 ||
         (s->new_session &&
          (s->options & SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION))) {
         if (!ssl_get_new_session(s, 1))
             goto err;
     } else {
-        i = ssl_get_prev_session(s, &extensions, &session_id);
+        i = ssl_get_prev_session(s, &clienthello);
         /*
          * Only resume if the session's version matches the negotiated
          * version.
@@ -1160,8 +1209,8 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, 
PACKET *pkt)
         }
     }
 
-    if (ssl_bytes_to_cipher_list(s, &cipher_suites, &(ciphers),
-                                 is_v2_record, &al) == NULL) {
+    if (ssl_bytes_to_cipher_list(s, &clienthello.ciphersuites, &ciphers,
+                                 clienthello.isv2, &al) == NULL) {
         goto f_err;
     }
 
@@ -1196,13 +1245,12 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, 
PACKET *pkt)
         }
     }
 
-    complen = PACKET_remaining(&compression);
-    for (loop = 0; loop < complen; loop++) {
-        if (PACKET_data(&compression)[loop] == 0)
+    for (loop = 0; loop < clienthello.compressions_len; loop++) {
+        if (clienthello.compressions[loop] == 0)
             break;
     }
 
-    if (loop >= complen) {
+    if (loop >= clienthello.compressions_len) {
         /* no compress */
         al = SSL_AD_DECODE_ERROR;
         SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_NO_COMPRESSION_SPECIFIED);
@@ -1210,11 +1258,9 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, 
PACKET *pkt)
     }
 
     /* TLS extensions */
-    if (s->version >= SSL3_VERSION) {
-        if (!ssl_parse_clienthello_tlsext(s, &extensions)) {
-            SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_PARSE_TLSEXT);
-            goto err;
-        }
+    if (!ssl_parse_clienthello_tlsext(s, &clienthello)) {
+        SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_PARSE_TLSEXT);
+        goto err;
     }
 
     /*
@@ -1305,11 +1351,11 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, 
PACKET *pkt)
             goto f_err;
         }
         /* Look for resumed method in compression list */
-        for (k = 0; k < complen; k++) {
-            if (PACKET_data(&compression)[k] == comp_id)
+        for (k = 0; k < clienthello.compressions_len; k++) {
+            if (clienthello.compressions[k] == comp_id)
                 break;
         }
-        if (k >= complen) {
+        if (k >= clienthello.compressions_len) {
             al = SSL_AD_ILLEGAL_PARAMETER;
             SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO,
                    SSL_R_REQUIRED_COMPRESSION_ALGORITHM_MISSING);
@@ -1326,8 +1372,8 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, 
PACKET *pkt)
         for (m = 0; m < nn; m++) {
             comp = sk_SSL_COMP_value(s->ctx->comp_methods, m);
             v = comp->id;
-            for (o = 0; o < complen; o++) {
-                if (v == PACKET_data(&compression)[o]) {
+            for (o = 0; o < clienthello.compressions_len; o++) {
+                if (v == clienthello.compressions[o]) {
                     done = 1;
                     break;
                 }
@@ -1376,6 +1422,7 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, 
PACKET *pkt)
     }
 
     sk_SSL_CIPHER_free(ciphers);
+    OPENSSL_free(clienthello.pre_proc_exts);
     return MSG_PROCESS_CONTINUE_PROCESSING;
  f_err:
     ssl3_send_alert(s, SSL3_AL_FATAL, al);
@@ -1383,8 +1430,9 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, 
PACKET *pkt)
     ossl_statem_set_error(s);
 
     sk_SSL_CIPHER_free(ciphers);
-    return MSG_PROCESS_ERROR;
+    OPENSSL_free(clienthello.pre_proc_exts);
 
+    return MSG_PROCESS_ERROR;
 }
 
 WORK_STATE tls_post_process_client_hello(SSL *s, WORK_STATE wst)
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 0523e54..6598776 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -1690,7 +1690,7 @@ static int tls1_alpn_handle_client_hello_late(SSL *s, int 
*al)
 #ifndef OPENSSL_NO_EC
 /*-
  * ssl_check_for_safari attempts to fingerprint Safari using OS X
- * SecureTransport using the TLS extension block in |pkt|.
+ * SecureTransport using the TLS extension block in |hello|.
  * Safari, since 10.6, sends exactly these extensions, in this order:
  *   SNI,
  *   elliptic_curves
@@ -1701,7 +1701,7 @@ static int tls1_alpn_handle_client_hello_late(SSL *s, int 
*al)
  * Sadly we cannot differentiate 10.6, 10.7 and 10.8.4 (which work), from
  * 10.8..10.8.3 (which don't work).
  */
-static void ssl_check_for_safari(SSL *s, const PACKET *pkt)
+static void ssl_check_for_safari(SSL *s, const CLIENTHELLO_MSG *hello)
 {
     unsigned int type;
     PACKET sni, tmppkt;
@@ -1733,7 +1733,7 @@ static void ssl_check_for_safari(SSL *s, const PACKET 
*pkt)
     /* Length of the common prefix (first two extensions). */
     static const size_t kSafariCommonExtensionsLength = 18;
 
-    tmppkt = *pkt;
+    tmppkt = hello->extensions;
 
     if (!PACKET_forward(&tmppkt, 2)
         || !PACKET_get_net_2(&tmppkt, &type)
@@ -1753,21 +1753,21 @@ static void ssl_check_for_safari(SSL *s, const PACKET 
*pkt)
 #endif                          /* !OPENSSL_NO_EC */
 
 /*
- * Parse ClientHello extensions and stash extension info in various parts of
- * the SSL object. Verify that there are no duplicate extensions.
+ * Loop through all remaining ClientHello extensions that we collected earlier
+ * and haven't already processed. For each one parse it and update the SSL
+ * object as required.
  *
  * Behaviour upon resumption is extension-specific. If the extension has no
  * effect during resumption, it is parsed (to verify its format) but otherwise
  * ignored.
  *
- * Consumes the entire packet in |pkt|. Returns 1 on success and 0 on failure.
+ * Returns 1 on success and 0 on failure.
  * Upon failure, sets |al| to the appropriate alert.
  */
-static int ssl_scan_clienthello_tlsext(SSL *s, PACKET *pkt, int *al)
+static int ssl_scan_clienthello_tlsext(SSL *s, CLIENTHELLO_MSG *hello, int *al)
 {
-    unsigned int type;
+    size_t loop;
     int renegotiate_seen = 0;
-    PACKET extensions;
 
     *al = SSL_AD_DECODE_ERROR;
     s->servername_done = 0;
@@ -1789,7 +1789,7 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET 
*pkt, int *al)
 
 #ifndef OPENSSL_NO_EC
     if (s->options & SSL_OP_SAFARI_ECDHE_ECDSA_BUG)
-        ssl_check_for_safari(s, pkt);
+        ssl_check_for_safari(s, hello);
 #endif                          /* !OPENSSL_NO_EC */
 
     /* Clear any signature algorithms extension received */
@@ -1804,32 +1804,23 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET 
*pkt, int *al)
 
     s->srtp_profile = NULL;
 
-    if (PACKET_remaining(pkt) == 0)
-        goto ri_check;
-
-    if (!PACKET_as_length_prefixed_2(pkt, &extensions))
-        return 0;
-
-    if (!tls1_check_duplicate_extensions(&extensions))
-        return 0;
-
     /*
      * We parse all extensions to ensure the ClientHello is well-formed but,
      * unless an extension specifies otherwise, we ignore extensions upon
      * resumption.
      */
-    while (PACKET_get_net_2(&extensions, &type)) {
-        PACKET extension;
-        if (!PACKET_get_length_prefixed_2(&extensions, &extension))
-            return 0;
+    for (loop = 0; loop < hello->num_extensions; loop++) {
+        RAW_EXTENSION *currext = &hello->pre_proc_exts[loop];
 
         if (s->tlsext_debug_cb)
-            s->tlsext_debug_cb(s, 0, type, PACKET_data(&extension),
-                               (int)PACKET_remaining(&extension),
+            s->tlsext_debug_cb(s, 0, currext->type,
+                               PACKET_data(&currext->data),
+                               PACKET_remaining(&currext->data),
                                s->tlsext_debug_arg);
 
-        if (type == TLSEXT_TYPE_renegotiate) {
-            if (!ssl_parse_clienthello_renegotiate_ext(s, &extension, al))
+        if (currext->type == TLSEXT_TYPE_renegotiate) {
+            if (!ssl_parse_clienthello_renegotiate_ext(s,
+                    &currext->data, al))
                 return 0;
             renegotiate_seen = 1;
         } else if (s->version == SSL3_VERSION) {
@@ -1859,11 +1850,11 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET 
*pkt, int *al)
  *
  */
 
-        else if (type == TLSEXT_TYPE_server_name) {
+        else if (currext->type == TLSEXT_TYPE_server_name) {
             unsigned int servname_type;
             PACKET sni, hostname;
 
-            if (!PACKET_as_length_prefixed_2(&extension, &sni)
+            if (!PACKET_as_length_prefixed_2(&currext->data, &sni)
                 /* ServerNameList must be at least 1 byte long. */
                 || PACKET_remaining(&sni) == 0) {
                 return 0;
@@ -1915,10 +1906,10 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET 
*pkt, int *al)
             }
         }
 #ifndef OPENSSL_NO_SRP
-        else if (type == TLSEXT_TYPE_srp) {
+        else if (currext->type == TLSEXT_TYPE_srp) {
             PACKET srp_I;
 
-            if (!PACKET_as_length_prefixed_1(&extension, &srp_I))
+            if (!PACKET_as_length_prefixed_1(&currext->data, &srp_I))
                 return 0;
 
             if (PACKET_contains_zero_byte(&srp_I))
@@ -1936,10 +1927,11 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET 
*pkt, int *al)
 #endif
 
 #ifndef OPENSSL_NO_EC
-        else if (type == TLSEXT_TYPE_ec_point_formats) {
+        else if (currext->type == TLSEXT_TYPE_ec_point_formats) {
             PACKET ec_point_format_list;
 
-            if (!PACKET_as_length_prefixed_1(&extension, &ec_point_format_list)
+            if (!PACKET_as_length_prefixed_1(&currext->data,
+                                             &ec_point_format_list)
                 || PACKET_remaining(&ec_point_format_list) == 0) {
                 return 0;
             }
@@ -1953,11 +1945,12 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET 
*pkt, int *al)
                     return 0;
                 }
             }
-        } else if (type == TLSEXT_TYPE_elliptic_curves) {
+        } else if (currext->type == TLSEXT_TYPE_elliptic_curves) {
             PACKET elliptic_curve_list;
 
             /* Each NamedCurve is 2 bytes and we must have at least 1. */
-            if (!PACKET_as_length_prefixed_2(&extension, &elliptic_curve_list)
+            if (!PACKET_as_length_prefixed_2(&currext->data,
+                                             &elliptic_curve_list)
                 || PACKET_remaining(&elliptic_curve_list) == 0
                 || (PACKET_remaining(&elliptic_curve_list) % 2) != 0) {
                 return 0;
@@ -1974,19 +1967,20 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET 
*pkt, int *al)
             }
         }
 #endif                          /* OPENSSL_NO_EC */
-        else if (type == TLSEXT_TYPE_session_ticket) {
+        else if (currext->type == TLSEXT_TYPE_session_ticket) {
             if (s->tls_session_ticket_ext_cb &&
-                !s->tls_session_ticket_ext_cb(s, PACKET_data(&extension),
-                                              
(int)PACKET_remaining(&extension),
-                                              
s->tls_session_ticket_ext_cb_arg))
-            {
+                !s->tls_session_ticket_ext_cb(s,
+                    PACKET_data(&currext->data),
+                    PACKET_remaining(&currext->data),
+                    s->tls_session_ticket_ext_cb_arg)) {
                 *al = TLS1_AD_INTERNAL_ERROR;
                 return 0;
             }
-        } else if (type == TLSEXT_TYPE_signature_algorithms) {
+        } else if (currext->type == TLSEXT_TYPE_signature_algorithms) {
             PACKET supported_sig_algs;
 
-            if (!PACKET_as_length_prefixed_2(&extension, &supported_sig_algs)
+            if (!PACKET_as_length_prefixed_2(&currext->data,
+                                             &supported_sig_algs)
                 || (PACKET_remaining(&supported_sig_algs) % 2) != 0
                 || PACKET_remaining(&supported_sig_algs) == 0) {
                 return 0;
@@ -1998,8 +1992,8 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET 
*pkt, int *al)
                     return 0;
                 }
             }
-        } else if (type == TLSEXT_TYPE_status_request) {
-            if (!PACKET_get_1(&extension,
+        } else if (currext->type == TLSEXT_TYPE_status_request) {
+            if (!PACKET_get_1(&currext->data,
                               (unsigned int *)&s->tlsext_status_type)) {
                 return 0;
             }
@@ -2008,7 +2002,7 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET 
*pkt, int *al)
                 const unsigned char *ext_data;
                 PACKET responder_id_list, exts;
                 if (!PACKET_get_length_prefixed_2
-                    (&extension, &responder_id_list))
+                    (&currext->data, &responder_id_list))
                     return 0;
 
                 /*
@@ -2058,7 +2052,8 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET 
*pkt, int *al)
                 }
 
                 /* Read in request_extensions */
-                if (!PACKET_as_length_prefixed_2(&extension, &exts))
+                if (!PACKET_as_length_prefixed_2(
+                        &currext->data, &exts))
                     return 0;
 
                 if (PACKET_remaining(&exts) > 0) {
@@ -2083,11 +2078,11 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET 
*pkt, int *al)
             }
         }
 #ifndef OPENSSL_NO_HEARTBEATS
-        else if (SSL_IS_DTLS(s) && type == TLSEXT_TYPE_heartbeat) {
+        else if (SSL_IS_DTLS(s) && currext->type == TLSEXT_TYPE_heartbeat) {
             unsigned int hbtype;
 
-            if (!PACKET_get_1(&extension, &hbtype)
-                || PACKET_remaining(&extension)) {
+            if (!PACKET_get_1(&currext->data, &hbtype)
+                || PACKET_remaining(&currext->data)) {
                 *al = SSL_AD_DECODE_ERROR;
                 return 0;
             }
@@ -2106,8 +2101,8 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET 
*pkt, int *al)
         }
 #endif
 #ifndef OPENSSL_NO_NEXTPROTONEG
-        else if (type == TLSEXT_TYPE_next_proto_neg &&
-                 s->s3->tmp.finish_md_len == 0) {
+        else if (currext->type == TLSEXT_TYPE_next_proto_neg
+                 && s->s3->tmp.finish_md_len == 0) {
             /*-
              * We shouldn't accept this extension on a
              * renegotiation.
@@ -2129,26 +2124,29 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET 
*pkt, int *al)
         }
 #endif
 
-        else if (type == TLSEXT_TYPE_application_layer_protocol_negotiation &&
-                 s->s3->tmp.finish_md_len == 0) {
-            if (!tls1_alpn_handle_client_hello(s, &extension, al))
+        else if (currext->type
+                     == TLSEXT_TYPE_application_layer_protocol_negotiation
+                 && s->s3->tmp.finish_md_len == 0) {
+            if (!tls1_alpn_handle_client_hello(s,
+                    &currext->data, al))
                 return 0;
         }
 
         /* session ticket processed earlier */
 #ifndef OPENSSL_NO_SRTP
         else if (SSL_IS_DTLS(s) && SSL_get_srtp_profiles(s)
-                 && type == TLSEXT_TYPE_use_srtp) {
-            if (ssl_parse_clienthello_use_srtp_ext(s, &extension, al))
+                 && currext->type == TLSEXT_TYPE_use_srtp) {
+            if (ssl_parse_clienthello_use_srtp_ext(s,
+                    &currext->data, al))
                 return 0;
         }
 #endif
-        else if (type == TLSEXT_TYPE_encrypt_then_mac &&
-                 !(s->options & SSL_OP_NO_ENCRYPT_THEN_MAC))
+        else if (currext->type == TLSEXT_TYPE_encrypt_then_mac
+                 && !(s->options & SSL_OP_NO_ENCRYPT_THEN_MAC))
             s->s3->flags |= TLS1_FLAGS_ENCRYPT_THEN_MAC;
         /*
          * Note: extended master secret extension handled in
-         * tls_check_serverhello_tlsext_early()
+         * tls_check_client_ems_support()
          */
 
         /*
@@ -2159,22 +2157,13 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET 
*pkt, int *al)
          * ServerHello may be later returned.
          */
         else if (!s->hit) {
-            if (custom_ext_parse(s, 1, type, PACKET_data(&extension),
-                                 PACKET_remaining(&extension), al) <= 0)
+            if (custom_ext_parse(s, 1, currext->type,
+                    PACKET_data(&currext->data),
+                    PACKET_remaining(&currext->data), al) <= 0)
                 return 0;
         }
     }
 
-    if (PACKET_remaining(pkt) != 0) {
-        /*
-         * tls1_check_duplicate_extensions should ensure this never happens.
-         */
-        *al = SSL_AD_INTERNAL_ERROR;
-        return 0;
-    }
-
- ri_check:
-
     /* Need RI if renegotiating */
 
     if (!renegotiate_seen && s->renegotiate &&
@@ -2194,11 +2183,11 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET 
*pkt, int *al)
     return 1;
 }
 
-int ssl_parse_clienthello_tlsext(SSL *s, PACKET *pkt)
+int ssl_parse_clienthello_tlsext(SSL *s, CLIENTHELLO_MSG *hello)
 {
     int al = -1;
     custom_ext_init(&s->cert->srv_ext);
-    if (ssl_scan_clienthello_tlsext(s, pkt, &al) <= 0) {
+    if (ssl_scan_clienthello_tlsext(s, hello, &al) <= 0) {
         ssl3_send_alert(s, SSL3_AL_FATAL, al);
         return 0;
     }
@@ -2793,16 +2782,33 @@ int ssl_parse_serverhello_tlsext(SSL *s, PACKET *pkt)
     return 1;
 }
 
-/*-
- * Since the server cache lookup is done early on in the processing of the
- * ClientHello and other operations depend on the result some extensions
- * need to be handled at the same time.
+/*
+ * Given a list of extensions that we collected earlier, find one of a given
+ * type and return it.
+ *
+ * |exts| is the set of extensions previously collected.
+ * |numexts| is the number of extensions that we have.
+ * |type| the type of the extension that we are looking for.
  *
- * Two extensions are currently handled, session ticket and extended master
- * secret.
+ * Returns a pointer to the found RAW_EXTENSION data, or NULL if not found.
+ */
+static RAW_EXTENSION *get_extension_by_type(RAW_EXTENSION *exts, size_t 
numexts,
+                                            unsigned int type)
+{
+    size_t loop;
+
+    for (loop = 0; loop < numexts; loop++) {
+        if (exts[loop].type == type)
+            return &exts[loop];
+    }
+
+    return NULL;
+}
+
+/*-
+ * Gets the ticket information supplied by the client if any.
  *
- *   session_id: ClientHello session ID.
- *   ext: ClientHello extensions (including length prefix)
+ *   hello: The parsed ClientHello data
  *   ret: (output) on return, if a ticket was decrypted, then this is set to
  *       point to the resulting session.
  *
@@ -2826,116 +2832,105 @@ int ssl_parse_serverhello_tlsext(SSL *s, PACKET *pkt)
  *   a session ticket or we couldn't use the one it gave us, or if
  *   s->ctx->tlsext_ticket_key_cb asked to renew the client's ticket.
  *   Otherwise, s->tlsext_ticket_expected is set to 0.
- *
- *   For extended master secret flag is set if the extension is present.
- *
  */
-int tls_check_serverhello_tlsext_early(SSL *s, const PACKET *ext,
-                                       const PACKET *session_id,
-                                       SSL_SESSION **ret)
+int tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello,
+                               SSL_SESSION **ret)
 {
-    unsigned int i;
-    PACKET local_ext = *ext;
-    int retv = -1;
-
-    int have_ticket = 0;
-    int use_ticket = tls_use_ticket(s);
+    int retv;
+    const unsigned char *etick;
+    size_t size;
+    RAW_EXTENSION *ticketext;
 
     *ret = NULL;
     s->tlsext_ticket_expected = 0;
-    s->s3->flags &= ~TLS1_FLAGS_RECEIVED_EXTMS;
 
     /*
      * If tickets disabled behave as if no ticket present to permit stateful
      * resumption.
      */
-    if ((s->version <= SSL3_VERSION))
+    if (s->version <= SSL3_VERSION || !tls_use_ticket(s))
         return 0;
 
-    if (!PACKET_get_net_2(&local_ext, &i)) {
-        retv = 0;
-        goto end;
+    ticketext = get_extension_by_type(hello->pre_proc_exts,
+                                      hello->num_extensions,
+                                      TLSEXT_TYPE_session_ticket);
+    if (ticketext == NULL)
+        return 0;
+
+    size = PACKET_remaining(&ticketext->data);
+    if (size == 0) {
+        /*
+         * The client will accept a ticket but doesn't currently have
+         * one.
+         */
+        s->tlsext_ticket_expected = 1;
+        return 1;
     }
-    while (PACKET_remaining(&local_ext) >= 4) {
-        unsigned int type, size;
+    if (s->tls_session_secret_cb) {
+        /*
+         * Indicate that the ticket couldn't be decrypted rather than
+         * generating the session from ticket now, trigger
+         * abbreviated handshake based on external mechanism to
+         * calculate the master secret later.
+         */
+        return 2;
+    }
+    if (!PACKET_get_bytes(&ticketext->data, &etick, size)) {
+        /* Shouldn't ever happen */
+        return -1;
+    }
+    retv = tls_decrypt_ticket(s, etick, size, hello->session_id,
+                           hello->session_id_len, ret);
+    switch (retv) {
+    case 2:            /* ticket couldn't be decrypted */
+        s->tlsext_ticket_expected = 1;
+        return 2;
 
-        if (!PACKET_get_net_2(&local_ext, &type)
-            || !PACKET_get_net_2(&local_ext, &size)) {
-            /* Shouldn't ever happen */
-            retv = -1;
-            goto end;
-        }
-        if (PACKET_remaining(&local_ext) < size) {
-            retv = 0;
-            goto end;
-        }
-        if (type == TLSEXT_TYPE_session_ticket && use_ticket) {
-            int r;
-            const unsigned char *etick;
+    case 3:            /* ticket was decrypted */
+        return 3;
 
-            /* Duplicate extension */
-            if (have_ticket != 0) {
-                retv = -1;
-                goto end;
-            }
-            have_ticket = 1;
+    case 4:            /* ticket decrypted but need to renew */
+        s->tlsext_ticket_expected = 1;
+        return 3;
 
-            if (size == 0) {
-                /*
-                 * The client will accept a ticket but doesn't currently have
-                 * one.
-                 */
-                s->tlsext_ticket_expected = 1;
-                retv = 1;
-                continue;
-            }
-            if (s->tls_session_secret_cb) {
-                /*
-                 * Indicate that the ticket couldn't be decrypted rather than
-                 * generating the session from ticket now, trigger
-                 * abbreviated handshake based on external mechanism to
-                 * calculate the master secret later.
-                 */
-                retv = 2;
-                continue;
-            }
-            if (!PACKET_get_bytes(&local_ext, &etick, size)) {
-                /* Shouldn't ever happen */
-                retv = -1;
-                goto end;
-            }
-            r = tls_decrypt_ticket(s, etick, size, PACKET_data(session_id),
-                                   PACKET_remaining(session_id), ret);
-            switch (r) {
-            case 2:            /* ticket couldn't be decrypted */
-                s->tlsext_ticket_expected = 1;
-                retv = 2;
-                break;
-            case 3:            /* ticket was decrypted */
-                retv = r;
-                break;
-            case 4:            /* ticket decrypted but need to renew */
-                s->tlsext_ticket_expected = 1;
-                retv = 3;
-                break;
-            default:           /* fatal error */
-                retv = -1;
-                break;
-            }
-            continue;
-        } else {
-            if (type == TLSEXT_TYPE_extended_master_secret)
-                s->s3->flags |= TLS1_FLAGS_RECEIVED_EXTMS;
-            if (!PACKET_forward(&local_ext, size)) {
-                retv = -1;
-                goto end;
-            }
-        }
+    default:           /* fatal error */
+        return -1;
     }
-    if (have_ticket == 0)
-        retv = 0;
- end:
-    return retv;
+}
+
+/*
+ * Sets the extended master secret flag if the extension is present in the
+ * ClientHello
+ * Returns:
+ *  1 on success
+ *  0 on error
+ */
+int tls_check_client_ems_support(SSL *s, const CLIENTHELLO_MSG *hello)
+{
+    RAW_EXTENSION *emsext;
+
+    s->s3->flags &= ~TLS1_FLAGS_RECEIVED_EXTMS;
+
+    if (s->version <= SSL3_VERSION)
+        return 1;
+
+    emsext = get_extension_by_type(hello->pre_proc_exts, hello->num_extensions,
+                                   TLSEXT_TYPE_extended_master_secret);
+
+    /*
+     * No extensions is a success - we have successfully discovered that the
+     * client doesn't support EMS.
+     */
+    if (emsext == NULL)
+        return 1;
+
+    /* The extensions must always be empty */
+    if (PACKET_remaining(&emsext->data) != 0)
+        return 0;
+
+    s->s3->flags |= TLS1_FLAGS_RECEIVED_EXTMS;
+
+    return 1;
 }
 
 /*-
_____
openssl-commits mailing list
To unsubscribe: https://mta.openssl.org/mailman/listinfo/openssl-commits

Reply via email to