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