diff options
author | Rob Austein <sra@hactrn.net> | 2006-09-22 21:30:07 +0000 |
---|---|---|
committer | Rob Austein <sra@hactrn.net> | 2006-09-22 21:30:07 +0000 |
commit | 6ebbfd4aa0b28a8af2174923fba0d052816a24f8 (patch) | |
tree | dd64f10a3b9c9974d5ba06b6feb49f094b3299d9 | |
parent | 37a656af95671358593ed9fb25357a2b0a3c7d26 (diff) |
Checkpoint
svn path=/rcynic/rcynic.c; revision=310
-rw-r--r-- | rcynic/rcynic.c | 369 |
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; } |