diff --git a/contrib/blacklist/bin/blacklistd.c b/contrib/blacklist/bin/blacklistd.c index e880eb457df0..4aa845e46525 100644 --- a/contrib/blacklist/bin/blacklistd.c +++ b/contrib/blacklist/bin/blacklistd.c @@ -1,582 +1,581 @@ /* $NetBSD: blacklistd.c,v 1.38 2019/02/27 02:20:18 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 #include __RCSID("$NetBSD: blacklistd.c,v 1.38 2019/02/27 02:20:18 christos Exp $"); #include #include #include #ifdef HAVE_LIBUTIL_H #include #endif #ifdef HAVE_UTIL_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 ] [-R ] " "[-P ] [-C ] [-D ] " "[-s ] [-t ]\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)"); + (*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); + (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); + (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); + (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; if (clock_gettime(CLOCK_REALTIME, &ts) == -1) { - (*lfun)(LOG_ERR, "clock_gettime failed (%m)"); + (*lfun)(LOG_ERR, "clock_gettime failed (%m)"); return; } if ((bi = bl_recv(bl)) == NULL) { - (*lfun)(LOG_ERR, "no message (%m)"); + (*lfun)(LOG_ERR, "no message (%m)"); return; } if (getremoteaddress(bi, &rss, &rsl) == -1) goto out; if (debug) { sockaddr_snprintf(rbuf, sizeof(rbuf), "%a:%p", (void *)&rss); (*lfun)(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"); goto out; } if (state_get(state, &c, &dbi) == -1) goto out; 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 one less than the * configured limit. Fallthrough to the normal BL_ADD * processing, which will increment the failure count * to the threshhold, and block the abusive address. */ if (c.c_nfail != -1) dbi.count = c.c_nfail - 1; /*FALLTHROUGH*/ case BL_ADD: dbi.count++; dbi.last = ts.tv_sec; if (dbi.id[0]) { /* * We should not be getting this since the rule * should have blocked the address. A possible * explanation is that someone removed that rule, * and another would be that we got another attempt * before we added the rule. In anycase, we remove * and re-add the rule because we don't want to add * it twice, because then we'd lose track of it. */ (*lfun)(LOG_DEBUG, "rule exists %s", dbi.id); (void)run_change("rem", &c, dbi.id, 0); dbi.id[0] = '\0'; } if (c.c_nfail != -1 && dbi.count >= c.c_nfail) { 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; case BL_BADUSER: /* ignore for now */ break; default: - (*lfun)(LOG_ERR, "unknown message %d", bi->bi_type); + (*lfun)(LOG_ERR, "unknown message %d", bi->bi_type); } state_put(state, &c, &dbi); out: close(bi->bi_fd); 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)"); + (*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); } state_del(state, &c); 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); 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) { struct conf c; struct dbinfo dbi; unsigned int f; for (f = 1; state_iterate(state, &c, &dbi, f) == 1; f = 0) { if (dbi.id[0] == '\0') continue; (void)run_change("add", &c, dbi.id, sizeof(dbi.id)); } } 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(0); 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) (*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; } diff --git a/contrib/blacklist/bin/conf.c b/contrib/blacklist/bin/conf.c index f21dd8b05158..5fc614d794b1 100644 --- a/contrib/blacklist/bin/conf.c +++ b/contrib/blacklist/bin/conf.c @@ -1,1145 +1,1145 @@ /* $NetBSD: conf.c,v 1.24 2016/04/04 15:52:56 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 #include __RCSID("$NetBSD: conf.c,v 1.24 2016/04/04 15:52:56 christos Exp $"); #include #ifdef HAVE_LIBUTIL_H #include #endif #ifdef HAVE_UTIL_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bl.h" #include "internal.h" #include "support.h" #include "conf.h" struct sockaddr_if { uint8_t sif_len; sa_family_t sif_family; in_port_t sif_port; char sif_name[16]; }; #define SIF_NAME(a) \ ((const struct sockaddr_if *)(const void *)(a))->sif_name static int conf_is_interface(const char *); #define FSTAR -1 #define FEQUAL -2 static void advance(char **p) { char *ep = *p; while (*ep && !isspace((unsigned char)*ep)) ep++; while (*ep && isspace((unsigned char)*ep)) *ep++ = '\0'; *p = ep; } static int conf_getnum(const char *f, size_t l, bool local, void *rp, const char *name, const char *p) { int e; intmax_t im; int *r = rp; if (strcmp(p, "*") == 0) { *r = FSTAR; return 0; } if (strcmp(p, "=") == 0) { if (local) goto out; *r = FEQUAL; return 0; } im = strtoi(p, NULL, 0, 0, INT_MAX, &e); if (e == 0) { *r = (int)im; return 0; } if (f == NULL) return -1; (*lfun)(LOG_ERR, "%s: %s, %zu: Bad number for %s [%s]", __func__, f, l, name, p); return -1; out: (*lfun)(LOG_ERR, "%s: %s, %zu: `=' for %s not allowed in local config", __func__, f, l, name); return -1; } static int conf_getnfail(const char *f, size_t l, bool local, struct conf *c, const char *p) { return conf_getnum(f, l, local, &c->c_nfail, "nfail", p); } static int conf_getsecs(const char *f, size_t l, bool local, struct conf *c, const char *p) { int e; char *ep; intmax_t tot, im; tot = 0; if (strcmp(p, "*") == 0) { c->c_duration = FSTAR; return 0; } if (strcmp(p, "=") == 0) { if (local) goto out; c->c_duration = FEQUAL; return 0; } again: im = strtoi(p, &ep, 0, 0, INT_MAX, &e); if (e == ENOTSUP) { switch (*ep) { case 'd': im *= 24; /*FALLTHROUGH*/ case 'h': im *= 60; /*FALLTHROUGH*/ case 'm': im *= 60; /*FALLTHROUGH*/ case 's': e = 0; tot += im; if (ep[1] != '\0') { p = ep + 2; goto again; } break; } - } else + } else tot = im; - + if (e == 0) { c->c_duration = (int)tot; return 0; } if (f == NULL) return -1; (*lfun)(LOG_ERR, "%s: %s, %zu: Bad number [%s]", __func__, f, l, p); return -1; out: (*lfun)(LOG_ERR, "%s: %s, %zu: `=' duration not allowed in local" " config", __func__, f, l); return -1; } static int conf_getport(const char *f, size_t l, bool local, void *r, const char *p) { struct servent *sv; // XXX: Pass in the proto instead if ((sv = getservbyname(p, "tcp")) != NULL) { *(int *)r = ntohs(sv->s_port); return 0; } if ((sv = getservbyname(p, "udp")) != NULL) { *(int *)r = ntohs(sv->s_port); return 0; } return conf_getnum(f, l, local, r, "service", p); } static int conf_getmask(const char *f, size_t l, bool local, const char **p, int *mask) { char *d; - const char *s = *p; + const char *s = *p; if ((d = strchr(s, ':')) != NULL) { *d++ = '\0'; *p = d; } if ((d = strchr(s, '/')) == NULL) { *mask = FSTAR; return 0; } *d++ = '\0'; return conf_getnum(f, l, local, mask, "mask", d); } static int conf_gethostport(const char *f, size_t l, bool local, struct conf *c, const char *p) { char *d; // XXX: Ok to write to string. in_port_t *port = NULL; const char *pstr; if (strcmp(p, "*") == 0) { c->c_port = FSTAR; c->c_lmask = FSTAR; return 0; } if ((d = strchr(p, ']')) != NULL) { *d++ = '\0'; pstr = d; p++; } else pstr = p; if (conf_getmask(f, l, local, &pstr, &c->c_lmask) == -1) goto out; if (d) { struct sockaddr_in6 *sin6 = (void *)&c->c_ss; if (debug) (*lfun)(LOG_DEBUG, "%s: host6 %s", __func__, p); if (strcmp(p, "*") != 0) { if (inet_pton(AF_INET6, p, &sin6->sin6_addr) == -1) goto out; sin6->sin6_family = AF_INET6; #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN sin6->sin6_len = sizeof(*sin6); #endif port = &sin6->sin6_port; - } + } } else if (pstr != p || strchr(p, '.') || conf_is_interface(p)) { if (pstr == p) pstr = "*"; struct sockaddr_in *sin = (void *)&c->c_ss; struct sockaddr_if *sif = (void *)&c->c_ss; if (debug) (*lfun)(LOG_DEBUG, "%s: host4 %s", __func__, p); if (strcmp(p, "*") != 0) { if (conf_is_interface(p)) { if (!local) goto out2; if (debug) (*lfun)(LOG_DEBUG, "%s: interface %s", __func__, p); if (c->c_lmask != FSTAR) goto out1; sif->sif_family = AF_MAX; strlcpy(sif->sif_name, p, sizeof(sif->sif_name)); #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN sif->sif_len = sizeof(*sif); #endif port = &sif->sif_port; } else if (inet_pton(AF_INET, p, &sin->sin_addr) != -1) { sin->sin_family = AF_INET; #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN sin->sin_len = sizeof(*sin); #endif port = &sin->sin_port; } else goto out; } } if (conf_getport(f, l, local, &c->c_port, pstr) == -1) return -1; if (port && c->c_port != FSTAR && c->c_port != FEQUAL) *port = htons((in_port_t)c->c_port); return 0; out: (*lfun)(LOG_ERR, "%s: %s, %zu: Bad address [%s]", __func__, f, l, pstr); return -1; out1: (*lfun)(LOG_ERR, "%s: %s, %zu: Can't specify mask %d with " "interface [%s]", __func__, f, l, c->c_lmask, p); return -1; out2: (*lfun)(LOG_ERR, "%s: %s, %zu: Interface spec does not make sense " "with remote config [%s]", __func__, f, l, p); return -1; } static int conf_getproto(const char *f, size_t l, bool local __unused, struct conf *c, const char *p) { if (strcmp(p, "stream") == 0) { c->c_proto = IPPROTO_TCP; return 0; } if (strcmp(p, "dgram") == 0) { c->c_proto = IPPROTO_UDP; return 0; } return conf_getnum(f, l, local, &c->c_proto, "protocol", p); } static int conf_getfamily(const char *f, size_t l, bool local __unused, struct conf *c, const char *p) { if (strncmp(p, "tcp", 3) == 0 || strncmp(p, "udp", 3) == 0) { c->c_family = p[3] == '6' ? AF_INET6 : AF_INET; return 0; } return conf_getnum(f, l, local, &c->c_family, "family", p); } static int conf_getuid(const char *f, size_t l, bool local __unused, struct conf *c, const char *p) { struct passwd *pw; if ((pw = getpwnam(p)) != NULL) { c->c_uid = (int)pw->pw_uid; return 0; } return conf_getnum(f, l, local, &c->c_uid, "user", p); } static int conf_getname(const char *f, size_t l, bool local, struct conf *c, const char *p) { if (conf_getmask(f, l, local, &p, &c->c_rmask) == -1) return -1; - + if (strcmp(p, "*") == 0) { strlcpy(c->c_name, rulename, CONFNAMESZ); return 0; } if (strcmp(p, "=") == 0) { if (local) goto out; c->c_name[0] = '\0'; return 0; } snprintf(c->c_name, CONFNAMESZ, "%s%s", *p == '-' ? rulename : "", p); return 0; out: (*lfun)(LOG_ERR, "%s: %s, %zu: `=' name not allowed in local" " config", __func__, f, l); return -1; } static int getvalue(const char *f, size_t l, bool local, void *r, char **p, int (*fun)(const char *, size_t, bool, struct conf *, const char *)) { char *ep = *p; advance(p); return (*fun)(f, l, local, r, ep); } static int conf_parseline(const char *f, size_t l, char *p, struct conf *c, bool local) { int e; while (*p && isspace((unsigned char)*p)) p++; memset(c, 0, sizeof(*c)); e = getvalue(f, l, local, c, &p, conf_gethostport); if (e) return -1; e = getvalue(f, l, local, c, &p, conf_getproto); if (e) return -1; e = getvalue(f, l, local, c, &p, conf_getfamily); if (e) return -1; e = getvalue(f, l, local, c, &p, conf_getuid); if (e) return -1; e = getvalue(f, l, local, c, &p, conf_getname); if (e) return -1; e = getvalue(f, l, local, c, &p, conf_getnfail); if (e) return -1; e = getvalue(f, l, local, c, &p, conf_getsecs); if (e) return -1; return 0; } static int conf_sort(const void *v1, const void *v2) { const struct conf *c1 = v1; const struct conf *c2 = v2; #define CMP(a, b, f) \ if ((a)->f > (b)->f) return -1; \ else if ((a)->f < (b)->f) return 1 CMP(c1, c2, c_ss.ss_family); CMP(c1, c2, c_lmask); CMP(c1, c2, c_port); CMP(c1, c2, c_proto); CMP(c1, c2, c_family); CMP(c1, c2, c_rmask); CMP(c1, c2, c_uid); #undef CMP return 0; } static int conf_is_interface(const char *name) { const struct ifaddrs *ifa; for (ifa = ifas; ifa; ifa = ifa->ifa_next) if (strcmp(ifa->ifa_name, name) == 0) return 1; return 0; } #define MASK(m) ((uint32_t)~((1 << (32 - (m))) - 1)) static int conf_amask_eq(const void *v1, const void *v2, size_t len, int mask) { const uint32_t *a1 = v1; const uint32_t *a2 = v2; uint32_t m; int omask = mask; len >>= 2; switch (mask) { case FSTAR: if (memcmp(v1, v2, len) == 0) return 1; goto out; case FEQUAL: - + (*lfun)(LOG_CRIT, "%s: Internal error: bad mask %d", __func__, mask); abort(); default: break; } for (size_t i = 0; i < len; i++) { if (mask > 32) { m = htonl((uint32_t)~0); mask -= 32; } else if (mask) { m = htonl(MASK(mask)); mask = 0; } else return 1; if ((a1[i] & m) != (a2[i] & m)) goto out; } return 1; out: if (debug > 1) { char b1[256], b2[256]; len <<= 2; blhexdump(b1, sizeof(b1), "a1", v1, len); blhexdump(b2, sizeof(b2), "a2", v2, len); (*lfun)(LOG_DEBUG, "%s: %s != %s [0x%x]", __func__, b1, b2, omask); } return 0; } /* * Apply the mask to the given address */ static void conf_apply_mask(void *v, size_t len, int mask) { uint32_t *a = v; uint32_t m; switch (mask) { case FSTAR: return; case FEQUAL: (*lfun)(LOG_CRIT, "%s: Internal error: bad mask %d", __func__, mask); abort(); default: break; } len >>= 2; for (size_t i = 0; i < len; i++) { if (mask > 32) { m = htonl((uint32_t)~0); mask -= 32; } else if (mask) { m = htonl(MASK(mask)); mask = 0; } else m = 0; a[i] &= m; } } /* * apply the mask and the port to the address given */ static void conf_addr_set(struct conf *c, const struct sockaddr_storage *ss) { struct sockaddr_in *sin; struct sockaddr_in6 *sin6; in_port_t *port; void *addr; size_t alen; c->c_lmask = c->c_rmask; c->c_ss = *ss; if (c->c_ss.ss_family != c->c_family) { (*lfun)(LOG_CRIT, "%s: Internal error: mismatched family " "%u != %u", __func__, c->c_ss.ss_family, c->c_family); abort(); } switch (c->c_ss.ss_family) { case AF_INET: sin = (void *)&c->c_ss; port = &sin->sin_port; addr = &sin->sin_addr; alen = sizeof(sin->sin_addr); break; case AF_INET6: sin6 = (void *)&c->c_ss; port = &sin6->sin6_port; addr = &sin6->sin6_addr; alen = sizeof(sin6->sin6_addr); break; default: (*lfun)(LOG_CRIT, "%s: Internal error: bad family %u", __func__, c->c_ss.ss_family); abort(); } *port = htons((in_port_t)c->c_port); conf_apply_mask(addr, alen, c->c_lmask); if (c->c_lmask == FSTAR) c->c_lmask = (int)(alen * 8); if (debug) { char buf[128]; sockaddr_snprintf(buf, sizeof(buf), "%a:%p", (void *)&c->c_ss); (*lfun)(LOG_DEBUG, "Applied address %s", buf); } } /* * Compared two addresses for equality applying the mask */ static int conf_inet_eq(const void *v1, const void *v2, int mask) { const struct sockaddr *sa1 = v1; const struct sockaddr *sa2 = v2; size_t size; if (sa1->sa_family != sa2->sa_family) return 0; switch (sa1->sa_family) { case AF_INET: { const struct sockaddr_in *s1 = v1; const struct sockaddr_in *s2 = v2; size = sizeof(s1->sin_addr); v1 = &s1->sin_addr; v2 = &s2->sin_addr; break; } case AF_INET6: { const struct sockaddr_in6 *s1 = v1; const struct sockaddr_in6 *s2 = v2; size = sizeof(s1->sin6_addr); v1 = &s1->sin6_addr; v2 = &s2->sin6_addr; break; } default: (*lfun)(LOG_CRIT, "%s: Internal error: bad family %u", __func__, sa1->sa_family); abort(); } return conf_amask_eq(v1, v2, size, mask); } static int conf_addr_in_interface(const struct sockaddr_storage *s1, const struct sockaddr_storage *s2, int mask) { const char *name = SIF_NAME(s2); const struct ifaddrs *ifa; for (ifa = ifas; ifa; ifa = ifa->ifa_next) { if ((ifa->ifa_flags & IFF_UP) == 0) continue; if (strcmp(ifa->ifa_name, name) != 0) continue; if (s1->ss_family != ifa->ifa_addr->sa_family) continue; bool eq; switch (s1->ss_family) { case AF_INET: case AF_INET6: eq = conf_inet_eq(ifa->ifa_addr, s1, mask); break; default: (*lfun)(LOG_ERR, "Bad family %u", s1->ss_family); continue; } if (eq) return 1; } return 0; } static int conf_addr_eq(const struct sockaddr_storage *s1, const struct sockaddr_storage *s2, int mask) { switch (s2->ss_family) { case 0: return 1; case AF_MAX: return conf_addr_in_interface(s1, s2, mask); case AF_INET: case AF_INET6: return conf_inet_eq(s1, s2, mask); default: (*lfun)(LOG_CRIT, "%s: Internal error: bad family %u", __func__, s1->ss_family); abort(); } } static int conf_eq(const struct conf *c1, const struct conf *c2) { - + if (!conf_addr_eq(&c1->c_ss, &c2->c_ss, c2->c_lmask)) return 0; #define CMP(a, b, f) \ if ((a)->f != (b)->f && (b)->f != FSTAR && (b)->f != FEQUAL) { \ if (debug > 1) \ (*lfun)(LOG_DEBUG, "%s: %s fail %d != %d", __func__, \ __STRING(f), (a)->f, (b)->f); \ return 0; \ } CMP(c1, c2, c_port); CMP(c1, c2, c_proto); CMP(c1, c2, c_family); CMP(c1, c2, c_uid); #undef CMP return 1; } static const char * conf_num(char *b, size_t l, int n) { switch (n) { case FSTAR: return "*"; case FEQUAL: return "="; default: snprintf(b, l, "%d", n); return b; } } static const char * fmtname(const char *n) { size_t l = strlen(rulename); if (l == 0) return "*"; if (strncmp(n, rulename, l) == 0) { if (n[l] != '\0') return n + l; else return "*"; } else if (!*n) return "="; else return n; } static void fmtport(char *b, size_t l, int port) { char buf[128]; if (port == FSTAR) return; - if (b[0] == '\0' || strcmp(b, "*") == 0) + if (b[0] == '\0' || strcmp(b, "*") == 0) snprintf(b, l, "%d", port); else { snprintf(buf, sizeof(buf), ":%d", port); strlcat(b, buf, l); } } static const char * fmtmask(char *b, size_t l, int fam, int mask) { char buf[128]; switch (mask) { case FSTAR: return ""; case FEQUAL: if (strcmp(b, "=") == 0) return ""; else { strlcat(b, "/=", l); return b; } default: break; } switch (fam) { case AF_INET: if (mask == 32) return ""; break; case AF_INET6: if (mask == 128) return ""; break; default: break; } snprintf(buf, sizeof(buf), "/%d", mask); strlcat(b, buf, l); return b; } static const char * conf_namemask(char *b, size_t l, const struct conf *c) { strlcpy(b, fmtname(c->c_name), l); fmtmask(b, l, c->c_family, c->c_rmask); return b; } const char * conf_print(char *buf, size_t len, const char *pref, const char *delim, const struct conf *c) { char ha[128], hb[32], b[5][64]; int sp; #define N(n, v) conf_num(b[n], sizeof(b[n]), (v)) switch (c->c_ss.ss_family) { case 0: snprintf(ha, sizeof(ha), "*"); break; case AF_MAX: snprintf(ha, sizeof(ha), "%s", SIF_NAME(&c->c_ss)); break; default: sockaddr_snprintf(ha, sizeof(ha), "%a", (const void *)&c->c_ss); break; } fmtmask(ha, sizeof(ha), c->c_family, c->c_lmask); fmtport(ha, sizeof(ha), c->c_port); - + sp = *delim == '\t' ? 20 : -1; hb[0] = '\0'; if (*delim) snprintf(buf, len, "%s%*.*s%s%s%s" "%s%s%s%s" "%s%s" "%s%s%s", pref, sp, sp, ha, delim, N(0, c->c_proto), delim, N(1, c->c_family), delim, N(2, c->c_uid), delim, conf_namemask(hb, sizeof(hb), c), delim, N(3, c->c_nfail), delim, N(4, c->c_duration)); else snprintf(buf, len, "%starget:%s, proto:%s, family:%s, " "uid:%s, name:%s, nfail:%s, duration:%s", pref, ha, N(0, c->c_proto), N(1, c->c_family), N(2, c->c_uid), conf_namemask(hb, sizeof(hb), c), N(3, c->c_nfail), N(4, c->c_duration)); return buf; } /* * Apply the local config match to the result */ static void conf_apply(struct conf *c, const struct conf *sc) { char buf[BUFSIZ]; if (debug) { (*lfun)(LOG_DEBUG, "%s: %s", __func__, conf_print(buf, sizeof(buf), "merge:\t", "", sc)); (*lfun)(LOG_DEBUG, "%s: %s", __func__, conf_print(buf, sizeof(buf), "to:\t", "", c)); } memcpy(c->c_name, sc->c_name, CONFNAMESZ); c->c_uid = sc->c_uid; c->c_rmask = sc->c_rmask; c->c_nfail = sc->c_nfail; c->c_duration = sc->c_duration; if (debug) (*lfun)(LOG_DEBUG, "%s: %s", __func__, conf_print(buf, sizeof(buf), "result:\t", "", c)); } /* * Merge a remote configuration to the result */ static void conf_merge(struct conf *c, const struct conf *sc) { char buf[BUFSIZ]; if (debug) { (*lfun)(LOG_DEBUG, "%s: %s", __func__, conf_print(buf, sizeof(buf), "merge:\t", "", sc)); (*lfun)(LOG_DEBUG, "%s: %s", __func__, conf_print(buf, sizeof(buf), "to:\t", "", c)); } - + if (sc->c_name[0]) memcpy(c->c_name, sc->c_name, CONFNAMESZ); if (sc->c_uid != FEQUAL) c->c_uid = sc->c_uid; if (sc->c_rmask != FEQUAL) c->c_lmask = c->c_rmask = sc->c_rmask; if (sc->c_nfail != FEQUAL) c->c_nfail = sc->c_nfail; if (sc->c_duration != FEQUAL) c->c_duration = sc->c_duration; if (debug) (*lfun)(LOG_DEBUG, "%s: %s", __func__, conf_print(buf, sizeof(buf), "result:\t", "", c)); } static void confset_init(struct confset *cs) { cs->cs_c = NULL; cs->cs_n = 0; cs->cs_m = 0; } static int confset_grow(struct confset *cs) { void *tc; cs->cs_m += 10; tc = realloc(cs->cs_c, cs->cs_m * sizeof(*cs->cs_c)); if (tc == NULL) { (*lfun)(LOG_ERR, "%s: Can't grow confset (%m)", __func__); return -1; } cs->cs_c = tc; return 0; } static struct conf * confset_get(struct confset *cs) { return &cs->cs_c[cs->cs_n]; } static bool confset_full(const struct confset *cs) { return cs->cs_n == cs->cs_m; } static void confset_sort(struct confset *cs) { qsort(cs->cs_c, cs->cs_n, sizeof(*cs->cs_c), conf_sort); } static void confset_add(struct confset *cs) { cs->cs_n++; } static void confset_free(struct confset *cs) { free(cs->cs_c); confset_init(cs); } static void confset_replace(struct confset *dc, struct confset *sc) { struct confset tc; tc = *dc; *dc = *sc; confset_init(sc); confset_free(&tc); } static void confset_list(const struct confset *cs, const char *msg, const char *where) { char buf[BUFSIZ]; (*lfun)(LOG_DEBUG, "[%s]", msg); (*lfun)(LOG_DEBUG, "%20.20s\ttype\tproto\towner\tname\tnfail\tduration", where); for (size_t i = 0; i < cs->cs_n; i++) (*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf), "", "\t", &cs->cs_c[i])); } /* * Match a configuration against the given list and apply the function * to it, returning the matched entry number. */ static size_t confset_match(const struct confset *cs, struct conf *c, void (*fun)(struct conf *, const struct conf *)) { char buf[BUFSIZ]; size_t i; for (i = 0; i < cs->cs_n; i++) { if (debug) (*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf), "check:\t", "", &cs->cs_c[i])); if (conf_eq(c, &cs->cs_c[i])) { if (debug) (*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf), "found:\t", "", &cs->cs_c[i])); (*fun)(c, &cs->cs_c[i]); break; } } return i; } const struct conf * conf_find(int fd, uid_t uid, const struct sockaddr_storage *rss, struct conf *cr) { int proto; socklen_t slen; struct sockaddr_storage lss; size_t i; char buf[BUFSIZ]; memset(cr, 0, sizeof(*cr)); slen = sizeof(lss); memset(&lss, 0, slen); if (getsockname(fd, (void *)&lss, &slen) == -1) { - (*lfun)(LOG_ERR, "getsockname failed (%m)"); + (*lfun)(LOG_ERR, "getsockname failed (%m)"); return NULL; } slen = sizeof(proto); if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &proto, &slen) == -1) { - (*lfun)(LOG_ERR, "getsockopt failed (%m)"); + (*lfun)(LOG_ERR, "getsockopt failed (%m)"); return NULL; } if (debug) { sockaddr_snprintf(buf, sizeof(buf), "%a:%p", (void *)&lss); (*lfun)(LOG_DEBUG, "listening socket: %s", buf); } switch (proto) { case SOCK_STREAM: cr->c_proto = IPPROTO_TCP; break; case SOCK_DGRAM: cr->c_proto = IPPROTO_UDP; break; default: - (*lfun)(LOG_ERR, "unsupported protocol %d", proto); + (*lfun)(LOG_ERR, "unsupported protocol %d", proto); return NULL; } switch (lss.ss_family) { case AF_INET: cr->c_port = ntohs(((struct sockaddr_in *)&lss)->sin_port); break; case AF_INET6: cr->c_port = ntohs(((struct sockaddr_in6 *)&lss)->sin6_port); break; default: - (*lfun)(LOG_ERR, "unsupported family %d", lss.ss_family); + (*lfun)(LOG_ERR, "unsupported family %d", lss.ss_family); return NULL; } cr->c_ss = lss; cr->c_lmask = FSTAR; cr->c_uid = (int)uid; cr->c_family = lss.ss_family; cr->c_name[0] = '\0'; cr->c_rmask = FSTAR; cr->c_nfail = FSTAR; cr->c_duration = FSTAR; if (debug) (*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf), "look:\t", "", cr)); /* match the local config */ i = confset_match(&lconf, cr, conf_apply); if (i == lconf.cs_n) { if (debug) (*lfun)(LOG_DEBUG, "not found"); return NULL; } conf_addr_set(cr, rss); /* match the remote config */ confset_match(&rconf, cr, conf_merge); /* to apply the mask */ conf_addr_set(cr, &cr->c_ss); return cr; } void conf_parse(const char *f) { FILE *fp; char *line; size_t lineno, len; struct confset lc, rc, *cs; if ((fp = fopen(f, "r")) == NULL) { (*lfun)(LOG_ERR, "%s: Cannot open `%s' (%m)", __func__, f); return; } lineno = 1; confset_init(&rc); confset_init(&lc); cs = &lc; for (; (line = fparseln(fp, &len, &lineno, NULL, 0)) != NULL; free(line)) { if (!*line) continue; if (strcmp(line, "[local]") == 0) { cs = &lc; continue; } if (strcmp(line, "[remote]") == 0) { cs = &rc; continue; } if (confset_full(cs)) { if (confset_grow(cs) == -1) { confset_free(&lc); confset_free(&rc); fclose(fp); free(line); return; } } if (conf_parseline(f, lineno, line, confset_get(cs), cs == &lc) == -1) continue; confset_add(cs); } fclose(fp); confset_sort(&lc); confset_sort(&rc); - + confset_replace(&rconf, &rc); confset_replace(&lconf, &lc); if (debug) { confset_list(&lconf, "local", "target"); confset_list(&rconf, "remote", "source"); } } diff --git a/contrib/blacklist/bin/run.c b/contrib/blacklist/bin/run.c index 8499edd3d9c2..5588f0198c04 100644 --- a/contrib/blacklist/bin/run.c +++ b/contrib/blacklist/bin/run.c @@ -1,156 +1,156 @@ /* $NetBSD: run.c,v 1.14 2016/04/04 15:52:56 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 #include __RCSID("$NetBSD: run.c,v 1.14 2016/04/04 15:52:56 christos Exp $"); #include #ifdef HAVE_LIBUTIL_H #include #endif #ifdef HAVE_UTIL_H #include #endif #include #include #include #include #include #include #include #include #include "run.h" #include "conf.h" #include "internal.h" #include "support.h" extern char **environ; static char * run(const char *cmd, const char *name, ...) { const char *argv[20]; size_t i; va_list ap; FILE *fp; char buf[10240], *res; argv[0] = "control"; argv[1] = cmd; argv[2] = name; va_start(ap, name); for (i = 3; i < __arraycount(argv) && (argv[i] = va_arg(ap, char *)) != NULL; i++) continue; va_end(ap); - + if (debug) { size_t z; int r; r = snprintf(buf, sizeof(buf), "run %s [", controlprog); if (r == -1 || (z = (size_t)r) >= sizeof(buf)) z = sizeof(buf); for (i = 0; argv[i]; i++) { r = snprintf(buf + z, sizeof(buf) - z, "%s%s", argv[i], argv[i + 1] ? " " : ""); if (r == -1 || (z += (size_t)r) >= sizeof(buf)) z = sizeof(buf); } (*lfun)(LOG_DEBUG, "%s]", buf); } fp = popenve(controlprog, __UNCONST(argv), environ, "r"); if (fp == NULL) { (*lfun)(LOG_ERR, "popen %s failed (%m)", controlprog); return NULL; } if (fgets(buf, sizeof(buf), fp) != NULL) res = strdup(buf); else res = NULL; pclose(fp); if (debug) (*lfun)(LOG_DEBUG, "%s returns %s", cmd, res); return res; } void run_flush(const struct conf *c) { free(run("flush", c->c_name, NULL)); } int run_change(const char *how, const struct conf *c, char *id, size_t len) { const char *prname; char poname[64], adname[128], maskname[32], *rv; size_t off; switch (c->c_proto) { case -1: prname = ""; break; case IPPROTO_TCP: prname = "tcp"; break; case IPPROTO_UDP: prname = "udp"; break; default: (*lfun)(LOG_ERR, "%s: bad protocol %d", __func__, c->c_proto); return -1; } if (c->c_port != -1) snprintf(poname, sizeof(poname), "%d", c->c_port); else poname[0] = '\0'; snprintf(maskname, sizeof(maskname), "%d", c->c_lmask); sockaddr_snprintf(adname, sizeof(adname), "%a", (const void *)&c->c_ss); rv = run(how, c->c_name, prname, adname, maskname, poname, id, NULL); if (rv == NULL) return -1; if (len != 0) { rv[strcspn(rv, "\n")] = '\0'; off = strncmp(rv, "OK ", 3) == 0 ? 3 : 0; strlcpy(id, rv + off, len); } free(rv); return 0; } diff --git a/contrib/blacklist/bin/support.c b/contrib/blacklist/bin/support.c index 79a1c6ee93ac..d560d2303223 100644 --- a/contrib/blacklist/bin/support.c +++ b/contrib/blacklist/bin/support.c @@ -1,161 +1,161 @@ /* $NetBSD: support.c,v 1.9 2018/09/18 22:12:19 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 #include __RCSID("$NetBSD: support.c,v 1.9 2018/09/18 22:12:19 christos Exp $"); #include #include #include #include #include #include #include #include "support.h" static __attribute__((__format_arg__(3))) const char * -expandm(char *buf, size_t len, const char *fmt) +expandm(char *buf, size_t len, const char *fmt) { char *p; size_t r; if ((p = strstr(fmt, "%m")) == NULL) return fmt; r = (size_t)(p - fmt); if (r >= len) return fmt; strlcpy(buf, fmt, r + 1); strlcat(buf, strerror(errno), len); strlcat(buf, fmt + r + 2, len); return buf; } void vdlog(int level __unused, const char *fmt, va_list ap) { char buf[BUFSIZ]; // fprintf(stderr, "%s: ", getprogname()); vfprintf(stderr, expandm(buf, sizeof(buf), fmt), ap); fprintf(stderr, "\n"); } void dlog(int level, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vdlog(level, fmt, ap); va_end(ap); } const char * fmttime(char *b, size_t l, time_t t) { struct tm tm; if (localtime_r(&t, &tm) == NULL) snprintf(b, l, "*%jd*", (intmax_t)t); else strftime(b, l, "%Y/%m/%d %H:%M:%S", &tm); return b; } const char * fmtydhms(char *b, size_t l, time_t t) { time_t s, m, h, d, y; int z; size_t o; s = t % 60; t /= 60; m = t % 60; t /= 60; h = t % 24; t /= 24; d = t % 365; t /= 365; y = t; z = 0; o = 0; #define APPEND(a) \ if (a) { \ z = snprintf(b + o, l - o, "%jd%s", (intmax_t)a, __STRING(a)); \ if (z == -1) \ return b; \ o += (size_t)z; \ if (o >= l) \ return b; \ } APPEND(y) APPEND(d) APPEND(h) APPEND(m) APPEND(s) return b; } ssize_t blhexdump(char *buf, size_t len, const char *str, const void *b, size_t l) { size_t z, cz; int r; const unsigned char *p = b; const unsigned char *e = p + l; r = snprintf(buf, len, "%s: ", str); if (r == -1) return -1; if ((cz = z = (size_t)r) >= len) cz = len; while (p < e) { r = snprintf(buf + cz, len - cz, "%.2x", *p++); if (r == -1) return -1; if ((cz = (z += (size_t)r)) >= len) cz = len; } return (ssize_t)z; } diff --git a/contrib/blacklist/diff/proftpd.diff b/contrib/blacklist/diff/proftpd.diff index c811c9cf50bc..455b7cd60c64 100644 --- a/contrib/blacklist/diff/proftpd.diff +++ b/contrib/blacklist/diff/proftpd.diff @@ -1,124 +1,124 @@ --- Make.rules.in.orig 2015-05-27 20:25:54.000000000 -0400 +++ Make.rules.in 2016-01-25 21:48:47.000000000 -0500 @@ -110,3 +110,8 @@ - + FTPWHO_OBJS=ftpwho.o scoreboard.o misc.o BUILD_FTPWHO_OBJS=utils/ftpwho.o utils/scoreboard.o utils/misc.o + +CPPFLAGS+=-DHAVE_BLACKLIST +LIBS+=-lblacklist +OBJS+= pfilter.o +BUILD_OBJS+= src/pfilter.o --- /dev/null 2016-01-22 17:30:55.000000000 -0500 +++ include/pfilter.h 2016-01-22 16:18:33.000000000 -0500 @@ -0,0 +1,3 @@ + +void pfilter_notify(int); +void pfilter_init(void); --- modules/mod_auth.c.orig 2015-05-27 20:25:54.000000000 -0400 +++ modules/mod_auth.c 2016-01-22 16:21:06.000000000 -0500 @@ -30,6 +30,7 @@ #include "conf.h" #include "privs.h" +#include "pfilter.h" extern pid_t mpid; @@ -84,6 +85,8 @@ _("Login timeout (%d %s): closing control connection"), TimeoutLogin, TimeoutLogin != 1 ? "seconds" : "second"); + pfilter_notify(1); + /* It's possible that any listeners of this event might terminate the * session process themselves (e.g. mod_ban). So write out that the * TimeoutLogin has been exceeded to the log here, in addition to the @@ -913,6 +916,7 @@ pr_memscrub(pass, strlen(pass)); } + pfilter_notify(1); pr_log_auth(PR_LOG_NOTICE, "SECURITY VIOLATION: Root login attempted"); return 0; } @@ -1726,6 +1730,7 @@ return 1; auth_failure: + pfilter_notify(1); if (pass) pr_memscrub(pass, strlen(pass)); session.user = session.group = NULL; --- src/main.c.orig 2016-01-22 17:36:43.000000000 -0500 +++ src/main.c 2016-01-22 17:37:58.000000000 -0500 @@ -49,6 +49,7 @@ #endif #include "privs.h" +#include "pfilter.h" int (*cmd_auth_chk)(cmd_rec *); void (*cmd_handler)(server_rec *, conn_t *); @@ -1050,6 +1051,7 @@ pid_t pid; sigset_t sig_set; + pfilter_init(); if (!nofork) { /* A race condition exists on heavily loaded servers where the parent @@ -1169,7 +1171,8 @@ /* Reseed pseudo-randoms */ srand((unsigned int) (time(NULL) * getpid())); - +#else + pfilter_init(); #endif /* PR_DEVEL_NO_FORK */ /* Child is running here */ --- /dev/null 2016-01-22 17:30:55.000000000 -0500 +++ src/pfilter.c 2016-01-22 16:37:55.000000000 -0500 @@ -0,0 +1,41 @@ +#include "pfilter.h" +#include "conf.h" +#include "privs.h" +#ifdef HAVE_BLACKLIST +#include +#endif + +static struct blacklist *blstate; + +void +pfilter_init(void) +{ +#ifdef HAVE_BLACKLIST + if (blstate == NULL) + blstate = blacklist_open(); +#endif +} + +void +pfilter_notify(int a) +{ +#ifdef HAVE_BLACKLIST + conn_t *c = session.c; + int fd; + + if (c == NULL) + return; + if (c->rfd != -1) + fd = c->rfd; + else if (c->wfd != -1) + fd = c->wfd; + else + return; + + if (blstate == NULL) + pfilter_init(); + if (blstate == NULL) + return; + (void)blacklist_r(blstate, a, fd, "proftpd"); +#endif +} diff --git a/contrib/blacklist/lib/bl.c b/contrib/blacklist/lib/bl.c index ab2bd7c43ebe..a3aa4ef1f9f6 100644 --- a/contrib/blacklist/lib/bl.c +++ b/contrib/blacklist/lib/bl.c @@ -1,533 +1,533 @@ /* $NetBSD: bl.c,v 1.28 2016/07/29 17:13:09 christos Exp $ */ /*- * Copyright (c) 2014 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 #include __RCSID("$NetBSD: bl.c,v 1.28 2016/07/29 17:13:09 christos Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _REENTRANT #include #endif #include "bl.h" typedef struct { uint32_t bl_len; uint32_t bl_version; uint32_t bl_type; uint32_t bl_salen; struct sockaddr_storage bl_ss; char bl_data[]; } bl_message_t; struct blacklist { #ifdef _REENTRANT pthread_mutex_t b_mutex; # define BL_INIT(b) pthread_mutex_init(&b->b_mutex, NULL) # define BL_LOCK(b) pthread_mutex_lock(&b->b_mutex) # define BL_UNLOCK(b) pthread_mutex_unlock(&b->b_mutex) #else # define BL_INIT(b) do {} while(/*CONSTCOND*/0) # define BL_LOCK(b) BL_INIT(b) # define BL_UNLOCK(b) BL_INIT(b) #endif int b_fd; int b_connected; struct sockaddr_un b_sun; void (*b_fun)(int, const char *, va_list); bl_info_t b_info; }; #define BL_VERSION 1 bool bl_isconnected(bl_t b) { return b->b_connected == 0; } int bl_getfd(bl_t b) { return b->b_fd; } static void bl_reset(bl_t b, bool locked) { int serrno = errno; if (!locked) BL_LOCK(b); close(b->b_fd); errno = serrno; b->b_fd = -1; b->b_connected = -1; if (!locked) BL_UNLOCK(b); } static void bl_log(void (*fun)(int, const char *, va_list), int level, const char *fmt, ...) { va_list ap; int serrno = errno; va_start(ap, fmt); (*fun)(level, fmt, ap); va_end(ap); errno = serrno; } static int bl_init(bl_t b, bool srv) { static int one = 1; /* AF_UNIX address of local logger */ mode_t om; int rv, serrno; struct sockaddr_un *sun = &b->b_sun; #ifndef SOCK_NONBLOCK #define SOCK_NONBLOCK 0 #endif #ifndef SOCK_CLOEXEC #define SOCK_CLOEXEC 0 #endif #ifndef SOCK_NOSIGPIPE #define SOCK_NOSIGPIPE 0 #endif BL_LOCK(b); if (b->b_fd == -1) { b->b_fd = socket(PF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK|SOCK_NOSIGPIPE, 0); if (b->b_fd == -1) { bl_log(b->b_fun, LOG_ERR, "%s: socket failed (%s)", __func__, strerror(errno)); BL_UNLOCK(b); return -1; } #if SOCK_CLOEXEC == 0 fcntl(b->b_fd, F_SETFD, FD_CLOEXEC); #endif #if SOCK_NONBLOCK == 0 fcntl(b->b_fd, F_SETFL, fcntl(b->b_fd, F_GETFL) | O_NONBLOCK); #endif #if SOCK_NOSIGPIPE == 0 #ifdef SO_NOSIGPIPE int o = 1; setsockopt(b->b_fd, SOL_SOCKET, SO_NOSIGPIPE, &o, sizeof(o)); #else signal(SIGPIPE, SIG_IGN); #endif #endif } if (bl_isconnected(b)) { BL_UNLOCK(b); return 0; } /* * We try to connect anyway even when we are a server to verify * that no other server is listening to the socket. If we succeed * to connect and we are a server, someone else owns it. */ rv = connect(b->b_fd, (const void *)sun, (socklen_t)sizeof(*sun)); if (rv == 0) { if (srv) { bl_log(b->b_fun, LOG_ERR, "%s: another daemon is handling `%s'", __func__, sun->sun_path); goto out; } } else { if (!srv) { /* * If the daemon is not running, we just try a * connect, so leave the socket alone until it does * and only log once. */ if (b->b_connected != 1) { bl_log(b->b_fun, LOG_DEBUG, "%s: connect failed for `%s' (%s)", __func__, sun->sun_path, strerror(errno)); b->b_connected = 1; } BL_UNLOCK(b); return -1; } bl_log(b->b_fun, LOG_DEBUG, "Connected to blacklist server", __func__); } if (srv) { (void)unlink(sun->sun_path); om = umask(0); rv = bind(b->b_fd, (const void *)sun, (socklen_t)sizeof(*sun)); serrno = errno; (void)umask(om); errno = serrno; if (rv == -1) { bl_log(b->b_fun, LOG_ERR, "%s: bind failed for `%s' (%s)", __func__, sun->sun_path, strerror(errno)); goto out; } } b->b_connected = 0; #define GOT_FD 1 #if defined(LOCAL_CREDS) #define CRED_LEVEL 0 #define CRED_NAME LOCAL_CREDS #define CRED_SC_UID sc_euid #define CRED_SC_GID sc_egid #define CRED_MESSAGE SCM_CREDS #define CRED_SIZE SOCKCREDSIZE(NGROUPS_MAX) #define CRED_TYPE struct sockcred #define GOT_CRED 2 #elif defined(SO_PASSCRED) #define CRED_LEVEL SOL_SOCKET #define CRED_NAME SO_PASSCRED #define CRED_SC_UID uid #define CRED_SC_GID gid #define CRED_MESSAGE SCM_CREDENTIALS #define CRED_SIZE sizeof(struct ucred) #define CRED_TYPE struct ucred #define GOT_CRED 2 #else #define GOT_CRED 0 /* * getpeereid() and LOCAL_PEERCRED don't help here * because we are not a stream socket! */ #define CRED_SIZE 0 #define CRED_TYPE void * __unused #endif #ifdef CRED_LEVEL if (setsockopt(b->b_fd, CRED_LEVEL, CRED_NAME, &one, (socklen_t)sizeof(one)) == -1) { bl_log(b->b_fun, LOG_ERR, "%s: setsockopt %s " "failed (%s)", __func__, __STRING(CRED_NAME), strerror(errno)); goto out; } #endif BL_UNLOCK(b); return 0; out: bl_reset(b, true); BL_UNLOCK(b); return -1; } bl_t bl_create(bool srv, const char *path, void (*fun)(int, const char *, va_list)) { bl_t b = calloc(1, sizeof(*b)); if (b == NULL) goto out; b->b_fun = fun == NULL ? vsyslog : fun; b->b_fd = -1; b->b_connected = -1; BL_INIT(b); memset(&b->b_sun, 0, sizeof(b->b_sun)); b->b_sun.sun_family = AF_LOCAL; #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN b->b_sun.sun_len = sizeof(b->b_sun); #endif strlcpy(b->b_sun.sun_path, path ? path : _PATH_BLSOCK, sizeof(b->b_sun.sun_path)); bl_init(b, srv); return b; out: free(b); bl_log(fun, LOG_ERR, "%s: malloc failed (%s)", __func__, strerror(errno)); return NULL; } void bl_destroy(bl_t b) { bl_reset(b, false); free(b); } static int bl_getsock(bl_t b, struct sockaddr_storage *ss, const struct sockaddr *sa, socklen_t slen, const char *ctx) { uint8_t family; memset(ss, 0, sizeof(*ss)); switch (slen) { case 0: return 0; case sizeof(struct sockaddr_in): family = AF_INET; break; case sizeof(struct sockaddr_in6): family = AF_INET6; break; default: bl_log(b->b_fun, LOG_ERR, "%s: invalid socket len %u (%s)", __func__, (unsigned)slen, ctx); errno = EINVAL; return -1; } memcpy(ss, sa, slen); if (ss->ss_family != family) { bl_log(b->b_fun, LOG_INFO, "%s: correcting socket family %d to %d (%s)", __func__, ss->ss_family, family, ctx); ss->ss_family = family; } #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN if (ss->ss_len != slen) { bl_log(b->b_fun, LOG_INFO, "%s: correcting socket len %u to %u (%s)", __func__, ss->ss_len, (unsigned)slen, ctx); ss->ss_len = (uint8_t)slen; } #endif return 0; } int bl_send(bl_t b, bl_type_t e, int pfd, const struct sockaddr *sa, socklen_t slen, const char *ctx) { struct msghdr msg; struct iovec iov; union { char ctrl[CMSG_SPACE(sizeof(int))]; uint32_t fd; } ua; struct cmsghdr *cmsg; union { bl_message_t bl; char buf[512]; } ub; size_t ctxlen, tried; #define NTRIES 5 ctxlen = strlen(ctx); if (ctxlen > 128) ctxlen = 128; iov.iov_base = ub.buf; iov.iov_len = sizeof(bl_message_t) + ctxlen; ub.bl.bl_len = (uint32_t)iov.iov_len; ub.bl.bl_version = BL_VERSION; ub.bl.bl_type = (uint32_t)e; if (bl_getsock(b, &ub.bl.bl_ss, sa, slen, ctx) == -1) return -1; ub.bl.bl_salen = slen; memcpy(ub.bl.bl_data, ctx, ctxlen); msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_flags = 0; msg.msg_control = ua.ctrl; msg.msg_controllen = sizeof(ua.ctrl); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_len = CMSG_LEN(sizeof(int)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; memcpy(CMSG_DATA(cmsg), &pfd, sizeof(pfd)); tried = 0; again: if (bl_init(b, false) == -1) return -1; if ((sendmsg(b->b_fd, &msg, 0) == -1) && tried++ < NTRIES) { bl_reset(b, false); goto again; } return tried >= NTRIES ? -1 : 0; } bl_info_t * bl_recv(bl_t b) { struct msghdr msg; struct iovec iov; union { char ctrl[CMSG_SPACE(sizeof(int)) + CMSG_SPACE(CRED_SIZE)]; uint32_t fd; CRED_TYPE sc; } ua; struct cmsghdr *cmsg; CRED_TYPE *sc; union { bl_message_t bl; char buf[512]; } ub; int got; ssize_t rlen; size_t rem; bl_info_t *bi = &b->b_info; got = 0; memset(bi, 0, sizeof(*bi)); iov.iov_base = ub.buf; iov.iov_len = sizeof(ub); msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_flags = 0; msg.msg_control = ua.ctrl; msg.msg_controllen = sizeof(ua.ctrl) + 100; rlen = recvmsg(b->b_fd, &msg, 0); if (rlen == -1) { bl_log(b->b_fun, LOG_ERR, "%s: recvmsg failed (%s)", __func__, strerror(errno)); return NULL; } for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level != SOL_SOCKET) { bl_log(b->b_fun, LOG_ERR, "%s: unexpected cmsg_level %d", __func__, cmsg->cmsg_level); continue; } switch (cmsg->cmsg_type) { case SCM_RIGHTS: if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) { bl_log(b->b_fun, LOG_ERR, "%s: unexpected cmsg_len %d != %zu", __func__, cmsg->cmsg_len, CMSG_LEN(2 * sizeof(int))); continue; } memcpy(&bi->bi_fd, CMSG_DATA(cmsg), sizeof(bi->bi_fd)); got |= GOT_FD; break; #ifdef CRED_MESSAGE case CRED_MESSAGE: sc = (void *)CMSG_DATA(cmsg); bi->bi_uid = sc->CRED_SC_UID; bi->bi_gid = sc->CRED_SC_GID; got |= GOT_CRED; break; #endif default: bl_log(b->b_fun, LOG_ERR, "%s: unexpected cmsg_type %d", __func__, cmsg->cmsg_type); continue; } } if (got != (GOT_CRED|GOT_FD)) { - bl_log(b->b_fun, LOG_ERR, "message missing %s %s", + bl_log(b->b_fun, LOG_ERR, "message missing %s %s", #if GOT_CRED != 0 (got & GOT_CRED) == 0 ? "cred" : #endif "", (got & GOT_FD) == 0 ? "fd" : ""); - + return NULL; } rem = (size_t)rlen; if (rem < sizeof(ub.bl)) { bl_log(b->b_fun, LOG_ERR, "message too short %zd", rlen); return NULL; } rem -= sizeof(ub.bl); if (ub.bl.bl_version != BL_VERSION) { bl_log(b->b_fun, LOG_ERR, "bad version %d", ub.bl.bl_version); return NULL; } bi->bi_type = ub.bl.bl_type; bi->bi_slen = ub.bl.bl_salen; bi->bi_ss = ub.bl.bl_ss; #ifndef CRED_MESSAGE bi->bi_uid = -1; bi->bi_gid = -1; #endif rem = MIN(sizeof(bi->bi_msg), rem); if (rem == 0) bi->bi_msg[0] = '\0'; else strlcpy(bi->bi_msg, ub.bl.bl_data, rem); return bi; }