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