]> jfr.im git - irc/rqf/shadowircd.git/blob - modules/m_gline.c
[svn] - the new plan:
[irc/rqf/shadowircd.git] / modules / m_gline.c
1 /*
2 * ircd-ratbox: A slightly useful ircd.
3 * m_gline.c: Votes towards globally banning a mask.
4 *
5 * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
6 * Copyright (C) 1996-2002 Hybrid Development Team
7 * Copyright (C) 2002-2005 ircd-ratbox development team
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 * USA
23 *
24 * $Id: m_gline.c 1146 2006-04-07 22:52:35Z jilles $
25 */
26
27 #include "stdinc.h"
28 #include "tools.h"
29 #include "s_gline.h"
30 #include "channel.h"
31 #include "client.h"
32 #include "common.h"
33 #include "config.h"
34 #include "irc_string.h"
35 #include "sprintf_irc.h"
36 #include "ircd.h"
37 #include "hostmask.h"
38 #include "numeric.h"
39 #include "commio.h"
40 #include "s_conf.h"
41 #include "s_newconf.h"
42 #include "scache.h"
43 #include "send.h"
44 #include "msg.h"
45 #include "s_serv.h"
46 #include "hash.h"
47 #include "parse.h"
48 #include "modules.h"
49 #include "s_log.h"
50
51 static int mo_gline(struct Client *, struct Client *, int, const char **);
52 static int mc_gline(struct Client *, struct Client *, int, const char **);
53 static int ms_gline(struct Client *, struct Client *, int, const char **);
54 static int mo_ungline(struct Client *, struct Client *, int, const char **);
55
56 struct Message gline_msgtab = {
57 "GLINE", 0, 0, 0, MFLG_SLOW,
58 {mg_unreg, mg_not_oper, {mc_gline, 3}, {ms_gline, 7}, mg_ignore, {mo_gline, 3}}
59 };
60 struct Message ungline_msgtab = {
61 "UNGLINE", 0, 0, 0, MFLG_SLOW,
62 {mg_unreg, mg_not_oper, mg_ignore, mg_ignore, mg_ignore, {mo_ungline, 2}}
63 };
64
65 mapi_clist_av1 gline_clist[] = { &gline_msgtab, &ungline_msgtab, NULL };
66 DECLARE_MODULE_AV1(gline, NULL, NULL, gline_clist, NULL, NULL, "$Revision: 1146 $");
67
68 static int majority_gline(struct Client *source_p, const char *user,
69 const char *host, const char *reason);
70 static void set_local_gline(struct Client *source_p, const char *user,
71 const char *host, const char *reason);
72
73 static int check_wild_gline(const char *, const char *);
74 static int invalid_gline(struct Client *, const char *, const char *, char *);
75
76 static int remove_temp_gline(const char *, const char *);
77
78
79 /* mo_gline()
80 *
81 * inputs - The usual for a m_ function
82 * output -
83 * side effects - place a gline if 3 opers agree
84 */
85 static int
86 mo_gline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
87 {
88 const char *user = NULL;
89 char *host = NULL; /* user and host of GLINE "victim" */
90 char *reason = NULL; /* reason for "victims" demise */
91 char splat[] = "*";
92 char *ptr;
93
94 if(!ConfigFileEntry.glines)
95 {
96 sendto_one(source_p, ":%s NOTICE %s :GLINE disabled, perhaps you want a clustered or remote KLINE?",
97 me.name, source_p->name);
98 return 0;
99 }
100
101 if(!IsOperGline(source_p))
102 {
103 sendto_one(source_p, form_str(ERR_NOPRIVS),
104 me.name, source_p->name, "gline");
105 return 0;
106 }
107
108 host = strchr(parv[1], '@');
109
110 /* specific user@host */
111 if(host != NULL)
112 {
113 user = parv[1];
114 *(host++) = '\0';
115
116 /* gline for "@host", use *@host */
117 if(*user == '\0')
118 user = splat;
119 }
120 /* just a host? */
121 else
122 {
123 /* ok, its not a host.. abort */
124 if(strchr(parv[1], '.') == NULL)
125 {
126 sendto_one(source_p,
127 ":%s NOTICE %s :Invalid parameters",
128 me.name, source_p->name);
129 return 0;
130 }
131
132 user = splat;
133 host = LOCAL_COPY(parv[1]);
134 }
135
136 reason = LOCAL_COPY(parv[2]);
137
138 if(invalid_gline(source_p, user, host, reason))
139 return 0;
140
141 /* Not enough non-wild characters were found, assume they are trying to gline *@*. */
142 if(check_wild_gline(user, host))
143 {
144 if(MyClient(source_p))
145 sendto_one(source_p,
146 ":%s NOTICE %s :Please include at least %d non-wildcard "
147 "characters with the user@host",
148 me.name, source_p->name,
149 ConfigFileEntry.min_nonwildcard);
150 return 0;
151 }
152
153 if((ptr = strchr(host, '/')) != NULL)
154 {
155 int bitlen;
156 bitlen = strtol(++ptr, NULL, 10);
157
158 /* ipv4? */
159 if(strchr(host, ':') == NULL)
160 {
161 if(bitlen < ConfigFileEntry.gline_min_cidr)
162 {
163 sendto_one(source_p, ":%s NOTICE %s :Cannot set G-Lines with cidr length < %d",
164 me.name, source_p->name,
165 ConfigFileEntry.gline_min_cidr);
166 return 0;
167 }
168 }
169 /* ipv6 */
170 else if(bitlen < ConfigFileEntry.gline_min_cidr6)
171 {
172 sendto_one(source_p, ":%s NOTICE %s :Cannot set G-Lines with cidr length < %d",
173 me.name, source_p->name,
174 ConfigFileEntry.gline_min_cidr6);
175 return 0;
176 }
177 }
178
179 /* inform users about the gline before we call majority_gline()
180 * so already voted comes below gline request --fl
181 */
182 sendto_realops_snomask(SNO_GENERAL, L_ALL,
183 "%s!%s@%s on %s is requesting gline for [%s@%s] [%s]",
184 source_p->name, source_p->username,
185 source_p->host, me.name, user, host, reason);
186 ilog(L_GLINE, "R %s %s %s %s %s %s %s",
187 source_p->name, source_p->username, source_p->host,
188 source_p->user->server, user, host, reason);
189
190 /* If at least 3 opers agree this user should be G lined then do it */
191 majority_gline(source_p, user, host, reason);
192
193 /* 4 param version for hyb-7 servers */
194 sendto_server(NULL, NULL, CAP_GLN|CAP_TS6, NOCAPS,
195 ":%s GLINE %s %s :%s",
196 use_id(source_p), user, host, reason);
197 sendto_server(NULL, NULL, CAP_GLN, CAP_TS6,
198 ":%s GLINE %s %s :%s",
199 source_p->name, user, host, reason);
200
201 /* 8 param for hyb-6 */
202 sendto_server(NULL, NULL, NOCAPS, CAP_GLN,
203 ":%s GLINE %s %s %s %s %s %s :%s",
204 me.name, source_p->name, source_p->username,
205 source_p->host, source_p->user->server,
206 user, host, reason);
207 return 0;
208 }
209
210 /* mc_gline()
211 */
212 static int
213 mc_gline(struct Client *client_p, struct Client *source_p,
214 int parc, const char *parv[])
215 {
216 struct Client *acptr;
217 const char *user;
218 const char *host;
219 char *reason;
220 char *ptr;
221
222 /* hyb6 allows empty gline reasons */
223 if(parc < 4 || EmptyString(parv[3]))
224 return 0;
225
226 acptr = source_p;
227
228 user = parv[1];
229 host = parv[2];
230 reason = LOCAL_COPY(parv[3]);
231
232 if(invalid_gline(acptr, user, host, reason))
233 return 0;
234
235 sendto_server(client_p, NULL, CAP_GLN|CAP_TS6, NOCAPS,
236 ":%s GLINE %s %s :%s",
237 use_id(acptr), user, host, reason);
238 sendto_server(client_p, NULL, CAP_GLN, CAP_TS6,
239 ":%s GLINE %s %s :%s",
240 acptr->name, user, host, reason);
241 sendto_server(client_p, NULL, NOCAPS, CAP_GLN,
242 ":%s GLINE %s %s %s %s %s %s :%s",
243 acptr->user->server, acptr->name,
244 acptr->username, acptr->host,
245 acptr->user->server, user, host, reason);
246
247 if(!ConfigFileEntry.glines)
248 return 0;
249
250 /* check theres enough non-wildcard chars */
251 if(check_wild_gline(user, host))
252 {
253 sendto_realops_snomask(SNO_GENERAL, L_ALL,
254 "%s!%s@%s on %s is requesting a gline without "
255 "%d non-wildcard characters for [%s@%s] [%s]",
256 acptr->name, acptr->username,
257 acptr->host, acptr->user->server,
258 ConfigFileEntry.min_nonwildcard,
259 user, host, reason);
260 return 0;
261 }
262
263 if((ptr = strchr(host, '/')) != NULL)
264 {
265 int bitlen;
266 bitlen = strtol(++ptr, NULL, 10);
267
268 /* ipv4? */
269 if(strchr(host, ':') == NULL)
270 {
271 if(bitlen < ConfigFileEntry.gline_min_cidr)
272 {
273 sendto_realops_snomask(SNO_GENERAL, L_ALL, "%s!%s@%s on %s is requesting a "
274 "gline with a cidr mask < %d for [%s@%s] [%s]",
275 acptr->name, acptr->username, acptr->host,
276 acptr->user->server,
277 ConfigFileEntry.gline_min_cidr,
278 user, host, reason);
279 return 0;
280 }
281 }
282 /* ipv6 */
283 else if(bitlen < ConfigFileEntry.gline_min_cidr6)
284 {
285 sendto_realops_snomask(SNO_GENERAL, L_ALL, "%s!%s@%s on %s is requesting a "
286 "gline with a cidr mask < %d for [%s@%s] [%s]",
287 acptr->name, acptr->username, acptr->host,
288 acptr->user->server,
289 ConfigFileEntry.gline_min_cidr6,
290 user, host, reason);
291 return 0;
292 }
293 }
294
295
296 sendto_realops_snomask(SNO_GENERAL, L_ALL,
297 "%s!%s@%s on %s is requesting gline for [%s@%s] [%s]",
298 acptr->name, acptr->username, acptr->host,
299 acptr->user->server, user, host, reason);
300
301 ilog(L_GLINE, "R %s %s %s %s %s %s %s",
302 source_p->name, source_p->username, source_p->host,
303 source_p->user->server, user, host, reason);
304
305 /* If at least 3 opers agree this user should be G lined then do it */
306 majority_gline(acptr, user, host, reason);
307
308 return 0;
309 }
310
311
312 /* ms_gline()
313 *
314 * inputs - The usual for a m_ function
315 * output -
316 * side effects - attempts to place a gline, if 3 opers agree
317 */
318 static int
319 ms_gline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
320 {
321 struct Client *acptr;
322 const char *user;
323 const char *host;
324 char *reason;
325
326 /* hyb6 allows empty gline reasons */
327 if(parc < 8 || EmptyString(parv[7]))
328 return 0;
329
330 /* client doesnt exist.. someones messing */
331 if((acptr = find_client(parv[1])) == NULL)
332 return 0;
333
334 /* client that sent the gline, isnt on the server that sent
335 * the gline out. somethings fucked.
336 */
337 if(acptr->servptr != source_p)
338 return 0;
339
340 user = parv[5];
341 host = parv[6];
342 reason = LOCAL_COPY(parv[7]);
343
344 if(invalid_gline(acptr, user, host, reason))
345 return 0;
346
347 sendto_server(client_p, NULL, CAP_GLN|CAP_TS6, NOCAPS,
348 ":%s GLINE %s %s :%s",
349 use_id(acptr), user, host, reason);
350 sendto_server(client_p, NULL, CAP_GLN, CAP_TS6,
351 ":%s GLINE %s %s :%s",
352 acptr->name, user, host, reason);
353 sendto_server(client_p, NULL, NOCAPS, CAP_GLN,
354 ":%s GLINE %s %s %s %s %s %s :%s",
355 acptr->user->server, acptr->name,
356 acptr->username, acptr->host,
357 acptr->user->server, user, host, reason);
358
359 if(!ConfigFileEntry.glines)
360 return 0;
361
362 /* check theres enough non-wildcard chars */
363 if(check_wild_gline(user, host))
364 {
365 sendto_realops_snomask(SNO_GENERAL, L_ALL,
366 "%s!%s@%s on %s is requesting a gline without "
367 "%d non-wildcard characters for [%s@%s] [%s]",
368 acptr->name, acptr->username,
369 acptr->host, acptr->user->server,
370 ConfigFileEntry.min_nonwildcard,
371 user, host, reason);
372 return 0;
373 }
374
375 sendto_realops_snomask(SNO_GENERAL, L_ALL,
376 "%s!%s@%s on %s is requesting gline for [%s@%s] [%s]",
377 acptr->name, acptr->username, acptr->host,
378 acptr->user->server, user, host, reason);
379
380 ilog(L_GLINE, "R %s %s %s %s %s %s %s",
381 acptr->name, acptr->username, acptr->host,
382 acptr->user->server, user, host, reason);
383
384 /* If at least 3 opers agree this user should be G lined then do it */
385 majority_gline(acptr, user, host, reason);
386
387 return 0;
388 }
389
390 /* mo_ungline()
391 *
392 * parv[0] = sender nick
393 * parv[1] = gline to remove
394 */
395 static int
396 mo_ungline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
397 {
398 const char *user;
399 char *h = LOCAL_COPY(parv[1]);
400 char *host;
401 char splat[] = "*";
402
403 if(!ConfigFileEntry.glines)
404 {
405 sendto_one(source_p, ":%s NOTICE %s :UNGLINE disabled, perhaps you want UNKLINE?", me.name, parv[0]);
406 return 0;
407 }
408
409 if(!IsOperUnkline(source_p) || !IsOperGline(source_p))
410 {
411 sendto_one(source_p, form_str(ERR_NOPRIVS),
412 me.name, source_p->name, "unkline");
413 return 0;
414 }
415
416 if((host = strchr(h, '@')) || *h == '*')
417 {
418 /* Explicit user@host mask given */
419
420 if(host)
421 {
422 *host++ = '\0';
423
424 /* check for @host */
425 if(*h)
426 user = h;
427 else
428 user = splat;
429
430 if(!*host)
431 host = splat;
432 }
433 else
434 {
435 user = splat;
436 host = h;
437 }
438 }
439 else
440 {
441 sendto_one(source_p, ":%s NOTICE %s :Invalid parameters", me.name, parv[0]);
442 return 0;
443 }
444
445 if(remove_temp_gline(user, host))
446 {
447 sendto_one(source_p, ":%s NOTICE %s :Un-glined [%s@%s]",
448 me.name, parv[0], user, host);
449 sendto_realops_snomask(SNO_GENERAL, L_ALL,
450 "%s has removed the G-Line for: [%s@%s]",
451 get_oper_name(source_p), user, host);
452 ilog(L_GLINE, "U %s %s %s %s %s %s",
453 source_p->name, source_p->username, source_p->host,
454 source_p->user->server, user, host);
455 }
456 else
457 {
458 sendto_one(source_p, ":%s NOTICE %s :No G-Line for %s@%s",
459 me.name, parv[0], user, host);
460 }
461
462 return 0;
463 }
464
465 /*
466 * check_wild_gline
467 *
468 * inputs - user, host of gline
469 * output - 1 if not enough non-wildchar char's, 0 if ok
470 * side effects - NONE
471 */
472 static int
473 check_wild_gline(const char *user, const char *host)
474 {
475 const char *p;
476 char tmpch;
477 int nonwild;
478
479 nonwild = 0;
480 p = user;
481
482 while ((tmpch = *p++))
483 {
484 if(!IsKWildChar(tmpch))
485 {
486 /* enough of them, break */
487 if(++nonwild >= ConfigFileEntry.min_nonwildcard)
488 break;
489 }
490 }
491
492 if(nonwild < ConfigFileEntry.min_nonwildcard)
493 {
494 /* user doesnt, try host */
495 p = host;
496 while ((tmpch = *p++))
497 {
498 if(!IsKWildChar(tmpch))
499 if(++nonwild >= ConfigFileEntry.min_nonwildcard)
500 break;
501 }
502 }
503
504 if(nonwild < ConfigFileEntry.min_nonwildcard)
505 return 1;
506 else
507 return 0;
508 }
509
510 /* invalid_gline
511 *
512 * inputs - pointer to source client, ident, host and reason
513 * outputs - 1 if invalid, 0 if valid
514 * side effects -
515 */
516 static int
517 invalid_gline(struct Client *source_p, const char *luser,
518 const char *lhost, char *lreason)
519 {
520 if(strchr(luser, '!'))
521 {
522 sendto_one(source_p, ":%s NOTICE %s :Invalid character '!' in gline",
523 me.name, source_p->name);
524 return 1;
525 }
526
527 if(strlen(lreason) > REASONLEN)
528 lreason[REASONLEN] = '\0';
529
530 return 0;
531 }
532
533 /*
534 * set_local_gline
535 *
536 * inputs - pointer to oper nick/username/host/server,
537 * victim user/host and reason
538 * output - NONE
539 * side effects -
540 */
541 static void
542 set_local_gline(struct Client *source_p, const char *user,
543 const char *host, const char *reason)
544 {
545 char buffer[IRCD_BUFSIZE];
546 struct ConfItem *aconf;
547 const char *current_date;
548 char *my_reason;
549 char *oper_reason;
550
551 current_date = smalldate();
552
553 my_reason = LOCAL_COPY(reason);
554
555 aconf = make_conf();
556 aconf->status = CONF_GLINE;
557 aconf->flags |= CONF_FLAGS_TEMPORARY;
558
559 if(strlen(my_reason) > REASONLEN)
560 my_reason[REASONLEN-1] = '\0';
561
562 if((oper_reason = strchr(my_reason, '|')) != NULL)
563 {
564 *oper_reason = '\0';
565 oper_reason++;
566
567 if(!EmptyString(oper_reason))
568 DupString(aconf->spasswd, oper_reason);
569 }
570
571 ircsnprintf(buffer, sizeof(buffer), "%s (%s)", reason, current_date);
572
573 DupString(aconf->passwd, buffer);
574 DupString(aconf->user, user);
575 DupString(aconf->host, host);
576 aconf->hold = CurrentTime + ConfigFileEntry.gline_time;
577 add_gline(aconf);
578
579 sendto_realops_snomask(SNO_GENERAL, L_ALL,
580 "%s!%s@%s on %s has triggered gline for [%s@%s] [%s]",
581 source_p->name, source_p->username,
582 source_p->host, source_p->user->server,
583 user, host, reason);
584 ilog(L_GLINE, "T %s %s %s %s %s %s %s",
585 source_p->name, source_p->username, source_p->host,
586 source_p->user->server, user, host, reason);
587
588 check_glines();
589 }
590
591 /* majority_gline()
592 *
593 * input - client doing gline, user, host and reason of gline
594 * output - YES if there are 3 different opers/servers agree, else NO
595 * side effects -
596 */
597 static int
598 majority_gline(struct Client *source_p, const char *user,
599 const char *host, const char *reason)
600 {
601 dlink_node *pending_node;
602 struct gline_pending *pending;
603
604 /* to avoid desync.. --fl */
605 cleanup_glines(NULL);
606
607 /* if its already glined, why bother? :) -- fl_ */
608 if(find_is_glined(host, user))
609 return NO;
610
611 DLINK_FOREACH(pending_node, pending_glines.head)
612 {
613 pending = pending_node->data;
614
615 if((irccmp(pending->user, user) == 0) &&
616 (irccmp(pending->host, host) == 0))
617 {
618 /* check oper or server hasnt already voted */
619 if(((irccmp(pending->oper_user1, source_p->username) == 0) ||
620 (irccmp(pending->oper_host1, source_p->host) == 0)))
621 {
622 sendto_realops_snomask(SNO_GENERAL, L_ALL, "oper has already voted");
623 return NO;
624 }
625 else if(irccmp(pending->oper_server1, source_p->user->server) == 0)
626 {
627 sendto_realops_snomask(SNO_GENERAL, L_ALL, "server has already voted");
628 return NO;
629 }
630
631 if(pending->oper_user2[0] != '\0')
632 {
633 /* if two other opers on two different servers have voted yes */
634 if(((irccmp(pending->oper_user2, source_p->username) == 0) ||
635 (irccmp(pending->oper_host2, source_p->host) == 0)))
636 {
637 sendto_realops_snomask(SNO_GENERAL, L_ALL,
638 "oper has already voted");
639 return NO;
640 }
641 else if(irccmp(pending->oper_server2, source_p->user->server) == 0)
642 {
643 sendto_realops_snomask(SNO_GENERAL, L_ALL,
644 "server has already voted");
645 return NO;
646 }
647
648 /* trigger the gline using the original reason --fl */
649 set_local_gline(source_p, user, host,
650 pending->reason1);
651
652 cleanup_glines(NULL);
653 return YES;
654 }
655 else
656 {
657 strlcpy(pending->oper_nick2, source_p->name,
658 sizeof(pending->oper_nick2));
659 strlcpy(pending->oper_user2, source_p->username,
660 sizeof(pending->oper_user2));
661 strlcpy(pending->oper_host2, source_p->host,
662 sizeof(pending->oper_host2));
663 DupString(pending->reason2, reason);
664 pending->oper_server2 = find_or_add(source_p->user->server);
665 pending->last_gline_time = CurrentTime;
666 pending->time_request2 = CurrentTime;
667 return NO;
668 }
669 }
670 }
671
672 /* no pending gline, create a new one */
673 pending = (struct gline_pending *)
674 MyMalloc(sizeof(struct gline_pending));
675
676 strlcpy(pending->oper_nick1, source_p->name,
677 sizeof(pending->oper_nick1));
678 strlcpy(pending->oper_user1, source_p->username,
679 sizeof(pending->oper_user1));
680 strlcpy(pending->oper_host1, source_p->host,
681 sizeof(pending->oper_host1));
682
683 pending->oper_server1 = find_or_add(source_p->user->server);
684
685 strlcpy(pending->user, user, sizeof(pending->user));
686 strlcpy(pending->host, host, sizeof(pending->host));
687 DupString(pending->reason1, reason);
688 pending->reason2 = NULL;
689
690 pending->last_gline_time = CurrentTime;
691 pending->time_request1 = CurrentTime;
692
693 dlinkAddAlloc(pending, &pending_glines);
694
695 return NO;
696 }
697
698 /* remove_temp_gline()
699 *
700 * inputs - username, hostname to ungline
701 * outputs -
702 * side effects - tries to ungline anything that matches
703 */
704 static int
705 remove_temp_gline(const char *user, const char *host)
706 {
707 struct ConfItem *aconf;
708 dlink_node *ptr;
709 struct irc_sockaddr_storage addr, caddr;
710 int bits, cbits;
711 int mtype, gtype;
712
713 mtype = parse_netmask(host, (struct sockaddr *)&addr, &bits);
714
715 DLINK_FOREACH(ptr, glines.head)
716 {
717 aconf = ptr->data;
718
719 gtype = parse_netmask(aconf->host, (struct sockaddr *)&caddr, &cbits);
720
721 if(gtype != mtype || (user && irccmp(user, aconf->user)))
722 continue;
723
724 if(gtype == HM_HOST)
725 {
726 if(irccmp(aconf->host, host))
727 continue;
728 }
729 else if(bits != cbits ||
730 !comp_with_mask_sock((struct sockaddr *)&addr,
731 (struct sockaddr *)&caddr, bits))
732 continue;
733
734 dlinkDestroy(ptr, &glines);
735 delete_one_address_conf(aconf->host, aconf);
736 return YES;
737 }
738
739 return NO;
740 }