-# $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
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
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}/..
-/* $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>
*
#include "test-common.c"
+#ifndef ASN1error
+void
+ASN1error(int err)
+{
+ ASN1err(0, err);
+}
+#endif
+
int verbose;
static void
-/* $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>
*
char *aki; /* AKI */
char *ski; /* SKI */
char *tal; /* basename of TAL for this cert */
+ time_t expires; /* do not use after */
};
/*
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
-/* $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>
*
{
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;
-/* $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>
*
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;
}
-/* $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>
{
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;
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
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;
}
-/* $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>
*
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;
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;
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));
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);
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)
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)++;
}
}
-.\" $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>
.\"
.\" 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
.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
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