mirror of
https://github.com/unrealircd/unrealircd.git
synced 2026-07-03 15:13:13 +02:00
Support ::destination and ::exclude-destination in security groups / mask items
at selected places (there needs to be explicit code in place to handle this).
At the moment it is supported at two places only:
* For spamfilters (was already possible via crules via ::rule with
a destination('xyz') but now non-crule destination "#xyz"; works as well, eg:
spamfilter {
...
except {
destination "#main";
}
}
Note that if you want to exempt a destination in all spamfilters,
we already have set::spamfilter::except for that!
* In restrict commands for like channel-message and such:
set {
restrict-commands {
channel-message {
except {
connect-time 600;
destination "#test";
}
}
}
}
Allow passing a crule_context via user_allowed_by_security_group_context()
and make user_allowed_by_security_group() call that.
Actually document spamfilter::except online in the docs (yeah you
won't see it in this commit, just mentioning...)
And yeah, by now i wonder if we should really call it crule_context
since it is more like a security group matching context, but.. whatever.
This commit is contained in:
@@ -1306,6 +1306,7 @@ extern void free_security_group(SecurityGroup *s);
|
||||
extern SecurityGroup *duplicate_security_group(SecurityGroup *s);
|
||||
extern void set_security_group_defaults(void);
|
||||
extern int user_allowed_by_security_group(Client *client, SecurityGroup *s);
|
||||
extern int user_allowed_by_security_group_context(Client *client, SecurityGroup *s, crule_context *context);
|
||||
extern int user_allowed_by_security_group_name(Client *client, const char *secgroupname);
|
||||
extern const char *get_security_groups(Client *client);
|
||||
extern int test_match_item(ConfigFile *conf, ConfigEntry *cep, int *errors);
|
||||
|
||||
@@ -2220,6 +2220,7 @@ struct SecurityGroup {
|
||||
NameList *security_group;
|
||||
char *prettyrule; /* ::rule as a string */
|
||||
CRuleNode *rule; /**< parsed crule */
|
||||
NameList *destination;
|
||||
NameValuePrioList *extended;
|
||||
/* Exclude */
|
||||
int exclude_identified;
|
||||
@@ -2233,6 +2234,7 @@ struct SecurityGroup {
|
||||
NameList *exclude_security_group;
|
||||
char *exclude_prettyrule; /* ::exclude-rule as a string */
|
||||
CRuleNode *exclude_rule; /**< parsed crule */
|
||||
NameList *exclude_destination;
|
||||
NameValuePrioList *exclude_extended;
|
||||
/* Settings */
|
||||
DynamicSetBlock settings;
|
||||
|
||||
@@ -49,8 +49,8 @@ int rcmd_configrun(ConfigFile *cf, ConfigEntry *ce, int type);
|
||||
int rcmd_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype);
|
||||
int rcmd_can_send_to_user(Client *client, Client *target, const char **text, const char **errmsg, SendType sendtype);
|
||||
int rcmd_can_join(Client *client, Channel *channel, const char *key, char **errmsg);
|
||||
int rcmd_block_message(Client *client, const char *text, SendType sendtype, const char **errmsg, const char *display, const char *conftag);
|
||||
int rcmd_block_join(Client *client, const char **errmsg);
|
||||
int rcmd_block_message(Client *client, const char *destination, const char *text, SendType sendtype, const char **errmsg, const char *display, const char *conftag);
|
||||
int rcmd_block_join(Client *client, Channel *channel, const char **errmsg);
|
||||
CMD_OVERRIDE_FUNC(rcmd_override);
|
||||
|
||||
// Globals
|
||||
@@ -298,7 +298,7 @@ int rcmd_configrun(ConfigFile *cf, ConfigEntry *ce, int type)
|
||||
rcmd->except->identified = config_checkval(cep2->value, CFG_YESNO);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (!strcmp(cep2->name, "exempt-webirc"))
|
||||
{
|
||||
rcmd->except->webirc = config_checkval(cep2->value, CFG_YESNO);
|
||||
@@ -323,18 +323,18 @@ int rcmd_configrun(ConfigFile *cf, ConfigEntry *ce, int type)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int rcmd_canbypass(Client *client, RestrictedCommand *rcmd)
|
||||
int rcmd_canbypass(Client *client, RestrictedCommand *rcmd, crule_context *context)
|
||||
{
|
||||
if (!client || !rcmd)
|
||||
return 1;
|
||||
if (user_allowed_by_security_group(client, rcmd->except))
|
||||
if (user_allowed_by_security_group_context(client, rcmd->except, context))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rcmd_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype)
|
||||
{
|
||||
if (rcmd_block_message(client, *msg, sendtype, errmsg, "channel", (sendtype == SEND_TYPE_NOTICE ? "channel-notice" : "channel-message")))
|
||||
if (rcmd_block_message(client, channel->name, *msg, sendtype, errmsg, "channel", (sendtype == SEND_TYPE_NOTICE ? "channel-notice" : "channel-message")))
|
||||
return HOOK_DENY;
|
||||
|
||||
return HOOK_CONTINUE;
|
||||
@@ -346,14 +346,15 @@ int rcmd_can_send_to_user(Client *client, Client *target, const char **text, con
|
||||
if ((client == target) || IsULine(target))
|
||||
return HOOK_CONTINUE; /* bypass/exempt */
|
||||
|
||||
if (rcmd_block_message(client, *text, sendtype, errmsg, "user", (sendtype == SEND_TYPE_NOTICE ? "private-notice" : "private-message")))
|
||||
if (rcmd_block_message(client, target->name, *text, sendtype, errmsg, "user", (sendtype == SEND_TYPE_NOTICE ? "private-notice" : "private-message")))
|
||||
return HOOK_DENY;
|
||||
|
||||
return HOOK_CONTINUE;
|
||||
}
|
||||
|
||||
int rcmd_block_message(Client *client, const char *text, SendType sendtype, const char **errmsg, const char *display, const char *conftag)
|
||||
int rcmd_block_message(Client *client, const char *destination, const char *text, SendType sendtype, const char **errmsg, const char *display, const char *conftag)
|
||||
{
|
||||
crule_context context;
|
||||
RestrictedCommand *rcmd;
|
||||
static char errbuf[256];
|
||||
|
||||
@@ -361,8 +362,12 @@ int rcmd_block_message(Client *client, const char *text, SendType sendtype, cons
|
||||
if (!MyUser(client) || !client->local || IsOper(client) || IsULine(client))
|
||||
return 0;
|
||||
|
||||
memset(&context, 0, sizeof(context));
|
||||
context.client = client;
|
||||
context.destination = destination;
|
||||
|
||||
rcmd = find_restrictions_byconftag(conftag);
|
||||
if (rcmd && !rcmd_canbypass(client, rcmd))
|
||||
if (rcmd && !rcmd_canbypass(client, rcmd, &context))
|
||||
{
|
||||
int notice = (sendtype == SEND_TYPE_NOTICE ? 1 : 0); // temporary hack FIXME !!!
|
||||
if (rcmd->except->connect_time)
|
||||
@@ -386,6 +391,7 @@ int rcmd_block_message(Client *client, const char *text, SendType sendtype, cons
|
||||
CMD_OVERRIDE_FUNC(rcmd_override)
|
||||
{
|
||||
RestrictedCommand *rcmd;
|
||||
crule_context context;
|
||||
|
||||
if (!MyUser(client) || !client->local || IsOper(client) || IsULine(client))
|
||||
{
|
||||
@@ -393,8 +399,11 @@ CMD_OVERRIDE_FUNC(rcmd_override)
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&context, 0, sizeof(context));
|
||||
context.client = client;
|
||||
|
||||
rcmd = find_restrictions_bycmd(ovr->command->cmd);
|
||||
if (rcmd && !rcmd_canbypass(client, rcmd))
|
||||
if (rcmd && !rcmd_canbypass(client, rcmd, &context))
|
||||
{
|
||||
if (rcmd->except->connect_time)
|
||||
{
|
||||
@@ -422,27 +431,32 @@ int rcmd_can_join(Client *client, Channel *channel, const char *key, char **errm
|
||||
if (channel->users || has_channel_mode(channel, 'P'))
|
||||
return 0;
|
||||
|
||||
else if (rcmd_block_join(client, &join_err))
|
||||
else if (rcmd_block_join(client, channel, &join_err))
|
||||
{
|
||||
static char formatted_errmsg[512];
|
||||
snprintf(formatted_errmsg, sizeof(formatted_errmsg), "JOIN :%s", join_err);
|
||||
|
||||
|
||||
*errmsg = formatted_errmsg;
|
||||
return ERR_CANNOTDOCOMMAND;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rcmd_block_join(Client *client, const char **errmsg)
|
||||
int rcmd_block_join(Client *client, Channel *channel, const char **errmsg)
|
||||
{
|
||||
RestrictedCommand *rcmd;
|
||||
crule_context context;
|
||||
static char errbuf[256];
|
||||
|
||||
if (!MyUser(client) || !client->local || IsOper(client) || IsULine(client))
|
||||
return 0;
|
||||
|
||||
memset(&context, 0, sizeof(context));
|
||||
context.client = client;
|
||||
context.destination = channel->name;
|
||||
|
||||
rcmd = find_restrictions_byconftag("channel-create");
|
||||
if (rcmd && !rcmd_canbypass(client, rcmd))
|
||||
if (rcmd && !rcmd_canbypass(client, rcmd, &context))
|
||||
{
|
||||
if (rcmd->except->connect_time)
|
||||
{
|
||||
|
||||
+8
-8
@@ -5362,6 +5362,7 @@ int _match_spamfilter(Client *client, const char *str_in, int target, const char
|
||||
int stop_processing_general_spamfilters = 0;
|
||||
int stop_processing_central_spamfilters = 0;
|
||||
int content_revealed = 0;
|
||||
crule_context context;
|
||||
|
||||
if (rettkl)
|
||||
*rettkl = NULL; /* initialize to NULL */
|
||||
@@ -5380,13 +5381,18 @@ int _match_spamfilter(Client *client, const char *str_in, int target, const char
|
||||
if (!client->user || ValidatePermissionsForPath("immune:server-ban:spamfilter",client,NULL,NULL,NULL) || IsULine(client))
|
||||
return 0;
|
||||
|
||||
memset(&context, 0, sizeof(context));
|
||||
context.client = client;
|
||||
context.text = str_in;
|
||||
context.destination = destination;
|
||||
|
||||
/* Client exempt from spamfilter checking?
|
||||
* Let's check that early: going through elines is likely faster than running the regex(es).
|
||||
*/
|
||||
if (find_tkl_exception(TKL_SPAMF, client))
|
||||
user_is_exempt_general = 1;
|
||||
|
||||
if (user_allowed_by_security_group(client, iConf.central_spamfilter_except))
|
||||
if (user_allowed_by_security_group_context(client, iConf.central_spamfilter_except, &context))
|
||||
user_is_exempt_central = 1;
|
||||
|
||||
for (tkl = tklines[tkl_hash('F')]; tkl; tkl = tkl->next)
|
||||
@@ -5420,22 +5426,16 @@ int _match_spamfilter(Client *client, const char *str_in, int target, const char
|
||||
/* Run any pre 'rule' if there is any (false means 'no hit') */
|
||||
if (tkl->ptr.spamfilter->rule)
|
||||
{
|
||||
crule_context context;
|
||||
memset(&context, 0, sizeof(context));
|
||||
context.client = client;
|
||||
context.text = str_in;
|
||||
context.destination = destination;
|
||||
if (!crule_eval(&context, tkl->ptr.spamfilter->rule))
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check any 'except' rule if there is any (true means 'no hit') */
|
||||
if (tkl->ptr.spamfilter->except && user_allowed_by_security_group(client, tkl->ptr.spamfilter->except))
|
||||
if (tkl->ptr.spamfilter->except && user_allowed_by_security_group_context(client, tkl->ptr.spamfilter->except, &context))
|
||||
continue;
|
||||
|
||||
if (tkl->ptr.spamfilter->match && (tkl->ptr.spamfilter->match->type != MATCH_NONE))
|
||||
{
|
||||
// TODO: wait, why are we running slow spamfilter detection for simple (non-regex) too ?
|
||||
#ifdef SPAMFILTER_DETECTSLOW
|
||||
if (tkl->ptr.spamfilter->match->type == MATCH_PCRE_REGEX)
|
||||
{
|
||||
|
||||
+32
-14
@@ -249,6 +249,9 @@ int test_match_item(ConfigFile *conf, ConfigEntry *cep, int *errors)
|
||||
if (!strcmp(cep->name, "security-group") || !strcmp(cep->name, "exclude-security-group"))
|
||||
{
|
||||
} else
|
||||
if (!strcmp(cep->name, "destination") || !strcmp(cep->name, "exclude-destination"))
|
||||
{
|
||||
} else
|
||||
if (!strcmp(cep->name, "rule") || !strcmp(cep->name, "exclude-rule"))
|
||||
{
|
||||
int val = crule_test(cep->value);
|
||||
@@ -435,6 +438,10 @@ int conf_match_item(ConfigFile *conf, ConfigEntry *cep, SecurityGroup **block)
|
||||
safe_strdup(s->prettyrule, cep->value);
|
||||
s->rule = crule_parse(s->prettyrule);
|
||||
}
|
||||
else if (!strcmp(cep->name, "destination"))
|
||||
{
|
||||
unreal_add_names(&s->destination, cep);
|
||||
}
|
||||
else if (!strcmp(cep->name, "exclude-webirc"))
|
||||
s->exclude_webirc = config_checkval(cep->value, CFG_YESNO);
|
||||
else if (!strcmp(cep->name, "exclude-websocket"))
|
||||
@@ -463,6 +470,10 @@ int conf_match_item(ConfigFile *conf, ConfigEntry *cep, SecurityGroup **block)
|
||||
safe_strdup(s->exclude_prettyrule, cep->value);
|
||||
s->exclude_rule = crule_parse(s->exclude_prettyrule);
|
||||
}
|
||||
else if (!strcmp(cep->name, "exclude-destination"))
|
||||
{
|
||||
unreal_add_names(&s->exclude_destination, cep);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Let's see if an extended server ban exists for this item... this needs to be LAST! */
|
||||
@@ -621,6 +632,8 @@ void free_security_group(SecurityGroup *s)
|
||||
unreal_delete_masks(s->exclude_mask);
|
||||
free_entire_name_list(s->security_group);
|
||||
free_entire_name_list(s->exclude_security_group);
|
||||
free_entire_name_list(s->destination);
|
||||
free_entire_name_list(s->exclude_destination);
|
||||
safe_crule_free(s->rule);
|
||||
safe_crule_free(s->exclude_rule);
|
||||
safe_free(s->prettyrule);
|
||||
@@ -647,6 +660,8 @@ SecurityGroup *duplicate_security_group(SecurityGroup *s)
|
||||
n->exclude_mask = unreal_duplicate_masks(s->exclude_mask);
|
||||
n->security_group = duplicate_name_list(s->security_group);
|
||||
n->exclude_security_group = duplicate_name_list(s->exclude_security_group);
|
||||
n->destination = duplicate_name_list(s->destination);
|
||||
n->exclude_destination = duplicate_name_list(s->exclude_destination);
|
||||
if (s->prettyrule)
|
||||
{
|
||||
safe_strdup(n->prettyrule, s->prettyrule);
|
||||
@@ -786,23 +801,12 @@ int user_allowed_by_security_group_list(Client *client, NameList *l)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Helper for security-group::rule and mask::rule */
|
||||
int user_allowed_by_rule(Client *client, CRuleNode *rule)
|
||||
{
|
||||
crule_context context;
|
||||
|
||||
memset(&context, 0, sizeof(context));
|
||||
context.client = client;
|
||||
|
||||
return crule_eval(&context, rule);
|
||||
}
|
||||
|
||||
/** Returns 1 if the user is OK as far as the security-group is concerned.
|
||||
* @param client The client to check
|
||||
* @param s The security-group to check against
|
||||
* @retval 1 if user is allowed by security-group, 0 if not.
|
||||
*/
|
||||
int user_allowed_by_security_group(Client *client, SecurityGroup *s)
|
||||
int user_allowed_by_security_group_context(Client *client, SecurityGroup *s, crule_context *context)
|
||||
{
|
||||
static int recursion_security_group = 0;
|
||||
|
||||
@@ -850,12 +854,14 @@ int user_allowed_by_security_group(Client *client, SecurityGroup *s)
|
||||
goto user_not_allowed;
|
||||
if (s->exclude_ip && unreal_match_iplist(client, s->exclude_ip))
|
||||
goto user_not_allowed;
|
||||
if (s->exclude_rule && user_allowed_by_rule(client, s->exclude_rule))
|
||||
if (s->exclude_rule && crule_eval(context, s->exclude_rule))
|
||||
goto user_not_allowed;
|
||||
if (s->exclude_extended && user_matches_extended_list(client, s->exclude_extended))
|
||||
goto user_not_allowed;
|
||||
if (s->exclude_security_group && user_allowed_by_security_group_list(client, s->exclude_security_group))
|
||||
goto user_not_allowed;
|
||||
if (s->exclude_destination && context->destination && find_name_list_match(s->exclude_destination, context->destination))
|
||||
goto user_not_allowed;
|
||||
|
||||
/* Then process INCLUSION criteria... */
|
||||
if (s->identified && IsLoggedIn(client))
|
||||
@@ -897,12 +903,14 @@ int user_allowed_by_security_group(Client *client, SecurityGroup *s)
|
||||
goto user_allowed;
|
||||
if (s->ip && unreal_match_iplist(client, s->ip))
|
||||
goto user_allowed;
|
||||
if (s->rule && user_allowed_by_rule(client, s->rule))
|
||||
if (s->rule && crule_eval(context, s->rule))
|
||||
goto user_allowed;
|
||||
if (s->extended && user_matches_extended_list(client, s->extended))
|
||||
goto user_allowed;
|
||||
if (s->security_group && user_allowed_by_security_group_list(client, s->security_group))
|
||||
goto user_allowed;
|
||||
if (s->destination && context->destination && find_name_list_match(s->destination, context->destination))
|
||||
goto user_allowed;
|
||||
|
||||
user_not_allowed:
|
||||
recursion_security_group--;
|
||||
@@ -913,6 +921,16 @@ user_allowed:
|
||||
return 1;
|
||||
}
|
||||
|
||||
int user_allowed_by_security_group(Client *client, SecurityGroup *s)
|
||||
{
|
||||
crule_context context;
|
||||
|
||||
memset(&context, 0, sizeof(context));
|
||||
context.client = client;
|
||||
|
||||
return user_allowed_by_security_group_context(client, s, &context);
|
||||
}
|
||||
|
||||
/** Returns 1 if the user is OK as far as the security-group is concerned - "by name" version.
|
||||
* @param client The client to check
|
||||
* @param secgroupname The name of the security-group to check against
|
||||
|
||||
Reference in New Issue
Block a user