gtsocial-umbx

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

transport.go (9418B)


      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 	"bufio"
      9 	"bytes"
     10 	"errors"
     11 	"io"
     12 	"log"
     13 )
     14 
     15 // debugTransport if set, will print packet types as they go over the
     16 // wire. No message decoding is done, to minimize the impact on timing.
     17 const debugTransport = false
     18 
     19 const (
     20 	gcm128CipherID = "aes128-gcm@openssh.com"
     21 	gcm256CipherID = "aes256-gcm@openssh.com"
     22 	aes128cbcID    = "aes128-cbc"
     23 	tripledescbcID = "3des-cbc"
     24 )
     25 
     26 // packetConn represents a transport that implements packet based
     27 // operations.
     28 type packetConn interface {
     29 	// Encrypt and send a packet of data to the remote peer.
     30 	writePacket(packet []byte) error
     31 
     32 	// Read a packet from the connection. The read is blocking,
     33 	// i.e. if error is nil, then the returned byte slice is
     34 	// always non-empty.
     35 	readPacket() ([]byte, error)
     36 
     37 	// Close closes the write-side of the connection.
     38 	Close() error
     39 }
     40 
     41 // transport is the keyingTransport that implements the SSH packet
     42 // protocol.
     43 type transport struct {
     44 	reader connectionState
     45 	writer connectionState
     46 
     47 	bufReader *bufio.Reader
     48 	bufWriter *bufio.Writer
     49 	rand      io.Reader
     50 	isClient  bool
     51 	io.Closer
     52 }
     53 
     54 // packetCipher represents a combination of SSH encryption/MAC
     55 // protocol.  A single instance should be used for one direction only.
     56 type packetCipher interface {
     57 	// writeCipherPacket encrypts the packet and writes it to w. The
     58 	// contents of the packet are generally scrambled.
     59 	writeCipherPacket(seqnum uint32, w io.Writer, rand io.Reader, packet []byte) error
     60 
     61 	// readCipherPacket reads and decrypts a packet of data. The
     62 	// returned packet may be overwritten by future calls of
     63 	// readPacket.
     64 	readCipherPacket(seqnum uint32, r io.Reader) ([]byte, error)
     65 }
     66 
     67 // connectionState represents one side (read or write) of the
     68 // connection. This is necessary because each direction has its own
     69 // keys, and can even have its own algorithms
     70 type connectionState struct {
     71 	packetCipher
     72 	seqNum           uint32
     73 	dir              direction
     74 	pendingKeyChange chan packetCipher
     75 }
     76 
     77 // prepareKeyChange sets up key material for a keychange. The key changes in
     78 // both directions are triggered by reading and writing a msgNewKey packet
     79 // respectively.
     80 func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) error {
     81 	ciph, err := newPacketCipher(t.reader.dir, algs.r, kexResult)
     82 	if err != nil {
     83 		return err
     84 	}
     85 	t.reader.pendingKeyChange <- ciph
     86 
     87 	ciph, err = newPacketCipher(t.writer.dir, algs.w, kexResult)
     88 	if err != nil {
     89 		return err
     90 	}
     91 	t.writer.pendingKeyChange <- ciph
     92 
     93 	return nil
     94 }
     95 
     96 func (t *transport) printPacket(p []byte, write bool) {
     97 	if len(p) == 0 {
     98 		return
     99 	}
    100 	who := "server"
    101 	if t.isClient {
    102 		who = "client"
    103 	}
    104 	what := "read"
    105 	if write {
    106 		what = "write"
    107 	}
    108 
    109 	log.Println(what, who, p[0])
    110 }
    111 
    112 // Read and decrypt next packet.
    113 func (t *transport) readPacket() (p []byte, err error) {
    114 	for {
    115 		p, err = t.reader.readPacket(t.bufReader)
    116 		if err != nil {
    117 			break
    118 		}
    119 		if len(p) == 0 || (p[0] != msgIgnore && p[0] != msgDebug) {
    120 			break
    121 		}
    122 	}
    123 	if debugTransport {
    124 		t.printPacket(p, false)
    125 	}
    126 
    127 	return p, err
    128 }
    129 
    130 func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) {
    131 	packet, err := s.packetCipher.readCipherPacket(s.seqNum, r)
    132 	s.seqNum++
    133 	if err == nil && len(packet) == 0 {
    134 		err = errors.New("ssh: zero length packet")
    135 	}
    136 
    137 	if len(packet) > 0 {
    138 		switch packet[0] {
    139 		case msgNewKeys:
    140 			select {
    141 			case cipher := <-s.pendingKeyChange:
    142 				s.packetCipher = cipher
    143 			default:
    144 				return nil, errors.New("ssh: got bogus newkeys message")
    145 			}
    146 
    147 		case msgDisconnect:
    148 			// Transform a disconnect message into an
    149 			// error. Since this is lowest level at which
    150 			// we interpret message types, doing it here
    151 			// ensures that we don't have to handle it
    152 			// elsewhere.
    153 			var msg disconnectMsg
    154 			if err := Unmarshal(packet, &msg); err != nil {
    155 				return nil, err
    156 			}
    157 			return nil, &msg
    158 		}
    159 	}
    160 
    161 	// The packet may point to an internal buffer, so copy the
    162 	// packet out here.
    163 	fresh := make([]byte, len(packet))
    164 	copy(fresh, packet)
    165 
    166 	return fresh, err
    167 }
    168 
    169 func (t *transport) writePacket(packet []byte) error {
    170 	if debugTransport {
    171 		t.printPacket(packet, true)
    172 	}
    173 	return t.writer.writePacket(t.bufWriter, t.rand, packet)
    174 }
    175 
    176 func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte) error {
    177 	changeKeys := len(packet) > 0 && packet[0] == msgNewKeys
    178 
    179 	err := s.packetCipher.writeCipherPacket(s.seqNum, w, rand, packet)
    180 	if err != nil {
    181 		return err
    182 	}
    183 	if err = w.Flush(); err != nil {
    184 		return err
    185 	}
    186 	s.seqNum++
    187 	if changeKeys {
    188 		select {
    189 		case cipher := <-s.pendingKeyChange:
    190 			s.packetCipher = cipher
    191 		default:
    192 			panic("ssh: no key material for msgNewKeys")
    193 		}
    194 	}
    195 	return err
    196 }
    197 
    198 func newTransport(rwc io.ReadWriteCloser, rand io.Reader, isClient bool) *transport {
    199 	t := &transport{
    200 		bufReader: bufio.NewReader(rwc),
    201 		bufWriter: bufio.NewWriter(rwc),
    202 		rand:      rand,
    203 		reader: connectionState{
    204 			packetCipher:     &streamPacketCipher{cipher: noneCipher{}},
    205 			pendingKeyChange: make(chan packetCipher, 1),
    206 		},
    207 		writer: connectionState{
    208 			packetCipher:     &streamPacketCipher{cipher: noneCipher{}},
    209 			pendingKeyChange: make(chan packetCipher, 1),
    210 		},
    211 		Closer: rwc,
    212 	}
    213 	t.isClient = isClient
    214 
    215 	if isClient {
    216 		t.reader.dir = serverKeys
    217 		t.writer.dir = clientKeys
    218 	} else {
    219 		t.reader.dir = clientKeys
    220 		t.writer.dir = serverKeys
    221 	}
    222 
    223 	return t
    224 }
    225 
    226 type direction struct {
    227 	ivTag     []byte
    228 	keyTag    []byte
    229 	macKeyTag []byte
    230 }
    231 
    232 var (
    233 	serverKeys = direction{[]byte{'B'}, []byte{'D'}, []byte{'F'}}
    234 	clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}}
    235 )
    236 
    237 // setupKeys sets the cipher and MAC keys from kex.K, kex.H and sessionId, as
    238 // described in RFC 4253, section 6.4. direction should either be serverKeys
    239 // (to setup server->client keys) or clientKeys (for client->server keys).
    240 func newPacketCipher(d direction, algs directionAlgorithms, kex *kexResult) (packetCipher, error) {
    241 	cipherMode := cipherModes[algs.Cipher]
    242 
    243 	iv := make([]byte, cipherMode.ivSize)
    244 	key := make([]byte, cipherMode.keySize)
    245 
    246 	generateKeyMaterial(iv, d.ivTag, kex)
    247 	generateKeyMaterial(key, d.keyTag, kex)
    248 
    249 	var macKey []byte
    250 	if !aeadCiphers[algs.Cipher] {
    251 		macMode := macModes[algs.MAC]
    252 		macKey = make([]byte, macMode.keySize)
    253 		generateKeyMaterial(macKey, d.macKeyTag, kex)
    254 	}
    255 
    256 	return cipherModes[algs.Cipher].create(key, iv, macKey, algs)
    257 }
    258 
    259 // generateKeyMaterial fills out with key material generated from tag, K, H
    260 // and sessionId, as specified in RFC 4253, section 7.2.
    261 func generateKeyMaterial(out, tag []byte, r *kexResult) {
    262 	var digestsSoFar []byte
    263 
    264 	h := r.Hash.New()
    265 	for len(out) > 0 {
    266 		h.Reset()
    267 		h.Write(r.K)
    268 		h.Write(r.H)
    269 
    270 		if len(digestsSoFar) == 0 {
    271 			h.Write(tag)
    272 			h.Write(r.SessionID)
    273 		} else {
    274 			h.Write(digestsSoFar)
    275 		}
    276 
    277 		digest := h.Sum(nil)
    278 		n := copy(out, digest)
    279 		out = out[n:]
    280 		if len(out) > 0 {
    281 			digestsSoFar = append(digestsSoFar, digest...)
    282 		}
    283 	}
    284 }
    285 
    286 const packageVersion = "SSH-2.0-Go"
    287 
    288 // Sends and receives a version line.  The versionLine string should
    289 // be US ASCII, start with "SSH-2.0-", and should not include a
    290 // newline. exchangeVersions returns the other side's version line.
    291 func exchangeVersions(rw io.ReadWriter, versionLine []byte) (them []byte, err error) {
    292 	// Contrary to the RFC, we do not ignore lines that don't
    293 	// start with "SSH-2.0-" to make the library usable with
    294 	// nonconforming servers.
    295 	for _, c := range versionLine {
    296 		// The spec disallows non US-ASCII chars, and
    297 		// specifically forbids null chars.
    298 		if c < 32 {
    299 			return nil, errors.New("ssh: junk character in version line")
    300 		}
    301 	}
    302 	if _, err = rw.Write(append(versionLine, '\r', '\n')); err != nil {
    303 		return
    304 	}
    305 
    306 	them, err = readVersion(rw)
    307 	return them, err
    308 }
    309 
    310 // maxVersionStringBytes is the maximum number of bytes that we'll
    311 // accept as a version string. RFC 4253 section 4.2 limits this at 255
    312 // chars
    313 const maxVersionStringBytes = 255
    314 
    315 // Read version string as specified by RFC 4253, section 4.2.
    316 func readVersion(r io.Reader) ([]byte, error) {
    317 	versionString := make([]byte, 0, 64)
    318 	var ok bool
    319 	var buf [1]byte
    320 
    321 	for length := 0; length < maxVersionStringBytes; length++ {
    322 		_, err := io.ReadFull(r, buf[:])
    323 		if err != nil {
    324 			return nil, err
    325 		}
    326 		// The RFC says that the version should be terminated with \r\n
    327 		// but several SSH servers actually only send a \n.
    328 		if buf[0] == '\n' {
    329 			if !bytes.HasPrefix(versionString, []byte("SSH-")) {
    330 				// RFC 4253 says we need to ignore all version string lines
    331 				// except the one containing the SSH version (provided that
    332 				// all the lines do not exceed 255 bytes in total).
    333 				versionString = versionString[:0]
    334 				continue
    335 			}
    336 			ok = true
    337 			break
    338 		}
    339 
    340 		// non ASCII chars are disallowed, but we are lenient,
    341 		// since Go doesn't use null-terminated strings.
    342 
    343 		// The RFC allows a comment after a space, however,
    344 		// all of it (version and comments) goes into the
    345 		// session hash.
    346 		versionString = append(versionString, buf[0])
    347 	}
    348 
    349 	if !ok {
    350 		return nil, errors.New("ssh: overflow reading version string")
    351 	}
    352 
    353 	// There might be a '\r' on the end which we should remove.
    354 	if len(versionString) > 0 && versionString[len(versionString)-1] == '\r' {
    355 		versionString = versionString[:len(versionString)-1]
    356 	}
    357 	return versionString, nil
    358 }