libder_read.c
1 /*- 2 * Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7 #include <sys/types.h> 8 9 #include <assert.h> 10 #include <err.h> 11 #include <errno.h> 12 #include <fcntl.h> 13 #include <poll.h> 14 #include <stdlib.h> 15 #include <string.h> 16 #include <unistd.h> 17 18 #include "libder_private.h" 19 20 enum libder_stream_type { 21 LDST_NONE, 22 LDST_FD, 23 LDST_FILE, 24 }; 25 26 struct libder_payload { 27 bool payload_heap; 28 uint8_t *payload_data; 29 size_t payload_size; 30 }; 31 32 struct libder_stream { 33 enum libder_stream_type stream_type; 34 struct libder_ctx *stream_ctx; 35 uint8_t *stream_buf; 36 size_t stream_bufsz; 37 38 size_t stream_offset; 39 size_t stream_resid; 40 size_t stream_consumed; 41 size_t stream_last_commit; 42 43 union { 44 const uint8_t *stream_src_buf; 45 FILE *stream_src_file; 46 int stream_src_fd; 47 }; 48 49 int stream_error; 50 bool stream_eof; 51 }; 52 53 static uint8_t * 54 payload_move(struct libder_payload *payload, size_t *sz) 55 { 56 uint8_t *data; 57 size_t datasz; 58 59 data = NULL; 60 datasz = payload->payload_size; 61 if (payload->payload_heap) { 62 data = payload->payload_data; 63 } else if (datasz > 0) { 64 data = malloc(datasz); 65 if (data == NULL) 66 return (NULL); 67 68 memcpy(data, payload->payload_data, datasz); 69 } 70 71 payload->payload_heap = false; 72 payload->payload_data = NULL; 73 payload->payload_size = 0; 74 75 *sz = datasz; 76 return (data); 77 } 78 79 static void 80 payload_free(struct libder_payload *payload) 81 { 82 83 if (!payload->payload_heap) 84 return; 85 86 if (payload->payload_data != NULL) { 87 libder_bzero(payload->payload_data, payload->payload_size); 88 free(payload->payload_data); 89 } 90 91 payload->payload_heap = false; 92 payload->payload_data = NULL; 93 payload->payload_size = 0; 94 } 95 96 static bool 97 libder_stream_init(struct libder_ctx *ctx, struct libder_stream *stream) 98 { 99 size_t buffer_size; 100 101 stream->stream_ctx = ctx; 102 stream->stream_error = 0; 103 stream->stream_eof = false; 104 stream->stream_offset = 0; 105 stream->stream_consumed = 0; 106 stream->stream_last_commit = 0; 107 if (stream->stream_type == LDST_NONE) { 108 assert(stream->stream_src_buf != NULL); 109 assert(stream->stream_bufsz != 0); 110 assert(stream->stream_resid != 0); 111 112 return (true); 113 } 114 115 buffer_size = libder_get_buffer_size(ctx); 116 assert(buffer_size != 0); 117 118 stream->stream_buf = malloc(buffer_size); 119 if (stream->stream_buf == NULL) { 120 libder_set_error(ctx, LDE_NOMEM); 121 } else { 122 stream->stream_bufsz = buffer_size; 123 stream->stream_resid = 0; /* Nothing read yet */ 124 } 125 126 return (stream->stream_buf != NULL); 127 } 128 129 static void 130 libder_stream_free(struct libder_stream *stream) 131 { 132 if (stream->stream_buf != NULL) { 133 libder_bzero(stream->stream_buf, stream->stream_bufsz); 134 free(stream->stream_buf); 135 } 136 } 137 138 static void 139 libder_stream_commit(struct libder_stream *stream) 140 { 141 142 if (stream->stream_offset <= stream->stream_last_commit) 143 return; 144 145 stream->stream_consumed += stream->stream_offset - stream->stream_last_commit; 146 stream->stream_last_commit = stream->stream_offset; 147 } 148 149 static bool 150 libder_stream_dynamic(const struct libder_stream *stream) 151 { 152 153 return (stream->stream_type != LDST_NONE); 154 } 155 156 static bool 157 libder_stream_eof(const struct libder_stream *stream) 158 { 159 160 /* 161 * We're not EOF until we're both EOF and have processed all of the data 162 * remaining in the buffer. 163 */ 164 return (stream->stream_eof && stream->stream_resid == 0); 165 } 166 167 static void 168 libder_stream_repack(struct libder_stream *stream) 169 { 170 171 /* 172 * Nothing to do, data's already at the beginning. 173 */ 174 if (stream->stream_offset == 0) 175 return; 176 177 /* 178 * If there's data in-flight, we'll repack it back to the beginning so 179 * that we can store more with fewer calls to refill. If there's no 180 * data in-flight, we naturally just reset the offset. 181 */ 182 if (stream->stream_resid != 0) { 183 uint8_t *dst = &stream->stream_buf[0]; 184 uint8_t *src = &stream->stream_buf[stream->stream_offset]; 185 186 memmove(dst, src, stream->stream_resid); 187 } 188 189 stream->stream_last_commit -= stream->stream_offset; 190 stream->stream_offset = 0; 191 } 192 193 static const uint8_t * 194 libder_stream_refill(struct libder_stream *stream, size_t req) 195 { 196 size_t offset = stream->stream_offset; 197 const uint8_t *src; 198 #ifndef NDEBUG 199 const uint8_t *bufend; 200 #endif 201 uint8_t *refill_buf; 202 size_t bufleft, freadsz, needed, totalsz; 203 ssize_t readsz; 204 205 /* 206 * For non-streaming, we just fulfill requests straight out of 207 * the source buffer. 208 */ 209 if (stream->stream_type == LDST_NONE) 210 src = stream->stream_src_buf; 211 else 212 src = stream->stream_buf; 213 214 if (stream->stream_resid >= req) { 215 stream->stream_offset += req; 216 stream->stream_resid -= req; 217 return (&src[offset]); 218 } 219 220 /* Cannot refill the non-streaming type. */ 221 if (stream->stream_type == LDST_NONE) { 222 stream->stream_eof = true; 223 return (NULL); 224 } 225 226 bufleft = stream->stream_bufsz - (stream->stream_offset + stream->stream_resid); 227 228 /* 229 * If we can't fit all of our data in the remainder of the buffer, we'll 230 * try to repack it to just fit as much as we can in. 231 */ 232 if (req > bufleft && stream->stream_offset != 0) { 233 libder_stream_repack(stream); 234 235 bufleft = stream->stream_bufsz - stream->stream_resid; 236 offset = stream->stream_offset; 237 } 238 239 refill_buf = &stream->stream_buf[offset + stream->stream_resid]; 240 needed = req - stream->stream_resid; 241 242 assert(needed <= bufleft); 243 244 #ifndef NDEBUG 245 bufend = &stream->stream_buf[stream->stream_bufsz]; 246 #endif 247 totalsz = 0; 248 249 switch (stream->stream_type) { 250 case LDST_FILE: 251 assert(stream->stream_src_file != NULL); 252 253 while (needed != 0) { 254 assert(refill_buf + needed <= bufend); 255 256 freadsz = fread(refill_buf, 1, needed, stream->stream_src_file); 257 if (freadsz == 0) { 258 /* 259 * Error always put us into EOF state. 260 */ 261 stream->stream_eof = true; 262 if (ferror(stream->stream_src_file)) 263 stream->stream_error = 1; 264 break; 265 } 266 267 stream->stream_resid += freadsz; 268 refill_buf += freadsz; 269 needed -= freadsz; 270 totalsz += freadsz; 271 } 272 break; 273 case LDST_FD: 274 assert(stream->stream_src_fd >= 0); 275 276 while (needed != 0) { 277 assert(refill_buf + needed <= bufend); 278 279 readsz = read(stream->stream_src_fd, refill_buf, needed); 280 if (readsz <= 0) { 281 /* 282 * In the future, we should likely make this 283 * configurable in some sense, but for now this 284 * seems fine. If, e.g., we caught a SIGINT, 285 * the application could always just close the 286 * fd on us if we should bail out. The problem 287 * right now is that we have no way to resume a 288 * partial transfer. 289 */ 290 if (readsz < 0 && errno == EINTR && 291 !libder_check_abort(stream->stream_ctx)) 292 continue; 293 stream->stream_eof = true; 294 if (readsz < 0) { 295 stream->stream_ctx->abort = false; 296 stream->stream_error = errno; 297 if (stream->stream_ctx->verbose > 0) 298 warn("libder_read"); 299 } 300 break; 301 } 302 303 stream->stream_resid += readsz; 304 refill_buf += readsz; 305 needed -= readsz; 306 totalsz += readsz; 307 } 308 309 break; 310 case LDST_NONE: 311 assert(0 && "Unrecognized stream type"); 312 break; 313 } 314 315 /* 316 * For streaming types, we commit as soon as we refill the buffer because 317 * we can't just rewind. 318 */ 319 stream->stream_consumed += totalsz; 320 stream->stream_last_commit += totalsz; 321 322 if (needed != 0) { 323 if (stream->stream_error != 0) 324 libder_set_error(stream->stream_ctx, LDE_STREAMERR); 325 return (NULL); 326 } else { 327 stream->stream_offset += req; 328 stream->stream_resid -= req; 329 } 330 331 return (&stream->stream_buf[offset]); 332 } 333 334 /* 335 * We can't just use realloc() because it won't provide any guarantees about 336 * the previous region if it can't just resize in-place, so we'll always just 337 * allocate a new one and copy ourselves. 338 */ 339 static uint8_t * 340 libder_read_realloc(uint8_t *ptr, size_t oldsz, size_t newsz) 341 { 342 uint8_t *newbuf; 343 344 if (oldsz == 0) 345 assert(ptr == NULL); 346 else 347 assert(ptr != NULL); 348 assert(newsz > oldsz); 349 350 newbuf = malloc(newsz); 351 if (newbuf == NULL) 352 return (NULL); 353 354 if (oldsz != 0) { 355 memcpy(newbuf, ptr, oldsz); 356 357 libder_bzero(ptr, oldsz); 358 free(ptr); 359 } 360 361 return (newbuf); 362 } 363 364 #define BER_TYPE_LONG_BATCH 0x04 365 366 static bool 367 der_read_structure_tag(struct libder_ctx *ctx, struct libder_stream *stream, 368 struct libder_tag *type) 369 { 370 const uint8_t *buf; 371 uint8_t *longbuf = NULL, val; 372 size_t longbufsz = 0, offset = 0, received = 0; 373 374 for (;;) { 375 /* 376 * We have to refill one byte at a time to avoid overreading 377 * into the structure size. 378 */ 379 if ((buf = libder_stream_refill(stream, 1)) == NULL) { 380 free(longbuf); 381 if (!libder_stream_eof(stream)) 382 libder_set_error(ctx, LDE_SHORTHDR); 383 return (false); 384 } 385 386 received++; 387 val = buf[0]; 388 if (received == 1) { 389 /* Deconstruct the class and p/c */ 390 type->tag_class = BER_TYPE_CLASS(val); 391 type->tag_constructed = BER_TYPE_CONSTRUCTED(val); 392 393 /* Long form, or short form? */ 394 if (BER_TYPE(val) != BER_TYPE_LONG_MASK) { 395 type->tag_short = BER_TYPE(val); 396 type->tag_size = sizeof(uint8_t); 397 type->tag_encoded = false; 398 399 return (true); 400 } 401 402 /* 403 * No content from this one, grab another byte. 404 */ 405 type->tag_encoded = true; 406 continue; 407 } 408 409 /* We might normalize it later, depending on flags. */ 410 if (offset == 0 && (val & 0x7f) == 0 && ctx->strict) { 411 libder_set_error(ctx, LDE_STRICT_TAG); 412 return (false); 413 } 414 415 /* XXX Impose a max size? Perhaps configurable. */ 416 if (offset == longbufsz) { 417 uint8_t *next; 418 size_t nextsz; 419 420 nextsz = longbufsz + BER_TYPE_LONG_BATCH; 421 next = realloc(longbuf, nextsz * sizeof(*longbuf)); 422 if (next == NULL) { 423 free(longbuf); 424 libder_set_error(ctx, LDE_NOMEM); 425 return (false); 426 } 427 428 longbuf = next; 429 longbufsz = nextsz; 430 } 431 432 longbuf[offset++] = val; 433 434 if ((val & 0x80) == 0) 435 break; 436 } 437 438 type->tag_long = longbuf; 439 type->tag_size = offset; 440 441 libder_normalize_type(ctx, type); 442 443 return (true); 444 } 445 446 static int 447 der_read_structure(struct libder_ctx *ctx, struct libder_stream *stream, 448 struct libder_tag *type, struct libder_payload *payload, bool *varlen) 449 { 450 const uint8_t *buf; 451 size_t rsz, offset, resid; 452 uint8_t bsz; 453 454 rsz = 0; 455 if (!der_read_structure_tag(ctx, stream, type)) { 456 return (-1); 457 } 458 459 if ((buf = libder_stream_refill(stream, 1)) == NULL) { 460 if (!libder_stream_eof(stream)) 461 libder_set_error(ctx, LDE_SHORTHDR); 462 goto failed; 463 } 464 465 bsz = *buf++; 466 467 #define LENBIT_LONG 0x80 468 *varlen = false; 469 if ((bsz & LENBIT_LONG) != 0) { 470 /* Long or long form, bsz describes how many bytes we have. */ 471 bsz &= ~LENBIT_LONG; 472 if (bsz != 0) { 473 /* Long */ 474 if (bsz > sizeof(rsz)) { 475 libder_set_error(ctx, LDE_LONGLEN); 476 goto failed; /* Only support up to long bytes. */ 477 } else if ((buf = libder_stream_refill(stream, bsz)) == NULL) { 478 libder_set_error(ctx, LDE_SHORTHDR); 479 goto failed; 480 } 481 482 rsz = 0; 483 for (int i = 0; i < bsz; i++) { 484 if (i != 0) 485 rsz <<= 8; 486 rsz |= *buf++; 487 } 488 } else { 489 if (ctx->strict && !type->tag_constructed) { 490 libder_set_error(ctx, LDE_STRICT_PVARLEN); 491 goto failed; 492 } 493 494 *varlen = true; 495 } 496 } else { 497 /* Short form */ 498 rsz = bsz; 499 } 500 501 if (rsz != 0) { 502 assert(!*varlen); 503 504 /* 505 * If we're not running a dynamic stream, we can just use a 506 * pointer into the buffer. The caller may copy the payload out 507 * anyways, but there's no sense in doing it up-front in case we 508 * hit an error in between then and now. 509 */ 510 if (!libder_stream_dynamic(stream)) { 511 /* 512 * This is a little dirty, but the caller won't mutate 513 * the data -- it'll either strictly read it, or it will 514 * copy it out to a known-mutable region. 515 */ 516 payload->payload_data = 517 __DECONST(void *, libder_stream_refill(stream, rsz)); 518 payload->payload_heap = false; 519 if (payload->payload_data == NULL) { 520 libder_set_error(ctx, LDE_SHORTDATA); 521 goto failed; 522 } 523 } else { 524 uint8_t *payload_data; 525 526 /* 527 * We play it conservative here: we could allocate the 528 * buffer up-front, but we have no idea how much data we 529 * actually have to receive! The length is a potentially 530 * attacker-controlled aspect, so we're cautiously optimistic 531 * that it's accurate. 532 */ 533 payload_data = NULL; 534 535 offset = 0; 536 resid = rsz; 537 while (resid != 0) { 538 uint8_t *next_data; 539 size_t req; 540 541 req = MIN(stream->stream_bufsz, resid); 542 if ((buf = libder_stream_refill(stream, req)) == NULL) { 543 libder_bzero(payload_data, offset); 544 free(payload_data); 545 546 libder_set_error(ctx, LDE_SHORTDATA); 547 goto failed; 548 } 549 550 next_data = libder_read_realloc(payload_data, 551 offset, offset + req); 552 if (next_data == NULL) { 553 libder_bzero(payload_data, offset); 554 free(payload_data); 555 556 libder_set_error(ctx, LDE_NOMEM); 557 goto failed; 558 } 559 560 payload_data = next_data; 561 next_data = NULL; 562 563 memcpy(&payload_data[offset], buf, req); 564 offset += req; 565 resid -= req; 566 } 567 568 payload->payload_heap = true; 569 payload->payload_data = payload_data; 570 } 571 572 payload->payload_size = rsz; 573 } 574 575 libder_stream_commit(stream); 576 return (0); 577 578 failed: 579 libder_type_release(type); 580 return (-1); 581 } 582 583 static struct libder_object * 584 libder_read_object(struct libder_ctx *ctx, struct libder_stream *stream) 585 { 586 struct libder_payload payload = { 0 }; 587 struct libder_object *child, **next, *obj; 588 struct libder_stream memstream, *childstream; 589 struct libder_tag type; 590 int error; 591 bool varlen; 592 593 /* Peel off one structure. */ 594 obj = NULL; 595 error = der_read_structure(ctx, stream, &type, &payload, &varlen); 596 if (error != 0) { 597 assert(payload.payload_data == NULL); 598 return (NULL); /* Error already set, if needed. */ 599 } 600 601 if (!libder_is_valid_obj(ctx, &type, payload.payload_data, 602 payload.payload_size, varlen)) { 603 /* 604 * libder_is_valid_obj may set a more specific error, e.g., a 605 * strict mode violation. 606 */ 607 if (ctx->error == LDE_NONE) 608 libder_set_error(ctx, LDE_BADOBJECT); 609 goto out; 610 } 611 612 if (!type.tag_constructed) { 613 uint8_t *payload_data; 614 size_t payloadsz; 615 616 /* 617 * Primitive types cannot use the indefinite form, they must 618 * have an encoded size. 619 */ 620 if (varlen) { 621 libder_set_error(ctx, LDE_BADVARLEN); 622 goto out; 623 } 624 625 /* 626 * Copy the payload out now if it's not heap-allocated. 627 */ 628 payload_data = payload_move(&payload, &payloadsz); 629 if (payload_data == NULL) { 630 libder_set_error(ctx, LDE_NOMEM); 631 goto out; 632 } 633 634 obj = libder_obj_alloc_internal(ctx, &type, payload_data, 635 payloadsz, 0); 636 if (obj == NULL) { 637 free(payload_data); 638 libder_set_error(ctx, LDE_NOMEM); 639 goto out; 640 } 641 642 libder_type_release(&type); 643 return (obj); 644 } 645 646 obj = libder_obj_alloc_internal(ctx, &type, NULL, 0, 0); 647 if (obj == NULL) { 648 libder_set_error(ctx, LDE_NOMEM); 649 goto out; 650 } 651 652 if (varlen) { 653 childstream = stream; 654 } else { 655 memstream = (struct libder_stream){ 656 .stream_type = LDST_NONE, 657 .stream_bufsz = payload.payload_size, 658 .stream_resid = payload.payload_size, 659 .stream_src_buf = payload.payload_data, 660 }; 661 662 childstream = &memstream; 663 } 664 665 /* Enumerate children */ 666 next = &obj->children; 667 for (;;) { 668 child = libder_read_object(ctx, childstream); 669 if (child == NULL) { 670 /* 671 * We may not know how much data we have, so this is our 672 * normal terminal condition. 673 */ 674 if (ctx->error != LDE_NONE) { 675 /* Free everything and bubble the error up. */ 676 libder_obj_free(obj); 677 obj = NULL; 678 } 679 break; 680 } 681 682 if (libder_type_is(child->type, BT_RESERVED) && 683 child->length == 0) { 684 /* 685 * This child is just a marker; free it, don't leak it, 686 * and stop here. 687 */ 688 libder_obj_free(child); 689 690 /* Malformed: shall not be present */ 691 if (!varlen) { 692 if (ctx->strict) { 693 libder_set_error(ctx, LDE_STRICT_EOC); 694 libder_obj_free(obj); 695 obj = NULL; 696 break; 697 } 698 699 continue; 700 } 701 702 /* Error detection */ 703 varlen = false; 704 break; 705 } 706 707 obj->nchildren++; 708 child->parent = obj; 709 *next = child; 710 next = &child->next; 711 } 712 713 if (varlen) { 714 libder_set_error(ctx, LDE_TRUNCVARLEN); 715 libder_obj_free(obj); 716 obj = NULL; 717 } 718 719 out: 720 libder_type_release(&type); 721 payload_free(&payload); 722 return (obj); 723 } 724 725 static struct libder_object * 726 libder_read_stream(struct libder_ctx *ctx, struct libder_stream *stream) 727 { 728 struct libder_object *root; 729 730 ctx->error = LDE_NONE; 731 root = libder_read_object(ctx, stream); 732 733 if (root != NULL && libder_type_is(root->type, BT_RESERVED) && 734 root->length == 0) { 735 /* Strict violation: must not appear. */ 736 if (ctx->strict) 737 libder_set_error(ctx, LDE_STRICT_EOC); 738 libder_obj_free(root); 739 root = NULL; 740 } 741 if (root != NULL) 742 assert(stream->stream_consumed != 0); 743 return (root); 744 } 745 746 /* 747 * Read the DER-encoded `data` into `ctx`. 748 * 749 * Returns an object on success, or NULL on failure. *datasz is updated to 750 * indicate the number of bytes consumed either way -- it will only be updated 751 * in the failure case if at least one object was valid. 752 */ 753 struct libder_object * 754 libder_read(struct libder_ctx *ctx, const uint8_t *data, size_t *datasz) 755 { 756 struct libder_stream *stream; 757 struct libder_object *root; 758 759 stream = malloc(sizeof(*stream)); 760 if (stream == NULL) { 761 libder_set_error(ctx, LDE_NOMEM); 762 return (NULL); 763 } 764 765 *stream = (struct libder_stream){ 766 .stream_type = LDST_NONE, 767 .stream_bufsz = *datasz, 768 .stream_resid = *datasz, 769 .stream_src_buf = data, 770 }; 771 772 libder_clear_abort(ctx); 773 ctx->error = LDE_NONE; 774 if (!libder_stream_init(ctx, stream)) { 775 free(stream); 776 return (NULL); 777 } 778 779 root = libder_read_stream(ctx, stream); 780 if (stream->stream_consumed != 0) 781 *datasz = stream->stream_consumed; 782 783 libder_stream_free(stream); 784 free(stream); 785 786 return (root); 787 } 788 789 /* 790 * Ditto above, but with an fd. *consumed is not ignored on entry, and returned 791 * with the number of bytes read from fd if consumed is not NULL. libder(3) 792 * tries to not over-read if an invalid structure is detected. 793 */ 794 struct libder_object * 795 libder_read_fd(struct libder_ctx *ctx, int fd, size_t *consumed) 796 { 797 struct libder_stream *stream; 798 struct libder_object *root; 799 800 stream = malloc(sizeof(*stream)); 801 if (stream == NULL) { 802 libder_set_error(ctx, LDE_NOMEM); 803 return (NULL); 804 } 805 806 *stream = (struct libder_stream){ 807 .stream_type = LDST_FD, 808 .stream_src_fd = fd, 809 }; 810 811 root = NULL; 812 libder_clear_abort(ctx); 813 ctx->error = LDE_NONE; 814 if (!libder_stream_init(ctx, stream)) { 815 free(stream); 816 return (NULL); 817 } 818 819 root = libder_read_stream(ctx, stream); 820 if (consumed != NULL && stream->stream_consumed != 0) 821 *consumed = stream->stream_consumed; 822 823 libder_stream_free(stream); 824 free(stream); 825 return (root); 826 } 827 828 /* 829 * Ditto above, but with a FILE instead of an fd. 830 */ 831 struct libder_object * 832 libder_read_file(struct libder_ctx *ctx, FILE *fp, size_t *consumed) 833 { 834 struct libder_stream *stream; 835 struct libder_object *root; 836 837 stream = malloc(sizeof(*stream)); 838 if (stream == NULL) { 839 libder_set_error(ctx, LDE_NOMEM); 840 return (NULL); 841 } 842 843 *stream = (struct libder_stream){ 844 .stream_type = LDST_FILE, 845 .stream_src_file = fp, 846 }; 847 848 root = NULL; 849 libder_clear_abort(ctx); 850 ctx->error = LDE_NONE; 851 if (!libder_stream_init(ctx, stream)) { 852 free(stream); 853 return (NULL); 854 } 855 856 root = libder_read_stream(ctx, stream); 857 if (consumed != NULL && stream->stream_consumed != 0) 858 *consumed = stream->stream_consumed; 859 860 libder_stream_free(stream); 861 free(stream); 862 863 return (root); 864 }