main.c revision 1.98
1/*	$OpenBSD: main.c,v 1.98 2021/02/05 12:26:52 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, 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, INFTIM)) == -1) {
956			if (errno == EINTR)
957				continue;
958			err(1, "poll");
959		}
960
961		if ((pfd[0].revents & (POLLERR|POLLNVAL)) ||
962		    (pfd[1].revents & (POLLERR|POLLNVAL)))
963			errx(1, "poll: bad fd");
964		if ((pfd[0].revents & POLLHUP) ||
965		    (pfd[1].revents & POLLHUP))
966			errx(1, "poll: hangup");
967
968		if (pfd[0].revents & POLLOUT) {
969			switch (msgbuf_write(&rsyncq)) {
970			case 0:
971				errx(1, "write: connection closed");
972			case -1:
973				err(1, "write");
974			}
975		}
976		if (pfd[1].revents & POLLOUT) {
977			switch (msgbuf_write(&procq)) {
978			case 0:
979				errx(1, "write: connection closed");
980			case -1:
981				err(1, "write");
982			}
983		}
984
985		/*
986		 * Check the rsync process.
987		 * This means that one of our modules has completed
988		 * downloading and we can flush the module requests into
989		 * the parser process.
990		 */
991
992		if ((pfd[0].revents & POLLIN)) {
993			int ok;
994			io_simple_read(rsync, &i, sizeof(size_t));
995			io_simple_read(rsync, &ok, sizeof(ok));
996			assert(i < rt.reposz);
997
998			assert(!rt.repos[i].loaded);
999			rt.repos[i].loaded = 1;
1000			if (ok)
1001				logx("%s: loaded from network",
1002				    rt.repos[i].local);
1003			else
1004				logx("%s: load from network failed, "
1005				    "fallback to cache", rt.repos[i].local);
1006			stats.repos++;
1007			entityq_flush(&q, &rt.repos[i]);
1008		}
1009
1010		/*
1011		 * The parser has finished something for us.
1012		 * Dequeue these one by one.
1013		 */
1014
1015		if ((pfd[1].revents & POLLIN)) {
1016			entity_process(proc, &stats, &q, &v);
1017		}
1018	}
1019
1020	if (killme) {
1021		syslog(LOG_CRIT|LOG_DAEMON,
1022		    "excessive runtime (%d seconds), giving up", timeout);
1023		errx(1, "excessive runtime (%d seconds), giving up", timeout);
1024	}
1025
1026	assert(TAILQ_EMPTY(&q));
1027	logx("all files parsed: generating output");
1028	rc = 0;
1029
1030	/*
1031	 * For clean-up, close the input for the parser and rsync
1032	 * process.
1033	 * This will cause them to exit, then we reap them.
1034	 */
1035
1036	close(proc);
1037	close(rsync);
1038
1039	if (waitpid(procpid, &st, 0) == -1)
1040		err(1, "waitpid");
1041	if (!WIFEXITED(st) || WEXITSTATUS(st) != 0) {
1042		warnx("parser process exited abnormally");
1043		rc = 1;
1044	}
1045	if (!noop) {
1046		if (waitpid(rsyncpid, &st, 0) == -1)
1047			err(1, "waitpid");
1048		if (!WIFEXITED(st) || WEXITSTATUS(st) != 0) {
1049			warnx("rsync process exited abnormally");
1050			rc = 1;
1051		}
1052	}
1053	gettimeofday(&now_time, NULL);
1054	timersub(&now_time, &start_time, &stats.elapsed_time);
1055	if (getrusage(RUSAGE_SELF, &ru) == 0) {
1056		stats.user_time = ru.ru_utime;
1057		stats.system_time = ru.ru_stime;
1058	}
1059	if (getrusage(RUSAGE_CHILDREN, &ru) == 0) {
1060		timeradd(&stats.user_time, &ru.ru_utime, &stats.user_time);
1061		timeradd(&stats.system_time, &ru.ru_stime, &stats.system_time);
1062	}
1063
1064	if (outputfiles(&v, &stats))
1065		rc = 1;
1066
1067	stats.del_files = repo_cleanup(cachedir);
1068
1069	logx("Route Origin Authorizations: %zu (%zu failed parse, %zu invalid)",
1070	    stats.roas, stats.roas_fail, stats.roas_invalid);
1071	logx("Certificates: %zu (%zu failed parse, %zu invalid)",
1072	    stats.certs, stats.certs_fail, stats.certs_invalid);
1073	logx("Trust Anchor Locators: %zu", stats.tals);
1074	logx("Manifests: %zu (%zu failed parse, %zu stale)",
1075	    stats.mfts, stats.mfts_fail, stats.mfts_stale);
1076	logx("Certificate revocation lists: %zu", stats.crls);
1077	logx("Ghostbuster records: %zu", stats.gbrs);
1078	logx("Repositories: %zu", stats.repos);
1079	logx("Files removed: %zu", stats.del_files);
1080	logx("VRP Entries: %zu (%zu unique)", stats.vrps, stats.uniqs);
1081
1082	/* Memory cleanup. */
1083	for (i = 0; i < rt.reposz; i++) {
1084		free(rt.repos[i].local);
1085		free(rt.repos[i].repo);
1086	}
1087	free(rt.repos);
1088
1089	for (i = 0; i < outsz; i++)
1090		roa_free(out[i]);
1091	free(out);
1092
1093	return rc;
1094
1095usage:
1096	fprintf(stderr,
1097	    "usage: rpki-client [-Bcjnov] [-b sourceaddr] [-d cachedir]"
1098	    " [-e rsync_prog]\n"
1099	    "                   [-s timeout] [-T table] [-t tal]"
1100	    " [outputdir]\n");
1101	return 1;
1102}
1103