sshbuf-getput-basic.c revision 1.13
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 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 30int 31sshbuf_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 43int 44sshbuf_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 56int 57sshbuf_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 69int 70sshbuf_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 82int 83sshbuf_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 95static int 96check_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 109static int 110check_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 122int 123sshbuf_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 137int 138sshbuf_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 152int 153sshbuf_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 167int 168sshbuf_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 182int 183sshbuf_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 209int 210sshbuf_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 235int 236sshbuf_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 266int 267sshbuf_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 301int 302sshbuf_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 321int 322sshbuf_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 334int 335sshbuf_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 342int 343sshbuf_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 354int 355sshbuf_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 387int 388sshbuf_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 399int 400sshbuf_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 411int 412sshbuf_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 423int 424sshbuf_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 435static int 436check_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 449int 450sshbuf_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 461int 462sshbuf_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 473int 474sshbuf_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 485int 486sshbuf_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 497int 498sshbuf_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 509int 510sshbuf_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 527int 528sshbuf_put_cstring(struct sshbuf *buf, const char *v) 529{ 530 return sshbuf_put_string(buf, v, v == NULL ? 0 : strlen(v)); 531} 532 533int 534sshbuf_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 542int 543sshbuf_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 566int 567sshbuf_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 595int 596sshbuf_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