main
Raw Download raw file
  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
  5package ssh
  6
  7import (
  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// These are string constants in the SSH protocol.
 21const (
 22	compressionNone = "none"
 23	serviceUserAuth = "ssh-userauth"
 24	serviceSSH      = "ssh-connection"
 25)
 26
 27// supportedCiphers lists ciphers we support but might not recommend.
 28var 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// preferredCiphers specifies the default preference for ciphers.
 38var preferredCiphers = []string{
 39	"aes128-gcm@openssh.com", gcm256CipherID,
 40	chacha20Poly1305ID,
 41	"aes128-ctr", "aes192-ctr", "aes256-ctr",
 42}
 43
 44// supportedKexAlgos specifies the supported key-exchange algorithms in
 45// preference order.
 46var supportedKexAlgos = []string{
 47	kexAlgoCurve25519SHA256, kexAlgoCurve25519SHA256LibSSH,
 48	// P384 and P521 are not constant-time yet, but since we don't
 49	// reuse ephemeral keys, using them for ECDH should be OK.
 50	kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
 51	kexAlgoDH14SHA256, kexAlgoDH16SHA512, kexAlgoDH14SHA1,
 52	kexAlgoDH1SHA1,
 53}
 54
 55// serverForbiddenKexAlgos contains key exchange algorithms, that are forbidden
 56// for the server half.
 57var serverForbiddenKexAlgos = map[string]struct{}{
 58	kexAlgoDHGEXSHA1:   {}, // server half implementation is only minimal to satisfy the automated tests
 59	kexAlgoDHGEXSHA256: {}, // server half implementation is only minimal to satisfy the automated tests
 60}
 61
 62// preferredKexAlgos specifies the default preference for key-exchange
 63// algorithms in preference order. The diffie-hellman-group16-sha512 algorithm
 64// is disabled by default because it is a bit slower than the others.
 65var preferredKexAlgos = []string{
 66	kexAlgoCurve25519SHA256, kexAlgoCurve25519SHA256LibSSH,
 67	kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
 68	kexAlgoDH14SHA256, kexAlgoDH14SHA1,
 69}
 70
 71// supportedHostKeyAlgos specifies the supported host-key algorithms (i.e. methods
 72// of authenticating servers) in preference order.
 73var 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// supportedMACs specifies a default set of MAC algorithms in preference order.
 86// This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed
 87// because they have reached the end of their useful life.
 88var 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
 92var supportedCompressions = []string{compressionNone}
 93
 94// hashFuncs keeps the mapping of supported signature algorithms to their
 95// respective hashes needed for signing and verification.
 96var 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	// KeyAlgoED25519 doesn't pre-hash.
105	KeyAlgoSKECDSA256: crypto.SHA256,
106	KeyAlgoSKED25519:  crypto.SHA256,
107}
108
109// algorithmsForKeyFormat returns the supported signature algorithms for a given
110// public key format (PublicKey.Type), in order of preference. See RFC 8332,
111// Section 2. See also the note in sendKexInit on backwards compatibility.
112func 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// isRSA returns whether algo is a supported RSA algorithm, including certificate
124// algorithms.
125func isRSA(algo string) bool {
126	algos := algorithmsForKeyFormat(KeyAlgoRSA)
127	return contains(algos, underlyingAlgo(algo))
128}
129
130func isRSACert(algo string) bool {
131	_, ok := certKeyAlgoNames[algo]
132	if !ok {
133		return false
134	}
135	return isRSA(algo)
136}
137
138// supportedPubKeyAuthAlgos specifies the supported client public key
139// authentication algorithms. Note that this doesn't include certificate types
140// since those use the underlying algorithm. This list is sent to the client if
141// it supports the server-sig-algs extension. Order is irrelevant.
142var supportedPubKeyAuthAlgos = []string{
143	KeyAlgoED25519,
144	KeyAlgoSKED25519, KeyAlgoSKECDSA256,
145	KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
146	KeyAlgoRSASHA256, KeyAlgoRSASHA512, KeyAlgoRSA,
147	KeyAlgoDSA,
148}
149
150// unexpectedMessageError results when the SSH message that we received didn't
151// match what we wanted.
152func unexpectedMessageError(expected, got uint8) error {
153	return fmt.Errorf("ssh: unexpected message type %d (expected %d)", got, expected)
154}
155
156// parseError results from a malformed SSH message.
157func parseError(tag uint8) error {
158	return fmt.Errorf("ssh: parse error in message type %d", tag)
159}
160
161func 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// directionAlgorithms records algorithm choices in one direction (either read or write)
173type directionAlgorithms struct {
174	Cipher      string
175	MAC         string
176	Compression string
177}
178
179// rekeyBytes returns a rekeying intervals in bytes.
180func (a *directionAlgorithms) rekeyBytes() int64 {
181	// According to RFC 4344 block ciphers should rekey after
182	// 2^(BLOCKSIZE/4) blocks. For all AES flavors BLOCKSIZE is
183	// 128.
184	switch a.Cipher {
185	case "aes128-ctr", "aes192-ctr", "aes256-ctr", gcm128CipherID, gcm256CipherID, aes128cbcID:
186		return 16 * (1 << 32)
187
188	}
189
190	// For others, stick with RFC 4253 recommendation to rekey after 1 Gb of data.
191	return 1 << 30
192}
193
194var aeadCiphers = map[string]bool{
195	gcm128CipherID:     true,
196	gcm256CipherID:     true,
197	chacha20Poly1305ID: true,
198}
199
200type algorithms struct {
201	kex     string
202	hostKey string
203	w       directionAlgorithms
204	r       directionAlgorithms
205}
206
207func 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// If rekeythreshold is too small, we can't make any progress sending
263// stuff.
264const minRekeyThreshold uint64 = 256
265
266// Config contains configuration data common to both ServerConfig and
267// ClientConfig.
268type Config struct {
269	// Rand provides the source of entropy for cryptographic
270	// primitives. If Rand is nil, the cryptographic random reader
271	// in package crypto/rand will be used.
272	Rand io.Reader
273
274	// The maximum number of bytes sent or received after which a
275	// new key is negotiated. It must be at least 256. If
276	// unspecified, a size suitable for the chosen cipher is used.
277	RekeyThreshold uint64
278
279	// The allowed key exchanges algorithms. If unspecified then a default set
280	// of algorithms is used. Unsupported values are silently ignored.
281	KeyExchanges []string
282
283	// The allowed cipher algorithms. If unspecified then a sensible default is
284	// used. Unsupported values are silently ignored.
285	Ciphers []string
286
287	// The allowed MAC algorithms. If unspecified then a sensible default is
288	// used. Unsupported values are silently ignored.
289	MACs []string
290}
291
292// SetDefaults sets sensible values for unset fields in config. This is
293// exported for testing: Configs passed to SSH functions are copied and have
294// default values set automatically.
295func (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			// Ignore the cipher if we have no cipherModes definition.
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			// Ignore the KEX if we have no kexAlgoMap definition.
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			// Ignore the MAC if we have no macModes definition.
330			macs = append(macs, m)
331		}
332	}
333	c.MACs = macs
334
335	if c.RekeyThreshold == 0 {
336		// cipher specific default
337	} else if c.RekeyThreshold < minRekeyThreshold {
338		c.RekeyThreshold = minRekeyThreshold
339	} else if c.RekeyThreshold >= math.MaxInt64 {
340		// Avoid weirdness if somebody uses -1 as a threshold.
341		c.RekeyThreshold = math.MaxInt64
342	}
343}
344
345// buildDataSignedForAuth returns the data that is signed in order to prove
346// possession of a private key. See RFC 4252, section 7. algo is the advertised
347// algorithm, and may be a certificate type.
348func 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
371func appendU16(buf []byte, n uint16) []byte {
372	return append(buf, byte(n>>8), byte(n))
373}
374
375func appendU32(buf []byte, n uint32) []byte {
376	return append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
377}
378
379func 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
385func appendInt(buf []byte, n int) []byte {
386	return appendU32(buf, uint32(n))
387}
388
389func appendString(buf []byte, s string) []byte {
390	buf = appendU32(buf, uint32(len(s)))
391	buf = append(buf, s...)
392	return buf
393}
394
395func appendBool(buf []byte, b bool) []byte {
396	if b {
397		return append(buf, 1)
398	}
399	return append(buf, 0)
400}
401
402// newCond is a helper to hide the fact that there is no usable zero
403// value for sync.Cond.
404func newCond() *sync.Cond { return sync.NewCond(new(sync.Mutex)) }
405
406// window represents the buffer available to clients
407// wishing to write to a channel.
408type window struct {
409	*sync.Cond
410	win          uint32 // RFC 4254 5.2 says the window size can grow to 2^32-1
411	writeWaiters int
412	closed       bool
413}
414
415// add adds win to the amount of window available
416// for consumers.
417func (w *window) add(win uint32) bool {
418	// a zero sized window adjust is a noop.
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	// It is unusual that multiple goroutines would be attempting to reserve
429	// window space, but not guaranteed. Use broadcast to notify all waiters
430	// that additional window is available.
431	w.Broadcast()
432	w.L.Unlock()
433	return true
434}
435
436// close sets the window to closed, so all reservations fail
437// immediately.
438func (w *window) close() {
439	w.L.Lock()
440	w.closed = true
441	w.Broadcast()
442	w.L.Unlock()
443}
444
445// reserve reserves win from the available window capacity.
446// If no capacity remains, reserve will block. reserve may
447// return less than requested.
448func (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// waitWriterBlocked waits until some goroutine is blocked for further
469// writes. It is used in tests only.
470func (w *window) waitWriterBlocked() {
471	w.Cond.L.Lock()
472	for w.writeWaiters == 0 {
473		w.Cond.Wait()
474	}
475	w.Cond.L.Unlock()
476}