xref: /openbsd-src/usr.sbin/rpki-client/ip.c (revision 381ee5995072b060a824e758e1421220f03cdcc0)
1*381ee599Stb /*	$OpenBSD: ip.c,v 1.34 2024/11/12 09:23:07 tb Exp $ */
29a7e9e7fSjob /*
39a7e9e7fSjob  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
49a7e9e7fSjob  *
59a7e9e7fSjob  * Permission to use, copy, modify, and distribute this software for any
69a7e9e7fSjob  * purpose with or without fee is hereby granted, provided that the above
79a7e9e7fSjob  * copyright notice and this permission notice appear in all copies.
89a7e9e7fSjob  *
99a7e9e7fSjob  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
109a7e9e7fSjob  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
119a7e9e7fSjob  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
129a7e9e7fSjob  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
139a7e9e7fSjob  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
149a7e9e7fSjob  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
159a7e9e7fSjob  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
169a7e9e7fSjob  */
179a7e9e7fSjob 
189a7e9e7fSjob #include <sys/socket.h>
1928d71d25Sderaadt #include <arpa/inet.h>
209a7e9e7fSjob 
219a7e9e7fSjob #include <err.h>
229a7e9e7fSjob #include <stdlib.h>
239a7e9e7fSjob #include <string.h>
249a7e9e7fSjob #include <unistd.h>
259a7e9e7fSjob 
269a7e9e7fSjob #include "extern.h"
279a7e9e7fSjob 
2822bf1969Sclaudio #define	PREFIX_SIZE(x)	(((x) + 7) / 8)
2922bf1969Sclaudio 
309a7e9e7fSjob /*
319a7e9e7fSjob  * Parse an IP address family.
329a7e9e7fSjob  * This is defined in different places in the ROA/X509 standards, but
339a7e9e7fSjob  * it's the same thing.
349a7e9e7fSjob  * We prohibit all but IPv4 and IPv6, without SAFI.
359a7e9e7fSjob  * Return zero on failure, non-zero on success.
369a7e9e7fSjob  */
379a7e9e7fSjob int
3880272c49Sderaadt ip_addr_afi_parse(const char *fn, const ASN1_OCTET_STRING *p, enum afi *afi)
399a7e9e7fSjob {
40fac5751dSclaudio 	uint16_t v;
419a7e9e7fSjob 
429a7e9e7fSjob 	if (p->length == 0 || p->length > 3) {
4380272c49Sderaadt 		warnx("%s: invalid field length, want 1--3, have %d",
4480272c49Sderaadt 		    fn, p->length);
459a7e9e7fSjob 		return 0;
469a7e9e7fSjob 	}
479a7e9e7fSjob 
48fac5751dSclaudio 	memcpy(&v, p->data, sizeof(v));
49fac5751dSclaudio 	v = ntohs(v);
509a7e9e7fSjob 
519a7e9e7fSjob 	/* Only accept IPv4 and IPv6 AFIs. */
529a7e9e7fSjob 
5322bf1969Sclaudio 	if (v != AFI_IPV4 && v != AFI_IPV6) {
5422bf1969Sclaudio 		warnx("%s: only AFI for IPV4 (1) and IPV6 (2) allowed: "
5522bf1969Sclaudio 		    "have %hd", fn, v);
569a7e9e7fSjob 		return 0;
579a7e9e7fSjob 	}
589a7e9e7fSjob 
599a7e9e7fSjob 	/* Disallow the optional SAFI. */
609a7e9e7fSjob 
619a7e9e7fSjob 	if (p->length == 3) {
629a7e9e7fSjob 		warnx("%s: SAFI not allowed", fn);
639a7e9e7fSjob 		return 0;
649a7e9e7fSjob 	}
659a7e9e7fSjob 
6622bf1969Sclaudio 	*afi = v;
679a7e9e7fSjob 	return 1;
689a7e9e7fSjob }
699a7e9e7fSjob 
709a7e9e7fSjob /*
719a7e9e7fSjob  * See if a given IP prefix is covered by the IP prefixes or ranges
729a7e9e7fSjob  * specified in the "ips" array.
739a7e9e7fSjob  * This means that the IP prefix must be strictly within the ranges or
749a7e9e7fSjob  * singletons given in the array.
75335482abStb  * Return 0 if we're inheriting from the issuer, >0 if we're covered,
769a7e9e7fSjob  * or <0 if we're not covered.
779a7e9e7fSjob  */
789a7e9e7fSjob int
799a7e9e7fSjob ip_addr_check_covered(enum afi afi,
809a7e9e7fSjob     const unsigned char *min, const unsigned char *max,
81*381ee599Stb     const struct cert_ip *ips, size_t num_ips)
829a7e9e7fSjob {
839a7e9e7fSjob 	size_t	 i, sz = AFI_IPV4 == afi ? 4 : 16;
849a7e9e7fSjob 
85*381ee599Stb 	for (i = 0; i < num_ips; i++) {
869a7e9e7fSjob 		if (ips[i].afi != afi)
879a7e9e7fSjob 			continue;
889a7e9e7fSjob 		if (ips[i].type == CERT_IP_INHERIT)
899a7e9e7fSjob 			return 0;
909a7e9e7fSjob 		if (memcmp(ips[i].min, min, sz) <= 0 &&
919a7e9e7fSjob 		    memcmp(ips[i].max, max, sz) >= 0)
929a7e9e7fSjob 			return 1;
939a7e9e7fSjob 	}
949a7e9e7fSjob 
959a7e9e7fSjob 	return -1;
969a7e9e7fSjob }
979a7e9e7fSjob 
989a7e9e7fSjob /*
999a7e9e7fSjob  * Given a newly-parsed IP address or range "ip", make sure that "ip"
1009a7e9e7fSjob  * does not overlap with any addresses or ranges in the "ips" array.
1019a7e9e7fSjob  * This is defined by RFC 3779 section 2.2.3.6.
1029a7e9e7fSjob  * Returns zero on failure, non-zero on success.
1039a7e9e7fSjob  */
1049a7e9e7fSjob int
1059a7e9e7fSjob ip_addr_check_overlap(const struct cert_ip *ip, const char *fn,
106*381ee599Stb     const struct cert_ip *ips, size_t num_ips, int quiet)
1079a7e9e7fSjob {
10860b5b204Sderaadt 	size_t	 i, sz = ip->afi == AFI_IPV4 ? 4 : 16;
10980272c49Sderaadt 	int	 inherit_v4 = 0, inherit_v6 = 0;
1108b5bbbc5Stb 	int	 has_v4 = 0, has_v6 = 0;
1119a7e9e7fSjob 
1129a7e9e7fSjob 	/*
1139a7e9e7fSjob 	 * FIXME: cache this by having a flag on the cert_ip, else we're
1149a7e9e7fSjob 	 * going to need to do a lot of scanning for big allocations.
1159a7e9e7fSjob 	 */
1169a7e9e7fSjob 
117*381ee599Stb 	for (i = 0; i < num_ips; i++)
1189a7e9e7fSjob 		if (ips[i].type == CERT_IP_INHERIT) {
1199a7e9e7fSjob 			if (ips[i].afi == AFI_IPV4)
1209a7e9e7fSjob 				inherit_v4 = 1;
1219a7e9e7fSjob 			else
1229a7e9e7fSjob 				inherit_v6 = 1;
1239a7e9e7fSjob 		} else {
1249a7e9e7fSjob 			if (ips[i].afi == AFI_IPV4)
1259a7e9e7fSjob 				has_v4 = 1;
1269a7e9e7fSjob 			else
1279a7e9e7fSjob 				has_v6 = 1;
1289a7e9e7fSjob 		}
1299a7e9e7fSjob 
1306822deefStb 	/* Disallow multiple inheritance per type. */
1319a7e9e7fSjob 
1329a7e9e7fSjob 	if ((inherit_v4 && ip->afi == AFI_IPV4) ||
1339a7e9e7fSjob 	    (inherit_v6 && ip->afi == AFI_IPV6) ||
1349a7e9e7fSjob 	    (has_v4 && ip->afi == AFI_IPV4 &&
1359a7e9e7fSjob 	     ip->type == CERT_IP_INHERIT) ||
1369a7e9e7fSjob 	    (has_v6 && ip->afi == AFI_IPV6 &&
1379a7e9e7fSjob 	     ip->type == CERT_IP_INHERIT)) {
1388b5bbbc5Stb 		if (!quiet) {
13980272c49Sderaadt 			warnx("%s: RFC 3779 section 2.2.3.5: "
1408b5bbbc5Stb 			    "cannot have multiple inheritance or inheritance "
1418b5bbbc5Stb 			    "and addresses of the same class", fn);
1428b5bbbc5Stb 		}
1439a7e9e7fSjob 		return 0;
1449a7e9e7fSjob 	}
1459a7e9e7fSjob 
1469a7e9e7fSjob 	/* Check our ranges. */
1479a7e9e7fSjob 
148*381ee599Stb 	for (i = 0; i < num_ips; i++) {
1499a7e9e7fSjob 		if (ips[i].afi != ip->afi)
1509a7e9e7fSjob 			continue;
1519a7e9e7fSjob 		if (memcmp(ips[i].max, ip->min, sz) <= 0 ||
1529a7e9e7fSjob 		    memcmp(ips[i].min, ip->max, sz) >= 0)
1539a7e9e7fSjob 			continue;
1548b5bbbc5Stb 		if (!quiet) {
1559a7e9e7fSjob 			warnx("%s: RFC 3779 section 2.2.3.5: "
1569a7e9e7fSjob 			    "cannot have overlapping IP addresses", fn);
1574032f119Stb 			ip_warn(fn, "certificate IP", ip);
1584032f119Stb 			ip_warn(fn, "offending IP", &ips[i]);
1598b5bbbc5Stb 		}
1609a7e9e7fSjob 		return 0;
1619a7e9e7fSjob 	}
1629a7e9e7fSjob 
1639a7e9e7fSjob 	return 1;
1649a7e9e7fSjob }
1659a7e9e7fSjob 
1669a7e9e7fSjob /*
1679a7e9e7fSjob  * Parse an IP address, RFC 3779, 2.2.3.8.
1689a7e9e7fSjob  * Return zero on failure, non-zero on success.
1699a7e9e7fSjob  */
1709a7e9e7fSjob int
1719a7e9e7fSjob ip_addr_parse(const ASN1_BIT_STRING *p,
1729a7e9e7fSjob     enum afi afi, const char *fn, struct ip_addr *addr)
1739a7e9e7fSjob {
1749a7e9e7fSjob 	long	 unused = 0;
1759a7e9e7fSjob 
1769a7e9e7fSjob 	/* Weird OpenSSL-ism to get unused bit count. */
1779a7e9e7fSjob 
17822bf1969Sclaudio 	if ((p->flags & ASN1_STRING_FLAG_BITS_LEFT))
1799eb589bcStb 		unused = p->flags & 0x07;
1809a7e9e7fSjob 
1819eb589bcStb 	if (p->length == 0 && unused != 0) {
1829ddda1aeSclaudio 		warnx("%s: RFC 3779 section 2.2.3.8: "
1839ddda1aeSclaudio 		    "unused bit count must be zero if length is zero", fn);
1849ddda1aeSclaudio 		return 0;
1859a7e9e7fSjob 	}
1869a7e9e7fSjob 
1879a7e9e7fSjob 	/*
1889a7e9e7fSjob 	 * Check that the unused bits are set to zero.
1899a7e9e7fSjob 	 * If we don't do this, stray bits will corrupt our composition
1909a7e9e7fSjob 	 * of the [minimum] address ranges.
1919a7e9e7fSjob 	 */
1929a7e9e7fSjob 
19322bf1969Sclaudio 	if (p->length != 0 &&
1949a7e9e7fSjob 	    (p->data[p->length - 1] & ((1 << unused) - 1))) {
19580272c49Sderaadt 		warnx("%s: RFC 3779 section 2.2.3.8: "
19680272c49Sderaadt 		    "unused bits must be set to zero", fn);
1979a7e9e7fSjob 		return 0;
1989a7e9e7fSjob 	}
1999a7e9e7fSjob 
2009a7e9e7fSjob 	/* Limit possible sizes of addresses. */
2019a7e9e7fSjob 
2029a7e9e7fSjob 	if ((afi == AFI_IPV4 && p->length > 4) ||
2039a7e9e7fSjob 	    (afi == AFI_IPV6 && p->length > 16)) {
2049a7e9e7fSjob 		warnx("%s: RFC 3779 section 2.2.3.8: "
2059a7e9e7fSjob 		    "IP address too long", fn);
2069a7e9e7fSjob 		return 0;
2079a7e9e7fSjob 	}
2089a7e9e7fSjob 
20900a1e1b3Sclaudio 	memset(addr, 0, sizeof(struct ip_addr));
21022bf1969Sclaudio 	addr->prefixlen = p->length * 8 - unused;
2119a7e9e7fSjob 	memcpy(addr->addr, p->data, p->length);
2129a7e9e7fSjob 	return 1;
2139a7e9e7fSjob }
2149a7e9e7fSjob 
2159a7e9e7fSjob /*
2169a7e9e7fSjob  * Convert a ip_addr into a NUL-terminated CIDR notation string
2179a7e9e7fSjob  * conforming to RFC 4632 or 4291.
2189a7e9e7fSjob  * The size of the buffer must be at least 64 (inclusive).
2199a7e9e7fSjob  */
2209a7e9e7fSjob void
2219a7e9e7fSjob ip_addr_print(const struct ip_addr *addr,
2229a7e9e7fSjob     enum afi afi, char *buf, size_t bufsz)
2239a7e9e7fSjob {
22483e5a181Sclaudio 	char ipbuf[INET6_ADDRSTRLEN];
22583e5a181Sclaudio 	int ret, af;
2269a7e9e7fSjob 
22783e5a181Sclaudio 	switch (afi) {
22883e5a181Sclaudio 	case AFI_IPV4:
22983e5a181Sclaudio 		af = AF_INET;
23083e5a181Sclaudio 		break;
23183e5a181Sclaudio 	case AFI_IPV6:
23283e5a181Sclaudio 		af = AF_INET6;
23383e5a181Sclaudio 		break;
23483e5a181Sclaudio 	default:
23583e5a181Sclaudio 		errx(1, "unsupported address family identifier");
23683e5a181Sclaudio 	}
23783e5a181Sclaudio 
23883e5a181Sclaudio 	if (inet_ntop(af, addr->addr, ipbuf, sizeof(ipbuf)) == NULL)
23983e5a181Sclaudio 		err(1, "inet_ntop");
24083e5a181Sclaudio 	ret = snprintf(buf, bufsz, "%s/%hhu", ipbuf, addr->prefixlen);
24183e5a181Sclaudio 	if (ret < 0 || (size_t)ret >= bufsz)
24283e5a181Sclaudio 		err(1, "malformed IP address");
2439a7e9e7fSjob }
2449a7e9e7fSjob 
2459a7e9e7fSjob /*
246782a58ffSjob  * Convert a ip_addr into a NUL-terminated range notation string.
247782a58ffSjob  * The size of the buffer must be at least 95 (inclusive).
248782a58ffSjob  */
2494032f119Stb static void
250782a58ffSjob ip_addr_range_print(const struct ip_addr_range *range,
251782a58ffSjob     enum afi afi, char *buf, size_t bufsz)
252782a58ffSjob {
2534032f119Stb 	struct cert_ip ip;
254782a58ffSjob 	char min[INET6_ADDRSTRLEN], max[INET6_ADDRSTRLEN];
255782a58ffSjob 	int ret, af;
256782a58ffSjob 
257782a58ffSjob 	switch (afi) {
258782a58ffSjob 	case AFI_IPV4:
259782a58ffSjob 		af = AF_INET;
260782a58ffSjob 		break;
261782a58ffSjob 	case AFI_IPV6:
262782a58ffSjob 		af = AF_INET6;
263782a58ffSjob 		break;
264782a58ffSjob 	default:
265782a58ffSjob 		errx(1, "unsupported address family identifier");
266782a58ffSjob 	}
267782a58ffSjob 
2684032f119Stb 	memset(&ip, 0, sizeof(ip));
2694032f119Stb 
2704032f119Stb 	ip.afi = afi;
2714032f119Stb 	ip.type = CERT_IP_RANGE;
2724032f119Stb 	ip.range = *range;
2734032f119Stb 	if (!ip_cert_compose_ranges(&ip))
2744032f119Stb 		errx(1, "failed to compose ranges");
2754032f119Stb 
2764032f119Stb 	if (inet_ntop(af, ip.min, min, sizeof(min)) == NULL)
277782a58ffSjob 		err(1, "inet_ntop");
2784032f119Stb 	if (inet_ntop(af, ip.max, max, sizeof(max)) == NULL)
279782a58ffSjob 		err(1, "inet_ntop");
280782a58ffSjob 
281782a58ffSjob 	ret = snprintf(buf, bufsz, "%s--%s", min, max);
282782a58ffSjob 	if (ret < 0 || (size_t)ret >= bufsz)
283782a58ffSjob 		err(1, "malformed IP address");
284782a58ffSjob }
285782a58ffSjob 
286782a58ffSjob /*
2879a7e9e7fSjob  * Given the addresses (range or IP) in cert_ip, fill in the "min" and
2889a7e9e7fSjob  * "max" fields with the minimum and maximum possible IP addresses given
2899a7e9e7fSjob  * those ranges (or singleton prefixed range).
2909a7e9e7fSjob  * This does nothing if CERT_IP_INHERIT.
2919a7e9e7fSjob  * Returns zero on failure (misordered ranges), non-zero on success.
2929a7e9e7fSjob  */
2939a7e9e7fSjob int
2949a7e9e7fSjob ip_cert_compose_ranges(struct cert_ip *p)
2959a7e9e7fSjob {
29622bf1969Sclaudio 	size_t sz;
2979a7e9e7fSjob 
2989a7e9e7fSjob 	switch (p->type) {
2999a7e9e7fSjob 	case CERT_IP_ADDR:
30022bf1969Sclaudio 		sz = PREFIX_SIZE(p->ip.prefixlen);
30122bf1969Sclaudio 		memset(p->min, 0x0, sizeof(p->min));
30222bf1969Sclaudio 		memcpy(p->min, p->ip.addr, sz);
3039a7e9e7fSjob 		memset(p->max, 0xff, sizeof(p->max));
30422bf1969Sclaudio 		memcpy(p->max, p->ip.addr, sz);
30522bf1969Sclaudio 		if (sz > 0 && p->ip.prefixlen % 8 != 0)
30622bf1969Sclaudio 			p->max[sz - 1] |= (1 << (8 - p->ip.prefixlen % 8)) - 1;
3079a7e9e7fSjob 		break;
3089a7e9e7fSjob 	case CERT_IP_RANGE:
30922bf1969Sclaudio 		memset(p->min, 0x0, sizeof(p->min));
31022bf1969Sclaudio 		sz = PREFIX_SIZE(p->range.min.prefixlen);
31122bf1969Sclaudio 		memcpy(p->min, p->range.min.addr, sz);
3129a7e9e7fSjob 		memset(p->max, 0xff, sizeof(p->max));
31322bf1969Sclaudio 		sz = PREFIX_SIZE(p->range.max.prefixlen);
31422bf1969Sclaudio 		memcpy(p->max, p->range.max.addr, sz);
31522bf1969Sclaudio 		if (sz > 0 && p->range.max.prefixlen % 8 != 0)
31622bf1969Sclaudio 			p->max[sz - 1] |=
31722bf1969Sclaudio 			    (1 << (8 - p->range.max.prefixlen % 8)) - 1;
3189a7e9e7fSjob 		break;
3199a7e9e7fSjob 	default:
3209a7e9e7fSjob 		return 1;
3219a7e9e7fSjob 	}
3229a7e9e7fSjob 
3234032f119Stb 	sz = p->afi == AFI_IPV4 ? 4 : 16;
3249a7e9e7fSjob 	return memcmp(p->min, p->max, sz) <= 0;
3259a7e9e7fSjob }
3269a7e9e7fSjob 
3279a7e9e7fSjob /*
3289a7e9e7fSjob  * Given the ROA's acceptable prefix, compute the minimum and maximum
3299a7e9e7fSjob  * address accepted by the prefix.
3309a7e9e7fSjob  */
3319a7e9e7fSjob void
3329a7e9e7fSjob ip_roa_compose_ranges(struct roa_ip *p)
3339a7e9e7fSjob {
33422bf1969Sclaudio 	size_t sz = PREFIX_SIZE(p->addr.prefixlen);
3359a7e9e7fSjob 
33622bf1969Sclaudio 	memset(p->min, 0x0, sizeof(p->min));
33722bf1969Sclaudio 	memcpy(p->min, p->addr.addr, sz);
3389a7e9e7fSjob 	memset(p->max, 0xff, sizeof(p->max));
33922bf1969Sclaudio 	memcpy(p->max, p->addr.addr, sz);
34022bf1969Sclaudio 	if (sz > 0 && p->addr.prefixlen % 8 != 0)
34122bf1969Sclaudio 		p->max[sz - 1] |= (1 << (8 - p->addr.prefixlen % 8)) - 1;
3429a7e9e7fSjob }
343891d6bceSjob 
344891d6bceSjob void
3454032f119Stb ip_warn(const char *fn, const char *msg, const struct cert_ip *ip)
346891d6bceSjob {
347891d6bceSjob 	char buf[128];
348891d6bceSjob 
3494032f119Stb 	switch (ip->type) {
350891d6bceSjob 	case CERT_IP_ADDR:
3514032f119Stb 		ip_addr_print(&ip->ip, ip->afi, buf, sizeof(buf));
3524032f119Stb 		warnx("%s: %s: %s", fn, msg, buf);
353891d6bceSjob 		break;
354891d6bceSjob 	case CERT_IP_RANGE:
3554032f119Stb 		ip_addr_range_print(&ip->range, ip->afi, buf, sizeof(buf));
3564032f119Stb 		warnx("%s: %s: %s", fn, msg, buf);
357891d6bceSjob 		break;
35876dbb2b6Stb 	case CERT_IP_INHERIT:
3594032f119Stb 		warnx("%s: %s: IP (inherit)", fn, msg);
36076dbb2b6Stb 		break;
361891d6bceSjob 	default:
362891d6bceSjob 		warnx("%s: corrupt cert", fn);
363891d6bceSjob 		break;
364891d6bceSjob 	}
365891d6bceSjob }
366