From 4e453d827da3ecb907960c52098600d1d0b60fc7 Mon Sep 17 00:00:00 2001 From: Benjamin Kaduk Date: Mon, 31 Aug 2020 12:27:33 -0700 Subject: QUIC: Buffer all provided quic data Make all data supplied via SSL_provide_quic_data() pass through an internal buffer, so that we can handle data supplied with arbitrary framing and only parse complete TLS records onto the list of QUIC_DATA managed by quic_input_data_head/quic_input_data_tail. This lets us remove the concept of "incomplete" QUIC_DATA structures, and the 'offset' field needed to support them. However, we've already moved the provided data onto the buffer by the time we can check for KeyUpdate messages, so defer that check to quic_get_message() (where it is adjacent to the preexisting ChangeCipherSpec check). To avoid extra memory copies, we also make the QUIC_DATA structures just store offsets into the consolidated buffer instead of having copies of the TLS handshake messages themselves. (cherry picked from commit 0bbcd60a1e7ff8b88c7cccda23d5ab0a0e423170) --- ssl/ssl_lib.c | 1 + ssl/ssl_local.h | 5 +-- ssl/ssl_quic.c | 75 +++++++++++++++++++--------------------- ssl/statem/statem_quic.c | 12 +++++-- 4 files changed, 50 insertions(+), 43 deletions(-) diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 9b2623bd9b..7c579307fb 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -1219,6 +1219,7 @@ void SSL_free(SSL *s) #ifndef OPENSSL_NO_QUIC OPENSSL_free(s->ext.quic_transport_params); OPENSSL_free(s->ext.peer_quic_transport_params); + BUF_MEM_free(s->quic_buf); while (s->quic_input_data_head != NULL) { QUIC_DATA *qd; diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h index 5271f55415..b2db990f76 100644 --- a/ssl/ssl_local.h +++ b/ssl/ssl_local.h @@ -1083,9 +1083,8 @@ typedef struct cert_pkey_st CERT_PKEY; struct quic_data_st { struct quic_data_st *next; OSSL_ENCRYPTION_LEVEL level; - size_t offset; + size_t start; /* offset into quic_buf->data */ size_t length; - /* char data[]; should be here but C90 VLAs not allowed here */ }; typedef struct quic_data_st QUIC_DATA; int quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL level); @@ -1407,8 +1406,10 @@ struct ssl_st { #ifndef OPENSSL_NO_QUIC OSSL_ENCRYPTION_LEVEL quic_read_level; OSSL_ENCRYPTION_LEVEL quic_write_level; + BUF_MEM *quic_buf; /* buffer incoming handshake messages */ QUIC_DATA *quic_input_data_head; QUIC_DATA *quic_input_data_tail; + size_t quic_next_record_start; const SSL_QUIC_METHOD *quic_method; #endif /* diff --git a/ssl/ssl_quic.c b/ssl/ssl_quic.c index c7b6f1f5c1..bc3c220067 100644 --- a/ssl/ssl_quic.c +++ b/ssl/ssl_quic.c @@ -91,8 +91,7 @@ OSSL_ENCRYPTION_LEVEL SSL_quic_write_level(const SSL *ssl) int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, const uint8_t *data, size_t len) { - size_t l; - uint8_t mt; + size_t l, offset; if (!SSL_IS_QUIC(ssl)) { SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); @@ -106,42 +105,46 @@ int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, return 0; } - /* Split on handshake message boundaries, if necessary */ - while (len > 0) { + if (ssl->quic_buf == NULL) { + BUF_MEM *buf; + if ((buf = BUF_MEM_new()) == NULL) { + SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, ERR_R_INTERNAL_ERROR); + return 0; + } + if (!BUF_MEM_grow(buf, SSL3_RT_MAX_PLAIN_LENGTH)) { + SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, ERR_R_INTERNAL_ERROR); + BUF_MEM_free(buf); + return 0; + } + ssl->quic_buf = buf; + /* We preallocated storage, but there's still no *data*. */ + ssl->quic_buf->length = 0; + buf = NULL; + } + + offset = ssl->quic_buf->length; + if (!BUF_MEM_grow(ssl->quic_buf, offset + len)) { + SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, ERR_R_INTERNAL_ERROR); + return 0; + } + memcpy(ssl->quic_buf->data + offset, data, len); + + /* Split on handshake message boundaries */ + while (ssl->quic_buf->length > ssl->quic_next_record_start + + SSL3_HM_HEADER_LENGTH) { QUIC_DATA *qd; const uint8_t *p; - /* Check for an incomplete block */ - qd = ssl->quic_input_data_tail; - if (qd != NULL) { - l = qd->length - qd->offset; - if (l != 0) { - /* we still need to copy `l` bytes into the last data block */ - if (l > len) - l = len; - memcpy((char*)(qd+1) + qd->offset, data, l); - qd->offset += l; - len -= l; - data += l; - continue; - } - } - - if (len < SSL3_HM_HEADER_LENGTH) { - SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, SSL_R_BAD_LENGTH); - return 0; - } /* TLS Handshake message header has 1-byte type and 3-byte length */ - mt = *data; - p = data + 1; + p = (const uint8_t *)ssl->quic_buf->data + + ssl->quic_next_record_start + 1; n2l3(p, l); l += SSL3_HM_HEADER_LENGTH; - if (mt == SSL3_MT_KEY_UPDATE) { - SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, SSL_R_UNEXPECTED_MESSAGE); - return 0; - } + /* Don't allocate a QUIC_DATA if we don't have a full record */ + if (l > ssl->quic_buf->length - ssl->quic_next_record_start) + break; - qd = OPENSSL_zalloc(sizeof(QUIC_DATA) + l); + qd = OPENSSL_zalloc(sizeof(*qd)); if (qd == NULL) { SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, ERR_R_INTERNAL_ERROR); return 0; @@ -149,21 +152,15 @@ int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, qd->next = NULL; qd->length = l; + qd->start = ssl->quic_next_record_start; qd->level = level; - /* partial data received? */ - if (l > len) - l = len; - qd->offset = l; - memcpy((void*)(qd + 1), data, l); if (ssl->quic_input_data_tail != NULL) ssl->quic_input_data_tail->next = qd; else ssl->quic_input_data_head = qd; ssl->quic_input_data_tail = qd; - - data += l; - len -= l; + ssl->quic_next_record_start += l; } return 1; diff --git a/ssl/statem/statem_quic.c b/ssl/statem/statem_quic.c index a2ba29337c..2843938ce2 100644 --- a/ssl/statem/statem_quic.c +++ b/ssl/statem/statem_quic.c @@ -17,7 +17,7 @@ int quic_get_message(SSL *s, int *mt, size_t *len) QUIC_DATA *qd = s->quic_input_data_head; uint8_t *p; - if (qd == NULL || (qd->length - qd->offset) != 0) { + if (qd == NULL) { s->rwstate = SSL_READING; *mt = *len = 0; return 0; @@ -46,7 +46,7 @@ int quic_get_message(SSL *s, int *mt, size_t *len) } /* Copy buffered data */ - memcpy(s->init_buf->data, (void*)(qd + 1), qd->length); + memcpy(s->init_buf->data, s->quic_buf->data + qd->start, qd->length); s->init_buf->length = qd->length; s->quic_input_data_head = qd->next; if (s->quic_input_data_head == NULL) @@ -67,6 +67,14 @@ int quic_get_message(SSL *s, int *mt, size_t *len) *len = 0; return 0; } + /* No KeyUpdate in QUIC */ + if (*mt == SSL3_MT_KEY_UPDATE) { + SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_F_QUIC_GET_MESSAGE, + SSL_R_UNEXPECTED_MESSAGE); + *len = 0; + return 0; + } + /* * If receiving Finished, record MAC of prior handshake messages for -- 2.35.3