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