-
Notifications
You must be signed in to change notification settings - Fork 1
/
sasl.go
142 lines (120 loc) · 3.48 KB
/
sasl.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// Package sasl implements the framework for RFC4422(https://tools.ietf.org/html/rfc4422).
// It provides high-level methods to conduct an authentication exchange as both a server
// and as a client. Mechanism implementations are defined in other packages.
package sasl
import (
"context"
"fmt"
)
// ClientMech handles authenticating with a server.
type ClientMech interface {
// Start initializes the mechanism and begins the authentication exchange.
Start(context.Context) (string, []byte, error)
// Next continues the exchange.
Next(context.Context, []byte) ([]byte, error)
// Completed indicates if the authentication exchange is complete from
// the client's perspective.
Completed() bool
}
// ServerMech handles authenticating with a client.
type ServerMech interface {
// Start initializes the mechanism and begins the authentication exchange.
Start(context.Context, []byte) (string, []byte, error)
// Next continues the exchange.
Next(context.Context, []byte) ([]byte, error)
// Completed indicates if the authentication exchange is complete from
// the server's perspective.
Completed() bool
}
// ConverseAsClient conducts an authentication exchange as a client.
func ConverseAsClient(ctx context.Context, mech ClientMech, incoming <-chan []byte, outgoing chan<- []byte) error {
mechName, response, err := mech.Start(ctx)
if err != nil {
return newError(fmt.Sprintf("sasl mechanism %s: unable to start exchange", mechName), err)
}
var challenge []byte
for {
select {
case outgoing <- response:
case <-ctx.Done():
return ctx.Err()
}
select {
case challenge = <-incoming:
case <-ctx.Done():
return ctx.Err()
}
response, err = mech.Next(ctx, challenge)
if err != nil {
if response != nil {
// some mechanisms communicate errors via the mechanism. In these cases,
// we can expect an error as well as an non-nil response.
select {
case outgoing <- response:
case <-ctx.Done():
return ctx.Err()
}
}
return newError(fmt.Sprintf("sasl mechanism %s: client failed to provide response", mechName), err)
}
if mech.Completed() {
break
}
}
return nil
}
// ConverseAsServer conducts an authentication exchange as a server.
func ConverseAsServer(ctx context.Context, mech ServerMech, response []byte, incoming <-chan []byte, outgoing chan<- []byte) error {
mechName, challenge, err := mech.Start(ctx, response)
if err != nil {
return newError(fmt.Sprintf("sasl mechanism %s: unable to start exchange", mechName), err)
}
for {
select {
case outgoing <- challenge:
case <-ctx.Done():
return ctx.Err()
}
if mech.Completed() {
break
}
select {
case response = <-incoming:
case <-ctx.Done():
return ctx.Err()
}
challenge, err = mech.Next(ctx, response)
if err != nil {
if challenge != nil {
// some mechanisms communicate errors via the mechanism. In these cases,
// we can expect an error as well as an non-nil challenge.
select {
case outgoing <- challenge:
case <-ctx.Done():
return ctx.Err()
}
}
return newError(fmt.Sprintf("sasl mechanism %s: server failed to provide challenge", mechName), err)
}
}
return nil
}
func newError(msg string, inner error) *Error {
return &Error{
Msg: msg,
Inner: inner,
}
}
// Error is always the type of error returned from ConverseAsClient and
// ConverseAsServer.
type Error struct {
Msg string
Inner error
}
func (e *Error) Error() string {
s := e.Msg
if e.Inner != nil {
s += ": " + e.Inner.Error()
}
return s
}