summaryrefslogtreecommitdiffstats
path: root/services/auth/source/smtp/auth.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--services/auth/source/smtp/auth.go106
1 files changed, 106 insertions, 0 deletions
diff --git a/services/auth/source/smtp/auth.go b/services/auth/source/smtp/auth.go
new file mode 100644
index 0000000..6446fcd
--- /dev/null
+++ b/services/auth/source/smtp/auth.go
@@ -0,0 +1,106 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package smtp
+
+import (
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "net"
+ "net/smtp"
+ "os"
+ "strconv"
+)
+
+// _________ __________________________
+// / _____/ / \__ ___/\______ \
+// \_____ \ / \ / \| | | ___/
+// / \/ Y \ | | |
+// /_______ /\____|__ /____| |____|
+// \/ \/
+
+type loginAuthenticator struct {
+ username, password string
+}
+
+func (auth *loginAuthenticator) Start(server *smtp.ServerInfo) (string, []byte, error) {
+ return "LOGIN", []byte(auth.username), nil
+}
+
+func (auth *loginAuthenticator) Next(fromServer []byte, more bool) ([]byte, error) {
+ if more {
+ switch string(fromServer) {
+ case "Username:":
+ return []byte(auth.username), nil
+ case "Password:":
+ return []byte(auth.password), nil
+ }
+ }
+ return nil, nil
+}
+
+// SMTP authentication type names.
+const (
+ PlainAuthentication = "PLAIN"
+ LoginAuthentication = "LOGIN"
+ CRAMMD5Authentication = "CRAM-MD5"
+)
+
+// Authenticators contains available SMTP authentication type names.
+var Authenticators = []string{PlainAuthentication, LoginAuthentication, CRAMMD5Authentication}
+
+// ErrUnsupportedLoginType login source is unknown error
+var ErrUnsupportedLoginType = errors.New("Login source is unknown")
+
+// Authenticate performs an SMTP authentication.
+func Authenticate(a smtp.Auth, source *Source) error {
+ tlsConfig := &tls.Config{
+ InsecureSkipVerify: source.SkipVerify,
+ ServerName: source.Host,
+ }
+
+ conn, err := net.Dial("tcp", net.JoinHostPort(source.Host, strconv.Itoa(source.Port)))
+ if err != nil {
+ return err
+ }
+ defer conn.Close()
+
+ if source.UseTLS() {
+ conn = tls.Client(conn, tlsConfig)
+ }
+
+ client, err := smtp.NewClient(conn, source.Host)
+ if err != nil {
+ return fmt.Errorf("failed to create NewClient: %w", err)
+ }
+ defer client.Close()
+
+ if !source.DisableHelo {
+ hostname := source.HeloHostname
+ if len(hostname) == 0 {
+ hostname, err = os.Hostname()
+ if err != nil {
+ return fmt.Errorf("failed to find Hostname: %w", err)
+ }
+ }
+
+ if err = client.Hello(hostname); err != nil {
+ return fmt.Errorf("failed to send Helo: %w", err)
+ }
+ }
+
+ // If not using SMTPS, always use STARTTLS if available
+ hasStartTLS, _ := client.Extension("STARTTLS")
+ if !source.UseTLS() && hasStartTLS {
+ if err = client.StartTLS(tlsConfig); err != nil {
+ return fmt.Errorf("failed to start StartTLS: %w", err)
+ }
+ }
+
+ if ok, _ := client.Extension("AUTH"); ok {
+ return client.Auth(a)
+ }
+
+ return ErrUnsupportedLoginType
+}