From 52120abefd82ebb577acd855454de088c76f4a54 Mon Sep 17 00:00:00 2001 From: Ralph Slooten Date: Tue, 30 May 2023 15:54:26 +1200 Subject: [PATCH] Feature: Add SMTP LOGIN authentication method for message relay See #118 --- config/config.go | 9 +++++++-- server/smtpd/smtp.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/config/config.go b/config/config.go index dbc9a82..26b32f1 100644 --- a/config/config.go +++ b/config/config.go @@ -115,11 +115,11 @@ type smtpRelayConfigStruct struct { Port int `yaml:"port"` STARTTLS bool `yaml:"starttls"` AllowInsecure bool `yaml:"allow-insecure"` - Auth string `yaml:"auth"` // none, plain, cram-md5 + Auth string `yaml:"auth"` // none, plain, login, cram-md5 Username string `yaml:"username"` // plain & cram-md5 Password string `yaml:"password"` // plain Secret string `yaml:"secret"` // cram-md5 - ReturnPath string `yaml:"return-path"` // allows overriding the boune address + ReturnPath string `yaml:"return-path"` // allow overriding the bounce address RecipientAllowlist string `yaml:"recipient-allowlist"` // regex, if set needs to match for mails to be relayed RecipientAllowlistRegexp *regexp.Regexp } @@ -285,6 +285,11 @@ func parseRelayConfig(c string) error { if SMTPRelayConfig.Username == "" || SMTPRelayConfig.Password == "" { return fmt.Errorf("SMTP relay host username or password not set for PLAIN authentication (%s)", c) } + } else if SMTPRelayConfig.Auth == "login" { + SMTPRelayConfig.Auth = "login" + if SMTPRelayConfig.Username == "" || SMTPRelayConfig.Password == "" { + return fmt.Errorf("SMTP relay host username or password not set for LOGIN authentication (%s)", c) + } } else if strings.HasPrefix(SMTPRelayConfig.Auth, "cram") { SMTPRelayConfig.Auth = "cram-md5" if SMTPRelayConfig.Username == "" || SMTPRelayConfig.Secret == "" { diff --git a/server/smtpd/smtp.go b/server/smtpd/smtp.go index c7d68f8..97103be 100644 --- a/server/smtpd/smtp.go +++ b/server/smtpd/smtp.go @@ -69,6 +69,10 @@ func Send(from string, to []string, msg []byte) error { a = smtp.PlainAuth("", config.SMTPRelayConfig.Username, config.SMTPRelayConfig.Password, config.SMTPRelayConfig.Host) } + if config.SMTPRelayConfig.Auth == "login" { + a = LoginAuth(config.SMTPRelayConfig.Username, config.SMTPRelayConfig.Password) + } + if config.SMTPRelayConfig.Auth == "cram-md5" { a = smtp.CRAMMD5Auth(config.SMTPRelayConfig.Username, config.SMTPRelayConfig.Secret) } @@ -103,3 +107,33 @@ func Send(from string, to []string, msg []byte) error { return c.Quit() } + +// Custom implementation of LOGIN SMTP authentication +// @see https://gist.github.com/andelf/5118732 +type loginAuth struct { + username, password string +} + +// LoginAuth authentication +func LoginAuth(username, password string) smtp.Auth { + return &loginAuth{username, password} +} + +func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) { + 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("Unknown fromServer") + } + } + + return nil, nil +}