1*38a52bd3SEd Maste /* $OpenBSD: sshbuf.c,v 1.18 2022/05/25 06:03:44 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 #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")); 4519261079SEd 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; 112*38a52bd3SEd Maste if (child->parent != NULL && child->parent != parent) 113*38a52bd3SEd Maste return SSH_ERR_INTERNAL_ERROR; 114a0ee8cc6SDag-Erling Smørgrav child->parent = parent; 115a0ee8cc6SDag-Erling Smørgrav child->parent->refcount++; 116a0ee8cc6SDag-Erling Smørgrav return 0; 117a0ee8cc6SDag-Erling Smørgrav } 118a0ee8cc6SDag-Erling Smørgrav 119a0ee8cc6SDag-Erling Smørgrav struct sshbuf * 120a0ee8cc6SDag-Erling Smørgrav sshbuf_fromb(struct sshbuf *buf) 121a0ee8cc6SDag-Erling Smørgrav { 122a0ee8cc6SDag-Erling Smørgrav struct sshbuf *ret; 123a0ee8cc6SDag-Erling Smørgrav 124a0ee8cc6SDag-Erling Smørgrav if (sshbuf_check_sanity(buf) != 0) 125a0ee8cc6SDag-Erling Smørgrav return NULL; 126a0ee8cc6SDag-Erling Smørgrav if ((ret = sshbuf_from(sshbuf_ptr(buf), sshbuf_len(buf))) == NULL) 127a0ee8cc6SDag-Erling Smørgrav return NULL; 128a0ee8cc6SDag-Erling Smørgrav if (sshbuf_set_parent(ret, buf) != 0) { 129a0ee8cc6SDag-Erling Smørgrav sshbuf_free(ret); 130a0ee8cc6SDag-Erling Smørgrav return NULL; 131a0ee8cc6SDag-Erling Smørgrav } 132a0ee8cc6SDag-Erling Smørgrav return ret; 133a0ee8cc6SDag-Erling Smørgrav } 134a0ee8cc6SDag-Erling Smørgrav 135a0ee8cc6SDag-Erling Smørgrav void 136a0ee8cc6SDag-Erling Smørgrav sshbuf_free(struct sshbuf *buf) 137a0ee8cc6SDag-Erling Smørgrav { 138a0ee8cc6SDag-Erling Smørgrav if (buf == NULL) 139a0ee8cc6SDag-Erling Smørgrav return; 140a0ee8cc6SDag-Erling Smørgrav /* 141a0ee8cc6SDag-Erling Smørgrav * The following will leak on insane buffers, but this is the safest 142a0ee8cc6SDag-Erling Smørgrav * course of action - an invalid pointer or already-freed pointer may 143a0ee8cc6SDag-Erling Smørgrav * have been passed to us and continuing to scribble over memory would 144a0ee8cc6SDag-Erling Smørgrav * be bad. 145a0ee8cc6SDag-Erling Smørgrav */ 146a0ee8cc6SDag-Erling Smørgrav if (sshbuf_check_sanity(buf) != 0) 147a0ee8cc6SDag-Erling Smørgrav return; 14819261079SEd Maste 149a0ee8cc6SDag-Erling Smørgrav /* 150a0ee8cc6SDag-Erling Smørgrav * If we are a parent with still-extant children, then don't free just 151a0ee8cc6SDag-Erling Smørgrav * yet. The last child's call to sshbuf_free should decrement our 152a0ee8cc6SDag-Erling Smørgrav * refcount to 0 and trigger the actual free. 153a0ee8cc6SDag-Erling Smørgrav */ 154a0ee8cc6SDag-Erling Smørgrav buf->refcount--; 155a0ee8cc6SDag-Erling Smørgrav if (buf->refcount > 0) 156a0ee8cc6SDag-Erling Smørgrav return; 15719261079SEd Maste 15819261079SEd Maste /* 15919261079SEd Maste * If we are a child, the free our parent to decrement its reference 16019261079SEd Maste * count and possibly free it. 16119261079SEd Maste */ 16219261079SEd Maste sshbuf_free(buf->parent); 16319261079SEd Maste buf->parent = NULL; 16419261079SEd Maste 165a0ee8cc6SDag-Erling Smørgrav if (!buf->readonly) { 166fc1ba28aSDag-Erling Smørgrav explicit_bzero(buf->d, buf->alloc); 167a0ee8cc6SDag-Erling Smørgrav free(buf->d); 168a0ee8cc6SDag-Erling Smørgrav } 16919261079SEd Maste freezero(buf, sizeof(*buf)); 170a0ee8cc6SDag-Erling Smørgrav } 171a0ee8cc6SDag-Erling Smørgrav 172a0ee8cc6SDag-Erling Smørgrav void 173a0ee8cc6SDag-Erling Smørgrav sshbuf_reset(struct sshbuf *buf) 174a0ee8cc6SDag-Erling Smørgrav { 175a0ee8cc6SDag-Erling Smørgrav u_char *d; 176a0ee8cc6SDag-Erling Smørgrav 177a0ee8cc6SDag-Erling Smørgrav if (buf->readonly || buf->refcount > 1) { 178a0ee8cc6SDag-Erling Smørgrav /* Nonsensical. Just make buffer appear empty */ 179a0ee8cc6SDag-Erling Smørgrav buf->off = buf->size; 180a0ee8cc6SDag-Erling Smørgrav return; 181a0ee8cc6SDag-Erling Smørgrav } 182*38a52bd3SEd Maste if (sshbuf_check_sanity(buf) != 0) 183*38a52bd3SEd Maste return; 184a0ee8cc6SDag-Erling Smørgrav buf->off = buf->size = 0; 185a0ee8cc6SDag-Erling Smørgrav if (buf->alloc != SSHBUF_SIZE_INIT) { 1864f52dfbbSDag-Erling Smørgrav if ((d = recallocarray(buf->d, buf->alloc, SSHBUF_SIZE_INIT, 1874f52dfbbSDag-Erling Smørgrav 1)) != NULL) { 188a0ee8cc6SDag-Erling Smørgrav buf->cd = buf->d = d; 189a0ee8cc6SDag-Erling Smørgrav buf->alloc = SSHBUF_SIZE_INIT; 190a0ee8cc6SDag-Erling Smørgrav } 191a0ee8cc6SDag-Erling Smørgrav } 192*38a52bd3SEd Maste explicit_bzero(buf->d, buf->alloc); 193a0ee8cc6SDag-Erling Smørgrav } 194a0ee8cc6SDag-Erling Smørgrav 195a0ee8cc6SDag-Erling Smørgrav size_t 196a0ee8cc6SDag-Erling Smørgrav sshbuf_max_size(const struct sshbuf *buf) 197a0ee8cc6SDag-Erling Smørgrav { 198a0ee8cc6SDag-Erling Smørgrav return buf->max_size; 199a0ee8cc6SDag-Erling Smørgrav } 200a0ee8cc6SDag-Erling Smørgrav 201a0ee8cc6SDag-Erling Smørgrav size_t 202a0ee8cc6SDag-Erling Smørgrav sshbuf_alloc(const struct sshbuf *buf) 203a0ee8cc6SDag-Erling Smørgrav { 204a0ee8cc6SDag-Erling Smørgrav return buf->alloc; 205a0ee8cc6SDag-Erling Smørgrav } 206a0ee8cc6SDag-Erling Smørgrav 207a0ee8cc6SDag-Erling Smørgrav const struct sshbuf * 208a0ee8cc6SDag-Erling Smørgrav sshbuf_parent(const struct sshbuf *buf) 209a0ee8cc6SDag-Erling Smørgrav { 210a0ee8cc6SDag-Erling Smørgrav return buf->parent; 211a0ee8cc6SDag-Erling Smørgrav } 212a0ee8cc6SDag-Erling Smørgrav 213a0ee8cc6SDag-Erling Smørgrav u_int 214a0ee8cc6SDag-Erling Smørgrav sshbuf_refcount(const struct sshbuf *buf) 215a0ee8cc6SDag-Erling Smørgrav { 216a0ee8cc6SDag-Erling Smørgrav return buf->refcount; 217a0ee8cc6SDag-Erling Smørgrav } 218a0ee8cc6SDag-Erling Smørgrav 219a0ee8cc6SDag-Erling Smørgrav int 220a0ee8cc6SDag-Erling Smørgrav sshbuf_set_max_size(struct sshbuf *buf, size_t max_size) 221a0ee8cc6SDag-Erling Smørgrav { 222a0ee8cc6SDag-Erling Smørgrav size_t rlen; 223a0ee8cc6SDag-Erling Smørgrav u_char *dp; 224a0ee8cc6SDag-Erling Smørgrav int r; 225a0ee8cc6SDag-Erling Smørgrav 226a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("set max buf = %p len = %zu", buf, max_size)); 227a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_sanity(buf)) != 0) 228a0ee8cc6SDag-Erling Smørgrav return r; 229a0ee8cc6SDag-Erling Smørgrav if (max_size == buf->max_size) 230a0ee8cc6SDag-Erling Smørgrav return 0; 231a0ee8cc6SDag-Erling Smørgrav if (buf->readonly || buf->refcount > 1) 232a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_BUFFER_READ_ONLY; 233a0ee8cc6SDag-Erling Smørgrav if (max_size > SSHBUF_SIZE_MAX) 234a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_NO_BUFFER_SPACE; 235a0ee8cc6SDag-Erling Smørgrav /* pack and realloc if necessary */ 236a0ee8cc6SDag-Erling Smørgrav sshbuf_maybe_pack(buf, max_size < buf->size); 237a0ee8cc6SDag-Erling Smørgrav if (max_size < buf->alloc && max_size > buf->size) { 238a0ee8cc6SDag-Erling Smørgrav if (buf->size < SSHBUF_SIZE_INIT) 239a0ee8cc6SDag-Erling Smørgrav rlen = SSHBUF_SIZE_INIT; 240a0ee8cc6SDag-Erling Smørgrav else 241ca86bcf2SDag-Erling Smørgrav rlen = ROUNDUP(buf->size, SSHBUF_SIZE_INC); 242a0ee8cc6SDag-Erling Smørgrav if (rlen > max_size) 243a0ee8cc6SDag-Erling Smørgrav rlen = max_size; 244a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("new alloc = %zu", rlen)); 2454f52dfbbSDag-Erling Smørgrav if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL) 246a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 247a0ee8cc6SDag-Erling Smørgrav buf->cd = buf->d = dp; 248a0ee8cc6SDag-Erling Smørgrav buf->alloc = rlen; 249a0ee8cc6SDag-Erling Smørgrav } 250a0ee8cc6SDag-Erling Smørgrav SSHBUF_TELL("new-max"); 251a0ee8cc6SDag-Erling Smørgrav if (max_size < buf->alloc) 252a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_NO_BUFFER_SPACE; 253a0ee8cc6SDag-Erling Smørgrav buf->max_size = max_size; 254a0ee8cc6SDag-Erling Smørgrav return 0; 255a0ee8cc6SDag-Erling Smørgrav } 256a0ee8cc6SDag-Erling Smørgrav 257a0ee8cc6SDag-Erling Smørgrav size_t 258a0ee8cc6SDag-Erling Smørgrav sshbuf_len(const struct sshbuf *buf) 259a0ee8cc6SDag-Erling Smørgrav { 260a0ee8cc6SDag-Erling Smørgrav if (sshbuf_check_sanity(buf) != 0) 261a0ee8cc6SDag-Erling Smørgrav return 0; 262a0ee8cc6SDag-Erling Smørgrav return buf->size - buf->off; 263a0ee8cc6SDag-Erling Smørgrav } 264a0ee8cc6SDag-Erling Smørgrav 265a0ee8cc6SDag-Erling Smørgrav size_t 266a0ee8cc6SDag-Erling Smørgrav sshbuf_avail(const struct sshbuf *buf) 267a0ee8cc6SDag-Erling Smørgrav { 268a0ee8cc6SDag-Erling Smørgrav if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1) 269a0ee8cc6SDag-Erling Smørgrav return 0; 270a0ee8cc6SDag-Erling Smørgrav return buf->max_size - (buf->size - buf->off); 271a0ee8cc6SDag-Erling Smørgrav } 272a0ee8cc6SDag-Erling Smørgrav 273a0ee8cc6SDag-Erling Smørgrav const u_char * 274a0ee8cc6SDag-Erling Smørgrav sshbuf_ptr(const struct sshbuf *buf) 275a0ee8cc6SDag-Erling Smørgrav { 276a0ee8cc6SDag-Erling Smørgrav if (sshbuf_check_sanity(buf) != 0) 277a0ee8cc6SDag-Erling Smørgrav return NULL; 278a0ee8cc6SDag-Erling Smørgrav return buf->cd + buf->off; 279a0ee8cc6SDag-Erling Smørgrav } 280a0ee8cc6SDag-Erling Smørgrav 281a0ee8cc6SDag-Erling Smørgrav u_char * 282a0ee8cc6SDag-Erling Smørgrav sshbuf_mutable_ptr(const struct sshbuf *buf) 283a0ee8cc6SDag-Erling Smørgrav { 284a0ee8cc6SDag-Erling Smørgrav if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1) 285a0ee8cc6SDag-Erling Smørgrav return NULL; 286a0ee8cc6SDag-Erling Smørgrav return buf->d + buf->off; 287a0ee8cc6SDag-Erling Smørgrav } 288a0ee8cc6SDag-Erling Smørgrav 289a0ee8cc6SDag-Erling Smørgrav int 290a0ee8cc6SDag-Erling Smørgrav sshbuf_check_reserve(const struct sshbuf *buf, size_t len) 291a0ee8cc6SDag-Erling Smørgrav { 292a0ee8cc6SDag-Erling Smørgrav int r; 293a0ee8cc6SDag-Erling Smørgrav 294a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_sanity(buf)) != 0) 295a0ee8cc6SDag-Erling Smørgrav return r; 296a0ee8cc6SDag-Erling Smørgrav if (buf->readonly || buf->refcount > 1) 297a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_BUFFER_READ_ONLY; 298a0ee8cc6SDag-Erling Smørgrav SSHBUF_TELL("check"); 299a0ee8cc6SDag-Erling Smørgrav /* Check that len is reasonable and that max_size + available < len */ 300a0ee8cc6SDag-Erling Smørgrav if (len > buf->max_size || buf->max_size - len < buf->size - buf->off) 301a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_NO_BUFFER_SPACE; 302a0ee8cc6SDag-Erling Smørgrav return 0; 303a0ee8cc6SDag-Erling Smørgrav } 304a0ee8cc6SDag-Erling Smørgrav 305a0ee8cc6SDag-Erling Smørgrav int 306ca86bcf2SDag-Erling Smørgrav sshbuf_allocate(struct sshbuf *buf, size_t len) 307a0ee8cc6SDag-Erling Smørgrav { 308a0ee8cc6SDag-Erling Smørgrav size_t rlen, need; 309a0ee8cc6SDag-Erling Smørgrav u_char *dp; 310a0ee8cc6SDag-Erling Smørgrav int r; 311a0ee8cc6SDag-Erling Smørgrav 312ca86bcf2SDag-Erling Smørgrav SSHBUF_DBG(("allocate buf = %p len = %zu", buf, len)); 313a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_reserve(buf, len)) != 0) 314a0ee8cc6SDag-Erling Smørgrav return r; 315a0ee8cc6SDag-Erling Smørgrav /* 316a0ee8cc6SDag-Erling Smørgrav * If the requested allocation appended would push us past max_size 317a0ee8cc6SDag-Erling Smørgrav * then pack the buffer, zeroing buf->off. 318a0ee8cc6SDag-Erling Smørgrav */ 319a0ee8cc6SDag-Erling Smørgrav sshbuf_maybe_pack(buf, buf->size + len > buf->max_size); 320ca86bcf2SDag-Erling Smørgrav SSHBUF_TELL("allocate"); 321ca86bcf2SDag-Erling Smørgrav if (len + buf->size <= buf->alloc) 322ca86bcf2SDag-Erling Smørgrav return 0; /* already have it. */ 323ca86bcf2SDag-Erling Smørgrav 324a0ee8cc6SDag-Erling Smørgrav /* 325a0ee8cc6SDag-Erling Smørgrav * Prefer to alloc in SSHBUF_SIZE_INC units, but 326a0ee8cc6SDag-Erling Smørgrav * allocate less if doing so would overflow max_size. 327a0ee8cc6SDag-Erling Smørgrav */ 328a0ee8cc6SDag-Erling Smørgrav need = len + buf->size - buf->alloc; 329ca86bcf2SDag-Erling Smørgrav rlen = ROUNDUP(buf->alloc + need, SSHBUF_SIZE_INC); 330a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("need %zu initial rlen %zu", need, rlen)); 331a0ee8cc6SDag-Erling Smørgrav if (rlen > buf->max_size) 332a0ee8cc6SDag-Erling Smørgrav rlen = buf->alloc + need; 333a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("adjusted rlen %zu", rlen)); 3344f52dfbbSDag-Erling Smørgrav if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL) { 335a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("realloc fail")); 336a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 337a0ee8cc6SDag-Erling Smørgrav } 338a0ee8cc6SDag-Erling Smørgrav buf->alloc = rlen; 339a0ee8cc6SDag-Erling Smørgrav buf->cd = buf->d = dp; 340a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_reserve(buf, len)) < 0) { 341a0ee8cc6SDag-Erling Smørgrav /* shouldn't fail */ 342a0ee8cc6SDag-Erling Smørgrav return r; 343a0ee8cc6SDag-Erling Smørgrav } 344ca86bcf2SDag-Erling Smørgrav SSHBUF_TELL("done"); 345ca86bcf2SDag-Erling Smørgrav return 0; 346a0ee8cc6SDag-Erling Smørgrav } 347ca86bcf2SDag-Erling Smørgrav 348ca86bcf2SDag-Erling Smørgrav int 349ca86bcf2SDag-Erling Smørgrav sshbuf_reserve(struct sshbuf *buf, size_t len, u_char **dpp) 350ca86bcf2SDag-Erling Smørgrav { 351ca86bcf2SDag-Erling Smørgrav u_char *dp; 352ca86bcf2SDag-Erling Smørgrav int r; 353ca86bcf2SDag-Erling Smørgrav 354ca86bcf2SDag-Erling Smørgrav if (dpp != NULL) 355ca86bcf2SDag-Erling Smørgrav *dpp = NULL; 356ca86bcf2SDag-Erling Smørgrav 357ca86bcf2SDag-Erling Smørgrav SSHBUF_DBG(("reserve buf = %p len = %zu", buf, len)); 358ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_allocate(buf, len)) != 0) 359ca86bcf2SDag-Erling Smørgrav return r; 360ca86bcf2SDag-Erling Smørgrav 361a0ee8cc6SDag-Erling Smørgrav dp = buf->d + buf->size; 362a0ee8cc6SDag-Erling Smørgrav buf->size += len; 363a0ee8cc6SDag-Erling Smørgrav if (dpp != NULL) 364a0ee8cc6SDag-Erling Smørgrav *dpp = dp; 365a0ee8cc6SDag-Erling Smørgrav return 0; 366a0ee8cc6SDag-Erling Smørgrav } 367a0ee8cc6SDag-Erling Smørgrav 368a0ee8cc6SDag-Erling Smørgrav int 369a0ee8cc6SDag-Erling Smørgrav sshbuf_consume(struct sshbuf *buf, size_t len) 370a0ee8cc6SDag-Erling Smørgrav { 371a0ee8cc6SDag-Erling Smørgrav int r; 372a0ee8cc6SDag-Erling Smørgrav 373a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("len = %zu", len)); 374a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_sanity(buf)) != 0) 375a0ee8cc6SDag-Erling Smørgrav return r; 376a0ee8cc6SDag-Erling Smørgrav if (len == 0) 377a0ee8cc6SDag-Erling Smørgrav return 0; 378a0ee8cc6SDag-Erling Smørgrav if (len > sshbuf_len(buf)) 379a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_MESSAGE_INCOMPLETE; 380a0ee8cc6SDag-Erling Smørgrav buf->off += len; 3814f52dfbbSDag-Erling Smørgrav /* deal with empty buffer */ 3824f52dfbbSDag-Erling Smørgrav if (buf->off == buf->size) 3834f52dfbbSDag-Erling Smørgrav buf->off = buf->size = 0; 384a0ee8cc6SDag-Erling Smørgrav SSHBUF_TELL("done"); 385a0ee8cc6SDag-Erling Smørgrav return 0; 386a0ee8cc6SDag-Erling Smørgrav } 387a0ee8cc6SDag-Erling Smørgrav 388a0ee8cc6SDag-Erling Smørgrav int 389a0ee8cc6SDag-Erling Smørgrav sshbuf_consume_end(struct sshbuf *buf, size_t len) 390a0ee8cc6SDag-Erling Smørgrav { 391a0ee8cc6SDag-Erling Smørgrav int r; 392a0ee8cc6SDag-Erling Smørgrav 393a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("len = %zu", len)); 394a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_sanity(buf)) != 0) 395a0ee8cc6SDag-Erling Smørgrav return r; 396a0ee8cc6SDag-Erling Smørgrav if (len == 0) 397a0ee8cc6SDag-Erling Smørgrav return 0; 398a0ee8cc6SDag-Erling Smørgrav if (len > sshbuf_len(buf)) 399a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_MESSAGE_INCOMPLETE; 400a0ee8cc6SDag-Erling Smørgrav buf->size -= len; 401a0ee8cc6SDag-Erling Smørgrav SSHBUF_TELL("done"); 402a0ee8cc6SDag-Erling Smørgrav return 0; 403a0ee8cc6SDag-Erling Smørgrav } 404a0ee8cc6SDag-Erling Smørgrav 405