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