Files
Marius Ciepluch 0116f9bea6 feat(real-world-vfc-slice): add labeled OpenSSL/Firefox/Chromium/curl pair corpus
Harvest 176 isolated vuln-vs-fix functions (109 CVEs, 352 training rows)
into benchmarks/pairs/vfc with provenance and a JSONL training manifest.
Score the slice as an honesty dashboard (ousast pairs --slice vfc);
keep the pair gate on the local fixtures only.
2026-09-04 16:45:38 +02:00

277 lines
9.0 KiB
C

/* Provenance: openssl/openssl port_default_packet_handler (fixed).
* repo: openssl/openssl
* commit: 70cebd74d3592f5272945501b58a60374c4e13af
* parent: 48f5785425ee6b5316f8e5f921978397ba632cc6
* commit_url: https://github.com/openssl/openssl/commit/70cebd74d3592f5272945501b58a60374c4e13af
* cve: CVE-2026-18798
* license: Apache-2.0
* function: port_default_packet_handler
* relpath: ssl/quic/quic_port.c
*/
static void port_default_packet_handler(QUIC_URXE *e, void *arg,
const QUIC_CONN_ID *dcid)
{
QUIC_PORT *port = arg;
PACKET pkt;
QUIC_PKT_HDR hdr;
QUIC_CHANNEL *ch = NULL, *new_ch = NULL;
QUIC_CONN_ID odcid;
uint8_t gen_new_token = 0;
OSSL_QRX *qrx = NULL, *qrx_ref;
OSSL_QRX *qrx_src = NULL;
OSSL_QRX_ARGS qrx_args = { 0 };
uint64_t cause_flags = 0;
OSSL_QRX_PKT *qrx_pkt = NULL;
/* Don't handle anything if we are no longer running. */
if (!ossl_quic_port_is_running(port))
goto undesirable;
if (port_try_handle_stateless_reset(port, e))
goto undesirable;
if (dcid != NULL
&& ossl_quic_lcidm_lookup(port->lcidm, dcid, NULL,
(void **)&ch)) {
assert(ch != NULL);
ossl_quic_channel_inject(ch, e);
return;
}
/*
* If we have an incoming packet which doesn't match any existing connection
* we assume this is an attempt to make a new connection.
*/
if (!port->allow_incoming)
goto undesirable;
/*
* packet without destination connection id is invalid/corrupted here.
* stop wasting CPU cycles now.
*/
if (dcid == NULL)
goto undesirable;
/*
* We have got a packet for an unknown DCID. This might be an attempt to
* open a new connection.
*/
if (e->data_len < QUIC_MIN_INITIAL_DGRAM_LEN)
goto undesirable;
if (!PACKET_buf_init(&pkt, ossl_quic_urxe_data(e), e->data_len))
goto undesirable;
/*
* We set short_conn_id_len to SIZE_MAX here which will cause the decode
* operation to fail if we get a 1-RTT packet. This is fine since we only
* care about Initial packets.
*/
if (!ossl_quic_wire_decode_pkt_hdr(&pkt, SIZE_MAX, 1, 0, &hdr, NULL,
&cause_flags)) {
/*
* If we fail due to a bad version, we know the packet up to the version
* number was decoded, and we use it below to send a version
* negotiation packet
*/
if ((cause_flags & QUIC_PKT_HDR_DECODE_BAD_VERSION) == 0)
goto undesirable;
}
switch (hdr.version) {
case QUIC_VERSION_1:
break;
case QUIC_VERSION_NONE:
default:
/*
* If we get here, then we have a bogus version, and might need
* to send a version negotiation packet. According to
* 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;
/*
* If we don't get a supported version, respond with a ver
* negotiation packet, and discard
* TODO(QUIC FUTURE): Rate limit the reception of these
*/
port_send_version_negotiation(port, &e->peer, &hdr);
goto undesirable;
}
/*
* We only care about Initial packets which might be trying to establish a
* connection.
*/
if (hdr.type != QUIC_PKT_TYPE_INITIAL)
goto undesirable;
if (port->max_pending_channels > 0 && ossl_list_incoming_ch_num(&port->incoming_channel_list) >= port->max_pending_channels)
goto undesirable;
odcid.id_len = 0;
/*
* Create qrx now so we can check integrity of packet
* which does not belong to any channel.
*/
qrx_args.libctx = port->engine->libctx;
qrx_args.demux = port->demux;
qrx_args.short_conn_id_len = dcid->id_len;
qrx_args.max_deferred = 32;
qrx = ossl_qrx_new(&qrx_args);
if (qrx == NULL)
goto undesirable;
/*
* Derive secrets for qrx only.
*/
if (!ossl_quic_provide_initial_secret(port->engine->libctx,
port->engine->propq,
&hdr.dst_conn_id,
/* is_server */ 1,
qrx, NULL))
goto undesirable;
if (ossl_qrx_validate_initial_packet(qrx, e, (const QUIC_CONN_ID *)dcid) == 0)
goto undesirable;
if (port->validate_addr == 0) {
/*
* Forget qrx, because it becomes (almost) useless here. We must let
* channel to create a new QRX for connection ID server chooses. The
* validation keys for new DCID will be derived by
* ossl_quic_channel_on_new_conn() when we will be creating channel.
* See RFC 9000 section 7.2 negotiating connection id to better
* understand what's going on here.
*
* Did we say qrx is almost useless? Why? Because qrx remembers packets
* we just validated. Those packets must be injected to channel we are
* going to create. We use qrx_src alias so we can read packets from
* qrx and inject them to channel.
*/
qrx_src = qrx;
qrx = NULL;
}
/*
* TODO(QUIC FUTURE): there should be some logic similar to accounting half-open
* states in TCP. If we reach certain threshold, then we want to
* validate clients.
*/
if (port->validate_addr == 1 && hdr.token == NULL) {
port_send_retry(port, &e->peer, &hdr);
goto undesirable;
}
/*
* Note, even if we don't enforce the sending of retry frames for
* server address validation, we may still get a token if we sent
* a NEW_TOKEN frame during a prior connection, which we should still
* validate here
*/
if (hdr.token != NULL
&& port_validate_token(&hdr, port, &e->peer,
&odcid, &gen_new_token)
== 0) {
/*
* RFC 9000 s 8.1.3
* When a server receives an Initial packet with an address
* validation token, it MUST attempt to validate the token,
* unless it has already completed address validation.
* If the token is invalid, then the server SHOULD proceed as
* if the client did not have a validated address,
* including potentially sending a Retry packet
* Note: If address validation is disabled, just act like
* the request is valid
*/
if (port->validate_addr == 1) {
/*
* Again: we should consider saving initial encryption level
* secrets to token here to save some CPU cycles.
*/
port_send_retry(port, &e->peer, &hdr);
goto undesirable;
}
/*
* client is under amplification limit, until it completes
* handshake.
*
* forget qrx so channel can create a new one
* with valid initial encryption level keys.
*/
if (qrx != NULL) {
qrx_src = qrx;
qrx = NULL;
}
}
qrx_ref = NULL;
if (qrx != NULL) {
/*
* if we are here, then client is validated via retry packet
* (client sent a valid token). In this case the qrx has valid
* secrets set for QUIC initial level encryption. We can pass
* reference to qrx to newly created channel.
*
* Note: port_bind_channel()/channel becomes owner of qrx_ref.
*/
qrx_ref = ossl_qrx_newref(qrx);
if (qrx_ref == NULL)
goto undesirable;
}
port_bind_channel(port, &e->peer, &hdr.dst_conn_id,
&odcid, qrx_ref, &new_ch);
/*
* if packet validates it gets moved to channel, we've just bound
* to port.
*/
if (new_ch == NULL)
goto undesirable;
/*
* Generate a token for sending in a later NEW_TOKEN frame
*/
if (gen_new_token == 1)
generate_new_token(new_ch, &e->peer);
if (qrx_src != NULL) {
/*
* Time to reinject packets from qrx to channel before
* qrx will be destroyed here.
*/
while (ossl_qrx_read_pkt(qrx_src, &qrx_pkt) == 1)
ossl_quic_channel_inject_pkt(new_ch, qrx_pkt);
ossl_qrx_update_pn_space(qrx_src, new_ch->qrx);
/*
* transfer ownership back to qrx;
*/
qrx = qrx_src;
qrx_src = NULL;
}
/*
* If function reaches this place, then packet got validated in
* ossl_qrx_validate_initial_packet(). Keep in mind the function
* ossl_qrx_validate_initial_packet() decrypts the packet to validate it.
* If packet validation was successful (and it was because we are here),
* then the function puts the packet to qrx->rx_pending. We must not call
* ossl_qrx_inject_urxe() here now, because we don't want to insert
* the packet to qrx->urx_pending which keeps packet waiting for decryption.
*
* We are going to call ossl_quic_demux_release_urxe() to dispose buffer
* which still holds encrypted data.
*/
undesirable:
ossl_qrx_free(qrx); /* releases reference */
ossl_qrx_free(qrx_src);
ossl_quic_demux_release_urxe(port->demux, e);
}