aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Austein <sra@hactrn.net>2006-09-22 21:30:07 +0000
committerRob Austein <sra@hactrn.net>2006-09-22 21:30:07 +0000
commit6ebbfd4aa0b28a8af2174923fba0d052816a24f8 (patch)
treedd64f10a3b9c9974d5ba06b6feb49f094b3299d9
parent37a656af95671358593ed9fb25357a2b0a3c7d26 (diff)
Checkpoint
svn path=/rcynic/rcynic.c; revision=310
-rw-r--r--rcynic/rcynic.c369
1 files changed, 278 insertions, 91 deletions
diff --git a/rcynic/rcynic.c b/rcynic/rcynic.c
index 03cfd35f..b5f451df 100644
--- a/rcynic/rcynic.c
+++ b/rcynic/rcynic.c
@@ -45,13 +45,31 @@
#include <openssl/x509v3.h>
#include <openssl/safestack.h>
-typedef struct rpki_cert {
+#ifndef FILENAME_MAX
+#define FILENAME_MAX 1024
+#endif
+
+#define SIZEOF_RSYNC (sizeof("rsync://") - 1)
+
+#define URI_MAX (FILENAME_MAX + SIZEOF_RSYNC)
+
+typedef struct certinfo {
int ca, ta;
- char *uri, *file, *sia, *aia, *crldp;
- X509 *x;
-} rpki_cert_t;
+ char file[FILENAME_MAX];
+ char uri[URI_MAX], sia[URI_MAX], aia[URI_MAX], crldp[URI_MAX];
+} certinfo_t;
+
+/*
+ * Working directories, including trailing slashes. Make these
+ * configurable eventually (at which point the config code should
+ * insure the trailing slashes...).
+ */
+static const char trust_anchor_tree[] = "rcynic-trust-anchors/";
+static const char authenticated[] = "rcynic-data/authenticated/";
+static const char old_authenticated[] = "rcynic-data/authenticated.old/";
+static const char unauthenticated[] = "rcynic-data/unauthenticated/";
-static char *jane;
+static char *jane = "rcynic";
static STACK *rsync_cache;
@@ -117,37 +135,113 @@ static int mkdir_maybe(char *name)
char *b, buffer[FILENAME_MAX];
assert(name != NULL);
- if (snprintf(buffer, sizeof(buffer), "%s/.", name) >= sizeof(buffer))
+ if (strlen(name) >= sizeof(buffer))
return 0;
+ strcpy(buffer, name);
+ if ((b = strrchr(buffer, '/')) == NULL)
+ return 1;
+ *b = '\0';
if (access(buffer, F_OK) == 0)
return 1;
- if ((b = strrchr(strrchr(buffer, '/'), '/')) != 0) {
- *b = '\0';
- if (!mkdir_maybe(buffer))
- return 0;
- }
+ if (!mkdir_maybe(buffer))
+ return 0;
return mkdir(name, 0777) == 0;
}
/*
+ * Is string an rsync URI?
+ */
+
+static int is_rsync(const char *s)
+{
+ return s && !strncmp(s, "rsync://", SIZEOF_RSYNC);
+}
+
+/*
* Convert an rsync URI to a filename, checking for evil character
* sequences.
*/
-static char *uri_to_filename(char *name)
+static int uri_to_filename(const char *name,
+ char *buffer,
+ const size_t buflen,
+ const char *prefix)
{
int n;
- if (!name || strncmp(name, "rsync://", sizeof("rsync://") - 1))
+ if (!is_rsync(name))
return 0;
- name += sizeof("rsync://") - 1;
+
+ name += SIZEOF_RSYNC;
+ n = strlen(name);
- if (name[0] == '/' || name[0] == '.' || !strcmp(name, "../") ||
- strstr(name, "//") || strstr(name, "/../") ||
- ((n = strlen(name)) >= 3 && !strcmp(name + n - 3, "/..")))
+ if (name[0] == '/' || name[0] == '.' || strstr(name, "//") ||
+ strstr(name, "/../") || (n >= 3 && !strcmp(name + n - 3, "/..")))
+ return 0;
+
+ if (prefix)
+ n += strlen(prefix);
+
+ if (n >= buflen)
+ return 0;
+
+ if (prefix) {
+ strcpy(buffer, prefix);
+ strcat(buffer, name);
+ } else {
+ strcpy(buffer, name);
+ }
+
+ return 1;
+}
+
+/*
+ * Install an object. It'd be nice if we could just use link(), but
+ * that would require us to trust rsync never to do anything bad. For
+ * now we just copy in the simplest way possible. Come back to this
+ * if profiling shows a hotspot here.
+ */
+
+static int install_object(const char *uri, const char *source)
+{
+ char target[FILENAME_MAX];
+ FILE *in, *out;
+ int c;
+
+ if (!uri_to_filename(uri, target, sizeof(target), authenticated)) {
+ logmsg("Couldn't generate installation name for URI %s", uri);
+ return 0;
+ }
+
+ if (!mkdir_maybe(target)) {
+ logmsg("Couldn't create directory for %s", target);
+ return 0;
+ }
+
+ if ((in = fopen(source, "rb")) == NULL) {
+ logmsg("Couldn't open %s", source);
+ return 0;
+ }
+
+ if ((out = fopen(target, "rb")) == NULL) {
+ logmsg("Couldn't open %s", target);
+ fclose(in);
+ return 0;
+ }
+
+ while ((c = getc(in)) != EOF) {
+ if (putc(c, out) == EOF) {
+ logmsg("Couldn't write to %s", target);
+ break;
+ }
+ }
+
+ if (fclose(in) == EOF || fclose(out) == EOF) {
+ logmsg("Trouble closing %s and %s", source, target);
return 0;
+ }
- return strdup(name);
+ return 1;
}
@@ -173,7 +267,7 @@ static int rsync_cmp(const char * const *a, const char * const *b)
static int rsync(char *args, ...)
{
- char *uri = 0, *path, *s, *argv[100], buffer[2000];
+ char *s, *argv[100], buffer[2000], *uri = 0, path[FILENAME_MAX];
int argc, pipe_fds[2], n, pid_status = -1;
va_list ap;
pid_t pid;
@@ -196,11 +290,14 @@ static int rsync(char *args, ...)
return 0;
}
- if ((path = uri_to_filename(uri)) == NULL) {
- logmsg("Couldn't extract filename from URI for rsync cache");
+ if (!uri_to_filename(uri, path, sizeof(path), unauthenticated)) {
+ logmsg("Couldn't extract filename from URI: %s", uri);
return 0;
}
-
+
+ assert(argc < sizeof(argv)/sizeof(*argv));
+ argv[argc++] = path;
+
assert(rsync_cache != NULL);
if ((s = sk_value(rsync_cache, sk_find(rsync_cache, path))) != NULL &&
!strncmp(s, path, strlen(s))) {
@@ -209,6 +306,11 @@ static int rsync(char *args, ...)
return 1;
}
+ if (!mkdir_maybe(path)) {
+ logmsg("Couldn't make target directory: %s", path);
+ return 0;
+ }
+
if (pipe(pipe_fds) < 0) {
logmsg("pipe() failed");
return 0;
@@ -261,134 +363,219 @@ static int rsync(char *args, ...)
/*
- * Extract URIs from certificate extensions.
+ * Read certificate in DER format.
+ */
+
+static X509 *read_cert(const char *filename)
+{
+ X509 *x = NULL;
+ BIO *b;
+
+ if ((b = BIO_new_file(filename, "r")) != NULL)
+ x = d2i_X509_bio(b, NULL);
+
+ BIO_free(b);
+ return x;
+}
+
+/*
+ * Read CRL in DER format.
+ */
+
+static X509_CRL *read_crl(const char *filename)
+{
+ X509_CRL *crl = NULL;
+ BIO *b;
+
+ if ((b = BIO_new_file(filename, "r")) != NULL)
+ crl = d2i_X509_CRL_bio(b, NULL);
+
+ BIO_free(b);
+ return crl;
+}
+
+
+
+/*
+ * Parse interesting stuff from a certificate.
*/
-static char *extract_crldp_uri(STACK_OF(DIST_POINT) *crldp)
+static void extract_crldp_uri(STACK_OF(DIST_POINT) *crldp,
+ char *uri, int urilen)
{
DIST_POINT *d;
int i;
if (!crldp || sk_DIST_POINT_num(crldp) != 1)
- return NULL;
+ return;
d = sk_DIST_POINT_value(crldp, 0);
if (d->reasons || d->CRLissuer || !d->distpoint || d->distpoint->type != 0)
- return NULL;
+ return;
for (i = 0; i < sk_GENERAL_NAME_num(d->distpoint->name.fullname); i++) {
GENERAL_NAME *n = sk_GENERAL_NAME_value(d->distpoint->name.fullname, i);
assert(n != NULL);
if (n->type != GEN_URI)
- return NULL;
- if (!strncmp(n->d.uniformResourceIdentifier->data,
- "rsync://", sizeof("rsync://") - 1))
- return strdup(n->d.uniformResourceIdentifier->data);
+ return;
+ if (is_rsync(n->d.uniformResourceIdentifier->data) &&
+ urilen > n->d.uniformResourceIdentifier->length) {
+ strcpy(uri, n->d.uniformResourceIdentifier->data);
+ return;
+ }
}
-
- return NULL;
}
-static char *extract_access_uri(AUTHORITY_INFO_ACCESS *xia,
- unsigned char *oid, int oidlen)
+static void extract_access_uri(AUTHORITY_INFO_ACCESS *xia,
+ unsigned char *oid, int oidlen,
+ char *uri, int urilen)
{
int i;
if (!xia)
- return NULL;
+ return;
for (i = 0; i < sk_ACCESS_DESCRIPTION_num(xia); i++) {
ACCESS_DESCRIPTION *a = sk_ACCESS_DESCRIPTION_value(xia, i);
assert(a != NULL);
if (a->location->type != GEN_URI)
- return NULL;
+ return;
if (a->method->length == oidlen &&
!memcmp(a->method->data, oid, oidlen) &&
- !strncmp(a->location->d.uniformResourceIdentifier->data,
- "rsync://", sizeof("rsync://") - 1))
- return strdup(a->location->d.uniformResourceIdentifier->data);
+ is_rsync(a->location->d.uniformResourceIdentifier->data) &&
+ urilen > a->location->d.uniformResourceIdentifier->length) {
+ strcpy(uri, a->location->d.uniformResourceIdentifier->data);
+ return;
+ }
}
}
-
-
-/*
- * Read certificate in DER format.
- */
-
-static X509 *read_cert(const char *filename)
-{
- X509 *x = NULL;
- BIO *b;
-
- if ((b = BIO_new_file(filename, "r")) != NULL)
- x = d2i_X509_bio(b, NULL);
-
- BIO_free(b);
- return x;
-}
-
-static void rpki_cert_free(rpki_cert_t *c)
-{
- if (!c)
- return;
- X509_free(c->x);
-
-}
-
-static rpki_cert_t *rpki_cert_read(const char *filename)
+static void parse_cert(X509 *x, certinfo_t *c)
{
static unsigned char aia_oid[] = {0x2b, 0x6, 0x1, 0x5, 0x5, 0x7, 0x30, 0x2};
static unsigned char sia_oid[] = {0x2b, 0x6, 0x1, 0x5, 0x5, 0x7, 0x30, 0x5};
STACK_OF(DIST_POINT) *crldp;
AUTHORITY_INFO_ACCESS *xia;
- rpki_cert_t *c;
- if ((c = malloc(sizeof(*c))) == NULL)
- return NULL;
+ assert(x != NULL && c != NULL);
memset(c, 0, sizeof(*c));
- if ((c->x = read_cert(filename)) == NULL) {
- rpki_cert_free(c);
- return NULL;
- }
-
- c->ca = X509_check_ca(c->x) == 1;
+ c->ca = X509_check_ca(x) == 1;
- if ((xia = X509_get_ext_d2i(c->x, NID_info_access, NULL, NULL)) != NULL) {
- c->aia = extract_access_uri(xia, aia_oid, sizeof(aia_oid));
+ if ((xia = X509_get_ext_d2i(x, NID_info_access, NULL, NULL)) != NULL) {
+ extract_access_uri(xia, aia_oid, sizeof(aia_oid), c->aia, sizeof(c->aia));
sk_ACCESS_DESCRIPTION_pop_free(xia, ACCESS_DESCRIPTION_free);
}
- if ((xia = X509_get_ext_d2i(c->x, NID_sinfo_access, NULL, NULL)) != NULL) {
- c->sia = extract_access_uri(xia, sia_oid, sizeof(sia_oid));
+ if ((xia = X509_get_ext_d2i(x, NID_sinfo_access, NULL, NULL)) != NULL) {
+ extract_access_uri(xia, sia_oid, sizeof(sia_oid), c->sia, sizeof(c->sia));
sk_ACCESS_DESCRIPTION_pop_free(xia, ACCESS_DESCRIPTION_free);
}
- if ((crldp = X509_get_ext_d2i(c->x, NID_crl_distribution_points,
+ if ((crldp = X509_get_ext_d2i(x, NID_crl_distribution_points,
NULL, NULL)) != NULL) {
- c->crldp = extract_crldp_uri(crldp);
+ extract_crldp_uri(crldp, c->crldp, sizeof(c->crldp));
sk_DIST_POINT_pop_free(crldp, DIST_POINT_free);
}
-
- return c;
}
/*
- * Read CRL in DER format.
+ * Functions I'll probably need for the rest of this:
+ *
+ * X509_verify() verify cert against a key (no chain)
+ * X509_CRL_verify() verify CRL against a key
+ * X509_verify_cert() verify cert against X509_STORE_CTX
+ * (but ctx points to X509_STORE,
+ * which points to X509_VERIFY_PARAM, ...)
+ * X509_get_pubkey() extract pubkey from cert for *_verify()
+ * X509_STORE_CTX_init() initialize ctx
+ * X509_STORE_CTX_trusted_stack() stack of trusted certs instead of
+ * bothering with X509_STORE
+ * X509_STORE_CTX_set0_crls() set crls
+ * X509_STORE_get_by_subject() find object in ctx/store
+ *
+ * We probably can't use the lookup method stuff because we're using
+ * URI naming, so just load everything ourselves and don't specify any
+ * lookup methods, either it works or it doesn't. Hmm, looks like
+ * X509_STORE_CTX_trusted_stack() was written for apps like this.
+ *
+ * Maybe we can restore stack state by using sk_dup() to save then
+ * swapping to the saved stack? Still need to clean up objects on the
+ * stack, though, sk_pop_free() will get rid of everything which is
+ * not what we want unless the reference counting thing bails us out.
+ * Don't think the reference counts work this way.
*/
-static X509_CRL *read_crl(const char *filename)
+
+
+/*
+ * Check whether we already have a particular CRL, attempt to get it
+ * if we don't.
+ */
+
+static X509_CRL *check_crl_1(const char *uri, char *path, int pathlen,
+ const char *prefix, STACK_OF(X509) *trusted_certs)
{
- X509_CRL *crl = NULL;
- BIO *b;
+ X509_STORE_CTX ctx;
+ X509_OBJECT xobj;
+ EVP_PKEY *pkey;
+ X509_CRL *crl;
+ int ret;
- if ((b = BIO_new_file(filename, "r")) != NULL)
- crl = d2i_X509_CRL_bio(b, NULL);
+ assert(uri != NULL && path != NULL && trusted_certs != NULL);
- BIO_free(b);
- return crl;
+ if (!uri_to_filename(uri, path, pathlen, prefix) ||
+ (crl = read_crl(path)) == NULL)
+ return NULL;
+
+ if (!X509_STORE_CTX_init(&ctx, NULL, NULL, NULL))
+ goto punt;
+ X509_STORE_CTX_trusted_stack(&ctx, trusted_certs);
+
+ if (X509_STORE_get_by_subject(&ctx, X509_LU_X509,
+ X509_CRL_get_issuer(crl), &xobj) <= 0)
+ goto punt;
+
+ pkey = X509_get_pubkey(xobj.data.x509);
+ X509_OBJECT_free_contents(&xobj);
+ if (!pkey)
+ goto punt;
+
+ ret = X509_CRL_verify(crl, pkey);
+ EVP_PKEY_free(pkey);
+ if (ret > 0)
+ return crl;
+
+ punt:
+ X509_CRL_free(crl);
+ return NULL;
+}
+
+static int check_crl(char *uri,
+ STACK_OF(X509) *trusted_certs,
+ STACK_OF(X509_CRL) *crl_cache)
+{
+ char path[FILENAME_MAX];
+ X509_CRL *crl;
+
+ if (uri_to_filename(uri, path, sizeof(path), authenticated) &&
+ access(path, R_OK) == 0)
+ return 1;
+
+ rsync(uri);
+
+ if ((crl = check_crl_1(uri, path, sizeof(path),
+ unauthenticated, trusted_certs)) ||
+ (crl = check_crl_1(uri, path, sizeof(path),
+ old_authenticated, trusted_certs))) {
+ install_object(uri, path);
+ if (!crl_cache || !sk_X509_CRL_push(crl_cache, crl))
+ X509_CRL_free(crl);
+ return 1;
+ }
+
+ return 0;
}