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