]> jfr.im git - irc/quakenet/newserv.git/blob - nterfacer/nterfacer_relay.c
Fix buffer overflow
[irc/quakenet/newserv.git] / nterfacer / nterfacer_relay.c
1 /*
2 nterfacer relay4
3 Copyright (C) 2004-2005 Chris Porter.
4
5 v1.14
6 - made it return an error instead of a timeout when sending to local users
7 v1.13
8 - found a load of stuff on froo's box
9 v1.12
10 - added nickprefix
11 v1.11
12 - made sure stats buffer was checked (no security problem, just helps pauline)
13 v1.10
14 - added stats support
15 v1.06
16 - found \r bug, and another format string problem...
17 v1.05
18 - oops, fixed quit bug
19 - oops, forgot about rehash hook
20 v1.04
21 - added S support, though it's gross
22 v1.03
23 - fixed audit found format string vunerability
24 v1.02
25 - split O username and password from .h file into config file
26 - added rehash support
27 v1.01
28 - item->schedule was not unset and checked upon timeout
29 */
30
31 #include <stdio.h>
32 #include <string.h>
33 #include <stdarg.h>
34
35 #include "../lib/irc_string.h"
36 #include "../lib/strlfunc.h"
37 #include "../core/schedule.h"
38 #include "../irc/irc.h"
39 #include "../core/config.h"
40 #include "../lib/version.h"
41 #include "library.h"
42 #include "nterfacer_relay.h"
43
44 MODULE_VERSION("");
45
46 struct service_node *node;
47 sstring *ousername, *opassword;
48 regex onickname;
49 int number = 1;
50 char started = 0;
51 char nickprefix[NICKLEN - 5 + 1];
52 static unsigned long userid;
53
54 int stats_handler(struct rline *ri, int argc, char **argv);
55
56 void _init(void) {
57 if(!load_config())
58 return;
59
60 node = register_service("R");
61 MemCheck(node);
62
63 register_handler(node, "relay", 4, relay_handler);
64 register_handler(node, "stats", 2, stats_handler);
65
66 registerhook(HOOK_NICK_LOSTNICK, &relay_quits);
67 registerhook(HOOK_IRC_DISCON, &relay_disconnect);
68 registerhook(HOOK_CORE_REHASH, &relay_rehash);
69
70 started = 1;
71 }
72
73 int load_config(void) {
74 /* eww, I don't know how to make this any nicer! */
75 sstring *ou, *op, *on, *opr;
76 regex newregex;
77
78 memset(&newregex, 0, sizeof(newregex));
79
80 userid = getcopyconfigitemintpositive("nterfacer", "serviceuserid", 0);
81 if(!userid) {
82 Error("nterfacer_relay", ERR_WARNING, "No user id specified (serviceuserid)!");
83 return 0;
84 }
85
86 opr = getcopyconfigitem("nterfacer", "nickprefix", DEFAULT_NICK_PREFIX, sizeof(nickprefix) - 1); /* the terminator is included in nickprefix */
87 strlcpy(nickprefix, (opr&&opr->content)?opr->content:DEFAULT_NICK_PREFIX, sizeof(nickprefix));
88 freesstring(opr); /* works fine with NULL */
89
90 ou = getcopyconfigitem("nterfacer", "serviceusername", "nterfacer", 100);
91 op = getcopyconfigitem("nterfacer", "servicepassword", "setme", 100);
92 on = getcopyconfigitem("nterfacer", "servicenickname", "^O$", 100);
93
94 if(on) {
95 int erroroffset;
96 const char *rerror;
97 newregex.phrase = pcre_compile(on->content, PCRE_CASELESS, &rerror, &erroroffset, NULL);
98 if(newregex.phrase) {
99 newregex.hint = pcre_study(newregex.phrase, 0, &rerror);
100 if(rerror) {
101 pcre_free(newregex.phrase);
102 newregex.phrase = NULL;
103 }
104 }
105 }
106
107 if(!ou || !op || !on || !newregex.phrase) {
108 freesstring(ou);
109 freesstring(op);
110 freesstring(on);
111 if(newregex.phrase) {
112 pcre_free(newregex.phrase);
113 if(newregex.hint)
114 pcre_free(newregex.hint);
115 }
116 MemError();
117 return 0;
118 }
119
120 if(started) {
121 if(ousername)
122 freesstring(ousername);
123 if(opassword)
124 freesstring(opassword);
125 if(onickname.phrase) {
126 pcre_free(onickname.phrase);
127 if(onickname.hint)
128 pcre_free(onickname.hint);
129 }
130 }
131
132 freesstring(on);
133
134 ousername = ou;
135 opassword = op;
136 memcpy(&onickname, &newregex, sizeof(onickname));
137 return 1;
138 }
139
140 void relay_rehash(int hook, void *args) {
141 load_config();
142 }
143
144 void _fini(void) {
145 struct rld *np, *lp;
146
147 if(!started)
148 return;
149
150 if(ousername)
151 freesstring(ousername);
152 if(opassword)
153 freesstring(opassword);
154 if(onickname.phrase) {
155 pcre_free(onickname.phrase);
156 if(onickname.hint)
157 pcre_free(onickname.hint);
158 }
159
160 deregisterhook(HOOK_NICK_LOSTNICK, &relay_quits);
161 deregisterhook(HOOK_IRC_DISCON, &relay_disconnect);
162 deregisterhook(HOOK_CORE_REHASH, &relay_rehash);
163
164 for(np=list;np;) {
165 lp = np->next;
166 ri_error(np->rline, RELAY_UNLOADED, "Module was unloaded");
167 dispose_rld(np);
168
169 np = lp;
170 }
171
172 if(node)
173 deregister_service(node);
174 }
175
176 nick *relay_getnick(void) {
177 static char ournick[NICKLEN + 1];
178 int attempts = 100;
179
180 do {
181 snprintf(ournick, sizeof(ournick), "%s%d", nickprefix, number++);
182 if(number > 60000)
183 number = 1;
184
185 if(!getnickbynick(ournick))
186 return registerlocaluserwithuserid(ournick, "nterf", "cer", "nterfacer relay", "nterfacer", userid, UMODE_SERVICE | UMODE_DEAF | UMODE_OPER | UMODE_INV | UMODE_ACCOUNT, &relay_messages);
187
188 attempts--;
189 } while(attempts > 0);
190
191 return NULL;
192 }
193
194 int relay_handler(struct rline *ri, int argc, char **argv) {
195 struct rld *np;
196 int lines = 0, i;
197 nick *dest;
198 const char *rerror;
199 int erroroffset;
200
201 if(!connected)
202 return ri_error(ri, RELAY_NOT_ON_IRC, "Not currently on IRC");
203
204 if(argv[0][0] == '1') {
205 lines = positive_atoi(argv[1]);
206 if(lines < 1)
207 return ri_error(ri, RELAY_PARSE_ERROR, "Parse error");
208 } else if(argv[0][0] != '2') {
209 return ri_error(ri, RELAY_PARSE_ERROR, "Parse error");
210 }
211
212 dest = getnickbynick(argv[2]);
213 if(!dest)
214 return ri_error(ri, RELAY_NICK_NOT_FOUND, "Nickname not found!");
215
216 if(homeserver(dest->numeric) == mylongnum)
217 return ri_error(ri, RELAY_LOCAL_USER, "Cannot relay to local users");
218
219 for(i=3;i<argc;i++)
220 if(strchr(argv[i], '\r'))
221 return ri_error(ri, RELAY_INVALID_CHARS, "Invalid character in input");
222
223 np = ntmalloc(sizeof(struct rld));
224 MemCheckR(np, ri_error(ri, RELAY_MEMORY_ERROR, "Memory error"));
225
226 np->rline = ri;
227 if(!lines) {
228 np->termination.pcre.phrase = pcre_compile(argv[1], PCRE_FLAGS, &rerror, &erroroffset, NULL);
229 if(!np->termination.pcre.phrase) {
230 MemError();
231 ntfree(np);
232 return ri_error(ri, RELAY_REGEX_ERROR, "Regex compliation error");
233 }
234 np->termination.pcre.hint = pcre_study(np->termination.pcre.phrase, 0, &rerror);
235 if(rerror) {
236 pcre_free(np->termination.pcre.phrase);
237 ntfree(np);
238 return ri_error(ri, RELAY_REGEX_HINT_ERROR, "Regex hint error");
239 }
240
241 np->mode = MODE_TAG;
242 } else {
243 np->mode = MODE_LINES;
244 np->termination.remaining_lines = lines;
245 }
246
247 if(!(np->nick = relay_getnick())) {
248 if(!lines) {
249 pcre_free(np->termination.pcre.phrase);
250 if(np->termination.pcre.hint)
251 pcre_free(np->termination.pcre.hint);
252 }
253 ntfree(np);
254 return ri_error(ri, RELAY_NICK_ERROR, "Unable to get a nickname!");
255 }
256
257 np->schedule = scheduleoneshot(time(NULL) + MESSAGE_TIMEOUT, &relay_timeout, np);
258 if(!np->schedule) {
259 MemError();
260 if(!lines) {
261 pcre_free(np->termination.pcre.phrase);
262 if(np->termination.pcre.hint)
263 pcre_free(np->termination.pcre.hint);
264 }
265 deregisterlocaluser(np->nick, NULL);
266 ntfree(np);
267 return ri_error(ri, RELAY_MEMORY_ERROR, "Memory error");
268 }
269
270 np->dest = dest;
271
272 np->next = list;
273 list = np;
274
275 if(pcre_exec(onickname.phrase, onickname.hint, dest->nick, strlen(dest->nick), 0, 0, NULL, 0) >= 0) {
276 np->mode|=MODE_IS_O;
277 sendsecuremessagetouser(np->nick, dest, serverlist[dest->numeric>>18].name->content, "AUTH %s %s", ousername->content, opassword->content);
278 }
279
280 for(i=3;i<argc;i++)
281 sendmessagetouser(np->nick, dest, "%s", argv[i]);
282
283 return RE_OK;
284 }
285
286 int stats_handler(struct rline *ri, int argc, char **argv) {
287 struct rld *np;
288 char *server = argv[0], *command = argv[1], *more;
289 int serverid = findserver(server);
290 char targetnumeric[3];
291
292 if(serverid == -1)
293 return ri_error(ri, RELAY_SERVER_NOT_FOUND, "Server not found");
294
295 if(!command || !command[0] || (command[0] == ' ') || (command[0] == '\r') || (command[0] == ':'))
296 return ri_error(ri, RELAY_INVALID_COMMAND, "Invalid command");
297
298 if(argc > 2) {
299 more = argv[2];
300 if(strchr(more, '\r'))
301 return ri_error(ri, RELAY_INVALID_CHARS, "Invalid character in input");
302 } else {
303 more = NULL;
304 }
305
306 np = ntmalloc(sizeof(struct rld));
307 MemCheckR(np, ri_error(ri, RELAY_MEMORY_ERROR, "Memory error"));
308
309 np->rline = ri;
310 np->mode = MODE_STATS;
311 np->dest = NULL;
312
313 if(!(np->nick = relay_getnick())) {
314 ntfree(np);
315 return ri_error(ri, RELAY_NICK_ERROR, "Unable to get a nickname!");
316 }
317
318 np->schedule = scheduleoneshot(time(NULL) + MESSAGE_TIMEOUT, &relay_timeout, np);
319 if(!np->schedule) {
320 MemError();
321 deregisterlocaluser(np->nick, NULL);
322 ntfree(np);
323 }
324
325 np->next = list;
326 list = np;
327
328 memcpy(targetnumeric, longtonumeric(serverid, 2), 3);
329 targetnumeric[2] = '\0';
330 if(more) {
331 irc_send("%s R %c %s :%s\r\n", longtonumeric(np->nick->numeric,5), command[0], targetnumeric, more);
332 } else {
333 irc_send("%s R %c :%s\r\n", longtonumeric(np->nick->numeric,5), command[0], targetnumeric);
334 }
335
336 return RE_OK;
337 }
338
339 void relay_messages(nick *target, int messagetype, void **args) {
340 struct rld *item, *prev = NULL;
341 for(item=list;item;prev=item,item=item->next)
342 if(item->nick == target)
343 break;
344
345 if(!item)
346 return;
347
348 switch(messagetype) {
349 case LU_PRIVNOTICE:
350 case LU_PRIVMSG:
351 case LU_SECUREMSG:
352 if(!item->dest || (item->dest != (nick *)args[0]))
353 return;
354
355 if((item->mode & MODE_IS_O) && !(item->mode & MODE_O_AUTH2)) {
356 if(item->mode & MODE_O_AUTH1) {
357 if(!strncmp((char *)args[1], "Your authlevel is ", 18)) {
358 item->mode|=MODE_O_AUTH2;
359 } else {
360 ri_error(item->rline, RELAY_O_AUTH_ERROR, "Unable to auth with O");
361 dispose_rld_prev(item, prev);
362 }
363 } else {
364 if(!strcmp((char *)args[1], "Auth successful.")) {
365 item->mode|=MODE_O_AUTH1;
366 } else {
367 ri_error(item->rline, RELAY_O_AUTH_ERROR, "Unable to auth with O");
368 dispose_rld_prev(item, prev);
369 }
370 }
371 } else if(ri_append(item->rline, "%s", (char *)args[1]) == BF_OVER) {
372 ri_error(item->rline, BF_OVER, "Buffer overflow");
373 dispose_rld_prev(item, prev);
374 } else {
375 if(((item->mode & MODE_LINES) && (!--item->termination.remaining_lines)) || ((item->mode & MODE_TAG) && (pcre_exec(item->termination.pcre.phrase, item->termination.pcre.hint, (char *)args[1], strlen((char *)args[1]), 0, 0, NULL, 0) >= 0))) {
376 ri_final(item->rline);
377 dispose_rld_prev(item, prev);
378 }
379 }
380 break;
381
382 case LU_KILLED:
383 ri_error(item->rline, RELAY_KILLED, "Killed");
384 if(prev) {
385 prev->next = item->next;
386 } else {
387 list = item->next;
388 }
389 dispose_rld_dontquit(item, 1);
390 break;
391
392 case LU_STATS:
393 if(item->mode != MODE_STATS)
394 return;
395 if(ri_append(item->rline, "%s", (char *)args[2]) == BF_OVER) {
396 ri_error(item->rline, BF_OVER, "Buffer overflow");
397 dispose_rld_prev(item, prev);
398 }
399 break;
400
401 case LU_STATS_END:
402 if(item->mode != MODE_STATS)
403 return;
404 ri_final(item->rline);
405 dispose_rld_prev(item, prev);
406 break;
407 }
408 }
409
410 void dispose_rld_prev(struct rld *item, struct rld *prev) {
411 if(prev) {
412 prev->next = item->next;
413 } else {
414 list = item->next;
415 }
416 dispose_rld(item);
417 }
418
419 void dispose_rld_dontquit(struct rld *item, int dontquit) {
420 if(item->schedule)
421 deleteschedule(item->schedule, &relay_timeout, item);
422 if(!dontquit)
423 deregisterlocaluser(item->nick, NULL);
424 if(item->mode & MODE_TAG) {
425 pcre_free(item->termination.pcre.phrase);
426 if(item->termination.pcre.hint)
427 pcre_free(item->termination.pcre.hint);
428 }
429 ntfree(item);
430 }
431
432 void relay_timeout(void *arg) {
433 struct rld *item = (struct rld *)arg, *lp = NULL, *np;
434
435 item->schedule = NULL;
436
437 ri_error(item->rline, RELAY_TIMEOUT, "Timed out");
438
439 for(np=list;np;lp=np,np=np->next) {
440 if(np == item) {
441 dispose_rld_prev(item, lp);
442 break;
443 }
444 }
445 }
446
447 void relay_quits(int hook, void *args) {
448 nick *np = (nick *)args;
449 struct rld *cp, *lp;
450
451 for(lp=NULL,cp=list;cp;) {
452 if(cp->dest == np) {
453 ri_error(cp->rline, RELAY_TARGET_LEFT, "Target left QuakeNet");
454 if(lp) {
455 lp->next = cp->next;
456 dispose_rld(cp);
457 cp = lp->next;
458 } else {
459 list = cp->next;
460 dispose_rld(cp);
461 cp = list;
462 }
463 } else {
464 lp = cp;
465 cp = cp->next;
466 }
467 }
468 }
469
470 void relay_disconnect(int hook, void *args) {
471 struct rld *np, *lp = NULL;
472 while(list) {
473 ri_error(list->rline, RELAY_DISCONNECTED, "Disconnected from IRC");
474 dispose_rld_prev(list, NULL);
475
476 np = lp;
477 }
478 }