1
0
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:
Bram Matthys
2024-07-06 09:03:49 +02:00
parent a804b24150
commit e03a5dfd5f
5 changed files with 71 additions and 36 deletions
+1
View File
@@ -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);
+2
View File
@@ -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;
+28 -14
View File
@@ -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
View File
@@ -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
View File
@@ -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