blob: ffa20a02cb7c9a516a1653841d41877b5900c0d2 [file] [log] [blame]
djm@openbsd.orgbe02d7c2019-09-06 04:53:27 +00001/* $OpenBSD: sshbuf-getput-basic.c,v 1.9 2019/09/06 04:53:27 djm Exp $ */
Damien Miller05e82c32014-05-15 14:33:43 +10002/*
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
Damien Millere5b9f0f2014-05-15 14:58:07 +100018#define SSHBUF_INTERNAL
Damien Miller05e82c32014-05-15 14:33:43 +100019#include "includes.h"
20
21#include <sys/types.h>
dtucker@openbsd.org9816fc52016-06-16 11:00:17 +000022
23#include <stdarg.h>
Damien Miller05e82c32014-05-15 14:33:43 +100024#include <stdlib.h>
25#include <stdio.h>
26#include <string.h>
djm@openbsd.orgbe02d7c2019-09-06 04:53:27 +000027#include <stdint.h>
Damien Miller05e82c32014-05-15 14:33:43 +100028
29#include "ssherr.h"
Damien Miller05e82c32014-05-15 14:33:43 +100030#include "sshbuf.h"
31
32int
33sshbuf_get(struct sshbuf *buf, void *v, size_t len)
34{
35 const u_char *p = sshbuf_ptr(buf);
36 int r;
37
38 if ((r = sshbuf_consume(buf, len)) < 0)
39 return r;
djm@openbsd.orga7f49dc2015-01-12 15:18:07 +000040 if (v != NULL && len != 0)
Damien Miller05e82c32014-05-15 14:33:43 +100041 memcpy(v, p, len);
42 return 0;
43}
44
45int
46sshbuf_get_u64(struct sshbuf *buf, u_int64_t *valp)
47{
48 const u_char *p = sshbuf_ptr(buf);
49 int r;
50
51 if ((r = sshbuf_consume(buf, 8)) < 0)
52 return r;
53 if (valp != NULL)
54 *valp = PEEK_U64(p);
55 return 0;
56}
57
58int
59sshbuf_get_u32(struct sshbuf *buf, u_int32_t *valp)
60{
61 const u_char *p = sshbuf_ptr(buf);
62 int r;
63
64 if ((r = sshbuf_consume(buf, 4)) < 0)
65 return r;
66 if (valp != NULL)
67 *valp = PEEK_U32(p);
68 return 0;
69}
70
71int
72sshbuf_get_u16(struct sshbuf *buf, u_int16_t *valp)
73{
74 const u_char *p = sshbuf_ptr(buf);
75 int r;
76
77 if ((r = sshbuf_consume(buf, 2)) < 0)
78 return r;
79 if (valp != NULL)
80 *valp = PEEK_U16(p);
81 return 0;
82}
83
84int
85sshbuf_get_u8(struct sshbuf *buf, u_char *valp)
86{
87 const u_char *p = sshbuf_ptr(buf);
88 int r;
89
90 if ((r = sshbuf_consume(buf, 1)) < 0)
91 return r;
92 if (valp != NULL)
93 *valp = (u_int8_t)*p;
94 return 0;
95}
96
djm@openbsd.org101d1642019-07-14 23:32:27 +000097static int
98check_offset(const struct sshbuf *buf, int wr, size_t offset, size_t len)
99{
100 if (sshbuf_ptr(buf) == NULL) /* calls sshbuf_check_sanity() */
101 return SSH_ERR_INTERNAL_ERROR;
102 if (offset >= SIZE_MAX - len)
103 return SSH_ERR_INVALID_ARGUMENT;
104 if (offset + len > sshbuf_len(buf)) {
105 return wr ?
106 SSH_ERR_NO_BUFFER_SPACE : SSH_ERR_MESSAGE_INCOMPLETE;
107 }
108 return 0;
109}
110
111static int
112check_roffset(const struct sshbuf *buf, size_t offset, size_t len,
113 const u_char **p)
114{
115 int r;
116
117 *p = NULL;
118 if ((r = check_offset(buf, 0, offset, len)) != 0)
119 return r;
120 *p = sshbuf_ptr(buf) + offset;
121 return 0;
122}
123
124int
125sshbuf_peek_u64(const struct sshbuf *buf, size_t offset, u_int64_t *valp)
126{
127 const u_char *p = NULL;
128 int r;
129
130 if (valp != NULL)
131 *valp = 0;
132 if ((r = check_roffset(buf, offset, 8, &p)) != 0)
133 return r;
134 if (valp != NULL)
135 *valp = PEEK_U64(p);
136 return 0;
137}
138
139int
140sshbuf_peek_u32(const struct sshbuf *buf, size_t offset, u_int32_t *valp)
141{
142 const u_char *p = NULL;
143 int r;
144
145 if (valp != NULL)
146 *valp = 0;
147 if ((r = check_roffset(buf, offset, 4, &p)) != 0)
148 return r;
149 if (valp != NULL)
150 *valp = PEEK_U32(p);
151 return 0;
152}
153
154int
155sshbuf_peek_u16(const struct sshbuf *buf, size_t offset, u_int16_t *valp)
156{
157 const u_char *p = NULL;
158 int r;
159
160 if (valp != NULL)
161 *valp = 0;
162 if ((r = check_roffset(buf, offset, 2, &p)) != 0)
163 return r;
164 if (valp != NULL)
165 *valp = PEEK_U16(p);
166 return 0;
167}
168
169int
170sshbuf_peek_u8(const struct sshbuf *buf, size_t offset, u_char *valp)
171{
172 const u_char *p = NULL;
173 int r;
174
175 if (valp != NULL)
176 *valp = 0;
177 if ((r = check_roffset(buf, offset, 1, &p)) != 0)
178 return r;
179 if (valp != NULL)
180 *valp = *p;
181 return 0;
182}
183
Damien Miller05e82c32014-05-15 14:33:43 +1000184int
185sshbuf_get_string(struct sshbuf *buf, u_char **valp, size_t *lenp)
186{
187 const u_char *val;
188 size_t len;
189 int r;
190
191 if (valp != NULL)
192 *valp = NULL;
193 if (lenp != NULL)
194 *lenp = 0;
195 if ((r = sshbuf_get_string_direct(buf, &val, &len)) < 0)
196 return r;
197 if (valp != NULL) {
198 if ((*valp = malloc(len + 1)) == NULL) {
199 SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL"));
200 return SSH_ERR_ALLOC_FAIL;
201 }
djm@openbsd.orga7f49dc2015-01-12 15:18:07 +0000202 if (len != 0)
203 memcpy(*valp, val, len);
Damien Miller05e82c32014-05-15 14:33:43 +1000204 (*valp)[len] = '\0';
205 }
206 if (lenp != NULL)
207 *lenp = len;
208 return 0;
209}
210
211int
212sshbuf_get_string_direct(struct sshbuf *buf, const u_char **valp, size_t *lenp)
213{
214 size_t len;
215 const u_char *p;
216 int r;
217
218 if (valp != NULL)
219 *valp = NULL;
220 if (lenp != NULL)
221 *lenp = 0;
222 if ((r = sshbuf_peek_string_direct(buf, &p, &len)) < 0)
223 return r;
mmcc@openbsd.org7d6c0362015-10-20 23:24:25 +0000224 if (valp != NULL)
Damien Miller05e82c32014-05-15 14:33:43 +1000225 *valp = p;
226 if (lenp != NULL)
227 *lenp = len;
228 if (sshbuf_consume(buf, len + 4) != 0) {
229 /* Shouldn't happen */
230 SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR"));
231 SSHBUF_ABORT();
232 return SSH_ERR_INTERNAL_ERROR;
233 }
234 return 0;
235}
236
237int
238sshbuf_peek_string_direct(const struct sshbuf *buf, const u_char **valp,
239 size_t *lenp)
240{
241 u_int32_t len;
242 const u_char *p = sshbuf_ptr(buf);
243
244 if (valp != NULL)
245 *valp = NULL;
246 if (lenp != NULL)
247 *lenp = 0;
248 if (sshbuf_len(buf) < 4) {
249 SSHBUF_DBG(("SSH_ERR_MESSAGE_INCOMPLETE"));
250 return SSH_ERR_MESSAGE_INCOMPLETE;
251 }
252 len = PEEK_U32(p);
253 if (len > SSHBUF_SIZE_MAX - 4) {
254 SSHBUF_DBG(("SSH_ERR_STRING_TOO_LARGE"));
255 return SSH_ERR_STRING_TOO_LARGE;
256 }
257 if (sshbuf_len(buf) - 4 < len) {
258 SSHBUF_DBG(("SSH_ERR_MESSAGE_INCOMPLETE"));
259 return SSH_ERR_MESSAGE_INCOMPLETE;
260 }
mmcc@openbsd.org7d6c0362015-10-20 23:24:25 +0000261 if (valp != NULL)
Damien Miller05e82c32014-05-15 14:33:43 +1000262 *valp = p + 4;
263 if (lenp != NULL)
264 *lenp = len;
265 return 0;
266}
267
268int
269sshbuf_get_cstring(struct sshbuf *buf, char **valp, size_t *lenp)
270{
271 size_t len;
272 const u_char *p, *z;
273 int r;
274
275 if (valp != NULL)
276 *valp = NULL;
277 if (lenp != NULL)
278 *lenp = 0;
279 if ((r = sshbuf_peek_string_direct(buf, &p, &len)) != 0)
280 return r;
281 /* Allow a \0 only at the end of the string */
282 if (len > 0 &&
283 (z = memchr(p , '\0', len)) != NULL && z < p + len - 1) {
284 SSHBUF_DBG(("SSH_ERR_INVALID_FORMAT"));
285 return SSH_ERR_INVALID_FORMAT;
286 }
287 if ((r = sshbuf_skip_string(buf)) != 0)
288 return -1;
289 if (valp != NULL) {
290 if ((*valp = malloc(len + 1)) == NULL) {
291 SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL"));
292 return SSH_ERR_ALLOC_FAIL;
293 }
djm@openbsd.orga7f49dc2015-01-12 15:18:07 +0000294 if (len != 0)
295 memcpy(*valp, p, len);
Damien Miller05e82c32014-05-15 14:33:43 +1000296 (*valp)[len] = '\0';
297 }
298 if (lenp != NULL)
299 *lenp = (size_t)len;
300 return 0;
301}
302
303int
304sshbuf_get_stringb(struct sshbuf *buf, struct sshbuf *v)
305{
306 u_int32_t len;
307 u_char *p;
308 int r;
309
310 /*
311 * Use sshbuf_peek_string_direct() to figure out if there is
312 * a complete string in 'buf' and copy the string directly
313 * into 'v'.
314 */
315 if ((r = sshbuf_peek_string_direct(buf, NULL, NULL)) != 0 ||
316 (r = sshbuf_get_u32(buf, &len)) != 0 ||
317 (r = sshbuf_reserve(v, len, &p)) != 0 ||
318 (r = sshbuf_get(buf, p, len)) != 0)
319 return r;
320 return 0;
321}
322
323int
324sshbuf_put(struct sshbuf *buf, const void *v, size_t len)
325{
326 u_char *p;
327 int r;
328
329 if ((r = sshbuf_reserve(buf, len, &p)) < 0)
330 return r;
djm@openbsd.orga7f49dc2015-01-12 15:18:07 +0000331 if (len != 0)
332 memcpy(p, v, len);
Damien Miller05e82c32014-05-15 14:33:43 +1000333 return 0;
334}
335
336int
337sshbuf_putb(struct sshbuf *buf, const struct sshbuf *v)
338{
339 return sshbuf_put(buf, sshbuf_ptr(v), sshbuf_len(v));
340}
341
342int
343sshbuf_putf(struct sshbuf *buf, const char *fmt, ...)
344{
345 va_list ap;
346 int r;
347
348 va_start(ap, fmt);
349 r = sshbuf_putfv(buf, fmt, ap);
350 va_end(ap);
351 return r;
352}
353
354int
355sshbuf_putfv(struct sshbuf *buf, const char *fmt, va_list ap)
356{
357 va_list ap2;
358 int r, len;
359 u_char *p;
360
Darren Tucker5abfb152016-07-15 14:48:30 +1000361 VA_COPY(ap2, ap);
Damien Miller05e82c32014-05-15 14:33:43 +1000362 if ((len = vsnprintf(NULL, 0, fmt, ap2)) < 0) {
363 r = SSH_ERR_INVALID_ARGUMENT;
364 goto out;
365 }
366 if (len == 0) {
367 r = 0;
368 goto out; /* Nothing to do */
369 }
370 va_end(ap2);
Darren Tucker5abfb152016-07-15 14:48:30 +1000371 VA_COPY(ap2, ap);
Damien Miller05e82c32014-05-15 14:33:43 +1000372 if ((r = sshbuf_reserve(buf, (size_t)len + 1, &p)) < 0)
373 goto out;
374 if ((r = vsnprintf((char *)p, len + 1, fmt, ap2)) != len) {
375 r = SSH_ERR_INTERNAL_ERROR;
376 goto out; /* Shouldn't happen */
377 }
378 /* Consume terminating \0 */
379 if ((r = sshbuf_consume_end(buf, 1)) != 0)
380 goto out;
381 r = 0;
382 out:
383 va_end(ap2);
384 return r;
385}
386
387int
388sshbuf_put_u64(struct sshbuf *buf, u_int64_t val)
389{
390 u_char *p;
391 int r;
392
393 if ((r = sshbuf_reserve(buf, 8, &p)) < 0)
394 return r;
395 POKE_U64(p, val);
396 return 0;
397}
398
399int
400sshbuf_put_u32(struct sshbuf *buf, u_int32_t val)
401{
402 u_char *p;
403 int r;
404
405 if ((r = sshbuf_reserve(buf, 4, &p)) < 0)
406 return r;
407 POKE_U32(p, val);
408 return 0;
409}
410
411int
412sshbuf_put_u16(struct sshbuf *buf, u_int16_t val)
413{
414 u_char *p;
415 int r;
416
417 if ((r = sshbuf_reserve(buf, 2, &p)) < 0)
418 return r;
419 POKE_U16(p, val);
420 return 0;
421}
422
423int
424sshbuf_put_u8(struct sshbuf *buf, u_char val)
425{
426 u_char *p;
427 int r;
428
429 if ((r = sshbuf_reserve(buf, 1, &p)) < 0)
430 return r;
431 p[0] = val;
432 return 0;
433}
434
djm@openbsd.org101d1642019-07-14 23:32:27 +0000435static int
436check_woffset(struct sshbuf *buf, size_t offset, size_t len, u_char **p)
437{
438 int r;
439
440 *p = NULL;
441 if ((r = check_offset(buf, 1, offset, len)) != 0)
442 return r;
443 if (sshbuf_mutable_ptr(buf) == NULL)
444 return SSH_ERR_BUFFER_READ_ONLY;
445 *p = sshbuf_mutable_ptr(buf) + offset;
446 return 0;
447}
448
449int
450sshbuf_poke_u64(struct sshbuf *buf, size_t offset, u_int64_t val)
451{
452 u_char *p = NULL;
453 int r;
454
455 if ((r = check_woffset(buf, offset, 8, &p)) != 0)
456 return r;
457 POKE_U64(p, val);
458 return 0;
459}
460
461int
462sshbuf_poke_u32(struct sshbuf *buf, size_t offset, u_int32_t val)
463{
464 u_char *p = NULL;
465 int r;
466
467 if ((r = check_woffset(buf, offset, 4, &p)) != 0)
468 return r;
469 POKE_U32(p, val);
470 return 0;
471}
472
473int
474sshbuf_poke_u16(struct sshbuf *buf, size_t offset, u_int16_t val)
475{
476 u_char *p = NULL;
477 int r;
478
479 if ((r = check_woffset(buf, offset, 2, &p)) != 0)
480 return r;
481 POKE_U16(p, val);
482 return 0;
483}
484
485int
486sshbuf_poke_u8(struct sshbuf *buf, size_t offset, u_char val)
487{
488 u_char *p = NULL;
489 int r;
490
491 if ((r = check_woffset(buf, offset, 1, &p)) != 0)
492 return r;
493 *p = val;
494 return 0;
495}
496
497int
498sshbuf_poke(struct sshbuf *buf, size_t offset, void *v, size_t len)
499{
500 u_char *p = NULL;
501 int r;
502
503 if ((r = check_woffset(buf, offset, len, &p)) != 0)
504 return r;
505 memcpy(p, v, len);
506 return 0;
507}
508
Damien Miller05e82c32014-05-15 14:33:43 +1000509int
510sshbuf_put_string(struct sshbuf *buf, const void *v, size_t len)
511{
512 u_char *d;
513 int r;
514
515 if (len > SSHBUF_SIZE_MAX - 4) {
516 SSHBUF_DBG(("SSH_ERR_NO_BUFFER_SPACE"));
517 return SSH_ERR_NO_BUFFER_SPACE;
518 }
519 if ((r = sshbuf_reserve(buf, len + 4, &d)) < 0)
520 return r;
521 POKE_U32(d, len);
djm@openbsd.orga7f49dc2015-01-12 15:18:07 +0000522 if (len != 0)
523 memcpy(d + 4, v, len);
Damien Miller05e82c32014-05-15 14:33:43 +1000524 return 0;
525}
526
527int
528sshbuf_put_cstring(struct sshbuf *buf, const char *v)
529{
djm@openbsd.org4b2e2d32017-06-01 04:51:58 +0000530 return sshbuf_put_string(buf, v, v == NULL ? 0 : strlen(v));
Damien Miller05e82c32014-05-15 14:33:43 +1000531}
532
533int
534sshbuf_put_stringb(struct sshbuf *buf, const struct sshbuf *v)
535{
536 return sshbuf_put_string(buf, sshbuf_ptr(v), sshbuf_len(v));
537}
538
539int
540sshbuf_froms(struct sshbuf *buf, struct sshbuf **bufp)
541{
542 const u_char *p;
543 size_t len;
544 struct sshbuf *ret;
545 int r;
546
547 if (buf == NULL || bufp == NULL)
548 return SSH_ERR_INVALID_ARGUMENT;
549 *bufp = NULL;
550 if ((r = sshbuf_peek_string_direct(buf, &p, &len)) != 0)
551 return r;
552 if ((ret = sshbuf_from(p, len)) == NULL)
553 return SSH_ERR_ALLOC_FAIL;
554 if ((r = sshbuf_consume(buf, len + 4)) != 0 || /* Shouldn't happen */
555 (r = sshbuf_set_parent(ret, buf)) != 0) {
556 sshbuf_free(ret);
557 return r;
558 }
559 *bufp = ret;
560 return 0;
561}
562
563int
564sshbuf_put_bignum2_bytes(struct sshbuf *buf, const void *v, size_t len)
565{
566 u_char *d;
567 const u_char *s = (const u_char *)v;
568 int r, prepend;
569
570 if (len > SSHBUF_SIZE_MAX - 5) {
571 SSHBUF_DBG(("SSH_ERR_NO_BUFFER_SPACE"));
572 return SSH_ERR_NO_BUFFER_SPACE;
573 }
574 /* Skip leading zero bytes */
575 for (; len > 0 && *s == 0; len--, s++)
576 ;
577 /*
578 * If most significant bit is set then prepend a zero byte to
579 * avoid interpretation as a negative number.
580 */
581 prepend = len > 0 && (s[0] & 0x80) != 0;
582 if ((r = sshbuf_reserve(buf, len + 4 + prepend, &d)) < 0)
583 return r;
584 POKE_U32(d, len + prepend);
585 if (prepend)
586 d[4] = 0;
djm@openbsd.orga7f49dc2015-01-12 15:18:07 +0000587 if (len != 0)
588 memcpy(d + 4 + prepend, s, len);
Damien Miller05e82c32014-05-15 14:33:43 +1000589 return 0;
590}
djm@openbsd.orga165bab2015-01-14 15:02:39 +0000591
592int
593sshbuf_get_bignum2_bytes_direct(struct sshbuf *buf,
594 const u_char **valp, size_t *lenp)
595{
596 const u_char *d;
597 size_t len, olen;
598 int r;
599
600 if ((r = sshbuf_peek_string_direct(buf, &d, &olen)) < 0)
601 return r;
602 len = olen;
603 /* Refuse negative (MSB set) bignums */
604 if ((len != 0 && (*d & 0x80) != 0))
605 return SSH_ERR_BIGNUM_IS_NEGATIVE;
606 /* Refuse overlong bignums, allow prepended \0 to avoid MSB set */
607 if (len > SSHBUF_MAX_BIGNUM + 1 ||
608 (len == SSHBUF_MAX_BIGNUM + 1 && *d != 0))
609 return SSH_ERR_BIGNUM_TOO_LARGE;
610 /* Trim leading zeros */
611 while (len > 0 && *d == 0x00) {
612 d++;
613 len--;
614 }
mmcc@openbsd.org7d6c0362015-10-20 23:24:25 +0000615 if (valp != NULL)
djm@openbsd.orga165bab2015-01-14 15:02:39 +0000616 *valp = d;
617 if (lenp != NULL)
618 *lenp = len;
619 if (sshbuf_consume(buf, olen + 4) != 0) {
620 /* Shouldn't happen */
621 SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR"));
622 SSHBUF_ABORT();
623 return SSH_ERR_INTERNAL_ERROR;
624 }
625 return 0;
626}