Skip to content

Commit 9aa077a

Browse files
committed
fix(mail): support LOGIN auth, fix this issue: #2688
1 parent 11a992e commit 9aa077a

File tree

2 files changed

+72
-16
lines changed

2 files changed

+72
-16
lines changed

util/mailer/auth.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package mailer
2+
3+
import (
4+
"bytes"
5+
"errors"
6+
"fmt"
7+
"net/smtp"
8+
"slices"
9+
)
10+
11+
func PlainOrLoginAuth(username, password, host string) smtp.Auth {
12+
return &plainOrLoginAuth{username: username, password: password, host: host}
13+
}
14+
15+
func isLocalhost(name string) bool {
16+
return name == "localhost" || name == "127.0.0.1" || name == "::1"
17+
}
18+
19+
type plainOrLoginAuth struct {
20+
username string
21+
password string
22+
host string
23+
authMethod string
24+
}
25+
26+
func (a *plainOrLoginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
27+
// Must have TLS, or else localhost server.
28+
// Note: If TLS is not true, then we can't trust ANYTHING in ServerInfo.
29+
// In particular, it doesn't matter if the server advertises PLAIN auth.
30+
// That might just be the attacker saying
31+
// "it's ok, you can trust me with your password."
32+
if !server.TLS && !isLocalhost(server.Name) {
33+
return "", nil, errors.New("unencrypted connection")
34+
}
35+
if server.Name != a.host {
36+
return "", nil, errors.New("wrong host name")
37+
}
38+
if !slices.Contains(server.Auth, "PLAIN") {
39+
a.authMethod = "LOGIN"
40+
return a.authMethod, nil, nil
41+
} else {
42+
a.authMethod = "PLAIN"
43+
resp := []byte("\x00" + a.username + "\x00" + a.password)
44+
return a.authMethod, resp, nil
45+
}
46+
}
47+
48+
func (a *plainOrLoginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
49+
if !more {
50+
return nil, nil
51+
}
52+
53+
if a.authMethod == "PLAIN" {
54+
// We've already sent everything.
55+
return nil, errors.New("unexpected server challenge")
56+
}
57+
58+
switch {
59+
case bytes.Equal(fromServer, []byte("Username:")):
60+
return []byte(a.username), nil
61+
case bytes.Equal(fromServer, []byte("Password:")):
62+
return []byte(a.password), nil
63+
default:
64+
return nil, fmt.Errorf("unexpected server challenge: %s", fromServer)
65+
}
66+
}

util/mailer/mailer.go

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -97,17 +97,12 @@ func plainauth(
9797
to string,
9898
body *bytes.Buffer,
9999
) error {
100+
auth := PlainOrLoginAuth(username, password, host)
101+
//auth := smtp.PlainAuth("", username, password, host)
102+
100103
return smtp.SendMail(
101-
net.JoinHostPort(
102-
host,
103-
port,
104-
),
105-
smtp.PlainAuth(
106-
"",
107-
username,
108-
password,
109-
host,
110-
),
104+
net.JoinHostPort(host, port),
105+
auth,
111106
from,
112107
[]string{to},
113108
body.Bytes(),
@@ -121,12 +116,7 @@ func anonymous(
121116
to string,
122117
body *bytes.Buffer,
123118
) error {
124-
c, err := smtp.Dial(
125-
net.JoinHostPort(
126-
host,
127-
port,
128-
),
129-
)
119+
c, err := smtp.Dial(net.JoinHostPort(host, port))
130120

131121
if err != nil {
132122
return err

0 commit comments

Comments
 (0)