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