main.c revision 1.123
1/* $OpenBSD: main.c,v 1.123 2021/03/25 12:18:45 claudio Exp $ */ 2/* 3 * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18#include <sys/queue.h> 19#include <sys/socket.h> 20#include <sys/resource.h> 21#include <sys/stat.h> 22#include <sys/tree.h> 23#include <sys/types.h> 24#include <sys/wait.h> 25 26#include <assert.h> 27#include <err.h> 28#include <errno.h> 29#include <dirent.h> 30#include <fcntl.h> 31#include <fnmatch.h> 32#include <fts.h> 33#include <poll.h> 34#include <pwd.h> 35#include <stdio.h> 36#include <stdlib.h> 37#include <signal.h> 38#include <string.h> 39#include <limits.h> 40#include <syslog.h> 41#include <unistd.h> 42#include <imsg.h> 43 44#include "extern.h" 45 46/* 47 * Maximum number of TAL files we'll load. 48 */ 49#define TALSZ_MAX 8 50 51/* 52 * An rsync repository. 53 */ 54#define REPO_MAX_URI 2 55struct repo { 56 SLIST_ENTRY(repo) entry; 57 char *repouri; /* CA repository base URI */ 58 char *local; /* local path name */ 59 char *temp; /* temporary file / dir */ 60 char *uris[REPO_MAX_URI]; /* URIs to fetch from */ 61 struct entityq queue; /* files waiting for this repo */ 62 size_t id; /* identifier (array index) */ 63 int uriidx; /* which URI is fetched */ 64 int loaded; /* whether loaded or not */ 65}; 66 67size_t entity_queue; 68int timeout = 60*60; 69volatile sig_atomic_t killme; 70void suicide(int sig); 71 72/* 73 * Table of all known repositories. 74 */ 75SLIST_HEAD(, repo) repos = SLIST_HEAD_INITIALIZER(repos); 76size_t repoid; 77 78/* 79 * Database of all file path accessed during a run. 80 */ 81struct filepath { 82 RB_ENTRY(filepath) entry; 83 char *file; 84}; 85 86static inline int 87filepathcmp(struct filepath *a, struct filepath *b) 88{ 89 return strcasecmp(a->file, b->file); 90} 91 92RB_HEAD(filepath_tree, filepath); 93RB_PROTOTYPE(filepath_tree, filepath, entry, filepathcmp); 94 95static struct filepath_tree fpt = RB_INITIALIZER(&fpt); 96static struct msgbuf procq, rsyncq, httpq; 97static int cachefd, outdirfd; 98 99const char *bird_tablename = "ROAS"; 100 101int verbose; 102int noop; 103 104struct stats stats; 105 106static void repo_fetch(struct repo *); 107static char *ta_filename(const struct repo *, int); 108 109/* 110 * Log a message to stderr if and only if "verbose" is non-zero. 111 * This uses the err(3) functionality. 112 */ 113void 114logx(const char *fmt, ...) 115{ 116 va_list ap; 117 118 if (verbose && fmt != NULL) { 119 va_start(ap, fmt); 120 vwarnx(fmt, ap); 121 va_end(ap); 122 } 123} 124 125/* 126 * Functions to lookup which files have been accessed during computation. 127 */ 128static int 129filepath_add(char *file) 130{ 131 struct filepath *fp; 132 133 if ((fp = malloc(sizeof(*fp))) == NULL) 134 err(1, NULL); 135 if ((fp->file = strdup(file)) == NULL) 136 err(1, NULL); 137 138 if (RB_INSERT(filepath_tree, &fpt, fp) != NULL) { 139 /* already in the tree */ 140 free(fp->file); 141 free(fp); 142 return 0; 143 } 144 145 return 1; 146} 147 148static int 149filepath_exists(char *file) 150{ 151 struct filepath needle; 152 153 needle.file = file; 154 return RB_FIND(filepath_tree, &fpt, &needle) != NULL; 155} 156 157RB_GENERATE(filepath_tree, filepath, entry, filepathcmp); 158 159void 160entity_free(struct entity *ent) 161{ 162 163 if (ent == NULL) 164 return; 165 166 free(ent->pkey); 167 free(ent->file); 168 free(ent->descr); 169 free(ent); 170} 171 172/* 173 * Read a queue entity from the descriptor. 174 * Matched by entity_buffer_req(). 175 * The pointer must be passed entity_free(). 176 */ 177void 178entity_read_req(int fd, struct entity *ent) 179{ 180 181 io_simple_read(fd, &ent->type, sizeof(enum rtype)); 182 io_str_read(fd, &ent->file); 183 io_simple_read(fd, &ent->has_pkey, sizeof(int)); 184 if (ent->has_pkey) 185 io_buf_read_alloc(fd, (void **)&ent->pkey, &ent->pkeysz); 186 io_str_read(fd, &ent->descr); 187} 188 189/* 190 * Write the queue entity. 191 * Matched by entity_read_req(). 192 */ 193static void 194entity_write_req(const struct entity *ent) 195{ 196 struct ibuf *b; 197 198 if ((b = ibuf_dynamic(sizeof(*ent), UINT_MAX)) == NULL) 199 err(1, NULL); 200 io_simple_buffer(b, &ent->type, sizeof(ent->type)); 201 io_str_buffer(b, ent->file); 202 io_simple_buffer(b, &ent->has_pkey, sizeof(int)); 203 if (ent->has_pkey) 204 io_buf_buffer(b, ent->pkey, ent->pkeysz); 205 io_str_buffer(b, ent->descr); 206 ibuf_close(&procq, b); 207} 208 209/* 210 * Scan through all queued requests and see which ones are in the given 211 * repo, then flush those into the parser process. 212 */ 213static void 214entityq_flush(struct repo *repo) 215{ 216 struct entity *p, *np; 217 218 TAILQ_FOREACH_SAFE(p, &repo->queue, entries, np) { 219 entity_write_req(p); 220 TAILQ_REMOVE(&repo->queue, p, entries); 221 entity_free(p); 222 } 223} 224 225/* 226 * Add the heap-allocated file to the queue for processing. 227 */ 228static void 229entityq_add(char *file, enum rtype type, struct repo *rp, 230 const unsigned char *pkey, size_t pkeysz, char *descr) 231{ 232 struct entity *p; 233 234 if (filepath_add(file) == 0) { 235 warnx("%s: File already visited", file); 236 return; 237 } 238 239 if ((p = calloc(1, sizeof(struct entity))) == NULL) 240 err(1, NULL); 241 242 p->type = type; 243 p->file = file; 244 p->has_pkey = pkey != NULL; 245 if (p->has_pkey) { 246 p->pkeysz = pkeysz; 247 if ((p->pkey = malloc(pkeysz)) == NULL) 248 err(1, NULL); 249 memcpy(p->pkey, pkey, pkeysz); 250 } 251 if (descr != NULL) 252 if ((p->descr = strdup(descr)) == NULL) 253 err(1, NULL); 254 255 entity_queue++; 256 257 /* 258 * Write to the queue if there's no repo or the repo has already 259 * been loaded else enqueue it for later. 260 */ 261 262 if (rp == NULL || rp->loaded) { 263 entity_write_req(p); 264 entity_free(p); 265 } else 266 TAILQ_INSERT_TAIL(&rp->queue, p, entries); 267} 268 269/* 270 * Allocate and insert a new repository. 271 */ 272static struct repo * 273repo_alloc(void) 274{ 275 struct repo *rp; 276 277 if ((rp = calloc(1, sizeof(*rp))) == NULL) 278 err(1, NULL); 279 280 rp->id = ++repoid; 281 TAILQ_INIT(&rp->queue); 282 SLIST_INSERT_HEAD(&repos, rp, entry); 283 284 return rp; 285} 286 287static struct repo * 288repo_find(size_t id) 289{ 290 struct repo *rp; 291 292 SLIST_FOREACH(rp, &repos, entry) 293 if (id == rp->id) 294 break; 295 return rp; 296} 297 298static void 299http_ta_fetch(struct repo *rp) 300{ 301 struct ibuf *b; 302 int filefd; 303 304 rp->temp = ta_filename(rp, 1); 305 306 filefd = mkostemp(rp->temp, O_CLOEXEC); 307 if (filefd == -1) 308 err(1, "mkostemp: %s", rp->temp); 309 if (fchmod(filefd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) 310 warn("fchmod: %s", rp->temp); 311 312 if ((b = ibuf_dynamic(256, UINT_MAX)) == NULL) 313 err(1, NULL); 314 io_simple_buffer(b, &rp->id, sizeof(rp->id)); 315 io_str_buffer(b, rp->uris[rp->uriidx]); 316 /* TODO last modified time */ 317 io_str_buffer(b, NULL); 318 /* pass file as fd */ 319 b->fd = filefd; 320 ibuf_close(&httpq, b); 321} 322 323static int 324http_done(struct repo *rp, enum http_result res) 325{ 326 if (rp->repouri == NULL) { 327 /* Move downloaded TA file into place, or unlink on failure. */ 328 if (res == HTTP_OK) { 329 char *file; 330 331 file = ta_filename(rp, 0); 332 if (renameat(cachefd, rp->temp, cachefd, file) == -1) 333 warn("rename to %s", file); 334 } else { 335 if (unlinkat(cachefd, rp->temp, 0) == -1) 336 warn("unlink %s", rp->temp); 337 } 338 free(rp->temp); 339 rp->temp = NULL; 340 } 341 342 if (res == HTTP_OK) 343 logx("%s: loaded from network", rp->local); 344 else if (rp->uriidx < REPO_MAX_URI - 1 && 345 rp->uris[rp->uriidx + 1] != NULL) { 346 logx("%s: load from network failed, retry", rp->local); 347 348 rp->uriidx++; 349 repo_fetch(rp); 350 return 0; 351 } else 352 logx("%s: load from network failed, " 353 "fallback to cache", rp->local); 354 355 return 1; 356} 357 358static void 359repo_fetch(struct repo *rp) 360{ 361 struct ibuf *b; 362 363 if (noop) { 364 rp->loaded = 1; 365 logx("%s: using cache", rp->local); 366 stats.repos++; 367 /* there is nothing in the queue so no need to flush */ 368 return; 369 } 370 371 /* 372 * Create destination location. 373 * Build up the tree to this point. 374 */ 375 376 if (mkpath(rp->local) == -1) 377 err(1, "%s", rp->local); 378 379 logx("%s: pulling from %s", rp->local, rp->uris[rp->uriidx]); 380 381 if (strncasecmp(rp->uris[rp->uriidx], "rsync://", 8) == 0) { 382 if ((b = ibuf_dynamic(256, UINT_MAX)) == NULL) 383 err(1, NULL); 384 io_simple_buffer(b, &rp->id, sizeof(rp->id)); 385 io_str_buffer(b, rp->local); 386 io_str_buffer(b, rp->uris[rp->uriidx]); 387 ibuf_close(&rsyncq, b); 388 } else { 389 /* 390 * Two cases for https. TA files load directly while 391 * for RRDP XML files are downloaded and parsed to build 392 * the repo. TA repos have a NULL repouri. 393 */ 394 if (rp->repouri == NULL) { 395 http_ta_fetch(rp); 396 } 397 } 398} 399 400/* 401 * Look up a trust anchor, queueing it for download if not found. 402 */ 403static struct repo * 404ta_lookup(const struct tal *tal) 405{ 406 struct repo *rp; 407 char *local; 408 size_t i, j; 409 410 if (asprintf(&local, "ta/%s", tal->descr) == -1) 411 err(1, NULL); 412 413 /* Look up in repository table. (Lookup should actually fail here) */ 414 SLIST_FOREACH(rp, &repos, entry) { 415 if (strcmp(rp->local, local) != 0) 416 continue; 417 free(local); 418 return rp; 419 } 420 421 rp = repo_alloc(); 422 rp->local = local; 423 for (i = 0, j = 0; i < tal->urisz && j < 2; i++) { 424 if ((rp->uris[j++] = strdup(tal->uri[i])) == NULL) 425 err(1, NULL); 426 } 427 if (j == 0) 428 errx(1, "TAL %s has no URI", tal->descr); 429 430 repo_fetch(rp); 431 return rp; 432} 433 434/* 435 * Look up a repository, queueing it for discovery if not found. 436 */ 437static struct repo * 438repo_lookup(const char *uri) 439{ 440 char *local, *repo; 441 struct repo *rp; 442 443 if ((repo = rsync_base_uri(uri)) == NULL) 444 return NULL; 445 446 /* Look up in repository table. */ 447 SLIST_FOREACH(rp, &repos, entry) { 448 if (rp->repouri == NULL || 449 strcmp(rp->repouri, repo) != 0) 450 continue; 451 free(repo); 452 return rp; 453 } 454 455 rp = repo_alloc(); 456 rp->repouri = repo; 457 local = strchr(repo, ':') + strlen("://"); 458 if (asprintf(&rp->local, "rsync/%s", local) == -1) 459 err(1, NULL); 460 if ((rp->uris[0] = strdup(repo)) == NULL) 461 err(1, NULL); 462 463 repo_fetch(rp); 464 return rp; 465} 466 467static char * 468ta_filename(const struct repo *repo, int temp) 469{ 470 const char *file; 471 char *nfile; 472 473 /* does not matter which URI, all end with same filename */ 474 file = strrchr(repo->uris[0], '/'); 475 assert(file); 476 477 if (asprintf(&nfile, "%s%s%s", repo->local, file, 478 temp ? ".XXXXXXXX": "") == -1) 479 err(1, NULL); 480 481 return nfile; 482} 483 484/* 485 * Build local file name base on the URI and the repo info. 486 */ 487static char * 488repo_filename(const struct repo *repo, const char *uri) 489{ 490 char *nfile; 491 492 if (strstr(uri, repo->repouri) != uri) 493 errx(1, "%s: URI outside of repository", uri); 494 uri += strlen(repo->repouri) + 1; /* skip base and '/' */ 495 496 if (asprintf(&nfile, "%s/%s", repo->local, uri) == -1) 497 err(1, NULL); 498 return nfile; 499} 500 501/* 502 * Add a file (CER, ROA, CRL) from an MFT file, RFC 6486. 503 * These are always relative to the directory in which "mft" sits. 504 */ 505static void 506queue_add_from_mft(const char *mft, const struct mftfile *file, enum rtype type) 507{ 508 char *cp, *nfile; 509 510 /* Construct local path from filename. */ 511 cp = strrchr(mft, '/'); 512 assert(cp != NULL); 513 assert(cp - mft < INT_MAX); 514 if (asprintf(&nfile, "%.*s/%s", (int)(cp - mft), mft, file->file) == -1) 515 err(1, NULL); 516 517 /* 518 * Since we're from the same directory as the MFT file, we know 519 * that the repository has already been loaded. 520 */ 521 522 entityq_add(nfile, type, NULL, NULL, 0, NULL); 523} 524 525/* 526 * Loops over queue_add_from_mft() for all files. 527 * The order here is important: we want to parse the revocation 528 * list *before* we parse anything else. 529 * FIXME: set the type of file in the mftfile so that we don't need to 530 * keep doing the check (this should be done in the parser, where we 531 * check the suffix anyway). 532 */ 533static void 534queue_add_from_mft_set(const struct mft *mft) 535{ 536 size_t i, sz; 537 const struct mftfile *f; 538 539 for (i = 0; i < mft->filesz; i++) { 540 f = &mft->files[i]; 541 sz = strlen(f->file); 542 assert(sz > 4); 543 if (strcasecmp(f->file + sz - 4, ".crl") != 0) 544 continue; 545 queue_add_from_mft(mft->file, f, RTYPE_CRL); 546 } 547 548 for (i = 0; i < mft->filesz; i++) { 549 f = &mft->files[i]; 550 sz = strlen(f->file); 551 assert(sz > 4); 552 if (strcasecmp(f->file + sz - 4, ".crl") == 0) 553 continue; 554 else if (strcasecmp(f->file + sz - 4, ".cer") == 0) 555 queue_add_from_mft(mft->file, f, RTYPE_CER); 556 else if (strcasecmp(f->file + sz - 4, ".roa") == 0) 557 queue_add_from_mft(mft->file, f, RTYPE_ROA); 558 else if (strcasecmp(f->file + sz - 4, ".gbr") == 0) 559 queue_add_from_mft(mft->file, f, RTYPE_GBR); 560 else 561 logx("%s: unsupported file type: %s", mft->file, 562 f->file); 563 } 564} 565 566/* 567 * Add a local TAL file (RFC 7730) to the queue of files to fetch. 568 */ 569static void 570queue_add_tal(const char *file) 571{ 572 char *nfile, *buf; 573 574 if ((nfile = strdup(file)) == NULL) 575 err(1, NULL); 576 buf = tal_read_file(file); 577 578 /* Record tal for later reporting */ 579 if (stats.talnames == NULL) { 580 if ((stats.talnames = strdup(file)) == NULL) 581 err(1, NULL); 582 } else { 583 char *tmp; 584 if (asprintf(&tmp, "%s %s", stats.talnames, file) == -1) 585 err(1, NULL); 586 free(stats.talnames); 587 stats.talnames = tmp; 588 } 589 590 /* Not in a repository, so directly add to queue. */ 591 entityq_add(nfile, RTYPE_TAL, NULL, NULL, 0, buf); 592 /* entityq_add makes a copy of buf */ 593 free(buf); 594} 595 596/* 597 * Add URIs (CER) from a TAL file, RFC 8630. 598 */ 599static void 600queue_add_from_tal(const struct tal *tal) 601{ 602 char *nfile; 603 struct repo *repo; 604 605 assert(tal->urisz); 606 607 /* Look up the repository. */ 608 repo = ta_lookup(tal); 609 610 nfile = ta_filename(repo, 0); 611 entityq_add(nfile, RTYPE_CER, repo, tal->pkey, 612 tal->pkeysz, tal->descr); 613} 614 615/* 616 * Add a manifest (MFT) found in an X509 certificate, RFC 6487. 617 */ 618static void 619queue_add_from_cert(const struct cert *cert) 620{ 621 struct repo *repo; 622 char *nfile; 623 624 repo = repo_lookup(cert->mft); 625 if (repo == NULL) /* bad repository URI */ 626 return; 627 628 nfile = repo_filename(repo, cert->mft); 629 630 entityq_add(nfile, RTYPE_MFT, repo, NULL, 0, NULL); 631} 632 633/* 634 * Process parsed content. 635 * For non-ROAs, we grok for more data. 636 * For ROAs, we want to extract the valid info. 637 * In all cases, we gather statistics. 638 */ 639static void 640entity_process(int proc, struct stats *st, struct vrp_tree *tree) 641{ 642 enum rtype type; 643 struct tal *tal; 644 struct cert *cert; 645 struct mft *mft; 646 struct roa *roa; 647 int c; 648 649 /* 650 * For most of these, we first read whether there's any content 651 * at all---this means that the syntactic parse failed (X509 652 * certificate, for example). 653 * We follow that up with whether the resources didn't parse. 654 */ 655 io_simple_read(proc, &type, sizeof(type)); 656 657 switch (type) { 658 case RTYPE_TAL: 659 st->tals++; 660 tal = tal_read(proc); 661 queue_add_from_tal(tal); 662 tal_free(tal); 663 break; 664 case RTYPE_CER: 665 st->certs++; 666 io_simple_read(proc, &c, sizeof(int)); 667 if (c == 0) { 668 st->certs_fail++; 669 break; 670 } 671 cert = cert_read(proc); 672 if (cert->valid) { 673 /* 674 * Process the revocation list from the 675 * certificate *first*, since it might mark that 676 * we're revoked and then we don't want to 677 * process the MFT. 678 */ 679 queue_add_from_cert(cert); 680 } else 681 st->certs_invalid++; 682 cert_free(cert); 683 break; 684 case RTYPE_MFT: 685 st->mfts++; 686 io_simple_read(proc, &c, sizeof(int)); 687 if (c == 0) { 688 st->mfts_fail++; 689 break; 690 } 691 mft = mft_read(proc); 692 if (mft->stale) 693 st->mfts_stale++; 694 queue_add_from_mft_set(mft); 695 mft_free(mft); 696 break; 697 case RTYPE_CRL: 698 st->crls++; 699 break; 700 case RTYPE_ROA: 701 st->roas++; 702 io_simple_read(proc, &c, sizeof(int)); 703 if (c == 0) { 704 st->roas_fail++; 705 break; 706 } 707 roa = roa_read(proc); 708 if (roa->valid) 709 roa_insert_vrps(tree, roa, &st->vrps, &st->uniqs); 710 else 711 st->roas_invalid++; 712 roa_free(roa); 713 break; 714 case RTYPE_GBR: 715 st->gbrs++; 716 break; 717 default: 718 abort(); 719 } 720 721 entity_queue--; 722} 723 724/* 725 * Assign filenames ending in ".tal" in "/etc/rpki" into "tals", 726 * returning the number of files found and filled-in. 727 * This may be zero. 728 * Don't exceded "max" filenames. 729 */ 730static size_t 731tal_load_default(const char *tals[], size_t max) 732{ 733 static const char *confdir = "/etc/rpki"; 734 size_t s = 0; 735 char *path; 736 DIR *dirp; 737 struct dirent *dp; 738 739 dirp = opendir(confdir); 740 if (dirp == NULL) 741 err(1, "open %s", confdir); 742 while ((dp = readdir(dirp)) != NULL) { 743 if (fnmatch("*.tal", dp->d_name, FNM_PERIOD) == FNM_NOMATCH) 744 continue; 745 if (s >= max) 746 err(1, "too many tal files found in %s", 747 confdir); 748 if (asprintf(&path, "%s/%s", confdir, dp->d_name) == -1) 749 err(1, NULL); 750 tals[s++] = path; 751 } 752 closedir (dirp); 753 return (s); 754} 755 756static char ** 757add_to_del(char **del, size_t *dsz, char *file) 758{ 759 size_t i = *dsz; 760 761 del = reallocarray(del, i + 1, sizeof(*del)); 762 if (del == NULL) 763 err(1, NULL); 764 if ((del[i] = strdup(file)) == NULL) 765 err(1, NULL); 766 *dsz = i + 1; 767 return del; 768} 769 770static size_t 771repo_cleanup(void) 772{ 773 size_t i, delsz = 0; 774 char *argv[2], **del = NULL; 775 struct repo *rp; 776 FTS *fts; 777 FTSENT *e; 778 779 SLIST_FOREACH(rp, &repos, entry) { 780 argv[0] = rp->local; 781 argv[1] = NULL; 782 if ((fts = fts_open(argv, FTS_PHYSICAL | FTS_NOSTAT, 783 NULL)) == NULL) 784 err(1, "fts_open"); 785 errno = 0; 786 while ((e = fts_read(fts)) != NULL) { 787 switch (e->fts_info) { 788 case FTS_NSOK: 789 if (!filepath_exists(e->fts_path)) 790 del = add_to_del(del, &delsz, 791 e->fts_path); 792 break; 793 case FTS_D: 794 case FTS_DP: 795 /* TODO empty directory pruning */ 796 break; 797 case FTS_SL: 798 case FTS_SLNONE: 799 warnx("symlink %s", e->fts_path); 800 del = add_to_del(del, &delsz, e->fts_path); 801 break; 802 case FTS_NS: 803 case FTS_ERR: 804 warnx("fts_read %s: %s", e->fts_path, 805 strerror(e->fts_errno)); 806 break; 807 default: 808 warnx("unhandled[%x] %s", e->fts_info, 809 e->fts_path); 810 break; 811 } 812 813 errno = 0; 814 } 815 if (errno) 816 err(1, "fts_read"); 817 if (fts_close(fts) == -1) 818 err(1, "fts_close"); 819 } 820 821 for (i = 0; i < delsz; i++) { 822 if (unlink(del[i]) == -1) 823 warn("unlink %s", del[i]); 824 if (verbose > 1) 825 logx("deleted %s", del[i]); 826 free(del[i]); 827 } 828 free(del); 829 830 return delsz; 831} 832 833void 834suicide(int sig __attribute__((unused))) 835{ 836 killme = 1; 837 838} 839 840int 841main(int argc, char *argv[]) 842{ 843 int rc = 1, c, st, proc, rsync, http, ok, 844 fl = SOCK_STREAM | SOCK_CLOEXEC; 845 size_t i, id, outsz = 0, talsz = 0; 846 pid_t procpid, rsyncpid, httppid; 847 int fd[2]; 848 struct pollfd pfd[3]; 849 struct roa **out = NULL; 850 struct repo *rp; 851 char *rsync_prog = "openrsync"; 852 char *bind_addr = NULL; 853 const char *cachedir = NULL, *outputdir = NULL; 854 const char *tals[TALSZ_MAX], *errs; 855 struct vrp_tree v = RB_INITIALIZER(&v); 856 struct rusage ru; 857 struct timeval start_time, now_time; 858 859 gettimeofday(&start_time, NULL); 860 861 /* If started as root, priv-drop to _rpki-client */ 862 if (getuid() == 0) { 863 struct passwd *pw; 864 865 pw = getpwnam("_rpki-client"); 866 if (!pw) 867 errx(1, "no _rpki-client user to revoke to"); 868 if (setgroups(1, &pw->pw_gid) == -1 || 869 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 || 870 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) 871 err(1, "unable to revoke privs"); 872 873 } 874 cachedir = RPKI_PATH_BASE_DIR; 875 outputdir = RPKI_PATH_OUT_DIR; 876 877 if (pledge("stdio rpath wpath cpath inet fattr dns sendfd recvfd " 878 "proc exec unveil", NULL) == -1) 879 err(1, "pledge"); 880 881 while ((c = getopt(argc, argv, "b:Bcd:e:jnos:t:T:vV")) != -1) 882 switch (c) { 883 case 'b': 884 bind_addr = optarg; 885 break; 886 case 'B': 887 outformats |= FORMAT_BIRD; 888 break; 889 case 'c': 890 outformats |= FORMAT_CSV; 891 break; 892 case 'd': 893 cachedir = optarg; 894 break; 895 case 'e': 896 rsync_prog = optarg; 897 break; 898 case 'j': 899 outformats |= FORMAT_JSON; 900 break; 901 case 'n': 902 noop = 1; 903 break; 904 case 'o': 905 outformats |= FORMAT_OPENBGPD; 906 break; 907 case 's': 908 timeout = strtonum(optarg, 0, 24*60*60, &errs); 909 if (errs) 910 errx(1, "-s: %s", errs); 911 break; 912 case 't': 913 if (talsz >= TALSZ_MAX) 914 err(1, 915 "too many tal files specified"); 916 tals[talsz++] = optarg; 917 break; 918 case 'T': 919 bird_tablename = optarg; 920 break; 921 case 'v': 922 verbose++; 923 break; 924 case 'V': 925 errx(0, "version: %s", RPKI_VERSION); 926 default: 927 goto usage; 928 } 929 930 argv += optind; 931 argc -= optind; 932 if (argc == 1) 933 outputdir = argv[0]; 934 else if (argc > 1) 935 goto usage; 936 937 if (timeout) { 938 signal(SIGALRM, suicide); 939 /* Commit suicide eventually - cron will normally start a new one */ 940 alarm(timeout); 941 } 942 943 if (cachedir == NULL) { 944 warnx("cache directory required"); 945 goto usage; 946 } 947 if (outputdir == NULL) { 948 warnx("output directory required"); 949 goto usage; 950 } 951 952 if ((cachefd = open(cachedir, O_RDONLY, 0)) == -1) 953 err(1, "cache directory %s", cachedir); 954 if ((outdirfd = open(outputdir, O_RDONLY, 0)) == -1) 955 err(1, "output directory %s", outputdir); 956 957 if (outformats == 0) 958 outformats = FORMAT_OPENBGPD; 959 960 if (talsz == 0) 961 talsz = tal_load_default(tals, TALSZ_MAX); 962 if (talsz == 0) 963 err(1, "no TAL files found in %s", "/etc/rpki"); 964 965 /* 966 * Create the file reader as a jailed child process. 967 * It will be responsible for reading all of the files (ROAs, 968 * manifests, certificates, etc.) and returning contents. 969 */ 970 971 if (socketpair(AF_UNIX, fl, 0, fd) == -1) 972 err(1, "socketpair"); 973 if ((procpid = fork()) == -1) 974 err(1, "fork"); 975 976 if (procpid == 0) { 977 close(fd[1]); 978 979 /* change working directory to the cache directory */ 980 if (fchdir(cachefd) == -1) 981 err(1, "fchdir"); 982 983 /* Only allow access to the cache directory. */ 984 if (unveil(".", "r") == -1) 985 err(1, "%s: unveil", cachedir); 986 if (pledge("stdio rpath", NULL) == -1) 987 err(1, "pledge"); 988 proc_parser(fd[0]); 989 errx(1, "parser process returned"); 990 } 991 992 close(fd[0]); 993 proc = fd[1]; 994 995 /* 996 * Create a process that will do the rsync'ing. 997 * This process is responsible for making sure that all the 998 * repositories referenced by a certificate manifest (or the 999 * TAL) exists and has been downloaded. 1000 */ 1001 1002 if (!noop) { 1003 if (socketpair(AF_UNIX, fl, 0, fd) == -1) 1004 err(1, "socketpair"); 1005 if ((rsyncpid = fork()) == -1) 1006 err(1, "fork"); 1007 1008 if (rsyncpid == 0) { 1009 close(proc); 1010 close(fd[1]); 1011 1012 /* change working directory to the cache directory */ 1013 if (fchdir(cachefd) == -1) 1014 err(1, "fchdir"); 1015 1016 if (pledge("stdio rpath proc exec unveil", NULL) == -1) 1017 err(1, "pledge"); 1018 1019 proc_rsync(rsync_prog, bind_addr, fd[0]); 1020 errx(1, "rsync process returned"); 1021 } 1022 1023 close(fd[0]); 1024 rsync = fd[1]; 1025 } else { 1026 rsync = -1; 1027 rsyncpid = -1; 1028 } 1029 1030 /* 1031 * Create a process that will fetch data via https. 1032 * With every request the http process receives a file descriptor 1033 * where the data should be written to. 1034 */ 1035 1036 if (!noop) { 1037 if (socketpair(AF_UNIX, fl, 0, fd) == -1) 1038 err(1, "socketpair"); 1039 if ((httppid = fork()) == -1) 1040 err(1, "fork"); 1041 1042 if (httppid == 0) { 1043 close(proc); 1044 close(rsync); 1045 close(fd[1]); 1046 1047 /* change working directory to the cache directory */ 1048 if (fchdir(cachefd) == -1) 1049 err(1, "fchdir"); 1050 1051 if (pledge("stdio rpath inet dns recvfd", NULL) == -1) 1052 err(1, "pledge"); 1053 1054 proc_http(bind_addr, fd[0]); 1055 errx(1, "http process returned"); 1056 } 1057 1058 close(fd[0]); 1059 http = fd[1]; 1060 } else { 1061 http = -1; 1062 httppid = -1; 1063 } 1064 1065 if (pledge("stdio rpath wpath cpath fattr sendfd", NULL) == -1) 1066 err(1, "pledge"); 1067 1068 msgbuf_init(&procq); 1069 msgbuf_init(&rsyncq); 1070 msgbuf_init(&httpq); 1071 procq.fd = proc; 1072 rsyncq.fd = rsync; 1073 httpq.fd = http; 1074 1075 /* 1076 * The main process drives the top-down scan to leaf ROAs using 1077 * data downloaded by the rsync process and parsed by the 1078 * parsing process. 1079 */ 1080 1081 pfd[0].fd = rsync; 1082 pfd[1].fd = proc; 1083 pfd[2].fd = http; 1084 1085 /* 1086 * Prime the process with our TAL file. 1087 * This will contain (hopefully) links to our manifest and we 1088 * can get the ball rolling. 1089 */ 1090 1091 for (i = 0; i < talsz; i++) 1092 queue_add_tal(tals[i]); 1093 1094 /* change working directory to the cache directory */ 1095 if (fchdir(cachefd) == -1) 1096 err(1, "fchdir"); 1097 1098 while (entity_queue > 0 && !killme) { 1099 pfd[0].events = POLLIN; 1100 if (rsyncq.queued) 1101 pfd[0].events |= POLLOUT; 1102 pfd[1].events = POLLIN; 1103 if (procq.queued) 1104 pfd[1].events |= POLLOUT; 1105 pfd[2].events = POLLIN; 1106 if (httpq.queued) 1107 pfd[2].events |= POLLOUT; 1108 1109 if ((c = poll(pfd, 3, INFTIM)) == -1) { 1110 if (errno == EINTR) 1111 continue; 1112 err(1, "poll"); 1113 } 1114 1115 if ((pfd[0].revents & (POLLERR|POLLNVAL)) || 1116 (pfd[1].revents & (POLLERR|POLLNVAL)) || 1117 (pfd[2].revents & (POLLERR|POLLNVAL))) 1118 errx(1, "poll: bad fd"); 1119 if ((pfd[0].revents & POLLHUP) || 1120 (pfd[1].revents & POLLHUP) || 1121 (pfd[2].revents & POLLHUP)) 1122 errx(1, "poll: hangup"); 1123 1124 if (pfd[0].revents & POLLOUT) { 1125 switch (msgbuf_write(&rsyncq)) { 1126 case 0: 1127 errx(1, "write: connection closed"); 1128 case -1: 1129 err(1, "write"); 1130 } 1131 } 1132 if (pfd[1].revents & POLLOUT) { 1133 switch (msgbuf_write(&procq)) { 1134 case 0: 1135 errx(1, "write: connection closed"); 1136 case -1: 1137 err(1, "write"); 1138 } 1139 } 1140 if (pfd[2].revents & POLLOUT) { 1141 switch (msgbuf_write(&httpq)) { 1142 case 0: 1143 errx(1, "write: connection closed"); 1144 case -1: 1145 err(1, "write"); 1146 } 1147 } 1148 1149 1150 /* 1151 * Check the rsync and http process. 1152 * This means that one of our modules has completed 1153 * downloading and we can flush the module requests into 1154 * the parser process. 1155 */ 1156 1157 if ((pfd[0].revents & POLLIN)) { 1158 io_simple_read(rsync, &id, sizeof(id)); 1159 io_simple_read(rsync, &ok, sizeof(ok)); 1160 rp = repo_find(id); 1161 if (rp == NULL) 1162 errx(1, "unknown repository id: %zu", id); 1163 1164 assert(!rp->loaded); 1165 if (ok) 1166 logx("%s: loaded from network", rp->local); 1167 else 1168 logx("%s: load from network failed, " 1169 "fallback to cache", rp->local); 1170 rp->loaded = 1; 1171 stats.repos++; 1172 entityq_flush(rp); 1173 } 1174 1175 if ((pfd[2].revents & POLLIN)) { 1176 enum http_result res; 1177 char *last_mod; 1178 1179 io_simple_read(http, &id, sizeof(id)); 1180 io_simple_read(http, &res, sizeof(res)); 1181 io_str_read(http, &last_mod); 1182 rp = repo_find(id); 1183 if (rp == NULL) 1184 errx(1, "unknown repository id: %zu", id); 1185 1186 assert(!rp->loaded); 1187 if (http_done(rp, res)) { 1188 rp->loaded = 1; 1189 stats.repos++; 1190 entityq_flush(rp); 1191 } 1192 free(last_mod); 1193 } 1194 1195 /* 1196 * The parser has finished something for us. 1197 * Dequeue these one by one. 1198 */ 1199 1200 if ((pfd[1].revents & POLLIN)) { 1201 entity_process(proc, &stats, &v); 1202 } 1203 } 1204 1205 if (killme) { 1206 syslog(LOG_CRIT|LOG_DAEMON, 1207 "excessive runtime (%d seconds), giving up", timeout); 1208 errx(1, "excessive runtime (%d seconds), giving up", timeout); 1209 } 1210 1211 assert(entity_queue == 0); 1212 logx("all files parsed: generating output"); 1213 rc = 0; 1214 1215 /* 1216 * For clean-up, close the input for the parser and rsync 1217 * process. 1218 * This will cause them to exit, then we reap them. 1219 */ 1220 1221 close(proc); 1222 close(rsync); 1223 close(http); 1224 1225 if (waitpid(procpid, &st, 0) == -1) 1226 err(1, "waitpid"); 1227 if (!WIFEXITED(st) || WEXITSTATUS(st) != 0) { 1228 warnx("parser process exited abnormally"); 1229 rc = 1; 1230 } 1231 if (!noop) { 1232 if (waitpid(rsyncpid, &st, 0) == -1) 1233 err(1, "waitpid"); 1234 if (!WIFEXITED(st) || WEXITSTATUS(st) != 0) { 1235 warnx("rsync process exited abnormally"); 1236 rc = 1; 1237 } 1238 1239 if (waitpid(httppid, &st, 0) == -1) 1240 err(1, "waitpid"); 1241 if (!WIFEXITED(st) || WEXITSTATUS(st) != 0) { 1242 warnx("http process exited abnormally"); 1243 rc = 1; 1244 } 1245 } 1246 1247 stats.del_files = repo_cleanup(); 1248 1249 gettimeofday(&now_time, NULL); 1250 timersub(&now_time, &start_time, &stats.elapsed_time); 1251 if (getrusage(RUSAGE_SELF, &ru) == 0) { 1252 stats.user_time = ru.ru_utime; 1253 stats.system_time = ru.ru_stime; 1254 } 1255 if (getrusage(RUSAGE_CHILDREN, &ru) == 0) { 1256 timeradd(&stats.user_time, &ru.ru_utime, &stats.user_time); 1257 timeradd(&stats.system_time, &ru.ru_stime, &stats.system_time); 1258 } 1259 1260 /* change working directory to the cache directory */ 1261 if (fchdir(outdirfd) == -1) 1262 err(1, "fchdir output dir"); 1263 1264 if (outputfiles(&v, &stats)) 1265 rc = 1; 1266 1267 1268 logx("Route Origin Authorizations: %zu (%zu failed parse, %zu invalid)", 1269 stats.roas, stats.roas_fail, stats.roas_invalid); 1270 logx("Certificates: %zu (%zu failed parse, %zu invalid)", 1271 stats.certs, stats.certs_fail, stats.certs_invalid); 1272 logx("Trust Anchor Locators: %zu", stats.tals); 1273 logx("Manifests: %zu (%zu failed parse, %zu stale)", 1274 stats.mfts, stats.mfts_fail, stats.mfts_stale); 1275 logx("Certificate revocation lists: %zu", stats.crls); 1276 logx("Ghostbuster records: %zu", stats.gbrs); 1277 logx("Repositories: %zu", stats.repos); 1278 logx("Files removed: %zu", stats.del_files); 1279 logx("VRP Entries: %zu (%zu unique)", stats.vrps, stats.uniqs); 1280 1281 /* Memory cleanup. */ 1282 while ((rp = SLIST_FIRST(&repos)) != NULL) { 1283 SLIST_REMOVE_HEAD(&repos, entry); 1284 free(rp->repouri); 1285 free(rp->local); 1286 free(rp->temp); 1287 free(rp->uris[0]); 1288 free(rp->uris[1]); 1289 free(rp); 1290 } 1291 1292 for (i = 0; i < outsz; i++) 1293 roa_free(out[i]); 1294 free(out); 1295 1296 return rc; 1297 1298usage: 1299 fprintf(stderr, 1300 "usage: rpki-client [-BcjnoVv] [-b sourceaddr] [-d cachedir]" 1301 " [-e rsync_prog]\n" 1302 " [-s timeout] [-T table] [-t tal]" 1303 " [outputdir]\n"); 1304 return 1; 1305} 1306