1package dbus 2 3import ( 4 "bufio" 5 "bytes" 6 "crypto/rand" 7 "crypto/sha1" 8 "encoding/hex" 9 "os" 10) 11 12// AuthCookieSha1 returns an Auth that authenticates as the given user with the 13// DBUS_COOKIE_SHA1 mechanism. The home parameter should specify the home 14// directory of the user. 15func AuthCookieSha1(user, home string) Auth { 16 return authCookieSha1{user, home} 17} 18 19type authCookieSha1 struct { 20 user, home string 21} 22 23func (a authCookieSha1) FirstData() ([]byte, []byte, AuthStatus) { 24 b := make([]byte, 2*len(a.user)) 25 hex.Encode(b, []byte(a.user)) 26 return []byte("DBUS_COOKIE_SHA1"), b, AuthContinue 27} 28 29func (a authCookieSha1) HandleData(data []byte) ([]byte, AuthStatus) { 30 challenge := make([]byte, len(data)/2) 31 _, err := hex.Decode(challenge, data) 32 if err != nil { 33 return nil, AuthError 34 } 35 b := bytes.Split(challenge, []byte{' '}) 36 if len(b) != 3 { 37 return nil, AuthError 38 } 39 context := b[0] 40 id := b[1] 41 svchallenge := b[2] 42 cookie := a.getCookie(context, id) 43 if cookie == nil { 44 return nil, AuthError 45 } 46 clchallenge := a.generateChallenge() 47 if clchallenge == nil { 48 return nil, AuthError 49 } 50 hash := sha1.New() 51 hash.Write(bytes.Join([][]byte{svchallenge, clchallenge, cookie}, []byte{':'})) 52 hexhash := make([]byte, 2*hash.Size()) 53 hex.Encode(hexhash, hash.Sum(nil)) 54 data = append(clchallenge, ' ') 55 data = append(data, hexhash...) 56 resp := make([]byte, 2*len(data)) 57 hex.Encode(resp, data) 58 return resp, AuthOk 59} 60 61// getCookie searches for the cookie identified by id in context and returns 62// the cookie content or nil. (Since HandleData can't return a specific error, 63// but only whether an error occured, this function also doesn't bother to 64// return an error.) 65func (a authCookieSha1) getCookie(context, id []byte) []byte { 66 file, err := os.Open(a.home + "/.dbus-keyrings/" + string(context)) 67 if err != nil { 68 return nil 69 } 70 defer file.Close() 71 rd := bufio.NewReader(file) 72 for { 73 line, err := rd.ReadBytes('\n') 74 if err != nil { 75 return nil 76 } 77 line = line[:len(line)-1] 78 b := bytes.Split(line, []byte{' '}) 79 if len(b) != 3 { 80 return nil 81 } 82 if bytes.Equal(b[0], id) { 83 return b[2] 84 } 85 } 86} 87 88// generateChallenge returns a random, hex-encoded challenge, or nil on error 89// (see above). 90func (a authCookieSha1) generateChallenge() []byte { 91 b := make([]byte, 16) 92 n, err := rand.Read(b) 93 if err != nil { 94 return nil 95 } 96 if n != 16 { 97 return nil 98 } 99 enc := make([]byte, 32) 100 hex.Encode(enc, b) 101 return enc 102} 103