QUIC: Echo PATH_CHALLENGE frames as PATH_RESPONSE frames
authorHugo Landau <hlandau@openssl.org>
Tue, 25 Jul 2023 10:32:24 +0000 (11:32 +0100)
committerMatt Caswell <matt@openssl.org>
Tue, 8 Aug 2023 13:33:42 +0000 (14:33 +0100)
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/21547)

include/internal/quic_tserver.h
ssl/quic/quic_rx_depack.c
ssl/quic/quic_tserver.c
ssl/quic/quic_txp.c
test/quic_multistream_test.c

index 1edb890a9473bbbb04b84539a73bb1a9e11de97d..3cbbc279a60c25202d346d4f88b91a38b8dfc9bd 100644 (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
index a3db49b7ff1042d9e8db97a4bfd88c1b70fb6056..74d84a177550233507033b8945da2591119dc1e4 100644 (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 */
index 233b71657ec110363b54fc99f45533fbf5b3d60e..9bd32146c306c745c476d1f6f9db4146973cd183 100644 (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);
+}
index 461dfaf238db5965755f8ab60922f9b8b28b8d18..25f3c14c4c7cbd22fb01eef82c827489379820af 100644 (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;
index 3d7d39f7525a6ad514f856ba6f42e6cd0c824a68..f49a440ab6939fce4e2de35a97af78602bfa0833 100644 (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)