sshbuf-getput-basic.c revision 1.8
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 29int 30sshbuf_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 42int 43sshbuf_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 55int 56sshbuf_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 68int 69sshbuf_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 81int 82sshbuf_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 94static int 95check_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 108static int 109check_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 121int 122sshbuf_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 136int 137sshbuf_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 151int 152sshbuf_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 166int 167sshbuf_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 181int 182sshbuf_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 208int 209sshbuf_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 234int 235sshbuf_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 265int 266sshbuf_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 300int 301sshbuf_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 320int 321sshbuf_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 333int 334sshbuf_putb(struct sshbuf *buf, const struct sshbuf *v) 335{ 336 return sshbuf_put(buf, sshbuf_ptr(v), sshbuf_len(v)); 337} 338 339int 340sshbuf_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 351int 352sshbuf_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 384int 385sshbuf_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 396int 397sshbuf_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 408int 409sshbuf_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 420int 421sshbuf_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 432static int 433check_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 446int 447sshbuf_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 458int 459sshbuf_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 470int 471sshbuf_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 482int 483sshbuf_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 494int 495sshbuf_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 506int 507sshbuf_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 524int 525sshbuf_put_cstring(struct sshbuf *buf, const char *v) 526{ 527 return sshbuf_put_string(buf, v, v == NULL ? 0 : strlen(v)); 528} 529 530int 531sshbuf_put_stringb(struct sshbuf *buf, const struct sshbuf *v) 532{ 533 return sshbuf_put_string(buf, sshbuf_ptr(v), sshbuf_len(v)); 534} 535 536int 537sshbuf_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 560int 561sshbuf_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 589int 590sshbuf_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