+Create struct and method to store RRDP URIs data.
+Create handler so that multiple threads can access RRDP URIs data.
+Rename 'gdata' property to 'global_data' at update_notification struct.
+Use prefered access method according to the order specified at the CAs.
+Implement RRDP URIs comparison, considers: URI, session ID and serial so that the caller can determine what to do (process snapshot, deltas, etc.)
+Document rrdp_objects.h structs.
+Add content length to 'publish' structure.
+Add functions to parse 'publish' elements.
+Validate that a new RRDP object parsed matches session ID and serial of the parent.
+Whenever a snapshot file is parsed (and validated), all of its 'publish' elements are parsed as well and created at the local repository.
+Use 'fnstack' to log whenever an RRDP file is being processed.
+Update 'uri.h' to explicitly create either rsync or https URIs.
+Use rrdp_handler at the validation state of each thread (or each TAL, it's the same thing).
+Fix wrong return value on error at __do_file_validation, it should return a 'no memory' error instead of 'invalid value'.
+Fix macro ARRAYLIST_FOREACH, one argument wasn't being utilized.
+Update unit tests, add reference to new header 'db_rrdp.h'.
fort_SOURCES += resource/ip6.h resource/ip6.c
fort_SOURCES += resource/asn.h resource/asn.c
+fort_SOURCES += rrdp/db_rrdp.h rrdp/db_rrdp.c
+fort_SOURCES += rrdp/rrdp_handler.h rrdp/rrdp_handler.c
fort_SOURCES += rrdp/rrdp_objects.h rrdp/rrdp_objects.c
fort_SOURCES += rrdp/rrdp_parser.h rrdp/rrdp_parser.c
DEFINE_ARRAY_LIST_FUNCTIONS(name, elem_type, )
#define ARRAYLIST_FOREACH(list, node, index) for ( \
- (i) = 0, (node) = (list)->array; \
- (i) < (list)->len; \
- (i)++, (node)++ \
+ (index) = 0, (node) = (list)->array; \
+ (index) < (list)->len; \
+ (index)++, (node)++ \
)
#endif /* SRC_DATA_STRUCTURE_ARRAY_LIST_H_ */
#include "object/name.h"
#include "object/manifest.h"
#include "object/signed_object.h"
+#include "rrdp/rrdp_objects.h"
#include "rrdp/rrdp_parser.h"
#include "rsync/rsync.h"
unsigned char **ski_data;
};
+/* Callback method to fetch repository objects */
+typedef int (access_method_exec)(struct sia_ca_uris *);
+
static void
sia_ca_uris_init(struct sia_ca_uris *sia_uris)
{
rcvd_cert = NULL;
tmp_size = 0;
- error = uri_create_str(&uri, file, strlen(file));
+ error = uri_create_rsync_str(&uri, file, strlen(file));
if (error)
return error;
return force_aia_validation(caIssuers, cert);
}
+static int
+exec_rrdp_method(struct sia_ca_uris *sia_uris)
+{
+ struct update_notification *upd_notification;
+ struct snapshot *snapshot;
+ enum rrdp_uri_cmp_result res;
+ int error;
+
+ error = rrdp_parse_notification(sia_uris->rpkiNotify.uri,
+ &upd_notification);
+ if (error)
+ return error;
+
+ res = rhandler_uri_cmp(uri_get_global(sia_uris->rpkiNotify.uri),
+ upd_notification->global_data.session_id,
+ upd_notification->global_data.serial);
+ switch(res) {
+ case RRDP_URI_EQUAL:
+ update_notification_destroy(upd_notification);
+ fnstack_pop();
+ return 0;
+ case RRDP_URI_DIFF_SERIAL:
+ /* FIXME (now) Fetch and process the deltas */
+ /* Store the new value */
+ error = rhandler_uri_update(
+ uri_get_global(sia_uris->rpkiNotify.uri),
+ upd_notification->global_data.session_id,
+ upd_notification->global_data.serial);
+ break;
+ case RRDP_URI_DIFF_SESSION:
+ /* FIXME (now) delete the old session files */
+ case RRDP_URI_NOTFOUND:
+ /* Process the snapshot */
+ error = rrdp_parse_snapshot(upd_notification, &snapshot);
+ if (error) {
+ break;
+ }
+ snapshot_destroy(snapshot);
+ fnstack_pop(); /* FIXME (now) Snapshot pop, shouldn't be here */
+ /* Store the new value */
+ error = rhandler_uri_update(
+ uri_get_global(sia_uris->rpkiNotify.uri),
+ upd_notification->global_data.session_id,
+ upd_notification->global_data.serial);
+
+ break;
+ default:
+ pr_crit("Unknown RRDP URI comparison result");
+ }
+
+ /*
+ * FIXME (now) Now do the validation, start by the root manifest
+ */
+ update_notification_destroy(upd_notification);
+ fnstack_pop();
+ if (error) {
+ return error;
+ }
+ return 0;
+}
+
+static int
+exec_rsync_method(struct sia_ca_uris *sia_uris)
+{
+ return download_files(sia_uris->caRepository.uri, false, false);
+}
+
+/*
+ * Currently only two access methods are supported, just consider those two:
+ * rsync and RRDP. If a new access method is supported, this function must
+ * change (and probably the sia_ca_uris struct as well).
+ */
+static int
+use_access_method(struct sia_ca_uris *sia_uris,
+ access_method_exec rsync_cb, access_method_exec rrdp_cb)
+{
+ access_method_exec *cb_primary;
+ access_method_exec *cb_secondary;
+ int error;
+
+ /*
+ * RSYNC will always be present (at least for now, see
+ * rfc6487#section-4.8.8.1)
+ */
+ if (sia_uris->rpkiNotify.uri == NULL)
+ return rsync_cb(sia_uris);
+
+ /* Get the preferred */
+ if (sia_uris->caRepository.position > sia_uris->rpkiNotify.position) {
+ cb_primary = rrdp_cb;
+ cb_secondary = rsync_cb;
+ } else {
+ cb_primary = rsync_cb;
+ cb_secondary = rrdp_cb;
+ }
+
+ /* Try with the preferred; in case of error, try with the next one */
+ error = cb_primary(sia_uris);
+ if (!error)
+ return 0;
+
+ return cb_secondary(sia_uris);
+}
+
/** Boilerplate code for CA certificate validation and recursive traversal. */
int
certificate_traverse(struct rpp *rpp_parent, struct rpki_uri *cert_uri)
goto revert_refs;
}
- /* Currently there's support for 2 access methods */
- /* FIXME (now) Download files according to the preferred method */
- if (sia_uris.rpkiNotify.uri != NULL &&
- sia_uris.caRepository.position > sia_uris.rpkiNotify.position) {
- pr_info("Preferred Access Method: RRDP, but doing rsync anyways");
- rrdp_parse_notification(sia_uris.rpkiNotify.uri);
- } else
- pr_info("Preferred Access Method: RSYNC [found RRDP? %s]",
- (sia_uris.rpkiNotify.uri == NULL ? "NO" : "YES"));
-
- /* Currently downloading RSYNC (will always be present) */
- error = download_files(sia_uris.caRepository.uri, false, false);
+ /*
+ * FIXME (now) Beware: what if RRDP is already being utilized?
+ *
+ * There isn't any restriction about the preferred access method of
+ * children CAs being the same as the parent CA.
+ *
+ * Two possible scenarios arise:
+ * 1) CA Parent didn't utilized (or didn't had) and RRDP update
+ * notification URI.
+ * 2) CA Parent successfully utilized an RRDP update notification URI.
+ *
+ * Step (1) is simple, do the check of the preferred access method.
+ * Step (2) must do something different.
+ * - The manifest should exist at the snapshot file (aka, directory
+ * structure), so it should be processed from there on.
+ * - Verify that the manifest file exists locally (must be the same
+ * session_id and serial, load those at start, the serial can
+ * be written to a local file, the session_id can be loaded from the
+ * directory structure <local-repository>/rrdp/<session_id>).:
+ * + Exists: Read and process normally (no need to do rsync)
+ * + Doesn't exists: check for preferred access method, keep the flow
+ * from there on.
+ */
+ error = use_access_method(&sia_uris, exec_rsync_method,
+ exec_rrdp_method);
if (error)
return error;
*/
struct validation_handler validation_handler;
+ struct rrdp_handler rrdp_handler;
struct validation *state;
struct cert_stack *certstack;
struct deferred_cert deferred;
validation_handler.handle_router_key = handle_router_key;
validation_handler.arg = arg;
- error = validation_prepare(&state, tal, &validation_handler);
+ rrdp_handler.uri_cmp = rrdp_uri_cmp;
+ rrdp_handler.uri_update = rrdp_uri_update;
+
+ error = validation_prepare(&state, tal, &validation_handler,
+ &rrdp_handler);
if (error)
return ENSURE_NEGATIVE(error);
validation_pubkey_state(state));
}
+ /* FIXME (now) Consider RRDP found scenario */
+
/*
* From now on, the tree should be considered valid, even if subsequent
* certificates fail.
thread = malloc(sizeof(struct thread));
if (thread == NULL) {
close_thread(pid, tal_file);
- error = -EINVAL;
+ error = pr_enomem();
goto free_param;
}
--- /dev/null
+#include "db_rrdp.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include "data_structure/uthash_nonfatal.h"
+#include "rrdp/rrdp_objects.h"
+#include "log.h"
+
+struct db_rrdp_uri {
+ /* Key */
+ char *uri;
+ struct global_data data;
+ UT_hash_handle hh;
+};
+
+struct db_rrdp {
+ struct db_rrdp_uri *uris;
+};
+
+static int
+db_rrdp_uri_create(char const *uri, char const *session_id,
+ unsigned long serial, struct db_rrdp_uri **result)
+{
+ struct db_rrdp_uri *tmp;
+ int error;
+
+ tmp = malloc(sizeof(struct db_rrdp_uri));
+ if (tmp == NULL)
+ return pr_enomem();
+ /* Needed by uthash */
+ memset(tmp, 0, sizeof(struct db_rrdp_uri));
+
+ tmp->uri = strdup(uri);
+ if (tmp->uri == NULL) {
+ error = pr_enomem();
+ goto release_tmp;
+ }
+
+ tmp->data.session_id = strdup(session_id);
+ if (tmp->data.session_id == NULL) {
+ error = pr_enomem();
+ goto release_uri;
+ }
+
+ tmp->data.serial = serial;
+
+ *result = tmp;
+ return 0;
+release_uri:
+ free(tmp->uri);
+release_tmp:
+ free(tmp);
+ return error;
+}
+
+static void
+db_rrdp_uri_destroy(struct db_rrdp_uri *uri)
+{
+ free(uri->data.session_id);
+ free(uri->uri);
+ free(uri);
+}
+
+static int
+add_rrdp_uri(struct db_rrdp *db, struct db_rrdp_uri *new_uri)
+{
+ struct db_rrdp_uri *old_uri;
+
+ errno = 0;
+ HASH_FIND_STR(db->uris, new_uri->uri, old_uri);
+ if (errno)
+ return pr_errno(errno,
+ "RRDP URI couldn't be added to hash table");
+
+ if (old_uri != NULL) {
+ HASH_DELETE(hh, db->uris, old_uri);
+ db_rrdp_uri_destroy(old_uri);
+ }
+ HASH_ADD_KEYPTR(hh, db->uris, new_uri->uri, strlen(new_uri->uri),
+ new_uri);
+
+ return 0;
+}
+
+enum rrdp_uri_cmp_result
+db_rrdp_cmp_uri(struct db_rrdp *db, char const *uri, char const *session_id,
+ unsigned long serial)
+{
+ struct db_rrdp_uri *found;
+
+ HASH_FIND_STR(db->uris, uri, found);
+ if (found == NULL)
+ return RRDP_URI_NOTFOUND;
+
+ if (strcmp(session_id, found->data.session_id) != 0)
+ return RRDP_URI_DIFF_SESSION;
+
+ if (serial != found->data.serial)
+ return RRDP_URI_DIFF_SERIAL;
+
+ return RRDP_URI_EQUAL;
+}
+
+int
+db_rrdp_add_uri(struct db_rrdp *db, char const *uri, char const *session_id,
+ unsigned long serial)
+{
+ struct db_rrdp_uri *db_uri;
+ int error;
+
+ db_uri = NULL;
+ error = db_rrdp_uri_create(uri, session_id, serial, &db_uri);
+ if (error)
+ return error;
+
+ error = add_rrdp_uri(db, db_uri);
+ if (error) {
+ db_rrdp_uri_destroy(db_uri);
+ return error;
+ }
+
+ return 0;
+}
+
+int
+db_rrdp_create(struct db_rrdp **result)
+{
+ struct db_rrdp *tmp;
+
+ tmp = malloc(sizeof(struct db_rrdp));
+ if (tmp == NULL)
+ return pr_enomem();
+
+ tmp->uris = NULL;
+
+ *result = tmp;
+ return 0;
+}
+
+void
+db_rddp_destroy(struct db_rrdp *db)
+{
+ struct db_rrdp_uri *uri_node, *uri_tmp;
+
+ HASH_ITER(hh, db->uris, uri_node, uri_tmp) {
+ HASH_DEL(db->uris, uri_node);
+ db_rrdp_uri_destroy(uri_node);
+ }
+
+ free(db);
+}
--- /dev/null
+#ifndef SRC_RRDP_DB_RRDP_H_
+#define SRC_RRDP_DB_RRDP_H_
+
+#include "rrdp/rrdp_objects.h"
+
+/*
+ * Struct and methods to persist RRDP information, such as URIs, session ID per
+ * URI and current serial number.
+ */
+struct db_rrdp;
+
+int db_rrdp_create(struct db_rrdp **);
+void db_rddp_destroy(struct db_rrdp *);
+
+enum rrdp_uri_cmp_result db_rrdp_cmp_uri(struct db_rrdp *, char const *,
+ char const *, unsigned long);
+int db_rrdp_add_uri(struct db_rrdp *, char const *, char const *,
+ unsigned long);
+
+#endif /* SRC_RRDP_DB_RRDP_H_ */
--- /dev/null
+#include "rrdp_handler.h"
+
+#include "thread_var.h"
+
+static int
+get_current_threads_handler(struct rrdp_handler const **result)
+{
+ struct validation *state;
+ struct rrdp_handler const *handler;
+
+ state = state_retrieve();
+ if (state == NULL)
+ return -EINVAL;
+
+ handler = validation_get_rrdp_handler(state);
+ if (handler == NULL)
+ pr_crit("This thread lacks an RRDP handler.");
+
+ *result = handler;
+ return 0;
+}
+
+enum rrdp_uri_cmp_result
+rhandler_uri_cmp(char const *uri, char const *session_id, unsigned long serial)
+{
+ struct rrdp_handler const *handler;
+ int error;
+
+ error = get_current_threads_handler(&handler);
+ if (error)
+ return error;
+
+ return (handler->uri_cmp != NULL)
+ ? handler->uri_cmp(uri, session_id, serial)
+ : RRDP_URI_NOTFOUND;
+}
+
+int
+rhandler_uri_update(char const *uri, char const *session_id,
+ unsigned long serial)
+{
+ struct rrdp_handler const *handler;
+ int error;
+
+ error = get_current_threads_handler(&handler);
+ if (error)
+ return error;
+
+ return (handler->uri_update != NULL)
+ ? handler->uri_update(uri, session_id, serial)
+ : 0;
+}
--- /dev/null
+#ifndef SRC_RRDP_RRDP_HANDLER_H_
+#define SRC_RRDP_RRDP_HANDLER_H_
+
+#include "rrdp/rrdp_objects.h"
+
+/*
+ * Almost the same idea as 'validation_handler.h', only that the main focus is
+ * a multithreaded environment.
+ *
+ * The RRDP URIs are expected to live at the main thread, the other threads can
+ * access such URIs. The handler must assure that the data is safe
+ * (handle r/w locks), that's the reason why there isn't any reference to a
+ * 'db_rrdp' struct.
+ */
+
+struct rrdp_handler {
+ /*
+ * Search the RRDP URI, returns the corresponding enum to indicate
+ * the comparison result.
+ */
+ enum rrdp_uri_cmp_result (*uri_cmp)(char const *, char const *,
+ unsigned long);
+ /* Add or update an RRDP URI */
+ int (*uri_update)(char const *, char const *, unsigned long);
+};
+
+enum rrdp_uri_cmp_result rhandler_uri_cmp(char const *, char const *, unsigned long);
+int rhandler_uri_update(char const *, char const *, unsigned long);
+
+#endif /* SRC_RRDP_RRDP_HANDLER_H_ */
doc_data_init(struct doc_data *data)
{
data->hash = NULL;
+ data->hash_len = 0;
data->uri = NULL;
return 0;
}
}
}
-
int
xml_source_set(struct xml_source *src, xmlDoc *orig)
{
if (tmp == NULL)
return pr_enomem();
- global_data_init(&tmp->gdata);
+ global_data_init(&tmp->global_data);
doc_data_init(&tmp->snapshot);
SLIST_INIT(&tmp->deltas_list);
free(head);
}
doc_data_cleanup(&file->snapshot);
- global_data_cleanup(&file->gdata);
+ global_data_cleanup(&file->global_data);
free(file);
}
return 0;
}
+
+int
+snapshot_create(struct snapshot **file)
+{
+ struct snapshot *tmp;
+
+ tmp = malloc(sizeof(struct snapshot));
+ if (tmp == NULL)
+ return pr_enomem();
+
+ global_data_init(&tmp->global_data);
+ SLIST_INIT(&tmp->publish_list);
+ tmp->source = NULL;
+
+ *file = tmp;
+ return 0;
+}
+
+void
+snapshot_destroy(struct snapshot *file)
+{
+ struct publish_list *list;
+ struct publish *head;
+
+ list = &file->publish_list;
+ while (!SLIST_EMPTY(list)) {
+ head = list->slh_first;
+ SLIST_REMOVE_HEAD(list, next);
+ doc_data_cleanup(&head->doc_data);
+ free(head->content);
+ free(head);
+ }
+ global_data_cleanup(&file->global_data);
+ xml_source_destroy(file->source);
+
+ free(file);
+}
+
+int
+publish_create(struct publish **file)
+{
+ struct publish *tmp;
+
+ tmp = malloc(sizeof(struct publish));
+ if (tmp == NULL)
+ return pr_enomem();
+
+ doc_data_init(&tmp->doc_data);
+ tmp->content = NULL;
+ tmp->content_len = 0;
+
+ *file = tmp;
+ return 0;
+}
+
+void
+publish_destroy(struct publish *file)
+{
+ doc_data_cleanup(&file->doc_data);
+ free(file->content);
+ free(file);
+}
+
+int
+publish_list_add(struct publish_list *list, struct publish *elem)
+{
+ SLIST_INSERT_HEAD(list, elem, next);
+ return 0;
+}
#include <sys/queue.h>
#include <stddef.h>
-/* Common structures */
+/* Possible results for an RRDP URI comparison */
+enum rrdp_uri_cmp_result {
+ /* The URI doesn't exists */
+ RRDP_URI_NOTFOUND,
+
+ /* The URI exists and has the same session ID and serial */
+ RRDP_URI_EQUAL,
+
+ /* The URI exists but has distinct serial */
+ RRDP_URI_DIFF_SERIAL,
+
+ /* The URI exists but has distinct session ID */
+ RRDP_URI_DIFF_SESSION,
+};
+
+/* Structure to remember the XML source file (useful for hash validations) */
struct xml_source;
+/* Global RRDP files data */
struct global_data {
char *session_id;
unsigned long serial;
};
+/* Specific RRDP files data, in some cases the hash can be omitted */
struct doc_data {
char *uri;
unsigned char *hash;
size_t hash_len;
};
+/* Represents a <publish> element to be utilized inside a list */
struct publish {
struct doc_data doc_data;
unsigned char *content;
+ size_t content_len;
SLIST_ENTRY(publish) next;
};
+/* Represents a <withdraw> element to be utilized inside a list */
struct withdraw {
struct doc_data doc_data;
SLIST_ENTRY(withdraw) next;
};
-/* Delta file structs */
+/* List of <publish> elements (either in a delta or a snapshot file) */
SLIST_HEAD(publish_list, publish);
+/* List of <withdraw> elements */
SLIST_HEAD(withdrawn_list, withdraw);
+/* Delta file content */
struct delta {
struct global_data global_data;
struct publish_list publish_list;
struct xml_source *source;
};
-/* Snapshot file structs */
+/* Snapshot file content */
struct snapshot {
struct global_data global_data;
struct publish_list publish_list;
struct xml_source *source;
};
-/* Update notification file structs */
+/* Delta element located at an update notification file */
struct delta_head {
unsigned long serial;
struct doc_data doc_data;
SLIST_ENTRY(delta_head) next;
};
+/* List of deltas inside an update notification file */
SLIST_HEAD(deltas_head, delta_head);
struct update_notification {
- struct global_data gdata;
+ struct global_data global_data;
struct doc_data snapshot;
struct deltas_head deltas_list;
};
int update_notification_deltas_add(struct deltas_head *, unsigned long, char **,
unsigned char **, size_t);
+int snapshot_create(struct snapshot **);
+void snapshot_destroy(struct snapshot *);
+
+int publish_create(struct publish **);
+void publish_destroy(struct publish *);
+
+int publish_list_add(struct publish_list *, struct publish *);
+
+
#endif /* SRC_RRDP_RRDP_OBJECTS_H_ */
#include <libxml/parser.h>
#include <libxml/tree.h>
+#include <openssl/evp.h>
+#include <sys/stat.h>
+#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
+#include <string.h>
+#include "crypto/base64.h"
#include "http/http.h"
#include "xml/relax_ng.h"
+#include "common.h"
+#include "file.h"
#include "log.h"
+#include "thread_var.h"
/* XML Elements */
#define RRDP_ELEM_NOTIFICATION "notification"
#define RRDP_ELEM_SNAPSHOT "snapshot"
#define RRDP_ELEM_DELTA "delta"
+#define RRDP_ELEM_PUBLISH "publish"
+#define RRDP_ELEM_WITHDRAW "withdraw"
/* XML Attributes */
#define RRDP_ATTR_VERSION "version"
return 0;
}
+static size_t
+write_local(unsigned char *content, size_t size, size_t nmemb, void *arg)
+{
+ FILE *fd = arg;
+ size_t read = size * nmemb;
+ size_t written;
+
+ written = fwrite(content, size, nmemb, fd);
+ if (written != nmemb)
+ return -EINVAL;
+
+ return read;
+}
+
+/* Trim @from, setting the result at @result pointer */
+static int
+trim(char *from, char **result, size_t *result_size)
+{
+ char *start, *end;
+ size_t tmp_size;
+
+ start = from;
+ tmp_size = strlen(from);
+ while (isspace(*start)) {
+ start++;
+ tmp_size--;
+ }
+ if (*start == '\0')
+ return pr_err("Invalid base64 encoded string (seems to be empty or full of spaces).");
+
+ end = start;
+ while (*end != '\0') {
+ if (!isspace(*end)) {
+ end++;
+ continue;
+ }
+ /* No middle spaces, newlines, etc. allowed */
+ *end = '\0';
+ tmp_size = end - start;
+ break;
+ }
+
+ *result = start;
+ *result_size = tmp_size;
+ return 0;
+}
+
+/*
+ * Get the base64 chars from @content and allocate to @out with lines no greater
+ * than 65 chars (including line feed).
+ *
+ * Why? LibreSSL doesn't like lines greater than 80 chars, so use a common
+ * length per line.
+ */
+static int
+base64_sanitize(char *content, char **out)
+{
+#define BUF_SIZE 65
+ char *result;
+ char *tmp;
+ size_t original_size, new_size;
+ size_t offset, buf_len;
+ int error;
+
+ original_size = 0;
+ error = trim(content, &tmp, &original_size);
+ if (error)
+ return error;
+
+ if (original_size <= BUF_SIZE) {
+ result = malloc(original_size + 1);
+ if (result == NULL)
+ return pr_enomem();
+ result[original_size] = '\0';
+ *out = result;
+ return 0;
+ }
+
+ new_size = original_size + (original_size / BUF_SIZE);
+ result = malloc(new_size + 1);
+ if (result == NULL)
+ return pr_enomem();
+
+ offset = 0;
+ while (original_size > 0){
+ buf_len = original_size > BUF_SIZE ? BUF_SIZE : original_size;
+ memcpy(&result[offset], tmp, buf_len);
+ tmp += buf_len;
+ offset += buf_len;
+ original_size -= buf_len;
+
+ if (original_size <= 0)
+ break;
+ result[offset] = '\n';
+ offset++;
+ }
+
+ /* Reallocate to exact size and add nul char */
+ if (offset != new_size + 1) {
+ tmp = realloc(result, offset + 1);
+ if (tmp == NULL) {
+ free(result);
+ return pr_enomem();
+ }
+ result = tmp;
+ }
+
+ result[offset] = '\0';
+ *out = result;
+ return 0;
+#undef BUF_SIZE
+}
+
+static int
+base64_read(char *content, unsigned char **out, size_t *out_len)
+{
+ BIO *encoded; /* base64 encoded. */
+ unsigned char *result;
+ char *sanitized;
+ size_t alloc_size;
+ size_t result_len;
+ int error;
+
+ sanitized = NULL;
+ error = base64_sanitize(content, &sanitized);
+ if (error)
+ return error;
+
+ encoded = BIO_new_mem_buf(sanitized, -1);
+ if (encoded == NULL) {
+ error = crypto_err("BIO_new_mem_buf() returned NULL");
+ goto release_sanitized;
+ }
+
+ alloc_size = EVP_DECODE_LENGTH(strlen(content));
+ result = malloc(alloc_size);
+ if (result == NULL) {
+ error = pr_enomem();
+ goto release_bio;
+ }
+
+ error = base64_decode(encoded, result, true, alloc_size, &result_len);
+ if (error)
+ goto release_result;
+
+ free(sanitized);
+ BIO_free(encoded);
+
+ *out = result;
+ (*out_len) = result_len;
+ return 0;
+release_result:
+ free(result);
+release_bio:
+ BIO_free(encoded);
+release_sanitized:
+ free(sanitized);
+ return error;
+}
+
static int
parse_string(xmlNode *root, char const *attr, char **result)
{
xmlChar *xml_value;
char *tmp;
- xml_value = xmlGetProp(root, BAD_CAST attr);
+ if (attr == NULL)
+ xml_value = xmlNodeGetContent(root);
+ else
+ xml_value = xmlGetProp(root, BAD_CAST attr);
+
if (xml_value == NULL)
- return pr_err("RRDP file: Couldn't find xml attribute %s",
- attr);
+ return pr_err("RRDP file: Couldn't find %s from '%s'",
+ (attr == NULL ? "string content" : "xml attribute"),
+ root->name);
tmp = malloc(xmlStrlen(xml_value) + 1);
if (tmp == NULL) {
/* @gdata elements are allocated */
static int
-parse_global_data(xmlNode *root, struct global_data *gdata)
+parse_global_data(xmlNode *root, struct global_data *gdata,
+ struct global_data *parent_data)
{
int error;
return error;
}
+ if (parent_data == NULL)
+ return 0;
+
+ /*
+ * FIXME (now) Prepare the callers to receive positive error values,
+ * which means the file was successfully parsed but is has a logic error
+ * (in this case, session ID or serial don't match parent's).
+ */
+ if (strcmp(parent_data->session_id, gdata->session_id) != 0) {
+ pr_info("Object session id doesn't match parent's session id");
+ return EINVAL;
+ }
+
+ if (parent_data->serial != gdata->serial) {
+ pr_info("Object serial doesn't match parent's serial");
+ return EINVAL;
+ }
+
return 0;
}
}
static int
-parse_notification(const char *path,
- struct update_notification **file)
+parse_publish(xmlNode *root, bool parse_hash, struct publish **publish)
+{
+ struct publish *tmp;
+ char *base64_str;
+ int error;
+
+ error = publish_create(&tmp);
+ if (error)
+ return error;
+
+ error = parse_doc_data(root, parse_hash, &tmp->doc_data);
+ if (error)
+ goto release_tmp;
+
+ error = parse_string(root, NULL, &base64_str);
+ if (error)
+ goto release_tmp;
+
+ error = base64_read(base64_str, &tmp->content, &tmp->content_len);
+ if (error)
+ goto release_base64;
+
+ free(base64_str);
+ *publish = tmp;
+ return 0;
+release_base64:
+ free(base64_str);
+release_tmp:
+ publish_destroy(tmp);
+ return error;
+}
+
+static int
+write_from_uri(char const *location, unsigned char *content, size_t content_len)
+{
+ struct rpki_uri *uri;
+ struct stat stat;
+ FILE *out;
+ size_t written;
+ int error;
+
+ error = uri_create_mixed_str(&uri, location, strlen(location));
+ if (error)
+ return error;
+
+ error = create_dir_recursive(uri_get_local(uri));
+ if (error) {
+ uri_refput(uri);
+ return error;
+ }
+
+ error = file_write(uri_get_local(uri), &out, &stat);
+ if (error) {
+ uri_refput(uri);
+ return error;
+ }
+
+ written = fwrite(content, sizeof(unsigned char), content_len, out);
+ if (written != content_len) {
+ uri_refput(uri);
+ file_close(out);
+ return pr_err("Coudln't write bytes to file %s",
+ uri_get_local(uri));
+ }
+
+ uri_refput(uri);
+ file_close(out);
+ return 0;
+}
+
+static int
+parse_snapshot_publish_list(xmlNode *root, struct snapshot *file)
+{
+ struct publish *tmp;
+ xmlNode *cur_node;
+ int error;
+
+ /* Only publish elements are expected, already validated by syntax */
+ for (cur_node = root->children; cur_node; cur_node = cur_node->next) {
+ if (xmlStrEqual(cur_node->name, BAD_CAST RRDP_ELEM_PUBLISH)) {
+ tmp = NULL;
+ error = parse_publish(cur_node, false, &tmp);
+ if (error)
+ return error;
+
+ error = write_from_uri(tmp->doc_data.uri, tmp->content,
+ tmp->content_len);
+ publish_destroy(tmp);
+ if (error)
+ return error;
+ }
+ }
+
+ return 0;
+}
+
+static int
+parse_notification(const char *path, struct update_notification **file)
{
xmlDoc *doc;
xmlNode *root;
error = get_root_element(doc, &root);
if (error)
- return error;
+ goto release_update;
/* FIXME (now) validate version, namespace, etc. */
- error = parse_global_data(root, &tmp->gdata);
+ error = parse_global_data(root, &tmp->global_data, NULL);
if (error)
goto release_update;
return error;
}
-static size_t
-write_local(unsigned char *content, size_t size, size_t nmemb, void *arg)
+static int
+parse_snapshot(const char *path, struct update_notification *parent,
+ struct snapshot **file)
{
- FILE *fd = arg;
- size_t read = size * nmemb;
- size_t written;
+ xmlDoc *doc;
+ xmlNode *root;
+ struct snapshot *tmp;
+ struct xml_source *source;
+ int error;
- written = fwrite(content, size, nmemb, fd);
- if (written != nmemb)
- return -EINVAL;
+ root = NULL;
- return read;
+ error = relax_ng_validate(path, &doc);
+ if (error)
+ return error;
+
+ error = snapshot_create(&tmp);
+ if (error)
+ goto release_doc;
+
+ error = get_root_element(doc, &root);
+ if (error)
+ goto release_snapshot;
+
+ /* FIXME (now) validate hash, version, namespace, etc. */
+ error = xml_source_create(&source);
+ if (error)
+ goto release_snapshot;
+
+ tmp->source = source;
+ error = xml_source_set(source, doc);
+ if (error)
+ goto release_snapshot;
+
+ error = parse_global_data(root, &tmp->global_data,
+ &parent->global_data);
+ if (error)
+ goto release_snapshot;
+
+ error = parse_snapshot_publish_list(root, tmp);
+ if (error)
+ goto release_snapshot;
+
+ *file = tmp;
+ /* Error 0 is ok */
+ goto release_doc;
+release_snapshot:
+ snapshot_destroy(tmp);
+release_doc:
+ xmlFreeDoc(doc);
+ return error;
}
-/* FIXME (now) Receive an **update_notification? */
int
-rrdp_parse_notification(struct rpki_uri *uri)
+rrdp_parse_notification(struct rpki_uri *uri,
+ struct update_notification **result)
{
- struct update_notification *tmp;
int error;
if (uri == NULL || uri_is_rsync(uri))
pr_crit("Wrong call, trying to parse a non HTTPS URI");
+ /*
+ * FIXME (now) Add "If-Modified-Since" header (see rfc8182#section-4.2)
+ */
error = http_download_file(uri, write_local);
if (error)
return error;
- error = parse_notification(uri_get_local(uri), &tmp);
+ fnstack_push_uri(uri);
+ error = parse_notification(uri_get_local(uri), result);
+ if (error) {
+ fnstack_pop();
+ return error;
+ }
+
+ return 0;
+}
+
+int
+rrdp_parse_snapshot(struct update_notification *parent,
+ struct snapshot **result)
+{
+ struct rpki_uri *uri;
+ struct snapshot *tmp;
+ int error;
+
+ error = uri_create_https_str(&uri, parent->snapshot.uri,
+ strlen(parent->snapshot.uri));
if (error)
return error;
- /* FIXME (now) This is just a test, must be removed */
- update_notification_destroy(tmp);
+ error = http_download_file(uri, write_local);
+ if (error)
+ goto release_uri;
+
+ fnstack_push_uri(uri);
+ error = parse_snapshot(uri_get_local(uri), parent, &tmp);
+ if (error)
+ goto release_uri;
- /* result = tmp; */
+ uri_refput(uri);
+ *result = tmp;
+ fnstack_pop();
return 0;
+release_uri:
+ fnstack_pop();
+ uri_refput(uri);
+ return error;
}
#include "rrdp/rrdp_objects.h"
#include "uri.h"
-int rrdp_parse_notification(struct rpki_uri *);
+int rrdp_parse_notification(struct rpki_uri *, struct update_notification **);
+int rrdp_parse_snapshot(struct update_notification *, struct snapshot **);
#endif /* SRC_RRDP_RRDP_PARSER_H_ */
if (global[i] == '/') {
slashes++;
if (slashes == 4)
- return uri_create_str(dst, global, i);
+ return uri_create_rsync_str(dst, global, i);
}
}
#include "data_structure/array_list.h"
#include "object/router_key.h"
#include "object/tal.h"
+#include "rrdp/db_rrdp.h"
#include "rtr/db/db_table.h"
#include "slurm/slurm_loader.h"
serial_t next_serial;
uint16_t v0_session_id;
uint16_t v1_session_id;
+
+ /* RRDP URIs known from certificates */
+ struct db_rrdp *rrdp_uris;
+
} state;
/** Read/write lock, which protects @state and its inhabitants. */
: (0xFFFFu);
state.slurm = NULL;
+ error = db_rrdp_create(&state.rrdp_uris);
+ if (error)
+ goto release_deltas;
error = pthread_rwlock_init(&state_lock, NULL);
if (error) {
- deltas_db_cleanup(&state.deltas, deltagroup_cleanup);
- return pr_errno(error, "state pthread_rwlock_init() errored");
+ error = pr_errno(error, "state pthread_rwlock_init() errored");
+ goto release_db_rrdp;
}
error = pthread_rwlock_init(&table_lock, NULL);
if (error) {
- pthread_rwlock_destroy(&state_lock);
- deltas_db_cleanup(&state.deltas, deltagroup_cleanup);
- return pr_errno(error, "table pthread_rwlock_init() errored");
+ error = pr_errno(error, "table pthread_rwlock_init() errored");
+ goto release_state_lock;
}
return 0;
+release_state_lock:
+ pthread_rwlock_destroy(&state_lock);
+release_db_rrdp:
+ db_rddp_destroy(state.rrdp_uris);
+release_deltas:
+ deltas_db_cleanup(&state.deltas, deltagroup_cleanup);
+ return error;
}
void
if (state.slurm != NULL)
db_slurm_destroy(state.slurm);
deltas_db_cleanup(&state.deltas, deltagroup_cleanup);
+ db_rddp_destroy(state.rrdp_uris);
/* Nothing to do with error codes from now on */
pthread_rwlock_destroy(&state_lock);
pthread_rwlock_destroy(&table_lock);
rwlock_unlock(lock); \
return error;
+#define RLOCK_HANDLER(lock, cb) \
+ int error; \
+ rwlock_read_lock(lock); \
+ error = cb; \
+ rwlock_unlock(lock); \
+ return error;
+
int
handle_roa_v4(uint32_t as, struct ipv4_prefix const *prefix,
uint8_t max_length, void *arg)
rtrhandler_handle_router_key(arg, ski, as, spk))
}
+enum rrdp_uri_cmp_result
+rrdp_uri_cmp(char const *uri, char const *session_id, unsigned long serial)
+{
+ RLOCK_HANDLER(&state_lock,
+ db_rrdp_cmp_uri(state.rrdp_uris, uri, session_id, serial))
+}
+
+int
+rrdp_uri_update(char const *uri, char const *session_id, unsigned long serial)
+{
+ WLOCK_HANDLER(&state_lock,
+ db_rrdp_add_uri(state.rrdp_uris, uri, session_id, serial))
+}
+
static int
__perform_standalone_validation(struct db_table **result)
{
old_base = NULL;
new_base = NULL;
+ /* FIXME (now) identify if RRDP is being utilized? */
error = __perform_standalone_validation(&new_base);
if (error)
return error;
int handle_router_key(unsigned char const *, uint32_t, unsigned char const *,
void *);
+enum rrdp_uri_cmp_result rrdp_uri_cmp(char const *, char const *,
+ unsigned long);
+int rrdp_uri_update(char const *, char const *, unsigned long);
+
uint16_t get_current_session_id(uint8_t);
#endif /* SRC_VRPS_H_ */
char addr_buffer2[INET6_ADDRSTRLEN];
struct validation_handler validation_handler;
+
+ /*
+ * Handler of RRDP functions, this handler can access data from the
+ * main thread, so that multiple threads can use it.
+ */
+ struct rrdp_handler rrdp_handler;
};
/*
*/
int
validation_prepare(struct validation **out, struct tal *tal,
- struct validation_handler *validation_handler)
+ struct validation_handler *validation_handler,
+ struct rrdp_handler *rrdp_handler)
{
struct validation *result;
X509_VERIFY_PARAM *params;
result->pubkey_state = PKS_UNTESTED;
result->validation_handler = *validation_handler;
+ result->rrdp_handler = *rrdp_handler;
result->x509_data.params = params; /* Ownership transfered */
*out = result;
{
return &state->validation_handler;
}
+
+struct rrdp_handler const *
+validation_get_rrdp_handler(struct validation *state)
+{
+ return &state->rrdp_handler;
+}
#include "cert_stack.h"
#include "validation_handler.h"
#include "object/tal.h"
+#include "rrdp/rrdp_handler.h"
#include "rsync/rsync.h"
struct validation;
int validation_prepare(struct validation **, struct tal *,
- struct validation_handler *);
+ struct validation_handler *, struct rrdp_handler *);
void validation_destroy(struct validation *);
struct tal *validation_tal(struct validation *);
struct validation_handler const *
validation_get_validation_handler(struct validation *);
+struct rrdp_handler const *
+validation_get_rrdp_handler(struct validation *);
+
#endif /* SRC_STATE_H_ */
}
int
-uri_create_str(struct rpki_uri **uri, char const *guri, size_t guri_len)
+uri_create_rsync_str(struct rpki_uri **uri, char const *guri, size_t guri_len)
{
return uri_create(uri, URI_VALID_RSYNC, guri, guri_len);
}
+int
+uri_create_https_str(struct rpki_uri **uri, char const *guri, size_t guri_len)
+{
+ return uri_create(uri, URI_VALID_HTTPS, guri, guri_len);
+}
+
/* A URI that can be rsync or https */
int
uri_create_mixed_str(struct rpki_uri **uri, char const *guri, size_t guri_len)
struct rpki_uri;
-int uri_create_str(struct rpki_uri **, char const *, size_t);
+int uri_create_rsync_str(struct rpki_uri **, char const *, size_t);
+int uri_create_https_str(struct rpki_uri **, char const *, size_t);
int uri_create_mixed_str(struct rpki_uri **, char const *, size_t);
int uri_create_mft(struct rpki_uri **, struct rpki_uri *, IA5String_t *);
int uri_create_ad(struct rpki_uri **, ACCESS_DESCRIPTION *, int);
struct rpki_uri *ancestor_uri;
struct rpki_uri *descendant_uri;
- ck_assert_int_eq(0, uri_create_str(&ancestor_uri, ancestor,
+ ck_assert_int_eq(0, uri_create_rsync_str(&ancestor_uri, ancestor,
strlen(ancestor)));
- ck_assert_int_eq(0, uri_create_str(&descendant_uri, descendant,
+ ck_assert_int_eq(0, uri_create_rsync_str(&descendant_uri, descendant,
strlen(descendant)));
ck_assert_int_eq(is_descendant(ancestor_uri, descendant_uri), expected);
__mark_as_downloaded(char *uri_str, struct uri_list *visited_uris)
{
struct rpki_uri *uri;
- ck_assert_int_eq(0, uri_create_str(&uri, uri_str, strlen(uri_str)));
+ ck_assert_int_eq(0, uri_create_rsync_str(&uri, uri_str, strlen(uri_str)));
ck_assert_int_eq(mark_as_downloaded(uri, visited_uris), 0);
uri_refput(uri);
}
assert_downloaded(char *uri_str, struct uri_list *visited_uris, bool expected)
{
struct rpki_uri *uri;
- ck_assert_int_eq(0, uri_create_str(&uri, uri_str, strlen(uri_str)));
+ ck_assert_int_eq(0, uri_create_rsync_str(&uri, uri_str, strlen(uri_str)));
ck_assert_int_eq(is_already_downloaded(uri, visited_uris), expected);
uri_refput(uri);
}
struct rpki_uri *src;
struct rpki_uri *dst;
- ck_assert_int_eq(0, uri_create_str(&src, test, strlen(test)));
+ ck_assert_int_eq(0, uri_create_rsync_str(&src, test, strlen(test)));
ck_assert_int_eq(handle_root_strategy(src, &dst), 0);
ck_assert_str_eq(uri_get_global(dst), expected);
#include "log.c"
#include "output_printer.c"
#include "object/router_key.c"
+#include "rrdp/db_rrdp.c"
#include "rtr/db/delta.c"
#include "rtr/db/db_table.c"
#include "rtr/db/rtr_db_impersonator.c"
#include "output_printer.c"
#include "crypto/base64.c"
#include "object/router_key.c"
+#include "rrdp/db_rrdp.c"
#include "rtr/pdu.c"
#include "rtr/pdu_handler.c"
#include "rtr/primitive_reader.c"
int
validation_prepare(struct validation **out, struct tal *tal,
- struct validation_handler *validation_handler)
+ struct validation_handler *validation_handler,
+ struct rrdp_handler *rrdp_handler)
{
return 0;
}