From: Hugo Landau Date: Tue, 6 Jun 2023 15:25:12 +0000 (+0100) Subject: QUIC CONFORMANCE: Validate preferred_addr transport parameter X-Git-Tag: openssl-3.2.0-alpha1~434 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=54bd1f24d48c668c125f59e4c63bf9af3fb2f954;p=thirdparty%2Fopenssl.git QUIC CONFORMANCE: Validate preferred_addr transport parameter Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell Reviewed-by: Paul Dale (Merged from https://github.com/openssl/openssl/pull/21135) --- diff --git a/include/internal/quic_types.h b/include/internal/quic_types.h index 6b86076ff8f..cc41adc5ab1 100644 --- a/include/internal/quic_types.h +++ b/include/internal/quic_types.h @@ -101,6 +101,13 @@ static ossl_unused ossl_inline int ossl_quic_conn_id_eq(const QUIC_CONN_ID *a, # define QUIC_STATELESS_RESET_TOKEN_LEN 16 +/* + * An encoded preferred_addr transport parameter cannot be longer than this + * number of bytes. + */ +# define QUIC_MIN_ENCODED_PREFERRED_ADDR_LEN 41 +# define QUIC_MAX_ENCODED_PREFERRED_ADDR_LEN 61 + # endif #endif diff --git a/include/internal/quic_wire.h b/include/internal/quic_wire.h index 8a1ef34ead6..d6423415eaf 100644 --- a/include/internal/quic_wire.h +++ b/include/internal/quic_wire.h @@ -764,6 +764,19 @@ int ossl_quic_wire_decode_transport_param_cid(PACKET *pkt, uint64_t *id, QUIC_CONN_ID *cid); +/* + * Decodes a QUIC transport parameter TLV containing a preferred_address. + */ +typedef struct quic_preferred_addr_st { + uint16_t ipv4_port, ipv6_port; + unsigned char ipv4[4], ipv6[16]; + unsigned char stateless_reset_token[QUIC_STATELESS_RESET_TOKEN_LEN]; + QUIC_CONN_ID cid; +} QUIC_PREFERRED_ADDR; + +int ossl_quic_wire_decode_transport_param_preferred_addr(PACKET *pkt, + QUIC_PREFERRED_ADDR *p); + # endif #endif diff --git a/ssl/quic/quic_channel.c b/ssl/quic/quic_channel.c index cf19d3b51c9..41d42896bc6 100644 --- a/ssl/quic/quic_channel.c +++ b/ssl/quic/quic_channel.c @@ -1344,18 +1344,38 @@ static int ch_on_transport_params(const unsigned char *params, break; case QUIC_TPARAM_PREFERRED_ADDR: - /* TODO(QUIC): Handle preferred address. */ - if (ch->is_server) { - reason = TP_REASON_SERVER_ONLY("PREFERRED_ADDR"); - goto malformed; + { + /* TODO(QUIC): Handle preferred address. */ + QUIC_PREFERRED_ADDR pfa; + + /* + * RFC 9000 s. 18.2: "A server that chooses a zero-length + * connection ID MUST NOT provide a preferred address. + * Similarly, a server MUST NOT include a zero-length connection + * ID in this transport parameter. A client MUST treat a + * violation of these requirements as a connection error of type + * TRANSPORT_PARAMETER_ERROR." + */ + if (ch->is_server) { + reason = TP_REASON_SERVER_ONLY("PREFERRED_ADDR"); + goto malformed; + } + + if (ch->cur_remote_dcid.id_len == 0) { + reason = "PREFERRED_ADDR provided for zero-length CID"; + goto malformed; + } + + if (!ossl_quic_wire_decode_transport_param_preferred_addr(&pkt, &pfa)) { + reason = TP_REASON_MALFORMED("PREFERRED_ADDR"); + goto malformed; + } + + if (pfa.cid.id_len == 0) { + reason = "zero-length CID in PREFERRED_ADDR"; + goto malformed; + } } - - body = ossl_quic_wire_decode_transport_param_bytes(&pkt, &id, &len); - if (body == NULL) { - reason = TP_REASON_MALFORMED("PREFERRED_ADDR"); - goto malformed; - } - break; case QUIC_TPARAM_DISABLE_ACTIVE_MIGRATION: @@ -1488,7 +1508,7 @@ static int ch_generate_transport_params(QUIC_CHANNEL *ch) goto err; if (!ossl_quic_wire_encode_transport_param_int(&wpkt, QUIC_TPARAM_ACTIVE_CONN_ID_LIMIT, - 2)) + QUIC_MIN_ACTIVE_CONN_ID_LIMIT)) goto err; if (!ossl_quic_wire_encode_transport_param_int(&wpkt, QUIC_TPARAM_INITIAL_MAX_DATA, diff --git a/ssl/quic/quic_wire.c b/ssl/quic/quic_wire.c index 0545b8f9569..2412e9afa5c 100644 --- a/ssl/quic/quic_wire.c +++ b/ssl/quic/quic_wire.c @@ -937,3 +937,39 @@ int ossl_quic_wire_decode_transport_param_cid(PACKET *pkt, memcpy(cid->id, body, cid->id_len); return 1; } + +int ossl_quic_wire_decode_transport_param_preferred_addr(PACKET *pkt, + QUIC_PREFERRED_ADDR *p) +{ + const unsigned char *body; + uint64_t id; + size_t len = 0; + PACKET pkt2; + unsigned int ipv4_port, ipv6_port, cidl; + + body = ossl_quic_wire_decode_transport_param_bytes(pkt, &id, &len); + if (body == NULL + || len < QUIC_MIN_ENCODED_PREFERRED_ADDR_LEN + || len > QUIC_MAX_ENCODED_PREFERRED_ADDR_LEN + || id != QUIC_TPARAM_PREFERRED_ADDR) + return 0; + + if (!PACKET_buf_init(&pkt2, body, len)) + return 0; + + if (!PACKET_copy_bytes(&pkt2, p->ipv4, sizeof(p->ipv4)) + || !PACKET_get_net_2(&pkt2, &ipv4_port) + || !PACKET_copy_bytes(&pkt2, p->ipv6, sizeof(p->ipv6)) + || !PACKET_get_net_2(&pkt2, &ipv6_port) + || !PACKET_get_1(&pkt2, &cidl) + || cidl > QUIC_MAX_CONN_ID_LEN + || !PACKET_copy_bytes(&pkt2, p->cid.id, cidl) + || !PACKET_copy_bytes(&pkt2, p->stateless_reset_token, + sizeof(p->stateless_reset_token))) + return 0; + + p->ipv4_port = (uint16_t)ipv4_port; + p->ipv6_port = (uint16_t)ipv6_port; + p->cid.id_len = (unsigned char)cidl; + return 1; +}