Fix DTLS handshake message size checks.

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>

(Imported from upstream's 0598468fc04fb0cf2438c4ee635b587aac1bcce6)

Change-Id: I4849498eabb45ec973fcb988d639b23145891e25
Reviewed-on: https://boringssl-review.googlesource.com/1434
Reviewed-by: Adam Langley <agl@google.com>
This commit is contained in:
Matt Caswell 2014-06-06 14:25:52 -07:00 committed by Adam Langley
parent 3873f6f33d
commit 2306fe5ff5

View File

@ -595,6 +595,16 @@ dtls1_retrieve_buffered_fragment(SSL *s, long max, int *ok)
return 0; 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 static int
dtls1_reassemble_fragment(SSL *s, struct hm_header_st* msg_hdr, int *ok) dtls1_reassemble_fragment(SSL *s, struct hm_header_st* msg_hdr, int *ok)
@ -603,20 +613,10 @@ dtls1_reassemble_fragment(SSL *s, struct hm_header_st* msg_hdr, int *ok)
pitem *item = NULL; pitem *item = NULL;
int i = -1, is_complete; int i = -1, is_complete;
unsigned char seq64be[8]; 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 ||
goto err; msg_hdr->msg_len > dtls1_max_handshake_message_len(s))
/* 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; goto err;
/* Try to find item in queue */ /* Try to find item in queue */
@ -756,6 +756,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) if (frag_len && frag_len < msg_hdr->msg_len)
return dtls1_reassemble_fragment(s, msg_hdr, ok); 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); frag = dtls1_hm_fragment_new(frag_len, 0);
if ( frag == NULL) if ( frag == NULL)
goto err; goto err;