From: Pauli Date: Tue, 18 Jul 2023 01:37:14 +0000 (+1000) Subject: quic conformance: 10.2.1 rate limiting X-Git-Tag: openssl-3.2.0-alpha1~311 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=50e76846bf2d431d431b0b026f63d0b708d6e960;p=thirdparty%2Fopenssl.git quic conformance: 10.2.1 rate limiting Implement the two requirements about limiting closing transmission size to no more than thrice the received size. Reviewed-by: Tim Hudson Reviewed-by: Hugo Landau (Merged from https://github.com/openssl/openssl/pull/21429) --- diff --git a/include/internal/quic_txp.h b/include/internal/quic_txp.h index 74590749fc3..d5ca1c4361a 100644 --- a/include/internal/quic_txp.h +++ b/include/internal/quic_txp.h @@ -56,7 +56,8 @@ typedef struct ossl_quic_tx_packetiser_args_st { * crypto[QUIC_PN_SPACE_APP] is the 1-RTT crypto stream. */ QUIC_SSTREAM *crypto[QUIC_PN_SPACE_NUM]; -} OSSL_QUIC_TX_PACKETISER_ARGS; + + } OSSL_QUIC_TX_PACKETISER_ARGS; typedef struct ossl_quic_tx_packetiser_st OSSL_QUIC_TX_PACKETISER; @@ -67,6 +68,14 @@ typedef void (ossl_quic_initial_token_free_fn)(const unsigned char *buf, void ossl_quic_tx_packetiser_free(OSSL_QUIC_TX_PACKETISER *txp); +/* + * When in the closing state we need to maintain a count of received bytes + * so that we can limit the number of close connection frames we send. + * Refer RFC 9000 s. 10.2.1 Closing Connection State. + */ +void ossl_quic_tx_packetiser_record_received_closing_bytes( + OSSL_QUIC_TX_PACKETISER *txp, size_t n); + /* * Generates a datagram by polling the various ELs to determine if they want to * generate any frames, and generating a datagram which coalesces packets for diff --git a/ssl/quic/quic_channel.c b/ssl/quic/quic_channel.c index 29ee89ac58f..f6f5b86125c 100644 --- a/ssl/quic/quic_channel.c +++ b/ssl/quic/quic_channel.c @@ -1783,6 +1783,7 @@ static void ch_rx_check_forged_pkt_limit(QUIC_CHANNEL *ch) static int ch_rx(QUIC_CHANNEL *ch) { int handled_any = 0; + const int closing = ossl_quic_channel_is_closing(ch); if (!ch->is_server && !ch->have_sent_any_pkt) /* @@ -1797,6 +1798,11 @@ static int ch_rx(QUIC_CHANNEL *ch) if (!ossl_qrx_read_pkt(ch->qrx, &ch->qrx_pkt)) break; + /* Track the amount of data received while in the closing state */ + if (closing) + ossl_quic_tx_packetiser_record_received_closing_bytes( + ch->txp, ch->qrx_pkt->hdr->len); + if (!handled_any) ch_update_idle(ch); @@ -1820,7 +1826,7 @@ static int ch_rx(QUIC_CHANNEL *ch) * When in TERMINATING - CLOSING, generate a CONN_CLOSE frame whenever we * process one or more incoming packets. */ - if (handled_any && ossl_quic_channel_is_closing(ch)) + if (handled_any && closing) ch->conn_close_queued = 1; return 1; @@ -1858,8 +1864,12 @@ static void ch_rx_handle_packet(QUIC_CHANNEL *ch) assert(ch->qrx_pkt != NULL); + /* + * RFC 9000 s. 10.2.1 Closing Connection State: + * An endpoint that is closing is not required to process any + * received frame. + */ if (!ossl_quic_channel_is_active(ch)) - /* Do not process packets once we are terminating. */ return; if (ossl_quic_pkt_type_is_encrypted(ch->qrx_pkt->hdr->type)) { diff --git a/ssl/quic/quic_channel_local.h b/ssl/quic/quic_channel_local.h index 8e7d78855ed..29625f7eab3 100644 --- a/ssl/quic/quic_channel_local.h +++ b/ssl/quic/quic_channel_local.h @@ -202,7 +202,7 @@ struct quic_channel_st { */ uint64_t txku_threshold_override; - /* Valid if we are in the TERMINATING or TERMINATED states. */ + /* Valid if we are in the TERMINATING or TERMINATED states. */ QUIC_TERMINATE_CAUSE terminate_cause; /* diff --git a/ssl/quic/quic_txp.c b/ssl/quic/quic_txp.c index f244488a4c7..6c5465b42da 100644 --- a/ssl/quic/quic_txp.c +++ b/ssl/quic/quic_txp.c @@ -89,6 +89,13 @@ struct ossl_quic_tx_packetiser_st { OSSL_QUIC_FRAME_CONN_CLOSE conn_close_frame; + /* + * Counts of the number of bytes received and sent while in the closing + * state. + */ + uint64_t closing_bytes_recv; + uint64_t closing_bytes_xmit; + /* Internal state - packet assembly. */ struct txp_el { unsigned char *scratch; /* scratch buffer for packet assembly */ @@ -1640,6 +1647,47 @@ static void on_sstream_updated(uint64_t stream_id, void *arg) ossl_quic_stream_map_update_state(txp->args.qsm, s); } +/* + * Returns 1 if we can send that many bytes in closing state, 0 otherwise. + * Also maintains the bytes sent state if it returns a success. + */ +static int try_commit_conn_close(OSSL_QUIC_TX_PACKETISER *txp, size_t n) +{ + int res; + + /* We can always send the first connection close frame */ + if (txp->closing_bytes_recv == 0) + return 1; + + /* + * RFC 9000 s. 10.2.1 Closing Connection State: + * To avoid being used for an amplification attack, such + * endpoints MUST limit the cumulative size of packets it sends + * to three times the cumulative size of the packets that are + * received and attributed to the connection. + * and: + * An endpoint in the closing state MUST either discard packets + * received from an unvalidated address or limit the cumulative + * size of packets it sends to an unvalidated address to three + * times the size of packets it receives from that address. + */ + res = txp->closing_bytes_xmit + n <= txp->closing_bytes_recv * 3; + + /* + * Attribute the bytes to the connection, if we are allowed to send them + * and this isn't the first closing frame. + */ + if (res && txp->closing_bytes_recv != 0) + txp->closing_bytes_xmit += n; + return res; +} + +void ossl_quic_tx_packetiser_record_received_closing_bytes( + OSSL_QUIC_TX_PACKETISER *txp, size_t n) +{ + txp->closing_bytes_recv += n; +} + static int txp_generate_pre_token(OSSL_QUIC_TX_PACKETISER *txp, struct txp_pkt *pkt, int chosen_for_conn_close, @@ -1692,6 +1740,7 @@ static int txp_generate_pre_token(OSSL_QUIC_TX_PACKETISER *txp, if (a->allow_conn_close && txp->want_conn_close && chosen_for_conn_close) { WPACKET *wpkt = tx_helper_begin(h); OSSL_QUIC_FRAME_CONN_CLOSE f, *pf = &txp->conn_close_frame; + size_t l; if (wpkt == NULL) return 0; @@ -1721,7 +1770,9 @@ static int txp_generate_pre_token(OSSL_QUIC_TX_PACKETISER *txp, pf->reason_len = 0; } - if (ossl_quic_wire_encode_frame_conn_close(wpkt, pf)) { + if (ossl_quic_wire_encode_frame_conn_close(wpkt, pf) + && WPACKET_get_total_written(wpkt, &l) + && try_commit_conn_close(txp, l)) { if (!tx_helper_commit(h)) return 0;