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 * 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 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 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 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 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 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 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 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 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 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 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 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 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