1
2
3
4
5 package ssh
6
7 import (
8 "crypto"
9 "crypto/rand"
10 "fmt"
11 "io"
12 "math"
13 "sync"
14
15 _ "crypto/sha1"
16 _ "crypto/sha256"
17 _ "crypto/sha512"
18 )
19
20
21 const (
22 compressionNone = "none"
23 serviceUserAuth = "ssh-userauth"
24 serviceSSH = "ssh-connection"
25 )
26
27
28 var supportedCiphers = []string{
29 "aes128-ctr", "aes192-ctr", "aes256-ctr",
30 "aes128-gcm@openssh.com", gcm256CipherID,
31 chacha20Poly1305ID,
32 "arcfour256", "arcfour128", "arcfour",
33 aes128cbcID,
34 tripledescbcID,
35 }
36
37
38 var preferredCiphers = []string{
39 "aes128-gcm@openssh.com", gcm256CipherID,
40 chacha20Poly1305ID,
41 "aes128-ctr", "aes192-ctr", "aes256-ctr",
42 }
43
44
45
46 var supportedKexAlgos = []string{
47 kexAlgoCurve25519SHA256, kexAlgoCurve25519SHA256LibSSH,
48
49
50 kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
51 kexAlgoDH14SHA256, kexAlgoDH16SHA512, kexAlgoDH14SHA1,
52 kexAlgoDH1SHA1,
53 }
54
55
56
57 var serverForbiddenKexAlgos = map[string]struct{}{
58 kexAlgoDHGEXSHA1: {},
59 kexAlgoDHGEXSHA256: {},
60 }
61
62
63
64
65 var preferredKexAlgos = []string{
66 kexAlgoCurve25519SHA256, kexAlgoCurve25519SHA256LibSSH,
67 kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
68 kexAlgoDH14SHA256, kexAlgoDH14SHA1,
69 }
70
71
72
73 var supportedHostKeyAlgos = []string{
74 CertAlgoRSASHA256v01, CertAlgoRSASHA512v01,
75 CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
76 CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01,
77
78 KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
79 KeyAlgoRSASHA256, KeyAlgoRSASHA512,
80 KeyAlgoRSA, KeyAlgoDSA,
81
82 KeyAlgoED25519,
83 }
84
85
86
87
88 var supportedMACs = []string{
89 "hmac-sha2-256-etm@openssh.com", "hmac-sha2-512-etm@openssh.com", "hmac-sha2-256", "hmac-sha2-512", "hmac-sha1", "hmac-sha1-96",
90 }
91
92 var supportedCompressions = []string{compressionNone}
93
94
95
96 var hashFuncs = map[string]crypto.Hash{
97 KeyAlgoRSA: crypto.SHA1,
98 KeyAlgoRSASHA256: crypto.SHA256,
99 KeyAlgoRSASHA512: crypto.SHA512,
100 KeyAlgoDSA: crypto.SHA1,
101 KeyAlgoECDSA256: crypto.SHA256,
102 KeyAlgoECDSA384: crypto.SHA384,
103 KeyAlgoECDSA521: crypto.SHA512,
104
105 KeyAlgoSKECDSA256: crypto.SHA256,
106 KeyAlgoSKED25519: crypto.SHA256,
107 }
108
109
110
111
112 func algorithmsForKeyFormat(keyFormat string) []string {
113 switch keyFormat {
114 case KeyAlgoRSA:
115 return []string{KeyAlgoRSASHA256, KeyAlgoRSASHA512, KeyAlgoRSA}
116 case CertAlgoRSAv01:
117 return []string{CertAlgoRSASHA256v01, CertAlgoRSASHA512v01, CertAlgoRSAv01}
118 default:
119 return []string{keyFormat}
120 }
121 }
122
123
124
125 func isRSA(algo string) bool {
126 algos := algorithmsForKeyFormat(KeyAlgoRSA)
127 return contains(algos, underlyingAlgo(algo))
128 }
129
130 func isRSACert(algo string) bool {
131 _, ok := certKeyAlgoNames[algo]
132 if !ok {
133 return false
134 }
135 return isRSA(algo)
136 }
137
138
139
140
141
142 var supportedPubKeyAuthAlgos = []string{
143 KeyAlgoED25519,
144 KeyAlgoSKED25519, KeyAlgoSKECDSA256,
145 KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
146 KeyAlgoRSASHA256, KeyAlgoRSASHA512, KeyAlgoRSA,
147 KeyAlgoDSA,
148 }
149
150
151
152 func unexpectedMessageError(expected, got uint8) error {
153 return fmt.Errorf("ssh: unexpected message type %d (expected %d)", got, expected)
154 }
155
156
157 func parseError(tag uint8) error {
158 return fmt.Errorf("ssh: parse error in message type %d", tag)
159 }
160
161 func findCommon(what string, client []string, server []string) (common string, err error) {
162 for _, c := range client {
163 for _, s := range server {
164 if c == s {
165 return c, nil
166 }
167 }
168 }
169 return "", fmt.Errorf("ssh: no common algorithm for %s; client offered: %v, server offered: %v", what, client, server)
170 }
171
172
173 type directionAlgorithms struct {
174 Cipher string
175 MAC string
176 Compression string
177 }
178
179
180 func (a *directionAlgorithms) rekeyBytes() int64 {
181
182
183
184 switch a.Cipher {
185 case "aes128-ctr", "aes192-ctr", "aes256-ctr", gcm128CipherID, gcm256CipherID, aes128cbcID:
186 return 16 * (1 << 32)
187
188 }
189
190
191 return 1 << 30
192 }
193
194 var aeadCiphers = map[string]bool{
195 gcm128CipherID: true,
196 gcm256CipherID: true,
197 chacha20Poly1305ID: true,
198 }
199
200 type algorithms struct {
201 kex string
202 hostKey string
203 w directionAlgorithms
204 r directionAlgorithms
205 }
206
207 func findAgreedAlgorithms(isClient bool, clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms, err error) {
208 result := &algorithms{}
209
210 result.kex, err = findCommon("key exchange", clientKexInit.KexAlgos, serverKexInit.KexAlgos)
211 if err != nil {
212 return
213 }
214
215 result.hostKey, err = findCommon("host key", clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos)
216 if err != nil {
217 return
218 }
219
220 stoc, ctos := &result.w, &result.r
221 if isClient {
222 ctos, stoc = stoc, ctos
223 }
224
225 ctos.Cipher, err = findCommon("client to server cipher", clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer)
226 if err != nil {
227 return
228 }
229
230 stoc.Cipher, err = findCommon("server to client cipher", clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient)
231 if err != nil {
232 return
233 }
234
235 if !aeadCiphers[ctos.Cipher] {
236 ctos.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer)
237 if err != nil {
238 return
239 }
240 }
241
242 if !aeadCiphers[stoc.Cipher] {
243 stoc.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient)
244 if err != nil {
245 return
246 }
247 }
248
249 ctos.Compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer)
250 if err != nil {
251 return
252 }
253
254 stoc.Compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient)
255 if err != nil {
256 return
257 }
258
259 return result, nil
260 }
261
262
263
264 const minRekeyThreshold uint64 = 256
265
266
267
268 type Config struct {
269
270
271
272 Rand io.Reader
273
274
275
276
277 RekeyThreshold uint64
278
279
280
281 KeyExchanges []string
282
283
284
285 Ciphers []string
286
287
288
289 MACs []string
290 }
291
292
293
294
295 func (c *Config) SetDefaults() {
296 if c.Rand == nil {
297 c.Rand = rand.Reader
298 }
299 if c.Ciphers == nil {
300 c.Ciphers = preferredCiphers
301 }
302 var ciphers []string
303 for _, c := range c.Ciphers {
304 if cipherModes[c] != nil {
305
306 ciphers = append(ciphers, c)
307 }
308 }
309 c.Ciphers = ciphers
310
311 if c.KeyExchanges == nil {
312 c.KeyExchanges = preferredKexAlgos
313 }
314 var kexs []string
315 for _, k := range c.KeyExchanges {
316 if kexAlgoMap[k] != nil {
317
318 kexs = append(kexs, k)
319 }
320 }
321 c.KeyExchanges = kexs
322
323 if c.MACs == nil {
324 c.MACs = supportedMACs
325 }
326 var macs []string
327 for _, m := range c.MACs {
328 if macModes[m] != nil {
329
330 macs = append(macs, m)
331 }
332 }
333 c.MACs = macs
334
335 if c.RekeyThreshold == 0 {
336
337 } else if c.RekeyThreshold < minRekeyThreshold {
338 c.RekeyThreshold = minRekeyThreshold
339 } else if c.RekeyThreshold >= math.MaxInt64 {
340
341 c.RekeyThreshold = math.MaxInt64
342 }
343 }
344
345
346
347
348 func buildDataSignedForAuth(sessionID []byte, req userAuthRequestMsg, algo string, pubKey []byte) []byte {
349 data := struct {
350 Session []byte
351 Type byte
352 User string
353 Service string
354 Method string
355 Sign bool
356 Algo string
357 PubKey []byte
358 }{
359 sessionID,
360 msgUserAuthRequest,
361 req.User,
362 req.Service,
363 req.Method,
364 true,
365 algo,
366 pubKey,
367 }
368 return Marshal(data)
369 }
370
371 func appendU16(buf []byte, n uint16) []byte {
372 return append(buf, byte(n>>8), byte(n))
373 }
374
375 func appendU32(buf []byte, n uint32) []byte {
376 return append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
377 }
378
379 func appendU64(buf []byte, n uint64) []byte {
380 return append(buf,
381 byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32),
382 byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
383 }
384
385 func appendInt(buf []byte, n int) []byte {
386 return appendU32(buf, uint32(n))
387 }
388
389 func appendString(buf []byte, s string) []byte {
390 buf = appendU32(buf, uint32(len(s)))
391 buf = append(buf, s...)
392 return buf
393 }
394
395 func appendBool(buf []byte, b bool) []byte {
396 if b {
397 return append(buf, 1)
398 }
399 return append(buf, 0)
400 }
401
402
403
404 func newCond() *sync.Cond { return sync.NewCond(new(sync.Mutex)) }
405
406
407
408 type window struct {
409 *sync.Cond
410 win uint32
411 writeWaiters int
412 closed bool
413 }
414
415
416
417 func (w *window) add(win uint32) bool {
418
419 if win == 0 {
420 return true
421 }
422 w.L.Lock()
423 if w.win+win < win {
424 w.L.Unlock()
425 return false
426 }
427 w.win += win
428
429
430
431 w.Broadcast()
432 w.L.Unlock()
433 return true
434 }
435
436
437
438 func (w *window) close() {
439 w.L.Lock()
440 w.closed = true
441 w.Broadcast()
442 w.L.Unlock()
443 }
444
445
446
447
448 func (w *window) reserve(win uint32) (uint32, error) {
449 var err error
450 w.L.Lock()
451 w.writeWaiters++
452 w.Broadcast()
453 for w.win == 0 && !w.closed {
454 w.Wait()
455 }
456 w.writeWaiters--
457 if w.win < win {
458 win = w.win
459 }
460 w.win -= win
461 if w.closed {
462 err = io.EOF
463 }
464 w.L.Unlock()
465 return win, err
466 }
467
468
469
470 func (w *window) waitWriterBlocked() {
471 w.Cond.L.Lock()
472 for w.writeWaiters == 0 {
473 w.Cond.Wait()
474 }
475 w.Cond.L.Unlock()
476 }
477
View as plain text