Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/blocklist/bin/blacklistd.c
39478 views
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
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);
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
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
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
578