#include "rpp.h"
#include "rsync.h"
#include "types/path.h"
+#include "types/url.h"
/* XXX force RRDP if one RPP fails to validate by rsync? */
}
/* @uri is either a caRepository or a rpkiNotify */
-static int
-try_uri(char const *uri, struct cache_node *root,
- dl_cb download, validate_cb validate, void *arg)
+static struct cache_node *
+do_refresh(char const *uri, struct cache_node *root, dl_cb download)
{
- struct cache_node *rpp;
- struct cache_mapping map;
- int error;
+ struct cache_node *node;
if (!uri)
- return 1; /* Protocol unavailable; ignore */
+ return NULL; /* Protocol unavailable; ignore */
- pr_val_debug("Trying %s (%s)...", uri, download ? "online" : "offline");
+ pr_val_debug("Trying %s (online)...", uri);
- rpp = cachent_provide(root, uri);
- if (!rpp)
- return pr_val_err("Malformed URL: %s", uri);
-
- if (download != NULL) {
- if (rpp->flags & CNF_FRESH) {
- if (rpp->dlerr)
- return rpp->dlerr;
- } else {
- rpp->flags |= CNF_FRESH;
- error = rpp->dlerr = download(rpp);
- if (error)
- return error;
- }
+ node = cachent_provide(root, uri);
+ if (!node) {
+ pr_val_err("Malformed URL: %s", uri);
+ return NULL;
}
- map.url = rpp->url;
- map.path = (download != NULL) ? get_tmppath(rpp) : rpp->path;
- error = validate(&map, arg);
- if (error) {
- pr_val_debug("RPP validation failed.");
- return error;
+ if (!(node->flags & CNF_FRESH)) {
+ node->flags |= CNF_FRESH;
+ node->dlerr = download(node);
}
+ if (node->dlerr)
+ pr_val_debug("Refresh failed.");
- pr_val_debug("RPP validated successfully.");
- rpp->flags |= CNF_VALID;
- return 0;
+ return node;
}
-static int
-try_uris(struct strlist *uris, struct cache_node *root,
- char const *prefix, dl_cb dl, validate_cb cb, void *arg)
+/* @url needs to outlive @map. */
+int
+cache_refresh_url(char *url, struct cache_mapping *map)
{
- char **str;
- int error;
+ struct cache_node *node = NULL;
- ARRAYLIST_FOREACH(uris, str)
- if (str_starts_with(*str, prefix)) {
- error = try_uri(*str, root, dl, cb, arg);
- if (error <= 0)
- return error;
- }
+ // XXX mutex
+ // XXX review result signs
- return 1;
+ if (url_is_https(url))
+ node = do_refresh(url, cache.https, dl_http);
+ else if (url_is_rsync(url))
+ node = do_refresh(url, cache.rsync, dl_rsync);
+ if (!node)
+ return EINVAL;
+
+ // 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;
}
+/* @url needs to outlive @map. */
int
-cache_download_uri(struct strlist *uris, validate_cb cb, void *arg)
+cache_fallback_url(char *url, struct cache_mapping *map)
{
- int error;
+ struct cache_node *node = NULL;
- // XXX mutex
- // XXX review result signs
-
- /* Online attempts */
- error = try_uris(uris, cache.https, "https://", dl_http, cb, arg);
- if (error <= 0)
- return error;
- error = try_uris(uris, cache.rsync, "rsync://", dl_rsync, cb, arg);
- if (error <= 0)
- return error;
+ 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;
- /* Offline attempts */
- error = try_uris(uris, cache.https, "https://", NULL, cb, arg);
- if (error <= 0)
- return error;
- return try_uris(uris, cache.rsync, "rsync://", NULL, cb, arg);
+ map->url = url;
+ map->path = node->path;
+ return 0;
}
/*
- * XXX outdated comment
- *
- * Assumes the URIs represent different ways to access the same content.
- *
- * Sequentially (in the order dictated by their priorities) attempts to update
- * (in the cache) the content pointed by each URL.
- * If a download succeeds, calls cb on it. If cb succeeds, returns without
- * trying more URLs.
- *
- * If none of the URLs download and callback properly, attempts to find one
- * that's already cached, and callbacks it.
+ * Attempts to refresh the RPP described by @sias, returns the resulting
+ * repository's mapping.
*/
int
-cache_download_alt(struct sia_uris *sias, validate_cb cb, void *arg)
+cache_refresh_sias(struct sia_uris *sias, struct cache_mapping *map)
{
- int error;
+ struct cache_node *hnode;
+ struct cache_node *rnode;
// XXX Make sure somewhere validates rpkiManifest matches caRepository.
- /* XXX mutex */
-
- /* Online attempts */
+ // XXX mutex
// XXX review result signs
// XXX normalize rpkiNotify & caRepository?
- error = try_uri(sias->rpkiNotify, cache.https, dl_rrdp, cb, arg);
- if (error <= 0)
- return error;
- error = try_uri(sias->caRepository, cache.rsync, dl_rsync, cb, arg);
- if (error <= 0)
- return error;
- /* Offline attempts */
- error = try_uri(sias->rpkiNotify, cache.https, NULL, cb, arg);
- if (error <= 0)
- return error;
- return try_uri(sias->caRepository, cache.rsync, NULL, cb, arg);
+ hnode = do_refresh(sias->rpkiNotify, cache.https, dl_rrdp);
+ if (hnode && !hnode->dlerr) {
+ map->url = hnode->url;
+ map->path = hnode->tmppath;
+ return 0;
+ }
+
+ rnode = do_refresh(sias->caRepository, cache.rsync, dl_rsync);
+ if (rnode && !rnode->dlerr) {
+ map->url = rnode->url;
+ map->path = rnode->tmppath;
+ return 0;
+ }
+
+ if (hnode && cachent_is_cached(hnode)) {
+ map->url = hnode->url;
+ map->path = hnode->path;
+ return 0;
+ }
+
+ if (hnode && cachent_is_cached(rnode)) {
+ map->url = hnode->url;
+ map->path = hnode->path;
+ return 0;
+ }
+
+ return EINVAL;
}
void
#define SRC_CACHE_LOCAL_CACHE_H_
#include "types/map.h"
-#include "types/str.h"
int cache_setup(void); /* Init this module */
void cache_teardown(void); /* Destroy this module */
void sias_init(struct sia_uris *);
void sias_cleanup(struct sia_uris *);
-/*
- * The callback should return
- *
- * - 0 on success ("Mapping handled successfully")
- * - > 0 on soft errors ("Try another mapping")
- * - < 0 on hard errors ("Abandon foreach")
- */
-typedef int (*validate_cb)(struct cache_mapping *, void *);
-int cache_download_uri(struct strlist *, validate_cb, void *);
-int cache_download_alt(struct sia_uris *, validate_cb, void *);
+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 *);
void cache_print(void); /* Dump cache in stdout. Recursive; tests only */
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 *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 *,
/* Steals ownership of @x509 on success. */
int
-x509stack_push(struct cert_stack *stack, struct cache_mapping *map, X509 *x509,
- enum rpki_policy policy, enum cert_type type)
+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;
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 *, X509 *,
- enum rpki_policy, enum cert_type);
+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 *);
#include "object/manifest.h"
#include "thread_var.h"
#include "types/path.h"
+#include "types/str.h"
#include "types/url.h"
/*
}
static int
-certificate_load(struct cache_mapping *map, X509 **result)
+certificate_load(struct cache_mapping const *map, X509 **result)
{
X509 *cert = NULL;
BIO *bio;
}
static int
-check_rpp(struct cache_mapping *map_rpp, void *rpkiManifest)
+check_rpp(struct cache_mapping const *map_rpp, char *rpkiManifest)
{
struct cache_mapping mft;
struct rpp *pp;
/* Boilerplate code for CA certificate validation and recursive traversal. */
int
-certificate_traverse(struct rpp *rpp_parent, struct cache_mapping *cert_map)
+certificate_traverse(struct rpp *rpp_parent,
+ struct cache_mapping const *cert_map)
{
struct validation *state;
int total_parents;
X509 *x509;
- struct sia_uris sia_uris;
+ struct sia_uris sias;
enum rpki_policy policy;
enum cert_type certype;
+ struct cache_mapping rpp;
int error;
state = state_retrieve();
if (error)
goto revert_cert;
- sias_init(&sia_uris);
+ sias_init(&sias);
error = (certype == CERTYPE_TA)
- ? certificate_validate_extensions_ta(x509, &sia_uris, &policy)
- : certificate_validate_extensions_ca(x509, &sia_uris, &policy,
+ ? certificate_validate_extensions_ta(x509, &sias, &policy)
+ : certificate_validate_extensions_ca(x509, &sias, &policy,
rpp_parent);
if (error)
- goto revert_uris;
+ goto revert_sias;
error = x509stack_push(validation_certstack(state), cert_map, x509,
policy, certype);
if (error)
- goto revert_uris;
+ goto revert_sias;
x509 = NULL; /* Ownership stolen */
- error = cache_download_alt(&sia_uris, check_rpp, sia_uris.rpkiManifest);
+ error = cache_refresh_sias(&sias, &rpp);
+ if (error) {
+ x509stack_cancel(validation_certstack(state));
+ goto revert_sias;
+ }
+
+ error = check_rpp(&rpp, sias.rpkiManifest);
if (error)
x509stack_cancel(validation_certstack(state));
-revert_uris:
- sias_cleanup(&sia_uris);
+revert_sias:
+ sias_cleanup(&sias);
revert_cert:
if (x509 != NULL)
X509_free(x509);
*/
int certificate_validate_aia(char const *, X509 *);
-int certificate_traverse(struct rpp *, struct cache_mapping *);
+int certificate_traverse(struct rpp *, struct cache_mapping const *);
#endif /* SRC_OBJECT_CERTIFICATE_H_ */
#include "log.h"
#include "thread_var.h"
#include "types/path.h"
+#include "types/str.h"
+#include "types/url.h"
struct tal {
char const *file_name;
/* List of threads, one per TAL file */
SLIST_HEAD(threads_list, validation_thread);
-struct handle_tal_args {
- struct tal tal;
- struct db_table *db;
-};
+#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)
if (is_blank(fc))
break;
- if (str_starts_with(fc, "https://") ||
- str_starts_with(fc, "rsync://"))
+ if (url_is_https(fc) || url_is_rsync(fc))
strlist_add(&tal->urls, pstrdup(fc));
fc = nl + cr + 1;
*len = tal->spki_len;
}
-/*
- * Performs the whole validation walkthrough that starts with Trust Anchor @ta,
- * which is assumed to have been extracted from TAL @arg->tal.
- */
-static int
-handle_ta(struct cache_mapping *ta, void *arg)
+static ta_status
+handle_ta(struct cache_mapping const *ta, struct validation *state)
{
- struct handle_tal_args *args = arg;
- struct validation_handler validation_handler;
- struct validation *state;
struct cert_stack *certstack;
struct deferred_cert deferred;
int error;
- validation_handler.handle_roa_v4 = handle_roa_v4;
- validation_handler.handle_roa_v6 = handle_roa_v6;
- validation_handler.handle_router_key = handle_router_key;
- validation_handler.arg = args->db;
-
- error = validation_prepare(&state, &args->tal, &validation_handler);
- if (error)
- return ENSURE_NEGATIVE(error);
-
- if (!str_ends_with(ta->url, ".cer")) {
- pr_op_err("TAL URI lacks '.cer' extension: %s", ta->url);
- error = EINVAL;
- goto end;
- }
-
- /* Handle root certificate. */
- error = certificate_traverse(NULL, ta);
- if (error) {
+ /* == Root certificate == */
+ if (certificate_traverse(NULL, ta) != 0) {
switch (validation_pubkey_state(state)) {
case PKS_INVALID:
- error = EINVAL;
- goto end;
+ /* 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:
- error = ENSURE_NEGATIVE(error);
- goto end;
+ /* We don't know; try some other URL. */
+ return ts_next;
}
+
pr_crit("Unknown public key state: %u",
validation_pubkey_state(state));
}
* (the root validated successfully; subtrees are isolated problems.)
*/
- /* Handle every other certificate. */
+ /* == Every other certificate == */
certstack = validation_certstack(state);
do {
error = deferstack_pop(certstack, &deferred);
- if (error == -ENOENT) {
- error = 0; /* No more certificates left; we're done */
- goto end;
- } else if (error) /* All other errors are critical, currently */
+ 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);
/*
map_cleanup(&deferred.map);
rpp_refput(deferred.pp);
} while (true);
+}
-end: validation_destroy(state);
- return error;
+static void
+__do_file_validation(struct validation_thread *thread)
+{
+ struct tal tal;
+ struct validation_handler collector;
+ struct db_table *db;
+ struct validation *state;
+ char **url;
+ struct cache_mapping map;
+ ta_status status;
+
+ thread->error = tal_init(&tal, thread->tal_file);
+ if (thread->error)
+ return;
+
+ collector.handle_roa_v4 = handle_roa_v4;
+ collector.handle_roa_v6 = handle_roa_v6;
+ collector.handle_router_key = handle_router_key;
+ collector.arg = db = db_table_create();
+
+ thread->error = validation_prepare(&state, &tal, &collector);
+ if (thread->error) {
+ db_table_destroy(db);
+ goto end2;
+ }
+
+ ARRAYLIST_FOREACH(&tal.urls, url) {
+ if (cache_refresh_url(*url, &map) != 0)
+ continue;
+
+ status = handle_ta(&map, state);
+ switch (status.v) {
+ case TS_SUCCESS: goto end1;
+ case TS_FALLBACK: goto fallback;
+ case TS_NEXT: ; /* Fall through */
+ }
+ }
+
+fallback:
+ ARRAYLIST_FOREACH(&tal.urls, url) {
+ if (cache_fallback_url(*url, &map) != 0)
+ continue;
+
+ status = handle_ta(&map, state);
+ switch (status.v) {
+ case TS_SUCCESS: goto end1;
+ case TS_FALLBACK: /* Already fallbacking */
+ case TS_NEXT: ; /* Fall through */
+ }
+ }
+
+ pr_op_err("None of the TAL URIs yielded a successful traversal.");
+ thread->error = EINVAL;
+ db_table_destroy(db);
+ db = NULL;
+
+end1: thread->db = db;
+end2: tal_cleanup(&tal);
}
static void *
do_file_validation(void *arg)
{
struct validation_thread *thread = arg;
- struct handle_tal_args args;
time_t start, finish;
start = time(NULL);
fnstack_init();
fnstack_push(thread->tal_file);
- thread->error = tal_init(&args.tal, thread->tal_file);
- if (thread->error)
- goto end;
-
- args.db = db_table_create();
- thread->error = cache_download_uri(&args.tal.urls, handle_ta, &args);
- if (thread->error) {
- pr_op_err("None of the TAL URIs yielded a successful traversal.");
- db_table_destroy(args.db);
- } else {
- thread->db = args.db;
- }
+ __do_file_validation(thread);
- tal_cleanup(&args.tal);
-end: fnstack_cleanup();
+ fnstack_cleanup();
finish = time(NULL);
if (start != ((time_t) -1) && finish != ((time_t) -1))
pr_op_debug("The %s tree took %.0lf seconds.",
- args.tal.file_name, difftime(finish, start));
+ path_filename(thread->tal_file),
+ difftime(finish, start));
return NULL;
}
#include "rsync.h"
#include "types/bio_seq.h"
#include "types/path.h"
+#include "types/url.h"
#define HDRSIZE 32
if (filename == NULL || strcmp(filename, "-") == 0)
return BIO_new_fp(stdin, BIO_NOCLOSE);
- if (str_starts_with(filename, "rsync://"))
+ if (url_is_rsync(filename))
return rsync2bio(filename);
return BIO_new_file(filename, "rb");
/* See fnstack_push(). @map needs to outlive the push/pop. */
void
-fnstack_push_map(struct cache_mapping *map)
+fnstack_push_map(struct cache_mapping const *map)
{
fnstack_push(map_val_get_printable(map));
}
void fnstack_cleanup(void);
void fnstack_push(char const *);
-void fnstack_push_map(struct cache_mapping *);
+void fnstack_push_map(struct cache_mapping const *);
char const *fnstack_peek(void);
void fnstack_pop(void);
#include "types/path.h"
static char const *
-map_get_printable(struct cache_mapping *map, enum filename_format format)
+map_get_printable(struct cache_mapping const *map, enum filename_format format)
{
switch (format) {
case FNF_GLOBAL:
}
char const *
-map_val_get_printable(struct cache_mapping *map)
+map_val_get_printable(struct cache_mapping const *map)
{
return map_get_printable(map, config_get_val_log_file_format());
}
char const *
-map_op_get_printable(struct cache_mapping *map)
+map_op_get_printable(struct cache_mapping const *map)
{
return map_get_printable(map, config_get_op_log_file_format());
}
void
-map_parent(struct cache_mapping *child, struct cache_mapping *parent)
+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 *parent, char const *name)
+map_child(struct cache_mapping const *parent, char const *name)
{
struct cache_mapping *child;
}
void
-map_copy(struct cache_mapping *dst, struct cache_mapping *src)
+map_copy(struct cache_mapping *dst, struct cache_mapping const *src)
{
dst->url = pstrdup(src->url);
dst->path = pstrdup(src->path);
char *path;
};
-char const *map_val_get_printable(struct cache_mapping *);
-char const *map_op_get_printable(struct cache_mapping *);
+char const *map_val_get_printable(struct cache_mapping const *);
+char const *map_op_get_printable(struct cache_mapping const *);
-void map_parent(struct cache_mapping *, struct cache_mapping *);
-struct cache_mapping *map_child(struct cache_mapping *, char 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 *);
+void map_copy(struct cache_mapping *, struct cache_mapping const *);
void map_cleanup(struct cache_mapping *);
#endif /* SRC_TYPES_MAP_H_ */
#include "types/url.h"
#include "alloc.h"
+#include "common.h"
#include "types/path.h"
+bool
+url_is_rsync(char const *url)
+{
+ return str_starts_with(url, "rsync://");
+}
+
+bool
+url_is_https(char const *url)
+{
+ return str_starts_with(url, "https://");
+}
+
/*
* XXX use this:
*
#define RPKI_SCHEMA_LEN 8 /* strlen("rsync://"), strlen("https://") */
+bool url_is_rsync(char const *);
+bool url_is_https(char const *);
+
char *url_normalize(char const *);
bool url_same_origin(char const *, char const *);
}
static int
-okay(struct cache_mapping *map, void *arg)
+okay(struct cache_mapping const *map, void *arg)
{
return 0;
}
//
// va_start(args, maps);
// while ((str = va_arg(args, char const *)) != NULL) {
-// if (str_starts_with(str, "https://"))
+// if (url_is_https(str))
// type = MAP_HTTP;
-// else if (str_starts_with(str, "rsync://"))
+// else if (url_is_rsync(str))
// type = MAP_RSYNC;
// else
// ck_abort_msg("Bad protocol: %s", str);