1 /* $NetBSD: blocklistd.c,v 1.5 2024/08/02 17:11:55 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: blocklistd.c,v 1.5 2024/08/02 17:11:55 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 82 sigusr1(int n __unused) 83 { 84 debug++; 85 } 86 87 static void 88 sigusr2(int n __unused) 89 { 90 debug--; 91 } 92 93 static void 94 sighup(int n __unused) 95 { 96 readconf++; 97 } 98 99 static void 100 sigdone(int n __unused) 101 { 102 done++; 103 } 104 105 static __dead void 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 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 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 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 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 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_r); 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 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 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 396 rules_restore(void) 397 { 398 DB *db; 399 struct conf c; 400 struct dbinfo dbi; 401 unsigned int f; 402 403 db = state_open(dbfile, O_RDONLY, 0); 404 if (db == NULL) { 405 (*lfun)(LOG_ERR, "Can't open `%s' to restore state (%m)", 406 dbfile); 407 return; 408 } 409 for (f = 1; state_iterate(db, &c, &dbi, f) == 1; f = 0) { 410 if (dbi.id[0] == '\0') 411 continue; 412 (void)run_change("add", &c, dbi.id, sizeof(dbi.id)); 413 state_put(state, &c, &dbi); 414 } 415 state_close(db); 416 state_sync(state); 417 } 418 419 int 420 main(int argc, char *argv[]) 421 { 422 int c, tout, flags, flush, restore, ret; 423 const char *spath, **blsock; 424 size_t nblsock, maxblsock; 425 426 setprogname(argv[0]); 427 428 spath = NULL; 429 blsock = NULL; 430 maxblsock = nblsock = 0; 431 flush = 0; 432 restore = 0; 433 tout = 0; 434 flags = O_RDWR|O_EXCL|O_CLOEXEC; 435 while ((c = getopt(argc, argv, "C:c:D:dfP:rR:s:t:v")) != -1) { 436 switch (c) { 437 case 'C': 438 controlprog = optarg; 439 break; 440 case 'c': 441 configfile = optarg; 442 break; 443 case 'D': 444 dbfile = optarg; 445 break; 446 case 'd': 447 debug++; 448 break; 449 case 'f': 450 flush++; 451 break; 452 case 'P': 453 spath = optarg; 454 break; 455 case 'R': 456 rulename = optarg; 457 break; 458 case 'r': 459 restore++; 460 break; 461 case 's': 462 if (nblsock >= maxblsock) { 463 maxblsock += 10; 464 void *p = realloc(blsock, 465 sizeof(*blsock) * maxblsock); 466 if (p == NULL) 467 err(EXIT_FAILURE, 468 "Can't allocate memory for %zu sockets", 469 maxblsock); 470 blsock = p; 471 } 472 blsock[nblsock++] = optarg; 473 break; 474 case 't': 475 tout = atoi(optarg) * 1000; 476 break; 477 case 'v': 478 vflag++; 479 break; 480 default: 481 usage(c); 482 } 483 } 484 485 argc -= optind; 486 if (argc) 487 usage('?'); 488 489 signal(SIGHUP, sighup); 490 signal(SIGINT, sigdone); 491 signal(SIGQUIT, sigdone); 492 signal(SIGTERM, sigdone); 493 signal(SIGUSR1, sigusr1); 494 signal(SIGUSR2, sigusr2); 495 496 openlog(getprogname(), LOG_PID, LOG_DAEMON); 497 498 if (debug) { 499 lfun = dlog; 500 if (tout == 0) 501 tout = 5000; 502 } else { 503 if (tout == 0) 504 tout = 15000; 505 } 506 507 update_interfaces(); 508 conf_parse(configfile); 509 if (flush) { 510 rules_flush(); 511 if (!restore) 512 flags |= O_TRUNC; 513 } 514 515 struct pollfd *pfd = NULL; 516 bl_t *bl = NULL; 517 size_t nfd = 0; 518 size_t maxfd = 0; 519 520 for (size_t i = 0; i < nblsock; i++) 521 addfd(&pfd, &bl, &nfd, &maxfd, blsock[i]); 522 free(blsock); 523 524 if (spath) { 525 FILE *fp = fopen(spath, "r"); 526 char *line; 527 if (fp == NULL) 528 err(EXIT_FAILURE, "Can't open `%s'", spath); 529 for (; (line = fparseln(fp, NULL, NULL, NULL, 0)) != NULL; 530 free(line)) 531 addfd(&pfd, &bl, &nfd, &maxfd, line); 532 fclose(fp); 533 } 534 if (nfd == 0) 535 addfd(&pfd, &bl, &nfd, &maxfd, _PATH_BLSOCK); 536 537 state = state_open(dbfile, flags, 0600); 538 if (state == NULL) 539 state = state_open(dbfile, flags | O_CREAT, 0600); 540 if (state == NULL) 541 return EXIT_FAILURE; 542 543 if (restore) { 544 if (!flush) 545 rules_flush(); 546 rules_restore(); 547 } 548 549 if (!debug) { 550 if (daemon(0, 0) == -1) 551 err(EXIT_FAILURE, "daemon failed"); 552 if (pidfile(NULL) == -1) 553 err(EXIT_FAILURE, "Can't create pidfile"); 554 } 555 556 for (size_t t = 0; !done; t++) { 557 if (readconf) { 558 readconf = 0; 559 conf_parse(configfile); 560 } 561 ret = poll(pfd, (nfds_t)nfd, tout); 562 if (debug) 563 (*lfun)(LOG_DEBUG, "received %d from poll()", ret); 564 switch (ret) { 565 case -1: 566 if (errno == EINTR) 567 continue; 568 (*lfun)(LOG_ERR, "poll (%m)"); 569 return EXIT_FAILURE; 570 case 0: 571 state_sync(state); 572 break; 573 default: 574 for (size_t i = 0; i < nfd; i++) 575 if (pfd[i].revents & POLLIN) 576 process(bl[i]); 577 } 578 if (t % 100 == 0) 579 state_sync(state); 580 if (t % 10000 == 0) 581 update_interfaces(); 582 update(); 583 } 584 state_close(state); 585 return 0; 586 } 587