| package dbus |
| |
| import ( |
| "bufio" |
| "bytes" |
| "crypto/rand" |
| "crypto/sha1" |
| "encoding/hex" |
| "os" |
| ) |
| |
| // AuthCookieSha1 returns an Auth that authenticates as the given user with the |
| // DBUS_COOKIE_SHA1 mechanism. The home parameter should specify the home |
| // directory of the user. |
| func AuthCookieSha1(user, home string) Auth { |
| return authCookieSha1{user, home} |
| } |
| |
| type authCookieSha1 struct { |
| user, home string |
| } |
| |
| func (a authCookieSha1) FirstData() ([]byte, []byte, AuthStatus) { |
| b := make([]byte, 2*len(a.user)) |
| hex.Encode(b, []byte(a.user)) |
| return []byte("DBUS_COOKIE_SHA1"), b, AuthContinue |
| } |
| |
| func (a authCookieSha1) HandleData(data []byte) ([]byte, AuthStatus) { |
| challenge := make([]byte, len(data)/2) |
| _, err := hex.Decode(challenge, data) |
| if err != nil { |
| return nil, AuthError |
| } |
| b := bytes.Split(challenge, []byte{' '}) |
| if len(b) != 3 { |
| return nil, AuthError |
| } |
| context := b[0] |
| id := b[1] |
| svchallenge := b[2] |
| cookie := a.getCookie(context, id) |
| if cookie == nil { |
| return nil, AuthError |
| } |
| clchallenge := a.generateChallenge() |
| if clchallenge == nil { |
| return nil, AuthError |
| } |
| hash := sha1.New() |
| hash.Write(bytes.Join([][]byte{svchallenge, clchallenge, cookie}, []byte{':'})) |
| hexhash := make([]byte, 2*hash.Size()) |
| hex.Encode(hexhash, hash.Sum(nil)) |
| data = append(clchallenge, ' ') |
| data = append(data, hexhash...) |
| resp := make([]byte, 2*len(data)) |
| hex.Encode(resp, data) |
| return resp, AuthOk |
| } |
| |
| // getCookie searches for the cookie identified by id in context and returns |
| // the cookie content or nil. (Since HandleData can't return a specific error, |
| // but only whether an error occured, this function also doesn't bother to |
| // return an error.) |
| func (a authCookieSha1) getCookie(context, id []byte) []byte { |
| file, err := os.Open(a.home + "/.dbus-keyrings/" + string(context)) |
| if err != nil { |
| return nil |
| } |
| defer file.Close() |
| rd := bufio.NewReader(file) |
| for { |
| line, err := rd.ReadBytes('\n') |
| if err != nil { |
| return nil |
| } |
| line = line[:len(line)-1] |
| b := bytes.Split(line, []byte{' '}) |
| if len(b) != 3 { |
| return nil |
| } |
| if bytes.Equal(b[0], id) { |
| return b[2] |
| } |
| } |
| } |
| |
| // generateChallenge returns a random, hex-encoded challenge, or nil on error |
| // (see above). |
| func (a authCookieSha1) generateChallenge() []byte { |
| b := make([]byte, 16) |
| n, err := rand.Read(b) |
| if err != nil { |
| return nil |
| } |
| if n != 16 { |
| return nil |
| } |
| enc := make([]byte, 32) |
| hex.Encode(enc, b) |
| return enc |
| } |