diff options
-rw-r--r-- | rcynic/rcynic.c | 380 | ||||
-rw-r--r-- | rcynic/rcynic.xsl | 27 |
2 files changed, 274 insertions, 133 deletions
diff --git a/rcynic/rcynic.c b/rcynic/rcynic.c index ca52c69a..a8d59dab 100644 --- a/rcynic/rcynic.c +++ b/rcynic/rcynic.c @@ -181,10 +181,12 @@ static const struct { QV(X509_V_ERR_UNNESTED_RESOURCE) /** - * MIB counters specific to rcynic. + * MIB counters specific to rcynic. "validation_ok" is not used as a + * counter, but is used as a validation status code. */ #define MIB_COUNTERS \ + QQ(validation_ok, "OK") \ QQ(backup_cert_accepted, "Backup certificates accepted") \ QQ(backup_cert_rejected, "Backup certificates rejected") \ QQ(backup_crl_accepted, "Backup CRLs accepted") \ @@ -236,6 +238,13 @@ static const struct { QQ(trust_anchor_not_self_signed, "Trust anchor not self-signed") \ QQ(uri_too_long, "URI too long") \ QQ(malformed_crldp, "Malformed CRDLP extension") \ + QQ(certificate_bad_signature, "Bad certificate signature") \ + QQ(certificate_bad_crl, "Bad certificate CRL") \ + QQ(manifest_bad_crl, "Manifest has bad CRL") \ + QQ(roa_resources_malformed, "ROA resources malformed") \ + QQ(roa_bad_afi, "ROA contains bad AFI value") \ + QQ(roa_not_nested, "ROA resources not in EE") \ + QQ(roa_bad_crl, "ROA EE has bad CRL") \ MIB_COUNTERS_FROM_OPENSSL #define QV(x) QQ(mib_openssl_##x, 0) @@ -272,6 +281,17 @@ typedef struct host_mib_counter { DECLARE_STACK_OF(HOST_MIB_COUNTER) /** + * Per-URI validation status object. + */ +typedef struct validation_status { + char uri[URI_MAX]; + time_t timestamp; + mib_counter_t code; +} VALIDATION_STATUS; + +DECLARE_STACK_OF(VALIDATION_STATUS) + +/** * Structure to hold data parsed out of a certificate. */ typedef struct certinfo { @@ -287,6 +307,7 @@ typedef struct rcynic_ctx { char *jane, *rsync_program; STACK_OF(OPENSSL_STRING) *rsync_cache, *backup_cache, *stale_cache; STACK_OF(HOST_MIB_COUNTER) *host_counters; + STACK_OF(VALIDATION_STATUS) *validation_status; int indent, use_syslog, allow_stale_crl, allow_stale_manifest, use_links; int require_crl_in_manifest, rsync_timeout, priority[LOG_LEVEL_T_MAX]; int allow_non_self_signed_trust_anchor; @@ -369,6 +390,17 @@ static HOST_MIB_COUNTER *HOST_MIB_COUNTER_new(void) } /** + * Allocate a new VALIDATION_STATUS object. + */ +static VALIDATION_STATUS *VALIDATION_STATUS_new(void) +{ + VALIDATION_STATUS *v = malloc(sizeof(*v)); + if (v) + memset(v, 0, sizeof(*v)); + return v; +} + +/** * Type-safe wrapper around free() to keep safestack macros happy. */ static void HOST_MIB_COUNTER_free(HOST_MIB_COUNTER *h) @@ -376,6 +408,14 @@ static void HOST_MIB_COUNTER_free(HOST_MIB_COUNTER *h) free(h); } +/** + * Type-safe wrapper around free() to keep safestack macros happy. + */ +static void VALIDATION_STATUS_free(VALIDATION_STATUS *v) +{ + free(v); +} + /* * Safestack macros for HOST_MIB_COUNTER. */ @@ -402,6 +442,32 @@ static void HOST_MIB_COUNTER_free(HOST_MIB_COUNTER *h) #define sk_HOST_MIB_COUNTER_sort(st) SKM_sk_sort(HOST_MIB_COUNTER, (st)) #define sk_HOST_MIB_COUNTER_is_sorted(st) SKM_sk_is_sorted(HOST_MIB_COUNTER, (st)) +/* + * Safestack macros for VALIDATION_STATUS. + */ + +#define sk_VALIDATION_STATUS_new(st) SKM_sk_new(VALIDATION_STATUS, (st)) +#define sk_VALIDATION_STATUS_new_null() SKM_sk_new_null(VALIDATION_STATUS) +#define sk_VALIDATION_STATUS_free(st) SKM_sk_free(VALIDATION_STATUS, (st)) +#define sk_VALIDATION_STATUS_num(st) SKM_sk_num(VALIDATION_STATUS, (st)) +#define sk_VALIDATION_STATUS_value(st, i) SKM_sk_value(VALIDATION_STATUS, (st), (i)) +#define sk_VALIDATION_STATUS_set(st, i, val) SKM_sk_set(VALIDATION_STATUS, (st), (i), (val)) +#define sk_VALIDATION_STATUS_zero(st) SKM_sk_zero(VALIDATION_STATUS, (st)) +#define sk_VALIDATION_STATUS_push(st, val) SKM_sk_push(VALIDATION_STATUS, (st), (val)) +#define sk_VALIDATION_STATUS_unshift(st, val) SKM_sk_unshift(VALIDATION_STATUS, (st), (val)) +#define sk_VALIDATION_STATUS_find(st, val) SKM_sk_find(VALIDATION_STATUS, (st), (val)) +#define sk_VALIDATION_STATUS_find_ex(st, val) SKM_sk_find_ex(VALIDATION_STATUS, (st), (val)) +#define sk_VALIDATION_STATUS_delete(st, i) SKM_sk_delete(VALIDATION_STATUS, (st), (i)) +#define sk_VALIDATION_STATUS_delete_ptr(st, ptr) SKM_sk_delete_ptr(VALIDATION_STATUS, (st), (ptr)) +#define sk_VALIDATION_STATUS_insert(st, val, i) SKM_sk_insert(VALIDATION_STATUS, (st), (val), (i)) +#define sk_VALIDATION_STATUS_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(VALIDATION_STATUS, (st), (cmp)) +#define sk_VALIDATION_STATUS_dup(st) SKM_sk_dup(VALIDATION_STATUS, st) +#define sk_VALIDATION_STATUS_pop_free(st, free_func) SKM_sk_pop_free(VALIDATION_STATUS, (st), (free_func)) +#define sk_VALIDATION_STATUS_shift(st) SKM_sk_shift(VALIDATION_STATUS, (st)) +#define sk_VALIDATION_STATUS_pop(st) SKM_sk_pop(VALIDATION_STATUS, (st)) +#define sk_VALIDATION_STATUS_sort(st) SKM_sk_sort(VALIDATION_STATUS, (st)) +#define sk_VALIDATION_STATUS_is_sorted(st) SKM_sk_is_sorted(VALIDATION_STATUS, (st)) + /* @@ -568,21 +634,19 @@ IMPLEMENT_ASN1_FUNCTIONS(ROA) /** * Logging. */ -static void logmsg(const rcynic_ctx_t *rc, - const log_level_t level, - const char *fmt, ...) +static void vlogmsg(const rcynic_ctx_t *rc, + const log_level_t level, + const char *fmt, + va_list ap) { char tad[sizeof("00:00:00")+1]; time_t tad_time; - va_list ap; assert(rc && fmt); if (rc->log_level < level) return; - va_start(ap, fmt); - if (rc->use_syslog) { vsyslog(rc->priority[level], fmt, ap); } else { @@ -596,7 +660,18 @@ static void logmsg(const rcynic_ctx_t *rc, vfprintf(stderr, fmt, ap); putc('\n', stderr); } +} +/** + * Logging. + */ +static void logmsg(const rcynic_ctx_t *rc, + const log_level_t level, + const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vlogmsg(rc, level, fmt, ap); va_end(ap); } @@ -848,7 +923,7 @@ static void mib_increment(const rcynic_ctx_t *rc, HOST_MIB_COUNTER *h = NULL, hn; char *s; - assert(rc && uri); + assert(rc && uri && strlen(uri) < URI_MAX); if (!rc->host_counters) return; @@ -883,6 +958,60 @@ static void mib_increment(const rcynic_ctx_t *rc, } /** + * Add a validation status entry to internal log. + */ +static void log_validation_status(const rcynic_ctx_t *rc, + const char *uri, + const mib_counter_t code) +{ + VALIDATION_STATUS *v = NULL; + + assert(rc && uri && strlen(uri) < URI_MAX); + + if (!rc->validation_status) + return; + + if ((v = VALIDATION_STATUS_new()) == NULL) { + logmsg(rc, log_sys_err, "Couldn't allocate validation status entry for %s", uri); + goto punt; + } + + strcpy(v->uri, uri); + v->timestamp = time(0); + v->code = code; + + if (!sk_VALIDATION_STATUS_push(rc->validation_status, v)) { + logmsg(rc, log_sys_err, "Couldn't store validation status entry for %s", uri); + goto punt; + } + + v = NULL; + + punt: + if (v) + free(v); +} + +/** + * Reject an object. + */ +static void reject(const rcynic_ctx_t *rc, + const char *uri, + const mib_counter_t code, + const char *fmt, ...) +{ + char format[URI_MAX * 2]; + va_list ap; + + assert(fmt && strlen(fmt) + sizeof("Rejected %s") < sizeof(format)); + snprintf(format, sizeof(format), "Rejected %s %s", uri, fmt); + log_validation_status(rc, uri, code); + va_start(ap, fmt); + vlogmsg(rc, log_data_err, format, ap); + va_end(ap); +} + +/** * Copy a file */ static int cp(const char *source, const char *target) @@ -945,7 +1074,7 @@ static int install_object(const rcynic_ctx_t *rc, (rc->use_links ? "link" : "copy"), source, target); return 0; } - + log_validation_status(rc, uri, validation_ok); logmsg(rc, log_telemetry, "Accepted %s", uri); return 1; } @@ -1634,8 +1763,8 @@ static X509_CRL *check_crl_1(const rcynic_ctx_t *rc, goto punt; if (hash && memcmp(hashbuf, hash, hashlen)) { - logmsg(rc, log_data_err, "Rejected %s because manifest digest did not match CRL", uri); - mib_increment(rc, uri, crl_digest_mismatch); + reject(rc, uri, crl_digest_mismatch, + "because digest of CRL did not match value from manifest"); goto punt; } @@ -1708,6 +1837,7 @@ static X509_CRL *check_crl(const rcynic_ctx_t *rc, static int check_x509_cb(int ok, X509_STORE_CTX *ctx) { rcynic_x509_store_ctx_t *rctx = (rcynic_x509_store_ctx_t *) ctx; + mib_counter_t counter; assert(rctx != NULL); @@ -1740,10 +1870,10 @@ static int check_x509_cb(int ok, X509_STORE_CTX *ctx) "Couldn't cache stale CRLDP %s, blundering onward", rctx->subj->crldp); } logmsg(rctx->rc, log_data_err, "Stale CRL %s", rctx->subj->crldp); - if (!ok) - logmsg(rctx->rc, log_data_err, "Rejected %s due to stale CRL %s", - rctx->subj->uri, rctx->subj->crldp); - mib_increment(rctx->rc, rctx->subj->uri, stale_crl); + if (ok) + mib_increment(rctx->rc, rctx->subj->uri, stale_crl); + else + reject(rctx->rc, rctx->subj->uri, stale_crl, "due to stale CRL %s", rctx->subj->crldp); return ok; case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: @@ -1762,35 +1892,36 @@ static int check_x509_cb(int ok, X509_STORE_CTX *ctx) */ if (rctx->rc->allow_non_self_signed_trust_anchor) ok = 1; - logmsg(rctx->rc, log_data_err, - (ok - ? "Trust anchor not self-signed while checking %s" - : "Rejected %s because trust anchor was not self-signed"), - rctx->subj->uri); - mib_increment(rctx->rc, rctx->subj->uri, trust_anchor_not_self_signed); + if (ok) + mib_increment(rctx->rc, rctx->subj->uri, trust_anchor_not_self_signed); + else + reject(rctx->rc, rctx->subj->uri, trust_anchor_not_self_signed, + "because trust anchor was not self-signed"); return ok; /* - * Increment counters for all known OpenSSL verify errors except the - * ones we handle explicitly above, then fall through to generate - * appropriate error message. + * Select correct MIB counter for every known OpenSSL verify errors + * except the ones we handle explicitly above, then fall through to + * common handling for all of these. */ #define QV(x) \ case x: \ - mib_increment(rctx->rc, rctx->subj->uri, mib_openssl_##x); \ + counter = mib_openssl_##x; \ break; MIB_COUNTERS_FROM_OPENSSL; #undef QV default: - mib_increment(rctx->rc, rctx->subj->uri, unknown_verify_error); + counter = unknown_verify_error; break; } - if (!ok) - logmsg(rctx->rc, log_data_err, - "Rejected %s due to validation failure at depth %d: %s", - rctx->subj->uri, ctx->error_depth, + if (ok) + mib_increment(rctx->rc, rctx->subj->uri, counter); + else + reject(rctx->rc, rctx->subj->uri, counter, + "due to validation failure at depth %d: %s", + ctx->error_depth, X509_verify_cert_error_string(ctx->error)); return ok; @@ -1823,17 +1954,15 @@ static int check_x509(const rcynic_ctx_t *rc, rctx.subj = subj; if (!subj->ta && - ((pkey = X509_get_pubkey(issuer)) == NULL || - X509_verify(x, pkey) <= 0)) { - logmsg(rc, log_data_err, - "Rejected %s because it failed signature check prior to CRL fetch", - subj->uri); + ((pkey = X509_get_pubkey(issuer)) == NULL || X509_verify(x, pkey) <= 0)) { + reject(rc, subj->uri, certificate_bad_signature, + "because it failed signature check prior to CRL fetch"); goto done; } if ((crl = check_crl(rc, subj->crldp, issuer, NULL, 0)) == NULL) { - logmsg(rc, log_data_err, "Rejected %s due to bad CRL %s", - subj->uri, subj->crldp); + reject(rc, subj->uri, certificate_bad_crl, + "due to bad CRL %s", subj->crldp); goto done; } @@ -1915,45 +2044,36 @@ static X509 *check_cert_1(const rcynic_ctx_t *rc, } if (hash && memcmp(hashbuf, hash, hashlen)) { - logmsg(rc, log_data_err, - "Rejected %s because did not match manifest digest", uri); - mib_increment(rc, uri, certificate_digest_mismatch); + reject(rc, uri, certificate_digest_mismatch, + "because digest did not match value in manifest"); goto punt; } parse_cert(rc, x, subj, uri); if (subj->sia[0] && subj->sia[strlen(subj->sia) - 1] != '/') { - logmsg(rc, log_data_err, "Rejected %s due to malformed SIA %s", - uri, subj->sia); - mib_increment(rc, uri, malformed_sia); + reject(rc, uri, malformed_sia, + "due to malformed SIA %s", subj->sia); goto punt; } if (!subj->aia[0]) { - logmsg(rc, log_data_err, "Rejected %s due to missing AIA", uri); - mib_increment(rc, uri, aia_missing); + reject(rc, uri, aia_missing, "due to missing AIA"); goto punt; } if (!issuer->ta && strcmp(issuer->uri, subj->aia)) { - logmsg(rc, log_data_err, "Rejected %s because AIA %s doesn't match parent", - uri, subj->aia); - mib_increment(rc, uri, aia_mismatch); + reject(rc, uri, aia_mismatch, "because AIA %s doesn't match parent", subj->aia); goto punt; } if (subj->ca && !subj->sia[0]) { - logmsg(rc, log_data_err, - "Rejected %s because SIA extension is missing", uri); - mib_increment(rc, uri, sia_missing); + reject(rc, uri, sia_missing, "because SIA extension is missing"); goto punt; } if (!subj->crldp[0]) { - logmsg(rc, log_data_err, - "Rejected %s because CRLDP extension is missing", uri); - mib_increment(rc, uri, crldp_missing); + reject(rc, uri, crldp_missing, "because CRLDP extension is missing"); goto punt; } @@ -2061,9 +2181,8 @@ static Manifest *check_manifest_1(const rcynic_ctx_t *rc, if ((eContentType = CMS_get0_eContentType(cms)) == NULL || oid_cmp(eContentType, id_ct_rpkiManifest, sizeof(id_ct_rpkiManifest))) { - logmsg(rc, log_data_err, - "Rejected %s due to bad manifest eContentType", uri); - mib_increment(rc, uri, manifest_bad_econtenttype); + reject(rc, uri, manifest_bad_econtenttype, + "due to bad manifest eContentType"); goto done; } @@ -2073,53 +2192,47 @@ static Manifest *check_manifest_1(const rcynic_ctx_t *rc, } if (CMS_verify(cms, NULL, NULL, NULL, bio, CMS_NO_SIGNER_CERT_VERIFY) <= 0) { - logmsg(rc, log_data_err, - "Rejected %s due to validation failure for manifest CMS message", uri); - mib_increment(rc, uri, manifest_invalid_cms); + reject(rc, uri, manifest_invalid_cms, + "due to validation failure for manifest CMS message"); goto done; } if ((signers = CMS_get0_signers(cms)) == NULL || sk_X509_num(signers) != 1) { - logmsg(rc, log_data_err, - "Rejected %s because could not couldn't extract manifest EE certificate from CMS", uri); - mib_increment(rc, uri, manifest_missing_signer); + reject(rc, uri, manifest_missing_signer, + "because could not couldn't extract manifest EE certificate from CMS"); goto done; } parse_cert(rc, sk_X509_value(signers, 0), &certinfo, uri); if (!certinfo.crldp[0]) { - logmsg(rc, log_data_err, "Rejected %s due to missing CRLDP in manifest EE certificate", uri); - mib_increment(rc, uri, manifest_missing_crldp); + reject(rc, uri, manifest_missing_crldp, + "due to missing CRLDP in manifest EE certificate"); goto done; } if ((crl_tail = strrchr(certinfo.crldp, '/')) == NULL) { - logmsg(rc, log_data_err, - "Rejected %s due to malformed CRLDP %s in manifest EE certificate", - uri, certinfo.crldp); - mib_increment(rc, uri, manifest_malformed_crldp); + reject(rc, uri, manifest_malformed_crldp, + "due to malformed CRLDP %s in manifest EE certificate", + certinfo.crldp); goto done; } crl_tail++; if ((manifest = ASN1_item_d2i_bio(ASN1_ITEM_rptr(Manifest), bio, NULL)) == NULL) { - logmsg(rc, log_data_err, "Rejected %s because unable to decode manifest", uri); - mib_increment(rc, uri, manifest_decode_error); + reject(rc, uri, manifest_decode_error, "because unable to decode manifest"); goto done; } if (manifest->version) { - logmsg(rc, log_data_err, - "Rejected %s because manifest version should be defaulted zero, not %ld", - uri, ASN1_INTEGER_get(manifest->version)); - mib_increment(rc, uri, manifest_wrong_version); + reject(rc, uri, manifest_wrong_version, + "because manifest version should be defaulted zero, not %ld", + ASN1_INTEGER_get(manifest->version)); goto done; } if (X509_cmp_current_time(manifest->thisUpdate) > 0) { - logmsg(rc, log_data_err, "Rejected %s because manifest not yet valid", uri); - mib_increment(rc, uri, manifest_not_yet_valid); + reject(rc, uri, manifest_not_yet_valid, "because manifest not yet valid"); goto done; } @@ -2127,14 +2240,13 @@ static Manifest *check_manifest_1(const rcynic_ctx_t *rc, sk_OPENSSL_STRING_find(rc->stale_cache, uri) < 0) { if (!sk_OPENSSL_STRING_push_strdup(rc->stale_cache, uri)) logmsg(rc, log_sys_err, "Couldn't cache stale manifest %s, blundering onward", uri); - logmsg(rc, log_data_err, - (rc->allow_stale_manifest - ? "Stale manifest %s" - : "Rejected %s because it is a stale manifest"), - uri); - mib_increment(rc, uri, stale_manifest); - if (!rc->allow_stale_manifest) + if (!rc->allow_stale_manifest) { + reject(rc, uri, stale_manifest, + "Rejected %s because it is a stale manifest"); goto done; + } + logmsg(rc, log_data_err, "Stale manifest %s", uri); + mib_increment(rc, uri, stale_manifest); } if (manifest->fileHashAlg == NULL || @@ -2148,22 +2260,19 @@ static Manifest *check_manifest_1(const rcynic_ctx_t *rc, if (fah) { crl = check_crl(rc, certinfo.crldp, sk_X509_value(certs, sk_X509_num(certs) - 1), fah->hash->data, fah->hash->length); + } else if (rc->require_crl_in_manifest) { + reject(rc, uri, crl_not_in_manifest, + "because CRL %s missing from manifest", certinfo.crldp); + goto done; } else { - logmsg(rc, log_data_err, - (rc->require_crl_in_manifest - ? "Rejected %s because CRL %s missing from manifest" - : "Manifest %s is missing entry for CRL %s"), - uri, certinfo.crldp); + logmsg(rc, log_data_err, "Manifest %s is missing entry for CRL %s", uri, certinfo.crldp); mib_increment(rc, uri, crl_not_in_manifest); - if (rc->require_crl_in_manifest) - goto done; crl = check_crl(rc, certinfo.crldp, sk_X509_value(certs, sk_X509_num(certs) - 1), NULL, 0); } if (!crl) { - logmsg(rc, log_data_err, - "Rejected %s due to bad manifest CRL %s", uri, certinfo.crldp); + reject(rc, uri, manifest_bad_crl, "due to bad manifest CRL %s", certinfo.crldp); goto done; } @@ -2344,18 +2453,16 @@ static int check_roa_1(const rcynic_ctx_t *rc, goto error; if (hash && memcmp(hashbuf, hash, hashlen)) { - logmsg(rc, log_data_err, - "Rejected %s because ROA does not match manifest digest", uri); - mib_increment(rc, uri, roa_digest_mismatch); + reject(rc, uri, roa_digest_mismatch, + "because ROA does not match manifest digest"); goto error; } if (!(eContentType = CMS_get0_eContentType(cms)) || oid_cmp(eContentType, id_ct_routeOriginAttestation, sizeof(id_ct_routeOriginAttestation))) { - logmsg(rc, log_data_err, - "Rejected %s because ROA has bad eContentType", uri); - mib_increment(rc, uri, roa_bad_econtenttype); + reject(rc, uri, roa_bad_econtenttype, + "because ROA has bad eContentType"); goto error; } @@ -2365,33 +2472,27 @@ static int check_roa_1(const rcynic_ctx_t *rc, } if (CMS_verify(cms, NULL, NULL, NULL, bio, CMS_NO_SIGNER_CERT_VERIFY) <= 0) { - logmsg(rc, log_data_err, - "Rejected %s because ROA CMS failed validation", uri); - mib_increment(rc, uri, roa_invalid_cms); + reject(rc, uri, roa_invalid_cms, "because ROA CMS failed validation"); goto error; } if (!(signers = CMS_get0_signers(cms)) || sk_X509_num(signers) != 1) { - logmsg(rc, log_data_err, - "Rejected %s because couldn't extract CMS signer from ROA", uri); - mib_increment(rc, uri, roa_missing_signer); + reject(rc, uri, roa_missing_signer, + "because couldn't extract CMS signer from ROA"); goto error; } parse_cert(rc, sk_X509_value(signers, 0), &certinfo, uri); if (!(roa = ASN1_item_d2i_bio(ASN1_ITEM_rptr(ROA), bio, NULL))) { - logmsg(rc, log_data_err, - "Rejected %s because could not decode ROA", uri); - mib_increment(rc, uri, roa_decode_error); + reject(rc, uri, roa_decode_error, "because could not decode ROA"); goto error; } if (roa->version) { - logmsg(rc, log_data_err, - "Rejected %s because ROA version should be defaulted zero, not %ld", - uri, ASN1_INTEGER_get(roa->version)); - mib_increment(rc, uri, roa_wrong_version); + reject(rc, uri, roa_wrong_version, + "because ROA version should be defaulted zero, not %ld", + ASN1_INTEGER_get(roa->version)); goto error; } @@ -2412,10 +2513,9 @@ static int check_roa_1(const rcynic_ctx_t *rc, for (i = 0; i < sk_ROAIPAddressFamily_num(roa->ipAddrBlocks); i++) { rf = sk_ROAIPAddressFamily_value(roa->ipAddrBlocks, i); if (!rf || !rf->addressFamily || rf->addressFamily->length < 2 || rf->addressFamily->length > 3) { - logmsg(rc, log_data_err, - "Rejected %s because ROA addressFamily length should be 2 or 3, not %lu", - uri, (unsigned long) rf->addressFamily->length); - mib_increment(rc, uri, malformed_roa_addressfamily); + reject(rc, uri, malformed_roa_addressfamily, + "because ROA addressFamily length should be 2 or 3, not %lu", + (unsigned long) rf->addressFamily->length); goto error; } afi = (rf->addressFamily->data[0] << 8) | (rf->addressFamily->data[1]); @@ -2426,8 +2526,8 @@ static int check_roa_1(const rcynic_ctx_t *rc, if (!ra || !extract_roa_prefix(addrbuf, &prefixlen, ra->IPAddress, afi) || !v3_addr_add_prefix(roa_resources, afi, safi, addrbuf, prefixlen)) { - logmsg(rc, log_data_err, - "Rejected %s because unable to copy resources from ROA into resource set", uri); + reject(rc, uri, roa_resources_malformed, + "because ROA resources appear malformed"); goto error; } } @@ -2447,8 +2547,8 @@ static int check_roa_1(const rcynic_ctx_t *rc, IPAddressFamily *f = sk_IPAddressFamily_value(roa_resources, i); if ((afi = v3_addr_get_afi(f)) == 0) { - logmsg(rc, log_data_err, - "Rejected %s because found bad AFI while extracting data from ROA", uri); + reject(rc, uri, roa_bad_afi, + "because found bad AFI while extracting data from ROA"); goto error; } @@ -2466,9 +2566,7 @@ static int check_roa_1(const rcynic_ctx_t *rc, if ((length = v3_addr_get_range(a, afi, a_min, a_max, ADDR_RAW_BUF_LEN)) == 0 || (length = v3_addr_get_range(b, afi, b_min, b_max, ADDR_RAW_BUF_LEN)) == 0) { - - logmsg(rc, log_data_err, - "Rejected %s because unable to extract addresses from ROA", uri); + reject(rc, uri, roa_resources_malformed, "because ROA resources appear malformed"); goto error; } @@ -2482,18 +2580,18 @@ static int check_roa_1(const rcynic_ctx_t *rc, } if (!v3_addr_canonize(roa_resources)) { - logmsg(rc, log_data_err, - "Rejected %s because unable to put ROA resources into canonical resource set form", uri); + reject(rc, uri, roa_resources_malformed, "because ROA resources appear malformed"); goto error; } if (!v3_addr_subset(roa_resources, ee_resources)) { - logmsg(rc, log_data_err, "Rejected %s because ROA resources are not a subset of its signing EE certificate's resources", uri); + reject(rc, uri, roa_not_nested, + "because ROA's resources are not a subset of its signing EE certificate's resources"); goto error; } if (!(crl = check_crl(rc, certinfo.crldp, sk_X509_value(certs, sk_X509_num(certs) - 1), NULL, 0))) { - logmsg(rc, log_data_err, "Rejected %s beacuse ROA EE certificate has bad CRL %s", uri, certinfo.crldp); + reject(rc, uri, roa_bad_crl, "because ROA EE certificate has bad CRL %s", certinfo.crldp); goto error; } @@ -2889,10 +2987,15 @@ int main(int argc, char *argv[]) goto done; } - if ((xmlfile != NULL) && - (rc.host_counters = sk_HOST_MIB_COUNTER_new(host_mib_counter_cmp)) == NULL) { - logmsg(&rc, log_sys_err, "Couldn't allocate host_counters stack"); - goto done; + if (xmlfile != NULL) { + if ((rc.host_counters = sk_HOST_MIB_COUNTER_new(host_mib_counter_cmp)) == NULL) { + logmsg(&rc, log_sys_err, "Couldn't allocate host_counters stack"); + goto done; + } + if ((rc.validation_status = sk_VALIDATION_STATUS_new_null()) == NULL) { + logmsg(&rc, log_sys_err, "Couldn't allocate validation_status stack"); + goto done; + } } if ((certs = sk_X509_new_null()) == NULL) { @@ -3082,7 +3185,7 @@ int main(int argc, char *argv[]) done: log_openssl_errors(&rc); - if (sk_HOST_MIB_COUNTER_num(rc.host_counters) > 0) { + if (xmlfile != NULL) { char tad[sizeof("2006-10-13T11:22:33Z") + 1]; char hostname[HOST_NAME_MAX]; @@ -3138,6 +3241,18 @@ int main(int argc, char *argv[]) ok &= fprintf(f, " </host>\n") != EOF; } + + for (i = 0; ok && i < sk_VALIDATION_STATUS_num(rc.validation_status); i++) { + VALIDATION_STATUS *v = sk_VALIDATION_STATUS_value(rc.validation_status, i); + assert(v); + + tad_tm = gmtime(&v->timestamp); + strftime(tad, sizeof(tad), "%Y-%m-%dT%H:%M:%SZ", tad_tm); + + ok &= fprintf(f, " <validation_status timestamp=\"%s\" status=\"%s\">%s</validation_status>\n", + tad, mib_counter_label[v->code], v->uri) != EOF; + } + if (ok) ok &= fprintf(f, "</rcynic-summary>\n") != EOF; @@ -3158,6 +3273,7 @@ int main(int argc, char *argv[]) sk_OPENSSL_STRING_pop_free(rc.backup_cache, OPENSSL_STRING_free); sk_OPENSSL_STRING_pop_free(rc.stale_cache, OPENSSL_STRING_free); sk_HOST_MIB_COUNTER_pop_free(rc.host_counters, HOST_MIB_COUNTER_free); + sk_VALIDATION_STATUS_pop_free(rc.validation_status, VALIDATION_STATUS_free); X509_STORE_free(rc.x509_store); NCONF_free(cfg_handle); CONF_modules_free(); diff --git a/rcynic/rcynic.xsl b/rcynic/rcynic.xsl index e99f3e38..c197a4cf 100644 --- a/rcynic/rcynic.xsl +++ b/rcynic/rcynic.xsl @@ -64,12 +64,16 @@ <xsl:if test="$refresh != 0"> <meta http-equiv="Refresh" content="{$refresh}"/> </xsl:if> + <style type="text/css"> + td { text-align: center; padding: 4px } + td.uri { text-align: left } + </style> </head> <body> <h1> <xsl:value-of select="$title"/> </h1> - <table rules="all" style="text-align: center"> + <table class="summary" rules="all"> <thead> <tr> <xsl:for-each select="rcynic-summary/labels/*"> @@ -119,6 +123,27 @@ </xsl:if> </tbody> </table> + <br/> + <h1>Validation Status</h1> + <table class="details" rules="all" > + <thead> + <tr> + <td class="timestamp"><b>Timestamp</b></td> + <td class="status"><b>Status</b></td> + <td class="uri"><b>URI</b></td> + </tr> + </thead> + <tbody> + <xsl:for-each select="rcynic-summary/validation_status"> + <xsl:variable name="status" select="@status"/> + <tr> + <td class="timestamp"><xsl:value-of select="@timestamp"/></td> + <td class="status"><xsl:value-of select="/rcynic-summary/labels/*[name() = $status] "/></td> + <td class="uri"><xsl:value-of select="."/></td> + </tr> + </xsl:for-each> + </tbody> + </table> </body> </html> </xsl:template> |