xref: /openbsd/usr.sbin/rpki-client/spl.c (revision 684ec124)
1 /*	$OpenBSD: spl.c,v 1.3 2024/05/15 14:43:32 claudio Exp $ */
2 /*
3  * Copyright (c) 2024 Job Snijders <job@fastly.com>
4  * Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
5  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <assert.h>
21 #include <err.h>
22 #include <stdint.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 #include <openssl/asn1.h>
28 #include <openssl/asn1t.h>
29 #include <openssl/stack.h>
30 #include <openssl/safestack.h>
31 #include <openssl/x509.h>
32 #include <openssl/x509v3.h>
33 
34 #include "extern.h"
35 
36 extern ASN1_OBJECT	*spl_oid;
37 
38 /*
39  * Types and templates for the SPL eContent.
40  */
41 
42 ASN1_ITEM_EXP AddressFamilyPrefixes_it;
43 ASN1_ITEM_EXP SignedPrefixList_it;
44 
45 DECLARE_STACK_OF(ASN1_BIT_STRING);
46 
47 typedef struct {
48 	ASN1_OCTET_STRING		*addressFamily;
49 	STACK_OF(ASN1_BIT_STRING)	*addressPrefixes;
50 } AddressFamilyPrefixes;
51 
52 DECLARE_STACK_OF(AddressFamilyPrefixes);
53 
54 ASN1_SEQUENCE(AddressFamilyPrefixes) = {
55 	ASN1_SIMPLE(AddressFamilyPrefixes, addressFamily, ASN1_OCTET_STRING),
56 	ASN1_SEQUENCE_OF(AddressFamilyPrefixes, addressPrefixes,
57 	    ASN1_BIT_STRING),
58 } ASN1_SEQUENCE_END(AddressFamilyPrefixes);
59 
60 #ifndef DEFINE_STACK_OF
61 #define sk_ASN1_BIT_STRING_num(st)	SKM_sk_num(ASN1_BIT_STRING, (st))
62 #define sk_ASN1_BIT_STRING_value(st, i)	SKM_sk_value(ASN1_BIT_STRING, (st), (i))
63 
64 #define sk_AddressFamilyPrefixes_num(st)	\
65     SKM_sk_num(AddressFamilyPrefixes, (st))
66 #define sk_AddressFamilyPrefixes_value(st, i)	\
67     SKM_sk_value(AddressFamilyPrefixes, (st), (i))
68 #endif
69 
70 typedef struct {
71 	ASN1_INTEGER			*version;
72 	ASN1_INTEGER			*asid;
73 	STACK_OF(AddressFamilyPrefixes)	*prefixBlocks;
74 } SignedPrefixList;
75 
76 ASN1_SEQUENCE(SignedPrefixList) = {
77 	ASN1_EXP_OPT(SignedPrefixList, version, ASN1_INTEGER, 0),
78 	ASN1_SIMPLE(SignedPrefixList, asid, ASN1_INTEGER),
79 	ASN1_SEQUENCE_OF(SignedPrefixList, prefixBlocks, AddressFamilyPrefixes)
80 } ASN1_SEQUENCE_END(SignedPrefixList);
81 
82 DECLARE_ASN1_FUNCTIONS(SignedPrefixList);
83 IMPLEMENT_ASN1_FUNCTIONS(SignedPrefixList);
84 
85 /*
86  * Comparator to help sorting elements in SPL prefixBlocks and VSPs.
87  * Returns -1 if 'a' should precede 'b', 1 if 'b' should precede 'a',
88  * or '0' if a and b are equal.
89  */
90 static int
prefix_cmp(enum afi afi,const struct ip_addr * a,const struct ip_addr * b)91 prefix_cmp(enum afi afi, const struct ip_addr *a, const struct ip_addr *b)
92 {
93 	int cmp;
94 
95 	switch (afi) {
96 	case AFI_IPV4:
97 		cmp = memcmp(&a->addr, &b->addr, 4);
98 		if (cmp < 0)
99 			return -1;
100 		if (cmp > 0)
101 			return 1;
102 		break;
103 	case AFI_IPV6:
104 		cmp = memcmp(&a->addr, &b->addr, 16);
105 		if (cmp < 0)
106 			return -1;
107 		if (cmp > 0)
108 			return 1;
109 		break;
110 	default:
111 		break;
112 	}
113 
114 	if (a->prefixlen < b->prefixlen)
115 		return -1;
116 	if (a->prefixlen > b->prefixlen)
117 		return 1;
118 
119 	return 0;
120 }
121 
122 /*
123  * Parses the eContent section of a SPL file,
124  * draft-ietf-sidrops-rpki-prefixlist-02 section 3.
125  * Returns zero on failure, non-zero on success.
126  */
127 static int
spl_parse_econtent(const char * fn,struct spl * spl,const unsigned char * d,size_t dsz)128 spl_parse_econtent(const char *fn, struct spl *spl, const unsigned char *d,
129     size_t dsz)
130 {
131 	const unsigned char		*oder;
132 	SignedPrefixList		*spl_asn1;
133 	const AddressFamilyPrefixes	*afp;
134 	const STACK_OF(ASN1_BIT_STRING)	*prefixes;
135 	const ASN1_BIT_STRING		*prefix_asn1;
136 	int				 afpsz, prefixesz;
137 	enum afi			 afi;
138 	struct ip_addr			 ip_addr;
139 	struct spl_pfx			*prefix;
140 	int				 ipv4_seen = 0, ipv6_seen = 0;
141 	int				 i, j, rc = 0;
142 
143 	oder = d;
144 	if ((spl_asn1 = d2i_SignedPrefixList(NULL, &d, dsz)) == NULL) {
145 		warnx("%s: RFC 6482 section 3: failed to parse "
146 		    "SignedPrefixList", fn);
147 		goto out;
148 	}
149 	if (d != oder + dsz) {
150 		warnx("%s: %td bytes trailing garbage in eContent", fn,
151 		    oder + dsz - d);
152 		goto out;
153 	}
154 
155 	if (!valid_econtent_version(fn, spl_asn1->version, 0))
156 		goto out;
157 
158 	if (!as_id_parse(spl_asn1->asid, &spl->asid)) {
159 		warnx("%s: asid: malformed AS identifier", fn);
160 		goto out;
161 	}
162 
163 	afpsz = sk_AddressFamilyPrefixes_num(spl_asn1->prefixBlocks);
164 	if (afpsz < 0 || afpsz > 2) {
165 		warnx("%s: unexpected number of AddressFamilyAddressPrefixes"
166 		    "(got %d, expected 0, 1, or 2)", fn, afpsz);
167 		goto out;
168 	}
169 
170 	for (i = 0; i < afpsz; i++) {
171 		struct ip_addr *prev_ip_addr = NULL;
172 
173 		afp = sk_AddressFamilyPrefixes_value(spl_asn1->prefixBlocks, i);
174 		prefixes = afp->addressPrefixes;
175 		prefixesz = sk_ASN1_BIT_STRING_num(afp->addressPrefixes);
176 
177 		if (prefixesz == 0) {
178 			warnx("%s: empty AddressFamilyAddressPrefixes", fn);
179 			goto out;
180 		}
181 		if (spl->pfxsz + prefixesz >= MAX_IP_SIZE) {
182 			warnx("%s: too many addressPrefixes entries", fn);
183 			goto out;
184 		}
185 
186 		if (!ip_addr_afi_parse(fn, afp->addressFamily, &afi))
187 			goto out;
188 
189 		switch (afi) {
190 		case AFI_IPV4:
191 			if (ipv4_seen++ > 0) {
192 				warnx("%s: addressFamilyIPv4 appeared twice",
193 				    fn);
194 				goto out;
195 			}
196 			if (ipv6_seen > 0) {
197 				warnx("%s: invalid sorting, IPv6 before IPv4",
198 				    fn);
199 				goto out;
200 			}
201 			break;
202 		case AFI_IPV6:
203 			if (ipv6_seen++ > 0) {
204 				warnx("%s: addressFamilyIPv6 appeared twice",
205 				    fn);
206 				goto out;
207 			}
208 		}
209 
210 		spl->pfxs = recallocarray(spl->pfxs, spl->pfxsz,
211 		    spl->pfxsz + prefixesz, sizeof(struct spl_pfx));
212 		if (spl->pfxs == NULL)
213 			err(1, NULL);
214 
215 		for (j = 0; j < prefixesz; j++) {
216 			prefix_asn1 = sk_ASN1_BIT_STRING_value(prefixes, j);
217 
218 			if (!ip_addr_parse(prefix_asn1, afi, fn, &ip_addr))
219 				goto out;
220 
221 			if (j > 0 &&
222 			    prefix_cmp(afi, prev_ip_addr, &ip_addr) != -1) {
223 				warnx("%s: invalid addressPrefixes sorting", fn);
224 				goto out;
225 			}
226 
227 			prefix = &spl->pfxs[spl->pfxsz++];
228 			prefix->prefix = ip_addr;
229 			prefix->afi = afi;
230 			prev_ip_addr = &prefix->prefix;
231 		}
232 	}
233 
234 	rc = 1;
235  out:
236 	SignedPrefixList_free(spl_asn1);
237 	return rc;
238 }
239 
240 /*
241  * Parse a full Signed Prefix List file.
242  * Returns the SPL, or NULL if the object was malformed.
243  */
244 struct spl *
spl_parse(X509 ** x509,const char * fn,int talid,const unsigned char * der,size_t len)245 spl_parse(X509 **x509, const char *fn, int talid, const unsigned char *der,
246     size_t len)
247 {
248 	struct spl	*spl;
249 	size_t		 cmsz;
250 	unsigned char	*cms;
251 	struct cert	*cert = NULL;
252 	time_t		 signtime = 0;
253 	int		 rc = 0;
254 
255 	cms = cms_parse_validate(x509, fn, der, len, spl_oid, &cmsz, &signtime);
256 	if (cms == NULL)
257 		return NULL;
258 
259 	if ((spl = calloc(1, sizeof(*spl))) == NULL)
260 		err(1, NULL);
261 	spl->signtime = signtime;
262 
263 	if (!x509_get_aia(*x509, fn, &spl->aia))
264 		goto out;
265 	if (!x509_get_aki(*x509, fn, &spl->aki))
266 		goto out;
267 	if (!x509_get_sia(*x509, fn, &spl->sia))
268 		goto out;
269 	if (!x509_get_ski(*x509, fn, &spl->ski))
270 		goto out;
271 	if (spl->aia == NULL || spl->aki == NULL || spl->sia == NULL ||
272 	    spl->ski == NULL) {
273 		warnx("%s: RFC 6487 section 4.8: "
274 		    "missing AIA, AKI, SIA, or SKI X509 extension", fn);
275 		goto out;
276 	}
277 
278 	if (!x509_get_notbefore(*x509, fn, &spl->notbefore))
279 		goto out;
280 	if (!x509_get_notafter(*x509, fn, &spl->notafter))
281 		goto out;
282 
283 	if (!spl_parse_econtent(fn, spl, cms, cmsz))
284 		goto out;
285 
286 	if (x509_any_inherits(*x509)) {
287 		warnx("%s: inherit elements not allowed in EE cert", fn);
288 		goto out;
289 	}
290 
291 	if ((cert = cert_parse_ee_cert(fn, talid, *x509)) == NULL)
292 		goto out;
293 
294 	if (cert->asz == 0) {
295 		warnx("%s: AS Resources extension missing", fn);
296 		goto out;
297 	}
298 
299 	if (cert->ipsz > 0) {
300 		warnx("%s: superfluous IP Resources extension present", fn);
301 		goto out;
302 	}
303 
304 	/*
305 	 * If the SPL isn't valid, we accept it anyway and depend upon
306 	 * the code around spl_read() to check the "valid" field itself.
307 	 */
308 	spl->valid = valid_spl(fn, cert, spl);
309 
310 	rc = 1;
311  out:
312 	if (rc == 0) {
313 		spl_free(spl);
314 		spl = NULL;
315 		X509_free(*x509);
316 		*x509 = NULL;
317 	}
318 	cert_free(cert);
319 	free(cms);
320 	return spl;
321 }
322 
323 void
spl_free(struct spl * s)324 spl_free(struct spl *s)
325 {
326 	if (s == NULL)
327 		return;
328 
329 	free(s->aia);
330 	free(s->aki);
331 	free(s->sia);
332 	free(s->ski);
333 	free(s->pfxs);
334 	free(s);
335 }
336 
337 /*
338  * Serialize parsed SPL content.
339  * See spl_read() for reader.
340  */
341 void
spl_buffer(struct ibuf * b,const struct spl * s)342 spl_buffer(struct ibuf *b, const struct spl *s)
343 {
344 	io_simple_buffer(b, &s->valid, sizeof(s->valid));
345 	io_simple_buffer(b, &s->asid, sizeof(s->asid));
346 	io_simple_buffer(b, &s->talid, sizeof(s->talid));
347 	io_simple_buffer(b, &s->pfxsz, sizeof(s->pfxsz));
348 	io_simple_buffer(b, &s->expires, sizeof(s->expires));
349 
350 	io_simple_buffer(b, s->pfxs, s->pfxsz * sizeof(s->pfxs[0]));
351 
352 	io_str_buffer(b, s->aia);
353 	io_str_buffer(b, s->aki);
354 	io_str_buffer(b, s->ski);
355 }
356 
357 /*
358  * Read parsed SPL content from descriptor.
359  * See spl_buffer() for writer.
360  * Result must be passed to spl_free().
361  */
362 struct spl *
spl_read(struct ibuf * b)363 spl_read(struct ibuf *b)
364 {
365 	struct spl *s;
366 
367 	if ((s = calloc(1, sizeof(struct spl))) == NULL)
368 		err(1, NULL);
369 
370 	io_read_buf(b, &s->valid, sizeof(s->valid));
371 	io_read_buf(b, &s->asid, sizeof(s->asid));
372 	io_read_buf(b, &s->talid, sizeof(s->talid));
373 	io_read_buf(b, &s->pfxsz, sizeof(s->pfxsz));
374 	io_read_buf(b, &s->expires, sizeof(s->expires));
375 
376 	if ((s->pfxs = calloc(s->pfxsz, sizeof(struct spl_pfx))) == NULL)
377 		err(1, NULL);
378 	io_read_buf(b, s->pfxs, s->pfxsz * sizeof(s->pfxs[0]));
379 
380 	io_read_str(b, &s->aia);
381 	io_read_str(b, &s->aki);
382 	io_read_str(b, &s->ski);
383 	assert(s->aia && s->aki && s->ski);
384 
385 	return s;
386 }
387 
388 static int
spl_pfx_cmp(const struct spl_pfx * a,const struct spl_pfx * b)389 spl_pfx_cmp(const struct spl_pfx *a, const struct spl_pfx *b)
390 {
391 	if (a->afi > b->afi)
392 		return 1;
393 	if (a->afi < b->afi)
394 		return -1;
395 
396 	return prefix_cmp(a->afi, &a->prefix, &b->prefix);
397 }
398 
399 static void
insert_vsp(struct vsp * vsp,size_t idx,struct spl_pfx * pfx)400 insert_vsp(struct vsp *vsp, size_t idx, struct spl_pfx *pfx)
401 {
402 	if (idx < vsp->prefixesz)
403 		memmove(vsp->prefixes + idx + 1, vsp->prefixes + idx,
404 		    (vsp->prefixesz - idx) * sizeof(*vsp->prefixes));
405 	vsp->prefixes[idx] = *pfx;
406 	vsp->prefixesz++;
407 }
408 
409 /*
410  * Add each prefix in the SPL into the VSP tree.
411  * Updates "vsps" to be the number of VSPs and "uniqs" to be the unique
412  * number of prefixes.
413  */
414 void
spl_insert_vsps(struct vsp_tree * tree,struct spl * spl,struct repo * rp)415 spl_insert_vsps(struct vsp_tree *tree, struct spl *spl, struct repo *rp)
416 {
417 	struct vsp	*vsp, *found;
418 	size_t		 i, j;
419 	int		 cmp;
420 
421 	if ((vsp = calloc(1, sizeof(*vsp))) == NULL)
422 		err(1, NULL);
423 
424 	vsp->asid = spl->asid;
425 	vsp->talid = spl->talid;
426 	vsp->expires = spl->expires;
427 	if (rp != NULL)
428 		vsp->repoid = repo_id(rp);
429 
430 	if ((found = RB_INSERT(vsp_tree, tree, vsp)) != NULL) {
431 		/* already exists */
432 		if (found->expires < vsp->expires) {
433 			/* adjust unique count */
434 			repo_stat_inc(repo_byid(found->repoid),
435 			    found->talid, RTYPE_SPL, STYPE_DEC_UNIQUE);
436 			found->expires = vsp->expires;
437 			found->talid = vsp->talid;
438 			found->repoid = vsp->repoid;
439 			repo_stat_inc(rp, vsp->talid, RTYPE_SPL,
440 			    STYPE_UNIQUE);
441 		}
442 		free(vsp);
443 		vsp = found;
444 	} else
445 		repo_stat_inc(rp, vsp->talid, RTYPE_SPL, STYPE_UNIQUE);
446 	repo_stat_inc(rp, spl->talid, RTYPE_SPL, STYPE_TOTAL);
447 
448 	/* merge content of multiple SPLs */
449 	vsp->prefixes = reallocarray(vsp->prefixes,
450 	    vsp->prefixesz + spl->pfxsz, sizeof(struct spl_pfx));
451 	if (vsp->prefixes == NULL)
452 		err(1, NULL);
453 
454 	/*
455 	 * Merge all data from the new SPL at hand into 'vsp': loop over
456 	 * all SPL->pfxs, and insert them in the right place in
457 	 * vsp->prefixes while keeping the order of the array.
458 	 */
459 	for (i = 0, j = 0; i < spl->pfxsz; ) {
460 		cmp = -1;
461 		if (j == vsp->prefixesz ||
462 		    (cmp = spl_pfx_cmp(&spl->pfxs[i], &vsp->prefixes[j])) < 0) {
463 			insert_vsp(vsp, j, &spl->pfxs[i]);
464 			i++;
465 		} else if (cmp == 0)
466 			i++;
467 
468 		if (j < vsp->prefixesz)
469 			j++;
470 	}
471 }
472 
473 /*
474  * Comparison function for the RB tree
475  */
476 static inline int
vspcmp(const struct vsp * a,const struct vsp * b)477 vspcmp(const struct vsp *a, const struct vsp *b)
478 {
479 	if (a->asid > b->asid)
480 		return 1;
481 	if (a->asid < b->asid)
482 		return -1;
483 
484 	return 0;
485 }
486 
487 RB_GENERATE(vsp_tree, vsp, entry, vspcmp);
488