1 /* $OpenBSD: sshbuf-getput-basic.c,v 1.7 2017/06/01 04:51:58 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 #define SSHBUF_INTERNAL 19 #include "includes.h" 20 21 #include <sys/types.h> 22 23 #include <stdarg.h> 24 #include <stdlib.h> 25 #include <stdio.h> 26 #include <string.h> 27 28 #include "ssherr.h" 29 #include "sshbuf.h" 30 31 int 32 sshbuf_get(struct sshbuf *buf, void *v, size_t len) 33 { 34 const u_char *p = sshbuf_ptr(buf); 35 int r; 36 37 if ((r = sshbuf_consume(buf, len)) < 0) 38 return r; 39 if (v != NULL && len != 0) 40 memcpy(v, p, len); 41 return 0; 42 } 43 44 int 45 sshbuf_get_u64(struct sshbuf *buf, u_int64_t *valp) 46 { 47 const u_char *p = sshbuf_ptr(buf); 48 int r; 49 50 if ((r = sshbuf_consume(buf, 8)) < 0) 51 return r; 52 if (valp != NULL) 53 *valp = PEEK_U64(p); 54 return 0; 55 } 56 57 int 58 sshbuf_get_u32(struct sshbuf *buf, u_int32_t *valp) 59 { 60 const u_char *p = sshbuf_ptr(buf); 61 int r; 62 63 if ((r = sshbuf_consume(buf, 4)) < 0) 64 return r; 65 if (valp != NULL) 66 *valp = PEEK_U32(p); 67 return 0; 68 } 69 70 int 71 sshbuf_get_u16(struct sshbuf *buf, u_int16_t *valp) 72 { 73 const u_char *p = sshbuf_ptr(buf); 74 int r; 75 76 if ((r = sshbuf_consume(buf, 2)) < 0) 77 return r; 78 if (valp != NULL) 79 *valp = PEEK_U16(p); 80 return 0; 81 } 82 83 int 84 sshbuf_get_u8(struct sshbuf *buf, u_char *valp) 85 { 86 const u_char *p = sshbuf_ptr(buf); 87 int r; 88 89 if ((r = sshbuf_consume(buf, 1)) < 0) 90 return r; 91 if (valp != NULL) 92 *valp = (u_int8_t)*p; 93 return 0; 94 } 95 96 int 97 sshbuf_get_string(struct sshbuf *buf, u_char **valp, size_t *lenp) 98 { 99 const u_char *val; 100 size_t len; 101 int r; 102 103 if (valp != NULL) 104 *valp = NULL; 105 if (lenp != NULL) 106 *lenp = 0; 107 if ((r = sshbuf_get_string_direct(buf, &val, &len)) < 0) 108 return r; 109 if (valp != NULL) { 110 if ((*valp = malloc(len + 1)) == NULL) { 111 SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL")); 112 return SSH_ERR_ALLOC_FAIL; 113 } 114 if (len != 0) 115 memcpy(*valp, val, len); 116 (*valp)[len] = '\0'; 117 } 118 if (lenp != NULL) 119 *lenp = len; 120 return 0; 121 } 122 123 int 124 sshbuf_get_string_direct(struct sshbuf *buf, const u_char **valp, size_t *lenp) 125 { 126 size_t len; 127 const u_char *p; 128 int r; 129 130 if (valp != NULL) 131 *valp = NULL; 132 if (lenp != NULL) 133 *lenp = 0; 134 if ((r = sshbuf_peek_string_direct(buf, &p, &len)) < 0) 135 return r; 136 if (valp != NULL) 137 *valp = p; 138 if (lenp != NULL) 139 *lenp = len; 140 if (sshbuf_consume(buf, len + 4) != 0) { 141 /* Shouldn't happen */ 142 SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); 143 SSHBUF_ABORT(); 144 return SSH_ERR_INTERNAL_ERROR; 145 } 146 return 0; 147 } 148 149 int 150 sshbuf_peek_string_direct(const struct sshbuf *buf, const u_char **valp, 151 size_t *lenp) 152 { 153 u_int32_t len; 154 const u_char *p = sshbuf_ptr(buf); 155 156 if (valp != NULL) 157 *valp = NULL; 158 if (lenp != NULL) 159 *lenp = 0; 160 if (sshbuf_len(buf) < 4) { 161 SSHBUF_DBG(("SSH_ERR_MESSAGE_INCOMPLETE")); 162 return SSH_ERR_MESSAGE_INCOMPLETE; 163 } 164 len = PEEK_U32(p); 165 if (len > SSHBUF_SIZE_MAX - 4) { 166 SSHBUF_DBG(("SSH_ERR_STRING_TOO_LARGE")); 167 return SSH_ERR_STRING_TOO_LARGE; 168 } 169 if (sshbuf_len(buf) - 4 < len) { 170 SSHBUF_DBG(("SSH_ERR_MESSAGE_INCOMPLETE")); 171 return SSH_ERR_MESSAGE_INCOMPLETE; 172 } 173 if (valp != NULL) 174 *valp = p + 4; 175 if (lenp != NULL) 176 *lenp = len; 177 return 0; 178 } 179 180 int 181 sshbuf_get_cstring(struct sshbuf *buf, char **valp, size_t *lenp) 182 { 183 size_t len; 184 const u_char *p, *z; 185 int r; 186 187 if (valp != NULL) 188 *valp = NULL; 189 if (lenp != NULL) 190 *lenp = 0; 191 if ((r = sshbuf_peek_string_direct(buf, &p, &len)) != 0) 192 return r; 193 /* Allow a \0 only at the end of the string */ 194 if (len > 0 && 195 (z = memchr(p , '\0', len)) != NULL && z < p + len - 1) { 196 SSHBUF_DBG(("SSH_ERR_INVALID_FORMAT")); 197 return SSH_ERR_INVALID_FORMAT; 198 } 199 if ((r = sshbuf_skip_string(buf)) != 0) 200 return -1; 201 if (valp != NULL) { 202 if ((*valp = malloc(len + 1)) == NULL) { 203 SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL")); 204 return SSH_ERR_ALLOC_FAIL; 205 } 206 if (len != 0) 207 memcpy(*valp, p, len); 208 (*valp)[len] = '\0'; 209 } 210 if (lenp != NULL) 211 *lenp = (size_t)len; 212 return 0; 213 } 214 215 int 216 sshbuf_get_stringb(struct sshbuf *buf, struct sshbuf *v) 217 { 218 u_int32_t len; 219 u_char *p; 220 int r; 221 222 /* 223 * Use sshbuf_peek_string_direct() to figure out if there is 224 * a complete string in 'buf' and copy the string directly 225 * into 'v'. 226 */ 227 if ((r = sshbuf_peek_string_direct(buf, NULL, NULL)) != 0 || 228 (r = sshbuf_get_u32(buf, &len)) != 0 || 229 (r = sshbuf_reserve(v, len, &p)) != 0 || 230 (r = sshbuf_get(buf, p, len)) != 0) 231 return r; 232 return 0; 233 } 234 235 int 236 sshbuf_put(struct sshbuf *buf, const void *v, size_t len) 237 { 238 u_char *p; 239 int r; 240 241 if ((r = sshbuf_reserve(buf, len, &p)) < 0) 242 return r; 243 if (len != 0) 244 memcpy(p, v, len); 245 return 0; 246 } 247 248 int 249 sshbuf_putb(struct sshbuf *buf, const struct sshbuf *v) 250 { 251 return sshbuf_put(buf, sshbuf_ptr(v), sshbuf_len(v)); 252 } 253 254 int 255 sshbuf_putf(struct sshbuf *buf, const char *fmt, ...) 256 { 257 va_list ap; 258 int r; 259 260 va_start(ap, fmt); 261 r = sshbuf_putfv(buf, fmt, ap); 262 va_end(ap); 263 return r; 264 } 265 266 int 267 sshbuf_putfv(struct sshbuf *buf, const char *fmt, va_list ap) 268 { 269 va_list ap2; 270 int r, len; 271 u_char *p; 272 273 VA_COPY(ap2, ap); 274 if ((len = vsnprintf(NULL, 0, fmt, ap2)) < 0) { 275 r = SSH_ERR_INVALID_ARGUMENT; 276 goto out; 277 } 278 if (len == 0) { 279 r = 0; 280 goto out; /* Nothing to do */ 281 } 282 va_end(ap2); 283 VA_COPY(ap2, ap); 284 if ((r = sshbuf_reserve(buf, (size_t)len + 1, &p)) < 0) 285 goto out; 286 if ((r = vsnprintf((char *)p, len + 1, fmt, ap2)) != len) { 287 r = SSH_ERR_INTERNAL_ERROR; 288 goto out; /* Shouldn't happen */ 289 } 290 /* Consume terminating \0 */ 291 if ((r = sshbuf_consume_end(buf, 1)) != 0) 292 goto out; 293 r = 0; 294 out: 295 va_end(ap2); 296 return r; 297 } 298 299 int 300 sshbuf_put_u64(struct sshbuf *buf, u_int64_t val) 301 { 302 u_char *p; 303 int r; 304 305 if ((r = sshbuf_reserve(buf, 8, &p)) < 0) 306 return r; 307 POKE_U64(p, val); 308 return 0; 309 } 310 311 int 312 sshbuf_put_u32(struct sshbuf *buf, u_int32_t val) 313 { 314 u_char *p; 315 int r; 316 317 if ((r = sshbuf_reserve(buf, 4, &p)) < 0) 318 return r; 319 POKE_U32(p, val); 320 return 0; 321 } 322 323 int 324 sshbuf_put_u16(struct sshbuf *buf, u_int16_t val) 325 { 326 u_char *p; 327 int r; 328 329 if ((r = sshbuf_reserve(buf, 2, &p)) < 0) 330 return r; 331 POKE_U16(p, val); 332 return 0; 333 } 334 335 int 336 sshbuf_put_u8(struct sshbuf *buf, u_char val) 337 { 338 u_char *p; 339 int r; 340 341 if ((r = sshbuf_reserve(buf, 1, &p)) < 0) 342 return r; 343 p[0] = val; 344 return 0; 345 } 346 347 int 348 sshbuf_put_string(struct sshbuf *buf, const void *v, size_t len) 349 { 350 u_char *d; 351 int r; 352 353 if (len > SSHBUF_SIZE_MAX - 4) { 354 SSHBUF_DBG(("SSH_ERR_NO_BUFFER_SPACE")); 355 return SSH_ERR_NO_BUFFER_SPACE; 356 } 357 if ((r = sshbuf_reserve(buf, len + 4, &d)) < 0) 358 return r; 359 POKE_U32(d, len); 360 if (len != 0) 361 memcpy(d + 4, v, len); 362 return 0; 363 } 364 365 int 366 sshbuf_put_cstring(struct sshbuf *buf, const char *v) 367 { 368 return sshbuf_put_string(buf, v, v == NULL ? 0 : strlen(v)); 369 } 370 371 int 372 sshbuf_put_stringb(struct sshbuf *buf, const struct sshbuf *v) 373 { 374 return sshbuf_put_string(buf, sshbuf_ptr(v), sshbuf_len(v)); 375 } 376 377 int 378 sshbuf_froms(struct sshbuf *buf, struct sshbuf **bufp) 379 { 380 const u_char *p; 381 size_t len; 382 struct sshbuf *ret; 383 int r; 384 385 if (buf == NULL || bufp == NULL) 386 return SSH_ERR_INVALID_ARGUMENT; 387 *bufp = NULL; 388 if ((r = sshbuf_peek_string_direct(buf, &p, &len)) != 0) 389 return r; 390 if ((ret = sshbuf_from(p, len)) == NULL) 391 return SSH_ERR_ALLOC_FAIL; 392 if ((r = sshbuf_consume(buf, len + 4)) != 0 || /* Shouldn't happen */ 393 (r = sshbuf_set_parent(ret, buf)) != 0) { 394 sshbuf_free(ret); 395 return r; 396 } 397 *bufp = ret; 398 return 0; 399 } 400 401 int 402 sshbuf_put_bignum2_bytes(struct sshbuf *buf, const void *v, size_t len) 403 { 404 u_char *d; 405 const u_char *s = (const u_char *)v; 406 int r, prepend; 407 408 if (len > SSHBUF_SIZE_MAX - 5) { 409 SSHBUF_DBG(("SSH_ERR_NO_BUFFER_SPACE")); 410 return SSH_ERR_NO_BUFFER_SPACE; 411 } 412 /* Skip leading zero bytes */ 413 for (; len > 0 && *s == 0; len--, s++) 414 ; 415 /* 416 * If most significant bit is set then prepend a zero byte to 417 * avoid interpretation as a negative number. 418 */ 419 prepend = len > 0 && (s[0] & 0x80) != 0; 420 if ((r = sshbuf_reserve(buf, len + 4 + prepend, &d)) < 0) 421 return r; 422 POKE_U32(d, len + prepend); 423 if (prepend) 424 d[4] = 0; 425 if (len != 0) 426 memcpy(d + 4 + prepend, s, len); 427 return 0; 428 } 429 430 int 431 sshbuf_get_bignum2_bytes_direct(struct sshbuf *buf, 432 const u_char **valp, size_t *lenp) 433 { 434 const u_char *d; 435 size_t len, olen; 436 int r; 437 438 if ((r = sshbuf_peek_string_direct(buf, &d, &olen)) < 0) 439 return r; 440 len = olen; 441 /* Refuse negative (MSB set) bignums */ 442 if ((len != 0 && (*d & 0x80) != 0)) 443 return SSH_ERR_BIGNUM_IS_NEGATIVE; 444 /* Refuse overlong bignums, allow prepended \0 to avoid MSB set */ 445 if (len > SSHBUF_MAX_BIGNUM + 1 || 446 (len == SSHBUF_MAX_BIGNUM + 1 && *d != 0)) 447 return SSH_ERR_BIGNUM_TOO_LARGE; 448 /* Trim leading zeros */ 449 while (len > 0 && *d == 0x00) { 450 d++; 451 len--; 452 } 453 if (valp != NULL) 454 *valp = d; 455 if (lenp != NULL) 456 *lenp = len; 457 if (sshbuf_consume(buf, olen + 4) != 0) { 458 /* Shouldn't happen */ 459 SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); 460 SSHBUF_ABORT(); 461 return SSH_ERR_INTERNAL_ERROR; 462 } 463 return 0; 464 } 465