3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-29 07:29:31 +01:00

implement review feedback

1. If both fingerprint and hash are specified, require both instead of either
2. Implement auto-oper on connect
This commit is contained in:
Shivaram Lingamneni 2019-12-19 06:33:43 -05:00
parent 6033d9f569
commit b717402b5e
6 changed files with 78 additions and 23 deletions

View File

@ -1354,3 +1354,18 @@ func (client *Client) CheckInvited(casefoldedChannel string) (invited bool) {
delete(client.invitedTo, casefoldedChannel) delete(client.invitedTo, casefoldedChannel)
return return
} }
// Implements auto-oper by certfp (scans for an auto-eligible operator block that matches
// the client's cert, then applies it).
func (client *Client) attemptAutoOper(session *Session) {
if client.certfp == "" || client.HasMode(modes.Operator) {
return
}
for _, oper := range client.server.Config().operators {
if oper.Auto && oper.Pass == nil && utils.CertfpsMatch(oper.Fingerprint, client.certfp) {
rb := NewResponseBuffer(session)
applyOper(client, oper, rb)
rb.Send(true)
}
}
}

View File

@ -212,6 +212,7 @@ type OperConfig struct {
WhoisLine string `yaml:"whois-line"` WhoisLine string `yaml:"whois-line"`
Password string Password string
Fingerprint string Fingerprint string
Auto bool
Modes string Modes string
} }
@ -461,6 +462,7 @@ type Oper struct {
Vhost string Vhost string
Pass []byte Pass []byte
Fingerprint string Fingerprint string
Auto bool
Modes []modes.ModeChange Modes []modes.ModeChange
} }
@ -477,11 +479,18 @@ func (conf *Config) Operators(oc map[string]*OperClass) (map[string]*Oper, error
} }
oper.Name = name oper.Name = name
if opConf.Password != "" {
oper.Pass, err = decodeLegacyPasswordHash(opConf.Password) oper.Pass, err = decodeLegacyPasswordHash(opConf.Password)
if err != nil { if err != nil {
return nil, err return nil, err
} }
}
oper.Fingerprint = opConf.Fingerprint oper.Fingerprint = opConf.Fingerprint
oper.Auto = opConf.Auto
if oper.Pass == nil && oper.Fingerprint == "" {
return nil, fmt.Errorf("Oper %s has neither a password nor a fingerprint", name)
}
oper.Vhost = opConf.Vhost oper.Vhost = opConf.Vhost
class, exists := oc[opConf.Class] class, exists := oc[opConf.Class]

View File

@ -2177,14 +2177,19 @@ func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
return false return false
} }
authorized := false // must have a matching oper block and not fail any enabled checks
// (config validation ensures that there is at least one check)
oper := server.GetOperator(msg.Params[0]) oper := server.GetOperator(msg.Params[0])
authorized := oper != nil
if oper != nil { if oper != nil {
if utils.CertfpsMatch(oper.Fingerprint, client.certfp) { if oper.Fingerprint != "" && !utils.CertfpsMatch(oper.Fingerprint, client.certfp) {
authorized = true authorized = false
} else if 1 < len(msg.Params) { } else if oper.Pass != nil {
password := []byte(msg.Params[1]) if len(msg.Params) == 1 {
authorized = (bcrypt.CompareHashAndPassword(oper.Pass, password) == nil) authorized = false
} else if bcrypt.CompareHashAndPassword(oper.Pass, []byte(msg.Params[1])) != nil {
authorized = false
}
} }
} }
if !authorized { if !authorized {
@ -2193,9 +2198,17 @@ func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
return true return true
} }
oldNickmask := client.NickMaskString() applyOper(client, oper, rb)
return false
}
// applies operator status to a client, who MUST NOT already be an operator
func applyOper(client *Client, oper *Oper, rb *ResponseBuffer) {
details := client.Details()
oldNickmask := details.nickMask
client.SetOper(oper) client.SetOper(oper)
if client.NickMaskString() != oldNickmask { newNickmask := client.NickMaskString()
if newNickmask != oldNickmask {
client.sendChghost(oldNickmask, oper.Vhost) client.sendChghost(oldNickmask, oper.Vhost)
} }
@ -2208,17 +2221,14 @@ func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
copy(modeChanges[1:], oper.Modes) copy(modeChanges[1:], oper.Modes)
applied := ApplyUserModeChanges(client, modeChanges, true) applied := ApplyUserModeChanges(client, modeChanges, true)
rb.Add(nil, server.name, RPL_YOUREOPER, client.nick, client.t("You are now an IRC operator")) client.server.snomasks.Send(sno.LocalOpers, fmt.Sprintf(ircfmt.Unescape("Client opered up $c[grey][$r%s$c[grey], $r%s$c[grey]]"), client.nickMaskString, oper.Name))
rb.Add(nil, server.name, "MODE", client.nick, applied.String())
server.snomasks.Send(sno.LocalOpers, fmt.Sprintf(ircfmt.Unescape("Client opered up $c[grey][$r%s$c[grey], $r%s$c[grey]]"), client.nickMaskString, oper.Name)) rb.Broadcast(nil, client.server.name, RPL_YOUREOPER, details.nick, client.t("You are now an IRC operator"))
rb.Broadcast(nil, client.server.name, "MODE", details.nick, applied.String())
// client may now be unthrottled by the fakelag system
for _, session := range client.Sessions() { for _, session := range client.Sessions() {
// client may now be unthrottled by the fakelag system
session.resetFakelag() session.resetFakelag()
} }
return false
} }
// PART <channel>{,<channel>} [<reason>] // PART <channel>{,<channel>} [<reason>]

View File

@ -77,6 +77,18 @@ func (rb *ResponseBuffer) Add(tags map[string]string, prefix string, command str
rb.AddMessage(ircmsg.MakeMessage(tags, prefix, command, params...)) rb.AddMessage(ircmsg.MakeMessage(tags, prefix, command, params...))
} }
// Broadcast adds a standard new message to our queue, then sends an unlabeled copy
// to all other sessions.
func (rb *ResponseBuffer) Broadcast(tags map[string]string, prefix string, command string, params ...string) {
// can't reuse the IrcMessage object because of tag pollution :-\
rb.Add(tags, prefix, command, params...)
for _, session := range rb.session.client.Sessions() {
if session != rb.session {
session.Send(tags, prefix, command, params...)
}
}
}
// AddFromClient adds a new message from a specific client to our queue. // AddFromClient adds a new message from a specific client to our queue.
func (rb *ResponseBuffer) AddFromClient(time time.Time, msgid string, fromNickMask string, fromAccount string, tags map[string]string, command string, params ...string) { func (rb *ResponseBuffer) AddFromClient(time time.Time, msgid string, fromNickMask string, fromAccount string, tags map[string]string, command string, params ...string) {
msg := ircmsg.MakeMessage(nil, fromNickMask, command, params...) msg := ircmsg.MakeMessage(nil, fromNickMask, command, params...)

View File

@ -440,6 +440,9 @@ func (server *Server) playRegistrationBurst(session *Session) {
if modestring != "+" { if modestring != "+" {
session.Send(nil, d.nickMask, RPL_UMODEIS, d.nick, modestring) session.Send(nil, d.nickMask, RPL_UMODEIS, d.nick, modestring)
} }
c.attemptAutoOper(session)
if server.logger.IsLoggingRawIO() { if server.logger.IsLoggingRawIO() {
session.Send(nil, c.server.name, "NOTICE", d.nick, c.t("This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) session.Send(nil, c.server.name, "NOTICE", d.nick, c.t("This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect."))
} }

View File

@ -446,13 +446,19 @@ opers:
# modes are the modes to auto-set upon opering-up # modes are the modes to auto-set upon opering-up
modes: +is acjknoqtux modes: +is acjknoqtux
# password to login with /OPER command # operators can be authenticated either by password (with the /OPER command),
# generated using "oragono genpasswd" # or by certificate fingerprint, or both. if a password hash is set, then a
# password is required to oper up (e.g., /OPER dan mypassword). to generate
# the hash, use `oragono genpasswd`.
password: "$2a$04$LiytCxaY0lI.guDj2pBN4eLRD5cdM2OLDwqmGAgB6M2OPirbF5Jcu" password: "$2a$04$LiytCxaY0lI.guDj2pBN4eLRD5cdM2OLDwqmGAgB6M2OPirbF5Jcu"
# if you're logged in using the client cert with this SHA-256 fingerprint, # if a SHA-256 certificate fingerprint is configured here, then it will be
# you'll be able to /OPER without a password # required to /OPER. if you comment out the password hash above, then you can
fingerprint: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789" # /OPER without a password.
#fingerprint: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
# if 'auto' is set (and no password hash is set), operator permissions will be
# granted automatically as soon as you connect with the right fingerprint.
#auto: true
# logging, takes inspiration from Insp # logging, takes inspiration from Insp
logging: logging: