xref: /openbsd-src/usr.bin/ssh/sshbuf-misc.c (revision 41c2b893527c757dc20005b0918896892f3947a8)
1*41c2b893Sdjm /*	$OpenBSD: sshbuf-misc.c,v 1.18 2022/01/22 00:43:43 djm Exp $	*/
215b55daeSdjm /*
315b55daeSdjm  * Copyright (c) 2011 Damien Miller
415b55daeSdjm  *
515b55daeSdjm  * Permission to use, copy, modify, and distribute this software for any
615b55daeSdjm  * purpose with or without fee is hereby granted, provided that the above
715b55daeSdjm  * copyright notice and this permission notice appear in all copies.
815b55daeSdjm  *
915b55daeSdjm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1015b55daeSdjm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1115b55daeSdjm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1215b55daeSdjm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1315b55daeSdjm  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1415b55daeSdjm  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1515b55daeSdjm  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1615b55daeSdjm  */
1715b55daeSdjm 
1815b55daeSdjm #include <sys/types.h>
1915b55daeSdjm #include <sys/socket.h>
2015b55daeSdjm #include <netinet/in.h>
2115b55daeSdjm #include <errno.h>
22bb3c2609Sdjm #include <stdlib.h>
231357284aSmillert #include <stdint.h>
2415b55daeSdjm #include <stdio.h>
25bb3c2609Sdjm #include <limits.h>
2615b55daeSdjm #include <string.h>
27bb3c2609Sdjm #include <resolv.h>
28bb3c2609Sdjm #include <ctype.h>
29*41c2b893Sdjm #include <unistd.h>
3015b55daeSdjm 
3115b55daeSdjm #include "ssherr.h"
3215b55daeSdjm #define SSHBUF_INTERNAL
3315b55daeSdjm #include "sshbuf.h"
3415b55daeSdjm 
3515b55daeSdjm void
sshbuf_dump_data(const void * s,size_t len,FILE * f)36ea2d8289Sdjm sshbuf_dump_data(const void *s, size_t len, FILE *f)
3715b55daeSdjm {
38ea2d8289Sdjm 	size_t i, j;
39ea2d8289Sdjm 	const u_char *p = (const u_char *)s;
4015b55daeSdjm 
4115b55daeSdjm 	for (i = 0; i < len; i += 16) {
42cfb5d775Smarkus 		fprintf(f, "%.4zu: ", i);
4315b55daeSdjm 		for (j = i; j < i + 16; j++) {
4415b55daeSdjm 			if (j < len)
4515b55daeSdjm 				fprintf(f, "%02x ", p[j]);
4615b55daeSdjm 			else
4715b55daeSdjm 				fprintf(f, "   ");
4815b55daeSdjm 		}
4915b55daeSdjm 		fprintf(f, " ");
5015b55daeSdjm 		for (j = i; j < i + 16; j++) {
5115b55daeSdjm 			if (j < len) {
5215b55daeSdjm 				if  (isascii(p[j]) && isprint(p[j]))
5315b55daeSdjm 					fprintf(f, "%c", p[j]);
5415b55daeSdjm 				else
5515b55daeSdjm 					fprintf(f, ".");
5615b55daeSdjm 			}
5715b55daeSdjm 		}
5815b55daeSdjm 		fprintf(f, "\n");
5915b55daeSdjm 	}
6015b55daeSdjm }
6115b55daeSdjm 
62ea2d8289Sdjm void
sshbuf_dump(const struct sshbuf * buf,FILE * f)6319d7d104Sdjm sshbuf_dump(const struct sshbuf *buf, FILE *f)
64ea2d8289Sdjm {
65ebf09617Sdjm 	fprintf(f, "buffer len = %zu\n", sshbuf_len(buf));
66ea2d8289Sdjm 	sshbuf_dump_data(sshbuf_ptr(buf), sshbuf_len(buf), f);
67ea2d8289Sdjm }
68ea2d8289Sdjm 
6915b55daeSdjm char *
sshbuf_dtob16(struct sshbuf * buf)7015b55daeSdjm sshbuf_dtob16(struct sshbuf *buf)
7115b55daeSdjm {
7215b55daeSdjm 	size_t i, j, len = sshbuf_len(buf);
7315b55daeSdjm 	const u_char *p = sshbuf_ptr(buf);
7415b55daeSdjm 	char *ret;
7515b55daeSdjm 	const char hex[] = "0123456789abcdef";
7615b55daeSdjm 
7715b55daeSdjm 	if (len == 0)
7815b55daeSdjm 		return strdup("");
7915b55daeSdjm 	if (SIZE_MAX / 2 <= len || (ret = malloc(len * 2 + 1)) == NULL)
8015b55daeSdjm 		return NULL;
8115b55daeSdjm 	for (i = j = 0; i < len; i++) {
8215b55daeSdjm 		ret[j++] = hex[(p[i] >> 4) & 0xf];
8315b55daeSdjm 		ret[j++] = hex[p[i] & 0xf];
8415b55daeSdjm 	}
8515b55daeSdjm 	ret[j] = '\0';
8615b55daeSdjm 	return ret;
8715b55daeSdjm }
8815b55daeSdjm 
89bbb0e5b6Sdjm int
sshbuf_dtob64(const struct sshbuf * d,struct sshbuf * b64,int wrap)90bbb0e5b6Sdjm sshbuf_dtob64(const struct sshbuf *d, struct sshbuf *b64, int wrap)
9115b55daeSdjm {
92bbb0e5b6Sdjm 	size_t i, slen = 0;
93bbb0e5b6Sdjm 	char *s = NULL;
94bbb0e5b6Sdjm 	int r;
95bbb0e5b6Sdjm 
96bbb0e5b6Sdjm 	if (d == NULL || b64 == NULL || sshbuf_len(d) >= SIZE_MAX / 2)
97bbb0e5b6Sdjm 		return SSH_ERR_INVALID_ARGUMENT;
98bbb0e5b6Sdjm 	if (sshbuf_len(d) == 0)
99bbb0e5b6Sdjm 		return 0;
100bbb0e5b6Sdjm 	slen = ((sshbuf_len(d) + 2) / 3) * 4 + 1;
101bbb0e5b6Sdjm 	if ((s = malloc(slen)) == NULL)
102bbb0e5b6Sdjm 		return SSH_ERR_ALLOC_FAIL;
103bbb0e5b6Sdjm 	if (b64_ntop(sshbuf_ptr(d), sshbuf_len(d), s, slen) == -1) {
104bbb0e5b6Sdjm 		r = SSH_ERR_INTERNAL_ERROR;
105bbb0e5b6Sdjm 		goto fail;
106bbb0e5b6Sdjm 	}
107bbb0e5b6Sdjm 	if (wrap) {
108bbb0e5b6Sdjm 		for (i = 0; s[i] != '\0'; i++) {
109bbb0e5b6Sdjm 			if ((r = sshbuf_put_u8(b64, s[i])) != 0)
110bbb0e5b6Sdjm 				goto fail;
111bbb0e5b6Sdjm 			if (i % 70 == 69 && (r = sshbuf_put_u8(b64, '\n')) != 0)
112bbb0e5b6Sdjm 				goto fail;
113bbb0e5b6Sdjm 		}
1145512dfc2Sdjm 		if ((i - 1) % 70 != 69 && (r = sshbuf_put_u8(b64, '\n')) != 0)
115bbb0e5b6Sdjm 			goto fail;
116bbb0e5b6Sdjm 	} else {
117bbb0e5b6Sdjm 		if ((r = sshbuf_put(b64, s, strlen(s))) != 0)
118bbb0e5b6Sdjm 			goto fail;
119bbb0e5b6Sdjm 	}
120bbb0e5b6Sdjm 	/* Success */
121bbb0e5b6Sdjm 	r = 0;
122bbb0e5b6Sdjm  fail:
123bbb0e5b6Sdjm 	freezero(s, slen);
124bbb0e5b6Sdjm 	return r;
125bbb0e5b6Sdjm }
126bbb0e5b6Sdjm 
127bbb0e5b6Sdjm char *
sshbuf_dtob64_string(const struct sshbuf * buf,int wrap)128bbb0e5b6Sdjm sshbuf_dtob64_string(const struct sshbuf *buf, int wrap)
129bbb0e5b6Sdjm {
130bbb0e5b6Sdjm 	struct sshbuf *tmp;
13115b55daeSdjm 	char *ret;
13215b55daeSdjm 
133bbb0e5b6Sdjm 	if ((tmp = sshbuf_new()) == NULL)
13415b55daeSdjm 		return NULL;
135bbb0e5b6Sdjm 	if (sshbuf_dtob64(buf, tmp, wrap) != 0) {
136bbb0e5b6Sdjm 		sshbuf_free(tmp);
13715b55daeSdjm 		return NULL;
13815b55daeSdjm 	}
139bbb0e5b6Sdjm 	ret = sshbuf_dup_string(tmp);
140bbb0e5b6Sdjm 	sshbuf_free(tmp);
14115b55daeSdjm 	return ret;
14215b55daeSdjm }
14315b55daeSdjm 
14415b55daeSdjm int
sshbuf_b64tod(struct sshbuf * buf,const char * b64)14515b55daeSdjm sshbuf_b64tod(struct sshbuf *buf, const char *b64)
14615b55daeSdjm {
14715b55daeSdjm 	size_t plen = strlen(b64);
14815b55daeSdjm 	int nlen, r;
14915b55daeSdjm 	u_char *p;
15015b55daeSdjm 
15115b55daeSdjm 	if (plen == 0)
15215b55daeSdjm 		return 0;
15315b55daeSdjm 	if ((p = malloc(plen)) == NULL)
15415b55daeSdjm 		return SSH_ERR_ALLOC_FAIL;
15515b55daeSdjm 	if ((nlen = b64_pton(b64, p, plen)) < 0) {
156c9831b39Sjsg 		freezero(p, plen);
15715b55daeSdjm 		return SSH_ERR_INVALID_FORMAT;
15815b55daeSdjm 	}
15915b55daeSdjm 	if ((r = sshbuf_put(buf, p, nlen)) < 0) {
160c9831b39Sjsg 		freezero(p, plen);
16115b55daeSdjm 		return r;
16215b55daeSdjm 	}
163c9831b39Sjsg 	freezero(p, plen);
16415b55daeSdjm 	return 0;
16515b55daeSdjm }
16615b55daeSdjm 
167d5e61526Sdjm int
sshbuf_dtourlb64(const struct sshbuf * d,struct sshbuf * b64,int wrap)168d5e61526Sdjm sshbuf_dtourlb64(const struct sshbuf *d, struct sshbuf *b64, int wrap)
169d5e61526Sdjm {
170d5e61526Sdjm 	int r = SSH_ERR_INTERNAL_ERROR;
171d5e61526Sdjm 	u_char *p;
172d5e61526Sdjm 	struct sshbuf *b = NULL;
173d5e61526Sdjm 	size_t i, l;
174d5e61526Sdjm 
175d5e61526Sdjm 	if ((b = sshbuf_new()) == NULL)
176d5e61526Sdjm 		return SSH_ERR_ALLOC_FAIL;
177d5e61526Sdjm 	/* Encode using regular base64; we'll transform it once done */
178d5e61526Sdjm 	if ((r = sshbuf_dtob64(d, b, wrap)) != 0)
179d5e61526Sdjm 		goto out;
180d5e61526Sdjm 	/* remove padding from end of encoded string*/
181d5e61526Sdjm 	for (;;) {
182d5e61526Sdjm 		l = sshbuf_len(b);
183d5e61526Sdjm 		if (l <= 1 || sshbuf_ptr(b) == NULL) {
184d5e61526Sdjm 			r = SSH_ERR_INTERNAL_ERROR;
185d5e61526Sdjm 			goto out;
186d5e61526Sdjm 		}
187d5e61526Sdjm 		if (sshbuf_ptr(b)[l - 1] != '=')
188d5e61526Sdjm 			break;
189d5e61526Sdjm 		if ((r = sshbuf_consume_end(b, 1)) != 0)
190d5e61526Sdjm 			goto out;
191d5e61526Sdjm 	}
192d5e61526Sdjm 	/* Replace characters with rfc4648 equivalents */
193d5e61526Sdjm 	l = sshbuf_len(b);
194d5e61526Sdjm 	if ((p = sshbuf_mutable_ptr(b)) == NULL) {
195d5e61526Sdjm 		r = SSH_ERR_INTERNAL_ERROR;
196d5e61526Sdjm 		goto out;
197d5e61526Sdjm 	}
198d5e61526Sdjm 	for (i = 0; i < l; i++) {
199d5e61526Sdjm 		if (p[i] == '+')
200d5e61526Sdjm 			p[i] = '-';
201d5e61526Sdjm 		else if (p[i] == '/')
202d5e61526Sdjm 			p[i] = '_';
203d5e61526Sdjm 	}
204d5e61526Sdjm 	r = sshbuf_putb(b64, b);
205d5e61526Sdjm  out:
206d5e61526Sdjm 	sshbuf_free(b);
207d5e61526Sdjm 	return r;
208d5e61526Sdjm }
209d5e61526Sdjm 
2109a1b52afSdjm char *
sshbuf_dup_string(struct sshbuf * buf)2119a1b52afSdjm sshbuf_dup_string(struct sshbuf *buf)
2129a1b52afSdjm {
2139a1b52afSdjm 	const u_char *p = NULL, *s = sshbuf_ptr(buf);
2149a1b52afSdjm 	size_t l = sshbuf_len(buf);
2159a1b52afSdjm 	char *r;
2169a1b52afSdjm 
2179a1b52afSdjm 	if (s == NULL || l > SIZE_MAX)
2189a1b52afSdjm 		return NULL;
2199a1b52afSdjm 	/* accept a nul only as the last character in the buffer */
2209a1b52afSdjm 	if (l > 0 && (p = memchr(s, '\0', l)) != NULL) {
2219a1b52afSdjm 		if (p != s + l - 1)
2229a1b52afSdjm 			return NULL;
2239a1b52afSdjm 		l--; /* the nul is put back below */
2249a1b52afSdjm 	}
2259a1b52afSdjm 	if ((r = malloc(l + 1)) == NULL)
2269a1b52afSdjm 		return NULL;
2279a1b52afSdjm 	if (l > 0)
2289a1b52afSdjm 		memcpy(r, s, l);
2299a1b52afSdjm 	r[l] = '\0';
2309a1b52afSdjm 	return r;
2319a1b52afSdjm }
2329a1b52afSdjm 
2339e7a6b9fSdjm int
sshbuf_cmp(const struct sshbuf * b,size_t offset,const void * s,size_t len)2349e7a6b9fSdjm sshbuf_cmp(const struct sshbuf *b, size_t offset,
235d8ff1cfbSdjm     const void *s, size_t len)
2369e7a6b9fSdjm {
2379e7a6b9fSdjm 	if (sshbuf_ptr(b) == NULL)
2389e7a6b9fSdjm 		return SSH_ERR_INTERNAL_ERROR;
2399e7a6b9fSdjm 	if (offset > SSHBUF_SIZE_MAX || len > SSHBUF_SIZE_MAX || len == 0)
2409e7a6b9fSdjm 		return SSH_ERR_INVALID_ARGUMENT;
2419e7a6b9fSdjm 	if (offset + len > sshbuf_len(b))
2429e7a6b9fSdjm 		return SSH_ERR_MESSAGE_INCOMPLETE;
2439e7a6b9fSdjm 	if (timingsafe_bcmp(sshbuf_ptr(b) + offset, s, len) != 0)
2449e7a6b9fSdjm 		return SSH_ERR_INVALID_FORMAT;
2459e7a6b9fSdjm 	return 0;
2469e7a6b9fSdjm }
2479e7a6b9fSdjm 
2489e7a6b9fSdjm int
sshbuf_find(const struct sshbuf * b,size_t start_offset,const void * s,size_t len,size_t * offsetp)2499e7a6b9fSdjm sshbuf_find(const struct sshbuf *b, size_t start_offset,
250d8ff1cfbSdjm     const void *s, size_t len, size_t *offsetp)
2519e7a6b9fSdjm {
2529e7a6b9fSdjm 	void *p;
2539e7a6b9fSdjm 
2549e7a6b9fSdjm 	if (offsetp != NULL)
2559e7a6b9fSdjm 		*offsetp = 0;
2569e7a6b9fSdjm 	if (sshbuf_ptr(b) == NULL)
2579e7a6b9fSdjm 		return SSH_ERR_INTERNAL_ERROR;
2589e7a6b9fSdjm 	if (start_offset > SSHBUF_SIZE_MAX || len > SSHBUF_SIZE_MAX || len == 0)
2599e7a6b9fSdjm 		return SSH_ERR_INVALID_ARGUMENT;
2609e7a6b9fSdjm 	if (start_offset > sshbuf_len(b) || start_offset + len > sshbuf_len(b))
2619e7a6b9fSdjm 		return SSH_ERR_MESSAGE_INCOMPLETE;
2629e7a6b9fSdjm 	if ((p = memmem(sshbuf_ptr(b) + start_offset,
2639e7a6b9fSdjm 	    sshbuf_len(b) - start_offset, s, len)) == NULL)
2649e7a6b9fSdjm 		return SSH_ERR_INVALID_FORMAT;
2659e7a6b9fSdjm 	if (offsetp != NULL)
2669e7a6b9fSdjm 		*offsetp = (const u_char *)p - sshbuf_ptr(b);
2679e7a6b9fSdjm 	return 0;
2689e7a6b9fSdjm }
269*41c2b893Sdjm 
270*41c2b893Sdjm int
sshbuf_read(int fd,struct sshbuf * buf,size_t maxlen,size_t * rlen)271*41c2b893Sdjm sshbuf_read(int fd, struct sshbuf *buf, size_t maxlen, size_t *rlen)
272*41c2b893Sdjm {
273*41c2b893Sdjm 	int r, oerrno;
274*41c2b893Sdjm 	size_t adjust;
275*41c2b893Sdjm 	ssize_t rr;
276*41c2b893Sdjm 	u_char *d;
277*41c2b893Sdjm 
278*41c2b893Sdjm 	if (rlen != NULL)
279*41c2b893Sdjm 		*rlen = 0;
280*41c2b893Sdjm 	if ((r = sshbuf_reserve(buf, maxlen, &d)) != 0)
281*41c2b893Sdjm 		return r;
282*41c2b893Sdjm 	rr = read(fd, d, maxlen);
283*41c2b893Sdjm 	oerrno = errno;
284*41c2b893Sdjm 
285*41c2b893Sdjm 	/* Adjust the buffer to include only what was actually read */
286*41c2b893Sdjm 	if ((adjust = maxlen - (rr > 0 ? rr : 0)) != 0) {
287*41c2b893Sdjm 		if ((r = sshbuf_consume_end(buf, adjust)) != 0) {
288*41c2b893Sdjm 			/* avoid returning uninitialised data to caller */
289*41c2b893Sdjm 			memset(d + rr, '\0', adjust);
290*41c2b893Sdjm 			return SSH_ERR_INTERNAL_ERROR; /* shouldn't happen */
291*41c2b893Sdjm 		}
292*41c2b893Sdjm 	}
293*41c2b893Sdjm 	if (rr < 0) {
294*41c2b893Sdjm 		errno = oerrno;
295*41c2b893Sdjm 		return SSH_ERR_SYSTEM_ERROR;
296*41c2b893Sdjm 	} else if (rr == 0) {
297*41c2b893Sdjm 		errno = EPIPE;
298*41c2b893Sdjm 		return SSH_ERR_SYSTEM_ERROR;
299*41c2b893Sdjm 	}
300*41c2b893Sdjm 	/* success */
301*41c2b893Sdjm 	if (rlen != NULL)
302*41c2b893Sdjm 		*rlen = (size_t)rr;
303*41c2b893Sdjm 	return 0;
304*41c2b893Sdjm }
305