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