unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
tkl.c (157703B)
1 /* 2 * Unreal Internet Relay Chat Daemon, src/modules/tkl.c 3 * TKL Commands: server bans, spamfilters, etc. 4 * (C) 1999-2019 Bram Matthys and The UnrealIRCd Team 5 * 6 * See file AUTHORS in IRC package for additional names of 7 * the programmers. 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 1, or (at your option) 12 * any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 22 */ 23 24 #include "unrealircd.h" 25 26 ModuleHeader MOD_HEADER 27 = { 28 "tkl", 29 "5.0", 30 "Server ban commands such as /GLINE, /SPAMFILTER, etc.", 31 "UnrealIRCd Team", 32 "unrealircd-6", 33 }; 34 35 /* Forward declarations */ 36 int tkl_config_test_spamfilter(ConfigFile *, ConfigEntry *, int, int *); 37 int tkl_config_run_spamfilter(ConfigFile *, ConfigEntry *, int); 38 int tkl_config_test_ban(ConfigFile *, ConfigEntry *, int, int *); 39 int tkl_config_run_ban(ConfigFile *, ConfigEntry *, int); 40 int tkl_config_test_except(ConfigFile *, ConfigEntry *, int, int *); 41 int tkl_config_run_except(ConfigFile *, ConfigEntry *, int); 42 int tkl_config_test_set(ConfigFile *, ConfigEntry *, int, int *); 43 int tkl_config_run_set(ConfigFile *, ConfigEntry *, int); 44 int tkl_ip_change(Client *client, const char *oldip); 45 int tkl_accept(Client *client); 46 void check_set_spamfilter_utf8_setting_changed(void); 47 CMD_FUNC(cmd_gline); 48 CMD_FUNC(cmd_shun); 49 CMD_FUNC(cmd_tempshun); 50 CMD_FUNC(cmd_gzline); 51 CMD_FUNC(cmd_kline); 52 CMD_FUNC(cmd_zline); 53 CMD_FUNC(cmd_spamfilter); 54 CMD_FUNC(cmd_eline); 55 void cmd_tkl_line(Client *client, int parc, const char *parv[], char *type); 56 int _tkl_hash(unsigned int c); 57 char _tkl_typetochar(int type); 58 int _tkl_chartotype(char c); 59 char _tkl_configtypetochar(const char *name); 60 int tkl_banexception_chartotype(char c); 61 char *_tkl_type_string(TKL *tk); 62 char *_tkl_type_config_string(TKL *tk); 63 char *tkl_banexception_configname_to_chars(char *name); 64 TKL *_tkl_add_serverban(int type, char *usermask, char *hostmask, char *reason, char *set_by, 65 time_t expire_at, time_t set_at, int soft, int flags); 66 TKL *_tkl_add_banexception(int type, char *usermask, char *hostmask, SecurityGroup *match, 67 char *reason, char *set_by, 68 time_t expire_at, time_t set_at, int soft, char *bantypes, int flags); 69 TKL *_tkl_add_nameban(int type, char *name, int hold, char *reason, char *set_by, 70 time_t expire_at, time_t set_at, int flags); 71 TKL *_tkl_add_spamfilter(int type, unsigned short target, BanAction action, Match *match, char *set_by, 72 time_t expire_at, time_t set_at, 73 time_t spamf_tkl_duration, char *spamf_tkl_reason, 74 int flags); 75 void _sendnotice_tkl_del(char *removed_by, TKL *tkl); 76 void _sendnotice_tkl_add(TKL *tkl); 77 void _free_tkl(TKL *tkl); 78 void _tkl_del_line(TKL *tkl); 79 static void _tkl_check_local_remove_shun(TKL *tmp); 80 char *_tkl_uhost(TKL *tkl, char *buf, size_t buflen, int options); 81 void tkl_expire_entry(TKL * tmp); 82 EVENT(tkl_check_expire); 83 int _find_tkline_match(Client *client, int skip_soft); 84 int _find_shun(Client *client); 85 int _find_spamfilter_user(Client *client, int flags); 86 TKL *_find_qline(Client *client, char *nick, int *ishold); 87 TKL *_find_tkline_match_zap(Client *client); 88 void _tkl_stats(Client *client, int type, const char *para, int *cnt); 89 void _tkl_sync(Client *client); 90 CMD_FUNC(_cmd_tkl); 91 int _place_host_ban(Client *client, BanAction action, char *reason, long duration); 92 int _match_spamfilter(Client *client, const char *str_in, int type, const char *cmd, const char *target, int flags, TKL **rettk); 93 int _match_spamfilter_mtags(Client *client, MessageTag *mtags, char *cmd); 94 int check_mtag_spamfilters_present(void); 95 int _join_viruschan(Client *client, TKL *tk, int type); 96 void _spamfilter_build_user_string(char *buf, char *nick, Client *client); 97 int _match_user(const char *rmask, Client *client, int options); 98 int _unreal_match_iplist(Client *client, NameList *l); 99 int _match_user_extended_server_ban(const char *banstr, Client *client); 100 void ban_target_to_tkl_layer(BanTarget ban_target, BanAction action, Client *client, const char **tkl_username, const char **tkl_hostname); 101 int _tkl_ip_hash(char *ip); 102 int _tkl_ip_hash_type(int type); 103 TKL *_find_tkl_serverban(int type, char *usermask, char *hostmask, int softban); 104 TKL *_find_tkl_banexception(int type, char *usermask, char *hostmask, int softban); 105 TKL *_find_tkl_nameban(int type, char *name, int hold); 106 TKL *_find_tkl_spamfilter(int type, char *match_string, BanAction action, unsigned short target); 107 int _find_tkl_exception(int ban_type, Client *client); 108 int _server_ban_parse_mask(Client *client, int add, char type, const char *str, char **usermask_out, char **hostmask_out, int *soft, const char **error); 109 int _server_ban_exception_parse_mask(Client *client, int add, const char *bantypes, const char *str, char **usermask_out, char **hostmask_out, int *soft, const char **error); 110 static void add_default_exempts(void); 111 int parse_extended_server_ban(const char *mask_in, Client *client, char **error, int skip_checking, char *buf1, size_t buf1len, char *buf2, size_t buf2len); 112 void _tkl_added(Client *client, TKL *tkl); 113 114 /* Externals (only for us :D) */ 115 extern int MODVAR spamf_ugly_vchanoverride; 116 117 typedef struct TKLTypeTable TKLTypeTable; 118 struct TKLTypeTable 119 { 120 char *config_name; /**< The name as used in the configuration file */ 121 char letter; /**< The letter ised in the TKL S2S command */ 122 int type; /**< TKL_xxx, optionally OR'ed with TKL_GLOBAL */ 123 char *log_name; /**< Used for logging and server notices */ 124 unsigned tkltype:1; /**< Is a type available in cmd_tkl() and friends */ 125 unsigned exceptiontype:1; /**< Is a type available for exceptions */ 126 unsigned needip:1; /**< When using this exempt option, only IP addresses are permitted (processed before DNS/ident lookups etc) */ 127 }; 128 129 /** This table which defines all TKL types and TKL exception types. 130 * If you wonder about the messy order: gline/kline/gzline/zline 131 * are at the top for performance reasons. They make up 99% of the TKLs. 132 * 133 * IMPORTANT IF YOU ARE ADDING A NEW TYPE TO THIS TABLE: 134 * - also update eline_syntax() 135 * - update help.conf (HELPOP ELINE) 136 * - more? 137 */ 138 TKLTypeTable tkl_types[] = { 139 /* <config name> <letter> <TKL_xxx type> <logging name> <tkl option?> <exempt option?> <need ip address?> */ 140 { "gline", 'G', TKL_KILL | TKL_GLOBAL, "G-Line", 1, 1, 0 }, 141 { "kline", 'k', TKL_KILL, "K-Line", 1, 1, 0 }, 142 { "gzline", 'Z', TKL_ZAP | TKL_GLOBAL, "Global Z-Line", 1, 1, 1 }, 143 { "zline", 'z', TKL_ZAP, "Z-Line", 1, 1, 1 }, 144 { "spamfilter", 'F', TKL_SPAMF | TKL_GLOBAL, "Spamfilter", 1, 1, 0 }, 145 { "qline", 'Q', TKL_NAME | TKL_GLOBAL, "Q-Line", 1, 1, 0 }, 146 { "except", 'E', TKL_EXCEPTION | TKL_GLOBAL, "Exception", 1, 0, 0 }, 147 { "shun", 's', TKL_SHUN | TKL_GLOBAL, "Shun", 1, 1, 0 }, 148 { "local-qline", 'q', TKL_NAME, "Local Q-Line", 1, 0, 0 }, 149 { "local-exception", 'e', TKL_EXCEPTION, "Local Exception", 1, 0, 0 }, 150 { "local-spamfilter", 'f', TKL_SPAMF, "Local Spamfilter", 1, 0, 0 }, 151 { "blacklist", 'b', TKL_BLACKLIST, "Blacklist", 0, 1, 1 }, 152 { "connect-flood", 'c', TKL_CONNECT_FLOOD, "Connect flood", 0, 1, 1 }, 153 { "maxperip", 'm', TKL_MAXPERIP, "Max-per-IP", 0, 1, 0 }, 154 { "handshake-data-flood", 'd', TKL_HANDSHAKE_DATA_FLOOD, "Handshake data flood", 0, 1, 1 }, 155 { "antirandom", 'r', TKL_ANTIRANDOM, "Antirandom", 0, 1, 0 }, 156 { "antimixedutf8", '8', TKL_ANTIMIXEDUTF8, "Antimixedutf8", 0, 1, 0 }, 157 { "ban-version", 'v', TKL_BAN_VERSION, "Ban Version", 0, 1, 0 }, 158 { NULL, '\0', 0, NULL, 0, 0, 0 }, 159 }; 160 #define ALL_VALID_EXCEPTION_TYPES "kline, gline, zline, gzline, spamfilter, shun, qline, blacklist, connect-flood, handshake-data-flood, antirandom, antimixedutf8, ban-version" 161 162 /* Global variables for this module */ 163 164 int max_stats_matches = 1000; 165 int mtag_spamfilters_present = 0; /**< Are any spamfilters with type SPAMF_MTAG present? */ 166 long previous_spamfilter_utf8 = 0; 167 static int firstboot = 0; 168 169 MOD_TEST() 170 { 171 MARK_AS_OFFICIAL_MODULE(modinfo); 172 HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, tkl_config_test_spamfilter); 173 HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, tkl_config_test_ban); 174 HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, tkl_config_test_except); 175 HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, tkl_config_test_set); 176 EfunctionAdd(modinfo->handle, EFUNC_TKL_HASH, _tkl_hash); 177 #if defined(__GNUC__) 178 #pragma GCC diagnostic push 179 #pragma GCC diagnostic ignored "-Wcast-function-type" 180 #endif 181 EfunctionAdd(modinfo->handle, EFUNC_TKL_TYPETOCHAR, TO_INTFUNC(_tkl_typetochar)); 182 EfunctionAdd(modinfo->handle, EFUNC_TKL_CHARTOTYPE, TO_INTFUNC(_tkl_chartotype)); 183 EfunctionAdd(modinfo->handle, EFUNC_TKL_CONFIGTYPETOCHAR, TO_INTFUNC(_tkl_configtypetochar)); 184 #if defined(__GNUC__) 185 #pragma GCC diagnostic pop 186 #endif 187 EfunctionAddString(modinfo->handle, EFUNC_TKL_TYPE_STRING, _tkl_type_string); 188 EfunctionAddString(modinfo->handle, EFUNC_TKL_TYPE_CONFIG_STRING, _tkl_type_config_string); 189 EfunctionAddPVoid(modinfo->handle, EFUNC_TKL_ADD_SERVERBAN, TO_PVOIDFUNC(_tkl_add_serverban)); 190 EfunctionAddPVoid(modinfo->handle, EFUNC_TKL_ADD_BANEXCEPTION, TO_PVOIDFUNC(_tkl_add_banexception)); 191 EfunctionAddPVoid(modinfo->handle, EFUNC_TKL_ADD_NAMEBAN, TO_PVOIDFUNC(_tkl_add_nameban)); 192 EfunctionAddPVoid(modinfo->handle, EFUNC_TKL_ADD_SPAMFILTER, TO_PVOIDFUNC(_tkl_add_spamfilter)); 193 EfunctionAddVoid(modinfo->handle, EFUNC_TKL_DEL_LINE, _tkl_del_line); 194 EfunctionAddVoid(modinfo->handle, EFUNC_FREE_TKL, _free_tkl); 195 EfunctionAddVoid(modinfo->handle, EFUNC_TKL_CHECK_LOCAL_REMOVE_SHUN, _tkl_check_local_remove_shun); 196 EfunctionAdd(modinfo->handle, EFUNC_FIND_TKLINE_MATCH, _find_tkline_match); 197 EfunctionAdd(modinfo->handle, EFUNC_FIND_SHUN, _find_shun); 198 EfunctionAdd(modinfo->handle, EFUNC_FIND_SPAMFILTER_USER, _find_spamfilter_user); 199 EfunctionAddPVoid(modinfo->handle, EFUNC_FIND_QLINE, TO_PVOIDFUNC(_find_qline)); 200 EfunctionAddPVoid(modinfo->handle, EFUNC_FIND_TKLINE_MATCH_ZAP, TO_PVOIDFUNC(_find_tkline_match_zap)); 201 EfunctionAddPVoid(modinfo->handle, EFUNC_FIND_TKL_SERVERBAN, TO_PVOIDFUNC(_find_tkl_serverban)); 202 EfunctionAddPVoid(modinfo->handle, EFUNC_FIND_TKL_BANEXCEPTION, TO_PVOIDFUNC(_find_tkl_banexception)); 203 EfunctionAddPVoid(modinfo->handle, EFUNC_FIND_TKL_NAMEBAN, TO_PVOIDFUNC(_find_tkl_nameban)); 204 EfunctionAddPVoid(modinfo->handle, EFUNC_FIND_TKL_SPAMFILTER, TO_PVOIDFUNC(_find_tkl_spamfilter)); 205 EfunctionAddVoid(modinfo->handle, EFUNC_TKL_STATS, _tkl_stats); 206 EfunctionAddVoid(modinfo->handle, EFUNC_TKL_SYNCH, _tkl_sync); 207 EfunctionAddVoid(modinfo->handle, EFUNC_CMD_TKL, _cmd_tkl); 208 EfunctionAdd(modinfo->handle, EFUNC_PLACE_HOST_BAN, _place_host_ban); 209 EfunctionAdd(modinfo->handle, EFUNC_MATCH_SPAMFILTER, _match_spamfilter); 210 EfunctionAdd(modinfo->handle, EFUNC_MATCH_SPAMFILTER_MTAGS, _match_spamfilter_mtags); 211 EfunctionAdd(modinfo->handle, EFUNC_JOIN_VIRUSCHAN, _join_viruschan); 212 EfunctionAddVoid(modinfo->handle, EFUNC_SPAMFILTER_BUILD_USER_STRING, _spamfilter_build_user_string); 213 EfunctionAdd(modinfo->handle, EFUNC_MATCH_USER, _match_user); 214 EfunctionAdd(modinfo->handle, EFUNC_TKL_IP_HASH, _tkl_ip_hash); 215 EfunctionAdd(modinfo->handle, EFUNC_TKL_IP_HASH_TYPE, _tkl_ip_hash_type); 216 EfunctionAddVoid(modinfo->handle, EFUNC_SENDNOTICE_TKL_ADD, _sendnotice_tkl_add); 217 EfunctionAddVoid(modinfo->handle, EFUNC_SENDNOTICE_TKL_DEL, _sendnotice_tkl_del); 218 EfunctionAdd(modinfo->handle, EFUNC_FIND_TKL_EXCEPTION, _find_tkl_exception); 219 EfunctionAddString(modinfo->handle, EFUNC_TKL_UHOST, _tkl_uhost); 220 EfunctionAdd(modinfo->handle, EFUNC_UNREAL_MATCH_IPLIST, _unreal_match_iplist); 221 EfunctionAdd(modinfo->handle, EFUNC_SERVER_BAN_PARSE_MASK, TO_INTFUNC(_server_ban_parse_mask)); 222 EfunctionAdd(modinfo->handle, EFUNC_SERVER_BAN_EXCEPTION_PARSE_MASK, TO_INTFUNC(_server_ban_exception_parse_mask)); 223 EfunctionAddVoid(modinfo->handle, EFUNC_TKL_ADDED, _tkl_added); 224 return MOD_SUCCESS; 225 } 226 227 MOD_INIT() 228 { 229 MARK_AS_OFFICIAL_MODULE(modinfo); 230 if (loop.booted == 0) 231 firstboot = 1; 232 LoadPersistentLong(modinfo, previous_spamfilter_utf8); 233 HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, tkl_config_run_spamfilter); 234 HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, tkl_config_run_ban); 235 HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, tkl_config_run_except); 236 HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, tkl_config_run_set); 237 HookAdd(modinfo->handle, HOOKTYPE_IP_CHANGE, 2000000000, tkl_ip_change); 238 HookAdd(modinfo->handle, HOOKTYPE_ACCEPT, -1000, tkl_accept); 239 CommandAdd(modinfo->handle, "GLINE", cmd_gline, 3, CMD_OPER); 240 CommandAdd(modinfo->handle, "SHUN", cmd_shun, 3, CMD_OPER); 241 CommandAdd(modinfo->handle, "TEMPSHUN", cmd_tempshun, 2, CMD_OPER); 242 CommandAdd(modinfo->handle, "ZLINE", cmd_zline, 3, CMD_OPER); 243 CommandAdd(modinfo->handle, "KLINE", cmd_kline, 3, CMD_OPER); 244 CommandAdd(modinfo->handle, "GZLINE", cmd_gzline, 3, CMD_OPER); 245 CommandAdd(modinfo->handle, "SPAMFILTER", cmd_spamfilter, 7, CMD_OPER); 246 CommandAdd(modinfo->handle, "ELINE", cmd_eline, 4, CMD_OPER); 247 CommandAdd(modinfo->handle, "TKL", _cmd_tkl, MAXPARA, CMD_OPER|CMD_SERVER); 248 add_default_exempts(); 249 return MOD_SUCCESS; 250 } 251 252 MOD_LOAD() 253 { 254 check_mtag_spamfilters_present(); 255 check_set_spamfilter_utf8_setting_changed(); 256 EventAdd(modinfo->handle, "tklexpire", tkl_check_expire, NULL, 5000, 0); 257 return MOD_SUCCESS; 258 } 259 260 MOD_UNLOAD() 261 { 262 SavePersistentLong(modinfo, previous_spamfilter_utf8); 263 return MOD_SUCCESS; 264 } 265 266 /** Test a spamfilter { } block in the configuration file */ 267 int tkl_config_test_spamfilter(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) 268 { 269 ConfigEntry *cep, *cepp; 270 int errors = 0; 271 char *match = NULL, *reason = NULL; 272 char has_target = 0, has_match = 0, has_action = 0, has_reason = 0, has_bantime = 0, has_match_type = 0; 273 int match_type = 0; 274 275 /* We are only interested in spamfilter { } blocks */ 276 if ((type != CONFIG_MAIN) || strcmp(ce->name, "spamfilter")) 277 return 0; 278 279 for (cep = ce->items; cep; cep = cep->next) 280 { 281 if (!strcmp(cep->name, "target")) 282 { 283 if (has_target) 284 { 285 config_warn_duplicate(cep->file->filename, 286 cep->line_number, "spamfilter::target"); 287 continue; 288 } 289 has_target = 1; 290 if (cep->value) 291 { 292 if (!spamfilter_getconftargets(cep->value)) 293 { 294 config_error("%s:%i: unknown spamfiler target type '%s'", 295 cep->file->filename, cep->line_number, cep->value); 296 errors++; 297 } 298 } 299 else if (cep->items) 300 { 301 for (cepp = cep->items; cepp; cepp = cepp->next) 302 { 303 if (!spamfilter_getconftargets(cepp->name)) 304 { 305 config_error("%s:%i: unknown spamfiler target type '%s'", 306 cepp->file->filename, 307 cepp->line_number, cepp->name); 308 errors++; 309 } 310 } 311 } 312 else 313 { 314 config_error_empty(cep->file->filename, 315 cep->line_number, "spamfilter", cep->name); 316 errors++; 317 } 318 continue; 319 } 320 if (!cep->value) 321 { 322 config_error_empty(cep->file->filename, cep->line_number, 323 "spamfilter", cep->name); 324 errors++; 325 continue; 326 } 327 if (!strcmp(cep->name, "reason")) 328 { 329 if (has_reason) 330 { 331 config_warn_duplicate(cep->file->filename, 332 cep->line_number, "spamfilter::reason"); 333 continue; 334 } 335 has_reason = 1; 336 reason = cep->value; 337 } 338 else if (!strcmp(cep->name, "match")) 339 { 340 if (has_match) 341 { 342 config_warn_duplicate(cep->file->filename, 343 cep->line_number, "spamfilter::match"); 344 continue; 345 } 346 has_match = 1; 347 match = cep->value; 348 } 349 else if (!strcmp(cep->name, "action")) 350 { 351 if (has_action) 352 { 353 config_warn_duplicate(cep->file->filename, 354 cep->line_number, "spamfilter::action"); 355 continue; 356 } 357 has_action = 1; 358 if (!banact_stringtoval(cep->value)) 359 { 360 config_error("%s:%i: spamfilter::action has unknown action type '%s'", 361 cep->file->filename, cep->line_number, cep->value); 362 errors++; 363 } 364 } 365 else if (!strcmp(cep->name, "ban-time")) 366 { 367 if (has_bantime) 368 { 369 config_warn_duplicate(cep->file->filename, 370 cep->line_number, "spamfilter::ban-time"); 371 continue; 372 } 373 has_bantime = 1; 374 } 375 else if (!strcmp(cep->name, "match-type")) 376 { 377 if (has_match_type) 378 { 379 config_warn_duplicate(cep->file->filename, 380 cep->line_number, "spamfilter::match-type"); 381 continue; 382 } 383 if (!strcasecmp(cep->value, "posix")) 384 { 385 config_error("%s:%i: this spamfilter uses match-type 'posix' which is no longer supported. " 386 "You must switch over to match-type 'regex' instead. " 387 "See https://www.unrealircd.org/docs/FAQ#spamfilter-posix-deprecated", 388 ce->file->filename, ce->line_number); 389 errors++; 390 *errs = errors; 391 return -1; /* return now, otherwise there will be issues */ 392 } 393 match_type = unreal_match_method_strtoval(cep->value); 394 if (match_type == 0) 395 { 396 config_error("%s:%i: spamfilter::match-type: unknown match type '%s', " 397 "should be one of: 'simple', 'regex' or 'posix'", 398 cep->file->filename, cep->line_number, 399 cep->value); 400 errors++; 401 continue; 402 } 403 has_match_type = 1; 404 } 405 else 406 { 407 config_error_unknown(cep->file->filename, cep->line_number, 408 "spamfilter", cep->name); 409 errors++; 410 continue; 411 } 412 } 413 414 if (match && match_type) 415 { 416 Match *m; 417 char *err; 418 419 m = unreal_create_match(match_type, match, &err); 420 if (!m) 421 { 422 config_error("%s:%i: spamfilter::match contains an invalid regex: %s", 423 ce->file->filename, 424 ce->line_number, 425 err); 426 errors++; 427 } else 428 { 429 unreal_delete_match(m); 430 } 431 } 432 433 if (!has_match) 434 { 435 config_error_missing(ce->file->filename, ce->line_number, 436 "spamfilter::match"); 437 errors++; 438 } 439 if (!has_target) 440 { 441 config_error_missing(ce->file->filename, ce->line_number, 442 "spamfilter::target"); 443 errors++; 444 } 445 if (!has_action) 446 { 447 config_error_missing(ce->file->filename, ce->line_number, 448 "spamfilter::action"); 449 errors++; 450 } 451 if (match && reason && (strlen(match) + strlen(reason) > 505)) 452 { 453 config_error("%s:%i: spamfilter block problem: match + reason field are together over 505 bytes, " 454 "please choose a shorter regex or reason", 455 ce->file->filename, ce->line_number); 456 errors++; 457 } 458 if (!has_match_type) 459 { 460 config_error_missing(ce->file->filename, ce->line_number, 461 "spamfilter::match-type"); 462 errors++; 463 } 464 465 if (match && !strcmp(match, "^LOL! //echo -a \\$\\(\\$decode\\(.+,m\\),[0-9]\\)$")) 466 { 467 config_warn("*** IMPORTANT ***"); 468 config_warn("You have old examples in your spamfilter.conf. " 469 "We suggest you to edit this file and replace the examples."); 470 config_warn("Please read https://www.unrealircd.org/docs/FAQ#old-spamfilter-conf !!!"); 471 config_warn("*****************"); 472 } 473 *errs = errors; 474 return errors ? -1 : 1; 475 } 476 477 /** Process a spamfilter { } block in the configuration file */ 478 int tkl_config_run_spamfilter(ConfigFile *cf, ConfigEntry *ce, int type) 479 { 480 ConfigEntry *cep; 481 ConfigEntry *cepp; 482 char *word = NULL; 483 time_t bantime = tempiConf.spamfilter_ban_time; 484 char *banreason = tempiConf.spamfilter_ban_reason; 485 int action = 0, target = 0; 486 int match_type = 0; 487 Match *m; 488 489 /* We are only interested in spamfilter { } blocks */ 490 if ((type != CONFIG_MAIN) || strcmp(ce->name, "spamfilter")) 491 return 0; 492 493 for (cep = ce->items; cep; cep = cep->next) 494 { 495 if (!strcmp(cep->name, "match")) 496 { 497 word = cep->value; 498 } 499 else if (!strcmp(cep->name, "target")) 500 { 501 if (cep->value) 502 target = spamfilter_getconftargets(cep->value); 503 else 504 { 505 for (cepp = cep->items; cepp; cepp = cepp->next) 506 target |= spamfilter_getconftargets(cepp->name); 507 } 508 } 509 else if (!strcmp(cep->name, "action")) 510 { 511 action = banact_stringtoval(cep->value); 512 } 513 else if (!strcmp(cep->name, "reason")) 514 { 515 banreason = cep->value; 516 } 517 else if (!strcmp(cep->name, "ban-time")) 518 { 519 bantime = config_checkval(cep->value, CFG_TIME); 520 } 521 else if (!strcmp(cep->name, "match-type")) 522 { 523 match_type = unreal_match_method_strtoval(cep->value); 524 } 525 } 526 527 m = unreal_create_match(match_type, word, NULL); 528 tkl_add_spamfilter(TKL_SPAMF, 529 target, 530 action, 531 m, 532 "-config-", 533 0, 534 TStime(), 535 bantime, 536 banreason, 537 TKL_FLAG_CONFIG); 538 return 1; 539 } 540 541 /** Test a ban { } block in the configuration file */ 542 int tkl_config_test_ban(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) 543 { 544 ConfigEntry *cep; 545 int errors = 0; 546 char has_mask = 0, has_reason = 0; 547 548 /* We are only interested in ban { } blocks */ 549 if (type != CONFIG_BAN) 550 return 0; 551 552 if (strcmp(ce->value, "nick") && strcmp(ce->value, "user") && 553 strcmp(ce->value, "ip")) 554 { 555 return 0; 556 } 557 558 for (cep = ce->items; cep; cep = cep->next) 559 { 560 if (config_is_blankorempty(cep, "ban")) 561 { 562 errors++; 563 continue; 564 } 565 if (!strcmp(cep->name, "mask")) 566 { 567 if (has_mask) 568 { 569 config_warn_duplicate(cep->file->filename, 570 cep->line_number, "ban::mask"); 571 continue; 572 } 573 has_mask = 1; 574 } 575 else if (!strcmp(cep->name, "reason")) 576 { 577 if (has_reason) 578 { 579 config_warn_duplicate(cep->file->filename, 580 cep->line_number, "ban::reason"); 581 continue; 582 } 583 has_reason = 1; 584 } 585 else 586 { 587 config_error("%s:%i: unknown directive ban %s::%s", 588 cep->file->filename, cep->line_number, 589 ce->value, 590 cep->name); 591 errors++; 592 } 593 } 594 595 if (!has_mask) 596 { 597 config_error_missing(ce->file->filename, ce->line_number, 598 "ban::mask"); 599 errors++; 600 } 601 602 if (!has_reason) 603 { 604 config_error_missing(ce->file->filename, ce->line_number, 605 "ban::reason"); 606 errors++; 607 } 608 609 *errs = errors; 610 return errors ? -1 : 1; 611 } 612 613 /** Process a ban { } block in the configuration file */ 614 int tkl_config_run_ban(ConfigFile *cf, ConfigEntry *ce, int configtype) 615 { 616 ConfigEntry *cep; 617 char *usermask = NULL; 618 char *hostmask = NULL; 619 char *reason = NULL; 620 int tkltype; 621 622 /* We are only interested in ban { } blocks */ 623 if (configtype != CONFIG_BAN) 624 return 0; 625 626 if (strcmp(ce->value, "nick") && strcmp(ce->value, "user") && 627 strcmp(ce->value, "ip")) 628 { 629 return 0; 630 } 631 632 for (cep = ce->items; cep; cep = cep->next) 633 { 634 if (!strcmp(cep->name, "mask")) 635 { 636 if (is_extended_server_ban(cep->value)) 637 { 638 char mask1buf[512], mask2buf[512]; 639 char *err = NULL; 640 641 if (!parse_extended_server_ban(cep->value, NULL, &err, 0, mask1buf, sizeof(mask1buf), mask2buf, sizeof(mask2buf))) 642 { 643 config_warn("%s:%d: Could not add extended server ban '%s': %s", 644 cep->file->filename, cep->line_number, cep->value, err); 645 goto tcrb_end; 646 } 647 safe_strdup(usermask, mask1buf); 648 safe_strdup(hostmask, mask2buf); 649 } else 650 { 651 char buf[512]; 652 char *p; 653 654 strlcpy(buf, cep->value, sizeof(buf)); 655 p = strchr(buf, '@'); 656 if (p) 657 { 658 *p++ = '\0'; 659 safe_strdup(usermask, buf); 660 safe_strdup(hostmask, p); 661 } else { 662 safe_strdup(hostmask, cep->value); 663 } 664 } 665 } else 666 if (!strcmp(cep->name, "reason")) 667 { 668 safe_strdup(reason, cep->value); 669 } 670 } 671 672 if (!usermask) 673 safe_strdup(usermask, "*"); 674 675 if (!reason) 676 safe_strdup(reason, "-"); 677 678 if (!strcmp(ce->value, "nick")) 679 tkltype = TKL_NAME; 680 else if (!strcmp(ce->value, "user")) 681 tkltype = TKL_KILL; 682 else if (!strcmp(ce->value, "ip")) 683 tkltype = TKL_ZAP; 684 else 685 abort(); /* impossible */ 686 687 if (TKLIsNameBanType(tkltype)) 688 tkl_add_nameban(tkltype, hostmask, 0, reason, "-config-", 0, TStime(), TKL_FLAG_CONFIG); 689 else if (TKLIsServerBanType(tkltype)) 690 tkl_add_serverban(tkltype, usermask, hostmask, reason, "-config-", 0, TStime(), 0, TKL_FLAG_CONFIG); 691 692 tcrb_end: 693 safe_free(usermask); 694 safe_free(hostmask); 695 safe_free(reason); 696 return 1; 697 } 698 699 int tkl_config_test_except(ConfigFile *cf, ConfigEntry *ce, int configtype, int *errs) 700 { 701 ConfigEntry *cep, *cepp; 702 int errors = 0; 703 char has_mask = 0, has_match = 0; 704 705 /* We are only interested in except { } blocks */ 706 if (configtype != CONFIG_EXCEPT) 707 return 0; 708 709 /* These are the types that we handle */ 710 if (strcmp(ce->value, "ban") && strcmp(ce->value, "throttle") && 711 strcmp(ce->value, "tkl") && strcmp(ce->value, "blacklist") && 712 strcmp(ce->value, "spamfilter")) 713 { 714 return 0; 715 } 716 717 if (!strcmp(ce->value, "tkl")) 718 { 719 config_error("%s:%d: except tkl { } has been renamed to except ban { }", 720 ce->file->filename, ce->line_number); 721 config_status("Please rename your block in the configuration file."); 722 *errs = 1; 723 return -1; 724 } 725 726 for (cep = ce->items; cep; cep = cep->next) 727 { 728 if (!strcmp(cep->name, "mask")) 729 { 730 if (cep->value || cep->items) 731 { 732 has_mask = 1; 733 test_match_block(cf, cep, &errors); 734 } 735 } else 736 if (!strcmp(cep->name, "match")) 737 { 738 if (cep->value || cep->items) 739 { 740 has_match = 1; 741 test_match_block(cf, cep, &errors); 742 } 743 } else 744 if (!strcmp(cep->name, "type")) 745 { 746 if (cep->items) 747 { 748 /* type { x; y; z; }; */ 749 for (cepp = cep->items; cepp; cepp = cepp->next) 750 if (!tkl_banexception_configname_to_chars(cepp->name)) 751 { 752 config_error("%s:%d: except ban::type '%s' unknown. Must be one of: %s", 753 cepp->file->filename, cepp->line_number, cepp->name, 754 ALL_VALID_EXCEPTION_TYPES); 755 errors++; 756 } 757 } else 758 if (cep->value) 759 { 760 /* type x; */ 761 if (!tkl_banexception_configname_to_chars(cep->value)) 762 { 763 config_error("%s:%d: except ban::type '%s' unknown. Must be one of: %s", 764 cep->file->filename, cep->line_number, cep->value, 765 ALL_VALID_EXCEPTION_TYPES); 766 errors++; 767 } 768 } 769 } else { 770 config_error_unknown(cep->file->filename, 771 cep->line_number, "except", cep->name); 772 errors++; 773 continue; 774 } 775 } 776 777 if (!has_mask && !has_match) 778 { 779 config_error_missing(ce->file->filename, ce->line_number, 780 "except ban::match"); 781 errors++; 782 } 783 if (has_mask && has_match) 784 { 785 config_error("%s:%d: You cannot have both ::mask and ::match. " 786 "You should only use except::match.", 787 ce->file->filename, ce->line_number); 788 errors++; 789 } 790 791 *errs = errors; 792 return errors ? -1 : 1; 793 } 794 795 int tkl_config_run_except(ConfigFile *cf, ConfigEntry *ce, int configtype) 796 { 797 ConfigEntry *cep, *cepp; 798 SecurityGroup *match = NULL; 799 char bantypes[64]; 800 801 /* We are only interested in except { } blocks */ 802 if (configtype != CONFIG_EXCEPT) 803 return 0; 804 805 /* These are the types that we handle */ 806 if (strcmp(ce->value, "ban") && strcmp(ce->value, "throttle") && 807 strcmp(ce->value, "blacklist") && 808 strcmp(ce->value, "spamfilter")) 809 { 810 return 0; 811 } 812 813 *bantypes = '\0'; 814 815 /* First configure all the types */ 816 for (cep = ce->items; cep; cep = cep->next) 817 { 818 if (!strcmp(cep->name, "type")) 819 { 820 if (cep->items) 821 { 822 /* type { x; y; z; }; */ 823 for (cepp = cep->items; cepp; cepp = cepp->next) 824 { 825 char *str = tkl_banexception_configname_to_chars(cepp->name); 826 strlcat(bantypes, str, sizeof(bantypes)); 827 } 828 } else 829 if (cep->value) 830 { 831 /* type x; */ 832 char *str = tkl_banexception_configname_to_chars(cep->value); 833 strlcat(bantypes, str, sizeof(bantypes)); 834 } 835 } else 836 if (!strcmp(cep->name, "match") || !strcmp(cep->name, "mask")) 837 { 838 conf_match_block(cf, cep, &match); 839 } 840 } 841 842 if (!*bantypes) 843 { 844 /* Default setting if no 'type' is specified: */ 845 if (!strcmp(ce->value, "ban")) 846 strlcpy(bantypes, "kGzZs", sizeof(bantypes)); 847 else if (!strcmp(ce->value, "throttle")) 848 strlcpy(bantypes, "c", sizeof(bantypes)); 849 else if (!strcmp(ce->value, "blacklist")) 850 strlcpy(bantypes, "b", sizeof(bantypes)); 851 else if (!strcmp(ce->value, "spamfilter")) 852 strlcpy(bantypes, "f", sizeof(bantypes)); 853 else 854 abort(); /* someone can't code */ 855 } 856 857 tkl_add_banexception(TKL_EXCEPTION, "-", "-", match, "Added in configuration file", 858 "-config-", 0, TStime(), 0, bantypes, TKL_FLAG_CONFIG); 859 860 return 1; 861 } 862 863 int tkl_config_test_set(ConfigFile *cf, ConfigEntry *ce, int configtype, int *errs) 864 { 865 int errors = 0; 866 867 /* We are only interested in set { } blocks */ 868 if (configtype != CONFIG_SET) 869 return 0; 870 871 if (!strcmp(ce->name, "max-stats-matches")) 872 { 873 if (!ce->value) 874 { 875 config_error("%s:%i: set::max-stats-matches: no value specified", 876 ce->file->filename, ce->line_number); 877 errors++; 878 } 879 // allow any other value, including 0 and negative. 880 *errs = errors; 881 return errors ? -1 : 1; 882 } 883 return 0; 884 } 885 886 int tkl_config_run_set(ConfigFile *cf, ConfigEntry *ce, int configtype) 887 { 888 /* We are only interested in set { } blocks */ 889 if (configtype != CONFIG_SET) 890 return 0; 891 892 if (!strcmp(ce->name, "max-stats-matches")) 893 { 894 max_stats_matches = atoi(ce->value); 895 return 1; 896 } 897 898 return 0; 899 } 900 901 /** Recompile all spamfilters due to set::spamfilter::utf8 setting change */ 902 void recompile_spamfilters(void) 903 { 904 TKL *tkl; 905 Match *m; 906 char *err; 907 int converted = 0; 908 int index; 909 910 index = tkl_hash('F'); 911 for (tkl = tklines[index]; tkl; tkl = tkl->next) 912 { 913 if (!TKLIsSpamfilter(tkl) || (tkl->ptr.spamfilter->match->type != MATCH_PCRE_REGEX)) 914 continue; 915 m = unreal_create_match(MATCH_PCRE_REGEX, tkl->ptr.spamfilter->match->str, &err); 916 if (!m) 917 { 918 unreal_log(ULOG_WARNING, "tkl", "SPAMFILTER_COMPILE_ERROR", NULL, 919 "Spamfilter no longer compiles upon utf8 change, error: $error. " 920 "Spamfilter '$tkl' ($tkl.reason). " 921 "Spamfilter not transformed to/from utf8.", 922 log_data_tkl("tkl", tkl), 923 log_data_string("error", err ? err : "Unknown")); 924 continue; 925 } 926 927 unreal_delete_match(tkl->ptr.spamfilter->match); /* unset old one */ 928 tkl->ptr.spamfilter->match = m; /* set new one */ 929 converted++; 930 } 931 unreal_log(ULOG_INFO, "tkl", "SPAMFILTER_UTF8_CONVERTED", NULL, 932 "Spamfilter: Recompiled $count spamfilters due to set::spamfilter::utf8 change.", 933 log_data_integer("count", converted)); 934 } 935 936 void check_set_spamfilter_utf8_setting_changed(void) 937 { 938 if (firstboot) 939 { 940 /* First boot, not a rehash */ 941 previous_spamfilter_utf8 = iConf.spamfilter_utf8; 942 return; 943 } 944 945 if (previous_spamfilter_utf8 != iConf.spamfilter_utf8) 946 recompile_spamfilters(); 947 948 previous_spamfilter_utf8 = iConf.spamfilter_utf8; 949 } 950 951 /** Return unique spamfilter id for TKL */ 952 char *spamfilter_id(TKL *tk) 953 { 954 static char buf[128]; 955 956 snprintf(buf, sizeof(buf), "%p", (void *)tk); 957 return buf; 958 } 959 960 int tkl_ip_change(Client *client, const char *oldip) 961 { 962 TKL *tkl; 963 if ((tkl = find_tkline_match_zap(client))) 964 banned_client(client, "Z-Lined", tkl->ptr.serverban->reason, (tkl->type & TKL_GLOBAL)?1:0, 0); 965 return 0; 966 } 967 968 int tkl_accept(Client *client) 969 { 970 TKL *tkl; 971 if ((tkl = find_tkline_match_zap(client))) 972 { 973 banned_client(client, "Z-Lined", tkl->ptr.serverban->reason, (tkl->type & TKL_GLOBAL)?1:0, NO_EXIT_CLIENT); 974 return HOOK_DENY; 975 } 976 return 0; 977 } 978 979 /** GLINE - Global kline. 980 ** Syntax: /gline [+|-]u@h mask time :reason 981 ** 982 ** parv[1] = [+|-]u@h mask 983 ** parv[2] = for how long 984 ** parv[3] = reason 985 */ 986 CMD_FUNC(cmd_gline) 987 { 988 if (IsServer(client)) 989 return; 990 991 if (!ValidatePermissionsForPath("server-ban:gline",client,NULL,NULL,NULL)) 992 { 993 sendnumeric(client, ERR_NOPRIVILEGES); 994 return; 995 } 996 997 if (parc == 1) 998 { 999 const char *parv[3]; 1000 parv[0] = NULL; 1001 parv[1] = "gline"; 1002 parv[2] = NULL; 1003 do_cmd(client, recv_mtags, "STATS", 2, parv); 1004 } else 1005 { 1006 cmd_tkl_line(client, parc, parv, "G"); 1007 } 1008 } 1009 1010 /** GZLINE - Global zline. 1011 */ 1012 CMD_FUNC(cmd_gzline) 1013 { 1014 if (IsServer(client)) 1015 return; 1016 1017 if (!ValidatePermissionsForPath("server-ban:zline:global",client,NULL,NULL,NULL)) 1018 { 1019 sendnumeric(client, ERR_NOPRIVILEGES); 1020 return; 1021 } 1022 1023 if (parc == 1) 1024 { 1025 const char *parv[3]; 1026 parv[0] = NULL; 1027 parv[1] = "gline"; /* (there's no /STATS gzline, it's included in /STATS gline output) */ 1028 parv[2] = NULL; 1029 do_cmd(client, recv_mtags, "STATS", 2, parv); 1030 } else { 1031 cmd_tkl_line(client, parc, parv, "Z"); 1032 } 1033 } 1034 1035 /** SHUN - Shun a user so it can no longer execute any meaningful commands. 1036 */ 1037 CMD_FUNC(cmd_shun) 1038 { 1039 if (IsServer(client)) 1040 return; 1041 1042 if (!ValidatePermissionsForPath("server-ban:shun",client,NULL,NULL,NULL)) 1043 { 1044 sendnumeric(client, ERR_NOPRIVILEGES); 1045 return; 1046 } 1047 1048 if (parc == 1) 1049 { 1050 const char *parv[3]; 1051 parv[0] = NULL; 1052 parv[1] = "shun"; 1053 parv[2] = NULL; 1054 do_cmd(client, recv_mtags, "STATS", 2, parv); 1055 } else { 1056 cmd_tkl_line(client, parc, parv, "s"); 1057 } 1058 } 1059 1060 /** TEMPSHUN - Temporarily shun a user so it can no longer execute 1061 * any meaningful commands - until the user disconnects (session only). 1062 */ 1063 CMD_FUNC(cmd_tempshun) 1064 { 1065 Client *target; 1066 const char *comment = ((parc > 2) && !BadPtr(parv[2])) ? parv[2] : "no reason"; 1067 const char *name; 1068 int remove = 0; 1069 1070 if (MyUser(client) && (!ValidatePermissionsForPath("server-ban:shun:temporary",client,NULL,NULL,NULL))) 1071 { 1072 sendnumeric(client, ERR_NOPRIVILEGES); 1073 return; 1074 } 1075 if ((parc < 2) || BadPtr(parv[1])) 1076 { 1077 sendnumeric(client, ERR_NEEDMOREPARAMS, "TEMPSHUN"); 1078 return; 1079 } 1080 if (parv[1][0] == '+') 1081 name = parv[1]+1; 1082 else if (parv[1][0] == '-') 1083 { 1084 name = parv[1]+1; 1085 remove = 1; 1086 } else 1087 name = parv[1]; 1088 1089 target = find_user(name, NULL); 1090 if (!target) 1091 { 1092 sendnumeric(client, ERR_NOSUCHNICK, name); 1093 return; 1094 } 1095 if (!MyUser(target)) 1096 { 1097 sendto_one(target, NULL, ":%s TEMPSHUN %c%s :%s", 1098 client->id, remove ? '-' : '+', target->id, comment); 1099 } else { 1100 char buf[1024]; 1101 if (!remove) 1102 { 1103 if (IsShunned(target)) 1104 { 1105 sendnotice(client, "User '%s' already shunned", target->name); 1106 } else if (ValidatePermissionsForPath("immune:server-ban:shun",target,NULL,NULL,NULL)) 1107 { 1108 sendnotice(client, "You cannot tempshun '%s' because (s)he is an oper with 'immune:server-ban:shun' privilege", target->name); 1109 } else 1110 { 1111 SetShunned(target); 1112 unreal_log(ULOG_INFO, "tkl", "TKL_ADD_TEMPSHUN", client, 1113 "Temporary shun added on user $target.details [reason: $shun_reason] [by: $client]", 1114 log_data_string("shun_reason", comment), 1115 log_data_client("target", target)); 1116 } 1117 } else { 1118 if (!IsShunned(target)) 1119 { 1120 sendnotice(client, "User '%s' is not shunned", target->name); 1121 } else { 1122 ClearShunned(target); 1123 unreal_log(ULOG_INFO, "tkl", "TKL_DEL_TEMPSHUN", client, 1124 "Temporary shun removed from user $target.details [by: $client]", 1125 log_data_client("target", target)); 1126 } 1127 } 1128 } 1129 } 1130 1131 /** KLINE - Kill line (ban user from local server) 1132 */ 1133 CMD_FUNC(cmd_kline) 1134 { 1135 if (IsServer(client)) 1136 return; 1137 1138 if (!ValidatePermissionsForPath("server-ban:kline:local:add",client,NULL,NULL,NULL)) 1139 { 1140 sendnumeric(client, ERR_NOPRIVILEGES); 1141 return; 1142 } 1143 1144 if (parc == 1) 1145 { 1146 const char *parv[3]; 1147 parv[0] = NULL; 1148 parv[1] = "kline"; 1149 parv[2] = NULL; 1150 do_cmd(client, recv_mtags, "STATS", 2, parv); 1151 return; 1152 } 1153 1154 if (!ValidatePermissionsForPath("server-ban:kline:remove",client,NULL,NULL,NULL) && *parv[1] == '-') 1155 { 1156 sendnumeric(client, ERR_NOPRIVILEGES); 1157 return; 1158 } 1159 1160 cmd_tkl_line(client, parc, parv, "k"); 1161 } 1162 1163 /** Generate stats for '/GLINE -stats' and such */ 1164 void tkl_general_stats(Client *client) 1165 { 1166 int index, index2; 1167 TKL *tkl; 1168 int total = 0; 1169 int subtotal; 1170 1171 /* First, hashed entries.. */ 1172 for (index = 0; index < TKLIPHASHLEN1; index++) 1173 { 1174 for (index2 = 0; index2 < TKLIPHASHLEN2; index2++) 1175 { 1176 subtotal = 0; 1177 for (tkl = tklines_ip_hash[index][index2]; tkl; tkl = tkl->next) 1178 subtotal++; 1179 if (subtotal > 0) 1180 sendnotice(client, "Slot %d:%d has %d item(s)", index, index2, subtotal); 1181 total += subtotal; 1182 } 1183 } 1184 sendnotice(client, "Hashed TKL items: %d item(s)", total); 1185 1186 /* Now normal entries.. */ 1187 subtotal = 0; 1188 for (index = 0; index < TKLISTLEN; index++) 1189 { 1190 for (tkl = tklines[index]; tkl; tkl = tkl->next) 1191 subtotal++; 1192 } 1193 sendnotice(client, "Standard TKL items: %d item(s)", subtotal); 1194 total += subtotal; 1195 sendnotice(client, "Grand total TKL items: %d item(s)", total); 1196 } 1197 1198 /** ZLINE - Kill a user as soon as it tries to connect to the server. 1199 * This happens before any DNS/ident lookups have been done and 1200 * before any data has been processed (including no TLS handshake, etc.) 1201 */ 1202 CMD_FUNC(cmd_zline) 1203 { 1204 if (IsServer(client)) 1205 return; 1206 1207 if (!ValidatePermissionsForPath("server-ban:zline:local:add",client,NULL,NULL,NULL)) 1208 { 1209 sendnumeric(client, ERR_NOPRIVILEGES); 1210 return; 1211 } 1212 1213 if (parc == 1) 1214 { 1215 const char *parv[3]; 1216 parv[0] = NULL; 1217 parv[1] = "kline"; /* (there's no /STATS zline, it's included in /STATS kline output) */ 1218 parv[2] = NULL; 1219 do_cmd(client, recv_mtags, "STATS", 2, parv); 1220 return; 1221 } 1222 1223 if ((parc > 1) && !BadPtr(parv[1]) && !strcasecmp(parv[1], "-stats")) 1224 { 1225 /* Print some statistics */ 1226 tkl_general_stats(client); 1227 return; 1228 } 1229 1230 cmd_tkl_line(client, parc, parv, "z"); 1231 } 1232 1233 /** Check if a ban is placed with a too broad mask (like '*') */ 1234 int ban_too_broad(char *usermask, char *hostmask) 1235 { 1236 char *p; 1237 int cnt = 0; 1238 1239 /* Scary config setting. Hmmm. */ 1240 if (ALLOW_INSANE_BANS) 1241 return 0; 1242 1243 /* Allow things like clone@*, dsfsf@*, etc.. */ 1244 if (!strchr(usermask, '*') && !strchr(usermask, '?')) 1245 return 0; 1246 1247 /* If it's a CIDR, then check /mask first.. */ 1248 p = strchr(hostmask, '/'); 1249 if (p) 1250 { 1251 int cidrlen = atoi(p+1); 1252 if (strchr(hostmask, ':')) 1253 { 1254 if (cidrlen < 48) 1255 return 1; /* too broad IPv6 CIDR mask */ 1256 } else { 1257 if (cidrlen < 16) 1258 return 1; /* too broad IPv4 CIDR mask */ 1259 } 1260 } 1261 1262 /* Must at least contain 4 non-wildcard/non-dot characters. 1263 * This will deal with non-CIDR and hosts, but any correct 1264 * CIDR mask will also pass this test (which is fine). 1265 */ 1266 for (p = hostmask; *p; p++) 1267 if (*p != '*' && *p != '.' && *p != '?' && *p != ':') 1268 cnt++; 1269 1270 if (cnt >= 4) 1271 return 0; 1272 1273 return 1; 1274 } 1275 1276 /** Ugly function, only meant to be called by cmd_tkl_line() */ 1277 static int xline_exists(char *type, char *usermask, char *hostmask) 1278 { 1279 char *umask = usermask; 1280 int softban = 0; 1281 int tpe = tkl_chartotype(type[0]); 1282 1283 if (*umask == '%') 1284 { 1285 umask++; 1286 softban = 1; 1287 } 1288 1289 return find_tkl_serverban(tpe, umask, hostmask, softban) ? 1 : 0; 1290 } 1291 1292 /** Parse an extended server ban such as ~S:aabbccddetc.. 1293 * Used for both syntax checking and to split it into userbuf/hostbuf for TKL protocol. 1294 * @param mask_in The input mask (eg: ~S:aabbccddetc) 1295 * @param client Client doing the request (used to send errors), can be NULL. 1296 * @param error Pointer to set to the error buffer (must be set!) 1297 * @param skip_checking Set this to 1 if coming from a remote user/server to skip the .is_ok() check. 1298 * Note that a .conv_param() call can still fail. 1299 * @param buf1 Buffer to store the extban starter in (eg "~S:") -- can be NULL if you don't need it 1300 * @param buf1len Length of buf1 1301 * @param buf2 Buffer to store the extban remainder in (eg "aabbccddetc") -- can be NULL if you don't need it 1302 * @param buf2len Length of buf2 1303 * @returns 1 if the server ban is acceptable. The ban will then be stored in buf1/buf2 (unless those 1304 * were set to NULL by the caller). On failure we return 0 and 'error' is set appropriately. 1305 */ 1306 int parse_extended_server_ban(const char *mask_in, Client *client, char **error, int skip_checking, char *buf1, size_t buf1len, char *buf2, size_t buf2len) 1307 { 1308 const char *nextbanstr = NULL; 1309 Extban *extban; 1310 const char *str; 1311 char *p; 1312 BanContext *b = NULL; 1313 char mask[USERLEN + NICKLEN + HOSTLEN + 32]; // same as extban_conv_param_nuh_or_extban() 1314 char newmask[USERLEN + NICKLEN + HOSTLEN + 32]; 1315 char soft_ban = 0; 1316 1317 *error = NULL; 1318 if (buf1 && buf2) 1319 *buf1 = *buf2 = '\0'; 1320 1321 /* Work on a copy */ 1322 if (*mask_in == '%') 1323 { 1324 strlcpy(mask, mask_in+1, sizeof(mask)); 1325 soft_ban = 1; 1326 } else { 1327 strlcpy(mask, mask_in, sizeof(mask)); 1328 } 1329 1330 extban = findmod_by_bantype(mask, &nextbanstr); 1331 if (!extban || !(extban->options & EXTBOPT_TKL)) 1332 { 1333 *error = "Invalid or unsupported extended server ban requested. Valid types are for example ~a, ~r, ~S."; 1334 goto fail_parse_extended_server_ban; 1335 } 1336 1337 b = safe_alloc(sizeof(BanContext)); 1338 b->client = client; 1339 b->banstr = nextbanstr; 1340 b->is_ok_check = EXBCHK_PARAM; 1341 b->what = MODE_ADD; 1342 b->ban_type = EXBTYPE_TKL; 1343 1344 /* Run .is_ok() for the extban. This check is skipped if coming from a remote user/server */ 1345 if (skip_checking == 0) 1346 { 1347 if (extban->is_ok && !extban->is_ok(b)) 1348 { 1349 *error = "Invalid extended server ban"; 1350 goto fail_parse_extended_server_ban; 1351 } 1352 } 1353 1354 b->banstr = nextbanstr; 1355 str = extban->conv_param(b, extban); 1356 if (!str) 1357 { 1358 *error = "Invalid extended server ban"; 1359 goto fail_parse_extended_server_ban; 1360 } 1361 str = prefix_with_extban(str, b, extban, newmask, sizeof(newmask)); 1362 if (str == NULL) 1363 { 1364 *error = "Unexpected error (1)"; 1365 goto fail_parse_extended_server_ban; 1366 } 1367 1368 p = strchr(newmask, ':'); 1369 if (!p) 1370 { 1371 *error = "Unexpected error (2)"; 1372 goto fail_parse_extended_server_ban; 1373 } 1374 1375 if (p[1] == ':') 1376 { 1377 *error = "For technical reasons you cannot use a double : at the beginning of an extended server ban (eg ~a::xyz)"; 1378 goto fail_parse_extended_server_ban; 1379 } 1380 1381 if (!p[1]) 1382 { 1383 *error = "Empty / too short extended server ban"; 1384 goto fail_parse_extended_server_ban; 1385 } 1386 1387 /* Now convert the result into two buffers for TKL protocol usage */ 1388 if (buf1 && buf2) 1389 { 1390 char save; 1391 p++; 1392 save = *p; 1393 *p = '\0'; 1394 /* First buffer is eg ~S: or %~S: */ 1395 snprintf(buf1, buf1len, "%s%s", 1396 soft_ban ? "%" : "", 1397 newmask); 1398 *p = save; 1399 strlcpy(buf2, p, buf2len); /* eg 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef */ 1400 } 1401 safe_free(b); 1402 return 1; 1403 1404 fail_parse_extended_server_ban: 1405 safe_free(b); 1406 return 0; 1407 } 1408 1409 /** Parse a server ban request such as 'blah@blah.com' or '~account:EvilUser' 1410 * @param client Client requesting the operation (can be NULL) 1411 * @param add Set to 1 for add ban, 0 for remove ban 1412 * @param type TKL type (character), see 2nd column of tkl_types[], eg 'G' for gline. 1413 * @param str The input string 1414 * @param usermask_out Will be set to the TKL usermask 1415 * @param hostmask_out Will be set to the TKL hostmask 1416 * @param soft Will be set to 1 if it's a softban, otherwise 0 1417 * @param error On failure, this will contain the error string 1418 * @retval 1 Success: usermask_out, hostmask_out and soft are set appropriately. 1419 * @retval 0 Failed: error is set appropriately 1420 */ 1421 int _server_ban_parse_mask(Client *client, int add, char type, const char *str, char **usermask_out, char **hostmask_out, int *soft, const char **error) 1422 { 1423 static char maskbuf[BUFSIZE], mask1buf[BUFSIZE], mask2buf[BUFSIZE]; 1424 char *hostmask = NULL, *usermask = NULL; 1425 char *mask, *p; 1426 1427 /* Set defaults */ 1428 *usermask_out = *hostmask_out = NULL; 1429 *soft = 0; 1430 1431 strlcpy(maskbuf, str, sizeof(maskbuf)); 1432 mask = maskbuf; 1433 1434 if ((*mask != '~') && strchr(mask, '!')) 1435 { 1436 *error = "Cannot have '!' in masks."; 1437 return 0; 1438 } 1439 1440 if (*mask == ':') 1441 { 1442 *error = "Mask cannot start with a ':'."; 1443 return 0; 1444 } 1445 1446 if (strchr(mask, ' ')) 1447 { 1448 *error = "Mask may not contain spaces"; 1449 return 0; 1450 } 1451 1452 /* Check if it's a softban */ 1453 if (*mask == '%') 1454 { 1455 *soft = 1; 1456 if (!strchr("kGs", type)) 1457 { 1458 *error = "The %% prefix (soft ban) is only available for KLINE, GLINE and SHUN. " 1459 "For technical reasons this will not work for (G)ZLINE."; 1460 return 0; 1461 } 1462 } 1463 1464 /* Check if it's an extended server ban */ 1465 if (is_extended_server_ban(mask)) 1466 { 1467 char *err; 1468 1469 if (!parse_extended_server_ban(mask, client, &err, 0, mask1buf, sizeof(mask1buf), mask2buf, sizeof(mask2buf))) 1470 { 1471 /* If adding, reject it */ 1472 if (add) 1473 { 1474 *error = err; 1475 return 0; 1476 } else 1477 { 1478 /* Always allow any removal attempt... */ 1479 char *p; 1480 char save; 1481 p = strchr(mask, ':'); 1482 p++; 1483 save = *p; 1484 *p = '\0'; 1485 strlcpy(mask1buf, mask, sizeof(mask1buf)); 1486 *p = save; 1487 strlcpy(mask2buf, p, sizeof(mask2buf)); 1488 /* fallthrough */ 1489 } 1490 } 1491 if (add && ((type == 'z') || (type == 'Z'))) 1492 { 1493 *error = "(G)Zlines must be placed at *@\037IPMASK\037. " 1494 "Extended server bans don't work here because (g)zlines are processed " 1495 "BEFORE dns and ident lookups are done and before reading any client data. " 1496 "If you want to use extended server bans then use a KLINE/GLINE instead."; 1497 return 0; 1498 } 1499 usermask = mask1buf; /* eg ~S: */ 1500 hostmask = mask2buf; /* eg 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef */ 1501 } else 1502 { 1503 /* Check if it's a hostmask and legal .. */ 1504 p = strchr(mask, '@'); 1505 if (p) { 1506 if ((p == mask) || !p[1]) 1507 { 1508 *error = "No user@host specified"; 1509 return 0; 1510 } 1511 usermask = strtok(mask, "@"); 1512 hostmask = strtok(NULL, ""); 1513 if (BadPtr(hostmask)) 1514 { 1515 if (BadPtr(usermask)) 1516 { 1517 *error = "Invalid mask"; 1518 return 0; 1519 } 1520 hostmask = usermask; 1521 usermask = "*"; 1522 } 1523 if (*hostmask == ':') 1524 { 1525 *error = "For technical reasons you cannot start the host with a ':', sorry"; 1526 return 0; 1527 } 1528 if (add && ((type == 'z') || (type == 'Z'))) 1529 { 1530 /* It's a (G)ZLINE, make sure the user isn't specyfing a HOST. 1531 * Just a warning in 3.2.3, but an error in 3.2.4. 1532 */ 1533 if (strcmp(usermask, "*")) 1534 { 1535 *error = "(G)Zlines must be placed at \037*\037@ipmask, not \037user\037@ipmask. This is " 1536 "because (g)zlines are processed BEFORE dns and ident lookups are done. " 1537 "If you want to use usermasks, use a KLINE/GLINE instead."; 1538 return 0; 1539 } 1540 for (p=hostmask; *p; p++) 1541 { 1542 if (isalpha(*p) && !isxdigit(*p)) 1543 { 1544 *error = "ERROR: (g)zlines must be placed at *@\037IPMASK\037, not *@\037HOSTMASK\037 " 1545 "(so for example *@192.168.* is ok, but *@*.aol.com is not). " 1546 "This is because (g)zlines are processed BEFORE dns and ident lookups are done. " 1547 "If you want to use hostmasks instead of ipmasks, use a KLINE/GLINE instead."; 1548 return 0; 1549 } 1550 } 1551 } 1552 } 1553 else 1554 { 1555 /* It's seemingly a nick .. let's see if we can find the user */ 1556 Client *acptr; 1557 if ((acptr = find_user(mask, NULL))) 1558 { 1559 BanAction action = BAN_ACT_KLINE; // just a dummy default 1560 if ((type == 'z') || (type == 'Z')) 1561 action = BAN_ACT_ZLINE; // to indicate zline (no hostname, no dns, etc) 1562 ban_target_to_tkl_layer(iConf.manual_ban_target, action, acptr, (const char **)&usermask, (const char **)&hostmask); 1563 } 1564 else 1565 { 1566 *error = "Nickname not found"; 1567 return 0; 1568 } 1569 } 1570 } 1571 1572 /* Success! */ 1573 *usermask_out = usermask; 1574 *hostmask_out = hostmask; 1575 return 1; 1576 } 1577 1578 /** Intermediate layer between user functions such as KLINE/GLINE 1579 * and the TKL layer (cmd_tkl). 1580 * This allows us doing some syntax checking and other helpful 1581 * things that are the same for many types of *LINES. 1582 */ 1583 void cmd_tkl_line(Client *client, int parc, const char *parv[], char *type) 1584 { 1585 time_t secs; 1586 int add = 1, soft; 1587 time_t i; 1588 Client *acptr = NULL; 1589 const char *mask; 1590 const char *error; 1591 char mo[64], mo2[64]; 1592 char *p, *usermask, *hostmask; 1593 const char *tkllayer[10] = { 1594 me.name, /*0 server.name */ 1595 NULL, /*1 +|- */ 1596 NULL, /*2 G */ 1597 NULL, /*3 user */ 1598 NULL, /*4 host */ 1599 NULL, /*5 set_by */ 1600 "0", /*6 expire_at */ 1601 NULL, /*7 set_at */ 1602 "no reason", /*8 reason */ 1603 NULL 1604 }; 1605 struct tm *t; 1606 1607 if ((parc == 1) || BadPtr(parv[1])) 1608 return; /* shouldn't happen */ 1609 1610 mask = parv[1]; 1611 1612 if (*mask == '-') 1613 { 1614 add = 0; 1615 mask++; 1616 } 1617 else if (*mask == '+') 1618 { 1619 add = 1; 1620 mask++; 1621 } 1622 1623 if (!server_ban_parse_mask(client, add, *type, mask, &usermask, &hostmask, &soft, &error)) 1624 { 1625 sendnotice(client, "[ERROR] %s", error); 1626 return; 1627 } 1628 1629 if (add && ban_too_broad(usermask, hostmask)) 1630 { 1631 sendnotice(client, "*** [error] Too broad mask"); 1632 return; 1633 } 1634 1635 secs = 0; 1636 1637 if (add && (parc > 3)) 1638 { 1639 secs = config_checkval(parv[2], CFG_TIME); 1640 if (secs < 0) 1641 { 1642 sendnotice(client, "*** [error] The time you specified is out of range!"); 1643 return; 1644 } 1645 } 1646 tkllayer[1] = add ? "+" : "-"; 1647 tkllayer[2] = type; 1648 tkllayer[3] = usermask; 1649 tkllayer[4] = hostmask; 1650 tkllayer[5] = make_nick_user_host(client->name, client->user->username, GetHost(client)); 1651 if (add) 1652 { 1653 if (secs == 0) 1654 { 1655 if (DEFAULT_BANTIME && (parc <= 3)) 1656 ircsnprintf(mo, sizeof(mo), "%lld", (long long)(DEFAULT_BANTIME + TStime())); 1657 else 1658 ircsnprintf(mo, sizeof(mo), "%lld", (long long)secs); /* "0" */ 1659 } 1660 else 1661 ircsnprintf(mo, sizeof(mo), "%lld", (long long)(secs + TStime())); 1662 ircsnprintf(mo2, sizeof(mo2), "%lld", (long long)TStime()); 1663 tkllayer[6] = mo; 1664 tkllayer[7] = mo2; 1665 if (parc > 3) { 1666 tkllayer[8] = parv[3]; 1667 } else if (parc > 2) { 1668 tkllayer[8] = parv[2]; 1669 } 1670 /* Blerghhh... */ 1671 i = atol(mo); 1672 t = gmtime(&i); 1673 if (!t) 1674 { 1675 sendnotice(client, "*** [error] The time you specified is out of range"); 1676 return; 1677 } 1678 1679 /* Some stupid checking */ 1680 if (xline_exists(type, usermask, hostmask)) 1681 { 1682 sendnotice(client, "ERROR: Ban for %s@%s already exists.", usermask, hostmask); 1683 return; 1684 } 1685 1686 /* call the tkl layer .. */ 1687 cmd_tkl(&me, NULL, 9, tkllayer); 1688 } 1689 else 1690 { 1691 /* call the tkl layer .. */ 1692 cmd_tkl(&me, NULL, 6, tkllayer); 1693 1694 } 1695 } 1696 1697 void eline_syntax(Client *client) 1698 { 1699 sendnotice(client, " Syntax: /ELINE <user@host> <bantypes> <expiry-time> <reason>"); 1700 sendnotice(client, " Or: /ELINE <extserverban> <bantypes> <expiry-time> <reason>"); 1701 sendnotice(client, "Valid bantypes are:"); 1702 sendnotice(client, "k: K-Line G: G-Line"); 1703 sendnotice(client, "z: Z-Line Z: Global Z-Line"); 1704 sendnotice(client, "Q: Q-Line"); 1705 sendnotice(client, "s: Shun"); 1706 sendnotice(client, "F: Spamfilter"); 1707 sendnotice(client, "b: Blacklist checking"); 1708 sendnotice(client, "c: Connect flood (bypass set::anti-flood::connect-flood))"); 1709 sendnotice(client, "d: Handshake data flood (no ZLINE on too much data before registration)"); 1710 sendnotice(client, "m: Bypass allow::maxperip restriction"); 1711 sendnotice(client, "r: Bypass antirandom module"); 1712 sendnotice(client, "8: Bypass antimixedutf8 module"); 1713 sendnotice(client, "v: Bypass ban version { } blocks"); 1714 sendnotice(client, "Examples:"); 1715 sendnotice(client, "/ELINE *@unrealircd.org kGF 0 This user is exempt"); 1716 sendnotice(client, "/ELINE ~S:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef kGF 0 Trusted user with this certificate fingerprint"); 1717 sendnotice(client, "-"); 1718 sendnotice(client, "To get a list of all current ELINEs, type: /STATS except"); 1719 } 1720 1721 /** Check if any of the specified types require the 1722 * exception to be placed on *@ip rather than 1723 * user@host or *@host. For eg zlines. 1724 */ 1725 TKLTypeTable *eline_type_requires_ip(const char *bantypes) 1726 { 1727 int i; 1728 1729 for (i=0; tkl_types[i].config_name; i++) 1730 if (tkl_types[i].needip && strchr(bantypes, tkl_types[i].letter)) 1731 return &tkl_types[i]; 1732 return NULL; 1733 } 1734 1735 /** Checks a string to see if it contains invalid ban exception types */ 1736 int contains_invalid_server_ban_exception_type(const char *str, char *c) 1737 { 1738 const char *p; 1739 for (p = str; *p; p++) 1740 { 1741 if (!tkl_banexception_chartotype(*p)) 1742 { 1743 *c = *p; 1744 return 1; 1745 } 1746 } 1747 return 0; 1748 } 1749 1750 /** Parse a server ban exception (ELINE) request such as 'blah@blah.com' or '~account:EvilUser' 1751 * @param client Client requesting the operation (can be NULL) 1752 * @param add Set to 1 for add ban, 0 for remove ban 1753 * @param bantypes Ban types to exempt from 1754 * @param str The input string 1755 * @param usermask_out Will be set to the TKL usermask 1756 * @param hostmask_out Will be set to the TKL hostmask 1757 * @param soft Will be set to 1 if it's a softban, otherwise 0 1758 * @param error On failure, this will contain the error string 1759 * @retval 1 Success: usermask_out, hostmask_out and soft are set appropriately. 1760 * @retval 0 Failed: error is set appropriately 1761 */ 1762 int _server_ban_exception_parse_mask(Client *client, int add, const char *bantypes, const char *str, char **usermask_out, char **hostmask_out, int *soft, const char **error) 1763 { 1764 static char maskbuf[BUFSIZE], mask1buf[BUFSIZE], mask2buf[BUFSIZE], errbuf[BUFSIZE]; 1765 char *hostmask = NULL, *usermask = NULL; 1766 char *mask, *p; 1767 TKLTypeTable *t; 1768 1769 /* Set defaults */ 1770 *usermask_out = *hostmask_out = NULL; 1771 *soft = 0; 1772 1773 strlcpy(maskbuf, str, sizeof(maskbuf)); 1774 mask = maskbuf; 1775 1776 if ((*mask != '~') && strchr(mask, '!')) 1777 { 1778 *error = "Cannot have '!' in masks."; 1779 return 0; 1780 } 1781 1782 if (*mask == ':') 1783 { 1784 *error = "Mask cannot start with a ':'."; 1785 return 0; 1786 } 1787 1788 if (strchr(mask, ' ')) 1789 { 1790 *error = "Mask may not contain spaces"; 1791 return 0; 1792 } 1793 1794 if (*mask == '%') 1795 { 1796 *soft = 1; 1797 /* do we need more sanity checks here? */ 1798 } 1799 1800 /* Check if it's an extended server ban */ 1801 if (is_extended_server_ban(mask)) 1802 { 1803 char *err; 1804 1805 if (!parse_extended_server_ban(mask, client, &err, 0, mask1buf, sizeof(mask1buf), mask2buf, sizeof(mask2buf))) 1806 { 1807 /* If adding, reject it */ 1808 if (add) 1809 { 1810 *error = err; 1811 return 0; 1812 } else 1813 { 1814 /* Always allow any removal attempt... */ 1815 char *p; 1816 char save; 1817 p = strchr(mask, ':'); 1818 p++; 1819 save = *p; 1820 *p = '\0'; 1821 strlcpy(mask1buf, mask, sizeof(mask1buf)); 1822 *p = save; 1823 strlcpy(mask2buf, p, sizeof(mask2buf)); 1824 /* fallthrough */ 1825 } 1826 } 1827 if (add && (t = eline_type_requires_ip(bantypes))) 1828 { 1829 snprintf(errbuf, sizeof(errbuf), 1830 "ERROR: Ban exception with type '%c' does not work on extended server bans. " 1831 "This is because checking for %s takes places BEFORE " 1832 "extended bans can be checked.", t->letter, t->log_name); 1833 *error = errbuf; 1834 return 0; 1835 } 1836 usermask = mask1buf; /* eg ~S: */ 1837 hostmask = mask2buf; /* eg 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef */ 1838 } else 1839 { 1840 /* Check if it's a hostmask and legal .. */ 1841 p = strchr(mask, '@'); 1842 if (p) { 1843 if ((p == mask) || !p[1]) 1844 { 1845 *error = "No user@host specified"; 1846 return 0; 1847 } 1848 usermask = strtok(mask, "@"); 1849 hostmask = strtok(NULL, ""); 1850 if (BadPtr(hostmask)) 1851 { 1852 if (BadPtr(usermask)) 1853 { 1854 *error = "Invalid mask"; 1855 return 0; 1856 } 1857 hostmask = usermask; 1858 usermask = "*"; 1859 } 1860 if (*hostmask == ':') 1861 { 1862 *error = "For technical reasons you cannot start the host with a ':', sorry"; 1863 return 0; 1864 } 1865 if (add && ((t = eline_type_requires_ip(bantypes)))) 1866 { 1867 /* Trying to exempt a user from a (G)ZLINE, 1868 * make sure the user isn't specifying a host then. 1869 */ 1870 if (strcmp(usermask, "*")) 1871 { 1872 snprintf(errbuf, sizeof(errbuf), 1873 "Ban exception with type '%c' need to be placed at \037*\037@ipmask, not \037user\037@ipmask. " 1874 "This is because checking %s takes places (possibly) BEFORE any dns and ident lookups.", 1875 t->letter, t->log_name); 1876 *error = errbuf; 1877 return 0; 1878 } 1879 for (p=hostmask; *p; p++) 1880 { 1881 if (isalpha(*p) && !isxdigit(*p)) 1882 { 1883 snprintf(errbuf, sizeof(errbuf), 1884 "Ban exception with type '%c' needs to be placed at *@\037ipmask\037, not *@\037hostmask\037. " 1885 "(so for example *@192.168.* is OK, but *@*.aol.com is not). " 1886 "This is because checking %s takes places (possibly) BEFORE any dns and ident lookups.", 1887 t->letter, t->log_name); 1888 *error = errbuf; 1889 return 0; 1890 } 1891 } 1892 } 1893 } 1894 else 1895 { 1896 /* It's seemingly a nick .. let's see if we can find the user */ 1897 Client *acptr; 1898 if ((acptr = find_user(mask, NULL))) 1899 { 1900 BanAction action = BAN_ACT_KLINE; // just a dummy default 1901 if (add && eline_type_requires_ip(bantypes)) 1902 action = BAN_ACT_ZLINE; // to indicate zline (no hostname, no dns, etc) 1903 ban_target_to_tkl_layer(iConf.manual_ban_target, action, acptr, (const char **)&usermask, (const char **)&hostmask); 1904 } 1905 else 1906 { 1907 *error = "Nickname not found"; 1908 return 0; 1909 } 1910 } 1911 } 1912 1913 /* Success! */ 1914 *usermask_out = usermask; 1915 *hostmask_out = hostmask; 1916 return 1; 1917 } 1918 1919 CMD_FUNC(cmd_eline) 1920 { 1921 time_t secs = 0; 1922 int add = 1; 1923 int soft = 0; 1924 const char *error = NULL; 1925 Client *acptr = NULL; 1926 char *mask = NULL; 1927 char mo[64], mo2[64]; 1928 char maskbuf[BUFSIZE]; 1929 char mask1buf[BUFSIZE]; 1930 char mask2buf[BUFSIZE]; 1931 const char *p, *bantypes=NULL, *reason=NULL; 1932 char *usermask, *hostmask; 1933 const char *tkllayer[11] = { 1934 me.name, /*0 server.name */ 1935 NULL, /*1 +|- */ 1936 NULL, /*2 E */ 1937 NULL, /*3 user */ 1938 NULL, /*4 host */ 1939 NULL, /*5 set_by */ 1940 "0", /*6 expire_at */ 1941 "-", /*7 set_at */ 1942 "-", /*8 ban types */ 1943 "-", /*9 reason */ 1944 NULL 1945 }; 1946 TKLTypeTable *t; 1947 1948 if (IsServer(client)) 1949 return; 1950 1951 if (!ValidatePermissionsForPath("server-ban:eline",client,NULL,NULL,NULL)) 1952 { 1953 sendnumeric(client, ERR_NOPRIVILEGES); 1954 return; 1955 } 1956 1957 /* For del we need at least: 1958 * ELINE -user@host 1959 * The 'add' case is checked later. 1960 */ 1961 if ((parc < 2) || BadPtr(parv[1])) 1962 { 1963 eline_syntax(client); 1964 return; 1965 } 1966 1967 strlcpy(maskbuf, parv[1], sizeof(maskbuf)); 1968 mask = maskbuf; 1969 if (*mask == '-') 1970 { 1971 add = 0; 1972 mask++; 1973 } 1974 else if (*mask == '+') 1975 { 1976 add = 1; 1977 mask++; 1978 } 1979 1980 /* For add we need more: 1981 * ELINE user@host bantypes expiry :reason 1982 */ 1983 if (add) 1984 { 1985 if ((parc < 5) || BadPtr(parv[4])) 1986 { 1987 eline_syntax(client); 1988 return; 1989 } 1990 bantypes = parv[2]; 1991 reason = parv[4]; 1992 } 1993 1994 if (!server_ban_exception_parse_mask(client, add, bantypes, mask, &usermask, &hostmask, &soft, &error)) 1995 { 1996 sendnotice(client, "[ERROR] %s", error); 1997 return; 1998 } 1999 2000 if (add) 2001 { 2002 secs = config_checkval(parv[3], CFG_TIME); 2003 if ((secs <= 0) && (*parv[3] != '0')) 2004 { 2005 sendnotice(client, "*** [error] The expiry time you specified is out of range!"); 2006 eline_syntax(client); 2007 return; 2008 } 2009 } 2010 2011 tkllayer[1] = add ? "+" : "-"; 2012 tkllayer[2] = "E"; 2013 tkllayer[3] = usermask; 2014 tkllayer[4] = hostmask; 2015 tkllayer[5] = make_nick_user_host(client->name, client->user->username, GetHost(client)); 2016 2017 if (add) 2018 { 2019 char c; 2020 /* Add ELINE */ 2021 if (secs == 0) 2022 ircsnprintf(mo, sizeof(mo), "%lld", (long long)secs); /* "0" */ 2023 else 2024 ircsnprintf(mo, sizeof(mo), "%lld", (long long)(secs + TStime())); 2025 ircsnprintf(mo2, sizeof(mo2), "%lld", (long long)TStime()); 2026 tkllayer[6] = mo; 2027 tkllayer[7] = mo2; 2028 tkllayer[8] = bantypes; 2029 if (contains_invalid_server_ban_exception_type(bantypes, &c)) 2030 { 2031 sendnotice(client, "ERROR: bantype '%c' is unrecognized (in '%s'). " 2032 "Note that the bantypes are case sensitive. " 2033 "Type /ELINE to see a list of all possible bantypes.", 2034 c, bantypes); 2035 return; 2036 } 2037 tkllayer[9] = reason; 2038 /* call the tkl layer .. */ 2039 cmd_tkl(&me, NULL, 10, tkllayer); 2040 } 2041 else 2042 { 2043 /* Remove ELINE */ 2044 /* call the tkl layer .. */ 2045 cmd_tkl(&me, NULL, 10, tkllayer); 2046 2047 } 2048 } 2049 2050 2051 /** Helper function for cmd_spamfilter, explaining usage. */ 2052 void spamfilter_usage(Client *client) 2053 { 2054 sendnotice(client, "Use: /spamfilter [add|del|remove|+|-] [-simple|-regex] [type] [action] [tkltime] [tklreason] [regex]"); 2055 sendnotice(client, "See '/helpop ?spamfilter' for more information."); 2056 sendnotice(client, "For an easy way to remove an existing spamfilter, use '/spamfilter del' without additional parameters"); 2057 } 2058 2059 /** Helper function for cmd_spamfilter, explaining usage has changed. */ 2060 void spamfilter_new_usage(Client *client, const char *parv[]) 2061 { 2062 sendnotice(client, "Unknown match-type '%s'. Must be one of: -regex (new fast PCRE regexes) or " 2063 "-simple (simple text with ? and * wildcards)", 2064 parv[2]); 2065 2066 if (*parv[2] != '-') 2067 sendnotice(client, "Using the old 3.2.x /SPAMFILTER syntax? Note the new -regex/-simple field!!"); 2068 2069 spamfilter_usage(client); 2070 } 2071 2072 /** Delete a spamfilter by ID (the ID can be obtained via '/SPAMFILTER del' */ 2073 void spamfilter_del_by_id(Client *client, const char *id) 2074 { 2075 int index; 2076 TKL *tk; 2077 int found = 0; 2078 char mo[32], mo2[32]; 2079 const char *tkllayer[13] = { 2080 me.name, /* 0 server.name */ 2081 NULL, /* 1 +|- */ 2082 "F", /* 2 F */ 2083 NULL, /* 3 usermask (targets) */ 2084 NULL, /* 4 hostmask (action) */ 2085 NULL, /* 5 set_by */ 2086 "0", /* 6 expire_at */ 2087 "0", /* 7 set_at */ 2088 "", /* 8 tkl time */ 2089 "", /* 9 tkl reason */ 2090 "", /* 10 match method */ 2091 "", /* 11 regex */ 2092 NULL 2093 }; 2094 2095 for (index = 0; index < TKLISTLEN; index++) 2096 { 2097 for (tk = tklines[index]; tk; tk = tk->next) 2098 { 2099 if (((tk->type & (TKL_GLOBAL|TKL_SPAMF)) == (TKL_GLOBAL|TKL_SPAMF)) && !strcmp(spamfilter_id(tk), id)) 2100 { 2101 found = 1; 2102 break; 2103 } 2104 } 2105 if (found) 2106 break; /* break outer loop */ 2107 } 2108 2109 if (!tk) 2110 { 2111 sendnotice(client, "Sorry, no spamfilter found with that ID. Did you run '/spamfilter del' to get the appropriate id?"); 2112 return; 2113 } 2114 2115 /* Spamfilter found. Now fill the tkllayer */ 2116 tkllayer[1] = "-"; 2117 tkllayer[3] = spamfilter_target_inttostring(tk->ptr.spamfilter->target); /* target(s) */ 2118 mo[0] = banact_valtochar(tk->ptr.spamfilter->action); 2119 mo[1] = '\0'; 2120 tkllayer[4] = mo; /* action */ 2121 tkllayer[5] = make_nick_user_host(client->name, client->user->username, GetHost(client)); 2122 tkllayer[8] = "-"; 2123 tkllayer[9] = "-"; 2124 tkllayer[10] = unreal_match_method_valtostr(tk->ptr.spamfilter->match->type); /* matching type */ 2125 tkllayer[11] = tk->ptr.spamfilter->match->str; /* regex */ 2126 ircsnprintf(mo2, sizeof(mo2), "%lld", (long long)TStime()); 2127 tkllayer[7] = mo2; /* deletion time */ 2128 2129 cmd_tkl(&me, NULL, 12, tkllayer); 2130 } 2131 2132 /** Spamfilter to fight spam, advertising, worms and other bad things on IRC. 2133 * See https://www.unrealircd.org/docs/Spamfilter for general documentation. 2134 * 2135 * /SPAMFILTER [add|del|remove|+|-] [match-type] [type] [action] [tkltime] [reason] [regex] 2136 * 1 2 3 4 5 6 7 2137 */ 2138 CMD_FUNC(cmd_spamfilter) 2139 { 2140 int add = 1; 2141 char mo[32], mo2[32]; 2142 const char *tkllayer[13] = { 2143 me.name, /* 0 server.name */ 2144 NULL, /* 1 +|- */ 2145 "F", /* 2 F */ 2146 NULL, /* 3 usermask (targets) */ 2147 NULL, /* 4 hostmask (action) */ 2148 NULL, /* 5 set_by */ 2149 "0", /* 6 expire_at */ 2150 "0", /* 7 set_at */ 2151 "", /* 8 tkl time */ 2152 "", /* 9 tkl reason */ 2153 "", /* 10 match method */ 2154 "", /* 11 regex */ 2155 NULL 2156 }; 2157 int targets = 0, action = 0; 2158 char targetbuf[64], actionbuf[2]; 2159 char reason[512]; 2160 int n; 2161 Match *m; 2162 int match_type = 0; 2163 char *err = NULL; 2164 2165 if (IsServer(client)) 2166 return; 2167 2168 if (!ValidatePermissionsForPath("server-ban:spamfilter",client,NULL,NULL,NULL)) 2169 { 2170 sendnumeric(client, ERR_NOPRIVILEGES); 2171 return; 2172 } 2173 2174 if (parc == 1) 2175 { 2176 const char *parv[3]; 2177 parv[0] = NULL; 2178 parv[1] = "spamfilter"; 2179 parv[2] = NULL; 2180 do_cmd(client, recv_mtags, "STATS", 2, parv); 2181 return; 2182 } 2183 2184 if ((parc <= 3) && !strcmp(parv[1], "del")) 2185 { 2186 if (!parv[2]) 2187 { 2188 /* Show STATS with appropriate SPAMFILTER del command */ 2189 const char *parv[5]; 2190 parv[0] = NULL; 2191 parv[1] = "spamfilter"; 2192 parv[2] = me.name; 2193 parv[3] = "del"; 2194 parv[4] = NULL; 2195 do_cmd(client, recv_mtags, "STATS", 4, parv); 2196 return; 2197 } 2198 spamfilter_del_by_id(client, parv[2]); 2199 return; 2200 } 2201 2202 if ((parc == 7) && (*parv[2] != '-')) 2203 { 2204 spamfilter_new_usage(client,parv); 2205 return; 2206 } 2207 2208 if ((parc < 8) || BadPtr(parv[7])) 2209 { 2210 spamfilter_usage(client); 2211 return; 2212 } 2213 2214 /* parv[1]: [add|del|+|-] 2215 * parv[2]: match-type 2216 * parv[3]: type 2217 * parv[4]: action 2218 * parv[5]: tkl time 2219 * parv[6]: tkl reason (or block reason..) 2220 * parv[7]: regex 2221 */ 2222 if (!strcasecmp(parv[1], "add") || !strcmp(parv[1], "+")) 2223 add = 1; 2224 else if (!strcasecmp(parv[1], "del") || !strcmp(parv[1], "-") || !strcasecmp(parv[1], "remove")) 2225 add = 0; 2226 else 2227 { 2228 sendnotice(client, "1st parameter invalid"); 2229 spamfilter_usage(client); 2230 return; 2231 } 2232 2233 if (add && !strcasecmp(parv[2]+1, "posix")) 2234 { 2235 sendnotice(client, "ERROR: Spamfilter type 'posix' is DEPRECATED. You must use type 'regex' instead."); 2236 sendnotice(client, "See https://www.unrealircd.org/docs/FAQ#spamfilter-posix-deprecated"); 2237 return; 2238 } 2239 2240 match_type = unreal_match_method_strtoval(parv[2]+1); 2241 if (!match_type) 2242 { 2243 spamfilter_new_usage(client, parv); 2244 return; 2245 } 2246 2247 targets = spamfilter_gettargets(parv[3], client); 2248 if (!targets) 2249 { 2250 spamfilter_usage(client); 2251 return; 2252 } 2253 2254 strlcpy(targetbuf, spamfilter_target_inttostring(targets), sizeof(targetbuf)); 2255 2256 action = banact_stringtoval(parv[4]); 2257 if (!action) 2258 { 2259 sendnotice(client, "Invalid 'action' field (%s)", parv[4]); 2260 spamfilter_usage(client); 2261 return; 2262 } 2263 actionbuf[0] = banact_valtochar(action); 2264 actionbuf[1] = '\0'; 2265 2266 if (add) 2267 { 2268 /* now check the regex / match field... */ 2269 m = unreal_create_match(match_type, parv[7], &err); 2270 if (!m) 2271 { 2272 sendnotice(client, "Error in regex '%s': %s", parv[7], err); 2273 return; 2274 } 2275 unreal_delete_match(m); 2276 } 2277 2278 tkllayer[1] = add ? "+" : "-"; 2279 tkllayer[3] = targetbuf; 2280 tkllayer[4] = actionbuf; 2281 tkllayer[5] = make_nick_user_host(client->name, client->user->username, GetHost(client)); 2282 2283 if (parv[5][0] == '-') 2284 { 2285 ircsnprintf(mo, sizeof(mo), "%lld", (long long)SPAMFILTER_BAN_TIME); 2286 tkllayer[8] = mo; 2287 } 2288 else 2289 tkllayer[8] = parv[5]; 2290 2291 if (parv[6][0] == '-') 2292 strlcpy(reason, unreal_encodespace(SPAMFILTER_BAN_REASON), sizeof(reason)); 2293 else 2294 strlcpy(reason, parv[6], sizeof(reason)); 2295 2296 tkllayer[9] = reason; 2297 tkllayer[10] = parv[2]+1; /* +1 to skip the '-' */ 2298 tkllayer[11] = parv[7]; 2299 2300 /* SPAMFILTER LENGTH CHECK. 2301 * We try to limit it here so '/stats f' output shows ok, output of that is: 2302 * :servername 229 destname F <target> <action> <num> <num> <num> <reason> <set_by> :<regex> 2303 * : ^NICKLEN ^ NICKLEN ^check ^check ^check 2304 * And for the other fields (and spacing/etc) we count on max 40 characters. 2305 * We also do >500 instead of >510, since that looks cleaner ;).. so actually we count 2306 * on 50 characters for the rest... -- Syzop 2307 */ 2308 n = strlen(reason) + strlen(parv[7]) + strlen(tkllayer[6]) + (NICKLEN * 2) + 40; 2309 if ((n > 500) && add) 2310 { 2311 sendnotice(client, "Sorry, spamfilter too long. You'll either have to trim down the " 2312 "reason or the regex (exceeded by %d bytes)", n - 500); 2313 return; 2314 } 2315 2316 if (add) 2317 { 2318 ircsnprintf(mo2, sizeof(mo2), "%lld", (long long)TStime()); 2319 tkllayer[7] = mo2; 2320 } 2321 2322 cmd_tkl(&me, NULL, 12, tkllayer); 2323 } 2324 2325 /** tkl hash method. 2326 * @param c The tkl type character, see tkl_typetochar(). 2327 * @note The input value 'c' is assumed to be in range a-z or A-Z! 2328 * Also, don't blindly change the hashmethod here, some things 2329 * depend on 'z' and 'Z' ending up in the same bucket. 2330 */ 2331 int _tkl_hash(unsigned int c) 2332 { 2333 #ifdef DEBUGMODE 2334 if ((c >= 'a') && (c <= 'z')) 2335 return c-'a'; 2336 else if ((c >= 'A') && (c <= 'Z')) 2337 return c-'A'; 2338 else { 2339 unreal_log(ULOG_ERROR, "bug", "TKL_HASH_INVALID", NULL, 2340 "tkl_hash() called with out of range parameter (c = '$tkl_char') !!!", 2341 log_data_char("tkl_char", c)); 2342 return 0; 2343 } 2344 #else 2345 return (isupper(c) ? c-'A' : c-'a'); 2346 #endif 2347 } 2348 2349 /** tkl type to tkl character. 2350 * NOTE: type is assumed to be valid. 2351 */ 2352 char _tkl_typetochar(int type) 2353 { 2354 int i; 2355 for (i=0; tkl_types[i].config_name; i++) 2356 if ((tkl_types[i].type == type) && tkl_types[i].tkltype) 2357 return tkl_types[i].letter; 2358 unreal_log(ULOG_ERROR, "bug", "TKL_TYPETOCHAR_INVALID", NULL, 2359 "tkl_typetochar(): unknown type $tkl_type!!!", 2360 log_data_integer("tkl_type", type)); 2361 return 0; 2362 } 2363 2364 /** tkl character to tkl type 2365 * Returns 0 if invalid type. 2366 */ 2367 int _tkl_chartotype(char c) 2368 { 2369 int i; 2370 for (i=0; tkl_types[i].config_name; i++) 2371 if ((tkl_types[i].letter == c) && tkl_types[i].tkltype) 2372 return tkl_types[i].type; 2373 return 0; 2374 } 2375 2376 char _tkl_configtypetochar(const char *name) 2377 { 2378 int i; 2379 for (i=0; tkl_types[i].config_name; i++) 2380 if (!strcmp(tkl_types[i].config_name, name)) 2381 return tkl_types[i].letter; 2382 return 0; 2383 } 2384 2385 int tkl_banexception_chartotype(char c) 2386 { 2387 int i; 2388 for (i=0; tkl_types[i].config_name; i++) 2389 if ((tkl_types[i].letter == c) && tkl_types[i].exceptiontype) 2390 return tkl_types[i].type; 2391 return 0; 2392 } 2393 2394 char *tkl_banexception_configname_to_chars(char *name) 2395 { 2396 static char buf[128]; 2397 int i; 2398 2399 if (!strcasecmp(name, "all")) 2400 { 2401 /* 'all' means everything except qline: */ 2402 char *p = buf; 2403 for (i=0; tkl_types[i].config_name; i++) 2404 { 2405 if (tkl_types[i].exceptiontype && !(tkl_types[i].type & TKL_NAME)) 2406 *p++ = tkl_types[i].letter; 2407 } 2408 *p = '\0'; 2409 return buf; 2410 } 2411 2412 for (i=0; tkl_types[i].config_name; i++) 2413 { 2414 if (!strcasecmp(name, tkl_types[i].config_name) && tkl_types[i].exceptiontype) 2415 { 2416 buf[0] = tkl_types[i].letter; 2417 buf[1] = '\0'; 2418 return buf; 2419 } 2420 } 2421 return NULL; 2422 } 2423 2424 /** Show TKL type as a string (used when adding/removing) */ 2425 char *_tkl_type_string(TKL *tkl) 2426 { 2427 static char txt[256]; 2428 int i; 2429 2430 *txt = '\0'; 2431 2432 if (TKLIsServerBan(tkl) && (tkl->ptr.serverban->subtype == TKL_SUBTYPE_SOFT)) 2433 strlcpy(txt, "Soft ", sizeof(txt)); 2434 2435 for (i=0; tkl_types[i].config_name; i++) 2436 { 2437 if ((tkl_types[i].type == tkl->type) && tkl_types[i].tkltype) 2438 { 2439 strlcat(txt, tkl_types[i].log_name, sizeof(txt)); 2440 return txt; 2441 } 2442 } 2443 2444 strlcpy(txt, "Unknown *-Line", sizeof(txt)); 2445 return txt; 2446 } 2447 2448 /** Short config string, lowercase alnum with possibly hyphens (eg: 'kline') */ 2449 char *_tkl_type_config_string(TKL *tkl) 2450 { 2451 int i; 2452 2453 for (i=0; tkl_types[i].config_name; i++) 2454 if ((tkl_types[i].type == tkl->type) && tkl_types[i].tkltype) 2455 return tkl_types[i].config_name; 2456 2457 return "???"; 2458 } 2459 2460 int tkl_banexception_matches_type(TKL *except, int bantype) 2461 { 2462 char *p; 2463 int extype; 2464 2465 if (!TKLIsBanException(except)) 2466 abort(); 2467 2468 for (p = except->ptr.banexception->bantypes; *p; p++) 2469 { 2470 extype = tkl_banexception_chartotype(*p); 2471 if ((extype & TKL_SPAMF) || (extype & TKL_SHUN) || (extype & TKL_NAME)) 2472 { 2473 /* For spamfilter, shun and qline we don't care 2474 * whether they are global or not. That would only 2475 * be confusing to the admin. 2476 */ 2477 extype &= ~TKL_GLOBAL; 2478 if (bantype & extype) 2479 return 1; 2480 } else { 2481 /* Rest requires an exact match */ 2482 if (bantype == extype) 2483 return 1; 2484 } 2485 } 2486 2487 return 0; 2488 } 2489 2490 /** Used for finding out which element of the tkl_ip hash table is used (primary element) */ 2491 int _tkl_ip_hash(char *ip) 2492 { 2493 char ipbuf[64], *p; 2494 2495 for (p = ip; *p; p++) 2496 { 2497 if ((*p == '?') || (*p == '*') || (*p == '/')) 2498 return -1; /* not an entry suitable for the ip hash table */ 2499 } 2500 if (inet_pton(AF_INET, ip, &ipbuf) == 1) 2501 { 2502 /* IPv4 */ 2503 unsigned int v = (ipbuf[0] << 24) + 2504 (ipbuf[1] << 16) + 2505 (ipbuf[2] << 8) + 2506 ipbuf[3]; 2507 return v % TKLIPHASHLEN2; 2508 } else 2509 if (inet_pton(AF_INET6, ip, &ipbuf) == 1) 2510 { 2511 /* IPv6 (only upper 64 bits) */ 2512 unsigned int v1 = (ipbuf[0] << 24) + 2513 (ipbuf[1] << 16) + 2514 (ipbuf[2] << 8) + 2515 ipbuf[3]; 2516 unsigned int v2 = (ipbuf[4] << 24) + 2517 (ipbuf[5] << 16) + 2518 (ipbuf[6] << 8) + 2519 ipbuf[7]; 2520 return (v1 ^ v2) % TKLIPHASHLEN2; 2521 } else 2522 { 2523 return -1; 2524 } 2525 } 2526 2527 // TODO: consider efunc 2528 int tkl_ip_hash_tkl(TKL *tkl) 2529 { 2530 if (TKLIsServerBan(tkl)) 2531 return tkl_ip_hash(tkl->ptr.serverban->hostmask); 2532 if (TKLIsBanException(tkl)) 2533 return tkl_ip_hash(tkl->ptr.banexception->hostmask); 2534 return -1; 2535 } 2536 2537 /** Used for finding out which tkl_ip hash table needs to be used (secondary element). 2538 * NOTE: Returns -1 for types that are never on the TKL ip hash table, such as spamfilter. 2539 * This can be used by the caller as a quick way to find out if the type is supported. 2540 */ 2541 int _tkl_ip_hash_type(int type) 2542 { 2543 if ((type == 'Z') || (type == 'z')) 2544 return 0; 2545 else if (type == 'G') 2546 return 1; 2547 else if (type == 'k') 2548 return 2; 2549 else if ((type == 'e') || (type == 'E')) 2550 return 3; 2551 else 2552 return -1; 2553 } 2554 2555 /* Find the appropriate list 'head' that we need to iterate. 2556 * This is simply a helper that is used at 3 places and I hate duplicate code. 2557 * NOTE: this function may return NULL. 2558 */ 2559 TKL *tkl_find_head(char type, char *hostmask, TKL *def) 2560 { 2561 int index, index2; 2562 2563 /* First, check ip hash table TKL's... */ 2564 index = tkl_ip_hash_type(type); 2565 if (index >= 0) 2566 { 2567 index2 = tkl_ip_hash(hostmask); 2568 if (index2 >= 0) 2569 { 2570 /* iterate tklines_ip_hash[index][index2] */ 2571 return tklines_ip_hash[index][index2]; 2572 } 2573 } 2574 /* Fallback to the default */ 2575 return def; 2576 } 2577 2578 /** Add a spamfilter entry to the list. 2579 * @param type TKL_SPAMF or TKL_SPAMF|TKL_GLOBAL. 2580 * @param target The spamfilter target (SPAMF_*) 2581 * @param action The spamfilter action (BAN_ACT_*) 2582 * @param match The match (this struct may contain a regex for example) 2583 * @param set_by Who (or what) set the ban 2584 * @param expire_at When will the ban expire (0 for permanent) 2585 * @param set_at When was the ban set 2586 * @param spamf_tkl_duration When will the ban placed by spamfilter expire 2587 * @param spamf_tkl_reason What is the reason for bans placed by spamfilter 2588 * @param flags Any TKL_FLAG_* (TKL_FLAG_CONFIG, etc..) 2589 * @returns The TKL entry, or NULL in case of a problem, 2590 * such as a regex failing to compile, memory problem, .. 2591 */ 2592 TKL *_tkl_add_spamfilter(int type, unsigned short target, BanAction action, Match *match, char *set_by, 2593 time_t expire_at, time_t set_at, 2594 time_t tkl_duration, char *tkl_reason, 2595 int flags) 2596 { 2597 TKL *tkl; 2598 int index; 2599 2600 if (!(type & TKL_SPAMF)) 2601 abort(); 2602 2603 tkl = safe_alloc(sizeof(TKL)); 2604 /* First the common fields */ 2605 tkl->type = type; 2606 tkl->flags = flags; 2607 tkl->set_at = set_at; 2608 safe_strdup(tkl->set_by, set_by); 2609 tkl->expire_at = expire_at; 2610 /* Then the spamfilter fields */ 2611 tkl->ptr.spamfilter = safe_alloc(sizeof(Spamfilter)); 2612 tkl->ptr.spamfilter->target = target; 2613 tkl->ptr.spamfilter->action = action; 2614 tkl->ptr.spamfilter->match = match; 2615 safe_strdup(tkl->ptr.spamfilter->tkl_reason, tkl_reason); 2616 tkl->ptr.spamfilter->tkl_duration = tkl_duration; 2617 2618 if (tkl->ptr.spamfilter->target & SPAMF_USER) 2619 loop.do_bancheck_spamf_user = 1; 2620 if (tkl->ptr.spamfilter->target & SPAMF_AWAY) 2621 loop.do_bancheck_spamf_away = 1; 2622 2623 /* Spamfilters go via the normal TKL list... */ 2624 index = tkl_hash(tkl_typetochar(type)); 2625 AddListItem(tkl, tklines[index]); 2626 2627 if (target & SPAMF_MTAG) 2628 mtag_spamfilters_present = 1; 2629 2630 return tkl; 2631 } 2632 2633 /** Add a server ban TKL entry. 2634 * @param type The TKL type, one of TKL_*, 2635 * optionally OR'ed with TKL_GLOBAL. 2636 * @param usermask The user mask 2637 * @param hostmask The host mask 2638 * @param reason The reason for the ban 2639 * @param set_by Who (or what) set the ban 2640 * @param expire_at When will the ban expire (0 for permanent) 2641 * @param set_at When was the ban set 2642 * @param soft Whether it's a soft-ban 2643 * @param flags Any TKL_FLAG_* (TKL_FLAG_CONFIG, etc..) 2644 * @returns The TKL entry, or NULL in case of a problem, 2645 * such as a regex failing to compile, memory problem, .. 2646 * @note 2647 * Be sure not to call this function for spamfilters, 2648 * qlines or exempts, which have their own function! 2649 */ 2650 TKL *_tkl_add_serverban(int type, char *usermask, char *hostmask, char *reason, char *set_by, 2651 time_t expire_at, time_t set_at, int soft, int flags) 2652 { 2653 TKL *tkl; 2654 int index, index2; 2655 2656 if (!TKLIsServerBanType(type)) 2657 abort(); 2658 2659 tkl = safe_alloc(sizeof(TKL)); 2660 /* First the common fields */ 2661 tkl->type = type; 2662 tkl->flags = flags; 2663 tkl->set_at = set_at; 2664 safe_strdup(tkl->set_by, set_by); 2665 tkl->expire_at = expire_at; 2666 /* Now the server ban fields */ 2667 tkl->ptr.serverban = safe_alloc(sizeof(ServerBan)); 2668 safe_strdup(tkl->ptr.serverban->usermask, usermask); 2669 safe_strdup(tkl->ptr.serverban->hostmask, hostmask); 2670 if (soft) 2671 tkl->ptr.serverban->subtype = TKL_SUBTYPE_SOFT; 2672 safe_strdup(tkl->ptr.serverban->reason, reason); 2673 2674 /* For ip hash table TKL's... */ 2675 index = tkl_ip_hash_type(tkl_typetochar(type)); 2676 if (index >= 0) 2677 { 2678 index2 = tkl_ip_hash_tkl(tkl); 2679 if (index2 >= 0) 2680 { 2681 AddListItem(tkl, tklines_ip_hash[index][index2]); 2682 return tkl; 2683 } 2684 } 2685 2686 /* If we get here it's just for our normal list.. */ 2687 index = tkl_hash(tkl_typetochar(type)); 2688 AddListItem(tkl, tklines[index]); 2689 2690 return tkl; 2691 } 2692 2693 /** Add a ban exception TKL entry. 2694 * @param type TKL_EXCEPTION or TKLEXCEPT|TKL_GLOBAL. 2695 * @param usermask The user mask 2696 * @param hostmask The host mask 2697 * @param match A securitygroup used for matching (can be NULL, 2698 * if not NULL then this field is used as-is and not copied 2699 * so caller should not free!) 2700 * @param reason The reason for the ban 2701 * @param set_by Who (or what) set the ban 2702 * @param expire_at When will the ban expire (0 for permanent) 2703 * @param set_at When was the ban set 2704 * @param soft Whether it's a soft-ban 2705 * @param bantypes The ban types to exempt from 2706 * @param flags Any TKL_FLAG_* (TKL_FLAG_CONFIG, etc..) 2707 * @returns The TKL entry, or NULL in case of a problem, 2708 * such as a regex failing to compile, memory problem, .. 2709 * @note 2710 * Be sure not to call this function for spamfilters, 2711 * qlines or exempts, which have their own function! 2712 */ 2713 TKL *_tkl_add_banexception(int type, char *usermask, char *hostmask, SecurityGroup *match, 2714 char *reason, char *set_by, 2715 time_t expire_at, time_t set_at, int soft, char *bantypes, int flags) 2716 { 2717 TKL *tkl; 2718 int index, index2; 2719 2720 if (!TKLIsBanExceptionType(type)) 2721 abort(); 2722 tkl = safe_alloc(sizeof(TKL)); 2723 /* First the common fields */ 2724 tkl->type = type; 2725 tkl->flags = flags; 2726 tkl->set_at = set_at; 2727 safe_strdup(tkl->set_by, set_by); 2728 tkl->expire_at = expire_at; 2729 /* Now the ban except fields */ 2730 tkl->ptr.banexception = safe_alloc(sizeof(BanException)); 2731 safe_strdup(tkl->ptr.banexception->usermask, usermask); 2732 safe_strdup(tkl->ptr.banexception->hostmask, hostmask); 2733 tkl->ptr.banexception->match = match; 2734 if (soft) 2735 tkl->ptr.banexception->subtype = TKL_SUBTYPE_SOFT; 2736 safe_strdup(tkl->ptr.banexception->bantypes, bantypes); 2737 safe_strdup(tkl->ptr.banexception->reason, reason); 2738 2739 /* For ip hash table TKL's... */ 2740 index = tkl_ip_hash_type(tkl_typetochar(type)); 2741 if (index >= 0) 2742 { 2743 index2 = tkl_ip_hash_tkl(tkl); 2744 if (index2 >= 0) 2745 { 2746 AddListItem(tkl, tklines_ip_hash[index][index2]); 2747 return tkl; 2748 } 2749 } 2750 2751 /* If we get here it's just for our normal list.. */ 2752 index = tkl_hash(tkl_typetochar(type)); 2753 AddListItem(tkl, tklines[index]); 2754 2755 return tkl; 2756 } 2757 2758 /** Add a name ban TKL entry (Q-Line), used for banning nicks and channels. 2759 * @param type The TKL type, one of TKL_*, 2760 * optionally OR'ed with TKL_GLOBAL. 2761 * @param name The nick or channel to be banned (wildcards accepted) 2762 * @param hold Flag to indicate services hold 2763 * @param reason The reason for the ban 2764 * @param set_by Who (or what) set the ban 2765 * @param expire_at When will the ban expire (0 for permanent) 2766 * @param set_at When was the ban set 2767 * @param flags Any TKL_FLAG_* (TKL_FLAG_CONFIG, etc..) 2768 * @returns The TKL entry, or NULL in case of a problem, 2769 * such as a regex failing to compile, memory problem, .. 2770 * @note 2771 * Be sure not to call this function for spamfilters, 2772 * qlines or exempts, which have their own function! 2773 */ 2774 TKL *_tkl_add_nameban(int type, char *name, int hold, char *reason, char *set_by, 2775 time_t expire_at, time_t set_at, int flags) 2776 { 2777 TKL *tkl; 2778 int index; 2779 2780 if (!TKLIsNameBanType(type)) 2781 abort(); 2782 2783 tkl = safe_alloc(sizeof(TKL)); 2784 /* First the common fields */ 2785 tkl->type = type; 2786 tkl->flags = flags; 2787 tkl->set_at = set_at; 2788 safe_strdup(tkl->set_by, set_by); 2789 tkl->expire_at = expire_at; 2790 /* Now the name ban fields */ 2791 tkl->ptr.nameban = safe_alloc(sizeof(ServerBan)); 2792 safe_strdup(tkl->ptr.nameban->name, name); 2793 tkl->ptr.nameban->hold = hold; 2794 safe_strdup(tkl->ptr.nameban->reason, reason); 2795 2796 /* Name bans go via the normal TKL list.. */ 2797 index = tkl_hash(tkl_typetochar(type)); 2798 AddListItem(tkl, tklines[index]); 2799 2800 return tkl; 2801 } 2802 2803 2804 /** Free a TKL entry but do not remove from the list. 2805 * (this assumes that it was not added yet or is already removed) 2806 * Most people will use tkl_del_line() instead. 2807 */ 2808 void _free_tkl(TKL *tkl) 2809 { 2810 /* Free the entry */ 2811 /* First, the common fields */ 2812 safe_free(tkl->set_by); 2813 /* Now the type specific fields */ 2814 if (TKLIsServerBan(tkl) && tkl->ptr.serverban) 2815 { 2816 safe_free(tkl->ptr.serverban->usermask); 2817 safe_free(tkl->ptr.serverban->hostmask); 2818 safe_free(tkl->ptr.serverban->reason); 2819 safe_free(tkl->ptr.serverban); 2820 } else 2821 if (TKLIsNameBan(tkl) && tkl->ptr.nameban) 2822 { 2823 safe_free(tkl->ptr.nameban->name); 2824 safe_free(tkl->ptr.nameban->reason); 2825 safe_free(tkl->ptr.nameban); 2826 } else 2827 if (TKLIsSpamfilter(tkl) && tkl->ptr.spamfilter) 2828 { 2829 /* Spamfilter */ 2830 safe_free(tkl->ptr.spamfilter->tkl_reason); 2831 if (tkl->ptr.spamfilter->match) 2832 unreal_delete_match(tkl->ptr.spamfilter->match); 2833 safe_free(tkl->ptr.spamfilter); 2834 } else 2835 if (TKLIsBanException(tkl) && tkl->ptr.banexception) 2836 { 2837 safe_free(tkl->ptr.banexception->usermask); 2838 safe_free(tkl->ptr.banexception->hostmask); 2839 if (tkl->ptr.banexception->match) 2840 free_security_group(tkl->ptr.banexception->match); 2841 safe_free(tkl->ptr.banexception->bantypes); 2842 safe_free(tkl->ptr.banexception->reason); 2843 safe_free(tkl->ptr.banexception); 2844 } 2845 safe_free(tkl); 2846 } 2847 2848 /** Delete a TKL entry from the list and free it. 2849 * @param tkl The TKL entry. 2850 */ 2851 void _tkl_del_line(TKL *tkl) 2852 { 2853 int index, index2; 2854 int found = 0; 2855 2856 /* Try to find it in the ip TKL hash table first 2857 * (this only applies to server bans) 2858 */ 2859 index = tkl_ip_hash_type(tkl_typetochar(tkl->type)); 2860 if (index >= 0) 2861 { 2862 index2 = tkl_ip_hash_tkl(tkl); 2863 if (index2 >= 0) 2864 { 2865 #if 1 2866 /* Temporary validation until an rmtkl(?) bug is fixed */ 2867 TKL *d; 2868 int really_found = 0; 2869 for (d = tklines_ip_hash[index][index2]; d; d = d->next) 2870 if (d == tkl) 2871 { 2872 really_found = 1; 2873 break; 2874 } 2875 if (!really_found) 2876 { 2877 unreal_log(ULOG_FATAL, "tkl", "BUG_TKL_DEL_LINE_HASH", NULL, 2878 "[BUG] [Crash] tkl_del_line() for $tkl (type: $tkl.type_string): " 2879 "NOT found in tklines_ip_hash. This should never happen!", 2880 log_data_tkl("tkl", tkl)); 2881 abort(); 2882 } 2883 #endif 2884 DelListItem(tkl, tklines_ip_hash[index][index2]); 2885 found = 1; 2886 } 2887 } 2888 2889 if (!found) 2890 { 2891 /* If we get here it's just for our normal list.. */ 2892 index = tkl_hash(tkl_typetochar(tkl->type)); 2893 DelListItem(tkl, tklines[index]); 2894 } 2895 2896 /* Finally, free the entry */ 2897 free_tkl(tkl); 2898 check_mtag_spamfilters_present(); 2899 } 2900 2901 /** Add some default ban exceptions - for localhost */ 2902 static void add_default_exempts(void) 2903 { 2904 /* The exempted ban types are only ones that will affect other connections as well, 2905 * such as gline, and not policy decissions such as maxperip exempt or bypass qlines. 2906 * Currently the list is: gline, kline, gzline, zline, shun, blacklist, 2907 * connect-flood, handshake-data-flood. 2908 */ 2909 tkl_add_banexception(TKL_EXCEPTION, "*", "127.0.0.1", NULL, "localhost is always exempt", 2910 "-default-", 0, TStime(), 0, "GkZzsbcd", TKL_FLAG_CONFIG); 2911 tkl_add_banexception(TKL_EXCEPTION, "*", "::1", NULL, "localhost is always exempt", 2912 "-default-", 0, TStime(), 0, "GkZzsbcd", TKL_FLAG_CONFIG); 2913 } 2914 2915 /* 2916 * tkl_check_local_remove_shun: 2917 * removes shun from currently connected users affected by tmp. 2918 */ 2919 // TODO / FIXME: audit this function, it looks crazy 2920 void _tkl_check_local_remove_shun(TKL *tmp) 2921 { 2922 long i; 2923 char *chost, *cname, *cip; 2924 int is_ip; 2925 Client *client; 2926 2927 TKL *tk; 2928 int keep_shun; 2929 2930 for (i = 0; i <= 5; i++) 2931 { 2932 list_for_each_entry(client, &lclient_list, lclient_node) 2933 if (MyUser(client) && IsShunned(client)) 2934 { 2935 chost = client->local->sockhost; 2936 cname = client->user->username; 2937 2938 cip = GetIP(client); 2939 2940 if ((*tmp->ptr.serverban->hostmask >= '0') && (*tmp->ptr.serverban->hostmask <= '9')) 2941 is_ip = 1; 2942 else 2943 is_ip = 0; 2944 2945 if (is_ip == 0 ? 2946 (match_simple(tmp->ptr.serverban->hostmask, chost) && match_simple(tmp->ptr.serverban->usermask, cname)) : 2947 (match_simple(tmp->ptr.serverban->hostmask, chost) || match_simple(tmp->ptr.serverban->hostmask, cip)) 2948 && match_simple(tmp->ptr.serverban->usermask, cname)) 2949 { 2950 /* 2951 before blindly marking this user as un-shunned, we need to check 2952 if the user is under any other existing shuns. (#0003906) 2953 Unfortunately, this requires crazy amounts of indentation ;-). 2954 2955 This enumeration code is based off of _tkl_stats() 2956 */ 2957 keep_shun = 0; 2958 for(tk = tklines[tkl_hash('s')]; tk && !keep_shun; tk = tk->next) 2959 if (tk != tmp && match_simple(tk->ptr.serverban->usermask, cname)) 2960 { 2961 if ((*tk->ptr.serverban->hostmask >= '0') && (*tk->ptr.serverban->hostmask <= '9') 2962 /* the hostmask is an IP */ 2963 && (match_simple(tk->ptr.serverban->hostmask, chost) || match_simple(tk->ptr.serverban->hostmask, cip))) 2964 keep_shun = 1; 2965 else 2966 /* the hostmask is not an IP */ 2967 if (match_simple(tk->ptr.serverban->hostmask, chost) && match_simple(tk->ptr.serverban->usermask, cname)) 2968 keep_shun = 1; 2969 } 2970 2971 if (!keep_shun) 2972 { 2973 ClearShunned(client); 2974 } 2975 } 2976 } 2977 } 2978 } 2979 2980 2981 /** This returns something like user@host, or %user@host, or ~a:Trusted 2982 * that can be used in oper notices like expiring kline, added kline, etc. 2983 */ 2984 #define NO_SOFT_PREFIX 1 2985 char *_tkl_uhost(TKL *tkl, char *buf, size_t buflen, int options) 2986 { 2987 if (TKLIsServerBan(tkl)) 2988 { 2989 if (is_extended_server_ban(tkl->ptr.serverban->usermask)) 2990 { 2991 ircsnprintf(buf, buflen, "%s%s%s", 2992 (!(options & NO_SOFT_PREFIX) && (tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT)) ? "%" : "", 2993 tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask); 2994 } else { 2995 ircsnprintf(buf, buflen, "%s%s@%s", 2996 (!(options & NO_SOFT_PREFIX) && (tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT)) ? "%" : "", 2997 tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask); 2998 } 2999 } else 3000 if (TKLIsBanException(tkl)) 3001 { 3002 if (is_extended_server_ban(tkl->ptr.banexception->usermask)) 3003 { 3004 ircsnprintf(buf, buflen, "%s%s%s", 3005 (!(options & NO_SOFT_PREFIX) && (tkl->ptr.banexception->subtype & TKL_SUBTYPE_SOFT)) ? "%" : "", 3006 tkl->ptr.banexception->usermask, tkl->ptr.banexception->hostmask); 3007 } else { 3008 ircsnprintf(buf, buflen, "%s%s@%s", 3009 (!(options & NO_SOFT_PREFIX) && (tkl->ptr.banexception->subtype & TKL_SUBTYPE_SOFT)) ? "%" : "", 3010 tkl->ptr.banexception->usermask, tkl->ptr.banexception->hostmask); 3011 } 3012 } else 3013 abort(); 3014 3015 return buf; 3016 } 3017 3018 /** Deal with expiration of a specific TKL entry. 3019 * This is a helper function for tkl_check_expire(). 3020 */ 3021 void tkl_expire_entry(TKL *tkl) 3022 { 3023 if (TKLIsServerBan(tkl)) 3024 { 3025 unreal_log(ULOG_INFO, "tkl", "TKL_EXPIRE", NULL, 3026 "Expiring $tkl.type_string '$tkl' [reason: $tkl.reason] [by: $tkl.set_by] [duration: $tkl.duration_string]", 3027 log_data_tkl("tkl", tkl)); 3028 } 3029 else if (TKLIsNameBan(tkl)) 3030 { 3031 if (!tkl->ptr.nameban->hold) 3032 { 3033 unreal_log(ULOG_INFO, "tkl", "TKL_EXPIRE", NULL, 3034 "Expiring $tkl.type_string '$tkl' [reason: $tkl.reason] [by: $tkl.set_by] [duration: $tkl.duration_string]", 3035 log_data_tkl("tkl", tkl)); 3036 } 3037 } 3038 else if (TKLIsBanException(tkl)) 3039 { 3040 unreal_log(ULOG_INFO, "tkl", "TKL_EXPIRE", NULL, 3041 "Expiring $tkl.type_string '$tkl' [type: $tkl.exception_types] [reason: $tkl.reason] [by: $tkl.set_by] [duration: $tkl.duration_string]", 3042 log_data_tkl("tkl", tkl)); 3043 } 3044 3045 // FIXME: so.. this isn't logged? or what? 3046 if (tkl->type & TKL_SHUN) 3047 tkl_check_local_remove_shun(tkl); 3048 3049 RunHook(HOOKTYPE_TKL_DEL, NULL, tkl); 3050 tkl_del_line(tkl); 3051 } 3052 3053 /** Regularly check TKL entries for expiration */ 3054 EVENT(tkl_check_expire) 3055 { 3056 TKL *tkl, *next; 3057 time_t nowtime; 3058 int index, index2; 3059 3060 nowtime = TStime(); 3061 3062 /* First, hashed entries.. */ 3063 for (index = 0; index < TKLIPHASHLEN1; index++) 3064 { 3065 for (index2 = 0; index2 < TKLIPHASHLEN2; index2++) 3066 { 3067 for (tkl = tklines_ip_hash[index][index2]; tkl; tkl = next) 3068 { 3069 next = tkl->next; 3070 if (tkl->expire_at <= nowtime && !(tkl->expire_at == 0)) 3071 { 3072 tkl_expire_entry(tkl); 3073 } 3074 } 3075 } 3076 } 3077 3078 /* Now normal entries.. */ 3079 for (index = 0; index < TKLISTLEN; index++) 3080 { 3081 for (tkl = tklines[index]; tkl; tkl = next) 3082 { 3083 next = tkl->next; 3084 if (tkl->expire_at <= nowtime && !(tkl->expire_at == 0)) 3085 { 3086 tkl_expire_entry(tkl); 3087 } 3088 } 3089 } 3090 } 3091 3092 /* This is just a helper function for find_tkl_exception() */ 3093 static int find_tkl_exception_matcher(Client *client, int ban_type, TKL *except_tkl) 3094 { 3095 char uhost[NICKLEN+HOSTLEN+1]; 3096 3097 if (!TKLIsBanException(except_tkl)) 3098 return 0; 3099 3100 if (!tkl_banexception_matches_type(except_tkl, ban_type)) 3101 return 0; 3102 3103 /* For config file except ban { } we use security groups instead of simple user/host */ 3104 if (except_tkl->ptr.banexception->match) 3105 return user_allowed_by_security_group(client, except_tkl->ptr.banexception->match); 3106 3107 tkl_uhost(except_tkl, uhost, sizeof(uhost), NO_SOFT_PREFIX); 3108 3109 if (match_user(uhost, client, MATCH_CHECK_REAL)) 3110 { 3111 if (!(except_tkl->ptr.banexception->subtype & TKL_SUBTYPE_SOFT)) 3112 return 1; /* hard ban exempt */ 3113 if ((except_tkl->ptr.banexception->subtype & TKL_SUBTYPE_SOFT) && IsLoggedIn(client)) 3114 return 1; /* soft ban exempt - only matches if user is logged in */ 3115 } 3116 3117 return 0; /* not found */ 3118 } 3119 3120 /** Search for TKL Exceptions for this user. 3121 * @param ban_type The ban type to check, normally ban_tkl->type. 3122 * @param client The user 3123 * @returns 1 if ban exempt, 0 if not. 3124 * @note 3125 * If you have a TKL ban that matched, say, 'ban_tkl'. 3126 * Then you call this function like this: 3127 * if (find_tkl_exception(ban_tkl->type, client)) 3128 * return 0; // User is exempt 3129 * [.. continue and ban the user..] 3130 */ 3131 int _find_tkl_exception(int ban_type, Client *client) 3132 { 3133 TKL *tkl; 3134 int index, index2; 3135 Hook *hook; 3136 3137 if (IsServer(client) || IsMe(client)) 3138 return 1; 3139 3140 /* First, the TKL ip hash table entries.. */ 3141 index = tkl_ip_hash_type('e'); 3142 index2 = tkl_ip_hash(GetIP(client)); 3143 if (index2 >= 0) 3144 { 3145 for (tkl = tklines_ip_hash[index][index2]; tkl; tkl = tkl->next) 3146 { 3147 if (find_tkl_exception_matcher(client, ban_type, tkl)) 3148 return 1; /* exempt */ 3149 } 3150 } 3151 3152 /* If not banned (yet), then check regular entries.. */ 3153 for (tkl = tklines[tkl_hash('e')]; tkl; tkl = tkl->next) 3154 { 3155 if (find_tkl_exception_matcher(client, ban_type, tkl)) 3156 return 1; /* exempt */ 3157 } 3158 3159 for (hook = Hooks[HOOKTYPE_TKL_EXCEPT]; hook; hook = hook->next) 3160 { 3161 if (hook->func.intfunc(client, ban_type) > 0) 3162 return 1; /* exempt by hook */ 3163 } 3164 return 0; /* Not exempt */ 3165 } 3166 3167 /** Helper function for find_tkline_match() */ 3168 int find_tkline_match_matcher(Client *client, int skip_soft, TKL *tkl) 3169 { 3170 char uhost[NICKLEN+HOSTLEN+1]; 3171 3172 if (!TKLIsServerBan(tkl) || (tkl->type & TKL_SHUN)) 3173 return 0; 3174 3175 if (skip_soft && (tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT)) 3176 return 0; 3177 3178 tkl_uhost(tkl, uhost, sizeof(uhost), NO_SOFT_PREFIX); 3179 3180 if (match_user(uhost, client, MATCH_CHECK_REAL)) 3181 { 3182 /* If hard-ban, or soft-ban&unauthenticated.. */ 3183 if (!(tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT) || 3184 ((tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT) && !IsLoggedIn(client))) 3185 { 3186 /* Found match. Now check for exception... */ 3187 if (find_tkl_exception(tkl->type, client)) 3188 return 0; /* exempted */ 3189 return 1; /* banned */ 3190 } 3191 } 3192 3193 return 0; /* no match */ 3194 } 3195 3196 /** Check if user matches a *LINE. If so, kill the user. 3197 * @retval 1 if client is banned, 0 if not 3198 * @note Do not continue processing if the client is killed (0 return value). 3199 * @note Return value changed with regards to UnrealIRCd 4! 3200 */ 3201 int _find_tkline_match(Client *client, int skip_soft) 3202 { 3203 TKL *tkl; 3204 int banned = 0; 3205 int index, index2; 3206 3207 if (IsServer(client) || IsMe(client)) 3208 return 0; 3209 3210 /* First, the TKL ip hash table entries.. */ 3211 index2 = tkl_ip_hash(GetIP(client)); 3212 if (index2 >= 0) 3213 { 3214 for (index = 0; index < TKLIPHASHLEN1; index++) 3215 { 3216 for (tkl = tklines_ip_hash[index][index2]; tkl; tkl = tkl->next) 3217 { 3218 banned = find_tkline_match_matcher(client, skip_soft, tkl); 3219 if (banned) 3220 break; 3221 } 3222 if (banned) 3223 break; 3224 } 3225 } 3226 3227 /* If not banned (yet), then check regular entries.. */ 3228 if (!banned) 3229 { 3230 for (index = 0; index < TKLISTLEN; index++) 3231 { 3232 for (tkl = tklines[index]; tkl; tkl = tkl->next) 3233 { 3234 banned = find_tkline_match_matcher(client, skip_soft, tkl); 3235 if (banned) 3236 break; 3237 } 3238 if (banned) 3239 break; 3240 } 3241 } 3242 3243 if (!banned) 3244 return 0; 3245 3246 /* User is banned... */ 3247 3248 RunHookReturnInt(HOOKTYPE_FIND_TKLINE_MATCH, !=99, client, tkl); 3249 3250 if (tkl->type & TKL_KILL) 3251 { 3252 ircstats.is_ref++; 3253 if (tkl->type & TKL_GLOBAL) 3254 banned_client(client, "G-Lined", tkl->ptr.serverban->reason, 1, 0); 3255 else 3256 banned_client(client, "K-Lined", tkl->ptr.serverban->reason, 0, 0); 3257 return 1; /* killed */ 3258 } else 3259 if (tkl->type & TKL_ZAP) 3260 { 3261 ircstats.is_ref++; 3262 banned_client(client, "Z-Lined", tkl->ptr.serverban->reason, (tkl->type & TKL_GLOBAL)?1:0, 0); 3263 return 1; /* killed */ 3264 } 3265 3266 return 0; 3267 } 3268 3269 /** Check if user is shunned. 3270 * @param client Client to check. 3271 * @returns 1 if shunned, 0 if not. 3272 */ 3273 int _find_shun(Client *client) 3274 { 3275 TKL *tkl; 3276 3277 if (IsServer(client) || IsMe(client)) 3278 return 0; 3279 3280 if (IsShunned(client)) 3281 return 1; 3282 3283 if (ValidatePermissionsForPath("immune:server-ban:shun",client,NULL,NULL,NULL)) 3284 return 0; 3285 3286 for (tkl = tklines[tkl_hash('s')]; tkl; tkl = tkl->next) 3287 { 3288 char uhost[NICKLEN+HOSTLEN+1]; 3289 3290 if (!(tkl->type & TKL_SHUN)) 3291 continue; 3292 3293 tkl_uhost(tkl, uhost, sizeof(uhost), NO_SOFT_PREFIX); 3294 3295 if (match_user(uhost, client, MATCH_CHECK_REAL)) 3296 { 3297 /* If hard-ban, or soft-ban&unauthenticated.. */ 3298 if (!(tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT) || 3299 ((tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT) && !IsLoggedIn(client))) 3300 { 3301 /* Found match. Now check for exception... */ 3302 if (find_tkl_exception(TKL_SHUN, client)) 3303 return 0; 3304 SetShunned(client); 3305 return 1; 3306 } 3307 } 3308 } 3309 3310 return 0; 3311 } 3312 3313 /** Helper function for spamfilter_build_user_string(). 3314 * This ensures IPv6 hosts are in brackets. 3315 */ 3316 char *SpamfilterMagicHost(char *i) 3317 { 3318 static char buf[256]; 3319 3320 if (!strchr(i, ':')) 3321 return i; 3322 3323 /* otherwise, it's IPv6.. prepend it with [ and append a ] */ 3324 ircsnprintf(buf, sizeof(buf), "[%s]", i); 3325 return buf; 3326 } 3327 3328 /** Build the nick:user@host:realname string 3329 * @param buf The buffer used for storage, the size of 3330 * which should be at least NICKLEN+USERLEN+HOSTLEN+1. 3331 * @param nick The nickname (because client can be nick-changing). 3332 * @param client The affected client. 3333 */ 3334 void _spamfilter_build_user_string(char *buf, char *nick, Client *client) 3335 { 3336 snprintf(buf, NICKLEN+USERLEN+HOSTLEN+1, "%s!%s@%s:%s", 3337 nick, client->user->username, SpamfilterMagicHost(client->user->realhost), client->info); 3338 } 3339 3340 3341 /** Checks if the user matches a spamfilter of type 'u' (user, 3342 * nick!user@host:realname ban). 3343 * Written by: Syzop 3344 * Assumes: only call for clients, possible assume on local clients [?] 3345 * Return values: see match_spamfilter() 3346 */ 3347 int _find_spamfilter_user(Client *client, int flags) 3348 { 3349 char spamfilter_user[NICKLEN + USERLEN + HOSTLEN + REALLEN + 64]; /* n!u@h:r */ 3350 3351 if (ValidatePermissionsForPath("immune:server-ban:spamfilter",client,NULL,NULL,NULL)) 3352 return 0; 3353 3354 spamfilter_build_user_string(spamfilter_user, client->name, client); 3355 return match_spamfilter(client, spamfilter_user, SPAMF_USER, NULL, NULL, flags, NULL); 3356 } 3357 3358 /** Check a spamfilter against all local users and print a message. 3359 * This is only used for the 'warn' action (BAN_ACT_WARN). 3360 */ 3361 int spamfilter_check_users(TKL *tkl) 3362 { 3363 char spamfilter_user[NICKLEN + USERLEN + HOSTLEN + REALLEN + 64]; /* n!u@h:r */ 3364 char buf[1024]; 3365 int matches = 0; 3366 Client *client; 3367 3368 list_for_each_entry_reverse(client, &lclient_list, lclient_node) 3369 { 3370 if (MyUser(client)) 3371 { 3372 spamfilter_build_user_string(spamfilter_user, client->name, client); 3373 if (!unreal_match(tkl->ptr.spamfilter->match, spamfilter_user)) 3374 continue; /* No match */ 3375 3376 /* matched! */ 3377 unreal_log(ULOG_INFO, "tkl", "SPAMFILTER_MATCH", client, 3378 "[Spamfilter] $client.details matches filter '$tkl': [cmd: $command: '$str'] [reason: $tkl.reason] [action: $tkl.ban_action]", 3379 log_data_tkl("tkl", tkl), 3380 log_data_string("command", "USER"), 3381 log_data_string("str", spamfilter_user)); 3382 3383 RunHook(HOOKTYPE_LOCAL_SPAMFILTER, client, spamfilter_user, spamfilter_user, SPAMF_USER, NULL, tkl); 3384 matches++; 3385 } 3386 } 3387 3388 return matches; 3389 } 3390 3391 /** Check if the nick or channel name is banned (Q-Line). 3392 * @param client The possibly affected user. 3393 * @param name The nick or channel to check. 3394 * @param is_hold This will be SET (so OUT) if it's a services hold. 3395 * 3396 * @note Special handling: 3397 * #*ble* will match with #bbleh 3398 * *ble* will NOT match with #bbleh, will with bbleh 3399 */ 3400 TKL *_find_qline(Client *client, char *name, int *ishold) 3401 { 3402 TKL *tkl; 3403 int points = 0; 3404 *ishold = 0; 3405 3406 if (IsServer(client) || IsMe(client)) 3407 return NULL; 3408 3409 for (tkl = tklines[tkl_hash('q')]; tkl; tkl = tkl->next) 3410 { 3411 points = 0; 3412 3413 if (!TKLIsNameBan(tkl)) 3414 continue; 3415 3416 if (((*tkl->ptr.nameban->name == '#' && *name == '#') || (*tkl->ptr.nameban->name != '#' && *name != '#')) 3417 && match_simple(tkl->ptr.nameban->name, name)) 3418 { 3419 points = 1; 3420 break; 3421 } 3422 } 3423 3424 if (points != 1) 3425 return NULL; 3426 3427 /* It's a services hold (except bans don't override this) */ 3428 if (tkl->ptr.nameban->hold) 3429 { 3430 *ishold = 1; 3431 return tkl; 3432 } 3433 3434 if (find_tkl_exception(TKL_NAME, client)) 3435 return NULL; /* exempt */ 3436 3437 return tkl; 3438 } 3439 3440 /** Helper function for find_tkline_match_zap() */ 3441 TKL *find_tkline_match_zap_matcher(Client *client, TKL *tkl) 3442 { 3443 if (!(tkl->type & TKL_ZAP)) 3444 return NULL; 3445 3446 if (match_user(tkl->ptr.serverban->hostmask, client, MATCH_CHECK_IP)) 3447 { 3448 if (find_tkl_exception(TKL_ZAP, client)) 3449 return NULL; /* exempt */ 3450 return tkl; /* banned */ 3451 } 3452 3453 return NULL; /* no match */ 3454 } 3455 3456 /** Find matching (G)ZLINE, if any. 3457 * Note: function prototype changed as per UnrealIRCd 4.2.0. 3458 * @retval The (G)Z-Line that matched, or NULL if no such ban was found. 3459 */ 3460 TKL *_find_tkline_match_zap(Client *client) 3461 { 3462 TKL *tkl, *ret; 3463 int index, index2; 3464 3465 if (IsServer(client) || IsMe(client)) 3466 return NULL; 3467 3468 /* First, the TKL ip hash table entries.. */ 3469 index = tkl_ip_hash_type('z'); 3470 index2 = tkl_ip_hash(GetIP(client)); 3471 if (index2 >= 0) 3472 { 3473 for (tkl = tklines_ip_hash[index][index2]; tkl; tkl = tkl->next) 3474 { 3475 ret = find_tkline_match_zap_matcher(client, tkl); 3476 if (ret) 3477 return ret; 3478 } 3479 } 3480 3481 /* If not banned (yet), then check regular entries.. */ 3482 for (tkl = tklines[tkl_hash('z')]; tkl; tkl = tkl->next) 3483 { 3484 ret = find_tkline_match_zap_matcher(client, tkl); 3485 if (ret) 3486 return ret; 3487 } 3488 3489 return NULL; 3490 } 3491 3492 #define BY_MASK 0x1 3493 #define BY_REASON 0x2 3494 #define NOT_BY_MASK 0x4 3495 #define NOT_BY_REASON 0x8 3496 #define BY_SETBY 0x10 3497 #define NOT_BY_SETBY 0x20 3498 3499 typedef struct { 3500 int flags; 3501 const char *mask; 3502 const char *reason; 3503 const char *set_by; 3504 } TKLFlag; 3505 3506 /** Parse STATS tkl parameters. 3507 * TODO: I don't think this is documented anywhere? Or underdocumented at least. 3508 */ 3509 static void parse_stats_params(const char *para, TKLFlag *flag) 3510 { 3511 static char paratmp[512]; /* <- copy of para, because it gets fragged by strtok() */ 3512 char *flags, *tmp; 3513 char what = '+'; 3514 3515 memset(flag, 0, sizeof(TKLFlag)); 3516 strlcpy(paratmp, para, sizeof(paratmp)); 3517 flags = strtok(paratmp, " "); 3518 if (!flags) 3519 return; 3520 3521 for (; *flags; flags++) 3522 { 3523 switch (*flags) 3524 { 3525 case '+': 3526 what = '+'; 3527 break; 3528 case '-': 3529 what = '-'; 3530 break; 3531 case 'm': 3532 if (flag->mask || !(tmp = strtok(NULL, " "))) 3533 continue; 3534 if (what == '+') 3535 flag->flags |= BY_MASK; 3536 else 3537 flag->flags |= NOT_BY_MASK; 3538 flag->mask = tmp; 3539 break; 3540 case 'r': 3541 if (flag->reason || !(tmp = strtok(NULL, " "))) 3542 continue; 3543 if (what == '+') 3544 flag->flags |= BY_REASON; 3545 else 3546 flag->flags |= NOT_BY_REASON; 3547 flag->reason = tmp; 3548 break; 3549 case 's': 3550 if (flag->set_by || !(tmp = strtok(NULL, " "))) 3551 continue; 3552 if (what == '+') 3553 flag->flags |= BY_SETBY; 3554 else 3555 flag->flags |= NOT_BY_SETBY; 3556 flag->set_by = tmp; 3557 break; 3558 } 3559 } 3560 } 3561 3562 /** Does this TKL entry match the search terms? 3563 * This is a helper function for tkl_stats(). 3564 */ 3565 int tkl_stats_matcher(Client *client, int type, const char *para, TKLFlag *tklflags, TKL *tkl) 3566 { 3567 /***** First, handle the selection ******/ 3568 3569 if (!BadPtr(para)) 3570 { 3571 if (tklflags->flags & BY_SETBY) 3572 if (!match_simple(tklflags->set_by, tkl->set_by)) 3573 return 0; 3574 if (tklflags->flags & NOT_BY_SETBY) 3575 if (match_simple(tklflags->set_by, tkl->set_by)) 3576 return 0; 3577 if (TKLIsServerBan(tkl)) 3578 { 3579 if (tklflags->flags & BY_MASK) 3580 { 3581 if (!match_simple(tklflags->mask, make_user_host(tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask))) 3582 return 0; 3583 } 3584 if (tklflags->flags & NOT_BY_MASK) 3585 { 3586 if (match_simple(tklflags->mask, make_user_host(tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask))) 3587 return 0; 3588 } 3589 if (tklflags->flags & BY_REASON) 3590 if (!match_simple(tklflags->reason, tkl->ptr.serverban->reason)) 3591 return 0; 3592 if (tklflags->flags & NOT_BY_REASON) 3593 if (match_simple(tklflags->reason, tkl->ptr.serverban->reason)) 3594 return 0; 3595 } else 3596 if (TKLIsNameBan(tkl)) 3597 { 3598 if (tklflags->flags & BY_MASK) 3599 { 3600 if (!match_simple(tklflags->mask, tkl->ptr.nameban->name)) 3601 return 0; 3602 } 3603 if (tklflags->flags & NOT_BY_MASK) 3604 { 3605 if (match_simple(tklflags->mask, tkl->ptr.nameban->name)) 3606 return 0; 3607 } 3608 if (tklflags->flags & BY_REASON) 3609 if (!match_simple(tklflags->reason, tkl->ptr.nameban->reason)) 3610 return 0; 3611 if (tklflags->flags & NOT_BY_REASON) 3612 if (match_simple(tklflags->reason, tkl->ptr.nameban->reason)) 3613 return 0; 3614 } else 3615 if (TKLIsBanException(tkl)) 3616 { 3617 if (tklflags->flags & BY_MASK) 3618 { 3619 if (!match_simple(tklflags->mask, make_user_host(tkl->ptr.banexception->usermask, tkl->ptr.banexception->hostmask))) 3620 return 0; 3621 } 3622 if (tklflags->flags & NOT_BY_MASK) 3623 { 3624 if (match_simple(tklflags->mask, make_user_host(tkl->ptr.banexception->usermask, tkl->ptr.banexception->hostmask))) 3625 return 0; 3626 } 3627 if (tklflags->flags & BY_REASON) 3628 if (!match_simple(tklflags->reason, tkl->ptr.banexception->reason)) 3629 return 0; 3630 if (tklflags->flags & NOT_BY_REASON) 3631 if (match_simple(tklflags->reason, tkl->ptr.banexception->reason)) 3632 return 0; 3633 } 3634 } 3635 3636 /***** If we are still here then we have a match and will will send the STATS entry */ 3637 if (TKLIsServerBan(tkl)) 3638 { 3639 char uhostbuf[BUFSIZE]; 3640 char *uhost = tkl_uhost(tkl, uhostbuf, sizeof(uhostbuf), 0); 3641 if (tkl->type == (TKL_KILL | TKL_GLOBAL)) 3642 { 3643 sendnumeric(client, RPL_STATSGLINE, 'G', uhost, 3644 (tkl->expire_at != 0) ? (long long)(tkl->expire_at - TStime()) : 0, 3645 (long long)(TStime() - tkl->set_at), tkl->set_by, tkl->ptr.serverban->reason); 3646 } else 3647 if (tkl->type == (TKL_ZAP | TKL_GLOBAL)) 3648 { 3649 sendnumeric(client, RPL_STATSGLINE, 'Z', uhost, 3650 (tkl->expire_at != 0) ? (long long)(tkl->expire_at - TStime()) : 0, 3651 (long long)(TStime() - tkl->set_at), tkl->set_by, tkl->ptr.serverban->reason); 3652 } else 3653 if (tkl->type == (TKL_SHUN | TKL_GLOBAL)) 3654 { 3655 sendnumeric(client, RPL_STATSGLINE, 's', uhost, 3656 (tkl->expire_at != 0) ? (long long)(tkl->expire_at - TStime()) : 0, 3657 (long long)(TStime() - tkl->set_at), tkl->set_by, tkl->ptr.serverban->reason); 3658 } else 3659 if (tkl->type == (TKL_KILL)) 3660 { 3661 sendnumeric(client, RPL_STATSGLINE, 'K', uhost, 3662 (tkl->expire_at != 0) ? (long long)(tkl->expire_at - TStime()) : 0, 3663 (long long)(TStime() - tkl->set_at), tkl->set_by, tkl->ptr.serverban->reason); 3664 } else 3665 if (tkl->type == (TKL_ZAP)) 3666 { 3667 sendnumeric(client, RPL_STATSGLINE, 'z', uhost, 3668 (tkl->expire_at != 0) ? (long long)(tkl->expire_at - TStime()) : 0, 3669 (long long)(TStime() - tkl->set_at), tkl->set_by, tkl->ptr.serverban->reason); 3670 } 3671 } else 3672 if (TKLIsSpamfilter(tkl)) 3673 { 3674 sendnumeric(client, RPL_STATSSPAMF, 3675 (tkl->type & TKL_GLOBAL) ? 'F' : 'f', 3676 unreal_match_method_valtostr(tkl->ptr.spamfilter->match->type), 3677 spamfilter_target_inttostring(tkl->ptr.spamfilter->target), 3678 banact_valtostring(tkl->ptr.spamfilter->action), 3679 (tkl->expire_at != 0) ? (long long)(tkl->expire_at - TStime()) : 0, 3680 (long long)(TStime() - tkl->set_at), 3681 (long long)tkl->ptr.spamfilter->tkl_duration, 3682 tkl->ptr.spamfilter->tkl_reason, 3683 tkl->set_by, 3684 tkl->ptr.spamfilter->match->str); 3685 if (para && !strcasecmp(para, "del")) 3686 { 3687 char *hash = spamfilter_id(tkl); 3688 if (tkl->type & TKL_GLOBAL) 3689 { 3690 sendtxtnumeric(client, "To delete this spamfilter, use /SPAMFILTER del %s", hash); 3691 sendtxtnumeric(client, "-"); 3692 } else { 3693 sendtxtnumeric(client, "This spamfilter is stored in the configuration file and cannot be removed with /SPAMFILTER del"); 3694 sendtxtnumeric(client, "-"); 3695 } 3696 } 3697 } else 3698 if (TKLIsNameBan(tkl)) 3699 { 3700 sendnumeric(client, RPL_STATSQLINE, 3701 (tkl->type & TKL_GLOBAL) ? 'Q' : 'q', 3702 tkl->ptr.nameban->name, 3703 (tkl->expire_at != 0) ? (long long)(tkl->expire_at - TStime()) : 0, 3704 (long long)(TStime() - tkl->set_at), 3705 tkl->set_by, 3706 tkl->ptr.nameban->reason); 3707 } else 3708 if (TKLIsBanException(tkl)) 3709 { 3710 if (tkl->ptr.banexception->match) 3711 { 3712 /* Config-added: uses security groups */ 3713 NameValuePrioList *m; 3714 for (m = tkl->ptr.banexception->match->printable_list; m; m = m->next) 3715 { 3716 sendnumeric(client, RPL_STATSEXCEPTTKL, namevalue_nospaces(m), 3717 tkl->ptr.banexception->bantypes, 3718 (tkl->expire_at != 0) ? (long long)(tkl->expire_at - TStime()) : 0, 3719 (long long)(TStime() - tkl->set_at), tkl->set_by, tkl->ptr.banexception->reason); 3720 } 3721 } else { 3722 /* IRC-added: uses simple user/host mask */ 3723 char uhostbuf[BUFSIZE]; 3724 char *uhost = tkl_uhost(tkl, uhostbuf, sizeof(uhostbuf), 0); 3725 sendnumeric(client, RPL_STATSEXCEPTTKL, uhost, 3726 tkl->ptr.banexception->bantypes, 3727 (tkl->expire_at != 0) ? (long long)(tkl->expire_at - TStime()) : 0, 3728 (long long)(TStime() - tkl->set_at), tkl->set_by, tkl->ptr.banexception->reason); 3729 } 3730 } else 3731 { 3732 /* That's weird, unknown TKL type */ 3733 return 0; 3734 } 3735 return 1; 3736 } 3737 3738 /* TKL Stats. This is used by /STATS gline and all the others */ 3739 void _tkl_stats(Client *client, int type, const char *para, int *cnt) 3740 { 3741 TKL *tk; 3742 TKLFlag tklflags; 3743 int index, index2; 3744 3745 if ((max_stats_matches > 0) && (*cnt >= max_stats_matches)) 3746 return; 3747 3748 if (!BadPtr(para)) 3749 parse_stats_params(para, &tklflags); 3750 3751 /* First the IP hashed entries (if applicable).. */ 3752 index = tkl_ip_hash_type(tkl_typetochar(type)); 3753 if (index >= 0) 3754 { 3755 for (index2 = 0; index2 < TKLIPHASHLEN2; index2++) 3756 { 3757 for (tk = tklines_ip_hash[index][index2]; tk; tk = tk->next) 3758 { 3759 if (type && tk->type != type) 3760 continue; 3761 if (tkl_stats_matcher(client, type, para, &tklflags, tk)) 3762 { 3763 *cnt += 1; 3764 if ((max_stats_matches > 0) && (*cnt >= max_stats_matches)) 3765 { 3766 sendnumeric(client, ERR_TOOMANYMATCHES, "STATS", "too many matches (set::max-stats-matches)"); 3767 sendnotice(client, "Consider searching on something more specific, eg '/STATS gline +m *.nl'. See '/STATS' (without parameters) for help."); 3768 return; 3769 } 3770 } 3771 } 3772 } 3773 } 3774 3775 /* Then the normal entries... */ 3776 for (index = 0; index < TKLISTLEN; index++) 3777 { 3778 for (tk = tklines[index]; tk; tk = tk->next) 3779 { 3780 if (type && tk->type != type) 3781 continue; 3782 if (tkl_stats_matcher(client, type, para, &tklflags, tk)) 3783 { 3784 *cnt += 1; 3785 if ((max_stats_matches > 0) && (*cnt >= max_stats_matches)) 3786 { 3787 sendnumeric(client, ERR_TOOMANYMATCHES, "STATS", "too many matches (set::max-stats-matches)"); 3788 sendnotice(client, "Consider searching on something more specific, eg '/STATS gline +m *.nl'. See '/STATS' (without parameters) for help."); 3789 return; 3790 } 3791 } 3792 } 3793 } 3794 3795 if ((type == (TKL_SPAMF|TKL_GLOBAL)) && (!para || strcasecmp(para, "del"))) 3796 { 3797 /* If requesting spamfilter stats and not spamfilter del, then suggest it. */ 3798 sendnotice(client, "Tip: if you are looking for an easy way to remove a spamfilter, run '/SPAMFILTER del'."); 3799 } 3800 } 3801 3802 /** Synchronize a TKL entry with the other server. 3803 * @param sender The sender (eg: &me). 3804 * @param to The remote server. 3805 * @param tkl The TKL entry. 3806 */ 3807 void tkl_sync_send_entry(int add, Client *sender, Client *to, TKL *tkl) 3808 { 3809 char typ; 3810 3811 if (!(tkl->type & TKL_GLOBAL)) 3812 return; /* nothing to sync */ 3813 3814 typ = tkl_typetochar(tkl->type); 3815 3816 if (TKLIsServerBan(tkl)) 3817 { 3818 sendto_one(to, NULL, ":%s TKL %c %c %s%s %s %s %lld %lld :%s", sender->name, 3819 add ? '+' : '-', 3820 typ, 3821 (tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT) ? "%" : "", 3822 *tkl->ptr.serverban->usermask ? tkl->ptr.serverban->usermask : "*", 3823 tkl->ptr.serverban->hostmask, tkl->set_by, 3824 (long long)tkl->expire_at, (long long)tkl->set_at, 3825 tkl->ptr.serverban->reason); 3826 } else 3827 if (TKLIsNameBan(tkl)) 3828 { 3829 sendto_one(to, NULL, ":%s TKL %c %c %c %s %s %lld %lld :%s", sender->name, 3830 add ? '+' : '-', 3831 typ, 3832 tkl->ptr.nameban->hold ? 'H' : '*', 3833 tkl->ptr.nameban->name, 3834 tkl->set_by, 3835 (long long)tkl->expire_at, (long long)tkl->set_at, 3836 tkl->ptr.nameban->reason); 3837 } else 3838 if (TKLIsSpamfilter(tkl)) 3839 { 3840 sendto_one(to, NULL, ":%s TKL %c %c %s %c %s %lld %lld %lld %s %s :%s", sender->name, 3841 add ? '+' : '-', 3842 typ, 3843 spamfilter_target_inttostring(tkl->ptr.spamfilter->target), 3844 banact_valtochar(tkl->ptr.spamfilter->action), 3845 tkl->set_by, 3846 (long long)tkl->expire_at, (long long)tkl->set_at, 3847 (long long)tkl->ptr.spamfilter->tkl_duration, tkl->ptr.spamfilter->tkl_reason, 3848 unreal_match_method_valtostr(tkl->ptr.spamfilter->match->type), 3849 tkl->ptr.spamfilter->match->str); 3850 } else 3851 if (TKLIsBanException(tkl)) 3852 { 3853 sendto_one(to, NULL, ":%s TKL %c %c %s%s %s %s %lld %lld %s :%s", sender->name, 3854 add ? '+' : '-', 3855 typ, 3856 (tkl->ptr.banexception->subtype & TKL_SUBTYPE_SOFT) ? "%" : "", 3857 *tkl->ptr.banexception->usermask ? tkl->ptr.banexception->usermask : "*", 3858 tkl->ptr.banexception->hostmask, tkl->set_by, 3859 (long long)tkl->expire_at, (long long)tkl->set_at, 3860 tkl->ptr.banexception->bantypes, 3861 tkl->ptr.banexception->reason); 3862 } else 3863 { 3864 unreal_log(ULOG_FATAL, "tkl", "BUG_TKL_SYNC_SEND_ENTRY", NULL, 3865 "[BUG] tkl_sync_send_entry() called, but unknown type: $tkl.type_string ($tkl_type_int)", 3866 log_data_tkl("tkl", tkl), 3867 log_data_integer("tkl_type_int", typ)); 3868 abort(); 3869 } 3870 } 3871 3872 /** Broadcast a TKL entry. 3873 * @param sender The sender, eg &me 3874 * @param skip The client to skip, eg 'client' or NULL. 3875 * @param tkl The TKL entry to synchronize with the other servers. 3876 */ 3877 void tkl_broadcast_entry(int add, Client *sender, Client *skip, TKL *tkl) 3878 { 3879 Client *acptr; 3880 3881 /* Silly fix for RPC calls that lead to broadcasts from this sender */ 3882 if (!IsUser(sender) && !IsServer(sender)) 3883 sender = &me; 3884 3885 list_for_each_entry(acptr, &server_list, special_node) 3886 { 3887 if (skip && acptr == skip->direction) 3888 continue; 3889 3890 tkl_sync_send_entry(add, sender, acptr, tkl); 3891 } 3892 } 3893 3894 /** Synchronize all TKL entries with this server. 3895 * @param client The server to synchronize with. 3896 */ 3897 void _tkl_sync(Client *client) 3898 { 3899 TKL *tkl; 3900 int index, index2; 3901 3902 /* First, hashed entries.. */ 3903 for (index = 0; index < TKLIPHASHLEN1; index++) 3904 { 3905 for (index2 = 0; index2 < TKLIPHASHLEN2; index2++) 3906 { 3907 for (tkl = tklines_ip_hash[index][index2]; tkl; tkl = tkl->next) 3908 { 3909 tkl_sync_send_entry(1, &me, client, tkl); 3910 } 3911 } 3912 } 3913 3914 /* Then, regular entries.. */ 3915 for (index = 0; index < TKLISTLEN; index++) 3916 { 3917 for (tkl = tklines[index]; tkl; tkl = tkl->next) 3918 { 3919 tkl_sync_send_entry(1, &me, client, tkl); 3920 } 3921 } 3922 } 3923 3924 /** Find a server ban TKL - only used to prevent duplicates and for deletion */ 3925 TKL *_find_tkl_serverban(int type, char *usermask, char *hostmask, int softban) 3926 { 3927 char tpe = tkl_typetochar(type); 3928 TKL *head, *tkl; 3929 3930 if (!TKLIsServerBanType(type)) 3931 abort(); 3932 3933 head = tkl_find_head(tpe, hostmask, tklines[tkl_hash(tpe)]); 3934 for (tkl = head; tkl; tkl = tkl->next) 3935 { 3936 if (tkl->type == type) 3937 { 3938 if (!strcasecmp(tkl->ptr.serverban->hostmask, hostmask) && 3939 !strcasecmp(tkl->ptr.serverban->usermask, usermask)) 3940 { 3941 /* And an extra check for soft/hard ban mismatches.. */ 3942 if ((tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT) == softban) 3943 return tkl; 3944 } 3945 } 3946 } 3947 return NULL; /* Not found */ 3948 } 3949 3950 /** Find a ban exception TKL - only used to prevent duplicates and for deletion */ 3951 TKL *_find_tkl_banexception(int type, char *usermask, char *hostmask, int softban) 3952 { 3953 char tpe = tkl_typetochar(type); 3954 TKL *head, *tkl; 3955 3956 if (!TKLIsBanExceptionType(type)) 3957 abort(); 3958 3959 head = tkl_find_head(tpe, hostmask, tklines[tkl_hash(tpe)]); 3960 for (tkl = head; tkl; tkl = tkl->next) 3961 { 3962 if (tkl->type == type) 3963 { 3964 if (!strcasecmp(tkl->ptr.banexception->hostmask, hostmask) && 3965 !strcasecmp(tkl->ptr.banexception->usermask, usermask)) 3966 { 3967 /* And an extra check for soft/hard ban mismatches.. */ 3968 if ((tkl->ptr.banexception->subtype & TKL_SUBTYPE_SOFT) == softban) 3969 return tkl; 3970 } 3971 } 3972 } 3973 return NULL; /* Not found */ 3974 } 3975 3976 /** Find a name ban TKL (qline) - only used to prevent duplicates and for deletion */ 3977 TKL *_find_tkl_nameban(int type, char *name, int hold) 3978 { 3979 char tpe = tkl_typetochar(type); 3980 TKL *tkl; 3981 3982 if (!TKLIsNameBanType(type)) 3983 abort(); 3984 3985 for (tkl = tklines[tkl_hash(tpe)]; tkl; tkl = tkl->next) 3986 { 3987 if ((tkl->type == type) && !strcasecmp(tkl->ptr.nameban->name, name)) 3988 return tkl; 3989 } 3990 return NULL; /* Not found */ 3991 } 3992 3993 /** Find a spamfilter TKL - only used to prevent duplicates and for deletion */ 3994 TKL *_find_tkl_spamfilter(int type, char *match_string, BanAction action, unsigned short target) 3995 { 3996 char tpe = tkl_typetochar(type); 3997 TKL *tkl; 3998 3999 if (!TKLIsSpamfilterType(type)) 4000 abort(); 4001 4002 for (tkl = tklines[tkl_hash(tpe)]; tkl; tkl = tkl->next) 4003 { 4004 if ((type == tkl->type) && 4005 !strcmp(match_string, tkl->ptr.spamfilter->match->str) && 4006 (action == tkl->ptr.spamfilter->action) && 4007 (target == tkl->ptr.spamfilter->target)) 4008 { 4009 return tkl; 4010 } 4011 } 4012 return NULL; /* Not found */ 4013 } 4014 4015 /** Send a notice to opers about the TKL that is being added */ 4016 void _sendnotice_tkl_add(TKL *tkl) 4017 { 4018 /* Don't show notices for temporary nick holds (issued by services) */ 4019 if (TKLIsNameBan(tkl) && tkl->ptr.nameban->hold) 4020 return; 4021 4022 if (TKLIsServerBan(tkl)) 4023 { 4024 unreal_log(ULOG_INFO, "tkl", "TKL_ADD", NULL, 4025 "$tkl.type_string added: '$tkl' [reason: $tkl.reason] [by: $tkl.set_by] [duration: $tkl.duration_string]", 4026 log_data_tkl("tkl", tkl)); 4027 } else 4028 if (TKLIsNameBan(tkl)) 4029 { 4030 unreal_log(ULOG_INFO, "tkl", "TKL_ADD", NULL, 4031 "$tkl.type_string added: '$tkl' [reason: $tkl.reason] [by: $tkl.set_by] [duration: $tkl.duration_string]", 4032 log_data_tkl("tkl", tkl)); 4033 } else 4034 if (TKLIsSpamfilter(tkl)) 4035 { 4036 unreal_log(ULOG_INFO, "tkl", "TKL_ADD", NULL, 4037 "Spamfilter added: '$tkl' [type: $tkl.match_type] [targets: $tkl.spamfilter_targets] " 4038 "[action: $tkl.ban_action] [reason: $tkl.reason] [by: $tkl.set_by]", 4039 log_data_tkl("tkl", tkl)); 4040 } else 4041 if (TKLIsBanException(tkl)) 4042 { 4043 unreal_log(ULOG_INFO, "tkl", "TKL_ADD", NULL, 4044 "$tkl.type_string added: '$tkl' [types: $tkl.exception_types] [by: $tkl.set_by] [duration: $tkl.duration_string]", 4045 log_data_tkl("tkl", tkl)); 4046 } else 4047 { 4048 unreal_log(ULOG_ERROR, "tkl", "BUG_UNKNOWN_TKL", NULL, 4049 "[BUG] TKL added of unknown type, unhandled in sendnotice_tkl_add()!!!!"); 4050 } 4051 } 4052 4053 /** Send a notice to opers about the TKL that is being deleted */ 4054 void _sendnotice_tkl_del(char *removed_by, TKL *tkl) 4055 { 4056 /* Don't show notices for temporary nick holds (issued by services) */ 4057 if (TKLIsNameBan(tkl) && tkl->ptr.nameban->hold) 4058 return; 4059 4060 if (TKLIsServerBan(tkl)) 4061 { 4062 unreal_log(ULOG_INFO, "tkl", "TKL_DEL", NULL, 4063 "$tkl.type_string removed: '$tkl' [reason: $tkl.reason] [by: $removed_by] [set at: $tkl.set_at_string]", 4064 log_data_tkl("tkl", tkl), 4065 log_data_string("removed_by", removed_by)); 4066 } else 4067 if (TKLIsNameBan(tkl)) 4068 { 4069 unreal_log(ULOG_INFO, "tkl", "TKL_DEL", NULL, 4070 "$tkl.type_string removed: '$tkl' [reason: $tkl.reason] [by: $removed_by] [set at: $tkl.set_at_string]", 4071 log_data_tkl("tkl", tkl), 4072 log_data_string("removed_by", removed_by)); 4073 } else 4074 if (TKLIsSpamfilter(tkl)) 4075 { 4076 unreal_log(ULOG_INFO, "tkl", "TKL_DEL", NULL, 4077 "Spamfilter removed: '$tkl' [type: $tkl.match_type] [targets: $tkl.spamfilter_targets] " 4078 "[action: $tkl.ban_action] [reason: $tkl.reason] [by: $removed_by] [set at: $tkl.set_at_string]", 4079 log_data_tkl("tkl", tkl), 4080 log_data_string("removed_by", removed_by)); 4081 } else 4082 if (TKLIsBanException(tkl)) 4083 { 4084 unreal_log(ULOG_INFO, "tkl", "TKL_DEL", NULL, 4085 "$tkl.type_string removed: '$tkl' [types: $tkl.exception_types] [by: $removed_by] [set at: $tkl.set_at_string]", 4086 log_data_tkl("tkl", tkl), 4087 log_data_string("removed_by", removed_by)); 4088 } else 4089 { 4090 unreal_log(ULOG_ERROR, "tkl", "BUG_UNKNOWN_TKL", NULL, 4091 "[BUG] TKL removed of unknown type, unhandled in sendnotice_tkl_del()!!!!"); 4092 } 4093 } 4094 4095 /** Called when a TKL is added by a remote user, local user, RPC user, .. 4096 * (but not when a TKL is added via the config) 4097 */ 4098 void _tkl_added(Client *client, TKL *tkl) 4099 { 4100 RunHook(HOOKTYPE_TKL_ADD, client, tkl); 4101 4102 sendnotice_tkl_add(tkl); 4103 4104 /* spamfilter 'warn' action is special */ 4105 if ((tkl->type & TKL_SPAMF) && (tkl->ptr.spamfilter->action == BAN_ACT_WARN) && (tkl->ptr.spamfilter->target & SPAMF_USER)) 4106 spamfilter_check_users(tkl); 4107 4108 /* Ban checking executes during run loop for efficiency */ 4109 loop.do_bancheck = 1; 4110 4111 if (tkl->type & TKL_GLOBAL) 4112 tkl_broadcast_entry(1, client, client, tkl); 4113 } 4114 4115 /** Add a TKL using the TKL layer. See cmd_tkl for parv[] and protocol documentation. */ 4116 CMD_FUNC(cmd_tkl_add) 4117 { 4118 TKL *tkl; 4119 int type; 4120 time_t expire_at, set_at; 4121 const char *set_by; 4122 char tkl_entry_exists = 0; 4123 4124 /* we rely on servers to be failsafe.. */ 4125 if (!IsServer(client) && !IsMe(client)) 4126 return; 4127 4128 if (parc < 9) 4129 return; 4130 4131 type = tkl_chartotype(parv[2][0]); 4132 if (!type) 4133 return; 4134 4135 /* All TKL types have the following fields in common when adding: 4136 * parv[5]: set_by 4137 * parv[6]: expire_at 4138 * parv[7]: set_at 4139 * ... so we validate them here at the beginning. 4140 */ 4141 4142 set_by = parv[5]; 4143 expire_at = atol(parv[6]); 4144 set_at = atol(parv[7]); 4145 4146 /* Validate set and expiry time */ 4147 if ((set_at < 0) || !short_date(set_at, NULL)) 4148 { 4149 unreal_log(ULOG_WARNING, "tkl", "TKL_ADD_INVALID", client, 4150 "Invalid TKL entry from $client: " 4151 "The set-at time is out of range ($set_at). Clock on other server incorrect or bogus entry.", 4152 log_data_integer("set_at", set_at)); 4153 return; 4154 } 4155 if ((expire_at < 0) || !short_date(expire_at, NULL)) 4156 { 4157 unreal_log(ULOG_WARNING, "tkl", "TKL_ADD_INVALID", client, 4158 "Invalid TKL entry from $client: " 4159 "The expire-at time is out of range ($expire_at). Clock on other server incorrect or bogus entry.", 4160 log_data_integer("expire_at", expire_at)); 4161 return; 4162 } 4163 4164 /* Now comes type-specific validation 4165 * and we check if the TKL entry already exists and needs updating too. 4166 */ 4167 4168 if (TKLIsServerBanType(type)) 4169 { 4170 /* Validate server ban TKL fields */ 4171 int softban = 0; 4172 const char *usermask = parv[3]; 4173 const char *hostmask = parv[4]; 4174 const char *reason = parv[8]; 4175 4176 /* Some simple validation on usermask and hostmask: 4177 * may not contain an @. Yeah, some services or self-written 4178 * linked servers are known to have sent this in the past. 4179 */ 4180 if (strchr(usermask, '@') || strchr(hostmask, '@')) 4181 { 4182 unreal_log(ULOG_WARNING, "tkl", "TKL_ADD_INVALID", client, 4183 "Invalid TKL entry from $client: " 4184 "Invalid user@host $usermask@$hostmask", 4185 log_data_string("usermask", usermask), 4186 log_data_string("hostmask", hostmask)); 4187 return; 4188 } 4189 4190 /* In case of a soft ban, strip the percent sign early, 4191 * so parv[3] (username) is really the username without any prefix. 4192 * Set the 'softban' flag if this is the case. 4193 */ 4194 if (*usermask == '%') 4195 { 4196 usermask++; 4197 softban = 1; 4198 } 4199 4200 tkl = find_tkl_serverban(type, usermask, hostmask, softban); 4201 if (tkl) 4202 { 4203 tkl_entry_exists = 1; 4204 } else { 4205 tkl = tkl_add_serverban(type, usermask, hostmask, reason, 4206 set_by, expire_at, set_at, softban, 0); 4207 } 4208 } else 4209 if (TKLIsBanExceptionType(type)) 4210 { 4211 /* Validate ban exception TKL fields */ 4212 int softban = 0; 4213 const char *usermask = parv[3]; 4214 const char *hostmask = parv[4]; 4215 const char *bantypes = parv[8]; 4216 const char *reason; 4217 4218 if (parc < 10) 4219 return; 4220 4221 reason = parv[9]; 4222 4223 /* Some simple validation on usermask and hostmask: 4224 * may not contain an @. Yeah, some services or self-written 4225 * linked servers are known to have sent this in the past. 4226 */ 4227 if (strchr(usermask, '@') || strchr(hostmask, '@')) 4228 { 4229 unreal_log(ULOG_WARNING, "tkl", "TKL_ADD_INVALID", client, 4230 "Invalid TKL entry from $client: " 4231 "Invalid TKL except user@host $usermask@$hostmask", 4232 log_data_string("usermask", usermask), 4233 log_data_string("hostmask", hostmask)); 4234 return; 4235 } 4236 4237 /* In case of a soft ban, strip the percent sign early, 4238 * so parv[3] (username) is really the username without any prefix. 4239 * Set the 'softban' flag if this is the case. 4240 */ 4241 if (*usermask == '%') 4242 { 4243 usermask++; 4244 softban = 1; 4245 } 4246 4247 /* At this moment we do not validate 'bantypes' since a missing 4248 * or wrong type does not cause harm anyway. 4249 */ 4250 tkl = find_tkl_banexception(type, usermask, hostmask, softban); 4251 if (tkl) 4252 { 4253 tkl_entry_exists = 1; 4254 } else { 4255 tkl = tkl_add_banexception(type, usermask, hostmask, NULL, reason, 4256 set_by, expire_at, set_at, softban, bantypes, 0); 4257 } 4258 } else 4259 if (TKLIsNameBanType(type)) 4260 { 4261 /* Validate name ban TKL fields */ 4262 int hold = 0; 4263 const char *name = parv[4]; 4264 const char *reason = parv[8]; 4265 4266 if (*parv[3] == 'H') 4267 hold = 1; 4268 4269 tkl = find_tkl_nameban(type, name, hold); 4270 if (tkl) 4271 { 4272 tkl_entry_exists = 1; 4273 } else { 4274 tkl = tkl_add_nameban(type, name, hold, reason, set_by, expire_at, 4275 set_at, 0); 4276 } 4277 } else 4278 if (TKLIsSpamfilterType(type)) 4279 { 4280 /* Validate spamfilter-specific TKL fields */ 4281 MatchType match_method; 4282 const char *match_string; 4283 Match *m; /* compiled match_string */ 4284 time_t tkl_duration; 4285 const char *tkl_reason; 4286 BanAction action; 4287 unsigned short target; 4288 /* helper variables */ 4289 char *err; 4290 4291 if (parc < 12) 4292 { 4293 unreal_log(ULOG_WARNING, "tkl", "TKL_ADD_INVALID", client, 4294 "Invalid TKL entry from $client: " 4295 "Spamfilter with too few parameters. Running very old UnrealIRCd protocol (3.2.X?)"); 4296 return; 4297 } 4298 4299 match_string = parv[11]; 4300 4301 match_method = unreal_match_method_strtoval(parv[10]); 4302 if (match_method == 0) 4303 { 4304 unreal_log(ULOG_WARNING, "tkl", "TKL_ADD_INVALID", client, 4305 "Invalid TKL entry from $client: " 4306 "Spamfilter '$spamfilter_string' has unknown match-type '$spamfilter_type'", 4307 log_data_string("spamfilter_string", match_string), 4308 log_data_string("spamfilter_type", parv[10])); 4309 return; 4310 } 4311 4312 if (!(target = spamfilter_gettargets(parv[3], NULL))) 4313 { 4314 unreal_log(ULOG_WARNING, "tkl", "TKL_ADD_INVALID", client, 4315 "Invalid TKL entry from $client: " 4316 "Spamfilter '$spamfilter_string' has unknown targets '$spamfilter_targets'", 4317 log_data_string("spamfilter_string", match_string), 4318 log_data_string("spamfilter_targets", parv[3])); 4319 return; 4320 } 4321 4322 if (!(action = banact_chartoval(*parv[4]))) 4323 { 4324 unreal_log(ULOG_WARNING, "tkl", "TKL_ADD_INVALID", client, 4325 "Invalid TKL entry from $client: " 4326 "Spamfilter '$spamfilter_string' has unknown action '$spamfilter_action'", 4327 log_data_string("spamfilter_string", match_string), 4328 log_data_string("spamfilter_action", parv[4])); 4329 return; 4330 } 4331 4332 tkl_duration = config_checkval(parv[8], CFG_TIME); 4333 tkl_reason = parv[9]; 4334 4335 tkl = find_tkl_spamfilter(type, match_string, action, target); 4336 4337 if (tkl) 4338 { 4339 tkl_entry_exists = 1; 4340 } else { 4341 m = unreal_create_match(match_method, match_string, &err); 4342 if (!m) 4343 { 4344 unreal_log(ULOG_WARNING, "tkl", "TKL_ADD_INVALID", client, 4345 "Invalid TKL entry from $client: " 4346 "Spamfilter '$spamfilter_string': regex does not compile: $spamfilter_regex_error", 4347 log_data_string("spamfilter_string", match_string), 4348 log_data_string("spamfilter_regex_error", err)); 4349 return; 4350 } 4351 tkl = tkl_add_spamfilter(type, target, action, m, set_by, expire_at, set_at, 4352 tkl_duration, tkl_reason, 0); 4353 } 4354 } else 4355 { 4356 /* Unhandled, should never happen */ 4357 abort(); 4358 } 4359 4360 if (!tkl) 4361 return; 4362 4363 if (tkl_entry_exists) 4364 { 4365 /* Let's see if we need to update the existing entry. 4366 * Note that we only update common fields, 4367 * which is acceptable to me. -- Syzop 4368 */ 4369 if ((set_at < tkl->set_at) || (expire_at != tkl->expire_at) || strcmp(tkl->set_by, parv[5])) 4370 { 4371 /* here's how it goes: 4372 * set_at: oldest wins 4373 * expire_at: longest wins 4374 * set_by: highest strcmp wins 4375 * 4376 * We broadcast the result of this back to all servers except 4377 * sptr->direction, because that side will do the same thing and 4378 * send it back to his servers (except us)... no need for a 4379 * double networkwide flood ;p. -- Syzop 4380 */ 4381 tkl->set_at = MIN(tkl->set_at, set_at); 4382 4383 if (!tkl->expire_at || !expire_at) 4384 tkl->expire_at = 0; 4385 else 4386 tkl->expire_at = MAX(tkl->expire_at, expire_at); 4387 4388 if (strcmp(tkl->set_by, parv[5]) < 0) 4389 safe_strdup(tkl->set_by, parv[5]); 4390 4391 if (type & TKL_GLOBAL) 4392 tkl_broadcast_entry(1, client, client, tkl); 4393 } 4394 return; 4395 } 4396 4397 tkl_added(client, tkl); 4398 } 4399 4400 /** Delete a TKL using the TKL layer. See cmd_tkl for parv[] and protocol documentation. */ 4401 CMD_FUNC(cmd_tkl_del) 4402 { 4403 TKL *tkl; 4404 int type; 4405 const char *removed_by; 4406 4407 if (!IsServer(client) && !IsMe(client)) 4408 return; 4409 4410 if (parc < 6) 4411 return; 4412 4413 type = tkl_chartotype(parv[2][0]); 4414 if (type == 0) 4415 return; 4416 4417 removed_by = parv[5]; 4418 4419 if (TKLIsServerBanType(type)) 4420 { 4421 const char *usermask = parv[3]; 4422 const char *hostmask = parv[4]; 4423 int softban = 0; 4424 4425 if (*usermask == '%') 4426 { 4427 usermask++; 4428 softban = 1; 4429 } 4430 4431 tkl = find_tkl_serverban(type, usermask, hostmask, softban); 4432 } 4433 else if (TKLIsBanExceptionType(type)) 4434 { 4435 const char *usermask = parv[3]; 4436 const char *hostmask = parv[4]; 4437 int softban = 0; 4438 /* other parameters are ignored */ 4439 4440 if (*usermask == '%') 4441 { 4442 usermask++; 4443 softban = 1; 4444 } 4445 4446 tkl = find_tkl_banexception(type, usermask, hostmask, softban); 4447 } 4448 else if (TKLIsNameBanType(type)) 4449 { 4450 int hold = 0; 4451 const char *name = parv[4]; 4452 4453 if (*parv[3] == 'H') 4454 hold = 1; 4455 tkl = find_tkl_nameban(type, name, hold); 4456 } 4457 else if (TKLIsSpamfilterType(type)) 4458 { 4459 const char *match_string; 4460 unsigned short target; 4461 BanAction action; 4462 4463 if (parc < 9) 4464 { 4465 unreal_log(ULOG_WARNING, "tkl", "TKL_DEL_INVALID", client, 4466 "Invalid TKL deletion request from $client: " 4467 "Spamfilter with too few parameters. Running very old UnrealIRCd protocol (3.2.X?)"); 4468 return; /* bogus */ 4469 } 4470 if (parc >= 12) 4471 match_string = parv[11]; 4472 else if (parc >= 11) 4473 match_string = parv[10]; 4474 else 4475 match_string = parv[8]; 4476 4477 if (!(target = spamfilter_gettargets(parv[3], NULL))) 4478 { 4479 unreal_log(ULOG_WARNING, "tkl", "TKL_DEL_INVALID", client, 4480 "Invalid TKL deletion request from $client: " 4481 "Spamfilter '$spamfilter_string' has unknown targets '$spamfilter_targets'", 4482 log_data_string("spamfilter_string", match_string), 4483 log_data_string("spamfilter_targets", parv[3])); 4484 return; 4485 } 4486 4487 if (!(action = banact_chartoval(*parv[4]))) 4488 { 4489 unreal_log(ULOG_WARNING, "tkl", "TKL_DEL_INVALID", client, 4490 "Invalid TKL deletion request from $client: " 4491 "Spamfilter '$spamfilter_string' has unknown action '$spamfilter_action'", 4492 log_data_string("spamfilter_string", match_string), 4493 log_data_string("spamfilter_action", parv[4])); 4494 return; 4495 } 4496 tkl = find_tkl_spamfilter(type, match_string, action, target); 4497 } else 4498 { 4499 /* This can never happen, unless someone added a TKL type 4500 * to UnrealIRCd but forgot to add the removal code :D. 4501 */ 4502 abort(); 4503 } 4504 4505 if (!tkl) 4506 return; /* Item not found, nothing to remove. */ 4507 4508 if (tkl->flags & TKL_FLAG_CONFIG) 4509 return; /* Item is in the configuration file (persistent) */ 4510 4511 /* broadcast remove msg to opers... */ 4512 sendnotice_tkl_del(removed_by, tkl); 4513 4514 if (type & TKL_SHUN) 4515 tkl_check_local_remove_shun(tkl); 4516 4517 RunHook(HOOKTYPE_TKL_DEL, client, tkl); 4518 4519 if (type & TKL_GLOBAL) 4520 { 4521 /* This is a bit of a hack for #5629. Will consider real fix post-release. */ 4522 safe_strdup(tkl->set_by, removed_by); 4523 tkl_broadcast_entry(0, client, client, tkl); 4524 } 4525 4526 if (TKLIsBanException(tkl)) 4527 { 4528 /* Since an exception has been removed we have to re-check if 4529 * any connected user is now matched by a ban. 4530 * Set flag here, actual checking takes place in main loop. 4531 */ 4532 loop.do_bancheck = 1; 4533 } 4534 4535 tkl_del_line(tkl); 4536 } 4537 4538 /** TKL command: server to server handling of *LINEs and SPAMFILTERs. 4539 * HISTORY: 4540 * This was originall called Timed KLines, but today it's 4541 * used by various *line types eg: zline, gline, gzline, shun, 4542 * but also by spamfilter etc... 4543 * DOCUMENTATION 4544 * See (also) https://www.unrealircd.org/docs/Server_protocol:TKL_command 4545 * USAGE: 4546 * This routine is used both internally by the ircd (to 4547 * for example add local klines, zlines, etc) and over the 4548 * network (glines, gzlines, spamfilter, etc). 4549 * 4550 * serverban serverban spamfilter spamfilter sqline: ban exception: 4551 * add: remove: remove in U4: with TKLEXT2: 4552 * parv[ 1]: + - - +/- +/- +/- 4553 * parv[ 2]: type type type type type type 4554 * parv[ 3]: user user target target hold user 4555 * parv[ 4]: host host action action host host 4556 * parv[ 5]: set_by removedby (un)set_by set_by/unset_by set_by set_by 4557 * parv[ 6]: expire_at expire_at (0) expire_at (0) expire_at expire_at 4558 * parv[ 7]: set_at set_at set_at set_at set_at 4559 * parv[ 8]: reason regex tkl duration reason except_type 4560 * parv[ 9]: tkl reason [A] reason 4561 * parv[10]: match-type [B] 4562 * parv[11]: match-string [C] 4563 * 4564 * [A] tkl reason field must be escaped by caller [eg: use unreal_encodespace() 4565 * if cmd_tkl is called internally]. 4566 * [B] match-type must be one of: regex, simple. 4567 * [C] Could be a regex or a regular string with wildcards, depending on [B] 4568 */ 4569 CMD_FUNC(_cmd_tkl) 4570 { 4571 if (!IsServer(client) && !IsOper(client) && !IsMe(client)) 4572 return; 4573 4574 if (parc < 2) 4575 return; 4576 4577 switch (*parv[1]) 4578 { 4579 case '+': 4580 CALL_CMD_FUNC(cmd_tkl_add); 4581 break; 4582 case '-': 4583 CALL_CMD_FUNC(cmd_tkl_del); 4584 break; 4585 default: 4586 break; 4587 } 4588 } 4589 4590 /** Configure the username/hostname TKL layer based on the BAN_TARGET_* configuration */ 4591 void ban_target_to_tkl_layer(BanTarget ban_target, BanAction action, Client *client, const char **tkl_username, const char **tkl_hostname) 4592 { 4593 static char username[USERLEN+1]; 4594 static char hostname[HOSTLEN+8]; 4595 4596 if ((action == BAN_ACT_ZLINE) || (action == BAN_ACT_GZLINE)) 4597 ban_target = BAN_TARGET_IP; /* The only possible choice with ZLINE/GZLINE, other info is unavailable */ 4598 4599 if (ban_target == BAN_TARGET_ACCOUNT) 4600 { 4601 if (IsLoggedIn(client) && (*client->user->account != ':')) 4602 { 4603 /* Place a ban on ~a:Accountname */ 4604 strlcpy(username, "~a:", sizeof(username)); 4605 strlcpy(hostname, client->user->account, sizeof(hostname)); 4606 *tkl_username = username; 4607 *tkl_hostname = hostname; 4608 return; 4609 } 4610 ban_target = BAN_TARGET_IP; /* fallback */ 4611 } else 4612 if (ban_target == BAN_TARGET_CERTFP) 4613 { 4614 const char *fp = moddata_client_get(client, "certfp"); 4615 if (fp) 4616 { 4617 /* Place a ban on ~S:sha256sumofclientcertificate */ 4618 strlcpy(username, "~S:", sizeof(username)); 4619 strlcpy(hostname, fp, sizeof(hostname)); 4620 *tkl_username = username; 4621 *tkl_hostname = hostname; 4622 return; 4623 } 4624 ban_target = BAN_TARGET_IP; /* fallback */ 4625 } 4626 4627 /* Below we deal with the more common choices... */ 4628 4629 /* First, set the username */ 4630 if (((ban_target == BAN_TARGET_USERIP) || (ban_target == BAN_TARGET_USERHOST)) && strcmp(client->ident, "unknown")) 4631 strlcpy(username, client->ident, sizeof(username)); 4632 else 4633 strlcpy(username, "*", sizeof(username)); 4634 4635 /* Now set the host-portion of the TKL */ 4636 if (((ban_target == BAN_TARGET_HOST) || (ban_target == BAN_TARGET_USERHOST)) && client->user && *client->user->realhost) 4637 strlcpy(hostname, client->user->realhost, sizeof(hostname)); 4638 else 4639 strlcpy(hostname, GetIP(client), sizeof(hostname)); 4640 4641 *tkl_username = username; 4642 *tkl_hostname = hostname; 4643 } 4644 4645 /** Take an action on the user, such as banning or killing. 4646 * @author Bram Matthys (Syzop), 2003-present 4647 * @param client The client which is affected. 4648 * @param action The type of ban (one of BAN_ACT_*). 4649 * @param reason The ban reason. 4650 * @param duration The ban duration in seconds. 4651 * @note This function assumes that client is a locally connected user. 4652 * @retval 1 if action is taken, 0 if user is exempted. 4653 * @note Be sure to check IsDead(client) if return value is 1 and you are 4654 * considering to continue processing. 4655 */ 4656 int _place_host_ban(Client *client, BanAction action, char *reason, long duration) 4657 { 4658 /* If this is a soft action and the user is logged in, then the ban does not apply. 4659 * NOTE: Actually in such a case it would be better if place_host_ban() would not 4660 * be called at all. Or at least, the caller should not take any action 4661 * (eg: the message should be delivered, the user may connect, etc..) 4662 * The following is more like secondary protection in case the caller forgets... 4663 */ 4664 if (IsSoftBanAction(action) && IsLoggedIn(client)) 4665 return 0; 4666 4667 switch(action) 4668 { 4669 case BAN_ACT_TEMPSHUN: 4670 /* We simply mark this connection as shunned and do not add a ban record */ 4671 unreal_log(ULOG_INFO, "tkl", "TKL_ADD_TEMPSHUN", &me, 4672 "Temporary shun added on user $target.details [reason: $shun_reason] [by: $client]", 4673 log_data_string("shun_reason", reason), 4674 log_data_client("target", client)); 4675 SetShunned(client); 4676 return 1; 4677 case BAN_ACT_GZLINE: 4678 case BAN_ACT_GLINE: 4679 case BAN_ACT_SOFT_GLINE: 4680 case BAN_ACT_ZLINE: 4681 case BAN_ACT_KLINE: 4682 case BAN_ACT_SOFT_KLINE: 4683 case BAN_ACT_SHUN: 4684 case BAN_ACT_SOFT_SHUN: 4685 { 4686 char ip[128], user[USERLEN+3], mo[100], mo2[100]; 4687 const char *tkllayer[9] = { 4688 me.name, /*0 server.name */ 4689 "+", /*1 +|- */ 4690 "?", /*2 type */ 4691 "*", /*3 user */ 4692 NULL, /*4 host */ 4693 NULL, 4694 NULL, /*6 expire_at */ 4695 NULL, /*7 set_at */ 4696 NULL /*8 reason */ 4697 }; 4698 4699 ban_target_to_tkl_layer(iConf.automatic_ban_target, action, client, &tkllayer[3], &tkllayer[4]); 4700 4701 /* For soft bans we need to prefix the % in the username */ 4702 if (IsSoftBanAction(action)) 4703 { 4704 char tmp[USERLEN+3]; 4705 snprintf(tmp, sizeof(tmp), "%%%s", tkllayer[3]); 4706 strlcpy(user, tmp, sizeof(user)); 4707 tkllayer[3] = user; 4708 } 4709 4710 if ((action == BAN_ACT_KLINE) || (action == BAN_ACT_SOFT_KLINE)) 4711 tkllayer[2] = "k"; 4712 else if (action == BAN_ACT_ZLINE) 4713 tkllayer[2] = "z"; 4714 else if (action == BAN_ACT_GZLINE) 4715 tkllayer[2] = "Z"; 4716 else if ((action == BAN_ACT_GLINE) || (action == BAN_ACT_SOFT_GLINE)) 4717 tkllayer[2] = "G"; 4718 else if ((action == BAN_ACT_SHUN) || (action == BAN_ACT_SOFT_SHUN)) 4719 tkllayer[2] = "s"; 4720 tkllayer[5] = me.name; 4721 if (!duration) 4722 strlcpy(mo, "0", sizeof(mo)); /* perm */ 4723 else 4724 ircsnprintf(mo, sizeof(mo), "%lld", (long long)(duration + TStime())); 4725 ircsnprintf(mo2, sizeof(mo2), "%lld", (long long)TStime()); 4726 tkllayer[6] = mo; 4727 tkllayer[7] = mo2; 4728 tkllayer[8] = reason; 4729 cmd_tkl(&me, NULL, 9, tkllayer); 4730 RunHookReturnInt(HOOKTYPE_PLACE_HOST_BAN, !=99, client, action, reason, duration); 4731 if ((action == BAN_ACT_SHUN) || (action == BAN_ACT_SOFT_SHUN)) 4732 { 4733 find_shun(client); 4734 return 1; 4735 } /* else.. */ 4736 return find_tkline_match(client, 0); 4737 } 4738 case BAN_ACT_SOFT_KILL: 4739 case BAN_ACT_KILL: 4740 default: 4741 RunHookReturnInt(HOOKTYPE_PLACE_HOST_BAN, !=99, client, action, reason, duration); 4742 exit_client(client, NULL, reason); 4743 return 1; 4744 } 4745 return 0; /* no action taken (weird) */ 4746 } 4747 4748 /** This function compares two spamfilters ('one' and 'two') and will return 4749 * a 'winner' based on which one has the strongest action. 4750 * If both have equal action then some additional logic is applied simply 4751 * to ensure we (almost) always return the same winner regardless of the 4752 * order of the spamfilters (which may differ between servers). 4753 */ 4754 TKL *choose_winning_spamfilter(TKL *one, TKL *two) 4755 { 4756 int n; 4757 4758 if (!TKLIsSpamfilter(one) || !TKLIsSpamfilter(two)) 4759 abort(); 4760 4761 /* First, see if the action field differs... */ 4762 if (one->ptr.spamfilter->action != two->ptr.spamfilter->action) 4763 { 4764 /* We can simply compare the action. Highest (strongest) wins. */ 4765 if (one->ptr.spamfilter->action > two->ptr.spamfilter->action) 4766 return one; 4767 else 4768 return two; 4769 } 4770 4771 /* Ok, try comparing the regex then.. */ 4772 n = strcmp(one->ptr.spamfilter->match->str, two->ptr.spamfilter->match->str); 4773 if (n < 0) 4774 return one; 4775 if (n > 0) 4776 return two; 4777 4778 /* Hmm.. regex is identical. Try the 'reason' field. */ 4779 n = strcmp(one->ptr.spamfilter->tkl_reason, two->ptr.spamfilter->tkl_reason); 4780 if (n < 0) 4781 return one; 4782 if (n > 0) 4783 return two; 4784 4785 /* Hmm.. 'reason' is identical as well. 4786 * Make a final decision, could still be identical but would be unlikely. 4787 */ 4788 return (one->ptr.spamfilter->target > two->ptr.spamfilter->target) ? one : two; 4789 } 4790 4791 /** Checks if 'target' is on the spamfilter exception list. 4792 * RETURNS 1 if found in list, 0 if not. 4793 */ 4794 static int target_is_spamexcept(const char *target) 4795 { 4796 SpamExcept *e; 4797 4798 for (e = iConf.spamexcept; e; e = e->next) 4799 { 4800 if (match_simple(e->name, target)) 4801 return 1; 4802 } 4803 return 0; 4804 } 4805 4806 /** Make user join the virus channel. 4807 * @param client The user that was doing something bad. 4808 * @param tk The TKL entry that matched this user. 4809 * @param type The spamfilter type (SPAMF_*) 4810 * TODO: Looks redundant? 4811 */ 4812 int _join_viruschan(Client *client, TKL *tkl, int type) 4813 { 4814 const char *xparv[3]; 4815 char chbuf[CHANNELLEN + 16], buf[2048]; 4816 Channel *channel; 4817 int ret; 4818 4819 snprintf(buf, sizeof(buf), "0,%s", SPAMFILTER_VIRUSCHAN); 4820 xparv[0] = NULL; 4821 xparv[1] = buf; 4822 xparv[2] = NULL; 4823 4824 /* RECURSIVE CAUTION in case we ever add blacklisted chans */ 4825 spamf_ugly_vchanoverride = 1; 4826 do_cmd(client, NULL, "JOIN", 2, xparv); 4827 spamf_ugly_vchanoverride = 0; 4828 4829 if (IsDead(client)) 4830 return 0; /* killed due to JOIN */ 4831 4832 sendnotice(client, "You are now restricted to talking in %s: %s", 4833 SPAMFILTER_VIRUSCHAN, unreal_decodespace(tkl->ptr.spamfilter->tkl_reason)); 4834 4835 channel = find_channel(SPAMFILTER_VIRUSCHAN); 4836 if (channel) 4837 { 4838 MessageTag *mtags = NULL; 4839 ircsnprintf(chbuf, sizeof(chbuf), "@%s", channel->name); 4840 ircsnprintf(buf, sizeof(buf), "[Spamfilter] %s matched filter '%s' [%s] [%s]", 4841 client->name, tkl->ptr.spamfilter->match->str, cmdname_by_spamftarget(type), 4842 unreal_decodespace(tkl->ptr.spamfilter->tkl_reason)); 4843 new_message(&me, NULL, &mtags); 4844 sendto_channel(channel, &me, NULL, "o", 4845 0, SEND_ALL|SKIP_DEAF, mtags, 4846 ":%s NOTICE %s :%s", me.name, chbuf, buf); 4847 free_message_tags(mtags); 4848 } 4849 SetVirus(client); 4850 return 1; 4851 } 4852 4853 /** match_spamfilter: executes the spamfilter on the input string. 4854 * @param str The text (eg msg text, notice text, part text, quit text, etc 4855 * @param target The spamfilter target (SPAMF_*) 4856 * @param cmd The command (eg: "PRIVMSG") 4857 * @param destination The destination as a text string (eg: "somenick", can be NULL.. eg for away) 4858 * @param flags Any flags (SPAMFLAG_*) 4859 * @param rettkl Pointer to an aTKLline struct, _used for special circumstances only_ 4860 * RETURN VALUE: 4861 * 1 if spamfilter matched and it should be blocked (or client exited), 0 if not matched. 4862 * In case of 1, be sure to check IsDead(client).. 4863 */ 4864 int _match_spamfilter(Client *client, const char *str_in, int target, const char *cmd, const char *destination, int flags, TKL **rettkl) 4865 { 4866 TKL *tkl; 4867 TKL *winner_tkl = NULL; 4868 const char *str; 4869 int ret = -1; 4870 char *reason = NULL; 4871 #ifdef SPAMFILTER_DETECTSLOW 4872 struct rusage rnow, rprev; 4873 long ms_past; 4874 #endif 4875 4876 if (rettkl) 4877 *rettkl = NULL; /* initialize to NULL */ 4878 4879 if (!cmd) 4880 cmd = cmdname_by_spamftarget(target); 4881 4882 if (target == SPAMF_USER) 4883 str = str_in; 4884 else 4885 str = StripControlCodes(str_in); 4886 4887 /* (note: using client->user check here instead of IsUser() 4888 * due to SPAMF_USER where user isn't marked as client/person yet. 4889 */ 4890 if (!client->user || ValidatePermissionsForPath("immune:server-ban:spamfilter",client,NULL,NULL,NULL) || IsULine(client)) 4891 return 0; 4892 4893 /* Client exempt from spamfilter checking? 4894 * Let's check that early: going through elines is likely faster than running the regex(es). 4895 */ 4896 if (find_tkl_exception(TKL_SPAMF, client)) 4897 return 0; 4898 4899 for (tkl = tklines[tkl_hash('F')]; tkl; tkl = tkl->next) 4900 { 4901 if (!(tkl->ptr.spamfilter->target & target)) 4902 continue; 4903 4904 if ((flags & SPAMFLAG_NOWARN) && (tkl->ptr.spamfilter->action == BAN_ACT_WARN)) 4905 continue; 4906 4907 /* If the action is 'soft' (for non-logged in users only) then 4908 * don't bother running the spamfilter if the user is logged in. 4909 */ 4910 if (IsSoftBanAction(tkl->ptr.spamfilter->action) && IsLoggedIn(client)) 4911 continue; 4912 4913 #ifdef SPAMFILTER_DETECTSLOW 4914 memset(&rnow, 0, sizeof(rnow)); 4915 memset(&rprev, 0, sizeof(rnow)); 4916 4917 getrusage(RUSAGE_SELF, &rprev); 4918 #endif 4919 4920 ret = unreal_match(tkl->ptr.spamfilter->match, str); 4921 4922 #ifdef SPAMFILTER_DETECTSLOW 4923 getrusage(RUSAGE_SELF, &rnow); 4924 4925 ms_past = ((rnow.ru_utime.tv_sec - rprev.ru_utime.tv_sec) * 1000) + 4926 ((rnow.ru_utime.tv_usec - rprev.ru_utime.tv_usec) / 1000); 4927 4928 if ((SPAMFILTER_DETECTSLOW_FATAL > 0) && (ms_past > SPAMFILTER_DETECTSLOW_FATAL)) 4929 { 4930 unreal_log(ULOG_ERROR, "tkl", "SPAMFILTER_SLOW_FATAL", NULL, 4931 "[Spamfilter] WARNING: Too slow spamfilter detected (took $msec_time msec to execute) " 4932 "-- spamfilter will be \002REMOVED!\002: $tkl", 4933 log_data_tkl("tkl", tkl), 4934 log_data_integer("msec_time", ms_past)); 4935 tkl_del_line(tkl); 4936 return 0; /* Act as if it didn't match, even if it did.. it's gone now anyway.. */ 4937 } else 4938 if ((SPAMFILTER_DETECTSLOW_WARN > 0) && (ms_past > SPAMFILTER_DETECTSLOW_WARN)) 4939 { 4940 unreal_log(ULOG_WARNING, "tkl", "SPAMFILTER_SLOW_WARN", NULL, 4941 "[Spamfilter] WARNING: Slow spamfilter detected (took $msec_time msec to execute): $tkl", 4942 log_data_tkl("tkl", tkl), 4943 log_data_integer("msec_time", ms_past)); 4944 } 4945 #endif 4946 4947 if (ret) 4948 { 4949 /* We have a match! But.. perhaps it's on the exceptions list? */ 4950 if (!winner_tkl && destination && target_is_spamexcept(destination)) 4951 return 0; /* No problem! */ 4952 4953 unreal_log(ULOG_INFO, "tkl", "SPAMFILTER_MATCH", client, 4954 "[Spamfilter] $client.details matches filter '$tkl': [cmd: $command$_space$destination: '$str'] [reason: $tkl.reason] [action: $tkl.ban_action]", 4955 log_data_tkl("tkl", tkl), 4956 log_data_string("command", cmd), 4957 log_data_string("_space", destination ? " " : ""), 4958 log_data_string("destination", destination ? destination : ""), 4959 log_data_string("str", str)); 4960 4961 RunHook(HOOKTYPE_LOCAL_SPAMFILTER, client, str, str_in, target, destination, tkl); 4962 4963 /* If we should stop after the first match, we end here... */ 4964 if (SPAMFILTER_STOP_ON_FIRST_MATCH) 4965 { 4966 winner_tkl = tkl; 4967 break; 4968 } 4969 4970 /* Otherwise.. we set 'winner_tkl' to the spamfilter with the strongest action. */ 4971 if (!winner_tkl) 4972 winner_tkl = tkl; 4973 else 4974 winner_tkl = choose_winning_spamfilter(tkl, winner_tkl); 4975 4976 /* and continue.. */ 4977 } 4978 } 4979 4980 tkl = winner_tkl; 4981 4982 if (!tkl) 4983 return 0; /* NOMATCH, we are done */ 4984 4985 /* Spamfilter matched, take action: */ 4986 4987 reason = unreal_decodespace(tkl->ptr.spamfilter->tkl_reason); 4988 if ((tkl->ptr.spamfilter->action == BAN_ACT_BLOCK) || (tkl->ptr.spamfilter->action == BAN_ACT_SOFT_BLOCK)) 4989 { 4990 switch(target) 4991 { 4992 case SPAMF_USERMSG: 4993 case SPAMF_USERNOTICE: 4994 { 4995 char errmsg[512]; 4996 ircsnprintf(errmsg, sizeof(errmsg), "Message blocked: %s", reason); 4997 sendnumeric(client, ERR_CANTSENDTOUSER, destination, errmsg); 4998 break; 4999 } 5000 case SPAMF_CHANNOTICE: 5001 break; /* no replies to notices */ 5002 case SPAMF_CHANMSG: 5003 { 5004 sendto_one(client, NULL, ":%s 404 %s %s :Message blocked: %s", 5005 me.name, client->name, destination, reason); 5006 break; 5007 } 5008 case SPAMF_MTAG: 5009 { 5010 sendnumericfmt(client, ERR_CANNOTDOCOMMAND, "%s :Command blocked: %s", 5011 cmd, reason); 5012 break; 5013 } 5014 case SPAMF_DCC: 5015 { 5016 char errmsg[512]; 5017 ircsnprintf(errmsg, sizeof(errmsg), "DCC blocked: %s", reason); 5018 sendnumeric(client, ERR_CANTSENDTOUSER, destination, errmsg); 5019 break; 5020 } 5021 case SPAMF_AWAY: 5022 /* hack to deal with 'after-away-was-set-filters' */ 5023 if (client->user->away && !strcmp(str_in, client->user->away)) 5024 { 5025 /* free away & broadcast the unset */ 5026 safe_free(client->user->away); 5027 client->user->away = NULL; 5028 sendto_server(client, 0, 0, NULL, ":%s AWAY", client->id); 5029 } 5030 break; 5031 case SPAMF_TOPIC: 5032 //... 5033 sendnotice(client, "Setting of topic on %s to that text is blocked: %s", 5034 destination, reason); 5035 break; 5036 default: 5037 break; 5038 } 5039 return 1; 5040 } else 5041 if ((tkl->ptr.spamfilter->action == BAN_ACT_WARN) || (tkl->ptr.spamfilter->action == BAN_ACT_SOFT_WARN)) 5042 { 5043 if ((target != SPAMF_USER) && (target != SPAMF_QUIT)) 5044 sendnumeric(client, RPL_SPAMCMDFWD, cmd, reason); 5045 return 0; 5046 } else 5047 if ((tkl->ptr.spamfilter->action == BAN_ACT_DCCBLOCK) || (tkl->ptr.spamfilter->action == BAN_ACT_SOFT_DCCBLOCK)) 5048 { 5049 if (target == SPAMF_DCC) 5050 { 5051 sendnotice(client, "DCC to %s blocked: %s", destination, reason); 5052 sendnotice(client, "*** You have been blocked from sending files, reconnect to regain permission to send files"); 5053 SetDCCBlock(client); 5054 } 5055 return 1; 5056 } else 5057 if ((tkl->ptr.spamfilter->action == BAN_ACT_VIRUSCHAN) || (tkl->ptr.spamfilter->action == BAN_ACT_SOFT_VIRUSCHAN)) 5058 { 5059 if (IsVirus(client)) /* Already tagged */ 5060 return 0; 5061 5062 /* There's a race condition for SPAMF_USER, so 'rettk' is used for SPAMF_USER 5063 * when a user is currently connecting and filters are checked: 5064 */ 5065 if (!IsUser(client)) 5066 { 5067 if (rettkl) 5068 *rettkl = tkl; 5069 return 1; 5070 } 5071 5072 join_viruschan(client, tkl, target); 5073 return 1; 5074 } else 5075 { 5076 return place_host_ban(client, tkl->ptr.spamfilter->action, reason, tkl->ptr.spamfilter->tkl_duration); 5077 } 5078 5079 return 0; /* NOTREACHED */ 5080 } 5081 5082 /** Check message-tag spamfilters. 5083 * @param client The client 5084 * @param mtags Message tags sent by client 5085 * @param cmd Command to be executed (can be NULL) 5086 * @retval Return 1 to stop processing the command (ignore it) or 0 to allow/continue as normal 5087 */ 5088 int _match_spamfilter_mtags(Client *client, MessageTag *mtags, char *cmd) 5089 { 5090 MessageTag *m; 5091 char buf[4096]; 5092 char *str; 5093 5094 /* This is a shortcut: if there are no spamfilters present 5095 * on message tags then we can return immediately. 5096 * Saves a lot of CPU and it is quite likely too! 5097 */ 5098 if (mtag_spamfilters_present == 0) 5099 return 0; 5100 5101 for (m = mtags; m; m = m->next) 5102 { 5103 if (m->value) 5104 { 5105 snprintf(buf, sizeof(buf), "%s=%s", m->name, m->value); 5106 str = buf; 5107 } else { 5108 str = m->name; 5109 } 5110 if (match_spamfilter(client, str, SPAMF_MTAG, cmd, NULL, 0, NULL)) 5111 return 1; 5112 } 5113 return 0; 5114 } 5115 5116 /** Updates 'mtag_spamfilters_present' based on if any spamfilters 5117 * are present with the SPAMF_MTAG target. 5118 */ 5119 int check_mtag_spamfilters_present(void) 5120 { 5121 TKL *tkl; 5122 5123 for (tkl = tklines[tkl_hash('F')]; tkl; tkl = tkl->next) 5124 { 5125 if (tkl->ptr.spamfilter->target & SPAMF_MTAG) 5126 { 5127 mtag_spamfilters_present = 1; 5128 return 1; 5129 } 5130 } 5131 5132 mtag_spamfilters_present = 0; 5133 return 0; 5134 } 5135 5136 /** CIDR function to compare the first 'mask' bits. 5137 * @author Taken from atheme 5138 * @returns 1 if equal, 0 if not. 5139 */ 5140 static int comp_with_mask(void *addr, void *dest, u_int mask) 5141 { 5142 if (memcmp(addr, dest, mask / 8) == 0) 5143 { 5144 int n = mask / 8; 5145 int m = (0xffff << (8 - (mask % 8))); 5146 if (mask % 8 == 0 || (((u_char *) addr)[n] & m) == (((u_char *) dest)[n] & m)) 5147 { 5148 return (1); 5149 } 5150 } 5151 return (0); 5152 } 5153 5154 #define IPSZ 16 5155 5156 /** Match a user against a mask. 5157 * This will deal with 'nick!user@host', 'user@host' and just 'host'. 5158 * We try to match the 'host' portion against the client IP, real host, etc... 5159 * CIDR support is available so 'host' may be like '1.2.0.0/16'. 5160 * @returns 1 on match, 0 on no match. 5161 */ 5162 int _match_user(const char *rmask, Client *client, int options) 5163 { 5164 char mask[NICKLEN+USERLEN+HOSTLEN+8]; 5165 char clientip[IPSZ], maskip[IPSZ]; 5166 char *p = NULL; 5167 char *nmask = NULL, *umask = NULL, *hmask = NULL; 5168 int cidr = -1; /* CIDR length, -1 for no CIDR */ 5169 5170 strlcpy(mask, rmask, sizeof(mask)); 5171 5172 if ((options & MATCH_CHECK_EXTENDED) && 5173 is_extended_server_ban(mask) && 5174 client->user) 5175 { 5176 /* Check user properties / extbans style */ 5177 return _match_user_extended_server_ban(rmask, client); 5178 } 5179 5180 if (!(options & MATCH_MASK_IS_UHOST)) 5181 { 5182 p = strchr(mask, '!'); 5183 if (p) 5184 { 5185 *p++ = '\0'; 5186 if (!*mask) 5187 return 0; /* NOMATCH: '!...' */ 5188 nmask = mask; 5189 umask = p; 5190 5191 /* Could just as well check nick right now */ 5192 if (!match_simple(nmask, client->name)) 5193 return 0; /* NOMATCH: nick mask did not match */ 5194 } 5195 } 5196 5197 if (!(options & (MATCH_MASK_IS_HOST))) 5198 { 5199 p = strchr(p ? p : mask, '@'); 5200 if (p) 5201 { 5202 char *client_username = (client->user && *client->user->username) ? client->user->username : client->ident; 5203 5204 *p++ = '\0'; 5205 if (!*p || !*mask) 5206 return 0; /* NOMATCH: '...@' or '@...' */ 5207 hmask = p; 5208 if (!umask) 5209 umask = mask; 5210 5211 /* Check user portion right away */ 5212 if (!match_simple(umask, client_username)) 5213 return 0; /* NOMATCH: user mask did not match */ 5214 } else { 5215 if (nmask) 5216 return 0; /* NOMATCH: 'abc!def' (or even just 'abc!') */ 5217 hmask = mask; 5218 } 5219 } else { 5220 hmask = mask; 5221 } 5222 5223 /* If we get here then we have done checking nick / ident (if it was needed) 5224 * and now need to match the 'host' portion. 5225 */ 5226 5227 /**** Check visible host ****/ 5228 if (options & MATCH_CHECK_VISIBLE_HOST) 5229 { 5230 char *hostname = client->user ? GetHost(client) : (MyUser(client) ? client->local->sockhost : NULL); 5231 if (hostname && match_simple(hmask, hostname)) 5232 return 1; /* MATCH: visible host */ 5233 } 5234 5235 /**** Check cloaked host ****/ 5236 if (options & MATCH_CHECK_CLOAKED_HOST) 5237 { 5238 if (client->user && match_simple(hmask, client->user->cloakedhost)) 5239 return 1; /* MATCH: cloaked host */ 5240 } 5241 5242 /**** check on IP ****/ 5243 if (options & MATCH_CHECK_IP) 5244 { 5245 p = strchr(hmask, '/'); 5246 if (p) 5247 { 5248 *p++ = '\0'; 5249 cidr = atoi(p); 5250 if (cidr <= 0) 5251 return 0; /* NOMATCH: invalid CIDR */ 5252 } 5253 5254 if (strchr(hmask, '?') || strchr(hmask, '*')) 5255 { 5256 /* Wildcards */ 5257 if (client->ip && match_simple(hmask, client->ip)) 5258 return 1; /* MATCH (IP with wildcards) */ 5259 } else 5260 if (strchr(hmask, ':')) 5261 { 5262 /* IPv6 hostmask */ 5263 5264 /* We can actually return here on match/nomatch as we don't need to check the 5265 * virtual host and things like that since ':' can never be in a hostname. 5266 */ 5267 if (!client->ip || !strchr(client->ip, ':')) 5268 return 0; /* NOMATCH: hmask is IPv6 address and client is not IPv6 */ 5269 if (!inet_pton(AF_INET6, client->ip, clientip)) 5270 return 0; /* NOMATCH: unusual failure */ 5271 if (!inet_pton(AF_INET6, hmask, maskip)) 5272 return 0; /* NOMATCH: invalid IPv6 IP in hostmask */ 5273 5274 if (cidr < 0) 5275 return comp_with_mask(clientip, maskip, 128); /* MATCH/NOMATCH by exact IP */ 5276 5277 if (cidr > 128) 5278 return 0; /* NOMATCH: invalid CIDR */ 5279 5280 return comp_with_mask(clientip, maskip, cidr); 5281 } else 5282 { 5283 /* Host is not IPv6 and does not contain wildcards. 5284 * So could be a literal IPv4 address or IPv4 CIDR. 5285 * NOTE: could also be neither (like a real hostname), so don't return 0 on nomatch, 5286 * in that case we should just continue... 5287 * The exception is CIDR. If we have CIDR mask then don't bother checking for 5288 * virtual hosts and things like that since '/' can never be in a hostname. 5289 */ 5290 if (client->ip && inet_pton(AF_INET, client->ip, clientip) && inet_pton(AF_INET, hmask, maskip)) 5291 { 5292 if (cidr < 0) 5293 { 5294 if (comp_with_mask(clientip, maskip, 32)) 5295 return 1; /* MATCH: exact IP */ 5296 } 5297 else if (cidr > 32) 5298 return 0; /* NOMATCH: invalid CIDR */ 5299 else 5300 return comp_with_mask(clientip, maskip, cidr); /* MATCH/NOMATCH by CIDR */ 5301 } 5302 } 5303 } 5304 5305 /**** Check real host ****/ 5306 if (options & MATCH_CHECK_REAL_HOST) 5307 { 5308 char *hostname = client->user ? client->user->realhost : (MyUser(client) ? client->local->sockhost : NULL); 5309 if (hostname && match_simple(hmask, hostname)) 5310 return 1; /* MATCH: hostname match */ 5311 } 5312 5313 return 0; /* NOMATCH: nothing of the above matched */ 5314 } 5315 5316 /** Returns 1 if the user is allowed by any of the security groups in the named list. 5317 * This is only used by security-group::security-group and 5318 * security-group::exclude-security-group. 5319 * @param client Client to check 5320 * @param l The NameList 5321 * @returns 1 if any of the security groups match, 0 if none of them matched. 5322 */ 5323 int _unreal_match_iplist(Client *client, NameList *l) 5324 { 5325 char client_ipv6 = 0; 5326 char clientip[IPSZ], maskip[IPSZ]; 5327 5328 if (!client->ip) 5329 return 0; /* unusual, maybe services? */ 5330 5331 if (strchr(client->ip, ':')) 5332 { 5333 client_ipv6 = 1; 5334 if (!inet_pton(AF_INET6, client->ip, clientip)) 5335 return 0; /* unusual failure */ 5336 } else { 5337 if (!inet_pton(AF_INET, client->ip, clientip)) 5338 return 0; /* unusual failure */ 5339 } 5340 5341 for (; l; l = l->next) 5342 { 5343 char mask[512], *p; 5344 int cidr = -1; /* CIDR length, -1 for no CIDR */ 5345 5346 strlcpy(mask, l->name, sizeof(mask)); 5347 p = strchr(mask, '/'); 5348 if (p) 5349 { 5350 *p++ = '\0'; 5351 cidr = atoi(p); 5352 if (cidr <= 0) 5353 return 0; /* NOMATCH: invalid CIDR */ 5354 } 5355 5356 /* Three possible types: wildcard, ipv6, ipv4 */ 5357 5358 if (strchr(mask, '*') || strchr(mask, '?')) 5359 { 5360 /* Wildcards */ 5361 if (match_simple(mask, client->ip)) 5362 return 1; /* MATCH by wildcard IP */ 5363 } 5364 else if (strchr(mask, ':')) 5365 { 5366 /* IPv6 */ 5367 if (!client_ipv6) 5368 continue; /* NOMATCH: client is IPv4 */ 5369 if (!inet_pton(AF_INET6, mask, maskip)) 5370 continue; /* NOMATCH: invalid IPv6 IP in mask */ 5371 if (cidr < 0) 5372 { 5373 /* Try to match by exact IP */ 5374 if (comp_with_mask(clientip, maskip, 128)) 5375 return 1; /* MATCH by exact IP */ 5376 } else 5377 if (cidr > 128) 5378 { 5379 continue; /* NOMATCH: invalid CIDR */ 5380 } else 5381 if (comp_with_mask(clientip, maskip, cidr)) 5382 { 5383 return 1; /* MATCH by CIDR */ 5384 } 5385 } else 5386 { 5387 /* IPv4 */ 5388 if (client_ipv6) 5389 continue; /* NOMATCH: client is IPv6 */ 5390 if (!inet_pton(AF_INET, mask, maskip)) 5391 continue; /* NOMATCH: invalid IPv6 IP in mask */ 5392 if (cidr < 0) 5393 { 5394 /* Try to match by exact IP */ 5395 if (comp_with_mask(clientip, maskip, 32)) 5396 return 1; /* MATCH: by exact IP */ 5397 } else 5398 if (cidr > 32) 5399 { 5400 continue; /* NOMATCH: invalid CIDR */ 5401 } else 5402 if (comp_with_mask(clientip, maskip, cidr)) 5403 { 5404 return 1; /* MATCH by CIDR */ 5405 } 5406 } 5407 } 5408 return 0; 5409 } 5410 5411 5412 int _match_user_extended_server_ban(const char *banstr, Client *client) 5413 { 5414 const char *nextbanstr; 5415 Extban *extban; 5416 BanContext *b; 5417 int ret; 5418 5419 if (!is_extended_server_ban(banstr)) 5420 return 0; /* we should never have been called */ 5421 5422 extban = findmod_by_bantype(banstr, &nextbanstr); 5423 if (!extban || 5424 !(extban->options & EXTBOPT_TKL) || 5425 !(extban->is_banned_events & BANCHK_TKL)) 5426 { 5427 return 0; /* extban not found or of incorrect type (eg ~T) */ 5428 } 5429 5430 b = safe_alloc(sizeof(BanContext)); 5431 b->client = client; 5432 b->banstr = nextbanstr; 5433 b->ban_check_types = BANCHK_TKL; 5434 ret = extban->is_banned(b); 5435 safe_free(b); 5436 return ret; 5437 }