From: Hugo Landau Date: Tue, 25 Jul 2023 10:32:24 +0000 (+0100) Subject: QUIC: Echo PATH_CHALLENGE frames as PATH_RESPONSE frames X-Git-Tag: openssl-3.2.0-alpha1~290 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=7eb330ff7aa5580d7d97f2d183606c2d6bbbb449;p=thirdparty%2Fopenssl.git QUIC: Echo PATH_CHALLENGE frames as PATH_RESPONSE frames Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/21547) --- diff --git a/include/internal/quic_tserver.h b/include/internal/quic_tserver.h index 1edb890a947..3cbbc279a60 100644 --- a/include/internal/quic_tserver.h +++ b/include/internal/quic_tserver.h @@ -187,6 +187,14 @@ int ossl_quic_tserver_shutdown(QUIC_TSERVER *srv); /* Force generation of an ACK-eliciting packet. */ int ossl_quic_tserver_ping(QUIC_TSERVER *srv); +/* Set tracing callback on channel. */ +void ossl_quic_tserver_set_msg_callback(QUIC_TSERVER *srv, + void (*f)(int write_p, int version, + int content_type, + const void *buf, size_t len, + SSL *ssl, void *arg), + void *arg); + # endif #endif diff --git a/ssl/quic/quic_rx_depack.c b/ssl/quic/quic_rx_depack.c index a3db49b7ff1..74d84a17755 100644 --- a/ssl/quic/quic_rx_depack.c +++ b/ssl/quic/quic_rx_depack.c @@ -875,11 +875,19 @@ static int depack_do_frame_retire_conn_id(PACKET *pkt, return 1; } +static void free_path_response(unsigned char *buf, size_t buf_len, void *arg) +{ + OPENSSL_free(buf); +} + static int depack_do_frame_path_challenge(PACKET *pkt, QUIC_CHANNEL *ch, OSSL_ACKM_RX_PKT *ackm_data) { uint64_t frame_data = 0; + unsigned char *encoded = NULL; + size_t encoded_len; + WPACKET wpkt; if (!ossl_quic_wire_decode_frame_path_challenge(pkt, &frame_data)) { ossl_quic_channel_raise_protocol_error(ch, @@ -889,9 +897,41 @@ static int depack_do_frame_path_challenge(PACKET *pkt, return 0; } - /* TODO(QUIC): ADD CODE to send |frame_data| to the ch manager */ + /* + * RFC 9000 s. 8.2.2: On receiving a PATH_CHALLENGE frame, an endpoint MUST + * respond by echoing the data contained in the PATH_CHALLENGE frame in a + * PATH_RESPONSE frame. + * + * TODO(QUIC): We should try to avoid allocation here in the future. + */ + encoded_len = sizeof(uint64_t) + 1; + if ((encoded = OPENSSL_malloc(encoded_len)) == NULL) + goto err; + + if (!WPACKET_init_static_len(&wpkt, encoded, encoded_len, 0)) + goto err; + + if (!ossl_quic_wire_encode_frame_path_response(&wpkt, frame_data)) { + WPACKET_cleanup(&wpkt); + goto err; + } + + WPACKET_finish(&wpkt); + + if (!ossl_quic_cfq_add_frame(ch->cfq, 0, QUIC_PN_SPACE_APP, + OSSL_QUIC_FRAME_TYPE_PATH_RESPONSE, + encoded, encoded_len, + free_path_response, NULL)) + goto err; return 1; + +err: + OPENSSL_free(encoded); + ossl_quic_channel_raise_protocol_error(ch, QUIC_ERR_INTERNAL_ERROR, + OSSL_QUIC_FRAME_TYPE_PATH_CHALLENGE, + "internal error"); + return 0; } static int depack_do_frame_path_response(PACKET *pkt, @@ -1224,6 +1264,7 @@ static int depack_process_frames(QUIC_CHANNEL *ch, PACKET *pkt, } if (!depack_do_frame_path_challenge(pkt, ch, ackm_data)) return 0; + break; case OSSL_QUIC_FRAME_TYPE_PATH_RESPONSE: /* PATH_RESPONSE frames are valid in 1RTT packets */ diff --git a/ssl/quic/quic_tserver.c b/ssl/quic/quic_tserver.c index 233b71657ec..9bd32146c30 100644 --- a/ssl/quic/quic_tserver.c +++ b/ssl/quic/quic_tserver.c @@ -511,3 +511,14 @@ int ossl_quic_tserver_ping(QUIC_TSERVER *srv) ossl_quic_reactor_tick(ossl_quic_channel_get_reactor(srv->ch), 0); return 1; } + +void ossl_quic_tserver_set_msg_callback(QUIC_TSERVER *srv, + void (*f)(int write_p, int version, + int content_type, + const void *buf, size_t len, + SSL *ssl, void *arg), + void *arg) +{ + ossl_quic_channel_set_msg_callback(srv->ch, f, NULL); + ossl_quic_channel_set_msg_callback_arg(srv->ch, arg); +} diff --git a/ssl/quic/quic_txp.c b/ssl/quic/quic_txp.c index 461dfaf238d..25f3c14c4c7 100644 --- a/ssl/quic/quic_txp.c +++ b/ssl/quic/quic_txp.c @@ -824,6 +824,7 @@ out: return pkts_done > 0 ? TX_PACKETISER_RES_SENT_PKT : res; } + static const struct archetype_data archetypes[QUIC_ENC_LEVEL_NUM][TX_PACKETISER_ARCHETYPE_NUM] = { /* EL 0(INITIAL) */ { @@ -1023,7 +1024,7 @@ static const struct archetype_data archetypes[QUIC_ENC_LEVEL_NUM][TX_PACKETISER_ /*allow_crypto =*/ 1, /*allow_handshake_done =*/ 1, /*allow_path_challenge =*/ 0, - /*allow_path_response =*/ 0, + /*allow_path_response =*/ 1, /*allow_new_conn_id =*/ 1, /*allow_retire_conn_id =*/ 1, /*allow_stream_rel =*/ 1, @@ -1043,7 +1044,7 @@ static const struct archetype_data archetypes[QUIC_ENC_LEVEL_NUM][TX_PACKETISER_ /*allow_crypto =*/ 1, /*allow_handshake_done =*/ 1, /*allow_path_challenge =*/ 0, - /*allow_path_response =*/ 0, + /*allow_path_response =*/ 1, /*allow_new_conn_id =*/ 1, /*allow_retire_conn_id =*/ 1, /*allow_stream_rel =*/ 1, @@ -1339,6 +1340,10 @@ static int txp_should_try_staging(OSSL_QUIC_TX_PACKETISER *txp, if (a.allow_new_token) return 1; break; + case OSSL_QUIC_FRAME_TYPE_PATH_RESPONSE: + if (a.allow_path_response) + return 1; + break; default: if (a.allow_cfq_other) return 1; diff --git a/test/quic_multistream_test.c b/test/quic_multistream_test.c index 3d7d39f7525..f49a440ab69 100644 --- a/test/quic_multistream_test.c +++ b/test/quic_multistream_test.c @@ -79,6 +79,7 @@ struct helper { int (*qtf_packet_plain_cb)(struct helper *h, QUIC_PKT_HDR *hdr, unsigned char *buf, size_t buf_len); uint64_t inject_word0, inject_word1; + uint64_t scratch0, scratch1; }; struct helper_local { @@ -2909,6 +2910,122 @@ static const struct script_op script_40[] = { OP_END }; +/* 41. Fault injection - PATH_CHALLENGE yields PATH_RESPONSE */ +static const uint64_t path_challenge = UINT64_C(0xbdeb9451169c83aa); + +static int script_41_inject_plain(struct helper *h, QUIC_PKT_HDR *hdr, + unsigned char *buf, size_t len) +{ + int ok = 0; + WPACKET wpkt; + unsigned char frame_buf[16]; + size_t written; + + if (h->inject_word0 == 0) + return 1; + + if (!TEST_true(WPACKET_init_static_len(&wpkt, frame_buf, + sizeof(frame_buf), 0))) + return 0; + + if (!TEST_true(WPACKET_quic_write_vlint(&wpkt, OSSL_QUIC_FRAME_TYPE_PATH_CHALLENGE)) + || !TEST_true(WPACKET_put_bytes_u64(&wpkt, path_challenge))) + goto err; + + if (!TEST_true(WPACKET_get_total_written(&wpkt, &written)) + || !TEST_size_t_eq(written, 9)) + goto err; + + if (!qtest_fault_prepend_frame(h->qtf, frame_buf, written)) + goto err; + + --h->inject_word0; + ok = 1; +err: + if (ok) + WPACKET_finish(&wpkt); + else + WPACKET_cleanup(&wpkt); + return ok; +} + +static void script_41_trace(int write_p, int version, int content_type, + const void *buf, size_t len, SSL *ssl, void *arg) +{ + uint64_t frame_type, frame_data; + int was_minimal; + struct helper *h = arg; + PACKET pkt; + + if (version != OSSL_QUIC1_VERSION + || content_type != SSL3_RT_QUIC_FRAME_FULL + || len < 1) + return; + + if (!TEST_true(PACKET_buf_init(&pkt, buf, len))) { + ++h->scratch1; + return; + } + + if (!TEST_true(ossl_quic_wire_peek_frame_header(&pkt, &frame_type, + &was_minimal))) { + ++h->scratch1; + return; + } + + if (frame_type != OSSL_QUIC_FRAME_TYPE_PATH_RESPONSE) + return; + + if (!TEST_true(ossl_quic_wire_decode_frame_path_response(&pkt, &frame_data)) + || !TEST_uint64_t_eq(frame_data, path_challenge)) { + ++h->scratch1; + return; + } + + ++h->scratch0; +} + +static int script_41_setup(struct helper *h, const struct script_op *op) +{ + ossl_quic_tserver_set_msg_callback(h->s, script_41_trace, h); + return 1; +} + +static int script_41_check(struct helper *h, const struct script_op *op) +{ + /* At least one valid challenge/response echo? */ + if (!TEST_uint64_t_gt(h->scratch0, 0)) + return 0; + + /* No failed tests? */ + if (!TEST_uint64_t_eq(h->scratch1, 0)) + return 0; + + return 1; +} + +static const struct script_op script_41[] = { + OP_S_SET_INJECT_PLAIN (script_41_inject_plain) + OP_C_SET_ALPN ("ossltest") + OP_C_CONNECT_WAIT () + OP_CHECK (script_41_setup, 0) + + OP_C_WRITE (DEFAULT, "apple", 5) + OP_S_BIND_STREAM_ID (a, C_BIDI_ID(0)) + OP_S_READ_EXPECT (a, "apple", 5) + + OP_SET_INJECT_WORD (1, 0) + + OP_S_WRITE (a, "orange", 6) + OP_C_READ_EXPECT (DEFAULT, "orange", 6) + + OP_C_WRITE (DEFAULT, "strawberry", 10) + OP_S_READ_EXPECT (a, "strawberry", 10) + + OP_CHECK (script_41_check, 0) + OP_END +}; + static const struct script_op *const scripts[] = { script_1, script_2, @@ -2950,6 +3067,7 @@ static const struct script_op *const scripts[] = { script_38, script_39, script_40, + script_41, }; static int test_script(int idx)