QUIC: Echo PATH_CHALLENGE frames as PATH_RESPONSE frames

Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/21547)
This commit is contained in:
Hugo Landau 2023-07-25 11:32:24 +01:00 committed by Matt Caswell
parent 7c729851d1
commit 7eb330ff7a
5 changed files with 186 additions and 3 deletions

View File

@ -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

View File

@ -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 */

View File

@ -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);
}

View File

@ -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;

View File

@ -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)