OSDN Git Service

new repo
[bytom/vapor.git] / vendor / golang.org / x / crypto / ssh / client.go
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         "bytes"
9         "errors"
10         "fmt"
11         "net"
12         "sync"
13         "time"
14 )
15
16 // Client implements a traditional SSH client that supports shells,
17 // subprocesses, TCP port/streamlocal forwarding and tunneled dialing.
18 type Client struct {
19         Conn
20
21         forwards        forwardList // forwarded tcpip connections from the remote side
22         mu              sync.Mutex
23         channelHandlers map[string]chan NewChannel
24 }
25
26 // HandleChannelOpen returns a channel on which NewChannel requests
27 // for the given type are sent. If the type already is being handled,
28 // nil is returned. The channel is closed when the connection is closed.
29 func (c *Client) HandleChannelOpen(channelType string) <-chan NewChannel {
30         c.mu.Lock()
31         defer c.mu.Unlock()
32         if c.channelHandlers == nil {
33                 // The SSH channel has been closed.
34                 c := make(chan NewChannel)
35                 close(c)
36                 return c
37         }
38
39         ch := c.channelHandlers[channelType]
40         if ch != nil {
41                 return nil
42         }
43
44         ch = make(chan NewChannel, chanSize)
45         c.channelHandlers[channelType] = ch
46         return ch
47 }
48
49 // NewClient creates a Client on top of the given connection.
50 func NewClient(c Conn, chans <-chan NewChannel, reqs <-chan *Request) *Client {
51         conn := &Client{
52                 Conn:            c,
53                 channelHandlers: make(map[string]chan NewChannel, 1),
54         }
55
56         go conn.handleGlobalRequests(reqs)
57         go conn.handleChannelOpens(chans)
58         go func() {
59                 conn.Wait()
60                 conn.forwards.closeAll()
61         }()
62         go conn.forwards.handleChannels(conn.HandleChannelOpen("forwarded-tcpip"))
63         go conn.forwards.handleChannels(conn.HandleChannelOpen("forwarded-streamlocal@openssh.com"))
64         return conn
65 }
66
67 // NewClientConn establishes an authenticated SSH connection using c
68 // as the underlying transport.  The Request and NewChannel channels
69 // must be serviced or the connection will hang.
70 func NewClientConn(c net.Conn, addr string, config *ClientConfig) (Conn, <-chan NewChannel, <-chan *Request, error) {
71         fullConf := *config
72         fullConf.SetDefaults()
73         if fullConf.HostKeyCallback == nil {
74                 c.Close()
75                 return nil, nil, nil, errors.New("ssh: must specify HostKeyCallback")
76         }
77
78         conn := &connection{
79                 sshConn: sshConn{conn: c},
80         }
81
82         if err := conn.clientHandshake(addr, &fullConf); err != nil {
83                 c.Close()
84                 return nil, nil, nil, fmt.Errorf("ssh: handshake failed: %v", err)
85         }
86         conn.mux = newMux(conn.transport)
87         return conn, conn.mux.incomingChannels, conn.mux.incomingRequests, nil
88 }
89
90 // clientHandshake performs the client side key exchange. See RFC 4253 Section
91 // 7.
92 func (c *connection) clientHandshake(dialAddress string, config *ClientConfig) error {
93         if config.ClientVersion != "" {
94                 c.clientVersion = []byte(config.ClientVersion)
95         } else {
96                 c.clientVersion = []byte(packageVersion)
97         }
98         var err error
99         c.serverVersion, err = exchangeVersions(c.sshConn.conn, c.clientVersion)
100         if err != nil {
101                 return err
102         }
103
104         c.transport = newClientTransport(
105                 newTransport(c.sshConn.conn, config.Rand, true /* is client */),
106                 c.clientVersion, c.serverVersion, config, dialAddress, c.sshConn.RemoteAddr())
107         if err := c.transport.waitSession(); err != nil {
108                 return err
109         }
110
111         c.sessionID = c.transport.getSessionID()
112         return c.clientAuthenticate(config)
113 }
114
115 // verifyHostKeySignature verifies the host key obtained in the key
116 // exchange.
117 func verifyHostKeySignature(hostKey PublicKey, result *kexResult) error {
118         sig, rest, ok := parseSignatureBody(result.Signature)
119         if len(rest) > 0 || !ok {
120                 return errors.New("ssh: signature parse error")
121         }
122
123         return hostKey.Verify(result.H, sig)
124 }
125
126 // NewSession opens a new Session for this client. (A session is a remote
127 // execution of a program.)
128 func (c *Client) NewSession() (*Session, error) {
129         ch, in, err := c.OpenChannel("session", nil)
130         if err != nil {
131                 return nil, err
132         }
133         return newSession(ch, in)
134 }
135
136 func (c *Client) handleGlobalRequests(incoming <-chan *Request) {
137         for r := range incoming {
138                 // This handles keepalive messages and matches
139                 // the behaviour of OpenSSH.
140                 r.Reply(false, nil)
141         }
142 }
143
144 // handleChannelOpens channel open messages from the remote side.
145 func (c *Client) handleChannelOpens(in <-chan NewChannel) {
146         for ch := range in {
147                 c.mu.Lock()
148                 handler := c.channelHandlers[ch.ChannelType()]
149                 c.mu.Unlock()
150
151                 if handler != nil {
152                         handler <- ch
153                 } else {
154                         ch.Reject(UnknownChannelType, fmt.Sprintf("unknown channel type: %v", ch.ChannelType()))
155                 }
156         }
157
158         c.mu.Lock()
159         for _, ch := range c.channelHandlers {
160                 close(ch)
161         }
162         c.channelHandlers = nil
163         c.mu.Unlock()
164 }
165
166 // Dial starts a client connection to the given SSH server. It is a
167 // convenience function that connects to the given network address,
168 // initiates the SSH handshake, and then sets up a Client.  For access
169 // to incoming channels and requests, use net.Dial with NewClientConn
170 // instead.
171 func Dial(network, addr string, config *ClientConfig) (*Client, error) {
172         conn, err := net.DialTimeout(network, addr, config.Timeout)
173         if err != nil {
174                 return nil, err
175         }
176         c, chans, reqs, err := NewClientConn(conn, addr, config)
177         if err != nil {
178                 return nil, err
179         }
180         return NewClient(c, chans, reqs), nil
181 }
182
183 // HostKeyCallback is the function type used for verifying server
184 // keys.  A HostKeyCallback must return nil if the host key is OK, or
185 // an error to reject it. It receives the hostname as passed to Dial
186 // or NewClientConn. The remote address is the RemoteAddr of the
187 // net.Conn underlying the the SSH connection.
188 type HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error
189
190 // A ClientConfig structure is used to configure a Client. It must not be
191 // modified after having been passed to an SSH function.
192 type ClientConfig struct {
193         // Config contains configuration that is shared between clients and
194         // servers.
195         Config
196
197         // User contains the username to authenticate as.
198         User string
199
200         // Auth contains possible authentication methods to use with the
201         // server. Only the first instance of a particular RFC 4252 method will
202         // be used during authentication.
203         Auth []AuthMethod
204
205         // HostKeyCallback is called during the cryptographic
206         // handshake to validate the server's host key. The client
207         // configuration must supply this callback for the connection
208         // to succeed. The functions InsecureIgnoreHostKey or
209         // FixedHostKey can be used for simplistic host key checks.
210         HostKeyCallback HostKeyCallback
211
212         // ClientVersion contains the version identification string that will
213         // be used for the connection. If empty, a reasonable default is used.
214         ClientVersion string
215
216         // HostKeyAlgorithms lists the key types that the client will
217         // accept from the server as host key, in order of
218         // preference. If empty, a reasonable default is used. Any
219         // string returned from PublicKey.Type method may be used, or
220         // any of the CertAlgoXxxx and KeyAlgoXxxx constants.
221         HostKeyAlgorithms []string
222
223         // Timeout is the maximum amount of time for the TCP connection to establish.
224         //
225         // A Timeout of zero means no timeout.
226         Timeout time.Duration
227 }
228
229 // InsecureIgnoreHostKey returns a function that can be used for
230 // ClientConfig.HostKeyCallback to accept any host key. It should
231 // not be used for production code.
232 func InsecureIgnoreHostKey() HostKeyCallback {
233         return func(hostname string, remote net.Addr, key PublicKey) error {
234                 return nil
235         }
236 }
237
238 type fixedHostKey struct {
239         key PublicKey
240 }
241
242 func (f *fixedHostKey) check(hostname string, remote net.Addr, key PublicKey) error {
243         if f.key == nil {
244                 return fmt.Errorf("ssh: required host key was nil")
245         }
246         if !bytes.Equal(key.Marshal(), f.key.Marshal()) {
247                 return fmt.Errorf("ssh: host key mismatch")
248         }
249         return nil
250 }
251
252 // FixedHostKey returns a function for use in
253 // ClientConfig.HostKeyCallback to accept only a specific host key.
254 func FixedHostKey(key PublicKey) HostKeyCallback {
255         hk := &fixedHostKey{key}
256         return hk.check
257 }