3
0
mirror of https://github.com/ergochat/ergo.git synced 2025-02-19 23:20:40 +01:00

accounts: Add initial SASL handler, still need to write mechanism handlers

This commit is contained in:
Daniel Oaks 2016-09-06 16:31:59 +10:00
parent 31333da632
commit 1679bc9ac2
4 changed files with 120 additions and 4 deletions

View File

@ -3,9 +3,23 @@
package irc package irc
import "time" import (
"encoding/base64"
"strings"
"time"
"github.com/DanielOaks/girc-go/ircmsg"
)
var ( var (
// EnabledSaslMechanisms contains the SASL mechanisms that exist and that we support.
// This can be moved to some other data structure/place if we need to load/unload mechs later.
EnabledSaslMechanisms = map[string]func(*Server, *Client, string, []byte) bool{
"PLAIN": authPlainHandler,
"EXTERNAL": authExternalHandler,
}
// NoAccount is a placeholder which means that the user is not logged into an account.
NoAccount = ClientAccount{ NoAccount = ClientAccount{
Name: "*", // * is used until actual account name is set Name: "*", // * is used until actual account name is set
} }
@ -20,3 +34,96 @@ type ClientAccount struct {
// Clients that are currently logged into this account (useful for notifications). // Clients that are currently logged into this account (useful for notifications).
Clients []*Client Clients []*Client
} }
// authenticateHandler parses the AUTHENTICATE command (for SASL authentication).
func authenticateHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
// sasl abort
if len(msg.Params) == 1 && msg.Params[0] == "*" {
if client.saslInProgress {
client.Send(nil, server.nameString, ERR_SASLABORTED, client.nickString, "SASL authentication aborted")
} else {
client.Send(nil, server.nameString, ERR_SASLFAIL, client.nickString, "SASL authentication failed")
}
client.saslInProgress = false
client.saslMechanism = ""
client.saslValue = ""
return false
}
// start new sasl session
if !client.saslInProgress {
mechanism := strings.ToUpper(msg.Params[0])
_, mechanismIsEnabled := EnabledSaslMechanisms[mechanism]
if mechanismIsEnabled {
client.saslInProgress = true
client.saslMechanism = mechanism
client.Send(nil, server.nameString, "AUTHENTICATE", "+")
} else {
client.Send(nil, server.nameString, ERR_SASLFAIL, client.nickString, "SASL authentication failed")
}
return false
}
// continue existing sasl session
rawData := msg.Params[0]
if len(rawData) > 400 {
client.Send(nil, server.nameString, ERR_SASLTOOLONG, client.nickString, "SASL message too long")
client.saslInProgress = false
client.saslMechanism = ""
client.saslValue = ""
return false
} else if len(rawData) == 400 {
client.saslValue += rawData
// allow 4 'continuation' lines before rejecting for length
if len(client.saslValue) > 400*4 {
client.Send(nil, server.nameString, ERR_SASLFAIL, client.nickString, "SASL authentication failed: Passphrase too long")
client.saslInProgress = false
client.saslMechanism = ""
client.saslValue = ""
return false
}
return false
} else if len(client.saslValue) > 0 {
client.saslValue += rawData
return false
}
client.saslValue += rawData
data, err := base64.StdEncoding.DecodeString(client.saslValue)
if err != nil {
client.Send(nil, server.nameString, ERR_SASLFAIL, client.nickString, "SASL authentication failed: Invalid b64 encoding")
client.saslInProgress = false
client.saslMechanism = ""
client.saslValue = ""
return false
}
// call actual handler
handler, handlerExists := EnabledSaslMechanisms[client.saslMechanism]
// like 100% not required, but it's good to be safe I guess
if !handlerExists {
client.Send(nil, server.nameString, ERR_SASLFAIL, client.nickString, "SASL authentication failed")
client.saslInProgress = false
client.saslMechanism = ""
client.saslValue = ""
return false
}
return handler(server, client, client.saslMechanism, data)
}
// authPlainHandler parses the SASL PLAIN mechanism.
func authPlainHandler(server *Server, client *Client, mechanism string, value []byte) bool {
client.Send(nil, server.nameString, ERR_SASLFAIL, client.nickString, "SASL authentication failed: Mechanism not yet implemented")
return false
}
// authExternalHandler parses the SASL EXTERNAL mechanism.
func authExternalHandler(server *Server, client *Client, mechanism string, value []byte) bool {
client.Send(nil, server.nameString, ERR_SASLFAIL, client.nickString, "SASL authentication failed: Mechanism not yet implemented")
return false
}

View File

@ -25,6 +25,7 @@ var (
SupportedCapabilities = CapabilitySet{ SupportedCapabilities = CapabilitySet{
ExtendedJoin: true, ExtendedJoin: true,
MultiPrefix: true, MultiPrefix: true,
SASL: true,
ServerTime: true, ServerTime: true,
UserhostInNames: true, UserhostInNames: true,
} }

View File

@ -27,14 +27,17 @@ var (
) )
type Client struct { type Client struct {
account *ClientAccount
atime time.Time atime time.Time
authorized bool authorized bool
awayMessage string awayMessage string
capabilities CapabilitySet capabilities CapabilitySet
capState CapState capState CapState
certfp string
channels ChannelSet channels ChannelSet
ctime time.Time ctime time.Time
flags map[UserMode]bool flags map[UserMode]bool
isDestroyed bool
isQuitting bool isQuitting bool
hasQuit bool hasQuit bool
hops uint hops uint
@ -45,13 +48,13 @@ type Client struct {
nickMaskString string // cache for nickmask string since it's used with lots of replies nickMaskString string // cache for nickmask string since it's used with lots of replies
quitTimer *time.Timer quitTimer *time.Timer
realname string realname string
account *ClientAccount
registered bool registered bool
saslInProgress bool
saslMechanism string
saslValue string
server *Server server *Server
socket *Socket socket *Socket
username Name username Name
isDestroyed bool
certfp string
} }
func NewClient(server *Server, conn net.Conn, isTLS bool) *Client { func NewClient(server *Server, conn net.Conn, isTLS bool) *Client {

View File

@ -49,6 +49,11 @@ func (cmd *Command) Run(server *Server, client *Client, msg ircmsg.IrcMessage) b
// Commands holds all commands executable by a client connected to us. // Commands holds all commands executable by a client connected to us.
var Commands = map[string]Command{ var Commands = map[string]Command{
"AUTHENTICATE": {
handler: authenticateHandler,
usablePreReg: true,
minParams: 1,
},
"AWAY": { "AWAY": {
handler: awayHandler, handler: awayHandler,
minParams: 0, minParams: 0,