Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(113)

Delta Between Two Patch Sets: ssh/client.go

Issue 14494058: code review 14494058: go.crypto/ssh: support rekeying in both directions. (Closed)
Left Patch Set: diff -r 5ff5636e18c9 https://code.google.com/p/go.crypto Created 10 years, 5 months ago
Right Patch Set: diff -r cd1eea1eb828 https://code.google.com/p/go.crypto Created 10 years, 5 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « no previous file | ssh/client_auth.go » ('j') | ssh/common.go » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 // Copyright 2011 The Go Authors. All rights reserved. 1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style 2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file. 3 // license that can be found in the LICENSE file.
4 4
5 package ssh 5 package ssh
6 6
7 import ( 7 import (
8 "crypto/rand" 8 "crypto/rand"
9 "encoding/binary" 9 "encoding/binary"
10 "errors" 10 "errors"
11 "fmt" 11 "fmt"
12 "io" 12 "io"
13 "net" 13 "net"
14 "sync" 14 "sync"
15 ) 15 )
16 16
17 // ClientConn represents the client side of an SSH connection. 17 // ClientConn represents the client side of an SSH connection.
18 type ClientConn struct { 18 type ClientConn struct {
19 » *handshakeTransport 19 » transport rekeyingTransport
20 20 » config *ClientConfig
21 sshConn 21 sshConn
22 config *ClientConfig
23 chanList // channels associated with this connection 22 chanList // channels associated with this connection
24 forwardList // forwarded tcpip connections from the remote side 23 forwardList // forwarded tcpip connections from the remote side
25 globalRequest 24 globalRequest
26 25
27 // Address as passed to the Dial function. 26 // Address as passed to the Dial function.
28 dialAddress string 27 dialAddress string
29 28
30 serverVersion string 29 serverVersion string
31 } 30 }
32 31
33 type globalRequest struct { 32 type globalRequest struct {
34 sync.Mutex 33 sync.Mutex
35 response chan interface{} 34 response chan interface{}
36 } 35 }
37 36
38 // Client returns a new SSH client connection using c as the underlying transpor t. 37 // Client returns a new SSH client connection using c as the underlying transpor t.
39 func Client(c net.Conn, config *ClientConfig) (*ClientConn, error) { 38 func Client(c net.Conn, config *ClientConfig) (*ClientConn, error) {
40 return clientWithAddress(c, "", config) 39 return clientWithAddress(c, "", config)
41 } 40 }
42 41
43 func clientWithAddress(c net.Conn, addr string, config *ClientConfig) (*ClientCo nn, error) { 42 func clientWithAddress(c net.Conn, addr string, config *ClientConfig) (*ClientCo nn, error) {
44 conn := &ClientConn{ 43 conn := &ClientConn{
45 sshConn: sshConn{c},
46 config: config, 44 config: config,
45 sshConn: sshConn{c, c},
47 globalRequest: globalRequest{response: make(chan interface{}, 1) }, 46 globalRequest: globalRequest{response: make(chan interface{}, 1) },
48 dialAddress: addr, 47 dialAddress: addr,
49 } 48 }
50 49
51 if err := conn.handshake(); err != nil { 50 if err := conn.handshake(); err != nil {
52 return nil, fmt.Errorf("ssh: handshake failed: %v", err) 51 return nil, fmt.Errorf("ssh: handshake failed: %v", err)
53 } 52 }
54 go conn.mainLoop() 53 go conn.mainLoop()
55 return conn, nil 54 return conn, nil
56 }
57
58 // Close closes the underlying network connection.
59 func (c *ClientConn) Close() error {
60 return c.sshConn.Conn.Close()
61 } 55 }
62 56
63 // handshake performs the client side key exchange. See RFC 4253 Section 7. 57 // handshake performs the client side key exchange. See RFC 4253 Section 7.
64 func (c *ClientConn) handshake() error { 58 func (c *ClientConn) handshake() error {
65 clientVersion := []byte(packageVersion) 59 clientVersion := []byte(packageVersion)
66 if c.config.ClientVersion != "" { 60 if c.config.ClientVersion != "" {
67 clientVersion = []byte(c.config.ClientVersion) 61 clientVersion = []byte(c.config.ClientVersion)
68 } 62 }
69 63
70 » serverVersion, err := exchangeVersions(c.sshConn.Conn, clientVersion) 64 » serverVersion, err := exchangeVersions(c.sshConn.conn, clientVersion)
71 if err != nil { 65 if err != nil {
72 return err 66 return err
73 } 67 }
74 c.serverVersion = string(serverVersion) 68 c.serverVersion = string(serverVersion)
75 69
76 » c.handshakeTransport = newHandshakeTransport( 70 » c.transport = newClientTransport(
77 » » newTransport(c.sshConn.Conn, c.config.rand(), true /* is client */), 71 » » newTransport(c.sshConn.conn, c.config.rand(), true /* is client */),
78 » » clientVersion, serverVersion) 72 » » clientVersion, serverVersion, c.config, c.dialAddress, c.sshConn .RemoteAddr())
79 73 » if err := c.transport.requestKeyChange(); err != nil {
80 » c.handshakeTransport.setClient(c.config, c.dialAddress, c.sshConn.Remote Addr())
81 » _, _, err = c.sendKexInit()
82 » if err != nil {
83 return err 74 return err
84 } 75 }
85 76
86 » // discard newkeys packet. 77 » if packet, err := c.transport.readPacket(); err != nil {
87 » if _, err = c.readPacket(); err != nil {
88 return err 78 return err
89 » } 79 » } else if packet[0] != msgNewKeys {
90 » return c.authenticate(c.handshakeTransport.conn.SessionID()) 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
91 } 105 }
92 106
93 // mainLoop reads incoming messages and routes channel messages 107 // mainLoop reads incoming messages and routes channel messages
94 // to their respective ClientChans. 108 // to their respective ClientChans.
95 func (c *ClientConn) mainLoop() { 109 func (c *ClientConn) mainLoop() {
96 defer func() { 110 defer func() {
97 » » c.Close() 111 » » c.transport.Close()
98 c.chanList.closeAll() 112 c.chanList.closeAll()
99 c.forwardList.closeAll() 113 c.forwardList.closeAll()
100 }() 114 }()
101 115
102 for { 116 for {
103 » » packet, err := c.readPacket() 117 » » packet, err := c.transport.readPacket()
104 if err != nil { 118 if err != nil {
105 break 119 break
106 } 120 }
107 // TODO(dfc) A note on blocking channel use. 121 // TODO(dfc) A note on blocking channel use.
108 // The msg, data and dataExt channels of a clientChan can 122 // The msg, data and dataExt channels of a clientChan can
109 // cause this loop to block indefinitely if the consumer does 123 // cause this loop to block indefinitely if the consumer does
110 // not service them. 124 // not service them.
111 switch packet[0] { 125 switch packet[0] {
112 case msgChannelData: 126 case msgChannelData:
113 if len(packet) < 9 { 127 if len(packet) < 9 {
(...skipping 28 matching lines...) Expand all
142 // RFC 4254 5.2 defines data_type_code 1 to be data dest ined 156 // RFC 4254 5.2 defines data_type_code 1 to be data dest ined
143 // for stderr on interactive sessions. Other data types are 157 // for stderr on interactive sessions. Other data types are
144 // silently discarded. 158 // silently discarded.
145 if datatype == 1 { 159 if datatype == 1 {
146 ch, ok := c.getChan(remoteId) 160 ch, ok := c.getChan(remoteId)
147 if !ok { 161 if !ok {
148 return 162 return
149 } 163 }
150 ch.stderr.write(packet) 164 ch.stderr.write(packet)
151 } 165 }
166
152 case msgNewKeys: 167 case msgNewKeys:
153 // A rekeying happened. 168 // A rekeying happened.
154 default: 169 default:
155 decoded, err := decode(packet) 170 decoded, err := decode(packet)
156 if err != nil { 171 if err != nil {
157 if _, ok := err.(UnexpectedMessageError); ok { 172 if _, ok := err.(UnexpectedMessageError); ok {
158 fmt.Printf("mainLoop: unexpected message : %v\n", err) 173 fmt.Printf("mainLoop: unexpected message : %v\n", err)
159 continue 174 continue
160 } 175 }
161 return 176 return
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
216 return 231 return
217 } 232 }
218 if !ch.remoteWin.add(msg.AdditionalBytes) { 233 if !ch.remoteWin.add(msg.AdditionalBytes) {
219 // invalid window update 234 // invalid window update
220 return 235 return
221 } 236 }
222 case *globalRequestMsg: 237 case *globalRequestMsg:
223 // This handles keepalive messages and matches 238 // This handles keepalive messages and matches
224 // the behaviour of OpenSSH. 239 // the behaviour of OpenSSH.
225 if msg.WantReply { 240 if msg.WantReply {
226 » » » » » c.writePacket(marshal(msgRequestFailure, globalRequestFailureMsg{})) 241 » » » » » c.transport.writePacket(marshal(msgReque stFailure, globalRequestFailureMsg{}))
227 } 242 }
228 case *globalRequestSuccessMsg, *globalRequestFailureMsg: 243 case *globalRequestSuccessMsg, *globalRequestFailureMsg:
229 c.globalRequest.response <- msg 244 c.globalRequest.response <- msg
230 case *disconnectMsg: 245 case *disconnectMsg:
231 return 246 return
232 default: 247 default:
233 fmt.Printf("mainLoop: unhandled message %T: %v\n ", msg, msg) 248 fmt.Printf("mainLoop: unhandled message %T: %v\n ", msg, msg)
234 } 249 }
235 } 250 }
236 } 251 }
(...skipping 22 matching lines...) Expand all
259 // connections. 274 // connections.
260 c.sendConnectionFailed(msg.PeersId) 275 c.sendConnectionFailed(msg.PeersId)
261 return 276 return
262 } 277 }
263 raddr, rest, ok := parseTCPAddr(rest) 278 raddr, rest, ok := parseTCPAddr(rest)
264 if !ok { 279 if !ok {
265 // invalid request 280 // invalid request
266 c.sendConnectionFailed(msg.PeersId) 281 c.sendConnectionFailed(msg.PeersId)
267 return 282 return
268 } 283 }
269 » » ch := c.newChan(c.handshakeTransport) 284 » » ch := c.newChan(c.transport)
270 ch.remoteId = msg.PeersId 285 ch.remoteId = msg.PeersId
271 ch.remoteWin.add(msg.PeersWindow) 286 ch.remoteWin.add(msg.PeersWindow)
272 ch.maxPacket = msg.MaxPacketSize 287 ch.maxPacket = msg.MaxPacketSize
273 288
274 m := channelOpenConfirmMsg{ 289 m := channelOpenConfirmMsg{
275 PeersId: ch.remoteId, 290 PeersId: ch.remoteId,
276 MyId: ch.localId, 291 MyId: ch.localId,
277 MyWindow: 1 << 14, 292 MyWindow: 1 << 14,
278 293
279 // As per RFC 4253 6.1, 32k is also the minimum. 294 // As per RFC 4253 6.1, 32k is also the minimum.
280 MaxPacketSize: 1 << 15, 295 MaxPacketSize: 1 << 15,
281 } 296 }
282 297
283 » » c.writePacket(marshal(msgChannelOpenConfirm, m)) 298 » » c.transport.writePacket(marshal(msgChannelOpenConfirm, m))
284 l <- forward{ch, raddr} 299 l <- forward{ch, raddr}
285 default: 300 default:
286 // unknown channel type 301 // unknown channel type
287 m := channelOpenFailureMsg{ 302 m := channelOpenFailureMsg{
288 PeersId: msg.PeersId, 303 PeersId: msg.PeersId,
289 Reason: UnknownChannelType, 304 Reason: UnknownChannelType,
290 Message: fmt.Sprintf("unknown channel type: %v", msg.Ch anType), 305 Message: fmt.Sprintf("unknown channel type: %v", msg.Ch anType),
291 Language: "en_US.UTF-8", 306 Language: "en_US.UTF-8",
292 } 307 }
293 » » c.writePacket(marshal(msgChannelOpenFailure, m)) 308 » » c.transport.writePacket(marshal(msgChannelOpenFailure, m))
294 } 309 }
295 } 310 }
296 311
297 // sendGlobalRequest sends a global request message as specified 312 // sendGlobalRequest sends a global request message as specified
298 // in RFC4254 section 4. To correctly synchronise messages, a lock 313 // in RFC4254 section 4. To correctly synchronise messages, a lock
299 // is held internally until a response is returned. 314 // is held internally until a response is returned.
300 func (c *ClientConn) sendGlobalRequest(m interface{}) (*globalRequestSuccessMsg, error) { 315 func (c *ClientConn) sendGlobalRequest(m interface{}) (*globalRequestSuccessMsg, error) {
301 c.globalRequest.Lock() 316 c.globalRequest.Lock()
302 defer c.globalRequest.Unlock() 317 defer c.globalRequest.Unlock()
303 » if err := c.writePacket(marshal(msgGlobalRequest, m)); err != nil { 318 » if err := c.transport.writePacket(marshal(msgGlobalRequest, m)); err != nil {
304 return nil, err 319 return nil, err
305 } 320 }
306 r := <-c.globalRequest.response 321 r := <-c.globalRequest.response
307 if r, ok := r.(*globalRequestSuccessMsg); ok { 322 if r, ok := r.(*globalRequestSuccessMsg); ok {
308 return r, nil 323 return r, nil
309 } 324 }
310 return nil, errors.New("request failed") 325 return nil, errors.New("request failed")
311 } 326 }
312 327
313 // sendConnectionFailed rejects an incoming channel identified 328 // sendConnectionFailed rejects an incoming channel identified
314 // by remoteId. 329 // by remoteId.
315 func (c *ClientConn) sendConnectionFailed(remoteId uint32) error { 330 func (c *ClientConn) sendConnectionFailed(remoteId uint32) error {
316 m := channelOpenFailureMsg{ 331 m := channelOpenFailureMsg{
317 PeersId: remoteId, 332 PeersId: remoteId,
318 Reason: ConnectionFailed, 333 Reason: ConnectionFailed,
319 Message: "invalid request", 334 Message: "invalid request",
320 Language: "en_US.UTF-8", 335 Language: "en_US.UTF-8",
321 } 336 }
322 » return c.writePacket(marshal(msgChannelOpenFailure, m)) 337 » return c.transport.writePacket(marshal(msgChannelOpenFailure, m))
323 } 338 }
324 339
325 // parseTCPAddr parses the originating address from the remote into a *net.TCPAd dr. 340 // parseTCPAddr parses the originating address from the remote into a *net.TCPAd dr.
326 // RFC 4254 section 7.2 is mute on what to do if parsing fails but the forwardli st 341 // RFC 4254 section 7.2 is mute on what to do if parsing fails but the forwardli st
327 // requires a valid *net.TCPAddr to operate, so we enforce that restriction here . 342 // requires a valid *net.TCPAddr to operate, so we enforce that restriction here .
328 func parseTCPAddr(b []byte) (*net.TCPAddr, []byte, bool) { 343 func parseTCPAddr(b []byte) (*net.TCPAddr, []byte, bool) {
329 addr, b, ok := parseString(b) 344 addr, b, ok := parseString(b)
330 if !ok { 345 if !ok {
331 return nil, b, false 346 return nil, b, false
332 } 347 }
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
433 defer c.Unlock() 448 defer c.Unlock()
434 449
435 for _, ch := range c.chans { 450 for _, ch := range c.chans {
436 if ch == nil { 451 if ch == nil {
437 continue 452 continue
438 } 453 }
439 ch.Close() 454 ch.Close()
440 close(ch.msg) 455 close(ch.msg)
441 } 456 }
442 } 457 }
LEFTRIGHT
« no previous file | ssh/client_auth.go » ('j') | Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Toggle Comments ('s')

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b