From 24c399e99a02e098cfb950694eed3ffbe8491e3e Mon Sep 17 00:00:00 2001 From: jsing Date: Fri, 22 Jul 2022 19:33:53 +0000 Subject: [PATCH] Add read and write support to tls_buffer. 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 | 5 +- lib/libssl/tls13_record.c | 5 +- lib/libssl/tls_buffer.c | 133 +++++++++++++++++++++++++++++-- lib/libssl/tls_internal.h | 9 ++- 4 files changed, 139 insertions(+), 13 deletions(-) diff --git a/lib/libssl/tls13_handshake_msg.c b/lib/libssl/tls13_handshake_msg.c index 946ccaccd6f..134cfb21739 100644 --- a/lib/libssl/tls13_handshake_msg.c +++ b/lib/libssl/tls13_handshake_msg.c @@ -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 * @@ -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; diff --git a/lib/libssl/tls13_record.c b/lib/libssl/tls13_record.c index 2c744668e5b..dbc835c5463 100644 --- a/lib/libssl/tls13_record.c +++ b/lib/libssl/tls13_record.c @@ -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 * @@ -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; diff --git a/lib/libssl/tls_buffer.c b/lib/libssl/tls_buffer.c index 9bb6b62e511..f70cfbc1a0d 100644 --- a/lib/libssl/tls_buffer.c +++ b/lib/libssl/tls_buffer.c @@ -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 + * Copyright (c) 2018, 2019, 2022 Joel Sing * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -21,8 +21,11 @@ #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; } diff --git a/lib/libssl/tls_internal.h b/lib/libssl/tls_internal.h index ac2d14da48d..88dae9e67e4 100644 --- a/lib/libssl/tls_internal.h +++ b/lib/libssl/tls_internal.h @@ -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 * @@ -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); /* -- 2.20.1