#include <sys/socket.h>
#include <arpa/inet.h>
#include <assert.h>
#include <err.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "extern.h"
#define PREFIX_SIZE(x) (((x) + 7) / 8)
int
ip_addr_afi_parse(const char *fn, const ASN1_OCTET_STRING *astr, enum afi *afi)
{
const unsigned char *buf;
int len;
uint16_t v;
buf = ASN1_STRING_get0_data(astr);
len = ASN1_STRING_length(astr);
if (len == 3) {
warnx("%s: SAFI not allowed", fn);
return 0;
}
if (len != sizeof(v)) {
warnx("%s: invalid AFI length, want 2, have %d", fn, len);
return 0;
}
memcpy(&v, buf, sizeof(v));
v = ntohs(v);
if (v != AFI_IPV4 && v != AFI_IPV6) {
warnx("%s: only AFI for IPV4 (1) and IPV6 (2) allowed: "
"have %hd", fn, v);
return 0;
}
*afi = v;
return 1;
}
int
ip_addr_check_covered(enum afi afi,
const unsigned char *min, const unsigned char *max,
const struct cert_ip *ips, size_t num_ips)
{
size_t i, sz = AFI_IPV4 == afi ? 4 : 16;
for (i = 0; i < num_ips; i++) {
if (ips[i].afi != afi)
continue;
if (ips[i].type == CERT_IP_INHERIT)
return 0;
if (memcmp(ips[i].min, min, sz) <= 0 &&
memcmp(ips[i].max, max, sz) >= 0)
return 1;
}
return -1;
}
int
ip_addr_check_overlap(const struct cert_ip *ip, const char *fn,
const struct cert_ip *ips, size_t num_ips, int quiet)
{
size_t i, sz = ip->afi == AFI_IPV4 ? 4 : 16;
int inherit_v4 = 0, inherit_v6 = 0;
int has_v4 = 0, has_v6 = 0;
for (i = 0; i < num_ips; i++)
if (ips[i].type == CERT_IP_INHERIT) {
if (ips[i].afi == AFI_IPV4)
inherit_v4 = 1;
else
inherit_v6 = 1;
} else {
if (ips[i].afi == AFI_IPV4)
has_v4 = 1;
else
has_v6 = 1;
}
if ((inherit_v4 && ip->afi == AFI_IPV4) ||
(inherit_v6 && ip->afi == AFI_IPV6) ||
(has_v4 && ip->afi == AFI_IPV4 &&
ip->type == CERT_IP_INHERIT) ||
(has_v6 && ip->afi == AFI_IPV6 &&
ip->type == CERT_IP_INHERIT)) {
if (!quiet) {
warnx("%s: RFC 3779 section 2.2.3.5: "
"cannot have multiple inheritance or inheritance "
"and addresses of the same class", fn);
}
return 0;
}
for (i = 0; i < num_ips; i++) {
if (ips[i].afi != ip->afi)
continue;
if (memcmp(ips[i].max, ip->min, sz) < 0 ||
memcmp(ips[i].min, ip->max, sz) > 0)
continue;
if (!quiet) {
warnx("%s: RFC 3779 section 2.2.3.5: "
"cannot have overlapping IP addresses", fn);
ip_warn(fn, "certificate IP", ip);
ip_warn(fn, "offending IP", &ips[i]);
}
return 0;
}
return 1;
}
int
ip_addr_parse(const ASN1_BIT_STRING *abs,
enum afi afi, const char *fn, struct ip_addr *addr)
{
const unsigned char *data;
size_t length;
int unused = 0;
data = ASN1_STRING_get0_data(abs);
if (!ASN1_BIT_STRING_get_length(abs, &length, &unused)) {
warnx("%s: invalid bit string in IP address", fn);
return 0;
}
if (length == 0 && unused != 0) {
warnx("%s: RFC 3779 section 2.2.3.8: "
"unused bit count must be zero if length is zero", fn);
return 0;
}
if (length != 0 && (data[length - 1] & ((1 << unused) - 1))) {
warnx("%s: RFC 3779 section 2.2.3.8: "
"unused bits must be set to zero", fn);
return 0;
}
if ((afi == AFI_IPV4 && length > 4) ||
(afi == AFI_IPV6 && length > 16)) {
warnx("%s: RFC 3779 section 2.2.3.8: "
"IP address too long", fn);
return 0;
}
memset(addr, 0, sizeof(struct ip_addr));
addr->prefixlen = length * 8 - unused;
memcpy(addr->addr, data, length);
return 1;
}
void
ip_addr_print(const struct ip_addr *addr,
enum afi afi, char *buf, size_t bufsz)
{
char ipbuf[INET6_ADDRSTRLEN];
int ret, af;
switch (afi) {
case AFI_IPV4:
af = AF_INET;
break;
case AFI_IPV6:
af = AF_INET6;
break;
default:
errx(1, "unsupported address family identifier");
}
if (inet_ntop(af, addr->addr, ipbuf, sizeof(ipbuf)) == NULL)
err(1, "inet_ntop");
ret = snprintf(buf, bufsz, "%s/%hhu", ipbuf, addr->prefixlen);
if (ret < 0 || (size_t)ret >= bufsz)
err(1, "malformed IP address");
}
static void
ip_addr_range_print(const struct ip_addr_range *range,
enum afi afi, char *buf, size_t bufsz)
{
struct cert_ip ip;
char min[INET6_ADDRSTRLEN], max[INET6_ADDRSTRLEN];
int ret, af;
switch (afi) {
case AFI_IPV4:
af = AF_INET;
break;
case AFI_IPV6:
af = AF_INET6;
break;
default:
errx(1, "unsupported address family identifier");
}
memset(&ip, 0, sizeof(ip));
ip.afi = afi;
ip.type = CERT_IP_RANGE;
ip.range = *range;
if (!ip_cert_compose_ranges(&ip))
errx(1, "failed to compose ranges");
if (inet_ntop(af, ip.min, min, sizeof(min)) == NULL)
err(1, "inet_ntop");
if (inet_ntop(af, ip.max, max, sizeof(max)) == NULL)
err(1, "inet_ntop");
ret = snprintf(buf, bufsz, "%s--%s", min, max);
if (ret < 0 || (size_t)ret >= bufsz)
err(1, "malformed IP address");
}
int
ip_cert_compose_ranges(struct cert_ip *p)
{
size_t sz;
switch (p->type) {
case CERT_IP_ADDR:
sz = PREFIX_SIZE(p->ip.prefixlen);
memset(p->min, 0x0, sizeof(p->min));
memcpy(p->min, p->ip.addr, sz);
memset(p->max, 0xff, sizeof(p->max));
memcpy(p->max, p->ip.addr, sz);
if (sz > 0 && p->ip.prefixlen % 8 != 0)
p->max[sz - 1] |= (1 << (8 - p->ip.prefixlen % 8)) - 1;
break;
case CERT_IP_RANGE:
memset(p->min, 0x0, sizeof(p->min));
sz = PREFIX_SIZE(p->range.min.prefixlen);
memcpy(p->min, p->range.min.addr, sz);
memset(p->max, 0xff, sizeof(p->max));
sz = PREFIX_SIZE(p->range.max.prefixlen);
memcpy(p->max, p->range.max.addr, sz);
if (sz > 0 && p->range.max.prefixlen % 8 != 0)
p->max[sz - 1] |=
(1 << (8 - p->range.max.prefixlen % 8)) - 1;
break;
default:
return 1;
}
sz = p->afi == AFI_IPV4 ? 4 : 16;
return memcmp(p->min, p->max, sz) <= 0;
}
void
ip_roa_compose_ranges(struct roa_ip *p)
{
size_t sz = PREFIX_SIZE(p->addr.prefixlen);
memset(p->min, 0x0, sizeof(p->min));
memcpy(p->min, p->addr.addr, sz);
memset(p->max, 0xff, sizeof(p->max));
memcpy(p->max, p->addr.addr, sz);
if (sz > 0 && p->addr.prefixlen % 8 != 0)
p->max[sz - 1] |= (1 << (8 - p->addr.prefixlen % 8)) - 1;
}
void
ip_warn(const char *fn, const char *msg, const struct cert_ip *ip)
{
char buf[128];
switch (ip->type) {
case CERT_IP_ADDR:
ip_addr_print(&ip->ip, ip->afi, buf, sizeof(buf));
warnx("%s: %s: %s", fn, msg, buf);
break;
case CERT_IP_RANGE:
ip_addr_range_print(&ip->range, ip->afi, buf, sizeof(buf));
warnx("%s: %s: %s", fn, msg, buf);
break;
case CERT_IP_INHERIT:
warnx("%s: %s: IP (inherit)", fn, msg);
break;
default:
warnx("%s: corrupt cert", fn);
break;
}
}
static int
append_ip(const char *fn, struct cert_ip *ips, size_t *num_ips,
const struct cert_ip *ip)
{
if (!ip_addr_check_overlap(ip, fn, ips, *num_ips, 0))
return 0;
ips[(*num_ips)++] = *ip;
return 1;
}
int
sbgp_addr(const char *fn, struct cert_ip *ips, size_t *num_ips, 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, num_ips, &ip);
}
int
sbgp_addr_range(const char *fn, struct cert_ip *ips, size_t *num_ips,
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, num_ips, &ip);
}
static int
sbgp_addr_inherit(const char *fn, struct cert_ip *ips, size_t *num_ips,
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, num_ips, &ip);
}
int
sbgp_parse_ipaddrblocks(const char *fn, const IPAddrBlocks *addrs,
struct cert_ip **out_ips, size_t *out_num_ips)
{
const IPAddressFamily *af;
const IPAddressOrRanges *aors;
const IPAddressOrRange *aor;
enum afi afi;
struct cert_ip *ips = NULL;
size_t num_ips = 0, num;
int ipv4_seen = 0, ipv6_seen = 0;
int i, j, addrsz;
assert(*out_ips == NULL && *out_num_ips == 0);
addrsz = sk_IPAddressFamily_num(addrs);
if (addrsz != 1 && addrsz != 2) {
warnx("%s: RFC 6487 section 4.8.10: unexpected number of "
"ipAddrBlocks (got %d, expected 1 or 2)", fn, addrsz);
goto out;
}
for (i = 0; i < addrsz; i++) {
af = sk_IPAddressFamily_value(addrs, i);
switch (af->ipAddressChoice->type) {
case IPAddressChoice_inherit:
aors = NULL;
num = num_ips + 1;
break;
case IPAddressChoice_addressesOrRanges:
aors = af->ipAddressChoice->u.addressesOrRanges;
num = num_ips + sk_IPAddressOrRange_num(aors);
break;
default:
warnx("%s: RFC 3779: IPAddressChoice: unknown type %d",
fn, af->ipAddressChoice->type);
goto out;
}
if (num == num_ips) {
warnx("%s: RFC 6487 section 4.8.10: "
"empty ipAddressesOrRanges", fn);
goto out;
}
if (num >= MAX_IP_SIZE)
goto out;
ips = recallocarray(ips, num_ips, num, sizeof(struct cert_ip));
if (ips == NULL)
err(1, NULL);
if (!ip_addr_afi_parse(fn, af->addressFamily, &afi)) {
warnx("%s: RFC 3779: invalid AFI", fn);
goto out;
}
switch (afi) {
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;
}
if (aors == NULL) {
if (!sbgp_addr_inherit(fn, ips, &num_ips, afi))
goto out;
continue;
}
for (j = 0; j < sk_IPAddressOrRange_num(aors); j++) {
aor = sk_IPAddressOrRange_value(aors, j);
switch (aor->type) {
case IPAddressOrRange_addressPrefix:
if (!sbgp_addr(fn, ips, &num_ips, afi,
aor->u.addressPrefix))
goto out;
break;
case IPAddressOrRange_addressRange:
if (!sbgp_addr_range(fn, ips, &num_ips, afi,
aor->u.addressRange))
goto out;
break;
default:
warnx("%s: RFC 3779: IPAddressOrRange: "
"unknown type %d", fn, aor->type);
goto out;
}
}
}
*out_ips = ips;
*out_num_ips = num_ips;
return 1;
out:
free(ips);
return 0;
}