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