diff --git a/config.go b/config.go index 36f32e5..bdcb54e 100644 --- a/config.go +++ b/config.go @@ -3,6 +3,8 @@ package main import ( "flag" "net" + "regexp" + "net/smtp" "github.com/vharitonsky/iniflags" "github.com/sirupsen/logrus" @@ -25,13 +27,16 @@ var ( localForceTLS = flag.Bool("local_forcetls", false, "Force STARTTLS (needs local_cert and local_key)") allowedNetsStr = flag.String("allowed_nets", "127.0.0.0/8 ::1/128", "Networks allowed to send mails") allowedNets = []*net.IPNet{} - allowedSender = flag.String("allowed_sender", "", "Regular expression for valid FROM EMail addresses") - allowedRecipients = flag.String("allowed_recipients", "", "Regular expression for valid TO EMail addresses") + allowedSenderStr = flag.String("allowed_sender", "", "Regular expression for valid FROM EMail addresses") + allowedSender *regexp.Regexp + allowedRecipStr = flag.String("allowed_recipients", "", "Regular expression for valid TO EMail addresses") + allowedRecipients *regexp.Regexp allowedUsers = flag.String("allowed_users", "", "Path to file with valid users/passwords") 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") ) @@ -59,6 +64,72 @@ func setupAllowedNetworks() { } } +func setupAllowedPatterns() { + var err error + + if (*allowedSenderStr != "") { + allowedSender, err = regexp.Compile(*allowedSenderStr) + if err != nil { + log.WithField("allowed_sender", *allowedSenderStr). + WithError(err). + Fatal("allowed_sender pattern invalid") + } + } + + if (*allowedRecipStr != "") { + allowedRecipients, err = regexp.Compile(*allowedRecipStr) + if err != nil { + log.WithField("allowed_recipients", *allowedRecipStr). + WithError(err). + Fatal("allowed_recipients pattern invalid") + } + } +} + + +func setupRemoteAuth() { + logger := log.WithField("remote_auth", *remoteAuthStr) + + // Remote auth disabled? + if *remoteAuthStr == "" || *remoteAuthStr == "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() @@ -70,4 +141,6 @@ func ConfigLoad() { } setupAllowedNetworks() + setupAllowedPatterns() + setupRemoteAuth() } diff --git a/main.go b/main.go index cbd5993..556647d 100644 --- a/main.go +++ b/main.go @@ -4,10 +4,8 @@ import ( "crypto/tls" "fmt" "net" - "net/smtp" "net/textproto" "os" - "regexp" "strings" "time" @@ -103,43 +101,31 @@ func senderChecker(peer smtpd.Peer, addr string) error { } } - if *allowedSender == "" { + if allowedSender == nil { + // Any sender is permitted return nil } - re, err := regexp.Compile(*allowedSender) - if err != nil { - log.WithFields(logrus.Fields{ - "allowed_sender": *allowedSender, - }).WithError(err).Warn("allowed_sender pattern invalid") - return smtpd.Error{Code: 451, Message: "Bad sender address"} - } - - if re.MatchString(addr) { + if allowedSender.MatchString(addr) { + // Permitted by regex return nil } log.WithFields(logrus.Fields{ "sender_address": addr, "peer": peer.Addr, - }).Warn("Sender address not allowed by allowed_sender pattern") + }).Warn("sender address not allowed by allowed_sender pattern") return smtpd.Error{Code: 451, Message: "Bad sender address"} } func recipientChecker(peer smtpd.Peer, addr string) error { - if *allowedRecipients == "" { + if allowedRecipients == nil { + // Any recipient is permitted return nil } - re, err := regexp.Compile(*allowedRecipients) - if err != nil { - log.WithFields(logrus.Fields{ - "allowed_recipients": *allowedRecipients, - }).WithError(err).Warn("allowed_recipients pattern invalid") - return smtpd.Error{Code: 451, Message: "Bad recipient address"} - } - - if re.MatchString(addr) { + if allowedRecipients.MatchString(addr) { + // Permitted by regex return nil } @@ -183,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 @@ -209,7 +181,7 @@ func mailHandler(peer smtpd.Peer, env smtpd.Envelope) error { err := SendMail( *remoteHost, - auth, + remoteAuth, sender, env.Recipients, env.Data, diff --git a/smtprelay.ini b/smtprelay.ini index 0b97399..b47f103 100644 --- a/smtprelay.ini +++ b/smtprelay.ini @@ -35,10 +35,12 @@ ;allowed_nets = 127.0.0.0/8 ::1/128 ; Regular expression for valid FROM EMail addresses +; If set to "", then any sender is permitted. ; Example: ^(.*)@localhost.localdomain$ ;allowed_sender = ; Regular expression for valid TO EMail addresses +; If set to "", then any recipient is permitted. ; Example: ^(.*)@localhost.localdomain$ ;allowed_recipients = @@ -71,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 =