]> jfr.im git - irc/quakenet/newserv.git/blob - nterface/nterfacer.c
Missed one...
[irc/quakenet/newserv.git] / nterface / nterfacer.c
1 /*
2 nterfacer
3 Copyright (C) 2004-2007 Chris Porter.
4 */
5
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <stdarg.h>
9 #include <sys/poll.h>
10 #include <sys/types.h>
11 #include <sys/socket.h>
12 #include <errno.h>
13 #include <unistd.h>
14 #include <sys/ioctl.h>
15 #include <netdb.h>
16 #include <string.h>
17 #include <strings.h>
18
19 #include "../lib/sstring.h"
20 #include "../lib/irc_string.h"
21 #include "../core/config.h"
22 #include "../core/events.h"
23 #include "../lib/version.h"
24
25 #include "nterfacer.h"
26 #include "logging.h"
27
28 MODULE_VERSION("1.1p" PROTOCOL_VERSION);
29
30 struct service_node *tree = NULL;
31 struct esocket_events nterfacer_events;
32 struct esocket *nterfacer_sock;
33 struct rline *rlines = NULL;
34 unsigned short nterfacer_token = BLANK_TOKEN;
35 struct nterface_auto_log *nrl;
36
37 struct service_node *ping = NULL;
38 int accept_fd = -1;
39 struct permitted *permits;
40 int permit_count = 0;
41
42 int ping_handler(struct rline *ri, int argc, char **argv);
43
44 void _init(void) {
45 int loaded;
46 int debug_mode = getcopyconfigitemintpositive("nterfacer", "debug", 0);
47
48 nrl = nterface_open_log("nterfacer", "logs/nterfacer.log", debug_mode);
49
50 loaded = load_permits();
51 if(!loaded) {
52 nterface_log(nrl, NL_ERROR, "No permits loaded successfully.");
53 return;
54 } else {
55 nterface_log(nrl, NL_INFO, "Loaded %d permit%s successfully.", loaded, loaded==1?"":"s");
56 }
57
58 nterfacer_events.on_accept = nterfacer_accept_event;
59 nterfacer_events.on_line = nterfacer_line_event;
60 nterfacer_events.on_disconnect = NULL;
61
62 nterfacer_token = esocket_token();
63
64 ping = register_service("nterfacer");
65 if(!ping) {
66 MemError();
67 } else {
68 register_handler(ping, "ping", 0, ping_handler);
69 }
70
71 accept_fd = setup_listening_socket();
72 if(accept_fd == -1) {
73 nterface_log(nrl, NL_ERROR, "Unable to setup listening socket!");
74 } else {
75 nterfacer_sock = esocket_add(accept_fd, ESOCKET_UNIX_DOMAIN, &nterfacer_events, nterfacer_token);
76 }
77
78 /* the main unix domain socket must NOT have a disconnect event. */
79 nterfacer_events.on_disconnect = nterfacer_disconnect_event;
80 }
81
82 void free_handler(struct handler *hp) {
83 freesstring(hp->command);
84 free(hp);
85 }
86
87 void free_handlers(struct service_node *tp) {
88 struct handler *hp, *lp;
89
90 for(hp=tp->handlers;hp;) {
91 lp = hp;
92 hp = hp->next;
93 free_handler(lp);
94 }
95
96 tp->handlers = NULL;
97 }
98
99 void _fini(void) {
100 struct service_node *tp, *lp;
101 int i;
102
103 if(ping)
104 deregister_service(ping);
105
106 for(tp=tree;tp;) {
107 lp = tp;
108 tp = tp->next;
109 free_handlers(lp);
110 free(lp);
111 }
112 tree = NULL;
113
114 if((accept_fd != -1) && nterfacer_sock) {
115 esocket_clean_by_token(nterfacer_token);
116 nterfacer_sock = NULL;
117 accept_fd = -1;
118 }
119
120 if(permits && permit_count) {
121 for(i=0;i<permit_count;i++) {
122 freesstring(permits[i].hostname);
123 freesstring(permits[i].password);
124 }
125 free(permits);
126 permit_count = 0;
127 permits = NULL;
128 }
129
130 nrl = nterface_close_log(nrl);
131 }
132
133 int load_permits(void) {
134 int loaded_lines = 0, i, j;
135 struct permitted *new_permits, *resized, *item;
136 struct hostent *host;
137 array *hostnamesa, *passwordsa;
138 sstring **hostnames, **passwords;
139
140 hostnamesa = getconfigitems("nterfacer", "hostname");
141 passwordsa = getconfigitems("nterfacer", "password");
142 if(!hostnamesa || !passwordsa) {
143 nterface_log(nrl, NL_ERROR, "Unable to load hostnames/passwords.");
144 return 0;
145 }
146 if(hostnamesa->cursi != passwordsa->cursi) {
147 nterface_log(nrl, NL_ERROR, "Different number of hostnames/passwords in config file.");
148 return 0;
149 }
150
151 hostnames = (sstring **)hostnamesa->content;
152 passwords = (sstring **)passwordsa->content;
153
154 new_permits = calloc(hostnamesa->cursi, sizeof(struct permitted));
155 item = new_permits;
156
157 for(i=0;i<hostnamesa->cursi;i++) {
158 item->hostname = getsstring(hostnames[i]->content, hostnames[i]->length);
159
160 host = gethostbyname(item->hostname->content);
161 if (!host) {
162 nterface_log(nrl, NL_WARNING, "Couldn't resolve hostname: %s (item %d).", item->hostname->content, i + 1);
163 freesstring(item->hostname);
164 continue;
165 }
166
167 item->ihost = (*(struct in_addr *)host->h_addr).s_addr;
168 for(j=0;j<loaded_lines;j++) {
169 if(new_permits[j].ihost == item->ihost) {
170 nterface_log(nrl, NL_WARNING, "Host with items %d and %d is identical, dropping item %d.", j + 1, i + 1, i + 1);
171 host = NULL;
172 }
173 }
174
175 if(!host) {
176 freesstring(item->hostname);
177 continue;
178 }
179
180 item->password = getsstring(passwords[i]->content, passwords[i]->length);
181 nterface_log(nrl, NL_DEBUG, "Loaded permit, hostname: %s.", item->hostname->content);
182
183 item++;
184 loaded_lines++;
185 }
186
187 if(!loaded_lines) {
188 free(new_permits);
189 return 0;
190 }
191
192 resized = realloc(new_permits, sizeof(struct permitted) * loaded_lines);
193 if(!resized) {
194 MemError();
195 free(new_permits);
196 return 0;
197 }
198 permits = resized;
199 permit_count = loaded_lines;
200
201 return permit_count;
202 }
203
204 int setup_listening_socket(void) {
205 struct sockaddr_in sin;
206 int fd;
207 unsigned int opt = 1;
208
209 fd = socket(AF_INET, SOCK_STREAM, 0);
210
211 /* also shamelessly ripped from proxyscan */
212 if(fd == -1) {
213 nterface_log(nrl, NL_ERROR, "Unable to open listening socket (%d).", errno);
214 return -1;
215 }
216
217 if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *) &opt, sizeof(opt)) != 0) {
218 nterface_log(nrl, NL_ERROR, "Unable to set SO_REUSEADDR on listen socket.");
219 return -1;
220 }
221
222 /* Initialiase the addresses */
223 memset(&sin, 0, sizeof(sin));
224 sin.sin_family = AF_INET;
225 sin.sin_port = htons(getcopyconfigitemintpositive("nterfacer", "port", NTERFACER_PORT));
226
227 if(bind(fd, (struct sockaddr *) &sin, sizeof(sin))) {
228 nterface_log(nrl, NL_ERROR, "Unable to bind listen socket (%d).", errno);
229 return -1;
230 }
231
232 listen(fd, 5);
233
234 if(ioctl(fd, FIONBIO, &opt)) {
235 nterface_log(nrl, NL_ERROR, "Unable to set listen socket non-blocking.");
236 return -1;
237 }
238
239 return fd;
240 }
241
242 struct service_node *register_service(char *name) {
243 struct service_node *np = malloc(sizeof(service_node));
244 MemCheckR(np, NULL);
245
246 np->name = getsstring(name, strlen(name));
247 if(!np->name) {
248 MemError();
249 free(np);
250 return NULL;
251 }
252
253 np->handlers = NULL;
254 np->next = tree;
255 tree = np;
256
257 return np;
258 }
259
260 struct handler *register_handler(struct service_node *service, char *command, int args, handler_function fp) {
261 struct handler *hp = malloc(sizeof(handler));
262 MemCheckR(hp, NULL);
263
264 hp->command = getsstring(command, strlen(command));
265 if(!hp->command) {
266 MemError();
267 free(hp);
268 return NULL;
269 }
270
271 hp->function = fp;
272 hp->args = args;
273
274 hp->next = service->handlers;
275 hp->service = service;
276 service->handlers = hp;
277
278 return hp;
279 }
280
281 void deregister_handler(struct handler *hl) {
282 struct service_node *service = (struct service_node *)hl->service;
283 struct handler *np, *lp = NULL;
284 for(np=service->handlers;np;lp=np,np=np->next) {
285 if(hl == np) {
286 if(lp) {
287 lp->next = np->next;
288 } else {
289 service->handlers = np->next;
290 }
291 free_handler(np);
292 return;
293 }
294 }
295 }
296
297 void deregister_service(struct service_node *service) {
298 struct service_node *sp, *lp = NULL;
299 struct rline *li, *pi = NULL;
300
301 for(sp=tree;sp;lp=sp,sp=sp->next) {
302 if(sp == service) {
303 if(lp) {
304 lp->next = sp->next;
305 } else {
306 tree = sp->next;
307 }
308 break;
309 }
310 }
311
312 if(!sp) /* already freed */
313 return;
314
315 free_handlers(service);
316
317 for(li=rlines;li;) {
318 if(li->service == service) {
319 if(pi) {
320 pi->next = li->next;
321 free(li);
322 li = pi->next;
323 } else {
324 rlines = li->next;
325 free(li);
326 li = rlines;
327 }
328 } else {
329 pi=li,li=li->next;
330 }
331 }
332 freesstring(service->name);
333
334 free(service);
335 }
336
337 void nterfacer_accept_event(struct esocket *socket) {
338 struct sockaddr_in sin;
339 unsigned int addrsize = sizeof(sin);
340 int newfd = accept(socket->fd, (struct sockaddr *)&sin, &addrsize), i;
341 struct sconnect *temp;
342 struct permitted *item = NULL;
343 struct esocket *newsocket;
344 unsigned int opt = 1;
345
346 if(newfd == -1) {
347 nterface_log(nrl, NL_WARNING, "Unable to accept nterfacer fd!");
348 return;
349 }
350
351 if(ioctl(newfd, FIONBIO, &opt)) {
352 nterface_log(nrl, NL_ERROR, "Unable to set accepted socket non-blocking.");
353 return;
354 }
355
356 for(i=0;i<permit_count;i++) {
357 if(permits[i].ihost == sin.sin_addr.s_addr) {
358 item = &permits[i];
359 break;
360 }
361 }
362
363 if(!item) {
364 /* Someone needs to figure out how to print the IP :) */
365 nterface_log(nrl, NL_INFO, "Unauthorised connection closed");
366 close(newfd);
367 return;
368 }
369
370 temp = (struct sconnect *)malloc(sizeof(struct sconnect));
371 if(!temp) {
372 MemError();
373 close(newfd);
374 return;
375 }
376
377 /* do checks on hostname first */
378
379 newsocket = esocket_add(newfd, ESOCKET_UNIX_DOMAIN_CONNECTED, &nterfacer_events, nterfacer_token);
380 if(!newsocket) {
381 free(temp);
382 close(newfd);
383 return;
384 }
385 newsocket->tag = temp;
386
387 nterface_log(nrl, NL_INFO, "New connection from %s.", item->hostname->content);
388
389 temp->status = SS_IDLE;
390 temp->permit = item;
391
392 esocket_write_line(newsocket, "nterfacer " PROTOCOL_VERSION);
393 }
394
395 void derive_key(unsigned char *out, char *password, char *segment, unsigned char *noncea, unsigned char *nonceb, unsigned char *extra, int extralen) {
396 SHA256_CTX c;
397 SHA256_Init(&c);
398 SHA256_Update(&c, (unsigned char *)password, strlen(password));
399 SHA256_Update(&c, (unsigned char *)":", 1);
400 SHA256_Update(&c, (unsigned char *)segment, strlen(segment));
401 SHA256_Update(&c, (unsigned char *)":", 1);
402 SHA256_Update(&c, noncea, 16);
403 SHA256_Update(&c, (unsigned char *)":", 1);
404 SHA256_Update(&c, nonceb, 16);
405 SHA256_Update(&c, (unsigned char *)":", 1);
406 SHA256_Update(&c, extra, extralen);
407 SHA256_Final(out, &c);
408
409 SHA256_Init(&c);
410 SHA256_Update(&c, out, 32);
411 SHA256_Final(out, &c);
412 }
413
414 int nterfacer_line_event(struct esocket *sock, char *newline) {
415 struct sconnect *socket = sock->tag;
416 char *response, *theirnonceh = NULL, *theirivh = NULL;
417 unsigned char theirnonce[16], theiriv[16];
418 int number, reason;
419
420 switch(socket->status) {
421 case SS_IDLE:
422 if(strcasecmp(newline, ANTI_FULL_VERSION)) {
423 nterface_log(nrl, NL_INFO, "Protocol mismatch from %s: %s", socket->permit->hostname->content, newline);
424 return 1;
425 } else {
426 unsigned char challenge[32];
427 char ivhex[16 * 2 + 1], noncehex[16 * 2 + 1];
428
429 if(!get_entropy(challenge, 32) || !get_entropy(socket->iv, 16)) {
430 nterface_log(nrl, NL_ERROR, "Unable to open challenge/IV entropy bin!");
431 return 1;
432 }
433
434 int_to_hex(challenge, socket->challenge, 32);
435 int_to_hex(socket->iv, ivhex, 16);
436
437 memcpy(socket->response, challenge_response(socket->challenge, socket->permit->password->content), sizeof(socket->response));
438 socket->response[sizeof(socket->response) - 1] = '\0'; /* just in case */
439
440 socket->status = SS_VERSIONED;
441 if(!generate_nonce(socket->ournonce, 1)) {
442 nterface_log(nrl, NL_ERROR, "Unable to generate nonce!");
443 return 1;
444 }
445 int_to_hex(socket->ournonce, noncehex, 16);
446
447 if(esocket_write_line(sock, "%s %s %s", socket->challenge, ivhex, noncehex))
448 return BUF_ERROR;
449 return 0;
450 }
451 break;
452 case SS_VERSIONED:
453 for(response=newline;*response;response++) {
454 if((*response == ' ') && (*(response + 1))) {
455 *response = '\0';
456 theirivh = response + 1;
457 break;
458 }
459 }
460
461 if(theirivh) {
462 for(response=theirivh;*response;response++) {
463 if((*response == ' ') && (*(response + 1))) {
464 *response = '\0';
465 theirnonceh = response + 1;
466 break;
467 }
468 }
469 }
470
471 if(!theirivh || (strlen(theirivh) != 32) || !hex_to_int(theirivh, theiriv, sizeof(theiriv)) ||
472 !theirnonceh || (strlen(theirnonceh) != 32) || !hex_to_int(theirnonceh, theirnonce, sizeof(theirnonce))) {
473 nterface_log(nrl, NL_INFO, "Protocol error drop: %s", socket->permit->hostname->content);
474 return 1;
475 }
476
477 if(!memcmp(socket->ournonce, theirnonce, sizeof(theirnonce))) {
478 nterface_log(nrl, NL_INFO, "Bad nonce drop: %s", socket->permit->hostname->content);
479 return 1;
480 }
481
482 if(!strncasecmp(newline, socket->response, sizeof(socket->response))) {
483 unsigned char theirkey[32], ourkey[32];
484
485 derive_key(ourkey, socket->permit->password->content, socket->challenge, socket->ournonce, theirnonce, (unsigned char *)"SERVER", 6);
486
487 derive_key(theirkey, socket->permit->password->content, socket->response, theirnonce, socket->ournonce, (unsigned char *)"CLIENT", 6);
488 nterface_log(nrl, NL_INFO, "Authed: %s", socket->permit->hostname->content);
489 socket->status = SS_AUTHENTICATED;
490 switch_buffer_mode(sock, ourkey, socket->iv, theirkey, theiriv);
491
492 if(esocket_write_line(sock, "Oauth"))
493 return BUF_ERROR;
494 } else {
495 nterface_log(nrl, NL_INFO, "Bad CR drop: %s", socket->permit->hostname->content);
496
497 return 1;
498 }
499 break;
500 case SS_AUTHENTICATED:
501 nterface_log(nrl, NL_INFO|NL_LOG_ONLY, "L(%s): %s", socket->permit->hostname->content, newline);
502 reason = nterfacer_new_rline(newline, sock, &number);
503 if(reason) {
504 if(reason == RE_SOCKET_ERROR)
505 return BUF_ERROR;
506 if(reason != RE_BAD_LINE) {
507 if(esocket_write_line(sock, "%d,E%d,%s", number, reason, request_error(reason)))
508 return BUF_ERROR;
509 return 0;
510 } else {
511 return 1;
512 }
513 }
514 break;
515 }
516
517 return 0;
518 }
519
520 int nterfacer_new_rline(char *line, struct esocket *socket, int *number) {
521 char *sp, *p, *parsebuf = NULL, *pp, commandbuf[MAX_BUFSIZE], *args[MAX_ARGS], *newp;
522 int argcount;
523 struct service_node *service;
524 struct rline *prequest;
525 struct handler *hl;
526 int re;
527
528 if(!line || !line[0] || (line[0] == ','))
529 return 0;
530
531 for(sp=line;*sp;sp++)
532 if(*sp == ',')
533 break;
534
535 if(!*sp || !*(sp + 1))
536 return RE_BAD_LINE;
537
538 *sp = '\0';
539
540 for(service=tree;service;service=service->next)
541 if(!strcmp(service->name->content, line))
542 break;
543
544 for(p=sp+1;*p;p++)
545 if(*p == ',')
546 break;
547
548 if(!*p || !(p + 1))
549 return RE_BAD_LINE;
550
551 *p = '\0';
552 *number = positive_atoi(sp + 1);
553
554 if((*number < 1) || (*number > 0xffff))
555 return RE_BAD_LINE;
556
557 if (!service) {
558 nterface_log(nrl, NL_DEBUG, "Unable to find service: %s", line);
559 return RE_SERVICER_NOT_FOUND;
560 }
561
562 newp = commandbuf;
563
564 for(pp=p+1;*pp;pp++) {
565 if((*pp == '\\') && *(pp + 1)) {
566 if(*(pp + 1) == ',') {
567 *newp++ = ',';
568 } else if(*(pp + 1) == '\\') {
569 *newp++ = '\\';
570 }
571 pp++;
572 } else if(*pp == ',') {
573 break;
574 } else {
575 *newp++ = *pp;
576 }
577 }
578
579 if(*pp == '\0') { /* if we're at the end already, we have no arguments */
580 argcount = 0;
581 } else {
582 argcount = 1; /* we have a comma, so we have one already */
583 }
584
585 *newp = '\0';
586
587 for(hl=service->handlers;hl;hl=hl->next)
588 if(!strncmp(hl->command->content, commandbuf, sizeof(commandbuf)))
589 break;
590
591 if(!hl)
592 return RE_COMMAND_NOT_FOUND;
593
594 if(argcount) {
595 parsebuf = (char *)malloc(strlen(pp) + 1);
596 MemCheckR(parsebuf, RE_MEM_ERROR);
597 newp = parsebuf;
598
599 for(newp=args[0]=parsebuf,pp++;*pp;pp++) {
600 if((*pp == '\\') && *(pp + 1)) {
601 if(*(pp + 1) == ',') {
602 *newp++ = ',';
603 } else if(*(pp + 1) == '\\') {
604 *newp++ = '\\';
605 }
606 pp++;
607 } else if(*pp == ',') {
608 *newp++ = '\0';
609 args[argcount++] = newp;
610 if(argcount > MAX_ARGS) {
611 free(parsebuf);
612 return RE_TOO_MANY_ARGS;
613 }
614 } else {
615 *newp++ = *pp;
616 }
617 }
618 *newp = '\0';
619 }
620 if(argcount < hl->args) {
621 if(argcount && parsebuf)
622 free(parsebuf);
623 return RE_WRONG_ARG_COUNT;
624 }
625
626 prequest = (struct rline *)malloc(sizeof(struct rline));
627 if(!prequest) {
628 MemError();
629 if(argcount && parsebuf)
630 free(parsebuf);
631 return RE_MEM_ERROR;
632 }
633
634 prequest->service = service;
635 prequest->handler = hl;
636 prequest->buf[0] = '\0';
637 prequest->curpos = prequest->buf;
638 prequest->tag = NULL;
639 prequest->id = *number;
640 prequest->next = rlines;
641 prequest->socket = socket;
642
643 rlines = prequest;
644 re = (hl->function)(prequest, argcount, args);
645
646 if(argcount && parsebuf)
647 free(parsebuf);
648
649 return re;
650 }
651
652 void nterfacer_disconnect_event(struct esocket *sock) {
653 struct sconnect *socket = sock->tag;
654 struct rline *li;
655 /* not tested */
656
657 nterface_log(nrl, NL_INFO, "Disconnected from %s.", socket->permit->hostname->content);
658
659 /* not tested */
660 for(li=rlines;li;li=li->next)
661 if(li->socket->tag == socket)
662 li->socket = NULL;
663
664 free(socket);
665 }
666
667 int ri_append(struct rline *li, char *format, ...) {
668 char buf[MAX_BUFSIZE], escapedbuf[MAX_BUFSIZE * 2 + 1], *p, *tp;
669 int sizeleft = sizeof(li->buf) - (li->curpos - li->buf);
670 va_list ap;
671
672 va_start(ap, format);
673
674 if(vsnprintf(buf, sizeof(buf), format, ap) >= sizeof(buf)) {
675 va_end(ap);
676 return BF_OVER;
677 }
678
679 va_end(ap);
680
681 for(tp=escapedbuf,p=buf;*p||(*tp='\0');*tp++=*p++)
682 if((*p == ',') || (*p == '\\'))
683 *tp++ = '\\';
684
685 if(sizeleft > 0) {
686 if(li->curpos == li->buf) {
687 li->curpos+=snprintf(li->curpos, sizeleft, "%s", escapedbuf);
688 } else {
689 li->curpos+=snprintf(li->curpos, sizeleft, ",%s", escapedbuf);
690 }
691 }
692
693 if(sizeof(li->buf) - (li->curpos - li->buf) > 0) {
694 return BF_OK;
695 } else {
696 return BF_OVER;
697 }
698 }
699
700 int ri_error(struct rline *li, int error_code, char *format, ...) {
701 char buf[MAX_BUFSIZE], escapedbuf[MAX_BUFSIZE * 2 + 1], *p, *tp;
702 struct rline *pp, *lp = NULL;
703 va_list ap;
704 int retval = RE_OK;
705
706 if(li->socket) {
707 va_start(ap, format);
708 vsnprintf(buf, sizeof(buf), format, ap);
709 va_end(ap);
710
711 for(tp=escapedbuf,p=buf;*p||(*tp='\0');*tp++=*p++)
712 if((*p == ',') || (*p == '\\'))
713 *tp++ = '\\';
714
715 if(esocket_write_line(li->socket, "%d,OE%d,%s", li->id, error_code, escapedbuf))
716 retval = RE_SOCKET_ERROR;
717 }
718
719 for(pp=rlines;pp;lp=pp,pp=pp->next) {
720 if(pp == li) {
721 if(lp) {
722 lp->next = li->next;
723 } else {
724 rlines = li->next;
725 }
726 free(li);
727 break;
728 }
729 }
730
731 return retval;
732 }
733
734 int ri_final(struct rline *li) {
735 struct rline *pp, *lp = NULL;
736 int retval = RE_OK;
737
738 if(li->socket)
739 if(esocket_write_line(li->socket, "%d,OO%s", li->id, li->buf))
740 retval = RE_SOCKET_ERROR;
741
742 for(pp=rlines;pp;lp=pp,pp=pp->next) {
743 if(pp == li) {
744 if(lp) {
745 lp->next = li->next;
746 } else {
747 rlines = li->next;
748 }
749 free(li);
750 break;
751 }
752 }
753
754 return retval;
755 }
756
757 int ping_handler(struct rline *ri, int argc, char **argv) {
758 ri_append(ri, "OK");
759 return ri_final(ri);
760 }