From: Neil Horman Date: Mon, 9 Dec 2024 15:21:54 +0000 (-0500) Subject: Add tracking of receive credit for unvalidated connections X-Git-Tag: openssl-3.5.0-alpha1~289 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d1c3bb2f74845568d09b83fc8eb6cf9811482925;p=thirdparty%2Fopenssl.git Add tracking of receive credit for unvalidated connections Reviewed-by: Tomas Mraz Reviewed-by: Saša Nedvědický (Merged from https://github.com/openssl/openssl/pull/26178) --- diff --git a/demos/guide/quic-hq-interop-server.c b/demos/guide/quic-hq-interop-server.c index df3864fcebf..226d9d10c2f 100644 --- a/demos/guide/quic-hq-interop-server.c +++ b/demos/guide/quic-hq-interop-server.c @@ -570,6 +570,7 @@ static int run_quic_server(SSL_CTX *ctx, BIO *sock) * Filter on the shutdown error, and only print an error * message if the cause is not SHUTDOWN */ + ERR_print_errors_fp(stderr); errcode = ERR_get_error(); if (ERR_GET_REASON(errcode) != SSL_R_PROTOCOL_IS_SHUTDOWN) fprintf(stderr, "Failure in accept stream, error %s\n", diff --git a/include/internal/quic_txp.h b/include/internal/quic_txp.h index a4b8125c8ea..70bbd05a0c3 100644 --- a/include/internal/quic_txp.h +++ b/include/internal/quic_txp.h @@ -71,7 +71,8 @@ void ossl_quic_tx_packetiser_add_unvalidated_credit(OSSL_QUIC_TX_PACKETISER *txp size_t credit); void ossl_quic_tx_packetiser_consume_unvalidated_credit(OSSL_QUIC_TX_PACKETISER *txp, size_t credit); -size_t ossl_quic_tx_packetiser_get_unvalidated_credit(OSSL_QUIC_TX_PACKETISER *txp); +int ossl_quic_tx_packetiser_check_unvalidated_credit(OSSL_QUIC_TX_PACKETISER *txp, + size_t req_credit); typedef void (ossl_quic_initial_token_free_fn)(const unsigned char *buf, size_t buf_len, void *arg); diff --git a/ssl/quic/quic_channel.c b/ssl/quic/quic_channel.c index e96dc4dbf28..61611bf3145 100644 --- a/ssl/quic/quic_channel.c +++ b/ssl/quic/quic_channel.c @@ -298,6 +298,10 @@ static int ch_init(QUIC_CHANNEL *ch) if (ch->txp == NULL) goto err; + /* clients have no amplification limit, so are considered always valid */ + if (!ch->is_server) + ossl_quic_tx_packetiser_set_validated(ch->txp); + ossl_quic_tx_packetiser_set_ack_tx_cb(ch->txp, ch_on_txp_ack_tx, ch); qrx_args.libctx = ch->port->engine->libctx; @@ -1052,6 +1056,13 @@ static int ch_on_handshake_complete(void *arg) if (!ossl_assert(ch->tx_enc_level == QUIC_ENC_LEVEL_1RTT)) return 0; + /* + * When handshake is complete, we no longer need to abide by the + * 3x amplification limit, though we should be validated as soon + * as we see a handshake key encrypted packet (see ossl_quic_handle_packet) + */ + ossl_quic_tx_packetiser_set_validated(ch->txp); + if (!ch->got_remote_transport_params) { /* * Was not a valid QUIC handshake if we did not get valid transport diff --git a/ssl/quic/quic_impl.c b/ssl/quic/quic_impl.c index e8b0888bcad..17d0d621cee 100644 --- a/ssl/quic/quic_impl.c +++ b/ssl/quic/quic_impl.c @@ -4436,6 +4436,8 @@ SSL *ossl_quic_accept_connection(SSL *ssl, uint64_t flags) out: qctx_unlock(&ctx); + if (qc != NULL) + ossl_quic_do_handshake(&qc->obj.ssl); return qc != NULL ? &qc->obj.ssl : NULL; } diff --git a/ssl/quic/quic_port.c b/ssl/quic/quic_port.c index 28457f3bcd3..5124aebc16c 100644 --- a/ssl/quic/quic_port.c +++ b/ssl/quic/quic_port.c @@ -11,6 +11,7 @@ #include "internal/quic_channel.h" #include "internal/quic_lcidm.h" #include "internal/quic_srtm.h" +#include "internal/quic_txp.h" #include "internal/ssl_unwrap.h" #include "quic_port_local.h" #include "quic_channel_local.h" @@ -606,6 +607,12 @@ static void port_bind_channel(QUIC_PORT *port, const BIO_ADDR *peer, return; if (odcid->id_len != 0) { + /* + * If we have an odcid, then we wen't through server address validation + * and as such, this channel need not conform to the 3x validation cap + * See RFC 9000 s. 8.1 + */ + ossl_quic_tx_packetiser_set_validated(ch->txp); if (!ossl_quic_bind_channel(ch, peer, scid, dcid, odcid)) { ossl_quic_channel_free(ch); return; @@ -1170,7 +1177,6 @@ static void port_default_packet_handler(QUIC_URXE *e, void *arg, * RFC 9000 s. 6 and 14.1, we only do so however, if the UDP datagram * is a minimum of 1200 bytes in size */ - if (e->data_len < 1200) goto undesirable; diff --git a/ssl/quic/quic_rx_depack.c b/ssl/quic/quic_rx_depack.c index 58a8edf03cd..31e311554ee 100644 --- a/ssl/quic/quic_rx_depack.c +++ b/ssl/quic/quic_rx_depack.c @@ -1411,6 +1411,7 @@ int ossl_quic_handle_frames(QUIC_CHANNEL *ch, OSSL_QRX_PKT *qpacket) PACKET pkt; OSSL_ACKM_RX_PKT ackm_data; uint32_t enc_level; + size_t dgram_len = qpacket->datagram_len; /* * ok has three states: @@ -1444,6 +1445,19 @@ int ossl_quic_handle_frames(QUIC_CHANNEL *ch, OSSL_QRX_PKT *qpacket) ok = 0; /* Still assume the worst */ ackm_data.pkt_space = ossl_quic_enc_level_to_pn_space(enc_level); + /* + * RFC 9000 s. 8.1 + * We can consider the connection to be validated, if we receive a packet + * from the client protected via handshake keys, meaning that the + * amplification limit no longer applies (i.e. we can set it as validated. + * Otherwise, add the size of this packet to the unvalidated credit for + * the connection. + */ + if (enc_level == QUIC_ENC_LEVEL_HANDSHAKE) + ossl_quic_tx_packetiser_set_validated(ch->txp); + else + ossl_quic_tx_packetiser_add_unvalidated_credit(ch->txp, dgram_len); + /* Now that special cases are out of the way, parse frames */ if (!PACKET_buf_init(&pkt, qpacket->hdr->data, qpacket->hdr->len) || !depack_process_frames(ch, &pkt, qpacket, diff --git a/ssl/quic/quic_txp.c b/ssl/quic/quic_txp.c index ff67ea9d570..9ff0e5aee5d 100644 --- a/ssl/quic/quic_txp.c +++ b/ssl/quic/quic_txp.c @@ -485,11 +485,12 @@ void ossl_quic_tx_packetiser_add_unvalidated_credit(OSSL_QUIC_TX_PACKETISER *txp size_t credit) { if (txp->unvalidated_credit != SIZE_MAX) { - if (SIZE_MAX - txp->unvalidated_credit < credit * 3) + if ((SIZE_MAX - txp->unvalidated_credit) > (credit * 3)) txp->unvalidated_credit += credit * 3; else txp->unvalidated_credit = SIZE_MAX - 1; } + return; } @@ -507,9 +508,12 @@ void ossl_quic_tx_packetiser_add_unvalidated_credit(OSSL_QUIC_TX_PACKETISER *txp void ossl_quic_tx_packetiser_consume_unvalidated_credit(OSSL_QUIC_TX_PACKETISER *txp, size_t credit) { - if (txp->unvalidated_credit != SIZE_MAX) - txp->unvalidated_credit -= credit; - + if (txp->unvalidated_credit != SIZE_MAX) { + if (txp->unvalidated_credit < credit) + txp->unvalidated_credit = 0; + else + txp->unvalidated_credit -= credit; + } } /** @@ -525,9 +529,10 @@ void ossl_quic_tx_packetiser_consume_unvalidated_credit(OSSL_QUIC_TX_PACKETISER * * @return 1 if the unvalidated credit exceeds `req_credit`, 0 otherwise. */ -int ossl_quic_tx_packetiser_check_unvalidated_credit(OSSL_QUIC_TX_PACKETISER *txp, size_t req_credit) +int ossl_quic_tx_packetiser_check_unvalidated_credit(OSSL_QUIC_TX_PACKETISER *txp, + size_t req_credit) { - return txp->unvalidated_credit; + return (txp->unvalidated_credit > req_credit); } OSSL_QUIC_TX_PACKETISER *ossl_quic_tx_packetiser_new(const OSSL_QUIC_TX_PACKETISER_ARGS *args) @@ -859,12 +864,13 @@ int ossl_quic_tx_packetiser_generate(OSSL_QUIC_TX_PACKETISER *txp, uint64_t cc_limit = txp->args.cc_method->get_tx_allowance(txp->args.cc_data); int need_padding = 0, txpim_pkt_reffed; + memset(status, 0, sizeof(*status)); + for (enc_level = QUIC_ENC_LEVEL_INITIAL; enc_level < QUIC_ENC_LEVEL_NUM; ++enc_level) pkt[enc_level].h_valid = 0; - memset(status, 0, sizeof(*status)); /* * Should not be needed, but a sanity check in case anyone else has been @@ -986,6 +992,13 @@ int ossl_quic_tx_packetiser_generate(OSSL_QUIC_TX_PACKETISER *txp, /* Nothing was generated for this EL, so skip. */ continue; + if (!ossl_quic_tx_packetiser_check_unvalidated_credit(txp, + pkt[enc_level].h.bytes_appended)) { + res = TXP_ERR_SPACE; + goto out; + } + ossl_quic_tx_packetiser_consume_unvalidated_credit(txp, pkt[enc_level].h.bytes_appended); + rc = txp_pkt_commit(txp, &pkt[enc_level], archetype, &txpim_pkt_reffed); if (rc) { @@ -1006,6 +1019,7 @@ int ossl_quic_tx_packetiser_generate(OSSL_QUIC_TX_PACKETISER *txp, goto out; ++pkts_done; + } /* Flush & Cleanup */ diff --git a/test/quic_txp_test.c b/test/quic_txp_test.c index 6c646f239b3..e77707613e6 100644 --- a/test/quic_txp_test.c +++ b/test/quic_txp_test.c @@ -212,6 +212,12 @@ static int helper_init(struct helper *h) if (!TEST_ptr(h->txp = ossl_quic_tx_packetiser_new(&h->args))) goto err; + /* + * Our helper should always skip validation + * as the tests are not written to expect delayed connections + */ + ossl_quic_tx_packetiser_set_validated(h->txp); + if (!TEST_ptr(h->demux = ossl_quic_demux_new(h->bio2, 8, fake_now, NULL))) goto err;