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