package sasl
import (
)
type State uint8
const (
Initial State = iota
AuthTextSent
ResponseSent
ValidServerResponse
StepMask = 0x3
)
const (
RemoteCB State = 1 << (iota + 3)
Errored
Receiving
)
func ( Mechanism, ...Option) *Negotiator {
:= &Negotiator{
mechanism: ,
}
getOpts(, ...)
for , := range .remoteMechanisms {
:= .Name
if == && strings.HasSuffix(, "-PLUS") {
.state |= RemoteCB
break
}
}
if len(.nonce) == 0 {
.nonce = nonce(noncerandlen, rand.Reader)
}
return
}
func ( Mechanism, func(*Negotiator) bool, ...Option) *Negotiator {
:= &Negotiator{
mechanism: ,
state: AuthTextSent | Receiving,
}
getOpts(, ...)
if != nil {
.permissions =
}
for , := range .remoteMechanisms {
:= .Name
if == && strings.HasSuffix(, "-PLUS") {
.state |= RemoteCB
break
}
}
if len(.nonce) == 0 {
.nonce = nonce(noncerandlen, rand.Reader)
}
return
}
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 ( *Negotiator) () []byte {
return .nonce
}
func ( *Negotiator) ( []byte) ( bool, []byte, error) {
if .state&Errored == Errored {
panic("sasl: Step called on a SASL state machine that has errored")
}
defer func() {
if != nil {
.state |= Errored
}
}()
switch .state & StepMask {
case Initial:
, , .cache, = .mechanism.Start()
.state = .state&^StepMask | AuthTextSent
case AuthTextSent:
, , .cache, = .mechanism.Next(, , .cache)
.state = .state&^StepMask | ResponseSent
case ResponseSent:
, , .cache, = .mechanism.Next(, , .cache)
.state = .state&^StepMask | ValidServerResponse
case ValidServerResponse:
, , .cache, = .mechanism.Next(, , .cache)
}
if != nil {
return false, nil,
}
return , ,
}
func ( *Negotiator) () State {
return .state
}
func ( *Negotiator) () {
.state = .state & (Receiving | RemoteCB)
if .state&Receiving == Receiving {
.state = .state&^StepMask | AuthTextSent
}
.nonce = nonce(noncerandlen, rand.Reader)
.cache = nil
}
func ( *Negotiator) () (, , []byte) {
if .credentials != nil {
return .credentials()
}
return
}
func ( *Negotiator) ( ...Option) bool {
if .permissions != nil {
:= *
getOpts(&, ...)
return .permissions(&)
}
return false
}
func ( *Negotiator) () *tls.ConnectionState {
if .tlsState != nil {
return .tlsState
}
return nil
}
func ( *Negotiator) () []string {
if .remoteMechanisms != nil {
return .remoteMechanisms
}
return nil
}