Testing and course correcting, week 3. Unstable.
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
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
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)
{
}
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;
/*
*/
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 */
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;
/* 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 **);
-/*
- * - 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 <ftw.h>
+#include <stdbool.h>
+#include <sys/stat.h>
-#include "cachent.h"
+#include "alloc.h"
#include "cachetmp.h"
#include "common.h"
#include "config.h"
#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"
#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)
{
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)
{
int
cache_setup(void)
{
+ init_tables();
init_cache_metafile();
init_tmp_dir();
init_cachedir_tag();
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;
// 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
// 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");
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);
}
/*
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();
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
{
cleanup_cache();
write_tal_json();
- cachent_delete(cache.rsync);
- cachent_delete(cache.https);
+ cache_foreach(delete_node);
}
void
free(sias->caRepository);
free(sias->rpkiNotify);
free(sias->rpkiManifest);
+ free(sias->crldp);
+ free(sias->caIssuers);
+ free(sias->signedObject);
}
#ifndef SRC_CACHE_LOCAL_CACHE_H_
#define SRC_CACHE_LOCAL_CACHE_H_
+#include <stdbool.h>
#include "types/map.h"
int cache_setup(void); /* Init 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_ */
+++ /dev/null
-#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);
-}
+++ /dev/null
-#ifndef SRC_CACHE_CACHENT_H_
-#define SRC_CACHE_CACHENT_H_
-
-/* CACHE ENTity, CACHE elemENT, CACHE componENT */
-
-#include <stdbool.h>
-#include <sys/stat.h>
-
-#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_ */
+++ /dev/null
-#include "cert_stack.h"
-
-#include <errno.h>
-#include <sys/queue.h>
-
-#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);
-}
+++ /dev/null
-#ifndef SRC_CERT_STACK_H_
-#define SRC_CERT_STACK_H_
-
-#include <openssl/bn.h>
-
-#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_ */
#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);
}
#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_ */
* 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)
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;
}
"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
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,
};
#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"
*/
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;
.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)
{
}
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;
}
* @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;
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.");
}
/*
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.");
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;
}
/*
}
int
-certificate_validate_rfc6487(X509 *cert, enum cert_type type)
+certificate_validate_rfc6487(struct rpki_certificate *cert)
{
int error;
*/
/* 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;
+ /* <Redacted> */
/* 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;
* "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;
/* 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;
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
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) {
}
/* 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.
* 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;
}
}
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;
}
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);
}
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;
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;
/* 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) {
ip_ext_found = true;
- error = handle_ip_extension(ext, resources);
+ error = handle_ip_extension(cert, ext);
if (error)
return error;
asn_ext_found = true;
- error = handle_asn_extension(ext, resources,
- allow_asn_inherit);
+ error = handle_asn_extension(cert, ext);
if (error)
return error;
/* 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
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
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;
* So we will store the URI in @refs, and validate it
* later.
*/
- return ia5s2string(str, &refs->crldp);
+ return ia5s2string(str, &sias->crldp);
}
}
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
/* 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));
}
/*
* 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) */
}
* 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.
}
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:
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;
+}
#ifndef SRC_OBJECT_CERTIFICATE_H_
#define SRC_OBJECT_CERTIFICATE_H_
+#include <openssl/x509.h>
+#include <sys/queue.h>
+
#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 {
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
* 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.
* 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_ */
}
static int
-crl_validate(X509_CRL *crl)
+crl_validate(X509_CRL *crl, X509 *parent)
{
long version;
int error;
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;
}
int
-crl_load(struct cache_mapping *map, X509_CRL **result)
+crl_load(struct cache_mapping *map, X509 *parent, X509_CRL **result)
{
int error;
if (error)
goto end;
- error = crl_validate(*result);
+ error = crl_validate(*result, parent);
if (error)
X509_CRL_free(*result);
#include <openssl/x509.h>
#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_ */
}
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 */
/* 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;
}
#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_ */
#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"
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)
{
/* 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;
fah->hash.buf, fah->hash.size);
}
-#define INFER_CHILD(parent, fah) \
- path_childn(parent, (char const *)fah->file.buf, fah->file.size)
-
/*
* XXX
*
*/
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;
}
#ifndef SRC_OBJECT_MANIFEST_H_
#define SRC_OBJECT_MANIFEST_H_
-#include "rpp.h"
+#include <openssl/sha.h>
+#include <openssl/x509.h>
-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_ */
}
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 */
/* 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;
}
#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_ */
int
signed_object_validate(struct signed_object *sobj, struct oid_arcs const *oid,
- struct ee_cert *ee)
+ struct rpki_certificate *ee)
{
int error;
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_ */
#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"
/* 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)
{
*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)
{
struct validation *state;
char **url;
struct cache_mapping map;
- ta_status status;
thread->error = tal_init(&tal, thread->tal_file);
if (thread->error)
}
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.");
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;
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.");
}
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);
}
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);
}
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);
}
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.");
}
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);
}
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);
}
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;
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;
}
int
-resources_add_ip(struct resources *resources, struct IPAddressFamily *obj)
+resources_add_ip(struct resources *resources, struct resources *parent,
+ struct IPAddressFamily *obj)
{
int family;
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);
}
}
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.");
}
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) {
}
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;
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;
}
}
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.");
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;
}
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)
{
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 *);
#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);
}
#ifndef SRC_RPP_H_
#define SRC_RPP_H_
-#include <openssl/safestack.h>
+// XXX move to types?
+
#include <openssl/x509.h>
#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_ */
#include "rrdp.h"
+#include <openssl/bn.h>
+#include <openssl/sha.h>
+#include <sys/queue.h>
+
#include "base64.h"
-#include "cachent.h"
#include "cachetmp.h"
#include "common.h"
#include "config.h"
#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"
#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. */
struct parser_args {
struct rrdp_session *session;
- struct cache_node *notif;
+ struct rrdp_state *state;
};
static BIGNUM *
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)
{
}
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;
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;
}
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: <publish> 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: <publish> is attempting to create '%s', but the file is already cached.",
+ error = pr_val_err("RRDP desync: "
+ "<publish> 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: <publish> 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: "
+ "<publish> 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);
}
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: <withdraw> 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: <withdraw> 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: "
+ "<withdraw> 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;
static int
xml_read_snapshot(xmlTextReaderPtr reader, void *arg)
{
- struct parser_args *args = arg;
xmlReaderTypes type;
xmlChar const *name;
int error;
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)
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;
}
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;
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);
static int
xml_read_delta(xmlTextReaderPtr reader, void *arg)
{
- struct parser_args *args = arg;
xmlReaderTypes type;
xmlChar const *name;
int error;
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)
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;
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;
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);
}
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;
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);
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;
}
* 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;
}
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);
}
}
* 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 */
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
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:
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"
}
json_t *
-rrdp_notif2json(struct cachefile_notification *notif)
+rrdp_state2json(struct rrdp_state *state)
{
json_t *json;
json_t *deltas;
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();
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] );
}
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) {
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) {
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;
}
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);
}
}
#define SRC_RRDP_H_
#include <jansson.h>
-#include <openssl/bn.h>
-#include <openssl/sha.h>
-#include <sys/queue.h>
+#include <stdbool.h>
+#include <time.h>
-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_ */
}
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 */
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;
}
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) */
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;
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++;
#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_ */
#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;
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.
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;
{
X509_VERIFY_PARAM_free(state->x509_data.params);
X509_STORE_free(state->x509_data.store);
- certstack_destroy(state->certstack);
free(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)
{
#ifndef SRC_STATE_H_
#define SRC_STATE_H_
-#include "cert_stack.h"
+#include <openssl/x509.h>
+
#include "object/tal.h"
#include "validation_handler.h"
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 *);
#define SRC_THREAD_VAR_H_
#include "state.h"
+#include "types/map.h"
int thvar_init(void); /* This function does not need cleanup. */
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)
{
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 *);
}
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;
* 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)
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 : "",
/* 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 *);
#include <string.h>
#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)
{
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
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)
#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)
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)
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 */
ck_assert_ptr_eq(NULL, hash);
- rrdp_notif_free(notif);
+ rrdp_state_free(notif);
}
START_TEST(test_update_notif)
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));
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
"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));
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);
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;
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\" ] }");