1/*	$OpenBSD: roa.c,v 1.78 2024/05/24 12:57:20 tb Exp $ */
2/*
3 * Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
4 * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <assert.h>
20#include <err.h>
21#include <stdint.h>
22#include <stdlib.h>
23#include <string.h>
24#include <unistd.h>
25
26#include <openssl/asn1.h>
27#include <openssl/asn1t.h>
28#include <openssl/stack.h>
29#include <openssl/safestack.h>
30#include <openssl/x509.h>
31
32#include "extern.h"
33
34extern ASN1_OBJECT	*roa_oid;
35
36/*
37 * Types and templates for the ROA eContent, RFC 6482, section 3.
38 */
39
40ASN1_ITEM_EXP ROAIPAddress_it;
41ASN1_ITEM_EXP ROAIPAddressFamily_it;
42ASN1_ITEM_EXP RouteOriginAttestation_it;
43
44typedef struct {
45	ASN1_BIT_STRING		*address;
46	ASN1_INTEGER		*maxLength;
47} ROAIPAddress;
48
49DECLARE_STACK_OF(ROAIPAddress);
50
51typedef struct {
52	ASN1_OCTET_STRING	*addressFamily;
53	STACK_OF(ROAIPAddress)	*addresses;
54} ROAIPAddressFamily;
55
56DECLARE_STACK_OF(ROAIPAddressFamily);
57
58#ifndef DEFINE_STACK_OF
59#define sk_ROAIPAddress_num(st)		SKM_sk_num(ROAIPAddress, (st))
60#define sk_ROAIPAddress_value(st, i)	SKM_sk_value(ROAIPAddress, (st), (i))
61
62#define sk_ROAIPAddressFamily_num(st)	SKM_sk_num(ROAIPAddressFamily, (st))
63#define sk_ROAIPAddressFamily_value(st, i) \
64    SKM_sk_value(ROAIPAddressFamily, (st), (i))
65#endif
66
67typedef struct {
68	ASN1_INTEGER			*version;
69	ASN1_INTEGER			*asid;
70	STACK_OF(ROAIPAddressFamily)	*ipAddrBlocks;
71} RouteOriginAttestation;
72
73ASN1_SEQUENCE(ROAIPAddress) = {
74	ASN1_SIMPLE(ROAIPAddress, address, ASN1_BIT_STRING),
75	ASN1_OPT(ROAIPAddress, maxLength, ASN1_INTEGER),
76} ASN1_SEQUENCE_END(ROAIPAddress);
77
78ASN1_SEQUENCE(ROAIPAddressFamily) = {
79	ASN1_SIMPLE(ROAIPAddressFamily, addressFamily, ASN1_OCTET_STRING),
80	ASN1_SEQUENCE_OF(ROAIPAddressFamily, addresses, ROAIPAddress),
81} ASN1_SEQUENCE_END(ROAIPAddressFamily);
82
83ASN1_SEQUENCE(RouteOriginAttestation) = {
84	ASN1_EXP_OPT(RouteOriginAttestation, version, ASN1_INTEGER, 0),
85	ASN1_SIMPLE(RouteOriginAttestation, asid, ASN1_INTEGER),
86	ASN1_SEQUENCE_OF(RouteOriginAttestation, ipAddrBlocks,
87	    ROAIPAddressFamily),
88} ASN1_SEQUENCE_END(RouteOriginAttestation);
89
90DECLARE_ASN1_FUNCTIONS(RouteOriginAttestation);
91IMPLEMENT_ASN1_FUNCTIONS(RouteOriginAttestation);
92
93/*
94 * Parses the eContent section of an ROA file, RFC 6482, section 3.
95 * Returns zero on failure, non-zero on success.
96 */
97static int
98roa_parse_econtent(const char *fn, struct roa *roa, const unsigned char *d,
99    size_t dsz)
100{
101	const unsigned char		*oder;
102	RouteOriginAttestation		*roa_asn1;
103	const ROAIPAddressFamily	*addrfam;
104	const STACK_OF(ROAIPAddress)	*addrs;
105	int				 addrsz, ipv4_seen = 0, ipv6_seen = 0;
106	enum afi			 afi;
107	const ROAIPAddress		*addr;
108	uint64_t			 maxlen;
109	struct ip_addr			 ipaddr;
110	struct roa_ip			*res;
111	int				 ipaddrblocksz;
112	int				 i, j, rc = 0;
113
114	oder = d;
115	if ((roa_asn1 = d2i_RouteOriginAttestation(NULL, &d, dsz)) == NULL) {
116		warnx("%s: RFC 6482 section 3: failed to parse "
117		    "RouteOriginAttestation", fn);
118		goto out;
119	}
120	if (d != oder + dsz) {
121		warnx("%s: %td bytes trailing garbage in eContent", fn,
122		    oder + dsz - d);
123		goto out;
124	}
125
126	if (!valid_econtent_version(fn, roa_asn1->version, 0))
127		goto out;
128
129	if (!as_id_parse(roa_asn1->asid, &roa->asid)) {
130		warnx("%s: RFC 6482 section 3.2: asID: "
131		    "malformed AS identifier", fn);
132		goto out;
133	}
134
135	ipaddrblocksz = sk_ROAIPAddressFamily_num(roa_asn1->ipAddrBlocks);
136	if (ipaddrblocksz != 1 && ipaddrblocksz != 2) {
137		warnx("%s: RFC 9582: unexpected number of ipAddrBlocks "
138		    "(got %d, expected 1 or 2)", fn, ipaddrblocksz);
139		goto out;
140	}
141
142	for (i = 0; i < ipaddrblocksz; i++) {
143		addrfam = sk_ROAIPAddressFamily_value(roa_asn1->ipAddrBlocks,
144		    i);
145		addrs = addrfam->addresses;
146		addrsz = sk_ROAIPAddress_num(addrs);
147
148		if (!ip_addr_afi_parse(fn, addrfam->addressFamily, &afi)) {
149			warnx("%s: RFC 6482 section 3.3: addressFamily: "
150			    "invalid", fn);
151			goto out;
152		}
153
154		switch (afi) {
155		case AFI_IPV4:
156			if (ipv4_seen++ > 0) {
157				warnx("%s: RFC 9582 section 4.3.2: "
158				    "IPv4 appears twice", fn);
159				goto out;
160			}
161			break;
162		case AFI_IPV6:
163			if (ipv6_seen++ > 0) {
164				warnx("%s: RFC 9582 section 4.3.2: "
165				    "IPv6 appears twice", fn);
166				goto out;
167			}
168			break;
169		}
170
171		if (addrsz == 0) {
172			warnx("%s: RFC 9582, section 4.3.2: "
173			    "empty ROAIPAddressFamily", fn);
174			goto out;
175		}
176
177		if (roa->ipsz + addrsz >= MAX_IP_SIZE) {
178			warnx("%s: too many ROAIPAddress entries: limit %d",
179			    fn, MAX_IP_SIZE);
180			goto out;
181		}
182		roa->ips = recallocarray(roa->ips, roa->ipsz,
183		    roa->ipsz + addrsz, sizeof(struct roa_ip));
184		if (roa->ips == NULL)
185			err(1, NULL);
186
187		for (j = 0; j < addrsz; j++) {
188			addr = sk_ROAIPAddress_value(addrs, j);
189
190			if (!ip_addr_parse(addr->address, afi, fn, &ipaddr)) {
191				warnx("%s: RFC 6482 section 3.3: address: "
192				    "invalid IP address", fn);
193				goto out;
194			}
195			maxlen = ipaddr.prefixlen;
196
197			if (addr->maxLength != NULL) {
198				if (!ASN1_INTEGER_get_uint64(&maxlen,
199				    addr->maxLength)) {
200					warnx("%s: RFC 6482 section 3.2: "
201					    "ASN1_INTEGER_get_uint64 failed",
202					    fn);
203					goto out;
204				}
205				if (ipaddr.prefixlen > maxlen) {
206					warnx("%s: prefixlen (%d) larger than "
207					    "maxLength (%llu)", fn,
208					    ipaddr.prefixlen,
209					    (unsigned long long)maxlen);
210					goto out;
211				}
212				if (maxlen > ((afi == AFI_IPV4) ? 32 : 128)) {
213					warnx("%s: maxLength (%llu) too large",
214					    fn, (unsigned long long)maxlen);
215					goto out;
216				}
217			}
218
219			res = &roa->ips[roa->ipsz++];
220			res->addr = ipaddr;
221			res->afi = afi;
222			res->maxlength = maxlen;
223			ip_roa_compose_ranges(res);
224		}
225	}
226
227	rc = 1;
228 out:
229	RouteOriginAttestation_free(roa_asn1);
230	return rc;
231}
232
233/*
234 * Parse a full RFC 6482 file.
235 * Returns the ROA or NULL if the document was malformed.
236 */
237struct roa *
238roa_parse(X509 **x509, const char *fn, int talid, const unsigned char *der,
239    size_t len)
240{
241	struct roa	*roa;
242	size_t		 cmsz;
243	unsigned char	*cms;
244	struct cert	*cert = NULL;
245	time_t		 signtime = 0;
246	int		 rc = 0;
247
248	cms = cms_parse_validate(x509, fn, der, len, roa_oid, &cmsz, &signtime);
249	if (cms == NULL)
250		return NULL;
251
252	if ((roa = calloc(1, sizeof(struct roa))) == NULL)
253		err(1, NULL);
254	roa->signtime = signtime;
255
256	if (!x509_get_aia(*x509, fn, &roa->aia))
257		goto out;
258	if (!x509_get_aki(*x509, fn, &roa->aki))
259		goto out;
260	if (!x509_get_sia(*x509, fn, &roa->sia))
261		goto out;
262	if (!x509_get_ski(*x509, fn, &roa->ski))
263		goto out;
264	if (roa->aia == NULL || roa->aki == NULL || roa->sia == NULL ||
265	    roa->ski == NULL) {
266		warnx("%s: RFC 6487 section 4.8: "
267		    "missing AIA, AKI, SIA, or SKI X509 extension", fn);
268		goto out;
269	}
270
271	if (!x509_get_notbefore(*x509, fn, &roa->notbefore))
272		goto out;
273	if (!x509_get_notafter(*x509, fn, &roa->notafter))
274		goto out;
275
276	if (!roa_parse_econtent(fn, roa, cms, cmsz))
277		goto out;
278
279	if (x509_any_inherits(*x509)) {
280		warnx("%s: inherit elements not allowed in EE cert", fn);
281		goto out;
282	}
283
284	if ((cert = cert_parse_ee_cert(fn, talid, *x509)) == NULL)
285		goto out;
286
287	if (cert->asz > 0) {
288		warnx("%s: superfluous AS Resources extension present", fn);
289		goto out;
290	}
291
292	/*
293	 * If the ROA isn't valid, we accept it anyway and depend upon
294	 * the code around roa_read() to check the "valid" field itself.
295	 */
296	roa->valid = valid_roa(fn, cert, roa);
297
298	rc = 1;
299out:
300	if (rc == 0) {
301		roa_free(roa);
302		roa = NULL;
303		X509_free(*x509);
304		*x509 = NULL;
305	}
306	cert_free(cert);
307	free(cms);
308	return roa;
309}
310
311/*
312 * Free an ROA pointer.
313 * Safe to call with NULL.
314 */
315void
316roa_free(struct roa *p)
317{
318
319	if (p == NULL)
320		return;
321	free(p->aia);
322	free(p->aki);
323	free(p->sia);
324	free(p->ski);
325	free(p->ips);
326	free(p);
327}
328
329/*
330 * Serialise parsed ROA content.
331 * See roa_read() for reader.
332 */
333void
334roa_buffer(struct ibuf *b, const struct roa *p)
335{
336	io_simple_buffer(b, &p->valid, sizeof(p->valid));
337	io_simple_buffer(b, &p->asid, sizeof(p->asid));
338	io_simple_buffer(b, &p->talid, sizeof(p->talid));
339	io_simple_buffer(b, &p->ipsz, sizeof(p->ipsz));
340	io_simple_buffer(b, &p->expires, sizeof(p->expires));
341
342	io_simple_buffer(b, p->ips, p->ipsz * sizeof(p->ips[0]));
343
344	io_str_buffer(b, p->aia);
345	io_str_buffer(b, p->aki);
346	io_str_buffer(b, p->ski);
347}
348
349/*
350 * Read parsed ROA content from descriptor.
351 * See roa_buffer() for writer.
352 * Result must be passed to roa_free().
353 */
354struct roa *
355roa_read(struct ibuf *b)
356{
357	struct roa	*p;
358
359	if ((p = calloc(1, sizeof(struct roa))) == NULL)
360		err(1, NULL);
361
362	io_read_buf(b, &p->valid, sizeof(p->valid));
363	io_read_buf(b, &p->asid, sizeof(p->asid));
364	io_read_buf(b, &p->talid, sizeof(p->talid));
365	io_read_buf(b, &p->ipsz, sizeof(p->ipsz));
366	io_read_buf(b, &p->expires, sizeof(p->expires));
367
368	if ((p->ips = calloc(p->ipsz, sizeof(struct roa_ip))) == NULL)
369		err(1, NULL);
370	io_read_buf(b, p->ips, p->ipsz * sizeof(p->ips[0]));
371
372	io_read_str(b, &p->aia);
373	io_read_str(b, &p->aki);
374	io_read_str(b, &p->ski);
375	assert(p->aia && p->aki && p->ski);
376
377	return p;
378}
379
380/*
381 * Add each IP address in the ROA into the VRP tree.
382 * Updates "vrps" to be the number of VRPs and "uniqs" to be the unique
383 * number of addresses.
384 */
385void
386roa_insert_vrps(struct vrp_tree *tree, struct roa *roa, struct repo *rp)
387{
388	struct vrp	*v, *found;
389	size_t		 i;
390
391	for (i = 0; i < roa->ipsz; i++) {
392		if ((v = malloc(sizeof(*v))) == NULL)
393			err(1, NULL);
394		v->afi = roa->ips[i].afi;
395		v->addr = roa->ips[i].addr;
396		v->maxlength = roa->ips[i].maxlength;
397		v->asid = roa->asid;
398		v->talid = roa->talid;
399		if (rp != NULL)
400			v->repoid = repo_id(rp);
401		else
402			v->repoid = 0;
403		v->expires = roa->expires;
404
405		/*
406		 * Check if a similar VRP already exists in the tree.
407		 * If the found VRP expires sooner, update it to this
408		 * ROAs later expiry moment.
409		 */
410		if ((found = RB_INSERT(vrp_tree, tree, v)) != NULL) {
411			/* already exists */
412			if (found->expires < v->expires) {
413				/* update found with preferred data */
414				/* adjust unique count */
415				repo_stat_inc(repo_byid(found->repoid),
416				    found->talid, RTYPE_ROA, STYPE_DEC_UNIQUE);
417				found->expires = v->expires;
418				found->talid = v->talid;
419				found->repoid = v->repoid;
420				repo_stat_inc(rp, v->talid, RTYPE_ROA,
421				    STYPE_UNIQUE);
422			}
423			free(v);
424		} else
425			repo_stat_inc(rp, v->talid, RTYPE_ROA, STYPE_UNIQUE);
426
427		repo_stat_inc(rp, roa->talid, RTYPE_ROA, STYPE_TOTAL);
428	}
429}
430
431static inline int
432vrpcmp(struct vrp *a, struct vrp *b)
433{
434	int rv;
435
436	if (a->afi > b->afi)
437		return 1;
438	if (a->afi < b->afi)
439		return -1;
440	switch (a->afi) {
441	case AFI_IPV4:
442		rv = memcmp(&a->addr.addr, &b->addr.addr, 4);
443		if (rv)
444			return rv;
445		break;
446	case AFI_IPV6:
447		rv = memcmp(&a->addr.addr, &b->addr.addr, 16);
448		if (rv)
449			return rv;
450		break;
451	default:
452		break;
453	}
454	/* a smaller prefixlen is considered bigger, e.g. /8 vs /10 */
455	if (a->addr.prefixlen < b->addr.prefixlen)
456		return 1;
457	if (a->addr.prefixlen > b->addr.prefixlen)
458		return -1;
459	if (a->maxlength < b->maxlength)
460		return 1;
461	if (a->maxlength > b->maxlength)
462		return -1;
463
464	if (a->asid > b->asid)
465		return 1;
466	if (a->asid < b->asid)
467		return -1;
468
469	return 0;
470}
471
472RB_GENERATE(vrp_tree, vrp, entry, vrpcmp);
473