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