Add the ability to mutate QUIC packets before they are written
authorMatt Caswell <matt@openssl.org>
Tue, 29 Nov 2022 16:04:23 +0000 (16:04 +0000)
committerHugo Landau <hlandau@openssl.org>
Wed, 22 Feb 2023 05:33:23 +0000 (05:33 +0000)
We add callbacks so that QUIC packets can be modified by the test
framework before they are encrypted and written to the network. This
enables us to simulate badly behaving endpoints.

Reviewed-by: Hugo Landau <hlandau@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/20030)

include/internal/quic_channel.h
include/internal/quic_record_tx.h
include/internal/quic_tserver.h
ssl/quic/quic_channel.c
ssl/quic/quic_record_tx.c
ssl/quic/quic_tserver.c

index 83eb90a66258b94483ba0e1f68735d9ef8f5f5ae..a4039d8c0221b4aabcc8af05ca4fb2d6d8ae7318 100644 (file)
@@ -73,6 +73,12 @@ QUIC_CHANNEL *ossl_quic_channel_new(const QUIC_CHANNEL_ARGS *args);
 /* No-op if ch is NULL. */
 void ossl_quic_channel_free(QUIC_CHANNEL *ch);
 
+/* Set mutator callbacks for test framework support */
+int ossl_quic_channel_set_mutator(QUIC_CHANNEL *ch,
+                                  ossl_mutate_packet_cb mutatecb,
+                                  ossl_finish_mutate_cb finishmutatecb,
+                                  void *mutatearg);
+
 /*
  * Connection Lifecycle Events
  * ===========================
index ba2cf7fe788e52dede31fee065b3009d557c1e98..2cc7333c029a9e0d86c3e0a48f1664e31627d670 100644 (file)
  * QUIC Record Layer - TX
  * ======================
  */
+typedef struct ossl_qtx_iovec_st {
+    const unsigned char    *buf;
+    size_t                  buf_len;
+} OSSL_QTX_IOVEC;
+
 typedef struct ossl_qtx_st OSSL_QTX;
 
+typedef int (*ossl_mutate_packet_cb)(const QUIC_PKT_HDR *hdrin,
+                                     const OSSL_QTX_IOVEC *iovecin, size_t numin,
+                                     QUIC_PKT_HDR **hdrout,
+                                     const OSSL_QTX_IOVEC **iovecout,
+                                     size_t *numout,
+                                     void *arg);
+
+typedef void (*ossl_finish_mutate_cb)(void *arg);
+
 typedef struct ossl_qtx_args_st {
     OSSL_LIB_CTX   *libctx;
     const char     *propq;
@@ -40,6 +54,10 @@ OSSL_QTX *ossl_qtx_new(const OSSL_QTX_ARGS *args);
 /* Frees the QTX. */
 void ossl_qtx_free(OSSL_QTX *qtx);
 
+/* Set mutator callbacks for test framework support */
+void ossl_qtx_set_mutator(OSSL_QTX *qtx, ossl_mutate_packet_cb mutatecb,
+                          ossl_finish_mutate_cb finishmutatecb, void *mutatearg);
+
 /*
  * Secret Management
  * -----------------
@@ -114,10 +132,6 @@ uint32_t ossl_qrl_get_suite_cipher_tag_len(uint32_t suite_id);
  * Packet Transmission
  * -------------------
  */
-typedef struct ossl_qtx_iovec_st {
-    const unsigned char    *buf;
-    size_t                  buf_len;
-} OSSL_QTX_IOVEC;
 
 typedef struct ossl_qtx_pkt_st {
     /* Logical packet header to be serialized. */
index 438c68d20a970b351f482f86b4836db2aceae308..a19ec882ef73f34a15dab00457414e9b62f60322 100644 (file)
@@ -41,6 +41,12 @@ QUIC_TSERVER *ossl_quic_tserver_new(const QUIC_TSERVER_ARGS *args,
 
 void ossl_quic_tserver_free(QUIC_TSERVER *srv);
 
+/* Set mutator callbacks for test framework support */
+int ossl_quic_tserver_set_mutator(QUIC_TSERVER *srv,
+                                  ossl_mutate_packet_cb mutatecb,
+                                  ossl_finish_mutate_cb finishmutatecb,
+                                  void *mutatearg);
+
 /* Advances the state machine. */
 int ossl_quic_tserver_tick(QUIC_TSERVER *srv);
 
index bffd0d3244ed6120d9c8168c8866331b44f69d91..6a0cc6d0049a6f8c60c98a3498f92bd7f5bde430 100644 (file)
@@ -347,6 +347,19 @@ void ossl_quic_channel_free(QUIC_CHANNEL *ch)
     OPENSSL_free(ch);
 }
 
+/* Set mutator callbacks for test framework support */
+int ossl_quic_channel_set_mutator(QUIC_CHANNEL *ch,
+                                  ossl_mutate_packet_cb mutatecb,
+                                  ossl_finish_mutate_cb finishmutatecb,
+                                  void *mutatearg)
+{
+    if (ch->qtx == NULL)
+        return 0;
+
+    ossl_qtx_set_mutator(ch->qtx, mutatecb, finishmutatecb, mutatearg);
+    return 1;
+}
+
 int ossl_quic_channel_get_peer_addr(QUIC_CHANNEL *ch, BIO_ADDR *peer_addr)
 {
     *peer_addr = ch->cur_peer_addr;
index dddb29663cab34d9d5d25b7b2c564cd3478e32ee..28ebc436bb2dccf01cd22eb87147322cc44a1c08 100644 (file)
@@ -90,6 +90,10 @@ struct ossl_qtx_st {
      * confidentiality limit.
      */
     uint64_t                    epoch_pkt_count;
+
+    ossl_mutate_packet_cb mutatecb;
+    ossl_finish_mutate_cb finishmutatecb;
+    void *mutatearg;
 };
 
 /* Instantiates a new QTX. */
@@ -141,6 +145,15 @@ void ossl_qtx_free(OSSL_QTX *qtx)
     OPENSSL_free(qtx);
 }
 
+/* Set mutator callbacks for test framework support */
+void ossl_qtx_set_mutator(OSSL_QTX *qtx, ossl_mutate_packet_cb mutatecb,
+                          ossl_finish_mutate_cb finishmutatecb, void *mutatearg)
+{
+    qtx->mutatecb       = mutatecb;
+    qtx->finishmutatecb = finishmutatecb;
+    qtx->mutatearg      = mutatearg;
+}
+
 int ossl_qtx_provide_secret(OSSL_QTX              *qtx,
                             uint32_t               enc_level,
                             uint32_t               suite_id,
@@ -414,7 +427,7 @@ int ossl_qtx_calculate_plaintext_payload_len(OSSL_QTX *qtx, uint32_t enc_level,
  */
 #define QTX_FAIL_INSUFFICIENT_LEN   (-2)
 
-static int qtx_write_hdr(OSSL_QTX *qtx, const OSSL_QTX_PKT *pkt, TXE *txe,
+static int qtx_write_hdr(OSSL_QTX *qtx, const QUIC_PKT_HDR *hdr, TXE *txe,
                          QUIC_PKT_HDR_PTRS *ptrs)
 {
     WPACKET wpkt;
@@ -424,8 +437,8 @@ static int qtx_write_hdr(OSSL_QTX *qtx, const OSSL_QTX_PKT *pkt, TXE *txe,
                                  txe->alloc_len - txe->data_len, 0))
         return 0;
 
-    if (!ossl_quic_wire_encode_pkt_hdr(&wpkt, pkt->hdr->dst_conn_id.id_len,
-                                       pkt->hdr, ptrs)
+    if (!ossl_quic_wire_encode_pkt_hdr(&wpkt, hdr->dst_conn_id.id_len,
+                                       hdr, ptrs)
         || !WPACKET_get_total_written(&wpkt, &l)) {
         WPACKET_finish(&wpkt);
         return 0;
@@ -534,6 +547,9 @@ static int qtx_write(OSSL_QTX *qtx, const OSSL_QTX_PKT *pkt, TXE *txe,
     QUIC_PKT_HDR_PTRS ptrs;
     unsigned char *hdr_start;
     OSSL_QRL_ENC_LEVEL *el = NULL;
+    QUIC_PKT_HDR *hdr;
+    const OSSL_QTX_IOVEC *iovec;
+    size_t num_iovec;
 
     /*
      * Determine if the packet needs encryption and the minimum conceivable
@@ -558,8 +574,25 @@ static int qtx_write(OSSL_QTX *qtx, const OSSL_QTX_PKT *pkt, TXE *txe,
         goto err;
     }
 
+    /* Set some fields in the header we are responsible for. */
+    if (pkt->hdr->type == QUIC_PKT_TYPE_1RTT)
+        pkt->hdr->key_phase = (unsigned char)(el->key_epoch & 1);
+
+    /* If we are running tests then mutate_packet may be non NULL */
+    if (qtx->mutatecb != NULL) {
+        if (!qtx->mutatecb(pkt->hdr, pkt->iovec, pkt->num_iovec, &hdr,
+                           &iovec, &num_iovec, qtx->mutatearg)) {
+            ret = QTX_FAIL_GENERIC;
+            goto err;
+        }
+    } else {
+        hdr = pkt->hdr;
+        iovec = pkt->iovec;
+        num_iovec = pkt->num_iovec;
+    }
+
     /* Walk the iovecs to determine actual input payload length. */
-    iovec_cur_init(&cur, pkt->iovec, pkt->num_iovec);
+    iovec_cur_init(&cur, iovec, num_iovec);
 
     if (cur.bytes_remaining == 0) {
         /* No zero-length payloads allowed. */
@@ -573,10 +606,10 @@ static int qtx_write(OSSL_QTX *qtx, const OSSL_QTX_PKT *pkt, TXE *txe,
                                 : cur.bytes_remaining;
 
     /* Determine header length. */
-    pkt->hdr->data  = NULL;
-    pkt->hdr->len   = payload_len;
-    pred_hdr_len = ossl_quic_wire_get_encoded_pkt_hdr_len(pkt->hdr->dst_conn_id.id_len,
-                                                          pkt->hdr);
+    hdr->data  = NULL;
+    hdr->len   = payload_len;
+    pred_hdr_len = ossl_quic_wire_get_encoded_pkt_hdr_len(hdr->dst_conn_id.id_len,
+                                                          hdr);
     if (pred_hdr_len == 0) {
         ret = QTX_FAIL_GENERIC;
         goto err;
@@ -590,14 +623,10 @@ static int qtx_write(OSSL_QTX *qtx, const OSSL_QTX_PKT *pkt, TXE *txe,
         goto err;
     }
 
-    /* Set some fields in the header we are responsible for. */
-    if (pkt->hdr->type == QUIC_PKT_TYPE_1RTT)
-        pkt->hdr->key_phase = (unsigned char)(el->key_epoch & 1);
-
-    if (ossl_quic_pkt_type_has_pn(pkt->hdr->type)) {
+    if (ossl_quic_pkt_type_has_pn(hdr->type)) {
         if (!ossl_quic_wire_encode_pkt_hdr_pn(pkt->pn,
-                                              pkt->hdr->pn,
-                                              pkt->hdr->pn_len)) {
+                                              hdr->pn,
+                                              hdr->pn_len)) {
             ret = QTX_FAIL_GENERIC;
             goto err;
         }
@@ -605,7 +634,7 @@ static int qtx_write(OSSL_QTX *qtx, const OSSL_QTX_PKT *pkt, TXE *txe,
 
     /* Append the header to the TXE. */
     hdr_start = txe_data(txe) + txe->data_len;
-    if (!qtx_write_hdr(qtx, pkt, txe, &ptrs)) {
+    if (!qtx_write_hdr(qtx, hdr, txe, &ptrs)) {
         ret = QTX_FAIL_GENERIC;
         goto err;
     }
@@ -638,6 +667,8 @@ static int qtx_write(OSSL_QTX *qtx, const OSSL_QTX_PKT *pkt, TXE *txe,
         assert(txe->data_len - orig_data_len == pkt_len);
     }
 
+    if (qtx->finishmutatecb != NULL)
+        qtx->finishmutatecb(qtx->mutatearg);
     return 1;
 
 err:
@@ -646,6 +677,8 @@ err:
      * TXE.
      */
     txe->data_len = orig_data_len;
+    if (qtx->finishmutatecb != NULL)
+        qtx->finishmutatecb(qtx->mutatearg);
     return ret;
 }
 
index 5a03aa53270dc4fa293e00b69efe623196d39191..2b5f04ac5a9ff5d2320d5f057aa7e0802bddef56 100644 (file)
@@ -122,6 +122,16 @@ void ossl_quic_tserver_free(QUIC_TSERVER *srv)
     OPENSSL_free(srv);
 }
 
+/* Set mutator callbacks for test framework support */
+int ossl_quic_tserver_set_mutator(QUIC_TSERVER *srv,
+                                  ossl_mutate_packet_cb mutatecb,
+                                  ossl_finish_mutate_cb finishmutatecb,
+                                  void *mutatearg)
+{
+    return ossl_quic_channel_set_mutator(srv->ch, mutatecb, finishmutatecb,
+                                         mutatearg);
+}
+
 int ossl_quic_tserver_tick(QUIC_TSERVER *srv)
 {
     ossl_quic_reactor_tick(ossl_quic_channel_get_reactor(srv->ch));