1*19261079SEd Maste /* $OpenBSD: sshbuf.c,v 1.15 2020/02/26 13:40:09 jsg 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 #define SSHBUF_INTERNAL 19a0ee8cc6SDag-Erling Smørgrav #include "includes.h" 20a0ee8cc6SDag-Erling Smørgrav 21a0ee8cc6SDag-Erling Smørgrav #include <sys/types.h> 22a0ee8cc6SDag-Erling Smørgrav #include <signal.h> 23a0ee8cc6SDag-Erling Smørgrav #include <stdlib.h> 24a0ee8cc6SDag-Erling Smørgrav #include <stdio.h> 25a0ee8cc6SDag-Erling Smørgrav #include <string.h> 26a0ee8cc6SDag-Erling Smørgrav 27a0ee8cc6SDag-Erling Smørgrav #include "ssherr.h" 28a0ee8cc6SDag-Erling Smørgrav #include "sshbuf.h" 29ca86bcf2SDag-Erling Smørgrav #include "misc.h" 30a0ee8cc6SDag-Erling Smørgrav 31a0ee8cc6SDag-Erling Smørgrav static inline int 32a0ee8cc6SDag-Erling Smørgrav sshbuf_check_sanity(const struct sshbuf *buf) 33a0ee8cc6SDag-Erling Smørgrav { 34a0ee8cc6SDag-Erling Smørgrav SSHBUF_TELL("sanity"); 35a0ee8cc6SDag-Erling Smørgrav if (__predict_false(buf == NULL || 36a0ee8cc6SDag-Erling Smørgrav (!buf->readonly && buf->d != buf->cd) || 37a0ee8cc6SDag-Erling Smørgrav buf->refcount < 1 || buf->refcount > SSHBUF_REFS_MAX || 38a0ee8cc6SDag-Erling Smørgrav buf->cd == NULL || 39a0ee8cc6SDag-Erling Smørgrav buf->max_size > SSHBUF_SIZE_MAX || 40a0ee8cc6SDag-Erling Smørgrav buf->alloc > buf->max_size || 41a0ee8cc6SDag-Erling Smørgrav buf->size > buf->alloc || 42a0ee8cc6SDag-Erling Smørgrav buf->off > buf->size)) { 43a0ee8cc6SDag-Erling Smørgrav /* Do not try to recover from corrupted buffer internals */ 44a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); 45*19261079SEd Maste ssh_signal(SIGSEGV, SIG_DFL); 46a0ee8cc6SDag-Erling Smørgrav raise(SIGSEGV); 47a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 48a0ee8cc6SDag-Erling Smørgrav } 49a0ee8cc6SDag-Erling Smørgrav return 0; 50a0ee8cc6SDag-Erling Smørgrav } 51a0ee8cc6SDag-Erling Smørgrav 52a0ee8cc6SDag-Erling Smørgrav static void 53a0ee8cc6SDag-Erling Smørgrav sshbuf_maybe_pack(struct sshbuf *buf, int force) 54a0ee8cc6SDag-Erling Smørgrav { 55a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("force %d", force)); 56a0ee8cc6SDag-Erling Smørgrav SSHBUF_TELL("pre-pack"); 57a0ee8cc6SDag-Erling Smørgrav if (buf->off == 0 || buf->readonly || buf->refcount > 1) 58a0ee8cc6SDag-Erling Smørgrav return; 59a0ee8cc6SDag-Erling Smørgrav if (force || 60a0ee8cc6SDag-Erling Smørgrav (buf->off >= SSHBUF_PACK_MIN && buf->off >= buf->size / 2)) { 61a0ee8cc6SDag-Erling Smørgrav memmove(buf->d, buf->d + buf->off, buf->size - buf->off); 62a0ee8cc6SDag-Erling Smørgrav buf->size -= buf->off; 63a0ee8cc6SDag-Erling Smørgrav buf->off = 0; 64a0ee8cc6SDag-Erling Smørgrav SSHBUF_TELL("packed"); 65a0ee8cc6SDag-Erling Smørgrav } 66a0ee8cc6SDag-Erling Smørgrav } 67a0ee8cc6SDag-Erling Smørgrav 68a0ee8cc6SDag-Erling Smørgrav struct sshbuf * 69a0ee8cc6SDag-Erling Smørgrav sshbuf_new(void) 70a0ee8cc6SDag-Erling Smørgrav { 71a0ee8cc6SDag-Erling Smørgrav struct sshbuf *ret; 72a0ee8cc6SDag-Erling Smørgrav 73a0ee8cc6SDag-Erling Smørgrav if ((ret = calloc(sizeof(*ret), 1)) == NULL) 74a0ee8cc6SDag-Erling Smørgrav return NULL; 75a0ee8cc6SDag-Erling Smørgrav ret->alloc = SSHBUF_SIZE_INIT; 76a0ee8cc6SDag-Erling Smørgrav ret->max_size = SSHBUF_SIZE_MAX; 77a0ee8cc6SDag-Erling Smørgrav ret->readonly = 0; 78a0ee8cc6SDag-Erling Smørgrav ret->refcount = 1; 79a0ee8cc6SDag-Erling Smørgrav ret->parent = NULL; 80a0ee8cc6SDag-Erling Smørgrav if ((ret->cd = ret->d = calloc(1, ret->alloc)) == NULL) { 81a0ee8cc6SDag-Erling Smørgrav free(ret); 82a0ee8cc6SDag-Erling Smørgrav return NULL; 83a0ee8cc6SDag-Erling Smørgrav } 84a0ee8cc6SDag-Erling Smørgrav return ret; 85a0ee8cc6SDag-Erling Smørgrav } 86a0ee8cc6SDag-Erling Smørgrav 87a0ee8cc6SDag-Erling Smørgrav struct sshbuf * 88a0ee8cc6SDag-Erling Smørgrav sshbuf_from(const void *blob, size_t len) 89a0ee8cc6SDag-Erling Smørgrav { 90a0ee8cc6SDag-Erling Smørgrav struct sshbuf *ret; 91a0ee8cc6SDag-Erling Smørgrav 92a0ee8cc6SDag-Erling Smørgrav if (blob == NULL || len > SSHBUF_SIZE_MAX || 93a0ee8cc6SDag-Erling Smørgrav (ret = calloc(sizeof(*ret), 1)) == NULL) 94a0ee8cc6SDag-Erling Smørgrav return NULL; 95a0ee8cc6SDag-Erling Smørgrav ret->alloc = ret->size = ret->max_size = len; 96a0ee8cc6SDag-Erling Smørgrav ret->readonly = 1; 97a0ee8cc6SDag-Erling Smørgrav ret->refcount = 1; 98a0ee8cc6SDag-Erling Smørgrav ret->parent = NULL; 99a0ee8cc6SDag-Erling Smørgrav ret->cd = blob; 100a0ee8cc6SDag-Erling Smørgrav ret->d = NULL; 101a0ee8cc6SDag-Erling Smørgrav return ret; 102a0ee8cc6SDag-Erling Smørgrav } 103a0ee8cc6SDag-Erling Smørgrav 104a0ee8cc6SDag-Erling Smørgrav int 105a0ee8cc6SDag-Erling Smørgrav sshbuf_set_parent(struct sshbuf *child, struct sshbuf *parent) 106a0ee8cc6SDag-Erling Smørgrav { 107a0ee8cc6SDag-Erling Smørgrav int r; 108a0ee8cc6SDag-Erling Smørgrav 109a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_sanity(child)) != 0 || 110a0ee8cc6SDag-Erling Smørgrav (r = sshbuf_check_sanity(parent)) != 0) 111a0ee8cc6SDag-Erling Smørgrav return r; 112a0ee8cc6SDag-Erling Smørgrav child->parent = parent; 113a0ee8cc6SDag-Erling Smørgrav child->parent->refcount++; 114a0ee8cc6SDag-Erling Smørgrav return 0; 115a0ee8cc6SDag-Erling Smørgrav } 116a0ee8cc6SDag-Erling Smørgrav 117a0ee8cc6SDag-Erling Smørgrav struct sshbuf * 118a0ee8cc6SDag-Erling Smørgrav sshbuf_fromb(struct sshbuf *buf) 119a0ee8cc6SDag-Erling Smørgrav { 120a0ee8cc6SDag-Erling Smørgrav struct sshbuf *ret; 121a0ee8cc6SDag-Erling Smørgrav 122a0ee8cc6SDag-Erling Smørgrav if (sshbuf_check_sanity(buf) != 0) 123a0ee8cc6SDag-Erling Smørgrav return NULL; 124a0ee8cc6SDag-Erling Smørgrav if ((ret = sshbuf_from(sshbuf_ptr(buf), sshbuf_len(buf))) == NULL) 125a0ee8cc6SDag-Erling Smørgrav return NULL; 126a0ee8cc6SDag-Erling Smørgrav if (sshbuf_set_parent(ret, buf) != 0) { 127a0ee8cc6SDag-Erling Smørgrav sshbuf_free(ret); 128a0ee8cc6SDag-Erling Smørgrav return NULL; 129a0ee8cc6SDag-Erling Smørgrav } 130a0ee8cc6SDag-Erling Smørgrav return ret; 131a0ee8cc6SDag-Erling Smørgrav } 132a0ee8cc6SDag-Erling Smørgrav 133a0ee8cc6SDag-Erling Smørgrav void 134a0ee8cc6SDag-Erling Smørgrav sshbuf_free(struct sshbuf *buf) 135a0ee8cc6SDag-Erling Smørgrav { 136a0ee8cc6SDag-Erling Smørgrav if (buf == NULL) 137a0ee8cc6SDag-Erling Smørgrav return; 138a0ee8cc6SDag-Erling Smørgrav /* 139a0ee8cc6SDag-Erling Smørgrav * The following will leak on insane buffers, but this is the safest 140a0ee8cc6SDag-Erling Smørgrav * course of action - an invalid pointer or already-freed pointer may 141a0ee8cc6SDag-Erling Smørgrav * have been passed to us and continuing to scribble over memory would 142a0ee8cc6SDag-Erling Smørgrav * be bad. 143a0ee8cc6SDag-Erling Smørgrav */ 144a0ee8cc6SDag-Erling Smørgrav if (sshbuf_check_sanity(buf) != 0) 145a0ee8cc6SDag-Erling Smørgrav return; 146*19261079SEd Maste 147a0ee8cc6SDag-Erling Smørgrav /* 148a0ee8cc6SDag-Erling Smørgrav * If we are a parent with still-extant children, then don't free just 149a0ee8cc6SDag-Erling Smørgrav * yet. The last child's call to sshbuf_free should decrement our 150a0ee8cc6SDag-Erling Smørgrav * refcount to 0 and trigger the actual free. 151a0ee8cc6SDag-Erling Smørgrav */ 152a0ee8cc6SDag-Erling Smørgrav buf->refcount--; 153a0ee8cc6SDag-Erling Smørgrav if (buf->refcount > 0) 154a0ee8cc6SDag-Erling Smørgrav return; 155*19261079SEd Maste 156*19261079SEd Maste /* 157*19261079SEd Maste * If we are a child, the free our parent to decrement its reference 158*19261079SEd Maste * count and possibly free it. 159*19261079SEd Maste */ 160*19261079SEd Maste sshbuf_free(buf->parent); 161*19261079SEd Maste buf->parent = NULL; 162*19261079SEd Maste 163a0ee8cc6SDag-Erling Smørgrav if (!buf->readonly) { 164fc1ba28aSDag-Erling Smørgrav explicit_bzero(buf->d, buf->alloc); 165a0ee8cc6SDag-Erling Smørgrav free(buf->d); 166a0ee8cc6SDag-Erling Smørgrav } 167*19261079SEd Maste freezero(buf, sizeof(*buf)); 168a0ee8cc6SDag-Erling Smørgrav } 169a0ee8cc6SDag-Erling Smørgrav 170a0ee8cc6SDag-Erling Smørgrav void 171a0ee8cc6SDag-Erling Smørgrav sshbuf_reset(struct sshbuf *buf) 172a0ee8cc6SDag-Erling Smørgrav { 173a0ee8cc6SDag-Erling Smørgrav u_char *d; 174a0ee8cc6SDag-Erling Smørgrav 175a0ee8cc6SDag-Erling Smørgrav if (buf->readonly || buf->refcount > 1) { 176a0ee8cc6SDag-Erling Smørgrav /* Nonsensical. Just make buffer appear empty */ 177a0ee8cc6SDag-Erling Smørgrav buf->off = buf->size; 178a0ee8cc6SDag-Erling Smørgrav return; 179a0ee8cc6SDag-Erling Smørgrav } 1804f52dfbbSDag-Erling Smørgrav (void) sshbuf_check_sanity(buf); 181a0ee8cc6SDag-Erling Smørgrav buf->off = buf->size = 0; 182a0ee8cc6SDag-Erling Smørgrav if (buf->alloc != SSHBUF_SIZE_INIT) { 1834f52dfbbSDag-Erling Smørgrav if ((d = recallocarray(buf->d, buf->alloc, SSHBUF_SIZE_INIT, 1844f52dfbbSDag-Erling Smørgrav 1)) != NULL) { 185a0ee8cc6SDag-Erling Smørgrav buf->cd = buf->d = d; 186a0ee8cc6SDag-Erling Smørgrav buf->alloc = SSHBUF_SIZE_INIT; 187a0ee8cc6SDag-Erling Smørgrav } 188a0ee8cc6SDag-Erling Smørgrav } 1894f52dfbbSDag-Erling Smørgrav explicit_bzero(buf->d, SSHBUF_SIZE_INIT); 190a0ee8cc6SDag-Erling Smørgrav } 191a0ee8cc6SDag-Erling Smørgrav 192a0ee8cc6SDag-Erling Smørgrav size_t 193a0ee8cc6SDag-Erling Smørgrav sshbuf_max_size(const struct sshbuf *buf) 194a0ee8cc6SDag-Erling Smørgrav { 195a0ee8cc6SDag-Erling Smørgrav return buf->max_size; 196a0ee8cc6SDag-Erling Smørgrav } 197a0ee8cc6SDag-Erling Smørgrav 198a0ee8cc6SDag-Erling Smørgrav size_t 199a0ee8cc6SDag-Erling Smørgrav sshbuf_alloc(const struct sshbuf *buf) 200a0ee8cc6SDag-Erling Smørgrav { 201a0ee8cc6SDag-Erling Smørgrav return buf->alloc; 202a0ee8cc6SDag-Erling Smørgrav } 203a0ee8cc6SDag-Erling Smørgrav 204a0ee8cc6SDag-Erling Smørgrav const struct sshbuf * 205a0ee8cc6SDag-Erling Smørgrav sshbuf_parent(const struct sshbuf *buf) 206a0ee8cc6SDag-Erling Smørgrav { 207a0ee8cc6SDag-Erling Smørgrav return buf->parent; 208a0ee8cc6SDag-Erling Smørgrav } 209a0ee8cc6SDag-Erling Smørgrav 210a0ee8cc6SDag-Erling Smørgrav u_int 211a0ee8cc6SDag-Erling Smørgrav sshbuf_refcount(const struct sshbuf *buf) 212a0ee8cc6SDag-Erling Smørgrav { 213a0ee8cc6SDag-Erling Smørgrav return buf->refcount; 214a0ee8cc6SDag-Erling Smørgrav } 215a0ee8cc6SDag-Erling Smørgrav 216a0ee8cc6SDag-Erling Smørgrav int 217a0ee8cc6SDag-Erling Smørgrav sshbuf_set_max_size(struct sshbuf *buf, size_t max_size) 218a0ee8cc6SDag-Erling Smørgrav { 219a0ee8cc6SDag-Erling Smørgrav size_t rlen; 220a0ee8cc6SDag-Erling Smørgrav u_char *dp; 221a0ee8cc6SDag-Erling Smørgrav int r; 222a0ee8cc6SDag-Erling Smørgrav 223a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("set max buf = %p len = %zu", buf, max_size)); 224a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_sanity(buf)) != 0) 225a0ee8cc6SDag-Erling Smørgrav return r; 226a0ee8cc6SDag-Erling Smørgrav if (max_size == buf->max_size) 227a0ee8cc6SDag-Erling Smørgrav return 0; 228a0ee8cc6SDag-Erling Smørgrav if (buf->readonly || buf->refcount > 1) 229a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_BUFFER_READ_ONLY; 230a0ee8cc6SDag-Erling Smørgrav if (max_size > SSHBUF_SIZE_MAX) 231a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_NO_BUFFER_SPACE; 232a0ee8cc6SDag-Erling Smørgrav /* pack and realloc if necessary */ 233a0ee8cc6SDag-Erling Smørgrav sshbuf_maybe_pack(buf, max_size < buf->size); 234a0ee8cc6SDag-Erling Smørgrav if (max_size < buf->alloc && max_size > buf->size) { 235a0ee8cc6SDag-Erling Smørgrav if (buf->size < SSHBUF_SIZE_INIT) 236a0ee8cc6SDag-Erling Smørgrav rlen = SSHBUF_SIZE_INIT; 237a0ee8cc6SDag-Erling Smørgrav else 238ca86bcf2SDag-Erling Smørgrav rlen = ROUNDUP(buf->size, SSHBUF_SIZE_INC); 239a0ee8cc6SDag-Erling Smørgrav if (rlen > max_size) 240a0ee8cc6SDag-Erling Smørgrav rlen = max_size; 241a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("new alloc = %zu", rlen)); 2424f52dfbbSDag-Erling Smørgrav if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL) 243a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 244a0ee8cc6SDag-Erling Smørgrav buf->cd = buf->d = dp; 245a0ee8cc6SDag-Erling Smørgrav buf->alloc = rlen; 246a0ee8cc6SDag-Erling Smørgrav } 247a0ee8cc6SDag-Erling Smørgrav SSHBUF_TELL("new-max"); 248a0ee8cc6SDag-Erling Smørgrav if (max_size < buf->alloc) 249a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_NO_BUFFER_SPACE; 250a0ee8cc6SDag-Erling Smørgrav buf->max_size = max_size; 251a0ee8cc6SDag-Erling Smørgrav return 0; 252a0ee8cc6SDag-Erling Smørgrav } 253a0ee8cc6SDag-Erling Smørgrav 254a0ee8cc6SDag-Erling Smørgrav size_t 255a0ee8cc6SDag-Erling Smørgrav sshbuf_len(const struct sshbuf *buf) 256a0ee8cc6SDag-Erling Smørgrav { 257a0ee8cc6SDag-Erling Smørgrav if (sshbuf_check_sanity(buf) != 0) 258a0ee8cc6SDag-Erling Smørgrav return 0; 259a0ee8cc6SDag-Erling Smørgrav return buf->size - buf->off; 260a0ee8cc6SDag-Erling Smørgrav } 261a0ee8cc6SDag-Erling Smørgrav 262a0ee8cc6SDag-Erling Smørgrav size_t 263a0ee8cc6SDag-Erling Smørgrav sshbuf_avail(const struct sshbuf *buf) 264a0ee8cc6SDag-Erling Smørgrav { 265a0ee8cc6SDag-Erling Smørgrav if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1) 266a0ee8cc6SDag-Erling Smørgrav return 0; 267a0ee8cc6SDag-Erling Smørgrav return buf->max_size - (buf->size - buf->off); 268a0ee8cc6SDag-Erling Smørgrav } 269a0ee8cc6SDag-Erling Smørgrav 270a0ee8cc6SDag-Erling Smørgrav const u_char * 271a0ee8cc6SDag-Erling Smørgrav sshbuf_ptr(const struct sshbuf *buf) 272a0ee8cc6SDag-Erling Smørgrav { 273a0ee8cc6SDag-Erling Smørgrav if (sshbuf_check_sanity(buf) != 0) 274a0ee8cc6SDag-Erling Smørgrav return NULL; 275a0ee8cc6SDag-Erling Smørgrav return buf->cd + buf->off; 276a0ee8cc6SDag-Erling Smørgrav } 277a0ee8cc6SDag-Erling Smørgrav 278a0ee8cc6SDag-Erling Smørgrav u_char * 279a0ee8cc6SDag-Erling Smørgrav sshbuf_mutable_ptr(const struct sshbuf *buf) 280a0ee8cc6SDag-Erling Smørgrav { 281a0ee8cc6SDag-Erling Smørgrav if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1) 282a0ee8cc6SDag-Erling Smørgrav return NULL; 283a0ee8cc6SDag-Erling Smørgrav return buf->d + buf->off; 284a0ee8cc6SDag-Erling Smørgrav } 285a0ee8cc6SDag-Erling Smørgrav 286a0ee8cc6SDag-Erling Smørgrav int 287a0ee8cc6SDag-Erling Smørgrav sshbuf_check_reserve(const struct sshbuf *buf, size_t len) 288a0ee8cc6SDag-Erling Smørgrav { 289a0ee8cc6SDag-Erling Smørgrav int r; 290a0ee8cc6SDag-Erling Smørgrav 291a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_sanity(buf)) != 0) 292a0ee8cc6SDag-Erling Smørgrav return r; 293a0ee8cc6SDag-Erling Smørgrav if (buf->readonly || buf->refcount > 1) 294a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_BUFFER_READ_ONLY; 295a0ee8cc6SDag-Erling Smørgrav SSHBUF_TELL("check"); 296a0ee8cc6SDag-Erling Smørgrav /* Check that len is reasonable and that max_size + available < len */ 297a0ee8cc6SDag-Erling Smørgrav if (len > buf->max_size || buf->max_size - len < buf->size - buf->off) 298a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_NO_BUFFER_SPACE; 299a0ee8cc6SDag-Erling Smørgrav return 0; 300a0ee8cc6SDag-Erling Smørgrav } 301a0ee8cc6SDag-Erling Smørgrav 302a0ee8cc6SDag-Erling Smørgrav int 303ca86bcf2SDag-Erling Smørgrav sshbuf_allocate(struct sshbuf *buf, size_t len) 304a0ee8cc6SDag-Erling Smørgrav { 305a0ee8cc6SDag-Erling Smørgrav size_t rlen, need; 306a0ee8cc6SDag-Erling Smørgrav u_char *dp; 307a0ee8cc6SDag-Erling Smørgrav int r; 308a0ee8cc6SDag-Erling Smørgrav 309ca86bcf2SDag-Erling Smørgrav SSHBUF_DBG(("allocate buf = %p len = %zu", buf, len)); 310a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_reserve(buf, len)) != 0) 311a0ee8cc6SDag-Erling Smørgrav return r; 312a0ee8cc6SDag-Erling Smørgrav /* 313a0ee8cc6SDag-Erling Smørgrav * If the requested allocation appended would push us past max_size 314a0ee8cc6SDag-Erling Smørgrav * then pack the buffer, zeroing buf->off. 315a0ee8cc6SDag-Erling Smørgrav */ 316a0ee8cc6SDag-Erling Smørgrav sshbuf_maybe_pack(buf, buf->size + len > buf->max_size); 317ca86bcf2SDag-Erling Smørgrav SSHBUF_TELL("allocate"); 318ca86bcf2SDag-Erling Smørgrav if (len + buf->size <= buf->alloc) 319ca86bcf2SDag-Erling Smørgrav return 0; /* already have it. */ 320ca86bcf2SDag-Erling Smørgrav 321a0ee8cc6SDag-Erling Smørgrav /* 322a0ee8cc6SDag-Erling Smørgrav * Prefer to alloc in SSHBUF_SIZE_INC units, but 323a0ee8cc6SDag-Erling Smørgrav * allocate less if doing so would overflow max_size. 324a0ee8cc6SDag-Erling Smørgrav */ 325a0ee8cc6SDag-Erling Smørgrav need = len + buf->size - buf->alloc; 326ca86bcf2SDag-Erling Smørgrav rlen = ROUNDUP(buf->alloc + need, SSHBUF_SIZE_INC); 327a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("need %zu initial rlen %zu", need, rlen)); 328a0ee8cc6SDag-Erling Smørgrav if (rlen > buf->max_size) 329a0ee8cc6SDag-Erling Smørgrav rlen = buf->alloc + need; 330a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("adjusted rlen %zu", rlen)); 3314f52dfbbSDag-Erling Smørgrav if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL) { 332a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("realloc fail")); 333a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 334a0ee8cc6SDag-Erling Smørgrav } 335a0ee8cc6SDag-Erling Smørgrav buf->alloc = rlen; 336a0ee8cc6SDag-Erling Smørgrav buf->cd = buf->d = dp; 337a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_reserve(buf, len)) < 0) { 338a0ee8cc6SDag-Erling Smørgrav /* shouldn't fail */ 339a0ee8cc6SDag-Erling Smørgrav return r; 340a0ee8cc6SDag-Erling Smørgrav } 341ca86bcf2SDag-Erling Smørgrav SSHBUF_TELL("done"); 342ca86bcf2SDag-Erling Smørgrav return 0; 343a0ee8cc6SDag-Erling Smørgrav } 344ca86bcf2SDag-Erling Smørgrav 345ca86bcf2SDag-Erling Smørgrav int 346ca86bcf2SDag-Erling Smørgrav sshbuf_reserve(struct sshbuf *buf, size_t len, u_char **dpp) 347ca86bcf2SDag-Erling Smørgrav { 348ca86bcf2SDag-Erling Smørgrav u_char *dp; 349ca86bcf2SDag-Erling Smørgrav int r; 350ca86bcf2SDag-Erling Smørgrav 351ca86bcf2SDag-Erling Smørgrav if (dpp != NULL) 352ca86bcf2SDag-Erling Smørgrav *dpp = NULL; 353ca86bcf2SDag-Erling Smørgrav 354ca86bcf2SDag-Erling Smørgrav SSHBUF_DBG(("reserve buf = %p len = %zu", buf, len)); 355ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_allocate(buf, len)) != 0) 356ca86bcf2SDag-Erling Smørgrav return r; 357ca86bcf2SDag-Erling Smørgrav 358a0ee8cc6SDag-Erling Smørgrav dp = buf->d + buf->size; 359a0ee8cc6SDag-Erling Smørgrav buf->size += len; 360a0ee8cc6SDag-Erling Smørgrav if (dpp != NULL) 361a0ee8cc6SDag-Erling Smørgrav *dpp = dp; 362a0ee8cc6SDag-Erling Smørgrav return 0; 363a0ee8cc6SDag-Erling Smørgrav } 364a0ee8cc6SDag-Erling Smørgrav 365a0ee8cc6SDag-Erling Smørgrav int 366a0ee8cc6SDag-Erling Smørgrav sshbuf_consume(struct sshbuf *buf, size_t len) 367a0ee8cc6SDag-Erling Smørgrav { 368a0ee8cc6SDag-Erling Smørgrav int r; 369a0ee8cc6SDag-Erling Smørgrav 370a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("len = %zu", len)); 371a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_sanity(buf)) != 0) 372a0ee8cc6SDag-Erling Smørgrav return r; 373a0ee8cc6SDag-Erling Smørgrav if (len == 0) 374a0ee8cc6SDag-Erling Smørgrav return 0; 375a0ee8cc6SDag-Erling Smørgrav if (len > sshbuf_len(buf)) 376a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_MESSAGE_INCOMPLETE; 377a0ee8cc6SDag-Erling Smørgrav buf->off += len; 3784f52dfbbSDag-Erling Smørgrav /* deal with empty buffer */ 3794f52dfbbSDag-Erling Smørgrav if (buf->off == buf->size) 3804f52dfbbSDag-Erling Smørgrav buf->off = buf->size = 0; 381a0ee8cc6SDag-Erling Smørgrav SSHBUF_TELL("done"); 382a0ee8cc6SDag-Erling Smørgrav return 0; 383a0ee8cc6SDag-Erling Smørgrav } 384a0ee8cc6SDag-Erling Smørgrav 385a0ee8cc6SDag-Erling Smørgrav int 386a0ee8cc6SDag-Erling Smørgrav sshbuf_consume_end(struct sshbuf *buf, size_t len) 387a0ee8cc6SDag-Erling Smørgrav { 388a0ee8cc6SDag-Erling Smørgrav int r; 389a0ee8cc6SDag-Erling Smørgrav 390a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("len = %zu", len)); 391a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_sanity(buf)) != 0) 392a0ee8cc6SDag-Erling Smørgrav return r; 393a0ee8cc6SDag-Erling Smørgrav if (len == 0) 394a0ee8cc6SDag-Erling Smørgrav return 0; 395a0ee8cc6SDag-Erling Smørgrav if (len > sshbuf_len(buf)) 396a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_MESSAGE_INCOMPLETE; 397a0ee8cc6SDag-Erling Smørgrav buf->size -= len; 398a0ee8cc6SDag-Erling Smørgrav SSHBUF_TELL("done"); 399a0ee8cc6SDag-Erling Smørgrav return 0; 400a0ee8cc6SDag-Erling Smørgrav } 401a0ee8cc6SDag-Erling Smørgrav 402