From 50e5d91c798e7d07ca0c68d9fca302a6b6610786 Mon Sep 17 00:00:00 2001 From: Bram Matthys Date: Sat, 7 May 2022 18:48:19 +0200 Subject: [PATCH] Add SVSO command which services can use to make someone IRCOp. This existed in UnrealIRCd 3.2.x but was later removed when switching to the new operclass system. Requested by Valware in https://bugs.unrealircd.org/view.php?id=6041 Syntax: SVSO All these parameters need to be set, you cannot leave any of them out, HOWEVER some can be set to "-" to skip setting them, this is true for: , , , In UnrealIRCd the will be prefixed by "services:" if not already present. It is up to you to include or omit it. If you want to set any swhoises you need to use the SWHOIS s2s command, other than that this command basically does everything for you, in fact it uses the same code as the OPER command does. Most of the "user is now ircop" code has been moved out of cmd_oper() to a new function make_oper() that is called by both cmd_oper() and cmd_svso(). This function also changes the hook HOOKTYPE_LOCAL_OPER: It no longer passes a ConfigItem_oper struct, since we can't do that for remote opers. Instead it passes oper name and oper class. The complete definition is now: int hooktype_local_oper(Client *client, int add, const char *oper_block, const char *operclass); --- Makefile.windows | 4 + doc/conf/modules.default.conf | 1 + include/h.h | 4 + include/modules.h | 4 +- src/api-efunctions.c | 2 + src/misc.c | 5 + src/modules/Makefile.in | 2 +- src/modules/mode.c | 2 +- src/modules/oper.c | 175 +++++++++++++++++++--------------- src/modules/operinfo.c | 8 +- src/modules/svsmode.c | 2 +- src/modules/svsnoop.c | 2 +- src/modules/svso.c | 138 +++++++++++++++++++++++++++ src/operclass.c | 16 ++-- 14 files changed, 272 insertions(+), 93 deletions(-) create mode 100644 src/modules/svso.c diff --git a/Makefile.windows b/Makefile.windows index 9d3ab6e01..b8c405df5 100644 --- a/Makefile.windows +++ b/Makefile.windows @@ -365,6 +365,7 @@ DLL_FILES=\ src/modules/svsnline.dll \ src/modules/svsnolag.dll \ src/modules/svsnoop.dll \ + src/modules/svso.dll \ src/modules/svspart.dll \ src/modules/svssilence.dll \ src/modules/svssno.dll \ @@ -1165,6 +1166,9 @@ src/modules/svsnolag.dll: src/modules/svsnolag.c $(INCLUDES) src/modules/svsnoop.dll: src/modules/svsnoop.c $(INCLUDES) $(CC) $(MODCFLAGS) src/modules/svsnoop.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/svsnoop.pdb $(MODLFLAGS) +src/modules/svso.dll: src/modules/svso.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/svso.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/svso.pdb $(MODLFLAGS) + src/modules/svspart.dll: src/modules/svspart.c $(INCLUDES) $(CC) $(MODCFLAGS) src/modules/svspart.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/svspart.pdb $(MODLFLAGS) diff --git a/doc/conf/modules.default.conf b/doc/conf/modules.default.conf index 0b6b97d28..2994633bd 100644 --- a/doc/conf/modules.default.conf +++ b/doc/conf/modules.default.conf @@ -137,6 +137,7 @@ loadmodule "svspart"; loadmodule "svssilence"; loadmodule "svssno"; loadmodule "svswatch"; +loadmodule "svso"; /*** Channel modes ***/ diff --git a/include/h.h b/include/h.h index ba95b0a28..d6f277d73 100644 --- a/include/h.h +++ b/include/h.h @@ -834,6 +834,7 @@ extern MODVAR char *(*tkl_uhost)(TKL *tkl, char *buf, size_t buflen, int options extern MODVAR void (*do_unreal_log_remote_deliver)(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized); extern MODVAR char *(*get_chmodes_for_user)(Client *client, const char *flags); extern MODVAR WhoisConfigDetails (*whois_get_policy)(Client *client, Client *target, const char *name); +extern MODVAR int (*make_oper)(Client *client, const char *operblock_name, const char *operclass, ConfigItem_class *clientclass, long modes, const char *snomask, const char *vhost); /* /Efuncs */ /* TLS functions */ @@ -859,6 +860,7 @@ extern MODVAR EVP_MD *sha1_function; extern MODVAR EVP_MD *md5_function; /* End of TLS functions */ +/* Default handlers for efunctions */ extern void parse_message_tags_default_handler(Client *client, char **str, MessageTag **mtag_list); extern const char *mtags_to_string_default_handler(MessageTag *m, Client *client); extern void *labeled_response_save_context_default_handler(void); @@ -868,6 +870,8 @@ extern int add_silence_default_handler(Client *client, const char *mask, int sen extern int del_silence_default_handler(Client *client, const char *mask); extern int is_silenced_default_handler(Client *client, Client *acptr); extern void do_unreal_log_remote_deliver_default_handler(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized); +extern int make_oper_default_handler(Client *client, const char *operblock_name, const char *operclass, ConfigItem_class *clientclass, long modes, const char *snomask, const char *vhost); +/* End of default handlers for efunctions */ extern MODVAR MOTDFile opermotd, svsmotd, motd, botmotd, smotd, rules; extern MODVAR int max_connection_count; diff --git a/include/modules.h b/include/modules.h index c5351440d..ccb04d116 100644 --- a/include/modules.h +++ b/include/modules.h @@ -1683,9 +1683,10 @@ int hooktype_stats(Client *client, const char *str); * @param client The client * @param add 1 if the user becomes IRCOp, 0 if the user is no longer IRCOp * @param oper_block The name of the oper block used to oper up + * @param operclass The name of the operclass * @return The return value is ignored (use return 0) */ -int hooktype_local_oper(Client *client, int add, ConfigItem_oper *oper_block); +int hooktype_local_oper(Client *client, int add, const char *oper_block, const char *operclass); /** Called when a client sends a PASS command (function prototype for HOOKTYPE_LOCAL_PASS). * @param client The client @@ -2384,6 +2385,7 @@ enum EfunctionType { EFUNC_DO_UNREAL_LOG_REMOTE_DELIVER, EFUNC_GET_CHMODES_FOR_USER, EFUNC_WHOIS_GET_POLICY, + EFUNC_MAKE_OPER, }; /* Module flags */ diff --git a/src/api-efunctions.c b/src/api-efunctions.c index d6dbfdcf8..89c9ab7da 100644 --- a/src/api-efunctions.c +++ b/src/api-efunctions.c @@ -135,6 +135,7 @@ int (*watch_check)(Client *client, int reply, int (*watch_notify)(Client *client void (*do_unreal_log_remote_deliver)(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized); char *(*get_chmodes_for_user)(Client *client, const char *flags); WhoisConfigDetails (*whois_get_policy)(Client *client, Client *target, const char *name); +int (*make_oper)(Client *client, const char *operblock_name, const char *operclass, ConfigItem_class *clientclass, long modes, const char *snomask, const char *vhost); Efunction *EfunctionAddMain(Module *module, EfunctionType eftype, int (*func)(), void (*vfunc)(), void *(*pvfunc)(), char *(*stringfunc)(), const char *(*conststringfunc)()) { @@ -406,4 +407,5 @@ void efunctions_init(void) efunc_init_function(EFUNC_DO_UNREAL_LOG_REMOTE_DELIVER, do_unreal_log_remote_deliver, do_unreal_log_remote_deliver_default_handler); efunc_init_function(EFUNC_GET_CHMODES_FOR_USER, get_chmodes_for_user, NULL); efunc_init_function(EFUNC_WHOIS_GET_POLICY, whois_get_policy, NULL); + efunc_init_function(EFUNC_MAKE_OPER, make_oper, make_oper_default_handler); } diff --git a/src/misc.c b/src/misc.c index 2d381ef96..af90750f2 100644 --- a/src/misc.c +++ b/src/misc.c @@ -1461,6 +1461,11 @@ void do_unreal_log_remote_deliver_default_handler(LogLevel loglevel, const char { } +int make_oper_default_handler(Client *client, const char *operblock_name, const char *operclass, ConfigItem_class *clientclass, long modes, const char *snomask, const char *vhost) +{ + return 0; +} + /** my_timegm: mktime()-like function which will use GMT/UTC. * Strangely enough there is no standard function for this. * On some *NIX OS's timegm() may be available, sometimes only diff --git a/src/modules/Makefile.in b/src/modules/Makefile.in index 739fe3edd..1fd736792 100644 --- a/src/modules/Makefile.in +++ b/src/modules/Makefile.in @@ -39,7 +39,7 @@ MODULES= \ sethost.so chghost.so chgident.so setname.so \ setident.so sdesc.so svsmode.so swhois.so\ svsmotd.so svsnline.so who_old.so whox.so mkpasswd.so \ - away.so svsnoop.so svsnick.so \ + away.so svsnoop.so svsnick.so svso.so \ chgname.so kill.so \ lag.so message.so oper.so pingpong.so \ quit.so sendumode.so sqline.so \ diff --git a/src/modules/mode.c b/src/modules/mode.c index 90dcc04d8..ca32bfa81 100644 --- a/src/modules/mode.c +++ b/src/modules/mode.c @@ -1338,7 +1338,7 @@ CMD_FUNC(_cmd_umode) { list_del(&client->special_node); if (MyUser(client)) - RunHook(HOOKTYPE_LOCAL_OPER, client, 0, NULL); + RunHook(HOOKTYPE_LOCAL_OPER, client, 0, NULL, NULL); remove_oper_privileges(client, 0); } diff --git a/src/modules/oper.c b/src/modules/oper.c index c2a654195..84c9aa4ee 100644 --- a/src/modules/oper.c +++ b/src/modules/oper.c @@ -20,10 +20,6 @@ #include "unrealircd.h" -CMD_FUNC(cmd_oper); - - -/* Place includes here */ #define MSG_OPER "OPER" /* OPER */ ModuleHeader MOD_HEADER @@ -35,27 +31,35 @@ ModuleHeader MOD_HEADER "unrealircd-6", }; -/* This is called on module init, before Server Ready */ -MOD_INIT() +/* 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); + +MOD_TEST() { - CommandAdd(modinfo->handle, MSG_OPER, cmd_oper, MAXPARA, CMD_USER); MARK_AS_OFFICIAL_MODULE(modinfo); + EfunctionAdd(modinfo->handle, EFUNC_MAKE_OPER, _make_oper); + return MOD_SUCCESS; +} + +MOD_INIT() +{ + MARK_AS_OFFICIAL_MODULE(modinfo); + CommandAdd(modinfo->handle, MSG_OPER, cmd_oper, MAXPARA, CMD_USER); return MOD_SUCCESS; } -/* Is first run when server is 100% ready */ MOD_LOAD() { return MOD_SUCCESS; } -/* Called when module is unloaded */ MOD_UNLOAD() { return MOD_SUCCESS; } -void set_oper_host(Client *client, char *host) +void set_oper_host(Client *client, const char *host) { char uhost[HOSTLEN + USERLEN + 1]; char *p; @@ -74,6 +78,88 @@ void set_oper_host(Client *client, char *host) SetHidden(client); } +int _make_oper(Client *client, const char *operblock_name, const char *operclass, ConfigItem_class *clientclass, long modes, const char *snomask, const char *vhost) +{ + long old_umodes = client->umodes & ALL_UMODES; + + /* Put in the right class (if any) */ + if (clientclass) + { + if (client->local->class) + client->local->class->clients--; + client->local->class = clientclass; + client->local->class->clients++; + } + + /* set oper user modes */ + client->umodes |= UMODE_OPER; + if (modes) + client->umodes |= modes; /* oper::modes */ + else + client->umodes |= OPER_MODES; /* set::modes-on-oper */ + + /* oper::vhost */ + if (vhost) + { + set_oper_host(client, vhost); + } else + if (IsHidden(client) && !client->user->virthost) + { + /* +x has just been set by modes-on-oper and no vhost. cloak the oper! */ + safe_strdup(client->user->virthost, client->user->cloakedhost); + } + + unreal_log(ULOG_INFO, "oper", "OPER_SUCCESS", client, + "$client.details is now an IRC Operator [oper-block: $oper_block] [operclass: $operclass]", + log_data_string("oper_block", operblock_name), + log_data_string("operclass", operclass)); + + /* set oper snomasks */ + if (snomask) + set_snomask(client, snomask); /* oper::snomask */ + else + set_snomask(client, OPER_SNOMASK); /* set::snomask-on-oper */ + + send_umode_out(client, 1, old_umodes); + if (client->user->snomask) + sendnumeric(client, RPL_SNOMASK, client->user->snomask); + + list_add(&client->special_node, &oper_list); + + RunHook(HOOKTYPE_LOCAL_OPER, client, 1, operblock_name, operclass); + + sendnumeric(client, RPL_YOUREOPER); + + /* Update statistics */ + if (IsInvisible(client) && !(old_umodes & UMODE_INVISIBLE)) + irccounts.invisible++; + if (IsOper(client) && !IsHideOper(client)) + irccounts.operators++; + + if (SHOWOPERMOTD == 1) + { + const char *args[1] = { NULL }; + do_cmd(client, NULL, "OPERMOTD", 1, args); + } + + if (!BadPtr(OPER_AUTO_JOIN_CHANS) && strcmp(OPER_AUTO_JOIN_CHANS, "0")) + { + char *chans = strdup(OPER_AUTO_JOIN_CHANS); + const char *args[3] = { + client->name, + chans, + NULL + }; + do_cmd(client, NULL, "JOIN", 3, args); + safe_free(chans); + /* Theoretically the oper may be killed on join. Would be fun, though */ + if (IsDead(client)) + return 0; + } + + return 1; +} + /* ** cmd_oper ** parv[1] = oper name @@ -83,7 +169,6 @@ CMD_FUNC(cmd_oper) { ConfigItem_oper *operblock; const char *operblock_name, *password; - long old_umodes = client->umodes & ALL_UMODES; if (!MyUser(client)) return; @@ -227,12 +312,6 @@ CMD_FUNC(cmd_oper) /* Store which oper block was used to become IRCOp (for maxlogins and whois) */ safe_strdup(client->user->operlogin, operblock->name); - /* Put in the right class */ - if (client->local->class) - client->local->class->clients--; - client->local->class = operblock->class; - client->local->class->clients++; - /* oper::swhois */ if (operblock->swhois) { @@ -241,67 +320,7 @@ CMD_FUNC(cmd_oper) swhois_add(client, "oper", -100, s->line, &me, NULL); } - /* set oper user modes */ - client->umodes |= UMODE_OPER; - if (operblock->modes) - client->umodes |= operblock->modes; /* oper::modes */ - else - client->umodes |= OPER_MODES; /* set::modes-on-oper */ - - /* oper::vhost */ - if (operblock->vhost) - { - set_oper_host(client, operblock->vhost); - } else - if (IsHidden(client) && !client->user->virthost) - { - /* +x has just been set by modes-on-oper and no vhost. cloak the oper! */ - safe_strdup(client->user->virthost, client->user->cloakedhost); - } - - unreal_log(ULOG_INFO, "oper", "OPER_SUCCESS", client, - "$client.details is now an IRC Operator [oper-block: $oper_block]", - log_data_string("oper_block", parv[1])); - - /* set oper snomasks */ - if (operblock->snomask) - set_snomask(client, operblock->snomask); /* oper::snomask */ - else - set_snomask(client, OPER_SNOMASK); /* set::snomask-on-oper */ - - send_umode_out(client, 1, old_umodes); - if (client->user->snomask) - sendnumeric(client, RPL_SNOMASK, client->user->snomask); - - list_add(&client->special_node, &oper_list); - - RunHook(HOOKTYPE_LOCAL_OPER, client, 1, operblock); - - sendnumeric(client, RPL_YOUREOPER); - - /* Update statistics */ - if (IsInvisible(client) && !(old_umodes & UMODE_INVISIBLE)) - irccounts.invisible++; - if (IsOper(client) && !IsHideOper(client)) - irccounts.operators++; - - if (SHOWOPERMOTD == 1) - do_cmd(client, NULL, "OPERMOTD", parc, parv); - - if (!BadPtr(OPER_AUTO_JOIN_CHANS) && strcmp(OPER_AUTO_JOIN_CHANS, "0")) - { - char *chans = strdup(OPER_AUTO_JOIN_CHANS); - const char *args[3] = { - client->name, - chans, - NULL - }; - do_cmd(client, NULL, "JOIN", 3, args); - safe_free(chans); - /* Theoretically the oper may be killed on join. Would be fun, though */ - if (IsDead(client)) - return; - } + make_oper(client, operblock->name, operblock->operclass, operblock->class, operblock->modes, operblock->snomask, operblock->vhost); /* set::plaintext-policy::oper 'warn' */ if (!IsSecure(client) && !IsLocalhost(client) && (iConf.plaintext_policy_oper == POLICY_WARN)) diff --git a/src/modules/operinfo.c b/src/modules/operinfo.c index 7571c3510..8d4a2c6d6 100644 --- a/src/modules/operinfo.c +++ b/src/modules/operinfo.c @@ -16,7 +16,7 @@ ModuleHeader MOD_HEADER }; /* Forward declarations */ -int operinfo_local_oper(Client *client, int up, ConfigItem_oper *oper_block); +int operinfo_local_oper(Client *client, int up, const char *oper_block, const char *operclass); void operinfo_free(ModData *m); const char *operinfo_serialize(ModData *m); void operinfo_unserialize(const char *str, ModData *m); @@ -68,12 +68,12 @@ MOD_UNLOAD() return MOD_SUCCESS; } -int operinfo_local_oper(Client *client, int up, ConfigItem_oper *oper_block) +int operinfo_local_oper(Client *client, int up, const char *oper_block, const char *operclass) { if (up) { - moddata_client_set(client, "operlogin", oper_block->name); - moddata_client_set(client, "operclass", oper_block->operclass); + moddata_client_set(client, "operlogin", oper_block); + moddata_client_set(client, "operclass", operclass); } else { moddata_client_set(client, "operlogin", NULL); moddata_client_set(client, "operclass", NULL); diff --git a/src/modules/svsmode.c b/src/modules/svsmode.c index f5348473d..de3048365 100644 --- a/src/modules/svsmode.c +++ b/src/modules/svsmode.c @@ -395,7 +395,7 @@ void do_svsmode(Client *client, MessageTag *recv_mtags, int parc, const char *pa * so remove all oper-only modes and snomasks. */ if (MyUser(client)) - RunHook(HOOKTYPE_LOCAL_OPER, client, 0, NULL); + RunHook(HOOKTYPE_LOCAL_OPER, client, 0, NULL, NULL); remove_oper_privileges(target, 0); } goto setmodex; diff --git a/src/modules/svsnoop.c b/src/modules/svsnoop.c index c86d02232..25de4d5a8 100644 --- a/src/modules/svsnoop.c +++ b/src/modules/svsnoop.c @@ -82,7 +82,7 @@ CMD_FUNC(cmd_svsnoop) if (!list_empty(&acptr->special_node)) list_del(&acptr->special_node); - RunHook(HOOKTYPE_LOCAL_OPER, client, 0, NULL); + RunHook(HOOKTYPE_LOCAL_OPER, client, 0, NULL, NULL); remove_oper_privileges(acptr, 1); } } diff --git a/src/modules/svso.c b/src/modules/svso.c new file mode 100644 index 000000000..316711dd3 --- /dev/null +++ b/src/modules/svso.c @@ -0,0 +1,138 @@ +/* src/modules/svso.c - Grant IRCOp rights (for Services) + * (C) Copyright 2022 Bram Matthys (Syzop) and the UnrealIRCd team + * License: GPLv2 + */ +#include "unrealircd.h" + +ModuleHeader MOD_HEADER += { + "svso", + "6.0.0", + "Grant oper privileges via SVSO services command", + "UnrealIRCd Team", + "unrealircd-6", +}; + +/* Forward declarations */ +CMD_FUNC(cmd_svso); + +MOD_INIT() +{ + MARK_AS_OFFICIAL_MODULE(modinfo); + + CommandAdd(modinfo->handle, "SVSO", cmd_svso, MAXPARA, CMD_USER|CMD_SERVER); + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +/* Syntax: SVSO + * All these parameters need to be set, you cannot leave any of them out, + * HOWEVER some can be set to "-" to skip setting them, this is true for: + * , , , + * + * In UnrealIRCd the will be prefixed by "services:" if not already + * present. It is up to you to include or omit it. + */ +CMD_FUNC(cmd_svso) +{ + Client *acptr; + char oper_account[64]; + const char *operclass; + const char *clientclass; + ConfigItem_class *clientclass_c; + const char *modes; + long modes_i = 0; + const char *snomask; + const char *vhost; + + if (!IsULine(client)) + return; + + if ((parc < 8) || BadPtr(parv[7])) + { + sendnumeric(client, ERR_NEEDMOREPARAMS, "SVSO"); + return; + } + + operclass = parv[3]; + clientclass = parv[4]; + modes = parv[5]; + snomask = parv[6]; + vhost = parv[7]; + + acptr = find_user(parv[1], NULL); + if (!acptr) + { + sendnumeric(client, ERR_NOSUCHNICK, parv[1]); + return; + } + + if (!MyUser(acptr)) + { + /* Forward it to the correct server, and we are done... */ + sendto_one(acptr, recv_mtags, ":%s SVSO %s %s %s %s %s %s %s", + client->name, acptr->id, parv[2], parv[3], parv[4], parv[5], parv[6], parv[7]); + return; + } + + /* CAVEAT ALERT ! + * Don't mix up 'client' and 'acptr' below... + * 'client' is the server or services pseudouser that requests the change + * 'acptr' is the person that will be made OPER + */ + + /* If we get here, we validate the request and then make the user oper. */ + if (!find_operclass(operclass)) + { + sendnumeric(client, ERR_CANNOTDOCOMMAND, "SVSO", "Operclass does not exist"); + return; + } + + /* Set any items to NULL if they are skipped (on request) */ + if (!strcmp(clientclass, "-")) + clientclass = NULL; + if (!strcmp(modes, "-")) + modes = NULL; + if (!strcmp(snomask, "-")) + snomask = NULL; + if (!strcmp(vhost, "-")) + vhost = NULL; + + /* First, maybe the user is oper already? Then de-oper them.. */ + if (IsOper(acptr)) + { + int was_hidden_oper = IsHideOper(acptr) ? 1 : 0; + + list_del(&acptr->special_node); + RunHook(HOOKTYPE_LOCAL_OPER, acptr, 0, NULL, NULL); + remove_oper_privileges(acptr, 1); + + if (!was_hidden_oper) + irccounts.operators--; + VERIFY_OPERCOUNT(acptr, "svso"); + + } + + /* Prefix the oper block name with "services:" if it hasn't already */ + if (!strncmp(parv[2], "services:", 9)) + strlcpy(oper_account, parv[2], sizeof(oper_account)); + else + snprintf(oper_account, sizeof(oper_account), "services:%s", parv[2]); + + /* These needs to be looked up... */ + clientclass_c = find_class(clientclass); /* NULL is fine! */ + if (modes) + modes_i = set_usermode(modes); + + if (!make_oper(acptr, oper_account, operclass, clientclass_c, modes_i, snomask, vhost)) + sendnumeric(client, ERR_CANNOTDOCOMMAND, "SVSO", "Failed to make user oper"); +} diff --git a/src/operclass.c b/src/operclass.c index 0eaaa1e64..028a1411d 100644 --- a/src/operclass.c +++ b/src/operclass.c @@ -282,6 +282,7 @@ OperPermission ValidatePermissionsForPathEx(OperClassACL *acl, OperClassACLPath OperPermission ValidatePermissionsForPath(const char *path, Client *client, Client *victim, Channel *channel, const void *extra) { ConfigItem_oper *ce_oper; + const char *operclass; ConfigItem_operclass *ce_operClass; OperClass *oc = NULL; OperClassACLPath *operPath; @@ -299,15 +300,18 @@ OperPermission ValidatePermissionsForPath(const char *path, Client *client, Clie ce_oper = find_oper(client->user->operlogin); if (!ce_oper) { - return OPER_DENY; - } - - ce_operClass = find_operclass(ce_oper->operclass); - if (!ce_operClass) + operclass = moddata_client_get(client, "operclass"); + if (!operclass) + return OPER_DENY; + } else { - return OPER_DENY; + operclass = ce_oper->operclass; } + ce_operClass = find_operclass(operclass); + if (!ce_operClass) + return OPER_DENY; + oc = ce_operClass->classStruct; operPath = OperClass_parsePath(path); while (oc && operPath)