1*ba1276acSMatthew Dillon /* $OpenBSD: sshbuf.c,v 1.19 2022/12/02 04:40:27 djm Exp $ */
236e94dc5SPeter Avalos /*
336e94dc5SPeter Avalos * Copyright (c) 2011 Damien Miller
436e94dc5SPeter Avalos *
536e94dc5SPeter Avalos * Permission to use, copy, modify, and distribute this software for any
636e94dc5SPeter Avalos * purpose with or without fee is hereby granted, provided that the above
736e94dc5SPeter Avalos * copyright notice and this permission notice appear in all copies.
836e94dc5SPeter Avalos *
936e94dc5SPeter Avalos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1036e94dc5SPeter Avalos * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1136e94dc5SPeter Avalos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1236e94dc5SPeter Avalos * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1336e94dc5SPeter Avalos * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1436e94dc5SPeter Avalos * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1536e94dc5SPeter Avalos * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1636e94dc5SPeter Avalos */
1736e94dc5SPeter Avalos
1836e94dc5SPeter Avalos #include "includes.h"
1936e94dc5SPeter Avalos
2036e94dc5SPeter Avalos #include <sys/types.h>
2136e94dc5SPeter Avalos #include <signal.h>
2236e94dc5SPeter Avalos #include <stdlib.h>
2336e94dc5SPeter Avalos #include <stdio.h>
2436e94dc5SPeter Avalos #include <string.h>
2536e94dc5SPeter Avalos
2636e94dc5SPeter Avalos #include "ssherr.h"
27*ba1276acSMatthew Dillon #define SSHBUF_INTERNAL
2836e94dc5SPeter Avalos #include "sshbuf.h"
29ce74bacaSMatthew Dillon #include "misc.h"
3036e94dc5SPeter Avalos
31*ba1276acSMatthew Dillon #ifdef SSHBUF_DEBUG
32*ba1276acSMatthew Dillon # define SSHBUF_TELL(what) do { \
33*ba1276acSMatthew Dillon printf("%s:%d %s: %s size %zu alloc %zu off %zu max %zu\n", \
34*ba1276acSMatthew Dillon __FILE__, __LINE__, __func__, what, \
35*ba1276acSMatthew Dillon buf->size, buf->alloc, buf->off, buf->max_size); \
36*ba1276acSMatthew Dillon fflush(stdout); \
37*ba1276acSMatthew Dillon } while (0)
38*ba1276acSMatthew Dillon #else
39*ba1276acSMatthew Dillon # define SSHBUF_TELL(what)
40*ba1276acSMatthew Dillon #endif
41*ba1276acSMatthew Dillon
42*ba1276acSMatthew Dillon struct sshbuf {
43*ba1276acSMatthew Dillon u_char *d; /* Data */
44*ba1276acSMatthew Dillon const u_char *cd; /* Const data */
45*ba1276acSMatthew Dillon size_t off; /* First available byte is buf->d + buf->off */
46*ba1276acSMatthew Dillon size_t size; /* Last byte is buf->d + buf->size - 1 */
47*ba1276acSMatthew Dillon size_t max_size; /* Maximum size of buffer */
48*ba1276acSMatthew Dillon size_t alloc; /* Total bytes allocated to buf->d */
49*ba1276acSMatthew Dillon int readonly; /* Refers to external, const data */
50*ba1276acSMatthew Dillon u_int refcount; /* Tracks self and number of child buffers */
51*ba1276acSMatthew Dillon struct sshbuf *parent; /* If child, pointer to parent */
52*ba1276acSMatthew Dillon };
53*ba1276acSMatthew Dillon
5436e94dc5SPeter Avalos static inline int
sshbuf_check_sanity(const struct sshbuf * buf)5536e94dc5SPeter Avalos sshbuf_check_sanity(const struct sshbuf *buf)
5636e94dc5SPeter Avalos {
5736e94dc5SPeter Avalos SSHBUF_TELL("sanity");
5836e94dc5SPeter Avalos if (__predict_false(buf == NULL ||
5936e94dc5SPeter Avalos (!buf->readonly && buf->d != buf->cd) ||
6036e94dc5SPeter Avalos buf->refcount < 1 || buf->refcount > SSHBUF_REFS_MAX ||
6136e94dc5SPeter Avalos buf->cd == NULL ||
6236e94dc5SPeter Avalos buf->max_size > SSHBUF_SIZE_MAX ||
6336e94dc5SPeter Avalos buf->alloc > buf->max_size ||
6436e94dc5SPeter Avalos buf->size > buf->alloc ||
6536e94dc5SPeter Avalos buf->off > buf->size)) {
6636e94dc5SPeter Avalos /* Do not try to recover from corrupted buffer internals */
6736e94dc5SPeter Avalos SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR"));
680cbfa66cSDaniel Fojt ssh_signal(SIGSEGV, SIG_DFL);
6936e94dc5SPeter Avalos raise(SIGSEGV);
7036e94dc5SPeter Avalos return SSH_ERR_INTERNAL_ERROR;
7136e94dc5SPeter Avalos }
7236e94dc5SPeter Avalos return 0;
7336e94dc5SPeter Avalos }
7436e94dc5SPeter Avalos
7536e94dc5SPeter Avalos static void
sshbuf_maybe_pack(struct sshbuf * buf,int force)7636e94dc5SPeter Avalos sshbuf_maybe_pack(struct sshbuf *buf, int force)
7736e94dc5SPeter Avalos {
7836e94dc5SPeter Avalos SSHBUF_DBG(("force %d", force));
7936e94dc5SPeter Avalos SSHBUF_TELL("pre-pack");
8036e94dc5SPeter Avalos if (buf->off == 0 || buf->readonly || buf->refcount > 1)
8136e94dc5SPeter Avalos return;
8236e94dc5SPeter Avalos if (force ||
8336e94dc5SPeter Avalos (buf->off >= SSHBUF_PACK_MIN && buf->off >= buf->size / 2)) {
8436e94dc5SPeter Avalos memmove(buf->d, buf->d + buf->off, buf->size - buf->off);
8536e94dc5SPeter Avalos buf->size -= buf->off;
8636e94dc5SPeter Avalos buf->off = 0;
8736e94dc5SPeter Avalos SSHBUF_TELL("packed");
8836e94dc5SPeter Avalos }
8936e94dc5SPeter Avalos }
9036e94dc5SPeter Avalos
9136e94dc5SPeter Avalos struct sshbuf *
sshbuf_new(void)9236e94dc5SPeter Avalos sshbuf_new(void)
9336e94dc5SPeter Avalos {
9436e94dc5SPeter Avalos struct sshbuf *ret;
9536e94dc5SPeter Avalos
9636e94dc5SPeter Avalos if ((ret = calloc(sizeof(*ret), 1)) == NULL)
9736e94dc5SPeter Avalos return NULL;
9836e94dc5SPeter Avalos ret->alloc = SSHBUF_SIZE_INIT;
9936e94dc5SPeter Avalos ret->max_size = SSHBUF_SIZE_MAX;
10036e94dc5SPeter Avalos ret->readonly = 0;
10136e94dc5SPeter Avalos ret->refcount = 1;
10236e94dc5SPeter Avalos ret->parent = NULL;
10336e94dc5SPeter Avalos if ((ret->cd = ret->d = calloc(1, ret->alloc)) == NULL) {
10436e94dc5SPeter Avalos free(ret);
10536e94dc5SPeter Avalos return NULL;
10636e94dc5SPeter Avalos }
10736e94dc5SPeter Avalos return ret;
10836e94dc5SPeter Avalos }
10936e94dc5SPeter Avalos
11036e94dc5SPeter Avalos struct sshbuf *
sshbuf_from(const void * blob,size_t len)11136e94dc5SPeter Avalos sshbuf_from(const void *blob, size_t len)
11236e94dc5SPeter Avalos {
11336e94dc5SPeter Avalos struct sshbuf *ret;
11436e94dc5SPeter Avalos
11536e94dc5SPeter Avalos if (blob == NULL || len > SSHBUF_SIZE_MAX ||
11636e94dc5SPeter Avalos (ret = calloc(sizeof(*ret), 1)) == NULL)
11736e94dc5SPeter Avalos return NULL;
11836e94dc5SPeter Avalos ret->alloc = ret->size = ret->max_size = len;
11936e94dc5SPeter Avalos ret->readonly = 1;
12036e94dc5SPeter Avalos ret->refcount = 1;
12136e94dc5SPeter Avalos ret->parent = NULL;
12236e94dc5SPeter Avalos ret->cd = blob;
12336e94dc5SPeter Avalos ret->d = NULL;
12436e94dc5SPeter Avalos return ret;
12536e94dc5SPeter Avalos }
12636e94dc5SPeter Avalos
12736e94dc5SPeter Avalos int
sshbuf_set_parent(struct sshbuf * child,struct sshbuf * parent)12836e94dc5SPeter Avalos sshbuf_set_parent(struct sshbuf *child, struct sshbuf *parent)
12936e94dc5SPeter Avalos {
13036e94dc5SPeter Avalos int r;
13136e94dc5SPeter Avalos
13236e94dc5SPeter Avalos if ((r = sshbuf_check_sanity(child)) != 0 ||
13336e94dc5SPeter Avalos (r = sshbuf_check_sanity(parent)) != 0)
13436e94dc5SPeter Avalos return r;
135ee116499SAntonio Huete Jimenez if (child->parent != NULL && child->parent != parent)
136ee116499SAntonio Huete Jimenez return SSH_ERR_INTERNAL_ERROR;
13736e94dc5SPeter Avalos child->parent = parent;
13836e94dc5SPeter Avalos child->parent->refcount++;
13936e94dc5SPeter Avalos return 0;
14036e94dc5SPeter Avalos }
14136e94dc5SPeter Avalos
14236e94dc5SPeter Avalos struct sshbuf *
sshbuf_fromb(struct sshbuf * buf)14336e94dc5SPeter Avalos sshbuf_fromb(struct sshbuf *buf)
14436e94dc5SPeter Avalos {
14536e94dc5SPeter Avalos struct sshbuf *ret;
14636e94dc5SPeter Avalos
14736e94dc5SPeter Avalos if (sshbuf_check_sanity(buf) != 0)
14836e94dc5SPeter Avalos return NULL;
14936e94dc5SPeter Avalos if ((ret = sshbuf_from(sshbuf_ptr(buf), sshbuf_len(buf))) == NULL)
15036e94dc5SPeter Avalos return NULL;
15136e94dc5SPeter Avalos if (sshbuf_set_parent(ret, buf) != 0) {
15236e94dc5SPeter Avalos sshbuf_free(ret);
15336e94dc5SPeter Avalos return NULL;
15436e94dc5SPeter Avalos }
15536e94dc5SPeter Avalos return ret;
15636e94dc5SPeter Avalos }
15736e94dc5SPeter Avalos
15836e94dc5SPeter Avalos void
sshbuf_free(struct sshbuf * buf)15936e94dc5SPeter Avalos sshbuf_free(struct sshbuf *buf)
16036e94dc5SPeter Avalos {
16136e94dc5SPeter Avalos if (buf == NULL)
16236e94dc5SPeter Avalos return;
16336e94dc5SPeter Avalos /*
16436e94dc5SPeter Avalos * The following will leak on insane buffers, but this is the safest
16536e94dc5SPeter Avalos * course of action - an invalid pointer or already-freed pointer may
16636e94dc5SPeter Avalos * have been passed to us and continuing to scribble over memory would
16736e94dc5SPeter Avalos * be bad.
16836e94dc5SPeter Avalos */
16936e94dc5SPeter Avalos if (sshbuf_check_sanity(buf) != 0)
17036e94dc5SPeter Avalos return;
171664f4763Szrj
17236e94dc5SPeter Avalos /*
17336e94dc5SPeter Avalos * If we are a parent with still-extant children, then don't free just
17436e94dc5SPeter Avalos * yet. The last child's call to sshbuf_free should decrement our
17536e94dc5SPeter Avalos * refcount to 0 and trigger the actual free.
17636e94dc5SPeter Avalos */
17736e94dc5SPeter Avalos buf->refcount--;
17836e94dc5SPeter Avalos if (buf->refcount > 0)
17936e94dc5SPeter Avalos return;
180664f4763Szrj
181664f4763Szrj /*
182664f4763Szrj * If we are a child, the free our parent to decrement its reference
183664f4763Szrj * count and possibly free it.
184664f4763Szrj */
185664f4763Szrj sshbuf_free(buf->parent);
186664f4763Szrj buf->parent = NULL;
187664f4763Szrj
18836e94dc5SPeter Avalos if (!buf->readonly) {
189e9778795SPeter Avalos explicit_bzero(buf->d, buf->alloc);
19036e94dc5SPeter Avalos free(buf->d);
19136e94dc5SPeter Avalos }
1920cbfa66cSDaniel Fojt freezero(buf, sizeof(*buf));
19336e94dc5SPeter Avalos }
19436e94dc5SPeter Avalos
19536e94dc5SPeter Avalos void
sshbuf_reset(struct sshbuf * buf)19636e94dc5SPeter Avalos sshbuf_reset(struct sshbuf *buf)
19736e94dc5SPeter Avalos {
19836e94dc5SPeter Avalos u_char *d;
19936e94dc5SPeter Avalos
20036e94dc5SPeter Avalos if (buf->readonly || buf->refcount > 1) {
20136e94dc5SPeter Avalos /* Nonsensical. Just make buffer appear empty */
20236e94dc5SPeter Avalos buf->off = buf->size;
20336e94dc5SPeter Avalos return;
20436e94dc5SPeter Avalos }
205ee116499SAntonio Huete Jimenez if (sshbuf_check_sanity(buf) != 0)
206ee116499SAntonio Huete Jimenez return;
20736e94dc5SPeter Avalos buf->off = buf->size = 0;
20836e94dc5SPeter Avalos if (buf->alloc != SSHBUF_SIZE_INIT) {
209ce74bacaSMatthew Dillon if ((d = recallocarray(buf->d, buf->alloc, SSHBUF_SIZE_INIT,
210ce74bacaSMatthew Dillon 1)) != NULL) {
21136e94dc5SPeter Avalos buf->cd = buf->d = d;
21236e94dc5SPeter Avalos buf->alloc = SSHBUF_SIZE_INIT;
21336e94dc5SPeter Avalos }
21436e94dc5SPeter Avalos }
215ee116499SAntonio Huete Jimenez explicit_bzero(buf->d, buf->alloc);
21636e94dc5SPeter Avalos }
21736e94dc5SPeter Avalos
21836e94dc5SPeter Avalos size_t
sshbuf_max_size(const struct sshbuf * buf)21936e94dc5SPeter Avalos sshbuf_max_size(const struct sshbuf *buf)
22036e94dc5SPeter Avalos {
22136e94dc5SPeter Avalos return buf->max_size;
22236e94dc5SPeter Avalos }
22336e94dc5SPeter Avalos
22436e94dc5SPeter Avalos size_t
sshbuf_alloc(const struct sshbuf * buf)22536e94dc5SPeter Avalos sshbuf_alloc(const struct sshbuf *buf)
22636e94dc5SPeter Avalos {
22736e94dc5SPeter Avalos return buf->alloc;
22836e94dc5SPeter Avalos }
22936e94dc5SPeter Avalos
23036e94dc5SPeter Avalos const struct sshbuf *
sshbuf_parent(const struct sshbuf * buf)23136e94dc5SPeter Avalos sshbuf_parent(const struct sshbuf *buf)
23236e94dc5SPeter Avalos {
23336e94dc5SPeter Avalos return buf->parent;
23436e94dc5SPeter Avalos }
23536e94dc5SPeter Avalos
23636e94dc5SPeter Avalos u_int
sshbuf_refcount(const struct sshbuf * buf)23736e94dc5SPeter Avalos sshbuf_refcount(const struct sshbuf *buf)
23836e94dc5SPeter Avalos {
23936e94dc5SPeter Avalos return buf->refcount;
24036e94dc5SPeter Avalos }
24136e94dc5SPeter Avalos
24236e94dc5SPeter Avalos int
sshbuf_set_max_size(struct sshbuf * buf,size_t max_size)24336e94dc5SPeter Avalos sshbuf_set_max_size(struct sshbuf *buf, size_t max_size)
24436e94dc5SPeter Avalos {
24536e94dc5SPeter Avalos size_t rlen;
24636e94dc5SPeter Avalos u_char *dp;
24736e94dc5SPeter Avalos int r;
24836e94dc5SPeter Avalos
24936e94dc5SPeter Avalos SSHBUF_DBG(("set max buf = %p len = %zu", buf, max_size));
25036e94dc5SPeter Avalos if ((r = sshbuf_check_sanity(buf)) != 0)
25136e94dc5SPeter Avalos return r;
25236e94dc5SPeter Avalos if (max_size == buf->max_size)
25336e94dc5SPeter Avalos return 0;
25436e94dc5SPeter Avalos if (buf->readonly || buf->refcount > 1)
25536e94dc5SPeter Avalos return SSH_ERR_BUFFER_READ_ONLY;
25636e94dc5SPeter Avalos if (max_size > SSHBUF_SIZE_MAX)
25736e94dc5SPeter Avalos return SSH_ERR_NO_BUFFER_SPACE;
25836e94dc5SPeter Avalos /* pack and realloc if necessary */
25936e94dc5SPeter Avalos sshbuf_maybe_pack(buf, max_size < buf->size);
26036e94dc5SPeter Avalos if (max_size < buf->alloc && max_size > buf->size) {
26136e94dc5SPeter Avalos if (buf->size < SSHBUF_SIZE_INIT)
26236e94dc5SPeter Avalos rlen = SSHBUF_SIZE_INIT;
26336e94dc5SPeter Avalos else
264ce74bacaSMatthew Dillon rlen = ROUNDUP(buf->size, SSHBUF_SIZE_INC);
26536e94dc5SPeter Avalos if (rlen > max_size)
26636e94dc5SPeter Avalos rlen = max_size;
26736e94dc5SPeter Avalos SSHBUF_DBG(("new alloc = %zu", rlen));
268ce74bacaSMatthew Dillon if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL)
26936e94dc5SPeter Avalos return SSH_ERR_ALLOC_FAIL;
27036e94dc5SPeter Avalos buf->cd = buf->d = dp;
27136e94dc5SPeter Avalos buf->alloc = rlen;
27236e94dc5SPeter Avalos }
27336e94dc5SPeter Avalos SSHBUF_TELL("new-max");
27436e94dc5SPeter Avalos if (max_size < buf->alloc)
27536e94dc5SPeter Avalos return SSH_ERR_NO_BUFFER_SPACE;
27636e94dc5SPeter Avalos buf->max_size = max_size;
27736e94dc5SPeter Avalos return 0;
27836e94dc5SPeter Avalos }
27936e94dc5SPeter Avalos
28036e94dc5SPeter Avalos size_t
sshbuf_len(const struct sshbuf * buf)28136e94dc5SPeter Avalos sshbuf_len(const struct sshbuf *buf)
28236e94dc5SPeter Avalos {
28336e94dc5SPeter Avalos if (sshbuf_check_sanity(buf) != 0)
28436e94dc5SPeter Avalos return 0;
28536e94dc5SPeter Avalos return buf->size - buf->off;
28636e94dc5SPeter Avalos }
28736e94dc5SPeter Avalos
28836e94dc5SPeter Avalos size_t
sshbuf_avail(const struct sshbuf * buf)28936e94dc5SPeter Avalos sshbuf_avail(const struct sshbuf *buf)
29036e94dc5SPeter Avalos {
29136e94dc5SPeter Avalos if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1)
29236e94dc5SPeter Avalos return 0;
29336e94dc5SPeter Avalos return buf->max_size - (buf->size - buf->off);
29436e94dc5SPeter Avalos }
29536e94dc5SPeter Avalos
29636e94dc5SPeter Avalos const u_char *
sshbuf_ptr(const struct sshbuf * buf)29736e94dc5SPeter Avalos sshbuf_ptr(const struct sshbuf *buf)
29836e94dc5SPeter Avalos {
29936e94dc5SPeter Avalos if (sshbuf_check_sanity(buf) != 0)
30036e94dc5SPeter Avalos return NULL;
30136e94dc5SPeter Avalos return buf->cd + buf->off;
30236e94dc5SPeter Avalos }
30336e94dc5SPeter Avalos
30436e94dc5SPeter Avalos u_char *
sshbuf_mutable_ptr(const struct sshbuf * buf)30536e94dc5SPeter Avalos sshbuf_mutable_ptr(const struct sshbuf *buf)
30636e94dc5SPeter Avalos {
30736e94dc5SPeter Avalos if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1)
30836e94dc5SPeter Avalos return NULL;
30936e94dc5SPeter Avalos return buf->d + buf->off;
31036e94dc5SPeter Avalos }
31136e94dc5SPeter Avalos
31236e94dc5SPeter Avalos int
sshbuf_check_reserve(const struct sshbuf * buf,size_t len)31336e94dc5SPeter Avalos sshbuf_check_reserve(const struct sshbuf *buf, size_t len)
31436e94dc5SPeter Avalos {
31536e94dc5SPeter Avalos int r;
31636e94dc5SPeter Avalos
31736e94dc5SPeter Avalos if ((r = sshbuf_check_sanity(buf)) != 0)
31836e94dc5SPeter Avalos return r;
31936e94dc5SPeter Avalos if (buf->readonly || buf->refcount > 1)
32036e94dc5SPeter Avalos return SSH_ERR_BUFFER_READ_ONLY;
32136e94dc5SPeter Avalos SSHBUF_TELL("check");
32236e94dc5SPeter Avalos /* Check that len is reasonable and that max_size + available < len */
32336e94dc5SPeter Avalos if (len > buf->max_size || buf->max_size - len < buf->size - buf->off)
32436e94dc5SPeter Avalos return SSH_ERR_NO_BUFFER_SPACE;
32536e94dc5SPeter Avalos return 0;
32636e94dc5SPeter Avalos }
32736e94dc5SPeter Avalos
32836e94dc5SPeter Avalos int
sshbuf_allocate(struct sshbuf * buf,size_t len)329ce74bacaSMatthew Dillon sshbuf_allocate(struct sshbuf *buf, size_t len)
33036e94dc5SPeter Avalos {
33136e94dc5SPeter Avalos size_t rlen, need;
33236e94dc5SPeter Avalos u_char *dp;
33336e94dc5SPeter Avalos int r;
33436e94dc5SPeter Avalos
335ce74bacaSMatthew Dillon SSHBUF_DBG(("allocate buf = %p len = %zu", buf, len));
33636e94dc5SPeter Avalos if ((r = sshbuf_check_reserve(buf, len)) != 0)
33736e94dc5SPeter Avalos return r;
33836e94dc5SPeter Avalos /*
33936e94dc5SPeter Avalos * If the requested allocation appended would push us past max_size
34036e94dc5SPeter Avalos * then pack the buffer, zeroing buf->off.
34136e94dc5SPeter Avalos */
34236e94dc5SPeter Avalos sshbuf_maybe_pack(buf, buf->size + len > buf->max_size);
343ce74bacaSMatthew Dillon SSHBUF_TELL("allocate");
344ce74bacaSMatthew Dillon if (len + buf->size <= buf->alloc)
345ce74bacaSMatthew Dillon return 0; /* already have it. */
346ce74bacaSMatthew Dillon
34736e94dc5SPeter Avalos /*
34836e94dc5SPeter Avalos * Prefer to alloc in SSHBUF_SIZE_INC units, but
34936e94dc5SPeter Avalos * allocate less if doing so would overflow max_size.
35036e94dc5SPeter Avalos */
35136e94dc5SPeter Avalos need = len + buf->size - buf->alloc;
352ce74bacaSMatthew Dillon rlen = ROUNDUP(buf->alloc + need, SSHBUF_SIZE_INC);
35336e94dc5SPeter Avalos SSHBUF_DBG(("need %zu initial rlen %zu", need, rlen));
35436e94dc5SPeter Avalos if (rlen > buf->max_size)
35536e94dc5SPeter Avalos rlen = buf->alloc + need;
35636e94dc5SPeter Avalos SSHBUF_DBG(("adjusted rlen %zu", rlen));
357ce74bacaSMatthew Dillon if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL) {
35836e94dc5SPeter Avalos SSHBUF_DBG(("realloc fail"));
35936e94dc5SPeter Avalos return SSH_ERR_ALLOC_FAIL;
36036e94dc5SPeter Avalos }
36136e94dc5SPeter Avalos buf->alloc = rlen;
36236e94dc5SPeter Avalos buf->cd = buf->d = dp;
36336e94dc5SPeter Avalos if ((r = sshbuf_check_reserve(buf, len)) < 0) {
36436e94dc5SPeter Avalos /* shouldn't fail */
36536e94dc5SPeter Avalos return r;
36636e94dc5SPeter Avalos }
367ce74bacaSMatthew Dillon SSHBUF_TELL("done");
368ce74bacaSMatthew Dillon return 0;
36936e94dc5SPeter Avalos }
370ce74bacaSMatthew Dillon
371ce74bacaSMatthew Dillon int
sshbuf_reserve(struct sshbuf * buf,size_t len,u_char ** dpp)372ce74bacaSMatthew Dillon sshbuf_reserve(struct sshbuf *buf, size_t len, u_char **dpp)
373ce74bacaSMatthew Dillon {
374ce74bacaSMatthew Dillon u_char *dp;
375ce74bacaSMatthew Dillon int r;
376ce74bacaSMatthew Dillon
377ce74bacaSMatthew Dillon if (dpp != NULL)
378ce74bacaSMatthew Dillon *dpp = NULL;
379ce74bacaSMatthew Dillon
380ce74bacaSMatthew Dillon SSHBUF_DBG(("reserve buf = %p len = %zu", buf, len));
381ce74bacaSMatthew Dillon if ((r = sshbuf_allocate(buf, len)) != 0)
382ce74bacaSMatthew Dillon return r;
383ce74bacaSMatthew Dillon
38436e94dc5SPeter Avalos dp = buf->d + buf->size;
38536e94dc5SPeter Avalos buf->size += len;
38636e94dc5SPeter Avalos if (dpp != NULL)
38736e94dc5SPeter Avalos *dpp = dp;
38836e94dc5SPeter Avalos return 0;
38936e94dc5SPeter Avalos }
39036e94dc5SPeter Avalos
39136e94dc5SPeter Avalos int
sshbuf_consume(struct sshbuf * buf,size_t len)39236e94dc5SPeter Avalos sshbuf_consume(struct sshbuf *buf, size_t len)
39336e94dc5SPeter Avalos {
39436e94dc5SPeter Avalos int r;
39536e94dc5SPeter Avalos
39636e94dc5SPeter Avalos SSHBUF_DBG(("len = %zu", len));
39736e94dc5SPeter Avalos if ((r = sshbuf_check_sanity(buf)) != 0)
39836e94dc5SPeter Avalos return r;
39936e94dc5SPeter Avalos if (len == 0)
40036e94dc5SPeter Avalos return 0;
40136e94dc5SPeter Avalos if (len > sshbuf_len(buf))
40236e94dc5SPeter Avalos return SSH_ERR_MESSAGE_INCOMPLETE;
40336e94dc5SPeter Avalos buf->off += len;
404ce74bacaSMatthew Dillon /* deal with empty buffer */
405ce74bacaSMatthew Dillon if (buf->off == buf->size)
406ce74bacaSMatthew Dillon buf->off = buf->size = 0;
40736e94dc5SPeter Avalos SSHBUF_TELL("done");
40836e94dc5SPeter Avalos return 0;
40936e94dc5SPeter Avalos }
41036e94dc5SPeter Avalos
41136e94dc5SPeter Avalos int
sshbuf_consume_end(struct sshbuf * buf,size_t len)41236e94dc5SPeter Avalos sshbuf_consume_end(struct sshbuf *buf, size_t len)
41336e94dc5SPeter Avalos {
41436e94dc5SPeter Avalos int r;
41536e94dc5SPeter Avalos
41636e94dc5SPeter Avalos SSHBUF_DBG(("len = %zu", len));
41736e94dc5SPeter Avalos if ((r = sshbuf_check_sanity(buf)) != 0)
41836e94dc5SPeter Avalos return r;
41936e94dc5SPeter Avalos if (len == 0)
42036e94dc5SPeter Avalos return 0;
42136e94dc5SPeter Avalos if (len > sshbuf_len(buf))
42236e94dc5SPeter Avalos return SSH_ERR_MESSAGE_INCOMPLETE;
42336e94dc5SPeter Avalos buf->size -= len;
42436e94dc5SPeter Avalos SSHBUF_TELL("done");
42536e94dc5SPeter Avalos return 0;
42636e94dc5SPeter Avalos }
42736e94dc5SPeter Avalos
428