]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
quic conformance: 10.2.1 rate limiting
authorPauli <pauli@openssl.org>
Tue, 18 Jul 2023 01:37:14 +0000 (11:37 +1000)
committerPauli <pauli@openssl.org>
Fri, 4 Aug 2023 01:55:45 +0000 (11:55 +1000)
Implement the two requirements about limiting closing transmission size to
no more than thrice the received size.

Reviewed-by: Tim Hudson <tjh@openssl.org>
Reviewed-by: Hugo Landau <hlandau@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/21429)

include/internal/quic_txp.h
ssl/quic/quic_channel.c
ssl/quic/quic_channel_local.h
ssl/quic/quic_txp.c

index 74590749fc3451f071132098e0ecb8dde4d4b77f..d5ca1c4361aba2f600e14f1814e4bc6c588c8e44 100644 (file)
@@ -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
index 29ee89ac58fe6f91373f2cf52f8e24f5a89f35c0..f6f5b86125c76bfa9e9193288036dccffa124171 100644 (file)
@@ -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)) {
index 8e7d78855edf000988a930356787284822382f69..29625f7eab3455cf0764dfb54df2775cd6ca6ac3 100644 (file)
@@ -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;
 
     /*
index f244488a4c789c027a5bec77de8424b7c4e1105d..6c5465b42da190ea7e9db5964f13c952ad8a2c03 100644 (file)
@@ -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;