downstream: add support for ANONYMOUS SASL auth

This clears any saved SASL credentials.

Closes: https://todo.sr.ht/~emersion/soju/198
This commit is contained in:
Simon Ser 2023-04-05 21:15:18 +02:00
parent 92796248d2
commit b3be05559b
1 changed files with 44 additions and 20 deletions

View File

@ -685,7 +685,7 @@ func (dc *downstreamConn) handleMessageUnregistered(ctx context.Context, msg *ir
break
}
default:
panic(fmt.Errorf("unexpected SASL mechanism %q", credentials.mechanism))
err = fmt.Errorf("unsupported SASL mechanism")
}
if err != nil {
@ -929,6 +929,10 @@ func (dc *downstreamConn) handleAuthenticate(msg *irc.Message) (result *downstre
dc.sasl.oauthBearer = &options
return nil
}))
case "ANONYMOUS":
server = sasl.NewAnonymousServer(func(trace string) error {
return nil
})
default:
return nil, ircError{&irc.Message{
Prefix: dc.srv.prefix(),
@ -1065,7 +1069,7 @@ func (dc *downstreamConn) updateSupportedCaps() {
}
if uc := dc.upstream(); uc != nil && uc.supportsSASL("PLAIN") {
dc.setSupportedCap("sasl", "PLAIN")
dc.setSupportedCap("sasl", "PLAIN,ANONYMOUS")
} else if dc.network != nil {
dc.unsetSupportedCap("sasl")
}
@ -2495,33 +2499,53 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
credentials, err := dc.handleAuthenticate(msg)
if err != nil {
return err
} else if credentials == nil {
break
}
if credentials != nil {
if credentials.mechanism != "PLAIN" {
dc.endSASL(&irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_SASLFAIL,
Params: []string{dc.nick, "Unsupported SASL authentication mechanism"},
})
return nil
}
if uc.saslClient != nil {
dc.endSASL(&irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_SASLFAIL,
Params: []string{dc.nick, "Another authentication attempt is already in progress"},
})
return nil
}
if uc.saslClient != nil {
dc.endSASL(&irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_SASLFAIL,
Params: []string{dc.nick, "Another authentication attempt is already in progress"},
})
break
}
switch credentials.mechanism {
case "PLAIN":
uc.logger.Printf("starting post-registration SASL PLAIN authentication with username %q", credentials.plain.Username)
uc.saslClient = sasl.NewPlainClient("", credentials.plain.Username, credentials.plain.Password)
uc.enqueueCommand(dc, &irc.Message{
Command: "AUTHENTICATE",
Params: []string{"PLAIN"},
})
case "ANONYMOUS":
if uc.network.SASL.Mechanism != "" {
record := uc.network.Network // copy network record because we'll mutate it
record.SASL.Plain.Username = ""
record.SASL.Plain.Password = ""
record.SASL.External.CertBlob = nil
record.SASL.External.PrivKeyBlob = nil
record.SASL.Mechanism = ""
_, err := dc.user.updateNetwork(ctx, &record)
if err != nil {
dc.logger.Printf("failed to clear SASL credentials")
dc.endSASL(&irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_SASLFAIL,
Params: []string{dc.nick, "Internal server error"},
})
break
}
}
dc.endSASL(nil)
default:
dc.endSASL(&irc.Message{
Prefix: dc.srv.prefix(),
Command: irc.ERR_SASLFAIL,
Params: []string{dc.nick, "Unsupported SASL authentication mechanism"},
})
}
case "REGISTER", "VERIFY":
// Check number of params here, since we'll use that to save the