accounts: Add SASL PLAIN handler

This commit is contained in:
Daniel Oaks 2016-09-07 20:46:01 +10:00
parent 1679bc9ac2
commit 70665850aa
2 changed files with 83 additions and 2 deletions

View File

@ -4,11 +4,17 @@
package irc package irc
import ( import (
"bytes"
"encoding/base64" "encoding/base64"
"encoding/json"
"errors"
"fmt"
"strconv"
"strings" "strings"
"time" "time"
"github.com/DanielOaks/girc-go/ircmsg" "github.com/DanielOaks/girc-go/ircmsg"
"github.com/tidwall/buntdb"
) )
var ( var (
@ -23,6 +29,9 @@ var (
NoAccount = ClientAccount{ NoAccount = ClientAccount{
Name: "*", // * is used until actual account name is set Name: "*", // * is used until actual account name is set
} }
// generic sasl fail error
errSaslFail = errors.New("SASL failed")
) )
// ClientAccount represents a user account. // ClientAccount represents a user account.
@ -113,12 +122,84 @@ func authenticateHandler(server *Server, client *Client, msg ircmsg.IrcMessage)
return false return false
} }
// sasl is being done now by the handler, so we empty the client's vars now
client.saslInProgress = false
client.saslMechanism = ""
client.saslValue = ""
return handler(server, client, client.saslMechanism, data) return handler(server, client, client.saslMechanism, data)
} }
// authPlainHandler parses the SASL PLAIN mechanism. // authPlainHandler parses the SASL PLAIN mechanism.
func authPlainHandler(server *Server, client *Client, mechanism string, value []byte) bool { 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") splitValue := bytes.Split(value, []byte{'\000'})
if len(splitValue) != 3 {
client.Send(nil, server.nameString, ERR_SASLFAIL, client.nickString, "SASL authentication failed: Invalid auth blob")
return false
}
accountString := string(splitValue[0])
authzid := string(splitValue[1])
if accountString != authzid {
client.Send(nil, server.nameString, ERR_SASLFAIL, client.nickString, "SASL authentication failed: authcid and authzid should be the same")
return false
}
// casefolding, rough for now bit will improve later.
// keep it the same as in the REG CREATE stage
accountString = NewName(accountString).String()
// load and check acct data all in one update to prevent races.
// as noted elsewhere, change to proper locking for Account type later probably
err := server.store.Update(func(tx *buntdb.Tx) error {
credText, err := tx.Get(fmt.Sprintf(keyAccountCredentials, accountString))
if err != nil {
return err
}
var creds AccountCredentials
err = json.Unmarshal([]byte(credText), &creds)
if err != nil {
return err
}
// ensure creds are valid
password := string(splitValue[2])
if len(creds.PassphraseHash) < 1 || len(creds.PassphraseSalt) < 1 || len(password) < 1 {
return errSaslFail
}
err = server.passwords.CompareHashAndPassword(creds.PassphraseHash, creds.PassphraseSalt, password)
// succeeded, load account info if necessary
account, exists := server.accounts[accountString]
if !exists {
name, _ := tx.Get(fmt.Sprintf(keyAccountName, accountString))
regTime, _ := tx.Get(fmt.Sprintf(keyAccountRegTime, accountString))
regTimeInt, _ := strconv.ParseInt(regTime, 10, 64)
accountInfo := ClientAccount{
Name: name,
RegisteredAt: time.Unix(regTimeInt, 0),
Clients: []*Client{},
}
server.accounts[accountString] = &accountInfo
account = &accountInfo
}
account.Clients = append(account.Clients, client)
client.account = account
return err
})
if err != nil {
client.Send(nil, server.nameString, ERR_SASLFAIL, client.nickString, "SASL authentication failed")
return false
}
client.Send(nil, server.nameString, RPL_LOGGEDIN, client.nickString, client.nickMaskString, client.account.Name, fmt.Sprintf("You are now logged in as %s", client.account.Name))
client.Send(nil, server.nameString, RPL_SASLSUCCESS, client.nickString, "SASL authentication successful")
return false return false
} }

View File

@ -216,7 +216,7 @@ func regCreateHandler(server *Server, client *Client, msg ircmsg.IrcMessage) boo
if err != nil { if err != nil {
return fmt.Errorf("Could not marshal creds: %s", err) return fmt.Errorf("Could not marshal creds: %s", err)
} }
tx.Set(keyAccountCredentials, string(credText), nil) tx.Set(fmt.Sprintf(keyAccountCredentials, account), string(credText), nil)
return nil return nil
}) })