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