From: Matt Caswell Date: Fri, 19 Aug 2022 15:54:09 +0000 (+0100) Subject: Create the write record layer method and object and use it X-Git-Tag: openssl-3.2.0-alpha1~2039 X-Git-Url: https://git.openssl.org/?a=commitdiff_plain;h=2b71b042202d11854801682d48ccf4e4e34cd5cf;p=openssl.git Create the write record layer method and object and use it Make sure we set the write record layer method and create the object where appropriate. Move the newly restructured writing code into the record layer object. For now we are cheating and still accessing the underlying SSL_CONNECTION object. This will be removed in subsequent commits. Reviewed-by: Hugo Landau Reviewed-by: Richard Levitte (Merged from https://github.com/openssl/openssl/pull/19198) --- diff --git a/ssl/record/methods/dtls_meth.c b/ssl/record/methods/dtls_meth.c index ac4f5f397b..c462dd13b7 100644 --- a/ssl/record/methods/dtls_meth.c +++ b/ssl/record/methods/dtls_meth.c @@ -700,8 +700,8 @@ const OSSL_RECORD_METHOD ossl_dtls_record_method = { tls_write_pending, tls_get_max_record_len, tls_get_max_records, - tls_write_records_tmp, - tls_retry_write_records_tmp, + tls_write_records, + tls_retry_write_records, tls_read_record, tls_release_record, tls_get_alert_code, diff --git a/ssl/record/methods/ktls_meth.c b/ssl/record/methods/ktls_meth.c index 2477b04e9e..d0db365c5b 100644 --- a/ssl/record/methods/ktls_meth.c +++ b/ssl/record/methods/ktls_meth.c @@ -545,7 +545,7 @@ const OSSL_RECORD_METHOD ossl_ktls_record_method = { tls_get_max_record_len, tls_get_max_records, tls_write_records, - tls_retry_write_records_tmp, + tls_retry_write_records, tls_read_record, tls_release_record, tls_get_alert_code, diff --git a/ssl/record/methods/recmethod_local.h b/ssl/record/methods/recmethod_local.h index 3dcfaa0f21..16f24b6d5e 100644 --- a/ssl/record/methods/recmethod_local.h +++ b/ssl/record/methods/recmethod_local.h @@ -275,10 +275,9 @@ size_t tls_app_data_pending(OSSL_RECORD_LAYER *rl); int tls_write_pending(OSSL_RECORD_LAYER *rl); size_t tls_get_max_record_len(OSSL_RECORD_LAYER *rl); size_t tls_get_max_records(OSSL_RECORD_LAYER *rl); -int tls_write_records_tmp(OSSL_RECORD_LAYER *rl, OSSL_RECORD_TEMPLATE **templates, - size_t numtempl, size_t allowance, size_t *sent); -int tls_retry_write_records_tmp(OSSL_RECORD_LAYER *rl, size_t allowance, - size_t *sent); +int tls_write_records(OSSL_RECORD_LAYER *rl, OSSL_RECORD_TEMPLATE *templates, + size_t numtempl); +int tls_retry_write_records(OSSL_RECORD_LAYER *rl); int tls_get_alert_code(OSSL_RECORD_LAYER *rl); int tls_set1_bio(OSSL_RECORD_LAYER *rl, BIO *bio); int tls_read_record(OSSL_RECORD_LAYER *rl, void **rechandle, int *rversion, diff --git a/ssl/record/methods/tls_common.c b/ssl/record/methods/tls_common.c index 56e04b1c4c..6a5b7c6696 100644 --- a/ssl/record/methods/tls_common.c +++ b/ssl/record/methods/tls_common.c @@ -1302,16 +1302,553 @@ size_t tls_get_max_records(OSSL_RECORD_LAYER *rl) return 0; } -int tls_write_records_tmp(OSSL_RECORD_LAYER *rl, OSSL_RECORD_TEMPLATE **templates, - size_t numtempl, size_t allowance, size_t *sent) +int tls_write_records(OSSL_RECORD_LAYER *rl, OSSL_RECORD_TEMPLATE *templates, + size_t numtempl) { - return 0; + WPACKET pkt[SSL_MAX_PIPELINES + 1]; + SSL3_RECORD wr[SSL_MAX_PIPELINES + 1]; + WPACKET *thispkt; + SSL3_RECORD *thiswr; + unsigned char *recordstart; + int i, mac_size, clear = 0; + int eivlen = 0; + size_t align = 0; + SSL3_BUFFER *wb; + SSL_SESSION *sess; + size_t totlen = 0, len, wpinited = 0; + size_t j, prefix = 0; + int using_ktls; + /* TODO(RECLAYER): REMOVE ME */ + SSL_CONNECTION *s = rl->cbarg; + SSL *ssl = SSL_CONNECTION_GET_SSL(s); + OSSL_RECORD_TEMPLATE prefixtempl; + OSSL_RECORD_TEMPLATE *thistempl; + + if (!ossl_assert(!RECORD_LAYER_write_pending(&s->rlayer))) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + goto err; + } + + /* If we have an alert to send, lets send it */ + if (s->s3.alert_dispatch) { + i = ssl->method->ssl_dispatch_alert(ssl); + if (i <= 0) { + /* SSLfatal() already called if appropriate */ + return i; + } + /* if it went, fall through and send more stuff */ + } + + sess = s->session; + + if ((sess == NULL) + || (s->enc_write_ctx == NULL) + || (EVP_MD_CTX_get0_md(s->write_hash) == NULL)) { + clear = s->enc_write_ctx ? 0 : 1; /* must be AEAD cipher */ + mac_size = 0; + } else { + mac_size = EVP_MD_CTX_get_size(s->write_hash); + if (mac_size < 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + goto err; + } + } + + /* + * 'create_empty_fragment' is true only when we have recursively called + * ourselves. + * Do we need to do that recursion in order to add an empty record prefix? + */ + prefix = s->s3.need_empty_fragments + && !clear + && !s->s3.empty_fragment_done + && templates[0].type == SSL3_RT_APPLICATION_DATA; + + if (s->rlayer.numwpipes < numtempl + prefix) { + /* + * TODO(RECLAYER): In the prefix case the first buffer can be a lot + * smaller. It is wasteful to allocate a full sized buffer here + */ + if (!ssl3_setup_write_buffer(s, numtempl + prefix, 0)) { + /* SSLfatal() already called */ + return -1; + } + } + + using_ktls = BIO_get_ktls_send(s->wbio); + if (!ossl_assert(!using_ktls || !prefix)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + goto err; + } + + if (prefix) { + /* + * countermeasure against known-IV weakness in CBC ciphersuites (see + * http://www.openssl.org/~bodo/tls-cbc.txt) + */ + prefixtempl.buf = NULL; + prefixtempl.buflen = 0; + prefixtempl.type = SSL3_RT_APPLICATION_DATA; + wpinited = 1; + + /* TODO(RECLAYER): Do we actually need this? */ + s->s3.empty_fragment_done = 1; + + wb = &s->rlayer.wbuf[0]; + /* TODO(RECLAYER): This alignment calculation no longer seems right */ +#if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD!=0 + /* + * extra fragment would be couple of cipher blocks, which would be + * multiple of SSL3_ALIGN_PAYLOAD, so if we want to align the real + * payload, then we can just pretend we simply have two headers. + */ + align = (size_t)SSL3_BUFFER_get_buf(wb) + 2 * SSL3_RT_HEADER_LENGTH; + align = SSL3_ALIGN_PAYLOAD - 1 - ((align - 1) % SSL3_ALIGN_PAYLOAD); +#endif + SSL3_BUFFER_set_offset(wb, align); + if (!WPACKET_init_static_len(&pkt[0], SSL3_BUFFER_get_buf(wb), + SSL3_BUFFER_get_len(wb), 0) + || !WPACKET_allocate_bytes(&pkt[0], align, NULL)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + goto err; + } + wpinited = 1; + } + for (j = 0; j < numtempl; j++) { + thispkt = &pkt[prefix + j]; + + wb = &s->rlayer.wbuf[prefix + j]; + wb->type = templates[j].type; + + if (using_ktls) { + /* + * ktls doesn't modify the buffer, but to avoid a warning we need + * to discard the const qualifier. + * This doesn't leak memory because the buffers have been + * released when switching to ktls. + */ + SSL3_BUFFER_set_buf(wb, (unsigned char *)templates[j].buf); + SSL3_BUFFER_set_offset(wb, 0); + SSL3_BUFFER_set_app_buffer(wb, 1); + } else { +#if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD != 0 + align = (size_t)SSL3_BUFFER_get_buf(wb) + SSL3_RT_HEADER_LENGTH; + align = SSL3_ALIGN_PAYLOAD - 1 + - ((align - 1) % SSL3_ALIGN_PAYLOAD); +#endif + /* TODO(RECLAYER): Is this alignment actually used somewhere? */ + SSL3_BUFFER_set_offset(wb, align); + if (!WPACKET_init_static_len(thispkt, SSL3_BUFFER_get_buf(wb), + SSL3_BUFFER_get_len(wb), 0) + || !WPACKET_allocate_bytes(thispkt, align, NULL)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + goto err; + } + wpinited++; + } + } + + if (!using_ktls) { + /* Explicit IV length, block ciphers appropriate version flag */ + if (s->enc_write_ctx && SSL_USE_EXPLICIT_IV(s) + && !SSL_CONNECTION_TREAT_AS_TLS13(s)) { + int mode = EVP_CIPHER_CTX_get_mode(s->enc_write_ctx); + if (mode == EVP_CIPH_CBC_MODE) { + eivlen = EVP_CIPHER_CTX_get_iv_length(s->enc_write_ctx); + if (eivlen < 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_LIBRARY_BUG); + goto err; + } + if (eivlen <= 1) + eivlen = 0; + } else if (mode == EVP_CIPH_GCM_MODE) { + /* Need explicit part of IV for GCM mode */ + eivlen = EVP_GCM_TLS_EXPLICIT_IV_LEN; + } else if (mode == EVP_CIPH_CCM_MODE) { + eivlen = EVP_CCM_TLS_EXPLICIT_IV_LEN; + } + } + } + + totlen = 0; + /* Clear our SSL3_RECORD structures */ + memset(wr, 0, sizeof(wr)); + for (j = 0; j < numtempl + prefix; j++) { + unsigned int version = (s->version == TLS1_3_VERSION) ? TLS1_2_VERSION + : s->version; + unsigned char *compressdata = NULL; + size_t maxcomplen; + unsigned int rectype; + + thispkt = &pkt[j]; + thiswr = &wr[j]; + thistempl = (j == 0 && prefix == 1) ? &prefixtempl : + &templates[j - prefix]; + + /* + * In TLSv1.3, once encrypting, we always use application data for the + * record type + */ + if (SSL_CONNECTION_TREAT_AS_TLS13(s) + && s->enc_write_ctx != NULL + && (s->statem.enc_write_state != ENC_WRITE_STATE_WRITE_PLAIN_ALERTS + || thistempl->type != SSL3_RT_ALERT)) + rectype = SSL3_RT_APPLICATION_DATA; + else + rectype = thistempl->type; + + SSL3_RECORD_set_type(thiswr, rectype); + + /* + * Some servers hang if initial client hello is larger than 256 bytes + * and record version number > TLS 1.0 + */ + if (SSL_get_state(ssl) == TLS_ST_CW_CLNT_HELLO + && !s->renegotiate + && TLS1_get_version(ssl) > TLS1_VERSION + && s->hello_retry_request == SSL_HRR_NONE) + version = TLS1_VERSION; + SSL3_RECORD_set_rec_version(thiswr, version); + + maxcomplen = thistempl->buflen; + if (s->compress != NULL) + maxcomplen += SSL3_RT_MAX_COMPRESSED_OVERHEAD; + + /* + * When using offload kernel will write the header. + * Otherwise write the header now + */ + if (!using_ktls + && (!WPACKET_put_bytes_u8(thispkt, rectype) + || !WPACKET_put_bytes_u16(thispkt, version) + || !WPACKET_start_sub_packet_u16(thispkt) + || (eivlen > 0 + && !WPACKET_allocate_bytes(thispkt, eivlen, NULL)) + || (maxcomplen > 0 + && !WPACKET_reserve_bytes(thispkt, maxcomplen, + &compressdata)))) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + goto err; + } + + /* lets setup the record stuff. */ + SSL3_RECORD_set_data(thiswr, compressdata); + SSL3_RECORD_set_length(thiswr, thistempl->buflen); + /* + * TODO(RECLAYER): Cast away the const. Should be safe - by why is this + * necessary? + */ + SSL3_RECORD_set_input(thiswr, (unsigned char *)thistempl->buf); + totlen += thistempl->buflen; + + /* + * we now 'read' from thiswr->input, thiswr->length bytes into + * thiswr->data + */ + + /* first we compress */ + if (s->compress != NULL) { + if (!ssl3_do_compress(s, thiswr) + || !WPACKET_allocate_bytes(thispkt, thiswr->length, NULL)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_COMPRESSION_FAILURE); + goto err; + } + } else { + if (using_ktls) { + SSL3_RECORD_reset_data(&wr[j]); + } else { + if (!WPACKET_memcpy(thispkt, thiswr->input, thiswr->length)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + goto err; + } + SSL3_RECORD_reset_input(&wr[j]); + } + } + + if (SSL_CONNECTION_TREAT_AS_TLS13(s) + && !using_ktls + && s->enc_write_ctx != NULL + && (s->statem.enc_write_state != ENC_WRITE_STATE_WRITE_PLAIN_ALERTS + || thistempl->type != SSL3_RT_ALERT)) { + size_t rlen, max_send_fragment; + + if (!WPACKET_put_bytes_u8(thispkt, thistempl->type)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + goto err; + } + SSL3_RECORD_add_length(thiswr, 1); + + /* Add TLS1.3 padding */ + max_send_fragment = ssl_get_max_send_fragment(s); + rlen = SSL3_RECORD_get_length(thiswr); + if (rlen < max_send_fragment) { + size_t padding = 0; + size_t max_padding = max_send_fragment - rlen; + if (s->record_padding_cb != NULL) { + padding = s->record_padding_cb(ssl, thistempl->type, rlen, + s->record_padding_arg); + } else if (s->block_padding > 0) { + size_t mask = s->block_padding - 1; + size_t remainder; + + /* optimize for power of 2 */ + if ((s->block_padding & mask) == 0) + remainder = rlen & mask; + else + remainder = rlen % s->block_padding; + /* don't want to add a block of padding if we don't have to */ + if (remainder == 0) + padding = 0; + else + padding = s->block_padding - remainder; + } + if (padding > 0) { + /* do not allow the record to exceed max plaintext length */ + if (padding > max_padding) + padding = max_padding; + if (!WPACKET_memset(thispkt, 0, padding)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + ERR_R_INTERNAL_ERROR); + goto err; + } + SSL3_RECORD_add_length(thiswr, padding); + } + } + } + + /* + * we should still have the output to thiswr->data and the input from + * wr->input. Length should be thiswr->length. thiswr->data still points + * in the wb->buf + */ + + if (!using_ktls && !SSL_WRITE_ETM(s) && mac_size != 0) { + unsigned char *mac; + + if (!WPACKET_allocate_bytes(thispkt, mac_size, &mac) + || !ssl->method->ssl3_enc->mac(s, thiswr, mac, 1)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + goto err; + } + } + + /* + * Reserve some bytes for any growth that may occur during encryption. + * This will be at most one cipher block or the tag length if using + * AEAD. SSL_RT_MAX_CIPHER_BLOCK_SIZE covers either case. + */ + if (!using_ktls) { + if (!WPACKET_reserve_bytes(thispkt, + SSL_RT_MAX_CIPHER_BLOCK_SIZE, + NULL) + /* + * We also need next the amount of bytes written to this + * sub-packet + */ + || !WPACKET_get_length(thispkt, &len)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + goto err; + } + + /* Get a pointer to the start of this record excluding header */ + recordstart = WPACKET_get_curr(thispkt) - len; + SSL3_RECORD_set_data(thiswr, recordstart); + SSL3_RECORD_reset_input(thiswr); + SSL3_RECORD_set_length(thiswr, len); + } + } + + if (s->statem.enc_write_state == ENC_WRITE_STATE_WRITE_PLAIN_ALERTS) { + /* + * We haven't actually negotiated the version yet, but we're trying to + * send early data - so we need to use the tls13enc function. + */ + if (tls13_enc(s, wr, numtempl, 1, NULL, mac_size) < 1) { + if (!ossl_statem_in_error(s)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + } + goto err; + } + } else { + if (!using_ktls) { + if (prefix) { + if (ssl->method->ssl3_enc->enc(s, wr, 1, 1, NULL, mac_size) < 1) { + if (!ossl_statem_in_error(s)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + } + goto err; + } + } + if (ssl->method->ssl3_enc->enc(s, wr + prefix, numtempl, 1, NULL, + mac_size) < 1) { + if (!ossl_statem_in_error(s)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + } + goto err; + } + } + } + + for (j = 0; j < prefix + numtempl; j++) { + size_t origlen; + + thispkt = &pkt[j]; + thiswr = &wr[j]; + thistempl = (prefix == 1 && j == 0) ? &prefixtempl + : &templates[j - prefix]; + + if (using_ktls) + goto mac_done; + + /* Allocate bytes for the encryption overhead */ + if (!WPACKET_get_length(thispkt, &origlen) + /* Encryption should never shrink the data! */ + || origlen > thiswr->length + || (thiswr->length > origlen + && !WPACKET_allocate_bytes(thispkt, + thiswr->length - origlen, + NULL))) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + goto err; + } + if (SSL_WRITE_ETM(s) && mac_size != 0) { + unsigned char *mac; + + if (!WPACKET_allocate_bytes(thispkt, mac_size, &mac) + || !ssl->method->ssl3_enc->mac(s, thiswr, mac, 1)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + goto err; + } + SSL3_RECORD_add_length(thiswr, mac_size); + } + + if (!WPACKET_get_length(thispkt, &len) + || !WPACKET_close(thispkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + goto err; + } + + if (s->msg_callback) { + recordstart = WPACKET_get_curr(thispkt) - len + - SSL3_RT_HEADER_LENGTH; + s->msg_callback(1, thiswr->rec_version, SSL3_RT_HEADER, recordstart, + SSL3_RT_HEADER_LENGTH, ssl, + s->msg_callback_arg); + + if (SSL_CONNECTION_TREAT_AS_TLS13(s) && s->enc_write_ctx != NULL) { + unsigned char ctype = thistempl->type; + + s->msg_callback(1, thiswr->rec_version, SSL3_RT_INNER_CONTENT_TYPE, + &ctype, 1, ssl, s->msg_callback_arg); + } + } + + if (!WPACKET_finish(thispkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + goto err; + } + + /* header is added by the kernel when using offload */ + SSL3_RECORD_add_length(thiswr, SSL3_RT_HEADER_LENGTH); + + mac_done: + /* + * we should now have thiswr->data pointing to the encrypted data, which + * is thiswr->length long. + * Setting the type is not needed but helps for debugging + */ + SSL3_RECORD_set_type(thiswr, thistempl->type); + + /* now let's set up wb */ + SSL3_BUFFER_set_left(&s->rlayer.wbuf[j], SSL3_RECORD_get_length(thiswr)); + } + + /* we now just need to write the buffers */ + return tls_retry_write_records(rl); + err: + for (j = 0; j < wpinited; j++) + WPACKET_cleanup(&pkt[j]); + return -1; } -int tls_retry_write_records_tmp(OSSL_RECORD_LAYER *rl, size_t allowance, - size_t *sent) +/* if SSL3_BUFFER_get_left() != 0, we need to call this + * + * Return values are as per SSL_write() + */ +int tls_retry_write_records(OSSL_RECORD_LAYER *rl) { - return 0; + int i; + SSL3_BUFFER *thiswb; + size_t currbuf = 0; + size_t tmpwrit = 0; + SSL_CONNECTION *s = rl->cbarg; + + for (;;) { + thiswb = &s->rlayer.wbuf[currbuf]; + /* Loop until we find a buffer we haven't written out yet */ + if (SSL3_BUFFER_get_left(thiswb) == 0 + && currbuf < s->rlayer.numwpipes - 1) { + currbuf++; + continue; + } + clear_sys_error(); + if (s->wbio != NULL) { + s->rwstate = SSL_WRITING; + + /* + * To prevent coalescing of control and data messages, + * such as in buffer_write, we flush the BIO + */ + if (BIO_get_ktls_send(s->wbio) + && thiswb->type != SSL3_RT_APPLICATION_DATA) { + i = BIO_flush(s->wbio); + if (i <= 0) + return i; + BIO_set_ktls_ctrl_msg(s->wbio, thiswb->type); + } + i = BIO_write(s->wbio, (char *) + &(SSL3_BUFFER_get_buf(thiswb) + [SSL3_BUFFER_get_offset(thiswb)]), + (unsigned int)SSL3_BUFFER_get_left(thiswb)); + if (i >= 0) + tmpwrit = i; + } else { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BIO_NOT_SET); + i = -1; + } + + /* + * When an empty fragment is sent on a connection using KTLS, + * it is sent as a write of zero bytes. If this zero byte + * write succeeds, i will be 0 rather than a non-zero value. + * Treat i == 0 as success rather than an error for zero byte + * writes to permit this case. + */ + if (i >= 0 && tmpwrit == SSL3_BUFFER_get_left(thiswb)) { + SSL3_BUFFER_set_left(thiswb, 0); + SSL3_BUFFER_add_offset(thiswb, tmpwrit); + if (currbuf + 1 < s->rlayer.numwpipes) + continue; + s->rwstate = SSL_NOTHING; + /* + * Next chunk of data should get another prepended empty fragment + * in ciphersuites with known-IV weakness: + */ + s->s3.empty_fragment_done = 0; + return 1; + } else if (i <= 0) { + if (SSL_CONNECTION_IS_DTLS(s)) { + /* + * For DTLS, just drop it. That's kind of the whole point in + * using a datagram service + */ + SSL3_BUFFER_set_left(thiswb, 0); + } + return i; + } + SSL3_BUFFER_add_offset(thiswb, tmpwrit); + SSL3_BUFFER_sub_left(thiswb, tmpwrit); + } } int tls_get_alert_code(OSSL_RECORD_LAYER *rl) @@ -1394,8 +1931,8 @@ const OSSL_RECORD_METHOD ossl_tls_record_method = { tls_write_pending, tls_get_max_record_len, tls_get_max_records, - tls_write_records_tmp, - tls_retry_write_records_tmp, + tls_write_records, + tls_retry_write_records, tls_read_record, tls_release_record, tls_get_alert_code, diff --git a/ssl/record/rec_layer_s3.c b/ssl/record/rec_layer_s3.c index c2e6e91e41..e6439ccda9 100644 --- a/ssl/record/rec_layer_s3.c +++ b/ssl/record/rec_layer_s3.c @@ -48,9 +48,14 @@ void RECORD_LAYER_clear(RECORD_LAYER *rl) if (rl->rrlmethod != NULL) rl->rrlmethod->free(rl->rrl); /* Ignore return value */ + if (rl->wrlmethod != NULL) + rl->wrlmethod->free(rl->wrl); /* Ignore return value */ BIO_free(rl->rrlnext); rl->rrlmethod = NULL; + rl->wrlmethod = NULL; rl->rrlnext = NULL; + rl->rrl = NULL; + rl->wrl = NULL; if (rl->d) DTLS_RECORD_LAYER_clear(rl); @@ -258,7 +263,7 @@ int ssl3_write_bytes(SSL *ssl, int type, const void *buf_, size_t len, return i; } else if (i > 0) { /* Retry needed */ - i = tls_retry_write_records(s); + i = s->rlayer.wrlmethod->retry_write_records(s->rlayer.wrl); if (i <= 0) return i; tot += s->rlayer.wpend_tot; @@ -510,7 +515,7 @@ int ssl3_write_bytes(SSL *ssl, int type, const void *buf_, size_t len, s->rlayer.wpend_tot = n; } - i = tls_write_records(s, tmpls, numpipes); + i = s->rlayer.wrlmethod->write_records(s->rlayer.wrl, tmpls, numpipes); if (i <= 0) { /* SSLfatal() already called if appropriate */ s->rlayer.wnum = tot; @@ -535,552 +540,6 @@ int ssl3_write_bytes(SSL *ssl, int type, const void *buf_, size_t len, } } -int tls_write_records(SSL_CONNECTION *s, OSSL_RECORD_TEMPLATE *templates, - size_t numtempl) -{ - WPACKET pkt[SSL_MAX_PIPELINES + 1]; - SSL3_RECORD wr[SSL_MAX_PIPELINES + 1]; - WPACKET *thispkt; - SSL3_RECORD *thiswr; - unsigned char *recordstart; - int i, mac_size, clear = 0; - int eivlen = 0; - size_t align = 0; - SSL3_BUFFER *wb; - SSL_SESSION *sess; - size_t totlen = 0, len, wpinited = 0; - size_t j, prefix = 0; - int using_ktls; - SSL *ssl = SSL_CONNECTION_GET_SSL(s); - OSSL_RECORD_TEMPLATE prefixtempl; - OSSL_RECORD_TEMPLATE *thistempl; - - if (!ossl_assert(!RECORD_LAYER_write_pending(&s->rlayer))) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); - goto err; - } - - /* If we have an alert to send, lets send it */ - if (s->s3.alert_dispatch) { - i = ssl->method->ssl_dispatch_alert(ssl); - if (i <= 0) { - /* SSLfatal() already called if appropriate */ - return i; - } - /* if it went, fall through and send more stuff */ - } - - sess = s->session; - - if ((sess == NULL) - || (s->enc_write_ctx == NULL) - || (EVP_MD_CTX_get0_md(s->write_hash) == NULL)) { - clear = s->enc_write_ctx ? 0 : 1; /* must be AEAD cipher */ - mac_size = 0; - } else { - mac_size = EVP_MD_CTX_get_size(s->write_hash); - if (mac_size < 0) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); - goto err; - } - } - - /* - * 'create_empty_fragment' is true only when we have recursively called - * ourselves. - * Do we need to do that recursion in order to add an empty record prefix? - */ - prefix = s->s3.need_empty_fragments - && !clear - && !s->s3.empty_fragment_done - && templates[0].type == SSL3_RT_APPLICATION_DATA; - - if (s->rlayer.numwpipes < numtempl + prefix) { - /* - * TODO(RECLAYER): In the prefix case the first buffer can be a lot - * smaller. It is wasteful to allocate a full sized buffer here - */ - if (!ssl3_setup_write_buffer(s, numtempl + prefix, 0)) { - /* SSLfatal() already called */ - return -1; - } - } - - using_ktls = BIO_get_ktls_send(s->wbio); - if (!ossl_assert(!using_ktls || !prefix)) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); - goto err; - } - - if (prefix) { - /* - * countermeasure against known-IV weakness in CBC ciphersuites (see - * http://www.openssl.org/~bodo/tls-cbc.txt) - */ - prefixtempl.buf = NULL; - prefixtempl.buflen = 0; - prefixtempl.type = SSL3_RT_APPLICATION_DATA; - wpinited = 1; - - /* TODO(RECLAYER): Do we actually need this? */ - s->s3.empty_fragment_done = 1; - - wb = &s->rlayer.wbuf[0]; - /* TODO(RECLAYER): This alignment calculation no longer seems right */ -#if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD!=0 - /* - * extra fragment would be couple of cipher blocks, which would be - * multiple of SSL3_ALIGN_PAYLOAD, so if we want to align the real - * payload, then we can just pretend we simply have two headers. - */ - align = (size_t)SSL3_BUFFER_get_buf(wb) + 2 * SSL3_RT_HEADER_LENGTH; - align = SSL3_ALIGN_PAYLOAD - 1 - ((align - 1) % SSL3_ALIGN_PAYLOAD); -#endif - SSL3_BUFFER_set_offset(wb, align); - if (!WPACKET_init_static_len(&pkt[0], SSL3_BUFFER_get_buf(wb), - SSL3_BUFFER_get_len(wb), 0) - || !WPACKET_allocate_bytes(&pkt[0], align, NULL)) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); - goto err; - } - wpinited = 1; - } - for (j = 0; j < numtempl; j++) { - thispkt = &pkt[prefix + j]; - - wb = &s->rlayer.wbuf[prefix + j]; - wb->type = templates[j].type; - - if (using_ktls) { - /* - * ktls doesn't modify the buffer, but to avoid a warning we need - * to discard the const qualifier. - * This doesn't leak memory because the buffers have been - * released when switching to ktls. - */ - SSL3_BUFFER_set_buf(wb, (unsigned char *)templates[j].buf); - SSL3_BUFFER_set_offset(wb, 0); - SSL3_BUFFER_set_app_buffer(wb, 1); - } else { -#if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD != 0 - align = (size_t)SSL3_BUFFER_get_buf(wb) + SSL3_RT_HEADER_LENGTH; - align = SSL3_ALIGN_PAYLOAD - 1 - - ((align - 1) % SSL3_ALIGN_PAYLOAD); -#endif - /* TODO(RECLAYER): Is this alignment actually used somewhere? */ - SSL3_BUFFER_set_offset(wb, align); - if (!WPACKET_init_static_len(thispkt, SSL3_BUFFER_get_buf(wb), - SSL3_BUFFER_get_len(wb), 0) - || !WPACKET_allocate_bytes(thispkt, align, NULL)) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); - goto err; - } - wpinited++; - } - } - - if (!using_ktls) { - /* Explicit IV length, block ciphers appropriate version flag */ - if (s->enc_write_ctx && SSL_USE_EXPLICIT_IV(s) - && !SSL_CONNECTION_TREAT_AS_TLS13(s)) { - int mode = EVP_CIPHER_CTX_get_mode(s->enc_write_ctx); - if (mode == EVP_CIPH_CBC_MODE) { - eivlen = EVP_CIPHER_CTX_get_iv_length(s->enc_write_ctx); - if (eivlen < 0) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_LIBRARY_BUG); - goto err; - } - if (eivlen <= 1) - eivlen = 0; - } else if (mode == EVP_CIPH_GCM_MODE) { - /* Need explicit part of IV for GCM mode */ - eivlen = EVP_GCM_TLS_EXPLICIT_IV_LEN; - } else if (mode == EVP_CIPH_CCM_MODE) { - eivlen = EVP_CCM_TLS_EXPLICIT_IV_LEN; - } - } - } - - totlen = 0; - /* Clear our SSL3_RECORD structures */ - memset(wr, 0, sizeof(wr)); - for (j = 0; j < numtempl + prefix; j++) { - unsigned int version = (s->version == TLS1_3_VERSION) ? TLS1_2_VERSION - : s->version; - unsigned char *compressdata = NULL; - size_t maxcomplen; - unsigned int rectype; - - thispkt = &pkt[j]; - thiswr = &wr[j]; - thistempl = (j == 0 && prefix == 1) ? &prefixtempl : - &templates[j - prefix]; - - /* - * In TLSv1.3, once encrypting, we always use application data for the - * record type - */ - if (SSL_CONNECTION_TREAT_AS_TLS13(s) - && s->enc_write_ctx != NULL - && (s->statem.enc_write_state != ENC_WRITE_STATE_WRITE_PLAIN_ALERTS - || thistempl->type != SSL3_RT_ALERT)) - rectype = SSL3_RT_APPLICATION_DATA; - else - rectype = thistempl->type; - - SSL3_RECORD_set_type(thiswr, rectype); - - /* - * Some servers hang if initial client hello is larger than 256 bytes - * and record version number > TLS 1.0 - */ - if (SSL_get_state(ssl) == TLS_ST_CW_CLNT_HELLO - && !s->renegotiate - && TLS1_get_version(ssl) > TLS1_VERSION - && s->hello_retry_request == SSL_HRR_NONE) - version = TLS1_VERSION; - SSL3_RECORD_set_rec_version(thiswr, version); - - maxcomplen = thistempl->buflen; - if (s->compress != NULL) - maxcomplen += SSL3_RT_MAX_COMPRESSED_OVERHEAD; - - /* - * When using offload kernel will write the header. - * Otherwise write the header now - */ - if (!using_ktls - && (!WPACKET_put_bytes_u8(thispkt, rectype) - || !WPACKET_put_bytes_u16(thispkt, version) - || !WPACKET_start_sub_packet_u16(thispkt) - || (eivlen > 0 - && !WPACKET_allocate_bytes(thispkt, eivlen, NULL)) - || (maxcomplen > 0 - && !WPACKET_reserve_bytes(thispkt, maxcomplen, - &compressdata)))) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); - goto err; - } - - /* lets setup the record stuff. */ - SSL3_RECORD_set_data(thiswr, compressdata); - SSL3_RECORD_set_length(thiswr, thistempl->buflen); - /* - * TODO(RECLAYER): Cast away the const. Should be safe - by why is this - * necessary? - */ - SSL3_RECORD_set_input(thiswr, (unsigned char *)thistempl->buf); - totlen += thistempl->buflen; - - /* - * we now 'read' from thiswr->input, thiswr->length bytes into - * thiswr->data - */ - - /* first we compress */ - if (s->compress != NULL) { - if (!ssl3_do_compress(s, thiswr) - || !WPACKET_allocate_bytes(thispkt, thiswr->length, NULL)) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_COMPRESSION_FAILURE); - goto err; - } - } else { - if (using_ktls) { - SSL3_RECORD_reset_data(&wr[j]); - } else { - if (!WPACKET_memcpy(thispkt, thiswr->input, thiswr->length)) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); - goto err; - } - SSL3_RECORD_reset_input(&wr[j]); - } - } - - if (SSL_CONNECTION_TREAT_AS_TLS13(s) - && !using_ktls - && s->enc_write_ctx != NULL - && (s->statem.enc_write_state != ENC_WRITE_STATE_WRITE_PLAIN_ALERTS - || thistempl->type != SSL3_RT_ALERT)) { - size_t rlen, max_send_fragment; - - if (!WPACKET_put_bytes_u8(thispkt, thistempl->type)) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); - goto err; - } - SSL3_RECORD_add_length(thiswr, 1); - - /* Add TLS1.3 padding */ - max_send_fragment = ssl_get_max_send_fragment(s); - rlen = SSL3_RECORD_get_length(thiswr); - if (rlen < max_send_fragment) { - size_t padding = 0; - size_t max_padding = max_send_fragment - rlen; - if (s->record_padding_cb != NULL) { - padding = s->record_padding_cb(ssl, thistempl->type, rlen, - s->record_padding_arg); - } else if (s->block_padding > 0) { - size_t mask = s->block_padding - 1; - size_t remainder; - - /* optimize for power of 2 */ - if ((s->block_padding & mask) == 0) - remainder = rlen & mask; - else - remainder = rlen % s->block_padding; - /* don't want to add a block of padding if we don't have to */ - if (remainder == 0) - padding = 0; - else - padding = s->block_padding - remainder; - } - if (padding > 0) { - /* do not allow the record to exceed max plaintext length */ - if (padding > max_padding) - padding = max_padding; - if (!WPACKET_memset(thispkt, 0, padding)) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, - ERR_R_INTERNAL_ERROR); - goto err; - } - SSL3_RECORD_add_length(thiswr, padding); - } - } - } - - /* - * we should still have the output to thiswr->data and the input from - * wr->input. Length should be thiswr->length. thiswr->data still points - * in the wb->buf - */ - - if (!using_ktls && !SSL_WRITE_ETM(s) && mac_size != 0) { - unsigned char *mac; - - if (!WPACKET_allocate_bytes(thispkt, mac_size, &mac) - || !ssl->method->ssl3_enc->mac(s, thiswr, mac, 1)) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); - goto err; - } - } - - /* - * Reserve some bytes for any growth that may occur during encryption. - * This will be at most one cipher block or the tag length if using - * AEAD. SSL_RT_MAX_CIPHER_BLOCK_SIZE covers either case. - */ - if (!using_ktls) { - if (!WPACKET_reserve_bytes(thispkt, - SSL_RT_MAX_CIPHER_BLOCK_SIZE, - NULL) - /* - * We also need next the amount of bytes written to this - * sub-packet - */ - || !WPACKET_get_length(thispkt, &len)) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); - goto err; - } - - /* Get a pointer to the start of this record excluding header */ - recordstart = WPACKET_get_curr(thispkt) - len; - SSL3_RECORD_set_data(thiswr, recordstart); - SSL3_RECORD_reset_input(thiswr); - SSL3_RECORD_set_length(thiswr, len); - } - } - - if (s->statem.enc_write_state == ENC_WRITE_STATE_WRITE_PLAIN_ALERTS) { - /* - * We haven't actually negotiated the version yet, but we're trying to - * send early data - so we need to use the tls13enc function. - */ - if (tls13_enc(s, wr, numtempl, 1, NULL, mac_size) < 1) { - if (!ossl_statem_in_error(s)) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); - } - goto err; - } - } else { - if (!using_ktls) { - if (prefix) { - if (ssl->method->ssl3_enc->enc(s, wr, 1, 1, NULL, mac_size) < 1) { - if (!ossl_statem_in_error(s)) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); - } - goto err; - } - } - if (ssl->method->ssl3_enc->enc(s, wr + prefix, numtempl, 1, NULL, - mac_size) < 1) { - if (!ossl_statem_in_error(s)) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); - } - goto err; - } - } - } - - for (j = 0; j < prefix + numtempl; j++) { - size_t origlen; - - thispkt = &pkt[j]; - thiswr = &wr[j]; - thistempl = (prefix == 1 && j == 0) ? &prefixtempl - : &templates[j - prefix]; - - if (using_ktls) - goto mac_done; - - /* Allocate bytes for the encryption overhead */ - if (!WPACKET_get_length(thispkt, &origlen) - /* Encryption should never shrink the data! */ - || origlen > thiswr->length - || (thiswr->length > origlen - && !WPACKET_allocate_bytes(thispkt, - thiswr->length - origlen, - NULL))) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); - goto err; - } - if (SSL_WRITE_ETM(s) && mac_size != 0) { - unsigned char *mac; - - if (!WPACKET_allocate_bytes(thispkt, mac_size, &mac) - || !ssl->method->ssl3_enc->mac(s, thiswr, mac, 1)) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); - goto err; - } - SSL3_RECORD_add_length(thiswr, mac_size); - } - - if (!WPACKET_get_length(thispkt, &len) - || !WPACKET_close(thispkt)) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); - goto err; - } - - if (s->msg_callback) { - recordstart = WPACKET_get_curr(thispkt) - len - - SSL3_RT_HEADER_LENGTH; - s->msg_callback(1, thiswr->rec_version, SSL3_RT_HEADER, recordstart, - SSL3_RT_HEADER_LENGTH, ssl, - s->msg_callback_arg); - - if (SSL_CONNECTION_TREAT_AS_TLS13(s) && s->enc_write_ctx != NULL) { - unsigned char ctype = thistempl->type; - - s->msg_callback(1, thiswr->rec_version, SSL3_RT_INNER_CONTENT_TYPE, - &ctype, 1, ssl, s->msg_callback_arg); - } - } - - if (!WPACKET_finish(thispkt)) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); - goto err; - } - - /* header is added by the kernel when using offload */ - SSL3_RECORD_add_length(thiswr, SSL3_RT_HEADER_LENGTH); - - mac_done: - /* - * we should now have thiswr->data pointing to the encrypted data, which - * is thiswr->length long. - * Setting the type is not needed but helps for debugging - */ - SSL3_RECORD_set_type(thiswr, thistempl->type); - - /* now let's set up wb */ - SSL3_BUFFER_set_left(&s->rlayer.wbuf[j], SSL3_RECORD_get_length(thiswr)); - } - - /* we now just need to write the buffers */ - return tls_retry_write_records(s); - err: - for (j = 0; j < wpinited; j++) - WPACKET_cleanup(&pkt[j]); - return -1; -} - -/* if SSL3_BUFFER_get_left() != 0, we need to call this - * - * Return values are as per SSL_write() - */ -int tls_retry_write_records(SSL_CONNECTION *s) -{ - int i; - SSL3_BUFFER *thiswb; - size_t currbuf = 0; - size_t tmpwrit = 0; - - for (;;) { - thiswb = &s->rlayer.wbuf[currbuf]; - /* Loop until we find a buffer we haven't written out yet */ - if (SSL3_BUFFER_get_left(thiswb) == 0 - && currbuf < s->rlayer.numwpipes - 1) { - currbuf++; - continue; - } - clear_sys_error(); - if (s->wbio != NULL) { - s->rwstate = SSL_WRITING; - - /* - * To prevent coalescing of control and data messages, - * such as in buffer_write, we flush the BIO - */ - if (BIO_get_ktls_send(s->wbio) - && thiswb->type != SSL3_RT_APPLICATION_DATA) { - i = BIO_flush(s->wbio); - if (i <= 0) - return i; - BIO_set_ktls_ctrl_msg(s->wbio, thiswb->type); - } - i = BIO_write(s->wbio, (char *) - &(SSL3_BUFFER_get_buf(thiswb) - [SSL3_BUFFER_get_offset(thiswb)]), - (unsigned int)SSL3_BUFFER_get_left(thiswb)); - if (i >= 0) - tmpwrit = i; - } else { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BIO_NOT_SET); - i = -1; - } - - /* - * When an empty fragment is sent on a connection using KTLS, - * it is sent as a write of zero bytes. If this zero byte - * write succeeds, i will be 0 rather than a non-zero value. - * Treat i == 0 as success rather than an error for zero byte - * writes to permit this case. - */ - if (i >= 0 && tmpwrit == SSL3_BUFFER_get_left(thiswb)) { - SSL3_BUFFER_set_left(thiswb, 0); - SSL3_BUFFER_add_offset(thiswb, tmpwrit); - if (currbuf + 1 < s->rlayer.numwpipes) - continue; - s->rwstate = SSL_NOTHING; - /* - * Next chunk of data should get another prepended empty fragment - * in ciphersuites with known-IV weakness: - */ - s->s3.empty_fragment_done = 0; - return 1; - } else if (i <= 0) { - if (SSL_CONNECTION_IS_DTLS(s)) { - /* - * For DTLS, just drop it. That's kind of the whole point in - * using a datagram service - */ - SSL3_BUFFER_set_left(thiswb, 0); - } - return i; - } - SSL3_BUFFER_add_offset(thiswb, tmpwrit); - SSL3_BUFFER_sub_left(thiswb, tmpwrit); - } -} - int ossl_tls_handle_rlayer_return(SSL_CONNECTION *s, int ret, char *file, int line) { @@ -1744,21 +1203,34 @@ static const OSSL_RECORD_METHOD *ssl_select_next_record_layer(SSL_CONNECTION *s, return s->rlayer.rrlmethod; } -static int ssl_post_record_layer_select(SSL_CONNECTION *s) +static int ssl_post_record_layer_select(SSL_CONNECTION *s, int direction) { + const OSSL_RECORD_METHOD *thismethod; + OSSL_RECORD_LAYER *thisrl; + + if (direction == OSSL_RECORD_DIRECTION_READ) { + thismethod = s->rlayer.rrlmethod; + thisrl = s->rlayer.rrl; + } else { + thismethod = s->rlayer.wrlmethod; + thisrl = s->rlayer.wrl; + } + #ifndef OPENSSL_NO_KTLS - SSL *ssl = SSL_CONNECTION_GET_SSL(s); + { + SSL *ssl = SSL_CONNECTION_GET_SSL(s); - if (s->rlayer.rrlmethod == &ossl_ktls_record_method) { - /* KTLS does not support renegotiation so disallow it */ - SSL_set_options(ssl, SSL_OP_NO_RENEGOTIATION); + if (s->rlayer.rrlmethod == &ossl_ktls_record_method) { + /* KTLS does not support renegotiation so disallow it */ + SSL_set_options(ssl, SSL_OP_NO_RENEGOTIATION); + } } #endif - if (SSL_IS_FIRST_HANDSHAKE(s) && s->rlayer.rrlmethod->set_first_handshake != NULL) - s->rlayer.rrlmethod->set_first_handshake(s->rlayer.rrl, 1); + if (SSL_IS_FIRST_HANDSHAKE(s) && thismethod->set_first_handshake != NULL) + thismethod->set_first_handshake(thisrl, 1); - if (s->max_pipelines != 0 && s->rlayer.rrlmethod->set_max_pipelines != NULL) - s->rlayer.rrlmethod->set_max_pipelines(s->rlayer.rrl, s->max_pipelines); + if (s->max_pipelines != 0 && thismethod->set_max_pipelines != NULL) + thismethod->set_max_pipelines(thisrl, s->max_pipelines); return 1; } @@ -1775,6 +1247,9 @@ int ssl_set_new_record_layer(SSL_CONNECTION *s, int version, OSSL_PARAM options[5], *opts = options; OSSL_PARAM settings[6], *set = settings; const OSSL_RECORD_METHOD *origmeth = s->rlayer.rrlmethod; + const OSSL_RECORD_METHOD **thismethod; + OSSL_RECORD_LAYER **thisrl; + BIO *thisbio; SSL_CTX *sctx = SSL_CONNECTION_GET_CTX(s); const OSSL_RECORD_METHOD *meth; int use_etm, stream_mac = 0, tlstree = 0; @@ -1784,16 +1259,26 @@ int ssl_set_new_record_layer(SSL_CONNECTION *s, int version, meth = ssl_select_next_record_layer(s, level); - if (s->rlayer.rrlmethod != NULL && !s->rlayer.rrlmethod->free(s->rlayer.rrl)) { + if (direction == OSSL_RECORD_DIRECTION_READ) { + thismethod = &s->rlayer.rrlmethod; + thisrl = &s->rlayer.rrl; + thisbio = s->rbio; + } else { + thismethod = &s->rlayer.wrlmethod; + thisrl = &s->rlayer.wrl; + thisbio = s->wbio; + } + + if (*thismethod != NULL && !(*thismethod)->free(*thisrl)) { ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR); return 0; } - s->rlayer.rrl = NULL; + *thisrl = NULL; if (meth != NULL) - s->rlayer.rrlmethod = meth; + *thismethod = meth; - if (!ossl_assert(s->rlayer.rrlmethod != NULL)) { + if (!ossl_assert(*thismethod != NULL)) { ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR); return 0; } @@ -1803,10 +1288,12 @@ int ssl_set_new_record_layer(SSL_CONNECTION *s, int version, &s->options); *opts++ = OSSL_PARAM_construct_uint32(OSSL_LIBSSL_RECORD_LAYER_PARAM_MODE, &s->mode); - *opts++ = OSSL_PARAM_construct_size_t(OSSL_LIBSSL_RECORD_LAYER_READ_BUFFER_LEN, - &s->rlayer.default_read_buf_len); - *opts++ = OSSL_PARAM_construct_int(OSSL_LIBSSL_RECORD_LAYER_PARAM_READ_AHEAD, - &s->rlayer.read_ahead); + if (direction == OSSL_RECORD_DIRECTION_READ) { + *opts++ = OSSL_PARAM_construct_size_t(OSSL_LIBSSL_RECORD_LAYER_READ_BUFFER_LEN, + &s->rlayer.default_read_buf_len); + *opts++ = OSSL_PARAM_construct_int(OSSL_LIBSSL_RECORD_LAYER_PARAM_READ_AHEAD, + &s->rlayer.read_ahead); + } *opts = OSSL_PARAM_construct_end(); /* Parameters that *must* be supported by a record layer if passed */ @@ -1862,43 +1349,49 @@ int ssl_set_new_record_layer(SSL_CONNECTION *s, int version, if (max_early_data != 0) *set++ = OSSL_PARAM_construct_uint(OSSL_LIBSSL_RECORD_LAYER_PARAM_MAX_EARLY_DATA, - &max_early_data); + &max_early_data); } *set = OSSL_PARAM_construct_end(); for (;;) { int rlret; - BIO *prev = s->rlayer.rrlnext; + BIO *prev = NULL; + BIO *next = NULL; unsigned int epoch = 0;; - if (SSL_CONNECTION_IS_DTLS(s) - && level != OSSL_RECORD_PROTECTION_LEVEL_NONE) - epoch = DTLS_RECORD_LAYER_get_r_epoch(&s->rlayer) + 1; /* new epoch */ - if (SSL_CONNECTION_IS_DTLS(s)) - s->rlayer.rrlnext = BIO_new(BIO_s_dgram_mem()); - else - s->rlayer.rrlnext = BIO_new(BIO_s_mem()); + if (direction == OSSL_RECORD_DIRECTION_READ) { + prev = s->rlayer.rrlnext; + if (SSL_CONNECTION_IS_DTLS(s) + && level != OSSL_RECORD_PROTECTION_LEVEL_NONE) + epoch = DTLS_RECORD_LAYER_get_r_epoch(&s->rlayer) + 1; /* new epoch */ - if (s->rlayer.rrlnext == NULL) { - BIO_free(prev); - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); - return 0; + if (SSL_CONNECTION_IS_DTLS(s)) + next = BIO_new(BIO_s_dgram_mem()); + else + next = BIO_new(BIO_s_mem()); + + if (next == NULL) { + BIO_free(prev); + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + s->rlayer.rrlnext = next; } - rlret = s->rlayer.rrlmethod->new_record_layer(sctx->libctx, - sctx->propq, - version, s->server, - direction, level, epoch, - key, keylen, iv, ivlen, - mackey, mackeylen, ciph, - taglen, mactype, md, comp, - prev, s->rbio, - s->rlayer.rrlnext, NULL, - NULL, settings, options, - rlayer_dispatch, s, - &s->rlayer.rrl); + rlret = (*thismethod)->new_record_layer(sctx->libctx, + sctx->propq, + version, s->server, + direction, level, epoch, + key, keylen, iv, ivlen, + mackey, mackeylen, ciph, + taglen, mactype, md, comp, + prev, thisbio, + next, NULL, + NULL, settings, options, + rlayer_dispatch, s, + thisrl); BIO_free(prev); switch (rlret) { case OSSL_RECORD_RETURN_FATAL: @@ -1906,12 +1399,12 @@ int ssl_set_new_record_layer(SSL_CONNECTION *s, int version, return 0; case OSSL_RECORD_RETURN_NON_FATAL_ERR: - if (s->rlayer.rrlmethod != origmeth && origmeth != NULL) { + if (*thismethod != origmeth && origmeth != NULL) { /* * We tried a new record layer method, but it didn't work out, * so we fallback to the original method and try again */ - s->rlayer.rrlmethod = origmeth; + *thismethod = origmeth; continue; } SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_NO_SUITABLE_RECORD_LAYER); @@ -1928,5 +1421,5 @@ int ssl_set_new_record_layer(SSL_CONNECTION *s, int version, break; } - return ssl_post_record_layer_select(s); + return ssl_post_record_layer_select(s, direction); } diff --git a/ssl/record/record.h b/ssl/record/record.h index fe31ccdab5..1442b8cb67 100644 --- a/ssl/record/record.h +++ b/ssl/record/record.h @@ -133,8 +133,12 @@ typedef struct record_layer_st { /* Method to use for the read record layer*/ const OSSL_RECORD_METHOD *rrlmethod; + /* Method to use for the write record layer*/ + const OSSL_RECORD_METHOD *wrlmethod; /* The read record layer object itself */ OSSL_RECORD_LAYER *rrl; + /* The write record layer object itself */ + OSSL_RECORD_LAYER *wrl; /* BIO to store data destined for the next read record layer epoch */ BIO *rrlnext; /* Default read buffer length to be passed to the record layer */ @@ -217,7 +221,6 @@ __owur int ssl3_enc(SSL_CONNECTION *s, SSL3_RECORD *inrecs, size_t n_recs, int send, SSL_MAC_BUF *mac, size_t macsize); __owur int n_ssl3_mac(SSL_CONNECTION *s, SSL3_RECORD *rec, unsigned char *md, int send); -__owur int tls_retry_write_records(SSL_CONNECTION *s); __owur int tls1_enc(SSL_CONNECTION *s, SSL3_RECORD *recs, size_t n_recs, int sending, SSL_MAC_BUF *mac, size_t macsize); __owur int tls1_mac_old(SSL_CONNECTION *s, SSL3_RECORD *rec, unsigned char *md, @@ -263,7 +266,4 @@ OSSL_CORE_MAKE_FUNC(void, rlayer_msg_callback, (int write_p, int version, void *cbarg)) # define OSSL_FUNC_RLAYER_SECURITY 3 OSSL_CORE_MAKE_FUNC(int, rlayer_security, (void *cbarg, int op, int bits, - int nid, void *other)) - -int tls_write_records(SSL_CONNECTION *s, OSSL_RECORD_TEMPLATE *templates, - size_t numtempl); + int nid, void *other)) \ No newline at end of file diff --git a/ssl/record/recordmethod.h b/ssl/record/recordmethod.h index 9e77c5f4e4..a6dd59fc32 100644 --- a/ssl/record/recordmethod.h +++ b/ssl/record/recordmethod.h @@ -186,11 +186,6 @@ struct ossl_record_method_st { * by |templates|. Each record should be no longer than the value returned * by get_max_record_len(), and there should be no more records than the * value returned by get_max_records(). - * |allowance| is the maximum amount of "on-the-wire" data that is allowed - * to be sent at the moment (including all QUIC headers, but excluding any - * UDP/IP headers). After a successful or retry return |*sent| will - * be updated with the amount of data that has been sent so far. In the case - * of a retry this could be 0. * Where possible the caller will attempt to ensure that all records are the * same length, except the last record. This may not always be possible so * the record method implementation should not rely on this being the case. @@ -206,24 +201,19 @@ struct ossl_record_method_st { * 0 on retry * -1 on failure */ - int (*write_records)(OSSL_RECORD_LAYER *rl, OSSL_RECORD_TEMPLATE **templates, - size_t numtempl, size_t allowance, size_t *sent); + int (*write_records)(OSSL_RECORD_LAYER *rl, OSSL_RECORD_TEMPLATE *templates, + size_t numtempl); /* * Retry a previous call to write_records. The caller should continue to * call this until the function returns with success or failure. After - * each retry more of the data may have been incrementally sent. |allowance| - * is the amount of "on-the-wire" data that is allowed to be sent at the - * moment. After a successful or retry return |*sent| will - * be updated with the amount of data that has been sent by this call to - * retry_write_records(). + * each retry more of the data may have been incrementally sent. * Returns: * 1 on success * 0 on retry * -1 on failure */ - int (*retry_write_records)(OSSL_RECORD_LAYER *rl, size_t allowance, - size_t *sent); + int (*retry_write_records)(OSSL_RECORD_LAYER *rl); /* * Read a record and return the record layer version and record type in diff --git a/ssl/s3_enc.c b/ssl/s3_enc.c index bfff734a4f..a1b91a0acb 100644 --- a/ssl/s3_enc.c +++ b/ssl/s3_enc.c @@ -99,6 +99,8 @@ int ssl3_change_cipher_state(SSL_CONNECTION *s, int which) int mdi; size_t n, iv_len, key_len; int reuse_dd = 0; + int direction = (which & SSL3_CC_READ) != 0 ? OSSL_RECORD_DIRECTION_READ + : OSSL_RECORD_DIRECTION_WRITE; ciph = s->s3.tmp.new_sym_enc; md = s->s3.tmp.new_hash; @@ -143,16 +145,16 @@ int ssl3_change_cipher_state(SSL_CONNECTION *s, int which) goto err; } - if (which & SSL3_CC_READ) { - if (!ssl_set_new_record_layer(s, SSL3_VERSION, - OSSL_RECORD_DIRECTION_READ, - OSSL_RECORD_PROTECTION_LEVEL_APPLICATION, - key, key_len, iv, iv_len, mac_secret, - md_len, ciph, 0, NID_undef, md, comp)) { - /* SSLfatal already called */ - goto err; - } + if (!ssl_set_new_record_layer(s, SSL3_VERSION, + direction, + OSSL_RECORD_PROTECTION_LEVEL_APPLICATION, + key, key_len, iv, iv_len, mac_secret, + md_len, ciph, 0, NID_undef, md, comp)) { + /* SSLfatal already called */ + goto err; + } + if (which & SSL3_CC_READ) { s->statem.enc_write_state = ENC_WRITE_STATE_VALID; return 1; } diff --git a/ssl/s3_msg.c b/ssl/s3_msg.c index 749c93e16d..06acc0b466 100644 --- a/ssl/s3_msg.c +++ b/ssl/s3_msg.c @@ -95,7 +95,7 @@ int ssl3_dispatch_alert(SSL *s) if (RECORD_LAYER_write_pending(&sc->rlayer)) return -1; - i = tls_write_records(sc, &templ, 1); + i = sc->rlayer.wrlmethod->write_records(sc->rlayer.wrl, &templ, 1); if (i <= 0) { sc->s3.alert_dispatch = 1; } else { diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index a143dbf317..b976324f92 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -669,6 +669,15 @@ int ossl_ssl_connection_reset(SSL *s) /* SSLfatal already called */ return 0; } + if (!ssl_set_new_record_layer(sc, + SSL_CONNECTION_IS_DTLS(sc) ? DTLS_ANY_VERSION : TLS_ANY_VERSION, + OSSL_RECORD_DIRECTION_WRITE, + OSSL_RECORD_PROTECTION_LEVEL_NONE, + NULL, 0, NULL, 0, NULL, 0, NULL, 0, + NID_undef, NULL, NULL)) { + /* SSLfatal already called */ + return 0; + } return 1; } diff --git a/ssl/t1_enc.c b/ssl/t1_enc.c index 920c8e028b..7c7876b7df 100644 --- a/ssl/t1_enc.c +++ b/ssl/t1_enc.c @@ -243,11 +243,11 @@ int tls1_change_cipher_state(SSL_CONNECTION *s, int which) s->mac_flags &= ~SSL_MAC_FLAG_READ_MAC_TLSTREE; if (!ssl_set_new_record_layer(s, s->version, - OSSL_RECORD_DIRECTION_READ, - OSSL_RECORD_PROTECTION_LEVEL_APPLICATION, - key, cl, iv, (size_t)k, mac_secret, - mac_secret_size, c, taglen, mac_type, - m, comp)) { + OSSL_RECORD_DIRECTION_READ, + OSSL_RECORD_PROTECTION_LEVEL_APPLICATION, + key, cl, iv, (size_t)k, mac_secret, + mac_secret_size, c, taglen, mac_type, + m, comp)) { /* SSLfatal already called */ goto err; } @@ -270,6 +270,17 @@ int tls1_change_cipher_state(SSL_CONNECTION *s, int which) s->mac_flags |= SSL_MAC_FLAG_WRITE_MAC_TLSTREE; else s->mac_flags &= ~SSL_MAC_FLAG_WRITE_MAC_TLSTREE; + + if (!ssl_set_new_record_layer(s, s->version, + OSSL_RECORD_DIRECTION_WRITE, + OSSL_RECORD_PROTECTION_LEVEL_APPLICATION, + key, cl, iv, (size_t)k, mac_secret, + mac_secret_size, c, taglen, mac_type, + m, comp)) { + /* SSLfatal already called */ + goto err; + } + if (s->enc_write_ctx != NULL && !SSL_CONNECTION_IS_DTLS(s)) { reuse_dd = 1; } else if ((s->enc_write_ctx = EVP_CIPHER_CTX_new()) == NULL) { diff --git a/ssl/tls13_enc.c b/ssl/tls13_enc.c index 539dcd2f91..829dfe3c10 100644 --- a/ssl/tls13_enc.c +++ b/ssl/tls13_enc.c @@ -458,6 +458,9 @@ int tls13_change_cipher_state(SSL_CONNECTION *s, int which) const EVP_CIPHER *cipher = NULL; SSL_CTX *sctx = SSL_CONNECTION_GET_CTX(s); size_t keylen, ivlen, taglen; + int level; + int direction = (which & SSL3_CC_READ) != 0 ? OSSL_RECORD_DIRECTION_READ + : OSSL_RECORD_DIRECTION_WRITE; #if !defined(OPENSSL_NO_KTLS) && defined(OPENSSL_KTLS_TLS13) ktls_crypto_info_t crypto_info; void *rl_sequence; @@ -702,20 +705,21 @@ int tls13_change_cipher_state(SSL_CONNECTION *s, int which) else s->statem.enc_write_state = ENC_WRITE_STATE_VALID; + level = (which & SSL3_CC_EARLY) != 0 + ? OSSL_RECORD_PROTECTION_LEVEL_EARLY + : ((which &SSL3_CC_HANDSHAKE) != 0 + ? OSSL_RECORD_PROTECTION_LEVEL_HANDSHAKE + : OSSL_RECORD_PROTECTION_LEVEL_APPLICATION); + + if (!ssl_set_new_record_layer(s, s->version, + direction, + level, key, keylen, iv, ivlen, NULL, 0, + cipher, taglen, NID_undef, NULL, NULL)) { + /* SSLfatal already called */ + goto err; + } + if ((which & SSL3_CC_READ) != 0) { - int level = (which & SSL3_CC_EARLY) != 0 - ? OSSL_RECORD_PROTECTION_LEVEL_EARLY - : ((which &SSL3_CC_HANDSHAKE) != 0 - ? OSSL_RECORD_PROTECTION_LEVEL_HANDSHAKE - : OSSL_RECORD_PROTECTION_LEVEL_APPLICATION); - - if (!ssl_set_new_record_layer(s, s->version, - OSSL_RECORD_DIRECTION_READ, - level, key, keylen, iv, ivlen, NULL, 0, - cipher, taglen, NID_undef, NULL, NULL)) { - /* SSLfatal already called */ - goto err; - } /* TODO(RECLAYER): Remove me when write rlayer done */ goto skip_ktls; } @@ -797,6 +801,8 @@ int tls13_update_key(SSL_CONNECTION *s, int sending) EVP_CIPHER_CTX *ciph_ctx; size_t keylen, ivlen, taglen; int ret = 0, l; + int direction = sending ? OSSL_RECORD_DIRECTION_WRITE + : OSSL_RECORD_DIRECTION_READ; if ((l = EVP_MD_get_size(md)) <= 0) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); @@ -830,16 +836,14 @@ int tls13_update_key(SSL_CONNECTION *s, int sending) memcpy(insecret, secret, hashlen); - if (!sending) { - if (!ssl_set_new_record_layer(s, s->version, - OSSL_RECORD_DIRECTION_READ, - OSSL_RECORD_PROTECTION_LEVEL_APPLICATION, - key, keylen, iv, ivlen, NULL, 0, - s->s3.tmp.new_sym_enc, taglen, NID_undef, NULL, - NULL)) { - /* SSLfatal already called */ - goto err; - } + if (!ssl_set_new_record_layer(s, s->version, + direction, + OSSL_RECORD_PROTECTION_LEVEL_APPLICATION, + key, keylen, iv, ivlen, NULL, 0, + s->s3.tmp.new_sym_enc, taglen, NID_undef, NULL, + NULL)) { + /* SSLfatal already called */ + goto err; } s->statem.enc_write_state = ENC_WRITE_STATE_VALID;