Skip to content

Commit

Permalink
Add SVSO command which services can use to make someone IRCOp.
Browse files Browse the repository at this point in the history
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);
  • Loading branch information
syzop committed May 7, 2022
1 parent 84f3efc commit 50e5d91
Show file tree
Hide file tree
Showing 14 changed files with 270 additions and 91 deletions.
4 changes: 4 additions & 0 deletions Makefile.windows
Expand Up @@ -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 \
Expand Down Expand Up @@ -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)

Expand Down
1 change: 1 addition & 0 deletions doc/conf/modules.default.conf
Expand Up @@ -137,6 +137,7 @@ loadmodule "svspart";
loadmodule "svssilence";
loadmodule "svssno";
loadmodule "svswatch";
loadmodule "svso";


/*** Channel modes ***/
Expand Down
4 changes: 4 additions & 0 deletions include/h.h
Expand Up @@ -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 */
Expand All @@ -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);
Expand All @@ -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;
Expand Down
4 changes: 3 additions & 1 deletion include/modules.h
Expand Up @@ -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
Expand Down Expand Up @@ -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 */
Expand Down
2 changes: 2 additions & 0 deletions src/api-efunctions.c
Expand Up @@ -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)())
{
Expand Down Expand Up @@ -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 changes: 5 additions & 0 deletions src/misc.c
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/modules/Makefile.in
Expand Up @@ -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 \
Expand Down
2 changes: 1 addition & 1 deletion src/modules/mode.c
Expand Up @@ -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);
}

Expand Down
173 changes: 96 additions & 77 deletions src/modules/oper.c
Expand Up @@ -20,10 +20,6 @@

#include "unrealircd.h"

CMD_FUNC(cmd_oper);


/* Place includes here */
#define MSG_OPER "OPER" /* OPER */

ModuleHeader MOD_HEADER
Expand All @@ -35,27 +31,35 @@ ModuleHeader MOD_HEADER
"unrealircd-6",
};

/* This is called on module init, before Server Ready */
/* 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()
{
MARK_AS_OFFICIAL_MODULE(modinfo);
EfunctionAdd(modinfo->handle, EFUNC_MAKE_OPER, _make_oper);
return MOD_SUCCESS;
}

MOD_INIT()
{
CommandAdd(modinfo->handle, MSG_OPER, cmd_oper, MAXPARA, CMD_USER);
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;
Expand All @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -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)
{
Expand All @@ -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))
Expand Down
8 changes: 4 additions & 4 deletions src/modules/operinfo.c
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/modules/svsmode.c
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/modules/svsnoop.c
Expand Up @@ -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);
}
}
Expand Down

0 comments on commit 50e5d91

Please sign in to comment.