mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-18 12:39:28 +00:00
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.
260 lines
8.3 KiB
C
260 lines
8.3 KiB
C
/* Provenance: openssl/openssl port_default_packet_handler (fixed).
|
|
* repo: openssl/openssl
|
|
* commit: 5097ec4dae239112498abb722e255ea60ef7ac48
|
|
* parent: 58633b658fe74095c8cdde191e4ce1bf94f8c5f9
|
|
* commit_url: https://github.com/openssl/openssl/commit/5097ec4dae239112498abb722e255ea60ef7ac48
|
|
* cve: CVE-2026-42764
|
|
* 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;
|
|
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;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
port_bind_channel(port, &e->peer, &hdr.dst_conn_id,
|
|
&odcid, qrx, &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 != NULL) {
|
|
/*
|
|
* The qrx belongs to channel now, so don't free it.
|
|
*/
|
|
qrx = NULL;
|
|
} else {
|
|
/*
|
|
* We still need to salvage packets from almost forgotten qrx
|
|
* and pass them to channel.
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
ossl_qrx_free(qrx_src);
|
|
ossl_quic_demux_release_urxe(port->demux, e);
|
|
}
|