1 /* $OpenBSD: sshbuf-getput-basic.c,v 1.10 2019/12/13 19:09:37 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 20 #include <stdarg.h> 21 #include <stdlib.h> 22 #include <stdio.h> 23 #include <string.h> 24 #include <stdint.h> 25 26 #include "ssherr.h" 27 #define SSHBUF_INTERNAL 28 #include "sshbuf.h" 29 30 int 31 sshbuf_get(struct sshbuf *buf, void *v, size_t len) 32 { 33 const u_char *p = sshbuf_ptr(buf); 34 int r; 35 36 if ((r = sshbuf_consume(buf, len)) < 0) 37 return r; 38 if (v != NULL && len != 0) 39 memcpy(v, p, len); 40 return 0; 41 } 42 43 int 44 sshbuf_get_u64(struct sshbuf *buf, u_int64_t *valp) 45 { 46 const u_char *p = sshbuf_ptr(buf); 47 int r; 48 49 if ((r = sshbuf_consume(buf, 8)) < 0) 50 return r; 51 if (valp != NULL) 52 *valp = PEEK_U64(p); 53 return 0; 54 } 55 56 int 57 sshbuf_get_u32(struct sshbuf *buf, u_int32_t *valp) 58 { 59 const u_char *p = sshbuf_ptr(buf); 60 int r; 61 62 if ((r = sshbuf_consume(buf, 4)) < 0) 63 return r; 64 if (valp != NULL) 65 *valp = PEEK_U32(p); 66 return 0; 67 } 68 69 int 70 sshbuf_get_u16(struct sshbuf *buf, u_int16_t *valp) 71 { 72 const u_char *p = sshbuf_ptr(buf); 73 int r; 74 75 if ((r = sshbuf_consume(buf, 2)) < 0) 76 return r; 77 if (valp != NULL) 78 *valp = PEEK_U16(p); 79 return 0; 80 } 81 82 int 83 sshbuf_get_u8(struct sshbuf *buf, u_char *valp) 84 { 85 const u_char *p = sshbuf_ptr(buf); 86 int r; 87 88 if ((r = sshbuf_consume(buf, 1)) < 0) 89 return r; 90 if (valp != NULL) 91 *valp = (u_int8_t)*p; 92 return 0; 93 } 94 95 static int 96 check_offset(const struct sshbuf *buf, int wr, size_t offset, size_t len) 97 { 98 if (sshbuf_ptr(buf) == NULL) /* calls sshbuf_check_sanity() */ 99 return SSH_ERR_INTERNAL_ERROR; 100 if (offset >= SIZE_MAX - len) 101 return SSH_ERR_INVALID_ARGUMENT; 102 if (offset + len > sshbuf_len(buf)) { 103 return wr ? 104 SSH_ERR_NO_BUFFER_SPACE : SSH_ERR_MESSAGE_INCOMPLETE; 105 } 106 return 0; 107 } 108 109 static int 110 check_roffset(const struct sshbuf *buf, size_t offset, size_t len, 111 const u_char **p) 112 { 113 int r; 114 115 *p = NULL; 116 if ((r = check_offset(buf, 0, offset, len)) != 0) 117 return r; 118 *p = sshbuf_ptr(buf) + offset; 119 return 0; 120 } 121 122 int 123 sshbuf_peek_u64(const struct sshbuf *buf, size_t offset, u_int64_t *valp) 124 { 125 const u_char *p = NULL; 126 int r; 127 128 if (valp != NULL) 129 *valp = 0; 130 if ((r = check_roffset(buf, offset, 8, &p)) != 0) 131 return r; 132 if (valp != NULL) 133 *valp = PEEK_U64(p); 134 return 0; 135 } 136 137 int 138 sshbuf_peek_u32(const struct sshbuf *buf, size_t offset, u_int32_t *valp) 139 { 140 const u_char *p = NULL; 141 int r; 142 143 if (valp != NULL) 144 *valp = 0; 145 if ((r = check_roffset(buf, offset, 4, &p)) != 0) 146 return r; 147 if (valp != NULL) 148 *valp = PEEK_U32(p); 149 return 0; 150 } 151 152 int 153 sshbuf_peek_u16(const struct sshbuf *buf, size_t offset, u_int16_t *valp) 154 { 155 const u_char *p = NULL; 156 int r; 157 158 if (valp != NULL) 159 *valp = 0; 160 if ((r = check_roffset(buf, offset, 2, &p)) != 0) 161 return r; 162 if (valp != NULL) 163 *valp = PEEK_U16(p); 164 return 0; 165 } 166 167 int 168 sshbuf_peek_u8(const struct sshbuf *buf, size_t offset, u_char *valp) 169 { 170 const u_char *p = NULL; 171 int r; 172 173 if (valp != NULL) 174 *valp = 0; 175 if ((r = check_roffset(buf, offset, 1, &p)) != 0) 176 return r; 177 if (valp != NULL) 178 *valp = *p; 179 return 0; 180 } 181 182 int 183 sshbuf_get_string(struct sshbuf *buf, u_char **valp, size_t *lenp) 184 { 185 const u_char *val; 186 size_t len; 187 int r; 188 189 if (valp != NULL) 190 *valp = NULL; 191 if (lenp != NULL) 192 *lenp = 0; 193 if ((r = sshbuf_get_string_direct(buf, &val, &len)) < 0) 194 return r; 195 if (valp != NULL) { 196 if ((*valp = malloc(len + 1)) == NULL) { 197 SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL")); 198 return SSH_ERR_ALLOC_FAIL; 199 } 200 if (len != 0) 201 memcpy(*valp, val, len); 202 (*valp)[len] = '\0'; 203 } 204 if (lenp != NULL) 205 *lenp = len; 206 return 0; 207 } 208 209 int 210 sshbuf_get_string_direct(struct sshbuf *buf, const u_char **valp, size_t *lenp) 211 { 212 size_t len; 213 const u_char *p; 214 int r; 215 216 if (valp != NULL) 217 *valp = NULL; 218 if (lenp != NULL) 219 *lenp = 0; 220 if ((r = sshbuf_peek_string_direct(buf, &p, &len)) < 0) 221 return r; 222 if (valp != NULL) 223 *valp = p; 224 if (lenp != NULL) 225 *lenp = len; 226 if (sshbuf_consume(buf, len + 4) != 0) { 227 /* Shouldn't happen */ 228 SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); 229 SSHBUF_ABORT(); 230 return SSH_ERR_INTERNAL_ERROR; 231 } 232 return 0; 233 } 234 235 int 236 sshbuf_peek_string_direct(const struct sshbuf *buf, const u_char **valp, 237 size_t *lenp) 238 { 239 u_int32_t len; 240 const u_char *p = sshbuf_ptr(buf); 241 242 if (valp != NULL) 243 *valp = NULL; 244 if (lenp != NULL) 245 *lenp = 0; 246 if (sshbuf_len(buf) < 4) { 247 SSHBUF_DBG(("SSH_ERR_MESSAGE_INCOMPLETE")); 248 return SSH_ERR_MESSAGE_INCOMPLETE; 249 } 250 len = PEEK_U32(p); 251 if (len > SSHBUF_SIZE_MAX - 4) { 252 SSHBUF_DBG(("SSH_ERR_STRING_TOO_LARGE")); 253 return SSH_ERR_STRING_TOO_LARGE; 254 } 255 if (sshbuf_len(buf) - 4 < len) { 256 SSHBUF_DBG(("SSH_ERR_MESSAGE_INCOMPLETE")); 257 return SSH_ERR_MESSAGE_INCOMPLETE; 258 } 259 if (valp != NULL) 260 *valp = p + 4; 261 if (lenp != NULL) 262 *lenp = len; 263 return 0; 264 } 265 266 int 267 sshbuf_get_cstring(struct sshbuf *buf, char **valp, size_t *lenp) 268 { 269 size_t len; 270 const u_char *p, *z; 271 int r; 272 273 if (valp != NULL) 274 *valp = NULL; 275 if (lenp != NULL) 276 *lenp = 0; 277 if ((r = sshbuf_peek_string_direct(buf, &p, &len)) != 0) 278 return r; 279 /* Allow a \0 only at the end of the string */ 280 if (len > 0 && 281 (z = memchr(p , '\0', len)) != NULL && z < p + len - 1) { 282 SSHBUF_DBG(("SSH_ERR_INVALID_FORMAT")); 283 return SSH_ERR_INVALID_FORMAT; 284 } 285 if ((r = sshbuf_skip_string(buf)) != 0) 286 return -1; 287 if (valp != NULL) { 288 if ((*valp = malloc(len + 1)) == NULL) { 289 SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL")); 290 return SSH_ERR_ALLOC_FAIL; 291 } 292 if (len != 0) 293 memcpy(*valp, p, len); 294 (*valp)[len] = '\0'; 295 } 296 if (lenp != NULL) 297 *lenp = (size_t)len; 298 return 0; 299 } 300 301 int 302 sshbuf_get_stringb(struct sshbuf *buf, struct sshbuf *v) 303 { 304 u_int32_t len; 305 u_char *p; 306 int r; 307 308 /* 309 * Use sshbuf_peek_string_direct() to figure out if there is 310 * a complete string in 'buf' and copy the string directly 311 * into 'v'. 312 */ 313 if ((r = sshbuf_peek_string_direct(buf, NULL, NULL)) != 0 || 314 (r = sshbuf_get_u32(buf, &len)) != 0 || 315 (r = sshbuf_reserve(v, len, &p)) != 0 || 316 (r = sshbuf_get(buf, p, len)) != 0) 317 return r; 318 return 0; 319 } 320 321 int 322 sshbuf_put(struct sshbuf *buf, const void *v, size_t len) 323 { 324 u_char *p; 325 int r; 326 327 if ((r = sshbuf_reserve(buf, len, &p)) < 0) 328 return r; 329 if (len != 0) 330 memcpy(p, v, len); 331 return 0; 332 } 333 334 int 335 sshbuf_putb(struct sshbuf *buf, const struct sshbuf *v) 336 { 337 return sshbuf_put(buf, sshbuf_ptr(v), sshbuf_len(v)); 338 } 339 340 int 341 sshbuf_putf(struct sshbuf *buf, const char *fmt, ...) 342 { 343 va_list ap; 344 int r; 345 346 va_start(ap, fmt); 347 r = sshbuf_putfv(buf, fmt, ap); 348 va_end(ap); 349 return r; 350 } 351 352 int 353 sshbuf_putfv(struct sshbuf *buf, const char *fmt, va_list ap) 354 { 355 va_list ap2; 356 int r, len; 357 u_char *p; 358 359 va_copy(ap2, ap); 360 if ((len = vsnprintf(NULL, 0, fmt, ap2)) < 0) { 361 r = SSH_ERR_INVALID_ARGUMENT; 362 goto out; 363 } 364 if (len == 0) { 365 r = 0; 366 goto out; /* Nothing to do */ 367 } 368 va_end(ap2); 369 va_copy(ap2, ap); 370 if ((r = sshbuf_reserve(buf, (size_t)len + 1, &p)) < 0) 371 goto out; 372 if ((r = vsnprintf((char *)p, len + 1, fmt, ap2)) != len) { 373 r = SSH_ERR_INTERNAL_ERROR; 374 goto out; /* Shouldn't happen */ 375 } 376 /* Consume terminating \0 */ 377 if ((r = sshbuf_consume_end(buf, 1)) != 0) 378 goto out; 379 r = 0; 380 out: 381 va_end(ap2); 382 return r; 383 } 384 385 int 386 sshbuf_put_u64(struct sshbuf *buf, u_int64_t val) 387 { 388 u_char *p; 389 int r; 390 391 if ((r = sshbuf_reserve(buf, 8, &p)) < 0) 392 return r; 393 POKE_U64(p, val); 394 return 0; 395 } 396 397 int 398 sshbuf_put_u32(struct sshbuf *buf, u_int32_t val) 399 { 400 u_char *p; 401 int r; 402 403 if ((r = sshbuf_reserve(buf, 4, &p)) < 0) 404 return r; 405 POKE_U32(p, val); 406 return 0; 407 } 408 409 int 410 sshbuf_put_u16(struct sshbuf *buf, u_int16_t val) 411 { 412 u_char *p; 413 int r; 414 415 if ((r = sshbuf_reserve(buf, 2, &p)) < 0) 416 return r; 417 POKE_U16(p, val); 418 return 0; 419 } 420 421 int 422 sshbuf_put_u8(struct sshbuf *buf, u_char val) 423 { 424 u_char *p; 425 int r; 426 427 if ((r = sshbuf_reserve(buf, 1, &p)) < 0) 428 return r; 429 p[0] = val; 430 return 0; 431 } 432 433 static int 434 check_woffset(struct sshbuf *buf, size_t offset, size_t len, u_char **p) 435 { 436 int r; 437 438 *p = NULL; 439 if ((r = check_offset(buf, 1, offset, len)) != 0) 440 return r; 441 if (sshbuf_mutable_ptr(buf) == NULL) 442 return SSH_ERR_BUFFER_READ_ONLY; 443 *p = sshbuf_mutable_ptr(buf) + offset; 444 return 0; 445 } 446 447 int 448 sshbuf_poke_u64(struct sshbuf *buf, size_t offset, u_int64_t val) 449 { 450 u_char *p = NULL; 451 int r; 452 453 if ((r = check_woffset(buf, offset, 8, &p)) != 0) 454 return r; 455 POKE_U64(p, val); 456 return 0; 457 } 458 459 int 460 sshbuf_poke_u32(struct sshbuf *buf, size_t offset, u_int32_t val) 461 { 462 u_char *p = NULL; 463 int r; 464 465 if ((r = check_woffset(buf, offset, 4, &p)) != 0) 466 return r; 467 POKE_U32(p, val); 468 return 0; 469 } 470 471 int 472 sshbuf_poke_u16(struct sshbuf *buf, size_t offset, u_int16_t val) 473 { 474 u_char *p = NULL; 475 int r; 476 477 if ((r = check_woffset(buf, offset, 2, &p)) != 0) 478 return r; 479 POKE_U16(p, val); 480 return 0; 481 } 482 483 int 484 sshbuf_poke_u8(struct sshbuf *buf, size_t offset, u_char val) 485 { 486 u_char *p = NULL; 487 int r; 488 489 if ((r = check_woffset(buf, offset, 1, &p)) != 0) 490 return r; 491 *p = val; 492 return 0; 493 } 494 495 int 496 sshbuf_poke(struct sshbuf *buf, size_t offset, void *v, size_t len) 497 { 498 u_char *p = NULL; 499 int r; 500 501 if ((r = check_woffset(buf, offset, len, &p)) != 0) 502 return r; 503 memcpy(p, v, len); 504 return 0; 505 } 506 507 int 508 sshbuf_put_string(struct sshbuf *buf, const void *v, size_t len) 509 { 510 u_char *d; 511 int r; 512 513 if (len > SSHBUF_SIZE_MAX - 4) { 514 SSHBUF_DBG(("SSH_ERR_NO_BUFFER_SPACE")); 515 return SSH_ERR_NO_BUFFER_SPACE; 516 } 517 if ((r = sshbuf_reserve(buf, len + 4, &d)) < 0) 518 return r; 519 POKE_U32(d, len); 520 if (len != 0) 521 memcpy(d + 4, v, len); 522 return 0; 523 } 524 525 int 526 sshbuf_put_cstring(struct sshbuf *buf, const char *v) 527 { 528 return sshbuf_put_string(buf, v, v == NULL ? 0 : strlen(v)); 529 } 530 531 int 532 sshbuf_put_stringb(struct sshbuf *buf, const struct sshbuf *v) 533 { 534 if (v == NULL) 535 return sshbuf_put_string(buf, NULL, 0); 536 537 return sshbuf_put_string(buf, sshbuf_ptr(v), sshbuf_len(v)); 538 } 539 540 int 541 sshbuf_froms(struct sshbuf *buf, struct sshbuf **bufp) 542 { 543 const u_char *p; 544 size_t len; 545 struct sshbuf *ret; 546 int r; 547 548 if (buf == NULL || bufp == NULL) 549 return SSH_ERR_INVALID_ARGUMENT; 550 *bufp = NULL; 551 if ((r = sshbuf_peek_string_direct(buf, &p, &len)) != 0) 552 return r; 553 if ((ret = sshbuf_from(p, len)) == NULL) 554 return SSH_ERR_ALLOC_FAIL; 555 if ((r = sshbuf_consume(buf, len + 4)) != 0 || /* Shouldn't happen */ 556 (r = sshbuf_set_parent(ret, buf)) != 0) { 557 sshbuf_free(ret); 558 return r; 559 } 560 *bufp = ret; 561 return 0; 562 } 563 564 int 565 sshbuf_put_bignum2_bytes(struct sshbuf *buf, const void *v, size_t len) 566 { 567 u_char *d; 568 const u_char *s = (const u_char *)v; 569 int r, prepend; 570 571 if (len > SSHBUF_SIZE_MAX - 5) { 572 SSHBUF_DBG(("SSH_ERR_NO_BUFFER_SPACE")); 573 return SSH_ERR_NO_BUFFER_SPACE; 574 } 575 /* Skip leading zero bytes */ 576 for (; len > 0 && *s == 0; len--, s++) 577 ; 578 /* 579 * If most significant bit is set then prepend a zero byte to 580 * avoid interpretation as a negative number. 581 */ 582 prepend = len > 0 && (s[0] & 0x80) != 0; 583 if ((r = sshbuf_reserve(buf, len + 4 + prepend, &d)) < 0) 584 return r; 585 POKE_U32(d, len + prepend); 586 if (prepend) 587 d[4] = 0; 588 if (len != 0) 589 memcpy(d + 4 + prepend, s, len); 590 return 0; 591 } 592 593 int 594 sshbuf_get_bignum2_bytes_direct(struct sshbuf *buf, 595 const u_char **valp, size_t *lenp) 596 { 597 const u_char *d; 598 size_t len, olen; 599 int r; 600 601 if ((r = sshbuf_peek_string_direct(buf, &d, &olen)) < 0) 602 return r; 603 len = olen; 604 /* Refuse negative (MSB set) bignums */ 605 if ((len != 0 && (*d & 0x80) != 0)) 606 return SSH_ERR_BIGNUM_IS_NEGATIVE; 607 /* Refuse overlong bignums, allow prepended \0 to avoid MSB set */ 608 if (len > SSHBUF_MAX_BIGNUM + 1 || 609 (len == SSHBUF_MAX_BIGNUM + 1 && *d != 0)) 610 return SSH_ERR_BIGNUM_TOO_LARGE; 611 /* Trim leading zeros */ 612 while (len > 0 && *d == 0x00) { 613 d++; 614 len--; 615 } 616 if (valp != NULL) 617 *valp = d; 618 if (lenp != NULL) 619 *lenp = len; 620 if (sshbuf_consume(buf, olen + 4) != 0) { 621 /* Shouldn't happen */ 622 SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); 623 SSHBUF_ABORT(); 624 return SSH_ERR_INTERNAL_ERROR; 625 } 626 return 0; 627 } 628