1 /* $OpenBSD: filemode.c,v 1.33 2023/05/30 16:02:28 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, 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, 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, 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, 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, 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, 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, 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 } else if (is_ta) { 472 if ((tal = find_tal(cert)) != NULL) { 473 cert = ta_parse(file, cert, tal->pkey, tal->pkeysz); 474 status = (cert != NULL); 475 if (outformats & FORMAT_JSON) 476 json_do_string("tal", tal->descr); 477 else 478 printf("TAL: %s\n", 479 tal->descr); 480 tal = NULL; 481 } else { 482 cert_free(cert); 483 cert = NULL; 484 expires = NULL; 485 status = 0; 486 } 487 } 488 489 if (expires != NULL) { 490 if (status && aia != NULL) 491 *expires = x509_find_expires(*notafter, a, &crlt); 492 493 switch (type) { 494 case RTYPE_ASPA: 495 aspa_print(x509, aspa); 496 break; 497 case RTYPE_CER: 498 cert_print(cert); 499 break; 500 case RTYPE_GBR: 501 gbr_print(x509, gbr); 502 break; 503 case RTYPE_GEOFEED: 504 geofeed_print(x509, geofeed); 505 break; 506 case RTYPE_MFT: 507 mft_print(x509, mft); 508 break; 509 case RTYPE_ROA: 510 roa_print(x509, roa); 511 break; 512 case RTYPE_RSC: 513 rsc_print(x509, rsc); 514 break; 515 case RTYPE_TAK: 516 tak_print(x509, tak); 517 break; 518 default: 519 break; 520 } 521 } 522 523 if (status) 524 valid = "OK"; 525 else if (aia == NULL) 526 valid = "N/A"; 527 else 528 valid = "Failed"; 529 530 if (outformats & FORMAT_JSON) { 531 json_do_string("validation", valid); 532 if (errstr != NULL) 533 json_do_string("error", errstr); 534 } else { 535 printf("Validation: %s", valid); 536 if (errstr != NULL) 537 printf(", %s", errstr); 538 } 539 540 if (outformats & FORMAT_JSON) 541 json_do_finish(); 542 else { 543 printf("\n"); 544 545 if (status && aia != NULL) { 546 print_signature_path(crl_uri, aia, a); 547 if (expires != NULL) 548 printf("Signature path expires: %s\n", 549 time2str(*expires)); 550 } 551 552 if (x509 == NULL) 553 goto out; 554 if (type == RTYPE_TAL || type == RTYPE_CRL) 555 goto out; 556 557 if (verbose) { 558 if (!X509_print_fp(stdout, x509)) 559 errx(1, "X509_print_fp"); 560 } 561 562 if (verbose > 1) { 563 if (!PEM_write_X509(stdout, x509)) 564 errx(1, "PEM_write_X509"); 565 } 566 } 567 568 out: 569 free(crl_uri); 570 X509_free(x509); 571 aspa_free(aspa); 572 cert_free(cert); 573 crl_free(crl); 574 gbr_free(gbr); 575 geofeed_free(geofeed); 576 mft_free(mft); 577 roa_free(roa); 578 rsc_free(rsc); 579 tak_free(tak); 580 tal_free(tal); 581 } 582 583 /* 584 * Process a file request, in general don't send anything back. 585 */ 586 static void 587 parse_file(struct entityq *q, struct msgbuf *msgq) 588 { 589 struct entity *entp; 590 struct ibuf *b; 591 struct tal *tal; 592 time_t dummy = 0; 593 594 while ((entp = TAILQ_FIRST(q)) != NULL) { 595 TAILQ_REMOVE(q, entp, entries); 596 597 switch (entp->type) { 598 case RTYPE_FILE: 599 proc_parser_file(entp->file, entp->data, entp->datasz); 600 break; 601 case RTYPE_TAL: 602 if ((tal = tal_parse(entp->file, entp->data, 603 entp->datasz)) == NULL) 604 errx(1, "%s: could not parse tal file", 605 entp->file); 606 tal->id = entp->talid; 607 talobj[tal->id] = tal; 608 parse_load_ta(tal); 609 break; 610 default: 611 errx(1, "unhandled entity type %d", entp->type); 612 } 613 614 b = io_new_buffer(); 615 io_simple_buffer(b, &entp->type, sizeof(entp->type)); 616 io_simple_buffer(b, &entp->repoid, sizeof(entp->repoid)); 617 io_simple_buffer(b, &entp->talid, sizeof(entp->talid)); 618 io_str_buffer(b, entp->file); 619 io_simple_buffer(b, &dummy, sizeof(dummy)); 620 io_close_buffer(msgq, b); 621 entity_free(entp); 622 } 623 } 624 625 /* 626 * Process responsible for parsing and validating content. 627 * All this process does is wait to be told about a file to parse, then 628 * it parses it and makes sure that the data being returned is fully 629 * validated and verified. 630 * The process will exit cleanly only when fd is closed. 631 */ 632 void 633 proc_filemode(int fd) 634 { 635 struct entityq q; 636 struct msgbuf msgq; 637 struct pollfd pfd; 638 struct entity *entp; 639 struct ibuf *b, *inbuf = NULL; 640 641 /* Only allow access to the cache directory. */ 642 if (unveil(".", "r") == -1) 643 err(1, "unveil cachedir"); 644 if (pledge("stdio rpath", NULL) == -1) 645 err(1, "pledge"); 646 647 ERR_load_crypto_strings(); 648 OpenSSL_add_all_ciphers(); 649 OpenSSL_add_all_digests(); 650 x509_init_oid(); 651 652 if ((ctx = X509_STORE_CTX_new()) == NULL) 653 cryptoerrx("X509_STORE_CTX_new"); 654 TAILQ_INIT(&q); 655 656 msgbuf_init(&msgq); 657 msgq.fd = fd; 658 659 pfd.fd = fd; 660 661 for (;;) { 662 pfd.events = POLLIN; 663 if (msgq.queued) 664 pfd.events |= POLLOUT; 665 666 if (poll(&pfd, 1, INFTIM) == -1) { 667 if (errno == EINTR) 668 continue; 669 err(1, "poll"); 670 } 671 if ((pfd.revents & (POLLERR|POLLNVAL))) 672 errx(1, "poll: bad descriptor"); 673 674 /* If the parent closes, return immediately. */ 675 676 if ((pfd.revents & POLLHUP)) 677 break; 678 679 if ((pfd.revents & POLLIN)) { 680 b = io_buf_read(fd, &inbuf); 681 if (b != NULL) { 682 entp = calloc(1, sizeof(struct entity)); 683 if (entp == NULL) 684 err(1, NULL); 685 entity_read_req(b, entp); 686 TAILQ_INSERT_TAIL(&q, entp, entries); 687 ibuf_free(b); 688 } 689 } 690 691 if (pfd.revents & POLLOUT) { 692 switch (msgbuf_write(&msgq)) { 693 case 0: 694 errx(1, "write: connection closed"); 695 case -1: 696 err(1, "write"); 697 } 698 } 699 700 parse_file(&q, &msgq); 701 } 702 703 msgbuf_clear(&msgq); 704 while ((entp = TAILQ_FIRST(&q)) != NULL) { 705 TAILQ_REMOVE(&q, entp, entries); 706 entity_free(entp); 707 } 708 709 auth_tree_free(&auths); 710 crl_tree_free(&crlt); 711 712 X509_STORE_CTX_free(ctx); 713 ibuf_free(inbuf); 714 715 exit(0); 716 } 717