unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
securitygroup.c (24715B)
1 /* 2 * Mask & security-group routines. 3 * (C) Copyright 2015-.. Syzop and the UnrealIRCd team 4 * 5 * This program is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU General Public License 7 * as published by the Free Software Foundation; either version 2 8 * of the License, or (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 */ 15 16 #include "unrealircd.h" 17 18 /* Global variables */ 19 SecurityGroup *securitygroups = NULL; 20 21 /** Free all masks in the mask list */ 22 void unreal_delete_masks(ConfigItem_mask *m) 23 { 24 ConfigItem_mask *m_next; 25 26 for (; m; m = m_next) 27 { 28 m_next = m->next; 29 30 safe_free(m->mask); 31 32 safe_free(m); 33 } 34 } 35 36 /** Internal function to add one individual mask to the list */ 37 static void unreal_add_mask(ConfigItem_mask **head, ConfigEntry *ce) 38 { 39 ConfigItem_mask *m = safe_alloc(sizeof(ConfigItem_mask)); 40 41 /* Since we allow both mask "xyz"; and mask { abc; def; };... */ 42 if (ce->value) 43 safe_strdup(m->mask, ce->value); 44 else 45 safe_strdup(m->mask, ce->name); 46 47 add_ListItem((ListStruct *)m, (ListStruct **)head); 48 } 49 50 /** Add mask entries from config */ 51 void unreal_add_masks(ConfigItem_mask **head, ConfigEntry *ce) 52 { 53 if (ce->items) 54 { 55 ConfigEntry *cep; 56 for (cep = ce->items; cep; cep = cep->next) 57 unreal_add_mask(head, cep); 58 } else 59 { 60 unreal_add_mask(head, ce); 61 } 62 } 63 64 /** Check if a client matches any of the masks in the mask list. 65 * The following rules apply: 66 * - If you have only negating entries, like '!abc' and '!def', then 67 * we assume an implicit * rule first, since that is clearly what 68 * the user wants. 69 * - If you have a mix, like '*.com', '!irc1*', '!irc2*' then the 70 * implicit * is dropped and we assume you only want to match *.com, 71 * with the exception of irc1*.com and irc2*.com. 72 * - If you only have normal entries without ! then things are 73 * as they always are. 74 * @param client The client to run the mask match against 75 * @param mask The mask entry from the config file 76 * @returns 1 on match, 0 on non-match. 77 */ 78 int unreal_mask_match(Client *client, ConfigItem_mask *mask) 79 { 80 int retval = 1; 81 ConfigItem_mask *m; 82 83 if (!mask) 84 return 0; /* Empty mask block is no match */ 85 86 /* First check normal matches (without ! prefix) */ 87 for (m = mask; m; m = m->next) 88 { 89 if (m->mask[0] != '!') 90 { 91 retval = 0; /* no implicit * */ 92 if (match_user(m->mask, client, MATCH_CHECK_REAL|MATCH_CHECK_EXTENDED)) 93 { 94 retval = 1; 95 break; 96 } 97 } 98 } 99 100 if (retval) 101 { 102 /* We matched. Check for exceptions (with ! prefix) */ 103 for (m = mask; m; m = m->next) 104 { 105 if ((m->mask[0] == '!') && match_user(m->mask+1, client, MATCH_CHECK_REAL|MATCH_CHECK_EXTENDED)) 106 return 0; 107 } 108 } 109 110 return retval; 111 } 112 113 /** Check if a string matches any of the masks in the mask list. 114 * The following rules apply: 115 * - If you have only negating entries, like '!abc' and '!def', then 116 * we assume an implicit * rule first, since that is clearly what 117 * the user wants. 118 * - If you have a mix, like '*.com', '!irc1*', '!irc2*' then the 119 * implicit * is dropped and we assume you only want to match *.com, 120 * with the exception of irc1*.com and irc2*.com. 121 * - If you only have normal entries without ! then things are 122 * as they always are. 123 * @param name The name to run the mask matching on 124 * @param mask The mask entry from the config file 125 * @returns 1 on match, 0 on non-match. 126 */ 127 int unreal_mask_match_string(const char *name, ConfigItem_mask *mask) 128 { 129 int retval = 1; 130 ConfigItem_mask *m; 131 132 if (!mask) 133 return 0; /* Empty mask block is no match */ 134 135 /* First check normal matches (without ! prefix) */ 136 for (m = mask; m; m = m->next) 137 { 138 if (m->mask[0] != '!') 139 { 140 retval = 0; /* no implicit * */ 141 if (match_simple(m->mask, name)) 142 { 143 retval = 1; 144 break; 145 } 146 } 147 } 148 149 if (retval) 150 { 151 /* We matched. Check for exceptions (with ! prefix) */ 152 for (m = mask; m; m = m->next) 153 { 154 if ((m->mask[0] == '!') && match_simple(m->mask+1, name)) 155 return 0; 156 } 157 } 158 159 return retval; 160 } 161 162 #define CheckNullX(x) if ((!(x)->value) || (!(*((x)->value)))) { config_error("%s:%i: missing parameter", (x)->file->filename, (x)->line_number); *errors = *errors + 1; return 0; } 163 int test_match_item(ConfigFile *conf, ConfigEntry *cep, int *errors) 164 { 165 ConfigEntry *cepp; 166 167 if (!strcmp(cep->name, "webirc") || !strcmp(cep->name, "exclude-webirc")) 168 { 169 CheckNullX(cep); 170 } else 171 if (!strcmp(cep->name, "websocket") || !strcmp(cep->name, "exclude-websocket")) 172 { 173 CheckNullX(cep); 174 } else 175 if (!strcmp(cep->name, "identified") || !strcmp(cep->name, "exclude-identified")) 176 { 177 CheckNullX(cep); 178 } else 179 if (!strcmp(cep->name, "tls") || !strcmp(cep->name, "exclude-tls")) 180 { 181 CheckNullX(cep); 182 } else 183 if (!strcmp(cep->name, "reputation-score") || !strcmp(cep->name, "exclude-reputation-score")) 184 { 185 const char *str = cep->value; 186 int v; 187 CheckNullX(cep); 188 if (*str == '<') 189 str++; 190 v = atoi(str); 191 if ((v < 1) || (v > 10000)) 192 { 193 config_error("%s:%i: %s needs to be a value of 1-10000", 194 cep->file->filename, cep->line_number, cep->name); 195 *errors = *errors + 1; 196 } 197 } else 198 if (!strcmp(cep->name, "connect-time") || !strcmp(cep->name, "exclude-connect-time")) 199 { 200 const char *str = cep->value; 201 long v; 202 CheckNullX(cep); 203 if (*str == '<') 204 str++; 205 v = config_checkval(str, CFG_TIME); 206 if (v < 1) 207 { 208 config_error("%s:%i: %s needs to be a time value (and more than 0 seconds)", 209 cep->file->filename, cep->line_number, cep->name); 210 *errors = *errors + 1; 211 } 212 } else 213 if (!strcmp(cep->name, "mask") || !strcmp(cep->name, "include-mask") || !strcmp(cep->name, "exclude-mask")) 214 { 215 for (cepp = cep->items; cepp; cepp = cepp->next) 216 { 217 if (!strcmp(cepp->name, "mask")) 218 continue; 219 if (cepp->items || cepp->value) 220 { 221 config_error("%s:%i: security-group::mask should contain hostmasks only. " 222 "Perhaps you meant to use it in security-group { %s ... } directly?", 223 cepp->file->filename, cepp->line_number, 224 cepp->name); 225 *errors = *errors + 1; 226 } 227 } 228 } else 229 if (!strcmp(cep->name, "ip")) 230 { 231 } else 232 if (!strcmp(cep->name, "security-group") || !strcmp(cep->name, "exclude-security-group")) 233 { 234 } else 235 { 236 /* Let's see if an extended server ban exists for this item... */ 237 Extban *extban; 238 if (!strncmp(cep->name, "exclude-", 8)) 239 extban = findmod_by_bantype_raw(cep->name+8, strlen(cep->name+8)); 240 else 241 extban = findmod_by_bantype_raw(cep->name, strlen(cep->name)); 242 if (extban && (extban->options & EXTBOPT_TKL) && (extban->is_banned_events & BANCHK_TKL)) 243 { 244 test_extended_list(extban, cep, errors); 245 return 1; /* Yup, handled */ 246 } 247 return 0; /* Unhandled: unknown item for us */ 248 } 249 return 1; /* Handled, but there could be errors */ 250 } 251 252 int test_match_block(ConfigFile *conf, ConfigEntry *ce, int *errors_out) 253 { 254 int errors = 0; 255 ConfigEntry *cep; 256 257 /* (If there is only a ce->value, trust that it is OK) */ 258 259 /* Test ce->items... */ 260 for (cep = ce->items; cep; cep = cep->next) 261 { 262 /* Only complain about things with values, 263 * as valueless things like "10.0.0.0/8" are treated as a mask. 264 */ 265 if (!test_match_item(conf, cep, &errors) && cep->value) 266 { 267 config_error_unknown(cep->file->filename, cep->line_number, 268 ce->name, cep->name); 269 errors++; 270 continue; 271 } 272 } 273 274 *errors_out = *errors_out + errors; 275 return errors ? 0 : 1; 276 } 277 278 #define tmbbw_is_wildcard(x) (!strcmp(x, "*") || !strcmp(x, "*@*")) 279 int test_match_block_too_broad(ConfigFile *conf, ConfigEntry *ce) 280 { 281 ConfigEntry *cep, *cepp; 282 283 // match *; 284 if (ce->value && tmbbw_is_wildcard(ce->value)) 285 return 1; 286 287 for (cep = ce->items; cep; cep = cep->next) 288 { 289 // match { *; } 290 if (!cep->value && tmbbw_is_wildcard(cep->name)) 291 return 1; 292 if (!strcmp(cep->name, "mask") || !strcmp(cep->name, "include-mask") || !strcmp(cep->name, "ip")) 293 { 294 // match { mask *; } 295 if (cep->value && tmbbw_is_wildcard(cep->value)) 296 return 1; 297 // match { mask { *; } } 298 for (cepp = cep->items; cepp; cepp = cepp->next) 299 if (tmbbw_is_wildcard(cepp->name)) 300 return 1; 301 } 302 } 303 304 return 0; 305 } 306 307 int _test_security_group(ConfigFile *conf, ConfigEntry *ce) 308 { 309 int errors = 0; 310 ConfigEntry *cep; 311 312 /* First, check the name of the security group */ 313 if (!ce->value) 314 { 315 config_error("%s:%i: security-group block needs a name, eg: security-group web-users {", 316 ce->file->filename, ce->line_number); 317 errors++; 318 } else { 319 if (!strcasecmp(ce->value, "unknown-users")) 320 { 321 config_error("%s:%i: The 'unknown-users' group is a special group that is the " 322 "inverse of 'known-users', you cannot create or adjust it in the " 323 "config file, as it is created automatically by UnrealIRCd.", 324 ce->file->filename, ce->line_number); 325 errors++; 326 return errors; 327 } 328 if (!security_group_valid_name(ce->value)) 329 { 330 config_error("%s:%i: security-group block name '%s' contains invalid characters or is too long. " 331 "Only letters, numbers, underscore and hyphen are allowed.", 332 ce->file->filename, ce->line_number, ce->value); 333 errors++; 334 } 335 } 336 337 for (cep = ce->items; cep; cep = cep->next) 338 { 339 if (!test_match_item(conf, cep, &errors)) 340 { 341 config_error_unknown(cep->file->filename, cep->line_number, 342 "security-group", cep->name); 343 errors++; 344 continue; 345 } 346 } 347 348 return errors; 349 } 350 351 int conf_match_item(ConfigFile *conf, ConfigEntry *cep, SecurityGroup **block) 352 { 353 int errors = 0; /* unused */ 354 SecurityGroup *s = *block; 355 356 /* The following code is there so we don't create a security group 357 * unless there is actually a valid config item for it encountered. 358 * This so the security group '*s' can stay NULL if there are zero 359 * items, so we don't waste any CPU if it is unused. 360 */ 361 if (*block == NULL) 362 { 363 /* Yeah we call a TEST routine from a CONFIG RUN routine ;). */ 364 if (!test_match_item(conf, cep, &errors)) 365 return 0; /* not for us */ 366 /* If we are still here then we must create the security group */ 367 *block = s = safe_alloc(sizeof(SecurityGroup)); 368 } 369 370 if (!strcmp(cep->name, "webirc")) 371 s->webirc = config_checkval(cep->value, CFG_YESNO); 372 if (!strcmp(cep->name, "websocket")) 373 s->websocket = config_checkval(cep->value, CFG_YESNO); 374 else if (!strcmp(cep->name, "identified")) 375 s->identified = config_checkval(cep->value, CFG_YESNO); 376 else if (!strcmp(cep->name, "tls")) 377 s->tls = config_checkval(cep->value, CFG_YESNO); 378 else if (!strcmp(cep->name, "reputation-score")) 379 { 380 if (*cep->value == '<') 381 s->reputation_score = 0 - atoi(cep->value+1); 382 else 383 s->reputation_score = atoi(cep->value); 384 } 385 else if (!strcmp(cep->name, "connect-time")) 386 { 387 if (*cep->value == '<') 388 s->connect_time = 0 - config_checkval(cep->value+1, CFG_TIME); 389 else 390 s->connect_time = config_checkval(cep->value, CFG_TIME); 391 } 392 else if (!strcmp(cep->name, "mask") || !strcmp(cep->name, "include-mask")) 393 { 394 unreal_add_masks(&s->mask, cep); 395 } 396 else if (!strcmp(cep->name, "ip")) 397 { 398 unreal_add_names(&s->ip, cep); 399 } 400 else if (!strcmp(cep->name, "security-group")) 401 { 402 unreal_add_names(&s->security_group, cep); 403 } 404 else if (!strcmp(cep->name, "exclude-webirc")) 405 s->exclude_webirc = config_checkval(cep->value, CFG_YESNO); 406 else if (!strcmp(cep->name, "exclude-websocket")) 407 s->exclude_websocket = config_checkval(cep->value, CFG_YESNO); 408 else if (!strcmp(cep->name, "exclude-identified")) 409 s->exclude_identified = config_checkval(cep->value, CFG_YESNO); 410 else if (!strcmp(cep->name, "exclude-tls")) 411 s->exclude_tls = config_checkval(cep->value, CFG_YESNO); 412 else if (!strcmp(cep->name, "exclude-reputation-score")) 413 { 414 if (*cep->value == '<') 415 s->exclude_reputation_score = 0 - atoi(cep->value+1); 416 else 417 s->exclude_reputation_score = atoi(cep->value); 418 } 419 else if (!strcmp(cep->name, "exclude-mask")) 420 { 421 unreal_add_masks(&s->exclude_mask, cep); 422 } 423 else if (!strcmp(cep->name, "exclude-security-group")) 424 { 425 unreal_add_names(&s->security_group, cep); 426 } 427 else 428 { 429 /* Let's see if an extended server ban exists for this item... this needs to be LAST! */ 430 Extban *extban; 431 const char *name = cep->name; 432 433 if (!strncmp(cep->name, "exclude-", 8)) 434 { 435 /* Extended (exclusive) ? */ 436 name = cep->name + 8; 437 if (findmod_by_bantype_raw(name, strlen(name))) 438 unreal_add_name_values(&s->exclude_extended, name, cep); 439 else 440 return 0; /* Unhandled */ 441 } else { 442 /* Extended (inclusive) */ 443 if (findmod_by_bantype_raw(name, strlen(name))) 444 unreal_add_name_values(&s->extended, name, cep); 445 else 446 return 0; /* Unhandled */ 447 } 448 } 449 450 /* And update the printable list */ 451 if (cep->items) 452 { 453 ConfigEntry *cep2; 454 for (cep2 = cep->items; cep2; cep2 = cep2->next) 455 add_nvplist(&s->printable_list, s->printable_list_counter++, cep->name, cep2->name); 456 } else { 457 add_nvplist(&s->printable_list, s->printable_list_counter++, cep->name, cep->value); 458 } 459 460 return 1; /* Handled by us (guaranteed earlier) */ 461 } 462 463 int conf_match_block(ConfigFile *conf, ConfigEntry *ce, SecurityGroup **block) 464 { 465 ConfigEntry *cep; 466 SecurityGroup *s = *block; 467 468 if (*block == NULL) 469 *block = s = safe_alloc(sizeof(SecurityGroup)); 470 471 /* Check for simple form: match *; / mask *; */ 472 if (ce->value) 473 { 474 unreal_add_masks(&s->mask, ce); 475 add_nvplist(&s->printable_list, s->printable_list_counter++, "mask", ce->value); 476 } 477 478 /* Check for long form: match { .... } / mask { .... } */ 479 for (cep = ce->items; cep; cep = cep->next) 480 { 481 if (!conf_match_item(conf, cep, &s) && !cep->value && !cep->items) 482 { 483 /* Valueless? Then it must be a mask like 10.0.0.0/8 */ 484 unreal_add_masks(&s->mask, cep); 485 add_nvplist(&s->printable_list, s->printable_list_counter++, "mask", cep->name); 486 } 487 } 488 return 1; 489 } 490 491 int _conf_security_group(ConfigFile *conf, ConfigEntry *ce) 492 { 493 ConfigEntry *cep; 494 SecurityGroup *s = add_security_group(ce->value, 1); 495 496 for (cep = ce->items; cep; cep = cep->next) 497 { 498 if (!strcmp(cep->name, "priority")) 499 { 500 s->priority = atoi(cep->value); 501 DelListItem(s, securitygroups); 502 AddListItemPrio(s, securitygroups, s->priority); 503 } else 504 conf_match_item(conf, cep, &s); 505 } 506 return 1; 507 } 508 509 /** Check if the name of the security-group contains only valid characters. 510 * @param name The name of the group 511 * @returns 1 if name is valid, 0 if not (eg: illegal characters) 512 */ 513 int security_group_valid_name(const char *name) 514 { 515 const char *p; 516 517 if (strlen(name) > SECURITYGROUPLEN) 518 return 0; /* Too long */ 519 520 for (p = name; *p; p++) 521 { 522 if (!isalnum(*p) && !strchr("_-", *p)) 523 return 0; /* Character not allowed */ 524 } 525 return 1; 526 } 527 528 /** Find a security-group. 529 * @param name The name of the security group 530 * @returns A SecurityGroup struct, or NULL if not found. 531 */ 532 SecurityGroup *find_security_group(const char *name) 533 { 534 SecurityGroup *s; 535 for (s = securitygroups; s; s = s->next) 536 if (!strcasecmp(name, s->name)) 537 return s; 538 return NULL; 539 } 540 541 /** Checks if a security-group exists. 542 * This function takes the 'unknown-users' magic group into account as well. 543 * @param name The name of the security group 544 * @returns 1 if it exists, 0 if not 545 */ 546 int security_group_exists(const char *name) 547 { 548 if (!strcmp(name, "unknown-users") || find_security_group(name)) 549 return 1; 550 return 0; 551 } 552 553 /** Add a new security-group and add it to the list, but search for existing one first. 554 * @param name The name of the security group 555 * @returns A SecurityGroup struct (already added to the 'securitygroups' linked list) 556 */ 557 SecurityGroup *add_security_group(const char *name, int priority) 558 { 559 SecurityGroup *s = find_security_group(name); 560 561 /* Existing? */ 562 if (s) 563 return s; 564 565 /* Otherwise, create a new entry */ 566 s = safe_alloc(sizeof(SecurityGroup)); 567 strlcpy(s->name, name, sizeof(s->name)); 568 s->priority = priority; 569 AddListItemPrio(s, securitygroups, priority); 570 return s; 571 } 572 573 /** Free a SecurityGroup struct */ 574 void free_security_group(SecurityGroup *s) 575 { 576 if (s == NULL) 577 return; 578 unreal_delete_masks(s->mask); 579 unreal_delete_masks(s->exclude_mask); 580 free_entire_name_list(s->security_group); 581 free_entire_name_list(s->exclude_security_group); 582 free_entire_name_list(s->ip); 583 free_entire_name_list(s->exclude_ip); 584 free_nvplist(s->extended); 585 free_nvplist(s->exclude_extended); 586 free_nvplist(s->printable_list); 587 safe_free(s); 588 } 589 590 /** Initialize the default security-group blocks */ 591 void set_security_group_defaults(void) 592 { 593 SecurityGroup *s, *s_next; 594 595 /* First free all security groups */ 596 for (s = securitygroups; s; s = s_next) 597 { 598 s_next = s->next; 599 free_security_group(s); 600 } 601 securitygroups = NULL; 602 603 /* Default group: webirc */ 604 s = add_security_group("webirc-users", 50); 605 s->webirc = 1; 606 607 /* Default group: websocket */ 608 s = add_security_group("websocket-users", 51); 609 s->websocket = 1; 610 611 /* Default group: known-users */ 612 s = add_security_group("known-users", 100); 613 s->identified = 1; 614 s->reputation_score = 25; 615 s->webirc = 0; 616 617 /* Default group: tls-and-known-users */ 618 s = add_security_group("tls-and-known-users", 200); 619 s->identified = 1; 620 s->reputation_score = 25; 621 s->webirc = 0; 622 s->tls = 1; 623 624 /* Default group: tls-users */ 625 s = add_security_group("tls-users", 300); 626 s->tls = 1; 627 } 628 629 int user_matches_extended_list(Client *client, NameValuePrioList *e) 630 { 631 Extban *extban; 632 BanContext b; 633 634 for (; e; e = e->next) 635 { 636 extban = findmod_by_bantype_raw(e->name, strlen(e->name)); 637 if (!extban || 638 !(extban->options & EXTBOPT_TKL) || 639 !(extban->is_banned_events & BANCHK_TKL)) 640 { 641 continue; /* extban not found or of incorrect type */ 642 } 643 644 memset(&b, 0, sizeof(BanContext)); 645 b.client = client; 646 b.banstr = e->value; 647 b.ban_check_types = BANCHK_TKL; 648 if (extban->is_banned(&b)) 649 return 1; 650 } 651 652 return 0; 653 } 654 655 int test_extended_list(Extban *extban, ConfigEntry *cep, int *errors) 656 { 657 BanContext b; 658 659 if (cep->value) 660 { 661 memset(&b, 0, sizeof(BanContext)); 662 b.banstr = cep->value; 663 b.ban_check_types = BANCHK_TKL; 664 b.what = MODE_ADD; 665 if (!extban->conv_param(&b, extban)) 666 { 667 config_error("%s:%i: %s has an invalid value", 668 cep->file->filename, cep->line_number, cep->name); 669 *errors = *errors + 1; 670 return 0; 671 } 672 } 673 674 for (cep = cep->items; cep; cep = cep->next) 675 { 676 memset(&b, 0, sizeof(BanContext)); 677 b.banstr = cep->name; 678 b.ban_check_types = BANCHK_TKL; 679 b.what = MODE_ADD; 680 if (!extban->conv_param(&b, extban)) 681 { 682 config_error("%s:%i: %s has an invalid value", 683 cep->file->filename, cep->line_number, cep->name); 684 *errors = *errors + 1; 685 return 0; 686 } 687 } 688 689 return 1; 690 } 691 692 /** Returns 1 if the user is allowed by any of the security groups in the named list. 693 * This is only used by security-group::security-group and 694 * security-group::exclude-security-group. 695 * @param client Client to check 696 * @param l The NameList 697 * @returns 1 if any of the security groups match, 0 if none of them matched. 698 */ 699 int user_allowed_by_security_group_list(Client *client, NameList *l) 700 { 701 for (; l; l = l->next) 702 if (user_allowed_by_security_group_name(client, l->name)) 703 return 1; 704 return 0; 705 } 706 707 /** Returns 1 if the user is OK as far as the security-group is concerned. 708 * @param client The client to check 709 * @param s The security-group to check against 710 * @retval 1 if user is allowed by security-group, 0 if not. 711 */ 712 int user_allowed_by_security_group(Client *client, SecurityGroup *s) 713 { 714 static int recursion_security_group = 0; 715 716 /* Allow NULL securitygroup, makes it easier in the code elsewhere */ 717 if (!s) 718 return 0; 719 720 if (recursion_security_group > 8) 721 { 722 unreal_log(ULOG_WARNING, "main", "SECURITY_GROUP_LOOP_DETECTED", client, 723 "Loop detected while processing security-group '$security_group' -- " 724 "are you perhaps referencing a security-group from a security-group?", 725 log_data_string("security_group", s->name)); 726 return 0; 727 } 728 recursion_security_group++; 729 730 /* DO NOT USE 'return' IN CODE BELOW!!!!!!!!! 731 * - use 'goto user_not_allowed' to reject 732 * - use 'goto user_allowed' to accept 733 */ 734 735 /* Process EXCLUSION criteria first... */ 736 if (s->exclude_identified && IsLoggedIn(client)) 737 goto user_not_allowed; 738 if (s->exclude_webirc && moddata_client_get(client, "webirc")) 739 goto user_not_allowed; 740 if (s->exclude_websocket && moddata_client_get(client, "websocket")) 741 goto user_not_allowed; 742 if ((s->exclude_reputation_score > 0) && (GetReputation(client) >= s->exclude_reputation_score)) 743 goto user_not_allowed; 744 if ((s->exclude_reputation_score < 0) && (GetReputation(client) < 0 - s->exclude_reputation_score)) 745 goto user_not_allowed; 746 if (s->exclude_connect_time != 0) 747 { 748 long connect_time = get_connected_time(client); 749 if ((s->exclude_connect_time > 0) && (connect_time >= s->exclude_connect_time)) 750 goto user_not_allowed; 751 if ((s->exclude_connect_time < 0) && (connect_time < 0 - s->exclude_connect_time)) 752 goto user_not_allowed; 753 } 754 if (s->exclude_tls && (IsSecureConnect(client) || (MyConnect(client) && IsSecure(client)))) 755 goto user_not_allowed; 756 if (s->exclude_mask && unreal_mask_match(client, s->exclude_mask)) 757 goto user_not_allowed; 758 if (s->exclude_ip && unreal_match_iplist(client, s->exclude_ip)) 759 goto user_not_allowed; 760 if (s->exclude_security_group && user_allowed_by_security_group_list(client, s->exclude_security_group)) 761 goto user_not_allowed; 762 if (s->exclude_extended && user_matches_extended_list(client, s->exclude_extended)) 763 goto user_not_allowed; 764 765 /* Then process INCLUSION criteria... */ 766 if (s->identified && IsLoggedIn(client)) 767 goto user_allowed; 768 if (s->webirc && moddata_client_get(client, "webirc")) 769 goto user_allowed; 770 if (s->websocket && moddata_client_get(client, "websocket")) 771 goto user_allowed; 772 if ((s->reputation_score > 0) && (GetReputation(client) >= s->reputation_score)) 773 goto user_allowed; 774 if ((s->reputation_score < 0) && (GetReputation(client) < 0 - s->reputation_score)) 775 goto user_allowed; 776 if (s->connect_time != 0) 777 { 778 long connect_time = get_connected_time(client); 779 if ((s->connect_time > 0) && (connect_time >= s->connect_time)) 780 goto user_allowed; 781 if ((s->connect_time < 0) && (connect_time < 0 - s->connect_time)) 782 goto user_allowed; 783 } 784 if (s->tls && (IsSecureConnect(client) || (MyConnect(client) && IsSecure(client)))) 785 goto user_allowed; 786 if (s->mask && unreal_mask_match(client, s->mask)) 787 goto user_allowed; 788 if (s->ip && unreal_match_iplist(client, s->ip)) 789 goto user_allowed; 790 if (s->security_group && user_allowed_by_security_group_list(client, s->security_group)) 791 goto user_allowed; 792 if (s->extended && user_matches_extended_list(client, s->extended)) 793 goto user_allowed; 794 795 user_not_allowed: 796 recursion_security_group--; 797 return 0; 798 799 user_allowed: 800 recursion_security_group--; 801 return 1; 802 } 803 804 /** Returns 1 if the user is OK as far as the security-group is concerned - "by name" version. 805 * @param client The client to check 806 * @param secgroupname The name of the security-group to check against 807 * @retval 1 if user is allowed by security-group, 0 if not. 808 */ 809 int user_allowed_by_security_group_name(Client *client, const char *secgroupname) 810 { 811 SecurityGroup *s; 812 813 /* Handle the magical 'unknown-users' case. */ 814 if (!strcmp(secgroupname, "unknown-users")) 815 { 816 /* This is simply the inverse of 'known-users' */ 817 s = find_security_group("known-users"); 818 if (!s) 819 return 0; /* that's weird!? pretty impossible. */ 820 return !user_allowed_by_security_group(client, s); 821 } 822 823 /* Find the group and evaluate it */ 824 s = find_security_group(secgroupname); 825 if (!s) 826 return 0; /* security group not found: no match */ 827 return user_allowed_by_security_group(client, s); 828 } 829 830 /** Get comma separated list of matching security groups for 'client'. 831 * This is usually only used for displaying purposes. 832 * @returns string like "unknown-users,tls-users" from a static buffer. 833 */ 834 const char *get_security_groups(Client *client) 835 { 836 SecurityGroup *s; 837 static char buf[512]; 838 839 *buf = '\0'; 840 841 /* We put known-users or unknown-users at the beginning. 842 * The latter is special and doesn't actually exist 843 * in the linked list, hence the special code here, 844 * and again later in the for loop to skip it. 845 */ 846 if (user_allowed_by_security_group_name(client, "known-users")) 847 strlcat(buf, "known-users,", sizeof(buf)); 848 else 849 strlcat(buf, "unknown-users,", sizeof(buf)); 850 851 for (s = securitygroups; s; s = s->next) 852 { 853 if (strcmp(s->name, "known-users") && 854 user_allowed_by_security_group(client, s)) 855 { 856 strlcat(buf, s->name, sizeof(buf)); 857 strlcat(buf, ",", sizeof(buf)); 858 } 859 } 860 861 if (*buf) 862 buf[strlen(buf)-1] = '\0'; 863 return buf; 864 }