1 /* $OpenBSD: spl.c,v 1.1 2024/02/22 12:49:42 job 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 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 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 * 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 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 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 * 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 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 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 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 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