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

Merge pull request #1024 from slingamn/issue1020_password.3

fix #1020
This commit is contained in:
Shivaram Lingamneni 2020-05-18 00:58:51 -07:00 committed by GitHub
commit 6028953ed4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 69 additions and 13 deletions

View File

@ -330,6 +330,10 @@ accounts:
# PASS as well, so it can be configured to authenticate with SASL only. # PASS as well, so it can be configured to authenticate with SASL only.
skip-server-password: false skip-server-password: false
# enable login to accounts via the PASS command, e.g., PASS account:password
# this is sometimes useful for compatibility with old clients that don't support SASL
login-via-pass-command: false
# require-sasl controls whether clients are required to have accounts # require-sasl controls whether clients are required to have accounts
# (and sign into them using SASL) to connect to the server # (and sign into them using SASL) to connect to the server
require-sasl: require-sasl:

View File

@ -97,6 +97,15 @@ func (s *saslStatus) Clear() {
*s = saslStatus{} *s = saslStatus{}
} }
// what stage the client is at w.r.t. the PASS command:
type serverPassStatus uint
const (
serverPassUnsent serverPassStatus = iota
serverPassSuccessful
serverPassFailed
)
// Session is an individual client connection to the server (TCP connection // Session is an individual client connection to the server (TCP connection
// and associated per-connection data, such as capabilities). There is a // and associated per-connection data, such as capabilities). There is a
// many-one relationship between sessions and clients. // many-one relationship between sessions and clients.
@ -117,9 +126,9 @@ type Session struct {
deferredFakelagCount int deferredFakelagCount int
destroyed uint32 destroyed uint32
certfp string certfp string
sasl saslStatus sasl saslStatus
sentPassCommand bool passStatus serverPassStatus
batchCounter uint32 batchCounter uint32
@ -510,7 +519,7 @@ const (
func (client *Client) isAuthorized(config *Config, session *Session) AuthOutcome { func (client *Client) isAuthorized(config *Config, session *Session) AuthOutcome {
saslSent := client.account != "" saslSent := client.account != ""
// PASS requirement // PASS requirement
if (config.Server.passwordBytes != nil) && !session.sentPassCommand && !(config.Accounts.SkipServerPassword && saslSent) { if (config.Server.passwordBytes != nil) && session.passStatus != serverPassSuccessful && !(config.Accounts.SkipServerPassword && saslSent) {
return authFailPass return authFailPass
} }
// Tor connections may be required to authenticate with SASL // Tor connections may be required to authenticate with SASL

View File

@ -254,12 +254,13 @@ type AccountConfig struct {
Exempted []string Exempted []string
exemptedNets []net.IPNet exemptedNets []net.IPNet
} `yaml:"require-sasl"` } `yaml:"require-sasl"`
DefaultUserModes *string `yaml:"default-user-modes"` DefaultUserModes *string `yaml:"default-user-modes"`
defaultUserModes modes.ModeChanges defaultUserModes modes.ModeChanges
LDAP ldap.ServerConfig LDAP ldap.ServerConfig
LoginThrottling ThrottleConfig `yaml:"login-throttling"` LoginThrottling ThrottleConfig `yaml:"login-throttling"`
SkipServerPassword bool `yaml:"skip-server-password"` SkipServerPassword bool `yaml:"skip-server-password"`
NickReservation struct { LoginViaPassCommand bool `yaml:"login-via-pass-command"`
NickReservation struct {
Enabled bool Enabled bool
AdditionalNickLimit int `yaml:"additional-nick-limit"` AdditionalNickLimit int `yaml:"additional-nick-limit"`
Method NickEnforcementMethod Method NickEnforcementMethod
@ -1078,6 +1079,9 @@ func LoadConfig(filename string) (config *Config, err error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if config.Accounts.LoginViaPassCommand && !config.Accounts.SkipServerPassword {
return nil, errors.New("Using a server password and login-via-pass-command requires skip-server-password as well")
}
} }
if config.Accounts.Registration.BcryptCost == 0 { if config.Accounts.Registration.BcryptCost == 0 {

View File

@ -2159,18 +2159,53 @@ func passHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
rb.Add(nil, server.name, ERR_ALREADYREGISTRED, client.nick, client.t("You may not reregister")) rb.Add(nil, server.name, ERR_ALREADYREGISTRED, client.nick, client.t("You may not reregister"))
return false return false
} }
// only give them one try to run the PASS command (all code paths end with this
// variable being set):
if rb.session.passStatus != serverPassUnsent {
return false
}
password := msg.Params[0]
config := server.Config()
if config.Accounts.LoginViaPassCommand {
colonIndex := strings.IndexByte(password, ':')
if colonIndex != -1 && client.Account() == "" {
// TODO consolidate all login throttle checks into AccountManager
throttled, _ := client.loginThrottle.Touch()
if !throttled {
account, accountPass := password[:colonIndex], password[colonIndex+1:]
err := server.accounts.AuthenticateByPassphrase(client, account, accountPass)
if err == nil {
sendSuccessfulAccountAuth(client, rb, false, true)
// login-via-pass-command entails that we do not need to check
// an actual server password (either no password or skip-server-password)
rb.session.passStatus = serverPassSuccessful
return false
}
}
}
}
// if login-via-PASS failed for any reason, proceed to try and interpret the
// provided password as the server password
serverPassword := config.Server.passwordBytes
// if no password exists, skip checking // if no password exists, skip checking
serverPassword := server.Config().Server.passwordBytes
if serverPassword == nil { if serverPassword == nil {
return false return false
} }
// check the provided password // check the provided password
password := []byte(msg.Params[0]) if bcrypt.CompareHashAndPassword(serverPassword, []byte(password)) == nil {
rb.session.sentPassCommand = bcrypt.CompareHashAndPassword(serverPassword, password) == nil rb.session.passStatus = serverPassSuccessful
} else {
rb.session.passStatus = serverPassFailed
}
// if they failed the check, we'll bounce them later when they try to complete registration // if they failed the check, we'll bounce them later when they try to complete registration
// note in particular that with skip-server-password, you can give the wrong server
// password here, then successfully SASL and be admitted
return false return false
} }

View File

@ -351,6 +351,10 @@ accounts:
# PASS as well, so it can be configured to authenticate with SASL only. # PASS as well, so it can be configured to authenticate with SASL only.
skip-server-password: false skip-server-password: false
# enable login to accounts via the PASS command, e.g., PASS account:password
# this is sometimes useful for compatibility with old clients that don't support SASL
login-via-pass-command: false
# require-sasl controls whether clients are required to have accounts # require-sasl controls whether clients are required to have accounts
# (and sign into them using SASL) to connect to the server # (and sign into them using SASL) to connect to the server
require-sasl: require-sasl: