return c.transport.reader.setupKeys(serverKeys, K, H, H, hashFunc)
}
-// authenticate authenticates with the remote server. See RFC 4252.
-// Only "password" authentication is supported.
-func (c *ClientConn) authenticate() error {
- if err := c.writePacket(marshal(msgServiceRequest, serviceRequestMsg{serviceUserAuth})); err != nil {
- return err
- }
- packet, err := c.readPacket()
- if err != nil {
- return err
- }
-
- var serviceAccept serviceAcceptMsg
- if err = unmarshal(&serviceAccept, packet, msgServiceAccept); err != nil {
- return err
- }
-
- // TODO(dfc) support proper authentication method negotation
- method := "none"
- if c.config.Password != "" {
- method = "password"
- }
- if err := c.sendUserAuthReq(method); err != nil {
- return err
- }
-
- if packet, err = c.readPacket(); err != nil {
- return err
- }
-
- if packet[0] != msgUserAuthSuccess {
- return UnexpectedMessageError{msgUserAuthSuccess, packet[0]}
- }
- return nil
-}
-
-func (c *ClientConn) sendUserAuthReq(method string) error {
- length := stringLength([]byte(c.config.Password)) + 1
- payload := make([]byte, length)
- // always false for password auth, see RFC 4252 Section 8.
- payload[0] = 0
- marshalString(payload[1:], []byte(c.config.Password))
-
- return c.writePacket(marshal(msgUserAuthRequest, userAuthRequestMsg{
- User: c.config.User,
- Service: serviceSSH,
- Method: method,
- Payload: payload,
- }))
-}
-
// kexDH performs Diffie-Hellman key agreement on a ClientConn. The
// returned values are given the same names as in RFC 4253, section 8.
func (c *ClientConn) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handshakeMagics, hostKeyAlgo string) ([]byte, []byte, error) {
// The username to authenticate.
User string
- // Used for "password" method authentication.
- Password string
+ // A slice of ClientAuth methods. Only the first instance
+ // of a particular RFC 4252 method will be used during authentication.
+ Auth []ClientAuth
}
func (c *ClientConfig) rand() io.Reader {
--- /dev/null
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ssh
+
+import (
+ "errors"
+)
+
+// authenticate authenticates with the remote server. See RFC 4252.
+func (c *ClientConn) authenticate() error {
+ // initiate user auth session
+ if err := c.writePacket(marshal(msgServiceRequest, serviceRequestMsg{serviceUserAuth})); err != nil {
+ return err
+ }
+ packet, err := c.readPacket()
+ if err != nil {
+ return err
+ }
+ var serviceAccept serviceAcceptMsg
+ if err := unmarshal(&serviceAccept, packet, msgServiceAccept); err != nil {
+ return err
+ }
+ // during the authentication phase the client first attempts the "none" method
+ // then any untried methods suggested by the server.
+ tried, remain := make(map[string]bool), make(map[string]bool)
+ for auth := ClientAuth(new(noneAuth)); auth != nil; {
+ ok, methods, err := auth.auth(c.config.User, c.transport)
+ if err != nil {
+ return err
+ }
+ if ok {
+ // success
+ return nil
+ }
+ tried[auth.method()] = true
+ delete(remain, auth.method())
+ for _, meth := range methods {
+ if tried[meth] {
+ // if we've tried meth already, skip it.
+ continue
+ }
+ remain[meth] = true
+ }
+ auth = nil
+ for _, a := range c.config.Auth {
+ if remain[a.method()] {
+ auth = a
+ break
+ }
+ }
+ }
+ return errors.New("ssh: unable to authenticate, no supported methods remain")
+}
+
+// A ClientAuth represents an instance of an RFC 4252 authentication method.
+type ClientAuth interface {
+ // auth authenticates user over transport t.
+ // Returns true if authentication is successful.
+ // If authentication is not successful, a []string of alternative
+ // method names is returned.
+ auth(user string, t *transport) (bool, []string, error)
+
+ // method returns the RFC 4252 method name.
+ method() string
+}
+
+// "none" authentication, RFC 4252 section 5.2.
+type noneAuth int
+
+func (n *noneAuth) auth(user string, t *transport) (bool, []string, error) {
+ if err := t.writePacket(marshal(msgUserAuthRequest, userAuthRequestMsg{
+ User: user,
+ Service: serviceSSH,
+ Method: "none",
+ })); err != nil {
+ return false, nil, err
+ }
+
+ packet, err := t.readPacket()
+ if err != nil {
+ return false, nil, err
+ }
+
+ switch packet[0] {
+ case msgUserAuthSuccess:
+ return true, nil, nil
+ case msgUserAuthFailure:
+ msg := decode(packet).(*userAuthFailureMsg)
+ return false, msg.Methods, nil
+ }
+ return false, nil, UnexpectedMessageError{msgUserAuthSuccess, packet[0]}
+}
+
+func (n *noneAuth) method() string {
+ return "none"
+}
+
+// "password" authentication, RFC 4252 Section 8.
+type passwordAuth struct {
+ ClientPassword
+}
+
+func (p *passwordAuth) auth(user string, t *transport) (bool, []string, error) {
+ type passwordAuthMsg struct {
+ User string
+ Service string
+ Method string
+ Reply bool
+ Password string
+ }
+
+ pw, err := p.Password(user)
+ if err != nil {
+ return false, nil, err
+ }
+
+ if err := t.writePacket(marshal(msgUserAuthRequest, passwordAuthMsg{
+ User: user,
+ Service: serviceSSH,
+ Method: "password",
+ Reply: false,
+ Password: pw,
+ })); err != nil {
+ return false, nil, err
+ }
+
+ packet, err := t.readPacket()
+ if err != nil {
+ return false, nil, err
+ }
+
+ switch packet[0] {
+ case msgUserAuthSuccess:
+ return true, nil, nil
+ case msgUserAuthFailure:
+ msg := decode(packet).(*userAuthFailureMsg)
+ return false, msg.Methods, nil
+ }
+ return false, nil, UnexpectedMessageError{msgUserAuthSuccess, packet[0]}
+}
+
+func (p *passwordAuth) method() string {
+ return "password"
+}
+
+// A ClientPassword implements access to a client's passwords.
+type ClientPassword interface {
+ // Password returns the password to use for user.
+ Password(user string) (password string, err error)
+}
+
+// ClientAuthPassword returns a ClientAuth using password authentication.
+func ClientAuthPassword(impl ClientPassword) ClientAuth {
+ return &passwordAuth{impl}
+}