1 /* $OpenBSD: sshbuf-misc.c,v 1.18 2022/01/22 00:43:43 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.14 2022/02/23 19:07:20 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(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 int 92 sshbuf_dtob64(const struct sshbuf *d, struct sshbuf *b64, int wrap) 93 { 94 size_t i, slen = 0; 95 char *s = NULL; 96 int r; 97 98 if (d == NULL || b64 == NULL || sshbuf_len(d) >= SIZE_MAX / 2) 99 return SSH_ERR_INVALID_ARGUMENT; 100 if (sshbuf_len(d) == 0) 101 return 0; 102 slen = ((sshbuf_len(d) + 2) / 3) * 4 + 1; 103 if ((s = malloc(slen)) == NULL) 104 return SSH_ERR_ALLOC_FAIL; 105 if (b64_ntop(sshbuf_ptr(d), sshbuf_len(d), s, slen) == -1) { 106 r = SSH_ERR_INTERNAL_ERROR; 107 goto fail; 108 } 109 if (wrap) { 110 for (i = 0; s[i] != '\0'; i++) { 111 if ((r = sshbuf_put_u8(b64, s[i])) != 0) 112 goto fail; 113 if (i % 70 == 69 && (r = sshbuf_put_u8(b64, '\n')) != 0) 114 goto fail; 115 } 116 if ((i - 1) % 70 != 69 && (r = sshbuf_put_u8(b64, '\n')) != 0) 117 goto fail; 118 } else { 119 if ((r = sshbuf_put(b64, s, strlen(s))) != 0) 120 goto fail; 121 } 122 /* Success */ 123 r = 0; 124 fail: 125 freezero(s, slen); 126 return r; 127 } 128 129 char * 130 sshbuf_dtob64_string(const struct sshbuf *buf, int wrap) 131 { 132 struct sshbuf *tmp; 133 char *ret; 134 135 if ((tmp = sshbuf_new()) == NULL) 136 return NULL; 137 if (sshbuf_dtob64(buf, tmp, wrap) != 0) { 138 sshbuf_free(tmp); 139 return NULL; 140 } 141 ret = sshbuf_dup_string(tmp); 142 sshbuf_free(tmp); 143 return ret; 144 } 145 146 int 147 sshbuf_b64tod(struct sshbuf *buf, const char *b64) 148 { 149 size_t plen = strlen(b64); 150 int nlen, r; 151 u_char *p; 152 153 if (plen == 0) 154 return 0; 155 if ((p = malloc(plen)) == NULL) 156 return SSH_ERR_ALLOC_FAIL; 157 if ((nlen = b64_pton(b64, p, plen)) < 0) { 158 freezero(p, plen); 159 return SSH_ERR_INVALID_FORMAT; 160 } 161 if ((r = sshbuf_put(buf, p, nlen)) < 0) { 162 freezero(p, plen); 163 return r; 164 } 165 freezero(p, plen); 166 return 0; 167 } 168 169 int 170 sshbuf_dtourlb64(const struct sshbuf *d, struct sshbuf *b64, int wrap) 171 { 172 int r = SSH_ERR_INTERNAL_ERROR; 173 u_char *p; 174 struct sshbuf *b = NULL; 175 size_t i, l; 176 177 if ((b = sshbuf_new()) == NULL) 178 return SSH_ERR_ALLOC_FAIL; 179 /* Encode using regular base64; we'll transform it once done */ 180 if ((r = sshbuf_dtob64(d, b, wrap)) != 0) 181 goto out; 182 /* remove padding from end of encoded string*/ 183 for (;;) { 184 l = sshbuf_len(b); 185 if (l <= 1 || sshbuf_ptr(b) == NULL) { 186 r = SSH_ERR_INTERNAL_ERROR; 187 goto out; 188 } 189 if (sshbuf_ptr(b)[l - 1] != '=') 190 break; 191 if ((r = sshbuf_consume_end(b, 1)) != 0) 192 goto out; 193 } 194 /* Replace characters with rfc4648 equivalents */ 195 l = sshbuf_len(b); 196 if ((p = sshbuf_mutable_ptr(b)) == NULL) { 197 r = SSH_ERR_INTERNAL_ERROR; 198 goto out; 199 } 200 for (i = 0; i < l; i++) { 201 if (p[i] == '+') 202 p[i] = '-'; 203 else if (p[i] == '/') 204 p[i] = '_'; 205 } 206 r = sshbuf_putb(b64, b); 207 out: 208 sshbuf_free(b); 209 return r; 210 } 211 212 char * 213 sshbuf_dup_string(struct sshbuf *buf) 214 { 215 const u_char *p = NULL, *s = sshbuf_ptr(buf); 216 size_t l = sshbuf_len(buf); 217 char *r; 218 219 if (s == NULL || l > SIZE_MAX) 220 return NULL; 221 /* accept a nul only as the last character in the buffer */ 222 if (l > 0 && (p = memchr(s, '\0', l)) != NULL) { 223 if (p != s + l - 1) 224 return NULL; 225 l--; /* the nul is put back below */ 226 } 227 if ((r = malloc(l + 1)) == NULL) 228 return NULL; 229 if (l > 0) 230 memcpy(r, s, l); 231 r[l] = '\0'; 232 return r; 233 } 234 235 int 236 sshbuf_cmp(const struct sshbuf *b, size_t offset, 237 const void *s, size_t len) 238 { 239 if (sshbuf_ptr(b) == NULL) 240 return SSH_ERR_INTERNAL_ERROR; 241 if (offset > SSHBUF_SIZE_MAX || len > SSHBUF_SIZE_MAX || len == 0) 242 return SSH_ERR_INVALID_ARGUMENT; 243 if (offset + len > sshbuf_len(b)) 244 return SSH_ERR_MESSAGE_INCOMPLETE; 245 if (timingsafe_bcmp(sshbuf_ptr(b) + offset, s, len) != 0) 246 return SSH_ERR_INVALID_FORMAT; 247 return 0; 248 } 249 250 int 251 sshbuf_find(const struct sshbuf *b, size_t start_offset, 252 const void *s, size_t len, size_t *offsetp) 253 { 254 void *p; 255 256 if (offsetp != NULL) 257 *offsetp = 0; 258 if (sshbuf_ptr(b) == NULL) 259 return SSH_ERR_INTERNAL_ERROR; 260 if (start_offset > SSHBUF_SIZE_MAX || len > SSHBUF_SIZE_MAX || len == 0) 261 return SSH_ERR_INVALID_ARGUMENT; 262 if (start_offset > sshbuf_len(b) || start_offset + len > sshbuf_len(b)) 263 return SSH_ERR_MESSAGE_INCOMPLETE; 264 if ((p = memmem(sshbuf_ptr(b) + start_offset, 265 sshbuf_len(b) - start_offset, s, len)) == NULL) 266 return SSH_ERR_INVALID_FORMAT; 267 if (offsetp != NULL) 268 *offsetp = (const u_char *)p - sshbuf_ptr(b); 269 return 0; 270 } 271 272 int 273 sshbuf_read(int fd, struct sshbuf *buf, size_t maxlen, size_t *rlen) 274 { 275 int r, oerrno; 276 size_t adjust; 277 ssize_t rr; 278 u_char *d; 279 280 if (rlen != NULL) 281 *rlen = 0; 282 if ((r = sshbuf_reserve(buf, maxlen, &d)) != 0) 283 return r; 284 rr = read(fd, d, maxlen); 285 oerrno = errno; 286 287 /* Adjust the buffer to include only what was actually read */ 288 if ((adjust = maxlen - (rr > 0 ? rr : 0)) != 0) { 289 if ((r = sshbuf_consume_end(buf, adjust)) != 0) { 290 /* avoid returning uninitialised data to caller */ 291 memset(d + rr, '\0', adjust); 292 return SSH_ERR_INTERNAL_ERROR; /* shouldn't happen */ 293 } 294 } 295 if (rr < 0) { 296 errno = oerrno; 297 return SSH_ERR_SYSTEM_ERROR; 298 } else if (rr == 0) { 299 errno = EPIPE; 300 return SSH_ERR_SYSTEM_ERROR; 301 } 302 /* success */ 303 if (rlen != NULL) 304 *rlen = (size_t)rr; 305 return 0; 306 } 307