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