From de31ab82192047af157d45368cd81ef0bcf21ad2 Mon Sep 17 00:00:00 2001 From: pcarana Date: Tue, 1 Sep 2020 14:29:20 -0500 Subject: [PATCH] Use a local workspace fort RRDP related files, fixes #39. +Whenever an RRDP file is identified (ie. update notification URI) create a directory at '--local-repository' where all of the RRDP files (XMLs as well as 'publish' elements at those snapshot/delta files) will be created and read. +The rsync URIs at the publish/withdraw elements, are mapped to the location <--local-repository>//. Eg. if '--local-repository=/tmp/fort' and the current workspace (each TAL has its own workspace) is 'ABC', then the URI 'rsync://example.com/foo/bar.cer' will be created at '/tmp/fort/ABC/example.com/foo/bar.cer'. -RSYNC repositories are still created at '--local-repository'. --- src/common.c | 11 ++- src/crypto/hash.c | 25 +++++ src/crypto/hash.h | 1 + src/object/certificate.c | 190 +++++++++++++++++++++++++++++-------- src/object/manifest.c | 12 ++- src/object/manifest.h | 3 +- src/rrdp/db/db_rrdp.c | 97 +++++++++++++++++-- src/rrdp/db/db_rrdp.h | 2 + src/rrdp/db/db_rrdp_uris.c | 58 +++++++++++ src/rrdp/db/db_rrdp_uris.h | 4 + src/rrdp/rrdp_parser.c | 12 +-- src/rsync/rsync.c | 44 ++++++--- src/state.c | 16 +++- src/state.h | 1 + src/uri.c | 85 ++++++++++++++--- src/uri.h | 13 ++- 16 files changed, 473 insertions(+), 101 deletions(-) diff --git a/src/common.c b/src/common.c index 64e2b4e7..85d86893 100644 --- a/src/common.c +++ b/src/common.c @@ -205,13 +205,15 @@ valid_file_or_dir(char const *location, bool check_file, bool check_dir, result = false; file = fopen(location, "rb"); if (file == NULL) { - cb(errno, "Could not open location '%s'", - location); + if (cb != NULL) + cb(errno, "Could not open location '%s'", + location); return false; } if (fstat(fileno(file), &attr) == -1) { - cb(errno, "fstat(%s) failed", location); + if (cb != NULL) + cb(errno, "fstat(%s) failed", location); goto end; } @@ -226,7 +228,8 @@ valid_file_or_dir(char const *location, bool check_file, bool check_dir, end: if (fclose(file) == -1) - cb(errno, "fclose() failed"); + if (cb != NULL) + cb(errno, "fclose() failed"); return result; } diff --git a/src/crypto/hash.c b/src/crypto/hash.c index a39ee135..4b6de3c8 100644 --- a/src/crypto/hash.c +++ b/src/crypto/hash.c @@ -236,3 +236,28 @@ hash_validate_octet_string(char const *algorithm, return hash_validate(algorithm, expected->buf, expected->size, data->buf, data->size); } + +/* + * Hash the @str using the specified @algorithm, setting the result at the + * @result buffer and its length at @result_len. + * + * Return 0 on success, any other value means error. + */ +int +hash_str(char const *algorithm, char const *str, unsigned char *result, + unsigned int *result_len) +{ + unsigned char *src; + int error; + + src = malloc(strlen(str)); + if (src == NULL) + return pr_enomem(); + + memcpy(src, str, strlen(str)); + + error = hash_buffer(algorithm, src, strlen(str), result, result_len); + + free(src); + return error; +} diff --git a/src/crypto/hash.h b/src/crypto/hash.h index 65910287..d9f31af2 100644 --- a/src/crypto/hash.h +++ b/src/crypto/hash.h @@ -18,5 +18,6 @@ int hash_validate_octet_string(char const *, OCTET_STRING_t const*, int hash_local_file(char const *, char const *, unsigned char *, unsigned int *); +int hash_str(char const *, char const *, unsigned char *, unsigned int *); #endif /* SRC_HASH_H_ */ diff --git a/src/object/certificate.c b/src/object/certificate.c index 49fa4a89..2446ab00 100644 --- a/src/object/certificate.c +++ b/src/object/certificate.c @@ -1536,9 +1536,10 @@ handle_ad(char const *ia_name, SIGNATURE_INFO_ACCESS *ia, char const *ad_name, int ad_nid, int uri_flags, bool required, int (*cb)(struct rpki_uri *, uint8_t, void *), void *arg) { -# define AD_METHOD (uri_flags == URI_VALID_RSYNC ? \ +# define AD_METHOD ((uri_flags & URI_VALID_RSYNC) == URI_VALID_RSYNC ? \ "RSYNC" : \ - ((uri_flags == URI_VALID_HTTPS) ? "HTTPS" : "RSYNC/HTTPS")) + (((uri_flags & URI_VALID_HTTPS) == URI_VALID_HTTPS) ? \ + "HTTPS" : "RSYNC/HTTPS")) ACCESS_DESCRIPTION *ad; struct rpki_uri *uri; bool found = false; @@ -1638,7 +1639,8 @@ handle_sia_ca(X509_EXTENSION *ext, void *arg) /* HTTPS RRDP */ error = handle_ad("SIA", sia, "rpkiNotify", nid_rpkiNotify(), - URI_VALID_HTTPS, false, handle_rpkiNotify, &uris->rpkiNotify); + URI_VALID_HTTPS | URI_USE_RRDP_WORKSPACE, false, handle_rpkiNotify, + &uris->rpkiNotify); if (error) goto end; @@ -1905,7 +1907,7 @@ get_certificate_type(X509 *cert, bool is_ta) * verified to comply with rfc6487#section-4.8.7 */ static int -force_aia_validation(struct rpki_uri *caIssuers, X509 *son, bool is_ta_child) +force_aia_validation(struct rpki_uri *caIssuers, X509 *son) { X509 *parent; struct rfc5280_name *son_name; @@ -1914,9 +1916,9 @@ force_aia_validation(struct rpki_uri *caIssuers, X509 *son, bool is_ta_child) pr_val_debug("AIA's URI didn't matched parent URI, trying to SYNC"); - /* RSYNC is still the preferred access mechanism */ + /* RSYNC is still the preferred access mechanism, force the sync */ do { - error = download_files(caIssuers, false, false); + error = download_files(caIssuers, false, true); if (!error) break; if (error == EREQFAILED) { @@ -1926,19 +1928,9 @@ force_aia_validation(struct rpki_uri *caIssuers, X509 *son, bool is_ta_child) return error; } while (0); - /* - * Consider the scenario where the TA was fetched from a distinct URI - * (maybe an HTTPS URI), in that case keep doing the validation since - * there's no "right" way to validate the AIA, that's where the flag - * 'is_ta_child' comes into play. - * - * A possible way is to load the certificate from the cert_stack, but - * the location itself isn't strictly the location from the AIA (the - * solution works, but maybe it isn't right. - */ error = certificate_load(caIssuers, &parent); if (error) - return !is_ta_child ? error : 0; + return error; error = x509_name_decode(X509_get_subject_name(parent), "subject", &parent_name); @@ -2003,20 +1995,20 @@ certificate_validate_aia(struct rpki_uri *caIssuers, X509 *cert) * of the certificate where this Issuer is the subject (the issuer's * immediate superior certificate)." * - * January 2020: this isn't a problem, all five RIRs are using one URI - * at their TALs, that matches AIA from the first level root certificate - * childs. Anyways, we'll try to consult the subject at IETF. + * As of today, this is a common scenario, since most of the TALs have + * an HTTPS URI. */ if (uri_equals(caIssuers, parent)) return 0; /* - * URI didn't match, try to match the immediate superior subject with - * the current issuer. This will force an RSYNC of AIA's URI, load - * the certificate and do the comparison. + * Avoid the check at direct TA childs, otherwise try to match the + * immediate superior subject with the current issuer. This will force + * an RSYNC of AIA's URI, load the certificate and do the comparison. */ - return force_aia_validation(caIssuers, cert, - certstack_get_x509_num(validation_certstack(state)) == 1); + return certstack_get_x509_num(validation_certstack(state)) == 1 ? + 0 : + force_aia_validation(caIssuers, cert); } /* @@ -2034,15 +2026,87 @@ verify_mft_loc(struct rpki_uri *mft_uri) return 0; } +/* + * Verify the manifest location at the local RRDP workspace. If the manifest + * exists, then update @mft_uri so that its location be at the workspace. + * + * Don't log in case the @mft_uri doesn't exist at the RRDP workspace. + */ +static int +verify_rrdp_mft_loc(struct rpki_uri **mft_uri) +{ + struct rpki_uri *tmp; + int error; + + if (db_rrdp_uris_workspace_get() == NULL) + return -ENOENT; + + tmp = NULL; + error = uri_create_rsync_str_rrdp(&tmp, uri_get_global(*mft_uri), + uri_get_global_len(*mft_uri)); + if (error) + return error; + + if (!valid_file_or_dir(uri_get_local(tmp), true, false, NULL)) { + uri_refput(tmp); + return -EINVAL; + } + + uri_refput(*mft_uri); + *mft_uri = tmp; + return 0; +} + +static int +replace_rrdp_mft_uri(struct sia_uri *sia_mft) +{ + struct rpki_uri *tmp; + int error; + + tmp = NULL; + error = uri_create_rsync_str_rrdp(&tmp, + uri_get_global(sia_mft->uri), + uri_get_global_len(sia_mft->uri)); + if (error) + return error; + + uri_refput(sia_mft->uri); + sia_mft->uri = tmp; + + return 0; +} + static int exec_rrdp_method(struct sia_ca_uris *sia_uris) { - return rrdp_load(sia_uris->rpkiNotify.uri); + int error; + + /* Start working on the RRDP local workspace */ + error = db_rrdp_uris_workspace_enable(); + if (error) + return error; + + error = rrdp_load(sia_uris->rpkiNotify.uri); + if (error) { + db_rrdp_uris_workspace_disable(); + return error; + } + + /* Successfully loaded (or no updates yet), update MFT local URI */ + error = replace_rrdp_mft_uri(&sia_uris->mft); + if (error) { + db_rrdp_uris_workspace_disable(); + return error; + } + + return 0; } static int exec_rsync_method(struct sia_ca_uris *sia_uris) { + /* Stop working on the RRDP local workspace */ + db_rrdp_uris_workspace_disable(); return download_files(sia_uris->caRepository.uri, false, false); } @@ -2053,33 +2117,56 @@ exec_rsync_method(struct sia_ca_uris *sia_uris) */ static int use_access_method(struct sia_ca_uris *sia_uris, - access_method_exec rsync_cb, access_method_exec rrdp_cb, + access_method_exec rsync_cb, access_method_exec rrdp_cb, bool new_level, bool *retry_repo_sync) { access_method_exec *cb_primary; access_method_exec *cb_secondary; rrdp_req_status_t rrdp_req_status; bool primary_rrdp; + int upd_error; int error; + /* + * By default, RRDP has a greater priority than rsync. + * See "http.priority" default value. + */ + primary_rrdp = true; + (*retry_repo_sync) = true; + + /* + * Very specific scenario, yet possible: + * - Still working at the same repository level + * - The previous object was working on an RRDP repository + * - This certificate doesn't have an update notification URI + * + * Probably the object does exist at the RRDP repository, so check if + * that's the case. Otherwise, just keep going. + * + * The main reason, is a (possible) hole at RFC 8182. Apparently, the + * CA childs aren't obligated to have the same RRDP accessMethod than + * their parent, so there's no problem if they don't use it at all; not + * even if such childs (and even the grandchilds or anyone below that + * level) "reside" at the RRDP repository. + */ + if (!new_level && db_rrdp_uris_workspace_get() != NULL && + sia_uris->rpkiNotify.uri == NULL && + verify_rrdp_mft_loc(&sia_uris->mft.uri) == 0) { + (*retry_repo_sync) = false; + return replace_rrdp_mft_uri(&sia_uris->mft); + } + /* * RSYNC will always be present (at least for now, see * rfc6487#section-4.8.8.1). If rsync is disabled, the cb will take * care of that. */ - (*retry_repo_sync) = true; - if (sia_uris->rpkiNotify.uri == NULL || !config_get_http_enabled()) { + if (sia_uris->rpkiNotify.uri == NULL) { + primary_rrdp = false; error = rsync_cb(sia_uris); goto verify_mft; } - /* RSYNC disabled, and RRDP is present, use it */ - if (!config_get_rsync_enabled()) { - (*retry_repo_sync) = false; - error = rrdp_cb(sia_uris); - goto verify_mft; - } - /* * There isn't any restriction about the preferred access method of * children CAs being the same as the parent CA. @@ -2097,8 +2184,13 @@ use_access_method(struct sia_ca_uris *sia_uris, error = db_rrdp_uris_get_request_status( uri_get_global(sia_uris->rpkiNotify.uri), &rrdp_req_status); if (error == 0 && rrdp_req_status == RRDP_URI_REQ_VISITED) { + error = db_rrdp_uris_workspace_enable(); + if (error) { + db_rrdp_uris_workspace_disable(); + return error; + } (*retry_repo_sync) = false; - return 0; + return replace_rrdp_mft_uri(&sia_uris->mft); } /* Use CA's or configured priority? */ @@ -2136,6 +2228,7 @@ use_access_method(struct sia_ca_uris *sia_uris, uri_get_global(sia_uris->rpkiNotify.uri)); } + /* Retry if rrdp was the first option but failed */ (*retry_repo_sync) = primary_rrdp; error = cb_secondary(sia_uris); /* No need to remember the working repository anymore */ @@ -2163,6 +2256,15 @@ verify_mft: default: return error; } + + /* Error and the primary access method was RRDP? Use its workspace */ + if (error && primary_rrdp) { + db_rrdp_uris_workspace_enable(); + upd_error = replace_rrdp_mft_uri(&sia_uris->mft); + if (upd_error) + return upd_error; + } + /* Look for the manifest */ return verify_mft_loc(sia_uris->mft.uri); } @@ -2211,7 +2313,7 @@ get_rsync_server_uri(struct rpki_uri *src, char **result, size_t *result_len) static int set_repository_level(bool is_ta, struct validation *state, - struct rpki_uri *cert_uri, struct sia_ca_uris *sia_uris) + struct rpki_uri *cert_uri, struct sia_ca_uris *sia_uris, bool *updated) { char *parent_server, *current_server; size_t parent_server_len, current_server_len; @@ -2260,6 +2362,7 @@ end: free(parent_server); free(current_server); + (*updated) = update; return 0; } @@ -2281,6 +2384,7 @@ certificate_traverse(struct rpp *rpp_parent, struct rpki_uri *cert_uri) enum cert_type type; struct rpp *pp; bool repo_retry; + bool new_level; int error; state = state_retrieve(); @@ -2374,7 +2478,9 @@ certificate_traverse(struct rpp *rpp_parent, struct rpki_uri *cert_uri) } /* Identify if this is a new repository before fetching it */ - error = set_repository_level(IS_TA, state, cert_uri, &sia_uris); + new_level = false; + error = set_repository_level(IS_TA, state, cert_uri, &sia_uris, + &new_level); if (error) goto revert_uris; @@ -2386,12 +2492,12 @@ certificate_traverse(struct rpp *rpp_parent, struct rpki_uri *cert_uri) * * Trying to avoid this issue, download the CA repository and validate * manifest (and its content) again. - * + * * Avoid to re-download the repo if the mft was fetched with RRDP. */ repo_retry = true; error = use_access_method(&sia_uris, exec_rsync_method, - exec_rrdp_method, &repo_retry); + exec_rrdp_method, new_level, &repo_retry); if (error) goto revert_uris; @@ -2404,7 +2510,7 @@ certificate_traverse(struct rpp *rpp_parent, struct rpki_uri *cert_uri) cert = NULL; /* Ownership stolen */ - error = handle_manifest(sia_uris.mft.uri, &pp); + error = handle_manifest(sia_uris.mft.uri, !repo_retry, &pp); if (error == 0 || !repo_retry) break; diff --git a/src/object/manifest.c b/src/object/manifest.c index fd269f59..327fe528 100644 --- a/src/object/manifest.c +++ b/src/object/manifest.c @@ -150,7 +150,8 @@ validate_manifest(struct Manifest *manifest) } static int -build_rpp(struct Manifest *mft, struct rpki_uri *mft_uri, struct rpp **pp) +build_rpp(struct Manifest *mft, struct rpki_uri *mft_uri, bool rrdp_workspace, + struct rpp **pp) { int i; struct FileAndHash *fah; @@ -164,7 +165,8 @@ build_rpp(struct Manifest *mft, struct rpki_uri *mft_uri, struct rpp **pp) for (i = 0; i < mft->fileList.list.count; i++) { fah = mft->fileList.list.array[i]; - error = uri_create_mft(&uri, mft_uri, &fah->file); + error = uri_create_mft(&uri, mft_uri, &fah->file, + rrdp_workspace); /* * Not handling ENOTRSYNC is fine because the manifest URL * should have been RSYNC. Something went wrong if an RSYNC URL @@ -225,10 +227,10 @@ fail: /** * Validates the manifest pointed by @uri, returns the RPP described by it in - * @pp. + * @pp. If @rrdp_workspace is true, use the local RRDP repository. */ int -handle_manifest(struct rpki_uri *uri, struct rpp **pp) +handle_manifest(struct rpki_uri *uri, bool rrdp_workspace, struct rpp **pp) { static OID oid = OID_MANIFEST; struct oid_arcs arcs = OID2ARCS("manifest", oid); @@ -251,7 +253,7 @@ handle_manifest(struct rpki_uri *uri, struct rpp **pp) goto revert_sobj; /* Initialize out parameter (@pp) */ - error = build_rpp(mft, uri, pp); + error = build_rpp(mft, uri, rrdp_workspace, pp); if (error) goto revert_manifest; diff --git a/src/object/manifest.h b/src/object/manifest.h index a9cfcb99..dbfc0bd6 100644 --- a/src/object/manifest.h +++ b/src/object/manifest.h @@ -1,9 +1,10 @@ #ifndef SRC_OBJECT_MANIFEST_H_ #define SRC_OBJECT_MANIFEST_H_ +#include #include "uri.h" #include "rpp.h" -int handle_manifest(struct rpki_uri *, struct rpp **); +int handle_manifest(struct rpki_uri *, bool, struct rpp **); #endif /* SRC_OBJECT_MANIFEST_H_ */ diff --git a/src/rrdp/db/db_rrdp.c b/src/rrdp/db/db_rrdp.c index b6e0363b..fe4723fd 100644 --- a/src/rrdp/db/db_rrdp.c +++ b/src/rrdp/db/db_rrdp.c @@ -2,15 +2,18 @@ #include #include +#include #include #include #include +#include "crypto/hash.h" #include "common.h" #include "log.h" struct tal_elem { char *file_name; struct db_rrdp_uri *uris; + char *workspace; bool visited; SLIST_ENTRY(tal_elem) next; }; @@ -26,6 +29,60 @@ static struct db_rrdp db; /** Read/write lock, which protects @db. */ static pthread_rwlock_t lock; +/* + * Creates an ID for the RRDP local workspace. + * + * The ID is generated using the hash (sha-1) of @base. The first 4 bytes of + * the hash are "stringified" (8 chars) and a '/' is added at the end to + * (later) facilitate the concatenation of the ID at --local-repository. + * + * The ID is allocated at @result. + */ +static int +get_workspace_path(char const *base, char **result) +{ +/* SHA1 produces 20 bytes */ +#define HASH_LEN 20 +/* We'll use the first 4 bytes (8 chars) */ +#define OUT_LEN 8 + unsigned char *hash; + unsigned int hash_len; + unsigned int i; + char *tmp; + char *ptr; + int error; + + hash = malloc(HASH_LEN * sizeof(unsigned char)); + if (hash == NULL) + return pr_enomem(); + + hash_len = 0; + error = hash_str("sha1", base, hash, &hash_len); + if (error) { + free(hash); + return error; + } + + /* Get the first bytes + one slash + NUL char */ + tmp = malloc(OUT_LEN + 2); + if (tmp == NULL) { + free(hash); + return pr_enomem(); + } + + ptr = tmp; + for (i = 0; i < OUT_LEN / 2; i++) { + sprintf(ptr, "%02X", hash[i]); + ptr += 2; + } + tmp[OUT_LEN] = '/'; + tmp[OUT_LEN + 1] = '\0'; + + free(hash); + *result = tmp; + return 0; +} + static int tal_elem_create(struct tal_elem **elem, char const *name) { @@ -39,22 +96,30 @@ tal_elem_create(struct tal_elem **elem, char const *name) tmp_uris = NULL; error = db_rrdp_uris_create(&tmp_uris); - if (error) { - free(tmp); - return error; - } + if (error) + goto end1; tmp->uris = tmp_uris; tmp->visited = true; tmp->file_name = strdup(name); if (tmp->file_name == NULL) { - db_rrdp_uris_destroy(tmp->uris); - free(tmp); - return pr_enomem(); + error = pr_enomem(); + goto end2; } + error = get_workspace_path(name, &tmp->workspace); + if (error) + goto end3; + *elem = tmp; return 0; +end3: + free(tmp->file_name); +end2: + db_rrdp_uris_destroy(tmp->uris); +end1: + free(tmp); + return error; } static void @@ -64,6 +129,7 @@ tal_elem_destroy(struct tal_elem *elem, bool remove_local) db_rrdp_uris_remove_all_local(elem->uris); db_rrdp_uris_destroy(elem->uris); free(elem->file_name); + free(elem->workspace); free(elem); } @@ -157,7 +223,22 @@ db_rrdp_get_uris(char const *tal_name) struct tal_elem *found; found = db_rrdp_find_tal(tal_name); - return found != NULL ? found->uris : NULL; + if (found == NULL) + pr_crit("db_rrdp_find_tal() returned NULL, means it hasn't been initialized"); + + return found->uris; +} + +char const * +db_rrdp_get_workspace(char const *tal_name) +{ + struct tal_elem *found; + + found = db_rrdp_find_tal(tal_name); + if (found == NULL) + pr_crit("db_rrdp_find_tal() returned NULL, means it hasn't been initialized"); + + return found->workspace; } /* Set all tals to non-visited */ diff --git a/src/rrdp/db/db_rrdp.h b/src/rrdp/db/db_rrdp.h index b0440e30..0607f99c 100644 --- a/src/rrdp/db/db_rrdp.h +++ b/src/rrdp/db/db_rrdp.h @@ -8,7 +8,9 @@ void db_rrdp_cleanup(void); int db_rrdp_add_tal(char const *); void db_rrdp_rem_tal(char const *); + struct db_rrdp_uri *db_rrdp_get_uris(char const *); +char const *db_rrdp_get_workspace(char const *); void db_rrdp_reset_visited_tals(void); void db_rrdp_rem_nonvisited_tals(void); diff --git a/src/rrdp/db/db_rrdp_uris.c b/src/rrdp/db/db_rrdp_uris.c index 082db39c..9928a7eb 100644 --- a/src/rrdp/db/db_rrdp_uris.c +++ b/src/rrdp/db/db_rrdp_uris.c @@ -24,6 +24,7 @@ struct uris_table { struct db_rrdp_uri { struct uris_table *table; + char const *current_workspace; }; static int @@ -115,6 +116,19 @@ get_thread_rrdp_uris(struct db_rrdp_uri **result) return 0; } +static int +get_thread_rrdp_workspace(char const **result) +{ + struct validation *state; + + state = state_retrieve(); + if (state == NULL) + return pr_val_err("No state related to this thread"); + + *result = validation_get_rrdp_workspace(state); + return 0; +} + int db_rrdp_uris_create(struct db_rrdp_uri **uris) { @@ -125,6 +139,7 @@ db_rrdp_uris_create(struct db_rrdp_uri **uris) return pr_enomem(); tmp->table = NULL; + tmp->current_workspace = NULL; *uris = tmp; return 0; @@ -349,3 +364,46 @@ db_rrdp_uris_remove_all_local(struct db_rrdp_uri *uris) return 0; } + +char const * +db_rrdp_uris_workspace_get(void) +{ + struct db_rrdp_uri *uris; + int error; + + uris = NULL; + error = get_thread_rrdp_uris(&uris); + if (error) + return NULL; + + return uris->current_workspace; +} + +int +db_rrdp_uris_workspace_enable(void) +{ + struct db_rrdp_uri *uris; + int error; + + uris = NULL; + error = get_thread_rrdp_uris(&uris); + if (error) + return error; + + return get_thread_rrdp_workspace(&uris->current_workspace); +} + +int +db_rrdp_uris_workspace_disable(void) +{ + struct db_rrdp_uri *uris; + int error; + + uris = NULL; + error = get_thread_rrdp_uris(&uris); + if (error) + return error; + + uris->current_workspace = NULL; + return 0; +} diff --git a/src/rrdp/db/db_rrdp_uris.h b/src/rrdp/db/db_rrdp_uris.h index 409dd731..e9896276 100644 --- a/src/rrdp/db/db_rrdp_uris.h +++ b/src/rrdp/db/db_rrdp_uris.h @@ -38,4 +38,8 @@ int db_rrdp_uris_get_visited_uris(char const *, struct visited_uris **); int db_rrdp_uris_remove_all_local(struct db_rrdp_uri *); +char const *db_rrdp_uris_workspace_get(void); +int db_rrdp_uris_workspace_enable(void); +int db_rrdp_uris_workspace_disable(void); + #endif /* SRC_RRDP_DB_DB_RRDP_URIS_H_ */ diff --git a/src/rrdp/rrdp_parser.c b/src/rrdp/rrdp_parser.c index 759ec3ef..69d33063 100644 --- a/src/rrdp/rrdp_parser.c +++ b/src/rrdp/rrdp_parser.c @@ -523,7 +523,7 @@ parse_publish(xmlTextReaderPtr reader, bool parse_hash, bool hash_required, uri = NULL; if (tmp->doc_data.hash_len > 0) { /* Get the current file from the uri */ - error = uri_create_rsync_str(&uri, tmp->doc_data.uri, + error = uri_create_rsync_str_rrdp(&uri, tmp->doc_data.uri, strlen(tmp->doc_data.uri)); if (error) goto release_base64; @@ -565,7 +565,7 @@ parse_withdraw(xmlTextReaderPtr reader, struct withdraw **withdraw) goto release_tmp; /* rfc8181#section-2.2, get the file from the uri */ - error = uri_create_rsync_str(&uri, tmp->doc_data.uri, + error = uri_create_rsync_str_rrdp(&uri, tmp->doc_data.uri, strlen(tmp->doc_data.uri)); if (error) goto release_tmp; @@ -596,7 +596,7 @@ write_from_uri(char const *location, unsigned char *content, size_t content_len, int error; /* rfc8181#section-2.2 must be an rsync URI */ - error = uri_create_rsync_str(&uri, location, strlen(location)); + error = uri_create_rsync_str_rrdp(&uri, location, strlen(location)); if (error) return error; @@ -655,7 +655,7 @@ __delete_from_uri(char const *location, struct visited_uris *visited_uris) int error; /* rfc8181#section-2.2 must be an rsync URI */ - error = uri_create_rsync_str(&uri, location, strlen(location)); + error = uri_create_rsync_str_rrdp(&uri, location, strlen(location)); if (error) return error; @@ -1032,7 +1032,7 @@ process_delta(struct delta_head *delta_head, void *arg) head_data = &delta_head->doc_data; pr_val_debug("Processing delta '%s'.", delta_head->doc_data.uri); - error = uri_create_https_str(&uri, head_data->uri, + error = uri_create_https_str_rrdp(&uri, head_data->uri, strlen(head_data->uri)); if (error) return error; @@ -1123,7 +1123,7 @@ rrdp_parse_snapshot(struct update_notification *parent, args.visited_uris = visited_uris; pr_val_debug("Processing snapshot '%s'.", parent->snapshot.uri); - error = uri_create_https_str(&uri, parent->snapshot.uri, + error = uri_create_https_str_rrdp(&uri, parent->snapshot.uri, strlen(parent->snapshot.uri)); if (error) return error; diff --git a/src/rsync/rsync.c b/src/rsync/rsync.c index 6c7f9c55..4ba18183 100644 --- a/src/rsync/rsync.c +++ b/src/rsync/rsync.c @@ -499,7 +499,7 @@ release_args: * Returned values if the ancestor URI of @error_uri: * 0 - didn't had a previous request error * EEXIST - had a previous request error - * > 0 - nothing, just something bad happened + * < 0 - nothing, just something bad happened */ static int ancestor_error(char const *error_uri, void *arg) @@ -509,23 +509,43 @@ ancestor_error(char const *error_uri, void *arg) int error; req_err_uri = NULL; - error = uri_create_rsync_str(&req_err_uri, error_uri, + error = uri_create_mixed_str(&req_err_uri, error_uri, strlen(error_uri)); switch(error) { case 0: break; - case ENOTRSYNC: - return 0; default: return ENSURE_NEGATIVE(error); } + /* Ignore non rsync error'd URIs */ + if (!uri_is_rsync(req_err_uri)) { + free(req_err_uri); + return 0; + } + error = is_descendant(req_err_uri, search) ? EEXIST : 0; free(req_err_uri); return error; } +/* Validate if the ancestor URI error'd */ +static int +check_ancestor_error(struct rpki_uri *requested_uri) +{ + int error; + + error = reqs_errors_foreach(ancestor_error, requested_uri); + if (error < 0) + return error; + /* Return the requests error'd code */ + if (error == EEXIST) + return EREQFAILED; + + return 0; +} + /** * @is_ta: Are we rsync'ing the TA? * The TA rsync will not be recursive, and will force SYNC_STRICT @@ -560,19 +580,15 @@ download_files(struct rpki_uri *requested_uri, bool is_ta, bool force) if (!force && is_already_downloaded(requested_uri, visited_uris)) { pr_val_debug("No need to redownload '%s'.", uri_val_get_printable(requested_uri)); - return 0; + return check_ancestor_error(requested_uri); } if (!force) error = get_rsync_uri(requested_uri, is_ta, &rsync_uri); else { - /* Validate if the ancestor URI error'd */ - error = reqs_errors_foreach(ancestor_error, requested_uri); - if (error < 0) + error = check_ancestor_error(requested_uri); + if (error) return error; - /* Return the requests error'd code */ - if (error == EEXIST) - return EREQFAILED; error = handle_strict_strategy(requested_uri, &rsync_uri); } @@ -581,18 +597,18 @@ download_files(struct rpki_uri *requested_uri, bool is_ta, bool force) pr_val_debug("Going to RSYNC '%s'.", uri_val_get_printable(rsync_uri)); - to_op_log = reqs_errors_log_uri(uri_get_global(requested_uri)); + to_op_log = reqs_errors_log_uri(uri_get_global(rsync_uri)); error = do_rsync(rsync_uri, is_ta, to_op_log); switch(error) { case 0: /* Don't store when "force" and if its already downloaded */ if (!(force && is_already_downloaded(rsync_uri, visited_uris))) error = mark_as_downloaded(rsync_uri, visited_uris); - reqs_errors_rem_uri(uri_get_global(requested_uri)); + reqs_errors_rem_uri(uri_get_global(rsync_uri)); break; case EREQFAILED: /* All attempts failed, avoid future requests */ - error = reqs_errors_add_uri(uri_get_global(requested_uri)); + error = reqs_errors_add_uri(uri_get_global(rsync_uri)); if (error) break; error = mark_as_downloaded(rsync_uri, visited_uris); diff --git a/src/state.c b/src/state.c index 4d51e023..6a962b6a 100644 --- a/src/state.c +++ b/src/state.c @@ -25,6 +25,9 @@ struct validation { struct uri_list *rsync_visited_uris; + /* Local RRDP workspace path */ + char const *rrdp_workspace; + /* Shallow copy of RRDP URIs and its corresponding visited uris */ struct db_rrdp_uri *rrdp_uris; @@ -88,7 +91,6 @@ validation_prepare(struct validation **out, struct tal *tal, struct validation_handler *validation_handler) { struct validation *result; - struct db_rrdp_uri *uris_table; X509_VERIFY_PARAM *params; int error; @@ -126,10 +128,8 @@ validation_prepare(struct validation **out, struct tal *tal, if (error) goto abort4; - uris_table = db_rrdp_get_uris(tal_get_file_name(tal)); - if (uris_table == NULL) - pr_crit("db_rrdp_get_uris() returned NULL, means it hasn't been initialized"); - result->rrdp_uris = uris_table; + result->rrdp_uris = db_rrdp_get_uris(tal_get_file_name(tal)); + result->rrdp_workspace = db_rrdp_get_workspace(tal_get_file_name(tal)); result->pubkey_state = PKS_UNTESTED; result->validation_handler = *validation_handler; @@ -223,3 +223,9 @@ validation_get_rrdp_uris(struct validation *state) { return state->rrdp_uris; } + +char const * +validation_get_rrdp_workspace(struct validation *state) +{ + return state->rrdp_workspace; +} diff --git a/src/state.h b/src/state.h index 190a5719..5fceca30 100644 --- a/src/state.h +++ b/src/state.h @@ -36,5 +36,6 @@ struct validation_handler const * validation_get_validation_handler(struct validation *); struct db_rrdp_uri *validation_get_rrdp_uris(struct validation *); +char const *validation_get_rrdp_workspace(struct validation *); #endif /* SRC_STATE_H_ */ diff --git a/src/uri.c b/src/uri.c index eb5901e2..ebdfadae 100644 --- a/src/uri.c +++ b/src/uri.c @@ -2,6 +2,7 @@ #include #include +#include "rrdp/db/db_rrdp_uris.h" #include "common.h" #include "config.h" #include "log.h" @@ -214,19 +215,23 @@ validate_gprefix(char const *global, size_t global_len, uint8_t flags, static char const *const PFX_HTTPS = "https://"; size_t const PFX_RSYNC_LEN = strlen(PFX_RSYNC); size_t const PFX_HTTPS_LEN = strlen(PFX_HTTPS); + uint8_t l_flags; int error; - if (flags == URI_VALID_RSYNC) { + /* Exclude RSYNC RRDP flag, isn't relevant here */ + l_flags = flags & ~URI_USE_RRDP_WORKSPACE; + + if (l_flags == URI_VALID_RSYNC) { (*type) = URI_RSYNC; return validate_uri_begin(PFX_RSYNC, PFX_RSYNC_LEN, global, global_len, size, ENOTRSYNC); } - if (flags == URI_VALID_HTTPS) { + if (l_flags == URI_VALID_HTTPS) { (*type) = URI_HTTPS; return validate_uri_begin(PFX_HTTPS, PFX_HTTPS_LEN, global, global_len, size, ENOTHTTPS); } - if (flags != (URI_VALID_RSYNC & URI_VALID_HTTPS)) + if (l_flags != (URI_VALID_RSYNC | URI_VALID_HTTPS)) pr_crit("Unknown URI flag"); /* It has both flags */ @@ -249,6 +254,28 @@ validate_gprefix(char const *global, size_t global_len, uint8_t flags, return 0; } +static int +get_local_workspace(char **result, size_t *result_len) +{ + char const *workspace; + char *tmp; + + workspace = db_rrdp_uris_workspace_get(); + if (workspace == NULL) { + *result = NULL; + *result_len = 0; + return 0; + } + + tmp = strdup(workspace); + if (tmp == NULL) + return pr_enomem(); + + *result = tmp; + *result_len = strlen(tmp); + return 0; +} + /** * Initializes @uri->local by converting @uri->global. * @@ -265,10 +292,12 @@ g2l(char const *global, size_t global_len, uint8_t flags, char **result, { char const *repository; char *local; + char *workspace; size_t prefix_len; size_t repository_len; size_t extra_slash; size_t offset; + size_t workspace_len; enum rpki_uri_type type; int error; @@ -283,9 +312,20 @@ g2l(char const *global, size_t global_len, uint8_t flags, char **result, global_len -= prefix_len; extra_slash = (repository[repository_len - 1] == '/') ? 0 : 1; - local = malloc(repository_len + extra_slash + global_len + 1); - if (!local) + workspace = NULL; + workspace_len = 0; + if ((flags & URI_USE_RRDP_WORKSPACE) != 0) { + error = get_local_workspace(&workspace, &workspace_len); + if (error) + return error; + } + + local = malloc(repository_len + extra_slash + workspace_len + + global_len + 1); + if (!local) { + free(workspace); return pr_enomem(); + } offset = 0; strcpy(local + offset, repository); @@ -294,10 +334,15 @@ g2l(char const *global, size_t global_len, uint8_t flags, char **result, strcpy(local + offset, "/"); offset += extra_slash; } + if (workspace_len > 0) { + strcpy(local + offset, workspace); + offset += workspace_len; + } strncpy(local + offset, global, global_len); offset += global_len; local[offset] = '\0'; + free(workspace); *result = local; (*result_type) = type; return 0; @@ -340,15 +385,25 @@ uri_create(struct rpki_uri **result, uint8_t flags, void const *guri, } int -uri_create_rsync_str(struct rpki_uri **uri, char const *guri, size_t guri_len) +uri_create_rsync_str_rrdp(struct rpki_uri **uri, char const *guri, + size_t guri_len) { - return uri_create(uri, URI_VALID_RSYNC, guri, guri_len); + return uri_create(uri, URI_VALID_RSYNC | URI_USE_RRDP_WORKSPACE, guri, + guri_len); +} + +int +uri_create_https_str_rrdp(struct rpki_uri **uri, char const *guri, + size_t guri_len) +{ + return uri_create(uri, URI_VALID_HTTPS | URI_USE_RRDP_WORKSPACE, guri, + guri_len); } int -uri_create_https_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_HTTPS, guri, guri_len); + return uri_create(uri, URI_VALID_RSYNC, guri, guri_len); } /* @@ -359,7 +414,7 @@ uri_create_https_str(struct rpki_uri **uri, char const *guri, size_t guri_len) int uri_create_mixed_str(struct rpki_uri **uri, char const *guri, size_t guri_len) { - return uri_create(uri, URI_VALID_RSYNC & URI_VALID_HTTPS, guri, + return uri_create(uri, URI_VALID_RSYNC | URI_VALID_HTTPS, guri, guri_len); } @@ -367,9 +422,11 @@ uri_create_mixed_str(struct rpki_uri **uri, char const *guri, size_t guri_len) * Manifests URIs are a little special in that they are relative. */ int -uri_create_mft(struct rpki_uri **result, struct rpki_uri *mft, IA5String_t *ia5) +uri_create_mft(struct rpki_uri **result, struct rpki_uri *mft, IA5String_t *ia5, + bool use_rrdp_workspace) { struct rpki_uri *uri; + uint8_t flags; int error; uri = malloc(sizeof(struct rpki_uri)); @@ -382,7 +439,11 @@ uri_create_mft(struct rpki_uri **result, struct rpki_uri *mft, IA5String_t *ia5) return error; } - error = autocomplete_local(uri, URI_VALID_RSYNC); + flags = URI_VALID_RSYNC; + if (use_rrdp_workspace) + flags |= URI_USE_RRDP_WORKSPACE; + + error = autocomplete_local(uri, flags); if (error) { free(uri->global); free(uri); diff --git a/src/uri.h b/src/uri.h index 0e7be926..6e2d7c1f 100644 --- a/src/uri.h +++ b/src/uri.h @@ -6,15 +6,20 @@ #include "asn1/asn1c/IA5String.h" /* Flags to indicate expected uri type */ -#define URI_VALID_RSYNC 0x01 -#define URI_VALID_HTTPS 0x02 +#define URI_VALID_RSYNC 0x01 +#define URI_VALID_HTTPS 0x02 +/* Work with a local workspace (eg. map rsync RRPD uri's) */ +#define URI_USE_RRDP_WORKSPACE 0x10 struct rpki_uri; +/* Maps RSYNC URIs of RRDP to a local workspace */ +int uri_create_rsync_str_rrdp(struct rpki_uri **, char const *, size_t); +int uri_create_https_str_rrdp(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_mft(struct rpki_uri **, struct rpki_uri *, IA5String_t *, bool); int uri_create_ad(struct rpki_uri **, ACCESS_DESCRIPTION *, int); void uri_refget(struct rpki_uri *); -- 2.47.2