code.gitea.io/gitea@v1.21.7/services/auth/source/smtp/auth.go (about) 1 // Copyright 2021 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package smtp 5 6 import ( 7 "crypto/tls" 8 "errors" 9 "fmt" 10 "net" 11 "net/smtp" 12 "os" 13 "strconv" 14 ) 15 16 // _________ __________________________ 17 // / _____/ / \__ ___/\______ \ 18 // \_____ \ / \ / \| | | ___/ 19 // / \/ Y \ | | | 20 // /_______ /\____|__ /____| |____| 21 // \/ \/ 22 23 type loginAuthenticator struct { 24 username, password string 25 } 26 27 func (auth *loginAuthenticator) Start(server *smtp.ServerInfo) (string, []byte, error) { 28 return "LOGIN", []byte(auth.username), nil 29 } 30 31 func (auth *loginAuthenticator) Next(fromServer []byte, more bool) ([]byte, error) { 32 if more { 33 switch string(fromServer) { 34 case "Username:": 35 return []byte(auth.username), nil 36 case "Password:": 37 return []byte(auth.password), nil 38 } 39 } 40 return nil, nil 41 } 42 43 // SMTP authentication type names. 44 const ( 45 PlainAuthentication = "PLAIN" 46 LoginAuthentication = "LOGIN" 47 CRAMMD5Authentication = "CRAM-MD5" 48 ) 49 50 // Authenticators contains available SMTP authentication type names. 51 var Authenticators = []string{PlainAuthentication, LoginAuthentication, CRAMMD5Authentication} 52 53 // ErrUnsupportedLoginType login source is unknown error 54 var ErrUnsupportedLoginType = errors.New("Login source is unknown") 55 56 // Authenticate performs an SMTP authentication. 57 func Authenticate(a smtp.Auth, source *Source) error { 58 tlsConfig := &tls.Config{ 59 InsecureSkipVerify: source.SkipVerify, 60 ServerName: source.Host, 61 } 62 63 conn, err := net.Dial("tcp", net.JoinHostPort(source.Host, strconv.Itoa(source.Port))) 64 if err != nil { 65 return err 66 } 67 defer conn.Close() 68 69 if source.UseTLS() { 70 conn = tls.Client(conn, tlsConfig) 71 } 72 73 client, err := smtp.NewClient(conn, source.Host) 74 if err != nil { 75 return fmt.Errorf("failed to create NewClient: %w", err) 76 } 77 defer client.Close() 78 79 if !source.DisableHelo { 80 hostname := source.HeloHostname 81 if len(hostname) == 0 { 82 hostname, err = os.Hostname() 83 if err != nil { 84 return fmt.Errorf("failed to find Hostname: %w", err) 85 } 86 } 87 88 if err = client.Hello(hostname); err != nil { 89 return fmt.Errorf("failed to send Helo: %w", err) 90 } 91 } 92 93 // If not using SMTPS, always use STARTTLS if available 94 hasStartTLS, _ := client.Extension("STARTTLS") 95 if !source.UseTLS() && hasStartTLS { 96 if err = client.StartTLS(tlsConfig); err != nil { 97 return fmt.Errorf("failed to start StartTLS: %w", err) 98 } 99 } 100 101 if ok, _ := client.Extension("AUTH"); ok { 102 return client.Auth(a) 103 } 104 105 return ErrUnsupportedLoginType 106 }