xref: /openbsd-src/usr.sbin/rpki-client/aspa.c (revision fc405d53b73a2d73393cb97f684863d17b583e38)
1 /*	$OpenBSD: aspa.c,v 1.17 2023/04/26 16:32:41 claudio Exp $ */
2 /*
3  * Copyright (c) 2022 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 
33 #include "extern.h"
34 
35 /*
36  * Parse results and data of the ASPA object.
37  */
38 struct	parse {
39 	const char	 *fn; /* ASPA file name */
40 	struct aspa	 *res; /* results */
41 };
42 
43 extern ASN1_OBJECT	*aspa_oid;
44 
45 /*
46  * Types and templates for ASPA eContent draft-ietf-sidrops-aspa-profile-08
47  */
48 
49 typedef struct {
50 	ASN1_INTEGER		*providerASID;
51 	ASN1_OCTET_STRING	*afiLimit;
52 } ProviderAS;
53 
54 DECLARE_STACK_OF(ProviderAS);
55 
56 #ifndef DEFINE_STACK_OF
57 #define sk_ProviderAS_num(sk)		SKM_sk_num(ProviderAS, (sk))
58 #define sk_ProviderAS_value(sk, i)	SKM_sk_value(ProviderAS, (sk), (i))
59 #endif
60 
61 ASN1_SEQUENCE(ProviderAS) = {
62 	ASN1_SIMPLE(ProviderAS, providerASID, ASN1_INTEGER),
63 	ASN1_OPT(ProviderAS, afiLimit, ASN1_OCTET_STRING),
64 } ASN1_SEQUENCE_END(ProviderAS);
65 
66 typedef struct {
67 	ASN1_INTEGER		*version;
68 	ASN1_INTEGER		*customerASID;
69 	STACK_OF(ProviderAS)	*providers;
70 } ASProviderAttestation;
71 
72 ASN1_SEQUENCE(ASProviderAttestation) = {
73 	ASN1_EXP_OPT(ASProviderAttestation, version, ASN1_INTEGER, 0),
74 	ASN1_SIMPLE(ASProviderAttestation, customerASID, ASN1_INTEGER),
75 	ASN1_SEQUENCE_OF(ASProviderAttestation, providers, ProviderAS),
76 } ASN1_SEQUENCE_END(ASProviderAttestation);
77 
78 DECLARE_ASN1_FUNCTIONS(ASProviderAttestation);
79 IMPLEMENT_ASN1_FUNCTIONS(ASProviderAttestation);
80 
81 /*
82  * Parse the ProviderASSet sequence.
83  * Return zero on failure, non-zero on success.
84  */
85 static int
86 aspa_parse_providers(struct parse *p, const STACK_OF(ProviderAS) *providers)
87 {
88 	ProviderAS		*pa;
89 	struct aspa_provider	 provider;
90 	size_t			 providersz, i;
91 
92 	if ((providersz = sk_ProviderAS_num(providers)) == 0) {
93 		warnx("%s: ASPA: ProviderASSet needs at least one entry",
94 		    p->fn);
95 		return 0;
96 	}
97 
98 	if (providersz >= MAX_ASPA_PROVIDERS) {
99 		warnx("%s: ASPA: too many providers (more than %d)", p->fn,
100 		    MAX_ASPA_PROVIDERS);
101 		return 0;
102 	}
103 
104 	p->res->providers = calloc(providersz, sizeof(provider));
105 	if (p->res->providers == NULL)
106 		err(1, NULL);
107 
108 	for (i = 0; i < providersz; i++) {
109 		pa = sk_ProviderAS_value(providers, i);
110 
111 		memset(&provider, 0, sizeof(provider));
112 
113 		if (!as_id_parse(pa->providerASID, &provider.as)) {
114 			warnx("%s: ASPA: malformed ProviderAS", p->fn);
115 			return 0;
116 		}
117 
118 		if (p->res->custasid == provider.as) {
119 			warnx("%s: ASPA: CustomerASID can't also be Provider",
120 			    p->fn);
121 			return 0;
122 		}
123 
124 		if (i > 0) {
125 			if  (p->res->providers[i - 1].as > provider.as) {
126 				warnx("%s: ASPA: invalid ProviderASSet order",
127 				    p->fn);
128 				return 0;
129 			}
130 			if (p->res->providers[i - 1].as == provider.as) {
131 				warnx("%s: ASPA: duplicate ProviderAS", p->fn);
132 				return 0;
133 			}
134 		}
135 
136 		if (pa->afiLimit != NULL && !ip_addr_afi_parse(p->fn,
137 		    pa->afiLimit, &provider.afi)) {
138 			warnx("%s: ASPA: invalid afiLimit", p->fn);
139 			return 0;
140 		}
141 
142 		p->res->providers[p->res->providersz++] = provider;
143 	}
144 
145 	return 1;
146 }
147 
148 /*
149  * Parse the eContent of an ASPA file.
150  * Returns zero on failure, non-zero on success.
151  */
152 static int
153 aspa_parse_econtent(const unsigned char *d, size_t dsz, struct parse *p)
154 {
155 	ASProviderAttestation	*aspa;
156 	int			 rc = 0;
157 
158 	if ((aspa = d2i_ASProviderAttestation(NULL, &d, dsz)) == NULL) {
159 		cryptowarnx("%s: ASPA: failed to parse ASProviderAttestation",
160 		    p->fn);
161 		goto out;
162 	}
163 
164 	if (!valid_econtent_version(p->fn, aspa->version))
165 		goto out;
166 
167 	if (!as_id_parse(aspa->customerASID, &p->res->custasid)) {
168 		warnx("%s: malformed CustomerASID", p->fn);
169 		goto out;
170 	}
171 
172 	if (!aspa_parse_providers(p, aspa->providers))
173 		goto out;
174 
175 	rc = 1;
176  out:
177 	ASProviderAttestation_free(aspa);
178 	return rc;
179 }
180 
181 /*
182  * Parse a full ASPA file.
183  * Returns the payload or NULL if the file was malformed.
184  */
185 struct aspa *
186 aspa_parse(X509 **x509, const char *fn, const unsigned char *der, size_t len)
187 {
188 	struct parse	 p;
189 	size_t		 cmsz;
190 	unsigned char	*cms;
191 	struct cert	*cert = NULL;
192 	time_t		 signtime = 0;
193 	int		 rc = 0;
194 
195 	memset(&p, 0, sizeof(struct parse));
196 	p.fn = fn;
197 
198 	cms = cms_parse_validate(x509, fn, der, len, aspa_oid, &cmsz,
199 	    &signtime);
200 	if (cms == NULL)
201 		return NULL;
202 
203 	if ((p.res = calloc(1, sizeof(*p.res))) == NULL)
204 		err(1, NULL);
205 
206 	p.res->signtime = signtime;
207 
208 	if (!x509_get_aia(*x509, fn, &p.res->aia))
209 		goto out;
210 	if (!x509_get_aki(*x509, fn, &p.res->aki))
211 		goto out;
212 	if (!x509_get_sia(*x509, fn, &p.res->sia))
213 		goto out;
214 	if (!x509_get_ski(*x509, fn, &p.res->ski))
215 		goto out;
216 	if (p.res->aia == NULL || p.res->aki == NULL || p.res->sia == NULL ||
217 	    p.res->ski == NULL) {
218 		warnx("%s: RFC 6487 section 4.8: "
219 		    "missing AIA, AKI, SIA, or SKI X509 extension", fn);
220 		goto out;
221 	}
222 
223 	if (X509_get_ext_by_NID(*x509, NID_sbgp_ipAddrBlock, -1) != -1) {
224 		warnx("%s: superfluous IP Resources extension present", fn);
225 		goto out;
226 	}
227 
228 	if (!x509_get_notbefore(*x509, fn, &p.res->notbefore))
229 		goto out;
230 	if (!x509_get_notafter(*x509, fn, &p.res->notafter))
231 		goto out;
232 
233 	if (x509_any_inherits(*x509)) {
234 		warnx("%s: inherit elements not allowed in EE cert", fn);
235 		goto out;
236 	}
237 
238 	if (!aspa_parse_econtent(cms, cmsz, &p))
239 		goto out;
240 
241 	if ((cert = cert_parse_ee_cert(fn, *x509)) == NULL)
242 		goto out;
243 
244 	p.res->valid = valid_aspa(fn, cert, p.res);
245 
246 	rc = 1;
247  out:
248 	if (rc == 0) {
249 		aspa_free(p.res);
250 		p.res = NULL;
251 		X509_free(*x509);
252 		*x509 = NULL;
253 	}
254 	cert_free(cert);
255 	free(cms);
256 	return p.res;
257 }
258 
259 /*
260  * Free an ASPA pointer.
261  * Safe to call with NULL.
262  */
263 void
264 aspa_free(struct aspa *p)
265 {
266 	if (p == NULL)
267 		return;
268 
269 	free(p->aia);
270 	free(p->aki);
271 	free(p->sia);
272 	free(p->ski);
273 	free(p->providers);
274 	free(p);
275 }
276 
277 /*
278  * Serialise parsed ASPA content.
279  * See aspa_read() for the reader on the other side.
280  */
281 void
282 aspa_buffer(struct ibuf *b, const struct aspa *p)
283 {
284 	io_simple_buffer(b, &p->valid, sizeof(p->valid));
285 	io_simple_buffer(b, &p->custasid, sizeof(p->custasid));
286 	io_simple_buffer(b, &p->talid, sizeof(p->talid));
287 	io_simple_buffer(b, &p->expires, sizeof(p->expires));
288 
289 	io_simple_buffer(b, &p->providersz, sizeof(size_t));
290 	io_simple_buffer(b, p->providers,
291 	    p->providersz * sizeof(p->providers[0]));
292 
293 	io_str_buffer(b, p->aia);
294 	io_str_buffer(b, p->aki);
295 	io_str_buffer(b, p->ski);
296 }
297 
298 /*
299  * Read parsed ASPA content from descriptor.
300  * See aspa_buffer() for writer.
301  * Result must be passed to aspa_free().
302  */
303 struct aspa *
304 aspa_read(struct ibuf *b)
305 {
306 	struct aspa	*p;
307 
308 	if ((p = calloc(1, sizeof(struct aspa))) == NULL)
309 		err(1, NULL);
310 
311 	io_read_buf(b, &p->valid, sizeof(p->valid));
312 	io_read_buf(b, &p->custasid, sizeof(p->custasid));
313 	io_read_buf(b, &p->talid, sizeof(p->talid));
314 	io_read_buf(b, &p->expires, sizeof(p->expires));
315 
316 	io_read_buf(b, &p->providersz, sizeof(size_t));
317 	if ((p->providers = calloc(p->providersz,
318 	    sizeof(struct aspa_provider))) == NULL)
319 		err(1, NULL);
320 	io_read_buf(b, p->providers, p->providersz * sizeof(p->providers[0]));
321 
322 	io_read_str(b, &p->aia);
323 	io_read_str(b, &p->aki);
324 	io_read_str(b, &p->ski);
325 	assert(p->aia && p->aki && p->ski);
326 
327 	return p;
328 }
329 
330 /*
331  * Insert a new aspa_provider at index idx in the struct vap v.
332  * All elements in the provider array from idx are moved up by one
333  * to make space for the new element.
334  */
335 static void
336 insert_vap(struct vap *v, uint32_t idx, struct aspa_provider *p)
337 {
338 	if (idx < v->providersz)
339 		memmove(v->providers + idx + 1, v->providers + idx,
340 		    (v->providersz - idx) * sizeof(*v->providers));
341 	v->providers[idx] = *p;
342 	v->providersz++;
343 }
344 
345 /*
346  * Add each ProviderAS entry into the Validated ASPA Providers (VAP) tree.
347  * Duplicated entries are merged.
348  */
349 void
350 aspa_insert_vaps(struct vap_tree *tree, struct aspa *aspa, struct repo *rp)
351 {
352 	struct vap	*v, *found;
353 	size_t		 i, j;
354 
355 	if ((v = calloc(1, sizeof(*v))) == NULL)
356 		err(1, NULL);
357 	v->custasid = aspa->custasid;
358 	v->talid = aspa->talid;
359 	if (rp != NULL)
360 		v->repoid = repo_id(rp);
361 	else
362 		v->repoid = 0;
363 	v->expires = aspa->expires;
364 
365 	if ((found = RB_INSERT(vap_tree, tree, v)) != NULL) {
366 		if (found->expires > v->expires) {
367 			/* decrement found */
368 			repo_stat_inc(repo_byid(found->repoid), found->talid,
369 			    RTYPE_ASPA, STYPE_DEC_UNIQUE);
370 			found->expires = v->expires;
371 			found->talid = v->talid;
372 			found->repoid = v->repoid;
373 			repo_stat_inc(rp, v->talid, RTYPE_ASPA, STYPE_UNIQUE);
374 		}
375 		free(v);
376 		v = found;
377 	} else
378 		repo_stat_inc(rp, v->talid, RTYPE_ASPA, STYPE_UNIQUE);
379 
380 	repo_stat_inc(rp, aspa->talid, RTYPE_ASPA, STYPE_TOTAL);
381 
382 	v->providers = reallocarray(v->providers,
383 	    v->providersz + aspa->providersz, sizeof(*v->providers));
384 	if (v->providers == NULL)
385 		err(1, NULL);
386 
387 	/*
388 	 * Merge all data from aspa into v: loop over all aspa providers,
389 	 * insert them in the right place in v->providers while keeping the
390 	 * order of the providers array.
391 	 */
392 	for (i = 0, j = 0; i < aspa->providersz; ) {
393 		if (j == v->providersz ||
394 		    aspa->providers[i].as < v->providers[j].as) {
395 			/* merge provider from aspa into v */
396 			repo_stat_inc(rp, v->talid, RTYPE_ASPA,
397 			    STYPE_BOTH + aspa->providers[i].afi);
398 			insert_vap(v, j, &aspa->providers[i]);
399 			i++;
400 		} else if (aspa->providers[i].as == v->providers[j].as) {
401 			/* duplicate provider, merge afi */
402 			if (v->providers[j].afi != aspa->providers[i].afi) {
403 				repo_stat_inc(rp, v->talid, RTYPE_ASPA,
404 				    STYPE_BOTH + aspa->providers[i].afi);
405 				v->providers[j].afi = 0;
406 			}
407 			i++;
408 		}
409 		if (j < v->providersz)
410 			j++;
411 	}
412 }
413 
414 static inline int
415 vapcmp(struct vap *a, struct vap *b)
416 {
417 	if (a->custasid > b->custasid)
418 		return 1;
419 	if (a->custasid < b->custasid)
420 		return -1;
421 
422 	return 0;
423 }
424 
425 RB_GENERATE(vap_tree, vap, entry, vapcmp);
426