ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/modules/m_gline.c
Revision: 3299
Committed: Sat Apr 12 16:55:13 2014 UTC (11 years, 4 months ago) by michael
Content type: text/x-csrc
File size: 17456 byte(s)
Log Message:
- doxygen

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 *
4 * Copyright (c) 1997-2014 ircd-hybrid development team
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 * USA
20 */
21
22 /*! \file m_gline.c
23 * \brief Includes required functions for processing the GLINE/GUNGLINE command.
24 * \version $Id$
25 */
26
27 #include "stdinc.h"
28 #include "list.h"
29 #include "s_gline.h"
30 #include "client.h"
31 #include "irc_string.h"
32 #include "ircd.h"
33 #include "conf.h"
34 #include "hostmask.h"
35 #include "numeric.h"
36 #include "s_misc.h"
37 #include "send.h"
38 #include "s_serv.h"
39 #include "parse.h"
40 #include "modules.h"
41 #include "log.h"
42 #include "conf_db.h"
43 #include "memory.h"
44
45 #define GLINE_NOT_PLACED 0
46 #define GLINE_ALREADY_VOTED -1
47 #define GLINE_PLACED 1
48
49
50 static void
51 check_gline(struct AddressRec *arec)
52 {
53 dlink_node *ptr = NULL, *ptr_next = NULL;
54
55 DLINK_FOREACH_SAFE(ptr, ptr_next, local_client_list.head)
56 {
57 struct Client *client_p = ptr->data;
58
59 if (IsDead(client_p))
60 continue;
61
62 if (match(arec->username, client_p->username))
63 continue;
64
65 switch (arec->masktype)
66 {
67 case HM_IPV4:
68 if (client_p->localClient->aftype == AF_INET)
69 if (match_ipv4(&client_p->localClient->ip, &arec->Mask.ipa.addr, arec->Mask.ipa.bits))
70 conf_try_ban(client_p, arec->conf);
71 break;
72 case HM_IPV6:
73 if (client_p->localClient->aftype == AF_INET6)
74 if (match_ipv6(&client_p->localClient->ip, &arec->Mask.ipa.addr, arec->Mask.ipa.bits))
75 conf_try_ban(client_p, arec->conf);
76 break;
77 default: /* HM_HOST */
78 if (!match(arec->Mask.hostname, client_p->host))
79 conf_try_ban(client_p, arec->conf);
80 break;
81 }
82 }
83 }
84
85 /*! \brief Adds a GLINE to the configuration subsystem.
86 *
87 * \param source_p Operator requesting gline
88 * \param user Username covered by the gline
89 * \param host Hostname covered by the gline
90 * \param reason Reason for the gline
91 */
92 static void
93 set_local_gline(const struct Client *source_p, const char *user,
94 const char *host, const char *reason)
95 {
96 char buffer[IRCD_BUFSIZE];
97 struct MaskItem *conf = conf_make(CONF_GLINE);
98
99 snprintf(buffer, sizeof(buffer), "%s (%s)", reason, smalldate(0));
100 conf->reason = xstrdup(buffer);
101 conf->user = xstrdup(user);
102 conf->host = xstrdup(host);
103
104 conf->setat = CurrentTime;
105 conf->until = CurrentTime + ConfigFileEntry.gline_time;
106 SetConfDatabase(conf);
107
108 sendto_realops_flags(UMODE_ALL, L_ALL, SEND_NOTICE,
109 "%s added G-Line for [%s@%s] [%s]",
110 get_oper_name(source_p),
111 conf->user, conf->host, conf->reason);
112 ilog(LOG_TYPE_GLINE, "%s added G-Line for [%s@%s] [%s]",
113 get_oper_name(source_p), conf->user, conf->host, conf->reason);
114
115 check_gline(add_conf_by_address(CONF_GLINE, conf));
116 }
117
118 /*! \brief Removes a GLINE from the configuration subsystem.
119 *
120 * \param user Username covered by the gline
121 * \param host Hostname covered by the gline
122 */
123 static int
124 remove_gline_match(const char *user, const char *host)
125 {
126 struct irc_ssaddr iphost, *piphost;
127 struct MaskItem *conf;
128 int t = 0;
129 int aftype = 0;
130
131 if ((t = parse_netmask(host, &iphost, NULL)) != HM_HOST)
132 {
133 #ifdef IPV6
134 if (t == HM_IPV6)
135 aftype = AF_INET6;
136 else
137 #endif
138 aftype = AF_INET;
139 piphost = &iphost;
140 }
141 else
142 piphost = NULL;
143
144 if ((conf = find_conf_by_address(host, piphost, CONF_GLINE, aftype, user, NULL, 0)))
145 {
146 if (IsConfDatabase(conf))
147 {
148 delete_one_address_conf(host, conf);
149 return 1;
150 }
151 }
152
153 return 0;
154 }
155
156 /*! \brief This function is called once a majority of opers have agreed on a
157 * GLINE/GUNGLINE, and it can be placed. The information about an
158 * operator being passed to us happens to be the operator who pushed us
159 * over the "majority" level needed. See check_majority() for more
160 * information.
161 *
162 * \param source_p Operator requesting gline
163 * \param user Username covered by the gline
164 * \param host Hostname covered by the gline
165 * \param reason Reason for the gline
166 * \param type Valid values are either GLINE_PENDING_ADD_TYPE, or
167 * GLINE_PENDING_DEL_TYPE
168 */
169 static void
170 add_new_majority(const struct Client *source_p, const char *user,
171 const char *host, const char *reason, const unsigned int type)
172 {
173 struct gline_pending *pending = MyMalloc(sizeof(struct gline_pending));
174
175 strlcpy(pending->vote_1.oper_nick, source_p->name, sizeof(pending->vote_1.oper_nick));
176 strlcpy(pending->vote_1.oper_user, source_p->username, sizeof(pending->vote_1.oper_user));
177 strlcpy(pending->vote_1.oper_host, source_p->host, sizeof(pending->vote_1.oper_host));
178 strlcpy(pending->vote_1.oper_server, source_p->servptr->name, sizeof(pending->vote_1.oper_server));
179
180 strlcpy(pending->user, user, sizeof(pending->user));
181 strlcpy(pending->host, host, sizeof(pending->host));
182 strlcpy(pending->vote_1.reason, reason, sizeof(pending->vote_1.reason));
183
184 pending->last_gline_time = CurrentTime;
185 pending->vote_1.time_request = CurrentTime;
186
187 dlinkAdd(pending, &pending->node, &pending_glines[type]);
188 }
189
190 /*! \brief See if there is a majority agreement on a GLINE on the given user.
191 * There must be at least 3 different opers agreeing on this
192 * GLINE/GUNGLINE
193 *
194 * \param source_p Operator requesting gline
195 * \param user Username covered by the gline
196 * \param host Hostname covered by the gline
197 * \param reason Reason for the gline
198 * \param type Valid values are either GLINE_PENDING_ADD_TYPE, or
199 * GLINE_PENDING_DEL_TYPE
200 *
201 * \return
202 * - GLINE_ALREADY_VOTED returned if oper/server has already voted
203 * - GLINE_PLACED returned if this triggers a gline
204 * - GLINE_NOT_PLACED returned if not triggered
205 */
206 static int
207 check_majority(const struct Client *source_p, const char *user,
208 const char *host, const char *reason, const int type)
209 {
210 dlink_node *dn_ptr = NULL;
211
212 cleanup_glines(NULL);
213
214 /* if its already glined, why bother? :) -- fl_ */
215 if ((type == GLINE_PENDING_ADD_TYPE) && find_is_glined(host, user))
216 return GLINE_NOT_PLACED;
217
218 DLINK_FOREACH(dn_ptr, pending_glines[type].head)
219 {
220 struct gline_pending *gp_ptr = dn_ptr->data;
221
222 if (irccmp(gp_ptr->user, user) ||
223 irccmp(gp_ptr->host, host))
224 continue;
225
226 if ((!irccmp(gp_ptr->vote_1.oper_user, source_p->username) &&
227 !irccmp(gp_ptr->vote_1.oper_host, source_p->host)) ||
228 !irccmp(gp_ptr->vote_1.oper_server, source_p->servptr->name))
229 return GLINE_ALREADY_VOTED;
230
231 if (gp_ptr->vote_2.oper_user[0] != '\0')
232 {
233 /* if two other opers on two different servers have voted yes */
234 if ((!irccmp(gp_ptr->vote_2.oper_user, source_p->username) &&
235 !irccmp(gp_ptr->vote_2.oper_host, source_p->host)) ||
236 !irccmp(gp_ptr->vote_2.oper_server, source_p->servptr->name))
237 return GLINE_ALREADY_VOTED;
238
239 if (type == GLINE_PENDING_DEL_TYPE)
240 {
241 if (remove_gline_match(user, host))
242 {
243 sendto_realops_flags(UMODE_ALL, L_ALL, SEND_NOTICE,
244 "%s has removed the G-Line for: [%s@%s]",
245 get_oper_name(source_p), user, host);
246 ilog(LOG_TYPE_GLINE, "%s removed G-Line for [%s@%s]",
247 get_oper_name(source_p), user, host);
248 }
249 }
250 else
251 /* trigger the gline using the original reason --fl */
252 set_local_gline(source_p, user, host, gp_ptr->vote_1.reason);
253
254 cleanup_glines(gp_ptr);
255 return GLINE_PLACED;
256 }
257
258 strlcpy(gp_ptr->vote_2.oper_nick, source_p->name,
259 sizeof(gp_ptr->vote_2.oper_nick));
260 strlcpy(gp_ptr->vote_2.oper_user, source_p->username,
261 sizeof(gp_ptr->vote_2.oper_user));
262 strlcpy(gp_ptr->vote_2.oper_host, source_p->host,
263 sizeof(gp_ptr->vote_2.oper_host));
264 strlcpy(gp_ptr->vote_2.reason, reason,
265 sizeof(gp_ptr->vote_2.reason));
266 strlcpy(gp_ptr->vote_2.oper_server, source_p->servptr->name,
267 sizeof(gp_ptr->vote_2.oper_server));
268 gp_ptr->last_gline_time = CurrentTime;
269 gp_ptr->vote_2.time_request = CurrentTime;
270 return GLINE_NOT_PLACED;
271 }
272
273 /*
274 * Didn't find this user@host gline in pending gline list
275 * so add it.
276 */
277 add_new_majority(source_p, user, host, reason, type);
278 return GLINE_NOT_PLACED;
279 }
280
281 /* ms_gline()
282 *
283 * inputs - The usual for a m_ function
284 * output -
285 * side effects -
286 *
287 * Place a G line if 3 opers agree on the identical user@host
288 *
289 * Allow this server to pass along GLINE if received and
290 * GLINES is not defined.
291 *
292 */
293 static int
294 ms_gline(struct Client *source_p, int parc, char *parv[])
295 {
296 const char *reason = NULL; /* reason for "victims" demise */
297 const char *user = NULL;
298 const char *host = NULL; /* user and host of GLINE "victim" */
299 const char *p = NULL;
300
301 if (!IsClient(source_p))
302 return 0;
303
304 if (parc != 4 || EmptyString(parv[3]))
305 return 0;
306
307 assert(source_p->servptr != NULL);
308
309 user = parv[1];
310 host = parv[2];
311 reason = parv[3];
312
313 sendto_server(source_p, CAP_GLN, NOCAPS, ":%s GLINE %s %s :%s",
314 source_p->id, user, host, reason);
315
316 if (!ConfigFileEntry.glines)
317 return 0;
318
319 if (!valid_wild_card(source_p, 1, 2, user, host))
320 return 0;
321
322 if ((p = strchr(host, '/')))
323 {
324 int bitlen = strtol(++p, NULL, 10);
325 int min_bitlen = strchr(host, ':') ? ConfigFileEntry.gline_min_cidr6 :
326 ConfigFileEntry.gline_min_cidr;
327
328 if (bitlen < min_bitlen)
329 {
330 sendto_realops_flags(UMODE_ALL, L_ALL, SEND_NOTICE,
331 "%s is requesting a GLINE with a CIDR mask < %d for [%s@%s] [%s]",
332 get_oper_name(source_p), min_bitlen,
333 user, host, reason);
334 return 0;
335 }
336 }
337
338 /* If at least 3 opers agree this user should be G lined then do it */
339 if (check_majority(source_p, user, host, reason, GLINE_PENDING_ADD_TYPE) ==
340 GLINE_ALREADY_VOTED)
341 {
342 sendto_realops_flags(UMODE_ALL, L_ALL, SEND_NOTICE,
343 "oper or server has already voted");
344 return 0;
345 }
346
347 sendto_realops_flags(UMODE_ALL, L_ALL, SEND_NOTICE,
348 "%s requesting G-Line for [%s@%s] [%s]",
349 get_oper_name(source_p),
350 user, host, reason);
351 ilog(LOG_TYPE_GLINE, "G-Line for [%s@%s] [%s] requested by %s",
352 user, host, reason, get_oper_name(source_p));
353 return 0;
354 }
355
356 /*! \brief GLINE command handler (called by operators)
357 *
358 * \param source_p Pointer to allocated Client struct from which the message
359 * originally comes from. This can be a local or remote client.
360 * \param parc Integer holding the number of supplied arguments.
361 * \param parv Argument vector where parv[0] .. parv[parc-1] are non-NULL
362 * pointers.
363 * \note Valid arguments for this command are:
364 * - parv[0] = command
365 * - parv[1] = user\@host mask
366 * - parv[2] = reason
367 */
368 static int
369 mo_gline(struct Client *source_p, int parc, char *parv[])
370 {
371 char *user = NULL;
372 char *host = NULL;
373 char *reason = NULL;
374 char *p;
375
376 if (!HasOFlag(source_p, OPER_FLAG_GLINE))
377 {
378 sendto_one_numeric(source_p, &me, ERR_NOPRIVS, "gline");
379 return 0;
380 }
381
382 if (!ConfigFileEntry.glines)
383 {
384 sendto_one_notice(source_p, &me, ":GLINE disabled");
385 return 0;
386 }
387
388 if (parse_aline("GLINE", source_p, parc, parv, AWILD,
389 &user, &host, NULL, NULL, &reason) < 0)
390 return 0;
391
392 if ((p = strchr(host, '/')) != NULL)
393 {
394 int bitlen = strtol(++p, NULL, 10);
395 int min_bitlen = strchr(host, ':') ? ConfigFileEntry.gline_min_cidr6 :
396 ConfigFileEntry.gline_min_cidr;
397 if (bitlen < min_bitlen)
398 {
399 sendto_one_notice(source_p, &me, ":Cannot set G-Lines with CIDR length < %d",
400 min_bitlen);
401 return 0;
402 }
403 }
404
405 /* If at least 3 opers agree this user should be G lined then do it */
406 if (check_majority(source_p, user, host, reason, GLINE_PENDING_ADD_TYPE) ==
407 GLINE_ALREADY_VOTED)
408 {
409 sendto_one_notice(source_p, &me, ":This server or oper has already voted");
410 return 0;
411 }
412
413 /*
414 * call these two functions first so the 'requesting' notice always comes
415 * before the 'has triggered' notice. -bill
416 */
417 sendto_realops_flags(UMODE_ALL, L_ALL, SEND_NOTICE,
418 "%s requesting G-Line for [%s@%s] [%s]",
419 get_oper_name(source_p),
420 user, host, reason);
421 ilog(LOG_TYPE_GLINE, "G-Line for [%s@%s] [%s] requested by %s",
422 user, host, reason, get_oper_name(source_p));
423
424 sendto_server(NULL, CAP_GLN, NOCAPS, ":%s GLINE %s %s :%s",
425 source_p->id, user, host, reason);
426 return 0;
427 }
428
429 static void
430 do_sungline(struct Client *source_p, const char *user,
431 const char *host, const char *reason, int prop)
432 {
433 assert(source_p->servptr != NULL);
434
435 sendto_realops_flags(UMODE_ALL, L_ALL, SEND_NOTICE,
436 "%s requesting UNG-Line for [%s@%s] [%s]",
437 get_oper_name(source_p), user, host, reason);
438 ilog(LOG_TYPE_GLINE, "UNG-Line for [%s@%s] [%s] requested by %s",
439 user, host, reason, get_oper_name(source_p));
440
441 /* If at least 3 opers agree this user should be un G lined then do it */
442 if (check_majority(source_p, user, host, reason, GLINE_PENDING_DEL_TYPE) ==
443 GLINE_ALREADY_VOTED)
444 sendto_realops_flags(UMODE_ALL, L_ALL, SEND_NOTICE,
445 "oper or server has already voted");
446
447 if (prop)
448 {
449 sendto_server(source_p, CAP_ENCAP, NOCAPS,
450 ":%s ENCAP * GUNGLINE %s %s :%s",
451 source_p->id, user, host, reason);
452 }
453 }
454
455 /*! \brief GUNGLINE command handler (called in response to an encapsulated
456 * GUNGLINE command)
457 *
458 * \param source_p Pointer to allocated Client struct from which the message
459 * originally comes from. This can be a local or remote client.
460 * \param parc Integer holding the number of supplied arguments.
461 * \param parv Argument vector where parv[0] .. parv[parc-1] are non-NULL
462 * pointers.
463 * \note Valid arguments for this command are:
464 * - parv[0] = command
465 * - parv[1] = username
466 * - parv[2] = hostname
467 * - parv[3] = reason
468 */
469 static int
470 me_gungline(struct Client *source_p, int parc, char *parv[])
471 {
472 if (ConfigFileEntry.glines)
473 do_sungline(source_p, parv[1], parv[2], parv[3], 0);
474 return 0;
475 }
476
477 /*! \brief GUNGLINE command handler (called by operators)
478 *
479 * \param source_p Pointer to allocated Client struct from which the message
480 * originally comes from. This can be a local or remote client.
481 * \param parc Integer holding the number of supplied arguments.
482 * \param parv Argument vector where parv[0] .. parv[parc-1] are non-NULL
483 * pointers.
484 * \note Valid arguments for this command are:
485 * - parv[0] = command
486 * - parv[1] = user\@host mask
487 * - parv[2] = reason
488 */
489 static int
490 mo_gungline(struct Client *source_p, int parc, char *parv[])
491 {
492 char *user = NULL;
493 char *host = NULL;
494 char *reason = NULL;
495
496 if (!HasOFlag(source_p, OPER_FLAG_GLINE))
497 {
498 sendto_one_numeric(source_p, &me, ERR_NOPRIVS, "gline");
499 return 0;
500 }
501
502 if (!ConfigFileEntry.glines)
503 {
504 sendto_one_notice(source_p, &me, ":GUNGLINE disabled");
505 return 0;
506 }
507
508 if (parse_aline("GUNGLINE", source_p, parc, parv, 0, &user,
509 &host, NULL, NULL, &reason) < 0)
510 return 0;
511
512 do_sungline(source_p, user, host, reason, 1);
513 return 0;
514 }
515
516 /*
517 * gline enforces 3 parameters to force operator to give a reason
518 * a gline is not valid with "No reason"
519 * -db
520 */
521 static struct Message gline_msgtab =
522 {
523 "GLINE", 0, 0, 3, MAXPARA, MFLG_SLOW, 0,
524 { m_unregistered, m_not_oper, ms_gline, m_ignore, mo_gline, m_ignore }
525 };
526
527 static struct Message ungline_msgtab =
528 {
529 "GUNGLINE", 0, 0, 3, MAXPARA, MFLG_SLOW, 0,
530 { m_unregistered, m_not_oper, m_ignore, me_gungline, mo_gungline, m_ignore }
531 };
532
533 static void
534 module_init(void)
535 {
536 mod_add_cmd(&gline_msgtab);
537 mod_add_cmd(&ungline_msgtab);
538 add_capability("GLN", CAP_GLN, 1);
539 }
540
541 static void
542 module_exit(void)
543 {
544 mod_del_cmd(&gline_msgtab);
545 mod_del_cmd(&ungline_msgtab);
546 delete_capability("GLN");
547 }
548
549 struct module module_entry =
550 {
551 .node = { NULL, NULL, NULL },
552 .name = NULL,
553 .version = "$Revision$",
554 .handle = NULL,
555 .modinit = module_init,
556 .modexit = module_exit,
557 .flags = 0
558 };

Properties

Name Value
svn:eol-style native
svn:keywords Id Revision