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