1 /* $OpenBSD: tls_buffer.c,v 1.4 2022/11/10 18:06:37 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
26 struct 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
34 static int tls_buffer_resize(struct tls_buffer *buf, size_t capacity);
35
36 struct tls_buffer *
tls_buffer_new(size_t init_size)37 tls_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
57 void
tls_buffer_clear(struct tls_buffer * buf)58 tls_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
68 void
tls_buffer_free(struct tls_buffer * buf)69 tls_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
79 static int
tls_buffer_grow(struct tls_buffer * buf,size_t capacity)80 tls_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
88 static int
tls_buffer_resize(struct tls_buffer * buf,size_t capacity)89 tls_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
118 void
tls_buffer_set_capacity_limit(struct tls_buffer * buf,size_t limit)119 tls_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
128 ssize_t
tls_buffer_extend(struct tls_buffer * buf,size_t len,tls_read_cb read_cb,void * cb_arg)129 tls_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
158 size_t
tls_buffer_remaining(struct tls_buffer * buf)159 tls_buffer_remaining(struct tls_buffer *buf)
160 {
161 if (buf->offset > buf->len)
162 return 0;
163
164 return buf->len - buf->offset;
165 }
166
167 ssize_t
tls_buffer_read(struct tls_buffer * buf,uint8_t * rbuf,size_t n)168 tls_buffer_read(struct tls_buffer *buf, uint8_t *rbuf, size_t n)
169 {
170 if (buf->offset > buf->len)
171 return TLS_IO_FAILURE;
172
173 if (buf->offset == buf->len)
174 return TLS_IO_WANT_POLLIN;
175
176 if (n > buf->len - buf->offset)
177 n = buf->len - buf->offset;
178
179 memcpy(rbuf, &buf->data[buf->offset], n);
180
181 buf->offset += n;
182
183 return n;
184 }
185
186 ssize_t
tls_buffer_write(struct tls_buffer * buf,const uint8_t * wbuf,size_t n)187 tls_buffer_write(struct tls_buffer *buf, const uint8_t *wbuf, size_t n)
188 {
189 if (buf->offset > buf->len)
190 return TLS_IO_FAILURE;
191
192 /*
193 * To avoid continually growing the buffer, pull data up to the
194 * start of the buffer. If all data has been read then we can simply
195 * reset, otherwise wait until we're going to save at least 4KB of
196 * memory to reduce overhead.
197 */
198 if (buf->offset == buf->len) {
199 buf->len = 0;
200 buf->offset = 0;
201 }
202 if (buf->offset >= 4096) {
203 memmove(buf->data, &buf->data[buf->offset],
204 buf->len - buf->offset);
205 buf->len -= buf->offset;
206 buf->offset = 0;
207 }
208
209 if (buf->len > SIZE_MAX - n)
210 return TLS_IO_FAILURE;
211 if (!tls_buffer_grow(buf, buf->len + n))
212 return TLS_IO_FAILURE;
213
214 memcpy(&buf->data[buf->len], wbuf, n);
215
216 buf->len += n;
217
218 return n;
219 }
220
221 int
tls_buffer_append(struct tls_buffer * buf,const uint8_t * wbuf,size_t n)222 tls_buffer_append(struct tls_buffer *buf, const uint8_t *wbuf, size_t n)
223 {
224 return tls_buffer_write(buf, wbuf, n) == n;
225 }
226
227 int
tls_buffer_data(struct tls_buffer * buf,CBS * out_cbs)228 tls_buffer_data(struct tls_buffer *buf, CBS *out_cbs)
229 {
230 CBS cbs;
231
232 CBS_init(&cbs, buf->data, buf->len);
233
234 if (!CBS_skip(&cbs, buf->offset))
235 return 0;
236
237 CBS_dup(&cbs, out_cbs);
238
239 return 1;
240 }
241
242 int
tls_buffer_finish(struct tls_buffer * buf,uint8_t ** out,size_t * out_len)243 tls_buffer_finish(struct tls_buffer *buf, uint8_t **out, size_t *out_len)
244 {
245 if (out == NULL || out_len == NULL)
246 return 0;
247
248 *out = buf->data;
249 *out_len = buf->len;
250
251 buf->data = NULL;
252 buf->capacity = 0;
253 buf->len = 0;
254 buf->offset = 0;
255
256 return 1;
257 }
258