xref: /freebsd/crypto/openssh/sshbuf.c (revision 19261079b74319502c6ffa1249920079f0f69a72)
1*19261079SEd Maste /*	$OpenBSD: sshbuf.c,v 1.15 2020/02/26 13:40:09 jsg 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"));
45*19261079SEd 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;
112a0ee8cc6SDag-Erling Smørgrav 	child->parent = parent;
113a0ee8cc6SDag-Erling Smørgrav 	child->parent->refcount++;
114a0ee8cc6SDag-Erling Smørgrav 	return 0;
115a0ee8cc6SDag-Erling Smørgrav }
116a0ee8cc6SDag-Erling Smørgrav 
117a0ee8cc6SDag-Erling Smørgrav struct sshbuf *
118a0ee8cc6SDag-Erling Smørgrav sshbuf_fromb(struct sshbuf *buf)
119a0ee8cc6SDag-Erling Smørgrav {
120a0ee8cc6SDag-Erling Smørgrav 	struct sshbuf *ret;
121a0ee8cc6SDag-Erling Smørgrav 
122a0ee8cc6SDag-Erling Smørgrav 	if (sshbuf_check_sanity(buf) != 0)
123a0ee8cc6SDag-Erling Smørgrav 		return NULL;
124a0ee8cc6SDag-Erling Smørgrav 	if ((ret = sshbuf_from(sshbuf_ptr(buf), sshbuf_len(buf))) == NULL)
125a0ee8cc6SDag-Erling Smørgrav 		return NULL;
126a0ee8cc6SDag-Erling Smørgrav 	if (sshbuf_set_parent(ret, buf) != 0) {
127a0ee8cc6SDag-Erling Smørgrav 		sshbuf_free(ret);
128a0ee8cc6SDag-Erling Smørgrav 		return NULL;
129a0ee8cc6SDag-Erling Smørgrav 	}
130a0ee8cc6SDag-Erling Smørgrav 	return ret;
131a0ee8cc6SDag-Erling Smørgrav }
132a0ee8cc6SDag-Erling Smørgrav 
133a0ee8cc6SDag-Erling Smørgrav void
134a0ee8cc6SDag-Erling Smørgrav sshbuf_free(struct sshbuf *buf)
135a0ee8cc6SDag-Erling Smørgrav {
136a0ee8cc6SDag-Erling Smørgrav 	if (buf == NULL)
137a0ee8cc6SDag-Erling Smørgrav 		return;
138a0ee8cc6SDag-Erling Smørgrav 	/*
139a0ee8cc6SDag-Erling Smørgrav 	 * The following will leak on insane buffers, but this is the safest
140a0ee8cc6SDag-Erling Smørgrav 	 * course of action - an invalid pointer or already-freed pointer may
141a0ee8cc6SDag-Erling Smørgrav 	 * have been passed to us and continuing to scribble over memory would
142a0ee8cc6SDag-Erling Smørgrav 	 * be bad.
143a0ee8cc6SDag-Erling Smørgrav 	 */
144a0ee8cc6SDag-Erling Smørgrav 	if (sshbuf_check_sanity(buf) != 0)
145a0ee8cc6SDag-Erling Smørgrav 		return;
146*19261079SEd Maste 
147a0ee8cc6SDag-Erling Smørgrav 	/*
148a0ee8cc6SDag-Erling Smørgrav 	 * If we are a parent with still-extant children, then don't free just
149a0ee8cc6SDag-Erling Smørgrav 	 * yet. The last child's call to sshbuf_free should decrement our
150a0ee8cc6SDag-Erling Smørgrav 	 * refcount to 0 and trigger the actual free.
151a0ee8cc6SDag-Erling Smørgrav 	 */
152a0ee8cc6SDag-Erling Smørgrav 	buf->refcount--;
153a0ee8cc6SDag-Erling Smørgrav 	if (buf->refcount > 0)
154a0ee8cc6SDag-Erling Smørgrav 		return;
155*19261079SEd Maste 
156*19261079SEd Maste 	/*
157*19261079SEd Maste 	 * If we are a child, the free our parent to decrement its reference
158*19261079SEd Maste 	 * count and possibly free it.
159*19261079SEd Maste 	 */
160*19261079SEd Maste 	sshbuf_free(buf->parent);
161*19261079SEd Maste 	buf->parent = NULL;
162*19261079SEd Maste 
163a0ee8cc6SDag-Erling Smørgrav 	if (!buf->readonly) {
164fc1ba28aSDag-Erling Smørgrav 		explicit_bzero(buf->d, buf->alloc);
165a0ee8cc6SDag-Erling Smørgrav 		free(buf->d);
166a0ee8cc6SDag-Erling Smørgrav 	}
167*19261079SEd Maste 	freezero(buf, sizeof(*buf));
168a0ee8cc6SDag-Erling Smørgrav }
169a0ee8cc6SDag-Erling Smørgrav 
170a0ee8cc6SDag-Erling Smørgrav void
171a0ee8cc6SDag-Erling Smørgrav sshbuf_reset(struct sshbuf *buf)
172a0ee8cc6SDag-Erling Smørgrav {
173a0ee8cc6SDag-Erling Smørgrav 	u_char *d;
174a0ee8cc6SDag-Erling Smørgrav 
175a0ee8cc6SDag-Erling Smørgrav 	if (buf->readonly || buf->refcount > 1) {
176a0ee8cc6SDag-Erling Smørgrav 		/* Nonsensical. Just make buffer appear empty */
177a0ee8cc6SDag-Erling Smørgrav 		buf->off = buf->size;
178a0ee8cc6SDag-Erling Smørgrav 		return;
179a0ee8cc6SDag-Erling Smørgrav 	}
1804f52dfbbSDag-Erling Smørgrav 	(void) sshbuf_check_sanity(buf);
181a0ee8cc6SDag-Erling Smørgrav 	buf->off = buf->size = 0;
182a0ee8cc6SDag-Erling Smørgrav 	if (buf->alloc != SSHBUF_SIZE_INIT) {
1834f52dfbbSDag-Erling Smørgrav 		if ((d = recallocarray(buf->d, buf->alloc, SSHBUF_SIZE_INIT,
1844f52dfbbSDag-Erling Smørgrav 		    1)) != NULL) {
185a0ee8cc6SDag-Erling Smørgrav 			buf->cd = buf->d = d;
186a0ee8cc6SDag-Erling Smørgrav 			buf->alloc = SSHBUF_SIZE_INIT;
187a0ee8cc6SDag-Erling Smørgrav 		}
188a0ee8cc6SDag-Erling Smørgrav 	}
1894f52dfbbSDag-Erling Smørgrav 	explicit_bzero(buf->d, SSHBUF_SIZE_INIT);
190a0ee8cc6SDag-Erling Smørgrav }
191a0ee8cc6SDag-Erling Smørgrav 
192a0ee8cc6SDag-Erling Smørgrav size_t
193a0ee8cc6SDag-Erling Smørgrav sshbuf_max_size(const struct sshbuf *buf)
194a0ee8cc6SDag-Erling Smørgrav {
195a0ee8cc6SDag-Erling Smørgrav 	return buf->max_size;
196a0ee8cc6SDag-Erling Smørgrav }
197a0ee8cc6SDag-Erling Smørgrav 
198a0ee8cc6SDag-Erling Smørgrav size_t
199a0ee8cc6SDag-Erling Smørgrav sshbuf_alloc(const struct sshbuf *buf)
200a0ee8cc6SDag-Erling Smørgrav {
201a0ee8cc6SDag-Erling Smørgrav 	return buf->alloc;
202a0ee8cc6SDag-Erling Smørgrav }
203a0ee8cc6SDag-Erling Smørgrav 
204a0ee8cc6SDag-Erling Smørgrav const struct sshbuf *
205a0ee8cc6SDag-Erling Smørgrav sshbuf_parent(const struct sshbuf *buf)
206a0ee8cc6SDag-Erling Smørgrav {
207a0ee8cc6SDag-Erling Smørgrav 	return buf->parent;
208a0ee8cc6SDag-Erling Smørgrav }
209a0ee8cc6SDag-Erling Smørgrav 
210a0ee8cc6SDag-Erling Smørgrav u_int
211a0ee8cc6SDag-Erling Smørgrav sshbuf_refcount(const struct sshbuf *buf)
212a0ee8cc6SDag-Erling Smørgrav {
213a0ee8cc6SDag-Erling Smørgrav 	return buf->refcount;
214a0ee8cc6SDag-Erling Smørgrav }
215a0ee8cc6SDag-Erling Smørgrav 
216a0ee8cc6SDag-Erling Smørgrav int
217a0ee8cc6SDag-Erling Smørgrav sshbuf_set_max_size(struct sshbuf *buf, size_t max_size)
218a0ee8cc6SDag-Erling Smørgrav {
219a0ee8cc6SDag-Erling Smørgrav 	size_t rlen;
220a0ee8cc6SDag-Erling Smørgrav 	u_char *dp;
221a0ee8cc6SDag-Erling Smørgrav 	int r;
222a0ee8cc6SDag-Erling Smørgrav 
223a0ee8cc6SDag-Erling Smørgrav 	SSHBUF_DBG(("set max buf = %p len = %zu", buf, max_size));
224a0ee8cc6SDag-Erling Smørgrav 	if ((r = sshbuf_check_sanity(buf)) != 0)
225a0ee8cc6SDag-Erling Smørgrav 		return r;
226a0ee8cc6SDag-Erling Smørgrav 	if (max_size == buf->max_size)
227a0ee8cc6SDag-Erling Smørgrav 		return 0;
228a0ee8cc6SDag-Erling Smørgrav 	if (buf->readonly || buf->refcount > 1)
229a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_BUFFER_READ_ONLY;
230a0ee8cc6SDag-Erling Smørgrav 	if (max_size > SSHBUF_SIZE_MAX)
231a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_NO_BUFFER_SPACE;
232a0ee8cc6SDag-Erling Smørgrav 	/* pack and realloc if necessary */
233a0ee8cc6SDag-Erling Smørgrav 	sshbuf_maybe_pack(buf, max_size < buf->size);
234a0ee8cc6SDag-Erling Smørgrav 	if (max_size < buf->alloc && max_size > buf->size) {
235a0ee8cc6SDag-Erling Smørgrav 		if (buf->size < SSHBUF_SIZE_INIT)
236a0ee8cc6SDag-Erling Smørgrav 			rlen = SSHBUF_SIZE_INIT;
237a0ee8cc6SDag-Erling Smørgrav 		else
238ca86bcf2SDag-Erling Smørgrav 			rlen = ROUNDUP(buf->size, SSHBUF_SIZE_INC);
239a0ee8cc6SDag-Erling Smørgrav 		if (rlen > max_size)
240a0ee8cc6SDag-Erling Smørgrav 			rlen = max_size;
241a0ee8cc6SDag-Erling Smørgrav 		SSHBUF_DBG(("new alloc = %zu", rlen));
2424f52dfbbSDag-Erling Smørgrav 		if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL)
243a0ee8cc6SDag-Erling Smørgrav 			return SSH_ERR_ALLOC_FAIL;
244a0ee8cc6SDag-Erling Smørgrav 		buf->cd = buf->d = dp;
245a0ee8cc6SDag-Erling Smørgrav 		buf->alloc = rlen;
246a0ee8cc6SDag-Erling Smørgrav 	}
247a0ee8cc6SDag-Erling Smørgrav 	SSHBUF_TELL("new-max");
248a0ee8cc6SDag-Erling Smørgrav 	if (max_size < buf->alloc)
249a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_NO_BUFFER_SPACE;
250a0ee8cc6SDag-Erling Smørgrav 	buf->max_size = max_size;
251a0ee8cc6SDag-Erling Smørgrav 	return 0;
252a0ee8cc6SDag-Erling Smørgrav }
253a0ee8cc6SDag-Erling Smørgrav 
254a0ee8cc6SDag-Erling Smørgrav size_t
255a0ee8cc6SDag-Erling Smørgrav sshbuf_len(const struct sshbuf *buf)
256a0ee8cc6SDag-Erling Smørgrav {
257a0ee8cc6SDag-Erling Smørgrav 	if (sshbuf_check_sanity(buf) != 0)
258a0ee8cc6SDag-Erling Smørgrav 		return 0;
259a0ee8cc6SDag-Erling Smørgrav 	return buf->size - buf->off;
260a0ee8cc6SDag-Erling Smørgrav }
261a0ee8cc6SDag-Erling Smørgrav 
262a0ee8cc6SDag-Erling Smørgrav size_t
263a0ee8cc6SDag-Erling Smørgrav sshbuf_avail(const struct sshbuf *buf)
264a0ee8cc6SDag-Erling Smørgrav {
265a0ee8cc6SDag-Erling Smørgrav 	if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1)
266a0ee8cc6SDag-Erling Smørgrav 		return 0;
267a0ee8cc6SDag-Erling Smørgrav 	return buf->max_size - (buf->size - buf->off);
268a0ee8cc6SDag-Erling Smørgrav }
269a0ee8cc6SDag-Erling Smørgrav 
270a0ee8cc6SDag-Erling Smørgrav const u_char *
271a0ee8cc6SDag-Erling Smørgrav sshbuf_ptr(const struct sshbuf *buf)
272a0ee8cc6SDag-Erling Smørgrav {
273a0ee8cc6SDag-Erling Smørgrav 	if (sshbuf_check_sanity(buf) != 0)
274a0ee8cc6SDag-Erling Smørgrav 		return NULL;
275a0ee8cc6SDag-Erling Smørgrav 	return buf->cd + buf->off;
276a0ee8cc6SDag-Erling Smørgrav }
277a0ee8cc6SDag-Erling Smørgrav 
278a0ee8cc6SDag-Erling Smørgrav u_char *
279a0ee8cc6SDag-Erling Smørgrav sshbuf_mutable_ptr(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 NULL;
283a0ee8cc6SDag-Erling Smørgrav 	return buf->d + buf->off;
284a0ee8cc6SDag-Erling Smørgrav }
285a0ee8cc6SDag-Erling Smørgrav 
286a0ee8cc6SDag-Erling Smørgrav int
287a0ee8cc6SDag-Erling Smørgrav sshbuf_check_reserve(const struct sshbuf *buf, size_t len)
288a0ee8cc6SDag-Erling Smørgrav {
289a0ee8cc6SDag-Erling Smørgrav 	int r;
290a0ee8cc6SDag-Erling Smørgrav 
291a0ee8cc6SDag-Erling Smørgrav 	if ((r = sshbuf_check_sanity(buf)) != 0)
292a0ee8cc6SDag-Erling Smørgrav 		return r;
293a0ee8cc6SDag-Erling Smørgrav 	if (buf->readonly || buf->refcount > 1)
294a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_BUFFER_READ_ONLY;
295a0ee8cc6SDag-Erling Smørgrav 	SSHBUF_TELL("check");
296a0ee8cc6SDag-Erling Smørgrav 	/* Check that len is reasonable and that max_size + available < len */
297a0ee8cc6SDag-Erling Smørgrav 	if (len > buf->max_size || buf->max_size - len < buf->size - buf->off)
298a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_NO_BUFFER_SPACE;
299a0ee8cc6SDag-Erling Smørgrav 	return 0;
300a0ee8cc6SDag-Erling Smørgrav }
301a0ee8cc6SDag-Erling Smørgrav 
302a0ee8cc6SDag-Erling Smørgrav int
303ca86bcf2SDag-Erling Smørgrav sshbuf_allocate(struct sshbuf *buf, size_t len)
304a0ee8cc6SDag-Erling Smørgrav {
305a0ee8cc6SDag-Erling Smørgrav 	size_t rlen, need;
306a0ee8cc6SDag-Erling Smørgrav 	u_char *dp;
307a0ee8cc6SDag-Erling Smørgrav 	int r;
308a0ee8cc6SDag-Erling Smørgrav 
309ca86bcf2SDag-Erling Smørgrav 	SSHBUF_DBG(("allocate buf = %p len = %zu", buf, len));
310a0ee8cc6SDag-Erling Smørgrav 	if ((r = sshbuf_check_reserve(buf, len)) != 0)
311a0ee8cc6SDag-Erling Smørgrav 		return r;
312a0ee8cc6SDag-Erling Smørgrav 	/*
313a0ee8cc6SDag-Erling Smørgrav 	 * If the requested allocation appended would push us past max_size
314a0ee8cc6SDag-Erling Smørgrav 	 * then pack the buffer, zeroing buf->off.
315a0ee8cc6SDag-Erling Smørgrav 	 */
316a0ee8cc6SDag-Erling Smørgrav 	sshbuf_maybe_pack(buf, buf->size + len > buf->max_size);
317ca86bcf2SDag-Erling Smørgrav 	SSHBUF_TELL("allocate");
318ca86bcf2SDag-Erling Smørgrav 	if (len + buf->size <= buf->alloc)
319ca86bcf2SDag-Erling Smørgrav 		return 0; /* already have it. */
320ca86bcf2SDag-Erling Smørgrav 
321a0ee8cc6SDag-Erling Smørgrav 	/*
322a0ee8cc6SDag-Erling Smørgrav 	 * Prefer to alloc in SSHBUF_SIZE_INC units, but
323a0ee8cc6SDag-Erling Smørgrav 	 * allocate less if doing so would overflow max_size.
324a0ee8cc6SDag-Erling Smørgrav 	 */
325a0ee8cc6SDag-Erling Smørgrav 	need = len + buf->size - buf->alloc;
326ca86bcf2SDag-Erling Smørgrav 	rlen = ROUNDUP(buf->alloc + need, SSHBUF_SIZE_INC);
327a0ee8cc6SDag-Erling Smørgrav 	SSHBUF_DBG(("need %zu initial rlen %zu", need, rlen));
328a0ee8cc6SDag-Erling Smørgrav 	if (rlen > buf->max_size)
329a0ee8cc6SDag-Erling Smørgrav 		rlen = buf->alloc + need;
330a0ee8cc6SDag-Erling Smørgrav 	SSHBUF_DBG(("adjusted rlen %zu", rlen));
3314f52dfbbSDag-Erling Smørgrav 	if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL) {
332a0ee8cc6SDag-Erling Smørgrav 		SSHBUF_DBG(("realloc fail"));
333a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_ALLOC_FAIL;
334a0ee8cc6SDag-Erling Smørgrav 	}
335a0ee8cc6SDag-Erling Smørgrav 	buf->alloc = rlen;
336a0ee8cc6SDag-Erling Smørgrav 	buf->cd = buf->d = dp;
337a0ee8cc6SDag-Erling Smørgrav 	if ((r = sshbuf_check_reserve(buf, len)) < 0) {
338a0ee8cc6SDag-Erling Smørgrav 		/* shouldn't fail */
339a0ee8cc6SDag-Erling Smørgrav 		return r;
340a0ee8cc6SDag-Erling Smørgrav 	}
341ca86bcf2SDag-Erling Smørgrav 	SSHBUF_TELL("done");
342ca86bcf2SDag-Erling Smørgrav 	return 0;
343a0ee8cc6SDag-Erling Smørgrav }
344ca86bcf2SDag-Erling Smørgrav 
345ca86bcf2SDag-Erling Smørgrav int
346ca86bcf2SDag-Erling Smørgrav sshbuf_reserve(struct sshbuf *buf, size_t len, u_char **dpp)
347ca86bcf2SDag-Erling Smørgrav {
348ca86bcf2SDag-Erling Smørgrav 	u_char *dp;
349ca86bcf2SDag-Erling Smørgrav 	int r;
350ca86bcf2SDag-Erling Smørgrav 
351ca86bcf2SDag-Erling Smørgrav 	if (dpp != NULL)
352ca86bcf2SDag-Erling Smørgrav 		*dpp = NULL;
353ca86bcf2SDag-Erling Smørgrav 
354ca86bcf2SDag-Erling Smørgrav 	SSHBUF_DBG(("reserve buf = %p len = %zu", buf, len));
355ca86bcf2SDag-Erling Smørgrav 	if ((r = sshbuf_allocate(buf, len)) != 0)
356ca86bcf2SDag-Erling Smørgrav 		return r;
357ca86bcf2SDag-Erling Smørgrav 
358a0ee8cc6SDag-Erling Smørgrav 	dp = buf->d + buf->size;
359a0ee8cc6SDag-Erling Smørgrav 	buf->size += len;
360a0ee8cc6SDag-Erling Smørgrav 	if (dpp != NULL)
361a0ee8cc6SDag-Erling Smørgrav 		*dpp = dp;
362a0ee8cc6SDag-Erling Smørgrav 	return 0;
363a0ee8cc6SDag-Erling Smørgrav }
364a0ee8cc6SDag-Erling Smørgrav 
365a0ee8cc6SDag-Erling Smørgrav int
366a0ee8cc6SDag-Erling Smørgrav sshbuf_consume(struct sshbuf *buf, size_t len)
367a0ee8cc6SDag-Erling Smørgrav {
368a0ee8cc6SDag-Erling Smørgrav 	int r;
369a0ee8cc6SDag-Erling Smørgrav 
370a0ee8cc6SDag-Erling Smørgrav 	SSHBUF_DBG(("len = %zu", len));
371a0ee8cc6SDag-Erling Smørgrav 	if ((r = sshbuf_check_sanity(buf)) != 0)
372a0ee8cc6SDag-Erling Smørgrav 		return r;
373a0ee8cc6SDag-Erling Smørgrav 	if (len == 0)
374a0ee8cc6SDag-Erling Smørgrav 		return 0;
375a0ee8cc6SDag-Erling Smørgrav 	if (len > sshbuf_len(buf))
376a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_MESSAGE_INCOMPLETE;
377a0ee8cc6SDag-Erling Smørgrav 	buf->off += len;
3784f52dfbbSDag-Erling Smørgrav 	/* deal with empty buffer */
3794f52dfbbSDag-Erling Smørgrav 	if (buf->off == buf->size)
3804f52dfbbSDag-Erling Smørgrav 		buf->off = buf->size = 0;
381a0ee8cc6SDag-Erling Smørgrav 	SSHBUF_TELL("done");
382a0ee8cc6SDag-Erling Smørgrav 	return 0;
383a0ee8cc6SDag-Erling Smørgrav }
384a0ee8cc6SDag-Erling Smørgrav 
385a0ee8cc6SDag-Erling Smørgrav int
386a0ee8cc6SDag-Erling Smørgrav sshbuf_consume_end(struct sshbuf *buf, size_t len)
387a0ee8cc6SDag-Erling Smørgrav {
388a0ee8cc6SDag-Erling Smørgrav 	int r;
389a0ee8cc6SDag-Erling Smørgrav 
390a0ee8cc6SDag-Erling Smørgrav 	SSHBUF_DBG(("len = %zu", len));
391a0ee8cc6SDag-Erling Smørgrav 	if ((r = sshbuf_check_sanity(buf)) != 0)
392a0ee8cc6SDag-Erling Smørgrav 		return r;
393a0ee8cc6SDag-Erling Smørgrav 	if (len == 0)
394a0ee8cc6SDag-Erling Smørgrav 		return 0;
395a0ee8cc6SDag-Erling Smørgrav 	if (len > sshbuf_len(buf))
396a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_MESSAGE_INCOMPLETE;
397a0ee8cc6SDag-Erling Smørgrav 	buf->size -= len;
398a0ee8cc6SDag-Erling Smørgrav 	SSHBUF_TELL("done");
399a0ee8cc6SDag-Erling Smørgrav 	return 0;
400a0ee8cc6SDag-Erling Smørgrav }
401a0ee8cc6SDag-Erling Smørgrav 
402