]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
QUIC: Minimally handle version negotiation packets
authorHugo Landau <hlandau@openssl.org>
Thu, 17 Aug 2023 07:55:52 +0000 (08:55 +0100)
committerTomas Mraz <tomas@openssl.org>
Tue, 29 Aug 2023 13:33:22 +0000 (15:33 +0200)
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/21764)

ssl/quic/quic_channel.c
ssl/quic/quic_channel_local.h
ssl/quic/quic_wire_pkt.c

index 98e1a0110fc8e562bb59ce6f4197a0a87badd24a..516b895d8df20a76cd9ab670a289ccafd7469e59 100644 (file)
@@ -99,6 +99,8 @@ static int ch_server_on_new_conn(QUIC_CHANNEL *ch, const BIO_ADDR *peer,
                                  const QUIC_CONN_ID *peer_dcid);
 static void ch_on_txp_ack_tx(const OSSL_QUIC_FRAME_ACK *ack, uint32_t pn_space,
                              void *arg);
+static void ch_rx_handle_version_neg(QUIC_CHANNEL *ch, OSSL_QRX_PKT *pkt);
+static void ch_raise_version_neg_failure(QUIC_CHANNEL *ch);
 
 DEFINE_LHASH_OF_EX(QUIC_SRT_ELEM);
 
@@ -2092,6 +2094,7 @@ static int bio_addr_eq(const BIO_ADDR *a, const BIO_ADDR *b)
 static void ch_rx_handle_packet(QUIC_CHANNEL *ch)
 {
     uint32_t enc_level;
+    int old_have_processed_any_pkt = ch->have_processed_any_pkt;
 
     assert(ch->qrx_pkt != NULL);
 
@@ -2164,6 +2167,8 @@ static void ch_rx_handle_packet(QUIC_CHANNEL *ch)
          */
         return;
 
+    ch->have_processed_any_pkt = 1;
+
     /*
      * RFC 9000 s. 17.2: "An endpoint MUST treat receipt of a packet that has a
      * non-zero value for [the reserved bits] after removing both packet and
@@ -2281,12 +2286,63 @@ static void ch_rx_handle_packet(QUIC_CHANNEL *ch)
         ossl_quic_handle_frames(ch, ch->qrx_pkt); /* best effort */
         break;
 
+    case QUIC_PKT_TYPE_VERSION_NEG:
+        /*
+         * "A client MUST discard any Version Negotiation packet if it has
+         * received and successfully processed any other packet."
+         */
+        if (!old_have_processed_any_pkt)
+            ch_rx_handle_version_neg(ch, ch->qrx_pkt);
+
+        break;
+
     default:
         assert(0);
         break;
     }
 }
 
+static void ch_rx_handle_version_neg(QUIC_CHANNEL *ch, OSSL_QRX_PKT *pkt)
+{
+    /*
+     * We do not support version negotiation at this time. As per RFC 9000 s.
+     * 6.2., we MUST abandon the connection attempt if we receive a Version
+     * Negotiation packet, unless we have already successfully processed another
+     * incoming packet, or the packet lists the QUIC version we want to use.
+     */
+    PACKET vpkt;
+    unsigned long v;
+
+    if (!PACKET_buf_init(&vpkt, pkt->hdr->data, pkt->hdr->len))
+        return;
+
+    while (PACKET_remaining(&vpkt) > 0) {
+        if (!PACKET_get_net_4(&vpkt, &v))
+            break;
+
+        if ((uint32_t)v == QUIC_VERSION_1)
+            return;
+    }
+
+    /* No match, this is a failure case. */
+    ch_raise_version_neg_failure(ch);
+}
+
+static void ch_raise_version_neg_failure(QUIC_CHANNEL *ch)
+{
+    QUIC_TERMINATE_CAUSE tcause = {0};
+
+    tcause.error_code = QUIC_ERR_CONNECTION_REFUSED;
+    tcause.reason     = "version negotiation failure";
+    tcause.reason_len = strlen(tcause.reason);
+
+    /*
+     * Skip TERMINATING state; this is not considered a protocol error and we do
+     * not send CONNECTION_CLOSE.
+     */
+    ch_start_terminating(ch, &tcause, 1);
+}
+
 /*
  * This is called by the demux when we get a packet not destined for any known
  * DCID.
index ff861f18c7c858905e7604a2fc5aba81460bdb0d..8cef1372552f8d5f931c623685a9ef921fe79f40 100644 (file)
@@ -304,6 +304,13 @@ struct quic_channel_st {
      */
     unsigned int                    have_received_enc_pkt   : 1;
 
+    /*
+     * Have we successfully processed any packet, including a Version
+     * Negotiation packet? If so, further Version Negotiation packets should be
+     * ignored.
+     */
+    unsigned int                    have_processed_any_pkt  : 1;
+
     /*
      * Have we sent literally any packet yet? If not, there is no point polling
      * RX.
index bd218b2361106606c50af7b2167fb38bcb66d38b..069f0c8fa5e6993f2f01a944e0e30cd42a523c36 100644 (file)
@@ -306,6 +306,13 @@ int ossl_quic_wire_decode_pkt_hdr(PACKET *pkt,
             hdr->data       = PACKET_data(pkt);
             hdr->len        = PACKET_remaining(pkt);
 
+            /*
+             * Version negotiation packets must contain an array of u32s, so it
+             * is invalid for their payload length to not be divisible by 4.
+             */
+            if ((hdr->len % 4) != 0)
+                return 0;
+
             /* Version negotiation packets are always fully decoded. */
             hdr->partial    = 0;