3
0
mirror of https://github.com/ergochat/ergo.git synced 2025-11-13 20:47:24 +01:00

changes to OPER command

* Impose a throttle on OPER attempts regardless of whether they caused a
  password check.
* Never disconnect the client on a failed attempt, even if there was a
  password check.
* Change error numeric to ERR_NOOPERHOST
* Explicit information about the failure in the server log (copying Insp)

Fixes #2296.
This commit is contained in:
Shivaram Lingamneni 2025-11-09 19:32:25 -05:00
parent efc1627d23
commit 6fdac13ad4
6 changed files with 43 additions and 8 deletions

View File

@ -358,6 +358,10 @@ server:
secure-nets:
# - "10.0.0.0/8"
# allow attempts to OPER with a password at most this often. default to
# 10 seconds when unset.
oper-throttle: 10s
# Ergo will write files to disk under certain circumstances, e.g.,
# CPU profiling or data export. by default, these files will be written
# to the working directory. set this to customize:

View File

@ -527,7 +527,7 @@ If your client or bot is failing to connect to Ergo, here are some things to che
## Why can't I oper?
If you try to oper unsuccessfully, Ergo will disconnect you from the network. If you're unable to oper, here are some things to double-check:
If your `OPER` command fails, check your server logs for more information. Here are some general issues to double-check:
1. Did you correctly generate the hashed password with `ergo genpasswd`?
1. Did you add the password hash to the correct config file, then save the file?

View File

@ -189,6 +189,8 @@ type Session struct {
fakelag Fakelag
deferredFakelagCount int
lastOperAttempt time.Time
certfp string
peerCerts []*x509.Certificate
sasl saslStatus

View File

@ -599,6 +599,7 @@ type Config struct {
Cloaks cloaks.CloakConfig `yaml:"ip-cloaking"`
SecureNetDefs []string `yaml:"secure-nets"`
secureNets []net.IPNet
OperThrottle time.Duration `yaml:"oper-throttle"`
supportedCaps *caps.Set
supportedCapsWithoutSTS *caps.Set
capValues caps.Values
@ -1480,6 +1481,10 @@ func LoadConfig(filename string) (config *Config, err error) {
config.Server.supportedCaps.Disable(caps.SASL)
}
if config.Server.OperThrottle <= 0 {
config.Server.OperThrottle = 10 * time.Second
}
if err := config.Accounts.OAuth2.Postprocess(); err != nil {
return nil, err
}

View File

@ -2545,8 +2545,19 @@ func operHandler(server *Server, client *Client, msg ircmsg.Message, rb *Respons
return false
}
config := server.Config()
now := time.Now()
nextAllowableAttempt := rb.session.lastOperAttempt.Add(config.Server.OperThrottle)
if now.Before(nextAllowableAttempt) {
timeLeft := nextAllowableAttempt.Sub(now).Round(time.Millisecond)
rb.Add(nil, server.name, ERR_NOOPERHOST, client.Nick(), fmt.Sprintf(client.t("You must wait %v before issuing OPER again"), timeLeft))
return false
}
rb.session.lastOperAttempt = now
// must pass at least one check, and all enabled checks
var checkPassed, checkFailed, passwordFailed bool
var checkPassed, checkFailed, certFailed, passwordFailed bool
oper := server.GetOperator(msg.Params[0])
if oper != nil {
if oper.Certfp != "" {
@ -2554,11 +2565,13 @@ func operHandler(server *Server, client *Client, msg ircmsg.Message, rb *Respons
checkPassed = true
} else {
checkFailed = true
certFailed = true
}
}
if !checkFailed && oper.Pass != nil {
if len(msg.Params) == 1 {
checkFailed = true
passwordFailed = true
} else if bcrypt.CompareHashAndPassword(oper.Pass, []byte(msg.Params[1])) != nil {
checkFailed = true
passwordFailed = true
@ -2569,14 +2582,21 @@ func operHandler(server *Server, client *Client, msg ircmsg.Message, rb *Respons
}
if !checkPassed || checkFailed {
rb.Add(nil, server.name, ERR_PASSWDMISMATCH, client.Nick(), client.t("Password incorrect"))
// #951: only disconnect them if we actually tried to check a password for them
if passwordFailed {
client.Quit(client.t("Password incorrect"), rb.session)
return true
rb.Add(nil, server.name, ERR_NOOPERHOST, client.Nick(), client.t("OPER failed; check the server logs for details."))
// hopefully not too spammy given the throttling:
if oper == nil {
server.logger.Info("opers", "OPER failed with invalid oper name", msg.Params[0])
} else if certFailed {
server.logger.Info("opers", "OPER attempt for", msg.Params[0], "failed with invalid certfp")
} else if passwordFailed {
server.logger.Info("opers", "OPER attempt for", msg.Params[0], "failed with invalid password")
} else {
return false
// should not be possible given config validation
server.logger.Info("opers", "OPER attempt for", msg.Params[0], "failed with invalid config")
}
return false
}
if oper != nil {

View File

@ -330,6 +330,10 @@ server:
secure-nets:
# - "10.0.0.0/8"
# allow attempts to OPER with a password at most this often. default to
# 10 seconds when unset.
oper-throttle: 10s
# Ergo will write files to disk under certain circumstances, e.g.,
# CPU profiling or data export. by default, these files will be written
# to the working directory. set this to customize: