/ external / libder / libder / libder_read.c
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  }