 299203ade4
			
		
	
	299203ade4
	
	
	
		
			
			Adjust paths that were not canonical. Add nested vendors that are required. Now no errors with latest `vndr`. Signed-off-by: Justin Cormack <justin.cormack@docker.com>
		
			
				
	
	
		
			254 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			254 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package dbus
 | |
| 
 | |
| import (
 | |
| 	"bufio"
 | |
| 	"bytes"
 | |
| 	"errors"
 | |
| 	"io"
 | |
| 	"os"
 | |
| 	"strconv"
 | |
| )
 | |
| 
 | |
| // AuthStatus represents the Status of an authentication mechanism.
 | |
| type AuthStatus byte
 | |
| 
 | |
| const (
 | |
| 	// AuthOk signals that authentication is finished; the next command
 | |
| 	// from the server should be an OK.
 | |
| 	AuthOk AuthStatus = iota
 | |
| 
 | |
| 	// AuthContinue signals that additional data is needed; the next command
 | |
| 	// from the server should be a DATA.
 | |
| 	AuthContinue
 | |
| 
 | |
| 	// AuthError signals an error; the server sent invalid data or some
 | |
| 	// other unexpected thing happened and the current authentication
 | |
| 	// process should be aborted.
 | |
| 	AuthError
 | |
| )
 | |
| 
 | |
| type authState byte
 | |
| 
 | |
| const (
 | |
| 	waitingForData authState = iota
 | |
| 	waitingForOk
 | |
| 	waitingForReject
 | |
| )
 | |
| 
 | |
| // Auth defines the behaviour of an authentication mechanism.
 | |
| type Auth interface {
 | |
| 	// Return the name of the mechnism, the argument to the first AUTH command
 | |
| 	// and the next status.
 | |
| 	FirstData() (name, resp []byte, status AuthStatus)
 | |
| 
 | |
| 	// Process the given DATA command, and return the argument to the DATA
 | |
| 	// command and the next status. If len(resp) == 0, no DATA command is sent.
 | |
| 	HandleData(data []byte) (resp []byte, status AuthStatus)
 | |
| }
 | |
| 
 | |
| // Auth authenticates the connection, trying the given list of authentication
 | |
| // mechanisms (in that order). If nil is passed, the EXTERNAL and
 | |
| // DBUS_COOKIE_SHA1 mechanisms are tried for the current user. For private
 | |
| // connections, this method must be called before sending any messages to the
 | |
| // bus. Auth must not be called on shared connections.
 | |
| func (conn *Conn) Auth(methods []Auth) error {
 | |
| 	if methods == nil {
 | |
| 		uid := strconv.Itoa(os.Getuid())
 | |
| 		methods = []Auth{AuthExternal(uid), AuthCookieSha1(uid, getHomeDir())}
 | |
| 	}
 | |
| 	in := bufio.NewReader(conn.transport)
 | |
| 	err := conn.transport.SendNullByte()
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	err = authWriteLine(conn.transport, []byte("AUTH"))
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	s, err := authReadLine(in)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if len(s) < 2 || !bytes.Equal(s[0], []byte("REJECTED")) {
 | |
| 		return errors.New("dbus: authentication protocol error")
 | |
| 	}
 | |
| 	s = s[1:]
 | |
| 	for _, v := range s {
 | |
| 		for _, m := range methods {
 | |
| 			if name, data, status := m.FirstData(); bytes.Equal(v, name) {
 | |
| 				var ok bool
 | |
| 				err = authWriteLine(conn.transport, []byte("AUTH"), []byte(v), data)
 | |
| 				if err != nil {
 | |
| 					return err
 | |
| 				}
 | |
| 				switch status {
 | |
| 				case AuthOk:
 | |
| 					err, ok = conn.tryAuth(m, waitingForOk, in)
 | |
| 				case AuthContinue:
 | |
| 					err, ok = conn.tryAuth(m, waitingForData, in)
 | |
| 				default:
 | |
| 					panic("dbus: invalid authentication status")
 | |
| 				}
 | |
| 				if err != nil {
 | |
| 					return err
 | |
| 				}
 | |
| 				if ok {
 | |
| 					if conn.transport.SupportsUnixFDs() {
 | |
| 						err = authWriteLine(conn, []byte("NEGOTIATE_UNIX_FD"))
 | |
| 						if err != nil {
 | |
| 							return err
 | |
| 						}
 | |
| 						line, err := authReadLine(in)
 | |
| 						if err != nil {
 | |
| 							return err
 | |
| 						}
 | |
| 						switch {
 | |
| 						case bytes.Equal(line[0], []byte("AGREE_UNIX_FD")):
 | |
| 							conn.EnableUnixFDs()
 | |
| 							conn.unixFD = true
 | |
| 						case bytes.Equal(line[0], []byte("ERROR")):
 | |
| 						default:
 | |
| 							return errors.New("dbus: authentication protocol error")
 | |
| 						}
 | |
| 					}
 | |
| 					err = authWriteLine(conn.transport, []byte("BEGIN"))
 | |
| 					if err != nil {
 | |
| 						return err
 | |
| 					}
 | |
| 					go conn.inWorker()
 | |
| 					go conn.outWorker()
 | |
| 					return nil
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return errors.New("dbus: authentication failed")
 | |
| }
 | |
| 
 | |
| // tryAuth tries to authenticate with m as the mechanism, using state as the
 | |
| // initial authState and in for reading input. It returns (nil, true) on
 | |
| // success, (nil, false) on a REJECTED and (someErr, false) if some other
 | |
| // error occured.
 | |
| func (conn *Conn) tryAuth(m Auth, state authState, in *bufio.Reader) (error, bool) {
 | |
| 	for {
 | |
| 		s, err := authReadLine(in)
 | |
| 		if err != nil {
 | |
| 			return err, false
 | |
| 		}
 | |
| 		switch {
 | |
| 		case state == waitingForData && string(s[0]) == "DATA":
 | |
| 			if len(s) != 2 {
 | |
| 				err = authWriteLine(conn.transport, []byte("ERROR"))
 | |
| 				if err != nil {
 | |
| 					return err, false
 | |
| 				}
 | |
| 				continue
 | |
| 			}
 | |
| 			data, status := m.HandleData(s[1])
 | |
| 			switch status {
 | |
| 			case AuthOk, AuthContinue:
 | |
| 				if len(data) != 0 {
 | |
| 					err = authWriteLine(conn.transport, []byte("DATA"), data)
 | |
| 					if err != nil {
 | |
| 						return err, false
 | |
| 					}
 | |
| 				}
 | |
| 				if status == AuthOk {
 | |
| 					state = waitingForOk
 | |
| 				}
 | |
| 			case AuthError:
 | |
| 				err = authWriteLine(conn.transport, []byte("ERROR"))
 | |
| 				if err != nil {
 | |
| 					return err, false
 | |
| 				}
 | |
| 			}
 | |
| 		case state == waitingForData && string(s[0]) == "REJECTED":
 | |
| 			return nil, false
 | |
| 		case state == waitingForData && string(s[0]) == "ERROR":
 | |
| 			err = authWriteLine(conn.transport, []byte("CANCEL"))
 | |
| 			if err != nil {
 | |
| 				return err, false
 | |
| 			}
 | |
| 			state = waitingForReject
 | |
| 		case state == waitingForData && string(s[0]) == "OK":
 | |
| 			if len(s) != 2 {
 | |
| 				err = authWriteLine(conn.transport, []byte("CANCEL"))
 | |
| 				if err != nil {
 | |
| 					return err, false
 | |
| 				}
 | |
| 				state = waitingForReject
 | |
| 			}
 | |
| 			conn.uuid = string(s[1])
 | |
| 			return nil, true
 | |
| 		case state == waitingForData:
 | |
| 			err = authWriteLine(conn.transport, []byte("ERROR"))
 | |
| 			if err != nil {
 | |
| 				return err, false
 | |
| 			}
 | |
| 		case state == waitingForOk && string(s[0]) == "OK":
 | |
| 			if len(s) != 2 {
 | |
| 				err = authWriteLine(conn.transport, []byte("CANCEL"))
 | |
| 				if err != nil {
 | |
| 					return err, false
 | |
| 				}
 | |
| 				state = waitingForReject
 | |
| 			}
 | |
| 			conn.uuid = string(s[1])
 | |
| 			return nil, true
 | |
| 		case state == waitingForOk && string(s[0]) == "REJECTED":
 | |
| 			return nil, false
 | |
| 		case state == waitingForOk && (string(s[0]) == "DATA" ||
 | |
| 			string(s[0]) == "ERROR"):
 | |
| 
 | |
| 			err = authWriteLine(conn.transport, []byte("CANCEL"))
 | |
| 			if err != nil {
 | |
| 				return err, false
 | |
| 			}
 | |
| 			state = waitingForReject
 | |
| 		case state == waitingForOk:
 | |
| 			err = authWriteLine(conn.transport, []byte("ERROR"))
 | |
| 			if err != nil {
 | |
| 				return err, false
 | |
| 			}
 | |
| 		case state == waitingForReject && string(s[0]) == "REJECTED":
 | |
| 			return nil, false
 | |
| 		case state == waitingForReject:
 | |
| 			return errors.New("dbus: authentication protocol error"), false
 | |
| 		default:
 | |
| 			panic("dbus: invalid auth state")
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // authReadLine reads a line and separates it into its fields.
 | |
| func authReadLine(in *bufio.Reader) ([][]byte, error) {
 | |
| 	data, err := in.ReadBytes('\n')
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	data = bytes.TrimSuffix(data, []byte("\r\n"))
 | |
| 	return bytes.Split(data, []byte{' '}), nil
 | |
| }
 | |
| 
 | |
| // authWriteLine writes the given line in the authentication protocol format
 | |
| // (elements of data separated by a " " and terminated by "\r\n").
 | |
| func authWriteLine(out io.Writer, data ...[]byte) error {
 | |
| 	buf := make([]byte, 0)
 | |
| 	for i, v := range data {
 | |
| 		buf = append(buf, v...)
 | |
| 		if i != len(data)-1 {
 | |
| 			buf = append(buf, ' ')
 | |
| 		}
 | |
| 	}
 | |
| 	buf = append(buf, '\r')
 | |
| 	buf = append(buf, '\n')
 | |
| 	n, err := out.Write(buf)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if n != len(buf) {
 | |
| 		return io.ErrUnexpectedEOF
 | |
| 	}
 | |
| 	return nil
 | |
| }
 |