From 4b5541db46d75182a7a83b1361ed785b401b9118 Mon Sep 17 00:00:00 2001 From: Carlos Tadeu Panato Junior Date: Thu, 1 Mar 2018 12:58:35 +0100 Subject: [PLT-8024] Support LOGIN authentication method for SMTP (#8140) * [PLT-8024] Support LOGIN authentication method for SMTP * added initial unit tests --- utils/mail.go | 58 ++++++++++++++++++++++++++++++++++++++++++++----- utils/mail_test.go | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 5 deletions(-) diff --git a/utils/mail.go b/utils/mail.go index 9023f7090..2bc0ce9e1 100644 --- a/utils/mail.go +++ b/utils/mail.go @@ -5,6 +5,8 @@ package utils import ( "crypto/tls" + "errors" + "io" "mime" "net" "net/mail" @@ -15,8 +17,6 @@ import ( "net/http" - "io" - l4g "github.com/alecthomas/log4go" "github.com/mattermost/html2text" "github.com/mattermost/mattermost-server/model" @@ -26,6 +26,56 @@ func encodeRFC2047Word(s string) string { return mime.BEncoding.Encode("utf-8", s) } +type authChooser struct { + smtp.Auth + Config *model.Config +} + +func (a *authChooser) Start(server *smtp.ServerInfo) (string, []byte, error) { + a.Auth = LoginAuth(a.Config.EmailSettings.SMTPUsername, a.Config.EmailSettings.SMTPPassword, a.Config.EmailSettings.SMTPServer+":"+a.Config.EmailSettings.SMTPPort) + for _, method := range server.Auth { + if method == "PLAIN" { + a.Auth = smtp.PlainAuth("", a.Config.EmailSettings.SMTPUsername, a.Config.EmailSettings.SMTPPassword, a.Config.EmailSettings.SMTPServer+":"+a.Config.EmailSettings.SMTPPort) + break + } + } + return a.Auth.Start(server) +} + +type loginAuth struct { + username, password, host string +} + +func LoginAuth(username, password, host string) smtp.Auth { + return &loginAuth{username, password, host} +} + +func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) { + if !server.TLS { + return "", nil, errors.New("unencrypted connection") + } + + if server.Name != a.host { + return "", nil, errors.New("wrong host name") + } + + return "LOGIN", []byte{}, nil +} + +func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) { + if more { + switch string(fromServer) { + case "Username:": + return []byte(a.username), nil + case "Password:": + return []byte(a.password), nil + default: + return nil, errors.New("Unkown fromServer") + } + } + return nil, nil +} + func connectToSMTPServer(config *model.Config) (net.Conn, *model.AppError) { var conn net.Conn var err error @@ -75,9 +125,7 @@ func newSMTPClient(conn net.Conn, config *model.Config) (*smtp.Client, *model.Ap } if *config.EmailSettings.EnableSMTPAuth { - auth := smtp.PlainAuth("", config.EmailSettings.SMTPUsername, config.EmailSettings.SMTPPassword, config.EmailSettings.SMTPServer+":"+config.EmailSettings.SMTPPort) - - if err = c.Auth(auth); err != nil { + if err = c.Auth(&authChooser{Config: config}); err != nil { return nil, model.NewAppError("SendMail", "utils.mail.new_client.auth.app_error", nil, err.Error(), http.StatusInternalServerError) } } diff --git a/utils/mail_test.go b/utils/mail_test.go index 068c90c60..31a4f8996 100644 --- a/utils/mail_test.go +++ b/utils/mail_test.go @@ -7,6 +7,9 @@ import ( "strings" "testing" + "net/smtp" + + "github.com/mattermost/mattermost-server/model" "github.com/stretchr/testify/require" ) @@ -169,3 +172,64 @@ func TestSendMailUsingConfig(t *testing.T) { } } }*/ + +func TestAuthMethods(t *testing.T) { + config := model.Config{ + EmailSettings: model.EmailSettings{ + EnableSMTPAuth: model.NewBool(false), + SMTPUsername: "test", + SMTPPassword: "fakepass", + SMTPServer: "fakeserver", + SMTPPort: "25", + }, + } + + auth := &authChooser{Config: &config} + tests := []struct { + desc string + server *smtp.ServerInfo + err string + }{ + { + desc: "auth PLAIN success", + server: &smtp.ServerInfo{Name: "fakeserver:25", Auth: []string{"PLAIN"}, TLS: true}, + }, + { + desc: "auth PLAIN unencrypted connection fail", + server: &smtp.ServerInfo{Name: "fakeserver:25", Auth: []string{"PLAIN"}, TLS: false}, + err: "unencrypted connection", + }, + { + desc: "auth PLAIN wrong host name", + server: &smtp.ServerInfo{Name: "wrongServer:999", Auth: []string{"PLAIN"}, TLS: true}, + err: "wrong host name", + }, + { + desc: "auth LOGIN success", + server: &smtp.ServerInfo{Name: "fakeserver:25", Auth: []string{"LOGIN"}, TLS: true}, + }, + { + desc: "auth LOGIN unencrypted connection fail", + server: &smtp.ServerInfo{Name: "wrongServer:999", Auth: []string{"LOGIN"}, TLS: true}, + err: "wrong host name", + }, + { + desc: "auth LOGIN wrong host name", + server: &smtp.ServerInfo{Name: "fakeserver:25", Auth: []string{"LOGIN"}, TLS: false}, + err: "unencrypted connection", + }, + } + + for i, test := range tests { + t.Run(test.desc, func(t *testing.T) { + _, _, err := auth.Start(test.server) + got := "" + if err != nil { + got = err.Error() + } + if got != test.err { + t.Errorf("%d. got error = %q; want %q", i, got, test.err) + } + }) + } +} -- cgit v1.2.3-1-g7c22