Skip to content

Commit 117251b

Browse files
committed
Seperate private and shared connections
This mirrors the API of libdbus. Shared connections as returned by SessionBus and SystemBus are already registered and authenticated, and should be used in most cases. For private connections obtained with Dial or NewConn, authentication can be controlled. As a result, Auth and Hello are exported, and Auth can be passed a list of auth mechanisms to try. The interface for authentication has changed as well. Also, the new functions {Session,System}BusPrivate return private connections to the respective message bus. Updates #18.
1 parent 18c531d commit 117251b

File tree

5 files changed

+165
-107
lines changed

5 files changed

+165
-107
lines changed

auth.go

+61-51
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"bytes"
66
"errors"
77
"io"
8+
"os/user"
89
)
910

1011
// AuthStatus represents the Status of an authentication mechanism.
@@ -30,26 +31,30 @@ const (
3031
waitingForReject
3132
)
3233

33-
// AuthMechanisms lists all authentication mechanisms that are tried. To
34-
// implement your own mechanism, just add it to this map before connecting. The
35-
// key must be the name that is used for the AUTH command.
36-
var AuthMechanisms = map[string]AuthMechanism{
37-
"DBUS_COOKIE_SHA1": AuthCookieSha1{},
38-
"EXTERNAL": AuthExternal{},
39-
}
40-
41-
// AuthMechanism defines the behaviour of an authentication mechanism.
42-
type AuthMechanism interface {
43-
// Return the argument to the first AUTH command and the next status.
44-
FirstData() (resp []byte, status AuthStatus)
34+
// Auth defines the behaviour of an authentication mechanism.
35+
type Auth interface {
36+
// Return the name of the mechnism, the argument to the first AUTH command
37+
// and the next status.
38+
FirstData() (name, resp []byte, status AuthStatus)
4539

4640
// Process the given DATA command, and return the argument to the DATA
4741
// command and the next status. If len(resp) == 0, no DATA command is sent.
4842
HandleData(data []byte) (resp []byte, status AuthStatus)
4943
}
5044

51-
// auth does the whole authentication stuff.
52-
func (conn *Conn) auth() error {
45+
// Auth authenticates the connection, trying the given list of authentication
46+
// mechanisms (in that order). If nil is passed, the EXTERNAL and
47+
// DBUS_COOKIE_SHA1 mechanisms are tried for the current user. For private
48+
// connections, this method must be called before sending any messages to the
49+
// bus. Auth must not be called on shared connections.
50+
func (conn *Conn) Auth(methods []Auth) error {
51+
if methods == nil {
52+
u, err := user.Current()
53+
if err != nil {
54+
return err
55+
}
56+
methods = []Auth{AuthExternal(u.Username), AuthCookieSha1(u.Username, u.HomeDir)}
57+
}
5358
in := bufio.NewReader(conn.transport)
5459
_, err := conn.transport.Write([]byte{0})
5560
if err != nil {
@@ -68,47 +73,52 @@ func (conn *Conn) auth() error {
6873
}
6974
s = s[1:]
7075
for _, v := range s {
71-
if m, ok := AuthMechanisms[string(v)]; ok {
72-
data, status := m.FirstData()
73-
err = authWriteLine(conn.transport, []byte("AUTH"), []byte(v), data)
74-
if err != nil {
75-
return err
76-
}
77-
switch status {
78-
case AuthOk:
79-
err, ok = conn.tryAuth(m, waitingForOk, in)
80-
case AuthContinue:
81-
err, ok = conn.tryAuth(m, waitingForData, in)
82-
default:
83-
panic("invalid authentication status")
84-
}
85-
if err != nil {
86-
return err
87-
}
88-
if ok {
89-
if conn.transport.SupportsUnixFDs() {
90-
err = authWriteLine(conn, []byte("NEGOTIATE_UNIX_FD"))
91-
if err != nil {
92-
return err
76+
for _, m := range methods {
77+
if name, data, status := m.FirstData(); bytes.Equal(v, name) {
78+
var ok bool
79+
err = authWriteLine(conn.transport, []byte("AUTH"), []byte(v), data)
80+
if err != nil {
81+
return err
82+
}
83+
switch status {
84+
case AuthOk:
85+
err, ok = conn.tryAuth(m, waitingForOk, in)
86+
case AuthContinue:
87+
err, ok = conn.tryAuth(m, waitingForData, in)
88+
default:
89+
panic("invalid authentication status")
90+
}
91+
if err != nil {
92+
return err
93+
}
94+
if ok {
95+
if conn.transport.SupportsUnixFDs() {
96+
err = authWriteLine(conn, []byte("NEGOTIATE_UNIX_FD"))
97+
if err != nil {
98+
return err
99+
}
100+
line, err := authReadLine(in)
101+
if err != nil {
102+
return err
103+
}
104+
switch {
105+
case bytes.Equal(line[0], []byte("AGREE_UNIX_FD")):
106+
conn.EnableUnixFDs()
107+
conn.unixFD = true
108+
case bytes.Equal(line[0], []byte("ERROR")):
109+
default:
110+
return errors.New("authentication protocol error")
111+
}
93112
}
94-
line, err := authReadLine(in)
113+
err = authWriteLine(conn.transport, []byte("BEGIN"))
95114
if err != nil {
96115
return err
97116
}
98-
switch {
99-
case bytes.Equal(line[0], []byte("AGREE_UNIX_FD")):
100-
conn.EnableUnixFDs()
101-
conn.unixFD = true
102-
case bytes.Equal(line[0], []byte("ERROR")):
103-
default:
104-
return errors.New("authentication protocol error")
105-
}
106-
}
107-
err = authWriteLine(conn.transport, []byte("BEGIN"))
108-
if err != nil {
109-
return err
117+
go conn.inWorker()
118+
go conn.outWorker()
119+
go conn.serials()
120+
return nil
110121
}
111-
return nil
112122
}
113123
}
114124
}
@@ -119,7 +129,7 @@ func (conn *Conn) auth() error {
119129
// initial authState and in for reading input. It returns (nil, true) on
120130
// success, (nil, false) on a REJECTED and (someErr, false) if some other
121131
// error occured.
122-
func (conn *Conn) tryAuth(m AuthMechanism, state authState, in *bufio.Reader) (error, bool) {
132+
func (conn *Conn) tryAuth(m Auth, state authState, in *bufio.Reader) (error, bool) {
123133
for {
124134
s, err := authReadLine(in)
125135
if err != nil {

auth_external.go

+14-11
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,25 @@ package dbus
22

33
import (
44
"encoding/hex"
5-
"os/user"
65
)
76

7+
// AuthExternal returns an Auth that authenticates as the given user with the
8+
// EXTERNAL mechanism.
9+
func AuthExternal(user string) Auth {
10+
return authExternal{user}
11+
}
12+
813
// AuthExternal implements the EXTERNAL authentication mechanism.
9-
type AuthExternal struct{}
14+
type authExternal struct {
15+
user string
16+
}
1017

11-
func (a AuthExternal) FirstData() ([]byte, AuthStatus) {
12-
u, err := user.Current()
13-
if err != nil {
14-
return nil, AuthError
15-
}
16-
b := make([]byte, 2*len(u.Username))
17-
hex.Encode(b, []byte(u.Username))
18-
return b, AuthOk
18+
func (a authExternal) FirstData() ([]byte, []byte, AuthStatus) {
19+
b := make([]byte, 2*len(a.user))
20+
hex.Encode(b, []byte(a.user))
21+
return []byte("EXTERNAL"), b, AuthOk
1922
}
2023

21-
func (a AuthExternal) HandleData(b []byte) ([]byte, AuthStatus) {
24+
func (a authExternal) HandleData(b []byte) ([]byte, AuthStatus) {
2225
return nil, AuthError
2326
}

auth_sha1.go

+18-19
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,26 @@ import (
77
"crypto/sha1"
88
"encoding/hex"
99
"os"
10-
"os/user"
1110
)
1211

13-
// AuthCookieSha1 implements the DBUS_COOKIE_SHA1 authentication mechanism.
14-
type AuthCookieSha1 struct{}
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.
15+
func AuthCookieSha1(user, home string) Auth {
16+
return authCookieSha1{user, home}
17+
}
1518

16-
func (a AuthCookieSha1) FirstData() ([]byte, AuthStatus) {
17-
u, err := user.Current()
18-
if err != nil {
19-
return nil, AuthError
20-
}
21-
b := make([]byte, 2*len(u.Username))
22-
hex.Encode(b, []byte(u.Username))
23-
return b, AuthContinue
19+
type authCookieSha1 struct {
20+
user, home string
2421
}
2522

26-
func (a AuthCookieSha1) HandleData(data []byte) ([]byte, AuthStatus) {
23+
func (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+
29+
func (a authCookieSha1) HandleData(data []byte) ([]byte, AuthStatus) {
2730
challenge := make([]byte, len(data)/2)
2831
_, err := hex.Decode(challenge, data)
2932
if err != nil {
@@ -59,12 +62,8 @@ func (a AuthCookieSha1) HandleData(data []byte) ([]byte, AuthStatus) {
5962
// the cookie content or nil. (Since HandleData can't return a specific error,
6063
// but only whether an error occured, this function also doesn't bother to
6164
// return an error.)
62-
func (a AuthCookieSha1) getCookie(context, id []byte) []byte {
63-
home := os.Getenv("HOME")
64-
if home == "" {
65-
return nil
66-
}
67-
file, err := os.Open(home + "/.dbus-keyrings/" + string(context))
65+
func (a authCookieSha1) getCookie(context, id []byte) []byte {
66+
file, err := os.Open(a.home + "/.dbus-keyrings/" + string(context))
6867
if err != nil {
6968
return nil
7069
}
@@ -88,7 +87,7 @@ func (a AuthCookieSha1) getCookie(context, id []byte) []byte {
8887

8988
// generateChallenge returns a random, hex-encoded challenge, or nil on error
9089
// (see above).
91-
func (a AuthCookieSha1) generateChallenge() []byte {
90+
func (a authCookieSha1) generateChallenge() []byte {
9291
b := make([]byte, 16)
9392
n, err := rand.Read(b)
9493
if err != nil {

0 commit comments

Comments
 (0)