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