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