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