xref: /freebsd/contrib/libder/libder/libder_read.c (revision 35c0a8c449fd2b7f75029ebed5e10852240f0865)
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