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
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
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 }