xref: /freebsd-src/crypto/openssh/sshbuf.c (revision a0ee8cc636cd5c2374ec44ca71226564ea0bca95)
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