From 78b2f6170781220658f7757e39800975b3aca366 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Fri, 9 Oct 2020 08:03:26 -0400 Subject: [PATCH] fix #1194 --- conventional.yaml | 7 ++++++- default.yaml | 7 ++++++- irc/client.go | 2 +- irc/config.go | 8 ++++++++ irc/handlers.go | 17 +++++++++-------- irc/server.go | 8 ++++---- 6 files changed, 34 insertions(+), 15 deletions(-) diff --git a/conventional.yaml b/conventional.yaml index ace5ad36..9b2d64b4 100644 --- a/conventional.yaml +++ b/conventional.yaml @@ -597,11 +597,16 @@ opers: class: "server-admin" # custom whois line - whois-line: is a cool dude + whois-line: is a server admin # custom hostname vhost: "n" + # normally, operator status is visible to unprivileged users in WHO and WHOIS + # responses. this can be disabled with 'hidden'. ('hidden' also causes the + # 'vhost' line above to be ignored.) + hidden: false + # modes are the modes to auto-set upon opering-up modes: +is acjknoqtuxv diff --git a/default.yaml b/default.yaml index 6af01d90..4bbcbdf8 100644 --- a/default.yaml +++ b/default.yaml @@ -625,11 +625,16 @@ opers: class: "server-admin" # custom whois line - whois-line: is a cool dude + whois-line: is a server admin # custom hostname vhost: "n" + # normally, operator status is visible to unprivileged users in WHO and WHOIS + # responses. this can be disabled with 'hidden'. ('hidden' also causes the + # 'vhost' line above to be ignored.) + hidden: false + # modes are the modes to auto-set upon opering-up modes: +is acjknoqtuxv diff --git a/irc/client.go b/irc/client.go index c03831ba..6256390a 100644 --- a/irc/client.go +++ b/irc/client.go @@ -1207,7 +1207,7 @@ func (client *Client) getVHostNoMutex() string { // hostserv vhost OR operclass vhost OR nothing (i.e., normal rdns hostmask) if client.vhost != "" { return client.vhost - } else if client.oper != nil { + } else if client.oper != nil && !client.oper.Hidden { return client.oper.Vhost } else { return "" diff --git a/irc/config.go b/irc/config.go index 5bb86853..e1571984 100644 --- a/irc/config.go +++ b/irc/config.go @@ -419,6 +419,7 @@ type OperConfig struct { Fingerprint *string // legacy name for certfp, #1050 Certfp string Auto bool + Hidden bool Modes string } @@ -723,9 +724,15 @@ type Oper struct { Pass []byte Certfp string Auto bool + Hidden bool Modes []modes.ModeChange } +// returns whether this is a publicly visible operator, for WHO/WHOIS purposes +func (oper *Oper) Visible(hasPrivs bool) bool { + return oper != nil && (hasPrivs || !oper.Hidden) +} + // Operators returns a map of operator configs from the given OperClass and config. func (conf *Config) Operators(oc map[string]*OperClass) (map[string]*Oper, error) { operators := make(map[string]*Oper) @@ -756,6 +763,7 @@ func (conf *Config) Operators(oc map[string]*OperClass) (map[string]*Oper, error } } oper.Auto = opConf.Auto + oper.Hidden = opConf.Hidden if oper.Pass == nil && oper.Certfp == "" { return nil, fmt.Errorf("Oper %s has neither a password nor a fingerprint", name) diff --git a/irc/handlers.go b/irc/handlers.go index 7f0b6ee8..c221e307 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -3018,7 +3018,7 @@ func (fields whoxFields) Has(field rune) bool { // [*][~|&|@|%|+][B] : // whox format: // [*][~|&|@|%|+][B] : -func (client *Client) rplWhoReply(channel *Channel, target *Client, rb *ResponseBuffer, isWhox bool, fields whoxFields, whoType string) { +func (client *Client) rplWhoReply(channel *Channel, target *Client, rb *ResponseBuffer, hasPrivs, isWhox bool, fields whoxFields, whoType string) { params := []string{client.Nick()} details := target.Details() @@ -3038,7 +3038,7 @@ func (client *Client) rplWhoReply(channel *Channel, target *Client, rb *Response } if fields.Has('i') { fIP := "255.255.255.255" - if client.HasMode(modes.Operator) || client == target { + if hasPrivs || client == target { // you can only see a target's IP if they're you or you're an oper fIP = target.IPString() } @@ -3061,7 +3061,7 @@ func (client *Client) rplWhoReply(channel *Channel, target *Client, rb *Response flags.WriteRune('H') // Here } - if target.HasMode(modes.Operator) { + if target.HasMode(modes.Operator) && target.Oper().Visible(hasPrivs) { flags.WriteRune('*') } @@ -3169,7 +3169,7 @@ func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Respo } for _, member := range members { if !member.HasMode(modes.Invisible) || isJoined || isOper { - client.rplWhoReply(channel, member, rb, isWhox, fields, whoType) + client.rplWhoReply(channel, member, rb, isOper, isWhox, fields, whoType) } } } @@ -3200,7 +3200,7 @@ func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Respo for mclient := range server.clients.FindAll(mask) { if isOper || !mclient.HasMode(modes.Invisible) || isFriend(mclient) { - client.rplWhoReply(nil, mclient, rb, isWhox, fields, whoType) + client.rplWhoReply(nil, mclient, rb, isOper, isWhox, fields, whoType) } } } @@ -3238,7 +3238,8 @@ func whoisHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res return true } - if client.HasMode(modes.Operator) { + hasPrivs := client.HasMode(modes.Operator) // TODO(#1176) figure out the right capab for this + if hasPrivs { for _, mask := range strings.Split(masksString, ",") { matches := server.clients.FindAll(mask) if len(matches) == 0 && !handleService(mask) { @@ -3246,7 +3247,7 @@ func whoisHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res continue } for mclient := range matches { - client.getWhoisOf(mclient, rb) + client.getWhoisOf(mclient, hasPrivs, rb) } } } else { @@ -3254,7 +3255,7 @@ func whoisHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res nick := strings.Split(masksString, ",")[0] mclient := server.clients.Get(nick) if mclient != nil { - client.getWhoisOf(mclient, rb) + client.getWhoisOf(mclient, hasPrivs, rb) } else if !handleService(nick) { rb.Add(nil, client.server.name, ERR_NOSUCHNICK, client.Nick(), utils.SafeErrorParam(masksString), client.t("No such nick")) } diff --git a/irc/server.go b/irc/server.go index 5b0f5593..807b3e5d 100644 --- a/irc/server.go +++ b/irc/server.go @@ -429,7 +429,7 @@ func (client *Client) WhoisChannelsNames(target *Client, multiPrefix bool) []str return chstrs } -func (client *Client) getWhoisOf(target *Client, rb *ResponseBuffer) { +func (client *Client) getWhoisOf(target *Client, hasPrivs bool, rb *ResponseBuffer) { cnick := client.Nick() targetInfo := target.Details() rb.Add(nil, client.server.name, RPL_WHOISUSER, cnick, targetInfo.nick, targetInfo.username, targetInfo.hostname, "*", targetInfo.realname) @@ -440,10 +440,10 @@ func (client *Client) getWhoisOf(target *Client, rb *ResponseBuffer) { rb.Add(nil, client.server.name, RPL_WHOISCHANNELS, cnick, tnick, strings.Join(whoischannels, " ")) } tOper := target.Oper() - if tOper != nil { + if tOper.Visible(hasPrivs) { rb.Add(nil, client.server.name, RPL_WHOISOPERATOR, cnick, tnick, tOper.WhoisLine) } - if client == target || client.HasRoleCapabs("local_ban") { + if client == target || hasPrivs { rb.Add(nil, client.server.name, RPL_WHOISACTUALLY, cnick, tnick, fmt.Sprintf("%s@%s", targetInfo.username, target.RawHostname()), target.IPString(), client.t("Actual user@host, Actual IP")) } if target.HasMode(modes.TLS) { @@ -456,7 +456,7 @@ func (client *Client) getWhoisOf(target *Client, rb *ResponseBuffer) { rb.Add(nil, client.server.name, RPL_WHOISBOT, cnick, tnick, ircfmt.Unescape(fmt.Sprintf(client.t("is a $bBot$b on %s"), client.server.Config().Network.Name))) } - if client == target || client.HasMode(modes.Operator) { + if client == target || hasPrivs { for _, session := range target.Sessions() { if session.certfp != "" { rb.Add(nil, client.server.name, RPL_WHOISCERTFP, cnick, tnick, fmt.Sprintf(client.t("has client certificate fingerprint %s"), session.certfp))