unrealircd

- supernets unrealircd source & configuration
git clone git://git.acid.vegas/unrealircd.git
Log | Files | Refs | Archive | README | LICENSE

api-extban.c (11828B)

      1 /************************************************************************
      2  *   IRC - Internet Relay Chat, api-extban.c
      3  *   (C) 2003 Bram Matthys (Syzop) and the UnrealIRCd Team
      4  *
      5  *   See file AUTHORS in IRC package for additional names of
      6  *   the programmers. 
      7  *
      8  *   This program is free software; you can redistribute it and/or modify
      9  *   it under the terms of the GNU General Public License as published by
     10  *   the Free Software Foundation; either version 1, or (at your option)
     11  *   any later version.
     12  *
     13  *   This program is distributed in the hope that it will be useful,
     14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16  *   GNU General Public License for more details.
     17  *
     18  *   You should have received a copy of the GNU General Public License
     19  *   along with this program; if not, write to the Free Software
     20  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     21  */
     22 
     23 #include "unrealircd.h"
     24 
     25 /** List of all extbans, their handlers, etc */
     26 MODVAR Extban *extbans = NULL;
     27 
     28 void set_isupport_extban(void)
     29 {
     30 	char extbanstr[512];
     31 	Extban *e;
     32 	char *p = extbanstr;
     33 
     34 	for (e = extbans; e; e = e->next)
     35 		*p++ = e->letter;
     36 	*p = '\0';
     37 
     38 	ISupportSetFmt(NULL, "EXTBAN", "~,%s", extbanstr);
     39 }
     40 
     41 Extban *findmod_by_bantype_raw(const char *str, int ban_name_length)
     42 {
     43 	Extban *e;
     44 
     45 	for (e=extbans; e; e = e->next)
     46 	{
     47 		if ((ban_name_length == 1) && (e->letter == str[0]))
     48 			return e;
     49 		if (e->name)
     50 		{
     51 			int namelen = strlen(e->name);
     52 			if ((namelen == ban_name_length) && !strncmp(e->name, str, namelen))
     53 				return e;
     54 		}
     55 	}
     56 
     57 	 return NULL;
     58 }
     59 
     60 Extban *findmod_by_bantype(const char *str, const char **remainder)
     61 {
     62 	int ban_name_length;
     63 	const char *p = strchr(str, ':');
     64 
     65 	if (!p || !p[1])
     66 	{
     67 		if (remainder)
     68 			*remainder = NULL;
     69 		return NULL;
     70 	}
     71 	if (remainder)
     72 		*remainder = p+1;
     73 
     74 	ban_name_length = p - str - 1;
     75 	return findmod_by_bantype_raw(str+1, ban_name_length);
     76 }
     77 
     78 /* Check if this is a valid extended ban name */
     79 int is_valid_extban_name(const char *p)
     80 {
     81 	if (!*p)
     82 		return 0; /* empty name */
     83 	if (strlen(p) > 32)
     84 		return 0; /* too long */
     85 	for (; *p; p++)
     86 		if (!islower(*p) && !isdigit(*p) && !strchr("_-", *p))
     87 			return 0;
     88 	return 1;
     89 }
     90 
     91 static void extban_add_sorted(Extban *n)
     92 {
     93 	Extban *m;
     94 
     95 	if (extbans == NULL)
     96 	{
     97 		extbans = n;
     98 		return;
     99 	}
    100 
    101 	for (m = extbans; m; m = m->next)
    102 	{
    103 		if (m->letter == '\0')
    104 			abort();
    105 		if (sort_character_lowercase_before_uppercase(n->letter, m->letter))
    106 		{
    107 			/* Insert us before */
    108 			if (m->prev)
    109 				m->prev->next = n;
    110 			else
    111 				extbans = n; /* new head */
    112 			n->prev = m->prev;
    113 
    114 			n->next = m;
    115 			m->prev = n;
    116 			return;
    117 		}
    118 		if (!m->next)
    119 		{
    120 			/* Append us at end */
    121 			m->next = n;
    122 			n->prev = m;
    123 			return;
    124 		}
    125 	}
    126 }
    127 
    128 Extban *ExtbanAdd(Module *module, ExtbanInfo req)
    129 {
    130 	Extban *e;
    131 	ModuleObject *banobj;
    132 	int existing = 0;
    133 
    134 	if (!req.name)
    135 	{
    136 		module->errorcode = MODERR_INVALID;
    137 		unreal_log(ULOG_ERROR, "module", "EXTBANADD_API_ERROR", NULL,
    138 			   "ExtbanAdd(): name must be specified for ban (new in U6). Module: $module_name",
    139 			   log_data_string("module_name", module->header->name));
    140 		return NULL;
    141 	}
    142 
    143 	if (!req.is_banned_events && req.is_banned)
    144 	{
    145 		module->errorcode = MODERR_INVALID;
    146 		unreal_log(ULOG_ERROR, "module", "EXTBANADD_API_ERROR", NULL,
    147 			   "ExtbanAdd(): module must indicate via .is_banned_events on which BANCHK_* "
    148 			   "events to listen on (new in U6). Module: $module_name",
    149 			   log_data_string("module_name", module->header->name));
    150 		return NULL;
    151 	}
    152 
    153 	if (!isalnum(req.letter))
    154 	{
    155 		module->errorcode = MODERR_INVALID;
    156 		unreal_log(ULOG_ERROR, "module", "EXTBANADD_API_ERROR", NULL,
    157 		           "ExtbanAdd(): module tried to add extban which is not alphanumeric. "
    158 		           "Module: $module_name",
    159 		           log_data_string("module_name", module->header->name));
    160 		return NULL;
    161 	}
    162 
    163 	if (!is_valid_extban_name(req.name))
    164 	{
    165 		module->errorcode = MODERR_INVALID;
    166 		unreal_log(ULOG_ERROR, "module", "EXTBANADD_API_ERROR", NULL,
    167 		           "ExtbanAdd(): module tried to add extban with an invalid name ($extban_name). "
    168 		           "Module: $module_name",
    169 		           log_data_string("module_name", module->header->name),
    170 		           log_data_string("extban_name", req.name));
    171 		return NULL;
    172 	}
    173 
    174 	if (!req.conv_param)
    175 	{
    176 		module->errorcode = MODERR_INVALID;
    177 		unreal_log(ULOG_ERROR, "module", "EXTBANADD_API_ERROR", NULL,
    178 			   "ExtbanAdd(): conv_param event missing. Module: $module_name",
    179 			   log_data_string("module_name", module->header->name));
    180 		return NULL;
    181 	}
    182 
    183 	for (e=extbans; e; e = e->next)
    184 	{
    185 		if (e->letter == req.letter)
    186 		{
    187 			/* Extban already exists in our list, let's see... */
    188 			if (e->unloaded)
    189 			{
    190 				e->unloaded = 0;
    191 				existing = 1;
    192 				break;
    193 			} else
    194 			if ((module->flags == MODFLAG_TESTING) && e->preregistered)
    195 			{
    196 				/* We are in MOD_INIT (yeah confusing, isn't it?)
    197 				 * and the extban already exists and it was preregistered.
    198 				 * Then go ahead with really registering it.
    199 				 */
    200 				e->preregistered = 0;
    201 				existing = 1;
    202 				break;
    203 			} else
    204 			if (module->flags == MODFLAG_NONE)
    205 			{
    206 				/* Better don't touch it, as we may still fail at this stage
    207 				 * and if we would set .conv_param etc to this and the new module
    208 				 * gets unloaded because of a config typo then we would be screwed
    209 				 * (now we are not).
    210 				 * NOTE: this does mean that if you hot-load an extban module
    211 				 * then it may only be available for config stuff the 2nd rehash.
    212 				 */
    213 				return e;
    214 			} else
    215 			{
    216 				module->errorcode = MODERR_EXISTS;
    217 				return NULL;
    218 			}
    219 		}
    220 	}
    221 
    222 	if (!e)
    223 	{
    224 		/* Not found, create */
    225 		e = safe_alloc(sizeof(Extban));
    226 		e->letter = req.letter;
    227 		extban_add_sorted(e);
    228 	}
    229 	e->letter = req.letter;
    230 	safe_strdup(e->name, req.name);
    231 	e->is_ok = req.is_ok;
    232 	e->conv_param = req.conv_param;
    233 	e->is_banned = req.is_banned;
    234 	e->is_banned_events = req.is_banned_events;
    235 	e->owner = module;
    236 	e->options = req.options;
    237 
    238 	if (module->flags == MODFLAG_NONE)
    239 		e->preregistered = 1;
    240 
    241 	banobj = safe_alloc(sizeof(ModuleObject));
    242 	banobj->object.extban = e;
    243 	banobj->type = MOBJ_EXTBAN;
    244 	AddListItem(banobj, module->objects);
    245 	module->errorcode = MODERR_NOERROR;
    246 
    247 	set_isupport_extban();
    248 	return e;
    249 }
    250 
    251 static void unload_extban_commit(Extban *e)
    252 {
    253 	/* Should we mass unban everywhere?
    254 	 * Hmmm. Not needed per se, user can always unset
    255 	 * themselves. Leaning towards no atm.
    256 	 */
    257 	// noop
    258 
    259 	/* Then unload the extban */
    260 	DelListItem(e, extbans);
    261 	safe_free(e->name);
    262 	safe_free(e);
    263 	set_isupport_extban();
    264 }
    265 
    266 /** Unload all unused extended bans after a REHASH */
    267 void unload_all_unused_extbans(void)
    268 {
    269 	Extban *e, *e_next;
    270 
    271 	for (e=extbans; e; e = e_next)
    272 	{
    273 		e_next = e->next;
    274 		if (e->letter && e->unloaded)
    275 		{
    276 			unload_extban_commit(e);
    277 		}
    278 	}
    279 
    280 }
    281 
    282 void ExtbanDel(Extban *e)
    283 {
    284 	/* Always free the module object */
    285 	if (e->owner)
    286 	{
    287 		ModuleObject *banobj;
    288 		for (banobj = e->owner->objects; banobj; banobj = banobj->next)
    289 		{
    290 			if (banobj->type == MOBJ_EXTBAN && banobj->object.extban == e)
    291 			{
    292 				DelListItem(banobj, e->owner->objects);
    293 				safe_free(banobj);
    294 				break;
    295 			}
    296 		}
    297 	}
    298 
    299 	/* Whether we can actually (already) free the Extban, it depends... */
    300 	if (loop.rehashing)
    301 		e->unloaded = 1;
    302 	else
    303 		unload_extban_commit(e);
    304 }
    305 
    306 /** General is_ok for n!u@h stuff that also deals with recursive extbans.
    307  */
    308 int extban_is_ok_nuh_extban(BanContext *b)
    309 {
    310 	int isok;
    311 	static int extban_is_ok_recursion = 0;
    312 
    313 	/* Mostly copied from clean_ban_mask - but note MyUser checks aren't needed here: extban->is_ok() according to cmd_mode isn't called for nonlocal. */
    314 	if (is_extended_ban(b->banstr))
    315 	{
    316 		const char *nextbanstr;
    317 		Extban *extban = NULL;
    318 
    319 		/* We're dealing with a stacked extended ban.
    320 		 * Rules:
    321 		 * 1) You can only stack once, so: ~x:~y:something and not ~x:~y:~z...
    322 		 * 2) The second item may never be an action modifier, nor have the
    323 		 *    EXTBOPT_NOSTACKCHILD letter set (for things like a textban).
    324 		 */
    325 
    326 		if (extban_is_ok_recursion)
    327 			return 0; /* Rule #1 violation (more than one stacked extban) */
    328 
    329 		if ((b->is_ok_check == EXBCHK_PARAM) && RESTRICT_EXTENDEDBANS && !ValidatePermissionsForPath("immune:restrict-extendedbans",b->client,NULL,b->channel,NULL))
    330 		{
    331 			/* Test if this specific extban has been disabled.
    332 			 * (We can be sure RESTRICT_EXTENDEDBANS is not *. Else this extended ban wouldn't be happening at all.)
    333 			 */
    334 			if (strchr(RESTRICT_EXTENDEDBANS, b->banstr[1]))
    335 			{
    336 				sendnotice(b->client, "Setting/removing of extended bantypes '%s' has been disabled.", RESTRICT_EXTENDEDBANS);
    337 				return 0; /* Fail */
    338 			}
    339 		}
    340 		extban = findmod_by_bantype(b->banstr, &nextbanstr);
    341 		if (!extban)
    342 		{
    343 			if (b->what == MODE_DEL)
    344 			{
    345 				return 1; /* Always allow killing unknowns. */
    346 			}
    347 			return 0; /* Don't add unknown extbans. */
    348 		}
    349 
    350 		if ((extban->options & EXTBOPT_ACTMODIFIER) || (extban->options & EXTBOPT_NOSTACKCHILD))
    351 		{
    352 			/* Rule #2 violation */
    353 			return 0;
    354 		}
    355 
    356 		/* Now we have to ask the stacked extban if it's ok. */
    357 		if (extban->is_ok)
    358 		{
    359 			b->banstr = nextbanstr;
    360 			extban_is_ok_recursion++;
    361 			isok = extban->is_ok(b);
    362 			extban_is_ok_recursion--;
    363 			return isok;
    364 		}
    365 	}
    366 	return 1; /* Either not an extban, or extban has NULL is_ok. Good to go. */
    367 }
    368 
    369 /** Some kind of general conv_param routine,
    370  * to ensure the parameter is nick!user@host.
    371  * most of the code is just copied from clean_ban_mask.
    372  */
    373 const char *extban_conv_param_nuh(BanContext *b, Extban *extban)
    374 {
    375 	char tmpbuf[USERLEN + NICKLEN + HOSTLEN + 32];
    376 	static char retbuf[USERLEN + NICKLEN + HOSTLEN + 32];
    377 
    378 	/* Work on a copy */
    379 	strlcpy(tmpbuf, b->banstr, sizeof(retbuf));
    380 	return convert_regular_ban(tmpbuf, retbuf, sizeof(retbuf));
    381 }
    382 
    383 /** conv_param to deal with stacked extbans.
    384  */
    385 const char *extban_conv_param_nuh_or_extban(BanContext *b, Extban *self_extban)
    386 {
    387 #if (USERLEN + NICKLEN + HOSTLEN + 32) > 256
    388  #error "wtf?"
    389 #endif
    390 	static char retbuf[256];
    391 	static char printbuf[256];
    392 	char *mask;
    393 	char tmpbuf[USERLEN + NICKLEN + HOSTLEN + 32];
    394 	const char *ret = NULL;
    395 	const char *nextbanstr;
    396 	Extban *extban = NULL;
    397 	static int extban_recursion = 0;
    398 
    399 	if (!is_extended_ban(b->banstr))
    400 		return extban_conv_param_nuh(b, self_extban);
    401 
    402 	/* We're dealing with a stacked extended ban.
    403 	 * Rules:
    404 	 * 1) You can only stack once, so: ~x:~y:something and not ~x:~y:~z...
    405 	 * 2) The second item may never be an action modifier, nor have the
    406 	 *    EXTBOPT_NOSTACKCHILD letter set (for things like a textban).
    407 	 */
    408 	 
    409 	/* Rule #1. Yes the recursion check is also in extban_is_ok_nuh_extban,
    410 	 * but it's possible to get here without the is_ok() function ever
    411 	 * being called (think: non-local client). And no, don't delete it
    412 	 * there either. It needs to be in BOTH places. -- Syzop
    413 	 */
    414 	if (extban_recursion)
    415 		return NULL;
    416 
    417 	strlcpy(tmpbuf, b->banstr, sizeof(tmpbuf));
    418 	extban = findmod_by_bantype(tmpbuf, &nextbanstr);
    419 
    420 	if (!extban)
    421 	{
    422 		/* Handling unknown bantypes in is_ok. Assume that it's ok here. */
    423 		return b->banstr;
    424 	}
    425 
    426 	b->banstr = nextbanstr;
    427 
    428 	if ((extban->options & EXTBOPT_ACTMODIFIER) || (extban->options & EXTBOPT_NOSTACKCHILD))
    429 	{
    430 		/* Rule #2 violation */
    431 		return NULL;
    432 	}
    433 
    434 	extban_recursion++;
    435 	ret = extban->conv_param(b, extban);
    436 	extban_recursion--;
    437 	ret = prefix_with_extban(ret, b, extban, retbuf, sizeof(retbuf));
    438 	return ret;
    439 }
    440 
    441 char *prefix_with_extban(const char *remainder, BanContext *b, Extban *extban, char *buf, size_t buflen)
    442 {
    443 	/* Yes, we support this because it makes code at the caller cleaner */
    444 	if (remainder == NULL)
    445 		return NULL;
    446 
    447 	if (iConf.named_extended_bans && !(b->conv_options & BCTX_CONV_OPTION_WRITE_LETTER_BANS))
    448 		snprintf(buf, buflen, "~%s:%s", extban->name, remainder);
    449 	else
    450 		snprintf(buf, buflen, "~%c:%s", extban->letter, remainder);
    451 
    452 	return buf;
    453 }