Home | History | Annotate | Line # | Download | only in dist
      1 /*	$OpenBSD: sshbuf-misc.c,v 1.23 2026/03/28 05:10:25 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 #include "includes.h"
     18 __RCSID("$NetBSD: sshbuf-misc.c,v 1.16 2026/04/08 18:58:41 christos Exp $");
     19 
     20 #include <sys/types.h>
     21 #include <sys/socket.h>
     22 #include <netinet/in.h>
     23 #include <errno.h>
     24 #include <stdlib.h>
     25 #include <stdint.h>
     26 #include <stdio.h>
     27 #include <limits.h>
     28 #include <string.h>
     29 #include <resolv.h>
     30 #include <ctype.h>
     31 #include <unistd.h>
     32 
     33 #include "ssherr.h"
     34 #define SSHBUF_INTERNAL
     35 #include "sshbuf.h"
     36 
     37 void
     38 sshbuf_dump_data(const void *s, size_t len, FILE *f)
     39 {
     40 	size_t i, j;
     41 	const u_char *p = (const u_char *)s;
     42 
     43 	for (i = 0; i < len; i += 16) {
     44 		fprintf(f, "%.4zu: ", i);
     45 		for (j = i; j < i + 16; j++) {
     46 			if (j < len)
     47 				fprintf(f, "%02x ", p[j]);
     48 			else
     49 				fprintf(f, "   ");
     50 		}
     51 		fprintf(f, " ");
     52 		for (j = i; j < i + 16; j++) {
     53 			if (j < len) {
     54 				if  (isascii(p[j]) && isprint(p[j]))
     55 					fprintf(f, "%c", p[j]);
     56 				else
     57 					fprintf(f, ".");
     58 			}
     59 		}
     60 		fprintf(f, "\n");
     61 	}
     62 }
     63 
     64 void
     65 sshbuf_dump(const struct sshbuf *buf, FILE *f)
     66 {
     67 	fprintf(f, "buffer len = %zu\n", sshbuf_len(buf));
     68 	sshbuf_dump_data(sshbuf_ptr(buf), sshbuf_len(buf), f);
     69 }
     70 
     71 char *
     72 sshbuf_dtob16(const struct sshbuf *buf)
     73 {
     74 	size_t i, j, len = sshbuf_len(buf);
     75 	const u_char *p = sshbuf_ptr(buf);
     76 	char *ret;
     77 	const char hex[] = "0123456789abcdef";
     78 
     79 	if (len == 0)
     80 		return strdup("");
     81 	if (SIZE_MAX / 2 <= len || (ret = malloc(len * 2 + 1)) == NULL)
     82 		return NULL;
     83 	for (i = j = 0; i < len; i++) {
     84 		ret[j++] = hex[(p[i] >> 4) & 0xf];
     85 		ret[j++] = hex[p[i] & 0xf];
     86 	}
     87 	ret[j] = '\0';
     88 	return ret;
     89 }
     90 
     91 static int
     92 b16tod(const char v)
     93 {
     94 	if (v >= '0' && v <= '9')
     95 		return v - '0';
     96 	if (v >= 'a' && v <= 'f')
     97 		return 10 + v - 'a';
     98 	if (v >= 'A' && v <= 'F')
     99 		return 10 + v - 'A';
    100 	return -1;
    101 }
    102 
    103 struct sshbuf *
    104 sshbuf_b16tod(const char *b16)
    105 {
    106 	struct sshbuf *ret;
    107 	size_t o;
    108 	int r, v1, v2;
    109 
    110 	if ((ret = sshbuf_new()) == NULL)
    111 		return NULL;
    112 	for (o = 0; b16[o] != '\0'; o += 2) {
    113 		if ((v1 = b16tod(b16[o])) == -1 ||
    114 		    (v2 = b16tod(b16[o + 1])) == -1) {
    115 			sshbuf_free(ret);
    116 			return NULL;
    117 		}
    118 		if ((r = sshbuf_put_u8(ret, (u_char)((v1 << 4) | v2))) != 0) {
    119 			sshbuf_free(ret);
    120 			return NULL;
    121 		}
    122 	}
    123 	/* success */
    124 	return ret;
    125 }
    126 
    127 int
    128 sshbuf_dtob64(const struct sshbuf *d, struct sshbuf *b64, int wrap)
    129 {
    130 	size_t i, slen = 0;
    131 	char *s = NULL;
    132 	int r;
    133 
    134 	if (d == NULL || b64 == NULL || sshbuf_len(d) >= SIZE_MAX / 2)
    135 		return SSH_ERR_INVALID_ARGUMENT;
    136 	if (sshbuf_len(d) == 0)
    137 		return 0;
    138 	slen = ((sshbuf_len(d) + 2) / 3) * 4 + 1;
    139 	if ((s = malloc(slen)) == NULL)
    140 		return SSH_ERR_ALLOC_FAIL;
    141 	if (b64_ntop(sshbuf_ptr(d), sshbuf_len(d), s, slen) == -1) {
    142 		r = SSH_ERR_INTERNAL_ERROR;
    143 		goto fail;
    144 	}
    145 	if (wrap) {
    146 		for (i = 0; s[i] != '\0'; i++) {
    147 			if ((r = sshbuf_put_u8(b64, s[i])) != 0)
    148 				goto fail;
    149 			if (i % 70 == 69 && (r = sshbuf_put_u8(b64, '\n')) != 0)
    150 				goto fail;
    151 		}
    152 		if ((i - 1) % 70 != 69 && (r = sshbuf_put_u8(b64, '\n')) != 0)
    153 			goto fail;
    154 	} else {
    155 		if ((r = sshbuf_put(b64, s, strlen(s))) != 0)
    156 			goto fail;
    157 	}
    158 	/* Success */
    159 	r = 0;
    160  fail:
    161 	freezero(s, slen);
    162 	return r;
    163 }
    164 
    165 char *
    166 sshbuf_dtob64_string(const struct sshbuf *buf, int wrap)
    167 {
    168 	struct sshbuf *tmp;
    169 	char *ret;
    170 
    171 	if ((tmp = sshbuf_new()) == NULL)
    172 		return NULL;
    173 	if (sshbuf_dtob64(buf, tmp, wrap) != 0) {
    174 		sshbuf_free(tmp);
    175 		return NULL;
    176 	}
    177 	ret = sshbuf_dup_string(tmp);
    178 	sshbuf_free(tmp);
    179 	return ret;
    180 }
    181 
    182 int
    183 sshbuf_b64tod(struct sshbuf *buf, const char *b64)
    184 {
    185 	size_t plen = strlen(b64);
    186 	int nlen, r;
    187 	u_char *p;
    188 
    189 	if (plen == 0)
    190 		return 0;
    191 	if ((p = malloc(plen)) == NULL)
    192 		return SSH_ERR_ALLOC_FAIL;
    193 	if ((nlen = b64_pton(b64, p, plen)) < 0) {
    194 		freezero(p, plen);
    195 		return SSH_ERR_INVALID_FORMAT;
    196 	}
    197 	if ((r = sshbuf_put(buf, p, nlen)) < 0) {
    198 		freezero(p, plen);
    199 		return r;
    200 	}
    201 	freezero(p, plen);
    202 	return 0;
    203 }
    204 
    205 int
    206 sshbuf_dtourlb64(const struct sshbuf *d, struct sshbuf *b64, int wrap)
    207 {
    208 	int r = SSH_ERR_INTERNAL_ERROR;
    209 	u_char *p;
    210 	struct sshbuf *b = NULL;
    211 	size_t i, l;
    212 
    213 	if (sshbuf_len(d) == 0)
    214 		return 0;
    215 
    216 	if ((b = sshbuf_new()) == NULL)
    217 		return SSH_ERR_ALLOC_FAIL;
    218 	/* Encode using regular base64; we'll transform it once done */
    219 	if ((r = sshbuf_dtob64(d, b, wrap)) != 0)
    220 		goto out;
    221 	/* remove padding from end of encoded string*/
    222 	for (;;) {
    223 		l = sshbuf_len(b);
    224 		if (l <= 1 || sshbuf_ptr(b) == NULL) {
    225 			r = SSH_ERR_INTERNAL_ERROR;
    226 			goto out;
    227 		}
    228 		if (sshbuf_ptr(b)[l - 1] != '=')
    229 			break;
    230 		if ((r = sshbuf_consume_end(b, 1)) != 0)
    231 			goto out;
    232 	}
    233 	/* Replace characters with rfc4648 equivalents */
    234 	l = sshbuf_len(b);
    235 	if ((p = sshbuf_mutable_ptr(b)) == NULL) {
    236 		r = SSH_ERR_INTERNAL_ERROR;
    237 		goto out;
    238 	}
    239 	for (i = 0; i < l; i++) {
    240 		if (p[i] == '+')
    241 			p[i] = '-';
    242 		else if (p[i] == '/')
    243 			p[i] = '_';
    244 	}
    245 	r = sshbuf_putb(b64, b);
    246  out:
    247 	sshbuf_free(b);
    248 	return r;
    249 }
    250 
    251 char *
    252 sshbuf_dup_string(struct sshbuf *buf)
    253 {
    254 	const u_char *p = NULL, *s = sshbuf_ptr(buf);
    255 	size_t l = sshbuf_len(buf);
    256 	char *r;
    257 
    258 	if (s == NULL || l >= SIZE_MAX)
    259 		return NULL;
    260 	/* accept a nul only as the last character in the buffer */
    261 	if (l > 0 && (p = memchr(s, '\0', l)) != NULL) {
    262 		if (p != s + l - 1)
    263 			return NULL;
    264 		l--; /* the nul is put back below */
    265 	}
    266 	if ((r = malloc(l + 1)) == NULL)
    267 		return NULL;
    268 	if (l > 0)
    269 		memcpy(r, s, l);
    270 	r[l] = '\0';
    271 	return r;
    272 }
    273 
    274 int
    275 sshbuf_cmp(const struct sshbuf *b, size_t offset,
    276     const void *s, size_t len)
    277 {
    278 	if (sshbuf_ptr(b) == NULL)
    279 		return SSH_ERR_INTERNAL_ERROR;
    280 	if (offset > SSHBUF_SIZE_MAX || len > SSHBUF_SIZE_MAX || len == 0)
    281 		return SSH_ERR_INVALID_ARGUMENT;
    282 	if (offset + len > sshbuf_len(b))
    283 		return SSH_ERR_MESSAGE_INCOMPLETE;
    284 	if (timingsafe_bcmp(sshbuf_ptr(b) + offset, s, len) != 0)
    285 		return SSH_ERR_INVALID_FORMAT;
    286 	return 0;
    287 }
    288 
    289 int
    290 sshbuf_equals(const struct sshbuf *a, const struct sshbuf *b)
    291 {
    292 	if (sshbuf_ptr(a) == NULL || sshbuf_ptr(b) == NULL)
    293 		return SSH_ERR_INTERNAL_ERROR;
    294 	if (sshbuf_len(a) != sshbuf_len(b))
    295 		return SSH_ERR_MESSAGE_INCOMPLETE;
    296 	if (sshbuf_len(a) == 0)
    297 		return 0;
    298 	if (memcmp(sshbuf_ptr(a), sshbuf_ptr(b), sshbuf_len(a)) != 0)
    299 		return SSH_ERR_INVALID_FORMAT;
    300 	return 0;
    301 }
    302 
    303 int
    304 sshbuf_find(const struct sshbuf *b, size_t start_offset,
    305     const void *s, size_t len, size_t *offsetp)
    306 {
    307 	void *p;
    308 
    309 	if (offsetp != NULL)
    310 		*offsetp = 0;
    311 	if (sshbuf_ptr(b) == NULL)
    312 		return SSH_ERR_INTERNAL_ERROR;
    313 	if (start_offset > SSHBUF_SIZE_MAX || len > SSHBUF_SIZE_MAX || len == 0)
    314 		return SSH_ERR_INVALID_ARGUMENT;
    315 	if (start_offset > sshbuf_len(b) || start_offset + len > sshbuf_len(b))
    316 		return SSH_ERR_MESSAGE_INCOMPLETE;
    317 	if ((p = memmem(sshbuf_ptr(b) + start_offset,
    318 	    sshbuf_len(b) - start_offset, s, len)) == NULL)
    319 		return SSH_ERR_INVALID_FORMAT;
    320 	if (offsetp != NULL)
    321 		*offsetp = (const u_char *)p - sshbuf_ptr(b);
    322 	return 0;
    323 }
    324 
    325 int
    326 sshbuf_read(int fd, struct sshbuf *buf, size_t maxlen, size_t *rlen)
    327 {
    328 	int r, oerrno;
    329 	size_t adjust;
    330 	ssize_t rr;
    331 	u_char *d;
    332 
    333 	if (rlen != NULL)
    334 		*rlen = 0;
    335 	if ((r = sshbuf_reserve(buf, maxlen, &d)) != 0)
    336 		return r;
    337 	rr = read(fd, d, maxlen);
    338 	oerrno = errno;
    339 
    340 	/* Adjust the buffer to include only what was actually read */
    341 	if ((adjust = maxlen - (rr > 0 ? rr : 0)) != 0) {
    342 		if ((r = sshbuf_consume_end(buf, adjust)) != 0) {
    343 			/* avoid returning uninitialised data to caller */
    344 			memset(d + rr, '\0', adjust);
    345 			return SSH_ERR_INTERNAL_ERROR; /* shouldn't happen */
    346 		}
    347 	}
    348 	if (rr < 0) {
    349 		errno = oerrno;
    350 		return SSH_ERR_SYSTEM_ERROR;
    351 	} else if (rr == 0) {
    352 		errno = EPIPE;
    353 		return SSH_ERR_SYSTEM_ERROR;
    354 	}
    355 	/* success */
    356 	if (rlen != NULL)
    357 		*rlen = (size_t)rr;
    358 	return 0;
    359 }
    360