Skip to content

Commit

Permalink
use SASL utilities from irc-go
Browse files Browse the repository at this point in the history
  • Loading branch information
slingamn committed Feb 7, 2024
1 parent 43b9c9a commit 9c71a96
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 58 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ require (
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
github.com/ergochat/confusables v0.0.0-20201108231250-4ab98ab61fb1
github.com/ergochat/go-ident v0.0.0-20230911071154-8c30606d6881
github.com/ergochat/irc-go v0.4.0
github.com/ergochat/irc-go v0.5.0-rc1
github.com/go-sql-driver/mysql v1.7.0
github.com/go-test/deep v1.0.6 // indirect
github.com/gofrs/flock v0.8.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ github.com/ergochat/go-ident v0.0.0-20230911071154-8c30606d6881 h1:+J5m88nvybxB5
github.com/ergochat/go-ident v0.0.0-20230911071154-8c30606d6881/go.mod h1:ASYJtQujNitna6cVHsNQTGrfWvMPJ5Sa2lZlmsH65uM=
github.com/ergochat/irc-go v0.4.0 h1:0YibCKfAAtwxQdNjLQd9xpIEPisLcJ45f8FNsMHAuZc=
github.com/ergochat/irc-go v0.4.0/go.mod h1:2vi7KNpIPWnReB5hmLpl92eMywQvuIeIIGdt/FQCph0=
github.com/ergochat/irc-go v0.5.0-rc1 h1:kFoIHExoNFQ2CV+iShAVna/H4xrXQB4t4jK5Sep2j9k=
github.com/ergochat/irc-go v0.5.0-rc1/go.mod h1:2vi7KNpIPWnReB5hmLpl92eMywQvuIeIIGdt/FQCph0=
github.com/ergochat/scram v1.0.2-ergo1 h1:2bYXiRFQH636pT0msOG39fmEYl4Eq+OuutcyDsCix/g=
github.com/ergochat/scram v1.0.2-ergo1/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
github.com/ergochat/websocket v1.4.2-oragono1 h1:plMUunFBM6UoSCIYCKKclTdy/TkkHfUslhOfJQzfueM=
Expand Down
13 changes: 11 additions & 2 deletions irc/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/ergochat/irc-go/ircfmt"
"github.com/ergochat/irc-go/ircmsg"
"github.com/ergochat/irc-go/ircreader"
"github.com/ergochat/irc-go/ircutils"
"github.com/xdg-go/scram"

"github.com/ergochat/ergo/irc/caps"
Expand Down Expand Up @@ -120,13 +121,20 @@ type Client struct {

type saslStatus struct {
mechanism string
value strings.Builder
value ircutils.SASLBuffer
scramConv *scram.ServerConversation
oauthConv *oauth2.OAuthBearerServer
}

func (s *saslStatus) Initialize() {
s.value.Initialize(saslMaxResponseLength)
}

func (s *saslStatus) Clear() {
*s = saslStatus{}
s.mechanism = ""
s.value.Clear()
s.scramConv = nil
s.oauthConv = nil
}

// what stage the client is at w.r.t. the PASS command:
Expand Down Expand Up @@ -364,6 +372,7 @@ func (server *Server) RunClient(conn IRCConn) {
isTor: wConn.Tor,
hideSTS: wConn.Tor || wConn.HideSTS,
}
session.sasl.Initialize()
client.sessions = []*Session{session}

session.resetFakelag()
Expand Down
74 changes: 22 additions & 52 deletions irc/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ package irc

import (
"bytes"
"encoding/base64"
"fmt"
"net"
"os"
Expand Down Expand Up @@ -180,7 +179,6 @@ func acceptHandler(server *Server, client *Client, msg ircmsg.Message, rb *Respo
}

const (
saslMaxArgLength = 400 // required by SASL spec
saslMaxResponseLength = 8192 // implementation-defined sanity check, long enough for bearer tokens
)

Expand All @@ -207,7 +205,7 @@ func authenticateHandler(server *Server, client *Client, msg ircmsg.Message, rb
return false
}

// start new sasl session
// start new sasl session: parameter is the authentication mechanism
if session.sasl.mechanism == "" {
throttled, remainingTime := client.loginThrottle.Touch()
if throttled {
Expand Down Expand Up @@ -246,44 +244,28 @@ func authenticateHandler(server *Server, client *Client, msg ircmsg.Message, rb
return false
}

// continue existing sasl session
rawData := msg.Params[0]

// https://ircv3.net/specs/extensions/sasl-3.1:
// "The response is encoded in Base64 (RFC 4648), then split to 400-byte chunks,
// and each chunk is sent as a separate AUTHENTICATE command."
if len(rawData) > saslMaxArgLength {
rb.Add(nil, server.name, ERR_SASLTOOLONG, details.nick, client.t("SASL message too long"))
session.sasl.Clear()
return false
} else if len(rawData) == saslMaxArgLength {
if session.sasl.value.Len() >= saslMaxResponseLength {
rb.Add(nil, server.name, ERR_SASLFAIL, details.nick, client.t("SASL authentication failed: Passphrase too long"))
session.sasl.Clear()
return false
// continue existing sasl session: parameter is a message chunk
done, value, err := session.sasl.value.Add(msg.Params[0])
if err == nil {
if done {
// call actual handler
handler := EnabledSaslMechanisms[session.sasl.mechanism]
return handler(server, client, session, value, rb)
} else {
return false // wait for continuation line
}
session.sasl.value.WriteString(rawData)
return false
}
if rawData != "+" {
session.sasl.value.WriteString(rawData)
}

var data []byte
var err error
if session.sasl.value.Len() > 0 {
data, err = base64.StdEncoding.DecodeString(session.sasl.value.String())
session.sasl.value.Reset()
if err != nil {
rb.Add(nil, server.name, ERR_SASLFAIL, details.nick, client.t("SASL authentication failed: Invalid b64 encoding"))
session.sasl.Clear()
return false
}
// else: error handling
switch err {
case ircutils.ErrSASLTooLong:
rb.Add(nil, server.name, ERR_SASLTOOLONG, details.nick, client.t("SASL message too long"))
case ircutils.ErrSASLLimitExceeded:
rb.Add(nil, server.name, ERR_SASLFAIL, details.nick, client.t("SASL authentication failed: Passphrase too long"))
default:
rb.Add(nil, server.name, ERR_SASLFAIL, details.nick, client.t("SASL authentication failed: Invalid b64 encoding"))
}

// call actual handler
handler := EnabledSaslMechanisms[session.sasl.mechanism]
return handler(server, client, session, data, rb)
session.sasl.Clear()
return false
}

// AUTHENTICATE PLAIN
Expand Down Expand Up @@ -495,21 +477,9 @@ func authOauthBearerHandler(server *Server, client *Client, session *Session, va

// helper to b64 a sasl response and chunk it into 400-byte lines
// as per https://ircv3.net/specs/extensions/sasl-3.1
// TODO replace this with ircutils.EncodeSASLResponse
func sendSASLChallenge(server *Server, rb *ResponseBuffer, challenge []byte) {
challengeStr := base64.StdEncoding.EncodeToString(challenge)
lastLen := 0
for len(challengeStr) > 0 {
end := saslMaxArgLength
if end > len(challengeStr) {
end = len(challengeStr)
}
lastLen = end
rb.Add(nil, server.name, "AUTHENTICATE", challengeStr[:end])
challengeStr = challengeStr[end:]
}
if lastLen == saslMaxArgLength {
rb.Add(nil, server.name, "AUTHENTICATE", "+")
for _, chunk := range ircutils.EncodeSASLResponse(challenge) {
rb.Add(nil, server.name, "AUTHENTICATE", chunk)
}
}

Expand Down
105 changes: 105 additions & 0 deletions vendor/github.com/ergochat/irc-go/ircutils/sasl.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions vendor/modules.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ github.com/ergochat/confusables
# github.com/ergochat/go-ident v0.0.0-20230911071154-8c30606d6881
## explicit; go 1.18
github.com/ergochat/go-ident
# github.com/ergochat/irc-go v0.4.0
# github.com/ergochat/irc-go v0.5.0-rc1
## explicit; go 1.15
github.com/ergochat/irc-go/ircfmt
github.com/ergochat/irc-go/ircmsg
Expand All @@ -30,8 +30,6 @@ github.com/go-sql-driver/mysql
# github.com/gofrs/flock v0.8.1
## explicit
github.com/gofrs/flock
# github.com/golang-jwt/jwt v3.2.2+incompatible
## explicit
# github.com/golang-jwt/jwt/v5 v5.2.0
## explicit; go 1.18
github.com/golang-jwt/jwt/v5
Expand Down

0 comments on commit 9c71a96

Please sign in to comment.