diff options
author | 2019-05-10 14:10:38 +0000 | |
---|---|---|
committer | 2019-05-10 14:10:38 +0000 | |
commit | 2d98827609ed0018da2c8b5885071d117d02abdc (patch) | |
tree | 4d39c96226b9cfe298f7f0111c57703f53590b4e | |
parent | When calculating how much payload ospf6d can put into DD and LSREQ packets (diff) | |
download | wireguard-openbsd-2d98827609ed0018da2c8b5885071d117d02abdc.tar.xz wireguard-openbsd-2d98827609ed0018da2c8b5885071d117d02abdc.zip |
Implement DNS block lists. If unwind is queried for a domain
in the block list it answers with rcode REFUSED.
-rw-r--r-- | sbin/unwind/captiveportal.c | 9 | ||||
-rw-r--r-- | sbin/unwind/frontend.c | 102 | ||||
-rw-r--r-- | sbin/unwind/parse.y | 21 | ||||
-rw-r--r-- | sbin/unwind/printconf.c | 5 | ||||
-rw-r--r-- | sbin/unwind/resolver.c | 9 | ||||
-rw-r--r-- | sbin/unwind/unwind.c | 30 | ||||
-rw-r--r-- | sbin/unwind/unwind.conf.5 | 10 | ||||
-rw-r--r-- | sbin/unwind/unwind.h | 5 |
8 files changed, 181 insertions, 10 deletions
diff --git a/sbin/unwind/captiveportal.c b/sbin/unwind/captiveportal.c index cbca055a055..bacc49877ea 100644 --- a/sbin/unwind/captiveportal.c +++ b/sbin/unwind/captiveportal.c @@ -1,4 +1,4 @@ -/* $OpenBSD: captiveportal.c,v 1.10 2019/03/15 16:48:37 florian Exp $ */ +/* $OpenBSD: captiveportal.c,v 1.11 2019/05/10 14:10:38 florian Exp $ */ /* * Copyright (c) 2018 Florian Obser <florian@openbsd.org> @@ -342,6 +342,13 @@ captiveportal_dispatch_main(int fd, short event, void *bula) strdup(imsg.data)) == NULL) fatal("%s: strdup", __func__); break; + case IMSG_RECONF_BLOCKLIST_FILE: + /* make sure this is a string */ + ((char *)imsg.data)[IMSG_DATA_SIZE(imsg) - 1] = '\0'; + if ((nconf->blocklist_file = strdup(imsg.data)) == + NULL) + fatal("%s: strdup", __func__); + break; case IMSG_RECONF_FORWARDER: if (IMSG_DATA_SIZE(imsg) != sizeof(struct uw_forwarder)) fatalx("%s: IMSG_RECONF_FORWARDER wrong length:" diff --git a/sbin/unwind/frontend.c b/sbin/unwind/frontend.c index 4ed4bf77c65..36ef983a6a8 100644 --- a/sbin/unwind/frontend.c +++ b/sbin/unwind/frontend.c @@ -1,4 +1,4 @@ -/* $OpenBSD: frontend.c,v 1.19 2019/05/08 21:59:13 florian Exp $ */ +/* $OpenBSD: frontend.c,v 1.20 2019/05/10 14:10:38 florian Exp $ */ /* * Copyright (c) 2018 Florian Obser <florian@openbsd.org> @@ -23,6 +23,7 @@ #include <sys/queue.h> #include <sys/socket.h> #include <sys/syslog.h> +#include <sys/tree.h> #include <sys/uio.h> #include <netinet/in.h> @@ -71,10 +72,16 @@ struct pending_query { uint64_t imsg_id; int fd; int bogus; + int rcode_override; }; TAILQ_HEAD(, pending_query) pending_queries; +struct bl_node { + RB_ENTRY(bl_node) entry; + char *domain; +}; + __dead void frontend_shutdown(void); void frontend_sig_handler(int, short, void *); void frontend_startup(void); @@ -92,6 +99,9 @@ void parse_dhcp_lease(int); void parse_trust_anchor(struct trust_anchor_head *, int); void send_trust_anchors(struct trust_anchor_head *); void write_trust_anchors(struct trust_anchor_head *, int); +void parse_blocklist(int); +int bl_cmp(struct bl_node *, struct bl_node *); +void free_bl(void); struct uw_conf *frontend_conf; struct imsgev *iev_main; @@ -103,6 +113,10 @@ int ta_fd = -1; static struct trust_anchor_head trust_anchors, new_trust_anchors; +RB_HEAD(bl_tree, bl_node) bl_head = RB_INITIALIZER(&bl_head); +RB_PROTOTYPE(bl_tree, bl_node, entry, bl_cmp) +RB_GENERATE(bl_tree, bl_node, entry, bl_cmp) + void frontend_sig_handler(int sig, short event, void *bula) { @@ -386,6 +400,13 @@ frontend_dispatch_main(int fd, short event, void *bula) strdup(imsg.data)) == NULL) fatal("%s: strdup", __func__); break; + case IMSG_RECONF_BLOCKLIST_FILE: + /* make sure this is a string */ + ((char *)imsg.data)[IMSG_DATA_SIZE(imsg) - 1] = '\0'; + if ((nconf->blocklist_file = strdup(imsg.data)) == + NULL) + fatal("%s: strdup", __func__); + break; case IMSG_RECONF_FORWARDER: if (IMSG_DATA_SIZE(imsg) != sizeof(struct uw_forwarder)) fatalx("%s: IMSG_RECONF_FORWARDER wrong length:" @@ -416,6 +437,8 @@ frontend_dispatch_main(int fd, short event, void *bula) fatalx("%s: IMSG_RECONF_END without " "IMSG_RECONF_CONF", __func__); merge_config(frontend_conf, nconf); + if (frontend_conf->blocklist_file == NULL) + free_bl(); nconf = NULL; break; case IMSG_UDP6SOCK: @@ -479,6 +502,12 @@ frontend_dispatch_main(int fd, short event, void *bula) if (!TAILQ_EMPTY(&trust_anchors)) send_trust_anchors(&trust_anchors); break; + case IMSG_BLFD: + if ((fd = imsg.fd) == -1) + fatalx("%s: expected to receive imsg block " + "list fd but didn't receive any", __func__); + parse_blocklist(fd); + break; default: log_debug("%s: error handling imsg %d", __func__, imsg.hdr.type); @@ -674,6 +703,7 @@ udp_receive(int fd, short events, void *arg) struct udp_ev *udpev = (struct udp_ev *)arg; struct pending_query *pq; struct query_imsg *query_imsg; + struct bl_node find; ssize_t len, rem_len, buf_len; uint16_t qdcount, ancount, nscount, arcount, t, c; uint8_t *queryp; @@ -736,6 +766,8 @@ udp_receive(int fd, short events, void *arg) return; } + pq->rcode_override = LDNS_RCODE_NOERROR; + if ((pq->query = malloc(len)) == NULL) { log_warn(NULL); free(pq); @@ -751,6 +783,14 @@ udp_receive(int fd, short events, void *arg) pq->from = udpev->from; pq->fd = fd; + find.domain = buf; + if (RB_FIND(bl_tree, &bl_head, &find) != NULL) { + pq->rcode_override = LDNS_RCODE_REFUSED; + TAILQ_INSERT_TAIL(&pending_queries, pq, entry); + send_answer(pq, NULL, 0); + return; + } + if ((query_imsg = calloc(1, sizeof(*query_imsg))) == NULL) { log_warn(NULL); return; @@ -791,7 +831,10 @@ send_answer(struct pending_query *pq, uint8_t *answer, ssize_t len) LDNS_QR_SET(answer); LDNS_RA_SET(answer); - LDNS_RCODE_SET(answer, LDNS_RCODE_SERVFAIL); + if (pq->rcode_override != LDNS_RCODE_NOERROR) + LDNS_RCODE_SET(answer, pq->rcode_override); + else + LDNS_RCODE_SET(answer, LDNS_RCODE_SERVFAIL); } else { if (pq->bogus) { if(LDNS_CD_WIRE(pq->query)) { @@ -1237,3 +1280,58 @@ out: ftruncate(fd, len); fsync(fd); } + +void +parse_blocklist(int fd) +{ + FILE *f; + struct bl_node *bl_node; + char *line = NULL; + size_t linesize = 0; + ssize_t linelen; + + if((f = fdopen(fd, "r")) == NULL) { + log_warn("cannot read block list"); + close(fd); + return; + } + + free_bl(); + + while ((linelen = getline(&line, &linesize, f)) != -1) { + if (line[linelen - 1] == '\n') { + if (linelen >= 2 && line[linelen - 2] != '.') + line[linelen - 1] = '.'; + else + line[linelen - 1] = '\0'; + } + + bl_node = malloc(sizeof *bl_node); + if (bl_node == NULL) + fatal("%s: malloc", __func__); + if ((bl_node->domain = strdup(line)) == NULL) + fatal("%s: strdup", __func__); + RB_INSERT(bl_tree, &bl_head, bl_node); + } + free(line); + if (ferror(f)) + log_warn("getline"); + fclose(f); +} + +int +bl_cmp(struct bl_node *e1, struct bl_node *e2) { + return (strcasecmp(e1->domain, e2->domain)); +} + +void +free_bl(void) +{ + struct bl_node *n, *nxt; + + for (n = RB_MIN(bl_tree, &bl_head); n != NULL; n = nxt) { + nxt = RB_NEXT(bl_tree, &bl_head, n); + RB_REMOVE(bl_tree, &bl_head, n); + free(n); + } +} diff --git a/sbin/unwind/parse.y b/sbin/unwind/parse.y index c7baa5d725e..6518975e47a 100644 --- a/sbin/unwind/parse.y +++ b/sbin/unwind/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.4 2019/04/03 03:48:45 florian Exp $ */ +/* $OpenBSD: parse.y,v 1.5 2019/05/10 14:10:38 florian Exp $ */ /* * Copyright (c) 2018 Florian Obser <florian@openbsd.org> @@ -102,6 +102,7 @@ typedef struct { %token STRICT YES NO INCLUDE ERROR %token FORWARDER DOT PORT CAPTIVE PORTAL URL EXPECTED RESPONSE %token STATUS AUTO AUTHENTICATION NAME PREFERENCE RECURSOR DHCP +%token BLOCK LIST %token <v.string> STRING %token <v.number> NUMBER @@ -118,6 +119,7 @@ grammar : /* empty */ | grammar uw_pref '\n' | grammar uw_forwarder '\n' | grammar captive_portal '\n' + | grammar block_list '\n' | grammar error '\n' { file->errors++; } ; @@ -180,6 +182,21 @@ optnl : '\n' optnl /* zero or more newlines */ | /*empty*/ ; +block_list : BLOCK LIST STRING { + if (conf->blocklist_file != NULL) { + yyerror("block list already " + "configured"); + free($3); + YYERROR; + } else { + conf->blocklist_file = strdup($3); + if (conf->blocklist_file == NULL) + err(1, "strdup"); + free($3); + } + } + ; + captive_portal : CAPTIVE PORTAL captive_portal_block ; captive_portal_block : '{' optnl captive_portal_opts_l '}' @@ -518,12 +535,14 @@ lookup(char *s) {"DoT", DOT}, {"authentication", AUTHENTICATION}, {"auto", AUTO}, + {"block", BLOCK}, {"captive", CAPTIVE}, {"dhcp", DHCP}, {"dot", DOT}, {"expected", EXPECTED}, {"forwarder", FORWARDER}, {"include", INCLUDE}, + {"list", LIST}, {"name", NAME}, {"no", NO}, {"port", PORT}, diff --git a/sbin/unwind/printconf.c b/sbin/unwind/printconf.c index b368c89cfaf..0c6fa5c5fe1 100644 --- a/sbin/unwind/printconf.c +++ b/sbin/unwind/printconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: printconf.c,v 1.8 2019/04/02 07:47:22 florian Exp $ */ +/* $OpenBSD: printconf.c,v 1.9 2019/05/10 14:10:38 florian Exp $ */ /* * Copyright (c) 2018 Florian Obser <florian@openbsd.org> @@ -109,4 +109,7 @@ print_config(struct uw_conf *conf) printf("\tauto %s\n", yesno(conf->captive_portal_auto)); printf("}\n"); } + + if (conf->blocklist_file != NULL) + printf("block list \"%s\"\n", conf->blocklist_file); } diff --git a/sbin/unwind/resolver.c b/sbin/unwind/resolver.c index 54446a08594..92c781c971b 100644 --- a/sbin/unwind/resolver.c +++ b/sbin/unwind/resolver.c @@ -1,4 +1,4 @@ -/* $OpenBSD: resolver.c,v 1.38 2019/05/06 17:31:25 florian Exp $ */ +/* $OpenBSD: resolver.c,v 1.39 2019/05/10 14:10:38 florian Exp $ */ /* * Copyright (c) 2018 Florian Obser <florian@openbsd.org> @@ -611,6 +611,13 @@ resolver_dispatch_main(int fd, short event, void *bula) strdup(imsg.data)) == NULL) fatal("%s: strdup", __func__); break; + case IMSG_RECONF_BLOCKLIST_FILE: + /* make sure this is a string */ + ((char *)imsg.data)[IMSG_DATA_SIZE(imsg) - 1] = '\0'; + if ((nconf->blocklist_file = strdup(imsg.data)) == + NULL) + fatal("%s: strdup", __func__); + break; case IMSG_RECONF_FORWARDER: if (IMSG_DATA_SIZE(imsg) != sizeof(struct uw_forwarder)) fatalx("%s: IMSG_RECONF_FORWARDER wrong length:" diff --git a/sbin/unwind/unwind.c b/sbin/unwind/unwind.c index 4baf8bd331e..f6d9a6abe44 100644 --- a/sbin/unwind/unwind.c +++ b/sbin/unwind/unwind.c @@ -1,4 +1,4 @@ -/* $OpenBSD: unwind.c,v 1.25 2019/05/03 13:02:00 florian Exp $ */ +/* $OpenBSD: unwind.c,v 1.26 2019/05/10 14:10:38 florian Exp $ */ /* * Copyright (c) 2018 Florian Obser <florian@openbsd.org> @@ -75,6 +75,7 @@ void open_dhcp_lease(int); void open_ports(void); void resolve_captive_portal(void); void resolve_captive_portal_done(struct asr_result *, void *); +void send_blocklist_fd(void); struct uw_conf *main_conf; struct imsgev *iev_frontend; @@ -307,6 +308,9 @@ main(int argc, char *argv[]) main_imsg_compose_frontend_fd(IMSG_ROUTESOCK, 0, frontend_routesock); main_imsg_send_config(main_conf); + if (main_conf->blocklist_file != NULL) + send_blocklist_fd(); + if (pledge("stdio inet dns rpath sendfd", NULL) == -1) fatal("pledge"); @@ -696,6 +700,9 @@ main_reload(void) merge_config(main_conf, xconf); + if (main_conf->blocklist_file != NULL) + send_blocklist_fd(); + return (0); } @@ -729,6 +736,13 @@ main_imsg_send_config(struct uw_conf *xconf) return (-1); } + if (xconf->blocklist_file != NULL) { + if (main_sendall(IMSG_RECONF_BLOCKLIST_FILE, + xconf->blocklist_file, strlen(xconf->blocklist_file) + 1) + == -1) + return (-1); + } + /* send static forwarders to children */ SIMPLEQ_FOREACH(uw_forwarder, &xconf->uw_forwarder_list, entry) { if (main_sendall(IMSG_RECONF_FORWARDER, uw_forwarder, @@ -801,6 +815,9 @@ merge_config(struct uw_conf *conf, struct uw_conf *xconf) conf->captive_portal_auto = xconf->captive_portal_auto; + free(conf->blocklist_file); + conf->blocklist_file = xconf->blocklist_file; + /* Add new forwarders. */ while ((uw_forwarder = SIMPLEQ_FIRST(&xconf->uw_forwarder_list)) != NULL) { @@ -995,3 +1012,14 @@ resolve_captive_portal_done(struct asr_result *ar, void *arg) freeaddrinfo(ar->ar_addrinfo); } + +void +send_blocklist_fd(void) +{ + int bl_fd; + + if ((bl_fd = open(main_conf->blocklist_file, O_RDONLY)) != -1) + main_imsg_compose_frontend_fd(IMSG_BLFD, 0, bl_fd); + else + log_warn("%s", main_conf->blocklist_file); +} diff --git a/sbin/unwind/unwind.conf.5 b/sbin/unwind/unwind.conf.5 index 44fd9feaff7..f87e0bab261 100644 --- a/sbin/unwind/unwind.conf.5 +++ b/sbin/unwind/unwind.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: unwind.conf.5,v 1.13 2019/04/03 03:48:45 florian Exp $ +.\" $OpenBSD: unwind.conf.5,v 1.14 2019/05/10 14:10:38 florian Exp $ .\" .\" Copyright (c) 2018 Florian Obser <florian@openbsd.org> .\" Copyright (c) 2005 Esben Norby <norby@openbsd.org> @@ -18,7 +18,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: April 3 2019 $ +.Dd $Mdocdate: May 10 2019 $ .Dt UNWIND.CONF 5 .Os .Sh NAME @@ -63,6 +63,12 @@ forwarder { $fwd1 $fwd2 } .Ed .Sh GLOBAL CONFIGURATION .Bl -tag -width Ds +.It Ic block list Ar file +A file containing domains to block, one per line. +If a domain from this list is queried +.Nm unwind +answers with a return code of +.Cm refused . .It Ic captive portal Brq ... .Nm unwind can detect when it is running behind a diff --git a/sbin/unwind/unwind.h b/sbin/unwind/unwind.h index f5ac2c99353..98e2df710c4 100644 --- a/sbin/unwind/unwind.h +++ b/sbin/unwind/unwind.h @@ -1,4 +1,4 @@ -/* $OpenBSD: unwind.h,v 1.14 2019/04/02 07:47:23 florian Exp $ */ +/* $OpenBSD: unwind.h,v 1.15 2019/05/10 14:10:38 florian Exp $ */ /* * Copyright (c) 2018 Florian Obser <florian@openbsd.org> @@ -86,6 +86,7 @@ enum imsg_type { IMSG_RECONF_CAPTIVE_PORTAL_HOST, IMSG_RECONF_CAPTIVE_PORTAL_PATH, IMSG_RECONF_CAPTIVE_PORTAL_EXPECTED_RESPONSE, + IMSG_RECONF_BLOCKLIST_FILE, IMSG_RECONF_FORWARDER, IMSG_RECONF_DOT_FORWARDER, IMSG_RECONF_END, @@ -120,6 +121,7 @@ enum imsg_type { IMSG_NEW_TAS_DONE, IMSG_RECHECK_RESOLVERS, IMSG_RESOLVE_CAPTIVE_PORTAL, + IMSG_BLFD, }; struct uw_forwarder { @@ -139,6 +141,7 @@ struct uw_conf { char *captive_portal_expected_response; int captive_portal_expected_status; int captive_portal_auto; + char *blocklist_file; }; struct query_imsg { |