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.
18 // clientVersion is the fixed identification string that the client will use.
19 var clientVersion = []byte("SSH-2.0-Go\r\n")
21 // ClientConn represents the client side of an SSH connection.
22 type ClientConn struct {
28 // Client returns a new SSH client connection using c as the underlying transport.
29 func Client(c net.Conn, config *ClientConfig) (*ClientConn, error) {
31 transport: newTransport(c, config.rand()),
34 if err := conn.handshake(); err != nil {
42 // handshake performs the client side key exchange. See RFC 4253 Section 7.
43 func (c *ClientConn) handshake() error {
44 var magics handshakeMagics
46 if _, err := c.Write(clientVersion); err != nil {
49 if err := c.Flush(); err != nil {
52 magics.clientVersion = clientVersion[:len(clientVersion)-2]
54 // read remote server version
55 version, err := readVersion(c)
59 magics.serverVersion = version
60 clientKexInit := kexInitMsg{
61 KexAlgos: supportedKexAlgos,
62 ServerHostKeyAlgos: supportedHostKeyAlgos,
63 CiphersClientServer: c.config.Crypto.ciphers(),
64 CiphersServerClient: c.config.Crypto.ciphers(),
65 MACsClientServer: supportedMACs,
66 MACsServerClient: supportedMACs,
67 CompressionClientServer: supportedCompressions,
68 CompressionServerClient: supportedCompressions,
70 kexInitPacket := marshal(msgKexInit, clientKexInit)
71 magics.clientKexInit = kexInitPacket
73 if err := c.writePacket(kexInitPacket); err != nil {
76 packet, err := c.readPacket()
81 magics.serverKexInit = packet
83 var serverKexInit kexInitMsg
84 if err = unmarshal(&serverKexInit, packet, msgKexInit); err != nil {
88 kexAlgo, hostKeyAlgo, ok := findAgreedAlgorithms(c.transport, &clientKexInit, &serverKexInit)
90 return errors.New("ssh: no common algorithms")
93 if serverKexInit.FirstKexFollows && kexAlgo != serverKexInit.KexAlgos[0] {
94 // The server sent a Kex message for the wrong algorithm,
95 // which we have to ignore.
96 if _, err := c.readPacket(); err != nil {
102 var hashFunc crypto.Hash
104 case kexAlgoDH14SHA1:
105 hashFunc = crypto.SHA1
106 dhGroup14Once.Do(initDHGroup14)
107 H, K, err = c.kexDH(dhGroup14, hashFunc, &magics, hostKeyAlgo)
109 err = fmt.Errorf("ssh: unexpected key exchange algorithm %v", kexAlgo)
115 if err = c.writePacket([]byte{msgNewKeys}); err != nil {
118 if err = c.transport.writer.setupKeys(clientKeys, K, H, H, hashFunc); err != nil {
121 if packet, err = c.readPacket(); err != nil {
124 if packet[0] != msgNewKeys {
125 return UnexpectedMessageError{msgNewKeys, packet[0]}
127 if err := c.transport.reader.setupKeys(serverKeys, K, H, H, hashFunc); err != nil {
130 return c.authenticate(H)
133 // kexDH performs Diffie-Hellman key agreement on a ClientConn. The
134 // returned values are given the same names as in RFC 4253, section 8.
135 func (c *ClientConn) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handshakeMagics, hostKeyAlgo string) ([]byte, []byte, error) {
136 x, err := rand.Int(c.config.rand(), group.p)
140 X := new(big.Int).Exp(group.g, x, group.p)
141 kexDHInit := kexDHInitMsg{
144 if err := c.writePacket(marshal(msgKexDHInit, kexDHInit)); err != nil {
148 packet, err := c.readPacket()
153 var kexDHReply = new(kexDHReplyMsg)
154 if err = unmarshal(kexDHReply, packet, msgKexDHReply); err != nil {
158 if kexDHReply.Y.Sign() == 0 || kexDHReply.Y.Cmp(group.p) >= 0 {
159 return nil, nil, errors.New("server DH parameter out of bounds")
162 kInt := new(big.Int).Exp(kexDHReply.Y, x, group.p)
164 writeString(h, magics.clientVersion)
165 writeString(h, magics.serverVersion)
166 writeString(h, magics.clientKexInit)
167 writeString(h, magics.serverKexInit)
168 writeString(h, kexDHReply.HostKey)
170 writeInt(h, kexDHReply.Y)
171 K := make([]byte, intLength(kInt))
180 // mainLoop reads incoming messages and routes channel messages
181 // to their respective ClientChans.
182 func (c *ClientConn) mainLoop() {
183 // TODO(dfc) signal the underlying close to all channels
186 packet, err := c.readPacket()
190 // TODO(dfc) A note on blocking channel use.
191 // The msg, win, data and dataExt channels of a clientChan can
192 // cause this loop to block indefinately if the consumer does
197 // malformed data packet
200 peersId := uint32(packet[1])<<24 | uint32(packet[2])<<16 | uint32(packet[3])<<8 | uint32(packet[4])
201 if length := int(packet[5])<<24 | int(packet[6])<<16 | int(packet[7])<<8 | int(packet[8]); length > 0 {
203 c.getChan(peersId).stdout.handleData(packet[:length])
205 case msgChannelExtendedData:
206 if len(packet) < 13 {
207 // malformed data packet
210 peersId := uint32(packet[1])<<24 | uint32(packet[2])<<16 | uint32(packet[3])<<8 | uint32(packet[4])
211 datatype := uint32(packet[5])<<24 | uint32(packet[6])<<16 | uint32(packet[7])<<8 | uint32(packet[8])
212 if length := int(packet[9])<<24 | int(packet[10])<<16 | int(packet[11])<<8 | int(packet[12]); length > 0 {
214 // RFC 4254 5.2 defines data_type_code 1 to be data destined
215 // for stderr on interactive sessions. Other data types are
216 // silently discarded.
218 c.getChan(peersId).stderr.handleData(packet[:length])
222 switch msg := decode(packet).(type) {
223 case *channelOpenMsg:
224 c.getChan(msg.PeersId).msg <- msg
225 case *channelOpenConfirmMsg:
226 c.getChan(msg.PeersId).msg <- msg
227 case *channelOpenFailureMsg:
228 c.getChan(msg.PeersId).msg <- msg
229 case *channelCloseMsg:
230 ch := c.getChan(msg.PeersId)
240 c.chanlist.remove(msg.PeersId)
242 ch := c.getChan(msg.PeersId)
244 // RFC 4254 is mute on how EOF affects dataExt messages but
245 // it is logical to signal EOF at the same time.
247 case *channelRequestSuccessMsg:
248 c.getChan(msg.PeersId).msg <- msg
249 case *channelRequestFailureMsg:
250 c.getChan(msg.PeersId).msg <- msg
251 case *channelRequestMsg:
252 c.getChan(msg.PeersId).msg <- msg
253 case *windowAdjustMsg:
254 c.getChan(msg.PeersId).stdin.win <- int(msg.AdditionalBytes)
258 fmt.Printf("mainLoop: unhandled message %T: %v\n", msg, msg)
264 // Dial connects to the given network address using net.Dial and
265 // then initiates a SSH handshake, returning the resulting client connection.
266 func Dial(network, addr string, config *ClientConfig) (*ClientConn, error) {
267 conn, err := net.Dial(network, addr)
271 return Client(conn, config)
274 // A ClientConfig structure is used to configure a ClientConn. After one has
275 // been passed to an SSH function it must not be modified.
276 type ClientConfig struct {
277 // Rand provides the source of entropy for key exchange. If Rand is
278 // nil, the cryptographic random reader in package crypto/rand will
282 // The username to authenticate.
285 // A slice of ClientAuth methods. Only the first instance
286 // of a particular RFC 4252 method will be used during authentication.
289 // Cryptographic-related configuration.
293 func (c *ClientConfig) rand() io.Reader {
300 // A clientChan represents a single RFC 4254 channel that is multiplexed
301 // over a single SSH connection.
302 type clientChan struct {
305 stdin *chanWriter // receives window adjustments
306 stdout *chanReader // receives the payload of channelData messages
307 stderr *chanReader // receives the payload of channelExtendedData messages
308 msg chan interface{} // incoming messages
310 theyClosed bool // indicates the close msg has been received from the remote side
311 weClosed bool // incidates the close msg has been sent from our side
314 // newClientChan returns a partially constructed *clientChan
315 // using the local id provided. To be usable clientChan.peersId
316 // needs to be assigned once known.
317 func newClientChan(t *transport, id uint32) *clientChan {
321 msg: make(chan interface{}, 16),
323 c.stdin = &chanWriter{
324 win: make(chan int, 16),
327 c.stdout = &chanReader{
328 data: make(chan []byte, 16),
331 c.stderr = &chanReader{
332 data: make(chan []byte, 16),
338 // waitForChannelOpenResponse, if successful, fills out
339 // the peerId and records any initial window advertisement.
340 func (c *clientChan) waitForChannelOpenResponse() error {
341 switch msg := (<-c.msg).(type) {
342 case *channelOpenConfirmMsg:
343 // fixup peersId field
345 c.stdin.win <- int(msg.MyWindow)
347 case *channelOpenFailureMsg:
348 return errors.New(safeString(msg.Message))
350 return errors.New("unexpected packet")
353 // sendEOF sends EOF to the server. RFC 4254 Section 5.3
354 func (c *clientChan) sendEOF() error {
355 return c.writePacket(marshal(msgChannelEOF, channelEOFMsg{
360 // sendClose signals the intent to close the channel.
361 func (c *clientChan) sendClose() error {
362 return c.writePacket(marshal(msgChannelClose, channelCloseMsg{
367 // Close closes the channel. This does not close the underlying connection.
368 func (c *clientChan) Close() error {
376 // Thread safe channel list.
377 type chanlist struct {
378 // protects concurrent access to chans
380 // chans are indexed by the local id of the channel, clientChan.id.
381 // The PeersId value of messages received by ClientConn.mainLoop is
382 // used to locate the right local clientChan in this slice.
386 // Allocate a new ClientChan with the next avail local id.
387 func (c *chanlist) newChan(t *transport) *clientChan {
390 for i := range c.chans {
391 if c.chans[i] == nil {
392 ch := newClientChan(t, uint32(i))
398 ch := newClientChan(t, uint32(i))
399 c.chans = append(c.chans, ch)
403 func (c *chanlist) getChan(id uint32) *clientChan {
406 return c.chans[int(id)]
409 func (c *chanlist) remove(id uint32) {
412 c.chans[int(id)] = nil
415 // A chanWriter represents the stdin of a remote process.
416 type chanWriter struct {
417 win chan int // receives window adjustments
418 rwin int // current rwin size
419 clientChan *clientChan // the channel backing this writer
422 // Write writes data to the remote process's standard input.
423 func (w *chanWriter) Write(data []byte) (n int, err error) {
433 peersId := w.clientChan.peersId
435 packet := make([]byte, 0, 9+n)
436 packet = append(packet, msgChannelData,
437 byte(peersId>>24), byte(peersId>>16), byte(peersId>>8), byte(peersId),
438 byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
439 err = w.clientChan.writePacket(append(packet, data...))
446 func (w *chanWriter) Close() error {
447 return w.clientChan.sendEOF()
450 // A chanReader represents stdout or stderr of a remote process.
451 type chanReader struct {
452 // TODO(dfc) a fixed size channel may not be the right data structure.
453 // If writes to this channel block, they will block mainLoop, making
454 // it unable to receive new messages from the remote side.
455 data chan []byte // receives data from remote
456 dataClosed bool // protects data from being closed twice
457 clientChan *clientChan // the channel backing this reader
461 // eof signals to the consumer that there is no more data to be received.
462 func (r *chanReader) eof() {
469 // handleData sends buf to the reader's consumer. If r.data is closed
470 // the data will be silently discarded
471 func (r *chanReader) handleData(buf []byte) {
477 // Read reads data from the remote process's stdout or stderr.
478 func (r *chanReader) Read(data []byte) (int, error) {
482 n := copy(data, r.buf)
484 msg := windowAdjustMsg{
485 PeersId: r.clientChan.peersId,
486 AdditionalBytes: uint32(n),
488 return n, r.clientChan.writePacket(marshal(msgChannelWindowAdjust, msg))