gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

common.go (12614B)


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