forked from emersion/go-imap
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cmd_noauth.go
127 lines (106 loc) · 2.57 KB
/
cmd_noauth.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
package server
import (
"crypto/tls"
"errors"
"net"
"github.com/emersion/go-imap"
"github.com/emersion/go-imap/commands"
"github.com/emersion/go-imap/responses"
"github.com/emersion/go-sasl"
)
// IMAP errors in Not Authenticated state.
var (
ErrAlreadyAuthenticated = errors.New("Already authenticated")
ErrAuthDisabled = errors.New("Authentication disabled")
)
type StartTLS struct {
commands.StartTLS
}
func (cmd *StartTLS) Handle(conn Conn) error {
ctx := conn.Context()
if ctx.State != imap.NotAuthenticatedState {
return ErrAlreadyAuthenticated
}
if conn.IsTLS() {
return errors.New("TLS is already enabled")
}
if conn.Server().TLSConfig == nil {
return errors.New("TLS support not enabled")
}
// Send an OK status response to let the client know that the TLS handshake
// can begin
return ErrStatusResp(&imap.StatusResp{
Type: imap.StatusRespOk,
Info: "Begin TLS negotiation now",
})
}
func (cmd *StartTLS) Upgrade(conn Conn) error {
tlsConfig := conn.Server().TLSConfig
var tlsConn *tls.Conn
err := conn.Upgrade(func(conn net.Conn) (net.Conn, error) {
tlsConn = tls.Server(conn, tlsConfig)
err := tlsConn.Handshake()
return tlsConn, err
})
if err != nil {
return err
}
conn.setTLSConn(tlsConn)
res := &responses.Capability{Caps: conn.Capabilities()}
return conn.WriteResp(res)
}
func afterAuthStatus(conn Conn) error {
return ErrStatusResp(&imap.StatusResp{
Type: imap.StatusRespOk,
Code: imap.CodeCapability,
Arguments: imap.FormatStringList(conn.Capabilities()),
})
}
func canAuth(conn Conn) bool {
for _, cap := range conn.Capabilities() {
if cap == "AUTH=PLAIN" {
return true
}
}
return false
}
type Login struct {
commands.Login
}
func (cmd *Login) Handle(conn Conn) error {
ctx := conn.Context()
if ctx.State != imap.NotAuthenticatedState {
return ErrAlreadyAuthenticated
}
if !canAuth(conn) {
return ErrAuthDisabled
}
user, err := conn.Server().Backend.Login(cmd.Username, cmd.Password)
if err != nil {
return err
}
ctx.State = imap.AuthenticatedState
ctx.User = user
return afterAuthStatus(conn)
}
type Authenticate struct {
commands.Authenticate
}
func (cmd *Authenticate) Handle(conn Conn) error {
ctx := conn.Context()
if ctx.State != imap.NotAuthenticatedState {
return ErrAlreadyAuthenticated
}
if !canAuth(conn) {
return ErrAuthDisabled
}
mechanisms := map[string]sasl.Server{}
for name, newSasl := range conn.Server().auths {
mechanisms[name] = newSasl(conn)
}
err := cmd.Authenticate.Handle(mechanisms, conn)
if err != nil {
return err
}
return afterAuthStatus(conn)
}