diff options
Diffstat (limited to 'ssl/d1_both.c')
-rw-r--r-- | ssl/d1_both.c | 308 |
1 files changed, 139 insertions, 169 deletions
diff --git a/ssl/d1_both.c b/ssl/d1_both.c index ee4cbc9e..78f566e5 100644 --- a/ssl/d1_both.c +++ b/ssl/d1_both.c @@ -115,14 +115,12 @@ #include <assert.h> #include <limits.h> -#include <stdio.h> #include <string.h> #include <openssl/buf.h> #include <openssl/err.h> #include <openssl/evp.h> #include <openssl/mem.h> -#include <openssl/obj.h> #include <openssl/rand.h> #include <openssl/x509.h> @@ -251,12 +249,12 @@ static void dtls1_update_mtu(SSL *ssl) { /* TODO(davidben): What is this code doing and do we need it? */ if (ssl->d1->mtu < dtls1_min_mtu() && !(SSL_get_options(ssl) & SSL_OP_NO_QUERY_MTU)) { - long mtu = BIO_ctrl(SSL_get_wbio(ssl), BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL); + long mtu = BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL); if (mtu >= 0 && mtu <= (1 << 30) && (unsigned)mtu >= dtls1_min_mtu()) { ssl->d1->mtu = (unsigned)mtu; } else { ssl->d1->mtu = kDefaultMTU; - BIO_ctrl(SSL_get_wbio(ssl), BIO_CTRL_DGRAM_SET_MTU, ssl->d1->mtu, NULL); + BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_SET_MTU, ssl->d1->mtu, NULL); } } @@ -276,7 +274,7 @@ static size_t dtls1_max_record_size(SSL *ssl) { } ret -= overhead; - size_t pending = BIO_wpending(SSL_get_wbio(ssl)); + size_t pending = BIO_wpending(ssl->wbio); if (ret <= pending) { return 0; } @@ -292,28 +290,24 @@ static int dtls1_write_change_cipher_spec(SSL *ssl, /* During the handshake, wbio is buffered to pack messages together. Flush the * buffer if the ChangeCipherSpec would not fit in a packet. */ if (dtls1_max_record_size(ssl) == 0) { - ssl->rwstate = SSL_WRITING; - int ret = BIO_flush(SSL_get_wbio(ssl)); + int ret = BIO_flush(ssl->wbio); if (ret <= 0) { + ssl->rwstate = SSL_WRITING; return ret; } - ssl->rwstate = SSL_NOTHING; } static const uint8_t kChangeCipherSpec[1] = {SSL3_MT_CCS}; int ret = - dtls1_write_bytes(ssl, SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec, - sizeof(kChangeCipherSpec), use_epoch); + dtls1_write_record(ssl, SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec, + sizeof(kChangeCipherSpec), use_epoch); if (ret <= 0) { return ret; } - if (ssl->msg_callback != NULL) { - ssl->msg_callback(1 /* write */, ssl->version, SSL3_RT_CHANGE_CIPHER_SPEC, - kChangeCipherSpec, sizeof(kChangeCipherSpec), ssl, - ssl->msg_callback_arg); - } - + ssl_do_msg_callback(ssl, 1 /* write */, ssl->version, + SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec, + sizeof(kChangeCipherSpec)); return 1; } @@ -342,14 +336,13 @@ int dtls1_do_handshake_write(SSL *ssl, enum dtls1_use_epoch_t use_epoch) { /* During the handshake, wbio is buffered to pack messages together. Flush * the buffer if there isn't enough room to make progress. */ if (dtls1_max_record_size(ssl) < DTLS1_HM_HEADER_LENGTH + 1) { - ssl->rwstate = SSL_WRITING; - int flush_ret = BIO_flush(SSL_get_wbio(ssl)); + int flush_ret = BIO_flush(ssl->wbio); if (flush_ret <= 0) { + ssl->rwstate = SSL_WRITING; ret = flush_ret; goto err; } - ssl->rwstate = SSL_NOTHING; - assert(BIO_wpending(SSL_get_wbio(ssl)) == 0); + assert(BIO_wpending(ssl->wbio) == 0); } size_t todo = dtls1_max_record_size(ssl); @@ -382,8 +375,8 @@ int dtls1_do_handshake_write(SSL *ssl, enum dtls1_use_epoch_t use_epoch) { goto err; } - int write_ret = dtls1_write_bytes(ssl, SSL3_RT_HANDSHAKE, buf, len, - use_epoch); + int write_ret = + dtls1_write_record(ssl, SSL3_RT_HANDSHAKE, buf, len, use_epoch); if (write_ret <= 0) { ret = write_ret; goto err; @@ -392,11 +385,9 @@ int dtls1_do_handshake_write(SSL *ssl, enum dtls1_use_epoch_t use_epoch) { ssl->init_num -= todo; } while (ssl->init_num > 0); - if (ssl->msg_callback != NULL) { - ssl->msg_callback( - 1 /* write */, ssl->version, SSL3_RT_HANDSHAKE, ssl->init_buf->data, - (size_t)(ssl->init_off + ssl->init_num), ssl, ssl->msg_callback_arg); - } + ssl_do_msg_callback(ssl, 1 /* write */, ssl->version, SSL3_RT_HANDSHAKE, + ssl->init_buf->data, + (size_t)(ssl->init_off + ssl->init_num)); ssl->init_off = 0; ssl->init_num = 0; @@ -424,24 +415,6 @@ static int dtls1_is_next_message_complete(SSL *ssl) { frag->reassembly == NULL; } -/* 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 *ssl, 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 = dtls1_read_bytes(ssl, SSL3_RT_HANDSHAKE, discard, chunk, 0); - if (ret != (int) chunk) { - return 0; - } - frag_len -= chunk; - } - 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 @@ -487,93 +460,99 @@ static hm_fragment *dtls1_get_buffered_message( return frag; } -/* dtls1_max_handshake_message_len returns the maximum number of bytes - * permitted in a DTLS handshake message for |ssl|. The minimum is 16KB, but may - * be greater if the maximum certificate list size requires it. */ -static size_t dtls1_max_handshake_message_len(const SSL *ssl) { - size_t max_len = DTLS1_HM_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH; - if (max_len < ssl->max_cert_list) { - return ssl->max_cert_list; - } - return max_len; -} +/* dtls1_process_handshake_record reads a handshake record and processes it. It + * returns one if the record was successfully processed and 0 or -1 on error. */ +static int dtls1_process_handshake_record(SSL *ssl) { + SSL3_RECORD *rr = &ssl->s3->rrec; -/* 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 *ssl) { - /* Read handshake message header. */ - uint8_t header[DTLS1_HM_HEADER_LENGTH]; - int ret = dtls1_read_bytes(ssl, SSL3_RT_HANDSHAKE, header, - DTLS1_HM_HEADER_LENGTH, 0); - if (ret <= 0) { - return ret; +start: + if (rr->length == 0) { + int ret = dtls1_get_record(ssl); + if (ret <= 0) { + return ret; + } } - if (ret != DTLS1_HM_HEADER_LENGTH) { - OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE); + + /* Cross-epoch records are discarded, but we may receive out-of-order + * application data between ChangeCipherSpec and Finished or a ChangeCipherSpec + * before the appropriate point in the handshake. Those must be silently + * discarded. + * + * However, only allow the out-of-order records in the correct epoch. + * Application data must come in the encrypted epoch, and ChangeCipherSpec in + * the unencrypted epoch (we never renegotiate). Other cases fall through and + * fail with a fatal error. */ + if ((rr->type == SSL3_RT_APPLICATION_DATA && + ssl->s3->aead_read_ctx != NULL) || + (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC && + ssl->s3->aead_read_ctx == NULL)) { + rr->length = 0; + goto start; + } + + if (rr->type != SSL3_RT_HANDSHAKE) { ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); return -1; } - /* Parse the message fragment header. */ - struct hm_header_st msg_hdr; - dtls1_get_message_header(header, &msg_hdr); - - /* TODO(davidben): dtls1_read_bytes is the wrong abstraction for DTLS. There - * should be no need to reach into |ssl->s3->rrec.length|. */ - 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(ssl) || - frag_len > ssl->s3->rrec.length) { - OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE); - ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); - return -1; - } + CBS cbs; + CBS_init(&cbs, rr->data, rr->length); - if (msg_hdr.seq < ssl->d1->handshake_read_seq || - msg_hdr.seq > (unsigned)ssl->d1->handshake_read_seq + - kHandshakeBufferSize) { - /* Ignore fragments from the past, or ones too far in the future. */ - if (!dtls1_discard_fragment_body(ssl, frag_len)) { + while (CBS_len(&cbs) > 0) { + /* Read a handshake fragment. */ + struct hm_header_st msg_hdr; + CBS body; + if (!dtls1_parse_fragment(&cbs, &msg_hdr, &body)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_HANDSHAKE_RECORD); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); return -1; } - return 1; - } - hm_fragment *frag = dtls1_get_buffered_message(ssl, &msg_hdr); - if (frag == NULL) { - return -1; - } - assert(frag->msg_header.msg_len == msg_len); + 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 > ssl_max_handshake_message_len(ssl)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + return -1; + } - if (frag->reassembly == NULL) { - /* The message is already assembled. */ - if (!dtls1_discard_fragment_body(ssl, frag_len)) { + if (msg_hdr.seq < ssl->d1->handshake_read_seq || + msg_hdr.seq > + (unsigned)ssl->d1->handshake_read_seq + kHandshakeBufferSize) { + /* Ignore fragments from the past, or ones too far in the future. */ + continue; + } + + hm_fragment *frag = dtls1_get_buffered_message(ssl, &msg_hdr); + if (frag == NULL) { return -1; } - return 1; - } - assert(msg_len > 0); + assert(frag->msg_header.msg_len == msg_len); - /* Read the body of the fragment. */ - ret = dtls1_read_bytes(ssl, SSL3_RT_HANDSHAKE, frag->fragment + frag_off, - frag_len, 0); - if (ret != (int) frag_len) { - OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); - ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); - return -1; + if (frag->reassembly == NULL) { + /* The message is already assembled. */ + continue; + } + assert(msg_len > 0); + + /* Copy the body into the fragment. */ + memcpy(frag->fragment + frag_off, CBS_data(&body), CBS_len(&body)); + dtls1_hm_fragment_mark(frag, frag_off, frag_off + frag_len); } - dtls1_hm_fragment_mark(frag, frag_off, frag_off + frag_len); + rr->length = 0; + ssl_read_buffer_discard(ssl); return 1; } /* 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 *ssl, int st1, int stn, int msg_type, long max, + * if |msg_type| == -1). Read an entire handshake message. Handshake messages + * arrive in fragments. */ +long dtls1_get_message(SSL *ssl, int msg_type, enum ssl_hash_message_t hash_message, int *ok) { pitem *item = NULL; hm_fragment *frag = NULL; @@ -593,14 +572,15 @@ long dtls1_get_message(SSL *ssl, int st1, int stn, int msg_type, long max, goto f_err; } *ok = 1; + assert(ssl->init_buf->length >= DTLS1_HM_HEADER_LENGTH); ssl->init_msg = (uint8_t *)ssl->init_buf->data + DTLS1_HM_HEADER_LENGTH; - ssl->init_num = (int)ssl->s3->tmp.message_size; + ssl->init_num = (int)ssl->init_buf->length - DTLS1_HM_HEADER_LENGTH; return ssl->init_num; } - /* Process fragments until one is found. */ + /* Process handshake records until the next message is ready. */ while (!dtls1_is_next_message_complete(ssl)) { - int ret = dtls1_process_fragment(ssl); + int ret = dtls1_process_handshake_record(ssl); if (ret <= 0) { *ok = 0; return ret; @@ -614,17 +594,11 @@ long dtls1_get_message(SSL *ssl, int st1, int stn, int msg_type, long max, assert(ssl->d1->handshake_read_seq == frag->msg_header.seq); assert(frag->reassembly == NULL); - if (frag->msg_header.msg_len > (size_t)max) { - OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE); - goto err; - } - /* Reconstruct the assembled message. */ - size_t len; CBB cbb; CBB_zero(&cbb); - if (!BUF_MEM_grow(ssl->init_buf, (size_t)frag->msg_header.msg_len + - DTLS1_HM_HEADER_LENGTH) || + if (!BUF_MEM_reserve(ssl->init_buf, (size_t)frag->msg_header.msg_len + + DTLS1_HM_HEADER_LENGTH) || !CBB_init_fixed(&cbb, (uint8_t *)ssl->init_buf->data, ssl->init_buf->max) || !CBB_add_u8(&cbb, frag->msg_header.type) || @@ -633,19 +607,19 @@ long dtls1_get_message(SSL *ssl, int st1, int stn, int msg_type, long max, !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_finish(&cbb, NULL, &ssl->init_buf->length)) { CBB_cleanup(&cbb); OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } - assert(len == (size_t)frag->msg_header.msg_len + DTLS1_HM_HEADER_LENGTH); + assert(ssl->init_buf->length == + (size_t)frag->msg_header.msg_len + DTLS1_HM_HEADER_LENGTH); ssl->d1->handshake_read_seq++; /* TODO(davidben): This function has a lot of implicit outputs. Simplify the * |ssl_get_message| API. */ ssl->s3->tmp.message_type = frag->msg_header.type; - ssl->s3->tmp.message_size = frag->msg_header.msg_len; ssl->init_msg = (uint8_t *)ssl->init_buf->data + DTLS1_HM_HEADER_LENGTH; ssl->init_num = frag->msg_header.msg_len; @@ -657,16 +631,14 @@ long dtls1_get_message(SSL *ssl, int st1, int stn, int msg_type, long max, if (hash_message == ssl_hash_message && !ssl3_hash_current_message(ssl)) { goto err; } - if (ssl->msg_callback) { - ssl->msg_callback(0, ssl->version, SSL3_RT_HANDSHAKE, ssl->init_buf->data, - ssl->init_num + DTLS1_HM_HEADER_LENGTH, ssl, - ssl->msg_callback_arg); - } + + ssl_do_msg_callback(ssl, 0 /* read */, ssl->version, SSL3_RT_HANDSHAKE, + ssl->init_buf->data, + ssl->init_num + DTLS1_HM_HEADER_LENGTH); pitem_free(item); dtls1_hm_fragment_free(frag); - ssl->state = stn; *ok = 1; return ssl->init_num; @@ -679,27 +651,6 @@ err: return -1; } -int dtls1_read_failed(SSL *ssl, int code) { - if (code > 0) { - assert(0); - return 1; - } - - if (!dtls1_is_timer_expired(ssl)) { - /* not a timeout, none of our business, let higher layers handle this. In - * fact, it's probably an error */ - return code; - } - - if (!SSL_in_init(ssl)) { - /* done, no need to send a retransmit */ - BIO_set_flags(SSL_get_rbio(ssl), BIO_FLAGS_READ); - return code; - } - - return DTLSv1_handle_timeout(ssl); -} - static uint16_t dtls1_get_queue_priority(uint16_t seq, int is_ccs) { assert(seq * 2 >= seq); @@ -741,25 +692,39 @@ static int dtls1_retransmit_message(SSL *ssl, hm_fragment *frag) { ret = dtls1_do_handshake_write(ssl, use_epoch); } - /* TODO(davidben): Check return value? */ - (void)BIO_flush(SSL_get_wbio(ssl)); return ret; } - int dtls1_retransmit_buffered_messages(SSL *ssl) { - pqueue sent = ssl->d1->sent_messages; - piterator iter = pqueue_iterator(sent); - pitem *item; + /* Ensure we are packing handshake messages. */ + const int was_buffered = ssl_is_wbio_buffered(ssl); + assert(was_buffered == SSL_in_init(ssl)); + if (!was_buffered && !ssl_init_wbio_buffer(ssl)) { + return -1; + } + assert(ssl_is_wbio_buffered(ssl)); + int ret = -1; + piterator iter = pqueue_iterator(ssl->d1->sent_messages); + pitem *item; for (item = pqueue_next(&iter); item != NULL; item = pqueue_next(&iter)) { hm_fragment *frag = (hm_fragment *)item->data; if (dtls1_retransmit_message(ssl, frag) <= 0) { - return -1; + goto err; } } - return 1; + ret = BIO_flush(ssl->wbio); + if (ret <= 0) { + ssl->rwstate = SSL_WRITING; + goto err; + } + +err: + if (!was_buffered) { + ssl_free_wbio_buffer(ssl); + } + return ret; } /* dtls1_buffer_change_cipher_spec adds a ChangeCipherSpec to the current @@ -869,13 +834,18 @@ unsigned int dtls1_min_mtu(void) { return kMinMTU; } -void dtls1_get_message_header(uint8_t *data, - struct hm_header_st *msg_hdr) { - memset(msg_hdr, 0x00, sizeof(struct hm_header_st)); - msg_hdr->type = *(data++); - n2l3(data, msg_hdr->msg_len); +int dtls1_parse_fragment(CBS *cbs, struct hm_header_st *out_hdr, + CBS *out_body) { + memset(out_hdr, 0x00, sizeof(struct hm_header_st)); - n2s(data, msg_hdr->seq); - n2l3(data, msg_hdr->frag_off); - n2l3(data, msg_hdr->frag_len); + if (!CBS_get_u8(cbs, &out_hdr->type) || + !CBS_get_u24(cbs, &out_hdr->msg_len) || + !CBS_get_u16(cbs, &out_hdr->seq) || + !CBS_get_u24(cbs, &out_hdr->frag_off) || + !CBS_get_u24(cbs, &out_hdr->frag_len) || + !CBS_get_bytes(cbs, out_body, out_hdr->frag_len)) { + return 0; + } + + return 1; } |