Mercurial > projects > managesieve
annotate auth.go @ 12:66b46b3d73be default tip
Handle capabilities sent by the server after negotiating a SASL security layer
author | Guido Berhoerster <guido+managesieve@berhoerster.name> |
---|---|
date | Tue, 09 Feb 2021 23:01:02 +0100 |
parents | 6369453d47a3 |
children |
rev | line source |
---|---|
0
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
1 // Copyright (C) 2020 Guido Berhoerster <guido+managesieve@berhoerster.name> |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
2 // |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
3 // Permission is hereby granted, free of charge, to any person obtaining |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
4 // a copy of this software and associated documentation files (the |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
5 // "Software"), to deal in the Software without restriction, including |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
6 // without limitation the rights to use, copy, modify, merge, publish, |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
7 // distribute, sublicense, and/or sell copies of the Software, and to |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
8 // permit persons to whom the Software is furnished to do so, subject to |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
9 // the following conditions: |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
10 // |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
11 // The above copyright notice and this permission notice shall be included |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
12 // in all copies or substantial portions of the Software. |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
13 // |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
15 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
17 // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
18 // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
19 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
20 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
21 |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
22 package managesieve |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
23 |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
24 import ( |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
25 "errors" |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
26 "fmt" |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
27 ) |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
28 |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
29 // this API is inspired by the SASL authentication API in net/smtp |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
30 |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
31 // ServerInfo stores information about the ManageSieve server. |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
32 type ServerInfo struct { |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
33 Name string // hostname of the server |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
34 TLS bool // whether a verified TLS connection is used |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
35 Auth []string // authentication methods advertised in capabilities |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
36 } |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
37 |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
38 // Check whether the server supports the wanted SASL authentication mechanism. |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
39 func (s *ServerInfo) HaveAuth(wanted string) bool { |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
40 for _, m := range s.Auth { |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
41 if m == wanted { |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
42 return true |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
43 } |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
44 } |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
45 return false |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
46 } |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
47 |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
48 type Auth interface { |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
49 // Initiate SASL authentication. A non-nil response will be sent in |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
50 // response to an empty challenge from the server if mandated by the |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
51 // authentication mechanism. The name of the SASL authentication |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
52 // mechanism is returned in mechanism. If an error is returned SASL |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
53 // authentication will be aborted and an AuthenticationError will be |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
54 // returned to the caller. |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
55 Start(server *ServerInfo) (mechanism string, response []byte, err error) |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
56 // Handle a challenge received from the server, if more is true the |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
57 // server expects a response, otherwise the response should be nil. If |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
58 // an error is returned SASL authentication will be aborted and an |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
59 // AuthenticationError will be returned to the caller. |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
60 Next(challenge []byte, more bool) (response []byte, err error) |
12
66b46b3d73be
Handle capabilities sent by the server after negotiating a SASL security layer
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
0
diff
changeset
|
61 // Returns true if a SASL security layer was negotiated. |
66b46b3d73be
Handle capabilities sent by the server after negotiating a SASL security layer
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
0
diff
changeset
|
62 SASLSecurityLayer() bool |
0
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
63 } |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
64 |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
65 var ( |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
66 // ErrPlainAuthNotSupported is returned if the server does not support |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
67 // the SASL PLAIN authentication mechanism. |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
68 ErrPlainAuthNotSupported = errors.New("the server does not support PLAIN authentication") |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
69 // ErrPlainAuthTLSRequired is returned when the SASL PLAIN |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
70 // authentication mechanism is used without TLS against a server other |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
71 // than localhost. |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
72 ErrPlainAuthTLSRequired = errors.New("PLAIN authentication requires a TLS connection") |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
73 ) |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
74 |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
75 // HostNameVerificationError is returned when the hostname which was passed to |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
76 // the Auth implementation could not be verified against the TLS certificate. |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
77 type HostNameVerificationError struct { |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
78 ExpectedHost, ActualHost string |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
79 } |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
80 |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
81 func (e *HostNameVerificationError) Error() string { |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
82 return fmt.Sprintf("host name mismatch: %s != %s", e.ActualHost, |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
83 e.ExpectedHost) |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
84 } |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
85 |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
86 type plainAuth struct { |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
87 identity string |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
88 username string |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
89 password string |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
90 host string |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
91 } |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
92 |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
93 func (a *plainAuth) Start(server *ServerInfo) (string, []byte, error) { |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
94 if !server.HaveAuth("PLAIN") { |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
95 return "PLAIN", nil, ErrPlainAuthNotSupported |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
96 } |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
97 |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
98 // enforce TLS for non-local servers in order to avoid leaking |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
99 // credentials via unencrypted connections or DNS spoofing |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
100 if !server.TLS && server.Name != "localhost" && |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
101 server.Name != "127.0.0.1" && server.Name != "::1" { |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
102 return "PLAIN", nil, ErrPlainAuthTLSRequired |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
103 } |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
104 |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
105 // verify server hostname before sending credentials |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
106 if server.Name != a.host { |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
107 return "PLAIN", nil, |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
108 &HostNameVerificationError{a.host, server.Name} |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
109 } |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
110 |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
111 resp := []byte(a.identity + "\x00" + a.username + "\x00" + a.password) |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
112 return "PLAIN", resp, nil |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
113 } |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
114 |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
115 func (a *plainAuth) Next(challenge []byte, more bool) ([]byte, error) { |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
116 return nil, nil |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
117 } |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
118 |
12
66b46b3d73be
Handle capabilities sent by the server after negotiating a SASL security layer
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
0
diff
changeset
|
119 func (a *plainAuth) SASLSecurityLayer() bool { |
66b46b3d73be
Handle capabilities sent by the server after negotiating a SASL security layer
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
0
diff
changeset
|
120 return false |
66b46b3d73be
Handle capabilities sent by the server after negotiating a SASL security layer
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
0
diff
changeset
|
121 } |
66b46b3d73be
Handle capabilities sent by the server after negotiating a SASL security layer
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
0
diff
changeset
|
122 |
0
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
123 // PlainAuth provides an Auth implementation of SASL PLAIN authentication as |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
124 // specified in RFC 4616 using the provided authorization identity, username |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
125 // and password. If the identity is an empty string the server will derive an |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
126 // identity from the credentials. |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
127 func PlainAuth(identity, username, password, host string) Auth { |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
128 return &plainAuth{identity, username, password, host} |
6369453d47a3
Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff
changeset
|
129 } |