sshbuf-getput-basic.c revision 296853
1/*	$OpenBSD: sshbuf-getput-basic.c,v 1.5 2015/10/20 23:24:25 mmcc 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 <stdlib.h>
23#include <stdio.h>
24#include <string.h>
25
26#include "ssherr.h"
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
94int
95sshbuf_get_string(struct sshbuf *buf, u_char **valp, size_t *lenp)
96{
97	const u_char *val;
98	size_t len;
99	int r;
100
101	if (valp != NULL)
102		*valp = NULL;
103	if (lenp != NULL)
104		*lenp = 0;
105	if ((r = sshbuf_get_string_direct(buf, &val, &len)) < 0)
106		return r;
107	if (valp != NULL) {
108		if ((*valp = malloc(len + 1)) == NULL) {
109			SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL"));
110			return SSH_ERR_ALLOC_FAIL;
111		}
112		if (len != 0)
113			memcpy(*valp, val, len);
114		(*valp)[len] = '\0';
115	}
116	if (lenp != NULL)
117		*lenp = len;
118	return 0;
119}
120
121int
122sshbuf_get_string_direct(struct sshbuf *buf, const u_char **valp, size_t *lenp)
123{
124	size_t len;
125	const u_char *p;
126	int r;
127
128	if (valp != NULL)
129		*valp = NULL;
130	if (lenp != NULL)
131		*lenp = 0;
132	if ((r = sshbuf_peek_string_direct(buf, &p, &len)) < 0)
133		return r;
134	if (valp != NULL)
135		*valp = p;
136	if (lenp != NULL)
137		*lenp = len;
138	if (sshbuf_consume(buf, len + 4) != 0) {
139		/* Shouldn't happen */
140		SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR"));
141		SSHBUF_ABORT();
142		return SSH_ERR_INTERNAL_ERROR;
143	}
144	return 0;
145}
146
147int
148sshbuf_peek_string_direct(const struct sshbuf *buf, const u_char **valp,
149    size_t *lenp)
150{
151	u_int32_t len;
152	const u_char *p = sshbuf_ptr(buf);
153
154	if (valp != NULL)
155		*valp = NULL;
156	if (lenp != NULL)
157		*lenp = 0;
158	if (sshbuf_len(buf) < 4) {
159		SSHBUF_DBG(("SSH_ERR_MESSAGE_INCOMPLETE"));
160		return SSH_ERR_MESSAGE_INCOMPLETE;
161	}
162	len = PEEK_U32(p);
163	if (len > SSHBUF_SIZE_MAX - 4) {
164		SSHBUF_DBG(("SSH_ERR_STRING_TOO_LARGE"));
165		return SSH_ERR_STRING_TOO_LARGE;
166	}
167	if (sshbuf_len(buf) - 4 < len) {
168		SSHBUF_DBG(("SSH_ERR_MESSAGE_INCOMPLETE"));
169		return SSH_ERR_MESSAGE_INCOMPLETE;
170	}
171	if (valp != NULL)
172		*valp = p + 4;
173	if (lenp != NULL)
174		*lenp = len;
175	return 0;
176}
177
178int
179sshbuf_get_cstring(struct sshbuf *buf, char **valp, size_t *lenp)
180{
181	size_t len;
182	const u_char *p, *z;
183	int r;
184
185	if (valp != NULL)
186		*valp = NULL;
187	if (lenp != NULL)
188		*lenp = 0;
189	if ((r = sshbuf_peek_string_direct(buf, &p, &len)) != 0)
190		return r;
191	/* Allow a \0 only at the end of the string */
192	if (len > 0 &&
193	    (z = memchr(p , '\0', len)) != NULL && z < p + len - 1) {
194		SSHBUF_DBG(("SSH_ERR_INVALID_FORMAT"));
195		return SSH_ERR_INVALID_FORMAT;
196	}
197	if ((r = sshbuf_skip_string(buf)) != 0)
198		return -1;
199	if (valp != NULL) {
200		if ((*valp = malloc(len + 1)) == NULL) {
201			SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL"));
202			return SSH_ERR_ALLOC_FAIL;
203		}
204		if (len != 0)
205			memcpy(*valp, p, len);
206		(*valp)[len] = '\0';
207	}
208	if (lenp != NULL)
209		*lenp = (size_t)len;
210	return 0;
211}
212
213int
214sshbuf_get_stringb(struct sshbuf *buf, struct sshbuf *v)
215{
216	u_int32_t len;
217	u_char *p;
218	int r;
219
220	/*
221	 * Use sshbuf_peek_string_direct() to figure out if there is
222	 * a complete string in 'buf' and copy the string directly
223	 * into 'v'.
224	 */
225	if ((r = sshbuf_peek_string_direct(buf, NULL, NULL)) != 0 ||
226	    (r = sshbuf_get_u32(buf, &len)) != 0 ||
227	    (r = sshbuf_reserve(v, len, &p)) != 0 ||
228	    (r = sshbuf_get(buf, p, len)) != 0)
229		return r;
230	return 0;
231}
232
233int
234sshbuf_put(struct sshbuf *buf, const void *v, size_t len)
235{
236	u_char *p;
237	int r;
238
239	if ((r = sshbuf_reserve(buf, len, &p)) < 0)
240		return r;
241	if (len != 0)
242		memcpy(p, v, len);
243	return 0;
244}
245
246int
247sshbuf_putb(struct sshbuf *buf, const struct sshbuf *v)
248{
249	return sshbuf_put(buf, sshbuf_ptr(v), sshbuf_len(v));
250}
251
252int
253sshbuf_putf(struct sshbuf *buf, const char *fmt, ...)
254{
255	va_list ap;
256	int r;
257
258	va_start(ap, fmt);
259	r = sshbuf_putfv(buf, fmt, ap);
260	va_end(ap);
261	return r;
262}
263
264int
265sshbuf_putfv(struct sshbuf *buf, const char *fmt, va_list ap)
266{
267	va_list ap2;
268	int r, len;
269	u_char *p;
270
271	va_copy(ap2, ap);
272	if ((len = vsnprintf(NULL, 0, fmt, ap2)) < 0) {
273		r = SSH_ERR_INVALID_ARGUMENT;
274		goto out;
275	}
276	if (len == 0) {
277		r = 0;
278		goto out; /* Nothing to do */
279	}
280	va_end(ap2);
281	va_copy(ap2, ap);
282	if ((r = sshbuf_reserve(buf, (size_t)len + 1, &p)) < 0)
283		goto out;
284	if ((r = vsnprintf((char *)p, len + 1, fmt, ap2)) != len) {
285		r = SSH_ERR_INTERNAL_ERROR;
286		goto out; /* Shouldn't happen */
287	}
288	/* Consume terminating \0 */
289	if ((r = sshbuf_consume_end(buf, 1)) != 0)
290		goto out;
291	r = 0;
292 out:
293	va_end(ap2);
294	return r;
295}
296
297int
298sshbuf_put_u64(struct sshbuf *buf, u_int64_t val)
299{
300	u_char *p;
301	int r;
302
303	if ((r = sshbuf_reserve(buf, 8, &p)) < 0)
304		return r;
305	POKE_U64(p, val);
306	return 0;
307}
308
309int
310sshbuf_put_u32(struct sshbuf *buf, u_int32_t val)
311{
312	u_char *p;
313	int r;
314
315	if ((r = sshbuf_reserve(buf, 4, &p)) < 0)
316		return r;
317	POKE_U32(p, val);
318	return 0;
319}
320
321int
322sshbuf_put_u16(struct sshbuf *buf, u_int16_t val)
323{
324	u_char *p;
325	int r;
326
327	if ((r = sshbuf_reserve(buf, 2, &p)) < 0)
328		return r;
329	POKE_U16(p, val);
330	return 0;
331}
332
333int
334sshbuf_put_u8(struct sshbuf *buf, u_char val)
335{
336	u_char *p;
337	int r;
338
339	if ((r = sshbuf_reserve(buf, 1, &p)) < 0)
340		return r;
341	p[0] = val;
342	return 0;
343}
344
345int
346sshbuf_put_string(struct sshbuf *buf, const void *v, size_t len)
347{
348	u_char *d;
349	int r;
350
351	if (len > SSHBUF_SIZE_MAX - 4) {
352		SSHBUF_DBG(("SSH_ERR_NO_BUFFER_SPACE"));
353		return SSH_ERR_NO_BUFFER_SPACE;
354	}
355	if ((r = sshbuf_reserve(buf, len + 4, &d)) < 0)
356		return r;
357	POKE_U32(d, len);
358	if (len != 0)
359		memcpy(d + 4, v, len);
360	return 0;
361}
362
363int
364sshbuf_put_cstring(struct sshbuf *buf, const char *v)
365{
366	return sshbuf_put_string(buf, (u_char *)v, v == NULL ? 0 : strlen(v));
367}
368
369int
370sshbuf_put_stringb(struct sshbuf *buf, const struct sshbuf *v)
371{
372	return sshbuf_put_string(buf, sshbuf_ptr(v), sshbuf_len(v));
373}
374
375int
376sshbuf_froms(struct sshbuf *buf, struct sshbuf **bufp)
377{
378	const u_char *p;
379	size_t len;
380	struct sshbuf *ret;
381	int r;
382
383	if (buf == NULL || bufp == NULL)
384		return SSH_ERR_INVALID_ARGUMENT;
385	*bufp = NULL;
386	if ((r = sshbuf_peek_string_direct(buf, &p, &len)) != 0)
387		return r;
388	if ((ret = sshbuf_from(p, len)) == NULL)
389		return SSH_ERR_ALLOC_FAIL;
390	if ((r = sshbuf_consume(buf, len + 4)) != 0 ||  /* Shouldn't happen */
391	    (r = sshbuf_set_parent(ret, buf)) != 0) {
392		sshbuf_free(ret);
393		return r;
394	}
395	*bufp = ret;
396	return 0;
397}
398
399int
400sshbuf_put_bignum2_bytes(struct sshbuf *buf, const void *v, size_t len)
401{
402	u_char *d;
403	const u_char *s = (const u_char *)v;
404	int r, prepend;
405
406	if (len > SSHBUF_SIZE_MAX - 5) {
407		SSHBUF_DBG(("SSH_ERR_NO_BUFFER_SPACE"));
408		return SSH_ERR_NO_BUFFER_SPACE;
409	}
410	/* Skip leading zero bytes */
411	for (; len > 0 && *s == 0; len--, s++)
412		;
413	/*
414	 * If most significant bit is set then prepend a zero byte to
415	 * avoid interpretation as a negative number.
416	 */
417	prepend = len > 0 && (s[0] & 0x80) != 0;
418	if ((r = sshbuf_reserve(buf, len + 4 + prepend, &d)) < 0)
419		return r;
420	POKE_U32(d, len + prepend);
421	if (prepend)
422		d[4] = 0;
423	if (len != 0)
424		memcpy(d + 4 + prepend, s, len);
425	return 0;
426}
427
428int
429sshbuf_get_bignum2_bytes_direct(struct sshbuf *buf,
430    const u_char **valp, size_t *lenp)
431{
432	const u_char *d;
433	size_t len, olen;
434	int r;
435
436	if ((r = sshbuf_peek_string_direct(buf, &d, &olen)) < 0)
437		return r;
438	len = olen;
439	/* Refuse negative (MSB set) bignums */
440	if ((len != 0 && (*d & 0x80) != 0))
441		return SSH_ERR_BIGNUM_IS_NEGATIVE;
442	/* Refuse overlong bignums, allow prepended \0 to avoid MSB set */
443	if (len > SSHBUF_MAX_BIGNUM + 1 ||
444	    (len == SSHBUF_MAX_BIGNUM + 1 && *d != 0))
445		return SSH_ERR_BIGNUM_TOO_LARGE;
446	/* Trim leading zeros */
447	while (len > 0 && *d == 0x00) {
448		d++;
449		len--;
450	}
451	if (valp != NULL)
452		*valp = d;
453	if (lenp != NULL)
454		*lenp = len;
455	if (sshbuf_consume(buf, olen + 4) != 0) {
456		/* Shouldn't happen */
457		SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR"));
458		SSHBUF_ABORT();
459		return SSH_ERR_INTERNAL_ERROR;
460	}
461	return 0;
462}
463