1966 lines
38 KiB
C
1966 lines
38 KiB
C
/* $OpenBSD: rfc3779.c,v 1.9 2023/04/20 07:39:17 tb Exp $ */
|
|
/*
|
|
* Copyright (c) 2021 Theo Buehler <tb@openbsd.org>
|
|
*
|
|
* 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 <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <openssl/asn1.h>
|
|
#include <openssl/asn1t.h>
|
|
#include <openssl/x509v3.h>
|
|
|
|
#define RAW_ADDRESS_SIZE 16
|
|
|
|
static void
|
|
hexdump(const unsigned char *buf, size_t len)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 1; i <= len; i++)
|
|
fprintf(stderr, " 0x%02hhx,%s", buf[i - 1], i % 8 ? "" : "\n");
|
|
|
|
if (len % 8)
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
static void
|
|
report_hexdump(const char *func, const char *description, const char *msg,
|
|
const unsigned char *want, size_t want_len,
|
|
const unsigned char *got, size_t got_len)
|
|
{
|
|
fprintf(stderr, "%s: \"%s\" %s\nwant:\n", func, description, msg);
|
|
hexdump(want, want_len);
|
|
fprintf(stderr, "got:\n");
|
|
hexdump(got, got_len);
|
|
}
|
|
|
|
static int
|
|
afi_size(int afi)
|
|
{
|
|
switch (afi) {
|
|
case IANA_AFI_IPV4:
|
|
return 4;
|
|
case IANA_AFI_IPV6:
|
|
return 16;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
struct IPAddressOrRange_test {
|
|
const char *description;
|
|
const uint8_t der[32];
|
|
size_t der_len;
|
|
unsigned afi;
|
|
const uint8_t min[RAW_ADDRESS_SIZE];
|
|
const uint8_t max[RAW_ADDRESS_SIZE];
|
|
};
|
|
|
|
const struct IPAddressOrRange_test IPAddressOrRange_test_data[] = {
|
|
/* Examples from RFC 3779, section 2.1.1 */
|
|
{
|
|
.description = "address 10.5.0.4",
|
|
.der = {
|
|
0x03, 0x05, 0x00, 0x0a, 0x05, 0x00, 0x04,
|
|
},
|
|
.der_len = 7,
|
|
.afi = IANA_AFI_IPV4,
|
|
.min = {
|
|
0x0a, 0x05, 0x00, 0x04,
|
|
},
|
|
.max = {
|
|
0x0a, 0x05, 0x00, 0x04,
|
|
}
|
|
},
|
|
{
|
|
.description = "prefix 10.5.0/23",
|
|
.der = {
|
|
0x03, 0x04, 0x01, 0x0a, 0x05, 0x00,
|
|
},
|
|
.der_len = 6,
|
|
.afi = IANA_AFI_IPV4,
|
|
.min = {
|
|
0x0a, 0x05, 0x00, 0x00,
|
|
},
|
|
.max = {
|
|
0x0a, 0x05, 0x01, 0xff,
|
|
}
|
|
},
|
|
{
|
|
.description = "address 2001:0:200:3::1",
|
|
.der = {
|
|
0x03, 0x11, 0x00, 0x20, 0x01, 0x00, 0x00, 0x02,
|
|
0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x01,
|
|
},
|
|
.der_len = 19,
|
|
.afi = IANA_AFI_IPV6,
|
|
.min = {
|
|
0x20, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
|
},
|
|
.max = {
|
|
0x20, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
|
},
|
|
},
|
|
{
|
|
.description = "prefix 2001:0:200/39",
|
|
.der = {
|
|
0x03, 0x06, 0x01, 0x20, 0x01, 0x00, 0x00, 0x02,
|
|
},
|
|
.der_len = 8,
|
|
.afi = IANA_AFI_IPV6,
|
|
.min = {
|
|
0x20, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
},
|
|
.max = {
|
|
0x20, 0x01, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
},
|
|
},
|
|
|
|
/* Examples from RFC 3779, Section 2.1.2 */
|
|
{
|
|
.description = "prefix 10.5.0/23 as a range",
|
|
.der = {
|
|
/* Sequence */
|
|
0x30, 0x0b,
|
|
/* 10.5.0.0 */
|
|
0x03, 0x03, 0x00, 0x0a, 0x05,
|
|
/* 10.5.1.255 */
|
|
0x03, 0x04, 0x01, 0x0a, 0x05, 0x00,
|
|
},
|
|
.der_len = 13,
|
|
.afi = IANA_AFI_IPV4,
|
|
.min = {
|
|
0x0a, 0x05, 0x00, 0x00,
|
|
},
|
|
.max = {
|
|
0x0a, 0x05, 0x01, 0xff,
|
|
}
|
|
},
|
|
{
|
|
.description = "prefix 2001:0:200/39 as a range",
|
|
.der = {
|
|
/* Sequence */
|
|
0x30, 0x10,
|
|
/* 2001:0:200:: */
|
|
0x03, 0x06, 0x01, 0x20, 0x01, 0x00, 0x00, 0x02,
|
|
/* 2001:0:3ff:ffff:ffff:ffff:ffff:ffff */
|
|
0x03, 0x06, 0x02, 0x20, 0x01, 0x00, 0x00, 0x00,
|
|
},
|
|
.der_len = 18,
|
|
.afi = IANA_AFI_IPV6,
|
|
.min = {
|
|
0x20, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
},
|
|
.max = {
|
|
0x20, 0x01, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
}
|
|
},
|
|
{
|
|
.description = "prefix 0/0",
|
|
.der = {
|
|
0x03, 0x01, 0x00,
|
|
},
|
|
.der_len = 3,
|
|
.afi = IANA_AFI_IPV4,
|
|
.min = {
|
|
0x00, 0x00, 0x00, 0x00,
|
|
},
|
|
.max = {
|
|
0xff, 0xff, 0xff, 0xff,
|
|
}
|
|
},
|
|
{
|
|
.description = "prefix 10.64/12",
|
|
.der = {
|
|
0x03, 0x03, 0x04, 0x0a, 0x40,
|
|
},
|
|
.der_len = 5,
|
|
.afi = IANA_AFI_IPV4,
|
|
.min = {
|
|
0x0a, 0x40, 0x00, 0x00,
|
|
},
|
|
.max = {
|
|
0x0a, 0x4f, 0xff, 0xff,
|
|
},
|
|
},
|
|
{
|
|
.description = "prefix 10.64/20",
|
|
.der = {
|
|
0x03, 0x04, 0x04, 0x0a, 0x40, 0x00,
|
|
},
|
|
.der_len = 6,
|
|
.afi = IANA_AFI_IPV4,
|
|
.min = {
|
|
0x0a, 0x40, 0x00, 0x00,
|
|
},
|
|
.max = {
|
|
0x0a, 0x40, 0x0f, 0xff,
|
|
},
|
|
},
|
|
};
|
|
|
|
const size_t N_IPADDRESSORRANGE_TESTS =
|
|
sizeof(IPAddressOrRange_test_data) / sizeof(IPAddressOrRange_test_data[0]);
|
|
|
|
static int
|
|
test_IPAddressOrRange(const struct IPAddressOrRange_test *test)
|
|
{
|
|
IPAddressOrRange *aor;
|
|
const unsigned char *p;
|
|
unsigned char min[RAW_ADDRESS_SIZE] = {0}, max[RAW_ADDRESS_SIZE] = {0};
|
|
unsigned char *out = NULL;
|
|
int out_len;
|
|
int afi_len;
|
|
int memcmp_failed = 0;
|
|
int failed = 1;
|
|
|
|
/*
|
|
* First, decode DER from the test case.
|
|
*/
|
|
|
|
p = &test->der[0];
|
|
if ((aor = d2i_IPAddressOrRange(NULL, &p, test->der_len)) == NULL) {
|
|
fprintf(stderr, "%s: \"%s\" d2i_IPAddressOrRange failed\n",
|
|
__func__, test->description);
|
|
goto err;
|
|
}
|
|
|
|
/*
|
|
* Now extract minimum and maximum from the parsed range.
|
|
*/
|
|
|
|
afi_len = afi_size(test->afi);
|
|
|
|
if (X509v3_addr_get_range(aor, test->afi, min, max, sizeof min) !=
|
|
afi_len) {
|
|
fprintf(stderr, "%s: \"%s\" X509v3_addr_get_range failed\n",
|
|
__func__, test->description);
|
|
goto err;
|
|
}
|
|
|
|
/*
|
|
* Check that min and max match expectations.
|
|
*/
|
|
|
|
if (memcmp(min, test->min, afi_len) != 0) {
|
|
memcmp_failed |= 1;
|
|
report_hexdump(__func__, test->description, "memcmp min failed",
|
|
test->min, afi_len, min, afi_len);
|
|
}
|
|
if (memcmp(max, test->max, afi_len) != 0) {
|
|
memcmp_failed |= 1;
|
|
report_hexdump(__func__, test->description, "memcmp max failed",
|
|
test->max, afi_len, max, afi_len);
|
|
}
|
|
if (memcmp_failed)
|
|
goto err;
|
|
|
|
/*
|
|
* Now turn the parsed IPAddressOrRange back into DER and check that
|
|
* it matches the DER in the test case.
|
|
*/
|
|
|
|
out = NULL;
|
|
if ((out_len = i2d_IPAddressOrRange(aor, &out)) <= 0) {
|
|
fprintf(stderr, "%s: \"%s\" i2d_IPAddressOrRange failed\n",
|
|
__func__, test->description);
|
|
goto err;
|
|
}
|
|
|
|
memcmp_failed = (size_t)out_len != test->der_len;
|
|
if (!memcmp_failed)
|
|
memcmp_failed = memcmp(test->der, out, out_len);
|
|
|
|
if (memcmp_failed) {
|
|
report_hexdump(__func__, test->description, "memcmp DER failed",
|
|
test->der, test->der_len, out, out_len);
|
|
goto err;
|
|
}
|
|
|
|
failed = 0;
|
|
err:
|
|
IPAddressOrRange_free(aor);
|
|
free(out);
|
|
|
|
return failed;
|
|
}
|
|
|
|
static int
|
|
run_IPAddressOrRange_tests(void)
|
|
{
|
|
size_t i;
|
|
int failed = 0;
|
|
|
|
for (i = 0; i < N_IPADDRESSORRANGE_TESTS; i++)
|
|
failed |=
|
|
test_IPAddressOrRange(&IPAddressOrRange_test_data[i]);
|
|
|
|
return failed;
|
|
}
|
|
|
|
/*
|
|
* XXX: These should really be part of the public API...
|
|
*/
|
|
static IPAddrBlocks *IPAddrBlocks_new(void);
|
|
static void IPAddrBlocks_free(IPAddrBlocks *addr);
|
|
static IPAddrBlocks *d2i_IPAddrBlocks(IPAddrBlocks **addrs,
|
|
const unsigned char **in, long len);
|
|
static int i2d_IPAddrBlocks(IPAddrBlocks *addrs, unsigned char **out);
|
|
|
|
static IPAddrBlocks *
|
|
IPAddrBlocks_new(void)
|
|
{
|
|
IPAddrBlocks *addrs;
|
|
|
|
/*
|
|
* XXX The comparison function IPAddressFamily_cmp() isn't public.
|
|
* Start with the default and exploit a side effect of the lovely API
|
|
* which helpfully sets the correct function in a few places. Let's
|
|
* use the cheapest and easiest to reach one.
|
|
*/
|
|
if ((addrs = sk_IPAddressFamily_new_null()) == NULL)
|
|
return NULL;
|
|
if (!X509v3_addr_canonize(addrs)) {
|
|
IPAddrBlocks_free(addrs);
|
|
return NULL;
|
|
}
|
|
|
|
return addrs;
|
|
}
|
|
|
|
static void
|
|
IPAddrBlocks_free(IPAddrBlocks *addr)
|
|
{
|
|
sk_IPAddressFamily_pop_free(addr, IPAddressFamily_free);
|
|
}
|
|
|
|
/*
|
|
* We want {d2i,i2d}_IPAddrBlocks() to play with the DER of the extension.
|
|
* These don't exist, so we have to implement them ourselves. IPAddrBlocks_it
|
|
* isn't public, so we need to fetch it from the library. We cache it in a
|
|
* static variable to avoid the cost of a binary search through all supported
|
|
* extensions on each call.
|
|
*/
|
|
|
|
static const ASN1_ITEM_EXP *
|
|
get_IPAddrBlocks_it(void)
|
|
{
|
|
static const ASN1_ITEM_EXP *my_IPAddrBlocks_it;
|
|
const X509V3_EXT_METHOD *v3_addr;
|
|
|
|
if (my_IPAddrBlocks_it != NULL)
|
|
return my_IPAddrBlocks_it;
|
|
|
|
if ((v3_addr = X509V3_EXT_get_nid(NID_sbgp_ipAddrBlock)) == NULL) {
|
|
fprintf(stderr, "could not get v3_addr\n");
|
|
return NULL;
|
|
}
|
|
|
|
my_IPAddrBlocks_it = v3_addr->it;
|
|
|
|
return my_IPAddrBlocks_it;
|
|
}
|
|
|
|
static IPAddrBlocks *
|
|
d2i_IPAddrBlocks(IPAddrBlocks **addrs, const unsigned char **in, long len)
|
|
{
|
|
const ASN1_ITEM_EXP *my_IPAddrBlocks_it;
|
|
|
|
if ((my_IPAddrBlocks_it = get_IPAddrBlocks_it()) == NULL)
|
|
return NULL;
|
|
|
|
return (IPAddrBlocks *)ASN1_item_d2i((ASN1_VALUE **)addrs, in, len,
|
|
my_IPAddrBlocks_it);
|
|
}
|
|
|
|
static int
|
|
i2d_IPAddrBlocks(IPAddrBlocks *addrs, unsigned char **out)
|
|
{
|
|
const ASN1_ITEM_EXP *my_IPAddrBlocks_it;
|
|
|
|
if ((my_IPAddrBlocks_it = get_IPAddrBlocks_it()) == NULL)
|
|
return -1;
|
|
|
|
return ASN1_item_i2d((ASN1_VALUE *)addrs, out, my_IPAddrBlocks_it);
|
|
}
|
|
|
|
struct ipv4_prefix {
|
|
unsigned char addr[4];
|
|
size_t addr_len;
|
|
size_t prefix_len;
|
|
};
|
|
|
|
struct ipv4_range {
|
|
unsigned char min[4];
|
|
unsigned char max[4];
|
|
};
|
|
|
|
union ipv4_choice {
|
|
struct ipv4_prefix prefix;
|
|
struct ipv4_range range;
|
|
};
|
|
|
|
struct ipv6_prefix {
|
|
unsigned char addr[16];
|
|
size_t addr_len;
|
|
size_t prefix_len;
|
|
};
|
|
|
|
struct ipv6_range {
|
|
unsigned char min[16];
|
|
unsigned char max[16];
|
|
};
|
|
|
|
union ipv6_choice {
|
|
struct ipv6_prefix prefix;
|
|
struct ipv6_range range;
|
|
};
|
|
|
|
enum choice_type {
|
|
choice_prefix,
|
|
choice_range,
|
|
choice_inherit,
|
|
choice_last,
|
|
};
|
|
|
|
union ip {
|
|
union ipv4_choice ipv4;
|
|
union ipv6_choice ipv6;
|
|
};
|
|
|
|
enum safi {
|
|
safi_none,
|
|
safi_unicast,
|
|
safi_multicast,
|
|
};
|
|
|
|
struct ip_addr_block {
|
|
unsigned int afi;
|
|
enum safi safi;
|
|
enum choice_type type;
|
|
union ip addr;
|
|
};
|
|
|
|
struct build_addr_block_test_data {
|
|
char *description;
|
|
struct ip_addr_block addrs[16];
|
|
char der[128];
|
|
size_t der_len;
|
|
int is_canonical;
|
|
int inherits;
|
|
unsigned int afis[4];
|
|
int afi_len;
|
|
};
|
|
|
|
const struct build_addr_block_test_data build_addr_block_tests[] = {
|
|
{
|
|
.description = "RFC 3779, Appendix B, example 1",
|
|
.addrs = {
|
|
{
|
|
.afi = IANA_AFI_IPV4,
|
|
.safi = safi_unicast,
|
|
.type = choice_prefix,
|
|
.addr.ipv4.prefix = {
|
|
.addr = {
|
|
10, 0, 32,
|
|
},
|
|
.addr_len = 3,
|
|
.prefix_len = 20,
|
|
},
|
|
},
|
|
{
|
|
.afi = IANA_AFI_IPV4,
|
|
.safi = safi_unicast,
|
|
.type = choice_prefix,
|
|
.addr.ipv4.prefix = {
|
|
.addr = {
|
|
10, 0, 64,
|
|
},
|
|
.addr_len = 3,
|
|
.prefix_len = 24,
|
|
},
|
|
},
|
|
{
|
|
.afi = IANA_AFI_IPV4,
|
|
.safi = safi_unicast,
|
|
.type = choice_prefix,
|
|
.addr.ipv4.prefix = {
|
|
.addr = {
|
|
10, 1,
|
|
},
|
|
.addr_len = 2,
|
|
.prefix_len = 16,
|
|
},
|
|
},
|
|
{
|
|
.afi = IANA_AFI_IPV4,
|
|
.safi = safi_unicast,
|
|
.type = choice_prefix,
|
|
.addr.ipv4.prefix = {
|
|
.addr = {
|
|
10, 2, 48,
|
|
},
|
|
.addr_len = 3,
|
|
.prefix_len = 20,
|
|
},
|
|
},
|
|
{
|
|
.afi = IANA_AFI_IPV4,
|
|
.safi = safi_unicast,
|
|
.type = choice_prefix,
|
|
.addr.ipv4.prefix = {
|
|
.addr = {
|
|
10, 2, 64,
|
|
},
|
|
.addr_len = 3,
|
|
.prefix_len = 24,
|
|
},
|
|
},
|
|
{
|
|
.afi = IANA_AFI_IPV4,
|
|
.safi = safi_unicast,
|
|
.type = choice_prefix,
|
|
.addr.ipv4.prefix = {
|
|
.addr = {
|
|
10, 3,
|
|
},
|
|
.addr_len = 2,
|
|
.prefix_len = 16,
|
|
},
|
|
},
|
|
{
|
|
.afi = IANA_AFI_IPV6,
|
|
.safi = safi_none,
|
|
.type = choice_inherit,
|
|
},
|
|
{
|
|
.type = choice_last,
|
|
},
|
|
},
|
|
.der = {
|
|
0x30, 0x35, 0x30, 0x2b, 0x04, 0x03, 0x00, 0x01,
|
|
0x01, 0x30, 0x24, 0x03, 0x04, 0x04, 0x0a, 0x00,
|
|
0x20, 0x03, 0x04, 0x00, 0x0a, 0x00, 0x40, 0x03,
|
|
0x03, 0x00, 0x0a, 0x01, 0x30, 0x0c, 0x03, 0x04,
|
|
0x04, 0x0a, 0x02, 0x30, 0x03, 0x04, 0x00, 0x0a,
|
|
0x02, 0x40, 0x03, 0x03, 0x00, 0x0a, 0x03, 0x30,
|
|
0x06, 0x04, 0x02, 0x00, 0x02, 0x05, 0x00,
|
|
},
|
|
.der_len = 55,
|
|
.is_canonical = 0,
|
|
.inherits = 1,
|
|
.afis = {
|
|
IANA_AFI_IPV4, IANA_AFI_IPV6,
|
|
},
|
|
.afi_len = 2,
|
|
},
|
|
{
|
|
.description = "RFC 3779, Appendix B, example 1 canonical",
|
|
.addrs = {
|
|
{
|
|
.afi = IANA_AFI_IPV4,
|
|
.safi = safi_unicast,
|
|
.type = choice_prefix,
|
|
.addr.ipv4.prefix = {
|
|
.addr = {
|
|
10, 0, 32,
|
|
},
|
|
.addr_len = 3,
|
|
.prefix_len = 20,
|
|
},
|
|
},
|
|
{
|
|
.afi = IANA_AFI_IPV4,
|
|
.safi = safi_unicast,
|
|
.type = choice_prefix,
|
|
.addr.ipv4.prefix = {
|
|
.addr = {
|
|
10, 0, 64,
|
|
},
|
|
.addr_len = 3,
|
|
.prefix_len = 24,
|
|
},
|
|
},
|
|
{
|
|
.afi = IANA_AFI_IPV4,
|
|
.safi = safi_unicast,
|
|
.type = choice_prefix,
|
|
.addr.ipv4.prefix = {
|
|
.addr = {
|
|
10, 1,
|
|
},
|
|
.addr_len = 2,
|
|
.prefix_len = 16,
|
|
},
|
|
},
|
|
{
|
|
.afi = IANA_AFI_IPV4,
|
|
.safi = safi_unicast,
|
|
.type = choice_range,
|
|
.addr.ipv4.range = {
|
|
.min = {
|
|
10, 2, 48, 00,
|
|
},
|
|
.max = {
|
|
10, 2, 64, 255,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
.afi = IANA_AFI_IPV4,
|
|
.safi = safi_unicast,
|
|
.type = choice_prefix,
|
|
.addr.ipv4.prefix = {
|
|
.addr = {
|
|
10, 3,
|
|
},
|
|
.addr_len = 2,
|
|
.prefix_len = 16,
|
|
},
|
|
},
|
|
{
|
|
.afi = IANA_AFI_IPV6,
|
|
.safi = safi_none,
|
|
.type = choice_inherit,
|
|
},
|
|
{
|
|
.type = choice_last,
|
|
},
|
|
},
|
|
.der = {
|
|
0x30, 0x35, 0x30, 0x2b, 0x04, 0x03, 0x00, 0x01,
|
|
0x01, 0x30, 0x24, 0x03, 0x04, 0x04, 0x0a, 0x00,
|
|
0x20, 0x03, 0x04, 0x00, 0x0a, 0x00, 0x40, 0x03,
|
|
0x03, 0x00, 0x0a, 0x01, 0x30, 0x0c, 0x03, 0x04,
|
|
0x04, 0x0a, 0x02, 0x30, 0x03, 0x04, 0x00, 0x0a,
|
|
0x02, 0x40, 0x03, 0x03, 0x00, 0x0a, 0x03, 0x30,
|
|
0x06, 0x04, 0x02, 0x00, 0x02, 0x05, 0x00,
|
|
},
|
|
.der_len = 55,
|
|
.is_canonical = 1,
|
|
.inherits = 1,
|
|
.afis = {
|
|
IANA_AFI_IPV4, IANA_AFI_IPV6,
|
|
},
|
|
.afi_len = 2,
|
|
},
|
|
{
|
|
.description = "RFC 3779, Appendix B, example 2",
|
|
.addrs = {
|
|
{
|
|
.afi = IANA_AFI_IPV6,
|
|
.safi = safi_none,
|
|
.type = choice_prefix,
|
|
.addr.ipv6.prefix = {
|
|
.addr = {
|
|
0x20, 0x01, 0x00, 0x00,
|
|
0x00, 0x02,
|
|
},
|
|
.addr_len = 6,
|
|
.prefix_len = 48,
|
|
},
|
|
},
|
|
{
|
|
.afi = IANA_AFI_IPV4,
|
|
.safi = safi_unicast,
|
|
.type = choice_prefix,
|
|
.addr.ipv4.prefix = {
|
|
.addr = {
|
|
10,
|
|
},
|
|
.addr_len = 1,
|
|
.prefix_len = 8,
|
|
},
|
|
},
|
|
{
|
|
.afi = IANA_AFI_IPV4,
|
|
.safi = safi_unicast,
|
|
.type = choice_prefix,
|
|
.addr.ipv4.prefix = {
|
|
.addr = {
|
|
172, 16,
|
|
},
|
|
.addr_len = 2,
|
|
.prefix_len = 12,
|
|
},
|
|
},
|
|
{
|
|
.afi = IANA_AFI_IPV4,
|
|
.safi = safi_multicast,
|
|
.type = choice_inherit,
|
|
},
|
|
{
|
|
.type = choice_last,
|
|
},
|
|
},
|
|
.der = {
|
|
0x30, 0x2c, 0x30, 0x10, 0x04, 0x03, 0x00, 0x01,
|
|
0x01, 0x30, 0x09, 0x03, 0x02, 0x00, 0x0a, 0x03,
|
|
0x03, 0x04, 0xac, 0x10, 0x30, 0x07, 0x04, 0x03,
|
|
0x00, 0x01, 0x02, 0x05, 0x00, 0x30, 0x0f, 0x04,
|
|
0x02, 0x00, 0x02, 0x30, 0x09, 0x03, 0x07, 0x00,
|
|
0x20, 0x01, 0x00, 0x00, 0x00, 0x02,
|
|
},
|
|
.der_len = 46,
|
|
.is_canonical = 0,
|
|
.inherits = 1,
|
|
.afis = {
|
|
IANA_AFI_IPV4, IANA_AFI_IPV4,
|
|
},
|
|
.afi_len = 2,
|
|
},
|
|
{
|
|
.description = "Range should be prefix 127/8",
|
|
.addrs = {
|
|
{
|
|
.afi = IANA_AFI_IPV4,
|
|
.safi = safi_none,
|
|
.type = choice_range,
|
|
.addr.ipv4.range = {
|
|
.min = {
|
|
127, 0, 0, 0,
|
|
},
|
|
.max = {
|
|
127, 255, 255, 255,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
.type = choice_last,
|
|
},
|
|
},
|
|
.der = {
|
|
0x30, 0x0c, 0x30, 0x0a, 0x04, 0x02, 0x00, 0x01,
|
|
0x30, 0x04, 0x03, 0x02, 0x00, 0x7f,
|
|
},
|
|
.der_len = 14,
|
|
.is_canonical = 1,
|
|
.inherits = 0,
|
|
.afis = {
|
|
IANA_AFI_IPV4,
|
|
},
|
|
.afi_len = 1,
|
|
},
|
|
};
|
|
|
|
const size_t N_BUILD_ADDR_BLOCK_TESTS =
|
|
sizeof(build_addr_block_tests) / sizeof(build_addr_block_tests[0]);
|
|
|
|
static unsigned int *
|
|
addr_block_get_safi(const struct ip_addr_block *addr)
|
|
{
|
|
static unsigned int safi;
|
|
|
|
switch (addr->safi) {
|
|
case safi_none:
|
|
return NULL;
|
|
case safi_unicast:
|
|
safi = 1;
|
|
break;
|
|
case safi_multicast:
|
|
safi = 2;
|
|
break;
|
|
}
|
|
|
|
return &safi;
|
|
}
|
|
|
|
static int
|
|
addr_block_add_ipv4_addr(IPAddrBlocks *block, enum choice_type type,
|
|
const union ipv4_choice *ipv4, unsigned int *safi)
|
|
{
|
|
unsigned char addr[RAW_ADDRESS_SIZE] = {0};
|
|
unsigned char min[RAW_ADDRESS_SIZE];
|
|
unsigned char max[RAW_ADDRESS_SIZE];
|
|
|
|
switch (type) {
|
|
case choice_prefix:
|
|
memcpy(addr, ipv4->prefix.addr, ipv4->prefix.addr_len);
|
|
return X509v3_addr_add_prefix(block, IANA_AFI_IPV4, safi,
|
|
addr, ipv4->prefix.prefix_len);
|
|
case choice_range:
|
|
memcpy(min, ipv4->range.min, sizeof(ipv4->range.min));
|
|
memcpy(max, ipv4->range.max, sizeof(ipv4->range.max));
|
|
return X509v3_addr_add_range(block, IANA_AFI_IPV4, safi,
|
|
min, max);
|
|
case choice_inherit:
|
|
return X509v3_addr_add_inherit(block, IANA_AFI_IPV4, safi);
|
|
case choice_last:
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int
|
|
addr_block_add_ipv6_addr(IPAddrBlocks *block, enum choice_type type,
|
|
const union ipv6_choice *ipv6, unsigned int *safi)
|
|
{
|
|
unsigned char addr[RAW_ADDRESS_SIZE] = {0};
|
|
unsigned char min[RAW_ADDRESS_SIZE];
|
|
unsigned char max[RAW_ADDRESS_SIZE];
|
|
|
|
switch (type) {
|
|
case choice_prefix:
|
|
memcpy(addr, ipv6->prefix.addr, ipv6->prefix.addr_len);
|
|
return X509v3_addr_add_prefix(block, IANA_AFI_IPV6, safi,
|
|
addr, ipv6->prefix.prefix_len);
|
|
case choice_range:
|
|
memcpy(min, ipv6->range.min, sizeof(ipv6->range.min));
|
|
memcpy(max, ipv6->range.max, sizeof(ipv6->range.max));
|
|
return X509v3_addr_add_range(block, IANA_AFI_IPV6, safi,
|
|
min, max);
|
|
case choice_inherit:
|
|
return X509v3_addr_add_inherit(block, IANA_AFI_IPV6, safi);
|
|
case choice_last:
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int
|
|
addr_block_add_addrs(IPAddrBlocks *block, const struct ip_addr_block addrs[])
|
|
{
|
|
const struct ip_addr_block *addr;
|
|
unsigned int *safi;
|
|
|
|
for (addr = &addrs[0]; addr->type != choice_last; addr++) {
|
|
safi = addr_block_get_safi(addr);
|
|
switch (addr->afi) {
|
|
case IANA_AFI_IPV4:
|
|
if (!addr_block_add_ipv4_addr(block, addr->type,
|
|
&addr->addr.ipv4, safi))
|
|
return 0;
|
|
break;
|
|
case IANA_AFI_IPV6:
|
|
if (!addr_block_add_ipv6_addr(block, addr->type,
|
|
&addr->addr.ipv6, safi))
|
|
return 0;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "%s: corrupt test data", __func__);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
build_addr_block_test(const struct build_addr_block_test_data *test)
|
|
{
|
|
IPAddrBlocks *addrs = NULL, *parsed = NULL;
|
|
const unsigned char *p;
|
|
unsigned char *out = NULL;
|
|
int out_len;
|
|
int i;
|
|
int memcmp_failed = 1;
|
|
int failed = 1;
|
|
|
|
if ((addrs = IPAddrBlocks_new()) == NULL)
|
|
goto err;
|
|
|
|
if (!addr_block_add_addrs(addrs, test->addrs))
|
|
goto err;
|
|
|
|
if (X509v3_addr_is_canonical(addrs) != test->is_canonical) {
|
|
fprintf(stderr, "%s: \"%s\" X509v3_addr_is_canonical not %d\n",
|
|
__func__, test->description, test->is_canonical);
|
|
goto err;
|
|
}
|
|
|
|
if (!X509v3_addr_canonize(addrs)) {
|
|
fprintf(stderr, "%s: \"%s\" failed to canonize\n",
|
|
__func__, test->description);
|
|
goto err;
|
|
}
|
|
|
|
if (!X509v3_addr_is_canonical(addrs)) {
|
|
fprintf(stderr, "%s: \"%s\" canonization wasn't canonical\n",
|
|
__func__, test->description);
|
|
goto err;
|
|
}
|
|
|
|
if ((out_len = i2d_IPAddrBlocks(addrs, &out)) <= 0) {
|
|
fprintf(stderr, "%s: \"%s\" i2d_IPAddrBlocks failed\n",
|
|
__func__, test->description);
|
|
goto err;
|
|
}
|
|
|
|
memcmp_failed = (size_t)out_len != test->der_len;
|
|
if (!memcmp_failed)
|
|
memcmp_failed = memcmp(out, test->der, test->der_len);
|
|
if (memcmp_failed) {
|
|
report_hexdump(__func__, test->description, "memcmp DER failed",
|
|
test->der, test->der_len, out, out_len);
|
|
goto err;
|
|
}
|
|
|
|
if (X509v3_addr_inherits(addrs) != test->inherits) {
|
|
fprintf(stderr, "%s: \"%s\" X509v3_addr_inherits not %d\n",
|
|
__func__, test->description, test->inherits);
|
|
goto err;
|
|
}
|
|
|
|
for (i = 0; i < sk_IPAddressFamily_num(addrs) && i < test->afi_len; i++) {
|
|
IPAddressFamily *family;
|
|
unsigned int afi;
|
|
|
|
family = sk_IPAddressFamily_value(addrs, i);
|
|
|
|
if ((afi = X509v3_addr_get_afi(family)) == 0) {
|
|
fprintf(stderr, "%s: \"%s\" X509v3_addr_get_afi"
|
|
" failed\n", __func__, test->description);
|
|
goto err;
|
|
}
|
|
if (test->afis[i] != afi){
|
|
fprintf(stderr, "%s: \"%s\" afi[%d] mismatch. "
|
|
"want: %u, got: %u\n", __func__,
|
|
test->description, i, test->afis[i], afi);
|
|
goto err;
|
|
}
|
|
}
|
|
if (i != test->afi_len) {
|
|
fprintf(stderr, "%s: \"%s\" checked %d afis, expected %d\n",
|
|
__func__, test->description, i, test->afi_len);
|
|
goto err;
|
|
}
|
|
|
|
p = test->der;
|
|
if ((parsed = d2i_IPAddrBlocks(NULL, &p, test->der_len)) == NULL) {
|
|
fprintf(stderr, "%s: \"%s\" d2i_IPAddrBlocks failed\n",
|
|
__func__, test->description);
|
|
goto err;
|
|
}
|
|
if (!X509v3_addr_is_canonical(parsed)) {
|
|
fprintf(stderr, "%s: \"%s\" parsed AddrBlocks isn't canonical\n",
|
|
__func__, test->description);
|
|
goto err;
|
|
}
|
|
/* Can't compare IPAddrBlocks with inheritance. */
|
|
if (!X509v3_addr_inherits(addrs) && !X509v3_addr_inherits(parsed)) {
|
|
if (!X509v3_addr_subset(addrs, parsed)) {
|
|
fprintf(stderr, "%s: \"%s\" addrs not subset of parsed\n",
|
|
__func__, test->description);
|
|
}
|
|
if (!X509v3_addr_subset(parsed, addrs)) {
|
|
fprintf(stderr, "%s: \"%s\" parsed not subset of addrs\n",
|
|
__func__, test->description);
|
|
}
|
|
}
|
|
|
|
failed = 0;
|
|
|
|
err:
|
|
IPAddrBlocks_free(addrs);
|
|
IPAddrBlocks_free(parsed);
|
|
free(out);
|
|
|
|
return failed;
|
|
}
|
|
|
|
static int
|
|
run_IPAddrBlock_tests(void)
|
|
{
|
|
size_t i;
|
|
int failed = 0;
|
|
|
|
for (i = 0; i < N_BUILD_ADDR_BLOCK_TESTS; i++)
|
|
failed |= build_addr_block_test(&build_addr_block_tests[i]);
|
|
|
|
return failed;
|
|
}
|
|
|
|
struct asid_or_range {
|
|
int type;
|
|
int inherit;
|
|
const unsigned char *min;
|
|
const unsigned char *max;
|
|
};
|
|
|
|
struct ASIdentifiers_build_test {
|
|
const char *description;
|
|
int should_build;
|
|
int inherits;
|
|
int canonical;
|
|
int should_canonize;
|
|
struct asid_or_range delegations[8];
|
|
const unsigned char der[128];
|
|
size_t der_len;
|
|
};
|
|
|
|
/* Sentinel value used for marking the end of the delegations table. */
|
|
#define V3_ASID_END -1
|
|
|
|
const struct ASIdentifiers_build_test ASIdentifiers_build_data[] = {
|
|
{
|
|
.description = "RFC 3779, Appendix C",
|
|
.should_build = 1,
|
|
.inherits = 1,
|
|
.canonical = 1,
|
|
.delegations = {
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "135",
|
|
.max = NULL,
|
|
},
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "3000",
|
|
.max = "3999",
|
|
},
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "5001",
|
|
.max = NULL,
|
|
},
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 1,
|
|
.min = NULL,
|
|
.max = NULL,
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.der = {
|
|
0x30, 0x1a, 0xa0, 0x14, 0x30, 0x12, 0x02, 0x02,
|
|
0x00, 0x87, 0x30, 0x08, 0x02, 0x02, 0x0b, 0xb8,
|
|
0x02, 0x02, 0x0f, 0x9f, 0x02, 0x02, 0x13, 0x89,
|
|
0xa1, 0x02, 0x05, 0x00,
|
|
},
|
|
.der_len = 28,
|
|
},
|
|
{
|
|
.description = "RFC 3779, Appendix C without rdi",
|
|
.should_build = 1,
|
|
.inherits = 0,
|
|
.canonical = 1,
|
|
.delegations = {
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "135",
|
|
.max = NULL,
|
|
},
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "3000",
|
|
.max = "3999",
|
|
},
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "5001",
|
|
.max = NULL,
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.der = {
|
|
0x30, 0x16, 0xa0, 0x14, 0x30, 0x12, 0x02, 0x02,
|
|
0x00, 0x87, 0x30, 0x08, 0x02, 0x02, 0x0b, 0xb8,
|
|
0x02, 0x02, 0x0f, 0x9f, 0x02, 0x02, 0x13, 0x89,
|
|
},
|
|
.der_len = 24,
|
|
},
|
|
{
|
|
.description = "RFC 3779, Appendix C variant",
|
|
.should_build = 1,
|
|
.inherits = 0,
|
|
.canonical = 1,
|
|
.delegations = {
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "135",
|
|
.max = NULL,
|
|
},
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "3000",
|
|
.max = "3999",
|
|
},
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "5001",
|
|
.max = NULL,
|
|
},
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 0,
|
|
.min = "135",
|
|
.max = NULL,
|
|
},
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 0,
|
|
.min = "3000",
|
|
.max = "3999",
|
|
},
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 0,
|
|
.min = "5001",
|
|
.max = NULL,
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.der = {
|
|
0x30, 0x2c, 0xa0, 0x14, 0x30, 0x12, 0x02, 0x02,
|
|
0x00, 0x87, 0x30, 0x08, 0x02, 0x02, 0x0b, 0xb8,
|
|
0x02, 0x02, 0x0f, 0x9f, 0x02, 0x02, 0x13, 0x89,
|
|
0xa1, 0x14, 0x30, 0x12, 0x02, 0x02, 0x00, 0x87,
|
|
0x30, 0x08, 0x02, 0x02, 0x0b, 0xb8, 0x02, 0x02,
|
|
0x0f, 0x9f, 0x02, 0x02, 0x13, 0x89,
|
|
},
|
|
.der_len = 46,
|
|
},
|
|
{
|
|
.description = "inherit only",
|
|
.should_build = 1,
|
|
.inherits = 1,
|
|
.canonical = 1,
|
|
.delegations = {
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 1,
|
|
},
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 1,
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.der = {
|
|
0x30, 0x08, 0xa0, 0x02, 0x05, 0x00, 0xa1, 0x02,
|
|
0x05, 0x00,
|
|
},
|
|
.der_len = 10,
|
|
},
|
|
{
|
|
.description = "adjacent unsorted ranges are merged",
|
|
.should_build = 1,
|
|
.inherits = 0,
|
|
.canonical = 0,
|
|
.should_canonize = 1,
|
|
.delegations = {
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 0,
|
|
.min = "27",
|
|
.max = NULL,
|
|
},
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 0,
|
|
.min = "28",
|
|
.max = "57",
|
|
},
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 0,
|
|
.min = "66",
|
|
.max = "68",
|
|
},
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 0,
|
|
.min = "58",
|
|
.max = "63",
|
|
},
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 0,
|
|
.min = "64",
|
|
.max = NULL,
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.der = {
|
|
0x30, 0x14, 0xa1, 0x12, 0x30, 0x10, 0x30, 0x06,
|
|
0x02, 0x01, 0x1b, 0x02, 0x01, 0x40, 0x30, 0x06,
|
|
0x02, 0x01, 0x42, 0x02, 0x01, 0x44,
|
|
},
|
|
.der_len = 22,
|
|
},
|
|
{
|
|
.description = "range of length 0",
|
|
.should_build = 1,
|
|
.inherits = 1,
|
|
.canonical = 1,
|
|
.should_canonize = 1,
|
|
.delegations = {
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 0,
|
|
.min = "27",
|
|
.max = "27",
|
|
},
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 1,
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.der = {
|
|
0x30, 0x10, 0xa0, 0x02, 0x05, 0x00, 0xa1, 0x0a,
|
|
0x30, 0x08, 0x30, 0x06, 0x02, 0x01, 0x1b, 0x02,
|
|
0x01, 0x1b,
|
|
},
|
|
.der_len = 18,
|
|
},
|
|
{
|
|
.description = "reversed range doesn't canonize",
|
|
.should_build = 1,
|
|
.inherits = 0,
|
|
.canonical = 0,
|
|
.should_canonize = 0,
|
|
.delegations = {
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "57",
|
|
.max = "42",
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
.description = "overlapping ranges don't canonize",
|
|
.should_build = 1,
|
|
.inherits = 0,
|
|
.canonical = 0,
|
|
.should_canonize = 0,
|
|
.delegations = {
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "42",
|
|
.max = "57",
|
|
},
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "57",
|
|
.max = "60",
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
.description = "reversed interior range doesn't canonize",
|
|
.should_build = 1,
|
|
.inherits = 0,
|
|
.canonical = 0,
|
|
.should_canonize = 0,
|
|
.delegations = {
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "1",
|
|
.max = "2",
|
|
},
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "57",
|
|
.max = "42",
|
|
},
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "65523",
|
|
.max = "65535",
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
.description = "can't inherit and add AS ids",
|
|
.should_build = 0,
|
|
.inherits = 0,
|
|
.canonical = 0,
|
|
.should_canonize = 0,
|
|
.delegations = {
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "1",
|
|
.max = "2",
|
|
},
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 1,
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
.description = "can't inherit and add rdis",
|
|
.should_build = 0,
|
|
.inherits = 0,
|
|
.canonical = 0,
|
|
.should_canonize = 0,
|
|
.delegations = {
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 0,
|
|
.min = "1",
|
|
.max = "2",
|
|
},
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 1,
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
const size_t N_ASIDENTIFIERS_BUILD_TESTS =
|
|
sizeof(ASIdentifiers_build_data) / sizeof(ASIdentifiers_build_data[0]);
|
|
|
|
static int
|
|
add_as_delegation(ASIdentifiers *asid, const struct asid_or_range *delegation)
|
|
{
|
|
ASN1_INTEGER *min = NULL, *max = NULL;
|
|
int ret = 0;
|
|
|
|
if (delegation->inherit)
|
|
return X509v3_asid_add_inherit(asid, delegation->type);
|
|
|
|
if ((min = s2i_ASN1_INTEGER(NULL, delegation->min)) == NULL)
|
|
goto err;
|
|
|
|
if (delegation->max != NULL) {
|
|
if ((max = s2i_ASN1_INTEGER(NULL, delegation->max)) == NULL)
|
|
goto err;
|
|
}
|
|
|
|
if (!X509v3_asid_add_id_or_range(asid, delegation->type, min, max))
|
|
goto err;
|
|
min = NULL;
|
|
max = NULL;
|
|
|
|
ret = 1;
|
|
|
|
err:
|
|
ASN1_INTEGER_free(min);
|
|
ASN1_INTEGER_free(max);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static ASIdentifiers *
|
|
build_asid(const struct asid_or_range delegations[])
|
|
{
|
|
ASIdentifiers *asid = NULL;
|
|
const struct asid_or_range *delegation;
|
|
|
|
if ((asid = ASIdentifiers_new()) == NULL)
|
|
goto err;
|
|
|
|
for (delegation = &delegations[0]; delegation->type != V3_ASID_END;
|
|
delegation++) {
|
|
if (!add_as_delegation(asid, delegation))
|
|
goto err;
|
|
}
|
|
|
|
return asid;
|
|
|
|
err:
|
|
ASIdentifiers_free(asid);
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
build_asid_test(const struct ASIdentifiers_build_test *test)
|
|
{
|
|
ASIdentifiers *asid = NULL;
|
|
unsigned char *out = NULL;
|
|
int out_len;
|
|
int memcmp_failed = 1;
|
|
int failed = 1;
|
|
|
|
if ((asid = build_asid(test->delegations)) == NULL) {
|
|
if (!test->should_build) {
|
|
failed = 0;
|
|
return failed;
|
|
}
|
|
fprintf(stderr, "%s: \"%s\" failed to build\n", __func__,
|
|
test->description);
|
|
return failed;
|
|
}
|
|
|
|
if (!test->canonical) {
|
|
if (X509v3_asid_is_canonical(asid)) {
|
|
fprintf(stderr, "%s: \"%s\" shouldn't be canonical\n",
|
|
__func__, test->description);
|
|
goto err;
|
|
}
|
|
if (X509v3_asid_canonize(asid) != test->should_canonize) {
|
|
fprintf(stderr, "%s: \"%s\" failed to canonize\n",
|
|
__func__, test->description);
|
|
goto err;
|
|
}
|
|
if (!test->should_canonize) {
|
|
failed = 0;
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Verify that asid is in canonical form before converting it to DER.
|
|
*/
|
|
if (!X509v3_asid_is_canonical(asid)) {
|
|
fprintf(stderr, "%s: asid is not canonical\n", __func__);
|
|
goto err;
|
|
}
|
|
|
|
/*
|
|
* Convert asid to DER and check that it matches expectations
|
|
*/
|
|
out = NULL;
|
|
if ((out_len = i2d_ASIdentifiers(asid, &out)) <= 0) {
|
|
fprintf(stderr, "%s: \"%s\" i2d_ASIdentifiers failed\n",
|
|
__func__, test->description);
|
|
goto err;
|
|
}
|
|
|
|
|
|
memcmp_failed = (size_t)out_len != test->der_len;
|
|
if (!memcmp_failed)
|
|
memcmp_failed = memcmp(out, test->der, test->der_len);
|
|
if (memcmp_failed) {
|
|
report_hexdump(__func__, test->description, "memcmp DER failed",
|
|
test->der, test->der_len, out, out_len);
|
|
goto err;
|
|
}
|
|
|
|
/*
|
|
* Verify that asid inherits as expected
|
|
*/
|
|
if (X509v3_asid_inherits(asid) != test->inherits) {
|
|
fprintf(stderr, "%s: \"%s\" unexpected asid inherit %d\n",
|
|
__func__, test->description, test->inherits);
|
|
goto err;
|
|
}
|
|
|
|
failed = 0;
|
|
|
|
err:
|
|
free(out);
|
|
ASIdentifiers_free(asid);
|
|
|
|
return failed;
|
|
}
|
|
|
|
static int
|
|
run_ASIdentifiers_build_test(void)
|
|
{
|
|
size_t i;
|
|
int failed = 0;
|
|
|
|
for (i = 0; i < N_ASIDENTIFIERS_BUILD_TESTS; i++)
|
|
failed |= build_asid_test(&ASIdentifiers_build_data[i]);
|
|
|
|
return failed;
|
|
}
|
|
|
|
struct ASIdentifiers_subset_test {
|
|
const char *description;
|
|
struct asid_or_range delegationsA[8];
|
|
struct asid_or_range delegationsB[8];
|
|
int is_subset;
|
|
int is_subset_if_canonized;
|
|
};
|
|
|
|
const struct ASIdentifiers_subset_test ASIdentifiers_subset_data[] = {
|
|
{
|
|
.description = "simple subset relation",
|
|
.delegationsA = {
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "2",
|
|
.max = "4",
|
|
},
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 0,
|
|
.min = "2",
|
|
.max = NULL,
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.delegationsB = {
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "1",
|
|
.max = "5",
|
|
},
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 0,
|
|
.min = "1",
|
|
.max = "5",
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.is_subset = 1,
|
|
.is_subset_if_canonized = 1,
|
|
},
|
|
{
|
|
.description = "only asnums",
|
|
.delegationsA = {
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "2",
|
|
.max = "4",
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.delegationsB = {
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "1",
|
|
.max = "5",
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.is_subset = 1,
|
|
.is_subset_if_canonized = 1,
|
|
},
|
|
{
|
|
.description = "only rdis",
|
|
.delegationsA = {
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 0,
|
|
.min = "2",
|
|
.max = NULL,
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.delegationsB = {
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 0,
|
|
.min = "1",
|
|
.max = "5",
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.is_subset = 1,
|
|
.is_subset_if_canonized = 1,
|
|
},
|
|
{
|
|
.description = "child only has asnums, parent only has rdis",
|
|
.delegationsA = {
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "2",
|
|
.max = "4",
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.delegationsB = {
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 0,
|
|
.min = "1",
|
|
.max = "5",
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.is_subset = 0,
|
|
.is_subset_if_canonized = 0,
|
|
},
|
|
{
|
|
.description = "child only has rdis, parent only has asnums",
|
|
.delegationsA = {
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 0,
|
|
.min = "2",
|
|
.max = "4",
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.delegationsB = {
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "1",
|
|
.max = "5",
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.is_subset = 0,
|
|
.is_subset_if_canonized = 0,
|
|
},
|
|
{
|
|
.description = "child only has rdis, parent has both",
|
|
.delegationsA = {
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 0,
|
|
.min = "2",
|
|
.max = "4",
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.delegationsB = {
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "1",
|
|
.max = "5",
|
|
},
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 0,
|
|
.min = "1",
|
|
.max = "5",
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.is_subset = 1,
|
|
.is_subset_if_canonized = 1,
|
|
},
|
|
{
|
|
.description = "subset relation only after canonization",
|
|
.delegationsA = {
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "2",
|
|
.max = NULL,
|
|
},
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "3",
|
|
.max = "4",
|
|
},
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 0,
|
|
.min = "2",
|
|
.max = NULL,
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.delegationsB = {
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "1",
|
|
.max = "3",
|
|
},
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "4",
|
|
.max = "5",
|
|
},
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 0,
|
|
.min = "1",
|
|
.max = "5",
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.is_subset = 0,
|
|
.is_subset_if_canonized = 1,
|
|
},
|
|
{
|
|
.description = "no subset if A inherits",
|
|
.delegationsA = {
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "2",
|
|
.max = NULL,
|
|
},
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "3",
|
|
.max = "4",
|
|
},
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 1,
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.delegationsB = {
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "1",
|
|
.max = "3",
|
|
},
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "4",
|
|
.max = "5",
|
|
},
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 0,
|
|
.min = "1",
|
|
.max = "5",
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.is_subset = 0,
|
|
.is_subset_if_canonized = 0,
|
|
},
|
|
{
|
|
.description = "no subset if B inherits",
|
|
.delegationsA = {
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "2",
|
|
.max = NULL,
|
|
},
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "3",
|
|
.max = "4",
|
|
},
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 0,
|
|
.min = "5",
|
|
.max = NULL,
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.delegationsB = {
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "1",
|
|
.max = "3",
|
|
},
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "4",
|
|
.max = "5",
|
|
},
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 1,
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.is_subset = 0,
|
|
.is_subset_if_canonized = 0,
|
|
},
|
|
{
|
|
.description = "no subset if both inherit",
|
|
.delegationsA = {
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "2",
|
|
.max = NULL,
|
|
},
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "3",
|
|
.max = "4",
|
|
},
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 1,
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.delegationsB = {
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "1",
|
|
.max = "3",
|
|
},
|
|
{
|
|
.type = V3_ASID_ASNUM,
|
|
.inherit = 0,
|
|
.min = "4",
|
|
.max = "5",
|
|
},
|
|
{
|
|
.type = V3_ASID_RDI,
|
|
.inherit = 1,
|
|
},
|
|
{
|
|
.type = V3_ASID_END,
|
|
},
|
|
},
|
|
.is_subset = 0,
|
|
.is_subset_if_canonized = 0,
|
|
},
|
|
};
|
|
|
|
const size_t N_ASIDENTIFIERS_SUBSET_TESTS =
|
|
sizeof(ASIdentifiers_subset_data) / sizeof(ASIdentifiers_subset_data[0]);
|
|
|
|
static int
|
|
asid_subset_test(const struct ASIdentifiers_subset_test *test)
|
|
{
|
|
ASIdentifiers *asidA = NULL, *asidB = NULL;
|
|
int failed = 0;
|
|
|
|
if ((asidA = build_asid(test->delegationsA)) == NULL)
|
|
goto err;
|
|
if ((asidB = build_asid(test->delegationsB)) == NULL)
|
|
goto err;
|
|
|
|
if (X509v3_asid_subset(asidA, asidB) != test->is_subset) {
|
|
fprintf(stderr, "%s: \"%s\" X509v3_asid_subset failed\n",
|
|
__func__, test->description);
|
|
failed = 1;
|
|
}
|
|
|
|
if (!test->is_subset) {
|
|
if (!X509v3_asid_canonize(asidA))
|
|
goto err;
|
|
if (!X509v3_asid_canonize(asidB))
|
|
goto err;
|
|
if (X509v3_asid_subset(asidA, asidB) !=
|
|
test->is_subset_if_canonized) {
|
|
fprintf(stderr, "%s: \"%s\" canonized subset failed\n",
|
|
__func__, test->description);
|
|
failed = 1;
|
|
}
|
|
}
|
|
|
|
err:
|
|
ASIdentifiers_free(asidA);
|
|
ASIdentifiers_free(asidB);
|
|
|
|
return failed;
|
|
}
|
|
|
|
static int
|
|
run_ASIdentifiers_subset_test(void)
|
|
{
|
|
size_t i;
|
|
int failed = 0;
|
|
|
|
for (i = 0; i < N_ASIDENTIFIERS_SUBSET_TESTS; i++)
|
|
failed |= asid_subset_test(&ASIdentifiers_subset_data[i]);
|
|
|
|
return failed;
|
|
}
|
|
|
|
int
|
|
main(void)
|
|
{
|
|
int failed = 0;
|
|
|
|
failed |= run_IPAddressOrRange_tests();
|
|
failed |= run_IPAddrBlock_tests();
|
|
failed |= run_ASIdentifiers_build_test();
|
|
failed |= run_ASIdentifiers_subset_test();
|
|
|
|
return failed;
|
|
}
|