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