]> jfr.im git - irc/quakenet/newserv.git/blob - trusts/trusts_policy.c
Add usercountadjustment where it was still missing.
[irc/quakenet/newserv.git] / trusts / trusts_policy.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdarg.h>
4 #include <unistd.h>
5 #include <sys/types.h>
6 #include <sys/socket.h>
7 #include <netinet/in.h>
8
9 #ifndef __USE_MISC
10 #define __USE_MISC /* inet_aton */
11 #endif
12
13 #include <arpa/inet.h>
14 #include <sys/un.h>
15 #include <sys/poll.h>
16 #include <errno.h>
17 #include <fcntl.h>
18
19 #include "../lib/hmac.h"
20 #include "../core/events.h"
21 #include "../core/schedule.h"
22 #include "../core/nsmalloc.h"
23 #include "../core/hooks.h"
24 #include "../core/config.h"
25 #include "../control/control.h"
26 #include "../lib/irc_string.h"
27 #include "../irc/irc.h"
28 #include "../glines/glines.h"
29 #include "trusts.h"
30
31 static int countext, enforcepolicy_irc, enforcepolicy_auth;
32
33 #define TRUSTBUFSIZE 8192
34 #define TRUSTPASSLEN 128
35 #define NONCELEN 16
36
37 typedef struct trustsocket {
38 int fd;
39 int authed;
40 char authuser[SERVERLEN+1];
41 char buf[TRUSTBUFSIZE];
42 unsigned char nonce[NONCELEN];
43 int size;
44 time_t connected;
45 time_t timeout;
46 int accepted;
47 int rejected;
48
49 struct trustsocket *next;
50 } trustsocket;
51
52 static trustsocket *tslist;
53 static int listenerfd = -1;
54 static FILE *urandom;
55
56 typedef struct trustaccount {
57 int used;
58 char server[SERVERLEN+1];
59 char password[TRUSTPASSLEN+1];
60 } trustaccount;
61
62 trustaccount trustaccounts[MAXSERVERS];
63
64 static int checkconnectionth(const char *username, struct irc_in_addr *ip, trusthost *th, int hooknum, int usercountadjustment, char *message, size_t messagelen) {
65 trustgroup *tg;
66
67 if(messagelen>0)
68 message[0] = '\0';
69
70 if(!th || !trustsdbloaded)
71 return POLICY_SUCCESS;
72
73 tg = th->group;
74
75 /*
76 * the purpose of this logic is to avoid spam like this:
77 * WARNING: tgX exceeded limit: 11 connected vs 10 max
78 * (goes back down to 10)
79 * WARNING: tgX exceeded limit: 11 connected vs 10 max
80 */
81
82 if(hooknum == HOOK_TRUSTS_NEWNICK) {
83 patricia_node_t *head, *node;
84 int nodecount = 0;
85
86 head = refnode(iptree, ip, th->nodebits);
87 PATRICIA_WALK(head, node)
88 {
89 nodecount += node->usercount;
90 }
91 PATRICIA_WALK_END;
92 derefnode(iptree, head);
93
94 if(th->maxpernode && nodecount + usercountadjustment > th->maxpernode) {
95 controlwall(NO_OPER, NL_TRUSTS, "Hard connection limit exceeded on subnet: %s (group: %s): %d connected, %d max.", trusts_cidr2str(ip, th->nodebits), tg->name->content, nodecount + usercountadjustment, th->maxpernode);
96 snprintf(message, messagelen, "Too many connections from your host (%s) - see http://www.quakenet.org/help/trusts/connection-limit for details.", IPtostr(*ip));
97 return POLICY_FAILURE_NODECOUNT;
98 }
99
100 if(tg->trustedfor && tg->count + usercountadjustment > tg->trustedfor) {
101 if(tg->count > (long)tg->exts[countext]) {
102 tg->exts[countext] = (void *)(long)tg->count;
103
104 controlwall(NO_OPER, NL_TRUSTS, "Hard connection limit exceeded (group %s): %d connected, %d max.", tg->name->content, tg->count + usercountadjustment, tg->trustedfor);
105 snprintf(message, messagelen, "Too many connections from your trust (%s) - see http://www.quakenet.org/help/trusts/connection-limit for details.", IPtostr(*ip));
106 return POLICY_FAILURE_GROUPCOUNT;
107 }
108
109 snprintf(message, messagelen, "Trust has %d out of %d allowed connections.", tg->count + usercountadjustment, tg->trustedfor);
110 }
111
112 if((tg->flags & TRUST_ENFORCE_IDENT) && (username[0] == '~')) {
113 controlwall(NO_OPER, NL_TRUSTS, "Ident required: %s@%s (group: %s).", username, IPtostr(*ip), tg->name->content);
114 snprintf(message, messagelen, "IDENTD required from your host (%s) - see http://www.quakenet.org/help/trusts/connection-limit for details.", IPtostr(*ip));
115 return POLICY_FAILURE_IDENTD;
116 }
117
118 if(tg->maxperident > 0) {
119 int identcount = 0;
120 trusthost *th2;
121 nick *tnp;
122
123 for(th2=tg->hosts;th2;th2=th2->next) {
124 for(tnp=th2->users;tnp;tnp=nextbytrust(tnp)) {
125 if(!ircd_strcmp(tnp->ident, username))
126 identcount++;
127 }
128 }
129
130 if(identcount + usercountadjustment > tg->maxperident) {
131 controlwall(NO_OPER, NL_TRUSTS, "Hard ident limit exceeded: %s@%s (group: %s): %d connected, %d max.", username, IPtostr(*ip), tg->name->content, identcount + usercountadjustment, tg->maxperident);
132 snprintf(message, messagelen, "Too many connections from your username (%s@%s) - see http://www.quakenet.org/help/trusts/connection-limit for details.", username, IPtostr(*ip));
133 return POLICY_FAILURE_IDENTCOUNT;
134 }
135 }
136 } else {
137 if(tg->count < tg->maxusage)
138 tg->exts[countext] = (void *)(long)tg->count;
139 }
140
141 return POLICY_SUCCESS;
142 }
143
144 static int checkconnection(const char *username, struct irc_in_addr *ip, int hooknum, int cloneadjustment, char *message, size_t messagelen) {
145 struct irc_in_addr ip_canonicalized;
146 ip_canonicalize_tunnel(&ip_canonicalized, ip);
147
148 return checkconnectionth(username, &ip_canonicalized, th_getbyhost(&ip_canonicalized), hooknum, cloneadjustment, message, messagelen);
149 }
150
151 static int trustdowrite(trustsocket *sock, char *format, ...) {
152 char buf[1024];
153 va_list va;
154 int r;
155
156 va_start(va, format);
157 r = vsnprintf(buf, sizeof(buf), format, va);
158 va_end(va);
159
160 if(r >= sizeof(buf))
161 r = sizeof(buf) - 1;
162
163 buf[r] = '\n';
164
165 if(write(sock->fd, buf, r + 1) != r + 1)
166 return 0;
167 return 1;
168 }
169
170 static int policycheck_auth(trustsocket *sock, const char *sequence_id, const char *username, const char *host) {
171 char message[512];
172 int verdict;
173 struct irc_in_addr ip;
174 unsigned char bits;
175
176 if(!ipmask_parse(host, &ip, &bits)) {
177 sock->accepted++;
178 return trustdowrite(sock, "PASS %s", sequence_id);
179 }
180
181 verdict = checkconnection(username, &ip, HOOK_TRUSTS_NEWNICK, 1, message, sizeof(message));
182
183 if(!enforcepolicy_auth)
184 verdict = POLICY_SUCCESS;
185
186 if (verdict == POLICY_SUCCESS) {
187 sock->accepted++;
188
189 if(message[0])
190 return trustdowrite(sock, "PASS %s %s", sequence_id, message);
191 else
192 return trustdowrite(sock, "PASS %s", sequence_id);
193 } else {
194 sock->rejected++;
195
196 controlwall(NO_OPER, NL_TRUSTS, "Rejected connection from %s@%s using IAuth: %s", username, host, message);
197 return trustdowrite(sock, "KILL %s %s", sequence_id, message);
198 }
199 }
200
201 static int trustkillconnection(trustsocket *sock, char *reason) {
202 trustdowrite(sock, "QUIT %s", reason);
203 return 0;
204 }
205
206 static void trustfreeconnection(trustsocket *sock) {
207 trustsocket **pnext = &tslist;
208
209 for(trustsocket *ts=tslist;ts;ts=ts->next) {
210 if(ts == sock) {
211 deregisterhandler(sock->fd, 1);
212 *pnext = sock->next;
213 nsfree(POOL_TRUSTS, sock);
214 break;
215 }
216
217 pnext = &(sock->next);
218 }
219 }
220
221 static int handletrustauth(trustsocket *sock, char *server_name, char *mac) {
222 int i;
223 char *password = NULL;
224 unsigned char digest[16];
225 char noncehexbuf[NONCELEN * 2 + 1];
226 char hexbuf[sizeof(digest) * 2 + 1];
227
228 for(i=0;i<MAXSERVERS;i++) {
229 if(trustaccounts[i].used && strcmp(trustaccounts[i].server, server_name) == 0) {
230 password = trustaccounts[i].password;
231 break;
232 }
233 }
234
235 if (!password) {
236 controlwall(NO_OPER, NL_TRUSTS, "Invalid servername for policy socket: '%s'", server_name);
237 return trustkillconnection(sock, "Invalid servername.");
238 }
239
240 hmacmd5 h;
241 hmacmd5_init(&h, (unsigned char *)password, strlen(password));
242 hmacmd5_update(&h, (unsigned char *)hmac_printhex(sock->nonce, noncehexbuf, NONCELEN), NONCELEN * 2);
243 hmacmd5_final(&h, digest);
244 if(hmac_strcmp(mac, hmac_printhex(digest, hexbuf, sizeof(digest)))) {
245 controlwall(NO_OPER, NL_TRUSTS, "Invalid password for policy socket with servername '%s'.", server_name);
246 return trustkillconnection(sock, "Bad MAC.");
247 }
248
249 sock->authed = 1;
250 strncpy(sock->authuser, server_name, SERVERLEN);
251
252 controlwall(NO_OPER, NL_TRUSTS, "Successful authentication for policy socket with servername '%s'.", server_name);
253 return trustdowrite(sock, "AUTHOK");
254 }
255
256 #define MAXTOKENS 10
257 static int handletrustline(trustsocket *sock, char *line) {
258 char *command, *p, *lastpos;
259 char *tokens[MAXTOKENS];
260 int tokensfound = -1;
261
262 for(command=lastpos=p=line;*p;p++) {
263 if(*p == ' ') {
264 *p = '\0';
265 if(tokensfound == MAXTOKENS)
266 return trustkillconnection(sock, "too many tokens");
267
268 if(tokensfound >= 0) {
269 tokens[tokensfound++] = lastpos;
270 } else {
271 tokensfound++;
272 }
273 lastpos = p + 1;
274 }
275 }
276 if(lastpos != p) {
277 if(tokensfound == MAXTOKENS)
278 return trustkillconnection(sock, "too many tokens");
279 tokens[tokensfound++] = lastpos;
280 }
281
282 if(!sock->authed && !strcmp("AUTH", command)) {
283 if(tokensfound != 2)
284 return trustkillconnection(sock, "incorrect arg count for command.");
285
286 return handletrustauth(sock, tokens[0], tokens[1]);
287 } else if(sock->authed && !strcmp("CHECK", command)) {
288 if(tokensfound != 3)
289 return trustkillconnection(sock, "incorrect arg count for command.");
290
291 policycheck_auth(sock, tokens[0], tokens[1], tokens[2]);
292 return 1;
293 } else {
294 Error("trusts_policy", ERR_WARNING, "Bad command: %s", command);
295 return 0;
296 }
297 }
298
299 static trustsocket *findtrustsocketbyfd(int fd) {
300 for(trustsocket *ts=tslist;ts;ts=ts->next)
301 if(ts->fd==fd)
302 return ts;
303
304 return NULL;
305 }
306
307 static int handletrustclient(trustsocket *sock) {
308 int r, remaining = TRUSTBUFSIZE - sock->size, i;
309 char *lastpos, *c;
310
311 if(!remaining) {
312 trustkillconnection(sock, "Buffer overflow.");
313 return 0;
314 }
315
316 r = read(sock->fd, sock->buf + sock->size, remaining);
317 if(r <= 0)
318 return 0;
319
320 sock->size+=r;
321 lastpos = sock->buf;
322
323 for(c=sock->buf,i=0;i<sock->size;i++,c++) {
324 if(*c != '\n')
325 continue;
326 *c = '\0';
327 if(!handletrustline(sock, lastpos))
328 return 0;
329
330 lastpos = c + 1; /* is this ok? */
331 }
332 sock->size-=lastpos - sock->buf;
333 memmove(sock->buf, lastpos, sock->size);
334
335 return 1;
336 }
337
338 static void processtrustclient(int fd, short events) {
339 trustsocket *sock = findtrustsocketbyfd(fd);
340
341 if(!sock)
342 return;
343
344 if(events & POLLIN)
345 if(!handletrustclient(sock))
346 trustfreeconnection(sock);
347 }
348
349 static void trustdotimeout(void *arg) {
350 time_t t = time(NULL);
351 trustsocket **pnext, *next, *sock;
352
353 pnext = &tslist;
354
355 for(sock=tslist;sock;) {
356 next = sock->next;
357
358 if(!sock->authed && t >= sock->timeout) {
359 trustkillconnection(sock, "Auth timeout.");
360 deregisterhandler(sock->fd, 1);
361 *pnext = sock->next;
362 nsfree(POOL_TRUSTS, sock);
363 } else {
364 pnext = &(sock->next);
365 }
366
367 sock=next;
368 }
369 }
370
371 static void processtrustlistener(int fd, short events) {
372 if(events & POLLIN) {
373 trustsocket *sock;
374 char buf[NONCELEN * 2 + 1];
375
376 int newfd = accept(fd, NULL, NULL), flags;
377 if(newfd == -1)
378 return;
379
380 flags = fcntl(newfd, F_GETFL, 0);
381 if(flags < 0) {
382 Error("trusts_policy", ERR_WARNING, "Unable to set socket non-blocking.");
383 close(newfd);
384 return;
385 }
386
387 if(fcntl(fd, F_SETFL, flags|O_NONBLOCK) < 0) {
388 Error("trusts_policy", ERR_WARNING, "Unable to set socket non-blocking.");
389 close(newfd);
390 return;
391 }
392
393 registerhandler(newfd, POLLIN|POLLERR|POLLHUP, processtrustclient);
394
395 sock = nsmalloc(POOL_TRUSTS, sizeof(trustsocket));
396 if(!sock) {
397 deregisterhandler(newfd, 1);
398 return;
399 }
400
401 sock->fd = newfd;
402 sock->next = tslist;
403 tslist = sock;
404
405 if(fread((char *)sock->nonce, 1, NONCELEN, urandom) != NONCELEN) {
406 Error("trusts_policy", ERR_WARNING, "Error getting random bytes.");
407 deregisterhandler(newfd, 1);
408 tslist = sock->next;
409 nsfree(POOL_TRUSTS, sock);
410 } else {
411 sock->authed = 0;
412 sock->size = 0;
413 sock->connected = time(NULL);
414 sock->timeout = time(NULL) + 30;
415 sock->accepted = 0;
416 sock->rejected = 0;
417 if(!trustdowrite(sock, "AUTH %s", hmac_printhex(sock->nonce, buf, NONCELEN))) {
418 Error("trusts_policy", ERR_WARNING, "Error writing auth to fd %d.", newfd);
419 deregisterhandler(newfd, 1);
420 tslist = sock->next;
421 nsfree(POOL_TRUSTS, sock);
422 return;
423 }
424 }
425 }
426 }
427
428 static int createlistenersock(int port) {
429 struct sockaddr_in s;
430 int fd;
431 int optval;
432
433 memset(&s, 0, sizeof(struct sockaddr_in));
434 s.sin_family = AF_INET;
435 s.sin_addr.s_addr = INADDR_ANY;
436 s.sin_port = htons(port);
437
438 fd = socket(PF_INET, SOCK_STREAM, 0);
439 if(fd < 0) {
440 Error("trusts_policy", ERR_WARNING, "Unable to get socket for trustfd.");
441 return -1;
442 }
443
444 optval = 1;
445 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
446
447 if(bind(fd, (struct sockaddr *)&s, sizeof(struct sockaddr_in)) < 0) {
448 Error("trusts_policy", ERR_WARNING, "Unable to bind trustfd.");
449 close(fd);
450 return -1;
451 }
452
453 if(listen(fd, 5) < 0) {
454 Error("trusts_policy", ERR_WARNING, "Unable to listen on trustfd.");
455 close(fd);
456 return -1;
457 }
458
459 registerhandler(fd, POLLIN, processtrustlistener);
460
461 return fd;
462 }
463
464 static void policycheck_irc(int hooknum, void *arg) {
465 void **args = arg;
466 nick *np = args[0];
467 long moving = (long)args[1];
468 char message[512];
469 int verdict;
470
471 if(moving)
472 return;
473
474 verdict = checkconnectionth(np->ident, &np->p_nodeaddr, gettrusthost(np), hooknum, 0, message, sizeof(message));
475
476 if(!enforcepolicy_irc)
477 verdict = POLICY_SUCCESS;
478
479 switch (verdict) {
480 case POLICY_FAILURE_NODECOUNT:
481 glinebynick(np, POLICY_GLINE_DURATION, message, GLINE_IGNORE_TRUST, "trusts_policy");
482 break;
483 case POLICY_FAILURE_IDENTD:
484 glinebyip("~*", &np->p_ipaddr, 128, POLICY_GLINE_DURATION, message, GLINE_ALWAYS_USER|GLINE_IGNORE_TRUST, "trusts_policy");
485 break;
486 case POLICY_FAILURE_IDENTCOUNT:
487 glinebynick(np, POLICY_GLINE_DURATION, message, GLINE_ALWAYS_USER|GLINE_IGNORE_TRUST, "trusts_policy");
488 break;
489 }
490 }
491
492 static int trusts_cmdtrustpolicyirc(void *source, int cargc, char **cargv) {
493 nick *sender = source;
494
495 if(cargc < 1) {
496 controlreply(sender, "Use of glines for trust policy enforcement is currently %s.", enforcepolicy_irc?"enabled":"disabled");
497 return CMD_OK;
498 }
499
500 enforcepolicy_irc = atoi(cargv[0]);
501 controlwall(NO_OPER, NL_TRUSTS, "%s %s use of glines for trust policy enforcement.", controlid(sender), enforcepolicy_irc?"enabled":"disabled");
502 controlreply(sender, "Use of glines for trust policy enforcement is now %s.", enforcepolicy_irc?"enabled":"disabled");
503
504 return CMD_OK;
505 }
506
507 static int trusts_cmdtrustpolicyauth(void *source, int cargc, char **cargv) {
508 nick *sender = source;
509
510 if(cargc < 1) {
511 controlreply(sender, "Trust policy enforcement with IAuth is currently %s.", enforcepolicy_auth?"enabled":"disabled");
512 return CMD_OK;
513 }
514
515 enforcepolicy_auth = atoi(cargv[0]);
516 controlwall(NO_OPER, NL_TRUSTS, "%s %s trust policy enforcement with IAuth.", controlid(sender), enforcepolicy_auth?"enabled":"disabled");
517 controlreply(sender, "Trust policy enforcement with IAuth is now %s.", enforcepolicy_auth?"enabled":"disabled");
518
519 return CMD_OK;
520 }
521
522
523 static int trusts_cmdtrustsockets(void *source, int cargc, char **cargv) {
524 nick *sender = source;
525 time_t now;
526 trustsocket *sock;
527
528 time(&now);
529
530 controlreply(sender, "Server Connected for Accepted Rejected");
531
532 for(sock=tslist;sock;sock=sock->next)
533 controlreply(sender, "%-20s %20s %10d %15d", sock->authed?sock->authuser:"<not authenticated>", longtoduration(now - sock->connected, 0), sock->accepted, sock->rejected);
534
535 controlreply(sender, "-- End of list.");
536 return CMD_OK;
537 }
538
539 void loadtrustaccounts(void) {
540 array *accts;
541
542 memset(trustaccounts, 0, sizeof(trustaccounts));
543
544 accts = getconfigitems("trusts_policy", "server");
545 if(!accts) {
546 Error("trusts_policy", ERR_INFO, "No servers added.");
547 } else {
548 sstring **servers = (sstring **)(accts->content);
549 int i;
550 for(i=0;i<accts->cursi;i++) {
551 char server[512];
552 char *pos;
553
554 if(i>=MAXSERVERS) {
555 Error("trusts_policy", ERR_INFO, "Too many servers specified.");
556 break;
557 }
558
559 strncpy(server, servers[i]->content, sizeof(server));
560
561 pos = strchr(server, ',');
562
563 if(!pos) {
564 Error("trusts_policy", ERR_INFO, "Server line is missing password: %s", server);
565 continue;
566 }
567
568 *pos = '\0';
569
570 trustaccounts[i].used = 1;
571 strncpy(trustaccounts[i].server, server, SERVERLEN);
572 strncpy(trustaccounts[i].password, pos+1, TRUSTPASSLEN);
573 }
574 }
575 }
576
577 static void trustaccounts_rehash(int hooknum, void *arg) {
578 loadtrustaccounts();
579 }
580
581 void _init(void) {
582 sstring *m;
583 int trustport;
584
585 countext = registertgext("count");
586 if(countext == -1)
587 return;
588
589 m = getconfigitem("trusts_policy", "enforcepolicy_irc");
590 if(m)
591 enforcepolicy_irc = atoi(m->content);
592
593 m = getconfigitem("trusts_policy", "enforcepolicy_auth");
594 if(m)
595 enforcepolicy_auth = atoi(m->content);
596
597 m = getconfigitem("trusts_policy", "trustport");
598 if(m)
599 trustport = atoi(m->content);
600 else
601 trustport = DEFAULT_TRUSTPORT;
602
603 if(trustport)
604 listenerfd = createlistenersock(trustport);
605
606 loadtrustaccounts();
607
608 registerhook(HOOK_TRUSTS_NEWNICK, policycheck_irc);
609 registerhook(HOOK_TRUSTS_LOSTNICK, &policycheck_irc);
610 registerhook(HOOK_CORE_REHASH, trustaccounts_rehash);
611
612 registercontrolhelpcmd("trustpolicyirc", NO_DEVELOPER, 1, trusts_cmdtrustpolicyirc, "Usage: trustpolicyirc ?1|0?\nEnables or disables policy enforcement (IRC). Shows current status when no parameter is specified.");
613 registercontrolhelpcmd("trustpolicyauth", NO_DEVELOPER, 1, trusts_cmdtrustpolicyauth, "Usage: trustpolicyauth ?1|0?\nEnables or disables policy enforcement (IAuth). Shows current status when no parameter is specified.");
614 registercontrolhelpcmd("trustsockets", NO_DEVELOPER, 0, trusts_cmdtrustsockets, "Usage: trustsockets\nLists all currently active TRUST sockets.");
615
616 schedulerecurring(time(NULL)+1, 0, 5, trustdotimeout, NULL);
617
618 urandom = fopen("/dev/urandom", "rb");
619 if(!urandom)
620 Error("trusts_policy", ERR_ERROR, "Couldn't open /dev/urandom.");
621 }
622
623 void _fini(void) {
624 trustsocket *sock, *next;
625
626 if(countext == -1)
627 return;
628
629 releasetgext(countext);
630
631 deregisterhook(HOOK_TRUSTS_NEWNICK, policycheck_irc);
632 deregisterhook(HOOK_TRUSTS_LOSTNICK, policycheck_irc);
633 deregisterhook(HOOK_CORE_REHASH, trustaccounts_rehash);
634
635 deregistercontrolcmd("trustpolicyirc", trusts_cmdtrustpolicyirc);
636 deregistercontrolcmd("trustpolicyauth", trusts_cmdtrustpolicyauth);
637 deregistercontrolcmd("trustsockets", trusts_cmdtrustsockets);
638
639 deleteallschedules(trustdotimeout);
640
641 if (urandom)
642 fclose(urandom);
643
644 if (listenerfd != -1)
645 deregisterhandler(listenerfd, 1);
646
647 for(sock=tslist;sock;) {
648 next = sock->next;
649
650 trustkillconnection(sock, "Unloading module.");
651 trustfreeconnection(sock);
652
653 sock = next;
654 }
655 }