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
}
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;
/* 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_ */
/* 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();
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;
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_ */
}
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;
if (error)
return error;
- error = file_open(filename, &file, &stat);
+ error = file_open(uri, &file, &stat);
if (error)
return error;
}
/**
- * 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];
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;
#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*,
#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;
}
}
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;
#include <stddef.h>
#include <stdio.h>
#include <sys/stat.h>
+#include "uri.h"
/*
* The entire contents of the file, loaded into a buffer.
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_ */
"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;
* 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
*/
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;
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:
error = 1;
}
- free(luri);
-
end:
validation_destroy(state);
pr_debug_rm("}");
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)
{
}
int
-certificate_load(const char *file, X509 **result)
+certificate_load(struct rpki_uri const *uri, X509 **result)
{
X509 *cert = NULL;
BIO *bio;
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;
}
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)
{
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;
}
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;
}
}
#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.
}
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;
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;
}
}
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;
#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_ */
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;
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;
* 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
* (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))
}
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;
}
}
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;
#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_ */
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;
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;
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);
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) {
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;
#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_ */
}
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,
struct SignedData *sdata;
int error;
- error = content_info_load(file, &cinfo);
+ error = content_info_load(uri, &cinfo);
if (error)
goto end1;
#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_ */
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;
}
/* 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 *);
struct resources *result;
result = malloc(sizeof(struct resources));
- if (result == NULL) {
- pr_enomem();
+ if (result == NULL)
return NULL;
- }
result->ip4s = NULL;
result->ip6s = NULL;
}
int
-download_files(char const *rsync_uri)
+download_files(struct rpki_uri const *uri)
{
int error;
char *rsync_uri_path, *localuri, *tmp;
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;
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;
#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);
resources = resources_create();
if (resources == NULL)
- return -ENOMEM;
+ return pr_enomem();
error = certificate_get_resources(cert, resources);
if (error)
--- /dev/null
+#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);
+}
--- /dev/null
+#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_ */