From: Alberto Leiva Popper Date: Mon, 2 Dec 2024 21:11:45 +0000 (-0300) Subject: Always pick most recent known manifest X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5f7ee67f5fa8b43730b06fb7e0ac6ca0af08faf6;p=thirdparty%2FFORT-validator.git Always pick most recent known manifest Non-strict version. Fixes new (still unnumbered) CVE. --- diff --git a/src/cache.c b/src/cache.c index 2f4572e3..f3b9e451 100644 --- a/src/cache.c +++ b/src/cache.c @@ -62,6 +62,7 @@ struct cache_node { time_t attempt_ts; /* Refresh: Dl attempt. Fallback: Commit */ time_t success_ts; /* Refresh: Dl success. Fallback: Commit */ + struct mft_meta mft; /* RPP fallbacks only */ struct rrdp_state *rrdp; UT_hash_handle hh; /* Hash table hook */ @@ -80,7 +81,7 @@ struct cache_table { char *name; bool enabled; struct cache_sequence seq; - struct cache_node *nodes; /* Hash Table */ + struct cache_node *nodes; /* Hash Table */ dl_cb download; pthread_mutex_t lock; }; @@ -108,6 +109,7 @@ struct cache_cage { struct cache_node const *refresh; struct cache_node const *fallback; char const *rpkiNotify; + struct mft_meta *mft; /* Fallback */ }; struct cache_commit { @@ -115,6 +117,7 @@ struct cache_commit { char *caRepository; struct cache_mapping *files; size_t nfiles; + struct mft_meta mft; /* RPPs commits only */ STAILQ_ENTRY(cache_commit) lh; }; @@ -392,6 +395,9 @@ json2node(json_t *json) error = json_get_ts(json, "success", &node->success_ts); if (error != 0 && error != ENOENT) goto fail; + error = json_get_ts(json, "mftUpdate", &node->mft.update); + if (error < 0) + goto fail; error = json_get_object(json, "rrdp", &rrdp); if (error < 0) goto fail; @@ -531,6 +537,8 @@ node2json(struct cache_node *node) goto fail; if (node->success_ts && json_add_ts(json, "success", node->success_ts)) goto fail; + if (node->mft.update && json_add_ts(json, "mftUpdate", node->mft.update)) + goto fail; if (node->rrdp) if (json_object_add(json, "rrdp", rrdp_state2json(node->rrdp))) goto fail; @@ -965,6 +973,12 @@ cage_disable_refresh(struct cache_cage *cage) return true; } +struct mft_meta const * +cage_mft_fallback(struct cache_cage *cage) +{ + return cage->mft; +} + /* * Steals ownership of @rpp->files and @rpp->nfiles, but they're not going to be * modified nor deleted until the cache cleanup. @@ -980,6 +994,7 @@ cache_commit_rpp(char const *rpkiNotify, char const *caRepository, commit->caRepository = pstrdup(caRepository); commit->files = rpp->files; commit->nfiles = rpp->nfiles; + commit->mft = rpp->mft; mutex_lock(&commits_lock); STAILQ_INSERT_TAIL(&commits, commit, lh); @@ -1070,6 +1085,8 @@ commit_rpp(struct cache_commit *commit, struct cache_node *fb) char const *dst; array_index i; + fb->mft = commit->mft; + for (i = 0; i < commit->nfiles; i++) { src = commit->files + i; diff --git a/src/cache.h b/src/cache.h index 552b95b5..0be38fff 100644 --- a/src/cache.h +++ b/src/cache.h @@ -44,6 +44,7 @@ struct cache_cage; int cache_refresh_by_sias(struct sia_uris *, struct cache_cage **); char const *cage_map_file(struct cache_cage *, char const *); bool cage_disable_refresh(struct cache_cage *); +struct mft_meta const *cage_mft_fallback(struct cache_cage *); void cache_commit_rpp(char const *, char const *, struct rpp *); void cache_commit_file(struct cache_mapping *); diff --git a/src/object/manifest.c b/src/object/manifest.c index 6700c123..361142e0 100644 --- a/src/object/manifest.c +++ b/src/object/manifest.c @@ -1,3 +1,6 @@ +#define _DEFAULT_SOURCE 1 /* timegm() on Linux */ +#define _DARWIN_C_SOURCE 1 /* timegm() on MacOS */ + #include "object/manifest.h" #include "algorithm.h" @@ -49,7 +52,8 @@ tm_cmp(struct tm *tm1, struct tm *tm2) } static int -validate_dates(GeneralizedTime_t *this, GeneralizedTime_t *next) +validate_dates(GeneralizedTime_t *this, GeneralizedTime_t *next, + struct mft_meta *meta) { #define TM_FMT "%02d/%02d/%02d %02d:%02d:%02d" #define TM_ARGS(tm) \ @@ -98,6 +102,13 @@ validate_dates(GeneralizedTime_t *this, GeneralizedTime_t *next) TM_ARGS(nextUpdate)); } + meta->update = timegm(&thisUpdate); + if (meta->update == (time_t)-1) { + error = errno; + return pr_val_err("Cannot convert '" TM_FMT "' to time_t: %s", + TM_ARGS(thisUpdate), strerror(error)); + } + return 0; #undef TM_FMT @@ -105,7 +116,23 @@ validate_dates(GeneralizedTime_t *this, GeneralizedTime_t *next) } static int -validate_manifest(struct Manifest *manifest) +check_more_recent(struct cache_cage *cage, struct mft_meta *current) +{ + struct mft_meta const *prev; + + prev = cage_mft_fallback(cage); + if (!prev) + return 0; + + if (prev->update && difftime(prev->update, current->update) > 0) + return pr_val_err("The fallback manifest is newer than the downloaded one."); + + return 0; +} + +static int +validate_manifest(struct Manifest *mft, struct cache_cage *cage, + struct mft_meta *meta) { unsigned long version; int error; @@ -130,8 +157,8 @@ validate_manifest(struct Manifest *manifest) */ /* rfc6486#section-4.4.2 */ - if (manifest->version != NULL) { - error = asn_INTEGER2ulong(manifest->version, &version); + if (mft->version != NULL) { + error = asn_INTEGER2ulong(mft->version, &version); if (error) { if (errno) { pr_val_err("Error casting manifest version: %s", @@ -147,11 +174,15 @@ validate_manifest(struct Manifest *manifest) * "Manifest verifiers MUST be able to handle number values up to * 20 octets." */ - if (manifest->manifestNumber.size > 20) + if (mft->manifestNumber.size > 20) return pr_val_err("Manifest number is larger than 20 octets"); /* rfc6486#section-4.4.3 */ - error = validate_dates(&manifest->thisUpdate, &manifest->nextUpdate); + error = validate_dates(&mft->thisUpdate, &mft->nextUpdate, meta); + if (error) + return error; + + error = check_more_recent(cage, meta); if (error) return error; @@ -163,7 +194,7 @@ validate_manifest(struct Manifest *manifest) * I'm going with the signed object hash function, since it appears to * be the closest match. */ - error = validate_cms_hashing_algorithm_oid(&manifest->fileHashAlg, + error = validate_cms_hashing_algorithm_oid(&mft->fileHashAlg, "manifest file"); if (error) return error; @@ -367,7 +398,7 @@ manifest_traverse(char const *url, char const *path, struct cache_cage *cage, error = signed_object_validate(&sobj, &arcs, &ee); if (error) goto end5; - error = validate_manifest(mft); + error = validate_manifest(mft, cage, &parent->rpp.mft); if (error) goto end5; error = refs_validate_ee(&ee.sias, parent->rpp.crl.map->url, url); diff --git a/src/object/manifest.h b/src/object/manifest.h index e3b28b03..ad09fac1 100644 --- a/src/object/manifest.h +++ b/src/object/manifest.h @@ -7,7 +7,7 @@ #include "cache.h" #include "object/certificate.h" -int manifest_traverse(char const *url, char const *path, - struct cache_cage *cage, struct rpki_certificate *parent); +int manifest_traverse(char const *, char const *, struct cache_cage *, + struct rpki_certificate *); #endif /* SRC_OBJECT_MANIFEST_H_ */ diff --git a/src/types/rpp.h b/src/types/rpp.h index 8440700f..97d324b4 100644 --- a/src/types/rpp.h +++ b/src/types/rpp.h @@ -2,9 +2,14 @@ #define SRC_RPP_H_ #include +#include #include "types/map.h" +struct mft_meta { + time_t update; /* Manifest's thisUpdate */ +}; + /* Repository Publication Point */ struct rpp { struct cache_mapping *files; @@ -14,6 +19,8 @@ struct rpp { struct cache_mapping *map; /* Points to @files entry */ X509_CRL *obj; } crl; + + struct mft_meta mft; }; void rpp_cleanup(struct rpp *);