summaryrefslogtreecommitdiff
path: root/usr.sbin/rpki-client
diff options
context:
space:
mode:
authorJob Snijders <job@cvs.openbsd.org>2022-11-02 12:43:03 +0000
committerJob Snijders <job@cvs.openbsd.org>2022-11-02 12:43:03 +0000
commite17f607fbc86892fab7be337c821a84027f605fb (patch)
treeedb189f0266296419ebca13718f32624812cc990 /usr.sbin/rpki-client
parentb931f2967cb6eae6a9015527fc4eb5053f70d6b2 (diff)
Add support for draft-ietf-sidrops-signed-tal-12
Add support validation of Signed Objects containing Trust Anchor Keys (TAKs - aka 'Signed TALs'). Signed TALs provide a mechanism for RIRs to distribute and sign the next Trust Anchor with the current Trust Anchor. This might be an improvement over visiting RIR websites and copy+pasting TAL data by hand. OK tb@
Diffstat (limited to 'usr.sbin/rpki-client')
-rw-r--r--usr.sbin/rpki-client/Makefile4
-rw-r--r--usr.sbin/rpki-client/extern.h38
-rw-r--r--usr.sbin/rpki-client/filemode.c12
-rw-r--r--usr.sbin/rpki-client/main.c6
-rw-r--r--usr.sbin/rpki-client/mft.c5
-rw-r--r--usr.sbin/rpki-client/output-json.c5
-rw-r--r--usr.sbin/rpki-client/parser.c43
-rw-r--r--usr.sbin/rpki-client/print.c94
-rw-r--r--usr.sbin/rpki-client/rpki-client.86
-rw-r--r--usr.sbin/rpki-client/rsync.c3
-rw-r--r--usr.sbin/rpki-client/tak.c335
-rw-r--r--usr.sbin/rpki-client/x509.c7
12 files changed, 544 insertions, 14 deletions
diff --git a/usr.sbin/rpki-client/Makefile b/usr.sbin/rpki-client/Makefile
index a7198aa4874..37393fbb3f4 100644
--- a/usr.sbin/rpki-client/Makefile
+++ b/usr.sbin/rpki-client/Makefile
@@ -1,11 +1,11 @@
-# $OpenBSD: Makefile,v 1.26 2022/08/30 18:56:49 job Exp $
+# $OpenBSD: Makefile,v 1.27 2022/11/02 12:43:02 job Exp $
PROG= rpki-client
SRCS= as.c aspa.c cert.c cms.c crl.c encoding.c filemode.c gbr.c http.c io.c \
ip.c log.c main.c mft.c mkdir.c output.c output-bgpd.c output-bird.c \
output-csv.c output-json.c parser.c print.c repo.c roa.c rrdp.c \
rrdp_delta.c rrdp_notification.c rrdp_snapshot.c rrdp_util.c \
- rsc.c rsync.c tal.c validate.c x509.c
+ rsc.c rsync.c tak.c tal.c validate.c x509.c
MAN= rpki-client.8
LDADD+= -lexpat -ltls -lssl -lcrypto -lutil
diff --git a/usr.sbin/rpki-client/extern.h b/usr.sbin/rpki-client/extern.h
index 77eee1dc97b..96d0025596d 100644
--- a/usr.sbin/rpki-client/extern.h
+++ b/usr.sbin/rpki-client/extern.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: extern.h,v 1.156 2022/09/03 21:24:02 job Exp $ */
+/* $OpenBSD: extern.h,v 1.157 2022/11/02 12:43:02 job Exp $ */
/*
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
*
@@ -183,6 +183,7 @@ enum rtype {
RTYPE_FILE,
RTYPE_RSC,
RTYPE_ASPA,
+ RTYPE_TAK,
};
enum location {
@@ -275,6 +276,33 @@ struct rsc {
};
/*
+ * Datastructure representing the TAKey sequence inside TAKs.
+ */
+struct takey {
+ char **comments; /* Comments */
+ size_t commentsz; /* number of Comments */
+ char **uris; /* CertificateURI */
+ size_t urisz; /* number of CertificateURIs */
+ unsigned char *pubkey; /* DER encoded SubjectPublicKeyInfo */
+ size_t pubkeysz;
+ char *ski; /* hex encoded SubjectKeyIdentifier of pubkey */
+};
+
+/*
+ * A Signed TAL (TAK) draft-ietf-sidrops-signed-tal-12
+ */
+struct tak {
+ int talid; /* TAK covered by what TAL */
+ struct takey *current;
+ struct takey *predecessor;
+ struct takey *successor;
+ char *aia; /* AIA */
+ char *aki; /* AKI */
+ char *ski; /* SKI */
+ time_t expires; /* Not After of the TAK EE */
+};
+
+/*
* A single Ghostbuster record
*/
struct gbr {
@@ -474,6 +502,7 @@ struct stats {
size_t rrdp_fails; /* failed rrdp repositories */
size_t crls; /* revocation lists */
size_t gbrs; /* ghostbuster records */
+ size_t taks; /* signed TAL objects */
size_t aspas; /* ASPA objects */
size_t aspas_fail; /* ASPA objects failing syntactic parse */
size_t aspas_invalid; /* ASPAs with invalid customerASID */
@@ -544,6 +573,12 @@ void rsc_free(struct rsc *);
struct rsc *rsc_parse(X509 **, const char *, const unsigned char *,
size_t);
+void takey_free(struct takey *);
+void tak_free(struct tak *);
+struct tak *tak_parse(X509 **, const char *, const unsigned char *,
+ size_t);
+struct tak *tak_read(struct ibuf *);
+
void aspa_buffer(struct ibuf *, const struct aspa *);
void aspa_free(struct aspa *);
void aspa_insert_vaps(struct vap_tree *, struct aspa *, size_t *,
@@ -726,6 +761,7 @@ void roa_print(const X509 *, const struct roa *);
void gbr_print(const X509 *, const struct gbr *);
void rsc_print(const X509 *, const struct rsc *);
void aspa_print(const X509 *, const struct aspa *);
+void tak_print(const X509 *, const struct tak *);
/* Output! */
diff --git a/usr.sbin/rpki-client/filemode.c b/usr.sbin/rpki-client/filemode.c
index d26ea22e075..2982f2d85ba 100644
--- a/usr.sbin/rpki-client/filemode.c
+++ b/usr.sbin/rpki-client/filemode.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: filemode.c,v 1.14 2022/09/06 11:16:51 job Exp $ */
+/* $OpenBSD: filemode.c,v 1.15 2022/11/02 12:43:02 job Exp $ */
/*
* Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org>
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -269,6 +269,7 @@ proc_parser_file(char *file, unsigned char *buf, size_t len)
struct tal *tal = NULL;
struct rsc *rsc = NULL;
struct aspa *aspa = NULL;
+ struct tak *tak = NULL;
char *aia = NULL, *aki = NULL;
char filehash[SHA256_DIGEST_LENGTH];
char *hash;
@@ -376,6 +377,14 @@ proc_parser_file(char *file, unsigned char *buf, size_t len)
aia = aspa->aia;
aki = aspa->aki;
break;
+ case RTYPE_TAK:
+ tak = tak_parse(&x509, file, buf, len);
+ if (tak == NULL)
+ break;
+ tak_print(x509, tak);
+ aia = tak->aia;
+ aki = tak->aki;
+ break;
default:
printf("%s: unsupported file type\n", file);
break;
@@ -469,6 +478,7 @@ proc_parser_file(char *file, unsigned char *buf, size_t len)
tal_free(tal);
rsc_free(rsc);
aspa_free(aspa);
+ tak_free(tak);
}
/*
diff --git a/usr.sbin/rpki-client/main.c b/usr.sbin/rpki-client/main.c
index 9d2cb3482fa..51f42114094 100644
--- a/usr.sbin/rpki-client/main.c
+++ b/usr.sbin/rpki-client/main.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: main.c,v 1.219 2022/09/03 09:22:25 claudio Exp $ */
+/* $OpenBSD: main.c,v 1.220 2022/11/02 12:43:02 job Exp $ */
/*
* Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -612,6 +612,9 @@ entity_process(struct ibuf *b, struct stats *st, struct vrp_tree *tree,
st->aspas_invalid++;
aspa_free(aspa);
break;
+ case RTYPE_TAK:
+ st->taks++;
+ break;
default:
errx(1, "unknown entity type %d", type);
}
@@ -1311,6 +1314,7 @@ main(int argc, char *argv[])
stats.mfts, stats.mfts_fail, stats.mfts_stale);
printf("Certificate revocation lists: %zu\n", stats.crls);
printf("Ghostbuster records: %zu\n", stats.gbrs);
+ printf("Trust Anchor Keys: %zu\n", stats.taks);
printf("Repositories: %zu\n", stats.repos);
printf("Cleanup: removed %zu files, %zu directories, %zu superfluous\n",
stats.del_files, stats.del_dirs, stats.extra_files);
diff --git a/usr.sbin/rpki-client/mft.c b/usr.sbin/rpki-client/mft.c
index 69f14bb30e7..7fde67f823f 100644
--- a/usr.sbin/rpki-client/mft.c
+++ b/usr.sbin/rpki-client/mft.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mft.c,v 1.75 2022/10/13 04:43:32 job Exp $ */
+/* $OpenBSD: mft.c,v 1.76 2022/11/02 12:43:02 job Exp $ */
/*
* Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -168,6 +168,8 @@ rtype_from_file_extension(const char *fn)
return RTYPE_RSC;
if (strcasecmp(fn + sz - 4, ".asa") == 0)
return RTYPE_ASPA;
+ if (strcasecmp(fn + sz - 4, ".tak") == 0)
+ return RTYPE_TAK;
return RTYPE_INVALID;
}
@@ -208,6 +210,7 @@ rtype_from_mftfile(const char *fn)
case RTYPE_GBR:
case RTYPE_ROA:
case RTYPE_ASPA:
+ case RTYPE_TAK:
return type;
default:
return RTYPE_INVALID;
diff --git a/usr.sbin/rpki-client/output-json.c b/usr.sbin/rpki-client/output-json.c
index 69f84136357..51b0c839e9a 100644
--- a/usr.sbin/rpki-client/output-json.c
+++ b/usr.sbin/rpki-client/output-json.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: output-json.c,v 1.28 2022/08/30 23:40:37 tb Exp $ */
+/* $OpenBSD: output-json.c,v 1.29 2022/11/02 12:43:02 job Exp $ */
/*
* Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org>
*
@@ -52,6 +52,7 @@ outputheader_json(FILE *out, struct stats *st)
"\t\t\"bgpsec_pubkeys\": %zu,\n"
"\t\t\"certificates\": %zu,\n"
"\t\t\"invalidcertificates\": %zu,\n"
+ "\t\t\"taks\": %zu,\n"
"\t\t\"tals\": %zu,\n"
"\t\t\"invalidtals\": %zu,\n"
"\t\t\"talfiles\": [\n",
@@ -59,7 +60,7 @@ outputheader_json(FILE *out, struct stats *st)
(long long)st->user_time.tv_sec, (long long)st->system_time.tv_sec,
st->roas, st->roas_fail, st->roas_invalid,
st->aspas, st->aspas_fail, st->aspas_invalid,
- st->brks, st->certs, st->certs_fail,
+ st->brks, st->certs, st->certs_fail, st->taks,
st->tals, talsz - st->tals) < 0)
return -1;
diff --git a/usr.sbin/rpki-client/parser.c b/usr.sbin/rpki-client/parser.c
index a6139669251..6f84e18ce51 100644
--- a/usr.sbin/rpki-client/parser.c
+++ b/usr.sbin/rpki-client/parser.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: parser.c,v 1.77 2022/09/03 21:24:02 job Exp $ */
+/* $OpenBSD: parser.c,v 1.78 2022/11/02 12:43:02 job Exp $ */
/*
* Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org>
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -521,6 +521,42 @@ proc_parser_aspa(char *file, const unsigned char *der, size_t len)
}
/*
+ * Parse a TAK object.
+ */
+static struct tak *
+proc_parser_tak(char *file, const unsigned char *der, size_t len)
+{
+ struct tak *tak;
+ X509 *x509;
+ struct crl *crl;
+ struct auth *a;
+ int rc = 0;
+
+ if ((tak = tak_parse(&x509, file, der, len)) == NULL)
+ return NULL;
+
+ a = valid_ski_aki(file, &auths, tak->ski, tak->aki);
+ crl = crl_get(&crlt, a);
+
+ if (!valid_x509(file, ctx, x509, a, crl, 0))
+ goto out;
+
+ /* TAK EE must be signed by self-signed CA */
+ if (a->parent != NULL)
+ goto out;
+
+ tak->talid = a->cert->talid;
+ rc = 1;
+ out:
+ if (rc == 0) {
+ tak_free(tak);
+ tak = NULL;
+ }
+ X509_free(x509);
+ return tak;
+}
+
+/*
* Load the file specified by the entity information.
*/
static char *
@@ -649,6 +685,11 @@ parse_entity(struct entityq *q, struct msgbuf *msgq)
aspa_buffer(b, aspa);
aspa_free(aspa);
break;
+ case RTYPE_TAK:
+ file = parse_load_file(entp, &f, &flen);
+ io_str_buffer(b, file);
+ proc_parser_tak(file, f, flen);
+ break;
default:
errx(1, "unhandled entity type %d", entp->type);
}
diff --git a/usr.sbin/rpki-client/print.c b/usr.sbin/rpki-client/print.c
index 607d748f26e..b56bd99d02f 100644
--- a/usr.sbin/rpki-client/print.c
+++ b/usr.sbin/rpki-client/print.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: print.c,v 1.16 2022/08/30 23:41:53 tb Exp $ */
+/* $OpenBSD: print.c,v 1.17 2022/11/02 12:43:02 job Exp $ */
/*
* Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -617,3 +617,95 @@ aspa_print(const X509 *x, const struct aspa *p)
}
}
}
+
+static void
+takey_print(char *name, const struct takey *t)
+{
+ char *spki = NULL;
+ size_t i, j = 0;
+
+ if (base64_encode(t->pubkey, t->pubkeysz, &spki) != 0)
+ errx(1, "base64_encode failed in %s", __func__);
+
+ if (outformats & FORMAT_JSON) {
+ printf("\t\t{\n\t\t\t\"name\": \"%s\",\n", name);
+ printf("\t\t\t\"comments\": [");
+ for (i = 0; i < t->commentsz; i++) {
+ printf("\"%s\"", t->comments[i]);
+ if (i + 1 < t->commentsz)
+ printf(", ");
+ }
+ printf("],\n");
+ printf("\t\t\t\"uris\": [");
+ for (i = 0; i < t->urisz; i++) {
+ printf("\"%s\"", t->uris[i]);
+ if (i + 1 < t->urisz)
+ printf(", ");
+ }
+ printf("],\n");
+ printf("\t\t\t\"spki\": \"%s\"\n\t\t}", spki);
+ } else {
+ printf("TAL derived from the '%s' Trust Anchor Key:\n\n", name);
+
+ for (i = 0; i < t->commentsz; i++) {
+ printf("\t# %s\n", t->comments[i]);
+ }
+
+ for (i = 0; i < t->urisz; i++) {
+ printf("\t%s\n\n\t", t->uris[i]);
+ }
+
+ for (i = 0; i < strlen(spki); i++) {
+ printf("%c", spki[i]);
+ j++;
+ if (j == 64) {
+ printf("\n\t");
+ j = 0;
+ }
+ }
+
+ printf("\n\n");
+ }
+
+ free(spki);
+}
+
+void
+tak_print(const X509 *x, const struct tak *p)
+{
+ char tbuf[21];
+
+ if (outformats & FORMAT_JSON) {
+ printf("\t\"type\": \"tak\",\n");
+ printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski));
+ x509_print(x);
+ printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki));
+ printf("\t\"aia\": \"%s\",\n", p->aia);
+ printf("\t\"valid_until\": %lld,\n", (long long)p->expires);
+ printf("\t\"takeys\": [\n");
+ } else {
+ strftime(tbuf, sizeof(tbuf), "%FT%TZ", gmtime(&p->expires));
+ printf("Subject key identifier: %s\n", pretty_key_id(p->ski));
+ x509_print(x);
+ printf("Authority key identifier: %s\n", pretty_key_id(p->aki));
+ printf("Authority info access: %s\n", p->aia);
+ printf("TAK EE certificate valid until: %s\n", tbuf);
+ }
+
+ takey_print("current", p->current);
+
+ if (p->predecessor != NULL) {
+ if (outformats & FORMAT_JSON)
+ printf(",\n");
+ takey_print("predecessor", p->predecessor);
+ }
+
+ if (p->successor != NULL) {
+ if (outformats & FORMAT_JSON)
+ printf(",\n");
+ takey_print("successor", p->successor);
+ }
+
+ if (outformats & FORMAT_JSON)
+ printf("\n\t],\n");
+}
diff --git a/usr.sbin/rpki-client/rpki-client.8 b/usr.sbin/rpki-client/rpki-client.8
index bcb85b2005c..c5d5cd03dbf 100644
--- a/usr.sbin/rpki-client/rpki-client.8
+++ b/usr.sbin/rpki-client/rpki-client.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: rpki-client.8,v 1.73 2022/09/05 20:08:26 job Exp $
+.\" $OpenBSD: rpki-client.8,v 1.74 2022/11/02 12:43:02 job Exp $
.\"
.\" Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
.\"
@@ -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: September 5 2022 $
+.Dd $Mdocdate: November 2 2022 $
.Dt RPKI-CLIENT 8
.Os
.Sh NAME
@@ -305,6 +305,8 @@ Resource Public Key Infrastructure (RPKI) Trust Anchor Locator.
A profile for Resource Public Key Infrastructure (RPKI) Signed Checklists (RSC).
.It draft-ietf-sidrops-aspa-profile-10
A Profile for Autonomous System Provider Authorization (ASPA).
+.It draft-ietf-sidrops-signed-tal-12
+RPKI Signed Object for Trust Anchor Key.
.El
.Sh HISTORY
.Nm
diff --git a/usr.sbin/rpki-client/rsync.c b/usr.sbin/rpki-client/rsync.c
index afcf3db5afd..fbf9daffc02 100644
--- a/usr.sbin/rpki-client/rsync.c
+++ b/usr.sbin/rpki-client/rsync.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: rsync.c,v 1.43 2022/09/02 17:39:51 claudio Exp $ */
+/* $OpenBSD: rsync.c,v 1.44 2022/11/02 12:43:02 job Exp $ */
/*
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
*
@@ -158,6 +158,7 @@ exec_rsync(const char *prog, const char *bind_addr, char *uri, char *dst,
args[i++] = "--include=*.mft";
args[i++] = "--include=*.roa";
args[i++] = "--include=*.asa";
+ args[i++] = "--include=*.tak";
args[i++] = "--exclude=*";
if (bind_addr != NULL) {
args[i++] = "--address";
diff --git a/usr.sbin/rpki-client/tak.c b/usr.sbin/rpki-client/tak.c
new file mode 100644
index 00000000000..d621acac31e
--- /dev/null
+++ b/usr.sbin/rpki-client/tak.c
@@ -0,0 +1,335 @@
+/* $OpenBSD: tak.c,v 1.1 2022/11/02 12:43:02 job Exp $ */
+/*
+ * Copyright (c) 2022 Job Snijders <job@fastly.com>
+ * Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
+ * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/safestack.h>
+#include <openssl/stack.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "extern.h"
+
+/*
+ * Parse results and data of the Trust Anchor Key file.
+ */
+struct parse {
+ const char *fn; /* TAK file name */
+ struct tak *res; /* results */
+};
+
+extern ASN1_OBJECT *tak_oid;
+
+/*
+ * ASN.1 templates for Trust Anchor Keys (draft-ietf-sidrops-signed-tal-12)
+ */
+
+DECLARE_STACK_OF(ASN1_IA5STRING);
+
+#ifndef DEFINE_STACK_OF
+#define sk_ASN1_IA5STRING_num(st) SKM_sk_num(ASN1_IA5STRING, (st))
+#define sk_ASN1_IA5STRING_value(st, i) SKM_sk_value(ASN1_IA5STRING, (st), (i))
+#endif
+
+typedef struct {
+ STACK_OF(ASN1_UTF8STRING) *comments;
+ STACK_OF(ASN1_IA5STRING) *certificateURIs;
+ X509_PUBKEY *subjectPublicKeyInfo;
+} TAKey;
+
+typedef struct {
+ ASN1_INTEGER *version;
+ TAKey *current;
+ TAKey *predecessor;
+ TAKey *successor;
+} TAK;
+
+ASN1_SEQUENCE(TAKey) = {
+ ASN1_SEQUENCE_OF(TAKey, comments, ASN1_UTF8STRING),
+ ASN1_SEQUENCE_OF(TAKey, certificateURIs, ASN1_IA5STRING),
+ ASN1_SIMPLE(TAKey, subjectPublicKeyInfo, X509_PUBKEY),
+} ASN1_SEQUENCE_END(TAKey);
+
+ASN1_SEQUENCE(TAK) = {
+ ASN1_EXP_OPT(TAK, version, ASN1_INTEGER, 0),
+ ASN1_SIMPLE(TAK, current, TAKey),
+ ASN1_EXP_OPT(TAK, predecessor, TAKey, 0),
+ ASN1_EXP_OPT(TAK, successor, TAKey, 1),
+} ASN1_SEQUENCE_END(TAK);
+
+DECLARE_ASN1_FUNCTIONS(TAK);
+IMPLEMENT_ASN1_FUNCTIONS(TAK);
+
+/*
+ * On success return pointer to allocated & valid takey structure,
+ * on failure return NULL.
+ */
+static struct takey *
+parse_takey(const char *fn, const TAKey *takey)
+{
+ const ASN1_UTF8STRING *comment;
+ const ASN1_IA5STRING *certURI;
+ EVP_PKEY *pkey;
+ RSA *r;
+ struct takey *res = NULL;
+ unsigned char *der = NULL, *rder = NULL;
+ unsigned char md[SHA_DIGEST_LENGTH];
+ size_t i;
+ int rdersz, rc = 0;
+
+ if ((res = calloc(1, sizeof(struct takey))) == NULL)
+ err(1, NULL);
+
+ res->commentsz = sk_ASN1_UTF8STRING_num(takey->comments);
+ if (res->commentsz > 0) {
+ res->comments = calloc(res->commentsz, sizeof(char *));
+ if (res->comments == NULL)
+ err(1, NULL);
+
+ for (i = 0; i < res->commentsz; i++) {
+ comment = sk_ASN1_UTF8STRING_value(takey->comments, i);
+ res->comments[i] = strndup(comment->data, comment->length);
+ if (res->comments[i] == NULL)
+ err(1, NULL);
+ }
+ }
+
+ res->urisz = sk_ASN1_IA5STRING_num(takey->certificateURIs);
+ if (res->urisz == 0) {
+ warnx("%s: Signed TAL requires at least 1 CertificateURI", fn);
+ goto out;
+ }
+ if ((res->uris = calloc(res->urisz, sizeof(char *))) == NULL)
+ err(1, NULL);
+
+ for (i = 0; i < res->urisz; i++) {
+ certURI = sk_ASN1_IA5STRING_value(takey->certificateURIs, i);
+ if (!valid_uri(certURI->data, certURI->length, NULL)) {
+ warnx("%s: invalid TA URI", fn);
+ goto out;
+ }
+
+ /* XXX: enforce that protocol is rsync or https. */
+
+ res->uris[i] = strndup(certURI->data, certURI->length);
+ if (res->uris[i] == NULL)
+ err(1, NULL);
+ }
+
+ if ((pkey = X509_PUBKEY_get0(takey->subjectPublicKeyInfo)) == NULL) {
+ warnx("%s: X509_PUBKEY_get0 failed", fn);
+ goto out;
+ }
+
+ if ((r = EVP_PKEY_get0_RSA(pkey)) == NULL) {
+ warnx("%s: EVP_PKEY_get0_RSA failed", fn);
+ goto out;
+ }
+
+ if ((rdersz = i2d_RSAPublicKey(r, &rder)) <= 0) {
+ warnx("%s: i2d_RSAPublicKey failed", fn);
+ goto out;
+ }
+
+ if (!EVP_Digest(rder, rdersz, md, NULL, EVP_sha1(), NULL)) {
+ warnx("%s: EVP_Digest failed", fn);
+ goto out;
+ }
+ res->ski = hex_encode(md, SHA_DIGEST_LENGTH);
+
+ if ((res->pubkeysz = i2d_PUBKEY(pkey, &der)) <= 0) {
+ warnx("%s: i2d_PUBKEY failed", fn);
+ goto out;
+ }
+
+ res->pubkey = der;
+ der = NULL;
+
+ rc = 1;
+ out:
+ if (rc == 0) {
+ takey_free(res);
+ res = NULL;
+ }
+ free(der);
+ free(rder);
+ return res;
+}
+
+/*
+ * Parses the eContent segment of an TAK file
+ * Returns zero on failure, non-zero on success.
+ */
+static int
+tak_parse_econtent(const unsigned char *d, size_t dsz, struct parse *p)
+{
+ TAK *tak;
+ const char *fn;
+ int rc = 0;
+
+ fn = p->fn;
+
+ if ((tak = d2i_TAK(NULL, &d, dsz)) == NULL) {
+ cryptowarnx("%s: failed to parse Trust Anchor Key", fn);
+ goto out;
+ }
+
+ if (!valid_econtent_version(fn, tak->version))
+ goto out;
+
+ p->res->current = parse_takey(fn, tak->current);
+ if (p->res->current == NULL)
+ goto out;
+
+ if (tak->predecessor != NULL) {
+ p->res->predecessor = parse_takey(fn, tak->predecessor);
+ if (p->res->predecessor == NULL)
+ goto out;
+ }
+
+ if (tak->successor != NULL) {
+ p->res->successor = parse_takey(fn, tak->successor);
+ if (p->res->successor == NULL)
+ goto out;
+ }
+
+ rc = 1;
+ out:
+ TAK_free(tak);
+ return rc;
+}
+
+/*
+ * Parse a full draft-ietf-sidrops-signed-tal file.
+ * Returns the TAK or NULL if the object was malformed.
+ */
+struct tak *
+tak_parse(X509 **x509, const char *fn, const unsigned char *der, size_t len)
+{
+ struct parse p;
+ unsigned char *cms;
+ size_t cmsz;
+ const ASN1_TIME *at;
+ int rc = 0;
+
+ memset(&p, 0, sizeof(struct parse));
+ p.fn = fn;
+
+ cms = cms_parse_validate(x509, fn, der, len, tak_oid, &cmsz);
+ if (cms == NULL)
+ return NULL;
+
+ if ((p.res = calloc(1, sizeof(struct tak))) == NULL)
+ err(1, NULL);
+
+ if (!x509_get_aia(*x509, fn, &p.res->aia))
+ goto out;
+ if (!x509_get_aki(*x509, fn, &p.res->aki))
+ goto out;
+ if (!x509_get_ski(*x509, fn, &p.res->ski))
+ goto out;
+ if (p.res->aia == NULL || p.res->aki == NULL || p.res->ski == NULL) {
+ warnx("%s: RFC 6487 section 4.8: "
+ "missing AIA, AKI or SKI X509 extension", fn);
+ goto out;
+ }
+
+ at = X509_get0_notAfter(*x509);
+ if (at == NULL) {
+ warnx("%s: X509_get0_notAfter failed", fn);
+ goto out;
+ }
+ if (!x509_get_time(at, &p.res->expires)) {
+ warnx("%s: ASN1_time_parse failed", fn);
+ goto out;
+ }
+
+ if (!x509_inherits(*x509)) {
+ warnx("%s: RFC 3779 extension not set to inherit", fn);
+ goto out;
+ }
+
+ if (!tak_parse_econtent(cms, cmsz, &p))
+ goto out;
+
+ if (strcmp(p.res->aki, p.res->current->ski) != 0) {
+ warnx("%s: current TAKey's SKI does not match EE AKI", fn);
+ goto out;
+ }
+
+ rc = 1;
+ out:
+ if (rc == 0) {
+ tak_free(p.res);
+ p.res = NULL;
+ X509_free(*x509);
+ *x509 = NULL;
+ }
+ free(cms);
+ return p.res;
+}
+
+/*
+ * Free TAKey pointer.
+ */
+void
+takey_free(struct takey *t)
+{
+ size_t i;
+
+ if (t == NULL)
+ return;
+
+ for (i = 0; i < t->commentsz; i++)
+ free(t->comments[i]);
+
+ for (i = 0; i < t->urisz; i++)
+ free(t->uris[i]);
+
+ free(t->comments);
+ free(t->uris);
+ free(t->ski);
+ free(t->pubkey);
+ free(t);
+}
+
+/*
+ * Free an TAK pointer.
+ * Safe to call with NULL.
+ */
+void
+tak_free(struct tak *t)
+{
+ if (t == NULL)
+ return;
+
+ takey_free(t->current);
+ takey_free(t->predecessor);
+ takey_free(t->successor);
+
+ free(t->aia);
+ free(t->aki);
+ free(t->ski);
+ free(t);
+}
diff --git a/usr.sbin/rpki-client/x509.c b/usr.sbin/rpki-client/x509.c
index 05d0eddd0c7..7ebf9e8e529 100644
--- a/usr.sbin/rpki-client/x509.c
+++ b/usr.sbin/rpki-client/x509.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: x509.c,v 1.52 2022/11/02 10:04:41 tb Exp $ */
+/* $OpenBSD: x509.c,v 1.53 2022/11/02 12:43:02 job Exp $ */
/*
* Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
* Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
@@ -45,6 +45,7 @@ ASN1_OBJECT *sign_time_oid; /* pkcs-9 id-signingTime */
ASN1_OBJECT *bin_sign_time_oid; /* pkcs-9 id-aa-binarySigningTime */
ASN1_OBJECT *rsc_oid; /* id-ct-signedChecklist */
ASN1_OBJECT *aspa_oid; /* id-ct-ASPA */
+ASN1_OBJECT *tak_oid; /* id-ct-SignedTAL */
static const struct {
const char *oid;
@@ -106,6 +107,10 @@ static const struct {
.oid = "1.2.840.113549.1.9.16.1.49",
.ptr = &aspa_oid,
},
+ {
+ .oid = "1.2.840.113549.1.9.16.1.50",
+ .ptr = &tak_oid,
+ },
};
void