diff --git a/irc/client.go b/irc/client.go index b75b822e..be6366da 100644 --- a/irc/client.go +++ b/irc/client.go @@ -121,15 +121,13 @@ type Session struct { // sets the session quit message, if there isn't one already func (sd *Session) SetQuitMessage(message string) (set bool) { if message == "" { - if sd.quitMessage == "" { - sd.quitMessage = "Connection closed" - return true - } else { - return false - } - } else { + message = "Connection closed" + } + if sd.quitMessage == "" { sd.quitMessage = message return true + } else { + return false } } @@ -286,18 +284,30 @@ func (client *Client) doIdentLookup(conn net.Conn) { } } -func (client *Client) isAuthorized(config *Config) bool { +type AuthOutcome uint + +const ( + authSuccess AuthOutcome = iota + authFailPass + authFailTorSaslRequired + authFailSaslRequired +) + +func (client *Client) isAuthorized(config *Config) AuthOutcome { saslSent := client.account != "" // PASS requirement if (config.Server.passwordBytes != nil) && !client.sentPassCommand && !(config.Accounts.SkipServerPassword && saslSent) { - return false + return authFailPass } // Tor connections may be required to authenticate with SASL if client.isTor && config.Server.TorListeners.RequireSasl && !saslSent { - return false + return authFailTorSaslRequired } // finally, enforce require-sasl - return !config.Accounts.RequireSasl.Enabled || saslSent || utils.IPInNets(client.IP(), config.Accounts.RequireSasl.exemptedNets) + if config.Accounts.RequireSasl.Enabled && !saslSent && !utils.IPInNets(client.IP(), config.Accounts.RequireSasl.exemptedNets) { + return authFailSaslRequired + } + return authSuccess } func (session *Session) resetFakelag() { diff --git a/irc/commands.go b/irc/commands.go index 38d50c66..f5049948 100644 --- a/irc/commands.go +++ b/irc/commands.go @@ -45,8 +45,8 @@ func (cmd *Command) Run(server *Server, client *Client, session *Session, msg ir rb.Send(true) // after each command, see if we can send registration to the client - if !client.registered { - server.tryRegister(client, session) + if !exiting && !client.registered { + exiting = server.tryRegister(client, session) } // most servers do this only for PING/PONG, but we'll do it for any command: diff --git a/irc/handlers.go b/irc/handlers.go index d9f18863..106ce7d9 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -2229,13 +2229,9 @@ func passHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp // check the provided password password := []byte(msg.Params[0]) - if bcrypt.CompareHashAndPassword(serverPassword, password) != nil { - rb.Add(nil, server.name, ERR_PASSWDMISMATCH, client.nick, client.t("Password incorrect")) - client.Quit(client.t("Password incorrect"), rb.session) - return true - } + client.sentPassCommand = bcrypt.CompareHashAndPassword(serverPassword, password) == nil - client.sentPassCommand = true + // if they failed the check, we'll bounce them later when they try to complete registration return false } diff --git a/irc/idletimer.go b/irc/idletimer.go index 1cda700a..c16065c7 100644 --- a/irc/idletimer.go +++ b/irc/idletimer.go @@ -274,8 +274,10 @@ func (nt *NickTimer) Touch(rb *ResponseBuffer) { for _, mSession := range nt.client.Sessions() { if mSession == session { rb.Add(nil, nsPrefix, "NOTICE", tnick, message) + rb.Add(nil, nt.client.server.name, "WARN", "*", "ACCOUNT_REQUIRED", message) } else { mSession.Send(nil, nsPrefix, "NOTICE", tnick, message) + mSession.Send(nil, nt.client.server.name, "WARN", "*", "ACCOUNT_REQUIRED", message) } } } else if shouldRename { diff --git a/irc/numerics.go b/irc/numerics.go index 2a87256e..f2f076d3 100644 --- a/irc/numerics.go +++ b/irc/numerics.go @@ -165,7 +165,6 @@ const ( ERR_CANNOTSENDRP = "573" RPL_WHOISSECURE = "671" RPL_YOURLANGUAGESARE = "687" - RPL_WHOISLANGUAGE = "690" ERR_CHANNAMEINUSE = "692" ERR_CANNOTRENAME = "693" RPL_HELPSTART = "704" diff --git a/irc/server.go b/irc/server.go index 9266ad7b..436b607e 100644 --- a/irc/server.go +++ b/irc/server.go @@ -330,7 +330,7 @@ func (server *Server) createListener(addr string, tlsConfig *tls.Config, isTor b // server functionality // -func (server *Server) tryRegister(c *Client, session *Session) { +func (server *Server) tryRegister(c *Client, session *Session) (exiting bool) { // if the session just sent us a RESUME line, try to resume if session.resumeDetails != nil { session.tryResume() @@ -344,11 +344,19 @@ func (server *Server) tryRegister(c *Client, session *Session) { // client MUST send PASS if necessary, or authenticate with SASL if necessary, // before completing the other registration commands - config := server.Config() - if !c.isAuthorized(config) { - c.Quit(c.t("Bad password"), nil) - c.destroy(nil) - return + authOutcome := c.isAuthorized(server.Config()) + var quitMessage string + switch authOutcome { + case authFailPass: + quitMessage = c.t("Password incorrect") + c.Send(nil, server.name, ERR_PASSWDMISMATCH, "*", quitMessage) + case authFailSaslRequired, authFailTorSaslRequired: + quitMessage = c.t("You must log in with SASL to join this server") + c.Send(nil, c.server.name, "FAIL", "*", "ACCOUNT_REQUIRED", quitMessage) + } + if authOutcome != authSuccess { + c.Quit(quitMessage, nil) + return true } rb := NewResponseBuffer(session) @@ -363,8 +371,7 @@ func (server *Server) tryRegister(c *Client, session *Session) { isBanned, info := server.klines.CheckMasks(c.AllNickmasks()...) if isBanned { c.Quit(info.BanMessage(c.t("You are banned from this server (%s)")), nil) - c.destroy(nil) - return + return true } if session.client != c { @@ -377,12 +384,13 @@ func (server *Server) tryRegister(c *Client, session *Session) { // registration has succeeded: c.SetRegistered() + // count new user in statistics server.stats.ChangeTotal(1) + server.monitorManager.AlertAbout(c, true) server.playRegistrationBurst(session) - - server.monitorManager.AlertAbout(c, true) + return false } func (server *Server) playRegistrationBurst(session *Session) { @@ -480,14 +488,6 @@ 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))) } - tLanguages := target.Languages() - if 0 < len(tLanguages) { - params := []string{cnick, tnick} - params = append(params, client.server.Languages().Codes(tLanguages)...) - params = append(params, client.t("can speak these languages")) - rb.Add(nil, client.server.name, RPL_WHOISLANGUAGE, params...) - } - if target.certfp != "" && (client.HasMode(modes.Operator) || client == target) { rb.Add(nil, client.server.name, RPL_WHOISCERTFP, cnick, tnick, fmt.Sprintf(client.t("has client certificate fingerprint %s"), target.certfp)) }