1 /* $OpenBSD: spl.c,v 1.7 2024/11/13 12:51:04 tb 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 num_afps, num_prefixes;
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 num_afps = sk_AddressFamilyPrefixes_num(spl_asn1->prefixBlocks);
164 if (num_afps < 0 || num_afps > 2) {
165 warnx("%s: unexpected number of AddressFamilyAddressPrefixes"
166 "(got %d, expected 0, 1, or 2)", fn, num_afps);
167 goto out;
168 }
169
170 for (i = 0; i < num_afps; i++) {
171 struct ip_addr *prev_ip_addr = NULL;
172
173 afp = sk_AddressFamilyPrefixes_value(spl_asn1->prefixBlocks, i);
174 prefixes = afp->addressPrefixes;
175 num_prefixes = sk_ASN1_BIT_STRING_num(afp->addressPrefixes);
176
177 if (num_prefixes == 0) {
178 warnx("%s: empty AddressFamilyAddressPrefixes", fn);
179 goto out;
180 }
181 if (spl->num_prefixes + num_prefixes >= 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->prefixes = recallocarray(spl->prefixes, spl->num_prefixes,
211 spl->num_prefixes + num_prefixes, sizeof(spl->prefixes[0]));
212 if (spl->prefixes == NULL)
213 err(1, NULL);
214
215 for (j = 0; j < num_prefixes; 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->prefixes[spl->num_prefixes++];
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->num_ases == 0) {
295 warnx("%s: AS Resources extension missing", fn);
296 goto out;
297 }
298
299 if (cert->num_ips > 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->prefixes);
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->num_prefixes, sizeof(s->num_prefixes));
348 io_simple_buffer(b, &s->expires, sizeof(s->expires));
349
350 io_simple_buffer(b, s->prefixes,
351 s->num_prefixes * sizeof(s->prefixes[0]));
352
353 io_str_buffer(b, s->aia);
354 io_str_buffer(b, s->aki);
355 io_str_buffer(b, s->ski);
356 }
357
358 /*
359 * Read parsed SPL content from descriptor.
360 * See spl_buffer() for writer.
361 * Result must be passed to spl_free().
362 */
363 struct spl *
spl_read(struct ibuf * b)364 spl_read(struct ibuf *b)
365 {
366 struct spl *s;
367
368 if ((s = calloc(1, sizeof(struct spl))) == NULL)
369 err(1, NULL);
370
371 io_read_buf(b, &s->valid, sizeof(s->valid));
372 io_read_buf(b, &s->asid, sizeof(s->asid));
373 io_read_buf(b, &s->talid, sizeof(s->talid));
374 io_read_buf(b, &s->num_prefixes, sizeof(s->num_prefixes));
375 io_read_buf(b, &s->expires, sizeof(s->expires));
376
377 if (s->num_prefixes > 0) {
378 if ((s->prefixes = calloc(s->num_prefixes,
379 sizeof(s->prefixes[0]))) == NULL)
380 err(1, NULL);
381 io_read_buf(b, s->prefixes,
382 s->num_prefixes * sizeof(s->prefixes[0]));
383 }
384
385 io_read_str(b, &s->aia);
386 io_read_str(b, &s->aki);
387 io_read_str(b, &s->ski);
388 assert(s->aia && s->aki && s->ski);
389
390 return s;
391 }
392
393 static int
spl_pfx_cmp(const struct spl_pfx * a,const struct spl_pfx * b)394 spl_pfx_cmp(const struct spl_pfx *a, const struct spl_pfx *b)
395 {
396 if (a->afi > b->afi)
397 return 1;
398 if (a->afi < b->afi)
399 return -1;
400
401 return prefix_cmp(a->afi, &a->prefix, &b->prefix);
402 }
403
404 static void
insert_vsp(struct vsp * vsp,size_t idx,struct spl_pfx * pfx)405 insert_vsp(struct vsp *vsp, size_t idx, struct spl_pfx *pfx)
406 {
407 if (idx < vsp->num_prefixes)
408 memmove(vsp->prefixes + idx + 1, vsp->prefixes + idx,
409 (vsp->num_prefixes - idx) * sizeof(vsp->prefixes[0]));
410 vsp->prefixes[idx] = *pfx;
411 vsp->num_prefixes++;
412 }
413
414 /*
415 * Add each prefix in the SPL into the VSP tree.
416 * Updates "vsps" to be the number of VSPs and "uniqs" to be the unique
417 * number of prefixes.
418 */
419 void
spl_insert_vsps(struct vsp_tree * tree,struct spl * spl,struct repo * rp)420 spl_insert_vsps(struct vsp_tree *tree, struct spl *spl, struct repo *rp)
421 {
422 struct vsp *vsp, *found;
423 size_t i, j;
424 int cmp;
425
426 if ((vsp = calloc(1, sizeof(*vsp))) == NULL)
427 err(1, NULL);
428
429 vsp->asid = spl->asid;
430 vsp->talid = spl->talid;
431 vsp->expires = spl->expires;
432 if (rp != NULL)
433 vsp->repoid = repo_id(rp);
434
435 if ((found = RB_INSERT(vsp_tree, tree, vsp)) != NULL) {
436 /* already exists */
437 if (found->expires < vsp->expires) {
438 /* adjust unique count */
439 repo_stat_inc(repo_byid(found->repoid),
440 found->talid, RTYPE_SPL, STYPE_DEC_UNIQUE);
441 found->expires = vsp->expires;
442 found->talid = vsp->talid;
443 found->repoid = vsp->repoid;
444 repo_stat_inc(rp, vsp->talid, RTYPE_SPL,
445 STYPE_UNIQUE);
446 }
447 free(vsp);
448 vsp = found;
449 } else
450 repo_stat_inc(rp, vsp->talid, RTYPE_SPL, STYPE_UNIQUE);
451 repo_stat_inc(rp, spl->talid, RTYPE_SPL, STYPE_TOTAL);
452
453 /* merge content of multiple SPLs */
454 vsp->prefixes = reallocarray(vsp->prefixes,
455 vsp->num_prefixes + spl->num_prefixes, sizeof(vsp->prefixes[0]));
456 if (vsp->prefixes == NULL)
457 err(1, NULL);
458
459 /*
460 * Merge all data from the new SPL at hand into 'vsp': loop over
461 * all SPL->pfxs, and insert them in the right place in
462 * vsp->prefixes while keeping the order of the array.
463 */
464 for (i = 0, j = 0; i < spl->num_prefixes; ) {
465 cmp = -1;
466 if (j == vsp->num_prefixes ||
467 (cmp = spl_pfx_cmp(&spl->prefixes[i],
468 &vsp->prefixes[j])) < 0) {
469 insert_vsp(vsp, j, &spl->prefixes[i]);
470 i++;
471 } else if (cmp == 0)
472 i++;
473
474 if (j < vsp->num_prefixes)
475 j++;
476 }
477 }
478
479 /*
480 * Comparison function for the RB tree
481 */
482 static inline int
vspcmp(const struct vsp * a,const struct vsp * b)483 vspcmp(const struct vsp *a, const struct vsp *b)
484 {
485 if (a->asid > b->asid)
486 return 1;
487 if (a->asid < b->asid)
488 return -1;
489
490 return 0;
491 }
492
493 RB_GENERATE(vsp_tree, vsp, entry, vspcmp);
494