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