From e1ed21abff8d73d86a0725b58d6e0b358c933284 Mon Sep 17 00:00:00 2001 From: Ralph Slooten Date: Thu, 24 Jul 2025 17:04:15 +1200 Subject: [PATCH] Chore: Allow sendmail to send to untrusted TLS server --- sendmail/cmd/smtp.go | 85 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/sendmail/cmd/smtp.go b/sendmail/cmd/smtp.go index e65b4a5..47aae9a 100644 --- a/sendmail/cmd/smtp.go +++ b/sendmail/cmd/smtp.go @@ -2,11 +2,15 @@ package cmd import ( + "crypto/tls" + "errors" "fmt" "net" "net/mail" "net/smtp" + "net/url" "os" + "strings" "github.com/axllent/mailpit/internal/logger" ) @@ -27,7 +31,7 @@ func Send(addr string, from string, to []string, msg []byte) error { } if !isSocket { - return smtp.SendMail(addr, nil, fromAddress.Address, to, msg) + return sendMail(addr, nil, fromAddress.Address, to, msg) } conn, err := net.Dial("unix", socketPath) @@ -69,3 +73,82 @@ func Send(addr string, from string, to []string, msg []byte) error { return nil } + +func sendMail(addr string, a smtp.Auth, from string, to []string, msg []byte) error { + addrParsed, err := url.Parse(addr) // ensure addr is a valid URL + if err != nil { + return fmt.Errorf("invalid address: %s", addr) + } + + host := addrParsed.Host + if err := validateLine(from); err != nil { + return err + } + + for _, recipient := range to { + if err := validateLine(recipient); err != nil { + return err + } + } + + c, err := smtp.Dial(addr) + if err != nil { + return err + } + defer func() { _ = c.Close() }() + + if err = c.Hello(addr); err != nil { + return err + } + + if ok, _ := c.Extension("STARTTLS"); ok { + config := &tls.Config{ServerName: host, InsecureSkipVerify: true} // #nosec + if err = c.StartTLS(config); err != nil { + return err + } + } + + if a != nil { + if ok, _ := c.Extension("AUTH"); !ok { + return errors.New("smtp: server doesn't support AUTH") + } + if err = c.Auth(a); err != nil { + return err + } + } + + if err = c.Mail(from); err != nil { + return err + } + + for _, addr := range to { + if err = c.Rcpt(addr); err != nil { + return err + } + } + + w, err := c.Data() + if err != nil { + return err + } + + _, err = w.Write(msg) + if err != nil { + return err + } + + err = w.Close() + if err != nil { + return err + } + + return c.Quit() +} + +// validateLine checks to see if a line has CR or LF as per RFC 5321. +func validateLine(line string) error { + if strings.ContainsAny(line, "\n\r") { + return errors.New("smtp: A line must not contain CR or LF") + } + return nil +}