Commit 934e22e8 authored by Dr. Stephen Henson's avatar Dr. Stephen Henson
Browse files

PR: 2230

Submitted By: Robin Seggelmann <seggelmann@fh-muenster.de>

Fix various DTLS fragment reassembly bugs.
parent 3122d1d3
Loading
Loading
Loading
Loading
+231 −83
Original line number Diff line number Diff line
@@ -123,6 +123,36 @@
#include <openssl/evp.h>
#include <openssl/x509.h>

#define RSMBLY_BITMASK_SIZE(msg_len) (((msg_len) + 7) / 8)

#define RSMBLY_BITMASK_MARK(bitmask, start, end) { \
			if ((end) - (start) <= 8) { \
				int ii; \
				for (ii = (start); ii < (end); ii++) bitmask[((ii) >> 3)] |= (1 << ((ii) & 7)); \
			} else { \
				int ii; \
				bitmask[((start) >> 3)] |= bitmask_start_values[((start) & 7)]; \
				for (ii = (((start) >> 3) + 1); ii < ((end) >> 3); ii++) bitmask[ii] = 0xff; \
				bitmask[((end) >> 3)] |= bitmask_end_values[((end) & 7)]; \
			} }

#define RSMBLY_BITMASK_IS_COMPLETE(bitmask, msg_len, is_complete) { \
			int ii; \
			is_complete = 1; \
			if (bitmask[((msg_len) >> 3)] != bitmask_end_values[((msg_len) & 7)]) is_complete = 0; \
			if (is_complete) for (ii = 0; ii < ((msg_len) >> 3); ii++) \
			if (bitmask[ii] != 0xff) { is_complete = 0; break; } }

#if 0
#define RSMBLY_BITMASK_PRINT(bitmask, msg_len) { \
			int ii; \
			printf("bitmask: "); for (ii = 0; ii < (msg_len); ii++) \
			printf("%d ", (bitmask[ii >> 3] & (1 << (ii & 7))) >> (ii & 7)); \
			printf("\n"); }
#endif

static unsigned char bitmask_start_values[] = {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80};
static unsigned char bitmask_end_values[]   = {0x00, 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};
@@ -140,10 +170,11 @@ static long dtls1_get_message_fragment(SSL *s, int st1, int stn,
	long max, int *ok);

static hm_fragment *
dtls1_hm_fragment_new(unsigned long frag_len)
dtls1_hm_fragment_new(unsigned long frag_len, int reassembly)
	{
	hm_fragment *frag = NULL;
	unsigned char *buf = NULL;
	unsigned char *bitmask = NULL;

	frag = (hm_fragment *)OPENSSL_malloc(sizeof(hm_fragment));
	if ( frag == NULL)
@@ -162,6 +193,21 @@ dtls1_hm_fragment_new(unsigned long frag_len)
	/* zero length fragment gets zero frag->fragment */
	frag->fragment = buf;

	/* Initialize reassembly bitmask if necessary */
	if (reassembly)
		{
		bitmask = (unsigned char *)OPENSSL_malloc(RSMBLY_BITMASK_SIZE(frag_len));
		if (bitmask == NULL)
			{
			if (buf != NULL) OPENSSL_free(buf);
			OPENSSL_free(frag);
			return NULL;
			}
		memset(bitmask, 0, RSMBLY_BITMASK_SIZE(frag_len));
		}

	frag->reassembly = bitmask;

	return frag;
	}

@@ -169,6 +215,7 @@ static void
dtls1_hm_fragment_free(hm_fragment *frag)
	{
	if (frag->fragment) OPENSSL_free(frag->fragment);
	if (frag->reassembly) OPENSSL_free(frag->reassembly);
	OPENSSL_free(frag);
	}

@@ -363,6 +410,8 @@ long dtls1_get_message(SSL *s, int st1, int stn, int mt, long max, int *ok)
	{
	int i, al;
	struct hm_header_st *msg_hdr;
	unsigned char *p;
	unsigned long msg_len;

	/* s3->tmp is used to store messages that are unexpected, caused
	 * by the absence of an optional handshake message */
@@ -382,38 +431,20 @@ long dtls1_get_message(SSL *s, int st1, int stn, int mt, long max, int *ok)
		}

	msg_hdr = &s->d1->r_msg_hdr;
	do
		{
		if ( msg_hdr->frag_off == 0)
			{
			/* s->d1->r_message_header.msg_len = 0; */
	memset(msg_hdr, 0x00, sizeof(struct hm_header_st));
			}

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 */
			continue;
		goto again;
	else if ( i <= 0 && !*ok)
		return i;

		/* Note that s->init_sum is used as a counter summing
		 * up fragments' lengths: as soon as they sum up to
		 * handshake packet length, we assume we have got all
		 * the fragments. Overlapping fragments would cause
		 * premature termination, so we don't expect overlaps.
		 * Well, handling overlaps would require something more
		 * drastic. Indeed, as it is now there is no way to
		 * tell if out-of-order fragment from the middle was
		 * the last. '>=' is the best/least we can do to control
		 * the potential damage caused by malformed overlaps. */
		if ((unsigned int)s->init_num >= msg_hdr->msg_len)
			{
			unsigned char *p = (unsigned char *)s->init_buf->data;
			unsigned long msg_len = msg_hdr->msg_len;

			/* reconstruct message header as if it was
			 * sent in single fragment */
	p = (unsigned char *)s->init_buf->data;
	msg_len = msg_hdr->msg_len;

	/* reconstruct message header */
	*(p++) = msg_hdr->type;
	l2n3(msg_len,p);
	s2n (msg_hdr->seq,p);
@@ -449,10 +480,6 @@ long dtls1_get_message(SSL *s, int st1, int stn, int mt, long max, int *ok)

	s->init_msg = s->init_buf->data + DTLS1_HM_HEADER_LENGTH;
	return s->init_num;
			}
		else
			msg_hdr->frag_off = i;
		} while(1) ;

f_err:
	ssl3_send_alert(s,SSL3_AL_FATAL,al);
@@ -529,6 +556,10 @@ dtls1_retrieve_buffered_fragment(SSL *s, long max, int *ok)

	frag = (hm_fragment *)item->data;
	
	/* Don't return if reassembly still in progress */
	if (frag->reassembly != NULL)
		return 0;

	if ( s->d1->handshake_read_seq == frag->msg_header.seq)
		{
		unsigned long frag_len = frag->msg_header.frag_len;
@@ -562,6 +593,109 @@ dtls1_retrieve_buffered_fragment(SSL *s, long max, int *ok)
	}


static int
dtls1_reassemble_fragment(SSL *s, 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;

	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;

	if ((msg_hdr->frag_off+frag_len) > max_len)
		goto err;

	/* Try to find item in queue */
	memset(seq64be,0,sizeof(seq64be));
	seq64be[6] = (unsigned char) (msg_hdr->seq>>8);
	seq64be[7] = (unsigned char) msg_hdr->seq;
	item = pqueue_find(s->d1->buffered_messages, seq64be);

	if (item == NULL)
		{
		frag = dtls1_hm_fragment_new(msg_hdr->msg_len, 1);
		if ( frag == NULL)
			goto err;
		memcpy(&(frag->msg_header), msg_hdr, sizeof(*msg_hdr));
		frag->msg_header.frag_len = frag->msg_header.msg_len;
		frag->msg_header.frag_off = 0;
		}
	else
		frag = (hm_fragment*) item->data;

	/* If message is already reassembled, this must be a
	 * retransmit and can be dropped.
	 */
	if (frag->reassembly == NULL)
		{
		unsigned char devnull [256];

		while (frag_len)
			{
			i = s->method->ssl_read_bytes(s,SSL3_RT_HANDSHAKE,
				devnull,
				frag_len>sizeof(devnull)?sizeof(devnull):frag_len,0);
			if (i<=0) goto err;
			frag_len -= i;
			}
		return DTLS1_HM_FRAGMENT_RETRY;
		}

	/* 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)
		goto err;

	RSMBLY_BITMASK_MARK(frag->reassembly, msg_hdr->frag_off,
	                    msg_hdr->frag_off + frag_len);

	RSMBLY_BITMASK_IS_COMPLETE(frag->reassembly, msg_hdr->msg_len,
	                           is_complete)

	if (is_complete)
		{
		OPENSSL_free(frag->reassembly);
		frag->reassembly = NULL;
		}

	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)
			{
			goto err;
			i = -1;
			}

		pqueue_insert(s->d1->buffered_messages, item);
		}

	return DTLS1_HM_FRAGMENT_RETRY;

err:
	if (frag != NULL) dtls1_hm_fragment_free(frag);
	if (item != NULL) OPENSSL_free(item);
	*ok = 0;
	return i;
	}


static int
dtls1_process_out_of_seq_message(SSL *s, struct hm_header_st* msg_hdr, int *ok)
{
@@ -580,6 +714,12 @@ dtls1_process_out_of_seq_message(SSL *s, struct hm_header_st* msg_hdr, int *ok)
	seq64be[7] = (unsigned char) msg_hdr->seq;
	item = pqueue_find(s->d1->buffered_messages, seq64be);

	/* 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)
		item = NULL;

	/* Discard the message if sequence number was already there, is
	 * too far in the future, already in the queue or if we received
	 * a FINISHED before the SERVER_HELLO, which then must be a stale
@@ -600,20 +740,25 @@ dtls1_process_out_of_seq_message(SSL *s, struct hm_header_st* msg_hdr, int *ok)
			frag_len -= i;
			}
		}

	if (frag_len)
	else
		{
		frag = dtls1_hm_fragment_new(frag_len);
		if (frag_len && frag_len < msg_hdr->msg_len)
			return dtls1_reassemble_fragment(s, msg_hdr, ok);

		frag = dtls1_hm_fragment_new(frag_len, 0);
		if ( frag == NULL)
			goto err;

		memcpy(&(frag->msg_header), msg_hdr, sizeof(*msg_hdr));

		if (frag_len)
			{
			/* 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)
				goto err;
			}

		memset(seq64be,0,sizeof(seq64be));
		seq64be[6] = (unsigned char)(msg_hdr->seq>>8);
@@ -640,14 +785,14 @@ static long
dtls1_get_message_fragment(SSL *s, int st1, int stn, long max, int *ok)
	{
	unsigned char wire[DTLS1_HM_HEADER_LENGTH];
	unsigned long l, frag_off, frag_len;
	unsigned long len, frag_off, frag_len;
	int i,al;
	struct hm_header_st msg_hdr;

	/* see if we have the required fragment already */
	if ((frag_len = dtls1_retrieve_buffered_fragment(s,max,ok)) || *ok)
		{
		if (*ok)	s->init_num += frag_len;
		if (*ok)	s->init_num = frag_len;
		return frag_len;
		}

@@ -672,10 +817,13 @@ dtls1_get_message_fragment(SSL *s, int st1, int stn, long max, int *ok)
	if ( msg_hdr.seq != s->d1->handshake_read_seq)
		return dtls1_process_out_of_seq_message(s, &msg_hdr, ok);

	l = msg_hdr.msg_len;
	len = msg_hdr.msg_len;
	frag_off = msg_hdr.frag_off;
	frag_len = msg_hdr.frag_len;

	if (frag_len && frag_len < len)
		return dtls1_reassemble_fragment(s, &msg_hdr, ok);

	if (!s->server && s->d1->r_msg_hdr.frag_off == 0 &&
		wire[0] == SSL3_MT_HELLO_REQUEST)
		{
@@ -735,7 +883,7 @@ dtls1_get_message_fragment(SSL *s, int st1, int stn, long max, int *ok)
	 * s->init_buf->data, but as a counter summing up fragments'
	 * lengths: as soon as they sum up to handshake packet
	 * length, we assume we have got all the fragments. */
	s->init_num += frag_len;
	s->init_num = frag_len;
	return frag_len;

f_err:
@@ -1010,7 +1158,7 @@ dtls1_buffer_message(SSL *s, int is_ccs)
	 * been serialized */
	OPENSSL_assert(s->init_off == 0);

	frag = dtls1_hm_fragment_new(s->init_num);
	frag = dtls1_hm_fragment_new(s->init_num, 0);

	memcpy(frag->fragment, s->init_buf->data, s->init_num);

+1 −0
Original line number Diff line number Diff line
@@ -169,6 +169,7 @@ typedef struct hm_fragment_st
	{
	struct hm_header_st msg_header;
	unsigned char *fragment;
	unsigned char *reassembly;
	} hm_fragment;

typedef struct dtls1_state_st