From d47fdbede49e9a2dddad94b91b9d81be77cdc853 Mon Sep 17 00:00:00 2001 From: Bram Matthys Date: Thu, 26 May 2022 20:57:03 +0200 Subject: [PATCH] Add oper::auto-login. When set to yes, opers are automatically logged in if the oper block permits, the user does not have to send "OPER xyz". Eg: security-group Syzop { certfp "xyz"; } oper Syzop { auto-login yes; mask { security-group Syzop; } operclass netadmin-with-override; class opers; } Then, if you connect with SSL with that certificate fingerprint, you become IRCOp automatically. --- include/struct.h | 1 + src/conf.c | 25 ++++++++++++++++++++++++- src/modules/oper.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/include/struct.h b/include/struct.h index 2ae7ea0a9..174bbcf3c 100644 --- a/include/struct.h +++ b/include/struct.h @@ -1635,6 +1635,7 @@ struct ConfigItem_oper { int maxlogins; int server_notice_colors; int server_notice_show_event; + int auto_login; }; /** The TLS options that are used in set::tls and otherblocks::tls-options. diff --git a/src/conf.c b/src/conf.c index 0a827b767..6d0c3b215 100644 --- a/src/conf.c +++ b/src/conf.c @@ -3999,6 +3999,10 @@ int _conf_oper(ConfigFile *conf, ConfigEntry *ce) { oper->server_notice_show_event = config_checkval(cep->value, CFG_YESNO); } + else if (!strcmp(cep->name, "auto-login")) + { + oper->auto_login = config_checkval(cep->value, CFG_YESNO); + } else if (!strcmp(cep->name, "modes")) { oper->modes = set_usermode(cep->value); @@ -4028,7 +4032,7 @@ 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_match = 0, has_broad_match = 0; - char has_maxlogins = 0, has_operclass = 0, has_vhost = 0; + char has_maxlogins = 0, has_operclass = 0, has_vhost = 0, has_auto_login = 0; ConfigEntry *cep; int errors = 0; @@ -4134,6 +4138,10 @@ int _test_oper(ConfigFile *conf, ConfigEntry *ce) else if (!strcmp(cep->name, "server-notice-show-event")) { } + else if (!strcmp(cep->name, "auto-login")) + { + has_auto_login = config_checkval(cep->value, CFG_YESNO); + } /* oper::modes */ else if (!strcmp(cep->name, "modes")) { @@ -4270,12 +4278,27 @@ int _test_oper(ConfigFile *conf, ConfigEntry *ce) } } + if (has_auto_login && has_broad_match) + { + config_error("%s:%i: your oper block for '%s' has auto-login but is completely unrestricted (mask *@*)!", + ce->file->filename, ce->line_number, ce->value); + errors++; + } else if (!has_password && has_broad_match) { config_error("%s:%i: your oper block for '%s' has no password and is completely unrestricted (mask *@*)!", ce->file->filename, ce->line_number, ce->value); errors++; } + + /* The rest should NOT be in an 'else'... */ + if (has_password && has_auto_login) + { + config_error("%s:%i: You have auto-login enabled for your oper block '%s' but you also have a password set. " + "Remove the password if you want to use auto-login.", + ce->file->filename, ce->line_number, ce->value); + errors++; + } if (!has_mask && !has_match) { config_error_missing(ce->file->filename, ce->line_number, diff --git a/src/modules/oper.c b/src/modules/oper.c index 2f9730c61..395f58a23 100644 --- a/src/modules/oper.c +++ b/src/modules/oper.c @@ -34,6 +34,7 @@ ModuleHeader MOD_HEADER /* Forward declarations */ CMD_FUNC(cmd_oper); int _make_oper(Client *client, const char *operblock_name, const char *operclass, ConfigItem_class *clientclass, long modes, const char *snomask, const char *vhost); +int oper_connect(Client *client); MOD_TEST() { @@ -46,6 +47,7 @@ MOD_INIT() { MARK_AS_OFFICIAL_MODULE(modinfo); CommandAdd(modinfo->handle, MSG_OPER, cmd_oper, MAXPARA, CMD_USER); + HookAdd(modinfo->handle, HOOKTYPE_LOCAL_CONNECT, 0, oper_connect); return MOD_SUCCESS; } @@ -348,3 +350,29 @@ CMD_FUNC(cmd_oper) log_data_string("warn_type", "OUTDATED_TLS_PROTOCOL_OR_CIPHER")); } } + +int oper_connect(Client *client) +{ + ConfigItem_oper *e; + + if (IsOper(client)) + return 0; + + for (e = conf_oper; e; e = e->next) + { + if (e->auto_login && user_allowed_by_security_group(client, e->match)) + { + /* Ideally we would check all the criteria that cmd_oper does. + * I'm taking a shortcut for now that is not ideal... + */ + const char *parx[3]; + parx[0] = NULL; + parx[1] = e->name; + parx[2] = NULL; + do_cmd(client, NULL, "OPER", 3, parx); + return 0; + } + } + + return 0; +}