| // 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} |
| } |