-
Notifications
You must be signed in to change notification settings - Fork 0
/
plain.go
75 lines (70 loc) · 2.3 KB
/
plain.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
package sasler
import (
"bytes"
"github.com/xdg-go/stringprep"
)
// PlainClient returns a ClientMech implementation for the PLAIN mechanism, as
// specified in [RFC 4616].
//
// [RFC 4616]: https://tools.ietf.org/html/rfc4616.
func PlainClient(authz, authn string, passwd []byte) ClientMech {
ir := make([]byte, len(authz)+len(authn)+len(passwd)+2)
copy(ir, []byte(authz))
copy(ir[len(authz)+1:], []byte(authn))
copy(ir[len(authz)+len(authn)+2:], passwd)
return &singleMessageClient{name: "PLAIN", ir: ir}
}
// PlainAuthenticator is supplied to [PlainServer] to implement password
// verification, authz derivation and authorization checking.
type PlainAuthenticator interface {
// VerifyPasswd verifies whether the supplied combination of authn and passwd
// is valid. Return false to fail authentication.
VerifyPasswd(authn string, passwd []byte) bool
// DerivceAuthz derives an authz from an authn. It is only called when no
// authz has been requested by the client. Return the empty string if no
// authz can be derived from the supplied authn.
DeriveAuthz(authn string) string
// Authorize verifies whether an authn is authorized to use the requested or
// derived authz. Return false to fail authorization.
Authorize(authz, authn string) bool
}
// PlainServer returns a ServerMech implementation for the PLAIN mechanism, as
// specified in [RFC 4616].
//
// [RFC 4616]: https://tools.ietf.org/html/rfc4616.
func PlainServer(auth PlainAuthenticator) ServerMech {
cb := func(ir []byte) (string, error) {
delim := bytes.IndexByte(ir, 0)
if delim == -1 {
return "", ErrInvalidMessage
}
authz := string(ir[:delim])
ir = ir[delim+1:]
delim = bytes.IndexByte(ir, 0)
if delim == -1 {
return "", ErrInvalidMessage
}
authn, err := stringprep.SASLprep.Prepare(string(ir[:delim]))
if err != nil {
return "", ErrInvalidMessage
}
passwd, err := stringprep.SASLprep.Prepare(string(ir[delim+1:]))
if err != nil {
return "", ErrInvalidMessage
}
if !auth.VerifyPasswd(authn, []byte(passwd)) {
return "", ErrAuthenticationFailed
}
if authz == "" {
authz = auth.DeriveAuthz(authn)
if authz == "" {
return "", ErrAuthenticationFailed
}
}
if !auth.Authorize(authz, authn) {
return "", ErrUnauthorized
}
return authz, nil
}
return &singleMessageServer{name: "PLAIN", cb: cb}
}