aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rcynic/rcynic.c145
1 files changed, 101 insertions, 44 deletions
diff --git a/rcynic/rcynic.c b/rcynic/rcynic.c
index 0d6e0e67..8c5015dd 100644
--- a/rcynic/rcynic.c
+++ b/rcynic/rcynic.c
@@ -260,6 +260,7 @@ static const struct {
QB(manifest_missing, "Manifest pointer missing") \
QB(manifest_mismatch, "Manifest doesn't match SIA") \
QB(trust_anchor_with_crldp, "Trust anchor can't have CRLDP") \
+ QW(object_not_in_manifest, "Object not in manifest") \
MIB_COUNTERS_FROM_OPENSSL
#define QV(x) QB(mib_openssl_##x, 0)
@@ -267,7 +268,7 @@ static const struct {
static const char
mib_counter_kind_good[] = "good",
mib_counter_kind_warn[] = "warn",
- mib_counter_kind_bad[] = "bad";
+ mib_counter_kind_bad[] = "bad";
#define QG(x,y) mib_counter_kind_good ,
#define QW(x,y) mib_counter_kind_warn ,
@@ -342,7 +343,7 @@ typedef struct rcynic_ctx {
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;
+ int allow_non_self_signed_trust_anchor, allow_object_not_in_manifest;
log_level_t log_level;
X509_STORE *x509_store;
} rcynic_ctx_t;
@@ -411,7 +412,19 @@ static const char rpki_policy_oid[] = "1.3.6.1.5.5.7.14.2";
*/
static void OPENSSL_STRING_free(OPENSSL_STRING s)
{
- free(s);
+ if (s)
+ free(s);
+}
+
+/**
+ * Wrapper around an idiom we use with OPENSSL_STRING stacks. There's
+ * a bug in the current sk_OPENSSL_STRING_delete() macro that casts
+ * the return value to the wrong type, so we cast it to something
+ * innocuous here and avoid using that macro elsewhere.
+ */
+static void sk_OPENSSL_STRING_remove(STACK_OF(OPENSSL_STRING) *sk, const char *str)
+{
+ OPENSSL_STRING_free((void *) sk_OPENSSL_STRING_delete(sk, sk_OPENSSL_STRING_find(sk, str)));
}
/**
@@ -441,7 +454,8 @@ static VALIDATION_STATUS *VALIDATION_STATUS_new(void)
*/
static void HOST_MIB_COUNTER_free(HOST_MIB_COUNTER *h)
{
- free(h);
+ if (h)
+ free(h);
}
/**
@@ -449,7 +463,8 @@ static void HOST_MIB_COUNTER_free(HOST_MIB_COUNTER *h)
*/
static void VALIDATION_STATUS_free(VALIDATION_STATUS *v)
{
- free(v);
+ if (v)
+ free(v);
}
/*
@@ -854,7 +869,7 @@ static int mkdir_maybe(const rcynic_ctx_t *rc, const char *name)
*/
static int sk_OPENSSL_STRING_push_strdup(STACK_OF(OPENSSL_STRING) *sk, const char *str)
{
- char *s = strdup(str);
+ OPENSSL_STRING s = strdup(str);
if (s && sk_OPENSSL_STRING_push(sk, s))
return 1;
@@ -1139,36 +1154,6 @@ static int startswith(const char *str, const char *prefix)
return len_str >= len_prefix && !strncmp(str, prefix, len_prefix);
}
-/**
- * Iterator over URIs in our copy of a SIA collection.
- * *iterator should be zero when first called.
- */
-static FileAndHash *next_uri(const rcynic_ctx_t *rc,
- const char *base_uri,
- const char *prefix,
- char *uri,
- const size_t urilen,
- const Manifest *manifest,
- int *iterator)
-{
- FileAndHash *fah = NULL;
-
- assert(base_uri && prefix && uri && manifest && iterator);
-
- while ((fah = sk_FileAndHash_value(manifest->fileList, *iterator)) != NULL) {
- ++*iterator;
- if (strlen(base_uri) + strlen((char *) fah->file->data) >= urilen) {
- logmsg(rc, log_data_err, "URI %s%s too long, skipping", base_uri, fah->file->data);
- continue;
- }
- strcpy(uri, base_uri);
- strcat(uri, (char *) fah->file->data);
- return fah;
- }
-
- *iterator = 0;
- return NULL;
-}
/**
* Set a directory name, making sure it has the trailing slash we
@@ -2265,7 +2250,7 @@ static X509 *check_cert(rcynic_ctx_t *rc,
mib_increment(rc, uri,
(backup ? backup_cert_accepted : current_cert_accepted));
if (!backup)
- (void) sk_OPENSSL_STRING_delete(rc->backup_cache, sk_OPENSSL_STRING_find(rc->backup_cache, uri));
+ sk_OPENSSL_STRING_remove(rc->backup_cache, uri);
else if (!sk_OPENSSL_STRING_push_strdup(rc->backup_cache, uri))
logmsg(rc, log_sys_err, "Couldn't cache URI %s, blundering onward", uri);
@@ -3048,6 +3033,79 @@ static void walk_cert_2(rcynic_ctx_t *rc,
* daisy chain recursion is to avoid having to duplicate the stack
* manipulation and error handling.
*/
+static void walk_cert_3(rcynic_ctx_t *rc,
+ STACK_OF(X509) *certs,
+ const certinfo_t *parent,
+ const char *prefix,
+ const int backup,
+ Manifest *manifest)
+{
+ char uri[URI_MAX], path[FILENAME_MAX];
+ FileAndHash *fah;
+ STACK_OF(OPENSSL_STRING) *stray_ducks = NULL;
+ DIR *dir = NULL;
+ struct dirent *d;
+ int i;
+
+ /*
+ * Pull all non-directory filenames from the publication point directory.
+ */
+ if ((stray_ducks = sk_OPENSSL_STRING_new(uri_cmp)) == NULL)
+ logmsg(rc, log_sys_err, "Couldn't allocate stray_ducks stack");
+ else if (!uri_to_filename(rc, parent->sia, path, sizeof(path), prefix) || (dir = opendir(path)) == NULL)
+ logmsg(rc, log_data_err, "Couldn't list directory %s, skipping check for out-of-manifest data", path);
+ else
+ while ((d = readdir(dir)) != NULL)
+ if (d->d_type != DT_DIR && !sk_OPENSSL_STRING_push_strdup(stray_ducks, d->d_name))
+ logmsg(rc, log_sys_err, "Couldn't strdup() string \"%s\", blundering onwards", d->d_name);
+
+ if (dir != NULL)
+ closedir(dir);
+
+ /*
+ * Loop over manifest, checking everything it lists. Remove any
+ * filenames we find in the manifest from our list of objects found
+ * in the publication point directory, so we don't check stuff twice.
+ */
+ for (i = 0; (fah = sk_FileAndHash_value(manifest->fileList, i)) != NULL; i++) {
+ sk_OPENSSL_STRING_remove(stray_ducks, (char *) fah->file->data);
+ if (strlen(parent->sia) + strlen((char *) fah->file->data) >= sizeof(uri)) {
+ logmsg(rc, log_data_err, "URI %s%s too long, skipping", parent->sia, fah->file->data);
+ } else {
+ strcpy(uri, parent->sia);
+ strcat(uri, (char *) fah->file->data);
+ walk_cert_2(rc, uri, certs, parent, prefix, backup, fah->hash->data, fah->hash->length);
+ }
+ }
+
+ /*
+ * Whine about and maybe check any object that was in the directory
+ * but not in the manifest, except for the manifest itself.
+ */
+ for (i = 0; i < sk_OPENSSL_STRING_num(stray_ducks); i++) {
+ char *s = sk_OPENSSL_STRING_value(stray_ducks, i);
+ if (strlen(parent->sia) + strlen(s) >= sizeof(uri)) {
+ logmsg(rc, log_data_err, "URI %s%s too long, skipping", parent->sia, s);
+ continue;
+ }
+ strcpy(uri, parent->sia);
+ strcat(uri, s);
+ if (!strcmp(uri, parent->manifest))
+ continue;
+ logmsg(rc, log_telemetry, "Object %s present in publication directory but not in manifest", uri);
+ mib_increment(rc, uri, object_not_in_manifest);
+ if (rc->allow_object_not_in_manifest)
+ walk_cert_2(rc, uri, certs, parent, prefix, backup, NULL, 0);
+ }
+
+ sk_OPENSSL_STRING_pop_free(stray_ducks, OPENSSL_STRING_free);
+}
+
+/**
+ * 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)
@@ -3056,10 +3114,7 @@ static void walk_cert(rcynic_ctx_t *rc,
if (parent->sia[0] && parent->ca) {
int n_cert = sk_X509_num(certs);
- char uri[URI_MAX];
- int iterator = 0;
Manifest *manifest = NULL;
- FileAndHash *fah;
rc->indent++;
@@ -3076,13 +3131,11 @@ static void walk_cert(rcynic_ctx_t *rc,
} else {
logmsg(rc, log_debug, "Walking unauthenticated store");
- while ((fah = next_uri(rc, parent->sia, rc->unauthenticated, uri, sizeof(uri), manifest, &iterator)) != NULL)
- walk_cert_2(rc, uri, certs, parent, rc->unauthenticated, 0, fah->hash->data, fah->hash->length);
+ walk_cert_3(rc, certs, parent, rc->unauthenticated, 0, manifest);
logmsg(rc, log_debug, "Done walking unauthenticated store");
logmsg(rc, log_debug, "Walking old authenticated store");
- while ((fah = next_uri(rc, parent->sia, rc->old_authenticated, uri, sizeof(uri), manifest, &iterator)) != NULL)
- walk_cert_2(rc, uri, certs, parent, rc->old_authenticated, 1, fah->hash->data, fah->hash->length);
+ walk_cert_3(rc, certs, parent, rc->old_authenticated, 1, manifest);
logmsg(rc, log_debug, "Done walking old authenticated store");
Manifest_free(manifest);
@@ -3263,6 +3316,10 @@ int main(int argc, char *argv[])
!configure_boolean(&rc, &rc.require_crl_in_manifest, val->value))
goto done;
+ else if (!name_cmp(val->name, "allow-object-not-in-manifest") &&
+ !configure_boolean(&rc, &rc.allow_object_not_in_manifest, val->value))
+ goto done;
+
else if (!name_cmp(val->name, "use-links") &&
!configure_boolean(&rc, &rc.use_links, val->value))
goto done;