1 /* $OpenBSD: filemode.c,v 1.42 2024/05/20 15:51:43 claudio Exp $ */ 2 /* 3 * Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org> 4 * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/queue.h> 20 #include <sys/tree.h> 21 #include <sys/types.h> 22 23 #include <assert.h> 24 #include <err.h> 25 #include <fcntl.h> 26 #include <poll.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <limits.h> 31 #include <unistd.h> 32 #include <imsg.h> 33 34 #include <openssl/asn1.h> 35 #include <openssl/err.h> 36 #include <openssl/evp.h> 37 #include <openssl/pem.h> 38 #include <openssl/x509.h> 39 #include <openssl/x509v3.h> 40 41 #include "extern.h" 42 #include "json.h" 43 44 static X509_STORE_CTX *ctx; 45 static struct auth_tree auths = RB_INITIALIZER(&auths); 46 static struct crl_tree crlt = RB_INITIALIZER(&crlt); 47 48 struct tal *talobj[TALSZ_MAX]; 49 50 struct uripath { 51 RB_ENTRY(uripath) entry; 52 const char *uri; 53 struct cert *cert; 54 }; 55 56 static RB_HEAD(uripath_tree, uripath) uritree; 57 58 static inline int 59 uripathcmp(const struct uripath *a, const struct uripath *b) 60 { 61 return strcmp(a->uri, b->uri); 62 } 63 64 RB_PROTOTYPE(uripath_tree, uripath, entry, uripathcmp); 65 66 static void 67 uripath_add(const char *uri, struct cert *cert) 68 { 69 struct uripath *up; 70 71 if ((up = calloc(1, sizeof(*up))) == NULL) 72 err(1, NULL); 73 if ((up->uri = strdup(uri)) == NULL) 74 err(1, NULL); 75 up->cert = cert; 76 if (RB_INSERT(uripath_tree, &uritree, up) != NULL) 77 errx(1, "corrupt AIA lookup tree"); 78 } 79 80 static struct cert * 81 uripath_lookup(const char *uri) 82 { 83 struct uripath needle = { .uri = uri }; 84 struct uripath *up; 85 86 up = RB_FIND(uripath_tree, &uritree, &needle); 87 if (up == NULL) 88 return NULL; 89 return up->cert; 90 } 91 92 RB_GENERATE(uripath_tree, uripath, entry, uripathcmp); 93 94 /* 95 * Use the X509 CRL Distribution Points to locate the CRL needed for 96 * verification. 97 */ 98 static void 99 parse_load_crl(char *uri) 100 { 101 struct crl *crl; 102 char *f; 103 size_t flen; 104 105 if (uri == NULL) 106 return; 107 if (strncmp(uri, RSYNC_PROTO, RSYNC_PROTO_LEN) != 0) { 108 warnx("bad CRL distribution point URI %s", uri); 109 return; 110 } 111 uri += RSYNC_PROTO_LEN; 112 113 f = load_file(uri, &flen); 114 if (f == NULL) { 115 warn("parse file %s", uri); 116 return; 117 } 118 119 crl = crl_parse(uri, f, flen); 120 if (crl != NULL && !crl_insert(&crlt, crl)) 121 crl_free(crl); 122 123 free(f); 124 } 125 126 /* 127 * Parse the cert pointed at by the AIA URI while doing that also load 128 * the CRL of this cert. While the CRL is validated the returned cert 129 * is not. The caller needs to make sure it is validated once all 130 * necessary certs were loaded. Returns NULL on failure. 131 */ 132 static struct cert * 133 parse_load_cert(char *uri) 134 { 135 struct cert *cert = NULL; 136 char *f; 137 size_t flen; 138 139 if (uri == NULL) 140 return NULL; 141 142 if (strncmp(uri, RSYNC_PROTO, RSYNC_PROTO_LEN) != 0) { 143 warnx("bad authority information access URI %s", uri); 144 return NULL; 145 } 146 uri += RSYNC_PROTO_LEN; 147 148 f = load_file(uri, &flen); 149 if (f == NULL) { 150 warn("parse file %s", uri); 151 goto done; 152 } 153 154 cert = cert_parse_pre(uri, f, flen); 155 free(f); 156 157 if (cert == NULL) 158 goto done; 159 if (cert->purpose != CERT_PURPOSE_CA) { 160 warnx("AIA reference to bgpsec cert %s", uri); 161 goto done; 162 } 163 /* try to load the CRL of this cert */ 164 parse_load_crl(cert->crl); 165 166 return cert; 167 168 done: 169 cert_free(cert); 170 return NULL; 171 } 172 173 /* 174 * Build the certificate chain by using the Authority Information Access. 175 * This requires that the TA are already validated and added to the auths 176 * tree. Once the TA is located in the chain the chain is validated in 177 * reverse order. 178 */ 179 static struct auth * 180 parse_load_certchain(char *uri) 181 { 182 struct cert *stack[MAX_CERT_DEPTH] = { 0 }; 183 char *filestack[MAX_CERT_DEPTH]; 184 struct cert *cert; 185 struct crl *crl; 186 struct auth *a; 187 const char *errstr; 188 int i; 189 190 for (i = 0; i < MAX_CERT_DEPTH; i++) { 191 if ((cert = uripath_lookup(uri)) != NULL) { 192 a = auth_find(&auths, cert->certid); 193 break; 194 } 195 filestack[i] = uri; 196 stack[i] = cert = parse_load_cert(uri); 197 if (cert == NULL || cert->purpose != CERT_PURPOSE_CA) { 198 warnx("failed to build authority chain: %s", uri); 199 goto fail; 200 } 201 uri = cert->aia; 202 } 203 204 if (i >= MAX_CERT_DEPTH) { 205 warnx("authority chain exceeds max depth of %d", 206 MAX_CERT_DEPTH); 207 goto fail; 208 } 209 210 /* TA found play back the stack and add all certs */ 211 for (; i > 0; i--) { 212 cert = stack[i - 1]; 213 uri = filestack[i - 1]; 214 215 crl = crl_get(&crlt, a); 216 if (!valid_x509(uri, ctx, cert->x509, a, crl, &errstr) || 217 !valid_cert(uri, a, cert)) { 218 if (errstr != NULL) 219 warnx("%s: %s", uri, errstr); 220 goto fail; 221 } 222 cert->talid = a->cert->talid; 223 a = auth_insert(uri, &auths, cert, a); 224 uripath_add(uri, cert); 225 stack[i] = NULL; 226 } 227 228 return a; 229 fail: 230 for (i = 0; i < MAX_CERT_DEPTH; i++) 231 cert_free(stack[i]); 232 return NULL; 233 } 234 235 static void 236 parse_load_ta(struct tal *tal) 237 { 238 const char *filename; 239 struct cert *cert; 240 unsigned char *f = NULL; 241 char *file; 242 size_t flen, i; 243 244 /* does not matter which URI, all end with same filename */ 245 filename = strrchr(tal->uri[0], '/'); 246 assert(filename); 247 248 if (asprintf(&file, "ta/%s%s", tal->descr, filename) == -1) 249 err(1, NULL); 250 251 f = load_file(file, &flen); 252 if (f == NULL) { 253 warn("parse file %s", file); 254 goto out; 255 } 256 257 /* Extract certificate data. */ 258 cert = cert_parse_pre(file, f, flen); 259 cert = ta_parse(file, cert, tal->pkey, tal->pkeysz); 260 if (cert == NULL) 261 goto out; 262 263 cert->talid = tal->id; 264 auth_insert(file, &auths, cert, NULL); 265 for (i = 0; i < tal->urisz; i++) { 266 if (strncasecmp(tal->uri[i], RSYNC_PROTO, RSYNC_PROTO_LEN) != 0) 267 continue; 268 /* Add all rsync uri since any of them could be used as AIA. */ 269 uripath_add(tal->uri[i], cert); 270 } 271 272 out: 273 free(file); 274 free(f); 275 } 276 277 static struct tal * 278 find_tal(struct cert *cert) 279 { 280 EVP_PKEY *pk, *opk; 281 struct tal *tal; 282 int i; 283 284 if ((opk = X509_get0_pubkey(cert->x509)) == NULL) 285 return NULL; 286 287 for (i = 0; i < TALSZ_MAX; i++) { 288 const unsigned char *pkey; 289 290 if (talobj[i] == NULL) 291 break; 292 tal = talobj[i]; 293 pkey = tal->pkey; 294 pk = d2i_PUBKEY(NULL, &pkey, tal->pkeysz); 295 if (pk == NULL) 296 continue; 297 if (EVP_PKEY_cmp(pk, opk) == 1) { 298 EVP_PKEY_free(pk); 299 return tal; 300 } 301 EVP_PKEY_free(pk); 302 } 303 return NULL; 304 } 305 306 static void 307 print_signature_path(const char *crl, const char *aia, const struct auth *a) 308 { 309 if (crl != NULL) 310 printf("Signature path: %s\n", crl); 311 if (a->cert->mft != NULL) 312 printf(" %s\n", a->cert->mft); 313 if (aia != NULL) 314 printf(" %s\n", aia); 315 316 for (; a != NULL; a = a->issuer) { 317 if (a->cert->crl != NULL) 318 printf(" %s\n", a->cert->crl); 319 if (a->issuer != NULL && a->issuer->cert != NULL && 320 a->issuer->cert->mft != NULL) 321 printf(" %s\n", 322 a->issuer->cert->mft); 323 if (a->cert->aia != NULL) 324 printf(" %s\n", a->cert->aia); 325 } 326 } 327 328 /* 329 * Parse file passed with -f option. 330 */ 331 static void 332 proc_parser_file(char *file, unsigned char *buf, size_t len) 333 { 334 static int num; 335 X509 *x509 = NULL; 336 struct aspa *aspa = NULL; 337 struct cert *cert = NULL; 338 struct crl *crl = NULL; 339 struct gbr *gbr = NULL; 340 struct geofeed *geofeed = NULL; 341 struct mft *mft = NULL; 342 struct roa *roa = NULL; 343 struct rsc *rsc = NULL; 344 struct spl *spl = NULL; 345 struct tak *tak = NULL; 346 struct tal *tal = NULL; 347 char *aia = NULL; 348 char *crl_uri = NULL; 349 time_t *expires = NULL, *notafter = NULL; 350 struct auth *a; 351 struct crl *c; 352 const char *errstr = NULL, *valid; 353 int status = 0; 354 char filehash[SHA256_DIGEST_LENGTH]; 355 char *hash; 356 enum rtype type; 357 int is_ta = 0; 358 359 if (outformats & FORMAT_JSON) { 360 json_do_start(stdout); 361 } else { 362 if (num++ > 0) 363 printf("--\n"); 364 } 365 366 if (strncmp(file, RSYNC_PROTO, RSYNC_PROTO_LEN) == 0) { 367 file += RSYNC_PROTO_LEN; 368 buf = load_file(file, &len); 369 if (buf == NULL) { 370 warn("parse file %s", file); 371 return; 372 } 373 } 374 375 if (!EVP_Digest(buf, len, filehash, NULL, EVP_sha256(), NULL)) 376 errx(1, "EVP_Digest failed in %s", __func__); 377 378 if (base64_encode(filehash, sizeof(filehash), &hash) == -1) 379 errx(1, "base64_encode failed in %s", __func__); 380 381 if (outformats & FORMAT_JSON) { 382 json_do_string("file", file); 383 json_do_string("hash_id", hash); 384 } else { 385 printf("File: %s\n", file); 386 printf("Hash identifier: %s\n", hash); 387 } 388 389 free(hash); 390 391 type = rtype_from_file_extension(file); 392 393 switch (type) { 394 case RTYPE_ASPA: 395 aspa = aspa_parse(&x509, file, -1, buf, len); 396 if (aspa == NULL) 397 break; 398 aia = aspa->aia; 399 expires = &aspa->expires; 400 notafter = &aspa->notafter; 401 break; 402 case RTYPE_CER: 403 cert = cert_parse_pre(file, buf, len); 404 if (cert == NULL) 405 break; 406 is_ta = X509_get_extension_flags(cert->x509) & EXFLAG_SS; 407 if (!is_ta) 408 cert = cert_parse(file, cert); 409 if (cert == NULL) 410 break; 411 aia = cert->aia; 412 x509 = cert->x509; 413 if (X509_up_ref(x509) == 0) 414 errx(1, "%s: X509_up_ref failed", __func__); 415 expires = &cert->expires; 416 notafter = &cert->notafter; 417 break; 418 case RTYPE_CRL: 419 crl = crl_parse(file, buf, len); 420 if (crl == NULL) 421 break; 422 crl_print(crl); 423 break; 424 case RTYPE_MFT: 425 mft = mft_parse(&x509, file, -1, buf, len); 426 if (mft == NULL) 427 break; 428 aia = mft->aia; 429 expires = &mft->expires; 430 notafter = &mft->nextupdate; 431 break; 432 case RTYPE_GBR: 433 gbr = gbr_parse(&x509, file, -1, buf, len); 434 if (gbr == NULL) 435 break; 436 aia = gbr->aia; 437 expires = &gbr->expires; 438 notafter = &gbr->notafter; 439 break; 440 case RTYPE_GEOFEED: 441 geofeed = geofeed_parse(&x509, file, -1, buf, len); 442 if (geofeed == NULL) 443 break; 444 aia = geofeed->aia; 445 expires = &geofeed->expires; 446 notafter = &geofeed->notafter; 447 break; 448 case RTYPE_ROA: 449 roa = roa_parse(&x509, file, -1, buf, len); 450 if (roa == NULL) 451 break; 452 aia = roa->aia; 453 expires = &roa->expires; 454 notafter = &roa->notafter; 455 break; 456 case RTYPE_RSC: 457 rsc = rsc_parse(&x509, file, -1, buf, len); 458 if (rsc == NULL) 459 break; 460 aia = rsc->aia; 461 expires = &rsc->expires; 462 notafter = &rsc->notafter; 463 break; 464 case RTYPE_SPL: 465 spl = spl_parse(&x509, file, -1, buf, len); 466 if (spl == NULL) 467 break; 468 aia = spl->aia; 469 expires = &spl->expires; 470 notafter = &spl->notafter; 471 break; 472 case RTYPE_TAK: 473 tak = tak_parse(&x509, file, -1, buf, len); 474 if (tak == NULL) 475 break; 476 aia = tak->aia; 477 expires = &tak->expires; 478 notafter = &tak->notafter; 479 break; 480 case RTYPE_TAL: 481 tal = tal_parse(file, buf, len); 482 if (tal == NULL) 483 break; 484 tal_print(tal); 485 break; 486 default: 487 printf("%s: unsupported file type\n", file); 488 break; 489 } 490 491 if (aia != NULL) { 492 x509_get_crl(x509, file, &crl_uri); 493 parse_load_crl(crl_uri); 494 a = parse_load_certchain(aia); 495 c = crl_get(&crlt, a); 496 497 if ((status = valid_x509(file, ctx, x509, a, c, &errstr))) { 498 switch (type) { 499 case RTYPE_ASPA: 500 status = aspa->valid; 501 break; 502 case RTYPE_GEOFEED: 503 status = geofeed->valid; 504 break; 505 case RTYPE_ROA: 506 status = roa->valid; 507 break; 508 case RTYPE_RSC: 509 status = rsc->valid; 510 break; 511 case RTYPE_SPL: 512 status = spl->valid; 513 default: 514 break; 515 } 516 } 517 if (status && cert == NULL) { 518 struct cert *eecert; 519 520 eecert = cert_parse_ee_cert(file, a->cert->talid, x509); 521 if (eecert == NULL) 522 status = 0; 523 cert_free(eecert); 524 } else if (status) { 525 cert->talid = a->cert->talid; 526 constraints_validate(file, cert); 527 } 528 } else if (is_ta) { 529 if ((tal = find_tal(cert)) != NULL) { 530 cert = ta_parse(file, cert, tal->pkey, tal->pkeysz); 531 status = (cert != NULL); 532 if (outformats & FORMAT_JSON) 533 json_do_string("tal", tal->descr); 534 else 535 printf("TAL: %s\n", 536 tal->descr); 537 tal = NULL; 538 } else { 539 cert_free(cert); 540 cert = NULL; 541 expires = NULL; 542 status = 0; 543 } 544 } 545 546 if (expires != NULL) { 547 if (status && aia != NULL) 548 *expires = x509_find_expires(*notafter, a, &crlt); 549 550 switch (type) { 551 case RTYPE_ASPA: 552 aspa_print(x509, aspa); 553 break; 554 case RTYPE_CER: 555 cert_print(cert); 556 break; 557 case RTYPE_GBR: 558 gbr_print(x509, gbr); 559 break; 560 case RTYPE_GEOFEED: 561 geofeed_print(x509, geofeed); 562 break; 563 case RTYPE_MFT: 564 mft_print(x509, mft); 565 break; 566 case RTYPE_ROA: 567 roa_print(x509, roa); 568 break; 569 case RTYPE_RSC: 570 rsc_print(x509, rsc); 571 break; 572 case RTYPE_SPL: 573 spl_print(x509, spl); 574 break; 575 case RTYPE_TAK: 576 tak_print(x509, tak); 577 break; 578 default: 579 break; 580 } 581 } 582 583 if (status) 584 valid = "OK"; 585 else if (aia == NULL) 586 valid = "N/A"; 587 else 588 valid = "Failed"; 589 590 if (outformats & FORMAT_JSON) { 591 json_do_string("validation", valid); 592 if (errstr != NULL) 593 json_do_string("error", errstr); 594 } else { 595 printf("Validation: %s", valid); 596 if (errstr != NULL) 597 printf(", %s", errstr); 598 } 599 600 if (outformats & FORMAT_JSON) 601 json_do_finish(); 602 else { 603 printf("\n"); 604 605 if (status && aia != NULL) { 606 print_signature_path(crl_uri, aia, a); 607 if (expires != NULL) 608 printf("Signature path expires: %s\n", 609 time2str(*expires)); 610 } 611 612 if (x509 == NULL) 613 goto out; 614 if (type == RTYPE_TAL || type == RTYPE_CRL) 615 goto out; 616 617 if (verbose) { 618 if (!X509_print_fp(stdout, x509)) 619 errx(1, "X509_print_fp"); 620 } 621 622 if (verbose > 1) { 623 if (!PEM_write_X509(stdout, x509)) 624 errx(1, "PEM_write_X509"); 625 } 626 } 627 628 out: 629 free(crl_uri); 630 X509_free(x509); 631 aspa_free(aspa); 632 cert_free(cert); 633 crl_free(crl); 634 gbr_free(gbr); 635 geofeed_free(geofeed); 636 mft_free(mft); 637 roa_free(roa); 638 rsc_free(rsc); 639 tak_free(tak); 640 tal_free(tal); 641 } 642 643 /* 644 * Process a file request, in general don't send anything back. 645 */ 646 static void 647 parse_file(struct entityq *q, struct msgbuf *msgq) 648 { 649 struct entity *entp; 650 struct ibuf *b; 651 struct tal *tal; 652 time_t dummy = 0; 653 654 while ((entp = TAILQ_FIRST(q)) != NULL) { 655 TAILQ_REMOVE(q, entp, entries); 656 657 switch (entp->type) { 658 case RTYPE_FILE: 659 proc_parser_file(entp->file, entp->data, entp->datasz); 660 break; 661 case RTYPE_TAL: 662 if ((tal = tal_parse(entp->file, entp->data, 663 entp->datasz)) == NULL) 664 errx(1, "%s: could not parse tal file", 665 entp->file); 666 tal->id = entp->talid; 667 talobj[tal->id] = tal; 668 parse_load_ta(tal); 669 break; 670 default: 671 errx(1, "unhandled entity type %d", entp->type); 672 } 673 674 b = io_new_buffer(); 675 io_simple_buffer(b, &entp->type, sizeof(entp->type)); 676 io_simple_buffer(b, &entp->repoid, sizeof(entp->repoid)); 677 io_simple_buffer(b, &entp->talid, sizeof(entp->talid)); 678 io_str_buffer(b, entp->file); 679 io_simple_buffer(b, &dummy, sizeof(dummy)); 680 io_close_buffer(msgq, b); 681 entity_free(entp); 682 } 683 } 684 685 /* 686 * Process responsible for parsing and validating content. 687 * All this process does is wait to be told about a file to parse, then 688 * it parses it and makes sure that the data being returned is fully 689 * validated and verified. 690 * The process will exit cleanly only when fd is closed. 691 */ 692 void 693 proc_filemode(int fd) 694 { 695 struct entityq q; 696 struct msgbuf msgq; 697 struct pollfd pfd; 698 struct entity *entp; 699 struct ibuf *b, *inbuf = NULL; 700 701 /* Only allow access to the cache directory. */ 702 if (unveil(".", "r") == -1) 703 err(1, "unveil cachedir"); 704 if (pledge("stdio rpath", NULL) == -1) 705 err(1, "pledge"); 706 707 ERR_load_crypto_strings(); 708 OpenSSL_add_all_ciphers(); 709 OpenSSL_add_all_digests(); 710 x509_init_oid(); 711 constraints_parse(); 712 713 if ((ctx = X509_STORE_CTX_new()) == NULL) 714 err(1, "X509_STORE_CTX_new"); 715 TAILQ_INIT(&q); 716 717 msgbuf_init(&msgq); 718 msgq.fd = fd; 719 720 pfd.fd = fd; 721 722 for (;;) { 723 pfd.events = POLLIN; 724 if (msgq.queued) 725 pfd.events |= POLLOUT; 726 727 if (poll(&pfd, 1, INFTIM) == -1) { 728 if (errno == EINTR) 729 continue; 730 err(1, "poll"); 731 } 732 if ((pfd.revents & (POLLERR|POLLNVAL))) 733 errx(1, "poll: bad descriptor"); 734 735 /* If the parent closes, return immediately. */ 736 737 if ((pfd.revents & POLLHUP)) 738 break; 739 740 if ((pfd.revents & POLLIN)) { 741 b = io_buf_read(fd, &inbuf); 742 if (b != NULL) { 743 entp = calloc(1, sizeof(struct entity)); 744 if (entp == NULL) 745 err(1, NULL); 746 entity_read_req(b, entp); 747 TAILQ_INSERT_TAIL(&q, entp, entries); 748 ibuf_free(b); 749 } 750 } 751 752 if (pfd.revents & POLLOUT) { 753 switch (msgbuf_write(&msgq)) { 754 case 0: 755 errx(1, "write: connection closed"); 756 case -1: 757 err(1, "write"); 758 } 759 } 760 761 parse_file(&q, &msgq); 762 } 763 764 msgbuf_clear(&msgq); 765 while ((entp = TAILQ_FIRST(&q)) != NULL) { 766 TAILQ_REMOVE(&q, entp, entries); 767 entity_free(entp); 768 } 769 770 auth_tree_free(&auths); 771 crl_tree_free(&crlt); 772 773 X509_STORE_CTX_free(ctx); 774 ibuf_free(inbuf); 775 776 exit(0); 777 } 778