Skip to content

Commit 426c618

Browse files
apreimlfoxcpp
authored andcommitted
add max_header_size, check header size in smtp session
1 parent 2d691f1 commit 426c618

File tree

3 files changed

+76
-1
lines changed

3 files changed

+76
-1
lines changed

internal/endpoint/smtp/session.go

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,31 @@ import (
4040
"github.com/foxcpp/maddy/framework/module"
4141
)
4242

43+
func limitReader(r io.Reader, n int64, err error) *limitedReader {
44+
return &limitedReader{R: r, N: n, E: err, Enabled: true}
45+
}
46+
47+
type limitedReader struct {
48+
R io.Reader
49+
N int64
50+
E error
51+
Enabled bool
52+
}
53+
54+
// same as io.LimitedReader.Read except returning the custom error and the option
55+
// to be disabled
56+
func (l *limitedReader) Read(p []byte) (n int, err error) {
57+
if l.Enabled && l.N <= 0 {
58+
return 0, l.E
59+
}
60+
if int64(len(p)) > l.N {
61+
p = p[0:l.N]
62+
}
63+
n, err = l.R.Read(p)
64+
l.N -= int64(n)
65+
return
66+
}
67+
4368
type Session struct {
4469
endp *Endpoint
4570

@@ -340,7 +365,13 @@ func (s *Session) Logout() error {
340365
}
341366

342367
func (s *Session) prepareBody(ctx context.Context, r io.Reader) (textproto.Header, buffer.Buffer, error) {
343-
bufr := bufio.NewReader(r)
368+
limitr := limitReader(r, int64(s.endp.maxHeaderBytes), &exterrors.SMTPError{
369+
Code: 552,
370+
EnhancedCode: exterrors.EnhancedCode{5, 3, 4},
371+
Message: "Message header size exceeds limit",
372+
})
373+
374+
bufr := bufio.NewReader(limitr)
344375
header, err := textproto.ReadHeader(bufr)
345376
if err != nil {
346377
return textproto.Header{}, nil, fmt.Errorf("I/O error while parsing header: %w", err)
@@ -353,6 +384,9 @@ func (s *Session) prepareBody(ctx context.Context, r io.Reader) (textproto.Heade
353384
}
354385
}
355386

387+
// the header size check is done. The message size will be checked by go-smtp
388+
limitr.Enabled = false
389+
356390
buf, err := s.endp.buffer(bufr)
357391
if err != nil {
358392
return textproto.Header{}, nil, fmt.Errorf("I/O error while writing buffer: %w", err)

internal/endpoint/smtp/smtp.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ type Endpoint struct {
6767
deferServerReject bool
6868
maxLoggedRcptErrors int
6969
maxReceived int
70+
maxHeaderBytes int
7071

7172
listenersWg sync.WaitGroup
7273

@@ -245,6 +246,7 @@ func (endp *Endpoint) setConfig(cfg *config.Map) error {
245246
cfg.Duration("write_timeout", false, false, 1*time.Minute, &endp.serv.WriteTimeout)
246247
cfg.Duration("read_timeout", false, false, 10*time.Minute, &endp.serv.ReadTimeout)
247248
cfg.DataSize("max_message_size", false, false, 32*1024*1024, &endp.serv.MaxMessageBytes)
249+
cfg.DataSize("max_header_size", false, false, 1*1024*1024, &endp.maxHeaderBytes)
248250
cfg.Int("max_recipients", false, false, 20000, &endp.serv.MaxRecipients)
249251
cfg.Int("max_received", false, false, 50, &endp.maxReceived)
250252
cfg.Custom("buffer", false, false, func() (interface{}, error) {

tests/smtp_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"errors"
2525
"io/ioutil"
2626
"path/filepath"
27+
"strings"
2728
"testing"
2829

2930
"github.com/foxcpp/go-mockdns"
@@ -456,3 +457,41 @@ func TestCheckCommand(tt *testing.T) {
456457
conn.Writeln("QUIT")
457458
conn.ExpectPattern("221 *")
458459
}
460+
461+
func TestHeaderSizeConstraint(tt *testing.T) {
462+
tt.Parallel()
463+
t := tests.NewT(tt)
464+
t.DNS(nil)
465+
t.Port("smtp")
466+
t.Config(`
467+
smtp tcp://127.0.0.1:{env:TEST_PORT_smtp} {
468+
hostname mx.maddy.test
469+
tls off
470+
deliver_to dummy
471+
max_header_size 1K
472+
}
473+
`)
474+
t.Run(1)
475+
defer t.Close()
476+
477+
conn := t.Conn("smtp")
478+
defer conn.Close()
479+
conn.SMTPNegotation("localhost", nil, nil)
480+
conn.Writeln("MAIL FROM:<[email protected]>")
481+
conn.ExpectPattern("250 *")
482+
conn.Writeln("RCPT TO:<[email protected]>")
483+
conn.ExpectPattern("250 *")
484+
conn.Writeln("DATA")
485+
conn.ExpectPattern("354 *")
486+
conn.Writeln("From: <[email protected]>")
487+
conn.Writeln("To: <[email protected]>")
488+
conn.Writeln("Subject: " + strings.Repeat("A", 2*1024))
489+
conn.Writeln("")
490+
conn.Writeln("Hi")
491+
conn.Writeln(".")
492+
493+
conn.ExpectPattern("552 5.3.4 Message header size exceeds limit *")
494+
495+
conn.Writeln("QUIT")
496+
conn.ExpectPattern("221 *")
497+
}

0 commit comments

Comments
 (0)