1*3d9fd9fcSEd Maste /* $OpenBSD: sshbuf.c,v 1.23 2024/08/14 15:42:18 tobias 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 #include "includes.h"
19a0ee8cc6SDag-Erling Smørgrav
20a0ee8cc6SDag-Erling Smørgrav #include <sys/types.h>
21a0ee8cc6SDag-Erling Smørgrav #include <signal.h>
22a0ee8cc6SDag-Erling Smørgrav #include <stdlib.h>
23a0ee8cc6SDag-Erling Smørgrav #include <stdio.h>
24a0ee8cc6SDag-Erling Smørgrav #include <string.h>
25a0ee8cc6SDag-Erling Smørgrav
26a0ee8cc6SDag-Erling Smørgrav #include "ssherr.h"
27f374ba41SEd Maste #define SSHBUF_INTERNAL
28a0ee8cc6SDag-Erling Smørgrav #include "sshbuf.h"
29ca86bcf2SDag-Erling Smørgrav #include "misc.h"
30a0ee8cc6SDag-Erling Smørgrav
31f374ba41SEd Maste #ifdef SSHBUF_DEBUG
32f374ba41SEd Maste # define SSHBUF_TELL(what) do { \
33f374ba41SEd Maste printf("%s:%d %s: %s size %zu alloc %zu off %zu max %zu\n", \
34f374ba41SEd Maste __FILE__, __LINE__, __func__, what, \
35f374ba41SEd Maste buf->size, buf->alloc, buf->off, buf->max_size); \
36f374ba41SEd Maste fflush(stdout); \
37f374ba41SEd Maste } while (0)
38f374ba41SEd Maste #else
39f374ba41SEd Maste # define SSHBUF_TELL(what)
40f374ba41SEd Maste #endif
41f374ba41SEd Maste
42f374ba41SEd Maste struct sshbuf {
43f374ba41SEd Maste u_char *d; /* Data */
44f374ba41SEd Maste const u_char *cd; /* Const data */
45f374ba41SEd Maste size_t off; /* First available byte is buf->d + buf->off */
46f374ba41SEd Maste size_t size; /* Last byte is buf->d + buf->size - 1 */
47f374ba41SEd Maste size_t max_size; /* Maximum size of buffer */
48f374ba41SEd Maste size_t alloc; /* Total bytes allocated to buf->d */
49f374ba41SEd Maste int readonly; /* Refers to external, const data */
50f374ba41SEd Maste u_int refcount; /* Tracks self and number of child buffers */
51f374ba41SEd Maste struct sshbuf *parent; /* If child, pointer to parent */
52f374ba41SEd Maste };
53f374ba41SEd Maste
54a0ee8cc6SDag-Erling Smørgrav static inline int
sshbuf_check_sanity(const struct sshbuf * buf)55a0ee8cc6SDag-Erling Smørgrav sshbuf_check_sanity(const struct sshbuf *buf)
56a0ee8cc6SDag-Erling Smørgrav {
57a0ee8cc6SDag-Erling Smørgrav SSHBUF_TELL("sanity");
58a0ee8cc6SDag-Erling Smørgrav if (__predict_false(buf == NULL ||
59a0ee8cc6SDag-Erling Smørgrav (!buf->readonly && buf->d != buf->cd) ||
60*3d9fd9fcSEd Maste buf->parent == buf ||
61a0ee8cc6SDag-Erling Smørgrav buf->refcount < 1 || buf->refcount > SSHBUF_REFS_MAX ||
62a0ee8cc6SDag-Erling Smørgrav buf->cd == NULL ||
63a0ee8cc6SDag-Erling Smørgrav buf->max_size > SSHBUF_SIZE_MAX ||
64a0ee8cc6SDag-Erling Smørgrav buf->alloc > buf->max_size ||
65a0ee8cc6SDag-Erling Smørgrav buf->size > buf->alloc ||
66a0ee8cc6SDag-Erling Smørgrav buf->off > buf->size)) {
67a0ee8cc6SDag-Erling Smørgrav /* Do not try to recover from corrupted buffer internals */
68a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR"));
6919261079SEd Maste ssh_signal(SIGSEGV, SIG_DFL);
70a0ee8cc6SDag-Erling Smørgrav raise(SIGSEGV);
71a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR;
72a0ee8cc6SDag-Erling Smørgrav }
73a0ee8cc6SDag-Erling Smørgrav return 0;
74a0ee8cc6SDag-Erling Smørgrav }
75a0ee8cc6SDag-Erling Smørgrav
76a0ee8cc6SDag-Erling Smørgrav static void
sshbuf_maybe_pack(struct sshbuf * buf,int force)77a0ee8cc6SDag-Erling Smørgrav sshbuf_maybe_pack(struct sshbuf *buf, int force)
78a0ee8cc6SDag-Erling Smørgrav {
79a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("force %d", force));
80a0ee8cc6SDag-Erling Smørgrav SSHBUF_TELL("pre-pack");
81a0ee8cc6SDag-Erling Smørgrav if (buf->off == 0 || buf->readonly || buf->refcount > 1)
82a0ee8cc6SDag-Erling Smørgrav return;
83a0ee8cc6SDag-Erling Smørgrav if (force ||
84a0ee8cc6SDag-Erling Smørgrav (buf->off >= SSHBUF_PACK_MIN && buf->off >= buf->size / 2)) {
85a0ee8cc6SDag-Erling Smørgrav memmove(buf->d, buf->d + buf->off, buf->size - buf->off);
86a0ee8cc6SDag-Erling Smørgrav buf->size -= buf->off;
87a0ee8cc6SDag-Erling Smørgrav buf->off = 0;
88a0ee8cc6SDag-Erling Smørgrav SSHBUF_TELL("packed");
89a0ee8cc6SDag-Erling Smørgrav }
90a0ee8cc6SDag-Erling Smørgrav }
91a0ee8cc6SDag-Erling Smørgrav
92a0ee8cc6SDag-Erling Smørgrav struct sshbuf *
sshbuf_new(void)93a0ee8cc6SDag-Erling Smørgrav sshbuf_new(void)
94a0ee8cc6SDag-Erling Smørgrav {
95a0ee8cc6SDag-Erling Smørgrav struct sshbuf *ret;
96a0ee8cc6SDag-Erling Smørgrav
97*3d9fd9fcSEd Maste if ((ret = calloc(1, sizeof(*ret))) == NULL)
98a0ee8cc6SDag-Erling Smørgrav return NULL;
99a0ee8cc6SDag-Erling Smørgrav ret->alloc = SSHBUF_SIZE_INIT;
100a0ee8cc6SDag-Erling Smørgrav ret->max_size = SSHBUF_SIZE_MAX;
101a0ee8cc6SDag-Erling Smørgrav ret->readonly = 0;
102a0ee8cc6SDag-Erling Smørgrav ret->refcount = 1;
103a0ee8cc6SDag-Erling Smørgrav ret->parent = NULL;
104a0ee8cc6SDag-Erling Smørgrav if ((ret->cd = ret->d = calloc(1, ret->alloc)) == NULL) {
105a0ee8cc6SDag-Erling Smørgrav free(ret);
106a0ee8cc6SDag-Erling Smørgrav return NULL;
107a0ee8cc6SDag-Erling Smørgrav }
108a0ee8cc6SDag-Erling Smørgrav return ret;
109a0ee8cc6SDag-Erling Smørgrav }
110a0ee8cc6SDag-Erling Smørgrav
111a0ee8cc6SDag-Erling Smørgrav struct sshbuf *
sshbuf_from(const void * blob,size_t len)112a0ee8cc6SDag-Erling Smørgrav sshbuf_from(const void *blob, size_t len)
113a0ee8cc6SDag-Erling Smørgrav {
114a0ee8cc6SDag-Erling Smørgrav struct sshbuf *ret;
115a0ee8cc6SDag-Erling Smørgrav
116a0ee8cc6SDag-Erling Smørgrav if (blob == NULL || len > SSHBUF_SIZE_MAX ||
117*3d9fd9fcSEd Maste (ret = calloc(1, sizeof(*ret))) == NULL)
118a0ee8cc6SDag-Erling Smørgrav return NULL;
119a0ee8cc6SDag-Erling Smørgrav ret->alloc = ret->size = ret->max_size = len;
120a0ee8cc6SDag-Erling Smørgrav ret->readonly = 1;
121a0ee8cc6SDag-Erling Smørgrav ret->refcount = 1;
122a0ee8cc6SDag-Erling Smørgrav ret->parent = NULL;
123a0ee8cc6SDag-Erling Smørgrav ret->cd = blob;
124a0ee8cc6SDag-Erling Smørgrav ret->d = NULL;
125a0ee8cc6SDag-Erling Smørgrav return ret;
126a0ee8cc6SDag-Erling Smørgrav }
127a0ee8cc6SDag-Erling Smørgrav
128a0ee8cc6SDag-Erling Smørgrav int
sshbuf_set_parent(struct sshbuf * child,struct sshbuf * parent)129a0ee8cc6SDag-Erling Smørgrav sshbuf_set_parent(struct sshbuf *child, struct sshbuf *parent)
130a0ee8cc6SDag-Erling Smørgrav {
131a0ee8cc6SDag-Erling Smørgrav int r;
132a0ee8cc6SDag-Erling Smørgrav
133a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_sanity(child)) != 0 ||
134a0ee8cc6SDag-Erling Smørgrav (r = sshbuf_check_sanity(parent)) != 0)
135a0ee8cc6SDag-Erling Smørgrav return r;
136*3d9fd9fcSEd Maste if ((child->parent != NULL && child->parent != parent) ||
137*3d9fd9fcSEd Maste child == parent)
13838a52bd3SEd Maste return SSH_ERR_INTERNAL_ERROR;
139a0ee8cc6SDag-Erling Smørgrav child->parent = parent;
140a0ee8cc6SDag-Erling Smørgrav child->parent->refcount++;
141a0ee8cc6SDag-Erling Smørgrav return 0;
142a0ee8cc6SDag-Erling Smørgrav }
143a0ee8cc6SDag-Erling Smørgrav
144a0ee8cc6SDag-Erling Smørgrav struct sshbuf *
sshbuf_fromb(struct sshbuf * buf)145a0ee8cc6SDag-Erling Smørgrav sshbuf_fromb(struct sshbuf *buf)
146a0ee8cc6SDag-Erling Smørgrav {
147a0ee8cc6SDag-Erling Smørgrav struct sshbuf *ret;
148a0ee8cc6SDag-Erling Smørgrav
149a0ee8cc6SDag-Erling Smørgrav if (sshbuf_check_sanity(buf) != 0)
150a0ee8cc6SDag-Erling Smørgrav return NULL;
151a0ee8cc6SDag-Erling Smørgrav if ((ret = sshbuf_from(sshbuf_ptr(buf), sshbuf_len(buf))) == NULL)
152a0ee8cc6SDag-Erling Smørgrav return NULL;
153a0ee8cc6SDag-Erling Smørgrav if (sshbuf_set_parent(ret, buf) != 0) {
154a0ee8cc6SDag-Erling Smørgrav sshbuf_free(ret);
155a0ee8cc6SDag-Erling Smørgrav return NULL;
156a0ee8cc6SDag-Erling Smørgrav }
157a0ee8cc6SDag-Erling Smørgrav return ret;
158a0ee8cc6SDag-Erling Smørgrav }
159a0ee8cc6SDag-Erling Smørgrav
160a0ee8cc6SDag-Erling Smørgrav void
sshbuf_free(struct sshbuf * buf)161a0ee8cc6SDag-Erling Smørgrav sshbuf_free(struct sshbuf *buf)
162a0ee8cc6SDag-Erling Smørgrav {
163a0ee8cc6SDag-Erling Smørgrav if (buf == NULL)
164a0ee8cc6SDag-Erling Smørgrav return;
165a0ee8cc6SDag-Erling Smørgrav /*
166a0ee8cc6SDag-Erling Smørgrav * The following will leak on insane buffers, but this is the safest
167a0ee8cc6SDag-Erling Smørgrav * course of action - an invalid pointer or already-freed pointer may
168a0ee8cc6SDag-Erling Smørgrav * have been passed to us and continuing to scribble over memory would
169a0ee8cc6SDag-Erling Smørgrav * be bad.
170a0ee8cc6SDag-Erling Smørgrav */
171a0ee8cc6SDag-Erling Smørgrav if (sshbuf_check_sanity(buf) != 0)
172a0ee8cc6SDag-Erling Smørgrav return;
17319261079SEd Maste
174a0ee8cc6SDag-Erling Smørgrav /*
175a0ee8cc6SDag-Erling Smørgrav * If we are a parent with still-extant children, then don't free just
176a0ee8cc6SDag-Erling Smørgrav * yet. The last child's call to sshbuf_free should decrement our
177a0ee8cc6SDag-Erling Smørgrav * refcount to 0 and trigger the actual free.
178a0ee8cc6SDag-Erling Smørgrav */
179a0ee8cc6SDag-Erling Smørgrav buf->refcount--;
180a0ee8cc6SDag-Erling Smørgrav if (buf->refcount > 0)
181a0ee8cc6SDag-Erling Smørgrav return;
18219261079SEd Maste
18319261079SEd Maste /*
184*3d9fd9fcSEd Maste * If we are a child, then free our parent to decrement its reference
18519261079SEd Maste * count and possibly free it.
18619261079SEd Maste */
18719261079SEd Maste sshbuf_free(buf->parent);
18819261079SEd Maste buf->parent = NULL;
18919261079SEd Maste
190*3d9fd9fcSEd Maste if (!buf->readonly)
191*3d9fd9fcSEd Maste freezero(buf->d, buf->alloc);
19219261079SEd Maste freezero(buf, sizeof(*buf));
193a0ee8cc6SDag-Erling Smørgrav }
194a0ee8cc6SDag-Erling Smørgrav
195a0ee8cc6SDag-Erling Smørgrav void
sshbuf_reset(struct sshbuf * buf)196a0ee8cc6SDag-Erling Smørgrav sshbuf_reset(struct sshbuf *buf)
197a0ee8cc6SDag-Erling Smørgrav {
198a0ee8cc6SDag-Erling Smørgrav u_char *d;
199a0ee8cc6SDag-Erling Smørgrav
200a0ee8cc6SDag-Erling Smørgrav if (buf->readonly || buf->refcount > 1) {
201a0ee8cc6SDag-Erling Smørgrav /* Nonsensical. Just make buffer appear empty */
202a0ee8cc6SDag-Erling Smørgrav buf->off = buf->size;
203a0ee8cc6SDag-Erling Smørgrav return;
204a0ee8cc6SDag-Erling Smørgrav }
20538a52bd3SEd Maste if (sshbuf_check_sanity(buf) != 0)
20638a52bd3SEd Maste return;
207a0ee8cc6SDag-Erling Smørgrav buf->off = buf->size = 0;
208a0ee8cc6SDag-Erling Smørgrav if (buf->alloc != SSHBUF_SIZE_INIT) {
2094f52dfbbSDag-Erling Smørgrav if ((d = recallocarray(buf->d, buf->alloc, SSHBUF_SIZE_INIT,
2104f52dfbbSDag-Erling Smørgrav 1)) != NULL) {
211a0ee8cc6SDag-Erling Smørgrav buf->cd = buf->d = d;
212a0ee8cc6SDag-Erling Smørgrav buf->alloc = SSHBUF_SIZE_INIT;
213a0ee8cc6SDag-Erling Smørgrav }
214a0ee8cc6SDag-Erling Smørgrav }
21538a52bd3SEd Maste explicit_bzero(buf->d, buf->alloc);
216a0ee8cc6SDag-Erling Smørgrav }
217a0ee8cc6SDag-Erling Smørgrav
218a0ee8cc6SDag-Erling Smørgrav size_t
sshbuf_max_size(const struct sshbuf * buf)219a0ee8cc6SDag-Erling Smørgrav sshbuf_max_size(const struct sshbuf *buf)
220a0ee8cc6SDag-Erling Smørgrav {
221a0ee8cc6SDag-Erling Smørgrav return buf->max_size;
222a0ee8cc6SDag-Erling Smørgrav }
223a0ee8cc6SDag-Erling Smørgrav
224a0ee8cc6SDag-Erling Smørgrav size_t
sshbuf_alloc(const struct sshbuf * buf)225a0ee8cc6SDag-Erling Smørgrav sshbuf_alloc(const struct sshbuf *buf)
226a0ee8cc6SDag-Erling Smørgrav {
227a0ee8cc6SDag-Erling Smørgrav return buf->alloc;
228a0ee8cc6SDag-Erling Smørgrav }
229a0ee8cc6SDag-Erling Smørgrav
230a0ee8cc6SDag-Erling Smørgrav const struct sshbuf *
sshbuf_parent(const struct sshbuf * buf)231a0ee8cc6SDag-Erling Smørgrav sshbuf_parent(const struct sshbuf *buf)
232a0ee8cc6SDag-Erling Smørgrav {
233a0ee8cc6SDag-Erling Smørgrav return buf->parent;
234a0ee8cc6SDag-Erling Smørgrav }
235a0ee8cc6SDag-Erling Smørgrav
236a0ee8cc6SDag-Erling Smørgrav u_int
sshbuf_refcount(const struct sshbuf * buf)237a0ee8cc6SDag-Erling Smørgrav sshbuf_refcount(const struct sshbuf *buf)
238a0ee8cc6SDag-Erling Smørgrav {
239a0ee8cc6SDag-Erling Smørgrav return buf->refcount;
240a0ee8cc6SDag-Erling Smørgrav }
241a0ee8cc6SDag-Erling Smørgrav
242a0ee8cc6SDag-Erling Smørgrav int
sshbuf_set_max_size(struct sshbuf * buf,size_t max_size)243a0ee8cc6SDag-Erling Smørgrav sshbuf_set_max_size(struct sshbuf *buf, size_t max_size)
244a0ee8cc6SDag-Erling Smørgrav {
245a0ee8cc6SDag-Erling Smørgrav size_t rlen;
246a0ee8cc6SDag-Erling Smørgrav u_char *dp;
247a0ee8cc6SDag-Erling Smørgrav int r;
248a0ee8cc6SDag-Erling Smørgrav
249a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("set max buf = %p len = %zu", buf, max_size));
250a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_sanity(buf)) != 0)
251a0ee8cc6SDag-Erling Smørgrav return r;
252a0ee8cc6SDag-Erling Smørgrav if (max_size == buf->max_size)
253a0ee8cc6SDag-Erling Smørgrav return 0;
254a0ee8cc6SDag-Erling Smørgrav if (buf->readonly || buf->refcount > 1)
255a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_BUFFER_READ_ONLY;
256a0ee8cc6SDag-Erling Smørgrav if (max_size > SSHBUF_SIZE_MAX)
257a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_NO_BUFFER_SPACE;
258a0ee8cc6SDag-Erling Smørgrav /* pack and realloc if necessary */
259a0ee8cc6SDag-Erling Smørgrav sshbuf_maybe_pack(buf, max_size < buf->size);
260a0ee8cc6SDag-Erling Smørgrav if (max_size < buf->alloc && max_size > buf->size) {
261a0ee8cc6SDag-Erling Smørgrav if (buf->size < SSHBUF_SIZE_INIT)
262a0ee8cc6SDag-Erling Smørgrav rlen = SSHBUF_SIZE_INIT;
263a0ee8cc6SDag-Erling Smørgrav else
264ca86bcf2SDag-Erling Smørgrav rlen = ROUNDUP(buf->size, SSHBUF_SIZE_INC);
265a0ee8cc6SDag-Erling Smørgrav if (rlen > max_size)
266a0ee8cc6SDag-Erling Smørgrav rlen = max_size;
267a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("new alloc = %zu", rlen));
2684f52dfbbSDag-Erling Smørgrav if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL)
269a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL;
270a0ee8cc6SDag-Erling Smørgrav buf->cd = buf->d = dp;
271a0ee8cc6SDag-Erling Smørgrav buf->alloc = rlen;
272a0ee8cc6SDag-Erling Smørgrav }
273a0ee8cc6SDag-Erling Smørgrav SSHBUF_TELL("new-max");
274a0ee8cc6SDag-Erling Smørgrav if (max_size < buf->alloc)
275a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_NO_BUFFER_SPACE;
276a0ee8cc6SDag-Erling Smørgrav buf->max_size = max_size;
277a0ee8cc6SDag-Erling Smørgrav return 0;
278a0ee8cc6SDag-Erling Smørgrav }
279a0ee8cc6SDag-Erling Smørgrav
280a0ee8cc6SDag-Erling Smørgrav size_t
sshbuf_len(const struct sshbuf * buf)281a0ee8cc6SDag-Erling Smørgrav sshbuf_len(const struct sshbuf *buf)
282a0ee8cc6SDag-Erling Smørgrav {
283a0ee8cc6SDag-Erling Smørgrav if (sshbuf_check_sanity(buf) != 0)
284a0ee8cc6SDag-Erling Smørgrav return 0;
285a0ee8cc6SDag-Erling Smørgrav return buf->size - buf->off;
286a0ee8cc6SDag-Erling Smørgrav }
287a0ee8cc6SDag-Erling Smørgrav
288a0ee8cc6SDag-Erling Smørgrav size_t
sshbuf_avail(const struct sshbuf * buf)289a0ee8cc6SDag-Erling Smørgrav sshbuf_avail(const struct sshbuf *buf)
290a0ee8cc6SDag-Erling Smørgrav {
291a0ee8cc6SDag-Erling Smørgrav if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1)
292a0ee8cc6SDag-Erling Smørgrav return 0;
293a0ee8cc6SDag-Erling Smørgrav return buf->max_size - (buf->size - buf->off);
294a0ee8cc6SDag-Erling Smørgrav }
295a0ee8cc6SDag-Erling Smørgrav
296a0ee8cc6SDag-Erling Smørgrav const u_char *
sshbuf_ptr(const struct sshbuf * buf)297a0ee8cc6SDag-Erling Smørgrav sshbuf_ptr(const struct sshbuf *buf)
298a0ee8cc6SDag-Erling Smørgrav {
299a0ee8cc6SDag-Erling Smørgrav if (sshbuf_check_sanity(buf) != 0)
300a0ee8cc6SDag-Erling Smørgrav return NULL;
301a0ee8cc6SDag-Erling Smørgrav return buf->cd + buf->off;
302a0ee8cc6SDag-Erling Smørgrav }
303a0ee8cc6SDag-Erling Smørgrav
304a0ee8cc6SDag-Erling Smørgrav u_char *
sshbuf_mutable_ptr(const struct sshbuf * buf)305a0ee8cc6SDag-Erling Smørgrav sshbuf_mutable_ptr(const struct sshbuf *buf)
306a0ee8cc6SDag-Erling Smørgrav {
307a0ee8cc6SDag-Erling Smørgrav if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1)
308a0ee8cc6SDag-Erling Smørgrav return NULL;
309a0ee8cc6SDag-Erling Smørgrav return buf->d + buf->off;
310a0ee8cc6SDag-Erling Smørgrav }
311a0ee8cc6SDag-Erling Smørgrav
312a0ee8cc6SDag-Erling Smørgrav int
sshbuf_check_reserve(const struct sshbuf * buf,size_t len)313a0ee8cc6SDag-Erling Smørgrav sshbuf_check_reserve(const struct sshbuf *buf, size_t len)
314a0ee8cc6SDag-Erling Smørgrav {
315a0ee8cc6SDag-Erling Smørgrav int r;
316a0ee8cc6SDag-Erling Smørgrav
317a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_sanity(buf)) != 0)
318a0ee8cc6SDag-Erling Smørgrav return r;
319a0ee8cc6SDag-Erling Smørgrav if (buf->readonly || buf->refcount > 1)
320a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_BUFFER_READ_ONLY;
321a0ee8cc6SDag-Erling Smørgrav SSHBUF_TELL("check");
322a0ee8cc6SDag-Erling Smørgrav /* Check that len is reasonable and that max_size + available < len */
323a0ee8cc6SDag-Erling Smørgrav if (len > buf->max_size || buf->max_size - len < buf->size - buf->off)
324a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_NO_BUFFER_SPACE;
325a0ee8cc6SDag-Erling Smørgrav return 0;
326a0ee8cc6SDag-Erling Smørgrav }
327a0ee8cc6SDag-Erling Smørgrav
328a0ee8cc6SDag-Erling Smørgrav int
sshbuf_allocate(struct sshbuf * buf,size_t len)329ca86bcf2SDag-Erling Smørgrav sshbuf_allocate(struct sshbuf *buf, size_t len)
330a0ee8cc6SDag-Erling Smørgrav {
331a0ee8cc6SDag-Erling Smørgrav size_t rlen, need;
332a0ee8cc6SDag-Erling Smørgrav u_char *dp;
333a0ee8cc6SDag-Erling Smørgrav int r;
334a0ee8cc6SDag-Erling Smørgrav
335ca86bcf2SDag-Erling Smørgrav SSHBUF_DBG(("allocate buf = %p len = %zu", buf, len));
336a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_reserve(buf, len)) != 0)
337a0ee8cc6SDag-Erling Smørgrav return r;
338a0ee8cc6SDag-Erling Smørgrav /*
339a0ee8cc6SDag-Erling Smørgrav * If the requested allocation appended would push us past max_size
340a0ee8cc6SDag-Erling Smørgrav * then pack the buffer, zeroing buf->off.
341a0ee8cc6SDag-Erling Smørgrav */
342a0ee8cc6SDag-Erling Smørgrav sshbuf_maybe_pack(buf, buf->size + len > buf->max_size);
343ca86bcf2SDag-Erling Smørgrav SSHBUF_TELL("allocate");
344ca86bcf2SDag-Erling Smørgrav if (len + buf->size <= buf->alloc)
345ca86bcf2SDag-Erling Smørgrav return 0; /* already have it. */
346ca86bcf2SDag-Erling Smørgrav
347a0ee8cc6SDag-Erling Smørgrav /*
348a0ee8cc6SDag-Erling Smørgrav * Prefer to alloc in SSHBUF_SIZE_INC units, but
349a0ee8cc6SDag-Erling Smørgrav * allocate less if doing so would overflow max_size.
350a0ee8cc6SDag-Erling Smørgrav */
351a0ee8cc6SDag-Erling Smørgrav need = len + buf->size - buf->alloc;
352ca86bcf2SDag-Erling Smørgrav rlen = ROUNDUP(buf->alloc + need, SSHBUF_SIZE_INC);
353a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("need %zu initial rlen %zu", need, rlen));
354a0ee8cc6SDag-Erling Smørgrav if (rlen > buf->max_size)
355a0ee8cc6SDag-Erling Smørgrav rlen = buf->alloc + need;
356a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("adjusted rlen %zu", rlen));
3574f52dfbbSDag-Erling Smørgrav if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL) {
358a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("realloc fail"));
359a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL;
360a0ee8cc6SDag-Erling Smørgrav }
361a0ee8cc6SDag-Erling Smørgrav buf->alloc = rlen;
362a0ee8cc6SDag-Erling Smørgrav buf->cd = buf->d = dp;
363a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_reserve(buf, len)) < 0) {
364a0ee8cc6SDag-Erling Smørgrav /* shouldn't fail */
365a0ee8cc6SDag-Erling Smørgrav return r;
366a0ee8cc6SDag-Erling Smørgrav }
367ca86bcf2SDag-Erling Smørgrav SSHBUF_TELL("done");
368ca86bcf2SDag-Erling Smørgrav return 0;
369a0ee8cc6SDag-Erling Smørgrav }
370ca86bcf2SDag-Erling Smørgrav
371ca86bcf2SDag-Erling Smørgrav int
sshbuf_reserve(struct sshbuf * buf,size_t len,u_char ** dpp)372ca86bcf2SDag-Erling Smørgrav sshbuf_reserve(struct sshbuf *buf, size_t len, u_char **dpp)
373ca86bcf2SDag-Erling Smørgrav {
374ca86bcf2SDag-Erling Smørgrav u_char *dp;
375ca86bcf2SDag-Erling Smørgrav int r;
376ca86bcf2SDag-Erling Smørgrav
377ca86bcf2SDag-Erling Smørgrav if (dpp != NULL)
378ca86bcf2SDag-Erling Smørgrav *dpp = NULL;
379ca86bcf2SDag-Erling Smørgrav
380ca86bcf2SDag-Erling Smørgrav SSHBUF_DBG(("reserve buf = %p len = %zu", buf, len));
381ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_allocate(buf, len)) != 0)
382ca86bcf2SDag-Erling Smørgrav return r;
383ca86bcf2SDag-Erling Smørgrav
384a0ee8cc6SDag-Erling Smørgrav dp = buf->d + buf->size;
385a0ee8cc6SDag-Erling Smørgrav buf->size += len;
386a0ee8cc6SDag-Erling Smørgrav if (dpp != NULL)
387a0ee8cc6SDag-Erling Smørgrav *dpp = dp;
388a0ee8cc6SDag-Erling Smørgrav return 0;
389a0ee8cc6SDag-Erling Smørgrav }
390a0ee8cc6SDag-Erling Smørgrav
391a0ee8cc6SDag-Erling Smørgrav int
sshbuf_consume(struct sshbuf * buf,size_t len)392a0ee8cc6SDag-Erling Smørgrav sshbuf_consume(struct sshbuf *buf, size_t len)
393a0ee8cc6SDag-Erling Smørgrav {
394a0ee8cc6SDag-Erling Smørgrav int r;
395a0ee8cc6SDag-Erling Smørgrav
396a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("len = %zu", len));
397a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_sanity(buf)) != 0)
398a0ee8cc6SDag-Erling Smørgrav return r;
399a0ee8cc6SDag-Erling Smørgrav if (len == 0)
400a0ee8cc6SDag-Erling Smørgrav return 0;
401a0ee8cc6SDag-Erling Smørgrav if (len > sshbuf_len(buf))
402a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_MESSAGE_INCOMPLETE;
403a0ee8cc6SDag-Erling Smørgrav buf->off += len;
4044f52dfbbSDag-Erling Smørgrav /* deal with empty buffer */
4054f52dfbbSDag-Erling Smørgrav if (buf->off == buf->size)
4064f52dfbbSDag-Erling Smørgrav buf->off = buf->size = 0;
407a0ee8cc6SDag-Erling Smørgrav SSHBUF_TELL("done");
408a0ee8cc6SDag-Erling Smørgrav return 0;
409a0ee8cc6SDag-Erling Smørgrav }
410a0ee8cc6SDag-Erling Smørgrav
411a0ee8cc6SDag-Erling Smørgrav int
sshbuf_consume_end(struct sshbuf * buf,size_t len)412a0ee8cc6SDag-Erling Smørgrav sshbuf_consume_end(struct sshbuf *buf, size_t len)
413a0ee8cc6SDag-Erling Smørgrav {
414a0ee8cc6SDag-Erling Smørgrav int r;
415a0ee8cc6SDag-Erling Smørgrav
416a0ee8cc6SDag-Erling Smørgrav SSHBUF_DBG(("len = %zu", len));
417a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_check_sanity(buf)) != 0)
418a0ee8cc6SDag-Erling Smørgrav return r;
419a0ee8cc6SDag-Erling Smørgrav if (len == 0)
420a0ee8cc6SDag-Erling Smørgrav return 0;
421a0ee8cc6SDag-Erling Smørgrav if (len > sshbuf_len(buf))
422a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_MESSAGE_INCOMPLETE;
423a0ee8cc6SDag-Erling Smørgrav buf->size -= len;
424a0ee8cc6SDag-Erling Smørgrav SSHBUF_TELL("done");
425a0ee8cc6SDag-Erling Smørgrav return 0;
426a0ee8cc6SDag-Erling Smørgrav }
427a0ee8cc6SDag-Erling Smørgrav
428