LEFT | RIGHT |
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 Loading... |
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 } |
LEFT | RIGHT |