From e5666f2803a53bf11e08f048c5230198c9a6457d Mon Sep 17 00:00:00 2001 From: Alberto Leiva Popper Date: Fri, 27 Sep 2024 20:53:03 -0600 Subject: [PATCH] Hard-linked fallback (Design 5) Testing and course correcting, week 3. Unstable. --- src/Makefile.am | 4 +- src/asn1/signed_data.c | 66 +--- src/asn1/signed_data.h | 21 +- src/cache.c | 646 ++++++++++++++----------------- src/cache.h | 30 +- src/cachent.c | 303 --------------- src/cachent.h | 96 ----- src/cert_stack.c | 404 -------------------- src/cert_stack.h | 32 -- src/certificate_refs.c | 68 +--- src/certificate_refs.h | 42 +- src/common.c | 2 +- src/extension.c | 11 +- src/incidence.c | 6 - src/incidence.h | 2 +- src/object/certificate.c | 744 ++++++++++++++++-------------------- src/object/certificate.h | 47 ++- src/object/crl.c | 8 +- src/object/crl.h | 2 +- src/object/ghostbusters.c | 28 +- src/object/ghostbusters.h | 4 +- src/object/manifest.c | 169 ++++---- src/object/manifest.h | 9 +- src/object/roa.c | 34 +- src/object/roa.h | 4 +- src/object/signed_object.c | 2 +- src/object/signed_object.h | 2 +- src/object/tal.c | 95 +---- src/print_file.c | 7 +- src/resource.c | 101 ++--- src/resource.h | 7 +- src/rpp.c | 144 +------ src/rpp.h | 23 +- src/rrdp.c | 452 ++++++++++++++-------- src/rrdp.h | 51 +-- src/rsync.c | 28 +- src/rsync.h | 4 +- src/state.c | 45 +-- src/state.h | 11 +- src/thread_var.h | 1 + src/types/map.c | 19 - src/types/map.h | 3 - src/types/name.c | 14 +- src/types/name.h | 2 +- test/cache_util.c | 8 +- test/cachent_test.c | 2 +- test/object/manifest_test.c | 4 +- test/object/tal_test.c | 3 - test/rrdp_test.c | 62 +-- 49 files changed, 1304 insertions(+), 2568 deletions(-) delete mode 100644 src/cachent.c delete mode 100644 src/cachent.h delete mode 100644 src/cert_stack.c delete mode 100644 src/cert_stack.h diff --git a/src/Makefile.am b/src/Makefile.am index 83ae4720..2674517a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,10 +10,8 @@ fort_SOURCES += asn1/oid.h asn1/oid.c fort_SOURCES += asn1/signed_data.h asn1/signed_data.c fort_SOURCES += base64.h base64.c fort_SOURCES += cache.c cache.h -fort_SOURCES += cachent.c cachent.h fort_SOURCES += cachetmp.c cachetmp.h fort_SOURCES += certificate_refs.h certificate_refs.c -fort_SOURCES += cert_stack.h cert_stack.c fort_SOURCES += common.c common.h fort_SOURCES += config/boolean.c config/boolean.h fort_SOURCES += config/filename_format.h config/filename_format.c @@ -40,7 +38,7 @@ fort_SOURCES += json_util.c json_util.h fort_SOURCES += libcrypto_util.h libcrypto_util.c fort_SOURCES += log.h log.c fort_SOURCES += nid.h nid.c -fort_SOURCES += object/bgpsec.h object/bgpsec.c +#fort_SOURCES += object/bgpsec.h object/bgpsec.c fort_SOURCES += object/certificate.h object/certificate.c fort_SOURCES += object/crl.h object/crl.c fort_SOURCES += object/ghostbusters.h object/ghostbusters.c diff --git a/src/asn1/signed_data.c b/src/asn1/signed_data.c index 05f120b1..3f37ae4c 100644 --- a/src/asn1/signed_data.c +++ b/src/asn1/signed_data.c @@ -16,21 +16,6 @@ static const OID oid_cta = OID_CONTENT_TYPE_ATTR; static const OID oid_mda = OID_MESSAGE_DIGEST_ATTR; static const OID oid_sta = OID_SIGNING_TIME_ATTR; -void -eecert_init(struct ee_cert *ee, STACK_OF(X509_CRL) *crls, bool force_inherit) -{ - ee->res = resources_create(RPKI_POLICY_RFC6484, force_inherit); - ee->crls = crls; - memset(&ee->refs, 0, sizeof(ee->refs)); -} - -void -eecert_cleanup(struct ee_cert *ee) -{ - resources_destroy(ee->res); - refs_cleanup(&ee->refs); -} - static int get_sid(struct SignerInfo *sinfo, OCTET_STRING_t **result) { @@ -48,12 +33,10 @@ get_sid(struct SignerInfo *sinfo, OCTET_STRING_t **result) } static int -handle_sdata_certificate(ANY_t *cert_encoded, struct ee_cert *ee, +handle_sdata_certificate(ANY_t *cert_encoded, struct rpki_certificate *ee, OCTET_STRING_t *sid, ANY_t *signedData, SignatureValue_t *signature) { const unsigned char *otmp, *tmp; - X509 *cert; - enum rpki_policy policy; int error; /* @@ -70,40 +53,31 @@ handle_sdata_certificate(ANY_t *cert_encoded, struct ee_cert *ee, */ tmp = (const unsigned char *) cert_encoded->buf; otmp = tmp; - cert = d2i_X509(NULL, &tmp, cert_encoded->size); - if (cert == NULL) { - error = val_crypto_err("Signed object's 'certificate' element does not decode into a Certificate"); - goto end1; - } - if (tmp != otmp + cert_encoded->size) { - error = val_crypto_err("Signed object's 'certificate' element contains trailing garbage"); - goto end2; - } + ee->x509 = d2i_X509(NULL, &tmp, cert_encoded->size); + if (ee->x509 == NULL) + return val_crypto_err("Signed object's 'certificate' element does not decode into a Certificate"); + if (tmp != otmp + cert_encoded->size) + return val_crypto_err("Signed object's 'certificate' element contains trailing garbage"); - x509_name_pr_debug("Issuer", X509_get_issuer_name(cert)); + x509_name_pr_debug("Issuer", X509_get_issuer_name(ee->x509)); - error = certificate_validate_chain(cert, ee->crls); + error = certificate_validate_chain(ee); if (error) - goto end2; - error = certificate_validate_rfc6487(cert, CERTYPE_EE); + return error; + error = certificate_validate_rfc6487(ee); if (error) - goto end2; - error = certificate_validate_extensions_ee(cert, sid, &ee->refs, - &policy); + return error; + error = certificate_validate_extensions_ee(ee, sid); if (error) - goto end2; - error = certificate_validate_aia(ee->refs.caIssuers, cert); + return error; + error = certificate_validate_aia(ee); if (error) - goto end2; - error = certificate_validate_signature(cert, signedData, signature); + return error; + error = certificate_validate_signature(ee->x509, signedData, signature); if (error) - goto end2; - - resources_set_policy(ee->res, policy); - error = certificate_get_resources(cert, ee->res, CERTYPE_EE); - -end2: X509_free(cert); -end1: return error; + return error; + resources_set_policy(ee->resources, ee->policy); + return certificate_get_resources(ee); } /* rfc6488#section-2.1.6.4.1 */ @@ -240,7 +214,7 @@ illegal_attrType: int signed_data_validate(ANY_t *encoded, struct SignedData *sdata, - struct ee_cert *ee) + struct rpki_certificate *ee) { struct SignerInfo *sinfo; OCTET_STRING_t *sid = NULL; diff --git a/src/asn1/signed_data.h b/src/asn1/signed_data.h index fe3f6266..fc22061c 100644 --- a/src/asn1/signed_data.h +++ b/src/asn1/signed_data.h @@ -4,26 +4,11 @@ /* Some wrappers for asn1/asn1c/SignedData.h. */ #include "asn1/asn1c/SignedData.h" -#include "certificate_refs.h" -#include "resource.h" - -struct ee_cert { - /** CRL that might or might not revoke the EE certificate. */ - STACK_OF(X509_CRL) *crls; - /** A copy of the resources carried by the EE certificate. */ - struct resources *res; - /** - * A bunch of URLs found in the EE certificate's extensions, - * recorded for future validation. - */ - struct certificate_refs refs; -}; - -void eecert_init(struct ee_cert *, STACK_OF(X509_CRL) *, bool); -void eecert_cleanup(struct ee_cert *); +#include "object/certificate.h" int signed_data_decode(ANY_t *, struct SignedData **); -int signed_data_validate(ANY_t *, struct SignedData *, struct ee_cert *); +int signed_data_validate(ANY_t *, struct SignedData *, + struct rpki_certificate *); int get_content_type_attr(struct SignedData *, OBJECT_IDENTIFIER_t **); diff --git a/src/cache.c b/src/cache.c index 5b07e973..472f731e 100644 --- a/src/cache.c +++ b/src/cache.c @@ -1,14 +1,10 @@ -/* - * - We only need to keep nodes for the rsync root. - * - The tree traverse only needs to touch files. - * - RRDP needs caging. - */ - #include "cache.h" #include +#include +#include -#include "cachent.h" +#include "alloc.h" #include "cachetmp.h" #include "common.h" #include "config.h" @@ -17,30 +13,51 @@ #include "http.h" #include "log.h" #include "rpp.h" +#include "rrdp.h" #include "rsync.h" #include "types/path.h" #include "types/url.h" +#include "types/uthash.h" -/* XXX force RRDP if one RPP fails to validate by rsync? */ +struct cache_node { + struct cache_mapping map; -typedef int (*dl_cb)(struct cache_node *rpp); + int fresh; /* Refresh already attempted? */ + int dlerr; /* Result code of recent download attempt */ + time_t mtim; /* Last successful download time, or zero */ -struct cached_file { - char *url; - char *path; - UT_hash_handle hh; /* Hash table hook */ + struct rrdp_state *rrdp; + + UT_hash_handle hh; /* Hash table hook */ }; -struct cached_rpp { - struct cached_file *ht; +typedef int (*dl_cb)(struct cache_node *rpp); + +struct cache_table { + char const *name; + bool enabled; + unsigned int next_id; + size_t pathlen; + struct cache_node *nodes; /* Hash Table */ + dl_cb download; }; static struct rpki_cache { - struct cache_node *rsync; - struct cache_node *https; -// time_t startup_ts; /* When we started the last validation */ + /* Latest view of the remote rsync modules */ + struct cache_table rsync; + /* Latest view of the remote HTTPS TAs */ + struct cache_table https; + /* Latest view of the remote RRDP cages */ + struct cache_table rrdp; + /* Committed RPPs and TAs (offline fallback hard links) */ + struct cache_table fallback; } cache; +struct cache_cage { + struct cache_node *refresh; + struct cache_node *fallback; +}; + #define CACHE_METAFILE "cache.json" #define TAGNAME_VERSION "fort-version" @@ -58,6 +75,26 @@ static struct rpki_cache { #define TYPEVALUE_RPP "RPP" #define TYPEVALUE_NOTIF "RRDP Notification" +#ifdef UNIT_TESTING +static void __delete_node_cb(struct cache_node const *); +#endif + +static void +delete_node(struct cache_table *tbl, struct cache_node *node) +{ +#ifdef UNIT_TESTING + __delete_node_cb(node); +#endif + + HASH_DEL(tbl->nodes, node); + + free(node->map.url); + free(node->map.path); + if (node->rrdp) + rrdp_state_cleanup(node->rrdp); + free(node); +} + static char * get_cache_filename(char const *name, bool fatal) { @@ -103,6 +140,29 @@ fail: return error; } +static int dl_rsync(struct cache_node *); +static int dl_http(struct cache_node *); +static int dl_rrdp(struct cache_node *); + +static void +init_table(struct cache_table *tbl, char const *name, bool enabled, dl_cb dl) +{ + memset(tbl, 0, sizeof(*tbl)); + tbl->name = name; + tbl->enabled = enabled; + tbl->pathlen = strlen(config_get_local_repository()) + strlen(name) + 6; + tbl->download = dl; +} + +static void +init_tables(void) +{ + init_table(&cache.rsync, "rsync", config_get_rsync_enabled(), dl_rsync); + init_table(&cache.rsync, "https", config_get_http_enabled(), dl_http); + init_table(&cache.rsync, "rrdp", config_get_http_enabled(), dl_rrdp); + init_table(&cache.fallback, "fallback", true, NULL); +} + static void init_cache_metafile(void) { @@ -188,6 +248,7 @@ init_tmp_dir(void) int cache_setup(void) { + init_tables(); init_cache_metafile(); init_tmp_dir(); init_cachedir_tag(); @@ -303,9 +364,6 @@ cache_teardown(void) static void load_tal_json(void) { - cache.rsync = cachent_root_rsync(); - cache.https = cachent_root_https(); - // char *filename; // json_t *root; // json_error_t jerror; @@ -447,355 +505,283 @@ write_tal_json(void) // free(filename); } -/* - * The "rsync module" is the component immediately after the domain. - * - * get_rsync_module(rsync://a.b.c/d/e/f/potato.mft) = d - */ -static struct cache_node * -get_rsync_module(struct cache_node *node) -{ - struct cache_node *gp; /* Grandparent */ - - if (!node || !node->parent || !node->parent->parent) - return NULL; - - for (gp = node->parent->parent; gp->parent != NULL; gp = gp->parent) - node = node->parent; - return node; -} - static int -dl_rrdp(struct cache_node *rpp) +dl_rsync(struct cache_node *module) { int error; - if (!config_get_http_enabled()) { - pr_val_debug("HTTP is disabled."); - return 1; - } - - // XXX needs to add all files to node. - // Probably also update node itself. - // XXX maybe pr_crit() on !mft->parent? - error = rrdp_update(rpp); + error = rsync_download(&module->map); if (error) - pr_val_debug("RRDP RPP: Failed refresh."); + return error; - return error; + module->mtim = time_nonfatal(); /* XXX probably not needed */ + return 0; } static int -dl_rsync(struct cache_node *rpp) +dl_rrdp(struct cache_node *notif) { - struct cache_node *module, *node; - char *tmppath; + time_t mtim; + bool changed; int error; - if (!config_get_rsync_enabled()) { - pr_val_debug("rsync is disabled."); - return 1; - } - - module = get_rsync_module(rpp); - if (module == NULL) - return -EINVAL; // XXX + mtim = time_nonfatal(); - error = cache_tmpfile(&tmppath); + error = rrdp_update(¬if->map, notif->mtim, &changed, ¬if->rrdp); if (error) return error; - error = rsync_download(module->url, tmppath, - (module->flags & CNF_CACHED) ? module->path : NULL); - if (error) { - free(tmppath); - return error; - } - - module->flags |= CNF_RSYNC | CNF_CACHED | CNF_FRESH | CNF_FREE_TMPPATH; - module->mtim = time_nonfatal(); - module->tmppath = tmppath; - - for (node = rpp; node != module; node = node->parent) { - node->flags |= RSYNC_INHERIT; - node->mtim = module->mtim; - } - + if (changed) + notif->mtim = mtim; return 0; } static int -dl_http(struct cache_node *node) +dl_http(struct cache_node *file) { - char *tmppath; time_t mtim; bool changed; int error; - if (!config_get_http_enabled()) { - pr_val_debug("HTTP is disabled."); - return 1; - } - - error = cache_tmpfile(&tmppath); - if (error) - return error; - mtim = time_nonfatal(); - error = http_download(node->url, tmppath, node->mtim, &changed); - if (error) { - free(tmppath); + error = http_download(file->map.url, file->map.path, + file->mtim, &changed); + if (error) return error; - } - node->flags |= CNF_CACHED | CNF_FRESH | CNF_FREE_TMPPATH; if (changed) - node->mtim = mtim; - node->tmppath = tmppath; + file->mtim = mtim; return 0; } -static char * -get_tmppath(struct cache_node *node) +static struct cache_node * +find_node(struct cache_table *tbl, char const *url, size_t urlen) { - struct cache_node *ancestor; - struct path_builder pb; - size_t skiplen; + struct cache_node *node; + HASH_FIND(hh, tbl->nodes, url, urlen, node); + return node; +} - if (node->tmppath != NULL) - return node->tmppath; +static char * +create_path(struct cache_table *tbl) +{ + char *path; + int len; - ancestor = node; do { - ancestor = ancestor->parent; - if (ancestor == NULL) { - /* May warrant pr_crit(), but I'm chickening out. */ - pr_val_err("The download function did not set any tmppaths."); + path = pmalloc(tbl->pathlen); + + len = snprintf(path, tbl->pathlen, "%s/%s/%X", + config_get_local_repository(), tbl->name, tbl->next_id); + if (len < 0) { + pr_val_err("Cannot compute new cache path: Unknown cause."); return NULL; } - } while (ancestor->tmppath == NULL); + if (len < tbl->pathlen) { + tbl->next_id++; + return path; /* Happy path */ + } - skiplen = strlen(ancestor->path); - if (strncmp(ancestor->path, node->path, skiplen) != 0) - pr_crit("???"); // XXX + tbl->pathlen++; + free(path); + } while (true); +} - pb_init(&pb); - if (pb_append(&pb, ancestor->tmppath) != 0) - goto cancel; - if (pb_append(&pb, node->path + skiplen) != 0) - goto cancel; +static struct cache_node * +provide_node(struct cache_table *tbl, char const *url) +{ + size_t urlen; + struct cache_node *node; - node->flags |= CNF_FREE_TMPPATH; - node->tmppath = pb.string; - return pb.string; + urlen = strlen(url); + node = find_node(tbl, url, urlen); + if (node) + return node; + + node = pzalloc(sizeof(struct cache_node)); + node->map.url = pstrdup(url); + node->map.path = create_path(tbl); + if (!node->map.path) { + free(node->map.url); + free(node); + return NULL; + } + HASH_ADD_KEYPTR(hh, tbl->nodes, node->map.url, urlen, node); -cancel: pb_cleanup(&pb); - return NULL; + return node; } /* @uri is either a caRepository or a rpkiNotify */ static struct cache_node * -do_refresh(char const *uri, struct cache_node *root, dl_cb download) +do_refresh(struct cache_table *tbl, char const *uri) { struct cache_node *node; - if (!uri) - return NULL; /* Protocol unavailable; ignore */ + if (!tbl->enabled) + return NULL; pr_val_debug("Trying %s (online)...", uri); - node = cachent_provide(root, uri); - if (!node) { - pr_val_err("Malformed URL: %s", uri); + node = provide_node(tbl, uri); + if (!node) return NULL; - } - if (!(node->flags & CNF_FRESH)) { - node->flags |= CNF_FRESH; - node->dlerr = download(node); + if (!node->fresh) { + node->fresh = true; + node->dlerr = tbl->download(node); } - if (node->dlerr) - pr_val_debug("Refresh failed."); + pr_val_debug(node->dlerr ? "Refresh failed." : "Refresh succeeded."); return node; } -/* @url needs to outlive @map. */ -int -cache_refresh_url(char *url, struct cache_mapping *map) +static struct cache_node * +get_fallback(char const *caRepository) +{ + struct cache_node *node; + + pr_val_debug("Retrieving %s fallback...", caRepository); + node = find_node(&cache.fallback, caRepository, strlen(caRepository)); + pr_val_debug(node ? "Fallback found." : "Fallback unavailable."); + + return node; +} + +/* Do not free nor modify the result. */ +char * +cache_refresh_url(char const *url) { struct cache_node *node = NULL; // XXX mutex // XXX review result signs + // XXX Normalize @url if (url_is_https(url)) - node = do_refresh(url, cache.https, dl_http); + node = do_refresh(&cache.https, url); else if (url_is_rsync(url)) - node = do_refresh(url, cache.rsync, dl_rsync); - if (!node) - return EINVAL; + node = do_refresh(&cache.rsync, url); - // XXX might want to const url and path. - // Alternatively, strdup path so the caller can't corrupt our string. - map->url = url; - map->path = get_tmppath(node); - return (map->path != NULL) ? 0 : EINVAL; + // XXX Maybe strdup path so the caller can't corrupt our string + return node ? node->map.path : NULL; } -/* @url needs to outlive @map. */ -int -cache_fallback_url(char *url, struct cache_mapping *map) +/* Do not free nor modify the result. */ +char * +cache_fallback_url(char const *url) { - struct cache_node *node = NULL; - - if (url_is_https(url)) - node = cachent_provide(cache.https, url); - else if (url_is_rsync(url)) - node = cachent_provide(cache.rsync, url); - if (!node) - return EINVAL; - - map->url = url; - map->path = node->path; - return 0; + struct cache_node *node; + node = find_node(&cache.fallback, url, strlen(url)); + return node ? node->map.path : NULL; } /* * Attempts to refresh the RPP described by @sias, returns the resulting * repository's mapping. + * + * XXX Need to normalize the sias. + * XXX Fallback only if parent is fallback */ -int -cache_refresh_sias(struct sia_uris *sias, struct cache_mapping *map) +struct cache_cage * +cache_refresh_sias(struct sia_uris *sias) { - struct cache_node *hnode; - struct cache_node *rnode; + struct cache_cage *cage; + struct cache_node *node; // XXX Make sure somewhere validates rpkiManifest matches caRepository. // XXX mutex // XXX review result signs // XXX normalize rpkiNotify & caRepository? + // XXX do module if rsync - hnode = do_refresh(sias->rpkiNotify, cache.https, dl_rrdp); - if (hnode && !hnode->dlerr) { - map->url = hnode->url; - map->path = hnode->tmppath; - return 0; - } + cage = pzalloc(sizeof(struct cache_cage)); + cage->fallback = get_fallback(sias->caRepository); - rnode = do_refresh(sias->caRepository, cache.rsync, dl_rsync); - if (rnode && !rnode->dlerr) { - map->url = rnode->url; - map->path = rnode->tmppath; - return 0; + if (sias->rpkiNotify) { + node = do_refresh(&cache.rrdp, sias->rpkiNotify); + if (node && !node->dlerr) { + cage->refresh = node; + return cage; /* RRDP + optional fallback happy path */ + } } - if (hnode && cachent_is_cached(hnode)) { - map->url = hnode->url; - map->path = hnode->path; - return 0; + node = do_refresh(&cache.rsync, sias->caRepository); + if (node && !node->dlerr) { + cage->refresh = node; + return cage; /* rsync + optional fallback happy path */ } - if (hnode && cachent_is_cached(rnode)) { - map->url = hnode->url; - map->path = hnode->path; - return 0; + if (cage->fallback == NULL) { + free(cage); + return NULL; } - return EINVAL; -} - -void -cache_print(void) -{ - cachent_print(cache.rsync); - cachent_print(cache.https); + return cage; /* fallback happy path */ } -static void -prune_rsync(void) +char const * +node2file(struct cache_node *node, char const *url) { - struct cache_node *domain, *tmp1; - struct cache_node *module, *tmp2; - struct cache_node *child, *tmp3; - - HASH_ITER(hh, cache.rsync->children, domain, tmp1) - HASH_ITER(hh, domain->children, module, tmp2) - HASH_ITER(hh, module->children, child, tmp3) { - pr_op_debug("Removing leftover: %s", child->url); - module->flags |= cachent_delete(child); - } + if (node == NULL) + return NULL; + return (node->rrdp) + ? /* RRDP */ rrdp_file(node->rrdp, url) + : /* rsync */ join_paths(node->map.path, url + RPKI_SCHEMA_LEN); // XXX wrong; need to get the module. } -static bool -commit_rpp_delta(struct cache_node *node) +char const * +cage_map_file(struct cache_cage *cage, char const *url) { - struct cache_node *child, *tmp; - int error; + char const *file; - pr_op_debug("Commiting %s", node->url); + file = node2file(cage->refresh, url); + if (!file) + file = node2file(cage->fallback, url); - if (node == cache.rsync || node == cache.https) { - pr_op_debug("Root; nothing to commit."); - goto branch; - } - - if (node->tmppath == NULL) { - if (node->children) { - pr_op_debug("Branch."); - goto branch; - } else { - pr_op_debug("Unchanged leaf; nothing to commit."); - return true; - } - } - - if (node->flags & CNF_VALID) { - pr_op_debug("Validation successful; committing."); - error = file_merge_into(node->tmppath, node->path); - if (error) - printf("rename errno: %d\n", error); // XXX - /* XXX Think more about the implications of this. */ - HASH_ITER(hh, node->children, child, tmp) - cachent_delete(child); - } else { - pr_op_debug("Validation unsuccessful; rollbacking."); - /* XXX just do remove()? */ - file_rm_f(node->tmppath); - } - - free(node->tmppath); - node->tmppath = NULL; - return true; + return file; +} -branch: /* Clean up state flags */ - node->flags &= ~(CNF_CACHED | CNF_FRESH | CNF_TOUCHED | CNF_VALID); - if (node->tmppath) { - free(node->tmppath); - node->tmppath = NULL; - } - return true; +/* Returns true if previously enabled */ +bool +cage_disable_refresh(struct cache_cage *cage) +{ + bool enabled = (cage->refresh != NULL); + cage->refresh = NULL; + return enabled; } -static int -rmf(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) +static void +cachent_print(struct cache_node *node) { - if (remove(fpath) < 0) - pr_op_warn("Can't remove %s: %s", fpath, strerror(errno)); + if (!node) + return; + + printf("\t%s (%s): ", node->map.url, node->map.path); + if (node->fresh) + printf("fresh (errcode %d)", node->dlerr); else - pr_op_debug("Removed %s.", fpath); - return 0; + printf("stale"); + printf("\n"); } static void -cleanup_tmp(void) +table_print(struct cache_table *tbl) { - char *tmpdir = get_cache_filename(CACHE_TMPDIR, true); - if (nftw(tmpdir, rmf, 32, FTW_DEPTH | FTW_PHYS)) - pr_op_warn("Cannot empty the cache's tmp directory: %s", - strerror(errno)); - free(tmpdir); + struct cache_node *node, *tmp; + + printf("%s (%s):", tbl->name, tbl->enabled ? "enabled" : "disabled"); + HASH_ITER(hh, tbl->nodes, node, tmp) + cachent_print(node); +} + +void +cache_print(void) +{ + table_print(&cache.rsync); + table_print(&cache.https); + table_print(&cache.rrdp); + table_print(&cache.fallback); } //static void @@ -866,88 +852,31 @@ cleanup_tmp(void) // return false; //} -static struct cache_node *nftw_root; - static int -nftw_remove_abandoned(const char *path, const struct stat *st, - int typeflag, struct FTW *ftw) +rmf(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) { - char const *lookup; - struct cache_node *pm; /* Perfect Match */ - struct cache_node *msm; /* Most Specific Match */ - struct timespec now; - - // XXX - lookup = path + strlen(config_get_local_repository()); - while (lookup[0] == '/') - lookup++; - pr_op_debug("Removing if abandoned: %s", lookup); - - pm = cachent_find(nftw_root, lookup, &msm); - if (pm == cache.rsync || pm == cache.https) { - pr_op_debug("Root; skipping."); - return 0; - } - if (!msm) { - pr_op_debug("Not matched by the tree; unknown."); - goto unknown; - } - if (!pm && !(msm->flags & CNF_RSYNC)) { - /* XXX what about HTTP? */ - pr_op_debug("RRDP and no perfect match; unknown."); - goto unknown; /* The traversal is depth-first */ - } - - if (S_ISDIR(st->st_mode)) { - /* - * rmdir() fails if the directory is not empty. - * This will happen most of the time. - */ - if (rmdir(path) == 0) { - pr_op_debug("Directory empty; purging node."); - cachent_delete(pm); - } else if (errno == ENOENT) { - pr_op_debug("Directory does not exist; purging node."); - cachent_delete(pm); - } else { - pr_op_debug("Directory exists and has contents; preserving."); - } - - } else if (S_ISREG(st->st_mode)) { - -// if ((msm->flags & CNF_RSYNC) || !pm || (pm->flags & CNF_WITHDRAWN)) - clock_gettime(CLOCK_REALTIME, &now); // XXX - PR_DEBUG_MSG("%ld > %ld", now.tv_sec - st->st_atim.tv_sec, cfg_cache_threshold()); - if (now.tv_sec - st->st_atim.tv_sec > cfg_cache_threshold()) { - pr_op_debug("Too old; abandoned."); - goto abandoned; - } - pr_op_debug("Still young; preserving."); - - } else { - pr_op_debug("Unknown type; abandoned."); - goto abandoned; - } - + if (remove(fpath) < 0) + pr_op_warn("Can't remove %s: %s", fpath, strerror(errno)); + else + pr_op_debug("Removed %s.", fpath); return 0; +} -abandoned: - if (pm) - cachent_delete(pm); -unknown: - if (remove(path) < 0) - PR_DEBUG_MSG("remove(): %s", strerror(errno)); // XXX - return 0; +static void +cleanup_tmp(void) +{ + char *tmpdir = get_cache_filename(CACHE_TMPDIR, true); + if (nftw(tmpdir, rmf, 32, FTW_DEPTH | FTW_PHYS)) + pr_op_warn("Cannot empty the cache's tmp directory: %s", + strerror(errno)); + free(tmpdir); } -/* - * Note: It'll probably be healthy if touched nodes also touch their parents. - * You don't always need to go up all the way to the root. - * But I'm afraid this will hit the mutexes. - */ static void remove_abandoned(void) { + // XXX no need to recurse anymore. + /* char *rootpath; rootpath = join_paths(config_get_local_repository(), "rsync"); @@ -961,24 +890,31 @@ remove_abandoned(void) nftw(rootpath, nftw_remove_abandoned, 32, FTW_DEPTH | FTW_PHYS); // XXX free(rootpath); + */ } -static bool -remove_orphaned(struct cache_node *node) +static void +remove_orphaned(struct cache_table *table, struct cache_node *node) { - if (file_exists(node->path) == ENOENT) { - pr_op_debug("Missing file; deleting node: %s", node->path); - cachent_delete(node); - - if (node == cache.https) - cache.https = NULL; - else if (node == cache.rsync) - cache.rsync = NULL; - - return false; + if (file_exists(node->map.path) == ENOENT) { + pr_op_debug("Missing file; deleting node: %s", node->map.path); + delete_node(table, node); } +} - return true; +static void +cache_foreach(void (*cb)(struct cache_table *, struct cache_node *)) +{ + struct cache_node *node, *tmp; + + HASH_ITER(hh, cache.rsync.nodes, node, tmp) + cb(&cache.rsync, node); + HASH_ITER(hh, cache.https.nodes, node, tmp) + cb(&cache.https, node); + HASH_ITER(hh, cache.rrdp.nodes, node, tmp) + cb(&cache.rrdp, node); + HASH_ITER(hh, cache.fallback.nodes, node, tmp) + cb(&cache.fallback, node); } /* @@ -987,12 +923,7 @@ remove_orphaned(struct cache_node *node) static void cleanup_cache(void) { - pr_op_debug("Ditching redundant rsync nodes."); - prune_rsync(); - - pr_op_debug("Committing successful RPPs."); - cachent_traverse(cache.rsync, commit_rpp_delta); - cachent_traverse(cache.https, commit_rpp_delta); + // XXX Review pr_op_debug("Cleaning up temporal files."); cleanup_tmp(); @@ -1001,8 +932,7 @@ cleanup_cache(void) remove_abandoned(); pr_op_debug("Cleaning up orphaned nodes."); - cachent_traverse(cache.rsync, remove_orphaned); - cachent_traverse(cache.https, remove_orphaned); + cache_foreach(remove_orphaned); } void @@ -1010,8 +940,7 @@ cache_commit(void) { cleanup_cache(); write_tal_json(); - cachent_delete(cache.rsync); - cachent_delete(cache.https); + cache_foreach(delete_node); } void @@ -1026,4 +955,7 @@ sias_cleanup(struct sia_uris *sias) free(sias->caRepository); free(sias->rpkiNotify); free(sias->rpkiManifest); + free(sias->crldp); + free(sias->caIssuers); + free(sias->signedObject); } diff --git a/src/cache.h b/src/cache.h index 4cfbf321..fc4ecb49 100644 --- a/src/cache.h +++ b/src/cache.h @@ -1,6 +1,7 @@ #ifndef SRC_CACHE_LOCAL_CACHE_H_ #define SRC_CACHE_LOCAL_CACHE_H_ +#include #include "types/map.h" int cache_setup(void); /* Init this module */ @@ -9,19 +10,40 @@ void cache_teardown(void); /* Destroy this module */ void cache_prepare(void); /* Prepare cache for new validation cycle */ void cache_commit(void); /* Finish validation cycle */ +/* XXX might wanna rename */ struct sia_uris { char *caRepository; /* RPP cage */ char *rpkiNotify; /* RRDP Notification */ char *rpkiManifest; + + /** + * CRL Distribution Points's fullName. Non-TA certificates only. + * RFC 6487, section 4.8.6. + */ + char *crldp; + /** + * AIA's caIssuers. Non-TA certificates only. + * RFC 6487, section 4.8.7. + */ + char *caIssuers; + /** + * SIA's signedObject. EE certificates only. + * RFC 6487, section 4.8.8.2. + */ + char *signedObject; }; void sias_init(struct sia_uris *); void sias_cleanup(struct sia_uris *); -int cache_refresh_url(char *, struct cache_mapping *); -int cache_fallback_url(char *, struct cache_mapping *); -int cache_refresh_sias(struct sia_uris *, struct cache_mapping *); +char *cache_refresh_url(char const *); +char *cache_fallback_url(char const *); + +struct cache_cage; +struct cache_cage *cache_refresh_sias(struct sia_uris *); +char const *cage_map_file(struct cache_cage *, char const *); +bool cage_disable_refresh(struct cache_cage *); -void cache_print(void); /* Dump cache in stdout. Recursive; tests only */ +void cache_print(void); /* Dump cache in stdout */ #endif /* SRC_CACHE_LOCAL_CACHE_H_ */ diff --git a/src/cachent.c b/src/cachent.c deleted file mode 100644 index 46c593e6..00000000 --- a/src/cachent.c +++ /dev/null @@ -1,303 +0,0 @@ -#include "cachent.h" - -#include "alloc.h" -#include "config.h" -#include "log.h" -#include "types/array.h" -#include "types/path.h" -#include "types/url.h" - -static struct cache_node * -cachent_root(char const *schema, char const *dir) -{ - struct cache_node *root; - - root = pzalloc(sizeof(struct cache_node)); - root->url = (char *)schema; - root->path = join_paths(config_get_local_repository(), dir); - root->name = path_filename(root->path); - root->flags = CNF_FREE_PATH; - - return root; -} - -struct cache_node * -cachent_root_rsync(void) -{ - return cachent_root("rsync://", "rsync"); -} - -struct cache_node * -cachent_root_https(void) -{ - return cachent_root("https://", "https"); -} - -bool -cachent_is_cached(struct cache_node *node) -{ - if (cachent_is_https(node)) - return node->flags & CNF_CACHED; - - for (; node != NULL; node = node->parent) - if (node->flags & CNF_CACHED) - return true; - return false; -} - -/* Preorder. @cb returns whether the children should be traversed. */ -void -cachent_traverse(struct cache_node *root, bool (*cb)(struct cache_node *)) -{ - struct cache_node *iter_start; - struct cache_node *parent, *child; - struct cache_node *tmp; - - if (!root) - return; - - if (!cb(root)) - return; - - parent = root; - iter_start = parent->children; /* aka "first child" */ - if (iter_start == NULL) - return; - -reloop: /* iter_start must not be NULL */ - HASH_ITER(hh, iter_start, child, tmp) { - if (cb(child) && (child->children != NULL)) { - parent = child; - iter_start = parent->children; - goto reloop; - } - } - - do { - if (parent == NULL) - return; - iter_start = parent->hh.next; - parent = parent->parent; - } while (iter_start == NULL); - - goto reloop; -} - -static struct cache_node * -find_child(struct cache_node *parent, char const *name, size_t namelen) -{ - struct cache_node *child; - HASH_FIND(hh, parent->children, name, namelen, child); - return child; -} - -/* - * Returns perfect match or NULL. @msm will point to the Most Specific Match. - * Assumes @path is normalized. - */ -struct cache_node * -cachent_find(struct cache_node *root, char const *path, struct cache_node **msm) -{ - struct tokenizer tkn; - struct cache_node *parent; - struct cache_node *child; - - token_init(&tkn, path); - - if (!token_next(&tkn) || strncmp(root->name, tkn.str, tkn.len) != 0) { - *msm = NULL; - return NULL; - } - - for (parent = child = root; token_next(&tkn); parent = child) { - child = find_child(parent, tkn.str, tkn.len); - if (!child) { - if (parent->flags & CNF_NOTIFICATION) { - child = find_child(parent->rrdp.subtree, - tkn.str, tkn.len); - if (!child) - goto nochild; - } else { -nochild: *msm = parent; - return NULL; - } - } - } - - *msm = parent; - return child; -} - -// XXX path_childn() dup -static char * -inherit_path(char const *parent, char const *name, size_t nlen) -{ - char *child; - size_t clen; - - clen = strlen(parent) + nlen + 2; - child = pmalloc(clen); - if (snprintf(child, clen, "%s/%.*s", parent, (int)nlen, name) >= clen) - pr_crit("aaaaaa"); // XXX - - return child; -} - -/* Get or create parent's child. */ -static struct cache_node * -provide(struct cache_node *parent, char const *url, - char const *name, size_t namelen) -{ - struct cache_node *child; - - child = find_child(parent, name, namelen); - if (child != NULL) - return child; - - child = pzalloc(sizeof(struct cache_node)); - child->url = pstrndup(url, name - url + namelen); - child->path = inherit_path(parent->path, name, namelen); - child->name = child->url + (name - url); - child->flags = CNF_FREE_URL | CNF_FREE_PATH; - if ((parent->flags & RSYNC_INHERIT) == RSYNC_INHERIT) - child->flags |= RSYNC_INHERIT; - if (parent->tmppath && !(parent->flags & CNF_RSYNC)) { - child->tmppath = inherit_path(parent->tmppath, name, namelen); - child->flags |= CNF_FREE_TMPPATH; - } - child->parent = parent; - HASH_ADD_KEYPTR(hh, parent->children, child->name, namelen, child); - - return child; -} - -/* - * Get or create ancestor's descendant. - * - * Suppose @url is "rsync://a.b.c/d/e/f.cer": @ancestor has to be either - * "rsync:", "rsync://a.b.c", "rsync://a.b.c/d", "rsync://a.b.c/d/e" or - * "rsync://a.b.c/d/e/f.cer". - * - * Returns NULL if @ancestor doesn't match @url. - * - * The point of @ancestor is caging. @url will not be allowed to point to - * anything that is not @ancestor or one of its descendants. (ie. dot-dotting is - * allowed, but the end result must not land outside of @ancestor.) - * - * XXX In the end, it seems this is only being used by root ancestors. - * Should probably separate the caging to a simple get. - */ -struct cache_node * -cachent_provide(struct cache_node *ancestor, char const *url) -{ - char *normal; - array_index i; - struct tokenizer tkn; - - normal = url_normalize(url); - if (!normal) - return NULL; - - for (i = 0; ancestor->url[i] != 0; i++) - if (ancestor->url[i] != normal[i]) - goto fail; - if (i != RPKI_SCHEMA_LEN && normal[i] != '/' && normal[i] != '\0') - goto fail; - - token_init(&tkn, normal + i); - while (token_next(&tkn)) - ancestor = provide(ancestor, normal, tkn.str, tkn.len); - free(normal); - return ancestor; - -fail: free(normal); - return NULL; -} - -#ifdef UNIT_TESTING -static void __delete_node_cb(struct cache_node const *); -#endif - -static int -__delete_node(struct cache_node *node) -{ - int valid = node->flags & CNF_VALID; - -#ifdef UNIT_TESTING - __delete_node_cb(node); -#endif - - if (node->parent != NULL) - HASH_DEL(node->parent->children, node); - if (node->flags & CNF_FREE_URL) - free(node->url); - if (node->flags & CNF_FREE_PATH) - free(node->path); - if (node->flags & CNF_FREE_TMPPATH) - free(node->tmppath); - if (node->flags & CNF_NOTIFICATION) - rrdp_notif_cleanup(&node->rrdp); - free(node); - - return valid; -} - -int -cachent_delete(struct cache_node *node) -{ - struct cache_node *parent; - int valid; - - if (!node) - return 0; - - valid = node->flags & CNF_VALID; - - parent = node->parent; - if (parent != NULL) { - HASH_DEL(parent->children, node); - node->parent = NULL; - } - - do { - while (node->children) - node = node->children; - - parent = node->parent; - valid |= __delete_node(node); - node = parent; - } while (node != NULL); - - return valid; -} - -static void -print_node(struct cache_node *node, unsigned int tabs) -{ - unsigned int i; - struct cache_node *child, *tmp; - - for (i = 0; i < tabs; i++) - printf("\t"); - - printf("%s -- ", node->name); - printf("%s", (node->flags & CNF_RSYNC) ? "rsync " : ""); - printf("%s", (node->flags & CNF_CACHED) ? "cached " : ""); - printf("%s", (node->flags & CNF_FRESH) ? "fresh " : ""); - printf("%s", (node->flags & CNF_TOUCHED) ? "touched " : ""); - printf("%s", (node->flags & CNF_VALID) ? "valid " : ""); - printf("%s", (node->flags & CNF_NOTIFICATION) ? "notification " : ""); - printf("%s", (node->flags & CNF_WITHDRAWN) ? "withdrawn " : ""); - printf(" -- %s", node->tmppath); - - printf("\n"); - HASH_ITER(hh, node->children, child, tmp) - print_node(child, tabs + 1); -} - -void -cachent_print(struct cache_node *node) -{ - if (node) - print_node(node, 0); -} diff --git a/src/cachent.h b/src/cachent.h deleted file mode 100644 index 2bed18f2..00000000 --- a/src/cachent.h +++ /dev/null @@ -1,96 +0,0 @@ -#ifndef SRC_CACHE_CACHENT_H_ -#define SRC_CACHE_CACHENT_H_ - -/* CACHE ENTity, CACHE elemENT, CACHE componENT */ - -#include -#include - -#include "rrdp.h" -#include "types/uthash.h" - -/* XXX rename "touched" and "valid" into "preserve"? */ - -// XXX trees now separated; consider removing this flag -#define CNF_RSYNC (1 << 0) -/* - * Do we have a (full) copy in the cache? - * If disabled, we don't know (because an ancestor was recursively rsync'd). - */ -#define CNF_CACHED (1 << 1) -/* - * Was it (allegedly) downloaded during the current cycle? - * "Allegedly" because we might have rsync'd an ancestor. - */ -#define CNF_FRESH (1 << 2) -/* Was it read during the current cycle? */ -#define CNF_TOUCHED (1 << 4) -/* - * Did it validate successfully (at least once) during the current cycle? - * (It's technically possible for two different repositories to map to the same - * cache node. One of them is likely going to fail validation.) - * This only refers to the tmp path; The final path, if it exists, always - * contains valid objects (until expiration). - */ -#define CNF_VALID (1 << 5) -/* Is the node an RRDP Update Notification? */ -#define CNF_NOTIFICATION (1 << 6) -/* Withdrawn by RRDP? */ -#define CNF_WITHDRAWN (1 << 7) - -#define CNF_FREE_URL (1 << 8) -#define CNF_FREE_PATH (1 << 9) -#define CNF_FREE_TMPPATH (1 << 10) - -/* - * Flags for children of downloaded rsync nodes that should be cleaned later. - * (FRESH prevents redownload.) - * XXX useful? - */ -#define RSYNC_INHERIT (CNF_RSYNC | CNF_FRESH) - -struct cache_node { - char *url; /* rsync://a.b.c/d/e (normalized) */ - char *path; /* path/to/cache/rsync/a.b.c/d/e */ - char const *name; /* Points to the last component of @url or @path XXX redundant */ - int flags; /* CNF_* */ - - int dlerr; /* Result code of recent download attempt */ - time_t mtim; /* Last successful download time, or zero */ - - /* - * If download attempted, path to the temporal directory where the - * refresh was dumped. - * (See --compare-dest at rsync(1). RRDP is basically the same.) - * Otherwise NULL. - */ - char *tmppath; /* path/to/cache/tmp/1234 */ - - /* Only if flags & CNF_NOTIFICATION */ - struct cachefile_notification rrdp; - - struct cache_node *parent; /* Tree parent */ - struct cache_node *children; /* Tree children */ - - UT_hash_handle hh; /* Hash table hook */ -}; - -struct cache_node *cachent_root_rsync(void); -struct cache_node *cachent_root_https(void); - -#define cachent_is_rsync(node) ((node)->flags & CNF_RSYNC) -#define cachent_is_https(node) (!cachent_is_rsync(node)) - -bool cachent_is_cached(struct cache_node *); - -void cachent_traverse(struct cache_node *, bool (*cb)(struct cache_node *)); - -struct cache_node *cachent_find(struct cache_node *, char const *, - struct cache_node **); -struct cache_node *cachent_provide(struct cache_node *, char const *); -int cachent_delete(struct cache_node *); - -/* Recursive; tests only. */ -void cachent_print(struct cache_node *); - -#endif /* SRC_CACHE_CACHENT_H_ */ diff --git a/src/cert_stack.c b/src/cert_stack.c deleted file mode 100644 index dbce8280..00000000 --- a/src/cert_stack.c +++ /dev/null @@ -1,404 +0,0 @@ -#include "cert_stack.h" - -#include -#include - -#include "log.h" -#include "thread_var.h" -#include "types/str.h" - -enum defer_node_type { - DNT_SEPARATOR, - DNT_CERT, -}; - -struct defer_node { - enum defer_node_type type; - - /* - * This field is only relevant if @type == DNT_CERT. - * Do not dereference members otherwise. - */ - struct deferred_cert deferred; - - /* Used by certstack. Points to the next stacked certificate. */ - SLIST_ENTRY(defer_node) next; -}; - -SLIST_HEAD(defer_stack, defer_node); - -struct serial_number { - BIGNUM *number; - char *file; /* File where this serial number was found. */ -}; - -STATIC_ARRAY_LIST(serial_numbers, struct serial_number) - -/* Cached certificate data */ -struct metadata_node { - struct cache_mapping map; - struct resources *resources; - /* - * Serial numbers of the children. - * This is an unsorted array list for two reasons: Certificates usually - * don't have many children, and I'm running out of time. - */ - struct serial_numbers serials; - - /* Used by certstack. Points to the next stacked certificate. */ - SLIST_ENTRY(metadata_node) next; -}; - -/* Certificates that need to be remembered during a validation cycle. */ -struct cert_stack { - /* - * Defer stack; certificates whose validation has been postponed. - * (Postponing certificates avoids us recursion and stack overflow.) - */ - struct defer_stack defers; - - /* - * X509 stack. Ancestor certificates of the current certificate. - * Formatted for immediate libcrypto consumption. - */ - STACK_OF(X509) *x509s; - - /* - * Metadata for each X509. Stuff that doesn't fit in libcrypto's struct. - * - * (This is a SLIST and not a STACK_OF because the LibreSSL STACK_OF - * is seemingly private.) - */ - SLIST_HEAD(, metadata_node) metas; -}; - -int -certstack_create(struct cert_stack **result) -{ - struct cert_stack *stack; - - stack = pmalloc(sizeof(struct cert_stack)); - - stack->x509s = sk_X509_new_null(); - if (stack->x509s == NULL) { - free(stack); - return val_crypto_err("sk_X509_new_null() returned NULL"); - } - - SLIST_INIT(&stack->defers); - SLIST_INIT(&stack->metas); - - *result = stack; - return 0; -} - -static void -defer_pop(struct cert_stack *stack) -{ - struct defer_node *defer; - - defer = SLIST_FIRST(&stack->defers); - if (defer == NULL) - pr_crit("Attempted to pop empty defer stack"); - - SLIST_REMOVE_HEAD(&stack->defers, next); - if (defer->type == DNT_CERT) { - map_cleanup(&defer->deferred.map); - rpp_refput(defer->deferred.pp); - } - free(defer); -} - -static void -serial_cleanup(struct serial_number *serial) -{ - BN_free(serial->number); - free(serial->file); -} - -static void -meta_pop(struct cert_stack *stack) -{ - struct metadata_node *meta; - - meta = SLIST_FIRST(&stack->metas); - if (meta == NULL) - pr_crit("Attempted to pop empty metadata stack"); - - SLIST_REMOVE_HEAD(&stack->metas, next); - map_cleanup(&meta->map); - resources_destroy(meta->resources); - serial_numbers_cleanup(&meta->serials, serial_cleanup); - free(meta); -} - -void -certstack_destroy(struct cert_stack *stack) -{ - int n; - - for (n = 0; !SLIST_EMPTY(&stack->defers); n++) - defer_pop(stack); - pr_val_debug("Deleted %d deferred certificates.", n); - - n = sk_X509_num(stack->x509s); - sk_X509_pop_free(stack->x509s, X509_free); - pr_val_debug("Deleted %d stacked x509s.", n); - - for (n = 0; !SLIST_EMPTY(&stack->metas); n++) - meta_pop(stack); - pr_val_debug("Deleted %u metadatas.", n); - - free(stack); -} - -void -deferstack_push(struct cert_stack *stack, struct cache_mapping *map, - struct rpp *pp) -{ - struct defer_node *node; - - node = pmalloc(sizeof(struct defer_node)); - - node->type = DNT_CERT; - map_copy(&node->deferred.map, map); - node->deferred.pp = pp; - rpp_refget(pp); - SLIST_INSERT_HEAD(&stack->defers, node, next); -} - -static void -x509stack_pop(struct cert_stack *stack) -{ - X509 *cert; - - cert = sk_X509_pop(stack->x509s); - if (cert == NULL) - pr_crit("Attempted to pop empty X509 stack"); - X509_free(cert); - - meta_pop(stack); -} - -/* Contract: Returns either 0 or -ENOENT. No other outcomes. */ -int -deferstack_pop(struct cert_stack *stack, struct deferred_cert *result) -{ - struct defer_node *node; - -again: node = SLIST_FIRST(&stack->defers); - if (node == NULL) - return -ENOENT; - - if (node->type == DNT_SEPARATOR) { - x509stack_pop(stack); - defer_pop(stack); - goto again; - } - - *result = node->deferred; - - SLIST_REMOVE_HEAD(&stack->defers, next); - free(node); - return 0; -} - -static int -init_resources(X509 *x509, enum rpki_policy policy, enum cert_type type, - struct resources **_result) -{ - struct resources *result; - int error; - - result = resources_create(policy, false); - - error = certificate_get_resources(x509, result, type); - if (error) - goto fail; - - /* - * rfc8630#section-2.3 - * "The INR extension(s) of this TA MUST contain a non-empty set of - * number resources." - * The "It MUST NOT use the "inherit" form of the INR extension(s)" - * part is already handled in certificate_get_resources(). - */ - if (type == CERTYPE_TA && resources_empty(result)) { - error = pr_val_err("Trust Anchor certificate does not define any number resources."); - goto fail; - } - - *_result = result; - return 0; - -fail: - resources_destroy(result); - return error; -} - -static struct defer_node * -create_separator(void) -{ - struct defer_node *result; - - result = pmalloc(sizeof(struct defer_node)); - result->type = DNT_SEPARATOR; - - return result; -} - -/* Steals ownership of @x509 on success. */ -int -x509stack_push(struct cert_stack *stack, struct cache_mapping const *map, - X509 *x509, enum rpki_policy policy, enum cert_type type) -{ - struct metadata_node *meta; - struct defer_node *defer_separator; - int ok; - int error; - - meta = pmalloc(sizeof(struct metadata_node)); - - map_copy(&meta->map, map); - serial_numbers_init(&meta->serials); - - error = init_resources(x509, policy, type, &meta->resources); - if (error) - goto cleanup_serial; - - defer_separator = create_separator(); - - ok = sk_X509_push(stack->x509s, x509); - if (ok <= 0) { - error = val_crypto_err( - "Could not add certificate to trusted stack: %d", ok); - goto destroy_separator; - } - - SLIST_INSERT_HEAD(&stack->defers, defer_separator, next); - SLIST_INSERT_HEAD(&stack->metas, meta, next); - - return 0; - -destroy_separator: - free(defer_separator); - resources_destroy(meta->resources); -cleanup_serial: - serial_numbers_cleanup(&meta->serials, serial_cleanup); - free(meta->map.url); - free(meta->map.path); - free(meta); - return error; -} - -/* - * This one is intended to revert a recent x509 push. - * Reverts that particular push. - * - * (x509 stack elements are otherwise indirectly popped through - * deferstack_pop().) - */ -void -x509stack_cancel(struct cert_stack *stack) -{ - x509stack_pop(stack); - defer_pop(stack); -} - -X509 * -x509stack_peek(struct cert_stack *stack) -{ - return sk_X509_value(stack->x509s, sk_X509_num(stack->x509s) - 1); -} - -struct resources * -x509stack_peek_resources(struct cert_stack *stack) -{ - struct metadata_node *meta = SLIST_FIRST(&stack->metas); - return (meta != NULL) ? meta->resources : NULL; -} - -static char * -get_current_file_name(void) -{ - char const *file_name; - - file_name = fnstack_peek(); - if (file_name == NULL) - pr_crit("The file name stack is empty."); - - return pstrdup(file_name); -} - -/* - * Intended to validate serial number uniqueness. - * "Stores" the serial number in the current relevant certificate metadata, - * and complains if there's a collision. That's all. - * - * This function will steal ownership of @number on success. - */ -void -x509stack_store_serial(struct cert_stack *stack, BIGNUM *number) -{ - struct metadata_node *meta; - struct serial_number *cursor; - struct serial_number duplicate; - char *string; - - /* Remember to free @number if you return 0 but don't store it. */ - - meta = SLIST_FIRST(&stack->metas); - if (meta == NULL) { - BN_free(number); - return; /* The TA lacks siblings, so serial is unique. */ - } - - /* - * Note: This is is reported as a warning, even though duplicate serial - * numbers are clearly a violation of the RFC and common sense. - * - * But it cannot be simply upgraded into an error because we are - * realizing the problem too late; our traversal is depth-first, so we - * already approved the other bogus certificate and its children. - * (I don't think changing to a breath-first traversal would be a good - * idea; the RAM usage would skyrocket because, since we need the entire - * certificate path to the root to validate any certificate, we would - * end up having the entire tree loaded in memory by the time we're done - * traversing.) - * - * So instead of arbitrarily approving one certificate but not the - * other, we will accept both but report a warning. - * - * Also: It's pretty odd; a significant amount of certificates seem to - * be breaking this rule. Maybe we're the only ones catching it? - * - * TODO I haven't seen this warning in a while. Review. - */ - ARRAYLIST_FOREACH(&meta->serials, cursor) { - if (BN_cmp(cursor->number, number) == 0) { - BN2string(number, &string); - pr_val_warn("Serial number '%s' is not unique. (Also found in '%s'.)", - string, cursor->file); - BN_free(number); - free(string); - return; - } - } - - duplicate.number = number; - duplicate.file = get_current_file_name(); - - serial_numbers_add(&meta->serials, &duplicate); -} - -STACK_OF(X509) * -certstack_get_x509s(struct cert_stack *stack) -{ - return stack->x509s; -} - -int -certstack_get_x509_num(struct cert_stack *stack) -{ - return sk_X509_num(stack->x509s); -} diff --git a/src/cert_stack.h b/src/cert_stack.h deleted file mode 100644 index 411bac3a..00000000 --- a/src/cert_stack.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef SRC_CERT_STACK_H_ -#define SRC_CERT_STACK_H_ - -#include - -#include "object/certificate.h" -#include "types/name.h" - -struct cert_stack; - -struct deferred_cert { - struct cache_mapping map; - struct rpp *pp; -}; - -int certstack_create(struct cert_stack **); -void certstack_destroy(struct cert_stack *); - -void deferstack_push(struct cert_stack *, struct cache_mapping *, struct rpp *); -int deferstack_pop(struct cert_stack *, struct deferred_cert *cert); - -int x509stack_push(struct cert_stack *, struct cache_mapping const *, - X509 *, enum rpki_policy, enum cert_type); -void x509stack_cancel(struct cert_stack *); -X509 *x509stack_peek(struct cert_stack *); -struct resources *x509stack_peek_resources(struct cert_stack *); -void x509stack_store_serial(struct cert_stack *, BIGNUM *); - -STACK_OF(X509) *certstack_get_x509s(struct cert_stack *); -int certstack_get_x509_num(struct cert_stack *); - -#endif /* SRC_CERT_STACK_H_ */ diff --git a/src/certificate_refs.c b/src/certificate_refs.c index a5b2d642..0f3403bf 100644 --- a/src/certificate_refs.c +++ b/src/certificate_refs.c @@ -6,97 +6,53 @@ #include "log.h" -void -refs_init(struct certificate_refs *refs) -{ - memset(refs, 0, sizeof(struct certificate_refs)); -} - -void -refs_cleanup(struct certificate_refs *refs) -{ - free(refs->crldp); - if (refs->caIssuers != NULL) - free(refs->caIssuers); - if (refs->signedObject != NULL) - free(refs->signedObject); -} - -static int -validate_cdp(struct certificate_refs *refs, struct rpp const *pp) +int +validate_cdp(struct sia_uris *sias, char const *crl_url) { - char const *crl_url; - - if (refs->crldp == NULL) + if (sias->crldp == NULL) pr_crit("Certificate's CRL Distribution Point was not recorded."); - crl_url = rpp_get_crl_url(pp); if (crl_url == NULL) pr_crit("Manifest's CRL was not recorded."); - if (strcmp(refs->crldp, crl_url) != 0) { + if (strcmp(sias->crldp, crl_url) != 0) { return pr_val_err("Certificate's CRL Distribution Point ('%s') does not match manifest's CRL ('%s').", - refs->crldp, crl_url); + sias->crldp, crl_url); } return 0; } static int -validate_signedObject(struct certificate_refs *refs, char const *url) +validate_signedObject(struct sia_uris *sias, char const *url) { - if (refs->signedObject == NULL) + if (sias->signedObject == NULL) pr_crit("Certificate's signedObject was not recorded."); /* XXX the left one is no longer normalized */ - if (strcmp(refs->signedObject, url) != 0) { + if (strcmp(sias->signedObject, url) != 0) { return pr_val_err("Certificate's signedObject ('%s') does not match the URI of its own signed object (%s).", - refs->signedObject, url); + sias->signedObject, url); } return 0; } -/** - * Ensures the @refs URIs match the parent Manifest's URIs. Assumes @refs came - * from a (non-TA) CA certificate. - * - * @refs: References you want validated. - * @pp: Repository Publication Point, as described by the parent Manifest. - */ -int -refs_validate_ca(struct certificate_refs *refs, struct rpp const *pp) -{ - int error; - - error = validate_cdp(refs, pp); - if (error) - return error; - - if (refs->signedObject != NULL) - pr_crit("CA summary has a signedObject ('%s').", - refs->signedObject); - - return 0; -} - /** * Ensures the @refs URIs match the Manifest URIs. Assumes @refs came from an * EE certificate. * * @refs: References you want validated. - * @pp: Repository Publication Point, as described by the Manifest. * @url: URL of the signed object that contains the EE certificate. */ int -refs_validate_ee(struct certificate_refs *refs, struct rpp const *pp, - char const *url) +refs_validate_ee(struct sia_uris *sias, char const *crl_url, char const *url) { int error; - error = validate_cdp(refs, pp); + error = validate_cdp(sias, crl_url); if (error) return error; - return validate_signedObject(refs, url); + return validate_signedObject(sias, url); } diff --git a/src/certificate_refs.h b/src/certificate_refs.h index 841ba94b..2df0890b 100644 --- a/src/certificate_refs.h +++ b/src/certificate_refs.h @@ -1,45 +1,11 @@ #ifndef SRC_CERTIFICATE_REFS_H_ #define SRC_CERTIFICATE_REFS_H_ -#include "rpp.h" +/* XXX delete this */ -/** - * Some of the URLs defined in Access Descriptions of a certificate's - * extensions. - * - * It's intended to address some awkward RFC requirements: - * RFC 6487 defines that these "MUST reference" certain files. I think the best - * way to validate this is to check that they equal the respective URLs from the - * manifest (because these will at some point be validated as part of the - * traversal anyway). Problem is, these URLs are not guaranteed to be parsed by - * the time the extension validation kicks in. So we store them in this - * structure and handle them later. - * - * It makes a mess out of the code, and I'm not even sure that validating them - * is our responsibility, but there you go. - */ -struct certificate_refs { - /** - * CRL Distribution Points's fullName. Non-TA certificates only. - * RFC 6487, section 4.8.6. - */ - char *crldp; - /** - * AIA's caIssuers. Non-TA certificates only. - * RFC 6487, section 4.8.7. - */ - char *caIssuers; - /** - * SIA's signedObject. EE certificates only. - * RFC 6487, section 4.8.8.2. - */ - char *signedObject; -}; +#include "cache.h" -void refs_init(struct certificate_refs *); -void refs_cleanup(struct certificate_refs *); -int refs_validate_ca(struct certificate_refs *, struct rpp const *); -int refs_validate_ee(struct certificate_refs *, struct rpp const *, - char const *); +int validate_cdp(struct sia_uris *, char const *); +int refs_validate_ee(struct sia_uris *, char const *, char const *); #endif /* SRC_CERTIFICATE_REFS_H_ */ diff --git a/src/common.c b/src/common.c index f986673e..6ef81098 100644 --- a/src/common.c +++ b/src/common.c @@ -321,7 +321,7 @@ end: free(path); * If parent's parent is now empty, delete parent's parent. * And so on. * - * FIXME this should be done by the cache cleaner instead. + * XXX this should be done by the cache cleaner instead. */ int delete_dir_recursive_bottom_up(char const *path) diff --git a/src/extension.c b/src/extension.c index 9b167215..6b3113be 100644 --- a/src/extension.c +++ b/src/extension.c @@ -997,24 +997,27 @@ int handle_aki(void *ext, void *arg) { AUTHORITY_KEYID *aki = ext; - X509 *parent; +// X509 *parent; if (aki->keyid == NULL) { - return pr_val_err("%s extension lacks a keyIdentifier.", + return pr_val_err("The %s lacks a keyIdentifier.", ext_aki()->name); } if (aki->issuer != NULL) { - return pr_val_err("%s extension contains an authorityCertIssuer.", + return pr_val_err("The %s contains an authorityCertIssuer.", ext_aki()->name); } if (aki->serial != NULL) { - return pr_val_err("%s extension contains an authorityCertSerialNumber.", + return pr_val_err("The %s contains an authorityCertSerialNumber.", ext_aki()->name); } + /* XXX parent = x509stack_peek(validation_certstack(state_retrieve())); if (parent == NULL) return pr_val_err("Certificate has no parent."); return validate_public_key_hash(parent, aki->keyid, "AKI"); + */ + return 0; } diff --git a/src/incidence.c b/src/incidence.c index 2641fcc3..b9a35b2c 100644 --- a/src/incidence.c +++ b/src/incidence.c @@ -45,12 +45,6 @@ static struct incidence incidences[__INID_MAX] = { "The current time is after the nextUpdate field at the manifest", INAC_ERROR, }, - { - INID_CRL_STALE, - "incid-crl-stale", - "The current time is after the nextUpdate field at the CRL", - INAC_ERROR, - }, }; static int diff --git a/src/incidence.h b/src/incidence.h index 93e97998..f0170aea 100644 --- a/src/incidence.h +++ b/src/incidence.h @@ -13,7 +13,7 @@ enum incidence_id { INID_MFT_FILE_NOT_FOUND, // XXX deprecate and no-op INID_MFT_FILE_HASH_NOT_MATCH, // XXX deprecate and no-op INID_MFT_STALE, - INID_CRL_STALE, + // XXX Document elimination of INID_CRL_STALE __INID_MAX, }; diff --git a/src/object/certificate.c b/src/object/certificate.c index 3968af30..e4bf5923 100644 --- a/src/object/certificate.c +++ b/src/object/certificate.c @@ -21,8 +21,11 @@ #include "libcrypto_util.h" #include "log.h" #include "nid.h" +#include "object/ghostbusters.h" #include "object/manifest.h" +#include "object/roa.h" #include "thread_var.h" +#include "types/name.h" #include "types/path.h" #include "types/str.h" #include "types/url.h" @@ -34,6 +37,9 @@ */ typedef AUTHORITY_INFO_ACCESS SIGNATURE_INFO_ACCESS; +/* Certificates that need to be postponed during a validation cycle. */ +SLIST_HEAD(cert_stack, rpki_certificate); + struct ski_arguments { X509 *cert; OCTET_STRING_t *sid; @@ -89,39 +95,6 @@ static const struct ad_metadata RPKI_MANIFEST = { .required = true, }; -static void -debug_serial_number(BIGNUM *number) -{ - char *number_str; - - number_str = BN_bn2dec(number); - if (number_str == NULL) { - val_crypto_err("Could not convert BN to string"); - return; - } - - pr_val_debug("serial Number: %s", number_str); - free(number_str); -} - -static int -validate_serial_number(X509 *cert) -{ - struct validation *state; - BIGNUM *number; - - number = ASN1_INTEGER_to_BN(X509_get0_serialNumber(cert), NULL); - if (number == NULL) - return val_crypto_err("Could not parse certificate serial number"); - - if (log_val_enabled(LOG_DEBUG)) - debug_serial_number(number); - - state = state_retrieve(); - x509stack_store_serial(validation_certstack(state), number); - return 0; -} - static int validate_signature_algorithm(X509 *cert) { @@ -133,25 +106,23 @@ validate_signature_algorithm(X509 *cert) } static int -validate_issuer(X509 *cert, bool is_ta) +validate_issuer(struct rpki_certificate *cert) { X509_NAME *issuer; struct rfc5280_name *name; int error; - issuer = X509_get_issuer_name(cert); - - if (!is_ta) - return validate_issuer_name("Certificate", issuer); + issuer = X509_get_issuer_name(cert->x509); - /* TODO wait. Shouldn't we check subject == issuer? */ + if (cert->type != CERTYPE_TA) + return validate_issuer_name(issuer, cert->parent->x509); error = x509_name_decode(issuer, "issuer", &name); if (error) return error; pr_val_debug("Issuer: %s", x509_name_commonName(name)); - x509_name_put(name); + return 0; } @@ -160,8 +131,7 @@ validate_issuer(X509 *cert, bool is_ta) * @diff_pk_cb when the public key is different; return 0 if both are equal. */ static int -spki_cmp(X509_PUBKEY *tal_spki, X509_PUBKEY *cert_spki, - int (*diff_alg_cb)(void), int (*diff_pk_cb)(void)) +spki_cmp(X509_PUBKEY *tal_spki, X509_PUBKEY *cert_spki) { ASN1_OBJECT *tal_alg; ASN1_OBJECT *cert_alg; @@ -181,13 +151,18 @@ spki_cmp(X509_PUBKEY *tal_spki, X509_PUBKEY *cert_spki, return val_crypto_err("X509_PUBKEY_get0_param() 2 returned %d", ok); if (OBJ_cmp(tal_alg, cert_alg) != 0) - return diff_alg_cb(); + goto root_different_alg_err; if (tal_spk_len != cert_spk_len) - return diff_pk_cb(); + goto root_different_pk_err; if (memcmp(tal_spk, cert_spk, cert_spk_len) != 0) - return diff_pk_cb(); + goto root_different_pk_err; return 0; + +root_different_alg_err: + return pr_val_err("TAL's public key algorithm is different than the root certificate's public key algorithm."); +root_different_pk_err: + return pr_val_err("TAL's public key is different than the root certificate's public key."); } /* @@ -239,28 +214,14 @@ fail: fnstack_pop(); return NULL; } -static int -root_different_alg_err(void) -{ - return pr_val_err("TAL's public key algorithm is different than the root certificate's public key algorithm."); -} - -static int -root_different_pk_err(void) -{ - return pr_val_err("TAL's public key is different than the root certificate's public key."); -} - static int validate_spki(X509_PUBKEY *cert_spki) { - struct validation *state; struct tal *tal; X509_PUBKEY *tal_spki; + int error; - state = state_retrieve(); - - tal = validation_tal(state); + tal = validation_tal(state_retrieve()); if (tal == NULL) pr_crit("Validation state has no TAL."); @@ -286,16 +247,10 @@ validate_spki(X509_PUBKEY *cert_spki) if (tal_spki == NULL) return -EINVAL; - if (spki_cmp(tal_spki, cert_spki, root_different_alg_err, - root_different_pk_err) != 0) { - X509_PUBKEY_free(tal_spki); - validation_pubkey_invalid(state); - return -EINVAL; - } + error = spki_cmp(tal_spki, cert_spki); X509_PUBKEY_free(tal_spki); - validation_pubkey_valid(state); - return 0; + return error; } /* @@ -491,7 +446,7 @@ validate_public_key(X509 *cert, enum cert_type type) } int -certificate_validate_rfc6487(X509 *cert, enum cert_type type) +certificate_validate_rfc6487(struct rpki_certificate *cert) { int error; @@ -504,21 +459,19 @@ certificate_validate_rfc6487(X509 *cert, enum cert_type type) */ /* rfc6487#section-4.1 */ - if (X509_get_version(cert) != 2) + if (X509_get_version(cert->x509) != 2) return pr_val_err("Certificate version is not v3."); /* rfc6487#section-4.2 */ - error = validate_serial_number(cert); - if (error) - return error; + /* */ /* rfc6487#section-4.3 */ - error = validate_signature_algorithm(cert); + error = validate_signature_algorithm(cert->x509); if (error) return error; /* rfc6487#section-4.4 */ - error = validate_issuer(cert, type == CERTYPE_TA); + error = validate_issuer(cert); if (error) return error; @@ -528,7 +481,7 @@ certificate_validate_rfc6487(X509 *cert, enum cert_type type) * "An issuer SHOULD use a different subject name if the subject's * key pair has changed" (it's a SHOULD, so [for now] avoid validation) */ - error = validate_subject(cert); + error = validate_subject(cert->x509); if (error) return error; @@ -537,7 +490,7 @@ certificate_validate_rfc6487(X509 *cert, enum cert_type type) /* rfc6487#section-4.7 */ /* Fragment of rfc8630#section-2.3 */ - error = validate_public_key(cert, type); + error = validate_public_key(cert->x509, cert->type); if (error) return error; @@ -794,75 +747,93 @@ end: return error; } -static int -certificate_load(struct cache_mapping const *map, X509 **result) +static X509 * +certificate_load(char const *path) { X509 *cert = NULL; BIO *bio; - int error; - - *result = NULL; bio = BIO_new(BIO_s_file()); - if (bio == NULL) - return val_crypto_err("BIO_new(BIO_s_file()) returned NULL"); - if (BIO_read_filename(bio, map->path) <= 0) { - error = val_crypto_err("Error reading certificate"); + if (bio == NULL) { + val_crypto_err("BIO_new(BIO_s_file()) returned NULL"); + return NULL; + } + if (BIO_read_filename(bio, path) <= 0) { + val_crypto_err("Error reading certificate"); goto end; } cert = d2i_X509_bio(bio, NULL); if (cert == NULL) { - error = val_crypto_err("Error parsing certificate"); + val_crypto_err("Error parsing certificate"); goto end; } - *result = cert; - error = 0; -end: - BIO_free(bio); - return error; +end: BIO_free(bio); + return cert; } -/* - * Allocates a clone of @original_crl and pushes it to @crls. - * - * Don't forget to pop from @crls and release the popped CRL. - */ -static int -update_crl_time(STACK_OF(X509_CRL) *crls, X509_CRL *original_crl) +static void +certificate_stack_push(struct cert_stack *stack, struct cache_mapping *map, + struct rpki_certificate *parent) { - ASN1_TIME *tm; - X509_CRL *clone; + struct rpki_certificate *cert; - /* - * Yes, this is an awful hack. The other options were: - * - Use X509_V_FLAG_NO_CHECK_TIME parameter, but this avoids also the - * time check for the certificate. - * - Avoid whole CRL check, but since we don't implement the - * certificate chain validation, we can't assure that the CRL has - * only the nextUpdate field wrong (maybe there are other invalid - * things). - */ - tm = ASN1_TIME_adj(NULL, time_fatal(), 0, 60); - if (tm == NULL) - return val_crypto_err("ASN1_TIME_adj() returned NULL."); - - clone = X509_CRL_dup(original_crl); - if (clone == NULL) { - ASN1_STRING_free(tm); - return val_crypto_err("X509_CRL_dup() returned NULL."); - } + cert = pzalloc(sizeof(*cert)); + cert->refcount++; - X509_CRL_set1_nextUpdate(clone, tm); - ASN1_STRING_free(tm); + map_copy(&cert->map, map); - if (sk_X509_CRL_push(crls, clone) <= 0) { - X509_CRL_free(clone); - return val_crypto_err("Error calling sk_X509_CRL_push()"); - } + cert->parent = parent; + parent->refcount++; - return 0; + cert->rpp.ancestors = X509_chain_up_ref(parent->rpp.ancestors); + if (!cert->rpp.ancestors) + goto fail; + if (sk_X509_push(cert->rpp.ancestors, parent->x509) <= 0) + goto fail; + if (!X509_up_ref(parent->x509)) + goto fail; + + SLIST_INSERT_HEAD(stack, cert, lh); + return; + +fail: rpki_certificate_free(cert); +} + +void +rpki_certificate_init_ee(struct rpki_certificate *ee, + struct rpki_certificate *parent, bool force_inherit) +{ + memset(ee, 0, sizeof(*ee)); + ee->type = CERTYPE_EE; + ee->policy = RPKI_POLICY_RFC6484; + ee->resources = resources_create(RPKI_POLICY_RFC6484, force_inherit); + ee->parent = parent; + ee->refcount = 1; +} + +void +rpki_certificate_cleanup(struct rpki_certificate *cert) +{ + map_cleanup(&cert->map); + if (cert->x509 != NULL) + X509_free(cert->x509); + resources_destroy(cert->resources); + sias_cleanup(&cert->sias); + // XXX Recursive. Try refcounting the resources. + rpki_certificate_free(cert->parent); + rpp_cleanup(&cert->rpp); +} + +void +rpki_certificate_free(struct rpki_certificate *cert) +{ + cert->refcount--; + if (cert->refcount == 0) { + rpki_certificate_cleanup(cert); + free(cert); + } } static void @@ -879,112 +850,34 @@ pr_debug_x509_dates(X509 *x509) free(na); } -/* - * Retry certificate validation without CRL time validation. - */ -static int -verify_cert_crl_stale(struct validation *state, X509 *cert, - STACK_OF(X509_CRL) *crls) -{ - X509_STORE_CTX *ctx; - X509_CRL *original_crl, *clone; - int error; - int ok; - - ctx = X509_STORE_CTX_new(); - if (ctx == NULL) { - val_crypto_err("X509_STORE_CTX_new() returned NULL"); - return -EINVAL; - } - - /* Returns 0 or 1 , all callers test ! only. */ - ok = X509_STORE_CTX_init(ctx, validation_store(state), cert, NULL); - if (!ok) { - error = val_crypto_err("X509_STORE_CTX_init() returned %d", ok); - goto release_ctx; - } - - original_crl = sk_X509_CRL_pop(crls); - error = update_crl_time(crls, original_crl); - if (error) - goto push_original; - - X509_STORE_CTX_trusted_stack(ctx, - certstack_get_x509s(validation_certstack(state))); - X509_STORE_CTX_set0_crls(ctx, crls); - - ok = X509_verify_cert(ctx); - if (ok > 0) { - error = 0; /* Happy path */ - goto pop_clone; - } - - error = X509_STORE_CTX_get_error(ctx); - if (error) - error = pr_val_err("Certificate validation failed: %s", - X509_verify_cert_error_string(error)); - else - error = val_crypto_err("Certificate validation failed: %d", ok); - - if (error && log_val_enabled(LOG_DEBUG)) - pr_debug_x509_dates(cert); - -pop_clone: - clone = sk_X509_CRL_pop(crls); - if (clone == NULL) - error = pr_val_err("Error calling sk_X509_CRL_pop()"); - else - X509_CRL_free(clone); -push_original: - /* Try to return to the "regular" CRL chain */ - ok = sk_X509_CRL_push(crls, original_crl); - if (ok <= 0) - error = val_crypto_err("Could not return CRL to a CRL stack"); -release_ctx: - X509_STORE_CTX_free(ctx); - return error; - -} - -static int -complain_crl_stale(STACK_OF(X509_CRL) *crls) +static void +complain_crl_stale(X509_CRL *crl) { - X509_CRL *crl; char *lu; char *nu; - int ret; - - if (sk_X509_CRL_num(crls) < 1) - pr_crit("Empty CRL stack despite validations."); - crl = sk_X509_CRL_value(crls, 0); - if (crl == NULL) - pr_crit("Unable to pop CRL from nonempty stack."); lu = asn1time2str(X509_CRL_get0_lastUpdate(crl)); nu = asn1time2str(X509_CRL_get0_nextUpdate(crl)); - ret = incidence(INID_CRL_STALE, - "CRL is stale/expired. (lastUpdate:%s, nextUpdate:%s)", lu, nu); + pr_val_err("CRL is stale/expired. (lastUpdate:%s, nextUpdate:%s)", + lu, nu); free(lu); free(nu); - return ret; } int -certificate_validate_chain(X509 *cert, STACK_OF(X509_CRL) *crls) +certificate_validate_chain(struct rpki_certificate *cert) { /* Reference: openbsd/src/usr.bin/openssl/verify.c */ - struct validation *state; X509_STORE_CTX *ctx; + STACK_OF(X509_CRL) *crls; int ok; int error; - if (crls == NULL) - return 0; /* Certificate is TA; no chain validation needed. */ - - state = state_retrieve(); + if (cert->type == CERTYPE_TA) + return 0; /* No chain to validate. */ ctx = X509_STORE_CTX_new(); if (ctx == NULL) { @@ -993,15 +886,28 @@ certificate_validate_chain(X509 *cert, STACK_OF(X509_CRL) *crls) } /* Returns 0 or 1 , all callers test ! only. */ - ok = X509_STORE_CTX_init(ctx, validation_store(state), cert, NULL); + ok = X509_STORE_CTX_init(ctx, validation_store(state_retrieve()), + cert->x509, NULL); if (!ok) { val_crypto_err("X509_STORE_CTX_init() returned %d", ok); goto abort; } - X509_STORE_CTX_trusted_stack(ctx, - certstack_get_x509s(validation_certstack(state))); - X509_STORE_CTX_set0_crls(ctx, crls); + X509_STORE_CTX_trusted_stack(ctx, cert->rpp.ancestors); + + crls = sk_X509_CRL_new_null(); + if (!crls) + enomem_panic(); + if (sk_X509_CRL_push(crls, cert->rpp.crl.obj) != 1) { + // XXX + } + // XXX These CRLs will only be used if CRL verification is enabled in + // the associated X509_VERIFY_PARAM structure. + X509_STORE_CTX_set0_crls(ctx, crls); // XXX needs free + // sk_X509_CRL_pop_free(cert->crl.stack, X509_CRL_free); + + if (log_val_enabled(LOG_DEBUG)) + pr_debug_x509_dates(cert->x509); /* * HERE'S THE MEAT OF LIBCRYPTO'S VALIDATION. @@ -1019,28 +925,18 @@ certificate_validate_chain(X509 *cert, STACK_OF(X509_CRL) *crls) * error code is stored in the context. */ error = X509_STORE_CTX_get_error(ctx); - if (error) { - if (error != X509_V_ERR_CRL_HAS_EXPIRED) { - pr_val_err("Certificate validation failed: %s", - X509_verify_cert_error_string(error)); - goto abort; - } - - if (complain_crl_stale(crls)) - goto abort; - X509_STORE_CTX_free(ctx); - if (incidence_get_action(INID_CRL_STALE) == INAC_WARN) - pr_val_info("Re-validating avoiding CRL time check"); - return verify_cert_crl_stale(state, cert, crls); - } else { + if (error == X509_V_ERR_CRL_HAS_EXPIRED) + complain_crl_stale(cert->rpp.crl.obj); + else if (error) + pr_val_err("Certificate validation failed: %s", + X509_verify_cert_error_string(error)); + else /* * ...But don't trust X509_STORE_CTX_get_error() either. * That said, there's not much to do about !error, * so hope for the best. */ val_crypto_err("Certificate validation failed: %d", ok); - } - goto abort; } @@ -1053,7 +949,7 @@ abort: } static int -handle_ip_extension(X509_EXTENSION *ext, struct resources *resources) +handle_ip_extension(struct rpki_certificate *cert, X509_EXTENSION *ext) { ASN1_OCTET_STRING *string; struct IPAddrBlocks *blocks; @@ -1094,7 +990,9 @@ handle_ip_extension(X509_EXTENSION *ext, struct resources *resources) } for (i = 0; i < blocks->list.count && !error; i++) - error = resources_add_ip(resources, blocks->list.array[i]); + error = resources_add_ip(cert->resources, + cert->parent->resources, + blocks->list.array[i]); end: ASN_STRUCT_FREE(asn_DEF_IPAddrBlocks, blocks); @@ -1102,8 +1000,7 @@ end: } static int -handle_asn_extension(X509_EXTENSION *ext, struct resources *resources, - bool allow_inherit) +handle_asn_extension(struct rpki_certificate *cert, X509_EXTENSION *ext) { ASN1_OCTET_STRING *string; struct ASIdentifiers *ids; @@ -1115,16 +1012,17 @@ handle_asn_extension(X509_EXTENSION *ext, struct resources *resources, if (error) return error; - error = resources_add_asn(resources, ids, allow_inherit); + error = resources_add_asn(cert->resources, cert->parent->resources, + ids, cert->type != CERTYPE_BGPSEC); ASN_STRUCT_FREE(asn_DEF_ASIdentifiers, ids); return error; } static int -__certificate_get_resources(X509 *cert, struct resources *resources, +__certificate_get_resources(struct rpki_certificate *cert, int addr_nid, int asn_nid, int bad_addr_nid, int bad_asn_nid, - char const *policy_rfc, char const *bad_ext_rfc, bool allow_asn_inherit) + char const *policy_rfc, char const *bad_ext_rfc) { X509_EXTENSION *ext; int nid; @@ -1136,8 +1034,8 @@ __certificate_get_resources(X509 *cert, struct resources *resources, /* Reference: X509_get_ext_d2i */ /* rfc6487#section-2 */ - for (i = 0; i < X509_get_ext_count(cert); i++) { - ext = X509_get_ext(cert, i); + for (i = 0; i < X509_get_ext_count(cert->x509); i++) { + ext = X509_get_ext(cert->x509, i); nid = OBJ_obj2nid(X509_EXTENSION_get_object(ext)); if (nid == addr_nid) { @@ -1148,7 +1046,7 @@ __certificate_get_resources(X509 *cert, struct resources *resources, ip_ext_found = true; - error = handle_ip_extension(ext, resources); + error = handle_ip_extension(cert, ext); if (error) return error; @@ -1160,8 +1058,7 @@ __certificate_get_resources(X509 *cert, struct resources *resources, asn_ext_found = true; - error = handle_asn_extension(ext, resources, - allow_asn_inherit); + error = handle_asn_extension(cert, ext); if (error) return error; @@ -1182,26 +1079,22 @@ __certificate_get_resources(X509 *cert, struct resources *resources, /* Copies the resources from @cert to @resources. */ int -certificate_get_resources(X509 *cert, struct resources *resources, - enum cert_type type) +certificate_get_resources(struct rpki_certificate *cert) { - enum rpki_policy policy; - - policy = resources_get_policy(resources); - switch (policy) { + switch (cert->policy) { case RPKI_POLICY_RFC6484: - return __certificate_get_resources(cert, resources, + return __certificate_get_resources(cert, NID_sbgp_ipAddrBlock, NID_sbgp_autonomousSysNum, nid_ipAddrBlocksv2(), nid_autonomousSysIdsv2(), - "6484", "8360", type != CERTYPE_BGPSEC); + "6484", "8360"); case RPKI_POLICY_RFC8360: - return __certificate_get_resources(cert, resources, + return __certificate_get_resources(cert, nid_ipAddrBlocksv2(), nid_autonomousSysIdsv2(), NID_sbgp_ipAddrBlock, NID_sbgp_autonomousSysNum, - "8360", "6484", type != CERTYPE_BGPSEC); + "8360", "6484"); } - pr_crit("Unknown policy: %u", policy); + pr_crit("Unknown policy: %u", cert->policy); } static bool @@ -1263,10 +1156,9 @@ handle_rpkiNotify(char *uri, void *arg) static void handle_signedObject(char *uri, void *arg) { - struct certificate_refs *refs = arg; + struct sia_uris *sias = arg; pr_val_debug("signedObject: %s", uri); - // XXX Maybe it's time to review this API. - refs->signedObject = uri; + sias->signedObject = uri; } static int @@ -1390,7 +1282,7 @@ static int handle_cdp(void *ext, void *arg) { STACK_OF(DIST_POINT) *crldp = ext; - struct certificate_refs *refs = arg; + struct sia_uris *sias = arg; DIST_POINT *dp; GENERAL_NAMES *names; GENERAL_NAME *name; @@ -1451,7 +1343,7 @@ handle_cdp(void *ext, void *arg) * So we will store the URI in @refs, and validate it * later. */ - return ia5s2string(str, &refs->crldp); + return ia5s2string(str, &sias->crldp); } } @@ -1586,13 +1478,15 @@ handle_ad(int nid, struct ad_metadata const *meta, SIGNATURE_INFO_ACCESS *ia, static void handle_caIssuers(char *uri, void *arg) { - struct certificate_refs *refs = arg; + struct sia_uris *sias = arg; /* * Bringing the parent certificate's URI all the way * over here is too much trouble, so do the handle_cdp() * hack. + * + * XXX Uh... it's extremely easy now. */ - refs->caIssuers = uri; + sias->caIssuers = uri; } static int @@ -1683,26 +1577,25 @@ handle_cp(void *ext, void *arg) /* Validates the certificate extensions, Trust Anchor style. */ static int -certificate_validate_extensions_ta(X509 *cert, struct sia_uris *sia_uris, - enum rpki_policy *policy) +validate_ta_extensions(struct rpki_certificate *cert) { struct extension_handler handlers[] = { - /* ext reqd handler arg */ - { ext_bc(), true, handle_bc, }, - { ext_ski(), true, handle_ski_ca, cert }, - { ext_aki(), false, handle_aki_ta, cert }, - { ext_ku(), true, handle_ku_ca, }, - { ext_sia(), true, handle_sia_ca, sia_uris }, - { ext_cp(), true, handle_cp, policy }, + /* ext reqd handler arg */ + { ext_bc(), true, handle_bc, }, + { ext_ski(), true, handle_ski_ca, cert->x509 }, + { ext_aki(), false, handle_aki_ta, cert->x509 }, + { ext_ku(), true, handle_ku_ca, }, + { ext_sia(), true, handle_sia_ca, &cert->sias }, + { ext_cp(), true, handle_cp, &cert->policy }, /* These are handled by certificate_get_resources(). */ - { ext_ir(), false, }, - { ext_ar(), false, }, - { ext_ir2(), false, }, - { ext_ar2(), false, }, + { ext_ir(), false, }, + { ext_ar(), false, }, + { ext_ir2(), false, }, + { ext_ar2(), false, }, { NULL }, }; - return handle_extensions(handlers, X509_get0_extensions(cert)); + return handle_extensions(handlers, X509_get0_extensions(cert->x509)); } /* @@ -1713,71 +1606,65 @@ certificate_validate_extensions_ta(X509 *cert, struct sia_uris *sia_uris, * extensions. */ static int -certificate_validate_extensions_ca(X509 *cert, struct sia_uris *sia_uris, - enum rpki_policy *policy, struct rpp *rpp_parent) +validate_ca_extensions(struct rpki_certificate *cert) { - struct certificate_refs refs = { 0 }; struct extension_handler handlers[] = { - /* ext reqd handler arg */ - { ext_bc(), true, handle_bc, }, - { ext_ski(), true, handle_ski_ca, cert }, - { ext_aki(), true, handle_aki, }, - { ext_ku(), true, handle_ku_ca, }, - { ext_cdp(), true, handle_cdp, &refs }, - { ext_aia(), true, handle_aia, &refs }, - { ext_sia(), true, handle_sia_ca, sia_uris }, - { ext_cp(), true, handle_cp, policy }, - { ext_ir(), false, }, - { ext_ar(), false, }, - { ext_ir2(), false, }, - { ext_ar2(), false, }, + /* ext reqd handler arg */ + { ext_bc(), true, handle_bc, }, + { ext_ski(), true, handle_ski_ca, cert->x509 }, + { ext_aki(), true, handle_aki, }, + { ext_ku(), true, handle_ku_ca, }, + { ext_cdp(), true, handle_cdp, &cert->sias }, + { ext_aia(), true, handle_aia, &cert->sias }, + { ext_sia(), true, handle_sia_ca, &cert->sias }, + { ext_cp(), true, handle_cp, &cert->policy }, + /* These are handled by certificate_get_resources(). */ + { ext_ir(), false, }, + { ext_ar(), false, }, + { ext_ir2(), false, }, + { ext_ar2(), false, }, { NULL }, }; int error; - error = handle_extensions(handlers, X509_get0_extensions(cert)); + error = handle_extensions(handlers, X509_get0_extensions(cert->x509)); if (error) - goto end; - error = certificate_validate_aia(refs.caIssuers, cert); + return error; + error = certificate_validate_aia(cert); if (error) - goto end; - error = refs_validate_ca(&refs, rpp_parent); - -end: - refs_cleanup(&refs); - return error; + return error; + return validate_cdp(&cert->sias, cert->rpp.crl.map->url); } int -certificate_validate_extensions_ee(X509 *cert, OCTET_STRING_t *sid, - struct certificate_refs *refs, enum rpki_policy *policy) +certificate_validate_extensions_ee(struct rpki_certificate *cert, + OCTET_STRING_t *sid) { struct ski_arguments ski_args; struct extension_handler handlers[] = { - /* ext reqd handler arg */ - { ext_ski(), true, handle_ski_ee, &ski_args }, - { ext_aki(), true, handle_aki, }, - { ext_ku(), true, handle_ku_ee, }, - { ext_cdp(), true, handle_cdp, refs }, - { ext_aia(), true, handle_aia, refs }, - { ext_sia(), true, handle_sia_ee, refs }, - { ext_cp(), true, handle_cp, policy }, - { ext_ir(), false, }, - { ext_ar(), false, }, - { ext_ir2(), false, }, - { ext_ar2(), false, }, + /* ext reqd handler arg */ + { ext_ski(), true, handle_ski_ee, &ski_args }, + { ext_aki(), true, handle_aki, }, + { ext_ku(), true, handle_ku_ee, }, + { ext_cdp(), true, handle_cdp, &cert->sias }, + { ext_aia(), true, handle_aia, &cert->sias }, + { ext_sia(), true, handle_sia_ee, &cert->sias }, + { ext_cp(), true, handle_cp, &cert->policy }, + { ext_ir(), false, }, + { ext_ar(), false, }, + { ext_ir2(), false, }, + { ext_ar2(), false, }, { NULL }, }; - ski_args.cert = cert; + ski_args.cert = cert->x509; ski_args.sid = sid; - return handle_extensions(handlers, X509_get0_extensions(cert)); + return handle_extensions(handlers, X509_get0_extensions(cert->x509)); } int -certificate_validate_extensions_bgpsec(X509 *cert, unsigned char **ski, - enum rpki_policy *policy, struct rpp *pp) +certificate_validate_extensions_bgpsec(void) { return 0; /* TODO (#58) */ } @@ -1810,34 +1697,26 @@ has_bgpsec_router_eku(X509 *cert) * Assumption: Meant to be used exclusively in the context of parsing a .cer * certificate. */ -static int -get_certificate_type(X509 *cert, bool is_ta, enum cert_type *result) +static enum cert_type +get_certificate_type(struct rpki_certificate *cert) { - if (is_ta) { - *result = CERTYPE_TA; - return 0; - } + if (cert->rpp.ancestors == NULL) + return CERTYPE_TA; - if (X509_check_purpose(cert, -1, -1) <= 0) - goto err; + if (X509_check_purpose(cert->x509, -1, -1) <= 0) + return CERTYPE_UNKNOWN; - if (X509_check_ca(cert) == 1) { - *result = CERTYPE_CA; - return 0; - } + if (X509_check_ca(cert->x509) == 1) + return CERTYPE_CA; - if (has_bgpsec_router_eku(cert)) { - *result = CERTYPE_BGPSEC; - return 0; - } + if (has_bgpsec_router_eku(cert->x509)) + return CERTYPE_BGPSEC; -err: - *result = CERTYPE_EE; /* Shuts up nonsense gcc 8.3 warning */ - return pr_val_err("Certificate is not TA, CA nor BGPsec. Ignoring..."); + return CERTYPE_UNKNOWN; } int -certificate_validate_aia(char const *caIssuers, X509 *cert) +certificate_validate_aia(struct rpki_certificate *cert) { /* * FIXME Compare the AIA to the parent's URI. @@ -1848,62 +1727,49 @@ certificate_validate_aia(char const *caIssuers, X509 *cert) } static int -check_rpp(struct cache_mapping const *map_rpp, char *rpkiManifest) +init_resources(struct rpki_certificate *cert) { - struct cache_mapping mft; - struct rpp *pp; int error; - mft.url = rpkiManifest; - mft.path = join_paths(map_rpp->path, mft.url + RPKI_SCHEMA_LEN); + cert->resources = resources_create(cert->policy, false); - error = handle_manifest(&mft, &pp); + error = certificate_get_resources(cert); if (error) - goto end; + return error; - rpp_traverse(pp); - rpp_refput(pp); + /* + * rfc8630#section-2.3 + * "The INR extension(s) of this TA MUST contain a non-empty set of + * number resources." + * The "It MUST NOT use the "inherit" form of the INR extension(s)" + * part is already handled in certificate_get_resources(). + */ + if (cert->type == CERTYPE_TA && resources_empty(cert->resources)) + return pr_val_err("Trust Anchor certificate does not define any number resources."); -end: free(mft.path); - return error; + return 0; } -/* Boilerplate code for CA certificate validation and recursive traversal. */ -int -certificate_traverse(struct rpp *rpp_parent, - struct cache_mapping const *cert_map) +static int +certificate_validate(struct rpki_certificate *cert) { - struct validation *state; - int total_parents; - X509 *x509; - struct sia_uris sias; - enum rpki_policy policy; - enum cert_type certype; - struct cache_mapping rpp; int error; - state = state_retrieve(); - - total_parents = certstack_get_x509_num(validation_certstack(state)); - if (total_parents >= config_get_max_cert_depth()) + if (sk_X509_num(cert->rpp.ancestors) >= config_get_max_cert_depth()) return pr_val_err("Certificate chain maximum depth exceeded."); - fnstack_push_map(cert_map); + fnstack_push_map(&cert->map); - /* -- Validate the certificate (@cert) -- */ - error = certificate_load(cert_map, &x509); - if (error) - goto revert_fnstack_and_debug; - error = certificate_validate_chain(x509, rpp_crl(rpp_parent)); - if (error) - goto revert_cert; + cert->x509 = certificate_load(cert->map.path); + if (!cert->x509) + return -EINVAL; + cert->type = get_certificate_type(cert); - error = get_certificate_type(x509, rpp_parent == NULL, &certype); + error = certificate_validate_chain(cert); if (error) - goto revert_cert; + goto end; - /* Debug cert type */ - switch (certype) { + switch (cert->type) { case CERTYPE_TA: break; case CERTYPE_CA: @@ -1913,46 +1779,112 @@ certificate_traverse(struct rpp *rpp_parent, pr_val_debug("Type: BGPsec EE. Ignoring..."); // error = handle_bgpsec(cert, x509stack_peek_resources( // validation_certstack(state)), rpp_parent); - goto revert_cert; + goto end; default: pr_val_debug("Type: Unknown. Ignoring..."); - goto revert_cert; + goto end; } - error = certificate_validate_rfc6487(x509, certype); + error = certificate_validate_rfc6487(cert); if (error) - goto revert_cert; + goto end; - sias_init(&sias); - error = (certype == CERTYPE_TA) - ? certificate_validate_extensions_ta(x509, &sias, &policy) - : certificate_validate_extensions_ca(x509, &sias, &policy, - rpp_parent); + error = (cert->type == CERTYPE_TA) + ? validate_ta_extensions(cert) + : validate_ca_extensions(cert); if (error) - goto revert_sias; + goto end; - error = x509stack_push(validation_certstack(state), cert_map, x509, - policy, certype); + error = init_resources(cert); + +end: fnstack_pop(); + return error; +} + +static int +certificate_traverse(struct rpki_certificate *ca, struct cert_stack *stack) +{ + struct cache_cage *cage; + char const *mft; + array_index i; + struct cache_mapping *map; + char const *ext; + int error; + + error = certificate_validate(ca); if (error) - goto revert_sias; - x509 = NULL; /* Ownership stolen */ + return error; + + if (ca->type != CERTYPE_TA && ca->type != CERTYPE_CA) + return 0; + + cage = cache_refresh_sias(&ca->sias); + if (!cage) + return pr_val_err("caRepository '%s' could not be refreshed, " + "and there is no fallback in the cache. " + "I'm going to have to skip it.", ca->sias.caRepository); + +retry: mft = cage_map_file(cage, ca->sias.rpkiManifest); + if (!mft) { + if (cage_disable_refresh(cage)) + goto retry; + error = pr_val_err("caRepository '%s' is missing a manifest.", + ca->sias.caRepository); + goto end; + } - error = cache_refresh_sias(&sias, &rpp); + error = manifest_validate(ca->sias.rpkiManifest, mft, cage, ca); if (error) { - x509stack_cancel(validation_certstack(state)); - goto revert_sias; + if (cage_disable_refresh(cage)) + goto retry; + goto end; } - error = check_rpp(&rpp, sias.rpkiManifest); - if (error) - x509stack_cancel(validation_certstack(state)); - -revert_sias: - sias_cleanup(&sias); -revert_cert: - if (x509 != NULL) - X509_free(x509); -revert_fnstack_and_debug: - fnstack_pop(); + for (i = 0; i < ca->rpp.nfiles; i++) { + map = ca->rpp.files + i; + ext = map->url + strlen(map->url) - 4; + if (strcmp(ext, ".cer") == 0) + certificate_stack_push(stack, map, ca); + else if (strcmp(ext, ".roa") == 0) + roa_traverse(map, ca); + else if (strcmp(ext, ".gbr") == 0) + ghostbusters_traverse(map, ca); + } + +end: free(cage); return error; } + +int +traverse_tree(struct cache_mapping const *ta_map, struct validation *state) +{ + struct cert_stack stack; + struct rpki_certificate ta = { .map = *ta_map }; + struct rpki_certificate *ca; + int error; + + SLIST_INIT(&stack); + + /* == Root certificate == */ + error = certificate_traverse(&ta, &stack); // XXX clean up TA + if (error) + return error; + + /* + * From now on, the tree should be considered valid, even if subsequent + * certificates fail. + * (the root validated successfully; subtrees are isolated problems.) + */ + + /* == Every other certificate == */ + while (!SLIST_EMPTY(&stack)) { + ca = SLIST_FIRST(&stack); + SLIST_REMOVE_HEAD(&stack, lh); + + certificate_traverse(ca, &stack); + + rpki_certificate_free(ca); + } + + return 0; +} diff --git a/src/object/certificate.h b/src/object/certificate.h index 5e9aa6f6..18c0ab08 100644 --- a/src/object/certificate.h +++ b/src/object/certificate.h @@ -1,10 +1,16 @@ #ifndef SRC_OBJECT_CERTIFICATE_H_ #define SRC_OBJECT_CERTIFICATE_H_ +#include +#include + #include "asn1/asn1c/ANY.h" #include "asn1/asn1c/SignatureValue.h" +#include "cache.h" #include "certificate_refs.h" #include "resource.h" +#include "rpp.h" +#include "state.h" /* Certificate types in the RPKI */ enum cert_type { @@ -12,23 +18,45 @@ enum cert_type { CERTYPE_CA, /* Certificate Authority */ CERTYPE_BGPSEC, /* BGPsec certificates */ CERTYPE_EE, /* End Entity certificates */ + CERTYPE_UNKNOWN, }; +struct rpki_certificate { + struct cache_mapping map; /* Nonexistent on EEs */ + X509 *x509; /* Initializes after dequeue */ + + enum cert_type type; + enum rpki_policy policy; /* XXX seems redundant */ + struct resources *resources; + struct sia_uris sias; + + struct rpki_certificate *parent; + struct rpp rpp; /* Nonexistent on EEs */ + + SLIST_ENTRY(rpki_certificate) lh; /* List Hook */ + unsigned int refcount; +}; + +void rpki_certificate_init_ee(struct rpki_certificate *, + struct rpki_certificate *, bool); +void rpki_certificate_cleanup(struct rpki_certificate *); +void rpki_certificate_free(struct rpki_certificate *); + /** * Performs the basic (RFC 5280, presumably) chain validation. * (Ignores the IP and AS extensions.) */ -int certificate_validate_chain(X509 *, STACK_OF(X509_CRL) *); +int certificate_validate_chain(struct rpki_certificate *); /** * Validates RFC 6487 compliance. * (Except extensions.) */ -int certificate_validate_rfc6487(X509 *, enum cert_type); +int certificate_validate_rfc6487(struct rpki_certificate *); int certificate_validate_signature(X509 *, ANY_t *coded, SignatureValue_t *); /** - * Returns the IP and AS resources declared in the respective extensions. + * Extracts the resources from cert->x509 into cert->resources. * * Note: One reason why this is separate from the validate_extensions functions * is because it needs to be handled after the policy has been extracted from @@ -36,7 +64,7 @@ int certificate_validate_signature(X509 *, ANY_t *coded, SignatureValue_t *); * not care about order. I don't know if you'll find other reasons if you choose * to migrate it. */ -int certificate_get_resources(X509 *, struct resources *, enum cert_type); +int certificate_get_resources(struct rpki_certificate *cert); /** * Validates the certificate extensions, End-Entity style. @@ -44,17 +72,16 @@ int certificate_get_resources(X509 *, struct resources *, enum cert_type); * Also initializes the second argument with the references found in the * extensions. */ -int certificate_validate_extensions_ee(X509 *, OCTET_STRING_t *, - struct certificate_refs *, enum rpki_policy *); -int certificate_validate_extensions_bgpsec(X509 *, unsigned char **, - enum rpki_policy *, struct rpp *); +int certificate_validate_extensions_ee(struct rpki_certificate *, + OCTET_STRING_t *); +int certificate_validate_extensions_bgpsec(void); /* * Specific validation of AIA (rfc6487#section-4.8.7) extension, public so that * CAs and EEs can access it. */ -int certificate_validate_aia(char const *, X509 *); +int certificate_validate_aia(struct rpki_certificate *); -int certificate_traverse(struct rpp *, struct cache_mapping const *); +int traverse_tree(struct cache_mapping const *, struct validation *); #endif /* SRC_OBJECT_CERTIFICATE_H_ */ diff --git a/src/object/crl.c b/src/object/crl.c index 3339aecf..36fd4cd5 100644 --- a/src/object/crl.c +++ b/src/object/crl.c @@ -139,7 +139,7 @@ validate_extensions(X509_CRL *crl) } static int -crl_validate(X509_CRL *crl) +crl_validate(X509_CRL *crl, X509 *parent) { long version; int error; @@ -153,7 +153,7 @@ crl_validate(X509_CRL *crl) if (error) return error; - error = validate_issuer_name("CRL", X509_CRL_get_issuer(crl)); + error = validate_issuer_name(X509_CRL_get_issuer(crl), parent); if (error) return error; @@ -165,7 +165,7 @@ crl_validate(X509_CRL *crl) } int -crl_load(struct cache_mapping *map, X509_CRL **result) +crl_load(struct cache_mapping *map, X509 *parent, X509_CRL **result) { int error; @@ -175,7 +175,7 @@ crl_load(struct cache_mapping *map, X509_CRL **result) if (error) goto end; - error = crl_validate(*result); + error = crl_validate(*result, parent); if (error) X509_CRL_free(*result); diff --git a/src/object/crl.h b/src/object/crl.h index 59006ba0..04240ff3 100644 --- a/src/object/crl.h +++ b/src/object/crl.h @@ -4,6 +4,6 @@ #include #include "types/map.h" -int crl_load(struct cache_mapping *, X509_CRL **); +int crl_load(struct cache_mapping *, X509 *, X509_CRL **); #endif /* SRC_OBJECT_CRL_H_ */ diff --git a/src/object/ghostbusters.c b/src/object/ghostbusters.c index 7f5bf217..d9d979d2 100644 --- a/src/object/ghostbusters.c +++ b/src/object/ghostbusters.c @@ -16,13 +16,13 @@ handle_vcard(struct signed_object *sobj) } int -ghostbusters_traverse(struct cache_mapping *map, struct rpp *pp) +ghostbusters_traverse(struct cache_mapping *map, + struct rpki_certificate *parent) { static OID oid = OID_GHOSTBUSTERS; struct oid_arcs arcs = OID2ARCS("ghostbusters", oid); struct signed_object sobj; - struct ee_cert ee; - STACK_OF(X509_CRL) *crl; + struct rpki_certificate ee; int error; /* Prepare */ @@ -31,30 +31,22 @@ ghostbusters_traverse(struct cache_mapping *map, struct rpp *pp) /* Decode */ error = signed_object_decode(&sobj, map->path); if (error) - goto revert_log; + goto end1; /* Prepare validation arguments */ - crl = rpp_crl(pp); - if (crl == NULL) { - error = -EINVAL; - goto revert_sobj; - } - eecert_init(&ee, crl, true); + rpki_certificate_init_ee(&ee, parent, true); /* Validate everything */ error = signed_object_validate(&sobj, &arcs, &ee); if (error) - goto revert_args; + goto end2; error = handle_vcard(&sobj); if (error) - goto revert_args; - error = refs_validate_ee(&ee.refs, pp, map->url); + goto end2; + error = refs_validate_ee(&ee.sias, parent->rpp.crl.map->url, map->url); -revert_args: - eecert_cleanup(&ee); -revert_sobj: +end2: rpki_certificate_cleanup(&ee); signed_object_cleanup(&sobj); -revert_log: - fnstack_pop(); +end1: fnstack_pop(); return error; } diff --git a/src/object/ghostbusters.h b/src/object/ghostbusters.h index 5e71f658..39350737 100644 --- a/src/object/ghostbusters.h +++ b/src/object/ghostbusters.h @@ -1,8 +1,8 @@ #ifndef SRC_OBJECT_GHOSTBUSTERS_H_ #define SRC_OBJECT_GHOSTBUSTERS_H_ -#include "rpp.h" +#include "object/certificate.h" -int ghostbusters_traverse(struct cache_mapping *, struct rpp *); +int ghostbusters_traverse(struct cache_mapping *, struct rpki_certificate *); #endif /* SRC_OBJECT_GHOSTBUSTERS_H_ */ diff --git a/src/object/manifest.c b/src/object/manifest.c index 644ec256..9b3edf2e 100644 --- a/src/object/manifest.c +++ b/src/object/manifest.c @@ -1,11 +1,13 @@ #include "object/manifest.h" #include "algorithm.h" +#include "alloc.h" #include "asn1/asn1c/Manifest.h" #include "asn1/decode.h" #include "common.h" #include "hash.h" #include "log.h" +#include "object/crl.h" #include "object/signed_object.h" #include "thread_var.h" #include "types/path.h" @@ -166,6 +168,25 @@ validate_manifest(struct Manifest *manifest) return 0; } +static void +shuffle_mft_files(struct Manifest *mft) +{ + int i, j; + unsigned int seed, rnd; + struct FileAndHash *tmpfah; + + seed = time(NULL) ^ getpid(); + + /* Fisher-Yates shuffle with modulo bias */ + for (i = 0; i < mft->fileList.list.count - 1; i++) { + rnd = rand_r(&seed); + j = i + rnd % (mft->fileList.list.count - i); + tmpfah = mft->fileList.list.array[j]; + mft->fileList.list.array[j] = mft->fileList.list.array[i]; + mft->fileList.list.array[i] = tmpfah; + } +} + static bool is_valid_mft_file_chara(uint8_t chara) { @@ -178,7 +199,7 @@ is_valid_mft_file_chara(uint8_t chara) /* RFC 9286, section 4.2.2 */ static int -validate_mft_file(IA5String_t *ia5) +validate_mft_filename(IA5String_t *ia5) { size_t dot; size_t i; @@ -214,9 +235,6 @@ check_file_and_hash(struct FileAndHash *fah, char const *path) fah->hash.buf, fah->hash.size); } -#define INFER_CHILD(parent, fah) \ - path_childn(parent, (char const *)fah->file.buf, fah->file.size) - /* * XXX * @@ -234,131 +252,124 @@ check_file_and_hash(struct FileAndHash *fah, char const *path) */ static int -build_rpp(struct cache_mapping *mft_map, struct Manifest *mft, - struct rpp **result) +build_rpp(char const *mft_url, struct Manifest *mft, struct cache_cage *cage, + struct rpki_certificate *parent) { - struct cache_mapping pp_map; - struct rpp *pp; - unsigned int i, j; - struct FileAndHash *fah, *tmpfah; - struct cache_mapping map; + struct rpp *rpp; + char *rpp_url; + unsigned int i; + struct FileAndHash *src; + struct cache_mapping *dst; + char const *path; int error; - unsigned int seed, rnd; - seed = time(NULL) ^ getpid(); + shuffle_mft_files(mft); - map_parent(mft_map, &pp_map); - pp = rpp_create(); - - /* Fisher-Yates shuffle with modulo bias */ - for (i = 0; i < mft->fileList.list.count - 1; i++) { - rnd = rand_r(&seed); - j = i + rnd % (mft->fileList.list.count - i); - tmpfah = mft->fileList.list.array[j]; - mft->fileList.list.array[j] = mft->fileList.list.array[i]; - mft->fileList.list.array[i] = tmpfah; - } + rpp = &parent->rpp; + rpp_url = path_parent(mft_url); + rpp->nfiles = mft->fileList.list.count; + rpp->files = pzalloc(rpp->nfiles * sizeof(*rpp->files)); for (i = 0; i < mft->fileList.list.count; i++) { - fah = mft->fileList.list.array[i]; + src = mft->fileList.list.array[i]; + dst = &rpp->files[i]; /* * IA5String is a subset of ASCII. However, IA5String_t doesn't * seem to be guaranteed to be NULL-terminated. */ - error = validate_mft_file(&fah->file); + error = validate_mft_filename(&src->file); if (error) - goto fail; - - map.url = INFER_CHILD(pp_map.url, fah); - map.path = INFER_CHILD(pp_map.path, fah); + goto revert; + + dst->url = path_childn(rpp_url, + (char const *)src->file.buf, + src->file.size); + + path = cage_map_file(cage, dst->url); + if (!path) { + error = pr_val_err( + "Manifest file '%s' is absent from the cache.", + dst->url); + goto revert; + } + dst->path = pstrdup(path); - error = check_file_and_hash(fah, map.path); + error = check_file_and_hash(src, dst->path); if (error) - goto fail2; + goto revert; - error = rpp_add_file(pp, &map); - if (error) - goto fail2; + if (strcmp(((char const *)src->file.buf) + src->file.size - 4, ".crl") == 0) { + if (rpp->crl.map != NULL) { + error = pr_val_err( + "Manifest has more than one CRL."); + goto revert; + } + rpp->crl.map = dst; + } } /* rfc6486#section-7 */ - if (rpp_crl(pp) == NULL) { + if (rpp->crl.map == NULL) { error = pr_val_err("Manifest lacks a CRL."); - goto fail; + goto revert; } - map_cleanup(&pp_map); - *result = pp; + error = crl_load(rpp->crl.map, parent->x509, &rpp->crl.obj); + if (error) + goto revert; + + free(rpp_url); return 0; -fail2: map_cleanup(&map); -fail: map_cleanup(&pp_map); - rpp_refput(pp); +revert: rpp_cleanup(rpp); + free(rpp_url); return error; } -/* Validates the manifest @map, returns the RPP described by it in @pp. */ int -handle_manifest(struct cache_mapping *map, struct rpp **pp) +manifest_validate(char const *url, char const *path, struct cache_cage *cage, + struct rpki_certificate *parent) { static OID oid = OID_MANIFEST; struct oid_arcs arcs = OID2ARCS("manifest", oid); struct signed_object sobj; - struct ee_cert ee; + struct rpki_certificate ee; struct Manifest *mft; - STACK_OF(X509_CRL) *crl; int error; /* Prepare */ - fnstack_push_map(map); + fnstack_push(url); // XXX /* Decode */ - error = signed_object_decode(&sobj, map->path); + error = signed_object_decode(&sobj, path); if (error) - goto revert_log; + goto end1; error = decode_manifest(&sobj, &mft); if (error) - goto revert_sobj; + goto end2; - /* Initialize @pp */ - error = build_rpp(map, mft, pp); + /* Initialize @summary */ + error = build_rpp(url, mft, cage, parent); if (error) - goto revert_manifest; + goto end3; /* Prepare validation arguments */ - crl = rpp_crl(*pp); - if (crl == NULL) { - error = -EINVAL; - goto revert_rpp; - } - eecert_init(&ee, crl, false); + rpki_certificate_init_ee(&ee, parent, false); /* Validate everything */ error = signed_object_validate(&sobj, &arcs, &ee); if (error) - goto revert_args; + goto end4; error = validate_manifest(mft); if (error) - goto revert_args; - error = refs_validate_ee(&ee.refs, *pp, map->url); - if (error) - goto revert_args; - - /* Success */ - eecert_cleanup(&ee); - goto revert_manifest; - -revert_args: - eecert_cleanup(&ee); -revert_rpp: - rpp_refput(*pp); -revert_manifest: - ASN_STRUCT_FREE(asn_DEF_Manifest, mft); -revert_sobj: - signed_object_cleanup(&sobj); -revert_log: - fnstack_pop(); + goto end4; + error = refs_validate_ee(&ee.sias, parent->rpp.crl.map->url, url); + +end4: rpki_certificate_cleanup(&ee); +end3: ASN_STRUCT_FREE(asn_DEF_Manifest, mft); +end2: signed_object_cleanup(&sobj); +end1: fnstack_pop(); return error; } diff --git a/src/object/manifest.h b/src/object/manifest.h index 3deb6ca3..12cb1648 100644 --- a/src/object/manifest.h +++ b/src/object/manifest.h @@ -1,8 +1,13 @@ #ifndef SRC_OBJECT_MANIFEST_H_ #define SRC_OBJECT_MANIFEST_H_ -#include "rpp.h" +#include +#include -int handle_manifest(struct cache_mapping *map, struct rpp **); +#include "cache.h" +#include "object/certificate.h" + +int manifest_validate(char const *url, char const *path, + struct cache_cage *cage, struct rpki_certificate *parent); #endif /* SRC_OBJECT_MANIFEST_H_ */ diff --git a/src/object/roa.c b/src/object/roa.c index c45f614e..cb70349f 100644 --- a/src/object/roa.c +++ b/src/object/roa.c @@ -200,14 +200,13 @@ family_error: } int -roa_traverse(struct cache_mapping *map, struct rpp *pp) +roa_traverse(struct cache_mapping *map, struct rpki_certificate *parent) { static OID oid = OID_ROA; struct oid_arcs arcs = OID2ARCS("roa", oid); struct signed_object sobj; - struct ee_cert ee; + struct rpki_certificate ee; struct RouteOriginAttestation *roa; - STACK_OF(X509_CRL) *crl; int error; /* Prepare */ @@ -216,35 +215,26 @@ roa_traverse(struct cache_mapping *map, struct rpp *pp) /* Decode */ error = signed_object_decode(&sobj, map->path); if (error) - goto revert_log; + goto end1; error = decode_roa(&sobj, &roa); if (error) - goto revert_sobj; + goto end2; /* Prepare validation arguments */ - crl = rpp_crl(pp); - if (crl == NULL) { - error = -EINVAL; - goto revert_roa; - } - eecert_init(&ee, crl, false); + rpki_certificate_init_ee(&ee, parent, false); /* Validate and handle everything */ error = signed_object_validate(&sobj, &arcs, &ee); if (error) - goto revert_args; - error = __handle_roa(roa, ee.res); + goto end3; + error = __handle_roa(roa, ee.resources); if (error) - goto revert_args; - error = refs_validate_ee(&ee.refs, pp, map->url); + goto end3; + error = refs_validate_ee(&ee.sias, parent->rpp.crl.map->url, map->url); -revert_args: - eecert_cleanup(&ee); -revert_roa: +end3: rpki_certificate_cleanup(&ee); ASN_STRUCT_FREE(asn_DEF_RouteOriginAttestation, roa); -revert_sobj: - signed_object_cleanup(&sobj); -revert_log: - fnstack_pop(); +end2: signed_object_cleanup(&sobj); +end1: fnstack_pop(); return error; } diff --git a/src/object/roa.h b/src/object/roa.h index 2ae9a227..9642f3cc 100644 --- a/src/object/roa.h +++ b/src/object/roa.h @@ -1,8 +1,8 @@ #ifndef SRC_OBJECT_ROA_H_ #define SRC_OBJECT_ROA_H_ -#include "rpp.h" +#include "object/certificate.h" -int roa_traverse(struct cache_mapping *, struct rpp *); +int roa_traverse(struct cache_mapping *, struct rpki_certificate *); #endif /* SRC_OBJECT_ROA_H_ */ diff --git a/src/object/signed_object.c b/src/object/signed_object.c index f6c11fef..8947a4e1 100644 --- a/src/object/signed_object.c +++ b/src/object/signed_object.c @@ -68,7 +68,7 @@ validate_content_type(struct SignedData *sdata, struct oid_arcs const *oid) int signed_object_validate(struct signed_object *sobj, struct oid_arcs const *oid, - struct ee_cert *ee) + struct rpki_certificate *ee) { int error; diff --git a/src/object/signed_object.h b/src/object/signed_object.h index cced66e6..35670ab0 100644 --- a/src/object/signed_object.h +++ b/src/object/signed_object.h @@ -12,7 +12,7 @@ struct signed_object { int signed_object_decode(struct signed_object *, char const *); int signed_object_validate(struct signed_object *, struct oid_arcs const *, - struct ee_cert *); + struct rpki_certificate *); void signed_object_cleanup(struct signed_object *); #endif /* SRC_OBJECT_SIGNED_OBJECT_H_ */ diff --git a/src/object/tal.c b/src/object/tal.c index 0802e28a..2aac5dad 100644 --- a/src/object/tal.c +++ b/src/object/tal.c @@ -10,6 +10,7 @@ #include "config.h" #include "file.h" #include "log.h" +#include "object/certificate.h" #include "thread_var.h" #include "types/path.h" #include "types/str.h" @@ -34,14 +35,6 @@ struct validation_thread { /* List of threads, one per TAL file */ SLIST_HEAD(threads_list, validation_thread); -#define TS_SUCCESS 0 /* TA looks sane. */ -#define TS_NEXT 1 /* TA seems compromised; try some other URL. */ -#define TS_FALLBACK 2 /* TA is broken; fall back to old cache. */ -typedef struct { int v; } ta_status; -static ta_status ts_success = { .v = TS_SUCCESS }; -static ta_status ts_next = { .v = TS_NEXT }; -static ta_status ts_fallback = { .v = TS_FALLBACK }; - static char * find_newline(char *str) { @@ -153,62 +146,6 @@ tal_get_spki(struct tal *tal, unsigned char const **buffer, size_t *len) *len = tal->spki_len; } -static ta_status -handle_ta(struct cache_mapping const *ta, struct validation *state) -{ - struct cert_stack *certstack; - struct deferred_cert deferred; - int error; - - /* == Root certificate == */ - if (certificate_traverse(NULL, ta) != 0) { - switch (validation_pubkey_state(state)) { - case PKS_INVALID: - /* Signature invalid; probably an impersonator. */ - return ts_next; - case PKS_VALID: - /* No impersonator but still error: Broken tree. */ - /* - * XXX Change to ts_next. This is the TA; - * we can't really afford to panic-fallback. - */ - return ts_fallback; - case PKS_UNTESTED: - /* We don't know; try some other URL. */ - return ts_next; - } - - pr_crit("Unknown public key state: %u", - validation_pubkey_state(state)); - } - - /* - * From now on, the tree should be considered valid, even if subsequent - * certificates fail. - * (the root validated successfully; subtrees are isolated problems.) - */ - - /* == Every other certificate == */ - certstack = validation_certstack(state); - - do { - error = deferstack_pop(certstack, &deferred); - if (error == -ENOENT) - return ts_success; /* No more certificates left */ - else if (error) /* All other errors are critical, currently */ - pr_crit("deferstack_pop() returned illegal %d.", error); - - /* - * Ignore result code; remaining certificates are unrelated, - * so they should not be affected. - */ - certificate_traverse(deferred.pp, &deferred.map); - - map_cleanup(&deferred.map); - rpp_refput(deferred.pp); - } while (true); -} - static void __do_file_validation(struct validation_thread *thread) { @@ -218,7 +155,6 @@ __do_file_validation(struct validation_thread *thread) struct validation *state; char **url; struct cache_mapping map; - ta_status status; thread->error = tal_init(&tal, thread->tal_file); if (thread->error) @@ -236,28 +172,23 @@ __do_file_validation(struct validation_thread *thread) } ARRAYLIST_FOREACH(&tal.urls, url) { - if (cache_refresh_url(*url, &map) != 0) + map.url = *url; + map.path = cache_refresh_url(*url); + if (!map.path) continue; - - status = handle_ta(&map, state); - switch (status.v) { - case TS_SUCCESS: goto end1; - case TS_FALLBACK: goto fallback; - case TS_NEXT: ; /* Fall through */ - } + if (traverse_tree(&map, state) != 0) + continue; + goto end1; /* Happy path */ } -fallback: ARRAYLIST_FOREACH(&tal.urls, url) { - if (cache_fallback_url(*url, &map) != 0) + map.url = *url; + map.path = cache_fallback_url(*url); + if (!map.path) continue; - - status = handle_ta(&map, state); - switch (status.v) { - case TS_SUCCESS: goto end1; - case TS_FALLBACK: /* Already fallbacking */ - case TS_NEXT: ; /* Fall through */ - } + if (traverse_tree(&map, state) != 0) + continue; + goto end1; /* Happy path */ } pr_op_err("None of the TAL URIs yielded a successful traversal."); diff --git a/src/print_file.c b/src/print_file.c index 27476c93..7d91ee10 100644 --- a/src/print_file.c +++ b/src/print_file.c @@ -20,10 +20,15 @@ static BIO * __rsync2bio(char const *src, char const *dst) { + struct cache_mapping map; int error; // XXX use the cache - error = rsync_download(src, dst, NULL); + + map.url = (char *)src; + map.path = (char *)dst; + + error = rsync_download(&map); if (error) { pr_op_err("rysnc download failed: %s", strerror(abs(error))); return NULL; diff --git a/src/resource.c b/src/resource.c index 7ab0394e..069565ee 100644 --- a/src/resource.c +++ b/src/resource.c @@ -78,18 +78,10 @@ unknown: return -1; } -static struct resources * -get_parent_resources(void) -{ - return x509stack_peek_resources(validation_certstack(state_retrieve())); -} - static int -inherit_aors(struct resources *resources, int family) +inherit_aors(struct resources *resources, struct resources *parent, int family) { - struct resources *parent; - - parent = get_parent_resources(); + // XXX is this really crit worthy? if (parent == NULL) pr_crit("Parent has no resources."); @@ -118,15 +110,13 @@ inherit_aors(struct resources *resources, int family) } static int -add_prefix4(struct resources *resources, IPAddress_t *addr) +add_prefix4(struct resources *resources, struct resources *parent, + IPAddress_t *addr) { - struct resources *parent; struct ipv4_prefix prefix; int error; - parent = get_parent_resources(); - - if ((parent != NULL) && (resources->ip4s == parent->ip4s)) + if (parent && (resources->ip4s == parent->ip4s)) return pr_val_err("Certificate defines IPv4 prefixes while also inheriting his parent's."); error = prefix4_decode(addr, &prefix); @@ -160,15 +150,13 @@ add_prefix4(struct resources *resources, IPAddress_t *addr) } static int -add_prefix6(struct resources *resources, IPAddress_t *addr) +add_prefix6(struct resources *resources, struct resources *parent, + IPAddress_t *addr) { - struct resources *parent; struct ipv6_prefix prefix; int error; - parent = get_parent_resources(); - - if ((parent != NULL) && (resources->ip6s == parent->ip6s)) + if (parent && (resources->ip6s == parent->ip6s)) return pr_val_err("Certificate defines IPv6 prefixes while also inheriting his parent's."); error = prefix6_decode(addr, &prefix); @@ -202,13 +190,14 @@ add_prefix6(struct resources *resources, IPAddress_t *addr) } static int -add_prefix(struct resources *resources, int family, IPAddress_t *addr) +add_prefix(struct resources *resources, struct resources *parent, + int family, IPAddress_t *addr) { switch (family) { case AF_INET: - return add_prefix4(resources, addr); + return add_prefix4(resources, parent, addr); case AF_INET6: - return add_prefix6(resources, addr); + return add_prefix6(resources, parent, addr); } pr_crit("Unknown address family '%d'", family); @@ -216,14 +205,12 @@ add_prefix(struct resources *resources, int family, IPAddress_t *addr) } static int -add_range4(struct resources *resources, IPAddressRange_t *input) +add_range4(struct resources *resources, struct resources *parent, + IPAddressRange_t *input) { - struct resources *parent; struct ipv4_range range; int error; - parent = get_parent_resources(); - if (parent && (resources->ip4s == parent->ip4s)) return pr_val_err("Certificate defines IPv4 ranges while also inheriting his parent's."); @@ -259,15 +246,13 @@ add_range4(struct resources *resources, IPAddressRange_t *input) } static int -add_range6(struct resources *resources, IPAddressRange_t *input) +add_range6(struct resources *resources, struct resources *parent, + IPAddressRange_t *input) { - struct resources *parent; struct ipv6_range range; int error; - parent = get_parent_resources(); - - if ((parent != NULL) && (resources->ip6s == parent->ip6s)) + if (parent && (resources->ip6s == parent->ip6s)) return pr_val_err("Certificate defines IPv6 ranges while also inheriting his parent's."); error = range6_decode(input, &range); @@ -302,13 +287,14 @@ add_range6(struct resources *resources, IPAddressRange_t *input) } static int -add_range(struct resources *resources, int family, IPAddressRange_t *range) +add_range(struct resources *resources, struct resources *parent, + int family, IPAddressRange_t *range) { switch (family) { case AF_INET: - return add_range4(resources, range); + return add_range4(resources, parent, range); case AF_INET6: - return add_range6(resources, range); + return add_range6(resources, parent, range); } pr_crit("Unknown address family '%d'", family); @@ -316,7 +302,7 @@ add_range(struct resources *resources, int family, IPAddressRange_t *range) } static int -add_aors(struct resources *resources, int family, +add_aors(struct resources *resources, struct resources *parent, int family, struct IPAddressChoice__addressesOrRanges *aors) { struct IPAddressOrRange *aor; @@ -332,13 +318,13 @@ add_aors(struct resources *resources, int family, aor = aors->list.array[i]; switch (aor->present) { case IPAddressOrRange_PR_addressPrefix: - error = add_prefix(resources, family, + error = add_prefix(resources, parent, family, &aor->choice.addressPrefix); if (error) return error; break; case IPAddressOrRange_PR_addressRange: - error = add_range(resources, family, + error = add_range(resources, parent, family, &aor->choice.addressRange); if (error) return error; @@ -354,7 +340,8 @@ add_aors(struct resources *resources, int family, } int -resources_add_ip(struct resources *resources, struct IPAddressFamily *obj) +resources_add_ip(struct resources *resources, struct resources *parent, + struct IPAddressFamily *obj) { int family; @@ -366,9 +353,9 @@ resources_add_ip(struct resources *resources, struct IPAddressFamily *obj) case IPAddressChoice_PR_NOTHING: break; case IPAddressChoice_PR_inherit: - return inherit_aors(resources, family); + return inherit_aors(resources, parent, family); case IPAddressChoice_PR_addressesOrRanges: - return add_aors(resources, family, + return add_aors(resources, parent, family, &obj->ipAddressChoice.choice.addressesOrRanges); } @@ -378,11 +365,8 @@ resources_add_ip(struct resources *resources, struct IPAddressFamily *obj) } static int -inherit_asiors(struct resources *resources) +inherit_asiors(struct resources *resources, struct resources *parent) { - struct resources *parent; - - parent = get_parent_resources(); if (parent == NULL) pr_crit("Parent has no resources."); @@ -461,15 +445,13 @@ add_asn(struct resources *resources, struct asn_range const *asns, } static int -add_asior(struct resources *resources, struct ASIdOrRange *obj) +add_asior(struct resources *resources, struct resources *parent, + struct ASIdOrRange *obj) { - struct resources *parent; struct asn_range asns; int error; - parent = get_parent_resources(); - - if ((parent != NULL) && (resources->asns == parent->asns)) + if (parent && (resources->asns == parent->asns)) return pr_val_err("Certificate defines ASN resources while also inheriting his parent's."); switch (obj->present) { @@ -497,7 +479,8 @@ add_asior(struct resources *resources, struct ASIdOrRange *obj) } static int -add_asiors(struct resources *resources, struct ASIdentifiers *ids) +add_asiors(struct resources *resources, struct resources *parent, + struct ASIdentifiers *ids) { struct ASIdentifierChoice__asIdsOrRanges *iors; int i; @@ -511,7 +494,7 @@ add_asiors(struct resources *resources, struct ASIdentifiers *ids) return pr_val_err("AS extension's set of AS number records is empty."); for (i = 0; i < iors->list.count; i++) { - error = add_asior(resources, iors->list.array[i]); + error = add_asior(resources, parent, iors->list.array[i]); if (error) return error; } @@ -520,8 +503,8 @@ add_asiors(struct resources *resources, struct ASIdentifiers *ids) } int -resources_add_asn(struct resources *resources, struct ASIdentifiers *ids, - bool allow_inherit) +resources_add_asn(struct resources *resources, struct resources *parent, + struct ASIdentifiers *ids, bool allow_inherit) { if (ids->asnum == NULL) return pr_val_err("ASN extension lacks 'asnum' element."); @@ -533,9 +516,9 @@ resources_add_asn(struct resources *resources, struct ASIdentifiers *ids, if (!allow_inherit) return pr_val_err("ASIdentifierChoice %u isn't allowed", ids->asnum->present); - return inherit_asiors(resources); + return inherit_asiors(resources, parent); case ASIdentifierChoice_PR_asIdsOrRanges: - return add_asiors(resources, ids); + return add_asiors(resources, parent, ids); case ASIdentifierChoice_PR_NOTHING: break; } @@ -569,12 +552,6 @@ resources_contains_ipv6(struct resources *res, struct ipv6_prefix const *prefix) return res6_contains_prefix(res->ip6s, prefix); } -enum rpki_policy -resources_get_policy(struct resources *res) -{ - return res->policy; -} - void resources_set_policy(struct resources *res, enum rpki_policy policy) { diff --git a/src/resource.h b/src/resource.h index 661e3b7b..5d811c23 100644 --- a/src/resource.h +++ b/src/resource.h @@ -26,15 +26,16 @@ struct resources; struct resources *resources_create(enum rpki_policy, bool); void resources_destroy(struct resources *); -int resources_add_ip(struct resources *, struct IPAddressFamily *); -int resources_add_asn(struct resources *, struct ASIdentifiers *, bool); +int resources_add_ip(struct resources *, struct resources *, + struct IPAddressFamily *); +int resources_add_asn(struct resources *, struct resources *, + struct ASIdentifiers *, bool); bool resources_empty(struct resources *); bool resources_contains_asns(struct resources *, struct asn_range const *); bool resources_contains_ipv4(struct resources *, struct ipv4_prefix const *); bool resources_contains_ipv6(struct resources *, struct ipv6_prefix const *); -enum rpki_policy resources_get_policy(struct resources *); void resources_set_policy(struct resources *, enum rpki_policy); int resources_foreach_asn(struct resources *, foreach_asn_cb, void *); diff --git a/src/rpp.c b/src/rpp.c index c3e75f94..160db8bf 100644 --- a/src/rpp.c +++ b/src/rpp.c @@ -1,144 +1,18 @@ #include "rpp.h" -#include "common.h" -#include "log.h" -#include "object/crl.h" -#include "object/ghostbusters.h" -#include "object/roa.h" -#include "thread_var.h" -#include "types/arraylist.h" - -STATIC_ARRAY_LIST(filelist, struct cache_mapping) - -/* A Repository Publication Point (RFC 6481), as described by some manifest. */ -struct rpp { - struct filelist files; - - struct { - struct cache_mapping map; - STACK_OF(X509_CRL) *stack; - } crl; - - /* - * Note that the reference counting functions are not prepared for - * multithreading, because this is not atomic. - */ - unsigned int references; -}; - -struct rpp * -rpp_create(void) -{ - struct rpp *result = pmalloc(sizeof(struct rpp)); - filelist_init(&result->files); - memset(&result->crl, 0, sizeof(result->crl)); - result->references = 1; - return result; -} +#include "types/array.h" void -rpp_refget(struct rpp *pp) +rpp_cleanup(struct rpp *rpp) { - pp->references++; -} - -void -rpp_refput(struct rpp *pp) -{ - pp->references--; - if (pp->references == 0) { - filelist_cleanup(&pp->files, map_cleanup); - free(pp->crl.map.url); - free(pp->crl.map.path); - sk_X509_CRL_pop_free(pp->crl.stack, X509_CRL_free); - free(pp); - } -} - -static int -set_crl(struct rpp *pp, struct cache_mapping *map) -{ - X509_CRL *crl; - int error; - - /* rfc6481#section-2.2 */ - if (pp->crl.stack != NULL) - return pr_val_err("Repository Publication Point has more than one CRL."); - - error = crl_load(map, &crl); - if (error) - return error; - - pp->crl.stack = sk_X509_CRL_new_null(); - if (pp->crl.stack == NULL) - enomem_panic(); - if (sk_X509_CRL_push(pp->crl.stack, crl) <= 0) { - X509_CRL_free(crl); - sk_X509_CRL_pop_free(pp->crl.stack, X509_CRL_free); - pp->crl.stack = NULL; - return val_crypto_err("Could not add CRL to a CRL stack"); - } - - pp->crl.map = *map; - return 0; -} - -/* Steals ownership of @map->* */ -int -rpp_add_file(struct rpp *pp, struct cache_mapping *map) -{ - if (str_ends_with(map->url, ".crl")) - return set_crl(pp, map); - - filelist_add(&pp->files, map); - return 0; -} - -char const * -rpp_get_crl_url(struct rpp const *pp) -{ - return pp->crl.map.url; -} - -/* - * The stack belongs to @pp and should not be released. Can be NULL, in which - * case you're currently validating the TA (since it lacks governing CRL). - */ -STACK_OF(X509_CRL) * -rpp_crl(struct rpp *pp) -{ - return (pp != NULL) ? pp->crl.stack : NULL; -} - -/* Traverses through all of @pp's known files, validating them. */ -void -rpp_traverse(struct rpp *pp) -{ - struct cert_stack *certstack; - struct cache_mapping *map; - - /* - * A subtree should not invalidate the rest of the tree, so error codes - * are ignored. - * (Errors log messages anyway.) - */ + array_index i; - /* - * Certificates cannot be validated now, because then - * the algorithm would be recursive. - * Store them in the defer stack (see cert_stack.h), - * will get back to them later. - */ + sk_X509_pop_free(rpp->ancestors, X509_free); - certstack = validation_certstack(state_retrieve()); + for (i = 0; i < rpp->nfiles; i++) + map_cleanup(&rpp->files[i]); + free(rpp->files); - ARRAYLIST_FOREACH(&pp->files, map) { - char const *ext = map->url + strlen(map->url) - 4; - if (strcmp(ext, ".cer") == 0) - deferstack_push(certstack, map, pp); - else if (strcmp(ext, ".roa") == 0) - roa_traverse(map, pp); - else if (strcmp(ext, ".gbr") == 0) - ghostbusters_traverse(map, pp); - } + if (rpp->crl.obj != NULL) + X509_CRL_free(rpp->crl.obj); } diff --git a/src/rpp.h b/src/rpp.h index 15a15e3a..f63f2246 100644 --- a/src/rpp.h +++ b/src/rpp.h @@ -1,22 +1,25 @@ #ifndef SRC_RPP_H_ #define SRC_RPP_H_ -#include +// XXX move to types? + #include #include "types/map.h" -struct rpp; - -struct rpp *rpp_create(void); -void rpp_refget(struct rpp *); -void rpp_refput(struct rpp *); +/* Repository Publication Point */ +struct rpp { + STACK_OF(X509) *ancestors; /* 1st = root, last = parent */ -int rpp_add_file(struct rpp *, struct cache_mapping *); + struct cache_mapping *files; + size_t nfiles; /* Number of maps in @files */ -char const *rpp_get_crl_url(struct rpp const *); -STACK_OF(X509_CRL) *rpp_crl(struct rpp *); + struct { + struct cache_mapping *map; /* Points to @files entry */ + X509_CRL *obj; + } crl; +}; -void rpp_traverse(struct rpp *); +void rpp_cleanup(struct rpp *); #endif /* SRC_RPP_H_ */ diff --git a/src/rrdp.c b/src/rrdp.c index ab87db9f..437cf706 100644 --- a/src/rrdp.c +++ b/src/rrdp.c @@ -1,7 +1,10 @@ #include "rrdp.h" +#include +#include +#include + #include "base64.h" -#include "cachent.h" #include "cachetmp.h" #include "common.h" #include "config.h" @@ -15,6 +18,7 @@ #include "types/arraylist.h" #include "types/path.h" #include "types/url.h" +#include "types/uthash.h" /* RRDP's XML namespace */ #define RRDP_NAMESPACE "http://www.ripe.net/rpki/rrdp" @@ -33,6 +37,47 @@ #define RRDP_ATTR_URI "uri" #define RRDP_ATTR_HASH "hash" +struct rrdp_serial { + BIGNUM *num; + char *str; /* String version of @num. */ +}; + +struct rrdp_session { + char *session_id; + struct rrdp_serial serial; +}; + +#define RRDP_HASH_LEN SHA256_DIGEST_LENGTH + +struct rrdp_hash { + unsigned char bytes[RRDP_HASH_LEN]; + STAILQ_ENTRY(rrdp_hash) hook; +}; + +struct cache_file { + struct cache_mapping map; + UT_hash_handle hh; /* Hash table hook */ +}; + +/* Subset of the notification that is relevant to the TAL's cachefile */ +struct rrdp_state { + char const *repo; /* Points to cache_node's map.path */ + + struct rrdp_session session; + + struct cache_file *files; /* Hash table */ + unsigned int next_id; + size_t pathlen; + + /* + * The 1st one contains the hash of the session.serial delta. + * The 2nd one contains the hash of the session.serial - 1 delta. + * The 3rd one contains the hash of the session.serial - 2 delta. + * And so on. + */ + STAILQ_HEAD(, rrdp_hash) delta_hashes; +}; + struct file_metadata { char *uri; unsigned char *hash; /* Array. Sometimes omitted. */ @@ -70,7 +115,7 @@ struct withdraw { struct parser_args { struct rrdp_session *session; - struct cache_node *notif; + struct rrdp_state *state; }; static BIGNUM * @@ -99,6 +144,27 @@ session_cleanup(struct rrdp_session *meta) free(meta->serial.str); } +static struct cache_file * +state_find_file(struct rrdp_state *state, char const *url, size_t len) +{ + struct cache_file *file; + HASH_FIND(hh, state->files, url, len, file); + return file; +} + +static void +state_flush_files(struct rrdp_state *state) +{ + struct cache_file *file, *tmp; + + HASH_ITER(hh, state->files, file, tmp) { + HASH_DEL(state->files, file); + free(file->map.url); + free(file->map.path); + free(file); + } +} + static void metadata_cleanup(struct file_metadata *meta) { @@ -371,7 +437,7 @@ parse_session(xmlTextReaderPtr reader, struct rrdp_session *meta) } static int -validate_session(xmlTextReaderPtr reader, struct rrdp_session *expected) +validate_session(xmlTextReaderPtr reader, struct parser_args *args) { struct rrdp_session actual = { 0 }; int error; @@ -380,15 +446,15 @@ validate_session(xmlTextReaderPtr reader, struct rrdp_session *expected) if (error) return error; - if (strcmp(expected->session_id, actual.session_id) != 0) { + if (strcmp(args->session->session_id, actual.session_id) != 0) { error = pr_val_err("File session id [%s] doesn't match notification's session id [%s]", - expected->session_id, actual.session_id); + args->session->session_id, actual.session_id); goto end; } - if (BN_cmp(actual.serial.num, expected->serial.num) != 0) { + if (BN_cmp(actual.serial.num, args->session->serial.num) != 0) { error = pr_val_err("File serial [%s] doesn't match notification's serial [%s]", - actual.serial.str, expected->serial.str); + actual.serial.str, args->session->serial.str); goto end; } @@ -469,64 +535,100 @@ parse_withdraw(xmlTextReaderPtr reader, struct withdraw *tag) return 0; } -/* Remove a local file and its directory tree (if empty) */ -static int -delete_file(char const *path) +static char * +create_path(struct rrdp_state *state) { - /* Delete parent dirs only if empty. */ - return delete_dir_recursive_bottom_up(path); + char *path; + int len; + + do { + path = pmalloc(state->pathlen); + + len = snprintf(path, state->pathlen, "%s/%X", + state->repo, state->next_id); + if (len < 0) { + pr_val_err("Cannot compute new cache path: Unknown cause."); + return NULL; + } + if (len < state->pathlen) { + state->next_id++; + return path; /* Happy path */ + } + + state->pathlen++; + free(path); + } while (true); } static int -handle_publish(xmlTextReaderPtr reader, struct cache_node *notif) +handle_publish(xmlTextReaderPtr reader, struct parser_args *args) { struct publish tag = { 0 }; - struct cache_node *subtree, *node; + struct cache_file *file; + size_t len; int error; error = parse_publish(reader, &tag); if (error) goto end; - if (!notif->rrdp.subtree) { - subtree = pzalloc(sizeof(struct cache_node)); - subtree->url = "rsync://"; - subtree->path = notif->path; - subtree->name = path_filename(subtree->path); - subtree->tmppath = notif->tmppath; - notif->rrdp.subtree = subtree; - } + pr_val_debug("Publish %s", logv_filename(tag.meta.uri)); - node = cachent_provide(notif->rrdp.subtree, tag.meta.uri); - if (!node) { - // XXX outdated msg - error = pr_val_err("Broken RRDP: is attempting to create file '%s' outside of its publication point '%s'.", - tag.meta.uri, notif->url); - goto end; - } + len = strlen(tag.meta.uri); + file = state_find_file(args->state, tag.meta.uri, len); /* rfc8181#section-2.2 */ - if (node->flags & CNF_CACHED) { + if (file) { if (tag.meta.hash == NULL) { // XXX watch out for this in the log before release - error = pr_val_err("RRDP desync: is attempting to create '%s', but the file is already cached.", + error = pr_val_err("RRDP desync: " + " is attempting to create '%s', " + "but the file is already cached.", tag.meta.uri); goto end; } - error = validate_hash(&tag.meta, node->path); + error = validate_hash(&tag.meta, file->map.path); if (error) goto end; - } else if (tag.meta.hash != NULL) { - // XXX watch out for this in the log before release - error = pr_val_err("RRDP desync: is attempting to overwrite '%s', but the file is absent in the cache.", - tag.meta.uri); - goto end; + /* + * Reminder: This is needed because the file might be + * hard-linked. Our repo file write should not propagate + * to the fallback. + */ + if (remove(file->map.path) < 0) { + error = errno; + pr_val_err("Cannot delete %s: %s", + file->map.path, strerror(error)); + if (error != ENOENT) + goto end; + } + + } else { + if (tag.meta.hash != NULL) { + // XXX watch out for this in the log before release + error = pr_val_err("RRDP desync: " + " is attempting to overwrite '%s', " + "but the file is absent in the cache.", + tag.meta.uri); + goto end; + } + + file = pzalloc(sizeof(struct cache_file)); + file->map.url = pstrdup(tag.meta.uri); + file->map.path = create_path(args->state); + if (!file->map.path) { + free(file->map.url); + free(file); + error = -EINVAL; + goto end; + } + + HASH_ADD_KEYPTR(hh, args->state->files, file->map.url, len, file); } - pr_val_debug("Publish %s", logv_filename(node->tmppath)); - error = file_write_full(node->tmppath, tag.content, tag.content_len); + error = file_write_full(file->map.path, tag.content, tag.content_len); end: metadata_cleanup(&tag.meta); free(tag.content); @@ -534,41 +636,43 @@ end: metadata_cleanup(&tag.meta); } static int -handle_withdraw(xmlTextReaderPtr reader, struct cache_node *notif) +handle_withdraw(xmlTextReaderPtr reader, struct parser_args *args) { struct withdraw tag = { 0 }; - struct cache_node *node; + struct cache_file *file; + size_t len; int error; error = parse_withdraw(reader, &tag); if (error) goto end; - node = cachent_provide(notif->rrdp.subtree, tag.meta.uri); - if (!node) { - // XXX outdated msg - error = pr_val_err("Broken RRDP: is attempting to delete file '%s' outside of its publication point '%s'.", - tag.meta.uri, notif->url); - goto end; - } + pr_val_debug("Withdraw %s", logv_filename(tag.meta.uri)); - /* - * XXX CNF_CACHED's comment suggests I should check parents, - * but this is not rsync. - */ - if (!(node->flags & CNF_CACHED)) { - /* XXX May want to query the actualy filesystem, to be sure */ - error = pr_val_err("RRDP desync: is attempting to delete file '%s', but it doesn't appear to exist.", + len = strlen(tag.meta.uri); + file = state_find_file(args->state, tag.meta.uri, len); + + if (!file) { + error = pr_val_err("Broken RRDP: " + " is attempting to delete unknown file '%s'.", tag.meta.uri); goto end; } - error = validate_hash(&tag.meta, node->path); + error = validate_hash(&tag.meta, file->map.path); if (error) goto end; - node->flags |= CNF_WITHDRAWN; - pr_val_debug("Withdraw %s", logv_filename(tag.meta.uri)); + if (remove(file->map.path) < 0) { + pr_val_warn("Cannot delete %s: %s", file->map.path, + strerror(errno)); + /* It's fine; keep going. */ + } + + HASH_DEL(args->state->files, file); + free(file->map.url); + free(file->map.path); + free(file); end: metadata_cleanup(&tag.meta); return error; @@ -765,7 +869,6 @@ parse_notification(char const *url, char const *path, static int xml_read_snapshot(xmlTextReaderPtr reader, void *arg) { - struct parser_args *args = arg; xmlReaderTypes type; xmlChar const *name; int error; @@ -775,9 +878,9 @@ xml_read_snapshot(xmlTextReaderPtr reader, void *arg) switch (type) { case XML_READER_TYPE_ELEMENT: if (xmlStrEqual(name, BAD_CAST RRDP_ELEM_PUBLISH)) - error = handle_publish(reader, args->notif); + error = handle_publish(reader, arg); else if (xmlStrEqual(name, BAD_CAST RRDP_ELEM_SNAPSHOT)) - error = validate_session(reader, args->session); + error = validate_session(reader, arg); else return pr_val_err("Unexpected '%s' element", name); if (error) @@ -792,14 +895,14 @@ xml_read_snapshot(xmlTextReaderPtr reader, void *arg) static int parse_snapshot(struct rrdp_session *session, char const *path, - struct cache_node *notif) + struct rrdp_state *state) { - struct parser_args args = { .session = session, .notif = notif }; + struct parser_args args = { .session = session, .state = state }; return relax_ng_parse(path, xml_read_snapshot, &args); } static int -validate_session_desync(struct cachefile_notification *old_notif, +validate_session_desync(struct rrdp_state *old_notif, struct update_notification *new_notif) { struct rrdp_hash *old_delta; @@ -851,7 +954,7 @@ dl_tmp(char const *url, char **path) } static int -handle_snapshot(struct update_notification *new, struct cache_node *notif) +handle_snapshot(struct update_notification *new, struct rrdp_state *state) { char *tmppath; int error; @@ -865,7 +968,7 @@ handle_snapshot(struct update_notification *new, struct cache_node *notif) error = validate_hash(&new->snapshot, tmppath); if (error) goto end2; - error = parse_snapshot(&new->session, tmppath, notif); + error = parse_snapshot(&new->session, tmppath, state); // delete_file(tmppath); XXX end2: free(tmppath); @@ -876,7 +979,6 @@ end1: fnstack_pop(); static int xml_read_delta(xmlTextReaderPtr reader, void *arg) { - struct parser_args *args = arg; xmlReaderTypes type; xmlChar const *name; int error; @@ -886,11 +988,11 @@ xml_read_delta(xmlTextReaderPtr reader, void *arg) switch (type) { case XML_READER_TYPE_ELEMENT: if (xmlStrEqual(name, BAD_CAST RRDP_ELEM_PUBLISH)) - error = handle_publish(reader, args->notif); + error = handle_publish(reader, arg); else if (xmlStrEqual(name, BAD_CAST RRDP_ELEM_WITHDRAW)) - error = handle_withdraw(reader, args->notif); + error = handle_withdraw(reader, arg); else if (xmlStrEqual(name, BAD_CAST RRDP_ELEM_DELTA)) - error = validate_session(reader, args->session); + error = validate_session(reader, arg); else return pr_val_err("Unexpected '%s' element", name); if (error) @@ -905,7 +1007,7 @@ xml_read_delta(xmlTextReaderPtr reader, void *arg) static int parse_delta(struct update_notification *notif, struct notification_delta *delta, - char const *path, struct cache_node *notif_node) + char const *path, struct rrdp_state *state) { struct parser_args args; struct rrdp_session session; @@ -918,14 +1020,14 @@ parse_delta(struct update_notification *notif, struct notification_delta *delta, session.session_id = notif->session.session_id; session.serial = delta->serial; args.session = &session; - args.notif = notif_node; + args.state = state; return relax_ng_parse(path, xml_read_delta, &args); } static int handle_delta(struct update_notification *notif, - struct notification_delta *delta, struct cache_node *notif_node) + struct notification_delta *delta, struct rrdp_state *state) { char *tmppath; int error; @@ -936,7 +1038,7 @@ handle_delta(struct update_notification *notif, error = dl_tmp(delta->meta.uri, &tmppath); if (error) goto end; - error = parse_delta(notif, delta, tmppath, notif_node); + error = parse_delta(notif, delta, tmppath, state); // delete_file(tmppath); XXX free(tmppath); @@ -945,7 +1047,7 @@ end: fnstack_pop(); } static int -handle_deltas(struct update_notification *notif, struct cache_node *notif_node) +handle_deltas(struct update_notification *notif, struct rrdp_state *state) { struct rrdp_serial *old; struct rrdp_serial *new; @@ -959,7 +1061,7 @@ handle_deltas(struct update_notification *notif, struct cache_node *notif_node) return -ENOENT; } - old = ¬if_node->rrdp.session.serial; + old = &state->session.serial; new = ¬if->session.serial; pr_val_debug("Handling RRDP delta serials %s-%s.", old->str, new->str); @@ -982,7 +1084,7 @@ handle_deltas(struct update_notification *notif, struct cache_node *notif_node) old->str, new->str); for (d = notif->deltas.len - diff; d < notif->deltas.len; d++) { - error = handle_delta(notif, ¬if->deltas.array[d], notif_node); + error = handle_delta(notif, ¬if->deltas.array[d], state); if (error) return error; } @@ -995,7 +1097,7 @@ handle_deltas(struct update_notification *notif, struct cache_node *notif_node) * Consumes @new. */ static void -init_notif(struct cachefile_notification *old, struct update_notification *new) +init_notif(struct rrdp_state *old, struct update_notification *new) { size_t dn; size_t i; @@ -1018,14 +1120,14 @@ init_notif(struct cachefile_notification *old, struct update_notification *new) } static void -drop_notif(struct cachefile_notification *notif) +drop_notif(struct rrdp_state *state) { struct rrdp_hash *hash; - session_cleanup(¬if->session); - while (!STAILQ_EMPTY(¬if->delta_hashes)) { - hash = STAILQ_FIRST(¬if->delta_hashes); - STAILQ_REMOVE_HEAD(¬if->delta_hashes, hook); + session_cleanup(&state->session); + while (!STAILQ_EMPTY(&state->delta_hashes)) { + hash = STAILQ_FIRST(&state->delta_hashes); + STAILQ_REMOVE_HEAD(&state->delta_hashes, hook); free(hash); } } @@ -1035,7 +1137,7 @@ drop_notif(struct cachefile_notification *notif) * Consumes @new on success. */ static int -update_notif(struct cachefile_notification *old, struct update_notification *new) +update_notif(struct rrdp_state *old, struct update_notification *new) { BIGNUM *diff_bn; BN_ULONG diff; /* difference between the old and new serials */ @@ -1083,100 +1185,102 @@ update_notif(struct cachefile_notification *old, struct update_notification *new return 0; } -static bool -dl_notif(struct cache_node *notif, struct update_notification *new) +static int +dl_notif(struct cache_mapping *map, time_t mtim, bool *changed, + struct update_notification *new) { char *tmppath; - time_t mtim; - bool changed; - - notif->dlerr = cache_tmpfile(&tmppath); - if (notif->dlerr) - return false; + int error; - mtim = time_nonfatal(); - changed = false; - notif->dlerr = http_download(notif->url, tmppath, notif->mtim, &changed); - notif->flags |= CNF_FRESH; + error = cache_tmpfile(&tmppath); + if (error) + return error; - if (notif->dlerr) + *changed = false; + error = http_download(map->url, tmppath, mtim, changed); + if (error) goto end; - if (!changed) { + if (!(*changed)) { pr_val_debug("The Notification has not changed."); goto end; } - notif->mtim = mtim; /* XXX should happen much later */ - notif->dlerr = parse_notification(notif->url, tmppath, new); - if (notif->dlerr) + error = parse_notification(map->url, tmppath, new); + if (error) goto end; if (remove(tmppath) < 0) { - /* - * Note, this could be ignored if we weren't planning on reusing - * the path. This is going to stop being an issue once streaming - * is implemented. - */ - notif->dlerr = errno; - pr_val_err("Can't remove notification's temporal file: %s", - strerror(notif->dlerr)); + pr_val_warn("Can't remove notification's temporal file: %s", + strerror(errno)); update_notification_cleanup(new); - goto end; + /* Nonfatal; fall through */ } - notif->flags |= CNF_FREE_TMPPATH; - notif->tmppath = tmppath; - return true; - end: free(tmppath); - return false; + return error; } /* - * Downloads the Update Notification @notif, and updates the cache accordingly. + * Downloads the Update Notification @notif->url, and updates the cache + * accordingly. * * "Updates the cache accordingly" means it downloads the missing deltas or - * snapshot, and explodes them into @notif's tmp directory. + * snapshot, and explodes them into @notif->path. */ int -rrdp_update(struct cache_node *notif) +rrdp_update(struct cache_mapping *notif, time_t mtim, bool *changed, + struct rrdp_state **state) { - struct cachefile_notification *old; + struct rrdp_state *old; struct update_notification new; int serial_cmp; + int error; fnstack_push(notif->url); pr_val_debug("Processing notification."); - if (!dl_notif(notif, &new)) - goto end; /* Unchanged or error */ + error = dl_notif(notif, mtim, changed, &new); + if (error) + goto end; + if (!(*changed)) + goto end; pr_val_debug("New session/serial: %s/%s", new.session.session_id, new.session.serial.str); - if (!(notif->flags & CNF_NOTIFICATION)) { + if ((*state) == NULL) { pr_val_debug("This is a new Notification."); - notif->dlerr = handle_snapshot(&new, notif); - if (notif->dlerr) + + old = pzalloc(sizeof(struct rrdp_state)); + old->repo = notif->path; + /* session postponed! */ + old->pathlen = strlen(old->repo) + 5; + STAILQ_INIT(&old->delta_hashes); + + error = handle_snapshot(&new, old); + if (error) { + state_flush_files(old); + free(old); goto clean_notif; + } - notif->flags |= CNF_NOTIFICATION; - init_notif(¬if->rrdp, &new); + init_notif(old, &new); + *state = old; goto end; } - old = ¬if->rrdp; + old = *state; serial_cmp = BN_cmp(old->session.serial.num, new.session.serial.num); if (serial_cmp < 0) { pr_val_debug("The Notification's serial changed."); - notif->dlerr = validate_session_desync(old, &new); - if (notif->dlerr) + error = validate_session_desync(old, &new); + if (error) goto snapshot_fallback; - notif->dlerr = handle_deltas(&new, notif); - if (notif->dlerr) + error = handle_deltas(&new, old); + if (error) goto snapshot_fallback; - notif->dlerr = update_notif(old, &new); - if (!notif->dlerr) + error = update_notif(old, &new); + if (!error) goto end; /* * The files are exploded and usable, but @cached is not @@ -1199,8 +1303,8 @@ rrdp_update(struct cache_node *notif) snapshot_fallback: pr_val_debug("Falling back to snapshot."); - notif->dlerr = handle_snapshot(&new, notif); - if (notif->dlerr) + error = handle_snapshot(&new, old); + if (error) goto clean_notif; reset_notif: @@ -1212,7 +1316,15 @@ clean_notif: update_notification_cleanup(&new); end: fnstack_pop(); - return notif->dlerr; + return error; +} + +char const * +rrdp_file(struct rrdp_state *state, char const *url) +{ + struct cache_file *file; + file = state_find_file(state, url, strlen(url)); + return file ? file->map.path : NULL; } #define TAGNAME_SESSION "session_id" @@ -1228,7 +1340,7 @@ hash_b2c(unsigned char bin) } json_t * -rrdp_notif2json(struct cachefile_notification *notif) +rrdp_state2json(struct rrdp_state *state) { json_t *json; json_t *deltas; @@ -1236,19 +1348,19 @@ rrdp_notif2json(struct cachefile_notification *notif) struct rrdp_hash *hash; size_t i; - if (notif == NULL) + if (state == NULL) return NULL; json = json_object(); if (json == NULL) enomem_panic(); - if (json_add_str(json, TAGNAME_SESSION, notif->session.session_id)) + if (json_add_str(json, TAGNAME_SESSION, state->session.session_id)) goto fail; - if (json_add_str(json, TAGNAME_SERIAL, notif->session.serial.str)) + if (json_add_str(json, TAGNAME_SERIAL, state->session.serial.str)) goto fail; - if (STAILQ_EMPTY(¬if->delta_hashes)) + if (STAILQ_EMPTY(&state->delta_hashes)) return json; /* Happy path, but unlikely. */ deltas = json_array(); @@ -1258,7 +1370,7 @@ rrdp_notif2json(struct cachefile_notification *notif) goto fail; hash_str[2 * RRDP_HASH_LEN] = '\0'; - STAILQ_FOREACH(hash, ¬if->delta_hashes, hook) { + STAILQ_FOREACH(hash, &state->delta_hashes, hook) { for (i = 0; i < RRDP_HASH_LEN; i++) { hash_str[2 * i ] = hash_b2c(hash->bytes[i] >> 4); hash_str[2 * i + 1] = hash_b2c(hash->bytes[i] ); @@ -1324,29 +1436,29 @@ bad_char: } static void -clear_delta_hashes(struct cachefile_notification *notif) +clear_delta_hashes(struct rrdp_state *state) { struct rrdp_hash *hash; - while (!STAILQ_EMPTY(¬if->delta_hashes)) { - hash = STAILQ_FIRST(¬if->delta_hashes); - STAILQ_REMOVE_HEAD(¬if->delta_hashes, hook); + while (!STAILQ_EMPTY(&state->delta_hashes)) { + hash = STAILQ_FIRST(&state->delta_hashes); + STAILQ_REMOVE_HEAD(&state->delta_hashes, hook); free(hash); } } int -rrdp_json2notif(json_t *json, struct cachefile_notification **result) +rrdp_json2state(json_t *json, struct rrdp_state **result) { - struct cachefile_notification *notif; + struct rrdp_state *state; char const *str; json_t *jdeltas; size_t d, dn; struct rrdp_hash *hash; int error; - notif = pzalloc(sizeof(struct cachefile_notification)); - STAILQ_INIT(¬if->delta_hashes); + state = pzalloc(sizeof(struct rrdp_state)); + STAILQ_INIT(&state->delta_hashes); error = json_get_str(json, TAGNAME_SESSION, &str); if (error) { @@ -1354,7 +1466,7 @@ rrdp_json2notif(json_t *json, struct cachefile_notification **result) pr_op_err("Node is missing the '" TAGNAME_SESSION "' tag."); goto revert_notif; } - notif->session.session_id = pstrdup(str); + state->session.session_id = pstrdup(str); error = json_get_str(json, TAGNAME_SERIAL, &str); if (error) { @@ -1362,11 +1474,11 @@ rrdp_json2notif(json_t *json, struct cachefile_notification **result) pr_op_err("Node is missing the '" TAGNAME_SERIAL "' tag."); goto revert_session; } - notif->session.serial.str = pstrdup(str); + state->session.serial.str = pstrdup(str); - notif->session.serial.num = BN_create(); - if (!BN_dec2bn(¬if->session.serial.num, notif->session.serial.str)) { - error = pr_op_err("Not a serial number: %s", notif->session.serial.str); + state->session.serial.num = BN_create(); + if (!BN_dec2bn(&state->session.serial.num, state->session.serial.str)) { + error = pr_op_err("Not a serial number: %s", state->session.serial.str); goto revert_serial; } @@ -1387,38 +1499,38 @@ rrdp_json2notif(json_t *json, struct cachefile_notification **result) error = json2dh(json_array_get(jdeltas, d), &hash); if (error) goto revert_deltas; - STAILQ_INSERT_TAIL(¬if->delta_hashes, hash, hook); + STAILQ_INSERT_TAIL(&state->delta_hashes, hash, hook); } success: - *result = notif; + *result = state; return 0; revert_deltas: - clear_delta_hashes(notif); + clear_delta_hashes(state); revert_serial: - BN_free(notif->session.serial.num); - free(notif->session.serial.str); + BN_free(state->session.serial.num); + free(state->session.serial.str); revert_session: - free(notif->session.session_id); + free(state->session.session_id); revert_notif: - free(notif); + free(state); return error; } void -rrdp_notif_cleanup(struct cachefile_notification *notif) +rrdp_state_cleanup(struct rrdp_state *state) { - session_cleanup(¬if->session); - cachent_delete(notif->subtree); - clear_delta_hashes(notif); + session_cleanup(&state->session); + state_flush_files(state); + clear_delta_hashes(state); } void -rrdp_notif_free(struct cachefile_notification *notif) +rrdp_state_free(struct rrdp_state *state) { - if (notif != NULL) { - rrdp_notif_cleanup(notif); - free(notif); + if (state != NULL) { + rrdp_state_cleanup(state); + free(state); } } diff --git a/src/rrdp.h b/src/rrdp.h index 984811a2..6c4663bd 100644 --- a/src/rrdp.h +++ b/src/rrdp.h @@ -2,51 +2,20 @@ #define SRC_RRDP_H_ #include -#include -#include -#include +#include +#include -struct cache_node; +#include "types/map.h" -/* These are supposed to be unbounded */ -struct rrdp_serial { - BIGNUM *num; - char *str; /* String version of @num. */ -}; +struct rrdp_state; -struct rrdp_session { - char *session_id; - struct rrdp_serial serial; -}; +int rrdp_update(struct cache_mapping *, time_t, bool *, struct rrdp_state **); +char const *rrdp_file(struct rrdp_state *, char const *); -#define RRDP_HASH_LEN SHA256_DIGEST_LENGTH +json_t *rrdp_state2json(struct rrdp_state *); +int rrdp_json2state(json_t *, struct rrdp_state **); -struct rrdp_hash { - unsigned char bytes[RRDP_HASH_LEN]; - STAILQ_ENTRY(rrdp_hash) hook; -}; - -/* - * Subset of the notification that is relevant to the TAL's cachefile. - */ -struct cachefile_notification { - struct rrdp_session session; - struct cache_node *subtree; - /* - * The 1st one contains the hash of the session.serial delta. - * The 2nd one contains the hash of the session.serial - 1 delta. - * The 3rd one contains the hash of the session.serial - 2 delta. - * And so on. - */ - STAILQ_HEAD(, rrdp_hash) delta_hashes; -}; - -int rrdp_update(struct cache_node *); - -json_t *rrdp_notif2json(struct cachefile_notification *); -int rrdp_json2notif(json_t *, struct cachefile_notification **); - -void rrdp_notif_cleanup(struct cachefile_notification *); -void rrdp_notif_free(struct cachefile_notification *); +void rrdp_state_cleanup(struct rrdp_state *); +void rrdp_state_free(struct rrdp_state *); #endif /* SRC_RRDP_H_ */ diff --git a/src/rsync.c b/src/rsync.c index 36aa596b..ec594181 100644 --- a/src/rsync.c +++ b/src/rsync.c @@ -38,13 +38,13 @@ duplicate_fds(int fds[2][2]) } static void -prepare_rsync(char **args, char const *src, char const *dst, char const *cmpdst) +prepare_rsync(char **args, struct cache_mapping *map) { size_t i = 0; /* - * execvp() is not going to tweak those strings; - * stop angsting over those casts. + * execvp() is not going to tweak these strings; + * stop angsting over the const-to-raw conversion. */ /* XXX review */ @@ -69,13 +69,9 @@ prepare_rsync(char **args, char const *src, char const *dst, char const *cmpdst) args[i++] = "--include=*.mft"; args[i++] = "--include=*.roa"; args[i++] = "--exclude=*"; - if (cmpdst) { - args[i++] = "--compare-dest"; - args[i++] = (char *)cmpdst; - } #endif - args[i++] = (char *)src; - args[i++] = (char *)dst; + args[i++] = map->url; + args[i++] = map->path; args[i++] = NULL; } @@ -276,9 +272,9 @@ exhaust_pipes(int fds[2][2]) return exhaust_read_fds(STDERR_READ(fds), STDOUT_READ(fds)); } -/* rsync [--compare-dest @cmpdst] @src @dst */ +/* rsync @src @dst */ int -rsync_download(char const *src, char const *dst, char const *cmpdst) +rsync_download(struct cache_mapping *map) { char *args[32]; /* Descriptors to pipe stderr (first element) and stdout (second) */ @@ -290,16 +286,16 @@ rsync_download(char const *src, char const *dst, char const *cmpdst) int error; /* Prepare everything for the child exec */ - prepare_rsync(args, src, dst, cmpdst); + prepare_rsync(args, map); - pr_val_info("rsync: %s", src); + pr_val_info("rsync: %s -> %s", map->url, map->path); if (log_val_enabled(LOG_DEBUG)) { pr_val_debug("Executing rsync:"); for (i = 0; args[i] != NULL; i++) pr_val_debug(" %s", args[i]); } - error = mkdir_p(dst, true); + error = mkdir_p(map->path, true); if (error) return error; @@ -369,11 +365,11 @@ rsync_download(char const *src, char const *dst, char const *cmpdst) if (retries == config_get_rsync_retry_count()) { if (retries > 0) pr_val_warn("Max RSYNC retries (%u) reached on '%s', won't retry again.", - retries, src); + retries, map->url); return EIO; } pr_val_warn("Retrying RSYNC '%s' in %u seconds, %u attempts remaining.", - src, + map->url, config_get_rsync_retry_interval(), config_get_rsync_retry_count() - retries); retries++; diff --git a/src/rsync.h b/src/rsync.h index 52781eed..beb96bed 100644 --- a/src/rsync.h +++ b/src/rsync.h @@ -1,6 +1,8 @@ #ifndef SRC_RSYNC_RSYNC_H_ #define SRC_RSYNC_RSYNC_H_ -int rsync_download(char const *, char const *, char const *); +#include "types/map.h" + +int rsync_download(struct cache_mapping *); #endif /* SRC_RSYNC_RSYNC_H_ */ diff --git a/src/state.c b/src/state.c index 62099e65..ed4d998f 100644 --- a/src/state.c +++ b/src/state.c @@ -5,11 +5,10 @@ #include "thread_var.h" /** - * The current state of the validation cycle. + * Just a bunch of thread-specific variables that are too much of a pain + * to keep passing around. * - * It is one of the core objects in this project. Every time a trust anchor - * triggers a validation cycle, the validator creates one of these objects and - * uses it to traverse the tree and keep track of validated data. + * Should be refactored away, honestly. */ struct validation { struct tal *tal; @@ -20,11 +19,6 @@ struct validation { X509_VERIFY_PARAM *params; } x509_data; - struct cert_stack *certstack; - - /* Did the TAL's public key match the root certificate's public key? */ - enum pubkey_state pubkey_state; - /** * Two buffers calling code will store stringified IP addresses in, * to prevent proliferation of similar buffers on the stack. @@ -107,20 +101,12 @@ validation_prepare(struct validation **out, struct tal *tal, X509_STORE_set1_param(result->x509_data.store, params); X509_STORE_set_verify_cb(result->x509_data.store, cb); - error = certstack_create(&result->certstack); - if (error) - goto undo_crypto; - - result->pubkey_state = PKS_UNTESTED; result->validation_handler = *validation_handler; result->x509_data.params = params; /* Ownership transfered */ *out = result; return 0; -undo_crypto: - X509_VERIFY_PARAM_free(params); - X509_STORE_free(result->x509_data.store); undo_result: free(result); return error; @@ -131,7 +117,6 @@ validation_destroy(struct validation *state) { X509_VERIFY_PARAM_free(state->x509_data.params); X509_STORE_free(state->x509_data.store); - certstack_destroy(state->certstack); free(state); } @@ -147,30 +132,6 @@ validation_store(struct validation *state) return state->x509_data.store; } -struct cert_stack * -validation_certstack(struct validation *state) -{ - return state->certstack; -} - -void -validation_pubkey_valid(struct validation *state) -{ - state->pubkey_state = PKS_VALID; -} - -void -validation_pubkey_invalid(struct validation *state) -{ - state->pubkey_state = PKS_INVALID; -} - -enum pubkey_state -validation_pubkey_state(struct validation *state) -{ - return state->pubkey_state; -} - char * validation_get_ip_buffer1(struct validation *state) { diff --git a/src/state.h b/src/state.h index e0095239..d168a715 100644 --- a/src/state.h +++ b/src/state.h @@ -1,7 +1,8 @@ #ifndef SRC_STATE_H_ #define SRC_STATE_H_ -#include "cert_stack.h" +#include + #include "object/tal.h" #include "validation_handler.h" @@ -13,17 +14,9 @@ void validation_destroy(struct validation *); struct tal *validation_tal(struct validation *); X509_STORE *validation_store(struct validation *); -struct cert_stack *validation_certstack(struct validation *); - -enum pubkey_state { - PKS_VALID, - PKS_INVALID, - PKS_UNTESTED, -}; void validation_pubkey_valid(struct validation *); void validation_pubkey_invalid(struct validation *); -enum pubkey_state validation_pubkey_state(struct validation *); char *validation_get_ip_buffer1(struct validation *); char *validation_get_ip_buffer2(struct validation *); diff --git a/src/thread_var.h b/src/thread_var.h index d2b0c72a..23bca5ae 100644 --- a/src/thread_var.h +++ b/src/thread_var.h @@ -2,6 +2,7 @@ #define SRC_THREAD_VAR_H_ #include "state.h" +#include "types/map.h" int thvar_init(void); /* This function does not need cleanup. */ diff --git a/src/types/map.c b/src/types/map.c index 7377f402..4eddaa56 100644 --- a/src/types/map.c +++ b/src/types/map.c @@ -33,25 +33,6 @@ map_op_get_printable(struct cache_mapping const *map) return map_get_printable(map, config_get_op_log_file_format()); } -void -map_parent(struct cache_mapping const *child, struct cache_mapping *parent) -{ - parent->url = path_parent(child->url); - parent->path = path_parent(child->path); -} - -struct cache_mapping * -map_child(struct cache_mapping const *parent, char const *name) -{ - struct cache_mapping *child; - - child = pmalloc(sizeof(struct cache_mapping)); - child->url = join_paths(parent->url, name); - child->path = join_paths(parent->path, name); - - return child; -} - void map_copy(struct cache_mapping *dst, struct cache_mapping const *src) { diff --git a/src/types/map.h b/src/types/map.h index 3ecc56fa..c7d9a4e6 100644 --- a/src/types/map.h +++ b/src/types/map.h @@ -12,9 +12,6 @@ struct cache_mapping { char const *map_val_get_printable(struct cache_mapping const *); char const *map_op_get_printable(struct cache_mapping const *); -void map_parent(struct cache_mapping const *, struct cache_mapping *); -struct cache_mapping *map_child(struct cache_mapping const *, char const *); - void map_copy(struct cache_mapping *, struct cache_mapping const *); void map_cleanup(struct cache_mapping *); diff --git a/src/types/name.c b/src/types/name.c index 0a128f7b..556b553b 100644 --- a/src/types/name.c +++ b/src/types/name.c @@ -140,10 +140,8 @@ x509_name_equals(struct rfc5280_name *a, struct rfc5280_name *b) } int -validate_issuer_name(char const *container, X509_NAME *issuer) +validate_issuer_name(X509_NAME *issuer, X509 *parent) { - struct validation *state; - X509 *parent; struct rfc5280_name *parent_subject; struct rfc5280_name *child_issuer; int error; @@ -155,13 +153,6 @@ validate_issuer_name(char const *container, X509_NAME *issuer) * But let's check it anyway. */ - state = state_retrieve(); - parent = x509stack_peek(validation_certstack(state)); - if (parent == NULL) { - return pr_val_err("%s appears to have no parent certificate.", - container); - } - error = x509_name_decode(X509_get_subject_name(parent), "subject", &parent_subject); if (error) @@ -178,8 +169,7 @@ validate_issuer_name(char const *container, X509_NAME *issuer) parent_serial = x509_name_serialNumber(parent_subject); child_serial = x509_name_serialNumber(child_issuer); - error = pr_val_err("%s's issuer name ('%s%s%s') does not equal issuer certificate's name ('%s%s%s').", - container, + error = pr_val_err("Issuer name ('%s%s%s') does not equal issuer certificate's name ('%s%s%s').", x509_name_commonName(child_issuer), (child_serial != NULL) ? "/" : "", (child_serial != NULL) ? child_serial : "", diff --git a/src/types/name.h b/src/types/name.h index 3c38b234..ca2d84d0 100644 --- a/src/types/name.h +++ b/src/types/name.h @@ -20,7 +20,7 @@ bool x509_name_equals(struct rfc5280_name *, struct rfc5280_name *); /* X509_NAME utils */ -int validate_issuer_name(char const *, X509_NAME *); +int validate_issuer_name(X509_NAME *, X509 *); void x509_name_pr_debug(char const *, X509_NAME *); diff --git a/test/cache_util.c b/test/cache_util.c index 8bfb7a84..d521d134 100644 --- a/test/cache_util.c +++ b/test/cache_util.c @@ -4,12 +4,6 @@ #include #include "types/uthash.h" -static int -cnf_clean(int flags) -{ - return flags & ~(CNF_FREE_URL | CNF_FREE_PATH | CNF_FREE_TMPPATH); -} - void ck_assert_cachent_eq(struct cache_node *expected, struct cache_node *actual) { @@ -20,7 +14,7 @@ ck_assert_cachent_eq(struct cache_node *expected, struct cache_node *actual) ck_assert_str_eq(expected->url, actual->url); ck_assert_str_eq(expected->path, actual->path); ck_assert_str_eq(expected->name, actual->name); - ck_assert_int_eq(cnf_clean(expected->flags), cnf_clean(actual->flags)); + ck_assert_int_eq(expected->flags, actual->flags); if (expected->tmppath) ck_assert_str_eq(expected->tmppath, actual->tmppath); else diff --git a/test/cachent_test.c b/test/cachent_test.c index 3d371660..37ae4384 100644 --- a/test/cachent_test.c +++ b/test/cachent_test.c @@ -10,7 +10,7 @@ static char deleted[16][6]; static unsigned int dn; -MOCK_ABORT_VOID(rrdp_notif_cleanup, struct cachefile_notification *notif) +MOCK_ABORT_VOID(rrdp_state_cleanup, struct cachefile_notification *notif) static void __delete_node_cb(struct cache_node const *node) diff --git a/test/object/manifest_test.c b/test/object/manifest_test.c index a73b86f8..4ee88568 100644 --- a/test/object/manifest_test.c +++ b/test/object/manifest_test.c @@ -10,7 +10,7 @@ #include "types/path.c" #include "types/url.c" -MOCK_ABORT_VOID(rrdp_notif_cleanup, struct cachefile_notification *notif) +MOCK_ABORT_VOID(rrdp_state_cleanup, struct cachefile_notification *notif) MOCK_ABORT_INT(signed_object_decode, struct signed_object *sobj, char const *path) MOCK_ABORT_VOID(signed_object_cleanup, struct signed_object *sobj) MOCK_VOID(__delete_node_cb, struct cache_node const *node) @@ -32,7 +32,7 @@ __test_validate(char const *src, size_t len) dst.buf = buffer; dst.size = len; - return validate_mft_file(&dst); + return validate_mft_filename(&dst); } #define test_validate(str) __test_validate(str, sizeof(str) - 1) diff --git a/test/object/tal_test.c b/test/object/tal_test.c index bf11db66..e41b04ed 100644 --- a/test/object/tal_test.c +++ b/test/object/tal_test.c @@ -26,13 +26,10 @@ MOCK_ABORT_INT(handle_roa_v6, uint32_t as, struct ipv6_prefix const *prefix, uint8_t max_length, void *arg) MOCK_ABORT_INT(handle_router_key, unsigned char const *ski, struct asn_range const *asns, unsigned char const *spk, void *arg) -MOCK_ABORT_VOID(rpp_refput, struct rpp *pp) MOCK(state_retrieve, struct validation *, NULL, void) -MOCK_ABORT_PTR(validation_certstack, cert_stack, struct validation *state) MOCK_ABORT_VOID(validation_destroy, struct validation *state) MOCK_ABORT_INT(validation_prepare, struct validation **out, struct tal *tal, struct validation_handler *validation_handler) -MOCK_ABORT_ENUM(validation_pubkey_state, pubkey_state, struct validation *state) MOCK(validation_tal, struct tal *, NULL, struct validation *state) /* Tests */ diff --git a/test/rrdp_test.c b/test/rrdp_test.c index 4041892c..a20fb913 100644 --- a/test/rrdp_test.c +++ b/test/rrdp_test.c @@ -410,7 +410,7 @@ validate_cachefile_notif(struct cachefile_notification *notif, ck_assert_ptr_eq(NULL, hash); - rrdp_notif_free(notif); + rrdp_state_free(notif); } START_TEST(test_update_notif) @@ -632,10 +632,10 @@ START_TEST(test_2s_simple) notif = create_cachefile_notif("session", "1234", 0); - json = rrdp_notif2json(notif); + json = rrdp_state2json(notif); ck_assert_ptr_ne(NULL, json); - rrdp_notif_free(notif); + rrdp_state_free(notif); notif = NULL; ck_assert_int_eq(0, json_get_str(json, TAGNAME_SESSION, &str)); @@ -644,12 +644,12 @@ START_TEST(test_2s_simple) ck_assert_str_eq("1234", str); ck_assert_int_eq(ENOENT, json_get_array(json, TAGNAME_DELTAS, &jdeltas)); - ck_assert_int_eq(0, rrdp_json2notif(json, ¬if)); + ck_assert_int_eq(0, rrdp_json2state(json, ¬if)); ck_rrdp_session("session", "1234", ¬if->session); ck_assert_uint_eq(true, STAILQ_EMPTY(¬if->delta_hashes)); json_decref(json); - rrdp_notif_free(notif); + rrdp_state_free(notif); } END_TEST @@ -672,10 +672,10 @@ START_TEST(test_2s_more) "123456789012345678901234567890123456789012", 0xAA, 0xBB, 0xCD, 0); - json = rrdp_notif2json(notif); + json = rrdp_state2json(notif); ck_assert_ptr_ne(NULL, json); - rrdp_notif_free(notif); + rrdp_state_free(notif); notif = NULL; ck_assert_int_eq(0, json_get_str(json, TAGNAME_SESSION, &str)); @@ -691,7 +691,7 @@ START_TEST(test_2s_more) ck_assert_str_eq("cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", json_string_value(json_array_get(jdeltas, 2))); - ck_assert_int_eq(0, rrdp_json2notif(json, ¬if)); + ck_assert_int_eq(0, rrdp_json2state(json, ¬if)); ck_rrdp_session("session", "123456789012345678901234567890123456789012", ¬if->session); hash = STAILQ_FIRST(¬if->delta_hashes); ck_assert_ptr_ne(NULL, hash); @@ -706,12 +706,12 @@ START_TEST(test_2s_more) ck_assert_ptr_eq(NULL, hash); json_decref(json); - rrdp_notif_free(notif); + rrdp_state_free(notif); } END_TEST void -ck_json2notif(int expected, char const *json_str) +ck_json2state(int expected, char const *json_str) { json_t *json; json_error_t error; @@ -721,55 +721,55 @@ ck_json2notif(int expected, char const *json_str) ck_assert_ptr_ne(NULL, json); notif = NULL; - ck_assert_int_eq(expected, rrdp_json2notif(json, ¬if)); + ck_assert_int_eq(expected, rrdp_json2state(json, ¬if)); json_decref(json); if (notif == NULL) - rrdp_notif_free(notif); + rrdp_state_free(notif); } START_TEST(test_2s_errors) { struct cachefile_notification notif = { 0 }; - ck_assert_ptr_eq(NULL, rrdp_notif2json(NULL)); - ck_assert_ptr_eq(NULL, rrdp_notif2json(¬if)); + ck_assert_ptr_eq(NULL, rrdp_state2json(NULL)); + ck_assert_ptr_eq(NULL, rrdp_state2json(¬if)); notif.session.session_id = "sid"; - ck_assert_ptr_eq(NULL, rrdp_notif2json(¬if)); - - ck_json2notif(ENOENT, "{}"); - ck_json2notif(0, "{ \"" TAGNAME_SESSION "\":\"sss\", \"" TAGNAME_SERIAL "\":\"123\" }"); - ck_json2notif(-EINVAL, "{ \"" TAGNAME_SESSION "\":null, \"" TAGNAME_SERIAL "\":\"123\" }"); - ck_json2notif(-EINVAL, "{ \"" TAGNAME_SESSION "\":\"sss\", \"" TAGNAME_SERIAL "\":null }"); - ck_json2notif(-EINVAL, "{ \"" TAGNAME_SESSION "\":123, \"" TAGNAME_SERIAL "\":\"123\" }"); - ck_json2notif(-EINVAL, "{ \"" TAGNAME_SESSION "\":\"sss\", \"" TAGNAME_SERIAL "\":123 }"); - ck_json2notif(ENOENT, "{ \"" TAGNAME_SESSION "\":\"sss\" }"); - ck_json2notif(ENOENT, "{ \"" TAGNAME_SERIAL "\":\"123\" }"); - ck_json2notif(-EINVAL, + ck_assert_ptr_eq(NULL, rrdp_state2json(¬if)); + + ck_json2state(ENOENT, "{}"); + ck_json2state(0, "{ \"" TAGNAME_SESSION "\":\"sss\", \"" TAGNAME_SERIAL "\":\"123\" }"); + ck_json2state(-EINVAL, "{ \"" TAGNAME_SESSION "\":null, \"" TAGNAME_SERIAL "\":\"123\" }"); + ck_json2state(-EINVAL, "{ \"" TAGNAME_SESSION "\":\"sss\", \"" TAGNAME_SERIAL "\":null }"); + ck_json2state(-EINVAL, "{ \"" TAGNAME_SESSION "\":123, \"" TAGNAME_SERIAL "\":\"123\" }"); + ck_json2state(-EINVAL, "{ \"" TAGNAME_SESSION "\":\"sss\", \"" TAGNAME_SERIAL "\":123 }"); + ck_json2state(ENOENT, "{ \"" TAGNAME_SESSION "\":\"sss\" }"); + ck_json2state(ENOENT, "{ \"" TAGNAME_SERIAL "\":\"123\" }"); + ck_json2state(-EINVAL, "{ \"" TAGNAME_SESSION "\":\"sss\"," "\"" TAGNAME_SERIAL "\":\"123\"," "\"" TAGNAME_DELTAS "\":null }"); - ck_json2notif(-EINVAL, + ck_json2state(-EINVAL, "{ \"" TAGNAME_SESSION "\":\"sss\"," "\"" TAGNAME_SERIAL "\":\"123\"," "\"" TAGNAME_DELTAS "\":\"123\" }"); - ck_json2notif(-EINVAL, + ck_json2state(-EINVAL, "{ \"" TAGNAME_SESSION "\":\"sss\"," "\"" TAGNAME_SERIAL "\":\"123\"," "\"" TAGNAME_DELTAS "\":{} }"); - ck_json2notif(0, + ck_json2state(0, "{ \"" TAGNAME_SESSION "\":\"sss\"," "\"" TAGNAME_SERIAL "\":\"123\"," "\"" TAGNAME_DELTAS "\":[] }"); - ck_json2notif(-EINVAL, + ck_json2state(-EINVAL, "{ \"" TAGNAME_SESSION "\":\"sss\"," "\"" TAGNAME_SERIAL "\":\"123\"," "\"" TAGNAME_DELTAS "\":[ 1 ] }"); - ck_json2notif(-EINVAL, + ck_json2state(-EINVAL, "{ \"" TAGNAME_SESSION "\":\"sss\"," "\"" TAGNAME_SERIAL "\":\"123\"," "\"" TAGNAME_DELTAS "\":[ \"111\" ] }"); - ck_json2notif(0, + ck_json2state(0, "{ \"" TAGNAME_SESSION "\":\"sss\"," "\"" TAGNAME_SERIAL "\":\"123\"," "\"" TAGNAME_DELTAS "\":[ \"1111111111111111111111111111111111111111111111111111111111111111\" ] }"); -- 2.47.3