Source file src/pkg/net/smtp/auth.go
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 return "", nil, errors.New("unencrypted connection") 58 } 59 if server.Name != a.host { 60 return "", nil, errors.New("wrong host name") 61 } 62 resp := []byte(a.identity + "\x00" + a.username + "\x00" + a.password) 63 return "PLAIN", resp, nil 64 } 65 66 func (a *plainAuth) Next(fromServer []byte, more bool) ([]byte, error) { 67 if more { 68 // We've already sent everything. 69 return nil, errors.New("unexpected server challenge") 70 } 71 return nil, nil 72 } 73 74 type cramMD5Auth struct { 75 username, secret string 76 } 77 78 // CRAMMD5Auth returns an Auth that implements the CRAM-MD5 authentication 79 // mechanism as defined in RFC 2195. 80 // The returned Auth uses the given username and secret to authenticate 81 // to the server using the challenge-response mechanism. 82 func CRAMMD5Auth(username, secret string) Auth { 83 return &cramMD5Auth{username, secret} 84 } 85 86 func (a *cramMD5Auth) Start(server *ServerInfo) (string, []byte, error) { 87 return "CRAM-MD5", nil, nil 88 } 89 90 func (a *cramMD5Auth) Next(fromServer []byte, more bool) ([]byte, error) { 91 if more { 92 d := hmac.New(md5.New, []byte(a.secret)) 93 d.Write(fromServer) 94 s := make([]byte, 0, d.Size()) 95 return []byte(fmt.Sprintf("%s %x", a.username, d.Sum(s))), nil 96 } 97 return nil, nil 98 }