mirror of
https://github.com/mjl-/mox.git
synced 2024-12-27 08:53:48 +03:00
implement the obsolete sasl login mechanism for smtp
so microsoft outlook "new" can login. that's the "new" email client that logs in from cloud servers.
This commit is contained in:
parent
c66fa64b8b
commit
91b7d3dda8
3 changed files with 76 additions and 3 deletions
29
sasl/sasl.go
29
sasl/sasl.go
|
@ -53,6 +53,35 @@ func (a *clientPlain) Next(fromServer []byte) (toServer []byte, last bool, rerr
|
|||
}
|
||||
}
|
||||
|
||||
type clientLogin struct {
|
||||
Username, Password string
|
||||
step int
|
||||
}
|
||||
|
||||
var _ Client = (*clientLogin)(nil)
|
||||
|
||||
// NewClientLogin returns a client for the obsolete SASL LOGIN authentication.
|
||||
// See https://datatracker.ietf.org/doc/html/draft-murchison-sasl-login-00
|
||||
func NewClientLogin(username, password string) Client {
|
||||
return &clientLogin{username, password, 0}
|
||||
}
|
||||
|
||||
func (a *clientLogin) Info() (name string, hasCleartextCredentials bool) {
|
||||
return "LOGIN", true
|
||||
}
|
||||
|
||||
func (a *clientLogin) Next(fromServer []byte) (toServer []byte, last bool, rerr error) {
|
||||
defer func() { a.step++ }()
|
||||
switch a.step {
|
||||
case 0:
|
||||
return []byte(a.Username), false, nil
|
||||
case 1:
|
||||
return []byte(a.Password), true, nil
|
||||
default:
|
||||
return nil, false, fmt.Errorf("invalid step %d", a.step)
|
||||
}
|
||||
}
|
||||
|
||||
type clientCRAMMD5 struct {
|
||||
Username, Password string
|
||||
step int
|
||||
|
|
|
@ -843,7 +843,7 @@ func (c *conn) cmdHello(p *parser, ehlo bool) {
|
|||
if c.submission {
|
||||
// ../rfc/4954:123
|
||||
if c.tls || !c.requireTLSForAuth {
|
||||
c.bwritelinef("250-AUTH SCRAM-SHA-256 SCRAM-SHA-1 CRAM-MD5 PLAIN")
|
||||
c.bwritelinef("250-AUTH SCRAM-SHA-256 SCRAM-SHA-1 CRAM-MD5 PLAIN LOGIN")
|
||||
} else {
|
||||
c.bwritelinef("250-AUTH ")
|
||||
}
|
||||
|
@ -949,8 +949,6 @@ func (c *conn) cmdAuth(p *parser) {
|
|||
}
|
||||
}()
|
||||
|
||||
// todo: implement "AUTH LOGIN"? it looks like PLAIN, but without the continuation. it is an obsolete sasl mechanism. an account in desktop outlook appears to go through the cloud, attempting to submit email only with unadvertised and AUTH LOGIN. it appears they don't know "plain".
|
||||
|
||||
// ../rfc/4954:699
|
||||
p.xspace()
|
||||
mech := p.xsaslMech()
|
||||
|
@ -1049,6 +1047,51 @@ func (c *conn) cmdAuth(p *parser) {
|
|||
// ../rfc/4954:276
|
||||
c.writecodeline(smtp.C235AuthSuccess, smtp.SePol7Other0, "nice", nil)
|
||||
|
||||
case "LOGIN":
|
||||
// LOGIN is obsoleted in favor of PLAIN, only implemented to support legacy
|
||||
// clients, see Internet-Draft (I-D):
|
||||
// https://datatracker.ietf.org/doc/html/draft-murchison-sasl-login-00
|
||||
|
||||
authVariant = "login"
|
||||
|
||||
// ../rfc/4954:343
|
||||
// ../rfc/4954:326
|
||||
if !c.tls && c.requireTLSForAuth {
|
||||
xsmtpUserErrorf(smtp.C538EncReqForAuth, smtp.SePol7EncReqForAuth11, "authentication requires tls")
|
||||
}
|
||||
|
||||
// Read user name. The I-D says the client should ignore the server challenge, we
|
||||
// send an empty one.
|
||||
// I-D says maximum length must be 64 bytes. We allow more, for long user names
|
||||
// (domains).
|
||||
username := string(xreadInitial())
|
||||
|
||||
// Again, client should ignore the challenge, we send the same as the example in
|
||||
// the I-D.
|
||||
c.writelinef("%d %s", smtp.C334ContinueAuth, base64.StdEncoding.EncodeToString([]byte("Password")))
|
||||
|
||||
// Password is in line in plain text, so hide it.
|
||||
defer c.xtrace(mlog.LevelTraceauth)()
|
||||
password := string(xreadContinuation())
|
||||
c.xtrace(mlog.LevelTrace) // Restore.
|
||||
|
||||
acc, err := store.OpenEmailAuth(username, password)
|
||||
if err != nil && errors.Is(err, store.ErrUnknownCredentials) {
|
||||
// ../rfc/4954:274
|
||||
authResult = "badcreds"
|
||||
c.log.Info("failed authentication attempt", mlog.Field("username", username), mlog.Field("remote", c.remoteIP))
|
||||
xsmtpUserErrorf(smtp.C535AuthBadCreds, smtp.SePol7AuthBadCreds8, "bad user/pass")
|
||||
}
|
||||
xcheckf(err, "verifying credentials")
|
||||
|
||||
authResult = "ok"
|
||||
c.authFailed = 0
|
||||
c.setSlow(false)
|
||||
c.account = acc
|
||||
c.username = username
|
||||
// ../rfc/4954:276
|
||||
c.writecodeline(smtp.C235AuthSuccess, smtp.SePol7Other0, "hello ancient smtp implementation", nil)
|
||||
|
||||
case "CRAM-MD5":
|
||||
authVariant = strings.ToLower(mech)
|
||||
|
||||
|
|
|
@ -256,6 +256,7 @@ func TestSubmission(t *testing.T) {
|
|||
testAuth(nil, "", "", &smtpclient.Error{Permanent: true, Code: smtp.C530SecurityRequired, Secode: smtp.SePol7Other0})
|
||||
authfns := []func(user, pass string) sasl.Client{
|
||||
sasl.NewClientPlain,
|
||||
sasl.NewClientLogin,
|
||||
sasl.NewClientCRAMMD5,
|
||||
sasl.NewClientSCRAMSHA1,
|
||||
sasl.NewClientSCRAMSHA256,
|
||||
|
|
Loading…
Reference in a new issue