diff --git a/doc/technical/ts6-protocol.txt b/doc/technical/ts6-protocol.txt index 05a4955c..cbc87986 100644 --- a/doc/technical/ts6-protocol.txt +++ b/doc/technical/ts6-protocol.txt @@ -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 diff --git a/extensions/extb_oper.c b/extensions/extb_oper.c index 72908b29..0400e724 100644 --- a/extensions/extb_oper.c +++ b/extensions/extb_oper.c @@ -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 */ diff --git a/include/client.h b/include/client.h index 9ded2c87..ccc3c2e9 100644 --- a/include/client.h +++ b/include/client.h @@ -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; diff --git a/include/s_newconf.h b/include/s_newconf.h index 41e74442..cced2d07 100644 --- a/include/s_newconf.h +++ b/include/s_newconf.h @@ -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")) diff --git a/ircd/client.c b/ircd/client.c index 76cb826b..a19c664f 100644 --- a/ircd/client.c +++ b/ircd/client.c @@ -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 */ diff --git a/ircd/s_conf.c b/ircd/s_conf.c index dbed64d0..4cbb0d5e 100644 --- a/ircd/s_conf.c +++ b/ircd/s_conf.c @@ -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; } diff --git a/ircd/s_serv.c b/ircd/s_serv.c index bc77b43c..a001cd16 100644 --- a/ircd/s_serv.c +++ b/ircd/s_serv.c @@ -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); } diff --git a/ircd/s_user.c b/ircd/s_user.c index 0c69f528..3a3dc8b6 100644 --- a/ircd/s_user.c +++ b/ircd/s_user.c @@ -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)) diff --git a/modules/m_challenge.c b/modules/m_challenge.c index d9a285ac..8174acbd 100644 --- a/modules/m_challenge.c +++ b/modules/m_challenge.c @@ -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."); diff --git a/modules/m_grant.c b/modules/m_grant.c index 20c3a622..3788fb95 100644 --- a/modules/m_grant.c +++ b/modules/m_grant.c @@ -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; } diff --git a/modules/m_oper.c b/modules/m_oper.c index 9afef1f8..66bd1f78 100644 --- a/modules/m_oper.c +++ b/modules/m_oper.c @@ -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 * diff --git a/modules/m_privs.c b/modules/m_privs.c index c71327dc..a650203c 100644 --- a/modules/m_privs.c +++ b/modules/m_privs.c @@ -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)); } diff --git a/modules/m_whois.c b/modules/m_whois.c index d3384ed3..0fc0f4f7 100644 --- a/modules/m_whois.c +++ b/modules/m_whois.c @@ -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); } diff --git a/tests/send1.c b/tests/send1.c index e80aeccb..266e7a44 100644 --- a/tests/send1.c +++ b/tests/send1.c @@ -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);