aboutsummaryrefslogtreecommitdiff
path: root/rcynic/rcynic.c
diff options
context:
space:
mode:
Diffstat (limited to 'rcynic/rcynic.c')
-rw-r--r--rcynic/rcynic.c146
1 files changed, 96 insertions, 50 deletions
diff --git a/rcynic/rcynic.c b/rcynic/rcynic.c
index ca082886..b16802ef 100644
--- a/rcynic/rcynic.c
+++ b/rcynic/rcynic.c
@@ -219,6 +219,8 @@ static const struct {
QQ(manifest_wrong_version, "Wrong manifest versions") \
QQ(roa_wrong_version, "Wrong ROA versions") \
QQ(trust_anchor_not_self_signed, "Trust anchor not self-signed") \
+ QQ(uri_too_long, "URI too long") \
+ QQ(malformed_crldp, "Malformed CRDLP extension") \
MIB_COUNTERS_FROM_OPENSSL
#define QV(x) QQ(mib_openssl_##x, 0)
@@ -752,38 +754,48 @@ static int is_rsync(const char *uri)
/**
* Convert an rsync URI to a filename, checking for evil character
- * sequences.
+ * sequences. NB: This routine can't call mib_increment(), because
+ * mib_increment() calls it, so errors detected here only go into
+ * the log, not the MIB.
*/
-static int uri_to_filename(const char *name,
+static int uri_to_filename(const rcynic_ctx_t *rc,
+ const char *uri,
char *buffer,
const size_t buflen,
const char *prefix)
{
+ const char *u;
size_t n;
buffer[0] = '\0';
- if (!is_rsync(name))
+ if (!is_rsync(uri)) {
+ logmsg(rc, log_telemetry, "%s is not an rsync URI, not converting to filename", uri);
return 0;
+ }
- name += SIZEOF_RSYNC;
- n = strlen(name);
+ u = uri + SIZEOF_RSYNC;
+ n = strlen(u);
- if (name[0] == '/' || name[0] == '.' || strstr(name, "//") ||
- strstr(name, "/../") || (n >= 3 && !strcmp(name + n - 3, "/..")))
+ if (u[0] == '/' || u[0] == '.' || strstr(u, "//") || strstr(u, "/../") ||
+ (n >= 3 && !strcmp(u + n - 3, "/.."))) {
+ logmsg(rc, log_data_err, "Dangerous URI %s, not converting to filename", uri);
return 0;
+ }
if (prefix)
n += strlen(prefix);
- if (n >= buflen)
+ if (n >= buflen) {
+ logmsg(rc, log_data_err, "URI %s too long, not converting to filename", uri);
return 0;
+ }
if (prefix) {
strcpy(buffer, prefix);
- strcat(buffer, name);
+ strcat(buffer, u);
} else {
- strcpy(buffer, name);
+ strcpy(buffer, u);
}
return 1;
@@ -826,7 +838,7 @@ static void mib_increment(const rcynic_ctx_t *rc,
memset(&hn, 0, sizeof(hn));
- if (!uri_to_filename(uri, hn.hostname, sizeof(hn.hostname), NULL)) {
+ if (!uri_to_filename(rc, uri, hn.hostname, sizeof(hn.hostname), NULL)) {
logmsg(rc, log_data_err, "Couldn't convert URI %s to hostname", uri);
return;
}
@@ -901,7 +913,7 @@ static int install_object(const rcynic_ctx_t *rc,
{
char target[FILENAME_MAX];
- if (!uri_to_filename(uri, target, sizeof(target), rc->authenticated)) {
+ if (!uri_to_filename(rc, uri, target, sizeof(target), rc->authenticated)) {
logmsg(rc, log_data_err, "Couldn't generate installation name for %s", uri);
return 0;
}
@@ -1122,7 +1134,7 @@ static int rsync(const rcynic_ctx_t *rc,
if (rc->rsync_program)
argv[0] = rc->rsync_program;
- if (!uri_to_filename(uri, path, sizeof(path), rc->unauthenticated)) {
+ if (!uri_to_filename(rc, uri, path, sizeof(path), rc->unauthenticated)) {
logmsg(rc, log_data_err, "Couldn't extract filename from URI: %s", uri);
return 0;
}
@@ -1450,40 +1462,66 @@ static CMS_ContentInfo *read_cms(const char *filename, unsigned char *hash, cons
/**
* Extract CRLDP data from a certificate.
*/
-static void extract_crldp_uri(const STACK_OF(DIST_POINT) *crldp,
- char *uri, const int urilen)
+static void extract_crldp_uri(const rcynic_ctx_t *rc,
+ const char *uri,
+ const STACK_OF(DIST_POINT) *crldp,
+ char *result,
+ const int resultlen)
{
DIST_POINT *d;
int i;
- if (!crldp || sk_DIST_POINT_num(crldp) != 1)
+ assert(crldp);
+
+ if (sk_DIST_POINT_num(crldp) != 1) {
+ logmsg(rc, log_data_err, "CRLDistributionPoints sequence length is %d (should be 1) for %s",
+ sk_DIST_POINT_num(crldp), uri);
+ mib_increment(rc, uri, malformed_crldp);
return;
+ }
d = sk_DIST_POINT_value(crldp, 0);
- if (d->reasons || d->CRLissuer || !d->distpoint || d->distpoint->type != 0)
+ if (d->reasons || d->CRLissuer || !d->distpoint || d->distpoint->type != 0) {
+ logmsg(rc, log_data_err, "CRLDP does not match RPKI certificate profile for %s", uri);
+ mib_increment(rc, uri, malformed_crldp);
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;
- if (is_rsync((char *) n->d.uniformResourceIdentifier->data) &&
- urilen > n->d.uniformResourceIdentifier->length) {
- strcpy(uri, (char *) n->d.uniformResourceIdentifier->data);
+ if (n->type != GEN_URI) {
+ logmsg(rc, log_data_err, "CRLDP contains non-URI GeneralName for %s", uri);
+ mib_increment(rc, uri, malformed_crldp);
return;
}
+ if (!is_rsync((char *) n->d.uniformResourceIdentifier->data)) {
+ logmsg(rc, log_verbose, "Skipping non-rsync URI %s for %s",
+ (char *) n->d.uniformResourceIdentifier->data, uri);
+ continue;
+ }
+ if (resultlen <= n->d.uniformResourceIdentifier->length) {
+ logmsg(rc, log_data_err, "Skipping improbably long URI %s for %s",
+ (char *) n->d.uniformResourceIdentifier->data, uri);
+ mib_increment(rc, uri, uri_too_long);
+ continue;
+ }
+ strcpy(result, (char *) n->d.uniformResourceIdentifier->data);
+ return;
}
}
/**
* Extract SIA or AIA data from a certificate.
*/
-static void extract_access_uri(const AUTHORITY_INFO_ACCESS *xia,
+static void extract_access_uri(const rcynic_ctx_t *rc,
+ const char *uri,
+ const AUTHORITY_INFO_ACCESS *xia,
const unsigned char *oid,
const int oidlen,
- char *uri, const int urilen)
+ char *result,
+ const int resultlen)
{
int i;
@@ -1495,19 +1533,28 @@ static void extract_access_uri(const AUTHORITY_INFO_ACCESS *xia,
assert(a != NULL);
if (a->location->type != GEN_URI)
return;
- if (!oid_cmp(a->method, oid, oidlen) &&
- is_rsync((char *) a->location->d.uniformResourceIdentifier->data) &&
- urilen > a->location->d.uniformResourceIdentifier->length) {
- strcpy(uri, (char *) a->location->d.uniformResourceIdentifier->data);
- return;
+ if (oid_cmp(a->method, oid, oidlen))
+ continue;
+ if (!is_rsync((char *) a->location->d.uniformResourceIdentifier->data)) {
+ logmsg(rc, log_verbose, "Skipping non-rsync URI %s for %s",
+ a->location->d.uniformResourceIdentifier->data, uri);
+ continue;
+ }
+ if (resultlen <= a->location->d.uniformResourceIdentifier->length) {
+ logmsg(rc, log_data_err, "Skipping improbably long URI %s for %s",
+ a->location->d.uniformResourceIdentifier->data, uri);
+ mib_increment(rc, uri, uri_too_long);
+ continue;
}
+ strcpy(result, (char *) a->location->d.uniformResourceIdentifier->data);
+ return;
}
}
/**
* Parse interesting stuff from a certificate.
*/
-static void parse_cert(X509 *x, certinfo_t *c, const char *uri)
+static void parse_cert(const rcynic_ctx_t *rc, X509 *x, certinfo_t *c, const char *uri)
{
STACK_OF(DIST_POINT) *crldp;
AUTHORITY_INFO_ACCESS *xia;
@@ -1521,19 +1568,18 @@ static void parse_cert(X509 *x, certinfo_t *c, const char *uri)
strcpy(c->uri, uri);
if ((xia = X509_get_ext_d2i(x, NID_info_access, NULL, NULL)) != NULL) {
- extract_access_uri(xia, id_ad_caIssuers, sizeof(id_ad_caIssuers), c->aia, sizeof(c->aia));
+ extract_access_uri(rc, uri, xia, id_ad_caIssuers, sizeof(id_ad_caIssuers), c->aia, sizeof(c->aia));
sk_ACCESS_DESCRIPTION_pop_free(xia, ACCESS_DESCRIPTION_free);
}
if ((xia = X509_get_ext_d2i(x, NID_sinfo_access, NULL, NULL)) != NULL) {
- extract_access_uri(xia, id_ad_caRepository, sizeof(id_ad_caRepository), c->sia, sizeof(c->sia));
- extract_access_uri(xia, id_ad_rpkiManifest, sizeof(id_ad_rpkiManifest), c->manifest, sizeof(c->manifest));
+ extract_access_uri(rc, uri, xia, id_ad_caRepository, sizeof(id_ad_caRepository), c->sia, sizeof(c->sia));
+ extract_access_uri(rc, uri, xia, id_ad_rpkiManifest, sizeof(id_ad_rpkiManifest), c->manifest, sizeof(c->manifest));
sk_ACCESS_DESCRIPTION_pop_free(xia, ACCESS_DESCRIPTION_free);
}
- if ((crldp = X509_get_ext_d2i(x, NID_crl_distribution_points,
- NULL, NULL)) != NULL) {
- extract_crldp_uri(crldp, c->crldp, sizeof(c->crldp));
+ if ((crldp = X509_get_ext_d2i(x, NID_crl_distribution_points, NULL, NULL)) != NULL) {
+ extract_crldp_uri(rc, uri, crldp, c->crldp, sizeof(c->crldp));
sk_DIST_POINT_pop_free(crldp, DIST_POINT_free);
}
}
@@ -1559,7 +1605,7 @@ static X509_CRL *check_crl_1(const rcynic_ctx_t *rc,
assert(uri && path && issuer && hashlen <= sizeof(hashbuf));
- if (!uri_to_filename(uri, path, pathlen, prefix))
+ if (!uri_to_filename(rc, uri, path, pathlen, prefix))
goto punt;
if (hash)
@@ -1602,7 +1648,7 @@ static X509_CRL *check_crl(const rcynic_ctx_t *rc,
char path[FILENAME_MAX];
X509_CRL *crl;
- if (uri_to_filename(uri, path, sizeof(path), rc->authenticated)) {
+ if (uri_to_filename(rc, uri, path, sizeof(path), rc->authenticated)) {
unsigned char hashbuf[EVP_MAX_MD_SIZE];
if (hash)
crl = read_crl(path, hashbuf, sizeof(hashbuf));
@@ -1816,7 +1862,7 @@ static X509 *check_cert_1(const rcynic_ctx_t *rc,
assert(uri && path && certs && issuer && subj);
- if (!uri_to_filename(uri, path, pathlen, prefix)) {
+ if (!uri_to_filename(rc, uri, path, pathlen, prefix)) {
logmsg(rc, log_data_err, "Can't convert URI %s to filename", uri);
return NULL;
}
@@ -1840,7 +1886,7 @@ static X509 *check_cert_1(const rcynic_ctx_t *rc,
goto punt;
}
- parse_cert(x, subj, uri);
+ parse_cert(rc, x, subj, uri);
if (subj->sia[0] && subj->sia[strlen(subj->sia) - 1] != '/') {
logmsg(rc, log_data_err, "Malformed SIA %s for %s", subj->sia, uri);
@@ -1909,7 +1955,7 @@ static X509 *check_cert(rcynic_ctx_t *rc,
* better data, just get out now.
*/
- if (uri_to_filename(uri, path, sizeof(path), rc->authenticated) &&
+ if (uri_to_filename(rc, uri, path, sizeof(path), rc->authenticated) &&
!access(path, R_OK)) {
if (backup || sk_STRING_find(rc->backup_cache, uri) < 0)
return NULL;
@@ -1968,7 +2014,7 @@ static Manifest *check_manifest_1(const rcynic_ctx_t *rc,
assert(rc && uri && path && prefix && certs && sk_X509_num(certs));
- if (!uri_to_filename(uri, path, pathlen, prefix) ||
+ if (!uri_to_filename(rc, uri, path, pathlen, prefix) ||
(cms = read_cms(path, NULL, 0)) == NULL)
goto done;
@@ -1996,7 +2042,7 @@ static Manifest *check_manifest_1(const rcynic_ctx_t *rc,
goto done;
}
- parse_cert(sk_X509_value(signers, 0), &certinfo, uri);
+ parse_cert(rc, sk_X509_value(signers, 0), &certinfo, uri);
if (!certinfo.crldp[0]) {
logmsg(rc, log_data_err, "No CRLDP in manifest %s", uri);
@@ -2117,7 +2163,7 @@ static Manifest *check_manifest(const rcynic_ctx_t *rc,
char path[FILENAME_MAX];
BIO *bio = NULL;
- if (uri_to_filename(uri, path, sizeof(path), rc->authenticated) &&
+ if (uri_to_filename(rc, uri, path, sizeof(path), rc->authenticated) &&
(cms = read_cms(path, NULL, 0)) != NULL &&
(bio = BIO_new(BIO_s_mem()))!= NULL &&
CMS_verify(cms, NULL, NULL, NULL, bio,
@@ -2223,7 +2269,7 @@ static int check_roa_1(const rcynic_ctx_t *rc,
assert(rc && uri && path && prefix && certs && sk_X509_num(certs));
- if (!uri_to_filename(uri, path, pathlen, prefix))
+ if (!uri_to_filename(rc, uri, path, pathlen, prefix))
goto error;
if (hash)
@@ -2265,7 +2311,7 @@ static int check_roa_1(const rcynic_ctx_t *rc,
goto error;
}
- parse_cert(sk_X509_value(signers, 0), &certinfo, uri);
+ 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, "Failure decoding ROA %s", uri);
@@ -2381,7 +2427,7 @@ static void check_roa(const rcynic_ctx_t *rc,
{
char path[FILENAME_MAX];
- if (uri_to_filename(uri, path, sizeof(path), rc->authenticated) &&
+ if (uri_to_filename(rc, uri, path, sizeof(path), rc->authenticated) &&
!access(path, F_OK))
return;
@@ -2831,8 +2877,8 @@ int main(int argc, char *argv[])
}
memcpy(uri, val->value, j);
uri[j] = '\0';
- if (!uri_to_filename(uri, path1, sizeof(path1), rc.unauthenticated) ||
- !uri_to_filename(uri, path2, sizeof(path2), rc.authenticated)) {
+ if (!uri_to_filename(&rc, uri, path1, sizeof(path1), rc.unauthenticated) ||
+ !uri_to_filename(&rc, uri, path2, sizeof(path2), rc.authenticated)) {
logmsg(&rc, log_usage_err, "Couldn't convert trust anchor URI %s to filename", uri);
goto done;
}
@@ -2872,7 +2918,7 @@ int main(int argc, char *argv[])
goto done;
}
- parse_cert(x, &ta_info, "");
+ parse_cert(&rc, x, &ta_info, "");
ta_info.ta = 1;
sk_X509_push(certs, x);