LEFT | RIGHT |
| 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 "crypto/rand" |
| 9 "encoding/binary" |
| 10 "errors" |
| 11 "fmt" |
| 12 "io" |
| 13 "net" |
| 14 "sync" |
| 15 ) |
1 | 16 |
2 // ClientConn represents the client side of an SSH connection. | 17 // ClientConn represents the client side of an SSH connection. |
3 type ClientConn struct { | 18 type ClientConn struct { |
4 » *transport | 19 » transport rekeyingTransport |
5 » config *ClientConfig | 20 » config *ClientConfig |
| 21 » sshConn |
6 chanList // channels associated with this connection | 22 chanList // channels associated with this connection |
7 forwardList // forwarded tcpip connections from the remote side | 23 forwardList // forwarded tcpip connections from the remote side |
| 24 globalRequest |
| 25 |
| 26 // Address as passed to the Dial function. |
| 27 dialAddress string |
| 28 |
| 29 serverVersion string |
| 30 } |
| 31 |
| 32 type globalRequest struct { |
| 33 sync.Mutex |
| 34 response chan interface{} |
| 35 } |
| 36 |
| 37 // Client returns a new SSH client connection using c as the underlying transpor
t. |
| 38 func Client(c net.Conn, config *ClientConfig) (*ClientConn, error) { |
| 39 return clientWithAddress(c, "", config) |
| 40 } |
| 41 |
| 42 func clientWithAddress(c net.Conn, addr string, config *ClientConfig) (*ClientCo
nn, error) { |
| 43 conn := &ClientConn{ |
| 44 config: config, |
| 45 sshConn: sshConn{c, c}, |
| 46 globalRequest: globalRequest{response: make(chan interface{}, 1)
}, |
| 47 dialAddress: addr, |
| 48 } |
| 49 |
| 50 if err := conn.handshake(); err != nil { |
| 51 return nil, fmt.Errorf("ssh: handshake failed: %v", err) |
| 52 } |
| 53 go conn.mainLoop() |
| 54 return conn, nil |
| 55 } |
| 56 |
| 57 // handshake performs the client side key exchange. See RFC 4253 Section 7. |
| 58 func (c *ClientConn) handshake() error { |
| 59 clientVersion := []byte(packageVersion) |
| 60 if c.config.ClientVersion != "" { |
| 61 clientVersion = []byte(c.config.ClientVersion) |
| 62 } |
| 63 |
| 64 serverVersion, err := exchangeVersions(c.sshConn.conn, clientVersion) |
| 65 if err != nil { |
| 66 return err |
| 67 } |
| 68 c.serverVersion = string(serverVersion) |
| 69 |
| 70 c.transport = newClientTransport( |
| 71 newTransport(c.sshConn.conn, c.config.rand(), true /* is client
*/), |
| 72 clientVersion, serverVersion, c.config, c.dialAddress, c.sshConn
.RemoteAddr()) |
| 73 if err := c.transport.requestKeyChange(); err != nil { |
| 74 return err |
| 75 } |
| 76 |
| 77 if packet, err := c.transport.readPacket(); err != nil { |
| 78 return err |
| 79 } else if packet[0] != msgNewKeys { |
| 80 return UnexpectedMessageError{msgNewKeys, packet[0]} |
| 81 } |
| 82 return c.authenticate() |
| 83 } |
| 84 |
| 85 // verifyHostKeySignature verifies the host key obtained in the key |
| 86 // exchange. |
| 87 func verifyHostKeySignature(hostKeyAlgo string, result *kexResult) error { |
| 88 hostKey, rest, ok := ParsePublicKey(result.HostKey) |
| 89 if len(rest) > 0 || !ok { |
| 90 return errors.New("ssh: could not parse hostkey") |
| 91 } |
| 92 |
| 93 sig, rest, ok := parseSignatureBody(result.Signature) |
| 94 if len(rest) > 0 || !ok { |
| 95 return errors.New("ssh: signature parse error") |
| 96 } |
| 97 if sig.Format != hostKeyAlgo { |
| 98 return fmt.Errorf("ssh: got signature type %q, want %q", sig.For
mat, hostKeyAlgo) |
| 99 } |
| 100 |
| 101 if !hostKey.Verify(result.H, sig.Blob) { |
| 102 return errors.New("ssh: host key signature error") |
| 103 } |
| 104 return nil |
| 105 } |
| 106 |
| 107 // mainLoop reads incoming messages and routes channel messages |
| 108 // to their respective ClientChans. |
| 109 func (c *ClientConn) mainLoop() { |
| 110 defer func() { |
| 111 c.transport.Close() |
| 112 c.chanList.closeAll() |
| 113 c.forwardList.closeAll() |
| 114 }() |
| 115 |
| 116 for { |
| 117 packet, err := c.transport.readPacket() |
| 118 if err != nil { |
| 119 break |
| 120 } |
| 121 // TODO(dfc) A note on blocking channel use. |
| 122 // The msg, data and dataExt channels of a clientChan can |
| 123 // cause this loop to block indefinitely if the consumer does |
| 124 // not service them. |
| 125 switch packet[0] { |
| 126 case msgChannelData: |
| 127 if len(packet) < 9 { |
| 128 // malformed data packet |
| 129 return |
| 130 } |
| 131 remoteId := binary.BigEndian.Uint32(packet[1:5]) |
| 132 length := binary.BigEndian.Uint32(packet[5:9]) |
| 133 packet = packet[9:] |
| 134 |
| 135 if length != uint32(len(packet)) { |
| 136 return |
| 137 } |
| 138 ch, ok := c.getChan(remoteId) |
| 139 if !ok { |
| 140 return |
| 141 } |
| 142 ch.stdout.write(packet) |
| 143 case msgChannelExtendedData: |
| 144 if len(packet) < 13 { |
| 145 // malformed data packet |
| 146 return |
| 147 } |
| 148 remoteId := binary.BigEndian.Uint32(packet[1:5]) |
| 149 datatype := binary.BigEndian.Uint32(packet[5:9]) |
| 150 length := binary.BigEndian.Uint32(packet[9:13]) |
| 151 packet = packet[13:] |
| 152 |
| 153 if length != uint32(len(packet)) { |
| 154 return |
| 155 } |
| 156 // RFC 4254 5.2 defines data_type_code 1 to be data dest
ined |
| 157 // for stderr on interactive sessions. Other data types
are |
| 158 // silently discarded. |
| 159 if datatype == 1 { |
| 160 ch, ok := c.getChan(remoteId) |
| 161 if !ok { |
| 162 return |
| 163 } |
| 164 ch.stderr.write(packet) |
| 165 } |
| 166 |
| 167 case msgNewKeys: |
| 168 // A rekeying happened. |
| 169 default: |
| 170 decoded, err := decode(packet) |
| 171 if err != nil { |
| 172 if _, ok := err.(UnexpectedMessageError); ok { |
| 173 fmt.Printf("mainLoop: unexpected message
: %v\n", err) |
| 174 continue |
| 175 } |
| 176 return |
| 177 } |
| 178 switch msg := decoded.(type) { |
| 179 case *channelOpenMsg: |
| 180 c.handleChanOpen(msg) |
| 181 case *channelOpenConfirmMsg: |
| 182 ch, ok := c.getChan(msg.PeersId) |
| 183 if !ok { |
| 184 return |
| 185 } |
| 186 ch.msg <- msg |
| 187 case *channelOpenFailureMsg: |
| 188 ch, ok := c.getChan(msg.PeersId) |
| 189 if !ok { |
| 190 return |
| 191 } |
| 192 ch.msg <- msg |
| 193 case *channelCloseMsg: |
| 194 ch, ok := c.getChan(msg.PeersId) |
| 195 if !ok { |
| 196 return |
| 197 } |
| 198 ch.Close() |
| 199 close(ch.msg) |
| 200 c.chanList.remove(msg.PeersId) |
| 201 case *channelEOFMsg: |
| 202 ch, ok := c.getChan(msg.PeersId) |
| 203 if !ok { |
| 204 return |
| 205 } |
| 206 ch.stdout.eof() |
| 207 // RFC 4254 is mute on how EOF affects dataExt m
essages but |
| 208 // it is logical to signal EOF at the same time. |
| 209 ch.stderr.eof() |
| 210 case *channelRequestSuccessMsg: |
| 211 ch, ok := c.getChan(msg.PeersId) |
| 212 if !ok { |
| 213 return |
| 214 } |
| 215 ch.msg <- msg |
| 216 case *channelRequestFailureMsg: |
| 217 ch, ok := c.getChan(msg.PeersId) |
| 218 if !ok { |
| 219 return |
| 220 } |
| 221 ch.msg <- msg |
| 222 case *channelRequestMsg: |
| 223 ch, ok := c.getChan(msg.PeersId) |
| 224 if !ok { |
| 225 return |
| 226 } |
| 227 ch.msg <- msg |
| 228 case *windowAdjustMsg: |
| 229 ch, ok := c.getChan(msg.PeersId) |
| 230 if !ok { |
| 231 return |
| 232 } |
| 233 if !ch.remoteWin.add(msg.AdditionalBytes) { |
| 234 // invalid window update |
| 235 return |
| 236 } |
| 237 case *globalRequestMsg: |
| 238 // This handles keepalive messages and matches |
| 239 // the behaviour of OpenSSH. |
| 240 if msg.WantReply { |
| 241 c.transport.writePacket(marshal(msgReque
stFailure, globalRequestFailureMsg{})) |
| 242 } |
| 243 case *globalRequestSuccessMsg, *globalRequestFailureMsg: |
| 244 c.globalRequest.response <- msg |
| 245 case *disconnectMsg: |
| 246 return |
| 247 default: |
| 248 fmt.Printf("mainLoop: unhandled message %T: %v\n
", msg, msg) |
| 249 } |
| 250 } |
| 251 } |
| 252 } |
| 253 |
| 254 // Handle channel open messages from the remote side. |
| 255 func (c *ClientConn) handleChanOpen(msg *channelOpenMsg) { |
| 256 if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 { |
| 257 c.sendConnectionFailed(msg.PeersId) |
| 258 } |
| 259 |
| 260 switch msg.ChanType { |
| 261 case "forwarded-tcpip": |
| 262 laddr, rest, ok := parseTCPAddr(msg.TypeSpecificData) |
| 263 if !ok { |
| 264 // invalid request |
| 265 c.sendConnectionFailed(msg.PeersId) |
| 266 return |
| 267 } |
| 268 |
| 269 l, ok := c.forwardList.lookup(*laddr) |
| 270 if !ok { |
| 271 // TODO: print on a more structured log. |
| 272 fmt.Println("could not find forward list entry for", lad
dr) |
| 273 // Section 7.2, implementations MUST reject spurious inc
oming |
| 274 // connections. |
| 275 c.sendConnectionFailed(msg.PeersId) |
| 276 return |
| 277 } |
| 278 raddr, rest, ok := parseTCPAddr(rest) |
| 279 if !ok { |
| 280 // invalid request |
| 281 c.sendConnectionFailed(msg.PeersId) |
| 282 return |
| 283 } |
| 284 ch := c.newChan(c.transport) |
| 285 ch.remoteId = msg.PeersId |
| 286 ch.remoteWin.add(msg.PeersWindow) |
| 287 ch.maxPacket = msg.MaxPacketSize |
| 288 |
| 289 m := channelOpenConfirmMsg{ |
| 290 PeersId: ch.remoteId, |
| 291 MyId: ch.localId, |
| 292 MyWindow: 1 << 14, |
| 293 |
| 294 // As per RFC 4253 6.1, 32k is also the minimum. |
| 295 MaxPacketSize: 1 << 15, |
| 296 } |
| 297 |
| 298 c.transport.writePacket(marshal(msgChannelOpenConfirm, m)) |
| 299 l <- forward{ch, raddr} |
| 300 default: |
| 301 // unknown channel type |
| 302 m := channelOpenFailureMsg{ |
| 303 PeersId: msg.PeersId, |
| 304 Reason: UnknownChannelType, |
| 305 Message: fmt.Sprintf("unknown channel type: %v", msg.Ch
anType), |
| 306 Language: "en_US.UTF-8", |
| 307 } |
| 308 c.transport.writePacket(marshal(msgChannelOpenFailure, m)) |
| 309 } |
| 310 } |
| 311 |
| 312 // sendGlobalRequest sends a global request message as specified |
| 313 // in RFC4254 section 4. To correctly synchronise messages, a lock |
| 314 // is held internally until a response is returned. |
| 315 func (c *ClientConn) sendGlobalRequest(m interface{}) (*globalRequestSuccessMsg,
error) { |
| 316 c.globalRequest.Lock() |
| 317 defer c.globalRequest.Unlock() |
| 318 if err := c.transport.writePacket(marshal(msgGlobalRequest, m)); err !=
nil { |
| 319 return nil, err |
| 320 } |
| 321 r := <-c.globalRequest.response |
| 322 if r, ok := r.(*globalRequestSuccessMsg); ok { |
| 323 return r, nil |
| 324 } |
| 325 return nil, errors.New("request failed") |
| 326 } |
| 327 |
| 328 // sendConnectionFailed rejects an incoming channel identified |
| 329 // by remoteId. |
| 330 func (c *ClientConn) sendConnectionFailed(remoteId uint32) error { |
| 331 m := channelOpenFailureMsg{ |
| 332 PeersId: remoteId, |
| 333 Reason: ConnectionFailed, |
| 334 Message: "invalid request", |
| 335 Language: "en_US.UTF-8", |
| 336 } |
| 337 return c.transport.writePacket(marshal(msgChannelOpenFailure, m)) |
| 338 } |
| 339 |
| 340 // parseTCPAddr parses the originating address from the remote into a *net.TCPAd
dr. |
| 341 // RFC 4254 section 7.2 is mute on what to do if parsing fails but the forwardli
st |
| 342 // requires a valid *net.TCPAddr to operate, so we enforce that restriction here
. |
| 343 func parseTCPAddr(b []byte) (*net.TCPAddr, []byte, bool) { |
| 344 addr, b, ok := parseString(b) |
| 345 if !ok { |
| 346 return nil, b, false |
| 347 } |
| 348 port, b, ok := parseUint32(b) |
| 349 if !ok { |
| 350 return nil, b, false |
| 351 } |
| 352 ip := net.ParseIP(string(addr)) |
| 353 if ip == nil { |
| 354 return nil, b, false |
| 355 } |
| 356 return &net.TCPAddr{IP: ip, Port: int(port)}, b, true |
| 357 } |
| 358 |
| 359 // Dial connects to the given network address using net.Dial and |
| 360 // then initiates a SSH handshake, returning the resulting client connection. |
| 361 func Dial(network, addr string, config *ClientConfig) (*ClientConn, error) { |
| 362 conn, err := net.Dial(network, addr) |
| 363 if err != nil { |
| 364 return nil, err |
| 365 } |
| 366 return clientWithAddress(conn, addr, config) |
| 367 } |
| 368 |
| 369 // A ClientConfig structure is used to configure a ClientConn. After one has |
| 370 // been passed to an SSH function it must not be modified. |
| 371 type ClientConfig struct { |
| 372 // Rand provides the source of entropy for key exchange. If Rand is |
| 373 // nil, the cryptographic random reader in package crypto/rand will |
| 374 // be used. |
| 375 Rand io.Reader |
| 376 |
| 377 // The username to authenticate. |
| 378 User string |
| 379 |
| 380 // A slice of ClientAuth methods. Only the first instance |
| 381 // of a particular RFC 4252 method will be used during authentication. |
| 382 Auth []ClientAuth |
| 383 |
| 384 // HostKeyChecker, if not nil, is called during the cryptographic |
| 385 // handshake to validate the server's host key. A nil HostKeyChecker |
| 386 // implies that all host keys are accepted. |
| 387 HostKeyChecker HostKeyChecker |
| 388 |
| 389 // Cryptographic-related configuration. |
| 390 Crypto CryptoConfig |
| 391 |
| 392 // The identification string that will be used for the connection. |
| 393 // If empty, a reasonable default is used. |
| 394 ClientVersion string |
| 395 } |
| 396 |
| 397 func (c *ClientConfig) rand() io.Reader { |
| 398 if c.Rand == nil { |
| 399 return rand.Reader |
| 400 } |
| 401 return c.Rand |
| 402 } |
| 403 |
| 404 // Thread safe channel list. |
| 405 type chanList struct { |
| 406 // protects concurrent access to chans |
| 407 sync.Mutex |
| 408 // chans are indexed by the local id of the channel, clientChan.localId. |
| 409 // The PeersId value of messages received by ClientConn.mainLoop is |
| 410 // used to locate the right local clientChan in this slice. |
| 411 chans []*clientChan |
| 412 } |
| 413 |
| 414 // Allocate a new ClientChan with the next avail local id. |
| 415 func (c *chanList) newChan(p packetConn) *clientChan { |
| 416 c.Lock() |
| 417 defer c.Unlock() |
| 418 for i := range c.chans { |
| 419 if c.chans[i] == nil { |
| 420 ch := newClientChan(p, uint32(i)) |
| 421 c.chans[i] = ch |
| 422 return ch |
| 423 } |
| 424 } |
| 425 i := len(c.chans) |
| 426 ch := newClientChan(p, uint32(i)) |
| 427 c.chans = append(c.chans, ch) |
| 428 return ch |
| 429 } |
| 430 |
| 431 func (c *chanList) getChan(id uint32) (*clientChan, bool) { |
| 432 c.Lock() |
| 433 defer c.Unlock() |
| 434 if id >= uint32(len(c.chans)) { |
| 435 return nil, false |
| 436 } |
| 437 return c.chans[id], true |
| 438 } |
| 439 |
| 440 func (c *chanList) remove(id uint32) { |
| 441 c.Lock() |
| 442 defer c.Unlock() |
| 443 c.chans[id] = nil |
| 444 } |
| 445 |
| 446 func (c *chanList) closeAll() { |
| 447 c.Lock() |
| 448 defer c.Unlock() |
| 449 |
| 450 for _, ch := range c.chans { |
| 451 if ch == nil { |
| 452 continue |
| 453 } |
| 454 ch.Close() |
| 455 close(ch.msg) |
| 456 } |
| 457 } |
LEFT | RIGHT |