Check for ClientHello message overruns
[openssl.git] / ssl / s3_srvr.c
index 486362d22102dde6082485133f35b8cffc1f8e83..3cdc73c07a240f32e807d58fc178a5f53fadf0a8 100644 (file)
@@ -991,6 +991,16 @@ int ssl3_get_client_hello(SSL *s)
     s->first_packet = 0;
     d = p = (unsigned char *)s->init_msg;
 
+    /*
+     * 2 bytes for client version, SSL3_RANDOM_SIZE bytes for random, 1 byte
+     * for session id length
+     */
+    if (n < 2 + SSL3_RANDOM_SIZE + 1) {
+        al = SSL_AD_DECODE_ERROR;
+        SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_TOO_SHORT);
+        goto f_err;
+    }
+
     /*
      * use version from inside client hello, not from record header (may
      * differ: see RFC 2246, Appendix E, second paragraph)
@@ -1022,6 +1032,12 @@ int ssl3_get_client_hello(SSL *s)
         unsigned int session_length, cookie_length;
 
         session_length = *(p + SSL3_RANDOM_SIZE);
+
+        if (p + SSL3_RANDOM_SIZE + session_length + 1 >= d + n) {
+            al = SSL_AD_DECODE_ERROR;
+            SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_TOO_SHORT);
+            goto f_err;
+        }
         cookie_length = *(p + SSL3_RANDOM_SIZE + session_length + 1);
 
         if (cookie_length == 0)
@@ -1035,6 +1051,12 @@ int ssl3_get_client_hello(SSL *s)
     /* get the session-id */
     j = *(p++);
 
+    if (p + j > d + n) {
+        al = SSL_AD_DECODE_ERROR;
+        SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_TOO_SHORT);
+        goto f_err;
+    }
+
     s->hit = 0;
     /*
      * Versions before 0.9.7 always allow clients to resume sessions in
@@ -1079,8 +1101,19 @@ int ssl3_get_client_hello(SSL *s)
 
     if (s->version == DTLS1_VERSION || s->version == DTLS1_BAD_VER) {
         /* cookie stuff */
+        if (p + 1 > d + n) {
+            al = SSL_AD_DECODE_ERROR;
+            SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_TOO_SHORT);
+            goto f_err;
+        }
         cookie_len = *(p++);
 
+        if (p + cookie_len > d + n) {
+            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
@@ -1121,6 +1154,11 @@ int ssl3_get_client_hello(SSL *s)
         p += cookie_len;
     }
 
+    if (p + 2 > d + n) {
+        al = SSL_AD_DECODE_ERROR;
+        SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_TOO_SHORT);
+        goto f_err;
+    }
     n2s(p, i);
     if ((i == 0) && (j != 0)) {
         /* we need a cipher if we are not resuming a session */
@@ -1128,7 +1166,9 @@ int ssl3_get_client_hello(SSL *s)
         SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_NO_CIPHERS_SPECIFIED);
         goto f_err;
     }
-    if ((p + i) >= (d + n)) {
+
+    /* i bytes of cipher data + 1 byte for compression length later */
+    if ((p + i + 1) > (d + n)) {
         /* not enough data */
         al = SSL_AD_DECODE_ERROR;
         SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_LENGTH_MISMATCH);
@@ -1425,17 +1465,17 @@ int ssl3_get_client_hello(SSL *s)
         }
     }
 
-        /*-
-         * we now have the following setup.
-         * client_random
-         * cipher_list          - our prefered list of ciphers
-         * ciphers              - the clients prefered list of ciphers
-         * compression          - basically ignored right now
-         * ssl version is set   - sslv3
-         * s->session           - The ssl session has been setup.
-         * s->hit               - session reuse flag
-         * s->tmp.new_cipher    - the new cipher to use.
-         */
+    /*-
+     * we now have the following setup.
+     * client_random
+     * cipher_list          - our prefered list of ciphers
+     * ciphers              - the clients prefered list of ciphers
+     * compression          - basically ignored right now
+     * ssl version is set   - sslv3
+     * s->session           - The ssl session has been setup.
+     * s->hit               - session reuse flag
+     * s->tmp.new_cipher    - the new cipher to use.
+     */
 
     /* Handles TLS extensions that we couldn't check earlier */
     if (s->version >= SSL3_VERSION) {
@@ -1481,22 +1521,22 @@ int ssl3_send_server_hello(SSL *s)
         memcpy(p, s->s3->server_random, SSL3_RANDOM_SIZE);
         p += SSL3_RANDOM_SIZE;
 
-                /*-
-                 * There are several cases for the session ID to send
-                 * back in the server hello:
-                 * - For session reuse from the session cache,
-                 *   we send back the old session ID.
-                 * - If stateless session reuse (using a session ticket)
-                 *   is successful, we send back the client's "session ID"
-                 *   (which doesn't actually identify the session).
-                 * - If it is a new session, we send back the new
-                 *   session ID.
-                 * - However, if we want the new session to be single-use,
-                 *   we send back a 0-length session ID.
-                 * s->hit is non-zero in either case of session reuse,
-                 * so the following won't overwrite an ID that we're supposed
-                 * to send back.
-                 */
+        /*-
+         * There are several cases for the session ID to send
+         * back in the server hello:
+         * - For session reuse from the session cache,
+         *   we send back the old session ID.
+         * - If stateless session reuse (using a session ticket)
+         *   is successful, we send back the client's "session ID"
+         *   (which doesn't actually identify the session).
+         * - If it is a new session, we send back the new
+         *   session ID.
+         * - However, if we want the new session to be single-use,
+         *   we send back a 0-length session ID.
+         * s->hit is non-zero in either case of session reuse,
+         * so the following won't overwrite an ID that we're supposed
+         * to send back.
+         */
         if (!(s->ctx->session_cache_mode & SSL_SESS_CACHE_SERVER)
             && !s->hit)
             s->session->session_id_length = 0;
@@ -2524,11 +2564,11 @@ int ssl3_get_client_key_exchange(SSL *s)
             }
         }
 
-                /*- Was doing kssl_ctx_free() here,
-                 *  but it caused problems for apache.
-                 *  kssl_ctx = kssl_ctx_free(kssl_ctx);
-                 *  if (s->kssl_ctx)  s->kssl_ctx = NULL;
-                 */
+        /*- Was doing kssl_ctx_free() here,
+         *  but it caused problems for apache.
+         *  kssl_ctx = kssl_ctx_free(kssl_ctx);
+         *  if (s->kssl_ctx)  s->kssl_ctx = NULL;
+         */
     } else
 #endif                          /* OPENSSL_NO_KRB5 */
 
@@ -3295,14 +3335,16 @@ int ssl3_send_server_certificate(SSL *s)
 /* send a new session ticket (not necessarily for a new session) */
 int ssl3_send_newsession_ticket(SSL *s)
 {
+    unsigned char *senc = NULL;
+    EVP_CIPHER_CTX ctx;
+    HMAC_CTX hctx;
+
     if (s->state == SSL3_ST_SW_SESSION_TICKET_A) {
-        unsigned char *p, *senc, *macstart;
+        unsigned char *p, *macstart;
         const unsigned char *const_p;
         int len, slen_full, slen;
         SSL_SESSION *sess;
         unsigned int hlen;
-        EVP_CIPHER_CTX ctx;
-        HMAC_CTX hctx;
         SSL_CTX *tctx = s->initial_ctx;
         unsigned char iv[EVP_MAX_IV_LENGTH];
         unsigned char key_name[16];
@@ -3313,70 +3355,75 @@ int ssl3_send_newsession_ticket(SSL *s)
          * Some length values are 16 bits, so forget it if session is too
          * long
          */
-        if (slen_full > 0xFF00)
+        if (slen_full == 0 || slen_full > 0xFF00)
             return -1;
         senc = OPENSSL_malloc(slen_full);
         if (!senc)
             return -1;
+
+        EVP_CIPHER_CTX_init(&ctx);
+        HMAC_CTX_init(&hctx);
+
         p = senc;
-        i2d_SSL_SESSION(s->session, &p);
+        if (!i2d_SSL_SESSION(s->session, &p))
+            goto err;
 
         /*
          * create a fresh copy (not shared with other threads) to clean up
          */
         const_p = senc;
         sess = d2i_SSL_SESSION(NULL, &const_p, slen_full);
-        if (sess == NULL) {
-            OPENSSL_free(senc);
-            return -1;
-        }
+        if (sess == NULL)
+            goto err;
         sess->session_id_length = 0; /* ID is irrelevant for the ticket */
 
         slen = i2d_SSL_SESSION(sess, NULL);
-        if (slen > slen_full) { /* shouldn't ever happen */
-            OPENSSL_free(senc);
-            return -1;
+        if (slen == 0 || slen > slen_full) { /* shouldn't ever happen */
+            SSL_SESSION_free(sess);
+            goto err;
         }
         p = senc;
-        i2d_SSL_SESSION(sess, &p);
+        if (!i2d_SSL_SESSION(sess, &p)) {
+            SSL_SESSION_free(sess);
+            goto err;
+        }
         SSL_SESSION_free(sess);
 
-                /*-
-                 * Grow buffer if need be: the length calculation is as
-                 * follows 1 (size of message name) + 3 (message length
-                 * bytes) + 4 (ticket lifetime hint) + 2 (ticket length) +
-                 * 16 (key name) + max_iv_len (iv length) +
-                 * session_length + max_enc_block_size (max encrypted session
-                 * length) + max_md_size (HMAC).
-                 */
+        /*-
+         * Grow buffer if need be: the length calculation is as
+         * follows 1 (size of message name) + 3 (message length
+         * bytes) + 4 (ticket lifetime hint) + 2 (ticket length) +
+         * 16 (key name) + max_iv_len (iv length) +
+         * session_length + max_enc_block_size (max encrypted session
+         * length) + max_md_size (HMAC).
+         */
         if (!BUF_MEM_grow(s->init_buf,
                           26 + EVP_MAX_IV_LENGTH + EVP_MAX_BLOCK_LENGTH +
                           EVP_MAX_MD_SIZE + slen))
-            return -1;
+            goto err;
 
         p = (unsigned char *)s->init_buf->data;
         /* do the header */
         *(p++) = SSL3_MT_NEWSESSION_TICKET;
         /* Skip message length for now */
         p += 3;
-        EVP_CIPHER_CTX_init(&ctx);
-        HMAC_CTX_init(&hctx);
         /*
          * Initialize HMAC and cipher contexts. If callback present it does
          * all the work otherwise use generated values from parent ctx.
          */
         if (tctx->tlsext_ticket_key_cb) {
             if (tctx->tlsext_ticket_key_cb(s, key_name, iv, &ctx,
-                                           &hctx, 1) < 0) {
-                OPENSSL_free(senc);
-                return -1;
-            }
+                                           &hctx, 1) < 0)
+                goto err;
         } else {
-            RAND_pseudo_bytes(iv, 16);
-            EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL,
-                               tctx->tlsext_tick_aes_key, iv);
-            HMAC_Init_ex(&hctx, tctx->tlsext_tick_hmac_key, 16,
-                         tlsext_tick_md(), NULL);
+            if (RAND_bytes(iv, 16) <= 0)
+                goto err;
+            if (!EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL,
+                                    tctx->tlsext_tick_aes_key, iv))
+                goto err;
+            if (!HMAC_Init_ex(&hctx, tctx->tlsext_tick_hmac_key, 16,
+                              tlsext_tick_md(), NULL))
+                goto err;
             memcpy(key_name, tctx->tlsext_tick_key_name, 16);
         }
 
@@ -3397,14 +3444,19 @@ int ssl3_send_newsession_ticket(SSL *s)
         memcpy(p, iv, EVP_CIPHER_CTX_iv_length(&ctx));
         p += EVP_CIPHER_CTX_iv_length(&ctx);
         /* Encrypt session data */
-        EVP_EncryptUpdate(&ctx, p, &len, senc, slen);
+        if (!EVP_EncryptUpdate(&ctx, p, &len, senc, slen))
+            goto err;
         p += len;
-        EVP_EncryptFinal(&ctx, p, &len);
+        if (!EVP_EncryptFinal(&ctx, p, &len))
+            goto err;
         p += len;
-        EVP_CIPHER_CTX_cleanup(&ctx);
 
-        HMAC_Update(&hctx, macstart, p - macstart);
-        HMAC_Final(&hctx, p, &hlen);
+        if (!HMAC_Update(&hctx, macstart, p - macstart))
+            goto err;
+        if (!HMAC_Final(&hctx, p, &hlen))
+            goto err;
+
+        EVP_CIPHER_CTX_cleanup(&ctx);
         HMAC_CTX_cleanup(&hctx);
 
         p += hlen;
@@ -3425,18 +3477,24 @@ int ssl3_send_newsession_ticket(SSL *s)
 
     /* SSL3_ST_SW_SESSION_TICKET_B */
     return (ssl3_do_write(s, SSL3_RT_HANDSHAKE));
+ err:
+    if (senc)
+        OPENSSL_free(senc);
+    EVP_CIPHER_CTX_cleanup(&ctx);
+    HMAC_CTX_cleanup(&hctx);
+    return -1;
 }
 
 int ssl3_send_cert_status(SSL *s)
 {
     if (s->state == SSL3_ST_SW_CERT_STATUS_A) {
         unsigned char *p;
-                /*-
-                 * Grow buffer if need be: the length calculation is as
-                 * follows 1 (message type) + 3 (message length) +
-                 * 1 (ocsp response type) + 3 (ocsp response length)
-                 * + (ocsp response)
-                 */
+        /*-
+         * Grow buffer if need be: the length calculation is as
+         * follows 1 (message type) + 3 (message length) +
+         * 1 (ocsp response type) + 3 (ocsp response length)
+         * + (ocsp response)
+         */
         if (!BUF_MEM_grow(s->init_buf, 8 + s->tlsext_ocsp_resplen))
             return -1;
 
@@ -3508,13 +3566,13 @@ int ssl3_get_next_proto(SSL *s)
 
     p = (unsigned char *)s->init_msg;
 
-        /*-
-         * The payload looks like:
-         *   uint8 proto_len;
-         *   uint8 proto[proto_len];
-         *   uint8 padding_len;
-         *   uint8 padding[padding_len];
-         */
+    /*-
+     * The payload looks like:
+     *   uint8 proto_len;
+     *   uint8 proto[proto_len];
+     *   uint8 padding_len;
+     *   uint8 padding[padding_len];
+     */
     proto_len = p[0];
     if (proto_len + 2 > s->init_num)
         return 0;