]> jfr.im git - solanum.git/blob - authd/providers/blacklist.c
filter: add #defines for nick,user,host
[solanum.git] / authd / providers / blacklist.c
1 /*
2 * charybdis: A slightly useful ircd.
3 * blacklist.c: Manages DNS blacklist entries and lookups
4 *
5 * Copyright (C) 2006-2011 charybdis development team
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * 3. The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
30 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 /* Originally written for charybdis circa 2006 (by nenolod?).
35 * Tweaked for authd. Some functions and structs renamed. Public/private
36 * interfaces have been shifted around. Some code has been cleaned up too.
37 * -- Elizafox 24 March 2016
38 */
39
40 #include "authd.h"
41 #include "defaults.h"
42 #include "provider.h"
43 #include "notice.h"
44 #include "stdinc.h"
45 #include "dns.h"
46
47 #define SELF_PID (blacklist_provider.id)
48
49 typedef enum filter_t
50 {
51 FILTER_ALL = 1,
52 FILTER_LAST = 2,
53 } filter_t;
54
55 /* Blacklist accepted IP types */
56 #define IPTYPE_IPV4 1
57 #define IPTYPE_IPV6 2
58
59 /* A configured DNSBL */
60 struct blacklist
61 {
62 char host[IRCD_RES_HOSTLEN + 1];
63 char reason[BUFSIZE]; /* Reason template (ircd fills in the blanks) */
64 uint8_t iptype; /* IP types supported */
65 rb_dlink_list filters; /* Filters for queries */
66
67 bool delete; /* If true delete when no clients */
68 int refcount; /* When 0 and delete is set, remove this blacklist */
69 unsigned int hits;
70
71 time_t lastwarning; /* Last warning about garbage replies sent */
72 };
73
74 /* A lookup in progress for a particular DNSBL for a particular client */
75 struct blacklist_lookup
76 {
77 struct blacklist *bl; /* Blacklist we're checking */
78 struct auth_client *auth; /* Client */
79 struct dns_query *query; /* DNS query pointer */
80
81 rb_dlink_node node;
82 };
83
84 /* A blacklist filter */
85 struct blacklist_filter
86 {
87 filter_t type; /* Type of filter */
88 char filter[HOSTIPLEN]; /* The filter itself */
89
90 rb_dlink_node node;
91 };
92
93 /* Blacklist user data attached to auth_client instance */
94 struct blacklist_user
95 {
96 bool started;
97 rb_dlink_list queries; /* Blacklist queries in flight */
98 };
99
100 /* public interfaces */
101 static void blacklists_destroy(void);
102
103 static bool blacklists_start(struct auth_client *);
104 static inline void blacklists_generic_cancel(struct auth_client *, const char *);
105 static void blacklists_timeout(struct auth_client *);
106 static void blacklists_cancel(struct auth_client *);
107 static void blacklists_cancel_none(struct auth_client *);
108
109 /* private interfaces */
110 static void unref_blacklist(struct blacklist *);
111 static struct blacklist *new_blacklist(const char *, const char *, uint8_t, rb_dlink_list *);
112 static struct blacklist *find_blacklist(const char *);
113 static bool blacklist_check_reply(struct blacklist_lookup *, const char *);
114 static void blacklist_dns_callback(const char *, bool, query_type, void *);
115 static void initiate_blacklist_dnsquery(struct blacklist *, struct auth_client *);
116
117 /* Variables */
118 static rb_dlink_list blacklist_list = { NULL, NULL, 0 };
119 static int blacklist_timeout = BLACKLIST_TIMEOUT_DEFAULT;
120
121 /* private interfaces */
122
123 static void
124 unref_blacklist(struct blacklist *bl)
125 {
126 rb_dlink_node *ptr, *nptr;
127
128 bl->refcount--;
129 if (bl->delete && bl->refcount <= 0)
130 {
131 RB_DLINK_FOREACH_SAFE(ptr, nptr, bl->filters.head)
132 {
133 rb_dlinkDelete(ptr, &bl->filters);
134 rb_free(ptr);
135 }
136
137 rb_dlinkFindDestroy(bl, &blacklist_list);
138 rb_free(bl);
139 }
140 }
141
142 static struct blacklist *
143 new_blacklist(const char *name, const char *reason, uint8_t iptype, rb_dlink_list *filters)
144 {
145 struct blacklist *bl;
146
147 if (name == NULL || reason == NULL || iptype == 0)
148 return NULL;
149
150 if((bl = find_blacklist(name)) == NULL)
151 {
152 bl = rb_malloc(sizeof(struct blacklist));
153 rb_dlinkAddAlloc(bl, &blacklist_list);
154 }
155 else
156 bl->delete = false;
157
158 rb_strlcpy(bl->host, name, IRCD_RES_HOSTLEN + 1);
159 rb_strlcpy(bl->reason, reason, BUFSIZE);
160 bl->iptype = iptype;
161
162 rb_dlinkMoveList(filters, &bl->filters);
163
164 bl->lastwarning = 0;
165
166 return bl;
167 }
168
169 static struct blacklist *
170 find_blacklist(const char *name)
171 {
172 rb_dlink_node *ptr;
173
174 RB_DLINK_FOREACH(ptr, blacklist_list.head)
175 {
176 struct blacklist *bl = (struct blacklist *)ptr->data;
177
178 if (!strcasecmp(bl->host, name))
179 return bl;
180 }
181
182 return NULL;
183 }
184
185 static inline bool
186 blacklist_check_reply(struct blacklist_lookup *bllookup, const char *ipaddr)
187 {
188 struct blacklist *bl = bllookup->bl;
189 const char *lastoctet;
190 rb_dlink_node *ptr;
191
192 /* No filters and entry found - thus positive match */
193 if (!rb_dlink_list_length(&bl->filters))
194 return true;
195
196 /* Below will prolly have to change if IPv6 address replies are sent back */
197 if ((lastoctet = strrchr(ipaddr, '.')) == NULL || *(++lastoctet) == '\0')
198 goto blwarn;
199
200 RB_DLINK_FOREACH(ptr, bl->filters.head)
201 {
202 struct blacklist_filter *filter = ptr->data;
203 const char *cmpstr;
204
205 if (filter->type == FILTER_ALL)
206 cmpstr = ipaddr;
207 else if (filter->type == FILTER_LAST)
208 cmpstr = lastoctet;
209 else
210 {
211 warn_opers(L_CRIT, "Blacklist: Unknown blacklist filter type (host %s): %d",
212 bl->host, filter->type);
213 exit(EX_PROVIDER_ERROR);
214 }
215
216 if (strcmp(cmpstr, filter->filter) == 0)
217 /* Match! */
218 return true;
219 }
220
221 return false;
222 blwarn:
223 if (bl->lastwarning + 3600 < rb_current_time())
224 {
225 warn_opers(L_WARN, "Garbage/undecipherable reply received from blacklist %s (reply %s)",
226 bl->host, ipaddr);
227 bl->lastwarning = rb_current_time();
228 }
229
230 return false;
231 }
232
233 static void
234 blacklist_dns_callback(const char *result, bool status, query_type type, void *data)
235 {
236 struct blacklist_lookup *bllookup = (struct blacklist_lookup *)data;
237 struct blacklist_user *bluser;
238 struct blacklist *bl;
239 struct auth_client *auth;
240
241 lrb_assert(bllookup != NULL);
242 lrb_assert(bllookup->auth != NULL);
243
244 bl = bllookup->bl;
245 auth = bllookup->auth;
246
247 if((bluser = get_provider_data(auth, SELF_PID)) == NULL)
248 return;
249
250 if (result != NULL && status && blacklist_check_reply(bllookup, result))
251 {
252 /* Match found, so proceed no further */
253 bl->hits++;
254 reject_client(auth, SELF_PID, bl->host, bl->reason);
255 blacklists_cancel(auth);
256 return;
257 }
258
259 unref_blacklist(bl);
260 cancel_query(bllookup->query); /* Ignore future responses */
261 rb_dlinkDelete(&bllookup->node, &bluser->queries);
262 rb_free(bllookup);
263
264 if(!rb_dlink_list_length(&bluser->queries))
265 {
266 /* Done here */
267 notice_client(auth->cid, "*** IP not found in DNS blacklist%s",
268 rb_dlink_list_length(&blacklist_list) > 1 ? "s" : "");
269 rb_free(bluser);
270 set_provider_data(auth, SELF_PID, NULL);
271 set_provider_timeout_absolute(auth, SELF_PID, 0);
272 provider_done(auth, SELF_PID);
273
274 auth_client_unref(auth);
275 }
276 }
277
278 static void
279 initiate_blacklist_dnsquery(struct blacklist *bl, struct auth_client *auth)
280 {
281 struct blacklist_lookup *bllookup = rb_malloc(sizeof(struct blacklist_lookup));
282 struct blacklist_user *bluser = get_provider_data(auth, SELF_PID);
283 char buf[IRCD_RES_HOSTLEN + 1];
284 int aftype;
285
286 bllookup->bl = bl;
287 bllookup->auth = auth;
288
289 aftype = GET_SS_FAMILY(&auth->c_addr);
290 if((aftype == AF_INET && (bl->iptype & IPTYPE_IPV4) == 0) ||
291 (aftype == AF_INET6 && (bl->iptype & IPTYPE_IPV6) == 0))
292 /* Incorrect blacklist type for this IP... */
293 {
294 rb_free(bllookup);
295 return;
296 }
297
298 build_rdns(buf, sizeof(buf), &auth->c_addr, bl->host);
299 bllookup->query = lookup_ip(buf, AF_INET, blacklist_dns_callback, bllookup);
300
301 rb_dlinkAdd(bllookup, &bllookup->node, &bluser->queries);
302 bl->refcount++;
303 }
304
305 static inline bool
306 lookup_all_blacklists(struct auth_client *auth)
307 {
308 struct blacklist_user *bluser = get_provider_data(auth, SELF_PID);
309 rb_dlink_node *ptr;
310 int iptype;
311
312 if(GET_SS_FAMILY(&auth->c_addr) == AF_INET)
313 iptype = IPTYPE_IPV4;
314 else if(GET_SS_FAMILY(&auth->c_addr) == AF_INET6)
315 iptype = IPTYPE_IPV6;
316 else
317 return false;
318
319 bluser->started = true;
320 notice_client(auth->cid, "*** Checking your IP against DNS blacklist%s",
321 rb_dlink_list_length(&blacklist_list) > 1 ? "s" : "");
322
323 RB_DLINK_FOREACH(ptr, blacklist_list.head)
324 {
325 struct blacklist *bl = (struct blacklist *)ptr->data;
326
327 if (!bl->delete && (bl->iptype & iptype))
328 initiate_blacklist_dnsquery(bl, auth);
329 }
330
331 if(!rb_dlink_list_length(&bluser->queries))
332 /* None checked. */
333 return false;
334
335 set_provider_timeout_relative(auth, SELF_PID, blacklist_timeout);
336
337 return true;
338 }
339
340 static inline void
341 delete_blacklist(struct blacklist *bl)
342 {
343 if (bl->refcount > 0)
344 bl->delete = true;
345 else
346 {
347 rb_dlinkFindDestroy(bl, &blacklist_list);
348 rb_free(bl);
349 }
350 }
351
352 static void
353 delete_all_blacklists(void)
354 {
355 rb_dlink_node *ptr, *nptr;
356
357 RB_DLINK_FOREACH_SAFE(ptr, nptr, blacklist_list.head)
358 {
359 delete_blacklist(ptr->data);
360 }
361 }
362
363 /* public interfaces */
364 static bool
365 blacklists_start(struct auth_client *auth)
366 {
367 lrb_assert(get_provider_data(auth, SELF_PID) == NULL);
368
369 if (!rb_dlink_list_length(&blacklist_list)) {
370 /* Nothing to do... */
371 provider_done(auth, SELF_PID);
372 return true;
373 }
374
375 auth_client_ref(auth);
376
377 set_provider_data(auth, SELF_PID, rb_malloc(sizeof(struct blacklist_user)));
378
379 if (run_after_provider(auth, "rdns") && run_after_provider(auth, "ident")) {
380 /* Start the lookup if ident and rdns are finished, or not loaded. */
381 if (!lookup_all_blacklists(auth)) {
382 blacklists_cancel_none(auth);
383 return true;
384 }
385 }
386
387 return true;
388 }
389
390 /* This is called every time a provider is completed as long as we are marked not done */
391 static void
392 blacklists_initiate(struct auth_client *auth, uint32_t provider)
393 {
394 struct blacklist_user *bluser = get_provider_data(auth, SELF_PID);
395
396 lrb_assert(provider != SELF_PID);
397 lrb_assert(!is_provider_done(auth, SELF_PID));
398 lrb_assert(rb_dlink_list_length(&blacklist_list) > 0);
399
400 if (bluser == NULL || bluser->started) {
401 /* Nothing to do */
402 return;
403 } else if (run_after_provider(auth, "rdns") && run_after_provider(auth, "ident")) {
404 /* Start the lookup if ident and rdns are finished, or not loaded. */
405 if (!lookup_all_blacklists(auth)) {
406 blacklists_cancel_none(auth);
407 }
408 }
409 }
410
411 static inline void
412 blacklists_generic_cancel(struct auth_client *auth, const char *message)
413 {
414 rb_dlink_node *ptr, *nptr;
415 struct blacklist_user *bluser = get_provider_data(auth, SELF_PID);
416
417 if(bluser == NULL)
418 return;
419
420 if(rb_dlink_list_length(&bluser->queries))
421 {
422 notice_client(auth->cid, message);
423
424 RB_DLINK_FOREACH_SAFE(ptr, nptr, bluser->queries.head)
425 {
426 struct blacklist_lookup *bllookup = ptr->data;
427
428 cancel_query(bllookup->query);
429 unref_blacklist(bllookup->bl);
430
431 rb_dlinkDelete(&bllookup->node, &bluser->queries);
432 rb_free(bllookup);
433 }
434 }
435
436 rb_free(bluser);
437 set_provider_data(auth, SELF_PID, NULL);
438 set_provider_timeout_absolute(auth, SELF_PID, 0);
439 provider_done(auth, SELF_PID);
440
441 auth_client_unref(auth);
442 }
443
444 static void
445 blacklists_timeout(struct auth_client *auth)
446 {
447 blacklists_generic_cancel(auth, "*** No response from DNS blacklists");
448 }
449
450 static void
451 blacklists_cancel(struct auth_client *auth)
452 {
453 blacklists_generic_cancel(auth, "*** Aborting DNS blacklist checks");
454 }
455
456 static void
457 blacklists_cancel_none(struct auth_client *auth)
458 {
459 blacklists_generic_cancel(auth, "*** Could not check DNS blacklists");
460 }
461
462 static void
463 blacklists_destroy(void)
464 {
465 rb_dictionary_iter iter;
466 struct auth_client *auth;
467
468 RB_DICTIONARY_FOREACH(auth, &iter, auth_clients)
469 {
470 blacklists_cancel(auth);
471 /* auth is now invalid as we have no reference */
472 }
473
474 delete_all_blacklists();
475 }
476
477 static void
478 add_conf_blacklist(const char *key, int parc, const char **parv)
479 {
480 rb_dlink_list filters = { NULL, NULL, 0 };
481 char *tmp, *elemlist = rb_strdup(parv[2]);
482 uint8_t iptype;
483
484 if(*elemlist == '*')
485 goto end;
486
487 for(char *elem = rb_strtok_r(elemlist, ",", &tmp); elem; elem = rb_strtok_r(NULL, ",", &tmp))
488 {
489 struct blacklist_filter *filter = rb_malloc(sizeof(struct blacklist_filter));
490 int dot_c = 0;
491 filter_t type = FILTER_LAST;
492
493 /* Check blacklist filter type and for validity */
494 for(char *c = elem; *c != '\0'; c++)
495 {
496 if(*c == '.')
497 {
498 if(++dot_c > 3)
499 {
500 warn_opers(L_CRIT, "Blacklist: addr_conf_blacklist got a bad filter (too many octets)");
501 exit(EX_PROVIDER_ERROR);
502 }
503
504 type = FILTER_ALL;
505 }
506 else if(!isdigit(*c))
507 {
508 warn_opers(L_CRIT, "Blacklist: addr_conf_blacklist got a bad filter (invalid character in blacklist filter: %c)",
509 *c);
510 exit(EX_PROVIDER_ERROR);
511 }
512 }
513
514 if(dot_c > 0 && dot_c < 3)
515 {
516 warn_opers(L_CRIT, "Blacklist: addr_conf_blacklist got a bad filter (insufficient octets)");
517 exit(EX_PROVIDER_ERROR);
518 }
519
520 filter->type = type;
521 rb_strlcpy(filter->filter, elem, sizeof(filter->filter));
522 rb_dlinkAdd(filter, &filter->node, &filters);
523 }
524
525 end:
526 rb_free(elemlist);
527
528 iptype = atoi(parv[1]) & 0x3;
529 if(new_blacklist(parv[0], parv[3], iptype, &filters) == NULL)
530 {
531 warn_opers(L_CRIT, "Blacklist: addr_conf_blacklist got a malformed blacklist");
532 exit(EX_PROVIDER_ERROR);
533 }
534 }
535
536 static void
537 del_conf_blacklist(const char *key, int parc, const char **parv)
538 {
539 struct blacklist *bl = find_blacklist(parv[0]);
540 if(bl == NULL)
541 {
542 /* Not fatal for now... */
543 warn_opers(L_WARN, "Blacklist: tried to remove nonexistent blacklist %s", parv[0]);
544 return;
545 }
546
547 delete_blacklist(bl);
548 }
549
550 static void
551 del_conf_blacklist_all(const char *key, int parc, const char **parv)
552 {
553 delete_all_blacklists();
554 }
555
556 static void
557 add_conf_blacklist_timeout(const char *key, int parc, const char **parv)
558 {
559 int timeout = atoi(parv[0]);
560
561 if(timeout < 0)
562 {
563 warn_opers(L_CRIT, "Blacklist: blacklist timeout < 0 (value: %d)", timeout);
564 exit(EX_PROVIDER_ERROR);
565 }
566
567 blacklist_timeout = timeout;
568 }
569
570 #if 0
571 static void
572 blacklist_stats(uint32_t rid, char letter)
573 {
574 rb_dlink_node *ptr;
575
576 RB_DLINK_FOREACH(ptr, blacklist_list.head)
577 {
578 struct blacklist *bl = ptr->data;
579
580 if(bl->delete)
581 continue;
582
583 stats_result(rid, letter, "%s %hhu %u", bl->host, bl->iptype, bl->hits);
584 }
585
586 stats_done(rid, letter);
587 }
588 #endif
589
590 struct auth_opts_handler blacklist_options[] =
591 {
592 { "rbl", 4, add_conf_blacklist },
593 { "rbl_del", 1, del_conf_blacklist },
594 { "rbl_del_all", 0, del_conf_blacklist_all },
595 { "rbl_timeout", 1, add_conf_blacklist_timeout },
596 { NULL, 0, NULL },
597 };
598
599 struct auth_provider blacklist_provider =
600 {
601 .name = "blacklist",
602 .letter = 'B',
603 .destroy = blacklists_destroy,
604 .start = blacklists_start,
605 .cancel = blacklists_cancel,
606 .timeout = blacklists_timeout,
607 .completed = blacklists_initiate,
608 .opt_handlers = blacklist_options,
609 /* .stats_handler = { 'B', blacklist_stats }, */
610 };