summaryrefslogtreecommitdiffstats
path: root/ssl
diff options
context:
space:
mode:
authorHugo Landau <hlandau@openssl.org>2023-08-17 08:55:52 +0100
committerTomas Mraz <tomas@openssl.org>2023-08-29 15:33:22 +0200
commit777a8a7f5d5b80919da906cdaf8825f502bcad4e (patch)
treedd9ed6452465c7e381e8640a6573acca3783a1f8 /ssl
parent1cfdbdd0d454b9b3882e364addfab920af3a29d5 (diff)
QUIC: Minimally handle version negotiation packets
Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/21764)
Diffstat (limited to 'ssl')
-rw-r--r--ssl/quic/quic_channel.c56
-rw-r--r--ssl/quic/quic_channel_local.h7
-rw-r--r--ssl/quic/quic_wire_pkt.c7
3 files changed, 70 insertions, 0 deletions
diff --git a/ssl/quic/quic_channel.c b/ssl/quic/quic_channel.c
index 98e1a0110f..516b895d8d 100644
--- a/ssl/quic/quic_channel.c
+++ b/ssl/quic/quic_channel.c
@@ -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.
diff --git a/ssl/quic/quic_channel_local.h b/ssl/quic/quic_channel_local.h
index ff861f18c7..8cef137255 100644
--- a/ssl/quic/quic_channel_local.h
+++ b/ssl/quic/quic_channel_local.h
@@ -305,6 +305,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.
*/
diff --git a/ssl/quic/quic_wire_pkt.c b/ssl/quic/quic_wire_pkt.c
index bd218b2361..069f0c8fa5 100644
--- a/ssl/quic/quic_wire_pkt.c
+++ b/ssl/quic/quic_wire_pkt.c
@@ -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;