1 /* $OpenBSD: sshbuf-getput-basic.c,v 1.11 2020/06/05 03:25:35 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 if (v == NULL) 338 return 0; 339 return sshbuf_put(buf, sshbuf_ptr(v), sshbuf_len(v)); 340 } 341 342 int 343 sshbuf_putf(struct sshbuf *buf, const char *fmt, ...) 344 { 345 va_list ap; 346 int r; 347 348 va_start(ap, fmt); 349 r = sshbuf_putfv(buf, fmt, ap); 350 va_end(ap); 351 return r; 352 } 353 354 int 355 sshbuf_putfv(struct sshbuf *buf, const char *fmt, va_list ap) 356 { 357 va_list ap2; 358 int r, len; 359 u_char *p; 360 361 va_copy(ap2, ap); 362 if ((len = vsnprintf(NULL, 0, fmt, ap2)) < 0) { 363 r = SSH_ERR_INVALID_ARGUMENT; 364 goto out; 365 } 366 if (len == 0) { 367 r = 0; 368 goto out; /* Nothing to do */ 369 } 370 va_end(ap2); 371 va_copy(ap2, ap); 372 if ((r = sshbuf_reserve(buf, (size_t)len + 1, &p)) < 0) 373 goto out; 374 if ((r = vsnprintf((char *)p, len + 1, fmt, ap2)) != len) { 375 r = SSH_ERR_INTERNAL_ERROR; 376 goto out; /* Shouldn't happen */ 377 } 378 /* Consume terminating \0 */ 379 if ((r = sshbuf_consume_end(buf, 1)) != 0) 380 goto out; 381 r = 0; 382 out: 383 va_end(ap2); 384 return r; 385 } 386 387 int 388 sshbuf_put_u64(struct sshbuf *buf, u_int64_t val) 389 { 390 u_char *p; 391 int r; 392 393 if ((r = sshbuf_reserve(buf, 8, &p)) < 0) 394 return r; 395 POKE_U64(p, val); 396 return 0; 397 } 398 399 int 400 sshbuf_put_u32(struct sshbuf *buf, u_int32_t val) 401 { 402 u_char *p; 403 int r; 404 405 if ((r = sshbuf_reserve(buf, 4, &p)) < 0) 406 return r; 407 POKE_U32(p, val); 408 return 0; 409 } 410 411 int 412 sshbuf_put_u16(struct sshbuf *buf, u_int16_t val) 413 { 414 u_char *p; 415 int r; 416 417 if ((r = sshbuf_reserve(buf, 2, &p)) < 0) 418 return r; 419 POKE_U16(p, val); 420 return 0; 421 } 422 423 int 424 sshbuf_put_u8(struct sshbuf *buf, u_char val) 425 { 426 u_char *p; 427 int r; 428 429 if ((r = sshbuf_reserve(buf, 1, &p)) < 0) 430 return r; 431 p[0] = val; 432 return 0; 433 } 434 435 static int 436 check_woffset(struct sshbuf *buf, size_t offset, size_t len, u_char **p) 437 { 438 int r; 439 440 *p = NULL; 441 if ((r = check_offset(buf, 1, offset, len)) != 0) 442 return r; 443 if (sshbuf_mutable_ptr(buf) == NULL) 444 return SSH_ERR_BUFFER_READ_ONLY; 445 *p = sshbuf_mutable_ptr(buf) + offset; 446 return 0; 447 } 448 449 int 450 sshbuf_poke_u64(struct sshbuf *buf, size_t offset, u_int64_t val) 451 { 452 u_char *p = NULL; 453 int r; 454 455 if ((r = check_woffset(buf, offset, 8, &p)) != 0) 456 return r; 457 POKE_U64(p, val); 458 return 0; 459 } 460 461 int 462 sshbuf_poke_u32(struct sshbuf *buf, size_t offset, u_int32_t val) 463 { 464 u_char *p = NULL; 465 int r; 466 467 if ((r = check_woffset(buf, offset, 4, &p)) != 0) 468 return r; 469 POKE_U32(p, val); 470 return 0; 471 } 472 473 int 474 sshbuf_poke_u16(struct sshbuf *buf, size_t offset, u_int16_t val) 475 { 476 u_char *p = NULL; 477 int r; 478 479 if ((r = check_woffset(buf, offset, 2, &p)) != 0) 480 return r; 481 POKE_U16(p, val); 482 return 0; 483 } 484 485 int 486 sshbuf_poke_u8(struct sshbuf *buf, size_t offset, u_char val) 487 { 488 u_char *p = NULL; 489 int r; 490 491 if ((r = check_woffset(buf, offset, 1, &p)) != 0) 492 return r; 493 *p = val; 494 return 0; 495 } 496 497 int 498 sshbuf_poke(struct sshbuf *buf, size_t offset, void *v, size_t len) 499 { 500 u_char *p = NULL; 501 int r; 502 503 if ((r = check_woffset(buf, offset, len, &p)) != 0) 504 return r; 505 memcpy(p, v, len); 506 return 0; 507 } 508 509 int 510 sshbuf_put_string(struct sshbuf *buf, const void *v, size_t len) 511 { 512 u_char *d; 513 int r; 514 515 if (len > SSHBUF_SIZE_MAX - 4) { 516 SSHBUF_DBG(("SSH_ERR_NO_BUFFER_SPACE")); 517 return SSH_ERR_NO_BUFFER_SPACE; 518 } 519 if ((r = sshbuf_reserve(buf, len + 4, &d)) < 0) 520 return r; 521 POKE_U32(d, len); 522 if (len != 0) 523 memcpy(d + 4, v, len); 524 return 0; 525 } 526 527 int 528 sshbuf_put_cstring(struct sshbuf *buf, const char *v) 529 { 530 return sshbuf_put_string(buf, v, v == NULL ? 0 : strlen(v)); 531 } 532 533 int 534 sshbuf_put_stringb(struct sshbuf *buf, const struct sshbuf *v) 535 { 536 if (v == NULL) 537 return sshbuf_put_string(buf, NULL, 0); 538 539 return sshbuf_put_string(buf, sshbuf_ptr(v), sshbuf_len(v)); 540 } 541 542 int 543 sshbuf_froms(struct sshbuf *buf, struct sshbuf **bufp) 544 { 545 const u_char *p; 546 size_t len; 547 struct sshbuf *ret; 548 int r; 549 550 if (buf == NULL || bufp == NULL) 551 return SSH_ERR_INVALID_ARGUMENT; 552 *bufp = NULL; 553 if ((r = sshbuf_peek_string_direct(buf, &p, &len)) != 0) 554 return r; 555 if ((ret = sshbuf_from(p, len)) == NULL) 556 return SSH_ERR_ALLOC_FAIL; 557 if ((r = sshbuf_consume(buf, len + 4)) != 0 || /* Shouldn't happen */ 558 (r = sshbuf_set_parent(ret, buf)) != 0) { 559 sshbuf_free(ret); 560 return r; 561 } 562 *bufp = ret; 563 return 0; 564 } 565 566 int 567 sshbuf_put_bignum2_bytes(struct sshbuf *buf, const void *v, size_t len) 568 { 569 u_char *d; 570 const u_char *s = (const u_char *)v; 571 int r, prepend; 572 573 if (len > SSHBUF_SIZE_MAX - 5) { 574 SSHBUF_DBG(("SSH_ERR_NO_BUFFER_SPACE")); 575 return SSH_ERR_NO_BUFFER_SPACE; 576 } 577 /* Skip leading zero bytes */ 578 for (; len > 0 && *s == 0; len--, s++) 579 ; 580 /* 581 * If most significant bit is set then prepend a zero byte to 582 * avoid interpretation as a negative number. 583 */ 584 prepend = len > 0 && (s[0] & 0x80) != 0; 585 if ((r = sshbuf_reserve(buf, len + 4 + prepend, &d)) < 0) 586 return r; 587 POKE_U32(d, len + prepend); 588 if (prepend) 589 d[4] = 0; 590 if (len != 0) 591 memcpy(d + 4 + prepend, s, len); 592 return 0; 593 } 594 595 int 596 sshbuf_get_bignum2_bytes_direct(struct sshbuf *buf, 597 const u_char **valp, size_t *lenp) 598 { 599 const u_char *d; 600 size_t len, olen; 601 int r; 602 603 if ((r = sshbuf_peek_string_direct(buf, &d, &olen)) < 0) 604 return r; 605 len = olen; 606 /* Refuse negative (MSB set) bignums */ 607 if ((len != 0 && (*d & 0x80) != 0)) 608 return SSH_ERR_BIGNUM_IS_NEGATIVE; 609 /* Refuse overlong bignums, allow prepended \0 to avoid MSB set */ 610 if (len > SSHBUF_MAX_BIGNUM + 1 || 611 (len == SSHBUF_MAX_BIGNUM + 1 && *d != 0)) 612 return SSH_ERR_BIGNUM_TOO_LARGE; 613 /* Trim leading zeros */ 614 while (len > 0 && *d == 0x00) { 615 d++; 616 len--; 617 } 618 if (valp != NULL) 619 *valp = d; 620 if (lenp != NULL) 621 *lenp = len; 622 if (sshbuf_consume(buf, olen + 4) != 0) { 623 /* Shouldn't happen */ 624 SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); 625 SSHBUF_ABORT(); 626 return SSH_ERR_INTERNAL_ERROR; 627 } 628 return 0; 629 } 630