diff options
Diffstat (limited to 'src/ssl/d1_both.c')
-rw-r--r-- | src/ssl/d1_both.c | 962 |
1 files changed, 357 insertions, 605 deletions
diff --git a/src/ssl/d1_both.c b/src/ssl/d1_both.c index 5edc93f..662f518 100644 --- a/src/ssl/d1_both.c +++ b/src/ssl/d1_both.c @@ -124,44 +124,8 @@ #include <openssl/rand.h> #include <openssl/x509.h> -#include "ssl_locl.h" - -#define RSMBLY_BITMASK_SIZE(msg_len) (((msg_len) + 7) / 8) - -#define RSMBLY_BITMASK_MARK(bitmask, start, end) \ - { \ - if ((end) - (start) <= 8) { \ - long ii; \ - for (ii = (start); ii < (end); ii++) \ - bitmask[((ii) >> 3)] |= (1 << ((ii)&7)); \ - } else { \ - long ii; \ - bitmask[((start) >> 3)] |= bitmask_start_values[((start)&7)]; \ - for (ii = (((start) >> 3) + 1); ii < ((((end)-1)) >> 3); ii++) \ - bitmask[ii] = 0xff; \ - bitmask[(((end)-1) >> 3)] |= bitmask_end_values[((end)&7)]; \ - } \ - } - -#define RSMBLY_BITMASK_IS_COMPLETE(bitmask, msg_len, is_complete) \ - { \ - long ii; \ - assert((msg_len) > 0); \ - is_complete = 1; \ - if (bitmask[(((msg_len)-1) >> 3)] != bitmask_end_values[((msg_len)&7)]) \ - is_complete = 0; \ - if (is_complete) \ - for (ii = (((msg_len)-1) >> 3) - 1; ii >= 0; ii--) \ - if (bitmask[ii] != 0xff) { \ - is_complete = 0; \ - break; \ - } \ - } +#include "internal.h" -static const uint8_t bitmask_start_values[] = {0xff, 0xfe, 0xfc, 0xf8, - 0xf0, 0xe0, 0xc0, 0x80}; -static const uint8_t bitmask_end_values[] = {0xff, 0x01, 0x03, 0x07, - 0x0f, 0x1f, 0x3f, 0x7f}; /* TODO(davidben): 28 comes from the size of IP + UDP header. Is this reasonable * for these values? Notably, why is kMinMTU a function of the transport @@ -175,25 +139,30 @@ static const unsigned int kMinMTU = 256 - 28; * the underlying BIO supplies one. */ static const unsigned int kDefaultMTU = 1500 - 28; +/* kMaxHandshakeBuffer is the maximum number of handshake messages ahead of the + * current one to buffer. */ +static const unsigned int kHandshakeBufferSize = 10; + 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, unsigned char *p); -static long dtls1_get_message_fragment(SSL *s, int stn, long max, int *ok); static hm_fragment *dtls1_hm_fragment_new(unsigned long frag_len, int reassembly) { hm_fragment *frag = NULL; - unsigned char *buf = NULL; - unsigned char *bitmask = NULL; + uint8_t *buf = NULL; + uint8_t *bitmask = NULL; frag = (hm_fragment *)OPENSSL_malloc(sizeof(hm_fragment)); if (frag == NULL) { + OPENSSL_PUT_ERROR(SSL, dtls1_hm_fragment_new, ERR_R_MALLOC_FAILURE); return NULL; } if (frag_len) { - buf = (unsigned char *)OPENSSL_malloc(frag_len); + buf = (uint8_t *)OPENSSL_malloc(frag_len); if (buf == NULL) { + OPENSSL_PUT_ERROR(SSL, dtls1_hm_fragment_new, ERR_R_MALLOC_FAILURE); OPENSSL_free(frag); return NULL; } @@ -203,16 +172,22 @@ static hm_fragment *dtls1_hm_fragment_new(unsigned long frag_len, frag->fragment = buf; /* Initialize reassembly bitmask if necessary */ - if (reassembly) { - bitmask = (unsigned char *)OPENSSL_malloc(RSMBLY_BITMASK_SIZE(frag_len)); + if (reassembly && frag_len > 0) { + if (frag_len + 7 < frag_len) { + OPENSSL_PUT_ERROR(SSL, dtls1_hm_fragment_new, ERR_R_OVERFLOW); + return NULL; + } + size_t bitmask_len = (frag_len + 7) / 8; + bitmask = (uint8_t *)OPENSSL_malloc(bitmask_len); if (bitmask == NULL) { + OPENSSL_PUT_ERROR(SSL, dtls1_hm_fragment_new, ERR_R_MALLOC_FAILURE); if (buf != NULL) { OPENSSL_free(buf); } OPENSSL_free(frag); return NULL; } - memset(bitmask, 0, RSMBLY_BITMASK_SIZE(frag_len)); + memset(bitmask, 0, bitmask_len); } frag->reassembly = bitmask; @@ -221,23 +196,65 @@ static hm_fragment *dtls1_hm_fragment_new(unsigned long frag_len, } void dtls1_hm_fragment_free(hm_fragment *frag) { - if (frag->msg_header.is_ccs) { - /* TODO(davidben): Simplify aead_write_ctx ownership, probably by just - * forbidding DTLS renego. */ - SSL_AEAD_CTX *aead_write_ctx = - frag->msg_header.saved_retransmit_state.aead_write_ctx; - if (aead_write_ctx) { - EVP_AEAD_CTX_cleanup(&aead_write_ctx->ctx); - OPENSSL_free(aead_write_ctx); + if (frag == NULL) { + return; + } + OPENSSL_free(frag->fragment); + OPENSSL_free(frag->reassembly); + OPENSSL_free(frag); +} + +#if !defined(inline) +#define inline __inline +#endif + +/* bit_range returns a |uint8_t| with bits |start|, inclusive, to |end|, + * exclusive, set. */ +static inline uint8_t bit_range(size_t start, size_t end) { + return (uint8_t)(~((1u << start) - 1) & ((1u << end) - 1)); +} + +/* dtls1_hm_fragment_mark marks bytes |start|, inclusive, to |end|, exclusive, + * as received in |frag|. If |frag| becomes complete, it clears + * |frag->reassembly|. The range must be within the bounds of |frag|'s message + * and |frag->reassembly| must not be NULL. */ +static void dtls1_hm_fragment_mark(hm_fragment *frag, size_t start, + size_t end) { + size_t i; + size_t msg_len = frag->msg_header.msg_len; + + if (frag->reassembly == NULL || start > end || end > msg_len) { + assert(0); + return; + } + /* A zero-length message will never have a pending reassembly. */ + assert(msg_len > 0); + + if ((start >> 3) == (end >> 3)) { + frag->reassembly[start >> 3] |= bit_range(start & 7, end & 7); + } else { + frag->reassembly[start >> 3] |= bit_range(start & 7, 8); + for (i = (start >> 3) + 1; i < (end >> 3); i++) { + frag->reassembly[i] = 0xff; + } + if ((end & 7) != 0) { + frag->reassembly[end >> 3] |= bit_range(0, end & 7); } } - if (frag->fragment) { - OPENSSL_free(frag->fragment); + + /* Check if the fragment is complete. */ + for (i = 0; i < (msg_len >> 3); i++) { + if (frag->reassembly[i] != 0xff) { + return; + } } - if (frag->reassembly) { - OPENSSL_free(frag->reassembly); + if ((msg_len & 7) != 0 && + frag->reassembly[msg_len >> 3] != bit_range(0, msg_len & 7)) { + return; } - OPENSSL_free(frag); + + OPENSSL_free(frag->reassembly); + frag->reassembly = NULL; } /* send s->init_buf in records of type 'type' (SSL3_RT_HANDSHAKE or @@ -368,514 +385,279 @@ int dtls1_do_write(SSL *s, int type) { return 0; } - -/* Obtain handshake message of message type 'mt' (any if mt == -1), maximum - * acceptable body length 'max'. Read an entire handshake message. Handshake - * messages arrive in fragments. */ -long dtls1_get_message(SSL *s, int st1, int stn, int mt, long max, - int hash_message, int *ok) { - int i, al; - struct hm_header_st *msg_hdr; - uint8_t *p; - unsigned long msg_len; - - /* s3->tmp is used to store messages that are unexpected, caused - * by the absence of an optional handshake message */ - if (s->s3->tmp.reuse_message) { - /* A SSL_GET_MESSAGE_DONT_HASH_MESSAGE call cannot be combined - * with reuse_message; the SSL_GET_MESSAGE_DONT_HASH_MESSAGE - * would have to have been applied to the previous call. */ - assert(hash_message != SSL_GET_MESSAGE_DONT_HASH_MESSAGE); - s->s3->tmp.reuse_message = 0; - if (mt >= 0 && s->s3->tmp.message_type != mt) { - al = SSL_AD_UNEXPECTED_MESSAGE; - OPENSSL_PUT_ERROR(SSL, dtls1_get_message, SSL_R_UNEXPECTED_MESSAGE); - goto f_err; - } - *ok = 1; - s->init_msg = (uint8_t *)s->init_buf->data + DTLS1_HM_HEADER_LENGTH; - s->init_num = (int)s->s3->tmp.message_size; - return s->init_num; - } - - msg_hdr = &s->d1->r_msg_hdr; - memset(msg_hdr, 0x00, sizeof(struct hm_header_st)); - -again: - i = dtls1_get_message_fragment(s, stn, max, ok); - if (i == DTLS1_HM_BAD_FRAGMENT || - i == DTLS1_HM_FRAGMENT_RETRY) { - /* bad fragment received */ - goto again; - } else if (i <= 0 && !*ok) { - return i; - } - - p = (uint8_t *)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); - l2n3(0, p); - l2n3(msg_len, p); - p -= DTLS1_HM_HEADER_LENGTH; - msg_len += DTLS1_HM_HEADER_LENGTH; - - s->init_msg = (uint8_t *)s->init_buf->data + DTLS1_HM_HEADER_LENGTH; - - if (hash_message != SSL_GET_MESSAGE_DONT_HASH_MESSAGE) { - ssl3_hash_current_message(s); - } - if (s->msg_callback) { - s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, p, msg_len, s, - s->msg_callback_arg); +/* dtls1_is_next_message_complete returns one if the next handshake message is + * complete and zero otherwise. */ +static int dtls1_is_next_message_complete(SSL *s) { + pitem *item = pqueue_peek(s->d1->buffered_messages); + if (item == NULL) { + return 0; } - memset(msg_hdr, 0x00, sizeof(struct hm_header_st)); - - s->d1->handshake_read_seq++; + hm_fragment *frag = (hm_fragment *)item->data; + assert(s->d1->handshake_read_seq <= frag->msg_header.seq); - return s->init_num; - -f_err: - ssl3_send_alert(s, SSL3_AL_FATAL, al); - *ok = 0; - return -1; + return s->d1->handshake_read_seq == frag->msg_header.seq && + frag->reassembly == NULL; } -static int dtls1_preprocess_fragment(SSL *s, struct hm_header_st *msg_hdr, - int max) { - size_t frag_off, frag_len, msg_len; - - msg_len = msg_hdr->msg_len; - frag_off = msg_hdr->frag_off; - frag_len = msg_hdr->frag_len; - - /* sanity checking */ - if ((frag_off + frag_len) > msg_len) { - OPENSSL_PUT_ERROR(SSL, dtls1_preprocess_fragment, - SSL_R_EXCESSIVE_MESSAGE_SIZE); - return SSL_AD_ILLEGAL_PARAMETER; - } - - if ((frag_off + frag_len) > (unsigned long)max) { - OPENSSL_PUT_ERROR(SSL, dtls1_preprocess_fragment, - SSL_R_EXCESSIVE_MESSAGE_SIZE); - return SSL_AD_ILLEGAL_PARAMETER; - } - - if (s->d1->r_msg_hdr.frag_off == 0) { - /* first fragment */ - /* msg_len is limited to 2^24, but is effectively checked - * against max above */ - if (!BUF_MEM_grow_clean(s->init_buf, msg_len + DTLS1_HM_HEADER_LENGTH)) { - OPENSSL_PUT_ERROR(SSL, dtls1_preprocess_fragment, ERR_R_BUF_LIB); - return SSL_AD_INTERNAL_ERROR; +/* dtls1_discard_fragment_body discards a handshake fragment body of length + * |frag_len|. It returns one on success and zero on error. + * + * TODO(davidben): This function will go away when ssl_read_bytes is gone from + * the DTLS side. */ +static int dtls1_discard_fragment_body(SSL *s, size_t frag_len) { + uint8_t discard[256]; + while (frag_len > 0) { + size_t chunk = frag_len < sizeof(discard) ? frag_len : sizeof(discard); + int ret = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, discard, chunk, + 0); + if (ret != chunk) { + return 0; } - - s->s3->tmp.message_size = msg_len; - s->d1->r_msg_hdr.msg_len = msg_len; - s->s3->tmp.message_type = msg_hdr->type; - s->d1->r_msg_hdr.type = msg_hdr->type; - s->d1->r_msg_hdr.seq = msg_hdr->seq; - } else if (msg_len != s->d1->r_msg_hdr.msg_len) { - /* They must be playing with us! BTW, failure to enforce - * upper limit would open possibility for buffer overrun. */ - OPENSSL_PUT_ERROR(SSL, dtls1_preprocess_fragment, - SSL_R_EXCESSIVE_MESSAGE_SIZE); - return SSL_AD_ILLEGAL_PARAMETER; + frag_len -= chunk; } - - return 0; /* no error */ + return 1; } +/* dtls1_get_buffered_message returns the buffered message corresponding to + * |msg_hdr|. If none exists, it creates a new one and inserts it in the + * queue. Otherwise, it checks |msg_hdr| is consistent with the existing one. It + * returns NULL on failure. The caller does not take ownership of the result. */ +static hm_fragment *dtls1_get_buffered_message( + SSL *s, const struct hm_header_st *msg_hdr) { + uint8_t seq64be[8]; + memset(seq64be, 0, sizeof(seq64be)); + seq64be[6] = (uint8_t)(msg_hdr->seq >> 8); + seq64be[7] = (uint8_t)msg_hdr->seq; + pitem *item = pqueue_find(s->d1->buffered_messages, seq64be); -static int dtls1_retrieve_buffered_fragment(SSL *s, long max, int *ok) { - /* (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 */ - pitem *item; hm_fragment *frag; - int al; - unsigned long frag_len; - - *ok = 0; - item = pqueue_peek(s->d1->buffered_messages); if (item == NULL) { - return 0; - } - - 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) { - return 0; - } - - frag_len = frag->msg_header.frag_len; - pqueue_pop(s->d1->buffered_messages); - - al = dtls1_preprocess_fragment(s, &frag->msg_header, max); - - if (al == 0) { - /* no alert */ - uint8_t *p = (uint8_t *)s->init_buf->data + DTLS1_HM_HEADER_LENGTH; - memcpy(&p[frag->msg_header.frag_off], frag->fragment, - frag->msg_header.frag_len); - } - - dtls1_hm_fragment_free(frag); - pitem_free(item); - - if (al == 0) { - *ok = 1; - return frag_len; + /* This is the first fragment from this message. */ + frag = dtls1_hm_fragment_new(msg_hdr->msg_len, + 1 /* reassembly buffer needed */); + if (frag == NULL) { + return NULL; + } + memcpy(&frag->msg_header, msg_hdr, sizeof(*msg_hdr)); + item = pitem_new(seq64be, frag); + if (item == NULL) { + dtls1_hm_fragment_free(frag); + return NULL; + } + item = pqueue_insert(s->d1->buffered_messages, item); + /* |pqueue_insert| fails iff a duplicate item is inserted, but |item| cannot + * be a duplicate. */ + assert(item != NULL); + } else { + frag = item->data; + assert(frag->msg_header.seq == msg_hdr->seq); + if (frag->msg_header.type != msg_hdr->type || + frag->msg_header.msg_len != msg_hdr->msg_len) { + /* The new fragment must be compatible with the previous fragments from + * this message. */ + OPENSSL_PUT_ERROR(SSL, dtls1_get_buffered_message, + SSL_R_FRAGMENT_MISMATCH); + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + return NULL; + } } - - ssl3_send_alert(s, SSL3_AL_FATAL, al); - s->init_num = 0; - *ok = 0; - return -1; + return frag; } /* 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) { +static size_t dtls1_max_handshake_message_len(const SSL *s) { + size_t max_len = DTLS1_HM_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH; + if (max_len < s->max_cert_list) { return s->max_cert_list; } return max_len; } -static int 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; - uint8_t seq64be[8]; - unsigned long frag_len = msg_hdr->frag_len; - - if ((msg_hdr->frag_off + frag_len) > msg_hdr->msg_len || - msg_hdr->msg_len > dtls1_max_handshake_message_len(s)) { - goto err; +/* dtls1_process_fragment reads a handshake fragment and processes it. It + * returns one if a fragment was successfully processed and 0 or -1 on error. */ +static int dtls1_process_fragment(SSL *s) { + /* Read handshake message header. + * + * TODO(davidben): ssl_read_bytes allows splitting the fragment header and + * body across two records. Change this interface to consume the fragment in + * one pass. */ + uint8_t header[DTLS1_HM_HEADER_LENGTH]; + int ret = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, header, + DTLS1_HM_HEADER_LENGTH, 0); + if (ret <= 0) { + return ret; + } + if (ret != DTLS1_HM_HEADER_LENGTH) { + OPENSSL_PUT_ERROR(SSL, dtls1_process_fragment, SSL_R_UNEXPECTED_MESSAGE); + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + return -1; + } + + /* Parse the message fragment header. */ + struct hm_header_st msg_hdr; + dtls1_get_message_header(header, &msg_hdr); + + const size_t frag_off = msg_hdr.frag_off; + const size_t frag_len = msg_hdr.frag_len; + const size_t msg_len = msg_hdr.msg_len; + if (frag_off > msg_len || frag_off + frag_len < frag_off || + frag_off + frag_len > msg_len || + msg_len > dtls1_max_handshake_message_len(s)) { + OPENSSL_PUT_ERROR(SSL, dtls1_process_fragment, + SSL_R_EXCESSIVE_MESSAGE_SIZE); + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + return -1; } - if (frag_len == 0) { - return DTLS1_HM_FRAGMENT_RETRY; + if (msg_hdr.seq < s->d1->handshake_read_seq || + msg_hdr.seq > (unsigned)s->d1->handshake_read_seq + + kHandshakeBufferSize) { + /* Ignore fragments from the past, or ones too far in the future. */ + if (!dtls1_discard_fragment_body(s, frag_len)) { + return -1; + } + return 1; } - /* Try to find item in queue */ - memset(seq64be, 0, sizeof(seq64be)); - seq64be[6] = (uint8_t)(msg_hdr->seq >> 8); - seq64be[7] = (uint8_t)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 (frag->msg_header.msg_len != msg_hdr->msg_len) { - item = NULL; - frag = NULL; - goto err; - } + hm_fragment *frag = dtls1_get_buffered_message(s, &msg_hdr); + if (frag == NULL) { + return -1; } + assert(frag->msg_header.msg_len == msg_len); - /* If message is already reassembled, this must be a - * retransmit and can be dropped. In this case item != NULL and so frag - * does not need to be freed. */ if (frag->reassembly == NULL) { - uint8_t devnull[256]; - - assert(item != NULL); - 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; + /* The message is already assembled. */ + if (!dtls1_discard_fragment_body(s, frag_len)) { + return -1; } - return DTLS1_HM_FRAGMENT_RETRY; + return 1; } + assert(msg_len > 0); - /* 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 ((unsigned long)i != frag_len) { - i = -1; - } - if (i <= 0) { - goto err; + /* Read the body of the fragment. */ + ret = s->method->ssl_read_bytes( + s, SSL3_RT_HANDSHAKE, frag->fragment + frag_off, frag_len, 0); + if (ret != frag_len) { + OPENSSL_PUT_ERROR(SSL, dtls1_process_fragment, SSL_R_UNEXPECTED_MESSAGE); + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + return -1; } + dtls1_hm_fragment_mark(frag, frag_off, frag_off + frag_len); - RSMBLY_BITMASK_MARK(frag->reassembly, (long)msg_hdr->frag_off, - (long)(msg_hdr->frag_off + frag_len)); - - RSMBLY_BITMASK_IS_COMPLETE(frag->reassembly, (long)msg_hdr->msg_len, - is_complete); + return 1; +} - if (is_complete) { - OPENSSL_free(frag->reassembly); - frag->reassembly = NULL; - } +/* dtls1_get_message reads a handshake message of message type |msg_type| (any + * if |msg_type| == -1), maximum acceptable body length |max|. Read an entire + * handshake message. Handshake messages arrive in fragments. */ +long dtls1_get_message(SSL *s, int st1, int stn, int msg_type, long max, + enum ssl_hash_message_t hash_message, int *ok) { + pitem *item = NULL; + hm_fragment *frag = NULL; + int al; - if (item == NULL) { - item = pitem_new(seq64be, frag); - if (item == NULL) { - i = -1; - goto err; + /* s3->tmp is used to store messages that are unexpected, caused + * by the absence of an optional handshake message */ + if (s->s3->tmp.reuse_message) { + /* A ssl_dont_hash_message call cannot be combined with reuse_message; the + * ssl_dont_hash_message would have to have been applied to the previous + * call. */ + assert(hash_message == ssl_hash_message); + s->s3->tmp.reuse_message = 0; + if (msg_type >= 0 && s->s3->tmp.message_type != msg_type) { + al = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, dtls1_get_message, SSL_R_UNEXPECTED_MESSAGE); + goto f_err; } - - 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. */ - assert(item != NULL); + *ok = 1; + s->init_msg = (uint8_t *)s->init_buf->data + DTLS1_HM_HEADER_LENGTH; + s->init_num = (int)s->s3->tmp.message_size; + return s->init_num; } - return DTLS1_HM_FRAGMENT_RETRY; - -err: - if (frag != NULL && item == NULL) { - dtls1_hm_fragment_free(frag); + /* Process fragments until one is found. */ + while (!dtls1_is_next_message_complete(s)) { + int ret = dtls1_process_fragment(s); + if (ret <= 0) { + *ok = 0; + return ret; + } } - *ok = 0; - return i; -} -static int dtls1_process_out_of_seq_message(SSL *s, - const struct hm_header_st *msg_hdr, - int *ok) { - int i = -1; - hm_fragment *frag = NULL; - pitem *item = NULL; - uint8_t seq64be[8]; - unsigned long frag_len = msg_hdr->frag_len; + /* Read out the next complete handshake message. */ + item = pqueue_pop(s->d1->buffered_messages); + assert(item != NULL); + frag = (hm_fragment *)item->data; + assert(s->d1->handshake_read_seq == frag->msg_header.seq); + assert(frag->reassembly == NULL); - if ((msg_hdr->frag_off + frag_len) > msg_hdr->msg_len) { + if (frag->msg_header.msg_len > (size_t)max) { + OPENSSL_PUT_ERROR(SSL, dtls1_get_message, SSL_R_EXCESSIVE_MESSAGE_SIZE); goto err; } - /* Try to find item in queue, to prevent duplicate entries */ - memset(seq64be, 0, sizeof(seq64be)); - seq64be[6] = (uint8_t)(msg_hdr->seq >> 8); - seq64be[7] = (uint8_t)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 - * retransmit. */ - if (msg_hdr->seq <= s->d1->handshake_read_seq || - msg_hdr->seq > s->d1->handshake_read_seq + 10 || item != NULL || - (s->d1->handshake_read_seq == 0 && msg_hdr->type == SSL3_MT_FINISHED)) { - uint8_t 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; - } - } else { - 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; - } - - 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 ((unsigned long)i != frag_len) { - i = -1; - } - if (i <= 0) { - goto err; - } - } - - item = pitem_new(seq64be, frag); - if (item == NULL) { - goto err; - } - - 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. */ - assert(item != NULL); + CBB cbb; + if (!BUF_MEM_grow(s->init_buf, + (size_t)frag->msg_header.msg_len + + DTLS1_HM_HEADER_LENGTH) || + !CBB_init_fixed(&cbb, (uint8_t *)s->init_buf->data, s->init_buf->max)) { + OPENSSL_PUT_ERROR(SSL, dtls1_get_message, ERR_R_MALLOC_FAILURE); + goto err; } - return DTLS1_HM_FRAGMENT_RETRY; - -err: - if (frag != NULL && item == NULL) { - dtls1_hm_fragment_free(frag); + /* Reconstruct the assembled message. */ + size_t len; + if (!CBB_add_u8(&cbb, frag->msg_header.type) || + !CBB_add_u24(&cbb, frag->msg_header.msg_len) || + !CBB_add_u16(&cbb, frag->msg_header.seq) || + !CBB_add_u24(&cbb, 0 /* frag_off */) || + !CBB_add_u24(&cbb, frag->msg_header.msg_len) || + !CBB_add_bytes(&cbb, frag->fragment, frag->msg_header.msg_len) || + !CBB_finish(&cbb, NULL, &len)) { + CBB_cleanup(&cbb); + OPENSSL_PUT_ERROR(SSL, dtls1_get_message, ERR_R_INTERNAL_ERROR); + goto err; } - *ok = 0; - return i; -} - + assert(len == (size_t)frag->msg_header.msg_len + DTLS1_HM_HEADER_LENGTH); -static long dtls1_get_message_fragment(SSL *s, int stn, long max, int *ok) { - uint8_t wire[DTLS1_HM_HEADER_LENGTH]; - unsigned long len, frag_off, frag_len; - int i, al; - struct hm_header_st msg_hdr; - -redo: - /* 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; - } - return frag_len; - } + s->d1->handshake_read_seq++; - /* read handshake message header */ - i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, wire, - DTLS1_HM_HEADER_LENGTH, 0); - if (i <= 0) { - /* nbio, or an error */ - s->rwstate = SSL_READING; - *ok = 0; - return i; - } + /* TODO(davidben): This function has a lot of implicit outputs. Simplify the + * |ssl_get_message| API. */ + s->s3->tmp.message_type = frag->msg_header.type; + s->s3->tmp.message_size = frag->msg_header.msg_len; + s->init_msg = (uint8_t *)s->init_buf->data + DTLS1_HM_HEADER_LENGTH; + s->init_num = frag->msg_header.msg_len; - /* Handshake fails if message header is incomplete */ - if (i != DTLS1_HM_HEADER_LENGTH) { + if (msg_type >= 0 && s->s3->tmp.message_type != msg_type) { al = SSL_AD_UNEXPECTED_MESSAGE; - OPENSSL_PUT_ERROR(SSL, dtls1_get_message_fragment, - SSL_R_UNEXPECTED_MESSAGE); + OPENSSL_PUT_ERROR(SSL, dtls1_get_message, SSL_R_UNEXPECTED_MESSAGE); goto f_err; } - - /* parse the message fragment header */ - dtls1_get_message_header(wire, &msg_hdr); - - /* if this is a future (or stale) message it gets buffered - * (or dropped)--no further processing at this time. */ - if (msg_hdr.seq != s->d1->handshake_read_seq) { - return dtls1_process_out_of_seq_message(s, &msg_hdr, ok); - } - - 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 (hash_message == ssl_hash_message && !ssl3_hash_current_message(s)) { + goto err; } - - if (!s->server && s->d1->r_msg_hdr.frag_off == 0 && - wire[0] == SSL3_MT_HELLO_REQUEST) { - /* The server may always send 'Hello Request' messages -- - * we are doing a handshake anyway now, so ignore them - * if their format is correct. Does not count for - * 'Finished' MAC. */ - if (wire[1] == 0 && wire[2] == 0 && wire[3] == 0) { - if (s->msg_callback) { - s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, wire, - DTLS1_HM_HEADER_LENGTH, s, s->msg_callback_arg); - } - - s->init_num = 0; - goto redo; - } else { - /* Incorrectly formated Hello request */ - al = SSL_AD_UNEXPECTED_MESSAGE; - OPENSSL_PUT_ERROR(SSL, dtls1_get_message_fragment, - SSL_R_UNEXPECTED_MESSAGE); - goto f_err; - } + if (s->msg_callback) { + s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, s->init_buf->data, + s->init_num + DTLS1_HM_HEADER_LENGTH, s, + s->msg_callback_arg); } - if ((al = dtls1_preprocess_fragment(s, &msg_hdr, max))) { - goto f_err; - } + pitem_free(item); + dtls1_hm_fragment_free(frag); - /* XDTLS: ressurect this when restart is in place */ s->state = stn; - - if (frag_len > 0) { - uint8_t *p = (uint8_t *)s->init_buf->data + DTLS1_HM_HEADER_LENGTH; - - i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, &p[frag_off], frag_len, - 0); - /* XDTLS: fix this--message fragments cannot span multiple packets */ - if (i <= 0) { - s->rwstate = SSL_READING; - *ok = 0; - return i; - } - } else { - i = 0; - } - - /* XDTLS: an incorrectly formatted fragment should cause the - * handshake to fail */ - if (i != (int)frag_len) { - al = SSL3_AD_ILLEGAL_PARAMETER; - OPENSSL_PUT_ERROR(SSL, dtls1_get_message_fragment, - SSL3_AD_ILLEGAL_PARAMETER); - goto f_err; - } - *ok = 1; - - /* Note that s->init_num is *not* used as current offset in - * 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; - return frag_len; + return s->init_num; f_err: ssl3_send_alert(s, SSL3_AL_FATAL, al); - s->init_num = 0; - +err: + pitem_free(item); + dtls1_hm_fragment_free(frag); *ok = 0; return -1; } @@ -913,7 +695,7 @@ int dtls1_send_change_cipher_spec(SSL *s, int a, int b) { int dtls1_read_failed(SSL *s, int code) { if (code > 0) { - fprintf(stderr, "invalid state reached %s:%d", __FILE__, __LINE__); + assert(0); return 1; } @@ -929,7 +711,7 @@ int dtls1_read_failed(SSL *s, int code) { return code; } - return dtls1_handle_timeout(s); + return DTLSv1_handle_timeout(s); } int dtls1_get_queue_priority(unsigned short seq, int is_ccs) { @@ -944,23 +726,82 @@ int dtls1_get_queue_priority(unsigned short seq, int is_ccs) { return seq * 2 - is_ccs; } +static int dtls1_retransmit_message(SSL *s, hm_fragment *frag) { + int ret; + /* XDTLS: for now assuming that read/writes are blocking */ + unsigned long header_length; + uint8_t save_write_sequence[8]; + + /* assert(s->init_num == 0); + assert(s->init_off == 0); */ + + if (frag->msg_header.is_ccs) { + header_length = DTLS1_CCS_HEADER_LENGTH; + } else { + header_length = DTLS1_HM_HEADER_LENGTH; + } + + memcpy(s->init_buf->data, frag->fragment, + frag->msg_header.msg_len + header_length); + s->init_num = frag->msg_header.msg_len + header_length; + + dtls1_set_message_header(s, frag->msg_header.type, + frag->msg_header.msg_len, frag->msg_header.seq, + 0, frag->msg_header.frag_len); + + /* Save current state. */ + SSL_AEAD_CTX *aead_write_ctx = s->aead_write_ctx; + uint16_t epoch = s->d1->w_epoch; + + /* DTLS renegotiation is unsupported, so only epochs 0 (NULL cipher) and 1 + * (negotiated cipher) exist. */ + assert(epoch == 0 || epoch == 1); + assert(frag->msg_header.epoch <= epoch); + const int fragment_from_previous_epoch = (epoch == 1 && + frag->msg_header.epoch == 0); + if (fragment_from_previous_epoch) { + /* Rewind to the previous epoch. + * + * TODO(davidben): Instead of swapping out connection-global state, this + * logic should pass a "use previous epoch" parameter down to lower-level + * functions. */ + s->d1->w_epoch = frag->msg_header.epoch; + s->aead_write_ctx = NULL; + memcpy(save_write_sequence, s->s3->write_sequence, + sizeof(s->s3->write_sequence)); + memcpy(s->s3->write_sequence, s->d1->last_write_sequence, + sizeof(s->s3->write_sequence)); + } else { + /* Otherwise the messages must be from the same epoch. */ + assert(frag->msg_header.epoch == epoch); + } + + ret = dtls1_do_write(s, frag->msg_header.is_ccs ? SSL3_RT_CHANGE_CIPHER_SPEC + : SSL3_RT_HANDSHAKE); + + if (fragment_from_previous_epoch) { + /* Restore the current epoch. */ + s->aead_write_ctx = aead_write_ctx; + s->d1->w_epoch = epoch; + memcpy(s->d1->last_write_sequence, s->s3->write_sequence, + sizeof(s->s3->write_sequence)); + memcpy(s->s3->write_sequence, save_write_sequence, + sizeof(s->s3->write_sequence)); + } + + (void)BIO_flush(SSL_get_wbio(s)); + return ret; +} + + int dtls1_retransmit_buffered_messages(SSL *s) { pqueue sent = s->d1->sent_messages; - piterator iter; + piterator iter = pqueue_iterator(sent); pitem *item; - hm_fragment *frag; - int found = 0; - - iter = pqueue_iterator(sent); for (item = pqueue_next(&iter); item != NULL; item = pqueue_next(&iter)) { - frag = (hm_fragment *)item->data; - if (dtls1_retransmit_message( - s, (unsigned short)dtls1_get_queue_priority( - frag->msg_header.seq, frag->msg_header.is_ccs), - 0, &found) <= 0 && - found) { - fprintf(stderr, "dtls1_retransmit_message() failed\n"); + hm_fragment *frag = (hm_fragment *)item->data; + if (dtls1_retransmit_message(s, frag) <= 0) { return -1; } } @@ -998,11 +839,7 @@ int dtls1_buffer_message(SSL *s, int is_ccs) { frag->msg_header.frag_off = 0; frag->msg_header.frag_len = s->d1->w_msg_hdr.msg_len; frag->msg_header.is_ccs = is_ccs; - - /* save current state*/ - frag->msg_header.saved_retransmit_state.aead_write_ctx = s->aead_write_ctx; - frag->msg_header.saved_retransmit_state.session = s->session; - frag->msg_header.saved_retransmit_state.epoch = s->d1->w_epoch; + frag->msg_header.epoch = s->d1->w_epoch; memset(seq64be, 0, sizeof(seq64be)); seq64be[6] = (uint8_t)( @@ -1021,85 +858,6 @@ int dtls1_buffer_message(SSL *s, int is_ccs) { return 1; } -int dtls1_retransmit_message(SSL *s, unsigned short seq, unsigned long frag_off, - int *found) { - int ret; - /* XDTLS: for now assuming that read/writes are blocking */ - pitem *item; - hm_fragment *frag; - unsigned long header_length; - uint8_t seq64be[8]; - struct dtls1_retransmit_state saved_state; - uint8_t save_write_sequence[8]; - - /* assert(s->init_num == 0); - assert(s->init_off == 0); */ - - /* XDTLS: the requested message ought to be found, otherwise error */ - memset(seq64be, 0, sizeof(seq64be)); - seq64be[6] = (uint8_t)(seq >> 8); - seq64be[7] = (uint8_t)seq; - - item = pqueue_find(s->d1->sent_messages, seq64be); - if (item == NULL) { - fprintf(stderr, "retransmit: message %d non-existant\n", seq); - *found = 0; - return 0; - } - - *found = 1; - frag = (hm_fragment *)item->data; - - if (frag->msg_header.is_ccs) { - header_length = DTLS1_CCS_HEADER_LENGTH; - } else { - header_length = DTLS1_HM_HEADER_LENGTH; - } - - memcpy(s->init_buf->data, frag->fragment, - frag->msg_header.msg_len + header_length); - s->init_num = frag->msg_header.msg_len + header_length; - - dtls1_set_message_header(s, frag->msg_header.type, - frag->msg_header.msg_len, frag->msg_header.seq, - 0, frag->msg_header.frag_len); - - /* save current state */ - saved_state.aead_write_ctx = s->aead_write_ctx; - saved_state.session = s->session; - saved_state.epoch = s->d1->w_epoch; - - /* restore state in which the message was originally sent */ - s->aead_write_ctx = frag->msg_header.saved_retransmit_state.aead_write_ctx; - s->session = frag->msg_header.saved_retransmit_state.session; - s->d1->w_epoch = frag->msg_header.saved_retransmit_state.epoch; - - if (frag->msg_header.saved_retransmit_state.epoch == saved_state.epoch - 1) { - memcpy(save_write_sequence, s->s3->write_sequence, - sizeof(s->s3->write_sequence)); - memcpy(s->s3->write_sequence, s->d1->last_write_sequence, - sizeof(s->s3->write_sequence)); - } - - ret = dtls1_do_write(s, frag->msg_header.is_ccs ? SSL3_RT_CHANGE_CIPHER_SPEC - : SSL3_RT_HANDSHAKE); - - /* restore current state */ - s->aead_write_ctx = saved_state.aead_write_ctx; - s->session = saved_state.session; - s->d1->w_epoch = saved_state.epoch; - - if (frag->msg_header.saved_retransmit_state.epoch == saved_state.epoch - 1) { - memcpy(s->d1->last_write_sequence, s->s3->write_sequence, - sizeof(s->s3->write_sequence)); - memcpy(s->s3->write_sequence, save_write_sequence, - sizeof(s->s3->write_sequence)); - } - - (void)BIO_flush(SSL_get_wbio(s)); - return ret; -} - /* call this function when the buffered messages are no longer needed */ void dtls1_clear_record_buffer(SSL *s) { pitem *item; @@ -1160,12 +918,6 @@ void dtls1_get_message_header(uint8_t *data, n2l3(data, msg_hdr->frag_len); } -void dtls1_get_ccs_header(uint8_t *data, struct ccs_header_st *ccs_hdr) { - memset(ccs_hdr, 0x00, sizeof(struct ccs_header_st)); - - ccs_hdr->type = *(data++); -} - int dtls1_shutdown(SSL *s) { int ret; ret = ssl3_shutdown(s); |