Involved Source Files
Package sasl implements the Simple Authentication and Security Layer (SASL)
as defined by RFC 4422.
Most users of this package will only need to create a Negotiator using
NewClient or NewServer and call its Step method repeatedly.
Authors implementing SASL mechanisms other than the builtin ones will want to
create a Mechanism struct which will likely use the other methods on the
Negotiator.
Be advised: This API is still unstable and is subject to change.
mechanism.gonegotiator.gononce.gooptions.goplain.goscram.goxor.goxor_amd64.goxor_amd64.s
Code Examples
package main
import (
"fmt"
"mellium.im/sasl"
)
func main() {
const (
username = "miranda"
password = "pencil"
)
creds := sasl.Credentials(func() ([]byte, []byte, []byte) {
return []byte(username), []byte(password), []byte{}
})
server := sasl.NewServer(sasl.Plain, func(n *sasl.Negotiator) bool {
user, pass, ident := n.Credentials()
// In a real auth system you might want to consider a constant time
// comparison and this would probably involve hashing and a database lookup.
if len(ident) == 0 && string(user) == username && string(pass) == password {
fmt.Println("auth success!")
return true
}
fmt.Println("auth failed!")
return false
}, creds)
client := sasl.NewClient(sasl.Plain, sasl.Credentials(func() ([]byte, []byte, []byte) {
// In a real auth system this would probably be user input.
return []byte(username), []byte("password!"), []byte{}
}))
_, resp, err := client.Step(nil)
if err != nil {
fmt.Println(err)
return
}
// Normally the response would come from the network, not from a client on the
// same machine.
_, _, err = server.Step(resp)
if err != sasl.ErrAuthn {
fmt.Println(err)
return
}
}
package main
import (
"fmt"
"mellium.im/sasl"
)
func main() {
const (
username = "miranda"
password = "pencil"
)
creds := sasl.Credentials(func() ([]byte, []byte, []byte) {
// In a real auth system this would probably be user input.
return []byte(username), []byte(password), []byte{}
})
server := sasl.NewServer(sasl.Plain, func(n *sasl.Negotiator) bool {
user, pass, ident := n.Credentials()
// In a real auth system you might want to consider a constant time
// comparison and this would probably involve hashing and a database lookup.
if len(ident) == 0 && string(user) == username && string(pass) == password {
fmt.Println("auth success!")
return true
}
fmt.Println("auth failed!")
return false
}, creds)
client := sasl.NewClient(sasl.Plain, creds)
_, resp, err := client.Step(nil)
if err != nil {
fmt.Println(err)
return
}
// Normally the response would come from the network, not from a client on the
// same machine.
_, _, err = server.Step(resp)
if err != nil {
fmt.Println(err)
return
}
}
package main
import (
"bytes"
"fmt"
"mellium.im/sasl"
)
// A custom SASL Mechanism that implements XOAUTH2:
// https://developers.google.com/gmail/xoauth2_protocol
var xoauth2 = sasl.Mechanism{
Name: "XOAUTH2",
Start: func(m *sasl.Negotiator) (bool, []byte, interface{}, error) {
// Start is called only by clients and returns the client first message.
username, password, _ := m.Credentials()
payload := []byte(`user=`)
payload = append(payload, username...)
payload = append(payload, '\x01')
payload = append(payload, []byte(`auth=Bearer `)...)
payload = append(payload, password...)
payload = append(payload, '\x01', '\x01')
return false, payload, nil, nil
},
Next: func(m *sasl.Negotiator, challenge []byte, _ interface{}) (bool, []byte, interface{}, error) {
// Next is called by both clients and servers and must be able to generate
// and handle every challenge except for the client first message which is
// generated (but not handled by) by Start.
state := m.State()
// If we're a client or a server that's past the AuthTextSent step, we
// should never actually hit this step for the XOAUTH2 mechanism so return
// an error.
if state&sasl.Receiving != sasl.Receiving || state&sasl.StepMask != sasl.AuthTextSent {
return false, nil, nil, sasl.ErrTooManySteps
}
parts := bytes.Split(challenge, []byte{1})
if len(parts) != 3 {
return false, nil, nil, sasl.ErrInvalidChallenge
}
user := bytes.TrimPrefix([]byte("user="), parts[0])
if len(user) == len(parts[0]) {
return false, nil, nil, sasl.ErrInvalidChallenge
}
pass := bytes.TrimPrefix([]byte("Auth=Bearer "), parts[1])
if len(pass) == len(parts[1]) {
return false, nil, nil, sasl.ErrInvalidChallenge
}
if len(parts[2]) > 0 {
return false, nil, nil, sasl.ErrInvalidChallenge
}
if m.Permissions(sasl.Credentials(func() ([]byte, []byte, []byte) {
return user, pass, nil
})) {
return false, nil, nil, nil
}
return false, nil, nil, sasl.ErrAuthn
},
}
func main() {
c := sasl.NewClient(
xoauth2,
sasl.Credentials(func() ([]byte, []byte, []byte) {
return []byte("someuser@example.com"), []byte("vF9dft4qmTc2Nvb3RlckBhdHRhdmlzdGEuY29tCg=="), []byte{}
}),
)
// This is the first step and we haven't received any challenge from the
// server yet.
more, resp, _ := c.Step(nil)
fmt.Printf("%v %s", more, bytes.Replace(resp, []byte{1}, []byte{' '}, -1))
}
Package-Level Type Names (total 4, all are exported)
/* sort exporteds by: | */
Mechanism represents a SASL mechanism that can be used by a Client or Server
to perform the actual negotiation. Base64 encoding the final challenges and
responses should not be performed by the mechanism.
Mechanisms must be stateless and may be shared between goroutines. When a
mechanism needs to store state between the different steps it can return
anything that it needs to store and the value will be cached by the
negotiator and passed in as the data parameter when the next challenge is
received.
NamestringNextfunc(n *Negotiator, challenge []byte, data interface{}) (more bool, resp []byte, cache interface{}, err error)Startfunc(n *Negotiator) (more bool, resp []byte, cache interface{}, err error)
func scram(name string, fn func() hash.Hash) Mechanism
func NewClient(m Mechanism, opts ...Option) *Negotiator
func NewServer(m Mechanism, permissions func(*Negotiator) bool, opts ...Option) *Negotiator
var Plain
var ScramSha1
var ScramSha1Plus
var ScramSha256
var ScramSha256Plus
var plain
A Negotiator represents a SASL client or server state machine that can
attempt to negotiate auth. Negotiators should not be used from multiple
goroutines, and must be reset between negotiation attempts.
cacheinterface{}credentialsfunc() (Username, Password, Identity []byte)mechanismMechanismnonce[]bytepermissionsfunc(*Negotiator) boolremoteMechanisms[]stringstateStatetlsState*tls.ConnectionState
Credentials returns a username, and password for authentication and optional
identity for authorization.
Nonce returns a unique nonce that is reset for each negotiation attempt. It
is used by SASL Mechanisms and should generally not be called directly.
Permissions is the callback used by the server to authenticate the user.
RemoteMechanisms is a list of mechanisms as advertised by the other side of a
SASL negotiation.
Reset resets the state machine to its initial state so that it can be reused
in another SASL exchange.
State returns the internal state of the SASL state machine.
Step attempts to transition the state machine to its next state. If Step is
called after a previous invocation generates an error (and the state machine
has not been reset to its initial state), Step panics.
TLSState is the state of any TLS connections being used to negotiate SASL
(it can be used for channel binding).
func NewClient(m Mechanism, opts ...Option) *Negotiator
func NewServer(m Mechanism, permissions func(*Negotiator) bool, opts ...Option) *Negotiator
func getGS2Header(name string, n *Negotiator) (gs2Header []byte)
func getOpts(n *Negotiator, o ...Option)
func scramClientNext(name string, fn func() hash.Hash, m *Negotiator, challenge []byte, data interface{}) (more bool, resp []byte, cache interface{}, err error)
func github.com/go-pg/pg/v10.readAuthSASLFinal(rd *pool.ReaderContext, client *Negotiator) error
State represents the current state of a Negotiator.
The first two bits represent the actual state of the state machine and the
last 3 bits are a bitmask that define the machines behavior.
The remaining bits should not be used.
func (*Negotiator).State() State
const AuthTextSent
const Errored
const Initial
const Receiving
const RemoteCB
const ResponseSent
const ValidServerResponse
Package-Level Functions (total 14, in which 5 are exported)
Credentials provides the negotiator with a username and password to
authenticate with and (optionally) an authorization identity.
Identity will normally be left empty to act as the username.
The Credentials function is called lazily and may be called multiple times by
the mechanism.
It is not memoized by the negotiator.
NewClient creates a new SASL Negotiator that supports creating authentication
requests using the given mechanism.
NewServer creates a new SASL Negotiator that supports receiving
authentication requests using the given mechanism.
A nil permissions function is the same as a function that always returns
false.
RemoteMechanisms sets a list of mechanisms supported by the remote client or
server with which the state machine will be negotiating.
It is used to determine if the server supports channel binding.
TLSState lets the state machine negotiate channel binding with a TLS session
if supported by the underlying mechanism.
Package-Level Variables (total 13, in which 9 are exported)
Define common errors used by SASL mechanisms and negotiators.
Define common errors used by SASL mechanisms and negotiators.
Define common errors used by SASL mechanisms and negotiators.
Define common errors used by SASL mechanisms and negotiators.
Plain is a Mechanism that implements the PLAIN authentication mechanism
as defined by RFC 4616.
ScramSha1 is a Mechanism that implements the SCRAM-SHA-1 authentication
mechanism defined in RFC 5802.
ScramSha1Plus is a Mechanism that implements the SCRAM-SHA-1-PLUS
authentication mechanism defined in RFC 5802.
The only supported channel binding types are tls-unique as defined in RFC
5929 and tls-exporter defined in RFC 9266.
ScramSha256 is a Mechanism that implements the SCRAM-SHA-256
authentication mechanism defined in RFC 7677.
ScramSha256Plus is a Mechanism that implements the SCRAM-SHA-256-PLUS
authentication mechanism defined in RFC 7677.
The only supported channel binding types are tls-unique as defined in RFC
5929 and tls-exporter defined in RFC 9266.