diff --git a/include/h.h b/include/h.h index 072802741..715685b84 100644 --- a/include/h.h +++ b/include/h.h @@ -921,6 +921,7 @@ extern MODVAR int (*spamreport)(Client *client, const char *ip, NameValuePrioLis extern MODVAR int (*crule_test)(const char *rule); extern MODVAR CRuleNode *(*crule_parse)(const char *rule); extern int (*crule_eval)(crule_context *context, CRuleNode *rule); +#define safe_crule_free(x) do { if (x) crule_free(&x); } while(0) extern void (*crule_free)(CRuleNode **); extern const char *(*crule_errstring)(int errcode); /* /Efuncs */ diff --git a/include/struct.h b/include/struct.h index 5be612f1a..96e2dac70 100644 --- a/include/struct.h +++ b/include/struct.h @@ -2157,6 +2157,8 @@ struct SecurityGroup { NameList *ip; ConfigItem_mask *mask; NameList *security_group; + char *prettyrule; /* ::rule as a string */ + CRuleNode *rule; /**< parsed crule */ NameValuePrioList *extended; /* Exclude */ int exclude_identified; @@ -2168,6 +2170,8 @@ struct SecurityGroup { NameList *exclude_ip; ConfigItem_mask *exclude_mask; NameList *exclude_security_group; + char *exclude_prettyrule; /* ::exclude-rule as a string */ + CRuleNode *exclude_rule; /**< parsed crule */ NameValuePrioList *exclude_extended; /* Settings */ DynamicSetBlock settings; diff --git a/src/modules/server.c b/src/modules/server.c index 8230ee19c..efe7f6c5e 100644 --- a/src/modules/server.c +++ b/src/modules/server.c @@ -181,7 +181,7 @@ void server_config_free(void) { d_next = d->next; unreal_delete_masks(d->mask); - crule_free(&d->rule); + safe_crule_free(d->rule); safe_free(d->prettyrule); safe_free(d->reason); DelListItem(d, conf_deny_link); diff --git a/src/modules/tkl.c b/src/modules/tkl.c index 7aa26e3d5..f15a0a045 100644 --- a/src/modules/tkl.c +++ b/src/modules/tkl.c @@ -2945,8 +2945,7 @@ void _free_tkl(TKL *tkl) safe_free(tkl->ptr.spamfilter->tkl_reason); if (tkl->ptr.spamfilter->match) unreal_delete_match(tkl->ptr.spamfilter->match); - if (tkl->ptr.spamfilter->rule) - crule_free(&tkl->ptr.spamfilter->rule); + safe_crule_free(tkl->ptr.spamfilter->rule); safe_free_all_ban_actions(tkl->ptr.spamfilter->action); safe_free(tkl->ptr.spamfilter->prettyrule); safe_free(tkl->ptr.spamfilter->id); diff --git a/src/securitygroup.c b/src/securitygroup.c index 0930e73ec..e6d5ab0d0 100644 --- a/src/securitygroup.c +++ b/src/securitygroup.c @@ -249,6 +249,18 @@ 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, "rule") || !strcmp(cep->name, "exclude-rule")) + { + int val = crule_test(cep->value); + if (val) + { + config_error("%s:%i: rule contains an invalid expression: %s", + cep->file->filename, + cep->line_number, + crule_errstring(val)); + errors++; + } + } else { /* Let's see if an extended server ban exists for this item... */ Extban *extban; @@ -418,6 +430,11 @@ int conf_match_item(ConfigFile *conf, ConfigEntry *cep, SecurityGroup **block) { unreal_add_names(&s->security_group, cep); } + else if (!strcmp(cep->name, "rule")) + { + safe_strdup(s->prettyrule, cep->value); + s->rule = crule_parse(s->prettyrule); + } else if (!strcmp(cep->name, "exclude-webirc")) s->exclude_webirc = config_checkval(cep->value, CFG_YESNO); else if (!strcmp(cep->name, "exclude-websocket")) @@ -441,6 +458,11 @@ int conf_match_item(ConfigFile *conf, ConfigEntry *cep, SecurityGroup **block) { unreal_add_names(&s->security_group, cep); } + else if (!strcmp(cep->name, "exclude-rule")) + { + safe_strdup(s->exclude_prettyrule, cep->value); + s->exclude_rule = crule_parse(s->exclude_prettyrule); + } else { /* Let's see if an extended server ban exists for this item... this needs to be LAST! */ @@ -599,6 +621,10 @@ 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); + safe_crule_free(s->rule); + safe_crule_free(s->exclude_rule); + safe_free(s->prettyrule); + safe_free(s->exclude_prettyrule); free_entire_name_list(s->ip); free_entire_name_list(s->exclude_ip); free_nvplist(s->extended); @@ -621,6 +647,16 @@ 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); + if (s->prettyrule) + { + safe_strdup(n->prettyrule, s->prettyrule); + n->rule = crule_parse(n->prettyrule); + } + if (s->exclude_prettyrule) + { + safe_strdup(n->exclude_prettyrule, s->exclude_prettyrule); + n->exclude_rule = crule_parse(n->exclude_prettyrule); + } n->ip = duplicate_name_list(s->exclude_ip); n->extended = duplicate_nvplist(s->extended); n->exclude_extended = duplicate_nvplist(s->exclude_extended); @@ -746,6 +782,17 @@ 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 @@ -799,10 +846,12 @@ 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_security_group && user_allowed_by_security_group_list(client, s->exclude_security_group)) + if (s->exclude_rule && user_allowed_by_rule(client, 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; /* Then process INCLUSION criteria... */ if (s->identified && IsLoggedIn(client)) @@ -829,10 +878,12 @@ 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->security_group && user_allowed_by_security_group_list(client, s->security_group)) + if (s->rule && user_allowed_by_rule(client, 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; user_not_allowed: recursion_security_group--;