Add Support for SMTP LOGIN Authentication Method (#1988)

This PR adds support for the SMTP LOGIN authentication method. Office 365 Exchange removed support for PLAIN authentication some time ago, and only supports LOGIN and an OAuth2 authentication method. This patch has been tested with a licensed O365 account. This method should also be usable with any other email server that advertises LOGIN in its 250-AUTH response.

Note: If using this with O365, the account used must not have MFA enabled.

Closes #1663
This commit is contained in:
James Thomas 2019-01-14 12:35:23 -08:00 committed by Zachary Wasserman
parent f7875dc19e
commit 5e5e4f3918
4 changed files with 39 additions and 0 deletions

View file

@ -15,6 +15,7 @@ import validate from 'components/forms/admin/AppConfigForm/validate';
const authMethodOptions = [
{ label: 'Plain', value: 'authmethod_plain' },
{ label: 'Cram MD5', value: 'authmethod_cram_md5' },
{ label: 'Login', value: 'authmethod_login' },
];
const authTypeOptions = [
{ label: 'Username and Password', value: 'authtype_username_password' },

View file

@ -30,6 +30,7 @@ type AppConfigService interface {
// SMTPAuthMethod
const (
AuthMethodNameCramMD5 = "authmethod_cram_md5"
AuthMethodNameLogin = "authmethod_login"
AuthMethodNamePlain = "authmethod_plain"
AuthTypeNameUserNamePassword = "authtype_username_password"
AuthTypeNameNone = "authtype_none"
@ -58,6 +59,7 @@ type SMTPAuthMethod int
const (
AuthMethodPlain SMTPAuthMethod = iota
AuthMethodCramMD5
AuthMethodLogin
)
func (m SMTPAuthMethod) String() string {
@ -66,6 +68,8 @@ func (m SMTPAuthMethod) String() string {
return AuthMethodNamePlain
case AuthMethodCramMD5:
return AuthMethodNameCramMD5
case AuthMethodLogin:
return AuthMethodNameLogin
default:
return ""
}

View file

@ -6,6 +6,7 @@ import (
"fmt"
"net"
"net/smtp"
"strings"
"time"
"github.com/kolide/fleet/server/kolide"
@ -70,6 +71,35 @@ func (m mailService) SendEmail(e kolide.Email) error {
return m.sendMail(e, msg)
}
type loginauth struct {
username string
password string
}
func LoginAuth(username, password string) smtp.Auth {
return &loginauth{username: username, password: password}
}
func (l *loginauth) Start(serverInfo *smtp.ServerInfo) (proto string, toServer []byte, err error) {
return "LOGIN", nil, nil
}
func (l *loginauth) Next(fromServer []byte, more bool) (toServer []byte, err error) {
if !more {
return nil, nil
}
prompt := strings.TrimSpace(string(fromServer))
switch prompt {
case "Username:":
return []byte(l.username), nil
case "Password:":
return []byte(l.password), nil
default:
return nil, errors.New("unexpected LOGIN prompt from server")
}
}
func smtpAuth(e kolide.Email) (smtp.Auth, error) {
if e.Config.SMTPAuthenticationType != kolide.AuthTypeUserNamePassword {
return nil, nil
@ -80,6 +110,8 @@ func smtpAuth(e kolide.Email) (smtp.Auth, error) {
auth = smtp.CRAMMD5Auth(e.Config.SMTPUserName, e.Config.SMTPPassword)
case kolide.AuthMethodPlain:
auth = smtp.PlainAuth("", e.Config.SMTPUserName, e.Config.SMTPPassword, e.Config.SMTPServer)
case kolide.AuthMethodLogin:
auth = LoginAuth(e.Config.SMTPUserName, e.Config.SMTPPassword)
default:
return nil, fmt.Errorf("unknown SMTP auth type '%d'", e.Config.SMTPAuthenticationMethod)
}

View file

@ -150,6 +150,8 @@ func appConfigFromAppConfigPayload(p kolide.AppConfigPayload, config kolide.AppC
config.SMTPAuthenticationMethod = kolide.AuthMethodCramMD5
case kolide.AuthMethodNamePlain:
config.SMTPAuthenticationMethod = kolide.AuthMethodPlain
case kolide.AuthMethodNameLogin:
config.SMTPAuthenticationMethod = kolide.AuthMethodLogin
default:
panic("unknown SMTP AuthMethod: " + *p.SMTPAuthenticationMethod)
}