go-mail/smtp/auth_login.go
Winni Neessen 0d6777ab39
Fork net/smpt into go-mail
As part of #97 we are going to fork the official `net/smtp` package into go-mail to provide us with more flexibility.

This commit fulfills the first big step of importing the package into smtp/. Also go-mail's own LoginAuth has been moved from auth/ into smtp/ to be consistent with the stdlib.

There are still a couple of open issues (i. e. license adjustments and making golangci-lint happy) but so far all tests already work, which is a good start.
2023-01-10 00:38:42 +01:00

69 lines
2.2 KiB
Go

// SPDX-FileCopyrightText: 2022 Winni Neessen <winni@neessen.dev>
//
// SPDX-License-Identifier: MIT
// Package auth implements the LOGIN and MD5-DIGEST smtp authentication mechanisms
package smtp
import (
"errors"
"fmt"
)
// loginAuth is the type that satisfies the Auth interface for the "SMTP LOGIN" auth
type loginAuth struct {
username, password string
host string
}
const (
// ServerRespUsername represents the "Username:" response by the SMTP server
ServerRespUsername = "Username:"
// ServerRespPassword represents the "Password:" response by the SMTP server
ServerRespPassword = "Password:"
)
// LoginAuth returns an Auth that implements the LOGIN authentication
// mechanism as it is used by MS Outlook. The Auth works similar to PLAIN
// but instead of sending all in one response, the login is handled within
// 3 steps:
// - Sending AUTH LOGIN (server responds with "Username:")
// - Sending the username (server responds with "Password:")
// - Sending the password (server authenticates)
//
// 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}
}
func (a *loginAuth) Start(server *ServerInfo) (string, []byte, error) {
// Must have TLS, or else localhost server.
// Note: If TLS is not true, then we can't trust ANYTHING in ServerInfo.
// 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) {
return "", nil, errors.New("unencrypted connection")
}
if server.Name != a.host {
return "", nil, errors.New("wrong host name")
}
return "LOGIN", nil, nil
}
func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
if more {
switch string(fromServer) {
case ServerRespUsername:
return []byte(a.username), nil
case ServerRespPassword:
return []byte(a.password), nil
default:
return nil, fmt.Errorf("unexpected server response: %s", string(fromServer))
}
}
return nil, nil
}