1*50a69bb5SSascha Wildner /* $OpenBSD: sshbuf-misc.c,v 1.17 2021/08/11 05:21:32 djm Exp $ */ 236e94dc5SPeter Avalos /* 336e94dc5SPeter Avalos * Copyright (c) 2011 Damien Miller 436e94dc5SPeter Avalos * 536e94dc5SPeter Avalos * Permission to use, copy, modify, and distribute this software for any 636e94dc5SPeter Avalos * purpose with or without fee is hereby granted, provided that the above 736e94dc5SPeter Avalos * copyright notice and this permission notice appear in all copies. 836e94dc5SPeter Avalos * 936e94dc5SPeter Avalos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1036e94dc5SPeter Avalos * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1136e94dc5SPeter Avalos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1236e94dc5SPeter Avalos * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1336e94dc5SPeter Avalos * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1436e94dc5SPeter Avalos * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1536e94dc5SPeter Avalos * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1636e94dc5SPeter Avalos */ 1736e94dc5SPeter Avalos 1836e94dc5SPeter Avalos #include "includes.h" 1936e94dc5SPeter Avalos 2036e94dc5SPeter Avalos #include <sys/types.h> 2136e94dc5SPeter Avalos #include <sys/socket.h> 2236e94dc5SPeter Avalos #include <netinet/in.h> 2336e94dc5SPeter Avalos #include <errno.h> 2436e94dc5SPeter Avalos #include <stdlib.h> 25e9778795SPeter Avalos #ifdef HAVE_STDINT_H 26e9778795SPeter Avalos # include <stdint.h> 27e9778795SPeter Avalos #endif 2836e94dc5SPeter Avalos #include <stdio.h> 2936e94dc5SPeter Avalos #include <limits.h> 3036e94dc5SPeter Avalos #include <string.h> 3136e94dc5SPeter Avalos #include <resolv.h> 3236e94dc5SPeter Avalos #include <ctype.h> 3336e94dc5SPeter Avalos 3436e94dc5SPeter Avalos #include "ssherr.h" 3536e94dc5SPeter Avalos #define SSHBUF_INTERNAL 3636e94dc5SPeter Avalos #include "sshbuf.h" 3736e94dc5SPeter Avalos 3836e94dc5SPeter Avalos void 3936e94dc5SPeter Avalos sshbuf_dump_data(const void *s, size_t len, FILE *f) 4036e94dc5SPeter Avalos { 4136e94dc5SPeter Avalos size_t i, j; 4236e94dc5SPeter Avalos const u_char *p = (const u_char *)s; 4336e94dc5SPeter Avalos 4436e94dc5SPeter Avalos for (i = 0; i < len; i += 16) { 45e9778795SPeter Avalos fprintf(f, "%.4zu: ", i); 4636e94dc5SPeter Avalos for (j = i; j < i + 16; j++) { 4736e94dc5SPeter Avalos if (j < len) 4836e94dc5SPeter Avalos fprintf(f, "%02x ", p[j]); 4936e94dc5SPeter Avalos else 5036e94dc5SPeter Avalos fprintf(f, " "); 5136e94dc5SPeter Avalos } 5236e94dc5SPeter Avalos fprintf(f, " "); 5336e94dc5SPeter Avalos for (j = i; j < i + 16; j++) { 5436e94dc5SPeter Avalos if (j < len) { 5536e94dc5SPeter Avalos if (isascii(p[j]) && isprint(p[j])) 5636e94dc5SPeter Avalos fprintf(f, "%c", p[j]); 5736e94dc5SPeter Avalos else 5836e94dc5SPeter Avalos fprintf(f, "."); 5936e94dc5SPeter Avalos } 6036e94dc5SPeter Avalos } 6136e94dc5SPeter Avalos fprintf(f, "\n"); 6236e94dc5SPeter Avalos } 6336e94dc5SPeter Avalos } 6436e94dc5SPeter Avalos 6536e94dc5SPeter Avalos void 66*50a69bb5SSascha Wildner sshbuf_dump(const struct sshbuf *buf, FILE *f) 6736e94dc5SPeter Avalos { 68*50a69bb5SSascha Wildner fprintf(f, "buffer len = %zu\n", sshbuf_len(buf)); 6936e94dc5SPeter Avalos sshbuf_dump_data(sshbuf_ptr(buf), sshbuf_len(buf), f); 7036e94dc5SPeter Avalos } 7136e94dc5SPeter Avalos 7236e94dc5SPeter Avalos char * 7336e94dc5SPeter Avalos sshbuf_dtob16(struct sshbuf *buf) 7436e94dc5SPeter Avalos { 7536e94dc5SPeter Avalos size_t i, j, len = sshbuf_len(buf); 7636e94dc5SPeter Avalos const u_char *p = sshbuf_ptr(buf); 7736e94dc5SPeter Avalos char *ret; 7836e94dc5SPeter Avalos const char hex[] = "0123456789abcdef"; 7936e94dc5SPeter Avalos 8036e94dc5SPeter Avalos if (len == 0) 8136e94dc5SPeter Avalos return strdup(""); 8236e94dc5SPeter Avalos if (SIZE_MAX / 2 <= len || (ret = malloc(len * 2 + 1)) == NULL) 8336e94dc5SPeter Avalos return NULL; 8436e94dc5SPeter Avalos for (i = j = 0; i < len; i++) { 8536e94dc5SPeter Avalos ret[j++] = hex[(p[i] >> 4) & 0xf]; 8636e94dc5SPeter Avalos ret[j++] = hex[p[i] & 0xf]; 8736e94dc5SPeter Avalos } 8836e94dc5SPeter Avalos ret[j] = '\0'; 8936e94dc5SPeter Avalos return ret; 9036e94dc5SPeter Avalos } 9136e94dc5SPeter Avalos 920cbfa66cSDaniel Fojt int 930cbfa66cSDaniel Fojt sshbuf_dtob64(const struct sshbuf *d, struct sshbuf *b64, int wrap) 9436e94dc5SPeter Avalos { 950cbfa66cSDaniel Fojt size_t i, slen = 0; 960cbfa66cSDaniel Fojt char *s = NULL; 9736e94dc5SPeter Avalos int r; 9836e94dc5SPeter Avalos 990cbfa66cSDaniel Fojt if (d == NULL || b64 == NULL || sshbuf_len(d) >= SIZE_MAX / 2) 1000cbfa66cSDaniel Fojt return SSH_ERR_INVALID_ARGUMENT; 1010cbfa66cSDaniel Fojt if (sshbuf_len(d) == 0) 1020cbfa66cSDaniel Fojt return 0; 1030cbfa66cSDaniel Fojt slen = ((sshbuf_len(d) + 2) / 3) * 4 + 1; 1040cbfa66cSDaniel Fojt if ((s = malloc(slen)) == NULL) 1050cbfa66cSDaniel Fojt return SSH_ERR_ALLOC_FAIL; 1060cbfa66cSDaniel Fojt if (b64_ntop(sshbuf_ptr(d), sshbuf_len(d), s, slen) == -1) { 1070cbfa66cSDaniel Fojt r = SSH_ERR_INTERNAL_ERROR; 1080cbfa66cSDaniel Fojt goto fail; 1090cbfa66cSDaniel Fojt } 1100cbfa66cSDaniel Fojt if (wrap) { 1110cbfa66cSDaniel Fojt for (i = 0; s[i] != '\0'; i++) { 1120cbfa66cSDaniel Fojt if ((r = sshbuf_put_u8(b64, s[i])) != 0) 1130cbfa66cSDaniel Fojt goto fail; 1140cbfa66cSDaniel Fojt if (i % 70 == 69 && (r = sshbuf_put_u8(b64, '\n')) != 0) 1150cbfa66cSDaniel Fojt goto fail; 1160cbfa66cSDaniel Fojt } 1170cbfa66cSDaniel Fojt if ((i - 1) % 70 != 69 && (r = sshbuf_put_u8(b64, '\n')) != 0) 1180cbfa66cSDaniel Fojt goto fail; 1190cbfa66cSDaniel Fojt } else { 1200cbfa66cSDaniel Fojt if ((r = sshbuf_put(b64, s, strlen(s))) != 0) 1210cbfa66cSDaniel Fojt goto fail; 1220cbfa66cSDaniel Fojt } 1230cbfa66cSDaniel Fojt /* Success */ 1240cbfa66cSDaniel Fojt r = 0; 1250cbfa66cSDaniel Fojt fail: 1260cbfa66cSDaniel Fojt freezero(s, slen); 1270cbfa66cSDaniel Fojt return r; 1280cbfa66cSDaniel Fojt } 1290cbfa66cSDaniel Fojt 1300cbfa66cSDaniel Fojt char * 1310cbfa66cSDaniel Fojt sshbuf_dtob64_string(const struct sshbuf *buf, int wrap) 1320cbfa66cSDaniel Fojt { 1330cbfa66cSDaniel Fojt struct sshbuf *tmp; 1340cbfa66cSDaniel Fojt char *ret; 1350cbfa66cSDaniel Fojt 1360cbfa66cSDaniel Fojt if ((tmp = sshbuf_new()) == NULL) 13736e94dc5SPeter Avalos return NULL; 1380cbfa66cSDaniel Fojt if (sshbuf_dtob64(buf, tmp, wrap) != 0) { 1390cbfa66cSDaniel Fojt sshbuf_free(tmp); 14036e94dc5SPeter Avalos return NULL; 14136e94dc5SPeter Avalos } 1420cbfa66cSDaniel Fojt ret = sshbuf_dup_string(tmp); 1430cbfa66cSDaniel Fojt sshbuf_free(tmp); 14436e94dc5SPeter Avalos return ret; 14536e94dc5SPeter Avalos } 14636e94dc5SPeter Avalos 14736e94dc5SPeter Avalos int 14836e94dc5SPeter Avalos sshbuf_b64tod(struct sshbuf *buf, const char *b64) 14936e94dc5SPeter Avalos { 15036e94dc5SPeter Avalos size_t plen = strlen(b64); 15136e94dc5SPeter Avalos int nlen, r; 15236e94dc5SPeter Avalos u_char *p; 15336e94dc5SPeter Avalos 15436e94dc5SPeter Avalos if (plen == 0) 15536e94dc5SPeter Avalos return 0; 15636e94dc5SPeter Avalos if ((p = malloc(plen)) == NULL) 15736e94dc5SPeter Avalos return SSH_ERR_ALLOC_FAIL; 15836e94dc5SPeter Avalos if ((nlen = b64_pton(b64, p, plen)) < 0) { 1590cbfa66cSDaniel Fojt freezero(p, plen); 16036e94dc5SPeter Avalos return SSH_ERR_INVALID_FORMAT; 16136e94dc5SPeter Avalos } 16236e94dc5SPeter Avalos if ((r = sshbuf_put(buf, p, nlen)) < 0) { 1630cbfa66cSDaniel Fojt freezero(p, plen); 16436e94dc5SPeter Avalos return r; 16536e94dc5SPeter Avalos } 1660cbfa66cSDaniel Fojt freezero(p, plen); 16736e94dc5SPeter Avalos return 0; 16836e94dc5SPeter Avalos } 16936e94dc5SPeter Avalos 170*50a69bb5SSascha Wildner int 171*50a69bb5SSascha Wildner sshbuf_dtourlb64(const struct sshbuf *d, struct sshbuf *b64, int wrap) 172*50a69bb5SSascha Wildner { 173*50a69bb5SSascha Wildner int r = SSH_ERR_INTERNAL_ERROR; 174*50a69bb5SSascha Wildner u_char *p; 175*50a69bb5SSascha Wildner struct sshbuf *b = NULL; 176*50a69bb5SSascha Wildner size_t i, l; 177*50a69bb5SSascha Wildner 178*50a69bb5SSascha Wildner if ((b = sshbuf_new()) == NULL) 179*50a69bb5SSascha Wildner return SSH_ERR_ALLOC_FAIL; 180*50a69bb5SSascha Wildner /* Encode using regular base64; we'll transform it once done */ 181*50a69bb5SSascha Wildner if ((r = sshbuf_dtob64(d, b, wrap)) != 0) 182*50a69bb5SSascha Wildner goto out; 183*50a69bb5SSascha Wildner /* remove padding from end of encoded string*/ 184*50a69bb5SSascha Wildner for (;;) { 185*50a69bb5SSascha Wildner l = sshbuf_len(b); 186*50a69bb5SSascha Wildner if (l <= 1 || sshbuf_ptr(b) == NULL) { 187*50a69bb5SSascha Wildner r = SSH_ERR_INTERNAL_ERROR; 188*50a69bb5SSascha Wildner goto out; 189*50a69bb5SSascha Wildner } 190*50a69bb5SSascha Wildner if (sshbuf_ptr(b)[l - 1] != '=') 191*50a69bb5SSascha Wildner break; 192*50a69bb5SSascha Wildner if ((r = sshbuf_consume_end(b, 1)) != 0) 193*50a69bb5SSascha Wildner goto out; 194*50a69bb5SSascha Wildner } 195*50a69bb5SSascha Wildner /* Replace characters with rfc4648 equivalents */ 196*50a69bb5SSascha Wildner l = sshbuf_len(b); 197*50a69bb5SSascha Wildner if ((p = sshbuf_mutable_ptr(b)) == NULL) { 198*50a69bb5SSascha Wildner r = SSH_ERR_INTERNAL_ERROR; 199*50a69bb5SSascha Wildner goto out; 200*50a69bb5SSascha Wildner } 201*50a69bb5SSascha Wildner for (i = 0; i < l; i++) { 202*50a69bb5SSascha Wildner if (p[i] == '+') 203*50a69bb5SSascha Wildner p[i] = '-'; 204*50a69bb5SSascha Wildner else if (p[i] == '/') 205*50a69bb5SSascha Wildner p[i] = '_'; 206*50a69bb5SSascha Wildner } 207*50a69bb5SSascha Wildner r = sshbuf_putb(b64, b); 208*50a69bb5SSascha Wildner out: 209*50a69bb5SSascha Wildner sshbuf_free(b); 210*50a69bb5SSascha Wildner return r; 211*50a69bb5SSascha Wildner } 212*50a69bb5SSascha Wildner 213e9778795SPeter Avalos char * 214e9778795SPeter Avalos sshbuf_dup_string(struct sshbuf *buf) 215e9778795SPeter Avalos { 216e9778795SPeter Avalos const u_char *p = NULL, *s = sshbuf_ptr(buf); 217e9778795SPeter Avalos size_t l = sshbuf_len(buf); 218e9778795SPeter Avalos char *r; 219e9778795SPeter Avalos 220e9778795SPeter Avalos if (s == NULL || l > SIZE_MAX) 221e9778795SPeter Avalos return NULL; 222e9778795SPeter Avalos /* accept a nul only as the last character in the buffer */ 223e9778795SPeter Avalos if (l > 0 && (p = memchr(s, '\0', l)) != NULL) { 224e9778795SPeter Avalos if (p != s + l - 1) 225e9778795SPeter Avalos return NULL; 226e9778795SPeter Avalos l--; /* the nul is put back below */ 227e9778795SPeter Avalos } 228e9778795SPeter Avalos if ((r = malloc(l + 1)) == NULL) 229e9778795SPeter Avalos return NULL; 230e9778795SPeter Avalos if (l > 0) 231e9778795SPeter Avalos memcpy(r, s, l); 232e9778795SPeter Avalos r[l] = '\0'; 233e9778795SPeter Avalos return r; 234e9778795SPeter Avalos } 235e9778795SPeter Avalos 2360cbfa66cSDaniel Fojt int 2370cbfa66cSDaniel Fojt sshbuf_cmp(const struct sshbuf *b, size_t offset, 2380cbfa66cSDaniel Fojt const void *s, size_t len) 2390cbfa66cSDaniel Fojt { 2400cbfa66cSDaniel Fojt if (sshbuf_ptr(b) == NULL) 2410cbfa66cSDaniel Fojt return SSH_ERR_INTERNAL_ERROR; 2420cbfa66cSDaniel Fojt if (offset > SSHBUF_SIZE_MAX || len > SSHBUF_SIZE_MAX || len == 0) 2430cbfa66cSDaniel Fojt return SSH_ERR_INVALID_ARGUMENT; 2440cbfa66cSDaniel Fojt if (offset + len > sshbuf_len(b)) 2450cbfa66cSDaniel Fojt return SSH_ERR_MESSAGE_INCOMPLETE; 2460cbfa66cSDaniel Fojt if (timingsafe_bcmp(sshbuf_ptr(b) + offset, s, len) != 0) 2470cbfa66cSDaniel Fojt return SSH_ERR_INVALID_FORMAT; 2480cbfa66cSDaniel Fojt return 0; 2490cbfa66cSDaniel Fojt } 2500cbfa66cSDaniel Fojt 2510cbfa66cSDaniel Fojt int 2520cbfa66cSDaniel Fojt sshbuf_find(const struct sshbuf *b, size_t start_offset, 2530cbfa66cSDaniel Fojt const void *s, size_t len, size_t *offsetp) 2540cbfa66cSDaniel Fojt { 2550cbfa66cSDaniel Fojt void *p; 2560cbfa66cSDaniel Fojt 2570cbfa66cSDaniel Fojt if (offsetp != NULL) 2580cbfa66cSDaniel Fojt *offsetp = 0; 2590cbfa66cSDaniel Fojt if (sshbuf_ptr(b) == NULL) 2600cbfa66cSDaniel Fojt return SSH_ERR_INTERNAL_ERROR; 2610cbfa66cSDaniel Fojt if (start_offset > SSHBUF_SIZE_MAX || len > SSHBUF_SIZE_MAX || len == 0) 2620cbfa66cSDaniel Fojt return SSH_ERR_INVALID_ARGUMENT; 2630cbfa66cSDaniel Fojt if (start_offset > sshbuf_len(b) || start_offset + len > sshbuf_len(b)) 2640cbfa66cSDaniel Fojt return SSH_ERR_MESSAGE_INCOMPLETE; 2650cbfa66cSDaniel Fojt if ((p = memmem(sshbuf_ptr(b) + start_offset, 2660cbfa66cSDaniel Fojt sshbuf_len(b) - start_offset, s, len)) == NULL) 2670cbfa66cSDaniel Fojt return SSH_ERR_INVALID_FORMAT; 2680cbfa66cSDaniel Fojt if (offsetp != NULL) 2690cbfa66cSDaniel Fojt *offsetp = (const u_char *)p - sshbuf_ptr(b); 2700cbfa66cSDaniel Fojt return 0; 2710cbfa66cSDaniel Fojt } 272