Support SSL_OP_CLEANSE_PLAINTEXT on QUIC streams
authorTomas Mraz <tomas@openssl.org>
Mon, 12 Jun 2023 16:16:35 +0000 (18:16 +0200)
committerTomas Mraz <tomas@openssl.org>
Fri, 23 Jun 2023 12:31:45 +0000 (14:31 +0200)
Reviewed-by: Paul Dale <pauli@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/21182)

12 files changed:
doc/man3/SSL_CTX_set_options.pod
include/internal/quic_sf_list.h
include/internal/quic_ssl.h
include/internal/quic_stream.h
include/internal/ring_buf.h
ssl/quic/quic_channel.c
ssl/quic/quic_impl.c
ssl/quic/quic_rstream.c
ssl/quic/quic_sf_list.c
ssl/quic/quic_sstream.c
ssl/ssl_lib.c
test/quic_stream_test.c

index c7170b160aa572820e8161e262cedc4cde9122f1..44d2fd1342c943e391190bdd379631e6d2a4095d 100644 (file)
@@ -443,6 +443,43 @@ renegotiation between OpenSSL clients and unpatched servers B<only>, while
 B<SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION> allows initial connections
 and renegotiation between OpenSSL and unpatched clients or servers.
 
+=head2 Applicability of options to QUIC connections and streams
+
+These options apply to SSL objects referencing a QUIC connection:
+
+=over 4
+
+=item SSL_OP_ALLOW_NO_DHE_KEX
+
+=item SSL_OP_NO_TX_CERTIFICATE_COMPRESSION
+
+=item SSL_OP_NO_RX_CERTIFICATE_COMPRESSION
+
+=item SSL_OP_NO_TICKET
+
+=item SSL_OP_PRIORITIZE_CHACHA
+
+=back
+
+Other options do not have an effect and will be ignored.
+
+These options apply to SSL objects referencing a QUIC stream:
+
+=over 4
+
+=item SSL_OP_CLEANSE_PLAINTEXT
+
+=back
+
+Other options do not have an effect and will be ignored.
+
+If an SSL object is a QUIC connection object with a default stream attached,
+only the stream-relevant options are applied. If it is a QUIC connection
+without a default stream, the stream-relevant options are ignored.
+
+Connection and stream relevant options are initialized from the options
+set on SSL_CTX before the connection or stream objects are created.
+
 =head1 RETURN VALUES
 
 SSL_CTX_set_options() and SSL_set_options() return the new options bit-mask
index f0efcea2bf3b506f613be281fb9441752d1824cc..2583ae28112dc341db369e4d47bee5a9ce1f66ae 100644 (file)
@@ -49,6 +49,8 @@ typedef struct sframe_list_st {
     uint64_t offset;
     /* Is head locked ? */
     int head_locked;
+    /* Cleanse data on release? */
+    int cleanse;
 } SFRAME_LIST;
 
 /*
index 28047f985c9acdf4c7f0750d8be77787ace3f18a..cfcd3a6b923e6bf3eed8b136546a7caa126fe061 100644 (file)
@@ -38,6 +38,7 @@ __owur int ossl_quic_key_update(SSL *s, int update_type);
 __owur int ossl_quic_get_key_update_type(const SSL *s);
 __owur int ossl_quic_num_ciphers(void);
 __owur const SSL_CIPHER *ossl_quic_get_cipher(unsigned int u);
+__owur int ossl_quic_set_ssl_op(SSL *ssl, uint64_t op);
 int ossl_quic_renegotiate_check(SSL *ssl, int initok);
 
 typedef struct quic_conn_st QUIC_CONNECTION;
index a1e88a4ab63b286e4a0ce22f1ed1457e4e649e42..4bd88d5b110a46f5e4ae0bd9c33bebee1822063e 100644 (file)
@@ -414,6 +414,11 @@ int ossl_quic_rstream_move_to_rbuf(QUIC_RSTREAM *qrs);
  * than currently occupied.
  */
 int ossl_quic_rstream_resize_rbuf(QUIC_RSTREAM *qrs, size_t rbuf_size);
+
+/*
+ * Sets flag to cleanse the buffered data when user reads it.
+ */
+void ossl_quic_rstream_set_cleanse(QUIC_RSTREAM *qrs, int cleanse);
 # endif
 
 #endif
index e7da3b32a0b5f7ef9640740457b1375a79b6b630..69b8df2aa83cdb5c96b57664cf7d741a93e0056d 100644 (file)
@@ -182,13 +182,31 @@ static ossl_inline int ring_buf_get_buf_at(const struct ring_buf *r,
 }
 
 static ossl_inline void ring_buf_cpop_range(struct ring_buf *r,
-                                            uint64_t start, uint64_t end)
+                                            uint64_t start, uint64_t end,
+                                            int cleanse)
 {
     assert(end >= start);
 
     if (start > r->ctail_offset)
         return;
 
+    if (cleanse && r->alloc > 0 && end > r->ctail_offset) {
+        size_t idx = r->ctail_offset % r->alloc;
+        uint64_t cleanse_end = end + 1;
+        size_t l;
+
+        if (cleanse_end > r->head_offset)
+            cleanse_end = r->head_offset;
+        l = (size_t)(cleanse_end - r->ctail_offset);
+        if (l > r->alloc - idx) {
+            OPENSSL_cleanse((unsigned char *)r->start + idx, r->alloc - idx);
+            l -= r->alloc - idx;
+            idx = 0;
+        }
+        if (l > 0)
+            OPENSSL_cleanse((unsigned char *)r->start + idx, l);
+    }
+
     r->ctail_offset = end + 1;
     /* Allow culling unpushed data */
     if (r->head_offset < r->ctail_offset)
index 586441f1382528bfd3556d868aa0f55ad430b480..53d86eac98871f0110d244e8a0f514345110046c 100644 (file)
@@ -2696,8 +2696,13 @@ static int ch_init_new_stream(QUIC_CHANNEL *ch, QUIC_STREAM *qs,
     if (can_send && (qs->sstream = ossl_quic_sstream_new(INIT_APP_BUF_LEN)) == NULL)
         goto err;
 
-    if (can_recv && (qs->rstream = ossl_quic_rstream_new(NULL, NULL, 0)) == NULL)
-        goto err;
+    if (can_recv) {
+        if ((qs->rstream = ossl_quic_rstream_new(NULL, NULL, 0)) == NULL)
+            goto err;
+        ossl_quic_rstream_set_cleanse(qs->rstream,
+                                      (ch->tls->ctx->options
+                                       & SSL_OP_CLEANSE_PLAINTEXT) != 0);
+    }
 
     /* TXFC */
     if (!ossl_quic_txfc_init(&qs->txfc, &ch->conn_txfc))
index 4f379e32ed2e550fcecc7cf181a80e407b1269a0..d2a79feb61b164688ea15724ace4eaf4126ce81f 100644 (file)
@@ -2798,6 +2798,24 @@ const SSL_CIPHER *ossl_quic_get_cipher(unsigned int u)
     return NULL;
 }
 
+int ossl_quic_set_ssl_op(SSL *ssl, uint64_t op)
+{
+    QCTX ctx;
+
+    if (!expect_quic_with_stream_lock(ssl, /*remote_init=*/-1, &ctx))
+        return 0;
+
+    if (ctx.xso->stream == NULL || ctx.xso->stream->rstream == NULL)
+        goto out;
+
+    ossl_quic_rstream_set_cleanse(ctx.xso->stream->rstream,
+                                  (op & SSL_OP_CLEANSE_PLAINTEXT) != 0);
+
+ out:
+    quic_unlock(ctx.qc);
+    return 1;
+}
+
 /*
  * Internal Testing APIs
  * =====================
index b35bd983af8ad6d99025c58ea9441ceace9f0aad..80970b084f4af26a4997b25964c98d188c4ea289 100644 (file)
@@ -120,7 +120,7 @@ static int read_internal(QUIC_RSTREAM *qrs, unsigned char *buf, size_t size,
 
     if (drop && offset != 0) {
         ret = ossl_sframe_list_drop_frames(&qrs->fl, offset);
-        ring_buf_cpop_range(&qrs->rbuf, 0, offset - 1);
+        ring_buf_cpop_range(&qrs->rbuf, 0, offset - 1, qrs->fl.cleanse);
     }
 
     if (ret) {
@@ -245,7 +245,7 @@ int ossl_quic_rstream_release_record(QUIC_RSTREAM *qrs, size_t read_len)
         return 0;
 
     if (offset > 0)
-        ring_buf_cpop_range(&qrs->rbuf, 0, offset - 1);
+        ring_buf_cpop_range(&qrs->rbuf, 0, offset - 1, qrs->fl.cleanse);
 
     if (qrs->rxfc != NULL) {
         OSSL_TIME rtt = get_rtt(qrs);
@@ -286,3 +286,8 @@ int ossl_quic_rstream_resize_rbuf(QUIC_RSTREAM *qrs, size_t rbuf_size)
 
     return 1;
 }
+
+void ossl_quic_rstream_set_cleanse(QUIC_RSTREAM *qrs, int cleanse)
+{
+    qrs->fl.cleanse = cleanse;
+}
index b53cbc173953eec8b1c1c26d9d1449ea7749892a..7f3fc9b84253145bf92fcc5c3922a28bf29de278 100644 (file)
@@ -20,6 +20,9 @@ struct stream_frame_st {
 
 static void stream_frame_free(SFRAME_LIST *fl, STREAM_FRAME *sf)
 {
+    if (fl->cleanse && sf->data != NULL)
+        OPENSSL_cleanse((unsigned char *)sf->data,
+                        (size_t)(sf->range.end - sf->range.start));
     ossl_qrx_pkt_release(sf->pkt);
     OPENSSL_free(sf);
 }
@@ -295,6 +298,10 @@ int ossl_sframe_list_move_data(SFRAME_LIST *fl,
                 /* data did not fit */
                 return 0;
 
+            if (fl->cleanse)
+                OPENSSL_cleanse((unsigned char *)sf->data,
+                                (size_t)(sf->range.end - sf->range.start));
+
             /* release the packet */
             sf->data = NULL;
             ossl_qrx_pkt_release(sf->pkt);
index 0e15dde51de6e06fe3e50fba011694b395781433..5ead14038a0ed52361f9f4f2bdcd7aa24df46719 100644 (file)
@@ -349,7 +349,7 @@ static void qss_cull(QUIC_SSTREAM *qss)
      * can only cull contiguous areas at the start of the ring buffer anyway.
      */
     if (h != NULL)
-        ring_buf_cpop_range(&qss->ring_buf, h->range.start, h->range.end);
+        ring_buf_cpop_range(&qss->ring_buf, h->range.start, h->range.end, 0);
 }
 
 int ossl_quic_sstream_set_buffer_size(QUIC_SSTREAM *qss, size_t num_bytes)
index 1894be7d59601c60229d403f5d752d367c7b4ac2..51a78fa3836d8f63404554bb90a01de899c1c7f8 100644 (file)
@@ -5880,10 +5880,17 @@ uint64_t SSL_CTX_set_options(SSL_CTX *ctx, uint64_t op)
 
 uint64_t SSL_set_options(SSL *s, uint64_t op)
 {
-    SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(s);
+    SSL_CONNECTION *sc;
     OSSL_PARAM options[2], *opts = options;
 
-    if (sc == NULL)
+#ifndef OPENSSL_NO_QUIC
+    if (IS_QUIC(s) && ossl_quic_set_ssl_op(s, op))
+        /* Handled by QUIC, return as set */
+        return op;
+#endif
+
+   sc = SSL_CONNECTION_FROM_SSL(s);
+   if (sc == NULL)
         return 0;
 
     sc->options |= op;
index e7690ff20f230b6e80d0a55999a9cdcae3bcf9dc..04882b21f046d29e1affe8756925aa9a35f0dbd5 100644 (file)
@@ -469,6 +469,9 @@ static int test_rstream_random(int idx)
         || !TEST_ptr(rstream = ossl_quic_rstream_new(NULL, NULL, 0)))
         goto err;
 
+    if (idx % 3 == 0)
+        ossl_quic_rstream_set_cleanse(rstream, 1);
+
     for (i = 0; i < data_size; ++i)
         bulk_data[i] = (unsigned char)(test_random() & 0xFF);
 
@@ -522,8 +525,9 @@ static int test_rstream_random(int idx)
         }
         if (!TEST_size_t_ge(readbytes, queued_min - read_off)
             || !TEST_size_t_le(readbytes + read_off, data_size)
-            || !TEST_mem_eq(read_buf, readbytes, bulk_data + read_off,
-                            readbytes))
+            || (idx % 3 != 0
+                && !TEST_mem_eq(read_buf, readbytes, bulk_data + read_off,
+                                readbytes)))
             goto err;
         read_off += readbytes;
         queued_min = read_off;
@@ -543,6 +547,11 @@ static int test_rstream_random(int idx)
 
     TEST_info("Total read bytes: %zu Fin rcvd: %d", read_off, fin);
 
+    if (idx % 3 == 0)
+        for (i = 0; i < read_off; i++)
+            if (!TEST_uchar_eq(bulk_data[i], 0))
+                goto err;
+
     if (read_off == data_size && fin_set && !fin) {
         /* We might still receive the final empty frame */
         if (idx % 2 == 0) {
@@ -561,9 +570,9 @@ static int test_rstream_random(int idx)
     ret = 1;
 
  err:
+    ossl_quic_rstream_free(rstream);
     OPENSSL_free(bulk_data);
     OPENSSL_free(read_buf);
-    ossl_quic_rstream_free(rstream);
     return ret;
 }