tls_buffer.c revision 1.3
1/* $OpenBSD: tls_buffer.c,v 1.3 2022/07/22 19:33:53 jsing Exp $ */
2/*
3 * Copyright (c) 2018, 2019, 2022 Joel Sing <jsing@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <stdlib.h>
19#include <string.h>
20
21#include "bytestring.h"
22#include "tls_internal.h"
23
24#define TLS_BUFFER_CAPACITY_LIMIT	(1024 * 1024)
25
26struct tls_buffer {
27	size_t capacity;
28	size_t capacity_limit;
29	uint8_t *data;
30	size_t len;
31	size_t offset;
32};
33
34static int tls_buffer_resize(struct tls_buffer *buf, size_t capacity);
35
36struct tls_buffer *
37tls_buffer_new(size_t init_size)
38{
39	struct tls_buffer *buf = NULL;
40
41	if ((buf = calloc(1, sizeof(struct tls_buffer))) == NULL)
42		goto err;
43
44	buf->capacity_limit = TLS_BUFFER_CAPACITY_LIMIT;
45
46	if (!tls_buffer_resize(buf, init_size))
47		goto err;
48
49	return buf;
50
51 err:
52	tls_buffer_free(buf);
53
54	return NULL;
55}
56
57void
58tls_buffer_clear(struct tls_buffer *buf)
59{
60	freezero(buf->data, buf->capacity);
61
62	buf->data = NULL;
63	buf->capacity = 0;
64	buf->len = 0;
65	buf->offset = 0;
66}
67
68void
69tls_buffer_free(struct tls_buffer *buf)
70{
71	if (buf == NULL)
72		return;
73
74	tls_buffer_clear(buf);
75
76	freezero(buf, sizeof(struct tls_buffer));
77}
78
79static int
80tls_buffer_grow(struct tls_buffer *buf, size_t capacity)
81{
82	if (buf->capacity >= capacity)
83		return 1;
84
85	return tls_buffer_resize(buf, capacity);
86}
87
88static int
89tls_buffer_resize(struct tls_buffer *buf, size_t capacity)
90{
91	uint8_t *data;
92
93	/*
94	 * XXX - Consider maintaining a minimum size and growing more
95	 * intelligently (rather than exactly).
96	 */
97	if (buf->capacity == capacity)
98		return 1;
99
100	if (capacity > buf->capacity_limit)
101		return 0;
102
103	if ((data = recallocarray(buf->data, buf->capacity, capacity, 1)) == NULL)
104		return 0;
105
106	buf->data = data;
107	buf->capacity = capacity;
108
109	/* Ensure that len and offset are valid if capacity decreased. */
110	if (buf->len > buf->capacity)
111		buf->len = buf->capacity;
112	if (buf->offset > buf->len)
113		buf->offset = buf->len;
114
115	return 1;
116}
117
118void
119tls_buffer_set_capacity_limit(struct tls_buffer *buf, size_t limit)
120{
121	/*
122	 * XXX - do we want to force a resize if this limit is less than current
123	 * capacity... and what do we do with existing data? Force a clear?
124	 */
125	buf->capacity_limit = limit;
126}
127
128ssize_t
129tls_buffer_extend(struct tls_buffer *buf, size_t len,
130    tls_read_cb read_cb, void *cb_arg)
131{
132	ssize_t ret;
133
134	if (len == buf->len)
135		return buf->len;
136
137	if (len < buf->len)
138		return TLS_IO_FAILURE;
139
140	if (!tls_buffer_resize(buf, len))
141		return TLS_IO_FAILURE;
142
143	for (;;) {
144		if ((ret = read_cb(&buf->data[buf->len],
145		    buf->capacity - buf->len, cb_arg)) <= 0)
146			return ret;
147
148		if (ret > buf->capacity - buf->len)
149			return TLS_IO_FAILURE;
150
151		buf->len += ret;
152
153		if (buf->len == buf->capacity)
154			return buf->len;
155	}
156}
157
158ssize_t
159tls_buffer_read(struct tls_buffer *buf, uint8_t *rbuf, size_t n)
160{
161	if (buf->offset > buf->len)
162		return TLS_IO_FAILURE;
163
164	if (buf->offset == buf->len)
165		return TLS_IO_WANT_POLLIN;
166
167	if (n > buf->len - buf->offset)
168		n = buf->len - buf->offset;
169
170	memcpy(rbuf, &buf->data[buf->offset], n);
171
172	buf->offset += n;
173
174	return n;
175}
176
177ssize_t
178tls_buffer_write(struct tls_buffer *buf, const uint8_t *wbuf, size_t n)
179{
180	if (buf->offset > buf->len)
181		return TLS_IO_FAILURE;
182
183	/*
184	 * To avoid continually growing the buffer, pull data up to the
185	 * start of the buffer. If all data has been read then we can simply
186	 * reset, otherwise wait until we're going to save at least 4KB of
187	 * memory to reduce overhead.
188	 */
189	if (buf->offset == buf->len) {
190		buf->len = 0;
191		buf->offset = 0;
192	}
193	if (buf->offset >= 4096) {
194		memmove(buf->data, &buf->data[buf->offset],
195		    buf->len - buf->offset);
196		buf->len -= buf->offset;
197		buf->offset = 0;
198	}
199
200	if (buf->len > SIZE_MAX - n)
201		return TLS_IO_FAILURE;
202	if (!tls_buffer_grow(buf, buf->len + n))
203		return TLS_IO_FAILURE;
204
205	memcpy(&buf->data[buf->len], wbuf, n);
206
207	buf->len += n;
208
209	return n;
210}
211
212int
213tls_buffer_append(struct tls_buffer *buf, const uint8_t *wbuf, size_t n)
214{
215	return tls_buffer_write(buf, wbuf, n) == n;
216}
217
218int
219tls_buffer_data(struct tls_buffer *buf, CBS *out_cbs)
220{
221	CBS cbs;
222
223	CBS_init(&cbs, buf->data, buf->len);
224
225	if (!CBS_skip(&cbs, buf->offset))
226		return 0;
227
228	CBS_dup(&cbs, out_cbs);
229
230	return 1;
231}
232
233int
234tls_buffer_finish(struct tls_buffer *buf, uint8_t **out, size_t *out_len)
235{
236	if (out == NULL || out_len == NULL)
237		return 0;
238
239	*out = buf->data;
240	*out_len = buf->len;
241
242	buf->data = NULL;
243	buf->capacity = 0;
244	buf->len = 0;
245	buf->offset = 0;
246
247	return 1;
248}
249