1 /*
2  *  ircd-ratbox: A slightly useful ircd.
3  *  m_gungline.c: Votes towards removing a gline.
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-2012 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
22  *  USA
23  *
24  *  $Id: m_gline.c 26421 2009-01-18 17:38:16Z jilles $
25  */
26 
27 #include "stdinc.h"
28 #include "struct.h"
29 #include "s_gline.h"
30 #include "client.h"
31 #include "match.h"
32 #include "ircd.h"
33 #include "hostmask.h"
34 #include "numeric.h"
35 #include "s_conf.h"
36 #include "s_newconf.h"
37 #include "scache.h"
38 #include "send.h"
39 #include "s_serv.h"
40 #include "hash.h"
41 #include "parse.h"
42 #include "modules.h"
43 #include "s_log.h"
44 #include "hook.h"
45 
46 static int mo_gungline(struct Client *, struct Client *, int, const char **);
47 static int me_gungline(struct Client *, struct Client *, int, const char **);
48 
49 static void h_gungline_stats(hook_data_int *);
50 
51 static int modinit(void);
52 static void moddeinit(void);
53 
54 struct Message gungline_msgtab = {
55 	"GUNGLINE", 0, 0, 0, MFLG_SLOW,
56 	{mg_unreg, mg_not_oper, mg_ignore, mg_ignore, {me_gungline, 4}, {mo_gungline, 3}}
57 };
58 
59 mapi_clist_av1 gungline_clist[] = { &gungline_msgtab, NULL };
60 
61 mapi_hfn_list_av1 gungline_hfnlist[] = {
62 	{"doing_stats", (hookfn) h_gungline_stats},
63 	{NULL, NULL}
64 };
65 
66 DECLARE_MODULE_AV1(gungline, modinit, moddeinit, gungline_clist, NULL, gungline_hfnlist, "$Revision: 26421 $");
67 
68 static int majority_ungline(struct Client *source_p, const char *user,
69 			  const char *host, const char *reason);
70 
71 static int invalid_gline(struct Client *, const char *, char *);
72 
73 static int remove_temp_gline(const char *, const char *);
74 static void expire_pending_gunglines(void *unused);
75 static struct ev_entry *pending_gungline_ev;
76 static void flush_pending_gunglines(void);
77 
78 static rb_dlink_list pending_gunglines;
79 
80 static int
modinit(void)81 modinit(void)
82 {
83 	pending_gungline_ev = rb_event_addish("expire_pending_gunglines", expire_pending_gunglines, NULL,
84 					   CLEANUP_GLINES_TIME);
85 	return 0;
86 }
87 
88 static void
moddeinit(void)89 moddeinit(void)
90 {
91 	rb_event_delete(pending_gungline_ev);
92 	if (rb_dlink_list_length(&pending_gunglines) > 0)
93 		sendto_realops_flags(UMODE_ALL, L_ALL,
94 				"Discarding pending gunglines because of module unload");
95 	flush_pending_gunglines();
96 }
97 
98 /* mo_gungline()
99  *
100  * inputs       - The usual for a m_ function
101  * output       -
102  * side effects - remove a gline if 3 opers agree
103  */
104 static int
mo_gungline(struct Client * client_p,struct Client * source_p,int parc,const char * parv[])105 mo_gungline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
106 {
107 	const char *user = NULL;
108 	char *host = NULL;
109 	char *reason = NULL;
110 	char splat[] = "*";
111 
112 	if(!ConfigFileEntry.glines)
113 	{
114 		sendto_one_notice(source_p, ":GUNGLINE disabled");
115 		return 0;
116 	}
117 
118 	if(!IsOperUnkline(source_p) || !IsOperGline(source_p))
119 	{
120 		sendto_one(source_p, form_str(ERR_NOPRIVS), me.name, source_p->name, "ungline");
121 		return 0;
122 	}
123 
124 	host = strchr(parv[1], '@');
125 
126 	/* specific user@host */
127 	if(host != NULL)
128 	{
129 		user = parv[1];
130 		*(host++) = '\0';
131 
132 		/* gline for "@host", use *@host */
133 		if(*user == '\0')
134 			user = splat;
135 	}
136 	/* just a host? */
137 	else
138 	{
139 		/* ok, its not a host.. abort */
140 		if(strchr(parv[1], '.') == NULL)
141 		{
142 			sendto_one_notice(source_p, ":Invalid parameters");
143 			return 0;
144 		}
145 
146 		user = splat;
147 		host = LOCAL_COPY(parv[1]);
148 	}
149 
150 	reason = LOCAL_COPY(parv[2]);
151 
152 	if(invalid_gline(source_p, user, reason))
153 		return 0;
154 
155 	/* inform users about the gline before we call majority_ungline()
156 	 * so already voted comes below gline request --fl
157 	 */
158 	sendto_realops_flags(UMODE_ALL, L_ALL,
159 			     "%s!%s@%s on %s is requesting ungline for [%s@%s] [%s]",
160 			     source_p->name, source_p->username,
161 			     source_p->host, me.name, user, host, reason);
162 	ilog(L_GLINE, "RU %s %s %s %s %s %s %s",
163 	     source_p->name, source_p->username, source_p->host,
164 	     source_p->servptr->name, user, host, reason);
165 
166 	/* If at least 3 opers agree this user should be G lined then do it */
167 	majority_ungline(source_p, user, host, reason);
168 
169 	sendto_server(client_p, NULL, CAP_ENCAP | CAP_TS6, NOCAPS,
170 		      ":%s ENCAP * GUNGLINE %s %s :%s", use_id(source_p), user, host, reason);
171 	sendto_server(client_p, NULL, CAP_ENCAP, CAP_TS6,
172 		      ":%s ENCAP * GUNGLINE %s %s :%s", source_p->name, user, host, reason);
173 
174 	return 0;
175 }
176 
177 /* mc_gungline()
178  */
179 static int
me_gungline(struct Client * client_p,struct Client * source_p,int parc,const char * parv[])180 me_gungline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
181 {
182 	struct Client *acptr;
183 	const char *user;
184 	const char *host;
185 	char *reason;
186 
187 	if (!IsClient(source_p))
188 		return 0;
189 
190 	acptr = source_p;
191 
192 	user = parv[1];
193 	host = parv[2];
194 	reason = LOCAL_COPY(parv[3]);
195 
196 	if(invalid_gline(acptr, user, reason))
197 		return 0;
198 
199 	if(!ConfigFileEntry.glines)
200 		return 0;
201 
202 	sendto_realops_flags(UMODE_ALL, L_ALL,
203 			     "%s!%s@%s on %s is requesting ungline for [%s@%s] [%s]",
204 			     acptr->name, acptr->username, acptr->host,
205 			     acptr->servptr->name, user, host, reason);
206 
207 	ilog(L_GLINE, "RU %s %s %s %s %s %s %s",
208 	     source_p->name, source_p->username, source_p->host,
209 	     source_p->servptr->name, user, host, reason);
210 
211 	/* If at least 3 opers agree this user should be G lined then do it */
212 	majority_ungline(acptr, user, host, reason);
213 
214 	return 0;
215 }
216 
217 
218 /* invalid_gline
219  *
220  * inputs	- pointer to source client, ident, host and reason
221  * outputs	- 1 if invalid, 0 if valid
222  * side effects -
223  */
224 static int
invalid_gline(struct Client * source_p,const char * luser,char * lreason)225 invalid_gline(struct Client *source_p, const char *luser, char *lreason)
226 {
227 	if(strchr(luser, '!'))
228 	{
229 		sendto_one_notice(source_p, ":Invalid character '!' in gline");
230 		return 1;
231 	}
232 
233 	if(strlen(lreason) > REASONLEN)
234 		lreason[REASONLEN] = '\0';
235 
236 	return 0;
237 }
238 
239 /*
240  * remove_local_gline
241  *
242  * inputs	- pointer to oper nick/username/host/server,
243  * 		  victim user/host and reason
244  * output	- NONE
245  * side effects	-
246  */
247 static void
remove_local_gline(struct Client * source_p,const char * user,const char * host,const char * reason)248 remove_local_gline(struct Client *source_p, const char *user, const char *host, const char *reason)
249 {
250 	if (!remove_temp_gline(user, host))
251 		return;
252 	sendto_realops_flags(UMODE_ALL, L_ALL,
253 			     "%s!%s@%s on %s has triggered ungline for [%s@%s] [%s]",
254 			     source_p->name, source_p->username,
255 			     source_p->host, source_p->servptr->name, user, host, reason);
256 	ilog(L_GLINE, "TU %s %s %s %s %s %s %s",
257 	     source_p->name, source_p->username, source_p->host,
258 	     source_p->servptr->name, user, host, reason);
259 }
260 
261 /* majority_ungline()
262  *
263  * input	- client doing gline, user, host and reason of gline
264  * output       - YES if there are 3 different opers/servers agree, else NO
265  * side effects -
266  */
267 static int
majority_ungline(struct Client * source_p,const char * user,const char * host,const char * reason)268 majority_ungline(struct Client *source_p, const char *user, const char *host, const char *reason)
269 {
270 	rb_dlink_node *pending_node;
271 	struct gline_pending *pending;
272 
273 	/* to avoid desync.. --fl */
274 	expire_pending_gunglines(NULL);
275 
276 	RB_DLINK_FOREACH(pending_node, pending_gunglines.head)
277 	{
278 		pending = pending_node->data;
279 
280 		if((irccmp(pending->user, user) == 0) && (irccmp(pending->host, host) == 0))
281 		{
282 			/* check oper or server hasnt already voted */
283 			if(((irccmp(pending->oper_user1, source_p->username) == 0) &&
284 			    (irccmp(pending->oper_host1, source_p->host) == 0)))
285 			{
286 				sendto_realops_flags(UMODE_ALL, L_ALL, "oper has already voted");
287 				return NO;
288 			}
289 			else if(irccmp(pending->oper_server1, source_p->servptr->name) == 0)
290 			{
291 				sendto_realops_flags(UMODE_ALL, L_ALL, "server has already voted");
292 				return NO;
293 			}
294 
295 			if(pending->oper_user2[0] != '\0')
296 			{
297 				/* if two other opers on two different servers have voted yes */
298 				if(((irccmp(pending->oper_user2, source_p->username) == 0) &&
299 				    (irccmp(pending->oper_host2, source_p->host) == 0)))
300 				{
301 					sendto_realops_flags(UMODE_ALL, L_ALL,
302 							     "oper has already voted");
303 					return NO;
304 				}
305 				else if(irccmp(pending->oper_server2, source_p->servptr->name) == 0)
306 				{
307 					sendto_realops_flags(UMODE_ALL, L_ALL,
308 							     "server has already voted");
309 					return NO;
310 				}
311 
312 				/* trigger the gline using the original reason --fl */
313 				remove_local_gline(source_p, user, host, pending->reason1);
314 
315 				expire_pending_gunglines(pending);
316 				return YES;
317 			}
318 			else
319 			{
320 				rb_strlcpy(pending->oper_nick2, source_p->name,
321 					   sizeof(pending->oper_nick2));
322 				rb_strlcpy(pending->oper_user2, source_p->username,
323 					   sizeof(pending->oper_user2));
324 				rb_strlcpy(pending->oper_host2, source_p->host,
325 					   sizeof(pending->oper_host2));
326 				pending->reason2 = rb_strdup(reason);
327 				pending->oper_server2 = scache_add(source_p->servptr->name);
328 				pending->last_gline_time = rb_current_time();
329 				pending->time_request2 = rb_current_time();
330 				return NO;
331 			}
332 		}
333 	}
334 
335 	/* no pending ungline, create a new one */
336 	pending = (struct gline_pending *)rb_malloc(sizeof(struct gline_pending));
337 
338 	rb_strlcpy(pending->oper_nick1, source_p->name, sizeof(pending->oper_nick1));
339 	rb_strlcpy(pending->oper_user1, source_p->username, sizeof(pending->oper_user1));
340 	rb_strlcpy(pending->oper_host1, source_p->host, sizeof(pending->oper_host1));
341 
342 	pending->oper_server1 = scache_add(source_p->servptr->name);
343 
344 	rb_strlcpy(pending->user, user, sizeof(pending->user));
345 	rb_strlcpy(pending->host, host, sizeof(pending->host));
346 	pending->reason1 = rb_strdup(reason);
347 	pending->reason2 = NULL;
348 
349 	pending->last_gline_time = rb_current_time();
350 	pending->time_request1 = rb_current_time();
351 
352 	rb_dlinkAddAlloc(pending, &pending_gunglines);
353 
354 	return NO;
355 }
356 
357 /* remove_temp_gline()
358  *
359  * inputs       - username, hostname to ungline
360  * outputs      -
361  * side effects - tries to ungline anything that matches
362  */
363 static int
remove_temp_gline(const char * user,const char * host)364 remove_temp_gline(const char *user, const char *host)
365 {
366 	struct ConfItem *aconf;
367 	rb_dlink_node *ptr;
368 	struct rb_sockaddr_storage addr, caddr;
369 	int bits, cbits;
370 	int mtype, gtype;
371 
372 	mtype = parse_netmask(host, (struct sockaddr *)&addr, &bits);
373 
374 	RB_DLINK_FOREACH(ptr, glines.head)
375 	{
376 		aconf = ptr->data;
377 
378 		gtype = parse_netmask(aconf->host, (struct sockaddr *)&caddr, &cbits);
379 
380 		if(gtype != mtype || (user && irccmp(user, aconf->user)))
381 			continue;
382 
383 		if(gtype == HM_HOST)
384 		{
385 			if(irccmp(aconf->host, host))
386 				continue;
387 		}
388 		else if(bits != cbits ||
389 			!comp_with_mask_sock((struct sockaddr *)&addr,
390 					     (struct sockaddr *)&caddr, bits))
391 			continue;
392 
393 		rb_dlinkDestroy(ptr, &glines);
394 		delete_one_address_conf(aconf->host, aconf);
395 		return YES;
396 	}
397 
398 	return NO;
399 }
400 
401 static void
h_gungline_stats(hook_data_int * data)402 h_gungline_stats(hook_data_int * data)
403 {
404 	char statchar = (char)data->arg2;
405 
406 	if(ConfigFileEntry.glines && statchar == 'g' && IsOper(data->client))
407 	{
408 		rb_dlink_node *pending_node;
409 		struct gline_pending *glp_ptr;
410 		char timebuffer[MAX_DATE_STRING];
411 		struct tm *tmptr;
412 
413 		RB_DLINK_FOREACH(pending_node, pending_gunglines.head)
414 		{
415 			glp_ptr = pending_node->data;
416 
417 			tmptr = gmtime(&glp_ptr->time_request1);
418 			strftime(timebuffer, MAX_DATE_STRING, "%Y/%m/%d %H:%M:%S", tmptr);
419 
420 			sendto_one_notice(data->client,
421 					  ":1) %s!%s@%s on %s requested ungline at %s for %s@%s [%s]",
422 					  glp_ptr->oper_nick1,
423 					  glp_ptr->oper_user1, glp_ptr->oper_host1,
424 					  glp_ptr->oper_server1, timebuffer,
425 					  glp_ptr->user, glp_ptr->host, glp_ptr->reason1);
426 
427 			if(glp_ptr->oper_nick2[0])
428 			{
429 				tmptr = gmtime(&glp_ptr->time_request2);
430 				strftime(timebuffer, MAX_DATE_STRING, "%Y/%m/%d %H:%M:%S", tmptr);
431 				sendto_one_notice(data->client,
432 						  ":2) %s!%s@%s on %s requested ungline at %s for %s@%s [%s]",
433 						  glp_ptr->oper_nick2,
434 						  glp_ptr->oper_user2, glp_ptr->oper_host2,
435 						  glp_ptr->oper_server2, timebuffer,
436 						  glp_ptr->user, glp_ptr->host, glp_ptr->reason2);
437 			}
438 		}
439 
440 		if(rb_dlink_list_length(&pending_gunglines) > 0)
441 			sendto_one_notice(data->client, ":End of Pending G-line Removals");
442 	}
443 }
444 /*
445  * expire_pending_gunglines
446  *
447  * inputs       - NONE
448  * output       - NONE
449  * side effects -
450  *
451  * Go through the pending gungline list, expire any that haven't had
452  * enough "votes" in the time period allowed
453  */
454 static void
expire_pending_gunglines(void * vptr)455 expire_pending_gunglines(void *vptr)
456 {
457 	rb_dlink_node *pending_node;
458 	rb_dlink_node *next_node;
459 	struct gline_pending *glp_ptr;
460 
461 	RB_DLINK_FOREACH_SAFE(pending_node, next_node, pending_gunglines.head)
462 	{
463 		glp_ptr = pending_node->data;
464 
465 		if((glp_ptr->last_gline_time + GLINE_PENDING_EXPIRE) <=
466 		    rb_current_time() || vptr == glp_ptr)
467 
468 		{
469 			rb_free(glp_ptr->reason1);
470 			rb_free(glp_ptr->reason2);
471 			rb_free(glp_ptr);
472 			rb_dlinkDestroy(pending_node, &pending_gunglines);
473 		}
474 	}
475 }
476 
477 static void
flush_pending_gunglines(void)478 flush_pending_gunglines(void)
479 {
480 	rb_dlink_node *pending_node;
481 	rb_dlink_node *next_node;
482 	struct gline_pending *glp_ptr;
483 
484 	RB_DLINK_FOREACH_SAFE(pending_node, next_node, pending_gunglines.head)
485 	{
486 		glp_ptr = pending_node->data;
487 
488 		rb_free(glp_ptr->reason1);
489 		rb_free(glp_ptr->reason2);
490 		rb_free(glp_ptr);
491 		rb_dlinkDestroy(pending_node, &pending_gunglines);
492 	}
493 }
494