Move SMTP auth setup to ConfigLoad()

This has several benefits:
- Configuration errors are caught at startup rather than upon a connection
- mailHandler() has less work to do for each connection

Rather than relying on remote_user and remote_pass to control whether
authentication is used, introduce an explicit "none" type for
remote_auth, and make that the default. (This is effectively the same
default behavior since remote_user and remote_pass default to empty.)

Also, we are in a better position to more thoroughly check for
configuration errors or mismatches:
- If remote_auth is given, remote_user and remote_pass are required.
- If remote_auth is given, remote_host is also required (because it
  makes no sense to say we're going to authenticate if we have no server
  to which to authenticate.)
- If remote_user or remote_pass are given, remote_auth cannot be "none".
This commit is contained in:
Jonathon Reinhart
2021-03-14 14:06:21 -04:00
parent 76ef135d33
commit 22ef0c2ee6
3 changed files with 52 additions and 19 deletions

View File

@@ -4,6 +4,7 @@ import (
"flag"
"net"
"regexp"
"net/smtp"
"github.com/vharitonsky/iniflags"
"github.com/sirupsen/logrus"
@@ -34,7 +35,8 @@ var (
remoteHost = flag.String("remote_host", "", "Outgoing SMTP server")
remoteUser = flag.String("remote_user", "", "Username for authentication on outgoing SMTP server")
remotePass = flag.String("remote_pass", "", "Password for authentication on outgoing SMTP server")
remoteAuth = flag.String("remote_auth", "plain", "Auth method on outgoing SMTP server (plain, login)")
remoteAuthStr = flag.String("remote_auth", "none", "Auth method on outgoing SMTP server (none, plain, login)")
remoteAuth smtp.Auth
remoteSender = flag.String("remote_sender", "", "Sender e-mail address on outgoing SMTP server")
versionInfo = flag.Bool("version", false, "Show version information")
)
@@ -84,6 +86,51 @@ func setupAllowedPatterns() {
}
}
func setupRemoteAuth() {
logger := log.WithField("remote_auth", *remoteAuthStr)
// Remote auth disabled?
switch *remoteAuthStr {
case "", "none":
if *remoteUser != "" {
logger.Fatal("remote_user given but not used")
}
if *remotePass != "" {
logger.Fatal("remote_pass given but not used")
}
// No auth; use empty default
return
}
// We need a username, password, and remote host
if *remoteUser == "" {
logger.Fatal("remote_user required but empty")
}
if *remotePass == "" {
logger.Fatal("remote_pass required but empty")
}
if *remoteHost == "" {
logger.Fatal("remote_auth without remote_host is pointless")
}
host, _, err := net.SplitHostPort(*remoteHost)
if err != nil {
logger.WithField("remote_host", *remoteHost).
Fatal("Invalid remote_host")
}
switch *remoteAuthStr {
case "plain":
remoteAuth = smtp.PlainAuth("", *remoteUser, *remotePass, host)
case "login":
remoteAuth = LoginAuth(*remoteUser, *remotePass)
default:
logger.Fatal("Invalid remote_auth type")
}
}
func ConfigLoad() {
iniflags.Parse()
@@ -96,4 +143,5 @@ func ConfigLoad() {
setupAllowedNetworks()
setupAllowedPatterns()
setupRemoteAuth()
}

17
main.go
View File

@@ -4,7 +4,6 @@ import (
"crypto/tls"
"fmt"
"net"
"net/smtp"
"net/textproto"
"os"
"strings"
@@ -170,20 +169,6 @@ func mailHandler(peer smtpd.Peer, env smtpd.Envelope) error {
logger.Info("delivering mail from peer using smarthost")
var auth smtp.Auth
host, _, _ := net.SplitHostPort(*remoteHost)
if *remoteUser != "" && *remotePass != "" {
switch *remoteAuth {
case "plain":
auth = smtp.PlainAuth("", *remoteUser, *remotePass, host)
case "login":
auth = LoginAuth(*remoteUser, *remotePass)
default:
return smtpd.Error{Code: 530, Message: "Authentication method not supported"}
}
}
env.AddReceivedLine(peer)
var sender string
@@ -196,7 +181,7 @@ func mailHandler(peer smtpd.Peer, env smtpd.Envelope) error {
err := SendMail(
*remoteHost,
auth,
remoteAuth,
sender,
env.Recipients,
env.Data,

View File

@@ -73,8 +73,8 @@
;remote_pass =
; Authentication method on outgoing SMTP server
; (plain, login)
;remote_auth = plain
; (none, plain, login)
;remote_auth = none
; Sender e-mail address on outgoing SMTP server
;remote_sender =