]> jfr.im git - irc/quakenet/newserv.git/blame - trusts/trusts_management.c
GLINES: fix null pointer deref in trustgline / trustungline
[irc/quakenet/newserv.git] / trusts / trusts_management.c
CommitLineData
35449aa5
CP
1#include <stdio.h>
2#include <string.h>
3#include "../control/control.h"
c4610da5 4#include "../lib/version.h"
35449aa5
CP
5#include "../lib/irc_string.h"
6#include "../lib/strlfunc.h"
82a316e7 7#include "../core/config.h"
caf2d02a 8#include "../core/schedule.h"
acd5f58f 9#include "../irc/irc.h"
2ab0a1e7 10#include "../lib/stringbuf.h"
4d64f9c6
GB
11#include "../control/control.h"
12#include "../control/control_policy.h"
35449aa5
CP
13#include "trusts.h"
14
c4610da5
GB
15MODULE_VERSION("");
16
35449aa5
CP
17static void registercommands(int, void *);
18static void deregistercommands(int, void *);
19
e40626f0 20typedef int (*trustmodificationfn)(void *, char *arg, nick *, int);
2ab0a1e7
CP
21
22struct trustmodification {
1d9ccd69 23 char name[50];
2ab0a1e7
CP
24 trustmodificationfn fn;
25};
26
35449aa5
CP
27static int trusts_cmdtrustadd(void *source, int cargc, char **cargv) {
28 trustgroup *tg;
29 nick *sender = source;
30 char *host;
6e6e98da
GB
31 struct irc_in_addr ip;
32 unsigned char bits;
35449aa5
CP
33 trusthost *th, *superset, *subset;
34
35 if(cargc < 2)
36 return CMD_USAGE;
37
38 tg = tg_strtotg(cargv[0]);
39 if(!tg) {
40 controlreply(sender, "Couldn't look up trustgroup.");
41 return CMD_ERROR;
42 }
43
44 host = cargv[1];
6e6e98da 45 if(!ipmask_parse(host, &ip, &bits)) {
35449aa5
CP
46 controlreply(sender, "Invalid host.");
47 return CMD_ERROR;
48 }
49
3821b43e
GB
50 if(!is_normalized_ipmask(&ip, bits)) {
51 controlreply(sender, "Invalid IP Mask.");
52 return CMD_ERROR;
53 }
54
e40626f0
GB
55 /* Don't allow non-developers to add trusts for large subnets or modify protected groups. */
56 if (!noperserv_policy_command_permitted(NO_DEVELOPER, sender)) {
57 int minbits = irc_in_addr_is_ipv4(&ip)?TRUST_MIN_UNPRIVILEGED_BITS_IPV4:TRUST_MIN_UNPRIVILEGED_BITS_IPV6;
58 if(bits < minbits) {
d6ff6878 59 controlreply(sender, "You don't have the necessary privileges to add a subnet larger than /%d.", irc_bitlen(&ip, minbits));
e40626f0
GB
60 return CMD_ERROR;
61 }
62
63 if(tg->flags & TRUST_PROTECTED) {
64 controlreply(sender, "You don't have the necessary privileges to modify a protected trust group.");
65 return CMD_ERROR;
66 }
67 }
68
35449aa5
CP
69 /* OKAY! Lots of checking here!
70 *
71 * Need to check:
10b21e1f 72 * - exact same host isn't already covered by given group (reject if it is)
35449aa5
CP
73 * - host doesn't already exist exactly already (reject if it does)
74 * - host is more specific than an existing one (warn if it is, fix up later)
75 * - host is less specific than an existing one (warn if it is, don't need to do anything special)
76 */
77
78 for(th=tg->hosts;th;th=th->next) {
10b21e1f 79 if(ipmask_check(&ip, &th->ip, th->bits) && th->bits == bits) {
35449aa5
CP
80 controlreply(sender, "This host (or part of it) is already covered in the given group.");
81 return CMD_ERROR;
82 }
83 }
84
6e6e98da 85 if(th_getbyhostandmask(&ip, bits)) {
35449aa5
CP
86 controlreply(sender, "This host already exists in another group with the same mask.");
87 return CMD_ERROR;
88 }
89
90 /* this function will set both to NULL if it's equal, hence the check above */
6e6e98da 91 th_getsuperandsubsets(&ip, bits, &superset, &subset);
35449aa5
CP
92 if(superset) {
93 /* a superset exists for us, we will be more specific than one existing host */
94
f6944d47 95 controlreply(sender, "Note: This host already exists in another group, but this new host will override it as it has a smaller prefix.");
35449aa5
CP
96 }
97 if(subset) {
98 /* a subset of us exists, we will be less specific than some existing hosts */
99
f6944d47 100 controlreply(sender, "Note: This host already exists in at least one other group, the new host has a larger prefix and therefore will not override those hosts.");
35449aa5 101 }
35449aa5
CP
102
103 th = th_new(tg, host);
104 if(!th) {
105 controlreply(sender, "An error occured adding the host to the group.");
106 return CMD_ERROR;
107 }
108
109 controlreply(sender, "Host added.");
82a316e7
CP
110 triggerhook(HOOK_TRUSTS_ADDHOST, th);
111
7e11a2c6 112 controlwall(NO_OPER, NL_TRUSTS, "%s TRUSTADD'ed host %s to group '%s'", controlid(sender), host, th->group->name->content);
7db61652 113 trustlog(tg, sender->authname, "Added host '%s'.", host);
35449aa5
CP
114
115 return CMD_OK;
116}
117
118static int trusts_cmdtrustgroupadd(void *source, int cargc, char **cargv) {
119 nick *sender = source;
120 char *name, *contact, *comment, createdby[ACCOUNTLEN + 2];
6e1fa89e 121 long howmany, maxperident, enforceident;
82a316e7 122 trustgroup *tg, itg;
3a8c35c9 123 int override, flags;
35449aa5 124
3b09e86f 125 if(cargc < 5)
35449aa5
CP
126 return CMD_USAGE;
127
1c732bec
GB
128 override = noperserv_policy_command_permitted(NO_DEVELOPER, sender);
129
35449aa5 130 name = cargv[0];
6e1fa89e 131 howmany = strtol(cargv[1], NULL, 10);
1c732bec 132 if(!override && (!howmany || (howmany > MAXTRUSTEDFOR))) {
35449aa5
CP
133 controlreply(sender, "Bad value maximum number of clients.");
134 return CMD_ERROR;
135 }
136
6e1fa89e 137 maxperident = strtol(cargv[2], NULL, 10);
1c732bec 138 if(maxperident < 0 || (maxperident > MAXPERIDENT)) {
35449aa5
CP
139 controlreply(sender, "Bad value for max per ident.");
140 return CMD_ERROR;
141 }
142
06d01a20 143 if(cargv[3][0] != '1' && cargv[3][0] != '0') {
35449aa5
CP
144 controlreply(sender, "Bad value for enforce ident (use 0 or 1).");
145 return CMD_ERROR;
146 }
a350e502 147 enforceident = cargv[3][0] == '1';
35449aa5 148
a350e502 149 contact = cargv[4];
35449aa5 150
a350e502 151 if(cargc < 6) {
35449aa5
CP
152 comment = "(no comment)";
153 } else {
a350e502 154 comment = cargv[5];
35449aa5
CP
155 }
156
157 /* don't allow #id or id forms */
6e1fa89e 158 if((name[0] == '#') || strtol(name, NULL, 10)) {
35449aa5
CP
159 controlreply(sender, "Invalid trustgroup name.");
160 return CMD_ERROR;
161 }
162
163 tg = tg_strtotg(name);
164 if(tg) {
e40626f0 165 controlreply(sender, "A group with that name already exists.");
35449aa5
CP
166 return CMD_ERROR;
167 }
168
169 snprintf(createdby, sizeof(createdby), "#%s", sender->authname);
170
3a8c35c9
GB
171 flags = 0;
172
173 if(maxperident > 0)
174 flags |= TRUST_RELIABLE_USERNAME;
175
176 if(enforceident)
177 flags |= TRUST_ENFORCE_IDENT;
178
82a316e7 179 itg.trustedfor = howmany;
3a8c35c9 180 itg.flags = flags;
82a316e7 181 itg.maxperident = maxperident;
3b09e86f 182 itg.expires = 0;
1f685425 183 itg.createdby = getsstring(createdby, CREATEDBYLEN);
82a316e7
CP
184 itg.contact = getsstring(contact, CONTACTLEN);
185 itg.comment = getsstring(comment, COMMENTLEN);
186 itg.name = getsstring(name, TRUSTNAMELEN);
187
188 if(itg.createdby && itg.contact && itg.comment && itg.name) {
189 tg = tg_new(&itg);
190 } else {
191 tg = NULL;
192 }
193
194 freesstring(itg.createdby);
195 freesstring(itg.comment);
196 freesstring(itg.name);
197 freesstring(itg.contact);
198
35449aa5
CP
199 if(!tg) {
200 controlreply(sender, "An error occured adding the trustgroup.");
201 return CMD_ERROR;
202 }
203
204 controlreply(sender, "Group added.");
82a316e7
CP
205 triggerhook(HOOK_TRUSTS_ADDGROUP, tg);
206
7e11a2c6 207 controlwall(NO_OPER, NL_TRUSTS, "%s TRUSTGROUPADD'ed '%s'", controlid(sender), tg->name->content);
7db61652 208 trustlog(tg, sender->authname, "Created trust group '%s' (ID #%d): howmany=%d, enforceident=%d, maxperident=%d, "
3b09e86f 209 "createdby=%s, contact=%s, comment=%s",
6423769c 210 tg->name->content, tg->id, howmany, enforceident, maxperident, createdby, contact, comment);
35449aa5
CP
211
212 return CMD_OK;
213}
214
2ab0a1e7
CP
215static int trusts_cmdtrustgroupdel(void *source, int cargc, char **cargv) {
216 trustgroup *tg;
217 nick *sender = source;
218
219 if(cargc < 1)
220 return CMD_USAGE;
221
222 tg = tg_strtotg(cargv[0]);
223 if(!tg) {
224 controlreply(sender, "Couldn't look up trustgroup.");
225 return CMD_ERROR;
226 }
227
e40626f0
GB
228 /* Don't allow non-developers to delete protected groups. */
229 if (!noperserv_policy_command_permitted(NO_DEVELOPER, sender)) {
230 if(tg->flags & TRUST_PROTECTED) {
231 controlreply(sender, "You don't have the necessary privileges to modify a protected trust group.");
232 return CMD_ERROR;
233 }
234 }
235
2ab0a1e7
CP
236 if(tg->hosts) {
237 controlreply(sender, "Delete all hosts before deleting the group.");
238 return CMD_ERROR;
239 }
240
7e11a2c6 241 controlwall(NO_OPER, NL_TRUSTS, "%s TRUSTGROUPDEL'ed '%s'.", controlid(sender), tg->name->content);
7db61652 242 trustlog(tg, sender->authname, "Deleted group '%s'.", tg->name->content);
7e11a2c6 243
2ab0a1e7
CP
244 triggerhook(HOOK_TRUSTS_DELGROUP, tg);
245 tg_delete(tg);
246 controlreply(sender, "Group deleted.");
247
2ab0a1e7
CP
248 return CMD_OK;
249}
250
251static int trusts_cmdtrustdel(void *source, int cargc, char **cargv) {
252 trustgroup *tg;
253 trusthost *th;
6e6e98da
GB
254 struct irc_in_addr ip;
255 unsigned char bits;
2ab0a1e7 256 nick *sender = source;
7e11a2c6 257 char *host;
2ab0a1e7
CP
258
259 if(cargc < 2)
260 return CMD_USAGE;
261
262 tg = tg_strtotg(cargv[0]);
263 if(!tg) {
264 controlreply(sender, "Couldn't look up trustgroup.");
265 return CMD_ERROR;
266 }
267
7e11a2c6 268 host = cargv[1];
6e6e98da 269 if(!ipmask_parse(host, &ip, &bits)) {
2ab0a1e7
CP
270 controlreply(sender, "Invalid IP/mask.");
271 return CMD_ERROR;
272 }
273
e40626f0
GB
274 /* Don't allow non-developers to remove trusts for large subnets or modify protected groups. */
275 if (!noperserv_policy_command_permitted(NO_DEVELOPER, sender)) {
276 int minbits = irc_in_addr_is_ipv4(&ip)?TRUST_MIN_UNPRIVILEGED_BITS_IPV4:TRUST_MIN_UNPRIVILEGED_BITS_IPV6;
277 if(bits < minbits) {
d6ff6878 278 controlreply(sender, "You don't have the necessary privileges to remove a subnet larger than /%d.", irc_bitlen(&ip, minbits));
e40626f0
GB
279 return CMD_ERROR;
280 }
281
282 if(tg->flags & TRUST_PROTECTED) {
283 controlreply(sender, "You don't have the necessary privileges to modify a protected trust group.");
284 return CMD_ERROR;
285 }
286 }
287
2ab0a1e7 288 for(th=tg->hosts;th;th=th->next)
6e6e98da 289 if(ipmask_check(&ip, &th->ip, th->bits) && th->bits == bits)
2ab0a1e7
CP
290 break;
291
292 if(!th) {
293 controlreply(sender, "Couldn't find that host in that group.");
294 return CMD_ERROR;
295 }
296
297 triggerhook(HOOK_TRUSTS_DELHOST, th);
298 th_delete(th);
299 controlreply(sender, "Host deleted.");
300
7e11a2c6 301 controlwall(NO_OPER, NL_TRUSTS, "%s TRUSTDEL'ed %s from group '%s'.", controlid(sender), host, tg->name->content);
7db61652 302 trustlog(tg, sender->authname, "Removed host '%s'.", host);
2ab0a1e7 303
7e11a2c6 304 return CMD_OK;
2ab0a1e7
CP
305}
306
e40626f0 307static int modifycomment(void *arg, char *comment, nick *source, int override) {
c1da06f9 308 trustgroup *tg = arg;
2ab0a1e7
CP
309 sstring *n = getsstring(comment, COMMENTLEN);
310 if(!n)
311 return 0;
312
313 freesstring(tg->comment);
314 tg->comment = n;
315
316 return 1;
317}
318
e40626f0 319static int modifycontact(void *arg, char *contact, nick *source, int override) {
c1da06f9 320 trustgroup *tg = arg;
2ab0a1e7
CP
321 sstring *n = getsstring(contact, CONTACTLEN);
322 if(!n)
323 return 0;
324
325 freesstring(tg->contact);
326 tg->contact = n;
327
328 return 1;
329}
330
e40626f0 331static int modifytrustedfor(void *arg, char *num, nick *source, int override) {
c1da06f9 332 trustgroup *tg = arg;
6e1fa89e 333 long trustedfor = strtol(num, NULL, 10);
2ab0a1e7 334
e40626f0
GB
335 if(trustedfor < 0) {
336 controlreply(source, "The clone limit must not be negative.");
2ab0a1e7 337 return 0;
e40626f0
GB
338 }
339
340 if(!override) {
341 if (trustedfor == 0) {
342 controlreply(source, "You don't have the necessary privileges to set an unlimited clone limit.");
343 return 0;
344 }
345
346 if (trustedfor > MAXTRUSTEDFOR) {
347 controlreply(source, "You don't have the necessary privileges to set the clone limit to a value higher than %d.", MAXTRUSTEDFOR);
348 return 0;
349 }
350 }
2ab0a1e7
CP
351
352 tg->trustedfor = trustedfor;
353
354 return 1;
355}
356
e40626f0 357static int modifymaxperident(void *arg, char *num, nick *source, int override) {
c1da06f9 358 trustgroup *tg = arg;
6e1fa89e 359 long maxperident = strtol(num, NULL, 10);
2ab0a1e7 360
e40626f0
GB
361 if(maxperident < 0) {
362 controlreply(source, "Ident limit must not be negative.");
2ab0a1e7 363 return 0;
e40626f0
GB
364 }
365
366 if(maxperident > MAXPERIDENT) {
367 controlreply(source, "Ident limit must not be higher than %d. Consider setting it to 0 (unlimited) instead.", MAXPERIDENT);
368 return 0;
369 }
2ab0a1e7 370
1f685425 371 tg->maxperident = maxperident;
2ab0a1e7
CP
372
373 return 1;
374}
375
e40626f0 376static int modifyenforceident(void *arg, char *num, nick *source, int override) {
c1da06f9
GB
377 trustgroup *tg = arg;
378
2ab0a1e7 379 if(num[0] == '1') {
de723023 380 tg->flags |= TRUST_ENFORCE_IDENT;
2ab0a1e7 381 } else if(num[0] == '0') {
de723023 382 tg->flags &= ~TRUST_ENFORCE_IDENT;
2ab0a1e7
CP
383 } else {
384 return 0;
385 }
386
387 return 1;
388}
389
6ba5f655
GB
390static int modifyreliableusername(void *arg, char *num, nick *source, int override) {
391 trustgroup *tg = arg;
392
393 if(num[0] == '1') {
394 tg->flags |= TRUST_RELIABLE_USERNAME;
395 } else if(num[0] == '0') {
396 tg->flags &= ~TRUST_RELIABLE_USERNAME;
397 } else {
398 return 0;
399 }
400
401 return 1;
402}
403
4b40d278
GB
404static int modifyunthrottle(void *arg, char *num, nick *source, int override) {
405 trustgroup *tg = arg;
406
407 if(num[0] == '1') {
408 tg->flags |= TRUST_UNTHROTTLE;
409 } else if(num[0] == '0') {
410 tg->flags &= ~TRUST_UNTHROTTLE;
411 } else {
412 return 0;
413 }
414
415 return 1;
416}
417
e40626f0 418static int modifyexpires(void *arg, char *expires, nick *source, int override) {
c1da06f9 419 trustgroup *tg = arg;
2ab0a1e7
CP
420 int howlong = durationtolong(expires);
421
1a760647
GB
422 if((howlong < 0) || (howlong > MAXDURATION)) {
423 controlreply(source, "Duration cannot be negative or greater than %s (use 0 instead if you don't want the group to expire).", longtoduration(MAXDURATION, 0));
2ab0a1e7 424 return 0;
1a760647 425 }
2ab0a1e7 426
ad3fd5ee 427 if(howlong)
acd5f58f 428 tg->expires = getnettime() + howlong;
ad3fd5ee
GB
429 else
430 tg->expires = 0; /* never */
2ab0a1e7
CP
431
432 return 1;
433}
434
e40626f0 435static int modifycleanup(void *arg, char *num, nick *source, int override) {
de723023
GB
436 trustgroup *tg = arg;
437
b657ac19
GB
438 if(!override) {
439 controlreply(source, "You don't have the necessary privileges to modify this attribute.");
2ab0a1e7 440 return 0;
b657ac19 441 }
2ab0a1e7 442
de723023
GB
443 if(num[0] == '1') {
444 tg->flags &= ~TRUST_NO_CLEANUP;
445 } else if(num[0] == '0') {
446 tg->flags |= TRUST_NO_CLEANUP;
447 } else {
448 return 0;
449 }
2ab0a1e7
CP
450
451 return 1;
452}
453
e40626f0
GB
454static int modifyprotected(void *arg, char *num, nick *source, int override) {
455 trustgroup *tg = arg;
456
457 if(!override) {
458 controlreply(source, "You don't have the necessary privileges to modify this attribute.");
459 return 0;
460 }
461
462 if(num[0] == '1') {
463 tg->flags |= TRUST_PROTECTED;
464 } else if(num[0] == '0') {
465 tg->flags &= ~TRUST_PROTECTED;
466 } else {
467 return 0;
468 }
469
470 return 1;
471}
472
473static int modifymaxpernode(void *arg, char *num, nick *source, int override) {
c1da06f9
GB
474 trusthost *th = arg;
475 int maxpernode = strtol(num, NULL, 10);
e40626f0
GB
476
477 if(maxpernode < 0) {
478 controlreply(source, "Node limit must not be negative.");
479 return 0;
480 }
481
482 if(maxpernode>MAXPERNODE) {
483 controlreply(source, "Node limit must not be higher than %d. Consider setting it to 0 (unlimited) instead.", MAXPERNODE);
c1da06f9 484 return 0;
e40626f0
GB
485 }
486
c1da06f9
GB
487 th->maxpernode = maxpernode;
488
489 return 1;
490}
491
e40626f0 492static int modifynodebits(void *arg, char *num, nick *source, int override) {
c1da06f9
GB
493 trusthost *th = arg;
494 int nodebits = strtol(num, NULL, 10);
495
e40626f0
GB
496 if(nodebits < 0) {
497 controlreply(source, "Node bits must not be negative.");
c1da06f9 498 return 0;
e40626f0
GB
499 }
500
1a760647
GB
501 if(irc_in_addr_is_ipv4(&th->ip))
502 nodebits += 96;
503
c2e27ec8
GB
504 if(nodebits > 128) {
505 controlreply(source, "Node bits is invalid.");
506 return 0;
507 }
508
e40626f0
GB
509 if(!override) {
510 int minbits = irc_in_addr_is_ipv4(&th->ip)?TRUST_MIN_UNPRIVILEGED_NODEBITS_IPV4:TRUST_MIN_UNPRIVILEGED_NODEBITS_IPV6;
511
512 if(nodebits < minbits) {
d6ff6878 513 controlreply(source, "You don't have the necessary privileges to set node bits to a subnet larger than /%d.", irc_bitlen(&th->ip, minbits));
e40626f0
GB
514 return 0;
515 }
516 }
c1da06f9 517
e40626f0 518 if(nodebits<th->bits) {
1a760647 519 controlreply(source, "Node bits must be smaller or equal to the trusted CIDR's subnet size.");
80cf3d8e 520 return 0;
e40626f0 521 }
80cf3d8e 522
c1da06f9
GB
523 th->nodebits = nodebits;
524
525 return 1;
526}
527
528static array trustgroupmods_a;
529static struct trustmodification *trustgroupmods;
530static array trusthostmods_a;
531static struct trustmodification *trusthostmods;
2ab0a1e7
CP
532
533static int trusts_cmdtrustgroupmodify(void *source, int cargc, char **cargv) {
534 trustgroup *tg;
535 nick *sender = source;
f6ecfee9 536 char *what, *to;
4a5ce902 537 int i, override;
2ab0a1e7
CP
538
539 if(cargc < 3)
540 return CMD_USAGE;
541
542 tg = tg_strtotg(cargv[0]);
543 if(!tg) {
544 controlreply(sender, "Couldn't look up trustgroup.");
545 return CMD_ERROR;
546 }
547
548 what = cargv[1];
549 to = cargv[2];
550
4a5ce902
GB
551 override = noperserv_policy_command_permitted(NO_DEVELOPER, sender);
552
e40626f0
GB
553 /* Don't allow non-developers to modify protected groups. */
554 if (!override && tg->flags & TRUST_PROTECTED) {
555 controlreply(sender, "You don't have the necessary privileges to modify a protected trust group.");
556 return CMD_ERROR;
557 }
558
c1da06f9
GB
559 for(i=0;i<trustgroupmods_a.cursi;i++) {
560 if(!strcmp(what, trustgroupmods[i].name)) {
e40626f0 561 if(!(trustgroupmods[i].fn)(tg, to, sender, override)) {
2ab0a1e7
CP
562 controlreply(sender, "An error occured changing that property, check the syntax.");
563 return CMD_ERROR;
564 }
565 break;
566 }
2ab0a1e7
CP
567 }
568
f6ecfee9
GB
569 if(i == trustgroupmods_a.cursi)
570 return CMD_USAGE;
2ab0a1e7
CP
571
572 triggerhook(HOOK_TRUSTS_MODIFYGROUP, tg);
573 tg_update(tg);
574 controlreply(sender, "Group modified.");
575
7e11a2c6 576 controlwall(NO_OPER, NL_TRUSTS, "%s TRUSTMODIFIED'ed group '%s' (field: %s, value: %s)", controlid(sender), tg->name->content, what, to);
7db61652 577 trustlog(tg, sender->authname, "Modified %s: %s", what, to);
7e11a2c6 578
2ab0a1e7
CP
579 return CMD_OK;
580}
581
c1da06f9
GB
582static int trusts_cmdtrusthostmodify(void *source, int cargc, char **cargv) {
583 trustgroup *tg;
584 trusthost *th;
585 nick *sender = source;
f6ecfee9 586 char *what, *to;
4a5ce902 587 int i, override;
c1da06f9
GB
588 struct irc_in_addr ip;
589 unsigned char bits;
590
591 if(cargc < 4)
592 return CMD_USAGE;
593
594 tg = tg_strtotg(cargv[0]);
595 if(!tg) {
596 controlreply(sender, "Couldn't look up trustgroup.");
597 return CMD_ERROR;
598 }
599
600 if(!ipmask_parse(cargv[1], &ip, &bits)) {
601 controlreply(sender, "Invalid host.");
602 return CMD_ERROR;
603 }
604
e40626f0
GB
605 /* Don't allow non-developers to modify trusts for large subnets or modify protected groups. */
606 if (!noperserv_policy_command_permitted(NO_DEVELOPER, sender)) {
607 int minbits = irc_in_addr_is_ipv4(&ip)?TRUST_MIN_UNPRIVILEGED_BITS_IPV4:TRUST_MIN_UNPRIVILEGED_BITS_IPV6;
608 if(bits < minbits) {
d6ff6878 609 controlreply(sender, "You don't have the necessary privileges to modify a subnet larger than /%d.", irc_bitlen(&ip, minbits));
e40626f0
GB
610 return CMD_ERROR;
611 }
612
613 if(tg->flags & TRUST_PROTECTED) {
614 controlreply(sender, "You don't have the necessary privileges to modify a protected trust group.");
615 return CMD_ERROR;
616 }
617 }
618
c1da06f9
GB
619 th = th_getbyhostandmask(&ip, bits);
620
4c585540 621 if(!th || th->group != tg) {
c1da06f9
GB
622 controlreply(sender, "Host does not belong to the specified group.");
623 return CMD_ERROR;
624 }
625
626 what = cargv[2];
627 to = cargv[3];
628
4a5ce902
GB
629 override = noperserv_policy_command_permitted(NO_DEVELOPER, sender);
630
c1da06f9
GB
631 for(i=0;i<trusthostmods_a.cursi;i++) {
632 if(!strcmp(what, trusthostmods[i].name)) {
e40626f0 633 if(!(trusthostmods[i].fn)(th, to, sender, override)) {
c1da06f9
GB
634 controlreply(sender, "An error occured changing that property, check the syntax.");
635 return CMD_ERROR;
636 }
637 break;
638 }
c1da06f9
GB
639 }
640
f6ecfee9
GB
641 if(i == trusthostmods_a.cursi)
642 return CMD_USAGE;
c1da06f9
GB
643
644 triggerhook(HOOK_TRUSTS_MODIFYHOST, th);
645 th_update(th);
646 controlreply(sender, "Host modified.");
647
3898f973
GB
648 controlwall(NO_OPER, NL_TRUSTS, "%s TRUSTMODIFIED'ed host '%s' in group '%s' (field: %s, value: %s)", controlid(sender), CIDRtostr(ip, bits), tg->name->content, what, to);
649 trustlog(tg, sender->authname, "Modified %s for host '%s': %s", what, CIDRtostr(ip, bits), to);
7e11a2c6 650
2ab0a1e7
CP
651 return CMD_OK;
652}
653
e5c0bccf 654static int trusts_cmdtrustlog(void *source, int cargc, char **cargv) {
01bd21d3 655 nick *sender = source;
01bd21d3
GB
656 char *name;
657 int groupid;
6e1fa89e 658 long limit = 0;
01bd21d3
GB
659
660 if(cargc < 1)
661 return CMD_USAGE;
662
01bd21d3 663 if(cargc>1)
6e1fa89e 664 limit = strtol(cargv[1], NULL, 10);
01bd21d3
GB
665
666 if(limit==0)
667 limit = 100;
668
1467e9a4
GB
669 name = cargv[0];
670
671 if (name[0] == '#') {
6e1fa89e 672 groupid = strtol(name + 1, NULL, 10);
1467e9a4
GB
673 trustlogspewid(sender, groupid, limit);
674 } else {
675 trustlogspewname(sender, name, limit);
676 }
01bd21d3
GB
677
678 return CMD_OK;
679}
680
681static int trusts_cmdtrustloggrep(void *source, int cargc, char **cargv) {
682 nick *sender = source;
683 char *pattern;
6e1fa89e 684 long limit = 0;
01bd21d3
GB
685
686 if(cargc < 1)
687 return CMD_USAGE;
688
689 pattern = cargv[0];
690
691 if(cargc>1)
6e1fa89e 692 limit = strtol(cargv[1], NULL, 10);
01bd21d3
GB
693
694 if(limit==0)
695 limit = 100;
696
697 trustloggrep(sender, pattern, limit);
698
699 return CMD_OK;
700}
701
702static int trusts_cmdtrustcomment(void *source, int cargc, char **cargv) {
703 nick *sender = source;
704 trustgroup *tg = NULL;
705 char *name, *comment;
706
707 if(cargc < 2)
708 return CMD_USAGE;
709
710 name = cargv[0];
711 comment = cargv[1];
712
1a760647
GB
713 if(strlen(comment)>TRUSTLOGLEN) {
714 controlreply(sender, "Your comment is too long (max: %d characters).", TRUSTLOGLEN);
715 return CMD_OK;
716 }
717
01bd21d3
GB
718 tg = tg_strtotg(name);
719
720 if(!tg) {
721 controlreply(sender, "Invalid trust group name or ID.");
722 return CMD_OK;
723 }
724
1a760647 725 controlwall(NO_OPER, NL_TRUSTS, "%s TRUSTCOMMENT'ed group '%s': %s", controlid(sender), tg->name->content, comment);
7db61652 726 trustlog(tg, sender->authname, "Comment: %s", comment);
01bd21d3
GB
727
728 return CMD_OK;
729}
730
caf2d02a
GB
731static void cleanuptrusts(void *arg);
732
733static int trusts_cmdtrustcleanup(void *source, int cargc, char **cargv) {
734 cleanuptrusts(source);
735
736 controlreply(source, "Done.");
737
738 return CMD_OK;
739}
740
01bd21d3 741
35449aa5
CP
742static int commandsregistered;
743
744static void registercommands(int hooknum, void *arg) {
f6ecfee9
GB
745 static char tgmhelp[512], thmhelp[512];
746 char validfields[512];
747 StringBuf b;
748 int i;
749
35449aa5
CP
750 if(commandsregistered)
751 return;
752 commandsregistered = 1;
753
a825c59a 754 registercontrolhelpcmd("trustgroupadd", NO_OPER, 6, trusts_cmdtrustgroupadd, "Usage: trustgroupadd <name> <howmany> <maxperident> <enforceident> <contact> ?comment?");
35449aa5 755 registercontrolhelpcmd("trustadd", NO_OPER, 2, trusts_cmdtrustadd, "Usage: trustadd <#id|name|id> <host>");
2ab0a1e7
CP
756 registercontrolhelpcmd("trustgroupdel", NO_OPER, 1, trusts_cmdtrustgroupdel, "Usage: trustgroupdel <#id|name|id>");
757 registercontrolhelpcmd("trustdel", NO_OPER, 2, trusts_cmdtrustdel, "Usage: trustdel <#id|name|id> <ip/mask>");
f6ecfee9
GB
758
759 sbinit(&b, validfields, sizeof(validfields));
760 for(i=0;i<trustgroupmods_a.cursi;i++) {
761 if(i > 0)
762 sbaddstr(&b, ", ");
763 sbaddstr(&b, trustgroupmods[i].name);
764 }
765 sbterminate(&b);
766
767 snprintf(tgmhelp, sizeof(tgmhelp), "Usage: trustgroupmodify <#id|name|id> <field> <new value>\nModifies a trust group.\nValid fields: %s", validfields);
768 registercontrolhelpcmd("trustgroupmodify", NO_OPER, 3, trusts_cmdtrustgroupmodify, tgmhelp);
769
770 sbinit(&b, validfields, sizeof(validfields));
771 for(i=0;i<trusthostmods_a.cursi;i++) {
772 if(i > 0)
773 sbaddstr(&b, ", ");
774 sbaddstr(&b, trusthostmods[i].name);
775 }
776 sbterminate(&b);
777
778 snprintf(thmhelp, sizeof(thmhelp), "Usage: trusthostmodify <#id|name|id> <host> <field> <new value>\nModifies a trust host\nValid fields: %s", validfields);
779 registercontrolhelpcmd("trusthostmodify", NO_OPER, 4, trusts_cmdtrusthostmodify, thmhelp);
780
e5c0bccf 781 registercontrolhelpcmd("trustlog", NO_OPER, 2, trusts_cmdtrustlog, "Usage: trustlog <#id|name> ?limit?\nShows log for the specified trust group.");
35c3513c 782 registercontrolhelpcmd("trustloggrep", NO_OPER, 2, trusts_cmdtrustloggrep, "Usage trustloggrep <pattern> ?limit?\nShows maching log entries.");
01bd21d3 783 registercontrolhelpcmd("trustcomment", NO_OPER, 2, trusts_cmdtrustcomment, "Usage: trustcomment <#id|name> <comment>\nLogs a comment for a trust.");
caf2d02a 784 registercontrolhelpcmd("trustcleanup", NO_DEVELOPER, 0, trusts_cmdtrustcleanup, "Usage: trustcleanup\nCleans up unused trusts.");
35449aa5
CP
785}
786
787static void deregistercommands(int hooknum, void *arg) {
788 if(!commandsregistered)
789 return;
790 commandsregistered = 0;
791
792 deregistercontrolcmd("trustgroupadd", trusts_cmdtrustgroupadd);
793 deregistercontrolcmd("trustadd", trusts_cmdtrustadd);
2ab0a1e7
CP
794 deregistercontrolcmd("trustgroupdel", trusts_cmdtrustgroupdel);
795 deregistercontrolcmd("trustdel", trusts_cmdtrustdel);
796 deregistercontrolcmd("trustgroupmodify", trusts_cmdtrustgroupmodify);
c1da06f9 797 deregistercontrolcmd("trusthostmodify", trusts_cmdtrusthostmodify);
e5c0bccf 798 deregistercontrolcmd("trustlog", trusts_cmdtrustlog);
01bd21d3
GB
799 deregistercontrolcmd("trustloggrep", trusts_cmdtrustloggrep);
800 deregistercontrolcmd("trustcomment", trusts_cmdtrustcomment);
caf2d02a 801 deregistercontrolcmd("trustcleanup", trusts_cmdtrustcleanup);
35449aa5
CP
802}
803
82a316e7
CP
804static int loaded;
805
1d9ccd69 806#define _ms_(x) (struct trustmodification){ .name = # x, .fn = modify ## x }
c1da06f9
GB
807#define MSGROUP(x) { int slot = array_getfreeslot(&trustgroupmods_a); trustgroupmods = (struct trustmodification *)trustgroupmods_a.content; memcpy(&trustgroupmods[slot], &_ms_(x), sizeof(struct trustmodification)); }
808#define MSHOST(x) { int slot = array_getfreeslot(&trusthostmods_a); trusthostmods = (struct trustmodification *)trusthostmods_a.content; memcpy(&trusthostmods[slot], &_ms_(x), sizeof(struct trustmodification)); }
1d9ccd69
CP
809
810static void setupmods(void) {
c1da06f9
GB
811 MSGROUP(expires);
812 MSGROUP(enforceident);
6ba5f655 813 MSGROUP(reliableusername);
c1da06f9
GB
814 MSGROUP(maxperident);
815 MSGROUP(contact);
816 MSGROUP(comment);
817 MSGROUP(trustedfor);
de723023 818 MSGROUP(cleanup);
e40626f0 819 MSGROUP(protected);
4b40d278 820 MSGROUP(unthrottle);
c1da06f9
GB
821
822 MSHOST(maxpernode);
823 MSHOST(nodebits);
1d9ccd69
CP
824}
825
caf2d02a
GB
826static int cleanuptrusts_active;
827
828static void cleanuptrusts(void *arg) {
829 unsigned int now, to_age;
830 nick *np = (nick *)arg;
831 trustgroup *tg;
832 trusthost *th;
833 int thcount = 0, tgcount = 0;
834 int i;
7834e8e5 835 flag_t noticelevel;
caf2d02a
GB
836 array expiredths, expiredtgs;
837
acd5f58f 838 now = getnettime();
caf2d02a
GB
839 to_age = now - (CLEANUP_TH_INACTIVE * 3600 * 24);
840
841 if(np) {
7834e8e5
TS
842 noticelevel = NL_TRUSTS;
843 controlwall(NO_OPER, noticelevel, "CLEANUPTRUSTS: Manually started by %s.", np->nick);
caf2d02a 844 } else {
7834e8e5
TS
845 noticelevel = NL_CLEANUP;
846 controlwall(NO_OPER, noticelevel, "CLEANUPTRUSTS: Automatically started.");
caf2d02a
GB
847 }
848
849 if (cleanuptrusts_active) {
850 controlwall(NO_OPER, NL_TRUSTS, "CLEANUPTRUSTS: ABORTED! Cleanup already in progress! BUG BUG BUG!");
851 return;
852 }
853
854 cleanuptrusts_active=1;
855
856 array_init(&expiredtgs, sizeof(trustgroup *));
857
858 for(tg=tglist;tg;tg=tg->next) {
859 array_init(&expiredths, sizeof(trusthost *));
860
de723023
GB
861 if(tg->flags & TRUST_NO_CLEANUP)
862 continue;
863
caf2d02a
GB
864 for(th=tg->hosts;th;th=th->next) {
865 if((th->count == 0 && th->created < to_age && th->lastseen < to_age) || (tg->expires && tg->expires < now)) {
866 int pos = array_getfreeslot(&expiredths);
867 ((trusthost **)(expiredths.content))[pos] = th;
868 }
869 }
870
871 for(i=0;i<expiredths.cursi;i++) {
4dcce883 872 const char *cidrstr;
caf2d02a
GB
873
874 th = ((trusthost **)(expiredths.content))[i];
875 triggerhook(HOOK_TRUSTS_DELHOST, th);
caf2d02a 876
3898f973 877 cidrstr = CIDRtostr(th->ip, th->bits);
caf2d02a
GB
878 trustlog(tg, "cleanuptrusts", "Removed host '%s' because it was unused for %d days.", cidrstr, CLEANUP_TH_INACTIVE);
879
3898f973
GB
880 th_delete(th);
881
caf2d02a
GB
882 thcount++;
883 }
884
885 if(!tg->hosts) {
886 int pos = array_getfreeslot(&expiredtgs);
887 ((trustgroup **)(expiredtgs.content))[pos] = tg;
888 }
889 }
890
891 for(i=0;i<expiredtgs.cursi;i++) {
892 tg = ((trustgroup **)(expiredtgs.content))[i];
893 triggerhook(HOOK_TRUSTS_DELGROUP, tg);
894 trustlog(tg, "cleanuptrusts", "Deleted group '%s' because it had no hosts left.", tg->name->content);
895 tg_delete(tg);
896 tgcount++;
897 }
898
7834e8e5 899 controlwall(NO_OPER, noticelevel, "CLEANUPTRUSTS: Removed %d trust hosts (inactive for %d days) and %d empty trust groups.", thcount, CLEANUP_TH_INACTIVE, tgcount);
caf2d02a
GB
900
901 cleanuptrusts_active=0;
902}
903
904static void schedulecleanup(int hooknum, void *arg) {
905 /* run at 1am but only if we're more than 15m away from it, otherwise run tomorrow */
906
907 time_t t = time(NULL);
908 time_t next_run = ((t / 86400) * 86400 + 86400) + 3600;
909 if(next_run - t < 900)
910 next_run+=86400;
911
912 schedulerecurring(next_run,0,86400,cleanuptrusts,NULL);
1d9ccd69
CP
913}
914
35449aa5 915void _init(void) {
82a316e7
CP
916 sstring *m;
917
c1da06f9
GB
918 array_init(&trustgroupmods_a, sizeof(struct trustmodification));
919 array_init(&trusthostmods_a, sizeof(struct trustmodification));
1d9ccd69
CP
920 setupmods();
921
82a316e7
CP
922 m = getconfigitem("trusts", "master");
923 if(!m || (atoi(m->content) != 1)) {
924 Error("trusts_management", ERR_ERROR, "Not a master server, not loaded.");
925 return;
926 }
927
928 loaded = 1;
929
35449aa5 930 registerhook(HOOK_TRUSTS_DB_LOADED, registercommands);
caf2d02a 931 registerhook(HOOK_TRUSTS_DB_LOADED, schedulecleanup);
35449aa5
CP
932 registerhook(HOOK_TRUSTS_DB_CLOSED, deregistercommands);
933
934 if(trustsdbloaded)
935 registercommands(0, NULL);
936}
937
938void _fini(void) {
c1da06f9
GB
939 array_free(&trustgroupmods_a);
940 array_free(&trusthostmods_a);
1d9ccd69 941
82a316e7
CP
942 if(!loaded)
943 return;
944
35449aa5
CP
945 deregisterhook(HOOK_TRUSTS_DB_LOADED, registercommands);
946 deregisterhook(HOOK_TRUSTS_DB_CLOSED, deregistercommands);
947
948 deregistercommands(0, NULL);
caf2d02a
GB
949
950 deleteallschedules(cleanuptrusts);
35449aa5 951}