1 /* $OpenBSD: main.c,v 1.236 2023/04/27 08:37:53 beck Exp $ */ 2 /* 3 * Copyright (c) 2021 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/types.h> 20 #include <sys/queue.h> 21 #include <sys/resource.h> 22 #include <sys/socket.h> 23 #include <sys/statvfs.h> 24 #include <sys/time.h> 25 #include <sys/tree.h> 26 #include <sys/wait.h> 27 28 #include <assert.h> 29 #include <dirent.h> 30 #include <err.h> 31 #include <errno.h> 32 #include <fcntl.h> 33 #include <fnmatch.h> 34 #include <limits.h> 35 #include <poll.h> 36 #include <pwd.h> 37 #include <signal.h> 38 #include <stdarg.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <syslog.h> 43 #include <time.h> 44 #include <unistd.h> 45 46 #include <imsg.h> 47 48 #include "extern.h" 49 #include "version.h" 50 51 const char *tals[TALSZ_MAX]; 52 const char *taldescs[TALSZ_MAX]; 53 unsigned int talrepocnt[TALSZ_MAX]; 54 struct repotalstats talstats[TALSZ_MAX]; 55 int talsz; 56 57 size_t entity_queue; 58 int timeout = 60*60; 59 volatile sig_atomic_t killme; 60 void suicide(int sig); 61 62 static struct filepath_tree fpt = RB_INITIALIZER(&fpt); 63 static struct msgbuf procq, rsyncq, httpq, rrdpq; 64 static int cachefd, outdirfd; 65 66 const char *bird_tablename = "ROAS"; 67 68 int verbose; 69 int noop; 70 int excludeaspa; 71 int filemode; 72 int shortlistmode; 73 int rrdpon = 1; 74 int repo_timeout; 75 time_t deadline; 76 77 int64_t evaluation_time; 78 79 struct stats stats; 80 81 struct fqdnlistentry { 82 LIST_ENTRY(fqdnlistentry) entry; 83 char *fqdn; 84 }; 85 LIST_HEAD(fqdns, fqdnlistentry); 86 87 struct fqdns shortlist = LIST_HEAD_INITIALIZER(fqdns); 88 struct fqdns skiplist = LIST_HEAD_INITIALIZER(fqdns); 89 90 /* 91 * Log a message to stderr if and only if "verbose" is non-zero. 92 * This uses the err(3) functionality. 93 */ 94 void 95 logx(const char *fmt, ...) 96 { 97 va_list ap; 98 99 if (verbose && fmt != NULL) { 100 va_start(ap, fmt); 101 vwarnx(fmt, ap); 102 va_end(ap); 103 } 104 } 105 106 time_t 107 getmonotime(void) 108 { 109 struct timespec ts; 110 111 if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) 112 err(1, "clock_gettime"); 113 return (ts.tv_sec); 114 } 115 116 void 117 entity_free(struct entity *ent) 118 { 119 if (ent == NULL) 120 return; 121 122 free(ent->path); 123 free(ent->file); 124 free(ent->mftaki); 125 free(ent->data); 126 free(ent); 127 } 128 129 /* 130 * Read a queue entity from the descriptor. 131 * Matched by entity_buffer_req(). 132 * The pointer must be passed entity_free(). 133 */ 134 void 135 entity_read_req(struct ibuf *b, struct entity *ent) 136 { 137 io_read_buf(b, &ent->type, sizeof(ent->type)); 138 io_read_buf(b, &ent->location, sizeof(ent->location)); 139 io_read_buf(b, &ent->repoid, sizeof(ent->repoid)); 140 io_read_buf(b, &ent->talid, sizeof(ent->talid)); 141 io_read_str(b, &ent->path); 142 io_read_str(b, &ent->file); 143 io_read_str(b, &ent->mftaki); 144 io_read_buf_alloc(b, (void **)&ent->data, &ent->datasz); 145 } 146 147 /* 148 * Write the queue entity. 149 * Matched by entity_read_req(). 150 */ 151 static void 152 entity_write_req(const struct entity *ent) 153 { 154 struct ibuf *b; 155 156 b = io_new_buffer(); 157 io_simple_buffer(b, &ent->type, sizeof(ent->type)); 158 io_simple_buffer(b, &ent->location, sizeof(ent->location)); 159 io_simple_buffer(b, &ent->repoid, sizeof(ent->repoid)); 160 io_simple_buffer(b, &ent->talid, sizeof(ent->talid)); 161 io_str_buffer(b, ent->path); 162 io_str_buffer(b, ent->file); 163 io_str_buffer(b, ent->mftaki); 164 io_buf_buffer(b, ent->data, ent->datasz); 165 io_close_buffer(&procq, b); 166 } 167 168 static void 169 entity_write_repo(struct repo *rp) 170 { 171 struct ibuf *b; 172 enum rtype type = RTYPE_REPO; 173 enum location loc = DIR_UNKNOWN; 174 unsigned int repoid; 175 char *path, *altpath; 176 int talid = 0; 177 178 repoid = repo_id(rp); 179 path = repo_basedir(rp, 0); 180 altpath = repo_basedir(rp, 1); 181 b = io_new_buffer(); 182 io_simple_buffer(b, &type, sizeof(type)); 183 io_simple_buffer(b, &loc, sizeof(loc)); 184 io_simple_buffer(b, &repoid, sizeof(repoid)); 185 io_simple_buffer(b, &talid, sizeof(talid)); 186 io_str_buffer(b, path); 187 io_str_buffer(b, altpath); 188 io_buf_buffer(b, NULL, 0); /* ent->mftaki */ 189 io_buf_buffer(b, NULL, 0); /* ent->data */ 190 io_close_buffer(&procq, b); 191 free(path); 192 free(altpath); 193 } 194 195 /* 196 * Scan through all queued requests and see which ones are in the given 197 * repo, then flush those into the parser process. 198 */ 199 void 200 entityq_flush(struct entityq *q, struct repo *rp) 201 { 202 struct entity *p, *np; 203 204 entity_write_repo(rp); 205 206 TAILQ_FOREACH_SAFE(p, q, entries, np) { 207 entity_write_req(p); 208 TAILQ_REMOVE(q, p, entries); 209 entity_free(p); 210 } 211 } 212 213 /* 214 * Add the heap-allocated file to the queue for processing. 215 */ 216 static void 217 entityq_add(char *path, char *file, enum rtype type, enum location loc, 218 struct repo *rp, unsigned char *data, size_t datasz, int talid, 219 char *mftaki) 220 { 221 struct entity *p; 222 223 if ((p = calloc(1, sizeof(struct entity))) == NULL) 224 err(1, NULL); 225 226 p->type = type; 227 p->location = loc; 228 p->talid = talid; 229 p->mftaki = mftaki; 230 p->path = path; 231 if (rp != NULL) 232 p->repoid = repo_id(rp); 233 p->file = file; 234 p->data = data; 235 p->datasz = (data != NULL) ? datasz : 0; 236 237 entity_queue++; 238 239 /* 240 * Write to the queue if there's no repo or the repo has already 241 * been loaded else enqueue it for later. 242 */ 243 244 if (rp == NULL || !repo_queued(rp, p)) { 245 entity_write_req(p); 246 entity_free(p); 247 } 248 } 249 250 static void 251 rrdp_file_resp(unsigned int id, int ok) 252 { 253 enum rrdp_msg type = RRDP_FILE; 254 struct ibuf *b; 255 256 b = io_new_buffer(); 257 io_simple_buffer(b, &type, sizeof(type)); 258 io_simple_buffer(b, &id, sizeof(id)); 259 io_simple_buffer(b, &ok, sizeof(ok)); 260 io_close_buffer(&rrdpq, b); 261 } 262 263 void 264 rrdp_fetch(unsigned int id, const char *uri, const char *local, 265 struct rrdp_session *s) 266 { 267 enum rrdp_msg type = RRDP_START; 268 struct ibuf *b; 269 270 b = io_new_buffer(); 271 io_simple_buffer(b, &type, sizeof(type)); 272 io_simple_buffer(b, &id, sizeof(id)); 273 io_str_buffer(b, local); 274 io_str_buffer(b, uri); 275 io_str_buffer(b, s->session_id); 276 io_simple_buffer(b, &s->serial, sizeof(s->serial)); 277 io_str_buffer(b, s->last_mod); 278 io_close_buffer(&rrdpq, b); 279 } 280 281 void 282 rrdp_abort(unsigned int id) 283 { 284 enum rrdp_msg type = RRDP_ABORT; 285 struct ibuf *b; 286 287 b = io_new_buffer(); 288 io_simple_buffer(b, &type, sizeof(type)); 289 io_simple_buffer(b, &id, sizeof(id)); 290 io_close_buffer(&rrdpq, b); 291 } 292 293 /* 294 * Request a repository sync via rsync URI to directory local. 295 */ 296 void 297 rsync_fetch(unsigned int id, const char *uri, const char *local, 298 const char *base) 299 { 300 struct ibuf *b; 301 302 b = io_new_buffer(); 303 io_simple_buffer(b, &id, sizeof(id)); 304 io_str_buffer(b, local); 305 io_str_buffer(b, base); 306 io_str_buffer(b, uri); 307 io_close_buffer(&rsyncq, b); 308 } 309 310 void 311 rsync_abort(unsigned int id) 312 { 313 struct ibuf *b; 314 315 b = io_new_buffer(); 316 io_simple_buffer(b, &id, sizeof(id)); 317 io_str_buffer(b, NULL); 318 io_str_buffer(b, NULL); 319 io_str_buffer(b, NULL); 320 io_close_buffer(&rsyncq, b); 321 } 322 323 /* 324 * Request a file from a https uri, data is written to the file descriptor fd. 325 */ 326 void 327 http_fetch(unsigned int id, const char *uri, const char *last_mod, int fd) 328 { 329 struct ibuf *b; 330 331 b = io_new_buffer(); 332 io_simple_buffer(b, &id, sizeof(id)); 333 io_str_buffer(b, uri); 334 io_str_buffer(b, last_mod); 335 /* pass file as fd */ 336 b->fd = fd; 337 io_close_buffer(&httpq, b); 338 } 339 340 /* 341 * Request some XML file on behalf of the rrdp parser. 342 * Create a pipe and pass the pipe endpoints to the http and rrdp process. 343 */ 344 static void 345 rrdp_http_fetch(unsigned int id, const char *uri, const char *last_mod) 346 { 347 enum rrdp_msg type = RRDP_HTTP_INI; 348 struct ibuf *b; 349 int pi[2]; 350 351 if (pipe2(pi, O_CLOEXEC | O_NONBLOCK) == -1) 352 err(1, "pipe"); 353 354 b = io_new_buffer(); 355 io_simple_buffer(b, &type, sizeof(type)); 356 io_simple_buffer(b, &id, sizeof(id)); 357 b->fd = pi[0]; 358 io_close_buffer(&rrdpq, b); 359 360 http_fetch(id, uri, last_mod, pi[1]); 361 } 362 363 void 364 rrdp_http_done(unsigned int id, enum http_result res, const char *last_mod) 365 { 366 enum rrdp_msg type = RRDP_HTTP_FIN; 367 struct ibuf *b; 368 369 /* RRDP request, relay response over to the rrdp process */ 370 b = io_new_buffer(); 371 io_simple_buffer(b, &type, sizeof(type)); 372 io_simple_buffer(b, &id, sizeof(id)); 373 io_simple_buffer(b, &res, sizeof(res)); 374 io_str_buffer(b, last_mod); 375 io_close_buffer(&rrdpq, b); 376 } 377 378 /* 379 * Add a file (CER, ROA, CRL) from an MFT file, RFC 6486. 380 * These are always relative to the directory in which "mft" sits. 381 */ 382 static void 383 queue_add_from_mft(const struct mft *mft) 384 { 385 size_t i; 386 struct repo *rp; 387 const struct mftfile *f; 388 char *mftaki, *nfile, *npath = NULL; 389 390 rp = repo_byid(mft->repoid); 391 for (i = 0; i < mft->filesz; i++) { 392 f = &mft->files[i]; 393 394 if (f->type == RTYPE_INVALID || f->type == RTYPE_CRL) 395 continue; 396 397 if (mft->path != NULL) 398 if ((npath = strdup(mft->path)) == NULL) 399 err(1, NULL); 400 if ((nfile = strdup(f->file)) == NULL) 401 err(1, NULL); 402 if ((mftaki = strdup(mft->aki)) == NULL) 403 err(1, NULL); 404 entityq_add(npath, nfile, f->type, f->location, rp, NULL, 0, 405 mft->talid, mftaki); 406 } 407 } 408 409 /* 410 * Add a local file to the queue of files to fetch. 411 */ 412 static void 413 queue_add_file(const char *file, enum rtype type, int talid) 414 { 415 unsigned char *buf = NULL; 416 char *nfile; 417 size_t len = 0; 418 419 if (!filemode || strncmp(file, "rsync://", strlen("rsync://")) != 0) { 420 buf = load_file(file, &len); 421 if (buf == NULL) 422 err(1, "%s", file); 423 } 424 425 if ((nfile = strdup(file)) == NULL) 426 err(1, NULL); 427 /* Not in a repository, so directly add to queue. */ 428 entityq_add(NULL, nfile, type, DIR_UNKNOWN, NULL, buf, len, talid, 429 NULL); 430 } 431 432 /* 433 * Add URIs (CER) from a TAL file, RFC 8630. 434 */ 435 static void 436 queue_add_from_tal(struct tal *tal) 437 { 438 struct repo *repo; 439 unsigned char *data; 440 char *nfile; 441 442 assert(tal->urisz); 443 444 if ((taldescs[tal->id] = strdup(tal->descr)) == NULL) 445 err(1, NULL); 446 447 /* figure out the TA filename, must be done before repo lookup */ 448 nfile = strrchr(tal->uri[0], '/'); 449 assert(nfile != NULL); 450 if ((nfile = strdup(nfile + 1)) == NULL) 451 err(1, NULL); 452 453 /* Look up the repository. */ 454 repo = ta_lookup(tal->id, tal); 455 if (repo == NULL) { 456 free(nfile); 457 return; 458 } 459 460 /* steal the pkey from the tal structure */ 461 data = tal->pkey; 462 tal->pkey = NULL; 463 entityq_add(NULL, nfile, RTYPE_CER, DIR_VALID, repo, data, 464 tal->pkeysz, tal->id, NULL); 465 } 466 467 /* 468 * Add a manifest (MFT) found in an X509 certificate, RFC 6487. 469 */ 470 static void 471 queue_add_from_cert(const struct cert *cert) 472 { 473 struct repo *repo; 474 struct fqdnlistentry *le; 475 char *nfile, *npath, *host; 476 const char *uri, *repouri, *file; 477 size_t repourisz; 478 int shortlisted = 0; 479 480 if (strncmp(cert->repo, "rsync://", 8) != 0) 481 errx(1, "unexpected protocol"); 482 host = cert->repo + 8; 483 484 LIST_FOREACH(le, &skiplist, entry) { 485 if (strncasecmp(host, le->fqdn, strcspn(host, "/")) == 0) { 486 warnx("skipping %s (listed in skiplist)", cert->repo); 487 return; 488 } 489 } 490 491 LIST_FOREACH(le, &shortlist, entry) { 492 if (strncasecmp(host, le->fqdn, strcspn(host, "/")) == 0) { 493 shortlisted = 1; 494 break; 495 } 496 } 497 if (shortlistmode && shortlisted == 0) { 498 if (verbose) 499 warnx("skipping %s (not shortlisted)", cert->repo); 500 return; 501 } 502 503 repo = repo_lookup(cert->talid, cert->repo, 504 rrdpon ? cert->notify : NULL); 505 if (repo == NULL) 506 return; 507 508 /* 509 * Figure out the cert filename and path by chopping up the 510 * MFT URI in the cert based on the repo base URI. 511 */ 512 uri = cert->mft; 513 repouri = repo_uri(repo); 514 repourisz = strlen(repouri); 515 if (strncmp(repouri, cert->mft, repourisz) != 0) { 516 warnx("%s: URI %s outside of repository", repouri, uri); 517 return; 518 } 519 uri += repourisz + 1; /* skip base and '/' */ 520 file = strrchr(uri, '/'); 521 if (file == NULL) { 522 npath = NULL; 523 if ((nfile = strdup(uri)) == NULL) 524 err(1, NULL); 525 } else { 526 if ((npath = strndup(uri, file - uri)) == NULL) 527 err(1, NULL); 528 if ((nfile = strdup(file + 1)) == NULL) 529 err(1, NULL); 530 } 531 532 entityq_add(npath, nfile, RTYPE_MFT, DIR_UNKNOWN, repo, NULL, 0, 533 cert->talid, NULL); 534 } 535 536 /* 537 * Process parsed content. 538 * For non-ROAs, we grok for more data. 539 * For ROAs, we want to extract the valid info. 540 * In all cases, we gather statistics. 541 */ 542 static void 543 entity_process(struct ibuf *b, struct stats *st, struct vrp_tree *tree, 544 struct brk_tree *brktree, struct vap_tree *vaptree) 545 { 546 enum rtype type; 547 struct tal *tal; 548 struct cert *cert; 549 struct mft *mft; 550 struct roa *roa; 551 struct aspa *aspa; 552 struct repo *rp; 553 char *file; 554 unsigned int id; 555 int talid; 556 int c; 557 558 /* 559 * For most of these, we first read whether there's any content 560 * at all---this means that the syntactic parse failed (X509 561 * certificate, for example). 562 * We follow that up with whether the resources didn't parse. 563 */ 564 io_read_buf(b, &type, sizeof(type)); 565 io_read_buf(b, &id, sizeof(id)); 566 io_read_buf(b, &talid, sizeof(talid)); 567 io_read_str(b, &file); 568 569 /* in filemode messages can be ignored, only the accounting matters */ 570 if (filemode) 571 goto done; 572 573 if (filepath_add(&fpt, file) == 0) { 574 warnx("%s: File already visited", file); 575 goto done; 576 } 577 578 rp = repo_byid(id); 579 repo_stat_inc(rp, talid, type, STYPE_OK); 580 switch (type) { 581 case RTYPE_TAL: 582 st->tals++; 583 tal = tal_read(b); 584 queue_add_from_tal(tal); 585 tal_free(tal); 586 break; 587 case RTYPE_CER: 588 io_read_buf(b, &c, sizeof(c)); 589 if (c == 0) { 590 repo_stat_inc(rp, talid, type, STYPE_FAIL); 591 break; 592 } 593 cert = cert_read(b); 594 switch (cert->purpose) { 595 case CERT_PURPOSE_CA: 596 queue_add_from_cert(cert); 597 break; 598 case CERT_PURPOSE_BGPSEC_ROUTER: 599 cert_insert_brks(brktree, cert); 600 repo_stat_inc(rp, talid, type, STYPE_BGPSEC); 601 break; 602 default: 603 errx(1, "unexpected cert purpose received"); 604 break; 605 } 606 cert_free(cert); 607 break; 608 case RTYPE_MFT: 609 io_read_buf(b, &c, sizeof(c)); 610 if (c == 0) { 611 repo_stat_inc(rp, talid, type, STYPE_FAIL); 612 break; 613 } 614 mft = mft_read(b); 615 if (!mft->stale) 616 queue_add_from_mft(mft); 617 else 618 repo_stat_inc(rp, talid, type, STYPE_STALE); 619 mft_free(mft); 620 break; 621 case RTYPE_CRL: 622 /* CRLs are sent together with MFT and not accounted for */ 623 entity_queue++; 624 break; 625 case RTYPE_ROA: 626 io_read_buf(b, &c, sizeof(c)); 627 if (c == 0) { 628 repo_stat_inc(rp, talid, type, STYPE_FAIL); 629 break; 630 } 631 roa = roa_read(b); 632 if (roa->valid) 633 roa_insert_vrps(tree, roa, rp); 634 else 635 repo_stat_inc(rp, talid, type, STYPE_INVALID); 636 roa_free(roa); 637 break; 638 case RTYPE_GBR: 639 break; 640 case RTYPE_ASPA: 641 io_read_buf(b, &c, sizeof(c)); 642 if (c == 0) { 643 repo_stat_inc(rp, talid, type, STYPE_FAIL); 644 break; 645 } 646 aspa = aspa_read(b); 647 if (aspa->valid) 648 aspa_insert_vaps(vaptree, aspa, rp); 649 else 650 repo_stat_inc(rp, talid, type, STYPE_INVALID); 651 aspa_free(aspa); 652 break; 653 case RTYPE_TAK: 654 break; 655 case RTYPE_FILE: 656 break; 657 default: 658 warnx("%s: unknown entity type %d", file, type); 659 break; 660 } 661 662 done: 663 free(file); 664 entity_queue--; 665 } 666 667 static void 668 rrdp_process(struct ibuf *b) 669 { 670 enum rrdp_msg type; 671 enum publish_type pt; 672 struct rrdp_session s; 673 char *uri, *last_mod, *data; 674 char hash[SHA256_DIGEST_LENGTH]; 675 size_t dsz; 676 unsigned int id; 677 int ok; 678 679 io_read_buf(b, &type, sizeof(type)); 680 io_read_buf(b, &id, sizeof(id)); 681 682 switch (type) { 683 case RRDP_END: 684 io_read_buf(b, &ok, sizeof(ok)); 685 rrdp_finish(id, ok); 686 break; 687 case RRDP_HTTP_REQ: 688 io_read_str(b, &uri); 689 io_read_str(b, &last_mod); 690 rrdp_http_fetch(id, uri, last_mod); 691 break; 692 case RRDP_SESSION: 693 io_read_str(b, &s.session_id); 694 io_read_buf(b, &s.serial, sizeof(s.serial)); 695 io_read_str(b, &s.last_mod); 696 rrdp_save_state(id, &s); 697 free(s.session_id); 698 free(s.last_mod); 699 break; 700 case RRDP_FILE: 701 io_read_buf(b, &pt, sizeof(pt)); 702 if (pt != PUB_ADD) 703 io_read_buf(b, &hash, sizeof(hash)); 704 io_read_str(b, &uri); 705 io_read_buf_alloc(b, (void **)&data, &dsz); 706 707 ok = rrdp_handle_file(id, pt, uri, hash, sizeof(hash), 708 data, dsz); 709 rrdp_file_resp(id, ok); 710 711 free(uri); 712 free(data); 713 break; 714 case RRDP_CLEAR: 715 rrdp_clear(id); 716 break; 717 default: 718 errx(1, "unexpected rrdp response"); 719 } 720 } 721 722 static void 723 sum_stats(const struct repo *rp, const struct repotalstats *in, void *arg) 724 { 725 struct repotalstats *out = arg; 726 727 out->mfts += in->mfts; 728 out->mfts_fail += in->mfts_fail; 729 out->mfts_stale += in->mfts_stale; 730 out->certs += in->certs; 731 out->certs_fail += in->certs_fail; 732 out->roas += in->roas; 733 out->roas_fail += in->roas_fail; 734 out->roas_invalid += in->roas_invalid; 735 out->aspas += in->aspas; 736 out->aspas_fail += in->aspas_fail; 737 out->aspas_invalid += in->aspas_invalid; 738 out->brks += in->brks; 739 out->crls += in->crls; 740 out->gbrs += in->gbrs; 741 out->taks += in->taks; 742 out->vrps += in->vrps; 743 out->vrps_uniqs += in->vrps_uniqs; 744 out->vaps += in->vaps; 745 out->vaps_uniqs += in->vaps_uniqs; 746 out->vaps_pas += in->vaps_pas; 747 out->vaps_pas4 += in->vaps_pas4; 748 out->vaps_pas6 += in->vaps_pas6; 749 } 750 751 static void 752 sum_repostats(const struct repo *rp, const struct repostats *in, void *arg) 753 { 754 struct repostats *out = arg; 755 756 out->del_files += in->del_files; 757 out->extra_files += in->extra_files; 758 out->del_dirs += in->del_dirs; 759 timespecadd(&in->sync_time, &out->sync_time, &out->sync_time); 760 } 761 762 /* 763 * Assign filenames ending in ".tal" in "/etc/rpki" into "tals", 764 * returning the number of files found and filled-in. 765 * This may be zero. 766 * Don't exceed "max" filenames. 767 */ 768 static int 769 tal_load_default(void) 770 { 771 static const char *confdir = "/etc/rpki"; 772 int s = 0; 773 char *path; 774 DIR *dirp; 775 struct dirent *dp; 776 777 dirp = opendir(confdir); 778 if (dirp == NULL) 779 err(1, "open %s", confdir); 780 while ((dp = readdir(dirp)) != NULL) { 781 if (fnmatch("*.tal", dp->d_name, FNM_PERIOD) == FNM_NOMATCH) 782 continue; 783 if (s >= TALSZ_MAX) 784 err(1, "too many tal files found in %s", 785 confdir); 786 if (asprintf(&path, "%s/%s", confdir, dp->d_name) == -1) 787 err(1, NULL); 788 tals[s++] = path; 789 } 790 closedir(dirp); 791 return s; 792 } 793 794 /* 795 * Load the list of FQDNs from the skiplist which are to be distrusted. 796 * Return 0 on success. 797 */ 798 static void 799 load_skiplist(const char *slf) 800 { 801 struct fqdnlistentry *le; 802 FILE *fp; 803 char *line = NULL; 804 size_t linesize = 0, linelen; 805 806 if ((fp = fopen(slf, "r")) == NULL) { 807 if (errno == ENOENT && strcmp(slf, DEFAULT_SKIPLIST_FILE) == 0) 808 return; 809 err(1, "failed to open %s", slf); 810 } 811 812 while (getline(&line, &linesize, fp) != -1) { 813 /* just eat comment lines or empty lines*/ 814 if (line[0] == '#' || line[0] == '\n') 815 continue; 816 817 if (line[0] == ' ' || line[0] == '\t') 818 errx(1, "invalid entry in skiplist: %s", line); 819 820 /* 821 * Ignore anything after comment sign, whitespaces, 822 * also chop off LF or CR. 823 */ 824 linelen = strcspn(line, " #\r\n\t"); 825 line[linelen] = '\0'; 826 827 if (!valid_uri(line, linelen, NULL)) 828 errx(1, "invalid entry in skiplist: %s", line); 829 830 if ((le = malloc(sizeof(struct fqdnlistentry))) == NULL) 831 err(1, NULL); 832 if ((le->fqdn = strdup(line)) == NULL) 833 err(1, NULL); 834 835 LIST_INSERT_HEAD(&skiplist, le, entry); 836 stats.skiplistentries++; 837 } 838 839 fclose(fp); 840 free(line); 841 } 842 843 /* 844 * Load shortlist entries. 845 */ 846 static void 847 load_shortlist(const char *fqdn) 848 { 849 struct fqdnlistentry *le; 850 851 if (!valid_uri(fqdn, strlen(fqdn), NULL)) 852 errx(1, "invalid fqdn passed to -q: %s", fqdn); 853 854 if ((le = malloc(sizeof(struct fqdnlistentry))) == NULL) 855 err(1, NULL); 856 857 if ((le->fqdn = strdup(fqdn)) == NULL) 858 err(1, NULL); 859 860 LIST_INSERT_HEAD(&shortlist, le, entry); 861 } 862 863 static void 864 check_fs_size(int fd, const char *cachedir) 865 { 866 struct statvfs fs; 867 const long long minsize = 500 * 1024 * 1024; 868 const long long minnode = 300 * 1000; 869 870 if (fstatvfs(fd, &fs) == -1) 871 err(1, "statfs %s", cachedir); 872 873 if (fs.f_bavail < minsize / fs.f_frsize || 874 (fs.f_ffree > 0 && fs.f_favail < minnode)) { 875 fprintf(stderr, "WARNING: rpki-client may need more than " 876 "the available disk space\n" 877 "on the file-system holding %s.\n", cachedir); 878 fprintf(stderr, "available space: %lldkB, " 879 "suggested minimum %lldkB\n", 880 (long long)fs.f_bavail * fs.f_frsize / 1024, 881 minsize / 1024); 882 fprintf(stderr, "available inodes %lld, " 883 "suggested minimum %lld\n\n", 884 (long long)fs.f_favail, minnode); 885 fflush(stderr); 886 } 887 } 888 889 static pid_t 890 process_start(const char *title, int *fd) 891 { 892 int fl = SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK; 893 pid_t pid; 894 int pair[2]; 895 896 if (socketpair(AF_UNIX, fl, 0, pair) == -1) 897 err(1, "socketpair"); 898 if ((pid = fork()) == -1) 899 err(1, "fork"); 900 901 if (pid == 0) { 902 setproctitle("%s", title); 903 /* change working directory to the cache directory */ 904 if (fchdir(cachefd) == -1) 905 err(1, "fchdir"); 906 if (!filemode && timeout > 0) 907 alarm(timeout); 908 close(pair[1]); 909 *fd = pair[0]; 910 } else { 911 close(pair[0]); 912 *fd = pair[1]; 913 } 914 return pid; 915 } 916 917 void 918 suicide(int sig __attribute__((unused))) 919 { 920 killme = 1; 921 } 922 923 #define NPFD 4 924 925 int 926 main(int argc, char *argv[]) 927 { 928 int rc, c, i, st, proc, rsync, http, rrdp, hangup = 0; 929 pid_t pid, procpid, rsyncpid, httppid, rrdppid; 930 struct pollfd pfd[NPFD]; 931 struct msgbuf *queues[NPFD]; 932 struct ibuf *b, *httpbuf = NULL, *procbuf = NULL; 933 struct ibuf *rrdpbuf = NULL, *rsyncbuf = NULL; 934 char *rsync_prog = "openrsync"; 935 char *bind_addr = NULL; 936 const char *cachedir = NULL, *outputdir = NULL; 937 const char *errs, *name; 938 const char *skiplistfile = NULL; 939 struct vrp_tree vrps = RB_INITIALIZER(&vrps); 940 struct brk_tree brks = RB_INITIALIZER(&brks); 941 struct vap_tree vaps = RB_INITIALIZER(&vaps); 942 struct rusage ru; 943 struct timespec start_time, now_time; 944 945 clock_gettime(CLOCK_MONOTONIC, &start_time); 946 947 /* If started as root, priv-drop to _rpki-client */ 948 if (getuid() == 0) { 949 struct passwd *pw; 950 951 pw = getpwnam("_rpki-client"); 952 if (!pw) 953 errx(1, "no _rpki-client user to revoke to"); 954 if (setgroups(1, &pw->pw_gid) == -1 || 955 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 || 956 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) 957 err(1, "unable to revoke privs"); 958 } 959 cachedir = RPKI_PATH_BASE_DIR; 960 outputdir = RPKI_PATH_OUT_DIR; 961 repo_timeout = timeout / 4; 962 skiplistfile = DEFAULT_SKIPLIST_FILE; 963 964 if (pledge("stdio rpath wpath cpath inet fattr dns sendfd recvfd " 965 "proc exec unveil", NULL) == -1) 966 err(1, "pledge"); 967 968 evaluation_time = time(NULL); 969 970 while ((c = getopt(argc, argv, "Ab:Bcd:e:fH:jmnoP:rRs:S:t:T:vV")) != -1) 971 switch (c) { 972 case 'A': 973 excludeaspa = 1; 974 break; 975 case 'b': 976 bind_addr = optarg; 977 break; 978 case 'B': 979 outformats |= FORMAT_BIRD; 980 break; 981 case 'c': 982 outformats |= FORMAT_CSV; 983 break; 984 case 'd': 985 cachedir = optarg; 986 break; 987 case 'e': 988 rsync_prog = optarg; 989 break; 990 case 'f': 991 filemode = 1; 992 noop = 1; 993 break; 994 case 'H': 995 shortlistmode = 1; 996 load_shortlist(optarg); 997 break; 998 case 'j': 999 outformats |= FORMAT_JSON; 1000 break; 1001 case 'm': 1002 outformats |= FORMAT_OMETRIC; 1003 break; 1004 case 'n': 1005 noop = 1; 1006 break; 1007 case 'o': 1008 outformats |= FORMAT_OPENBGPD; 1009 break; 1010 case 'P': 1011 evaluation_time = strtonum(optarg, X509_TIME_MIN, 1012 X509_TIME_MAX, &errs); 1013 if (errs) 1014 errx(1, "-P: time in seconds %s", errs); 1015 break; 1016 case 'R': 1017 rrdpon = 0; 1018 break; 1019 case 'r': /* Remove after OpenBSD 7.3 */ 1020 rrdpon = 1; 1021 break; 1022 case 's': 1023 timeout = strtonum(optarg, 0, 24*60*60, &errs); 1024 if (errs) 1025 errx(1, "-s: %s", errs); 1026 if (timeout == 0) 1027 repo_timeout = 24*60*60; 1028 else 1029 repo_timeout = timeout / 4; 1030 break; 1031 case 'S': 1032 skiplistfile = optarg; 1033 break; 1034 case 't': 1035 if (talsz >= TALSZ_MAX) 1036 err(1, "too many tal files specified"); 1037 tals[talsz++] = optarg; 1038 break; 1039 case 'T': 1040 bird_tablename = optarg; 1041 break; 1042 case 'v': 1043 verbose++; 1044 break; 1045 case 'V': 1046 fprintf(stderr, "rpki-client %s\n", RPKI_VERSION); 1047 return 0; 1048 default: 1049 goto usage; 1050 } 1051 1052 argv += optind; 1053 argc -= optind; 1054 1055 if (!filemode) { 1056 if (argc == 1) 1057 outputdir = argv[0]; 1058 else if (argc > 1) 1059 goto usage; 1060 1061 if (outputdir == NULL) { 1062 warnx("output directory required"); 1063 goto usage; 1064 } 1065 } else { 1066 if (argc == 0) 1067 goto usage; 1068 outputdir = NULL; 1069 } 1070 1071 if (cachedir == NULL) { 1072 warnx("cache directory required"); 1073 goto usage; 1074 } 1075 1076 signal(SIGPIPE, SIG_IGN); 1077 1078 if ((cachefd = open(cachedir, O_RDONLY | O_DIRECTORY)) == -1) 1079 err(1, "cache directory %s", cachedir); 1080 if (outputdir != NULL) { 1081 if ((outdirfd = open(outputdir, O_RDONLY | O_DIRECTORY)) == -1) 1082 err(1, "output directory %s", outputdir); 1083 if (outformats == 0) 1084 outformats = FORMAT_OPENBGPD; 1085 } 1086 1087 check_fs_size(cachefd, cachedir); 1088 1089 if (talsz == 0) 1090 talsz = tal_load_default(); 1091 if (talsz == 0) 1092 err(1, "no TAL files found in %s", "/etc/rpki"); 1093 1094 /* 1095 * Create the file reader as a jailed child process. 1096 * It will be responsible for reading all of the files (ROAs, 1097 * manifests, certificates, etc.) and returning contents. 1098 */ 1099 1100 procpid = process_start("parser", &proc); 1101 if (procpid == 0) { 1102 if (!filemode) 1103 proc_parser(proc); 1104 else 1105 proc_filemode(proc); 1106 } 1107 1108 /* 1109 * Create a process that will do the rsync'ing. 1110 * This process is responsible for making sure that all the 1111 * repositories referenced by a certificate manifest (or the 1112 * TAL) exists and has been downloaded. 1113 */ 1114 1115 if (!noop) { 1116 rsyncpid = process_start("rsync", &rsync); 1117 if (rsyncpid == 0) { 1118 close(proc); 1119 proc_rsync(rsync_prog, bind_addr, rsync); 1120 } 1121 } else { 1122 rsync = -1; 1123 rsyncpid = -1; 1124 } 1125 1126 /* 1127 * Create a process that will fetch data via https. 1128 * With every request the http process receives a file descriptor 1129 * where the data should be written to. 1130 */ 1131 1132 if (!noop && rrdpon) { 1133 httppid = process_start("http", &http); 1134 1135 if (httppid == 0) { 1136 close(proc); 1137 close(rsync); 1138 proc_http(bind_addr, http); 1139 } 1140 } else { 1141 http = -1; 1142 httppid = -1; 1143 } 1144 1145 /* 1146 * Create a process that will process RRDP. 1147 * The rrdp process requires the http process to fetch the various 1148 * XML files and does this via the main process. 1149 */ 1150 1151 if (!noop && rrdpon) { 1152 rrdppid = process_start("rrdp", &rrdp); 1153 if (rrdppid == 0) { 1154 close(proc); 1155 close(rsync); 1156 close(http); 1157 proc_rrdp(rrdp); 1158 } 1159 } else { 1160 rrdp = -1; 1161 rrdppid = -1; 1162 } 1163 1164 if (!filemode && timeout > 0) { 1165 /* 1166 * Commit suicide eventually 1167 * cron will normally start a new one 1168 */ 1169 alarm(timeout); 1170 signal(SIGALRM, suicide); 1171 1172 /* give up a bit before the hard timeout and try to finish up */ 1173 if (!noop) 1174 deadline = getmonotime() + timeout - repo_timeout / 2; 1175 } 1176 1177 if (pledge("stdio rpath wpath cpath fattr sendfd unveil", NULL) == -1) 1178 err(1, "pledge"); 1179 1180 msgbuf_init(&procq); 1181 msgbuf_init(&rsyncq); 1182 msgbuf_init(&httpq); 1183 msgbuf_init(&rrdpq); 1184 procq.fd = proc; 1185 rsyncq.fd = rsync; 1186 httpq.fd = http; 1187 rrdpq.fd = rrdp; 1188 1189 /* 1190 * The main process drives the top-down scan to leaf ROAs using 1191 * data downloaded by the rsync process and parsed by the 1192 * parsing process. 1193 */ 1194 1195 pfd[0].fd = proc; 1196 queues[0] = &procq; 1197 pfd[1].fd = rsync; 1198 queues[1] = &rsyncq; 1199 pfd[2].fd = http; 1200 queues[2] = &httpq; 1201 pfd[3].fd = rrdp; 1202 queues[3] = &rrdpq; 1203 1204 load_skiplist(skiplistfile); 1205 1206 /* 1207 * Prime the process with our TAL files. 1208 * These will (hopefully) contain links to manifests and we 1209 * can get the ball rolling. 1210 */ 1211 1212 for (i = 0; i < talsz; i++) 1213 queue_add_file(tals[i], RTYPE_TAL, i); 1214 1215 if (filemode) { 1216 while (*argv != NULL) 1217 queue_add_file(*argv++, RTYPE_FILE, 0); 1218 1219 if (unveil(cachedir, "r") == -1) 1220 err(1, "unveil cachedir"); 1221 } else { 1222 if (unveil(outputdir, "rwc") == -1) 1223 err(1, "unveil outputdir"); 1224 if (unveil(cachedir, "rwc") == -1) 1225 err(1, "unveil cachedir"); 1226 } 1227 if (pledge("stdio rpath wpath cpath fattr sendfd", NULL) == -1) 1228 err(1, "unveil"); 1229 1230 /* change working directory to the cache directory */ 1231 if (fchdir(cachefd) == -1) 1232 err(1, "fchdir"); 1233 1234 while (entity_queue > 0 && !killme) { 1235 int polltim; 1236 1237 for (i = 0; i < NPFD; i++) { 1238 pfd[i].events = POLLIN; 1239 if (queues[i]->queued) 1240 pfd[i].events |= POLLOUT; 1241 } 1242 1243 polltim = repo_check_timeout(INFTIM); 1244 1245 if (poll(pfd, NPFD, polltim) == -1) { 1246 if (errno == EINTR) 1247 continue; 1248 err(1, "poll"); 1249 } 1250 1251 for (i = 0; i < NPFD; i++) { 1252 if (pfd[i].revents & (POLLERR|POLLNVAL)) { 1253 warnx("poll[%d]: bad fd", i); 1254 hangup = 1; 1255 } 1256 if (pfd[i].revents & POLLHUP) 1257 hangup = 1; 1258 if (pfd[i].revents & POLLOUT) { 1259 switch (msgbuf_write(queues[i])) { 1260 case 0: 1261 warnx("write[%d]: " 1262 "connection closed", i); 1263 hangup = 1; 1264 break; 1265 case -1: 1266 warn("write[%d]", i); 1267 hangup = 1; 1268 break; 1269 } 1270 } 1271 } 1272 if (hangup) 1273 break; 1274 1275 /* 1276 * Check the rsync and http process. 1277 * This means that one of our modules has completed 1278 * downloading and we can flush the module requests into 1279 * the parser process. 1280 */ 1281 1282 if ((pfd[1].revents & POLLIN)) { 1283 b = io_buf_read(rsync, &rsyncbuf); 1284 if (b != NULL) { 1285 unsigned int id; 1286 int ok; 1287 1288 io_read_buf(b, &id, sizeof(id)); 1289 io_read_buf(b, &ok, sizeof(ok)); 1290 rsync_finish(id, ok); 1291 ibuf_free(b); 1292 } 1293 } 1294 1295 if ((pfd[2].revents & POLLIN)) { 1296 b = io_buf_read(http, &httpbuf); 1297 if (b != NULL) { 1298 unsigned int id; 1299 enum http_result res; 1300 char *last_mod; 1301 1302 io_read_buf(b, &id, sizeof(id)); 1303 io_read_buf(b, &res, sizeof(res)); 1304 io_read_str(b, &last_mod); 1305 http_finish(id, res, last_mod); 1306 free(last_mod); 1307 ibuf_free(b); 1308 } 1309 } 1310 1311 /* 1312 * Handle RRDP requests here. 1313 */ 1314 if ((pfd[3].revents & POLLIN)) { 1315 b = io_buf_read(rrdp, &rrdpbuf); 1316 if (b != NULL) { 1317 rrdp_process(b); 1318 ibuf_free(b); 1319 } 1320 } 1321 1322 /* 1323 * The parser has finished something for us. 1324 * Dequeue these one by one. 1325 */ 1326 1327 if ((pfd[0].revents & POLLIN)) { 1328 b = io_buf_read(proc, &procbuf); 1329 if (b != NULL) { 1330 entity_process(b, &stats, &vrps, &brks, &vaps); 1331 ibuf_free(b); 1332 } 1333 } 1334 } 1335 1336 signal(SIGALRM, SIG_DFL); 1337 if (killme) { 1338 syslog(LOG_CRIT|LOG_DAEMON, 1339 "excessive runtime (%d seconds), giving up", timeout); 1340 errx(1, "excessive runtime (%d seconds), giving up", timeout); 1341 } 1342 1343 /* 1344 * For clean-up, close the input for the parser and rsync 1345 * process. 1346 * This will cause them to exit, then we reap them. 1347 */ 1348 1349 close(proc); 1350 close(rsync); 1351 close(http); 1352 close(rrdp); 1353 1354 rc = 0; 1355 for (;;) { 1356 pid = waitpid(WAIT_ANY, &st, 0); 1357 if (pid == -1) { 1358 if (errno == EINTR) 1359 continue; 1360 if (errno == ECHILD) 1361 break; 1362 err(1, "wait"); 1363 } 1364 1365 if (pid == procpid) 1366 name = "parser"; 1367 else if (pid == rsyncpid) 1368 name = "rsync"; 1369 else if (pid == httppid) 1370 name = "http"; 1371 else if (pid == rrdppid) 1372 name = "rrdp"; 1373 else 1374 name = "unknown"; 1375 1376 if (WIFSIGNALED(st)) { 1377 warnx("%s terminated signal %d", name, WTERMSIG(st)); 1378 rc = 1; 1379 } else if (!WIFEXITED(st) || WEXITSTATUS(st) != 0) { 1380 warnx("%s process exited abnormally", name); 1381 rc = 1; 1382 } 1383 } 1384 1385 /* processing did not finish because of error */ 1386 if (entity_queue != 0) 1387 errx(1, "not all files processed, giving up"); 1388 1389 /* if processing in filemode the process is done, no cleanup */ 1390 if (filemode) 1391 return rc; 1392 1393 logx("all files parsed: generating output"); 1394 1395 if (!noop) 1396 repo_cleanup(&fpt, cachefd); 1397 1398 clock_gettime(CLOCK_MONOTONIC, &now_time); 1399 timespecsub(&now_time, &start_time, &stats.elapsed_time); 1400 if (getrusage(RUSAGE_SELF, &ru) == 0) { 1401 TIMEVAL_TO_TIMESPEC(&ru.ru_utime, &stats.user_time); 1402 TIMEVAL_TO_TIMESPEC(&ru.ru_stime, &stats.system_time); 1403 } 1404 if (getrusage(RUSAGE_CHILDREN, &ru) == 0) { 1405 struct timespec ts; 1406 1407 TIMEVAL_TO_TIMESPEC(&ru.ru_utime, &ts); 1408 timespecadd(&stats.user_time, &ts, &stats.user_time); 1409 TIMEVAL_TO_TIMESPEC(&ru.ru_stime, &ts); 1410 timespecadd(&stats.system_time, &ts, &stats.system_time); 1411 } 1412 1413 /* change working directory to the output directory */ 1414 if (fchdir(outdirfd) == -1) 1415 err(1, "fchdir output dir"); 1416 1417 for (i = 0; i < talsz; i++) { 1418 repo_tal_stats_collect(sum_stats, i, &talstats[i]); 1419 repo_tal_stats_collect(sum_stats, i, &stats.repo_tal_stats); 1420 } 1421 repo_stats_collect(sum_repostats, &stats.repo_stats); 1422 1423 if (outputfiles(&vrps, &brks, &vaps, &stats)) 1424 rc = 1; 1425 1426 printf("Processing time %lld seconds " 1427 "(%lld seconds user, %lld seconds system)\n", 1428 (long long)stats.elapsed_time.tv_sec, 1429 (long long)stats.user_time.tv_sec, 1430 (long long)stats.system_time.tv_sec); 1431 printf("Skiplist entries: %u\n", stats.skiplistentries); 1432 printf("Route Origin Authorizations: %u (%u failed parse, %u " 1433 "invalid)\n", stats.repo_tal_stats.roas, 1434 stats.repo_tal_stats.roas_fail, 1435 stats.repo_tal_stats.roas_invalid); 1436 printf("AS Provider Attestations: %u (%u failed parse, %u " 1437 "invalid)\n", stats.repo_tal_stats.aspas, 1438 stats.repo_tal_stats.aspas_fail, 1439 stats.repo_tal_stats.aspas_invalid); 1440 printf("BGPsec Router Certificates: %u\n", stats.repo_tal_stats.brks); 1441 printf("Certificates: %u (%u invalid)\n", 1442 stats.repo_tal_stats.certs, stats.repo_tal_stats.certs_fail); 1443 printf("Trust Anchor Locators: %u (%u invalid)\n", 1444 stats.tals, talsz - stats.tals); 1445 printf("Manifests: %u (%u failed parse, %u stale)\n", 1446 stats.repo_tal_stats.mfts, stats.repo_tal_stats.mfts_fail, 1447 stats.repo_tal_stats.mfts_stale); 1448 printf("Certificate revocation lists: %u\n", stats.repo_tal_stats.crls); 1449 printf("Ghostbuster records: %u\n", stats.repo_tal_stats.gbrs); 1450 printf("Trust Anchor Keys: %u\n", stats.repo_tal_stats.taks); 1451 printf("Repositories: %u\n", stats.repos); 1452 printf("Cleanup: removed %u files, %u directories, %u superfluous\n", 1453 stats.repo_stats.del_files, stats.repo_stats.del_dirs, 1454 stats.repo_stats.extra_files); 1455 printf("VRP Entries: %u (%u unique)\n", stats.repo_tal_stats.vrps, 1456 stats.repo_tal_stats.vrps_uniqs); 1457 printf("VAP Entries: %u (%u unique)\n", stats.repo_tal_stats.vaps, 1458 stats.repo_tal_stats.vaps_uniqs); 1459 1460 /* Memory cleanup. */ 1461 repo_free(); 1462 1463 return rc; 1464 1465 usage: 1466 fprintf(stderr, 1467 "usage: rpki-client [-ABcjmnoRrVv] [-b sourceaddr] [-d cachedir]" 1468 " [-e rsync_prog]\n" 1469 " [-H fqdn] [-S skiplist] [-s timeout] [-T table]" 1470 " [-t tal]\n" 1471 " [outputdir]\n" 1472 " rpki-client [-Vv] [-d cachedir] [-j] [-t tal] -f file ..." 1473 "\n"); 1474 return 1; 1475 } 1476