mirror of
https://github.com/wneessen/go-mail.git
synced 2024-11-15 02:12:55 +01:00
Add support for PLAIN authentication without encryption
Implemented a new SMTPAuthPlainNoEnc option to allow PLAIN authentication over unencrypted connections. Refactored the PlainAuth function to accept an additional allowUnencryptedAuth parameter. Updated relevant tests to cover the new authentication method.
This commit is contained in:
parent
91caf200ec
commit
f5531eae14
4 changed files with 27 additions and 7 deletions
14
auth.go
14
auth.go
|
@ -62,6 +62,20 @@ const (
|
||||||
// https://datatracker.ietf.org/doc/html/rfc4616/
|
// https://datatracker.ietf.org/doc/html/rfc4616/
|
||||||
SMTPAuthPlain SMTPAuthType = "PLAIN"
|
SMTPAuthPlain SMTPAuthType = "PLAIN"
|
||||||
|
|
||||||
|
// SMTPAuthPlainNoEnc is the "PLAIN" authentication mechanism as described in RFC 4616.
|
||||||
|
//
|
||||||
|
// Since the "PLAIN" SASL authentication mechanism transmits the username and password in
|
||||||
|
// plaintext over the internet connection, bye default we only allow this mechanism over
|
||||||
|
// a TLS secured connection. This authentiation mechanism overrides this default and will
|
||||||
|
// allow PLAIN authentication via an unencrypted channel. This can be useful if the
|
||||||
|
// connection has already been secured in a different way (e. g. a SSH tunnel)
|
||||||
|
//
|
||||||
|
// Note: Use this authentication method with caution. If used in the wrong way, you might
|
||||||
|
// expose your authentication information over unencrypted channels!
|
||||||
|
//
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc4616/
|
||||||
|
SMTPAuthPlainNoEnc SMTPAuthType = "PLAIN-NOENC"
|
||||||
|
|
||||||
// SMTPAuthXOAUTH2 is the "XOAUTH2" SASL authentication mechanism.
|
// SMTPAuthXOAUTH2 is the "XOAUTH2" SASL authentication mechanism.
|
||||||
// https://developers.google.com/gmail/imap/xoauth2-protocol
|
// https://developers.google.com/gmail/imap/xoauth2-protocol
|
||||||
SMTPAuthXOAUTH2 SMTPAuthType = "XOAUTH2"
|
SMTPAuthXOAUTH2 SMTPAuthType = "XOAUTH2"
|
||||||
|
|
|
@ -1096,7 +1096,12 @@ func (c *Client) auth() error {
|
||||||
if !strings.Contains(smtpAuthType, string(SMTPAuthPlain)) {
|
if !strings.Contains(smtpAuthType, string(SMTPAuthPlain)) {
|
||||||
return ErrPlainAuthNotSupported
|
return ErrPlainAuthNotSupported
|
||||||
}
|
}
|
||||||
c.smtpAuth = smtp.PlainAuth("", c.user, c.pass, c.host)
|
c.smtpAuth = smtp.PlainAuth("", c.user, c.pass, c.host, false)
|
||||||
|
case SMTPAuthPlainNoEnc:
|
||||||
|
if !strings.Contains(smtpAuthType, string(SMTPAuthPlain)) {
|
||||||
|
return ErrPlainAuthNotSupported
|
||||||
|
}
|
||||||
|
c.smtpAuth = smtp.PlainAuth("", c.user, c.pass, c.host, true)
|
||||||
case SMTPAuthLogin:
|
case SMTPAuthLogin:
|
||||||
if !strings.Contains(smtpAuthType, string(SMTPAuthLogin)) {
|
if !strings.Contains(smtpAuthType, string(SMTPAuthLogin)) {
|
||||||
return ErrLoginAuthNotSupported
|
return ErrLoginAuthNotSupported
|
||||||
|
|
|
@ -110,7 +110,7 @@ func TestNewClientWithOptions(t *testing.T) {
|
||||||
{"WithSMTPAuth()", WithSMTPAuth(SMTPAuthLogin), false},
|
{"WithSMTPAuth()", WithSMTPAuth(SMTPAuthLogin), false},
|
||||||
{
|
{
|
||||||
"WithSMTPAuthCustom()",
|
"WithSMTPAuthCustom()",
|
||||||
WithSMTPAuthCustom(smtp.PlainAuth("", "", "", "")),
|
WithSMTPAuthCustom(smtp.PlainAuth("", "", "", "", false)),
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{"WithUsername()", WithUsername("test"), false},
|
{"WithUsername()", WithUsername("test"), false},
|
||||||
|
@ -606,7 +606,7 @@ func TestSetSMTPAuthCustom(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{"SMTPAuth: CRAM-MD5", smtp.CRAMMD5Auth("", ""), "CRAM-MD5", false},
|
{"SMTPAuth: CRAM-MD5", smtp.CRAMMD5Auth("", ""), "CRAM-MD5", false},
|
||||||
{"SMTPAuth: LOGIN", smtp.LoginAuth("", "", ""), "LOGIN", false},
|
{"SMTPAuth: LOGIN", smtp.LoginAuth("", "", ""), "LOGIN", false},
|
||||||
{"SMTPAuth: PLAIN", smtp.PlainAuth("", "", "", ""), "PLAIN", false},
|
{"SMTPAuth: PLAIN", smtp.PlainAuth("", "", "", "", false), "PLAIN", false},
|
||||||
}
|
}
|
||||||
si := smtp.ServerInfo{TLS: true}
|
si := smtp.ServerInfo{TLS: true}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
@ -1227,7 +1227,7 @@ func TestClient_DialWithContext_switchAuth(t *testing.T) {
|
||||||
|
|
||||||
// We switch to CUSTOM by providing PLAIN auth as function - the server supports this
|
// We switch to CUSTOM by providing PLAIN auth as function - the server supports this
|
||||||
client.SetSMTPAuthCustom(smtp.PlainAuth("", os.Getenv("TEST_SMTPAUTH_USER"),
|
client.SetSMTPAuthCustom(smtp.PlainAuth("", os.Getenv("TEST_SMTPAUTH_USER"),
|
||||||
os.Getenv("TEST_SMTPAUTH_PASS"), os.Getenv("TEST_HOST")))
|
os.Getenv("TEST_SMTPAUTH_PASS"), os.Getenv("TEST_HOST"), false))
|
||||||
if client.smtpAuthType != SMTPAuthCustom {
|
if client.smtpAuthType != SMTPAuthCustom {
|
||||||
t.Errorf("expected auth type to be Custom, got: %s", client.smtpAuthType)
|
t.Errorf("expected auth type to be Custom, got: %s", client.smtpAuthType)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ package smtp
|
||||||
type plainAuth struct {
|
type plainAuth struct {
|
||||||
identity, username, password string
|
identity, username, password string
|
||||||
host string
|
host string
|
||||||
|
allowUnencryptedAuth bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// PlainAuth returns an [Auth] that implements the PLAIN authentication
|
// PlainAuth returns an [Auth] that implements the PLAIN authentication
|
||||||
|
@ -27,8 +28,8 @@ type plainAuth struct {
|
||||||
// PlainAuth will only send the credentials if the connection is using TLS
|
// PlainAuth will only send the credentials if the connection is using TLS
|
||||||
// or is connected to localhost. Otherwise authentication will fail with an
|
// or is connected to localhost. Otherwise authentication will fail with an
|
||||||
// error, without sending the credentials.
|
// error, without sending the credentials.
|
||||||
func PlainAuth(identity, username, password, host string) Auth {
|
func PlainAuth(identity, username, password, host string, allowUnEnc bool) Auth {
|
||||||
return &plainAuth{identity, username, password, host}
|
return &plainAuth{identity, username, password, host, allowUnEnc}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *plainAuth) Start(server *ServerInfo) (string, []byte, error) {
|
func (a *plainAuth) Start(server *ServerInfo) (string, []byte, error) {
|
||||||
|
@ -37,7 +38,7 @@ func (a *plainAuth) Start(server *ServerInfo) (string, []byte, error) {
|
||||||
// In particular, it doesn't matter if the server advertises PLAIN auth.
|
// In particular, it doesn't matter if the server advertises PLAIN auth.
|
||||||
// That might just be the attacker saying
|
// That might just be the attacker saying
|
||||||
// "it's ok, you can trust me with your password."
|
// "it's ok, you can trust me with your password."
|
||||||
if !server.TLS && !isLocalhost(server.Name) {
|
if !a.allowUnencryptedAuth && !server.TLS && !isLocalhost(server.Name) {
|
||||||
return "", nil, ErrUnencrypted
|
return "", nil, ErrUnencrypted
|
||||||
}
|
}
|
||||||
if server.Name != a.host {
|
if server.Name != a.host {
|
||||||
|
|
Loading…
Reference in a new issue