Add and use a constant-time memcmp.
[openssl.git] / ssl / d1_pkt.c
index d88ef19fcde255831f0f5207c03fdf0446f8eb1c..8e9ee5a77e5fab1a2b1ab8044dca69a87b47c5b8 100644 (file)
@@ -134,12 +134,11 @@ static int dtls1_record_needs_buffering(SSL *s, SSL3_RECORD *rr,
        unsigned short *priority, unsigned long *offset);
 #endif
 static int dtls1_buffer_record(SSL *s, record_pqueue *q,
-       PQ_64BIT priority);
+       PQ_64BIT *priority);
 static int dtls1_process_record(SSL *s);
 #if PQ_64BIT_IS_INTEGER
 static PQ_64BIT bytes_to_long_long(unsigned char *bytes, PQ_64BIT *num);
 #endif
-static void dtls1_clear_timeouts(SSL *s);
 
 /* copy buffered record into SSL structure */
 static int
@@ -156,13 +155,16 @@ dtls1_copy_record(SSL *s, pitem *item)
     s->packet_length = rdata->packet_length;
     memcpy(&(s->s3->rbuf), &(rdata->rbuf), sizeof(SSL3_BUFFER));
     memcpy(&(s->s3->rrec), &(rdata->rrec), sizeof(SSL3_RECORD));
+       
+       /* Set proper sequence number for mac calculation */
+       memcpy(&(s->s3->read_sequence[2]), &(rdata->packet[5]), 6);
     
     return(1);
     }
 
 
 static int
-dtls1_buffer_record(SSL *s, record_pqueue *queue, PQ_64BIT priority)
+dtls1_buffer_record(SSL *s, record_pqueue *queue, PQ_64BIT *priority)
 {
     DTLS1_RECORD_DATA *rdata;
        pitem *item;
@@ -172,7 +174,7 @@ dtls1_buffer_record(SSL *s, record_pqueue *queue, PQ_64BIT priority)
                return 0;
                
        rdata = OPENSSL_malloc(sizeof(DTLS1_RECORD_DATA));
-       item = pitem_new(priority, rdata);
+       item = pitem_new(*priority, rdata);
        if (rdata == NULL || item == NULL)
                {
                if (rdata != NULL) OPENSSL_free(rdata);
@@ -253,9 +255,6 @@ dtls1_process_buffered_records(SSL *s)
     item = pqueue_peek(s->d1->unprocessed_rcds.q);
     if (item)
         {
-        DTLS1_RECORD_DATA *rdata;
-        rdata = (DTLS1_RECORD_DATA *)item->data;
-        
         /* Check if epoch is current. */
         if (s->d1->unprocessed_rcds.epoch != s->d1->r_epoch)
             return(1);  /* Nothing to do. */
@@ -267,7 +266,7 @@ dtls1_process_buffered_records(SSL *s)
             if ( ! dtls1_process_record(s))
                 return(0);
             dtls1_buffer_record(s, &(s->d1->processed_rcds), 
-                s->s3->rrec.seq_num);
+                &s->s3->rrec.seq_num);
             }
         }
 
@@ -328,13 +327,15 @@ dtls1_get_buffered_record(SSL *s)
 static int
 dtls1_process_record(SSL *s)
 {
-    int i,al;
+    int al;
        int clear=0;
     int enc_err;
        SSL_SESSION *sess;
     SSL3_RECORD *rr;
        unsigned int mac_size;
        unsigned char md[EVP_MAX_MD_SIZE];
+       int decryption_failed_or_bad_record_mac = 0;
+       unsigned char *mac = NULL;
 
 
        rr= &(s->s3->rrec);
@@ -369,12 +370,10 @@ dtls1_process_record(SSL *s)
        enc_err = s->method->ssl3_enc->enc(s,0);
        if (enc_err <= 0)
                {
-               if (enc_err == 0)
-                       /* SSLerr() and ssl3_send_alert() have been called */
-                       goto err;
-
-               /* otherwise enc_err == -1 */
-               goto decryption_failed_or_bad_record_mac;
+               /* To minimize information leaked via timing, we will always
+                * perform all computations before discarding the message.
+                */
+               decryption_failed_or_bad_record_mac = 1;
                }
 
 #ifdef TLS_DEBUG
@@ -400,28 +399,32 @@ if (      (sess == NULL) ||
                        SSLerr(SSL_F_DTLS1_PROCESS_RECORD,SSL_R_PRE_MAC_LENGTH_TOO_LONG);
                        goto f_err;
 #else
-                       goto decryption_failed_or_bad_record_mac;
+                       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 (rr->length >= mac_size)
                        {
-#if 0 /* OK only for stream ciphers */
-                       al=SSL_AD_DECODE_ERROR;
-                       SSLerr(SSL_F_DTLS1_PROCESS_RECORD,SSL_R_LENGTH_TOO_SHORT);
-                       goto f_err;
-#else
-                       goto decryption_failed_or_bad_record_mac;
-#endif
+                       rr->length -= mac_size;
+                       mac = &rr->data[rr->length];
                        }
-               rr->length-=mac_size;
+               else
+                       rr->length = 0;
                i=s->method->ssl3_enc->mac(s,md,0);
-               if (memcmp(md,&(rr->data[rr->length]),mac_size) != 0)
+               if (i < 0 || mac == NULL || CRYPTO_memcmp(md,mac,mac_size) != 0)
                        {
-                       goto decryption_failed_or_bad_record_mac;
+                       decryption_failed_or_bad_record_mac = 1;
                        }
                }
 
+       if (decryption_failed_or_bad_record_mac)
+               {
+               /* decryption failed, silently discard message */
+               rr->length = 0;
+               s->packet_length = 0;
+               goto err;
+               }
+
        /* r->length is now just compressed */
        if (s->expand != NULL)
                {
@@ -460,14 +463,6 @@ if (       (sess == NULL) ||
     dtls1_record_bitmap_update(s, &(s->d1->bitmap));/* Mark receipt of record. */
     return(1);
 
-decryption_failed_or_bad_record_mac:
-       /* Separate 'decryption_failed' alert was introduced with TLS 1.0,
-        * SSL 3.0 only has 'bad_record_mac'.  But unless a decryption
-        * failure is directly visible from the ciphertext anyway,
-        * we should not reveal which kind of error occured -- this
-        * might become visible to an attacker (e.g. via logfile) */
-       al=SSL_AD_BAD_RECORD_MAC;
-       SSLerr(SSL_F_DTLS1_PROCESS_RECORD,SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC);
 f_err:
        ssl3_send_alert(s,SSL3_AL_FATAL,al);
 err:
@@ -486,22 +481,19 @@ err:
 /* used only by dtls1_read_bytes */
 int dtls1_get_record(SSL *s)
        {
-       int ssl_major,ssl_minor,al;
+       int ssl_major,ssl_minor;
        int i,n;
        SSL3_RECORD *rr;
-       SSL_SESSION *sess;
-       unsigned char *p;
+       unsigned char *p = NULL;
        unsigned short version;
        DTLS1_BITMAP *bitmap;
        unsigned int is_next_epoch;
 
        rr= &(s->s3->rrec);
-       sess=s->session;
 
     /* The epoch may have changed.  If so, process all the
      * pending records.  This is a non-blocking operation. */
-    if ( ! dtls1_process_buffered_records(s))
-        return 0;
+    dtls1_process_buffered_records(s);
 
        /* if we're renegotiating, then there may be buffered records */
        if (dtls1_get_processed_record(s))
@@ -517,7 +509,12 @@ again:
                /* read timeout is handled by dtls1_read_bytes */
                if (n <= 0) return(n); /* error or non-blocking */
 
-               OPENSSL_assert(s->packet_length == DTLS1_RT_HEADER_LENGTH);
+               /* this packet contained a partial record, dump it */
+               if (s->packet_length != DTLS1_RT_HEADER_LENGTH)
+                       {
+                       s->packet_length = 0;
+                       goto again;
+                       }
 
                s->rstate=SSL_ST_READ_BODY;
 
@@ -542,27 +539,28 @@ again:
                        {
                        if (version != s->version && version != DTLS1_BAD_VER)
                                {
-                               SSLerr(SSL_F_DTLS1_GET_RECORD,SSL_R_WRONG_VERSION_NUMBER);
-                               /* Send back error using their
-                                * version number :-) */
-                               s->version=version;
-                               al=SSL_AD_PROTOCOL_VERSION;
-                               goto f_err;
+                               /* unexpected version, silently discard */
+                               rr->length = 0;
+                               s->packet_length = 0;
+                               goto again;
                                }
                        }
 
                if ((version & 0xff00) != (DTLS1_VERSION & 0xff00) &&
                    (version & 0xff00) != (DTLS1_BAD_VER & 0xff00))
                        {
-                       SSLerr(SSL_F_DTLS1_GET_RECORD,SSL_R_WRONG_VERSION_NUMBER);
-                       goto err;
+                       /* wrong version, silently discard record */
+                       rr->length = 0;
+                       s->packet_length = 0;
+                       goto again;
                        }
 
                if (rr->length > SSL3_RT_MAX_ENCRYPTED_LENGTH)
                        {
-                       al=SSL_AD_RECORD_OVERFLOW;
-                       SSLerr(SSL_F_DTLS1_GET_RECORD,SSL_R_PACKET_LENGTH_TOO_LONG);
-                       goto f_err;
+                       /* record too long, silently discard it */
+                       rr->length = 0;
+                       s->packet_length = 0;
+                       goto again;
                        }
 
                s->client_version = version;
@@ -581,6 +579,7 @@ again:
                /* this packet contained a partial record, dump it */
                if ( n != i)
                        {
+                       rr->length = 0;
                        s->packet_length = 0;
                        goto again;
                        }
@@ -594,12 +593,20 @@ again:
        bitmap = dtls1_get_bitmap(s, rr, &is_next_epoch);
        if ( bitmap == NULL)
         {
+       rr->length = 0;
         s->packet_length = 0;  /* dump this record */
         goto again;   /* get another record */
                }
 
-       /* check whether this is a repeat, or aged record */
-       if ( ! dtls1_record_replay_check(s, bitmap, &(rr->seq_num)))
+       /* Check whether this is a repeat, or aged record.
+        * Don't check if we're listening and this message is
+        * a ClientHello. They can look as if they're replayed,
+        * since they arrive from different connections and
+        * would be dropped unnecessarily.
+        */
+       if (!(s->d1->listen && rr->type == SSL3_RT_HANDSHAKE &&
+               *p == SSL3_MT_CLIENT_HELLO) &&
+               ! dtls1_record_replay_check(s, bitmap, &(rr->seq_num)))
                {
                rr->length = 0;
                s->packet_length=0; /* dump this record */
@@ -609,28 +616,31 @@ again:
        /* just read a 0 length packet */
        if (rr->length == 0) goto again;
 
-    /* If this record is from the next epoch (either HM or ALERT), buffer it
-     * since it cannot be processed at this time.
-     * Records from the next epoch are marked as received even though they are 
-     * not processed, so as to prevent any potential resource DoS attack */
-    if (is_next_epoch)
-        {
-        dtls1_record_bitmap_update(s, bitmap);
-        dtls1_buffer_record(s, &(s->d1->unprocessed_rcds), rr->seq_num);
+       /* If this record is from the next epoch (either HM or ALERT),
+        * and a handshake is currently in progress, buffer it since it
+        * cannot be processed at this time. However, do not buffer
+        * anything while listening.
+        */
+       if (is_next_epoch)
+               {
+               if ((SSL_in_init(s) || s->in_handshake) && !s->d1->listen)
+                       {
+                       dtls1_buffer_record(s, &(s->d1->unprocessed_rcds), &rr->seq_num);
+                       }
+               rr->length = 0;
         s->packet_length = 0;
         goto again;
         }
 
-    if ( ! dtls1_process_record(s))
-        return(0);
+    if (!dtls1_process_record(s))
+               {
+               rr->length = 0;
+               s->packet_length=0; /* dump this record */
+               goto again;     /* get another record */
+               }
 
-       dtls1_clear_timeouts(s);  /* done waiting */
        return(1);
 
-f_err:
-       ssl3_send_alert(s,SSL3_AL_FATAL,al);
-err:
-       return(0);
        }
 
 /* Return up to 'len' payload bytes received in 'type' records.
@@ -725,11 +735,8 @@ start:
                }
 
        /* Check for timeout */
-       if (dtls1_is_timer_expired(s))
-               {
-               if (dtls1_read_failed(s, -1) > 0);
-                       goto start;
-               }
+       if (dtls1_handle_timeout(s) > 0)
+               goto start;
 
        /* get new packet if necessary */
        if ((rr->length == 0) || (s->rstate == SSL_ST_READ_BODY))
@@ -757,7 +764,7 @@ start:
                 * buffer the application data for later processing rather
                 * than dropping the connection.
                 */
-               dtls1_buffer_record(s, &(s->d1->buffered_app_data), 0);
+               dtls1_buffer_record(s, &(s->d1->buffered_app_data), &rr->seq_num);
                rr->length = 0;
                goto start;
                }
@@ -1100,6 +1107,9 @@ start:
                 */
                if (msg_hdr.type == SSL3_MT_FINISHED)
                        {
+                       if (dtls1_check_timeout_num(s) < 0)
+                               return -1;
+
                        dtls1_retransmit_buffered_messages(s);
                        rr->length = 0;
                        goto start;
@@ -1211,7 +1221,6 @@ err:
 int
 dtls1_write_app_data_bytes(SSL *s, int type, const void *buf_, int len)
        {
-       unsigned int n,tot;
        int i;
 
        if (SSL_in_init(s) && !s->in_handshake)
@@ -1225,31 +1234,14 @@ dtls1_write_app_data_bytes(SSL *s, int type, const void *buf_, int len)
                        }
                }
 
-       tot = s->s3->wnum;
-       n = len - tot;
-
-       while( n)
+       if (len > SSL3_RT_MAX_PLAIN_LENGTH)
                {
-               /* dtls1_write_bytes sends one record at a time, sized according to 
-                * the currently known MTU */
-               i = dtls1_write_bytes(s, type, buf_, len);
-               if (i <= 0) return i;
-               
-               if ((i == (int)n) ||
-                       (type == SSL3_RT_APPLICATION_DATA &&
-                               (s->mode & SSL_MODE_ENABLE_PARTIAL_WRITE)))
-                       {
-                       /* next chunk of data should get another prepended empty fragment
-                        * in ciphersuites with known-IV weakness: */
-                       s->s3->empty_fragment_done = 0;
-                       return tot+i;
-                       }
-
-               tot += i;
-               n-=i;
+                       SSLerr(SSL_F_DTLS1_WRITE_APP_DATA_BYTES,SSL_R_DTLS_MESSAGE_TOO_BIG);
+                       return -1;
                }
 
-       return tot;
+       i = dtls1_write_bytes(s, type, buf_, len);
+       return i;
        }
 
 
@@ -1290,46 +1282,13 @@ have_handshake_fragment(SSL *s, int type, unsigned char *buf,
 /* Call this to write data in records of type 'type'
  * It will return <= 0 if not all data has been sent or non-blocking IO.
  */
-int dtls1_write_bytes(SSL *s, int type, const void *buf_, int len)
+int dtls1_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 mtu;
 
+       OPENSSL_assert(len <= SSL3_RT_MAX_PLAIN_LENGTH);
        s->rwstate=SSL_NOTHING;
-       tot=s->s3->wnum;
-
-       n=(len-tot);
-
-       /* handshake layer figures out MTU for itself, but data records
-        * are also sent through this interface, so need to figure out MTU */
-#if 0
-       mtu = BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_GET_MTU, 0, NULL);
-       mtu += DTLS1_HM_HEADER_LENGTH;  /* HM already inserted */
-#endif
-       mtu = s->d1->mtu;
-
-       if (mtu > SSL3_RT_MAX_PLAIN_LENGTH)
-               mtu = SSL3_RT_MAX_PLAIN_LENGTH;
-
-       if (n > mtu)
-               nw=mtu;
-       else
-               nw=n;
-       
-       i=do_dtls1_write(s, type, &(buf[tot]), nw, 0);
-       if (i <= 0)
-               {
-               s->s3->wnum=tot;
-               return i;
-               }
-
-       if ( (int)s->s3->wnum + i == len)
-               s->s3->wnum = 0;
-       else 
-               s->s3->wnum += i;
-
+       i=do_dtls1_write(s, type, buf, len, 0);
        return i;
        }
 
@@ -1854,10 +1813,3 @@ bytes_to_long_long(unsigned char *bytes, PQ_64BIT *num)
        return _num;
        }
 #endif
-
-
-static void
-dtls1_clear_timeouts(SSL *s)
-       {
-       memset(&(s->d1->timeout), 0x00, sizeof(struct dtls1_timeout_st));
-       }