Browse Source

Merge branch 'unrealircd:unreal60_dev' into piss60

piss60
angryce 2 months ago committed by GitHub
parent
commit
f738ae8610
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      Makefile.windows
  2. 45
      doc/RELEASE-NOTES.md
  3. 20
      doc/conf/examples/example.conf
  4. 2
      doc/conf/modules.default.conf
  5. 37
      doc/conf/modules.optional.conf
  6. 10
      include/h.h
  7. 17
      include/license.h
  8. 5
      include/modules.h
  9. 34
      include/struct.h
  10. 77
      src/api-extban.c
  11. 504
      src/conf.c
  12. 70
      src/misc.c
  13. 4
      src/modules/Makefile.in
  14. 19
      src/modules/antimixedutf8.c
  15. 40
      src/modules/antirandom.c
  16. 95
      src/modules/channel-context.c
  17. 95
      src/modules/connthrottle.c
  18. 99
      src/modules/creationtime.c
  19. 25
      src/modules/extbans/account.c
  20. 35
      src/modules/extbans/certfp.c
  21. 23
      src/modules/extbans/country.c
  22. 25
      src/modules/extbans/realname.c
  23. 23
      src/modules/extbans/securitygroup.c
  24. 21
      src/modules/nick.c
  25. 2
      src/modules/oper.c
  26. 4
      src/modules/server.c
  27. 34
      src/modules/stats.c
  28. 2
      src/modules/vhost.c
  29. 47
      src/modules/whois.c
  30. 190
      src/user.c

8
Makefile.windows

@ -254,6 +254,7 @@ DLL_FILES=\
src/modules/close.dll \
src/modules/connect.dll \
src/modules/connthrottle.dll \
src/modules/creationtime.dll \
src/modules/cycle.dll \
src/modules/dccallow.dll \
src/modules/dccdeny.dll \
@ -381,6 +382,7 @@ DLL_FILES=\
src/modules/trace.dll \
src/modules/tsctl.dll \
src/modules/typing-indicator.dll \
src/modules/channel-context.dll \
src/modules/umode2.dll \
src/modules/unreal_server_compat.dll \
src/modules/unsqline.dll \
@ -830,6 +832,9 @@ src/modules/connect.dll: src/modules/connect.c $(INCLUDES)
src/modules/connthrottle.dll: src/modules/connthrottle.c $(INCLUDES)
$(CC) $(MODCFLAGS) src/modules/connthrottle.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/connthrottle.pdb $(MODLFLAGS)
src/modules/creationtime.dll: src/modules/creationtime.c $(INCLUDES)
$(CC) $(MODCFLAGS) src/modules/creationtime.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/creationtime.pdb $(MODLFLAGS)
src/modules/cycle.dll: src/modules/cycle.c $(INCLUDES)
$(CC) $(MODCFLAGS) src/modules/cycle.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/cycle.pdb $(MODLFLAGS)
@ -1214,6 +1219,9 @@ src/modules/tsctl.dll: src/modules/tsctl.c $(INCLUDES)
src/modules/typing-indicator.dll: src/modules/typing-indicator.c $(INCLUDES)
$(CC) $(MODCFLAGS) src/modules/typing-indicator.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/typing-indicator.pdb $(MODLFLAGS)
src/modules/channel-context.dll: src/modules/channel-context.c $(INCLUDES)
$(CC) $(MODCFLAGS) src/modules/channel-context.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/channel-context.pdb $(MODLFLAGS)
src/modules/umode2.dll: src/modules/umode2.c $(INCLUDES)
$(CC) $(MODCFLAGS) src/modules/umode2.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/umode2.pdb $(MODLFLAGS)

45
doc/RELEASE-NOTES.md

@ -6,10 +6,49 @@ If you are already running UnrealIRCd 6 then read below. Otherwise, jump
straight to the [summary about UnrealIRCd 6](#Summary) to learn more
about UnrealIRCd 6.
Fixes:
*
Enhancements:
* The [security-group block](https://www.unrealircd.org/docs/Security-group_block)
has been expanded and the same functionality is now available in
[mask items](https://www.unrealircd.org/docs/Mask_item) too:
* This means the existing options like identified, webirc, tls and
reputation-score can be used in allow::mask, tld::mask, etc.
* New options (in both security-group and mask) are:
* connect-time: time a user is connected to IRC
* security-group: to check another security group
* account: services account name
* country: country as found by GeoIP
* realname: realname (gecos) of the user
* certfp: certificate fingerprint
* Some of this functionality was already available in a different way
(extended server bans) but this makes it easier to read in the config file
and is more flexible.
* Every option also has an exclude- variant, eg. *exclude-country*.
If a user matches any exclude- option then it is considered not a match.
* Example of direct use in a ::mask item:
```
tld {
mask { country { ES; MX; } }
motd "motd.es.txt";
rules "rules.es.txt";
}
```
* Example of defining a security group and using it in a mask item later:
```
security-group irccloud {
mask { ip1; ip2; ip3; ip4 }
}
allow {
mask { security-group irccloud; }
class clients;
maxperip 128;
}
except ban {
mask { security-group irccloud; }
type { blacklist; connect-flood; handshake-data-flood; }
}
```
Fixes:
*
UnrealIRCd 6.0.3

20
doc/conf/examples/example.conf

@ -520,22 +520,24 @@ set {
set {
connthrottle {
/* First we must configure what we call "known users".
/* First we configure which users are exempt from the
* restrictions. These users are always allowed in!
* By default these are users on IP addresses that have
* a score of 24 or higher. A score of 24 means that the
* IP was connected to this network for at least 2 hours
* in the past month (or minimum 1 hour if registered).
* The sasl-bypass option is another setting. It means
* that users who authenticate to services via SASL
* are considered known users as well.
* Users in the "known-users" group (either by reputation
* or by SASL) are always allowed in by this module.
* We also allow users who are identified to services via
* SASL to bypass the restrictions.
*/
known-users {
minimum-reputation-score 24;
sasl-bypass yes;
except {
reputation-score 24;
identified yes;
/* for more options, see
* https://www.unrealircd.org/docs/Mask_item
*/
}
/* New users are all users that do not belong in the
* known-users group. They are considered "new" and in
* case of a high number of such new users connecting

2
doc/conf/modules.default.conf

@ -117,6 +117,7 @@ loadmodule "umode2";
loadmodule "sinfo";
loadmodule "require-module";
loadmodule "slog";
loadmodule "creationtime";
loadmodule "unreal_server_compat";
// Services commands
@ -222,6 +223,7 @@ loadmodule "echo-message"; /* shows clients if their messages are altered/filter
loadmodule "labeled-response"; /* correlate requests and responses easily */
loadmodule "bot-tag"; /* indicate the message comes from a bot (draft/bot) */
loadmodule "typing-indicator"; /* typing indicator in PM and channels (+typing) */
loadmodule "channel-context";
loadmodule "reply-tag"; /* indicate to which message you are responding (+draft/reply) */
loadmodule "clienttagdeny"; /* informs clients about supported client-only message tags */
loadmodule "sts"; /* strict transport policy (set::tls::sts-policy) */

37
doc/conf/modules.optional.conf

@ -109,23 +109,21 @@ set {
*/
show-failedconnects yes;
/* EXCEPT-HOSTS:
* Hostmasks on this list are matched against the IP and hostname of the connecting
* user. If it matches then we do not check if the nick/ident/realname is random.
* NOTE: Use the REAL host or IP here, not any cloaked hosts!
/* EXCEPT:
* Except these users. See https://www.unrealircd.org/docs/Mask_item for options.
*/
except-hosts {
mask 192.168.0.0/16;
mask 127.0.0.0/8;
}
except {
/* Exempt WEBIRC gateways because these frequently
* cause false positives. So the default is yes.
*/
webirc yes; // on by default
/* EXCEPT-WEBIRC:
* This will make antirandom not check connections from WEBIRC gateways.
* ( see https://www.unrealircd.org/docs/WebIRC_block )
* It seems WEBIRC connections frequently cause false positives so the
* default is 'yes'.
*/
except-webirc yes;
/* Exempt LAN users */
mask { 192.168.0.0/16; 127.0.0.0/8; }
/* Optionally you could exempt these: */
// security-group known-users;
}
}
}
@endif
@ -171,6 +169,15 @@ set {
/* Duration of ban (does not apply to block/kill) */
ban-time 4h; // For other types
/* Except these users - see
* https://www.unrealircd.org/docs/Mask_item for options.
*/
//commented out by default:
//except {
// security-group known-users;
// webirc yes;
//}
}
}
@endif

10
include/h.h

@ -677,6 +677,7 @@ extern void unreal_setfilemodtime(const char *filename, time_t mtime);
extern void DeleteTempModules(void);
extern MODVAR Extban *extbaninfo;
extern Extban *findmod_by_bantype(const char *str, const char **remainder);
extern Extban *findmod_by_bantype_raw(const char *str, int ban_name_length);
extern Extban *ExtbanAdd(Module *reserved, ExtbanInfo req);
extern void ExtbanDel(Extban *);
extern void extban_init(void);
@ -1104,6 +1105,11 @@ extern void free_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_name(Client *client, const char *secgroupname);
extern int test_match_item(ConfigFile *conf, ConfigEntry *cep, int *errors);
extern int conf_match_item(ConfigFile *conf, ConfigEntry *cep, SecurityGroup **block);
extern int test_match_block(ConfigFile *conf, ConfigEntry *ce, int *errors_out);
extern int conf_match_block(ConfigFile *conf, ConfigEntry *ce, SecurityGroup **block);
extern int test_extended_list(Extban *extban, ConfigEntry *cep, int *errors);
#define nv_find_by_name(stru, name) do_nv_find_by_name(stru, name, ARRAY_SIZEOF((stru)))
extern long do_nv_find_by_name(NameValue *table, const char *cmd, int numelements);
#define nv_find_by_value(stru, value) do_nv_find_by_value(stru, value, ARRAY_SIZEOF((stru)))
@ -1123,6 +1129,9 @@ extern void add_fmt_nvplist(NameValuePrioList **lst, int priority, const char *n
extern void add_nvplist_numeric_fmt(NameValuePrioList **lst, int priority, const char *name, Client *to, int numeric, FORMAT_STRING(const char *pattern), ...) __attribute__((format(printf,6,7)));
extern NameValuePrioList *find_nvplist(NameValuePrioList *list, const char *name);
extern void free_nvplist(NameValuePrioList *lst);
extern void unreal_add_name_values(NameValuePrioList **n, const char *name, ConfigEntry *ce);
extern const char *namevalue(NameValuePrioList *n);
extern const char *namevalue_nospaces(NameValuePrioList *n);
extern const char *get_connect_extinfo(Client *client);
extern char *unreal_strftime(const char *str);
extern void strtolower(char *str);
@ -1253,3 +1262,4 @@ extern void add_proc_io_server(void);
extern void procio_post_rehash(int failure);
/* end of proc i/o */
extern int minimum_msec_since_last_run(struct timeval *tv_old, long minimum);
extern long get_connected_time(Client *client);

17
include/license.h

@ -27,10 +27,10 @@
char *gnulicense[] = {
" \2UnrealIRCd License\2",
"This program is free software; you can redistribute it and/or modify",
"it under the terms of the GNU General Public License as published by",
"the Free Software Foundation; either version 1, or (at your option)",
"any later version.",
"This program is free software; you can redistribute it and/or",
"modify it under the terms of the GNU General Public License",
"as published by the Free Software Foundation; either version 2",
"of the License, or (at your option) any later version.",
"",
"This program is distributed in the hope that it will be useful,",
"but WITHOUT ANY WARRANTY; without even the implied warranty of",
@ -39,10 +39,11 @@ char *gnulicense[] = {
"",
"You should have received a copy of the GNU General Public License",
"along with this program; if not, write to the Free Software",
"Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.",
"Foundation, Inc., 51 Franklin Street, Fifth Floor,",
"Boston, MA 02110-1301, USA.",
"",
"To see the UnrealIRCd License, please point your browser",
"at http://www.gnu.org/copyleft/gpl.html or look in the",
"file LICENSE in the UnrealIRCd dist",
0
"to https://www.gnu.org/licenses/old-licenses/gpl-2.0.html",
"or look at the LICENSE file in the UnrealIRCd distribution.",
NULL
};

5
include/modules.h

@ -450,8 +450,11 @@ struct Extban {
/** extbans module */
Module *owner;
/* Set to 1 during rehash when module is unloading (which may be re-used, and then set to 0) */
/** Set to 1 during rehash when module is unloading (which may be re-used, and then set to 0) */
char unloaded;
/** Set to 1 when it is preregistered in MOD_TEST already */
char preregistered;
};
typedef struct {

34
include/struct.h

@ -609,6 +609,7 @@ union ModData
{
int i;
long l;
long long ll;
char *str;
void *ptr;
};
@ -763,6 +764,8 @@ struct NameList {
/** Delete an entry from a NameList - AND free it */
#define del_name_list(list, str) _del_name_list(&list, str)
extern void unreal_add_names(NameList **n, ConfigEntry *ce);
/** @} */
typedef struct MultiLine MultiLine;
@ -1558,7 +1561,7 @@ struct ConfigFlag_allow {
struct ConfigItem_allow {
ConfigItem_allow *prev, *next;
ConfigFlag flag;
ConfigItem_mask *mask;
SecurityGroup *match;
char *server;
AuthConfig *auth;
int maxperip; /**< Maximum connections permitted per IP address (locally) */
@ -1625,7 +1628,7 @@ struct ConfigItem_oper {
AuthConfig *auth;
char *operclass;
ConfigItem_class *class;
ConfigItem_mask *mask;
SecurityGroup *match;
unsigned long modes, require_modes;
char *vhost;
int maxlogins;
@ -1680,7 +1683,7 @@ struct ConfigItem_ulines {
struct ConfigItem_tld {
ConfigItem_tld *prev, *next;
ConfigFlag_tld flag;
ConfigItem_mask *mask;
SecurityGroup *match;
char *channel;
char *motd_file, *rules_file, *smotd_file;
char *botmotd_file, *opermotd_file;
@ -1714,7 +1717,7 @@ struct ConfigItem_sni {
struct ConfigItem_vhost {
ConfigItem_vhost *prev, *next;
ConfigFlag flag;
ConfigItem_mask *mask;
SecurityGroup *match;
char *login, *virthost, *virtuser;
SWhois *swhois;
AuthConfig *auth;
@ -1726,7 +1729,7 @@ struct ConfigItem_link {
/* config options: */
char *servername; /**< Name of the server ('link <servername> { }') */
struct {
ConfigItem_mask *mask; /**< incoming mask(s) to accept */
SecurityGroup *match; /**< incoming mask(s) to accept */
} incoming;
struct {
char *file; /**< UNIX domain socket to connect to */
@ -1781,14 +1784,14 @@ struct ConfigItem_deny_channel {
ConfigFlag flag;
char *channel, *reason, *redirect, *class;
unsigned char warn;
ConfigItem_mask *mask;
SecurityGroup *match;
};
struct ConfigItem_allow_channel {
ConfigItem_allow_channel *prev, *next;
ConfigFlag flag;
char *channel, *class;
ConfigItem_mask *mask;
SecurityGroup *match;
};
struct ConfigItem_allow_dcc {
@ -1861,11 +1864,26 @@ struct SecurityGroup {
SecurityGroup *prev, *next;
int priority;
char name[SECURITYGROUPLEN+1];
NameValuePrioList *printable_list;
int printable_list_counter;
/* Include */
int identified;
int reputation_score;
long connect_time;
int webirc;
int tls;
ConfigItem_mask *include_mask;
ConfigItem_mask *mask;
NameList *security_group;
NameValuePrioList *extended;
/* Exclude */
int exclude_identified;
int exclude_reputation_score;
long exclude_connect_time;
int exclude_webirc;
int exclude_tls;
ConfigItem_mask *exclude_mask;
NameList *exclude_security_group;
NameValuePrioList *exclude_extended;
};
#define HM_HOST 1

77
src/api-extban.c

@ -38,9 +38,27 @@ void set_isupport_extban(void)
ISupportSetFmt(NULL, "EXTBAN", "~,%s", extbanstr);
}
Extban *findmod_by_bantype(const char *str, const char **remainder)
Extban *findmod_by_bantype_raw(const char *str, int ban_name_length)
{
Extban *e;
for (e=extbans; e; e = e->next)
{
if ((ban_name_length == 1) && (e->letter == str[0]))
return e;
if (e->name)
{
int namelen = strlen(e->name);
if ((namelen == ban_name_length) && !strncmp(e->name, str, namelen))
return e;
}
}
return NULL;
}
Extban *findmod_by_bantype(const char *str, const char **remainder)
{
int ban_name_length;
const char *p = strchr(str, ':');
@ -54,20 +72,7 @@ Extban *findmod_by_bantype(const char *str, const char **remainder)
*remainder = p+1;
ban_name_length = p - str - 1;
for (e=extbans; e; e = e->next)
{
if ((ban_name_length == 1) && (e->letter == str[1]))
return e;
if (e->name)
{
int namelen = strlen(e->name);
if ((namelen == ban_name_length) && !strncmp(e->name, str+1, namelen))
return e;
}
}
return NULL;
return findmod_by_bantype_raw(str+1, ban_name_length);
}
/* Check if this is a valid extended ban name */
@ -167,12 +172,34 @@ Extban *ExtbanAdd(Module *module, ExtbanInfo req)
{
if (e->letter == req.letter)
{
/* Extban already exists in our list, let's see... */
if (e->unloaded)
{
e->unloaded = 0;
existing = 1;
break;
} else {
} else
if ((module->flags == MODFLAG_TESTING) && e->preregistered)
{
/* We are in MOD_INIT (yeah confusing, isn't it?)
* and the extban already exists and it was preregistered.
* Then go ahead with really registering it.
*/
e->preregistered = 0;
existing = 1;
} else
if (module->flags == MODFLAG_NONE)
{
/* Better don't touch it, as we may still fail at this stage
* and if we would set .conv_param etc to this and the new module
* gets unloaded because of a config typo then we would be screwed
* (now we are not).
* NOTE: this does mean that if you hot-load an extban module
* then it may only be available for config stuff the 2nd rehash.
*/
return e;
} else
{
if (module)
module->errorcode = MODERR_EXISTS;
return NULL;
@ -195,6 +222,8 @@ Extban *ExtbanAdd(Module *module, ExtbanInfo req)
e->is_banned_events = req.is_banned_events;
e->owner = module;
e->options = req.options;
if (module->flags == MODFLAG_NONE)
e->preregistered = 1;
if (module)
{
ModuleObject *banobj = safe_alloc(sizeof(ModuleObject));
@ -221,6 +250,22 @@ static void unload_extban_commit(Extban *e)
set_isupport_extban();
}
/** Unload all unused extended bans after a REHASH */
void unload_all_unused_extbans(void)
{
Extban *e, *e_next;
for (e=extbans; e; e = e_next)
{
e_next = e->next;
if (e->letter && e->unloaded)
{
unload_extban_commit(e);
}
}
}
void ExtbanDel(Extban *e)
{
/* Always free the module object */

504
src/conf.c

@ -185,6 +185,7 @@ ConfigEntry *config_find_entry(ConfigEntry *ce, const char *name);
extern void add_entropy_configfile(struct stat *st, const char *buf);
extern void unload_all_unused_umodes(void);
extern void unload_all_unused_extcmodes(void);
extern void unload_all_unused_extbans(void);
extern void unload_all_unused_caps(void);
extern void unload_all_unused_history_backends(void);
int reloadable_perm_module_unloaded(void);
@ -2353,7 +2354,7 @@ void config_rehash()
safe_free(oper_ptr->operclass);
safe_free(oper_ptr->vhost);
Auth_FreeAuthConfig(oper_ptr->auth);
unreal_delete_masks(oper_ptr->mask);
free_security_group(oper_ptr->match);
DelListItem(oper_ptr, conf_oper);
for (s = oper_ptr->swhois; s; s = s_next)
{
@ -2400,7 +2401,7 @@ void config_rehash()
for (allow_ptr = conf_allow; allow_ptr; allow_ptr = (ConfigItem_allow *) next)
{
next = (ListStruct *)allow_ptr->next;
unreal_delete_masks(allow_ptr->mask);
free_security_group(allow_ptr->match);
Auth_FreeAuthConfig(allow_ptr->auth);
DelListItem(allow_ptr, conf_allow);
safe_free(allow_ptr);
@ -2437,6 +2438,8 @@ void config_rehash()
free_motd(&tld_ptr->opermotd);
free_motd(&tld_ptr->botmotd);
free_security_group(tld_ptr->match);
DelListItem(tld_ptr, conf_tld);
safe_free(tld_ptr);
}
@ -2450,7 +2453,7 @@ void config_rehash()
Auth_FreeAuthConfig(vhost_ptr->auth);
safe_free(vhost_ptr->virthost);
safe_free(vhost_ptr->virtuser);
unreal_delete_masks(vhost_ptr->mask);
free_security_group(vhost_ptr->match);
for (s = vhost_ptr->swhois; s; s = s_next)
{
s_next = s->next;
@ -2488,7 +2491,7 @@ void config_rehash()
safe_free(deny_channel_ptr->reason);
safe_free(deny_channel_ptr->class);
DelListItem(deny_channel_ptr, conf_deny_channel);
unreal_delete_masks(deny_channel_ptr->mask);
free_security_group(deny_channel_ptr->match);
safe_free(deny_channel_ptr);
}
@ -2498,7 +2501,7 @@ void config_rehash()
safe_free(allow_channel_ptr->channel);
safe_free(allow_channel_ptr->class);
DelListItem(allow_channel_ptr, conf_allow_channel);
unreal_delete_masks(allow_channel_ptr->mask);
free_security_group(allow_channel_ptr->match);
safe_free(allow_channel_ptr);
}
@ -3036,7 +3039,7 @@ ConfigItem_tld *find_tld(Client *client)
for (tld = conf_tld; tld; tld = tld->next)
{
if (unreal_mask_match(client, tld->mask))
if (user_allowed_by_security_group(client, tld->match))
{
if ((tld->options & TLD_TLS) && !IsSecureConnect(client))
continue;
@ -3056,7 +3059,8 @@ ConfigItem_link *find_link(const char *servername, Client *client)
for (link = conf_link; link; link = link->next)
{
if (match_simple(link->servername, servername) && unreal_mask_match(client, link->incoming.mask))
if (match_simple(link->servername, servername) &&
user_allowed_by_security_group(client, link->incoming.match))
{
return link;
}
@ -3137,7 +3141,7 @@ ConfigItem_deny_channel *find_channel_allowed(Client *client, const char *name)
{
if (dchannel->class && strcmp(client->local->class->name, dchannel->class))
continue;
if (dchannel->mask && !unreal_mask_match(client, dchannel->mask))
if (dchannel->match && !user_allowed_by_security_group(client, dchannel->match))
continue;
break; /* MATCH deny channel { } */
}
@ -3152,7 +3156,7 @@ ConfigItem_deny_channel *find_channel_allowed(Client *client, const char *name)
{
if (achannel->class && strcmp(client->local->class->name, achannel->class))
continue;
if (achannel->mask && !unreal_mask_match(client, achannel->mask))
if (achannel->match && !user_allowed_by_security_group(client, achannel->match))
continue;
break; /* MATCH allow channel { } */
}
@ -3935,6 +3939,7 @@ int _conf_oper(ConfigFile *conf, ConfigEntry *ce)
oper = safe_alloc(sizeof(ConfigItem_oper));
safe_strdup(oper->name, ce->value);
oper->match = safe_alloc(sizeof(SecurityGroup));
/* Inherit some defaults: */
oper->server_notice_colors = tempiConf.server_notice_colors;
@ -4002,9 +4007,9 @@ int _conf_oper(ConfigFile *conf, ConfigEntry *ce)
{
oper->maxlogins = atoi(cep->value);
}
else if (!strcmp(cep->name, "mask"))
else if (!strcmp(cep->name, "mask") || !strcmp(cep->name, "match"))
{
unreal_add_masks(&oper->mask, cep);
conf_match_block(conf, cep, &oper->match);
}
else if (!strcmp(cep->name, "vhost"))
{
@ -4018,7 +4023,7 @@ int _conf_oper(ConfigFile *conf, ConfigEntry *ce)
int _test_oper(ConfigFile *conf, ConfigEntry *ce)
{
char has_class = 0, has_password = 0, has_snomask = 0;
char has_modes = 0, has_require_modes = 0, has_mask = 0, has_maxlogins = 0;
char has_modes = 0, has_require_modes = 0, has_mask = 0, has_match = 0, has_maxlogins = 0;
char has_operclass = 0, has_vhost = 0;
ConfigEntry *cep;
int errors = 0;
@ -4187,7 +4192,18 @@ int _test_oper(ConfigFile *conf, ConfigEntry *ce)
else if (!strcmp(cep->name, "mask"))
{
if (cep->value || cep->items)
{
has_mask = 1;
test_match_block(conf, cep, &errors);
}
}
else if (!strcmp(cep->name, "match"))
{
if (cep->value || cep->items)
{
has_match = 1;
test_match_block(conf, cep, &errors);
}
}
else
{
@ -4207,7 +4223,18 @@ int _test_oper(ConfigFile *conf, ConfigEntry *ce)
else if (!strcmp(cep->name, "mask"))
{
if (cep->value || cep->items)
{
has_mask = 1;
test_match_block(conf, cep, &errors);
}
}
else if (!strcmp(cep->name, "match"))
{
if (cep->value || cep->items)
{
has_match = 1;
test_match_block(conf, cep, &errors);
}
}
else if (!strcmp(cep->name, "password"))
{
@ -4236,10 +4263,17 @@ int _test_oper(ConfigFile *conf, ConfigEntry *ce)
"oper::password");
errors++;
}
if (!has_mask)
if (!has_mask && !has_match)
{
config_error_missing(ce->file->filename, ce->line_number,
"oper::mask");
"oper::match");
errors++;
}
if (has_mask && has_match)
{
config_error("%s:%d: You cannot have both ::mask and ::match. "
"You should only use oper::match.",
ce->file->filename, ce->line_number);
errors++;
}
if (!has_class)
@ -4590,8 +4624,8 @@ int _conf_tld(ConfigFile *conf, ConfigEntry *ce)
for (cep = ce->items; cep; cep = cep->next)
{
if (!strcmp(cep->name, "mask"))
unreal_add_masks(&ca->mask, cep);
if (!strcmp(cep->name, "match") || !strcmp(cep->name, "mask"))
conf_match_block(conf, cep, &ca->match);
else if (!strcmp(cep->name, "motd"))
{
safe_strdup(ca->motd_file, cep->value);
@ -4640,8 +4674,8 @@ int _test_tld(ConfigFile *conf, ConfigEntry *ce)
ConfigEntry *cep;
int errors = 0;
int fd = -1;
char has_mask = 0, has_motd = 0, has_rules = 0, has_shortmotd = 0, has_channel = 0;
char has_opermotd = 0, has_botmotd = 0, has_options = 0;
char has_mask = 0, has_match = 0, has_motd = 0, has_rules = 0, has_shortmotd = 0;
char has_channel = 0, has_opermotd = 0, has_botmotd = 0, has_options = 0;
for (cep = ce->items; cep; cep = cep->next)
{
@ -4656,7 +4690,18 @@ int _test_tld(ConfigFile *conf, ConfigEntry *ce)
if (!strcmp(cep->name, "mask"))
{
if (cep->value || cep->items)
{
has_mask = 1;
test_match_block(conf, cep, &errors);
}
}
else if (!strcmp(cep->name, "match"))
{
if (cep->value || cep->items)
{
has_match = 1;
test_match_block(conf, cep, &errors);
}
}
/* tld::motd */
else if (!strcmp(cep->name, "motd"))
@ -4806,10 +4851,17 @@ int _test_tld(ConfigFile *conf, ConfigEntry *ce)
continue;
}
}
if (!has_mask)
if (!has_mask && !has_match)
{
config_error_missing(ce->file->filename, ce->line_number,
"tld::mask");
"tld::match");
errors++;
}
if (has_mask && has_match)
{
config_error("%s:%d: You cannot have both ::mask and ::match. "
"You should only use %s::match.",
ce->file->filename, ce->line_number, ce->name);
errors++;
}
if (!has_motd)
@ -5342,12 +5394,13 @@ int _conf_allow(ConfigFile *conf, ConfigEntry *ce)
}
allow = safe_alloc(sizeof(ConfigItem_allow));
allow->ipv6_clone_mask = tempiConf.default_ipv6_clone_mask;
allow->match = safe_alloc(sizeof(SecurityGroup));
for (cep = ce->items; cep; cep = cep->next)
{
if (!strcmp(cep->name, "mask") || !strcmp(cep->name, "ip") || !strcmp(cep->name, "hostname"))
if (!strcmp(cep->name, "match") || !strcmp(cep->name, "mask") || !strcmp(cep->name, "ip") || !strcmp(cep->name, "hostname"))
{
unreal_add_masks(&allow->mask, cep);
conf_match_block(conf, cep, &allow->match);
}
else if (!strcmp(cep->name, "password"))
allow->auth = AuthBlockToAuthConfig(cep);
@ -5413,7 +5466,7 @@ int _test_allow(ConfigFile *conf, ConfigEntry *ce)
ConfigEntry *cep, *cepp;
int errors = 0;
Hook *h;
char has_ip = 0, has_hostname = 0, has_mask = 0;
char has_ip = 0, has_hostname = 0, has_mask = 0, has_match = 0;
char has_maxperip = 0, has_global_maxperip = 0, has_password = 0, has_class = 0;
char has_redirectserver = 0, has_redirectport = 0, has_options = 0;
int hostname_possible_silliness = 0;
@ -5463,6 +5516,7 @@ int _test_allow(ConfigFile *conf, ConfigEntry *ce)
for (cep = ce->items; cep; cep = cep->next)
{
if (strcmp(cep->name, "options") &&
strcmp(cep->name, "match") &&
strcmp(cep->name, "mask") &&
config_is_blankorempty(cep, "allow"))
{
@ -5494,6 +5548,12 @@ int _test_allow(ConfigFile *conf, ConfigEntry *ce)
else if (!strcmp(cep->name, "mask"))
{
has_mask = 1;
test_match_block(conf, cep, &errors);
}
else if (!strcmp(cep->name, "match"))
{
has_match = 1;
test_match_block(conf, cep, &errors);
}
else if (!strcmp(cep->name, "maxperip"))
{
@ -5641,27 +5701,33 @@ int _test_allow(ConfigFile *conf, ConfigEntry *ce)
}
}
if (has_mask && (has_ip || has_hostname))
if ((has_mask || has_match) && (has_ip || has_hostname))
{
config_error("%s:%d: The allow block uses allow::mask, but you also have an allow::ip and allow::hostname.",
config_error("%s:%d: The allow block uses allow::match, but you also have an allow::ip and allow::hostname.",
ce->file->filename, ce->line_number);
config_error("Please delete your allow::ip and allow::hostname entries and/or integrate them into allow::mask");
config_error("Please delete your allow::ip and allow::hostname entries and/or integrate them into allow::match");
} else
if (has_ip)
{
config_warn("%s:%d: The allow block uses allow::mask nowadays. Rename your allow::ip item to allow::mask.",
config_warn("%s:%d: The allow block uses allow::match nowadays. Rename your allow::ip item to allow::match.",
ce->file->filename, ce->line_number);
config_warn("See https://www.unrealircd.org/docs/FAQ#allow-mask for more information");
} else
if (has_hostname)
{
config_warn("%s:%d: The allow block uses allow::mask nowadays. Rename your allow::hostname item to allow::mask.",
config_warn("%s:%d: The allow block uses allow::match nowadays. Rename your allow::hostname item to allow::match.",
ce->file->filename, ce->line_number);
config_warn("See https://www.unrealircd.org/docs/FAQ#allow-mask for more information");
} else
if (!has_mask)
if (has_mask && has_match)
{
config_error("%s:%d: You cannot have both ::mask and ::match. You should only use allow::match.",
ce->file->filename, ce->line_number);
errors++;
} else
if (!has_match && !has_mask)
{
config_error("%s:%d: allow block needs an allow::mask",
config_error("%s:%d: allow block needs an allow::match",
ce->file->filename, ce->line_number);
errors++;
}
@ -5702,15 +5768,15 @@ int _conf_allow_channel(ConfigFile *conf, ConfigEntry *ce)
ConfigItem_allow_channel *allow = NULL;
ConfigEntry *cep;
char *class = NULL;
ConfigEntry *mask = NULL;
ConfigEntry *match = NULL;
/* First, search for ::class, if any */
for (cep = ce->items; cep; cep = cep->next)
{
if (!strcmp(cep->name, "class"))
class = cep->value;
else if (!strcmp(cep->name, "mask"))
mask = cep;
else if (!strcmp(cep->name, "match") || !strcmp(cep->name, "mask"))
match = cep;
}
for (cep = ce->items; cep; cep = cep->next)
@ -5722,8 +5788,8 @@ int _conf_allow_channel(ConfigFile *conf, ConfigEntry *ce)
safe_strdup(allow->channel, cep->value);
if (class)
safe_strdup(allow->class, class);
if (mask)
unreal_add_masks(&allow->mask, mask);
if (match)
conf_match_block(conf, match, &allow->match);
AddListItem(allow, conf_allow_channel);
}
}
@ -5732,9 +5798,10 @@ int _conf_allow_channel(ConfigFile *conf, ConfigEntry *ce)
int _test_allow_channel(ConfigFile *conf, ConfigEntry *ce)
{
ConfigEntry *cep;
int errors = 0;
char has_channel = 0, has_class = 0;
ConfigEntry *cep;
int errors = 0;
char has_match = 0, has_mask = 0, has_channel = 0, has_class = 0;
for (cep = ce->items; cep; cep = cep->next)
{
if (config_is_blankorempty(cep, "allow channel"))
@ -5758,8 +5825,15 @@ int _test_allow_channel(ConfigFile *conf, ConfigEntry *ce)
}
has_class = 1;
}
else if (!strcmp(cep->name, "match"))
{
has_match = 1;
test_match_block(conf, cep, &errors);
}
else if (!strcmp(cep->name, "mask"))
{
has_mask = 1;
test_match_block(conf, cep, &errors);
}
else
{
@ -5768,6 +5842,13 @@ int _test_allow_channel(ConfigFile *conf, ConfigEntry *ce)
errors++;
}
}
if (has_mask && has_match)
{
config_error("%s:%d: You cannot have both ::mask and ::match. "
"You should only use %s::match.",
ce->file->filename, ce->line_number, ce->name);
errors++;
}
if (!has_channel)
{
config_error_missing(ce->file->filename, ce->line_number,
@ -5860,6 +5941,7 @@ int _conf_vhost(ConfigFile *conf, ConfigEntry *ce)
ConfigItem_vhost *vhost;
ConfigEntry *cep, *cepp;
vhost = safe_alloc(sizeof(ConfigItem_vhost));
vhost->match = safe_alloc(sizeof(SecurityGroup));
for (cep = ce->items; cep; cep = cep->next)
{
@ -5880,9 +5962,9 @@ int _conf_vhost(ConfigFile *conf, ConfigEntry *ce)
safe_strdup(vhost->login, cep->value);
else if (!strcmp(cep->name, "password"))
vhost->auth = AuthBlockToAuthConfig(cep);
else if (!strcmp(cep->name, "mask"))
else if (!strcmp(cep->name, "match") || !strcmp(cep->name, "mask"))
{
unreal_add_masks(&vhost->mask, cep);
conf_match_block(conf, cep, &vhost->match);
}
else if (!strcmp(cep->name, "swhois"))
{
@ -5914,7 +5996,7 @@ int _test_vhost(ConfigFile *conf, ConfigEntry *ce)
{
int errors = 0;
ConfigEntry *cep;
char has_vhost = 0, has_login = 0, has_password = 0, has_mask = 0;
char has_vhost = 0, has_login = 0, has_password = 0, has_mask = 0, has_match = 0;
for (cep = ce->items; cep; cep = cep->next)
{
@ -6007,6 +6089,12 @@ int _test_vhost(ConfigFile *conf, ConfigEntry *ce)
else if (!strcmp(cep->name, "mask"))
{
has_mask = 1;
test_match_block(conf, cep, &errors);
}
else if (!strcmp(cep->name, "match"))
{
has_match = 1;
test_match_block(conf, cep, &errors);
}
else if (!strcmp(cep->name, "swhois"))
{
@ -6038,10 +6126,17 @@ int _test_vhost(ConfigFile *conf, ConfigEntry *ce)
"vhost::password");
errors++;
}
if (!has_mask)
if (!has_mask && !has_match)
{
config_error_missing(ce->file->filename, ce->line_number,
"vhost::mask");
"vhost::match");
errors++;
}
if (has_mask && has_match)
{
config_error("%s:%d: You cannot have both ::mask and ::match. "
"You should only use %s::match.",
ce->file->filename, ce->line_number, ce->name);
errors++;
}
return errors;
@ -6172,9 +6267,9 @@ int _conf_link(ConfigFile *conf, ConfigEntry *ce)
{
for (cepp = cep->items; cepp; cepp = cepp->next)
{
if (!strcmp(cepp->name, "mask"))
if (!strcmp(cepp->name, "match") || !strcmp(cepp->name, "mask"))
{
unreal_add_masks(&link->incoming.mask, cepp);
conf_match_block(conf, cepp, &link->incoming.match);
}
}
}
@ -6275,7 +6370,7 @@ int _test_link(ConfigFile *conf, ConfigEntry *ce)
ConfigEntry *cep, *cepp, *ceppp;
int errors = 0;
int has_incoming = 0, has_incoming_mask = 0, has_outgoing = 0, has_outgoing_file = 0;
int has_incoming = 0, has_incoming_mask = 0, has_incoming_match = 0, has_outgoing = 0, has_outgoing_file = 0;
int has_outgoing_bind_ip = 0, has_outgoing_hostname = 0, has_outgoing_port = 0;
int has_outgoing_options = 0, has_hub = 0, has_leaf = 0, has_leaf_depth = 0;
int has_password = 0, has_class = 0, has_options = 0;
@ -6302,11 +6397,26 @@ int _test_link(ConfigFile *conf, ConfigEntry *ce)
config_detect_duplicate(&has_incoming, cep, &errors);
for (cepp = cep->items; cepp; cepp = cepp->next)
{
if (!strcmp(cepp->name, "match"))
{
if (cepp->value || cepp->items)
{
has_incoming_match = 1;
test_match_block(conf, cepp, &errors);
} else
if (config_is_blankorempty(cepp, "link::incoming"))
{
errors++;
continue;
}
} else
if (!strcmp(cepp->name, "mask"))
{
if (cepp->value || cepp->items)
{
has_incoming_mask = 1;
else
test_match_block(conf, cepp, &errors);
} else
if (config_is_blankorempty(cepp, "link::incoming"))
{
errors++;
@ -6502,9 +6612,16 @@ int _test_link(ConfigFile *conf, ConfigEntry *ce)
if (has_incoming)
{
/* If we have an incoming sub-block then we need at least 'mask' and 'password' */
if (!has_incoming_mask)
if (!has_incoming_mask && !has_incoming_match)
{
config_error_missing(ce->file->filename, ce->line_number, "link::incoming::mask");
config_error_missing(ce->file->filename, ce->line_number, "link::incoming::match");
errors++;
}
if (has_incoming_mask && has_incoming_match)
{
config_error("%s:%d: You cannot have both link::incoming::mask and link::incoming::match. "
"You should only use link::incoming::match.",
ce->file->filename, ce->line_number);
errors++;
}
}
@ -9839,9 +9956,9 @@ int _conf_deny_channel(ConfigFile *conf, ConfigEntry *ce)
{
safe_strdup(deny->class, cep->value);
}
else if (!strcmp(cep->name, "mask"))
else if (!strcmp(cep->name, "match") || !strcmp(cep->name, "mask"))
{
unreal_add_masks(&deny->mask, cep);
conf_match_block(conf, cep, &deny->match);
}
}
AddListItem(deny, conf_deny_channel);
@ -9915,6 +10032,7 @@ int _test_deny(ConfigFile *conf, ConfigEntry *ce)
if (!strcmp(ce->value, "channel"))
{
char has_channel = 0, has_warn = 0, has_reason = 0, has_redirect = 0, has_class = 0;
char has_mask = 0, has_match = 0;
for (cep = ce->items; cep; cep = cep->next)
{
if (config_is_blankorempty(cep, "deny channel"))
@ -9972,8 +10090,15 @@ int _test_deny(ConfigFile *conf, ConfigEntry *ce)
}
has_class = 1;
}
else if (!strcmp(cep->name, "match"))
{
has_match = 1;
test_match_block(conf, cep, &errors);
}
else if (!strcmp(cep->name, "mask"))
{
has_mask = 1;
test_match_block(conf, cep, &errors);
}
else
{
@ -9994,6 +10119,13 @@ int _test_deny(ConfigFile *conf, ConfigEntry *ce)
"deny channel::reason");
errors++;
}
if (has_mask && has_match)
{
config_error("%s:%d: You cannot have both ::mask and ::match. "
"You should only use %s %s::match.",
ce->file->filename, ce->line_number, ce->name, ce->value);
errors++;
}
}
else if (!strcmp(ce->value, "link"))
{
@ -10198,11 +10330,109 @@ int _test_deny(ConfigFile *conf, ConfigEntry *ce)
return errors;
}
#define CheckNullX(x) if ((!(x)->value) || (!(*((x)->value)))) { config_error("%s:%i: missing parameter", (x)->file->filename, (x)->line_number); *errors = *errors + 1; return 0; }
int test_match_item(ConfigFile *conf, ConfigEntry *cep, int *errors)
{
if (!strcmp(cep->name, "webirc") || !strcmp(cep->name, "exclude-webirc"))
{
CheckNullX(cep);
} else
if (!strcmp(cep->name, "identified") || !strcmp(cep->name, "exclude-identified"))
{
CheckNullX(cep);
} else
if (!strcmp(cep->name, "tls") || !strcmp(cep->name, "exclude-tls"))
{
CheckNullX(cep);
} else
if (!strcmp(cep->name, "reputation-score") || !strcmp(cep->name, "exclude-reputation-score"))
{
const char *str = cep->value;
int v;
CheckNullX(cep);
if (*str == '<')
str++;
v = atoi(str);
if ((v < 1) || (v > 10000))
{
config_error("%s:%i: %s needs to be a value of 1-10000",
cep->file->filename, cep->line_number, cep->name);
*errors = *errors + 1;
}
} else
if (!strcmp(cep->name, "connect-time") || !strcmp(cep->name, "exclude-connect-time"))
{
const char *str = cep->value;
long v;
CheckNullX(cep);
if (*str == '<')
str++;
v = config_checkval(str, CFG_TIME);
if (v < 1)
{
config_error("%s:%i: %s needs to be a time value (and more than 0 seconds)",
cep->file->filename, cep->line_number, cep->name);
*errors = *errors + 1;
}
} else
if (!strcmp(cep->name, "mask") || !strcmp(cep->name, "include-mask") || !strcmp(cep->name, "exclude-mask"))
{
} else
if (!strcmp(cep->name, "security-group") || !strcmp(cep->name, "exclude-security-group"))
{
CheckNullX(cep);
} else
{
/* Let's see if an extended server ban exists for this item... */
Extban *extban;
if (!strncmp(cep->name, "exclude-", 8))
extban = findmod_by_bantype_raw(cep->name+8, strlen(cep->name+8));
else
extban = findmod_by_bantype_raw(cep->name, strlen(cep->name));
if (extban && (extban->options & EXTBOPT_TKL) && (extban->is_banned_events & BANCHK_TKL))
{
CheckNullX(cep);
test_extended_list(extban, cep, errors);
return 1; /* Yup, handled */
}
return 0; /* Unhandled: unknown item for us */
}
return 1; /* Handled, but there could be errors */
}
int test_match_block(ConfigFile *conf, ConfigEntry *ce, int *errors_out)
{
int errors = 0;