diff options
Diffstat (limited to 'bin/blocklistd.c')
| -rw-r--r-- | bin/blocklistd.c | 590 |
1 files changed, 0 insertions, 590 deletions
diff --git a/bin/blocklistd.c b/bin/blocklistd.c deleted file mode 100644 index 7c39f00106f4..000000000000 --- a/bin/blocklistd.c +++ /dev/null @@ -1,590 +0,0 @@ -/* $NetBSD: blocklistd.c,v 1.12 2025/10/25 18:43:51 christos Exp $ */ - -/*- - * Copyright (c) 2015 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Christos Zoulas. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_SYS_CDEFS_H -#include <sys/cdefs.h> -#endif -__RCSID("$NetBSD: blocklistd.c,v 1.12 2025/10/25 18:43:51 christos Exp $"); - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/queue.h> - -#ifdef HAVE_LIBUTIL_H -#include <libutil.h> -#endif -#ifdef HAVE_UTIL_H -#include <util.h> -#endif -#include <string.h> -#include <signal.h> -#include <netdb.h> -#include <stdio.h> -#include <stdbool.h> -#include <string.h> -#include <inttypes.h> -#include <syslog.h> -#include <ctype.h> -#include <limits.h> -#include <errno.h> -#include <poll.h> -#include <fcntl.h> -#include <err.h> -#include <stdlib.h> -#include <unistd.h> -#include <time.h> -#include <ifaddrs.h> -#include <netinet/in.h> - -#include "bl.h" -#include "internal.h" -#include "conf.h" -#include "run.h" -#include "state.h" -#include "support.h" - -static const char *configfile = _PATH_BLCONF; -static DB *state; -static const char *dbfile = _PATH_BLSTATE; -static sig_atomic_t readconf; -static sig_atomic_t done; -static int vflag; - -static void -sigusr1(int n __unused) -{ - debug++; -} - -static void -sigusr2(int n __unused) -{ - debug--; -} - -static void -sighup(int n __unused) -{ - readconf++; -} - -static void -sigdone(int n __unused) -{ - done++; -} - -static __dead void -usage(int c) -{ - if (c != '?') - warnx("Unknown option `%c'", (char)c); - fprintf(stderr, "Usage: %s [-vdfr] [-c <config>] [-R <rulename>] " - "[-P <sockpathsfile>] [-C <controlprog>] [-D <dbfile>] " - "[-s <sockpath>] [-t <timeout>]\n", getprogname()); - exit(EXIT_FAILURE); -} - -static int -getremoteaddress(bl_info_t *bi, struct sockaddr_storage *rss, socklen_t *rsl) -{ - *rsl = sizeof(*rss); - memset(rss, 0, *rsl); - - if (getpeername(bi->bi_fd, (void *)rss, rsl) != -1) - return 0; - - if (errno != ENOTCONN) { - (*lfun)(LOG_ERR, "getpeername failed (%m)"); - return -1; - } - - if (bi->bi_slen == 0) { - (*lfun)(LOG_ERR, "unconnected socket with no peer in message"); - return -1; - } - - switch (bi->bi_ss.ss_family) { - case AF_INET: - *rsl = sizeof(struct sockaddr_in); - break; - case AF_INET6: - *rsl = sizeof(struct sockaddr_in6); - break; - default: - (*lfun)(LOG_ERR, "bad client passed socket family %u", - (unsigned)bi->bi_ss.ss_family); - return -1; - } - - if (*rsl != bi->bi_slen) { - (*lfun)(LOG_ERR, "bad client passed socket length %u != %u", - (unsigned)*rsl, (unsigned)bi->bi_slen); - return -1; - } - - memcpy(rss, &bi->bi_ss, *rsl); - -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - if (*rsl != rss->ss_len) { - (*lfun)(LOG_ERR, - "bad client passed socket internal length %u != %u", - (unsigned)*rsl, (unsigned)rss->ss_len); - return -1; - } -#endif - return 0; -} - -static void -process(bl_t bl) -{ - struct sockaddr_storage rss; - socklen_t rsl; - char rbuf[BUFSIZ]; - bl_info_t *bi; - struct conf c; - struct dbinfo dbi; - struct timespec ts; - - memset(&dbi, 0, sizeof(dbi)); - memset(&c, 0, sizeof(c)); - if (clock_gettime(CLOCK_REALTIME, &ts) == -1) { - (*lfun)(LOG_ERR, "clock_gettime failed (%m)"); - return; - } - - if ((bi = bl_recv(bl)) == NULL) { - (*lfun)(LOG_ERR, "no message (%m)"); - return; - } - - if (getremoteaddress(bi, &rss, &rsl) == -1) - return; - - if (debug || bi->bi_msg[0]) { - sockaddr_snprintf(rbuf, sizeof(rbuf), "%a:%p", (void *)&rss); - (*lfun)(bi->bi_msg[0] ? LOG_INFO : LOG_DEBUG, - "processing type=%d fd=%d remote=%s msg=\"%s\" uid=%lu gid=%lu", - bi->bi_type, bi->bi_fd, rbuf, - bi->bi_msg, (unsigned long)bi->bi_uid, - (unsigned long)bi->bi_gid); - } - - if (conf_find(bi->bi_fd, bi->bi_uid, &rss, &c) == NULL) { - (*lfun)(LOG_DEBUG, "no rule matched"); - return; - } - - - if (state_get(state, &c, &dbi) == -1) - return; - - if (debug) { - char b1[128], b2[128]; - (*lfun)(LOG_DEBUG, "%s: initial db state for %s: count=%d/%d " - "last=%s now=%s", __func__, rbuf, dbi.count, c.c_nfail, - fmttime(b1, sizeof(b1), dbi.last), - fmttime(b2, sizeof(b2), ts.tv_sec)); - } - - switch (bi->bi_type) { - case BL_ABUSE: - /* - * If the application has signaled abusive behavior, set the - * number of fails to be two less than the configured limit. - * Fall through to the normal BL_ADD and BL_BADUSER processing, - * which will increment the failure count to the threshold, and - * block the abusive address. - */ - if (c.c_nfail != -1) - dbi.count = c.c_nfail - 2; - /*FALLTHROUGH*/ - case BL_ADD: - dbi.count++; /* will become += 2 */ - /*FALLTHROUGH*/ - case BL_BADUSER: - dbi.count++; - dbi.last = ts.tv_sec; - if (c.c_nfail != -1 && dbi.count >= c.c_nfail) { - /* - * No point in re-adding the rule. - * It might exist already due to latency in processing - * and removing the rule is the wrong thing to do as - * it allows a window to attack again. - */ - if (dbi.id[0] == '\0') { - int res = run_change("add", &c, - dbi.id, sizeof(dbi.id)); - if (res == -1) - goto out; - } - sockaddr_snprintf(rbuf, sizeof(rbuf), "%a", - (void *)&rss); - (*lfun)(LOG_INFO, - "blocked %s/%d:%d for %d seconds", - rbuf, c.c_lmask, c.c_port, c.c_duration); - } - break; - case BL_DELETE: - if (dbi.last == 0) - goto out; - dbi.count = 0; - dbi.last = 0; - break; - default: - (*lfun)(LOG_ERR, "unknown message %d", bi->bi_type); - } - state_put(state, &c, &dbi); - -out: - if (debug) { - char b1[128], b2[128]; - (*lfun)(LOG_DEBUG, "%s: final db state for %s: count=%d/%d " - "last=%s now=%s", __func__, rbuf, dbi.count, c.c_nfail, - fmttime(b1, sizeof(b1), dbi.last), - fmttime(b2, sizeof(b2), ts.tv_sec)); - } -} - -static void -update_interfaces(void) -{ - struct ifaddrs *oifas, *nifas; - - if (getifaddrs(&nifas) == -1) - return; - - oifas = ifas; - ifas = nifas; - - if (oifas) - freeifaddrs(oifas); -} - -static void -update(void) -{ - struct timespec ts; - struct conf c; - struct dbinfo dbi; - unsigned int f, n; - char buf[128]; - void *ss = &c.c_ss; - - if (clock_gettime(CLOCK_REALTIME, &ts) == -1) { - (*lfun)(LOG_ERR, "clock_gettime failed (%m)"); - return; - } - -again: - for (n = 0, f = 1; state_iterate(state, &c, &dbi, f) == 1; - f = 0, n++) - { - time_t when = c.c_duration + dbi.last; - if (debug > 1) { - char b1[64], b2[64]; - sockaddr_snprintf(buf, sizeof(buf), "%a:%p", ss); - (*lfun)(LOG_DEBUG, "%s:[%u] %s count=%d duration=%d " - "last=%s " "now=%s", __func__, n, buf, dbi.count, - c.c_duration, fmttime(b1, sizeof(b1), dbi.last), - fmttime(b2, sizeof(b2), ts.tv_sec)); - } - if (c.c_duration == -1 || when >= ts.tv_sec) - continue; - if (dbi.id[0]) { - run_change("rem", &c, dbi.id, 0); - sockaddr_snprintf(buf, sizeof(buf), "%a", ss); - (*lfun)(LOG_INFO, "released %s/%d:%d after %d seconds", - buf, c.c_lmask, c.c_port, c.c_duration); - } - if (state_del(state, &c) == 0) - goto again; - } -} - -static void -addfd(struct pollfd **pfdp, bl_t **blp, size_t *nfd, size_t *maxfd, - const char *path) -{ - bl_t bl = bl_create(true, path, vflag ? vdlog : vsyslog_r); - if (bl == NULL || !bl_isconnected(bl)) - exit(EXIT_FAILURE); - if (*nfd >= *maxfd) { - *maxfd += 10; - *blp = realloc(*blp, sizeof(**blp) * *maxfd); - if (*blp == NULL) - err(EXIT_FAILURE, "malloc"); - *pfdp = realloc(*pfdp, sizeof(**pfdp) * *maxfd); - if (*pfdp == NULL) - err(EXIT_FAILURE, "malloc"); - } - - (*pfdp)[*nfd].fd = bl_getfd(bl); - (*pfdp)[*nfd].events = POLLIN; - (*blp)[*nfd] = bl; - *nfd += 1; -} - -static void -uniqueadd(struct conf ***listp, size_t *nlist, size_t *mlist, struct conf *c) -{ - struct conf **list = *listp; - - if (c->c_name[0] == '\0') - return; - for (size_t i = 0; i < *nlist; i++) { - if (strcmp(list[i]->c_name, c->c_name) == 0) - return; - } - if (*nlist == *mlist) { - *mlist += 10; - void *p = realloc(*listp, *mlist * sizeof(*list)); - if (p == NULL) - err(EXIT_FAILURE, "Can't allocate for rule list"); - list = *listp = p; - } - list[(*nlist)++] = c; -} - -static void -rules_flush(void) -{ - struct conf **list; - size_t nlist, mlist; - - list = NULL; - mlist = nlist = 0; - for (size_t i = 0; i < rconf.cs_n; i++) - uniqueadd(&list, &nlist, &mlist, &rconf.cs_c[i]); - for (size_t i = 0; i < lconf.cs_n; i++) - uniqueadd(&list, &nlist, &mlist, &lconf.cs_c[i]); - - for (size_t i = 0; i < nlist; i++) - run_flush(list[i]); - free(list); -} - -static void -rules_restore(void) -{ - DB *db; - struct conf c; - struct dbinfo dbi; - unsigned int f; - - db = state_open(dbfile, O_RDONLY, 0); - if (db == NULL) { - (*lfun)(LOG_ERR, "Can't open `%s' to restore state (%m)", - dbfile); - return; - } - for (f = 1; state_iterate(db, &c, &dbi, f) == 1; f = 0) { - if (dbi.id[0] == '\0') - continue; - (void)run_change("add", &c, dbi.id, sizeof(dbi.id)); - state_put(state, &c, &dbi); - } - state_close(db); - state_sync(state); -} - -int -main(int argc, char *argv[]) -{ - int c, tout, flags, flush, restore, ret; - const char *spath, **blsock; - size_t nblsock, maxblsock; - - setprogname(argv[0]); - - spath = NULL; - blsock = NULL; - maxblsock = nblsock = 0; - flush = 0; - restore = 0; - tout = 0; - flags = O_RDWR|O_EXCL|O_CLOEXEC; - while ((c = getopt(argc, argv, "C:c:D:dfP:rR:s:t:v")) != -1) { - switch (c) { - case 'C': - controlprog = optarg; - break; - case 'c': - configfile = optarg; - break; - case 'D': - dbfile = optarg; - break; - case 'd': - debug++; - break; - case 'f': - flush++; - break; - case 'P': - spath = optarg; - break; - case 'R': - rulename = optarg; - break; - case 'r': - restore++; - break; - case 's': - if (nblsock >= maxblsock) { - maxblsock += 10; - void *p = realloc(blsock, - sizeof(*blsock) * maxblsock); - if (p == NULL) - err(EXIT_FAILURE, - "Can't allocate memory for %zu sockets", - maxblsock); - blsock = p; - } - blsock[nblsock++] = optarg; - break; - case 't': - tout = atoi(optarg) * 1000; - break; - case 'v': - vflag++; - break; - default: - usage(c); - } - } - - argc -= optind; - if (argc) - usage('?'); - - signal(SIGHUP, sighup); - signal(SIGINT, sigdone); - signal(SIGQUIT, sigdone); - signal(SIGTERM, sigdone); - signal(SIGUSR1, sigusr1); - signal(SIGUSR2, sigusr2); - - openlog(getprogname(), LOG_PID, LOG_DAEMON); - - if (debug) { - lfun = dlog; - if (tout == 0) - tout = 5000; - } else { - if (tout == 0) - tout = 15000; - } - - update_interfaces(); - conf_parse(configfile); - if (flush) { - rules_flush(); - if (!restore) - flags |= O_TRUNC; - } - - struct pollfd *pfd = NULL; - bl_t *bl = NULL; - size_t nfd = 0; - size_t maxfd = 0; - - for (size_t i = 0; i < nblsock; i++) - addfd(&pfd, &bl, &nfd, &maxfd, blsock[i]); - free(blsock); - - if (spath) { - FILE *fp = fopen(spath, "r"); - char *line; - if (fp == NULL) - err(EXIT_FAILURE, "Can't open `%s'", spath); - for (; (line = fparseln(fp, NULL, NULL, NULL, 0)) != NULL; - free(line)) - addfd(&pfd, &bl, &nfd, &maxfd, line); - fclose(fp); - } - if (nfd == 0) - addfd(&pfd, &bl, &nfd, &maxfd, _PATH_BLSOCK); - - state = state_open(dbfile, flags, 0600); - if (state == NULL) - state = state_open(dbfile, flags | O_CREAT, 0600); - if (state == NULL) - return EXIT_FAILURE; - - if (restore) { - if (!flush) - rules_flush(); - rules_restore(); - } - - if (!debug) { - if (daemon(0, 0) == -1) - err(EXIT_FAILURE, "daemon failed"); - if (pidfile(NULL) == -1) - err(EXIT_FAILURE, "Can't create pidfile"); - } - - for (size_t t = 0; !done; t++) { - if (readconf) { - readconf = 0; - conf_parse(configfile); - } - ret = poll(pfd, (nfds_t)nfd, tout); - if (debug && ret != 0) - (*lfun)(LOG_DEBUG, "received %d from poll()", ret); - switch (ret) { - case -1: - if (errno == EINTR) - continue; - (*lfun)(LOG_ERR, "poll (%m)"); - return EXIT_FAILURE; - case 0: - state_sync(state); - break; - default: - for (size_t i = 0; i < nfd; i++) - if (pfd[i].revents & POLLIN) - process(bl[i]); - } - if (t % 100 == 0) - state_sync(state); - if (t % 10000 == 0) - update_interfaces(); - update(); - } - state_close(state); - return 0; -} |
