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 *
payload_move(struct libder_payload * payload,size_t * sz)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
payload_free(struct libder_payload * payload)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
libder_stream_init(struct libder_ctx * ctx,struct libder_stream * stream)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
libder_stream_free(struct libder_stream * stream)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
libder_stream_commit(struct libder_stream * stream)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
libder_stream_dynamic(const struct libder_stream * stream)150 libder_stream_dynamic(const struct libder_stream *stream)
151 {
152
153 return (stream->stream_type != LDST_NONE);
154 }
155
156 static bool
libder_stream_eof(const struct libder_stream * stream)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
libder_stream_repack(struct libder_stream * stream)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 *
libder_stream_refill(struct libder_stream * stream,size_t req)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 *
libder_read_realloc(uint8_t * ptr,size_t oldsz,size_t newsz)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
der_read_structure_tag(struct libder_ctx * ctx,struct libder_stream * stream,struct libder_tag * type)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
der_read_structure(struct libder_ctx * ctx,struct libder_stream * stream,struct libder_tag * type,struct libder_payload * payload,bool * varlen)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 *
libder_read_object(struct libder_ctx * ctx,struct libder_stream * stream)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 *
libder_read_stream(struct libder_ctx * ctx,struct libder_stream * stream)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 *
libder_read(struct libder_ctx * ctx,const uint8_t * data,size_t * datasz)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 *
libder_read_fd(struct libder_ctx * ctx,int fd,size_t * consumed)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 *
libder_read_file(struct libder_ctx * ctx,FILE * fp,size_t * consumed)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 }
865