Allow AUTHENTICATE before NICK

Now that dc.nick is not blank during registration, sasl replies from the
server are correct and cap handling can be a bit simplified.
This commit is contained in:
Hubert Hirtz 2021-11-18 09:19:27 +01:00 committed by Simon Ser
parent 98af48d254
commit dcc1eff130

View File

@ -178,7 +178,8 @@ func updateNetworkAttrs(record *Network, attrs irc.Tags, subcommand string) erro
} }
// ' ' and ':' break the IRC message wire format, '@' and '!' break prefixes, // ' ' and ':' break the IRC message wire format, '@' and '!' break prefixes,
// '*' and '?' break masks, '$' breaks server masks in PRIVMSG/NOTICE // '*' and '?' break masks, '$' breaks server masks in PRIVMSG/NOTICE,
// "*" is the reserved nickname for registration
const illegalNickChars = " :@!*?$" const illegalNickChars = " :@!*?$"
// permanentDownstreamCaps is the list of always-supported downstream // permanentDownstreamCaps is the list of always-supported downstream
@ -280,6 +281,8 @@ func newDownstreamConn(srv *Server, ic ircConn, id uint64) *downstreamConn {
dc := &downstreamConn{ dc := &downstreamConn{
conn: *newConn(srv, ic, &options), conn: *newConn(srv, ic, &options),
id: id, id: id,
nick: "*",
nickCM: "*",
supportedCaps: make(map[string]string), supportedCaps: make(map[string]string),
caps: make(map[string]bool), caps: make(map[string]bool),
monitored: newCasemapMap(0), monitored: newCasemapMap(0),
@ -684,27 +687,24 @@ func (dc *downstreamConn) handleMessageUnregistered(ctx context.Context, msg *ir
case "AUTHENTICATE": case "AUTHENTICATE":
if !dc.caps["sasl"] { if !dc.caps["sasl"] {
return ircError{&irc.Message{ return ircError{&irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_SASLFAIL, Command: irc.ERR_SASLFAIL,
Params: []string{"*", "AUTHENTICATE requires the \"sasl\" capability to be enabled"}, Params: []string{"*", "AUTHENTICATE requires the \"sasl\" capability to be enabled"},
}} }}
} }
if len(msg.Params) == 0 { if len(msg.Params) == 0 {
return ircError{&irc.Message{ return ircError{&irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_SASLFAIL, Command: irc.ERR_SASLFAIL,
Params: []string{"*", "Missing AUTHENTICATE argument"}, Params: []string{"*", "Missing AUTHENTICATE argument"},
}} }}
} }
if dc.nick == "" {
return ircError{&irc.Message{
Command: irc.ERR_SASLFAIL,
Params: []string{"*", "Expected NICK command before AUTHENTICATE"},
}}
}
var resp []byte var resp []byte
if msg.Params[0] == "*" { if msg.Params[0] == "*" {
dc.saslServer = nil dc.saslServer = nil
return ircError{&irc.Message{ return ircError{&irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_SASLABORTED, Command: irc.ERR_SASLABORTED,
Params: []string{"*", "SASL authentication aborted"}, Params: []string{"*", "SASL authentication aborted"},
}} }}
@ -720,6 +720,7 @@ func (dc *downstreamConn) handleMessageUnregistered(ctx context.Context, msg *ir
})) }))
default: default:
return ircError{&irc.Message{ return ircError{&irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_SASLFAIL, Command: irc.ERR_SASLFAIL,
Params: []string{"*", fmt.Sprintf("Unsupported SASL mechanism %q", mech)}, Params: []string{"*", fmt.Sprintf("Unsupported SASL mechanism %q", mech)},
}} }}
@ -733,6 +734,7 @@ func (dc *downstreamConn) handleMessageUnregistered(ctx context.Context, msg *ir
if err != nil { if err != nil {
dc.saslServer = nil dc.saslServer = nil
return ircError{&irc.Message{ return ircError{&irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_SASLFAIL, Command: irc.ERR_SASLFAIL,
Params: []string{"*", "Invalid base64-encoded response"}, Params: []string{"*", "Invalid base64-encoded response"},
}} }}
@ -744,6 +746,7 @@ func (dc *downstreamConn) handleMessageUnregistered(ctx context.Context, msg *ir
dc.saslServer = nil dc.saslServer = nil
if ircErr, ok := err.(ircError); ok && ircErr.Message.Command == irc.ERR_PASSWDMISMATCH { if ircErr, ok := err.(ircError); ok && ircErr.Message.Command == irc.ERR_PASSWDMISMATCH {
return ircError{&irc.Message{ return ircError{&irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_SASLFAIL, Command: irc.ERR_SASLFAIL,
Params: []string{"*", ircErr.Message.Params[1]}, Params: []string{"*", ircErr.Message.Params[1]},
}} }}
@ -823,7 +826,7 @@ func (dc *downstreamConn) handleMessageUnregistered(ctx context.Context, msg *ir
dc.logger.Printf("unhandled message: %v", msg) dc.logger.Printf("unhandled message: %v", msg)
return newUnknownCommandError(msg.Command) return newUnknownCommandError(msg.Command)
} }
if dc.rawUsername != "" && dc.nick != "" && !dc.negotiatingCaps { if dc.rawUsername != "" && dc.nick != "*" && !dc.negotiatingCaps {
return dc.register(ctx) return dc.register(ctx)
} }
return nil return nil
@ -832,11 +835,6 @@ func (dc *downstreamConn) handleMessageUnregistered(ctx context.Context, msg *ir
func (dc *downstreamConn) handleCapCommand(cmd string, args []string) error { func (dc *downstreamConn) handleCapCommand(cmd string, args []string) error {
cmd = strings.ToUpper(cmd) cmd = strings.ToUpper(cmd)
replyTo := dc.nick
if !dc.registered {
replyTo = "*"
}
switch cmd { switch cmd {
case "LS": case "LS":
if len(args) > 0 { if len(args) > 0 {
@ -867,7 +865,7 @@ func (dc *downstreamConn) handleCapCommand(cmd string, args []string) error {
dc.SendMessage(&irc.Message{ dc.SendMessage(&irc.Message{
Prefix: dc.srv.prefix(), Prefix: dc.srv.prefix(),
Command: "CAP", Command: "CAP",
Params: []string{replyTo, "LS", strings.Join(caps, " ")}, Params: []string{dc.nick, "LS", strings.Join(caps, " ")},
}) })
if dc.capVersion >= 302 { if dc.capVersion >= 302 {
@ -890,13 +888,13 @@ func (dc *downstreamConn) handleCapCommand(cmd string, args []string) error {
dc.SendMessage(&irc.Message{ dc.SendMessage(&irc.Message{
Prefix: dc.srv.prefix(), Prefix: dc.srv.prefix(),
Command: "CAP", Command: "CAP",
Params: []string{replyTo, "LIST", strings.Join(caps, " ")}, Params: []string{dc.nick, "LIST", strings.Join(caps, " ")},
}) })
case "REQ": case "REQ":
if len(args) == 0 { if len(args) == 0 {
return ircError{&irc.Message{ return ircError{&irc.Message{
Command: err_invalidcapcmd, Command: err_invalidcapcmd,
Params: []string{replyTo, cmd, "Missing argument in CAP REQ command"}, Params: []string{dc.nick, cmd, "Missing argument in CAP REQ command"},
}} }}
} }
@ -936,7 +934,7 @@ func (dc *downstreamConn) handleCapCommand(cmd string, args []string) error {
dc.SendMessage(&irc.Message{ dc.SendMessage(&irc.Message{
Prefix: dc.srv.prefix(), Prefix: dc.srv.prefix(),
Command: "CAP", Command: "CAP",
Params: []string{replyTo, reply, args[0]}, Params: []string{dc.nick, reply, args[0]},
}) })
if !dc.registered { if !dc.registered {
@ -947,7 +945,7 @@ func (dc *downstreamConn) handleCapCommand(cmd string, args []string) error {
default: default:
return ircError{&irc.Message{ return ircError{&irc.Message{
Command: err_invalidcapcmd, Command: err_invalidcapcmd,
Params: []string{replyTo, cmd, "Unknown CAP command"}, Params: []string{dc.nick, cmd, "Unknown CAP command"},
}} }}
} }
return nil return nil
@ -962,11 +960,6 @@ func (dc *downstreamConn) setSupportedCap(name, value string) {
return return
} }
replyTo := dc.nick
if !dc.registered {
replyTo = "*"
}
cap := name cap := name
if value != "" && dc.capVersion >= 302 { if value != "" && dc.capVersion >= 302 {
cap = name + "=" + value cap = name + "=" + value
@ -975,7 +968,7 @@ func (dc *downstreamConn) setSupportedCap(name, value string) {
dc.SendMessage(&irc.Message{ dc.SendMessage(&irc.Message{
Prefix: dc.srv.prefix(), Prefix: dc.srv.prefix(),
Command: "CAP", Command: "CAP",
Params: []string{replyTo, "NEW", cap}, Params: []string{dc.nick, "NEW", cap},
}) })
} }
@ -988,15 +981,10 @@ func (dc *downstreamConn) unsetSupportedCap(name string) {
return return
} }
replyTo := dc.nick
if !dc.registered {
replyTo = "*"
}
dc.SendMessage(&irc.Message{ dc.SendMessage(&irc.Message{
Prefix: dc.srv.prefix(), Prefix: dc.srv.prefix(),
Command: "CAP", Command: "CAP",
Params: []string{replyTo, "DEL", name}, Params: []string{dc.nick, "DEL", name},
}) })
} }