xref: /dragonfly/crypto/libressl/ssl/tls_buffer.c (revision 961e30ea7dc61d1112b778ea4981eac68129fb86)
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 
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 ssize_t
tls_buffer_read(struct tls_buffer * buf,uint8_t * rbuf,size_t n)159 tls_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 
177 ssize_t
tls_buffer_write(struct tls_buffer * buf,const uint8_t * wbuf,size_t n)178 tls_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 
212 int
tls_buffer_append(struct tls_buffer * buf,const uint8_t * wbuf,size_t n)213 tls_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 
218 int
tls_buffer_data(struct tls_buffer * buf,CBS * out_cbs)219 tls_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 
233 int
tls_buffer_finish(struct tls_buffer * buf,uint8_t ** out,size_t * out_len)234 tls_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