Refactor ClientHello processing so that extensions get parsed earlier
authorMatt Caswell <matt@openssl.org>
Sat, 22 Oct 2016 16:24:37 +0000 (17:24 +0100)
committerMatt Caswell <matt@openssl.org>
Wed, 9 Nov 2016 09:10:29 +0000 (09:10 +0000)
Reviewed-by: Kurt Roeckx <kurt@openssl.org>
Reviewed-by: Rich Salz <rsalz@openssl.org>
include/openssl/ssl.h
ssl/ssl_err.c
ssl/ssl_locl.h
ssl/ssl_sess.c
ssl/statem/statem_lib.c
ssl/statem/statem_locl.h
ssl/statem/statem_srvr.c
ssl/t1_lib.c

index c781323..905eeb5 100644 (file)
@@ -2256,6 +2256,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_F_TLS_CONSTRUCT_SERVER_KEY_EXCHANGE          377
 # define SSL_F_TLS_GET_MESSAGE_BODY                       351
 # define SSL_F_TLS_GET_MESSAGE_HEADER                     387
+# define SSL_F_TLS_PARSE_RAW_EXTENSIONS                   432
 # define SSL_F_TLS_POST_PROCESS_CLIENT_HELLO              378
 # define SSL_F_TLS_POST_PROCESS_CLIENT_KEY_EXCHANGE       384
 # define SSL_F_TLS_PREPARE_CLIENT_CERTIFICATE             360
index 5c2e961..eb5675b 100644 (file)
@@ -279,6 +279,7 @@ static ERR_STRING_DATA SSL_str_functs[] = {
      "tls_construct_server_key_exchange"},
     {ERR_FUNC(SSL_F_TLS_GET_MESSAGE_BODY), "tls_get_message_body"},
     {ERR_FUNC(SSL_F_TLS_GET_MESSAGE_HEADER), "tls_get_message_header"},
+    {ERR_FUNC(SSL_F_TLS_PARSE_RAW_EXTENSIONS), "tls_parse_raw_extensions"},
     {ERR_FUNC(SSL_F_TLS_POST_PROCESS_CLIENT_HELLO),
      "tls_post_process_client_hello"},
     {ERR_FUNC(SSL_F_TLS_POST_PROCESS_CLIENT_KEY_EXCHANGE),
index 0c6bd31..8f7f930 100644 (file)
@@ -1624,6 +1624,29 @@ typedef struct ssl3_comp_st {
 } SSL3_COMP;
 # endif
 
+typedef struct {
+    unsigned int type;
+    PACKET data;
+} RAW_EXTENSION;
+
+#define MAX_COMPRESSIONS_SIZE   255
+
+typedef struct {
+    unsigned int isv2;
+    unsigned int 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, CLIENTHELLO_MSG *hello);
 
 __owur int tls12_get_sigandhash(WPACKET *pkt, const EVP_PKEY *pk,
                                 const EVP_MD *md);
index 44101cb..a8bfeb7 100644 (file)
@@ -445,7 +445,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 +454,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 +479,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 +499,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) {
index 24159da..3d2e3f3 100644 (file)
@@ -152,6 +152,94 @@ static void ssl3_take_mac(SSL *s)
 }
 #endif
 
+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;
+    else
+        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.
+ */
+int tls_parse_raw_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 done;
+        }
+        num_extensions++;
+    }
+
+    if (num_extensions > 0) {
+        raw_extensions = OPENSSL_malloc(sizeof(RAW_EXTENSION) * num_extensions);
+        if (raw_extensions == NULL) {
+            *ad = SSL_AD_INTERNAL_ERROR;
+            SSLerr(SSL_F_TLS_PARSE_RAW_EXTENSIONS, ERR_R_MALLOC_FAILURE);
+            goto done;
+        }
+
+        /* Second pass: gather the extension types. */
+        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_PARSE_RAW_EXTENSIONS, ERR_R_INTERNAL_ERROR);
+                goto done;
+            }
+        }
+
+        if (PACKET_remaining(packet) != 0) {
+            *ad = SSL_AD_DECODE_ERROR;
+            SSLerr(SSL_F_TLS_PARSE_RAW_EXTENSIONS, SSL_R_LENGTH_MISMATCH);
+            goto done;
+        }
+        /* Sort the extensions and make sure there are no duplicates. */
+        qsort(raw_extensions, num_extensions, sizeof(RAW_EXTENSION),
+              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 done;
+            }
+        }
+    }
+
+    *res = raw_extensions;
+    *numfound = num_extensions;
+    return 1;
+
+ done:
+    OPENSSL_free(raw_extensions);
+    return 0;
+}
+
+
+
 MSG_PROCESS_RETURN tls_process_change_cipher_spec(SSL *s, PACKET *pkt)
 {
     int al;
@@ -875,7 +963,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 +975,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->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)
index a360fc9..9c1def7 100644 (file)
@@ -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_parse_raw_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);
index 6aa897b..9911e3c 100644 (file)
@@ -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,16 +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 step is to parse the raw ClientHello data into the CLIENTHELLO_MSG
+     * structure.
+     */
+
+    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
@@ -934,73 +938,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.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.
          */
-        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,
@@ -1008,6 +964,7 @@ 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;
 
         if (session_id_len > SSL_MAX_SSL_SESSION_ID_LENGTH) {
             al = SSL_AD_DECODE_ERROR;
@@ -1015,8 +972,10 @@ 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_get_sub_packet(pkt, &session_id,
+                                      clienthello.session_id_len)
             || !PACKET_get_sub_packet(pkt, &challenge, challenge_len)
             /* No extensions. */
             || PACKET_remaining(pkt) != 0) {
@@ -1029,9 +988,9 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
         /* 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);
+        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,55 +999,136 @@ 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)
+        if (!PACKET_copy_bytes(pkt, clienthello.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) {
-            al = SSL_AD_DECODE_ERROR;
-            SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_LENGTH_MISMATCH);
-            goto f_err;
-        }
-
         if (SSL_IS_DTLS(s)) {
             if (!PACKET_get_length_prefixed_1(pkt, &cookie)) {
                 al = SSL_AD_DECODE_ERROR;
                 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)
+                || !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;
         }
+
         /* 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(&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;
+    }
+
+    /* We preserve the raw extensions PACKET for later use */
+    extensions = clienthello.extensions;
+    if (!tls_parse_raw_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.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 ((clienthello.version & 0xff00) == (SSL3_VERSION_MAJOR << 8)) {
+            /* SSLv3/TLS */
+            s->client_version = clienthello.version;
+        } else {
+            /* No idea what protocol this is */
+            SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_UNKNOWN_PROTOCOL);
+            goto 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, &clienthello);
+    } else if (s->method->version != DTLS_ANY_VERSION &&
+               DTLS_VERSION_LT((int)clienthello.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 = clienthello.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 +1136,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 +1146,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 +1158,15 @@ 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 +1183,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 +1211,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 +1247,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 +1260,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 +1353,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 +1374,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;
                 }
index 0523e54..e8357af 100644 (file)
@@ -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, 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)
@@ -1763,11 +1763,10 @@ static void ssl_check_for_safari(SSL *s, const PACKET *pkt)
  * Consumes the entire packet in |pkt|. 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 +1788,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 +1803,21 @@ 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++) {
         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, hello->pre_proc_exts[loop].type,
+                               PACKET_data(&hello->pre_proc_exts[loop].data),
+                               PACKET_remaining(&hello->pre_proc_exts[loop].data),
                                s->tlsext_debug_arg);
 
-        if (type == TLSEXT_TYPE_renegotiate) {
-            if (!ssl_parse_clienthello_renegotiate_ext(s, &extension, al))
+        if (hello->pre_proc_exts[loop].type == TLSEXT_TYPE_renegotiate) {
+            if (!ssl_parse_clienthello_renegotiate_ext(s,
+                    &hello->pre_proc_exts[loop].data, al))
                 return 0;
             renegotiate_seen = 1;
         } else if (s->version == SSL3_VERSION) {
@@ -1859,11 +1847,12 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET *pkt, int *al)
  *
  */
 
-        else if (type == TLSEXT_TYPE_server_name) {
+        else if (hello->pre_proc_exts[loop].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(&hello->pre_proc_exts[loop].data,
+                                             &sni)
                 /* ServerNameList must be at least 1 byte long. */
                 || PACKET_remaining(&sni) == 0) {
                 return 0;
@@ -1915,10 +1904,11 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET *pkt, int *al)
             }
         }
 #ifndef OPENSSL_NO_SRP
-        else if (type == TLSEXT_TYPE_srp) {
+        else if (hello->pre_proc_exts[loop].type == TLSEXT_TYPE_srp) {
             PACKET srp_I;
 
-            if (!PACKET_as_length_prefixed_1(&extension, &srp_I))
+            if (!PACKET_as_length_prefixed_1(&hello->pre_proc_exts[loop].data,
+                                             &srp_I))
                 return 0;
 
             if (PACKET_contains_zero_byte(&srp_I))
@@ -1936,10 +1926,12 @@ 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 (hello->pre_proc_exts[loop].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(&hello->pre_proc_exts[loop].data,
+                                             &ec_point_format_list)
                 || PACKET_remaining(&ec_point_format_list) == 0) {
                 return 0;
             }
@@ -1953,11 +1945,13 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET *pkt, int *al)
                     return 0;
                 }
             }
-        } else if (type == TLSEXT_TYPE_elliptic_curves) {
+        } else if (hello->pre_proc_exts[loop].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(&hello->pre_proc_exts[loop].data,
+                                             &elliptic_curve_list)
                 || PACKET_remaining(&elliptic_curve_list) == 0
                 || (PACKET_remaining(&elliptic_curve_list) % 2) != 0) {
                 return 0;
@@ -1974,19 +1968,22 @@ 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 (hello->pre_proc_exts[loop].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(&hello->pre_proc_exts[loop].data),
+                    PACKET_remaining(&hello->pre_proc_exts[loop].data),
+                    s->tls_session_ticket_ext_cb_arg)) {
                 *al = TLS1_AD_INTERNAL_ERROR;
                 return 0;
             }
-        } else if (type == TLSEXT_TYPE_signature_algorithms) {
+        } else if (hello->pre_proc_exts[loop].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(&hello->pre_proc_exts[loop].data,
+                                             &supported_sig_algs)
                 || (PACKET_remaining(&supported_sig_algs) % 2) != 0
                 || PACKET_remaining(&supported_sig_algs) == 0) {
                 return 0;
@@ -1998,8 +1995,9 @@ 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 (hello->pre_proc_exts[loop].type
+                   == TLSEXT_TYPE_status_request) {
+            if (!PACKET_get_1(&hello->pre_proc_exts[loop].data,
                               (unsigned int *)&s->tlsext_status_type)) {
                 return 0;
             }
@@ -2008,7 +2006,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))
+                    (&hello->pre_proc_exts[loop].data, &responder_id_list))
                     return 0;
 
                 /*
@@ -2058,7 +2056,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(
+                        &hello->pre_proc_exts[loop].data, &exts))
                     return 0;
 
                 if (PACKET_remaining(&exts) > 0) {
@@ -2083,11 +2082,12 @@ 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)
+                 && hello->pre_proc_exts[loop].type == TLSEXT_TYPE_heartbeat) {
             unsigned int hbtype;
 
-            if (!PACKET_get_1(&extension, &hbtype)
-                || PACKET_remaining(&extension)) {
+            if (!PACKET_get_1(&hello->pre_proc_exts[loop].data, &hbtype)
+                || PACKET_remaining(&hello->pre_proc_exts[loop].data)) {
                 *al = SSL_AD_DECODE_ERROR;
                 return 0;
             }
@@ -2106,8 +2106,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 (hello->pre_proc_exts[loop].type == TLSEXT_TYPE_next_proto_neg
+                 && s->s3->tmp.finish_md_len == 0) {
             /*-
              * We shouldn't accept this extension on a
              * renegotiation.
@@ -2129,26 +2129,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 (hello->pre_proc_exts[loop].type
+                     == TLSEXT_TYPE_application_layer_protocol_negotiation
+                 && s->s3->tmp.finish_md_len == 0) {
+            if (!tls1_alpn_handle_client_hello(s,
+                    &hello->pre_proc_exts[loop].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))
+                 && hello->pre_proc_exts[loop].type == TLSEXT_TYPE_use_srtp) {
+            if (ssl_parse_clienthello_use_srtp_ext(s,
+                    &hello->pre_proc_exts[loop].data, al))
                 return 0;
         }
 #endif
-        else if (type == TLSEXT_TYPE_encrypt_then_mac &&
-                 !(s->options & SSL_OP_NO_ENCRYPT_THEN_MAC))
+        else if (hello->pre_proc_exts[loop].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 +2162,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, hello->pre_proc_exts[loop].type,
+                    PACKET_data(&hello->pre_proc_exts[loop].data),
+                    PACKET_remaining(&hello->pre_proc_exts[loop].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 +2188,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 +2787,23 @@ int ssl_parse_serverhello_tlsext(SSL *s, PACKET *pkt)
     return 1;
 }
 
+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;
+}
+
 /*-
- * 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.
+ * Gets the ticket information supplied by the client if any.
  *
- * Two extensions are currently handled, session ticket and extended master
- * secret.
- *
- *   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 +2827,102 @@ 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 is set if the extension is present
+ * in the ClientHello
+ */
+int tls_check_client_ems_support(SSL *s, 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;
 }
 
 /*-