Propagate OPER
Move opername and privset storage to struct User, so it can exist for remote opers. On /oper and when bursting opers, send: :foo OPER opername privset which sets foo's opername and privset. The contents of the privset on remote servers come from the remote server's config, so the potential for confusion exists if these do not match. If an oper's privset does not exist on a server that sees it, it will complain, but create a placeholder privset. If the privset is created by a rehash, this will be reflected properly. /privs is udpated to take an optional argument, the server to query, and is now local by default: /privs [[nick_or_server] nick]
This commit is contained in:
parent
742ddc8fac
commit
ed3ca2ff16
14 changed files with 129 additions and 55 deletions
|
@ -670,6 +670,13 @@ and most error messages are suppressed.
|
|||
Servers may not send '$$', '$#' and opers@server notices. Older servers may
|
||||
not allow servers to send to specific statuses on a channel.
|
||||
|
||||
OPER
|
||||
source: user
|
||||
parameters: opername, privset
|
||||
|
||||
Sets the source user's oper name and privset. Sent after the +o mode change, or
|
||||
during burst, to inform other servers of an oper's privileges.
|
||||
|
||||
OPERSPY
|
||||
encap only
|
||||
encap target: *
|
||||
|
@ -1222,7 +1229,6 @@ MODRESTART
|
|||
MODUNLOAD
|
||||
MONITOR
|
||||
NAMES
|
||||
OPER
|
||||
POST
|
||||
PUT
|
||||
RESTART
|
||||
|
|
|
@ -42,7 +42,7 @@ static int eb_oper(const char *data, struct Client *client_p,
|
|||
if (data != NULL)
|
||||
{
|
||||
struct PrivilegeSet *set = privilegeset_get(data);
|
||||
if (set != NULL && client_p->localClient->privset == set)
|
||||
if (set != NULL && client_p->user->privset == set)
|
||||
return EXTBAN_MATCH;
|
||||
|
||||
/* $o:admin or whatever */
|
||||
|
|
|
@ -79,6 +79,9 @@ struct User
|
|||
char *away; /* pointer to away message */
|
||||
int refcnt; /* Number of times this block is referenced */
|
||||
|
||||
char *opername; /* name of operator{} block being used or tried (challenge) */
|
||||
struct PrivilegeSet *privset;
|
||||
|
||||
char suser[NICKLEN+1];
|
||||
};
|
||||
|
||||
|
@ -225,7 +228,6 @@ struct LocalUser
|
|||
*/
|
||||
char *passwd;
|
||||
char *auth_user;
|
||||
char *opername; /* name of operator{} block being used or tried (challenge) */
|
||||
char *challenge;
|
||||
char *fullcaps;
|
||||
char *cipher_string;
|
||||
|
@ -282,8 +284,6 @@ struct LocalUser
|
|||
uint16_t cork_count; /* used for corking/uncorking connections */
|
||||
struct ev_entry *event; /* used for associated events */
|
||||
|
||||
struct PrivilegeSet *privset; /* privset... */
|
||||
|
||||
char sasl_agent[IDLEN];
|
||||
unsigned char sasl_out;
|
||||
unsigned char sasl_complete;
|
||||
|
|
|
@ -146,7 +146,7 @@ extern void cluster_generic(struct Client *, const char *, int cltype,
|
|||
#define IsOperConfEncrypted(x) ((x)->flags & OPER_ENCRYPTED)
|
||||
#define IsOperConfNeedSSL(x) ((x)->flags & OPER_NEEDSSL)
|
||||
|
||||
#define HasPrivilege(x, y) ((x)->localClient != NULL && (x)->localClient->privset != NULL && privilegeset_in_set((x)->localClient->privset, (y)))
|
||||
#define HasPrivilege(x, y) ((x)->user != NULL && (x)->user->privset != NULL && privilegeset_in_set((x)->user->privset, (y)))
|
||||
|
||||
#define IsOperGlobalKill(x) (HasPrivilege((x), "oper:global_kill"))
|
||||
#define IsOperLocalKill(x) (HasPrivilege((x), "oper:local_kill"))
|
||||
|
|
|
@ -300,10 +300,7 @@ free_local_client(struct Client *client_p)
|
|||
rb_free(client_p->localClient->auth_user);
|
||||
rb_free(client_p->localClient->challenge);
|
||||
rb_free(client_p->localClient->fullcaps);
|
||||
rb_free(client_p->localClient->opername);
|
||||
rb_free(client_p->localClient->mangledhost);
|
||||
if (client_p->localClient->privset)
|
||||
privilegeset_unref(client_p->localClient->privset);
|
||||
|
||||
if (IsSSL(client_p))
|
||||
ssld_decrement_clicount(client_p->localClient->ssl_ctl);
|
||||
|
@ -1920,6 +1917,9 @@ free_user(struct User *user, struct Client *client_p)
|
|||
{
|
||||
if(user->away)
|
||||
rb_free((char *) user->away);
|
||||
rb_free(user->opername);
|
||||
if (user->privset)
|
||||
privilegeset_unref(user->privset);
|
||||
/*
|
||||
* sanity check
|
||||
*/
|
||||
|
|
|
@ -1267,7 +1267,7 @@ get_oper_name(struct Client *client_p)
|
|||
{
|
||||
snprintf(buffer, sizeof(buffer), "%s!%s@%s{%s}",
|
||||
client_p->name, client_p->username,
|
||||
client_p->host, client_p->localClient->opername);
|
||||
client_p->host, client_p->user->opername);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
|
|
@ -669,6 +669,12 @@ burst_TS6(struct Client *client_p)
|
|||
use_id(target_p),
|
||||
target_p->user->away);
|
||||
|
||||
if(IsOper(target_p) && target_p->user && target_p->user->opername && target_p->user->privset)
|
||||
sendto_one(client_p, ":%s OPER %s %s",
|
||||
use_id(target_p),
|
||||
target_p->user->opername,
|
||||
target_p->user->privset->name);
|
||||
|
||||
hclientinfo.target = target_p;
|
||||
call_hook(h_burst_client, &hclientinfo);
|
||||
}
|
||||
|
|
|
@ -1121,12 +1121,19 @@ user_mode(struct Client *client_p, struct Client *source_p, int parc, const char
|
|||
}
|
||||
source_p->flags &= ~OPER_FLAGS;
|
||||
|
||||
rb_free(source_p->localClient->opername);
|
||||
source_p->localClient->opername = NULL;
|
||||
|
||||
rb_dlinkFindDestroy(source_p, &local_oper_list);
|
||||
privilegeset_unref(source_p->localClient->privset);
|
||||
source_p->localClient->privset = NULL;
|
||||
}
|
||||
|
||||
if(source_p->user->opername != NULL)
|
||||
{
|
||||
rb_free(source_p->user->opername);
|
||||
source_p->user->opername = NULL;
|
||||
}
|
||||
|
||||
if(source_p->user->privset != NULL)
|
||||
{
|
||||
privilegeset_unref(source_p->user->privset);
|
||||
source_p->user->privset = NULL;
|
||||
}
|
||||
|
||||
rb_dlinkFindDestroy(source_p, &oper_list);
|
||||
|
@ -1413,8 +1420,8 @@ oper_up(struct Client *source_p, struct oper_conf *oper_p)
|
|||
SetExemptKline(source_p);
|
||||
|
||||
source_p->flags |= oper_p->flags;
|
||||
source_p->localClient->opername = rb_strdup(oper_p->name);
|
||||
source_p->localClient->privset = privilegeset_ref(oper_p->privset);
|
||||
source_p->user->opername = rb_strdup(oper_p->name);
|
||||
source_p->user->privset = privilegeset_ref(oper_p->privset);
|
||||
|
||||
rb_dlinkAddAlloc(source_p, &local_oper_list);
|
||||
rb_dlinkAddAlloc(source_p, &oper_list);
|
||||
|
@ -1433,6 +1440,8 @@ oper_up(struct Client *source_p, struct oper_conf *oper_p)
|
|||
sendto_realops_snomask(SNO_GENERAL, L_ALL,
|
||||
"%s (%s!%s@%s) is now an operator", oper_p->name, source_p->name,
|
||||
source_p->username, source_p->host);
|
||||
sendto_server(NULL, NULL, CAP_TS6, NOCAPS, ":%s OPER %s %s",
|
||||
use_id(source_p), oper_p->name, oper_p->privset->name);
|
||||
if(!(old & UMODE_INVISIBLE) && IsInvisible(source_p))
|
||||
++Count.invisi;
|
||||
if((old & UMODE_INVISIBLE) && !IsInvisible(source_p))
|
||||
|
|
|
@ -93,9 +93,9 @@ cleanup_challenge(struct Client *target_p)
|
|||
return;
|
||||
|
||||
rb_free(target_p->localClient->challenge);
|
||||
rb_free(target_p->localClient->opername);
|
||||
rb_free(target_p->user->opername);
|
||||
target_p->localClient->challenge = NULL;
|
||||
target_p->localClient->opername = NULL;
|
||||
target_p->user->opername = NULL;
|
||||
target_p->localClient->chal_time = 0;
|
||||
}
|
||||
|
||||
|
@ -131,7 +131,7 @@ m_challenge(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *sou
|
|||
{
|
||||
sendto_one(source_p, form_str(ERR_PASSWDMISMATCH), me.name, source_p->name);
|
||||
ilog(L_FOPER, "EXPIRED CHALLENGE (%s) by (%s!%s@%s) (%s)",
|
||||
source_p->localClient->opername, source_p->name,
|
||||
source_p->user->opername, source_p->name,
|
||||
source_p->username, source_p->host, source_p->sockhost);
|
||||
|
||||
if(ConfigFileEntry.failed_oper_notice)
|
||||
|
@ -151,7 +151,7 @@ m_challenge(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *sou
|
|||
{
|
||||
sendto_one(source_p, form_str(ERR_PASSWDMISMATCH), me.name, source_p->name);
|
||||
ilog(L_FOPER, "FAILED CHALLENGE (%s) by (%s!%s@%s) (%s)",
|
||||
source_p->localClient->opername, source_p->name,
|
||||
source_p->user->opername, source_p->name,
|
||||
source_p->username, source_p->host, source_p->sockhost);
|
||||
|
||||
if(ConfigFileEntry.failed_oper_notice)
|
||||
|
@ -169,13 +169,13 @@ m_challenge(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *sou
|
|||
|
||||
oper_p = find_oper_conf(source_p->username, source_p->orighost,
|
||||
source_p->sockhost,
|
||||
source_p->localClient->opername);
|
||||
source_p->user->opername);
|
||||
|
||||
if(oper_p == NULL)
|
||||
{
|
||||
sendto_one_numeric(source_p, ERR_NOOPERHOST, form_str(ERR_NOOPERHOST));
|
||||
ilog(L_FOPER, "FAILED OPER (%s) by (%s!%s@%s) (%s)",
|
||||
source_p->localClient->opername, source_p->name,
|
||||
source_p->user->opername, source_p->name,
|
||||
source_p->username, source_p->host,
|
||||
source_p->sockhost);
|
||||
|
||||
|
@ -192,7 +192,7 @@ m_challenge(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *sou
|
|||
oper_up(source_p, oper_p);
|
||||
|
||||
ilog(L_OPERED, "OPER %s by %s!%s@%s (%s)",
|
||||
source_p->localClient->opername, source_p->name,
|
||||
source_p->user->opername, source_p->name,
|
||||
source_p->username, source_p->host, source_p->sockhost);
|
||||
return;
|
||||
}
|
||||
|
@ -274,7 +274,7 @@ m_challenge(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *sou
|
|||
sendto_one(source_p, form_str(RPL_ENDOFRSACHALLENGE2),
|
||||
me.name, source_p->name);
|
||||
rb_free(challenge);
|
||||
source_p->localClient->opername = rb_strdup(oper_p->name);
|
||||
source_p->user->opername = rb_strdup(oper_p->name);
|
||||
}
|
||||
else
|
||||
sendto_one_notice(source_p, ":Failed to generate challenge.");
|
||||
|
|
|
@ -61,7 +61,7 @@ void set_privset(struct Client *const source,
|
|||
return;
|
||||
}
|
||||
|
||||
if(IsOper(target) && target->localClient->privset == privset)
|
||||
if(IsOper(target) && target->user->privset == privset)
|
||||
{
|
||||
sendto_one_notice(source, ":%s already has role of %s.", target->name, privset_name);
|
||||
return;
|
||||
|
@ -71,7 +71,7 @@ void set_privset(struct Client *const source,
|
|||
{
|
||||
sendto_one_notice(target, ":%s has changed your role to %s.", source->name, privset_name);
|
||||
sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "%s has changed %s's role to %s.", get_oper_name(source), target->name, privset_name);
|
||||
target->localClient->privset = privset;
|
||||
target->user->privset = privset;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "s_newconf.h"
|
||||
#include "logger.h"
|
||||
#include "s_user.h"
|
||||
#include "s_serv.h"
|
||||
#include "send.h"
|
||||
#include "msg.h"
|
||||
#include "parse.h"
|
||||
|
@ -41,12 +42,13 @@
|
|||
static const char oper_desc[] = "Provides the OPER command to become an IRC operator";
|
||||
|
||||
static void m_oper(struct MsgBuf *, struct Client *, struct Client *, int, const char **);
|
||||
static void mc_oper(struct MsgBuf *, struct Client *, struct Client *, int, const char **);
|
||||
|
||||
static bool match_oper_password(const char *password, struct oper_conf *oper_p);
|
||||
|
||||
struct Message oper_msgtab = {
|
||||
"OPER", 0, 0, 0, 0,
|
||||
{mg_unreg, {m_oper, 3}, mg_ignore, mg_ignore, mg_ignore, {m_oper, 3}}
|
||||
{mg_unreg, {m_oper, 3}, {mc_oper, 3}, mg_ignore, mg_ignore, {m_oper, 3}}
|
||||
};
|
||||
|
||||
mapi_clist_av1 oper_clist[] = { &oper_msgtab, NULL };
|
||||
|
@ -161,6 +163,35 @@ m_oper(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* mc_oper - server-to-server OPER propagation
|
||||
* parv[1] = opername
|
||||
* parv[2] = privset
|
||||
*/
|
||||
static void
|
||||
mc_oper(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
|
||||
{
|
||||
struct PrivilegeSet *privset;
|
||||
sendto_server(client_p, NULL, CAP_TS6, NOCAPS, ":%s OPER %s %s", use_id(source_p), parv[1], parv[2]);
|
||||
|
||||
privset = privilegeset_get(parv[2]);
|
||||
if(privset == NULL)
|
||||
{
|
||||
/* if we don't have a matching privset, we'll create an empty one and
|
||||
* mark it illegal, so it gets picked up on a rehash later */
|
||||
sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Received OPER for %s with unknown privset %s", source_p->name, parv[2]);
|
||||
privset = privilegeset_set_new(parv[2], "", 0);
|
||||
privset->status |= CONF_ILLEGAL;
|
||||
}
|
||||
|
||||
privset = privilegeset_ref(privset);
|
||||
if (source_p->user->privset != NULL)
|
||||
privilegeset_unref(source_p->user->privset);
|
||||
|
||||
source_p->user->privset = privset;
|
||||
source_p->user->opername = rb_strdup(parv[1]);
|
||||
}
|
||||
|
||||
/*
|
||||
* match_oper_password
|
||||
*
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "modules.h"
|
||||
#include "s_conf.h"
|
||||
#include "s_newconf.h"
|
||||
#include "hash.h"
|
||||
|
||||
static const char privs_desc[] = "Provides the PRIVS command to inspect an operator's privileges";
|
||||
|
||||
|
@ -86,21 +87,24 @@ static void show_privs(struct Client *source_p, struct Client *target_p)
|
|||
struct mode_table *p;
|
||||
|
||||
buf[0] = '\0';
|
||||
if (target_p->localClient->privset)
|
||||
rb_strlcat(buf, target_p->localClient->privset->privs, sizeof buf);
|
||||
if (target_p->user->privset)
|
||||
rb_strlcat(buf, target_p->user->privset->privs, sizeof buf);
|
||||
if (IsOper(target_p))
|
||||
{
|
||||
if (buf[0] != '\0')
|
||||
rb_strlcat(buf, " ", sizeof buf);
|
||||
rb_strlcat(buf, "operator:", sizeof buf);
|
||||
rb_strlcat(buf, target_p->localClient->opername, sizeof buf);
|
||||
if (target_p->user->opername)
|
||||
{
|
||||
if (buf[0] != '\0')
|
||||
rb_strlcat(buf, " ", sizeof buf);
|
||||
rb_strlcat(buf, "operator:", sizeof buf);
|
||||
rb_strlcat(buf, target_p->user->opername, sizeof buf);
|
||||
}
|
||||
|
||||
if (target_p->localClient->privset)
|
||||
if (target_p->user->privset)
|
||||
{
|
||||
if (buf[0] != '\0')
|
||||
rb_strlcat(buf, " ", sizeof buf);
|
||||
rb_strlcat(buf, "privset:", sizeof buf);
|
||||
rb_strlcat(buf, target_p->localClient->privset->name, sizeof buf);
|
||||
rb_strlcat(buf, target_p->user->privset->name, sizeof buf);
|
||||
}
|
||||
}
|
||||
p = &auth_client_table[0];
|
||||
|
@ -126,8 +130,9 @@ me_privs(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source
|
|||
if (!IsOper(source_p) || parc < 2 || EmptyString(parv[1]))
|
||||
return;
|
||||
|
||||
/* we cannot show privs for remote clients */
|
||||
if((target_p = find_person(parv[1])) && MyClient(target_p))
|
||||
target_p = find_person(parv[1]);
|
||||
|
||||
if (target_p != NULL)
|
||||
show_privs(source_p, target_p);
|
||||
}
|
||||
|
||||
|
@ -135,13 +140,24 @@ static void
|
|||
mo_privs(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
|
||||
{
|
||||
struct Client *target_p;
|
||||
struct Client *server_p;
|
||||
|
||||
if (parc < 2 || EmptyString(parv[1]))
|
||||
target_p = source_p;
|
||||
{
|
||||
server_p = target_p = source_p;
|
||||
}
|
||||
else
|
||||
{
|
||||
target_p = find_named_person(parv[1]);
|
||||
if (target_p == NULL)
|
||||
if (parc >= 3)
|
||||
{
|
||||
server_p = find_named_client(parv[1]);
|
||||
target_p = find_named_person(parv[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
server_p = target_p = find_named_person(parv[1]);
|
||||
}
|
||||
if (server_p == NULL || target_p == NULL)
|
||||
{
|
||||
sendto_one_numeric(source_p, ERR_NOSUCHNICK,
|
||||
form_str(ERR_NOSUCHNICK), parv[1]);
|
||||
|
@ -149,12 +165,15 @@ mo_privs(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source
|
|||
}
|
||||
}
|
||||
|
||||
if (MyClient(target_p))
|
||||
if (!IsServer(server_p))
|
||||
server_p = server_p->servptr;
|
||||
|
||||
if (IsMe(server_p))
|
||||
show_privs(source_p, target_p);
|
||||
else
|
||||
sendto_one(target_p, ":%s ENCAP %s PRIVS %s",
|
||||
get_id(source_p, target_p),
|
||||
target_p->servptr->name,
|
||||
sendto_one(server_p, ":%s ENCAP %s PRIVS %s",
|
||||
get_id(source_p, server_p),
|
||||
server_p->name,
|
||||
use_id(target_p));
|
||||
}
|
||||
|
||||
|
|
|
@ -318,11 +318,14 @@ single_whois(struct Client *source_p, struct Client *target_p, int operspy)
|
|||
GlobalSetOptions.operstring));
|
||||
}
|
||||
|
||||
if(MyClient(target_p) && !EmptyString(target_p->localClient->opername) && IsOper(target_p) && IsOper(source_p))
|
||||
if(!EmptyString(target_p->user->opername) && IsOper(target_p) && IsOper(source_p))
|
||||
{
|
||||
char buf[512];
|
||||
const char *privset = "(missing)";
|
||||
if (target_p->user->privset != NULL)
|
||||
privset = target_p->user->privset->name;
|
||||
snprintf(buf, sizeof(buf), "is opered as %s, privset %s",
|
||||
target_p->localClient->opername, target_p->localClient->privset->name);
|
||||
target_p->user->opername, privset);
|
||||
sendto_one_numeric(source_p, RPL_WHOISSPECIAL, form_str(RPL_WHOISSPECIAL),
|
||||
target_p->name, buf);
|
||||
}
|
||||
|
|
|
@ -3898,8 +3898,8 @@ static void sendto_realops_snomask1(void)
|
|||
oper3->snomask = SNO_BOTS | SNO_SKILL;
|
||||
oper4->snomask = SNO_GENERAL | SNO_REJ;
|
||||
|
||||
oper3->localClient->privset = privilegeset_get("admin");
|
||||
oper4->localClient->privset = privilegeset_get("admin");
|
||||
oper3->user->privset = privilegeset_get("admin");
|
||||
oper4->user->privset = privilegeset_get("admin");
|
||||
|
||||
server->localClient->caps = CAP_ENCAP | CAP_TS6;
|
||||
server2->localClient->caps = 0;
|
||||
|
@ -4125,8 +4125,8 @@ static void sendto_realops_snomask1__tags(void)
|
|||
oper3->snomask = SNO_BOTS | SNO_SKILL;
|
||||
oper4->snomask = SNO_GENERAL | SNO_REJ;
|
||||
|
||||
oper3->localClient->privset = privilegeset_get("admin");
|
||||
oper4->localClient->privset = privilegeset_get("admin");
|
||||
oper3->user->privset = privilegeset_get("admin");
|
||||
oper4->user->privset = privilegeset_get("admin");
|
||||
|
||||
server->localClient->caps = CAP_ENCAP | CAP_TS6;
|
||||
server2->localClient->caps = 0;
|
||||
|
@ -4340,8 +4340,8 @@ static void sendto_realops_snomask_from1(void)
|
|||
oper3->snomask = SNO_BOTS | SNO_SKILL;
|
||||
oper4->snomask = SNO_GENERAL | SNO_REJ;
|
||||
|
||||
oper3->localClient->privset = privilegeset_get("admin");
|
||||
oper4->localClient->privset = privilegeset_get("admin");
|
||||
oper3->user->privset = privilegeset_get("admin");
|
||||
oper4->user->privset = privilegeset_get("admin");
|
||||
|
||||
sendto_realops_snomask_from(SNO_BOTS, L_ALL, &me, "Hello %s!", "World");
|
||||
is_client_sendq(":" TEST_ME_NAME " NOTICE * :*** Notice -- Hello World!" CRLF, oper1, "Matches mask; " MSG);
|
||||
|
@ -4460,8 +4460,8 @@ static void sendto_realops_snomask_from1__tags(void)
|
|||
oper3->snomask = SNO_BOTS | SNO_SKILL;
|
||||
oper4->snomask = SNO_GENERAL | SNO_REJ;
|
||||
|
||||
oper3->localClient->privset = privilegeset_get("admin");
|
||||
oper4->localClient->privset = privilegeset_get("admin");
|
||||
oper3->user->privset = privilegeset_get("admin");
|
||||
oper4->user->privset = privilegeset_get("admin");
|
||||
|
||||
sendto_realops_snomask_from(SNO_BOTS, L_ALL, &me, "Hello %s!", "World");
|
||||
is_client_sendq("@time=" ADVENTURE_TIME " :" TEST_ME_NAME " NOTICE * :*** Notice -- Hello World!" CRLF, oper1, "Matches mask; " MSG);
|
||||
|
|
Loading…
Reference in a new issue