From 5a2857b60bf0135a745069460ea8351096188344 Mon Sep 17 00:00:00 2001 From: job Date: Mon, 27 Jun 2022 10:18:27 +0000 Subject: [PATCH] Add skiplist option to steer clear of skiplisted hosts Blocking outbound connections towards RPKI publication servers based on IP or IPv6 address in external instrumentation like HTTP proxies or pf(4) rules is somewhat unwieldy. It might be easier for operators if we offer a mechanism that cuts at the CA cert SIA parsing step. OK claudio@ tb@ --- usr.sbin/rpki-client/extern.h | 14 ++++- usr.sbin/rpki-client/main.c | 88 +++++++++++++++++++++++++++--- usr.sbin/rpki-client/rpki-client.8 | 26 ++++++++- 3 files changed, 118 insertions(+), 10 deletions(-) diff --git a/usr.sbin/rpki-client/extern.h b/usr.sbin/rpki-client/extern.h index 81e76f898e6..fc79967a615 100644 --- a/usr.sbin/rpki-client/extern.h +++ b/usr.sbin/rpki-client/extern.h @@ -1,4 +1,4 @@ -/* $OpenBSD: extern.h,v 1.142 2022/06/10 10:36:43 tb Exp $ */ +/* $OpenBSD: extern.h,v 1.143 2022/06/27 10:18:27 job Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons * @@ -24,6 +24,15 @@ #include #include +/* + * Distrusted hosts (loaded from skipfile). + */ +struct skiplistentry { + LIST_ENTRY(skiplistentry) entry; + char *value; /* FQDN */ +}; +LIST_HEAD(skiplist, skiplistentry); + /* * Enumeration for ASN.1 explicit tags in RSC eContent */ @@ -430,6 +439,7 @@ struct stats { size_t extra_files; /* number of superfluous files */ size_t del_dirs; /* number of directories removed in cleanup */ size_t brks; /* number of BGPsec Router Key (BRK) certificates */ + size_t skiplistentries; /* number of skiplist entries */ struct timeval elapsed_time; struct timeval user_time; struct timeval system_time; @@ -689,6 +699,8 @@ int mkpathat(int, const char *); #define RPKI_PATH_OUT_DIR "/var/db/rpki-client" #define RPKI_PATH_BASE_DIR "/var/cache/rpki-client" +#define DEFAULT_SKIPLIST_FILE "/etc/rpki/skiplist" + /* Maximum number of TAL files we'll load. */ #define TALSZ_MAX 8 diff --git a/usr.sbin/rpki-client/main.c b/usr.sbin/rpki-client/main.c index 9f4ae29805a..4949d758329 100644 --- a/usr.sbin/rpki-client/main.c +++ b/usr.sbin/rpki-client/main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: main.c,v 1.207 2022/06/25 20:25:43 tb Exp $ */ +/* $OpenBSD: main.c,v 1.208 2022/06/27 10:18:27 job Exp $ */ /* * Copyright (c) 2021 Claudio Jeker * Copyright (c) 2019 Kristaps Dzonsons @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -66,6 +67,8 @@ int filemode; int rrdpon = 1; int repo_timeout; +struct skiplist skiplist = LIST_HEAD_INITIALIZER(skiplist); + struct stats stats; /* @@ -415,10 +418,22 @@ queue_add_from_tal(struct tal *tal) static void queue_add_from_cert(const struct cert *cert) { - struct repo *repo; - char *nfile, *npath; - const char *uri, *repouri, *file; - size_t repourisz; + struct repo *repo; + struct skiplistentry *sle; + char *nfile, *npath, *host; + const char *uri, *repouri, *file; + size_t repourisz; + + LIST_FOREACH(sle, &skiplist, entry) { + if (strncmp(cert->repo, "rsync://", 8) != 0) + errx(1, "unexpected protocol"); + host = cert->repo + 8; + + if (strncasecmp(host, sle->value, strcspn(host, "/")) == 0) { + warnx("skipping %s (listed in skiplist)", cert->repo); + return; + } + } repo = repo_lookup(cert->talid, cert->repo, rrdpon ? cert->notify : NULL); @@ -649,6 +664,57 @@ tal_load_default(void) return s; } +/* + * Load the list of FQDNs from the skiplist which are to be distrusted. + * Return 0 on success. + */ +static void +load_skiplist(const char *slf) +{ + struct skiplistentry *sle; + FILE *fp; + char *line = NULL; + size_t linesize = 0, s; + ssize_t linelen; + + if ((fp = fopen(slf, "r")) == NULL) { + if (strcmp(slf, DEFAULT_SKIPLIST_FILE) != 0) + errx(1, "failed to open skiplist %s", slf); + return; + } + + while ((linelen = getline(&line, &linesize, fp)) != -1) { + /* just eat comment lines or empty lines*/ + if (line[0] == '#' || line[0] == '\n') + continue; + + if (line[0] == ' ' || line[0] == '\t') + errx(1, "invalid entry in skiplist: %s", line); + + /* + * Ignore anything after comment sign, whitespaces, + * also chop off LF or CR. + */ + line[strcspn(line, " #\r\n\t")] = 0; + + for (s = 0; s < strlen(line); s++) + if (!isalnum((unsigned char)line[s]) && + !ispunct((unsigned char)line[s])) + errx(1, "invalid entry in skiplist: %s", line); + + if ((sle = malloc(sizeof(struct skiplistentry))) == NULL) + err(1, NULL); + if ((sle->value = strdup(line)) == NULL) + err(1, NULL); + + LIST_INSERT_HEAD(&skiplist, sle, entry); + stats.skiplistentries++; + } + + fclose(fp); + free(line); +} + static void check_fs_size(int fd, const char *cachedir) { @@ -724,6 +790,7 @@ main(int argc, char *argv[]) char *bind_addr = NULL; const char *cachedir = NULL, *outputdir = NULL; const char *errs, *name; + const char *skiplistfile = NULL; struct vrp_tree vrps = RB_INITIALIZER(&vrps); struct brk_tree brks = RB_INITIALIZER(&brks); struct rusage ru; @@ -746,12 +813,13 @@ main(int argc, char *argv[]) cachedir = RPKI_PATH_BASE_DIR; outputdir = RPKI_PATH_OUT_DIR; repo_timeout = timeout / 4; + skiplistfile = DEFAULT_SKIPLIST_FILE; if (pledge("stdio rpath wpath cpath inet fattr dns sendfd recvfd " "proc exec unveil", NULL) == -1) err(1, "pledge"); - while ((c = getopt(argc, argv, "b:Bcd:e:fjnorRs:t:T:vV")) != -1) + while ((c = getopt(argc, argv, "b:Bcd:e:fjnorRs:S:t:T:vV")) != -1) switch (c) { case 'b': bind_addr = optarg; @@ -796,6 +864,9 @@ main(int argc, char *argv[]) else repo_timeout = timeout / 4; break; + case 'S': + skiplistfile = optarg; + break; case 't': if (talsz >= TALSZ_MAX) err(1, "too many tal files specified"); @@ -963,6 +1034,8 @@ main(int argc, char *argv[]) pfd[3].fd = rrdp; queues[3] = &rrdpq; + load_skiplist(skiplistfile); + /* * Prime the process with our TAL files. * These will (hopefully) contain links to manifests and we @@ -1168,6 +1241,7 @@ main(int argc, char *argv[]) (long long)stats.elapsed_time.tv_sec, (long long)stats.user_time.tv_sec, (long long)stats.system_time.tv_sec); + printf("Skiplist entries: %zu\n", stats.skiplistentries); printf("Route Origin Authorizations: %zu (%zu failed parse, %zu invalid)\n", stats.roas, stats.roas_fail, stats.roas_invalid); printf("BGPsec Router Certificates: %zu\n", stats.brks); @@ -1193,7 +1267,7 @@ usage: fprintf(stderr, "usage: rpki-client [-BcjnoRrVv] [-b sourceaddr] [-d cachedir]" " [-e rsync_prog]\n" - " [-s timeout] [-T table] [-t tal]" + " [-S skiplist] [-s timeout] [-T table] [-t tal]" " [outputdir]\n" " rpki-client [-Vv] [-d cachedir] [-t tal] -f file ...\n"); return 1; diff --git a/usr.sbin/rpki-client/rpki-client.8 b/usr.sbin/rpki-client/rpki-client.8 index 4f738fb9025..d0c63f7f6dd 100644 --- a/usr.sbin/rpki-client/rpki-client.8 +++ b/usr.sbin/rpki-client/rpki-client.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: rpki-client.8,v 1.65 2022/05/31 18:42:26 tb Exp $ +.\" $OpenBSD: rpki-client.8,v 1.66 2022/06/27 10:18:27 job Exp $ .\" .\" Copyright (c) 2019 Kristaps Dzonsons .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: May 31 2022 $ +.Dd $Mdocdate: June 27 2022 $ .Dt RPKI-CLIENT 8 .Os .Sh NAME @@ -26,6 +26,7 @@ .Op Fl b Ar sourceaddr .Op Fl d Ar cachedir .Op Fl e Ar rsync_prog +.Op Fl S Ar skiplist .Op Fl s Ar timeout .Op Fl T Ar table .Op Fl t Ar tal @@ -151,6 +152,23 @@ If RRDP fails, RSYNC will be used. This is the default. Mutually exclusive with .Fl n . +.It Fl S Ar skiplist +Do not connect to any hosts listed in the +.Ar skiplist +file. +Entries in the +.Ar skiplist +are newline separated +.Em Fully Qualified Domain Names Pq FQDNs . +A +.Ql # +indicates the beginning of a comment; characters up to the end of the line are +not interpreted. +The skip filter is enforced during processing of the +.Em Subject Information Access Pq SIA +extension in CA certificates, thus applies to both RSYNC and RRDP connections. +By default load entries from +.Pa /etc/rpki/skiplist . .It Fl s Ar timeout Terminate after .Ar timeout @@ -215,6 +233,10 @@ URL of HTTP proxy to use. default TAL files used unless .Fl t Ar tal is specified. +.It Pa /etc/rpki/skiplist +default skiplist file, unless +.Fl S Ar skiplist +is specified. .It Pa /var/cache/rpki-client cached repository data. .It Pa /var/db/rpki-client/openbgpd -- 2.20.1