1*a56972ebStb /* $OpenBSD: cert.c,v 1.155 2024/12/18 21:12:26 tb Exp $ */ 29a7e9e7fSjob /* 3740e9a54Stb * Copyright (c) 2022 Theo Buehler <tb@openbsd.org> 46b83d8e3Sjob * Copyright (c) 2021 Job Snijders <job@openbsd.org> 59a7e9e7fSjob * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> 69a7e9e7fSjob * 79a7e9e7fSjob * Permission to use, copy, modify, and distribute this software for any 89a7e9e7fSjob * purpose with or without fee is hereby granted, provided that the above 99a7e9e7fSjob * copyright notice and this permission notice appear in all copies. 109a7e9e7fSjob * 119a7e9e7fSjob * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 129a7e9e7fSjob * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 139a7e9e7fSjob * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 149a7e9e7fSjob * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 159a7e9e7fSjob * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 169a7e9e7fSjob * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 179a7e9e7fSjob * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 189a7e9e7fSjob */ 199a7e9e7fSjob 209a7e9e7fSjob #include <assert.h> 219a7e9e7fSjob #include <err.h> 229a7e9e7fSjob #include <stdlib.h> 239a7e9e7fSjob #include <string.h> 249a7e9e7fSjob #include <unistd.h> 259a7e9e7fSjob 26a1753de6Sclaudio #include <openssl/asn1.h> 27a1753de6Sclaudio #include <openssl/x509.h> 28f9f55a97Stb #include <openssl/x509v3.h> 299a7e9e7fSjob 309a7e9e7fSjob #include "extern.h" 319a7e9e7fSjob 32f9f55a97Stb extern ASN1_OBJECT *certpol_oid; /* id-cp-ipAddr-asNumber cert policy */ 33de9b6f5dSclaudio extern ASN1_OBJECT *carepo_oid; /* 1.3.6.1.5.5.7.48.5 (caRepository) */ 34de9b6f5dSclaudio extern ASN1_OBJECT *manifest_oid; /* 1.3.6.1.5.5.7.48.10 (rpkiManifest) */ 35de9b6f5dSclaudio extern ASN1_OBJECT *notify_oid; /* 1.3.6.1.5.5.7.48.13 (rpkiNotify) */ 36d2e465bbSclaudio 371d8c6443Stb int certid = TALSZ_MAX; 380bc420b9Sclaudio 399a7e9e7fSjob /* 409a7e9e7fSjob * Append an IP address structure to our list of results. 416822deefStb * This will also constrain us to having at most one inheritance 426822deefStb * statement per AFI and also not have overlapping ranges (as prohibited 439a7e9e7fSjob * in section 2.2.3.6). 449a7e9e7fSjob * It does not make sure that ranges can't coalesce, that is, that any 459a7e9e7fSjob * two ranges abut each other. 469a7e9e7fSjob * This is warned against in section 2.2.3.6, but doesn't change the 479a7e9e7fSjob * semantics of the system. 4895404126Stb * Returns zero on failure (IP overlap) non-zero on success. 499a7e9e7fSjob */ 509a7e9e7fSjob static int 51381ee599Stb append_ip(const char *fn, struct cert_ip *ips, size_t *num_ips, 52565a9191Stb const struct cert_ip *ip) 539a7e9e7fSjob { 54381ee599Stb if (!ip_addr_check_overlap(ip, fn, ips, *num_ips, 0)) 559a7e9e7fSjob return 0; 56381ee599Stb ips[(*num_ips)++] = *ip; 579a7e9e7fSjob return 1; 589a7e9e7fSjob } 599a7e9e7fSjob 609a7e9e7fSjob /* 619a7e9e7fSjob * Append an AS identifier structure to our list of results. 629a7e9e7fSjob * Makes sure that the identifiers do not overlap or improperly inherit 639a7e9e7fSjob * as defined by RFC 3779 section 3.3. 649a7e9e7fSjob */ 659a7e9e7fSjob static int 66381ee599Stb append_as(const char *fn, struct cert_as *ases, size_t *num_ases, 67565a9191Stb const struct cert_as *as) 689a7e9e7fSjob { 69381ee599Stb if (!as_check_overlap(as, fn, ases, *num_ases, 0)) 709a7e9e7fSjob return 0; 71381ee599Stb ases[(*num_ases)++] = *as; 729a7e9e7fSjob return 1; 739a7e9e7fSjob } 749a7e9e7fSjob 759a7e9e7fSjob /* 761356ae1fStb * Parse a range of AS identifiers as in 3.2.3.8. 779a7e9e7fSjob * Returns zero on failure, non-zero on success. 789a7e9e7fSjob */ 79565a9191Stb int 80381ee599Stb sbgp_as_range(const char *fn, struct cert_as *ases, size_t *num_ases, 81565a9191Stb const ASRange *range) 829a7e9e7fSjob { 839a7e9e7fSjob struct cert_as as; 849a7e9e7fSjob 859a7e9e7fSjob memset(&as, 0, sizeof(struct cert_as)); 869a7e9e7fSjob as.type = CERT_AS_RANGE; 879a7e9e7fSjob 88230ecba3Stb if (!as_id_parse(range->min, &as.range.min)) { 89dcb56be1Sjob warnx("%s: RFC 3779 section 3.2.3.8 (via RFC 1930): " 90565a9191Stb "malformed AS identifier", fn); 91230ecba3Stb return 0; 929a7e9e7fSjob } 939a7e9e7fSjob 94230ecba3Stb if (!as_id_parse(range->max, &as.range.max)) { 95dcb56be1Sjob warnx("%s: RFC 3779 section 3.2.3.8 (via RFC 1930): " 96565a9191Stb "malformed AS identifier", fn); 97230ecba3Stb return 0; 989a7e9e7fSjob } 999a7e9e7fSjob 1009a7e9e7fSjob if (as.range.max == as.range.min) { 1019a7e9e7fSjob warnx("%s: RFC 3379 section 3.2.3.8: ASRange: " 102565a9191Stb "range is singular", fn); 103230ecba3Stb return 0; 1049a7e9e7fSjob } else if (as.range.max < as.range.min) { 1059a7e9e7fSjob warnx("%s: RFC 3379 section 3.2.3.8: ASRange: " 106565a9191Stb "range is out of order", fn); 107230ecba3Stb return 0; 108c1baef53Sclaudio } 1099a7e9e7fSjob 110381ee599Stb return append_as(fn, ases, num_ases, &as); 1119a7e9e7fSjob } 1129a7e9e7fSjob 1139a7e9e7fSjob /* 1149a7e9e7fSjob * Parse an entire 3.2.3.10 integer type. 1159a7e9e7fSjob */ 116565a9191Stb int 117381ee599Stb sbgp_as_id(const char *fn, struct cert_as *ases, size_t *num_ases, 118565a9191Stb const ASN1_INTEGER *i) 1199a7e9e7fSjob { 1209a7e9e7fSjob struct cert_as as; 1219a7e9e7fSjob 1229a7e9e7fSjob memset(&as, 0, sizeof(struct cert_as)); 1239a7e9e7fSjob as.type = CERT_AS_ID; 1249a7e9e7fSjob 1259a7e9e7fSjob if (!as_id_parse(i, &as.id)) { 126dcb56be1Sjob warnx("%s: RFC 3779 section 3.2.3.10 (via RFC 1930): " 127565a9191Stb "malformed AS identifier", fn); 1289a7e9e7fSjob return 0; 129c1baef53Sclaudio } 130c1baef53Sclaudio if (as.id == 0) { 131dcb56be1Sjob warnx("%s: RFC 3779 section 3.2.3.10 (via RFC 1930): " 132565a9191Stb "AS identifier zero is reserved", fn); 1339a7e9e7fSjob return 0; 1349a7e9e7fSjob } 1359a7e9e7fSjob 136381ee599Stb return append_as(fn, ases, num_ases, &as); 1379a7e9e7fSjob } 1389a7e9e7fSjob 1399a7e9e7fSjob static int 140381ee599Stb sbgp_as_inherit(const char *fn, struct cert_as *ases, size_t *num_ases) 1419a7e9e7fSjob { 1429a7e9e7fSjob struct cert_as as; 1439a7e9e7fSjob 1449a7e9e7fSjob memset(&as, 0, sizeof(struct cert_as)); 1459a7e9e7fSjob as.type = CERT_AS_INHERIT; 146dd7dce3fSclaudio 147381ee599Stb return append_as(fn, ases, num_ases, &as); 1489a7e9e7fSjob } 1499a7e9e7fSjob 15018c42b30Stb int 15118c42b30Stb sbgp_parse_assysnum(const char *fn, const ASIdentifiers *asidentifiers, 152381ee599Stb struct cert_as **out_as, size_t *out_num_ases) 1539a7e9e7fSjob { 154dd7dce3fSclaudio const ASIdOrRanges *aors = NULL; 15518c42b30Stb struct cert_as *as = NULL; 156381ee599Stb size_t num_ases = 0, num; 15718c42b30Stb int i; 1589a7e9e7fSjob 159381ee599Stb assert(*out_as == NULL && *out_num_ases == 0); 1609a7e9e7fSjob 161230ecba3Stb if (asidentifiers->rdi != NULL) { 1629a7e9e7fSjob warnx("%s: RFC 6487 section 4.8.11: autonomousSysNum: " 16318c42b30Stb "should not have RDI values", fn); 1649a7e9e7fSjob goto out; 1659a7e9e7fSjob } 1669a7e9e7fSjob 167230ecba3Stb if (asidentifiers->asnum == NULL) { 16880272c49Sderaadt warnx("%s: RFC 6487 section 4.8.11: autonomousSysNum: " 16918c42b30Stb "no AS number resource set", fn); 1709a7e9e7fSjob goto out; 1719a7e9e7fSjob } 1729a7e9e7fSjob 173dd7dce3fSclaudio switch (asidentifiers->asnum->type) { 174dd7dce3fSclaudio case ASIdentifierChoice_inherit: 175381ee599Stb num = 1; 176dd7dce3fSclaudio break; 177dd7dce3fSclaudio case ASIdentifierChoice_asIdsOrRanges: 178dd7dce3fSclaudio aors = asidentifiers->asnum->u.asIdsOrRanges; 179381ee599Stb num = sk_ASIdOrRange_num(aors); 180dd7dce3fSclaudio break; 181dd7dce3fSclaudio default: 182dd7dce3fSclaudio warnx("%s: RFC 3779 section 3.2.3.2: ASIdentifierChoice: " 18318c42b30Stb "unknown type %d", fn, asidentifiers->asnum->type); 1849a7e9e7fSjob goto out; 185dd7dce3fSclaudio } 186dd7dce3fSclaudio 187381ee599Stb if (num == 0) { 18818c42b30Stb warnx("%s: RFC 6487 section 4.8.11: empty asIdsOrRanges", fn); 18926660650Stb goto out; 19026660650Stb } 191381ee599Stb if (num >= MAX_AS_SIZE) { 192dd7dce3fSclaudio warnx("%s: too many AS number entries: limit %d", 19318c42b30Stb fn, MAX_AS_SIZE); 194dd7dce3fSclaudio goto out; 195dd7dce3fSclaudio } 196381ee599Stb as = calloc(num, sizeof(struct cert_as)); 19718c42b30Stb if (as == NULL) 198dd7dce3fSclaudio err(1, NULL); 199dd7dce3fSclaudio 200dd7dce3fSclaudio if (aors == NULL) { 201381ee599Stb if (!sbgp_as_inherit(fn, as, &num_ases)) 202dd7dce3fSclaudio goto out; 203dd7dce3fSclaudio } 204dd7dce3fSclaudio 205dd7dce3fSclaudio for (i = 0; i < sk_ASIdOrRange_num(aors); i++) { 206dd7dce3fSclaudio const ASIdOrRange *aor; 207dd7dce3fSclaudio 208dd7dce3fSclaudio aor = sk_ASIdOrRange_value(aors, i); 209dd7dce3fSclaudio switch (aor->type) { 210dd7dce3fSclaudio case ASIdOrRange_id: 211381ee599Stb if (!sbgp_as_id(fn, as, &num_ases, aor->u.id)) 212dd7dce3fSclaudio goto out; 213dd7dce3fSclaudio break; 214dd7dce3fSclaudio case ASIdOrRange_range: 215381ee599Stb if (!sbgp_as_range(fn, as, &num_ases, aor->u.range)) 216dd7dce3fSclaudio goto out; 217dd7dce3fSclaudio break; 218dd7dce3fSclaudio default: 219dd7dce3fSclaudio warnx("%s: RFC 3779 section 3.2.3.5: ASIdOrRange: " 22018c42b30Stb "unknown type %d", fn, aor->type); 221dd7dce3fSclaudio goto out; 222dd7dce3fSclaudio } 223dd7dce3fSclaudio } 2249a7e9e7fSjob 22518c42b30Stb *out_as = as; 226381ee599Stb *out_num_ases = num_ases; 22718c42b30Stb 22818c42b30Stb return 1; 22918c42b30Stb 23018c42b30Stb out: 23118c42b30Stb free(as); 23218c42b30Stb 23318c42b30Stb return 0; 23418c42b30Stb } 23518c42b30Stb 23618c42b30Stb /* 23718c42b30Stb * Parse RFC 6487 4.8.11 X509v3 extension, with syntax documented in RFC 23818c42b30Stb * 3779 starting in section 3.2. 23918c42b30Stb * Returns zero on failure, non-zero on success. 24018c42b30Stb */ 24118c42b30Stb static int 2422b470c61Stb sbgp_assysnum(const char *fn, struct cert *cert, X509_EXTENSION *ext) 24318c42b30Stb { 24418c42b30Stb ASIdentifiers *asidentifiers = NULL; 24518c42b30Stb int rc = 0; 24618c42b30Stb 24718c42b30Stb if (!X509_EXTENSION_get_critical(ext)) { 24818c42b30Stb warnx("%s: RFC 6487 section 4.8.11: autonomousSysNum: " 2492b470c61Stb "extension not critical", fn); 25018c42b30Stb goto out; 25118c42b30Stb } 25218c42b30Stb 25318c42b30Stb if ((asidentifiers = X509V3_EXT_d2i(ext)) == NULL) { 25418c42b30Stb warnx("%s: RFC 6487 section 4.8.11: autonomousSysNum: " 2552b470c61Stb "failed extension parse", fn); 25618c42b30Stb goto out; 25718c42b30Stb } 25818c42b30Stb 259381ee599Stb if (!sbgp_parse_assysnum(fn, asidentifiers, &cert->ases, 260381ee599Stb &cert->num_ases)) 26118c42b30Stb goto out; 26218c42b30Stb 2639a7e9e7fSjob rc = 1; 2649a7e9e7fSjob out: 265230ecba3Stb ASIdentifiers_free(asidentifiers); 2669a7e9e7fSjob return rc; 2679a7e9e7fSjob } 2689a7e9e7fSjob 2699a7e9e7fSjob /* 2706de5d2cdStb * Construct a RFC 3779 2.2.3.8 range from its bit string. 2716de5d2cdStb * Returns zero on failure, non-zero on success. 2726de5d2cdStb */ 273565a9191Stb int 274381ee599Stb sbgp_addr(const char *fn, struct cert_ip *ips, size_t *num_ips, enum afi afi, 275565a9191Stb const ASN1_BIT_STRING *bs) 2766de5d2cdStb { 277e212aee8Stb struct cert_ip ip; 278e212aee8Stb 279e212aee8Stb memset(&ip, 0, sizeof(struct cert_ip)); 280e212aee8Stb 281e212aee8Stb ip.afi = afi; 282e212aee8Stb ip.type = CERT_IP_ADDR; 283e212aee8Stb 284565a9191Stb if (!ip_addr_parse(bs, afi, fn, &ip.ip)) { 2856de5d2cdStb warnx("%s: RFC 3779 section 2.2.3.8: IPAddress: " 286565a9191Stb "invalid IP address", fn); 2876de5d2cdStb return 0; 2886de5d2cdStb } 2896de5d2cdStb 290e212aee8Stb if (!ip_cert_compose_ranges(&ip)) { 2916de5d2cdStb warnx("%s: RFC 3779 section 2.2.3.8: IPAddress: " 292565a9191Stb "IP address range reversed", fn); 2936de5d2cdStb return 0; 2946de5d2cdStb } 2956de5d2cdStb 296381ee599Stb return append_ip(fn, ips, num_ips, &ip); 2976de5d2cdStb } 2986de5d2cdStb 2996de5d2cdStb /* 3009a7e9e7fSjob * Parse RFC 3779 2.2.3.9 range of addresses. 30195404126Stb * Returns zero on failure, non-zero on success. 3029a7e9e7fSjob */ 303565a9191Stb int 304381ee599Stb sbgp_addr_range(const char *fn, struct cert_ip *ips, size_t *num_ips, 305565a9191Stb enum afi afi, const IPAddressRange *range) 3069a7e9e7fSjob { 307e212aee8Stb struct cert_ip ip; 308e212aee8Stb 309e212aee8Stb memset(&ip, 0, sizeof(struct cert_ip)); 310e212aee8Stb 311e212aee8Stb ip.afi = afi; 312e212aee8Stb ip.type = CERT_IP_RANGE; 313e212aee8Stb 314565a9191Stb if (!ip_addr_parse(range->min, afi, fn, &ip.range.min)) { 3159a7e9e7fSjob warnx("%s: RFC 3779 section 2.2.3.9: IPAddressRange: " 316565a9191Stb "invalid IP address", fn); 31783093aedStb return 0; 3189a7e9e7fSjob } 3199a7e9e7fSjob 320565a9191Stb if (!ip_addr_parse(range->max, afi, fn, &ip.range.max)) { 3219a7e9e7fSjob warnx("%s: RFC 3779 section 2.2.3.9: IPAddressRange: " 322565a9191Stb "invalid IP address", fn); 32383093aedStb return 0; 3249a7e9e7fSjob } 3259a7e9e7fSjob 326e212aee8Stb if (!ip_cert_compose_ranges(&ip)) { 3279a7e9e7fSjob warnx("%s: RFC 3779 section 2.2.3.9: IPAddressRange: " 328565a9191Stb "IP address range reversed", fn); 32983093aedStb return 0; 3309a7e9e7fSjob } 3319a7e9e7fSjob 332381ee599Stb return append_ip(fn, ips, num_ips, &ip); 333e212aee8Stb } 334e212aee8Stb 335e212aee8Stb static int 336381ee599Stb sbgp_addr_inherit(const char *fn, struct cert_ip *ips, size_t *num_ips, 337565a9191Stb enum afi afi) 338e212aee8Stb { 339e212aee8Stb struct cert_ip ip; 340e212aee8Stb 341e212aee8Stb memset(&ip, 0, sizeof(struct cert_ip)); 342e212aee8Stb 343e212aee8Stb ip.afi = afi; 344e212aee8Stb ip.type = CERT_IP_INHERIT; 345e212aee8Stb 346381ee599Stb return append_ip(fn, ips, num_ips, &ip); 3479a7e9e7fSjob } 3489a7e9e7fSjob 34918c42b30Stb int 35018c42b30Stb sbgp_parse_ipaddrblk(const char *fn, const IPAddrBlocks *addrblk, 351381ee599Stb struct cert_ip **out_ips, size_t *out_num_ips) 35218c42b30Stb { 35318c42b30Stb const IPAddressFamily *af; 35418c42b30Stb const IPAddressOrRanges *aors; 35518c42b30Stb const IPAddressOrRange *aor; 35618c42b30Stb enum afi afi; 35718c42b30Stb struct cert_ip *ips = NULL; 358381ee599Stb size_t num_ips = 0, num; 359de494ec3Stb int ipv4_seen = 0, ipv6_seen = 0; 360de494ec3Stb int i, j, ipaddrblocksz; 36118c42b30Stb 362381ee599Stb assert(*out_ips == NULL && *out_num_ips == 0); 36318c42b30Stb 364de494ec3Stb ipaddrblocksz = sk_IPAddressFamily_num(addrblk); 365de494ec3Stb if (ipaddrblocksz != 1 && ipaddrblocksz != 2) { 366de494ec3Stb warnx("%s: RFC 6487 section 4.8.10: unexpected number of " 367de494ec3Stb "ipAddrBlocks (got %d, expected 1 or 2)", 368de494ec3Stb fn, ipaddrblocksz); 369de494ec3Stb goto out; 370de494ec3Stb } 371de494ec3Stb 372de494ec3Stb for (i = 0; i < ipaddrblocksz; i++) { 37318c42b30Stb af = sk_IPAddressFamily_value(addrblk, i); 37418c42b30Stb 37518c42b30Stb switch (af->ipAddressChoice->type) { 37618c42b30Stb case IPAddressChoice_inherit: 37718c42b30Stb aors = NULL; 378381ee599Stb num = num_ips + 1; 37918c42b30Stb break; 38018c42b30Stb case IPAddressChoice_addressesOrRanges: 38118c42b30Stb aors = af->ipAddressChoice->u.addressesOrRanges; 382381ee599Stb num = num_ips + sk_IPAddressOrRange_num(aors); 38318c42b30Stb break; 38418c42b30Stb default: 38518c42b30Stb warnx("%s: RFC 3779: IPAddressChoice: unknown type %d", 38618c42b30Stb fn, af->ipAddressChoice->type); 38718c42b30Stb goto out; 38818c42b30Stb } 389381ee599Stb if (num == num_ips) { 39018c42b30Stb warnx("%s: RFC 6487 section 4.8.10: " 39118c42b30Stb "empty ipAddressesOrRanges", fn); 39218c42b30Stb goto out; 39318c42b30Stb } 39418c42b30Stb 395381ee599Stb if (num >= MAX_IP_SIZE) 39618c42b30Stb goto out; 397381ee599Stb ips = recallocarray(ips, num_ips, num, sizeof(struct cert_ip)); 39818c42b30Stb if (ips == NULL) 39918c42b30Stb err(1, NULL); 40018c42b30Stb 40118c42b30Stb if (!ip_addr_afi_parse(fn, af->addressFamily, &afi)) { 40218c42b30Stb warnx("%s: RFC 3779: invalid AFI", fn); 40318c42b30Stb goto out; 40418c42b30Stb } 40518c42b30Stb 406de494ec3Stb switch (afi) { 407de494ec3Stb case AFI_IPV4: 408de494ec3Stb if (ipv4_seen++ > 0) { 409de494ec3Stb warnx("%s: RFC 6487 section 4.8.10: " 410de494ec3Stb "IPv4 appears twice", fn); 411de494ec3Stb goto out; 412de494ec3Stb } 413de494ec3Stb break; 414de494ec3Stb case AFI_IPV6: 415de494ec3Stb if (ipv6_seen++ > 0) { 416de494ec3Stb warnx("%s: RFC 6487 section 4.8.10: " 417de494ec3Stb "IPv6 appears twice", fn); 418de494ec3Stb goto out; 419de494ec3Stb } 420de494ec3Stb break; 421de494ec3Stb } 422de494ec3Stb 42318c42b30Stb if (aors == NULL) { 424381ee599Stb if (!sbgp_addr_inherit(fn, ips, &num_ips, afi)) 42518c42b30Stb goto out; 42618c42b30Stb continue; 42718c42b30Stb } 42818c42b30Stb 42918c42b30Stb for (j = 0; j < sk_IPAddressOrRange_num(aors); j++) { 43018c42b30Stb aor = sk_IPAddressOrRange_value(aors, j); 43118c42b30Stb switch (aor->type) { 43218c42b30Stb case IPAddressOrRange_addressPrefix: 433381ee599Stb if (!sbgp_addr(fn, ips, &num_ips, afi, 43418c42b30Stb aor->u.addressPrefix)) 43518c42b30Stb goto out; 43618c42b30Stb break; 43718c42b30Stb case IPAddressOrRange_addressRange: 438381ee599Stb if (!sbgp_addr_range(fn, ips, &num_ips, afi, 43918c42b30Stb aor->u.addressRange)) 44018c42b30Stb goto out; 44118c42b30Stb break; 44218c42b30Stb default: 44318c42b30Stb warnx("%s: RFC 3779: IPAddressOrRange: " 44418c42b30Stb "unknown type %d", fn, aor->type); 44518c42b30Stb goto out; 44618c42b30Stb } 44718c42b30Stb } 44818c42b30Stb } 44918c42b30Stb 45018c42b30Stb *out_ips = ips; 451381ee599Stb *out_num_ips = num_ips; 45218c42b30Stb 45318c42b30Stb return 1; 45418c42b30Stb 45518c42b30Stb out: 45618c42b30Stb free(ips); 45718c42b30Stb 45818c42b30Stb return 0; 45918c42b30Stb } 46018c42b30Stb 4619a7e9e7fSjob /* 4629a7e9e7fSjob * Parse an sbgp-ipAddrBlock X509 extension, RFC 6487 4.8.10, with 4639a7e9e7fSjob * syntax documented in RFC 3779 starting in section 2.2. 4649a7e9e7fSjob * Returns zero on failure, non-zero on success. 4659a7e9e7fSjob */ 4669a7e9e7fSjob static int 4672b470c61Stb sbgp_ipaddrblk(const char *fn, struct cert *cert, X509_EXTENSION *ext) 4689a7e9e7fSjob { 469891d6bceSjob IPAddrBlocks *addrblk = NULL; 47018c42b30Stb int rc = 0; 4719a7e9e7fSjob 4720bb66b69Stb if (!X509_EXTENSION_get_critical(ext)) { 473c0528901Stb warnx("%s: RFC 6487 section 4.8.10: sbgp-ipAddrBlock: " 4742b470c61Stb "extension not critical", fn); 4750bb66b69Stb goto out; 4760bb66b69Stb } 4770bb66b69Stb 47883093aedStb if ((addrblk = X509V3_EXT_d2i(ext)) == NULL) { 479c0528901Stb warnx("%s: RFC 6487 section 4.8.10: sbgp-ipAddrBlock: " 4802b470c61Stb "failed extension parse", fn); 4819a7e9e7fSjob goto out; 4829a7e9e7fSjob } 4839a7e9e7fSjob 484381ee599Stb if (!sbgp_parse_ipaddrblk(fn, addrblk, &cert->ips, &cert->num_ips)) 4859a7e9e7fSjob goto out; 486b0a85f64Stb 487381ee599Stb if (cert->num_ips == 0) { 4882b470c61Stb warnx("%s: RFC 6487 section 4.8.10: empty ipAddrBlock", fn); 48926660650Stb goto out; 49026660650Stb } 49126660650Stb 4929a7e9e7fSjob rc = 1; 4939a7e9e7fSjob out: 494891d6bceSjob IPAddrBlocks_free(addrblk); 4959a7e9e7fSjob return rc; 4969a7e9e7fSjob } 4979a7e9e7fSjob 498d23e87a2Stb /* 499c5305b1dStb * Parse "Subject Information Access" extension for a CA cert, 500c5305b1dStb * RFC 6487, section 4.8.8.1 and RFC 8182, section 3.2. 5012edf3539Stb * Returns zero on failure, non-zero on success. 5022edf3539Stb */ 5032edf3539Stb static int 5042b470c61Stb sbgp_sia(const char *fn, struct cert *cert, X509_EXTENSION *ext) 5052edf3539Stb { 5062edf3539Stb AUTHORITY_INFO_ACCESS *sia = NULL; 5072edf3539Stb ACCESS_DESCRIPTION *ad; 5082edf3539Stb ASN1_OBJECT *oid; 509c180fb4aSjob const char *mftfilename; 51024ad82f4Stb char *carepo = NULL, *rpkimft = NULL, *notify = NULL; 5112edf3539Stb int i, rc = 0; 5122edf3539Stb 513c5305b1dStb assert(cert->repo == NULL && cert->mft == NULL && cert->notify == NULL); 514c5305b1dStb 5152edf3539Stb if (X509_EXTENSION_get_critical(ext)) { 5162edf3539Stb warnx("%s: RFC 6487 section 4.8.8: SIA: " 5172b470c61Stb "extension not non-critical", fn); 5182edf3539Stb goto out; 5192edf3539Stb } 5202edf3539Stb 5212edf3539Stb if ((sia = X509V3_EXT_d2i(ext)) == NULL) { 522c0528901Stb warnx("%s: RFC 6487 section 4.8.8: SIA: failed extension parse", 5232b470c61Stb fn); 5242edf3539Stb goto out; 5252edf3539Stb } 5262edf3539Stb 5272edf3539Stb for (i = 0; i < sk_ACCESS_DESCRIPTION_num(sia); i++) { 5282edf3539Stb ad = sk_ACCESS_DESCRIPTION_value(sia, i); 5292edf3539Stb 5302edf3539Stb oid = ad->method; 5312edf3539Stb 5322edf3539Stb if (OBJ_cmp(oid, carepo_oid) == 0) { 5332b872fe6Stb if (!x509_location(fn, "SIA: caRepository", 534c5305b1dStb ad->location, &carepo)) 5352edf3539Stb goto out; 536c5305b1dStb if (cert->repo == NULL && strncasecmp(carepo, 537c5305b1dStb RSYNC_PROTO, RSYNC_PROTO_LEN) == 0) { 53839ab19dfStb if (carepo[strlen(carepo) - 1] != '/') { 53939ab19dfStb char *carepo_tmp; 54039ab19dfStb 54139ab19dfStb if (asprintf(&carepo_tmp, "%s/", 54239ab19dfStb carepo) == -1) 54339ab19dfStb errx(1, NULL); 54439ab19dfStb free(carepo); 54539ab19dfStb carepo = carepo_tmp; 54639ab19dfStb } 54739ab19dfStb 548c5305b1dStb cert->repo = carepo; 549c5305b1dStb carepo = NULL; 550c5305b1dStb continue; 551c5305b1dStb } 552c5305b1dStb if (verbose) 553c5305b1dStb warnx("%s: RFC 6487 section 4.8.8: SIA: " 554c5305b1dStb "ignoring location %s", fn, carepo); 555c5305b1dStb free(carepo); 556c5305b1dStb carepo = NULL; 5572edf3539Stb } else if (OBJ_cmp(oid, manifest_oid) == 0) { 5582b872fe6Stb if (!x509_location(fn, "SIA: rpkiManifest", 559c5305b1dStb ad->location, &rpkimft)) 5602edf3539Stb goto out; 561c5305b1dStb if (cert->mft == NULL && strncasecmp(rpkimft, 562c5305b1dStb RSYNC_PROTO, RSYNC_PROTO_LEN) == 0) { 563c5305b1dStb cert->mft = rpkimft; 564c5305b1dStb rpkimft = NULL; 565c5305b1dStb continue; 566c5305b1dStb } 567c5305b1dStb if (verbose) 568c5305b1dStb warnx("%s: RFC 6487 section 4.8.8: SIA: " 569c5305b1dStb "ignoring location %s", fn, rpkimft); 570c5305b1dStb free(rpkimft); 571c5305b1dStb rpkimft = NULL; 5722edf3539Stb } else if (OBJ_cmp(oid, notify_oid) == 0) { 5732b470c61Stb if (!x509_location(fn, "SIA: rpkiNotify", 57424ad82f4Stb ad->location, ¬ify)) 5752edf3539Stb goto out; 57624ad82f4Stb if (strncasecmp(notify, HTTPS_PROTO, 5772b872fe6Stb HTTPS_PROTO_LEN) != 0) { 5782b872fe6Stb warnx("%s: non-https uri in rpkiNotify: %s", 5792b872fe6Stb fn, cert->notify); 58024ad82f4Stb free(notify); 5812b872fe6Stb goto out; 5822b872fe6Stb } 58324ad82f4Stb if (cert->notify != NULL) { 58424ad82f4Stb warnx("%s: unexpected rpkiNotify accessMethod", 58524ad82f4Stb fn); 58624ad82f4Stb free(notify); 58724ad82f4Stb goto out; 58824ad82f4Stb } 58924ad82f4Stb cert->notify = notify; 59024ad82f4Stb notify = NULL; 59178268cf8Stb } else { 59278268cf8Stb char buf[128]; 59378268cf8Stb 59478268cf8Stb OBJ_obj2txt(buf, sizeof(buf), oid, 0); 59578268cf8Stb warnx("%s: RFC 6487 section 4.8.8.1: unexpected" 59678268cf8Stb " accessMethod: %s", fn, buf); 59778268cf8Stb goto out; 5982edf3539Stb } 5992edf3539Stb } 6002edf3539Stb 6012b470c61Stb if (cert->mft == NULL || cert->repo == NULL) { 602572cd5a8Stb warnx("%s: RFC 6487 section 4.8.8: SIA: missing caRepository " 6032b470c61Stb "or rpkiManifest", fn); 6042edf3539Stb goto out; 6052edf3539Stb } 6062edf3539Stb 6072b470c61Stb mftfilename = strrchr(cert->mft, '/'); 6086747684dSjob if (mftfilename == NULL) { 6092b470c61Stb warnx("%s: SIA: invalid rpkiManifest entry", fn); 6106747684dSjob goto out; 6116747684dSjob } 6126747684dSjob mftfilename++; 6136747684dSjob if (!valid_filename(mftfilename, strlen(mftfilename))) { 614c180fb4aSjob warnx("%s: SIA: rpkiManifest filename contains invalid " 6152b470c61Stb "characters", fn); 616c180fb4aSjob goto out; 617c180fb4aSjob } 618c180fb4aSjob 6192e5628f8Stb if (strstr(cert->mft, cert->repo) != cert->mft || 6202e5628f8Stb cert->mft + strlen(cert->repo) != mftfilename) { 6212edf3539Stb warnx("%s: RFC 6487 section 4.8.8: SIA: " 6222b470c61Stb "conflicting URIs for caRepository and rpkiManifest", fn); 6232edf3539Stb goto out; 6242edf3539Stb } 6252edf3539Stb 6262b470c61Stb if (rtype_from_file_extension(cert->mft) != RTYPE_MFT) { 6272b470c61Stb warnx("%s: RFC 6487 section 4.8.8: SIA: not an MFT file", fn); 6282edf3539Stb goto out; 6292edf3539Stb } 6302edf3539Stb 6312edf3539Stb rc = 1; 6322edf3539Stb out: 6332edf3539Stb AUTHORITY_INFO_ACCESS_free(sia); 6342edf3539Stb return rc; 6352edf3539Stb } 6362edf3539Stb 6372edf3539Stb /* 638d23e87a2Stb * Parse the certificate policies extension and check that it follows RFC 7318. 639d23e87a2Stb * Returns zero on failure, non-zero on success. 640d23e87a2Stb */ 641f9f55a97Stb static int 6422b470c61Stb certificate_policies(const char *fn, struct cert *cert, X509_EXTENSION *ext) 643f9f55a97Stb { 644f9f55a97Stb STACK_OF(POLICYINFO) *policies = NULL; 645f9f55a97Stb POLICYINFO *policy; 646f9f55a97Stb STACK_OF(POLICYQUALINFO) *qualifiers; 647f9f55a97Stb POLICYQUALINFO *qualifier; 648f9f55a97Stb int nid; 649f9f55a97Stb int rc = 0; 650f9f55a97Stb 651f9f55a97Stb if (!X509_EXTENSION_get_critical(ext)) { 652c0528901Stb warnx("%s: RFC 6487 section 4.8.9: certificatePolicies: " 6532b470c61Stb "extension not critical", fn); 654f9f55a97Stb goto out; 655f9f55a97Stb } 656f9f55a97Stb 657f9f55a97Stb if ((policies = X509V3_EXT_d2i(ext)) == NULL) { 658c0528901Stb warnx("%s: RFC 6487 section 4.8.9: certificatePolicies: " 6592b470c61Stb "failed extension parse", fn); 660f9f55a97Stb goto out; 661f9f55a97Stb } 662f9f55a97Stb 663f9f55a97Stb if (sk_POLICYINFO_num(policies) != 1) { 664f9f55a97Stb warnx("%s: RFC 6487 section 4.8.9: certificatePolicies: " 6652b470c61Stb "want 1 policy, got %d", fn, sk_POLICYINFO_num(policies)); 666f9f55a97Stb goto out; 667f9f55a97Stb } 668f9f55a97Stb 669f9f55a97Stb policy = sk_POLICYINFO_value(policies, 0); 670f9f55a97Stb assert(policy != NULL && policy->policyid != NULL); 671f9f55a97Stb 672f9f55a97Stb if (OBJ_cmp(policy->policyid, certpol_oid) != 0) { 673f9f55a97Stb char pbuf[128], cbuf[128]; 674f9f55a97Stb 675f9f55a97Stb OBJ_obj2txt(pbuf, sizeof(pbuf), policy->policyid, 1); 676f9f55a97Stb OBJ_obj2txt(cbuf, sizeof(cbuf), certpol_oid, 1); 677f9f55a97Stb warnx("%s: RFC 7318 section 2: certificatePolicies: " 6782b470c61Stb "unexpected OID: %s, want %s", fn, pbuf, cbuf); 679f9f55a97Stb goto out; 680f9f55a97Stb } 681f9f55a97Stb 682f9f55a97Stb /* Policy qualifiers are optional. If they're absent, we're done. */ 683f9f55a97Stb if ((qualifiers = policy->qualifiers) == NULL) { 684f9f55a97Stb rc = 1; 685f9f55a97Stb goto out; 686f9f55a97Stb } 687f9f55a97Stb 688f9f55a97Stb if (sk_POLICYQUALINFO_num(qualifiers) != 1) { 689f9f55a97Stb warnx("%s: RFC 7318 section 2: certificatePolicies: " 6902b470c61Stb "want 1 policy qualifier, got %d", fn, 691f9f55a97Stb sk_POLICYQUALINFO_num(qualifiers)); 692f9f55a97Stb goto out; 693f9f55a97Stb } 694f9f55a97Stb 695f9f55a97Stb qualifier = sk_POLICYQUALINFO_value(qualifiers, 0); 696f9f55a97Stb assert(qualifier != NULL && qualifier->pqualid != NULL); 697f9f55a97Stb 698f9f55a97Stb if ((nid = OBJ_obj2nid(qualifier->pqualid)) != NID_id_qt_cps) { 699f9f55a97Stb warnx("%s: RFC 7318 section 2: certificatePolicies: " 7002b470c61Stb "want CPS, got %s", fn, nid2str(nid)); 701f9f55a97Stb goto out; 702f9f55a97Stb } 703f9f55a97Stb 7048eb56b7eSjob if (verbose > 1 && !filemode) 7052b470c61Stb warnx("%s: CPS %.*s", fn, qualifier->d.cpsuri->length, 706f9f55a97Stb qualifier->d.cpsuri->data); 707f9f55a97Stb 708f9f55a97Stb rc = 1; 709f9f55a97Stb out: 710f9f55a97Stb sk_POLICYINFO_pop_free(policies, POLICYINFO_free); 711f9f55a97Stb return rc; 712f9f55a97Stb } 713f9f55a97Stb 7140466b83fStb static int 7150466b83fStb cert_check_subject_and_issuer(const char *fn, const X509 *x) 7160466b83fStb { 7170466b83fStb const X509_NAME *name; 7180466b83fStb 7190466b83fStb if ((name = X509_get_subject_name(x)) == NULL) { 7200466b83fStb warnx("%s: X509_get_subject_name", fn); 7210466b83fStb return 0; 7220466b83fStb } 7230466b83fStb if (!x509_valid_name(fn, "subject", name)) 7240466b83fStb return 0; 7250466b83fStb 7260466b83fStb if ((name = X509_get_issuer_name(x)) == NULL) { 7270466b83fStb warnx("%s: X509_get_issuer_name", fn); 7280466b83fStb return 0; 7290466b83fStb } 7300466b83fStb if (!x509_valid_name(fn, "issuer", name)) 7310466b83fStb return 0; 7320466b83fStb 7330466b83fStb return 1; 7340466b83fStb } 7350466b83fStb 7369a7e9e7fSjob /* 7377cc1142dSjob * Lightweight version of cert_parse_pre() for EE certs. 7387cc1142dSjob * Parses the two RFC 3779 extensions, and performs some sanity checks. 73999dbdb7fStb * Returns cert on success and NULL on failure. 74099dbdb7fStb */ 74199dbdb7fStb struct cert * 742891d6bceSjob cert_parse_ee_cert(const char *fn, int talid, X509 *x) 74399dbdb7fStb { 7442b470c61Stb struct cert *cert; 74599dbdb7fStb X509_EXTENSION *ext; 74699dbdb7fStb int index; 74799dbdb7fStb 7482b470c61Stb if ((cert = calloc(1, sizeof(struct cert))) == NULL) 74999dbdb7fStb err(1, NULL); 75099dbdb7fStb 751138af1ebSjob if (X509_get_version(x) != 2) { 752138af1ebSjob warnx("%s: RFC 6487 4.1: X.509 version must be v3", fn); 753138af1ebSjob goto out; 754138af1ebSjob } 755138af1ebSjob 7560466b83fStb if (!cert_check_subject_and_issuer(fn, x)) 7577cc1142dSjob goto out; 7587cc1142dSjob 759e891962dStb if (!x509_cache_extensions(x, fn)) 760e891962dStb goto out; 761e891962dStb 762a5826434Stb if ((cert->purpose = x509_get_purpose(x, fn)) != CERT_PURPOSE_EE) { 763a5826434Stb warnx("%s: expected EE cert, got %s", fn, 764a5826434Stb purpose2str(cert->purpose)); 765a5826434Stb goto out; 766a5826434Stb } 767a5826434Stb 76899dbdb7fStb index = X509_get_ext_by_NID(x, NID_sbgp_ipAddrBlock, -1); 76999dbdb7fStb if ((ext = X509_get_ext(x, index)) != NULL) { 7702b470c61Stb if (!sbgp_ipaddrblk(fn, cert, ext)) 77199dbdb7fStb goto out; 77299dbdb7fStb } 77399dbdb7fStb 77499dbdb7fStb index = X509_get_ext_by_NID(x, NID_sbgp_autonomousSysNum, -1); 77599dbdb7fStb if ((ext = X509_get_ext(x, index)) != NULL) { 7762b470c61Stb if (!sbgp_assysnum(fn, cert, ext)) 77799dbdb7fStb goto out; 77899dbdb7fStb } 77999dbdb7fStb 78099dbdb7fStb if (!X509_up_ref(x)) { 781c0528901Stb warnx("%s: X509_up_ref failed", fn); 78299dbdb7fStb goto out; 78399dbdb7fStb } 78499dbdb7fStb 7852b470c61Stb cert->x509 = x; 7862b470c61Stb cert->talid = talid; 787891d6bceSjob 7882b470c61Stb if (!constraints_validate(fn, cert)) 789891d6bceSjob goto out; 790891d6bceSjob 7912b470c61Stb return cert; 79299dbdb7fStb 79399dbdb7fStb out: 7942b470c61Stb cert_free(cert); 79599dbdb7fStb return NULL; 79699dbdb7fStb } 79799dbdb7fStb 79899dbdb7fStb /* 7999a7e9e7fSjob * Parse and partially validate an RPKI X509 certificate (either a trust 8009a7e9e7fSjob * anchor or a certificate) as defined in RFC 6487. 801f999fe57Sclaudio * Returns the parse results or NULL on failure. 8029a7e9e7fSjob */ 803ba153bd8Sclaudio struct cert * 804ba153bd8Sclaudio cert_parse_pre(const char *fn, const unsigned char *der, size_t len) 8059a7e9e7fSjob { 8062b470c61Stb struct cert *cert; 807797cceeeStb const unsigned char *oder; 80828d6404fStb size_t j; 80928d6404fStb int i, extsz; 8109a7e9e7fSjob X509 *x = NULL; 8119a7e9e7fSjob X509_EXTENSION *ext = NULL; 8127a3e7ef3Stb const ASN1_BIT_STRING *issuer_uid = NULL, *subject_uid = NULL; 8139a7e9e7fSjob ASN1_OBJECT *obj; 814ae36eebeSjob EVP_PKEY *pkey; 81576281e49Stb int nid, ip, as, sia, cp, crldp, aia, aki, ski, 81676281e49Stb eku, bc, ku; 81776281e49Stb 81876281e49Stb nid = ip = as = sia = cp = crldp = aia = aki = ski = eku = bc = ku = 0; 8199a7e9e7fSjob 820803d3b9aSclaudio /* just fail for empty buffers, the warning was printed elsewhere */ 821803d3b9aSclaudio if (der == NULL) 8225ff8d7beSclaudio return NULL; 8239a7e9e7fSjob 8242b470c61Stb if ((cert = calloc(1, sizeof(struct cert))) == NULL) 825ab4d4874Sbenno err(1, NULL); 8269a7e9e7fSjob 827797cceeeStb oder = der; 8289551aa73Sclaudio if ((x = d2i_X509(NULL, &der, len)) == NULL) { 8292b470c61Stb warnx("%s: d2i_X509", fn); 8309a7e9e7fSjob goto out; 8319a7e9e7fSjob } 832797cceeeStb if (der != oder + len) { 833797cceeeStb warnx("%s: %td bytes trailing garbage", fn, oder + len - der); 834797cceeeStb goto out; 835797cceeeStb } 8369a7e9e7fSjob 837e891962dStb if (!x509_cache_extensions(x, fn)) 838b2d6bcdcStb goto out; 839b2d6bcdcStb 840138af1ebSjob if (X509_get_version(x) != 2) { 841138af1ebSjob warnx("%s: RFC 6487 4.1: X.509 version must be v3", fn); 842138af1ebSjob goto out; 843138af1ebSjob } 844138af1ebSjob 845073e107aStb if ((nid = X509_get_signature_nid(x)) == NID_undef) { 846073e107aStb warnx("%s: unknown signature type", fn); 8477c9af4b7Sjob goto out; 8487c9af4b7Sjob } 84981a06611Sclaudio if (experimental && nid == NID_ecdsa_with_SHA256) { 850ec1cc732Sjob if (verbose) 8518fcc9cc2Sjob warnx("%s: P-256 support is experimental", fn); 852ec1cc732Sjob } else if (nid != NID_sha256WithRSAEncryption) { 8537c9af4b7Sjob warnx("%s: RFC 7935: wrong signature algorithm %s, want %s", 85478de3577Stb fn, nid2str(nid), LN_sha256WithRSAEncryption); 8557c9af4b7Sjob goto out; 8567c9af4b7Sjob } 8577c9af4b7Sjob 8587a3e7ef3Stb X509_get0_uids(x, &issuer_uid, &subject_uid); 8597a3e7ef3Stb if (issuer_uid != NULL || subject_uid != NULL) { 8604cf8d64cSjob warnx("%s: issuer or subject unique identifiers not allowed", 8614cf8d64cSjob fn); 8624cf8d64cSjob goto out; 8634cf8d64cSjob } 8644cf8d64cSjob 8650466b83fStb if (!cert_check_subject_and_issuer(fn, x)) 8667cc1142dSjob goto out; 8677cc1142dSjob 8689a7e9e7fSjob /* Look for X509v3 extensions. */ 86928d6404fStb if ((extsz = X509_get_ext_count(x)) <= 0) { 87028d6404fStb warnx("%s: certificate without X.509v3 extensions", fn); 87128d6404fStb goto out; 87228d6404fStb } 8739a7e9e7fSjob 87428d6404fStb for (i = 0; i < extsz; i++) { 8759a7e9e7fSjob ext = X509_get_ext(x, i); 8769a7e9e7fSjob assert(ext != NULL); 8779a7e9e7fSjob obj = X509_EXTENSION_get_object(ext); 8789a7e9e7fSjob assert(obj != NULL); 8799a7e9e7fSjob 880de9327feStb switch (nid = OBJ_obj2nid(obj)) { 8819a7e9e7fSjob case NID_sbgp_ipAddrBlock: 882de9327feStb if (ip++ > 0) 88376281e49Stb goto dup; 8842b470c61Stb if (!sbgp_ipaddrblk(fn, cert, ext)) 8852dc0b9b1Stb goto out; 8869a7e9e7fSjob break; 8879a7e9e7fSjob case NID_sbgp_autonomousSysNum: 88876281e49Stb if (as++ > 0) 88976281e49Stb goto dup; 8902b470c61Stb if (!sbgp_assysnum(fn, cert, ext)) 8912dc0b9b1Stb goto out; 8929a7e9e7fSjob break; 8939a7e9e7fSjob case NID_sinfo_access: 89476281e49Stb if (sia++ > 0) 89576281e49Stb goto dup; 896c5305b1dStb /* 897c5305b1dStb * This will fail for BGPsec certs, but they must omit 898c5305b1dStb * this extension anyway (RFC 8209, section 3.1.3.3). 899c5305b1dStb */ 9002b470c61Stb if (!sbgp_sia(fn, cert, ext)) 9012dc0b9b1Stb goto out; 9029a7e9e7fSjob break; 903f9f55a97Stb case NID_certificate_policies: 90476281e49Stb if (cp++ > 0) 90576281e49Stb goto dup; 9062b470c61Stb if (!certificate_policies(fn, cert, ext)) 9072dc0b9b1Stb goto out; 908f9f55a97Stb break; 9099a7e9e7fSjob case NID_crl_distribution_points: 91076281e49Stb if (crldp++ > 0) 91176281e49Stb goto dup; 9129a7e9e7fSjob break; 913ebd55816Sjob case NID_info_access: 91476281e49Stb if (aia++ > 0) 91576281e49Stb goto dup; 916ebd55816Sjob break; 9179a7e9e7fSjob case NID_authority_key_identifier: 91876281e49Stb if (aki++ > 0) 91976281e49Stb goto dup; 9209a7e9e7fSjob break; 9219a7e9e7fSjob case NID_subject_key_identifier: 92276281e49Stb if (ski++ > 0) 92376281e49Stb goto dup; 9249a7e9e7fSjob break; 925fdfddccfSjob case NID_ext_key_usage: 92676281e49Stb if (eku++ > 0) 92776281e49Stb goto dup; 928fdfddccfSjob break; 9296c43a88aSjob case NID_basic_constraints: 93076281e49Stb if (bc++ > 0) 93176281e49Stb goto dup; 9326c43a88aSjob break; 9336c43a88aSjob case NID_key_usage: 93476281e49Stb if (ku++ > 0) 93576281e49Stb goto dup; 9366c43a88aSjob break; 9379a7e9e7fSjob default: 9386c43a88aSjob /* unexpected extensions warrant investigation */ 9396c43a88aSjob { 9409a7e9e7fSjob char objn[64]; 9419a7e9e7fSjob OBJ_obj2txt(objn, sizeof(objn), obj, 0); 9429a7e9e7fSjob warnx("%s: ignoring %s (NID %d)", 9432b470c61Stb fn, objn, OBJ_obj2nid(obj)); 9446c43a88aSjob } 9459a7e9e7fSjob break; 9469a7e9e7fSjob } 9479a7e9e7fSjob } 9489a7e9e7fSjob 9492b470c61Stb if (!x509_get_aki(x, fn, &cert->aki)) 950f999fe57Sclaudio goto out; 9512b470c61Stb if (!x509_get_ski(x, fn, &cert->ski)) 952f999fe57Sclaudio goto out; 9532b470c61Stb if (!x509_get_aia(x, fn, &cert->aia)) 954f999fe57Sclaudio goto out; 9552b470c61Stb if (!x509_get_crl(x, fn, &cert->crl)) 956f999fe57Sclaudio goto out; 9572b470c61Stb if (!x509_get_notbefore(x, fn, &cert->notbefore)) 958f5999ddfSjob goto out; 9592b470c61Stb if (!x509_get_notafter(x, fn, &cert->notafter)) 96061c641a8Sbeck goto out; 961041d165fSclaudio 9629a7e9e7fSjob /* Validation on required fields. */ 963eb6f3761Stb cert->purpose = x509_get_purpose(x, fn); 9642b470c61Stb switch (cert->purpose) { 965eb6f3761Stb case CERT_PURPOSE_TA: 966eb6f3761Stb /* XXX - caller should indicate if it expects TA or CA cert */ 9676b83d8e3Sjob case CERT_PURPOSE_CA: 968ae36eebeSjob if ((pkey = X509_get0_pubkey(x)) == NULL) { 9692b470c61Stb warnx("%s: X509_get0_pubkey failed", fn); 970ae36eebeSjob goto out; 971ae36eebeSjob } 9722b470c61Stb if (!valid_ca_pkey(fn, pkey)) 973ae36eebeSjob goto out; 974ae36eebeSjob 9752b470c61Stb if (cert->mft == NULL) { 9762b470c61Stb warnx("%s: RFC 6487 section 4.8.8: missing SIA", fn); 9776b83d8e3Sjob goto out; 9786b83d8e3Sjob } 979381ee599Stb if (cert->num_ases == 0 && cert->num_ips == 0) { 9802b470c61Stb warnx("%s: missing IP or AS resources", fn); 9816b83d8e3Sjob goto out; 9826b83d8e3Sjob } 9836b83d8e3Sjob break; 9846b83d8e3Sjob case CERT_PURPOSE_BGPSEC_ROUTER: 9852b470c61Stb cert->pubkey = x509_get_pubkey(x, fn); 9862b470c61Stb if (cert->pubkey == NULL) { 9872b470c61Stb warnx("%s: x509_get_pubkey failed", fn); 9886b83d8e3Sjob goto out; 9896b83d8e3Sjob } 990381ee599Stb if (cert->num_ips > 0) { 9912b470c61Stb warnx("%s: unexpected IP resources in BGPsec cert", fn); 9926b83d8e3Sjob goto out; 9936b83d8e3Sjob } 994381ee599Stb for (j = 0; j < cert->num_ases; j++) { 995381ee599Stb if (cert->ases[j].type == CERT_AS_INHERIT) { 996ef8bdb37Sjob warnx("%s: inherit elements not allowed in EE" 9972b470c61Stb " cert", fn); 998bc1fb2e9Stb goto out; 999bc1fb2e9Stb } 1000bc1fb2e9Stb } 100176281e49Stb if (sia) { 10022dadf524Sclaudio warnx("%s: unexpected SIA extension in BGPsec cert", 10032b470c61Stb fn); 10042dadf524Sclaudio goto out; 10052dadf524Sclaudio } 10066b83d8e3Sjob break; 1007eb6f3761Stb case CERT_PURPOSE_EE: 1008eb6f3761Stb warn("%s: unexpected EE cert", fn); 1009eb6f3761Stb goto out; 10106b83d8e3Sjob default: 10112b470c61Stb warnx("%s: x509_get_purpose failed in %s", fn, __func__); 10126b83d8e3Sjob goto out; 10136b83d8e3Sjob } 10146b83d8e3Sjob 10152b470c61Stb if (cert->ski == NULL) { 10162b470c61Stb warnx("%s: RFC 6487 section 8.4.2: missing SKI", fn); 10179a7e9e7fSjob goto out; 10189a7e9e7fSjob } 10199a7e9e7fSjob 10202b470c61Stb cert->x509 = x; 10212b470c61Stb return cert; 10229a7e9e7fSjob 102376281e49Stb dup: 102478de3577Stb warnx("%s: RFC 5280 section 4.2: duplicate extension: %s", fn, 102578de3577Stb nid2str(nid)); 10269a7e9e7fSjob out: 10272b470c61Stb cert_free(cert); 10289a7e9e7fSjob X509_free(x); 10292dc0b9b1Stb return NULL; 10309a7e9e7fSjob } 10319a7e9e7fSjob 10329a7e9e7fSjob struct cert * 1033ba153bd8Sclaudio cert_parse(const char *fn, struct cert *p) 10349a7e9e7fSjob { 1035ad462a11Sclaudio if (p == NULL) 1036ad462a11Sclaudio return NULL; 1037ad462a11Sclaudio 10380a039610Sclaudio if (p->aki == NULL) { 10390a039610Sclaudio warnx("%s: RFC 6487 section 8.4.2: " 10400a039610Sclaudio "non-trust anchor missing AKI", fn); 10410a039610Sclaudio goto badcert; 10420a039610Sclaudio } 10430a039610Sclaudio if (strcmp(p->aki, p->ski) == 0) { 10440a039610Sclaudio warnx("%s: RFC 6487 section 8.4.2: " 10450a039610Sclaudio "non-trust anchor AKI may not match SKI", fn); 10460a039610Sclaudio goto badcert; 10470a039610Sclaudio } 10480a039610Sclaudio if (p->aia == NULL) { 1049f999fe57Sclaudio warnx("%s: RFC 6487 section 8.4.7: AIA: extension missing", fn); 1050f999fe57Sclaudio goto badcert; 1051f999fe57Sclaudio } 1052f999fe57Sclaudio if (p->crl == NULL) { 1053f999fe57Sclaudio warnx("%s: RFC 6487 section 4.8.6: CRL: " 1054f999fe57Sclaudio "no CRL distribution point extension", fn); 10550a039610Sclaudio goto badcert; 10560a039610Sclaudio } 10570a039610Sclaudio return p; 10580a039610Sclaudio 10590a039610Sclaudio badcert: 10600a039610Sclaudio cert_free(p); 10610a039610Sclaudio return NULL; 10629a7e9e7fSjob } 10639a7e9e7fSjob 1064e489b848Sjob /* 1065e489b848Sjob * Reject TA certificates with an overly long validity period. 1066e489b848Sjob * 1067e489b848Sjob * The schedule is as follows: 1068e489b848Sjob * Before February 2nd, 2026, warn on TA certs valid for longer than 15 years. 1069e489b848Sjob * After February 2nd, 2026, reject TA certs valid for longer than 15 years. 1070e489b848Sjob * Before March 3rd, 2027, warn on TA certs valid for longer than 3 years. 1071e489b848Sjob * After March 3rd, 2027, reject TA certs valid for longer than 3 years. 1072e489b848Sjob * 1073e489b848Sjob * Return 1 if the validity period is acceptable and 0 otherwise. 1074e489b848Sjob */ 1075e489b848Sjob static int 1076e489b848Sjob ta_check_validity(const char *fn, const struct cert *p, time_t now) 1077e489b848Sjob { 1078e489b848Sjob time_t validity = p->notafter - p->notbefore; 1079e489b848Sjob time_t cutoff_15y = 1769990400; /* 2026-02-02T00:00:00Z */ 1080e489b848Sjob time_t cutoff_3y = 1804032000; /* 2027-03-03T00:00:00Z */ 1081e489b848Sjob time_t cutoff = cutoff_3y; 1082e489b848Sjob int warn_years = 3; 1083e489b848Sjob int exceeds_15y = 0, exceeds_3y = 0; 1084e489b848Sjob int complain = 0, acceptable = 1; 1085e489b848Sjob 1086e489b848Sjob if (validity >= 15 * 365 * 86400) 1087e489b848Sjob exceeds_15y = 1; 1088e489b848Sjob if (validity >= 3 * 365 * 86400) 1089e489b848Sjob exceeds_3y = 1; 1090e489b848Sjob 1091e489b848Sjob if (now < cutoff_15y) { 1092e489b848Sjob warn_years = 15; 1093e489b848Sjob cutoff = cutoff_15y; 1094e489b848Sjob if (exceeds_15y) 1095e489b848Sjob complain = 1; 1096e489b848Sjob } else if (now < cutoff_3y) { 1097e489b848Sjob if (exceeds_15y) 1098e489b848Sjob acceptable = 0; 1099e489b848Sjob if (exceeds_3y) 1100e489b848Sjob complain = 1; 1101e489b848Sjob } else if (exceeds_3y) { 1102e489b848Sjob acceptable = 0; 1103e489b848Sjob complain = 1; 1104e489b848Sjob } 1105e489b848Sjob 1106e489b848Sjob /* 1107*a56972ebStb * Suppress warnings for previously fetched TA certs. 1108e489b848Sjob */ 1109*a56972ebStb if (verbose == 0 && strncmp(fn, "ta/", strlen("ta/")) == 0) 1110e489b848Sjob goto out; 1111e489b848Sjob 1112e489b848Sjob if (!acceptable) { 1113e489b848Sjob warnx("%s: TA cert rejected: validity period exceeds %d years. " 1114e489b848Sjob "Ask the TA operator to reissue their TA cert with a " 1115e489b848Sjob "shorter validity period.", fn, warn_years); 1116e489b848Sjob goto out; 1117e489b848Sjob } 1118e489b848Sjob 1119e489b848Sjob if (complain) { 1120e489b848Sjob warnx("%s: TA validity period exceeds %d years. After %s this " 1121e489b848Sjob "certificate will be rejected.", fn, warn_years, 1122e489b848Sjob time2str(cutoff)); 1123e489b848Sjob goto out; 1124e489b848Sjob } 1125e489b848Sjob 1126e489b848Sjob out: 1127e489b848Sjob return acceptable; 1128e489b848Sjob } 1129e489b848Sjob 11309a7e9e7fSjob struct cert * 1131ba153bd8Sclaudio ta_parse(const char *fn, struct cert *p, const unsigned char *pkey, 1132ba153bd8Sclaudio size_t pkeysz) 11339a7e9e7fSjob { 1134ba153bd8Sclaudio EVP_PKEY *pk, *opk; 1135e9d2d4cdSjob time_t now = get_current_time(); 11369a7e9e7fSjob 1137ad462a11Sclaudio if (p == NULL) 1138ad462a11Sclaudio return NULL; 1139ad462a11Sclaudio 11404fc676bcSclaudio /* first check pubkey against the one from the TAL */ 11419a7e9e7fSjob pk = d2i_PUBKEY(NULL, &pkey, pkeysz); 11424fc676bcSclaudio if (pk == NULL) { 1143c0528901Stb warnx("%s: RFC 6487 (trust anchor): bad TAL pubkey", fn); 11444fc676bcSclaudio goto badcert; 11454fc676bcSclaudio } 11464fc676bcSclaudio if ((opk = X509_get0_pubkey(p->x509)) == NULL) { 1147c0528901Stb warnx("%s: RFC 6487 (trust anchor): missing pubkey", fn); 11484fc676bcSclaudio goto badcert; 1149ba153bd8Sclaudio } 1150ba153bd8Sclaudio if (EVP_PKEY_cmp(pk, opk) != 1) { 1151c0528901Stb warnx("%s: RFC 6487 (trust anchor): " 11529a7e9e7fSjob "pubkey does not match TAL pubkey", fn); 11534fc676bcSclaudio goto badcert; 1154f929d3afStobhe } 1155e489b848Sjob 115681fe2f6dStb if (p->notbefore > now) { 11574fc676bcSclaudio warnx("%s: certificate not yet valid", fn); 11584fc676bcSclaudio goto badcert; 11594fc676bcSclaudio } 116081fe2f6dStb if (p->notafter < now) { 11614fc676bcSclaudio warnx("%s: certificate has expired", fn); 11624fc676bcSclaudio goto badcert; 11634fc676bcSclaudio } 1164e489b848Sjob if (!ta_check_validity(fn, p, now)) 1165e489b848Sjob goto badcert; 1166e489b848Sjob 11670a039610Sclaudio if (p->aki != NULL && strcmp(p->aki, p->ski)) { 11684fdd5861Stb warnx("%s: RFC 6487 section 4.8.3: " 11690a039610Sclaudio "trust anchor AKI, if specified, must match SKI", fn); 11700a039610Sclaudio goto badcert; 11710a039610Sclaudio } 11720a039610Sclaudio if (p->aia != NULL) { 11734fdd5861Stb warnx("%s: RFC 6487 section 4.8.7: " 11740a039610Sclaudio "trust anchor must not have AIA", fn); 11750a039610Sclaudio goto badcert; 11760a039610Sclaudio } 11770a039610Sclaudio if (p->crl != NULL) { 11784fdd5861Stb warnx("%s: RFC 6487 section 4.8.6: " 11790a039610Sclaudio "trust anchor may not specify CRL resource", fn); 11800a039610Sclaudio goto badcert; 11810a039610Sclaudio } 1182a5826434Stb if (p->purpose != CERT_PURPOSE_TA) { 1183a5826434Stb warnx("%s: expected trust anchor purpose, got %s", fn, 1184a5826434Stb purpose2str(p->purpose)); 11850a039610Sclaudio goto badcert; 11860a039610Sclaudio } 118760aa04efStb /* 118860aa04efStb * Do not replace with a <= 0 check since OpenSSL 3 broke that: 118960aa04efStb * https://github.com/openssl/openssl/issues/24575 119060aa04efStb */ 1191b6a9c519Stb if (X509_verify(p->x509, pk) != 1) { 1192b6a9c519Stb warnx("%s: failed to verify signature", fn); 1193b6a9c519Stb goto badcert; 1194b6a9c519Stb } 1195c9e39c95Sjob if (x509_any_inherits(p->x509)) { 1196c9e39c95Sjob warnx("%s: Trust anchor IP/AS resources may not inherit", fn); 1197c9e39c95Sjob goto badcert; 1198c9e39c95Sjob } 11994fc676bcSclaudio 12000a039610Sclaudio EVP_PKEY_free(pk); 12010a039610Sclaudio return p; 12024fc676bcSclaudio 12034fc676bcSclaudio badcert: 12044fc676bcSclaudio EVP_PKEY_free(pk); 12059a7e9e7fSjob cert_free(p); 12060a039610Sclaudio return NULL; 12079a7e9e7fSjob } 12089a7e9e7fSjob 12099a7e9e7fSjob /* 12109a7e9e7fSjob * Free parsed certificate contents. 12119a7e9e7fSjob * Passing NULL is a noop. 12129a7e9e7fSjob */ 12139a7e9e7fSjob void 12149a7e9e7fSjob cert_free(struct cert *p) 12159a7e9e7fSjob { 12167ea57ab8Sderaadt if (p == NULL) 12179a7e9e7fSjob return; 12189a7e9e7fSjob 12199a7e9e7fSjob free(p->crl); 122084c20e47Sclaudio free(p->repo); 12219a7e9e7fSjob free(p->mft); 12223d81c3dfSclaudio free(p->notify); 12239a7e9e7fSjob free(p->ips); 1224381ee599Stb free(p->ases); 1225ebd55816Sjob free(p->aia); 12269a7e9e7fSjob free(p->aki); 12279a7e9e7fSjob free(p->ski); 122817304ed1Sjob free(p->pubkey); 122973072160Sbenno X509_free(p->x509); 12309a7e9e7fSjob free(p); 12319a7e9e7fSjob } 12329a7e9e7fSjob 12339a7e9e7fSjob /* 12349a7e9e7fSjob * Write certificate parsed content into buffer. 12359a7e9e7fSjob * See cert_read() for the other side of the pipe. 12369a7e9e7fSjob */ 12379a7e9e7fSjob void 123808db1177Sclaudio cert_buffer(struct ibuf *b, const struct cert *p) 12399a7e9e7fSjob { 12409f544822Sjob io_simple_buffer(b, &p->notafter, sizeof(p->notafter)); 1241dc508150Sclaudio io_simple_buffer(b, &p->purpose, sizeof(p->purpose)); 1242dc508150Sclaudio io_simple_buffer(b, &p->talid, sizeof(p->talid)); 12430bc420b9Sclaudio io_simple_buffer(b, &p->certid, sizeof(p->certid)); 1244c94cf448Sclaudio io_simple_buffer(b, &p->repoid, sizeof(p->repoid)); 1245381ee599Stb io_simple_buffer(b, &p->num_ips, sizeof(p->num_ips)); 1246381ee599Stb io_simple_buffer(b, &p->num_ases, sizeof(p->num_ases)); 12474ef6f693Sclaudio 1248381ee599Stb io_simple_buffer(b, p->ips, p->num_ips * sizeof(p->ips[0])); 1249381ee599Stb io_simple_buffer(b, p->ases, p->num_ases * sizeof(p->ases[0])); 12504ef6f693Sclaudio 125108db1177Sclaudio io_str_buffer(b, p->mft); 125208db1177Sclaudio io_str_buffer(b, p->notify); 125384c20e47Sclaudio io_str_buffer(b, p->repo); 125408db1177Sclaudio io_str_buffer(b, p->crl); 1255ebd55816Sjob io_str_buffer(b, p->aia); 125608db1177Sclaudio io_str_buffer(b, p->aki); 125708db1177Sclaudio io_str_buffer(b, p->ski); 125817304ed1Sjob io_str_buffer(b, p->pubkey); 12599a7e9e7fSjob } 12609a7e9e7fSjob 12619a7e9e7fSjob /* 12629a7e9e7fSjob * Allocate and read parsed certificate content from descriptor. 12639a7e9e7fSjob * The pointer must be freed with cert_free(). 12649a7e9e7fSjob * Always returns a valid pointer. 12659a7e9e7fSjob */ 12669a7e9e7fSjob struct cert * 12677eb79a4aSclaudio cert_read(struct ibuf *b) 12689a7e9e7fSjob { 12699a7e9e7fSjob struct cert *p; 12709a7e9e7fSjob 12719a7e9e7fSjob if ((p = calloc(1, sizeof(struct cert))) == NULL) 1272ab4d4874Sbenno err(1, NULL); 12739a7e9e7fSjob 12749f544822Sjob io_read_buf(b, &p->notafter, sizeof(p->notafter)); 1275dc508150Sclaudio io_read_buf(b, &p->purpose, sizeof(p->purpose)); 1276dc508150Sclaudio io_read_buf(b, &p->talid, sizeof(p->talid)); 12770bc420b9Sclaudio io_read_buf(b, &p->certid, sizeof(p->certid)); 1278c94cf448Sclaudio io_read_buf(b, &p->repoid, sizeof(p->repoid)); 1279381ee599Stb io_read_buf(b, &p->num_ips, sizeof(p->num_ips)); 1280381ee599Stb io_read_buf(b, &p->num_ases, sizeof(p->num_ases)); 12817eb79a4aSclaudio 1282381ee599Stb if (p->num_ips > 0) { 1283381ee599Stb if ((p->ips = calloc(p->num_ips, sizeof(p->ips[0]))) == NULL) 1284ab4d4874Sbenno err(1, NULL); 1285381ee599Stb io_read_buf(b, p->ips, p->num_ips * sizeof(p->ips[0])); 1286f814cda1Stb } 12879a7e9e7fSjob 1288381ee599Stb if (p->num_ases > 0) { 1289381ee599Stb if ((p->ases = calloc(p->num_ases, sizeof(p->ases[0]))) == NULL) 1290ab4d4874Sbenno err(1, NULL); 1291381ee599Stb io_read_buf(b, p->ases, p->num_ases * sizeof(p->ases[0])); 1292f814cda1Stb } 12939a7e9e7fSjob 12947eb79a4aSclaudio io_read_str(b, &p->mft); 12957eb79a4aSclaudio io_read_str(b, &p->notify); 12967eb79a4aSclaudio io_read_str(b, &p->repo); 12977eb79a4aSclaudio io_read_str(b, &p->crl); 12987eb79a4aSclaudio io_read_str(b, &p->aia); 12997eb79a4aSclaudio io_read_str(b, &p->aki); 13007eb79a4aSclaudio io_read_str(b, &p->ski); 13017eb79a4aSclaudio io_read_str(b, &p->pubkey); 13027eb79a4aSclaudio 1303fdfddccfSjob assert(p->mft != NULL || p->purpose == CERT_PURPOSE_BGPSEC_ROUTER); 130452c8fec2Sclaudio assert(p->ski); 13059a7e9e7fSjob return p; 13069a7e9e7fSjob } 13079a7e9e7fSjob 1308c4a9443cSclaudio static inline int 1309c4a9443cSclaudio authcmp(struct auth *a, struct auth *b) 1310c4a9443cSclaudio { 13110bc420b9Sclaudio if (a->cert->certid > b->cert->certid) 13120bc420b9Sclaudio return 1; 13130bc420b9Sclaudio if (a->cert->certid < b->cert->certid) 13140bc420b9Sclaudio return -1; 13150bc420b9Sclaudio return 0; 1316c4a9443cSclaudio } 1317c4a9443cSclaudio 1318c4a9443cSclaudio RB_GENERATE_STATIC(auth_tree, auth, entry, authcmp); 1319c4a9443cSclaudio 132091176c18Sjob void 132191176c18Sjob auth_tree_free(struct auth_tree *auths) 132291176c18Sjob { 132391176c18Sjob struct auth *auth, *tauth; 132491176c18Sjob 132591176c18Sjob RB_FOREACH_SAFE(auth, auth_tree, auths, tauth) { 132691176c18Sjob RB_REMOVE(auth_tree, auths, auth); 132791176c18Sjob cert_free(auth->cert); 132891176c18Sjob free(auth); 132991176c18Sjob } 133091176c18Sjob } 133191176c18Sjob 1332a079bbf8Sclaudio struct auth * 13330bc420b9Sclaudio auth_find(struct auth_tree *auths, int id) 1334a079bbf8Sclaudio { 1335a079bbf8Sclaudio struct auth a; 1336a079bbf8Sclaudio struct cert c; 1337a079bbf8Sclaudio 13380bc420b9Sclaudio c.certid = id; 1339a079bbf8Sclaudio a.cert = &c; 1340a079bbf8Sclaudio 1341a079bbf8Sclaudio return RB_FIND(auth_tree, auths, &a); 1342a079bbf8Sclaudio } 1343a079bbf8Sclaudio 1344ad462a11Sclaudio struct auth * 13450bc420b9Sclaudio auth_insert(const char *fn, struct auth_tree *auths, struct cert *cert, 13460bc420b9Sclaudio struct auth *issuer) 1347198a0520Sclaudio { 1348198a0520Sclaudio struct auth *na; 1349198a0520Sclaudio 13500bc420b9Sclaudio na = calloc(1, sizeof(*na)); 1351198a0520Sclaudio if (na == NULL) 1352198a0520Sclaudio err(1, NULL); 1353198a0520Sclaudio 13540bc420b9Sclaudio if (issuer == NULL) { 13550bc420b9Sclaudio cert->certid = cert->talid; 13560bc420b9Sclaudio } else { 13570bc420b9Sclaudio cert->certid = ++certid; 13581d8c6443Stb if (certid > CERTID_MAX) { 13591d8c6443Stb if (certid == CERTID_MAX + 1) 13601d8c6443Stb warnx("%s: too many certificates in store", fn); 13611d8c6443Stb free(na); 13621d8c6443Stb return NULL; 13631d8c6443Stb } 13640bc420b9Sclaudio na->depth = issuer->depth + 1; 13650bc420b9Sclaudio } 13660bc420b9Sclaudio 13670bc420b9Sclaudio if (na->depth >= MAX_CERT_DEPTH) { 13680bc420b9Sclaudio warnx("%s: maximum certificate chain depth exhausted", fn); 13690bc420b9Sclaudio free(na); 13700bc420b9Sclaudio return NULL; 13710bc420b9Sclaudio } 13720bc420b9Sclaudio 1373335482abStb na->issuer = issuer; 1374198a0520Sclaudio na->cert = cert; 1375967224c8Stb na->any_inherits = x509_any_inherits(cert->x509); 1376198a0520Sclaudio 1377198a0520Sclaudio if (RB_INSERT(auth_tree, auths, na) != NULL) 13780bc420b9Sclaudio errx(1, "auth tree corrupted"); 1379ad462a11Sclaudio 1380ad462a11Sclaudio return na; 1381198a0520Sclaudio } 1382198a0520Sclaudio 13836b83d8e3Sjob static void 13846b83d8e3Sjob insert_brk(struct brk_tree *tree, struct cert *cert, int asid) 13856b83d8e3Sjob { 13866b83d8e3Sjob struct brk *b, *found; 13876b83d8e3Sjob 13886b83d8e3Sjob if ((b = calloc(1, sizeof(*b))) == NULL) 13896b83d8e3Sjob err(1, NULL); 13906b83d8e3Sjob 13916b83d8e3Sjob b->asid = asid; 13929f544822Sjob b->expires = cert->notafter; 1393dc508150Sclaudio b->talid = cert->talid; 139417304ed1Sjob if ((b->ski = strdup(cert->ski)) == NULL) 139517304ed1Sjob err(1, NULL); 139617304ed1Sjob if ((b->pubkey = strdup(cert->pubkey)) == NULL) 139717304ed1Sjob err(1, NULL); 13986b83d8e3Sjob 13996b83d8e3Sjob /* 14006b83d8e3Sjob * Check if a similar BRK already exists in the tree. If the found BRK 14016b83d8e3Sjob * expires sooner, update it to this BRK's later expiry moment. 14026b83d8e3Sjob */ 14036b83d8e3Sjob if ((found = RB_INSERT(brk_tree, tree, b)) != NULL) { 14046b83d8e3Sjob if (found->expires < b->expires) { 14056b83d8e3Sjob found->expires = b->expires; 1406dc508150Sclaudio found->talid = b->talid; 14076b83d8e3Sjob } 140817304ed1Sjob free(b->ski); 140917304ed1Sjob free(b->pubkey); 14106b83d8e3Sjob free(b); 14116b83d8e3Sjob } 14126b83d8e3Sjob } 14136b83d8e3Sjob 14146b83d8e3Sjob /* 14156b83d8e3Sjob * Add each BGPsec Router Key into the BRK tree. 14166b83d8e3Sjob */ 14176b83d8e3Sjob void 14186b83d8e3Sjob cert_insert_brks(struct brk_tree *tree, struct cert *cert) 14196b83d8e3Sjob { 14206b83d8e3Sjob size_t i, asid; 14216b83d8e3Sjob 1422381ee599Stb for (i = 0; i < cert->num_ases; i++) { 1423381ee599Stb switch (cert->ases[i].type) { 14246b83d8e3Sjob case CERT_AS_ID: 1425381ee599Stb insert_brk(tree, cert, cert->ases[i].id); 14266b83d8e3Sjob break; 14276b83d8e3Sjob case CERT_AS_RANGE: 1428381ee599Stb for (asid = cert->ases[i].range.min; 1429381ee599Stb asid <= cert->ases[i].range.max; asid++) 14306b83d8e3Sjob insert_brk(tree, cert, asid); 14316b83d8e3Sjob break; 14326b83d8e3Sjob default: 14336b83d8e3Sjob warnx("invalid AS identifier type"); 14346b83d8e3Sjob continue; 14356b83d8e3Sjob } 14366b83d8e3Sjob } 14376b83d8e3Sjob } 14386b83d8e3Sjob 14396b83d8e3Sjob static inline int 14406b83d8e3Sjob brkcmp(struct brk *a, struct brk *b) 14416b83d8e3Sjob { 144217304ed1Sjob int rv; 144317304ed1Sjob 14446b83d8e3Sjob if (a->asid > b->asid) 14456b83d8e3Sjob return 1; 14466b83d8e3Sjob if (a->asid < b->asid) 14476b83d8e3Sjob return -1; 14486b83d8e3Sjob 144917304ed1Sjob rv = strcmp(a->ski, b->ski); 145017304ed1Sjob if (rv > 0) 145117304ed1Sjob return 1; 145217304ed1Sjob if (rv < 0) 145317304ed1Sjob return -1; 145417304ed1Sjob 145517304ed1Sjob return strcmp(a->pubkey, b->pubkey); 14566b83d8e3Sjob } 14576b83d8e3Sjob 14586b83d8e3Sjob RB_GENERATE(brk_tree, brk, entry, brkcmp); 1459