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