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