Skip to content

Commit

Permalink
Add an 'expires' column to CSV & JSON output
Browse files Browse the repository at this point in the history
The 'expires' value contains a reasonable earliest moment a VRP would expire,
in light of the currently available set of CAs and CRLs. The 'expires' value
can be used to avoid route selection based on stale data when generating VRP
sets, when faced with loss of communication between consumer and valdiator,
or validator and CA repository.

OK claudio@
  • Loading branch information
job committed May 6, 2021
1 parent 2ee2628 commit a66158d
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 29 deletions.
15 changes: 12 additions & 3 deletions regress/usr.sbin/rpki-client/Makefile.inc
@@ -1,4 +1,4 @@
# $OpenBSD: Makefile.inc,v 1.9 2021/04/01 06:47:18 claudio Exp $
# $OpenBSD: Makefile.inc,v 1.10 2021/05/06 17:03:57 job Exp $

.PATH: ${.CURDIR}/../../../../usr.sbin/rpki-client

Expand Down Expand Up @@ -40,14 +40,23 @@ mft_gen.c: mft.c
cat $> >> $@.tmp
mv -f $@.tmp $@

CLEANFILES += mft_gen.c mft_gen.c.tmp
# Provide missing prototypes for OpenSSL
roa_gen.c: roa.c
echo '#include <openssl/asn1.h>\n' > $@.tmp
echo 'int ASN1_time_parse(const char *, size_t, struct tm *, int);' \
>> $@.tmp
echo 'int ASN1_time_tm_cmp(struct tm *, struct tm *);' >> $@.tmp
cat $> >> $@.tmp
mv -f $@.tmp $@

CLEANFILES += mft_gen.c mft_gen.c.tmp roa_gen.c roa_gen.c.tmp

SRCS_test-mft+= test-mft.c mft_gen.c cms.c x509.c io.c log.c validate.c \
encoding.c dummy.c
run-regress-test-mft: test-mft
./test-mft -v ${.CURDIR}/../mft/*.mft

SRCS_test-roa= test-roa.c roa.c cms.c x509.c ip.c as.c io.c log.c encoding.c
SRCS_test-roa+= test-roa.c roa_gen.c cms.c x509.c ip.c as.c io.c log.c encoding.c
run-regress-test-roa: test-roa
./test-roa -v ${.CURDIR}/../roa/*.roa

Expand Down
1 change: 1 addition & 0 deletions regress/usr.sbin/rpki-client/openssl11/Makefile
Expand Up @@ -13,6 +13,7 @@ a_time_tm_gen.c: a_time_tm.c
CLEANFILES += a_time_tm_gen.c a_time_tm_gen.c.tmp

SRCS_test-mft = a_time_tm_gen.c o_time.c
SRCS_test-roa = a_time_tm_gen.c o_time.c
CFLAGS += -I${.CURDIR}/../../../../lib/libcrypto/

.PATH: ${.CURDIR}/..
Expand Down
10 changes: 9 additions & 1 deletion regress/usr.sbin/rpki-client/test-roa.c
@@ -1,4 +1,4 @@
/* $Id: test-roa.c,v 1.10 2021/03/29 15:47:34 claudio Exp $ */
/* $Id: test-roa.c,v 1.11 2021/05/06 17:03:57 job Exp $ */
/*
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
*
Expand Down Expand Up @@ -32,6 +32,14 @@

#include "test-common.c"

#ifndef ASN1error
void
ASN1error(int err)
{
ASN1err(0, err);
}
#endif

int verbose;

static void
Expand Down
4 changes: 3 additions & 1 deletion usr.sbin/rpki-client/extern.h
@@ -1,4 +1,4 @@
/* $OpenBSD: extern.h,v 1.63 2021/04/14 18:05:47 benno Exp $ */
/* $OpenBSD: extern.h,v 1.64 2021/05/06 17:03:57 job Exp $ */
/*
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
*
Expand Down Expand Up @@ -188,6 +188,7 @@ struct roa {
char *aki; /* AKI */
char *ski; /* SKI */
char *tal; /* basename of TAL for this cert */
time_t expires; /* do not use after */
};

/*
Expand All @@ -210,6 +211,7 @@ struct vrp {
char *tal; /* basename of TAL for this cert */
enum afi afi;
unsigned char maxlength;
time_t expires; /* transitive expiry moment */
};
/*
* Tree of VRP sorted by afi, addr, maxlength and asid
Expand Down
9 changes: 5 additions & 4 deletions usr.sbin/rpki-client/output-csv.c
@@ -1,4 +1,4 @@
/* $OpenBSD: output-csv.c,v 1.9 2021/04/19 17:04:35 deraadt Exp $ */
/* $OpenBSD: output-csv.c,v 1.10 2021/05/06 17:03:57 job Exp $ */
/*
* Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org>
*
Expand All @@ -24,15 +24,16 @@ output_csv(FILE *out, struct vrp_tree *vrps, struct stats *st)
{
struct vrp *v;

if (fprintf(out, "ASN,IP Prefix,Max Length,Trust Anchor\n") < 0)
if (fprintf(out, "ASN,IP Prefix,Max Length,Trust Anchor,Expires\n") < 0)
return -1;

RB_FOREACH(v, vrp_tree, vrps) {
char buf[64];

ip_addr_print(&v->addr, v->afi, buf, sizeof(buf));
if (fprintf(out, "AS%u,%s,%u,%s\n", v->asid, buf, v->maxlength,
v->tal) < 0)

if (fprintf(out, "AS%u,%s,%u,%s,%lld\n", v->asid, buf,
v->maxlength, v->tal, (long long)v->expires) < 0)
return -1;
}
return 0;
Expand Down
7 changes: 4 additions & 3 deletions usr.sbin/rpki-client/output-json.c
@@ -1,4 +1,4 @@
/* $OpenBSD: output-json.c,v 1.16 2021/05/05 17:25:44 job Exp $ */
/* $OpenBSD: output-json.c,v 1.17 2021/05/06 17:03:57 job Exp $ */
/*
* Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org>
*
Expand Down Expand Up @@ -101,8 +101,9 @@ output_json(FILE *out, struct vrp_tree *vrps, struct stats *st)
ip_addr_print(&v->addr, v->afi, buf, sizeof(buf));

if (fprintf(out, "\t\t{ \"asn\": %u, \"prefix\": \"%s\", "
"\"maxLength\": %u, \"ta\": \"%s\" }",
v->asid, buf, v->maxlength, v->tal) < 0)
"\"maxLength\": %u, \"ta\": \"%s\", \"expires\": %lld }",
v->asid, buf, v->maxlength, v->tal, (long long)v->expires)
< 0)
return -1;
}

Expand Down
71 changes: 66 additions & 5 deletions usr.sbin/rpki-client/parser.c
@@ -1,4 +1,4 @@
/* $OpenBSD: parser.c,v 1.7 2021/04/01 08:29:10 claudio Exp $ */
/* $OpenBSD: parser.c,v 1.8 2021/05/06 17:03:57 job Exp $ */
/*
* Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org>
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
Expand Down Expand Up @@ -52,10 +52,13 @@ proc_parser_roa(struct entity *entp,
{
struct roa *roa;
X509 *x509;
int c;
int c, i;
struct auth *a;
STACK_OF(X509) *chain;
STACK_OF(X509_CRL) *crls;
const ASN1_TIME *at;
struct tm expires_tm;
time_t expires;

if ((roa = roa_parse(&x509, entp->file)) == NULL)
return NULL;
Expand Down Expand Up @@ -85,9 +88,62 @@ proc_parser_roa(struct entity *entp,
return NULL;
}
X509_STORE_CTX_cleanup(ctx);
sk_X509_free(chain);
sk_X509_CRL_free(crls);
X509_free(x509);

/*
* Scan the stack of CRLs to figure out the soonest transitive
* expiry moment
*/
for (i = 0; i < sk_X509_CRL_num(crls); i++) {
X509_CRL *ci = sk_X509_CRL_value(crls, i);
if (ci->crl == NULL) {
err(1, "sk_X509_value failed");
goto out;
}
at = X509_CRL_get0_nextUpdate(ci);
if (at == NULL) {
err(1, "X509_CRL_get0_nextUpdate failed");
goto out;
}
if (ASN1_time_parse(at->data, at->length, &expires_tm,
V_ASN1_UTCTIME) != V_ASN1_UTCTIME) {
err(1, "ASN1_time_parse failed");
goto out;
}
if ((expires = mktime(&expires_tm)) == -1) {
err(1, "mktime failed");
goto out;
}
if (roa->expires > expires)
roa->expires = expires;
}

/*
* Scan the stack of CAs to figure out the soonest transitive
* expiry moment
*/
for (i = 0; i < sk_X509_num(chain); i++) {
X509 *xi = sk_X509_value(chain, i);
if (xi->cert_info == NULL) {
err(1, "sk_X509_value failed");
goto out;
}
at = X509_get0_notAfter(xi);
if (at == NULL) {
err(1, "X509_get0_notafter failed");
goto out;
}
if (ASN1_time_parse(at->data, at->length, &expires_tm,
V_ASN1_UTCTIME) != V_ASN1_UTCTIME) {
err(1, "ASN1_time_parse failed");
goto out;
}
if ((expires = mktime(&expires_tm)) == -1) {
err(1, "mktime failed");
goto out;
}
if (roa->expires > expires)
roa->expires = expires;
}

/*
* If the ROA isn't valid, we accept it anyway and depend upon
Expand All @@ -97,6 +153,11 @@ proc_parser_roa(struct entity *entp,
if (valid_roa(entp->file, auths, roa))
roa->valid = 1;

out:
sk_X509_free(chain);
sk_X509_CRL_free(crls);
X509_free(x509);

return roa;
}

Expand Down
49 changes: 43 additions & 6 deletions usr.sbin/rpki-client/roa.c
@@ -1,4 +1,4 @@
/* $OpenBSD: roa.c,v 1.17 2021/03/29 06:50:44 tb Exp $ */
/* $OpenBSD: roa.c,v 1.18 2021/05/06 17:03:57 job Exp $ */
/*
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
*
Expand Down Expand Up @@ -335,6 +335,9 @@ roa_parse(X509 **x509, const char *fn)
size_t cmsz;
unsigned char *cms;
int rc = 0;
const ASN1_TIME *at;
struct tm expires_tm;
time_t expires;

memset(&p, 0, sizeof(struct parse));
p.fn = fn;
Expand All @@ -358,6 +361,21 @@ roa_parse(X509 **x509, const char *fn)
goto out;
}

at = X509_get0_notAfter(*x509);
if (at == NULL) {
warnx("%s: X509_get0_notAfter failed", fn);
goto out;
}
if (ASN1_time_parse(at->data, at->length, &expires_tm, 0) == -1) {
warnx("%s: ASN1_time_parse failed", fn);
goto out;
}
if ((expires = mktime(&expires_tm)) == -1) {
err(1, "mktime failed");
goto out;
}
p.res->expires = expires;

if (!roa_parse_econtent(cms, cmsz, &p))
goto out;

Expand Down Expand Up @@ -404,6 +422,7 @@ roa_buffer(struct ibuf *b, const struct roa *p)
io_simple_buffer(b, &p->valid, sizeof(int));
io_simple_buffer(b, &p->asid, sizeof(uint32_t));
io_simple_buffer(b, &p->ipsz, sizeof(size_t));
io_simple_buffer(b, &p->expires, sizeof(time_t));

for (i = 0; i < p->ipsz; i++) {
io_simple_buffer(b, &p->ips[i].afi, sizeof(enum afi));
Expand Down Expand Up @@ -436,6 +455,7 @@ roa_read(int fd)
io_simple_read(fd, &p->valid, sizeof(int));
io_simple_read(fd, &p->asid, sizeof(uint32_t));
io_simple_read(fd, &p->ipsz, sizeof(size_t));
io_simple_read(fd, &p->expires, sizeof(time_t));

if ((p->ips = calloc(p->ipsz, sizeof(struct roa_ip))) == NULL)
err(1, NULL);
Expand Down Expand Up @@ -466,8 +486,8 @@ void
roa_insert_vrps(struct vrp_tree *tree, struct roa *roa, size_t *vrps,
size_t *uniqs)
{
struct vrp *v;
size_t i;
struct vrp *v, *found;
size_t i;

for (i = 0; i < roa->ipsz; i++) {
if ((v = malloc(sizeof(*v))) == NULL)
Expand All @@ -478,10 +498,27 @@ roa_insert_vrps(struct vrp_tree *tree, struct roa *roa, size_t *vrps,
v->asid = roa->asid;
if ((v->tal = strdup(roa->tal)) == NULL)
err(1, NULL);
if (RB_INSERT(vrp_tree, tree, v) == NULL)
(*uniqs)++;
else /* already exists */
v->expires = roa->expires;

/*
* Check if a similar VRP already exists in the tree.
* If the found VRP expires sooner, update it to this
* ROAs later expiry moment.
*/
if ((found = RB_INSERT(vrp_tree, tree, v)) != NULL) {
/* already exists */
if (found->expires < v->expires) {
/* update found with preferred data */
found->expires = roa->expires;
free(found->tal);
found->tal = v->tal;
v->tal = NULL;
}
free(v->tal);
free(v);
} else
(*uniqs)++;

(*vrps)++;
}
}
Expand Down
18 changes: 12 additions & 6 deletions usr.sbin/rpki-client/rpki-client.8
@@ -1,4 +1,4 @@
.\" $OpenBSD: rpki-client.8,v 1.44 2021/05/05 17:24:00 job Exp $
.\" $OpenBSD: rpki-client.8,v 1.45 2021/05/06 17:03:57 job Exp $
.\"
.\" Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
.\"
Expand All @@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: May 5 2021 $
.Dd $Mdocdate: May 6 2021 $
.Dt RPKI-CLIENT 8
.Os
.Sh NAME
Expand Down Expand Up @@ -66,9 +66,13 @@ with multiple interfaces.
.It Fl c
Create output in the file
.Pa csv
in the output directory as comma-separated values of the prefix in slash notation,
the maximum prefix length, the autonomous system number, and an abbreviation
for the trust anchor the entry is derived from.
in the output directory as comma-separated values of the
.Em Autonomous System ,
the prefix in slash notation, the maximum prefix length, an abbreviation for
the
.Em Trust Anchor
the entry is derived from, and the moment the VRP will expire derived from
the chain of X.509 certificates and CRLs in seconds since the Epoch, UTC.
.It Fl d Ar cachedir
The directory where
.Nm
Expand All @@ -90,7 +94,9 @@ flags and connect with rsync-protocol locations.
Create output in the file
.Pa json
in the output directory as JSON object.
This format is similar to that produced by other RPKI validators.
See
.Fl c
for a description of the fields.
.It Fl n
Offline mode.
Validate the contents of
Expand Down

0 comments on commit a66158d

Please sign in to comment.