Compare commits

..

17 commits

Author SHA1 Message Date
Michael Fuchs
2d92fc0efe
Merge cc4c5bfd04 into cec7e38332 2024-10-24 10:38:27 +02:00
cec7e38332
Merge pull request #345 from wneessen/dependabot/github_actions/github/codeql-action-3.27.0
Bump github/codeql-action from 3.26.13 to 3.27.0
2024-10-23 15:32:09 +02:00
dependabot[bot]
9ad77012e3
Bump github/codeql-action from 3.26.13 to 3.27.0
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.26.13 to 3.27.0.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](f779452ac5...662472033e)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-23 13:16:56 +00:00
55e5a1536e
Merge pull request #344 from wneessen/feature/342_allow-bypass-plain-auth-tls-check
Allow unencrypted PLAIN and LOGIN smtp authentication
2024-10-22 17:39:04 +02:00
c7d0a03ddc
Change error log to debug log in client_test.go
Updated the log level from error to debug for the client.Close() call failure in client_test.go. This change helps reduce noise in test output when the server connection fails.
2024-10-22 16:21:09 +02:00
f79c1b8ebe
Add fig.StringUnmarshaler support for LOGIN-NOENC and PLAIN-NOENC authentication methods 2024-10-22 16:09:12 +02:00
df1a141368
Handle client close errors in SMTP tests
Update defer statements to log errors if client fails to close in smtp_test.go. Additionally, add a return statement to avoid further errors after a failed SendMail operation.
2024-10-22 16:02:43 +02:00
e2ed5b747a
Add tests for PlainAuth and LoginAuth without encryption
Introduced new test functions TestAuthPlainNoEnc and TestAuthLoginNoEnc in smtp_test.go to verify behaviors of PlainAuth and LoginAuth without TLS encryption. These tests ensure that authentication mechanisms handle non-encrypted and diverse server configurations correctly.
2024-10-22 15:50:18 +02:00
2bd950469a
Add 'skipTLS' parameter to auth functions in tests
Updated PlainAuth and LoginAuth calls in smtp_test.go and example_test.go to include a 'skipTLS' boolean parameter. This ensures consistent function signatures throughout the test cases and examples.
2024-10-22 15:44:40 +02:00
3c29f68cc1
Add support for unsecured SMTP LOGIN auth
Implemented an option to allow SMTP LOGIN authentication over unencrypted channels by introducing a new `SMTPAuthLoginNoEnc` type. Updated relevant functions and tests to handle the new parameter for unsecured authentication.
2024-10-22 15:38:51 +02:00
f5531eae14
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.
2024-10-22 15:30:15 +02:00
91caf200ec
Merge pull request #343 from wneessen/dependabot/github_actions/actions/dependency-review-action-4.3.5
Bump actions/dependency-review-action from 4.3.4 to 4.3.5
2024-10-22 15:13:07 +02:00
dependabot[bot]
63e6fc882d
Bump actions/dependency-review-action from 4.3.4 to 4.3.5
Bumps [actions/dependency-review-action](https://github.com/actions/dependency-review-action) from 4.3.4 to 4.3.5.
- [Release notes](https://github.com/actions/dependency-review-action/releases)
- [Commits](5a2ce3f5b9...a6993e2c61)

---
updated-dependencies:
- dependency-name: actions/dependency-review-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-22 13:10:46 +00:00
957cd8e0ca
Merge pull request #341 from wneessen/fig-support
Add fig.StringUnmarshaler support for SMTPAuthType
2024-10-21 22:51:46 +02:00
09133ef2a4
Add test for invalid SMTPAuthType
Introduce a new test case to verify that `UnmarshalString` fails when given an invalid string. This ensures the robustness of the error handling in the `SMTPAuthType` unmarshalling process.
2024-10-21 22:34:47 +02:00
bf44fd2ad1
Add SPDX license headers to auth_test.go
Included the SPDX license identifiers for copyright and license type at the beginning of the auth_test.go file. This ensures clear licensing information is provided and helps with automated license compliance.
2024-10-21 22:26:30 +02:00
7bebdda27c
Add fig.StringUnmarshaler support for SMTPAuthType
Implement the fig.StringUnmarshaler interface for SMTPAuthType to map strings to corresponding authentication types. Added comprehensive unit tests to ensure correct functionality for all supported auth type strings.
2024-10-21 22:24:21 +02:00
11 changed files with 291 additions and 40 deletions

View file

@ -54,7 +54,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13
uses: github/codeql-action/init@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@ -65,7 +65,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13
uses: github/codeql-action/autobuild@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@ -79,4 +79,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13
uses: github/codeql-action/analyze@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0

View file

@ -28,4 +28,4 @@ jobs:
- name: 'Checkout Repository'
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
- name: 'Dependency Review'
uses: actions/dependency-review-action@5a2ce3f5b92ee19cbb1541a4984c76d921601d7c # v4.3.4
uses: actions/dependency-review-action@a6993e2c61fd5dc440b409aa1d6904921c5e1894 # v4.3.5

View file

@ -75,6 +75,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13
uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0
with:
sarif_file: results.sarif

73
auth.go
View file

@ -4,7 +4,11 @@
package mail
import "errors"
import (
"errors"
"fmt"
"strings"
)
// SMTPAuthType is a type wrapper for a string type. It represents the type of SMTP authentication
// mechanism to be used.
@ -44,6 +48,25 @@ const (
// https://datatracker.ietf.org/doc/html/draft-murchison-sasl-login-00
SMTPAuthLogin SMTPAuthType = "LOGIN"
// SMTPAuthLoginNoEnc is the "LOGIN" SASL authentication mechanism. This authentication mechanism
// does not have an official RFC that could be followed. There is a spec by Microsoft and an
// IETF draft. The IETF draft is more lax than the MS spec, therefore we follow the I-D, which
// automatically matches the MS spec.
//
// Since the "LOGIN" SASL authentication mechanism transmits the username and password in
// plaintext over the internet connection, by default we only allow this mechanism over
// a TLS secured connection. This authentiation mechanism overrides this default and will
// allow LOGIN 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://msopenspecs.azureedge.net/files/MS-XLOGIN/%5bMS-XLOGIN%5d.pdf
//
// https://datatracker.ietf.org/doc/html/draft-murchison-sasl-login-00
SMTPAuthLoginNoEnc SMTPAuthType = "LOGIN-NOENC"
// SMTPAuthNoAuth is equivalent to performing no authentication at all. It is a convenience
// option and should not be used. Instead, for mail servers that do no support/require
// authentication, the Client should not be passed the WithSMTPAuth option at all.
@ -58,6 +81,20 @@ const (
// https://datatracker.ietf.org/doc/html/rfc4616/
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, by 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.
// https://developers.google.com/gmail/imap/xoauth2-protocol
SMTPAuthXOAUTH2 SMTPAuthType = "XOAUTH2"
@ -134,3 +171,37 @@ var (
// authentication type.
ErrSCRAMSHA256PLUSAuthNotSupported = errors.New("server does not support SMTP AUTH type: SCRAM-SHA-256-PLUS")
)
// UnmarshalString satisfies the fig.StringUnmarshaler interface for the SMTPAuthType type
// https://pkg.go.dev/github.com/kkyr/fig#StringUnmarshaler
func (sa *SMTPAuthType) UnmarshalString(value string) error {
switch strings.ToLower(value) {
case "cram-md5", "crammd5", "cram":
*sa = SMTPAuthCramMD5
case "custom":
*sa = SMTPAuthCustom
case "login":
*sa = SMTPAuthLogin
case "login-noenc":
*sa = SMTPAuthLoginNoEnc
case "none", "noauth", "no":
*sa = SMTPAuthNoAuth
case "plain":
*sa = SMTPAuthPlain
case "plain-noenc":
*sa = SMTPAuthPlainNoEnc
case "scram-sha-1", "scram-sha1", "scramsha1":
*sa = SMTPAuthSCRAMSHA1
case "scram-sha-1-plus", "scram-sha1-plus", "scramsha1plus":
*sa = SMTPAuthSCRAMSHA1PLUS
case "scram-sha-256", "scram-sha256", "scramsha256":
*sa = SMTPAuthSCRAMSHA256
case "scram-sha-256-plus", "scram-sha256-plus", "scramsha256plus":
*sa = SMTPAuthSCRAMSHA256PLUS
case "xoauth2", "oauth2":
*sa = SMTPAuthXOAUTH2
default:
return fmt.Errorf("unsupported SMTP auth type: %s", value)
}
return nil
}

59
auth_test.go Normal file
View file

@ -0,0 +1,59 @@
// SPDX-FileCopyrightText: 2024 The go-mail Authors
//
// SPDX-License-Identifier: MIT
package mail
import "testing"
func TestSMTPAuthType_UnmarshalString(t *testing.T) {
tests := []struct {
name string
authString string
expected SMTPAuthType
}{
{"CRAM-MD5: cram-md5", "cram-md5", SMTPAuthCramMD5},
{"CRAM-MD5: crammd5", "crammd5", SMTPAuthCramMD5},
{"CRAM-MD5: cram", "cram", SMTPAuthCramMD5},
{"CUSTOM", "custom", SMTPAuthCustom},
{"LOGIN", "login", SMTPAuthLogin},
{"LOGIN-NOENC", "login-noenc", SMTPAuthLoginNoEnc},
{"NONE: none", "none", SMTPAuthNoAuth},
{"NONE: noauth", "noauth", SMTPAuthNoAuth},
{"NONE: no", "no", SMTPAuthNoAuth},
{"PLAIN", "plain", SMTPAuthPlain},
{"PLAIN-NOENC", "plain-noenc", SMTPAuthPlainNoEnc},
{"SCRAM-SHA-1: scram-sha-1", "scram-sha-1", SMTPAuthSCRAMSHA1},
{"SCRAM-SHA-1: scram-sha1", "scram-sha1", SMTPAuthSCRAMSHA1},
{"SCRAM-SHA-1: scramsha1", "scramsha1", SMTPAuthSCRAMSHA1},
{"SCRAM-SHA-1-PLUS: scram-sha-1-plus", "scram-sha-1-plus", SMTPAuthSCRAMSHA1PLUS},
{"SCRAM-SHA-1-PLUS: scram-sha1-plus", "scram-sha1-plus", SMTPAuthSCRAMSHA1PLUS},
{"SCRAM-SHA-1-PLUS: scramsha1plus", "scramsha1plus", SMTPAuthSCRAMSHA1PLUS},
{"SCRAM-SHA-256: scram-sha-256", "scram-sha-256", SMTPAuthSCRAMSHA256},
{"SCRAM-SHA-256: scram-sha256", "scram-sha256", SMTPAuthSCRAMSHA256},
{"SCRAM-SHA-256: scramsha256", "scramsha256", SMTPAuthSCRAMSHA256},
{"SCRAM-SHA-256-PLUS: scram-sha-256-plus", "scram-sha-256-plus", SMTPAuthSCRAMSHA256PLUS},
{"SCRAM-SHA-256-PLUS: scram-sha256-plus", "scram-sha256-plus", SMTPAuthSCRAMSHA256PLUS},
{"SCRAM-SHA-256-PLUS: scramsha256plus", "scramsha256plus", SMTPAuthSCRAMSHA256PLUS},
{"XOAUTH2: xoauth2", "xoauth2", SMTPAuthXOAUTH2},
{"XOAUTH2: oauth2", "oauth2", SMTPAuthXOAUTH2},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var authType SMTPAuthType
if err := authType.UnmarshalString(tt.authString); err != nil {
t.Errorf("UnmarshalString() for type %s failed: %s", tt.authString, err)
}
if authType != tt.expected {
t.Errorf("UnmarshalString() for type %s failed: expected %s, got %s",
tt.authString, tt.expected, authType)
}
})
}
t.Run("should fail", func(t *testing.T) {
var authType SMTPAuthType
if err := authType.UnmarshalString("invalid"); err == nil {
t.Error("UnmarshalString() should have failed")
}
})
}

View file

@ -1096,12 +1096,22 @@ func (c *Client) auth() error {
if !strings.Contains(smtpAuthType, string(SMTPAuthPlain)) {
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:
if !strings.Contains(smtpAuthType, string(SMTPAuthLogin)) {
return ErrLoginAuthNotSupported
}
c.smtpAuth = smtp.LoginAuth(c.user, c.pass, c.host)
c.smtpAuth = smtp.LoginAuth(c.user, c.pass, c.host, false)
case SMTPAuthLoginNoEnc:
if !strings.Contains(smtpAuthType, string(SMTPAuthLogin)) {
return ErrLoginAuthNotSupported
}
c.smtpAuth = smtp.LoginAuth(c.user, c.pass, c.host, true)
case SMTPAuthCramMD5:
if !strings.Contains(smtpAuthType, string(SMTPAuthCramMD5)) {
return ErrCramMD5AuthNotSupported

View file

@ -110,7 +110,7 @@ func TestNewClientWithOptions(t *testing.T) {
{"WithSMTPAuth()", WithSMTPAuth(SMTPAuthLogin), false},
{
"WithSMTPAuthCustom()",
WithSMTPAuthCustom(smtp.PlainAuth("", "", "", "")),
WithSMTPAuthCustom(smtp.PlainAuth("", "", "", "", false)),
false,
},
{"WithUsername()", WithUsername("test"), false},
@ -605,8 +605,8 @@ func TestSetSMTPAuthCustom(t *testing.T) {
sf bool
}{
{"SMTPAuth: CRAM-MD5", smtp.CRAMMD5Auth("", ""), "CRAM-MD5", false},
{"SMTPAuth: LOGIN", smtp.LoginAuth("", "", ""), "LOGIN", false},
{"SMTPAuth: PLAIN", smtp.PlainAuth("", "", "", ""), "PLAIN", false},
{"SMTPAuth: LOGIN", smtp.LoginAuth("", "", "", false), "LOGIN", false},
{"SMTPAuth: PLAIN", smtp.PlainAuth("", "", "", "", false), "PLAIN", false},
}
si := smtp.ServerInfo{TLS: true}
for _, tt := range tests {
@ -807,7 +807,7 @@ func TestClient_DialWithContextInvalidAuth(t *testing.T) {
}
c.user = "invalid"
c.pass = "invalid"
c.SetSMTPAuthCustom(smtp.LoginAuth("invalid", "invalid", "invalid"))
c.SetSMTPAuthCustom(smtp.LoginAuth("invalid", "invalid", "invalid", false))
ctx := context.Background()
if err = c.DialWithContext(ctx); err == nil {
t.Errorf("dial succeeded but was supposed to fail")
@ -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
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 {
t.Errorf("expected auth type to be Custom, got: %s", client.smtpAuthType)
}
@ -1955,7 +1955,7 @@ func TestClient_DialSendConcurrent_local(t *testing.T) {
wg.Wait()
if err = client.Close(); err != nil {
t.Errorf("failed to close server connection: %s", err)
t.Logf("failed to close server connection: %s", err)
}
}

View file

@ -10,9 +10,10 @@ import (
// loginAuth is the type that satisfies the Auth interface for the "SMTP LOGIN" auth
type loginAuth struct {
username, password string
host string
respStep uint8
username, password string
host string
respStep uint8
allowUnencryptedAuth bool
}
// LoginAuth returns an [Auth] that implements the LOGIN authentication
@ -35,8 +36,8 @@ type loginAuth struct {
// LoginAuth will only send the credentials if the connection is using TLS
// or is connected to localhost. Otherwise authentication will fail with an
// error, without sending the credentials.
func LoginAuth(username, password, host string) Auth {
return &loginAuth{username, password, host, 0}
func LoginAuth(username, password, host string, allowUnEnc bool) Auth {
return &loginAuth{username, password, host, 0, allowUnEnc}
}
// Start begins the SMTP authentication process by validating server's TLS status and hostname.
@ -47,7 +48,7 @@ func (a *loginAuth) Start(server *ServerInfo) (string, []byte, error) {
// In particular, it doesn't matter if the server advertises LOGIN auth.
// That might just be the attacker saying
// "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
}
if server.Name != a.host {

View file

@ -17,6 +17,7 @@ package smtp
type plainAuth struct {
identity, username, password string
host string
allowUnencryptedAuth bool
}
// 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
// or is connected to localhost. Otherwise authentication will fail with an
// error, without sending the credentials.
func PlainAuth(identity, username, password, host string) Auth {
return &plainAuth{identity, username, password, host}
func PlainAuth(identity, username, password, host string, allowUnEnc bool) Auth {
return &plainAuth{identity, username, password, host, allowUnEnc}
}
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.
// That might just be the attacker saying
// "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
}
if server.Name != a.host {

View file

@ -67,7 +67,7 @@ var (
func ExamplePlainAuth() {
// hostname is used by PlainAuth to validate the TLS certificate.
hostname := "mail.example.com"
auth := smtp.PlainAuth("", "user@example.com", "password", hostname)
auth := smtp.PlainAuth("", "user@example.com", "password", hostname, false)
err := smtp.SendMail(hostname+":25", auth, from, recipients, msg)
if err != nil {
@ -77,7 +77,7 @@ func ExamplePlainAuth() {
func ExampleSendMail() {
// Set up authentication information.
auth := smtp.PlainAuth("", "user@example.com", "password", "mail.example.com")
auth := smtp.PlainAuth("", "user@example.com", "password", "mail.example.com", false)
// Connect to the server, authenticate, set the sender and recipient,
// and send the email all in one step.

View file

@ -50,7 +50,7 @@ type authTest struct {
var authTests = []authTest{
{
PlainAuth("", "user", "pass", "testserver"),
PlainAuth("", "user", "pass", "testserver", false),
[]string{},
"PLAIN",
[]string{"\x00user\x00pass"},
@ -58,7 +58,15 @@ var authTests = []authTest{
false,
},
{
PlainAuth("foo", "bar", "baz", "testserver"),
PlainAuth("", "user", "pass", "testserver", true),
[]string{},
"PLAIN",
[]string{"\x00user\x00pass"},
[]bool{false, false},
false,
},
{
PlainAuth("foo", "bar", "baz", "testserver", false),
[]string{},
"PLAIN",
[]string{"foo\x00bar\x00baz"},
@ -66,7 +74,7 @@ var authTests = []authTest{
false,
},
{
PlainAuth("foo", "bar", "baz", "testserver"),
PlainAuth("foo", "bar", "baz", "testserver", false),
[]string{"foo"},
"PLAIN",
[]string{"foo\x00bar\x00baz", ""},
@ -74,7 +82,7 @@ var authTests = []authTest{
false,
},
{
LoginAuth("user", "pass", "testserver"),
LoginAuth("user", "pass", "testserver", false),
[]string{"Username:", "Password:"},
"LOGIN",
[]string{"", "user", "pass"},
@ -82,7 +90,15 @@ var authTests = []authTest{
false,
},
{
LoginAuth("user", "pass", "testserver"),
LoginAuth("user", "pass", "testserver", true),
[]string{"Username:", "Password:"},
"LOGIN",
[]string{"", "user", "pass"},
[]bool{false, false},
false,
},
{
LoginAuth("user", "pass", "testserver", false),
[]string{"User Name\x00", "Password\x00"},
"LOGIN",
[]string{"", "user", "pass"},
@ -90,7 +106,7 @@ var authTests = []authTest{
false,
},
{
LoginAuth("user", "pass", "testserver"),
LoginAuth("user", "pass", "testserver", false),
[]string{"Invalid", "Invalid:"},
"LOGIN",
[]string{"", "user", "pass"},
@ -98,7 +114,7 @@ var authTests = []authTest{
false,
},
{
LoginAuth("user", "pass", "testserver"),
LoginAuth("user", "pass", "testserver", false),
[]string{"Invalid", "Invalid:", "Too many"},
"LOGIN",
[]string{"", "user", "pass", ""},
@ -237,7 +253,47 @@ func TestAuthPlain(t *testing.T) {
},
}
for i, tt := range tests {
auth := PlainAuth("foo", "bar", "baz", tt.authName)
auth := PlainAuth("foo", "bar", "baz", tt.authName, false)
_, _, err := auth.Start(tt.server)
got := ""
if err != nil {
got = err.Error()
}
if got != tt.err {
t.Errorf("%d. got error = %q; want %q", i, got, tt.err)
}
}
}
func TestAuthPlainNoEnc(t *testing.T) {
tests := []struct {
authName string
server *ServerInfo
err string
}{
{
authName: "servername",
server: &ServerInfo{Name: "servername", TLS: true},
},
{
// OK to use PlainAuth on localhost without TLS
authName: "localhost",
server: &ServerInfo{Name: "localhost", TLS: false},
},
{
// Also OK on non-TLS secured connections. The NoEnc mechanism is meant to allow
// non-encrypted connections.
authName: "servername",
server: &ServerInfo{Name: "servername", Auth: []string{"PLAIN"}},
},
{
authName: "servername",
server: &ServerInfo{Name: "attacker", TLS: true},
err: "wrong host name",
},
}
for i, tt := range tests {
auth := PlainAuth("foo", "bar", "baz", tt.authName, true)
_, _, err := auth.Start(tt.server)
got := ""
if err != nil {
@ -283,7 +339,51 @@ func TestAuthLogin(t *testing.T) {
},
}
for i, tt := range tests {
auth := LoginAuth("foo", "bar", tt.authName)
auth := LoginAuth("foo", "bar", tt.authName, false)
_, _, err := auth.Start(tt.server)
got := ""
if err != nil {
got = err.Error()
}
if got != tt.err {
t.Errorf("%d. got error = %q; want %q", i, got, tt.err)
}
}
}
func TestAuthLoginNoEnc(t *testing.T) {
tests := []struct {
authName string
server *ServerInfo
err string
}{
{
authName: "servername",
server: &ServerInfo{Name: "servername", TLS: true},
},
{
// OK to use LoginAuth on localhost without TLS
authName: "localhost",
server: &ServerInfo{Name: "localhost", TLS: false},
},
{
// Also OK on non-TLS secured connections. The NoEnc mechanism is meant to allow
// non-encrypted connections.
authName: "servername",
server: &ServerInfo{Name: "servername", Auth: []string{"LOGIN"}},
},
{
authName: "servername",
server: &ServerInfo{Name: "servername", Auth: []string{"CRAM-MD5"}},
},
{
authName: "servername",
server: &ServerInfo{Name: "attacker", TLS: true},
err: "wrong host name",
},
}
for i, tt := range tests {
auth := LoginAuth("foo", "bar", tt.authName, true)
_, _, err := auth.Start(tt.server)
got := ""
if err != nil {
@ -317,7 +417,11 @@ func TestXOAuth2OK(t *testing.T) {
if err != nil {
t.Fatalf("NewClient: %v", err)
}
defer c.Close()
defer func() {
if err := c.Close(); err != nil {
t.Errorf("failed to close client: %s", err)
}
}()
auth := XOAuth2Auth("user", "token")
err = c.Auth(auth)
@ -355,7 +459,11 @@ func TestXOAuth2Error(t *testing.T) {
if err != nil {
t.Fatalf("NewClient: %v", err)
}
defer c.Close()
defer func() {
if err := c.Close(); err != nil {
t.Errorf("failed to close client: %s", err)
}
}()
auth := XOAuth2Auth("user", "token")
err = c.Auth(auth)
@ -707,7 +815,7 @@ func TestBasic(t *testing.T) {
// fake TLS so authentication won't complain
c.tls = true
c.serverName = "smtp.google.com"
if err := c.Auth(PlainAuth("", "user", "pass", "smtp.google.com")); err != nil {
if err := c.Auth(PlainAuth("", "user", "pass", "smtp.google.com", false)); err != nil {
t.Fatalf("AUTH failed: %s", err)
}
@ -1278,7 +1386,7 @@ func TestHello(t *testing.T) {
case 3:
c.tls = true
c.serverName = "smtp.google.com"
err = c.Auth(PlainAuth("", "user", "pass", "smtp.google.com"))
err = c.Auth(PlainAuth("", "user", "pass", "smtp.google.com", false))
case 4:
err = c.Mail("test@example.com")
case 5:
@ -1523,7 +1631,7 @@ func TestSendMailWithAuth(t *testing.T) {
}
}()
err = SendMail(l.Addr().String(), PlainAuth("", "user", "pass", "smtp.google.com"), "test@example.com", []string{"other@example.com"}, []byte(strings.Replace(`From: test@example.com
err = SendMail(l.Addr().String(), PlainAuth("", "user", "pass", "smtp.google.com", false), "test@example.com", []string{"other@example.com"}, []byte(strings.Replace(`From: test@example.com
To: other@example.com
Subject: SendMail test
@ -1531,6 +1639,7 @@ SendMail is working for me.
`, "\n", "\r\n", -1)))
if err == nil {
t.Error("SendMail: Server doesn't support AUTH, expected to get an error, but got none ")
return
}
if err.Error() != "smtp: server doesn't support AUTH" {
t.Errorf("Expected: smtp: server doesn't support AUTH, got: %s", err)
@ -1558,7 +1667,7 @@ func TestAuthFailed(t *testing.T) {
c.tls = true
c.serverName = "smtp.google.com"
err = c.Auth(PlainAuth("", "user", "pass", "smtp.google.com"))
err = c.Auth(PlainAuth("", "user", "pass", "smtp.google.com", false))
if err == nil {
t.Error("Auth: expected error; got none")