xref: /freebsd/crypto/openssh/sshbuf.c (revision 38a52bd3b5cac3da6f7f6eef3dd050e6aa08ebb3)
1*38a52bd3SEd Maste /*	$OpenBSD: sshbuf.c,v 1.18 2022/05/25 06:03:44 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->max_size > SSHBUF_SIZE_MAX ||
40a0ee8cc6SDag-Erling Smørgrav 	    buf->alloc > buf->max_size ||
41a0ee8cc6SDag-Erling Smørgrav 	    buf->size > buf->alloc ||
42a0ee8cc6SDag-Erling Smørgrav 	    buf->off > buf->size)) {
43a0ee8cc6SDag-Erling Smørgrav 		/* Do not try to recover from corrupted buffer internals */
44a0ee8cc6SDag-Erling Smørgrav 		SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR"));
4519261079SEd Maste 		ssh_signal(SIGSEGV, SIG_DFL);
46a0ee8cc6SDag-Erling Smørgrav 		raise(SIGSEGV);
47a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_INTERNAL_ERROR;
48a0ee8cc6SDag-Erling Smørgrav 	}
49a0ee8cc6SDag-Erling Smørgrav 	return 0;
50a0ee8cc6SDag-Erling Smørgrav }
51a0ee8cc6SDag-Erling Smørgrav 
52a0ee8cc6SDag-Erling Smørgrav static void
53a0ee8cc6SDag-Erling Smørgrav sshbuf_maybe_pack(struct sshbuf *buf, int force)
54a0ee8cc6SDag-Erling Smørgrav {
55a0ee8cc6SDag-Erling Smørgrav 	SSHBUF_DBG(("force %d", force));
56a0ee8cc6SDag-Erling Smørgrav 	SSHBUF_TELL("pre-pack");
57a0ee8cc6SDag-Erling Smørgrav 	if (buf->off == 0 || buf->readonly || buf->refcount > 1)
58a0ee8cc6SDag-Erling Smørgrav 		return;
59a0ee8cc6SDag-Erling Smørgrav 	if (force ||
60a0ee8cc6SDag-Erling Smørgrav 	    (buf->off >= SSHBUF_PACK_MIN && buf->off >= buf->size / 2)) {
61a0ee8cc6SDag-Erling Smørgrav 		memmove(buf->d, buf->d + buf->off, buf->size - buf->off);
62a0ee8cc6SDag-Erling Smørgrav 		buf->size -= buf->off;
63a0ee8cc6SDag-Erling Smørgrav 		buf->off = 0;
64a0ee8cc6SDag-Erling Smørgrav 		SSHBUF_TELL("packed");
65a0ee8cc6SDag-Erling Smørgrav 	}
66a0ee8cc6SDag-Erling Smørgrav }
67a0ee8cc6SDag-Erling Smørgrav 
68a0ee8cc6SDag-Erling Smørgrav struct sshbuf *
69a0ee8cc6SDag-Erling Smørgrav sshbuf_new(void)
70a0ee8cc6SDag-Erling Smørgrav {
71a0ee8cc6SDag-Erling Smørgrav 	struct sshbuf *ret;
72a0ee8cc6SDag-Erling Smørgrav 
73a0ee8cc6SDag-Erling Smørgrav 	if ((ret = calloc(sizeof(*ret), 1)) == NULL)
74a0ee8cc6SDag-Erling Smørgrav 		return NULL;
75a0ee8cc6SDag-Erling Smørgrav 	ret->alloc = SSHBUF_SIZE_INIT;
76a0ee8cc6SDag-Erling Smørgrav 	ret->max_size = SSHBUF_SIZE_MAX;
77a0ee8cc6SDag-Erling Smørgrav 	ret->readonly = 0;
78a0ee8cc6SDag-Erling Smørgrav 	ret->refcount = 1;
79a0ee8cc6SDag-Erling Smørgrav 	ret->parent = NULL;
80a0ee8cc6SDag-Erling Smørgrav 	if ((ret->cd = ret->d = calloc(1, ret->alloc)) == NULL) {
81a0ee8cc6SDag-Erling Smørgrav 		free(ret);
82a0ee8cc6SDag-Erling Smørgrav 		return NULL;
83a0ee8cc6SDag-Erling Smørgrav 	}
84a0ee8cc6SDag-Erling Smørgrav 	return ret;
85a0ee8cc6SDag-Erling Smørgrav }
86a0ee8cc6SDag-Erling Smørgrav 
87a0ee8cc6SDag-Erling Smørgrav struct sshbuf *
88a0ee8cc6SDag-Erling Smørgrav sshbuf_from(const void *blob, size_t len)
89a0ee8cc6SDag-Erling Smørgrav {
90a0ee8cc6SDag-Erling Smørgrav 	struct sshbuf *ret;
91a0ee8cc6SDag-Erling Smørgrav 
92a0ee8cc6SDag-Erling Smørgrav 	if (blob == NULL || len > SSHBUF_SIZE_MAX ||
93a0ee8cc6SDag-Erling Smørgrav 	    (ret = calloc(sizeof(*ret), 1)) == NULL)
94a0ee8cc6SDag-Erling Smørgrav 		return NULL;
95a0ee8cc6SDag-Erling Smørgrav 	ret->alloc = ret->size = ret->max_size = len;
96a0ee8cc6SDag-Erling Smørgrav 	ret->readonly = 1;
97a0ee8cc6SDag-Erling Smørgrav 	ret->refcount = 1;
98a0ee8cc6SDag-Erling Smørgrav 	ret->parent = NULL;
99a0ee8cc6SDag-Erling Smørgrav 	ret->cd = blob;
100a0ee8cc6SDag-Erling Smørgrav 	ret->d = NULL;
101a0ee8cc6SDag-Erling Smørgrav 	return ret;
102a0ee8cc6SDag-Erling Smørgrav }
103a0ee8cc6SDag-Erling Smørgrav 
104a0ee8cc6SDag-Erling Smørgrav int
105a0ee8cc6SDag-Erling Smørgrav sshbuf_set_parent(struct sshbuf *child, struct sshbuf *parent)
106a0ee8cc6SDag-Erling Smørgrav {
107a0ee8cc6SDag-Erling Smørgrav 	int r;
108a0ee8cc6SDag-Erling Smørgrav 
109a0ee8cc6SDag-Erling Smørgrav 	if ((r = sshbuf_check_sanity(child)) != 0 ||
110a0ee8cc6SDag-Erling Smørgrav 	    (r = sshbuf_check_sanity(parent)) != 0)
111a0ee8cc6SDag-Erling Smørgrav 		return r;
112*38a52bd3SEd Maste 	if (child->parent != NULL && child->parent != parent)
113*38a52bd3SEd Maste 		return SSH_ERR_INTERNAL_ERROR;
114a0ee8cc6SDag-Erling Smørgrav 	child->parent = parent;
115a0ee8cc6SDag-Erling Smørgrav 	child->parent->refcount++;
116a0ee8cc6SDag-Erling Smørgrav 	return 0;
117a0ee8cc6SDag-Erling Smørgrav }
118a0ee8cc6SDag-Erling Smørgrav 
119a0ee8cc6SDag-Erling Smørgrav struct sshbuf *
120a0ee8cc6SDag-Erling Smørgrav sshbuf_fromb(struct sshbuf *buf)
121a0ee8cc6SDag-Erling Smørgrav {
122a0ee8cc6SDag-Erling Smørgrav 	struct sshbuf *ret;
123a0ee8cc6SDag-Erling Smørgrav 
124a0ee8cc6SDag-Erling Smørgrav 	if (sshbuf_check_sanity(buf) != 0)
125a0ee8cc6SDag-Erling Smørgrav 		return NULL;
126a0ee8cc6SDag-Erling Smørgrav 	if ((ret = sshbuf_from(sshbuf_ptr(buf), sshbuf_len(buf))) == NULL)
127a0ee8cc6SDag-Erling Smørgrav 		return NULL;
128a0ee8cc6SDag-Erling Smørgrav 	if (sshbuf_set_parent(ret, buf) != 0) {
129a0ee8cc6SDag-Erling Smørgrav 		sshbuf_free(ret);
130a0ee8cc6SDag-Erling Smørgrav 		return NULL;
131a0ee8cc6SDag-Erling Smørgrav 	}
132a0ee8cc6SDag-Erling Smørgrav 	return ret;
133a0ee8cc6SDag-Erling Smørgrav }
134a0ee8cc6SDag-Erling Smørgrav 
135a0ee8cc6SDag-Erling Smørgrav void
136a0ee8cc6SDag-Erling Smørgrav sshbuf_free(struct sshbuf *buf)
137a0ee8cc6SDag-Erling Smørgrav {
138a0ee8cc6SDag-Erling Smørgrav 	if (buf == NULL)
139a0ee8cc6SDag-Erling Smørgrav 		return;
140a0ee8cc6SDag-Erling Smørgrav 	/*
141a0ee8cc6SDag-Erling Smørgrav 	 * The following will leak on insane buffers, but this is the safest
142a0ee8cc6SDag-Erling Smørgrav 	 * course of action - an invalid pointer or already-freed pointer may
143a0ee8cc6SDag-Erling Smørgrav 	 * have been passed to us and continuing to scribble over memory would
144a0ee8cc6SDag-Erling Smørgrav 	 * be bad.
145a0ee8cc6SDag-Erling Smørgrav 	 */
146a0ee8cc6SDag-Erling Smørgrav 	if (sshbuf_check_sanity(buf) != 0)
147a0ee8cc6SDag-Erling Smørgrav 		return;
14819261079SEd Maste 
149a0ee8cc6SDag-Erling Smørgrav 	/*
150a0ee8cc6SDag-Erling Smørgrav 	 * If we are a parent with still-extant children, then don't free just
151a0ee8cc6SDag-Erling Smørgrav 	 * yet. The last child's call to sshbuf_free should decrement our
152a0ee8cc6SDag-Erling Smørgrav 	 * refcount to 0 and trigger the actual free.
153a0ee8cc6SDag-Erling Smørgrav 	 */
154a0ee8cc6SDag-Erling Smørgrav 	buf->refcount--;
155a0ee8cc6SDag-Erling Smørgrav 	if (buf->refcount > 0)
156a0ee8cc6SDag-Erling Smørgrav 		return;
15719261079SEd Maste 
15819261079SEd Maste 	/*
15919261079SEd Maste 	 * If we are a child, the free our parent to decrement its reference
16019261079SEd Maste 	 * count and possibly free it.
16119261079SEd Maste 	 */
16219261079SEd Maste 	sshbuf_free(buf->parent);
16319261079SEd Maste 	buf->parent = NULL;
16419261079SEd Maste 
165a0ee8cc6SDag-Erling Smørgrav 	if (!buf->readonly) {
166fc1ba28aSDag-Erling Smørgrav 		explicit_bzero(buf->d, buf->alloc);
167a0ee8cc6SDag-Erling Smørgrav 		free(buf->d);
168a0ee8cc6SDag-Erling Smørgrav 	}
16919261079SEd Maste 	freezero(buf, sizeof(*buf));
170a0ee8cc6SDag-Erling Smørgrav }
171a0ee8cc6SDag-Erling Smørgrav 
172a0ee8cc6SDag-Erling Smørgrav void
173a0ee8cc6SDag-Erling Smørgrav sshbuf_reset(struct sshbuf *buf)
174a0ee8cc6SDag-Erling Smørgrav {
175a0ee8cc6SDag-Erling Smørgrav 	u_char *d;
176a0ee8cc6SDag-Erling Smørgrav 
177a0ee8cc6SDag-Erling Smørgrav 	if (buf->readonly || buf->refcount > 1) {
178a0ee8cc6SDag-Erling Smørgrav 		/* Nonsensical. Just make buffer appear empty */
179a0ee8cc6SDag-Erling Smørgrav 		buf->off = buf->size;
180a0ee8cc6SDag-Erling Smørgrav 		return;
181a0ee8cc6SDag-Erling Smørgrav 	}
182*38a52bd3SEd Maste 	if (sshbuf_check_sanity(buf) != 0)
183*38a52bd3SEd Maste 		return;
184a0ee8cc6SDag-Erling Smørgrav 	buf->off = buf->size = 0;
185a0ee8cc6SDag-Erling Smørgrav 	if (buf->alloc != SSHBUF_SIZE_INIT) {
1864f52dfbbSDag-Erling Smørgrav 		if ((d = recallocarray(buf->d, buf->alloc, SSHBUF_SIZE_INIT,
1874f52dfbbSDag-Erling Smørgrav 		    1)) != NULL) {
188a0ee8cc6SDag-Erling Smørgrav 			buf->cd = buf->d = d;
189a0ee8cc6SDag-Erling Smørgrav 			buf->alloc = SSHBUF_SIZE_INIT;
190a0ee8cc6SDag-Erling Smørgrav 		}
191a0ee8cc6SDag-Erling Smørgrav 	}
192*38a52bd3SEd Maste 	explicit_bzero(buf->d, buf->alloc);
193a0ee8cc6SDag-Erling Smørgrav }
194a0ee8cc6SDag-Erling Smørgrav 
195a0ee8cc6SDag-Erling Smørgrav size_t
196a0ee8cc6SDag-Erling Smørgrav sshbuf_max_size(const struct sshbuf *buf)
197a0ee8cc6SDag-Erling Smørgrav {
198a0ee8cc6SDag-Erling Smørgrav 	return buf->max_size;
199a0ee8cc6SDag-Erling Smørgrav }
200a0ee8cc6SDag-Erling Smørgrav 
201a0ee8cc6SDag-Erling Smørgrav size_t
202a0ee8cc6SDag-Erling Smørgrav sshbuf_alloc(const struct sshbuf *buf)
203a0ee8cc6SDag-Erling Smørgrav {
204a0ee8cc6SDag-Erling Smørgrav 	return buf->alloc;
205a0ee8cc6SDag-Erling Smørgrav }
206a0ee8cc6SDag-Erling Smørgrav 
207a0ee8cc6SDag-Erling Smørgrav const struct sshbuf *
208a0ee8cc6SDag-Erling Smørgrav sshbuf_parent(const struct sshbuf *buf)
209a0ee8cc6SDag-Erling Smørgrav {
210a0ee8cc6SDag-Erling Smørgrav 	return buf->parent;
211a0ee8cc6SDag-Erling Smørgrav }
212a0ee8cc6SDag-Erling Smørgrav 
213a0ee8cc6SDag-Erling Smørgrav u_int
214a0ee8cc6SDag-Erling Smørgrav sshbuf_refcount(const struct sshbuf *buf)
215a0ee8cc6SDag-Erling Smørgrav {
216a0ee8cc6SDag-Erling Smørgrav 	return buf->refcount;
217a0ee8cc6SDag-Erling Smørgrav }
218a0ee8cc6SDag-Erling Smørgrav 
219a0ee8cc6SDag-Erling Smørgrav int
220a0ee8cc6SDag-Erling Smørgrav sshbuf_set_max_size(struct sshbuf *buf, size_t max_size)
221a0ee8cc6SDag-Erling Smørgrav {
222a0ee8cc6SDag-Erling Smørgrav 	size_t rlen;
223a0ee8cc6SDag-Erling Smørgrav 	u_char *dp;
224a0ee8cc6SDag-Erling Smørgrav 	int r;
225a0ee8cc6SDag-Erling Smørgrav 
226a0ee8cc6SDag-Erling Smørgrav 	SSHBUF_DBG(("set max buf = %p len = %zu", buf, max_size));
227a0ee8cc6SDag-Erling Smørgrav 	if ((r = sshbuf_check_sanity(buf)) != 0)
228a0ee8cc6SDag-Erling Smørgrav 		return r;
229a0ee8cc6SDag-Erling Smørgrav 	if (max_size == buf->max_size)
230a0ee8cc6SDag-Erling Smørgrav 		return 0;
231a0ee8cc6SDag-Erling Smørgrav 	if (buf->readonly || buf->refcount > 1)
232a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_BUFFER_READ_ONLY;
233a0ee8cc6SDag-Erling Smørgrav 	if (max_size > SSHBUF_SIZE_MAX)
234a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_NO_BUFFER_SPACE;
235a0ee8cc6SDag-Erling Smørgrav 	/* pack and realloc if necessary */
236a0ee8cc6SDag-Erling Smørgrav 	sshbuf_maybe_pack(buf, max_size < buf->size);
237a0ee8cc6SDag-Erling Smørgrav 	if (max_size < buf->alloc && max_size > buf->size) {
238a0ee8cc6SDag-Erling Smørgrav 		if (buf->size < SSHBUF_SIZE_INIT)
239a0ee8cc6SDag-Erling Smørgrav 			rlen = SSHBUF_SIZE_INIT;
240a0ee8cc6SDag-Erling Smørgrav 		else
241ca86bcf2SDag-Erling Smørgrav 			rlen = ROUNDUP(buf->size, SSHBUF_SIZE_INC);
242a0ee8cc6SDag-Erling Smørgrav 		if (rlen > max_size)
243a0ee8cc6SDag-Erling Smørgrav 			rlen = max_size;
244a0ee8cc6SDag-Erling Smørgrav 		SSHBUF_DBG(("new alloc = %zu", rlen));
2454f52dfbbSDag-Erling Smørgrav 		if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL)
246a0ee8cc6SDag-Erling Smørgrav 			return SSH_ERR_ALLOC_FAIL;
247a0ee8cc6SDag-Erling Smørgrav 		buf->cd = buf->d = dp;
248a0ee8cc6SDag-Erling Smørgrav 		buf->alloc = rlen;
249a0ee8cc6SDag-Erling Smørgrav 	}
250a0ee8cc6SDag-Erling Smørgrav 	SSHBUF_TELL("new-max");
251a0ee8cc6SDag-Erling Smørgrav 	if (max_size < buf->alloc)
252a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_NO_BUFFER_SPACE;
253a0ee8cc6SDag-Erling Smørgrav 	buf->max_size = max_size;
254a0ee8cc6SDag-Erling Smørgrav 	return 0;
255a0ee8cc6SDag-Erling Smørgrav }
256a0ee8cc6SDag-Erling Smørgrav 
257a0ee8cc6SDag-Erling Smørgrav size_t
258a0ee8cc6SDag-Erling Smørgrav sshbuf_len(const struct sshbuf *buf)
259a0ee8cc6SDag-Erling Smørgrav {
260a0ee8cc6SDag-Erling Smørgrav 	if (sshbuf_check_sanity(buf) != 0)
261a0ee8cc6SDag-Erling Smørgrav 		return 0;
262a0ee8cc6SDag-Erling Smørgrav 	return buf->size - buf->off;
263a0ee8cc6SDag-Erling Smørgrav }
264a0ee8cc6SDag-Erling Smørgrav 
265a0ee8cc6SDag-Erling Smørgrav size_t
266a0ee8cc6SDag-Erling Smørgrav sshbuf_avail(const struct sshbuf *buf)
267a0ee8cc6SDag-Erling Smørgrav {
268a0ee8cc6SDag-Erling Smørgrav 	if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1)
269a0ee8cc6SDag-Erling Smørgrav 		return 0;
270a0ee8cc6SDag-Erling Smørgrav 	return buf->max_size - (buf->size - buf->off);
271a0ee8cc6SDag-Erling Smørgrav }
272a0ee8cc6SDag-Erling Smørgrav 
273a0ee8cc6SDag-Erling Smørgrav const u_char *
274a0ee8cc6SDag-Erling Smørgrav sshbuf_ptr(const struct sshbuf *buf)
275a0ee8cc6SDag-Erling Smørgrav {
276a0ee8cc6SDag-Erling Smørgrav 	if (sshbuf_check_sanity(buf) != 0)
277a0ee8cc6SDag-Erling Smørgrav 		return NULL;
278a0ee8cc6SDag-Erling Smørgrav 	return buf->cd + buf->off;
279a0ee8cc6SDag-Erling Smørgrav }
280a0ee8cc6SDag-Erling Smørgrav 
281a0ee8cc6SDag-Erling Smørgrav u_char *
282a0ee8cc6SDag-Erling Smørgrav sshbuf_mutable_ptr(const struct sshbuf *buf)
283a0ee8cc6SDag-Erling Smørgrav {
284a0ee8cc6SDag-Erling Smørgrav 	if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1)
285a0ee8cc6SDag-Erling Smørgrav 		return NULL;
286a0ee8cc6SDag-Erling Smørgrav 	return buf->d + buf->off;
287a0ee8cc6SDag-Erling Smørgrav }
288a0ee8cc6SDag-Erling Smørgrav 
289a0ee8cc6SDag-Erling Smørgrav int
290a0ee8cc6SDag-Erling Smørgrav sshbuf_check_reserve(const struct sshbuf *buf, size_t len)
291a0ee8cc6SDag-Erling Smørgrav {
292a0ee8cc6SDag-Erling Smørgrav 	int r;
293a0ee8cc6SDag-Erling Smørgrav 
294a0ee8cc6SDag-Erling Smørgrav 	if ((r = sshbuf_check_sanity(buf)) != 0)
295a0ee8cc6SDag-Erling Smørgrav 		return r;
296a0ee8cc6SDag-Erling Smørgrav 	if (buf->readonly || buf->refcount > 1)
297a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_BUFFER_READ_ONLY;
298a0ee8cc6SDag-Erling Smørgrav 	SSHBUF_TELL("check");
299a0ee8cc6SDag-Erling Smørgrav 	/* Check that len is reasonable and that max_size + available < len */
300a0ee8cc6SDag-Erling Smørgrav 	if (len > buf->max_size || buf->max_size - len < buf->size - buf->off)
301a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_NO_BUFFER_SPACE;
302a0ee8cc6SDag-Erling Smørgrav 	return 0;
303a0ee8cc6SDag-Erling Smørgrav }
304a0ee8cc6SDag-Erling Smørgrav 
305a0ee8cc6SDag-Erling Smørgrav int
306ca86bcf2SDag-Erling Smørgrav sshbuf_allocate(struct sshbuf *buf, size_t len)
307a0ee8cc6SDag-Erling Smørgrav {
308a0ee8cc6SDag-Erling Smørgrav 	size_t rlen, need;
309a0ee8cc6SDag-Erling Smørgrav 	u_char *dp;
310a0ee8cc6SDag-Erling Smørgrav 	int r;
311a0ee8cc6SDag-Erling Smørgrav 
312ca86bcf2SDag-Erling Smørgrav 	SSHBUF_DBG(("allocate buf = %p len = %zu", buf, len));
313a0ee8cc6SDag-Erling Smørgrav 	if ((r = sshbuf_check_reserve(buf, len)) != 0)
314a0ee8cc6SDag-Erling Smørgrav 		return r;
315a0ee8cc6SDag-Erling Smørgrav 	/*
316a0ee8cc6SDag-Erling Smørgrav 	 * If the requested allocation appended would push us past max_size
317a0ee8cc6SDag-Erling Smørgrav 	 * then pack the buffer, zeroing buf->off.
318a0ee8cc6SDag-Erling Smørgrav 	 */
319a0ee8cc6SDag-Erling Smørgrav 	sshbuf_maybe_pack(buf, buf->size + len > buf->max_size);
320ca86bcf2SDag-Erling Smørgrav 	SSHBUF_TELL("allocate");
321ca86bcf2SDag-Erling Smørgrav 	if (len + buf->size <= buf->alloc)
322ca86bcf2SDag-Erling Smørgrav 		return 0; /* already have it. */
323ca86bcf2SDag-Erling Smørgrav 
324a0ee8cc6SDag-Erling Smørgrav 	/*
325a0ee8cc6SDag-Erling Smørgrav 	 * Prefer to alloc in SSHBUF_SIZE_INC units, but
326a0ee8cc6SDag-Erling Smørgrav 	 * allocate less if doing so would overflow max_size.
327a0ee8cc6SDag-Erling Smørgrav 	 */
328a0ee8cc6SDag-Erling Smørgrav 	need = len + buf->size - buf->alloc;
329ca86bcf2SDag-Erling Smørgrav 	rlen = ROUNDUP(buf->alloc + need, SSHBUF_SIZE_INC);
330a0ee8cc6SDag-Erling Smørgrav 	SSHBUF_DBG(("need %zu initial rlen %zu", need, rlen));
331a0ee8cc6SDag-Erling Smørgrav 	if (rlen > buf->max_size)
332a0ee8cc6SDag-Erling Smørgrav 		rlen = buf->alloc + need;
333a0ee8cc6SDag-Erling Smørgrav 	SSHBUF_DBG(("adjusted rlen %zu", rlen));
3344f52dfbbSDag-Erling Smørgrav 	if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL) {
335a0ee8cc6SDag-Erling Smørgrav 		SSHBUF_DBG(("realloc fail"));
336a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_ALLOC_FAIL;
337a0ee8cc6SDag-Erling Smørgrav 	}
338a0ee8cc6SDag-Erling Smørgrav 	buf->alloc = rlen;
339a0ee8cc6SDag-Erling Smørgrav 	buf->cd = buf->d = dp;
340a0ee8cc6SDag-Erling Smørgrav 	if ((r = sshbuf_check_reserve(buf, len)) < 0) {
341a0ee8cc6SDag-Erling Smørgrav 		/* shouldn't fail */
342a0ee8cc6SDag-Erling Smørgrav 		return r;
343a0ee8cc6SDag-Erling Smørgrav 	}
344ca86bcf2SDag-Erling Smørgrav 	SSHBUF_TELL("done");
345ca86bcf2SDag-Erling Smørgrav 	return 0;
346a0ee8cc6SDag-Erling Smørgrav }
347ca86bcf2SDag-Erling Smørgrav 
348ca86bcf2SDag-Erling Smørgrav int
349ca86bcf2SDag-Erling Smørgrav sshbuf_reserve(struct sshbuf *buf, size_t len, u_char **dpp)
350ca86bcf2SDag-Erling Smørgrav {
351ca86bcf2SDag-Erling Smørgrav 	u_char *dp;
352ca86bcf2SDag-Erling Smørgrav 	int r;
353ca86bcf2SDag-Erling Smørgrav 
354ca86bcf2SDag-Erling Smørgrav 	if (dpp != NULL)
355ca86bcf2SDag-Erling Smørgrav 		*dpp = NULL;
356ca86bcf2SDag-Erling Smørgrav 
357ca86bcf2SDag-Erling Smørgrav 	SSHBUF_DBG(("reserve buf = %p len = %zu", buf, len));
358ca86bcf2SDag-Erling Smørgrav 	if ((r = sshbuf_allocate(buf, len)) != 0)
359ca86bcf2SDag-Erling Smørgrav 		return r;
360ca86bcf2SDag-Erling Smørgrav 
361a0ee8cc6SDag-Erling Smørgrav 	dp = buf->d + buf->size;
362a0ee8cc6SDag-Erling Smørgrav 	buf->size += len;
363a0ee8cc6SDag-Erling Smørgrav 	if (dpp != NULL)
364a0ee8cc6SDag-Erling Smørgrav 		*dpp = dp;
365a0ee8cc6SDag-Erling Smørgrav 	return 0;
366a0ee8cc6SDag-Erling Smørgrav }
367a0ee8cc6SDag-Erling Smørgrav 
368a0ee8cc6SDag-Erling Smørgrav int
369a0ee8cc6SDag-Erling Smørgrav sshbuf_consume(struct sshbuf *buf, size_t len)
370a0ee8cc6SDag-Erling Smørgrav {
371a0ee8cc6SDag-Erling Smørgrav 	int r;
372a0ee8cc6SDag-Erling Smørgrav 
373a0ee8cc6SDag-Erling Smørgrav 	SSHBUF_DBG(("len = %zu", len));
374a0ee8cc6SDag-Erling Smørgrav 	if ((r = sshbuf_check_sanity(buf)) != 0)
375a0ee8cc6SDag-Erling Smørgrav 		return r;
376a0ee8cc6SDag-Erling Smørgrav 	if (len == 0)
377a0ee8cc6SDag-Erling Smørgrav 		return 0;
378a0ee8cc6SDag-Erling Smørgrav 	if (len > sshbuf_len(buf))
379a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_MESSAGE_INCOMPLETE;
380a0ee8cc6SDag-Erling Smørgrav 	buf->off += len;
3814f52dfbbSDag-Erling Smørgrav 	/* deal with empty buffer */
3824f52dfbbSDag-Erling Smørgrav 	if (buf->off == buf->size)
3834f52dfbbSDag-Erling Smørgrav 		buf->off = buf->size = 0;
384a0ee8cc6SDag-Erling Smørgrav 	SSHBUF_TELL("done");
385a0ee8cc6SDag-Erling Smørgrav 	return 0;
386a0ee8cc6SDag-Erling Smørgrav }
387a0ee8cc6SDag-Erling Smørgrav 
388a0ee8cc6SDag-Erling Smørgrav int
389a0ee8cc6SDag-Erling Smørgrav sshbuf_consume_end(struct sshbuf *buf, size_t len)
390a0ee8cc6SDag-Erling Smørgrav {
391a0ee8cc6SDag-Erling Smørgrav 	int r;
392a0ee8cc6SDag-Erling Smørgrav 
393a0ee8cc6SDag-Erling Smørgrav 	SSHBUF_DBG(("len = %zu", len));
394a0ee8cc6SDag-Erling Smørgrav 	if ((r = sshbuf_check_sanity(buf)) != 0)
395a0ee8cc6SDag-Erling Smørgrav 		return r;
396a0ee8cc6SDag-Erling Smørgrav 	if (len == 0)
397a0ee8cc6SDag-Erling Smørgrav 		return 0;
398a0ee8cc6SDag-Erling Smørgrav 	if (len > sshbuf_len(buf))
399a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_MESSAGE_INCOMPLETE;
400a0ee8cc6SDag-Erling Smørgrav 	buf->size -= len;
401a0ee8cc6SDag-Erling Smørgrav 	SSHBUF_TELL("done");
402a0ee8cc6SDag-Erling Smørgrav 	return 0;
403a0ee8cc6SDag-Erling Smørgrav }
404a0ee8cc6SDag-Erling Smørgrav 
405