diff options
Diffstat (limited to 'rcynic/rcynic.c')
-rw-r--r-- | rcynic/rcynic.c | 226 |
1 files changed, 147 insertions, 79 deletions
diff --git a/rcynic/rcynic.c b/rcynic/rcynic.c index 5825ef3f..9c9cd45e 100644 --- a/rcynic/rcynic.c +++ b/rcynic/rcynic.c @@ -16,9 +16,13 @@ /* $Id$ */ -/* +/** @mainpage * "Cynical rsync": Recursively walk RPKI tree using rsync to pull * data from remote sites, validating certificates and CRLs as we go. + * + * Doxygen doesn't quite know what to make of a one-file C program, + * and ends up putting most of the interesting data @link rcynic.c + * here. @endlink */ #include <assert.h> @@ -70,7 +74,7 @@ #define XML_SUMMARY_VERSION 1 -/* +/** * Logging levels. Same general idea as syslog(), but our own * catagories based on what makes sense for this program. Default * mappings to syslog() priorities are here because it's the easiest @@ -98,10 +102,9 @@ static const struct { }; #undef QQ -/* - * MIB counters. We import a long list of validation failure codes - * from OpenSSL (crypto/x509/x509_vfy.h), but we also have codes - * specific to rcynic. +/** + * MIB counters derived from OpenSSL. Long list of validation failure + * codes from OpenSSL (crypto/x509/x509_vfy.h). */ #define MIB_COUNTERS_FROM_OPENSSL \ @@ -146,6 +149,10 @@ static const struct { QV(X509_V_ERR_NO_EXPLICIT_POLICY) \ QV(X509_V_ERR_UNNESTED_RESOURCE) +/** + * MIB counters specific to rcynic. + */ + #define MIB_COUNTERS \ QQ(backup_cert_accepted, "Backup certificates accepted") \ QQ(backup_cert_rejected, "Backup certificates rejected") \ @@ -201,7 +208,7 @@ static const long mib_counter_openssl[] = { MIB_COUNTERS 0 }; #undef QV #undef QQ -/* +/** * Per-host MIB counter object. * hostname[] must be first element. */ @@ -210,7 +217,7 @@ typedef struct host_counter { unsigned long counters[MIB_COUNTER_T_MAX]; } host_mib_counter_t; -/* +/** * Structure to hold data parsed out of a certificate. */ typedef struct certinfo { @@ -218,7 +225,7 @@ typedef struct certinfo { char uri[URI_MAX], sia[URI_MAX], aia[URI_MAX], crldp[URI_MAX], manifest[URI_MAX]; } certinfo_t; -/* +/** * Program context that would otherwise be a mess of global variables. */ typedef struct rcynic_ctx { @@ -231,9 +238,9 @@ typedef struct rcynic_ctx { X509_STORE *x509_store; } rcynic_ctx_t; -/* +/** * Extended context for verify callbacks. This is a wrapper around - * OpenSSL's X509_STORE_CTX, and the embedded X509_STORE_CTX -must- be + * OpenSSL's X509_STORE_CTX, and the embedded X509_STORE_CTX @em must be * the first element of this structure in order for the evil cast to * do the right thing. This is ugly but safe, as the C language * promises us that the address of the first element of a structure is @@ -245,6 +252,9 @@ typedef struct rcynic_x509_store_ctx { const certinfo_t *subj; } rcynic_x509_store_ctx_t; +/** + * Subversion ID data. + */ static const char svn_id[] = "$Id$"; @@ -295,7 +305,7 @@ IMPLEMENT_ASN1_FUNCTIONS(Manifest) -/* +/** * Logging. */ static void logmsg(const rcynic_ctx_t *rc, @@ -330,7 +340,7 @@ static void logmsg(const rcynic_ctx_t *rc, va_end(ap); } -/* +/** * Print OpenSSL library errors. */ static void log_openssl_errors(const rcynic_ctx_t *rc) @@ -352,7 +362,7 @@ static void log_openssl_errors(const rcynic_ctx_t *rc) } } -/* +/** * Configure logging. */ static int configure_logmsg(rcynic_ctx_t *rc, const char *name) @@ -372,7 +382,7 @@ static int configure_logmsg(rcynic_ctx_t *rc, const char *name) return 0; } -/* +/** * Configure syslog. */ static int configure_syslog(const rcynic_ctx_t *rc, @@ -394,7 +404,7 @@ static int configure_syslog(const rcynic_ctx_t *rc, } } -/* +/** * Configure boolean variable. */ static int configure_boolean(const rcynic_ctx_t *rc, @@ -416,7 +426,7 @@ static int configure_boolean(const rcynic_ctx_t *rc, } } -/* +/** * Configure integer variable. */ static int configure_integer(const rcynic_ctx_t *rc, @@ -441,7 +451,7 @@ static int configure_integer(const rcynic_ctx_t *rc, -/* +/** * Make a directory if it doesn't already exist. */ static int mkdir_maybe(const rcynic_ctx_t *rc, const char *name) @@ -468,7 +478,7 @@ static int mkdir_maybe(const rcynic_ctx_t *rc, const char *name) return mkdir(buffer, 0777) == 0; } -/* +/** * strdup() a string and push it onto a stack. */ static int sk_push_strdup(STACK *sk, const char *str) @@ -482,7 +492,7 @@ static int sk_push_strdup(STACK *sk, const char *str) return 0; } -/* +/** * Compare two URI strings, for OpenSSL STACK operations. */ @@ -491,7 +501,7 @@ static int uri_cmp(const char * const *a, const char * const *b) return strcmp(*a, *b); } -/* +/** * Is string an rsync URI? */ static int is_rsync(const char *uri) @@ -499,7 +509,7 @@ static int is_rsync(const char *uri) return uri && !strncmp(uri, "rsync://", SIZEOF_RSYNC); } -/* +/** * Convert an rsync URI to a filename, checking for evil character * sequences. */ @@ -538,7 +548,7 @@ static int uri_to_filename(const char *name, return 1; } -/* +/** * OID comparison. */ static int oid_cmp(const ASN1_OBJECT *obj, const unsigned char *oid, const size_t oidlen) @@ -550,7 +560,7 @@ static int oid_cmp(const ASN1_OBJECT *obj, const unsigned char *oid, const size_ return memcmp(obj->data, oid, oidlen); } -/* +/** * Host MIB counter comparision. This relies on hostname[] being the * first element of a host_mib_counter_t, hence the (unreadable, but * correct ANSI/ISO C) assertion. Given all the icky casts involved @@ -563,7 +573,7 @@ static int host_counter_cmp(const char * const *a, const char * const *b) return strcasecmp(*a, *b); } -/* +/** * MIB counter manipulation. */ static void mib_increment(const rcynic_ctx_t *rc, @@ -605,16 +615,9 @@ static void mib_increment(const rcynic_ctx_t *rc, h->counters[counter]++; } -/* - * 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. - * - * Well, ok, profiling didn't show an issue, but inode exhaustion did. - * So we now make copy vs link a configuration choice. +/** + * Copy a file */ - static int cp(const char *source, const char *target) { FILE *in = NULL, *out = NULL; @@ -636,12 +639,26 @@ static int cp(const char *source, const char *target) return ret; } +/** + * Link a file + */ + static int ln(const char *source, const char *target) { unlink(target); return link(source, target) == 0; } +/** + * 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. + * + * Well, ok, profiling didn't show an issue, but inode exhaustion did. + * So we now make copy vs link a configuration choice. + */ + static int install_object(const rcynic_ctx_t *rc, const char *uri, const char *source, @@ -669,7 +686,7 @@ static int install_object(const rcynic_ctx_t *rc, return 1; } -/* +/** * Iterator over URIs in our copy of a SIA collection. * *dir should be NULL when first called. */ @@ -715,7 +732,7 @@ static int next_uri(const rcynic_ctx_t *rc, return 0; } -/* +/** * Set a directory name, making sure it has the trailing slash we * require in various other routines. */ @@ -739,7 +756,7 @@ static void set_directory(char **out, const char *in) *out = s; } -/* +/** * Remove a directory tree, like rm -rf. */ static int rm_rf(const char *name) @@ -800,24 +817,8 @@ static int rm_rf(const char *name) -/* - * Run rsync. This is fairly nasty, because we need to: - * - * (a) Construct the argument list for rsync; - * - * (b) Run rsync in a child process; - * - * (c) Sit listening to rsync's output, logging whatever we get; - * - * (d) Impose an optional time limit on rsync's execution time - * - * (e) Clean up from (b), (c), and (d); and - * - * (f) Keep track of which URIs we've already fetched, so we don't - * have to do it again. - * - * Taken all together, this is pretty icky. Breaking it into separate - * functions wouldn't help much. Don't read this on a full stomach. +/** + * Maintain a cache of URIs we've already fetched. */ static int rsync_cached(const rcynic_ctx_t *rc, @@ -837,6 +838,26 @@ static int rsync_cached(const rcynic_ctx_t *rc, return 1; } +/** + * Run rsync. This is fairly nasty, because we need to: + * + * @li Construct the argument list for rsync; + * + * @li Run rsync in a child process; + * + * @li Sit listening to rsync's output, logging whatever we get; + * + * @li Impose an optional time limit on rsync's execution time + * + * @li Clean up from (b), (c), and (d); and + * + * @li Keep track of which URIs we've already fetched, so we don't + * have to do it again. + * + * Taken all together, this is pretty icky. Breaking it into separate + * functions wouldn't help much. Don't read this on a full stomach. + */ + static int rsync(const rcynic_ctx_t *rc, const char * const *args, const char *uri) @@ -1023,16 +1044,28 @@ static int rsync(const rcynic_ctx_t *rc, return ret; } +/** + * rsync a CRL. + */ + static int rsync_crl(const rcynic_ctx_t *rc, const char *uri) { return rsync(rc, NULL, uri); } +/** + * rsync a manifest. + */ + static int rsync_manifest(const rcynic_ctx_t *rc, const char *uri) { return rsync(rc, NULL, uri); } +/** + * rsync an SIA collection. + */ + static int rsync_sia(const rcynic_ctx_t *rc, const char *uri) { static const char * const rsync_args[] = { "--recursive", "--delete", NULL }; @@ -1041,7 +1074,7 @@ static int rsync_sia(const rcynic_ctx_t *rc, const char *uri) -/* +/** * Clean up old stuff from previous rsync runs. --delete doesn't help * if the URI changes and we never visit the old URI again. */ @@ -1125,7 +1158,7 @@ static int prune_unauthenticated(const rcynic_ctx_t *rc, -/* +/** * Read a DER object using a BIO pipeline that hashes the file content * as we read it. Returns the internal form of the parsed DER object, * sets the hash buffer (if specified) as a side effect. The default @@ -1170,7 +1203,7 @@ static void *read_file_with_hash(const char *filename, return result; } -/* +/** * Read and hash a certificate. */ static X509 *read_cert(const char *filename, unsigned char *hashbuf, const size_t hashlen) @@ -1178,7 +1211,7 @@ static X509 *read_cert(const char *filename, unsigned char *hashbuf, const size_ return read_file_with_hash(filename, ASN1_ITEM_rptr(X509), NULL, hashbuf, hashlen); } -/* +/** * Read and hash a CRL. */ static X509_CRL *read_crl(const char *filename, unsigned char *hashbuf, const size_t hashlen) @@ -1186,7 +1219,7 @@ static X509_CRL *read_crl(const char *filename, unsigned char *hashbuf, const si return read_file_with_hash(filename, ASN1_ITEM_rptr(X509_CRL), NULL, hashbuf, hashlen); } -/* +/** * Read and hash a CMS message. */ static CMS_ContentInfo *read_cms(const char *filename, unsigned char *hashbuf, const size_t hashlen) @@ -1196,8 +1229,8 @@ static CMS_ContentInfo *read_cms(const char *filename, unsigned char *hashbuf, c -/* - * Parse interesting stuff from a certificate. +/** + * Extract CRLDP data from a certificate. */ static void extract_crldp_uri(const STACK_OF(DIST_POINT) *crldp, @@ -1227,6 +1260,10 @@ static void extract_crldp_uri(const STACK_OF(DIST_POINT) *crldp, } } +/** + * Extract SIA or AIA data from a certificate. + */ + static void extract_access_uri(const AUTHORITY_INFO_ACCESS *xia, const unsigned char *oid, const int oidlen, @@ -1251,6 +1288,10 @@ static void extract_access_uri(const AUTHORITY_INFO_ACCESS *xia, } } +/** + * Parse interesting stuff from a certificate. + */ + static void parse_cert(X509 *x, certinfo_t *c, const char *uri) { static const unsigned char id_ad_caIssuers[] = {0x2b, 0x6, 0x1, 0x5, 0x5, 0x7, 0x30, 0x2}; @@ -1288,9 +1329,8 @@ static void parse_cert(X509 *x, certinfo_t *c, const char *uri) -/* - * Check whether we already have a particular CRL, attempt to fetch it - * and check issuer's signature if we don't. +/** + * Attempt to read and check one CRL from disk. */ static X509_CRL *check_crl_1(const char *uri, @@ -1321,6 +1361,11 @@ static X509_CRL *check_crl_1(const char *uri, return NULL; } +/** + * Check whether we already have a particular CRL, attempt to fetch it + * and check issuer's signature if we don't. + */ + static X509_CRL *check_crl(const rcynic_ctx_t *rc, const char *uri, X509 *issuer) @@ -1359,13 +1404,12 @@ static X509_CRL *check_crl(const rcynic_ctx_t *rc, -/* - * Check whether we already have a particular manifest, attempt to fetch it - * and check issuer's signature if we don't. - */ - static int check_x509_cb(int ok, X509_STORE_CTX *ctx); +/** + * Read and check one manifest from disk. + */ + static Manifest *check_manifest_1(const rcynic_ctx_t *rc, const char *uri, char *path, @@ -1492,6 +1536,11 @@ static Manifest *check_manifest_1(const rcynic_ctx_t *rc, return result; } +/** + * Check whether we already have a particular manifest, attempt to fetch it + * and check issuer's signature if we don't. + */ + static Manifest *check_manifest(const rcynic_ctx_t *rc, const char *uri, STACK_OF(X509) *certs) @@ -1543,9 +1592,8 @@ static Manifest *check_manifest(const rcynic_ctx_t *rc, -/* - * Check a certificate, including all the crypto, path validation, - * and checks for conformance to the RPKI certificate profile. +/** + * Validation callback function for use with x509_verify_cert(). */ static int check_x509_cb(int ok, X509_STORE_CTX *ctx) @@ -1607,6 +1655,11 @@ static int check_x509_cb(int ok, X509_STORE_CTX *ctx) return ok; } +/** + * Check crypto aspects of a certificate, including policy checks + * and RFC 3779 path validation. + */ + static int check_x509(const rcynic_ctx_t *rc, STACK_OF(X509) *certs, X509 *x, @@ -1681,6 +1734,10 @@ static int check_x509(const rcynic_ctx_t *rc, return ret; } +/** + * Check a certificate for conformance to the RPKI certificate profile. + */ + static X509 *check_cert_1(const rcynic_ctx_t *rc, const char *uri, char *path, @@ -1752,6 +1809,11 @@ static X509 *check_cert_1(const rcynic_ctx_t *rc, return NULL; } +/** + * Try to find a good copy of a certificate either in fresh data or in + * backup data from a previous run of this program. + */ + static X509 *check_cert(rcynic_ctx_t *rc, char *uri, STACK_OF(X509) *certs, @@ -1804,16 +1866,16 @@ static X509 *check_cert(rcynic_ctx_t *rc, -/* +static void walk_cert(rcynic_ctx_t *rc, + const certinfo_t *parent, + STACK_OF(X509) *certs); + +/** * Recursive walk of certificate hierarchy (core of the program). The * daisy chain recursion is to avoid having to duplicate the stack * manipulation and error handling. */ -static void walk_cert(rcynic_ctx_t *rc, - const certinfo_t *parent, - STACK_OF(X509) *certs); - static void walk_cert_1(rcynic_ctx_t *rc, char *uri, STACK_OF(X509) *certs, @@ -1837,6 +1899,12 @@ static void walk_cert_1(rcynic_ctx_t *rc, X509_free(sk_X509_pop(certs)); } +/** + * Recursive walk of certificate hierarchy (core of the program). The + * daisy chain recursion is to avoid having to duplicate the stack + * manipulation and error handling. + */ + static void walk_cert(rcynic_ctx_t *rc, const certinfo_t *parent, STACK_OF(X509) *certs) @@ -1883,7 +1951,7 @@ static void walk_cert(rcynic_ctx_t *rc, -/* +/** * Main program. Parse command line, read config file, iterate over * trust anchors found via config file and do a tree walk for each * trust anchor. |