12
// AuthStatus represents the Status of an authentication mechanism.
16
// AuthOk signals that authentication is finished; the next command
17
// from the server should be an OK.
18
AuthOk AuthStatus = iota
20
// AuthContinue signals that additional data is needed; the next command
21
// from the server should be a DATA.
24
// AuthError signals an error; the server sent invalid data or some
25
// other unexpected thing happened and the current authentication
26
// process should be aborted.
33
waitingForData authState = iota
38
// Auth defines the behaviour of an authentication mechanism.
40
// Return the name of the mechnism, the argument to the first AUTH command
41
// and the next status.
42
FirstData() (name, resp []byte, status AuthStatus)
44
// Process the given DATA command, and return the argument to the DATA
45
// command and the next status. If len(resp) == 0, no DATA command is sent.
46
HandleData(data []byte) (resp []byte, status AuthStatus)
49
// Auth authenticates the connection, trying the given list of authentication
50
// mechanisms (in that order). If nil is passed, the EXTERNAL and
51
// DBUS_COOKIE_SHA1 mechanisms are tried for the current user. For private
52
// connections, this method must be called before sending any messages to the
53
// bus. Auth must not be called on shared connections.
54
func (conn *Conn) Auth(methods []Auth) error {
56
uid := strconv.Itoa(os.Getuid())
57
methods = []Auth{AuthExternal(uid), AuthCookieSha1(uid, getHomeDir())}
59
in := bufio.NewReader(conn.transport)
60
err := conn.transport.SendNullByte()
64
err = authWriteLine(conn.transport, []byte("AUTH"))
68
s, err := authReadLine(in)
72
if len(s) < 2 || !bytes.Equal(s[0], []byte("REJECTED")) {
73
return errors.New("dbus: authentication protocol error")
77
for _, m := range methods {
78
if name, data, status := m.FirstData(); bytes.Equal(v, name) {
80
err = authWriteLine(conn.transport, []byte("AUTH"), []byte(v), data)
86
err, ok = conn.tryAuth(m, waitingForOk, in)
88
err, ok = conn.tryAuth(m, waitingForData, in)
90
panic("dbus: invalid authentication status")
96
if conn.transport.SupportsUnixFDs() {
97
err = authWriteLine(conn, []byte("NEGOTIATE_UNIX_FD"))
101
line, err := authReadLine(in)
106
case bytes.Equal(line[0], []byte("AGREE_UNIX_FD")):
109
case bytes.Equal(line[0], []byte("ERROR")):
111
return errors.New("dbus: authentication protocol error")
114
err = authWriteLine(conn.transport, []byte("BEGIN"))
125
return errors.New("dbus: authentication failed")
128
// tryAuth tries to authenticate with m as the mechanism, using state as the
129
// initial authState and in for reading input. It returns (nil, true) on
130
// success, (nil, false) on a REJECTED and (someErr, false) if some other
132
func (conn *Conn) tryAuth(m Auth, state authState, in *bufio.Reader) (error, bool) {
134
s, err := authReadLine(in)
139
case state == waitingForData && string(s[0]) == "DATA":
141
err = authWriteLine(conn.transport, []byte("ERROR"))
147
data, status := m.HandleData(s[1])
149
case AuthOk, AuthContinue:
151
err = authWriteLine(conn.transport, []byte("DATA"), data)
156
if status == AuthOk {
160
err = authWriteLine(conn.transport, []byte("ERROR"))
165
case state == waitingForData && string(s[0]) == "REJECTED":
167
case state == waitingForData && string(s[0]) == "ERROR":
168
err = authWriteLine(conn.transport, []byte("CANCEL"))
172
state = waitingForReject
173
case state == waitingForData && string(s[0]) == "OK":
175
err = authWriteLine(conn.transport, []byte("CANCEL"))
179
state = waitingForReject
181
conn.uuid = string(s[1])
183
case state == waitingForData:
184
err = authWriteLine(conn.transport, []byte("ERROR"))
188
case state == waitingForOk && string(s[0]) == "OK":
190
err = authWriteLine(conn.transport, []byte("CANCEL"))
194
state = waitingForReject
196
conn.uuid = string(s[1])
198
case state == waitingForOk && string(s[0]) == "REJECTED":
200
case state == waitingForOk && (string(s[0]) == "DATA" ||
201
string(s[0]) == "ERROR"):
203
err = authWriteLine(conn.transport, []byte("CANCEL"))
207
state = waitingForReject
208
case state == waitingForOk:
209
err = authWriteLine(conn.transport, []byte("ERROR"))
213
case state == waitingForReject && string(s[0]) == "REJECTED":
215
case state == waitingForReject:
216
return errors.New("dbus: authentication protocol error"), false
218
panic("dbus: invalid auth state")
223
// authReadLine reads a line and separates it into its fields.
224
func authReadLine(in *bufio.Reader) ([][]byte, error) {
225
data, err := in.ReadBytes('\n')
229
data = bytes.TrimSuffix(data, []byte("\r\n"))
230
return bytes.Split(data, []byte{' '}), nil
233
// authWriteLine writes the given line in the authentication protocol format
234
// (elements of data separated by a " " and terminated by "\r\n").
235
func authWriteLine(out io.Writer, data ...[]byte) error {
236
buf := make([]byte, 0)
237
for i, v := range data {
238
buf = append(buf, v...)
239
if i != len(data)-1 {
240
buf = append(buf, ' ')
243
buf = append(buf, '\r')
244
buf = append(buf, '\n')
245
n, err := out.Write(buf)
250
return io.ErrUnexpectedEOF