Fix signed/unsigned warning.
[oweals/openssl.git] / ssl / s3_pkt.c
index a2ba5748d5eb4f6b4aca75fb115eb97c75449f50..169f235c61eafe68ef72d88f7989183dfb4f8800 100644 (file)
@@ -246,11 +246,8 @@ static int ssl3_get_record(SSL *s)
        unsigned char *p;
        unsigned char md[EVP_MAX_MD_SIZE];
        short version;
-       unsigned int mac_size;
-       int clear=0;
+       unsigned mac_size, orig_len;
        size_t extra;
-       int decryption_failed_or_bad_record_mac = 0;
-       unsigned char *mac = NULL;
 
        rr= &(s->s3->rrec);
        sess=s->session;
@@ -291,9 +288,9 @@ again:
                        if (version != s->version)
                                {
                                SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_WRONG_VERSION_NUMBER);
-                               /* Send back error using their
-                                * version number :-) */
-                               s->version=version;
+                                if ((s->version & 0xFF00) == (version & 0xFF00))
+                                       /* Send back error using their minor version number :-) */
+                                       s->version = (unsigned short)version;
                                al=SSL_AD_PROTOCOL_VERSION;
                                goto f_err;
                                }
@@ -356,17 +353,15 @@ again:
        rr->data=rr->input;
 
        enc_err = s->method->ssl3_enc->enc(s,0);
-       if (enc_err <= 0)
+       /* enc_err is:
+        *    0: (in non-constant time) if the record is publically invalid.
+        *    1: if the padding is valid
+        *    -1: if the padding is invalid */
+       if (enc_err == 0)
                {
-               if (enc_err == 0)
-                       /* SSLerr() and ssl3_send_alert() have been called */
-                       goto err;
-
-               /* Otherwise enc_err == -1, which indicates bad padding
-                * (rec->length has not been changed in this case).
-                * To minimize information leaked via timing, we will perform
-                * the MAC computation anyway. */
-               decryption_failed_or_bad_record_mac = 1;
+               al=SSL_AD_DECRYPTION_FAILED;
+               SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_BLOCK_CIPHER_PAD_IS_WRONG);
+               goto f_err;
                }
 
 #ifdef TLS_DEBUG
@@ -376,51 +371,62 @@ printf("\n");
 #endif
 
        /* r->length is now the compressed data plus mac */
-       if (    (sess == NULL) ||
-               (s->enc_read_ctx == NULL) ||
-               (s->read_hash == NULL))
-               clear=1;
-
-       if (!clear)
+       if ((sess != NULL) &&
+           (s->enc_read_ctx != NULL) &&
+           (s->read_hash != NULL))
                {
+               /* s->read_hash != NULL => mac_size != -1 */
+               unsigned char *mac = NULL;
+               unsigned char mac_tmp[EVP_MAX_MD_SIZE];
                mac_size=EVP_MD_size(s->read_hash);
+               OPENSSL_assert(mac_size <= EVP_MAX_MD_SIZE);
 
-               if (rr->length > SSL3_RT_MAX_COMPRESSED_LENGTH+extra+mac_size)
+               /* kludge: *_cbc_remove_padding passes padding length in rr->type */
+               orig_len = rr->length+((unsigned int)rr->type>>8);
+
+               /* orig_len is the length of the record before any padding was
+                * removed. This is public information, as is the MAC in use,
+                * therefore we can safely process the record in a different
+                * amount of time if it's too short to possibly contain a MAC.
+                */
+               if (orig_len < mac_size ||
+                   /* CBC records must have a padding length byte too. */
+                   (EVP_CIPHER_CTX_mode(s->enc_read_ctx) == EVP_CIPH_CBC_MODE &&
+                    orig_len < mac_size+1))
                        {
-#if 0 /* OK only for stream ciphers (then rr->length is visible from ciphertext anyway) */
-                       al=SSL_AD_RECORD_OVERFLOW;
-                       SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_PRE_MAC_LENGTH_TOO_LONG);
+                       al=SSL_AD_DECODE_ERROR;
+                       SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_LENGTH_TOO_SHORT);
                        goto f_err;
-#else
-                       decryption_failed_or_bad_record_mac = 1;
-#endif                 
                        }
-               /* check the MAC for rr->input (it's in mac_size bytes at the tail) */
-               if (rr->length >= mac_size)
+
+               if (EVP_CIPHER_CTX_mode(s->enc_read_ctx) == EVP_CIPH_CBC_MODE)
                        {
+                       /* We update the length so that the TLS header bytes
+                        * can be constructed correctly but we need to extract
+                        * the MAC in constant time from within the record,
+                        * without leaking the contents of the padding bytes.
+                        * */
+                       mac = mac_tmp;
+                       ssl3_cbc_copy_mac(mac_tmp, rr, mac_size, orig_len);
                        rr->length -= mac_size;
-                       mac = &rr->data[rr->length];
                        }
                else
                        {
-                       /* record (minus padding) is too short to contain a MAC */
-#if 0 /* OK only for stream ciphers */
-                       al=SSL_AD_DECODE_ERROR;
-                       SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_LENGTH_TOO_SHORT);
-                       goto f_err;
-#else
-                       decryption_failed_or_bad_record_mac = 1;
-                       rr->length = 0;
-#endif
-                       }
-               i=s->method->ssl3_enc->mac(s,md,0);
-               if (mac == NULL || memcmp(md, mac, mac_size) != 0)
-                       {
-                       decryption_failed_or_bad_record_mac = 1;
+                       /* In this case there's no padding, so |orig_len|
+                        * equals |rec->length| and we checked that there's
+                        * enough bytes for |mac_size| above. */
+                       rr->length -= mac_size;
+                       mac = &rr->data[rr->length];
                        }
+
+               i=s->method->ssl3_enc->mac(s,md,0 /* not send */);
+               if (i < 0 || mac == NULL || CRYPTO_memcmp(md, mac, (size_t)mac_size) != 0)
+                       enc_err = -1;
+               if (rr->length > SSL3_RT_MAX_COMPRESSED_LENGTH+extra+mac_size)
+                       enc_err = -1;
                }
 
-       if (decryption_failed_or_bad_record_mac)
+       if (enc_err < 0)
                {
                /* A separate 'decryption_failed' alert was introduced with TLS 1.0,
                 * SSL 3.0 only has 'bad_record_mac'.  But unless a decryption
@@ -523,10 +529,11 @@ int ssl3_do_compress(SSL *ssl)
 int ssl3_write_bytes(SSL *s, int type, const void *buf_, int len)
        {
        const unsigned char *buf=buf_;
-       unsigned int tot,n,nw;
-       int i;
+       unsigned int n,nw;
+       int i,tot;
 
        s->rwstate=SSL_NOTHING;
+       OPENSSL_assert(s->s3->wnum < INT_MAX);
        tot=s->s3->wnum;
        s->s3->wnum=0;
 
@@ -541,6 +548,22 @@ int ssl3_write_bytes(SSL *s, int type, const void *buf_, int len)
                        }
                }
 
+       /* ensure that if we end up with a smaller value of data to write 
+        * out than the the original len from a write which didn't complete 
+        * for non-blocking I/O and also somehow ended up avoiding 
+        * the check for this in ssl3_write_pending/SSL_R_BAD_WRITE_RETRY as
+        * it must never be possible to end up with (len-tot) as a large
+        * number that will then promptly send beyond the end of the users
+        * buffer ... so we trap and report the error in a way the user
+        * will notice
+        */
+       if (len < tot)
+               {
+               SSLerr(SSL_F_SSL3_WRITE_BYTES,SSL_R_BAD_LENGTH);
+               return(-1);
+               }
+
+
        n=(len-tot);
        for (;;)
                {