1
0
mirror of https://blitiri.com.ar/repos/chasquid synced 2025-12-21 15:17:01 +00:00

smtpsrv: Keep reading DATA input even if it's too large

When the DATA input is too large, we should keep on reading through it
until we reach the end marker, otherwise there is a security problem:
the remaining data will be interpreted as SMTP commands, so for example
a forwarded message that is too long might end up executing SMTP
commands under an authenticated user.

This patch implements this behaviour, while being careful not to consume
extra memory to avoid opening up the possibility of a DoS.

Note the equivalent logic for single long lines is already implemented.
This commit is contained in:
Alberto Bertogli
2019-12-04 01:46:54 +00:00
parent 28cb9169cc
commit 4edcd79a25
3 changed files with 85 additions and 0 deletions

View File

@@ -1,7 +1,9 @@
package smtpsrv
import (
"bufio"
"net"
"strings"
"testing"
"blitiri.com.ar/go/chasquid/internal/domaininfo"
@@ -79,6 +81,57 @@ func TestIsHeader(t *testing.T) {
}
}
func TestReadUntilDot(t *testing.T) {
// This must be > than the minimum buffer size for bufio.Reader, which
// unfortunately is not available to us. The current value is 16, these
// tests will break if it gets increased, and the nonfinal cases will need
// to be adjusted.
size := 20
xs := "12345678901234567890"
final := []string{
"", ".", "..",
".\r\n", "\r\n.", "\r\n.\r\n",
".\n", "\n.", "\n.\n",
".\r", "\r.", "\r.\r",
xs + "\r\n.\r\n",
xs + "1234\r\n.\r\n",
xs + xs + "\r\n.\r\n",
xs + xs + xs + "\r\n.\r\n",
xs + "." + xs + "\n.",
xs + ".\n" + xs + "\n.",
}
for _, s := range final {
t.Logf("testing %q", s)
buf := bufio.NewReaderSize(strings.NewReader(s), size)
readUntilDot(buf)
if r := buf.Buffered(); r != 0 {
t.Errorf("%q: there are %d remaining bytes", s, r)
}
}
nonfinal := []struct {
s string
r int
}{
{".\na", 1},
{"\n.\na", 1},
{"\n.\nabc", 3},
{"\n.\n12345678", 8},
{"\n.\n" + xs, size - 3},
{"\n.\n" + xs + xs, size - 3},
{"\n.\n.\n", 2},
}
for _, c := range nonfinal {
t.Logf("testing %q", c.s)
buf := bufio.NewReaderSize(strings.NewReader(c.s), size)
readUntilDot(buf)
if r := buf.Buffered(); r != c.r {
t.Errorf("%q: expected %d remaining bytes, got %d", c.s, c.r, r)
}
}
}
func TestAddrLiteral(t *testing.T) {
// TCP addresses.
casesTCP := []struct {