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