diff options
author | Joel Sing <jsing@cvs.openbsd.org> | 2022-07-22 19:33:54 +0000 |
---|---|---|
committer | Joel Sing <jsing@cvs.openbsd.org> | 2022-07-22 19:33:54 +0000 |
commit | 9456d4e8735197d76f5563cceb5fb86d15325cab (patch) | |
tree | 9db0c2c6d75046f24eb8fea1c3aae9cf41ca9f00 /lib/libssl/tls_buffer.c | |
parent | be16e4e9292d4af770db9010d1876934399e5a90 (diff) |
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@
Diffstat (limited to 'lib/libssl/tls_buffer.c')
-rw-r--r-- | lib/libssl/tls_buffer.c | 133 |
1 files changed, 126 insertions, 7 deletions
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 <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 @@ -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; @@ -50,32 +55,76 @@ tls_buffer_new(size_t init_size) } 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; } |