Bug Summary

File:src/usr.sbin/rpki-client/main.c
Warning:line 429, column 3
Potential leak of memory pointed to by 'nfile'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name main.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.sbin/rpki-client/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.sbin/rpki-client -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.sbin/rpki-client/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.sbin/rpki-client/main.c
1/* $OpenBSD: main.c,v 1.173 2022/01/11 13:06:07 claudio 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/socket.h>
22#include <sys/resource.h>
23#include <sys/statvfs.h>
24#include <sys/tree.h>
25#include <sys/wait.h>
26
27#include <assert.h>
28#include <err.h>
29#include <errno(*__errno()).h>
30#include <dirent.h>
31#include <fcntl.h>
32#include <fnmatch.h>
33#include <poll.h>
34#include <pwd.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <signal.h>
38#include <string.h>
39#include <limits.h>
40#include <syslog.h>
41#include <unistd.h>
42#include <imsg.h>
43
44#include "extern.h"
45#include "version.h"
46
47/*
48 * Maximum number of TAL files we'll load.
49 */
50#define TALSZ_MAX8 8
51
52const char *tals[TALSZ_MAX8];
53const char *taldescs[TALSZ_MAX8];
54unsigned int talrepocnt[TALSZ_MAX8];
55size_t talsz;
56
57size_t entity_queue;
58int timeout = 60*60;
59volatile sig_atomic_t killme;
60void suicide(int sig);
61
62static struct filepath_tree fpt = RB_INITIALIZER(&fpt){ ((void*)0) };
63static struct msgbuf procq, rsyncq, httpq, rrdpq;
64static int cachefd, outdirfd;
65
66const char *bird_tablename = "ROAS";
67
68int verbose;
69int noop;
70int rrdpon = 1;
71int repo_timeout;
72
73struct stats stats;
74
75/*
76 * Log a message to stderr if and only if "verbose" is non-zero.
77 * This uses the err(3) functionality.
78 */
79void
80logx(const char *fmt, ...)
81{
82 va_list ap;
83
84 if (verbose && fmt != NULL((void*)0)) {
85 va_start(ap, fmt)__builtin_va_start(ap, fmt);
86 vwarnx(fmt, ap);
87 va_end(ap)__builtin_va_end(ap);
88 }
89}
90
91time_t
92getmonotime(void)
93{
94 struct timespec ts;
95
96 if (clock_gettime(CLOCK_MONOTONIC3, &ts) != 0)
97 err(1, "clock_gettime");
98 return (ts.tv_sec);
99}
100
101void
102entity_free(struct entity *ent)
103{
104 if (ent == NULL((void*)0))
105 return;
106
107 free(ent->path);
108 free(ent->file);
109 free(ent->data);
110 free(ent);
111}
112
113/*
114 * Read a queue entity from the descriptor.
115 * Matched by entity_buffer_req().
116 * The pointer must be passed entity_free().
117 */
118void
119entity_read_req(struct ibuf *b, struct entity *ent)
120{
121 io_read_buf(b, &ent->type, sizeof(ent->type));
122 io_read_buf(b, &ent->repoid, sizeof(ent->repoid));
123 io_read_buf(b, &ent->talid, sizeof(ent->talid));
124 io_read_str(b, &ent->path);
125 io_read_str(b, &ent->file);
126 io_read_buf_alloc(b, (void **)&ent->data, &ent->datasz);
127}
128
129/*
130 * Write the queue entity.
131 * Matched by entity_read_req().
132 */
133static void
134entity_write_req(const struct entity *ent)
135{
136 struct ibuf *b;
137
138 b = io_new_buffer();
139 io_simple_buffer(b, &ent->type, sizeof(ent->type));
140 io_simple_buffer(b, &ent->repoid, sizeof(ent->repoid));
141 io_simple_buffer(b, &ent->talid, sizeof(ent->talid));
142 io_str_buffer(b, ent->path);
143 io_str_buffer(b, ent->file);
144 io_buf_buffer(b, ent->data, ent->datasz);
145 io_close_buffer(&procq, b);
146}
147
148static void
149entity_write_repo(struct repo *rp)
150{
151 struct ibuf *b;
152 enum rtype type = RTYPE_REPO;
153 unsigned int repoid;
154 char *path;
155 int talid = 0;
156
157 repoid = repo_id(rp);
158 path = repo_basedir(rp);
159 b = io_new_buffer();
160 io_simple_buffer(b, &type, sizeof(type));
161 io_simple_buffer(b, &repoid, sizeof(repoid));
162 io_simple_buffer(b, &talid, sizeof(talid));
163 io_str_buffer(b, path);
164 io_str_buffer(b, NULL((void*)0));
165 io_buf_buffer(b, NULL((void*)0), 0);
166 io_close_buffer(&procq, b);
167 free(path);
168}
169
170/*
171 * Scan through all queued requests and see which ones are in the given
172 * repo, then flush those into the parser process.
173 */
174void
175entityq_flush(struct entityq *q, struct repo *rp)
176{
177 struct entity *p, *np;
178
179 entity_write_repo(rp);
180
181 TAILQ_FOREACH_SAFE(p, q, entries, np)for ((p) = ((q)->tqh_first); (p) != ((void*)0) && (
(np) = ((p)->entries.tqe_next), 1); (p) = (np))
{
182 entity_write_req(p);
183 TAILQ_REMOVE(q, p, entries)do { if (((p)->entries.tqe_next) != ((void*)0)) (p)->entries
.tqe_next->entries.tqe_prev = (p)->entries.tqe_prev; else
(q)->tqh_last = (p)->entries.tqe_prev; *(p)->entries
.tqe_prev = (p)->entries.tqe_next; ; ; } while (0)
;
184 entity_free(p);
185 }
186}
187
188/*
189 * Add the heap-allocated file to the queue for processing.
190 */
191static void
192entityq_add(char *path, char *file, enum rtype type, struct repo *rp,
193 unsigned char *data, size_t datasz, int talid)
194{
195 struct entity *p;
196
197 if ((p = calloc(1, sizeof(struct entity))) == NULL((void*)0))
198 err(1, NULL((void*)0));
199
200 p->type = type;
201 p->talid = talid;
202 p->path = path;
203 if (rp != NULL((void*)0))
204 p->repoid = repo_id(rp);
205 p->file = file;
206 p->data = data;
207 p->datasz = (data != NULL((void*)0)) ? datasz : 0;
208
209 entity_queue++;
210
211 /*
212 * Write to the queue if there's no repo or the repo has already
213 * been loaded else enqueue it for later.
214 */
215
216 if (rp == NULL((void*)0) || !repo_queued(rp, p)) {
217 entity_write_req(p);
218 entity_free(p);
219 }
220}
221
222static void
223rrdp_file_resp(unsigned int id, int ok)
224{
225 enum rrdp_msg type = RRDP_FILE;
226 struct ibuf *b;
227
228 b = io_new_buffer();
229 io_simple_buffer(b, &type, sizeof(type));
230 io_simple_buffer(b, &id, sizeof(id));
231 io_simple_buffer(b, &ok, sizeof(ok));
232 io_close_buffer(&rrdpq, b);
233}
234
235void
236rrdp_fetch(unsigned int id, const char *uri, const char *local,
237 struct rrdp_session *s)
238{
239 enum rrdp_msg type = RRDP_START;
240 struct ibuf *b;
241
242 b = io_new_buffer();
243 io_simple_buffer(b, &type, sizeof(type));
244 io_simple_buffer(b, &id, sizeof(id));
245 io_str_buffer(b, local);
246 io_str_buffer(b, uri);
247 io_str_buffer(b, s->session_id);
248 io_simple_buffer(b, &s->serial, sizeof(s->serial));
249 io_str_buffer(b, s->last_mod);
250 io_close_buffer(&rrdpq, b);
251}
252
253/*
254 * Request a repository sync via rsync URI to directory local.
255 */
256void
257rsync_fetch(unsigned int id, const char *uri, const char *local)
258{
259 struct ibuf *b;
260
261 b = io_new_buffer();
262 io_simple_buffer(b, &id, sizeof(id));
263 io_str_buffer(b, local);
264 io_str_buffer(b, uri);
265 io_close_buffer(&rsyncq, b);
266}
267
268/*
269 * Request a file from a https uri, data is written to the file descriptor fd.
270 */
271void
272http_fetch(unsigned int id, const char *uri, const char *last_mod, int fd)
273{
274 struct ibuf *b;
275
276 b = io_new_buffer();
277 io_simple_buffer(b, &id, sizeof(id));
278 io_str_buffer(b, uri);
279 io_str_buffer(b, last_mod);
280 /* pass file as fd */
281 b->fd = fd;
282 io_close_buffer(&httpq, b);
283}
284
285/*
286 * Request some XML file on behalf of the rrdp parser.
287 * Create a pipe and pass the pipe endpoints to the http and rrdp process.
288 */
289static void
290rrdp_http_fetch(unsigned int id, const char *uri, const char *last_mod)
291{
292 enum rrdp_msg type = RRDP_HTTP_INI;
293 struct ibuf *b;
294 int pi[2];
295
296 if (pipe2(pi, O_CLOEXEC0x10000 | O_NONBLOCK0x0004) == -1)
297 err(1, "pipe");
298
299 b = io_new_buffer();
300 io_simple_buffer(b, &type, sizeof(type));
301 io_simple_buffer(b, &id, sizeof(id));
302 b->fd = pi[0];
303 io_close_buffer(&rrdpq, b);
304
305 http_fetch(id, uri, last_mod, pi[1]);
306}
307
308void
309rrdp_http_done(unsigned int id, enum http_result res, const char *last_mod)
310{
311 enum rrdp_msg type = RRDP_HTTP_FIN;
312 struct ibuf *b;
313
314 /* RRDP request, relay response over to the rrdp process */
315 b = io_new_buffer();
316 io_simple_buffer(b, &type, sizeof(type));
317 io_simple_buffer(b, &id, sizeof(id));
318 io_simple_buffer(b, &res, sizeof(res));
319 io_str_buffer(b, last_mod);
320 io_close_buffer(&rrdpq, b);
321}
322
323/*
324 * Add a file (CER, ROA, CRL) from an MFT file, RFC 6486.
325 * These are always relative to the directory in which "mft" sits.
326 */
327static void
328queue_add_from_mft(const char *path, const struct mftfile *file,
329 enum rtype type, struct repo *rp)
330{
331 char *nfile, *npath = NULL((void*)0);
332
333 if (path != NULL((void*)0))
334 if ((npath = strdup(path)) == NULL((void*)0))
335 err(1, NULL((void*)0));
336 if ((nfile = strdup(file->file)) == NULL((void*)0))
337 err(1, NULL((void*)0));
338
339 entityq_add(npath, nfile, type, rp, NULL((void*)0), 0, -1);
340}
341
342/*
343 * Loops over queue_add_from_mft() for all files.
344 * The order here is important: we want to parse the revocation
345 * list *before* we parse anything else.
346 * FIXME: set the type of file in the mftfile so that we don't need to
347 * keep doing the check (this should be done in the parser, where we
348 * check the suffix anyway).
349 */
350static void
351queue_add_from_mft_set(const struct mft *mft, const char *name, struct repo *rp)
352{
353 size_t i, sz;
354 const struct mftfile *f;
355
356 for (i = 0; i < mft->filesz; i++) {
357 f = &mft->files[i];
358 sz = strlen(f->file);
359 assert(sz > 4)((sz > 4) ? (void)0 : __assert2("/usr/src/usr.sbin/rpki-client/main.c"
, 359, __func__, "sz > 4"))
;
360 if (strcasecmp(f->file + sz - 4, ".crl") != 0)
361 continue;
362 queue_add_from_mft(mft->path, f, RTYPE_CRL, rp);
363 }
364
365 for (i = 0; i < mft->filesz; i++) {
366 f = &mft->files[i];
367 sz = strlen(f->file);
368 assert(sz > 4)((sz > 4) ? (void)0 : __assert2("/usr/src/usr.sbin/rpki-client/main.c"
, 368, __func__, "sz > 4"))
;
369 if (strcasecmp(f->file + sz - 4, ".crl") == 0)
370 continue;
371 else if (strcasecmp(f->file + sz - 4, ".cer") == 0)
372 queue_add_from_mft(mft->path, f, RTYPE_CER, rp);
373 else if (strcasecmp(f->file + sz - 4, ".roa") == 0)
374 queue_add_from_mft(mft->path, f, RTYPE_ROA, rp);
375 else if (strcasecmp(f->file + sz - 4, ".gbr") == 0)
376 queue_add_from_mft(mft->path, f, RTYPE_GBR, rp);
377 else
378 logx("%s: unsupported file type: %s", name,
379 f->file);
380 }
381}
382
383/*
384 * Add a local TAL file (RFC 7730) to the queue of files to fetch.
385 */
386static void
387queue_add_tal(const char *file, int talid)
388{
389 unsigned char *buf;
390 char *nfile;
391 size_t len;
392
393 buf = load_file(file, &len);
394 if (buf == NULL((void*)0)) {
395 warn("%s", file);
396 return;
397 }
398
399 if ((nfile = strdup(file)) == NULL((void*)0))
400 err(1, NULL((void*)0));
401 /* Not in a repository, so directly add to queue. */
402 entityq_add(NULL((void*)0), nfile, RTYPE_TAL, NULL((void*)0), buf, len, talid);
403}
404
405/*
406 * Add URIs (CER) from a TAL file, RFC 8630.
407 */
408static void
409queue_add_from_tal(struct tal *tal)
410{
411 struct repo *repo;
412 unsigned char *data;
413 char *nfile;
414
415 assert(tal->urisz)((tal->urisz) ? (void)0 : __assert2("/usr/src/usr.sbin/rpki-client/main.c"
, 415, __func__, "tal->urisz"))
;
5
Assuming field 'urisz' is not equal to 0
6
'?' condition is true
416
417 if ((taldescs[tal->id] = strdup(tal->descr)) == NULL((void*)0))
7
Assuming the condition is false
8
Taking false branch
418 err(1, NULL((void*)0));
419
420 /* figure out the TA filename, must be done before repo lookup */
421 nfile = strrchr(tal->uri[0], '/');
422 assert(nfile != NULL)((nfile != ((void*)0)) ? (void)0 : __assert2("/usr/src/usr.sbin/rpki-client/main.c"
, 422, __func__, "nfile != NULL"))
;
9
Assuming 'nfile' is not equal to null
10
'?' condition is true
423 if ((nfile = strdup(nfile + 1)) == NULL((void*)0))
11
Memory is allocated
12
Assuming the condition is false
13
Taking false branch
424 err(1, NULL((void*)0));
425
426 /* Look up the repository. */
427 repo = ta_lookup(tal->id, tal);
428 if (repo == NULL((void*)0))
14
Assuming 'repo' is equal to NULL
15
Taking true branch
429 return;
16
Potential leak of memory pointed to by 'nfile'
430
431 /* steal the pkey from the tal structure */
432 data = tal->pkey;
433 tal->pkey = NULL((void*)0);
434 entityq_add(NULL((void*)0), nfile, RTYPE_CER, repo, data, tal->pkeysz, tal->id);
435}
436
437/*
438 * Add a manifest (MFT) found in an X509 certificate, RFC 6487.
439 */
440static void
441queue_add_from_cert(const struct cert *cert)
442{
443 struct repo *repo;
444 char *nfile, *npath;
445 const char *uri, *repouri, *file;
446 size_t repourisz;
447
448 repo = repo_lookup(cert->talid, cert->repo,
449 rrdpon ? cert->notify : NULL((void*)0));
450 if (repo == NULL((void*)0))
451 return;
452
453 /*
454 * Figure out the cert filename and path by chopping up the
455 * MFT URI in the cert based on the repo base URI.
456 */
457 uri = cert->mft;
458 repouri = repo_uri(repo);
459 repourisz = strlen(repouri);
460 if (strncmp(repouri, cert->mft, repourisz) != 0) {
461 warnx("%s: URI %s outside of repository", repouri, uri);
462 return;
463 }
464 uri += repourisz + 1; /* skip base and '/' */
465 file = strrchr(uri, '/');
466 if (file == NULL((void*)0)) {
467 npath = NULL((void*)0);
468 if ((nfile = strdup(uri)) == NULL((void*)0))
469 err(1, NULL((void*)0));
470 } else {
471 if ((npath = strndup(uri, file - uri)) == NULL((void*)0))
472 err(1, NULL((void*)0));
473 if ((nfile = strdup(file + 1)) == NULL((void*)0))
474 err(1, NULL((void*)0));
475 }
476
477 entityq_add(npath, nfile, RTYPE_MFT, repo, NULL((void*)0), 0, -1);
478}
479
480/*
481 * Process parsed content.
482 * For non-ROAs, we grok for more data.
483 * For ROAs, we want to extract the valid info.
484 * In all cases, we gather statistics.
485 */
486static void
487entity_process(struct ibuf *b, struct stats *st, struct vrp_tree *tree,
488 struct brk_tree *brktree)
489{
490 enum rtype type;
491 struct tal *tal;
492 struct cert *cert;
493 struct mft *mft;
494 struct roa *roa;
495 char *file;
496 int c;
497
498 /*
499 * For most of these, we first read whether there's any content
500 * at all---this means that the syntactic parse failed (X509
501 * certificate, for example).
502 * We follow that up with whether the resources didn't parse.
503 */
504 io_read_buf(b, &type, sizeof(type));
505 io_read_str(b, &file);
506
507 if (filepath_add(&fpt, file) == 0) {
1
Assuming the condition is false
2
Taking false branch
508 warnx("%s: File already visited", file);
509 free(file);
510 entity_queue--;
511 return;
512 }
513
514 switch (type) {
3
Control jumps to 'case RTYPE_TAL:' at line 515
515 case RTYPE_TAL:
516 st->tals++;
517 tal = tal_read(b);
518 queue_add_from_tal(tal);
4
Calling 'queue_add_from_tal'
519 tal_free(tal);
520 break;
521 case RTYPE_CER:
522 st->certs++;
523 io_read_buf(b, &c, sizeof(c));
524 if (c == 0) {
525 st->certs_fail++;
526 break;
527 }
528 cert = cert_read(b);
529 if (cert->purpose == CERT_PURPOSE_CA) {
530 /*
531 * Process the revocation list from the
532 * certificate *first*, since it might mark that
533 * we're revoked and then we don't want to
534 * process the MFT.
535 */
536 queue_add_from_cert(cert);
537 } else if (cert->purpose == CERT_PURPOSE_BGPSEC_ROUTER) {
538 cert_insert_brks(brktree, cert);
539 st->brks++;
540 } else
541 st->certs_fail++;
542 cert_free(cert);
543 break;
544 case RTYPE_MFT:
545 st->mfts++;
546 io_read_buf(b, &c, sizeof(c));
547 if (c == 0) {
548 st->mfts_fail++;
549 break;
550 }
551 mft = mft_read(b);
552 if (!mft->stale)
553 queue_add_from_mft_set(mft, file,
554 repo_byid(mft->repoid));
555 else
556 st->mfts_stale++;
557 mft_free(mft);
558 break;
559 case RTYPE_CRL:
560 st->crls++;
561 break;
562 case RTYPE_ROA:
563 st->roas++;
564 io_read_buf(b, &c, sizeof(c));
565 if (c == 0) {
566 st->roas_fail++;
567 break;
568 }
569 roa = roa_read(b);
570 if (roa->valid)
571 roa_insert_vrps(tree, roa, &st->vrps, &st->uniqs);
572 else
573 st->roas_invalid++;
574 roa_free(roa);
575 break;
576 case RTYPE_GBR:
577 st->gbrs++;
578 break;
579 default:
580 errx(1, "unknown entity type %d", type);
581 }
582
583 free(file);
584 entity_queue--;
585}
586
587static void
588rrdp_process(struct ibuf *b)
589{
590 enum rrdp_msg type;
591 enum publish_type pt;
592 struct rrdp_session s;
593 char *uri, *last_mod, *data;
594 char hash[SHA256_DIGEST_LENGTH32];
595 size_t dsz;
596 unsigned int id;
597 int ok;
598
599 io_read_buf(b, &type, sizeof(type));
600 io_read_buf(b, &id, sizeof(id));
601
602 switch (type) {
603 case RRDP_END:
604 io_read_buf(b, &ok, sizeof(ok));
605 rrdp_finish(id, ok);
606 break;
607 case RRDP_HTTP_REQ:
608 io_read_str(b, &uri);
609 io_read_str(b, &last_mod);
610 rrdp_http_fetch(id, uri, last_mod);
611 break;
612 case RRDP_SESSION:
613 io_read_str(b, &s.session_id);
614 io_read_buf(b, &s.serial, sizeof(s.serial));
615 io_read_str(b, &s.last_mod);
616 rrdp_save_state(id, &s);
617 free(s.session_id);
618 free(s.last_mod);
619 break;
620 case RRDP_FILE:
621 io_read_buf(b, &pt, sizeof(pt));
622 if (pt != PUB_ADD)
623 io_read_buf(b, &hash, sizeof(hash));
624 io_read_str(b, &uri);
625 io_read_buf_alloc(b, (void **)&data, &dsz);
626
627 ok = rrdp_handle_file(id, pt, uri, hash, sizeof(hash),
628 data, dsz);
629 rrdp_file_resp(id, ok);
630
631 free(uri);
632 free(data);
633 break;
634 default:
635 errx(1, "unexpected rrdp response");
636 }
637}
638
639/*
640 * Assign filenames ending in ".tal" in "/etc/rpki" into "tals",
641 * returning the number of files found and filled-in.
642 * This may be zero.
643 * Don't exceded "max" filenames.
644 */
645static size_t
646tal_load_default(void)
647{
648 static const char *confdir = "/etc/rpki";
649 size_t s = 0;
650 char *path;
651 DIR *dirp;
652 struct dirent *dp;
653
654 dirp = opendir(confdir);
655 if (dirp == NULL((void*)0))
656 err(1, "open %s", confdir);
657 while ((dp = readdir(dirp)) != NULL((void*)0)) {
658 if (fnmatch("*.tal", dp->d_name, FNM_PERIOD0x04) == FNM_NOMATCH1)
659 continue;
660 if (s >= TALSZ_MAX8)
661 err(1, "too many tal files found in %s",
662 confdir);
663 if (asprintf(&path, "%s/%s", confdir, dp->d_name) == -1)
664 err(1, NULL((void*)0));
665 tals[s++] = path;
666 }
667 closedir(dirp);
668 return s;
669}
670
671static void
672check_fs_size(int fd, const char *cachedir)
673{
674 struct statvfs fs;
675 const long long minsize = 500 * 1024 * 1024;
676 const long long minnode = 300 * 1000;
677
678 if (fstatvfs(fd, &fs) == -1)
679 err(1, "statfs %s", cachedir);
680
681 if (fs.f_bavail < minsize / fs.f_frsize || fs.f_favail < minnode) {
682 fprintf(stderr(&__sF[2]), "WARNING: rpki-client may need more than "
683 "the available disk space\n"
684 "on the file-system holding %s.\n", cachedir);
685 fprintf(stderr(&__sF[2]), "available space: %lldkB, "
686 "suggested minimum %lldkB\n",
687 (long long)fs.f_bavail * fs.f_frsize / 1024,
688 minsize / 1024);
689 fprintf(stderr(&__sF[2]), "available inodes %lld, "
690 "suggested minimum %lld\n\n",
691 (long long)fs.f_favail, minnode);
692 fflush(stderr(&__sF[2]));
693 }
694}
695
696void
697suicide(int sig __attribute__((unused)))
698{
699 killme = 1;
700}
701
702#define NPFD4 4
703
704int
705main(int argc, char *argv[])
706{
707 int rc, c, st, proc, rsync, http, rrdp, hangup = 0;
708 int fl = SOCK_STREAM1 | SOCK_CLOEXEC0x8000 | SOCK_NONBLOCK0x4000;
709 size_t i;
710 pid_t pid, procpid, rsyncpid, httppid, rrdppid;
711 int fd[2];
712 struct pollfd pfd[NPFD4];
713 struct msgbuf *queues[NPFD4];
714 struct ibuf *b, *httpbuf = NULL((void*)0), *procbuf = NULL((void*)0);
715 struct ibuf *rrdpbuf = NULL((void*)0), *rsyncbuf = NULL((void*)0);
716 char *rsync_prog = "openrsync";
717 char *bind_addr = NULL((void*)0);
718 const char *cachedir = NULL((void*)0), *outputdir = NULL((void*)0);
719 const char *errs, *name;
720 const char *file = NULL((void*)0);
721 struct vrp_tree vrps = RB_INITIALIZER(&vrps){ ((void*)0) };
722 struct brk_tree brks = RB_INITIALIZER(&brks){ ((void*)0) };
723 struct rusage ru;
724 struct timeval start_time, now_time;
725
726 gettimeofday(&start_time, NULL((void*)0));
727
728 /* If started as root, priv-drop to _rpki-client */
729 if (getuid() == 0) {
730 struct passwd *pw;
731
732 pw = getpwnam("_rpki-client");
733 if (!pw)
734 errx(1, "no _rpki-client user to revoke to");
735 if (setgroups(1, &pw->pw_gid) == -1 ||
736 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 ||
737 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
738 err(1, "unable to revoke privs");
739 }
740 cachedir = RPKI_PATH_BASE_DIR"/var/cache/rpki-client";
741 outputdir = RPKI_PATH_OUT_DIR"/var/db/rpki-client";
742 repo_timeout = timeout / 4;
743
744 if (pledge("stdio rpath wpath cpath inet fattr dns sendfd recvfd "
745 "proc exec unveil", NULL((void*)0)) == -1)
746 err(1, "pledge");
747
748 while ((c = getopt(argc, argv, "b:Bcd:e:f:jnorRs:t:T:vV")) != -1)
749 switch (c) {
750 case 'b':
751 bind_addr = optarg;
752 break;
753 case 'B':
754 outformats |= FORMAT_BIRD0x02;
755 break;
756 case 'c':
757 outformats |= FORMAT_CSV0x04;
758 break;
759 case 'd':
760 cachedir = optarg;
761 break;
762 case 'e':
763 rsync_prog = optarg;
764 break;
765 case 'f':
766 file = optarg;
767 noop = 1;
768 break;
769 case 'j':
770 outformats |= FORMAT_JSON0x08;
771 break;
772 case 'n':
773 noop = 1;
774 break;
775 case 'o':
776 outformats |= FORMAT_OPENBGPD0x01;
777 break;
778 case 'R':
779 rrdpon = 0;
780 break;
781 case 'r':
782 rrdpon = 1;
783 break;
784 case 's':
785 timeout = strtonum(optarg, 0, 24*60*60, &errs);
786 if (errs)
787 errx(1, "-s: %s", errs);
788 if (timeout == 0)
789 repo_timeout = 24*60*60;
790 else if (timeout < 1)
791 errx(1, "-s: %i too small", timeout);
792 else
793 repo_timeout = timeout / 4;
794 break;
795 case 't':
796 if (talsz >= TALSZ_MAX8)
797 err(1,
798 "too many tal files specified");
799 tals[talsz++] = optarg;
800 break;
801 case 'T':
802 bird_tablename = optarg;
803 break;
804 case 'v':
805 verbose++;
806 break;
807 case 'V':
808 fprintf(stderr(&__sF[2]), "rpki-client %s\n", RPKI_VERSION"7.5");
809 return 0;
810 default:
811 goto usage;
812 }
813
814 argv += optind;
815 argc -= optind;
816 if (argc == 1)
817 outputdir = argv[0];
818 else if (argc > 1)
819 goto usage;
820
821 signal(SIGPIPE13, SIG_IGN(void (*)(int))1);
822
823 if (cachedir == NULL((void*)0)) {
824 warnx("cache directory required");
825 goto usage;
826 }
827 if (outputdir == NULL((void*)0)) {
828 warnx("output directory required");
829 goto usage;
830 }
831
832 if ((cachefd = open(cachedir, O_RDONLY0x0000 | O_DIRECTORY0x20000)) == -1)
833 err(1, "cache directory %s", cachedir);
834 if ((outdirfd = open(outputdir, O_RDONLY0x0000 | O_DIRECTORY0x20000)) == -1)
835 err(1, "output directory %s", outputdir);
836
837 check_fs_size(cachefd, cachedir);
838
839 if (outformats == 0)
840 outformats = FORMAT_OPENBGPD0x01;
841
842 if (talsz == 0)
843 talsz = tal_load_default();
844 if (talsz == 0)
845 err(1, "no TAL files found in %s", "/etc/rpki");
846
847 /*
848 * Create the file reader as a jailed child process.
849 * It will be responsible for reading all of the files (ROAs,
850 * manifests, certificates, etc.) and returning contents.
851 */
852
853 if (socketpair(AF_UNIX1, fl, 0, fd) == -1)
854 err(1, "socketpair");
855 if ((procpid = fork()) == -1)
856 err(1, "fork");
857
858 if (procpid == 0) {
859 close(fd[1]);
860
861 setproctitle("parser");
862 /* change working directory to the cache directory */
863 if (fchdir(cachefd) == -1)
864 err(1, "fchdir");
865
866 if (timeout)
867 alarm(timeout);
868
869 /* Only allow access to the cache directory. */
870 if (unveil(".", "r") == -1)
871 err(1, "%s: unveil", cachedir);
872 if (pledge("stdio rpath", NULL((void*)0)) == -1)
873 err(1, "pledge");
874 proc_parser(fd[0]);
875 errx(1, "parser process returned");
876 }
877
878 close(fd[0]);
879 proc = fd[1];
880
881 /*
882 * Create a process that will do the rsync'ing.
883 * This process is responsible for making sure that all the
884 * repositories referenced by a certificate manifest (or the
885 * TAL) exists and has been downloaded.
886 */
887
888 if (!noop) {
889 if (socketpair(AF_UNIX1, fl, 0, fd) == -1)
890 err(1, "socketpair");
891 if ((rsyncpid = fork()) == -1)
892 err(1, "fork");
893
894 if (rsyncpid == 0) {
895 close(proc);
896 close(fd[1]);
897
898 setproctitle("rsync");
899 /* change working directory to the cache directory */
900 if (fchdir(cachefd) == -1)
901 err(1, "fchdir");
902
903 if (timeout)
904 alarm(timeout);
905
906 if (pledge("stdio rpath proc exec unveil", NULL((void*)0)) == -1)
907 err(1, "pledge");
908
909 proc_rsync(rsync_prog, bind_addr, fd[0]);
910 errx(1, "rsync process returned");
911 }
912
913 close(fd[0]);
914 rsync = fd[1];
915 } else {
916 rsync = -1;
917 rsyncpid = -1;
918 }
919
920 /*
921 * Create a process that will fetch data via https.
922 * With every request the http process receives a file descriptor
923 * where the data should be written to.
924 */
925
926 if (!noop) {
927 if (socketpair(AF_UNIX1, fl, 0, fd) == -1)
928 err(1, "socketpair");
929 if ((httppid = fork()) == -1)
930 err(1, "fork");
931
932 if (httppid == 0) {
933 close(proc);
934 close(rsync);
935 close(fd[1]);
936
937 setproctitle("http");
938 /* change working directory to the cache directory */
939 if (fchdir(cachefd) == -1)
940 err(1, "fchdir");
941
942 if (timeout)
943 alarm(timeout);
944
945 if (pledge("stdio rpath inet dns recvfd", NULL((void*)0)) == -1)
946 err(1, "pledge");
947
948 proc_http(bind_addr, fd[0]);
949 errx(1, "http process returned");
950 }
951
952 close(fd[0]);
953 http = fd[1];
954 } else {
955 http = -1;
956 httppid = -1;
957 }
958
959 /*
960 * Create a process that will process RRDP.
961 * The rrdp process requires the http process to fetch the various
962 * XML files and does this via the main process.
963 */
964
965 if (!noop && rrdpon) {
966 if (socketpair(AF_UNIX1, fl, 0, fd) == -1)
967 err(1, "socketpair");
968 if ((rrdppid = fork()) == -1)
969 err(1, "fork");
970
971 if (rrdppid == 0) {
972 close(proc);
973 close(rsync);
974 close(http);
975 close(fd[1]);
976
977 setproctitle("rrdp");
978 /* change working directory to the cache directory */
979 if (fchdir(cachefd) == -1)
980 err(1, "fchdir");
981
982 if (timeout)
983 alarm(timeout);
984
985 if (pledge("stdio recvfd", NULL((void*)0)) == -1)
986 err(1, "pledge");
987
988 proc_rrdp(fd[0]);
989 /* NOTREACHED */
990 }
991
992 close(fd[0]);
993 rrdp = fd[1];
994 } else {
995 rrdp = -1;
996 rrdppid = -1;
997 }
998
999 if (timeout) {
1000 /*
1001 * Commit suicide eventually
1002 * cron will normally start a new one
1003 */
1004 alarm(timeout);
1005 signal(SIGALRM14, suicide);
1006 }
1007
1008 /* TODO unveil cachedir and outputdir, no other access allowed */
1009 if (pledge("stdio rpath wpath cpath fattr sendfd", NULL((void*)0)) == -1)
1010 err(1, "pledge");
1011
1012 msgbuf_init(&procq);
1013 msgbuf_init(&rsyncq);
1014 msgbuf_init(&httpq);
1015 msgbuf_init(&rrdpq);
1016 procq.fd = proc;
1017 rsyncq.fd = rsync;
1018 httpq.fd = http;
1019 rrdpq.fd = rrdp;
1020
1021 /*
1022 * The main process drives the top-down scan to leaf ROAs using
1023 * data downloaded by the rsync process and parsed by the
1024 * parsing process.
1025 */
1026
1027 pfd[0].fd = proc;
1028 queues[0] = &procq;
1029 pfd[1].fd = rsync;
1030 queues[1] = &rsyncq;
1031 pfd[2].fd = http;
1032 queues[2] = &httpq;
1033 pfd[3].fd = rrdp;
1034 queues[3] = &rrdpq;
1035
1036 /*
1037 * Prime the process with our TAL file.
1038 * This will contain (hopefully) links to our manifest and we
1039 * can get the ball rolling.
1040 */
1041
1042 for (i = 0; i < talsz; i++)
1043 queue_add_tal(tals[i], i);
1044
1045 /* change working directory to the cache directory */
1046 if (fchdir(cachefd) == -1)
1047 err(1, "fchdir");
1048
1049 while (entity_queue > 0 && !killme) {
1050 int polltim;
1051
1052 for (i = 0; i < NPFD4; i++) {
1053 pfd[i].events = POLLIN0x0001;
1054 if (queues[i]->queued)
1055 pfd[i].events |= POLLOUT0x0004;
1056 }
1057
1058 polltim = repo_next_timeout(INFTIM(-1));
1059
1060 if ((c = poll(pfd, NPFD4, polltim)) == -1) {
1061 if (errno(*__errno()) == EINTR4)
1062 continue;
1063 err(1, "poll");
1064 }
1065
1066 for (i = 0; i < NPFD4; i++) {
1067 if (pfd[i].revents & (POLLERR0x0008|POLLNVAL0x0020)) {
1068 warnx("poll[%zu]: bad fd", i);
1069 hangup = 1;
1070 }
1071 if (pfd[i].revents & POLLHUP0x0010)
1072 hangup = 1;
1073 if (pfd[i].revents & POLLOUT0x0004) {
1074 switch (msgbuf_write(queues[i])) {
1075 case 0:
1076 warnx("write[%zu]: "
1077 "connection closed", i);
1078 hangup = 1;
1079 break;
1080 case -1:
1081 warn("write[%zu]", i);
1082 hangup = 1;
1083 break;
1084 }
1085 }
1086 }
1087 if (hangup)
1088 break;
1089
1090 repo_check_timeout();
1091
1092 /*
1093 * Check the rsync and http process.
1094 * This means that one of our modules has completed
1095 * downloading and we can flush the module requests into
1096 * the parser process.
1097 */
1098
1099 if ((pfd[1].revents & POLLIN0x0001)) {
1100 b = io_buf_read(rsync, &rsyncbuf);
1101 if (b != NULL((void*)0)) {
1102 unsigned int id;
1103 int ok;
1104
1105 io_read_buf(b, &id, sizeof(id));
1106 io_read_buf(b, &ok, sizeof(ok));
1107 rsync_finish(id, ok);
1108 ibuf_free(b);
1109 }
1110 }
1111
1112 if ((pfd[2].revents & POLLIN0x0001)) {
1113 b = io_buf_read(http, &httpbuf);
1114 if (b != NULL((void*)0)) {
1115 unsigned int id;
1116 enum http_result res;
1117 char *last_mod;
1118
1119 io_read_buf(b, &id, sizeof(id));
1120 io_read_buf(b, &res, sizeof(res));
1121 io_read_str(b, &last_mod);
1122 http_finish(id, res, last_mod);
1123 free(last_mod);
1124 ibuf_free(b);
1125 }
1126 }
1127
1128 /*
1129 * Handle RRDP requests here.
1130 */
1131 if ((pfd[3].revents & POLLIN0x0001)) {
1132 b = io_buf_read(rrdp, &rrdpbuf);
1133 if (b != NULL((void*)0)) {
1134 rrdp_process(b);
1135 ibuf_free(b);
1136 }
1137 }
1138
1139 /*
1140 * The parser has finished something for us.
1141 * Dequeue these one by one.
1142 */
1143
1144 if ((pfd[0].revents & POLLIN0x0001)) {
1145 b = io_buf_read(proc, &procbuf);
1146 if (b != NULL((void*)0)) {
1147 entity_process(b, &stats, &vrps, &brks);
1148 ibuf_free(b);
1149 }
1150 }
1151 }
1152
1153 signal(SIGALRM14, SIG_DFL(void (*)(int))0);
1154 if (killme) {
1155 syslog(LOG_CRIT2|LOG_DAEMON(3<<3),
1156 "excessive runtime (%d seconds), giving up", timeout);
1157 errx(1, "excessive runtime (%d seconds), giving up", timeout);
1158 }
1159
1160 /*
1161 * For clean-up, close the input for the parser and rsync
1162 * process.
1163 * This will cause them to exit, then we reap them.
1164 */
1165
1166 close(proc);
1167 close(rsync);
1168 close(http);
1169 close(rrdp);
1170
1171 rc = 0;
1172 for (;;) {
1173 pid = waitpid(WAIT_ANY(-1), &st, 0);
1174 if (pid == -1) {
1175 if (errno(*__errno()) == EINTR4)
1176 continue;
1177 if (errno(*__errno()) == ECHILD10)
1178 break;
1179 err(1, "wait");
1180 }
1181
1182 if (pid == procpid)
1183 name = "parser";
1184 else if (pid == rsyncpid)
1185 name = "rsync";
1186 else if (pid == httppid)
1187 name = "http";
1188 else if (pid == rrdppid)
1189 name = "rrdp";
1190 else
1191 name = "unknown";
1192
1193 if (WIFSIGNALED(st)(((st) & 0177) != 0177 && ((st) & 0177) != 0)) {
1194 warnx("%s terminated signal %d", name, WTERMSIG(st)(((st) & 0177)));
1195 rc = 1;
1196 } else if (!WIFEXITED(st)(((st) & 0177) == 0) || WEXITSTATUS(st)(int)(((unsigned)(st) >> 8) & 0xff) != 0) {
1197 warnx("%s process exited abnormally", name);
1198 rc = 1;
1199 }
1200 }
1201
1202 /* processing did not finish because of error */
1203 if (entity_queue != 0)
1204 errx(1, "not all files processed, giving up");
1205
1206 logx("all files parsed: generating output");
1207
1208 repo_cleanup(&fpt);
1209
1210 gettimeofday(&now_time, NULL((void*)0));
1211 timersub(&now_time, &start_time, &stats.elapsed_time)do { (&stats.elapsed_time)->tv_sec = (&now_time)->
tv_sec - (&start_time)->tv_sec; (&stats.elapsed_time
)->tv_usec = (&now_time)->tv_usec - (&start_time
)->tv_usec; if ((&stats.elapsed_time)->tv_usec <
0) { (&stats.elapsed_time)->tv_sec--; (&stats.elapsed_time
)->tv_usec += 1000000; } } while (0)
;
1212 if (getrusage(RUSAGE_SELF0, &ru) == 0) {
1213 stats.user_time = ru.ru_utime;
1214 stats.system_time = ru.ru_stime;
1215 }
1216 if (getrusage(RUSAGE_CHILDREN(-1), &ru) == 0) {
1217 timeradd(&stats.user_time, &ru.ru_utime, &stats.user_time)do { (&stats.user_time)->tv_sec = (&stats.user_time
)->tv_sec + (&ru.ru_utime)->tv_sec; (&stats.user_time
)->tv_usec = (&stats.user_time)->tv_usec + (&ru
.ru_utime)->tv_usec; if ((&stats.user_time)->tv_usec
>= 1000000) { (&stats.user_time)->tv_sec++; (&
stats.user_time)->tv_usec -= 1000000; } } while (0)
;
1218 timeradd(&stats.system_time, &ru.ru_stime, &stats.system_time)do { (&stats.system_time)->tv_sec = (&stats.system_time
)->tv_sec + (&ru.ru_stime)->tv_sec; (&stats.system_time
)->tv_usec = (&stats.system_time)->tv_usec + (&
ru.ru_stime)->tv_usec; if ((&stats.system_time)->tv_usec
>= 1000000) { (&stats.system_time)->tv_sec++; (&
stats.system_time)->tv_usec -= 1000000; } } while (0)
;
1219 }
1220
1221 /* change working directory to the output directory */
1222 if (fchdir(outdirfd) == -1)
1223 err(1, "fchdir output dir");
1224
1225 if (outputfiles(&vrps, &brks, &stats))
1226 rc = 1;
1227
1228 logx("Processing time %lld seconds "
1229 "(%lld seconds user, %lld seconds system)",
1230 (long long)stats.elapsed_time.tv_sec,
1231 (long long)stats.user_time.tv_sec,
1232 (long long)stats.system_time.tv_sec);
1233 logx("Route Origin Authorizations: %zu (%zu failed parse, %zu invalid)",
1234 stats.roas, stats.roas_fail, stats.roas_invalid);
1235 logx("BGPsec Router Certificates: %zu", stats.brks);
1236 logx("Certificates: %zu (%zu invalid)",
1237 stats.certs, stats.certs_fail);
1238 logx("Trust Anchor Locators: %zu (%zu invalid)",
1239 stats.tals, talsz - stats.tals);
1240 logx("Manifests: %zu (%zu failed parse, %zu stale)",
1241 stats.mfts, stats.mfts_fail, stats.mfts_stale);
1242 logx("Certificate revocation lists: %zu", stats.crls);
1243 logx("Ghostbuster records: %zu", stats.gbrs);
1244 logx("Repositories: %zu", stats.repos);
1245 logx("Cleanup: removed %zu files, %zu directories",
1246 stats.del_files, stats.del_dirs);
1247 logx("VRP Entries: %zu (%zu unique)", stats.vrps, stats.uniqs);
1248
1249 /* Memory cleanup. */
1250 repo_free();
1251
1252 return rc;
1253
1254usage:
1255 fprintf(stderr(&__sF[2]),
1256 "usage: rpki-client [-BcjnoRrVv] [-b sourceaddr] [-d cachedir]"
1257 " [-e rsync_prog]\n"
1258 " [-s timeout] [-T table] [-t tal]"
1259 " [outputdir]\n");
1260 return 1;
1261}