1
0
mirror of https://github.com/unrealircd/unrealircd.git synced 2026-07-06 22:03:12 +02:00

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 <uid|nick> <oper account> <operclass> <class> <modes> <snomask> <vhost>
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:
<class>, <modes>, <snomask>, <vhost>

In UnrealIRCd the <operclass> 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);
This commit is contained in:
Bram Matthys
2022-05-07 18:48:19 +02:00
parent 84f3efc105
commit 50e5d91c79
14 changed files with 272 additions and 93 deletions
+4
View File
@@ -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)
+1
View File
@@ -137,6 +137,7 @@ loadmodule "svspart";
loadmodule "svssilence";
loadmodule "svssno";
loadmodule "svswatch";
loadmodule "svso";
/*** Channel modes ***/
+4
View File
@@ -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;
+3 -1
View File
@@ -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 */
+2
View File
@@ -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);
}
+5
View File
@@ -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
+1 -1
View File
@@ -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 \
+1 -1
View File
@@ -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);
}
+97 -78
View File
@@ -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))
+4 -4
View File
@@ -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);
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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);
}
}
+138
View File
@@ -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 <uid|nick> <oper account> <operclass> <class> <modes> <snomask> <vhost>
* 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:
* <class>, <modes>, <snomask>, <vhost>
*
* In UnrealIRCd the <operclass> 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");
}
+10 -6
View File
@@ -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)