github.com/mdempsky/go@v0.0.0-20151201204031-5dd372bd1e70/src/net/smtp/auth.go (about) 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 the LICENSE file. 4 5 package smtp 6 7 import ( 8 "crypto/hmac" 9 "crypto/md5" 10 "errors" 11 "fmt" 12 ) 13 14 // Auth is implemented by an SMTP authentication mechanism. 15 type Auth interface { 16 // Start begins an authentication with a server. 17 // It returns the name of the authentication protocol 18 // and optionally data to include in the initial AUTH message 19 // sent to the server. It can return proto == "" to indicate 20 // that the authentication should be skipped. 21 // If it returns a non-nil error, the SMTP client aborts 22 // the authentication attempt and closes the connection. 23 Start(server *ServerInfo) (proto string, toServer []byte, err error) 24 25 // Next continues the authentication. The server has just sent 26 // the fromServer data. If more is true, the server expects a 27 // response, which Next should return as toServer; otherwise 28 // Next should return toServer == nil. 29 // If Next returns a non-nil error, the SMTP client aborts 30 // the authentication attempt and closes the connection. 31 Next(fromServer []byte, more bool) (toServer []byte, err error) 32 } 33 34 // ServerInfo records information about an SMTP server. 35 type ServerInfo struct { 36 Name string // SMTP server name 37 TLS bool // using TLS, with valid certificate for Name 38 Auth []string // advertised authentication mechanisms 39 } 40 41 type plainAuth struct { 42 identity, username, password string 43 host string 44 } 45 46 // PlainAuth returns an Auth that implements the PLAIN authentication 47 // mechanism as defined in RFC 4616. 48 // The returned Auth uses the given username and password to authenticate 49 // on TLS connections to host and act as identity. Usually identity will be 50 // left blank to act as username. 51 func PlainAuth(identity, username, password, host string) Auth { 52 return &plainAuth{identity, username, password, host} 53 } 54 55 func (a *plainAuth) Start(server *ServerInfo) (string, []byte, error) { 56 if !server.TLS { 57 advertised := false 58 for _, mechanism := range server.Auth { 59 if mechanism == "PLAIN" { 60 advertised = true 61 break 62 } 63 } 64 if !advertised { 65 return "", nil, errors.New("unencrypted connection") 66 } 67 } 68 if server.Name != a.host { 69 return "", nil, errors.New("wrong host name") 70 } 71 resp := []byte(a.identity + "\x00" + a.username + "\x00" + a.password) 72 return "PLAIN", resp, nil 73 } 74 75 func (a *plainAuth) Next(fromServer []byte, more bool) ([]byte, error) { 76 if more { 77 // We've already sent everything. 78 return nil, errors.New("unexpected server challenge") 79 } 80 return nil, nil 81 } 82 83 type cramMD5Auth struct { 84 username, secret string 85 } 86 87 // CRAMMD5Auth returns an Auth that implements the CRAM-MD5 authentication 88 // mechanism as defined in RFC 2195. 89 // The returned Auth uses the given username and secret to authenticate 90 // to the server using the challenge-response mechanism. 91 func CRAMMD5Auth(username, secret string) Auth { 92 return &cramMD5Auth{username, secret} 93 } 94 95 func (a *cramMD5Auth) Start(server *ServerInfo) (string, []byte, error) { 96 return "CRAM-MD5", nil, nil 97 } 98 99 func (a *cramMD5Auth) Next(fromServer []byte, more bool) ([]byte, error) { 100 if more { 101 d := hmac.New(md5.New, []byte(a.secret)) 102 d.Write(fromServer) 103 s := make([]byte, 0, d.Size()) 104 return []byte(fmt.Sprintf("%s %x", a.username, d.Sum(s))), nil 105 } 106 return nil, nil 107 }