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

Delta Between Two Patch Sets: ssh/client.go

Issue 14225043: code review 14225043: go.crypto/ssh: reimplement SSH connection protocol modu... (Closed)
Left Patch Set: diff -r bb19605bfacc 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 | « ssh/channel.go ('k') | ssh/example_test.go » ('j') | no next file with change/comment »
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 "errors" 9 "errors"
10 "fmt" 10 "fmt"
11 "io" 11 "io"
12 "net" 12 "net"
13 ) 13 )
14 14
15 // clientVersion is the default identification string that the client will use.
16 var clientVersion = []byte("SSH-2.0-Go")
17
18 // ClientConn represents the client side of an SSH connection. 15 // ClientConn represents the client side of an SSH connection.
19 type ClientConn struct { 16 type ClientConn struct {
20 » *transport 17 » transport *transport
21 config *ClientConfig 18 config *ClientConfig
22 forwardList // forwarded tcpip connections from the remote side 19 forwardList // forwarded tcpip connections from the remote side
23 20
24 // Address as passed to the Dial function. 21 // Address as passed to the Dial function.
25 dialAddress string 22 dialAddress string
26 23
27 serverVersion string 24 serverVersion string
28 25
29 mux *mux 26 mux *mux
30 } 27 }
31 28
32 // Client returns a new SSH client connection using c as the underlying transpor t. 29 // Client returns a new SSH client connection using c as the underlying transpor t.
33 func Client(c net.Conn, config *ClientConfig) (*ClientConn, error) { 30 func Client(c net.Conn, config *ClientConfig) (*ClientConn, error) {
34 return clientWithAddress(c, "", config) 31 return clientWithAddress(c, "", config)
35 } 32 }
36 33
37 func clientWithAddress(c net.Conn, addr string, config *ClientConfig) (*ClientCo nn, error) { 34 func clientWithAddress(c net.Conn, addr string, config *ClientConfig) (*ClientCo nn, error) {
38 conn := &ClientConn{ 35 conn := &ClientConn{
39 transport: newTransport(c, config.rand(), true /* is client */ ), 36 transport: newTransport(c, config.rand(), true /* is client */ ),
40 config: config, 37 config: config,
41 dialAddress: addr, 38 dialAddress: addr,
42 } 39 }
43 40
44 if err := conn.handshake(); err != nil { 41 if err := conn.handshake(); err != nil {
45 » » conn.Close() 42 » » conn.transport.Close()
46 return nil, fmt.Errorf("handshake failed: %v", err) 43 return nil, fmt.Errorf("handshake failed: %v", err)
47 } 44 }
48 45
49 » conn.mux = newMux(conn) 46 » conn.mux = newMux(conn.transport)
50 » go conn.loop() 47 » go conn.handleGlobalRequests(conn.mux.incomingRequests)
51 » go conn.handleGlobalRequests() 48 » go conn.handleChannelOpens(conn.mux.incomingChannels)
52 » go conn.handleChannelOpens() 49 » go func() {
50 » » conn.mux.Loop()
51 » » conn.forwardList.closeAll()
52 » }()
53 return conn, nil 53 return conn, nil
54 } 54 }
55 55
56 func (c *ClientConn) loop() { 56 // Close closes the connection.
57 » c.mux.Loop() 57 func (c *ClientConn) Close() error { return c.transport.Close() }
58 » c.forwardList.closeAll() 58
59 } 59 // LocalAddr returns the local network address.
60 func (c *ClientConn) LocalAddr() net.Addr { return c.transport.LocalAddr() }
61
62 // RemoteAddr returns the remote network address.
63 func (c *ClientConn) RemoteAddr() net.Addr { return c.transport.RemoteAddr() }
60 64
61 // handshake performs the client side key exchange. See RFC 4253 Section 7. 65 // handshake performs the client side key exchange. See RFC 4253 Section 7.
62 func (c *ClientConn) handshake() error { 66 func (c *ClientConn) handshake() error {
63 » var myVersion []byte 67 » clientVersion := []byte(packageVersion)
64 » if len(c.config.ClientVersion) > 0 { 68 » if c.config.ClientVersion != "" {
65 » » myVersion = []byte(c.config.ClientVersion) 69 » » clientVersion = []byte(c.config.ClientVersion)
66 » } else { 70 » }
67 » » myVersion = clientVersion 71
68 » } 72 » serverVersion, err := exchangeVersions(c.transport.Conn, clientVersion)
69
70 » if _, err := c.Write(append(myVersion, '\r', '\n')); err != nil {
71 » » return err
72 » }
73 » if err := c.Flush(); err != nil {
74 » » return err
75 » }
76
77 » // read remote server version
78 » serverVersion, err := readVersion(c)
79 if err != nil { 73 if err != nil {
80 return err 74 return err
81 } 75 }
82 c.serverVersion = string(serverVersion) 76 c.serverVersion = string(serverVersion)
83
84 clientKexInit := kexInitMsg{ 77 clientKexInit := kexInitMsg{
85 KexAlgos: c.config.Crypto.kexes(), 78 KexAlgos: c.config.Crypto.kexes(),
86 ServerHostKeyAlgos: supportedHostKeyAlgos, 79 ServerHostKeyAlgos: supportedHostKeyAlgos,
87 CiphersClientServer: c.config.Crypto.ciphers(), 80 CiphersClientServer: c.config.Crypto.ciphers(),
88 CiphersServerClient: c.config.Crypto.ciphers(), 81 CiphersServerClient: c.config.Crypto.ciphers(),
89 MACsClientServer: c.config.Crypto.macs(), 82 MACsClientServer: c.config.Crypto.macs(),
90 MACsServerClient: c.config.Crypto.macs(), 83 MACsServerClient: c.config.Crypto.macs(),
91 CompressionClientServer: supportedCompressions, 84 CompressionClientServer: supportedCompressions,
92 CompressionServerClient: supportedCompressions, 85 CompressionServerClient: supportedCompressions,
93 } 86 }
94 kexInitPacket := marshal(msgKexInit, clientKexInit) 87 kexInitPacket := marshal(msgKexInit, clientKexInit)
95 » if err := c.writePacket(kexInitPacket); err != nil { 88 » if err := c.transport.writePacket(kexInitPacket); err != nil {
96 » » return err 89 » » return err
97 » } 90 » }
98 » packet, err := c.readPacket() 91 » packet, err := c.transport.readPacket()
99 if err != nil { 92 if err != nil {
100 return err 93 return err
101 } 94 }
102 95
103 var serverKexInit kexInitMsg 96 var serverKexInit kexInitMsg
104 if err = unmarshal(&serverKexInit, packet, msgKexInit); err != nil { 97 if err = unmarshal(&serverKexInit, packet, msgKexInit); err != nil {
105 return err 98 return err
106 } 99 }
107 100
108 algs := findAgreedAlgorithms(&clientKexInit, &serverKexInit) 101 algs := findAgreedAlgorithms(&clientKexInit, &serverKexInit)
109 if algs == nil { 102 if algs == nil {
110 return errors.New("ssh: no common algorithms") 103 return errors.New("ssh: no common algorithms")
111 } 104 }
112 105
113 if serverKexInit.FirstKexFollows && algs.kex != serverKexInit.KexAlgos[0 ] { 106 if serverKexInit.FirstKexFollows && algs.kex != serverKexInit.KexAlgos[0 ] {
114 // The server sent a Kex message for the wrong algorithm, 107 // The server sent a Kex message for the wrong algorithm,
115 // which we have to ignore. 108 // which we have to ignore.
116 » » if _, err := c.readPacket(); err != nil { 109 » » if _, err := c.transport.readPacket(); err != nil {
117 return err 110 return err
118 } 111 }
119 } 112 }
120 113
121 kex, ok := kexAlgoMap[algs.kex] 114 kex, ok := kexAlgoMap[algs.kex]
122 if !ok { 115 if !ok {
123 return fmt.Errorf("ssh: unexpected key exchange algorithm %v", a lgs.kex) 116 return fmt.Errorf("ssh: unexpected key exchange algorithm %v", a lgs.kex)
124 } 117 }
125 118
126 magics := handshakeMagics{ 119 magics := handshakeMagics{
127 » » clientVersion: myVersion, 120 » » clientVersion: clientVersion,
128 serverVersion: serverVersion, 121 serverVersion: serverVersion,
129 clientKexInit: kexInitPacket, 122 clientKexInit: kexInitPacket,
130 serverKexInit: packet, 123 serverKexInit: packet,
131 } 124 }
132 » result, err := kex.Client(c, c.config.rand(), &magics) 125 » result, err := kex.Client(c.transport, c.config.rand(), &magics)
133 if err != nil { 126 if err != nil {
134 return err 127 return err
135 } 128 }
136 129
137 err = verifyHostKeySignature(algs.hostKey, result.HostKey, result.H, res ult.Signature) 130 err = verifyHostKeySignature(algs.hostKey, result.HostKey, result.H, res ult.Signature)
138 if err != nil { 131 if err != nil {
139 return err 132 return err
140 } 133 }
141 134
142 if checker := c.config.HostKeyChecker; checker != nil { 135 if checker := c.config.HostKeyChecker; checker != nil {
143 » » err = checker.Check(c.dialAddress, c.RemoteAddr(), algs.hostKey, result.HostKey) 136 » » err = checker.Check(c.dialAddress, c.transport.RemoteAddr(), alg s.hostKey, result.HostKey)
144 if err != nil { 137 if err != nil {
145 return err 138 return err
146 } 139 }
147 } 140 }
148 141
149 c.transport.prepareKeyChange(algs, result) 142 c.transport.prepareKeyChange(algs, result)
150 143
151 » if err = c.writePacket([]byte{msgNewKeys}); err != nil { 144 » if err = c.transport.writePacket([]byte{msgNewKeys}); err != nil {
152 » » return err 145 » » return err
153 » } 146 » }
154 » if packet, err = c.readPacket(); err != nil { 147 » if packet, err = c.transport.readPacket(); err != nil {
155 return err 148 return err
156 } 149 }
157 if packet[0] != msgNewKeys { 150 if packet[0] != msgNewKeys {
158 return UnexpectedMessageError{msgNewKeys, packet[0]} 151 return UnexpectedMessageError{msgNewKeys, packet[0]}
159 } 152 }
160 » return c.authenticate(result.H) 153 » return c.authenticate()
161 } 154 }
162 155
163 // Verify the host key obtained in the key exchange. 156 // Verify the host key obtained in the key exchange.
164 func verifyHostKeySignature(hostKeyAlgo string, hostKeyBytes []byte, data []byte , signature []byte) error { 157 func verifyHostKeySignature(hostKeyAlgo string, hostKeyBytes []byte, data []byte , signature []byte) error {
165 hostKey, rest, ok := ParsePublicKey(hostKeyBytes) 158 hostKey, rest, ok := ParsePublicKey(hostKeyBytes)
166 if len(rest) > 0 || !ok { 159 if len(rest) > 0 || !ok {
167 return errors.New("ssh: could not parse hostkey") 160 return errors.New("ssh: could not parse hostkey")
168 } 161 }
169 162
170 sig, rest, ok := parseSignatureBody(signature) 163 sig, rest, ok := parseSignatureBody(signature)
171 if len(rest) > 0 || !ok { 164 if len(rest) > 0 || !ok {
172 return errors.New("ssh: signature parse error") 165 return errors.New("ssh: signature parse error")
173 } 166 }
174 if sig.Format != hostKeyAlgo { 167 if sig.Format != hostKeyAlgo {
175 return fmt.Errorf("ssh: unexpected signature type %q", sig.Forma t) 168 return fmt.Errorf("ssh: unexpected signature type %q", sig.Forma t)
176 } 169 }
177 170
178 if !hostKey.Verify(data, sig.Blob) { 171 if !hostKey.Verify(data, sig.Blob) {
179 return errors.New("ssh: host key signature error") 172 return errors.New("ssh: host key signature error")
180 } 173 }
181 return nil 174 return nil
182 } 175 }
183 176
184 func (c *ClientConn) handleGlobalRequests() { 177 func (c *ClientConn) handleGlobalRequests(incoming chan *ChannelRequest) {
185 » for r := range c.mux.ReceivedRequests() { 178 » for r := range incoming {
186 if r.WantReply { 179 if r.WantReply {
187 // This handles keepalive messages and matches 180 // This handles keepalive messages and matches
188 // the behaviour of OpenSSH. 181 // the behaviour of OpenSSH.
189 c.mux.AckRequest(false, nil) 182 c.mux.AckRequest(false, nil)
190 } 183 }
191 } 184 }
192 } 185 }
193 186
194 // Handle channel open messages from the remote side. 187 // Handle channel open messages from the remote side.
195 func (c *ClientConn) handleChannelOpens() { 188 func (c *ClientConn) handleChannelOpens(in chan *channel) {
196 » for { 189 » for ch := range in {
197 » » ch, err := c.mux.Accept()
198 » » if err != nil {
199 » » » break
200 » » }
201 c.handleChannelOpen(ch) 190 c.handleChannelOpen(ch)
202 } 191 }
203 } 192 }
204 193
205 func (c *ClientConn) handleChannelOpen(ch Channel) { 194 func (c *ClientConn) handleChannelOpen(ch *channel) {
206 switch ch.ChannelType() { 195 switch ch.ChannelType() {
207 case "forwarded-tcpip": 196 case "forwarded-tcpip":
208 laddr, rest, ok := parseTCPAddr(ch.ExtraData()) 197 laddr, rest, ok := parseTCPAddr(ch.ExtraData())
209 if !ok { 198 if !ok {
210 // invalid request 199 // invalid request
211 ch.Reject(ConnectionFailed, "could not parse TCP address ") 200 ch.Reject(ConnectionFailed, "could not parse TCP address ")
212 return 201 return
213 } 202 }
214 203
215 l, ok := c.forwardList.lookup(*laddr) 204 l, ok := c.forwardList.lookup(*laddr)
216 if !ok { 205 if !ok {
217 // Section 7.2, implementations MUST reject suprious inc oming 206 // Section 7.2, implementations MUST reject suprious inc oming
218 // connections. 207 // connections.
219 ch.Reject(Prohibited, "no forward for address") 208 ch.Reject(Prohibited, "no forward for address")
220 return 209 return
221 } 210 }
222 211
223 raddr, rest, ok := parseTCPAddr(rest) 212 raddr, rest, ok := parseTCPAddr(rest)
224 if !ok { 213 if !ok {
225 ch.Reject(ConnectionFailed, "could not parse TCP address ") 214 ch.Reject(ConnectionFailed, "could not parse TCP address ")
226 return 215 return
227 } 216 }
228 217
229 » » ch.Accept() 218 » » if err := ch.Accept(); err == nil {
230 » » l <- forward{ch, raddr} 219 » » » l <- forward{ch, raddr}
220 » » }
231 default: 221 default:
232 // unknown channel type 222 // unknown channel type
233 ch.Reject(UnknownChannelType, fmt.Sprintf("unknown channel type: %v", ch.ChannelType())) 223 ch.Reject(UnknownChannelType, fmt.Sprintf("unknown channel type: %v", ch.ChannelType()))
234 } 224 }
235 } 225 }
236 226
237 // parseTCPAddr parses the originating address from the remote into a 227 // parseTCPAddr parses the originating address from the remote into a
238 // *net.TCPAddr. RFC 4254 section 7.2 is mute on what to do if 228 // *net.TCPAddr. RFC 4254 section 7.2 is mute on what to do if
239 // parsing fails but the forwardlist requires a valid *net.TCPAddr to 229 // parsing fails but the forwardlist requires a valid *net.TCPAddr to
240 // operate, so we enforce that restriction here. 230 // operate, so we enforce that restriction here.
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
291 // If empty, a reasonable default is used. 281 // If empty, a reasonable default is used.
292 ClientVersion string 282 ClientVersion string
293 } 283 }
294 284
295 func (c *ClientConfig) rand() io.Reader { 285 func (c *ClientConfig) rand() io.Reader {
296 if c.Rand == nil { 286 if c.Rand == nil {
297 return rand.Reader 287 return rand.Reader
298 } 288 }
299 return c.Rand 289 return c.Rand
300 } 290 }
LEFTRIGHT

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