1 /* $NetBSD: blocklistd.c,v 1.10 2025/03/26 17:09:35 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.10 2025/03/26 17:09:35 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 "bl.h" 71 #include "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 goto out; 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 goto out; 208 } 209 210 211 if (state_get(state, &c, &dbi) == -1) 212 goto out; 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 threshhold, 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 close(bi->bi_fd); 273 274 if (debug) { 275 char b1[128], b2[128]; 276 (*lfun)(LOG_DEBUG, "%s: final db state for %s: count=%d/%d " 277 "last=%s now=%s", __func__, rbuf, dbi.count, c.c_nfail, 278 fmttime(b1, sizeof(b1), dbi.last), 279 fmttime(b2, sizeof(b2), ts.tv_sec)); 280 } 281 } 282 283 static void 284 update_interfaces(void) 285 { 286 struct ifaddrs *oifas, *nifas; 287 288 if (getifaddrs(&nifas) == -1) 289 return; 290 291 oifas = ifas; 292 ifas = nifas; 293 294 if (oifas) 295 freeifaddrs(oifas); 296 } 297 298 static void 299 update(void) 300 { 301 struct timespec ts; 302 struct conf c; 303 struct dbinfo dbi; 304 unsigned int f, n; 305 char buf[128]; 306 void *ss = &c.c_ss; 307 308 if (clock_gettime(CLOCK_REALTIME, &ts) == -1) { 309 (*lfun)(LOG_ERR, "clock_gettime failed (%m)"); 310 return; 311 } 312 313 again: 314 for (n = 0, f = 1; state_iterate(state, &c, &dbi, f) == 1; 315 f = 0, n++) 316 { 317 time_t when = c.c_duration + dbi.last; 318 if (debug > 1) { 319 char b1[64], b2[64]; 320 sockaddr_snprintf(buf, sizeof(buf), "%a:%p", ss); 321 (*lfun)(LOG_DEBUG, "%s:[%u] %s count=%d duration=%d " 322 "last=%s " "now=%s", __func__, n, buf, dbi.count, 323 c.c_duration, fmttime(b1, sizeof(b1), dbi.last), 324 fmttime(b2, sizeof(b2), ts.tv_sec)); 325 } 326 if (c.c_duration == -1 || when >= ts.tv_sec) 327 continue; 328 if (dbi.id[0]) { 329 run_change("rem", &c, dbi.id, 0); 330 sockaddr_snprintf(buf, sizeof(buf), "%a", ss); 331 (*lfun)(LOG_INFO, "released %s/%d:%d after %d seconds", 332 buf, c.c_lmask, c.c_port, c.c_duration); 333 } 334 state_del(state, &c); 335 goto again; 336 } 337 } 338 339 static void 340 addfd(struct pollfd **pfdp, bl_t **blp, size_t *nfd, size_t *maxfd, 341 const char *path) 342 { 343 bl_t bl = bl_create(true, path, vflag ? vdlog : vsyslog_r); 344 if (bl == NULL || !bl_isconnected(bl)) 345 exit(EXIT_FAILURE); 346 if (*nfd >= *maxfd) { 347 *maxfd += 10; 348 *blp = realloc(*blp, sizeof(**blp) * *maxfd); 349 if (*blp == NULL) 350 err(EXIT_FAILURE, "malloc"); 351 *pfdp = realloc(*pfdp, sizeof(**pfdp) * *maxfd); 352 if (*pfdp == NULL) 353 err(EXIT_FAILURE, "malloc"); 354 } 355 356 (*pfdp)[*nfd].fd = bl_getfd(bl); 357 (*pfdp)[*nfd].events = POLLIN; 358 (*blp)[*nfd] = bl; 359 *nfd += 1; 360 } 361 362 static void 363 uniqueadd(struct conf ***listp, size_t *nlist, size_t *mlist, struct conf *c) 364 { 365 struct conf **list = *listp; 366 367 if (c->c_name[0] == '\0') 368 return; 369 for (size_t i = 0; i < *nlist; i++) { 370 if (strcmp(list[i]->c_name, c->c_name) == 0) 371 return; 372 } 373 if (*nlist == *mlist) { 374 *mlist += 10; 375 void *p = realloc(*listp, *mlist * sizeof(*list)); 376 if (p == NULL) 377 err(EXIT_FAILURE, "Can't allocate for rule list"); 378 list = *listp = p; 379 } 380 list[(*nlist)++] = c; 381 } 382 383 static void 384 rules_flush(void) 385 { 386 struct conf **list; 387 size_t nlist, mlist; 388 389 list = NULL; 390 mlist = nlist = 0; 391 for (size_t i = 0; i < rconf.cs_n; i++) 392 uniqueadd(&list, &nlist, &mlist, &rconf.cs_c[i]); 393 for (size_t i = 0; i < lconf.cs_n; i++) 394 uniqueadd(&list, &nlist, &mlist, &lconf.cs_c[i]); 395 396 for (size_t i = 0; i < nlist; i++) 397 run_flush(list[i]); 398 free(list); 399 } 400 401 static void 402 rules_restore(void) 403 { 404 DB *db; 405 struct conf c; 406 struct dbinfo dbi; 407 unsigned int f; 408 409 db = state_open(dbfile, O_RDONLY, 0); 410 if (db == NULL) { 411 (*lfun)(LOG_ERR, "Can't open `%s' to restore state (%m)", 412 dbfile); 413 return; 414 } 415 for (f = 1; state_iterate(db, &c, &dbi, f) == 1; f = 0) { 416 if (dbi.id[0] == '\0') 417 continue; 418 (void)run_change("add", &c, dbi.id, sizeof(dbi.id)); 419 state_put(state, &c, &dbi); 420 } 421 state_close(db); 422 state_sync(state); 423 } 424 425 int 426 main(int argc, char *argv[]) 427 { 428 int c, tout, flags, flush, restore, ret; 429 const char *spath, **blsock; 430 size_t nblsock, maxblsock; 431 432 setprogname(argv[0]); 433 434 spath = NULL; 435 blsock = NULL; 436 maxblsock = nblsock = 0; 437 flush = 0; 438 restore = 0; 439 tout = 0; 440 flags = O_RDWR|O_EXCL|O_CLOEXEC; 441 while ((c = getopt(argc, argv, "C:c:D:dfP:rR:s:t:v")) != -1) { 442 switch (c) { 443 case 'C': 444 controlprog = optarg; 445 break; 446 case 'c': 447 configfile = optarg; 448 break; 449 case 'D': 450 dbfile = optarg; 451 break; 452 case 'd': 453 debug++; 454 break; 455 case 'f': 456 flush++; 457 break; 458 case 'P': 459 spath = optarg; 460 break; 461 case 'R': 462 rulename = optarg; 463 break; 464 case 'r': 465 restore++; 466 break; 467 case 's': 468 if (nblsock >= maxblsock) { 469 maxblsock += 10; 470 void *p = realloc(blsock, 471 sizeof(*blsock) * maxblsock); 472 if (p == NULL) 473 err(EXIT_FAILURE, 474 "Can't allocate memory for %zu sockets", 475 maxblsock); 476 blsock = p; 477 } 478 blsock[nblsock++] = optarg; 479 break; 480 case 't': 481 tout = atoi(optarg) * 1000; 482 break; 483 case 'v': 484 vflag++; 485 break; 486 default: 487 usage(c); 488 } 489 } 490 491 argc -= optind; 492 if (argc) 493 usage('?'); 494 495 signal(SIGHUP, sighup); 496 signal(SIGINT, sigdone); 497 signal(SIGQUIT, sigdone); 498 signal(SIGTERM, sigdone); 499 signal(SIGUSR1, sigusr1); 500 signal(SIGUSR2, sigusr2); 501 502 openlog(getprogname(), LOG_PID, LOG_DAEMON); 503 504 if (debug) { 505 lfun = dlog; 506 if (tout == 0) 507 tout = 5000; 508 } else { 509 if (tout == 0) 510 tout = 15000; 511 } 512 513 update_interfaces(); 514 conf_parse(configfile); 515 if (flush) { 516 rules_flush(); 517 if (!restore) 518 flags |= O_TRUNC; 519 } 520 521 struct pollfd *pfd = NULL; 522 bl_t *bl = NULL; 523 size_t nfd = 0; 524 size_t maxfd = 0; 525 526 for (size_t i = 0; i < nblsock; i++) 527 addfd(&pfd, &bl, &nfd, &maxfd, blsock[i]); 528 free(blsock); 529 530 if (spath) { 531 FILE *fp = fopen(spath, "r"); 532 char *line; 533 if (fp == NULL) 534 err(EXIT_FAILURE, "Can't open `%s'", spath); 535 for (; (line = fparseln(fp, NULL, NULL, NULL, 0)) != NULL; 536 free(line)) 537 addfd(&pfd, &bl, &nfd, &maxfd, line); 538 fclose(fp); 539 } 540 if (nfd == 0) 541 addfd(&pfd, &bl, &nfd, &maxfd, _PATH_BLSOCK); 542 543 state = state_open(dbfile, flags, 0600); 544 if (state == NULL) 545 state = state_open(dbfile, flags | O_CREAT, 0600); 546 if (state == NULL) 547 return EXIT_FAILURE; 548 549 if (restore) { 550 if (!flush) 551 rules_flush(); 552 rules_restore(); 553 } 554 555 if (!debug) { 556 if (daemon(0, 0) == -1) 557 err(EXIT_FAILURE, "daemon failed"); 558 if (pidfile(NULL) == -1) 559 err(EXIT_FAILURE, "Can't create pidfile"); 560 } 561 562 for (size_t t = 0; !done; t++) { 563 if (readconf) { 564 readconf = 0; 565 conf_parse(configfile); 566 } 567 ret = poll(pfd, (nfds_t)nfd, tout); 568 if (debug) 569 (*lfun)(LOG_DEBUG, "received %d from poll()", ret); 570 switch (ret) { 571 case -1: 572 if (errno == EINTR) 573 continue; 574 (*lfun)(LOG_ERR, "poll (%m)"); 575 return EXIT_FAILURE; 576 case 0: 577 state_sync(state); 578 break; 579 default: 580 for (size_t i = 0; i < nfd; i++) 581 if (pfd[i].revents & POLLIN) 582 process(bl[i]); 583 } 584 if (t % 100 == 0) 585 state_sync(state); 586 if (t % 10000 == 0) 587 update_interfaces(); 588 update(); 589 } 590 state_close(state); 591 return 0; 592 } 593