/src/libressl/ssl/tls_buffer.c
Line | Count | Source (jump to first uncovered line) |
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 | 41.4k | #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 * |
37 | | tls_buffer_new(size_t init_size) |
38 | 41.4k | { |
39 | 41.4k | struct tls_buffer *buf = NULL; |
40 | | |
41 | 41.4k | if ((buf = calloc(1, sizeof(struct tls_buffer))) == NULL) |
42 | 0 | goto err; |
43 | | |
44 | 41.4k | buf->capacity_limit = TLS_BUFFER_CAPACITY_LIMIT; |
45 | | |
46 | 41.4k | if (!tls_buffer_resize(buf, init_size)) |
47 | 0 | goto err; |
48 | | |
49 | 41.4k | return buf; |
50 | | |
51 | 0 | err: |
52 | 0 | tls_buffer_free(buf); |
53 | |
|
54 | 0 | return NULL; |
55 | 41.4k | } |
56 | | |
57 | | void |
58 | | tls_buffer_clear(struct tls_buffer *buf) |
59 | 48.2k | { |
60 | 48.2k | freezero(buf->data, buf->capacity); |
61 | | |
62 | 48.2k | buf->data = NULL; |
63 | 48.2k | buf->capacity = 0; |
64 | 48.2k | buf->len = 0; |
65 | 48.2k | buf->offset = 0; |
66 | 48.2k | } |
67 | | |
68 | | void |
69 | | tls_buffer_free(struct tls_buffer *buf) |
70 | 65.3k | { |
71 | 65.3k | if (buf == NULL) |
72 | 23.9k | return; |
73 | | |
74 | 41.4k | tls_buffer_clear(buf); |
75 | | |
76 | 41.4k | freezero(buf, sizeof(struct tls_buffer)); |
77 | 41.4k | } |
78 | | |
79 | | static int |
80 | | tls_buffer_grow(struct tls_buffer *buf, size_t capacity) |
81 | 19.4k | { |
82 | 19.4k | if (buf->capacity >= capacity) |
83 | 0 | return 1; |
84 | | |
85 | 19.4k | return tls_buffer_resize(buf, capacity); |
86 | 19.4k | } |
87 | | |
88 | | static int |
89 | | tls_buffer_resize(struct tls_buffer *buf, size_t capacity) |
90 | 102k | { |
91 | 102k | uint8_t *data; |
92 | | |
93 | | /* |
94 | | * XXX - Consider maintaining a minimum size and growing more |
95 | | * intelligently (rather than exactly). |
96 | | */ |
97 | 102k | if (buf->capacity == capacity) |
98 | 20.2k | return 1; |
99 | | |
100 | 82.0k | if (capacity > buf->capacity_limit) |
101 | 0 | return 0; |
102 | | |
103 | 82.0k | if ((data = recallocarray(buf->data, buf->capacity, capacity, 1)) == NULL) |
104 | 0 | return 0; |
105 | | |
106 | 82.0k | buf->data = data; |
107 | 82.0k | buf->capacity = capacity; |
108 | | |
109 | | /* Ensure that len and offset are valid if capacity decreased. */ |
110 | 82.0k | if (buf->len > buf->capacity) |
111 | 0 | buf->len = buf->capacity; |
112 | 82.0k | if (buf->offset > buf->len) |
113 | 0 | buf->offset = buf->len; |
114 | | |
115 | 82.0k | return 1; |
116 | 82.0k | } |
117 | | |
118 | | void |
119 | | tls_buffer_set_capacity_limit(struct tls_buffer *buf, size_t limit) |
120 | 0 | { |
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 | 0 | buf->capacity_limit = limit; |
126 | 0 | } |
127 | | |
128 | | ssize_t |
129 | | tls_buffer_extend(struct tls_buffer *buf, size_t len, |
130 | | tls_read_cb read_cb, void *cb_arg) |
131 | 41.4k | { |
132 | 41.4k | ssize_t ret; |
133 | | |
134 | 41.4k | if (len == buf->len) |
135 | 49 | return buf->len; |
136 | | |
137 | 41.4k | if (len < buf->len) |
138 | 0 | return TLS_IO_FAILURE; |
139 | | |
140 | 41.4k | if (!tls_buffer_resize(buf, len)) |
141 | 0 | return TLS_IO_FAILURE; |
142 | | |
143 | 47.4k | for (;;) { |
144 | 47.4k | if ((ret = read_cb(&buf->data[buf->len], |
145 | 47.4k | buf->capacity - buf->len, cb_arg)) <= 0) |
146 | 562 | return ret; |
147 | | |
148 | 46.9k | if (ret > buf->capacity - buf->len) |
149 | 0 | return TLS_IO_FAILURE; |
150 | | |
151 | 46.9k | buf->len += ret; |
152 | | |
153 | 46.9k | if (buf->len == buf->capacity) |
154 | 40.8k | return buf->len; |
155 | 46.9k | } |
156 | 41.4k | } |
157 | | |
158 | | ssize_t |
159 | | tls_buffer_read(struct tls_buffer *buf, uint8_t *rbuf, size_t n) |
160 | 0 | { |
161 | 0 | if (buf->offset > buf->len) |
162 | 0 | return TLS_IO_FAILURE; |
163 | | |
164 | 0 | if (buf->offset == buf->len) |
165 | 0 | return TLS_IO_WANT_POLLIN; |
166 | | |
167 | 0 | if (n > buf->len - buf->offset) |
168 | 0 | n = buf->len - buf->offset; |
169 | |
|
170 | 0 | memcpy(rbuf, &buf->data[buf->offset], n); |
171 | |
|
172 | 0 | buf->offset += n; |
173 | |
|
174 | 0 | return n; |
175 | 0 | } |
176 | | |
177 | | ssize_t |
178 | | tls_buffer_write(struct tls_buffer *buf, const uint8_t *wbuf, size_t n) |
179 | 19.4k | { |
180 | 19.4k | if (buf->offset > buf->len) |
181 | 0 | 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 | 19.4k | if (buf->offset == buf->len) { |
190 | 6.76k | buf->len = 0; |
191 | 6.76k | buf->offset = 0; |
192 | 6.76k | } |
193 | 19.4k | if (buf->offset >= 4096) { |
194 | 0 | memmove(buf->data, &buf->data[buf->offset], |
195 | 0 | buf->len - buf->offset); |
196 | 0 | buf->len -= buf->offset; |
197 | 0 | buf->offset = 0; |
198 | 0 | } |
199 | | |
200 | 19.4k | if (buf->len > SIZE_MAX - n) |
201 | 0 | return TLS_IO_FAILURE; |
202 | 19.4k | if (!tls_buffer_grow(buf, buf->len + n)) |
203 | 0 | return TLS_IO_FAILURE; |
204 | | |
205 | 19.4k | memcpy(&buf->data[buf->len], wbuf, n); |
206 | | |
207 | 19.4k | buf->len += n; |
208 | | |
209 | 19.4k | return n; |
210 | 19.4k | } |
211 | | |
212 | | int |
213 | | tls_buffer_append(struct tls_buffer *buf, const uint8_t *wbuf, size_t n) |
214 | 19.4k | { |
215 | 19.4k | return tls_buffer_write(buf, wbuf, n) == n; |
216 | 19.4k | } |
217 | | |
218 | | int |
219 | | tls_buffer_data(struct tls_buffer *buf, CBS *out_cbs) |
220 | 26.7k | { |
221 | 26.7k | CBS cbs; |
222 | | |
223 | 26.7k | CBS_init(&cbs, buf->data, buf->len); |
224 | | |
225 | 26.7k | if (!CBS_skip(&cbs, buf->offset)) |
226 | 0 | return 0; |
227 | | |
228 | 26.7k | CBS_dup(&cbs, out_cbs); |
229 | | |
230 | 26.7k | return 1; |
231 | 26.7k | } |
232 | | |
233 | | int |
234 | | tls_buffer_finish(struct tls_buffer *buf, uint8_t **out, size_t *out_len) |
235 | 20.3k | { |
236 | 20.3k | if (out == NULL || out_len == NULL) |
237 | 0 | return 0; |
238 | | |
239 | 20.3k | *out = buf->data; |
240 | 20.3k | *out_len = buf->len; |
241 | | |
242 | 20.3k | buf->data = NULL; |
243 | 20.3k | buf->capacity = 0; |
244 | 20.3k | buf->len = 0; |
245 | 20.3k | buf->offset = 0; |
246 | | |
247 | 20.3k | return 1; |
248 | 20.3k | } |