From: Alberto Leiva Popper Date: Tue, 22 Jan 2019 22:57:11 +0000 (-0600) Subject: Engineer URIs a little X-Git-Tag: v0.0.2~109 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=02b2f940a7516cc4d3406cc06f9bc079da2595e3;p=thirdparty%2FFORT-validator.git Engineer URIs a little Should make URIs easier to use, and prevent the missing null character bug from appearing again. --- diff --git a/src/Makefile.am b/src/Makefile.am index 5b4c7104..aa3077e9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -17,6 +17,7 @@ rpki_validator_SOURCES += resource.h resource.c rpki_validator_SOURCES += sorted_array.h sorted_array.c rpki_validator_SOURCES += state.h state.c rpki_validator_SOURCES += thread_var.h thread_var.c +rpki_validator_SOURCES += uri.h uri.c rpki_validator_SOURCES += rsync/rsync.h rsync/rsync.c diff --git a/src/asn1/content_info.c b/src/asn1/content_info.c index 147d1653..b74cd7b8 100644 --- a/src/asn1/content_info.c +++ b/src/asn1/content_info.c @@ -48,12 +48,12 @@ decode(struct file_contents *fc, struct ContentInfo **result) } int -content_info_load(const char *file_name, struct ContentInfo **result) +content_info_load(struct rpki_uri const *uri, struct ContentInfo **result) { struct file_contents fc; int error; - error = file_load(file_name, &fc); + error = file_load(uri, &fc); if (error) return error; diff --git a/src/asn1/content_info.h b/src/asn1/content_info.h index 74c104ef..11c7969a 100644 --- a/src/asn1/content_info.h +++ b/src/asn1/content_info.h @@ -4,8 +4,9 @@ /* Some wrappers for libcmscodec's ContentInfo. */ #include +#include "uri.h" -int content_info_load(const char *, struct ContentInfo **); +int content_info_load(struct rpki_uri const *, struct ContentInfo **); void content_info_free(struct ContentInfo *); #endif /* SRC_CONTENT_INFO_H_ */ diff --git a/src/asn1/oid.c b/src/asn1/oid.c index 6828628a..0366c408 100644 --- a/src/asn1/oid.c +++ b/src/asn1/oid.c @@ -38,6 +38,7 @@ oid2arcs(OBJECT_IDENTIFIER_t *oid, struct oid_arcs *result) /* If necessary, reallocate arcs array and try again. */ if (count > MAX_ARCS) { + /* TODO realloc tmp */ result->arcs = realloc(result->arcs, count * sizeof(asn_oid_arc_t)); if (!result->arcs) return pr_enomem(); diff --git a/src/common.c b/src/common.c index cbef2d0e..7b51730a 100644 --- a/src/common.c +++ b/src/common.c @@ -8,66 +8,4 @@ char const *repository; size_t repository_len; int NID_rpkiManifest; int NID_signedObject; - -/* @extension must include the period. */ -bool -file_has_extension(char const *filename, size_t filename_len, char const *ext) -{ - size_t ext_len; - - ext_len = strlen(ext); - if (filename_len < ext_len) - return false; - - return strncmp(filename + filename_len - ext_len, ext, ext_len) == 0; -} - -/** - * Converts the global URI @guri to its local (rsync clone) equivalent. - * For example, given repository "/tmp/rpki" and global uri - * "rsync://rpki.ripe.net/repo/manifest.mft", returns - * "/tmp/rpki/rpki.ripe.net/repo/manifest.mft". - * - * You need to free the result once you're done. - * This function does not assume that @guri is null-terminated. - * - * By contract, if @guri is not RSYNC, this will return ENOTRSYNC. - * This often should not be treated as an error; please handle gracefully. - * TODO open call hirarchy. - */ -int -uri_g2l(char const *guri, size_t guri_len, char **result) -{ - static char const *const PREFIX = "rsync://"; - char *luri; - size_t prefix_len; - size_t extra_slash; - size_t offset; - - prefix_len = strlen(PREFIX); - - if (guri_len < prefix_len || strncmp(PREFIX, guri, prefix_len) != 0) { - pr_err("Global URI does not begin with '%s'.", PREFIX); - return ENOTRSYNC; /* Not really an error, so not negative */ - } - - guri += prefix_len; - guri_len -= prefix_len; - extra_slash = (repository[repository_len - 1] == '/') ? 0 : 1; - - luri = malloc(repository_len + extra_slash + guri_len + 1); - if (!luri) - return -ENOMEM; - - offset = 0; - strcpy(luri + offset, repository); - offset += repository_len; - strncpy(luri + offset, "/", extra_slash); - offset += extra_slash; - strncpy(luri + offset, guri, guri_len); - offset += guri_len; - luri[offset] = '\0'; - - *result = luri; - return 0; -} +int NID_rpkiNotify; diff --git a/src/common.h b/src/common.h index 4962d5c2..af282730 100644 --- a/src/common.h +++ b/src/common.h @@ -20,10 +20,8 @@ extern char const *repository; extern size_t repository_len; extern int NID_rpkiManifest; extern int NID_signedObject; +extern int NID_rpkiNotify; #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0])) -bool file_has_extension(char const *, size_t, char const *); -int uri_g2l(char const *, size_t, char **); - #endif /* SRC_RTR_COMMON_H_ */ diff --git a/src/crypto/hash.c b/src/crypto/hash.c index d533cb51..1dd6cefe 100644 --- a/src/crypto/hash.c +++ b/src/crypto/hash.c @@ -49,8 +49,8 @@ hash_matches(unsigned char const *expected, size_t expected_len, } static int -hash_file(char const *algorithm, char const *filename, unsigned char *result, - unsigned int *result_len) +hash_file(char const *algorithm, struct rpki_uri const *uri, + unsigned char *result, unsigned int *result_len) { EVP_MD const *md; FILE *file; @@ -65,7 +65,7 @@ hash_file(char const *algorithm, char const *filename, unsigned char *result, if (error) return error; - error = file_open(filename, &file, &stat); + error = file_open(uri, &file, &stat); if (error) return error; @@ -116,12 +116,11 @@ end1: } /** - * Computes the hash of the file whose name is @filename, and compares it to - * @expected (The "expected" hash). Returns 0 if no errors happened and the - * hashes match. + * Computes the hash of the file @uri, and compares it to @expected (The + * "expected" hash). Returns 0 if no errors happened and the hashes match. */ int -hash_validate_file(char const *algorithm, char const *filename, +hash_validate_file(char const *algorithm, struct rpki_uri const *uri, BIT_STRING_t const *expected) { unsigned char actual[EVP_MAX_MD_SIZE]; @@ -131,7 +130,7 @@ hash_validate_file(char const *algorithm, char const *filename, if (expected->bits_unused != 0) return pr_err("Hash string has unused bits."); - error = hash_file(algorithm, filename, actual, &actual_len); + error = hash_file(algorithm, uri, actual, &actual_len); if (error) return error; diff --git a/src/crypto/hash.h b/src/crypto/hash.h index 9232caa6..b44b76bf 100644 --- a/src/crypto/hash.h +++ b/src/crypto/hash.h @@ -5,9 +5,11 @@ #include #include #include +#include "uri.h" int hash_is_sha256(OBJECT_IDENTIFIER_t *, bool *); -int hash_validate_file(char const *, char const *, BIT_STRING_t const *); +int hash_validate_file(char const *, struct rpki_uri const *uri, + BIT_STRING_t const *); int hash_validate(char const *, unsigned char const *, size_t, unsigned char const *, size_t); int hash_validate_octet_string(char const *, OCTET_STRING_t const*, diff --git a/src/file.c b/src/file.c index afa899be..e8abb962 100644 --- a/src/file.c +++ b/src/file.c @@ -5,21 +5,21 @@ #include "log.h" int -file_open(const char *filename, FILE **result, struct stat *stat) +file_open(struct rpki_uri const *uri, FILE **result, struct stat *stat) { FILE *file; int error; - file = fopen(filename, "rb"); + file = fopen(uri->local, "rb"); if (file == NULL) - return pr_errno(errno, "Could not open file '%s'", filename); + return pr_errno(errno, "Could not open file '%s'", uri->local); if (fstat(fileno(file), stat) == -1) { - error = pr_errno(errno, "fstat(%s) failed", filename); + error = pr_errno(errno, "fstat(%s) failed", uri->local); goto fail; } if (!S_ISREG(stat->st_mode)) { - error = pr_err("%s does not seem to be a file", filename); + error = pr_err("%s does not seem to be a file", uri->local); goto fail; } @@ -39,14 +39,14 @@ file_close(FILE *file) } int -file_load(const char *filename, struct file_contents *fc) +file_load(struct rpki_uri const *uri, struct file_contents *fc) { FILE *file; struct stat stat; size_t fread_result; int error; - error = file_open(filename, &file, &stat); + error = file_open(uri, &file, &stat); if (error) return error; diff --git a/src/file.h b/src/file.h index 4264d956..a420c8a5 100644 --- a/src/file.h +++ b/src/file.h @@ -4,6 +4,7 @@ #include #include #include +#include "uri.h" /* * The entire contents of the file, loaded into a buffer. @@ -15,10 +16,10 @@ struct file_contents { size_t buffer_size; }; -int file_open(const char *, FILE **, struct stat *); +int file_open(struct rpki_uri const *, FILE **, struct stat *); void file_close(FILE *file); -int file_load(const char *, struct file_contents *); +int file_load(struct rpki_uri const *, struct file_contents *); void file_free(struct file_contents *); #endif /* SRC_FILE_H_ */ diff --git a/src/main.c b/src/main.c index e7eface4..d5def1b6 100644 --- a/src/main.c +++ b/src/main.c @@ -26,15 +26,20 @@ add_rpki_oids(void) "id-ad-signedObject (RFC 6487)", /* TODO */ ""); printf("signedObject registered. Its nid is %d.\n", NID_signedObject); + + NID_rpkiNotify = OBJ_create("1.3.6.1.5.5.7.48.13", + "id-ad-rpkiNotify (RFC 8182)", + /* TODO */ "Blah blah"); + printf("rpkiNotify registered. Its nid is %d.\n", NID_rpkiNotify); } static int -handle_tal_certificate(char *uri) +handle_tal_certificate(struct rpki_uri const *uri) { X509 *cert; int error; - fnstack_push(uri); + fnstack_push(uri->global); error = certificate_load(uri, &cert); if (error) goto end; @@ -56,7 +61,7 @@ end: * have been extracted from a TAL. */ static int -handle_tal_uri(struct tal *tal, char const *guri) +handle_tal_uri(struct tal *tal, struct rpki_uri const *uri) { /* * Because of the way the foreach iterates, this function must return @@ -75,11 +80,10 @@ handle_tal_uri(struct tal *tal, char const *guri) */ struct validation *state; - char *luri; int error; /* TODO this probably needs the state... */ - error = download_files(guri); + error = download_files(uri); if (error) return 0; @@ -87,22 +91,16 @@ handle_tal_uri(struct tal *tal, char const *guri) if (error) return -abs(error); - pr_debug_add("TAL URI %s {", guri); + pr_debug_add("TAL URI %s {", uri->global); - if (!is_certificate(guri)) { + if (!uri_is_certificate(uri)) { pr_err("TAL file does not point to a certificate. (Expected .cer, got '%s')", - guri); + uri->global); error = -EINVAL; goto end; } - error = uri_g2l(guri, strlen(guri), &luri); - if (error) { - error = -abs(error); - goto end; - } - - error = handle_tal_certificate(luri); + error = handle_tal_certificate(uri); if (error) { switch (validation_pubkey_state(state)) { case PKS_INVALID: @@ -117,8 +115,6 @@ handle_tal_uri(struct tal *tal, char const *guri) error = 1; } - free(luri); - end: validation_destroy(state); pr_debug_rm("}"); diff --git a/src/object/certificate.c b/src/object/certificate.c index 7b23d829..bbf6b0b7 100644 --- a/src/object/certificate.c +++ b/src/object/certificate.c @@ -101,11 +101,6 @@ struct sia_arguments { STACK_OF(X509_CRL) *crls; }; -bool is_certificate(char const *file_name) -{ - return file_has_extension(file_name, strlen(file_name), ".cer"); -} - static int validate_serial_number(X509 *cert) { @@ -360,7 +355,7 @@ certificate_validate_rfc6487(X509 *cert, bool is_root) } int -certificate_load(const char *file, X509 **result) +certificate_load(struct rpki_uri const *uri, X509 **result) { X509 *cert = NULL; BIO *bio; @@ -369,7 +364,7 @@ certificate_load(const char *file, X509 **result) bio = BIO_new(BIO_s_file()); if (bio == NULL) return crypto_err("BIO_new(BIO_s_file()) returned NULL"); - if (BIO_read_filename(bio, file) <= 0) { + if (BIO_read_filename(bio, uri->local) <= 0) { error = crypto_err("Error reading certificate"); goto end; } @@ -457,85 +452,6 @@ abort: return -EINVAL; } -/** - * Get GENERAL_NAME data. - */ -static int -get_gn(GENERAL_NAME *name, char **guri) -{ - ASN1_STRING *asn1_string; - int type; - - asn1_string = GENERAL_NAME_get0_value(name, &type); - - /* - * RFC 6487: "This extension MUST have an instance of an - * AccessDescription with an accessMethod of id-ad-rpkiManifest, (...) - * with an rsync URI [RFC5781] form of accessLocation." - * - * Ehhhhhh. It's a little annoying in that it seems to be stucking more - * than one requirement in a single sentence, which I think is rather - * rare for an RFC. Normally they tend to hammer things more. - * - * Does it imply that the GeneralName CHOICE is constrained to type - * "uniformResourceIdentifier"? I guess so, though I don't see anything - * stopping a few of the other types from also being capable of storing - * URIs. - * - * Also, nobody seems to be using the other types, and handling them - * would be a titanic pain in the ass. So this is what I'm committing - * to. - */ - if (type != GEN_URI) { - pr_err("Unknown GENERAL_NAME type: %d", type); - return -ENOTSUPPORTED; - } - - /* - * GEN_URI signals an IA5String. - * IA5String is a subset of ASCII, so this cast is safe. - * No guarantees of a NULL chara, though. - * - * TODO (testers) According to RFC 5280, accessLocation can be an IRI - * somehow converted into URI form. I don't think that's an issue - * because the RSYNC clone operation should not have performed the - * conversion, so we should be looking at precisely the IA5String - * directory our g2l version of @asn1_string should contain. - * But ask the testers to keep an eye on it anyway. - */ - *guri = (char *) ASN1_STRING_get0_data(asn1_string); - return 0; -} - -/* - * "GENERAL_NAME, global to local" - * Result has to be freed. - * - * If this function returns ENOTRSYNC, it means that @name was not an RSYNC URI. - * This often should not be treated as an error; please handle gracefully. - * TODO open call hierarchy. - */ -static int -gn_g2l(GENERAL_NAME *name, char **luri) -{ - char *guri; - int error; - - error = get_gn(name, &guri); - if (error) - return error; /* message already printed. */ - - /* - * TODO (testers) According to RFC 5280, accessLocation can be an IRI - * somehow converted into URI form. I don't think that's an issue - * because the RSYNC clone operation should not have performed the - * conversion, so we should be looking at precisely the IA5String - * directory our g2l version of @asn1_string should contain. - * But ask the testers to keep an eye on it anyway. - */ - return uri_g2l((char const *) guri, strlen(guri), luri); -} - static int handle_ip_extension(X509_EXTENSION *ext, struct resources *resources) { @@ -679,48 +595,49 @@ is_rsync(ASN1_IA5STRING *uri) static int handle_rpkiManifest(ACCESS_DESCRIPTION *ad, STACK_OF(X509_CRL) *crls) { - char *uri; + struct rpki_uri uri; int error; - error = gn_g2l(ad->location, &uri); + error = uri_init_ad(&uri, ad); if (error) return error; - error = handle_manifest(uri, crls); + error = handle_manifest(&uri, crls); - free(uri); + uri_cleanup(&uri); return error; } static int handle_caRepository(ACCESS_DESCRIPTION *ad) { - char *uri; + struct rpki_uri uri; int error; - error = get_gn(ad->location, &uri); + error = uri_init_ad(&uri, ad); if (error) return error; - pr_debug("caRepository: %s", uri); - error = download_files(uri); + pr_debug("caRepository: %s", uri.global); + error = download_files(&uri); + uri_cleanup(&uri); return error; } static int handle_signedObject(ACCESS_DESCRIPTION *ad) { - char *uri; + struct rpki_uri uri; int error; - error = gn_g2l(ad->location, &uri); + error = uri_init_ad(&uri, ad); if (error) return error; - pr_debug("signedObject: %s", uri); + pr_debug("signedObject: %s", uri.global); - free(uri); + uri_cleanup(&uri); return error; } @@ -1216,10 +1133,19 @@ handle_sia_ee(X509_EXTENSION *ext, void *arg) signedObject_found = true; } else { - /* rfc6487#section-4.8.8.2 */ - error = pr_err("EE Certificate has an non-signedObject access description. (NID: %d)", + /* + * rfc6487#section-4.8.8.2 says that non-signedObject + * ADs are prohibited, but then 8182 contradicts this. + * And so there's nothing stopping future RFCs from + * defining even more ADs. So shrug them off. + * + * rpkiNotify is known to fall through here, but we + * don't support it yet. + * + * This should be level INFO. + */ + pr_debug("EE Certificate has an non-signedObject access description. (NID: %d)", nid); - goto end; } } diff --git a/src/object/certificate.h b/src/object/certificate.h index cae7e107..90c90509 100644 --- a/src/object/certificate.h +++ b/src/object/certificate.h @@ -4,9 +4,9 @@ #include #include #include "resource.h" +#include "uri.h" -bool is_certificate(char const *); -int certificate_load(const char *, X509 **); +int certificate_load(struct rpki_uri const *, X509 **); /** * Performs the basic (RFC 5280, presumably) chain validation. diff --git a/src/object/crl.c b/src/object/crl.c index 22c47e9a..6d995ca5 100644 --- a/src/object/crl.c +++ b/src/object/crl.c @@ -43,7 +43,7 @@ print_serials(X509_CRL *crl) } static int -__crl_load(const char *file, X509_CRL **result) +__crl_load(struct rpki_uri const *uri, X509_CRL **result) { X509_CRL *crl = NULL; BIO *bio; @@ -52,14 +52,14 @@ __crl_load(const char *file, X509_CRL **result) bio = BIO_new(BIO_s_file()); if (bio == NULL) return crypto_err("BIO_new(BIO_s_file()) returned NULL"); - if (BIO_read_filename(bio, file) <= 0) { - error = crypto_err("Error reading CRL '%s'", file); + if (BIO_read_filename(bio, uri->local) <= 0) { + error = crypto_err("Error reading CRL '%s'", uri->local); goto end; } crl = d2i_X509_CRL_bio(bio, NULL); if (crl == NULL) { - error = crypto_err("Error parsing CRL '%s'", file); + error = crypto_err("Error parsing CRL '%s'", uri->local); goto end; } @@ -74,12 +74,12 @@ end: } int -crl_load(char const *file, X509_CRL **result) +crl_load(struct rpki_uri const *uri, X509_CRL **result) { int error; - pr_debug_add("CRL %s {", file); - error = __crl_load(file, result); + pr_debug_add("CRL %s {", uri->global); + error = __crl_load(uri, result); pr_debug_rm("}"); return error; diff --git a/src/object/crl.h b/src/object/crl.h index aecffed4..f54a7fb0 100644 --- a/src/object/crl.h +++ b/src/object/crl.h @@ -2,7 +2,8 @@ #define SRC_OBJECT_CRL_H_ #include +#include "uri.h" -int crl_load(char const *, X509_CRL **); +int crl_load(struct rpki_uri const *uri, X509_CRL **); #endif /* SRC_OBJECT_CRL_H_ */ diff --git a/src/object/manifest.c b/src/object/manifest.c index cf95506f..f73f8d96 100644 --- a/src/object/manifest.c +++ b/src/object/manifest.c @@ -102,94 +102,45 @@ validate_manifest(struct Manifest *manifest) return 0; } -/** - * Given manifest path @mft and its referenced file @file, returns a path - * @file can be accessed with. - * - * ie. if @mft is "a/b/c.mft" and @file is "d/e/f.cer", returns "a/b/d/e/f.cer". - * - * The result needs to be freed in the end. - */ -static int -get_relative_file(char const *mft, char const *file, size_t file_len, - char **result) -{ - char *joined; - char *slash_pos; - int dir_len; - - slash_pos = strrchr(mft, '/'); - if (slash_pos == NULL) { - joined = malloc(file_len + 1); - if (!joined) - return -ENOMEM; - strncpy(joined, file, file_len); - joined[file_len] = '\0'; - goto succeed; - } - - dir_len = (slash_pos + 1) - mft; - joined = malloc(dir_len + file_len + 1); - if (!joined) - return -ENOMEM; - - strncpy(joined, mft, dir_len); - strncpy(joined + dir_len, file, file_len); - joined[dir_len + file_len] = '\0'; - -succeed: - *result = joined; - return 0; -} - -typedef int (*foreach_cb)(char *, void *); +typedef int (*foreach_cb)(struct rpki_uri const *, void *); static int foreach_file(struct manifest *mft, char *extension, foreach_cb cb, void *arg) { struct FileAndHash *fah; - char *uri; - size_t uri_len; - char *luri; /* "Local URI". As in "URI that we can easily reference." */ + struct rpki_uri uri; int i; int error; for (i = 0; i < mft->obj->fileList.list.count; i++) { fah = mft->obj->fileList.list.array[i]; - /* - * IA5String is just a subset of ASCII, so this cast is fine. - * I don't see any guarantees that the string will be - * zero-terminated though, so we'll handle that the hard way. - */ - uri = (char *) fah->file.buf; - uri_len = fah->file.size; - - if (file_has_extension(uri, uri_len, extension)) { - error = get_relative_file(mft->file_path, uri, uri_len, - &luri); - if (error) - return error; - - error = hash_validate_file("sha256", luri, &fah->hash); - if (error) { - free(luri); - continue; - } - - error = cb(luri, arg); - - free(luri); - if (error) - return error; + error = uri_init_ia5(&uri, mft->file_path, &fah->file); + if (error) + return error; + + if (!uri_has_extension(&uri, extension)) { + uri_cleanup(&uri); + continue; + } + + error = hash_validate_file("sha256", &uri, &fah->hash); + if (error) { + uri_cleanup(&uri); + continue; } + + error = cb(&uri, arg); + uri_cleanup(&uri); + if (error) + return error; } return 0; } static int -pile_crls(char *file, void *crls) +pile_crls(struct rpki_uri const *uri, void *crls) { X509_CRL *crl; int error; @@ -199,9 +150,9 @@ pile_crls(char *file, void *crls) if (sk_X509_CRL_num(crls) != 0) return pr_err("The Manifest defines more than one CRL."); - fnstack_push(file); + fnstack_push(uri->global); - error = crl_load(file, &crl); + error = crl_load(uri, &crl); if (error) goto end; @@ -231,12 +182,12 @@ end: * certificate is CA or EE. But it just doesn't exist. */ static int -traverse_ca_certs(char *file, void *crls) +traverse_ca_certs(struct rpki_uri const *uri, void *crls) { X509 *cert; pr_debug_add("(CA?) Certificate {"); - fnstack_push(file); + fnstack_push(uri->global); /* * Errors on at least some of these functions should not interrupt the @@ -244,7 +195,7 @@ traverse_ca_certs(char *file, void *crls) * (Error messages should have been printed in stderr.) */ - if (certificate_load(file, &cert)) + if (certificate_load(uri, &cert)) goto revert1; /* Fine */ if (certificate_validate_chain(cert, crls)) @@ -262,9 +213,9 @@ revert1: } static int -print_roa(char *file, void *arg) +print_roa(struct rpki_uri const *uri, void *arg) { - handle_roa(file, arg); + handle_roa(uri, arg); return 0; } @@ -298,19 +249,19 @@ end: } int -handle_manifest(char const *file_path, STACK_OF(X509_CRL) *crls) +handle_manifest(struct rpki_uri const *uri, STACK_OF(X509_CRL) *crls) { static OID oid = OID_MANIFEST; struct oid_arcs arcs = OID2ARCS(oid); struct manifest mft; int error; - pr_debug_add("Manifest %s {", file_path); - fnstack_push(file_path); + pr_debug_add("Manifest %s {", uri->global); + fnstack_push(uri->global); - mft.file_path = file_path; + mft.file_path = uri->global; - error = signed_object_decode(file_path, &asn_DEF_Manifest, &arcs, + error = signed_object_decode(uri, &asn_DEF_Manifest, &arcs, (void **) &mft.obj, crls, NULL); if (error) goto end; diff --git a/src/object/manifest.h b/src/object/manifest.h index f0a4240f..fc3e07ca 100644 --- a/src/object/manifest.h +++ b/src/object/manifest.h @@ -2,7 +2,8 @@ #define SRC_OBJECT_MANIFEST_H_ #include +#include "uri.h" -int handle_manifest(char const *, STACK_OF(X509_CRL) *); +int handle_manifest(struct rpki_uri const *, STACK_OF(X509_CRL) *); #endif /* SRC_OBJECT_MANIFEST_H_ */ diff --git a/src/object/roa.c b/src/object/roa.c index 3fbf2181..3707f845 100644 --- a/src/object/roa.c +++ b/src/object/roa.c @@ -45,11 +45,11 @@ print_addr4(struct resources *parent, long asn, struct ROAIPAddress *roa_addr) prefix.len); } - printf("%ld,%s/%u", asn, str2, prefix.len); + printf("AS%ld,%s/%u", asn, str2, prefix.len); if (roa_addr->maxLength != NULL) - printf("-%ld", max_length); + printf(",%ld", max_length); else - printf("-%u", prefix.len); + printf(",%u", prefix.len); printf("\n"); return 0; @@ -91,11 +91,11 @@ print_addr6(struct resources *parent, long asn, struct ROAIPAddress *roa_addr) prefix.len); } - printf("%ld,%s/%u", asn, str2, prefix.len); + printf("AS%ld,%s/%u", asn, str2, prefix.len); if (roa_addr->maxLength != NULL) - printf("-%ld", max_length); + printf(",%ld", max_length); else - printf("-%u", prefix.len); + printf(",%u", prefix.len); printf("\n"); return 0; @@ -168,7 +168,7 @@ family_error: return pr_err("ROA's IP family is not v4 or v6."); } -int handle_roa(char const *file, STACK_OF(X509_CRL) *crls) +int handle_roa(struct rpki_uri const *uri, STACK_OF(X509_CRL) *crls) { static OID oid = OID_ROA; struct oid_arcs arcs = OID2ARCS(oid); @@ -177,8 +177,8 @@ int handle_roa(char const *file, STACK_OF(X509_CRL) *crls) struct resources *cert_resources; int error; - pr_debug_add("ROA %s {", file); - fnstack_push(file); + pr_debug_add("ROA %s {", uri->global); + fnstack_push(uri->global); cert_resources = resources_create(); if (cert_resources == NULL) { @@ -186,7 +186,7 @@ int handle_roa(char const *file, STACK_OF(X509_CRL) *crls) goto end1; } - error = signed_object_decode(file, &asn_DEF_RouteOriginAttestation, + error = signed_object_decode(uri, &asn_DEF_RouteOriginAttestation, &arcs, (void **) &roa, crls, cert_resources); if (error) goto end2; diff --git a/src/object/roa.h b/src/object/roa.h index 7fffc4e5..9638b17b 100644 --- a/src/object/roa.h +++ b/src/object/roa.h @@ -2,7 +2,8 @@ #define SRC_OBJECT_ROA_H_ #include +#include "uri.h" -int handle_roa(char const *, STACK_OF(X509_CRL) *); +int handle_roa(struct rpki_uri const *uri, STACK_OF(X509_CRL) *); #endif /* SRC_OBJECT_ROA_H_ */ diff --git a/src/object/signed_object.c b/src/object/signed_object.c index 2a52fe04..5c05e461 100644 --- a/src/object/signed_object.c +++ b/src/object/signed_object.c @@ -56,7 +56,7 @@ validate_content_type(struct SignedData *sdata, } int -signed_object_decode(char const *file, +signed_object_decode(struct rpki_uri const *uri, asn_TYPE_descriptor_t const *descriptor, struct oid_arcs const *oid, void **result, @@ -67,7 +67,7 @@ signed_object_decode(char const *file, struct SignedData *sdata; int error; - error = content_info_load(file, &cinfo); + error = content_info_load(uri, &cinfo); if (error) goto end1; diff --git a/src/object/signed_object.h b/src/object/signed_object.h index 2c5abf79..55db7d27 100644 --- a/src/object/signed_object.h +++ b/src/object/signed_object.h @@ -4,8 +4,9 @@ #include #include "asn1/oid.h" #include "resource.h" +#include "uri.h" -int signed_object_decode(char const *, asn_TYPE_descriptor_t const *, +int signed_object_decode(struct rpki_uri const *, asn_TYPE_descriptor_t const *, struct oid_arcs const *, void **, STACK_OF(X509_CRL) *, struct resources *); #endif /* SRC_OBJECT_SIGNED_OBJECT_H_ */ diff --git a/src/object/tal.c b/src/object/tal.c index 5a8f1aa1..7000d117 100644 --- a/src/object/tal.c +++ b/src/object/tal.c @@ -194,11 +194,17 @@ void tal_destroy(struct tal *tal) int foreach_uri(struct tal *tal, foreach_uri_cb cb) { + struct rpki_uri uri; unsigned int i; int error; for (i = 0; i < tal->uris.count; i++) { - error = cb(tal, tal->uris.array[i]); + error = uri_init(&uri, tal->uris.array[i]); + if (error) + return error; + + error = cb(tal, &uri); + uri_cleanup(&uri); if (error) return error; } diff --git a/src/object/tal.h b/src/object/tal.h index 6be3099e..87a3e579 100644 --- a/src/object/tal.h +++ b/src/object/tal.h @@ -4,13 +4,14 @@ /* This is RFC 7730. */ #include +#include "uri.h" struct tal; int tal_load(const char *, struct tal **); void tal_destroy(struct tal *); -typedef int (*foreach_uri_cb)(struct tal *, char const *); +typedef int (*foreach_uri_cb)(struct tal *, struct rpki_uri const *); int foreach_uri(struct tal *, foreach_uri_cb); void tal_shuffle_uris(struct tal *); diff --git a/src/resource.c b/src/resource.c index 0e343016..22eabbcb 100644 --- a/src/resource.c +++ b/src/resource.c @@ -36,10 +36,8 @@ resources_create(void) struct resources *result; result = malloc(sizeof(struct resources)); - if (result == NULL) { - pr_enomem(); + if (result == NULL) return NULL; - } result->ip4s = NULL; result->ip6s = NULL; diff --git a/src/rsync/rsync.c b/src/rsync/rsync.c index f1d71aad..d4a2a13b 100644 --- a/src/rsync/rsync.c +++ b/src/rsync/rsync.c @@ -364,7 +364,7 @@ create_dir_recursive(char *localuri) } int -download_files(char const *rsync_uri) +download_files(struct rpki_uri const *uri) { int error; char *rsync_uri_path, *localuri, *tmp; @@ -375,20 +375,20 @@ download_files(char const *rsync_uri) if (!execute_rsync) return 0; - if (strlen(rsync_uri) < prefix_len || - strncmp(RSYNC_PREFIX, rsync_uri, prefix_len) != 0) + if (strlen(uri->global) < prefix_len || + strncmp(RSYNC_PREFIX, uri->global, prefix_len) != 0) return pr_err("Global URI '%s' does not begin with '%s'.", - rsync_uri, RSYNC_PREFIX); + uri->global, RSYNC_PREFIX); - if (is_uri_in_list(rsync_uri)){ - pr_debug("(%s) ON LIST: %s", __func__, rsync_uri); + if (is_uri_in_list(uri->global)){ + pr_debug("(%s) ON LIST: %s", __func__, uri->global); error = 0; goto end; } else { - pr_debug("(%s) DOWNLOAD: %s", __func__, rsync_uri); + pr_debug("(%s) DOWNLOAD: %s", __func__, uri->global); } - error = get_path_only(rsync_uri, strlen(rsync_uri), prefix_len, + error = get_path_only(uri->global, uri->global_len, prefix_len, &rsync_uri_path); if (error) return error; @@ -403,7 +403,7 @@ download_files(char const *rsync_uri) rsync_uri_path = tmp; } - error = uri_g2l(rsync_uri_path, strlen(rsync_uri_path), &localuri); + error = uri_g2l(rsync_uri_path, &localuri); if (error) goto free_uri_path; diff --git a/src/rsync/rsync.h b/src/rsync/rsync.h index 5005908b..688c1df6 100644 --- a/src/rsync/rsync.h +++ b/src/rsync/rsync.h @@ -2,8 +2,9 @@ #define SRC_RSYNC_RSYNC_H_ #include +#include "uri.h" -int download_files(const char *); +int download_files(struct rpki_uri const *); int rsync_init(bool); void rsync_destroy(void); diff --git a/src/state.c b/src/state.c index f0a8e62e..9c4c08d6 100644 --- a/src/state.c +++ b/src/state.c @@ -189,7 +189,7 @@ validation_push_cert(struct validation *state, X509 *cert, bool is_ta) resources = resources_create(); if (resources == NULL) - return -ENOMEM; + return pr_enomem(); error = certificate_get_resources(cert, resources); if (error) diff --git a/src/uri.c b/src/uri.c new file mode 100644 index 00000000..58f6c266 --- /dev/null +++ b/src/uri.c @@ -0,0 +1,248 @@ +#include "uri.h" + +#include "common.h" +#include "log.h" + +/** + * Initializes @uri->global* by cloning @str. + * This function does not assume that @str is null-terminated. + */ +static int +str2global(char const *str, size_t str_len, struct rpki_uri *uri) +{ + uri->global = malloc(str_len + 1); + if (uri->global == NULL) + return pr_enomem(); + strncpy(uri->global, str, str_len); + uri->global[str_len] = '\0'; + + uri->global_len = str_len; + + return 0; +} + +/** + * Initializes @uri->global given manifest path @mft and its referenced file + * @ia5. + * + * ie. if @mft is "rsync://a/b/c.mft" and @ia5 is "d/e/f.cer", @uri->global will + * be "rsync://a/b/d/e/f.cer". + */ +static int +ia5str2global(struct rpki_uri *uri, char const *mft, IA5String_t *ia5) +{ + char *joined; + char *slash_pos; + int dir_len; + + /* + * IA5String is a subset of ASCII. However, IA5String_t doesn't seem to + * be guaranteed to be NULL-terminated. + * `(char *) ia5->buf` is fair, but `strlen(ia5->buf)` is not. + */ + + slash_pos = strrchr(mft, '/'); + if (slash_pos == NULL) { + joined = malloc(ia5->size + 1); + if (!joined) + return pr_enomem(); + strncpy(joined, (char *) ia5->buf, ia5->size); + joined[ia5->size] = '\0'; + dir_len = 0; + goto succeed; + } + + dir_len = (slash_pos + 1) - mft; + joined = malloc(dir_len + ia5->size + 1); + if (!joined) + return pr_enomem(); + + strncpy(joined, mft, dir_len); + strncpy(joined + dir_len, (char *) ia5->buf, ia5->size); + joined[dir_len + ia5->size] = '\0'; + +succeed: + uri->global = joined; + uri->global_len = dir_len + ia5->size; + return 0; +} + +/** + * Initializes @uri->local by converting @uri->global. + * + * For example, given local cache repository "/tmp/rpki" and global uri + * "rsync://rpki.ripe.net/repo/manifest.mft", initializes @uri->local as + * "/tmp/rpki/rpki.ripe.net/repo/manifest.mft". + * + * By contract, if @guri is not RSYNC, this will return ENOTRSYNC. + * This often should not be treated as an error; please handle gracefully. + * TODO open call hirarchy. + */ +static int +g2l(char const *global, size_t global_len, char **result) +{ + static char const *const PREFIX = "rsync://"; + char *local; + size_t prefix_len; + size_t extra_slash; + size_t offset; + + prefix_len = strlen(PREFIX); + + if (global_len < prefix_len + || strncmp(PREFIX, global, prefix_len) != 0) { + pr_err("Global URI '%s' does not begin with '%s'.", + global, PREFIX); + return ENOTRSYNC; /* Not an error, so not negative */ + } + + global += prefix_len; + global_len -= prefix_len; + extra_slash = (repository[repository_len - 1] == '/') ? 0 : 1; + + local = malloc(repository_len + extra_slash + global_len + 1); + if (!local) + return pr_enomem(); + + offset = 0; + strcpy(local + offset, repository); + offset += repository_len; + strncpy(local + offset, "/", extra_slash); + offset += extra_slash; + strncpy(local + offset, global, global_len); + offset += global_len; + local[offset] = '\0'; + + *result = local; + return 0; +} + +static int +autocomplete_local(struct rpki_uri *uri) +{ + return g2l(uri->global, uri->global_len, &uri->local); +} + +int +uri_init(struct rpki_uri *uri, char const *guri) +{ + int error; + + error = str2global(guri, strlen(guri), uri); + if (error) + return error; + + error = autocomplete_local(uri); + if (error) { + free(uri->global); + return error; + } + + return 0; +} + +int +uri_init_ia5(struct rpki_uri *uri, char const *mft, IA5String_t *ia5) +{ + int error; + + error = ia5str2global(uri, mft, ia5); + if (error) + return error; + + error = autocomplete_local(uri); + if (error) + free(uri->global); + + return error; +} + +int +uri_init_ad(struct rpki_uri *uri, ACCESS_DESCRIPTION *ad) +{ + ASN1_STRING *asn1_string; + int type; + int error; + + asn1_string = GENERAL_NAME_get0_value(ad->location, &type); + + /* + * RFC 6487: "This extension MUST have an instance of an + * AccessDescription with an accessMethod of id-ad-rpkiManifest, (...) + * with an rsync URI [RFC5781] form of accessLocation." + * + * Ehhhhhh. It's a little annoying in that it seems to be stucking more + * than one requirement in a single sentence, which I think is rather + * rare for an RFC. Normally they tend to hammer things more. + * + * Does it imply that the GeneralName CHOICE is constrained to type + * "uniformResourceIdentifier"? I guess so, though I don't see anything + * stopping a few of the other types from also being capable of storing + * URIs. + * + * Also, nobody seems to be using the other types, and handling them + * would be a titanic pain in the ass. So this is what I'm committing + * to. + */ + if (type != GEN_URI) { + pr_err("Unknown GENERAL_NAME type: %d", type); + return -ENOTSUPPORTED; + } + + /* + * GEN_URI signals an IA5String. + * IA5String is a subset of ASCII, so this cast is safe. + * No guarantees of a NULL chara, though. + * + * TODO (testers) According to RFC 5280, accessLocation can be an IRI + * somehow converted into URI form. I don't think that's an issue + * because the RSYNC clone operation should not have performed the + * conversion, so we should be looking at precisely the IA5String + * directory our g2l version of @asn1_string should contain. + * But ask the testers to keep an eye on it anyway. + */ + error = str2global((char *) ASN1_STRING_get0_data(asn1_string), + ASN1_STRING_length(asn1_string), uri); + if (error) + return error; + + error = autocomplete_local(uri); + if (error) + free(uri->global); + + return error; +} + +void +uri_cleanup(struct rpki_uri *uri) +{ + free(uri->global); + free(uri->local); +} + +/* @ext must include the period. */ +bool +uri_has_extension(struct rpki_uri const *uri, char const *ext) +{ + size_t ext_len; + int cmp; + + ext_len = strlen(ext); + if (uri->global_len < ext_len) + return false; + + cmp = strncmp(uri->global + uri->global_len - ext_len, ext, ext_len); + return cmp == 0; +} + +bool +uri_is_certificate(struct rpki_uri const *uri) +{ + return uri_has_extension(uri, ".cer"); +} + +int +uri_g2l(char const *global, char **local) +{ + return g2l(global, strlen(global), local); +} diff --git a/src/uri.h b/src/uri.h new file mode 100644 index 00000000..62c6ed55 --- /dev/null +++ b/src/uri.h @@ -0,0 +1,41 @@ +#ifndef SRC_URI_H_ +#define SRC_URI_H_ + +#include +#include +#include + +/** + * These are expected to live on the stack, or as part of other objects. + */ +struct rpki_uri { + /** + * "Global URI". + * The one that always starts with "rsync://". + * As currently implemented, it's expected to live in the heap. + */ + char *global; + /** Length of @global. */ + size_t global_len; + + /** + * "Local URI". + * The file pointed by @global, but cached in the local filesystem. + * As currently implemented, it's expected to live in the heap. + */ + char *local; + + /* "local_len" is not needed for now. */ +}; + +int uri_init(struct rpki_uri *, char const *guri); +int uri_init_ia5(struct rpki_uri *, char const *, IA5String_t *); +int uri_init_ad(struct rpki_uri *, ACCESS_DESCRIPTION *ad); +void uri_cleanup(struct rpki_uri *); + +bool uri_has_extension(struct rpki_uri const *, char const *); +bool uri_is_certificate(struct rpki_uri const *); + +int uri_g2l(char const *, char **); + +#endif /* SRC_URI_H_ */