2024-02-17 22:37:04 +01:00
|
|
|
/* $OpenBSD: cert.c,v 1.127 2024/02/16 14:48:47 tb Exp $ */
|
2023-04-30 03:15:27 +02:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
|
|
|
|
* Copyright (c) 2021 Job Snijders <job@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 <assert.h>
|
|
|
|
#include <err.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include <openssl/asn1.h>
|
|
|
|
#include <openssl/x509.h>
|
|
|
|
#include <openssl/x509v3.h>
|
|
|
|
|
|
|
|
#include "extern.h"
|
|
|
|
|
|
|
|
extern ASN1_OBJECT *certpol_oid; /* id-cp-ipAddr-asNumber cert policy */
|
|
|
|
extern ASN1_OBJECT *carepo_oid; /* 1.3.6.1.5.5.7.48.5 (caRepository) */
|
|
|
|
extern ASN1_OBJECT *manifest_oid; /* 1.3.6.1.5.5.7.48.10 (rpkiManifest) */
|
|
|
|
extern ASN1_OBJECT *notify_oid; /* 1.3.6.1.5.5.7.48.13 (rpkiNotify) */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Append an IP address structure to our list of results.
|
|
|
|
* This will also constrain us to having at most one inheritance
|
|
|
|
* statement per AFI and also not have overlapping ranges (as prohibited
|
|
|
|
* in section 2.2.3.6).
|
|
|
|
* It does not make sure that ranges can't coalesce, that is, that any
|
|
|
|
* two ranges abut each other.
|
|
|
|
* This is warned against in section 2.2.3.6, but doesn't change the
|
|
|
|
* semantics of the system.
|
|
|
|
* Returns zero on failure (IP overlap) non-zero on success.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
append_ip(const char *fn, struct cert_ip *ips, size_t *ipsz,
|
|
|
|
const struct cert_ip *ip)
|
|
|
|
{
|
2023-10-13 21:11:38 +02:00
|
|
|
if (!ip_addr_check_overlap(ip, fn, ips, *ipsz, 0))
|
2023-04-30 03:15:27 +02:00
|
|
|
return 0;
|
|
|
|
ips[(*ipsz)++] = *ip;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Append an AS identifier structure to our list of results.
|
|
|
|
* Makes sure that the identifiers do not overlap or improperly inherit
|
|
|
|
* as defined by RFC 3779 section 3.3.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
append_as(const char *fn, struct cert_as *ases, size_t *asz,
|
|
|
|
const struct cert_as *as)
|
|
|
|
{
|
2023-10-13 21:11:38 +02:00
|
|
|
if (!as_check_overlap(as, fn, ases, *asz, 0))
|
2023-04-30 03:15:27 +02:00
|
|
|
return 0;
|
|
|
|
ases[(*asz)++] = *as;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse a range of AS identifiers as in 3.2.3.8.
|
|
|
|
* Returns zero on failure, non-zero on success.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sbgp_as_range(const char *fn, struct cert_as *ases, size_t *asz,
|
|
|
|
const ASRange *range)
|
|
|
|
{
|
|
|
|
struct cert_as as;
|
|
|
|
|
|
|
|
memset(&as, 0, sizeof(struct cert_as));
|
|
|
|
as.type = CERT_AS_RANGE;
|
|
|
|
|
|
|
|
if (!as_id_parse(range->min, &as.range.min)) {
|
|
|
|
warnx("%s: RFC 3779 section 3.2.3.8 (via RFC 1930): "
|
|
|
|
"malformed AS identifier", fn);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!as_id_parse(range->max, &as.range.max)) {
|
|
|
|
warnx("%s: RFC 3779 section 3.2.3.8 (via RFC 1930): "
|
|
|
|
"malformed AS identifier", fn);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (as.range.max == as.range.min) {
|
|
|
|
warnx("%s: RFC 3379 section 3.2.3.8: ASRange: "
|
|
|
|
"range is singular", fn);
|
|
|
|
return 0;
|
|
|
|
} else if (as.range.max < as.range.min) {
|
|
|
|
warnx("%s: RFC 3379 section 3.2.3.8: ASRange: "
|
|
|
|
"range is out of order", fn);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return append_as(fn, ases, asz, &as);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse an entire 3.2.3.10 integer type.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sbgp_as_id(const char *fn, struct cert_as *ases, size_t *asz,
|
|
|
|
const ASN1_INTEGER *i)
|
|
|
|
{
|
|
|
|
struct cert_as as;
|
|
|
|
|
|
|
|
memset(&as, 0, sizeof(struct cert_as));
|
|
|
|
as.type = CERT_AS_ID;
|
|
|
|
|
|
|
|
if (!as_id_parse(i, &as.id)) {
|
|
|
|
warnx("%s: RFC 3779 section 3.2.3.10 (via RFC 1930): "
|
|
|
|
"malformed AS identifier", fn);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (as.id == 0) {
|
|
|
|
warnx("%s: RFC 3779 section 3.2.3.10 (via RFC 1930): "
|
|
|
|
"AS identifier zero is reserved", fn);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return append_as(fn, ases, asz, &as);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
sbgp_as_inherit(const char *fn, struct cert_as *ases, size_t *asz)
|
|
|
|
{
|
|
|
|
struct cert_as as;
|
|
|
|
|
|
|
|
memset(&as, 0, sizeof(struct cert_as));
|
|
|
|
as.type = CERT_AS_INHERIT;
|
|
|
|
|
|
|
|
return append_as(fn, ases, asz, &as);
|
|
|
|
}
|
|
|
|
|
2023-09-28 10:40:30 +02:00
|
|
|
int
|
|
|
|
sbgp_parse_assysnum(const char *fn, const ASIdentifiers *asidentifiers,
|
|
|
|
struct cert_as **out_as, size_t *out_asz)
|
2023-04-30 03:15:27 +02:00
|
|
|
{
|
|
|
|
const ASIdOrRanges *aors = NULL;
|
2023-09-28 10:40:30 +02:00
|
|
|
struct cert_as *as = NULL;
|
|
|
|
size_t asz = 0, sz;
|
|
|
|
int i;
|
2023-04-30 03:15:27 +02:00
|
|
|
|
2023-09-28 10:40:30 +02:00
|
|
|
assert(*out_as == NULL && *out_asz == 0);
|
2023-04-30 03:15:27 +02:00
|
|
|
|
|
|
|
if (asidentifiers->rdi != NULL) {
|
|
|
|
warnx("%s: RFC 6487 section 4.8.11: autonomousSysNum: "
|
2023-09-28 10:40:30 +02:00
|
|
|
"should not have RDI values", fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asidentifiers->asnum == NULL) {
|
|
|
|
warnx("%s: RFC 6487 section 4.8.11: autonomousSysNum: "
|
2023-09-28 10:40:30 +02:00
|
|
|
"no AS number resource set", fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (asidentifiers->asnum->type) {
|
|
|
|
case ASIdentifierChoice_inherit:
|
2023-09-28 10:40:30 +02:00
|
|
|
sz = 1;
|
2023-04-30 03:15:27 +02:00
|
|
|
break;
|
|
|
|
case ASIdentifierChoice_asIdsOrRanges:
|
|
|
|
aors = asidentifiers->asnum->u.asIdsOrRanges;
|
2023-09-28 10:40:30 +02:00
|
|
|
sz = sk_ASIdOrRange_num(aors);
|
2023-04-30 03:15:27 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
warnx("%s: RFC 3779 section 3.2.3.2: ASIdentifierChoice: "
|
2023-09-28 10:40:30 +02:00
|
|
|
"unknown type %d", fn, asidentifiers->asnum->type);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2023-09-28 10:40:30 +02:00
|
|
|
if (sz == 0) {
|
|
|
|
warnx("%s: RFC 6487 section 4.8.11: empty asIdsOrRanges", fn);
|
2023-06-24 04:57:55 +02:00
|
|
|
goto out;
|
|
|
|
}
|
2023-09-28 10:40:30 +02:00
|
|
|
if (sz >= MAX_AS_SIZE) {
|
2023-04-30 03:15:27 +02:00
|
|
|
warnx("%s: too many AS number entries: limit %d",
|
2023-09-28 10:40:30 +02:00
|
|
|
fn, MAX_AS_SIZE);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
2023-09-28 10:40:30 +02:00
|
|
|
as = calloc(sz, sizeof(struct cert_as));
|
|
|
|
if (as == NULL)
|
2023-04-30 03:15:27 +02:00
|
|
|
err(1, NULL);
|
|
|
|
|
|
|
|
if (aors == NULL) {
|
2023-09-28 10:40:30 +02:00
|
|
|
if (!sbgp_as_inherit(fn, as, &asz))
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < sk_ASIdOrRange_num(aors); i++) {
|
|
|
|
const ASIdOrRange *aor;
|
|
|
|
|
|
|
|
aor = sk_ASIdOrRange_value(aors, i);
|
|
|
|
switch (aor->type) {
|
|
|
|
case ASIdOrRange_id:
|
2023-09-28 10:40:30 +02:00
|
|
|
if (!sbgp_as_id(fn, as, &asz, aor->u.id))
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
break;
|
|
|
|
case ASIdOrRange_range:
|
2023-09-28 10:40:30 +02:00
|
|
|
if (!sbgp_as_range(fn, as, &asz, aor->u.range))
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
warnx("%s: RFC 3779 section 3.2.3.5: ASIdOrRange: "
|
2023-09-28 10:40:30 +02:00
|
|
|
"unknown type %d", fn, aor->type);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-28 10:40:30 +02:00
|
|
|
*out_as = as;
|
|
|
|
*out_asz = asz;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
out:
|
|
|
|
free(as);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse RFC 6487 4.8.11 X509v3 extension, with syntax documented in RFC
|
|
|
|
* 3779 starting in section 3.2.
|
|
|
|
* Returns zero on failure, non-zero on success.
|
|
|
|
*/
|
|
|
|
static int
|
2024-02-17 22:37:04 +01:00
|
|
|
sbgp_assysnum(const char *fn, struct cert *cert, X509_EXTENSION *ext)
|
2023-09-28 10:40:30 +02:00
|
|
|
{
|
|
|
|
ASIdentifiers *asidentifiers = NULL;
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
if (!X509_EXTENSION_get_critical(ext)) {
|
|
|
|
warnx("%s: RFC 6487 section 4.8.11: autonomousSysNum: "
|
2024-02-17 22:37:04 +01:00
|
|
|
"extension not critical", fn);
|
2023-09-28 10:40:30 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((asidentifiers = X509V3_EXT_d2i(ext)) == NULL) {
|
|
|
|
warnx("%s: RFC 6487 section 4.8.11: autonomousSysNum: "
|
2024-02-17 22:37:04 +01:00
|
|
|
"failed extension parse", fn);
|
2023-09-28 10:40:30 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2024-02-17 22:37:04 +01:00
|
|
|
if (!sbgp_parse_assysnum(fn, asidentifiers, &cert->as, &cert->asz))
|
2023-09-28 10:40:30 +02:00
|
|
|
goto out;
|
|
|
|
|
2023-04-30 03:15:27 +02:00
|
|
|
rc = 1;
|
|
|
|
out:
|
|
|
|
ASIdentifiers_free(asidentifiers);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Construct a RFC 3779 2.2.3.8 range from its bit string.
|
|
|
|
* Returns zero on failure, non-zero on success.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sbgp_addr(const char *fn, struct cert_ip *ips, size_t *ipsz, enum afi afi,
|
|
|
|
const ASN1_BIT_STRING *bs)
|
|
|
|
{
|
|
|
|
struct cert_ip ip;
|
|
|
|
|
|
|
|
memset(&ip, 0, sizeof(struct cert_ip));
|
|
|
|
|
|
|
|
ip.afi = afi;
|
|
|
|
ip.type = CERT_IP_ADDR;
|
|
|
|
|
|
|
|
if (!ip_addr_parse(bs, afi, fn, &ip.ip)) {
|
|
|
|
warnx("%s: RFC 3779 section 2.2.3.8: IPAddress: "
|
|
|
|
"invalid IP address", fn);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ip_cert_compose_ranges(&ip)) {
|
|
|
|
warnx("%s: RFC 3779 section 2.2.3.8: IPAddress: "
|
|
|
|
"IP address range reversed", fn);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return append_ip(fn, ips, ipsz, &ip);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse RFC 3779 2.2.3.9 range of addresses.
|
|
|
|
* Returns zero on failure, non-zero on success.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sbgp_addr_range(const char *fn, struct cert_ip *ips, size_t *ipsz,
|
|
|
|
enum afi afi, const IPAddressRange *range)
|
|
|
|
{
|
|
|
|
struct cert_ip ip;
|
|
|
|
|
|
|
|
memset(&ip, 0, sizeof(struct cert_ip));
|
|
|
|
|
|
|
|
ip.afi = afi;
|
|
|
|
ip.type = CERT_IP_RANGE;
|
|
|
|
|
|
|
|
if (!ip_addr_parse(range->min, afi, fn, &ip.range.min)) {
|
|
|
|
warnx("%s: RFC 3779 section 2.2.3.9: IPAddressRange: "
|
|
|
|
"invalid IP address", fn);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ip_addr_parse(range->max, afi, fn, &ip.range.max)) {
|
|
|
|
warnx("%s: RFC 3779 section 2.2.3.9: IPAddressRange: "
|
|
|
|
"invalid IP address", fn);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ip_cert_compose_ranges(&ip)) {
|
|
|
|
warnx("%s: RFC 3779 section 2.2.3.9: IPAddressRange: "
|
|
|
|
"IP address range reversed", fn);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return append_ip(fn, ips, ipsz, &ip);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
sbgp_addr_inherit(const char *fn, struct cert_ip *ips, size_t *ipsz,
|
|
|
|
enum afi afi)
|
|
|
|
{
|
|
|
|
struct cert_ip ip;
|
|
|
|
|
|
|
|
memset(&ip, 0, sizeof(struct cert_ip));
|
|
|
|
|
|
|
|
ip.afi = afi;
|
|
|
|
ip.type = CERT_IP_INHERIT;
|
|
|
|
|
|
|
|
return append_ip(fn, ips, ipsz, &ip);
|
|
|
|
}
|
|
|
|
|
2023-09-28 10:40:30 +02:00
|
|
|
int
|
|
|
|
sbgp_parse_ipaddrblk(const char *fn, const IPAddrBlocks *addrblk,
|
|
|
|
struct cert_ip **out_ips, size_t *out_ipsz)
|
2023-04-30 03:15:27 +02:00
|
|
|
{
|
2023-09-28 10:40:30 +02:00
|
|
|
const IPAddressFamily *af;
|
|
|
|
const IPAddressOrRanges *aors;
|
|
|
|
const IPAddressOrRange *aor;
|
|
|
|
enum afi afi;
|
|
|
|
struct cert_ip *ips = NULL;
|
|
|
|
size_t ipsz = 0, sz;
|
2023-12-15 05:25:16 +01:00
|
|
|
int ipv4_seen = 0, ipv6_seen = 0;
|
|
|
|
int i, j, ipaddrblocksz;
|
2023-04-30 03:15:27 +02:00
|
|
|
|
2023-09-28 10:40:30 +02:00
|
|
|
assert(*out_ips == NULL && *out_ipsz == 0);
|
2023-04-30 03:15:27 +02:00
|
|
|
|
2023-12-15 05:25:16 +01:00
|
|
|
ipaddrblocksz = sk_IPAddressFamily_num(addrblk);
|
|
|
|
if (ipaddrblocksz != 1 && ipaddrblocksz != 2) {
|
|
|
|
warnx("%s: RFC 6487 section 4.8.10: unexpected number of "
|
|
|
|
"ipAddrBlocks (got %d, expected 1 or 2)",
|
|
|
|
fn, ipaddrblocksz);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < ipaddrblocksz; i++) {
|
2023-04-30 03:15:27 +02:00
|
|
|
af = sk_IPAddressFamily_value(addrblk, i);
|
|
|
|
|
|
|
|
switch (af->ipAddressChoice->type) {
|
|
|
|
case IPAddressChoice_inherit:
|
|
|
|
aors = NULL;
|
2023-09-28 10:40:30 +02:00
|
|
|
sz = ipsz + 1;
|
2023-04-30 03:15:27 +02:00
|
|
|
break;
|
|
|
|
case IPAddressChoice_addressesOrRanges:
|
|
|
|
aors = af->ipAddressChoice->u.addressesOrRanges;
|
2023-09-28 10:40:30 +02:00
|
|
|
sz = ipsz + sk_IPAddressOrRange_num(aors);
|
2023-04-30 03:15:27 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
warnx("%s: RFC 3779: IPAddressChoice: unknown type %d",
|
2023-09-28 10:40:30 +02:00
|
|
|
fn, af->ipAddressChoice->type);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
2023-09-28 10:40:30 +02:00
|
|
|
if (sz == ipsz) {
|
2023-06-24 04:57:55 +02:00
|
|
|
warnx("%s: RFC 6487 section 4.8.10: "
|
2023-09-28 10:40:30 +02:00
|
|
|
"empty ipAddressesOrRanges", fn);
|
2023-06-24 04:57:55 +02:00
|
|
|
goto out;
|
|
|
|
}
|
2023-04-30 03:15:27 +02:00
|
|
|
|
2023-09-28 10:40:30 +02:00
|
|
|
if (sz >= MAX_IP_SIZE)
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
2023-09-28 10:40:30 +02:00
|
|
|
ips = recallocarray(ips, ipsz, sz, sizeof(struct cert_ip));
|
|
|
|
if (ips == NULL)
|
2023-04-30 03:15:27 +02:00
|
|
|
err(1, NULL);
|
|
|
|
|
2023-09-28 10:40:30 +02:00
|
|
|
if (!ip_addr_afi_parse(fn, af->addressFamily, &afi)) {
|
|
|
|
warnx("%s: RFC 3779: invalid AFI", fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2024-02-17 22:37:04 +01:00
|
|
|
switch (afi) {
|
2023-12-15 05:25:16 +01:00
|
|
|
case AFI_IPV4:
|
|
|
|
if (ipv4_seen++ > 0) {
|
|
|
|
warnx("%s: RFC 6487 section 4.8.10: "
|
|
|
|
"IPv4 appears twice", fn);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case AFI_IPV6:
|
|
|
|
if (ipv6_seen++ > 0) {
|
|
|
|
warnx("%s: RFC 6487 section 4.8.10: "
|
|
|
|
"IPv6 appears twice", fn);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-04-30 03:15:27 +02:00
|
|
|
if (aors == NULL) {
|
2023-09-28 10:40:30 +02:00
|
|
|
if (!sbgp_addr_inherit(fn, ips, &ipsz, afi))
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (j = 0; j < sk_IPAddressOrRange_num(aors); j++) {
|
|
|
|
aor = sk_IPAddressOrRange_value(aors, j);
|
|
|
|
switch (aor->type) {
|
|
|
|
case IPAddressOrRange_addressPrefix:
|
2023-09-28 10:40:30 +02:00
|
|
|
if (!sbgp_addr(fn, ips, &ipsz, afi,
|
|
|
|
aor->u.addressPrefix))
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
break;
|
|
|
|
case IPAddressOrRange_addressRange:
|
2023-09-28 10:40:30 +02:00
|
|
|
if (!sbgp_addr_range(fn, ips, &ipsz, afi,
|
|
|
|
aor->u.addressRange))
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
warnx("%s: RFC 3779: IPAddressOrRange: "
|
2023-09-28 10:40:30 +02:00
|
|
|
"unknown type %d", fn, aor->type);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-28 10:40:30 +02:00
|
|
|
*out_ips = ips;
|
|
|
|
*out_ipsz = ipsz;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
out:
|
|
|
|
free(ips);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse an sbgp-ipAddrBlock X509 extension, RFC 6487 4.8.10, with
|
|
|
|
* syntax documented in RFC 3779 starting in section 2.2.
|
|
|
|
* Returns zero on failure, non-zero on success.
|
|
|
|
*/
|
|
|
|
static int
|
2024-02-17 22:37:04 +01:00
|
|
|
sbgp_ipaddrblk(const char *fn, struct cert *cert, X509_EXTENSION *ext)
|
2023-09-28 10:40:30 +02:00
|
|
|
{
|
2023-10-13 21:11:38 +02:00
|
|
|
IPAddrBlocks *addrblk = NULL;
|
|
|
|
int rc = 0;
|
2023-09-28 10:40:30 +02:00
|
|
|
|
|
|
|
if (!X509_EXTENSION_get_critical(ext)) {
|
|
|
|
warnx("%s: RFC 6487 section 4.8.10: sbgp-ipAddrBlock: "
|
2024-02-17 22:37:04 +01:00
|
|
|
"extension not critical", fn);
|
2023-09-28 10:40:30 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((addrblk = X509V3_EXT_d2i(ext)) == NULL) {
|
|
|
|
warnx("%s: RFC 6487 section 4.8.10: sbgp-ipAddrBlock: "
|
2024-02-17 22:37:04 +01:00
|
|
|
"failed extension parse", fn);
|
2023-09-28 10:40:30 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2024-02-17 22:37:04 +01:00
|
|
|
if (!sbgp_parse_ipaddrblk(fn, addrblk, &cert->ips, &cert->ipsz))
|
2023-09-28 10:40:30 +02:00
|
|
|
goto out;
|
|
|
|
|
2024-02-17 22:37:04 +01:00
|
|
|
if (cert->ipsz == 0) {
|
|
|
|
warnx("%s: RFC 6487 section 4.8.10: empty ipAddrBlock", fn);
|
2023-06-24 04:57:55 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2023-04-30 03:15:27 +02:00
|
|
|
rc = 1;
|
|
|
|
out:
|
2023-10-13 21:11:38 +02:00
|
|
|
IPAddrBlocks_free(addrblk);
|
2023-04-30 03:15:27 +02:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse "Subject Information Access" extension, RFC 6487 4.8.8.
|
|
|
|
* Returns zero on failure, non-zero on success.
|
|
|
|
*/
|
|
|
|
static int
|
2024-02-17 22:37:04 +01:00
|
|
|
sbgp_sia(const char *fn, struct cert *cert, X509_EXTENSION *ext)
|
2023-04-30 03:15:27 +02:00
|
|
|
{
|
|
|
|
AUTHORITY_INFO_ACCESS *sia = NULL;
|
|
|
|
ACCESS_DESCRIPTION *ad;
|
|
|
|
ASN1_OBJECT *oid;
|
|
|
|
const char *mftfilename;
|
|
|
|
int i, rc = 0;
|
|
|
|
|
|
|
|
if (X509_EXTENSION_get_critical(ext)) {
|
|
|
|
warnx("%s: RFC 6487 section 4.8.8: SIA: "
|
2024-02-17 22:37:04 +01:00
|
|
|
"extension not non-critical", fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((sia = X509V3_EXT_d2i(ext)) == NULL) {
|
2023-06-29 12:53:26 +02:00
|
|
|
warnx("%s: RFC 6487 section 4.8.8: SIA: failed extension parse",
|
2024-02-17 22:37:04 +01:00
|
|
|
fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < sk_ACCESS_DESCRIPTION_num(sia); i++) {
|
|
|
|
ad = sk_ACCESS_DESCRIPTION_value(sia, i);
|
|
|
|
|
|
|
|
oid = ad->method;
|
|
|
|
|
|
|
|
if (OBJ_cmp(oid, carepo_oid) == 0) {
|
2024-02-17 22:37:04 +01:00
|
|
|
if (!x509_location(fn, "SIA: caRepository",
|
|
|
|
"rsync://", ad->location, &cert->repo))
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
} else if (OBJ_cmp(oid, manifest_oid) == 0) {
|
2024-02-17 22:37:04 +01:00
|
|
|
if (!x509_location(fn, "SIA: rpkiManifest",
|
|
|
|
"rsync://", ad->location, &cert->mft))
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
} else if (OBJ_cmp(oid, notify_oid) == 0) {
|
2024-02-17 22:37:04 +01:00
|
|
|
if (!x509_location(fn, "SIA: rpkiNotify",
|
|
|
|
"https://", ad->location, &cert->notify))
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-17 22:37:04 +01:00
|
|
|
if (cert->mft == NULL || cert->repo == NULL) {
|
2023-04-30 03:15:27 +02:00
|
|
|
warnx("%s: RFC 6487 section 4.8.8: SIA: missing caRepository "
|
2024-02-17 22:37:04 +01:00
|
|
|
"or rpkiManifest", fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2024-02-17 22:37:04 +01:00
|
|
|
mftfilename = strrchr(cert->mft, '/');
|
2023-04-30 03:15:27 +02:00
|
|
|
if (mftfilename == NULL) {
|
2024-02-17 22:37:04 +01:00
|
|
|
warnx("%s: SIA: invalid rpkiManifest entry", fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
mftfilename++;
|
|
|
|
if (!valid_filename(mftfilename, strlen(mftfilename))) {
|
|
|
|
warnx("%s: SIA: rpkiManifest filename contains invalid "
|
2024-02-17 22:37:04 +01:00
|
|
|
"characters", fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2024-02-17 22:37:04 +01:00
|
|
|
if (strstr(cert->mft, cert->repo) != cert->mft) {
|
2023-04-30 03:15:27 +02:00
|
|
|
warnx("%s: RFC 6487 section 4.8.8: SIA: "
|
2024-02-17 22:37:04 +01:00
|
|
|
"conflicting URIs for caRepository and rpkiManifest", fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2024-02-17 22:37:04 +01:00
|
|
|
if (rtype_from_file_extension(cert->mft) != RTYPE_MFT) {
|
|
|
|
warnx("%s: RFC 6487 section 4.8.8: SIA: not an MFT file", fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = 1;
|
|
|
|
out:
|
|
|
|
AUTHORITY_INFO_ACCESS_free(sia);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse the certificate policies extension and check that it follows RFC 7318.
|
|
|
|
* Returns zero on failure, non-zero on success.
|
|
|
|
*/
|
|
|
|
static int
|
2024-02-17 22:37:04 +01:00
|
|
|
certificate_policies(const char *fn, struct cert *cert, X509_EXTENSION *ext)
|
2023-04-30 03:15:27 +02:00
|
|
|
{
|
|
|
|
STACK_OF(POLICYINFO) *policies = NULL;
|
|
|
|
POLICYINFO *policy;
|
|
|
|
STACK_OF(POLICYQUALINFO) *qualifiers;
|
|
|
|
POLICYQUALINFO *qualifier;
|
|
|
|
int nid;
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
if (!X509_EXTENSION_get_critical(ext)) {
|
2023-06-29 12:53:26 +02:00
|
|
|
warnx("%s: RFC 6487 section 4.8.9: certificatePolicies: "
|
2024-02-17 22:37:04 +01:00
|
|
|
"extension not critical", fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((policies = X509V3_EXT_d2i(ext)) == NULL) {
|
2023-06-29 12:53:26 +02:00
|
|
|
warnx("%s: RFC 6487 section 4.8.9: certificatePolicies: "
|
2024-02-17 22:37:04 +01:00
|
|
|
"failed extension parse", fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sk_POLICYINFO_num(policies) != 1) {
|
|
|
|
warnx("%s: RFC 6487 section 4.8.9: certificatePolicies: "
|
2024-02-17 22:37:04 +01:00
|
|
|
"want 1 policy, got %d", fn, sk_POLICYINFO_num(policies));
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
policy = sk_POLICYINFO_value(policies, 0);
|
|
|
|
assert(policy != NULL && policy->policyid != NULL);
|
|
|
|
|
|
|
|
if (OBJ_cmp(policy->policyid, certpol_oid) != 0) {
|
|
|
|
char pbuf[128], cbuf[128];
|
|
|
|
|
|
|
|
OBJ_obj2txt(pbuf, sizeof(pbuf), policy->policyid, 1);
|
|
|
|
OBJ_obj2txt(cbuf, sizeof(cbuf), certpol_oid, 1);
|
|
|
|
warnx("%s: RFC 7318 section 2: certificatePolicies: "
|
2024-02-17 22:37:04 +01:00
|
|
|
"unexpected OID: %s, want %s", fn, pbuf, cbuf);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Policy qualifiers are optional. If they're absent, we're done. */
|
|
|
|
if ((qualifiers = policy->qualifiers) == NULL) {
|
|
|
|
rc = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sk_POLICYQUALINFO_num(qualifiers) != 1) {
|
|
|
|
warnx("%s: RFC 7318 section 2: certificatePolicies: "
|
2024-02-17 22:37:04 +01:00
|
|
|
"want 1 policy qualifier, got %d", fn,
|
2023-04-30 03:15:27 +02:00
|
|
|
sk_POLICYQUALINFO_num(qualifiers));
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
qualifier = sk_POLICYQUALINFO_value(qualifiers, 0);
|
|
|
|
assert(qualifier != NULL && qualifier->pqualid != NULL);
|
|
|
|
|
|
|
|
if ((nid = OBJ_obj2nid(qualifier->pqualid)) != NID_id_qt_cps) {
|
|
|
|
warnx("%s: RFC 7318 section 2: certificatePolicies: "
|
2024-02-17 22:37:04 +01:00
|
|
|
"want CPS, got %s", fn, nid2str(nid));
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (verbose > 1 && !filemode)
|
2024-02-17 22:37:04 +01:00
|
|
|
warnx("%s: CPS %.*s", fn, qualifier->d.cpsuri->length,
|
2023-04-30 03:15:27 +02:00
|
|
|
qualifier->d.cpsuri->data);
|
|
|
|
|
|
|
|
rc = 1;
|
|
|
|
out:
|
|
|
|
sk_POLICYINFO_pop_free(policies, POLICYINFO_free);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2023-09-12 18:38:01 +02:00
|
|
|
* Lightweight version of cert_parse_pre() for EE certs.
|
|
|
|
* Parses the two RFC 3779 extensions, and performs some sanity checks.
|
2023-04-30 03:15:27 +02:00
|
|
|
* Returns cert on success and NULL on failure.
|
|
|
|
*/
|
|
|
|
struct cert *
|
2023-10-13 21:11:38 +02:00
|
|
|
cert_parse_ee_cert(const char *fn, int talid, X509 *x)
|
2023-04-30 03:15:27 +02:00
|
|
|
{
|
2024-02-17 22:37:04 +01:00
|
|
|
struct cert *cert;
|
2023-04-30 03:15:27 +02:00
|
|
|
X509_EXTENSION *ext;
|
|
|
|
int index;
|
|
|
|
|
2024-02-17 22:37:04 +01:00
|
|
|
if ((cert = calloc(1, sizeof(struct cert))) == NULL)
|
2023-04-30 03:15:27 +02:00
|
|
|
err(1, NULL);
|
|
|
|
|
2023-06-20 22:38:03 +02:00
|
|
|
if (X509_get_version(x) != 2) {
|
|
|
|
warnx("%s: RFC 6487 4.1: X.509 version must be v3", fn);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2023-09-12 18:38:01 +02:00
|
|
|
if (!x509_valid_subject(fn, x))
|
|
|
|
goto out;
|
|
|
|
|
2023-04-30 03:15:27 +02:00
|
|
|
if (X509_get_key_usage(x) != KU_DIGITAL_SIGNATURE) {
|
|
|
|
warnx("%s: RFC 6487 section 4.8.4: KU must be digitalSignature",
|
|
|
|
fn);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* EKU may be allowed for some purposes in the future. */
|
|
|
|
if (X509_get_extended_key_usage(x) != UINT32_MAX) {
|
|
|
|
warnx("%s: RFC 6487 section 4.8.5: EKU not allowed", fn);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
index = X509_get_ext_by_NID(x, NID_sbgp_ipAddrBlock, -1);
|
|
|
|
if ((ext = X509_get_ext(x, index)) != NULL) {
|
2024-02-17 22:37:04 +01:00
|
|
|
if (!sbgp_ipaddrblk(fn, cert, ext))
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
index = X509_get_ext_by_NID(x, NID_sbgp_autonomousSysNum, -1);
|
|
|
|
if ((ext = X509_get_ext(x, index)) != NULL) {
|
2024-02-17 22:37:04 +01:00
|
|
|
if (!sbgp_assysnum(fn, cert, ext))
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!X509_up_ref(x)) {
|
2023-06-29 12:53:26 +02:00
|
|
|
warnx("%s: X509_up_ref failed", fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2024-02-17 22:37:04 +01:00
|
|
|
cert->x509 = x;
|
|
|
|
cert->talid = talid;
|
2023-10-13 21:11:38 +02:00
|
|
|
|
2024-02-17 22:37:04 +01:00
|
|
|
if (!constraints_validate(fn, cert))
|
2023-10-13 21:11:38 +02:00
|
|
|
goto out;
|
|
|
|
|
2024-02-17 22:37:04 +01:00
|
|
|
return cert;
|
2023-04-30 03:15:27 +02:00
|
|
|
|
|
|
|
out:
|
2024-02-17 22:37:04 +01:00
|
|
|
cert_free(cert);
|
2023-04-30 03:15:27 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse and partially validate an RPKI X509 certificate (either a trust
|
|
|
|
* anchor or a certificate) as defined in RFC 6487.
|
|
|
|
* Returns the parse results or NULL on failure.
|
|
|
|
*/
|
|
|
|
struct cert *
|
|
|
|
cert_parse_pre(const char *fn, const unsigned char *der, size_t len)
|
|
|
|
{
|
2024-02-17 22:37:04 +01:00
|
|
|
struct cert *cert;
|
2023-04-30 03:15:27 +02:00
|
|
|
const unsigned char *oder;
|
2024-02-05 02:13:44 +01:00
|
|
|
size_t j;
|
|
|
|
int i, extsz;
|
2023-04-30 03:15:27 +02:00
|
|
|
X509 *x = NULL;
|
|
|
|
X509_EXTENSION *ext = NULL;
|
|
|
|
const X509_ALGOR *palg;
|
|
|
|
const ASN1_BIT_STRING *piuid = NULL, *psuid = NULL;
|
|
|
|
const ASN1_OBJECT *cobj;
|
|
|
|
ASN1_OBJECT *obj;
|
|
|
|
EVP_PKEY *pkey;
|
2023-06-24 04:57:55 +02:00
|
|
|
int nid, ip, as, sia, cp, crldp, aia, aki, ski,
|
|
|
|
eku, bc, ku;
|
|
|
|
|
|
|
|
nid = ip = as = sia = cp = crldp = aia = aki = ski = eku = bc = ku = 0;
|
2023-04-30 03:15:27 +02:00
|
|
|
|
|
|
|
/* just fail for empty buffers, the warning was printed elsewhere */
|
|
|
|
if (der == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2024-02-17 22:37:04 +01:00
|
|
|
if ((cert = calloc(1, sizeof(struct cert))) == NULL)
|
2023-04-30 03:15:27 +02:00
|
|
|
err(1, NULL);
|
|
|
|
|
|
|
|
oder = der;
|
|
|
|
if ((x = d2i_X509(NULL, &der, len)) == NULL) {
|
2024-02-17 22:37:04 +01:00
|
|
|
warnx("%s: d2i_X509", fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (der != oder + len) {
|
|
|
|
warnx("%s: %td bytes trailing garbage", fn, oder + len - der);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Cache X509v3 extensions, see X509_check_ca(3). */
|
|
|
|
if (X509_check_purpose(x, -1, -1) <= 0) {
|
2024-02-17 22:37:04 +01:00
|
|
|
warnx("%s: could not cache X509v3 extensions", fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2023-06-20 22:38:03 +02:00
|
|
|
if (X509_get_version(x) != 2) {
|
|
|
|
warnx("%s: RFC 6487 4.1: X.509 version must be v3", fn);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2023-04-30 03:15:27 +02:00
|
|
|
X509_get0_signature(NULL, &palg, x);
|
|
|
|
if (palg == NULL) {
|
2024-02-17 22:37:04 +01:00
|
|
|
warnx("%s: X509_get0_signature", fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
X509_ALGOR_get0(&cobj, NULL, NULL, palg);
|
2023-11-11 02:29:48 +01:00
|
|
|
nid = OBJ_obj2nid(cobj);
|
|
|
|
if (nid == NID_ecdsa_with_SHA256) {
|
|
|
|
if (verbose)
|
2023-12-10 14:25:40 +01:00
|
|
|
warnx("%s: P-256 support is experimental", fn);
|
2023-11-11 02:29:48 +01:00
|
|
|
} else if (nid != NID_sha256WithRSAEncryption) {
|
2023-04-30 03:15:27 +02:00
|
|
|
warnx("%s: RFC 7935: wrong signature algorithm %s, want %s",
|
2024-02-02 02:54:27 +01:00
|
|
|
fn, nid2str(nid), LN_sha256WithRSAEncryption);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
X509_get0_uids(x, &piuid, &psuid);
|
|
|
|
if (piuid != NULL || psuid != NULL) {
|
|
|
|
warnx("%s: issuer or subject unique identifiers not allowed",
|
|
|
|
fn);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2024-02-17 22:37:04 +01:00
|
|
|
if (!x509_valid_subject(fn, x))
|
2023-09-12 18:38:01 +02:00
|
|
|
goto out;
|
|
|
|
|
2023-04-30 03:15:27 +02:00
|
|
|
/* Look for X509v3 extensions. */
|
2024-02-05 02:13:44 +01:00
|
|
|
if ((extsz = X509_get_ext_count(x)) <= 0) {
|
|
|
|
warnx("%s: certificate without X.509v3 extensions", fn);
|
|
|
|
goto out;
|
|
|
|
}
|
2023-04-30 03:15:27 +02:00
|
|
|
|
2024-02-05 02:13:44 +01:00
|
|
|
for (i = 0; i < extsz; i++) {
|
2023-04-30 03:15:27 +02:00
|
|
|
ext = X509_get_ext(x, i);
|
|
|
|
assert(ext != NULL);
|
|
|
|
obj = X509_EXTENSION_get_object(ext);
|
|
|
|
assert(obj != NULL);
|
|
|
|
|
2023-06-25 23:25:02 +02:00
|
|
|
switch (nid = OBJ_obj2nid(obj)) {
|
2023-04-30 03:15:27 +02:00
|
|
|
case NID_sbgp_ipAddrBlock:
|
2023-06-25 23:25:02 +02:00
|
|
|
if (ip++ > 0)
|
2023-06-24 04:57:55 +02:00
|
|
|
goto dup;
|
2024-02-17 22:37:04 +01:00
|
|
|
if (!sbgp_ipaddrblk(fn, cert, ext))
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
break;
|
|
|
|
case NID_sbgp_autonomousSysNum:
|
2023-06-24 04:57:55 +02:00
|
|
|
if (as++ > 0)
|
|
|
|
goto dup;
|
2024-02-17 22:37:04 +01:00
|
|
|
if (!sbgp_assysnum(fn, cert, ext))
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
break;
|
|
|
|
case NID_sinfo_access:
|
2023-06-24 04:57:55 +02:00
|
|
|
if (sia++ > 0)
|
|
|
|
goto dup;
|
2024-02-17 22:37:04 +01:00
|
|
|
if (!sbgp_sia(fn, cert, ext))
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
break;
|
|
|
|
case NID_certificate_policies:
|
2023-06-24 04:57:55 +02:00
|
|
|
if (cp++ > 0)
|
|
|
|
goto dup;
|
2024-02-17 22:37:04 +01:00
|
|
|
if (!certificate_policies(fn, cert, ext))
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
break;
|
|
|
|
case NID_crl_distribution_points:
|
2023-06-24 04:57:55 +02:00
|
|
|
if (crldp++ > 0)
|
|
|
|
goto dup;
|
2023-04-30 03:15:27 +02:00
|
|
|
break;
|
|
|
|
case NID_info_access:
|
2023-06-24 04:57:55 +02:00
|
|
|
if (aia++ > 0)
|
|
|
|
goto dup;
|
2023-04-30 03:15:27 +02:00
|
|
|
break;
|
|
|
|
case NID_authority_key_identifier:
|
2023-06-24 04:57:55 +02:00
|
|
|
if (aki++ > 0)
|
|
|
|
goto dup;
|
2023-04-30 03:15:27 +02:00
|
|
|
break;
|
|
|
|
case NID_subject_key_identifier:
|
2023-06-24 04:57:55 +02:00
|
|
|
if (ski++ > 0)
|
|
|
|
goto dup;
|
2023-04-30 03:15:27 +02:00
|
|
|
break;
|
|
|
|
case NID_ext_key_usage:
|
2023-06-24 04:57:55 +02:00
|
|
|
if (eku++ > 0)
|
|
|
|
goto dup;
|
2023-04-30 03:15:27 +02:00
|
|
|
break;
|
|
|
|
case NID_basic_constraints:
|
2023-06-24 04:57:55 +02:00
|
|
|
if (bc++ > 0)
|
|
|
|
goto dup;
|
2023-04-30 03:15:27 +02:00
|
|
|
break;
|
|
|
|
case NID_key_usage:
|
2023-06-24 04:57:55 +02:00
|
|
|
if (ku++ > 0)
|
|
|
|
goto dup;
|
2023-04-30 03:15:27 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* unexpected extensions warrant investigation */
|
|
|
|
{
|
|
|
|
char objn[64];
|
|
|
|
OBJ_obj2txt(objn, sizeof(objn), obj, 0);
|
|
|
|
warnx("%s: ignoring %s (NID %d)",
|
2024-02-17 22:37:04 +01:00
|
|
|
fn, objn, OBJ_obj2nid(obj));
|
2023-04-30 03:15:27 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-17 22:37:04 +01:00
|
|
|
if (!x509_get_aki(x, fn, &cert->aki))
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
2024-02-17 22:37:04 +01:00
|
|
|
if (!x509_get_ski(x, fn, &cert->ski))
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
2024-02-17 22:37:04 +01:00
|
|
|
if (!x509_get_aia(x, fn, &cert->aia))
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
2024-02-17 22:37:04 +01:00
|
|
|
if (!x509_get_crl(x, fn, &cert->crl))
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
2024-02-17 22:37:04 +01:00
|
|
|
if (!x509_get_notbefore(x, fn, &cert->notbefore))
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
2024-02-17 22:37:04 +01:00
|
|
|
if (!x509_get_notafter(x, fn, &cert->notafter))
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
2024-02-17 22:37:04 +01:00
|
|
|
cert->purpose = x509_get_purpose(x, fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
|
|
|
|
/* Validation on required fields. */
|
|
|
|
|
2024-02-17 22:37:04 +01:00
|
|
|
switch (cert->purpose) {
|
2023-04-30 03:15:27 +02:00
|
|
|
case CERT_PURPOSE_CA:
|
|
|
|
if ((pkey = X509_get0_pubkey(x)) == NULL) {
|
2024-02-17 22:37:04 +01:00
|
|
|
warnx("%s: X509_get0_pubkey failed", fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
2024-02-17 22:37:04 +01:00
|
|
|
if (!valid_ca_pkey(fn, pkey))
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (X509_get_key_usage(x) != (KU_KEY_CERT_SIGN | KU_CRL_SIGN)) {
|
|
|
|
warnx("%s: RFC 6487 section 4.8.4: key usage violation",
|
2024-02-17 22:37:04 +01:00
|
|
|
fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* EKU may be allowed for some purposes in the future. */
|
|
|
|
if (X509_get_extended_key_usage(x) != UINT32_MAX) {
|
|
|
|
warnx("%s: RFC 6487 section 4.8.5: EKU not allowed",
|
|
|
|
fn);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2024-02-17 22:37:04 +01:00
|
|
|
if (cert->mft == NULL) {
|
|
|
|
warnx("%s: RFC 6487 section 4.8.8: missing SIA", fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
2024-02-17 22:37:04 +01:00
|
|
|
if (cert->asz == 0 && cert->ipsz == 0) {
|
|
|
|
warnx("%s: missing IP or AS resources", fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CERT_PURPOSE_BGPSEC_ROUTER:
|
2024-02-17 22:37:04 +01:00
|
|
|
cert->pubkey = x509_get_pubkey(x, fn);
|
|
|
|
if (cert->pubkey == NULL) {
|
|
|
|
warnx("%s: x509_get_pubkey failed", fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
2024-02-17 22:37:04 +01:00
|
|
|
if (cert->ipsz > 0) {
|
|
|
|
warnx("%s: unexpected IP resources in BGPsec cert", fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
2024-02-17 22:37:04 +01:00
|
|
|
for (j = 0; j < cert->asz; j++) {
|
|
|
|
if (cert->as[j].type == CERT_AS_INHERIT) {
|
2023-04-30 03:15:27 +02:00
|
|
|
warnx("%s: inherit elements not allowed in EE"
|
2024-02-17 22:37:04 +01:00
|
|
|
" cert", fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
2023-06-24 04:57:55 +02:00
|
|
|
if (sia) {
|
2023-04-30 03:15:27 +02:00
|
|
|
warnx("%s: unexpected SIA extension in BGPsec cert",
|
2024-02-17 22:37:04 +01:00
|
|
|
fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2024-02-17 22:37:04 +01:00
|
|
|
warnx("%s: x509_get_purpose failed in %s", fn, __func__);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2024-02-17 22:37:04 +01:00
|
|
|
if (cert->ski == NULL) {
|
|
|
|
warnx("%s: RFC 6487 section 8.4.2: missing SKI", fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2024-02-17 22:37:04 +01:00
|
|
|
cert->x509 = x;
|
|
|
|
return cert;
|
2023-04-30 03:15:27 +02:00
|
|
|
|
2023-06-24 04:57:55 +02:00
|
|
|
dup:
|
2024-02-02 02:54:27 +01:00
|
|
|
warnx("%s: RFC 5280 section 4.2: duplicate extension: %s", fn,
|
|
|
|
nid2str(nid));
|
2023-06-24 04:57:55 +02:00
|
|
|
out:
|
2024-02-17 22:37:04 +01:00
|
|
|
cert_free(cert);
|
2023-04-30 03:15:27 +02:00
|
|
|
X509_free(x);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct cert *
|
|
|
|
cert_parse(const char *fn, struct cert *p)
|
|
|
|
{
|
|
|
|
if (p == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (p->aki == NULL) {
|
|
|
|
warnx("%s: RFC 6487 section 8.4.2: "
|
|
|
|
"non-trust anchor missing AKI", fn);
|
|
|
|
goto badcert;
|
|
|
|
}
|
|
|
|
if (strcmp(p->aki, p->ski) == 0) {
|
|
|
|
warnx("%s: RFC 6487 section 8.4.2: "
|
|
|
|
"non-trust anchor AKI may not match SKI", fn);
|
|
|
|
goto badcert;
|
|
|
|
}
|
|
|
|
if (p->aia == NULL) {
|
|
|
|
warnx("%s: RFC 6487 section 8.4.7: AIA: extension missing", fn);
|
|
|
|
goto badcert;
|
|
|
|
}
|
|
|
|
if (p->crl == NULL) {
|
|
|
|
warnx("%s: RFC 6487 section 4.8.6: CRL: "
|
|
|
|
"no CRL distribution point extension", fn);
|
|
|
|
goto badcert;
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
|
|
|
|
badcert:
|
|
|
|
cert_free(p);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct cert *
|
|
|
|
ta_parse(const char *fn, struct cert *p, const unsigned char *pkey,
|
|
|
|
size_t pkeysz)
|
|
|
|
{
|
|
|
|
ASN1_TIME *notBefore, *notAfter;
|
|
|
|
EVP_PKEY *pk, *opk;
|
2024-01-12 02:27:06 +01:00
|
|
|
time_t now = get_current_time();
|
2023-04-30 03:15:27 +02:00
|
|
|
|
|
|
|
if (p == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* first check pubkey against the one from the TAL */
|
|
|
|
pk = d2i_PUBKEY(NULL, &pkey, pkeysz);
|
|
|
|
if (pk == NULL) {
|
2023-06-29 12:53:26 +02:00
|
|
|
warnx("%s: RFC 6487 (trust anchor): bad TAL pubkey", fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto badcert;
|
|
|
|
}
|
|
|
|
if ((opk = X509_get0_pubkey(p->x509)) == NULL) {
|
2023-06-29 12:53:26 +02:00
|
|
|
warnx("%s: RFC 6487 (trust anchor): missing pubkey", fn);
|
2023-04-30 03:15:27 +02:00
|
|
|
goto badcert;
|
|
|
|
}
|
|
|
|
if (EVP_PKEY_cmp(pk, opk) != 1) {
|
2023-06-29 12:53:26 +02:00
|
|
|
warnx("%s: RFC 6487 (trust anchor): "
|
2023-04-30 03:15:27 +02:00
|
|
|
"pubkey does not match TAL pubkey", fn);
|
|
|
|
goto badcert;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((notBefore = X509_get_notBefore(p->x509)) == NULL) {
|
|
|
|
warnx("%s: certificate has invalid notBefore", fn);
|
|
|
|
goto badcert;
|
|
|
|
}
|
|
|
|
if ((notAfter = X509_get_notAfter(p->x509)) == NULL) {
|
|
|
|
warnx("%s: certificate has invalid notAfter", fn);
|
|
|
|
goto badcert;
|
|
|
|
}
|
2024-01-12 02:27:06 +01:00
|
|
|
if (X509_cmp_time(notBefore, &now) != -1) {
|
2023-04-30 03:15:27 +02:00
|
|
|
warnx("%s: certificate not yet valid", fn);
|
|
|
|
goto badcert;
|
|
|
|
}
|
2024-01-12 02:27:06 +01:00
|
|
|
if (X509_cmp_time(notAfter, &now) != 1) {
|
2023-04-30 03:15:27 +02:00
|
|
|
warnx("%s: certificate has expired", fn);
|
|
|
|
goto badcert;
|
|
|
|
}
|
|
|
|
if (p->aki != NULL && strcmp(p->aki, p->ski)) {
|
|
|
|
warnx("%s: RFC 6487 section 8.4.2: "
|
|
|
|
"trust anchor AKI, if specified, must match SKI", fn);
|
|
|
|
goto badcert;
|
|
|
|
}
|
|
|
|
if (p->aia != NULL) {
|
|
|
|
warnx("%s: RFC 6487 section 8.4.7: "
|
|
|
|
"trust anchor must not have AIA", fn);
|
|
|
|
goto badcert;
|
|
|
|
}
|
|
|
|
if (p->crl != NULL) {
|
|
|
|
warnx("%s: RFC 6487 section 8.4.2: "
|
|
|
|
"trust anchor may not specify CRL resource", fn);
|
|
|
|
goto badcert;
|
|
|
|
}
|
|
|
|
if (p->purpose == CERT_PURPOSE_BGPSEC_ROUTER) {
|
|
|
|
warnx("%s: BGPsec cert cannot be a trust anchor", fn);
|
|
|
|
goto badcert;
|
|
|
|
}
|
|
|
|
if (x509_any_inherits(p->x509)) {
|
|
|
|
warnx("%s: Trust anchor IP/AS resources may not inherit", fn);
|
|
|
|
goto badcert;
|
|
|
|
}
|
|
|
|
|
|
|
|
EVP_PKEY_free(pk);
|
|
|
|
return p;
|
|
|
|
|
|
|
|
badcert:
|
|
|
|
EVP_PKEY_free(pk);
|
|
|
|
cert_free(p);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Free parsed certificate contents.
|
|
|
|
* Passing NULL is a noop.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
cert_free(struct cert *p)
|
|
|
|
{
|
|
|
|
if (p == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
free(p->crl);
|
|
|
|
free(p->repo);
|
|
|
|
free(p->mft);
|
|
|
|
free(p->notify);
|
|
|
|
free(p->ips);
|
|
|
|
free(p->as);
|
|
|
|
free(p->aia);
|
|
|
|
free(p->aki);
|
|
|
|
free(p->ski);
|
|
|
|
free(p->pubkey);
|
|
|
|
X509_free(p->x509);
|
|
|
|
free(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write certificate parsed content into buffer.
|
|
|
|
* See cert_read() for the other side of the pipe.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
cert_buffer(struct ibuf *b, const struct cert *p)
|
|
|
|
{
|
|
|
|
io_simple_buffer(b, &p->notafter, sizeof(p->notafter));
|
|
|
|
io_simple_buffer(b, &p->purpose, sizeof(p->purpose));
|
|
|
|
io_simple_buffer(b, &p->talid, sizeof(p->talid));
|
|
|
|
io_simple_buffer(b, &p->repoid, sizeof(p->repoid));
|
|
|
|
io_simple_buffer(b, &p->ipsz, sizeof(p->ipsz));
|
|
|
|
io_simple_buffer(b, &p->asz, sizeof(p->asz));
|
|
|
|
|
|
|
|
io_simple_buffer(b, p->ips, p->ipsz * sizeof(p->ips[0]));
|
|
|
|
io_simple_buffer(b, p->as, p->asz * sizeof(p->as[0]));
|
|
|
|
|
|
|
|
io_str_buffer(b, p->mft);
|
|
|
|
io_str_buffer(b, p->notify);
|
|
|
|
io_str_buffer(b, p->repo);
|
|
|
|
io_str_buffer(b, p->crl);
|
|
|
|
io_str_buffer(b, p->aia);
|
|
|
|
io_str_buffer(b, p->aki);
|
|
|
|
io_str_buffer(b, p->ski);
|
|
|
|
io_str_buffer(b, p->pubkey);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate and read parsed certificate content from descriptor.
|
|
|
|
* The pointer must be freed with cert_free().
|
|
|
|
* Always returns a valid pointer.
|
|
|
|
*/
|
|
|
|
struct cert *
|
|
|
|
cert_read(struct ibuf *b)
|
|
|
|
{
|
|
|
|
struct cert *p;
|
|
|
|
|
|
|
|
if ((p = calloc(1, sizeof(struct cert))) == NULL)
|
|
|
|
err(1, NULL);
|
|
|
|
|
|
|
|
io_read_buf(b, &p->notafter, sizeof(p->notafter));
|
|
|
|
io_read_buf(b, &p->purpose, sizeof(p->purpose));
|
|
|
|
io_read_buf(b, &p->talid, sizeof(p->talid));
|
|
|
|
io_read_buf(b, &p->repoid, sizeof(p->repoid));
|
|
|
|
io_read_buf(b, &p->ipsz, sizeof(p->ipsz));
|
|
|
|
io_read_buf(b, &p->asz, sizeof(p->asz));
|
|
|
|
|
|
|
|
p->ips = calloc(p->ipsz, sizeof(struct cert_ip));
|
|
|
|
if (p->ips == NULL)
|
|
|
|
err(1, NULL);
|
|
|
|
io_read_buf(b, p->ips, p->ipsz * sizeof(p->ips[0]));
|
|
|
|
|
|
|
|
p->as = calloc(p->asz, sizeof(struct cert_as));
|
|
|
|
if (p->as == NULL)
|
|
|
|
err(1, NULL);
|
|
|
|
io_read_buf(b, p->as, p->asz * sizeof(p->as[0]));
|
|
|
|
|
|
|
|
io_read_str(b, &p->mft);
|
|
|
|
io_read_str(b, &p->notify);
|
|
|
|
io_read_str(b, &p->repo);
|
|
|
|
io_read_str(b, &p->crl);
|
|
|
|
io_read_str(b, &p->aia);
|
|
|
|
io_read_str(b, &p->aki);
|
|
|
|
io_read_str(b, &p->ski);
|
|
|
|
io_read_str(b, &p->pubkey);
|
|
|
|
|
|
|
|
assert(p->mft != NULL || p->purpose == CERT_PURPOSE_BGPSEC_ROUTER);
|
|
|
|
assert(p->ski);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
authcmp(struct auth *a, struct auth *b)
|
|
|
|
{
|
|
|
|
return strcmp(a->cert->ski, b->cert->ski);
|
|
|
|
}
|
|
|
|
|
|
|
|
RB_GENERATE_STATIC(auth_tree, auth, entry, authcmp);
|
|
|
|
|
|
|
|
void
|
|
|
|
auth_tree_free(struct auth_tree *auths)
|
|
|
|
{
|
|
|
|
struct auth *auth, *tauth;
|
|
|
|
|
|
|
|
RB_FOREACH_SAFE(auth, auth_tree, auths, tauth) {
|
|
|
|
RB_REMOVE(auth_tree, auths, auth);
|
|
|
|
cert_free(auth->cert);
|
|
|
|
free(auth);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct auth *
|
|
|
|
auth_find(struct auth_tree *auths, const char *aki)
|
|
|
|
{
|
|
|
|
struct auth a;
|
|
|
|
struct cert c;
|
|
|
|
|
|
|
|
/* we look up the cert where the ski == aki */
|
|
|
|
c.ski = (char *)aki;
|
|
|
|
a.cert = &c;
|
|
|
|
|
|
|
|
return RB_FIND(auth_tree, auths, &a);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct auth *
|
|
|
|
auth_insert(struct auth_tree *auths, struct cert *cert, struct auth *parent)
|
|
|
|
{
|
|
|
|
struct auth *na;
|
|
|
|
|
|
|
|
na = malloc(sizeof(*na));
|
|
|
|
if (na == NULL)
|
|
|
|
err(1, NULL);
|
|
|
|
|
|
|
|
na->parent = parent;
|
|
|
|
na->cert = cert;
|
2023-05-13 16:25:18 +02:00
|
|
|
na->any_inherits = x509_any_inherits(cert->x509);
|
2023-04-30 03:15:27 +02:00
|
|
|
|
|
|
|
if (RB_INSERT(auth_tree, auths, na) != NULL)
|
|
|
|
err(1, "auth tree corrupted");
|
|
|
|
|
|
|
|
return na;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
insert_brk(struct brk_tree *tree, struct cert *cert, int asid)
|
|
|
|
{
|
|
|
|
struct brk *b, *found;
|
|
|
|
|
|
|
|
if ((b = calloc(1, sizeof(*b))) == NULL)
|
|
|
|
err(1, NULL);
|
|
|
|
|
|
|
|
b->asid = asid;
|
|
|
|
b->expires = cert->notafter;
|
|
|
|
b->talid = cert->talid;
|
|
|
|
if ((b->ski = strdup(cert->ski)) == NULL)
|
|
|
|
err(1, NULL);
|
|
|
|
if ((b->pubkey = strdup(cert->pubkey)) == NULL)
|
|
|
|
err(1, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if a similar BRK already exists in the tree. If the found BRK
|
|
|
|
* expires sooner, update it to this BRK's later expiry moment.
|
|
|
|
*/
|
|
|
|
if ((found = RB_INSERT(brk_tree, tree, b)) != NULL) {
|
|
|
|
if (found->expires < b->expires) {
|
|
|
|
found->expires = b->expires;
|
|
|
|
found->talid = b->talid;
|
|
|
|
}
|
|
|
|
free(b->ski);
|
|
|
|
free(b->pubkey);
|
|
|
|
free(b);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add each BGPsec Router Key into the BRK tree.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
cert_insert_brks(struct brk_tree *tree, struct cert *cert)
|
|
|
|
{
|
|
|
|
size_t i, asid;
|
|
|
|
|
|
|
|
for (i = 0; i < cert->asz; i++) {
|
|
|
|
switch (cert->as[i].type) {
|
|
|
|
case CERT_AS_ID:
|
|
|
|
insert_brk(tree, cert, cert->as[i].id);
|
|
|
|
break;
|
|
|
|
case CERT_AS_RANGE:
|
|
|
|
for (asid = cert->as[i].range.min;
|
|
|
|
asid <= cert->as[i].range.max; asid++)
|
|
|
|
insert_brk(tree, cert, asid);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
warnx("invalid AS identifier type");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
brkcmp(struct brk *a, struct brk *b)
|
|
|
|
{
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
if (a->asid > b->asid)
|
|
|
|
return 1;
|
|
|
|
if (a->asid < b->asid)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
rv = strcmp(a->ski, b->ski);
|
|
|
|
if (rv > 0)
|
|
|
|
return 1;
|
|
|
|
if (rv < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return strcmp(a->pubkey, b->pubkey);
|
|
|
|
}
|
|
|
|
|
|
|
|
RB_GENERATE(brk_tree, brk, entry, brkcmp);
|