1*f374ba41SEd Maste /* $OpenBSD: sshbuf.c,v 1.19 2022/12/02 04:40:27 djm Exp $ */ 2a0ee8cc6SDag-Erling Smørgrav /* 3a0ee8cc6SDag-Erling Smørgrav * Copyright (c) 2011 Damien Miller 4a0ee8cc6SDag-Erling Smørgrav * 5a0ee8cc6SDag-Erling Smørgrav * Permission to use, copy, modify, and distribute this software for any 6a0ee8cc6SDag-Erling Smørgrav * purpose with or without fee is hereby granted, provided that the above 7a0ee8cc6SDag-Erling Smørgrav * copyright notice and this permission notice appear in all copies. 8a0ee8cc6SDag-Erling Smørgrav * 9a0ee8cc6SDag-Erling Smørgrav * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10a0ee8cc6SDag-Erling Smørgrav * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11a0ee8cc6SDag-Erling Smørgrav * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12a0ee8cc6SDag-Erling Smørgrav * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13a0ee8cc6SDag-Erling Smørgrav * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14a0ee8cc6SDag-Erling Smørgrav * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15a0ee8cc6SDag-Erling Smørgrav * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16a0ee8cc6SDag-Erling Smørgrav */ 17a0ee8cc6SDag-Erling Smørgrav 18a0ee8cc6SDag-Erling Smørgrav #include "includes.h" 19a0ee8cc6SDag-Erling Smørgrav 20a0ee8cc6SDag-Erling Smørgrav #include <sys/types.h> 21a0ee8cc6SDag-Erling Smørgrav #include <signal.h> 22a0ee8cc6SDag-Erling Smørgrav #include <stdlib.h> 23a0ee8cc6SDag-Erling Smørgrav #include <stdio.h> 24a0ee8cc6SDag-Erling Smørgrav #include <string.h> 25a0ee8cc6SDag-Erling Smørgrav 26a0ee8cc6SDag-Erling Smørgrav #include "ssherr.h" 27*f374ba41SEd Maste #define SSHBUF_INTERNAL 28a0ee8cc6SDag-Erling Smørgrav #include "sshbuf.h" 29ca86bcf2SDag-Erling Smørgrav #include "misc.h" 30a0ee8cc6SDag-Erling Smørgrav 31*f374ba41SEd Maste #ifdef SSHBUF_DEBUG 32*f374ba41SEd Maste # define SSHBUF_TELL(what) do { \ 33*f374ba41SEd Maste printf("%s:%d %s: %s size %zu alloc %zu off %zu max %zu\n", \ 34*f374ba41SEd Maste __FILE__, __LINE__, __func__, what, \ 35*f374ba41SEd Maste buf->size, buf->alloc, buf->off, buf->max_size); \ 36*f374ba41SEd Maste fflush(stdout); \ 37*f374ba41SEd Maste } while (0) 38*f374ba41SEd Maste #else 39*f374ba41SEd Maste # define SSHBUF_TELL(what) 40*f374ba41SEd Maste #endif 41*f374ba41SEd Maste 42*f374ba41SEd Maste struct sshbuf { 43*f374ba41SEd Maste u_char *d; /* Data */ 44*f374ba41SEd Maste const u_char *cd; /* Const data */ 45*f374ba41SEd Maste size_t off; /* First available byte is buf->d + buf->off */ 46*f374ba41SEd Maste size_t size; /* Last byte is buf->d + buf->size - 1 */ 47*f374ba41SEd Maste size_t max_size; /* Maximum size of buffer */ 48*f374ba41SEd Maste size_t alloc; /* Total bytes allocated to buf->d */ 49*f374ba41SEd Maste int readonly; /* Refers to external, const data */ 50*f374ba41SEd Maste u_int refcount; /* Tracks self and number of child buffers */ 51*f374ba41SEd Maste struct sshbuf *parent; /* If child, pointer to parent */ 52*f374ba41SEd Maste }; 53*f374ba41SEd Maste 54a0ee8cc6SDag-Erling Smørgrav static inline int 55a0ee8cc6SDag-Erling Smørgrav sshbuf_check_sanity(const struct sshbuf *buf) 56a0ee8cc6SDag-Erling Smørgrav { 57a0ee8cc6SDag-Erling Smørgrav SSHBUF_TELL("sanity"); 58a0ee8cc6SDag-Erling Smørgrav if (__predict_false(buf == NULL || 59a0ee8cc6SDag-Erling Smørgrav (!buf->readonly && buf->d != buf->cd) || 60a0ee8cc6SDag-Erling Smørgrav buf->refcount < 1 || buf->refcount > SSHBUF_REFS_MAX || 61a0ee8cc6SDag-Erling Smørgrav buf->cd == NULL || 62a0ee8cc6SDag-Erling Smørgrav buf->max_size > SSHBUF_SIZE_MAX || 63a0ee8cc6SDag-Erling Smørgrav buf->alloc > buf->max_size || 64a0ee8cc6SDag-Erling Smørgrav buf->size > buf->alloc || 65a0ee8cc6SDag-Erling Smørgrav buf->off > buf->size)) { 66a0ee8cc6SDag-Erling Smørgrav /* Do not try to recover from corrupted buffer internals */ 67a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); 6819261079SEd Maste ssh_signal(SIGSEGV, SIG_DFL); 69a0ee8cc6SDag-Erling Smørgrav raise(SIGSEGV); 70a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 71a0ee8cc6SDag-Erling Smørgrav } 72a0ee8cc6SDag-Erling Smørgrav return 0; 73a0ee8cc6SDag-Erling Smørgrav } 74a0ee8cc6SDag-Erling Smørgrav 75a0ee8cc6SDag-Erling Smørgrav static void 76a0ee8cc6SDag-Erling Smørgrav sshbuf_maybe_pack(struct sshbuf *buf, int force) 77a0ee8cc6SDag-Erling Smørgrav { 78a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("force %d", force)); 79a0ee8cc6SDag-Erling Smørgrav SSHBUF_TELL("pre-pack"); 80a0ee8cc6SDag-Erling Smørgrav if (buf->off == 0 || buf->readonly || buf->refcount > 1) 81a0ee8cc6SDag-Erling Smørgrav return; 82a0ee8cc6SDag-Erling Smørgrav if (force || 83a0ee8cc6SDag-Erling Smørgrav (buf->off >= SSHBUF_PACK_MIN && buf->off >= buf->size / 2)) { 84a0ee8cc6SDag-Erling Smørgrav memmove(buf->d, buf->d + buf->off, buf->size - buf->off); 85a0ee8cc6SDag-Erling Smørgrav buf->size -= buf->off; 86a0ee8cc6SDag-Erling Smørgrav buf->off = 0; 87a0ee8cc6SDag-Erling Smørgrav SSHBUF_TELL("packed"); 88a0ee8cc6SDag-Erling Smørgrav } 89a0ee8cc6SDag-Erling Smørgrav } 90a0ee8cc6SDag-Erling Smørgrav 91a0ee8cc6SDag-Erling Smørgrav struct sshbuf * 92a0ee8cc6SDag-Erling Smørgrav sshbuf_new(void) 93a0ee8cc6SDag-Erling Smørgrav { 94a0ee8cc6SDag-Erling Smørgrav struct sshbuf *ret; 95a0ee8cc6SDag-Erling Smørgrav 96a0ee8cc6SDag-Erling Smørgrav if ((ret = calloc(sizeof(*ret), 1)) == NULL) 97a0ee8cc6SDag-Erling Smørgrav return NULL; 98a0ee8cc6SDag-Erling Smørgrav ret->alloc = SSHBUF_SIZE_INIT; 99a0ee8cc6SDag-Erling Smørgrav ret->max_size = SSHBUF_SIZE_MAX; 100a0ee8cc6SDag-Erling Smørgrav ret->readonly = 0; 101a0ee8cc6SDag-Erling Smørgrav ret->refcount = 1; 102a0ee8cc6SDag-Erling Smørgrav ret->parent = NULL; 103a0ee8cc6SDag-Erling Smørgrav if ((ret->cd = ret->d = calloc(1, ret->alloc)) == NULL) { 104a0ee8cc6SDag-Erling Smørgrav free(ret); 105a0ee8cc6SDag-Erling Smørgrav return NULL; 106a0ee8cc6SDag-Erling Smørgrav } 107a0ee8cc6SDag-Erling Smørgrav return ret; 108a0ee8cc6SDag-Erling Smørgrav } 109a0ee8cc6SDag-Erling Smørgrav 110a0ee8cc6SDag-Erling Smørgrav struct sshbuf * 111a0ee8cc6SDag-Erling Smørgrav sshbuf_from(const void *blob, size_t len) 112a0ee8cc6SDag-Erling Smørgrav { 113a0ee8cc6SDag-Erling Smørgrav struct sshbuf *ret; 114a0ee8cc6SDag-Erling Smørgrav 115a0ee8cc6SDag-Erling Smørgrav if (blob == NULL || len > SSHBUF_SIZE_MAX || 116a0ee8cc6SDag-Erling Smørgrav (ret = calloc(sizeof(*ret), 1)) == NULL) 117a0ee8cc6SDag-Erling Smørgrav return NULL; 118a0ee8cc6SDag-Erling Smørgrav ret->alloc = ret->size = ret->max_size = len; 119a0ee8cc6SDag-Erling Smørgrav ret->readonly = 1; 120a0ee8cc6SDag-Erling Smørgrav ret->refcount = 1; 121a0ee8cc6SDag-Erling Smørgrav ret->parent = NULL; 122a0ee8cc6SDag-Erling Smørgrav ret->cd = blob; 123a0ee8cc6SDag-Erling Smørgrav ret->d = NULL; 124a0ee8cc6SDag-Erling Smørgrav return ret; 125a0ee8cc6SDag-Erling Smørgrav } 126a0ee8cc6SDag-Erling Smørgrav 127a0ee8cc6SDag-Erling Smørgrav int 128a0ee8cc6SDag-Erling Smørgrav sshbuf_set_parent(struct sshbuf *child, struct sshbuf *parent) 129a0ee8cc6SDag-Erling Smørgrav { 130a0ee8cc6SDag-Erling Smørgrav int r; 131a0ee8cc6SDag-Erling Smørgrav 132a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_sanity(child)) != 0 || 133a0ee8cc6SDag-Erling Smørgrav (r = sshbuf_check_sanity(parent)) != 0) 134a0ee8cc6SDag-Erling Smørgrav return r; 13538a52bd3SEd Maste if (child->parent != NULL && child->parent != parent) 13638a52bd3SEd Maste return SSH_ERR_INTERNAL_ERROR; 137a0ee8cc6SDag-Erling Smørgrav child->parent = parent; 138a0ee8cc6SDag-Erling Smørgrav child->parent->refcount++; 139a0ee8cc6SDag-Erling Smørgrav return 0; 140a0ee8cc6SDag-Erling Smørgrav } 141a0ee8cc6SDag-Erling Smørgrav 142a0ee8cc6SDag-Erling Smørgrav struct sshbuf * 143a0ee8cc6SDag-Erling Smørgrav sshbuf_fromb(struct sshbuf *buf) 144a0ee8cc6SDag-Erling Smørgrav { 145a0ee8cc6SDag-Erling Smørgrav struct sshbuf *ret; 146a0ee8cc6SDag-Erling Smørgrav 147a0ee8cc6SDag-Erling Smørgrav if (sshbuf_check_sanity(buf) != 0) 148a0ee8cc6SDag-Erling Smørgrav return NULL; 149a0ee8cc6SDag-Erling Smørgrav if ((ret = sshbuf_from(sshbuf_ptr(buf), sshbuf_len(buf))) == NULL) 150a0ee8cc6SDag-Erling Smørgrav return NULL; 151a0ee8cc6SDag-Erling Smørgrav if (sshbuf_set_parent(ret, buf) != 0) { 152a0ee8cc6SDag-Erling Smørgrav sshbuf_free(ret); 153a0ee8cc6SDag-Erling Smørgrav return NULL; 154a0ee8cc6SDag-Erling Smørgrav } 155a0ee8cc6SDag-Erling Smørgrav return ret; 156a0ee8cc6SDag-Erling Smørgrav } 157a0ee8cc6SDag-Erling Smørgrav 158a0ee8cc6SDag-Erling Smørgrav void 159a0ee8cc6SDag-Erling Smørgrav sshbuf_free(struct sshbuf *buf) 160a0ee8cc6SDag-Erling Smørgrav { 161a0ee8cc6SDag-Erling Smørgrav if (buf == NULL) 162a0ee8cc6SDag-Erling Smørgrav return; 163a0ee8cc6SDag-Erling Smørgrav /* 164a0ee8cc6SDag-Erling Smørgrav * The following will leak on insane buffers, but this is the safest 165a0ee8cc6SDag-Erling Smørgrav * course of action - an invalid pointer or already-freed pointer may 166a0ee8cc6SDag-Erling Smørgrav * have been passed to us and continuing to scribble over memory would 167a0ee8cc6SDag-Erling Smørgrav * be bad. 168a0ee8cc6SDag-Erling Smørgrav */ 169a0ee8cc6SDag-Erling Smørgrav if (sshbuf_check_sanity(buf) != 0) 170a0ee8cc6SDag-Erling Smørgrav return; 17119261079SEd Maste 172a0ee8cc6SDag-Erling Smørgrav /* 173a0ee8cc6SDag-Erling Smørgrav * If we are a parent with still-extant children, then don't free just 174a0ee8cc6SDag-Erling Smørgrav * yet. The last child's call to sshbuf_free should decrement our 175a0ee8cc6SDag-Erling Smørgrav * refcount to 0 and trigger the actual free. 176a0ee8cc6SDag-Erling Smørgrav */ 177a0ee8cc6SDag-Erling Smørgrav buf->refcount--; 178a0ee8cc6SDag-Erling Smørgrav if (buf->refcount > 0) 179a0ee8cc6SDag-Erling Smørgrav return; 18019261079SEd Maste 18119261079SEd Maste /* 18219261079SEd Maste * If we are a child, the free our parent to decrement its reference 18319261079SEd Maste * count and possibly free it. 18419261079SEd Maste */ 18519261079SEd Maste sshbuf_free(buf->parent); 18619261079SEd Maste buf->parent = NULL; 18719261079SEd Maste 188a0ee8cc6SDag-Erling Smørgrav if (!buf->readonly) { 189fc1ba28aSDag-Erling Smørgrav explicit_bzero(buf->d, buf->alloc); 190a0ee8cc6SDag-Erling Smørgrav free(buf->d); 191a0ee8cc6SDag-Erling Smørgrav } 19219261079SEd Maste freezero(buf, sizeof(*buf)); 193a0ee8cc6SDag-Erling Smørgrav } 194a0ee8cc6SDag-Erling Smørgrav 195a0ee8cc6SDag-Erling Smørgrav void 196a0ee8cc6SDag-Erling Smørgrav sshbuf_reset(struct sshbuf *buf) 197a0ee8cc6SDag-Erling Smørgrav { 198a0ee8cc6SDag-Erling Smørgrav u_char *d; 199a0ee8cc6SDag-Erling Smørgrav 200a0ee8cc6SDag-Erling Smørgrav if (buf->readonly || buf->refcount > 1) { 201a0ee8cc6SDag-Erling Smørgrav /* Nonsensical. Just make buffer appear empty */ 202a0ee8cc6SDag-Erling Smørgrav buf->off = buf->size; 203a0ee8cc6SDag-Erling Smørgrav return; 204a0ee8cc6SDag-Erling Smørgrav } 20538a52bd3SEd Maste if (sshbuf_check_sanity(buf) != 0) 20638a52bd3SEd Maste return; 207a0ee8cc6SDag-Erling Smørgrav buf->off = buf->size = 0; 208a0ee8cc6SDag-Erling Smørgrav if (buf->alloc != SSHBUF_SIZE_INIT) { 2094f52dfbbSDag-Erling Smørgrav if ((d = recallocarray(buf->d, buf->alloc, SSHBUF_SIZE_INIT, 2104f52dfbbSDag-Erling Smørgrav 1)) != NULL) { 211a0ee8cc6SDag-Erling Smørgrav buf->cd = buf->d = d; 212a0ee8cc6SDag-Erling Smørgrav buf->alloc = SSHBUF_SIZE_INIT; 213a0ee8cc6SDag-Erling Smørgrav } 214a0ee8cc6SDag-Erling Smørgrav } 21538a52bd3SEd Maste explicit_bzero(buf->d, buf->alloc); 216a0ee8cc6SDag-Erling Smørgrav } 217a0ee8cc6SDag-Erling Smørgrav 218a0ee8cc6SDag-Erling Smørgrav size_t 219a0ee8cc6SDag-Erling Smørgrav sshbuf_max_size(const struct sshbuf *buf) 220a0ee8cc6SDag-Erling Smørgrav { 221a0ee8cc6SDag-Erling Smørgrav return buf->max_size; 222a0ee8cc6SDag-Erling Smørgrav } 223a0ee8cc6SDag-Erling Smørgrav 224a0ee8cc6SDag-Erling Smørgrav size_t 225a0ee8cc6SDag-Erling Smørgrav sshbuf_alloc(const struct sshbuf *buf) 226a0ee8cc6SDag-Erling Smørgrav { 227a0ee8cc6SDag-Erling Smørgrav return buf->alloc; 228a0ee8cc6SDag-Erling Smørgrav } 229a0ee8cc6SDag-Erling Smørgrav 230a0ee8cc6SDag-Erling Smørgrav const struct sshbuf * 231a0ee8cc6SDag-Erling Smørgrav sshbuf_parent(const struct sshbuf *buf) 232a0ee8cc6SDag-Erling Smørgrav { 233a0ee8cc6SDag-Erling Smørgrav return buf->parent; 234a0ee8cc6SDag-Erling Smørgrav } 235a0ee8cc6SDag-Erling Smørgrav 236a0ee8cc6SDag-Erling Smørgrav u_int 237a0ee8cc6SDag-Erling Smørgrav sshbuf_refcount(const struct sshbuf *buf) 238a0ee8cc6SDag-Erling Smørgrav { 239a0ee8cc6SDag-Erling Smørgrav return buf->refcount; 240a0ee8cc6SDag-Erling Smørgrav } 241a0ee8cc6SDag-Erling Smørgrav 242a0ee8cc6SDag-Erling Smørgrav int 243a0ee8cc6SDag-Erling Smørgrav sshbuf_set_max_size(struct sshbuf *buf, size_t max_size) 244a0ee8cc6SDag-Erling Smørgrav { 245a0ee8cc6SDag-Erling Smørgrav size_t rlen; 246a0ee8cc6SDag-Erling Smørgrav u_char *dp; 247a0ee8cc6SDag-Erling Smørgrav int r; 248a0ee8cc6SDag-Erling Smørgrav 249a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("set max buf = %p len = %zu", buf, max_size)); 250a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_sanity(buf)) != 0) 251a0ee8cc6SDag-Erling Smørgrav return r; 252a0ee8cc6SDag-Erling Smørgrav if (max_size == buf->max_size) 253a0ee8cc6SDag-Erling Smørgrav return 0; 254a0ee8cc6SDag-Erling Smørgrav if (buf->readonly || buf->refcount > 1) 255a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_BUFFER_READ_ONLY; 256a0ee8cc6SDag-Erling Smørgrav if (max_size > SSHBUF_SIZE_MAX) 257a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_NO_BUFFER_SPACE; 258a0ee8cc6SDag-Erling Smørgrav /* pack and realloc if necessary */ 259a0ee8cc6SDag-Erling Smørgrav sshbuf_maybe_pack(buf, max_size < buf->size); 260a0ee8cc6SDag-Erling Smørgrav if (max_size < buf->alloc && max_size > buf->size) { 261a0ee8cc6SDag-Erling Smørgrav if (buf->size < SSHBUF_SIZE_INIT) 262a0ee8cc6SDag-Erling Smørgrav rlen = SSHBUF_SIZE_INIT; 263a0ee8cc6SDag-Erling Smørgrav else 264ca86bcf2SDag-Erling Smørgrav rlen = ROUNDUP(buf->size, SSHBUF_SIZE_INC); 265a0ee8cc6SDag-Erling Smørgrav if (rlen > max_size) 266a0ee8cc6SDag-Erling Smørgrav rlen = max_size; 267a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("new alloc = %zu", rlen)); 2684f52dfbbSDag-Erling Smørgrav if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL) 269a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 270a0ee8cc6SDag-Erling Smørgrav buf->cd = buf->d = dp; 271a0ee8cc6SDag-Erling Smørgrav buf->alloc = rlen; 272a0ee8cc6SDag-Erling Smørgrav } 273a0ee8cc6SDag-Erling Smørgrav SSHBUF_TELL("new-max"); 274a0ee8cc6SDag-Erling Smørgrav if (max_size < buf->alloc) 275a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_NO_BUFFER_SPACE; 276a0ee8cc6SDag-Erling Smørgrav buf->max_size = max_size; 277a0ee8cc6SDag-Erling Smørgrav return 0; 278a0ee8cc6SDag-Erling Smørgrav } 279a0ee8cc6SDag-Erling Smørgrav 280a0ee8cc6SDag-Erling Smørgrav size_t 281a0ee8cc6SDag-Erling Smørgrav sshbuf_len(const struct sshbuf *buf) 282a0ee8cc6SDag-Erling Smørgrav { 283a0ee8cc6SDag-Erling Smørgrav if (sshbuf_check_sanity(buf) != 0) 284a0ee8cc6SDag-Erling Smørgrav return 0; 285a0ee8cc6SDag-Erling Smørgrav return buf->size - buf->off; 286a0ee8cc6SDag-Erling Smørgrav } 287a0ee8cc6SDag-Erling Smørgrav 288a0ee8cc6SDag-Erling Smørgrav size_t 289a0ee8cc6SDag-Erling Smørgrav sshbuf_avail(const struct sshbuf *buf) 290a0ee8cc6SDag-Erling Smørgrav { 291a0ee8cc6SDag-Erling Smørgrav if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1) 292a0ee8cc6SDag-Erling Smørgrav return 0; 293a0ee8cc6SDag-Erling Smørgrav return buf->max_size - (buf->size - buf->off); 294a0ee8cc6SDag-Erling Smørgrav } 295a0ee8cc6SDag-Erling Smørgrav 296a0ee8cc6SDag-Erling Smørgrav const u_char * 297a0ee8cc6SDag-Erling Smørgrav sshbuf_ptr(const struct sshbuf *buf) 298a0ee8cc6SDag-Erling Smørgrav { 299a0ee8cc6SDag-Erling Smørgrav if (sshbuf_check_sanity(buf) != 0) 300a0ee8cc6SDag-Erling Smørgrav return NULL; 301a0ee8cc6SDag-Erling Smørgrav return buf->cd + buf->off; 302a0ee8cc6SDag-Erling Smørgrav } 303a0ee8cc6SDag-Erling Smørgrav 304a0ee8cc6SDag-Erling Smørgrav u_char * 305a0ee8cc6SDag-Erling Smørgrav sshbuf_mutable_ptr(const struct sshbuf *buf) 306a0ee8cc6SDag-Erling Smørgrav { 307a0ee8cc6SDag-Erling Smørgrav if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1) 308a0ee8cc6SDag-Erling Smørgrav return NULL; 309a0ee8cc6SDag-Erling Smørgrav return buf->d + buf->off; 310a0ee8cc6SDag-Erling Smørgrav } 311a0ee8cc6SDag-Erling Smørgrav 312a0ee8cc6SDag-Erling Smørgrav int 313a0ee8cc6SDag-Erling Smørgrav sshbuf_check_reserve(const struct sshbuf *buf, size_t len) 314a0ee8cc6SDag-Erling Smørgrav { 315a0ee8cc6SDag-Erling Smørgrav int r; 316a0ee8cc6SDag-Erling Smørgrav 317a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_sanity(buf)) != 0) 318a0ee8cc6SDag-Erling Smørgrav return r; 319a0ee8cc6SDag-Erling Smørgrav if (buf->readonly || buf->refcount > 1) 320a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_BUFFER_READ_ONLY; 321a0ee8cc6SDag-Erling Smørgrav SSHBUF_TELL("check"); 322a0ee8cc6SDag-Erling Smørgrav /* Check that len is reasonable and that max_size + available < len */ 323a0ee8cc6SDag-Erling Smørgrav if (len > buf->max_size || buf->max_size - len < buf->size - buf->off) 324a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_NO_BUFFER_SPACE; 325a0ee8cc6SDag-Erling Smørgrav return 0; 326a0ee8cc6SDag-Erling Smørgrav } 327a0ee8cc6SDag-Erling Smørgrav 328a0ee8cc6SDag-Erling Smørgrav int 329ca86bcf2SDag-Erling Smørgrav sshbuf_allocate(struct sshbuf *buf, size_t len) 330a0ee8cc6SDag-Erling Smørgrav { 331a0ee8cc6SDag-Erling Smørgrav size_t rlen, need; 332a0ee8cc6SDag-Erling Smørgrav u_char *dp; 333a0ee8cc6SDag-Erling Smørgrav int r; 334a0ee8cc6SDag-Erling Smørgrav 335ca86bcf2SDag-Erling Smørgrav SSHBUF_DBG(("allocate buf = %p len = %zu", buf, len)); 336a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_reserve(buf, len)) != 0) 337a0ee8cc6SDag-Erling Smørgrav return r; 338a0ee8cc6SDag-Erling Smørgrav /* 339a0ee8cc6SDag-Erling Smørgrav * If the requested allocation appended would push us past max_size 340a0ee8cc6SDag-Erling Smørgrav * then pack the buffer, zeroing buf->off. 341a0ee8cc6SDag-Erling Smørgrav */ 342a0ee8cc6SDag-Erling Smørgrav sshbuf_maybe_pack(buf, buf->size + len > buf->max_size); 343ca86bcf2SDag-Erling Smørgrav SSHBUF_TELL("allocate"); 344ca86bcf2SDag-Erling Smørgrav if (len + buf->size <= buf->alloc) 345ca86bcf2SDag-Erling Smørgrav return 0; /* already have it. */ 346ca86bcf2SDag-Erling Smørgrav 347a0ee8cc6SDag-Erling Smørgrav /* 348a0ee8cc6SDag-Erling Smørgrav * Prefer to alloc in SSHBUF_SIZE_INC units, but 349a0ee8cc6SDag-Erling Smørgrav * allocate less if doing so would overflow max_size. 350a0ee8cc6SDag-Erling Smørgrav */ 351a0ee8cc6SDag-Erling Smørgrav need = len + buf->size - buf->alloc; 352ca86bcf2SDag-Erling Smørgrav rlen = ROUNDUP(buf->alloc + need, SSHBUF_SIZE_INC); 353a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("need %zu initial rlen %zu", need, rlen)); 354a0ee8cc6SDag-Erling Smørgrav if (rlen > buf->max_size) 355a0ee8cc6SDag-Erling Smørgrav rlen = buf->alloc + need; 356a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("adjusted rlen %zu", rlen)); 3574f52dfbbSDag-Erling Smørgrav if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL) { 358a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("realloc fail")); 359a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 360a0ee8cc6SDag-Erling Smørgrav } 361a0ee8cc6SDag-Erling Smørgrav buf->alloc = rlen; 362a0ee8cc6SDag-Erling Smørgrav buf->cd = buf->d = dp; 363a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_reserve(buf, len)) < 0) { 364a0ee8cc6SDag-Erling Smørgrav /* shouldn't fail */ 365a0ee8cc6SDag-Erling Smørgrav return r; 366a0ee8cc6SDag-Erling Smørgrav } 367ca86bcf2SDag-Erling Smørgrav SSHBUF_TELL("done"); 368ca86bcf2SDag-Erling Smørgrav return 0; 369a0ee8cc6SDag-Erling Smørgrav } 370ca86bcf2SDag-Erling Smørgrav 371ca86bcf2SDag-Erling Smørgrav int 372ca86bcf2SDag-Erling Smørgrav sshbuf_reserve(struct sshbuf *buf, size_t len, u_char **dpp) 373ca86bcf2SDag-Erling Smørgrav { 374ca86bcf2SDag-Erling Smørgrav u_char *dp; 375ca86bcf2SDag-Erling Smørgrav int r; 376ca86bcf2SDag-Erling Smørgrav 377ca86bcf2SDag-Erling Smørgrav if (dpp != NULL) 378ca86bcf2SDag-Erling Smørgrav *dpp = NULL; 379ca86bcf2SDag-Erling Smørgrav 380ca86bcf2SDag-Erling Smørgrav SSHBUF_DBG(("reserve buf = %p len = %zu", buf, len)); 381ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_allocate(buf, len)) != 0) 382ca86bcf2SDag-Erling Smørgrav return r; 383ca86bcf2SDag-Erling Smørgrav 384a0ee8cc6SDag-Erling Smørgrav dp = buf->d + buf->size; 385a0ee8cc6SDag-Erling Smørgrav buf->size += len; 386a0ee8cc6SDag-Erling Smørgrav if (dpp != NULL) 387a0ee8cc6SDag-Erling Smørgrav *dpp = dp; 388a0ee8cc6SDag-Erling Smørgrav return 0; 389a0ee8cc6SDag-Erling Smørgrav } 390a0ee8cc6SDag-Erling Smørgrav 391a0ee8cc6SDag-Erling Smørgrav int 392a0ee8cc6SDag-Erling Smørgrav sshbuf_consume(struct sshbuf *buf, size_t len) 393a0ee8cc6SDag-Erling Smørgrav { 394a0ee8cc6SDag-Erling Smørgrav int r; 395a0ee8cc6SDag-Erling Smørgrav 396a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("len = %zu", len)); 397a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_sanity(buf)) != 0) 398a0ee8cc6SDag-Erling Smørgrav return r; 399a0ee8cc6SDag-Erling Smørgrav if (len == 0) 400a0ee8cc6SDag-Erling Smørgrav return 0; 401a0ee8cc6SDag-Erling Smørgrav if (len > sshbuf_len(buf)) 402a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_MESSAGE_INCOMPLETE; 403a0ee8cc6SDag-Erling Smørgrav buf->off += len; 4044f52dfbbSDag-Erling Smørgrav /* deal with empty buffer */ 4054f52dfbbSDag-Erling Smørgrav if (buf->off == buf->size) 4064f52dfbbSDag-Erling Smørgrav buf->off = buf->size = 0; 407a0ee8cc6SDag-Erling Smørgrav SSHBUF_TELL("done"); 408a0ee8cc6SDag-Erling Smørgrav return 0; 409a0ee8cc6SDag-Erling Smørgrav } 410a0ee8cc6SDag-Erling Smørgrav 411a0ee8cc6SDag-Erling Smørgrav int 412a0ee8cc6SDag-Erling Smørgrav sshbuf_consume_end(struct sshbuf *buf, size_t len) 413a0ee8cc6SDag-Erling Smørgrav { 414a0ee8cc6SDag-Erling Smørgrav int r; 415a0ee8cc6SDag-Erling Smørgrav 416a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("len = %zu", len)); 417a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_sanity(buf)) != 0) 418a0ee8cc6SDag-Erling Smørgrav return r; 419a0ee8cc6SDag-Erling Smørgrav if (len == 0) 420a0ee8cc6SDag-Erling Smørgrav return 0; 421a0ee8cc6SDag-Erling Smørgrav if (len > sshbuf_len(buf)) 422a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_MESSAGE_INCOMPLETE; 423a0ee8cc6SDag-Erling Smørgrav buf->size -= len; 424a0ee8cc6SDag-Erling Smørgrav SSHBUF_TELL("done"); 425a0ee8cc6SDag-Erling Smørgrav return 0; 426a0ee8cc6SDag-Erling Smørgrav } 427a0ee8cc6SDag-Erling Smørgrav 428