return
}
- conn.log.Info("ReadLine()", zap.String("line", conn.line))
+ lineForLog := conn.line
+ const authPlain = "AUTH PLAIN "
+ if strings.HasPrefix(conn.line, authPlain) {
+ lineForLog = authPlain + "[redacted]"
+ }
+ conn.log.Info("ReadLine()", zap.String("line", lineForLog))
var cmd string
if _, err = fmt.Sscanf(conn.line, "%s", &cmd); err != nil {
return
}
- var cmd, authType string
- _, err := fmt.Sscanf(conn.line, "%s %s", &cmd, &authType)
- if err != nil {
+ var cmd, authType, authString string
+ n, err := fmt.Sscanf(conn.line, "%s %s %s", &cmd, &authType, &authString)
+ if n < 2 {
conn.reply(ReplyBadSyntax)
return
}
return
}
+ // If only 2 tokens were scanned, then an initial response was not provided.
+ if n == 2 && conn.line[len(conn.line)-1] != ' ' {
+ conn.reply(ReplyBadSyntax)
+ return
+ }
+
conn.log.Info("doAUTH()")
- conn.writeReply(334, " ")
+ if authString == "" {
+ conn.writeReply(334, " ")
- authLine, err := conn.tp.ReadLine()
- if err != nil {
- conn.log.Error("failed to read auth line", zap.Error(err))
- conn.reply(ReplyBadSyntax)
- return
+ authString, err = conn.tp.ReadLine()
+ if err != nil {
+ conn.log.Error("failed to read auth line", zap.Error(err))
+ conn.reply(ReplyBadSyntax)
+ return
+ }
}
- authBytes, err := base64.StdEncoding.DecodeString(authLine)
+ authBytes, err := base64.StdEncoding.DecodeString(authString)
if err != nil {
conn.reply(ReplyBadSyntax)
return
func runTableTest(t testing.TB, conn *textproto.Conn, seq []requestResponse) {
for i, rr := range seq {
- t.Logf("%s case %d", _fl(1), i)
ok(t, conn.PrintfLine(rr.request))
if rr.handler != nil {
rr.handler(t, conn)
} else {
readCodeLine(t, conn, rr.responseCode)
}
+ if t.Failed() {
+ t.Logf("%s case %d", _fl(1), i)
+ }
}
}
runTableTest(t, conn, []requestResponse{
{"AUTH", 501, nil},
{"AUTH OAUTHBEARER", 504, nil},
- {"AUTH PLAIN", 334, nil},
+ {"AUTH PLAIN", 501, nil}, // Bad syntax, missing space.
+ {"AUTH PLAIN ", 334, nil},
{b64enc("abc\x00def\x00ghf"), 535, nil},
- {"AUTH PLAIN", 334, nil},
+ {"AUTH PLAIN ", 334, nil},
{b64enc("\x00"), 501, nil},
- {"AUTH PLAIN", 334, nil},
+ {"AUTH PLAIN ", 334, nil},
{"this isn't base 64", 501, nil},
- {"AUTH PLAIN", 334, nil},
+ {"AUTH PLAIN ", 334, nil},
{b64enc("-authz-\x00-authc-\x00goats"), 250, nil},
- {"AUTH PLAIN", 503, nil}, // already authenticated
+ {"AUTH PLAIN ", 503, nil}, // Already authenticated.
{"NOOP", 250, nil},
})
}
+func TestAuthNoInitialResponse(t *testing.T) {
+ l := runServer(t, &testServer{
+ tlsConfig: getTLSConfig(t),
+ userAuth: &userAuth{
+ authz: "",
+ authc: "user",
+ passwd: "longpassword",
+ },
+ })
+ defer l.Close()
+
+ conn := setupTLSClient(t, l.Addr())
+
+ runTableTest(t, conn, []requestResponse{
+ {"AUTH PLAIN " + b64enc("\x00user\x00longpassword"), 250, nil},
+ })
+}
+
func TestRelayRequiresAuth(t *testing.T) {
l := runServer(t, &testServer{
domain: "example.com",
runTableTest(t, conn, []requestResponse{
{"MAIL FROM:<apples@example.com>", 550, nil},
{"MAIL FROM:<mailbox@example.com>", 550, nil},
- {"AUTH PLAIN", 334, nil},
+ {"AUTH PLAIN ", 334, nil},
{b64enc("\x00mailbox@example.com\x00test"), 250, nil},
{"MAIL FROM:<mailbox@example.com>", 250, nil},
})
l = runServer(t, server)
conn = setupTLSClient(t, l.Addr())
runTableTest(t, conn, []requestResponse{
- {"AUTH PLAIN", 334, nil},
+ {"AUTH PLAIN ", 334, nil},
{b64enc("\x00mailbox@example.com\x00test"), 250, nil},
})
return
})
if len(server.relayed) != 1 {
- t.Errorf("Expected 1 relayed message, got %d", len(server.relayed))
+ t.Fatalf("Expected 1 relayed message, got %d", len(server.relayed))
}
replaced := "source@example.com"
})
if len(server.relayed) != 1 {
- t.Errorf("Expected 1 relayed message, got %d", len(server.relayed))
+ t.Fatalf("Expected 1 relayed message, got %d", len(server.relayed))
}
replaced := "source@example.com"