auth.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. // Copyright 2010 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in https://raw.githubusercontent.com/golang/go/master/LICENSE
  4. // auth.go file is a modification of smtp golang package what is frozen and is not accepting new features.
  5. package mail
  6. import (
  7. "crypto/hmac"
  8. "crypto/md5"
  9. "errors"
  10. "fmt"
  11. "strings"
  12. )
  13. // auth is implemented by an SMTP authentication mechanism.
  14. type auth interface {
  15. // start begins an authentication with a server.
  16. // It returns the name of the authentication protocol
  17. // and optionally data to include in the initial AUTH message
  18. // sent to the server. It can return proto == "" to indicate
  19. // that the authentication should be skipped.
  20. // If it returns a non-nil error, the SMTP client aborts
  21. // the authentication attempt and closes the connection.
  22. start(server *serverInfo) (proto string, toServer []byte, err error)
  23. // next continues the authentication. The server has just sent
  24. // the fromServer data. If more is true, the server expects a
  25. // response, which next should return as toServer; otherwise
  26. // next should return toServer == nil.
  27. // If next returns a non-nil error, the SMTP client aborts
  28. // the authentication attempt and closes the connection.
  29. next(fromServer []byte, more bool) (toServer []byte, err error)
  30. }
  31. // serverInfo records information about an SMTP server.
  32. type serverInfo struct {
  33. name string // SMTP server name
  34. tls bool // using TLS, with valid certificate for Name
  35. auth []string // advertised authentication mechanisms
  36. }
  37. type plainAuth struct {
  38. identity, username, password string
  39. host string
  40. }
  41. // plainAuthfn returns an auth that implements the PLAIN authentication
  42. // mechanism as defined in RFC 4616. The returned Auth uses the given
  43. // username and password to authenticate to host and act as identity.
  44. // Usually identity should be the empty string, to act as username.
  45. //
  46. // plainAuthfn will only send the credentials if the connection is using TLS
  47. // or is connected to localhost. Otherwise authentication will fail with an
  48. // error, without sending the credentials.
  49. func plainAuthfn(identity, username, password, host string) auth {
  50. return &plainAuth{identity, username, password, host}
  51. }
  52. func (a *plainAuth) start(server *serverInfo) (string, []byte, error) {
  53. // Must have TLS, or else localhost server. Unencrypted connection is permitted here too but is not recommended
  54. // Note: If TLS is not true, then we can't trust ANYTHING in serverInfo.
  55. // In particular, it doesn't matter if the server advertises PLAIN auth.
  56. // That might just be the attacker saying
  57. // "it's ok, you can trust me with your password."
  58. if server.name != a.host {
  59. return "", nil, errors.New("wrong host name")
  60. }
  61. resp := []byte(a.identity + "\x00" + a.username + "\x00" + a.password)
  62. return "PLAIN", resp, nil
  63. }
  64. func (a *plainAuth) next(fromServer []byte, more bool) ([]byte, error) {
  65. if more {
  66. // We've already sent everything.
  67. return nil, errors.New("unexpected server challenge")
  68. }
  69. return nil, nil
  70. }
  71. /*
  72. loginAuthfn authentication implements LOGIN Authentication, is the same PLAIN
  73. but username and password are sent in different commands
  74. */
  75. type loginAuth struct {
  76. identity, username, password string
  77. host string
  78. }
  79. func loginAuthfn(identity, username, password, host string) auth {
  80. return &loginAuth{identity, username, password, host}
  81. }
  82. func (a *loginAuth) start(server *serverInfo) (string, []byte, error) {
  83. if server.name != a.host {
  84. return "", nil, errors.New("wrong host name")
  85. }
  86. resp := []byte(a.username)
  87. return "LOGIN", resp, nil
  88. }
  89. func (a *loginAuth) next(fromServer []byte, more bool) ([]byte, error) {
  90. if more {
  91. if strings.Contains(string(fromServer), "Username") {
  92. resp := []byte(a.username)
  93. return resp, nil
  94. }
  95. if strings.Contains(string(fromServer), "Password") {
  96. resp := []byte(a.password)
  97. return resp, nil
  98. }
  99. // We've already sent everything.
  100. return nil, errors.New("unexpected server challenge")
  101. }
  102. return nil, nil
  103. }
  104. type cramMD5Auth struct {
  105. username, secret string
  106. }
  107. // cramMD5Authfn returns an Auth that implements the CRAM-MD5 authentication
  108. // mechanism as defined in RFC 2195.
  109. // The returned Auth uses the given username and secret to authenticate
  110. // to the server using the challenge-response mechanism.
  111. func cramMD5Authfn(username, secret string) auth {
  112. return &cramMD5Auth{username, secret}
  113. }
  114. func (a *cramMD5Auth) start(server *serverInfo) (string, []byte, error) {
  115. return "CRAM-MD5", nil, nil
  116. }
  117. func (a *cramMD5Auth) next(fromServer []byte, more bool) ([]byte, error) {
  118. if more {
  119. d := hmac.New(md5.New, []byte(a.secret))
  120. d.Write(fromServer)
  121. s := make([]byte, 0, d.Size())
  122. return []byte(fmt.Sprintf("%s %x", a.username, d.Sum(s))), nil
  123. }
  124. return nil, nil
  125. }