add strict mode when parsing messages, typically enabled for incoming special-use messages like tls/dmarc reports, subjectpass emails

and pass a logger to the message parser, so problems with message parsing get
the cid logged.
This commit is contained in:
Mechiel Lukkien
2023-08-15 08:25:56 +02:00
parent f5f953b3ab
commit 34c2dcd49d
24 changed files with 312 additions and 153 deletions

View File

@ -10,6 +10,7 @@ import (
"time"
"github.com/mjl-/mox/message"
"github.com/mjl-/mox/mlog"
"github.com/mjl-/mox/moxio"
)
@ -137,9 +138,9 @@ func Parse(r io.Reader) (*Report, error) {
// ParseMessage parses a Report from a mail message.
// The maximum size of the message is 15MB, the maximum size of the
// decompressed report is 20MB.
func ParseMessage(r io.ReaderAt) (*Report, error) {
func ParseMessage(log *mlog.Log, r io.ReaderAt) (*Report, error) {
// ../rfc/8460:905
p, err := message.Parse(&moxio.LimitAtReader{R: r, Limit: 15 * 1024 * 1024})
p, err := message.Parse(log, true, &moxio.LimitAtReader{R: r, Limit: 15 * 1024 * 1024})
if err != nil {
return nil, fmt.Errorf("parsing mail message: %s", err)
}
@ -147,10 +148,10 @@ func ParseMessage(r io.ReaderAt) (*Report, error) {
// Using multipart appears optional, and similar to DMARC someone may decide to
// send it like that, so accept a report if it's the entire message.
const allow = true
return parseMessageReport(p, allow)
return parseMessageReport(log, p, allow)
}
func parseMessageReport(p message.Part, allow bool) (*Report, error) {
func parseMessageReport(log *mlog.Log, p message.Part, allow bool) (*Report, error) {
if p.MediaType != "MULTIPART" {
if !allow {
return nil, ErrNoReport
@ -159,7 +160,7 @@ func parseMessageReport(p message.Part, allow bool) (*Report, error) {
}
for {
sp, err := p.ParseNextPart()
sp, err := p.ParseNextPart(log)
if err == io.EOF {
return nil, ErrNoReport
}
@ -169,7 +170,7 @@ func parseMessageReport(p message.Part, allow bool) (*Report, error) {
if p.MediaSubType == "REPORT" && p.ContentTypeParams["report-type"] != "tlsrpt" {
return nil, fmt.Errorf("unknown report-type parameter %q", p.ContentTypeParams["report-type"])
}
report, err := parseMessageReport(*sp, p.MediaSubType == "REPORT")
report, err := parseMessageReport(log, *sp, p.MediaSubType == "REPORT")
if err == ErrNoReport {
continue
} else if err != nil || report != nil {

View File

@ -109,19 +109,19 @@ func TestReport(t *testing.T) {
t.Fatalf("parsing report: %s", err)
}
if _, err := ParseMessage(strings.NewReader(tlsrptMessage)); err != nil {
if _, err := ParseMessage(xlog, strings.NewReader(tlsrptMessage)); err != nil {
t.Fatalf("parsing TLSRPT from message: %s", err)
}
if _, err := ParseMessage(strings.NewReader(tlsrptMessage2)); err != nil {
if _, err := ParseMessage(xlog, strings.NewReader(tlsrptMessage2)); err != nil {
t.Fatalf("parsing TLSRPT from message: %s", err)
}
if _, err := ParseMessage(strings.NewReader(strings.ReplaceAll(tlsrptMessage, "multipart/report", "multipart/related"))); err != ErrNoReport {
if _, err := ParseMessage(xlog, strings.NewReader(strings.ReplaceAll(tlsrptMessage, "multipart/report", "multipart/related"))); err != ErrNoReport {
t.Fatalf("got err %v, expected ErrNoReport", err)
}
if _, err := ParseMessage(strings.NewReader(strings.ReplaceAll(tlsrptMessage, "application/tlsrpt+json", "application/json"))); err != ErrNoReport {
if _, err := ParseMessage(xlog, strings.NewReader(strings.ReplaceAll(tlsrptMessage, "application/tlsrpt+json", "application/json"))); err != ErrNoReport {
t.Fatalf("got err %v, expected ErrNoReport", err)
}
@ -134,7 +134,7 @@ func TestReport(t *testing.T) {
if err != nil {
t.Fatalf("open %q: %s", file, err)
}
if _, err := ParseMessage(f); err != nil {
if _, err := ParseMessage(xlog, f); err != nil {
t.Fatalf("parsing TLSRPT from message %q: %s", file.Name(), err)
}
f.Close()
@ -144,6 +144,6 @@ func TestReport(t *testing.T) {
func FuzzParseMessage(f *testing.F) {
f.Add(tlsrptMessage)
f.Fuzz(func(t *testing.T, s string) {
ParseMessage(strings.NewReader(s))
ParseMessage(xlog, strings.NewReader(s))
})
}