Add read and write support to tls_buffer.
authorjsing <jsing@openbsd.org>
Fri, 22 Jul 2022 19:33:53 +0000 (19:33 +0000)
committerjsing <jsing@openbsd.org>
Fri, 22 Jul 2022 19:33:53 +0000 (19:33 +0000)
tls_buffer was original created for a specific use case, namely reading in
length prefixed messages. This adds read and write support, along with a
capacity limit, allowing it to be used in additional use cases.

ok beck@ tb@

lib/libssl/tls13_handshake_msg.c
lib/libssl/tls13_record.c
lib/libssl/tls_buffer.c
lib/libssl/tls_internal.h

index 946ccac..134cfb2 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls13_handshake_msg.c,v 1.5 2022/07/20 06:32:24 jsing Exp $ */
+/* $OpenBSD: tls13_handshake_msg.c,v 1.6 2022/07/22 19:33:53 jsing Exp $ */
 /*
  * Copyright (c) 2018, 2019 Joel Sing <jsing@openbsd.org>
  *
@@ -136,7 +136,8 @@ tls13_handshake_msg_recv(struct tls13_handshake_msg *msg,
                    tls13_handshake_msg_read_cb, rl)) <= 0)
                        return ret;
 
-               tls_buffer_cbs(msg->buf, &cbs);
+               if (!tls_buffer_data(msg->buf, &cbs))
+                       return TLS13_IO_FAILURE;
 
                if (!CBS_get_u8(&cbs, &msg_type))
                        return TLS13_IO_FAILURE;
index 2c74466..dbc835c 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls13_record.c,v 1.9 2021/10/23 13:12:14 jsing Exp $ */
+/* $OpenBSD: tls13_record.c,v 1.10 2022/07/22 19:33:53 jsing Exp $ */
 /*
  * Copyright (c) 2018, 2019 Joel Sing <jsing@openbsd.org>
  *
@@ -134,7 +134,8 @@ tls13_record_recv(struct tls13_record *rec, tls_read_cb wire_read,
                    TLS13_RECORD_HEADER_LEN, wire_read, wire_arg)) <= 0)
                        return ret;
 
-               tls_buffer_cbs(rec->buf, &cbs);
+               if (!tls_buffer_data(rec->buf, &cbs))
+                       return TLS13_IO_FAILURE;
 
                if (!CBS_get_u8(&cbs, &content_type))
                        return TLS13_IO_FAILURE;
index 9bb6b62..f70cfbc 100644 (file)
@@ -1,6 +1,6 @@
-/* $OpenBSD: tls_buffer.c,v 1.2 2022/07/20 06:32:24 jsing Exp $ */
+/* $OpenBSD: tls_buffer.c,v 1.3 2022/07/22 19:33:53 jsing Exp $ */
 /*
- * Copyright (c) 2018, 2019 Joel Sing <jsing@openbsd.org>
+ * Copyright (c) 2018, 2019, 2022 Joel Sing <jsing@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
 #include "bytestring.h"
 #include "tls_internal.h"
 
+#define TLS_BUFFER_CAPACITY_LIMIT      (1024 * 1024)
+
 struct tls_buffer {
        size_t capacity;
+       size_t capacity_limit;
        uint8_t *data;
        size_t len;
        size_t offset;
@@ -38,6 +41,8 @@ tls_buffer_new(size_t init_size)
        if ((buf = calloc(1, sizeof(struct tls_buffer))) == NULL)
                goto err;
 
+       buf->capacity_limit = TLS_BUFFER_CAPACITY_LIMIT;
+
        if (!tls_buffer_resize(buf, init_size))
                goto err;
 
@@ -49,33 +54,77 @@ tls_buffer_new(size_t init_size)
        return NULL;
 }
 
+void
+tls_buffer_clear(struct tls_buffer *buf)
+{
+       freezero(buf->data, buf->capacity);
+
+       buf->data = NULL;
+       buf->capacity = 0;
+       buf->len = 0;
+       buf->offset = 0;
+}
+
 void
 tls_buffer_free(struct tls_buffer *buf)
 {
        if (buf == NULL)
                return;
 
-       freezero(buf->data, buf->capacity);
+       tls_buffer_clear(buf);
+
        freezero(buf, sizeof(struct tls_buffer));
 }
 
+static int
+tls_buffer_grow(struct tls_buffer *buf, size_t capacity)
+{
+       if (buf->capacity >= capacity)
+               return 1;
+
+       return tls_buffer_resize(buf, capacity);
+}
+
 static int
 tls_buffer_resize(struct tls_buffer *buf, size_t capacity)
 {
        uint8_t *data;
 
+       /*
+        * XXX - Consider maintaining a minimum size and growing more
+        * intelligently (rather than exactly).
+        */
        if (buf->capacity == capacity)
                return 1;
 
+       if (capacity > buf->capacity_limit)
+               return 0;
+
        if ((data = recallocarray(buf->data, buf->capacity, capacity, 1)) == NULL)
                return 0;
 
        buf->data = data;
        buf->capacity = capacity;
 
+       /* Ensure that len and offset are valid if capacity decreased. */
+       if (buf->len > buf->capacity)
+               buf->len = buf->capacity;
+       if (buf->offset > buf->len)
+               buf->offset = buf->len;
+
        return 1;
 }
 
+void
+tls_buffer_set_capacity_limit(struct tls_buffer *buf, size_t limit)
+{
+       /*
+        * XXX - do we want to force a resize if this limit is less than current
+        * capacity... and what do we do with existing data? Force a clear?
+        */
+       buf->capacity_limit = limit;
+}
+
 ssize_t
 tls_buffer_extend(struct tls_buffer *buf, size_t len,
     tls_read_cb read_cb, void *cb_arg)
@@ -106,10 +155,79 @@ tls_buffer_extend(struct tls_buffer *buf, size_t len,
        }
 }
 
-void
-tls_buffer_cbs(struct tls_buffer *buf, CBS *cbs)
+ssize_t
+tls_buffer_read(struct tls_buffer *buf, uint8_t *rbuf, size_t n)
+{
+       if (buf->offset > buf->len)
+               return TLS_IO_FAILURE;
+
+       if (buf->offset == buf->len)
+               return TLS_IO_WANT_POLLIN;
+
+       if (n > buf->len - buf->offset)
+               n = buf->len - buf->offset;
+
+       memcpy(rbuf, &buf->data[buf->offset], n);
+
+       buf->offset += n;
+
+       return n;
+}
+
+ssize_t
+tls_buffer_write(struct tls_buffer *buf, const uint8_t *wbuf, size_t n)
+{
+       if (buf->offset > buf->len)
+               return TLS_IO_FAILURE;
+
+       /*
+        * To avoid continually growing the buffer, pull data up to the
+        * start of the buffer. If all data has been read then we can simply
+        * reset, otherwise wait until we're going to save at least 4KB of
+        * memory to reduce overhead.
+        */
+       if (buf->offset == buf->len) {
+               buf->len = 0;
+               buf->offset = 0;
+       }
+       if (buf->offset >= 4096) {
+               memmove(buf->data, &buf->data[buf->offset],
+                   buf->len - buf->offset);
+               buf->len -= buf->offset;
+               buf->offset = 0;
+       }
+
+       if (buf->len > SIZE_MAX - n)
+               return TLS_IO_FAILURE;
+       if (!tls_buffer_grow(buf, buf->len + n))
+               return TLS_IO_FAILURE;
+
+       memcpy(&buf->data[buf->len], wbuf, n);
+
+       buf->len += n;
+
+       return n;
+}
+
+int
+tls_buffer_append(struct tls_buffer *buf, const uint8_t *wbuf, size_t n)
+{
+       return tls_buffer_write(buf, wbuf, n) == n;
+}
+
+int
+tls_buffer_data(struct tls_buffer *buf, CBS *out_cbs)
 {
-       CBS_init(cbs, buf->data, buf->len);
+       CBS cbs;
+
+       CBS_init(&cbs, buf->data, buf->len);
+
+       if (!CBS_skip(&cbs, buf->offset))
+               return 0;
+
+       CBS_dup(&cbs, out_cbs);
+
+       return 1;
 }
 
 int
@@ -121,9 +239,10 @@ tls_buffer_finish(struct tls_buffer *buf, uint8_t **out, size_t *out_len)
        *out = buf->data;
        *out_len = buf->len;
 
-       buf->capacity = 0;
        buf->data = NULL;
+       buf->capacity = 0;
        buf->len = 0;
+       buf->offset = 0;
 
        return 1;
 }
index ac2d14d..88dae9e 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls_internal.h,v 1.7 2022/07/20 06:32:24 jsing Exp $ */
+/* $OpenBSD: tls_internal.h,v 1.8 2022/07/22 19:33:53 jsing Exp $ */
 /*
  * Copyright (c) 2018, 2019, 2021 Joel Sing <jsing@openbsd.org>
  *
@@ -47,10 +47,15 @@ typedef ssize_t (*tls_flush_cb)(void *_cb_arg);
 struct tls_buffer;
 
 struct tls_buffer *tls_buffer_new(size_t init_size);
+void tls_buffer_clear(struct tls_buffer *buf);
 void tls_buffer_free(struct tls_buffer *buf);
+void tls_buffer_set_capacity_limit(struct tls_buffer *buf, size_t limit);
 ssize_t tls_buffer_extend(struct tls_buffer *buf, size_t len,
     tls_read_cb read_cb, void *cb_arg);
-void tls_buffer_cbs(struct tls_buffer *buf, CBS *cbs);
+ssize_t tls_buffer_read(struct tls_buffer *buf, uint8_t *rbuf, size_t n);
+ssize_t tls_buffer_write(struct tls_buffer *buf, const uint8_t *wbuf, size_t n);
+int tls_buffer_append(struct tls_buffer *buf, const uint8_t *wbuf, size_t n);
+int tls_buffer_data(struct tls_buffer *buf, CBS *cbs);
 int tls_buffer_finish(struct tls_buffer *buf, uint8_t **out, size_t *out_len);
 
 /*