sshbuf-getput-basic.c revision 1.8
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
29int
30sshbuf_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
42int
43sshbuf_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
55int
56sshbuf_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
68int
69sshbuf_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
81int
82sshbuf_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
94static int
95check_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
108static int
109check_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
121int
122sshbuf_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
136int
137sshbuf_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
151int
152sshbuf_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
166int
167sshbuf_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
181int
182sshbuf_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
208int
209sshbuf_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
234int
235sshbuf_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
265int
266sshbuf_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
300int
301sshbuf_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
320int
321sshbuf_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
333int
334sshbuf_putb(struct sshbuf *buf, const struct sshbuf *v)
335{
336	return sshbuf_put(buf, sshbuf_ptr(v), sshbuf_len(v));
337}
338
339int
340sshbuf_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
351int
352sshbuf_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
384int
385sshbuf_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
396int
397sshbuf_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
408int
409sshbuf_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
420int
421sshbuf_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
432static int
433check_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
446int
447sshbuf_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
458int
459sshbuf_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
470int
471sshbuf_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
482int
483sshbuf_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
494int
495sshbuf_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
506int
507sshbuf_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
524int
525sshbuf_put_cstring(struct sshbuf *buf, const char *v)
526{
527	return sshbuf_put_string(buf, v, v == NULL ? 0 : strlen(v));
528}
529
530int
531sshbuf_put_stringb(struct sshbuf *buf, const struct sshbuf *v)
532{
533	return sshbuf_put_string(buf, sshbuf_ptr(v), sshbuf_len(v));
534}
535
536int
537sshbuf_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
560int
561sshbuf_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
589int
590sshbuf_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