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