Appease gcc's Wmaybe-uninitialized
[openssl.git] / ssl / s3_srvr.c
index acb2fa94bc4edd8f91e48580603ed639eca9d75a..7bf5828ad38ae04987dbb9a386c8683bccc69c04 100644 (file)
 #include <openssl/bn.h>
 #include <openssl/md5.h>
 
-static STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, unsigned char *p,
-    int num, STACK_OF(SSL_CIPHER) **skp, int sslv2format);
+static STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s,
+                                                      PACKET *cipher_suites,
+                                                      STACK_OF(SSL_CIPHER) **skp,
+                                                      int sslv2format, int *al);
 
 
 #ifndef OPENSSL_NO_SRP
@@ -444,7 +446,7 @@ int ssl3_accept(SSL *s)
                  */
 #ifndef OPENSSL_NO_PSK
                 /* Only send SKE if we have identity hint for plain PSK */
-                || ((alg_k & (SSL_kPSK | SSL_kRSAPSK)) && s->ctx->psk_identity_hint)
+                || ((alg_k & (SSL_kPSK | SSL_kRSAPSK)) && s->cert->psk_identity_hint)
                 /* For other PSK always send SKE */
                 || (alg_k & (SSL_PSK & (SSL_kDHEPSK | SSL_kECDHEPSK)))
 #endif
@@ -838,19 +840,18 @@ int ssl3_send_hello_request(SSL *s)
 int ssl3_get_client_hello(SSL *s)
 {
     int i, ok, al = SSL_AD_INTERNAL_ERROR, ret = -1;
-    unsigned int j, cipherlen, complen;
-    unsigned int cookie_len = 0;
+    unsigned int j, complen = 0;
     long n;
     unsigned long id;
     SSL_CIPHER *c;
 #ifndef OPENSSL_NO_COMP
-    unsigned char *q = NULL;
     SSL_COMP *comp = NULL;
 #endif
     STACK_OF(SSL_CIPHER) *ciphers = NULL;
     int protverr = 1;
-    PACKET pkt;
-    unsigned char *sess, *cdata;
+    /* |cookie| will only be initialized for DTLS. */
+    PACKET pkt, session_id, cipher_suites, compression, extensions, cookie;
+    int is_v2_record;
 
     if (s->state == SSL3_ST_SR_CLNT_HELLO_C && !s->first_packet)
         goto retry_cert;
@@ -874,10 +875,17 @@ int ssl3_get_client_hello(SSL *s)
     if (!ok)
         return ((int)n);
     s->first_packet = 0;
-    PACKET_buf_init(&pkt, s->init_msg, n);
+    if (!PACKET_buf_init(&pkt, s->init_msg, n)) {
+        SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, ERR_R_INTERNAL_ERROR);
+        al = SSL_AD_INTERNAL_ERROR;
+        goto f_err;
+    }
+
+    is_v2_record = RECORD_LAYER_is_sslv2_record(&s->rlayer);
 
+    PACKET_null_init(&cookie);
     /* First lets get s->client_version set correctly */
-    if (RECORD_LAYER_is_sslv2_record(&s->rlayer)) {
+    if (is_v2_record) {
         unsigned int version;
         unsigned int mt;
         /*-
@@ -1003,325 +1011,232 @@ int ssl3_get_client_hello(SSL *s)
         goto f_err;
     }
 
-    if (RECORD_LAYER_is_sslv2_record(&s->rlayer)) {
+    /* Parse the message and load client random. */
+    if (is_v2_record) {
         /*
          * 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 csl, sil, cl;
-
-        if (!PACKET_get_net_2(&pkt, &csl)
-                || !PACKET_get_net_2(&pkt, &sil)
-                || !PACKET_get_net_2(&pkt, &cl)) {
-            SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_RECORD_LENGTH_MISMATCH);
-            al = SSL_AD_DECODE_ERROR;
-            goto f_err;
-        }
-
-        if (csl == 0) {
-            /* we need at least one cipher */
-            al = SSL_AD_ILLEGAL_PARAMETER;
-            SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_NO_CIPHERS_SPECIFIED);
-            goto f_err;
-        }
+        unsigned int cipher_len, session_id_len, challenge_len;
+        PACKET challenge;
 
-        if (!PACKET_get_bytes(&pkt, &cdata, csl)) {
+        if (!PACKET_get_net_2(&pkt, &cipher_len)
+                || !PACKET_get_net_2(&pkt, &session_id_len)
+                || !PACKET_get_net_2(&pkt, &challenge_len)) {
             SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_RECORD_LENGTH_MISMATCH);
             al = SSL_AD_DECODE_ERROR;
             goto f_err;
         }
 
-        if (ssl_bytes_to_cipher_list(s, cdata, csl, &(ciphers), 1) == NULL) {
-            goto err;
-        }
-
-        /*
-         * Ignore any session id. We don't allow resumption in a backwards
-         * compatible ClientHello
-         */
-        if (!PACKET_forward(&pkt, sil)) {
+        if (!PACKET_get_sub_packet(&pkt, &cipher_suites, cipher_len)
+            || !PACKET_get_sub_packet(&pkt, &session_id, session_id_len)
+            || !PACKET_get_sub_packet(&pkt, &challenge, challenge_len)
+            /* No extensions. */
+            || PACKET_remaining(&pkt) != 0) {
             SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_RECORD_LENGTH_MISMATCH);
             al = SSL_AD_DECODE_ERROR;
             goto f_err;
         }
-        s->hit = 0;
-
-        if (!ssl_get_new_session(s, 1))
-            goto err;
 
         /* Load the client random */
-        i = (cl > SSL3_RANDOM_SIZE) ? SSL3_RANDOM_SIZE : cl;
+        challenge_len = challenge_len > SSL3_RANDOM_SIZE ? SSL3_RANDOM_SIZE :
+            challenge_len;
         memset(s->s3->client_random, 0, SSL3_RANDOM_SIZE);
-        if (!PACKET_peek_copy_bytes(&pkt, s->s3->client_random, i)
-                || !PACKET_forward(&pkt, cl)
-                || !PACKET_remaining(&pkt) == 0) {
-            SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_RECORD_LENGTH_MISMATCH);
-            al = SSL_AD_DECODE_ERROR;
+        if (!PACKET_copy_bytes(&challenge,
+                               s->s3->client_random + SSL3_RANDOM_SIZE -
+                               challenge_len, challenge_len)) {
+            SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, ERR_R_INTERNAL_ERROR);
+            al = SSL_AD_INTERNAL_ERROR;
             goto f_err;
         }
 
-        /* No compression, so set complen to 0 */
-        complen = 0;
+        PACKET_null_init(&compression);
+        PACKET_null_init(&extensions);
     } else {
-        /* If we get here we've got SSLv3+ in an SSLv3+ record */
-
-        /* load the client random and get the session-id */
+        /* Regular ClientHello. */
         if (!PACKET_copy_bytes(&pkt, s->s3->client_random, SSL3_RANDOM_SIZE)
-                || !PACKET_get_1(&pkt, &j)
-                || !PACKET_get_bytes(&pkt, &sess, j)) {
+            || !PACKET_get_length_prefixed_1(&pkt, &session_id)) {
             al = SSL_AD_DECODE_ERROR;
-            SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_TOO_SHORT);
+            SSLerr(SSL_F_SSL3_GET_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_peek_1(&pkt, &cookie_len)) {
+        if (SSL_IS_DTLS(s)) {
+            if (!PACKET_get_length_prefixed_1(&pkt, &cookie)) {
                 al = SSL_AD_DECODE_ERROR;
-                SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_TOO_SHORT);
+                SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_MISMATCH);
                 goto f_err;
             }
-
-            if (cookie_len == 0)
-                return 1;
-        }
-
-        s->hit = 0;
-        /*
-         * Versions before 0.9.7 always allow clients to resume sessions in
-         * renegotiation. 0.9.7 and later allow this by default, but optionally
-         * ignore resumption requests with flag
-         * SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION (it's a new flag rather
-         * than a change to default behavior so that applications relying on
-         * this for security won't even compile against older library versions).
-         * 1.0.1 and later also have a function SSL_renegotiate_abbreviated() to
-         * request renegotiation but not a new session (s->new_session remains
-         * unset): for servers, this essentially just means that the
-         * SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION setting will be
-         * ignored.
-         */
-        if ((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, &pkt, sess, j);
             /*
-             * Only resume if the session's version matches the negotiated
-             * version.
-             * RFC 5246 does not provide much useful advice on resumption
-             * with a different protocol version. It doesn't forbid it but
-             * the sanity of such behaviour would be questionable.
-             * In practice, clients do not accept a version mismatch and
-             * will abort the handshake with an error.
+             * 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 (i == 1 && s->version == s->session->ssl_version) {
-                /* previous session */
-                s->hit = 1;
-            } else if (i == -1)
-                goto err;
-            else {
-                /* i == 0 */
-                if (!ssl_get_new_session(s, 1))
-                    goto err;
+            if (SSL_get_options(s) & SSL_OP_COOKIE_EXCHANGE) {
+                if (PACKET_remaining(&cookie) == 0)
+                return 1;
             }
         }
 
-        if (SSL_IS_DTLS(s)) {
-            if (!PACKET_get_1(&pkt, &cookie_len)) {
-                al = SSL_AD_DECODE_ERROR;
-                SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_TOO_SHORT);
-                goto f_err;
-            }
-            /*
-             * The ClientHello may contain a cookie even if the
-             * HelloVerify message has not been sent--make sure that it
-             * does not cause an overflow.
-             */
-            if (cookie_len > sizeof(s->d1->rcvd_cookie)) {
-                /* too much data */
+        if (!PACKET_get_length_prefixed_2(&pkt, &cipher_suites)
+            || !PACKET_get_length_prefixed_1(&pkt, &compression)) {
                 al = SSL_AD_DECODE_ERROR;
-                SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_COOKIE_MISMATCH);
+                SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_MISMATCH);
                 goto f_err;
-            }
+        }
+        /* Could be empty. */
+        extensions = pkt;
+    }
 
-            /* verify the cookie if appropriate option is set. */
-            if ((SSL_get_options(s) & SSL_OP_COOKIE_EXCHANGE)
-                    && cookie_len > 0) {
-                /* Get cookie */
-                if (!PACKET_copy_bytes(&pkt, s->d1->rcvd_cookie,
-                                              cookie_len)) {
-                    al = SSL_AD_DECODE_ERROR;
-                    SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_TOO_SHORT);
-                    goto f_err;
-                }
+    s->hit = 0;
 
-                if (s->ctx->app_verify_cookie_cb != NULL) {
-                    if (s->ctx->app_verify_cookie_cb(s, s->d1->rcvd_cookie,
-                                                     cookie_len) == 0) {
-                        al = SSL_AD_HANDSHAKE_FAILURE;
-                        SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,
-                               SSL_R_COOKIE_MISMATCH);
-                        goto f_err;
-                    }
-                    /* else cookie verification succeeded */
-                }
-                /* default verification */
-                else if (memcmp(s->d1->rcvd_cookie, s->d1->cookie,
-                                s->d1->cookie_len) != 0) {
-                    al = SSL_AD_HANDSHAKE_FAILURE;
-                    SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_COOKIE_MISMATCH);
-                    goto f_err;
-                }
-                /* Set to -2 so if successful we return 2 */
-                ret = -2;
-            } else {
-                /* Skip over cookie */
-                if (!PACKET_forward(&pkt, cookie_len)) {
-                    al = SSL_AD_DECODE_ERROR;
-                    SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_TOO_SHORT);
-                    goto f_err;
-                }
-            }
+    /*
+     * We don't allow resumption in a backwards compatible ClientHello.
+     * TODO(openssl-team): in TLS1.1+, session_id MUST be empty.
+     *
+     * Versions before 0.9.7 always allow clients to resume sessions in
+     * renegotiation. 0.9.7 and later allow this by default, but optionally
+     * ignore resumption requests with flag
+     * SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION (it's a new flag rather
+     * than a change to default behavior so that applications relying on
+     * this for security won't even compile against older library versions).
+     * 1.0.1 and later also have a function SSL_renegotiate_abbreviated() to
+     * request renegotiation but not a new session (s->new_session remains
+     * unset): for servers, this essentially just means that the
+     * SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION setting will be
+     * ignored.
+     */
+    if (is_v2_record ||
+        (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);
+        /*
+         * Only resume if the session's version matches the negotiated
+         * version.
+         * RFC 5246 does not provide much useful advice on resumption
+         * with a different protocol version. It doesn't forbid it but
+         * the sanity of such behaviour would be questionable.
+         * In practice, clients do not accept a version mismatch and
+         * will abort the handshake with an error.
+         */
+        if (i == 1 && s->version == s->session->ssl_version) {
+            /* previous session */
+            s->hit = 1;
+        } else if (i == -1) {
+            goto err;
+        } else {
+            /* i == 0 */
+            if (!ssl_get_new_session(s, 1))
+                goto err;
+        }
+    }
 
-            if (s->method->version == DTLS_ANY_VERSION) {
-                /* Select version to use */
-                if (s->client_version <= DTLS1_2_VERSION &&
-                    !(s->options & SSL_OP_NO_DTLSv1_2)) {
-                    s->version = DTLS1_2_VERSION;
-                    s->method = DTLSv1_2_server_method();
-                } else if (tls1_suiteb(s)) {
-                    SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,
-                           SSL_R_ONLY_DTLS_1_2_ALLOWED_IN_SUITEB_MODE);
-                    s->version = s->client_version;
-                    al = SSL_AD_PROTOCOL_VERSION;
-                    goto f_err;
-                } else if (s->client_version <= DTLS1_VERSION &&
-                           !(s->options & SSL_OP_NO_DTLSv1)) {
-                    s->version = DTLS1_VERSION;
-                    s->method = DTLSv1_server_method();
-                } else {
+    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),
+                                                 PACKET_remaining(&cookie)) == 0) {
+                    al = SSL_AD_HANDSHAKE_FAILURE;
                     SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,
-                           SSL_R_WRONG_VERSION_NUMBER);
-                    s->version = s->client_version;
-                    al = SSL_AD_PROTOCOL_VERSION;
+                           SSL_R_COOKIE_MISMATCH);
                     goto f_err;
+                    /* else cookie verification succeeded */
                 }
-                s->session->ssl_version = s->version;
+            /* default verification */
+            } else if (!PACKET_equal(&cookie, s->d1->cookie,
+                                     s->d1->cookie_len)) {
+                al = SSL_AD_HANDSHAKE_FAILURE;
+                SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_COOKIE_MISMATCH);
+                goto f_err;
             }
+            /* Set to -2 so if successful we return 2 */
+            ret = -2;
         }
-
-        if (!PACKET_get_net_2(&pkt, &cipherlen)) {
-            al = SSL_AD_DECODE_ERROR;
-            SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_TOO_SHORT);
-            goto f_err;
-        }
-
-        if (cipherlen == 0) {
-            al = SSL_AD_ILLEGAL_PARAMETER;
-            SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_NO_CIPHERS_SPECIFIED);
-            goto f_err;
-        }
-
-        if (!PACKET_get_bytes(&pkt, &cdata, cipherlen)) {
-            /* not enough data */
-            al = SSL_AD_DECODE_ERROR;
-            SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_MISMATCH);
-            goto f_err;
+        if (s->method->version == DTLS_ANY_VERSION) {
+            /* Select version to use */
+            if (s->client_version <= DTLS1_2_VERSION &&
+                !(s->options & SSL_OP_NO_DTLSv1_2)) {
+                s->version = DTLS1_2_VERSION;
+                s->method = DTLSv1_2_server_method();
+            } else if (tls1_suiteb(s)) {
+                SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,
+                       SSL_R_ONLY_DTLS_1_2_ALLOWED_IN_SUITEB_MODE);
+                s->version = s->client_version;
+                al = SSL_AD_PROTOCOL_VERSION;
+                goto f_err;
+            } else if (s->client_version <= DTLS1_VERSION &&
+                       !(s->options & SSL_OP_NO_DTLSv1)) {
+                s->version = DTLS1_VERSION;
+                s->method = DTLSv1_server_method();
+            } else {
+                SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,
+                       SSL_R_WRONG_VERSION_NUMBER);
+                s->version = s->client_version;
+                al = SSL_AD_PROTOCOL_VERSION;
+                goto f_err;
+            }
+            s->session->ssl_version = s->version;
         }
+    }
 
-        if (ssl_bytes_to_cipher_list(s, cdata, cipherlen, &(ciphers), 0) == NULL) {
-            goto err;
-        }
+    if (ssl_bytes_to_cipher_list(s, &cipher_suites, &(ciphers),
+                                 is_v2_record, &al) == NULL) {
+        goto f_err;
+    }
 
-        /* If it is a hit, check that the cipher is in the list */
-        if (s->hit) {
-            j = 0;
-            id = s->session->cipher->id;
+    /* If it is a hit, check that the cipher is in the list */
+    if (s->hit) {
+        j = 0;
+        id = s->session->cipher->id;
 
 #ifdef CIPHER_DEBUG
-            fprintf(stderr, "client sent %d ciphers\n",
-                    sk_SSL_CIPHER_num(ciphers));
+        fprintf(stderr, "client sent %d ciphers\n",
+                sk_SSL_CIPHER_num(ciphers));
 #endif
-            for (i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) {
-                c = sk_SSL_CIPHER_value(ciphers, i);
+        for (i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) {
+            c = sk_SSL_CIPHER_value(ciphers, i);
 #ifdef CIPHER_DEBUG
-                fprintf(stderr, "client [%2d of %2d]:%s\n",
-                        i, sk_SSL_CIPHER_num(ciphers), SSL_CIPHER_get_name(c));
+            fprintf(stderr, "client [%2d of %2d]:%s\n",
+                    i, sk_SSL_CIPHER_num(ciphers), SSL_CIPHER_get_name(c));
 #endif
-                if (c->id == id) {
-                    j = 1;
-                    break;
-                }
+            if (c->id == id) {
+                j = 1;
+                break;
             }
+        }
+        if (j == 0) {
             /*
-             * Disabled because it can be used in a ciphersuite downgrade
-             * attack:
-             * CVE-2010-4180.
+             * we need to have the cipher in the cipher list if we are asked
+             * to reuse it
              */
-#if 0
-            if (j == 0 && (s->options & SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG)
-                && (sk_SSL_CIPHER_num(ciphers) == 1)) {
-                /*
-                 * Special case as client bug workaround: the previously used
-                 * cipher may not be in the current list, the client instead
-                 * might be trying to continue using a cipher that before wasn't
-                 * chosen due to server preferences.  We'll have to reject the
-                 * connection if the cipher is not enabled, though.
-                 */
-                c = sk_SSL_CIPHER_value(ciphers, 0);
-                if (sk_SSL_CIPHER_find(SSL_get_ciphers(s), c) >= 0) {
-                    s->session->cipher = c;
-                    j = 1;
-                }
-            }
-#endif
-            if (j == 0) {
-                /*
-                 * we need to have the cipher in the cipher list if we are asked
-                 * to reuse it
-                 */
-                al = SSL_AD_ILLEGAL_PARAMETER;
-                SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,
-                       SSL_R_REQUIRED_CIPHER_MISSING);
-                goto f_err;
-            }
-        }
-
-        /* compression */
-        if (!PACKET_get_1(&pkt, &complen)
-            || !PACKET_get_bytes(&pkt, &cdata, complen)) {
-            /* not enough data */
-            al = SSL_AD_DECODE_ERROR;
-            SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_MISMATCH);
+            al = SSL_AD_ILLEGAL_PARAMETER;
+            SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,
+                   SSL_R_REQUIRED_CIPHER_MISSING);
             goto f_err;
         }
+    }
 
-#ifndef OPENSSL_NO_COMP
-        q = cdata;
-#endif
-        for (j = 0; j < complen; j++) {
-            if (cdata[j] == 0)
-                break;
-        }
-
-        if (j >= complen) {
-            /* no compress */
-            al = SSL_AD_DECODE_ERROR;
-            SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_NO_COMPRESSION_SPECIFIED);
-            goto f_err;
-        }
+    complen = PACKET_remaining(&compression);
+    for (j = 0; j < complen; j++) {
+        if (PACKET_data(&compression)[j] == 0)
+            break;
     }
 
+    if (j >= complen) {
+        /* no compress */
+        al = SSL_AD_DECODE_ERROR;
+        SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_NO_COMPRESSION_SPECIFIED);
+        goto f_err;
+    }
+    
     /* TLS extensions */
     if (s->version >= SSL3_VERSION) {
-        if (!ssl_parse_clienthello_tlsext(s, &pkt)) {
+        if (!ssl_parse_clienthello_tlsext(s, &extensions)) {
             SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_PARSE_TLSEXT);
             goto err;
         }
@@ -1409,7 +1324,7 @@ int ssl3_get_client_hello(SSL *s)
         }
         /* Look for resumed method in compression list */
         for (k = 0; k < complen; k++) {
-            if (q[k] == comp_id)
+            if (PACKET_data(&compression)[k] == comp_id)
                 break;
         }
         if (k >= complen) {
@@ -1430,7 +1345,7 @@ int ssl3_get_client_hello(SSL *s)
             comp = sk_SSL_COMP_value(s->ctx->comp_methods, m);
             v = comp->id;
             for (o = 0; o < complen; o++) {
-                if (v == q[o]) {
+                if (v == PACKET_data(&compression)[o]) {
                     done = 1;
                     break;
                 }
@@ -1707,8 +1622,8 @@ int ssl3_send_server_key_exchange(SSL *s)
              * reserve size for record length and PSK identity hint
              */
             n += 2;
-            if (s->ctx->psk_identity_hint)
-                n += strlen(s->ctx->psk_identity_hint);
+            if (s->cert->psk_identity_hint)
+                n += strlen(s->cert->psk_identity_hint);
         }
         /* Plain PSK or RSAPSK nothing to do */
         if (type & (SSL_kPSK | SSL_kRSAPSK)) {
@@ -1990,11 +1905,11 @@ int ssl3_send_server_key_exchange(SSL *s)
 #ifndef OPENSSL_NO_PSK
         if (type & SSL_PSK) {
             /* copy PSK identity hint */
-            if (s->ctx->psk_identity_hint) {
-                s2n(strlen(s->ctx->psk_identity_hint), p);
-                strncpy((char *)p, s->ctx->psk_identity_hint,
-                        strlen(s->ctx->psk_identity_hint));
-                p += strlen(s->ctx->psk_identity_hint);
+            if (s->cert->psk_identity_hint) {
+                s2n(strlen(s->cert->psk_identity_hint), p);
+                strncpy((char *)p, s->cert->psk_identity_hint,
+                        strlen(s->cert->psk_identity_hint));
+                p += strlen(s->cert->psk_identity_hint);
             } else {
                 s2n(0, p);
             }
@@ -2207,10 +2122,10 @@ int ssl3_send_certificate_request(SSL *s)
 
 int ssl3_get_client_key_exchange(SSL *s)
 {
-    int i, al, ok;
+    unsigned int i;
+    int al, ok;
     long n;
     unsigned long alg_k;
-    unsigned char *p;
 #ifndef OPENSSL_NO_RSA
     RSA *rsa = NULL;
     EVP_PKEY *pkey = NULL;
@@ -2225,6 +2140,8 @@ int ssl3_get_client_key_exchange(SSL *s)
     EC_POINT *clnt_ecpoint = NULL;
     BN_CTX *bn_ctx = NULL;
 #endif
+    PACKET pkt, enc_premaster;
+    unsigned char *data, *rsa_decrypt = NULL;
 
     n = s->method->ssl_get_message(s,
                                    SSL3_ST_SR_KEY_EXCH_A,
@@ -2233,7 +2150,11 @@ int ssl3_get_client_key_exchange(SSL *s)
 
     if (!ok)
         return ((int)n);
-    p = (unsigned char *)s->init_msg;
+    if (!PACKET_buf_init(&pkt, s->init_msg, n)) {
+        al = SSL_AD_INTERNAL_ERROR;
+        SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, ERR_R_INTERNAL_ERROR);
+        goto f_err;
+    }
 
     alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
 
@@ -2242,18 +2163,14 @@ int ssl3_get_client_key_exchange(SSL *s)
     if (alg_k & SSL_PSK) {
         unsigned char psk[PSK_MAX_PSK_LEN];
         size_t psklen;
-        if (n < 2) {
-            al = SSL_AD_DECODE_ERROR;
-            SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, SSL_R_LENGTH_MISMATCH);
-            goto f_err;
-        }
-        n2s(p, i);
-        if (i + 2 > n) {
+       PACKET psk_identity;
+
+        if (!PACKET_get_length_prefixed_2(&pkt, &psk_identity)) {
             al = SSL_AD_DECODE_ERROR;
             SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, SSL_R_LENGTH_MISMATCH);
             goto f_err;
         }
-        if (i > PSK_MAX_IDENTITY_LEN) {
+        if (PACKET_remaining(&psk_identity) > PSK_MAX_IDENTITY_LEN) {
             al = SSL_AD_DECODE_ERROR;
             SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE,
                    SSL_R_DATA_LENGTH_TOO_LONG);
@@ -2266,13 +2183,9 @@ int ssl3_get_client_key_exchange(SSL *s)
             goto f_err;
         }
 
-        OPENSSL_free(s->session->psk_identity);
-        s->session->psk_identity = BUF_strndup((char *)p, i);
-
-        if (s->session->psk_identity == NULL) {
+        if (!PACKET_strndup(&psk_identity, &s->session->psk_identity)) {
+            SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, ERR_R_INTERNAL_ERROR);
             al = SSL_AD_INTERNAL_ERROR;
-            SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE,
-                   ERR_R_MALLOC_FAILURE);
             goto f_err;
         }
 
@@ -2304,13 +2217,10 @@ int ssl3_get_client_key_exchange(SSL *s)
         }
 
         s->s3->tmp.psklen = psklen;
-
-        n -= i + 2;
-        p += i;
     }
     if (alg_k & SSL_kPSK) {
         /* Identity extracted earlier: should be nothing left */
-        if (n != 0) {
+        if (PACKET_remaining(&pkt) != 0) {
             al = SSL_AD_HANDSHAKE_FAILURE;
             SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, SSL_R_LENGTH_MISMATCH);
             goto f_err;
@@ -2356,32 +2266,41 @@ int ssl3_get_client_key_exchange(SSL *s)
             rsa = pkey->pkey.rsa;
         }
 
-        /* TLS and [incidentally] DTLS{0xFEFF} */
-        if (s->version > SSL3_VERSION && s->version != DTLS1_BAD_VER) {
-            n2s(p, i);
-            if (n != i + 2) {
-                if (!(s->options & SSL_OP_TLS_D5_BUG)) {
+        /* SSLv3 and pre-standard DTLS omit the length bytes. */
+        if (s->version == SSL3_VERSION || s->version == DTLS1_BAD_VER) {
+            enc_premaster = pkt;
+        } else {
+            PACKET orig = pkt;
+            if (!PACKET_get_length_prefixed_2(&pkt, &enc_premaster)
+                || PACKET_remaining(&pkt) != 0) {
+                /* Try SSLv3 behaviour for TLS. */
+                if (s->options & SSL_OP_TLS_D5_BUG) {
+                    enc_premaster = orig;
+                } else {
                     al = SSL_AD_DECODE_ERROR;
-                    SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE,
-                           SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG);
+                    SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, SSL_R_LENGTH_MISMATCH);
                     goto f_err;
-                } else
-                    p -= 2;
-            } else
-                n = i;
+                }
+            }
         }
 
         /*
-         * Reject overly short RSA ciphertext because we want to be sure
-         * that the buffer size makes it safe to iterate over the entire
-         * size of a premaster secret (SSL_MAX_MASTER_KEY_LENGTH). The
-         * actual expected size is larger due to RSA padding, but the
-         * bound is sufficient to be safe.
+         * We want to be sure that the plaintext buffer size makes it safe to
+         * iterate over the entire size of a premaster secret
+         * (SSL_MAX_MASTER_KEY_LENGTH). Reject overly short RSA keys because
+         * their ciphertext cannot accommodate a premaster secret anyway.
          */
-        if (n < SSL_MAX_MASTER_KEY_LENGTH) {
-            al = SSL_AD_DECRYPT_ERROR;
+        if (RSA_size(rsa) < SSL_MAX_MASTER_KEY_LENGTH) {
+            al = SSL_AD_INTERNAL_ERROR;
             SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE,
-                   SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG);
+                   RSA_R_KEY_SIZE_TOO_SMALL);
+            goto f_err;
+        }
+
+        rsa_decrypt = OPENSSL_malloc(RSA_size(rsa));
+        if (rsa_decrypt == NULL) {
+            al = SSL_AD_INTERNAL_ERROR;
+            SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, ERR_R_MALLOC_FAILURE);
             goto f_err;
         }
 
@@ -2394,10 +2313,13 @@ int ssl3_get_client_key_exchange(SSL *s)
          */
 
         if (RAND_bytes(rand_premaster_secret,
-                              sizeof(rand_premaster_secret)) <= 0)
+                       sizeof(rand_premaster_secret)) <= 0) {
             goto err;
-        decrypt_len =
-            RSA_private_decrypt((int)n, p, p, rsa, RSA_PKCS1_PADDING);
+        }
+
+        decrypt_len = RSA_private_decrypt(PACKET_remaining(&enc_premaster),
+                                          PACKET_data(&enc_premaster),
+                                          rsa_decrypt, rsa, RSA_PKCS1_PADDING);
         ERR_clear_error();
 
         /*
@@ -2416,9 +2338,11 @@ int ssl3_get_client_key_exchange(SSL *s)
          * constant time and are treated like any other decryption error.
          */
         version_good =
-            constant_time_eq_8(p[0], (unsigned)(s->client_version >> 8));
+            constant_time_eq_8(rsa_decrypt[0],
+                               (unsigned)(s->client_version >> 8));
         version_good &=
-            constant_time_eq_8(p[1], (unsigned)(s->client_version & 0xff));
+            constant_time_eq_8(rsa_decrypt[1],
+                               (unsigned)(s->client_version & 0xff));
 
         /*
          * The premaster secret must contain the same version number as the
@@ -2432,9 +2356,10 @@ int ssl3_get_client_key_exchange(SSL *s)
         if (s->options & SSL_OP_TLS_ROLLBACK_BUG) {
             unsigned char workaround_good;
             workaround_good =
-                constant_time_eq_8(p[0], (unsigned)(s->version >> 8));
+                constant_time_eq_8(rsa_decrypt[0], (unsigned)(s->version >> 8));
             workaround_good &=
-                constant_time_eq_8(p[1], (unsigned)(s->version & 0xff));
+                constant_time_eq_8(rsa_decrypt[1],
+                                   (unsigned)(s->version & 0xff));
             version_good |= workaround_good;
         }
 
@@ -2451,24 +2376,29 @@ int ssl3_get_client_key_exchange(SSL *s)
          * it is still sufficiently large to read from.
          */
         for (j = 0; j < sizeof(rand_premaster_secret); j++) {
-            p[j] = constant_time_select_8(decrypt_good, p[j],
-                                          rand_premaster_secret[j]);
+            rsa_decrypt[j] =
+                constant_time_select_8(decrypt_good, rsa_decrypt[j],
+                                       rand_premaster_secret[j]);
         }
 
-        if (!ssl_generate_master_secret(s, p, sizeof(rand_premaster_secret), 0)) {
+        if (!ssl_generate_master_secret(s, rsa_decrypt,
+                                        sizeof(rand_premaster_secret), 0)) {
             al = SSL_AD_INTERNAL_ERROR;
             SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, ERR_R_INTERNAL_ERROR);
             goto f_err;
         }
+        OPENSSL_free(rsa_decrypt);
+        rsa_decrypt = NULL;
     } else
 #endif
 #ifndef OPENSSL_NO_DH
     if (alg_k & (SSL_kDHE | SSL_kDHr | SSL_kDHd | SSL_kDHEPSK)) {
         int idx = -1;
         EVP_PKEY *skey = NULL;
-        if (n > 1) {
-            n2s(p, i);
-        } else {
+        PACKET bookmark = pkt;
+        unsigned char shared[(OPENSSL_DH_MAX_MODULUS_BITS + 7) / 8];
+
+        if (!PACKET_get_net_2(&pkt, &i)) {
             if (alg_k & (SSL_kDHE | SSL_kDHEPSK)) {
                 al = SSL_AD_HANDSHAKE_FAILURE;
                 SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE,
@@ -2477,14 +2407,14 @@ int ssl3_get_client_key_exchange(SSL *s)
             }
             i = 0;
         }
-        if (n && n != i + 2) {
+        if (PACKET_remaining(&pkt) != i) {
             if (!(s->options & SSL_OP_SSLEAY_080_CLIENT_DH_BUG)) {
                 SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE,
                        SSL_R_DH_PUBLIC_VALUE_LENGTH_IS_WRONG);
                 goto err;
             } else {
-                p -= 2;
-                i = (int)n;
+                pkt = bookmark;
+                i = PACKET_remaining(&pkt);
             }
         }
         if (alg_k & SSL_kDHr)
@@ -2524,14 +2454,22 @@ int ssl3_get_client_key_exchange(SSL *s)
             }
             EVP_PKEY_free(clkey);
             pub = dh_clnt->pub_key;
-        } else
-            pub = BN_bin2bn(p, i, NULL);
+        } else {
+            if (!PACKET_get_bytes(&pkt, &data, i)) {
+                /* We already checked we have enough data */
+                al = SSL_AD_INTERNAL_ERROR;
+                SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE,
+                       ERR_R_INTERNAL_ERROR);
+                goto f_err;
+            }
+            pub = BN_bin2bn(data, i, NULL);
+        }
         if (pub == NULL) {
             SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, SSL_R_BN_LIB);
             goto err;
         }
 
-        i = DH_compute_key(p, pub, dh_srvr);
+        i = DH_compute_key(shared, pub, dh_srvr);
 
         if (i <= 0) {
             SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, ERR_R_DH_LIB);
@@ -2546,7 +2484,7 @@ int ssl3_get_client_key_exchange(SSL *s)
         else
             BN_clear_free(pub);
         pub = NULL;
-        if (!ssl_generate_master_secret(s, p, i, 0)) {
+        if (!ssl_generate_master_secret(s, shared, i, 0)) {
             al = SSL_AD_INTERNAL_ERROR;
             SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, ERR_R_INTERNAL_ERROR);
             goto f_err;
@@ -2563,6 +2501,7 @@ int ssl3_get_client_key_exchange(SSL *s)
         const EC_KEY *tkey;
         const EC_GROUP *group;
         const BIGNUM *priv_key;
+        unsigned char *shared;
 
         /* initialize structures for server's ECDH key pair */
         if ((srvr_ecdh = EC_KEY_new()) == NULL) {
@@ -2641,21 +2580,21 @@ int ssl3_get_client_key_exchange(SSL *s)
             }
 
             /* Get encoded point length */
-            i = *p;
-            p += 1;
-            if (n != 1 + i) {
+            if (!PACKET_get_1(&pkt, &i)) {
+                al = SSL_AD_DECODE_ERROR;
+                SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE,
+                       SSL_R_LENGTH_MISMATCH);
+                goto f_err;
+            }
+            if (!PACKET_get_bytes(&pkt, &data, i)
+                    || PACKET_remaining(&pkt) != 0) {
                 SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, ERR_R_EC_LIB);
                 goto err;
             }
-            if (EC_POINT_oct2point(group, clnt_ecpoint, p, i, bn_ctx) == 0) {
+            if (EC_POINT_oct2point(group, clnt_ecpoint, data, i, bn_ctx) == 0) {
                 SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, ERR_R_EC_LIB);
                 goto err;
             }
-            /*
-             * p is pointing to somewhere in the buffer currently, so set it
-             * to the start
-             */
-            p = (unsigned char *)s->init_buf->data;
         }
 
         /* Compute the shared pre-master secret */
@@ -2664,10 +2603,16 @@ int ssl3_get_client_key_exchange(SSL *s)
             SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, ERR_R_ECDH_LIB);
             goto err;
         }
-        i = ECDH_compute_key(p, (field_size + 7) / 8, clnt_ecpoint, srvr_ecdh,
-                             NULL);
+        shared = OPENSSL_malloc((field_size + 7) / 8);
+        if (shared == NULL) {
+            SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        i = ECDH_compute_key(shared, (field_size + 7) / 8, clnt_ecpoint,
+                             srvr_ecdh, NULL);
         if (i <= 0) {
             SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, ERR_R_ECDH_LIB);
+            OPENSSL_free(shared);
             goto err;
         }
 
@@ -2678,7 +2623,7 @@ int ssl3_get_client_key_exchange(SSL *s)
         EC_KEY_free(s->s3->tmp.ecdh);
         s->s3->tmp.ecdh = NULL;
 
-        if (!ssl_generate_master_secret(s, p, i, 0)) {
+        if (!ssl_generate_master_secret(s, shared, i, 1)) {
             al = SSL_AD_INTERNAL_ERROR;
             SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, ERR_R_INTERNAL_ERROR);
             goto f_err;
@@ -2688,17 +2633,13 @@ int ssl3_get_client_key_exchange(SSL *s)
 #endif
 #ifndef OPENSSL_NO_SRP
     if (alg_k & SSL_kSRP) {
-        int param_len;
-
-        n2s(p, i);
-        param_len = i + 2;
-        if (param_len > n) {
+        if (!PACKET_get_net_2(&pkt, &i)
+                || !PACKET_get_bytes(&pkt, &data, i)) {
             al = SSL_AD_DECODE_ERROR;
-            SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE,
-                   SSL_R_BAD_SRP_A_LENGTH);
+            SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, SSL_R_BAD_SRP_A_LENGTH);
             goto f_err;
         }
-        if ((s->srp_ctx.A = BN_bin2bn(p, i, NULL)) == NULL) {
+        if ((s->srp_ctx.A = BN_bin2bn(data, i, NULL)) == NULL) {
             SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, ERR_R_BN_LIB);
             goto err;
         }
@@ -2720,8 +2661,6 @@ int ssl3_get_client_key_exchange(SSL *s)
             SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, ERR_R_INTERNAL_ERROR);
             goto err;
         }
-
-        p += i;
     } else
 #endif                          /* OPENSSL_NO_SRP */
     if (alg_k & SSL_kGOST) {
@@ -2753,15 +2692,20 @@ int ssl3_get_client_key_exchange(SSL *s)
                 ERR_clear_error();
         }
         /* Decrypt session key */
+        if (!PACKET_get_bytes(&pkt, &data, n)) {
+            al = SSL_AD_INTERNAL_ERROR;
+            SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE, ERR_R_INTERNAL_ERROR);
+            goto f_err;
+        }
         if (ASN1_get_object
-            ((const unsigned char **)&p, &Tlen, &Ttag, &Tclass,
+            ((const unsigned char **)&data, &Tlen, &Ttag, &Tclass,
              n) != V_ASN1_CONSTRUCTED || Ttag != V_ASN1_SEQUENCE
             || Tclass != V_ASN1_UNIVERSAL) {
             SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE,
                    SSL_R_DECRYPTION_FAILED);
             goto gerr;
         }
-        start = p;
+        start = data;
         inlen = Tlen;
         if (EVP_PKEY_decrypt
             (pkey_ctx, premaster_secret, &outlen, start, inlen) <= 0) {
@@ -2805,6 +2749,7 @@ int ssl3_get_client_key_exchange(SSL *s)
     EC_POINT_free(clnt_ecpoint);
     EC_KEY_free(srvr_ecdh);
     BN_CTX_free(bn_ctx);
+    OPENSSL_free(rsa_decrypt);
 #endif
 #ifndef OPENSSL_NO_PSK
     OPENSSL_clear_free(s->s3->tmp.psk, s->s3->tmp.psklen);
@@ -3399,9 +3344,9 @@ int ssl3_send_cert_status(SSL *s)
 int ssl3_get_next_proto(SSL *s)
 {
     int ok;
-    unsigned int proto_len, padding_len;
     long n;
-    PACKET pkt;
+    PACKET pkt, next_proto, padding;
+    size_t next_proto_len;
 
     /*
      * Clients cannot send a NextProtocol message if we didn't see the
@@ -3450,25 +3395,20 @@ int ssl3_get_next_proto(SSL *s)
      *   uint8 padding_len;
      *   uint8 padding[padding_len];
      */
-    if (!PACKET_get_1(&pkt, &proto_len)){
+    if (!PACKET_get_length_prefixed_1(&pkt, &next_proto)
+        || !PACKET_get_length_prefixed_1(&pkt, &padding)
+        || PACKET_remaining(&pkt) > 0) {
         SSLerr(SSL_F_SSL3_GET_NEXT_PROTO, SSL_R_LENGTH_MISMATCH);
         goto err;
     }
 
-    s->next_proto_negotiated = OPENSSL_malloc(proto_len);
-    if (s->next_proto_negotiated == NULL) {
-        SSLerr(SSL_F_SSL3_GET_NEXT_PROTO, ERR_R_MALLOC_FAILURE);
+    if (!PACKET_memdup(&next_proto, &s->next_proto_negotiated,
+                       &next_proto_len)) {
+        s->next_proto_negotiated_len = 0;
         goto err;
     }
 
-    if (!PACKET_copy_bytes(&pkt, s->next_proto_negotiated, proto_len)
-            || !PACKET_get_1(&pkt, &padding_len)
-            || PACKET_remaining(&pkt) != padding_len) {
-        OPENSSL_free(s->next_proto_negotiated);
-        s->next_proto_negotiated = NULL;
-        SSLerr(SSL_F_SSL3_GET_NEXT_PROTO, SSL_R_LENGTH_MISMATCH);
-        goto err;
-    }
+    s->next_proto_negotiated_len = (unsigned char)next_proto_len;
 
     return 1;
 err:
@@ -3479,32 +3419,40 @@ err:
 
 #define SSLV2_CIPHER_LEN    3
 
-STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, unsigned char *p,
-                                               int num,
+STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s,
+                                               PACKET *cipher_suites,
                                                STACK_OF(SSL_CIPHER) **skp,
-                                               int sslv2format)
+                                               int sslv2format, int *al
+                                               )
 {
     const SSL_CIPHER *c;
     STACK_OF(SSL_CIPHER) *sk;
-    int i, n;
+    int n;
+    /* 3 = SSLV2_CIPHER_LEN > TLS_CIPHER_LEN = 2. */
+    unsigned char cipher[SSLV2_CIPHER_LEN];
 
-    if (s->s3)
-        s->s3->send_connection_binding = 0;
+    s->s3->send_connection_binding = 0;
 
-    if(sslv2format) {
-        n = SSLV2_CIPHER_LEN;
-    } else {
-        n = ssl_put_cipher_by_char(s, NULL, NULL);
+    n = sslv2format ? SSLV2_CIPHER_LEN : TLS_CIPHER_LEN;
+
+    if (PACKET_remaining(cipher_suites) == 0) {
+        SSLerr(SSL_F_SSL_BYTES_TO_CIPHER_LIST, SSL_R_NO_CIPHERS_SPECIFIED);
+        *al = SSL_AD_ILLEGAL_PARAMETER;
+        return NULL;
     }
-    if (n == 0 || (num % n) != 0) {
+
+    if (PACKET_remaining(cipher_suites) % n != 0) {
         SSLerr(SSL_F_SSL_BYTES_TO_CIPHER_LIST,
                SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST);
-        return (NULL);
+        *al = SSL_AD_DECODE_ERROR;
+        return NULL;
     }
+
     if ((skp == NULL) || (*skp == NULL)) {
         sk = sk_SSL_CIPHER_new_null(); /* change perhaps later */
         if(sk == NULL) {
             SSLerr(SSL_F_SSL_BYTES_TO_CIPHER_LIST, ERR_R_MALLOC_FAILURE);
+            *al = SSL_AD_INTERNAL_ERROR;
             return NULL;
         }
     } else {
@@ -3512,28 +3460,32 @@ STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, unsigned char *p,
         sk_SSL_CIPHER_zero(sk);
     }
 
-    OPENSSL_free(s->s3->tmp.ciphers_raw);
-    s->s3->tmp.ciphers_raw = BUF_memdup(p, num);
-    if (s->s3->tmp.ciphers_raw == NULL) {
-        SSLerr(SSL_F_SSL_BYTES_TO_CIPHER_LIST, ERR_R_MALLOC_FAILURE);
+    if (!PACKET_memdup(cipher_suites, &s->s3->tmp.ciphers_raw,
+                       &s->s3->tmp.ciphers_rawlen)) {
+        *al = SSL_AD_INTERNAL_ERROR;
         goto err;
     }
-    s->s3->tmp.ciphers_rawlen = (size_t)num;
 
-    for (i = 0; i < num; i += n) {
+    while (PACKET_copy_bytes(cipher_suites, cipher, n)) {
+        /*
+         * SSLv3 ciphers wrapped in an SSLv2-compatible ClientHello have the
+         * first byte set to zero, while true SSLv2 ciphers have a non-zero
+         * first byte. We don't support any true SSLv2 ciphers, so skip them.
+         */
+        if (sslv2format && cipher[0] != '\0')
+                continue;
+
         /* Check for TLS_EMPTY_RENEGOTIATION_INFO_SCSV */
-        if (s->s3 && (n != 3 || !p[0]) &&
-            (p[n - 2] == ((SSL3_CK_SCSV >> 8) & 0xff)) &&
-            (p[n - 1] == (SSL3_CK_SCSV & 0xff))) {
+        if ((cipher[n - 2] == ((SSL3_CK_SCSV >> 8) & 0xff)) &&
+            (cipher[n - 1] == (SSL3_CK_SCSV & 0xff))) {
             /* SCSV fatal if renegotiating */
             if (s->renegotiate) {
                 SSLerr(SSL_F_SSL_BYTES_TO_CIPHER_LIST,
                        SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING);
-                ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+                *al = SSL_AD_HANDSHAKE_FAILURE;
                 goto err;
             }
             s->s3->send_connection_binding = 1;
-            p += n;
 #ifdef OPENSSL_RI_DEBUG
             fprintf(stderr, "SCSV received by server\n");
 #endif
@@ -3541,9 +3493,8 @@ STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, unsigned char *p,
         }
 
         /* Check for TLS_FALLBACK_SCSV */
-        if ((n != 3 || !p[0]) &&
-            (p[n - 2] == ((SSL3_CK_FALLBACK_SCSV >> 8) & 0xff)) &&
-            (p[n - 1] == (SSL3_CK_FALLBACK_SCSV & 0xff))) {
+        if ((cipher[n - 2] == ((SSL3_CK_FALLBACK_SCSV >> 8) & 0xff)) &&
+            (cipher[n - 1] == (SSL3_CK_FALLBACK_SCSV & 0xff))) {
             /*
              * The SCSV indicates that the client previously tried a higher
              * version. Fail if the current version is an unexpected
@@ -3552,37 +3503,27 @@ STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, unsigned char *p,
             if (!SSL_ctrl(s, SSL_CTRL_CHECK_PROTO_VERSION, 0, NULL)) {
                 SSLerr(SSL_F_SSL_BYTES_TO_CIPHER_LIST,
                        SSL_R_INAPPROPRIATE_FALLBACK);
-                if (s->s3)
-                    ssl3_send_alert(s, SSL3_AL_FATAL,
-                                    SSL_AD_INAPPROPRIATE_FALLBACK);
+                *al = SSL_AD_INAPPROPRIATE_FALLBACK;
                 goto err;
             }
-            p += n;
             continue;
         }
 
-        if(sslv2format) {
-            /*
-             * We only support SSLv2 format ciphers in SSLv3+ using a
-             * SSLv2 backward compatible ClientHello. In this case the first
-             * byte is always 0 for SSLv3 compatible ciphers. Anything else
-             * is an SSLv2 cipher and we ignore it
-             */
-            if(p[0] == 0)
-                c = ssl_get_cipher_by_char(s, &p[1]);
-            else
-                c = NULL;
-        } else {
-            c = ssl_get_cipher_by_char(s, p);
-        }
-        p += n;
+        /* For SSLv2-compat, ignore leading 0-byte. */
+        c = ssl_get_cipher_by_char(s, sslv2format ? &cipher[1] : cipher);
         if (c != NULL) {
             if (!sk_SSL_CIPHER_push(sk, c)) {
                 SSLerr(SSL_F_SSL_BYTES_TO_CIPHER_LIST, ERR_R_MALLOC_FAILURE);
+                *al = SSL_AD_INTERNAL_ERROR;
                 goto err;
             }
         }
     }
+    if (PACKET_remaining(cipher_suites) > 0) {
+        *al = SSL_AD_INTERNAL_ERROR;
+        SSLerr(SSL_F_SSL_BYTES_TO_CIPHER_LIST, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
 
     if (skp != NULL)
         *skp = sk;
@@ -3590,5 +3531,5 @@ STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, unsigned char *p,
  err:
     if ((skp == NULL) || (*skp == NULL))
         sk_SSL_CIPHER_free(sk);
-    return (NULL);
+    return NULL;
 }