Fix DTLS handshake message size checks.
authorMatt Caswell <matt@openssl.org>
Fri, 6 Jun 2014 21:25:52 +0000 (14:25 -0700)
committerMatt Caswell <matt@openssl.org>
Wed, 6 Aug 2014 19:36:40 +0000 (20:36 +0100)
In |dtls1_reassemble_fragment|, the value of
|msg_hdr->frag_off+frag_len| was being checked against the maximum
handshake message size, but then |msg_len| bytes were allocated for the
fragment buffer. This means that so long as the fragment was within the
allowed size, the pending handshake message could consume 16MB + 2MB
(for the reassembly bitmap). Approx 10 outstanding handshake messages
are allowed, meaning that an attacker could consume ~180MB per DTLS
connection.

In the non-fragmented path (in |dtls1_process_out_of_seq_message|), no
check was applied.

Fixes CVE-2014-3506

Wholly based on patch by Adam Langley with one minor amendment.

Reviewed-by: Emilia Käsper <emilia@openssl.org>
ssl/d1_both.c

index d98ae9392f841168d8a7287b289f7cc9f793705a..c5beea8824612c4d512da68f07a0146e0892c269 100644 (file)
@@ -592,6 +592,16 @@ 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)
@@ -600,20 +610,10 @@ dtls1_reassemble_fragment(SSL *s, struct hm_header_st* msg_hdr, int *ok)
        pitem *item = NULL;
        int i = -1, is_complete;
        unsigned char seq64be[8];
-       unsigned long frag_len = msg_hdr->frag_len, max_len;
-
-       if ((msg_hdr->frag_off+frag_len) > msg_hdr->msg_len)
-               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;
+       unsigned long frag_len = msg_hdr->frag_len;
 
-       if ((msg_hdr->frag_off+frag_len) > max_len)
+       if ((msg_hdr->frag_off+frag_len) > msg_hdr->msg_len ||
+           msg_hdr->msg_len > dtls1_max_handshake_message_len(s))
                goto err;
 
        /* Try to find item in queue */
@@ -754,6 +754,9 @@ dtls1_process_out_of_seq_message(SSL *s, struct hm_header_st* msg_hdr, int *ok)
                if (frag_len && 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;