package sasl
import (
"crypto/rand"
"crypto/tls"
"strings"
)
type State uint8
const (
Initial State = iota
AuthTextSent
ResponseSent
ValidServerResponse
StepMask = 0x3
)
const (
RemoteCB State = 1 << (iota + 3 )
Errored
Receiving
)
func NewClient (m Mechanism , opts ...Option ) *Negotiator {
machine := &Negotiator {
mechanism : m ,
nonce : nonce (noncerandlen , rand .Reader ),
}
getOpts (machine , opts ...)
for _ , rname := range machine .remoteMechanisms {
lname := m .Name
if lname == rname && strings .HasSuffix (lname , "-PLUS" ) {
machine .state |= RemoteCB
return machine
}
}
return machine
}
func NewServer (m Mechanism , permissions func (*Negotiator ) bool , opts ...Option ) *Negotiator {
machine := &Negotiator {
mechanism : m ,
nonce : nonce (noncerandlen , rand .Reader ),
state : AuthTextSent | Receiving ,
}
getOpts (machine , opts ...)
if permissions != nil {
machine .permissions = permissions
}
for _ , rname := range machine .remoteMechanisms {
lname := m .Name
if lname == rname && strings .HasSuffix (lname , "-PLUS" ) {
machine .state |= RemoteCB
return machine
}
}
return machine
}
type Negotiator struct {
tlsState *tls .ConnectionState
remoteMechanisms []string
credentials func () (Username, Password, Identity []byte )
permissions func (*Negotiator ) bool
mechanism Mechanism
state State
nonce []byte
cache interface {}
}
func (c *Negotiator ) Nonce () []byte {
return c .nonce
}
func (c *Negotiator ) Step (challenge []byte ) (more bool , resp []byte , err error ) {
if c .state &Errored == Errored {
panic ("sasl: Step called on a SASL state machine that has errored" )
}
defer func () {
if err != nil {
c .state |= Errored
}
}()
switch c .state & StepMask {
case Initial :
more , resp , c .cache , err = c .mechanism .Start (c )
c .state = c .state &^StepMask | AuthTextSent
case AuthTextSent :
more , resp , c .cache , err = c .mechanism .Next (c , challenge , c .cache )
c .state = c .state &^StepMask | ResponseSent
case ResponseSent :
more , resp , c .cache , err = c .mechanism .Next (c , challenge , c .cache )
c .state = c .state &^StepMask | ValidServerResponse
case ValidServerResponse :
more , resp , c .cache , err = c .mechanism .Next (c , challenge , c .cache )
}
if err != nil {
return false , nil , err
}
return more , resp , err
}
func (c *Negotiator ) State () State {
return c .state
}
func (c *Negotiator ) Reset () {
c .state = c .state & (Receiving | RemoteCB )
if c .state &Receiving == Receiving {
c .state = c .state &^StepMask | AuthTextSent
}
c .nonce = nonce (noncerandlen , rand .Reader )
c .cache = nil
}
func (c *Negotiator ) Credentials () (username , password , identity []byte ) {
if c .credentials != nil {
return c .credentials ()
}
return
}
func (c *Negotiator ) Permissions (opts ...Option ) bool {
if c .permissions != nil {
nn := *c
getOpts (&nn , opts ...)
return c .permissions (&nn )
}
return false
}
func (c *Negotiator ) TLSState () *tls .ConnectionState {
if c .tlsState != nil {
return c .tlsState
}
return nil
}
func (c *Negotiator ) RemoteMechanisms () []string {
if c .remoteMechanisms != nil {
return c .remoteMechanisms
}
return nil
}
The pages are generated with Golds v0.3.6 . (GOOS=darwin GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .