github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/third_party/code.google.com/p/go.crypto/ssh/client_auth.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package ssh
     6  
     7  import (
     8  	"errors"
     9  	"io"
    10  )
    11  
    12  // authenticate authenticates with the remote server. See RFC 4252.
    13  func (c *ClientConn) authenticate(session []byte) error {
    14  	// initiate user auth session
    15  	if err := c.writePacket(marshal(msgServiceRequest, serviceRequestMsg{serviceUserAuth})); err != nil {
    16  		return err
    17  	}
    18  	packet, err := c.readPacket()
    19  	if err != nil {
    20  		return err
    21  	}
    22  	var serviceAccept serviceAcceptMsg
    23  	if err := unmarshal(&serviceAccept, packet, msgServiceAccept); err != nil {
    24  		return err
    25  	}
    26  	// during the authentication phase the client first attempts the "none" method
    27  	// then any untried methods suggested by the server.
    28  	tried, remain := make(map[string]bool), make(map[string]bool)
    29  	for auth := ClientAuth(new(noneAuth)); auth != nil; {
    30  		ok, methods, err := auth.auth(session, c.config.User, c.transport, c.config.rand())
    31  		if err != nil {
    32  			return err
    33  		}
    34  		if ok {
    35  			// success
    36  			return nil
    37  		}
    38  		tried[auth.method()] = true
    39  		delete(remain, auth.method())
    40  		for _, meth := range methods {
    41  			if tried[meth] {
    42  				// if we've tried meth already, skip it.
    43  				continue
    44  			}
    45  			remain[meth] = true
    46  		}
    47  		auth = nil
    48  		for _, a := range c.config.Auth {
    49  			if remain[a.method()] {
    50  				auth = a
    51  				break
    52  			}
    53  		}
    54  	}
    55  	return errors.New("ssh: unable to authenticate, no supported methods remain")
    56  }
    57  
    58  // A ClientAuth represents an instance of an RFC 4252 authentication method.
    59  type ClientAuth interface {
    60  	// auth authenticates user over transport t.
    61  	// Returns true if authentication is successful.
    62  	// If authentication is not successful, a []string of alternative
    63  	// method names is returned.
    64  	auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error)
    65  
    66  	// method returns the RFC 4252 method name.
    67  	method() string
    68  }
    69  
    70  // "none" authentication, RFC 4252 section 5.2.
    71  type noneAuth int
    72  
    73  func (n *noneAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) {
    74  	if err := t.writePacket(marshal(msgUserAuthRequest, userAuthRequestMsg{
    75  		User:    user,
    76  		Service: serviceSSH,
    77  		Method:  "none",
    78  	})); err != nil {
    79  		return false, nil, err
    80  	}
    81  
    82  	return handleAuthResponse(t)
    83  }
    84  
    85  func (n *noneAuth) method() string {
    86  	return "none"
    87  }
    88  
    89  // "password" authentication, RFC 4252 Section 8.
    90  type passwordAuth struct {
    91  	ClientPassword
    92  }
    93  
    94  func (p *passwordAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) {
    95  	type passwordAuthMsg struct {
    96  		User     string
    97  		Service  string
    98  		Method   string
    99  		Reply    bool
   100  		Password string
   101  	}
   102  
   103  	pw, err := p.Password(user)
   104  	if err != nil {
   105  		return false, nil, err
   106  	}
   107  
   108  	if err := t.writePacket(marshal(msgUserAuthRequest, passwordAuthMsg{
   109  		User:     user,
   110  		Service:  serviceSSH,
   111  		Method:   "password",
   112  		Reply:    false,
   113  		Password: pw,
   114  	})); err != nil {
   115  		return false, nil, err
   116  	}
   117  
   118  	return handleAuthResponse(t)
   119  }
   120  
   121  func (p *passwordAuth) method() string {
   122  	return "password"
   123  }
   124  
   125  // A ClientPassword implements access to a client's passwords.
   126  type ClientPassword interface {
   127  	// Password returns the password to use for user.
   128  	Password(user string) (password string, err error)
   129  }
   130  
   131  // ClientAuthPassword returns a ClientAuth using password authentication.
   132  func ClientAuthPassword(impl ClientPassword) ClientAuth {
   133  	return &passwordAuth{impl}
   134  }
   135  
   136  // ClientKeyring implements access to a client key ring.
   137  type ClientKeyring interface {
   138  	// Key returns the i'th rsa.Publickey or dsa.Publickey, or nil if
   139  	// no key exists at i.
   140  	Key(i int) (key interface{}, err error)
   141  
   142  	// Sign returns a signature of the given data using the i'th key
   143  	// and the supplied random source.
   144  	Sign(i int, rand io.Reader, data []byte) (sig []byte, err error)
   145  }
   146  
   147  // "publickey" authentication, RFC 4252 Section 7.
   148  type publickeyAuth struct {
   149  	ClientKeyring
   150  }
   151  
   152  type publickeyAuthMsg struct {
   153  	User    string
   154  	Service string
   155  	Method  string
   156  	// HasSig indicates to the reciver packet that the auth request is signed and
   157  	// should be used for authentication of the request.
   158  	HasSig   bool
   159  	Algoname string
   160  	Pubkey   string
   161  	// Sig is defined as []byte so marshal will exclude it during validateKey
   162  	Sig []byte `ssh:"rest"`
   163  }
   164  
   165  func (p *publickeyAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) {
   166  
   167  	// Authentication is performed in two stages. The first stage sends an
   168  	// enquiry to test if each key is acceptable to the remote. The second
   169  	// stage attempts to authenticate with the valid keys obtained in the
   170  	// first stage.
   171  
   172  	var index int
   173  	// a map of public keys to their index in the keyring
   174  	validKeys := make(map[int]interface{})
   175  	for {
   176  		key, err := p.Key(index)
   177  		if err != nil {
   178  			return false, nil, err
   179  		}
   180  		if key == nil {
   181  			// no more keys in the keyring
   182  			break
   183  		}
   184  
   185  		if ok, err := p.validateKey(key, user, t); ok {
   186  			validKeys[index] = key
   187  		} else {
   188  			if err != nil {
   189  				return false, nil, err
   190  			}
   191  		}
   192  		index++
   193  	}
   194  
   195  	// methods that may continue if this auth is not successful.
   196  	var methods []string
   197  	for i, key := range validKeys {
   198  		pubkey := serializePublickey(key)
   199  		algoname := algoName(key)
   200  		sign, err := p.Sign(i, rand, buildDataSignedForAuth(session, userAuthRequestMsg{
   201  			User:    user,
   202  			Service: serviceSSH,
   203  			Method:  p.method(),
   204  		}, []byte(algoname), pubkey))
   205  		if err != nil {
   206  			return false, nil, err
   207  		}
   208  		// manually wrap the serialized signature in a string
   209  		s := serializeSignature(algoname, sign)
   210  		sig := make([]byte, stringLength(s))
   211  		marshalString(sig, s)
   212  		msg := publickeyAuthMsg{
   213  			User:     user,
   214  			Service:  serviceSSH,
   215  			Method:   p.method(),
   216  			HasSig:   true,
   217  			Algoname: algoname,
   218  			Pubkey:   string(pubkey),
   219  			Sig:      sig,
   220  		}
   221  		p := marshal(msgUserAuthRequest, msg)
   222  		if err := t.writePacket(p); err != nil {
   223  			return false, nil, err
   224  		}
   225  		success, methods, err := handleAuthResponse(t)
   226  		if err != nil {
   227  			return false, nil, err
   228  		}
   229  		if success {
   230  			return success, methods, err
   231  		}
   232  	}
   233  	return false, methods, nil
   234  }
   235  
   236  // validateKey validates the key provided it is acceptable to the server.
   237  func (p *publickeyAuth) validateKey(key interface{}, user string, t *transport) (bool, error) {
   238  	pubkey := serializePublickey(key)
   239  	algoname := algoName(key)
   240  	msg := publickeyAuthMsg{
   241  		User:     user,
   242  		Service:  serviceSSH,
   243  		Method:   p.method(),
   244  		HasSig:   false,
   245  		Algoname: algoname,
   246  		Pubkey:   string(pubkey),
   247  	}
   248  	if err := t.writePacket(marshal(msgUserAuthRequest, msg)); err != nil {
   249  		return false, err
   250  	}
   251  
   252  	return p.confirmKeyAck(key, t)
   253  }
   254  
   255  func (p *publickeyAuth) confirmKeyAck(key interface{}, t *transport) (bool, error) {
   256  	pubkey := serializePublickey(key)
   257  	algoname := algoName(key)
   258  
   259  	for {
   260  		packet, err := t.readPacket()
   261  		if err != nil {
   262  			return false, err
   263  		}
   264  		switch packet[0] {
   265  		case msgUserAuthBanner:
   266  			// TODO(gpaul): add callback to present the banner to the user
   267  		case msgUserAuthPubKeyOk:
   268  			msg := decode(packet).(*userAuthPubKeyOkMsg)
   269  			if msg.Algo != algoname || msg.PubKey != string(pubkey) {
   270  				return false, nil
   271  			}
   272  			return true, nil
   273  		case msgUserAuthFailure:
   274  			return false, nil
   275  		default:
   276  			return false, UnexpectedMessageError{msgUserAuthSuccess, packet[0]}
   277  		}
   278  	}
   279  	panic("unreachable")
   280  }
   281  
   282  func (p *publickeyAuth) method() string {
   283  	return "publickey"
   284  }
   285  
   286  // ClientAuthKeyring returns a ClientAuth using public key authentication.
   287  func ClientAuthKeyring(impl ClientKeyring) ClientAuth {
   288  	return &publickeyAuth{impl}
   289  }
   290  
   291  // handleAuthResponse returns whether the preceding authentication request succeeded
   292  // along with a list of remaining authentication methods to try next and
   293  // an error if an unexpected response was received.
   294  func handleAuthResponse(t *transport) (bool, []string, error) {
   295  	for {
   296  		packet, err := t.readPacket()
   297  		if err != nil {
   298  			return false, nil, err
   299  		}
   300  
   301  		switch packet[0] {
   302  		case msgUserAuthBanner:
   303  			// TODO: add callback to present the banner to the user
   304  		case msgUserAuthFailure:
   305  			msg := decode(packet).(*userAuthFailureMsg)
   306  			return false, msg.Methods, nil
   307  		case msgUserAuthSuccess:
   308  			return true, nil, nil
   309  		case msgDisconnect:
   310  			return false, nil, io.EOF
   311  		default:
   312  			return false, nil, UnexpectedMessageError{msgUserAuthSuccess, packet[0]}
   313  		}
   314  	}
   315  	panic("unreachable")
   316  }