Properly handle .mft files as intended by the RFC. Instead of always
authorclaudio <claudio@openbsd.org>
Fri, 28 Jan 2022 15:30:23 +0000 (15:30 +0000)
committerclaudio <claudio@openbsd.org>
Fri, 28 Jan 2022 15:30:23 +0000 (15:30 +0000)
selecting the newest file this opens both the new (from rrdp or rsync)
and old (valid) MFT. It then compares the manifest number and based on
that the 'newer' MFT is selected.
The MFT file and hash check is also changed to always try both locations
and selecting whatever matches up with the hash. The selction is passed
back to the the main process and used later on to open exactly the same
file as was checked against the hash.
The MFT parsing code has been split up into multiple steps so that the
files can be parsed, compared and then fully validated.
In most cases this makes no difference but it prevents replay attacks
using old but still valid files.
With and OK tb@

usr.sbin/rpki-client/extern.h
usr.sbin/rpki-client/main.c
usr.sbin/rpki-client/mft.c
usr.sbin/rpki-client/parser.c

index d4f0f18..28f64da 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: extern.h,v 1.115 2022/01/24 17:29:37 claudio Exp $ */
+/*     $OpenBSD: extern.h,v 1.116 2022/01/28 15:30:23 claudio Exp $ */
 /*
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
  *
@@ -164,12 +164,19 @@ enum rtype {
        RTYPE_FILE,
 };
 
+enum location {
+       DIR_UNKNOWN,
+       DIR_TEMP,
+       DIR_VALID,
+};
+
 /*
  * Files specified in an MFT have their bodies hashed with SHA256.
  */
 struct mftfile {
        char            *file; /* filename (CER/ROA/CRL, no path) */
        enum rtype       type; /* file type as determined by extension */
+       enum location    location;      /* temporary or valid directory */
        unsigned char    hash[SHA256_DIGEST_LENGTH]; /* sha256 of body */
 };
 
@@ -181,11 +188,13 @@ struct mftfile {
 struct mft {
        char            *path; /* relative path to directory of the MFT */
        struct mftfile  *files; /* file and hash */
-       size_t           filesz; /* number of filenames */
        char            *seqnum; /* manifestNumber */
        char            *aia; /* AIA */
        char            *aki; /* AKI */
        char            *ski; /* SKI */
+       time_t           valid_from;
+       time_t           valid_until;
+       size_t           filesz; /* number of filenames */
        unsigned int     repoid;
        int              stale; /* if a stale manifest */
 };
@@ -349,6 +358,7 @@ struct entity {
        unsigned int     repoid;        /* repository identifier */
        int              talid;         /* tal identifier */
        enum rtype       type;          /* type of entity (not RTYPE_EOF) */
+       enum location    location;      /* which directroy the file lives in */
 };
 TAILQ_HEAD(entityq, entity);
 
@@ -416,12 +426,13 @@ struct cert       *ta_parse(const char *, const unsigned char *, size_t,
 struct cert    *cert_read(struct ibuf *);
 void            cert_insert_brks(struct brk_tree *, struct cert *);
 
+enum rtype      rtype_from_file_extension(const char *);
 void            mft_buffer(struct ibuf *, const struct mft *);
 void            mft_free(struct mft *);
 struct mft     *mft_parse(X509 **, const char *, const unsigned char *,
                    size_t);
 struct mft     *mft_read(struct ibuf *);
-enum rtype      rtype_from_file_extension(const char *);
+int             mft_compare(const struct mft *, const struct mft *);
 
 void            roa_buffer(struct ibuf *, const struct roa *);
 void            roa_free(struct roa *);
index 90cb3ab..84bae59 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: main.c,v 1.186 2022/01/26 14:42:39 claudio Exp $ */
+/*     $OpenBSD: main.c,v 1.187 2022/01/28 15:30:23 claudio Exp $ */
 /*
  * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -120,6 +120,7 @@ void
 entity_read_req(struct ibuf *b, struct entity *ent)
 {
        io_read_buf(b, &ent->type, sizeof(ent->type));
+       io_read_buf(b, &ent->location, sizeof(ent->location));
        io_read_buf(b, &ent->repoid, sizeof(ent->repoid));
        io_read_buf(b, &ent->talid, sizeof(ent->talid));
        io_read_str(b, &ent->path);
@@ -138,6 +139,7 @@ entity_write_req(const struct entity *ent)
 
        b = io_new_buffer();
        io_simple_buffer(b, &ent->type, sizeof(ent->type));
+       io_simple_buffer(b, &ent->location, sizeof(ent->location));
        io_simple_buffer(b, &ent->repoid, sizeof(ent->repoid));
        io_simple_buffer(b, &ent->talid, sizeof(ent->talid));
        io_str_buffer(b, ent->path);
@@ -151,6 +153,7 @@ entity_write_repo(struct repo *rp)
 {
        struct ibuf *b;
        enum rtype type = RTYPE_REPO;
+       enum location loc = DIR_UNKNOWN;
        unsigned int repoid;
        char *path, *altpath;
        int talid = 0;
@@ -160,6 +163,7 @@ entity_write_repo(struct repo *rp)
        altpath = repo_basedir(rp, 1);
        b = io_new_buffer();
        io_simple_buffer(b, &type, sizeof(type));
+       io_simple_buffer(b, &loc, sizeof(loc));
        io_simple_buffer(b, &repoid, sizeof(repoid));
        io_simple_buffer(b, &talid, sizeof(talid));
        io_str_buffer(b, path);
@@ -192,8 +196,8 @@ entityq_flush(struct entityq *q, struct repo *rp)
  * Add the heap-allocated file to the queue for processing.
  */
 static void
-entityq_add(char *path, char *file, enum rtype type, struct repo *rp,
-    unsigned char *data, size_t datasz, int talid)
+entityq_add(char *path, char *file, enum rtype type, enum location loc,
+    struct repo *rp, unsigned char *data, size_t datasz, int talid)
 {
        struct entity   *p;
 
@@ -201,6 +205,7 @@ entityq_add(char *path, char *file, enum rtype type, struct repo *rp,
                err(1, NULL);
 
        p->type = type;
+       p->location = loc;
        p->talid = talid;
        p->path = path;
        if (rp != NULL)
@@ -341,7 +346,7 @@ queue_add_from_mft(const char *path, const struct mftfile *file,
        if ((nfile = strdup(file->file)) == NULL)
                err(1, NULL);
 
-       entityq_add(npath, nfile, file->type, rp, NULL, 0, -1);
+       entityq_add(npath, nfile, file->type, file->location, rp, NULL, 0, -1);
 }
 
 /*
@@ -400,7 +405,7 @@ queue_add_file(const char *file, enum rtype type, int talid)
        if ((nfile = strdup(file)) == NULL)
                err(1, NULL);
        /* Not in a repository, so directly add to queue. */
-       entityq_add(NULL, nfile, type, NULL, buf, len, talid);
+       entityq_add(NULL, nfile, type, DIR_UNKNOWN, NULL, buf, len, talid);
 }
 
 /*
@@ -434,7 +439,8 @@ queue_add_from_tal(struct tal *tal)
        /* steal the pkey from the tal structure */
        data = tal->pkey;
        tal->pkey = NULL;
-       entityq_add(NULL, nfile, RTYPE_CER, repo, data, tal->pkeysz, tal->id);
+       entityq_add(NULL, nfile, RTYPE_CER, DIR_VALID, repo, data,
+           tal->pkeysz, tal->id);
 }
 
 /*
@@ -477,7 +483,7 @@ queue_add_from_cert(const struct cert *cert)
                        err(1, NULL);
        }
 
-       entityq_add(npath, nfile, RTYPE_MFT, repo, NULL, 0, -1);
+       entityq_add(npath, nfile, RTYPE_MFT, DIR_UNKNOWN, repo, NULL, 0, -1);
 }
 
 /*
index d1e6c1e..30014dc 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: mft.c,v 1.51 2022/01/24 17:29:37 claudio Exp $ */
+/*     $OpenBSD: mft.c,v 1.52 2022/01/28 15:30:23 claudio Exp $ */
 /*
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
  *
@@ -42,23 +42,6 @@ struct       parse {
 
 extern ASN1_OBJECT    *mft_oid;
 
-static const char *
-gentime2str(const ASN1_GENERALIZEDTIME *time)
-{
-       static char     buf[64];
-       BIO             *mem;
-
-       if ((mem = BIO_new(BIO_s_mem())) == NULL)
-               cryptoerrx("BIO_new");
-       if (!ASN1_GENERALIZEDTIME_print(mem, time))
-               cryptoerrx("ASN1_GENERALIZEDTIME_print");
-       if (BIO_gets(mem, buf, sizeof(buf)) < 0)
-               cryptoerrx("BIO_gets");
-
-       BIO_free(mem);
-       return buf;
-}
-
 /*
  * Convert an ASN1_GENERALIZEDTIME to a struct tm.
  * Returns 1 on success, 0 on failure.
@@ -79,45 +62,33 @@ generalizedtime_to_tm(const ASN1_GENERALIZEDTIME *gtime, struct tm *tm)
 
 /*
  * Validate and verify the time validity of the mft.
- * Returns 1 if all is good, 0 if mft is stale, any other case -1.
+ * Returns 1 if all is good and for any other case 0.
  */
 static int
-check_validity(const ASN1_GENERALIZEDTIME *from,
-    const ASN1_GENERALIZEDTIME *until, const char *fn)
+mft_parse_time(const ASN1_GENERALIZEDTIME *from,
+    const ASN1_GENERALIZEDTIME *until, struct parse *p)
 {
-       time_t now = time(NULL);
-       struct tm tm_from, tm_until, tm_now;
-
-       if (gmtime_r(&now, &tm_now) == NULL) {
-               warnx("%s: could not get current time", fn);
-               return -1;
-       }
+       struct tm tm_from, tm_until;
 
        if (!generalizedtime_to_tm(from, &tm_from)) {
-               warnx("%s: embedded from time format invalid", fn);
-               return -1;
+               warnx("%s: embedded from time format invalid", p->fn);
+               return 0;
        }
        if (!generalizedtime_to_tm(until, &tm_until)) {
-               warnx("%s: embedded until time format invalid", fn);
-               return -1;
+               warnx("%s: embedded until time format invalid", p->fn);
+               return 0;
        }
 
        /* check that until is not before from */
        if (ASN1_time_tm_cmp(&tm_until, &tm_from) < 0) {
-               warnx("%s: bad update interval", fn);
-               return -1;
-       }
-       /* check that now is not before from */
-       if (ASN1_time_tm_cmp(&tm_from, &tm_now) > 0) {
-               warnx("%s: mft not yet valid %s", fn, gentime2str(from));
-               return -1;
-       }
-       /* check that now is not after until */
-       if (ASN1_time_tm_cmp(&tm_until, &tm_now) < 0) {
-               warnx("%s: mft expired on %s", fn, gentime2str(until));
+               warnx("%s: bad update interval", p->fn);
                return 0;
        }
 
+       if ((p->res->valid_from = mktime(&tm_from)) == -1 ||
+           (p->res->valid_until = mktime(&tm_until)) == -1)
+               errx(1, "%s: mktime failed", p->fn);
+
        return 1;
 }
 
@@ -426,15 +397,8 @@ mft_parse_econtent(const unsigned char *d, size_t dsz, struct parse *p)
        }
        until = t->value.generalizedtime;
 
-       switch (check_validity(from, until, p->fn)) {
-       case 0:
-               p->res->stale = 1;
-               /* FALLTHROUGH */
-       case 1:
-               break;
-       case -1:
+       if (!mft_parse_time(from, until, p))
                goto out;
-       }
 
        /* File list algorithm. */
 
@@ -570,6 +534,8 @@ mft_buffer(struct ibuf *b, const struct mft *p)
                io_str_buffer(b, p->files[i].file);
                io_simple_buffer(b, &p->files[i].type,
                    sizeof(p->files[i].type));
+               io_simple_buffer(b, &p->files[i].location,
+                   sizeof(p->files[i].location));
                io_simple_buffer(b, p->files[i].hash, SHA256_DIGEST_LENGTH);
        }
 }
@@ -603,8 +569,36 @@ mft_read(struct ibuf *b)
        for (i = 0; i < p->filesz; i++) {
                io_read_str(b, &p->files[i].file);
                io_read_buf(b, &p->files[i].type, sizeof(p->files[i].type));
+               io_read_buf(b, &p->files[i].location,
+                   sizeof(p->files[i].location));
                io_read_buf(b, p->files[i].hash, SHA256_DIGEST_LENGTH);
        }
 
        return p;
 }
+
+/*
+ * Compare two MFT files, returns 1 if first MFT is preferred and 0 if second
+ * MFT should be used.
+ */
+int
+mft_compare(const struct mft *a, const struct mft *b)
+{
+       int r;
+
+       if (b == NULL)
+               return 1;
+       if (a == NULL)
+               return 0;
+
+       r = strlen(a->seqnum) - strlen(b->seqnum);
+       if (r > 0)      /* seqnum in a is longer -> higher */
+               return 1;
+       if (r < 0)      /* seqnum in a is shorter -> smaller */
+               return 0;
+
+       r = strcmp(a->seqnum, b->seqnum);
+       if (r >= 0)     /* a is greater or equal, prefer a */
+               return 1;
+       return 0;
+}
index e583650..37250f3 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: parser.c,v 1.57 2022/01/28 06:33:27 guenther Exp $ */
+/*     $OpenBSD: parser.c,v 1.58 2022/01/28 15:30:23 claudio Exp $ */
 /*
  * Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org>
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -91,49 +91,48 @@ repo_add(unsigned int id, char *path, char *validpath)
                errx(1, "repository already added: id %d, %s", id, path);
 }
 
+static char *
+time2str(time_t t)
+{
+       static char buf[64];
+       struct tm tm;
+
+       if (gmtime_r(&t, &tm) == NULL)
+               return "could not convert time";
+
+       strftime(buf, sizeof(buf), "%h %d %T %Y %Z", &tm);
+       return buf;
+}
+
 /*
- * Build access path to file based on repoid, path and file values.
- * If wantalt == 1 the function can return NULL, if wantalt == 0 it
- * can not fail.
+ * Build access path to file based on repoid, path, location and file values.
  */
 static char *
 parse_filepath(unsigned int repoid, const char *path, const char *file,
-    int wantalt)
+    enum location loc)
 {
        struct parse_repo       *rp;
        char                    *fn, *repopath;
 
        /* build file path based on repoid, entity path and filename */
        rp = repo_get(repoid);
-       if (rp == NULL) {
-               /* no repo so no alternative path. */
-               if (wantalt)
-                       return NULL;
-
-               if (path == NULL) {
-                       if ((fn = strdup(file)) == NULL)
-                               err(1, NULL);
-               } else {
-                       if (asprintf(&fn, "%s/%s", path, file) == -1)
-                               err(1, NULL);
-               }
-       } else {
-               if (wantalt || rp->path == NULL)
-                       repopath = rp->validpath;
-               else
-                       repopath = rp->path;
+       if (rp == NULL)
+               return NULL;
+       
+       if (loc == DIR_VALID)
+               repopath = rp->validpath;
+       else
+               repopath = rp->path;
 
-               if (repopath == NULL)
-                       return NULL;
+       if (repopath == NULL)
+               return NULL;
 
-               if (path == NULL) {
-                       if (asprintf(&fn, "%s/%s", repopath, file) == -1)
-                               err(1, NULL);
-               } else {
-                       if (asprintf(&fn, "%s/%s/%s", repopath, path,
-                           file) == -1)
-                               err(1, NULL);
-               }
+       if (path == NULL) {
+               if (asprintf(&fn, "%s/%s", repopath, file) == -1)
+                       err(1, NULL);
+       } else {
+               if (asprintf(&fn, "%s/%s/%s", repopath, path, file) == -1)
+                       err(1, NULL);
        }
        return fn;
 }
@@ -205,7 +204,7 @@ verify_cb(int ok, X509_STORE_CTX *store_ctx)
  */
 static int
 valid_x509(char *file, X509 *x509, struct auth *a, struct crl *crl,
-    unsigned long flags)
+    unsigned long flags, int nowarn)
 {
        STACK_OF(X509)          *chain;
        STACK_OF(X509_CRL)      *crls = NULL;
@@ -229,7 +228,8 @@ valid_x509(char *file, X509 *x509, struct auth *a, struct crl *crl,
 
        if (X509_verify_cert(ctx) <= 0) {
                c = X509_STORE_CTX_get_error(ctx);
-               warnx("%s: %s", file, X509_verify_cert_error_string(c));
+               if (!nowarn || verbose > 1)
+                       warnx("%s: %s", file, X509_verify_cert_error_string(c));
                X509_STORE_CTX_cleanup(ctx);
                sk_X509_free(chain);
                sk_X509_CRL_free(crls);
@@ -261,7 +261,7 @@ proc_parser_roa(char *file, const unsigned char *der, size_t len)
        a = valid_ski_aki(file, &auths, roa->ski, roa->aki);
        crl = get_crl(a);
 
-       if (!valid_x509(file, x509, a, crl, X509_V_FLAG_CRL_CHECK)) {
+       if (!valid_x509(file, x509, a, crl, X509_V_FLAG_CRL_CHECK, 0)) {
                X509_free(x509);
                roa_free(roa);
                return NULL;
@@ -303,28 +303,35 @@ proc_parser_roa(char *file, const unsigned char *der, size_t len)
 static int
 proc_parser_mft_check(const char *fn, struct mft *p)
 {
-       size_t  i;
-       int     rc = 1;
+       const enum location loc[2] = { DIR_TEMP, DIR_VALID };
+       size_t   i;
+       int      rc = 1;
        char    *path;
 
        for (i = 0; i < p->filesz; i++) {
-               const struct mftfile *m = &p->files[i];
-               int fd = -1, try = 0;
-
-               path = NULL;
-               do {
-                       free(path);
+               struct mftfile *m = &p->files[i];
+               int try, fd = -1, noent = 0, valid = 0;
+               for (try = 0; try < 2 && !valid; try++) {
                        if ((path = parse_filepath(p->repoid, p->path, m->file,
-                           try++)) == NULL)
-                               break;
+                           loc[try])) == NULL)
+                               continue;
                        fd = open(path, O_RDONLY);
-               } while (fd == -1 && try < 2);
+                       if (fd == -1 && errno == ENOENT)
+                               noent++;
+                       free(path);
 
-               free(path);
+                       /* remember which path was checked */
+                       m->location = loc[try];
+                       valid = valid_filehash(fd, m->hash, sizeof(m->hash));
+               }
 
-               if (!valid_filehash(fd, m->hash, sizeof(m->hash))) {
+               if (!valid) {
+                       /* silently skip not-existing unknown files */
+                       if (m->type == RTYPE_INVALID && noent == 2)
+                               continue;
                        warnx("%s: bad message digest for %s", fn, m->file);
                        rc = 0;
+                       continue;
                }
        }
 
@@ -332,7 +339,9 @@ proc_parser_mft_check(const char *fn, struct mft *p)
 }
 
 /*
- * Parse and validate a manifest file.
+ * Parse and validate a manifest file. Skip checking the fileandhash
+ * this is done in the post check. After this step we know the mft is
+ * valid and can be compared.
  * Here we *don't* validate against the list of CRLs, because the
  * certificate used to sign the manifest may specify a CRL that the root
  * certificate didn't, and we haven't scanned for it yet.
@@ -342,8 +351,7 @@ proc_parser_mft_check(const char *fn, struct mft *p)
  * Return the mft on success or NULL on failure.
  */
 static struct mft *
-proc_parser_mft(char *file, const unsigned char *der, size_t len,
-    const char *path, unsigned int repoid)
+proc_parser_mft_pre(char *file, const unsigned char *der, size_t len)
 {
        struct mft              *mft;
        X509                    *x509;
@@ -355,13 +363,45 @@ proc_parser_mft(char *file, const unsigned char *der, size_t len,
        a = valid_ski_aki(file, &auths, mft->ski, mft->aki);
 
        /* CRL checks disabled here because CRL is referenced from mft */
-       if (!valid_x509(file, x509, a, NULL, 0)) {
+       if (!valid_x509(file, x509, a, NULL, 0, 1)) {
                mft_free(mft);
                X509_free(x509);
                return NULL;
        }
        X509_free(x509);
 
+       return mft;
+}
+
+/*
+ * Do the end of manifest validation.
+ * Return the mft on success or NULL on failure.
+ */
+static struct mft *
+proc_parser_mft_post(char *file, struct mft *mft, const char *path,
+    unsigned int repoid)
+{
+       /* check that now is not before from */
+       time_t now = time(NULL);
+
+       if (mft == NULL) {
+               warnx("%s: no valid mft available", file);
+               return NULL;
+       }
+
+       /* check that now is not before from */
+       if (now < mft->valid_from) {
+               warnx("%s: mft not yet valid %s", file,
+                   time2str(mft->valid_from));
+               mft->stale = 1;
+       }
+       /* check that now is not after until */
+       if (now > mft->valid_until) {
+               warnx("%s: mft expired on %s", file,
+                   time2str(mft->valid_until));
+               mft->stale = 1;
+       }
+
        mft->repoid = repoid;
        if (path != NULL)
                if ((mft->path = strdup(path)) == NULL)
@@ -388,7 +428,7 @@ proc_parser_cert_validate(char *file, struct cert *cert)
        a = valid_ski_aki(file, &auths, cert->ski, cert->aki);
        crl = get_crl(a);
 
-       if (!valid_x509(file, cert->x509, a, crl, X509_V_FLAG_CRL_CHECK)) {
+       if (!valid_x509(file, cert->x509, a, crl, X509_V_FLAG_CRL_CHECK, 0)) {
                cert_free(cert);
                return NULL;
        }
@@ -537,7 +577,7 @@ proc_parser_gbr(char *file, const unsigned char *der, size_t len)
        crl = get_crl(a);
 
        /* return value can be ignored since nothing happens here */
-       valid_x509(file, x509, a, crl, X509_V_FLAG_CRL_CHECK);
+       valid_x509(file, x509, a, crl, X509_V_FLAG_CRL_CHECK, 0);
 
        X509_free(x509);
        gbr_free(gbr);
@@ -599,42 +639,63 @@ build_crls(const struct crl *crl, STACK_OF(X509_CRL) **crls)
                err(1, "sk_X509_CRL_push");
 }
 
+/*
+ * Load the file specified by the entity information.
+ */
 static char *
 parse_load_file(struct entity *entp, unsigned char **f, size_t *flen)
 {
-       char *file, *nfile;
+       char *file;
 
-       file = parse_filepath(entp->repoid, entp->path, entp->file, 0);
-
-       /* TAL files include the data already */
-       if (entp->type == RTYPE_TAL) {
-               *f = NULL;
-               *flen = 0;
-               return file;
-       }
+       file = parse_filepath(entp->repoid, entp->path, entp->file,
+           entp->location);
+       if (file == NULL)
+               errx(1, "no path to file");
 
        *f = load_file(file, flen);
-       if (*f != NULL)
-               return file;
+       if (*f == NULL)
+               warn("parse file %s", file);
+
+       return file;
+}
 
-       if (errno != ENOENT)
-               goto fail;
+static char *
+parse_load_mft(struct entity *entp, struct mft **mft)
+{
+       struct mft      *mft1 = NULL, *mft2 = NULL;
+       char            *f, *file1, *file2;
+       size_t           flen;
 
-       /* try alternate file location */
-       nfile = parse_filepath(entp->repoid, entp->path, entp->file, 1);
-       if (nfile == NULL)
-               goto fail;
+       file1 = parse_filepath(entp->repoid, entp->path, entp->file, DIR_VALID);
+       file2 = parse_filepath(entp->repoid, entp->path, entp->file, DIR_TEMP);
 
-       free(file);
-       file = nfile;
+       if (file1 != NULL) {
+               f = load_file(file1, &flen);
+               if (f == NULL && errno != ENOENT)
+                       warn("parse file %s", file1);
+               mft1 = proc_parser_mft_pre(file1, f, flen);
+               free(f);
+       }
 
-       *f = load_file(file, flen);
-       if (*f != NULL)
-               return file;
+       if (file2 != NULL) {
+               f = load_file(file2, &flen);
+               if (f == NULL && errno != ENOENT)
+                       warn("parse file %s", file2);
+               mft2 = proc_parser_mft_pre(file2, f, flen);
+               free(f);
+       }
 
-fail:
-       warn("parse file %s", file);
-       return file;
+       if (mft_compare(mft1, mft2) == 1) {
+               mft_free(mft2);
+               free(file2);
+               *mft = mft1;
+               return file1;
+       } else {
+               mft_free(mft1);
+               free(file1);
+               *mft = mft2;
+               return file2;
+       }
 }
 
 /*
@@ -664,15 +725,15 @@ parse_entity(struct entityq *q, struct msgbuf *msgq)
                        continue;
                }
 
-               file = parse_load_file(entp, &f, &flen);
-
                /* pass back at least type, repoid and filename */
                b = io_new_buffer();
                io_simple_buffer(b, &entp->type, sizeof(entp->type));
-               io_str_buffer(b, file);
 
+               file = NULL;
+               f = NULL;
                switch (entp->type) {
                case RTYPE_TAL:
+                       io_str_buffer(b, entp->file);
                        if ((tal = tal_parse(entp->file, entp->data,
                            entp->datasz)) == NULL)
                                errx(1, "%s: could not parse tal file",
@@ -682,6 +743,8 @@ parse_entity(struct entityq *q, struct msgbuf *msgq)
                        tal_free(tal);
                        break;
                case RTYPE_CER:
+                       file = parse_load_file(entp, &f, &flen);
+                       io_str_buffer(b, file);
                        if (entp->data != NULL)
                                cert = proc_parser_root_cert(file,
                                    f, flen, entp->data, entp->datasz,
@@ -695,15 +758,21 @@ parse_entity(struct entityq *q, struct msgbuf *msgq)
                        /*
                         * The parsed certificate data "cert" is now
                         * managed in the "auths" table, so don't free
-                        * it here (see the loop after "out").
+                        * it here.
                         */
                        break;
                case RTYPE_CRL:
+                       file = parse_load_file(entp, &f, &flen);
+                       io_str_buffer(b, file);
                        proc_parser_crl(file, f, flen);
                        break;
                case RTYPE_MFT:
-                       mft = proc_parser_mft(file, f, flen,
+                       file = parse_load_mft(entp, &mft);
+
+                       mft = proc_parser_mft_post(file, mft,
                            entp->path, entp->repoid);
+
+                       io_str_buffer(b, file);
                        c = (mft != NULL);
                        io_simple_buffer(b, &c, sizeof(int));
                        if (mft != NULL)
@@ -711,6 +780,8 @@ parse_entity(struct entityq *q, struct msgbuf *msgq)
                        mft_free(mft);
                        break;
                case RTYPE_ROA:
+                       file = parse_load_file(entp, &f, &flen);
+                       io_str_buffer(b, file);
                        roa = proc_parser_roa(file, f, flen);
                        c = (roa != NULL);
                        io_simple_buffer(b, &c, sizeof(int));
@@ -719,6 +790,8 @@ parse_entity(struct entityq *q, struct msgbuf *msgq)
                        roa_free(roa);
                        break;
                case RTYPE_GBR:
+                       file = parse_load_file(entp, &f, &flen);
+                       io_str_buffer(b, file);
                        proc_parser_gbr(file, f, flen);
                        break;
                default:
@@ -981,7 +1054,7 @@ proc_parser_file(char *file, unsigned char *buf, size_t len)
                a = auth_find(&auths, aki);
                crl = get_crl(a);
 
-               if (valid_x509(file, x509, a, crl, verify_flags))
+               if (valid_x509(file, x509, a, crl, verify_flags, 0))
                        printf("Validation: OK\n");
                else
                        printf("Validation: Failed\n");