xref: /openssh-portable/sshbuf.c (revision d5ba1c03)
1*d5ba1c03Sjsg@openbsd.org /*	$OpenBSD: sshbuf.c,v 1.15 2020/02/26 13:40:09 jsg Exp $	*/
205e82c3bSDamien Miller /*
305e82c3bSDamien Miller  * Copyright (c) 2011 Damien Miller
405e82c3bSDamien Miller  *
505e82c3bSDamien Miller  * Permission to use, copy, modify, and distribute this software for any
605e82c3bSDamien Miller  * purpose with or without fee is hereby granted, provided that the above
705e82c3bSDamien Miller  * copyright notice and this permission notice appear in all copies.
805e82c3bSDamien Miller  *
905e82c3bSDamien Miller  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1005e82c3bSDamien Miller  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1105e82c3bSDamien Miller  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1205e82c3bSDamien Miller  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1305e82c3bSDamien Miller  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1405e82c3bSDamien Miller  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1505e82c3bSDamien Miller  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1605e82c3bSDamien Miller  */
1705e82c3bSDamien Miller 
18e5b9f0f2SDamien Miller #define SSHBUF_INTERNAL
1905e82c3bSDamien Miller #include "includes.h"
2005e82c3bSDamien Miller 
2105e82c3bSDamien Miller #include <sys/types.h>
2205e82c3bSDamien Miller #include <signal.h>
2305e82c3bSDamien Miller #include <stdlib.h>
2405e82c3bSDamien Miller #include <stdio.h>
2505e82c3bSDamien Miller #include <string.h>
2605e82c3bSDamien Miller 
2705e82c3bSDamien Miller #include "ssherr.h"
2805e82c3bSDamien Miller #include "sshbuf.h"
299136ec13Sderaadt@openbsd.org #include "misc.h"
3005e82c3bSDamien Miller 
3105e82c3bSDamien Miller static inline int
sshbuf_check_sanity(const struct sshbuf * buf)3205e82c3bSDamien Miller sshbuf_check_sanity(const struct sshbuf *buf)
3305e82c3bSDamien Miller {
3405e82c3bSDamien Miller 	SSHBUF_TELL("sanity");
3505e82c3bSDamien Miller 	if (__predict_false(buf == NULL ||
3605e82c3bSDamien Miller 	    (!buf->readonly && buf->d != buf->cd) ||
3705e82c3bSDamien Miller 	    buf->refcount < 1 || buf->refcount > SSHBUF_REFS_MAX ||
3805e82c3bSDamien Miller 	    buf->cd == NULL ||
3905e82c3bSDamien Miller 	    buf->max_size > SSHBUF_SIZE_MAX ||
4005e82c3bSDamien Miller 	    buf->alloc > buf->max_size ||
4105e82c3bSDamien Miller 	    buf->size > buf->alloc ||
4205e82c3bSDamien Miller 	    buf->off > buf->size)) {
4305e82c3bSDamien Miller 		/* Do not try to recover from corrupted buffer internals */
4405e82c3bSDamien Miller 		SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR"));
453bf2a6acSdtucker@openbsd.org 		ssh_signal(SIGSEGV, SIG_DFL);
4605e82c3bSDamien Miller 		raise(SIGSEGV);
4705e82c3bSDamien Miller 		return SSH_ERR_INTERNAL_ERROR;
4805e82c3bSDamien Miller 	}
4905e82c3bSDamien Miller 	return 0;
5005e82c3bSDamien Miller }
5105e82c3bSDamien Miller 
5205e82c3bSDamien Miller static void
sshbuf_maybe_pack(struct sshbuf * buf,int force)5305e82c3bSDamien Miller sshbuf_maybe_pack(struct sshbuf *buf, int force)
5405e82c3bSDamien Miller {
5505e82c3bSDamien Miller 	SSHBUF_DBG(("force %d", force));
5605e82c3bSDamien Miller 	SSHBUF_TELL("pre-pack");
5705e82c3bSDamien Miller 	if (buf->off == 0 || buf->readonly || buf->refcount > 1)
5805e82c3bSDamien Miller 		return;
5905e82c3bSDamien Miller 	if (force ||
6005e82c3bSDamien Miller 	    (buf->off >= SSHBUF_PACK_MIN && buf->off >= buf->size / 2)) {
6105e82c3bSDamien Miller 		memmove(buf->d, buf->d + buf->off, buf->size - buf->off);
6205e82c3bSDamien Miller 		buf->size -= buf->off;
6305e82c3bSDamien Miller 		buf->off = 0;
6405e82c3bSDamien Miller 		SSHBUF_TELL("packed");
6505e82c3bSDamien Miller 	}
6605e82c3bSDamien Miller }
6705e82c3bSDamien Miller 
6805e82c3bSDamien Miller struct sshbuf *
sshbuf_new(void)6905e82c3bSDamien Miller sshbuf_new(void)
7005e82c3bSDamien Miller {
7105e82c3bSDamien Miller 	struct sshbuf *ret;
7205e82c3bSDamien Miller 
7305e82c3bSDamien Miller 	if ((ret = calloc(sizeof(*ret), 1)) == NULL)
7405e82c3bSDamien Miller 		return NULL;
7505e82c3bSDamien Miller 	ret->alloc = SSHBUF_SIZE_INIT;
7605e82c3bSDamien Miller 	ret->max_size = SSHBUF_SIZE_MAX;
7705e82c3bSDamien Miller 	ret->readonly = 0;
7805e82c3bSDamien Miller 	ret->refcount = 1;
7905e82c3bSDamien Miller 	ret->parent = NULL;
8005e82c3bSDamien Miller 	if ((ret->cd = ret->d = calloc(1, ret->alloc)) == NULL) {
8105e82c3bSDamien Miller 		free(ret);
8205e82c3bSDamien Miller 		return NULL;
8305e82c3bSDamien Miller 	}
8405e82c3bSDamien Miller 	return ret;
8505e82c3bSDamien Miller }
8605e82c3bSDamien Miller 
8705e82c3bSDamien Miller struct sshbuf *
sshbuf_from(const void * blob,size_t len)8805e82c3bSDamien Miller sshbuf_from(const void *blob, size_t len)
8905e82c3bSDamien Miller {
9005e82c3bSDamien Miller 	struct sshbuf *ret;
9105e82c3bSDamien Miller 
9205e82c3bSDamien Miller 	if (blob == NULL || len > SSHBUF_SIZE_MAX ||
9305e82c3bSDamien Miller 	    (ret = calloc(sizeof(*ret), 1)) == NULL)
9405e82c3bSDamien Miller 		return NULL;
9505e82c3bSDamien Miller 	ret->alloc = ret->size = ret->max_size = len;
9605e82c3bSDamien Miller 	ret->readonly = 1;
9705e82c3bSDamien Miller 	ret->refcount = 1;
9805e82c3bSDamien Miller 	ret->parent = NULL;
9905e82c3bSDamien Miller 	ret->cd = blob;
10005e82c3bSDamien Miller 	ret->d = NULL;
10105e82c3bSDamien Miller 	return ret;
10205e82c3bSDamien Miller }
10305e82c3bSDamien Miller 
10405e82c3bSDamien Miller int
sshbuf_set_parent(struct sshbuf * child,struct sshbuf * parent)10505e82c3bSDamien Miller sshbuf_set_parent(struct sshbuf *child, struct sshbuf *parent)
10605e82c3bSDamien Miller {
10705e82c3bSDamien Miller 	int r;
10805e82c3bSDamien Miller 
10905e82c3bSDamien Miller 	if ((r = sshbuf_check_sanity(child)) != 0 ||
11005e82c3bSDamien Miller 	    (r = sshbuf_check_sanity(parent)) != 0)
11105e82c3bSDamien Miller 		return r;
11205e82c3bSDamien Miller 	child->parent = parent;
11305e82c3bSDamien Miller 	child->parent->refcount++;
11405e82c3bSDamien Miller 	return 0;
11505e82c3bSDamien Miller }
11605e82c3bSDamien Miller 
11705e82c3bSDamien Miller struct sshbuf *
sshbuf_fromb(struct sshbuf * buf)11805e82c3bSDamien Miller sshbuf_fromb(struct sshbuf *buf)
11905e82c3bSDamien Miller {
12005e82c3bSDamien Miller 	struct sshbuf *ret;
12105e82c3bSDamien Miller 
12205e82c3bSDamien Miller 	if (sshbuf_check_sanity(buf) != 0)
12305e82c3bSDamien Miller 		return NULL;
12405e82c3bSDamien Miller 	if ((ret = sshbuf_from(sshbuf_ptr(buf), sshbuf_len(buf))) == NULL)
12505e82c3bSDamien Miller 		return NULL;
12605e82c3bSDamien Miller 	if (sshbuf_set_parent(ret, buf) != 0) {
12705e82c3bSDamien Miller 		sshbuf_free(ret);
12805e82c3bSDamien Miller 		return NULL;
12905e82c3bSDamien Miller 	}
13005e82c3bSDamien Miller 	return ret;
13105e82c3bSDamien Miller }
13205e82c3bSDamien Miller 
13305e82c3bSDamien Miller void
sshbuf_free(struct sshbuf * buf)13405e82c3bSDamien Miller sshbuf_free(struct sshbuf *buf)
13505e82c3bSDamien Miller {
13605e82c3bSDamien Miller 	if (buf == NULL)
13705e82c3bSDamien Miller 		return;
13805e82c3bSDamien Miller 	/*
13905e82c3bSDamien Miller 	 * The following will leak on insane buffers, but this is the safest
14005e82c3bSDamien Miller 	 * course of action - an invalid pointer or already-freed pointer may
14105e82c3bSDamien Miller 	 * have been passed to us and continuing to scribble over memory would
14205e82c3bSDamien Miller 	 * be bad.
14305e82c3bSDamien Miller 	 */
14405e82c3bSDamien Miller 	if (sshbuf_check_sanity(buf) != 0)
14505e82c3bSDamien Miller 		return;
14615182fd9Sdjm@openbsd.org 
14705e82c3bSDamien Miller 	/*
14805e82c3bSDamien Miller 	 * If we are a parent with still-extant children, then don't free just
14905e82c3bSDamien Miller 	 * yet. The last child's call to sshbuf_free should decrement our
15005e82c3bSDamien Miller 	 * refcount to 0 and trigger the actual free.
15105e82c3bSDamien Miller 	 */
15205e82c3bSDamien Miller 	buf->refcount--;
15305e82c3bSDamien Miller 	if (buf->refcount > 0)
15405e82c3bSDamien Miller 		return;
15515182fd9Sdjm@openbsd.org 
15615182fd9Sdjm@openbsd.org 	/*
15715182fd9Sdjm@openbsd.org 	 * If we are a child, the free our parent to decrement its reference
15815182fd9Sdjm@openbsd.org 	 * count and possibly free it.
15915182fd9Sdjm@openbsd.org 	 */
16015182fd9Sdjm@openbsd.org 	sshbuf_free(buf->parent);
16115182fd9Sdjm@openbsd.org 	buf->parent = NULL;
16215182fd9Sdjm@openbsd.org 
16305e82c3bSDamien Miller 	if (!buf->readonly) {
164905b054eSdjm@openbsd.org 		explicit_bzero(buf->d, buf->alloc);
16505e82c3bSDamien Miller 		free(buf->d);
16605e82c3bSDamien Miller 	}
167*d5ba1c03Sjsg@openbsd.org 	freezero(buf, sizeof(*buf));
16805e82c3bSDamien Miller }
16905e82c3bSDamien Miller 
17005e82c3bSDamien Miller void
sshbuf_reset(struct sshbuf * buf)17105e82c3bSDamien Miller sshbuf_reset(struct sshbuf *buf)
17205e82c3bSDamien Miller {
17305e82c3bSDamien Miller 	u_char *d;
17405e82c3bSDamien Miller 
17505e82c3bSDamien Miller 	if (buf->readonly || buf->refcount > 1) {
17605e82c3bSDamien Miller 		/* Nonsensical. Just make buffer appear empty */
17705e82c3bSDamien Miller 		buf->off = buf->size;
17805e82c3bSDamien Miller 		return;
17905e82c3bSDamien Miller 	}
1809e509d4eSderaadt@openbsd.org 	(void) sshbuf_check_sanity(buf);
18105e82c3bSDamien Miller 	buf->off = buf->size = 0;
18205e82c3bSDamien Miller 	if (buf->alloc != SSHBUF_SIZE_INIT) {
1839e509d4eSderaadt@openbsd.org 		if ((d = recallocarray(buf->d, buf->alloc, SSHBUF_SIZE_INIT,
1849e509d4eSderaadt@openbsd.org 		    1)) != NULL) {
18505e82c3bSDamien Miller 			buf->cd = buf->d = d;
18605e82c3bSDamien Miller 			buf->alloc = SSHBUF_SIZE_INIT;
18705e82c3bSDamien Miller 		}
188cc812bafSdjm@openbsd.org 	}
1899e509d4eSderaadt@openbsd.org 	explicit_bzero(buf->d, SSHBUF_SIZE_INIT);
19005e82c3bSDamien Miller }
19105e82c3bSDamien Miller 
19205e82c3bSDamien Miller size_t
sshbuf_max_size(const struct sshbuf * buf)19305e82c3bSDamien Miller sshbuf_max_size(const struct sshbuf *buf)
19405e82c3bSDamien Miller {
19505e82c3bSDamien Miller 	return buf->max_size;
19605e82c3bSDamien Miller }
19705e82c3bSDamien Miller 
19805e82c3bSDamien Miller size_t
sshbuf_alloc(const struct sshbuf * buf)19905e82c3bSDamien Miller sshbuf_alloc(const struct sshbuf *buf)
20005e82c3bSDamien Miller {
20105e82c3bSDamien Miller 	return buf->alloc;
20205e82c3bSDamien Miller }
20305e82c3bSDamien Miller 
20405e82c3bSDamien Miller const struct sshbuf *
sshbuf_parent(const struct sshbuf * buf)20505e82c3bSDamien Miller sshbuf_parent(const struct sshbuf *buf)
20605e82c3bSDamien Miller {
20705e82c3bSDamien Miller 	return buf->parent;
20805e82c3bSDamien Miller }
20905e82c3bSDamien Miller 
21005e82c3bSDamien Miller u_int
sshbuf_refcount(const struct sshbuf * buf)21105e82c3bSDamien Miller sshbuf_refcount(const struct sshbuf *buf)
21205e82c3bSDamien Miller {
21305e82c3bSDamien Miller 	return buf->refcount;
21405e82c3bSDamien Miller }
21505e82c3bSDamien Miller 
21605e82c3bSDamien Miller int
sshbuf_set_max_size(struct sshbuf * buf,size_t max_size)21705e82c3bSDamien Miller sshbuf_set_max_size(struct sshbuf *buf, size_t max_size)
21805e82c3bSDamien Miller {
21905e82c3bSDamien Miller 	size_t rlen;
22005e82c3bSDamien Miller 	u_char *dp;
22105e82c3bSDamien Miller 	int r;
22205e82c3bSDamien Miller 
22305e82c3bSDamien Miller 	SSHBUF_DBG(("set max buf = %p len = %zu", buf, max_size));
22405e82c3bSDamien Miller 	if ((r = sshbuf_check_sanity(buf)) != 0)
22505e82c3bSDamien Miller 		return r;
22605e82c3bSDamien Miller 	if (max_size == buf->max_size)
22705e82c3bSDamien Miller 		return 0;
22805e82c3bSDamien Miller 	if (buf->readonly || buf->refcount > 1)
22905e82c3bSDamien Miller 		return SSH_ERR_BUFFER_READ_ONLY;
23005e82c3bSDamien Miller 	if (max_size > SSHBUF_SIZE_MAX)
23105e82c3bSDamien Miller 		return SSH_ERR_NO_BUFFER_SPACE;
23205e82c3bSDamien Miller 	/* pack and realloc if necessary */
23305e82c3bSDamien Miller 	sshbuf_maybe_pack(buf, max_size < buf->size);
23405e82c3bSDamien Miller 	if (max_size < buf->alloc && max_size > buf->size) {
23505e82c3bSDamien Miller 		if (buf->size < SSHBUF_SIZE_INIT)
23605e82c3bSDamien Miller 			rlen = SSHBUF_SIZE_INIT;
23705e82c3bSDamien Miller 		else
2389136ec13Sderaadt@openbsd.org 			rlen = ROUNDUP(buf->size, SSHBUF_SIZE_INC);
23905e82c3bSDamien Miller 		if (rlen > max_size)
24005e82c3bSDamien Miller 			rlen = max_size;
24105e82c3bSDamien Miller 		SSHBUF_DBG(("new alloc = %zu", rlen));
2429e509d4eSderaadt@openbsd.org 		if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL)
24305e82c3bSDamien Miller 			return SSH_ERR_ALLOC_FAIL;
24405e82c3bSDamien Miller 		buf->cd = buf->d = dp;
24505e82c3bSDamien Miller 		buf->alloc = rlen;
24605e82c3bSDamien Miller 	}
24705e82c3bSDamien Miller 	SSHBUF_TELL("new-max");
24805e82c3bSDamien Miller 	if (max_size < buf->alloc)
24905e82c3bSDamien Miller 		return SSH_ERR_NO_BUFFER_SPACE;
25005e82c3bSDamien Miller 	buf->max_size = max_size;
25105e82c3bSDamien Miller 	return 0;
25205e82c3bSDamien Miller }
25305e82c3bSDamien Miller 
25405e82c3bSDamien Miller size_t
sshbuf_len(const struct sshbuf * buf)25505e82c3bSDamien Miller sshbuf_len(const struct sshbuf *buf)
25605e82c3bSDamien Miller {
25705e82c3bSDamien Miller 	if (sshbuf_check_sanity(buf) != 0)
25805e82c3bSDamien Miller 		return 0;
25905e82c3bSDamien Miller 	return buf->size - buf->off;
26005e82c3bSDamien Miller }
26105e82c3bSDamien Miller 
26205e82c3bSDamien Miller size_t
sshbuf_avail(const struct sshbuf * buf)26305e82c3bSDamien Miller sshbuf_avail(const struct sshbuf *buf)
26405e82c3bSDamien Miller {
26505e82c3bSDamien Miller 	if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1)
26605e82c3bSDamien Miller 		return 0;
26705e82c3bSDamien Miller 	return buf->max_size - (buf->size - buf->off);
26805e82c3bSDamien Miller }
26905e82c3bSDamien Miller 
27005e82c3bSDamien Miller const u_char *
sshbuf_ptr(const struct sshbuf * buf)27105e82c3bSDamien Miller sshbuf_ptr(const struct sshbuf *buf)
27205e82c3bSDamien Miller {
27305e82c3bSDamien Miller 	if (sshbuf_check_sanity(buf) != 0)
27405e82c3bSDamien Miller 		return NULL;
27505e82c3bSDamien Miller 	return buf->cd + buf->off;
27605e82c3bSDamien Miller }
27705e82c3bSDamien Miller 
27805e82c3bSDamien Miller u_char *
sshbuf_mutable_ptr(const struct sshbuf * buf)27905e82c3bSDamien Miller sshbuf_mutable_ptr(const struct sshbuf *buf)
28005e82c3bSDamien Miller {
28105e82c3bSDamien Miller 	if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1)
28205e82c3bSDamien Miller 		return NULL;
28305e82c3bSDamien Miller 	return buf->d + buf->off;
28405e82c3bSDamien Miller }
28505e82c3bSDamien Miller 
28605e82c3bSDamien Miller int
sshbuf_check_reserve(const struct sshbuf * buf,size_t len)28705e82c3bSDamien Miller sshbuf_check_reserve(const struct sshbuf *buf, size_t len)
28805e82c3bSDamien Miller {
28905e82c3bSDamien Miller 	int r;
29005e82c3bSDamien Miller 
29105e82c3bSDamien Miller 	if ((r = sshbuf_check_sanity(buf)) != 0)
29205e82c3bSDamien Miller 		return r;
29305e82c3bSDamien Miller 	if (buf->readonly || buf->refcount > 1)
29405e82c3bSDamien Miller 		return SSH_ERR_BUFFER_READ_ONLY;
29505e82c3bSDamien Miller 	SSHBUF_TELL("check");
29605e82c3bSDamien Miller 	/* Check that len is reasonable and that max_size + available < len */
29705e82c3bSDamien Miller 	if (len > buf->max_size || buf->max_size - len < buf->size - buf->off)
29805e82c3bSDamien Miller 		return SSH_ERR_NO_BUFFER_SPACE;
29905e82c3bSDamien Miller 	return 0;
30005e82c3bSDamien Miller }
30105e82c3bSDamien Miller 
30205e82c3bSDamien Miller int
sshbuf_allocate(struct sshbuf * buf,size_t len)303a9c74608Sdjm@openbsd.org sshbuf_allocate(struct sshbuf *buf, size_t len)
30405e82c3bSDamien Miller {
30505e82c3bSDamien Miller 	size_t rlen, need;
30605e82c3bSDamien Miller 	u_char *dp;
30705e82c3bSDamien Miller 	int r;
30805e82c3bSDamien Miller 
309a9c74608Sdjm@openbsd.org 	SSHBUF_DBG(("allocate buf = %p len = %zu", buf, len));
31005e82c3bSDamien Miller 	if ((r = sshbuf_check_reserve(buf, len)) != 0)
31105e82c3bSDamien Miller 		return r;
31205e82c3bSDamien Miller 	/*
31305e82c3bSDamien Miller 	 * If the requested allocation appended would push us past max_size
31405e82c3bSDamien Miller 	 * then pack the buffer, zeroing buf->off.
31505e82c3bSDamien Miller 	 */
31605e82c3bSDamien Miller 	sshbuf_maybe_pack(buf, buf->size + len > buf->max_size);
317a9c74608Sdjm@openbsd.org 	SSHBUF_TELL("allocate");
318a9c74608Sdjm@openbsd.org 	if (len + buf->size <= buf->alloc)
319a9c74608Sdjm@openbsd.org 		return 0; /* already have it. */
320a9c74608Sdjm@openbsd.org 
32105e82c3bSDamien Miller 	/*
32205e82c3bSDamien Miller 	 * Prefer to alloc in SSHBUF_SIZE_INC units, but
32305e82c3bSDamien Miller 	 * allocate less if doing so would overflow max_size.
32405e82c3bSDamien Miller 	 */
32505e82c3bSDamien Miller 	need = len + buf->size - buf->alloc;
3269136ec13Sderaadt@openbsd.org 	rlen = ROUNDUP(buf->alloc + need, SSHBUF_SIZE_INC);
32705e82c3bSDamien Miller 	SSHBUF_DBG(("need %zu initial rlen %zu", need, rlen));
32805e82c3bSDamien Miller 	if (rlen > buf->max_size)
32905e82c3bSDamien Miller 		rlen = buf->alloc + need;
33005e82c3bSDamien Miller 	SSHBUF_DBG(("adjusted rlen %zu", rlen));
3319e509d4eSderaadt@openbsd.org 	if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL) {
33205e82c3bSDamien Miller 		SSHBUF_DBG(("realloc fail"));
33305e82c3bSDamien Miller 		return SSH_ERR_ALLOC_FAIL;
33405e82c3bSDamien Miller 	}
33505e82c3bSDamien Miller 	buf->alloc = rlen;
33605e82c3bSDamien Miller 	buf->cd = buf->d = dp;
33705e82c3bSDamien Miller 	if ((r = sshbuf_check_reserve(buf, len)) < 0) {
33805e82c3bSDamien Miller 		/* shouldn't fail */
33905e82c3bSDamien Miller 		return r;
34005e82c3bSDamien Miller 	}
341a9c74608Sdjm@openbsd.org 	SSHBUF_TELL("done");
342a9c74608Sdjm@openbsd.org 	return 0;
34305e82c3bSDamien Miller }
344a9c74608Sdjm@openbsd.org 
345a9c74608Sdjm@openbsd.org int
sshbuf_reserve(struct sshbuf * buf,size_t len,u_char ** dpp)346a9c74608Sdjm@openbsd.org sshbuf_reserve(struct sshbuf *buf, size_t len, u_char **dpp)
347a9c74608Sdjm@openbsd.org {
348a9c74608Sdjm@openbsd.org 	u_char *dp;
349a9c74608Sdjm@openbsd.org 	int r;
350a9c74608Sdjm@openbsd.org 
351a9c74608Sdjm@openbsd.org 	if (dpp != NULL)
352a9c74608Sdjm@openbsd.org 		*dpp = NULL;
353a9c74608Sdjm@openbsd.org 
354a9c74608Sdjm@openbsd.org 	SSHBUF_DBG(("reserve buf = %p len = %zu", buf, len));
355a9c74608Sdjm@openbsd.org 	if ((r = sshbuf_allocate(buf, len)) != 0)
356a9c74608Sdjm@openbsd.org 		return r;
357a9c74608Sdjm@openbsd.org 
35805e82c3bSDamien Miller 	dp = buf->d + buf->size;
35905e82c3bSDamien Miller 	buf->size += len;
36005e82c3bSDamien Miller 	if (dpp != NULL)
36105e82c3bSDamien Miller 		*dpp = dp;
36205e82c3bSDamien Miller 	return 0;
36305e82c3bSDamien Miller }
36405e82c3bSDamien Miller 
36505e82c3bSDamien Miller int
sshbuf_consume(struct sshbuf * buf,size_t len)36605e82c3bSDamien Miller sshbuf_consume(struct sshbuf *buf, size_t len)
36705e82c3bSDamien Miller {
36805e82c3bSDamien Miller 	int r;
36905e82c3bSDamien Miller 
37005e82c3bSDamien Miller 	SSHBUF_DBG(("len = %zu", len));
37105e82c3bSDamien Miller 	if ((r = sshbuf_check_sanity(buf)) != 0)
37205e82c3bSDamien Miller 		return r;
37305e82c3bSDamien Miller 	if (len == 0)
37405e82c3bSDamien Miller 		return 0;
37505e82c3bSDamien Miller 	if (len > sshbuf_len(buf))
37605e82c3bSDamien Miller 		return SSH_ERR_MESSAGE_INCOMPLETE;
37705e82c3bSDamien Miller 	buf->off += len;
378813f5533Smarkus@openbsd.org 	/* deal with empty buffer */
379813f5533Smarkus@openbsd.org 	if (buf->off == buf->size)
380813f5533Smarkus@openbsd.org 		buf->off = buf->size = 0;
38105e82c3bSDamien Miller 	SSHBUF_TELL("done");
38205e82c3bSDamien Miller 	return 0;
38305e82c3bSDamien Miller }
38405e82c3bSDamien Miller 
38505e82c3bSDamien Miller int
sshbuf_consume_end(struct sshbuf * buf,size_t len)38605e82c3bSDamien Miller sshbuf_consume_end(struct sshbuf *buf, size_t len)
38705e82c3bSDamien Miller {
38805e82c3bSDamien Miller 	int r;
38905e82c3bSDamien Miller 
39005e82c3bSDamien Miller 	SSHBUF_DBG(("len = %zu", len));
39105e82c3bSDamien Miller 	if ((r = sshbuf_check_sanity(buf)) != 0)
39205e82c3bSDamien Miller 		return r;
39305e82c3bSDamien Miller 	if (len == 0)
39405e82c3bSDamien Miller 		return 0;
39505e82c3bSDamien Miller 	if (len > sshbuf_len(buf))
39605e82c3bSDamien Miller 		return SSH_ERR_MESSAGE_INCOMPLETE;
39705e82c3bSDamien Miller 	buf->size -= len;
39805e82c3bSDamien Miller 	SSHBUF_TELL("done");
39905e82c3bSDamien Miller 	return 0;
40005e82c3bSDamien Miller }
40105e82c3bSDamien Miller 
402