1/* $OpenBSD: filemode.c,v 1.47 2024/06/17 18:54:36 tb 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 44static X509_STORE_CTX *ctx; 45static struct auth_tree auths = RB_INITIALIZER(&auths); 46static struct crl_tree crlt = RB_INITIALIZER(&crlt); 47 48struct tal *talobj[TALSZ_MAX]; 49 50struct uripath { 51 RB_ENTRY(uripath) entry; 52 const char *uri; 53 struct cert *cert; 54}; 55 56static RB_HEAD(uripath_tree, uripath) uritree; 57 58static inline int 59uripathcmp(const struct uripath *a, const struct uripath *b) 60{ 61 return strcmp(a->uri, b->uri); 62} 63 64RB_PROTOTYPE(uripath_tree, uripath, entry, uripathcmp); 65 66static void 67uripath_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 80static struct cert * 81uripath_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 92RB_GENERATE(uripath_tree, uripath, entry, uripathcmp); 93 94/* 95 * Use the X509 CRL Distribution Points to locate the CRL needed for 96 * verification. 97 */ 98static void 99parse_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 */ 132static struct cert * 133parse_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 %s in %s", 161 purpose2str(cert->purpose), uri); 162 goto done; 163 } 164 /* try to load the CRL of this cert */ 165 parse_load_crl(cert->crl); 166 167 return cert; 168 169 done: 170 cert_free(cert); 171 return NULL; 172} 173 174/* 175 * Build the certificate chain by using the Authority Information Access. 176 * This requires that the TA are already validated and added to the auths 177 * tree. Once the TA is located in the chain the chain is validated in 178 * reverse order. 179 */ 180static struct auth * 181parse_load_certchain(char *uri) 182{ 183 struct cert *stack[MAX_CERT_DEPTH] = { 0 }; 184 char *filestack[MAX_CERT_DEPTH]; 185 struct cert *cert; 186 struct crl *crl; 187 struct auth *a; 188 const char *errstr; 189 int i; 190 191 for (i = 0; i < MAX_CERT_DEPTH; i++) { 192 if ((cert = uripath_lookup(uri)) != NULL) { 193 a = auth_find(&auths, cert->certid); 194 if (a == NULL) { 195 warnx("failed to find issuer for %s", uri); 196 goto fail; 197 } 198 break; 199 } 200 filestack[i] = uri; 201 stack[i] = cert = parse_load_cert(uri); 202 if (cert == NULL || cert->purpose != CERT_PURPOSE_CA) { 203 warnx("failed to build authority chain: %s", uri); 204 goto fail; 205 } 206 uri = cert->aia; 207 } 208 209 if (i >= MAX_CERT_DEPTH) { 210 warnx("authority chain exceeds max depth of %d", 211 MAX_CERT_DEPTH); 212 goto fail; 213 } 214 215 /* TA found play back the stack and add all certs */ 216 for (; i > 0; i--) { 217 cert = stack[i - 1]; 218 uri = filestack[i - 1]; 219 220 crl = crl_get(&crlt, a); 221 if (!valid_x509(uri, ctx, cert->x509, a, crl, &errstr) || 222 !valid_cert(uri, a, cert)) { 223 if (errstr != NULL) 224 warnx("%s: %s", uri, errstr); 225 goto fail; 226 } 227 cert->talid = a->cert->talid; 228 a = auth_insert(uri, &auths, cert, a); 229 uripath_add(uri, cert); 230 stack[i - 1] = NULL; 231 } 232 233 return a; 234fail: 235 for (i = 0; i < MAX_CERT_DEPTH; i++) 236 cert_free(stack[i]); 237 return NULL; 238} 239 240static void 241parse_load_ta(struct tal *tal) 242{ 243 const char *filename; 244 struct cert *cert; 245 unsigned char *f = NULL; 246 char *file; 247 size_t flen, i; 248 249 /* does not matter which URI, all end with same filename */ 250 filename = strrchr(tal->uri[0], '/'); 251 assert(filename); 252 253 if (asprintf(&file, "ta/%s%s", tal->descr, filename) == -1) 254 err(1, NULL); 255 256 f = load_file(file, &flen); 257 if (f == NULL) { 258 warn("parse file %s", file); 259 goto out; 260 } 261 262 /* Extract certificate data. */ 263 cert = cert_parse_pre(file, f, flen); 264 cert = ta_parse(file, cert, tal->pkey, tal->pkeysz); 265 if (cert == NULL) 266 goto out; 267 268 cert->talid = tal->id; 269 auth_insert(file, &auths, cert, NULL); 270 for (i = 0; i < tal->urisz; i++) { 271 if (strncasecmp(tal->uri[i], RSYNC_PROTO, RSYNC_PROTO_LEN) != 0) 272 continue; 273 /* Add all rsync uri since any of them could be used as AIA. */ 274 uripath_add(tal->uri[i], cert); 275 } 276 277out: 278 free(file); 279 free(f); 280} 281 282static struct tal * 283find_tal(struct cert *cert) 284{ 285 EVP_PKEY *pk, *opk; 286 struct tal *tal; 287 int i; 288 289 if ((opk = X509_get0_pubkey(cert->x509)) == NULL) 290 return NULL; 291 292 for (i = 0; i < TALSZ_MAX; i++) { 293 const unsigned char *pkey; 294 295 if (talobj[i] == NULL) 296 break; 297 tal = talobj[i]; 298 pkey = tal->pkey; 299 pk = d2i_PUBKEY(NULL, &pkey, tal->pkeysz); 300 if (pk == NULL) 301 continue; 302 if (EVP_PKEY_cmp(pk, opk) == 1) { 303 EVP_PKEY_free(pk); 304 return tal; 305 } 306 EVP_PKEY_free(pk); 307 } 308 return NULL; 309} 310 311static void 312print_signature_path(const char *crl, const char *aia, const struct auth *a) 313{ 314 if (crl != NULL) 315 printf("Signature path: %s\n", crl); 316 if (a->cert->mft != NULL) 317 printf(" %s\n", a->cert->mft); 318 if (aia != NULL) 319 printf(" %s\n", aia); 320 321 for (; a != NULL; a = a->issuer) { 322 if (a->cert->crl != NULL) 323 printf(" %s\n", a->cert->crl); 324 if (a->issuer != NULL && a->issuer->cert != NULL && 325 a->issuer->cert->mft != NULL) 326 printf(" %s\n", 327 a->issuer->cert->mft); 328 if (a->cert->aia != NULL) 329 printf(" %s\n", a->cert->aia); 330 } 331} 332 333/* 334 * Parse file passed with -f option. 335 */ 336static void 337proc_parser_file(char *file, unsigned char *buf, size_t len) 338{ 339 static int num; 340 X509 *x509 = NULL; 341 struct aspa *aspa = NULL; 342 struct cert *cert = NULL; 343 struct crl *crl = NULL; 344 struct gbr *gbr = NULL; 345 struct geofeed *geofeed = NULL; 346 struct mft *mft = NULL; 347 struct roa *roa = NULL; 348 struct rsc *rsc = NULL; 349 struct spl *spl = NULL; 350 struct tak *tak = NULL; 351 struct tal *tal = NULL; 352 char *aia = NULL; 353 char *crl_uri = NULL; 354 time_t *expires = NULL, *notafter = NULL; 355 struct auth *a; 356 struct crl *c; 357 const char *errstr = NULL, *valid; 358 int status = 0; 359 char filehash[SHA256_DIGEST_LENGTH]; 360 char *hash; 361 enum rtype type; 362 int is_ta = 0; 363 364 if (outformats & FORMAT_JSON) { 365 json_do_start(stdout); 366 } else { 367 if (num++ > 0) 368 printf("--\n"); 369 } 370 371 if (strncmp(file, RSYNC_PROTO, RSYNC_PROTO_LEN) == 0) { 372 file += RSYNC_PROTO_LEN; 373 buf = load_file(file, &len); 374 if (buf == NULL) { 375 warn("parse file %s", file); 376 return; 377 } 378 } 379 380 if (!EVP_Digest(buf, len, filehash, NULL, EVP_sha256(), NULL)) 381 errx(1, "EVP_Digest failed in %s", __func__); 382 383 if (base64_encode(filehash, sizeof(filehash), &hash) == -1) 384 errx(1, "base64_encode failed in %s", __func__); 385 386 if (outformats & FORMAT_JSON) { 387 json_do_string("file", file); 388 json_do_string("hash_id", hash); 389 } else { 390 printf("File: %s\n", file); 391 printf("Hash identifier: %s\n", hash); 392 } 393 394 free(hash); 395 396 type = rtype_from_file_extension(file); 397 398 switch (type) { 399 case RTYPE_ASPA: 400 aspa = aspa_parse(&x509, file, -1, buf, len); 401 if (aspa == NULL) 402 break; 403 aia = aspa->aia; 404 expires = &aspa->expires; 405 notafter = &aspa->notafter; 406 break; 407 case RTYPE_CER: 408 cert = cert_parse_pre(file, buf, len); 409 if (cert == NULL) 410 break; 411 is_ta = (cert->purpose == CERT_PURPOSE_TA); 412 if (!is_ta) 413 cert = cert_parse(file, cert); 414 if (cert == NULL) 415 break; 416 aia = cert->aia; 417 x509 = cert->x509; 418 if (X509_up_ref(x509) == 0) 419 errx(1, "%s: X509_up_ref failed", __func__); 420 expires = &cert->expires; 421 notafter = &cert->notafter; 422 break; 423 case RTYPE_CRL: 424 crl = crl_parse(file, buf, len); 425 if (crl == NULL) 426 break; 427 crl_print(crl); 428 break; 429 case RTYPE_MFT: 430 mft = mft_parse(&x509, file, -1, buf, len); 431 if (mft == NULL) 432 break; 433 aia = mft->aia; 434 expires = &mft->expires; 435 notafter = &mft->nextupdate; 436 break; 437 case RTYPE_GBR: 438 gbr = gbr_parse(&x509, file, -1, buf, len); 439 if (gbr == NULL) 440 break; 441 aia = gbr->aia; 442 expires = &gbr->expires; 443 notafter = &gbr->notafter; 444 break; 445 case RTYPE_GEOFEED: 446 geofeed = geofeed_parse(&x509, file, -1, buf, len); 447 if (geofeed == NULL) 448 break; 449 aia = geofeed->aia; 450 expires = &geofeed->expires; 451 notafter = &geofeed->notafter; 452 break; 453 case RTYPE_ROA: 454 roa = roa_parse(&x509, file, -1, buf, len); 455 if (roa == NULL) 456 break; 457 aia = roa->aia; 458 expires = &roa->expires; 459 notafter = &roa->notafter; 460 break; 461 case RTYPE_RSC: 462 rsc = rsc_parse(&x509, file, -1, buf, len); 463 if (rsc == NULL) 464 break; 465 aia = rsc->aia; 466 expires = &rsc->expires; 467 notafter = &rsc->notafter; 468 break; 469 case RTYPE_SPL: 470 spl = spl_parse(&x509, file, -1, buf, len); 471 if (spl == NULL) 472 break; 473 aia = spl->aia; 474 expires = &spl->expires; 475 notafter = &spl->notafter; 476 break; 477 case RTYPE_TAK: 478 tak = tak_parse(&x509, file, -1, buf, len); 479 if (tak == NULL) 480 break; 481 aia = tak->aia; 482 expires = &tak->expires; 483 notafter = &tak->notafter; 484 break; 485 case RTYPE_TAL: 486 tal = tal_parse(file, buf, len); 487 if (tal == NULL) 488 break; 489 tal_print(tal); 490 break; 491 default: 492 printf("%s: unsupported file type\n", file); 493 break; 494 } 495 496 if (aia != NULL) { 497 x509_get_crl(x509, file, &crl_uri); 498 parse_load_crl(crl_uri); 499 a = parse_load_certchain(aia); 500 c = crl_get(&crlt, a); 501 502 if ((status = valid_x509(file, ctx, x509, a, c, &errstr))) { 503 switch (type) { 504 case RTYPE_ASPA: 505 status = aspa->valid; 506 break; 507 case RTYPE_GEOFEED: 508 status = geofeed->valid; 509 break; 510 case RTYPE_ROA: 511 status = roa->valid; 512 break; 513 case RTYPE_RSC: 514 status = rsc->valid; 515 break; 516 case RTYPE_SPL: 517 status = spl->valid; 518 default: 519 break; 520 } 521 } 522 if (status && cert == NULL) { 523 struct cert *eecert; 524 525 eecert = cert_parse_ee_cert(file, a->cert->talid, x509); 526 if (eecert == NULL) 527 status = 0; 528 cert_free(eecert); 529 } else if (status) { 530 cert->talid = a->cert->talid; 531 constraints_validate(file, cert); 532 } 533 } else if (is_ta) { 534 expires = NULL; 535 notafter = NULL; 536 if ((tal = find_tal(cert)) != NULL) { 537 cert = ta_parse(file, cert, tal->pkey, tal->pkeysz); 538 status = (cert != NULL); 539 if (status) { 540 expires = &cert->expires; 541 notafter = &cert->notafter; 542 } 543 if (outformats & FORMAT_JSON) 544 json_do_string("tal", tal->descr); 545 else 546 printf("TAL: %s\n", 547 tal->descr); 548 tal = NULL; 549 } else { 550 cert_free(cert); 551 cert = NULL; 552 status = 0; 553 } 554 } 555 556 if (expires != NULL) { 557 if (status && aia != NULL) 558 *expires = x509_find_expires(*notafter, a, &crlt); 559 560 switch (type) { 561 case RTYPE_ASPA: 562 aspa_print(x509, aspa); 563 break; 564 case RTYPE_CER: 565 cert_print(cert); 566 break; 567 case RTYPE_GBR: 568 gbr_print(x509, gbr); 569 break; 570 case RTYPE_GEOFEED: 571 geofeed_print(x509, geofeed); 572 break; 573 case RTYPE_MFT: 574 mft_print(x509, mft); 575 break; 576 case RTYPE_ROA: 577 roa_print(x509, roa); 578 break; 579 case RTYPE_RSC: 580 rsc_print(x509, rsc); 581 break; 582 case RTYPE_SPL: 583 spl_print(x509, spl); 584 break; 585 case RTYPE_TAK: 586 tak_print(x509, tak); 587 break; 588 default: 589 break; 590 } 591 } 592 593 if (status) 594 valid = "OK"; 595 else if (aia == NULL) 596 valid = "N/A"; 597 else 598 valid = "Failed"; 599 600 if (outformats & FORMAT_JSON) { 601 json_do_string("validation", valid); 602 if (errstr != NULL) 603 json_do_string("error", errstr); 604 } else { 605 printf("Validation: %s", valid); 606 if (errstr != NULL) 607 printf(", %s", errstr); 608 } 609 610 if (outformats & FORMAT_JSON) 611 json_do_finish(); 612 else { 613 printf("\n"); 614 615 if (status && aia != NULL) { 616 print_signature_path(crl_uri, aia, a); 617 if (expires != NULL) 618 printf("Signature path expires: %s\n", 619 time2str(*expires)); 620 } 621 622 if (x509 == NULL) 623 goto out; 624 if (type == RTYPE_TAL || type == RTYPE_CRL) 625 goto out; 626 627 if (verbose) { 628 if (!X509_print_fp(stdout, x509)) 629 errx(1, "X509_print_fp"); 630 } 631 632 if (verbose > 1) { 633 if (!PEM_write_X509(stdout, x509)) 634 errx(1, "PEM_write_X509"); 635 } 636 } 637 638 out: 639 free(crl_uri); 640 X509_free(x509); 641 aspa_free(aspa); 642 cert_free(cert); 643 crl_free(crl); 644 gbr_free(gbr); 645 geofeed_free(geofeed); 646 mft_free(mft); 647 roa_free(roa); 648 rsc_free(rsc); 649 tak_free(tak); 650 tal_free(tal); 651} 652 653/* 654 * Process a file request, in general don't send anything back. 655 */ 656static void 657parse_file(struct entityq *q, struct msgbuf *msgq) 658{ 659 struct entity *entp; 660 struct ibuf *b; 661 struct tal *tal; 662 time_t dummy = 0; 663 664 while ((entp = TAILQ_FIRST(q)) != NULL) { 665 TAILQ_REMOVE(q, entp, entries); 666 667 switch (entp->type) { 668 case RTYPE_FILE: 669 proc_parser_file(entp->file, entp->data, entp->datasz); 670 break; 671 case RTYPE_TAL: 672 if ((tal = tal_parse(entp->file, entp->data, 673 entp->datasz)) == NULL) 674 errx(1, "%s: could not parse tal file", 675 entp->file); 676 tal->id = entp->talid; 677 talobj[tal->id] = tal; 678 parse_load_ta(tal); 679 break; 680 default: 681 errx(1, "unhandled entity type %d", entp->type); 682 } 683 684 b = io_new_buffer(); 685 io_simple_buffer(b, &entp->type, sizeof(entp->type)); 686 io_simple_buffer(b, &entp->repoid, sizeof(entp->repoid)); 687 io_simple_buffer(b, &entp->talid, sizeof(entp->talid)); 688 io_str_buffer(b, entp->file); 689 io_simple_buffer(b, &dummy, sizeof(dummy)); 690 io_close_buffer(msgq, b); 691 entity_free(entp); 692 } 693} 694 695/* 696 * Process responsible for parsing and validating content. 697 * All this process does is wait to be told about a file to parse, then 698 * it parses it and makes sure that the data being returned is fully 699 * validated and verified. 700 * The process will exit cleanly only when fd is closed. 701 */ 702void 703proc_filemode(int fd) 704{ 705 struct entityq q; 706 struct msgbuf msgq; 707 struct pollfd pfd; 708 struct entity *entp; 709 struct ibuf *b, *inbuf = NULL; 710 711 /* Only allow access to the cache directory. */ 712 if (unveil(".", "r") == -1) 713 err(1, "unveil cachedir"); 714 if (pledge("stdio rpath", NULL) == -1) 715 err(1, "pledge"); 716 717 ERR_load_crypto_strings(); 718 OpenSSL_add_all_ciphers(); 719 OpenSSL_add_all_digests(); 720 x509_init_oid(); 721 constraints_parse(); 722 723 if ((ctx = X509_STORE_CTX_new()) == NULL) 724 err(1, "X509_STORE_CTX_new"); 725 TAILQ_INIT(&q); 726 727 msgbuf_init(&msgq); 728 msgq.fd = fd; 729 730 pfd.fd = fd; 731 732 for (;;) { 733 pfd.events = POLLIN; 734 if (msgq.queued) 735 pfd.events |= POLLOUT; 736 737 if (poll(&pfd, 1, INFTIM) == -1) { 738 if (errno == EINTR) 739 continue; 740 err(1, "poll"); 741 } 742 if ((pfd.revents & (POLLERR|POLLNVAL))) 743 errx(1, "poll: bad descriptor"); 744 745 /* If the parent closes, return immediately. */ 746 747 if ((pfd.revents & POLLHUP)) 748 break; 749 750 if ((pfd.revents & POLLIN)) { 751 b = io_buf_read(fd, &inbuf); 752 if (b != NULL) { 753 entp = calloc(1, sizeof(struct entity)); 754 if (entp == NULL) 755 err(1, NULL); 756 entity_read_req(b, entp); 757 TAILQ_INSERT_TAIL(&q, entp, entries); 758 ibuf_free(b); 759 } 760 } 761 762 if (pfd.revents & POLLOUT) { 763 switch (msgbuf_write(&msgq)) { 764 case 0: 765 errx(1, "write: connection closed"); 766 case -1: 767 err(1, "write"); 768 } 769 } 770 771 parse_file(&q, &msgq); 772 } 773 774 msgbuf_clear(&msgq); 775 while ((entp = TAILQ_FIRST(&q)) != NULL) { 776 TAILQ_REMOVE(&q, entp, entries); 777 entity_free(entp); 778 } 779 780 auth_tree_free(&auths); 781 crl_tree_free(&crlt); 782 783 X509_STORE_CTX_free(ctx); 784 ibuf_free(inbuf); 785 786 exit(0); 787} 788