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