]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
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" | |
9 | "crypto/rand" | |
10 | "fmt" | |
11 | "io" | |
12 | "sync" | |
13 | ||
14 | _ "crypto/sha1" | |
15 | _ "crypto/sha256" | |
16 | _ "crypto/sha512" | |
17 | ) | |
18 | ||
19 | // These are string constants in the SSH protocol. | |
20 | const ( | |
21 | compressionNone = "none" | |
22 | serviceUserAuth = "ssh-userauth" | |
23 | serviceSSH = "ssh-connection" | |
24 | ) | |
25 | ||
26 | // supportedCiphers specifies the supported ciphers in preference order. | |
27 | var supportedCiphers = []string{ | |
28 | "aes128-ctr", "aes192-ctr", "aes256-ctr", | |
29 | "aes128-gcm@openssh.com", | |
30 | "arcfour256", "arcfour128", | |
31 | } | |
32 | ||
33 | // supportedKexAlgos specifies the supported key-exchange algorithms in | |
34 | // preference order. | |
35 | var supportedKexAlgos = []string{ | |
36 | kexAlgoCurve25519SHA256, | |
37 | // P384 and P521 are not constant-time yet, but since we don't | |
38 | // reuse ephemeral keys, using them for ECDH should be OK. | |
39 | kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521, | |
40 | kexAlgoDH14SHA1, kexAlgoDH1SHA1, | |
41 | } | |
42 | ||
43 | // supportedKexAlgos specifies the supported host-key algorithms (i.e. methods | |
44 | // of authenticating servers) in preference order. | |
45 | var supportedHostKeyAlgos = []string{ | |
46 | CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, | |
47 | CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01, | |
48 | ||
49 | KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, | |
50 | KeyAlgoRSA, KeyAlgoDSA, | |
51 | ||
52 | KeyAlgoED25519, | |
53 | } | |
54 | ||
55 | // supportedMACs specifies a default set of MAC algorithms in preference order. | |
56 | // This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed | |
57 | // because they have reached the end of their useful life. | |
58 | var supportedMACs = []string{ | |
59 | "hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1", "hmac-sha1-96", | |
60 | } | |
61 | ||
62 | var supportedCompressions = []string{compressionNone} | |
63 | ||
64 | // hashFuncs keeps the mapping of supported algorithms to their respective | |
65 | // hashes needed for signature verification. | |
66 | var hashFuncs = map[string]crypto.Hash{ | |
67 | KeyAlgoRSA: crypto.SHA1, | |
68 | KeyAlgoDSA: crypto.SHA1, | |
69 | KeyAlgoECDSA256: crypto.SHA256, | |
70 | KeyAlgoECDSA384: crypto.SHA384, | |
71 | KeyAlgoECDSA521: crypto.SHA512, | |
72 | CertAlgoRSAv01: crypto.SHA1, | |
73 | CertAlgoDSAv01: crypto.SHA1, | |
74 | CertAlgoECDSA256v01: crypto.SHA256, | |
75 | CertAlgoECDSA384v01: crypto.SHA384, | |
76 | CertAlgoECDSA521v01: crypto.SHA512, | |
77 | } | |
78 | ||
79 | // unexpectedMessageError results when the SSH message that we received didn't | |
80 | // match what we wanted. | |
81 | func unexpectedMessageError(expected, got uint8) error { | |
82 | return fmt.Errorf("ssh: unexpected message type %d (expected %d)", got, expected) | |
83 | } | |
84 | ||
85 | // parseError results from a malformed SSH message. | |
86 | func parseError(tag uint8) error { | |
87 | return fmt.Errorf("ssh: parse error in message type %d", tag) | |
88 | } | |
89 | ||
90 | func findCommon(what string, client []string, server []string) (common string, err error) { | |
91 | for _, c := range client { | |
92 | for _, s := range server { | |
93 | if c == s { | |
94 | return c, nil | |
95 | } | |
96 | } | |
97 | } | |
98 | return "", fmt.Errorf("ssh: no common algorithm for %s; client offered: %v, server offered: %v", what, client, server) | |
99 | } | |
100 | ||
101 | type directionAlgorithms struct { | |
102 | Cipher string | |
103 | MAC string | |
104 | Compression string | |
105 | } | |
106 | ||
107 | // rekeyBytes returns a rekeying intervals in bytes. | |
108 | func (a *directionAlgorithms) rekeyBytes() int64 { | |
109 | // According to RFC4344 block ciphers should rekey after | |
110 | // 2^(BLOCKSIZE/4) blocks. For all AES flavors BLOCKSIZE is | |
111 | // 128. | |
112 | switch a.Cipher { | |
113 | case "aes128-ctr", "aes192-ctr", "aes256-ctr", gcmCipherID, aes128cbcID: | |
114 | return 16 * (1 << 32) | |
115 | ||
116 | } | |
117 | ||
118 | // For others, stick with RFC4253 recommendation to rekey after 1 Gb of data. | |
119 | return 1 << 30 | |
120 | } | |
121 | ||
122 | type algorithms struct { | |
123 | kex string | |
124 | hostKey string | |
125 | w directionAlgorithms | |
126 | r directionAlgorithms | |
127 | } | |
128 | ||
129 | func findAgreedAlgorithms(clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms, err error) { | |
130 | result := &algorithms{} | |
131 | ||
132 | result.kex, err = findCommon("key exchange", clientKexInit.KexAlgos, serverKexInit.KexAlgos) | |
133 | if err != nil { | |
134 | return | |
135 | } | |
136 | ||
137 | result.hostKey, err = findCommon("host key", clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos) | |
138 | if err != nil { | |
139 | return | |
140 | } | |
141 | ||
142 | result.w.Cipher, err = findCommon("client to server cipher", clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer) | |
143 | if err != nil { | |
144 | return | |
145 | } | |
146 | ||
147 | result.r.Cipher, err = findCommon("server to client cipher", clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient) | |
148 | if err != nil { | |
149 | return | |
150 | } | |
151 | ||
152 | result.w.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer) | |
153 | if err != nil { | |
154 | return | |
155 | } | |
156 | ||
157 | result.r.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient) | |
158 | if err != nil { | |
159 | return | |
160 | } | |
161 | ||
162 | result.w.Compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer) | |
163 | if err != nil { | |
164 | return | |
165 | } | |
166 | ||
167 | result.r.Compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient) | |
168 | if err != nil { | |
169 | return | |
170 | } | |
171 | ||
172 | return result, nil | |
173 | } | |
174 | ||
175 | // If rekeythreshold is too small, we can't make any progress sending | |
176 | // stuff. | |
177 | const minRekeyThreshold uint64 = 256 | |
178 | ||
179 | // Config contains configuration data common to both ServerConfig and | |
180 | // ClientConfig. | |
181 | type Config struct { | |
182 | // Rand provides the source of entropy for cryptographic | |
183 | // primitives. If Rand is nil, the cryptographic random reader | |
184 | // in package crypto/rand will be used. | |
185 | Rand io.Reader | |
186 | ||
187 | // The maximum number of bytes sent or received after which a | |
188 | // new key is negotiated. It must be at least 256. If | |
189 | // unspecified, 1 gigabyte is used. | |
190 | RekeyThreshold uint64 | |
191 | ||
192 | // The allowed key exchanges algorithms. If unspecified then a | |
193 | // default set of algorithms is used. | |
194 | KeyExchanges []string | |
195 | ||
196 | // The allowed cipher algorithms. If unspecified then a sensible | |
197 | // default is used. | |
198 | Ciphers []string | |
199 | ||
200 | // The allowed MAC algorithms. If unspecified then a sensible default | |
201 | // is used. | |
202 | MACs []string | |
203 | } | |
204 | ||
205 | // SetDefaults sets sensible values for unset fields in config. This is | |
206 | // exported for testing: Configs passed to SSH functions are copied and have | |
207 | // default values set automatically. | |
208 | func (c *Config) SetDefaults() { | |
209 | if c.Rand == nil { | |
210 | c.Rand = rand.Reader | |
211 | } | |
212 | if c.Ciphers == nil { | |
213 | c.Ciphers = supportedCiphers | |
214 | } | |
215 | var ciphers []string | |
216 | for _, c := range c.Ciphers { | |
217 | if cipherModes[c] != nil { | |
218 | // reject the cipher if we have no cipherModes definition | |
219 | ciphers = append(ciphers, c) | |
220 | } | |
221 | } | |
222 | c.Ciphers = ciphers | |
223 | ||
224 | if c.KeyExchanges == nil { | |
225 | c.KeyExchanges = supportedKexAlgos | |
226 | } | |
227 | ||
228 | if c.MACs == nil { | |
229 | c.MACs = supportedMACs | |
230 | } | |
231 | ||
232 | if c.RekeyThreshold == 0 { | |
233 | // RFC 4253, section 9 suggests rekeying after 1G. | |
234 | c.RekeyThreshold = 1 << 30 | |
235 | } | |
236 | if c.RekeyThreshold < minRekeyThreshold { | |
237 | c.RekeyThreshold = minRekeyThreshold | |
238 | } | |
239 | } | |
240 | ||
241 | // buildDataSignedForAuth returns the data that is signed in order to prove | |
242 | // possession of a private key. See RFC 4252, section 7. | |
243 | func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte { | |
244 | data := struct { | |
245 | Session []byte | |
246 | Type byte | |
247 | User string | |
248 | Service string | |
249 | Method string | |
250 | Sign bool | |
251 | Algo []byte | |
252 | PubKey []byte | |
253 | }{ | |
254 | sessionId, | |
255 | msgUserAuthRequest, | |
256 | req.User, | |
257 | req.Service, | |
258 | req.Method, | |
259 | true, | |
260 | algo, | |
261 | pubKey, | |
262 | } | |
263 | return Marshal(data) | |
264 | } | |
265 | ||
266 | func appendU16(buf []byte, n uint16) []byte { | |
267 | return append(buf, byte(n>>8), byte(n)) | |
268 | } | |
269 | ||
270 | func appendU32(buf []byte, n uint32) []byte { | |
271 | return append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) | |
272 | } | |
273 | ||
274 | func appendU64(buf []byte, n uint64) []byte { | |
275 | return append(buf, | |
276 | byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32), | |
277 | byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) | |
278 | } | |
279 | ||
280 | func appendInt(buf []byte, n int) []byte { | |
281 | return appendU32(buf, uint32(n)) | |
282 | } | |
283 | ||
284 | func appendString(buf []byte, s string) []byte { | |
285 | buf = appendU32(buf, uint32(len(s))) | |
286 | buf = append(buf, s...) | |
287 | return buf | |
288 | } | |
289 | ||
290 | func appendBool(buf []byte, b bool) []byte { | |
291 | if b { | |
292 | return append(buf, 1) | |
293 | } | |
294 | return append(buf, 0) | |
295 | } | |
296 | ||
297 | // newCond is a helper to hide the fact that there is no usable zero | |
298 | // value for sync.Cond. | |
299 | func newCond() *sync.Cond { return sync.NewCond(new(sync.Mutex)) } | |
300 | ||
301 | // window represents the buffer available to clients | |
302 | // wishing to write to a channel. | |
303 | type window struct { | |
304 | *sync.Cond | |
305 | win uint32 // RFC 4254 5.2 says the window size can grow to 2^32-1 | |
306 | writeWaiters int | |
307 | closed bool | |
308 | } | |
309 | ||
310 | // add adds win to the amount of window available | |
311 | // for consumers. | |
312 | func (w *window) add(win uint32) bool { | |
313 | // a zero sized window adjust is a noop. | |
314 | if win == 0 { | |
315 | return true | |
316 | } | |
317 | w.L.Lock() | |
318 | if w.win+win < win { | |
319 | w.L.Unlock() | |
320 | return false | |
321 | } | |
322 | w.win += win | |
323 | // It is unusual that multiple goroutines would be attempting to reserve | |
324 | // window space, but not guaranteed. Use broadcast to notify all waiters | |
325 | // that additional window is available. | |
326 | w.Broadcast() | |
327 | w.L.Unlock() | |
328 | return true | |
329 | } | |
330 | ||
331 | // close sets the window to closed, so all reservations fail | |
332 | // immediately. | |
333 | func (w *window) close() { | |
334 | w.L.Lock() | |
335 | w.closed = true | |
336 | w.Broadcast() | |
337 | w.L.Unlock() | |
338 | } | |
339 | ||
340 | // reserve reserves win from the available window capacity. | |
341 | // If no capacity remains, reserve will block. reserve may | |
342 | // return less than requested. | |
343 | func (w *window) reserve(win uint32) (uint32, error) { | |
344 | var err error | |
345 | w.L.Lock() | |
346 | w.writeWaiters++ | |
347 | w.Broadcast() | |
348 | for w.win == 0 && !w.closed { | |
349 | w.Wait() | |
350 | } | |
351 | w.writeWaiters-- | |
352 | if w.win < win { | |
353 | win = w.win | |
354 | } | |
355 | w.win -= win | |
356 | if w.closed { | |
357 | err = io.EOF | |
358 | } | |
359 | w.L.Unlock() | |
360 | return win, err | |
361 | } | |
362 | ||
363 | // waitWriterBlocked waits until some goroutine is blocked for further | |
364 | // writes. It is used in tests only. | |
365 | func (w *window) waitWriterBlocked() { | |
366 | w.Cond.L.Lock() | |
367 | for w.writeWaiters == 0 { | |
368 | w.Cond.Wait() | |
369 | } | |
370 | w.Cond.L.Unlock() | |
371 | } |