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