From: job Date: Wed, 2 Nov 2022 12:43:02 +0000 (+0000) Subject: Add support for draft-ietf-sidrops-signed-tal-12 X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=ee2a33daaeea41bd3caa3faa3d08e73f5cec094a;p=openbsd 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@ --- 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 * @@ -183,6 +183,7 @@ enum rtype { RTYPE_FILE, RTYPE_RSC, RTYPE_ASPA, + RTYPE_TAK, }; enum location { @@ -274,6 +275,33 @@ struct rsc { time_t expires; /* Not After of the RSC EE */ }; +/* + * 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 */ @@ -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 * Copyright (c) 2019 Kristaps Dzonsons @@ -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 * Copyright (c) 2019 Kristaps Dzonsons @@ -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 * Copyright (c) 2019 Kristaps Dzonsons @@ -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 * @@ -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 * Copyright (c) 2019 Kristaps Dzonsons @@ -520,6 +520,42 @@ proc_parser_aspa(char *file, const unsigned char *der, size_t len) return aspa; } +/* + * 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. */ @@ -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 * Copyright (c) 2019 Kristaps Dzonsons @@ -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 .\" @@ -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 * @@ -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 + * Copyright (c) 2022 Theo Buehler + * Copyright (c) 2019 Kristaps Dzonsons + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 * Copyright (c) 2021 Claudio Jeker @@ -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