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