indent has problems with comments that are on the right hand side of a line.
[openssl.git] / ssl / d1_both.c
index b720a54311b4858b3c62991c9bf7e3e172b164fc..b70192b4428e7ea313e58322849382ec42c7c034 100644 (file)
@@ -156,9 +156,8 @@ static unsigned char bitmask_start_values[] = {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe
 static unsigned char bitmask_end_values[]   = {0xff, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f};
 
 /* XDTLS:  figure out the right values */
-static unsigned int g_probable_mtu[] = {1500 - 28, 512 - 28, 256 - 28};
+static const unsigned int g_probable_mtu[] = {1500, 512, 256};
 
-static unsigned int dtls1_guess_mtu(unsigned int curr_mtu);
 static void dtls1_fix_message_header(SSL *s, unsigned long frag_off, 
        unsigned long frag_len);
 static unsigned char *dtls1_write_message_header(SSL *s,
@@ -211,8 +210,7 @@ dtls1_hm_fragment_new(unsigned long frag_len, int reassembly)
        return frag;
        }
 
-static void
-dtls1_hm_fragment_free(hm_fragment *frag)
+void dtls1_hm_fragment_free(hm_fragment *frag)
        {
 
        if (frag->msg_header.is_ccs)
@@ -225,53 +223,50 @@ dtls1_hm_fragment_free(hm_fragment *frag)
        OPENSSL_free(frag);
        }
 
-/* send s->init_buf in records of type 'type' (SSL3_RT_HANDSHAKE or SSL3_RT_CHANGE_CIPHER_SPEC) */
-int dtls1_do_write(SSL *s, int type)
-       {
-       int ret;
-       int curr_mtu;
-       unsigned int len, frag_off, mac_size, blocksize;
+static int dtls1_query_mtu(SSL *s)
+{
+       if(s->d1->link_mtu)
+               {
+               s->d1->mtu = s->d1->link_mtu-BIO_dgram_get_mtu_overhead(SSL_get_wbio(s));
+               s->d1->link_mtu = 0;
+               }
 
        /* AHA!  Figure out the MTU, and stick to the right size */
-       if (s->d1->mtu < dtls1_min_mtu() && !(SSL_get_options(s) & SSL_OP_NO_QUERY_MTU))
+       if (s->d1->mtu < dtls1_min_mtu(s))
                {
-               s->d1->mtu = 
-                       BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL);
-
-               /* I've seen the kernel return bogus numbers when it doesn't know
-                * (initial write), so just make sure we have a reasonable number */
-               if (s->d1->mtu < dtls1_min_mtu())
+               if(!(SSL_get_options(s) & SSL_OP_NO_QUERY_MTU))
                        {
-                       s->d1->mtu = 0;
-                       s->d1->mtu = dtls1_guess_mtu(s->d1->mtu);
-                       BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SET_MTU, 
-                               s->d1->mtu, NULL);
+                       s->d1->mtu =
+                               BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL);
+
+                       /* I've seen the kernel return bogus numbers when it doesn't know
+                        * (initial write), so just make sure we have a reasonable number */
+                       if (s->d1->mtu < dtls1_min_mtu(s))
+                               {
+                               /* Set to min mtu */
+                               s->d1->mtu = dtls1_min_mtu(s);
+                               BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SET_MTU,
+                                       s->d1->mtu, NULL);
+                               }
                        }
+               else
+                       return 0;
                }
-#if 0 
-       mtu = s->d1->mtu;
-
-       fprintf(stderr, "using MTU = %d\n", mtu);
-
-       mtu -= (DTLS1_HM_HEADER_LENGTH + DTLS1_RT_HEADER_LENGTH);
-
-       curr_mtu = mtu - BIO_wpending(SSL_get_wbio(s));
+       return 1;
+}
 
-       if ( curr_mtu > 0)
-               mtu = curr_mtu;
-       else if ( ( ret = BIO_flush(SSL_get_wbio(s))) <= 0)
-               return ret;
+/* send s->init_buf in records of type 'type' (SSL3_RT_HANDSHAKE or SSL3_RT_CHANGE_CIPHER_SPEC) */
+int dtls1_do_write(SSL *s, int type)
+       {
+       int ret;
+       unsigned int curr_mtu;
+       int retry = 1;
+       unsigned int len, frag_off, mac_size, blocksize, used_len;
 
-       if ( BIO_wpending(SSL_get_wbio(s)) + s->init_num >= mtu)
-               {
-               ret = BIO_flush(SSL_get_wbio(s));
-               if ( ret <= 0)
-                       return ret;
-               mtu = s->d1->mtu - (DTLS1_HM_HEADER_LENGTH + DTLS1_RT_HEADER_LENGTH);
-               }
-#endif
+       if(!dtls1_query_mtu(s))
+               return -1;
 
-       OPENSSL_assert(s->d1->mtu >= dtls1_min_mtu());  /* should have something reasonable now */
+       OPENSSL_assert(s->d1->mtu >= dtls1_min_mtu(s));  /* should have something reasonable now */
 
        if ( s->init_off == 0  && type == SSL3_RT_HANDSHAKE)
                OPENSSL_assert(s->init_num == 
@@ -294,10 +289,15 @@ int dtls1_do_write(SSL *s, int type)
                blocksize = 0;
 
        frag_off = 0;
-       while( s->init_num)
+       /* s->init_num shouldn't ever be < 0...but just in case */
+       while(s->init_num > 0)
                {
-               curr_mtu = s->d1->mtu - BIO_wpending(SSL_get_wbio(s)) - 
-                       DTLS1_RT_HEADER_LENGTH - mac_size - blocksize;
+               used_len = BIO_wpending(SSL_get_wbio(s)) +  DTLS1_RT_HEADER_LENGTH
+                       + mac_size + blocksize;
+               if(s->d1->mtu > used_len)
+                       curr_mtu = s->d1->mtu - used_len;
+               else
+                       curr_mtu = 0;
 
                if ( curr_mtu <= DTLS1_HM_HEADER_LENGTH)
                        {
@@ -305,15 +305,27 @@ int dtls1_do_write(SSL *s, int type)
                        ret = BIO_flush(SSL_get_wbio(s));
                        if ( ret <= 0)
                                return ret;
-                       curr_mtu = s->d1->mtu - DTLS1_RT_HEADER_LENGTH -
-                               mac_size - blocksize;
+                       used_len = DTLS1_RT_HEADER_LENGTH + mac_size + blocksize;
+                       if(s->d1->mtu > used_len + DTLS1_HM_HEADER_LENGTH)
+                               {
+                               curr_mtu = s->d1->mtu - used_len;
+                               }
+                       else
+                               {
+                               /* Shouldn't happen */
+                               return -1;
+                               }
                        }
 
-               if ( s->init_num > curr_mtu)
+               /* We just checked that s->init_num > 0 so this cast should be safe */
+               if (((unsigned int)s->init_num) > curr_mtu)
                        len = curr_mtu;
                else
                        len = s->init_num;
 
+               /* Shouldn't ever happen */
+               if(len > INT_MAX)
+                       len = INT_MAX;
 
                /* XDTLS: this function is too long.  split out the CCS part */
                if ( type == SSL3_RT_HANDSHAKE)
@@ -324,18 +336,29 @@ int dtls1_do_write(SSL *s, int type)
                                s->init_off -= DTLS1_HM_HEADER_LENGTH;
                                s->init_num += DTLS1_HM_HEADER_LENGTH;
 
-                               if ( s->init_num > curr_mtu)
+                               /* We just checked that s->init_num > 0 so this cast should be safe */
+                               if (((unsigned int)s->init_num) > curr_mtu)
                                        len = curr_mtu;
                                else
                                        len = s->init_num;
                                }
 
+                       /* Shouldn't ever happen */
+                       if(len > INT_MAX)
+                               len = INT_MAX;
+
+                       if ( len < DTLS1_HM_HEADER_LENGTH )
+                               {
+                               /*
+                                * len is so small that we really can't do anything sensible
+                                * so fail
+                                */
+                               return -1;
+                               }
                        dtls1_fix_message_header(s, frag_off, 
                                len - DTLS1_HM_HEADER_LENGTH);
 
                        dtls1_write_message_header(s, (unsigned char *)&s->init_buf->data[s->init_off]);
-
-                       OPENSSL_assert(len >= DTLS1_HM_HEADER_LENGTH);
                        }
 
                ret=dtls1_write_bytes(s,type,&s->init_buf->data[s->init_off],
@@ -348,12 +371,23 @@ int dtls1_do_write(SSL *s, int type)
                         * is fine and wait for an alert to handle the
                         * retransmit 
                         */
-                       if ( BIO_ctrl(SSL_get_wbio(s),
+                       if ( retry && BIO_ctrl(SSL_get_wbio(s),
                                BIO_CTRL_DGRAM_MTU_EXCEEDED, 0, NULL) > 0 )
-                               s->d1->mtu = BIO_ctrl(SSL_get_wbio(s),
-                                       BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL);
+                               {
+                               if(!(SSL_get_options(s) & SSL_OP_NO_QUERY_MTU))
+                                       {
+                                       if(!dtls1_query_mtu(s))
+                                               return -1;
+                                       /* Have one more go */
+                                       retry = 0;
+                                       }
+                               else
+                                       return -1;
+                               }
                        else
+                               {
                                return(-1);
+                               }
                        }
                else
                        {
@@ -447,10 +481,15 @@ long dtls1_get_message(SSL *s, int st1, int stn, int mt, long max, int *ok)
 again:
        i = dtls1_get_message_fragment(s, st1, stn, max, ok);
        if ( i == DTLS1_HM_BAD_FRAGMENT ||
-               i == DTLS1_HM_FRAGMENT_RETRY)  /* bad fragment received */
+               i == DTLS1_HM_FRAGMENT_RETRY)
+               {
+               /* bad fragment received */
                goto again;
+               }
        else if ( i <= 0 && !*ok)
+               {
                return i;
+               }
 
        p = (unsigned char *)s->init_buf->data;
        msg_len = msg_hdr->msg_len;
@@ -540,7 +579,8 @@ static int dtls1_preprocess_fragment(SSL *s,struct hm_header_st *msg_hdr,int max
 static int
 dtls1_retrieve_buffered_fragment(SSL *s, long max, int *ok)
        {
-       /* (0) check whether the desired fragment is available
+       /*-
+        * (0) check whether the desired fragment is available
         * if so:
         * (1) copy over the fragment to s->init_buf->data[]
         * (2) update s->init_num
@@ -592,29 +632,32 @@ dtls1_retrieve_buffered_fragment(SSL *s, long max, int *ok)
                return 0;
        }
 
+/* dtls1_max_handshake_message_len returns the maximum number of bytes
+ * permitted in a DTLS handshake message for |s|. The minimum is 16KB, but may
+ * be greater if the maximum certificate list size requires it. */
+static unsigned long dtls1_max_handshake_message_len(const SSL *s)
+       {
+       unsigned long max_len = DTLS1_HM_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH;
+       if (max_len < (unsigned long)s->max_cert_list)
+               return s->max_cert_list;
+       return max_len;
+       }
 
 static int
-dtls1_reassemble_fragment(SSL *s, struct hm_header_st* msg_hdr, int *ok)
+dtls1_reassemble_fragment(SSL *s, const struct hm_header_st* msg_hdr, int *ok)
        {
        hm_fragment *frag = NULL;
        pitem *item = NULL;
        int i = -1, is_complete;
        unsigned char seq64be[8];
-       unsigned long frag_len = msg_hdr->frag_len, max_len;
+       unsigned long frag_len = msg_hdr->frag_len;
 
-       if ((msg_hdr->frag_off+frag_len) > msg_hdr->msg_len)
+       if ((msg_hdr->frag_off+frag_len) > msg_hdr->msg_len ||
+           msg_hdr->msg_len > dtls1_max_handshake_message_len(s))
                goto err;
 
-       /* Determine maximum allowed message size. Depends on (user set)
-        * maximum certificate length, but 16k is minimum.
-        */
-       if (DTLS1_HM_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH < s->max_cert_list)
-               max_len = s->max_cert_list;
-       else
-               max_len = DTLS1_HM_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH;
-
-       if ((msg_hdr->frag_off+frag_len) > max_len)
-               goto err;
+       if (frag_len == 0)
+               return DTLS1_HM_FRAGMENT_RETRY;
 
        /* Try to find item in queue */
        memset(seq64be,0,sizeof(seq64be));
@@ -644,7 +687,8 @@ dtls1_reassemble_fragment(SSL *s, struct hm_header_st* msg_hdr, int *ok)
 
 
        /* If message is already reassembled, this must be a
-        * retransmit and can be dropped.
+        * retransmit and can be dropped. In this case item != NULL and so frag
+        * does not need to be freed.
         */
        if (frag->reassembly == NULL)
                {
@@ -664,7 +708,9 @@ dtls1_reassemble_fragment(SSL *s, struct hm_header_st* msg_hdr, int *ok)
        /* read the body of the fragment (header has already been read */
        i = s->method->ssl_read_bytes(s,SSL3_RT_HANDSHAKE,
                frag->fragment + msg_hdr->frag_off,frag_len,0);
-       if (i<=0 || (unsigned long)i!=frag_len)
+       if ((unsigned long)i!=frag_len)
+               i=-1;
+       if (i<=0)
                goto err;
 
        RSMBLY_BITMASK_MARK(frag->reassembly, (long)msg_hdr->frag_off,
@@ -681,10 +727,6 @@ dtls1_reassemble_fragment(SSL *s, struct hm_header_st* msg_hdr, int *ok)
 
        if (item == NULL)
                {
-               memset(seq64be,0,sizeof(seq64be));
-               seq64be[6] = (unsigned char)(msg_hdr->seq>>8);
-               seq64be[7] = (unsigned char)(msg_hdr->seq);
-
                item = pitem_new(seq64be, frag);
                if (item == NULL)
                        {
@@ -692,7 +734,12 @@ dtls1_reassemble_fragment(SSL *s, struct hm_header_st* msg_hdr, int *ok)
                        goto err;
                        }
 
-               pqueue_insert(s->d1->buffered_messages, item);
+               item = pqueue_insert(s->d1->buffered_messages, item);
+               /* pqueue_insert fails iff a duplicate item is inserted.
+                * However, |item| cannot be a duplicate. If it were,
+                * |pqueue_find|, above, would have returned it and control
+                * would never have reached this branch. */
+               OPENSSL_assert(item != NULL);
                }
 
        return DTLS1_HM_FRAGMENT_RETRY;
@@ -705,7 +752,7 @@ err:
 
 
 static int
-dtls1_process_out_of_seq_message(SSL *s, struct hm_header_st* msg_hdr, int *ok)
+dtls1_process_out_of_seq_message(SSL *s, const struct hm_header_st* msg_hdr, int *ok)
 {
        int i=-1;
        hm_fragment *frag = NULL;
@@ -725,7 +772,7 @@ dtls1_process_out_of_seq_message(SSL *s, struct hm_header_st* msg_hdr, int *ok)
        /* If we already have an entry and this one is a fragment,
         * don't discard it and rather try to reassemble it.
         */
-       if (item != NULL && frag_len < msg_hdr->msg_len)
+       if (item != NULL && frag_len != msg_hdr->msg_len)
                item = NULL;
 
        /* Discard the message if sequence number was already there, is
@@ -750,9 +797,12 @@ dtls1_process_out_of_seq_message(SSL *s, struct hm_header_st* msg_hdr, int *ok)
                }
        else
                {
-               if (frag_len && frag_len < msg_hdr->msg_len)
+               if (frag_len != msg_hdr->msg_len)
                        return dtls1_reassemble_fragment(s, msg_hdr, ok);
 
+               if (frag_len > dtls1_max_handshake_message_len(s))
+                       goto err;
+
                frag = dtls1_hm_fragment_new(frag_len, 0);
                if ( frag == NULL)
                        goto err;
@@ -764,19 +814,25 @@ dtls1_process_out_of_seq_message(SSL *s, struct hm_header_st* msg_hdr, int *ok)
                        /* read the body of the fragment (header has already been read */
                        i = s->method->ssl_read_bytes(s,SSL3_RT_HANDSHAKE,
                                frag->fragment,frag_len,0);
-                       if (i<=0 || (unsigned long)i!=frag_len)
+                       if ((unsigned long)i!=frag_len)
+                               i = -1;
+                       if (i<=0)
                                goto err;
                        }
 
-               memset(seq64be,0,sizeof(seq64be));
-               seq64be[6] = (unsigned char)(msg_hdr->seq>>8);
-               seq64be[7] = (unsigned char)(msg_hdr->seq);
-
                item = pitem_new(seq64be, frag);
                if ( item == NULL)
                        goto err;
 
-               pqueue_insert(s->d1->buffered_messages, item);
+               item = pqueue_insert(s->d1->buffered_messages, item);
+               /* pqueue_insert fails iff a duplicate item is inserted.
+                * However, |item| cannot be a duplicate. If it were,
+                * |pqueue_find|, above, would have returned it. Then, either
+                * |frag_len| != |msg_hdr->msg_len| in which case |item| is set
+                * to NULL and it will have been processed with
+                * |dtls1_reassemble_fragment|, above, or the record will have
+                * been discarded. */
+               OPENSSL_assert(item != NULL);
                }
 
        return DTLS1_HM_FRAGMENT_RETRY;
@@ -914,7 +970,8 @@ f_err:
        return(-1);
        }
 
-/* for these 2 messages, we need to
+/*-
+ * for these 2 messages, we need to
  * ssl->enc_read_ctx                   re-init
  * ssl->s3->read_sequence              zero
  * ssl->s3->read_mac_secret            re-init
@@ -970,9 +1027,11 @@ int dtls1_read_failed(SSL *s, int code)
                }
 
 #ifndef OPENSSL_NO_HEARTBEATS
-       if (!SSL_in_init(s) && !s->tlsext_hb_pending)  /* done, no need to send a retransmit */
+       /* done, no need to send a retransmit */
+       if (!SSL_in_init(s) && !s->tlsext_hb_pending)
 #else
-       if (!SSL_in_init(s))  /* done, no need to send a retransmit */
+       /* done, no need to send a retransmit */
+       if (!SSL_in_init(s))
 #endif
                {
                BIO_set_flags(SSL_get_rbio(s), BIO_FLAGS_READ);
@@ -1115,7 +1174,7 @@ dtls1_retransmit_message(SSL *s, unsigned short seq, unsigned long frag_off,
        struct dtls1_retransmit_state saved_state;
        unsigned char save_write_sequence[8];
 
-       /*
+       /*-
          OPENSSL_assert(s->init_num == 0);
          OPENSSL_assert(s->init_off == 0);
         */
@@ -1267,28 +1326,20 @@ dtls1_write_message_header(SSL *s, unsigned char *p)
        return p;
        }
 
-unsigned int 
-dtls1_min_mtu(void)
+unsigned int
+dtls1_link_min_mtu(void)
        {
        return (g_probable_mtu[(sizeof(g_probable_mtu) / 
                sizeof(g_probable_mtu[0])) - 1]);
        }
 
-static unsigned int 
-dtls1_guess_mtu(unsigned int curr_mtu)
+unsigned int
+dtls1_min_mtu(SSL *s)
        {
-       unsigned int i;
-
-       if ( curr_mtu == 0 )
-               return g_probable_mtu[0] ;
-
-       for ( i = 0; i < sizeof(g_probable_mtu)/sizeof(g_probable_mtu[0]); i++)
-               if ( curr_mtu > g_probable_mtu[i])
-                       return g_probable_mtu[i];
-
-       return curr_mtu;
+       return dtls1_link_min_mtu()-BIO_dgram_get_mtu_overhead(SSL_get_wbio(s));
        }
 
+
 void
 dtls1_get_message_header(unsigned char *data, struct hm_header_st *msg_hdr)
        {
@@ -1347,6 +1398,9 @@ dtls1_process_heartbeat(SSL *s)
        /* Read type and payload length first */
        if (1 + 2 + 16 > s->s3->rrec.length)
                return 0; /* silently discard */
+       if (s->s3->rrec.length > SSL3_RT_MAX_PLAIN_LENGTH)
+               return 0; /* silently discard per RFC 6520 sec. 4 */
+
        hbtype = *p++;
        n2s(p, payload);
        if (1 + 2 + payload + 16 > s->s3->rrec.length)
@@ -1369,6 +1423,8 @@ dtls1_process_heartbeat(SSL *s)
                 * payload, plus padding
                 */
                buffer = OPENSSL_malloc(write_length);
+               if (buffer == NULL)
+                       return -1;
                bp = buffer;
 
                /* Enter response type, length and copy payload */
@@ -1446,7 +1502,8 @@ dtls1_heartbeat(SSL *s)
         */
        OPENSSL_assert(payload + padding <= 16381);
 
-       /* Create HeartBeat message, we just use a sequence number
+       /*-
+        * Create HeartBeat message, we just use a sequence number
         * as payload to distuingish different messages and add
         * some random stuff.
         *  - Message Type, 1 byte
@@ -1456,6 +1513,11 @@ dtls1_heartbeat(SSL *s)
         *  - Padding
         */
        buf = OPENSSL_malloc(1 + 2 + payload + padding);
+       if (buf == NULL)
+               {
+               SSLerr(SSL_F_DTLS1_HEARTBEAT, ERR_R_MALLOC_FAILURE);
+               return -1;
+               }
        p = buf;
        /* Message Type */
        *p++ = TLS1_HB_REQUEST;