]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Engineer URIs a little
authorAlberto Leiva Popper <ydahhrk@gmail.com>
Tue, 22 Jan 2019 22:57:11 +0000 (16:57 -0600)
committerAlberto Leiva Popper <ydahhrk@gmail.com>
Tue, 22 Jan 2019 23:57:39 +0000 (17:57 -0600)
Should make URIs easier to use, and prevent the missing
null character bug from appearing again.

29 files changed:
src/Makefile.am
src/asn1/content_info.c
src/asn1/content_info.h
src/asn1/oid.c
src/common.c
src/common.h
src/crypto/hash.c
src/crypto/hash.h
src/file.c
src/file.h
src/main.c
src/object/certificate.c
src/object/certificate.h
src/object/crl.c
src/object/crl.h
src/object/manifest.c
src/object/manifest.h
src/object/roa.c
src/object/roa.h
src/object/signed_object.c
src/object/signed_object.h
src/object/tal.c
src/object/tal.h
src/resource.c
src/rsync/rsync.c
src/rsync/rsync.h
src/state.c
src/uri.c [new file with mode: 0644]
src/uri.h [new file with mode: 0644]

index 5b4c71049d4d2791d0c01ae9e23dcd77ff893667..aa3077e95e1fe1c58fd55583785f336e8916ccd9 100644 (file)
@@ -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
 
index 147d1653818ba25853d7ce12cf9ab7c29bb3cdd1..b74cd7b8b2f8d5259c8073ecb87ec6c100b9f48c 100644 (file)
@@ -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;
 
index 74c104ef6664139b3770c415da7e1afb54581f7a..11c7969a44f272ac60b6866487a14531b37a466a 100644 (file)
@@ -4,8 +4,9 @@
 /* Some wrappers for libcmscodec's ContentInfo. */
 
 #include <libcmscodec/ContentInfo.h>
+#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_ */
index 6828628a76ce0fbc9663f8fa938d87efb979b369..0366c408d706e9d3361e7a9f63369b9184d9dce4 100644 (file)
@@ -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();
index cbef2d0e51876a23792c964676f813e9e76f4632..7b51730ac8ea6676295db473fdefa9a7763cb148 100644 (file)
@@ -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;
index 4962d5c22342300be01fe7a707d3ad4828eae6c1..af282730ac1fd7ff7ec8ae27ab55efd5980fe5c6 100644 (file)
@@ -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_ */
index d533cb5139a803252370af49e0b3d49c98e4537f..1dd6cefe4ec6783013e37256cbc090bbb117f11b 100644 (file)
@@ -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;
 
index 9232caa662adf1ed4ff74d4d89ec81eaca2303be..b44b76bf7fe0f327afbe7a6694635cb7f258a0d4 100644 (file)
@@ -5,9 +5,11 @@
 #include <stddef.h>
 #include <libcmscodec/BIT_STRING.h>
 #include <libcmscodec/OBJECT_IDENTIFIER.h>
+#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*,
index afa899be6b8b5e71583157b1381cff6f20860119..e8abb962d68bdd7d501d5bc6d93ccfc050c0a540 100644 (file)
@@ -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;
 
index 4264d956f88568f7c5adbcb0e385f42fbae24590..a420c8a5690b74daf012778f222c7a7589b73bb5 100644 (file)
@@ -4,6 +4,7 @@
 #include <stddef.h>
 #include <stdio.h>
 #include <sys/stat.h>
+#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_ */
index e7eface4cad9b07dec262e1653cd747f166e71c8..d5def1b693a1466d621bebbaa26de75e2f4cb4c0 100644 (file)
@@ -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("}");
index 7b23d82943e10ee618131a67f00d81d96c20f5d1..bbf6b0b7ddc950befa94ee2f5496aff8de56acb1 100644 (file)
@@ -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;
                }
        }
 
index cae7e107c2ff8663a1dcc294fa29d37bbcb70f1e..90c9050969a18cc60f638f6bf4d86a5b6bf1eeb5 100644 (file)
@@ -4,9 +4,9 @@
 #include <stdbool.h>
 #include <openssl/x509.h>
 #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.
index 22c47e9a6c67c7cf9e567d41e5413467c4c3ac92..6d995ca5a2e2a609a1512703d336c86209843686 100644 (file)
@@ -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;
index aecffed4ea2caff16e7632289ea0e67a776b0fe5..f54a7fb077a4f835c2c61f8cc38b6e8dfe087d49 100644 (file)
@@ -2,7 +2,8 @@
 #define SRC_OBJECT_CRL_H_
 
 #include <openssl/x509.h>
+#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_ */
index cf95506f1b58eee7af9db70c841d2108aa1e567f..f73f8d9666d6099489f0b3fac668db6dcfdd0ae3 100644 (file)
@@ -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;
index f0a4240fdcda52bf79d917c959bb6996a267fe39..fc3e07caeae316c7b21b7ac39a696d49867fe81f 100644 (file)
@@ -2,7 +2,8 @@
 #define SRC_OBJECT_MANIFEST_H_
 
 #include <openssl/x509.h>
+#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_ */
index 3fbf2181a87a5951768f53b3539d9c68a91c0526..3707f845b157eecaf2d11f2060eb81b5ec550df6 100644 (file)
@@ -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;
index 7fffc4e54ab3c5287fae3d2bbf18948602d34763..9638b17b62b692fefad1affcfaf52dd51eeecac2 100644 (file)
@@ -2,7 +2,8 @@
 #define SRC_OBJECT_ROA_H_
 
 #include <openssl/x509.h>
+#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_ */
index 2a52fe04dd6c1fff118129d5ed288ce665060d72..5c05e46119879bccf4f3ab084faa21849a5eae18 100644 (file)
@@ -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;
 
index 2c5abf7951f7670675edc4dc734c5870e6787d0c..55db7d27122ad3268e067f96dc7e93ff825b9713 100644 (file)
@@ -4,8 +4,9 @@
 #include <openssl/x509.h>
 #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_ */
index 5a8f1aa148fd388977e06967babf9e7bbab386ec..7000d1175c2b2bddb94209a4b642a715b44cd2fe 100644 (file)
@@ -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;
        }
index 6be3099eb7ff1252409d93fed3482b6d155e77c3..87a3e57900ed1a94f57e5f9d9bfe7d68874667a1 100644 (file)
@@ -4,13 +4,14 @@
 /* This is RFC 7730. */
 
 #include <stddef.h>
+#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 *);
 
index 0e343016710ea794ea4672c70f8e1785af28dabb..22eabbcbedced0c34bffba2309159514a4710d4a 100644 (file)
@@ -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;
index f1d71aadb27a3548f09d820048633754ee9e18aa..d4a2a13b359632b162c5d59020020b5f4d0b7213 100644 (file)
@@ -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;
 
index 5005908b2480a6b79b39a919fd9f9f69ae633d2d..688c1df6b49b9ea05d1d050e5b4b5dd0938c43c3 100644 (file)
@@ -2,8 +2,9 @@
 #define SRC_RSYNC_RSYNC_H_
 
 #include <stdbool.h>
+#include "uri.h"
 
-int download_files(const char *);
+int download_files(struct rpki_uri const *);
 int rsync_init(bool);
 void rsync_destroy(void);
 
index f0a8e62efd84795154ab80fcaff59f84c8a60909..9c4c08d6855f68e963a2acdc455a4d514e15a01f 100644 (file)
@@ -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 (file)
index 0000000..58f6c26
--- /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 (file)
index 0000000..62c6ed5
--- /dev/null
+++ b/src/uri.h
@@ -0,0 +1,41 @@
+#ifndef SRC_URI_H_
+#define SRC_URI_H_
+
+#include <stdbool.h>
+#include <libcmscodec/IA5String.h>
+#include <openssl/x509v3.h>
+
+/**
+ * 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_ */