mft.c revision 1.43
150276Speter/*	$OpenBSD: mft.c,v 1.43 2022/01/06 16:06:30 claudio Exp $ */
276726Speter/*
350276Speter * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
450276Speter *
550276Speter * Permission to use, copy, modify, and distribute this software for any
650276Speter * purpose with or without fee is hereby granted, provided that the above
750276Speter * copyright notice and this permission notice appear in all copies.
850276Speter *
950276Speter * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1050276Speter * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1150276Speter * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1250276Speter * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1350276Speter * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1450276Speter * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1550276Speter * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1650276Speter */
1750276Speter
1850276Speter#include <assert.h>
1950276Speter#include <err.h>
2050276Speter#include <limits.h>
2150276Speter#include <stdarg.h>
2250276Speter#include <stdint.h>
2350276Speter#include <fcntl.h>
2450276Speter#include <stdlib.h>
2550276Speter#include <string.h>
2650276Speter#include <unistd.h>
2750276Speter
2850276Speter#include <openssl/bn.h>
2950276Speter#include <openssl/asn1.h>
3050276Speter#include <openssl/sha.h>
3176726Speter#include <openssl/x509.h>
3250276Speter
3366963Speter#include "extern.h"
3466963Speter
3566963Speter/*
3666963Speter * Parse results and data of the manifest file.
3766963Speter */
3876726Speterstruct	parse {
3950276Speter	const char	*fn; /* manifest file name */
4050276Speter	struct mft	*res; /* result object */
4150276Speter};
4250276Speter
4366963Speterstatic ASN1_OBJECT    *mft_oid;
4450276Speter
4550276Speterstatic const char *
4650276Spetergentime2str(const ASN1_GENERALIZEDTIME *time)
4750276Speter{
4850276Speter	static char	buf[64];
4950276Speter	BIO		*mem;
5050276Speter
5150276Speter	if ((mem = BIO_new(BIO_s_mem())) == NULL)
5250276Speter		cryptoerrx("BIO_new");
5350276Speter	if (!ASN1_GENERALIZEDTIME_print(mem, time))
5450276Speter		cryptoerrx("ASN1_GENERALIZEDTIME_print");
5550276Speter	if (BIO_gets(mem, buf, sizeof(buf)) < 0)
5650276Speter		cryptoerrx("BIO_gets");
5750276Speter
5850276Speter	BIO_free(mem);
5950276Speter	return buf;
6050276Speter}
6150276Speter
6250276Speter/*
6350276Speter * Convert an ASN1_GENERALIZEDTIME to a struct tm.
6450276Speter * Returns 1 on success, 0 on failure.
6550276Speter */
6650276Speterstatic int
6750276Spetergeneralizedtime_to_tm(const ASN1_GENERALIZEDTIME *gtime, struct tm *tm)
6850276Speter{
6976726Speter	const char *data;
7050276Speter	size_t len;
7176726Speter
7250276Speter	data = ASN1_STRING_get0_data(gtime);
7350276Speter	len = ASN1_STRING_length(gtime);
7450276Speter
7550276Speter	memset(tm, 0, sizeof(*tm));
7650276Speter	return ASN1_time_parse(data, len, tm, V_ASN1_GENERALIZEDTIME) ==
7750276Speter	    V_ASN1_GENERALIZEDTIME;
7850276Speter}
7950276Speter
8050276Speter/*
8150276Speter * Validate and verify the time validity of the mft.
8250276Speter * Returns 1 if all is good, 0 if mft is stale, any other case -1.
8366963Speter */
8466963Speterstatic int
8566963Spetercheck_validity(const ASN1_GENERALIZEDTIME *from,
8650276Speter    const ASN1_GENERALIZEDTIME *until, const char *fn)
8766963Speter{
8866963Speter	time_t now = time(NULL);
8966963Speter	struct tm tm_from, tm_until, tm_now;
9066963Speter
9150276Speter	if (gmtime_r(&now, &tm_now) == NULL) {
9266963Speter		warnx("%s: could not get current time", fn);
9350276Speter		return -1;
9450276Speter	}
9550276Speter
9650276Speter	if (!generalizedtime_to_tm(from, &tm_from)) {
9750276Speter		warnx("%s: embedded from time format invalid", fn);
9850276Speter		return -1;
9950276Speter	}
10050276Speter	if (!generalizedtime_to_tm(until, &tm_until)) {
10150276Speter		warnx("%s: embedded until time format invalid", fn);
10250276Speter		return -1;
10350276Speter	}
10450276Speter
10550276Speter	/* check that until is not before from */
10650276Speter	if (ASN1_time_tm_cmp(&tm_until, &tm_from) < 0) {
10750276Speter		warnx("%s: bad update interval", fn);
10850276Speter		return -1;
10950276Speter	}
11050276Speter	/* check that now is not before from */
11150276Speter	if (ASN1_time_tm_cmp(&tm_from, &tm_now) > 0) {
11256639Speter		warnx("%s: mft not yet valid %s", fn, gentime2str(from));
11350276Speter		return -1;
11456639Speter	}
11550276Speter	/* check that now is not after until */
11650276Speter	if (ASN1_time_tm_cmp(&tm_until, &tm_now) < 0) {
11750276Speter		warnx("%s: mft expired on %s", fn, gentime2str(until));
11856639Speter		return 0;
11956639Speter	}
12056639Speter
12156639Speter	return 1;
12262449Speter}
12362449Speter
12462449Speter/*
12556639Speter * Parse an individual "FileAndHash", RFC 6486, sec. 4.2.
12662449Speter * Return zero on failure, non-zero on success.
12750276Speter */
12866963Speterstatic int
12966963Spetermft_parse_filehash(struct parse *p, const ASN1_OCTET_STRING *os)
13066963Speter{
13176726Speter	ASN1_SEQUENCE_ANY	*seq;
13266963Speter	const ASN1_TYPE		*file, *hash;
13366963Speter	char			*fn = NULL;
13466963Speter	const unsigned char	*d = os->data;
13566963Speter	size_t			 dsz = os->length;
13666963Speter	int			 rc = 0;
13766963Speter	struct mftfile		*fent;
13850276Speter
13956639Speter	if ((seq = d2i_ASN1_SEQUENCE_ANY(NULL, &d, dsz)) == NULL) {
14056639Speter		cryptowarnx("%s: RFC 6486 section 4.2.1: FileAndHash: "
14156639Speter		    "failed ASN.1 sequence parse", p->fn);
14256639Speter		goto out;
14356639Speter	} else if (sk_ASN1_TYPE_num(seq) != 2) {
14456639Speter		warnx("%s: RFC 6486 section 4.2.1: FileAndHash: "
14556639Speter		    "want 2 elements, have %d", p->fn,
14650276Speter		    sk_ASN1_TYPE_num(seq));
14750276Speter		goto out;
14850276Speter	}
14950276Speter
15050276Speter	/* First is the filename itself. */
15150276Speter
15250276Speter	file = sk_ASN1_TYPE_value(seq, 0);
15350276Speter	if (file->type != V_ASN1_IA5STRING) {
15450276Speter		warnx("%s: RFC 6486 section 4.2.1: FileAndHash: "
15550276Speter		    "want ASN.1 IA5 string, have %s (NID %d)",
15650276Speter		    p->fn, ASN1_tag2str(file->type), file->type);
15750276Speter		goto out;
15850276Speter	}
15950276Speter	fn = strndup((const char *)file->value.ia5string->data,
16076726Speter	    file->value.ia5string->length);
16176726Speter	if (fn == NULL)
16276726Speter		err(1, NULL);
16376726Speter
16476726Speter	if (!valid_filename(fn)) {
16576726Speter		warnx("%s: invalid filename: %s", p->fn, fn);
16676726Speter		goto out;
16750276Speter	}
16850276Speter
16950276Speter	/* Now hash value. */
17050276Speter
17150276Speter	hash = sk_ASN1_TYPE_value(seq, 1);
17250276Speter	if (hash->type != V_ASN1_BIT_STRING) {
17366963Speter		warnx("%s: RFC 6486 section 4.2.1: FileAndHash: "
17450276Speter		    "want ASN.1 bit string, have %s (NID %d)",
17550276Speter		    p->fn, ASN1_tag2str(hash->type), hash->type);
17650276Speter		goto out;
17750276Speter	}
17866963Speter
17950276Speter	if (hash->value.bit_string->length != SHA256_DIGEST_LENGTH) {
18050276Speter		warnx("%s: RFC 6486 section 4.2.1: hash: "
18166963Speter		    "invalid SHA256 length, have %d",
18250276Speter		    p->fn, hash->value.bit_string->length);
18350276Speter		goto out;
18450276Speter	}
18550276Speter
18650276Speter	/* Insert the filename and hash value. */
18750276Speter	fent = &p->res->files[p->res->filesz++];
18850276Speter
18950276Speter	fent->file = fn;
19050276Speter	fn = NULL;
19150276Speter	memcpy(fent->hash, hash->value.bit_string->data, SHA256_DIGEST_LENGTH);
19250276Speter
19350276Speter	rc = 1;
19450276Speterout:
19550276Speter	free(fn);
19650276Speter	sk_ASN1_TYPE_pop_free(seq, ASN1_TYPE_free);
19750276Speter	return rc;
19850276Speter}
19950276Speter
20050276Speter/*
20150276Speter * Parse the "FileAndHash" sequence, RFC 6486, sec. 4.2.
20266963Speter * Return zero on failure, non-zero on success.
20350276Speter */
20450276Speterstatic int
20566963Spetermft_parse_flist(struct parse *p, const ASN1_OCTET_STRING *os)
20666963Speter{
20750276Speter	ASN1_SEQUENCE_ANY	*seq;
20850276Speter	const ASN1_TYPE		*t;
20950276Speter	const unsigned char	*d = os->data;
21050276Speter	size_t			 dsz = os->length;
21150276Speter	int			 i, rc = 0;
21250276Speter
21350276Speter	if ((seq = d2i_ASN1_SEQUENCE_ANY(NULL, &d, dsz)) == NULL) {
21450276Speter		cryptowarnx("%s: RFC 6486 section 4.2: fileList: "
21562449Speter		    "failed ASN.1 sequence parse", p->fn);
21650276Speter		goto out;
21776726Speter	}
21876726Speter
21976726Speter	if (sk_ASN1_TYPE_num(seq) > MAX_MANIFEST_ENTRIES) {
22076726Speter		warnx("%s: %d exceeds manifest entry limit (%d)", p->fn,
22176726Speter		    sk_ASN1_TYPE_num(seq), MAX_MANIFEST_ENTRIES);
22276726Speter		goto out;
22376726Speter	}
22476726Speter
22576726Speter	p->res->files = calloc(sk_ASN1_TYPE_num(seq), sizeof(struct mftfile));
22676726Speter	if (p->res->files == NULL)
22776726Speter		err(1, NULL);
22876726Speter
22976726Speter	for (i = 0; i < sk_ASN1_TYPE_num(seq); i++) {
23076726Speter		t = sk_ASN1_TYPE_value(seq, i);
23150276Speter		if (t->type != V_ASN1_SEQUENCE) {
23250276Speter			warnx("%s: RFC 6486 section 4.2: fileList: "
23350276Speter			    "want ASN.1 sequence, have %s (NID %d)",
23450276Speter			    p->fn, ASN1_tag2str(t->type), t->type);
23550276Speter			goto out;
23650276Speter		} else if (!mft_parse_filehash(p, t->value.octet_string))
23750276Speter			goto out;
23850276Speter	}
23950276Speter
24050276Speter	rc = 1;
24150276Speter out:
24250276Speter	sk_ASN1_TYPE_pop_free(seq, ASN1_TYPE_free);
24350276Speter	return rc;
24450276Speter}
24550276Speter
24650276Speter/*
24750276Speter * Handle the eContent of the manifest object, RFC 6486 sec. 4.2.
24850276Speter * Returns 0 on failure and 1 on success.
24950276Speter */
25050276Speterstatic int
25150276Spetermft_parse_econtent(const unsigned char *d, size_t dsz, struct parse *p)
25250276Speter{
25350276Speter	ASN1_SEQUENCE_ANY	*seq;
25450276Speter	const ASN1_TYPE		*t;
25550276Speter	const ASN1_GENERALIZEDTIME *from, *until;
25650276Speter	long			 mft_version;
25750276Speter	BIGNUM			*mft_seqnum = NULL;
25850276Speter	int			 i = 0, rc = 0;
25950276Speter
26050276Speter	if ((seq = d2i_ASN1_SEQUENCE_ANY(NULL, &d, dsz)) == NULL) {
26150276Speter		cryptowarnx("%s: RFC 6486 section 4.2: Manifest: "
26250276Speter		    "failed ASN.1 sequence parse", p->fn);
26362449Speter		goto out;
26462449Speter	}
26562449Speter
26662449Speter	/* Test if the optional profile version field is present. */
26776726Speter	if (sk_ASN1_TYPE_num(seq) != 5 &&
26876726Speter	    sk_ASN1_TYPE_num(seq) != 6) {
26976726Speter		warnx("%s: RFC 6486 section 4.2: Manifest: "
27062449Speter		    "want 5 or 6 elements, have %d", p->fn,
27162449Speter		    sk_ASN1_TYPE_num(seq));
27262449Speter		goto out;
27362449Speter	}
27462449Speter
27562449Speter	/* Parse the optional version field */
27662449Speter	if (sk_ASN1_TYPE_num(seq) == 6) {
27762449Speter		t = sk_ASN1_TYPE_value(seq, i++);
27862449Speter		d = t->value.asn1_string->data;
27962449Speter		dsz = t->value.asn1_string->length;
28062449Speter
28162449Speter		if (cms_econtent_version(p->fn, &d, dsz, &mft_version) == -1)
28262449Speter			goto out;
28362449Speter
28462449Speter		switch (mft_version) {
28562449Speter		case 0:
28662449Speter			warnx("%s: incorrect encoding for version 0", p->fn);
28762449Speter			goto out;
28862449Speter		default:
28962449Speter			warnx("%s: version %ld not supported (yet)", p->fn,
29062449Speter			    mft_version);
29162449Speter			goto out;
29262449Speter		}
29362449Speter	}
29462449Speter
29562449Speter	/* Now the manifest sequence number. */
29662449Speter
29762449Speter	t = sk_ASN1_TYPE_value(seq, i++);
29862449Speter	if (t->type != V_ASN1_INTEGER) {
29950276Speter		warnx("%s: RFC 6486 section 4.2.1: manifestNumber: "
30050276Speter		    "want ASN.1 integer, have %s (NID %d)",
30150276Speter		    p->fn, ASN1_tag2str(t->type), t->type);
30250276Speter		goto out;
30350276Speter	}
30450276Speter
30550276Speter	mft_seqnum = ASN1_INTEGER_to_BN(t->value.integer, NULL);
30650276Speter	if (mft_seqnum == NULL) {
30750276Speter		warnx("%s: ASN1_INTEGER_to_BN error", p->fn);
30850276Speter		goto out;
30950276Speter	}
31050276Speter
31150276Speter	if (BN_is_negative(mft_seqnum)) {
31250276Speter		warnx("%s: RFC 6486 section 4.2.1: manifestNumber: "
31366963Speter		    "want positive integer, have negative.", p->fn);
31450276Speter		goto out;
31550276Speter	}
31650276Speter
31750276Speter	if (BN_num_bytes(mft_seqnum) > 20) {
31850276Speter		warnx("%s: RFC 6486 section 4.2.1: manifestNumber: "
31950276Speter		    "want 20 or less than octets, have more.", p->fn);
32050276Speter		goto out;
32150276Speter	}
32250276Speter
32366963Speter	p->res->seqnum = BN_bn2hex(mft_seqnum);
32450276Speter	if (p->res->seqnum == NULL) {
32550276Speter		warnx("%s: BN_bn2hex error", p->fn);
32650276Speter		goto out;
32750276Speter	}
32850276Speter
32950276Speter	/*
33050276Speter	 * Timestamps: this and next update time.
33150276Speter	 * Validate that the current date falls into this interval.
33250276Speter	 * This is required by section 4.4, (3).
33350276Speter	 * If we're after the given date, then the MFT is stale.
33466963Speter	 * This is made super complicated because it uses OpenSSL's
33550276Speter	 * ASN1_GENERALIZEDTIME instead of ASN1_TIME, which we could
33650276Speter	 * compare against the current time trivially.
33750276Speter	 */
33850276Speter
33950276Speter	t = sk_ASN1_TYPE_value(seq, i++);
34056639Speter	if (t->type != V_ASN1_GENERALIZEDTIME) {
34150276Speter		warnx("%s: RFC 6486 section 4.2.1: thisUpdate: "
34250276Speter		    "want ASN.1 generalised time, have %s (NID %d)",
34350276Speter		    p->fn, ASN1_tag2str(t->type), t->type);
34450276Speter		goto out;
34550276Speter	}
34650276Speter	from = t->value.generalizedtime;
34750276Speter
34850276Speter	t = sk_ASN1_TYPE_value(seq, i++);
34950276Speter	if (t->type != V_ASN1_GENERALIZEDTIME) {
35050276Speter		warnx("%s: RFC 6486 section 4.2.1: nextUpdate: "
35150276Speter		    "want ASN.1 generalised time, have %s (NID %d)",
35250276Speter		    p->fn, ASN1_tag2str(t->type), t->type);
35350276Speter		goto out;
35450276Speter	}
35550276Speter	until = t->value.generalizedtime;
35650276Speter
35762449Speter	switch (check_validity(from, until, p->fn)) {
35850276Speter	case 0:
35976726Speter		p->res->stale = 1;
36050276Speter		/* FALLTHROUGH */
36150276Speter	case 1:
36250276Speter		break;
36350276Speter	case -1:
36450276Speter		goto out;
36550276Speter	}
36662449Speter
36750276Speter	/* File list algorithm. */
36850276Speter
36950276Speter	t = sk_ASN1_TYPE_value(seq, i++);
37050276Speter	if (t->type != V_ASN1_OBJECT) {
37150276Speter		warnx("%s: RFC 6486 section 4.2.1: fileHashAlg: "
37250276Speter		    "want ASN.1 object, have %s (NID %d)",
37350276Speter		    p->fn, ASN1_tag2str(t->type), t->type);
37450276Speter		goto out;
37550276Speter	}
37650276Speter	if (OBJ_obj2nid(t->value.object) != NID_sha256) {
37750276Speter		warnx("%s: RFC 6486 section 4.2.1: fileHashAlg: "
37850276Speter		    "want SHA256 object, have %s (NID %d)", p->fn,
37950276Speter		    ASN1_tag2str(OBJ_obj2nid(t->value.object)),
38050276Speter		    OBJ_obj2nid(t->value.object));
38150276Speter		goto out;
38250276Speter	}
38350276Speter
38450276Speter	/* Now the sequence. */
38550276Speter
38650276Speter	t = sk_ASN1_TYPE_value(seq, i++);
38750276Speter	if (t->type != V_ASN1_SEQUENCE) {
38850276Speter		warnx("%s: RFC 6486 section 4.2.1: fileList: "
38950276Speter		    "want ASN.1 sequence, have %s (NID %d)",
39050276Speter		    p->fn, ASN1_tag2str(t->type), t->type);
39162449Speter		goto out;
39276726Speter	}
39350276Speter
39450276Speter	if (!mft_parse_flist(p, t->value.octet_string))
39550276Speter		goto out;
39650276Speter
39750276Speter	rc = 1;
39866963Speterout:
39966963Speter	sk_ASN1_TYPE_pop_free(seq, ASN1_TYPE_free);
40066963Speter	BN_free(mft_seqnum);
40150276Speter	return rc;
40266963Speter}
40366963Speter
40466963Speter/*
40566963Speter * Parse the objects that have been published in the manifest.
40666963Speter * This conforms to RFC 6486.
40766963Speter * Note that if the MFT is stale, all referenced objects are stripped
40866963Speter * from the parsed content.
40966963Speter * The MFT content is otherwise returned.
41066963Speter */
41166963Speterstruct mft *
41266963Spetermft_parse(X509 **x509, const char *fn, const unsigned char *der, size_t len)
41366963Speter{
41466963Speter	struct parse	 p;
41566963Speter	int		 rc = 0;
41666963Speter	size_t		 cmsz;
41766963Speter	unsigned char	*cms;
41866963Speter
41966963Speter	memset(&p, 0, sizeof(struct parse));
42066963Speter	p.fn = fn;
42150276Speter
42250276Speter	if (mft_oid == NULL) {
42350276Speter		mft_oid = OBJ_txt2obj("1.2.840.113549.1.9.16.1.26", 1);
42450276Speter		if (mft_oid == NULL)
42550276Speter			errx(1, "OBJ_txt2obj for %s failed",
42650276Speter			    "1.2.840.113549.1.9.16.1.26");
42750276Speter	}
42850276Speter
42950276Speter	cms = cms_parse_validate(x509, fn, der, len, mft_oid, &cmsz);
43066963Speter	if (cms == NULL)
43166963Speter		return NULL;
43266963Speter	assert(*x509 != NULL);
43366963Speter
43466963Speter	if ((p.res = calloc(1, sizeof(struct mft))) == NULL)
43566963Speter		err(1, NULL);
43666963Speter	if ((p.res->file = strdup(fn)) == NULL)
43750276Speter		err(1, NULL);
43850276Speter
43950276Speter	p.res->aia = x509_get_aia(*x509, fn);
44050276Speter	p.res->aki = x509_get_aki(*x509, 0, fn);
44150276Speter	p.res->ski = x509_get_ski(*x509, fn);
44250276Speter	if (p.res->aia == NULL || p.res->aki == NULL || p.res->ski == NULL) {
44350276Speter		warnx("%s: RFC 6487 section 4.8: "
44450276Speter		    "missing AIA, AKI or SKI X509 extension", fn);
44550276Speter		goto out;
44666963Speter	}
44766963Speter
44866963Speter	if (mft_parse_econtent(cms, cmsz, &p) == 0)
44966963Speter		goto out;
45066963Speter
45166963Speter	rc = 1;
45266963Speterout:
45366963Speter	if (rc == 0) {
45466963Speter		mft_free(p.res);
45550276Speter		p.res = NULL;
45650276Speter		X509_free(*x509);
45766963Speter		*x509 = NULL;
45866963Speter	}
45950276Speter	free(cms);
46050276Speter	return p.res;
46150276Speter}
46250276Speter
46350276Speter/*
46450276Speter * Check all files and their hashes in a MFT structure.
46550276Speter * Return zero on failure, non-zero on success.
46650276Speter */
46750276Speterint
46866963Spetermft_check(const char *fn, struct mft *p)
46966963Speter{
47066963Speter	size_t	i;
47166963Speter	int	rc = 1;
47266963Speter	char	*cp, *h, *path = NULL;
47366963Speter
47466963Speter	/* Check hash of file now, but first build path for it */
47550276Speter	cp = strrchr(fn, '/');
47650276Speter	assert(cp != NULL);
47750276Speter	assert(cp - fn < INT_MAX);
47850276Speter
47950276Speter	for (i = 0; i < p->filesz; i++) {
48050276Speter		const struct mftfile *m = &p->files[i];
48150276Speter		if (!valid_filename(m->file)) {
48250276Speter			if (base64_encode(m->hash, sizeof(m->hash), &h) == -1)
48350276Speter				errx(1, "base64_encode failed in %s", __func__);
48450276Speter			warnx("%s: unsupported filename for %s", fn, h);
48550276Speter			free(h);
48650276Speter			continue;
48750276Speter		}
48850276Speter		if (asprintf(&path, "%.*s/%s", (int)(cp - fn), fn,
48950276Speter		    m->file) == -1)
49050276Speter			err(1, NULL);
49150276Speter		if (!valid_filehash(path, m->hash, sizeof(m->hash))) {
49250276Speter			warnx("%s: bad message digest for %s", fn, m->file);
49350276Speter			rc = 0;
49450276Speter		}
49550276Speter		free(path);
49650276Speter	}
49750276Speter
49850276Speter	return rc;
49950276Speter}
50050276Speter
50150276Speter/*
50250276Speter * Free an MFT pointer.
50350276Speter * Safe to call with NULL.
50450276Speter */
50550276Spetervoid
50650276Spetermft_free(struct mft *p)
50750276Speter{
50850276Speter	size_t	 i;
50950276Speter
51050276Speter	if (p == NULL)
51150276Speter		return;
51250276Speter
51350276Speter	if (p->files != NULL)
51450276Speter		for (i = 0; i < p->filesz; i++)
51550276Speter			free(p->files[i].file);
51650276Speter
51750276Speter	free(p->aia);
51850276Speter	free(p->aki);
51950276Speter	free(p->ski);
52050276Speter	free(p->file);
52150276Speter	free(p->files);
52250276Speter	free(p->seqnum);
52350276Speter	free(p);
52450276Speter}
52550276Speter
52650276Speter/*
52750276Speter * Serialise MFT parsed content into the given buffer.
52850276Speter * See mft_read() for the other side of the pipe.
52950276Speter */
53050276Spetervoid
53150276Spetermft_buffer(struct ibuf *b, const struct mft *p)
53250276Speter{
53350276Speter	size_t		 i;
53450276Speter
53550276Speter	io_simple_buffer(b, &p->stale, sizeof(int));
53650276Speter	io_str_buffer(b, p->file);
53750276Speter	io_simple_buffer(b, &p->filesz, sizeof(size_t));
53850276Speter
53950276Speter	for (i = 0; i < p->filesz; i++) {
54050276Speter		io_str_buffer(b, p->files[i].file);
54150276Speter		io_simple_buffer(b, p->files[i].hash, SHA256_DIGEST_LENGTH);
54250276Speter	}
54350276Speter
54450276Speter	io_str_buffer(b, p->aia);
54550276Speter	io_str_buffer(b, p->aki);
54650276Speter	io_str_buffer(b, p->ski);
54762449Speter}
54862449Speter
54962449Speter/*
55062449Speter * Read an MFT structure from the file descriptor.
55162449Speter * Result must be passed to mft_free().
55262449Speter */
55362449Speterstruct mft *
55462449Spetermft_read(struct ibuf *b)
55562449Speter{
55676726Speter	struct mft	*p = NULL;
55776726Speter	size_t		 i;
55876726Speter
55976726Speter	if ((p = calloc(1, sizeof(struct mft))) == NULL)
56076726Speter		err(1, NULL);
56176726Speter
56276726Speter	io_read_buf(b, &p->stale, sizeof(int));
56376726Speter	io_read_str(b, &p->file);
56476726Speter	io_read_buf(b, &p->filesz, sizeof(size_t));
56550276Speter
56650276Speter	assert(p->file);
56750276Speter	if ((p->files = calloc(p->filesz, sizeof(struct mftfile))) == NULL)
56850276Speter		err(1, NULL);
56950276Speter
57050276Speter	for (i = 0; i < p->filesz; i++) {
57150276Speter		io_read_str(b, &p->files[i].file);
57250276Speter		io_read_buf(b, p->files[i].hash, SHA256_DIGEST_LENGTH);
57350276Speter	}
57462449Speter
57562449Speter	io_read_str(b, &p->aia);
57662449Speter	io_read_str(b, &p->aki);
57762449Speter	io_read_str(b, &p->ski);
57862449Speter	assert(p->aia && p->aki && p->ski);
57962449Speter
58062449Speter	return p;
58162449Speter}
58262449Speter