6 #include <sys/socket.h>
7 #include <netinet/in.h>
8 #include <netinet/tcp.h>
11 #define __USE_MISC /* inet_aton */
14 #include <arpa/inet.h>
20 #include "../lib/version.h"
21 #include "../lib/hmac.h"
22 #include "../core/events.h"
23 #include "../core/schedule.h"
24 #include "../core/nsmalloc.h"
25 #include "../core/hooks.h"
26 #include "../core/config.h"
27 #include "../control/control.h"
28 #include "../lib/irc_string.h"
29 #include "../irc/irc.h"
30 #include "../glines/glines.h"
31 #include "../patricianick/patricianick.h"
36 static int countext
, enforcepolicy_irc
, enforcepolicy_auth
;
38 #define TRUSTBUFSIZE 8192
39 #define TRUSTPASSLEN 128
42 typedef struct trustsocket
{
45 char authuser
[SERVERLEN
+1];
46 char buf
[TRUSTBUFSIZE
];
47 unsigned char nonce
[NONCELEN
];
55 struct trustsocket
*next
;
58 static trustsocket
*tslist
;
59 static int listenerfd
= -1;
62 typedef struct trustaccount
{
64 char server
[SERVERLEN
+1];
65 char password
[TRUSTPASSLEN
+1];
68 trustaccount trustaccounts
[MAXSERVERS
];
70 static int checkconnection(const char *username
, struct irc_in_addr
*ipaddress
, int hooknum
, int usercountadjustment
, char *message
, size_t messagelen
, int *unthrottle
) {
73 struct irc_in_addr ipaddress_canonical
;
75 ip_canonicalize_tunnel(&ipaddress_canonical
, ipaddress
);
77 th
= th_getbyhost(&ipaddress_canonical
);
85 if(!th
|| !trustsdbloaded
|| irc_in_addr_is_loopback(ipaddress
))
86 return POLICY_SUCCESS
;
91 * the purpose of this logic is to avoid spam like this:
92 * WARNING: tgX exceeded limit: 11 connected vs 10 max
93 * (goes back down to 10)
94 * WARNING: tgX exceeded limit: 11 connected vs 10 max
97 if(hooknum
== HOOK_TRUSTS_NEWNICK
) {
98 patricia_node_t
*head
, *node
;
103 head
= refnode(iptree
, &ipaddress_canonical
, th
->nodebits
);
104 nodecount
= head
->usercount
;
106 /* Account for borrowed IP addresses. */
107 PATRICIA_WALK(head
, node
) {
108 pnp
= node
->exts
[pnode_ext
];
111 for (i
= 0; i
< PATRICIANICK_HASHSIZE
; i
++)
112 for (npp
= pnp
->identhash
[i
]; npp
; npp
=npp
->exts
[pnick_ext
])
113 if (NickOnServiceServer(npp
))
114 usercountadjustment
--;
118 derefnode(iptree
, head
);
120 if(th
->maxpernode
&& nodecount
+ usercountadjustment
> th
->maxpernode
) {
121 controlwall(NO_OPER
, NL_CLONING
, "Hard connection limit exceeded on subnet: %s (group: %s): %d connected, %d max.", CIDRtostr(*ipaddress
, th
->nodebits
), tg
->name
->content
, nodecount
+ usercountadjustment
, th
->maxpernode
);
122 snprintf(message
, messagelen
, "Too many connections from your host (%s) - see https://www.quakenet.org/help/trusts/connection-limit for details.", IPtostr(*ipaddress
));
123 return POLICY_FAILURE_NODECOUNT
;
126 if(tg
->trustedfor
&& tg
->count
+ usercountadjustment
> tg
->trustedfor
) {
127 if(tg
->count
> (long)tg
->exts
[countext
]) {
128 tg
->exts
[countext
] = (void *)(long)tg
->count
;
130 controlwall(NO_OPER
, NL_CLONING
, "Hard connection limit exceeded (group %s): %d connected, %d max.", tg
->name
->content
, tg
->count
+ usercountadjustment
, tg
->trustedfor
);
131 snprintf(message
, messagelen
, "Too many connections from your trust (%s) - see https://www.quakenet.org/help/trusts/connection-limit for details.", IPtostr(*ipaddress
));
134 snprintf(message
, messagelen
, "Too many connections from your trust (%s) - see https://www.quakenet.org/help/trusts/connection-limit for details.", IPtostr(*ipaddress
));
135 return POLICY_FAILURE_GROUPCOUNT
;
138 if((tg
->flags
& TRUST_ENFORCE_IDENT
) && (username
[0] == '~')) {
139 controlwall(NO_OPER
, NL_CLONING
, "Ident required: %s@%s (group: %s).", username
, IPtostr(*ipaddress
), tg
->name
->content
);
140 snprintf(message
, messagelen
, "IDENTD required from your host (%s) - see https://www.quakenet.org/help/trusts/connection-limit for details.", IPtostr(*ipaddress
));
141 return POLICY_FAILURE_IDENTD
;
144 if(tg
->maxperident
> 0) {
149 for(th2
=tg
->hosts
;th2
;th2
=th2
->next
) {
150 for(tnp
=th2
->users
;tnp
;tnp
=nextbytrust(tnp
)) {
151 if(!ircd_strcmp(tnp
->ident
, username
))
156 if(identcount
+ usercountadjustment
> tg
->maxperident
) {
157 controlwall(NO_OPER
, NL_CLONING
, "Hard ident limit exceeded: %s@%s (group: %s): %d connected, %d max.", username
, IPtostr(*ipaddress
), tg
->name
->content
, identcount
+ usercountadjustment
, tg
->maxperident
);
158 snprintf(message
, messagelen
, "Too many connections from your username (%s@%s) - see https://www.quakenet.org/help/trusts/connection-limit for details.", username
, IPtostr(*ipaddress
));
159 return POLICY_FAILURE_IDENTCOUNT
;
163 if(tg
->count
< tg
->maxusage
)
164 tg
->exts
[countext
] = (void *)(long)tg
->count
;
167 if(tg
->trustedfor
> 0)
168 snprintf(message
, messagelen
, "Trust has %d out of %d allowed connections.", tg
->count
+ usercountadjustment
, tg
->trustedfor
);
170 if(unthrottle
&& (tg
->flags
& TRUST_UNTHROTTLE
))
173 return POLICY_SUCCESS
;
176 static int trustdowrite(trustsocket
*sock
, char *format
, ...) {
181 va_start(va
, format
);
182 r
= vsnprintf(buf
, sizeof(buf
), format
, va
);
190 if(write(sock
->fd
, buf
, r
+ 1) != r
+ 1)
195 static int policycheck_auth(trustsocket
*sock
, const char *sequence_id
, const char *username
, const char *host
) {
197 int verdict
, unthrottle
;
198 struct irc_in_addr ipaddress
;
201 if(!ipmask_parse(host
, &ipaddress
, &bits
)) {
203 return trustdowrite(sock
, "PASS %s", sequence_id
);
206 verdict
= checkconnection(username
, &ipaddress
, HOOK_TRUSTS_NEWNICK
, 1, message
, sizeof(message
), &unthrottle
);
208 if(!enforcepolicy_auth
)
209 verdict
= POLICY_SUCCESS
;
211 if (verdict
== POLICY_SUCCESS
) {
216 trustdowrite(sock
, "UNTHROTTLE %s", sequence_id
);
220 return trustdowrite(sock
, "PASS %s %s", sequence_id
, message
);
222 return trustdowrite(sock
, "PASS %s", sequence_id
);
226 controlwall(NO_OPER
, NL_CLONING
, "Rejected connection from %s@%s using IAuth: %s", username
, host
, message
);
227 return trustdowrite(sock
, "KILL %s %s", sequence_id
, message
);
231 static int trustkillconnection(trustsocket
*sock
, char *reason
) {
232 trustdowrite(sock
, "QUIT %s", reason
);
236 static void trustfreeconnection(trustsocket
*sock
, int unlink
) {
237 trustsocket
**pnext
, *ts
;
240 controlwall(NO_OPER
, NL_TRUSTS
, "Lost connection on policy socket for '%s'.", sock
->authed
?sock
->authuser
:"<unauthenticated connection>");
242 deregisterhandler(sock
->fd
, 1);
243 nsfree(POOL_TRUSTS
, sock
);
247 for(pnext
=&tslist
;*pnext
;pnext
=&((*pnext
)->next
)) {
251 trustfreeconnection(sock
, 0);
257 static int handletrustauth(trustsocket
*sock
, char *server_name
, char *mac
) {
259 char *password
= NULL
;
260 unsigned char digest
[16];
261 char noncehexbuf
[NONCELEN
* 2 + 1];
262 char hexbuf
[sizeof(digest
) * 2 + 1];
263 trustsocket
*ts
, **pnext
;
265 for(i
=0;i
<MAXSERVERS
;i
++) {
266 if(trustaccounts
[i
].used
&& strcmp(trustaccounts
[i
].server
, server_name
) == 0) {
267 password
= trustaccounts
[i
].password
;
273 controlwall(NO_OPER
, NL_TRUSTS
, "Invalid servername for policy socket: '%s'", server_name
);
274 return trustkillconnection(sock
, "Invalid servername.");
278 hmacmd5_init(&h
, (unsigned char *)password
, strlen(password
));
279 hmacmd5_update(&h
, (unsigned char *)hmac_printhex(sock
->nonce
, noncehexbuf
, NONCELEN
), NONCELEN
* 2);
280 hmacmd5_final(&h
, digest
);
281 if(hmac_strcmp(mac
, hmac_printhex(digest
, hexbuf
, sizeof(digest
)))) {
282 controlwall(NO_OPER
, NL_TRUSTS
, "Invalid password for policy socket with servername '%s'.", server_name
);
283 return trustkillconnection(sock
, "Bad MAC.");
286 for(pnext
=&tslist
;*pnext
;pnext
=&((*pnext
)->next
)) {
288 if(ts
->authed
&& strcmp(ts
->authuser
, server_name
) == 0) {
289 trustkillconnection(ts
, "New connection with same server name.");
291 trustfreeconnection(ts
, 0);
297 strncpy(sock
->authuser
, server_name
, SERVERLEN
);
299 controlwall(NO_OPER
, NL_TRUSTS
, "Successful authentication for policy socket with servername '%s'.", server_name
);
300 return trustdowrite(sock
, "AUTHOK");
304 static int handletrustline(trustsocket
*sock
, char *line
) {
305 char *command
, *p
, *lastpos
;
306 char *tokens
[MAXTOKENS
];
307 int tokensfound
= -1;
309 for(command
=lastpos
=p
=line
;*p
;p
++) {
312 if(tokensfound
== MAXTOKENS
)
313 return trustkillconnection(sock
, "too many tokens");
315 if(tokensfound
>= 0) {
316 tokens
[tokensfound
++] = lastpos
;
324 if(tokensfound
== MAXTOKENS
)
325 return trustkillconnection(sock
, "too many tokens");
326 tokens
[tokensfound
++] = lastpos
;
329 if(!sock
->authed
&& !strcmp("AUTH", command
)) {
331 return trustkillconnection(sock
, "incorrect arg count for command.");
333 return handletrustauth(sock
, tokens
[0], tokens
[1]);
334 } else if(sock
->authed
&& !strcmp("CHECK", command
)) {
336 return trustkillconnection(sock
, "incorrect arg count for command.");
338 policycheck_auth(sock
, tokens
[0], tokens
[1], tokens
[2]);
340 } else if(!strcmp("VERSION", command
)) {
341 /* Ignore this command for now. */
344 Error("trusts_policy", ERR_WARNING
, "Bad command: %s", command
);
349 static trustsocket
*findtrustsocketbyfd(int fd
) {
350 for(trustsocket
*ts
=tslist
;ts
;ts
=ts
->next
)
357 static int handletrustclient(trustsocket
*sock
) {
358 int r
, remaining
= TRUSTBUFSIZE
- sock
->size
, i
;
362 trustkillconnection(sock
, "Buffer overflow.");
366 r
= read(sock
->fd
, sock
->buf
+ sock
->size
, remaining
);
373 for(c
=sock
->buf
,i
=0;i
<sock
->size
;i
++,c
++) {
377 if(!handletrustline(sock
, lastpos
))
380 lastpos
= c
+ 1; /* is this ok? */
382 sock
->size
-=lastpos
- sock
->buf
;
383 memmove(sock
->buf
, lastpos
, sock
->size
);
388 static void processtrustclient(int fd
, short events
) {
389 trustsocket
*sock
= findtrustsocketbyfd(fd
);
394 if (events
& (POLLPRI
| POLLERR
| POLLHUP
| POLLNVAL
)) {
395 trustfreeconnection(sock
, 1);
400 if(!handletrustclient(sock
))
401 trustfreeconnection(sock
, 1);
404 static void trustdotimeout(void *arg
) {
405 time_t t
= time(NULL
);
406 trustsocket
**pnext
, *sock
;
408 for(pnext
=&tslist
;*pnext
;pnext
=&((*pnext
)->next
)) {
410 if(!sock
->authed
&& t
>= sock
->timeout
) {
411 trustkillconnection(sock
, "Auth timeout.");
413 trustfreeconnection(sock
, 0);
418 static void processtrustlistener(int fd
, short events
) {
419 if(events
& POLLIN
) {
421 char buf
[NONCELEN
* 2 + 1];
424 int newfd
= accept(fd
, NULL
, NULL
), flags
;
428 flags
= fcntl(newfd
, F_GETFL
, 0);
430 Error("trusts_policy", ERR_WARNING
, "Unable to set socket non-blocking.");
435 if(fcntl(fd
, F_SETFL
, flags
|O_NONBLOCK
) < 0) {
436 Error("trusts_policy", ERR_WARNING
, "Unable to set socket non-blocking.");
442 setsockopt(newfd
, SOL_SOCKET
, SO_KEEPALIVE
, &optval
, sizeof(optval
));
444 setsockopt(newfd
, IPPROTO_TCP
, TCP_KEEPIDLE
, &optval
, sizeof(optval
));
446 setsockopt(newfd
, IPPROTO_TCP
, TCP_KEEPCNT
, &optval
, sizeof(optval
));
448 setsockopt(newfd
, IPPROTO_TCP
, TCP_KEEPINTVL
, &optval
, sizeof(optval
));
450 registerhandler(newfd
, POLLIN
|POLLERR
|POLLHUP
, processtrustclient
);
452 sock
= nsmalloc(POOL_TRUSTS
, sizeof(trustsocket
));
454 deregisterhandler(newfd
, 1);
462 if(fread((char *)sock
->nonce
, 1, NONCELEN
, urandom
) != NONCELEN
) {
463 Error("trusts_policy", ERR_WARNING
, "Error getting random bytes.");
464 deregisterhandler(newfd
, 1);
466 nsfree(POOL_TRUSTS
, sock
);
470 sock
->connected
= time(NULL
);
471 sock
->timeout
= time(NULL
) + 30;
474 sock
->unthrottled
= 0;
475 if(!trustdowrite(sock
, "AUTH %s", hmac_printhex(sock
->nonce
, buf
, NONCELEN
))) {
476 Error("trusts_policy", ERR_WARNING
, "Error writing auth to fd %d.", newfd
);
477 deregisterhandler(newfd
, 1);
479 nsfree(POOL_TRUSTS
, sock
);
486 static int createlistenersock(int port
) {
487 struct sockaddr_in s
;
491 memset(&s
, 0, sizeof(struct sockaddr_in
));
492 s
.sin_family
= AF_INET
;
493 s
.sin_addr
.s_addr
= INADDR_ANY
;
494 s
.sin_port
= htons(port
);
496 fd
= socket(PF_INET
, SOCK_STREAM
, 0);
498 Error("trusts_policy", ERR_WARNING
, "Unable to get socket for trustfd.");
503 setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
, &optval
, sizeof(optval
));
505 if(bind(fd
, (struct sockaddr
*)&s
, sizeof(struct sockaddr_in
)) < 0) {
506 Error("trusts_policy", ERR_WARNING
, "Unable to bind trustfd.");
511 if(listen(fd
, 5) < 0) {
512 Error("trusts_policy", ERR_WARNING
, "Unable to listen on trustfd.");
517 registerhandler(fd
, POLLIN
, processtrustlistener
);
522 static void policycheck_irc(int hooknum
, void *arg
) {
525 long moving
= (long)args
[1];
527 int verdict
, unthrottle
;
532 verdict
= checkconnection(np
->ident
, &np
->ipaddress
, hooknum
, 0, message
, sizeof(message
), &unthrottle
);
534 if(!enforcepolicy_irc
)
535 verdict
= POLICY_SUCCESS
;
538 case POLICY_FAILURE_NODECOUNT
:
539 glinebynick(np
, POLICY_GLINE_DURATION
, message
, GLINE_IGNORE_TRUST
, "trusts_policy");
541 case POLICY_FAILURE_IDENTD
:
542 glinebyip("~*", &np
->ipaddress
, 128, POLICY_GLINE_DURATION
, message
, GLINE_ALWAYS_USER
|GLINE_IGNORE_TRUST
, "trusts_policy");
544 case POLICY_FAILURE_IDENTCOUNT
:
545 glinebynick(np
, POLICY_GLINE_DURATION
, message
, GLINE_ALWAYS_USER
|GLINE_IGNORE_TRUST
, "trusts_policy");
551 static int trusts_cmdtrustpolicyirc(void *source
, int cargc
, char **cargv
) {
552 nick
*sender
= source
;
555 controlreply(sender
, "Use of glines for trust policy enforcement is currently %s.", enforcepolicy_irc
?"enabled":"disabled");
559 enforcepolicy_irc
= atoi(cargv
[0]);
560 controlwall(NO_OPER
, NL_TRUSTS
, "%s %s use of glines for trust policy enforcement.", controlid(sender
), enforcepolicy_irc
?"enabled":"disabled");
561 controlreply(sender
, "Use of glines for trust policy enforcement is now %s.", enforcepolicy_irc
?"enabled":"disabled");
566 static int trusts_cmdtrustpolicyauth(void *source
, int cargc
, char **cargv
) {
567 nick
*sender
= source
;
570 controlreply(sender
, "Trust policy enforcement with IAuth is currently %s.", enforcepolicy_auth
?"enabled":"disabled");
574 enforcepolicy_auth
= atoi(cargv
[0]);
575 controlwall(NO_OPER
, NL_TRUSTS
, "%s %s trust policy enforcement with IAuth.", controlid(sender
), enforcepolicy_auth
?"enabled":"disabled");
576 controlreply(sender
, "Trust policy enforcement with IAuth is now %s.", enforcepolicy_auth
?"enabled":"disabled");
582 static int trusts_cmdtrustsockets(void *source
, int cargc
, char **cargv
) {
583 nick
*sender
= source
;
589 controlreply(sender
, "Server Connected for Accepted Rejected Unthrottled");
591 for(sock
=tslist
;sock
;sock
=sock
->next
)
592 controlreply(sender
, "%-35s %-20s %-15d %-15d %-15d", sock
->authed
?sock
->authuser
:"<unauthenticated connection>", longtoduration(now
- sock
->connected
, 0), sock
->accepted
, sock
->rejected
, sock
->unthrottled
);
594 controlreply(sender
, "-- End of list.");
598 void loadtrustaccounts(void) {
601 memset(trustaccounts
, 0, sizeof(trustaccounts
));
603 accts
= getconfigitems("trusts_policy", "server");
605 Error("trusts_policy", ERR_INFO
, "No servers added.");
607 sstring
**servers
= (sstring
**)(accts
->content
);
609 for(i
=0;i
<accts
->cursi
;i
++) {
614 Error("trusts_policy", ERR_INFO
, "Too many servers specified.");
618 strncpy(server
, servers
[i
]->content
, sizeof(server
));
620 pos
= strchr(server
, ',');
623 Error("trusts_policy", ERR_INFO
, "Server line is missing password: %s", server
);
629 trustaccounts
[i
].used
= 1;
630 strncpy(trustaccounts
[i
].server
, server
, SERVERLEN
);
631 strncpy(trustaccounts
[i
].password
, pos
+1, TRUSTPASSLEN
);
636 static void trustaccounts_rehash(int hooknum
, void *arg
) {
644 countext
= registertgext("count");
648 m
= getconfigitem("trusts_policy", "enforcepolicy_irc");
650 enforcepolicy_irc
= atoi(m
->content
);
652 m
= getconfigitem("trusts_policy", "enforcepolicy_auth");
654 enforcepolicy_auth
= atoi(m
->content
);
656 m
= getconfigitem("trusts_policy", "trustport");
658 trustport
= atoi(m
->content
);
660 trustport
= DEFAULT_TRUSTPORT
;
663 listenerfd
= createlistenersock(trustport
);
667 registerhook(HOOK_TRUSTS_NEWNICK
, policycheck_irc
);
668 registerhook(HOOK_TRUSTS_LOSTNICK
, &policycheck_irc
);
669 registerhook(HOOK_CORE_REHASH
, trustaccounts_rehash
);
671 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.");
672 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.");
673 registercontrolhelpcmd("trustsockets", NO_DEVELOPER
, 0, trusts_cmdtrustsockets
, "Usage: trustsockets\nLists all currently active TRUST sockets.");
675 schedulerecurring(time(NULL
)+1, 0, 5, trustdotimeout
, NULL
);
677 urandom
= fopen("/dev/urandom", "rb");
679 Error("trusts_policy", ERR_ERROR
, "Couldn't open /dev/urandom.");
683 trustsocket
*sock
, *next
;
688 releasetgext(countext
);
690 deregisterhook(HOOK_TRUSTS_NEWNICK
, policycheck_irc
);
691 deregisterhook(HOOK_TRUSTS_LOSTNICK
, policycheck_irc
);
692 deregisterhook(HOOK_CORE_REHASH
, trustaccounts_rehash
);
694 deregistercontrolcmd("trustpolicyirc", trusts_cmdtrustpolicyirc
);
695 deregistercontrolcmd("trustpolicyauth", trusts_cmdtrustpolicyauth
);
696 deregistercontrolcmd("trustsockets", trusts_cmdtrustsockets
);
698 deleteallschedules(trustdotimeout
);
703 if (listenerfd
!= -1)
704 deregisterhandler(listenerfd
, 1);
706 for(sock
=tslist
;sock
;) {
709 trustkillconnection(sock
, "Unloading module.");
710 trustfreeconnection(sock
, 0);