attempt at improving interoperability of with outlook 365 using the smtp "login" sasl auth mechanism

by sending the (encoded) string "User Name" as mentioned by the internet-draft,
https://datatracker.ietf.org/doc/html/draft-murchison-sasl-login-00#section-2.1

that document says clients should ignore the challenge (which is why were were
not doing any effort and sending an empty challenge). but it also says some
clients require the challenge "Username:" instead of "User Name", implying that
it's important to not send an empty challenge. we can't send both challenges
though...

for issue #51
This commit is contained in:
Mechiel Lukkien 2024-07-18 21:17:33 +02:00
parent c629ae26af
commit aead738836
No known key found for this signature in database

View file

@ -1018,10 +1018,12 @@ func (c *conn) cmdAuth(p *parser) {
p.xspace() p.xspace()
mech := p.xsaslMech() mech := p.xsaslMech()
xreadInitial := func() []byte { // Read the first parameter, either as initial parameter or by sending a
// continuation with the optional encChal (must already be base64-encoded).
xreadInitial := func(encChal string) []byte {
var auth string var auth string
if p.empty() { if p.empty() {
c.writelinef("%d ", smtp.C334ContinueAuth) // ../rfc/4954:205 c.writelinef("%d %s", smtp.C334ContinueAuth, encChal) // ../rfc/4954:205
// todo future: handle max length of 12288 octets and return proper responde codes otherwise ../rfc/4954:253 // todo future: handle max length of 12288 octets and return proper responde codes otherwise ../rfc/4954:253
auth = c.readline() auth = c.readline()
if auth == "*" { if auth == "*" {
@ -1080,7 +1082,7 @@ func (c *conn) cmdAuth(p *parser) {
// Password is in line in plain text, so hide it. // Password is in line in plain text, so hide it.
defer c.xtrace(mlog.LevelTraceauth)() defer c.xtrace(mlog.LevelTraceauth)()
buf := xreadInitial() buf := xreadInitial("")
c.xtrace(mlog.LevelTrace) // Restore. c.xtrace(mlog.LevelTrace) // Restore.
plain := bytes.Split(buf, []byte{0}) plain := bytes.Split(buf, []byte{0})
if len(plain) != 3 { if len(plain) != 3 {
@ -1125,11 +1127,13 @@ func (c *conn) cmdAuth(p *parser) {
xsmtpUserErrorf(smtp.C538EncReqForAuth, smtp.SePol7EncReqForAuth11, "authentication requires tls") xsmtpUserErrorf(smtp.C538EncReqForAuth, smtp.SePol7EncReqForAuth11, "authentication requires tls")
} }
// Read user name. The I-D says the client should ignore the server challenge, we // Read user name. The I-D says the client should ignore the server challenge, but
// send an empty one. // also that some clients may require challenge "Username:" instead of "User
// Name". We can't sent both...
// I-D says maximum length must be 64 bytes. We allow more, for long user names // I-D says maximum length must be 64 bytes. We allow more, for long user names
// (domains). // (domains).
username := string(xreadInitial()) encChal := base64.StdEncoding.EncodeToString([]byte("User Name"))
username := string(xreadInitial(encChal))
username = norm.NFC.String(username) username = norm.NFC.String(username)
// Again, client should ignore the challenge, we send the same as the example in // Again, client should ignore the challenge, we send the same as the example in
@ -1258,7 +1262,7 @@ func (c *conn) cmdAuth(p *parser) {
xcs := c.conn.(*tls.Conn).ConnectionState() xcs := c.conn.(*tls.Conn).ConnectionState()
cs = &xcs cs = &xcs
} }
c0 := xreadInitial() c0 := xreadInitial("")
ss, err := scram.NewServer(h, c0, cs, channelBindingRequired) ss, err := scram.NewServer(h, c0, cs, channelBindingRequired)
xcheckf(err, "starting scram") xcheckf(err, "starting scram")
authc := norm.NFC.String(ss.Authentication) authc := norm.NFC.String(ss.Authentication)