]> jfr.im git - irc/rqf/shadowircd.git/blame - modules/m_dline.c
[svn] Don't leak auth{} spoofed IP addresses in +f notices.
[irc/rqf/shadowircd.git] / modules / m_dline.c
CommitLineData
212380e3 1/*
2 * ircd-ratbox: A slightly useful ircd.
3 * m_dline.c: Bans/unbans a user.
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_dline.c 3161 2007-01-25 07:23:01Z nenolod $
212380e3 25 */
26
27#include "stdinc.h"
28#include "tools.h"
29#include "channel.h"
30#include "class.h"
31#include "client.h"
32#include "common.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 "commio.h"
39#include "s_conf.h"
40#include "s_newconf.h"
41#include "s_log.h"
42#include "send.h"
43#include "hash.h"
44#include "s_serv.h"
45#include "msg.h"
46#include "parse.h"
47#include "modules.h"
48
49static int mo_dline(struct Client *, struct Client *, int, const char **);
50static int mo_undline(struct Client *, struct Client *, int, const char **);
51
52struct Message dline_msgtab = {
53 "DLINE", 0, 0, 0, MFLG_SLOW,
54 {mg_unreg, mg_not_oper, mg_ignore, mg_ignore, mg_ignore, {mo_dline, 2}}
55};
56struct Message undline_msgtab = {
57 "UNDLINE", 0, 0, 0, MFLG_SLOW,
58 {mg_unreg, mg_not_oper, mg_ignore, mg_ignore, mg_ignore, {mo_undline, 2}}
59};
60
61mapi_clist_av1 dline_clist[] = { &dline_msgtab, &undline_msgtab, NULL };
5366977b 62DECLARE_MODULE_AV1(dline, NULL, NULL, dline_clist, NULL, NULL, "$Revision: 3161 $");
212380e3 63
64static int valid_comment(char *comment);
65static int flush_write(struct Client *, FILE *, char *, char *);
66static int remove_temp_dline(const char *);
67
68/* mo_dline()
69 *
70 * parv[1] - dline to add
71 * parv[2] - reason
72 */
73static int
74mo_dline(struct Client *client_p, struct Client *source_p,
75 int parc, const char *parv[])
76{
77 char def[] = "No Reason";
78 const char *dlhost;
79 char *oper_reason;
80 char *reason = def;
81 struct irc_sockaddr_storage daddr;
82 char cidr_form_host[HOSTLEN + 1];
83 struct ConfItem *aconf;
84 int bits;
85 char dlbuffer[IRCD_BUFSIZE];
86 const char *current_date;
87 int tdline_time = 0;
88 int loc = 1;
89
90 if(!IsOperK(source_p))
91 {
92 sendto_one(source_p, form_str(ERR_NOPRIVS),
93 me.name, source_p->name, "kline");
94 return 0;
95 }
96
97 if((tdline_time = valid_temp_time(parv[loc])) >= 0)
98 loc++;
99
100 if(parc < loc + 1)
101 {
102 sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS),
103 me.name, source_p->name, "DLINE");
104 return 0;
105 }
106
107 dlhost = parv[loc];
108 strlcpy(cidr_form_host, dlhost, sizeof(cidr_form_host));
109
110 if(!parse_netmask(dlhost, NULL, &bits))
111 {
112 sendto_one(source_p, ":%s NOTICE %s :Invalid D-Line",
113 me.name, source_p->name);
114 return 0;
115 }
116
117 loc++;
118
119 if(parc >= loc + 1) /* host :reason */
120 {
121 if(!EmptyString(parv[loc]))
122 reason = LOCAL_COPY(parv[loc]);
123
124 if(!valid_comment(reason))
125 {
126 sendto_one(source_p,
127 ":%s NOTICE %s :Invalid character '\"' in comment",
128 me.name, source_p->name);
129 return 0;
130 }
131 }
132
133 if(IsOperAdmin(source_p))
134 {
135 if(bits < 8)
136 {
137 sendto_one(source_p,
138 ":%s NOTICE %s :For safety, bitmasks less than 8 require conf access.",
139 me.name, parv[0]);
140 return 0;
141 }
142 }
143 else
144 {
145 if(bits < 16)
146 {
147 sendto_one(source_p,
148 ":%s NOTICE %s :Dline bitmasks less than 16 are for admins only.",
149 me.name, parv[0]);
150 return 0;
151 }
152 }
153
154 if(ConfigFileEntry.non_redundant_klines)
155 {
156 const char *creason;
157 int t = AF_INET, ty, b;
158 ty = parse_netmask(dlhost, (struct sockaddr *)&daddr, &b);
159#ifdef IPV6
160 if(ty == HM_IPV6)
161 t = AF_INET6;
162 else
163#endif
164 t = AF_INET;
165
166 if((aconf = find_dline((struct sockaddr *)&daddr, t)) != NULL)
167 {
168 int bx;
169 parse_netmask(aconf->host, NULL, &bx);
170 if(b >= bx)
171 {
172 creason = aconf->passwd ? aconf->passwd : "<No Reason>";
173 if(IsConfExemptKline(aconf))
174 sendto_one(source_p,
175 ":%s NOTICE %s :[%s] is (E)d-lined by [%s] - %s",
176 me.name, parv[0], dlhost, aconf->host, creason);
177 else
178 sendto_one(source_p,
179 ":%s NOTICE %s :[%s] already D-lined by [%s] - %s",
180 me.name, parv[0], dlhost, aconf->host, creason);
181 return 0;
182 }
183 }
184 }
185
186 set_time();
187 current_date = smalldate();
188
189 aconf = make_conf();
190 aconf->status = CONF_DLINE;
191 DupString(aconf->host, dlhost);
192
193 /* Look for an oper reason */
194 if((oper_reason = strchr(reason, '|')) != NULL)
195 {
196 *oper_reason = '\0';
197 oper_reason++;
198
199 if(!EmptyString(oper_reason))
200 DupString(aconf->spasswd, oper_reason);
201 }
202
203 if(tdline_time > 0)
204 {
205 ircsnprintf(dlbuffer, sizeof(dlbuffer),
206 "Temporary D-line %d min. - %s (%s)",
207 (int) (tdline_time / 60), reason, current_date);
208 DupString(aconf->passwd, dlbuffer);
209 aconf->hold = CurrentTime + tdline_time;
210 add_temp_dline(aconf);
211
212 if(EmptyString(oper_reason))
213 {
214 sendto_realops_snomask(SNO_GENERAL, L_ALL,
215 "%s added temporary %d min. D-Line for [%s] [%s]",
216 get_oper_name(source_p), tdline_time / 60,
217 aconf->host, reason);
218 ilog(L_KLINE, "D %s %d %s %s",
219 get_oper_name(source_p), tdline_time / 60,
220 aconf->host, reason);
221 }
222 else
223 {
224 sendto_realops_snomask(SNO_GENERAL, L_ALL,
225 "%s added temporary %d min. D-Line for [%s] [%s|%s]",
226 get_oper_name(source_p), tdline_time / 60,
227 aconf->host, reason, oper_reason);
228 ilog(L_KLINE, "D %s %d %s %s|%s",
229 get_oper_name(source_p), tdline_time / 60,
230 aconf->host, reason, oper_reason);
231 }
232
233 sendto_one(source_p, ":%s NOTICE %s :Added temporary %d min. D-Line for [%s]",
234 me.name, source_p->name, tdline_time / 60, aconf->host);
235 }
236 else
237 {
238 ircsnprintf(dlbuffer, sizeof(dlbuffer), "%s (%s)", reason, current_date);
239 DupString(aconf->passwd, dlbuffer);
240 add_conf_by_address(aconf->host, CONF_DLINE, NULL, aconf);
241 write_confitem(DLINE_TYPE, source_p, NULL, aconf->host, reason,
242 oper_reason, current_date, 0);
243 }
244
245 check_dlines();
246 return 0;
247}
248
249/* mo_undline()
250 *
251 * parv[1] = dline to remove
252 */
253static int
254mo_undline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
255{
256 FILE *in;
257 FILE *out;
258 char buf[BUFSIZE], buff[BUFSIZE], temppath[BUFSIZE], *p;
259 const char *filename, *found_cidr;
260 const char *cidr;
261 int pairme = NO, error_on_write = NO;
262 mode_t oldumask;
263
264 ircsnprintf(temppath, sizeof(temppath), "%s.tmp", ConfigFileEntry.dlinefile);
265
266 if(!IsOperUnkline(source_p))
267 {
268 sendto_one(source_p, form_str(ERR_NOPRIVS),
269 me.name, source_p->name, "unkline");
270 return 0;
271 }
272
273 cidr = parv[1];
274
275 if(parse_netmask(cidr, NULL, NULL) == HM_HOST)
276 {
5366977b 277 sendto_one_notice(source_p, ":Invalid D-Line");
212380e3 278 return 0;
279 }
280
281 if(remove_temp_dline(cidr))
282 {
283 sendto_one(source_p,
284 ":%s NOTICE %s :Un-dlined [%s] from temporary D-lines",
285 me.name, parv[0], cidr);
286 sendto_realops_snomask(SNO_GENERAL, L_ALL,
287 "%s has removed the temporary D-Line for: [%s]",
288 get_oper_name(source_p), cidr);
289 ilog(L_KLINE, "UD %s %s", get_oper_name(source_p), cidr);
290 return 0;
291 }
292
293 filename = get_conf_name(DLINE_TYPE);
294
295 if((in = fopen(filename, "r")) == 0)
296 {
297 sendto_one(source_p, ":%s NOTICE %s :Cannot open %s", me.name, parv[0], filename);
298 return 0;
299 }
300
301 oldumask = umask(0);
302 if((out = fopen(temppath, "w")) == 0)
303 {
304 sendto_one(source_p, ":%s NOTICE %s :Cannot open %s", me.name, parv[0], temppath);
305 fclose(in);
306 umask(oldumask);
307 return 0;
308 }
309
310 umask(oldumask);
311
312 while (fgets(buf, sizeof(buf), in))
313 {
314 strlcpy(buff, buf, sizeof(buff));
315
316 if((p = strchr(buff, '\n')) != NULL)
317 *p = '\0';
318
319 if((*buff == '\0') || (*buff == '#'))
320 {
321 if(!error_on_write)
322 flush_write(source_p, out, buf, temppath);
323 continue;
324 }
325
326 if((found_cidr = getfield(buff)) == NULL)
327 {
328 if(!error_on_write)
329 flush_write(source_p, out, buf, temppath);
330 continue;
331 }
332
333 if(irccmp(found_cidr, cidr) == 0)
334 {
335 pairme++;
336 }
337 else
338 {
339 if(!error_on_write)
340 flush_write(source_p, out, buf, temppath);
341 continue;
342 }
343 }
344
345 fclose(in);
346 if (fclose(out))
347 error_on_write = YES;
348
349 if(error_on_write)
350 {
351 sendto_one(source_p,
352 ":%s NOTICE %s :Couldn't write D-line file, aborted",
353 me.name, parv[0]);
354 return 0;
355 }
356 else if(!pairme)
357 {
358 sendto_one(source_p, ":%s NOTICE %s :No D-Line for %s",
359 me.name, parv[0], cidr);
360
361 if(temppath != NULL)
362 (void) unlink(temppath);
363
364 return 0;
365 }
366
367 if (rename(temppath, filename))
368 {
369 sendto_one_notice(source_p, ":Couldn't rename temp file, aborted");
370 return 0;
371 }
372 rehash_bans(0);
373
374
375 sendto_one(source_p, ":%s NOTICE %s :D-Line for [%s] is removed", me.name, parv[0], cidr);
376 sendto_realops_snomask(SNO_GENERAL, L_ALL,
377 "%s has removed the D-Line for: [%s]", get_oper_name(source_p), cidr);
378 ilog(L_KLINE, "UD %s %s", get_oper_name(source_p), cidr);
379
380 return 0;
381}
382
383/*
384 * valid_comment
385 * inputs - pointer to client
386 * - pointer to comment
387 * output - 0 if no valid comment, 1 if valid
388 * side effects - NONE
389 */
390static int
391valid_comment(char *comment)
392{
393 if(strchr(comment, '"'))
394 return 0;
395
396 if(strlen(comment) > REASONLEN)
397 comment[REASONLEN] = '\0';
398
399 return 1;
400}
401
402/*
403 * flush_write()
404 *
405 * inputs - pointer to client structure of oper requesting unkline
406 * - out is the file descriptor
407 * - buf is the buffer to write
408 * - ntowrite is the expected number of character to be written
409 * - temppath is the temporary file name to be written
410 * output - YES for error on write
411 * - NO for success
412 * side effects - if successful, the buf is written to output file
413 * if a write failure happesn, and the file pointed to
414 * by temppath, if its non NULL, is removed.
415 *
416 * The idea here is, to be as robust as possible when writing to the
417 * kline file.
418 *
419 * -Dianora
420 */
421static int
422flush_write(struct Client *source_p, FILE * out, char *buf, char *temppath)
423{
424 int error_on_write = (fputs(buf, out) < 0) ? YES : NO;
425
426 if(error_on_write)
427 {
5366977b 428 sendto_one_notice(source_p, ":Unable to write to %s", temppath);
212380e3 429 fclose(out);
430 if(temppath != NULL)
431 (void) unlink(temppath);
432 }
433 return (error_on_write);
434}
435
436/* remove_temp_dline()
437 *
438 * inputs - hostname to undline
439 * outputs -
440 * side effects - tries to undline anything that matches
441 */
442static int
443remove_temp_dline(const char *host)
444{
445 struct ConfItem *aconf;
446 dlink_node *ptr;
447 struct irc_sockaddr_storage addr, caddr;
448 int bits, cbits;
449 int i;
450
451 parse_netmask(host, (struct sockaddr *)&addr, &bits);
452
453 for (i = 0; i < LAST_TEMP_TYPE; i++)
454 {
455 DLINK_FOREACH(ptr, temp_dlines[i].head)
456 {
457 aconf = ptr->data;
458
459 parse_netmask(aconf->host, (struct sockaddr *)&caddr, &cbits);
460
461 if(comp_with_mask_sock((struct sockaddr *)&addr, (struct sockaddr *)&caddr, bits) && bits == cbits)
462 {
463 dlinkDestroy(ptr, &temp_dlines[i]);
464 delete_one_address_conf(aconf->host, aconf);
465 return YES;
466 }
467 }
468 }
469
470 return NO;
471}