xref: /freebsd/contrib/blocklist/bin/blacklistd.c (revision 5f4c09dd85bff675e0ca63c55ea3c517e0fddfcc)
1 /*	$NetBSD: blacklistd.c,v 1.38 2019/02/27 02:20:18 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 2015 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Christos Zoulas.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34 #include <sys/cdefs.h>
35 __RCSID("$NetBSD: blacklistd.c,v 1.38 2019/02/27 02:20:18 christos Exp $");
36 
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <sys/queue.h>
40 
41 #ifdef HAVE_LIBUTIL_H
42 #include <libutil.h>
43 #endif
44 #ifdef HAVE_UTIL_H
45 #include <util.h>
46 #endif
47 #include <string.h>
48 #include <signal.h>
49 #include <netdb.h>
50 #include <stdio.h>
51 #include <stdbool.h>
52 #include <string.h>
53 #include <inttypes.h>
54 #include <syslog.h>
55 #include <ctype.h>
56 #include <limits.h>
57 #include <errno.h>
58 #include <poll.h>
59 #include <fcntl.h>
60 #include <err.h>
61 #include <stdlib.h>
62 #include <unistd.h>
63 #include <time.h>
64 #include <ifaddrs.h>
65 #include <netinet/in.h>
66 
67 #include "bl.h"
68 #include "internal.h"
69 #include "conf.h"
70 #include "run.h"
71 #include "state.h"
72 #include "support.h"
73 
74 static const char *configfile = _PATH_BLCONF;
75 static DB *state;
76 static const char *dbfile = _PATH_BLSTATE;
77 static sig_atomic_t readconf;
78 static sig_atomic_t done;
79 static int vflag;
80 
81 static void
sigusr1(int n __unused)82 sigusr1(int n __unused)
83 {
84 	debug++;
85 }
86 
87 static void
sigusr2(int n __unused)88 sigusr2(int n __unused)
89 {
90 	debug--;
91 }
92 
93 static void
sighup(int n __unused)94 sighup(int n __unused)
95 {
96 	readconf++;
97 }
98 
99 static void
sigdone(int n __unused)100 sigdone(int n __unused)
101 {
102 	done++;
103 }
104 
105 static __dead void
usage(int c)106 usage(int c)
107 {
108 	if (c != '?')
109 		warnx("Unknown option `%c'", (char)c);
110 	fprintf(stderr, "Usage: %s [-vdfr] [-c <config>] [-R <rulename>] "
111 	    "[-P <sockpathsfile>] [-C <controlprog>] [-D <dbfile>] "
112 	    "[-s <sockpath>] [-t <timeout>]\n", getprogname());
113 	exit(EXIT_FAILURE);
114 }
115 
116 static int
getremoteaddress(bl_info_t * bi,struct sockaddr_storage * rss,socklen_t * rsl)117 getremoteaddress(bl_info_t *bi, struct sockaddr_storage *rss, socklen_t *rsl)
118 {
119 	*rsl = sizeof(*rss);
120 	memset(rss, 0, *rsl);
121 
122 	if (getpeername(bi->bi_fd, (void *)rss, rsl) != -1)
123 		return 0;
124 
125 	if (errno != ENOTCONN) {
126 		(*lfun)(LOG_ERR, "getpeername failed (%m)");
127 		return -1;
128 	}
129 
130 	if (bi->bi_slen == 0) {
131 		(*lfun)(LOG_ERR, "unconnected socket with no peer in message");
132 		return -1;
133 	}
134 
135 	switch (bi->bi_ss.ss_family) {
136 	case AF_INET:
137 		*rsl = sizeof(struct sockaddr_in);
138 		break;
139 	case AF_INET6:
140 		*rsl = sizeof(struct sockaddr_in6);
141 		break;
142 	default:
143 		(*lfun)(LOG_ERR, "bad client passed socket family %u",
144 		    (unsigned)bi->bi_ss.ss_family);
145 		return -1;
146 	}
147 
148 	if (*rsl != bi->bi_slen) {
149 		(*lfun)(LOG_ERR, "bad client passed socket length %u != %u",
150 		    (unsigned)*rsl, (unsigned)bi->bi_slen);
151 		return -1;
152 	}
153 
154 	memcpy(rss, &bi->bi_ss, *rsl);
155 
156 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
157 	if (*rsl != rss->ss_len) {
158 		(*lfun)(LOG_ERR,
159 		    "bad client passed socket internal length %u != %u",
160 		    (unsigned)*rsl, (unsigned)rss->ss_len);
161 		return -1;
162 	}
163 #endif
164 	return 0;
165 }
166 
167 static void
process(bl_t bl)168 process(bl_t bl)
169 {
170 	struct sockaddr_storage rss;
171 	socklen_t rsl;
172 	char rbuf[BUFSIZ];
173 	bl_info_t *bi;
174 	struct conf c;
175 	struct dbinfo dbi;
176 	struct timespec ts;
177 
178 	if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
179 		(*lfun)(LOG_ERR, "clock_gettime failed (%m)");
180 		return;
181 	}
182 
183 	if ((bi = bl_recv(bl)) == NULL) {
184 		(*lfun)(LOG_ERR, "no message (%m)");
185 		return;
186 	}
187 
188 	if (getremoteaddress(bi, &rss, &rsl) == -1)
189 		goto out;
190 
191 	if (debug) {
192 		sockaddr_snprintf(rbuf, sizeof(rbuf), "%a:%p", (void *)&rss);
193 		(*lfun)(LOG_DEBUG, "processing type=%d fd=%d remote=%s msg=%s"
194 		    " uid=%lu gid=%lu", bi->bi_type, bi->bi_fd, rbuf,
195 		    bi->bi_msg, (unsigned long)bi->bi_uid,
196 		    (unsigned long)bi->bi_gid);
197 	}
198 
199 	if (conf_find(bi->bi_fd, bi->bi_uid, &rss, &c) == NULL) {
200 		(*lfun)(LOG_DEBUG, "no rule matched");
201 		goto out;
202 	}
203 
204 
205 	if (state_get(state, &c, &dbi) == -1)
206 		goto out;
207 
208 	if (debug) {
209 		char b1[128], b2[128];
210 		(*lfun)(LOG_DEBUG, "%s: initial db state for %s: count=%d/%d "
211 		    "last=%s now=%s", __func__, rbuf, dbi.count, c.c_nfail,
212 		    fmttime(b1, sizeof(b1), dbi.last),
213 		    fmttime(b2, sizeof(b2), ts.tv_sec));
214 	}
215 
216 	switch (bi->bi_type) {
217 	case BL_ABUSE:
218 		/*
219 		 * If the application has signaled abusive behavior,
220 		 * set the number of fails to be one less than the
221 		 * configured limit.  Fallthrough to the normal BL_ADD
222 		 * processing, which will increment the failure count
223 		 * to the threshhold, and block the abusive address.
224 		 */
225 		if (c.c_nfail != -1)
226 			dbi.count = c.c_nfail - 1;
227 		/*FALLTHROUGH*/
228 	case BL_ADD:
229 		dbi.count++;
230 		dbi.last = ts.tv_sec;
231 		if (c.c_nfail != -1 && dbi.count >= c.c_nfail) {
232 			/*
233 			 * No point in re-adding the rule.
234 			 * It might exist already due to latency in processing
235 			 * and removing the rule is the wrong thing to do as
236 			 * it allows a window to attack again.
237 			 */
238 			if (dbi.id[0] == '\0') {
239 				int res = run_change("add", &c,
240 				    dbi.id, sizeof(dbi.id));
241 				if (res == -1)
242 					goto out;
243 			}
244 			sockaddr_snprintf(rbuf, sizeof(rbuf), "%a",
245 			    (void *)&rss);
246 			(*lfun)(LOG_INFO,
247 			    "blocked %s/%d:%d for %d seconds",
248 			    rbuf, c.c_lmask, c.c_port, c.c_duration);
249 		}
250 		break;
251 	case BL_DELETE:
252 		if (dbi.last == 0)
253 			goto out;
254 		dbi.count = 0;
255 		dbi.last = 0;
256 		break;
257 	case BL_BADUSER:
258 		/* ignore for now */
259 		break;
260 	default:
261 		(*lfun)(LOG_ERR, "unknown message %d", bi->bi_type);
262 	}
263 	state_put(state, &c, &dbi);
264 
265 out:
266 	close(bi->bi_fd);
267 
268 	if (debug) {
269 		char b1[128], b2[128];
270 		(*lfun)(LOG_DEBUG, "%s: final db state for %s: count=%d/%d "
271 		    "last=%s now=%s", __func__, rbuf, dbi.count, c.c_nfail,
272 		    fmttime(b1, sizeof(b1), dbi.last),
273 		    fmttime(b2, sizeof(b2), ts.tv_sec));
274 	}
275 }
276 
277 static void
update_interfaces(void)278 update_interfaces(void)
279 {
280 	struct ifaddrs *oifas, *nifas;
281 
282 	if (getifaddrs(&nifas) == -1)
283 		return;
284 
285 	oifas = ifas;
286 	ifas = nifas;
287 
288 	if (oifas)
289 		freeifaddrs(oifas);
290 }
291 
292 static void
update(void)293 update(void)
294 {
295 	struct timespec ts;
296 	struct conf c;
297 	struct dbinfo dbi;
298 	unsigned int f, n;
299 	char buf[128];
300 	void *ss = &c.c_ss;
301 
302 	if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
303 		(*lfun)(LOG_ERR, "clock_gettime failed (%m)");
304 		return;
305 	}
306 
307 again:
308 	for (n = 0, f = 1; state_iterate(state, &c, &dbi, f) == 1;
309 	    f = 0, n++)
310 	{
311 		time_t when = c.c_duration + dbi.last;
312 		if (debug > 1) {
313 			char b1[64], b2[64];
314 			sockaddr_snprintf(buf, sizeof(buf), "%a:%p", ss);
315 			(*lfun)(LOG_DEBUG, "%s:[%u] %s count=%d duration=%d "
316 			    "last=%s " "now=%s", __func__, n, buf, dbi.count,
317 			    c.c_duration, fmttime(b1, sizeof(b1), dbi.last),
318 			    fmttime(b2, sizeof(b2), ts.tv_sec));
319 		}
320 		if (c.c_duration == -1 || when >= ts.tv_sec)
321 			continue;
322 		if (dbi.id[0]) {
323 			run_change("rem", &c, dbi.id, 0);
324 			sockaddr_snprintf(buf, sizeof(buf), "%a", ss);
325 			(*lfun)(LOG_INFO, "released %s/%d:%d after %d seconds",
326 			    buf, c.c_lmask, c.c_port, c.c_duration);
327 		}
328 		state_del(state, &c);
329 		goto again;
330 	}
331 }
332 
333 static void
addfd(struct pollfd ** pfdp,bl_t ** blp,size_t * nfd,size_t * maxfd,const char * path)334 addfd(struct pollfd **pfdp, bl_t **blp, size_t *nfd, size_t *maxfd,
335     const char *path)
336 {
337 	bl_t bl = bl_create(true, path, vflag ? vdlog : vsyslog);
338 	if (bl == NULL || !bl_isconnected(bl))
339 		exit(EXIT_FAILURE);
340 	if (*nfd >= *maxfd) {
341 		*maxfd += 10;
342 		*blp = realloc(*blp, sizeof(**blp) * *maxfd);
343 		if (*blp == NULL)
344 			err(EXIT_FAILURE, "malloc");
345 		*pfdp = realloc(*pfdp, sizeof(**pfdp) * *maxfd);
346 		if (*pfdp == NULL)
347 			err(EXIT_FAILURE, "malloc");
348 	}
349 
350 	(*pfdp)[*nfd].fd = bl_getfd(bl);
351 	(*pfdp)[*nfd].events = POLLIN;
352 	(*blp)[*nfd] = bl;
353 	*nfd += 1;
354 }
355 
356 static void
uniqueadd(struct conf *** listp,size_t * nlist,size_t * mlist,struct conf * c)357 uniqueadd(struct conf ***listp, size_t *nlist, size_t *mlist, struct conf *c)
358 {
359 	struct conf **list = *listp;
360 
361 	if (c->c_name[0] == '\0')
362 		return;
363 	for (size_t i = 0; i < *nlist; i++) {
364 		if (strcmp(list[i]->c_name, c->c_name) == 0)
365 			return;
366 	}
367 	if (*nlist == *mlist) {
368 		*mlist += 10;
369 		void *p = realloc(*listp, *mlist * sizeof(*list));
370 		if (p == NULL)
371 			err(EXIT_FAILURE, "Can't allocate for rule list");
372 		list = *listp = p;
373 	}
374 	list[(*nlist)++] = c;
375 }
376 
377 static void
rules_flush(void)378 rules_flush(void)
379 {
380 	struct conf **list;
381 	size_t nlist, mlist;
382 
383 	list = NULL;
384 	mlist = nlist = 0;
385 	for (size_t i = 0; i < rconf.cs_n; i++)
386 		uniqueadd(&list, &nlist, &mlist, &rconf.cs_c[i]);
387 	for (size_t i = 0; i < lconf.cs_n; i++)
388 		uniqueadd(&list, &nlist, &mlist, &lconf.cs_c[i]);
389 
390 	for (size_t i = 0; i < nlist; i++)
391 		run_flush(list[i]);
392 	free(list);
393 }
394 
395 static void
rules_restore(void)396 rules_restore(void)
397 {
398 	struct conf c;
399 	struct dbinfo dbi;
400 	unsigned int f;
401 
402 	for (f = 1; state_iterate(state, &c, &dbi, f) == 1; f = 0) {
403 		if (dbi.id[0] == '\0')
404 			continue;
405 		(void)run_change("add", &c, dbi.id, sizeof(dbi.id));
406 	}
407 }
408 
409 int
main(int argc,char * argv[])410 main(int argc, char *argv[])
411 {
412 	int c, tout, flags, flush, restore, ret;
413 	const char *spath, **blsock;
414 	size_t nblsock, maxblsock;
415 
416 	setprogname(argv[0]);
417 
418 	spath = NULL;
419 	blsock = NULL;
420 	maxblsock = nblsock = 0;
421 	flush = 0;
422 	restore = 0;
423 	tout = 0;
424 	flags = O_RDWR|O_EXCL|O_CLOEXEC;
425 	while ((c = getopt(argc, argv, "C:c:D:dfP:rR:s:t:v")) != -1) {
426 		switch (c) {
427 		case 'C':
428 			controlprog = optarg;
429 			break;
430 		case 'c':
431 			configfile = optarg;
432 			break;
433 		case 'D':
434 			dbfile = optarg;
435 			break;
436 		case 'd':
437 			debug++;
438 			break;
439 		case 'f':
440 			flush++;
441 			break;
442 		case 'P':
443 			spath = optarg;
444 			break;
445 		case 'R':
446 			rulename = optarg;
447 			break;
448 		case 'r':
449 			restore++;
450 			break;
451 		case 's':
452 			if (nblsock >= maxblsock) {
453 				maxblsock += 10;
454 				void *p = realloc(blsock,
455 				    sizeof(*blsock) * maxblsock);
456 				if (p == NULL)
457 				    err(EXIT_FAILURE,
458 					"Can't allocate memory for %zu sockets",
459 					maxblsock);
460 				blsock = p;
461 			}
462 			blsock[nblsock++] = optarg;
463 			break;
464 		case 't':
465 			tout = atoi(optarg) * 1000;
466 			break;
467 		case 'v':
468 			vflag++;
469 			break;
470 		default:
471 			usage(c);
472 		}
473 	}
474 
475 	argc -= optind;
476 	if (argc)
477 		usage('?');
478 
479 	signal(SIGHUP, sighup);
480 	signal(SIGINT, sigdone);
481 	signal(SIGQUIT, sigdone);
482 	signal(SIGTERM, sigdone);
483 	signal(SIGUSR1, sigusr1);
484 	signal(SIGUSR2, sigusr2);
485 
486 	openlog(getprogname(), LOG_PID, LOG_DAEMON);
487 
488 	if (debug) {
489 		lfun = dlog;
490 		if (tout == 0)
491 			tout = 5000;
492 	} else {
493 		if (tout == 0)
494 			tout = 15000;
495 	}
496 
497 	update_interfaces();
498 	conf_parse(configfile);
499 	if (flush) {
500 		rules_flush();
501 		if (!restore)
502 			flags |= O_TRUNC;
503 	}
504 
505 	struct pollfd *pfd = NULL;
506 	bl_t *bl = NULL;
507 	size_t nfd = 0;
508 	size_t maxfd = 0;
509 
510 	for (size_t i = 0; i < nblsock; i++)
511 		addfd(&pfd, &bl, &nfd, &maxfd, blsock[i]);
512 	free(blsock);
513 
514 	if (spath) {
515 		FILE *fp = fopen(spath, "r");
516 		char *line;
517 		if (fp == NULL)
518 			err(EXIT_FAILURE, "Can't open `%s'", spath);
519 		for (; (line = fparseln(fp, NULL, NULL, NULL, 0)) != NULL;
520 		    free(line))
521 			addfd(&pfd, &bl, &nfd, &maxfd, line);
522 		fclose(fp);
523 	}
524 	if (nfd == 0)
525 		addfd(&pfd, &bl, &nfd, &maxfd, _PATH_BLSOCK);
526 
527 	state = state_open(dbfile, flags, 0600);
528 	if (state == NULL)
529 		state = state_open(dbfile,  flags | O_CREAT, 0600);
530 	if (state == NULL)
531 		return EXIT_FAILURE;
532 
533 	if (restore) {
534 		if (!flush)
535 			rules_flush();
536 		rules_restore();
537 	}
538 
539 	if (!debug) {
540 		if (daemon(0, 0) == -1)
541 			err(EXIT_FAILURE, "daemon failed");
542 		if (pidfile(NULL) == -1)
543 			err(EXIT_FAILURE, "Can't create pidfile");
544 	}
545 
546 	for (size_t t = 0; !done; t++) {
547 		if (readconf) {
548 			readconf = 0;
549 			conf_parse(configfile);
550 		}
551 		ret = poll(pfd, (nfds_t)nfd, tout);
552 		if (debug)
553 			(*lfun)(LOG_DEBUG, "received %d from poll()", ret);
554 		switch (ret) {
555 		case -1:
556 			if (errno == EINTR)
557 				continue;
558 			(*lfun)(LOG_ERR, "poll (%m)");
559 			return EXIT_FAILURE;
560 		case 0:
561 			state_sync(state);
562 			break;
563 		default:
564 			for (size_t i = 0; i < nfd; i++)
565 				if (pfd[i].revents & POLLIN)
566 					process(bl[i]);
567 		}
568 		if (t % 100 == 0)
569 			state_sync(state);
570 		if (t % 10000 == 0)
571 			update_interfaces();
572 		update();
573 	}
574 	state_close(state);
575 	return 0;
576 }
577