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