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